@astrojs/cloudflare 13.0.0-beta.5 → 13.0.0-beta.7
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/esbuild-plugin-astro-frontmatter.d.ts +9 -0
- package/dist/esbuild-plugin-astro-frontmatter.js +29 -0
- package/dist/index.d.ts +0 -8
- package/dist/index.js +39 -18
- 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/image-config.d.ts +2 -2
- 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 +5 -6
- 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.7";
|
|
87
87
|
const localPrefix = `${colors.dim("\u2503")} Local `;
|
|
88
88
|
const networkPrefix = `${colors.dim("\u2503")} Network `;
|
|
89
89
|
const emptyPrefix = " ".repeat(11);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { DepOptimizationConfig } from 'vite';
|
|
2
|
+
type ESBuildPlugin = NonNullable<NonNullable<DepOptimizationConfig['esbuildOptions']>['plugins']>[0];
|
|
3
|
+
/**
|
|
4
|
+
* An esbuild plugin that extracts frontmatter from .astro files during
|
|
5
|
+
* dependency optimization scanning. This allows Vite to discover imports
|
|
6
|
+
* in the server-side frontmatter code.
|
|
7
|
+
*/
|
|
8
|
+
export declare function astroFrontmatterScanPlugin(): ESBuildPlugin;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---/;
|
|
3
|
+
function astroFrontmatterScanPlugin() {
|
|
4
|
+
return {
|
|
5
|
+
name: "astro-frontmatter-scan",
|
|
6
|
+
setup(build) {
|
|
7
|
+
build.onLoad({ filter: /\.astro$/ }, async (args) => {
|
|
8
|
+
try {
|
|
9
|
+
const code = await readFile(args.path, "utf-8");
|
|
10
|
+
const frontmatterMatch = FRONTMATTER_RE.exec(code);
|
|
11
|
+
if (frontmatterMatch) {
|
|
12
|
+
return {
|
|
13
|
+
contents: frontmatterMatch[1],
|
|
14
|
+
loader: "ts"
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
} catch {
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
contents: "",
|
|
21
|
+
loader: "ts"
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
astroFrontmatterScanPlugin
|
|
29
|
+
};
|
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.
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createInterface } from "node:readline/promises";
|
|
|
4
4
|
import { removeLeadingForwardSlash } from "@astrojs/internal-helpers/path";
|
|
5
5
|
import { createRedirectsFromAstroRoutes, printAsRedirects } from "@astrojs/underscore-redirects";
|
|
6
6
|
import { cloudflare as cfVitePlugin } from "@cloudflare/vite-plugin";
|
|
7
|
-
import {
|
|
7
|
+
import { astroFrontmatterScanPlugin } from "./esbuild-plugin-astro-frontmatter.js";
|
|
8
8
|
import { createRoutesFile, getParts } from "./utils/generate-routes-json.js";
|
|
9
9
|
import { setImageConfig } from "./utils/image-config.js";
|
|
10
10
|
import { createConfigPlugin } from "./vite-plugin-config.js";
|
|
@@ -13,14 +13,12 @@ import {
|
|
|
13
13
|
DEFAULT_SESSION_KV_BINDING_NAME,
|
|
14
14
|
DEFAULT_IMAGES_BINDING_NAME
|
|
15
15
|
} from "./wrangler.js";
|
|
16
|
-
import {
|
|
16
|
+
import { parseEnv } from "node:util";
|
|
17
17
|
import { sessionDrivers } from "astro/config";
|
|
18
|
+
import { createCloudflarePrerenderer } from "./prerenderer.js";
|
|
18
19
|
function createIntegration(args) {
|
|
19
20
|
let _config;
|
|
20
21
|
let finalBuildOutput;
|
|
21
|
-
const cloudflareModulePlugin = cloudflareModuleLoader(
|
|
22
|
-
args?.cloudflareModules ?? true
|
|
23
|
-
);
|
|
24
22
|
let _routes;
|
|
25
23
|
const sessionKVBindingName = args?.sessionKVBindingName ?? DEFAULT_SESSION_KV_BINDING_NAME;
|
|
26
24
|
const imagesBindingName = args?.imagesBindingName ?? DEFAULT_IMAGES_BINDING_NAME;
|
|
@@ -51,22 +49,29 @@ function createIntegration(args) {
|
|
|
51
49
|
config: cloudflareConfigCustomizer({
|
|
52
50
|
sessionKVBindingName: args?.sessionKVBindingName,
|
|
53
51
|
imagesBindingName: args?.imageService === "cloudflare-binding" ? args?.imagesBindingName : false
|
|
54
|
-
})
|
|
52
|
+
}),
|
|
53
|
+
experimental: {
|
|
54
|
+
prerenderWorker: {
|
|
55
|
+
config(_, { entryWorkerConfig }) {
|
|
56
|
+
return {
|
|
57
|
+
...entryWorkerConfig,
|
|
58
|
+
// This is the Vite environment name used for prerendering
|
|
59
|
+
name: "prerender"
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
55
64
|
};
|
|
56
65
|
updateConfig({
|
|
57
66
|
build: {
|
|
58
67
|
client: new URL(`./client/`, config.outDir),
|
|
59
68
|
server: new URL("./_worker.js/", config.outDir),
|
|
60
|
-
serverEntry: config.build.serverEntry ?? "index.js",
|
|
61
69
|
redirects: false
|
|
62
70
|
},
|
|
63
71
|
session,
|
|
64
72
|
vite: {
|
|
65
73
|
plugins: [
|
|
66
74
|
cfVitePlugin(cfPluginConfig),
|
|
67
|
-
// https://developers.cloudflare.com/pages/functions/module-support/
|
|
68
|
-
// Allows imports of '.wasm', '.bin', and '.txt' file types
|
|
69
|
-
cloudflareModulePlugin,
|
|
70
75
|
{
|
|
71
76
|
name: "@astrojs/cloudflare:cf-imports",
|
|
72
77
|
enforce: "pre",
|
|
@@ -82,8 +87,10 @@ function createIntegration(args) {
|
|
|
82
87
|
{
|
|
83
88
|
name: "@astrojs/cloudflare:environment",
|
|
84
89
|
configEnvironment(environmentName, _options) {
|
|
85
|
-
const isServerEnvironment = ["ssr", "prerender"].includes(
|
|
86
|
-
|
|
90
|
+
const isServerEnvironment = ["astro", "ssr", "prerender"].includes(
|
|
91
|
+
environmentName
|
|
92
|
+
);
|
|
93
|
+
if (isServerEnvironment && !_options.optimizeDeps?.noDiscovery) {
|
|
87
94
|
return {
|
|
88
95
|
optimizeDeps: {
|
|
89
96
|
include: [
|
|
@@ -111,7 +118,10 @@ function createIntegration(args) {
|
|
|
111
118
|
"virtual:astro:*",
|
|
112
119
|
"virtual:astro-cloudflare:*",
|
|
113
120
|
"virtual:@astrojs/*"
|
|
114
|
-
]
|
|
121
|
+
],
|
|
122
|
+
esbuildOptions: {
|
|
123
|
+
plugins: [astroFrontmatterScanPlugin()]
|
|
124
|
+
}
|
|
115
125
|
}
|
|
116
126
|
};
|
|
117
127
|
} else if (environmentName === "client") {
|
|
@@ -175,10 +185,9 @@ function createIntegration(args) {
|
|
|
175
185
|
i18nDomains: "experimental",
|
|
176
186
|
sharpImageService: {
|
|
177
187
|
support: "limited",
|
|
178
|
-
message:
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
suppress: args?.imageService ? "all" : "default"
|
|
188
|
+
message: "When using a custom image service, ensure it is compatible with the Cloudflare Workers runtime.",
|
|
189
|
+
// Only 'custom' could potentially use sharp at runtime.
|
|
190
|
+
suppress: args?.imageService === "custom" ? "default" : "all"
|
|
182
191
|
},
|
|
183
192
|
envGetSecret: "stable"
|
|
184
193
|
}
|
|
@@ -187,7 +196,7 @@ function createIntegration(args) {
|
|
|
187
196
|
if (existsSync(devVarsPath)) {
|
|
188
197
|
try {
|
|
189
198
|
const data = readFileSync(devVarsPath, "utf-8");
|
|
190
|
-
const parsed =
|
|
199
|
+
const parsed = parseEnv(data);
|
|
191
200
|
Object.assign(process.env, parsed);
|
|
192
201
|
} catch {
|
|
193
202
|
logger.error(
|
|
@@ -196,6 +205,17 @@ function createIntegration(args) {
|
|
|
196
205
|
}
|
|
197
206
|
}
|
|
198
207
|
},
|
|
208
|
+
"astro:build:start": ({ setPrerenderer }) => {
|
|
209
|
+
setPrerenderer(
|
|
210
|
+
createCloudflarePrerenderer({
|
|
211
|
+
root: _config.root,
|
|
212
|
+
serverDir: _config.build.server,
|
|
213
|
+
clientDir: _config.build.client,
|
|
214
|
+
base: _config.base,
|
|
215
|
+
trailingSlash: _config.trailingSlash
|
|
216
|
+
})
|
|
217
|
+
);
|
|
218
|
+
},
|
|
199
219
|
"astro:build:setup": ({ vite, target }) => {
|
|
200
220
|
if (target === "server") {
|
|
201
221
|
vite.resolve ||= {};
|
|
@@ -284,6 +304,7 @@ function createIntegration(args) {
|
|
|
284
304
|
logger.error("Failed to write _redirects file");
|
|
285
305
|
}
|
|
286
306
|
}
|
|
307
|
+
delete process.env.CLOUDFLARE_VITE_BUILD;
|
|
287
308
|
}
|
|
288
309
|
}
|
|
289
310
|
};
|
|
@@ -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;
|
|
@@ -14,7 +14,7 @@ export declare function setImageConfig(service: ImageService, config: AstroConfi
|
|
|
14
14
|
pathname?: string | undefined;
|
|
15
15
|
}[];
|
|
16
16
|
responsiveStyles: boolean;
|
|
17
|
-
layout?: "
|
|
17
|
+
layout?: "fixed" | "none" | "constrained" | "full-width" | undefined;
|
|
18
18
|
objectFit?: string | undefined;
|
|
19
19
|
objectPosition?: string | undefined;
|
|
20
20
|
breakpoints?: number[] | undefined;
|
|
@@ -31,7 +31,7 @@ export declare function setImageConfig(service: ImageService, config: AstroConfi
|
|
|
31
31
|
pathname?: string | undefined;
|
|
32
32
|
}[];
|
|
33
33
|
responsiveStyles: boolean;
|
|
34
|
-
layout?: "
|
|
34
|
+
layout?: "fixed" | "none" | "constrained" | "full-width" | undefined;
|
|
35
35
|
objectFit?: string | undefined;
|
|
36
36
|
objectPosition?: string | undefined;
|
|
37
37
|
breakpoints?: number[] | undefined;
|
|
@@ -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.7",
|
|
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": {
|
|
@@ -49,10 +48,10 @@
|
|
|
49
48
|
},
|
|
50
49
|
"devDependencies": {
|
|
51
50
|
"@cloudflare/workers-types": "^4.20260131.0",
|
|
51
|
+
"@types/node": "^25.2.2",
|
|
52
52
|
"cheerio": "1.2.0",
|
|
53
53
|
"devalue": "^5.6.2",
|
|
54
|
-
"
|
|
55
|
-
"astro": "6.0.0-beta.9",
|
|
54
|
+
"astro": "6.0.0-beta.11",
|
|
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
|
-
};
|