GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — module: handle null source from async loader hooks in sync hooks · nodejs/node@be1b84f

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • lib/internal/modules
    • cjs
      • loader.js
    • customization_hooks.js
    • esm
      • hooks.js
      • load.js
      • loader.js
      • translators.js
  • test
    • fixtures/module-hooks/sync-and-async
      • app.js
      • async-customize-loader.js
      • async-customize.js
      • async-forward-loader.js
      • async-forward.js
      • sync-customize.js
      • sync-forward.js
    • module-hooks
      • test-module-hooks-load-async-and-sync.js
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎lib/internal/modules/cjs/loader.js‎

Copy file name to clipboardExpand all lines: lib/internal/modules/cjs/loader.js
+4-4Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ const {
177177
registerHooks,
178178
resolveHooks,
179179
resolveWithHooks,
180+
validateLoadStrict,
180181
} = require('internal/modules/customization_hooks');
181182
const { stripTypeScriptModuleTypes } = require('internal/modules/typescript');
182183
const packageJsonReader = require('internal/modules/package_json_reader');
@@ -1175,7 +1176,7 @@ function loadBuiltinWithHooks(id, url, format) {
11751176
url ??= `node:${id}`;
11761177
// TODO(joyeecheung): do we really want to invoke the load hook for the builtins?
11771178
const loadResult = loadWithHooks(url, format || 'builtin', /* importAttributes */ undefined,
1178-
getCjsConditionsArray(), getDefaultLoad(url, id));
1179+
getCjsConditionsArray(), getDefaultLoad(url, id), validateLoadStrict);
11791180
if (loadResult.format && loadResult.format !== 'builtin') {
11801181
return undefined; // Format has been overridden, return undefined for the caller to continue loading.
11811182
}
@@ -1791,10 +1792,9 @@ function loadSource(mod, filename, formatFromNode) {
17911792
mod[kURL] = convertCJSFilenameToURL(filename);
17921793
}
17931794

