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/loaders'); 22 23const { validateString } = require('internal/validators'); 24const path = require('path'); 25const { pathToFileURL, fileURLToPath, URL } = require('internal/url'); 26 27const { getOptionValue } = require('internal/options'); 28const { setOwnProperty } = require('internal/util'); 29const userConditions = getOptionValue('--conditions'); 30 31const { 32 privateSymbols: { 33 require_private_symbol, 34 }, 35} = internalBinding('util'); 36 37let debug = require('internal/util/debuglog').debuglog('module', (fn) => { 38 debug = fn; 39}); 40 41const noAddons = getOptionValue('--no-addons'); 42const addonConditions = noAddons ? [] : ['node-addons']; 43 44// TODO: Use this set when resolving pkg#exports conditions in loader.js. 45const cjsConditions = new SafeSet([ 46 'require', 47 'node', 48 ...addonConditions, 49 ...userConditions, 50]); 51 52function loadBuiltinModule(filename, request) { 53 const mod = BuiltinModule.map.get(filename); 54 if (mod?.canBeRequiredByUsers) { 55 debug('load built-in module %s', request); 56 // compileForPublicLoader() throws if mod.canBeRequiredByUsers is false: 57 mod.compileForPublicLoader(); 58 return mod; 59 } 60} 61 62let $Module = null; 63function lazyModule() { 64 $Module = $Module || require('internal/modules/cjs/loader').Module; 65 return $Module; 66} 67 68// Invoke with makeRequireFunction(module) where |module| is the Module object 69// to use as the context for the require() function. 70// Use redirects to set up a mapping from a policy and restrict dependencies 71const urlToFileCache = new SafeMap(); 72function makeRequireFunction(mod, redirects) { 73 // lazy due to cycle 74 const Module = lazyModule(); 75 if (mod instanceof Module !== true) { 76 throw new ERR_INVALID_ARG_TYPE('mod', 'Module', mod); 77 } 78 79 let require; 80 if (redirects) { 81 const id = mod.filename || mod.id; 82 const conditions = cjsConditions; 83 const { resolve, reaction } = redirects; 84 require = function require(specifier) { 85 let missing = true; 86 const destination = resolve(specifier, conditions); 87 if (destination === true) { 88 missing = false; 89 } else if (destination) { 90 const { href, protocol } = destination; 91 if (protocol === 'node:') { 92 const specifier = destination.pathname; 93 const mod = loadBuiltinModule(specifier, href); 94 if (mod && mod.canBeRequiredByUsers) { 95 return mod.exports; 96 } 97 throw new ERR_UNKNOWN_BUILTIN_MODULE(specifier); 98 } else if (protocol === 'file:') { 99 let filepath = urlToFileCache.get(href); 100 if (!filepath) { 101 filepath = fileURLToPath(destination); 102 urlToFileCache.set(href, filepath); 103 } 104 return mod[require_private_symbol](mod, filepath); 105 } 106 } 107 if (missing) { 108 reaction(new ERR_MANIFEST_DEPENDENCY_MISSING( 109 id, 110 specifier, 111 ArrayPrototypeJoin([...conditions], ', '), 112 )); 113 } 114 return mod[require_private_symbol](mod, specifier); 115 }; 116 } else { 117 require = function require(path) { 118 // When no policy manifest, the original prototype.require is sustained 119 return mod.require(path); 120 }; 121 } 122 123 function resolve(request, options) { 124 validateString(request, 'request'); 125 return Module._resolveFilename(request, mod, false, options); 126 } 127 128 require.resolve = resolve; 129 130 function paths(request) { 131 validateString(request, 'request'); 132 return Module._resolveLookupPaths(request, mod); 133 } 134 135 resolve.paths = paths; 136 137 setOwnProperty(require, 'main', process.mainModule); 138 139 // Enable support to add extra extension types. 140 require.extensions = Module._extensions; 141 142 require.cache = Module._cache; 143 144 return require; 145} 146 147/** 148 * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM) 149 * because the buffer-to-string conversion in `fs.readFileSync()` 150 * translates it to FEFF, the UTF-16 BOM. 151 */ 152function stripBOM(content) { 153 if (StringPrototypeCharCodeAt(content) === 0xFEFF) { 154 content = StringPrototypeSlice(content, 1); 155 } 156 return content; 157} 158 159function addBuiltinLibsToObject(object, dummyModuleName) { 160 // Make built-in modules available directly (loaded lazily). 161 const Module = require('internal/modules/cjs/loader').Module; 162 const { builtinModules } = Module; 163 164 // To require built-in modules in user-land and ignore modules whose 165 // `canBeRequiredByUsers` is false. So we create a dummy module object and not 166 // use `require()` directly. 167 const dummyModule = new Module(dummyModuleName); 168 169 ArrayPrototypeForEach(builtinModules, (name) => { 170 // Neither add underscored modules, nor ones that contain slashes (e.g., 171 // 'fs/promises') or ones that are already defined. 172 if (StringPrototypeStartsWith(name, '_') || 173 StringPrototypeIncludes(name, '/') || 174 ObjectPrototypeHasOwnProperty(object, name)) { 175 return; 176 } 177 // Goals of this mechanism are: 178 // - Lazy loading of built-in modules 179 // - Having all built-in modules available as non-enumerable properties 180 // - Allowing the user to re-assign these variables as if there were no 181 // pre-existing globals with the same name. 182 183 const setReal = (val) => { 184 // Deleting the property before re-assigning it disables the 185 // getter/setter mechanism. 186 delete object[name]; 187 object[name] = val; 188 }; 189 190 ObjectDefineProperty(object, name, { 191 __proto__: null, 192 get: () => { 193 const lib = dummyModule.require(name); 194 195 try { 196 // Override the current getter/setter and set up a new 197 // non-enumerable property. 198 ObjectDefineProperty(object, name, { 199 __proto__: null, 200 get: () => lib, 201 set: setReal, 202 configurable: true, 203 enumerable: false, 204 }); 205 } catch { 206 // If the property is no longer configurable, ignore the error. 207 } 208 209 return lib; 210 }, 211 set: setReal, 212 configurable: true, 213 enumerable: false, 214 }); 215 }); 216} 217 218/** 219 * 220 * @param {string | URL} referrer 221 * @returns {string} 222 */ 223function normalizeReferrerURL(referrer) { 224 if (typeof referrer === 'string' && path.isAbsolute(referrer)) { 225 return pathToFileURL(referrer).href; 226 } 227 return new URL(referrer).href; 228} 229 230// For error messages only - used to check if ESM syntax is in use. 231function hasEsmSyntax(code) { 232 debug('Checking for ESM syntax'); 233 const parser = require('internal/deps/acorn/acorn/dist/acorn').Parser; 234 let root; 235 try { 236 root = parser.parse(code, { sourceType: 'module', ecmaVersion: 'latest' }); 237 } catch { 238 return false; 239 } 240 241 return ArrayPrototypeSome(root.body, (stmt) => 242 stmt.type === 'ExportDefaultDeclaration' || 243 stmt.type === 'ExportNamedDeclaration' || 244 stmt.type === 'ImportDeclaration' || 245 stmt.type === 'ExportAllDeclaration'); 246} 247 248module.exports = { 249 addBuiltinLibsToObject, 250 cjsConditions, 251 hasEsmSyntax, 252 loadBuiltinModule, 253 makeRequireFunction, 254 normalizeReferrerURL, 255 stripBOM, 256}; 257