- For imported CJS, if it's not customized by asynchronous hooks, make sure it won't use the quirky re-invented require in all cases. - When the imported CJS module is customized by synchronous hooks, in the synthetic module evalutation step, avoid calling the respective default step again. - Make the branching of loadCJSModuleWithModuleLoad() and loadCJSModuleWithSpecialRequire() more explicit, and fold the tentative fs read in the 'commonjs' translator into the share createCJSModuleWrap() helper instead of checking it twice in the same path. Signed-off-by: Joyee Cheung <joyeec9h3@gmail.com> PR-URL: https://github.com/nodejs/node/pull/62920 Fixes: https://github.com/nodejs/node/issues/63060 Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
221 lines
5.9 KiB
JavaScript
221 lines
5.9 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
RegExpPrototypeExec,
|
|
} = primordials;
|
|
const {
|
|
kEmptyObject,
|
|
} = require('internal/util');
|
|
|
|
const { defaultGetFormat } = require('internal/modules/esm/get_format');
|
|
const { validateAttributes, emitImportAssertionWarning } = require('internal/modules/esm/assert');
|
|
const fs = require('fs');
|
|
|
|
const { Buffer: { from: BufferFrom } } = require('buffer');
|
|
|
|
const { URL } = require('internal/url');
|
|
const {
|
|
ERR_INVALID_URL,
|
|
ERR_UNKNOWN_MODULE_FORMAT,
|
|
ERR_UNSUPPORTED_ESM_URL_SCHEME,
|
|
} = require('internal/errors').codes;
|
|
|
|
const {
|
|
dataURLProcessor,
|
|
} = require('internal/data_url');
|
|
|
|
/**
|
|
* @param {URL} url URL to the module
|
|
* @param {LoadContext} context used to decorate error messages
|
|
* @returns {{ responseURL: string, source: string | BufferView }}
|
|
*/
|
|
function getSourceSync(url, context) {
|
|
const { protocol, href } = url;
|
|
const responseURL = href;
|
|
let source;
|
|
if (protocol === 'file:') {
|
|
// If you are reading this code to figure out how to patch Node.js module loading
|
|
// behavior - DO NOT depend on the patchability in new code: Node.js
|
|
// internals may stop going through the JavaScript fs module entirely.
|
|
// Prefer module.registerHooks() or other more formal fs hooks released in the future.
|
|
source = fs.readFileSync(url);
|
|
} else if (protocol === 'data:') {
|
|
const result = dataURLProcessor(url);
|
|
if (result === 'failure') {
|
|
throw new ERR_INVALID_URL(responseURL);
|
|
}
|
|
source = BufferFrom(result.body);
|
|
} else {
|
|
const supportedSchemes = ['file', 'data'];
|
|
throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(url, supportedSchemes);
|
|
}
|
|
return { __proto__: null, responseURL, source };
|
|
}
|
|
|
|
|
|
/**
|
|
* Node.js default load hook.
|
|
* @param {string} url
|
|
* @param {LoadContext} context
|
|
* @returns {LoadReturn}
|
|
*/
|
|
function defaultLoad(url, context = kEmptyObject) {
|
|
let responseURL = url;
|
|
let {
|
|
importAttributes,
|
|
format,
|
|
source,
|
|
} = context;
|
|
|
|
if (importAttributes == null && !('importAttributes' in context) && 'importAssertions' in context) {
|
|
emitImportAssertionWarning();
|
|
importAttributes = context.importAssertions;
|
|
// Alias `importAssertions` to `importAttributes`
|
|
context = {
|
|
...context,
|
|
importAttributes,
|
|
};
|
|
}
|
|
|
|
const urlInstance = new URL(url);
|
|
|
|
throwIfUnsupportedURLScheme(urlInstance);
|
|
|
|
if (urlInstance.protocol === 'node:') {
|
|
source = null;
|
|
format ??= 'builtin';
|
|
} else if (format === 'addon') {
|
|
// Skip loading addon file content. It must be loaded with dlopen from file system.
|
|
source = null;
|
|
} else if (format !== 'commonjs') {
|
|
if (source == null) {
|
|
({ responseURL, source } = getSourceSync(urlInstance, context));
|
|
context = { __proto__: context, source };
|
|
}
|
|
|
|
if (format == null) {
|
|
// Now that we have the source for the module, run `defaultGetFormat` to detect its format.
|
|
format = defaultGetFormat(urlInstance, context);
|
|
|
|
if (format === 'commonjs') {
|
|
// For backward compatibility reasons, we need to discard the source in
|
|
// order for the CJS loader to re-fetch it.
|
|
source = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
validateAttributes(url, format, importAttributes);
|
|
|
|
return {
|
|
__proto__: null,
|
|
format,
|
|
responseURL,
|
|
source,
|
|
};
|
|
}
|
|
/**
|
|
* @typedef LoadContext
|
|
* @property {string} [format] A hint (possibly returned from `resolve`)
|
|
* @property {string | Buffer | ArrayBuffer} [source] source
|
|
* @property {Record<string, string>} [importAttributes] import attributes
|
|
*/
|
|
|
|
/**
|
|
* @typedef LoadReturn
|
|
* @property {string} format format
|
|
* @property {URL['href']} responseURL The module's fully resolved URL
|
|
* @property {Buffer} source source
|
|
*/
|
|
|
|
/**
|
|
* @param {URL['href']} url
|
|
* @param {LoadContext} [context]
|
|
* @returns {LoadReturn}
|
|
*/
|
|
function defaultLoadSync(url, context = kEmptyObject) {
|
|
let responseURL = url;
|
|
const { importAttributes } = context;
|
|
let {
|
|
format,
|
|
source,
|
|
} = context;
|
|
|
|
const urlInstance = new URL(url);
|
|
|
|
throwIfUnsupportedURLScheme(urlInstance, false);
|
|
|
|
if (urlInstance.protocol === 'node:') {
|
|
source = null;
|
|
format ??= 'builtin';
|
|
} else if (format === 'addon') {
|
|
// Skip loading addon file content. It must be loaded with dlopen from file system.
|
|
source = null;
|
|
} else {
|
|
if (source == null) {
|
|
({ responseURL, source } = getSourceSync(urlInstance, context));
|
|
context = { __proto__: context, source };
|
|
}
|
|
|
|
// Now that we have the source for the module, run `defaultGetFormat` to detect its format.
|
|
format ??= defaultGetFormat(urlInstance, context);
|
|
}
|
|
validateAttributes(url, format, importAttributes);
|
|
|
|
return {
|
|
__proto__: null,
|
|
format,
|
|
responseURL,
|
|
source,
|
|
};
|
|
}
|
|
|
|
|
|
/**
|
|
* throws an error if the protocol is not one of the protocols
|
|
* that can be loaded in the default loader
|
|
* @param {URL} parsed
|
|
*/
|
|
function throwIfUnsupportedURLScheme(parsed) {
|
|
// Avoid accessing the `protocol` property due to the lazy getters.
|
|
const protocol = parsed?.protocol;
|
|
if (
|
|
protocol &&
|
|
protocol !== 'file:' &&
|
|
protocol !== 'data:' &&
|
|
protocol !== 'node:' &&
|
|
(
|
|
protocol !== 'https:' &&
|
|
protocol !== 'http:'
|
|
)
|
|
) {
|
|
const schemes = ['file', 'data', 'node'];
|
|
throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(parsed, schemes);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* For a falsy `format` returned from `load`, throw an error.
|
|
* This could happen from either a custom user loader _or_ from the default loader, because the default loader tries to
|
|
* determine formats for data URLs.
|
|
* @param {string} url The resolved URL of the module
|
|
* @param {(null|undefined|false|0|'')} format Falsy format returned from `load`
|
|
*/
|
|
function throwUnknownModuleFormat(url, format) {
|
|
const dataUrl = RegExpPrototypeExec(
|
|
/^data:([^/]+\/[^;,]+)(?:[^,]*?)(;base64)?,/,
|
|
url,
|
|
);
|
|
|
|
throw new ERR_UNKNOWN_MODULE_FORMAT(
|
|
dataUrl ? dataUrl[1] : format,
|
|
url);
|
|
}
|
|
|
|
|
|
module.exports = {
|
|
defaultLoad,
|
|
defaultLoadSync,
|
|
getSourceSync,
|
|
throwUnknownModuleFormat,
|
|
};
|