mirror of
https://github.com/nodejs/node.git
synced 2026-11-09 15:11:11 -05:00
Adds a "worker" package condition that is applied within Web Worker threads created via the `Worker` global, for the worker entry point, its dependencies, `import()`, `require()` and `import.meta.resolve()`. Threads created via `worker_threads`, including from within a Web Worker, do not apply the condition. Other conditions are unaffected. Assisted-by: OpenCode Signed-off-by: Guy Bedford <guybedford@gmail.com> PR-URL: https://github.com/nodejs/node/pull/66635 Reviewed-By: James M Snell <jasnell@gmail.com>
742 lines
24 KiB
JavaScript
742 lines
24 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
ArrayPrototypeForEach,
|
|
ArrayPrototypePush,
|
|
MapPrototypeForEach,
|
|
MapPrototypeGet,
|
|
ObjectDefineProperty,
|
|
ObjectFreeze,
|
|
ObjectKeys,
|
|
ObjectPrototypeHasOwnProperty,
|
|
ReflectApply,
|
|
SafeMap,
|
|
SafeSet,
|
|
StringPrototypeCharCodeAt,
|
|
StringPrototypeIncludes,
|
|
StringPrototypeSlice,
|
|
StringPrototypeStartsWith,
|
|
Symbol,
|
|
} = primordials;
|
|
const {
|
|
ERR_INVALID_ARG_TYPE,
|
|
ERR_INVALID_RETURN_PROPERTY_VALUE,
|
|
ERR_UNKNOWN_BUILTIN_MODULE,
|
|
} = require('internal/errors').codes;
|
|
const { BuiltinModule } = require('internal/bootstrap/realm');
|
|
|
|
const { validateString } = require('internal/validators');
|
|
const fs = require('fs'); // Import all of `fs` so that it can be monkey-patched.
|
|
const internalFS = require('internal/fs/utils');
|
|
const path = require('path');
|
|
const { pathToFileURL, fileURLToPath, URL } = require('internal/url');
|
|
const assert = require('internal/assert');
|
|
|
|
const { getOptionValue } = require('internal/options');
|
|
const { kEmptyObject, setOwnProperty, getLazy } = require('internal/util');
|
|
const { inspect } = require('internal/util/inspect');
|
|
const { emitWarningSync } = require('internal/process/warning');
|
|
const {
|
|
privateSymbols: {
|
|
module_first_parent_private_symbol: kFirstModuleParent,
|
|
},
|
|
} = internalBinding('util');
|
|
const lazyTmpdir = getLazy(() => require('os').tmpdir());
|
|
const { join } = path;
|
|
|
|
const internalFsBinding = internalBinding('fs');
|
|
const { canParse: URLCanParse } = internalBinding('url');
|
|
const modulesBinding = internalBinding('modules');
|
|
const {
|
|
enableCompileCache: _enableCompileCache,
|
|
getCompileCacheDir: _getCompileCacheDir,
|
|
compileCacheStatus: _compileCacheStatus,
|
|
flushCompileCache,
|
|
} = modulesBinding;
|
|
|
|
const lazyCJSLoader = getLazy(() => require('internal/modules/cjs/loader'));
|
|
let debug = require('internal/util/debuglog').debuglog('module', (fn) => {
|
|
debug = fn;
|
|
});
|
|
|
|
/** @typedef {import('internal/modules/cjs/loader.js').Module} Module */
|
|
|
|
/**
|
|
* Cache for storing resolved real paths of modules.
|
|
* In order to minimize unnecessary lstat() calls, this cache is a list of known-real paths.
|
|
* Set to an empty Map to reset.
|
|
* @type {Map<string, string>}
|
|
*/
|
|
const realpathCache = new SafeMap();
|
|
|
|
// Toggleable loader hooks for VFS support. Overrides that return
|
|
// `undefined` fall through to the pinned native implementation.
|
|
const nativeLoaderMethods = {
|
|
internalModuleStat: (filename) => internalFsBinding.internalModuleStat(filename),
|
|
readFileSync: (filename, options) => fs.readFileSync(filename, options),
|
|
realpathSync: (requestPath) => fs.realpathSync(requestPath, {
|
|
[internalFS.realpathCacheKey]: realpathCache,
|
|
}),
|
|
legacyMainResolve: (pkgPath, main, base) =>
|
|
internalFsBinding.legacyMainResolve(pkgPath, main, base),
|
|
getFormatOfExtensionlessFile: (filePath) =>
|
|
internalFsBinding.getFormatOfExtensionlessFile(filePath),
|
|
readPackageJSON: (jsonPath, isESM, base, specifier) =>
|
|
modulesBinding.readPackageJSON(jsonPath, isESM, base, specifier),
|
|
getNearestParentPackageJSON: (checkPath) =>
|
|
modulesBinding.getNearestParentPackageJSON(checkPath),
|
|
getRootPackageJSON: (checkPath) =>
|
|
modulesBinding.getRootPackageJSON(checkPath),
|
|
getNearestParentPackageJSONType: (checkPath) =>
|
|
modulesBinding.getNearestParentPackageJSONType(checkPath),
|
|
getPackageScopeConfig: (resolved) =>
|
|
modulesBinding.getPackageScopeConfig(resolved),
|
|
getPackageType: (url) => modulesBinding.getPackageType(url),
|
|
// Synthetic hook with no native counterpart: returns the directory at
|
|
// which upward walks (node_modules lookups) must stop for `path`, or
|
|
// undefined to walk to the file system root.
|
|
getResolutionRoot: () => undefined,
|
|
};
|
|
const loaderMethodKeys = ObjectKeys(nativeLoaderMethods);
|
|
// Overrides use this sentinel when they handled a request but their method's
|
|
// meaningful result is `undefined`. A plain `undefined` means fall through to
|
|
// the native implementation.
|
|
const kLoaderOverrideNoResult = Symbol('kLoaderOverrideNoResult');
|
|
const loaderOverrides = new SafeMap();
|
|
let hasLoaderOverrides = false;
|
|
|
|
/**
|
|
* @param {string} key Name of the wrapped method in `nativeLoaderMethods`.
|
|
* @param {Function} originalFn Native fallback pinned at load time.
|
|
* @returns {Function} Wrapper that consults `loaderOverrides` and falls
|
|
* through to `originalFn` when no override is set or when the
|
|
* override returns `undefined`.
|
|
*/
|
|
function wrapLoaderMethod(key, originalFn) {
|
|
return function loaderMethodWrapper(...args) {
|
|
if (hasLoaderOverrides) {
|
|
const override = MapPrototypeGet(loaderOverrides, key);
|
|
if (override !== undefined) {
|
|
const result = ReflectApply(override, undefined, args);
|
|
if (result === kLoaderOverrideNoResult) { return undefined; }
|
|
if (result !== undefined) { return result; }
|
|
}
|
|
}
|
|
return ReflectApply(originalFn, undefined, args);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Toggleable versions of the loader's fs and package.json methods, keyed
|
|
* like `nativeLoaderMethods`. The module loader calls these instead of the
|
|
* native implementations so that VFS overrides can be consulted first.
|
|
*/
|
|
const loaderMethods = { __proto__: null };
|
|
for (let i = 0; i < loaderMethodKeys.length; i++) {
|
|
const key = loaderMethodKeys[i];
|
|
loaderMethods[key] = wrapLoaderMethod(key, nativeLoaderMethods[key]);
|
|
}
|
|
|
|
/**
|
|
* Set override functions for the module loader's fs and package.json
|
|
* operations. Fields are keyed like `nativeLoaderMethods`; missing or
|
|
* nullish fields disable that hook (fast-path restored).
|
|
* @param {Record<string, Function|undefined>} overrides
|
|
*/
|
|
function setLoaderOverrides(overrides = kEmptyObject) {
|
|
for (let i = 0; i < loaderMethodKeys.length; i++) {
|
|
const key = loaderMethodKeys[i];
|
|
const override = overrides[key];
|
|
if (override) {
|
|
loaderOverrides.set(key, override);
|
|
} else {
|
|
loaderOverrides.delete(key);
|
|
}
|
|
}
|
|
hasLoaderOverrides = loaderOverrides.size > 0;
|
|
}
|
|
|
|
/**
|
|
* Delete every entry in `cache` whose owned string is under
|
|
* `mountPoint`. `cache` is either a plain object (Module._cache,
|
|
* Module._pathCache) or a SafeMap (statCache, realpathCache,
|
|
* the package.json caches). By default the entry KEY is tested;
|
|
* pass `byValue: true` to test the entry value instead (used for
|
|
* Module._pathCache whose keys are `request\0parent` tokens and
|
|
* whose values are the resolved filenames).
|
|
*
|
|
* Mount points live in the reserved VFS namespace so a prefix scan
|
|
* on strings is exact - real-fs entries never match.
|
|
* @param {object|Map<string, *>} cache
|
|
* @param {string} mountPoint Absolute mount-point path
|
|
* @param {boolean} [byValue] Test the entry value instead of the key
|
|
*/
|
|
function cleanForVfsPrefix(cache, mountPoint, byValue = false) {
|
|
const prefix = mountPoint + path.sep;
|
|
const owns = (s) =>
|
|
typeof s === 'string' &&
|
|
(s === mountPoint || StringPrototypeStartsWith(s, prefix));
|
|
if (typeof cache.forEach === 'function') {
|
|
MapPrototypeForEach(cache, (v, k) => {
|
|
if (owns(byValue ? v : k)) { cache.delete(k); }
|
|
});
|
|
} else {
|
|
ArrayPrototypeForEach(ObjectKeys(cache), (k) => {
|
|
if (owns(byValue ? cache[k] : k)) { delete cache[k]; }
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Drop realpath-cache entries under an unmounted VFS mount point.
|
|
* Called by internal/vfs/setup.js.
|
|
* @param {string} mountPoint Absolute mount-point path
|
|
*/
|
|
function purgeRealpathCacheForPrefix(mountPoint) {
|
|
cleanForVfsPrefix(realpathCache, mountPoint);
|
|
}
|
|
|
|
/** Whether the `worker` condition applies, i.e. this is a Web Worker thread. */
|
|
let workerCondition = false;
|
|
|
|
function enableWorkerCondition() {
|
|
workerCondition = true;
|
|
}
|
|
|
|
function hasWorkerCondition() {
|
|
return workerCondition;
|
|
}
|
|
|
|
/** @type {Set<string>} */
|
|
let cjsConditions;
|
|
/** @type {string[]} */
|
|
let cjsConditionsArray;
|
|
|
|
/**
|
|
* Define the conditions that apply to the CommonJS loader.
|
|
* @returns {void}
|
|
*/
|
|
function initializeCjsConditions() {
|
|
const userConditions = getOptionValue('--conditions');
|
|
const noAddons = getOptionValue('--no-addons');
|
|
const addonConditions = noAddons ? [] : ['node-addons'];
|
|
// TODO: Use this set when resolving pkg#exports conditions in loader.js.
|
|
cjsConditionsArray = [
|
|
'require',
|
|
'node',
|
|
...addonConditions,
|
|
...(workerCondition ? ['worker'] : []),
|
|
...userConditions,
|
|
];
|
|
if (getOptionValue('--require-module')) {
|
|
cjsConditionsArray.push('module-sync');
|
|
}
|
|
ObjectFreeze(cjsConditionsArray);
|
|
cjsConditions = new SafeSet(cjsConditionsArray);
|
|
}
|
|
|
|
/**
|
|
* Get the conditions that apply to the CommonJS loader.
|
|
* @returns {Set<string>}
|
|
*/
|
|
function getCjsConditions() {
|
|
if (cjsConditions === undefined) {
|
|
initializeCjsConditions();
|
|
}
|
|
return cjsConditions;
|
|
}
|
|
|
|
function getCjsConditionsArray() {
|
|
if (cjsConditionsArray === undefined) {
|
|
initializeCjsConditions();
|
|
}
|
|
return cjsConditionsArray;
|
|
}
|
|
|
|
/**
|
|
* Provide one of Node.js' public modules to user code.
|
|
* @param {string} id - The identifier/specifier of the builtin module to load
|
|
* @returns {object|undefined}
|
|
*/
|
|
function loadBuiltinModule(id) {
|
|
if (!BuiltinModule.canBeRequiredByUsers(id)) {
|
|
return;
|
|
}
|
|
/** @type {import('internal/bootstrap/realm.js').BuiltinModule} */
|
|
const mod = BuiltinModule.map.get(id);
|
|
debug('load built-in module %s', id);
|
|
// compileForPublicLoader() throws if canBeRequiredByUsers is false:
|
|
mod.compileForPublicLoader();
|
|
return mod;
|
|
}
|
|
|
|
let isSEABuiltinWarningNeeded_;
|
|
function isSEABuiltinWarningNeeded() {
|
|
if (isSEABuiltinWarningNeeded_ === undefined) {
|
|
const { isExperimentalSeaWarningNeeded, isSea } = internalBinding('sea');
|
|
isSEABuiltinWarningNeeded_ = isSea() && isExperimentalSeaWarningNeeded();
|
|
}
|
|
return isSEABuiltinWarningNeeded_;
|
|
}
|
|
|
|
let warnedAboutBuiltins = false;
|
|
/**
|
|
* Helpers to load built-in modules for embedder modules.
|
|
* @param {string} id
|
|
* @returns {import('internal/bootstrap/realm.js').BuiltinModule}
|
|
*/
|
|
function loadBuiltinModuleForEmbedder(id) {
|
|
const normalized = BuiltinModule.normalizeRequirableId(id);
|
|
if (normalized) {
|
|
const mod = loadBuiltinModule(normalized);
|
|
if (mod) {
|
|
return mod;
|
|
}
|
|
}
|
|
if (isSEABuiltinWarningNeeded() && !warnedAboutBuiltins) {
|
|
emitWarningSync(
|
|
'Currently the require() provided to the main script embedded into ' +
|
|
'single-executable applications only supports loading built-in modules.\n' +
|
|
'To load a module from disk after the single executable application is ' +
|
|
'launched, use require("module").createRequire().\n' +
|
|
'Support for bundled module loading or virtual file systems are under ' +
|
|
'discussions in https://github.com/nodejs/single-executable');
|
|
warnedAboutBuiltins = true;
|
|
}
|
|
throw new ERR_UNKNOWN_BUILTIN_MODULE(id);
|
|
}
|
|
|
|
/** @type {Module} */
|
|
let $Module = null;
|
|
/**
|
|
* Import the Module class on first use.
|
|
* @returns {object}
|
|
*/
|
|
function lazyModule() {
|
|
return $Module ??= require('internal/modules/cjs/loader').Module;
|
|
}
|
|
|
|
/**
|
|
* Create the module-scoped `require` function to pass into CommonJS modules.
|
|
* @param {Module} mod - The module to create the `require` function for.
|
|
* @returns {function(string): unknown}
|
|
*/
|
|
function makeRequireFunction(mod) {
|
|
// lazy due to cycle
|
|
const Module = lazyModule();
|
|
if (mod instanceof Module !== true) {
|
|
throw new ERR_INVALID_ARG_TYPE('mod', 'Module', mod);
|
|
}
|
|
|
|
function require(path) {
|
|
return mod.require(path);
|
|
}
|
|
|
|
/**
|
|
* The `resolve` method that gets attached to module-scope `require`.
|
|
* @param {string} request
|
|
* @param {Parameters<Module['_resolveFilename']>[3]} options
|
|
* @returns {string}
|
|
*/
|
|
function resolve(request, options) {
|
|
validateString(request, 'request');
|
|
const { resolveForCJSWithHooks } = lazyCJSLoader();
|
|
// require.resolve() has different behaviors from the internal resolution used by
|
|
// Module._load:
|
|
// 1. When the request resolves to a non-existent built-in, it throws MODULE_NOT_FOUND
|
|
// instead of UNKNOWN_BUILTIN_MODULE. This is handled by resolveForCJSWithHooks.
|
|
// 2. If the request is a prefixed built-in, the returned value is also prefixed. This
|
|
// is handled below.
|
|
const { filename, url } = resolveForCJSWithHooks(
|
|
request, mod, /* isMain */ false, {
|
|
__proto__: null,
|
|
shouldSkipModuleHooks: false,
|
|
requireResolveOptions: options ?? {},
|
|
});
|
|
if (url === request && StringPrototypeStartsWith(request, 'node:')) {
|
|
return url;
|
|
}
|
|
return filename;
|
|
}
|
|
|
|
require.resolve = resolve;
|
|
|
|
/**
|
|
* The `paths` method that gets attached to module-scope `require`.
|
|
* @param {string} request
|
|
* @returns {object}
|
|
*/
|
|
function paths(request) {
|
|
validateString(request, 'request');
|
|
return Module._resolveLookupPaths(request, mod);
|
|
}
|
|
|
|
resolve.paths = paths;
|
|
|
|
setOwnProperty(require, 'main', process.mainModule);
|
|
|
|
// Enable support to add extra extension types.
|
|
require.extensions = Module._extensions;
|
|
|
|
require.cache = Module._cache;
|
|
|
|
return require;
|
|
}
|
|
|
|
/**
|
|
* Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
|
|
* because the buffer-to-string conversion in `fs.readFileSync()`
|
|
* translates it to FEFF, the UTF-16 BOM.
|
|
* @param {string} content
|
|
* @returns {string}
|
|
*/
|
|
function stripBOM(content) {
|
|
if (StringPrototypeCharCodeAt(content) === 0xFEFF) {
|
|
content = StringPrototypeSlice(content, 1);
|
|
}
|
|
return content;
|
|
}
|
|
|
|
/**
|
|
* Add built-in modules to a global or REPL scope object.
|
|
* @param {Record<string, unknown>} object - The object such as `globalThis` to add the built-in modules to.
|
|
* @param {string} dummyModuleName - The label representing the set of built-in modules to add.
|
|
*/
|
|
function addBuiltinLibsToObject(object, dummyModuleName) {
|
|
// Make built-in modules available directly (loaded lazily).
|
|
const Module = require('internal/modules/cjs/loader').Module;
|
|
const { builtinModules } = Module;
|
|
|
|
// To require built-in modules in user-land and ignore modules whose
|
|
// `canBeRequiredByUsers` is false. So we create a dummy module object and not
|
|
// use `require()` directly.
|
|
const dummyModule = new Module(dummyModuleName);
|
|
|
|
ArrayPrototypeForEach(builtinModules, (name) => {
|
|
// Neither add underscored modules, nor ones that contain slashes (e.g.,
|
|
// 'fs/promises') or ones that are already defined.
|
|
if (name[0] === '_' ||
|
|
StringPrototypeIncludes(name, '/') ||
|
|
ObjectPrototypeHasOwnProperty(object, name)) {
|
|
return;
|
|
}
|
|
// Goals of this mechanism are:
|
|
// - Lazy loading of built-in modules
|
|
// - Having all built-in modules available as non-enumerable properties
|
|
// - Allowing the user to re-assign these variables as if there were no
|
|
// pre-existing globals with the same name.
|
|
|
|
const setReal = (val) => {
|
|
// Deleting the property before re-assigning it disables the
|
|
// getter/setter mechanism.
|
|
delete object[name];
|
|
object[name] = val;
|
|
};
|
|
|
|
ObjectDefineProperty(object, name, {
|
|
__proto__: null,
|
|
get: () => {
|
|
const lib = dummyModule.require(name);
|
|
|
|
try {
|
|
// Override the current getter/setter and set up a new
|
|
// non-enumerable property.
|
|
ObjectDefineProperty(object, name, {
|
|
__proto__: null,
|
|
get: () => lib,
|
|
set: setReal,
|
|
configurable: true,
|
|
enumerable: false,
|
|
});
|
|
} catch {
|
|
// If the property is no longer configurable, ignore the error.
|
|
}
|
|
|
|
return lib;
|
|
},
|
|
set: setReal,
|
|
configurable: true,
|
|
enumerable: false,
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Normalize the referrer name as a URL.
|
|
* If it's a string containing an absolute path or a URL it's normalized as
|
|
* a URL string.
|
|
* Otherwise it's returned as undefined.
|
|
* @param {string | null | undefined} referrerName
|
|
* @returns {string | undefined}
|
|
*/
|
|
function normalizeReferrerURL(referrerName) {
|
|
if (referrerName === null || referrerName === undefined) {
|
|
return undefined;
|
|
}
|
|
|
|
if (typeof referrerName === 'string') {
|
|
if (path.isAbsolute(referrerName)) {
|
|
return pathToFileURL(referrerName).href;
|
|
}
|
|
|
|
if (StringPrototypeStartsWith(referrerName, 'file://') ||
|
|
URLCanParse(referrerName)) {
|
|
return referrerName;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
assert.fail('Unreachable code reached by ' + inspect(referrerName));
|
|
}
|
|
|
|
/**
|
|
* Coerce a URL string to a filename. This is used by the ESM loader
|
|
* to map ESM URLs to entries in the CJS module cache on a best-effort basis.
|
|
* TODO(joyeecheung): this can be rather expensive, cache the result on the
|
|
* ModuleWrap wherever we can.
|
|
* @param {string|undefined} url URL to convert to filename
|
|
* @returns {string|undefined}
|
|
*/
|
|
function urlToFilename(url) {
|
|
if (url && StringPrototypeStartsWith(url, 'file://')) {
|
|
let urlObj;
|
|
try {
|
|
urlObj = new URL(url);
|
|
} catch {
|
|
// Not a proper URL, return as-is as the cache key.
|
|
return url;
|
|
}
|
|
try {
|
|
return fileURLToPath(urlObj);
|
|
} catch {
|
|
// This is generally only possible when the URL is provided by a custom loader.
|
|
// Just use the path and ignore whether it's absolute or not as there's no such
|
|
// requirement for CJS cache.
|
|
return urlObj.pathname;
|
|
}
|
|
}
|
|
// Not a file URL, return as-is.
|
|
return url;
|
|
}
|
|
|
|
// Whether we have started executing any user-provided CJS code.
|
|
// This is set right before we call the wrapped CJS code (not after,
|
|
// in case we are half-way in the execution when internals check this).
|
|
// Used for internal assertions.
|
|
let _hasStartedUserCJSExecution = false;
|
|
// Similar to _hasStartedUserCJSExecution but for ESM. This is set
|
|
// right before ESM evaluation in the default ESM loader. We do not
|
|
// update this during vm SourceTextModule execution because at that point
|
|
// some user code must already have been run to execute code via vm
|
|
// there is little value checking whether any user JS code is run anyway.
|
|
let _hasStartedUserESMExecution = false;
|
|
|
|
/**
|
|
* Load a public built-in module. ID may or may not be prefixed by `node:` and
|
|
* will be normalized.
|
|
* @param {string} id ID of the built-in to be loaded.
|
|
* @returns {object|undefined} exports of the built-in. Undefined if the built-in
|
|
* does not exist.
|
|
*/
|
|
function getBuiltinModule(id) {
|
|
validateString(id, 'id');
|
|
const normalizedId = BuiltinModule.normalizeRequirableId(id);
|
|
return normalizedId ? require(normalizedId) : undefined;
|
|
}
|
|
|
|
/** @type {import('internal/util/types')} */
|
|
let _TYPES = null;
|
|
/**
|
|
* Lazily loads and returns the internal/util/types module.
|
|
* @returns {object}
|
|
*/
|
|
function lazyTypes() {
|
|
if (_TYPES !== null) { return _TYPES; }
|
|
return _TYPES = require('internal/util/types');
|
|
}
|
|
|
|
|
|
/**
|
|
* Asserts that the given body is a buffer source (either a string, array buffer, or typed array).
|
|
* Throws an error if the body is not a buffer source.
|
|
* @param {string | ArrayBufferView | ArrayBuffer} body - The body to check.
|
|
* @param {boolean} allowString - Whether or not to allow a string as a valid buffer source.
|
|
* @param {string} hookName - The name of the hook being called.
|
|
* @throws {ERR_INVALID_RETURN_PROPERTY_VALUE} If the body is not a buffer source.
|
|
* @returns {void}
|
|
*/
|
|
function assertBufferSource(body, allowString, hookName) {
|
|
if (allowString && typeof body === 'string') {
|
|
return;
|
|
}
|
|
const { isArrayBufferView, isAnyArrayBuffer } = lazyTypes();
|
|
if (isArrayBufferView(body) || isAnyArrayBuffer(body)) {
|
|
return;
|
|
}
|
|
throw new ERR_INVALID_RETURN_PROPERTY_VALUE(
|
|
`${allowString ? 'string, ' : ''}array buffer, or typed array`,
|
|
hookName,
|
|
'source',
|
|
body,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Converts a buffer or buffer-like object to a string.
|
|
* @param {string | ArrayBuffer | ArrayBufferView} body - The buffer or buffer-like object to convert to a string.
|
|
* @returns {string} The resulting string.
|
|
*/
|
|
function stringify(body) {
|
|
if (typeof body === 'string') { return body; }
|
|
assertBufferSource(body, false, 'load');
|
|
return require('internal/encoding').getUtf8Decoder().decode(body);
|
|
}
|
|
|
|
/**
|
|
* Enable on-disk compiled cache for all user modules being compiled in the current Node.js instance
|
|
* after this method is called.
|
|
* This method accepts either:
|
|
* - A string: path to the cache directory.
|
|
* - An options object `{directory?: string, portable?: boolean, readOnly?: boolean}`:
|
|
* - `directory`: A string path to the cache directory.
|
|
* - `portable`: If `portable` is true, the cache directory will be considered relative.
|
|
* Defaults to `NODE_COMPILE_CACHE_PORTABLE === '1'`.
|
|
* - `readOnly`: If `readOnly` is true, existing cache entries are used but nothing is
|
|
* written, and the cache directory is not created. Defaults to
|
|
* `NODE_COMPILE_CACHE_READONLY === '1'`.
|
|
* If cache directory is undefined, it defaults to the `NODE_COMPILE_CACHE` environment variable.
|
|
* If `NODE_COMPILE_CACHE` isn't set, it defaults to `path.join(os.tmpdir(), 'node-compile-cache')`.
|
|
* @param {string | { directory?: string, portable?: boolean, readOnly?: boolean } | undefined} options
|
|
* @returns {{status: number, message?: string, directory?: string}}
|
|
*/
|
|
function enableCompileCache(options) {
|
|
let portable;
|
|
let readOnly;
|
|
let directory;
|
|
|
|
if (typeof options === 'object' && options !== null) {
|
|
({ directory, portable, readOnly } = options);
|
|
} else {
|
|
directory = options;
|
|
}
|
|
if (directory === undefined) {
|
|
directory = process.env.NODE_COMPILE_CACHE || join(lazyTmpdir(), 'node-compile-cache');
|
|
}
|
|
if (portable === undefined) {
|
|
portable = process.env.NODE_COMPILE_CACHE_PORTABLE === '1';
|
|
}
|
|
if (readOnly === undefined) {
|
|
readOnly = process.env.NODE_COMPILE_CACHE_READONLY === '1';
|
|
}
|
|
const nativeResult = _enableCompileCache(directory, portable, readOnly);
|
|
const result = { status: nativeResult[0] };
|
|
if (nativeResult[1]) {
|
|
result.message = nativeResult[1];
|
|
}
|
|
if (nativeResult[2]) {
|
|
result.directory = nativeResult[2];
|
|
}
|
|
return result;
|
|
}
|
|
|
|
const compileCacheStatus = { __proto__: null };
|
|
for (let i = 0; i < _compileCacheStatus.length; ++i) {
|
|
compileCacheStatus[_compileCacheStatus[i]] = i;
|
|
}
|
|
ObjectFreeze(compileCacheStatus);
|
|
const constants = { __proto__: null, compileCacheStatus };
|
|
ObjectFreeze(constants);
|
|
|
|
/**
|
|
* Get the compile cache directory if on-disk compile cache is enabled.
|
|
* @returns {string|undefined} Path to the module compile cache directory if it is enabled,
|
|
* or undefined otherwise.
|
|
*/
|
|
function getCompileCacheDir() {
|
|
return _getCompileCacheDir() || undefined;
|
|
}
|
|
|
|
function getRequireStack(parent) {
|
|
const requireStack = [];
|
|
for (let cursor = parent;
|
|
cursor;
|
|
// TODO(joyeecheung): it makes more sense to use kLastModuleParent here.
|
|
cursor = cursor[kFirstModuleParent]) {
|
|
ArrayPrototypePush(requireStack, cursor.filename || cursor.id);
|
|
}
|
|
return requireStack;
|
|
}
|
|
|
|
// Suffixes tried by legacy CommonJS `main` resolution; indexes match
|
|
// the values returned by the `legacyMainResolve` C++ binding.
|
|
const legacyMainResolveExtensions = [
|
|
'',
|
|
'.js',
|
|
'.json',
|
|
'.node',
|
|
'/index.js',
|
|
'/index.json',
|
|
'/index.node',
|
|
'./index.js',
|
|
'./index.json',
|
|
'./index.node',
|
|
];
|
|
|
|
const legacyMainResolveExtensionsIndexes = {
|
|
// 0-6: when packageConfig.main is defined
|
|
kResolvedByMain: 0,
|
|
kResolvedByMainJs: 1,
|
|
kResolvedByMainJson: 2,
|
|
kResolvedByMainNode: 3,
|
|
kResolvedByMainIndexJs: 4,
|
|
kResolvedByMainIndexJson: 5,
|
|
kResolvedByMainIndexNode: 6,
|
|
// 7-9: when packageConfig.main is not defined
|
|
kResolvedByPackageAndJs: 7,
|
|
kResolvedByPackageAndJson: 8,
|
|
kResolvedByPackageAndNode: 9,
|
|
};
|
|
|
|
module.exports = {
|
|
addBuiltinLibsToObject,
|
|
assertBufferSource,
|
|
cleanForVfsPrefix,
|
|
purgeRealpathCacheForPrefix,
|
|
constants,
|
|
enableCompileCache,
|
|
enableWorkerCondition,
|
|
flushCompileCache,
|
|
getBuiltinModule,
|
|
getCjsConditions,
|
|
getCjsConditionsArray,
|
|
getCompileCacheDir,
|
|
hasWorkerCondition,
|
|
initializeCjsConditions,
|
|
legacyMainResolveExtensions,
|
|
legacyMainResolveExtensionsIndexes,
|
|
kLoaderOverrideNoResult,
|
|
loaderMethods,
|
|
loadBuiltinModuleForEmbedder,
|
|
loadBuiltinModule,
|
|
makeRequireFunction,
|
|
normalizeReferrerURL,
|
|
setLoaderOverrides,
|
|
stringify,
|
|
stripBOM,
|
|
hasStartedUserCJSExecution() {
|
|
return _hasStartedUserCJSExecution;
|
|
},
|
|
setHasStartedUserCJSExecution() {
|
|
_hasStartedUserCJSExecution = true;
|
|
},
|
|
hasStartedUserESMExecution() {
|
|
return _hasStartedUserESMExecution;
|
|
},
|
|
setHasStartedUserESMExecution() {
|
|
_hasStartedUserESMExecution = true;
|
|
},
|
|
urlToFilename,
|
|
getRequireStack,
|
|
};
|