UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

253 lines (221 loc) 7.53 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/modules/esm/load.js import { kEmptyObject } from "nstdlib/lib/internal/util"; import { defaultGetFormat } from "nstdlib/lib/internal/modules/esm/get_format"; import { validateAttributes, emitImportAssertionWarning, } from "nstdlib/lib/internal/modules/esm/assert"; import { getOptionValue } from "nstdlib/lib/internal/options"; import { readFileSync } from "nstdlib/lib/fs"; import { Buffer as __Buffer__ } from "nstdlib/lib/buffer"; import { isUnderNodeModules } from "nstdlib/lib/internal/modules/helpers"; import { URL } from "nstdlib/lib/internal/url"; import { codes as __codes__ } from "nstdlib/lib/internal/errors"; import * as __hoisted_internal_fs_promises__ from "nstdlib/lib/internal/fs/promises"; const defaultType = getOptionValue("--experimental-default-type"); const { from: BufferFrom } = __Buffer__; const { ERR_INVALID_URL, ERR_UNKNOWN_MODULE_FORMAT, ERR_UNSUPPORTED_ESM_URL_SCHEME, ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING, } = __codes__; const DATA_URL_PATTERN = /^[^/]+\/[^,;]+(?:[^,]*?)(;base64)?,([\s\S]*)$/; /** * @param {URL} url URL to the module * @param {ESModuleContext} context used to decorate error messages * @returns {Promise<{ responseURL: string, source: string | BufferView }>} */ async function getSource(url, context) { const { protocol, href } = url; const responseURL = href; let source; if (protocol === "file:") { const { exports: { readFile: readFileAsync }, } = __hoisted_internal_fs_promises__; source = await readFileAsync(url); } else if (protocol === "data:") { const match = RegExp.prototype.exec.call(DATA_URL_PATTERN, url.pathname); if (!match) { throw new ERR_INVALID_URL(responseURL); } const { 1: base64, 2: body } = match; source = BufferFrom(decodeURIComponent(body), base64 ? "base64" : "utf8"); } else { const supportedSchemes = ["file", "data"]; throw new ERR_UNSUPPORTED_ESM_URL_SCHEME(url, supportedSchemes); } return { __proto__: null, responseURL, source }; } /** * @param {URL} url URL to the module * @param {ESModuleContext} 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:") { source = readFileSync(url); } else if (protocol === "data:") { const match = RegExp.prototype.exec.call(DATA_URL_PATTERN, url.pathname); if (!match) { throw new ERR_INVALID_URL(responseURL); } const { 1: base64, 2: body } = match; source = BufferFrom(decodeURIComponent(body), base64 ? "base64" : "utf8"); } 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} */ async 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 !== "commonjs" || defaultType === "module") { if (source == null) { ({ responseURL, source } = await getSource(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 = await 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); // Use the synchronous commonjs translator which can deal with cycles. if ( format === "commonjs" && getOptionValue("--experimental-require-module") ) { format = "commonjs-sync"; } if ( getOptionValue("--experimental-strip-types") && (format === "module-typescript" || format === "commonjs-typescript") && isUnderNodeModules(url) ) { throw new ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING(url); } 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; } else if (source == null) { ({ responseURL, source } = getSourceSync(urlInstance, context)); context.source = source; } format ??= defaultGetFormat(urlInstance, context); validateAttributes(url, format, importAttributes); // Use the synchronous commonjs translator which can deal with cycles. if ( format === "commonjs" && getOptionValue("--experimental-require-module") ) { format = "commonjs-sync"; } 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 | -0 | 0n | ''} format Falsy format returned from `load` */ function throwUnknownModuleFormat(url, format) { const dataUrl = RegExp.prototype.exec.call( /^data:([^/]+\/[^;,]+)(?:[^,]*?)(;base64)?,/, url, ); throw new ERR_UNKNOWN_MODULE_FORMAT(dataUrl ? dataUrl[1] : format, url); } export { defaultLoad }; export { defaultLoadSync }; export { getSourceSync }; export { throwUnknownModuleFormat };