Skip to content

Commit

Permalink
Fix ESM loader to work with Node 20
Browse files Browse the repository at this point in the history
`process.send` is no longer accessible from ESM loader in Node 20, therefore loader needs to use context.port and a proxy function to send messages to the parent. See https://nodejs.org/api/esm.html#globalpreload
  • Loading branch information
HitkoDev committed Sep 15, 2023
1 parent 9ab4eb6 commit 8f97932
Show file tree
Hide file tree
Showing 6 changed files with 23 additions and 24 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ jobs:
script: npm run lint
language: node_js
node_js:
- 20
- 18
- 16
- 14
Expand Down
4 changes: 4 additions & 0 deletions lib/loaders/ipc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@ const cmd = 'NODE_DEV';
export const send = m => {
if (process.connected) process.send({ ...m, cmd });
};

export const sendPort = (port, m) => {
if (port) port.postMessage({ ...m, cmd });
};
20 changes: 18 additions & 2 deletions lib/loaders/load.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { createRequire } from 'module';
import { fileURLToPath } from 'url';
import { send } from './ipc.mjs';
import { sendPort } from './ipc.mjs';

const require = createRequire(import.meta.url);

// Port used for communication between the loader and ESM modules
// https://nodejs.org/api/esm.html#globalpreload
let port;

export async function load(url, context, defaultLoad) {
const required = url.startsWith('file://') ? fileURLToPath(url) : url;

send({ required });
sendPort(port, { required });

try {
return await defaultLoad(url, context, defaultLoad);
Expand All @@ -19,3 +23,15 @@ export async function load(url, context, defaultLoad) {
});
}
}

export const globalPreload = (context) => {
// Store port
port = context.port;

// Inject code to forward loader events to the parent
return `
port.onmessage = (evt) => {
if (process.connected) process.send(evt.data);
};
`;
};
1 change: 0 additions & 1 deletion test/fixture/experimental-specifier-resolution/index.mjs

This file was deleted.

7 changes: 0 additions & 7 deletions test/fixture/resolution.mjs

This file was deleted.

14 changes: 0 additions & 14 deletions test/spawn/esmodule.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,6 @@ const tap = require('tap');

const { spawn, touchFile } = require('../utils');

tap.test('Supports ECMAScript modules with experimental-specifier-resolution', t => {
spawn('--experimental-specifier-resolution=node resolution.mjs', out => {
if (out.match(/touch message.js/)) {
touchFile('message.js');
return out2 => {
if (out2.match(/Restarting/)) {
t.match(out2, /\[INFO\] \d{2}:\d{2}:\d{2} Restarting/);
return { exit: t.end.bind(t) };
}
};
}
});
});

tap.test('Supports ECMAScript modules', t => {
spawn('ecma-script-modules.mjs', out => {
if (out.match(/touch message.mjs/)) {
Expand Down

0 comments on commit 8f97932

Please sign in to comment.