@astrojs/cloudflare 0.0.0-cloudcannon-fix-20230306211609

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/CHANGELOG.md +379 -0
  3. package/LICENSE +61 -0
  4. package/README.md +119 -0
  5. package/dist/index.d.ts +7 -0
  6. package/dist/index.js +156 -0
  7. package/dist/runtime.d.ts +16 -0
  8. package/dist/runtime.js +12 -0
  9. package/dist/server.advanced.d.ts +13 -0
  10. package/dist/server.advanced.js +39 -0
  11. package/dist/server.directory.d.ts +7 -0
  12. package/dist/server.directory.js +47 -0
  13. package/dist/util.d.ts +2 -0
  14. package/dist/util.js +17 -0
  15. package/package.json +51 -0
  16. package/runtime.d.ts +3 -0
  17. package/src/index.ts +219 -0
  18. package/src/runtime.ts +28 -0
  19. package/src/server.advanced.ts +53 -0
  20. package/src/server.directory.ts +58 -0
  21. package/src/util.ts +19 -0
  22. package/test/basics.test.js +32 -0
  23. package/test/directory.test.js +22 -0
  24. package/test/fixtures/basics/astro.config.mjs +10 -0
  25. package/test/fixtures/basics/node_modules/.bin/astro +17 -0
  26. package/test/fixtures/basics/package.json +9 -0
  27. package/test/fixtures/basics/src/pages/index.astro +9 -0
  28. package/test/fixtures/no-output/astro.config.mjs +6 -0
  29. package/test/fixtures/no-output/node_modules/.bin/astro +17 -0
  30. package/test/fixtures/no-output/package.json +9 -0
  31. package/test/fixtures/prerender/astro.config.mjs +7 -0
  32. package/test/fixtures/prerender/node_modules/.bin/astro +17 -0
  33. package/test/fixtures/prerender/package.json +9 -0
  34. package/test/fixtures/prerender/src/pages/index.astro +8 -0
  35. package/test/fixtures/prerender/src/pages/one.astro +11 -0
  36. package/test/no-output.test.js +24 -0
  37. package/test/prerender.test.js +19 -0
  38. package/test/test-utils.js +62 -0
  39. package/test/wrangler.toml +4 -0
  40. package/tsconfig.json +10 -0
