summaryrefslogtreecommitdiff
path: root/packages/integrations/cloudflare/src
diff options
context:
space:
mode:
authorGravatar Emanuele Stoppa <my.burning@gmail.com> 2023-08-10 11:49:52 +0100
committerGravatar Emanuele Stoppa <my.burning@gmail.com> 2023-08-10 11:49:52 +0100
commit91fa61a497eb2aeabfe2d07c4988deca2614a0e4 (patch)
tree16f96e2e26b3c83aa9059fa0f020952768a51d78 /packages/integrations/cloudflare/src
parent846715ef969073c7371ee5975f30f75ab2e71fa4 (diff)
parentc1239103afceb1c45e8696dec48a6410be5985c1 (diff)
downloadastro-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.ts90
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;
+ });
+}