@astrojs/cloudflare 10.2.6 → 10.4.0
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/server.d.ts +1 -0
- package/dist/entrypoints/server.js +4 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +13 -2
- package/dist/utils/cloudflare-module-loader.d.ts +21 -0
- package/dist/utils/{wasm-module-loader.js → cloudflare-module-loader.js} +59 -30
- package/dist/utils/env.d.ts +2 -0
- package/dist/utils/env.js +11 -0
- package/dist/utils/image-config.d.ts +1 -1
- package/dist/utils/non-server-chunk-detector.d.ts +2 -2
- package/package.json +3 -3
- package/dist/utils/wasm-module-loader.d.ts +0 -16
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CacheStorage as CLOUDFLARE_CACHESTORAGE, Request as CLOUDFLARE_REQUEST, ExecutionContext } from '@cloudflare/workers-types';
|
|
2
2
|
import type { SSRManifest } from 'astro';
|
|
3
3
|
type Env = {
|
|
4
|
+
[key: string]: unknown;
|
|
4
5
|
ASSETS: {
|
|
5
6
|
fetch: (req: Request | string) => Promise<Response>;
|
|
6
7
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { App } from 'astro/app';
|
|
2
|
+
import { createGetEnv } from '../utils/env.js';
|
|
2
3
|
export function createExports(manifest) {
|
|
3
4
|
const app = new App(manifest);
|
|
4
5
|
const fetch = async (request, env, context) => {
|
|
@@ -32,6 +33,9 @@ export function createExports(manifest) {
|
|
|
32
33
|
},
|
|
33
34
|
},
|
|
34
35
|
};
|
|
36
|
+
// Won't throw if the virtual module is not available because it's not supported in
|
|
37
|
+
// the users's astro version or if astro:env is not enabled in the project
|
|
38
|
+
await import('astro/env/setup').then((mod) => mod.setGetEnv(createGetEnv(env))).catch(() => { });
|
|
35
39
|
const response = await app.render(request, { routeData, locals });
|
|
36
40
|
if (app.setCookieHeaders) {
|
|
37
41
|
for (const setCookieHeader of app.setCookieHeaders(response)) {
|
package/dist/index.d.ts
CHANGED
|
@@ -43,9 +43,14 @@ export type Options = {
|
|
|
43
43
|
};
|
|
44
44
|
};
|
|
45
45
|
/**
|
|
46
|
-
* Allow bundling cloudflare worker specific file types
|
|
47
|
-
*
|
|
46
|
+
* Allow bundling cloudflare worker specific file types as importable modules. Defaults to true.
|
|
47
|
+
* When enabled, allows imports of '.wasm', '.bin', and '.txt' file types
|
|
48
|
+
*
|
|
49
|
+
* See https://developers.cloudflare.com/pages/functions/module-support/
|
|
50
|
+
* for reference on how these file types are exported
|
|
48
51
|
*/
|
|
52
|
+
cloudflareModules?: boolean;
|
|
53
|
+
/** @deprecated - use `cloudflareModules`, which defaults to true. You can set `cloudflareModuleLoading: false` to disable */
|
|
49
54
|
wasmModuleImports?: boolean;
|
|
50
55
|
};
|
|
51
56
|
export default function createIntegration(args?: Options): AstroIntegration;
|
package/dist/index.js
CHANGED
|
@@ -7,14 +7,15 @@ import { AstroError } from 'astro/errors';
|
|
|
7
7
|
import { walk } from 'estree-walker';
|
|
8
8
|
import MagicString from 'magic-string';
|
|
9
9
|
import { getPlatformProxy } from 'wrangler';
|
|
10
|
+
import { cloudflareModuleLoader, } from './utils/cloudflare-module-loader.js';
|
|
11
|
+
import { createGetEnv } from './utils/env.js';
|
|
10
12
|
import { createRoutesFile, getParts } from './utils/generate-routes-json.js';
|
|
11
13
|
import { setImageConfig } from './utils/image-config.js';
|
|
12
14
|
import { mutateDynamicPageImportsInPlace, mutatePageMapInPlace } from './utils/index.js';
|
|
13
15
|
import { NonServerChunkDetector } from './utils/non-server-chunk-detector.js';
|
|
14
|
-
import { cloudflareModuleLoader } from './utils/wasm-module-loader.js';
|
|
15
16
|
export default function createIntegration(args) {
|
|
16
17
|
let _config;
|
|
17
|
-
const cloudflareModulePlugin = cloudflareModuleLoader(args?.wasmModuleImports ??
|
|
18
|
+
const cloudflareModulePlugin = cloudflareModuleLoader(args?.cloudflareModules ?? args?.wasmModuleImports ?? true);
|
|
18
19
|
// Initialize the unused chunk analyzer as a shared state between hooks.
|
|
19
20
|
// The analyzer is used on earlier hooks to collect information about used hooks on a Vite plugin
|
|
20
21
|
// and then later after the full build to clean up unused chunks, so it has to be shared between them.
|
|
@@ -107,6 +108,7 @@ export default function createIntegration(args) {
|
|
|
107
108
|
isSharpCompatible: false,
|
|
108
109
|
isSquooshCompatible: false,
|
|
109
110
|
},
|
|
111
|
+
envGetSecret: 'experimental',
|
|
110
112
|
},
|
|
111
113
|
});
|
|
112
114
|
},
|
|
@@ -117,6 +119,15 @@ export default function createIntegration(args) {
|
|
|
117
119
|
experimentalJsonConfig: args.platformProxy.experimentalJsonConfig ?? false,
|
|
118
120
|
persist: args.platformProxy.persist ?? true,
|
|
119
121
|
});
|
|
122
|
+
const getEnv = createGetEnv(platformProxy.env);
|
|
123
|
+
if (_config.experimental.env?.schema) {
|
|
124
|
+
for (const key of Object.keys(_config.experimental.env.schema)) {
|
|
125
|
+
const value = getEnv(key);
|
|
126
|
+
if (value !== undefined) {
|
|
127
|
+
process.env[key] = value;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
120
131
|
const clientLocalsSymbol = Symbol.for('astro.locals');
|
|
121
132
|
server.middlewares.use(async function middleware(req, res, next) {
|
|
122
133
|
Reflect.set(req, clientLocalsSymbol, {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AstroConfig } from 'astro';
|
|
2
|
+
import type { PluginOption } from 'vite';
|
|
3
|
+
export interface CloudflareModulePluginExtra {
|
|
4
|
+
afterBuildCompleted(config: AstroConfig): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export type ModuleType = 'CompiledWasm' | 'Text' | 'Data';
|
|
7
|
+
/**
|
|
8
|
+
* Enables support for various non-standard extensions in module imports that cloudflare workers supports.
|
|
9
|
+
*
|
|
10
|
+
* See https://developers.cloudflare.com/pages/functions/module-support/ for reference
|
|
11
|
+
*
|
|
12
|
+
* This adds supports for imports in the following formats:
|
|
13
|
+
* - .wasm
|
|
14
|
+
* - .wasm?module
|
|
15
|
+
* - .bin
|
|
16
|
+
* - .txt
|
|
17
|
+
*
|
|
18
|
+
* @param enabled - if true, will load all cloudflare pages supported types
|
|
19
|
+
* @returns Vite plugin with additional extension method to hook into astro build
|
|
20
|
+
*/
|
|
21
|
+
export declare function cloudflareModuleLoader(enabled: boolean): PluginOption & CloudflareModulePluginExtra;
|
|
@@ -2,21 +2,32 @@ import * as fs from 'node:fs/promises';
|
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import * as url from 'node:url';
|
|
4
4
|
/**
|
|
5
|
-
* Enables support for
|
|
5
|
+
* Enables support for various non-standard extensions in module imports that cloudflare workers supports.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* See https://developers.cloudflare.com/pages/functions/module-support/ for reference
|
|
8
|
+
*
|
|
9
|
+
* This adds supports for imports in the following formats:
|
|
10
|
+
* - .wasm
|
|
11
|
+
* - .wasm?module
|
|
12
|
+
* - .bin
|
|
13
|
+
* - .txt
|
|
14
|
+
*
|
|
15
|
+
* @param enabled - if true, will load all cloudflare pages supported types
|
|
16
|
+
* @returns Vite plugin with additional extension method to hook into astro build
|
|
12
17
|
*/
|
|
13
18
|
export function cloudflareModuleLoader(enabled) {
|
|
14
|
-
|
|
19
|
+
/**
|
|
20
|
+
* It's likely that eventually cloudflare will add support for custom extensions, like they do in vanilla cloudflare workers,
|
|
21
|
+
* by adding rules to your wrangler.tome
|
|
22
|
+
* https://developers.cloudflare.com/workers/wrangler/bundling/
|
|
23
|
+
*/
|
|
24
|
+
const adaptersByExtension = enabled ? { ...defaultAdapters } : {};
|
|
25
|
+
const extensions = Object.keys(adaptersByExtension);
|
|
15
26
|
let isDev = false;
|
|
16
27
|
const MAGIC_STRING = '__CLOUDFLARE_ASSET__';
|
|
17
28
|
const replacements = [];
|
|
18
29
|
return {
|
|
19
|
-
name: 'vite:
|
|
30
|
+
name: 'vite:cf-module-loader',
|
|
20
31
|
enforce: 'pre',
|
|
21
32
|
configResolved(config) {
|
|
22
33
|
isDev = config.command === 'serve';
|
|
@@ -24,27 +35,30 @@ export function cloudflareModuleLoader(enabled) {
|
|
|
24
35
|
config(_, __) {
|
|
25
36
|
// let vite know that file format and the magic import string is intentional, and will be handled in this plugin
|
|
26
37
|
return {
|
|
27
|
-
assetsInclude:
|
|
38
|
+
assetsInclude: extensions.map((x) => `**/*${x}`),
|
|
28
39
|
build: {
|
|
29
40
|
rollupOptions: {
|
|
30
41
|
// mark the wasm files as external so that they are not bundled and instead are loaded from the files
|
|
31
|
-
external:
|
|
42
|
+
external: extensions.map((x) => new RegExp(`^${MAGIC_STRING}.+${escapeRegExp(x)}.mjs$`, 'i')),
|
|
32
43
|
},
|
|
33
44
|
},
|
|
34
45
|
};
|
|
35
46
|
},
|
|
36
47
|
async load(id, _) {
|
|
37
|
-
const
|
|
38
|
-
|
|
48
|
+
const maybeExtension = extensions.find((x) => id.endsWith(x));
|
|
49
|
+
const moduleType = (maybeExtension && adaptersByExtension[maybeExtension]) || undefined;
|
|
50
|
+
if (!moduleType || !maybeExtension) {
|
|
39
51
|
return;
|
|
40
52
|
}
|
|
41
53
|
if (!enabled) {
|
|
42
|
-
throw new Error(`Cloudflare module loading is experimental. The ${
|
|
54
|
+
throw new Error(`Cloudflare module loading is experimental. The ${maybeExtension} module cannot be loaded unless you add \`wasmModuleImports: true\` to your astro config.`);
|
|
43
55
|
}
|
|
44
|
-
const
|
|
56
|
+
const moduleLoader = renderers[moduleType];
|
|
57
|
+
const filePath = id.replace(/\?\w+$/, '');
|
|
58
|
+
const extension = maybeExtension.replace(/\?\w+$/, '');
|
|
45
59
|
const data = await fs.readFile(filePath);
|
|
46
60
|
const base64 = data.toString('base64');
|
|
47
|
-
const inlineModule =
|
|
61
|
+
const inlineModule = moduleLoader(data);
|
|
48
62
|
if (isDev) {
|
|
49
63
|
// no need to wire up the assets in dev mode, just rewrite
|
|
50
64
|
return inlineModule;
|
|
@@ -53,7 +67,7 @@ export function cloudflareModuleLoader(enabled) {
|
|
|
53
67
|
const hash = hashString(base64);
|
|
54
68
|
// emit the wasm binary as an asset file, to be picked up later by the esbuild bundle for the worker.
|
|
55
69
|
// give it a shared deterministic name to make things easy for esbuild to switch on later
|
|
56
|
-
const assetName = `${path.basename(filePath).split('.')[0]}.${hash}
|
|
70
|
+
const assetName = `${path.basename(filePath).split('.')[0]}.${hash}${extension}`;
|
|
57
71
|
this.emitFile({
|
|
58
72
|
type: 'asset',
|
|
59
73
|
// emit the data explicitly as an esset with `fileName` rather than `name` so that
|
|
@@ -68,7 +82,7 @@ export function cloudflareModuleLoader(enabled) {
|
|
|
68
82
|
fileName: `${assetName}.mjs`,
|
|
69
83
|
code: inlineModule,
|
|
70
84
|
});
|
|
71
|
-
return `import module from "${MAGIC_STRING}${chunkId}
|
|
85
|
+
return `import module from "${MAGIC_STRING}${chunkId}${extension}.mjs";export default module;`;
|
|
72
86
|
},
|
|
73
87
|
// output original wasm file relative to the chunk now that chunking has been achieved
|
|
74
88
|
renderChunk(code, chunk, _) {
|
|
@@ -79,10 +93,10 @@ export function cloudflareModuleLoader(enabled) {
|
|
|
79
93
|
// SSR will need the .mjs suffix removed from the import before this works in cloudflare, but this is done as a final step
|
|
80
94
|
// so as to support prerendering from nodejs runtime
|
|
81
95
|
let replaced = code;
|
|
82
|
-
for (const
|
|
83
|
-
|
|
96
|
+
for (const ext of extensions) {
|
|
97
|
+
const extension = ext.replace(/\?\w+$/, '');
|
|
84
98
|
// chunk id can be many things, (alpha numeric, dollars, or underscores, maybe more)
|
|
85
|
-
new RegExp(`${MAGIC_STRING}([^\\s]+?)
|
|
99
|
+
replaced = replaced.replaceAll(new RegExp(`${MAGIC_STRING}([^\\s]+?)${escapeRegExp(extension)}\\.mjs`, 'g'), (s, assetId) => {
|
|
86
100
|
const fileName = this.getFileName(assetId);
|
|
87
101
|
const relativePath = path
|
|
88
102
|
.relative(path.dirname(chunk.fileName), fileName)
|
|
@@ -96,9 +110,6 @@ export function cloudflareModuleLoader(enabled) {
|
|
|
96
110
|
return `./${relativePath}`;
|
|
97
111
|
});
|
|
98
112
|
}
|
|
99
|
-
if (replaced.includes(MAGIC_STRING)) {
|
|
100
|
-
console.error('failed to replace', replaced);
|
|
101
|
-
}
|
|
102
113
|
return { code: replaced };
|
|
103
114
|
},
|
|
104
115
|
generateBundle(_, bundle) {
|
|
@@ -126,8 +137,9 @@ export function cloudflareModuleLoader(enabled) {
|
|
|
126
137
|
const baseDir = url.fileURLToPath(config.outDir);
|
|
127
138
|
const replacementsByFileName = new Map();
|
|
128
139
|
for (const replacement of replacements) {
|
|
129
|
-
if (!replacement.fileName)
|
|
140
|
+
if (!replacement.fileName) {
|
|
130
141
|
continue;
|
|
142
|
+
}
|
|
131
143
|
const repls = replacementsByFileName.get(replacement.fileName) || [];
|
|
132
144
|
if (!repls.length) {
|
|
133
145
|
replacementsByFileName.set(replacement.fileName, repls);
|
|
@@ -139,22 +151,36 @@ export function cloudflareModuleLoader(enabled) {
|
|
|
139
151
|
const contents = await fs.readFile(filepath, 'utf-8');
|
|
140
152
|
let updated = contents;
|
|
141
153
|
for (const replacement of repls) {
|
|
142
|
-
updated =
|
|
154
|
+
updated = updated.replaceAll(replacement.nodejsImport, replacement.cloudflareImport);
|
|
143
155
|
}
|
|
144
156
|
await fs.writeFile(filepath, updated, 'utf-8');
|
|
145
157
|
}
|
|
146
158
|
},
|
|
147
159
|
};
|
|
148
160
|
}
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
qualifiedExtension: 'wasm?module',
|
|
152
|
-
asNodeModule(fileContents) {
|
|
161
|
+
const renderers = {
|
|
162
|
+
CompiledWasm(fileContents) {
|
|
153
163
|
const base64 = fileContents.toString('base64');
|
|
154
164
|
return `const wasmModule = new WebAssembly.Module(Uint8Array.from(atob("${base64}"), c => c.charCodeAt(0)));export default wasmModule;`;
|
|
155
165
|
},
|
|
166
|
+
Data(fileContents) {
|
|
167
|
+
const base64 = fileContents.toString('base64');
|
|
168
|
+
return `const binModule = Uint8Array.from(atob("${base64}"), c => c.charCodeAt(0)).buffer;export default binModule;`;
|
|
169
|
+
},
|
|
170
|
+
Text(fileContents) {
|
|
171
|
+
const escaped = JSON.stringify(fileContents.toString('utf-8'));
|
|
172
|
+
return `const stringModule = ${escaped};export default stringModule;`;
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
const defaultAdapters = {
|
|
176
|
+
// Loads '*.wasm?module' imports as WebAssembly modules, which is the only way to load WASM in cloudflare workers.
|
|
177
|
+
// Current proposal for WASM modules: https://github.com/WebAssembly/esm-integration/tree/main/proposals/esm-integration
|
|
178
|
+
'.wasm?module': 'CompiledWasm',
|
|
179
|
+
// treats the module as a WASM module
|
|
180
|
+
'.wasm': 'CompiledWasm',
|
|
181
|
+
'.bin': 'Data',
|
|
182
|
+
'.txt': 'Text',
|
|
156
183
|
};
|
|
157
|
-
const cloudflareImportAdapters = [wasmImportAdapter];
|
|
158
184
|
/**
|
|
159
185
|
* Returns a deterministic 32 bit hash code from a string
|
|
160
186
|
*/
|
|
@@ -167,3 +193,6 @@ function hashString(str) {
|
|
|
167
193
|
}
|
|
168
194
|
return new Uint32Array([hash])[0].toString(36);
|
|
169
195
|
}
|
|
196
|
+
function escapeRegExp(string) {
|
|
197
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
|
|
198
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const createGetEnv = (env) => (key) => {
|
|
2
|
+
const v = env[key];
|
|
3
|
+
if (typeof v === 'undefined' || typeof v === 'string') {
|
|
4
|
+
return v;
|
|
5
|
+
}
|
|
6
|
+
if (typeof v === 'boolean' || typeof v === 'number') {
|
|
7
|
+
// let astro:env handle the validation and transformation
|
|
8
|
+
return v.toString();
|
|
9
|
+
}
|
|
10
|
+
return undefined;
|
|
11
|
+
};
|
|
@@ -3,9 +3,9 @@ export declare function setImageConfig(service: string, config: AstroConfig['ima
|
|
|
3
3
|
service: import("astro").ImageServiceConfig<Record<string, any>>;
|
|
4
4
|
domains: string[];
|
|
5
5
|
remotePatterns: {
|
|
6
|
+
port?: string | undefined;
|
|
6
7
|
protocol?: string | undefined;
|
|
7
8
|
hostname?: string | undefined;
|
|
8
|
-
port?: string | undefined;
|
|
9
9
|
pathname?: string | undefined;
|
|
10
10
|
}[];
|
|
11
11
|
endpoint?: string | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PluginOption } from 'vite';
|
|
2
2
|
/**
|
|
3
3
|
* A Vite bundle analyzer that identifies chunks that are not used for server rendering.
|
|
4
4
|
*
|
|
@@ -8,7 +8,7 @@ import type { Plugin } from 'vite';
|
|
|
8
8
|
*/
|
|
9
9
|
export declare class NonServerChunkDetector {
|
|
10
10
|
private nonServerChunks?;
|
|
11
|
-
getPlugin():
|
|
11
|
+
getPlugin(): PluginOption;
|
|
12
12
|
private processBundle;
|
|
13
13
|
getNonServerChunks(): string[];
|
|
14
14
|
}
|
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": "10.
|
|
4
|
+
"version": "10.4.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
7
7
|
"author": "withastro",
|
|
@@ -42,13 +42,13 @@
|
|
|
42
42
|
"astro": "^4.2.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"astro": "^4.
|
|
45
|
+
"astro": "^4.10.1",
|
|
46
46
|
"cheerio": "1.0.0-rc.12",
|
|
47
47
|
"execa": "^8.0.1",
|
|
48
48
|
"fast-glob": "^3.3.2",
|
|
49
49
|
"rollup": "^4.14.0",
|
|
50
50
|
"strip-ansi": "^7.1.0",
|
|
51
|
-
"vite": "^5.2.
|
|
51
|
+
"vite": "^5.2.12",
|
|
52
52
|
"@astrojs/test-utils": "0.0.1",
|
|
53
53
|
"astro-scripts": "0.0.14"
|
|
54
54
|
},
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { AstroConfig } from 'astro';
|
|
2
|
-
import type { PluginOption } from 'vite';
|
|
3
|
-
export interface CloudflareModulePluginExtra {
|
|
4
|
-
afterBuildCompleted(config: AstroConfig): Promise<void>;
|
|
5
|
-
}
|
|
6
|
-
/**
|
|
7
|
-
* Enables support for wasm modules within cloudflare pages functions
|
|
8
|
-
*
|
|
9
|
-
* Loads '*.wasm?module' and `*.wasm` imports as WebAssembly modules, which is the only way to load WASM in cloudflare workers.
|
|
10
|
-
* Current proposal for WASM modules: https://github.com/WebAssembly/esm-integration/tree/main/proposals/esm-integration
|
|
11
|
-
* Cloudflare worker WASM from javascript support: https://developers.cloudflare.com/workers/runtime-apis/webassembly/javascript/
|
|
12
|
-
* @param enabled - if true, load '.wasm' imports as Uint8Arrays, otherwise will throw errors when encountered to clarify that it must be enabled
|
|
13
|
-
* @returns Vite plugin to load WASM tagged with '?module' as a WASM modules
|
|
14
|
-
*/
|
|
15
|
-
export declare function cloudflareModuleLoader(enabled: boolean): PluginOption & CloudflareModulePluginExtra;
|
|
16
|
-
export type ImportType = 'wasm';
|