1'use strict'; 2 3const { 4 ArrayPrototypeForEach, 5 ArrayPrototypeJoin, 6 ObjectDefineProperty, 7 ObjectPrototypeHasOwnProperty, 8 SafeMap, 9 SafeSet, 10 StringPrototypeCharCodeAt, 11 StringPrototypeIncludes, 12 StringPrototypeSlice, 13 StringPrototypeStartsWith, 14} = primordials; 15const { 16 ERR_MANIFEST_DEPENDENCY_MISSING, 17 ERR_UNKNOWN_BUILTIN_MODULE 18} = require('internal/errors').codes; 19const { NativeModule } = require('internal/bootstrap/loaders'); 20 21const { validateString } = require('internal/validators'); 22const path = require('path'); 23const { pathToFileURL, fileURLToPath, URL } = require('internal/url'); 24 25const { getOptionValue } = require('internal/options'); 26const userConditions = getOptionValue('--conditions'); 27 28let debug = require('internal/util/debuglog').debuglog('module', (fn) => { 29 debug = fn; 30}); 31 32const noAddons = getOptionValue('--no-addons'); 33const addonConditions = noAddons ? [] : ['node-addons']; 34 35// TODO: Use this set when resolving pkg#exports conditions in loader.js. 36const cjsConditions = new SafeSet([ 37 'require', 38 'node', 39 ...addonConditions, 40 ...userConditions, 41]); 42 43function loadNativeModule(filename, request) { 44 const mod = NativeModule.map.get(filename); 45 if (mod?.canBeRequiredByUsers) { 46 debug('load native module %s', request); 47 // compileForPublicLoader() throws if mod.canBeRequiredByUsers is false: 48 mod.compileForPublicLoader(); 49 return mod; 50 } 51} 52 53// Invoke with makeRequireFunction(module) where |module| is the Module object 54// to use as the context for the require() function. 55// Use redirects to set up a mapping from a policy and restrict dependencies 56const urlToFileCache = new SafeMap(); 57function makeRequireFunction(mod, redirects) { 58 const Module = mod.constructor; 59 60 let require; 61 if (redirects) { 62 const id = mod.filename || mod.id; 63 const conditions = cjsConditions; 64 const { resolve, reaction } = redirects; 65 require = function require(specifier) { 66 let missing = true; 67 const destination = resolve(specifier, conditions); 68 if (destination === true) { 69 missing = false; 70 } else if (destination) { 71 const href = destination.href; 72 if (destination.protocol === 'node:') { 73 const specifier = destination.pathname; 74 const mod = loadNativeModule(specifier, href); 75 if (mod && mod.canBeRequiredByUsers) { 76 return mod.exports; 77 } 78 throw new ERR_UNKNOWN_BUILTIN_MODULE(specifier); 79 } else if (destination.protocol === 'file:') { 80 let filepath; 81 if (urlToFileCache.has(href)) { 82 filepath = urlToFileCache.get(href); 83 } else { 84 filepath = fileURLToPath(destination); 85 urlToFileCache.set(href, filepath); 86 } 87 return mod.require(filepath); 88 } 89 } 90 if (missing) { 91 reaction(new ERR_MANIFEST_DEPENDENCY_MISSING( 92 id, 93 specifier, 94 ArrayPrototypeJoin([...conditions], ', ') 95 )); 96 } 97 return mod.require(specifier); 98 }; 99 } else { 100 require = function require(path) { 101 return mod.require(path); 102 }; 103 } 104 105 function resolve(request, options) { 106 validateString(request, 'request'); 107 return Module._resolveFilename(request, mod, false, options); 108 } 109 110 require.resolve = resolve; 111 112 function paths(request) { 113 validateString(request, 'request'); 114 return Module._resolveLookupPaths(request, mod); 115 } 116 117 resolve.paths = paths; 118 119 require.main = process.mainModule; 120 121 // Enable support to add extra extension types. 122 require.extensions = Module._extensions; 123 124 require.cache = Module._cache; 125 126 return require; 127} 128 129/** 130 * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM) 131 * because the buffer-to-string conversion in `fs.readFileSync()` 132 * translates it to FEFF, the UTF-16 BOM. 133 */ 134function stripBOM(content) { 135 if (StringPrototypeCharCodeAt(content) === 0xFEFF) { 136 content = StringPrototypeSlice(content, 1); 137 } 138 return content; 139} 140 141function addBuiltinLibsToObject(object) { 142 // Make built-in modules available directly (loaded lazily). 143 const { builtinModules } = require('internal/modules/cjs/loader').Module; 144 ArrayPrototypeForEach(builtinModules, (name) => { 145 // Neither add underscored modules, nor ones that contain slashes (e.g., 146 // 'fs/promises') or ones that are already defined. 147 if (StringPrototypeStartsWith(name, '_') || 148 StringPrototypeIncludes(name, '/') || 149 ObjectPrototypeHasOwnProperty(object, name)) { 150 return; 151 } 152 // Goals of this mechanism are: 153 // - Lazy loading of built-in modules 154 // - Having all built-in modules available as non-enumerable properties 155 // - Allowing the user to re-assign these variables as if there were no 156 // pre-existing globals with the same name. 157 158 const setReal = (val) => { 159 // Deleting the property before re-assigning it disables the 160 // getter/setter mechanism. 161 delete object[name]; 162 object[name] = val; 163 }; 164 165 ObjectDefineProperty(object, name, { 166 get: () => { 167 const lib = require(name); 168 169 // Disable the current getter/setter and set up a new 170 // non-enumerable property. 171 delete object[name]; 172 ObjectDefineProperty(object, name, { 173 get: () => lib, 174 set: setReal, 175 configurable: true, 176 enumerable: false 177 }); 178 179 return lib; 180 }, 181 set: setReal, 182 configurable: true, 183 enumerable: false 184 }); 185 }); 186} 187 188function normalizeReferrerURL(referrer) { 189 if (typeof referrer === 'string' && path.isAbsolute(referrer)) { 190 return pathToFileURL(referrer).href; 191 } 192 return new URL(referrer).href; 193} 194 195module.exports = { 196 addBuiltinLibsToObject, 197 cjsConditions, 198 loadNativeModule, 199 makeRequireFunction, 200 normalizeReferrerURL, 201 stripBOM, 202}; 203