summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/astro/src/core/app/index.ts23
-rw-r--r--packages/astro/src/core/app/types.ts1
-rw-r--r--packages/astro/src/core/build/buildPipeline.ts7
-rw-r--r--packages/astro/src/core/build/generate.ts9
-rw-r--r--packages/astro/src/core/build/plugins/plugin-manifest.ts14
-rw-r--r--packages/astro/src/core/build/plugins/plugin-middleware.ts6
-rw-r--r--packages/astro/src/core/build/types.ts2
-rw-r--r--packages/astro/src/core/redirects/component.ts1
-rw-r--r--packages/astro/src/vite-plugin-astro-server/plugin.ts1
-rw-r--r--packages/astro/test/fixtures/middleware-dev/src/middleware.js8
-rw-r--r--packages/astro/test/fixtures/middleware-dev/src/pages/404.astro6
-rw-r--r--packages/astro/test/middleware.test.js17
-rw-r--r--packages/astro/test/test-adapter.js12
13 files changed, 81 insertions, 26 deletions
diff --git a/packages/astro/src/core/app/index.ts b/packages/astro/src/core/app/index.ts
index 566d0c60d..6b92bed92 100644
--- a/packages/astro/src/core/app/index.ts
+++ b/packages/astro/src/core/app/index.ts
@@ -9,7 +9,7 @@ import type {
import type { SinglePageBuiltModule } from '../build/types';
import { getSetCookiesFromResponse } from '../cookies/index.js';
import { consoleLogDestination } from '../logger/console.js';
-import { error, type LogOptions } from '../logger/core.js';
+import { error, type LogOptions, warn } from '../logger/core.js';
import {
collapseDuplicateSlashes,
prependForwardSlash,
@@ -56,6 +56,8 @@ export class App {
};
#baseWithoutTrailingSlash: string;
#pipeline: SSRRoutePipeline;
+ #onRequest: MiddlewareEndpointHandler | undefined;
+ #middlewareLoaded: boolean;
constructor(manifest: SSRManifest, streaming = true) {
this.#manifest = manifest;
@@ -65,6 +67,7 @@ export class App {
this.#routeDataToRouteInfo = new Map(manifest.routes.map((route) => [route.routeData, route]));
this.#baseWithoutTrailingSlash = removeTrailingForwardSlash(this.#manifest.base);
this.#pipeline = new SSRRoutePipeline(this.#createEnvironment(streaming));
+ this.#middlewareLoaded = false;
}
set setManifest(newManifest: SSRManifest) {
@@ -128,7 +131,21 @@ export class App {
if (!routeData || routeData.prerender) return undefined;
return routeData;
}
+
+ async #getOnRequest() {
+ if (this.#manifest.middlewareEntryPoint && !this.#middlewareLoaded) {
+ try {
+ const middleware = await import(this.#manifest.middlewareEntryPoint);
+ this.#pipeline.setMiddlewareFunction(middleware.onRequest as MiddlewareEndpointHandler);
+ } catch (e) {
+ warn(this.#logging, 'SSR', "Couldn't load the middleware entry point");
+ }
+ }
+ this.#middlewareLoaded = true;
+ }
+
async render(request: Request, routeData?: RouteData, locals?: object): Promise<Response> {
+ await this.#getOnRequest();
// Handle requests with duplicate slashes gracefully by cloning with a cleaned-up request URL
if (request.url !== collapseDuplicateSlashes(request.url)) {
request = new Request(collapseDuplicateSlashes(request.url), request);
@@ -156,10 +173,6 @@ export class App {
);
let response;
try {
- // 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) {
if (err instanceof EndpointNotFoundError) {
diff --git a/packages/astro/src/core/app/types.ts b/packages/astro/src/core/app/types.ts
index 8812d2c44..faeb9377e 100644
--- a/packages/astro/src/core/app/types.ts
+++ b/packages/astro/src/core/app/types.ts
@@ -49,6 +49,7 @@ export type SSRManifest = {
componentMetadata: SSRResult['componentMetadata'];
pageModule?: SinglePageBuiltModule;
pageMap?: Map<ComponentPath, ImportComponentInstance>;
+ middlewareEntryPoint: string | undefined;
};
export type SerializedSSRManifest = Omit<
diff --git a/packages/astro/src/core/build/buildPipeline.ts b/packages/astro/src/core/build/buildPipeline.ts
index 5815fa5f5..ea6e9ecf4 100644
--- a/packages/astro/src/core/build/buildPipeline.ts
+++ b/packages/astro/src/core/build/buildPipeline.ts
@@ -77,6 +77,13 @@ export class BuildPipeline extends Pipeline {
return this.#manifest;
}
+ async retrieveMiddlewareFunction() {
+ if (this.#internals.middlewareEntryPoint) {
+ const middleware = await import(this.#internals.middlewareEntryPoint.toString());
+ this.setMiddlewareFunction(middleware.onRequest);
+ }
+ }
+
/**
* The SSR build emits two important files:
* - dist/server/manifest.mjs
diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts
index 224b96b63..73bb4a40f 100644
--- a/packages/astro/src/core/build/generate.ts
+++ b/packages/astro/src/core/build/generate.ts
@@ -10,7 +10,6 @@ import type {
ComponentInstance,
GetStaticPathsItem,
ImageTransform,
- MiddlewareEndpointHandler,
RouteData,
RouteType,
SSRError,
@@ -138,6 +137,7 @@ export async function generatePages(opts: StaticBuildOptions, internals: BuildIn
);
}
const buildPipeline = new BuildPipeline(opts, internals, manifest);
+ await buildPipeline.retrieveMiddlewareFunction();
const outFolder = ssr
? opts.settings.config.build.server
: getOutDirWithinCwd(opts.settings.config.outDir);
@@ -248,10 +248,6 @@ async function generatePage(
.reduce(mergeInlineCss, []);
const pageModulePromise = ssrEntry.page;
- const onRequest = ssrEntry.onRequest;
- if (onRequest) {
- pipeline.setMiddlewareFunction(onRequest as MiddlewareEndpointHandler);
- }
if (!pageModulePromise) {
throw new Error(
@@ -612,5 +608,8 @@ export function createBuildManifest(
? new URL(settings.config.base, settings.config.site).toString()
: settings.config.site,
componentMetadata: internals.componentMetadata,
+ middlewareEntryPoint: internals.middlewareEntryPoint
+ ? internals.middlewareEntryPoint.toString()
+ : undefined,
};
}
diff --git a/packages/astro/src/core/build/plugins/plugin-manifest.ts b/packages/astro/src/core/build/plugins/plugin-manifest.ts
index a7b254554..1364ebf1e 100644
--- a/packages/astro/src/core/build/plugins/plugin-manifest.ts
+++ b/packages/astro/src/core/build/plugins/plugin-manifest.ts
@@ -93,14 +93,19 @@ export function pluginManifest(
}
const manifest = await createManifest(options, internals);
+ const shouldPassMiddlewareEntryPoint =
+ // TODO: remove in Astro 4.0
+ options.settings.config.build.excludeMiddleware ||
+ options.settings.adapter?.adapterFeatures?.edgeMiddleware;
await runHookBuildSsr({
config: options.settings.config,
manifest,
logging: options.logging,
entryPoints: internals.entryPoints,
- middlewareEntryPoint: internals.middlewareEntryPoint,
+ middlewareEntryPoint: shouldPassMiddlewareEntryPoint
+ ? internals.middlewareEntryPoint
+ : undefined,
});
- // TODO: use the manifest entry chunk instead
const code = injectManifest(manifest, internals.manifestEntryChunk);
mutate(internals.manifestEntryChunk, 'server', code);
},
@@ -232,6 +237,10 @@ function buildManifest(
// Set this to an empty string so that the runtime knows not to try and load this.
entryModules[BEFORE_HYDRATION_SCRIPT_ID] = '';
}
+ const isEdgeMiddleware =
+ // TODO: remove in Astro 4.0
+ settings.config.build.excludeMiddleware ||
+ settings.adapter?.adapterFeatures?.edgeMiddleware;
const ssrManifest: SerializedSSRManifest = {
adapterName: opts.settings.adapter?.name ?? '',
@@ -245,6 +254,7 @@ function buildManifest(
clientDirectives: Array.from(settings.clientDirectives),
entryModules,
assets: staticFiles.map(prefixAssetPath),
+ middlewareEntryPoint: !isEdgeMiddleware ? internals.middlewareEntryPoint?.toString() : undefined,
};
return ssrManifest;
diff --git a/packages/astro/src/core/build/plugins/plugin-middleware.ts b/packages/astro/src/core/build/plugins/plugin-middleware.ts
index 5ed532d5e..7c945740c 100644
--- a/packages/astro/src/core/build/plugins/plugin-middleware.ts
+++ b/packages/astro/src/core/build/plugins/plugin-middleware.ts
@@ -4,6 +4,7 @@ import { addRollupInput } from '../add-rollup-input.js';
import type { BuildInternals } from '../internal';
import type { AstroBuildPlugin } from '../plugin';
import type { StaticBuildOptions } from '../types';
+import { getOutputDirectory } from '../../../prerender/utils.js';
export const MIDDLEWARE_MODULE_ID = '@astro-middleware';
@@ -56,8 +57,9 @@ export function vitePluginMiddleware(
if (chunk.type === 'asset') {
continue;
}
- if (chunk.fileName === 'middleware.mjs' && opts.settings.config.build.excludeMiddleware) {
- internals.middlewareEntryPoint = new URL(chunkName, opts.settings.config.build.server);
+ if (chunk.fileName === 'middleware.mjs') {
+ const outputDirectory = getOutputDirectory(opts.settings.config);
+ internals.middlewareEntryPoint = new URL(chunkName, outputDirectory);
}
}
},
diff --git a/packages/astro/src/core/build/types.ts b/packages/astro/src/core/build/types.ts
index 472dc4b34..35451cacb 100644
--- a/packages/astro/src/core/build/types.ts
+++ b/packages/astro/src/core/build/types.ts
@@ -4,7 +4,6 @@ import type {
AstroSettings,
ComponentInstance,
ManifestData,
- MiddlewareHandler,
RouteData,
RuntimeMode,
SSRLoadedRenderer,
@@ -52,7 +51,6 @@ export interface SinglePageBuiltModule {
/**
* The `onRequest` hook exported by the middleware
*/
- onRequest?: MiddlewareHandler<unknown>;
renderers: SSRLoadedRenderer[];
}
diff --git a/packages/astro/src/core/redirects/component.ts b/packages/astro/src/core/redirects/component.ts
index d10cae4fe..93b1aa732 100644
--- a/packages/astro/src/core/redirects/component.ts
+++ b/packages/astro/src/core/redirects/component.ts
@@ -12,6 +12,5 @@ export const RedirectComponentInstance: ComponentInstance = {
export const RedirectSinglePageBuiltModule: SinglePageBuiltModule = {
page: () => Promise.resolve(RedirectComponentInstance),
- onRequest: (ctx, next) => next(),
renderers: [],
};
diff --git a/packages/astro/src/vite-plugin-astro-server/plugin.ts b/packages/astro/src/vite-plugin-astro-server/plugin.ts
index f7f7b0068..374f31fec 100644
--- a/packages/astro/src/vite-plugin-astro-server/plugin.ts
+++ b/packages/astro/src/vite-plugin-astro-server/plugin.ts
@@ -99,5 +99,6 @@ export function createDevelopmentManifest(settings: AstroSettings): SSRManifest
? new URL(settings.config.base, settings.config.site).toString()
: settings.config.site,
componentMetadata: new Map(),
+ middlewareEntryPoint: undefined,
};
}
diff --git a/packages/astro/test/fixtures/middleware-dev/src/middleware.js b/packages/astro/test/fixtures/middleware-dev/src/middleware.js
index 854c997c1..eeb902fb8 100644
--- a/packages/astro/test/fixtures/middleware-dev/src/middleware.js
+++ b/packages/astro/test/fixtures/middleware-dev/src/middleware.js
@@ -18,18 +18,20 @@ const first = defineMiddleware(async (context, next) => {
return new Response(JSON.stringify(object), {
headers: response.headers,
});
- } else if(context.url.pathname === '/clone') {
+ } else if (context.url.pathname === '/clone') {
const response = await next();
const newResponse = response.clone();
const /** @type {string} */ html = await newResponse.text();
const newhtml = html.replace('<h1>testing</h1>', '<h1>it works</h1>');
return new Response(newhtml, { status: 200, headers: response.headers });
} else {
- if(context.url.pathname === '/') {
+ if (context.url.pathname === '/') {
context.cookies.set('foo', 'bar');
}
- context.locals.name = 'bar';
+ context.locals = {
+ name: 'bar',
+ };
}
return await next();
});
diff --git a/packages/astro/test/fixtures/middleware-dev/src/pages/404.astro b/packages/astro/test/fixtures/middleware-dev/src/pages/404.astro
new file mode 100644
index 000000000..ea85d240c
--- /dev/null
+++ b/packages/astro/test/fixtures/middleware-dev/src/pages/404.astro
@@ -0,0 +1,6 @@
+---
+const name = Astro.locals.name;
+---
+
+<title>Error</title>
+<p>{name}</p>
diff --git a/packages/astro/test/middleware.test.js b/packages/astro/test/middleware.test.js
index 3796a341f..b09c686f4 100644
--- a/packages/astro/test/middleware.test.js
+++ b/packages/astro/test/middleware.test.js
@@ -211,6 +211,16 @@ describe('Middleware API in PROD mode, SSR', () => {
expect(text.includes('REDACTED')).to.be.true;
});
+ it('should correctly call the middleware function for 404', async () => {
+ const app = await fixture.loadTestAdapterApp();
+ const request = new Request('http://example.com/funky-url');
+ const routeData = app.match(request, { matchNotFound: true });
+ const response = await app.render(request, routeData);
+ const text = await response.text();
+ expect(text.includes('Error')).to.be.true;
+ expect(text.includes('bar')).to.be.true;
+ });
+
it('the integration should receive the path to the middleware', async () => {
fixture = await loadFixture({
root: './fixtures/middleware-dev/',
@@ -219,10 +229,8 @@ describe('Middleware API in PROD mode, SSR', () => {
excludeMiddleware: true,
},
adapter: testAdapter({
- setEntryPoints(entryPointsOrMiddleware) {
- if (entryPointsOrMiddleware instanceof URL) {
- middlewarePath = entryPointsOrMiddleware;
- }
+ setMiddlewareEntryPoint(entryPointsOrMiddleware) {
+ middlewarePath = entryPointsOrMiddleware;
},
}),
});
@@ -237,6 +245,7 @@ describe('Middleware API in PROD mode, SSR', () => {
throw e;
}
});
+
});
describe('Middleware with tailwind', () => {
diff --git a/packages/astro/test/test-adapter.js b/packages/astro/test/test-adapter.js
index 67058023d..0090b6d9d 100644
--- a/packages/astro/test/test-adapter.js
+++ b/packages/astro/test/test-adapter.js
@@ -5,7 +5,13 @@ import { viteID } from '../dist/core/util.js';
* @returns {import('../src/@types/astro').AstroIntegration}
*/
export default function (
- { provideAddress = true, extendAdapter, setEntryPoints = undefined, setRoutes = undefined } = {
+ {
+ provideAddress = true,
+ extendAdapter,
+ setEntryPoints = undefined,
+ setMiddlewareEntryPoint = undefined,
+ setRoutes = undefined,
+ } = {
provideAddress: true,
}
) {
@@ -86,7 +92,9 @@ export default function (
'astro:build:ssr': ({ entryPoints, middlewareEntryPoint }) => {
if (setEntryPoints) {
setEntryPoints(entryPoints);
- setEntryPoints(middlewareEntryPoint);
+ }
+ if (setMiddlewareEntryPoint) {
+ setMiddlewareEntryPoint(middlewareEntryPoint);
}
},
'astro:build:done': ({ routes }) => {