1'use strict'; 2 3const { 4 ArrayPrototypeForEach, 5 ArrayPrototypeJoin, 6 ArrayPrototypeSome, 7 ObjectDefineProperty, 8 ObjectPrototypeHasOwnProperty, 9 SafeMap, 10 SafeSet, 11 StringPrototypeCharCodeAt, 12 StringPrototypeIncludes, 13 StringPrototypeSlice, 14 StringPrototypeStartsWith, 15} = primordials; 16const { 17 ERR_INVALID_ARG_TYPE, 18 ERR_MANIFEST_DEPENDENCY_MISSING, 19 ERR_UNKNOWN_BUILTIN_MODULE, 20} = require('internal/errors').codes; 21const { BuiltinModule } = require('internal/bootstrap/realm'); 22 23const { validateString } = require('internal/validators'); 24const fs = require('fs'); // Import all of `fs` so that it can be monkey-patched. 25const internalFS = require('internal/fs/utils'); 26const path = require('path'); 27const { pathToFileURL, fileURLToPath, URL } = require('internal/url'); 28 29const { getOptionValue } = require('internal/options'); 30const { setOwnProperty } = require('internal/util'); 31 32const { 33 privateSymbols: { 34 require_private_symbol, 35 }, 36} = internalBinding('util'); 37 38let debug = require('internal/util/debuglog').debuglog('module', (fn) => { 39 debug = fn; 40}); 41 42/** @typedef {import('internal/modules/cjs/loader.js').Module} Module */ 43 44/** 45 * Cache for storing resolved real paths of modules. 46 * In order to minimize unnecessary lstat() calls, this cache is a list of known-real paths. 47 * Set to an empty Map to reset. 48 * @type {Map<string, string>} 49 */ 50const realpathCache = new SafeMap(); 51/** 52 * Resolves the path of a given `require` specifier, following symlinks. 53 * @param {string} requestPath The `require` specifier 54 */ 55function toRealPath(requestPath) { 56 return fs.realpathSync(requestPath, { 57 [internalFS.realpathCacheKey]: realpathCache, 58 }); 59} 60 61/** @type {Set<string>} */ 62let cjsConditions; 63/** 64 * Define the conditions that apply to the CommonJS loader. 65 */ 66function initializeCjsConditions() { 67 const userConditions = getOptionValue('--conditions'); 68 const noAddons = getOptionValue('--no-addons'); 69 const addonConditions = noAddons ? [] : ['node-addons']; 70 // TODO: Use this set when resolving pkg#exports conditions in loader.js. 71 cjsConditions = new SafeSet([ 72 'require', 73 'node', 74 ...addonConditions, 75 ...userConditions, 76 ]); 77} 78 79/** 80 * Get the conditions that apply to the CommonJS loader. 81 */ 82function getCjsConditions() { 83 if (cjsConditions === undefined) { 84 initializeCjsConditions(); 85 } 86 return cjsConditions; 87} 88 89/** 90 * Provide one of Node.js' public modules to user code. 91 * @param {string} id - The identifier/specifier of the builtin module to load 92 * @param {string} request - The module requiring or importing the builtin module 93 */ 94function loadBuiltinModule(id, request) { 95 if (!BuiltinModule.canBeRequiredByUsers(id)) { 96 return; 97 } 98 /** @type {import('internal/bootstrap/realm.js').BuiltinModule} */ 99 const mod = BuiltinModule.map.get(id); 100 debug('load built-in module %s', request); 101 // compileForPublicLoader() throws if canBeRequiredByUsers is false: 102 mod.compileForPublicLoader(); 103 return mod; 104} 105 106/** @type {Module} */ 107let $Module = null; 108/** 109 * Import the Module class on first use. 110 */ 111function lazyModule() { 112 $Module = $Module || require('internal/modules/cjs/loader').Module; 113 return $Module; 114} 115 116/** 117 * Invoke with `makeRequireFunction(module)` where `module` is the `Module` object to use as the context for the 118 * `require()` function. 119 * Use redirects to set up a mapping from a policy and restrict dependencies. 120 */ 121const urlToFileCache = new SafeMap(); 122/** 123 * Create the module-scoped `require` function to pass into CommonJS modules. 124 * @param {Module} mod - The module to create the `require` function for. 125 * @param {ReturnType<import('internal/policy/manifest.js').Manifest['getDependencyMapper']>} redirects 126 * @typedef {(specifier: string) => unknown} RequireFunction 127 */ 128function makeRequireFunction(mod, redirects) { 129 // lazy due to cycle 130 const Module = lazyModule(); 131 if (mod instanceof Module !== true) { 132 throw new ERR_INVALID_ARG_TYPE('mod', 'Module', mod); 133 } 134 135 /** @type {RequireFunction} */ 136 let require; 137 if (redirects) { 138 const id = mod.filename || mod.id; 139 const conditions = getCjsConditions(); 140 const { resolve, reaction } = redirects; 141 require = function require(specifier) { 142 let missing = true; 143 const destination = resolve(specifier, conditions); 144 if (destination === true) { 145 missing = false; 146 } else if (destination) { 147 const { href, protocol } = destination; 148 if (protocol === 'node:') { 149 const specifier = destination.pathname; 150 151 if (BuiltinModule.canBeRequiredByUsers(specifier)) { 152 const mod = loadBuiltinModule(specifier, href); 153 return mod.exports; 154 } 155 throw new ERR_UNKNOWN_BUILTIN_MODULE(specifier); 156 } else if (protocol === 'file:') { 157 let filepath = urlToFileCache.get(href); 158 if (!filepath) { 159 filepath = fileURLToPath(destination); 160 urlToFileCache.set(href, filepath); 161 } 162 return mod[require_private_symbol](mod, filepath); 163 } 164 } 165 if (missing) { 166 reaction(new ERR_MANIFEST_DEPENDENCY_MISSING( 167 id, 168 specifier, 169 ArrayPrototypeJoin([...conditions], ', '), 170 )); 171 } 172 return mod[require_private_symbol](mod, specifier); 173 }; 174 } else { 175 require = function require(path) { 176 // When no policy manifest, the original prototype.require is sustained 177 return mod.require(path); 178 }; 179 } 180 181 /** 182 * The `resolve` method that gets attached to module-scope `require`. 183 * @param {string} request 184 * @param {Parameters<Module['_resolveFilename']>[3]} options 185 */ 186 function resolve(request, options) { 187 validateString(request, 'request'); 188 return Module._resolveFilename(request, mod, false, options); 189 } 190 191 require.resolve = resolve; 192 193 /** 194 * The `paths` method that gets attached to module-scope `require`. 195 * @param {string} request 196 */ 197 function paths(request) { 198 validateString(request, 'request'); 199 return Module._resolveLookupPaths(request, mod); 200 } 201 202 resolve.paths = paths; 203 204 setOwnProperty(require, 'main', process.mainModule); 205 206 // Enable support to add extra extension types. 207 require.extensions = Module._extensions; 208 209 require.cache = Module._cache; 210 211 return require; 212} 213 214/** 215 * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM) 216 * because the buffer-to-string conversion in `fs.readFileSync()` 217 * translates it to FEFF, the UTF-16 BOM. 218 * @param {string} content 219 */ 220function stripBOM(content) { 221 if (StringPrototypeCharCodeAt(content) === 0xFEFF) { 222 content = StringPrototypeSlice(content, 1); 223 } 224 return content; 225} 226 227/** 228 * Add built-in modules to a global or REPL scope object. 229 * @param {Record<string, unknown>} object - The object such as `globalThis` to add the built-in modules to. 230 * @param {string} dummyModuleName - The label representing the set of built-in modules to add. 231 */ 232function addBuiltinLibsToObject(object, dummyModuleName) { 233 // Make built-in modules available directly (loaded lazily). 234 const Module = require('internal/modules/cjs/loader').Module; 235 const { builtinModules } = Module; 236 237 // To require built-in modules in user-land and ignore modules whose 238 // `canBeRequiredByUsers` is false. So we create a dummy module object and not 239 // use `require()` directly. 240 const dummyModule = new Module(dummyModuleName); 241 242 ArrayPrototypeForEach(builtinModules, (name) => { 243 // Neither add underscored modules, nor ones that contain slashes (e.g., 244 // 'fs/promises') or ones that are already defined. 245 if (StringPrototypeStartsWith(name, '_') || 246 StringPrototypeIncludes(name, '/') || 247 ObjectPrototypeHasOwnProperty(object, name)) { 248 return; 249 } 250 // Goals of this mechanism are: 251 // - Lazy loading of built-in modules 252 // - Having all built-in modules available as non-enumerable properties 253 // - Allowing the user to re-assign these variables as if there were no 254 // pre-existing globals with the same name. 255 256 const setReal = (val) => { 257 // Deleting the property before re-assigning it disables the 258 // getter/setter mechanism. 259 delete object[name]; 260 object[name] = val; 261 }; 262 263 ObjectDefineProperty(object, name, { 264 __proto__: null, 265 get: () => { 266 const lib = dummyModule.require(name); 267 268 try { 269 // Override the current getter/setter and set up a new 270 // non-enumerable property. 271 ObjectDefineProperty(object, name, { 272 __proto__: null, 273 get: () => lib, 274 set: setReal, 275 configurable: true, 276 enumerable: false, 277 }); 278 } catch { 279 // If the property is no longer configurable, ignore the error. 280 } 281 282 return lib; 283 }, 284 set: setReal, 285 configurable: true, 286 enumerable: false, 287 }); 288 }); 289} 290 291/** 292 * If a referrer is an URL instance or absolute path, convert it into an URL string. 293 * @param {string | URL} referrer 294 */ 295function normalizeReferrerURL(referrer) { 296 if (typeof referrer === 'string' && path.isAbsolute(referrer)) { 297 return pathToFileURL(referrer).href; 298 } 299 return new URL(referrer).href; 300} 301 302/** 303 * For error messages only, check if ESM syntax is in use. 304 * @param {string} code 305 */ 306function hasEsmSyntax(code) { 307 debug('Checking for ESM syntax'); 308 const parser = require('internal/deps/acorn/acorn/dist/acorn').Parser; 309 let root; 310 try { 311 root = parser.parse(code, { sourceType: 'module', ecmaVersion: 'latest' }); 312 } catch { 313 return false; 314 } 315 316 return ArrayPrototypeSome(root.body, (stmt) => 317 stmt.type === 'ExportDefaultDeclaration' || 318 stmt.type === 'ExportNamedDeclaration' || 319 stmt.type === 'ImportDeclaration' || 320 stmt.type === 'ExportAllDeclaration'); 321} 322 323module.exports = { 324 addBuiltinLibsToObject, 325 getCjsConditions, 326 initializeCjsConditions, 327 hasEsmSyntax, 328 loadBuiltinModule, 329 makeRequireFunction, 330 normalizeReferrerURL, 331 stripBOM, 332 toRealPath, 333}; 334