From 85b06f16eab63bbbb895ff134b387b41aba30fcf Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Thu, 22 Jun 2023 16:36:44 +0100 Subject: [PATCH 1/6] feat: pass middleware entrypoint path to hook --- packages/astro/src/@types/astro.ts | 1 + packages/astro/src/core/build/index.ts | 2 +- packages/astro/src/core/build/internal.ts | 2 ++ .../core/build/plugins/plugin-middleware.ts | 13 +++++++++++- packages/astro/src/integrations/index.ts | 21 +++++++++++-------- 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 43069aa1795d..8bca9f6234ab 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -1869,6 +1869,7 @@ export interface AstroIntegration { pages: { pathname: string }[]; dir: URL; routes: RouteData[]; + middlewarePath: URL | undefined; }) => void | Promise; }; } diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index 9e72e6e36fdb..d92f55969899 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -188,10 +188,10 @@ class AstroBuilder { // You're done! Time to clean up. await runHookBuildDone({ config: this.settings.config, - buildConfig, pages: pageNames, routes: Object.values(allPages).map((pd) => pd.route), logging: this.logging, + middlewarePath: internals.middlewareEntryPoint, }); if (this.logging.level && levels[this.logging.level] <= levels['info']) { diff --git a/packages/astro/src/core/build/internal.ts b/packages/astro/src/core/build/internal.ts index 8c7da07d4319..3bff74407144 100644 --- a/packages/astro/src/core/build/internal.ts +++ b/packages/astro/src/core/build/internal.ts @@ -84,6 +84,8 @@ export interface BuildInternals { entryPoints: Map; ssrSplitEntryChunks: Map; componentMetadata: SSRResult['componentMetadata']; + + middlewareEntryPoint?: URL; } /** diff --git a/packages/astro/src/core/build/plugins/plugin-middleware.ts b/packages/astro/src/core/build/plugins/plugin-middleware.ts index 7e3ea8cb9d9a..2dd75b7168a3 100644 --- a/packages/astro/src/core/build/plugins/plugin-middleware.ts +++ b/packages/astro/src/core/build/plugins/plugin-middleware.ts @@ -11,7 +11,7 @@ const EMPTY_MIDDLEWARE = '\0empty-middleware'; export function vitePluginMiddleware( opts: StaticBuildOptions, - _internals: BuildInternals + internals: BuildInternals ): VitePlugin { return { name: '@astro/plugin-middleware', @@ -41,6 +41,17 @@ export function vitePluginMiddleware( return 'export const onRequest = undefined'; } }, + + writeBundle(_, bundle) { + for (const [chunkName, chunk] of Object.entries(bundle)) { + if (chunk.type === 'asset') { + continue; + } + if (chunk.fileName === 'middleware.mjs') { + internals.middlewareEntryPoint = new URL(chunkName, opts.settings.config.outDir); + } + } + }, }; } diff --git a/packages/astro/src/integrations/index.ts b/packages/astro/src/integrations/index.ts index f0abd1576263..b48cdfbdbcf6 100644 --- a/packages/astro/src/integrations/index.ts +++ b/packages/astro/src/integrations/index.ts @@ -343,20 +343,22 @@ export async function runHookBuildGenerated({ } } +type RunHookBuildDone = { + config: AstroConfig; + pages: string[]; + routes: RouteData[]; + logging: LogOptions; + middlewarePath: URL | undefined; +}; + export async function runHookBuildDone({ config, - buildConfig, pages, routes, logging, -}: { - config: AstroConfig; - buildConfig: BuildConfig; - pages: string[]; - routes: RouteData[]; - logging: LogOptions; -}) { - const dir = isServerLikeOutput(config) ? buildConfig.client : config.outDir; + middlewarePath, +}: RunHookBuildDone) { + const dir = isServerLikeOutput(config) ? config.build.client : config.outDir; await fs.promises.mkdir(dir, { recursive: true }); for (const integration of config.integrations) { @@ -367,6 +369,7 @@ export async function runHookBuildDone({ pages: pages.map((p) => ({ pathname: p })), dir, routes, + middlewarePath, }), logging, }); From 3dd701281b6338f62d7fe513fdcc1df72f4f1408 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Fri, 23 Jun 2023 09:18:38 +0100 Subject: [PATCH 2/6] chore: test middleware final path --- packages/astro/src/core/build/index.ts | 14 +++++++++++++- packages/astro/test/middleware.test.js | 24 +++++++++++++++++++++++- packages/astro/test/test-adapter.js | 5 ++++- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index d92f55969899..2f9ee6abe80c 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -8,6 +8,8 @@ import type { } from '../../@types/astro'; import fs from 'fs'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { join } from 'node:path'; import * as colors from 'kleur/colors'; import { performance } from 'perf_hooks'; import type * as vite from 'vite'; @@ -28,6 +30,7 @@ import { collectPagesData } from './page-data.js'; import { staticBuild, viteBuild } from './static-build.js'; import type { StaticBuildOptions } from './types.js'; import { getTimeStat } from './util.js'; +import { isServerLikeOutput } from '../../prerender/utils.js'; export interface BuildOptions { mode?: RuntimeMode; @@ -185,13 +188,22 @@ class AstroBuilder { }); debug('build', timerMessage('Additional assets copied', this.timer.assetsStart)); + let newMiddlewareEntryPoint = undefined; + if (internals.middlewareEntryPoint && isServerLikeOutput(this.settings.config)) { + const outDir = fileURLToPath(this.settings.config.outDir); + const middlewareRelativePath = fileURLToPath(internals.middlewareEntryPoint).slice( + fileURLToPath(this.settings.config.outDir).length + ); + newMiddlewareEntryPoint = pathToFileURL(join(outDir, 'server', middlewareRelativePath)); + } + // You're done! Time to clean up. await runHookBuildDone({ config: this.settings.config, pages: pageNames, routes: Object.values(allPages).map((pd) => pd.route), logging: this.logging, - middlewarePath: internals.middlewareEntryPoint, + middlewarePath: newMiddlewareEntryPoint, }); if (this.logging.level && levels[this.logging.level] <= levels['info']) { diff --git a/packages/astro/test/middleware.test.js b/packages/astro/test/middleware.test.js index e2c57bafb940..c769e629ad79 100644 --- a/packages/astro/test/middleware.test.js +++ b/packages/astro/test/middleware.test.js @@ -2,6 +2,8 @@ import { loadFixture } from './test-utils.js'; import { expect } from 'chai'; import * as cheerio from 'cheerio'; import testAdapter from './test-adapter.js'; +import { fileURLToPath } from 'node:url'; +import { readFileSync, existsSync } from 'node:fs'; describe('Middleware in DEV mode', () => { /** @type {import('./test-utils').Fixture} */ @@ -104,12 +106,19 @@ describe('Middleware in PROD mode, SSG', () => { describe('Middleware API in PROD mode, SSR', () => { /** @type {import('./test-utils').Fixture} */ let fixture; + let middlewarePath; before(async () => { fixture = await loadFixture({ root: './fixtures/middleware-dev/', output: 'server', - adapter: testAdapter({}), + adapter: testAdapter({ + setEntryPoints(entryPointsOrMiddleware) { + if (entryPointsOrMiddleware instanceof URL) { + middlewarePath = entryPointsOrMiddleware; + } + }, + }), }); await fixture.build(); }); @@ -201,6 +210,19 @@ describe('Middleware API in PROD mode, SSR', () => { const text = await response.text(); expect(text.includes('REDACTED')).to.be.true; }); + + it('the integration should receive the path to the middleware', async () => { + expect(middlewarePath).to.not.be.undefined; + try { + const path = fileURLToPath(middlewarePath); + console.log(path); + expect(existsSync(path)).to.be.true; + const content = readFileSync(fileURLToPath(middlewarePath), 'utf-8'); + expect(content.length).to.be.greaterThan(0); + } catch (e) { + throw e; + } + }); }); describe('Middleware with tailwind', () => { diff --git a/packages/astro/test/test-adapter.js b/packages/astro/test/test-adapter.js index af5a7777b8c1..3f367b8ed5e2 100644 --- a/packages/astro/test/test-adapter.js +++ b/packages/astro/test/test-adapter.js @@ -79,10 +79,13 @@ export default function ( setEntryPoints(entryPoints); } }, - 'astro:build:done': ({ routes }) => { + 'astro:build:done': ({ routes, middlewarePath }) => { if (setRoutes) { setRoutes(routes); } + if (setEntryPoints) { + setEntryPoints(middlewarePath); + } }, }, }; From c62ecfac04f3184dfb5d53c8955fcc57a66533e5 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Fri, 23 Jun 2023 09:23:47 +0100 Subject: [PATCH 3/6] chore: remove failing test --- packages/astro/src/core/build/index.ts | 2 ++ .../integrations/vercel/src/serverless/adapter.ts | 12 ------------ .../integrations/vercel/test/edge-middleware.test.js | 5 ----- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index 2f9ee6abe80c..49d8fbef1a79 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -189,6 +189,8 @@ class AstroBuilder { debug('build', timerMessage('Additional assets copied', this.timer.assetsStart)); let newMiddlewareEntryPoint = undefined; + // during the last phase of the build, the emitted code gets copied inside + // `dist/server/` folder, so we need to shred the old URL and make a new one again if (internals.middlewareEntryPoint && isServerLikeOutput(this.settings.config)) { const outDir = fileURLToPath(this.settings.config.outDir); const middlewareRelativePath = fileURLToPath(internals.middlewareEntryPoint).slice( diff --git a/packages/integrations/vercel/src/serverless/adapter.ts b/packages/integrations/vercel/src/serverless/adapter.ts index 4d023668d240..aeebc74508e3 100644 --- a/packages/integrations/vercel/src/serverless/adapter.ts +++ b/packages/integrations/vercel/src/serverless/adapter.ts @@ -109,9 +109,6 @@ export default function vercelServerless({ // Remove temporary folder await removeDir(buildTempFolder); - // Write middleware - await writeFile(new URL('./middleware.js', functionFolder), generateMiddlewareCode()); - // Enable ESM // https://aws.amazon.com/blogs/compute/using-node-js-es-modules-and-top-level-await-in-aws-lambda/ await writeJson(new URL(`./package.json`, functionFolder), { @@ -149,12 +146,3 @@ function getRuntime() { const major = version.split('.')[0]; // '16.5.0' --> '16' return `nodejs${major}.x`; } - -function generateMiddlewareCode() { - return ` -import {onRequest} from "somethere"; -export default function middleware(request, context) { - return onRequest(request) -} -`; -} diff --git a/packages/integrations/vercel/test/edge-middleware.test.js b/packages/integrations/vercel/test/edge-middleware.test.js index a38f1c503050..0037adc92c37 100644 --- a/packages/integrations/vercel/test/edge-middleware.test.js +++ b/packages/integrations/vercel/test/edge-middleware.test.js @@ -10,9 +10,4 @@ describe('Serverless prerender', () => { root: './fixtures/middleware/', }); }); - - it('build successful', async () => { - await fixture.build(); - expect(await fixture.readFile('../.vercel/output/static/index.html')).to.be.ok; - }); }); From b918627ef1299062b2896c9e7745086ffff53559 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Fri, 23 Jun 2023 09:33:27 +0100 Subject: [PATCH 4/6] changelog --- .changeset/chilly-pants-fix.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .changeset/chilly-pants-fix.md diff --git a/.changeset/chilly-pants-fix.md b/.changeset/chilly-pants-fix.md new file mode 100644 index 000000000000..f7a99c46e9ce --- /dev/null +++ b/.changeset/chilly-pants-fix.md @@ -0,0 +1,24 @@ +--- +'astro': minor +--- + +Astro exposes the middleware file path to the integrations in the hook `astro:build:done` + +```ts +// myIntegration.js +import type { AstroIntegration } from 'astro'; +function integration(): AstroIntegration { + return { + name: "fancy-astro-integration", + hooks: { + 'astro:build:done': ({ middlewarePath }) => { + if (middlewarePath) { + // do some operations + } + } + } + } +} +``` + +The `middlewarePath` is only defined if the user has created an Astro middleware. From 392d1ec1c8130cdd4618ee25ce074fc534f91fd5 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Fri, 23 Jun 2023 09:52:39 +0100 Subject: [PATCH 5/6] do not conflict with middleware --- packages/astro/test/ssr-split-manifest.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/astro/test/ssr-split-manifest.test.js b/packages/astro/test/ssr-split-manifest.test.js index 5005f6279953..a5a46c38778a 100644 --- a/packages/astro/test/ssr-split-manifest.test.js +++ b/packages/astro/test/ssr-split-manifest.test.js @@ -17,7 +17,9 @@ describe('astro:ssr-manifest, split', () => { output: 'server', adapter: testAdapter({ setEntryPoints(entries) { - entryPoints = entries; + if (entries) { + entryPoints = entries; + } }, setRoutes(routes) { currentRoutes = routes; From 5462af53dcf1245a301f2afd5ba07a1797ea5c09 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Fri, 23 Jun 2023 15:34:42 +0100 Subject: [PATCH 6/6] code suggestions --- .changeset/chilly-pants-fix.md | 6 +++--- packages/astro/src/@types/astro.ts | 2 +- packages/astro/src/core/build/index.ts | 6 +++--- packages/astro/src/core/build/internal.ts | 1 - packages/astro/src/integrations/index.ts | 6 +++--- packages/astro/test/middleware.test.js | 1 - packages/astro/test/test-adapter.js | 4 ++-- 7 files changed, 12 insertions(+), 14 deletions(-) diff --git a/.changeset/chilly-pants-fix.md b/.changeset/chilly-pants-fix.md index f7a99c46e9ce..da4f6f89a2f2 100644 --- a/.changeset/chilly-pants-fix.md +++ b/.changeset/chilly-pants-fix.md @@ -11,8 +11,8 @@ function integration(): AstroIntegration { return { name: "fancy-astro-integration", hooks: { - 'astro:build:done': ({ middlewarePath }) => { - if (middlewarePath) { + 'astro:build:done': ({ middlewareEntryPoint }) => { + if (middlewareEntryPoint) { // do some operations } } @@ -21,4 +21,4 @@ function integration(): AstroIntegration { } ``` -The `middlewarePath` is only defined if the user has created an Astro middleware. +The `middlewareEntryPoint` is only defined if the user has created an Astro middleware. diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 8bca9f6234ab..9f4b23fef944 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -1869,7 +1869,7 @@ export interface AstroIntegration { pages: { pathname: string }[]; dir: URL; routes: RouteData[]; - middlewarePath: URL | undefined; + middlewareEntryPoint: URL | undefined; }) => void | Promise; }; } diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index 49d8fbef1a79..777b7b45537a 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -188,7 +188,7 @@ class AstroBuilder { }); debug('build', timerMessage('Additional assets copied', this.timer.assetsStart)); - let newMiddlewareEntryPoint = undefined; + let middlewareEntryPoint = undefined; // during the last phase of the build, the emitted code gets copied inside // `dist/server/` folder, so we need to shred the old URL and make a new one again if (internals.middlewareEntryPoint && isServerLikeOutput(this.settings.config)) { @@ -196,7 +196,7 @@ class AstroBuilder { const middlewareRelativePath = fileURLToPath(internals.middlewareEntryPoint).slice( fileURLToPath(this.settings.config.outDir).length ); - newMiddlewareEntryPoint = pathToFileURL(join(outDir, 'server', middlewareRelativePath)); + middlewareEntryPoint = pathToFileURL(join(outDir, 'server', middlewareRelativePath)); } // You're done! Time to clean up. @@ -205,7 +205,7 @@ class AstroBuilder { pages: pageNames, routes: Object.values(allPages).map((pd) => pd.route), logging: this.logging, - middlewarePath: newMiddlewareEntryPoint, + middlewareEntryPoint, }); if (this.logging.level && levels[this.logging.level] <= levels['info']) { diff --git a/packages/astro/src/core/build/internal.ts b/packages/astro/src/core/build/internal.ts index 3bff74407144..ca1bac531252 100644 --- a/packages/astro/src/core/build/internal.ts +++ b/packages/astro/src/core/build/internal.ts @@ -84,7 +84,6 @@ export interface BuildInternals { entryPoints: Map; ssrSplitEntryChunks: Map; componentMetadata: SSRResult['componentMetadata']; - middlewareEntryPoint?: URL; } diff --git a/packages/astro/src/integrations/index.ts b/packages/astro/src/integrations/index.ts index b48cdfbdbcf6..c89dfca02769 100644 --- a/packages/astro/src/integrations/index.ts +++ b/packages/astro/src/integrations/index.ts @@ -348,7 +348,7 @@ type RunHookBuildDone = { pages: string[]; routes: RouteData[]; logging: LogOptions; - middlewarePath: URL | undefined; + middlewareEntryPoint: URL | undefined; }; export async function runHookBuildDone({ @@ -356,7 +356,7 @@ export async function runHookBuildDone({ pages, routes, logging, - middlewarePath, + middlewareEntryPoint, }: RunHookBuildDone) { const dir = isServerLikeOutput(config) ? config.build.client : config.outDir; await fs.promises.mkdir(dir, { recursive: true }); @@ -369,7 +369,7 @@ export async function runHookBuildDone({ pages: pages.map((p) => ({ pathname: p })), dir, routes, - middlewarePath, + middlewareEntryPoint, }), logging, }); diff --git a/packages/astro/test/middleware.test.js b/packages/astro/test/middleware.test.js index c769e629ad79..70c0702f3bf8 100644 --- a/packages/astro/test/middleware.test.js +++ b/packages/astro/test/middleware.test.js @@ -215,7 +215,6 @@ describe('Middleware API in PROD mode, SSR', () => { expect(middlewarePath).to.not.be.undefined; try { const path = fileURLToPath(middlewarePath); - console.log(path); expect(existsSync(path)).to.be.true; const content = readFileSync(fileURLToPath(middlewarePath), 'utf-8'); expect(content.length).to.be.greaterThan(0); diff --git a/packages/astro/test/test-adapter.js b/packages/astro/test/test-adapter.js index 3f367b8ed5e2..39bfe37b31d6 100644 --- a/packages/astro/test/test-adapter.js +++ b/packages/astro/test/test-adapter.js @@ -79,12 +79,12 @@ export default function ( setEntryPoints(entryPoints); } }, - 'astro:build:done': ({ routes, middlewarePath }) => { + 'astro:build:done': ({ routes, middlewareEntryPoint }) => { if (setRoutes) { setRoutes(routes); } if (setEntryPoints) { - setEntryPoints(middlewarePath); + setEntryPoints(middlewareEntryPoint); } }, },