From 6ec040761ef657df0e0f5ac103788da4b98fa688 Mon Sep 17 00:00:00 2001 From: Alexander Niebuhr Date: Mon, 17 Jul 2023 15:12:41 +0200 Subject: [PATCH] fix(@astrojs/cloudflare): SSR split file renaming misses ts endpoints (#7568) * fix bug, where ts files where not renamed correctly * try to make rename logic more robust * remove log * update tests * update changeset * cleanup * fix lint * debug windows tests * fix windows support * fix cloudflare directory code * use EventContext type * improve for loop * change changeset Co-authored-by: Emanuele Stoppa * change changeset Co-authored-by: Emanuele Stoppa --------- Co-authored-by: Emanuele Stoppa --- .changeset/large-meals-joke.md | 5 ++ .changeset/sweet-bats-clap.md | 5 ++ packages/integrations/cloudflare/src/index.ts | 79 +++++++++++-------- .../cloudflare/src/server.directory.ts | 4 +- .../cloudflare/test/directory-split.test.js | 3 + .../fixtures/split/src/pages/javascript.js | 0 .../fixtures/split/src/pages/test.json.ts | 0 .../fixtures/split/src/pages/trpc/[trpc].ts | 0 8 files changed, 63 insertions(+), 33 deletions(-) create mode 100644 .changeset/large-meals-joke.md create mode 100644 .changeset/sweet-bats-clap.md create mode 100644 packages/integrations/cloudflare/test/fixtures/split/src/pages/javascript.js create mode 100644 packages/integrations/cloudflare/test/fixtures/split/src/pages/test.json.ts create mode 100644 packages/integrations/cloudflare/test/fixtures/split/src/pages/trpc/[trpc].ts diff --git a/.changeset/large-meals-joke.md b/.changeset/large-meals-joke.md new file mode 100644 index 000000000..45060ea93 --- /dev/null +++ b/.changeset/large-meals-joke.md @@ -0,0 +1,5 @@ +--- +'@astrojs/cloudflare': patch +--- + +Fix a bug where asset redirects caused Cloudflare error diff --git a/.changeset/sweet-bats-clap.md b/.changeset/sweet-bats-clap.md new file mode 100644 index 000000000..8cf15f8e5 --- /dev/null +++ b/.changeset/sweet-bats-clap.md @@ -0,0 +1,5 @@ +--- +'@astrojs/cloudflare': patch +--- + +Fix bug where `.ts` files are not renamed to `.js` diff --git a/packages/integrations/cloudflare/src/index.ts b/packages/integrations/cloudflare/src/index.ts index 40ee006f3..3385acd0d 100644 --- a/packages/integrations/cloudflare/src/index.ts +++ b/packages/integrations/cloudflare/src/index.ts @@ -3,7 +3,7 @@ import type { AstroAdapter, AstroConfig, AstroIntegration, RouteData } from 'ast import esbuild from 'esbuild'; import * as fs from 'fs'; import * as os from 'os'; -import { dirname } from 'path'; +import { sep } from 'path'; import glob from 'tiny-glob'; import { fileURLToPath, pathToFileURL } from 'url'; @@ -21,15 +21,15 @@ interface BuildConfig { export function getAdapter(isModeDirectory: boolean): AstroAdapter { return isModeDirectory ? { - name: '@astrojs/cloudflare', - serverEntrypoint: '@astrojs/cloudflare/server.directory.js', - exports: ['onRequest', 'manifest'], - } + name: '@astrojs/cloudflare', + serverEntrypoint: '@astrojs/cloudflare/server.directory.js', + exports: ['onRequest', 'manifest'], + } : { - name: '@astrojs/cloudflare', - serverEntrypoint: '@astrojs/cloudflare/server.advanced.js', - exports: ['default'], - }; + name: '@astrojs/cloudflare', + serverEntrypoint: '@astrojs/cloudflare/server.advanced.js', + exports: ['default'], + }; } const SHIM = `globalThis.process = { @@ -112,13 +112,12 @@ export default function createIntegration(args?: Options): AstroIntegration { } if (isModeDirectory && _buildConfig.split) { - const entryPointsRouteData = [..._entryPoints.keys()]; const entryPointsURL = [..._entryPoints.values()]; const entryPaths = entryPointsURL.map((entry) => fileURLToPath(entry)); - const outputDir = fileURLToPath(new URL('.astro', _buildConfig.server)); + const outputUrl = new URL('$astro', _buildConfig.server) + const outputDir = fileURLToPath(outputUrl); - // NOTE: AFAIK, esbuild keeps the order of the entryPoints array - const { outputFiles } = await esbuild.build({ + await esbuild.build({ target: 'es2020', platform: 'browser', conditions: ['workerd', 'worker', 'browser'], @@ -134,28 +133,44 @@ export default function createIntegration(args?: Options): AstroIntegration { logOverride: { 'ignored-bare-import': 'silent', }, - write: false, }); - // loop through all bundled files and write them to the functions folder - for (const [index, outputFile] of outputFiles.entries()) { - // we need to make sure the filename in the functions folder - // matches to cloudflares routing capabilities (see their docs) - // IN: src/pages/[language]/files/[...path].astro - // OUT: [language]/files/[[path]].js - const fileName = entryPointsRouteData[index].component - .replace('src/pages/', '') - .replace('.astro', '.js') - .replace(/(\[\.\.\.)(\w+)(\])/g, (_match, _p1, p2) => { - return `[[${p2}]]`; - }); + const outputFiles: Array = ( + await glob(`**/*`, { + cwd: outputDir, + filesOnly: true, + }) + ) - const fileUrl = new URL(fileName, functionsUrl); - const newFileDir = dirname(fileURLToPath(fileUrl)); - if (!fs.existsSync(newFileDir)) { - fs.mkdirSync(newFileDir, { recursive: true }); - } - await fs.promises.writeFile(fileUrl, outputFile.contents); + // move the files into the functions folder + // & make sure the file names match Cloudflare syntax for routing + for (const outputFile of outputFiles) { + const path = outputFile.split(sep); + + const finalSegments = path.map((segment) => segment + .replace(/(\_)(\w+)(\_)/g, (_, __, prop) => { + return `[${prop}]`; + }) + .replace(/(\_\-\-\-)(\w+)(\_)/g, (_, __, prop) => { + return `[[${prop}]]`; + }) + ); + + finalSegments[finalSegments.length - 1] = finalSegments[finalSegments.length - 1] + .replace('entry.', '') + .replace(/(.*)\.(\w+)\.(\w+)$/g, (_, fileName, __, newExt) => { + return `${fileName}.${newExt}`; + }) + + const finalDirPath = finalSegments.slice(0, -1).join(sep); + const finalPath = finalSegments.join(sep); + + const newDirUrl = new URL(finalDirPath, functionsUrl); + await fs.promises.mkdir(newDirUrl, { recursive: true }) + + const oldFileUrl = new URL(`$astro/${outputFile}`, outputUrl); + const newFileUrl = new URL(finalPath, functionsUrl); + await fs.promises.rename(oldFileUrl, newFileUrl); } } else { const entryPath = fileURLToPath(new URL(_buildConfig.serverEntry, _buildConfig.server)); diff --git a/packages/integrations/cloudflare/src/server.directory.ts b/packages/integrations/cloudflare/src/server.directory.ts index f9f71a33b..e6cc104f9 100644 --- a/packages/integrations/cloudflare/src/server.directory.ts +++ b/packages/integrations/cloudflare/src/server.directory.ts @@ -24,7 +24,9 @@ export function createExports(manifest: SSRManifest) { const { pathname } = new URL(request.url); // static assets fallback, in case default _routes.json is not used if (manifest.assets.has(pathname)) { - return next(request); + // we need this so the page does not error + // https://developers.cloudflare.com/pages/platform/functions/advanced-mode/#set-up-a-function + return (runtimeEnv.env as EventContext['env']).ASSETS.fetch(request); } let routeData = app.match(request, { matchNotFound: true }); diff --git a/packages/integrations/cloudflare/test/directory-split.test.js b/packages/integrations/cloudflare/test/directory-split.test.js index 384543a4b..6e6b0bfe2 100644 --- a/packages/integrations/cloudflare/test/directory-split.test.js +++ b/packages/integrations/cloudflare/test/directory-split.test.js @@ -36,6 +36,9 @@ describe('Cloudflare SSR split', () => { expect(await fixture.pathExists('../functions/[person]/[car].js')).to.be.true; expect(await fixture.pathExists('../functions/files/[[path]].js')).to.be.true; expect(await fixture.pathExists('../functions/[language]/files/[[path]].js')).to.be.true; + expect(await fixture.pathExists('../functions/trpc/[trpc].js')).to.be.true; + expect(await fixture.pathExists('../functions/javascript.js')).to.be.true; + expect(await fixture.pathExists('../functions/test.json.js')).to.be.true; }); it('generates pre-rendered files', async () => { diff --git a/packages/integrations/cloudflare/test/fixtures/split/src/pages/javascript.js b/packages/integrations/cloudflare/test/fixtures/split/src/pages/javascript.js new file mode 100644 index 000000000..e69de29bb diff --git a/packages/integrations/cloudflare/test/fixtures/split/src/pages/test.json.ts b/packages/integrations/cloudflare/test/fixtures/split/src/pages/test.json.ts new file mode 100644 index 000000000..e69de29bb diff --git a/packages/integrations/cloudflare/test/fixtures/split/src/pages/trpc/[trpc].ts b/packages/integrations/cloudflare/test/fixtures/split/src/pages/trpc/[trpc].ts new file mode 100644 index 000000000..e69de29bb