Skip to content

Commit c64510f

Browse files
authored
[ESM] always load the ESM loader (#279)
* [ESM] always load the ESM loader * Enable esmodule tests on Windows * Suppress ExperimentalWarning messages
1 parent befe479 commit c64510f

11 files changed

Lines changed: 74 additions & 71 deletions

lib/get-source-loader.mjs

Lines changed: 0 additions & 6 deletions
This file was deleted.

lib/hook.js

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const vm = require('vm');
22

3-
module.exports = (patchVM, wrapper, callback) => {
3+
module.exports = (patchVM, callback) => {
44
// Hook into Node's `require(...)`
55
updateHooks();
66

@@ -48,12 +48,6 @@ module.exports = (patchVM, wrapper, callback) => {
4848
*/
4949
function createHook(handler) {
5050
return function nodeDevHook(module, filename) {
51-
if (module.parent === wrapper) {
52-
// If the main module is required conceal the wrapper
53-
module.id = '.';
54-
module.parent = null;
55-
process.mainModule = module;
56-
}
5751
if (!module.loaded) callback(module.filename);
5852

5953
// Invoke the original handler

lib/index.js

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
const { fork } = require('child_process');
22
const filewatcher = require('filewatcher');
3-
const { extname } = require('path');
43
const semver = require('semver');
5-
const getPackageType = require('get-package-type');
4+
const { pathToFileURL } = require('url');
65

76
const { clearFactory } = require('./clear');
87
const { configureDeps, configureIgnore } = require('./ignore');
@@ -55,8 +54,6 @@ module.exports = function (
5554
const isIgnored = configureIgnore(ignore);
5655
const isTooDeep = configureDeps(deps);
5756

58-
const wrapper = resolveMain(localPath('wrap.js'));
59-
6057
// Run ./dedupe.js as preload script
6158
if (dedupe) process.env.NODE_DEV_PRELOAD = localPath('dedupe');
6259

@@ -96,27 +93,24 @@ module.exports = function (
9693

9794
const args = nodeArgs.slice();
9895

99-
if (extname(script) === '.mjs' || getPackageType.sync(script) === 'module') {
100-
if (semver.satisfies(process.version, '>=10 <12.11.1')) {
101-
const resolveLoader = resolveMain(localPath('resolve-loader.mjs'));
102-
args.push('--experimental-modules', `--loader=${resolveLoader}`);
103-
} else if (semver.satisfies(process.version, '>=12.11.1')) {
104-
if (semver.satisfies(process.version, '<12.17.0')) {
105-
args.push('--experimental-modules');
106-
}
107-
const loaderPath = semver.satisfies(process.version, '>=16.12.0')
108-
? 'load-loader.mjs'
109-
: 'get-source-loader.mjs';
110-
const experimentalLoader = resolveMain(localPath(loaderPath));
111-
args.push(`--experimental-loader=${experimentalLoader}`);
112-
}
96+
args.push(`--require=${resolveMain(localPath('wrap'))}`);
97+
const loaderPath = semver.satisfies(process.version, '<16.12.0')
98+
? 'loader-legacy.mjs'
99+
: 'loader.mjs';
100+
const loader = pathToFileURL(resolveMain(localPath(loaderPath)));
101+
if (semver.satisfies(process.version, '<12.17.0')) {
102+
args.push('--experimental-modules');
103+
}
104+
if (semver.satisfies(process.version, '<12.11.1')) {
105+
args.push(`--loader=${loader.href}`);
106+
} else {
107+
args.push(`--experimental-loader=${loader.href}`);
113108
}
114109

115-
const cmd = args.concat(wrapper, script, scriptArgs);
116-
117-
child = fork(cmd[0], cmd.slice(1), {
110+
child = fork(script, scriptArgs, {
118111
cwd: process.cwd(),
119-
env: process.env
112+
env: process.env,
113+
execArgv: args
120114
});
121115

122116
if (respawn) {

lib/load-loader.mjs

Lines changed: 0 additions & 6 deletions
This file was deleted.

lib/loader-legacy.mjs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { createRequire } from 'module';
2+
import { fileURLToPath } from 'url';
3+
import { send } from './ipc.mjs';
4+
5+
const require = createRequire(import.meta.url);
6+
7+
export async function getFormat(url, context, defaultGetFormat) {
8+
const getPackageType = require('get-package-type');
9+
const filePath = fileURLToPath(url);
10+
11+
send({ required: filePath });
12+
try {
13+
return await defaultGetFormat(url, context, defaultGetFormat);
14+
} catch (err) {
15+
if (err.code === 'ERR_UNKNOWN_FILE_EXTENSION') {
16+
return { format: await getPackageType(filePath) };
17+
}
18+
throw err;
19+
}
20+
}

lib/loader.mjs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { createRequire } from 'module';
2+
import { fileURLToPath } from 'url';
3+
import { send } from './ipc.mjs';
4+
5+
const require = createRequire(import.meta.url);
6+
7+
export async function load(url, context, defaultLoad) {
8+
const getPackageType = require('get-package-type');
9+
const filePath = fileURLToPath(url);
10+
11+
send({ required: filePath });
12+
try {
13+
return await defaultLoad(url, context, defaultLoad);
14+
} catch (err) {
15+
if (err.code === 'ERR_UNKNOWN_FILE_EXTENSION') {
16+
return { format: await getPackageType(filePath), source: null };
17+
}
18+
throw err;
19+
}
20+
}

lib/resolve-loader.mjs

Lines changed: 0 additions & 13 deletions
This file was deleted.

lib/suppress-experimental.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const { emitWarning } = process;
2+
3+
process.emitWarning = (warning, ...args) => {
4+
if (args[0] === 'ExperimentalWarning') {
5+
return;
6+
}
7+
8+
if (args[0] && typeof args[0] === 'object' && args[0].type === 'ExperimentalWarning') {
9+
return;
10+
}
11+
12+
return emitWarning(warning, ...args);
13+
};

lib/wrap.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,19 @@
11
const { dirname, extname } = require('path');
22
const childProcess = require('child_process');
33
const { sync: resolve } = require('resolve');
4-
const getPackageType = require('get-package-type');
54

65
const { getConfig } = require('./cfg');
76
const hook = require('./hook');
87
const { relay, send } = require('./ipc');
98
const resolveMain = require('./resolve-main');
109

11-
// Remove wrap.js from the argv array
12-
process.argv.splice(1, 1);
13-
1410
const script = process.argv[1];
1511
const { extensions, fork, vm } = getConfig(script);
1612

1713
if (process.env.NODE_DEV_PRELOAD) {
1814
require(process.env.NODE_DEV_PRELOAD);
1915
}
16+
require('./suppress-experimental');
2017

2118
// We want to exit on SIGTERM, but defer to existing SIGTERM handlers.
2219
process.once('SIGTERM', () => process.listenerCount('SIGTERM') || process.exit(0));
@@ -26,7 +23,7 @@ if (fork) {
2623
// too. We also need to relay messages about required files to the parent.
2724
const originalFork = childProcess.fork;
2825
childProcess.fork = (modulePath, args, options) => {
29-
const child = originalFork(__filename, [modulePath].concat(args), options);
26+
const child = originalFork(modulePath, args, options);
3027
relay(child);
3128
return child;
3229
};
@@ -48,7 +45,7 @@ process.on('uncaughtException', err => {
4845
});
4946

5047
// Hook into require() and notify the parent process about required files
51-
hook(vm, module, required => send({ required }));
48+
hook(vm, required => send({ required }));
5249

5350
// Check if a module is registered for this extension
5451
const main = resolveMain(script);
@@ -66,6 +63,3 @@ if (typeof mod === 'object' && mod.name) {
6663
} else if (typeof mod === 'string') {
6764
require(resolve(mod, { basedir }));
6865
}
69-
70-
// Execute the wrapped script
71-
ext === 'mjs' || getPackageType.sync(main) === 'module' ? import(main) : require(main);

test/spawn/argv.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ tap.test('should not show up in argv', t => {
66
spawn('argv.js foo', out => {
77
const argv = JSON.parse(out.replace(/'/g, '"'));
88
t.match(argv[0], /.*?node(js|\.exe)?$/);
9-
t.equal(argv[1], 'argv.js');
9+
t.match(argv[1], /.*[\\/]argv\.js$/);
1010
t.equal(argv[2], 'foo');
1111
return { exit: t.end.bind(t) };
1212
});

0 commit comments

Comments
 (0)