GeezerVison is showing github.com — esm: fallback to `getSource` when `load` returns nullish `source` · nodejs/node@843d5f8
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/load.js +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -262,5 +262,6 @@ function throwUnknownModuleFormat(url, format) { | 262 | 262 | module.exports = { | 263 | 263 | defaultLoad, | 264 | 264 | defaultLoadSync, | | 265 | + getSourceSync, | 265 | 266 | throwUnknownModuleFormat, | 266 | 267 | }; |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/translators.js +23-12Lines changed: 23 additions & 12 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -31,6 +31,7 @@ function lazyTypes() { | 31 | 31 | } | 32 | 32 |
| 33 | 33 | const { containsModuleSyntax } = internalBinding('contextify'); | | 34 | +const { BuiltinModule } = require('internal/bootstrap/realm'); | 34 | 35 | const assert = require('internal/assert'); | 35 | 36 | const { readFileSync } = require('fs'); | 36 | 37 | const { dirname, extname, isAbsolute } = require('path'); | @@ -58,6 +59,17 @@ const asyncESM = require('internal/process/esm_loader'); | 58 | 59 | const { emitWarningSync } = require('internal/process/warning'); | 59 | 60 | const { internalCompileFunction } = require('internal/vm'); | 60 | 61 |
| | 62 | +// Lazy-loading to avoid circular dependencies. | | 63 | +let getSourceSync; | | 64 | +/** | | 65 | + * @param {Parameters<typeof import('./load').getSourceSync>[0]} url | | 66 | + * @returns {ReturnType<typeof import('./load').getSourceSync>} | | 67 | + */ | | 68 | +function getSource(url) { | | 69 | + getSourceSync ??= require('internal/modules/esm/load').getSourceSync; | | 70 | + return getSourceSync(url); | | 71 | +} | | 72 | + | 61 | 73 | /** @type {import('deps/cjs-module-lexer/lexer.js').parse} */ | 62 | 74 | let cjsParse; | 63 | 75 | /** | @@ -225,21 +237,19 @@ function loadCJSModule(module, source, url, filename) { | 225 | 237 | // eslint-disable-next-line func-name-matching,func-style | 226 | 238 | const requireFn = function require(specifier) { | 227 | 239 | let importAttributes = kEmptyObject; | 228 | | - if (!StringPrototypeStartsWith(specifier, 'node:')) { | | 240 | + if (!StringPrototypeStartsWith(specifier, 'node:') && !BuiltinModule.normalizeRequirableId(specifier)) { | 229 | 241 | // TODO: do not depend on the monkey-patchable CJS loader here. | 230 | 242 | const path = CJSModule._resolveFilename(specifier, module); | 231 | | - if (specifier !== path) { | 232 | | - switch (extname(path)) { | 233 | | - case '.json': | 234 | | - importAttributes = { __proto__: null, type: 'json' }; | 235 | | - break; | 236 | | - case '.node': | 237 | | - return CJSModule._load(specifier, module); | 238 | | - default: | | 243 | + switch (extname(path)) { | | 244 | + case '.json': | | 245 | + importAttributes = { __proto__: null, type: 'json' }; | | 246 | + break; | | 247 | + case '.node': | | 248 | + return CJSModule._load(specifier, module); | | 249 | + default: | 239 | 250 | // fall through | 240 | | - } | 241 | | - specifier = `${pathToFileURL(path)}`; | 242 | 251 | } | | 252 | + specifier = `${pathToFileURL(path)}`; | 243 | 253 | } | 244 | 254 | const job = asyncESM.esmLoader.getModuleJobSync(specifier, url, importAttributes); | 245 | 255 | job.runSync(); | @@ -276,7 +286,8 @@ function createCJSModuleWrap(url, source, isMain, loadCJS = loadCJSModule) { | 276 | 286 | debug(`Translating CJSModule ${url}`); | 277 | 287 |
| 278 | 288 | const filename = StringPrototypeStartsWith(url, 'file://') ? fileURLToPath(url) : url; | 279 | | - source = stringify(source); | | 289 | + // In case the source was not provided by the `load` step, we need fetch it now. | | 290 | + source = stringify(source ?? getSource(new URL(url)).source); | 280 | 291 |
| 281 | 292 | const { exportNames, module } = cjsPreparseModuleExports(filename, source); | 282 | 293 | cjsCache.set(url, module); | |
Collapse file Copy file name to clipboardExpand all lines: test/es-module/test-esm-loader-hooks.mjs +35Lines changed: 35 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -736,4 +736,39 @@ describe('Loader hooks', { concurrency: true }, () => { | 736 | 736 | assert.strictEqual(code, 0); | 737 | 737 | assert.strictEqual(signal, null); | 738 | 738 | }); | | 739 | + | | 740 | + it('should handle mixed of opt-in modules and non-opt-in ones', async () => { | | 741 | + const { code, signal, stdout, stderr } = await spawnPromisified(execPath, [ | | 742 | + '--no-warnings', | | 743 | + '--experimental-loader', | | 744 | + `data:text/javascript,const fixtures=${JSON.stringify(fixtures.path('empty.js'))};export ${ | | 745 | + encodeURIComponent(function resolve(s, c, n) { | | 746 | + if (s.endsWith('entry-point')) { | | 747 | + return { | | 748 | + shortCircuit: true, | | 749 | + url: 'file:///c:/virtual-entry-point', | | 750 | + format: 'commonjs', | | 751 | + }; | | 752 | + } | | 753 | + return n(s, c); | | 754 | + }) | | 755 | + }export ${ | | 756 | + encodeURIComponent(async function load(u, c, n) { | | 757 | + if (u === 'file:///c:/virtual-entry-point') { | | 758 | + return { | | 759 | + shortCircuit: true, | | 760 | + source: `"use strict";require(${JSON.stringify(fixtures)});console.log("Hello");`, | | 761 | + format: 'commonjs', | | 762 | + }; | | 763 | + } | | 764 | + return n(u, c); | | 765 | + })}`, | | 766 | + 'entry-point', | | 767 | + ]); | | 768 | + | | 769 | + assert.strictEqual(stderr, ''); | | 770 | + assert.strictEqual(stdout, 'Hello\n'); | | 771 | + assert.strictEqual(code, 0); | | 772 | + assert.strictEqual(signal, null); | | 773 | + }); | 739 | 774 | }); |
|