diff options
author | 2023-08-10 11:49:52 +0100 | |
---|---|---|
committer | 2023-08-10 11:49:52 +0100 | |
commit | 91fa61a497eb2aeabfe2d07c4988deca2614a0e4 (patch) | |
tree | 16f96e2e26b3c83aa9059fa0f020952768a51d78 /packages/integrations/cloudflare/src | |
parent | 846715ef969073c7371ee5975f30f75ab2e71fa4 (diff) | |
parent | c1239103afceb1c45e8696dec48a6410be5985c1 (diff) | |
download | astro-91fa61a497eb2aeabfe2d07c4988deca2614a0e4.tar.gz astro-91fa61a497eb2aeabfe2d07c4988deca2614a0e4.tar.zst astro-91fa61a497eb2aeabfe2d07c4988deca2614a0e4.zip |
Merge remote-tracking branch 'origin/main' into next
Diffstat (limited to 'packages/integrations/cloudflare/src')
-rw-r--r-- | packages/integrations/cloudflare/src/index.ts | 90 |
1 files changed, 87 insertions, 3 deletions
diff --git a/packages/integrations/cloudflare/src/index.ts b/packages/integrations/cloudflare/src/index.ts index a3bb76fbb..46d87a3eb 100644 --- a/packages/integrations/cloudflare/src/index.ts +++ b/packages/integrations/cloudflare/src/index.ts @@ -59,6 +59,11 @@ const SHIM = `globalThis.process = { const SERVER_BUILD_FOLDER = '/$server_build/'; +/** + * These route types are candiates for being part of the `_routes.json` `include` array. + */ +const potentialFunctionRouteTypes = ['endpoint', 'page']; + export default function createIntegration(args?: Options): AstroIntegration { let _config: AstroConfig; let _buildConfig: BuildConfig; @@ -253,6 +258,32 @@ export default function createIntegration(args?: Options): AstroIntegration { // cloudflare to handle static files and support _redirects configuration // (without calling the function) if (!routesExists) { + const functionEndpoints = routes + // Certain route types, when their prerender option is set to false, a run on the server as function invocations + .filter((route) => potentialFunctionRouteTypes.includes(route.type) && !route.prerender) + .map((route) => { + const includePattern = + '/' + + route.segments + .flat() + .map((segment) => (segment.dynamic ? '*' : segment.content)) + .join('/'); + + const regexp = new RegExp( + '^\\/' + + route.segments + .flat() + .map((segment) => (segment.dynamic ? '(.*)' : segment.content)) + .join('\\/') + + '$' + ); + + return { + includePattern, + regexp, + }; + }); + const staticPathList: Array<string> = ( await glob(`${fileURLToPath(_buildConfig.client)}/**/*`, { cwd: fileURLToPath(_config.outDir), @@ -260,7 +291,7 @@ export default function createIntegration(args?: Options): AstroIntegration { }) ) .filter((file: string) => cloudflareSpecialFiles.indexOf(file) < 0) - .map((file: string) => `/${file}`); + .map((file: string) => `/${file.replace(/\\/g, '/')}`); for (let page of pages) { let pagePath = prependForwardSlash(page.pathname); @@ -323,13 +354,41 @@ export default function createIntegration(args?: Options): AstroIntegration { ); } + staticPathList.push(...routes.filter((r) => r.type === 'redirect').map((r) => r.route)); + + // In order to product the shortest list of patterns, we first try to + // include all function endpoints, and then exclude all static paths + let include = deduplicatePatterns( + functionEndpoints.map((endpoint) => endpoint.includePattern) + ); + let exclude = deduplicatePatterns( + staticPathList.filter((file: string) => + functionEndpoints.some((endpoint) => endpoint.regexp.test(file)) + ) + ); + + // Cloudflare requires at least one include pattern: + // https://developers.cloudflare.com/pages/platform/functions/routing/#limits + // So we add a pattern that we immediately exclude again + if (include.length === 0) { + include = ['/']; + exclude = ['/']; + } + + // If using only an exclude list would produce a shorter list of patterns, + // we use that instead + if (include.length + exclude.length > staticPathList.length) { + include = ['/*']; + exclude = deduplicatePatterns(staticPathList); + } + await fs.promises.writeFile( new URL('./_routes.json', _config.outDir), JSON.stringify( { version: 1, - include: ['/*'], - exclude: staticPathList, + include, + exclude, }, null, 2 @@ -344,3 +403,28 @@ export default function createIntegration(args?: Options): AstroIntegration { function prependForwardSlash(path: string) { return path[0] === '/' ? path : '/' + path; } + +/** + * Remove duplicates and redundant patterns from an `include` or `exclude` list. + * Otherwise Cloudflare will throw an error on deployment. Plus, it saves more entries. + * E.g. `['/foo/*', '/foo/*', '/foo/bar'] => ['/foo/*']` + * @param patterns a list of `include` or `exclude` patterns + * @returns a deduplicated list of patterns + */ +function deduplicatePatterns(patterns: string[]) { + const openPatterns: RegExp[] = []; + + return [...new Set(patterns)] + .sort((a, b) => a.length - b.length) + .filter((pattern) => { + if (openPatterns.some((p) => p.test(pattern))) { + return false; + } + + if (pattern.endsWith('*')) { + openPatterns.push(new RegExp(`^${pattern.replace(/(\*\/)*\*$/g, '.*')}`)); + } + + return true; + }); +} |