Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit af0921d

Browse filesBrowse files
authored
esm: add --import flag
PR-URL: #43942 Fixes: #40110 Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Jacob Smith <jacob@frende.me> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent 659dc12 commit af0921d
Copy full SHA for af0921d

File tree

Expand file treeCollapse file tree

15 files changed

+346
-64
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

15 files changed

+346
-64
lines changed
Open diff view settings
Collapse file

‎doc/api/cli.md‎

Copy file name to clipboardExpand all lines: doc/api/cli.md
+22-3Lines changed: 22 additions & 3 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,8 @@ Only the root context is supported. There is no guarantee that
445445
`globalThis.Array` is indeed the default intrinsic reference. Code may break
446446
under this flag.
447447

448-
To allow polyfills to be added, `--require` runs before freezing intrinsics.
448+
To allow polyfills to be added,
449+
[`--require`][] and [`--import`][] both run before freezing intrinsics.
449450

450451
### `--force-node-api-uncaught-exceptions-policy`
451452

@@ -594,6 +595,18 @@ added: v0.11.15
594595

595596
Specify ICU data load path. (Overrides `NODE_ICU_DATA`.)
596597

598+
### `--import=module`
599+
600+
<!-- YAML
601+
added: REPLACEME
602+
-->
603+
604+
Preload the specified module at startup.
605+
606+
Follows [ECMAScript module][] resolution rules.
607+
Use [`--require`][] to load a [CommonJS module][].
608+
Modules preloaded with `--require` will run before modules preloaded with `--import`.
609+
597610
### `--input-type=type`
598611

599612
<!-- YAML
@@ -1527,8 +1540,9 @@ Preload the specified module at startup.
15271540
Follows `require()`'s module resolution
15281541
rules. `module` may be either a path to a file, or a node module name.
15291542

1530-
Only CommonJS modules are supported. Attempting to preload a
1531-
ES6 Module using `--require` will fail with an error.
1543+
Only CommonJS modules are supported.
1544+
Use [`--import`][] to preload an [ECMAScript module][].
1545+
Modules preloaded with `--require` will run before modules preloaded with `--import`.
15321546

15331547
### `-v`, `--version`
15341548

