@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.
- package/.turbo/turbo-build.log +5 -0
- package/CHANGELOG.md +379 -0
- package/LICENSE +61 -0
- package/README.md +119 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +156 -0
- package/dist/runtime.d.ts +16 -0
- package/dist/runtime.js +12 -0
- package/dist/server.advanced.d.ts +13 -0
- package/dist/server.advanced.js +39 -0
- package/dist/server.directory.d.ts +7 -0
- package/dist/server.directory.js +47 -0
- package/dist/util.d.ts +2 -0
- package/dist/util.js +17 -0
- package/package.json +51 -0
- package/runtime.d.ts +3 -0
- package/src/index.ts +219 -0
- package/src/runtime.ts +28 -0
- package/src/server.advanced.ts +53 -0
- package/src/server.directory.ts +58 -0
- package/src/util.ts +19 -0
- package/test/basics.test.js +32 -0
- package/test/directory.test.js +22 -0
- package/test/fixtures/basics/astro.config.mjs +10 -0
- package/test/fixtures/basics/node_modules/.bin/astro +17 -0
- package/test/fixtures/basics/package.json +9 -0
- package/test/fixtures/basics/src/pages/index.astro +9 -0
- package/test/fixtures/no-output/astro.config.mjs +6 -0
- package/test/fixtures/no-output/node_modules/.bin/astro +17 -0
- package/test/fixtures/no-output/package.json +9 -0
- package/test/fixtures/prerender/astro.config.mjs +7 -0
- package/test/fixtures/prerender/node_modules/.bin/astro +17 -0
- package/test/fixtures/prerender/package.json +9 -0
- package/test/fixtures/prerender/src/pages/index.astro +8 -0
- package/test/fixtures/prerender/src/pages/one.astro +11 -0
- package/test/no-output.test.js +24 -0
- package/test/prerender.test.js +19 -0
- package/test/test-utils.js +62 -0
- package/test/wrangler.toml +4 -0
- 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>;
|
package/dist/runtime.js
ADDED
|
@@ -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
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
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
|
+
}
|