Skip to content

Commit afdb4ac

Browse files
authoredJul 29, 2024··
fix: handle non node: prefixed Node.js builtins when no npm specifiers were found (#5785)
* test: add test case using node builtin import without node: prefix * fix: handle non node: prefixed Node.js builtins when no npm specifiers were found * test: update snapshots (expanded importMapData)
1 parent 0313f24 commit afdb4ac

File tree

7 files changed

+100
-45
lines changed

7 files changed

+100
-45
lines changed
 

‎packages/build/src/error/monitor/normalize.js

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ const normalizeMessage = function (message, [regExp, replacement]) {
4141
const NORMALIZE_REGEXPS = [
4242
// Base64 URL
4343
[/(data:[^;]+;base64),[\w+/-=]+/g, 'dataURI'],
44+
// Node builtins mapping - normalize it to single one so it's not dependent on Node.js version it did run on
45+
[/(\\"[^"]+\\":\\"node:[^"]+\\",)+/g, '\\"builtins\\":\\"node:builtins\\",'],
4446
// File paths
4547
[/(["'`, ]|^)([^"'`, \n]*[/\\][^"'`, \n]*)(?=["'`, ]|$)/gm, '$1/file/path'],
4648
// Semantic versions

‎packages/build/tests/monitor/snapshots/tests.js.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -1620,7 +1620,7 @@ Generated by [AVA](https://avajs.dev).
16201620
────────────────────────────────────────────────────────────────␊
16211621
16221622
Error message␊
1623-
Command failed with exit code 1: deno run --allow-all --no-config --import-map=packages/edge-bundler/deno/vendor/import_map.json --quiet packages/edge-bundler/deno/bundle.ts {"basePath":"packages/build/tests/monitor/fixtures/edge_function_error","destPath":"packages/build/tests/monitor/fixtures/edge_function_error/.netlify/edge-functions-dist/HEXADECIMAL_ID.eszip","externals":[],"functions":[{"name":"trouble","path":"packages/build/tests/monitor/fixtures/edge_function_error/netlify/edge-functions/trouble.ts"}],"importMapData":"{/"imports/":{/"@netlify/edge-functions/":/"https://edge.netlify.com/v1.0.0/index.ts/",/"netlify:edge/":/"https://edge.netlify.com/v1.0.0/index.ts?v=legacy/"},/"scopes/":{}}"}␊
1623+
Command failed with exit code 1: deno run --allow-all --no-config --import-map=packages/edge-bundler/deno/vendor/import_map.json --quiet packages/edge-bundler/deno/bundle.ts {"basePath":"packages/build/tests/monitor/fixtures/edge_function_error","destPath":"packages/build/tests/monitor/fixtures/edge_function_error/.netlify/edge-functions-dist/HEXADECIMAL_ID.eszip","externals":[],"functions":[{"name":"trouble","path":"packages/build/tests/monitor/fixtures/edge_function_error/netlify/edge-functions/trouble.ts"}],"importMapData":"{/"imports/":{/"builtins/":/"node:builtins/",/"@netlify/edge-functions/":/"https://edge.netlify.com/v1.0.0/index.ts/",/"netlify:edge/":/"https://edge.netlify.com/v1.0.0/index.ts?v=legacy/"},/"scopes/":{}}","vendorDirectory":"/external/path"}␊
16241624
error: Uncaught (in promise) Error: Error: Could not find file: packages/build/tests/monitor/fixtures/edge_function_error/netlify/edge-functions/file.ts␊
16251625
const ret = new Error(getStringFromWasm0(arg0, arg1));␊
16261626
^␊
@@ -1638,9 +1638,9 @@ Generated by [AVA](https://avajs.dev).
16381638
Error monitoring payload:␊
16391639
{␊
16401640
"errorClass": "functionsBundling",␊
1641-
"errorMessage": "Command failed with exit code 1: deno run --allow-all --no-config --import-map=packages/edge-bundler/deno/vendor/import_map.json --quiet packages/edge-bundler/deno/bundle.ts {/"basePath/":/"packages/build/tests/monitor/fixtures/edge_function_error",/"destPath/":/"packages/build/tests/monitor/fixtures/edge_function_error/.netlify/edge-functions-dist/HEXADECIMAL_ID.eszip",/"externals/":[],/"functions/":[{/"name/":/"trouble/",/"path/":/"packages/build/tests/monitor/fixtures/edge_function_error/netlify/edge-functions/trouble.ts"}],/"importMapData/":/"{//"imports//":{//"@netlify/edge-functions//"://"https://edge.netlify.com/v1.0.0/index.ts//",//"netlify:edge//"://"https://edge.netlify.com/v1.0.0/index.ts?v=legacy//"},//"scopes//":{}}/"}/nerror: Uncaught (in promise) Error: Error: Could not find file: packages/build/tests/monitor/fixtures/edge_function_error/netlify/edge-functions/file.ts/n const ret = new Error(getStringFromWasm0(arg0, arg1));/n ^/n at __wbg_new_HEXADECIMAL_ID (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm.generated.js:80:80)/n at <anonymous> (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm_bg.wasm:1:80)/n at <anonymous> (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm_bg.wasm:1:80)/n at <anonymous> (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm_bg.wasm:1:80)/n at __wbg_adapter_40 (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm.generated.js:80:8)/n at real (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm.generated.js:80:80)/n at eventLoopTick (ext:core/01_core.js:80:7)",␊
1641+
"errorMessage": "Command failed with exit code 1: deno run --allow-all --no-config --import-map=packages/edge-bundler/deno/vendor/import_map.json --quiet packages/edge-bundler/deno/bundle.ts {/"basePath/":/"packages/build/tests/monitor/fixtures/edge_function_error",/"destPath/":/"packages/build/tests/monitor/fixtures/edge_function_error/.netlify/edge-functions-dist/HEXADECIMAL_ID.eszip",/"externals/":[],/"functions/":[{/"name/":/"trouble/",/"path/":/"packages/build/tests/monitor/fixtures/edge_function_error/netlify/edge-functions/trouble.ts"}],/"importMapData/":/"{//"imports//":{//"builtins//"://"node:builtins//",//"@netlify/edge-functions//"://"https://edge.netlify.com/v1.0.0/index.ts//",//"netlify:edge//"://"https://edge.netlify.com/v1.0.0/index.ts?v=legacy//"},//"scopes//":{}}/",/"vendorDirectory/":/"/external/path"}/nerror: Uncaught (in promise) Error: Error: Could not find file: packages/build/tests/monitor/fixtures/edge_function_error/netlify/edge-functions/file.ts/n const ret = new Error(getStringFromWasm0(arg0, arg1));/n ^/n at __wbg_new_HEXADECIMAL_ID (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm.generated.js:80:80)/n at <anonymous> (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm_bg.wasm:1:80)/n at <anonymous> (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm_bg.wasm:1:80)/n at <anonymous> (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm_bg.wasm:1:80)/n at __wbg_adapter_40 (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm.generated.js:80:8)/n at real (packages/edge-bundler/deno/vendor/deno.land/x/eszip@v1.0.0/eszip_wasm.generated.js:80:80)/n at eventLoopTick (ext:core/01_core.js:80:7)",␊
16421642
"context": "Bundling of edge function failed",␊
1643-
"groupingHash": "Bundling of edge function failed/nCommand failed with exit code 0: deno run --allow-all --no-config /external/path --quiet /external/path {/"/":/"/",/"/":/"/",/"/":[],/"/":[{/"/":/"/",/"/":/"/"}],/"/":/"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"}/nerror: Uncaught (in promise) Error: Error: Could not find file: /external/path const ret = new Error(getStringFromWasm0(arg0, arg0));/n ^/n at __wbg_new_hex /external/path at <anonymous> /external/path at <anonymous> /external/path at <anonymous> /external/path at __wbg_adapter_0 /external/path at real /external/path at eventLoopTick /external/path",␊
1643+
"groupingHash": "Bundling of edge function failed/nCommand failed with exit code 0: deno run --allow-all --no-config /external/path --quiet /external/path {/"/":/"/",/"/":/"/",/"/":[],/"/":[{/"/":/"/",/"/":/"/"}],/"/":/"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/",/"/":/"/"}/nerror: Uncaught (in promise) Error: Error: Could not find file: /external/path const ret = new Error(getStringFromWasm0(arg0, arg0));/n ^/n at __wbg_new_hex /external/path at <anonymous> /external/path at <anonymous> /external/path at <anonymous> /external/path at __wbg_adapter_0 /external/path at real /external/path at eventLoopTick /external/path",␊
16441644
"severity": "info",␊
16451645
"unhandled": false,␊
16461646
"location": {␊
@@ -1651,7 +1651,7 @@ Generated by [AVA](https://avajs.dev).
16511651
"pluginPackageJson": false,␊
16521652
"BUILD_ID": "0",␊
16531653
"other": {␊
1654-
"groupingHash": "Bundling of edge function failed/nCommand failed with exit code 0: deno run --allow-all --no-config /external/path --quiet /external/path {/"/":/"/",/"/":/"/",/"/":[],/"/":[{/"/":/"/",/"/":/"/"}],/"/":/"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"}/nerror: Uncaught (in promise) Error: Error: Could not find file: /external/path const ret = new Error(getStringFromWasm0(arg0, arg0));/n ^/n at __wbg_new_hex /external/path at <anonymous> /external/path at <anonymous> /external/path at <anonymous> /external/path at __wbg_adapter_0 /external/path at real /external/path at eventLoopTick /external/path"␊
1654+
"groupingHash": "Bundling of edge function failed/nCommand failed with exit code 0: deno run --allow-all --no-config /external/path --quiet /external/path {/"/":/"/",/"/":/"/",/"/":[],/"/":[{/"/":/"/",/"/":/"/"}],/"/":/"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/"/external/path"/",/"/":/"/"}/nerror: Uncaught (in promise) Error: Error: Could not find file: /external/path const ret = new Error(getStringFromWasm0(arg0, arg0));/n ^/n at __wbg_new_hex /external/path at <anonymous> /external/path at <anonymous> /external/path at <anonymous> /external/path at __wbg_adapter_0 /external/path at real /external/path at eventLoopTick /external/path"␊
16551655
}␊
16561656
}`
16571657

Binary file not shown.

‎packages/edge-bundler/node/bundler.test.ts

+36
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,42 @@ test('Handles imports with the `node:` prefix', async () => {
437437
await cleanup()
438438
})
439439

440+
test('Handles Node builtin imports without the `node:` prefix', async () => {
441+
const { basePath, cleanup, distPath } = await useFixture('imports_node_builtin')
442+
const userDirectory = join(basePath, 'netlify', 'edge-functions')
443+
const result = await bundle([userDirectory], distPath, [], {
444+
basePath,
445+
importMapPaths: [join(userDirectory, 'import_map.json')],
446+
})
447+
const generatedFiles = await readdir(distPath)
448+
449+
expect(result.functions.length).toBe(1)
450+
expect(generatedFiles.length).toBe(2)
451+
452+
const manifestFile = await readFile(resolve(distPath, 'manifest.json'), 'utf8')
453+
const manifest = JSON.parse(manifestFile)
454+
455+
expect(() => validateManifest(manifest)).not.toThrowError()
456+
457+
const { bundles, import_map: importMapURL, routes } = manifest
458+
459+
expect(bundles.length).toBe(1)
460+
expect(bundles[0].format).toBe('eszip2')
461+
expect(generatedFiles.includes(bundles[0].asset)).toBe(true)
462+
expect(importMapURL).toBe(importMapSpecifier)
463+
expect(routes.length).toBe(1)
464+
expect(routes[0].function).toBe('func1')
465+
expect(routes[0].pattern).toBe('^/func1/?$')
466+
467+
const bundlePath = join(distPath, bundles[0].asset)
468+
469+
const { func1 } = await runESZIP(bundlePath)
470+
471+
expect(func1).toBe('ok')
472+
473+
await cleanup()
474+
})
475+
440476
test('Loads npm modules from bare specifiers', async () => {
441477
const systemLogger = vi.fn()
442478
const { basePath, cleanup, distPath } = await useFixture('imports_npm_module')

‎packages/edge-bundler/node/npm_dependencies.ts

+43-41
Original file line numberDiff line numberDiff line change
@@ -239,11 +239,6 @@ export const vendorNPMSpecifiers = async ({
239239
rootPath,
240240
})
241241

242-
// If we found no specifiers, there's nothing left to do here.
243-
if (Object.keys(npmSpecifiers).length === 0) {
244-
return
245-
}
246-
247242
// To bundle an entire module and all its dependencies, create a entrypoint file
248243
// where we re-export everything from that specifier. We do this for every
249244
// specifier, and each of these files will become entry points to esbuild.
@@ -257,42 +252,49 @@ export const vendorNPMSpecifiers = async ({
257252
return { filePath, specifier, types }
258253
}),
259254
)
260-
const entryPoints = ops.map(({ filePath }) => filePath)
261-
// Bundle each of the entrypoints we created. We'll end up with a compiled
262-
// version of each, plus any chunks of shared code
263-
// between them (such that a common module isn't bundled twice).
264-
const { outputFiles } = await build({
265-
allowOverwrite: true,
266-
banner,
267-
bundle: true,
268-
entryPoints,
269-
format: 'esm',
270-
mainFields: ['module', 'browser', 'main'],
271-
logLevel: 'error',
272-
nodePaths,
273-
outdir: temporaryDirectory.path,
274-
platform: 'node',
275-
splitting: true,
276-
target: 'es2020',
277-
write: false,
278-
define:
279-
environment === 'production'
280-
? {
281-
'process.env.NODE_ENV': '"production"',
282-
}
283-
: undefined,
284-
})
285255

286-
await Promise.all(
287-
outputFiles.map(async (file) => {
288-
const types = ops.find((op) => path.basename(file.path) === path.basename(op.filePath))?.types
289-
let content = file.text
290-
if (types) {
291-
content = `/// <reference types="${path.relative(path.dirname(file.path), types)}" />\n${content}`
292-
}
293-
await fs.writeFile(file.path, content)
294-
}),
295-
)
256+
const outputFiles: string[] = []
257+
258+
if (ops.length !== 0) {
259+
const entryPoints = ops.map(({ filePath }) => filePath)
260+
// Bundle each of the entrypoints we created. We'll end up with a compiled
261+
// version of each, plus any chunks of shared code
262+
// between them (such that a common module isn't bundled twice).
263+
const { outputFiles: outputFilesFromEsBuild } = await build({
264+
allowOverwrite: true,
265+
banner,
266+
bundle: true,
267+
entryPoints,
268+
format: 'esm',
269+
mainFields: ['module', 'browser', 'main'],
270+
logLevel: 'error',
271+
nodePaths,
272+
outdir: temporaryDirectory.path,
273+
platform: 'node',
274+
splitting: true,
275+
target: 'es2020',
276+
write: false,
277+
define:
278+
environment === 'production'
279+
? {
280+
'process.env.NODE_ENV': '"production"',
281+
}
282+
: undefined,
283+
})
284+
285+
outputFiles.push(...outputFilesFromEsBuild.map((file) => file.path))
286+
287+
await Promise.all(
288+
outputFilesFromEsBuild.map(async (file) => {
289+
const types = ops.find((op) => path.basename(file.path) === path.basename(op.filePath))?.types
290+
let content = file.text
291+
if (types) {
292+
content = `/// <reference types="${path.relative(path.dirname(file.path), types)}" />\n${content}`
293+
}
294+
await fs.writeFile(file.path, content)
295+
}),
296+
)
297+
}
296298

297299
// Add all Node.js built-ins to the import map, so any unprefixed specifiers
298300
// (e.g. `process`) resolve to the prefixed versions (e.g. `node:prefix`),
@@ -340,6 +342,6 @@ export const vendorNPMSpecifiers = async ({
340342
directory: temporaryDirectory.path,
341343
importMap: newImportMap,
342344
npmSpecifiersWithExtraneousFiles,
343-
outputFiles: outputFiles.map((file) => file.path),
345+
outputFiles,
344346
}
345347
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import assert from 'assert'
2+
import process from 'process'
3+
4+
export default () => {
5+
Deno.env.set('NETLIFY_TEST', '12345')
6+
assert.deepEqual(process.env.NETLIFY_TEST, '12345')
7+
8+
return new Response('ok')
9+
}
10+
11+
export const config = {
12+
path: '/func1',
13+
}

‎packages/testing/src/normalize.ts

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ const NORMALIZE_REGEXPS = [
2424
[new RegExp(figures.pointer, 'g'), '>'],
2525
[new RegExp(figures.arrowDown, 'g'), '↓'],
2626
[//gu, '‼'],
27+
[/(\/\/"[^"]+\/\/":\/\/"node:[^"]+\/\/",)+/g, '//"builtins//"://"node:builtins//",'],
28+
[/(\/"[^"]+\/":\/"node:[^"]+\/",)+/g, '/"builtins/":/"node:builtins/",'],
2729
// A bug in nyc (https://github.com/istanbuljs/istanbuljs/issues/141) is
2830
// creating those error messages on Windows. This happens randomly and
2931
// seldomly. This might be fixed by nyc@15

0 commit comments

Comments
 (0)
Please sign in to comment.