@astrojs/cloudflare 13.0.0-beta.4 → 13.0.0-beta.6
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/dist/entrypoints/preview.js +1 -1
- package/dist/index.d.ts +0 -28
- package/dist/index.js +34 -29
- package/dist/prerender-types.d.ts +22 -0
- package/dist/prerender-types.js +0 -0
- package/dist/prerenderer.d.ts +14 -0
- package/dist/prerenderer.js +88 -0
- package/dist/utils/handler.js +18 -4
- package/dist/utils/prerender-constants.d.ts +7 -0
- package/dist/utils/prerender-constants.js +6 -0
- package/dist/utils/prerender.d.ts +34 -0
- package/dist/utils/prerender.js +43 -0
- package/dist/vite-plugin-config.d.ts +3 -3
- package/dist/vite-plugin-config.js +5 -2
- package/package.json +7 -8
- package/dist/utils/cloudflare-module-loader.d.ts +0 -16
- package/dist/utils/cloudflare-module-loader.js +0 -152
|
@@ -83,7 +83,7 @@ function serverStart({
|
|
|
83
83
|
host,
|
|
84
84
|
base
|
|
85
85
|
}) {
|
|
86
|
-
const version = "13.0.0-beta.
|
|
86
|
+
const version = "13.0.0-beta.6";
|
|
87
87
|
const localPrefix = `${colors.dim("\u2503")} Local `;
|
|
88
88
|
const networkPrefix = `${colors.dim("\u2503")} Network `;
|
|
89
89
|
const emptyPrefix = " ".repeat(11);
|
package/dist/index.d.ts
CHANGED
|
@@ -28,14 +28,6 @@ export type Options = {
|
|
|
28
28
|
}[];
|
|
29
29
|
};
|
|
30
30
|
};
|
|
31
|
-
/**
|
|
32
|
-
* Allow bundling cloudflare worker specific file types as importable modules. Defaults to true.
|
|
33
|
-
* When enabled, allows imports of '.wasm', '.bin', and '.txt' file types
|
|
34
|
-
*
|
|
35
|
-
* See https://developers.cloudflare.com/pages/functions/module-support/
|
|
36
|
-
* for reference on how these file types are exported
|
|
37
|
-
*/
|
|
38
|
-
cloudflareModules?: boolean;
|
|
39
31
|
/**
|
|
40
32
|
* By default, Astro will be configured to use Cloudflare KV to store session data. The KV namespace
|
|
41
33
|
* will be automatically provisioned when you deploy.
|
|
@@ -56,25 +48,5 @@ export type Options = {
|
|
|
56
48
|
* See https://developers.cloudflare.com/images/transform-images/bindings/ for more details.
|
|
57
49
|
*/
|
|
58
50
|
imagesBindingName?: string;
|
|
59
|
-
/**
|
|
60
|
-
* This configuration option allows you to specify a custom entryPoint for your Cloudflare Worker.
|
|
61
|
-
* The entry point is the file that will be executed when your Worker is invoked.
|
|
62
|
-
* By default, this is set to `@astrojs/cloudflare/entrypoints/server.js` and `['default']`.
|
|
63
|
-
* @docs https://docs.astro.build/en/guides/integrations-guide/cloudflare/#workerEntryPoint
|
|
64
|
-
*/
|
|
65
|
-
workerEntryPoint?: {
|
|
66
|
-
/**
|
|
67
|
-
* The path to the entry file. This should be a relative path from the root of your Astro project.
|
|
68
|
-
* @example`'src/worker.ts'`
|
|
69
|
-
* @docs https://docs.astro.build/en/guides/integrations-guide/cloudflare/#workerentrypointpath
|
|
70
|
-
*/
|
|
71
|
-
path: string | URL;
|
|
72
|
-
/**
|
|
73
|
-
* Additional named exports to use for the entry file. Astro always includes the default export (`['default']`). If you need to have other top level named exports use this option.
|
|
74
|
-
* @example ['MyDurableObject', 'namedExport']
|
|
75
|
-
* @docs https://docs.astro.build/en/guides/integrations-guide/cloudflare/#workerentrypointnamedexports
|
|
76
|
-
*/
|
|
77
|
-
namedExports?: string[];
|
|
78
|
-
};
|
|
79
51
|
};
|
|
80
52
|
export default function createIntegration(args?: Options): AstroIntegration;
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import { createReadStream, existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { appendFile, stat } from "node:fs/promises";
|
|
3
|
-
import { createRequire } from "node:module";
|
|
4
3
|
import { createInterface } from "node:readline/promises";
|
|
5
|
-
import { pathToFileURL } from "node:url";
|
|
6
4
|
import { removeLeadingForwardSlash } from "@astrojs/internal-helpers/path";
|
|
7
5
|
import { createRedirectsFromAstroRoutes, printAsRedirects } from "@astrojs/underscore-redirects";
|
|
8
6
|
import { cloudflare as cfVitePlugin } from "@cloudflare/vite-plugin";
|
|
9
|
-
import { cloudflareModuleLoader } from "./utils/cloudflare-module-loader.js";
|
|
10
7
|
import { createRoutesFile, getParts } from "./utils/generate-routes-json.js";
|
|
11
8
|
import { setImageConfig } from "./utils/image-config.js";
|
|
12
9
|
import { createConfigPlugin } from "./vite-plugin-config.js";
|
|
@@ -15,14 +12,12 @@ import {
|
|
|
15
12
|
DEFAULT_SESSION_KV_BINDING_NAME,
|
|
16
13
|
DEFAULT_IMAGES_BINDING_NAME
|
|
17
14
|
} from "./wrangler.js";
|
|
18
|
-
import {
|
|
15
|
+
import { parseEnv } from "node:util";
|
|
19
16
|
import { sessionDrivers } from "astro/config";
|
|
17
|
+
import { createCloudflarePrerenderer } from "./prerenderer.js";
|
|
20
18
|
function createIntegration(args) {
|
|
21
19
|
let _config;
|
|
22
20
|
let finalBuildOutput;
|
|
23
|
-
const cloudflareModulePlugin = cloudflareModuleLoader(
|
|
24
|
-
args?.cloudflareModules ?? true
|
|
25
|
-
);
|
|
26
21
|
let _routes;
|
|
27
22
|
const sessionKVBindingName = args?.sessionKVBindingName ?? DEFAULT_SESSION_KV_BINDING_NAME;
|
|
28
23
|
const imagesBindingName = args?.imagesBindingName ?? DEFAULT_IMAGES_BINDING_NAME;
|
|
@@ -53,7 +48,18 @@ function createIntegration(args) {
|
|
|
53
48
|
config: cloudflareConfigCustomizer({
|
|
54
49
|
sessionKVBindingName: args?.sessionKVBindingName,
|
|
55
50
|
imagesBindingName: args?.imageService === "cloudflare-binding" ? args?.imagesBindingName : false
|
|
56
|
-
})
|
|
51
|
+
}),
|
|
52
|
+
experimental: {
|
|
53
|
+
prerenderWorker: {
|
|
54
|
+
config(_, { entryWorkerConfig }) {
|
|
55
|
+
return {
|
|
56
|
+
...entryWorkerConfig,
|
|
57
|
+
// This is the Vite environment name used for prerendering
|
|
58
|
+
name: "prerender"
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
57
63
|
};
|
|
58
64
|
updateConfig({
|
|
59
65
|
build: {
|
|
@@ -66,9 +72,6 @@ function createIntegration(args) {
|
|
|
66
72
|
vite: {
|
|
67
73
|
plugins: [
|
|
68
74
|
cfVitePlugin(cfPluginConfig),
|
|
69
|
-
// https://developers.cloudflare.com/pages/functions/module-support/
|
|
70
|
-
// Allows imports of '.wasm', '.bin', and '.txt' file types
|
|
71
|
-
cloudflareModulePlugin,
|
|
72
75
|
{
|
|
73
76
|
name: "@astrojs/cloudflare:cf-imports",
|
|
74
77
|
enforce: "pre",
|
|
@@ -84,8 +87,10 @@ function createIntegration(args) {
|
|
|
84
87
|
{
|
|
85
88
|
name: "@astrojs/cloudflare:environment",
|
|
86
89
|
configEnvironment(environmentName, _options) {
|
|
87
|
-
const isServerEnvironment = ["ssr", "prerender"].includes(
|
|
88
|
-
|
|
90
|
+
const isServerEnvironment = ["astro", "ssr", "prerender"].includes(
|
|
91
|
+
environmentName
|
|
92
|
+
);
|
|
93
|
+
if (isServerEnvironment && !_options.optimizeDeps?.noDiscovery) {
|
|
89
94
|
return {
|
|
90
95
|
optimizeDeps: {
|
|
91
96
|
include: [
|
|
@@ -162,19 +167,8 @@ function createIntegration(args) {
|
|
|
162
167
|
filename: "cloudflare.d.ts",
|
|
163
168
|
content: '/// <reference types="@astrojs/cloudflare/types.d.ts" />'
|
|
164
169
|
});
|
|
165
|
-
let customWorkerEntryPoint;
|
|
166
|
-
if (args?.workerEntryPoint && typeof args.workerEntryPoint.path === "string") {
|
|
167
|
-
const require2 = createRequire(config.root);
|
|
168
|
-
try {
|
|
169
|
-
customWorkerEntryPoint = pathToFileURL(require2.resolve(args.workerEntryPoint.path));
|
|
170
|
-
} catch {
|
|
171
|
-
customWorkerEntryPoint = new URL(args.workerEntryPoint.path, config.root);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
170
|
setAdapter({
|
|
175
171
|
name: "@astrojs/cloudflare",
|
|
176
|
-
serverEntrypoint: customWorkerEntryPoint ?? "@astrojs/cloudflare/entrypoints/server.js",
|
|
177
|
-
exports: [.../* @__PURE__ */ new Set(["default", ...args?.workerEntryPoint?.namedExports ?? []])],
|
|
178
172
|
adapterFeatures: {
|
|
179
173
|
edgeMiddleware: false,
|
|
180
174
|
buildOutput: "server"
|
|
@@ -188,10 +182,9 @@ function createIntegration(args) {
|
|
|
188
182
|
i18nDomains: "experimental",
|
|
189
183
|
sharpImageService: {
|
|
190
184
|
support: "limited",
|
|
191
|
-
message:
|
|
192
|
-
//
|
|
193
|
-
|
|
194
|
-
suppress: args?.imageService ? "all" : "default"
|
|
185
|
+
message: "When using a custom image service, ensure it is compatible with the Cloudflare Workers runtime.",
|
|
186
|
+
// Only 'custom' could potentially use sharp at runtime.
|
|
187
|
+
suppress: args?.imageService === "custom" ? "default" : "all"
|
|
195
188
|
},
|
|
196
189
|
envGetSecret: "stable"
|
|
197
190
|
}
|
|
@@ -200,7 +193,7 @@ function createIntegration(args) {
|
|
|
200
193
|
if (existsSync(devVarsPath)) {
|
|
201
194
|
try {
|
|
202
195
|
const data = readFileSync(devVarsPath, "utf-8");
|
|
203
|
-
const parsed =
|
|
196
|
+
const parsed = parseEnv(data);
|
|
204
197
|
Object.assign(process.env, parsed);
|
|
205
198
|
} catch {
|
|
206
199
|
logger.error(
|
|
@@ -209,6 +202,17 @@ function createIntegration(args) {
|
|
|
209
202
|
}
|
|
210
203
|
}
|
|
211
204
|
},
|
|
205
|
+
"astro:build:start": ({ setPrerenderer }) => {
|
|
206
|
+
setPrerenderer(
|
|
207
|
+
createCloudflarePrerenderer({
|
|
208
|
+
root: _config.root,
|
|
209
|
+
serverDir: _config.build.server,
|
|
210
|
+
clientDir: _config.build.client,
|
|
211
|
+
base: _config.base,
|
|
212
|
+
trailingSlash: _config.trailingSlash
|
|
213
|
+
})
|
|
214
|
+
);
|
|
215
|
+
},
|
|
212
216
|
"astro:build:setup": ({ vite, target }) => {
|
|
213
217
|
if (target === "server") {
|
|
214
218
|
vite.resolve ||= {};
|
|
@@ -297,6 +301,7 @@ function createIntegration(args) {
|
|
|
297
301
|
logger.error("Failed to write _redirects file");
|
|
298
302
|
}
|
|
299
303
|
}
|
|
304
|
+
delete process.env.CLOUDFLARE_VITE_BUILD;
|
|
300
305
|
}
|
|
301
306
|
}
|
|
302
307
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { SerializedRouteData } from 'astro/app/manifest';
|
|
2
|
+
/**
|
|
3
|
+
* A pathname with its serialized route data, used for prerendering over HTTP.
|
|
4
|
+
*/
|
|
5
|
+
interface SerializedPathWithRoute {
|
|
6
|
+
pathname: string;
|
|
7
|
+
route: SerializedRouteData;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Response from the /__astro_static_paths endpoint.
|
|
11
|
+
*/
|
|
12
|
+
export interface StaticPathsResponse {
|
|
13
|
+
paths: SerializedPathWithRoute[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Request body for the /__astro_prerender endpoint.
|
|
17
|
+
*/
|
|
18
|
+
export interface PrerenderRequest {
|
|
19
|
+
url: string;
|
|
20
|
+
routeData: SerializedRouteData;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AstroConfig, AstroPrerenderer } from 'astro';
|
|
2
|
+
interface CloudflarePrerendererOptions {
|
|
3
|
+
root: AstroConfig['root'];
|
|
4
|
+
serverDir: AstroConfig['build']['server'];
|
|
5
|
+
clientDir: AstroConfig['build']['client'];
|
|
6
|
+
base: AstroConfig['base'];
|
|
7
|
+
trailingSlash: AstroConfig['trailingSlash'];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Creates a prerenderer that uses Cloudflare's workerd runtime via a preview server.
|
|
11
|
+
* This allows prerendering to happen in the same runtime that will serve the pages.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createCloudflarePrerenderer({ root, serverDir, clientDir, base, trailingSlash, }: CloudflarePrerendererOptions): AstroPrerenderer;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { preview } from "vite";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { mkdir } from "node:fs/promises";
|
|
4
|
+
import { cloudflare as cfVitePlugin } from "@cloudflare/vite-plugin";
|
|
5
|
+
import { cloudflareConfigCustomizer } from "./wrangler.js";
|
|
6
|
+
import { serializeRouteData, deserializeRouteData } from "astro/app/manifest";
|
|
7
|
+
import { STATIC_PATHS_ENDPOINT, PRERENDER_ENDPOINT } from "./utils/prerender-constants.js";
|
|
8
|
+
function createCloudflarePrerenderer({
|
|
9
|
+
root,
|
|
10
|
+
serverDir,
|
|
11
|
+
clientDir,
|
|
12
|
+
base,
|
|
13
|
+
trailingSlash
|
|
14
|
+
}) {
|
|
15
|
+
let previewServer;
|
|
16
|
+
let serverUrl;
|
|
17
|
+
return {
|
|
18
|
+
name: "@astrojs/cloudflare:prerenderer",
|
|
19
|
+
async setup() {
|
|
20
|
+
await mkdir(clientDir, { recursive: true });
|
|
21
|
+
const cfPluginConfig = {
|
|
22
|
+
viteEnvironment: { name: "prerender" },
|
|
23
|
+
config: cloudflareConfigCustomizer()
|
|
24
|
+
};
|
|
25
|
+
previewServer = await preview({
|
|
26
|
+
configFile: false,
|
|
27
|
+
base,
|
|
28
|
+
appType: "mpa",
|
|
29
|
+
build: {
|
|
30
|
+
outDir: fileURLToPath(serverDir)
|
|
31
|
+
},
|
|
32
|
+
root: fileURLToPath(root),
|
|
33
|
+
preview: {
|
|
34
|
+
host: "localhost",
|
|
35
|
+
port: 0,
|
|
36
|
+
// Let the OS pick a free port
|
|
37
|
+
open: false
|
|
38
|
+
},
|
|
39
|
+
plugins: [cfVitePlugin(cfPluginConfig)]
|
|
40
|
+
});
|
|
41
|
+
const address = previewServer.httpServer.address();
|
|
42
|
+
if (address && typeof address === "object") {
|
|
43
|
+
serverUrl = `http://localhost:${address.port}`;
|
|
44
|
+
} else {
|
|
45
|
+
throw new Error(
|
|
46
|
+
"Failed to start the Cloudflare prerender server. The preview server did not return a valid address. This is likely a bug in @astrojs/cloudflare. Please file an issue at https://github.com/withastro/astro/issues"
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
async getStaticPaths() {
|
|
51
|
+
const response = await fetch(`${serverUrl}${STATIC_PATHS_ENDPOINT}`, {
|
|
52
|
+
method: "POST",
|
|
53
|
+
headers: { "Content-Type": "application/json" }
|
|
54
|
+
});
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Failed to get static paths from the Cloudflare prerender server (${response.status}: ${response.statusText}). This is likely a bug in @astrojs/cloudflare. Please file an issue at https://github.com/withastro/astro/issues`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
const data = await response.json();
|
|
61
|
+
return data.paths.map(({ pathname, route }) => ({
|
|
62
|
+
pathname,
|
|
63
|
+
route: deserializeRouteData(route)
|
|
64
|
+
}));
|
|
65
|
+
},
|
|
66
|
+
async render(request, { routeData }) {
|
|
67
|
+
const body = {
|
|
68
|
+
url: request.url,
|
|
69
|
+
routeData: serializeRouteData(routeData, trailingSlash)
|
|
70
|
+
};
|
|
71
|
+
const response = await fetch(`${serverUrl}${PRERENDER_ENDPOINT}`, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
headers: { "Content-Type": "application/json" },
|
|
74
|
+
body: JSON.stringify(body)
|
|
75
|
+
});
|
|
76
|
+
return response;
|
|
77
|
+
},
|
|
78
|
+
async teardown() {
|
|
79
|
+
if (previewServer) {
|
|
80
|
+
await previewServer.close();
|
|
81
|
+
previewServer = void 0;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
createCloudflarePrerenderer
|
|
88
|
+
};
|
package/dist/utils/handler.js
CHANGED
|
@@ -1,19 +1,33 @@
|
|
|
1
1
|
import { env as globalEnv } from "cloudflare:workers";
|
|
2
|
-
import { sessionKVBindingName } from "virtual:astro-cloudflare:config";
|
|
2
|
+
import { sessionKVBindingName, isPrerender } from "virtual:astro-cloudflare:config";
|
|
3
3
|
import { createApp } from "astro/app/entrypoint";
|
|
4
4
|
import { setGetEnv } from "astro/env/setup";
|
|
5
5
|
import { createGetEnv } from "../utils/env.js";
|
|
6
|
+
import {
|
|
7
|
+
isStaticPathsRequest,
|
|
8
|
+
isPrerenderRequest,
|
|
9
|
+
handleStaticPathsRequest,
|
|
10
|
+
handlePrerenderRequest
|
|
11
|
+
} from "./prerender.js";
|
|
6
12
|
setGetEnv(createGetEnv(globalEnv));
|
|
13
|
+
const app = createApp();
|
|
7
14
|
async function handle(request, env, context) {
|
|
8
|
-
|
|
9
|
-
|
|
15
|
+
if (isPrerender) {
|
|
16
|
+
if (isStaticPathsRequest(request)) {
|
|
17
|
+
return handleStaticPathsRequest(app);
|
|
18
|
+
}
|
|
19
|
+
if (isPrerenderRequest(request)) {
|
|
20
|
+
return handlePrerenderRequest(app, request);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const { pathname: requestPathname } = new URL(request.url);
|
|
10
24
|
if (env[sessionKVBindingName]) {
|
|
11
25
|
const sessionConfigOptions = app.manifest.sessionConfig?.options ?? {};
|
|
12
26
|
Object.assign(sessionConfigOptions, {
|
|
13
27
|
binding: env[sessionKVBindingName]
|
|
14
28
|
});
|
|
15
29
|
}
|
|
16
|
-
if (app.manifest.assets.has(
|
|
30
|
+
if (app.manifest.assets.has(requestPathname)) {
|
|
17
31
|
return env.ASSETS.fetch(request.url.replace(/\.html$/, ""));
|
|
18
32
|
}
|
|
19
33
|
let routeData = void 0;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants for prerender endpoints used by Cloudflare adapter
|
|
3
|
+
*/
|
|
4
|
+
/** Internal endpoint for fetching all static paths during prerendering */
|
|
5
|
+
export declare const STATIC_PATHS_ENDPOINT = "/__astro_static_paths";
|
|
6
|
+
/** Internal endpoint for rendering a specific page during prerendering */
|
|
7
|
+
export declare const PRERENDER_ENDPOINT = "/__astro_prerender";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prerender utilities for Cloudflare adapter
|
|
3
|
+
*
|
|
4
|
+
* During the build process, Astro prerenders pages by making requests to internal endpoints
|
|
5
|
+
* served by the Cloudflare worker running in workerd. These endpoints are:
|
|
6
|
+
*
|
|
7
|
+
* - `/__astro_static_paths`: Returns all static paths that need to be prerendered.
|
|
8
|
+
* The prerenderer calls this to discover which routes/pages need to be generated.
|
|
9
|
+
*
|
|
10
|
+
* - `/__astro_prerender`: Renders a specific page given its URL and route data.
|
|
11
|
+
* The prerenderer calls this for each path to generate the static HTML.
|
|
12
|
+
*
|
|
13
|
+
* These endpoints are only active during the prerender build phase and are not
|
|
14
|
+
* available in production or development.
|
|
15
|
+
*/
|
|
16
|
+
import type { BaseApp } from 'astro/app';
|
|
17
|
+
/**
|
|
18
|
+
* Checks if the request is for the static paths prerender endpoint.
|
|
19
|
+
* This endpoint returns all paths that need to be prerendered.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isStaticPathsRequest(request: Request): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Checks if the request is for the prerender endpoint.
|
|
24
|
+
* This endpoint renders a specific page during the prerender phase.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isPrerenderRequest(request: Request): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Handles the static paths request, returning all paths that need prerendering.
|
|
29
|
+
*/
|
|
30
|
+
export declare function handleStaticPathsRequest(app: BaseApp): Promise<Response>;
|
|
31
|
+
/**
|
|
32
|
+
* Handles a prerender request, rendering the specified page.
|
|
33
|
+
*/
|
|
34
|
+
export declare function handlePrerenderRequest(app: BaseApp, request: Request): Promise<Response>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { serializeRouteData, deserializeRouteData } from "astro/app/manifest";
|
|
2
|
+
import { StaticPaths } from "astro:static-paths";
|
|
3
|
+
import { STATIC_PATHS_ENDPOINT, PRERENDER_ENDPOINT } from "./prerender-constants.js";
|
|
4
|
+
function isStaticPathsRequest(request) {
|
|
5
|
+
const { pathname } = new URL(request.url);
|
|
6
|
+
return pathname === STATIC_PATHS_ENDPOINT && request.method === "POST";
|
|
7
|
+
}
|
|
8
|
+
function isPrerenderRequest(request) {
|
|
9
|
+
const { pathname } = new URL(request.url);
|
|
10
|
+
return pathname === PRERENDER_ENDPOINT && request.method === "POST";
|
|
11
|
+
}
|
|
12
|
+
async function handleStaticPathsRequest(app) {
|
|
13
|
+
const staticPaths = new StaticPaths(app);
|
|
14
|
+
const paths = await staticPaths.getAll();
|
|
15
|
+
const response = {
|
|
16
|
+
paths: paths.map(({ pathname, route }) => ({
|
|
17
|
+
pathname,
|
|
18
|
+
route: serializeRouteData(route, app.manifest.trailingSlash)
|
|
19
|
+
}))
|
|
20
|
+
};
|
|
21
|
+
return new Response(JSON.stringify(response), {
|
|
22
|
+
headers: { "Content-Type": "application/json" }
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
async function handlePrerenderRequest(app, request) {
|
|
26
|
+
const headers = new Headers();
|
|
27
|
+
for (const [key, value] of request.headers.entries()) {
|
|
28
|
+
headers.append(key, value);
|
|
29
|
+
}
|
|
30
|
+
const body = await request.json();
|
|
31
|
+
const routeData = deserializeRouteData(body.routeData);
|
|
32
|
+
const prerenderRequest = new Request(body.url, {
|
|
33
|
+
method: "GET",
|
|
34
|
+
headers
|
|
35
|
+
});
|
|
36
|
+
return app.render(prerenderRequest, { routeData });
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
handlePrerenderRequest,
|
|
40
|
+
handleStaticPathsRequest,
|
|
41
|
+
isPrerenderRequest,
|
|
42
|
+
isStaticPathsRequest
|
|
43
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PluginOption } from 'vite';
|
|
2
|
-
interface
|
|
2
|
+
export interface Config {
|
|
3
3
|
sessionKVBindingName: string;
|
|
4
|
+
isPrerender: boolean;
|
|
4
5
|
}
|
|
5
|
-
export declare function createConfigPlugin(config:
|
|
6
|
-
export {};
|
|
6
|
+
export declare function createConfigPlugin(config: Omit<Config, 'isPrerender'>): PluginOption;
|
|
@@ -2,7 +2,7 @@ const VIRTUAL_CONFIG_ID = "virtual:astro-cloudflare:config";
|
|
|
2
2
|
const RESOLVED_VIRTUAL_CONFIG_ID = "\0" + VIRTUAL_CONFIG_ID;
|
|
3
3
|
function createConfigPlugin(config) {
|
|
4
4
|
return {
|
|
5
|
-
name:
|
|
5
|
+
name: VIRTUAL_CONFIG_ID,
|
|
6
6
|
resolveId: {
|
|
7
7
|
filter: {
|
|
8
8
|
id: new RegExp(`^${VIRTUAL_CONFIG_ID}$`)
|
|
@@ -16,7 +16,10 @@ function createConfigPlugin(config) {
|
|
|
16
16
|
id: new RegExp(`^${RESOLVED_VIRTUAL_CONFIG_ID}$`)
|
|
17
17
|
},
|
|
18
18
|
handler() {
|
|
19
|
-
return
|
|
19
|
+
return [
|
|
20
|
+
...Object.entries(config).map(([k, v]) => `export const ${k} = ${JSON.stringify(v)};`),
|
|
21
|
+
`export const isPrerender = ${this.environment?.name === "prerender"};`
|
|
22
|
+
].join("\n");
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
25
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrojs/cloudflare",
|
|
3
3
|
"description": "Deploy your site to Cloudflare Workers/Pages",
|
|
4
|
-
"version": "13.0.0-beta.
|
|
4
|
+
"version": "13.0.0-beta.6",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"author": "withastro",
|
|
@@ -35,12 +35,11 @@
|
|
|
35
35
|
"types.d.ts"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@cloudflare/vite-plugin": "^1.
|
|
39
|
-
"dotenv": "^17.2.3",
|
|
38
|
+
"@cloudflare/vite-plugin": "^1.23.0",
|
|
40
39
|
"piccolore": "^0.1.3",
|
|
41
40
|
"tinyglobby": "^0.2.15",
|
|
42
41
|
"vite": "^7.3.1",
|
|
43
|
-
"@astrojs/internal-helpers": "0.8.0-beta.
|
|
42
|
+
"@astrojs/internal-helpers": "0.8.0-beta.1",
|
|
44
43
|
"@astrojs/underscore-redirects": "1.0.0"
|
|
45
44
|
},
|
|
46
45
|
"peerDependencies": {
|
|
@@ -48,11 +47,11 @@
|
|
|
48
47
|
"wrangler": "^4.61.1"
|
|
49
48
|
},
|
|
50
49
|
"devDependencies": {
|
|
51
|
-
"@cloudflare/workers-types": "^4.
|
|
52
|
-
"
|
|
50
|
+
"@cloudflare/workers-types": "^4.20260131.0",
|
|
51
|
+
"@types/node": "^25.2.2",
|
|
52
|
+
"cheerio": "1.2.0",
|
|
53
53
|
"devalue": "^5.6.2",
|
|
54
|
-
"
|
|
55
|
-
"astro": "6.0.0-beta.7",
|
|
54
|
+
"astro": "6.0.0-beta.10",
|
|
56
55
|
"astro-scripts": "0.0.14"
|
|
57
56
|
},
|
|
58
57
|
"publishConfig": {
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { PluginOption } from 'vite';
|
|
2
|
-
/**
|
|
3
|
-
* Enables support for various non-standard extensions in module imports that cloudflare workers supports.
|
|
4
|
-
*
|
|
5
|
-
* See https://developers.cloudflare.com/pages/functions/module-support/ for reference
|
|
6
|
-
*
|
|
7
|
-
* This adds supports for imports in the following formats:
|
|
8
|
-
* - .wasm
|
|
9
|
-
* - .wasm?module
|
|
10
|
-
* - .bin
|
|
11
|
-
* - .txt
|
|
12
|
-
*
|
|
13
|
-
* @param enabled - if true, will load all cloudflare pages supported types
|
|
14
|
-
* @returns Vite plugin with additional extension method to hook into astro build
|
|
15
|
-
*/
|
|
16
|
-
export declare function cloudflareModuleLoader(enabled: boolean): PluginOption;
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs/promises";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
function cloudflareModuleLoader(enabled) {
|
|
4
|
-
const adaptersByExtension = enabled ? { ...defaultAdapters } : {};
|
|
5
|
-
const extensions = Object.keys(adaptersByExtension);
|
|
6
|
-
let isDev = false;
|
|
7
|
-
const MAGIC_STRING = "__CLOUDFLARE_ASSET__";
|
|
8
|
-
const replacements = [];
|
|
9
|
-
return {
|
|
10
|
-
name: "vite:cf-module-loader",
|
|
11
|
-
enforce: "pre",
|
|
12
|
-
applyToEnvironment(environment) {
|
|
13
|
-
return environment.name === "ssr" || environment.name === "client";
|
|
14
|
-
},
|
|
15
|
-
configResolved(config) {
|
|
16
|
-
isDev = config.command === "serve";
|
|
17
|
-
},
|
|
18
|
-
config(_, __) {
|
|
19
|
-
return {
|
|
20
|
-
assetsInclude: extensions.map((x) => `**/*${x}`),
|
|
21
|
-
build: {
|
|
22
|
-
rollupOptions: {
|
|
23
|
-
// mark the wasm files as external so that they are not bundled and instead are loaded from the files
|
|
24
|
-
external: extensions.map(
|
|
25
|
-
(x) => new RegExp(`^${MAGIC_STRING}.+${escapeRegExp(x)}$`, "i")
|
|
26
|
-
)
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
},
|
|
31
|
-
load: {
|
|
32
|
-
filter: {
|
|
33
|
-
// Escape the dot in the regex
|
|
34
|
-
id: new RegExp(`.(${extensions.map((ext) => ext.slice(1)).join("|")})$`)
|
|
35
|
-
},
|
|
36
|
-
async handler(id, _) {
|
|
37
|
-
const maybeExtension = extensions.find((x) => id.endsWith(x));
|
|
38
|
-
const moduleType = adaptersByExtension[maybeExtension];
|
|
39
|
-
if (!enabled) {
|
|
40
|
-
throw new Error(
|
|
41
|
-
`Cloudflare module loading is experimental. The ${maybeExtension} module cannot be loaded unless you add \`cloudflareModules: true\` to your astro config.`
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
const moduleLoader = renderers[moduleType];
|
|
45
|
-
const filePath = id.replace(/\?\w+$/, "");
|
|
46
|
-
const extension = maybeExtension.replace(/\?\w+$/, "");
|
|
47
|
-
const data = await fs.readFile(filePath);
|
|
48
|
-
const base64 = data.toString("base64");
|
|
49
|
-
const inlineModule = moduleLoader(data);
|
|
50
|
-
if (isDev) {
|
|
51
|
-
return inlineModule;
|
|
52
|
-
}
|
|
53
|
-
const hash = hashString(base64);
|
|
54
|
-
const assetName = `${path.basename(filePath).split(".")[0]}.${hash}${extension}`;
|
|
55
|
-
this.emitFile({
|
|
56
|
-
type: "asset",
|
|
57
|
-
// emit the data explicitly as an asset with `fileName` rather than `name` so that
|
|
58
|
-
// vite doesn't give it a random hash-id in its name--We need to be able to easily rewrite from
|
|
59
|
-
// the loader and the actual asset later in the build for the worker
|
|
60
|
-
fileName: assetName,
|
|
61
|
-
source: data
|
|
62
|
-
});
|
|
63
|
-
const chunkId = this.emitFile({
|
|
64
|
-
type: "prebuilt-chunk",
|
|
65
|
-
fileName: assetName,
|
|
66
|
-
code: inlineModule
|
|
67
|
-
});
|
|
68
|
-
return `import module from "${MAGIC_STRING}${chunkId}${extension}";export default module;`;
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
// output original wasm file relative to the chunk now that chunking has been achieved
|
|
72
|
-
renderChunk(code, chunk, _) {
|
|
73
|
-
if (isDev) return;
|
|
74
|
-
if (!code.includes(MAGIC_STRING)) return;
|
|
75
|
-
let replaced = code;
|
|
76
|
-
for (const ext of extensions) {
|
|
77
|
-
const extension = ext.replace(/\?\w+$/, "");
|
|
78
|
-
replaced = replaced.replaceAll(
|
|
79
|
-
new RegExp(`${MAGIC_STRING}([^\\s]+?)${escapeRegExp(extension)}`, "g"),
|
|
80
|
-
(_s, assetId) => {
|
|
81
|
-
const fileName = this.getFileName(assetId);
|
|
82
|
-
const relativePath = path.relative(path.dirname(chunk.fileName), fileName).replaceAll("\\", "/");
|
|
83
|
-
replacements.push({
|
|
84
|
-
chunkName: chunk.name,
|
|
85
|
-
cloudflareImport: relativePath,
|
|
86
|
-
nodejsImport: relativePath
|
|
87
|
-
});
|
|
88
|
-
return `./${relativePath}`;
|
|
89
|
-
}
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
return { code: replaced };
|
|
93
|
-
},
|
|
94
|
-
generateBundle(_, bundle) {
|
|
95
|
-
const replacementsByChunkName = /* @__PURE__ */ new Map();
|
|
96
|
-
for (const replacement of replacements) {
|
|
97
|
-
const repls = replacementsByChunkName.get(replacement.chunkName) || [];
|
|
98
|
-
if (!repls.length) {
|
|
99
|
-
replacementsByChunkName.set(replacement.chunkName, repls);
|
|
100
|
-
}
|
|
101
|
-
repls.push(replacement);
|
|
102
|
-
}
|
|
103
|
-
for (const chunk of Object.values(bundle)) {
|
|
104
|
-
const repls = chunk.name && replacementsByChunkName.get(chunk.name);
|
|
105
|
-
for (const replacement of repls || []) {
|
|
106
|
-
if (!replacement.fileName) {
|
|
107
|
-
replacement.fileName = [];
|
|
108
|
-
}
|
|
109
|
-
replacement.fileName.push(chunk.fileName);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
const renderers = {
|
|
116
|
-
CompiledWasm(fileContents) {
|
|
117
|
-
const base64 = fileContents.toString("base64");
|
|
118
|
-
return `const wasmModule = new WebAssembly.Module(Uint8Array.from(atob("${base64}"), c => c.charCodeAt(0)));export default wasmModule;`;
|
|
119
|
-
},
|
|
120
|
-
Data(fileContents) {
|
|
121
|
-
const base64 = fileContents.toString("base64");
|
|
122
|
-
return `const binModule = Uint8Array.from(atob("${base64}"), c => c.charCodeAt(0)).buffer;export default binModule;`;
|
|
123
|
-
},
|
|
124
|
-
Text(fileContents) {
|
|
125
|
-
const escaped = JSON.stringify(fileContents.toString("utf-8"));
|
|
126
|
-
return `const stringModule = ${escaped};export default stringModule;`;
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
const defaultAdapters = {
|
|
130
|
-
// Loads '*.wasm?module' imports as WebAssembly modules, which is the only way to load WASM in cloudflare workers.
|
|
131
|
-
// Current proposal for WASM modules: https://github.com/WebAssembly/esm-integration/tree/main/proposals/esm-integration
|
|
132
|
-
".wasm?module": "CompiledWasm",
|
|
133
|
-
// treats the module as a WASM module
|
|
134
|
-
".wasm": "CompiledWasm",
|
|
135
|
-
".bin": "Data",
|
|
136
|
-
".txt": "Text"
|
|
137
|
-
};
|
|
138
|
-
function hashString(str) {
|
|
139
|
-
let hash = 0;
|
|
140
|
-
for (let i = 0; i < str.length; i++) {
|
|
141
|
-
const char = str.charCodeAt(i);
|
|
142
|
-
hash = (hash << 5) - hash + char;
|
|
143
|
-
hash &= hash;
|
|
144
|
-
}
|
|
145
|
-
return new Uint32Array([hash])[0].toString(36);
|
|
146
|
-
}
|
|
147
|
-
function escapeRegExp(string) {
|
|
148
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
149
|
-
}
|
|
150
|
-
export {
|
|
151
|
-
cloudflareModuleLoader
|
|
152
|
-
};
|