Skip to content

Commit

Permalink
Node compat (#11675)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
Rich-Harris and Rich-Harris authored Jan 19, 2024
1 parent ada5959 commit df381db
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 14 deletions.
5 changes: 5 additions & 0 deletions .changeset/chatty-pandas-swim.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/adapter-vercel': minor
---

feat: allow compatible subset of Node.js built-in modules when targeting edge functions
5 changes: 5 additions & 0 deletions .changeset/two-pianos-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/adapter-netlify': minor
---

feat: allow Node.js built-in modules when targeting edge functions
8 changes: 7 additions & 1 deletion packages/adapter-netlify/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { appendFileSync, existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
import { dirname, join, resolve, posix } from 'node:path';
import { fileURLToPath } from 'node:url';
import { builtinModules } from 'node:module';
import esbuild from 'esbuild';
import toml from '@iarna/toml';

Expand Down Expand Up @@ -165,7 +166,12 @@ async function generate_edge_functions({ builder }) {
format: 'esm',
platform: 'browser',
sourcemap: 'linked',
target: 'es2020'
target: 'es2020',

// Node built-ins are allowed, but must be prefixed with `node:`
// https://docs.netlify.com/edge-functions/api/#runtime-environment
external: builtinModules.map((id) => `node:${id}`),
alias: Object.fromEntries(builtinModules.map((id) => [id, `node:${id}`]))
});

writeFileSync('.netlify/edge-functions/manifest.json', JSON.stringify(edge_manifest));
Expand Down
70 changes: 57 additions & 13 deletions packages/adapter-vercel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ const get_default_runtime = () => {
);
};

// https://vercel.com/docs/functions/edge-functions/edge-runtime#compatible-node.js-modules
const compatible_node_modules = ['async_hooks', 'events', 'buffer', 'assert', 'util'];

/** @type {import('.').default} **/
const plugin = function (defaults = {}) {
if ('edge' in defaults) {
Expand Down Expand Up @@ -109,20 +112,61 @@ const plugin = function (defaults = {}) {
`export const manifest = ${builder.generateManifest({ relativePath, routes })};\n`
);

await esbuild.build({
entryPoints: [`${tmp}/edge.js`],
outfile: `${dirs.functions}/${name}.func/index.js`,
target: 'es2020', // TODO verify what the edge runtime supports
bundle: true,
platform: 'browser',
format: 'esm',
external: config.external,
sourcemap: 'linked',
banner: { js: 'globalThis.global = globalThis;' },
loader: {
'.wasm': 'copy'
try {
const result = await esbuild.build({
entryPoints: [`${tmp}/edge.js`],
outfile: `${dirs.functions}/${name}.func/index.js`,
target: 'es2020', // TODO verify what the edge runtime supports
bundle: true,
platform: 'browser',
format: 'esm',
external: [
...compatible_node_modules,
...compatible_node_modules.map((id) => `node:${id}`),
...(config.external || [])
],
sourcemap: 'linked',
banner: { js: 'globalThis.global = globalThis;' },
loader: {
'.wasm': 'copy'
}
});

if (result.warnings.length > 0) {
const formatted = await esbuild.formatMessages(result.warnings, {
kind: 'warning',
color: true
});

console.error(formatted.join('\n'));
}
});
} catch (error) {
for (const e of error.errors) {
for (const node of e.notes) {
const match =
/The package "(.+)" wasn't found on the file system but is built into node/.exec(
node.text
);

if (match) {
node.text = `Cannot use "${match[1]}" when deploying to Vercel Edge Functions.`;
}
}
}

const formatted = await esbuild.formatMessages(error.errors, {
kind: 'error',
color: true
});

console.error(formatted.join('\n'));

throw new Error(
`Bundling with esbuild failed with ${error.errors.length} ${
error.errors.length === 1 ? 'error' : 'errors'
}`
);
}

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

0 comments on commit df381db

Please sign in to comment.