GeezerVison is showing github.com — module: do less CJS module loader initialization at run time · nodejs/node@89ed24b
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/bootstrap/realm.js +47-12Lines changed: 47 additions & 12 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -128,11 +128,15 @@ const legacyWrapperList = new SafeSet([ | 128 | 128 | 'util', | 129 | 129 | ]); | 130 | 130 |
| | 131 | +// The code bellow assumes that the two lists must not contain any modules | | 132 | +// beginning with "internal/". | 131 | 133 | // Modules that can only be imported via the node: scheme. | 132 | 134 | const schemelessBlockList = new SafeSet([ | 133 | 135 | 'test', | 134 | 136 | 'test/reporters', | 135 | 137 | ]); | | 138 | +// Modules that will only be enabled at run time. | | 139 | +const experimentalModuleList = new SafeSet(); | 136 | 140 |
| 137 | 141 | // Set up process.binding() and process._linkedBinding(). | 138 | 142 | { | @@ -199,6 +203,20 @@ const getOwn = (target, property, receiver) => { | 199 | 203 | undefined; | 200 | 204 | }; | 201 | 205 |
| | 206 | +const publicBuiltinIds = builtinIds | | 207 | + .filter((id) => | | 208 | + !StringPrototypeStartsWith(id, 'internal/') && | | 209 | + !experimentalModuleList.has(id), | | 210 | + ); | | 211 | +// Do not expose the loaders to user land even with --expose-internals. | | 212 | +const internalBuiltinIds = builtinIds | | 213 | + .filter((id) => StringPrototypeStartsWith(id, 'internal/') && id !== selfId); | | 214 | + | | 215 | +// When --expose-internals is on we'll add the internal builtin ids to these. | | 216 | +const canBeRequiredByUsersList = new SafeSet(publicBuiltinIds); | | 217 | +const canBeRequiredByUsersWithoutSchemeList = | | 218 | + new SafeSet(publicBuiltinIds.filter((id) => !schemelessBlockList.has(id))); | | 219 | + | 202 | 220 | /** | 203 | 221 | * An internal abstraction for the built-in JavaScript modules of Node.js. | 204 | 222 | * Be careful not to expose this to user land unless --expose-internals is | @@ -216,7 +234,6 @@ class BuiltinModule { | 216 | 234 | constructor(id) { | 217 | 235 | this.filename = `${id}.js`; | 218 | 236 | this.id = id; | 219 | | - this.canBeRequiredByUsers = !StringPrototypeStartsWith(id, 'internal/'); | 220 | 237 |
| 221 | 238 | // The CJS exports object of the module. | 222 | 239 | this.exports = {}; | @@ -238,14 +255,23 @@ class BuiltinModule { | 238 | 255 | this.exportKeys = undefined; | 239 | 256 | } | 240 | 257 |
| | 258 | + static allowRequireByUsers(id) { | | 259 | + if (id === selfId) { | | 260 | + // No code because this is an assertion against bugs. | | 261 | + // eslint-disable-next-line no-restricted-syntax | | 262 | + throw new Error(`Should not allow ${id}`); | | 263 | + } | | 264 | + canBeRequiredByUsersList.add(id); | | 265 | + if (!schemelessBlockList.has(id)) { | | 266 | + canBeRequiredByUsersWithoutSchemeList.add(id); | | 267 | + } | | 268 | + } | | 269 | + | 241 | 270 | // To be called during pre-execution when --expose-internals is on. | 242 | 271 | // Enables the user-land module loader to access internal modules. | 243 | 272 | static exposeInternals() { | 244 | | - for (const { 0: id, 1: mod } of BuiltinModule.map) { | 245 | | - // Do not expose this to user land even with --expose-internals. | 246 | | - if (id !== selfId) { | 247 | | - mod.canBeRequiredByUsers = true; | 248 | | - } | | 273 | + for (let i = 0; i < internalBuiltinIds.length; ++i) { | | 274 | + BuiltinModule.allowRequireByUsers(internalBuiltinIds[i]); | 249 | 275 | } | 250 | 276 | } | 251 | 277 |
| @@ -254,14 +280,23 @@ class BuiltinModule { | 254 | 280 | } | 255 | 281 |
| 256 | 282 | static canBeRequiredByUsers(id) { | 257 | | - const mod = BuiltinModule.map.get(id); | 258 | | - return mod && mod.canBeRequiredByUsers; | | 283 | + return canBeRequiredByUsersList.has(id); | 259 | 284 | } | 260 | 285 |
| 261 | | - // Determine if a core module can be loaded without the node: prefix. This | 262 | | - // function does not validate if the module actually exists. | 263 | 286 | static canBeRequiredWithoutScheme(id) { | 264 | | - return !schemelessBlockList.has(id); | | 287 | + return canBeRequiredByUsersWithoutSchemeList.has(id); | | 288 | + } | | 289 | + | | 290 | + static isBuiltin(id) { | | 291 | + return BuiltinModule.canBeRequiredWithoutScheme(id) || ( | | 292 | + typeof id === 'string' && | | 293 | + StringPrototypeStartsWith(id, 'node:') && | | 294 | + BuiltinModule.canBeRequiredByUsers(StringPrototypeSlice(id, 5)) | | 295 | + ); | | 296 | + } | | 297 | + | | 298 | + static getCanBeRequiredByUsersWithoutSchemeList() { | | 299 | + return ArrayFrom(canBeRequiredByUsersWithoutSchemeList); | 265 | 300 | } | 266 | 301 |
| 267 | 302 | static getSchemeOnlyModuleNames() { | @@ -270,7 +305,7 @@ class BuiltinModule { | 270 | 305 |
| 271 | 306 | // Used by user-land module loaders to compile and load builtins. | 272 | 307 | compileForPublicLoader() { | 273 | | - if (!this.canBeRequiredByUsers) { | | 308 | + if (!BuiltinModule.canBeRequiredByUsers(this.id)) { | 274 | 309 | // No code because this is an assertion against bugs | 275 | 310 | // eslint-disable-next-line no-restricted-syntax | 276 | 311 | throw new Error(`Should not compile ${this.id} for public use`); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/cjs/loader.js +27-48Lines changed: 27 additions & 48 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -34,7 +34,6 @@ const { | 34 | 34 | ArrayPrototypeSplice, | 35 | 35 | ArrayPrototypeUnshift, | 36 | 36 | ArrayPrototypeUnshiftApply, | 37 | | - ArrayPrototypeFlatMap, | 38 | 37 | Boolean, | 39 | 38 | Error, | 40 | 39 | JSONParse, | @@ -52,7 +51,6 @@ const { | 52 | 51 | ReflectSet, | 53 | 52 | RegExpPrototypeExec, | 54 | 53 | SafeMap, | 55 | | - SafeSet, | 56 | 54 | SafeWeakMap, | 57 | 55 | String, | 58 | 56 | StringPrototypeCharAt, | @@ -82,7 +80,7 @@ const { | 82 | 80 | } = require('internal/source_map/source_map_cache'); | 83 | 81 | const { pathToFileURL, fileURLToPath, isURL } = require('internal/url'); | 84 | 82 | const { | 85 | | - deprecate, | | 83 | + pendingDeprecate, | 86 | 84 | emitExperimentalWarning, | 87 | 85 | kEmptyObject, | 88 | 86 | filterOwnProperties, | @@ -310,44 +308,29 @@ let debug = require('internal/util/debuglog').debuglog('module', (fn) => { | 310 | 308 | debug = fn; | 311 | 309 | }); | 312 | 310 |
| 313 | | -const builtinModules = []; | | 311 | +ObjectDefineProperty(Module.prototype, 'parent', { | | 312 | + __proto__: null, | | 313 | + get: pendingDeprecate( | | 314 | + getModuleParent, | | 315 | + 'module.parent is deprecated due to accuracy issues. Please use ' + | | 316 | + 'require.main to find program entry point instead.', | | 317 | + 'DEP0144', | | 318 | + ), | | 319 | + set: pendingDeprecate( | | 320 | + setModuleParent, | | 321 | + 'module.parent is deprecated due to accuracy issues. Please use ' + | | 322 | + 'require.main to find program entry point instead.', | | 323 | + 'DEP0144', | | 324 | + ), | | 325 | +}); | | 326 | +Module._debug = pendingDeprecate(debug, 'Module._debug is deprecated.', 'DEP0077'); | | 327 | +Module.isBuiltin = BuiltinModule.isBuiltin; | | 328 | + | 314 | 329 | // This function is called during pre-execution, before any user code is run. | 315 | 330 | function initializeCJS() { | 316 | | - const pendingDeprecation = getOptionValue('--pending-deprecation'); | 317 | | - ObjectDefineProperty(Module.prototype, 'parent', { | 318 | | - __proto__: null, | 319 | | - get: pendingDeprecation ? deprecate( | 320 | | - getModuleParent, | 321 | | - 'module.parent is deprecated due to accuracy issues. Please use ' + | 322 | | - 'require.main to find program entry point instead.', | 323 | | - 'DEP0144', | 324 | | - ) : getModuleParent, | 325 | | - set: pendingDeprecation ? deprecate( | 326 | | - setModuleParent, | 327 | | - 'module.parent is deprecated due to accuracy issues. Please use ' + | 328 | | - 'require.main to find program entry point instead.', | 329 | | - 'DEP0144', | 330 | | - ) : setModuleParent, | 331 | | - }); | 332 | | - Module._debug = deprecate(debug, 'Module._debug is deprecated.', 'DEP0077'); | 333 | | - | 334 | | - for (const { 0: id, 1: mod } of BuiltinModule.map) { | 335 | | - if (mod.canBeRequiredByUsers && | 336 | | - BuiltinModule.canBeRequiredWithoutScheme(id)) { | 337 | | - ArrayPrototypePush(builtinModules, id); | 338 | | - } | 339 | | - } | 340 | | - | 341 | | - const allBuiltins = new SafeSet( | 342 | | - ArrayPrototypeFlatMap(builtinModules, (bm) => [bm, `node:${bm}`]), | 343 | | - ); | 344 | | - BuiltinModule.getSchemeOnlyModuleNames().forEach((builtin) => allBuiltins.add(`node:${builtin}`)); | 345 | | - ObjectFreeze(builtinModules); | 346 | | - Module.builtinModules = builtinModules; | 347 | | - | 348 | | - Module.isBuiltin = function isBuiltin(moduleName) { | 349 | | - return allBuiltins.has(moduleName); | 350 | | - }; | | 331 | + // This need to be done at runtime in case --expose-internals is set. | | 332 | + const builtinModules = BuiltinModule.getCanBeRequiredByUsersWithoutSchemeList(); | | 333 | + Module.builtinModules = ObjectFreeze(builtinModules); | 351 | 334 |
| 352 | 335 | initializeCjsConditions(); | 353 | 336 |
| @@ -803,7 +786,6 @@ Module._resolveLookupPaths = function(request, parent) { | 803 | 786 | StringPrototypeStartsWith(request, 'node:') && | 804 | 787 | BuiltinModule.canBeRequiredByUsers(StringPrototypeSlice(request, 5)) | 805 | 788 | ) || ( | 806 | | - BuiltinModule.canBeRequiredByUsers(request) && | 807 | 789 | BuiltinModule.canBeRequiredWithoutScheme(request) | 808 | 790 | )) { | 809 | 791 | debug('looking for %j in []', request); | @@ -925,11 +907,11 @@ Module._load = function(request, parent, isMain) { | 925 | 907 | // Slice 'node:' prefix | 926 | 908 | const id = StringPrototypeSlice(request, 5); | 927 | 909 |
| 928 | | - const module = loadBuiltinModule(id, request); | 929 | | - if (!module?.canBeRequiredByUsers) { | | 910 | + if (!BuiltinModule.canBeRequiredByUsers(id)) { | 930 | 911 | throw new ERR_UNKNOWN_BUILTIN_MODULE(request); | 931 | 912 | } | 932 | 913 |
| | 914 | + const module = loadBuiltinModule(id, request); | 933 | 915 | return module.exports; | 934 | 916 | } | 935 | 917 |
| @@ -947,9 +929,8 @@ Module._load = function(request, parent, isMain) { | 947 | 929 | } | 948 | 930 | } | 949 | 931 |
| 950 | | - const mod = loadBuiltinModule(filename, request); | 951 | | - if (mod?.canBeRequiredByUsers && | 952 | | - BuiltinModule.canBeRequiredWithoutScheme(filename)) { | | 932 | + if (BuiltinModule.canBeRequiredWithoutScheme(filename)) { | | 933 | + const mod = loadBuiltinModule(filename, request); | 953 | 934 | return mod.exports; | 954 | 935 | } | 955 | 936 |
| @@ -1003,7 +984,6 @@ Module._resolveFilename = function(request, parent, isMain, options) { | 1003 | 984 | StringPrototypeStartsWith(request, 'node:') && | 1004 | 985 | BuiltinModule.canBeRequiredByUsers(StringPrototypeSlice(request, 5)) | 1005 | 986 | ) || ( | 1006 | | - BuiltinModule.canBeRequiredByUsers(request) && | 1007 | 987 | BuiltinModule.canBeRequiredWithoutScheme(request) | 1008 | 988 | ) | 1009 | 989 | ) { | @@ -1459,8 +1439,7 @@ Module._preloadModules = function(requests) { | 1459 | 1439 |
| 1460 | 1440 | Module.syncBuiltinESMExports = function syncBuiltinESMExports() { | 1461 | 1441 | for (const mod of BuiltinModule.map.values()) { | 1462 | | - if (mod.canBeRequiredByUsers && | 1463 | | - BuiltinModule.canBeRequiredWithoutScheme(mod.id)) { | | 1442 | + if (BuiltinModule.canBeRequiredWithoutScheme(mod.id)) { | 1464 | 1443 | mod.syncExports(); | 1465 | 1444 | } | 1466 | 1445 | } | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/hooks.js +1-2Lines changed: 1 addition & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -207,8 +207,7 @@ class Hooks { | 207 | 207 | globalThis, | 208 | 208 | // Param getBuiltin | 209 | 209 | (builtinName) => { | 210 | | - if (BuiltinModule.canBeRequiredByUsers(builtinName) && | 211 | | - BuiltinModule.canBeRequiredWithoutScheme(builtinName)) { | | 210 | + if (BuiltinModule.canBeRequiredWithoutScheme(builtinName)) { | 212 | 211 | return require(builtinName); | 213 | 212 | } | 214 | 213 | throw new ERR_INVALID_ARG_VALUE('builtinName', builtinName); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/resolve.js +2-4Lines changed: 2 additions & 4 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -803,8 +803,7 @@ function parsePackageName(specifier, base) { | 803 | 803 | * @returns {resolved: URL, format? : string} | 804 | 804 | */ | 805 | 805 | function packageResolve(specifier, base, conditions) { | 806 | | - if (BuiltinModule.canBeRequiredByUsers(specifier) && | 807 | | - BuiltinModule.canBeRequiredWithoutScheme(specifier)) { | | 806 | + if (BuiltinModule.canBeRequiredWithoutScheme(specifier)) { | 808 | 807 | return new URL('node:' + specifier); | 809 | 808 | } | 810 | 809 |
| @@ -990,8 +989,7 @@ function checkIfDisallowedImport(specifier, parsed, parsedParentURL) { | 990 | 989 |
| 991 | 990 | return { url: parsed.href }; | 992 | 991 | } | 993 | | - if (BuiltinModule.canBeRequiredByUsers(specifier) && | 994 | | - BuiltinModule.canBeRequiredWithoutScheme(specifier)) { | | 992 | + if (BuiltinModule.canBeRequiredWithoutScheme(specifier)) { | 995 | 993 | throw new ERR_NETWORK_IMPORT_DISALLOWED( | 996 | 994 | specifier, | 997 | 995 | parsedParentURL, | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/helpers.js +10-8Lines changed: 10 additions & 8 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -59,13 +59,14 @@ function getCjsConditions() { | 59 | 59 | } | 60 | 60 |
| 61 | 61 | function loadBuiltinModule(filename, request) { | 62 | | - const mod = BuiltinModule.map.get(filename); | 63 | | - if (mod?.canBeRequiredByUsers) { | 64 | | - debug('load built-in module %s', request); | 65 | | - // compileForPublicLoader() throws if mod.canBeRequiredByUsers is false: | 66 | | - mod.compileForPublicLoader(); | 67 | | - return mod; | | 62 | + if (!BuiltinModule.canBeRequiredByUsers(filename)) { | | 63 | + return; | 68 | 64 | } | | 65 | + const mod = BuiltinModule.map.get(filename); | | 66 | + debug('load built-in module %s', request); | | 67 | + // compileForPublicLoader() throws if canBeRequiredByUsers is false: | | 68 | + mod.compileForPublicLoader(); | | 69 | + return mod; | 69 | 70 | } | 70 | 71 |
| 71 | 72 | let $Module = null; | @@ -99,8 +100,9 @@ function makeRequireFunction(mod, redirects) { | 99 | 100 | const { href, protocol } = destination; | 100 | 101 | if (protocol === 'node:') { | 101 | 102 | const specifier = destination.pathname; | 102 | | - const mod = loadBuiltinModule(specifier, href); | 103 | | - if (mod && mod.canBeRequiredByUsers) { | | 103 | + | | 104 | + if (BuiltinModule.canBeRequiredByUsers(specifier)) { | | 105 | + const mod = loadBuiltinModule(specifier, href); | 104 | 106 | return mod.exports; | 105 | 107 | } | 106 | 108 | throw new ERR_UNKNOWN_BUILTIN_MODULE(specifier); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/process/warning.js +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -166,8 +166,8 @@ function emitWarning(warning, type, code, ctor) { | 166 | 166 | process.nextTick(doEmitWarning, warning); | 167 | 167 | } | 168 | 168 |
| 169 | | -function emitWarningSync(warning) { | 170 | | - process.emit('warning', createWarningObject(warning)); | | 169 | +function emitWarningSync(warning, type, code, ctor) { | | 170 | + process.emit('warning', createWarningObject(warning, type, code, ctor)); | 171 | 171 | } | 172 | 172 |
| 173 | 173 | function createWarningObject(warning, type, code, ctor, detail) { | |
|