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 <my.burning@gmail.com>

* change changeset

Co-authored-by: Emanuele Stoppa <my.burning@gmail.com>

---------

Co-authored-by: Emanuele Stoppa <my.burning@gmail.com>
This commit is contained in:
Alexander Niebuhr 2023-07-17 15:12:41 +02:00 committed by GitHub
parent 1f0d0b5863
commit 6ec040761e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 63 additions and 33 deletions

View file

@ -0,0 +1,5 @@
---
'@astrojs/cloudflare': patch
---
Fix a bug where asset redirects caused Cloudflare error

View file

@ -0,0 +1,5 @@
---
'@astrojs/cloudflare': patch
---
Fix bug where `.ts` files are not renamed to `.js`

View file

@ -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<string> = (
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));

View file

@ -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<unknown, string, unknown>['env']).ASSETS.fetch(request);
}
let routeData = app.match(request, { matchNotFound: true });

View file

@ -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 () => {