From 2540feedb06785d5a20eecc3668849f147d778d4 Mon Sep 17 00:00:00 2001 From: Alexander Niebuhr Date: Thu, 17 Aug 2023 23:40:28 +0200 Subject: [PATCH] feature(@astrojs/cloudflare): port functionPerRoute (#8078) * port functionPerRoute to cloudflare * add changeset * port bugfix to next * update changeset * Update packages/astro/src/core/build/generate.ts Co-authored-by: Emanuele Stoppa * update changeset * update README * add TODO comment * Update .changeset/wise-cameras-agree.md Co-authored-by: Sarah Rainsberger * Update .changeset/wise-cameras-agree.md Co-authored-by: Sarah Rainsberger * Update .changeset/nasty-garlics-listen.md Co-authored-by: Sarah Rainsberger * update README * Update .changeset/wise-cameras-agree.md Co-authored-by: Yan Thomas <61414485+Yan-Thomas@users.noreply.github.com> * Update packages/integrations/cloudflare/README.md Co-authored-by: Yan Thomas <61414485+Yan-Thomas@users.noreply.github.com> --------- Co-authored-by: Emanuele Stoppa Co-authored-by: Sarah Rainsberger Co-authored-by: Yan Thomas <61414485+Yan-Thomas@users.noreply.github.com> --- .changeset/nasty-garlics-listen.md | 5 ++++ .changeset/wise-cameras-agree.md | 23 ++++++++++++++ packages/astro/src/core/build/generate.ts | 6 +++- packages/integrations/cloudflare/README.md | 30 ++++++++++++++----- packages/integrations/cloudflare/src/index.ts | 22 +++++++++++--- .../function-per-route/astro.config.mjs | 15 ++++++++++ .../package.json | 2 +- .../src/middleware.ts | 0 .../pages/[language]/files/[...path].astro | 0 .../src/pages/[person]/[car].astro | 0 .../src/pages/blog/[post].astro | 0 .../src/pages/blog/cool.astro | 0 .../src/pages/files/[...path].astro | 0 .../src/pages/index.astro | 0 .../src/pages/javascript.js | 0 .../src/pages/prerender.astro | 0 .../src/pages/test.json.ts | 0 .../src/pages/trpc/[trpc].ts | 0 ...lit.test.js => function-per-route.test.js} | 18 ++--------- pnpm-lock.yaml | 10 +++---- 20 files changed, 97 insertions(+), 34 deletions(-) create mode 100644 .changeset/nasty-garlics-listen.md create mode 100644 .changeset/wise-cameras-agree.md create mode 100644 packages/integrations/cloudflare/test/fixtures/function-per-route/astro.config.mjs rename packages/integrations/cloudflare/test/fixtures/{split => function-per-route}/package.json (71%) rename packages/integrations/cloudflare/test/fixtures/{split => function-per-route}/src/middleware.ts (100%) rename packages/integrations/cloudflare/test/fixtures/{split => function-per-route}/src/pages/[language]/files/[...path].astro (100%) rename packages/integrations/cloudflare/test/fixtures/{split => function-per-route}/src/pages/[person]/[car].astro (100%) rename packages/integrations/cloudflare/test/fixtures/{split => function-per-route}/src/pages/blog/[post].astro (100%) rename packages/integrations/cloudflare/test/fixtures/{split => function-per-route}/src/pages/blog/cool.astro (100%) rename packages/integrations/cloudflare/test/fixtures/{split => function-per-route}/src/pages/files/[...path].astro (100%) rename packages/integrations/cloudflare/test/fixtures/{split => function-per-route}/src/pages/index.astro (100%) rename packages/integrations/cloudflare/test/fixtures/{split => function-per-route}/src/pages/javascript.js (100%) rename packages/integrations/cloudflare/test/fixtures/{split => function-per-route}/src/pages/prerender.astro (100%) rename packages/integrations/cloudflare/test/fixtures/{split => function-per-route}/src/pages/test.json.ts (100%) rename packages/integrations/cloudflare/test/fixtures/{split => function-per-route}/src/pages/trpc/[trpc].ts (100%) rename packages/integrations/cloudflare/test/{directory-split.test.js => function-per-route.test.js} (78%) diff --git a/.changeset/nasty-garlics-listen.md b/.changeset/nasty-garlics-listen.md new file mode 100644 index 000000000000..e1b19b96b2e1 --- /dev/null +++ b/.changeset/nasty-garlics-listen.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Reimplement https://github.com/withastro/astro/pull/7509 to correctly emit pre-rendered pages now that `build.split` is deprecated and this configuration has been moved to `functionPerRoute` inside the adapter. diff --git a/.changeset/wise-cameras-agree.md b/.changeset/wise-cameras-agree.md new file mode 100644 index 000000000000..c5aa7fbc17bc --- /dev/null +++ b/.changeset/wise-cameras-agree.md @@ -0,0 +1,23 @@ +--- +'@astrojs/cloudflare': major +--- + +The configuration `build.split` and `build.excludeMiddleware` are deprecated. + +You can now configure this behavior using `functionPerRoute` in your Cloudflare integration config: + +```diff +import {defineConfig} from "astro/config"; +import cloudflare from '@astrojs/cloudflare'; + +export default defineConfig({ +- build: { +- split: true +- }, +- adapter: cloudflare() ++ adapter: cloudflare({ ++ mode: 'directory', ++ functionPerRoute: true ++ }) +}) +``` diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 4e89dfb61f7f..6c3acefdbc5d 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -158,7 +158,11 @@ export async function generatePages(opts: StaticBuildOptions, internals: BuildIn if (pageData.route.prerender) { const ssrEntryURLPage = createEntryURL(filePath, outFolder); const ssrEntryPage = await import(ssrEntryURLPage.toString()); - if (opts.settings.config.build.split) { + if ( + // TODO: remove in Astro 4.0 + opts.settings.config.build.split || + opts.settings.adapter?.adapterFeatures?.functionPerRoute + ) { // forcing to use undefined, so we fail in an expected way if the module is not even there. const ssrEntry = ssrEntryPage?.manifest?.pageModule; if (ssrEntry) { diff --git a/packages/integrations/cloudflare/README.md b/packages/integrations/cloudflare/README.md index 61db6effc6ec..d8c1fd1979b9 100644 --- a/packages/integrations/cloudflare/README.md +++ b/packages/integrations/cloudflare/README.md @@ -44,23 +44,37 @@ export default defineConfig({ default `"advanced"` -Cloudflare Pages has 2 different modes for deploying functions, `advanced` mode which picks up the `_worker.js` in `dist`, or a directory mode where pages will compile the worker out of a functions folder in the project root. +Cloudflare Pages has 2 different modes for deploying functions, `advanced` mode which picks up the `_worker.js` in `dist`, or a directory mode where pages will compile the worker out of a functions folder in the project root. For most projects the adapter default of `advanced` will be sufficient; the `dist` folder will contain your compiled project. -For most projects the adapter default of `advanced` will be sufficient; the `dist` folder will contain your compiled project. Switching to directory mode allows you to use [pages plugins](https://developers.cloudflare.com/pages/platform/functions/plugins/) such as [Sentry](https://developers.cloudflare.com/pages/platform/functions/plugins/sentry/) or write custom code to enable logging. +#### `mode:directory` -In directory mode, the adapter will compile the client side part of your app the same way by default, but moves the worker script into a `functions` folder in the project root. In this case, the adapter will only ever place a `[[path]].js` in that folder, allowing you to add additional plugins and pages middleware which can be checked into version control. - -With the build configuration `split: true`, the adapter instead compiles a separate bundle for each page. This option requires some manual maintenance of the `functions` folder. Files emitted by Astro will overwrite existing `functions` files with identical names, so you must choose unique file names for each file you manually add. Additionally, the adapter will never empty the `functions` folder of outdated files, so you must clean up the folder manually when you remove pages. - -Note that this adapter does not support using [Cloudflare Pages Middleware](https://developers.cloudflare.com/pages/platform/functions/middleware/). Astro will bundle the [Astro middleware](https://docs.astro.build/en/guides/middleware/) into each page. +Switching to directory mode allows you to use [pages plugins](https://developers.cloudflare.com/pages/platform/functions/plugins/) such as [Sentry](https://developers.cloudflare.com/pages/platform/functions/plugins/sentry/) or write custom code to enable logging. ```ts -// directory mode +// astro.config.mjs export default defineConfig({ adapter: cloudflare({ mode: 'directory' }), }); ``` +In `directory` mode, the adapter will compile the client-side part of your app the same way as in `advanced` mode by default, but moves the worker script into a `functions` folder in the project root. In this case, the adapter will only ever place a `[[path]].js` in that folder, allowing you to add additional plugins and pages middleware which can be checked into version control. + +To instead compile a separate bundle for each page, set the `functionPerPath` option in your Cloudflare adapter config. This option requires some manual maintenance of the `functions` folder. Files emitted by Astro will overwrite existing `functions` files with identical names, so you must choose unique file names for each file you manually add. Additionally, the adapter will never empty the `functions` folder of outdated files, so you must clean up the folder manually when you remove pages. + +```diff +import {defineConfig} from "astro/config"; +import cloudflare from '@astrojs/cloudflare'; + +export default defineConfig({ + adapter: cloudflare({ + mode: 'directory', ++ functionPerRoute: true + }) +}) +``` + +Note that this adapter does not support using [Cloudflare Pages Middleware](https://developers.cloudflare.com/pages/platform/functions/middleware/). Astro will bundle the [Astro middleware](https://docs.astro.build/en/guides/middleware/) into each page. + ## Enabling Preview In order for preview to work you must install `wrangler` diff --git a/packages/integrations/cloudflare/src/index.ts b/packages/integrations/cloudflare/src/index.ts index 6b64a0a1fc6d..76e9092cae45 100644 --- a/packages/integrations/cloudflare/src/index.ts +++ b/packages/integrations/cloudflare/src/index.ts @@ -9,6 +9,7 @@ import glob from 'tiny-glob'; type Options = { mode: 'directory' | 'advanced'; + functionPerRoute?: boolean; }; interface BuildConfig { @@ -18,12 +19,22 @@ interface BuildConfig { split?: boolean; } -export function getAdapter(isModeDirectory: boolean): AstroAdapter { +export function getAdapter({ + isModeDirectory, + functionPerRoute, +}: { + isModeDirectory: boolean; + functionPerRoute: boolean; +}): AstroAdapter { return isModeDirectory ? { name: '@astrojs/cloudflare', serverEntrypoint: '@astrojs/cloudflare/server.directory.js', exports: ['onRequest', 'manifest'], + adapterFeatures: { + functionPerRoute, + edgeMiddleware: false, + }, supportedAstroFeatures: { hybridOutput: 'stable', staticOutput: 'unsupported', @@ -67,9 +78,11 @@ const potentialFunctionRouteTypes = ['endpoint', 'page']; export default function createIntegration(args?: Options): AstroIntegration { let _config: AstroConfig; let _buildConfig: BuildConfig; - const isModeDirectory = args?.mode === 'directory'; let _entryPoints = new Map(); + const isModeDirectory = args?.mode === 'directory'; + const functionPerRoute = args?.functionPerRoute ?? false; + return { name: '@astrojs/cloudflare', hooks: { @@ -84,7 +97,7 @@ export default function createIntegration(args?: Options): AstroIntegration { }); }, 'astro:config:done': ({ setAdapter, config }) => { - setAdapter(getAdapter(isModeDirectory)); + setAdapter(getAdapter({ isModeDirectory, functionPerRoute })); _config = config; _buildConfig = config.build; @@ -136,7 +149,8 @@ export default function createIntegration(args?: Options): AstroIntegration { await fs.promises.mkdir(functionsUrl, { recursive: true }); } - if (isModeDirectory && _buildConfig.split) { + // TODO: remove _buildConfig.split in Astro 4.0 + if (isModeDirectory && (_buildConfig.split || functionPerRoute)) { const entryPointsURL = [..._entryPoints.values()]; const entryPaths = entryPointsURL.map((entry) => fileURLToPath(entry)); const outputUrl = new URL('$astro', _buildConfig.server); diff --git a/packages/integrations/cloudflare/test/fixtures/function-per-route/astro.config.mjs b/packages/integrations/cloudflare/test/fixtures/function-per-route/astro.config.mjs new file mode 100644 index 000000000000..aa3f3dabcadc --- /dev/null +++ b/packages/integrations/cloudflare/test/fixtures/function-per-route/astro.config.mjs @@ -0,0 +1,15 @@ +import { defineConfig } from 'astro/config'; +import cloudflare from '@astrojs/cloudflare'; + +export default defineConfig({ + adapter: cloudflare({ + mode: 'directory', + functionPerRoute: true + }), + output: 'server', + vite: { + build: { + minify: false, + }, + }, +}); diff --git a/packages/integrations/cloudflare/test/fixtures/split/package.json b/packages/integrations/cloudflare/test/fixtures/function-per-route/package.json similarity index 71% rename from packages/integrations/cloudflare/test/fixtures/split/package.json rename to packages/integrations/cloudflare/test/fixtures/function-per-route/package.json index fd7dcc2530da..54dded9dd0e1 100644 --- a/packages/integrations/cloudflare/test/fixtures/split/package.json +++ b/packages/integrations/cloudflare/test/fixtures/function-per-route/package.json @@ -1,5 +1,5 @@ { - "name": "@test/astro-cloudflare-split", + "name": "@test/astro-cloudflare-function-per-route", "version": "0.0.0", "private": true, "dependencies": { diff --git a/packages/integrations/cloudflare/test/fixtures/split/src/middleware.ts b/packages/integrations/cloudflare/test/fixtures/function-per-route/src/middleware.ts similarity index 100% rename from packages/integrations/cloudflare/test/fixtures/split/src/middleware.ts rename to packages/integrations/cloudflare/test/fixtures/function-per-route/src/middleware.ts diff --git a/packages/integrations/cloudflare/test/fixtures/split/src/pages/[language]/files/[...path].astro b/packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/[language]/files/[...path].astro similarity index 100% rename from packages/integrations/cloudflare/test/fixtures/split/src/pages/[language]/files/[...path].astro rename to packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/[language]/files/[...path].astro diff --git a/packages/integrations/cloudflare/test/fixtures/split/src/pages/[person]/[car].astro b/packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/[person]/[car].astro similarity index 100% rename from packages/integrations/cloudflare/test/fixtures/split/src/pages/[person]/[car].astro rename to packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/[person]/[car].astro diff --git a/packages/integrations/cloudflare/test/fixtures/split/src/pages/blog/[post].astro b/packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/blog/[post].astro similarity index 100% rename from packages/integrations/cloudflare/test/fixtures/split/src/pages/blog/[post].astro rename to packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/blog/[post].astro diff --git a/packages/integrations/cloudflare/test/fixtures/split/src/pages/blog/cool.astro b/packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/blog/cool.astro similarity index 100% rename from packages/integrations/cloudflare/test/fixtures/split/src/pages/blog/cool.astro rename to packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/blog/cool.astro diff --git a/packages/integrations/cloudflare/test/fixtures/split/src/pages/files/[...path].astro b/packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/files/[...path].astro similarity index 100% rename from packages/integrations/cloudflare/test/fixtures/split/src/pages/files/[...path].astro rename to packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/files/[...path].astro diff --git a/packages/integrations/cloudflare/test/fixtures/split/src/pages/index.astro b/packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/index.astro similarity index 100% rename from packages/integrations/cloudflare/test/fixtures/split/src/pages/index.astro rename to packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/index.astro diff --git a/packages/integrations/cloudflare/test/fixtures/split/src/pages/javascript.js b/packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/javascript.js similarity index 100% rename from packages/integrations/cloudflare/test/fixtures/split/src/pages/javascript.js rename to packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/javascript.js diff --git a/packages/integrations/cloudflare/test/fixtures/split/src/pages/prerender.astro b/packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/prerender.astro similarity index 100% rename from packages/integrations/cloudflare/test/fixtures/split/src/pages/prerender.astro rename to packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/prerender.astro diff --git a/packages/integrations/cloudflare/test/fixtures/split/src/pages/test.json.ts b/packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/test.json.ts similarity index 100% rename from packages/integrations/cloudflare/test/fixtures/split/src/pages/test.json.ts rename to packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/test.json.ts diff --git a/packages/integrations/cloudflare/test/fixtures/split/src/pages/trpc/[trpc].ts b/packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/trpc/[trpc].ts similarity index 100% rename from packages/integrations/cloudflare/test/fixtures/split/src/pages/trpc/[trpc].ts rename to packages/integrations/cloudflare/test/fixtures/function-per-route/src/pages/trpc/[trpc].ts diff --git a/packages/integrations/cloudflare/test/directory-split.test.js b/packages/integrations/cloudflare/test/function-per-route.test.js similarity index 78% rename from packages/integrations/cloudflare/test/directory-split.test.js rename to packages/integrations/cloudflare/test/function-per-route.test.js index 6e6b0bfe29aa..d20b0fa7c3f3 100644 --- a/packages/integrations/cloudflare/test/directory-split.test.js +++ b/packages/integrations/cloudflare/test/function-per-route.test.js @@ -1,25 +1,13 @@ import { loadFixture } from './test-utils.js'; import { expect } from 'chai'; -import cloudflare from '../dist/index.js'; -/** @type {import('./test-utils').Fixture} */ -describe('Cloudflare SSR split', () => { +/** @type {import('./test-utils.js').Fixture} */ +describe('Cloudflare SSR functionPerRoute', () => { let fixture; before(async () => { fixture = await loadFixture({ - root: './fixtures/split/', - adapter: cloudflare({ mode: 'directory' }), - output: 'server', - build: { - split: true, - excludeMiddleware: false, - }, - vite: { - build: { - minify: false, - }, - }, + root: './fixtures/function-per-route/', }); await fixture.build(); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 47c2d5ca50c4..3b79ac9a7121 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3644,7 +3644,7 @@ importers: specifier: workspace:* version: link:../../../../../astro - packages/integrations/cloudflare/test/fixtures/no-output: + packages/integrations/cloudflare/test/fixtures/function-per-route: dependencies: '@astrojs/cloudflare': specifier: workspace:* @@ -3653,7 +3653,7 @@ importers: specifier: workspace:* version: link:../../../../../astro - packages/integrations/cloudflare/test/fixtures/prerender: + packages/integrations/cloudflare/test/fixtures/no-output: dependencies: '@astrojs/cloudflare': specifier: workspace:* @@ -3662,7 +3662,7 @@ importers: specifier: workspace:* version: link:../../../../../astro - packages/integrations/cloudflare/test/fixtures/routesJson: + packages/integrations/cloudflare/test/fixtures/prerender: dependencies: '@astrojs/cloudflare': specifier: workspace:* @@ -3671,7 +3671,7 @@ importers: specifier: workspace:* version: link:../../../../../astro - packages/integrations/cloudflare/test/fixtures/runtime: + packages/integrations/cloudflare/test/fixtures/routesJson: dependencies: '@astrojs/cloudflare': specifier: workspace:* @@ -3680,7 +3680,7 @@ importers: specifier: workspace:* version: link:../../../../../astro - packages/integrations/cloudflare/test/fixtures/split: + packages/integrations/cloudflare/test/fixtures/runtime: dependencies: '@astrojs/cloudflare': specifier: workspace:*