package/dist/index.js ADDED
@@ -0,0 +1,156 @@
1
+ import esbuild from "esbuild";
2
+ import * as fs from "fs";
3
+ import * as os from "os";
4
+ import glob from "tiny-glob";
5
+ import { fileURLToPath, pathToFileURL } from "url";
6
+ function getAdapter(isModeDirectory) {
7
+ return isModeDirectory ? {
8
+ name: "@astrojs/cloudflare",
9
+ serverEntrypoint: "@astrojs/cloudflare/server.directory.js",
10
+ exports: ["onRequest"]
11
+ } : {
12
+ name: "@astrojs/cloudflare",
13
+ serverEntrypoint: "@astrojs/cloudflare/server.advanced.js",
14
+ exports: ["default"]
15
+ };
16
+ }
17
+ const SHIM = `globalThis.process = {
18
+ argv: [],
19
+ env: {},
20
+ };`;
21
+ const SERVER_BUILD_FOLDER = "/$server_build/";
22
+ function createIntegration(args) {
23
+ let _config;
24
+ let _buildConfig;
25
+ const isModeDirectory = (args == null ? void 0 : args.mode) === "directory";
26
+ return {
27
+ name: "@astrojs/cloudflare",
28
+ hooks: {
29
+ "astro:config:setup": ({ config, updateConfig }) => {
30
+ updateConfig({
31
+ build: {
32
+ client: new URL(`.${config.base}`, config.outDir),
33
+ server: new URL(`.${SERVER_BUILD_FOLDER}`, config.outDir),
34
+ serverEntry: "_worker.mjs"
35
+ }
36
+ });
37
+ },
38
+ "astro:config:done": ({ setAdapter, config }) => {
39
+ setAdapter(getAdapter(isModeDirectory));
40
+ _config = config;
41
+ _buildConfig = config.build;
42
+ if (config.output === "static") {
43
+ throw new Error(`
44
+ [@astrojs/cloudflare] \`output: "server"\` is required to use this adapter. Otherwise, this adapter is not necessary to deploy a static site to Cloudflare.
45
+
46
+ `);
47
+ }
48
+ if (config.base === SERVER_BUILD_FOLDER) {
49
+ throw new Error(`
50
+ [@astrojs/cloudflare] \`base: "${SERVER_BUILD_FOLDER}"\` is not allowed. Please change your \`base\` config to something else.`);
51
+ }
52
+ },
53
+ "astro:build:setup": ({ vite, target }) => {
54
+ if (target === "server") {
55
+ vite.resolve = vite.resolve || {};
56
+ vite.resolve.alias = vite.resolve.alias || {};
57
+ const aliases = [{ find: "react-dom/server", replacement: "react-dom/server.browser" }];
58
+ if (Array.isArray(vite.resolve.alias)) {
59
+ vite.resolve.alias = [...vite.resolve.alias, ...aliases];
60
+ } else {
61
+ for (const alias of aliases) {
62
+ vite.resolve.alias[alias.find] = alias.replacement;
63
+ }
64
+ }
65
+ vite.ssr = vite.ssr || {};
66
+ vite.ssr.target = vite.ssr.target || "webworker";
67
+ }
68
+ },
69
+ "astro:build:done": async ({ pages }) => {
70
+ const entryPath = fileURLToPath(new URL(_buildConfig.serverEntry, _buildConfig.server));
71
+ const entryUrl = new URL(_buildConfig.serverEntry, _config.outDir);
72
+ const buildPath = fileURLToPath(entryUrl);
73
+ const finalBuildUrl = pathToFileURL(buildPath.replace(/\.mjs$/, ".js"));
74
+ await esbuild.build({
75
+ target: "es2020",
76
+ platform: "browser",
77
+ entryPoints: [entryPath],
78
+ outfile: buildPath,
79
+ allowOverwrite: true,
80
+ format: "esm",
81
+ bundle: true,
82
+ minify: true,
83
+ banner: {
84
+ js: SHIM
85
+ }
86
+ });
87
+ await fs.promises.rename(buildPath, finalBuildUrl);
88
+ const serverUrl = new URL(_buildConfig.server);
89
+ await fs.promises.rm(serverUrl, { recursive: true, force: true });
90
+ const cloudflareSpecialFiles = ["_headers", "_redirects", "_routes.json"];
91
+ if (_config.base !== "/") {
92
+ for (const file of cloudflareSpecialFiles) {
93
+ try {
94
+ await fs.promises.rename(
95
+ new URL(file, _buildConfig.client),
96
+ new URL(file, _config.outDir)
97
+ );
98
+ } catch (e) {
99
+ }
100
+ }
101
+ }
102
+ const routesExists = await fs.promises.stat(new URL("./_routes.json", _config.outDir)).then((stat) => stat.isFile()).catch(() => false);
103
+ if (!routesExists) {
104
+ const staticPathList = (await glob(`${fileURLToPath(_buildConfig.client)}/**/*`, {
105
+ cwd: fileURLToPath(_config.outDir),
106
+ filesOnly: true
107
+ })).filter((file) => cloudflareSpecialFiles.indexOf(file) < 0).map((file) => `/${file}`);
108
+ for (let page of pages) {
109
+ staticPathList.push(prependForwardSlash(page.pathname));
110
+ }
111
+ const redirectsExists = await fs.promises.stat(new URL("./_redirects", _config.outDir)).then((stat) => stat.isFile()).catch(() => false);
112
+ if (redirectsExists) {
113
+ const redirects = (await fs.promises.readFile(new URL("./_redirects", _config.outDir), "utf-8")).split(os.EOL).map((line) => {
114
+ const parts = line.split(" ");
115
+ if (parts.length < 2) {
116
+ return null;
117
+ } else {
118
+ return parts[0].replace(/\/:.*?(?=\/|$)/g, "/*").replace(/\?.*$/, "");
119
+ }
120
+ }).filter(
121
+ (line, index, arr) => line !== null && arr.indexOf(line) === index
122
+ );
123
+ if (redirects.length > 0) {
124
+ staticPathList.push(...redirects);
125
+ }
126
+ }
127
+ await fs.promises.writeFile(
128
+ new URL("./_routes.json", _config.outDir),
129
+ JSON.stringify(
130
+ {
131
+ version: 1,
132
+ include: ["/*"],
133
+ exclude: staticPathList
134
+ },
135
+ null,
136
+ 2
137
+ )
138
+ );
139
+ }
140
+ if (isModeDirectory) {
141
+ const functionsUrl = new URL("functions/", _config.root);
142
+ await fs.promises.mkdir(functionsUrl, { recursive: true });
143
+ const directoryUrl = new URL("[[path]].js", functionsUrl);
144
+ await fs.promises.rename(finalBuildUrl, directoryUrl);
145
+ }
146
+ }
147
+ }
148
+ };
149
+ }
150
+ function prependForwardSlash(path) {
151
+ return path[0] === "/" ? path : "/" + path;
152
+ }
153
+ export {
154
+ createIntegration as default,
155
+ getAdapter
156
+ };
@@ -0,0 +1,16 @@
1
+ export declare type WorkerRuntime<T = unknown> = {
2
+ name: 'cloudflare';
3
+ env: T;
4
+ waitUntil(promise: Promise<any>): void;
5
+ passThroughOnException(): void;
6
+ };
7
+ export declare type PagesRuntime<T = unknown, U = unknown> = {
8
+ name: 'cloudflare';
9
+ env: T;
10
+ functionPath: string;
11
+ params: Record<string, string>;
12
+ data: U;
13
+ waitUntil(promise: Promise<any>): void;
14
+ next(request: Request): void;
15
+ };
16
+ export declare function getRuntime<T = unknown, U = unknown>(request: Request): WorkerRuntime<T> | PagesRuntime<T, U>;
@@ -0,0 +1,12 @@
1
+ function getRuntime(request) {
2
+ if (!!request) {
3
+ return Reflect.get(request, Symbol.for("runtime"));
4
+ } else {
5
+ throw new Error(
6
+ "To retrieve the current cloudflare runtime you need to pass in the Astro request object"
7
+ );
8
+ }
9
+ }
10
+ export {
11
+ getRuntime
12
+ };
@@ -0,0 +1,13 @@
1
+ import type { SSRManifest } from 'astro';
2
+ declare type Env = {
3
+ ASSETS: {
4
+ fetch: (req: Request) => Promise<Response>;
5
+ };
6
+ name: string;
7
+ };
8
+ export declare function createExports(manifest: SSRManifest): {
9
+ default: {
10
+ fetch: (request: Request, env: Env, context: any) => Promise<Response>;
11
+ };
12
+ };
13
+ export {};
@@ -0,0 +1,39 @@
1
+ import { App } from "astro/app";
2
+ import { getProcessEnvProxy, isNode } from "./util.js";
3
+ if (!isNode) {
4
+ process.env = getProcessEnvProxy();
5
+ }
6
+ function createExports(manifest) {
7
+ const app = new App(manifest);
8
+ const fetch = async (request, env, context) => {
9
+ process.env = env;
10
+ const { pathname } = new URL(request.url);
11
+ if (manifest.assets.has(pathname)) {
12
+ return env.ASSETS.fetch(request);
13
+ }
14
+ let routeData = app.match(request, { matchNotFound: true });
15
+ if (routeData) {
16
+ Reflect.set(
17
+ request,
18
+ Symbol.for("astro.clientAddress"),
19
+ request.headers.get("cf-connecting-ip")
20
+ );
21
+ Reflect.set(request, Symbol.for("runtime"), { env, name: "cloudflare", ...context });
22
+ let response = await app.render(request, routeData);
23
+ if (app.setCookieHeaders) {
24
+ for (const setCookieHeader of app.setCookieHeaders(response)) {
25
+ response.headers.append("Set-Cookie", setCookieHeader);
26
+ }
27
+ }
28
+ return response;
29
+ }
30
+ return new Response(null, {
31
+ status: 404,
32
+ statusText: "Not found"
33
+ });
34
+ };
35
+ return { default: { fetch } };
36
+ }
37
+ export {
38
+ createExports
39
+ };
@@ -0,0 +1,7 @@
1
+ import type { SSRManifest } from 'astro';
2
+ export declare function createExports(manifest: SSRManifest): {
3
+ onRequest: ({ request, next, ...runtimeEnv }: {
4
+ request: Request;
5
+ next: (request: Request) => void;
6
+ } & Record<string, unknown>) => Promise<void | Response>;
7
+ };
@@ -0,0 +1,47 @@
1
+ import { App } from "astro/app";
2
+ import { getProcessEnvProxy, isNode } from "./util.js";
3
+ if (!isNode) {
4
+ process.env = getProcessEnvProxy();
5
+ }
6
+ function createExports(manifest) {
7
+ const app = new App(manifest);
8
+ const onRequest = async ({
9
+ request,
10
+ next,
11
+ ...runtimeEnv
12
+ }) => {
13
+ process.env = runtimeEnv.env;
14
+ const { pathname } = new URL(request.url);
15
+ if (manifest.assets.has(pathname)) {
16
+ return next(request);
17
+ }
18
+ let routeData = app.match(request, { matchNotFound: true });
19
+ if (routeData) {
20
+ Reflect.set(
21
+ request,
22
+ Symbol.for("astro.clientAddress"),
23
+ request.headers.get("cf-connecting-ip")
24
+ );
25
+ Reflect.set(request, Symbol.for("runtime"), {
26
+ ...runtimeEnv,
27
+ name: "cloudflare",
28
+ next
29
+ });
30
+ let response = await app.render(request, routeData);
31
+ if (app.setCookieHeaders) {
32
+ for (const setCookieHeader of app.setCookieHeaders(response)) {
33
+ response.headers.append("Set-Cookie", setCookieHeader);
34
+ }
35
+ }
36
+ return response;
37
+ }
38
+ return new Response(null, {
39
+ status: 404,
40
+ statusText: "Not found"
41
+ });
42
+ };
43
+ return { onRequest };
44
+ }
45
+ export {
46
+ createExports
47
+ };
package/dist/util.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare const isNode: boolean;
2
+ export declare function getProcessEnvProxy(): {};
package/dist/util.js ADDED
@@ -0,0 +1,17 @@
1
+ const isNode = typeof process === "object" && Object.prototype.toString.call(process) === "[object process]";
2
+ function getProcessEnvProxy() {
3
+ return new Proxy(
4
+ {},
5
+ {
6
+ get: (target, prop) => {
7
+ console.warn(
8
+ `Unable to access \`import.meta\0.env.${prop.toString()}\` on initialization as the Cloudflare platform only provides the environment variables per request. Please move the environment variable access inside a function that's only called after a request has been received.`
9
+ );
10
+ }
11
+ }
12
+ );
13
+ }
14
+ export {
15
+ getProcessEnvProxy,
16
+ isNode
17
+ };
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@astrojs/cloudflare",
3
+ "description": "Deploy your site to cloudflare workers or cloudflare pages",
4
+ "version": "0.0.0-cloudcannon-fix-20230306211609",
5
+ "type": "module",
6
+ "types": "./dist/index.d.ts",
7
+ "author": "withastro",
8
+ "license": "MIT",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/withastro/astro.git",
12
+ "directory": "packages/integrations/cloudflare"
13
+ },
14
+ "keywords": [
15
+ "withastro",
16
+ "astro-adapter"
17
+ ],
18
+ "bugs": "https://github.com/withastro/astro/issues",
19
+ "homepage": "https://docs.astro.build/en/guides/integrations-guide/cloudflare/",
20
+ "exports": {
21
+ ".": "./dist/index.js",
22
+ "./runtime": {
23
+ "types": "./runtime.d.ts",
24
+ "default": "./dist/runtime.js"
25
+ },
26
+ "./server.advanced.js": "./dist/server.advanced.js",
27
+ "./server.directory.js": "./dist/server.directory.js",
28
+ "./package.json": "./package.json"
29
+ },
30
+ "dependencies": {
31
+ "esbuild": "^0.15.18",
32
+ "tiny-glob": "^0.2.9"
33
+ },
34
+ "peerDependencies": {
35
+ "astro": "0.0.0-cloudcannon-fix-20230306211609"
36
+ },
37
+ "devDependencies": {
38
+ "astro": "0.0.0-cloudcannon-fix-20230306211609",
39
+ "astro-scripts": "0.0.0-cloudcannon-fix-20230306211609",
40
+ "chai": "^4.3.6",
41
+ "cheerio": "^1.0.0-rc.11",
42
+ "mocha": "^9.2.2",
43
+ "wrangler": "^2.0.23"
44
+ },
45
+ "scripts": {
46
+ "build": "astro-scripts build \"src/**/*.ts\" && tsc",
47
+ "build:ci": "astro-scripts build \"src/**/*.ts\"",
48
+ "dev": "astro-scripts dev \"src/**/*.ts\"",
49
+ "test": "mocha --exit --timeout 30000 test/"
50
+ }
51
+ }
package/runtime.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export type { WorkerRuntime, PagesRuntime } from './dist/runtime';
2
+
3
+ export { getRuntime } from './dist/runtime';
package/src/index.ts ADDED
@@ -0,0 +1,219 @@
1
+ import type { AstroAdapter, AstroConfig, AstroIntegration } from 'astro';
2
+ import esbuild from 'esbuild';
3
+ import * as fs from 'fs';
4
+ import * as os from 'os';
5
+ import glob from 'tiny-glob';
6
+ import { fileURLToPath, pathToFileURL } from 'url';
7
+
8
+ type Options = {
9
+ mode: 'directory' | 'advanced';
10
+ };
11
+
12
+ interface BuildConfig {
13
+ server: URL;
14
+ client: URL;
15
+ serverEntry: string;
16
+ }
17
+
18
+ export function getAdapter(isModeDirectory: boolean): AstroAdapter {
19
+ return isModeDirectory
20
+ ? {
21
+ name: '@astrojs/cloudflare',
22
+ serverEntrypoint: '@astrojs/cloudflare/server.directory.js',
23
+ exports: ['onRequest'],
24
+ }
25
+ : {
26
+ name: '@astrojs/cloudflare',
27
+ serverEntrypoint: '@astrojs/cloudflare/server.advanced.js',
28
+ exports: ['default'],
29
+ };
30
+ }
31
+
32
+ const SHIM = `globalThis.process = {
33
+ argv: [],
34
+ env: {},
35
+ };`;
36
+
37
+ const SERVER_BUILD_FOLDER = '/$server_build/';
38
+
39
+ export default function createIntegration(args?: Options): AstroIntegration {
40
+ let _config: AstroConfig;
41
+ let _buildConfig: BuildConfig;
42
+ const isModeDirectory = args?.mode === 'directory';
43
+
44
+ return {
45
+ name: '@astrojs/cloudflare',
46
+ hooks: {
47
+ 'astro:config:setup': ({ config, updateConfig }) => {
48
+ updateConfig({
49
+ build: {
50
+ client: new URL(`.${config.base}`, config.outDir),
51
+ server: new URL(`.${SERVER_BUILD_FOLDER}`, config.outDir),
52
+ serverEntry: '_worker.mjs',
53
+ },
54
+ });
55
+ },
56
+ 'astro:config:done': ({ setAdapter, config }) => {
57
+ setAdapter(getAdapter(isModeDirectory));
58
+ _config = config;
59
+ _buildConfig = config.build;
60
+
61
+ if (config.output === 'static') {
62
+ throw new Error(`
63
+ [@astrojs/cloudflare] \`output: "server"\` is required to use this adapter. Otherwise, this adapter is not necessary to deploy a static site to Cloudflare.
64
+
65
+ `);
66
+ }
67
+
68
+ if (config.base === SERVER_BUILD_FOLDER) {
69
+ throw new Error(`
70
+ [@astrojs/cloudflare] \`base: "${SERVER_BUILD_FOLDER}"\` is not allowed. Please change your \`base\` config to something else.`);
71
+ }
72
+ },
73
+ 'astro:build:setup': ({ vite, target }) => {
74
+ if (target === 'server') {
75
+ vite.resolve = vite.resolve || {};
76
+ vite.resolve.alias = vite.resolve.alias || {};
77
+
78
+ const aliases = [{ find: 'react-dom/server', replacement: 'react-dom/server.browser' }];
79
+
80
+ if (Array.isArray(vite.resolve.alias)) {
81
+ vite.resolve.alias = [...vite.resolve.alias, ...aliases];
82
+ } else {
83
+ for (const alias of aliases) {
84
+ (vite.resolve.alias as Record<string, string>)[alias.find] = alias.replacement;
85
+ }
86
+ }
87
+ vite.ssr = vite.ssr || {};
88
+ vite.ssr.target = vite.ssr.target || 'webworker';
89
+ }
90
+ },
91
+ 'astro:build:done': async ({ pages }) => {
92
+ const entryPath = fileURLToPath(new URL(_buildConfig.serverEntry, _buildConfig.server));
93
+ const entryUrl = new URL(_buildConfig.serverEntry, _config.outDir);
94
+ const buildPath = fileURLToPath(entryUrl);
95
+ // A URL for the final build path after renaming
96
+ const finalBuildUrl = pathToFileURL(buildPath.replace(/\.mjs$/, '.js'));
97
+
98
+ await esbuild.build({
99
+ target: 'es2020',
100
+ platform: 'browser',
101
+ entryPoints: [entryPath],
102
+ outfile: buildPath,
103
+ allowOverwrite: true,
104
+ format: 'esm',
105
+ bundle: true,
106
+ minify: true,
107
+ banner: {
108
+ js: SHIM,
109
+ },
110
+ });
111
+
112
+ // Rename to worker.js
113
+ await fs.promises.rename(buildPath, finalBuildUrl);
114
+
115
+ // throw the server folder in the bin
116
+ const serverUrl = new URL(_buildConfig.server);
117
+ await fs.promises.rm(serverUrl, { recursive: true, force: true });
118
+
119
+ // move cloudflare specific files to the root
120
+ const cloudflareSpecialFiles = ['_headers', '_redirects', '_routes.json'];
121
+ if (_config.base !== '/') {
122
+ for (const file of cloudflareSpecialFiles) {
123
+ try {
124
+ await fs.promises.rename(
125
+ new URL(file, _buildConfig.client),
126
+ new URL(file, _config.outDir)
127
+ );
128
+ } catch (e) {
129
+ // ignore
130
+ }
131
+ }
132
+ }
133
+
134
+ const routesExists = await fs.promises
135
+ .stat(new URL('./_routes.json', _config.outDir))
136
+ .then((stat) => stat.isFile())
137
+ .catch(() => false);
138
+
139
+ // this creates a _routes.json, in case there is none present to enable
140
+ // cloudflare to handle static files and support _redirects configuration
141
+ // (without calling the function)
142
+ if (!routesExists) {
143
+ const staticPathList: Array<string> = (
144
+ await glob(`${fileURLToPath(_buildConfig.client)}/**/*`, {
145
+ cwd: fileURLToPath(_config.outDir),
146
+ filesOnly: true,
147
+ })
148
+ )
149
+ .filter((file: string) => cloudflareSpecialFiles.indexOf(file) < 0)
150
+ .map((file: string) => `/${file}`);
151
+
152
+ for (let page of pages) {
153
+ staticPathList.push(prependForwardSlash(page.pathname));
154
+ }
155
+
156
+ const redirectsExists = await fs.promises
157
+ .stat(new URL('./_redirects', _config.outDir))
158
+ .then((stat) => stat.isFile())
159
+ .catch(() => false);
160
+
161
+ // convert all redirect source paths into a list of routes
162
+ // and add them to the static path
163
+ if (redirectsExists) {
164
+ const redirects = (
165
+ await fs.promises.readFile(new URL('./_redirects', _config.outDir), 'utf-8')
166
+ )
167
+ .split(os.EOL)
168
+ .map((line) => {
169
+ const parts = line.split(' ');
170
+ if (parts.length < 2) {
171
+ return null;
172
+ } else {
173
+ // convert /products/:id to /products/*
174
+ return (
175
+ parts[0]
176
+ .replace(/\/:.*?(?=\/|$)/g, '/*')
177
+ // remove query params as they are not supported by cloudflare
178
+ .replace(/\?.*$/, '')
179
+ );
180
+ }
181
+ })
182
+ .filter(
183
+ (line, index, arr) => line !== null && arr.indexOf(line) === index
184
+ ) as string[];
185
+
186
+ if (redirects.length > 0) {
187
+ staticPathList.push(...redirects);
188
+ }
189
+ }
190
+
191
+ await fs.promises.writeFile(
192
+ new URL('./_routes.json', _config.outDir),
193
+ JSON.stringify(
194
+ {
195
+ version: 1,
196
+ include: ['/*'],
197
+ exclude: staticPathList,
198
+ },
199
+ null,
200
+ 2
201
+ )
202
+ );
203
+ }
204
+
205
+ if (isModeDirectory) {
206
+ const functionsUrl = new URL('functions/', _config.root);
207
+ await fs.promises.mkdir(functionsUrl, { recursive: true });
208
+
209
+ const directoryUrl = new URL('[[path]].js', functionsUrl);
210
+ await fs.promises.rename(finalBuildUrl, directoryUrl);
211
+ }
212
+ },
213
+ },
214
+ };
215
+ }
216
+
217
+ function prependForwardSlash(path: string) {
218
+ return path[0] === '/' ? path : '/' + path;
219
+ }
package/src/runtime.ts ADDED
@@ -0,0 +1,28 @@
1
+ export type WorkerRuntime<T = unknown> = {
2
+ name: 'cloudflare';
3
+ env: T;
4
+ waitUntil(promise: Promise<any>): void;
5
+ passThroughOnException(): void;
6
+ };
7
+
8
+ export type PagesRuntime<T = unknown, U = unknown> = {
9
+ name: 'cloudflare';
10
+ env: T;
11
+ functionPath: string;
12
+ params: Record<string, string>;
13
+ data: U;
14
+ waitUntil(promise: Promise<any>): void;
15
+ next(request: Request): void;
16
+ };
17
+
18
+ export function getRuntime<T = unknown, U = unknown>(
19
+ request: Request
20
+ ): WorkerRuntime<T> | PagesRuntime<T, U> {
21
+ if (!!request) {
22
+ return Reflect.get(request, Symbol.for('runtime'));
23
+ } else {
24
+ throw new Error(
25
+ 'To retrieve the current cloudflare runtime you need to pass in the Astro request object'
26
+ );
27
+ }
28
+ }
@@ -0,0 +1,53 @@
1
+ import type { SSRManifest } from 'astro';
2
+ import { App } from 'astro/app';
3
+ import { getProcessEnvProxy, isNode } from './util.js';
4
+
5
+ if (!isNode) {
6
+ process.env = getProcessEnvProxy();
7
+ }
8
+
9
+ type Env = {
10
+ ASSETS: { fetch: (req: Request) => Promise<Response> };
11
+ name: string;
12
+ };
13
+
14
+ export function createExports(manifest: SSRManifest) {
15
+ const app = new App(manifest);
16
+
17
+ const fetch = async (request: Request, env: Env, context: any) => {
18
+ process.env = env as any;
19
+
20
+ const { pathname } = new URL(request.url);
21
+
22
+ // static assets fallback, in case default _routes.json is not used
23
+ if (manifest.assets.has(pathname)) {
24
+ return env.ASSETS.fetch(request);
25
+ }
26
+
27
+ let routeData = app.match(request, { matchNotFound: true });
28
+ if (routeData) {
29
+ Reflect.set(
30
+ request,
31
+ Symbol.for('astro.clientAddress'),
32
+ request.headers.get('cf-connecting-ip')
33
+ );
34
+ Reflect.set(request, Symbol.for('runtime'), { env, name: 'cloudflare', ...context });
35
+ let response = await app.render(request, routeData);
36
+
37
+ if (app.setCookieHeaders) {
38
+ for (const setCookieHeader of app.setCookieHeaders(response)) {
39
+ response.headers.append('Set-Cookie', setCookieHeader);
40
+ }
41
+ }
42
+
43
+ return response;
44
+ }
45
+
46
+ return new Response(null, {
47
+ status: 404,
48
+ statusText: 'Not found',
49
+ });
50
+ };
51
+
52
+ return { default: { fetch } };
53
+ }