@@ -1682,6 +1696,7 @@ Node.js options that are allowed are:
16821696
* `--heapsnapshot-signal`
16831697
* `--http-parser`
16841698
* `--icu-data-dir`
1699+
* `--import`
16851700
* `--input-type`
16861701
* `--insecure-http-parser`
16871702
* `--inspect-brk`
@@ -2086,7 +2101,9 @@ done
20862101
[#42511]: https://github.com/nodejs/node/issues/42511
20872102
[Chrome DevTools Protocol]: https://chromedevtools.github.io/devtools-protocol/
20882103
[CommonJS]: modules.md
2104+
[CommonJS module]: modules.md
20892105
[CustomEvent Web API]: https://dom.spec.whatwg.org/#customevent
2106+
[ECMAScript module]: esm.md#modules-ecmascript-modules
20902107
[ECMAScript module loader]: esm.md#loaders
20912108
[Fetch API]: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
20922109
[Modules loaders]: packages.md#modules-loaders
@@ -2103,8 +2120,10 @@ done
21032120
[`--diagnostic-dir`]: #--diagnostic-dirdirectory
21042121
[`--experimental-wasm-modules`]: #--experimental-wasm-modules
21052122
[`--heap-prof-dir`]: #--heap-prof-dir
2123+
[`--import`]: #--importmodule
21062124
[`--openssl-config`]: #--openssl-configfile
21072125
[`--redirect-warnings`]: #--redirect-warningsfile
2126+
[`--require`]: #-r---require-module
21082127
[`Atomics.wait()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics/wait
21092128
[`Buffer`]: buffer.md#class-buffer
21102129
[`CRYPTO_secure_malloc_init`]: https://www.openssl.org/docs/man1.1.0/man3/CRYPTO_secure_malloc_init.html
Collapse file

‎lib/internal/main/check_syntax.js‎

Copy file name to clipboardExpand all lines: lib/internal/main/check_syntax.js
+21-6Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// If user passed `-c` or `--check` arguments to Node, check its syntax
44
// instead of actually running the file.
55

6+
const { getOptionValue } = require('internal/options');
67
const {
78
prepareMainThreadExecution
89
} = require('internal/bootstrap/pre_execution');
@@ -36,18 +37,29 @@ if (process.argv[1] && process.argv[1] !== '-') {
3637

3738
markBootstrapComplete();
3839

39-
checkSyntax(source, filename);
40+
loadESMIfNeeded(() => checkSyntax(source, filename));
4041
} else {
4142
markBootstrapComplete();
4243

43-
readStdin((code) => {
44+
loadESMIfNeeded(() => readStdin((code) => {
4445
checkSyntax(code, '[stdin]');
45-
});
46+
}));
4647
}
4748

48-
async function checkSyntax(source, filename) {
49+
function loadESMIfNeeded(cb) {
4950
const { getOptionValue } = require('internal/options');
50-
let isModule = false;
51+
const hasModulePreImport = getOptionValue('--import').length > 0;
52+
53+
if (hasModulePreImport) {
54+
const { loadESM } = require('internal/process/esm_loader');
55+
loadESM(cb);
56+
return;
57+
}
58+
cb();
59+
}
60+
61+
async function checkSyntax(source, filename) {
62+
let isModule = true;
5163
if (filename === '[stdin]' || filename === '[eval]') {
5264
isModule = getOptionValue('--input-type') === 'module';
5365
} else {
@@ -57,11 +69,14 @@ async function checkSyntax(source, filename) {
5769
const format = await defaultGetFormat(url);
5870
isModule = format === 'module';
5971
}
72+
6073
if (isModule) {
6174
const { ModuleWrap } = internalBinding('module_wrap');
6275
new ModuleWrap(filename, undefined, source, 0, 0);
6376
return;
6477
}
6578

66-
wrapSafe(filename, source);
79+
const { loadESM } = require('internal/process/esm_loader');
80+
const { handleMainPromise } = require('internal/modules/run_main');
81+
handleMainPromise(loadESM((loader) => wrapSafe(filename, source)));
6782
}
Collapse file

‎lib/internal/main/eval_stdin.js‎

Copy file name to clipboardExpand all lines: lib/internal/main/eval_stdin.js
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ readStdin((code) => {
2323
process._eval = code;
2424

2525
const print = getOptionValue('--print');
26+
const loadESM = getOptionValue('--import').length > 0;
2627
if (getOptionValue('--input-type') === 'module')
2728
evalModule(code, print);
2829
else
2930
evalScript('[stdin]',
3031
code,
3132
getOptionValue('--inspect-brk'),
32-
print);
33+
print,
34+
loadESM);
3335
});
Collapse file

‎lib/internal/main/eval_string.js‎

Copy file name to clipboardExpand all lines: lib/internal/main/eval_string.js
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@ markBootstrapComplete();
2121

2222
const source = getOptionValue('--eval');
2323
const print = getOptionValue('--print');
24+
const loadESM = getOptionValue('--import').length > 0;
2425
if (getOptionValue('--input-type') === 'module')
2526
evalModule(source, print);
2627
else
2728
evalScript('[eval]',
2829
source,
2930
getOptionValue('--inspect-brk'),
30-
print);
31+
print,
32+
loadESM);
Collapse file

‎lib/internal/modules/run_main.js‎

Copy file name to clipboardExpand all lines: lib/internal/modules/run_main.js
+6-1Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,12 @@ function shouldUseESMLoader(mainPath) {
3333
* (or an empty list when none have been registered).
3434
*/
3535
const userLoaders = getOptionValue('--experimental-loader');
36-
if (userLoaders.length > 0)
36+
/**
37+
* @type {string[]} userImports A list of preloaded modules registered by the user
38+
* (or an empty list when none have been registered).
39+
*/
40+
const userImports = getOptionValue('--import');
41+
if (userLoaders.length > 0 || userImports.length > 0)
3742
return true;
3843
const esModuleSpecifierResolution =
3944
getOptionValue('--experimental-specifier-resolution');
Collapse file

‎lib/internal/process/esm_loader.js‎

Copy file name to clipboardExpand all lines: lib/internal/process/esm_loader.js
+20-9Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const {
4+
ArrayIsArray,
45
ObjectCreate,
56
} = primordials;
67

@@ -56,8 +57,23 @@ async function initializeLoader() {
5657

5758
const { getOptionValue } = require('internal/options');
5859
const customLoaders = getOptionValue('--experimental-loader');
60+
const preloadModules = getOptionValue('--import');
61+
const loaders = await loadModulesInIsolation(customLoaders);
5962

60-
if (customLoaders.length === 0) return;
63+
// Hooks must then be added to external/public loader
64+
// (so they're triggered in userland)
65+
esmLoader.addCustomLoaders(loaders);
66+
67+
// Preload after loaders are added so they can be used
68+
if (preloadModules?.length) {
69+
await loadModulesInIsolation(preloadModules, loaders);
70+
}
71+
72+
isESMInitialized = true;
73+
}
74+
75+
function loadModulesInIsolation(specifiers, loaders = []) {
76+
if (!ArrayIsArray(specifiers) || specifiers.length === 0) { return; }
6177

6278
let cwd;
6379
try {
@@ -70,19 +86,14 @@ async function initializeLoader() {
7086
// between internal Node.js and userland. For example, a module with internal
7187
// state (such as a counter) should be independent.
7288
const internalEsmLoader = new ESMLoader();
89+
internalEsmLoader.addCustomLoaders(loaders);
7390

7491
// Importation must be handled by internal loader to avoid poluting userland
75-
const keyedExportsList = await internalEsmLoader.import(
76-
customLoaders,
92+
return internalEsmLoader.import(
93+
specifiers,
7794
pathToFileURL(cwd).href,
7895
ObjectCreate(null),
7996
);
80-
81-
// Hooks must then be added to external/public loader
82-
// (so they're triggered in userland)
83-
await esmLoader.addCustomLoaders(keyedExportsList);
84-
85-
isESMInitialized = true;
8697
}
8798

8899
exports.loadESM = async function loadESM(callback) {
Collapse file

‎lib/internal/process/execution.js‎

Copy file name to clipboardExpand all lines: lib/internal/process/execution.js
+35-26Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ function evalModule(source, print) {
4848
return handleMainPromise(loadESM((loader) => loader.eval(source)));
4949
}
5050

51-
function evalScript(name, body, breakFirstLine, print) {
51+
function evalScript(name, body, breakFirstLine, print, shouldLoadESM = false) {
5252
const CJSModule = require('internal/modules/cjs/loader').Module;
5353
const { kVmBreakFirstLineSymbol } = require('internal/util');
5454
const { pathToFileURL } = require('url');
@@ -60,35 +60,44 @@ function evalScript(name, body, breakFirstLine, print) {
6060
module.filename = path.join(cwd, name);
6161
module.paths = CJSModule._nodeModulePaths(cwd);
6262

63+
const { handleMainPromise } = require('internal/modules/run_main');
6364
const asyncESM = require('internal/process/esm_loader');
6465
const baseUrl = pathToFileURL(module.filename).href;
66+
const { loadESM } = asyncESM;
67+
68+
const runScript = () => {
69+
// Create wrapper for cache entry
70+
const script = `
71+
globalThis.module = module;
72+
globalThis.exports = exports;
73+
globalThis.__dirname = __dirname;
74+
globalThis.require = require;
75+
return (main) => main();
76+
`;
77+
globalThis.__filename = name;
78+
const result = module._compile(script, `${name}-wrapper`)(() =>
79+
require('vm').runInThisContext(body, {
80+
filename: name,
81+
displayErrors: true,
82+
[kVmBreakFirstLineSymbol]: !!breakFirstLine,
83+
importModuleDynamically(specifier, _, importAssertions) {
84+
const loader = asyncESM.esmLoader;
85+
return loader.import(specifier, baseUrl, importAssertions);
86+
}
87+
}));
88+
if (print) {
89+
const { log } = require('internal/console/global');
90+
log(result);
91+
}
6592

66-
// Create wrapper for cache entry
67-
const script = `
68-
globalThis.module = module;
69-
globalThis.exports = exports;
70-
globalThis.__dirname = __dirname;
71-
globalThis.require = require;
72-
return (main) => main();
73-
`;
74-
globalThis.__filename = name;
75-
const result = module._compile(script, `${name}-wrapper`)(() =>
76-
require('vm').runInThisContext(body, {
77-
filename: name,
78-
displayErrors: true,
79-
[kVmBreakFirstLineSymbol]: !!breakFirstLine,
80-
importModuleDynamically(specifier, _, importAssertions) {
81-
const loader = asyncESM.esmLoader;
82-
return loader.import(specifier, baseUrl, importAssertions);
83-
}
84-
}));
85-
if (print) {
86-
const { log } = require('internal/console/global');
87-
log(result);
88-
}
93+
if (origModule !== undefined)
94+
globalThis.module = origModule;
95+
};
8996

90-
if (origModule !== undefined)
91-
globalThis.module = origModule;
97+
if (shouldLoadESM) {
98+
return handleMainPromise(loadESM(runScript));
99+
}
100+
return runScript();
92101
}
93102

94103
const exceptionHandlerState = {
Collapse file

‎src/node_options.cc‎

Copy file name to clipboardExpand all lines: src/node_options.cc
+6-2Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -608,10 +608,14 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
608608
AddAlias("-pe", { "--print", "--eval" });
609609
AddAlias("-p", "--print");
610610
AddOption("--require",
611-
"module to preload (option can be repeated)",
612-
&EnvironmentOptions::preload_modules,
611+
"CommonJS module to preload (option can be repeated)",
612+
&EnvironmentOptions::preload_cjs_modules,
613613
kAllowedInEnvironment);
614614
AddAlias("-r", "--require");
615+
AddOption("--import",
616+
"ES module to preload (option can be repeated)",
617+
&EnvironmentOptions::preload_esm_modules,
618+
kAllowedInEnvironment);
615619
AddOption("--interactive",
616620
"always enter the REPL even if stdin does not appear "
617621
"to be a terminal",
Collapse file

‎src/node_options.h‎

Copy file name to clipboardExpand all lines: src/node_options.h
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,9 @@ class EnvironmentOptions : public Options {
188188
bool tls_max_v1_3 = false;
189189
std::string tls_keylog;
190190

191-
std::vector<std::string> preload_modules;
191+
std::vector<std::string> preload_cjs_modules;
192+
193+
std::vector<std::string> preload_esm_modules;
192194

193195
std::vector<std::string> user_argv;
194196

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.