Skip to content

Commit df381db

Browse files
authored
Node compat (#11675)
* allow compatible node modules when targeting vercel edge functions * test * confirmed it works, deleting test case * allow node built-ins when creating edge functions * changesets --------- Co-authored-by: Rich Harris <rich.harris@vercel.com>
1 parent ada5959 commit df381db

File tree

4 files changed

+74
-14
lines changed

4 files changed

+74
-14
lines changed

.changeset/chatty-pandas-swim.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/adapter-vercel': minor
3+
---
4+
5+
feat: allow compatible subset of Node.js built-in modules when targeting edge functions

.changeset/two-pianos-obey.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/adapter-netlify': minor
3+
---
4+
5+
feat: allow Node.js built-in modules when targeting edge functions

packages/adapter-netlify/index.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { appendFileSync, existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
22
import { dirname, join, resolve, posix } from 'node:path';
33
import { fileURLToPath } from 'node:url';
4+
import { builtinModules } from 'node:module';
45
import esbuild from 'esbuild';
56
import toml from '@iarna/toml';
67

@@ -165,7 +166,12 @@ async function generate_edge_functions({ builder }) {
165166
format: 'esm',
166167
platform: 'browser',
167168
sourcemap: 'linked',
168-
target: 'es2020'
169+
target: 'es2020',
170+
171+
// Node built-ins are allowed, but must be prefixed with `node:`
172+
// https://docs.netlify.com/edge-functions/api/#runtime-environment
173+
external: builtinModules.map((id) => `node:${id}`),
174+
alias: Object.fromEntries(builtinModules.map((id) => [id, `node:${id}`]))
169175
});
170176

171177
writeFileSync('.netlify/edge-functions/manifest.json', JSON.stringify(edge_manifest));

packages/adapter-vercel/index.js

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ const get_default_runtime = () => {
1818
);
1919
};
2020

21+
// https://vercel.com/docs/functions/edge-functions/edge-runtime#compatible-node.js-modules
22+
const compatible_node_modules = ['async_hooks', 'events', 'buffer', 'assert', 'util'];
23+
2124
/** @type {import('.').default} **/
2225
const plugin = function (defaults = {}) {
2326
if ('edge' in defaults) {
@@ -109,20 +112,61 @@ const plugin = function (defaults = {}) {
109112
`export const manifest = ${builder.generateManifest({ relativePath, routes })};\n`
110113
);
111114

112-
await esbuild.build({
113-
entryPoints: [`${tmp}/edge.js`],
114-
outfile: `${dirs.functions}/${name}.func/index.js`,
115-
target: 'es2020', // TODO verify what the edge runtime supports
116-
bundle: true,
117-
platform: 'browser',
118-
format: 'esm',
119-
external: config.external,
120-
sourcemap: 'linked',
121-
banner: { js: 'globalThis.global = globalThis;' },
122-
loader: {
123-
'.wasm': 'copy'
115+
try {
116+
const result = await esbuild.build({
117+
entryPoints: [`${tmp}/edge.js`],
118+
outfile: `${dirs.functions}/${name}.func/index.js`,
119+
target: 'es2020', // TODO verify what the edge runtime supports
120+
bundle: true,
121+
platform: 'browser',
122+
format: 'esm',
123+
external: [
124+
...compatible_node_modules,
125+
...compatible_node_modules.map((id) => `node:${id}`),
126+
...(config.external || [])
127+
],
128+
sourcemap: 'linked',
129+
banner: { js: 'globalThis.global = globalThis;' },
130+
loader: {
131+
'.wasm': 'copy'
132+
}
133+
});
134+
135+
if (result.warnings.length > 0) {
136+
const formatted = await esbuild.formatMessages(result.warnings, {
137+
kind: 'warning',
138+
color: true
139+
});
140+
141+
console.error(formatted.join('\n'));
124142
}
125-
});
143+
} catch (error) {
144+
for (const e of error.errors) {
145+
for (const node of e.notes) {
146+
const match =
147+
/The package "(.+)" wasn't found on the file system but is built into node/.exec(
148+
node.text
149+
);
150+
151+
if (match) {
152+
node.text = `Cannot use "${match[1]}" when deploying to Vercel Edge Functions.`;
153+
}
154+
}
155+
}
156+
157+
const formatted = await esbuild.formatMessages(error.errors, {
158+
kind: 'error',
159+
color: true
160+
});
161+
162+
console.error(formatted.join('\n'));
163+
164+
throw new Error(
165+
`Bundling with esbuild failed with ${error.errors.length} ${
166+
error.errors.length === 1 ? 'error' : 'errors'
167+
}`
168+
);
169+
}
126170

127171
write(
128172
`${dirs.functions}/${name}.func/.vc-config.json`,

0 commit comments

Comments
 (0)