refactor: add pipeline concept (#8020)
This commit is contained in:
parent
924bef998e
commit
a39ff7ed6b
15 changed files with 279 additions and 78 deletions
|
@ -3,3 +3,16 @@
|
|||
Code that executes within the top-level Node context. Contains the main Astro logic for the `build` and `dev` commands, and also manages the Vite server and SSR.
|
||||
|
||||
[See CONTRIBUTING.md](../../../../CONTRIBUTING.md) for a code overview.
|
||||
|
||||
## Pipeline
|
||||
|
||||
The pipeline is an internal concept that describes how Astro pages are eventually created and rendered to the user.
|
||||
|
||||
Each pipeline has different requirements, criteria and quirks. Although, each pipeline must use the same underline functions, because
|
||||
the core of the pipeline is the same.
|
||||
|
||||
The core of the pipeline is rendering a generic route (page, endpoint or redirect) and returning a `Response`.
|
||||
When rendering a route, a pipeline must pass a `RenderContext` and `ComponentInstance`. The way these two information are
|
||||
computed doesn't concern the core of a pipeline. In fact, these types will be computed in different manner based on the type of pipeline.
|
||||
|
||||
Each consumer will decide how to handle a `Response`.
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import mime from 'mime';
|
||||
import type {
|
||||
EndpointHandler,
|
||||
ManifestData,
|
||||
MiddlewareEndpointHandler,
|
||||
RouteData,
|
||||
SSRElement,
|
||||
SSRManifest,
|
||||
} from '../../@types/astro';
|
||||
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 { error, type LogOptions } from '../logger/core.js';
|
||||
import {
|
||||
|
@ -16,12 +16,10 @@ import {
|
|||
removeTrailingForwardSlash,
|
||||
} from '../path.js';
|
||||
import { RedirectSinglePageBuiltModule } from '../redirects/index.js';
|
||||
import { isResponse } from '../render/core.js';
|
||||
import {
|
||||
createEnvironment,
|
||||
createRenderContext,
|
||||
tryRenderRoute,
|
||||
type Environment,
|
||||
type RenderContext,
|
||||
} from '../render/index.js';
|
||||
import { RouteCache } from '../render/route-cache.js';
|
||||
|
@ -32,6 +30,7 @@ import {
|
|||
} from '../render/ssr-element.js';
|
||||
import { matchRoute } from '../routing/match.js';
|
||||
import type { RouteInfo } from './types';
|
||||
import { EndpointNotFoundError, SSRRoutePipeline } from './ssrPipeline.js';
|
||||
export { deserializeManifest } from './common.js';
|
||||
|
||||
const clientLocalsSymbol = Symbol.for('astro.locals');
|
||||
|
@ -53,16 +52,15 @@ export class App {
|
|||
/**
|
||||
* The current environment of the application
|
||||
*/
|
||||
#env: Environment;
|
||||
#manifest: SSRManifest;
|
||||
#manifestData: ManifestData;
|
||||
#routeDataToRouteInfo: Map<RouteData, RouteInfo>;
|
||||
#encoder = new TextEncoder();
|
||||
#logging: LogOptions = {
|
||||
dest: consoleLogDestination,
|
||||
level: 'info',
|
||||
};
|
||||
#baseWithoutTrailingSlash: string;
|
||||
#pipeline: SSRRoutePipeline;
|
||||
|
||||
constructor(manifest: SSRManifest, streaming = true) {
|
||||
this.#manifest = manifest;
|
||||
|
@ -71,7 +69,7 @@ export class App {
|
|||
};
|
||||
this.#routeDataToRouteInfo = new Map(manifest.routes.map((route) => [route.routeData, route]));
|
||||
this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#manifest.base);
|
||||
this.#env = this.#createEnvironment(streaming);
|
||||
this.#pipeline = new SSRRoutePipeline(this.#createEnvironment(streaming));
|
||||
}
|
||||
|
||||
set setManifest(newManifest: SSRManifest) {
|
||||
|
@ -163,19 +161,21 @@ export class App {
|
|||
);
|
||||
let response;
|
||||
try {
|
||||
response = await tryRenderRoute(
|
||||
routeData.type,
|
||||
renderContext,
|
||||
this.#env,
|
||||
pageModule,
|
||||
mod.onRequest
|
||||
);
|
||||
// NOTE: ideally we could set the middleware function just once, but we don't have the infrastructure to that yet
|
||||
if (mod.onRequest) {
|
||||
this.#pipeline.setMiddlewareFunction(mod.onRequest as MiddlewareEndpointHandler);
|
||||
}
|
||||
response = await this.#pipeline.renderRoute(renderContext, pageModule);
|
||||
} catch (err: any) {
|
||||
error(this.#logging, 'ssr', err.stack || err.message || String(err));
|
||||
return this.#renderError(request, { status: 500 });
|
||||
if (err instanceof EndpointNotFoundError) {
|
||||
return this.#renderError(request, { status: 404, response: err.originalResponse });
|
||||
} else {
|
||||
error(this.#logging, 'ssr', err.stack || err.message || String(err));
|
||||
return this.#renderError(request, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
if (isResponse(response, routeData.type)) {
|
||||
if (SSRRoutePipeline.isResponse(response, routeData.type)) {
|
||||
if (STATUS_CODES.has(response.status)) {
|
||||
return this.#renderError(request, {
|
||||
response,
|
||||
|
@ -184,35 +184,8 @@ export class App {
|
|||
}
|
||||
Reflect.set(response, responseSentSymbol, true);
|
||||
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) {
|
||||
|
@ -238,7 +211,7 @@ export class App {
|
|||
pathname,
|
||||
route: routeData,
|
||||
status,
|
||||
env: this.#env,
|
||||
env: this.#pipeline.env,
|
||||
mod: handler as any,
|
||||
});
|
||||
} else {
|
||||
|
@ -272,7 +245,7 @@ export class App {
|
|||
route: routeData,
|
||||
status,
|
||||
mod,
|
||||
env: this.#env,
|
||||
env: this.#pipeline.env,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -301,9 +274,8 @@ export class App {
|
|||
);
|
||||
const page = (await mod.page()) as any;
|
||||
const response = (await tryRenderRoute(
|
||||
'page', // this is hardcoded to ensure proper behavior for missing endpoints
|
||||
newRenderContext,
|
||||
this.#env,
|
||||
this.#pipeline.env,
|
||||
page
|
||||
)) as Response;
|
||||
return this.#mergeResponses(response, originalResponse);
|
||||
|
|
54
packages/astro/src/core/app/ssrPipeline.ts
Normal file
54
packages/astro/src/core/app/ssrPipeline.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
import type { Environment } from '../render';
|
||||
import type { EndpointCallResult } from '../endpoint/index.js';
|
||||
import mime from 'mime';
|
||||
import { attachCookiesToResponse } from '../cookies/index.js';
|
||||
import { Pipeline } from '../pipeline.js';
|
||||
|
||||
/**
|
||||
* Thrown when an endpoint contains a response with the header "X-Astro-Response" === 'Not-Found'
|
||||
*/
|
||||
export class EndpointNotFoundError extends Error {
|
||||
originalResponse: Response;
|
||||
constructor(originalResponse: Response) {
|
||||
super();
|
||||
this.originalResponse = originalResponse;
|
||||
}
|
||||
}
|
||||
|
||||
export class SSRRoutePipeline extends Pipeline {
|
||||
encoder = new TextEncoder();
|
||||
|
||||
constructor(env: Environment) {
|
||||
super(env);
|
||||
this.setEndpointHandler(this.#ssrEndpointHandler);
|
||||
}
|
||||
|
||||
// This function is responsible for handling the result coming from an endpoint.
|
||||
async #ssrEndpointHandler(request: Request, response: EndpointCallResult): Promise<Response> {
|
||||
if (response.type === 'response') {
|
||||
if (response.response.headers.get('X-Astro-Response') === 'Not-Found') {
|
||||
throw new EndpointNotFoundError(response.response);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -547,7 +547,7 @@ async function generatePath(
|
|||
|
||||
let response;
|
||||
try {
|
||||
response = await tryRenderRoute(pageData.route.type, renderContext, env, mod, onRequest);
|
||||
response = await tryRenderRoute(renderContext, env, mod, onRequest);
|
||||
} catch (err) {
|
||||
if (!AstroError.is(err) && !(err as SSRError).id && typeof err === 'object') {
|
||||
(err as SSRError).id = pageData.component;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import type { Node as ESTreeNode } from 'estree-walker';
|
||||
import type { ModuleInfo, PluginContext } from 'rollup';
|
||||
import type { Plugin as VitePlugin } from 'vite';
|
||||
import type { PluginMetadata as AstroPluginMetadata } from '../../../vite-plugin-astro/types';
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
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');
|
||||
|
||||
export function attachToResponse(response: Response, cookies: AstroCookies) {
|
||||
export function attachCookiesToResponse(response: Response, cookies: AstroCookies) {
|
||||
Reflect.set(response, astroCookiesSymbol, cookies);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import type {
|
|||
import type { Environment, RenderContext } from '../render/index';
|
||||
import { renderEndpoint } from '../../runtime/server/index.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 { warn } from '../logger/core.js';
|
||||
import { callMiddleware } from '../middleware/callMiddleware.js';
|
||||
|
@ -125,7 +125,7 @@ export async function callEndpoint<MiddlewareResult = Response | EndpointOutput>
|
|||
}
|
||||
|
||||
if (response instanceof Response) {
|
||||
attachToResponse(response, context.cookies);
|
||||
attachCookiesToResponse(response, context.cookies);
|
||||
return {
|
||||
type: 'response',
|
||||
response,
|
||||
|
|
156
packages/astro/src/core/pipeline.ts
Normal file
156
packages/astro/src/core/pipeline.ts
Normal file
|
@ -0,0 +1,156 @@
|
|||
import { type RenderContext, type Environment } from './render/index.js';
|
||||
import { type EndpointCallResult, callEndpoint, createAPIContext } from './endpoint/index.js';
|
||||
import type {
|
||||
MiddlewareHandler,
|
||||
MiddlewareResponseHandler,
|
||||
ComponentInstance,
|
||||
MiddlewareEndpointHandler,
|
||||
RouteType,
|
||||
EndpointHandler,
|
||||
} from '../@types/astro';
|
||||
import { callMiddleware } from './middleware/callMiddleware.js';
|
||||
import { renderPage } from './render/core.js';
|
||||
|
||||
type EndpointResultHandler = (
|
||||
originalRequest: Request,
|
||||
result: EndpointCallResult
|
||||
) => Promise<Response> | Response;
|
||||
|
||||
/**
|
||||
* This is the basic class of a pipeline.
|
||||
*
|
||||
* Check the {@link ./README.md|README} for more information about the pipeline.
|
||||
*/
|
||||
export class Pipeline {
|
||||
env: Environment;
|
||||
onRequest?: MiddlewareEndpointHandler;
|
||||
/**
|
||||
* The handler accepts the *original* `Request` and result returned by the endpoint.
|
||||
* It must return a `Response`.
|
||||
*/
|
||||
endpointHandler?: EndpointResultHandler;
|
||||
|
||||
/**
|
||||
* When creating a pipeline, an environment is mandatory.
|
||||
* The environment won't change for the whole lifetime of the pipeline.
|
||||
*/
|
||||
constructor(env: Environment) {
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
/**
|
||||
* When rendering a route, an "endpoint" will a type that needs to be handled and transformed into a `Response`.
|
||||
*
|
||||
* Each consumer might have different needs; use this function to set up the handler.
|
||||
*/
|
||||
setEndpointHandler(handler: EndpointResultHandler) {
|
||||
this.endpointHandler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* A middleware function that will be called before each request.
|
||||
*/
|
||||
setMiddlewareFunction(onRequest: MiddlewareEndpointHandler) {
|
||||
this.onRequest = onRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main function of the pipeline. Use this function to render any route known to Astro;
|
||||
*/
|
||||
async renderRoute(
|
||||
renderContext: RenderContext,
|
||||
componentInstance: ComponentInstance
|
||||
): Promise<Response> {
|
||||
const result = await this.#tryRenderRoute(
|
||||
renderContext,
|
||||
this.env,
|
||||
componentInstance,
|
||||
this.onRequest
|
||||
);
|
||||
if (Pipeline.isEndpointResult(result, renderContext.route.type)) {
|
||||
if (!this.endpointHandler) {
|
||||
throw new Error(
|
||||
'You created a pipeline that does not know how to handle the result coming from an endpoint.'
|
||||
);
|
||||
}
|
||||
return this.endpointHandler(renderContext.request, result);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* It attempts to render a route. A route can be a:
|
||||
* - page
|
||||
* - redirect
|
||||
* - endpoint
|
||||
*
|
||||
* ## Errors
|
||||
*
|
||||
* It throws an error if the page can't be rendered.
|
||||
*/
|
||||
async #tryRenderRoute<MiddlewareReturnType = Response>(
|
||||
renderContext: Readonly<RenderContext>,
|
||||
env: Readonly<Environment>,
|
||||
mod: Readonly<ComponentInstance>,
|
||||
onRequest?: MiddlewareHandler<MiddlewareReturnType>
|
||||
): Promise<Response | EndpointCallResult> {
|
||||
const apiContext = createAPIContext({
|
||||
request: renderContext.request,
|
||||
params: renderContext.params,
|
||||
props: renderContext.props,
|
||||
site: env.site,
|
||||
adapterName: env.adapterName,
|
||||
});
|
||||
|
||||
switch (renderContext.route.type) {
|
||||
case 'page':
|
||||
case 'redirect': {
|
||||
if (onRequest) {
|
||||
return await callMiddleware<Response>(
|
||||
env.logging,
|
||||
onRequest as MiddlewareResponseHandler,
|
||||
apiContext,
|
||||
() => {
|
||||
return renderPage({
|
||||
mod,
|
||||
renderContext,
|
||||
env,
|
||||
cookies: apiContext.cookies,
|
||||
});
|
||||
}
|
||||
);
|
||||
} else {
|
||||
return await renderPage({
|
||||
mod,
|
||||
renderContext,
|
||||
env,
|
||||
cookies: apiContext.cookies,
|
||||
});
|
||||
}
|
||||
}
|
||||
case 'endpoint': {
|
||||
const result = await callEndpoint(
|
||||
mod as any as EndpointHandler,
|
||||
env,
|
||||
renderContext,
|
||||
onRequest
|
||||
);
|
||||
return result;
|
||||
}
|
||||
default:
|
||||
throw new Error(`Couldn't find route of type [${renderContext.route.type}]`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this function
|
||||
*/
|
||||
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');
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ export interface RenderContext {
|
|||
links?: Set<SSRElement>;
|
||||
styles?: Set<SSRElement>;
|
||||
componentMetadata?: SSRResult['componentMetadata'];
|
||||
route?: RouteData;
|
||||
route: RouteData;
|
||||
status?: number;
|
||||
params: Params;
|
||||
props: Props;
|
||||
|
@ -32,6 +32,7 @@ export interface RenderContext {
|
|||
export type CreateRenderContextArgs = Partial<
|
||||
Omit<RenderContext, 'params' | 'props' | 'locals'>
|
||||
> & {
|
||||
route: RouteData;
|
||||
request: RenderContext['request'];
|
||||
mod: ComponentInstance;
|
||||
env: Environment;
|
||||
|
|
|
@ -7,7 +7,7 @@ import type {
|
|||
RouteType,
|
||||
} from '../../@types/astro';
|
||||
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 { callMiddleware } from '../middleware/callMiddleware.js';
|
||||
import { redirectRouteGenerate, redirectRouteStatus, routeIsRedirect } from '../redirects/index.js';
|
||||
|
@ -22,7 +22,7 @@ export type RenderPage = {
|
|||
cookies: AstroCookies;
|
||||
};
|
||||
|
||||
async function renderPage({ mod, renderContext, env, cookies }: RenderPage) {
|
||||
export async function renderPage({ mod, renderContext, env, cookies }: RenderPage) {
|
||||
if (routeIsRedirect(renderContext.route)) {
|
||||
return new Response(null, {
|
||||
status: redirectRouteStatus(renderContext.route, renderContext.request.method),
|
||||
|
@ -70,7 +70,7 @@ async function renderPage({ mod, renderContext, env, cookies }: RenderPage) {
|
|||
// If there is an Astro.cookies instance, attach it to the response so that
|
||||
// adapters can grab the Set-Cookie headers.
|
||||
if (result.cookies) {
|
||||
attachToResponse(response, result.cookies);
|
||||
attachCookiesToResponse(response, result.cookies);
|
||||
}
|
||||
|
||||
return response;
|
||||
|
@ -85,9 +85,9 @@ async function renderPage({ mod, renderContext, env, cookies }: RenderPage) {
|
|||
* ## Errors
|
||||
*
|
||||
* It throws an error if the page can't be rendered.
|
||||
* @deprecated Use the pipeline instead
|
||||
*/
|
||||
export async function tryRenderRoute<MiddlewareReturnType = Response>(
|
||||
routeType: RouteType,
|
||||
renderContext: Readonly<RenderContext>,
|
||||
env: Readonly<Environment>,
|
||||
mod: Readonly<ComponentInstance>,
|
||||
|
@ -101,7 +101,7 @@ export async function tryRenderRoute<MiddlewareReturnType = Response>(
|
|||
adapterName: env.adapterName,
|
||||
});
|
||||
|
||||
switch (routeType) {
|
||||
switch (renderContext.route.type) {
|
||||
case 'page':
|
||||
case 'redirect': {
|
||||
if (onRequest) {
|
||||
|
@ -137,7 +137,7 @@ export async function tryRenderRoute<MiddlewareReturnType = Response>(
|
|||
return result;
|
||||
}
|
||||
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;
|
||||
/** optional, in case we need to render something outside of a dev server */
|
||||
route?: RouteData;
|
||||
route: RouteData;
|
||||
/**
|
||||
* Optional middlewares
|
||||
*/
|
||||
|
|
|
@ -8,7 +8,7 @@ import type {
|
|||
SSRElement,
|
||||
SSRManifest,
|
||||
} 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 { warn } from '../core/logger/core.js';
|
||||
import { loadMiddleware } from '../core/middleware/loadMiddleware.js';
|
||||
|
@ -49,18 +49,18 @@ export interface MatchedRoute {
|
|||
mod: ComponentInstance;
|
||||
}
|
||||
|
||||
function getCustom404Route(manifest: ManifestData): RouteData | undefined {
|
||||
function getCustom404Route(manifestData: ManifestData): RouteData | undefined {
|
||||
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(
|
||||
pathname: string,
|
||||
env: DevelopmentEnvironment,
|
||||
manifest: ManifestData
|
||||
manifestData: ManifestData
|
||||
): Promise<MatchedRoute | undefined> {
|
||||
const { logging, settings, routeCache } = env;
|
||||
const matches = matchAllRoutes(pathname, manifest);
|
||||
const matches = matchAllRoutes(pathname, manifestData);
|
||||
const preloadedMatches = await getSortedPreloadedMatches({ env, matches, settings });
|
||||
|
||||
for await (const { preloadedComponent, route: maybeRoute, filePath } of preloadedMatches) {
|
||||
|
@ -96,7 +96,7 @@ export async function matchRoute(
|
|||
// build formats, and is necessary based on how the manifest tracks build targets.
|
||||
const altPathname = pathname.replace(/(index)?\.html$/, '');
|
||||
if (altPathname !== pathname) {
|
||||
return await matchRoute(altPathname, env, manifest);
|
||||
return await matchRoute(altPathname, env, manifestData);
|
||||
}
|
||||
|
||||
if (matches.length) {
|
||||
|
@ -112,7 +112,7 @@ export async function matchRoute(
|
|||
}
|
||||
|
||||
log404(logging, pathname);
|
||||
const custom404 = getCustom404Route(manifest);
|
||||
const custom404 = getCustom404Route(manifestData);
|
||||
|
||||
if (custom404) {
|
||||
const filePath = new URL(`./${custom404.component}`, settings.config.root);
|
||||
|
@ -216,7 +216,7 @@ export async function handleRoute({
|
|||
});
|
||||
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 (result.type === 'response') {
|
||||
if (result.response.headers.get('X-Astro-Response') === 'Not-Found') {
|
||||
|
@ -255,7 +255,7 @@ export async function handleRoute({
|
|||
},
|
||||
}
|
||||
);
|
||||
attachToResponse(response, result.cookies);
|
||||
attachCookiesToResponse(response, result.cookies);
|
||||
await writeWebResponse(incomingResponse, response);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -90,13 +90,14 @@ describe('core/render', () => {
|
|||
|
||||
const PageModule = createAstroModule(Page);
|
||||
const ctx = await createRenderContext({
|
||||
route: { type: 'page', pathname: '/index' },
|
||||
request: new Request('http://example.com/'),
|
||||
links: [{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' }],
|
||||
mod: PageModule,
|
||||
env,
|
||||
});
|
||||
|
||||
const response = await tryRenderRoute('page', ctx, env, PageModule);
|
||||
const response = await tryRenderRoute(ctx, env, PageModule);
|
||||
|
||||
const html = await response.text();
|
||||
const $ = cheerio.load(html);
|
||||
|
@ -170,13 +171,14 @@ describe('core/render', () => {
|
|||
|
||||
const PageModule = createAstroModule(Page);
|
||||
const ctx = await createRenderContext({
|
||||
route: { type: 'page', pathname: '/index' },
|
||||
request: new Request('http://example.com/'),
|
||||
links: [{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' }],
|
||||
env,
|
||||
mod: PageModule,
|
||||
});
|
||||
|
||||
const response = await tryRenderRoute('page', ctx, env, PageModule);
|
||||
const response = await tryRenderRoute(ctx, env, PageModule);
|
||||
const html = await response.text();
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
|
@ -216,13 +218,14 @@ describe('core/render', () => {
|
|||
|
||||
const PageModule = createAstroModule(Page);
|
||||
const ctx = await createRenderContext({
|
||||
route: { type: 'page', pathname: '/index' },
|
||||
request: new Request('http://example.com/'),
|
||||
links: [{ name: 'link', props: { rel: 'stylesheet', href: '/main.css' }, children: '' }],
|
||||
env,
|
||||
mod: PageModule,
|
||||
});
|
||||
|
||||
const response = await tryRenderRoute('page', ctx, env, PageModule);
|
||||
const response = await tryRenderRoute(ctx, env, PageModule);
|
||||
const html = await response.text();
|
||||
const $ = cheerio.load(html);
|
||||
|
||||
|
|
|
@ -45,12 +45,13 @@ describe('core/render', () => {
|
|||
|
||||
const mod = createAstroModule(Page);
|
||||
const ctx = await createRenderContext({
|
||||
route: { type: 'page', pathname: '/index' },
|
||||
request: new Request('http://example.com/'),
|
||||
env,
|
||||
mod,
|
||||
});
|
||||
|
||||
const response = await tryRenderRoute('page', ctx, env, mod);
|
||||
const response = await tryRenderRoute(ctx, env, mod);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
|
||||
|
@ -90,11 +91,12 @@ describe('core/render', () => {
|
|||
|
||||
const mod = createAstroModule(Page);
|
||||
const ctx = await createRenderContext({
|
||||
route: { type: 'page', pathname: '/index' },
|
||||
request: new Request('http://example.com/'),
|
||||
env,
|
||||
mod,
|
||||
});
|
||||
const response = await tryRenderRoute('page', ctx, env, mod);
|
||||
const response = await tryRenderRoute(ctx, env, mod);
|
||||
|
||||
expect(response.status).to.equal(200);
|
||||
|
||||
|
@ -115,12 +117,13 @@ describe('core/render', () => {
|
|||
|
||||
const mod = createAstroModule(Page);
|
||||
const ctx = await createRenderContext({
|
||||
route: { type: 'page', pathname: '/index' },
|
||||
request: new Request('http://example.com/'),
|
||||
env,
|
||||
mod,
|
||||
});
|
||||
|
||||
const response = await tryRenderRoute('page', ctx, env, mod);
|
||||
const response = await tryRenderRoute(ctx, env, mod);
|
||||
|
||||
try {
|
||||
await response.text();
|
||||
|
|
Loading…
Reference in a new issue