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:*