1795+
const defaultLoad = getDefaultLoad(mod[kURL], filename);
17941796
const loadResult = loadWithHooks(mod[kURL], mod[kFormat], /* importAttributes */ undefined,
1795-
getCjsConditionsArray(),
1796-
getDefaultLoad(mod[kURL], filename));
1797-
1797+
getCjsConditionsArray(), defaultLoad, validateLoadStrict);
17981798
// Reset the module properties with load hook results.
17991799
if (loadResult.format !== undefined) {
18001800
mod[kFormat] = loadResult.format;
Collapse file

‎lib/internal/modules/customization_hooks.js‎

Copy file name to clipboardExpand all lines: lib/internal/modules/customization_hooks.js
+34-10Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -262,29 +262,56 @@ function validateResolve(specifier, context, result) {
262262
*/
263263

264264
/**
265-
* Validate the result returned by a chain of resolve hook.
265+
* Validate the result returned by a chain of load hook.
266266
* @param {string} url URL passed into the hooks.
267267
* @param {ModuleLoadContext} context Context passed into the hooks.
268268
* @param {ModuleLoadResult} result Result produced by load hooks.
269269
* @returns {ModuleLoadResult}
270270
*/
271-
function validateLoad(url, context, result) {
271+
function validateLoadStrict(url, context, result) {
272+
validateSourceStrict(url, context, result);
273+
validateFormat(url, context, result);
274+
return result;
275+
}
276+
277+
function validateLoadSloppy(url, context, result) {
278+
validateSourcePermissive(url, context, result);
279+
validateFormat(url, context, result);
280+
return result;
281+
}
282+
283+
function validateSourceStrict(url, context, result) {
272284
const { source, format } = result;
273285
// To align with module.register(), the load hooks are still invoked for
274286
// the builtins even though the default load step only provides null as source,
275287
// and any source content for builtins provided by the user hooks are ignored.
276288
if (!StringPrototypeStartsWith(url, 'node:') &&
277289
typeof result.source !== 'string' &&
278290
!isAnyArrayBuffer(source) &&
279-
!isArrayBufferView(source)) {
291+
!isArrayBufferView(source) &&
292+
format !== 'addon') {
280293
throw new ERR_INVALID_RETURN_PROPERTY_VALUE(
281294
'a string, an ArrayBuffer, or a TypedArray',
282295
'load',
283296
'source',
284297
source,
285298
);
286299
}
300+
}
287301

302+
function validateSourcePermissive(url, context, result) {
303+
const { source, format } = result;
304+
if (format === 'commonjs' && source == null) {
305+
// Accommodate the quirk in defaultLoad used by asynchronous loader hooks
306+
// which sets source to null for commonjs.
307+
// See: https://github.com/nodejs/node/issues/57327#issuecomment-2701382020
308+
return;
309+
}
310+
validateSourceStrict(url, context, result);
311+
}
312+
313+
function validateFormat(url, context, result) {
314+
const { format } = result;
288315
if (typeof format !== 'string' && format !== undefined) {
289316
throw new ERR_INVALID_RETURN_PROPERTY_VALUE(
290317
'a string',
@@ -293,12 +320,6 @@ function validateLoad(url, context, result) {
293320
format,
294321
);
295322
}
296-
297-
return {
298-
__proto__: null,
299-
format,
300-
source,
301-
};
302323
}
303324

304325
class ModuleResolveContext {
@@ -338,9 +359,10 @@ let decoder;
338359
* @param {ImportAttributes|undefined} importAttributes
339360
* @param {string[]} conditions
340361
* @param {(url: string, context: ModuleLoadContext) => ModuleLoadResult} defaultLoad
362+
* @param {(url: string, context: ModuleLoadContext, result: ModuleLoadResult) => ModuleLoadResult} validateLoad
341363
* @returns {ModuleLoadResult}
342364
*/
343-
function loadWithHooks(url, originalFormat, importAttributes, conditions, defaultLoad) {
365+
function loadWithHooks(url, originalFormat, importAttributes, conditions, defaultLoad, validateLoad) {
344366
debug('loadWithHooks', url, originalFormat);
345367
const context = new ModuleLoadContext(originalFormat, importAttributes, conditions);
346368
if (loadHooks.length === 0) {
@@ -403,4 +425,6 @@ module.exports = {
403425
registerHooks,
404426
resolveHooks,
405427
resolveWithHooks,
428+
validateLoadStrict,
429+
validateLoadSloppy,
406430
};
Collapse file

‎lib/internal/modules/esm/hooks.js‎

Copy file name to clipboardExpand all lines: lib/internal/modules/esm/hooks.js
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ const {
6161
SHARED_MEMORY_BYTE_LENGTH,
6262
WORKER_TO_MAIN_THREAD_NOTIFICATION,
6363
} = require('internal/modules/esm/shared_constants');
64-
let debug = require('internal/util/debuglog').debuglog('esm', (fn) => {
64+
let debug = require('internal/util/debuglog').debuglog('async_loader_worker', (fn) => {
6565
debug = fn;
6666
});
6767
let importMetaInitializer;
Collapse file

‎lib/internal/modules/esm/load.js‎

Copy file name to clipboardExpand all lines: lib/internal/modules/esm/load.js
+17-5Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,22 +141,34 @@ function defaultLoadSync(url, context = kEmptyObject) {
141141

142142
throwIfUnsupportedURLScheme(urlInstance, false);
143143

144+
let shouldBeReloadedByCJSLoader = false;
144145
if (urlInstance.protocol === 'node:') {
145146
source = null;
146-
} else if (source == null) {
147-
({ responseURL, source } = getSourceSync(urlInstance, context));
148-
context.source = source;
149-
}
147+
format ??= 'builtin';
148+
} else if (format === 'addon') {
149+
// Skip loading addon file content. It must be loaded with dlopen from file system.
150+
source = null;
151+
} else {
152+
if (source == null) {
153+
({ responseURL, source } = getSourceSync(urlInstance, context));
154+
context = { __proto__: context, source };
155+
}
150156

151-
format ??= defaultGetFormat(urlInstance, context);
157+
// Now that we have the source for the module, run `defaultGetFormat` to detect its format.
158+
format ??= defaultGetFormat(urlInstance, context);
152159

160+
// For backward compatibility reasons, we need to let go through Module._load
161+
// again.
162+
shouldBeReloadedByCJSLoader = (format === 'commonjs');
163+
}
153164
validateAttributes(url, format, importAttributes);
154165

155166
return {
156167
__proto__: null,
157168
format,
158169
responseURL,
159170
source,
171+
shouldBeReloadedByCJSLoader,
160172
};
161173
}
162174

Collapse file

‎lib/internal/modules/esm/loader.js‎

Copy file name to clipboardExpand all lines: lib/internal/modules/esm/loader.js
+35-26Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,9 @@ const {
5555
resolveWithHooks,
5656
loadHooks,
5757
loadWithHooks,
58+
validateLoadSloppy,
5859
} = require('internal/modules/customization_hooks');
59-
let defaultResolve, defaultLoad, defaultLoadSync, importMetaInitializer;
60+
let defaultResolve, defaultLoadSync, importMetaInitializer;
6061

6162
const { tracingChannel } = require('diagnostics_channel');
6263
const onImport = tracingChannel('module.import');
@@ -146,6 +147,10 @@ let hooksProxy;
146147
* @typedef {ArrayBuffer|TypedArray|string} ModuleSource
147148
*/
148149

150+
/**
151+
* @typedef {{ format: ModuleFormat, source: ModuleSource, translatorKey: string }} TranslateContext
152+
*/
153+
149154
/**
150155
* This class covers the base machinery of module loading. To add custom
151156
* behavior you can pass a customizations object and this object will be
@@ -503,18 +508,19 @@ class ModuleLoader {
503508

504509
const loadResult = this.#loadSync(url, { format, importAttributes });
505510

511+
const formatFromLoad = loadResult.format;
506512
// Use the synchronous commonjs translator which can deal with cycles.
507-
const finalFormat =
508-
loadResult.format === 'commonjs' ||
509-
loadResult.format === 'commonjs-typescript' ? 'commonjs-sync' : loadResult.format;
513+
const translatorKey = (formatFromLoad === 'commonjs' || formatFromLoad === 'commonjs-typescript') ?
514+
'commonjs-sync' : formatFromLoad;
510515

511-
if (finalFormat === 'wasm') {
516+
if (translatorKey === 'wasm') {
512517
assert.fail('WASM is currently unsupported by require(esm)');
513518
}
514519

515520
const { source } = loadResult;
516521
const isMain = (parentURL === undefined);
517-
const wrap = this.#translate(url, finalFormat, source, parentURL);
522+
const translateContext = { format: formatFromLoad, source, translatorKey, __proto__: null };
523+
const wrap = this.#translate(url, translateContext, parentURL);
518524
assert(wrap instanceof ModuleWrap, `Translator used for require(${url}) should not be async`);
519525

520526
if (process.env.WATCH_REPORT_DEPENDENCIES && process.send) {
@@ -523,7 +529,7 @@ class ModuleLoader {
523529

524530
const cjsModule = wrap[imported_cjs_symbol];
525531
if (cjsModule) {
526-
assert(finalFormat === 'commonjs-sync');
532+
assert(translatorKey === 'commonjs-sync');
527533
// Check if the ESM initiating import CJS is being required by the same CJS module.
528534
if (cjsModule?.[kIsExecuting]) {
529535
const parentFilename = urlToFilename(parentURL);
@@ -547,22 +553,22 @@ class ModuleLoader {
547553
* Translate a loaded module source into a ModuleWrap. This is run synchronously,
548554
* but the translator may return the ModuleWrap in a Promise.
549555
* @param {string} url URL of the module to be translated.
550-
* @param {string} format Format of the module to be translated. This is used to find
551-
* matching translators.
552-
* @param {ModuleSource} source Source of the module to be translated.
553-
* @param {string|undefined} parentURL URL of the parent module. Undefined if it's the entry point.
556+
* @param {TranslateContext} translateContext Context for the translator
557+
* @param {string|undefined} parentURL URL of the module initiating the module loading for the first time.
558+
* Undefined if it's the entry point.
554559
* @returns {ModuleWrap}
555560
*/
556-
#translate(url, format, source, parentURL) {
561+
#translate(url, translateContext, parentURL) {
562+
const { translatorKey, format } = translateContext;
557563
this.validateLoadResult(url, format);
558-
const translator = getTranslators().get(format);
564+
const translator = getTranslators().get(translatorKey);
559565

560566
if (!translator) {
561-
throw new ERR_UNKNOWN_MODULE_FORMAT(format, url);
567+
throw new ERR_UNKNOWN_MODULE_FORMAT(translatorKey, url);
562568
}
563569

564-
const result = FunctionPrototypeCall(translator, this, url, source, parentURL === undefined);
565-
assert(result instanceof ModuleWrap);
570+
const result = FunctionPrototypeCall(translator, this, url, translateContext, parentURL);
571+
assert(result instanceof ModuleWrap, `The ${format} module returned is not a ModuleWrap`);
566572
return result;
567573
}
568574

@@ -575,7 +581,8 @@ class ModuleLoader {
575581
* @returns {ModuleWrap}
576582
*/
577583
loadAndTranslateForRequireInImportedCJS(url, loadContext, parentURL) {
578-
const { format: formatFromLoad, source } = this.#loadSync(url, loadContext);
584+
const loadResult = this.#loadSync(url, loadContext);
585+
const formatFromLoad = loadResult.format;
579586

580587
if (formatFromLoad === 'wasm') { // require(wasm) is not supported.
581588
throw new ERR_UNKNOWN_MODULE_FORMAT(formatFromLoad, url);
@@ -587,15 +594,16 @@ class ModuleLoader {
587594
}
588595
}
589596

590-
let finalFormat = formatFromLoad;
597+
let translatorKey = formatFromLoad;
591598
if (formatFromLoad === 'commonjs') {
592-
finalFormat = 'require-commonjs';
599+
translatorKey = 'require-commonjs';
593600
}
594601
if (formatFromLoad === 'commonjs-typescript') {
595-
finalFormat = 'require-commonjs-typescript';
602+
translatorKey = 'require-commonjs-typescript';
596603
}
597604

598-
const wrap = this.#translate(url, finalFormat, source, parentURL);
605+
const translateContext = { ...loadResult, translatorKey, __proto__: null };
606+
const wrap = this.#translate(url, translateContext, parentURL);
599607
assert(wrap instanceof ModuleWrap, `Translator used for require(${url}) should not be async`);
600608
return wrap;
601609
}
@@ -610,8 +618,9 @@ class ModuleLoader {
610618
*/
611619
loadAndTranslate(url, loadContext, parentURL) {
612620
const maybePromise = this.load(url, loadContext);
613-
const afterLoad = ({ format, source }) => {
614-
return this.#translate(url, format, source, parentURL);
621+
const afterLoad = (loadResult) => {
622+
const translateContext = { ...loadResult, translatorKey: loadResult.format, __proto__: null };
623+
return this.#translate(url, translateContext, parentURL);
615624
};
616625
if (isPromise(maybePromise)) {
617626
return maybePromise.then(afterLoad);
@@ -837,8 +846,8 @@ class ModuleLoader {
837846
return this.#customizations.load(url, context);
838847
}
839848

840-
defaultLoad ??= require('internal/modules/esm/load').defaultLoad;
841-
return defaultLoad(url, context);
849+
defaultLoadSync ??= require('internal/modules/esm/load').defaultLoadSync;
850+
return defaultLoadSync(url, context);
842851
}
843852

844853
/**
@@ -873,7 +882,7 @@ class ModuleLoader {
873882
// TODO(joyeecheung): construct the ModuleLoadContext in the loaders directly instead
874883
// of converting them from plain objects in the hooks.
875884
return loadWithHooks(url, context.format, context.importAttributes, this.#defaultConditions,
876-
this.#loadAndMaybeBlockOnLoaderThread.bind(this));
885+
this.#loadAndMaybeBlockOnLoaderThread.bind(this), validateLoadSloppy);
877886
}
878887
return this.#loadAndMaybeBlockOnLoaderThread(url, context);
879888
}

0 commit comments

Comments
0 (0)
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.