summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/astro/src/core/app/index.ts13
-rw-r--r--packages/astro/src/core/pipeline.ts2
-rw-r--r--packages/astro/src/core/render/environment.ts8
-rw-r--r--packages/astro/src/core/render/index.ts11
-rw-r--r--packages/astro/src/core/render/renderer.ts10
-rw-r--r--packages/astro/src/prerender/routing.ts16
-rw-r--r--packages/astro/src/vite-plugin-astro-server/devPipeline.ts128
-rw-r--r--packages/astro/src/vite-plugin-astro-server/environment.ts12
-rw-r--r--packages/astro/src/vite-plugin-astro-server/index.ts16
-rw-r--r--packages/astro/src/vite-plugin-astro-server/plugin.ts8
-rw-r--r--packages/astro/src/vite-plugin-astro-server/request.ts20
-rw-r--r--packages/astro/src/vite-plugin-astro-server/route.ts167
-rw-r--r--packages/astro/test/units/routing/route-matching.test.js10
-rw-r--r--packages/astro/test/units/vite-plugin-astro-server/request.test.js30
14 files changed, 255 insertions, 196 deletions
diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts
index 92f671b85..86b9c9d41 100644
--- a/packages/astro/src/core/app/index.ts
+++ b/packages/astro/src/core/app/index.ts
@@ -16,12 +16,7 @@ import {
removeTrailingForwardSlash,
} from '../path.js';
import { RedirectSinglePageBuiltModule } from '../redirects/index.js';
-import {
- createEnvironment,
- createRenderContext,
- tryRenderRoute,
- type RenderContext,
-} from '../render/index.js';
+import { createEnvironment, createRenderContext, type RenderContext } from '../render/index.js';
import { RouteCache } from '../render/route-cache.js';
import {
createAssetLink,
@@ -282,11 +277,7 @@ export class App {
status
);
const page = (await mod.page()) as any;
- const response = (await tryRenderRoute(
- newRenderContext,
- this.#pipeline.env,
- page
- )) as Response;
+ const response = await this.#pipeline.renderRoute(newRenderContext, page);
return this.#mergeResponses(response, originalResponse);
} catch {}
}
diff --git a/packages/astro/src/core/pipeline.ts b/packages/astro/src/core/pipeline.ts
index b5c66517a..42ed7d5da 100644
--- a/packages/astro/src/core/pipeline.ts
+++ b/packages/astro/src/core/pipeline.ts
@@ -59,7 +59,7 @@ export class Pipeline {
/**
* Returns the current environment
*/
- getEnvironment() {
+ getEnvironment(): Readonly<Environment> {
return this.env;
}
diff --git a/packages/astro/src/core/render/environment.ts b/packages/astro/src/core/render/environment.ts
index 32dfb454b..f38d98551 100644
--- a/packages/astro/src/core/render/environment.ts
+++ b/packages/astro/src/core/render/environment.ts
@@ -1,6 +1,5 @@
-import type { AstroSettings, RuntimeMode, SSRLoadedRenderer } from '../../@types/astro';
+import type { RuntimeMode, SSRLoadedRenderer } from '../../@types/astro';
import type { LogOptions } from '../logger/core.js';
-import type { ModuleLoader } from '../module-loader';
import type { RouteCache } from './route-cache.js';
/**
@@ -38,8 +37,3 @@ export type CreateEnvironmentArgs = Environment;
export function createEnvironment(options: CreateEnvironmentArgs): Environment {
return options;
}
-
-export type DevelopmentEnvironment = Environment & {
- loader: ModuleLoader;
- settings: AstroSettings;
-};
diff --git a/packages/astro/src/core/render/index.ts b/packages/astro/src/core/render/index.ts
index 20b964fa7..f39c02ae2 100644
--- a/packages/astro/src/core/render/index.ts
+++ b/packages/astro/src/core/render/index.ts
@@ -1,18 +1,17 @@
import type { AstroMiddlewareInstance, ComponentInstance, RouteData } from '../../@types/astro';
-import type { DevelopmentEnvironment } from './environment';
-
export { createRenderContext } from './context.js';
export type { RenderContext } from './context.js';
export { tryRenderRoute } from './core.js';
-export type { Environment } from './environment';
+import type { Environment } from './environment';
export { createEnvironment } from './environment.js';
export { getParamsAndProps } from './params-and-props.js';
-export { loadRenderer, loadRenderers } from './renderer.js';
-export type { DevelopmentEnvironment };
+export { loadRenderer } from './renderer.js';
+
+export type { Environment };
export interface SSROptions {
/** The environment instance */
- env: DevelopmentEnvironment;
+ env: Environment;
/** location of file on disk */
filePath: URL;
/** the web request (needed for dynamic routes) */
diff --git a/packages/astro/src/core/render/renderer.ts b/packages/astro/src/core/render/renderer.ts
index 8e5e97202..e64a27ba5 100644
--- a/packages/astro/src/core/render/renderer.ts
+++ b/packages/astro/src/core/render/renderer.ts
@@ -1,14 +1,6 @@
-import type { AstroRenderer, AstroSettings, SSRLoadedRenderer } from '../../@types/astro';
+import type { AstroRenderer, SSRLoadedRenderer } from '../../@types/astro';
import type { ModuleLoader } from '../module-loader/index.js';
-export async function loadRenderers(
- settings: AstroSettings,
- moduleLoader: ModuleLoader
-): Promise<SSRLoadedRenderer[]> {
- const renderers = await Promise.all(settings.renderers.map((r) => loadRenderer(r, moduleLoader)));
- return renderers.filter(Boolean) as SSRLoadedRenderer[];
-}
-
export async function loadRenderer(
renderer: AstroRenderer,
moduleLoader: ModuleLoader
diff --git a/packages/astro/src/prerender/routing.ts b/packages/astro/src/prerender/routing.ts
index a3b3c0d54..2fcfe207b 100644
--- a/packages/astro/src/prerender/routing.ts
+++ b/packages/astro/src/prerender/routing.ts
@@ -1,22 +1,22 @@
import type { AstroSettings, ComponentInstance, RouteData } from '../@types/astro';
import { RedirectComponentInstance, routeIsRedirect } from '../core/redirects/index.js';
-import type { DevelopmentEnvironment } from '../core/render';
import { preload } from '../vite-plugin-astro-server/index.js';
import { getPrerenderStatus } from './metadata.js';
+import type DevPipeline from '../vite-plugin-astro-server/devPipeline';
type GetSortedPreloadedMatchesParams = {
- env: DevelopmentEnvironment;
+ pipeline: DevPipeline;
matches: RouteData[];
settings: AstroSettings;
};
export async function getSortedPreloadedMatches({
- env,
+ pipeline,
matches,
settings,
}: GetSortedPreloadedMatchesParams) {
return (
await preloadAndSetPrerenderStatus({
- env,
+ pipeline,
matches,
settings,
})
@@ -24,7 +24,7 @@ export async function getSortedPreloadedMatches({
}
type PreloadAndSetPrerenderStatusParams = {
- env: DevelopmentEnvironment;
+ pipeline: DevPipeline;
matches: RouteData[];
settings: AstroSettings;
};
@@ -36,7 +36,7 @@ type PreloadAndSetPrerenderStatusResult = {
};
async function preloadAndSetPrerenderStatus({
- env,
+ pipeline,
matches,
settings,
}: PreloadAndSetPrerenderStatusParams): Promise<PreloadAndSetPrerenderStatusResult[]> {
@@ -52,12 +52,12 @@ async function preloadAndSetPrerenderStatus({
};
}
- const preloadedComponent = await preload({ env, filePath });
+ const preloadedComponent = await preload({ pipeline, filePath });
// gets the prerender metadata set by the `astro:scanner` vite plugin
const prerenderStatus = getPrerenderStatus({
filePath,
- loader: env.loader,
+ loader: pipeline.getModuleLoader(),
});
if (prerenderStatus !== undefined) {
diff --git a/packages/astro/src/vite-plugin-astro-server/devPipeline.ts b/packages/astro/src/vite-plugin-astro-server/devPipeline.ts
new file mode 100644
index 000000000..eae6cc1c6
--- /dev/null
+++ b/packages/astro/src/vite-plugin-astro-server/devPipeline.ts
@@ -0,0 +1,128 @@
+import { Pipeline } from '../core/pipeline.js';
+import type { AstroConfig, AstroSettings, RouteData } from '../@types/astro';
+import type { ModuleLoader } from '../core/module-loader';
+import type { Environment } from '../core/render';
+import { createEnvironment, loadRenderer } from '../core/render/index.js';
+import { createResolve } from './resolve.js';
+import { RouteCache } from '../core/render/route-cache.js';
+import { isServerLikeOutput } from '../prerender/utils.js';
+import type { RuntimeMode, SSRManifest, SSRLoadedRenderer } from '../@types/astro';
+import type { LogOptions } from '../core/logger/core';
+import { Logger } from '../core/logger/core.js';
+import type { EndpointCallResult } from '../core/endpoint/index.js';
+import mime from 'mime';
+import { attachCookiesToResponse } from '../core/cookies/index.js';
+
+export default class DevPipeline extends Pipeline {
+ #settings: AstroSettings;
+ #loader: ModuleLoader;
+ #devLogger: Logger;
+ #currentMatchedRoute: RouteData | undefined;
+
+ constructor({
+ manifest,
+ logging,
+ settings,
+ loader,
+ }: {
+ manifest: SSRManifest;
+ logging: LogOptions;
+ settings: AstroSettings;
+ loader: ModuleLoader;
+ }) {
+ const env = DevPipeline.createDevelopmentEnvironment(manifest, settings, logging, loader);
+ super(env);
+ this.#devLogger = new Logger(logging);
+ this.#settings = settings;
+ this.#loader = loader;
+ this.setEndpointHandler(this.#handleEndpointResult);
+ }
+
+ setCurrentMatchedRoute(route: RouteData) {
+ this.#currentMatchedRoute = route;
+ }
+
+ clearRouteCache() {
+ this.env.routeCache.clearAll();
+ }
+
+ getSettings(): Readonly<AstroSettings> {
+ return this.#settings;
+ }
+
+ getConfig(): Readonly<AstroConfig> {
+ return this.#settings.config;
+ }
+
+ getModuleLoader(): Readonly<ModuleLoader> {
+ return this.#loader;
+ }
+
+ get logger(): Readonly<Logger> {
+ return this.#devLogger;
+ }
+
+ async loadRenderers() {
+ const renderers = await Promise.all(
+ this.#settings.renderers.map((r) => loadRenderer(r, this.#loader))
+ );
+ this.env.renderers = renderers.filter(Boolean) as SSRLoadedRenderer[];
+ }
+
+ static createDevelopmentEnvironment(
+ manifest: SSRManifest,
+ settings: AstroSettings,
+ logging: LogOptions,
+ loader: ModuleLoader
+ ): Environment {
+ const mode: RuntimeMode = 'development';
+
+ return createEnvironment({
+ adapterName: manifest.adapterName,
+ logging,
+ mode,
+ // This will be overridden in the dev server
+ renderers: [],
+ clientDirectives: manifest.clientDirectives,
+ compressHTML: manifest.compressHTML,
+ resolve: createResolve(loader, settings.config.root),
+ routeCache: new RouteCache(logging, mode),
+ site: manifest.site,
+ ssr: isServerLikeOutput(settings.config),
+ streaming: true,
+ });
+ }
+
+ async #handleEndpointResult(_: Request, result: EndpointCallResult): Promise<Response> {
+ if (result.type === 'simple') {
+ if (!this.#currentMatchedRoute) {
+ throw new Error(
+ 'In development mode, you must set the current matched route before handling a endpoint.'
+ );
+ }
+ let contentType = 'text/plain';
+ // Dynamic routes don't include `route.pathname`, so synthesize a path for these (e.g. 'src/pages/[slug].svg')
+ const filepath =
+ this.#currentMatchedRoute.pathname ||
+ this.#currentMatchedRoute.segments
+ .map((segment) => segment.map((p) => p.content).join(''))
+ .join('/');
+ const computedMimeType = mime.getType(filepath);
+ if (computedMimeType) {
+ contentType = computedMimeType;
+ }
+ const response = new Response(
+ result.encoding !== 'binary' ? Buffer.from(result.body, result.encoding) : result.body,
+ {
+ status: 200,
+ headers: {
+ 'Content-Type': `${contentType};charset=utf-8`,
+ },
+ }
+ );
+ attachCookiesToResponse(response, result.cookies);
+ return response;
+ }
+ return result.response;
+ }
+}
diff --git a/packages/astro/src/vite-plugin-astro-server/environment.ts b/packages/astro/src/vite-plugin-astro-server/environment.ts
index ce7b92662..010c1b96a 100644
--- a/packages/astro/src/vite-plugin-astro-server/environment.ts
+++ b/packages/astro/src/vite-plugin-astro-server/environment.ts
@@ -1,7 +1,7 @@
import type { AstroSettings, RuntimeMode, SSRManifest } from '../@types/astro.js';
import type { LogOptions } from '../core/logger/core.js';
import type { ModuleLoader } from '../core/module-loader';
-import type { DevelopmentEnvironment } from '../core/render';
+import type { Environment } from '../core/render';
import { createEnvironment } from '../core/render/index.js';
import { RouteCache } from '../core/render/route-cache.js';
import { isServerLikeOutput } from '../prerender/utils.js';
@@ -12,9 +12,9 @@ export function createDevelopmentEnvironment(
settings: AstroSettings,
logging: LogOptions,
loader: ModuleLoader
-): DevelopmentEnvironment {
+): Environment {
const mode: RuntimeMode = 'development';
- let env = createEnvironment({
+ return createEnvironment({
adapterName: manifest.adapterName,
logging,
mode,
@@ -28,10 +28,4 @@ export function createDevelopmentEnvironment(
ssr: isServerLikeOutput(settings.config),
streaming: true,
});
-
- return {
- ...env,
- loader,
- settings,
- };
}
diff --git a/packages/astro/src/vite-plugin-astro-server/index.ts b/packages/astro/src/vite-plugin-astro-server/index.ts
index 17302ba41..b02c57e1c 100644
--- a/packages/astro/src/vite-plugin-astro-server/index.ts
+++ b/packages/astro/src/vite-plugin-astro-server/index.ts
@@ -1,26 +1,22 @@
import type { ComponentInstance } from '../@types/astro.js';
import { enhanceViteSSRError } from '../core/errors/dev/index.js';
import { AggregateError, CSSError, MarkdownError } from '../core/errors/index.js';
-import type { DevelopmentEnvironment } from '../core/render/environment';
-import { loadRenderers } from '../core/render/index.js';
import { viteID } from '../core/util.js';
+import type DevPipeline from './devPipeline';
export async function preload({
- env,
+ pipeline,
filePath,
}: {
- env: DevelopmentEnvironment;
+ pipeline: DevPipeline;
filePath: URL;
}): Promise<ComponentInstance> {
// Important: This needs to happen first, in case a renderer provides polyfills.
- const renderers = await loadRenderers(env.settings, env.loader);
- // Override the environment's renderers. This ensures that if renderers change (HMR)
- // The new instances are passed through.
- env.renderers = renderers;
+ await pipeline.loadRenderers();
try {
// Load the module from the Vite SSR Runtime.
- const mod = (await env.loader.import(viteID(filePath))) as ComponentInstance;
+ const mod = (await pipeline.getModuleLoader().import(viteID(filePath))) as ComponentInstance;
return mod;
} catch (error) {
@@ -29,7 +25,7 @@ export async function preload({
throw error;
}
- throw enhanceViteSSRError({ error, filePath, loader: env.loader });
+ throw enhanceViteSSRError({ error, filePath, loader: pipeline.getModuleLoader() });
}
}
diff --git a/packages/astro/src/vite-plugin-astro-server/plugin.ts b/packages/astro/src/vite-plugin-astro-server/plugin.ts
index dfaf976bf..262d50148 100644
--- a/packages/astro/src/vite-plugin-astro-server/plugin.ts
+++ b/packages/astro/src/vite-plugin-astro-server/plugin.ts
@@ -7,8 +7,8 @@ import { createViteLoader } from '../core/module-loader/index.js';
import { createRouteManifest } from '../core/routing/index.js';
import { baseMiddleware } from './base.js';
import { createController } from './controller.js';
-import { createDevelopmentEnvironment } from './environment.js';
import { handleRequest } from './request.js';
+import DevPipeline from './devPipeline.js';
export interface AstroPluginOptions {
settings: AstroSettings;
@@ -26,13 +26,13 @@ export default function createVitePluginAstroServer({
configureServer(viteServer) {
const loader = createViteLoader(viteServer);
const manifest = createDevelopmentManifest(settings);
- const env = createDevelopmentEnvironment(manifest, settings, logging, loader);
+ const pipeline = new DevPipeline({ logging, manifest, settings, loader });
let manifestData: ManifestData = createRouteManifest({ settings, fsMod }, logging);
const controller = createController({ loader });
/** rebuild the route cache + manifest, as needed. */
function rebuildManifest(needsManifestRebuild: boolean) {
- env.routeCache.clearAll();
+ pipeline.clearRouteCache();
if (needsManifestRebuild) {
manifestData = createRouteManifest({ settings }, logging);
}
@@ -57,7 +57,7 @@ export default function createVitePluginAstroServer({
return;
}
handleRequest({
- env,
+ pipeline,
manifestData,
controller,
incomingRequest: request,
diff --git a/packages/astro/src/vite-plugin-astro-server/request.ts b/packages/astro/src/vite-plugin-astro-server/request.ts
index ae476f9be..72ce29b1b 100644
--- a/packages/astro/src/vite-plugin-astro-server/request.ts
+++ b/packages/astro/src/vite-plugin-astro-server/request.ts
@@ -1,11 +1,8 @@
import type http from 'node:http';
import type { ManifestData, SSRManifest } from '../@types/astro';
-import type { DevelopmentEnvironment } from '../core/render/index';
import type { DevServerController } from './controller';
-
import { collectErrorMetadata } from '../core/errors/dev/index.js';
import { createSafeError } from '../core/errors/index.js';
-import { error } from '../core/logger/core.js';
import * as msg from '../core/messages.js';
import { collapseDuplicateSlashes, removeTrailingForwardSlash } from '../core/path.js';
import { eventError, telemetry } from '../events/index.js';
@@ -13,9 +10,10 @@ import { isServerLikeOutput } from '../prerender/utils.js';
import { runWithErrorHandling } from './controller.js';
import { handle500Response } from './response.js';
import { handleRoute, matchRoute } from './route.js';
+import type DevPipeline from './devPipeline';
type HandleRequest = {
- env: DevelopmentEnvironment;
+ pipeline: DevPipeline;
manifestData: ManifestData;
controller: DevServerController;
incomingRequest: http.IncomingMessage;
@@ -25,15 +23,15 @@ type HandleRequest = {
/** The main logic to route dev server requests to pages in Astro. */
export async function handleRequest({
- env,
+ pipeline,
manifestData,
controller,
incomingRequest,
incomingResponse,
manifest,
}: HandleRequest) {
- const { settings, loader: moduleLoader } = env;
- const { config } = settings;
+ const config = pipeline.getConfig();
+ const moduleLoader = pipeline.getModuleLoader();
const origin = `${moduleLoader.isHttps() ? 'https' : 'http'}://${incomingRequest.headers.host}`;
const buildingToSSR = isServerLikeOutput(config);
@@ -75,7 +73,7 @@ export async function handleRequest({
controller,
pathname,
async run() {
- const matchedRoute = await matchRoute(pathname, env, manifestData);
+ const matchedRoute = await matchRoute(pathname, manifestData, pipeline);
const resolvedPathname = matchedRoute?.resolvedPathname ?? pathname;
return await handleRoute({
matchedRoute,
@@ -83,7 +81,7 @@ export async function handleRequest({
pathname: resolvedPathname,
body,
origin,
- env,
+ pipeline,
manifestData,
incomingRequest: incomingRequest,
incomingResponse: incomingResponse,
@@ -95,7 +93,7 @@ export async function handleRequest({
// This could be a runtime error from Vite's SSR module, so try to fix it here
try {
- env.loader.fixStacktrace(err);
+ moduleLoader.fixStacktrace(err);
} catch {}
// This is our last line of defense regarding errors where we still might have some information about the request
@@ -104,7 +102,7 @@ export async function handleRequest({
telemetry.record(eventError({ cmd: 'dev', err: errorWithMetadata, isFatal: false }));
- error(env.logging, null, msg.formatErrorMessage(errorWithMetadata));
+ pipeline.logger.error(null, msg.formatErrorMessage(errorWithMetadata));
handle500Response(moduleLoader, incomingResponse, errorWithMetadata);
return err;
diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts
index 0bbaacbe2..ae4318092 100644
--- a/packages/astro/src/vite-plugin-astro-server/route.ts
+++ b/packages/astro/src/vite-plugin-astro-server/route.ts
@@ -1,25 +1,15 @@
-import mime from 'mime';
import type http from 'node:http';
import type {
ComponentInstance,
ManifestData,
- MiddlewareResponseHandler,
+ MiddlewareEndpointHandler,
RouteData,
SSRElement,
SSRManifest,
} from '../@types/astro';
-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';
-import { isEndpointResult } from '../core/render/core.js';
-import {
- createRenderContext,
- getParamsAndProps,
- tryRenderRoute,
- type DevelopmentEnvironment,
- type SSROptions,
-} from '../core/render/index.js';
+import { createRenderContext, getParamsAndProps, type SSROptions } from '../core/render/index.js';
import { createRequest } from '../core/request.js';
import { matchAllRoutes } from '../core/routing/index.js';
import { isPage, resolveIdToUrl, viteID } from '../core/util.js';
@@ -32,6 +22,7 @@ import { preload } from './index.js';
import { getComponentMetadata } from './metadata.js';
import { handle404Response, writeSSRResult, writeWebResponse } from './response.js';
import { getScriptsForURL } from './scripts.js';
+import type DevPipeline from './devPipeline.js';
const clientLocalsSymbol = Symbol.for('astro.locals');
@@ -56,12 +47,17 @@ function getCustom404Route(manifestData: ManifestData): RouteData | undefined {
export async function matchRoute(
pathname: string,
- env: DevelopmentEnvironment,
- manifestData: ManifestData
+ manifestData: ManifestData,
+ pipeline: DevPipeline
): Promise<MatchedRoute | undefined> {
- const { logging, settings, routeCache } = env;
+ const env = pipeline.getEnvironment();
+ const { routeCache, logging } = env;
const matches = matchAllRoutes(pathname, manifestData);
- const preloadedMatches = await getSortedPreloadedMatches({ env, matches, settings });
+ const preloadedMatches = await getSortedPreloadedMatches({
+ pipeline,
+ matches,
+ settings: pipeline.getSettings(),
+ });
for await (const { preloadedComponent, route: maybeRoute, filePath } of preloadedMatches) {
// attempt to get static paths
@@ -73,7 +69,7 @@ export async function matchRoute(
routeCache,
pathname: pathname,
logging,
- ssr: isServerLikeOutput(settings.config),
+ ssr: isServerLikeOutput(pipeline.getConfig()),
});
return {
route: maybeRoute,
@@ -96,14 +92,13 @@ 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, manifestData);
+ return await matchRoute(altPathname, manifestData, pipeline);
}
if (matches.length) {
const possibleRoutes = matches.flatMap((route) => route.component);
- warn(
- logging,
+ pipeline.logger.warn(
'getStaticPaths',
`${AstroErrorData.NoMatchingStaticPathFound.message(
pathname
@@ -115,8 +110,8 @@ export async function matchRoute(
const custom404 = getCustom404Route(manifestData);
if (custom404) {
- const filePath = new URL(`./${custom404.component}`, settings.config.root);
- const preloadedComponent = await preload({ env, filePath });
+ const filePath = new URL(`./${custom404.component}`, pipeline.getConfig().root);
+ const preloadedComponent = await preload({ pipeline, filePath });
return {
route: custom404,
@@ -136,12 +131,12 @@ type HandleRoute = {
pathname: string;
body: ArrayBuffer | undefined;
origin: string;
- env: DevelopmentEnvironment;
manifestData: ManifestData;
incomingRequest: http.IncomingMessage;
incomingResponse: http.ServerResponse;
manifest: SSRManifest;
status?: 404 | 500;
+ pipeline: DevPipeline;
};
export async function handleRoute({
@@ -151,18 +146,21 @@ export async function handleRoute({
status = getStatus(matchedRoute),
body,
origin,
- env,
+ pipeline,
manifestData,
incomingRequest,
incomingResponse,
manifest,
}: HandleRoute): Promise<void> {
- const { logging, settings } = env;
+ const env = pipeline.getEnvironment();
+ const settings = pipeline.getSettings();
+ const config = pipeline.getConfig();
+ const moduleLoader = pipeline.getModuleLoader();
+ const { logging } = env;
if (!matchedRoute) {
return handle404Response(origin, incomingRequest, incomingResponse);
}
- const { config } = settings;
const filePath: URL | undefined = matchedRoute.filePath;
const { route, preloadedComponent } = matchedRoute;
const buildingToSSR = isServerLikeOutput(config);
@@ -192,14 +190,14 @@ export async function handleRoute({
request,
route,
};
- const middleware = await loadMiddleware(env.loader, env.settings.config.srcDir);
+ const middleware = await loadMiddleware(moduleLoader, settings.config.srcDir);
if (middleware) {
options.middleware = middleware;
}
const mod = options.preload;
const { scripts, links, styles, metadata } = await getScriptsAndStyles({
- env: options.env,
+ pipeline,
filePath: options.filePath,
});
@@ -214,70 +212,32 @@ export async function handleRoute({
mod,
env,
});
- const onRequest = options.middleware?.onRequest as MiddlewareResponseHandler | undefined;
-
- 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') {
- const fourOhFourRoute = await matchRoute('/404', env, manifestData);
- return handleRoute({
- matchedRoute: fourOhFourRoute,
- url: new URL('/404', url),
- pathname: '/404',
- status: 404,
- body,
- origin,
- env,
- manifestData,
- incomingRequest,
- incomingResponse,
- manifest,
- });
- }
- await writeWebResponse(incomingResponse, result.response);
- } else {
- let contentType = 'text/plain';
- // Dynamic routes don't include `route.pathname`, so synthesize a path for these (e.g. 'src/pages/[slug].svg')
- const filepath =
- route.pathname ||
- route.segments.map((segment) => segment.map((p) => p.content).join('')).join('/');
- const computedMimeType = mime.getType(filepath);
- if (computedMimeType) {
- contentType = computedMimeType;
- }
- const response = new Response(
- result.encoding !== 'binary' ? Buffer.from(result.body, result.encoding) : result.body,
- {
- status: 200,
- headers: {
- 'Content-Type': `${contentType};charset=utf-8`,
- },
- }
- );
- attachCookiesToResponse(response, result.cookies);
- await writeWebResponse(incomingResponse, response);
- }
+ const onRequest = options.middleware?.onRequest as MiddlewareEndpointHandler | undefined;
+ if (onRequest) {
+ pipeline.setMiddlewareFunction(onRequest);
+ }
+ pipeline.setCurrentMatchedRoute(route);
+
+ let response = await pipeline.renderRoute(renderContext, mod);
+ if (response.status === 404) {
+ const fourOhFourRoute = await matchRoute('/404', manifestData, pipeline);
+ return handleRoute({
+ ...options,
+ matchedRoute: fourOhFourRoute,
+ url: new URL(pathname, url),
+ status: 404,
+ body,
+ origin,
+ pipeline,
+ manifestData,
+ incomingRequest,
+ incomingResponse,
+ manifest,
+ });
+ }
+ if (route.type === 'endpoint') {
+ await writeWebResponse(incomingResponse, response);
} else {
- if (result.status === 404) {
- const fourOhFourRoute = await matchRoute('/404', env, manifestData);
- return handleRoute({
- ...options,
- matchedRoute: fourOhFourRoute,
- url: new URL(pathname, url),
- status: 404,
- body,
- origin,
- env,
- manifestData,
- incomingRequest,
- incomingResponse,
- manifest,
- });
- }
-
- let response = result;
-
if (
// We are in a recursion, and it's possible that this function is called itself with a status code
// By default, the status code passed via parameters is computed by the matched route.
@@ -291,23 +251,26 @@ export async function handleRoute({
return;
} else if (status && response.status !== status && (status === 404 || status === 500)) {
// Response.status is read-only, so a clone is required to override
- response = new Response(result.body, { ...result, status });
+ response = new Response(response.body, { ...response, status });
}
await writeSSRResult(request, response, incomingResponse);
}
}
interface GetScriptsAndStylesParams {
- env: DevelopmentEnvironment;
+ pipeline: DevPipeline;
filePath: URL;
}
-async function getScriptsAndStyles({ env, filePath }: GetScriptsAndStylesParams) {
+async function getScriptsAndStyles({ pipeline, filePath }: GetScriptsAndStylesParams) {
+ const moduleLoader = pipeline.getModuleLoader();
+ const settings = pipeline.getSettings();
+ const mode = pipeline.getEnvironment().mode;
// Add hoisted script tags
- const scripts = await getScriptsForURL(filePath, env.settings.config.root, env.loader);
+ const scripts = await getScriptsForURL(filePath, settings.config.root, moduleLoader);
// Inject HMR scripts
- if (isPage(filePath, env.settings) && env.mode === 'development') {
+ if (isPage(filePath, settings) && mode === 'development') {
scripts.add({
props: { type: 'module', src: '/@vite/client' },
children: '',
@@ -315,20 +278,20 @@ async function getScriptsAndStyles({ env, filePath }: GetScriptsAndStylesParams)
scripts.add({
props: {
type: 'module',
- src: await resolveIdToUrl(env.loader, 'astro/runtime/client/hmr.js'),
+ src: await resolveIdToUrl(moduleLoader, 'astro/runtime/client/hmr.js'),
},
children: '',
});
}
// TODO: We should allow adding generic HTML elements to the head, not just scripts
- for (const script of env.settings.scripts) {
+ for (const script of settings.scripts) {
if (script.stage === 'head-inline') {
scripts.add({
props: {},
children: script.content,
});
- } else if (script.stage === 'page' && isPage(filePath, env.settings)) {
+ } else if (script.stage === 'page' && isPage(filePath, settings)) {
scripts.add({
props: { type: 'module', src: `/@id/${PAGE_SCRIPT_ID}` },
children: '',
@@ -337,7 +300,7 @@ async function getScriptsAndStyles({ env, filePath }: GetScriptsAndStylesParams)
}
// Pass framework CSS in as style tags to be appended to the page.
- const { urls: styleUrls, stylesMap } = await getStylesForURL(filePath, env.loader, env.mode);
+ const { urls: styleUrls, stylesMap } = await getStylesForURL(filePath, moduleLoader, mode);
let links = new Set<SSRElement>();
[...styleUrls].forEach((href) => {
links.add({
@@ -364,13 +327,13 @@ async function getScriptsAndStyles({ env, filePath }: GetScriptsAndStylesParams)
props: {
type: 'text/css',
// Track the ID so we can match it to Vite's injected style later
- 'data-astro-dev-id': viteID(new URL(`.${url}`, env.settings.config.root)),
+ 'data-astro-dev-id': viteID(new URL(`.${url}`, settings.config.root)),
},
children: content,
});
});
- const metadata = await getComponentMetadata(filePath, env.loader);
+ const metadata = await getComponentMetadata(filePath, moduleLoader);
return { scripts, styles, links, metadata };
}
diff --git a/packages/astro/test/units/routing/route-matching.test.js b/packages/astro/test/units/routing/route-matching.test.js
index e1c4df5c5..c0bf7d6e6 100644
--- a/packages/astro/test/units/routing/route-matching.test.js
+++ b/packages/astro/test/units/routing/route-matching.test.js
@@ -12,8 +12,8 @@ import { createContainer } from '../../../dist/core/dev/container.js';
import * as cheerio from 'cheerio';
import testAdapter from '../../test-adapter.js';
import { getSortedPreloadedMatches } from '../../../dist/prerender/routing.js';
-import { createDevelopmentEnvironment } from '../../../dist/vite-plugin-astro-server/environment.js';
import { createDevelopmentManifest } from '../../../dist/vite-plugin-astro-server/plugin.js';
+import DevPipeline from '../../../dist/vite-plugin-astro-server/devPipeline.js';
const root = new URL('../../fixtures/alias/', import.meta.url);
const fileSystem = {
@@ -124,7 +124,7 @@ const fileSystem = {
};
describe('Route matching', () => {
- let env;
+ let pipeline;
let manifestData;
let container;
let settings;
@@ -145,7 +145,7 @@ describe('Route matching', () => {
const loader = createViteLoader(container.viteServer);
const manifest = createDevelopmentManifest(container.settings);
- env = createDevelopmentEnvironment(manifest, container.settings, defaultLogging, loader);
+ pipeline = new DevPipeline({ manifest, logging: defaultLogging, settings, loader });
manifestData = createRouteManifest(
{
cwd: fileURLToPath(root),
@@ -163,7 +163,7 @@ describe('Route matching', () => {
describe('Matched routes', () => {
it('should be sorted correctly', async () => {
const matches = matchAllRoutes('/try-matching-a-route', manifestData);
- const preloadedMatches = await getSortedPreloadedMatches({ env, matches, settings });
+ const preloadedMatches = await getSortedPreloadedMatches({ pipeline, matches, settings });
const sortedRouteNames = preloadedMatches.map((match) => match.route.route);
expect(sortedRouteNames).to.deep.equal([
@@ -177,7 +177,7 @@ describe('Route matching', () => {
});
it('nested should be sorted correctly', async () => {
const matches = matchAllRoutes('/nested/try-matching-a-route', manifestData);
- const preloadedMatches = await getSortedPreloadedMatches({ env, matches, settings });
+ const preloadedMatches = await getSortedPreloadedMatches({ pipeline, matches, settings });
const sortedRouteNames = preloadedMatches.map((match) => match.route.route);
expect(sortedRouteNames).to.deep.equal([
diff --git a/packages/astro/test/units/vite-plugin-astro-server/request.test.js b/packages/astro/test/units/vite-plugin-astro-server/request.test.js
index 48d449ccd..58ad404fd 100644
--- a/packages/astro/test/units/vite-plugin-astro-server/request.test.js
+++ b/packages/astro/test/units/vite-plugin-astro-server/request.test.js
@@ -1,31 +1,35 @@
import { expect } from 'chai';
-
import { createLoader } from '../../../dist/core/module-loader/index.js';
import { createRouteManifest } from '../../../dist/core/routing/index.js';
import { createComponent, render } from '../../../dist/runtime/server/index.js';
import { createController, handleRequest } from '../../../dist/vite-plugin-astro-server/index.js';
import {
createAstroModule,
- createBasicEnvironment,
createBasicSettings,
createFs,
createRequestAndResponse,
defaultLogging,
} from '../test-utils.js';
+import { createDevelopmentManifest } from '../../../dist/vite-plugin-astro-server/plugin.js';
+import DevPipeline from '../../../dist/vite-plugin-astro-server/devPipeline.js';
+
+async function createDevPipeline(overrides = {}) {
+ const settings = overrides.settings ?? (await createBasicSettings({ root: '/' }));
+ const loader = overrides.loader ?? createLoader();
+ const manifest = createDevelopmentManifest(settings);
-async function createDevEnvironment(overrides = {}) {
- const env = createBasicEnvironment();
- env.settings = await createBasicSettings({ root: '/' });
- env.settings.renderers = [];
- env.loader = createLoader();
- Object.assign(env, overrides);
- return env;
+ return new DevPipeline({
+ manifest,
+ settings,
+ logging: defaultLogging,
+ loader,
+ });
}
describe('vite-plugin-astro-server', () => {
describe('request', () => {
it('renders a request', async () => {
- const env = await createDevEnvironment({
+ const pipeline = await createDevPipeline({
loader: createLoader({
import() {
const Page = createComponent(() => {
@@ -35,7 +39,7 @@ describe('vite-plugin-astro-server', () => {
},
}),
});
- const controller = createController({ loader: env.loader });
+ const controller = createController({ loader: pipeline.getModuleLoader() });
const { req, res, text } = createRequestAndResponse();
const fs = createFs(
{
@@ -47,14 +51,14 @@ describe('vite-plugin-astro-server', () => {
const manifestData = createRouteManifest(
{
fsMod: fs,
- settings: env.settings,
+ settings: pipeline.getSettings(),
},
defaultLogging
);
try {
await handleRequest({
- env,
+ pipeline,
manifestData,
controller,
incomingRequest: req,