From 1377086da4e44c6e0f28fb4328ae9c9f1fd8d497 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 12:39:13 -0400 Subject: [PATCH 1/8] add notes and todo list --- NOTES.md | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ TODO.md | 6 ++++++ 2 files changed, 67 insertions(+) create mode 100644 NOTES.md create mode 100644 TODO.md diff --git a/NOTES.md b/NOTES.md new file mode 100644 index 000000000..0f0cbf15d --- /dev/null +++ b/NOTES.md @@ -0,0 +1,61 @@ +*Delete this file before merging this PR* + +## PnP interop + +Asked about it here: +https://discord.com/channels/226791405589233664/654372321225605128/957301175609344070 + +PnP API checks if import specifiers are for dependencies: non-relative, non-absolute + libfoo + @scope/libfoo + +When they are, it does `resolveToUnqualified` to map to an unqualified path. +This path points to the module's location on disk (in a zip, perhaps) but does +not handle file extension resolution or stuff like that. + +To interop with PnP, we need PnP to *only* `resolveToUnqualified`. +We do everything else. + +```typescript +import { Module } from 'module'; +import fs from 'fs'; + +const pathRegExp = /^(?![a-zA-Z]:[\\/]|\\\\|\.{0,2}(?:\/|$))((?:@[^/]+\/)?[^/]+)\/*(.*|)$/; + +const originalModuleResolveFilename = Module._resolveFilename; +Module._resolveFilename = function ( + request: string, + parent: typeof Module | null | undefined, + isMain: boolean, + options?: { [key: string]: any } +) { + const dependencyNameMatch = request.match(pathRegExp); + if (dependencyNameMatch !== null) { + + const [, dependencyName, subPath] = dependencyNameMatch; + + const unqualified = pnpapi.resolveToUnqualified(....); + + // Do your modified resolution on the unqualified path here + + } else { + + // Do your modified resolution here; no need for PnP + + } + +}; +``` + +PnP can be installed at runtime. + +To conditionally check if PnP is available at the start of *every* resolution: + +```typescript +// Get the pnpapi of either the issuer or the specifier. +// The latter is required when the specifier is an absolute path to a +// zip file and the issuer doesn't belong to a pnpapi +const {findPnPApi} = Module; +const pnpapi = findPnPApi ? (findPnpApi(issuer) ?? (url ? findPnpApi(specifier) : null)) : null; +if (pnpapi) {...} +``` diff --git a/TODO.md b/TODO.md new file mode 100644 index 000000000..d52aee867 --- /dev/null +++ b/TODO.md @@ -0,0 +1,6 @@ +*Delete this file before merging this PR* + +## TODOs + +Copy any relevant changes from `add-cjs-loader-resolve` + From c9b1c309e883ef000cb4af4dd1f344d13aefc106 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 12:57:44 -0400 Subject: [PATCH 2/8] safely repeating changes from add-cjs-loader-resolve to remember what I did and filter any WIP mess I may have forgotten about --- .gitignore | 2 +- TODO.md | 6 +++ dist-raw/NODE-LICENSE.md | 24 ++++++++++ dist-raw/README.md | 12 +++++ dist-raw/node-internal-errors.js | 46 ++++++++++++++++++ ...=> node-internal-modules-cjs-helpers.d.ts} | 0 ...s => node-internal-modules-cjs-helpers.js} | 0 ...> node-internal-modules-cjs-loader-old.js} | 48 +------------------ package.json | 1 + ...-internal-modules-esm-resolve-v13.12.0.js} | 0 ...l-await.js => node-internal-repl-await.js} | 0 11 files changed, 91 insertions(+), 48 deletions(-) create mode 100644 dist-raw/NODE-LICENSE.md create mode 100644 dist-raw/node-internal-errors.js rename dist-raw/{node-cjs-helpers.d.ts => node-internal-modules-cjs-helpers.d.ts} (100%) rename dist-raw/{node-cjs-helpers.js => node-internal-modules-cjs-helpers.js} (100%) rename dist-raw/{node-cjs-loader-utils.js => node-internal-modules-cjs-loader-old.js} (57%) rename raw/{node-esm-resolve-implementation-v13.12.0.js => node-internal-modules-esm-resolve-v13.12.0.js} (100%) rename raw/{node-repl-await.js => node-internal-repl-await.js} (100%) diff --git a/.gitignore b/.gitignore index f8e97ed5f..5b0df0a80 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,7 @@ coverage/ .DS_Store npm-debug.log -dist/ +/dist/ tsconfig.schema.json tsconfig.schemastore-schema.json .idea/ diff --git a/TODO.md b/TODO.md index d52aee867..45f519a16 100644 --- a/TODO.md +++ b/TODO.md @@ -4,3 +4,9 @@ Copy any relevant changes from `add-cjs-loader-resolve` +I forgot exactly where I was in `add-cjs-loader-resolve` +Re-do the renaming and moving that I did in that branch. +Then diff to see that I did it correctly. +Avoid introducing any accidental behavioral changes. + + diff --git a/dist-raw/NODE-LICENSE.md b/dist-raw/NODE-LICENSE.md new file mode 100644 index 000000000..c1de8e9d2 --- /dev/null +++ b/dist-raw/NODE-LICENSE.md @@ -0,0 +1,24 @@ +This directory contains portions of Node.js source code which is licensed as follows: + +--- + +Copyright Joyent, Inc. and other Node contributors. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/dist-raw/README.md b/dist-raw/README.md index b7b4c4d7d..8a598a35a 100644 --- a/dist-raw/README.md +++ b/dist-raw/README.md @@ -11,3 +11,15 @@ in a factory function, we will not indent the function body, to avoid whitespace One obvious problem with this approach: the code has been pulled from one version of node, whereas users of ts-node run multiple versions of node. Users running node 12 may see that ts-node behaves like node 14, for example. + +--- + +## Naming convention + +Not used consistently, but the idea is: + +`node-(...-)-.js` + +`node-internal-errors.js` -> `github.com/nodejs/node/blob/TAG/lib/internal/errors.js` + +So, take the path within node's `lib/` directory, and replace slashes with hyphens. diff --git a/dist-raw/node-internal-errors.js b/dist-raw/node-internal-errors.js new file mode 100644 index 000000000..849e38eb9 --- /dev/null +++ b/dist-raw/node-internal-errors.js @@ -0,0 +1,46 @@ +// Native ERR_REQUIRE_ESM Error is declared here: +// https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/errors.js#L1294-L1313 +// Error class factory is implemented here: +// function E: https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/errors.js#L323-L341 +// function makeNodeErrorWithCode: https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/errors.js#L251-L278 +// The code below should create an error that matches the native error as closely as possible. +// Third-party libraries which attempt to catch the native ERR_REQUIRE_ESM should recognize our imitation error. +function createErrRequireEsm(filename, parentPath, packageJsonPath) { + const code = 'ERR_REQUIRE_ESM' + const err = new Error(getMessage(filename, parentPath, packageJsonPath)) + // Set `name` to be used in stack trace, generate stack trace with that name baked in, then re-declare the `name` field. + // This trick is copied from node's source. + err.name = `Error [${ code }]` + err.stack + Object.defineProperty(err, 'name', { + value: 'Error', + enumerable: false, + writable: true, + configurable: true + }) + err.code = code + return err + + // Copy-pasted from https://github.com/nodejs/node/blob/b533fb3508009e5f567cc776daba8fbf665386a6/lib/internal/errors.js#L1293-L1311 + // so that our error message is identical to the native message. + function getMessage(filename, parentPath = null, packageJsonPath = null) { + const ext = path.extname(filename) + let msg = `Must use import to load ES Module: ${filename}`; + if (parentPath && packageJsonPath) { + const path = require('path'); + const basename = path.basename(filename) === path.basename(parentPath) ? + filename : path.basename(filename); + msg += + '\nrequire() of ES modules is not supported.\nrequire() of ' + + `${filename} ${parentPath ? `from ${parentPath} ` : ''}` + + `is an ES module file as it is a ${ext} file whose nearest parent ` + + `package.json contains "type": "module" which defines all ${ext} ` + + 'files in that package scope as ES modules.\nInstead ' + + 'change the requiring code to use ' + + 'import(), or remove "type": "module" from ' + + `${packageJsonPath}.\n`; + return msg; + } + return msg; + } +} diff --git a/dist-raw/node-cjs-helpers.d.ts b/dist-raw/node-internal-modules-cjs-helpers.d.ts similarity index 100% rename from dist-raw/node-cjs-helpers.d.ts rename to dist-raw/node-internal-modules-cjs-helpers.d.ts diff --git a/dist-raw/node-cjs-helpers.js b/dist-raw/node-internal-modules-cjs-helpers.js similarity index 100% rename from dist-raw/node-cjs-helpers.js rename to dist-raw/node-internal-modules-cjs-helpers.js diff --git a/dist-raw/node-cjs-loader-utils.js b/dist-raw/node-internal-modules-cjs-loader-old.js similarity index 57% rename from dist-raw/node-cjs-loader-utils.js rename to dist-raw/node-internal-modules-cjs-loader-old.js index b7ec0d531..ba19fb049 100644 --- a/dist-raw/node-cjs-loader-utils.js +++ b/dist-raw/node-internal-modules-cjs-loader-old.js @@ -6,6 +6,7 @@ const path = require('path'); const packageJsonReader = require('./node-package-json-reader'); const {JSONParse} = require('./node-primordials'); const {normalizeSlashes} = require('../dist/util'); +const {createErrRequireEsm} = require('./node-internal-errors'); module.exports.assertScriptCanLoadAsCJSImpl = assertScriptCanLoadAsCJSImpl; @@ -84,50 +85,3 @@ function readPackage(requestPath) { throw e; } } - -// Native ERR_REQUIRE_ESM Error is declared here: -// https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/errors.js#L1294-L1313 -// Error class factory is implemented here: -// function E: https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/errors.js#L323-L341 -// function makeNodeErrorWithCode: https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/errors.js#L251-L278 -// The code below should create an error that matches the native error as closely as possible. -// Third-party libraries which attempt to catch the native ERR_REQUIRE_ESM should recognize our imitation error. -function createErrRequireEsm(filename, parentPath, packageJsonPath) { - const code = 'ERR_REQUIRE_ESM' - const err = new Error(getMessage(filename, parentPath, packageJsonPath)) - // Set `name` to be used in stack trace, generate stack trace with that name baked in, then re-declare the `name` field. - // This trick is copied from node's source. - err.name = `Error [${ code }]` - err.stack - Object.defineProperty(err, 'name', { - value: 'Error', - enumerable: false, - writable: true, - configurable: true - }) - err.code = code - return err - - // Copy-pasted from https://github.com/nodejs/node/blob/b533fb3508009e5f567cc776daba8fbf665386a6/lib/internal/errors.js#L1293-L1311 - // so that our error message is identical to the native message. - function getMessage(filename, parentPath = null, packageJsonPath = null) { - const ext = path.extname(filename) - let msg = `Must use import to load ES Module: ${filename}`; - if (parentPath && packageJsonPath) { - const path = require('path'); - const basename = path.basename(filename) === path.basename(parentPath) ? - filename : path.basename(filename); - msg += - '\nrequire() of ES modules is not supported.\nrequire() of ' + - `${filename} ${parentPath ? `from ${parentPath} ` : ''}` + - `is an ES module file as it is a ${ext} file whose nearest parent ` + - `package.json contains "type": "module" which defines all ${ext} ` + - 'files in that package scope as ES modules.\nInstead ' + - 'change the requiring code to use ' + - 'import(), or remove "type": "module" from ' + - `${packageJsonPath}.\n`; - return msg; - } - return msg; - } -} diff --git a/package.json b/package.json index 1f158f1fb..f888cfc9f 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "/transpilers/", "/dist/", "!/dist/test", + "/dist-raw/NODE-LICENSE.md", "/dist-raw/**.js", "/register/", "/esm/", diff --git a/raw/node-esm-resolve-implementation-v13.12.0.js b/raw/node-internal-modules-esm-resolve-v13.12.0.js similarity index 100% rename from raw/node-esm-resolve-implementation-v13.12.0.js rename to raw/node-internal-modules-esm-resolve-v13.12.0.js diff --git a/raw/node-repl-await.js b/raw/node-internal-repl-await.js similarity index 100% rename from raw/node-repl-await.js rename to raw/node-internal-repl-await.js From ac34ce83198294e903715630ebf6e7c34021cd80 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 13:15:56 -0400 Subject: [PATCH 3/8] more sync --- dist-raw/node-internal-errors.js | 4 + dist-raw/node-internal-modules-cjs-loader.js | 1283 +++++++++++++++++ raw/download-and-compare.sh | 8 + ...ode-internal-modules-cjs-loader-v17.0.1.js | 1283 +++++++++++++++++ 4 files changed, 2578 insertions(+) create mode 100644 dist-raw/node-internal-modules-cjs-loader.js create mode 100644 raw/download-and-compare.sh create mode 100644 raw/node-internal-modules-cjs-loader-v17.0.1.js diff --git a/dist-raw/node-internal-errors.js b/dist-raw/node-internal-errors.js index 849e38eb9..75148b85c 100644 --- a/dist-raw/node-internal-errors.js +++ b/dist-raw/node-internal-errors.js @@ -44,3 +44,7 @@ function createErrRequireEsm(filename, parentPath, packageJsonPath) { return msg; } } + +module.exports = { + createErrRequireEsm +}; diff --git a/dist-raw/node-internal-modules-cjs-loader.js b/dist-raw/node-internal-modules-cjs-loader.js new file mode 100644 index 000000000..622805ea7 --- /dev/null +++ b/dist-raw/node-internal-modules-cjs-loader.js @@ -0,0 +1,1283 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const { + ArrayIsArray, + ArrayPrototypeConcat, + ArrayPrototypeFilter, + ArrayPrototypeIncludes, + ArrayPrototypeIndexOf, + ArrayPrototypeJoin, + ArrayPrototypePush, + ArrayPrototypeSlice, + ArrayPrototypeSplice, + ArrayPrototypeUnshift, + ArrayPrototypeUnshiftApply, + Boolean, + Error, + JSONParse, + ObjectCreate, + ObjectDefineProperty, + ObjectFreeze, + ObjectGetOwnPropertyDescriptor, + ObjectGetPrototypeOf, + ObjectKeys, + ObjectPrototype, + ObjectPrototypeHasOwnProperty, + ObjectSetPrototypeOf, + Proxy, + ReflectApply, + ReflectSet, + RegExpPrototypeExec, + RegExpPrototypeTest, + SafeMap, + SafeWeakMap, + String, + StringPrototypeCharAt, + StringPrototypeCharCodeAt, + StringPrototypeEndsWith, + StringPrototypeLastIndexOf, + StringPrototypeIndexOf, + StringPrototypeMatch, + StringPrototypeRepeat, + StringPrototypeSlice, + StringPrototypeSplit, + StringPrototypeStartsWith, +} = primordials; + +// Map used to store CJS parsing data. +const cjsParseCache = new SafeWeakMap(); + +// Set first due to cycle with ESM loader functions. +module.exports = { + wrapSafe, Module, toRealPath, readPackageScope, cjsParseCache, + get hasLoadedAnyUserCJSModule() { return hasLoadedAnyUserCJSModule; } +}; + +const { NativeModule } = require('internal/bootstrap/loaders'); +const { + maybeCacheSourceMap, +} = require('internal/source_map/source_map_cache'); +const { pathToFileURL, fileURLToPath, isURLInstance } = require('internal/url'); +const { deprecate } = require('internal/util'); +const vm = require('vm'); +const assert = require('internal/assert'); +const fs = require('fs'); +const internalFS = require('internal/fs/utils'); +const path = require('path'); +const { sep } = path; +const { internalModuleStat } = internalBinding('fs'); +const packageJsonReader = require('internal/modules/package_json_reader'); +const { safeGetenv } = internalBinding('credentials'); +const { + cjsConditions, + hasEsmSyntax, + loadNativeModule, + makeRequireFunction, + normalizeReferrerURL, + stripBOM, +} = require('internal/modules/cjs/helpers'); +const { getOptionValue } = require('internal/options'); +const preserveSymlinks = getOptionValue('--preserve-symlinks'); +const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main'); +// Do not eagerly grab .manifest, it may be in TDZ +const policy = getOptionValue('--experimental-policy') ? + require('internal/process/policy') : + null; + +// Whether any user-provided CJS modules had been loaded (executed). +// Used for internal assertions. +let hasLoadedAnyUserCJSModule = false; + +const { + codes: { + ERR_INVALID_ARG_VALUE, + ERR_INVALID_MODULE_SPECIFIER, + ERR_REQUIRE_ESM, + ERR_UNKNOWN_BUILTIN_MODULE, + }, + setArrowMessage, +} = require('internal/errors'); +const { validateString } = require('internal/validators'); +const pendingDeprecation = getOptionValue('--pending-deprecation'); + +const { + CHAR_FORWARD_SLASH, + CHAR_BACKWARD_SLASH, + CHAR_COLON +} = require('internal/constants'); + +const { + isProxy +} = require('internal/util/types'); + +const asyncESM = require('internal/process/esm_loader'); +const { enrichCJSError } = require('internal/modules/esm/translators'); +const { kEvaluated } = internalBinding('module_wrap'); +const { + encodedSepRegEx, + packageExportsResolve, + packageImportsResolve +} = require('internal/modules/esm/resolve'); + +const isWindows = process.platform === 'win32'; + +const relativeResolveCache = ObjectCreate(null); + +let requireDepth = 0; +let statCache = null; +let isPreloading = false; + +function stat(filename) { + filename = path.toNamespacedPath(filename); + if (statCache !== null) { + const result = statCache.get(filename); + if (result !== undefined) return result; + } + const result = internalModuleStat(filename); + if (statCache !== null && result >= 0) { + // Only set cache when `internalModuleStat(filename)` succeeds. + statCache.set(filename, result); + } + return result; +} + +function updateChildren(parent, child, scan) { + const children = parent?.children; + if (children && !(scan && ArrayPrototypeIncludes(children, child))) + ArrayPrototypePush(children, child); +} + +const moduleParentCache = new SafeWeakMap(); +function Module(id = '', parent) { + this.id = id; + this.path = path.dirname(id); + this.exports = {}; + moduleParentCache.set(this, parent); + updateChildren(parent, this, false); + this.filename = null; + this.loaded = false; + this.children = []; +} + +const builtinModules = []; +for (const { 0: id, 1: mod } of NativeModule.map) { + if (mod.canBeRequiredByUsers) { + ArrayPrototypePush(builtinModules, id); + } +} + +ObjectFreeze(builtinModules); +Module.builtinModules = builtinModules; + +Module._cache = ObjectCreate(null); +Module._pathCache = ObjectCreate(null); +Module._extensions = ObjectCreate(null); +let modulePaths = []; +Module.globalPaths = []; + +let patched = false; + +// eslint-disable-next-line func-style +let wrap = function(script) { + return Module.wrapper[0] + script + Module.wrapper[1]; +}; + +const wrapper = [ + '(function (exports, require, module, __filename, __dirname) { ', + '\n});', +]; + +let wrapperProxy = new Proxy(wrapper, { + set(target, property, value, receiver) { + patched = true; + return ReflectSet(target, property, value, receiver); + }, + + defineProperty(target, property, descriptor) { + patched = true; + return ObjectDefineProperty(target, property, descriptor); + } +}); + +ObjectDefineProperty(Module, 'wrap', { + get() { + return wrap; + }, + + set(value) { + patched = true; + wrap = value; + } +}); + +ObjectDefineProperty(Module, 'wrapper', { + get() { + return wrapperProxy; + }, + + set(value) { + patched = true; + wrapperProxy = value; + } +}); + +const isPreloadingDesc = { get() { return isPreloading; } }; +ObjectDefineProperty(Module.prototype, 'isPreloading', isPreloadingDesc); +ObjectDefineProperty(NativeModule.prototype, 'isPreloading', isPreloadingDesc); + +function getModuleParent() { + return moduleParentCache.get(this); +} + +function setModuleParent(value) { + moduleParentCache.set(this, value); +} + +ObjectDefineProperty(Module.prototype, 'parent', { + get: pendingDeprecation ? deprecate( + getModuleParent, + 'module.parent is deprecated due to accuracy issues. Please use ' + + 'require.main to find program entry point instead.', + 'DEP0144' + ) : getModuleParent, + set: pendingDeprecation ? deprecate( + setModuleParent, + 'module.parent is deprecated due to accuracy issues. Please use ' + + 'require.main to find program entry point instead.', + 'DEP0144' + ) : setModuleParent, +}); + +let debug = require('internal/util/debuglog').debuglog('module', (fn) => { + debug = fn; +}); +Module._debug = deprecate(debug, 'Module._debug is deprecated.', 'DEP0077'); + +// Given a module name, and a list of paths to test, returns the first +// matching file in the following precedence. +// +// require("a.") +// -> a. +// +// require("a") +// -> a +// -> a. +// -> a/index. + +const packageJsonCache = new SafeMap(); + +function readPackage(requestPath) { + const jsonPath = path.resolve(requestPath, 'package.json'); + + const existing = packageJsonCache.get(jsonPath); + if (existing !== undefined) return existing; + + const result = packageJsonReader.read(jsonPath); + const json = result.containsKeys === false ? '{}' : result.string; + if (json === undefined) { + packageJsonCache.set(jsonPath, false); + return false; + } + + try { + const parsed = JSONParse(json); + const filtered = { + name: parsed.name, + main: parsed.main, + exports: parsed.exports, + imports: parsed.imports, + type: parsed.type + }; + packageJsonCache.set(jsonPath, filtered); + return filtered; + } catch (e) { + e.path = jsonPath; + e.message = 'Error parsing ' + jsonPath + ': ' + e.message; + throw e; + } +} + +function readPackageScope(checkPath) { + const rootSeparatorIndex = StringPrototypeIndexOf(checkPath, sep); + let separatorIndex; + do { + separatorIndex = StringPrototypeLastIndexOf(checkPath, sep); + checkPath = StringPrototypeSlice(checkPath, 0, separatorIndex); + if (StringPrototypeEndsWith(checkPath, sep + 'node_modules')) + return false; + const pjson = readPackage(checkPath + sep); + if (pjson) return { + data: pjson, + path: checkPath, + }; + } while (separatorIndex > rootSeparatorIndex); + return false; +} + +function tryPackage(requestPath, exts, isMain, originalPath) { + const pkg = readPackage(requestPath)?.main; + + if (!pkg) { + return tryExtensions(path.resolve(requestPath, 'index'), exts, isMain); + } + + const filename = path.resolve(requestPath, pkg); + let actual = tryFile(filename, isMain) || + tryExtensions(filename, exts, isMain) || + tryExtensions(path.resolve(filename, 'index'), exts, isMain); + if (actual === false) { + actual = tryExtensions(path.resolve(requestPath, 'index'), exts, isMain); + if (!actual) { + // eslint-disable-next-line no-restricted-syntax + const err = new Error( + `Cannot find module '${filename}'. ` + + 'Please verify that the package.json has a valid "main" entry' + ); + err.code = 'MODULE_NOT_FOUND'; + err.path = path.resolve(requestPath, 'package.json'); + err.requestPath = originalPath; + // TODO(BridgeAR): Add the requireStack as well. + throw err; + } else { + const jsonPath = path.resolve(requestPath, 'package.json'); + process.emitWarning( + `Invalid 'main' field in '${jsonPath}' of '${pkg}'. ` + + 'Please either fix that or report it to the module author', + 'DeprecationWarning', + 'DEP0128' + ); + } + } + return actual; +} + +// In order to minimize unnecessary lstat() calls, +// this cache is a list of known-real paths. +// Set to an empty Map to reset. +const realpathCache = new SafeMap(); + +// Check if the file exists and is not a directory +// if using --preserve-symlinks and isMain is false, +// keep symlinks intact, otherwise resolve to the +// absolute realpath. +function tryFile(requestPath, isMain) { + const rc = stat(requestPath); + if (rc !== 0) return; + if (preserveSymlinks && !isMain) { + return path.resolve(requestPath); + } + return toRealPath(requestPath); +} + +function toRealPath(requestPath) { + return fs.realpathSync(requestPath, { + [internalFS.realpathCacheKey]: realpathCache + }); +} + +// Given a path, check if the file exists with any of the set extensions +function tryExtensions(p, exts, isMain) { + for (let i = 0; i < exts.length; i++) { + const filename = tryFile(p + exts[i], isMain); + + if (filename) { + return filename; + } + } + return false; +} + +// Find the longest (possibly multi-dot) extension registered in +// Module._extensions +function findLongestRegisteredExtension(filename) { + const name = path.basename(filename); + let currentExtension; + let index; + let startIndex = 0; + while ((index = StringPrototypeIndexOf(name, '.', startIndex)) !== -1) { + startIndex = index + 1; + if (index === 0) continue; // Skip dotfiles like .gitignore + currentExtension = StringPrototypeSlice(name, index); + if (Module._extensions[currentExtension]) return currentExtension; + } + return '.js'; +} + +function trySelfParentPath(parent) { + if (!parent) return false; + + if (parent.filename) { + return parent.filename; + } else if (parent.id === '' || parent.id === 'internal/preload') { + try { + return process.cwd() + path.sep; + } catch { + return false; + } + } +} + +function trySelf(parentPath, request) { + if (!parentPath) return false; + + const { data: pkg, path: pkgPath } = readPackageScope(parentPath) || {}; + if (!pkg || pkg.exports === undefined) return false; + if (typeof pkg.name !== 'string') return false; + + let expansion; + if (request === pkg.name) { + expansion = '.'; + } else if (StringPrototypeStartsWith(request, `${pkg.name}/`)) { + expansion = '.' + StringPrototypeSlice(request, pkg.name.length); + } else { + return false; + } + + try { + return finalizeEsmResolution(packageExportsResolve( + pathToFileURL(pkgPath + '/package.json'), expansion, pkg, + pathToFileURL(parentPath), cjsConditions), parentPath, pkgPath); + } catch (e) { + if (e.code === 'ERR_MODULE_NOT_FOUND') + throw createEsmNotFoundErr(request, pkgPath + '/package.json'); + throw e; + } +} + +// This only applies to requests of a specific form: +// 1. name/.* +// 2. @scope/name/.* +const EXPORTS_PATTERN = /^((?:@[^/\\%]+\/)?[^./\\%][^/\\%]*)(\/.*)?$/; +function resolveExports(nmPath, request) { + // The implementation's behavior is meant to mirror resolution in ESM. + const { 1: name, 2: expansion = '' } = + StringPrototypeMatch(request, EXPORTS_PATTERN) || []; + if (!name) + return; + const pkgPath = path.resolve(nmPath, name); + const pkg = readPackage(pkgPath); + if (pkg?.exports != null) { + try { + return finalizeEsmResolution(packageExportsResolve( + pathToFileURL(pkgPath + '/package.json'), '.' + expansion, pkg, null, + cjsConditions), null, pkgPath); + } catch (e) { + if (e.code === 'ERR_MODULE_NOT_FOUND') + throw createEsmNotFoundErr(request, pkgPath + '/package.json'); + throw e; + } + } +} + +const trailingSlashRegex = /(?:^|\/)\.?\.$/; +Module._findPath = function(request, paths, isMain) { + const absoluteRequest = path.isAbsolute(request); + if (absoluteRequest) { + paths = ['']; + } else if (!paths || paths.length === 0) { + return false; + } + + const cacheKey = request + '\x00' + ArrayPrototypeJoin(paths, '\x00'); + const entry = Module._pathCache[cacheKey]; + if (entry) + return entry; + + let exts; + let trailingSlash = request.length > 0 && + StringPrototypeCharCodeAt(request, request.length - 1) === + CHAR_FORWARD_SLASH; + if (!trailingSlash) { + trailingSlash = RegExpPrototypeTest(trailingSlashRegex, request); + } + + // For each path + for (let i = 0; i < paths.length; i++) { + // Don't search further if path doesn't exist + const curPath = paths[i]; + if (curPath && stat(curPath) < 1) continue; + + if (!absoluteRequest) { + const exportsResolved = resolveExports(curPath, request); + if (exportsResolved) + return exportsResolved; + } + + const basePath = path.resolve(curPath, request); + let filename; + + const rc = stat(basePath); + if (!trailingSlash) { + if (rc === 0) { // File. + if (!isMain) { + if (preserveSymlinks) { + filename = path.resolve(basePath); + } else { + filename = toRealPath(basePath); + } + } else if (preserveSymlinksMain) { + // For the main module, we use the preserveSymlinksMain flag instead + // mainly for backward compatibility, as the preserveSymlinks flag + // historically has not applied to the main module. Most likely this + // was intended to keep .bin/ binaries working, as following those + // symlinks is usually required for the imports in the corresponding + // files to resolve; that said, in some use cases following symlinks + // causes bigger problems which is why the preserveSymlinksMain option + // is needed. + filename = path.resolve(basePath); + } else { + filename = toRealPath(basePath); + } + } + + if (!filename) { + // Try it with each of the extensions + if (exts === undefined) + exts = ObjectKeys(Module._extensions); + filename = tryExtensions(basePath, exts, isMain); + } + } + + if (!filename && rc === 1) { // Directory. + // try it with each of the extensions at "index" + if (exts === undefined) + exts = ObjectKeys(Module._extensions); + filename = tryPackage(basePath, exts, isMain, request); + } + + if (filename) { + Module._pathCache[cacheKey] = filename; + return filename; + } + } + + return false; +}; + +// 'node_modules' character codes reversed +const nmChars = [ 115, 101, 108, 117, 100, 111, 109, 95, 101, 100, 111, 110 ]; +const nmLen = nmChars.length; +if (isWindows) { + // 'from' is the __dirname of the module. + Module._nodeModulePaths = function(from) { + // Guarantee that 'from' is absolute. + from = path.resolve(from); + + // note: this approach *only* works when the path is guaranteed + // to be absolute. Doing a fully-edge-case-correct path.split + // that works on both Windows and Posix is non-trivial. + + // return root node_modules when path is 'D:\\'. + // path.resolve will make sure from.length >=3 in Windows. + if (StringPrototypeCharCodeAt(from, from.length - 1) === + CHAR_BACKWARD_SLASH && + StringPrototypeCharCodeAt(from, from.length - 2) === CHAR_COLON) + return [from + 'node_modules']; + + const paths = []; + for (let i = from.length - 1, p = 0, last = from.length; i >= 0; --i) { + const code = StringPrototypeCharCodeAt(from, i); + // The path segment separator check ('\' and '/') was used to get + // node_modules path for every path segment. + // Use colon as an extra condition since we can get node_modules + // path for drive root like 'C:\node_modules' and don't need to + // parse drive name. + if (code === CHAR_BACKWARD_SLASH || + code === CHAR_FORWARD_SLASH || + code === CHAR_COLON) { + if (p !== nmLen) + ArrayPrototypePush( + paths, + StringPrototypeSlice(from, 0, last) + '\\node_modules' + ); + last = i; + p = 0; + } else if (p !== -1) { + if (nmChars[p] === code) { + ++p; + } else { + p = -1; + } + } + } + + return paths; + }; +} else { // posix + // 'from' is the __dirname of the module. + Module._nodeModulePaths = function(from) { + // Guarantee that 'from' is absolute. + from = path.resolve(from); + // Return early not only to avoid unnecessary work, but to *avoid* returning + // an array of two items for a root: [ '//node_modules', '/node_modules' ] + if (from === '/') + return ['/node_modules']; + + // note: this approach *only* works when the path is guaranteed + // to be absolute. Doing a fully-edge-case-correct path.split + // that works on both Windows and Posix is non-trivial. + const paths = []; + for (let i = from.length - 1, p = 0, last = from.length; i >= 0; --i) { + const code = StringPrototypeCharCodeAt(from, i); + if (code === CHAR_FORWARD_SLASH) { + if (p !== nmLen) + ArrayPrototypePush( + paths, + StringPrototypeSlice(from, 0, last) + '/node_modules' + ); + last = i; + p = 0; + } else if (p !== -1) { + if (nmChars[p] === code) { + ++p; + } else { + p = -1; + } + } + } + + // Append /node_modules to handle root paths. + ArrayPrototypePush(paths, '/node_modules'); + + return paths; + }; +} + +Module._resolveLookupPaths = function(request, parent) { + if (NativeModule.canBeRequiredByUsers(request)) { + debug('looking for %j in []', request); + return null; + } + + // Check for node modules paths. + if (StringPrototypeCharAt(request, 0) !== '.' || + (request.length > 1 && + StringPrototypeCharAt(request, 1) !== '.' && + StringPrototypeCharAt(request, 1) !== '/' && + (!isWindows || StringPrototypeCharAt(request, 1) !== '\\'))) { + + let paths = modulePaths; + if (parent?.paths?.length) { + paths = ArrayPrototypeConcat(parent.paths, paths); + } + + debug('looking for %j in %j', request, paths); + return paths.length > 0 ? paths : null; + } + + // In REPL, parent.filename is null. + if (!parent || !parent.id || !parent.filename) { + // Make require('./path/to/foo') work - normally the path is taken + // from realpath(__filename) but in REPL there is no filename + const mainPaths = ['.']; + + debug('looking for %j in %j', request, mainPaths); + return mainPaths; + } + + debug('RELATIVE: requested: %s from parent.id %s', request, parent.id); + + const parentDir = [path.dirname(parent.filename)]; + debug('looking for %j', parentDir); + return parentDir; +}; + +function emitCircularRequireWarning(prop) { + process.emitWarning( + `Accessing non-existent property '${String(prop)}' of module exports ` + + 'inside circular dependency' + ); +} + +// A Proxy that can be used as the prototype of a module.exports object and +// warns when non-existent properties are accessed. +const CircularRequirePrototypeWarningProxy = new Proxy({}, { + get(target, prop) { + // Allow __esModule access in any case because it is used in the output + // of transpiled code to determine whether something comes from an + // ES module, and is not used as a regular key of `module.exports`. + if (prop in target || prop === '__esModule') return target[prop]; + emitCircularRequireWarning(prop); + return undefined; + }, + + getOwnPropertyDescriptor(target, prop) { + if (ObjectPrototypeHasOwnProperty(target, prop) || prop === '__esModule') + return ObjectGetOwnPropertyDescriptor(target, prop); + emitCircularRequireWarning(prop); + return undefined; + } +}); + +function getExportsForCircularRequire(module) { + if (module.exports && + !isProxy(module.exports) && + ObjectGetPrototypeOf(module.exports) === ObjectPrototype && + // Exclude transpiled ES6 modules / TypeScript code because those may + // employ unusual patterns for accessing 'module.exports'. That should + // be okay because ES6 modules have a different approach to circular + // dependencies anyway. + !module.exports.__esModule) { + // This is later unset once the module is done loading. + ObjectSetPrototypeOf( + module.exports, CircularRequirePrototypeWarningProxy); + } + + return module.exports; +} + +// Check the cache for the requested file. +// 1. If a module already exists in the cache: return its exports object. +// 2. If the module is native: call +// `NativeModule.prototype.compileForPublicLoader()` and return the exports. +// 3. Otherwise, create a new module for the file and save it to the cache. +// Then have it load the file contents before returning its exports +// object. +Module._load = function(request, parent, isMain) { + let relResolveCacheIdentifier; + if (parent) { + debug('Module._load REQUEST %s parent: %s', request, parent.id); + // Fast path for (lazy loaded) modules in the same directory. The indirect + // caching is required to allow cache invalidation without changing the old + // cache key names. + relResolveCacheIdentifier = `${parent.path}\x00${request}`; + const filename = relativeResolveCache[relResolveCacheIdentifier]; + if (filename !== undefined) { + const cachedModule = Module._cache[filename]; + if (cachedModule !== undefined) { + updateChildren(parent, cachedModule, true); + if (!cachedModule.loaded) + return getExportsForCircularRequire(cachedModule); + return cachedModule.exports; + } + delete relativeResolveCache[relResolveCacheIdentifier]; + } + } + + const filename = Module._resolveFilename(request, parent, isMain); + if (StringPrototypeStartsWith(filename, 'node:')) { + // Slice 'node:' prefix + const id = StringPrototypeSlice(filename, 5); + + const module = loadNativeModule(id, request); + if (!module?.canBeRequiredByUsers) { + throw new ERR_UNKNOWN_BUILTIN_MODULE(filename); + } + + return module.exports; + } + + const cachedModule = Module._cache[filename]; + if (cachedModule !== undefined) { + updateChildren(parent, cachedModule, true); + if (!cachedModule.loaded) { + const parseCachedModule = cjsParseCache.get(cachedModule); + if (!parseCachedModule || parseCachedModule.loaded) + return getExportsForCircularRequire(cachedModule); + parseCachedModule.loaded = true; + } else { + return cachedModule.exports; + } + } + + const mod = loadNativeModule(filename, request); + if (mod?.canBeRequiredByUsers) return mod.exports; + + // Don't call updateChildren(), Module constructor already does. + const module = cachedModule || new Module(filename, parent); + + if (isMain) { + process.mainModule = module; + module.id = '.'; + } + + Module._cache[filename] = module; + if (parent !== undefined) { + relativeResolveCache[relResolveCacheIdentifier] = filename; + } + + let threw = true; + try { + module.load(filename); + threw = false; + } finally { + if (threw) { + delete Module._cache[filename]; + if (parent !== undefined) { + delete relativeResolveCache[relResolveCacheIdentifier]; + const children = parent?.children; + if (ArrayIsArray(children)) { + const index = ArrayPrototypeIndexOf(children, module); + if (index !== -1) { + ArrayPrototypeSplice(children, index, 1); + } + } + } + } else if (module.exports && + !isProxy(module.exports) && + ObjectGetPrototypeOf(module.exports) === + CircularRequirePrototypeWarningProxy) { + ObjectSetPrototypeOf(module.exports, ObjectPrototype); + } + } + + return module.exports; +}; + +Module._resolveFilename = function(request, parent, isMain, options) { + if (StringPrototypeStartsWith(request, 'node:') || + NativeModule.canBeRequiredByUsers(request)) { + return request; + } + + let paths; + + if (typeof options === 'object' && options !== null) { + if (ArrayIsArray(options.paths)) { + const isRelative = StringPrototypeStartsWith(request, './') || + StringPrototypeStartsWith(request, '../') || + ((isWindows && StringPrototypeStartsWith(request, '.\\')) || + StringPrototypeStartsWith(request, '..\\')); + + if (isRelative) { + paths = options.paths; + } else { + const fakeParent = new Module('', null); + + paths = []; + + for (let i = 0; i < options.paths.length; i++) { + const path = options.paths[i]; + fakeParent.paths = Module._nodeModulePaths(path); + const lookupPaths = Module._resolveLookupPaths(request, fakeParent); + + for (let j = 0; j < lookupPaths.length; j++) { + if (!ArrayPrototypeIncludes(paths, lookupPaths[j])) + ArrayPrototypePush(paths, lookupPaths[j]); + } + } + } + } else if (options.paths === undefined) { + paths = Module._resolveLookupPaths(request, parent); + } else { + throw new ERR_INVALID_ARG_VALUE('options.paths', options.paths); + } + } else { + paths = Module._resolveLookupPaths(request, parent); + } + + if (parent?.filename) { + if (request[0] === '#') { + const pkg = readPackageScope(parent.filename) || {}; + if (pkg.data?.imports != null) { + try { + return finalizeEsmResolution( + packageImportsResolve(request, pathToFileURL(parent.filename), + cjsConditions), parent.filename, + pkg.path); + } catch (e) { + if (e.code === 'ERR_MODULE_NOT_FOUND') + throw createEsmNotFoundErr(request); + throw e; + } + } + } + } + + // Try module self resolution first + const parentPath = trySelfParentPath(parent); + const selfResolved = trySelf(parentPath, request); + if (selfResolved) { + const cacheKey = request + '\x00' + + (paths.length === 1 ? paths[0] : ArrayPrototypeJoin(paths, '\x00')); + Module._pathCache[cacheKey] = selfResolved; + return selfResolved; + } + + // Look up the filename first, since that's the cache key. + const filename = Module._findPath(request, paths, isMain, false); + if (filename) return filename; + const requireStack = []; + for (let cursor = parent; + cursor; + cursor = moduleParentCache.get(cursor)) { + ArrayPrototypePush(requireStack, cursor.filename || cursor.id); + } + let message = `Cannot find module '${request}'`; + if (requireStack.length > 0) { + message = message + '\nRequire stack:\n- ' + + ArrayPrototypeJoin(requireStack, '\n- '); + } + // eslint-disable-next-line no-restricted-syntax + const err = new Error(message); + err.code = 'MODULE_NOT_FOUND'; + err.requireStack = requireStack; + throw err; +}; + +function finalizeEsmResolution(resolved, parentPath, pkgPath) { + if (RegExpPrototypeTest(encodedSepRegEx, resolved)) + throw new ERR_INVALID_MODULE_SPECIFIER( + resolved, 'must not include encoded "/" or "\\" characters', parentPath); + const filename = fileURLToPath(resolved); + const actual = tryFile(filename); + if (actual) + return actual; + const err = createEsmNotFoundErr(filename, + path.resolve(pkgPath, 'package.json')); + throw err; +} + +function createEsmNotFoundErr(request, path) { + // eslint-disable-next-line no-restricted-syntax + const err = new Error(`Cannot find module '${request}'`); + err.code = 'MODULE_NOT_FOUND'; + if (path) + err.path = path; + // TODO(BridgeAR): Add the requireStack as well. + return err; +} + +// Given a file name, pass it to the proper extension handler. +Module.prototype.load = function(filename) { + debug('load %j for module %j', filename, this.id); + + assert(!this.loaded); + this.filename = filename; + this.paths = Module._nodeModulePaths(path.dirname(filename)); + + const extension = findLongestRegisteredExtension(filename); + // allow .mjs to be overridden + if (StringPrototypeEndsWith(filename, '.mjs') && !Module._extensions['.mjs']) + throw new ERR_REQUIRE_ESM(filename, true); + + Module._extensions[extension](this, filename); + this.loaded = true; + + const esmLoader = asyncESM.esmLoader; + // Create module entry at load time to snapshot exports correctly + const exports = this.exports; + // Preemptively cache + if ((module?.module === undefined || + module.module.getStatus() < kEvaluated) && + !esmLoader.cjsCache.has(this)) + esmLoader.cjsCache.set(this, exports); +}; + + +// Loads a module at the given file path. Returns that module's +// `exports` property. +Module.prototype.require = function(id) { + validateString(id, 'id'); + if (id === '') { + throw new ERR_INVALID_ARG_VALUE('id', id, + 'must be a non-empty string'); + } + requireDepth++; + try { + return Module._load(id, this, /* isMain */ false); + } finally { + requireDepth--; + } +}; + + +// Resolved path to process.argv[1] will be lazily placed here +// (needed for setting breakpoint when called with --inspect-brk) +let resolvedArgv; +let hasPausedEntry = false; + +function wrapSafe(filename, content, cjsModuleInstance) { + if (patched) { + const wrapper = Module.wrap(content); + return vm.runInThisContext(wrapper, { + filename, + lineOffset: 0, + displayErrors: true, + importModuleDynamically: async (specifier) => { + const loader = asyncESM.esmLoader; + return loader.import(specifier, normalizeReferrerURL(filename)); + }, + }); + } + try { + return vm.compileFunction(content, [ + 'exports', + 'require', + 'module', + '__filename', + '__dirname', + ], { + filename, + importModuleDynamically(specifier) { + const loader = asyncESM.esmLoader; + return loader.import(specifier, normalizeReferrerURL(filename)); + }, + }); + } catch (err) { + if (process.mainModule === cjsModuleInstance) + enrichCJSError(err, content); + throw err; + } +} + +// Run the file contents in the correct scope or sandbox. Expose +// the correct helper variables (require, module, exports) to +// the file. +// Returns exception, if any. +Module.prototype._compile = function(content, filename) { + let moduleURL; + let redirects; + if (policy?.manifest) { + moduleURL = pathToFileURL(filename); + redirects = policy.manifest.getDependencyMapper(moduleURL); + policy.manifest.assertIntegrity(moduleURL, content); + } + + maybeCacheSourceMap(filename, content, this); + const compiledWrapper = wrapSafe(filename, content, this); + + let inspectorWrapper = null; + if (getOptionValue('--inspect-brk') && process._eval == null) { + if (!resolvedArgv) { + // We enter the repl if we're not given a filename argument. + if (process.argv[1]) { + try { + resolvedArgv = Module._resolveFilename(process.argv[1], null, false); + } catch { + // We only expect this codepath to be reached in the case of a + // preloaded module (it will fail earlier with the main entry) + assert(ArrayIsArray(getOptionValue('--require'))); + } + } else { + resolvedArgv = 'repl'; + } + } + + // Set breakpoint on module start + if (resolvedArgv && !hasPausedEntry && filename === resolvedArgv) { + hasPausedEntry = true; + inspectorWrapper = internalBinding('inspector').callAndPauseOnStart; + } + } + const dirname = path.dirname(filename); + const require = makeRequireFunction(this, redirects); + let result; + const exports = this.exports; + const thisValue = exports; + const module = this; + if (requireDepth === 0) statCache = new SafeMap(); + if (inspectorWrapper) { + result = inspectorWrapper(compiledWrapper, thisValue, exports, + require, module, filename, dirname); + } else { + result = ReflectApply(compiledWrapper, thisValue, + [exports, require, module, filename, dirname]); + } + hasLoadedAnyUserCJSModule = true; + if (requireDepth === 0) statCache = null; + return result; +}; + +// Native extension for .js +Module._extensions['.js'] = function(module, filename) { + // If already analyzed the source, then it will be cached. + const cached = cjsParseCache.get(module); + let content; + if (cached?.source) { + content = cached.source; + cached.source = undefined; + } else { + content = fs.readFileSync(filename, 'utf8'); + } + if (StringPrototypeEndsWith(filename, '.js')) { + const pkg = readPackageScope(filename); + // Function require shouldn't be used in ES modules. + if (pkg?.data?.type === 'module') { + const parent = moduleParentCache.get(module); + const parentPath = parent?.filename; + const packageJsonPath = path.resolve(pkg.path, 'package.json'); + const usesEsm = hasEsmSyntax(content); + const err = new ERR_REQUIRE_ESM(filename, usesEsm, parentPath, + packageJsonPath); + // Attempt to reconstruct the parent require frame. + if (Module._cache[parentPath]) { + let parentSource; + try { + parentSource = fs.readFileSync(parentPath, 'utf8'); + } catch {} + if (parentSource) { + const errLine = StringPrototypeSplit( + StringPrototypeSlice(err.stack, StringPrototypeIndexOf( + err.stack, ' at ')), '\n', 1)[0]; + const { 1: line, 2: col } = + RegExpPrototypeExec(/(\d+):(\d+)\)/, errLine) || []; + if (line && col) { + const srcLine = StringPrototypeSplit(parentSource, '\n')[line - 1]; + const frame = `${parentPath}:${line}\n${srcLine}\n${ + StringPrototypeRepeat(' ', col - 1)}^\n`; + setArrowMessage(err, frame); + } + } + } + throw err; + } + } + module._compile(content, filename); +}; + + +// Native extension for .json +Module._extensions['.json'] = function(module, filename) { + const content = fs.readFileSync(filename, 'utf8'); + + if (policy?.manifest) { + const moduleURL = pathToFileURL(filename); + policy.manifest.assertIntegrity(moduleURL, content); + } + + try { + module.exports = JSONParse(stripBOM(content)); + } catch (err) { + err.message = filename + ': ' + err.message; + throw err; + } +}; + + +// Native extension for .node +Module._extensions['.node'] = function(module, filename) { + if (policy?.manifest) { + const content = fs.readFileSync(filename); + const moduleURL = pathToFileURL(filename); + policy.manifest.assertIntegrity(moduleURL, content); + } + // Be aware this doesn't use `content` + return process.dlopen(module, path.toNamespacedPath(filename)); +}; + +function createRequireFromPath(filename) { + // Allow a directory to be passed as the filename + const trailingSlash = + StringPrototypeEndsWith(filename, '/') || + (isWindows && StringPrototypeEndsWith(filename, '\\')); + + const proxyPath = trailingSlash ? + path.join(filename, 'noop.js') : + filename; + + const m = new Module(proxyPath); + m.filename = proxyPath; + + m.paths = Module._nodeModulePaths(m.path); + return makeRequireFunction(m, null); +} + +const createRequireError = 'must be a file URL object, file URL string, or ' + + 'absolute path string'; + +function createRequire(filename) { + let filepath; + + if (isURLInstance(filename) || + (typeof filename === 'string' && !path.isAbsolute(filename))) { + try { + filepath = fileURLToPath(filename); + } catch { + throw new ERR_INVALID_ARG_VALUE('filename', filename, + createRequireError); + } + } else if (typeof filename !== 'string') { + throw new ERR_INVALID_ARG_VALUE('filename', filename, createRequireError); + } else { + filepath = filename; + } + return createRequireFromPath(filepath); +} + +Module.createRequire = createRequire; + +Module._initPaths = function() { + const homeDir = isWindows ? process.env.USERPROFILE : safeGetenv('HOME'); + const nodePath = isWindows ? process.env.NODE_PATH : safeGetenv('NODE_PATH'); + + // process.execPath is $PREFIX/bin/node except on Windows where it is + // $PREFIX\node.exe where $PREFIX is the root of the Node.js installation. + const prefixDir = isWindows ? + path.resolve(process.execPath, '..') : + path.resolve(process.execPath, '..', '..'); + + const paths = [path.resolve(prefixDir, 'lib', 'node')]; + + if (homeDir) { + ArrayPrototypeUnshift(paths, path.resolve(homeDir, '.node_libraries')); + ArrayPrototypeUnshift(paths, path.resolve(homeDir, '.node_modules')); + } + + if (nodePath) { + ArrayPrototypeUnshiftApply(paths, ArrayPrototypeFilter( + StringPrototypeSplit(nodePath, path.delimiter), + Boolean + )); + } + + modulePaths = paths; + + // Clone as a shallow copy, for introspection. + Module.globalPaths = ArrayPrototypeSlice(modulePaths); +}; + +Module._preloadModules = function(requests) { + if (!ArrayIsArray(requests)) + return; + + isPreloading = true; + + // Preloaded modules have a dummy parent module which is deemed to exist + // in the current working directory. This seeds the search path for + // preloaded modules. + const parent = new Module('internal/preload', null); + try { + parent.paths = Module._nodeModulePaths(process.cwd()); + } catch (e) { + if (e.code !== 'ENOENT') { + isPreloading = false; + throw e; + } + } + for (let n = 0; n < requests.length; n++) + parent.require(requests[n]); + isPreloading = false; +}; + +Module.syncBuiltinESMExports = function syncBuiltinESMExports() { + for (const mod of NativeModule.map.values()) { + if (mod.canBeRequiredByUsers) { + mod.syncExports(); + } + } +}; + +// Backwards compatibility +Module.Module = Module; diff --git a/raw/download-and-compare.sh b/raw/download-and-compare.sh new file mode 100644 index 000000000..88ce7751f --- /dev/null +++ b/raw/download-and-compare.sh @@ -0,0 +1,8 @@ +# No need to ever run this script. +# It serves as helpful documentation for where these files came from. + +curl https://raw.githubusercontent.com/nodejs/node/v17.0.1/lib/internal/modules/cjs/loader.js > ./node-internal-modules-cjs-loader-v17.0.1.js +diff raw/node-internal-modules-cjs-loader-v17.0.1.js dist-raw/node-internal-modules-cjs-loader.js + +curl https://raw.githubusercontent.com/nodejs/node/v13.12.0/lib/internal/modules/esm/resolve.js > ./node-internal-modules-esm-resolve-v13.12.0.js +diff raw/node-internal-modules-esm-resolve-v13.12.0.js dist-raw/node-internal-modules-esm-resolve.js diff --git a/raw/node-internal-modules-cjs-loader-v17.0.1.js b/raw/node-internal-modules-cjs-loader-v17.0.1.js new file mode 100644 index 000000000..622805ea7 --- /dev/null +++ b/raw/node-internal-modules-cjs-loader-v17.0.1.js @@ -0,0 +1,1283 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const { + ArrayIsArray, + ArrayPrototypeConcat, + ArrayPrototypeFilter, + ArrayPrototypeIncludes, + ArrayPrototypeIndexOf, + ArrayPrototypeJoin, + ArrayPrototypePush, + ArrayPrototypeSlice, + ArrayPrototypeSplice, + ArrayPrototypeUnshift, + ArrayPrototypeUnshiftApply, + Boolean, + Error, + JSONParse, + ObjectCreate, + ObjectDefineProperty, + ObjectFreeze, + ObjectGetOwnPropertyDescriptor, + ObjectGetPrototypeOf, + ObjectKeys, + ObjectPrototype, + ObjectPrototypeHasOwnProperty, + ObjectSetPrototypeOf, + Proxy, + ReflectApply, + ReflectSet, + RegExpPrototypeExec, + RegExpPrototypeTest, + SafeMap, + SafeWeakMap, + String, + StringPrototypeCharAt, + StringPrototypeCharCodeAt, + StringPrototypeEndsWith, + StringPrototypeLastIndexOf, + StringPrototypeIndexOf, + StringPrototypeMatch, + StringPrototypeRepeat, + StringPrototypeSlice, + StringPrototypeSplit, + StringPrototypeStartsWith, +} = primordials; + +// Map used to store CJS parsing data. +const cjsParseCache = new SafeWeakMap(); + +// Set first due to cycle with ESM loader functions. +module.exports = { + wrapSafe, Module, toRealPath, readPackageScope, cjsParseCache, + get hasLoadedAnyUserCJSModule() { return hasLoadedAnyUserCJSModule; } +}; + +const { NativeModule } = require('internal/bootstrap/loaders'); +const { + maybeCacheSourceMap, +} = require('internal/source_map/source_map_cache'); +const { pathToFileURL, fileURLToPath, isURLInstance } = require('internal/url'); +const { deprecate } = require('internal/util'); +const vm = require('vm'); +const assert = require('internal/assert'); +const fs = require('fs'); +const internalFS = require('internal/fs/utils'); +const path = require('path'); +const { sep } = path; +const { internalModuleStat } = internalBinding('fs'); +const packageJsonReader = require('internal/modules/package_json_reader'); +const { safeGetenv } = internalBinding('credentials'); +const { + cjsConditions, + hasEsmSyntax, + loadNativeModule, + makeRequireFunction, + normalizeReferrerURL, + stripBOM, +} = require('internal/modules/cjs/helpers'); +const { getOptionValue } = require('internal/options'); +const preserveSymlinks = getOptionValue('--preserve-symlinks'); +const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main'); +// Do not eagerly grab .manifest, it may be in TDZ +const policy = getOptionValue('--experimental-policy') ? + require('internal/process/policy') : + null; + +// Whether any user-provided CJS modules had been loaded (executed). +// Used for internal assertions. +let hasLoadedAnyUserCJSModule = false; + +const { + codes: { + ERR_INVALID_ARG_VALUE, + ERR_INVALID_MODULE_SPECIFIER, + ERR_REQUIRE_ESM, + ERR_UNKNOWN_BUILTIN_MODULE, + }, + setArrowMessage, +} = require('internal/errors'); +const { validateString } = require('internal/validators'); +const pendingDeprecation = getOptionValue('--pending-deprecation'); + +const { + CHAR_FORWARD_SLASH, + CHAR_BACKWARD_SLASH, + CHAR_COLON +} = require('internal/constants'); + +const { + isProxy +} = require('internal/util/types'); + +const asyncESM = require('internal/process/esm_loader'); +const { enrichCJSError } = require('internal/modules/esm/translators'); +const { kEvaluated } = internalBinding('module_wrap'); +const { + encodedSepRegEx, + packageExportsResolve, + packageImportsResolve +} = require('internal/modules/esm/resolve'); + +const isWindows = process.platform === 'win32'; + +const relativeResolveCache = ObjectCreate(null); + +let requireDepth = 0; +let statCache = null; +let isPreloading = false; + +function stat(filename) { + filename = path.toNamespacedPath(filename); + if (statCache !== null) { + const result = statCache.get(filename); + if (result !== undefined) return result; + } + const result = internalModuleStat(filename); + if (statCache !== null && result >= 0) { + // Only set cache when `internalModuleStat(filename)` succeeds. + statCache.set(filename, result); + } + return result; +} + +function updateChildren(parent, child, scan) { + const children = parent?.children; + if (children && !(scan && ArrayPrototypeIncludes(children, child))) + ArrayPrototypePush(children, child); +} + +const moduleParentCache = new SafeWeakMap(); +function Module(id = '', parent) { + this.id = id; + this.path = path.dirname(id); + this.exports = {}; + moduleParentCache.set(this, parent); + updateChildren(parent, this, false); + this.filename = null; + this.loaded = false; + this.children = []; +} + +const builtinModules = []; +for (const { 0: id, 1: mod } of NativeModule.map) { + if (mod.canBeRequiredByUsers) { + ArrayPrototypePush(builtinModules, id); + } +} + +ObjectFreeze(builtinModules); +Module.builtinModules = builtinModules; + +Module._cache = ObjectCreate(null); +Module._pathCache = ObjectCreate(null); +Module._extensions = ObjectCreate(null); +let modulePaths = []; +Module.globalPaths = []; + +let patched = false; + +// eslint-disable-next-line func-style +let wrap = function(script) { + return Module.wrapper[0] + script + Module.wrapper[1]; +}; + +const wrapper = [ + '(function (exports, require, module, __filename, __dirname) { ', + '\n});', +]; + +let wrapperProxy = new Proxy(wrapper, { + set(target, property, value, receiver) { + patched = true; + return ReflectSet(target, property, value, receiver); + }, + + defineProperty(target, property, descriptor) { + patched = true; + return ObjectDefineProperty(target, property, descriptor); + } +}); + +ObjectDefineProperty(Module, 'wrap', { + get() { + return wrap; + }, + + set(value) { + patched = true; + wrap = value; + } +}); + +ObjectDefineProperty(Module, 'wrapper', { + get() { + return wrapperProxy; + }, + + set(value) { + patched = true; + wrapperProxy = value; + } +}); + +const isPreloadingDesc = { get() { return isPreloading; } }; +ObjectDefineProperty(Module.prototype, 'isPreloading', isPreloadingDesc); +ObjectDefineProperty(NativeModule.prototype, 'isPreloading', isPreloadingDesc); + +function getModuleParent() { + return moduleParentCache.get(this); +} + +function setModuleParent(value) { + moduleParentCache.set(this, value); +} + +ObjectDefineProperty(Module.prototype, 'parent', { + get: pendingDeprecation ? deprecate( + getModuleParent, + 'module.parent is deprecated due to accuracy issues. Please use ' + + 'require.main to find program entry point instead.', + 'DEP0144' + ) : getModuleParent, + set: pendingDeprecation ? deprecate( + setModuleParent, + 'module.parent is deprecated due to accuracy issues. Please use ' + + 'require.main to find program entry point instead.', + 'DEP0144' + ) : setModuleParent, +}); + +let debug = require('internal/util/debuglog').debuglog('module', (fn) => { + debug = fn; +}); +Module._debug = deprecate(debug, 'Module._debug is deprecated.', 'DEP0077'); + +// Given a module name, and a list of paths to test, returns the first +// matching file in the following precedence. +// +// require("a.") +// -> a. +// +// require("a") +// -> a +// -> a. +// -> a/index. + +const packageJsonCache = new SafeMap(); + +function readPackage(requestPath) { + const jsonPath = path.resolve(requestPath, 'package.json'); + + const existing = packageJsonCache.get(jsonPath); + if (existing !== undefined) return existing; + + const result = packageJsonReader.read(jsonPath); + const json = result.containsKeys === false ? '{}' : result.string; + if (json === undefined) { + packageJsonCache.set(jsonPath, false); + return false; + } + + try { + const parsed = JSONParse(json); + const filtered = { + name: parsed.name, + main: parsed.main, + exports: parsed.exports, + imports: parsed.imports, + type: parsed.type + }; + packageJsonCache.set(jsonPath, filtered); + return filtered; + } catch (e) { + e.path = jsonPath; + e.message = 'Error parsing ' + jsonPath + ': ' + e.message; + throw e; + } +} + +function readPackageScope(checkPath) { + const rootSeparatorIndex = StringPrototypeIndexOf(checkPath, sep); + let separatorIndex; + do { + separatorIndex = StringPrototypeLastIndexOf(checkPath, sep); + checkPath = StringPrototypeSlice(checkPath, 0, separatorIndex); + if (StringPrototypeEndsWith(checkPath, sep + 'node_modules')) + return false; + const pjson = readPackage(checkPath + sep); + if (pjson) return { + data: pjson, + path: checkPath, + }; + } while (separatorIndex > rootSeparatorIndex); + return false; +} + +function tryPackage(requestPath, exts, isMain, originalPath) { + const pkg = readPackage(requestPath)?.main; + + if (!pkg) { + return tryExtensions(path.resolve(requestPath, 'index'), exts, isMain); + } + + const filename = path.resolve(requestPath, pkg); + let actual = tryFile(filename, isMain) || + tryExtensions(filename, exts, isMain) || + tryExtensions(path.resolve(filename, 'index'), exts, isMain); + if (actual === false) { + actual = tryExtensions(path.resolve(requestPath, 'index'), exts, isMain); + if (!actual) { + // eslint-disable-next-line no-restricted-syntax + const err = new Error( + `Cannot find module '${filename}'. ` + + 'Please verify that the package.json has a valid "main" entry' + ); + err.code = 'MODULE_NOT_FOUND'; + err.path = path.resolve(requestPath, 'package.json'); + err.requestPath = originalPath; + // TODO(BridgeAR): Add the requireStack as well. + throw err; + } else { + const jsonPath = path.resolve(requestPath, 'package.json'); + process.emitWarning( + `Invalid 'main' field in '${jsonPath}' of '${pkg}'. ` + + 'Please either fix that or report it to the module author', + 'DeprecationWarning', + 'DEP0128' + ); + } + } + return actual; +} + +// In order to minimize unnecessary lstat() calls, +// this cache is a list of known-real paths. +// Set to an empty Map to reset. +const realpathCache = new SafeMap(); + +// Check if the file exists and is not a directory +// if using --preserve-symlinks and isMain is false, +// keep symlinks intact, otherwise resolve to the +// absolute realpath. +function tryFile(requestPath, isMain) { + const rc = stat(requestPath); + if (rc !== 0) return; + if (preserveSymlinks && !isMain) { + return path.resolve(requestPath); + } + return toRealPath(requestPath); +} + +function toRealPath(requestPath) { + return fs.realpathSync(requestPath, { + [internalFS.realpathCacheKey]: realpathCache + }); +} + +// Given a path, check if the file exists with any of the set extensions +function tryExtensions(p, exts, isMain) { + for (let i = 0; i < exts.length; i++) { + const filename = tryFile(p + exts[i], isMain); + + if (filename) { + return filename; + } + } + return false; +} + +// Find the longest (possibly multi-dot) extension registered in +// Module._extensions +function findLongestRegisteredExtension(filename) { + const name = path.basename(filename); + let currentExtension; + let index; + let startIndex = 0; + while ((index = StringPrototypeIndexOf(name, '.', startIndex)) !== -1) { + startIndex = index + 1; + if (index === 0) continue; // Skip dotfiles like .gitignore + currentExtension = StringPrototypeSlice(name, index); + if (Module._extensions[currentExtension]) return currentExtension; + } + return '.js'; +} + +function trySelfParentPath(parent) { + if (!parent) return false; + + if (parent.filename) { + return parent.filename; + } else if (parent.id === '' || parent.id === 'internal/preload') { + try { + return process.cwd() + path.sep; + } catch { + return false; + } + } +} + +function trySelf(parentPath, request) { + if (!parentPath) return false; + + const { data: pkg, path: pkgPath } = readPackageScope(parentPath) || {}; + if (!pkg || pkg.exports === undefined) return false; + if (typeof pkg.name !== 'string') return false; + + let expansion; + if (request === pkg.name) { + expansion = '.'; + } else if (StringPrototypeStartsWith(request, `${pkg.name}/`)) { + expansion = '.' + StringPrototypeSlice(request, pkg.name.length); + } else { + return false; + } + + try { + return finalizeEsmResolution(packageExportsResolve( + pathToFileURL(pkgPath + '/package.json'), expansion, pkg, + pathToFileURL(parentPath), cjsConditions), parentPath, pkgPath); + } catch (e) { + if (e.code === 'ERR_MODULE_NOT_FOUND') + throw createEsmNotFoundErr(request, pkgPath + '/package.json'); + throw e; + } +} + +// This only applies to requests of a specific form: +// 1. name/.* +// 2. @scope/name/.* +const EXPORTS_PATTERN = /^((?:@[^/\\%]+\/)?[^./\\%][^/\\%]*)(\/.*)?$/; +function resolveExports(nmPath, request) { + // The implementation's behavior is meant to mirror resolution in ESM. + const { 1: name, 2: expansion = '' } = + StringPrototypeMatch(request, EXPORTS_PATTERN) || []; + if (!name) + return; + const pkgPath = path.resolve(nmPath, name); + const pkg = readPackage(pkgPath); + if (pkg?.exports != null) { + try { + return finalizeEsmResolution(packageExportsResolve( + pathToFileURL(pkgPath + '/package.json'), '.' + expansion, pkg, null, + cjsConditions), null, pkgPath); + } catch (e) { + if (e.code === 'ERR_MODULE_NOT_FOUND') + throw createEsmNotFoundErr(request, pkgPath + '/package.json'); + throw e; + } + } +} + +const trailingSlashRegex = /(?:^|\/)\.?\.$/; +Module._findPath = function(request, paths, isMain) { + const absoluteRequest = path.isAbsolute(request); + if (absoluteRequest) { + paths = ['']; + } else if (!paths || paths.length === 0) { + return false; + } + + const cacheKey = request + '\x00' + ArrayPrototypeJoin(paths, '\x00'); + const entry = Module._pathCache[cacheKey]; + if (entry) + return entry; + + let exts; + let trailingSlash = request.length > 0 && + StringPrototypeCharCodeAt(request, request.length - 1) === + CHAR_FORWARD_SLASH; + if (!trailingSlash) { + trailingSlash = RegExpPrototypeTest(trailingSlashRegex, request); + } + + // For each path + for (let i = 0; i < paths.length; i++) { + // Don't search further if path doesn't exist + const curPath = paths[i]; + if (curPath && stat(curPath) < 1) continue; + + if (!absoluteRequest) { + const exportsResolved = resolveExports(curPath, request); + if (exportsResolved) + return exportsResolved; + } + + const basePath = path.resolve(curPath, request); + let filename; + + const rc = stat(basePath); + if (!trailingSlash) { + if (rc === 0) { // File. + if (!isMain) { + if (preserveSymlinks) { + filename = path.resolve(basePath); + } else { + filename = toRealPath(basePath); + } + } else if (preserveSymlinksMain) { + // For the main module, we use the preserveSymlinksMain flag instead + // mainly for backward compatibility, as the preserveSymlinks flag + // historically has not applied to the main module. Most likely this + // was intended to keep .bin/ binaries working, as following those + // symlinks is usually required for the imports in the corresponding + // files to resolve; that said, in some use cases following symlinks + // causes bigger problems which is why the preserveSymlinksMain option + // is needed. + filename = path.resolve(basePath); + } else { + filename = toRealPath(basePath); + } + } + + if (!filename) { + // Try it with each of the extensions + if (exts === undefined) + exts = ObjectKeys(Module._extensions); + filename = tryExtensions(basePath, exts, isMain); + } + } + + if (!filename && rc === 1) { // Directory. + // try it with each of the extensions at "index" + if (exts === undefined) + exts = ObjectKeys(Module._extensions); + filename = tryPackage(basePath, exts, isMain, request); + } + + if (filename) { + Module._pathCache[cacheKey] = filename; + return filename; + } + } + + return false; +}; + +// 'node_modules' character codes reversed +const nmChars = [ 115, 101, 108, 117, 100, 111, 109, 95, 101, 100, 111, 110 ]; +const nmLen = nmChars.length; +if (isWindows) { + // 'from' is the __dirname of the module. + Module._nodeModulePaths = function(from) { + // Guarantee that 'from' is absolute. + from = path.resolve(from); + + // note: this approach *only* works when the path is guaranteed + // to be absolute. Doing a fully-edge-case-correct path.split + // that works on both Windows and Posix is non-trivial. + + // return root node_modules when path is 'D:\\'. + // path.resolve will make sure from.length >=3 in Windows. + if (StringPrototypeCharCodeAt(from, from.length - 1) === + CHAR_BACKWARD_SLASH && + StringPrototypeCharCodeAt(from, from.length - 2) === CHAR_COLON) + return [from + 'node_modules']; + + const paths = []; + for (let i = from.length - 1, p = 0, last = from.length; i >= 0; --i) { + const code = StringPrototypeCharCodeAt(from, i); + // The path segment separator check ('\' and '/') was used to get + // node_modules path for every path segment. + // Use colon as an extra condition since we can get node_modules + // path for drive root like 'C:\node_modules' and don't need to + // parse drive name. + if (code === CHAR_BACKWARD_SLASH || + code === CHAR_FORWARD_SLASH || + code === CHAR_COLON) { + if (p !== nmLen) + ArrayPrototypePush( + paths, + StringPrototypeSlice(from, 0, last) + '\\node_modules' + ); + last = i; + p = 0; + } else if (p !== -1) { + if (nmChars[p] === code) { + ++p; + } else { + p = -1; + } + } + } + + return paths; + }; +} else { // posix + // 'from' is the __dirname of the module. + Module._nodeModulePaths = function(from) { + // Guarantee that 'from' is absolute. + from = path.resolve(from); + // Return early not only to avoid unnecessary work, but to *avoid* returning + // an array of two items for a root: [ '//node_modules', '/node_modules' ] + if (from === '/') + return ['/node_modules']; + + // note: this approach *only* works when the path is guaranteed + // to be absolute. Doing a fully-edge-case-correct path.split + // that works on both Windows and Posix is non-trivial. + const paths = []; + for (let i = from.length - 1, p = 0, last = from.length; i >= 0; --i) { + const code = StringPrototypeCharCodeAt(from, i); + if (code === CHAR_FORWARD_SLASH) { + if (p !== nmLen) + ArrayPrototypePush( + paths, + StringPrototypeSlice(from, 0, last) + '/node_modules' + ); + last = i; + p = 0; + } else if (p !== -1) { + if (nmChars[p] === code) { + ++p; + } else { + p = -1; + } + } + } + + // Append /node_modules to handle root paths. + ArrayPrototypePush(paths, '/node_modules'); + + return paths; + }; +} + +Module._resolveLookupPaths = function(request, parent) { + if (NativeModule.canBeRequiredByUsers(request)) { + debug('looking for %j in []', request); + return null; + } + + // Check for node modules paths. + if (StringPrototypeCharAt(request, 0) !== '.' || + (request.length > 1 && + StringPrototypeCharAt(request, 1) !== '.' && + StringPrototypeCharAt(request, 1) !== '/' && + (!isWindows || StringPrototypeCharAt(request, 1) !== '\\'))) { + + let paths = modulePaths; + if (parent?.paths?.length) { + paths = ArrayPrototypeConcat(parent.paths, paths); + } + + debug('looking for %j in %j', request, paths); + return paths.length > 0 ? paths : null; + } + + // In REPL, parent.filename is null. + if (!parent || !parent.id || !parent.filename) { + // Make require('./path/to/foo') work - normally the path is taken + // from realpath(__filename) but in REPL there is no filename + const mainPaths = ['.']; + + debug('looking for %j in %j', request, mainPaths); + return mainPaths; + } + + debug('RELATIVE: requested: %s from parent.id %s', request, parent.id); + + const parentDir = [path.dirname(parent.filename)]; + debug('looking for %j', parentDir); + return parentDir; +}; + +function emitCircularRequireWarning(prop) { + process.emitWarning( + `Accessing non-existent property '${String(prop)}' of module exports ` + + 'inside circular dependency' + ); +} + +// A Proxy that can be used as the prototype of a module.exports object and +// warns when non-existent properties are accessed. +const CircularRequirePrototypeWarningProxy = new Proxy({}, { + get(target, prop) { + // Allow __esModule access in any case because it is used in the output + // of transpiled code to determine whether something comes from an + // ES module, and is not used as a regular key of `module.exports`. + if (prop in target || prop === '__esModule') return target[prop]; + emitCircularRequireWarning(prop); + return undefined; + }, + + getOwnPropertyDescriptor(target, prop) { + if (ObjectPrototypeHasOwnProperty(target, prop) || prop === '__esModule') + return ObjectGetOwnPropertyDescriptor(target, prop); + emitCircularRequireWarning(prop); + return undefined; + } +}); + +function getExportsForCircularRequire(module) { + if (module.exports && + !isProxy(module.exports) && + ObjectGetPrototypeOf(module.exports) === ObjectPrototype && + // Exclude transpiled ES6 modules / TypeScript code because those may + // employ unusual patterns for accessing 'module.exports'. That should + // be okay because ES6 modules have a different approach to circular + // dependencies anyway. + !module.exports.__esModule) { + // This is later unset once the module is done loading. + ObjectSetPrototypeOf( + module.exports, CircularRequirePrototypeWarningProxy); + } + + return module.exports; +} + +// Check the cache for the requested file. +// 1. If a module already exists in the cache: return its exports object. +// 2. If the module is native: call +// `NativeModule.prototype.compileForPublicLoader()` and return the exports. +// 3. Otherwise, create a new module for the file and save it to the cache. +// Then have it load the file contents before returning its exports +// object. +Module._load = function(request, parent, isMain) { + let relResolveCacheIdentifier; + if (parent) { + debug('Module._load REQUEST %s parent: %s', request, parent.id); + // Fast path for (lazy loaded) modules in the same directory. The indirect + // caching is required to allow cache invalidation without changing the old + // cache key names. + relResolveCacheIdentifier = `${parent.path}\x00${request}`; + const filename = relativeResolveCache[relResolveCacheIdentifier]; + if (filename !== undefined) { + const cachedModule = Module._cache[filename]; + if (cachedModule !== undefined) { + updateChildren(parent, cachedModule, true); + if (!cachedModule.loaded) + return getExportsForCircularRequire(cachedModule); + return cachedModule.exports; + } + delete relativeResolveCache[relResolveCacheIdentifier]; + } + } + + const filename = Module._resolveFilename(request, parent, isMain); + if (StringPrototypeStartsWith(filename, 'node:')) { + // Slice 'node:' prefix + const id = StringPrototypeSlice(filename, 5); + + const module = loadNativeModule(id, request); + if (!module?.canBeRequiredByUsers) { + throw new ERR_UNKNOWN_BUILTIN_MODULE(filename); + } + + return module.exports; + } + + const cachedModule = Module._cache[filename]; + if (cachedModule !== undefined) { + updateChildren(parent, cachedModule, true); + if (!cachedModule.loaded) { + const parseCachedModule = cjsParseCache.get(cachedModule); + if (!parseCachedModule || parseCachedModule.loaded) + return getExportsForCircularRequire(cachedModule); + parseCachedModule.loaded = true; + } else { + return cachedModule.exports; + } + } + + const mod = loadNativeModule(filename, request); + if (mod?.canBeRequiredByUsers) return mod.exports; + + // Don't call updateChildren(), Module constructor already does. + const module = cachedModule || new Module(filename, parent); + + if (isMain) { + process.mainModule = module; + module.id = '.'; + } + + Module._cache[filename] = module; + if (parent !== undefined) { + relativeResolveCache[relResolveCacheIdentifier] = filename; + } + + let threw = true; + try { + module.load(filename); + threw = false; + } finally { + if (threw) { + delete Module._cache[filename]; + if (parent !== undefined) { + delete relativeResolveCache[relResolveCacheIdentifier]; + const children = parent?.children; + if (ArrayIsArray(children)) { + const index = ArrayPrototypeIndexOf(children, module); + if (index !== -1) { + ArrayPrototypeSplice(children, index, 1); + } + } + } + } else if (module.exports && + !isProxy(module.exports) && + ObjectGetPrototypeOf(module.exports) === + CircularRequirePrototypeWarningProxy) { + ObjectSetPrototypeOf(module.exports, ObjectPrototype); + } + } + + return module.exports; +}; + +Module._resolveFilename = function(request, parent, isMain, options) { + if (StringPrototypeStartsWith(request, 'node:') || + NativeModule.canBeRequiredByUsers(request)) { + return request; + } + + let paths; + + if (typeof options === 'object' && options !== null) { + if (ArrayIsArray(options.paths)) { + const isRelative = StringPrototypeStartsWith(request, './') || + StringPrototypeStartsWith(request, '../') || + ((isWindows && StringPrototypeStartsWith(request, '.\\')) || + StringPrototypeStartsWith(request, '..\\')); + + if (isRelative) { + paths = options.paths; + } else { + const fakeParent = new Module('', null); + + paths = []; + + for (let i = 0; i < options.paths.length; i++) { + const path = options.paths[i]; + fakeParent.paths = Module._nodeModulePaths(path); + const lookupPaths = Module._resolveLookupPaths(request, fakeParent); + + for (let j = 0; j < lookupPaths.length; j++) { + if (!ArrayPrototypeIncludes(paths, lookupPaths[j])) + ArrayPrototypePush(paths, lookupPaths[j]); + } + } + } + } else if (options.paths === undefined) { + paths = Module._resolveLookupPaths(request, parent); + } else { + throw new ERR_INVALID_ARG_VALUE('options.paths', options.paths); + } + } else { + paths = Module._resolveLookupPaths(request, parent); + } + + if (parent?.filename) { + if (request[0] === '#') { + const pkg = readPackageScope(parent.filename) || {}; + if (pkg.data?.imports != null) { + try { + return finalizeEsmResolution( + packageImportsResolve(request, pathToFileURL(parent.filename), + cjsConditions), parent.filename, + pkg.path); + } catch (e) { + if (e.code === 'ERR_MODULE_NOT_FOUND') + throw createEsmNotFoundErr(request); + throw e; + } + } + } + } + + // Try module self resolution first + const parentPath = trySelfParentPath(parent); + const selfResolved = trySelf(parentPath, request); + if (selfResolved) { + const cacheKey = request + '\x00' + + (paths.length === 1 ? paths[0] : ArrayPrototypeJoin(paths, '\x00')); + Module._pathCache[cacheKey] = selfResolved; + return selfResolved; + } + + // Look up the filename first, since that's the cache key. + const filename = Module._findPath(request, paths, isMain, false); + if (filename) return filename; + const requireStack = []; + for (let cursor = parent; + cursor; + cursor = moduleParentCache.get(cursor)) { + ArrayPrototypePush(requireStack, cursor.filename || cursor.id); + } + let message = `Cannot find module '${request}'`; + if (requireStack.length > 0) { + message = message + '\nRequire stack:\n- ' + + ArrayPrototypeJoin(requireStack, '\n- '); + } + // eslint-disable-next-line no-restricted-syntax + const err = new Error(message); + err.code = 'MODULE_NOT_FOUND'; + err.requireStack = requireStack; + throw err; +}; + +function finalizeEsmResolution(resolved, parentPath, pkgPath) { + if (RegExpPrototypeTest(encodedSepRegEx, resolved)) + throw new ERR_INVALID_MODULE_SPECIFIER( + resolved, 'must not include encoded "/" or "\\" characters', parentPath); + const filename = fileURLToPath(resolved); + const actual = tryFile(filename); + if (actual) + return actual; + const err = createEsmNotFoundErr(filename, + path.resolve(pkgPath, 'package.json')); + throw err; +} + +function createEsmNotFoundErr(request, path) { + // eslint-disable-next-line no-restricted-syntax + const err = new Error(`Cannot find module '${request}'`); + err.code = 'MODULE_NOT_FOUND'; + if (path) + err.path = path; + // TODO(BridgeAR): Add the requireStack as well. + return err; +} + +// Given a file name, pass it to the proper extension handler. +Module.prototype.load = function(filename) { + debug('load %j for module %j', filename, this.id); + + assert(!this.loaded); + this.filename = filename; + this.paths = Module._nodeModulePaths(path.dirname(filename)); + + const extension = findLongestRegisteredExtension(filename); + // allow .mjs to be overridden + if (StringPrototypeEndsWith(filename, '.mjs') && !Module._extensions['.mjs']) + throw new ERR_REQUIRE_ESM(filename, true); + + Module._extensions[extension](this, filename); + this.loaded = true; + + const esmLoader = asyncESM.esmLoader; + // Create module entry at load time to snapshot exports correctly + const exports = this.exports; + // Preemptively cache + if ((module?.module === undefined || + module.module.getStatus() < kEvaluated) && + !esmLoader.cjsCache.has(this)) + esmLoader.cjsCache.set(this, exports); +}; + + +// Loads a module at the given file path. Returns that module's +// `exports` property. +Module.prototype.require = function(id) { + validateString(id, 'id'); + if (id === '') { + throw new ERR_INVALID_ARG_VALUE('id', id, + 'must be a non-empty string'); + } + requireDepth++; + try { + return Module._load(id, this, /* isMain */ false); + } finally { + requireDepth--; + } +}; + + +// Resolved path to process.argv[1] will be lazily placed here +// (needed for setting breakpoint when called with --inspect-brk) +let resolvedArgv; +let hasPausedEntry = false; + +function wrapSafe(filename, content, cjsModuleInstance) { + if (patched) { + const wrapper = Module.wrap(content); + return vm.runInThisContext(wrapper, { + filename, + lineOffset: 0, + displayErrors: true, + importModuleDynamically: async (specifier) => { + const loader = asyncESM.esmLoader; + return loader.import(specifier, normalizeReferrerURL(filename)); + }, + }); + } + try { + return vm.compileFunction(content, [ + 'exports', + 'require', + 'module', + '__filename', + '__dirname', + ], { + filename, + importModuleDynamically(specifier) { + const loader = asyncESM.esmLoader; + return loader.import(specifier, normalizeReferrerURL(filename)); + }, + }); + } catch (err) { + if (process.mainModule === cjsModuleInstance) + enrichCJSError(err, content); + throw err; + } +} + +// Run the file contents in the correct scope or sandbox. Expose +// the correct helper variables (require, module, exports) to +// the file. +// Returns exception, if any. +Module.prototype._compile = function(content, filename) { + let moduleURL; + let redirects; + if (policy?.manifest) { + moduleURL = pathToFileURL(filename); + redirects = policy.manifest.getDependencyMapper(moduleURL); + policy.manifest.assertIntegrity(moduleURL, content); + } + + maybeCacheSourceMap(filename, content, this); + const compiledWrapper = wrapSafe(filename, content, this); + + let inspectorWrapper = null; + if (getOptionValue('--inspect-brk') && process._eval == null) { + if (!resolvedArgv) { + // We enter the repl if we're not given a filename argument. + if (process.argv[1]) { + try { + resolvedArgv = Module._resolveFilename(process.argv[1], null, false); + } catch { + // We only expect this codepath to be reached in the case of a + // preloaded module (it will fail earlier with the main entry) + assert(ArrayIsArray(getOptionValue('--require'))); + } + } else { + resolvedArgv = 'repl'; + } + } + + // Set breakpoint on module start + if (resolvedArgv && !hasPausedEntry && filename === resolvedArgv) { + hasPausedEntry = true; + inspectorWrapper = internalBinding('inspector').callAndPauseOnStart; + } + } + const dirname = path.dirname(filename); + const require = makeRequireFunction(this, redirects); + let result; + const exports = this.exports; + const thisValue = exports; + const module = this; + if (requireDepth === 0) statCache = new SafeMap(); + if (inspectorWrapper) { + result = inspectorWrapper(compiledWrapper, thisValue, exports, + require, module, filename, dirname); + } else { + result = ReflectApply(compiledWrapper, thisValue, + [exports, require, module, filename, dirname]); + } + hasLoadedAnyUserCJSModule = true; + if (requireDepth === 0) statCache = null; + return result; +}; + +// Native extension for .js +Module._extensions['.js'] = function(module, filename) { + // If already analyzed the source, then it will be cached. + const cached = cjsParseCache.get(module); + let content; + if (cached?.source) { + content = cached.source; + cached.source = undefined; + } else { + content = fs.readFileSync(filename, 'utf8'); + } + if (StringPrototypeEndsWith(filename, '.js')) { + const pkg = readPackageScope(filename); + // Function require shouldn't be used in ES modules. + if (pkg?.data?.type === 'module') { + const parent = moduleParentCache.get(module); + const parentPath = parent?.filename; + const packageJsonPath = path.resolve(pkg.path, 'package.json'); + const usesEsm = hasEsmSyntax(content); + const err = new ERR_REQUIRE_ESM(filename, usesEsm, parentPath, + packageJsonPath); + // Attempt to reconstruct the parent require frame. + if (Module._cache[parentPath]) { + let parentSource; + try { + parentSource = fs.readFileSync(parentPath, 'utf8'); + } catch {} + if (parentSource) { + const errLine = StringPrototypeSplit( + StringPrototypeSlice(err.stack, StringPrototypeIndexOf( + err.stack, ' at ')), '\n', 1)[0]; + const { 1: line, 2: col } = + RegExpPrototypeExec(/(\d+):(\d+)\)/, errLine) || []; + if (line && col) { + const srcLine = StringPrototypeSplit(parentSource, '\n')[line - 1]; + const frame = `${parentPath}:${line}\n${srcLine}\n${ + StringPrototypeRepeat(' ', col - 1)}^\n`; + setArrowMessage(err, frame); + } + } + } + throw err; + } + } + module._compile(content, filename); +}; + + +// Native extension for .json +Module._extensions['.json'] = function(module, filename) { + const content = fs.readFileSync(filename, 'utf8'); + + if (policy?.manifest) { + const moduleURL = pathToFileURL(filename); + policy.manifest.assertIntegrity(moduleURL, content); + } + + try { + module.exports = JSONParse(stripBOM(content)); + } catch (err) { + err.message = filename + ': ' + err.message; + throw err; + } +}; + + +// Native extension for .node +Module._extensions['.node'] = function(module, filename) { + if (policy?.manifest) { + const content = fs.readFileSync(filename); + const moduleURL = pathToFileURL(filename); + policy.manifest.assertIntegrity(moduleURL, content); + } + // Be aware this doesn't use `content` + return process.dlopen(module, path.toNamespacedPath(filename)); +}; + +function createRequireFromPath(filename) { + // Allow a directory to be passed as the filename + const trailingSlash = + StringPrototypeEndsWith(filename, '/') || + (isWindows && StringPrototypeEndsWith(filename, '\\')); + + const proxyPath = trailingSlash ? + path.join(filename, 'noop.js') : + filename; + + const m = new Module(proxyPath); + m.filename = proxyPath; + + m.paths = Module._nodeModulePaths(m.path); + return makeRequireFunction(m, null); +} + +const createRequireError = 'must be a file URL object, file URL string, or ' + + 'absolute path string'; + +function createRequire(filename) { + let filepath; + + if (isURLInstance(filename) || + (typeof filename === 'string' && !path.isAbsolute(filename))) { + try { + filepath = fileURLToPath(filename); + } catch { + throw new ERR_INVALID_ARG_VALUE('filename', filename, + createRequireError); + } + } else if (typeof filename !== 'string') { + throw new ERR_INVALID_ARG_VALUE('filename', filename, createRequireError); + } else { + filepath = filename; + } + return createRequireFromPath(filepath); +} + +Module.createRequire = createRequire; + +Module._initPaths = function() { + const homeDir = isWindows ? process.env.USERPROFILE : safeGetenv('HOME'); + const nodePath = isWindows ? process.env.NODE_PATH : safeGetenv('NODE_PATH'); + + // process.execPath is $PREFIX/bin/node except on Windows where it is + // $PREFIX\node.exe where $PREFIX is the root of the Node.js installation. + const prefixDir = isWindows ? + path.resolve(process.execPath, '..') : + path.resolve(process.execPath, '..', '..'); + + const paths = [path.resolve(prefixDir, 'lib', 'node')]; + + if (homeDir) { + ArrayPrototypeUnshift(paths, path.resolve(homeDir, '.node_libraries')); + ArrayPrototypeUnshift(paths, path.resolve(homeDir, '.node_modules')); + } + + if (nodePath) { + ArrayPrototypeUnshiftApply(paths, ArrayPrototypeFilter( + StringPrototypeSplit(nodePath, path.delimiter), + Boolean + )); + } + + modulePaths = paths; + + // Clone as a shallow copy, for introspection. + Module.globalPaths = ArrayPrototypeSlice(modulePaths); +}; + +Module._preloadModules = function(requests) { + if (!ArrayIsArray(requests)) + return; + + isPreloading = true; + + // Preloaded modules have a dummy parent module which is deemed to exist + // in the current working directory. This seeds the search path for + // preloaded modules. + const parent = new Module('internal/preload', null); + try { + parent.paths = Module._nodeModulePaths(process.cwd()); + } catch (e) { + if (e.code !== 'ENOENT') { + isPreloading = false; + throw e; + } + } + for (let n = 0; n < requests.length; n++) + parent.require(requests[n]); + isPreloading = false; +}; + +Module.syncBuiltinESMExports = function syncBuiltinESMExports() { + for (const mod of NativeModule.map.values()) { + if (mod.canBeRequiredByUsers) { + mod.syncExports(); + } + } +}; + +// Backwards compatibility +Module.Module = Module; From 88dedb38d106da102436911088122a2d1c59ebbb Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 13:37:47 -0400 Subject: [PATCH 4/8] fix downloaded artifacts --- raw/download-and-compare.sh | 40 ++++++++++++++--- ...ode-internal-modules-cjs-loader-v17.0.1.js | 2 + ...e-internal-modules-esm-resolve-v13.12.0.js | 3 +- ...js => node-internal-repl-await-v17.0.0.js} | 44 ++++++++++--------- 4 files changed, 62 insertions(+), 27 deletions(-) mode change 100644 => 100755 raw/download-and-compare.sh rename raw/{node-internal-repl-await.js => node-internal-repl-await-v17.0.0.js} (87%) diff --git a/raw/download-and-compare.sh b/raw/download-and-compare.sh old mode 100644 new mode 100755 index 88ce7751f..db98d901b --- a/raw/download-and-compare.sh +++ b/raw/download-and-compare.sh @@ -1,8 +1,36 @@ -# No need to ever run this script. -# It serves as helpful documentation for where these files came from. +#!/usr/bin/env bash +set -euo pipefail +shopt -s inherit_errexit +__dirname="$(CDPATH= cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$__dirname" +set -x -curl https://raw.githubusercontent.com/nodejs/node/v17.0.1/lib/internal/modules/cjs/loader.js > ./node-internal-modules-cjs-loader-v17.0.1.js -diff raw/node-internal-modules-cjs-loader-v17.0.1.js dist-raw/node-internal-modules-cjs-loader.js +# This script serves as helpful documentation for where these files came from. -curl https://raw.githubusercontent.com/nodejs/node/v13.12.0/lib/internal/modules/esm/resolve.js > ./node-internal-modules-esm-resolve-v13.12.0.js -diff raw/node-internal-modules-esm-resolve-v13.12.0.js dist-raw/node-internal-modules-esm-resolve.js +function download() { + echo "// Copied from https://github.com/nodejs/node/blob/$version/$path$ext"$'\n' > "$local-$version$ext" + curl "https://raw.githubusercontent.com/nodejs/node/$version/$path$ext" >> "$local-$version$ext" +} +compare() { + diff "$local-$version$ext" "../dist-raw/$local$ext" || true +} + +ext=.js + +version=v17.0.1 +path=lib/internal/modules/cjs/loader +local=node-internal-modules-cjs-loader +download +compare + +version=v13.12.0 +path=lib/internal/modules/esm/resolve +local=node-internal-modules-esm-resolve +download +compare + +version=v17.0.0 +path=lib/internal/repl/await +local=node-internal-repl-await +download +compare diff --git a/raw/node-internal-modules-cjs-loader-v17.0.1.js b/raw/node-internal-modules-cjs-loader-v17.0.1.js index 622805ea7..95f9a158c 100644 --- a/raw/node-internal-modules-cjs-loader-v17.0.1.js +++ b/raw/node-internal-modules-cjs-loader-v17.0.1.js @@ -1,3 +1,5 @@ +// Copied from https://github.com/nodejs/node/blob/v17.0.1/lib/internal/modules/cjs/loader.js + // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a diff --git a/raw/node-internal-modules-esm-resolve-v13.12.0.js b/raw/node-internal-modules-esm-resolve-v13.12.0.js index 1c2b8e67f..0313bb4d0 100644 --- a/raw/node-internal-modules-esm-resolve-v13.12.0.js +++ b/raw/node-internal-modules-esm-resolve-v13.12.0.js @@ -1,3 +1,5 @@ +// Copied from https://github.com/nodejs/node/blob/v13.12.0/lib/internal/modules/esm/resolve.js + 'use strict'; const { @@ -661,4 +663,3 @@ module.exports = { defaultResolve, getPackageType }; - diff --git a/raw/node-internal-repl-await.js b/raw/node-internal-repl-await-v17.0.0.js similarity index 87% rename from raw/node-internal-repl-await.js rename to raw/node-internal-repl-await-v17.0.0.js index b8b9153f4..db79785a0 100644 --- a/raw/node-internal-repl-await.js +++ b/raw/node-internal-repl-await-v17.0.0.js @@ -1,8 +1,8 @@ -// downloaded from https://github.com/nodejs/node/blob/88799930794045795e8abac874730f9eba7e2300/lib/internal/repl/await.js +// Copied from https://github.com/nodejs/node/blob/v17.0.0/lib/internal/repl/await.js + 'use strict'; const { - ArrayFrom, ArrayPrototypeForEach, ArrayPrototypeIncludes, ArrayPrototypeJoin, @@ -48,7 +48,7 @@ const visitorsWithoutAncestors = { walk.base.ForOfStatement(node, state, c); }, FunctionDeclaration(node, state, c) { - state.prepend(node, `${node.id.name}=`); + state.prepend(node, `this.${node.id.name} = ${node.id.name}; `); ArrayPrototypePush( state.hoistedDeclarationStatements, `var ${node.id.name}; ` @@ -156,7 +156,7 @@ for (const nodeType of ObjectKeys(walk.base)) { function processTopLevelAwait(src) { const wrapPrefix = '(async () => { '; const wrapped = `${wrapPrefix}${src} })()`; - const wrappedArray = ArrayFrom(wrapped); + const wrappedArray = StringPrototypeSplit(wrapped, ''); let root; try { root = parser.parse(wrapped, { ecmaVersion: 'latest' }); @@ -223,22 +223,26 @@ function processTopLevelAwait(src) { return null; } - const last = body.body[body.body.length - 1]; - if (last.type === 'ExpressionStatement') { - // For an expression statement of the form - // ( expr ) ; - // ^^^^^^^^^^ // last - // ^^^^ // last.expression - // - // We do not want the left parenthesis before the `return` keyword; - // therefore we prepend the `return (` to `last`. - // - // On the other hand, we do not want the right parenthesis after the - // semicolon. Since there can only be more right parentheses between - // last.expression.end and the semicolon, appending one more to - // last.expression should be fine. - state.prepend(last, 'return ('); - state.append(last.expression, ')'); + for (let i = body.body.length - 1; i >= 0; i--) { + const node = body.body[i]; + if (node.type === 'EmptyStatement') continue; + if (node.type === 'ExpressionStatement') { + // For an expression statement of the form + // ( expr ) ; + // ^^^^^^^^^^ // node + // ^^^^ // node.expression + // + // We do not want the left parenthesis before the `return` keyword; + // therefore we prepend the `return (` to `node`. + // + // On the other hand, we do not want the right parenthesis after the + // semicolon. Since there can only be more right parentheses between + // node.expression.end and the semicolon, appending one more to + // node.expression should be fine. + state.prepend(node, 'return ('); + state.append(node.expression, ')'); + } + break; } return ( From 70a6eb0c4d39c1a484ecfe5f75397f951d9fcf37 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 13:45:29 -0400 Subject: [PATCH 5/8] sync --- dist-raw/node-internal-modules-cjs-loader.js | 277 ------------------- 1 file changed, 277 deletions(-) diff --git a/dist-raw/node-internal-modules-cjs-loader.js b/dist-raw/node-internal-modules-cjs-loader.js index 622805ea7..8f2719f90 100644 --- a/dist-raw/node-internal-modules-cjs-loader.js +++ b/dist-raw/node-internal-modules-cjs-loader.js @@ -747,104 +747,6 @@ function getExportsForCircularRequire(module) { return module.exports; } -// Check the cache for the requested file. -// 1. If a module already exists in the cache: return its exports object. -// 2. If the module is native: call -// `NativeModule.prototype.compileForPublicLoader()` and return the exports. -// 3. Otherwise, create a new module for the file and save it to the cache. -// Then have it load the file contents before returning its exports -// object. -Module._load = function(request, parent, isMain) { - let relResolveCacheIdentifier; - if (parent) { - debug('Module._load REQUEST %s parent: %s', request, parent.id); - // Fast path for (lazy loaded) modules in the same directory. The indirect - // caching is required to allow cache invalidation without changing the old - // cache key names. - relResolveCacheIdentifier = `${parent.path}\x00${request}`; - const filename = relativeResolveCache[relResolveCacheIdentifier]; - if (filename !== undefined) { - const cachedModule = Module._cache[filename]; - if (cachedModule !== undefined) { - updateChildren(parent, cachedModule, true); - if (!cachedModule.loaded) - return getExportsForCircularRequire(cachedModule); - return cachedModule.exports; - } - delete relativeResolveCache[relResolveCacheIdentifier]; - } - } - - const filename = Module._resolveFilename(request, parent, isMain); - if (StringPrototypeStartsWith(filename, 'node:')) { - // Slice 'node:' prefix - const id = StringPrototypeSlice(filename, 5); - - const module = loadNativeModule(id, request); - if (!module?.canBeRequiredByUsers) { - throw new ERR_UNKNOWN_BUILTIN_MODULE(filename); - } - - return module.exports; - } - - const cachedModule = Module._cache[filename]; - if (cachedModule !== undefined) { - updateChildren(parent, cachedModule, true); - if (!cachedModule.loaded) { - const parseCachedModule = cjsParseCache.get(cachedModule); - if (!parseCachedModule || parseCachedModule.loaded) - return getExportsForCircularRequire(cachedModule); - parseCachedModule.loaded = true; - } else { - return cachedModule.exports; - } - } - - const mod = loadNativeModule(filename, request); - if (mod?.canBeRequiredByUsers) return mod.exports; - - // Don't call updateChildren(), Module constructor already does. - const module = cachedModule || new Module(filename, parent); - - if (isMain) { - process.mainModule = module; - module.id = '.'; - } - - Module._cache[filename] = module; - if (parent !== undefined) { - relativeResolveCache[relResolveCacheIdentifier] = filename; - } - - let threw = true; - try { - module.load(filename); - threw = false; - } finally { - if (threw) { - delete Module._cache[filename]; - if (parent !== undefined) { - delete relativeResolveCache[relResolveCacheIdentifier]; - const children = parent?.children; - if (ArrayIsArray(children)) { - const index = ArrayPrototypeIndexOf(children, module); - if (index !== -1) { - ArrayPrototypeSplice(children, index, 1); - } - } - } - } else if (module.exports && - !isProxy(module.exports) && - ObjectGetPrototypeOf(module.exports) === - CircularRequirePrototypeWarningProxy) { - ObjectSetPrototypeOf(module.exports, ObjectPrototype); - } - } - - return module.exports; -}; - Module._resolveFilename = function(request, parent, isMain, options) { if (StringPrototypeStartsWith(request, 'node:') || NativeModule.canBeRequiredByUsers(request)) { @@ -959,50 +861,6 @@ function createEsmNotFoundErr(request, path) { return err; } -// Given a file name, pass it to the proper extension handler. -Module.prototype.load = function(filename) { - debug('load %j for module %j', filename, this.id); - - assert(!this.loaded); - this.filename = filename; - this.paths = Module._nodeModulePaths(path.dirname(filename)); - - const extension = findLongestRegisteredExtension(filename); - // allow .mjs to be overridden - if (StringPrototypeEndsWith(filename, '.mjs') && !Module._extensions['.mjs']) - throw new ERR_REQUIRE_ESM(filename, true); - - Module._extensions[extension](this, filename); - this.loaded = true; - - const esmLoader = asyncESM.esmLoader; - // Create module entry at load time to snapshot exports correctly - const exports = this.exports; - // Preemptively cache - if ((module?.module === undefined || - module.module.getStatus() < kEvaluated) && - !esmLoader.cjsCache.has(this)) - esmLoader.cjsCache.set(this, exports); -}; - - -// Loads a module at the given file path. Returns that module's -// `exports` property. -Module.prototype.require = function(id) { - validateString(id, 'id'); - if (id === '') { - throw new ERR_INVALID_ARG_VALUE('id', id, - 'must be a non-empty string'); - } - requireDepth++; - try { - return Module._load(id, this, /* isMain */ false); - } finally { - requireDepth--; - } -}; - - // Resolved path to process.argv[1] will be lazily placed here // (needed for setting breakpoint when called with --inspect-brk) let resolvedArgv; @@ -1042,141 +900,6 @@ function wrapSafe(filename, content, cjsModuleInstance) { } } -// Run the file contents in the correct scope or sandbox. Expose -// the correct helper variables (require, module, exports) to -// the file. -// Returns exception, if any. -Module.prototype._compile = function(content, filename) { - let moduleURL; - let redirects; - if (policy?.manifest) { - moduleURL = pathToFileURL(filename); - redirects = policy.manifest.getDependencyMapper(moduleURL); - policy.manifest.assertIntegrity(moduleURL, content); - } - - maybeCacheSourceMap(filename, content, this); - const compiledWrapper = wrapSafe(filename, content, this); - - let inspectorWrapper = null; - if (getOptionValue('--inspect-brk') && process._eval == null) { - if (!resolvedArgv) { - // We enter the repl if we're not given a filename argument. - if (process.argv[1]) { - try { - resolvedArgv = Module._resolveFilename(process.argv[1], null, false); - } catch { - // We only expect this codepath to be reached in the case of a - // preloaded module (it will fail earlier with the main entry) - assert(ArrayIsArray(getOptionValue('--require'))); - } - } else { - resolvedArgv = 'repl'; - } - } - - // Set breakpoint on module start - if (resolvedArgv && !hasPausedEntry && filename === resolvedArgv) { - hasPausedEntry = true; - inspectorWrapper = internalBinding('inspector').callAndPauseOnStart; - } - } - const dirname = path.dirname(filename); - const require = makeRequireFunction(this, redirects); - let result; - const exports = this.exports; - const thisValue = exports; - const module = this; - if (requireDepth === 0) statCache = new SafeMap(); - if (inspectorWrapper) { - result = inspectorWrapper(compiledWrapper, thisValue, exports, - require, module, filename, dirname); - } else { - result = ReflectApply(compiledWrapper, thisValue, - [exports, require, module, filename, dirname]); - } - hasLoadedAnyUserCJSModule = true; - if (requireDepth === 0) statCache = null; - return result; -}; - -// Native extension for .js -Module._extensions['.js'] = function(module, filename) { - // If already analyzed the source, then it will be cached. - const cached = cjsParseCache.get(module); - let content; - if (cached?.source) { - content = cached.source; - cached.source = undefined; - } else { - content = fs.readFileSync(filename, 'utf8'); - } - if (StringPrototypeEndsWith(filename, '.js')) { - const pkg = readPackageScope(filename); - // Function require shouldn't be used in ES modules. - if (pkg?.data?.type === 'module') { - const parent = moduleParentCache.get(module); - const parentPath = parent?.filename; - const packageJsonPath = path.resolve(pkg.path, 'package.json'); - const usesEsm = hasEsmSyntax(content); - const err = new ERR_REQUIRE_ESM(filename, usesEsm, parentPath, - packageJsonPath); - // Attempt to reconstruct the parent require frame. - if (Module._cache[parentPath]) { - let parentSource; - try { - parentSource = fs.readFileSync(parentPath, 'utf8'); - } catch {} - if (parentSource) { - const errLine = StringPrototypeSplit( - StringPrototypeSlice(err.stack, StringPrototypeIndexOf( - err.stack, ' at ')), '\n', 1)[0]; - const { 1: line, 2: col } = - RegExpPrototypeExec(/(\d+):(\d+)\)/, errLine) || []; - if (line && col) { - const srcLine = StringPrototypeSplit(parentSource, '\n')[line - 1]; - const frame = `${parentPath}:${line}\n${srcLine}\n${ - StringPrototypeRepeat(' ', col - 1)}^\n`; - setArrowMessage(err, frame); - } - } - } - throw err; - } - } - module._compile(content, filename); -}; - - -// Native extension for .json -Module._extensions['.json'] = function(module, filename) { - const content = fs.readFileSync(filename, 'utf8'); - - if (policy?.manifest) { - const moduleURL = pathToFileURL(filename); - policy.manifest.assertIntegrity(moduleURL, content); - } - - try { - module.exports = JSONParse(stripBOM(content)); - } catch (err) { - err.message = filename + ': ' + err.message; - throw err; - } -}; - - -// Native extension for .node -Module._extensions['.node'] = function(module, filename) { - if (policy?.manifest) { - const content = fs.readFileSync(filename); - const moduleURL = pathToFileURL(filename); - policy.manifest.assertIntegrity(moduleURL, content); - } - // Be aware this doesn't use `content` - return process.dlopen(module, path.toNamespacedPath(filename)); -}; - function createRequireFromPath(filename) { // Allow a directory to be passed as the filename const trailingSlash = From 0c0a76dcad3c21002410339723f7ff59116c3016 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 13:46:34 -0400 Subject: [PATCH 6/8] sync --- dist-raw/node-internal-modules-cjs-loader.js | 144 ------------------- 1 file changed, 144 deletions(-) diff --git a/dist-raw/node-internal-modules-cjs-loader.js b/dist-raw/node-internal-modules-cjs-loader.js index 8f2719f90..8aa5202f6 100644 --- a/dist-raw/node-internal-modules-cjs-loader.js +++ b/dist-raw/node-internal-modules-cjs-loader.js @@ -860,147 +860,3 @@ function createEsmNotFoundErr(request, path) { // TODO(BridgeAR): Add the requireStack as well. return err; } - -// Resolved path to process.argv[1] will be lazily placed here -// (needed for setting breakpoint when called with --inspect-brk) -let resolvedArgv; -let hasPausedEntry = false; - -function wrapSafe(filename, content, cjsModuleInstance) { - if (patched) { - const wrapper = Module.wrap(content); - return vm.runInThisContext(wrapper, { - filename, - lineOffset: 0, - displayErrors: true, - importModuleDynamically: async (specifier) => { - const loader = asyncESM.esmLoader; - return loader.import(specifier, normalizeReferrerURL(filename)); - }, - }); - } - try { - return vm.compileFunction(content, [ - 'exports', - 'require', - 'module', - '__filename', - '__dirname', - ], { - filename, - importModuleDynamically(specifier) { - const loader = asyncESM.esmLoader; - return loader.import(specifier, normalizeReferrerURL(filename)); - }, - }); - } catch (err) { - if (process.mainModule === cjsModuleInstance) - enrichCJSError(err, content); - throw err; - } -} - -function createRequireFromPath(filename) { - // Allow a directory to be passed as the filename - const trailingSlash = - StringPrototypeEndsWith(filename, '/') || - (isWindows && StringPrototypeEndsWith(filename, '\\')); - - const proxyPath = trailingSlash ? - path.join(filename, 'noop.js') : - filename; - - const m = new Module(proxyPath); - m.filename = proxyPath; - - m.paths = Module._nodeModulePaths(m.path); - return makeRequireFunction(m, null); -} - -const createRequireError = 'must be a file URL object, file URL string, or ' + - 'absolute path string'; - -function createRequire(filename) { - let filepath; - - if (isURLInstance(filename) || - (typeof filename === 'string' && !path.isAbsolute(filename))) { - try { - filepath = fileURLToPath(filename); - } catch { - throw new ERR_INVALID_ARG_VALUE('filename', filename, - createRequireError); - } - } else if (typeof filename !== 'string') { - throw new ERR_INVALID_ARG_VALUE('filename', filename, createRequireError); - } else { - filepath = filename; - } - return createRequireFromPath(filepath); -} - -Module.createRequire = createRequire; - -Module._initPaths = function() { - const homeDir = isWindows ? process.env.USERPROFILE : safeGetenv('HOME'); - const nodePath = isWindows ? process.env.NODE_PATH : safeGetenv('NODE_PATH'); - - // process.execPath is $PREFIX/bin/node except on Windows where it is - // $PREFIX\node.exe where $PREFIX is the root of the Node.js installation. - const prefixDir = isWindows ? - path.resolve(process.execPath, '..') : - path.resolve(process.execPath, '..', '..'); - - const paths = [path.resolve(prefixDir, 'lib', 'node')]; - - if (homeDir) { - ArrayPrototypeUnshift(paths, path.resolve(homeDir, '.node_libraries')); - ArrayPrototypeUnshift(paths, path.resolve(homeDir, '.node_modules')); - } - - if (nodePath) { - ArrayPrototypeUnshiftApply(paths, ArrayPrototypeFilter( - StringPrototypeSplit(nodePath, path.delimiter), - Boolean - )); - } - - modulePaths = paths; - - // Clone as a shallow copy, for introspection. - Module.globalPaths = ArrayPrototypeSlice(modulePaths); -}; - -Module._preloadModules = function(requests) { - if (!ArrayIsArray(requests)) - return; - - isPreloading = true; - - // Preloaded modules have a dummy parent module which is deemed to exist - // in the current working directory. This seeds the search path for - // preloaded modules. - const parent = new Module('internal/preload', null); - try { - parent.paths = Module._nodeModulePaths(process.cwd()); - } catch (e) { - if (e.code !== 'ENOENT') { - isPreloading = false; - throw e; - } - } - for (let n = 0; n < requests.length; n++) - parent.require(requests[n]); - isPreloading = false; -}; - -Module.syncBuiltinESMExports = function syncBuiltinESMExports() { - for (const mod of NativeModule.map.values()) { - if (mod.canBeRequiredByUsers) { - mod.syncExports(); - } - } -}; - -// Backwards compatibility -Module.Module = Module; From 8caff79448ab2768e1af373b8505bdfd9cc075d1 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 13:58:22 -0400 Subject: [PATCH 7/8] sync --- raw/download-and-compare.sh | 12 + ...e-internal-modules-esm-resolve-v15.3.0.js} | 3 +- ...8799930794045795e8abac874730f9eba7e2300.js | 253 ++++++++++++++++++ 3 files changed, 267 insertions(+), 1 deletion(-) rename raw/{node-esm-resolve-implementation-v15.3.0.js => node-internal-modules-esm-resolve-v15.3.0.js} (99%) create mode 100644 raw/node-internal-repl-await-88799930794045795e8abac874730f9eba7e2300.js diff --git a/raw/download-and-compare.sh b/raw/download-and-compare.sh index db98d901b..cf22b33c9 100755 --- a/raw/download-and-compare.sh +++ b/raw/download-and-compare.sh @@ -34,3 +34,15 @@ path=lib/internal/repl/await local=node-internal-repl-await download compare + +version=88799930794045795e8abac874730f9eba7e2300 +path=lib/internal/repl/await +local=node-internal-repl-await +download +compare + +version=v15.3.0 +path=lib/internal/modules/esm/resolve +local=node-internal-modules-esm-resolve +download +compare diff --git a/raw/node-esm-resolve-implementation-v15.3.0.js b/raw/node-internal-modules-esm-resolve-v15.3.0.js similarity index 99% rename from raw/node-esm-resolve-implementation-v15.3.0.js rename to raw/node-internal-modules-esm-resolve-v15.3.0.js index 782b892a6..19a7e2161 100644 --- a/raw/node-esm-resolve-implementation-v15.3.0.js +++ b/raw/node-internal-modules-esm-resolve-v15.3.0.js @@ -1,3 +1,5 @@ +// Copied from https://github.com/nodejs/node/blob/v15.3.0/lib/internal/modules/esm/resolve.js + 'use strict'; const { @@ -897,4 +899,3 @@ module.exports = { packageExportsResolve, packageImportsResolve }; - diff --git a/raw/node-internal-repl-await-88799930794045795e8abac874730f9eba7e2300.js b/raw/node-internal-repl-await-88799930794045795e8abac874730f9eba7e2300.js new file mode 100644 index 000000000..0f95d97e4 --- /dev/null +++ b/raw/node-internal-repl-await-88799930794045795e8abac874730f9eba7e2300.js @@ -0,0 +1,253 @@ +// Copied from https://github.com/nodejs/node/blob/88799930794045795e8abac874730f9eba7e2300/lib/internal/repl/await.js + +'use strict'; + +const { + ArrayFrom, + ArrayPrototypeForEach, + ArrayPrototypeIncludes, + ArrayPrototypeJoin, + ArrayPrototypePop, + ArrayPrototypePush, + FunctionPrototype, + ObjectKeys, + RegExpPrototypeSymbolReplace, + StringPrototypeEndsWith, + StringPrototypeIncludes, + StringPrototypeIndexOf, + StringPrototypeRepeat, + StringPrototypeSplit, + StringPrototypeStartsWith, + SyntaxError, +} = primordials; + +const parser = require('internal/deps/acorn/acorn/dist/acorn').Parser; +const walk = require('internal/deps/acorn/acorn-walk/dist/walk'); +const { Recoverable } = require('internal/repl'); + +function isTopLevelDeclaration(state) { + return state.ancestors[state.ancestors.length - 2] === state.body; +} + +const noop = FunctionPrototype; +const visitorsWithoutAncestors = { + ClassDeclaration(node, state, c) { + if (isTopLevelDeclaration(state)) { + state.prepend(node, `${node.id.name}=`); + ArrayPrototypePush( + state.hoistedDeclarationStatements, + `let ${node.id.name}; ` + ); + } + + walk.base.ClassDeclaration(node, state, c); + }, + ForOfStatement(node, state, c) { + if (node.await === true) { + state.containsAwait = true; + } + walk.base.ForOfStatement(node, state, c); + }, + FunctionDeclaration(node, state, c) { + state.prepend(node, `${node.id.name}=`); + ArrayPrototypePush( + state.hoistedDeclarationStatements, + `var ${node.id.name}; ` + ); + }, + FunctionExpression: noop, + ArrowFunctionExpression: noop, + MethodDefinition: noop, + AwaitExpression(node, state, c) { + state.containsAwait = true; + walk.base.AwaitExpression(node, state, c); + }, + ReturnStatement(node, state, c) { + state.containsReturn = true; + walk.base.ReturnStatement(node, state, c); + }, + VariableDeclaration(node, state, c) { + const variableKind = node.kind; + const isIterableForDeclaration = ArrayPrototypeIncludes( + ['ForOfStatement', 'ForInStatement'], + state.ancestors[state.ancestors.length - 2].type + ); + + if (variableKind === 'var' || isTopLevelDeclaration(state)) { + state.replace( + node.start, + node.start + variableKind.length + (isIterableForDeclaration ? 1 : 0), + variableKind === 'var' && isIterableForDeclaration ? + '' : + 'void' + (node.declarations.length === 1 ? '' : ' (') + ); + + if (!isIterableForDeclaration) { + ArrayPrototypeForEach(node.declarations, (decl) => { + state.prepend(decl, '('); + state.append(decl, decl.init ? ')' : '=undefined)'); + }); + + if (node.declarations.length !== 1) { + state.append(node.declarations[node.declarations.length - 1], ')'); + } + } + + const variableIdentifiersToHoist = [ + ['var', []], + ['let', []], + ]; + function registerVariableDeclarationIdentifiers(node) { + switch (node.type) { + case 'Identifier': + ArrayPrototypePush( + variableIdentifiersToHoist[variableKind === 'var' ? 0 : 1][1], + node.name + ); + break; + case 'ObjectPattern': + ArrayPrototypeForEach(node.properties, (property) => { + registerVariableDeclarationIdentifiers(property.value); + }); + break; + case 'ArrayPattern': + ArrayPrototypeForEach(node.elements, (element) => { + registerVariableDeclarationIdentifiers(element); + }); + break; + } + } + + ArrayPrototypeForEach(node.declarations, (decl) => { + registerVariableDeclarationIdentifiers(decl.id); + }); + + ArrayPrototypeForEach( + variableIdentifiersToHoist, + ({ 0: kind, 1: identifiers }) => { + if (identifiers.length > 0) { + ArrayPrototypePush( + state.hoistedDeclarationStatements, + `${kind} ${ArrayPrototypeJoin(identifiers, ', ')}; ` + ); + } + } + ); + } + + walk.base.VariableDeclaration(node, state, c); + } +}; + +const visitors = {}; +for (const nodeType of ObjectKeys(walk.base)) { + const callback = visitorsWithoutAncestors[nodeType] || walk.base[nodeType]; + visitors[nodeType] = (node, state, c) => { + const isNew = node !== state.ancestors[state.ancestors.length - 1]; + if (isNew) { + ArrayPrototypePush(state.ancestors, node); + } + callback(node, state, c); + if (isNew) { + ArrayPrototypePop(state.ancestors); + } + }; +} + +function processTopLevelAwait(src) { + const wrapPrefix = '(async () => { '; + const wrapped = `${wrapPrefix}${src} })()`; + const wrappedArray = ArrayFrom(wrapped); + let root; + try { + root = parser.parse(wrapped, { ecmaVersion: 'latest' }); + } catch (e) { + if (StringPrototypeStartsWith(e.message, 'Unterminated ')) + throw new Recoverable(e); + // If the parse error is before the first "await", then use the execution + // error. Otherwise we must emit this parse error, making it look like a + // proper syntax error. + const awaitPos = StringPrototypeIndexOf(src, 'await'); + const errPos = e.pos - wrapPrefix.length; + if (awaitPos > errPos) + return null; + // Convert keyword parse errors on await into their original errors when + // possible. + if (errPos === awaitPos + 6 && + StringPrototypeIncludes(e.message, 'Expecting Unicode escape sequence')) + return null; + if (errPos === awaitPos + 7 && + StringPrototypeIncludes(e.message, 'Unexpected token')) + return null; + const line = e.loc.line; + const column = line === 1 ? e.loc.column - wrapPrefix.length : e.loc.column; + let message = '\n' + StringPrototypeSplit(src, '\n')[line - 1] + '\n' + + StringPrototypeRepeat(' ', column) + + '^\n\n' + RegExpPrototypeSymbolReplace(/ \([^)]+\)/, e.message, ''); + // V8 unexpected token errors include the token string. + if (StringPrototypeEndsWith(message, 'Unexpected token')) + message += " '" + + // Wrapper end may cause acorn to report error position after the source + (src[e.pos - wrapPrefix.length] ?? src[src.length - 1]) + + "'"; + // eslint-disable-next-line no-restricted-syntax + throw new SyntaxError(message); + } + const body = root.body[0].expression.callee.body; + const state = { + body, + ancestors: [], + hoistedDeclarationStatements: [], + replace(from, to, str) { + for (let i = from; i < to; i++) { + wrappedArray[i] = ''; + } + if (from === to) str += wrappedArray[from]; + wrappedArray[from] = str; + }, + prepend(node, str) { + wrappedArray[node.start] = str + wrappedArray[node.start]; + }, + append(node, str) { + wrappedArray[node.end - 1] += str; + }, + containsAwait: false, + containsReturn: false + }; + + walk.recursive(body, state, visitors); + + // Do not transform if + // 1. False alarm: there isn't actually an await expression. + // 2. There is a top-level return, which is not allowed. + if (!state.containsAwait || state.containsReturn) { + return null; + } + + const last = body.body[body.body.length - 1]; + if (last.type === 'ExpressionStatement') { + // For an expression statement of the form + // ( expr ) ; + // ^^^^^^^^^^ // last + // ^^^^ // last.expression + // + // We do not want the left parenthesis before the `return` keyword; + // therefore we prepend the `return (` to `last`. + // + // On the other hand, we do not want the right parenthesis after the + // semicolon. Since there can only be more right parentheses between + // last.expression.end and the semicolon, appending one more to + // last.expression should be fine. + state.prepend(last, 'return ('); + state.append(last.expression, ')'); + } + + return ( + ArrayPrototypeJoin(state.hoistedDeclarationStatements, '') + + ArrayPrototypeJoin(wrappedArray, '') + ); +} + +module.exports = { + processTopLevelAwait +}; From baf9e871ea7f973f73d2208f8776a7fbe51dab62 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 14:08:36 -0400 Subject: [PATCH 8/8] sync --- dist-raw/README.md | 13 +- raw/README.md | 1 + ...nal-modules-cjs-loader-v17.0.1-stripped.js | 1197 +++++++++++++++++ 3 files changed, 1210 insertions(+), 1 deletion(-) create mode 100644 raw/README.md create mode 100644 raw/node-internal-modules-cjs-loader-v17.0.1-stripped.js diff --git a/dist-raw/README.md b/dist-raw/README.md index 8a598a35a..9eeaed31d 100644 --- a/dist-raw/README.md +++ b/dist-raw/README.md @@ -12,7 +12,11 @@ One obvious problem with this approach: the code has been pulled from one versio run multiple versions of node. Users running node 12 may see that ts-node behaves like node 14, for example. ---- +## `raw` directory + +Within the `raw` directory, we keep unmodified copies of the node source files. This allows us to use diffing tools to +compare files in `raw` to those in `dist-raw`, which will highlight all of the changes we have made. Hopefully, these +changes are as minimal as possible. ## Naming convention @@ -23,3 +27,10 @@ Not used consistently, but the idea is: `node-internal-errors.js` -> `github.com/nodejs/node/blob/TAG/lib/internal/errors.js` So, take the path within node's `lib/` directory, and replace slashes with hyphens. + +In the `raw` directory, files are suffixed with the version number or revision from which +they were downloaded. + +If they have a `stripped` suffix, this means they have large chunks of code deleted, but no other modifications. +This is useful when diffing. Sometimes our `dist-raw` files only have a small part of a much larger node source file. +It is easier to diff `raw/*-stripped.js` against `dist-raw/*.js`. diff --git a/raw/README.md b/raw/README.md new file mode 100644 index 000000000..154e9bf58 --- /dev/null +++ b/raw/README.md @@ -0,0 +1 @@ +See the README.md in `dist-raw` diff --git a/raw/node-internal-modules-cjs-loader-v17.0.1-stripped.js b/raw/node-internal-modules-cjs-loader-v17.0.1-stripped.js new file mode 100644 index 000000000..0ea7182d2 --- /dev/null +++ b/raw/node-internal-modules-cjs-loader-v17.0.1-stripped.js @@ -0,0 +1,1197 @@ +// Copied from https://github.com/nodejs/node/blob/v17.0.1/lib/internal/modules/cjs/loader.js + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +'use strict'; + +const { + ArrayIsArray, + ArrayPrototypeConcat, + ArrayPrototypeFilter, + ArrayPrototypeIncludes, + ArrayPrototypeIndexOf, + ArrayPrototypeJoin, + ArrayPrototypePush, + ArrayPrototypeSlice, + ArrayPrototypeSplice, + ArrayPrototypeUnshift, + ArrayPrototypeUnshiftApply, + Boolean, + Error, + JSONParse, + ObjectCreate, + ObjectDefineProperty, + ObjectFreeze, + ObjectGetOwnPropertyDescriptor, + ObjectGetPrototypeOf, + ObjectKeys, + ObjectPrototype, + ObjectPrototypeHasOwnProperty, + ObjectSetPrototypeOf, + Proxy, + ReflectApply, + ReflectSet, + RegExpPrototypeExec, + RegExpPrototypeTest, + SafeMap, + SafeWeakMap, + String, + StringPrototypeCharAt, + StringPrototypeCharCodeAt, + StringPrototypeEndsWith, + StringPrototypeLastIndexOf, + StringPrototypeIndexOf, + StringPrototypeMatch, + StringPrototypeRepeat, + StringPrototypeSlice, + StringPrototypeSplit, + StringPrototypeStartsWith, +} = primordials; + +// Map used to store CJS parsing data. +const cjsParseCache = new SafeWeakMap(); + +// Set first due to cycle with ESM loader functions. +module.exports = { + wrapSafe, Module, toRealPath, readPackageScope, cjsParseCache, + get hasLoadedAnyUserCJSModule() { return hasLoadedAnyUserCJSModule; } +}; + +const { NativeModule } = require('internal/bootstrap/loaders'); +const { + maybeCacheSourceMap, +} = require('internal/source_map/source_map_cache'); +const { pathToFileURL, fileURLToPath, isURLInstance } = require('internal/url'); +const { deprecate } = require('internal/util'); +const vm = require('vm'); +const assert = require('internal/assert'); +const fs = require('fs'); +const internalFS = require('internal/fs/utils'); +const path = require('path'); +const { sep } = path; +const { internalModuleStat } = internalBinding('fs'); +const packageJsonReader = require('internal/modules/package_json_reader'); +const { safeGetenv } = internalBinding('credentials'); +const { + cjsConditions, + hasEsmSyntax, + loadNativeModule, + makeRequireFunction, + normalizeReferrerURL, + stripBOM, +} = require('internal/modules/cjs/helpers'); +const { getOptionValue } = require('internal/options'); +const preserveSymlinks = getOptionValue('--preserve-symlinks'); +const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main'); +// Do not eagerly grab .manifest, it may be in TDZ +const policy = getOptionValue('--experimental-policy') ? + require('internal/process/policy') : + null; + +// Whether any user-provided CJS modules had been loaded (executed). +// Used for internal assertions. +let hasLoadedAnyUserCJSModule = false; + +const { + codes: { + ERR_INVALID_ARG_VALUE, + ERR_INVALID_MODULE_SPECIFIER, + ERR_REQUIRE_ESM, + ERR_UNKNOWN_BUILTIN_MODULE, + }, + setArrowMessage, +} = require('internal/errors'); +const { validateString } = require('internal/validators'); +const pendingDeprecation = getOptionValue('--pending-deprecation'); + +const { + CHAR_FORWARD_SLASH, + CHAR_BACKWARD_SLASH, + CHAR_COLON +} = require('internal/constants'); + +const { + isProxy +} = require('internal/util/types'); + +const asyncESM = require('internal/process/esm_loader'); +const { enrichCJSError } = require('internal/modules/esm/translators'); +const { kEvaluated } = internalBinding('module_wrap'); +const { + encodedSepRegEx, + packageExportsResolve, + packageImportsResolve +} = require('internal/modules/esm/resolve'); + +const isWindows = process.platform === 'win32'; + +const relativeResolveCache = ObjectCreate(null); + +let requireDepth = 0; +let statCache = null; +let isPreloading = false; + +function stat(filename) { + filename = path.toNamespacedPath(filename); + if (statCache !== null) { + const result = statCache.get(filename); + if (result !== undefined) return result; + } + const result = internalModuleStat(filename); + if (statCache !== null && result >= 0) { + // Only set cache when `internalModuleStat(filename)` succeeds. + statCache.set(filename, result); + } + return result; +} + +function updateChildren(parent, child, scan) { + const children = parent?.children; + if (children && !(scan && ArrayPrototypeIncludes(children, child))) + ArrayPrototypePush(children, child); +} + +const moduleParentCache = new SafeWeakMap(); + +let modulePaths = []; + +let patched = false; + +function getModuleParent() { + return moduleParentCache.get(this); +} + +function setModuleParent(value) { + moduleParentCache.set(this, value); +} + +let debug = require('internal/util/debuglog').debuglog('module', (fn) => { + debug = fn; +}); + +// Given a module name, and a list of paths to test, returns the first +// matching file in the following precedence. +// +// require("a.") +// -> a. +// +// require("a") +// -> a +// -> a. +// -> a/index. + +const packageJsonCache = new SafeMap(); + +function readPackage(requestPath) { + const jsonPath = path.resolve(requestPath, 'package.json'); + + const existing = packageJsonCache.get(jsonPath); + if (existing !== undefined) return existing; + + const result = packageJsonReader.read(jsonPath); + const json = result.containsKeys === false ? '{}' : result.string; + if (json === undefined) { + packageJsonCache.set(jsonPath, false); + return false; + } + + try { + const parsed = JSONParse(json); + const filtered = { + name: parsed.name, + main: parsed.main, + exports: parsed.exports, + imports: parsed.imports, + type: parsed.type + }; + packageJsonCache.set(jsonPath, filtered); + return filtered; + } catch (e) { + e.path = jsonPath; + e.message = 'Error parsing ' + jsonPath + ': ' + e.message; + throw e; + } +} + +function readPackageScope(checkPath) { + const rootSeparatorIndex = StringPrototypeIndexOf(checkPath, sep); + let separatorIndex; + do { + separatorIndex = StringPrototypeLastIndexOf(checkPath, sep); + checkPath = StringPrototypeSlice(checkPath, 0, separatorIndex); + if (StringPrototypeEndsWith(checkPath, sep + 'node_modules')) + return false; + const pjson = readPackage(checkPath + sep); + if (pjson) return { + data: pjson, + path: checkPath, + }; + } while (separatorIndex > rootSeparatorIndex); + return false; +} + +function tryPackage(requestPath, exts, isMain, originalPath) { + const pkg = readPackage(requestPath)?.main; + + if (!pkg) { + return tryExtensions(path.resolve(requestPath, 'index'), exts, isMain); + } + + const filename = path.resolve(requestPath, pkg); + let actual = tryFile(filename, isMain) || + tryExtensions(filename, exts, isMain) || + tryExtensions(path.resolve(filename, 'index'), exts, isMain); + if (actual === false) { + actual = tryExtensions(path.resolve(requestPath, 'index'), exts, isMain); + if (!actual) { + // eslint-disable-next-line no-restricted-syntax + const err = new Error( + `Cannot find module '${filename}'. ` + + 'Please verify that the package.json has a valid "main" entry' + ); + err.code = 'MODULE_NOT_FOUND'; + err.path = path.resolve(requestPath, 'package.json'); + err.requestPath = originalPath; + // TODO(BridgeAR): Add the requireStack as well. + throw err; + } else { + const jsonPath = path.resolve(requestPath, 'package.json'); + process.emitWarning( + `Invalid 'main' field in '${jsonPath}' of '${pkg}'. ` + + 'Please either fix that or report it to the module author', + 'DeprecationWarning', + 'DEP0128' + ); + } + } + return actual; +} + +// In order to minimize unnecessary lstat() calls, +// this cache is a list of known-real paths. +// Set to an empty Map to reset. +const realpathCache = new SafeMap(); + +// Check if the file exists and is not a directory +// if using --preserve-symlinks and isMain is false, +// keep symlinks intact, otherwise resolve to the +// absolute realpath. +function tryFile(requestPath, isMain) { + const rc = stat(requestPath); + if (rc !== 0) return; + if (preserveSymlinks && !isMain) { + return path.resolve(requestPath); + } + return toRealPath(requestPath); +} + +function toRealPath(requestPath) { + return fs.realpathSync(requestPath, { + [internalFS.realpathCacheKey]: realpathCache + }); +} + +// Given a path, check if the file exists with any of the set extensions +function tryExtensions(p, exts, isMain) { + for (let i = 0; i < exts.length; i++) { + const filename = tryFile(p + exts[i], isMain); + + if (filename) { + return filename; + } + } + return false; +} + +// Find the longest (possibly multi-dot) extension registered in +// Module._extensions +function findLongestRegisteredExtension(filename) { + const name = path.basename(filename); + let currentExtension; + let index; + let startIndex = 0; + while ((index = StringPrototypeIndexOf(name, '.', startIndex)) !== -1) { + startIndex = index + 1; + if (index === 0) continue; // Skip dotfiles like .gitignore + currentExtension = StringPrototypeSlice(name, index); + if (Module._extensions[currentExtension]) return currentExtension; + } + return '.js'; +} + +function trySelfParentPath(parent) { + if (!parent) return false; + + if (parent.filename) { + return parent.filename; + } else if (parent.id === '' || parent.id === 'internal/preload') { + try { + return process.cwd() + path.sep; + } catch { + return false; + } + } +} + +function trySelf(parentPath, request) { + if (!parentPath) return false; + + const { data: pkg, path: pkgPath } = readPackageScope(parentPath) || {}; + if (!pkg || pkg.exports === undefined) return false; + if (typeof pkg.name !== 'string') return false; + + let expansion; + if (request === pkg.name) { + expansion = '.'; + } else if (StringPrototypeStartsWith(request, `${pkg.name}/`)) { + expansion = '.' + StringPrototypeSlice(request, pkg.name.length); + } else { + return false; + } + + try { + return finalizeEsmResolution(packageExportsResolve( + pathToFileURL(pkgPath + '/package.json'), expansion, pkg, + pathToFileURL(parentPath), cjsConditions), parentPath, pkgPath); + } catch (e) { + if (e.code === 'ERR_MODULE_NOT_FOUND') + throw createEsmNotFoundErr(request, pkgPath + '/package.json'); + throw e; + } +} + +// This only applies to requests of a specific form: +// 1. name/.* +// 2. @scope/name/.* +const EXPORTS_PATTERN = /^((?:@[^/\\%]+\/)?[^./\\%][^/\\%]*)(\/.*)?$/; +function resolveExports(nmPath, request) { + // The implementation's behavior is meant to mirror resolution in ESM. + const { 1: name, 2: expansion = '' } = + StringPrototypeMatch(request, EXPORTS_PATTERN) || []; + if (!name) + return; + const pkgPath = path.resolve(nmPath, name); + const pkg = readPackage(pkgPath); + if (pkg?.exports != null) { + try { + return finalizeEsmResolution(packageExportsResolve( + pathToFileURL(pkgPath + '/package.json'), '.' + expansion, pkg, null, + cjsConditions), null, pkgPath); + } catch (e) { + if (e.code === 'ERR_MODULE_NOT_FOUND') + throw createEsmNotFoundErr(request, pkgPath + '/package.json'); + throw e; + } + } +} + +const trailingSlashRegex = /(?:^|\/)\.?\.$/; +Module._findPath = function(request, paths, isMain) { + const absoluteRequest = path.isAbsolute(request); + if (absoluteRequest) { + paths = ['']; + } else if (!paths || paths.length === 0) { + return false; + } + + const cacheKey = request + '\x00' + ArrayPrototypeJoin(paths, '\x00'); + const entry = Module._pathCache[cacheKey]; + if (entry) + return entry; + + let exts; + let trailingSlash = request.length > 0 && + StringPrototypeCharCodeAt(request, request.length - 1) === + CHAR_FORWARD_SLASH; + if (!trailingSlash) { + trailingSlash = RegExpPrototypeTest(trailingSlashRegex, request); + } + + // For each path + for (let i = 0; i < paths.length; i++) { + // Don't search further if path doesn't exist + const curPath = paths[i]; + if (curPath && stat(curPath) < 1) continue; + + if (!absoluteRequest) { + const exportsResolved = resolveExports(curPath, request); + if (exportsResolved) + return exportsResolved; + } + + const basePath = path.resolve(curPath, request); + let filename; + + const rc = stat(basePath); + if (!trailingSlash) { + if (rc === 0) { // File. + if (!isMain) { + if (preserveSymlinks) { + filename = path.resolve(basePath); + } else { + filename = toRealPath(basePath); + } + } else if (preserveSymlinksMain) { + // For the main module, we use the preserveSymlinksMain flag instead + // mainly for backward compatibility, as the preserveSymlinks flag + // historically has not applied to the main module. Most likely this + // was intended to keep .bin/ binaries working, as following those + // symlinks is usually required for the imports in the corresponding + // files to resolve; that said, in some use cases following symlinks + // causes bigger problems which is why the preserveSymlinksMain option + // is needed. + filename = path.resolve(basePath); + } else { + filename = toRealPath(basePath); + } + } + + if (!filename) { + // Try it with each of the extensions + if (exts === undefined) + exts = ObjectKeys(Module._extensions); + filename = tryExtensions(basePath, exts, isMain); + } + } + + if (!filename && rc === 1) { // Directory. + // try it with each of the extensions at "index" + if (exts === undefined) + exts = ObjectKeys(Module._extensions); + filename = tryPackage(basePath, exts, isMain, request); + } + + if (filename) { + Module._pathCache[cacheKey] = filename; + return filename; + } + } + + return false; +}; + +// 'node_modules' character codes reversed +const nmChars = [ 115, 101, 108, 117, 100, 111, 109, 95, 101, 100, 111, 110 ]; +const nmLen = nmChars.length; +if (isWindows) { + // 'from' is the __dirname of the module. + Module._nodeModulePaths = function(from) { + // Guarantee that 'from' is absolute. + from = path.resolve(from); + + // note: this approach *only* works when the path is guaranteed + // to be absolute. Doing a fully-edge-case-correct path.split + // that works on both Windows and Posix is non-trivial. + + // return root node_modules when path is 'D:\\'. + // path.resolve will make sure from.length >=3 in Windows. + if (StringPrototypeCharCodeAt(from, from.length - 1) === + CHAR_BACKWARD_SLASH && + StringPrototypeCharCodeAt(from, from.length - 2) === CHAR_COLON) + return [from + 'node_modules']; + + const paths = []; + for (let i = from.length - 1, p = 0, last = from.length; i >= 0; --i) { + const code = StringPrototypeCharCodeAt(from, i); + // The path segment separator check ('\' and '/') was used to get + // node_modules path for every path segment. + // Use colon as an extra condition since we can get node_modules + // path for drive root like 'C:\node_modules' and don't need to + // parse drive name. + if (code === CHAR_BACKWARD_SLASH || + code === CHAR_FORWARD_SLASH || + code === CHAR_COLON) { + if (p !== nmLen) + ArrayPrototypePush( + paths, + StringPrototypeSlice(from, 0, last) + '\\node_modules' + ); + last = i; + p = 0; + } else if (p !== -1) { + if (nmChars[p] === code) { + ++p; + } else { + p = -1; + } + } + } + + return paths; + }; +} else { // posix + // 'from' is the __dirname of the module. + Module._nodeModulePaths = function(from) { + // Guarantee that 'from' is absolute. + from = path.resolve(from); + // Return early not only to avoid unnecessary work, but to *avoid* returning + // an array of two items for a root: [ '//node_modules', '/node_modules' ] + if (from === '/') + return ['/node_modules']; + + // note: this approach *only* works when the path is guaranteed + // to be absolute. Doing a fully-edge-case-correct path.split + // that works on both Windows and Posix is non-trivial. + const paths = []; + for (let i = from.length - 1, p = 0, last = from.length; i >= 0; --i) { + const code = StringPrototypeCharCodeAt(from, i); + if (code === CHAR_FORWARD_SLASH) { + if (p !== nmLen) + ArrayPrototypePush( + paths, + StringPrototypeSlice(from, 0, last) + '/node_modules' + ); + last = i; + p = 0; + } else if (p !== -1) { + if (nmChars[p] === code) { + ++p; + } else { + p = -1; + } + } + } + + // Append /node_modules to handle root paths. + ArrayPrototypePush(paths, '/node_modules'); + + return paths; + }; +} + +Module._resolveLookupPaths = function(request, parent) { + if (NativeModule.canBeRequiredByUsers(request)) { + debug('looking for %j in []', request); + return null; + } + + // Check for node modules paths. + if (StringPrototypeCharAt(request, 0) !== '.' || + (request.length > 1 && + StringPrototypeCharAt(request, 1) !== '.' && + StringPrototypeCharAt(request, 1) !== '/' && + (!isWindows || StringPrototypeCharAt(request, 1) !== '\\'))) { + + let paths = modulePaths; + if (parent?.paths?.length) { + paths = ArrayPrototypeConcat(parent.paths, paths); + } + + debug('looking for %j in %j', request, paths); + return paths.length > 0 ? paths : null; + } + + // In REPL, parent.filename is null. + if (!parent || !parent.id || !parent.filename) { + // Make require('./path/to/foo') work - normally the path is taken + // from realpath(__filename) but in REPL there is no filename + const mainPaths = ['.']; + + debug('looking for %j in %j', request, mainPaths); + return mainPaths; + } + + debug('RELATIVE: requested: %s from parent.id %s', request, parent.id); + + const parentDir = [path.dirname(parent.filename)]; + debug('looking for %j', parentDir); + return parentDir; +}; + +function emitCircularRequireWarning(prop) { + process.emitWarning( + `Accessing non-existent property '${String(prop)}' of module exports ` + + 'inside circular dependency' + ); +} + +// A Proxy that can be used as the prototype of a module.exports object and +// warns when non-existent properties are accessed. +const CircularRequirePrototypeWarningProxy = new Proxy({}, { + get(target, prop) { + // Allow __esModule access in any case because it is used in the output + // of transpiled code to determine whether something comes from an + // ES module, and is not used as a regular key of `module.exports`. + if (prop in target || prop === '__esModule') return target[prop]; + emitCircularRequireWarning(prop); + return undefined; + }, + + getOwnPropertyDescriptor(target, prop) { + if (ObjectPrototypeHasOwnProperty(target, prop) || prop === '__esModule') + return ObjectGetOwnPropertyDescriptor(target, prop); + emitCircularRequireWarning(prop); + return undefined; + } +}); + +function getExportsForCircularRequire(module) { + if (module.exports && + !isProxy(module.exports) && + ObjectGetPrototypeOf(module.exports) === ObjectPrototype && + // Exclude transpiled ES6 modules / TypeScript code because those may + // employ unusual patterns for accessing 'module.exports'. That should + // be okay because ES6 modules have a different approach to circular + // dependencies anyway. + !module.exports.__esModule) { + // This is later unset once the module is done loading. + ObjectSetPrototypeOf( + module.exports, CircularRequirePrototypeWarningProxy); + } + + return module.exports; +} + +// Check the cache for the requested file. +// 1. If a module already exists in the cache: return its exports object. +// 2. If the module is native: call +// `NativeModule.prototype.compileForPublicLoader()` and return the exports. +// 3. Otherwise, create a new module for the file and save it to the cache. +// Then have it load the file contents before returning its exports +// object. +Module._load = function(request, parent, isMain) { + let relResolveCacheIdentifier; + if (parent) { + debug('Module._load REQUEST %s parent: %s', request, parent.id); + // Fast path for (lazy loaded) modules in the same directory. The indirect + // caching is required to allow cache invalidation without changing the old + // cache key names. + relResolveCacheIdentifier = `${parent.path}\x00${request}`; + const filename = relativeResolveCache[relResolveCacheIdentifier]; + if (filename !== undefined) { + const cachedModule = Module._cache[filename]; + if (cachedModule !== undefined) { + updateChildren(parent, cachedModule, true); + if (!cachedModule.loaded) + return getExportsForCircularRequire(cachedModule); + return cachedModule.exports; + } + delete relativeResolveCache[relResolveCacheIdentifier]; + } + } + + const filename = Module._resolveFilename(request, parent, isMain); + if (StringPrototypeStartsWith(filename, 'node:')) { + // Slice 'node:' prefix + const id = StringPrototypeSlice(filename, 5); + + const module = loadNativeModule(id, request); + if (!module?.canBeRequiredByUsers) { + throw new ERR_UNKNOWN_BUILTIN_MODULE(filename); + } + + return module.exports; + } + + const cachedModule = Module._cache[filename]; + if (cachedModule !== undefined) { + updateChildren(parent, cachedModule, true); + if (!cachedModule.loaded) { + const parseCachedModule = cjsParseCache.get(cachedModule); + if (!parseCachedModule || parseCachedModule.loaded) + return getExportsForCircularRequire(cachedModule); + parseCachedModule.loaded = true; + } else { + return cachedModule.exports; + } + } + + const mod = loadNativeModule(filename, request); + if (mod?.canBeRequiredByUsers) return mod.exports; + + // Don't call updateChildren(), Module constructor already does. + const module = cachedModule || new Module(filename, parent); + + if (isMain) { + process.mainModule = module; + module.id = '.'; + } + + Module._cache[filename] = module; + if (parent !== undefined) { + relativeResolveCache[relResolveCacheIdentifier] = filename; + } + + let threw = true; + try { + module.load(filename); + threw = false; + } finally { + if (threw) { + delete Module._cache[filename]; + if (parent !== undefined) { + delete relativeResolveCache[relResolveCacheIdentifier]; + const children = parent?.children; + if (ArrayIsArray(children)) { + const index = ArrayPrototypeIndexOf(children, module); + if (index !== -1) { + ArrayPrototypeSplice(children, index, 1); + } + } + } + } else if (module.exports && + !isProxy(module.exports) && + ObjectGetPrototypeOf(module.exports) === + CircularRequirePrototypeWarningProxy) { + ObjectSetPrototypeOf(module.exports, ObjectPrototype); + } + } + + return module.exports; +}; + +Module._resolveFilename = function(request, parent, isMain, options) { + if (StringPrototypeStartsWith(request, 'node:') || + NativeModule.canBeRequiredByUsers(request)) { + return request; + } + + let paths; + + if (typeof options === 'object' && options !== null) { + if (ArrayIsArray(options.paths)) { + const isRelative = StringPrototypeStartsWith(request, './') || + StringPrototypeStartsWith(request, '../') || + ((isWindows && StringPrototypeStartsWith(request, '.\\')) || + StringPrototypeStartsWith(request, '..\\')); + + if (isRelative) { + paths = options.paths; + } else { + const fakeParent = new Module('', null); + + paths = []; + + for (let i = 0; i < options.paths.length; i++) { + const path = options.paths[i]; + fakeParent.paths = Module._nodeModulePaths(path); + const lookupPaths = Module._resolveLookupPaths(request, fakeParent); + + for (let j = 0; j < lookupPaths.length; j++) { + if (!ArrayPrototypeIncludes(paths, lookupPaths[j])) + ArrayPrototypePush(paths, lookupPaths[j]); + } + } + } + } else if (options.paths === undefined) { + paths = Module._resolveLookupPaths(request, parent); + } else { + throw new ERR_INVALID_ARG_VALUE('options.paths', options.paths); + } + } else { + paths = Module._resolveLookupPaths(request, parent); + } + + if (parent?.filename) { + if (request[0] === '#') { + const pkg = readPackageScope(parent.filename) || {}; + if (pkg.data?.imports != null) { + try { + return finalizeEsmResolution( + packageImportsResolve(request, pathToFileURL(parent.filename), + cjsConditions), parent.filename, + pkg.path); + } catch (e) { + if (e.code === 'ERR_MODULE_NOT_FOUND') + throw createEsmNotFoundErr(request); + throw e; + } + } + } + } + + // Try module self resolution first + const parentPath = trySelfParentPath(parent); + const selfResolved = trySelf(parentPath, request); + if (selfResolved) { + const cacheKey = request + '\x00' + + (paths.length === 1 ? paths[0] : ArrayPrototypeJoin(paths, '\x00')); + Module._pathCache[cacheKey] = selfResolved; + return selfResolved; + } + + // Look up the filename first, since that's the cache key. + const filename = Module._findPath(request, paths, isMain, false); + if (filename) return filename; + const requireStack = []; + for (let cursor = parent; + cursor; + cursor = moduleParentCache.get(cursor)) { + ArrayPrototypePush(requireStack, cursor.filename || cursor.id); + } + let message = `Cannot find module '${request}'`; + if (requireStack.length > 0) { + message = message + '\nRequire stack:\n- ' + + ArrayPrototypeJoin(requireStack, '\n- '); + } + // eslint-disable-next-line no-restricted-syntax + const err = new Error(message); + err.code = 'MODULE_NOT_FOUND'; + err.requireStack = requireStack; + throw err; +}; + +function finalizeEsmResolution(resolved, parentPath, pkgPath) { + if (RegExpPrototypeTest(encodedSepRegEx, resolved)) + throw new ERR_INVALID_MODULE_SPECIFIER( + resolved, 'must not include encoded "/" or "\\" characters', parentPath); + const filename = fileURLToPath(resolved); + const actual = tryFile(filename); + if (actual) + return actual; + const err = createEsmNotFoundErr(filename, + path.resolve(pkgPath, 'package.json')); + throw err; +} + +function createEsmNotFoundErr(request, path) { + // eslint-disable-next-line no-restricted-syntax + const err = new Error(`Cannot find module '${request}'`); + err.code = 'MODULE_NOT_FOUND'; + if (path) + err.path = path; + // TODO(BridgeAR): Add the requireStack as well. + return err; +} + +// Given a file name, pass it to the proper extension handler. +Module.prototype.load = function(filename) { + debug('load %j for module %j', filename, this.id); + + assert(!this.loaded); + this.filename = filename; + this.paths = Module._nodeModulePaths(path.dirname(filename)); + + const extension = findLongestRegisteredExtension(filename); + // allow .mjs to be overridden + if (StringPrototypeEndsWith(filename, '.mjs') && !Module._extensions['.mjs']) + throw new ERR_REQUIRE_ESM(filename, true); + + Module._extensions[extension](this, filename); + this.loaded = true; + + const esmLoader = asyncESM.esmLoader; + // Create module entry at load time to snapshot exports correctly + const exports = this.exports; + // Preemptively cache + if ((module?.module === undefined || + module.module.getStatus() < kEvaluated) && + !esmLoader.cjsCache.has(this)) + esmLoader.cjsCache.set(this, exports); +}; + + +// Loads a module at the given file path. Returns that module's +// `exports` property. +Module.prototype.require = function(id) { + validateString(id, 'id'); + if (id === '') { + throw new ERR_INVALID_ARG_VALUE('id', id, + 'must be a non-empty string'); + } + requireDepth++; + try { + return Module._load(id, this, /* isMain */ false); + } finally { + requireDepth--; + } +}; + + +// Resolved path to process.argv[1] will be lazily placed here +// (needed for setting breakpoint when called with --inspect-brk) +let resolvedArgv; +let hasPausedEntry = false; + +function wrapSafe(filename, content, cjsModuleInstance) { + if (patched) { + const wrapper = Module.wrap(content); + return vm.runInThisContext(wrapper, { + filename, + lineOffset: 0, + displayErrors: true, + importModuleDynamically: async (specifier) => { + const loader = asyncESM.esmLoader; + return loader.import(specifier, normalizeReferrerURL(filename)); + }, + }); + } + try { + return vm.compileFunction(content, [ + 'exports', + 'require', + 'module', + '__filename', + '__dirname', + ], { + filename, + importModuleDynamically(specifier) { + const loader = asyncESM.esmLoader; + return loader.import(specifier, normalizeReferrerURL(filename)); + }, + }); + } catch (err) { + if (process.mainModule === cjsModuleInstance) + enrichCJSError(err, content); + throw err; + } +} + +// Run the file contents in the correct scope or sandbox. Expose +// the correct helper variables (require, module, exports) to +// the file. +// Returns exception, if any. +Module.prototype._compile = function(content, filename) { + let moduleURL; + let redirects; + if (policy?.manifest) { + moduleURL = pathToFileURL(filename); + redirects = policy.manifest.getDependencyMapper(moduleURL); + policy.manifest.assertIntegrity(moduleURL, content); + } + + maybeCacheSourceMap(filename, content, this); + const compiledWrapper = wrapSafe(filename, content, this); + + let inspectorWrapper = null; + if (getOptionValue('--inspect-brk') && process._eval == null) { + if (!resolvedArgv) { + // We enter the repl if we're not given a filename argument. + if (process.argv[1]) { + try { + resolvedArgv = Module._resolveFilename(process.argv[1], null, false); + } catch { + // We only expect this codepath to be reached in the case of a + // preloaded module (it will fail earlier with the main entry) + assert(ArrayIsArray(getOptionValue('--require'))); + } + } else { + resolvedArgv = 'repl'; + } + } + + // Set breakpoint on module start + if (resolvedArgv && !hasPausedEntry && filename === resolvedArgv) { + hasPausedEntry = true; + inspectorWrapper = internalBinding('inspector').callAndPauseOnStart; + } + } + const dirname = path.dirname(filename); + const require = makeRequireFunction(this, redirects); + let result; + const exports = this.exports; + const thisValue = exports; + const module = this; + if (requireDepth === 0) statCache = new SafeMap(); + if (inspectorWrapper) { + result = inspectorWrapper(compiledWrapper, thisValue, exports, + require, module, filename, dirname); + } else { + result = ReflectApply(compiledWrapper, thisValue, + [exports, require, module, filename, dirname]); + } + hasLoadedAnyUserCJSModule = true; + if (requireDepth === 0) statCache = null; + return result; +}; + +// Native extension for .js +Module._extensions['.js'] = function(module, filename) { + // If already analyzed the source, then it will be cached. + const cached = cjsParseCache.get(module); + let content; + if (cached?.source) { + content = cached.source; + cached.source = undefined; + } else { + content = fs.readFileSync(filename, 'utf8'); + } + if (StringPrototypeEndsWith(filename, '.js')) { + const pkg = readPackageScope(filename); + // Function require shouldn't be used in ES modules. + if (pkg?.data?.type === 'module') { + const parent = moduleParentCache.get(module); + const parentPath = parent?.filename; + const packageJsonPath = path.resolve(pkg.path, 'package.json'); + const usesEsm = hasEsmSyntax(content); + const err = new ERR_REQUIRE_ESM(filename, usesEsm, parentPath, + packageJsonPath); + // Attempt to reconstruct the parent require frame. + if (Module._cache[parentPath]) { + let parentSource; + try { + parentSource = fs.readFileSync(parentPath, 'utf8'); + } catch {} + if (parentSource) { + const errLine = StringPrototypeSplit( + StringPrototypeSlice(err.stack, StringPrototypeIndexOf( + err.stack, ' at ')), '\n', 1)[0]; + const { 1: line, 2: col } = + RegExpPrototypeExec(/(\d+):(\d+)\)/, errLine) || []; + if (line && col) { + const srcLine = StringPrototypeSplit(parentSource, '\n')[line - 1]; + const frame = `${parentPath}:${line}\n${srcLine}\n${ + StringPrototypeRepeat(' ', col - 1)}^\n`; + setArrowMessage(err, frame); + } + } + } + throw err; + } + } + module._compile(content, filename); +}; + + +// Native extension for .json +Module._extensions['.json'] = function(module, filename) { + const content = fs.readFileSync(filename, 'utf8'); + + if (policy?.manifest) { + const moduleURL = pathToFileURL(filename); + policy.manifest.assertIntegrity(moduleURL, content); + } + + try { + module.exports = JSONParse(stripBOM(content)); + } catch (err) { + err.message = filename + ': ' + err.message; + throw err; + } +}; + + +// Native extension for .node +Module._extensions['.node'] = function(module, filename) { + if (policy?.manifest) { + const content = fs.readFileSync(filename); + const moduleURL = pathToFileURL(filename); + policy.manifest.assertIntegrity(moduleURL, content); + } + // Be aware this doesn't use `content` + return process.dlopen(module, path.toNamespacedPath(filename)); +}; + +function createRequireFromPath(filename) { + // Allow a directory to be passed as the filename + const trailingSlash = + StringPrototypeEndsWith(filename, '/') || + (isWindows && StringPrototypeEndsWith(filename, '\\')); + + const proxyPath = trailingSlash ? + path.join(filename, 'noop.js') : + filename; + + const m = new Module(proxyPath); + m.filename = proxyPath; + + m.paths = Module._nodeModulePaths(m.path); + return makeRequireFunction(m, null); +} + +const createRequireError = 'must be a file URL object, file URL string, or ' + + 'absolute path string'; + +function createRequire(filename) { + let filepath; + + if (isURLInstance(filename) || + (typeof filename === 'string' && !path.isAbsolute(filename))) { + try { + filepath = fileURLToPath(filename); + } catch { + throw new ERR_INVALID_ARG_VALUE('filename', filename, + createRequireError); + } + } else if (typeof filename !== 'string') { + throw new ERR_INVALID_ARG_VALUE('filename', filename, createRequireError); + } else { + filepath = filename; + } + return createRequireFromPath(filepath); +} + +Module.createRequire = createRequire; + +Module._initPaths = function() { + const homeDir = isWindows ? process.env.USERPROFILE : safeGetenv('HOME'); + const nodePath = isWindows ? process.env.NODE_PATH : safeGetenv('NODE_PATH'); + + // process.execPath is $PREFIX/bin/node except on Windows where it is + // $PREFIX\node.exe where $PREFIX is the root of the Node.js installation. + const prefixDir = isWindows ? + path.resolve(process.execPath, '..') : + path.resolve(process.execPath, '..', '..'); + + const paths = [path.resolve(prefixDir, 'lib', 'node')]; + + if (homeDir) { + ArrayPrototypeUnshift(paths, path.resolve(homeDir, '.node_libraries')); + ArrayPrototypeUnshift(paths, path.resolve(homeDir, '.node_modules')); + } + + if (nodePath) { + ArrayPrototypeUnshiftApply(paths, ArrayPrototypeFilter( + StringPrototypeSplit(nodePath, path.delimiter), + Boolean + )); + } + + modulePaths = paths; + + // Clone as a shallow copy, for introspection. + Module.globalPaths = ArrayPrototypeSlice(modulePaths); +}; + +Module._preloadModules = function(requests) { + if (!ArrayIsArray(requests)) + return; + + isPreloading = true; + + // Preloaded modules have a dummy parent module which is deemed to exist + // in the current working directory. This seeds the search path for + // preloaded modules. + const parent = new Module('internal/preload', null); + try { + parent.paths = Module._nodeModulePaths(process.cwd()); + } catch (e) { + if (e.code !== 'ENOENT') { + isPreloading = false; + throw e; + } + } + for (let n = 0; n < requests.length; n++) + parent.require(requests[n]); + isPreloading = false; +}; + +Module.syncBuiltinESMExports = function syncBuiltinESMExports() { + for (const mod of NativeModule.map.values()) { + if (mod.canBeRequiredByUsers) { + mod.syncExports(); + } + } +}; + +// Backwards compatibility +Module.Module = Module;