From abd97f3e646c285ddb6dbf254fde6803d2b79012 Mon Sep 17 00:00:00 2001 From: LiviaMedeiros Date: Wed, 30 Aug 2023 15:54:46 +0800 Subject: [PATCH 1/3] build,module: add `node-esm` support --- Makefile | 4 ++++ lib/internal/modules/esm/get_format.js | 11 +++++++++++ lib/internal/modules/helpers.js | 6 ++++++ lib/internal/modules/run_main.js | 8 +++++++- 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b1c267ed5526fe..f931c047d7e929 100644 --- a/Makefile +++ b/Makefile @@ -77,6 +77,7 @@ EXEEXT := $(shell $(PYTHON) -c \ "import sys; print('.exe' if sys.platform == 'win32' else '')") NODE_EXE = node$(EXEEXT) +NODE_ESM_EXE = node-esm${EXEEXT} NODE ?= ./$(NODE_EXE) NODE_G_EXE = node_g$(EXEEXT) NPM ?= ./deps/npm/bin/npm-cli.js @@ -132,6 +133,7 @@ $(NODE_EXE): build_type:=Release $(NODE_G_EXE): build_type:=Debug $(NODE_EXE) $(NODE_G_EXE): config.gypi out/Makefile $(MAKE) -C out BUILDTYPE=${build_type} V=$(V) + ln -fs $(NODE_EXE) out/${build_type}/$(NODE_ESM_EXE) if [ ! -r $@ ] || [ ! -L $@ ]; then \ ln -fs out/${build_type}/$(NODE_EXE) $@; fi else @@ -147,10 +149,12 @@ else endif $(NODE_EXE): config.gypi out/Release/build.ninja $(NINJA) -C out/Release $(NINJA_ARGS) + ln -fs $(NODE_EXE) out/Release/$(NODE_ESM_EXE) if [ ! -r $@ ] || [ ! -L $@ ]; then ln -fs out/Release/$(NODE_EXE) $@; fi $(NODE_G_EXE): config.gypi out/Debug/build.ninja $(NINJA) -C out/Debug $(NINJA_ARGS) + ln -fs $(NODE_EXE) out/Debug/$(NODE_ESM_EXE) if [ ! -r $@ ] || [ ! -L $@ ]; then ln -fs out/Debug/$(NODE_EXE) $@; fi else $(NODE_EXE) $(NODE_G_EXE): diff --git a/lib/internal/modules/esm/get_format.js b/lib/internal/modules/esm/get_format.js index 4ac9c011d153f4..cd28a65b309959 100644 --- a/lib/internal/modules/esm/get_format.js +++ b/lib/internal/modules/esm/get_format.js @@ -13,6 +13,7 @@ const { extensionFormatMap, mimeToFormat, } = require('internal/modules/esm/formats'); +const { isNodeESM } = require('internal/modules/helpers'); const experimentalNetworkImports = getOptionValue('--experimental-network-imports'); @@ -73,8 +74,13 @@ function extname(url) { * @returns {string} */ function getFileProtocolModuleFormat(url, context, ignoreErrors) { + const defaultESM = isNodeESM(); + const ext = extname(url); if (ext === '.js') { + if (defaultESM) { + return getPackageType(url) === 'commonjs' ? 'commonjs' : 'module'; + } return getPackageType(url) === 'module' ? 'module' : 'commonjs'; } @@ -83,6 +89,11 @@ function getFileProtocolModuleFormat(url, context, ignoreErrors) { // Explicit undefined return indicates load hook should rerun format check if (ignoreErrors) { return undefined; } + + if (defaultESM) { + return 'module'; + } + const filepath = fileURLToPath(url); let suggestion = ''; if (getPackageType(url) === 'module' && ext === '') { diff --git a/lib/internal/modules/helpers.js b/lib/internal/modules/helpers.js index 307a34cb09b512..837dcd682a6073 100644 --- a/lib/internal/modules/helpers.js +++ b/lib/internal/modules/helpers.js @@ -256,11 +256,17 @@ function hasEsmSyntax(code) { stmt.type === 'ExportAllDeclaration'); } +function isNodeESM() { + const execname = path.basename(process.argv0); + return execname === 'node-esm' || execname === 'node-esm.exe'; +} + module.exports = { addBuiltinLibsToObject, getCjsConditions, initializeCjsConditions, hasEsmSyntax, + isNodeESM, loadBuiltinModule, makeRequireFunction, normalizeReferrerURL, diff --git a/lib/internal/modules/run_main.js b/lib/internal/modules/run_main.js index 0bfe7b11241416..e9a87af56cb35e 100644 --- a/lib/internal/modules/run_main.js +++ b/lib/internal/modules/run_main.js @@ -5,6 +5,7 @@ const { } = primordials; const { getOptionValue } = require('internal/options'); +const { isNodeESM } = require('internal/modules/helpers'); const path = require('path'); function resolveMainPath(main) { @@ -42,8 +43,13 @@ function shouldUseESMLoader(mainPath) { return true; if (!mainPath || StringPrototypeEndsWith(mainPath, '.cjs')) return false; + const pkg = readPackageScope(mainPath); - return pkg && pkg.data.type === 'module'; + + if (isNodeESM()) + return pkg.data?.type !== 'commonjs'; + + return pkg.data?.type === 'module'; } function runMainESM(mainPath) { From 30bd45d09ae64e57689411cf31289ced7b50dd93 Mon Sep 17 00:00:00 2001 From: LiviaMedeiros Date: Wed, 30 Aug 2023 21:17:21 +0800 Subject: [PATCH 2/3] squash: adjust line number in fixture --- test/fixtures/errors/force_colors.snapshot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixtures/errors/force_colors.snapshot b/test/fixtures/errors/force_colors.snapshot index be1d45d0d8e8ba..5049e8a48fb396 100644 --- a/test/fixtures/errors/force_colors.snapshot +++ b/test/fixtures/errors/force_colors.snapshot @@ -8,7 +8,7 @@ Error: Should include grayed stack trace  at Module._extensions..js (node:internal*modules*cjs*loader:1295:10)  at Module.load (node:internal*modules*cjs*loader:1091:32)  at Module._load (node:internal*modules*cjs*loader:938:12) - at Function.executeUserEntryPoint [as runMain] (node:internal*modules*run_main:83:12) + at Function.executeUserEntryPoint [as runMain] (node:internal*modules*run_main:89:12)  at node:internal*main*run_main_module:23:47 Node.js * From 02f7d19e3f2d1aa94613cb5b879d0943df52cc33 Mon Sep 17 00:00:00 2001 From: LiviaMedeiros Date: Wed, 30 Aug 2023 21:20:29 +0800 Subject: [PATCH 3/3] squash: add basic test --- test/parallel/test-node-esm.mjs | 68 +++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 test/parallel/test-node-esm.mjs diff --git a/test/parallel/test-node-esm.mjs b/test/parallel/test-node-esm.mjs new file mode 100644 index 00000000000000..b48a84e1fe8f14 --- /dev/null +++ b/test/parallel/test-node-esm.mjs @@ -0,0 +1,68 @@ +import '../common/index.mjs'; +import { strictEqual } from 'node:assert'; +import fs from 'node:fs/promises'; +import { execFileSync } from 'node:child_process'; +import { createRequire } from 'node:module'; +const tmpdir = createRequire(import.meta.url)('../common/tmpdir.js'); + +tmpdir.refresh(); + +const code = 'process.stdout.write(this === undefined ? "module" : "commonjs");\n'; + +// Copying would be significantly slower +const copyMethod = async (src, dest) => fs.link(src, dest); + +const pathJS = tmpdir.resolve('blep.js'); +await fs.writeFile(pathJS, code); +const pathMJS = tmpdir.resolve('blep.mjs'); +await fs.writeFile(pathMJS, code); +const pathCJS = tmpdir.resolve('blep.cjs'); +await fs.writeFile(pathCJS, code); +const path = tmpdir.resolve('blep'); +await fs.writeFile(path, code); +const pathIDK = tmpdir.resolve('blep.idontknowthistype'); +await fs.writeFile(pathIDK, code); + +{ + const execPath = tmpdir.resolve('node'); + await copyMethod(process.execPath, execPath); + + strictEqual(execFileSync(execPath, [pathJS]).toString(), 'commonjs'); + strictEqual(execFileSync(execPath, [pathMJS]).toString(), 'module'); + strictEqual(execFileSync(execPath, [pathCJS]).toString(), 'commonjs'); + strictEqual(execFileSync(execPath, [path]).toString(), 'commonjs'); + strictEqual(execFileSync(execPath, [pathIDK]).toString(), 'commonjs'); +} + +{ + const execPath = tmpdir.resolve('node-esm'); + await copyMethod(process.execPath, execPath); + + strictEqual(execFileSync(execPath, [pathJS]).toString(), 'module'); + strictEqual(execFileSync(execPath, [pathMJS]).toString(), 'module'); + strictEqual(execFileSync(execPath, [pathCJS]).toString(), 'commonjs'); + strictEqual(execFileSync(execPath, [path]).toString(), 'module'); + strictEqual(execFileSync(execPath, [pathIDK]).toString(), 'module'); +} + +{ + const execPath = tmpdir.resolve('node.exe'); + await copyMethod(process.execPath, execPath); + + strictEqual(execFileSync(execPath, [pathJS]).toString(), 'commonjs'); + strictEqual(execFileSync(execPath, [pathMJS]).toString(), 'module'); + strictEqual(execFileSync(execPath, [pathCJS]).toString(), 'commonjs'); + strictEqual(execFileSync(execPath, [path]).toString(), 'commonjs'); + strictEqual(execFileSync(execPath, [pathIDK]).toString(), 'commonjs'); +} + +{ + const execPath = tmpdir.resolve('node-esm.exe'); + await copyMethod(process.execPath, execPath); + + strictEqual(execFileSync(execPath, [pathJS]).toString(), 'module'); + strictEqual(execFileSync(execPath, [pathMJS]).toString(), 'module'); + strictEqual(execFileSync(execPath, [pathCJS]).toString(), 'commonjs'); + strictEqual(execFileSync(execPath, [path]).toString(), 'module'); + strictEqual(execFileSync(execPath, [pathIDK]).toString(), 'module'); +}