@astrojs/cloudflare 12.2.0 → 12.2.2
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/LICENSE +59 -0
- package/dist/entrypoints/image-endpoint.js +14 -10
- package/dist/entrypoints/image-service.js +32 -33
- package/dist/entrypoints/middleware.js +10 -8
- package/dist/entrypoints/server.js +57 -48
- package/dist/index.js +255 -250
- package/dist/utils/assets.js +62 -60
- package/dist/utils/cloudflare-module-loader.js +164 -192
- package/dist/utils/env.js +12 -10
- package/dist/utils/generate-routes-json.js +212 -247
- package/dist/utils/image-config.js +32 -29
- package/dist/utils/non-server-chunk-detector.js +48 -65
- package/package.json +13 -15
|
@@ -1,203 +1,175 @@
|
|
|
1
|
-
import * as fs from
|
|
2
|
-
import * as path from
|
|
3
|
-
import * as url from
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import * as url from "node:url";
|
|
4
|
+
function cloudflareModuleLoader(enabled) {
|
|
5
|
+
const adaptersByExtension = enabled ? { ...defaultAdapters } : {};
|
|
6
|
+
const extensions = Object.keys(adaptersByExtension);
|
|
7
|
+
let isDev = false;
|
|
8
|
+
const MAGIC_STRING = "__CLOUDFLARE_ASSET__";
|
|
9
|
+
const replacements = [];
|
|
10
|
+
return {
|
|
11
|
+
name: "vite:cf-module-loader",
|
|
12
|
+
enforce: "pre",
|
|
13
|
+
configResolved(config) {
|
|
14
|
+
isDev = config.command === "serve";
|
|
15
|
+
},
|
|
16
|
+
config(_, __) {
|
|
17
|
+
return {
|
|
18
|
+
assetsInclude: extensions.map((x) => `**/*${x}`),
|
|
19
|
+
build: {
|
|
20
|
+
rollupOptions: {
|
|
21
|
+
// mark the wasm files as external so that they are not bundled and instead are loaded from the files
|
|
22
|
+
external: extensions.map(
|
|
23
|
+
(x) => new RegExp(`^${MAGIC_STRING}.+${escapeRegExp(x)}.mjs$`, "i")
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
async load(id, _) {
|
|
30
|
+
const maybeExtension = extensions.find((x) => id.endsWith(x));
|
|
31
|
+
const moduleType = maybeExtension && adaptersByExtension[maybeExtension] || void 0;
|
|
32
|
+
if (!moduleType || !maybeExtension) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (!enabled) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`Cloudflare module loading is experimental. The ${maybeExtension} module cannot be loaded unless you add \`cloudflareModules: true\` to your astro config.`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
const moduleLoader = renderers[moduleType];
|
|
41
|
+
const filePath = id.replace(/\?\w+$/, "");
|
|
42
|
+
const extension = maybeExtension.replace(/\?\w+$/, "");
|
|
43
|
+
const data = await fs.readFile(filePath);
|
|
44
|
+
const base64 = data.toString("base64");
|
|
45
|
+
const inlineModule = moduleLoader(data);
|
|
46
|
+
if (isDev) {
|
|
47
|
+
return inlineModule;
|
|
48
|
+
}
|
|
49
|
+
const hash = hashString(base64);
|
|
50
|
+
const assetName = `${path.basename(filePath).split(".")[0]}.${hash}${extension}`;
|
|
51
|
+
this.emitFile({
|
|
52
|
+
type: "asset",
|
|
53
|
+
// emit the data explicitly as an esset with `fileName` rather than `name` so that
|
|
54
|
+
// vite doesn't give it a random hash-id in its name--We need to be able to easily rewrite from
|
|
55
|
+
// the .mjs loader and the actual wasm asset later in the ESbuild for the worker
|
|
56
|
+
fileName: assetName,
|
|
57
|
+
source: data
|
|
58
|
+
});
|
|
59
|
+
const chunkId = this.emitFile({
|
|
60
|
+
type: "prebuilt-chunk",
|
|
61
|
+
fileName: `${assetName}.mjs`,
|
|
62
|
+
code: inlineModule
|
|
63
|
+
});
|
|
64
|
+
return `import module from "${MAGIC_STRING}${chunkId}${extension}.mjs";export default module;`;
|
|
65
|
+
},
|
|
66
|
+
// output original wasm file relative to the chunk now that chunking has been achieved
|
|
67
|
+
renderChunk(code, chunk, _) {
|
|
68
|
+
if (isDev) return;
|
|
69
|
+
if (!code.includes(MAGIC_STRING)) return;
|
|
70
|
+
let replaced = code;
|
|
71
|
+
for (const ext of extensions) {
|
|
72
|
+
const extension = ext.replace(/\?\w+$/, "");
|
|
73
|
+
replaced = replaced.replaceAll(
|
|
74
|
+
new RegExp(`${MAGIC_STRING}([^\\s]+?)${escapeRegExp(extension)}\\.mjs`, "g"),
|
|
75
|
+
(_s, assetId) => {
|
|
76
|
+
const fileName = this.getFileName(assetId);
|
|
77
|
+
const relativePath = path.relative(path.dirname(chunk.fileName), fileName).replaceAll("\\", "/");
|
|
78
|
+
replacements.push({
|
|
79
|
+
chunkName: chunk.name,
|
|
80
|
+
cloudflareImport: relativePath.replace(/\.mjs$/, ""),
|
|
81
|
+
nodejsImport: relativePath
|
|
82
|
+
});
|
|
83
|
+
return `./${relativePath}`;
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
return { code: replaced };
|
|
88
|
+
},
|
|
89
|
+
generateBundle(_, bundle) {
|
|
90
|
+
const replacementsByChunkName = /* @__PURE__ */ new Map();
|
|
91
|
+
for (const replacement of replacements) {
|
|
92
|
+
const repls = replacementsByChunkName.get(replacement.chunkName) || [];
|
|
93
|
+
if (!repls.length) {
|
|
94
|
+
replacementsByChunkName.set(replacement.chunkName, repls);
|
|
95
|
+
}
|
|
96
|
+
repls.push(replacement);
|
|
97
|
+
}
|
|
98
|
+
for (const chunk of Object.values(bundle)) {
|
|
99
|
+
const repls = chunk.name && replacementsByChunkName.get(chunk.name);
|
|
100
|
+
for (const replacement of repls || []) {
|
|
101
|
+
if (!replacement.fileName) {
|
|
102
|
+
replacement.fileName = [];
|
|
103
|
+
}
|
|
104
|
+
replacement.fileName.push(chunk.fileName);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
19
108
|
/**
|
|
20
|
-
*
|
|
21
|
-
* by adding rules to your wrangler.tome
|
|
22
|
-
* https://developers.cloudflare.com/workers/wrangler/bundling/
|
|
109
|
+
* Once prerendering is complete, restore the imports in the _worker.js to cloudflare compatible ones, removing the .mjs suffix.
|
|
23
110
|
*/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (!moduleType || !maybeExtension) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
if (!enabled) {
|
|
54
|
-
throw new Error(`Cloudflare module loading is experimental. The ${maybeExtension} module cannot be loaded unless you add \`cloudflareModules: true\` to your astro config.`);
|
|
55
|
-
}
|
|
56
|
-
const moduleLoader = renderers[moduleType];
|
|
57
|
-
const filePath = id.replace(/\?\w+$/, '');
|
|
58
|
-
const extension = maybeExtension.replace(/\?\w+$/, '');
|
|
59
|
-
const data = await fs.readFile(filePath);
|
|
60
|
-
const base64 = data.toString('base64');
|
|
61
|
-
const inlineModule = moduleLoader(data);
|
|
62
|
-
if (isDev) {
|
|
63
|
-
// no need to wire up the assets in dev mode, just rewrite
|
|
64
|
-
return inlineModule;
|
|
65
|
-
}
|
|
66
|
-
// just some shared ID
|
|
67
|
-
const hash = hashString(base64);
|
|
68
|
-
// emit the wasm binary as an asset file, to be picked up later by the esbuild bundle for the worker.
|
|
69
|
-
// give it a shared deterministic name to make things easy for esbuild to switch on later
|
|
70
|
-
const assetName = `${path.basename(filePath).split('.')[0]}.${hash}${extension}`;
|
|
71
|
-
this.emitFile({
|
|
72
|
-
type: 'asset',
|
|
73
|
-
// emit the data explicitly as an esset with `fileName` rather than `name` so that
|
|
74
|
-
// vite doesn't give it a random hash-id in its name--We need to be able to easily rewrite from
|
|
75
|
-
// the .mjs loader and the actual wasm asset later in the ESbuild for the worker
|
|
76
|
-
fileName: assetName,
|
|
77
|
-
source: data,
|
|
78
|
-
});
|
|
79
|
-
// however, by default, the SSG generator cannot import the .wasm as a module, so embed as a base64 string
|
|
80
|
-
const chunkId = this.emitFile({
|
|
81
|
-
type: 'prebuilt-chunk',
|
|
82
|
-
fileName: `${assetName}.mjs`,
|
|
83
|
-
code: inlineModule,
|
|
84
|
-
});
|
|
85
|
-
return `import module from "${MAGIC_STRING}${chunkId}${extension}.mjs";export default module;`;
|
|
86
|
-
},
|
|
87
|
-
// output original wasm file relative to the chunk now that chunking has been achieved
|
|
88
|
-
renderChunk(code, chunk, _) {
|
|
89
|
-
if (isDev)
|
|
90
|
-
return;
|
|
91
|
-
if (!code.includes(MAGIC_STRING))
|
|
92
|
-
return;
|
|
93
|
-
// SSR will need the .mjs suffix removed from the import before this works in cloudflare, but this is done as a final step
|
|
94
|
-
// so as to support prerendering from nodejs runtime
|
|
95
|
-
let replaced = code;
|
|
96
|
-
for (const ext of extensions) {
|
|
97
|
-
const extension = ext.replace(/\?\w+$/, '');
|
|
98
|
-
// chunk id can be many things, (alpha numeric, dollars, or underscores, maybe more)
|
|
99
|
-
replaced = replaced.replaceAll(new RegExp(`${MAGIC_STRING}([^\\s]+?)${escapeRegExp(extension)}\\.mjs`, 'g'), (s, assetId) => {
|
|
100
|
-
const fileName = this.getFileName(assetId);
|
|
101
|
-
const relativePath = path
|
|
102
|
-
.relative(path.dirname(chunk.fileName), fileName)
|
|
103
|
-
.replaceAll('\\', '/'); // fix windows paths for import
|
|
104
|
-
// record this replacement for later, to adjust it to import the unbundled asset
|
|
105
|
-
replacements.push({
|
|
106
|
-
chunkName: chunk.name,
|
|
107
|
-
cloudflareImport: relativePath.replace(/\.mjs$/, ''),
|
|
108
|
-
nodejsImport: relativePath,
|
|
109
|
-
});
|
|
110
|
-
return `./${relativePath}`;
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
return { code: replaced };
|
|
114
|
-
},
|
|
115
|
-
generateBundle(_, bundle) {
|
|
116
|
-
// associate the chunk name to the final file name. After the prerendering is done, we can use this to replace the imports in the _worker.js
|
|
117
|
-
// in a targetted way
|
|
118
|
-
const replacementsByChunkName = new Map();
|
|
119
|
-
for (const replacement of replacements) {
|
|
120
|
-
const repls = replacementsByChunkName.get(replacement.chunkName) || [];
|
|
121
|
-
if (!repls.length) {
|
|
122
|
-
replacementsByChunkName.set(replacement.chunkName, repls);
|
|
123
|
-
}
|
|
124
|
-
repls.push(replacement);
|
|
125
|
-
}
|
|
126
|
-
for (const chunk of Object.values(bundle)) {
|
|
127
|
-
const repls = chunk.name && replacementsByChunkName.get(chunk.name);
|
|
128
|
-
for (const replacement of repls || []) {
|
|
129
|
-
if (!replacement.fileName) {
|
|
130
|
-
replacement.fileName = [];
|
|
131
|
-
}
|
|
132
|
-
replacement.fileName.push(chunk.fileName);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
/**
|
|
137
|
-
* Once prerendering is complete, restore the imports in the _worker.js to cloudflare compatible ones, removing the .mjs suffix.
|
|
138
|
-
*/
|
|
139
|
-
async afterBuildCompleted(config) {
|
|
140
|
-
const baseDir = url.fileURLToPath(config.outDir);
|
|
141
|
-
const replacementsByFileName = new Map();
|
|
142
|
-
for (const replacement of replacements) {
|
|
143
|
-
if (!replacement.fileName) {
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
for (const fileName of replacement.fileName) {
|
|
147
|
-
const repls = replacementsByFileName.get(fileName) || [];
|
|
148
|
-
if (!repls.length) {
|
|
149
|
-
replacementsByFileName.set(fileName, repls);
|
|
150
|
-
}
|
|
151
|
-
repls.push(replacement);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
for (const [fileName, repls] of replacementsByFileName.entries()) {
|
|
155
|
-
const filepath = path.join(baseDir, '_worker.js', fileName);
|
|
156
|
-
const contents = await fs.readFile(filepath, 'utf-8');
|
|
157
|
-
let updated = contents;
|
|
158
|
-
for (const replacement of repls) {
|
|
159
|
-
updated = updated.replaceAll(replacement.nodejsImport, replacement.cloudflareImport);
|
|
160
|
-
}
|
|
161
|
-
await fs.writeFile(filepath, updated, 'utf-8');
|
|
162
|
-
}
|
|
163
|
-
},
|
|
164
|
-
};
|
|
111
|
+
async afterBuildCompleted(config) {
|
|
112
|
+
const baseDir = url.fileURLToPath(config.outDir);
|
|
113
|
+
const replacementsByFileName = /* @__PURE__ */ new Map();
|
|
114
|
+
for (const replacement of replacements) {
|
|
115
|
+
if (!replacement.fileName) {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
for (const fileName of replacement.fileName) {
|
|
119
|
+
const repls = replacementsByFileName.get(fileName) || [];
|
|
120
|
+
if (!repls.length) {
|
|
121
|
+
replacementsByFileName.set(fileName, repls);
|
|
122
|
+
}
|
|
123
|
+
repls.push(replacement);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (const [fileName, repls] of replacementsByFileName.entries()) {
|
|
127
|
+
const filepath = path.join(baseDir, "_worker.js", fileName);
|
|
128
|
+
const contents = await fs.readFile(filepath, "utf-8");
|
|
129
|
+
let updated = contents;
|
|
130
|
+
for (const replacement of repls) {
|
|
131
|
+
updated = updated.replaceAll(replacement.nodejsImport, replacement.cloudflareImport);
|
|
132
|
+
}
|
|
133
|
+
await fs.writeFile(filepath, updated, "utf-8");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
165
137
|
}
|
|
166
138
|
const renderers = {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
139
|
+
CompiledWasm(fileContents) {
|
|
140
|
+
const base64 = fileContents.toString("base64");
|
|
141
|
+
return `const wasmModule = new WebAssembly.Module(Uint8Array.from(atob("${base64}"), c => c.charCodeAt(0)));export default wasmModule;`;
|
|
142
|
+
},
|
|
143
|
+
Data(fileContents) {
|
|
144
|
+
const base64 = fileContents.toString("base64");
|
|
145
|
+
return `const binModule = Uint8Array.from(atob("${base64}"), c => c.charCodeAt(0)).buffer;export default binModule;`;
|
|
146
|
+
},
|
|
147
|
+
Text(fileContents) {
|
|
148
|
+
const escaped = JSON.stringify(fileContents.toString("utf-8"));
|
|
149
|
+
return `const stringModule = ${escaped};export default stringModule;`;
|
|
150
|
+
}
|
|
179
151
|
};
|
|
180
152
|
const defaultAdapters = {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
153
|
+
// Loads '*.wasm?module' imports as WebAssembly modules, which is the only way to load WASM in cloudflare workers.
|
|
154
|
+
// Current proposal for WASM modules: https://github.com/WebAssembly/esm-integration/tree/main/proposals/esm-integration
|
|
155
|
+
".wasm?module": "CompiledWasm",
|
|
156
|
+
// treats the module as a WASM module
|
|
157
|
+
".wasm": "CompiledWasm",
|
|
158
|
+
".bin": "Data",
|
|
159
|
+
".txt": "Text"
|
|
188
160
|
};
|
|
189
|
-
/**
|
|
190
|
-
* Returns a deterministic 32 bit hash code from a string
|
|
191
|
-
*/
|
|
192
161
|
function hashString(str) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
162
|
+
let hash = 0;
|
|
163
|
+
for (let i = 0; i < str.length; i++) {
|
|
164
|
+
const char = str.charCodeAt(i);
|
|
165
|
+
hash = (hash << 5) - hash + char;
|
|
166
|
+
hash &= hash;
|
|
167
|
+
}
|
|
168
|
+
return new Uint32Array([hash])[0].toString(36);
|
|
200
169
|
}
|
|
201
170
|
function escapeRegExp(string) {
|
|
202
|
-
|
|
171
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
203
172
|
}
|
|
173
|
+
export {
|
|
174
|
+
cloudflareModuleLoader
|
|
175
|
+
};
|
package/dist/utils/env.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
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
|
+
return v.toString();
|
|
8
|
+
}
|
|
9
|
+
return void 0;
|
|
10
|
+
};
|
|
11
|
+
export {
|
|
12
|
+
createGetEnv
|
|
11
13
|
};
|