@netlify/edge-bundler 14.10.2 → 15.0.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.
|
@@ -165,33 +165,95 @@ async function getRequiredSourceFiles(deno, entryPoints, importMap) {
|
|
|
165
165
|
pathToFileURL(entryPoint).href,
|
|
166
166
|
]);
|
|
167
167
|
const graph = JSON.parse(stdout);
|
|
168
|
+
// Collect the specifiers of every module reachable through a *code* (runtime)
|
|
169
|
+
// import edge. Deno's module graph classifies each dependency as either a `code`
|
|
170
|
+
// edge (a real runtime import) or a `type`-only edge (`import type`, `@deno-types`).
|
|
171
|
+
// Type-only edges are erased during transpilation and are never loaded at runtime
|
|
172
|
+
// (`deno run` doesn't type-check), so the files they point to don't belong in the
|
|
173
|
+
// bundle. The entry points themselves are always runtime.
|
|
174
|
+
const runtimeSpecifiers = new Set(graph.roots);
|
|
175
|
+
for (const module of graph.modules) {
|
|
176
|
+
for (const dependency of module.dependencies ?? []) {
|
|
177
|
+
if (dependency.code?.specifier) {
|
|
178
|
+
runtimeSpecifiers.add(dependency.code.specifier);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
168
182
|
// Extract all local files from the module graph
|
|
169
183
|
for (const module of graph.modules) {
|
|
170
|
-
if (module.specifier.startsWith('file://')) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
184
|
+
if (!module.specifier.startsWith('file://')) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (module.error) {
|
|
188
|
+
// A module reachable only through type-only edges (e.g. a directory specifier
|
|
189
|
+
// behind `import type`) can fail to resolve as an ES module. That's safe to
|
|
190
|
+
// ignore: the runtime never loads it.
|
|
191
|
+
if (!runtimeSpecifiers.has(module.specifier)) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (module.error.startsWith('Module not found')) {
|
|
195
|
+
// Module graph contains all found imported/required modules, even if they don't
|
|
196
|
+
// actually exist. This can happen for optional dependencies (dynamic import or
|
|
197
|
+
// require in try/catch).
|
|
174
198
|
continue;
|
|
175
199
|
}
|
|
176
|
-
const filePath = fileURLToPath(module.specifier);
|
|
177
|
-
localFiles.add(filePath);
|
|
178
200
|
}
|
|
201
|
+
localFiles.add(fileURLToPath(module.specifier));
|
|
179
202
|
}
|
|
180
203
|
}
|
|
181
204
|
return localFiles;
|
|
182
205
|
}
|
|
206
|
+
// WebAssembly binary magic bytes: `\0asm` (0x00 0x61 0x73 0x6d).
|
|
207
|
+
const WASM_MAGIC = Buffer.from([0x00, 0x61, 0x73, 0x6d]);
|
|
208
|
+
/**
|
|
209
|
+
* Detects whether a file is a raw WebAssembly module by its magic bytes.
|
|
210
|
+
* Deno <2.6 vendors imported `.wasm` modules under a `.d.mts` extension even
|
|
211
|
+
* though the content is the raw WASM binary, so we cannot rely on extension
|
|
212
|
+
* alone to decide whether a file is safe to read as UTF-8 and rewrite.
|
|
213
|
+
*/
|
|
214
|
+
async function isWasm(sourceFile) {
|
|
215
|
+
const fd = await fs.open(sourceFile, 'r');
|
|
216
|
+
try {
|
|
217
|
+
const buf = Buffer.alloc(WASM_MAGIC.length);
|
|
218
|
+
const { bytesRead } = await fd.read(buf, 0, buf.length, 0);
|
|
219
|
+
return bytesRead === WASM_MAGIC.length && buf.equals(WASM_MAGIC);
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
await fd.close();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Decides whether a source file should be parsed and rewritten. Cheap extension
|
|
227
|
+
* check first; only if it passes do we open the file to rule out raw WebAssembly
|
|
228
|
+
* binaries (Deno <2.6 vendors `.wasm` imports under `.d.mts`) — reading those
|
|
229
|
+
* as UTF-8 would corrupt the binary on round-trip.
|
|
230
|
+
*/
|
|
231
|
+
async function shouldRewrite(sourceFile) {
|
|
232
|
+
if (!REWRITABLE_EXTENSIONS.has(path.extname(sourceFile))) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
if (await isWasm(sourceFile)) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
183
240
|
/**
|
|
184
241
|
* Rewrites import assert into import with in the bundle directory
|
|
185
242
|
* Defaults to copying the file in its current form
|
|
186
243
|
*/
|
|
187
244
|
export async function rewriteImportAssertions(sourceFile, destPath) {
|
|
188
|
-
if (!
|
|
245
|
+
if (!(await shouldRewrite(sourceFile))) {
|
|
189
246
|
if (sourceFile !== destPath) {
|
|
190
247
|
await fs.copyFile(sourceFile, destPath);
|
|
191
248
|
}
|
|
192
249
|
return;
|
|
193
250
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
251
|
+
try {
|
|
252
|
+
const source = await fs.readFile(sourceFile, 'utf-8');
|
|
253
|
+
const modified = rewriteSourceImportAssertions(source);
|
|
254
|
+
await fs.writeFile(destPath, modified);
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
throw new Error(`Failed to rewrite import assertions in ${sourceFile}`, { cause: error });
|
|
258
|
+
}
|
|
197
259
|
}
|
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
import { Parser } from 'acorn';
|
|
2
2
|
import { tsPlugin } from '@sveltejs/acorn-typescript';
|
|
3
|
-
const
|
|
3
|
+
const acornNoJSX = Parser.extend(tsPlugin({ jsx: false }));
|
|
4
|
+
const acornJSX = Parser.extend(tsPlugin({ jsx: true }));
|
|
5
|
+
const parseOptions = {
|
|
6
|
+
ecmaVersion: 'latest',
|
|
7
|
+
sourceType: 'module',
|
|
8
|
+
locations: true,
|
|
9
|
+
};
|
|
10
|
+
const parseAST = (source) => {
|
|
11
|
+
try {
|
|
12
|
+
return acornJSX.parse(source, parseOptions);
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
// for non-jsx typescript casting to type via "<type> value" (normally done with "value as type") will throw an "Unexpected token" error in acorn-jsx,
|
|
16
|
+
// but is valid syntax in TypeScript. In this case, we can retry parsing with the non-jsx parser.
|
|
17
|
+
if (error instanceof SyntaxError) {
|
|
18
|
+
return acornNoJSX.parse(source, parseOptions);
|
|
19
|
+
}
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
4
23
|
/**
|
|
5
24
|
* Given source code rewrites import assert into import with
|
|
6
25
|
*/
|
|
@@ -10,11 +29,7 @@ export function rewriteSourceImportAssertions(source) {
|
|
|
10
29
|
}
|
|
11
30
|
let modified = source;
|
|
12
31
|
try {
|
|
13
|
-
const parsedAST =
|
|
14
|
-
ecmaVersion: 'latest',
|
|
15
|
-
sourceType: 'module',
|
|
16
|
-
locations: true,
|
|
17
|
-
});
|
|
32
|
+
const parsedAST = parseAST(source);
|
|
18
33
|
const statements = collectImportAssertions(source, parsedAST.body);
|
|
19
34
|
// Bulk replacement of import assertions
|
|
20
35
|
for (const statement of statements.sort((a, b) => b.start - a.start)) {
|
package/dist/test/util.js
CHANGED
|
@@ -49,16 +49,18 @@ const inspectESZIPFunction = (path) => `
|
|
|
49
49
|
console.log(JSON.stringify(responses));
|
|
50
50
|
`;
|
|
51
51
|
const inspectTarballFunction = () => `
|
|
52
|
-
import path from "node:path";
|
|
53
|
-
import { pathToFileURL } from "node:url";
|
|
54
52
|
import manifest from "./___netlify-edge-functions.json" with { type: "json" };
|
|
55
53
|
|
|
56
54
|
const responses = {};
|
|
57
55
|
|
|
58
56
|
for (const functionName in manifest.functions) {
|
|
59
57
|
const req = new Request("https://test.netlify");
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
// Import via a relative specifier so Deno resolves the module URL itself,
|
|
59
|
+
// keeping its encoding consistent with the import map base (both derived from
|
|
60
|
+
// cwd). Pre-building an absolute file:// URL on the Node side encodes paths
|
|
61
|
+
// differently from Deno (e.g. '~' in Windows 8.3 short names), which breaks
|
|
62
|
+
// import map prefix matching on Deno 2.8+.
|
|
63
|
+
const func = await import("./" + manifest.functions[functionName]);
|
|
62
64
|
const res = await func.default(req);
|
|
63
65
|
|
|
64
66
|
responses[functionName] = await res.text();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlify/edge-bundler",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "15.0.0",
|
|
4
4
|
"description": "Intelligently prepare Netlify Edge Functions for deployment",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/node/index.js",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"vitest": "^3.0.0"
|
|
56
56
|
},
|
|
57
57
|
"engines": {
|
|
58
|
-
"node": ">=
|
|
58
|
+
"node": ">=22.12.0"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@import-maps/resolve": "^2.0.0",
|
|
@@ -80,5 +80,5 @@
|
|
|
80
80
|
"tmp-promise": "^3.0.3",
|
|
81
81
|
"urlpattern-polyfill": "8.0.2"
|
|
82
82
|
},
|
|
83
|
-
"gitHead": "
|
|
83
|
+
"gitHead": "bdaf7cd5e14049c31a236c9432f192f78c6c31c8"
|
|
84
84
|
}
|