poc
This commit is contained in:
parent
16161afb2b
commit
b452149595
10 changed files with 212 additions and 68 deletions
|
@ -1869,7 +1869,7 @@ export interface SSRLoadedRenderer extends AstroRenderer {
|
||||||
|
|
||||||
export type HookParameters<
|
export type HookParameters<
|
||||||
Hook extends keyof AstroIntegration['hooks'],
|
Hook extends keyof AstroIntegration['hooks'],
|
||||||
Fn = AstroIntegration['hooks'][Hook]
|
Fn = AstroIntegration['hooks'][Hook],
|
||||||
> = Fn extends (...args: any) => any ? Parameters<Fn>[0] : never;
|
> = Fn extends (...args: any) => any ? Parameters<Fn>[0] : never;
|
||||||
|
|
||||||
export interface AstroIntegration {
|
export interface AstroIntegration {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import mime from 'mime';
|
|
||||||
import type {
|
import type {
|
||||||
EndpointHandler,
|
EndpointHandler,
|
||||||
ManifestData,
|
ManifestData,
|
||||||
|
MiddlewareEndpointHandler,
|
||||||
RouteData,
|
RouteData,
|
||||||
SSRElement,
|
SSRElement,
|
||||||
SSRManifest,
|
SSRManifest,
|
||||||
} from '../../@types/astro';
|
} from '../../@types/astro';
|
||||||
import type { SinglePageBuiltModule } from '../build/types';
|
import type { SinglePageBuiltModule } from '../build/types';
|
||||||
import { attachToResponse, getSetCookiesFromResponse } from '../cookies/index.js';
|
import { getSetCookiesFromResponse } from '../cookies/index.js';
|
||||||
import { consoleLogDestination } from '../logger/console.js';
|
import { consoleLogDestination } from '../logger/console.js';
|
||||||
import { error, type LogOptions } from '../logger/core.js';
|
import { error, type LogOptions } from '../logger/core.js';
|
||||||
import {
|
import {
|
||||||
|
@ -16,12 +16,10 @@ import {
|
||||||
removeTrailingForwardSlash,
|
removeTrailingForwardSlash,
|
||||||
} from '../path.js';
|
} from '../path.js';
|
||||||
import { RedirectSinglePageBuiltModule } from '../redirects/index.js';
|
import { RedirectSinglePageBuiltModule } from '../redirects/index.js';
|
||||||
import { isResponse } from '../render/core.js';
|
|
||||||
import {
|
import {
|
||||||
createEnvironment,
|
createEnvironment,
|
||||||
createRenderContext,
|
createRenderContext,
|
||||||
tryRenderRoute,
|
tryRenderRoute,
|
||||||
type Environment,
|
|
||||||
type RenderContext,
|
type RenderContext,
|
||||||
} from '../render/index.js';
|
} from '../render/index.js';
|
||||||
import { RouteCache } from '../render/route-cache.js';
|
import { RouteCache } from '../render/route-cache.js';
|
||||||
|
@ -32,6 +30,7 @@ import {
|
||||||
} from '../render/ssr-element.js';
|
} from '../render/ssr-element.js';
|
||||||
import { matchRoute } from '../routing/match.js';
|
import { matchRoute } from '../routing/match.js';
|
||||||
import type { RouteInfo } from './types';
|
import type { RouteInfo } from './types';
|
||||||
|
import { SSRRoutePipeline } from '../pipeline';
|
||||||
export { deserializeManifest } from './common.js';
|
export { deserializeManifest } from './common.js';
|
||||||
|
|
||||||
const clientLocalsSymbol = Symbol.for('astro.locals');
|
const clientLocalsSymbol = Symbol.for('astro.locals');
|
||||||
|
@ -53,16 +52,15 @@ export class App {
|
||||||
/**
|
/**
|
||||||
* The current environment of the application
|
* The current environment of the application
|
||||||
*/
|
*/
|
||||||
#env: Environment;
|
|
||||||
#manifest: SSRManifest;
|
#manifest: SSRManifest;
|
||||||
#manifestData: ManifestData;
|
#manifestData: ManifestData;
|
||||||
#routeDataToRouteInfo: Map<RouteData, RouteInfo>;
|
#routeDataToRouteInfo: Map<RouteData, RouteInfo>;
|
||||||
#encoder = new TextEncoder();
|
|
||||||
#logging: LogOptions = {
|
#logging: LogOptions = {
|
||||||
dest: consoleLogDestination,
|
dest: consoleLogDestination,
|
||||||
level: 'info',
|
level: 'info',
|
||||||
};
|
};
|
||||||
#baseWithoutTrailingSlash: string;
|
#baseWithoutTrailingSlash: string;
|
||||||
|
#pipeline: SSRRoutePipeline;
|
||||||
|
|
||||||
constructor(manifest: SSRManifest, streaming = true) {
|
constructor(manifest: SSRManifest, streaming = true) {
|
||||||
this.#manifest = manifest;
|
this.#manifest = manifest;
|
||||||
|
@ -71,7 +69,7 @@ export class App {
|
||||||
};
|
};
|
||||||
this.#routeDataToRouteInfo = new Map(manifest.routes.map((route) => [route.routeData, route]));
|
this.#routeDataToRouteInfo = new Map(manifest.routes.map((route) => [route.routeData, route]));
|
||||||
this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#manifest.base);
|
this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#manifest.base);
|
||||||
this.#env = this.#createEnvironment(streaming);
|
this.#pipeline = new SSRRoutePipeline(this.#createEnvironment(streaming));
|
||||||
}
|
}
|
||||||
|
|
||||||
set setManifest(newManifest: SSRManifest) {
|
set setManifest(newManifest: SSRManifest) {
|
||||||
|
@ -164,19 +162,16 @@ export class App {
|
||||||
);
|
);
|
||||||
let response;
|
let response;
|
||||||
try {
|
try {
|
||||||
response = await tryRenderRoute(
|
if (mod.onRequest) {
|
||||||
routeData.type,
|
this.#pipeline.setMiddlewareFunction(mod.onRequest as MiddlewareEndpointHandler);
|
||||||
renderContext,
|
}
|
||||||
this.#env,
|
response = await this.#pipeline.renderRoute(renderContext, pageModule);
|
||||||
pageModule,
|
|
||||||
mod.onRequest
|
|
||||||
);
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
error(this.#logging, 'ssr', err.stack || err.message || String(err));
|
error(this.#logging, 'ssr', err.stack || err.message || String(err));
|
||||||
return this.#renderError(request, { status: 500 });
|
return this.#renderError(request, { status: 500 });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isResponse(response, routeData.type)) {
|
if (SSRRoutePipeline.isResponse(response, routeData.type)) {
|
||||||
if (STATUS_CODES.has(response.status)) {
|
if (STATUS_CODES.has(response.status)) {
|
||||||
return this.#renderError(request, {
|
return this.#renderError(request, {
|
||||||
response,
|
response,
|
||||||
|
@ -185,35 +180,8 @@ export class App {
|
||||||
}
|
}
|
||||||
Reflect.set(response, responseSentSymbol, true);
|
Reflect.set(response, responseSentSymbol, true);
|
||||||
return response;
|
return response;
|
||||||
} else {
|
|
||||||
if (response.type === 'response') {
|
|
||||||
if (response.response.headers.get('X-Astro-Response') === 'Not-Found') {
|
|
||||||
return this.#renderError(request, {
|
|
||||||
response: response.response,
|
|
||||||
status: 404,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return response.response;
|
|
||||||
} else {
|
|
||||||
const headers = new Headers();
|
|
||||||
const mimeType = mime.getType(url.pathname);
|
|
||||||
if (mimeType) {
|
|
||||||
headers.set('Content-Type', `${mimeType};charset=utf-8`);
|
|
||||||
} else {
|
|
||||||
headers.set('Content-Type', 'text/plain;charset=utf-8');
|
|
||||||
}
|
|
||||||
const bytes =
|
|
||||||
response.encoding !== 'binary' ? this.#encoder.encode(response.body) : response.body;
|
|
||||||
headers.set('Content-Length', bytes.byteLength.toString());
|
|
||||||
|
|
||||||
const newResponse = new Response(bytes, {
|
|
||||||
status: 200,
|
|
||||||
headers,
|
|
||||||
});
|
|
||||||
attachToResponse(newResponse, response.cookies);
|
|
||||||
return newResponse;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
setCookieHeaders(response: Response) {
|
setCookieHeaders(response: Response) {
|
||||||
|
@ -239,7 +207,7 @@ export class App {
|
||||||
pathname,
|
pathname,
|
||||||
route: routeData,
|
route: routeData,
|
||||||
status,
|
status,
|
||||||
env: this.#env,
|
env: this.#pipeline.env,
|
||||||
mod: handler as any,
|
mod: handler as any,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -273,7 +241,7 @@ export class App {
|
||||||
route: routeData,
|
route: routeData,
|
||||||
status,
|
status,
|
||||||
mod,
|
mod,
|
||||||
env: this.#env,
|
env: this.#pipeline.env,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -302,9 +270,8 @@ export class App {
|
||||||
);
|
);
|
||||||
const page = (await mod.page()) as any;
|
const page = (await mod.page()) as any;
|
||||||
const response = (await tryRenderRoute(
|
const response = (await tryRenderRoute(
|
||||||
'page', // this is hardcoded to ensure proper behavior for missing endpoints
|
|
||||||
newRenderContext,
|
newRenderContext,
|
||||||
this.#env,
|
this.#pipeline.env,
|
||||||
page
|
page
|
||||||
)) as Response;
|
)) as Response;
|
||||||
return this.#mergeResponses(response, originalResponse);
|
return this.#mergeResponses(response, originalResponse);
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
export { AstroCookies } from './cookies.js';
|
export { AstroCookies } from './cookies.js';
|
||||||
export { attachToResponse, getSetCookiesFromResponse } from './response.js';
|
export { attachCookiesToResponse, getSetCookiesFromResponse } from './response.js';
|
||||||
|
|
|
@ -2,7 +2,7 @@ import type { AstroCookies } from './cookies';
|
||||||
|
|
||||||
const astroCookiesSymbol = Symbol.for('astro.cookies');
|
const astroCookiesSymbol = Symbol.for('astro.cookies');
|
||||||
|
|
||||||
export function attachToResponse(response: Response, cookies: AstroCookies) {
|
export function attachCookiesToResponse(response: Response, cookies: AstroCookies) {
|
||||||
Reflect.set(response, astroCookiesSymbol, cookies);
|
Reflect.set(response, astroCookiesSymbol, cookies);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import type { Environment, RenderContext } from '../render/index';
|
||||||
|
|
||||||
import { renderEndpoint } from '../../runtime/server/index.js';
|
import { renderEndpoint } from '../../runtime/server/index.js';
|
||||||
import { ASTRO_VERSION } from '../constants.js';
|
import { ASTRO_VERSION } from '../constants.js';
|
||||||
import { AstroCookies, attachToResponse } from '../cookies/index.js';
|
import { AstroCookies, attachCookiesToResponse } from '../cookies/index.js';
|
||||||
import { AstroError, AstroErrorData } from '../errors/index.js';
|
import { AstroError, AstroErrorData } from '../errors/index.js';
|
||||||
import { warn } from '../logger/core.js';
|
import { warn } from '../logger/core.js';
|
||||||
import { callMiddleware } from '../middleware/callMiddleware.js';
|
import { callMiddleware } from '../middleware/callMiddleware.js';
|
||||||
|
@ -125,7 +125,7 @@ export async function callEndpoint<MiddlewareResult = Response | EndpointOutput>
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response instanceof Response) {
|
if (response instanceof Response) {
|
||||||
attachToResponse(response, context.cookies);
|
attachCookiesToResponse(response, context.cookies);
|
||||||
return {
|
return {
|
||||||
type: 'response',
|
type: 'response',
|
||||||
response,
|
response,
|
||||||
|
|
176
packages/astro/src/core/pipeline.ts
Normal file
176
packages/astro/src/core/pipeline.ts
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
import type { Environment } from './render/environment';
|
||||||
|
import {createRenderContext, type RenderContext, tryRenderRoute} from './render';
|
||||||
|
import type { EndpointCallResult } from './endpoint';
|
||||||
|
import type { ComponentInstance, MiddlewareEndpointHandler, RouteType } from '../@types/astro';
|
||||||
|
import { attachCookiesToResponse } from './cookies';
|
||||||
|
import { TextEncoder } from 'util';
|
||||||
|
import mime from 'mime';
|
||||||
|
import type {TransformResult} from "@astrojs/compiler";
|
||||||
|
import {createBasicEnvironment} from "../../test/units/test-utils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Questions:
|
||||||
|
* 1. Can we call `getStaticPaths` really early?? Ideally when we load the component. -> idea is to make type the result of
|
||||||
|
* `getStaticPaths`, so we can make serializable and stub it via JS (no need of compiler or make a module).
|
||||||
|
* 2. When rendering a route, what are the info that belong to that route that are not shared with other routes? I guess:
|
||||||
|
* - the Request
|
||||||
|
* - a component instance?
|
||||||
|
* - styles?
|
||||||
|
* - scripts?
|
||||||
|
* - links?
|
||||||
|
* 3. In `RenderContext` we have a route which is a `RouteData`. What's used for? and why it can be optional?
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IDEAS:
|
||||||
|
* - what if `handleRequest` dev, instead of directly rendering the page, returns only the info needed to render a route?
|
||||||
|
* It would return only the `RenderContext`, because that's what needed for a route to render.
|
||||||
|
*/
|
||||||
|
|
||||||
|
type EndpointHandler = (
|
||||||
|
originalRequest: Request,
|
||||||
|
result: EndpointCallResult
|
||||||
|
) => Promise<Response> | Response;
|
||||||
|
|
||||||
|
export class Pipeline {
|
||||||
|
env: Environment;
|
||||||
|
onRequest?: MiddlewareEndpointHandler;
|
||||||
|
endpointHandler?: EndpointHandler;
|
||||||
|
|
||||||
|
constructor(env: Environment) {
|
||||||
|
this.env = env;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEndpointHandler(handler: EndpointHandler) {
|
||||||
|
this.endpointHandler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
setMiddlewareFunction(onRequest: MiddlewareEndpointHandler) {
|
||||||
|
this.onRequest = onRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
async renderRoute(
|
||||||
|
renderContext: RenderContext,
|
||||||
|
componentInstance: ComponentInstance
|
||||||
|
): Promise<Response> {
|
||||||
|
const result = await tryRenderRoute(renderContext, this.env, componentInstance, this.onRequest);
|
||||||
|
if (Pipeline.isEndpointResult(result, renderContext.route.type)) {
|
||||||
|
if (!this.endpointHandler) {
|
||||||
|
throw new Error('You must set the endpoint handler');
|
||||||
|
}
|
||||||
|
return this.endpointHandler(renderContext.request, result);
|
||||||
|
} else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static isEndpointResult(result: any, routeType: RouteType): result is EndpointCallResult {
|
||||||
|
return !(result instanceof Response) && routeType === 'endpoint';
|
||||||
|
}
|
||||||
|
|
||||||
|
static isResponse(result: any, routeType: RouteType): result is Response {
|
||||||
|
return result instanceof Response && (routeType === 'page' || routeType === 'redirect');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DevRoutePipeline extends Pipeline {
|
||||||
|
clearRouteCache() {
|
||||||
|
this.env.routeCache.clearAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BuildRoutePipeline extends Pipeline {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestRoutePipeline extends Pipeline {
|
||||||
|
// NOTE: we can also store JSX renderers is we need?
|
||||||
|
constructor() {
|
||||||
|
super(createBasicEnvironment());
|
||||||
|
}
|
||||||
|
|
||||||
|
async renderAstroPage(contents: string) {
|
||||||
|
const compilationResult = await this.#compile(contents);
|
||||||
|
const renderContext = await this.#computeTestContext(compilationResult);
|
||||||
|
const componentInstance = await this.#computeComponentInstance(compilationResult);
|
||||||
|
const response = await super.renderRoute(renderContext, componentInstance);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: compute `RenderContext` from compilation result, probably
|
||||||
|
async #computeTestContext(result: Readonly<TransformResult>): Promise<RenderContext> {
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: compute `ComponentInstance` from compilation result, probably
|
||||||
|
async #computeComponentInstance(result: Readonly<TransformResult>): Promise<ComponentInstance> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async #compile(contents: string): Promise<TransformResult> {
|
||||||
|
const compiler = await import("@astrojs/compiler");
|
||||||
|
const result = await compiler.transform(contents);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example of testing
|
||||||
|
|
||||||
|
|
||||||
|
async function middleware_should_work() {
|
||||||
|
const testPipeline = new TestRoutePipeline();
|
||||||
|
const page = `
|
||||||
|
---
|
||||||
|
const title = Astro.locals.title;
|
||||||
|
---
|
||||||
|
<title>{title}</title>
|
||||||
|
`;
|
||||||
|
testPipeline.setMiddlewareFunction((context, next) => {
|
||||||
|
context.locals = {
|
||||||
|
title: "Test"
|
||||||
|
}
|
||||||
|
return next();
|
||||||
|
})
|
||||||
|
const result = await testPipeline.renderAstroPage(page);
|
||||||
|
const text = await result.text();
|
||||||
|
// assertion text contains "Test"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class SSRRoutePipeline extends Pipeline {
|
||||||
|
encoder = new TextEncoder();
|
||||||
|
|
||||||
|
constructor(env: Environment) {
|
||||||
|
super(env);
|
||||||
|
this.setEndpointHandler(this.ssrEndpointHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
async ssrEndpointHandler(request: Request, response: EndpointCallResult): Promise<Response> {
|
||||||
|
if (response.type === 'response') {
|
||||||
|
if (response.response.headers.get('X-Astro-Response') === 'Not-Found') {
|
||||||
|
// TODO: throw proper astro error to catch in the app/index.ts, and render a 404 instead
|
||||||
|
throw new Error('');
|
||||||
|
}
|
||||||
|
return response.response;
|
||||||
|
} else {
|
||||||
|
const url = new URL(request.url);
|
||||||
|
const headers = new Headers();
|
||||||
|
const mimeType = mime.getType(url.pathname);
|
||||||
|
if (mimeType) {
|
||||||
|
headers.set('Content-Type', `${mimeType};charset=utf-8`);
|
||||||
|
} else {
|
||||||
|
headers.set('Content-Type', 'text/plain;charset=utf-8');
|
||||||
|
}
|
||||||
|
const bytes =
|
||||||
|
response.encoding !== 'binary' ? this.encoder.encode(response.body) : response.body;
|
||||||
|
headers.set('Content-Length', bytes.byteLength.toString());
|
||||||
|
|
||||||
|
const newResponse = new Response(bytes, {
|
||||||
|
status: 200,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
attachCookiesToResponse(newResponse, response.cookies);
|
||||||
|
return newResponse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ export interface RenderContext {
|
||||||
links?: Set<SSRElement>;
|
links?: Set<SSRElement>;
|
||||||
styles?: Set<SSRElement>;
|
styles?: Set<SSRElement>;
|
||||||
componentMetadata?: SSRResult['componentMetadata'];
|
componentMetadata?: SSRResult['componentMetadata'];
|
||||||
route?: RouteData;
|
route: RouteData;
|
||||||
status?: number;
|
status?: number;
|
||||||
params: Params;
|
params: Params;
|
||||||
props: Props;
|
props: Props;
|
||||||
|
@ -32,6 +32,7 @@ export interface RenderContext {
|
||||||
export type CreateRenderContextArgs = Partial<
|
export type CreateRenderContextArgs = Partial<
|
||||||
Omit<RenderContext, 'params' | 'props' | 'locals'>
|
Omit<RenderContext, 'params' | 'props' | 'locals'>
|
||||||
> & {
|
> & {
|
||||||
|
route: RouteData;
|
||||||
request: RenderContext['request'];
|
request: RenderContext['request'];
|
||||||
mod: ComponentInstance;
|
mod: ComponentInstance;
|
||||||
env: Environment;
|
env: Environment;
|
||||||
|
|
|
@ -7,7 +7,7 @@ import type {
|
||||||
RouteType,
|
RouteType,
|
||||||
} from '../../@types/astro';
|
} from '../../@types/astro';
|
||||||
import { renderPage as runtimeRenderPage } from '../../runtime/server/index.js';
|
import { renderPage as runtimeRenderPage } from '../../runtime/server/index.js';
|
||||||
import { attachToResponse } from '../cookies/index.js';
|
import { attachCookiesToResponse } from '../cookies/index.js';
|
||||||
import { callEndpoint, createAPIContext, type EndpointCallResult } from '../endpoint/index.js';
|
import { callEndpoint, createAPIContext, type EndpointCallResult } from '../endpoint/index.js';
|
||||||
import { callMiddleware } from '../middleware/callMiddleware.js';
|
import { callMiddleware } from '../middleware/callMiddleware.js';
|
||||||
import { redirectRouteGenerate, redirectRouteStatus, routeIsRedirect } from '../redirects/index.js';
|
import { redirectRouteGenerate, redirectRouteStatus, routeIsRedirect } from '../redirects/index.js';
|
||||||
|
@ -76,7 +76,7 @@ async function renderPage({ mod, renderContext, env, cookies }: RenderPage) {
|
||||||
// If there is an Astro.cookies instance, attach it to the response so that
|
// If there is an Astro.cookies instance, attach it to the response so that
|
||||||
// adapters can grab the Set-Cookie headers.
|
// adapters can grab the Set-Cookie headers.
|
||||||
if (result.cookies) {
|
if (result.cookies) {
|
||||||
attachToResponse(response, result.cookies);
|
attachCookiesToResponse(response, result.cookies);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
|
@ -93,7 +93,6 @@ async function renderPage({ mod, renderContext, env, cookies }: RenderPage) {
|
||||||
* It throws an error if the page can't be rendered.
|
* It throws an error if the page can't be rendered.
|
||||||
*/
|
*/
|
||||||
export async function tryRenderRoute<MiddlewareReturnType = Response>(
|
export async function tryRenderRoute<MiddlewareReturnType = Response>(
|
||||||
routeType: RouteType,
|
|
||||||
renderContext: Readonly<RenderContext>,
|
renderContext: Readonly<RenderContext>,
|
||||||
env: Readonly<Environment>,
|
env: Readonly<Environment>,
|
||||||
mod: Readonly<ComponentInstance>,
|
mod: Readonly<ComponentInstance>,
|
||||||
|
@ -107,7 +106,7 @@ export async function tryRenderRoute<MiddlewareReturnType = Response>(
|
||||||
adapterName: env.adapterName,
|
adapterName: env.adapterName,
|
||||||
});
|
});
|
||||||
|
|
||||||
switch (routeType) {
|
switch (renderContext.route.type) {
|
||||||
case 'page':
|
case 'page':
|
||||||
case 'redirect': {
|
case 'redirect': {
|
||||||
if (onRequest) {
|
if (onRequest) {
|
||||||
|
@ -143,7 +142,7 @@ export async function tryRenderRoute<MiddlewareReturnType = Response>(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error(`Couldn't find route of type [${routeType}]`);
|
throw new Error(`Couldn't find route of type [${renderContext.route.type}]`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ export interface SSROptions {
|
||||||
/** Request */
|
/** Request */
|
||||||
request: Request;
|
request: Request;
|
||||||
/** optional, in case we need to render something outside of a dev server */
|
/** optional, in case we need to render something outside of a dev server */
|
||||||
route?: RouteData;
|
route: RouteData;
|
||||||
/**
|
/**
|
||||||
* Optional middlewares
|
* Optional middlewares
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -5,10 +5,11 @@ import type {
|
||||||
ManifestData,
|
ManifestData,
|
||||||
MiddlewareResponseHandler,
|
MiddlewareResponseHandler,
|
||||||
RouteData,
|
RouteData,
|
||||||
|
RouteType,
|
||||||
SSRElement,
|
SSRElement,
|
||||||
SSRManifest,
|
SSRManifest,
|
||||||
} from '../@types/astro';
|
} from '../@types/astro';
|
||||||
import { attachToResponse } from '../core/cookies/index.js';
|
import { attachCookiesToResponse } from '../core/cookies/index.js';
|
||||||
import { AstroErrorData, isAstroError } from '../core/errors/index.js';
|
import { AstroErrorData, isAstroError } from '../core/errors/index.js';
|
||||||
import { warn } from '../core/logger/core.js';
|
import { warn } from '../core/logger/core.js';
|
||||||
import { loadMiddleware } from '../core/middleware/loadMiddleware.js';
|
import { loadMiddleware } from '../core/middleware/loadMiddleware.js';
|
||||||
|
@ -49,18 +50,18 @@ export interface MatchedRoute {
|
||||||
mod: ComponentInstance;
|
mod: ComponentInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCustom404Route(manifest: ManifestData): RouteData | undefined {
|
function getCustom404Route(manifestData: ManifestData): RouteData | undefined {
|
||||||
const route404 = /^\/404\/?$/;
|
const route404 = /^\/404\/?$/;
|
||||||
return manifest.routes.find((r) => route404.test(r.route));
|
return manifestData.routes.find((r) => route404.test(r.route));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function matchRoute(
|
export async function matchRoute(
|
||||||
pathname: string,
|
pathname: string,
|
||||||
env: DevelopmentEnvironment,
|
env: DevelopmentEnvironment,
|
||||||
manifest: ManifestData
|
manifestData: ManifestData
|
||||||
): Promise<MatchedRoute | undefined> {
|
): Promise<MatchedRoute | undefined> {
|
||||||
const { logging, settings, routeCache } = env;
|
const { logging, settings, routeCache } = env;
|
||||||
const matches = matchAllRoutes(pathname, manifest);
|
const matches = matchAllRoutes(pathname, manifestData);
|
||||||
const preloadedMatches = await getSortedPreloadedMatches({ env, matches, settings });
|
const preloadedMatches = await getSortedPreloadedMatches({ env, matches, settings });
|
||||||
|
|
||||||
for await (const { preloadedComponent, route: maybeRoute, filePath } of preloadedMatches) {
|
for await (const { preloadedComponent, route: maybeRoute, filePath } of preloadedMatches) {
|
||||||
|
@ -96,7 +97,7 @@ export async function matchRoute(
|
||||||
// build formats, and is necessary based on how the manifest tracks build targets.
|
// build formats, and is necessary based on how the manifest tracks build targets.
|
||||||
const altPathname = pathname.replace(/(index)?\.html$/, '');
|
const altPathname = pathname.replace(/(index)?\.html$/, '');
|
||||||
if (altPathname !== pathname) {
|
if (altPathname !== pathname) {
|
||||||
return await matchRoute(altPathname, env, manifest);
|
return await matchRoute(altPathname, env, manifestData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matches.length) {
|
if (matches.length) {
|
||||||
|
@ -112,7 +113,7 @@ export async function matchRoute(
|
||||||
}
|
}
|
||||||
|
|
||||||
log404(logging, pathname);
|
log404(logging, pathname);
|
||||||
const custom404 = getCustom404Route(manifest);
|
const custom404 = getCustom404Route(manifestData);
|
||||||
|
|
||||||
if (custom404) {
|
if (custom404) {
|
||||||
const filePath = new URL(`./${custom404.component}`, settings.config.root);
|
const filePath = new URL(`./${custom404.component}`, settings.config.root);
|
||||||
|
@ -216,7 +217,7 @@ export async function handleRoute({
|
||||||
});
|
});
|
||||||
const onRequest = options.middleware?.onRequest as MiddlewareResponseHandler | undefined;
|
const onRequest = options.middleware?.onRequest as MiddlewareResponseHandler | undefined;
|
||||||
|
|
||||||
const result = await tryRenderRoute(route.type, renderContext, env, mod, onRequest);
|
const result = await tryRenderRoute(renderContext, env, mod, onRequest);
|
||||||
if (isEndpointResult(result, route.type)) {
|
if (isEndpointResult(result, route.type)) {
|
||||||
if (result.type === 'response') {
|
if (result.type === 'response') {
|
||||||
if (result.response.headers.get('X-Astro-Response') === 'Not-Found') {
|
if (result.response.headers.get('X-Astro-Response') === 'Not-Found') {
|
||||||
|
@ -255,7 +256,7 @@ export async function handleRoute({
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
attachToResponse(response, result.cookies);
|
attachCookiesToResponse(response, result.cookies);
|
||||||
await writeWebResponse(incomingResponse, response);
|
await writeWebResponse(incomingResponse, response);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue