From 1377086da4e44c6e0f28fb4328ae9c9f1fd8d497 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 12:39:13 -0400 Subject: [PATCH 01/30] 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 02/30] 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 03/30] 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 04/30] 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 05/30] 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 06/30] 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 07/30] 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 08/30] 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; From 791b0528bc5a08c9b943fec2a60aa557ae9aaa68 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 14:23:03 -0400 Subject: [PATCH 09/30] cleanup --- NOTES.md | 61 -------------------------------------------------------- TODO.md | 12 ----------- 2 files changed, 73 deletions(-) delete mode 100644 NOTES.md delete mode 100644 TODO.md diff --git a/NOTES.md b/NOTES.md deleted file mode 100644 index 0f0cbf15d..000000000 --- a/NOTES.md +++ /dev/null @@ -1,61 +0,0 @@ -*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 deleted file mode 100644 index 45f519a16..000000000 --- a/TODO.md +++ /dev/null @@ -1,12 +0,0 @@ -*Delete this file before merging this PR* - -## TODOs - -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. - - From 385e0947ef258312ab8f60e941061c4626e03d30 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 14:45:45 -0400 Subject: [PATCH 10/30] sync node-internal-modules-cjs-loader-old.js with raw --- .../node-internal-modules-cjs-loader-old.js | 88 +- raw/download-and-compare.sh | 30 +- ...d5d77306f6dff9110c1f77fefab25f973415770.js | 1364 +++++++++++++++++ ...nal-modules-cjs-loader-v15.3.0-stripped.js | 92 ++ ...ode-internal-modules-cjs-loader-v15.3.0.js | 1272 +++++++++++++++ 5 files changed, 2795 insertions(+), 51 deletions(-) create mode 100644 raw/node-internal-modules-cjs-loader-2d5d77306f6dff9110c1f77fefab25f973415770.js create mode 100644 raw/node-internal-modules-cjs-loader-v15.3.0-stripped.js create mode 100644 raw/node-internal-modules-cjs-loader-v15.3.0.js diff --git a/dist-raw/node-internal-modules-cjs-loader-old.js b/dist-raw/node-internal-modules-cjs-loader-old.js index ba19fb049..17b0247f4 100644 --- a/dist-raw/node-internal-modules-cjs-loader-old.js +++ b/dist-raw/node-internal-modules-cjs-loader-old.js @@ -2,54 +2,13 @@ // https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/modules/cjs/loader.js // Each function and variable below must have a comment linking to the source in node's github repo. +const {JSONParse} = require('./node-primordials'); 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; - -/** - * copied from Module._extensions['.js'] - * https://github.com/nodejs/node/blob/v15.3.0/lib/internal/modules/cjs/loader.js#L1113-L1120 - * @param {import('../src/index').Service} service - * @param {NodeJS.Module} module - * @param {string} filename - */ -function assertScriptCanLoadAsCJSImpl(service, module, filename) { - const pkg = readPackageScope(filename); - - // ts-node modification: allow our configuration to override - const tsNodeClassification = service.moduleTypeClassifier.classifyModule(normalizeSlashes(filename)); - if(tsNodeClassification.moduleType === 'cjs') return; - - // Function require shouldn't be used in ES modules. - if (tsNodeClassification.moduleType === 'esm' || (pkg && pkg.data && pkg.data.type === 'module')) { - const parentPath = module.parent && module.parent.filename; - const packageJsonPath = pkg ? path.resolve(pkg.path, 'package.json') : null; - throw createErrRequireEsm(filename, parentPath, packageJsonPath); - } -} -// Copied from https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/modules/cjs/loader.js#L285-L301 -function readPackageScope(checkPath) { - const rootSeparatorIndex = checkPath.indexOf(path.sep); - let separatorIndex; - while ( - (separatorIndex = checkPath.lastIndexOf(path.sep)) > rootSeparatorIndex - ) { - checkPath = checkPath.slice(0, separatorIndex); - if (checkPath.endsWith(path.sep + 'node_modules')) - return false; - const pjson = readPackage(checkPath); - if (pjson) return { - path: checkPath, - data: pjson - }; - } - return false; -} // Copied from https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/modules/cjs/loader.js#L249 const packageJsonCache = new Map(); @@ -85,3 +44,48 @@ function readPackage(requestPath) { throw e; } } + +// Copied from https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/modules/cjs/loader.js#L285-L301 +function readPackageScope(checkPath) { + const rootSeparatorIndex = checkPath.indexOf(path.sep); + let separatorIndex; + while ( + (separatorIndex = checkPath.lastIndexOf(path.sep)) > rootSeparatorIndex + ) { + checkPath = checkPath.slice(0, separatorIndex); + if (checkPath.endsWith(path.sep + 'node_modules')) + return false; + const pjson = readPackage(checkPath); + if (pjson) return { + path: checkPath, + data: pjson + }; + } + return false; +} + +/** + * copied from Module._extensions['.js'] + * https://github.com/nodejs/node/blob/v15.3.0/lib/internal/modules/cjs/loader.js#L1113-L1120 + * @param {import('../src/index').Service} service + * @param {NodeJS.Module} module + * @param {string} filename + */ +function assertScriptCanLoadAsCJSImpl(service, module, filename) { + const pkg = readPackageScope(filename); + + // ts-node modification: allow our configuration to override + const tsNodeClassification = service.moduleTypeClassifier.classifyModule(normalizeSlashes(filename)); + if(tsNodeClassification.moduleType === 'cjs') return; + + // Function require shouldn't be used in ES modules. + if (tsNodeClassification.moduleType === 'esm' || (pkg && pkg.data && pkg.data.type === 'module')) { + const parentPath = module.parent && module.parent.filename; + const packageJsonPath = pkg ? path.resolve(pkg.path, 'package.json') : null; + throw createErrRequireEsm(filename, parentPath, packageJsonPath); + } +} + +module.exports = { + assertScriptCanLoadAsCJSImpl +}; diff --git a/raw/download-and-compare.sh b/raw/download-and-compare.sh index cf22b33c9..11e47ff2b 100755 --- a/raw/download-and-compare.sh +++ b/raw/download-and-compare.sh @@ -17,32 +17,44 @@ compare() { ext=.js -version=v17.0.1 +#### + path=lib/internal/modules/cjs/loader local=node-internal-modules-cjs-loader +version=v17.0.1 download compare -version=v13.12.0 +version=v15.3.0 +download +compare + +version=2d5d77306f6dff9110c1f77fefab25f973415770 +download +compare + +#### + path=lib/internal/modules/esm/resolve local=node-internal-modules-esm-resolve +version=v13.12.0 download compare -version=v17.0.0 -path=lib/internal/repl/await -local=node-internal-repl-await +version=v15.3.0 download compare -version=88799930794045795e8abac874730f9eba7e2300 +#### + path=lib/internal/repl/await local=node-internal-repl-await +version=v17.0.0 download compare -version=v15.3.0 -path=lib/internal/modules/esm/resolve -local=node-internal-modules-esm-resolve +version=88799930794045795e8abac874730f9eba7e2300 download compare + +#### diff --git a/raw/node-internal-modules-cjs-loader-2d5d77306f6dff9110c1f77fefab25f973415770.js b/raw/node-internal-modules-cjs-loader-2d5d77306f6dff9110c1f77fefab25f973415770.js new file mode 100644 index 000000000..ffba34181 --- /dev/null +++ b/raw/node-internal-modules-cjs-loader-2d5d77306f6dff9110c1f77fefab25f973415770.js @@ -0,0 +1,1364 @@ +// Copied from https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/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, + Error, + JSONParse, + Map, + Number, + ObjectCreate, + ObjectDefineProperty, + ObjectFreeze, + ObjectGetOwnPropertyDescriptor, + ObjectGetPrototypeOf, + ObjectIs, + ObjectKeys, + ObjectPrototypeHasOwnProperty, + ObjectSetPrototypeOf, + ReflectSet, + RegExpPrototypeTest, + SafeMap, + String, + StringPrototypeIndexOf, + StringPrototypeMatch, + StringPrototypeSlice, + StringPrototypeStartsWith, +} = primordials; + +const { NativeModule } = require('internal/bootstrap/loaders'); +const { + maybeCacheSourceMap, + rekeySourceMap +} = require('internal/source_map/source_map_cache'); +const { pathToFileURL, fileURLToPath, URL } = 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 { emitWarningSync } = require('internal/process/warning'); +const { + internalModuleReadJSON, + internalModuleStat +} = internalBinding('fs'); +const { safeGetenv } = internalBinding('credentials'); +const { + makeRequireFunction, + normalizeReferrerURL, + stripBOM, + loadNativeModule +} = require('internal/modules/cjs/helpers'); +const { getOptionValue } = require('internal/options'); +const enableSourceMaps = getOptionValue('--enable-source-maps'); +const preserveSymlinks = getOptionValue('--preserve-symlinks'); +const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main'); +const manifest = getOptionValue('--experimental-policy') ? + require('internal/process/policy').manifest : + null; +const { compileFunction } = internalBinding('contextify'); + +// Whether any user-provided CJS modules had been loaded (executed). +// Used for internal assertions. +let hasLoadedAnyUserCJSModule = false; + +const { + ERR_INVALID_ARG_VALUE, + ERR_INVALID_OPT_VALUE, + ERR_INVALID_PACKAGE_CONFIG, + ERR_INVALID_PACKAGE_TARGET, + ERR_INVALID_MODULE_SPECIFIER, + ERR_PACKAGE_PATH_NOT_EXPORTED, + ERR_REQUIRE_ESM +} = require('internal/errors').codes; +const { validateString } = require('internal/validators'); +const pendingDeprecation = getOptionValue('--pending-deprecation'); + +module.exports = { + wrapSafe, Module, toRealPath, readPackageScope, + get hasLoadedAnyUserCJSModule() { return hasLoadedAnyUserCJSModule; } +}; + +let asyncESM, ModuleJob, ModuleWrap, kInstantiated; + +const { + CHAR_FORWARD_SLASH, + CHAR_BACKWARD_SLASH, + CHAR_COLON +} = require('internal/constants'); + + +const { + isProxy +} = require('internal/util/types'); + +const isWindows = process.platform === 'win32'; + +const relativeResolveCache = ObjectCreate(null); + +let requireDepth = 0; +let statCache = null; + +function enrichCJSError(err) { + const stack = err.stack.split('\n'); + + const lineWithErr = stack[1]; + + /* + The regular expression below targets the most common import statement + usage. However, some cases are not matching, cases like import statement + after a comment block and/or after a variable definition. + */ + if (err.message.startsWith('Unexpected token \'export\'') || + (RegExpPrototypeTest(/^\s*import(?=[ {'"*])\s*(?![ (])/, lineWithErr))) { + // Emit the warning synchronously because we are in the middle of handling + // a SyntaxError that will throw and likely terminate the process before an + // asynchronous warning would be emitted. + emitWarningSync( + 'To load an ES module, set "type": "module" in the package.json or use ' + + 'the .mjs extension.' + ); + } +} + +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) statCache.set(filename, result); + return result; +} + +function updateChildren(parent, child, scan) { + const children = parent && parent.children; + if (children && !(scan && children.includes(child))) + children.push(child); +} + +function Module(id = '', parent) { + this.id = id; + this.path = path.dirname(id); + this.exports = {}; + this.parent = parent; + updateChildren(parent, this, false); + this.filename = null; + this.loaded = false; + this.children = []; +} + +const builtinModules = []; +for (const [id, mod] of NativeModule.map) { + if (mod.canBeRequiredByUsers) { + builtinModules.push(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 debug = require('internal/util/debuglog').debuglog('module'); +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 json = internalModuleReadJSON(path.toNamespacedPath(jsonPath)); + if (json === undefined) { + packageJsonCache.set(jsonPath, false); + return false; + } + + if (manifest) { + const jsonURL = pathToFileURL(jsonPath); + manifest.assertIntegrity(jsonURL, json); + } + + try { + const parsed = JSONParse(json); + const filtered = { + name: parsed.name, + main: parsed.main, + exports: parsed.exports, + 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 = checkPath.indexOf(path.sep); + let separatorIndex; + while ( + (separatorIndex = checkPath.lastIndexOf(path.sep)) > rootSeparatorIndex + ) { + checkPath = checkPath.slice(0, separatorIndex); + if (checkPath.endsWith(path.sep + 'node_modules')) + return false; + const pjson = readPackage(checkPath); + if (pjson) return { + path: checkPath, + data: pjson + }; + } + return false; +} + +function readPackageMain(requestPath) { + const pkg = readPackage(requestPath); + return pkg ? pkg.main : undefined; +} + +function readPackageExports(requestPath) { + const pkg = readPackage(requestPath); + return pkg ? pkg.exports : undefined; +} + +function tryPackage(requestPath, exts, isMain, originalPath) { + const pkg = readPackageMain(requestPath); + + 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 if (pendingDeprecation) { + 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 Map(); + +// 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 = name.indexOf('.', startIndex)) !== -1) { + startIndex = index + 1; + if (index === 0) continue; // Skip dotfiles like .gitignore + currentExtension = name.slice(index); + if (Module._extensions[currentExtension]) return currentExtension; + } + return '.js'; +} + +function trySelf(parentPath, request) { + const { data: pkg, path: basePath } = 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; + } + + const fromExports = applyExports(basePath, expansion); + if (fromExports) { + return tryFile(fromExports, false); + } + assert(fromExports !== false); +} + +function isConditionalDotExportSugar(exports, basePath) { + if (typeof exports === 'string') + return true; + if (ArrayIsArray(exports)) + return true; + if (typeof exports !== 'object') + return false; + let isConditional = false; + let firstCheck = true; + for (const key of ObjectKeys(exports)) { + const curIsConditional = key[0] !== '.'; + if (firstCheck) { + firstCheck = false; + isConditional = curIsConditional; + } else if (isConditional !== curIsConditional) { + throw new ERR_INVALID_PACKAGE_CONFIG(basePath, '"exports" cannot ' + + 'contain some keys starting with \'.\' and some not. The exports ' + + 'object must either be an object of package subpath keys or an ' + + 'object of main entry condition name keys only.'); + } + } + return isConditional; +} + +function applyExports(basePath, expansion) { + const mappingKey = `.${expansion}`; + + let pkgExports = readPackageExports(basePath); + if (pkgExports === undefined || pkgExports === null) + return false; + + if (isConditionalDotExportSugar(pkgExports, basePath)) + pkgExports = { '.': pkgExports }; + + if (typeof pkgExports === 'object') { + if (ObjectPrototypeHasOwnProperty(pkgExports, mappingKey)) { + const mapping = pkgExports[mappingKey]; + return resolveExportsTarget(pathToFileURL(basePath + '/'), mapping, '', + mappingKey); + } + + let dirMatch = ''; + for (const candidateKey of ObjectKeys(pkgExports)) { + if (candidateKey[candidateKey.length - 1] !== '/') continue; + if (candidateKey.length > dirMatch.length && + StringPrototypeStartsWith(mappingKey, candidateKey)) { + dirMatch = candidateKey; + } + } + + if (dirMatch !== '') { + const mapping = pkgExports[dirMatch]; + const subpath = StringPrototypeSlice(mappingKey, dirMatch.length); + const resolved = resolveExportsTarget(pathToFileURL(basePath + '/'), + mapping, subpath, mappingKey); + // Extension searching for folder exports only + const rc = stat(resolved); + if (rc === 0) return resolved; + if (!(RegExpPrototypeTest(trailingSlashRegex, resolved))) { + const exts = ObjectKeys(Module._extensions); + const filename = tryExtensions(resolved, exts, false); + if (filename) return filename; + } + if (rc === 1) { + const exts = ObjectKeys(Module._extensions); + const filename = tryPackage(resolved, exts, false, + basePath + expansion); + if (filename) return filename; + } + // Undefined means not found + return; + } + } + + throw new ERR_PACKAGE_PATH_NOT_EXPORTED(basePath, mappingKey); +} + +// 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 [, name, expansion = ''] = + StringPrototypeMatch(request, EXPORTS_PATTERN) || []; + if (!name) { + return false; + } + + const basePath = path.resolve(nmPath, name); + const fromExports = applyExports(basePath, expansion); + if (fromExports) { + return tryFile(fromExports, false); + } + return fromExports; +} + +function isArrayIndex(p) { + assert(typeof p === 'string'); + const n = Number(p); + if (String(n) !== p) + return false; + if (ObjectIs(n, +0)) + return true; + if (!Number.isInteger(n)) + return false; + return n >= 0 && n < (2 ** 32) - 1; +} + +function resolveExportsTarget(baseUrl, target, subpath, mappingKey) { + if (typeof target === 'string') { + let resolvedTarget, resolvedTargetPath; + const pkgPathPath = baseUrl.pathname; + if (StringPrototypeStartsWith(target, './')) { + resolvedTarget = new URL(target, baseUrl); + resolvedTargetPath = resolvedTarget.pathname; + if (!StringPrototypeStartsWith(resolvedTargetPath, pkgPathPath) || + StringPrototypeIndexOf(resolvedTargetPath, '/node_modules/', + pkgPathPath.length - 1) !== -1) + resolvedTarget = undefined; + } + if (subpath.length > 0 && target[target.length - 1] !== '/') + resolvedTarget = undefined; + if (resolvedTarget === undefined) + throw new ERR_INVALID_PACKAGE_TARGET(StringPrototypeSlice(baseUrl.pathname + , 0, -1), mappingKey, subpath, target); + const resolved = new URL(subpath, resolvedTarget); + const resolvedPath = resolved.pathname; + if (StringPrototypeStartsWith(resolvedPath, resolvedTargetPath) && + StringPrototypeIndexOf(resolvedPath, '/node_modules/', + pkgPathPath.length - 1) === -1) { + return fileURLToPath(resolved); + } + throw new ERR_INVALID_MODULE_SPECIFIER(StringPrototypeSlice(baseUrl.pathname + , 0, -1), mappingKey); + } else if (ArrayIsArray(target)) { + if (target.length === 0) + throw new ERR_PACKAGE_PATH_NOT_EXPORTED( + StringPrototypeSlice(baseUrl.pathname, 0, -1), mappingKey + subpath); + let lastException; + for (const targetValue of target) { + try { + return resolveExportsTarget(baseUrl, targetValue, subpath, mappingKey); + } catch (e) { + lastException = e; + if (e.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED' && + e.code !== 'ERR_INVALID_PACKAGE_TARGET') + throw e; + } + } + // Throw last fallback error + assert(lastException !== undefined); + throw lastException; + } else if (typeof target === 'object' && target !== null) { + const keys = ObjectKeys(target); + if (keys.some(isArrayIndex)) { + throw new ERR_INVALID_PACKAGE_CONFIG(baseUrl, '"exports" cannot ' + + 'contain numeric property keys.'); + } + for (const p of keys) { + switch (p) { + case 'node': + case 'require': + try { + return resolveExportsTarget(baseUrl, target[p], subpath, + mappingKey); + } catch (e) { + if (e.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') throw e; + } + break; + case 'default': + try { + return resolveExportsTarget(baseUrl, target.default, subpath, + mappingKey); + } catch (e) { + if (e.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') throw e; + } + } + } + throw new ERR_PACKAGE_PATH_NOT_EXPORTED( + StringPrototypeSlice(baseUrl.pathname, 0, -1), mappingKey + subpath); + } else if (target === null) { + throw new ERR_PACKAGE_PATH_NOT_EXPORTED( + StringPrototypeSlice(baseUrl.pathname, 0, -1), mappingKey + subpath); + } + throw new ERR_INVALID_PACKAGE_TARGET( + StringPrototypeSlice(baseUrl.pathname, 0, -1), mappingKey, subpath, target); +} + +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' + + (paths.length === 1 ? paths[0] : paths.join('\x00')); + const entry = Module._pathCache[cacheKey]; + if (entry) + return entry; + + let exts; + let trailingSlash = request.length > 0 && + request.charCodeAt(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); + // Undefined means not found, false means no exports + if (exportsResolved === undefined) + break; + 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 (from.charCodeAt(from.length - 1) === CHAR_BACKWARD_SLASH && + from.charCodeAt(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 = from.charCodeAt(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) + paths.push(from.slice(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 = from.charCodeAt(i); + if (code === CHAR_FORWARD_SLASH) { + if (p !== nmLen) + paths.push(from.slice(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. + paths.push('/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 (request.charAt(0) !== '.' || + (request.length > 1 && + request.charAt(1) !== '.' && + request.charAt(1) !== '/' && + (!isWindows || request.charAt(1) !== '\\'))) { + + let paths = modulePaths; + if (parent != null && parent.paths && parent.paths.length) { + paths = parent.paths.concat(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; + } +}); + +// Object.prototype and ObjectPrototype refer to our 'primordials' versions +// and are not identical to the versions on the global object. +const PublicObjectPrototype = global.Object.prototype; + +function getExportsForCircularRequire(module) { + if (module.exports && + !isProxy(module.exports) && + ObjectGetPrototypeOf(module.exports) === PublicObjectPrototype && + // 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); + + const cachedModule = Module._cache[filename]; + if (cachedModule !== undefined) { + updateChildren(parent, cachedModule, true); + if (!cachedModule.loaded) + return getExportsForCircularRequire(cachedModule); + return cachedModule.exports; + } + + const mod = loadNativeModule(filename, request); + if (mod && mod.canBeRequiredByUsers) return mod.exports; + + // Don't call updateChildren(), Module constructor already does. + const module = 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 { + // Intercept exceptions that occur during the first tick and rekey them + // on error instance rather than module instance (which will immediately be + // garbage collected). + if (enableSourceMaps) { + try { + module.load(filename); + } catch (err) { + rekeySourceMap(Module._cache[filename], err); + throw err; /* node-do-not-add-exception-line */ + } + } else { + module.load(filename); + } + threw = false; + } finally { + if (threw) { + delete Module._cache[filename]; + if (parent !== undefined) { + delete relativeResolveCache[relResolveCacheIdentifier]; + const children = parent && parent.children; + if (ArrayIsArray(children)) { + const index = children.indexOf(module); + if (index !== -1) { + children.splice(index, 1); + } + } + } + } else if (module.exports && + !isProxy(module.exports) && + ObjectGetPrototypeOf(module.exports) === + CircularRequirePrototypeWarningProxy) { + ObjectSetPrototypeOf(module.exports, PublicObjectPrototype); + } + } + + return module.exports; +}; + +Module._resolveFilename = function(request, parent, isMain, options) { + if (NativeModule.canBeRequiredByUsers(request)) { + return request; + } + + let paths; + + if (typeof options === 'object' && options !== null) { + if (ArrayIsArray(options.paths)) { + const isRelative = request.startsWith('./') || + request.startsWith('../') || + ((isWindows && request.startsWith('.\\')) || + request.startsWith('..\\')); + + 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 (!paths.includes(lookupPaths[j])) + paths.push(lookupPaths[j]); + } + } + } + } else if (options.paths === undefined) { + paths = Module._resolveLookupPaths(request, parent); + } else { + throw new ERR_INVALID_OPT_VALUE('options.paths', options.paths); + } + } else { + paths = Module._resolveLookupPaths(request, parent); + } + + if (parent && parent.filename) { + const filename = trySelf(parent.filename, request); + if (filename) { + const cacheKey = request + '\x00' + + (paths.length === 1 ? paths[0] : paths.join('\x00')); + Module._pathCache[cacheKey] = filename; + return filename; + } + } + + // 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 = cursor.parent) { + requireStack.push(cursor.filename || cursor.id); + } + let message = `Cannot find module '${request}'`; + if (requireStack.length > 0) { + message = message + '\nRequire stack:\n- ' + requireStack.join('\n- '); + } + // eslint-disable-next-line no-restricted-syntax + const err = new Error(message); + err.code = 'MODULE_NOT_FOUND'; + err.requireStack = requireStack; + throw 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 (filename.endsWith('.mjs') && !Module._extensions['.mjs']) { + throw new ERR_REQUIRE_ESM(filename); + } + Module._extensions[extension](this, filename); + this.loaded = true; + + const ESMLoader = asyncESM.ESMLoader; + const url = `${pathToFileURL(filename)}`; + const module = ESMLoader.moduleMap.get(url); + // Create module entry at load time to snapshot exports correctly + const exports = this.exports; + // Called from cjs translator + if (module !== undefined && module.module !== undefined) { + if (module.module.getStatus() >= kInstantiated) + module.module.setExport('default', exports); + } else { + // Preemptively cache + // We use a function to defer promise creation for async hooks. + ESMLoader.moduleMap.set( + url, + // Module job creation will start promises. + // We make it a function to lazily trigger those promises + // for async hooks compatibility. + () => new ModuleJob(ESMLoader, url, () => + new ModuleWrap(url, undefined, ['default'], function() { + this.setExport('default', exports); + }) + , false /* isMain */, false /* inspectBrk */) + ); + } +}; + + +// 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)); + }, + }); + } + let compiled; + try { + compiled = compileFunction( + content, + filename, + 0, + 0, + undefined, + false, + undefined, + [], + [ + 'exports', + 'require', + 'module', + '__filename', + '__dirname', + ] + ); + } catch (err) { + if (process.mainModule === cjsModuleInstance) + enrichCJSError(err); + throw err; + } + + const { callbackMap } = internalBinding('module_wrap'); + callbackMap.set(compiled.cacheKey, { + importModuleDynamically: async (specifier) => { + const loader = asyncESM.ESMLoader; + return loader.import(specifier, normalizeReferrerURL(filename)); + } + }); + + return compiled.function; +} + +// 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 (manifest) { + moduleURL = pathToFileURL(filename); + redirects = manifest.getRedirector(moduleURL); + 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 Map(); + if (inspectorWrapper) { + result = inspectorWrapper(compiledWrapper, thisValue, exports, + require, module, filename, dirname); + } else { + result = compiledWrapper.call(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 (filename.endsWith('.js')) { + const pkg = readPackageScope(filename); + // Function require shouldn't be used in ES modules. + if (pkg && pkg.data && pkg.data.type === 'module') { + const parentPath = module.parent && module.parent.filename; + const packageJsonPath = path.resolve(pkg.path, 'package.json'); + throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath); + } + } + const content = fs.readFileSync(filename, 'utf8'); + module._compile(content, filename); +}; + + +// Native extension for .json +Module._extensions['.json'] = function(module, filename) { + const content = fs.readFileSync(filename, 'utf8'); + + if (manifest) { + const moduleURL = pathToFileURL(filename); + 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 (manifest) { + const content = fs.readFileSync(filename); + const moduleURL = pathToFileURL(filename); + 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 = + filename.endsWith('/') || (isWindows && filename.endsWith('\\')); + + 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); +} + +Module.createRequireFromPath = deprecate( + createRequireFromPath, + 'Module.createRequireFromPath() is deprecated. ' + + 'Use Module.createRequire() instead.', + 'DEP0130' +); + +const createRequireError = 'must be a file URL object, file URL string, or ' + + 'absolute path string'; + +function createRequire(filename) { + let filepath; + + if (filename instanceof URL || + (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, '..', '..'); + + let paths = [path.resolve(prefixDir, 'lib', 'node')]; + + if (homeDir) { + paths.unshift(path.resolve(homeDir, '.node_libraries')); + paths.unshift(path.resolve(homeDir, '.node_modules')); + } + + if (nodePath) { + paths = nodePath.split(path.delimiter).filter(function pathsFilterCB(path) { + return !!path; + }).concat(paths); + } + + modulePaths = paths; + + // Clone as a shallow copy, for introspection. + Module.globalPaths = modulePaths.slice(0); +}; + +Module._preloadModules = function(requests) { + if (!ArrayIsArray(requests)) + return; + + // 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') { + throw e; + } + } + for (let n = 0; n < requests.length; n++) + parent.require(requests[n]); +}; + +Module.syncBuiltinESMExports = function syncBuiltinESMExports() { + for (const mod of NativeModule.map.values()) { + if (mod.canBeRequiredByUsers) { + mod.syncExports(); + } + } +}; + +// Backwards compatibility +Module.Module = Module; + +// We have to load the esm things after module.exports! +asyncESM = require('internal/process/esm_loader'); +ModuleJob = require('internal/modules/esm/module_job'); +({ ModuleWrap, kInstantiated } = internalBinding('module_wrap')); diff --git a/raw/node-internal-modules-cjs-loader-v15.3.0-stripped.js b/raw/node-internal-modules-cjs-loader-v15.3.0-stripped.js new file mode 100644 index 000000000..c33807525 --- /dev/null +++ b/raw/node-internal-modules-cjs-loader-v15.3.0-stripped.js @@ -0,0 +1,92 @@ +// Copied from https://github.com/nodejs/node/blob/v15.3.0/lib/internal/modules/cjs/loader.js + +'use strict'; + +const { + JSONParse, + SafeMap, + StringPrototypeEndsWith, + StringPrototypeLastIndexOf, + StringPrototypeIndexOf, + StringPrototypeSlice, +} = primordials; +const path = require('path'); +const packageJsonReader = require('internal/modules/package_json_reader'); + +const { + ERR_REQUIRE_ESM +} = require('internal/errors').codes; + +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; +} + +// Native extension for .js +Module._extensions['.js'] = function(module, filename) { + if (StringPrototypeEndsWith(filename, '.js')) { + const pkg = readPackageScope(filename); + // Function require shouldn't be used in ES modules. + if (pkg && pkg.data && pkg.data.type === 'module') { + const parent = moduleParentCache.get(module); + const parentPath = parent && parent.filename; + const packageJsonPath = path.resolve(pkg.path, 'package.json'); + throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath); + } + } + // If already analyzed the source, then it will be cached. + const cached = cjsParseCache.get(module); + let content; + if (cached && cached.source) { + content = cached.source; + cached.source = undefined; + } else { + content = fs.readFileSync(filename, 'utf8'); + } + module._compile(content, filename); +}; diff --git a/raw/node-internal-modules-cjs-loader-v15.3.0.js b/raw/node-internal-modules-cjs-loader-v15.3.0.js new file mode 100644 index 000000000..894724791 --- /dev/null +++ b/raw/node-internal-modules-cjs-loader-v15.3.0.js @@ -0,0 +1,1272 @@ +// Copied from https://github.com/nodejs/node/blob/v15.3.0/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, + Boolean, + Error, + JSONParse, + ObjectCreate, + ObjectDefineProperty, + ObjectFreeze, + ObjectGetOwnPropertyDescriptor, + ObjectGetPrototypeOf, + ObjectKeys, + ObjectPrototype, + ObjectPrototypeHasOwnProperty, + ObjectSetPrototypeOf, + ReflectApply, + ReflectSet, + RegExpPrototypeTest, + SafeMap, + SafeWeakMap, + String, + StringPrototypeCharAt, + StringPrototypeCharCodeAt, + StringPrototypeEndsWith, + StringPrototypeLastIndexOf, + StringPrototypeIndexOf, + StringPrototypeMatch, + 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 { + getSourceMapsEnabled, + maybeCacheSourceMap, + rekeySourceMap +} = 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 { + makeRequireFunction, + normalizeReferrerURL, + stripBOM, + cjsConditions, + loadNativeModule +} = 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; +const { compileFunction } = internalBinding('contextify'); + +// Whether any user-provided CJS modules had been loaded (executed). +// Used for internal assertions. +let hasLoadedAnyUserCJSModule = false; + +const { + ERR_INVALID_ARG_VALUE, + ERR_INVALID_MODULE_SPECIFIER, + ERR_REQUIRE_ESM +} = require('internal/errors').codes; +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; + +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) statCache.set(filename, result); + return result; +} + +function updateChildren(parent, child, scan) { + const children = parent && 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 [id, 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; + } +}); + +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 if (pendingDeprecation) { + 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), request, 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 [, name, expansion = ''] = + StringPrototypeMatch(request, EXPORTS_PATTERN) || []; + if (!name) + return; + const pkgPath = path.resolve(nmPath, name); + const pkg = readPackage(pkgPath); + if (pkg && pkg.exports !== null && pkg.exports !== undefined) { + try { + return finalizeEsmResolution(packageExportsResolve( + pathToFileURL(pkgPath + '/package.json'), '.' + expansion, pkg, null, + cjsConditions), request, 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 != null && parent.paths && 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); + + 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 && 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 { + // Intercept exceptions that occur during the first tick and rekey them + // on error instance rather than module instance (which will immediately be + // garbage collected). + if (getSourceMapsEnabled()) { + try { + module.load(filename); + } catch (err) { + rekeySourceMap(Module._cache[filename], err); + throw err; /* node-do-not-add-exception-line */ + } + } else { + module.load(filename); + } + threw = false; + } finally { + if (threw) { + delete Module._cache[filename]; + if (parent !== undefined) { + delete relativeResolveCache[relResolveCacheIdentifier]; + const children = parent && 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 (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 && parent.filename) { + if (request[0] === '#') { + const pkg = readPackageScope(parent.filename) || {}; + if (pkg.data && pkg.data.imports !== null && + pkg.data.imports !== undefined) { + try { + return finalizeEsmResolution( + packageImportsResolve(request, pathToFileURL(parent.filename), + cjsConditions), request, parent.filename, + pkg.path); + } catch (e) { + if (e.code === 'ERR_MODULE_NOT_FOUND') + throw createEsmNotFoundErr(request); + throw e; + } + } + } + } + + // Try module self resoultion 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(match, request, parentPath, pkgPath) { + const { resolved, exact } = match; + if (RegExpPrototypeTest(encodedSepRegEx, resolved)) + throw new ERR_INVALID_MODULE_SPECIFIER( + resolved, 'must not include encoded "/" or "\\" characters', parentPath); + const filename = fileURLToPath(resolved); + let actual = tryFile(filename); + if (!exact && !actual) { + const exts = ObjectKeys(Module._extensions); + actual = tryExtensions(filename, exts, false) || + tryPackage(filename, exts, false, request); + } + 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); + + 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)); + }, + }); + } + let compiled; + try { + compiled = compileFunction( + content, + filename, + 0, + 0, + undefined, + false, + undefined, + [], + [ + 'exports', + 'require', + 'module', + '__filename', + '__dirname', + ] + ); + } catch (err) { + if (process.mainModule === cjsModuleInstance) + enrichCJSError(err); + throw err; + } + + const { callbackMap } = internalBinding('module_wrap'); + callbackMap.set(compiled.cacheKey, { + importModuleDynamically: async (specifier) => { + const loader = asyncESM.ESMLoader; + return loader.import(specifier, normalizeReferrerURL(filename)); + } + }); + + return compiled.function; +} + +// 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 (StringPrototypeEndsWith(filename, '.js')) { + const pkg = readPackageScope(filename); + // Function require shouldn't be used in ES modules. + if (pkg && pkg.data && pkg.data.type === 'module') { + const parent = moduleParentCache.get(module); + const parentPath = parent && parent.filename; + const packageJsonPath = path.resolve(pkg.path, 'package.json'); + throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath); + } + } + // If already analyzed the source, then it will be cached. + const cached = cjsParseCache.get(module); + let content; + if (cached && cached.source) { + content = cached.source; + cached.source = undefined; + } else { + content = fs.readFileSync(filename, 'utf8'); + } + 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); +} + +Module.createRequireFromPath = deprecate( + createRequireFromPath, + 'Module.createRequireFromPath() is deprecated. ' + + 'Use Module.createRequire() instead.', + 'DEP0130' +); + +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, '..', '..'); + + let paths = [path.resolve(prefixDir, 'lib', 'node')]; + + if (homeDir) { + paths.unshift(path.resolve(homeDir, '.node_libraries')); + paths.unshift(path.resolve(homeDir, '.node_modules')); + } + + if (nodePath) { + paths = ArrayPrototypeConcat(ArrayPrototypeFilter( + StringPrototypeSplit(nodePath, path.delimiter), + Boolean + ), paths); + } + + modulePaths = paths; + + // Clone as a shallow copy, for introspection. + Module.globalPaths = ArrayPrototypeSlice(modulePaths); +}; + +Module._preloadModules = function(requests) { + if (!ArrayIsArray(requests)) + return; + + // 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') { + throw e; + } + } + for (let n = 0; n < requests.length; n++) + parent.require(requests[n]); +}; + +Module.syncBuiltinESMExports = function syncBuiltinESMExports() { + for (const mod of NativeModule.map.values()) { + if (mod.canBeRequiredByUsers) { + mod.syncExports(); + } + } +}; + +// Backwards compatibility +Module.Module = Module; From 8521fe5ce151a458815aeed3966b66b78e6751a7 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 14:49:01 -0400 Subject: [PATCH 11/30] sync internal/modules/package_json_reader with new naming convention --- .../node-internal-modules-cjs-loader-old.js | 2 +- ...e-internal-modules-package_json_reader.js} | 0 raw/download-and-compare.sh | 6 +++ ...nal-modules-package_json_reader-v15.3.0.js | 43 +++++++++++++++++++ 4 files changed, 50 insertions(+), 1 deletion(-) rename dist-raw/{node-package-json-reader.js => node-internal-modules-package_json_reader.js} (100%) create mode 100644 raw/node-internal-modules-package_json_reader-v15.3.0.js diff --git a/dist-raw/node-internal-modules-cjs-loader-old.js b/dist-raw/node-internal-modules-cjs-loader-old.js index 17b0247f4..f5600cda8 100644 --- a/dist-raw/node-internal-modules-cjs-loader-old.js +++ b/dist-raw/node-internal-modules-cjs-loader-old.js @@ -4,7 +4,7 @@ const {JSONParse} = require('./node-primordials'); const path = require('path'); -const packageJsonReader = require('./node-package-json-reader'); +const packageJsonReader = require('./node-internal-modules-package_json_reader'); const {normalizeSlashes} = require('../dist/util'); const {createErrRequireEsm} = require('./node-internal-errors'); diff --git a/dist-raw/node-package-json-reader.js b/dist-raw/node-internal-modules-package_json_reader.js similarity index 100% rename from dist-raw/node-package-json-reader.js rename to dist-raw/node-internal-modules-package_json_reader.js diff --git a/raw/download-and-compare.sh b/raw/download-and-compare.sh index 11e47ff2b..95156ca7a 100755 --- a/raw/download-and-compare.sh +++ b/raw/download-and-compare.sh @@ -58,3 +58,9 @@ download compare #### + +path=lib/internal/modules/package_json_reader +local=node-internal-modules-package_json_reader +version=v15.3.0 +download +compare diff --git a/raw/node-internal-modules-package_json_reader-v15.3.0.js b/raw/node-internal-modules-package_json_reader-v15.3.0.js new file mode 100644 index 000000000..51bd1a03e --- /dev/null +++ b/raw/node-internal-modules-package_json_reader-v15.3.0.js @@ -0,0 +1,43 @@ +// Copied from https://github.com/nodejs/node/blob/v15.3.0/lib/internal/modules/package_json_reader.js + +'use strict'; + +const { SafeMap } = primordials; +const { internalModuleReadJSON } = internalBinding('fs'); +const { pathToFileURL } = require('url'); +const { toNamespacedPath } = require('path'); + +const cache = new SafeMap(); + +let manifest; + +/** + * + * @param {string} jsonPath + */ +function read(jsonPath) { + if (cache.has(jsonPath)) { + return cache.get(jsonPath); + } + + const [string, containsKeys] = internalModuleReadJSON( + toNamespacedPath(jsonPath) + ); + const result = { string, containsKeys }; + const { getOptionValue } = require('internal/options'); + if (string !== undefined) { + if (manifest === undefined) { + manifest = getOptionValue('--experimental-policy') ? + require('internal/process/policy').manifest : + null; + } + if (manifest !== null) { + const jsonURL = pathToFileURL(jsonPath); + manifest.assertIntegrity(jsonURL, string); + } + } + cache.set(jsonPath, result); + return result; +} + +module.exports = { read }; From 94b5de87289c6225ab20c6b1b2e212730e853aeb Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 15:09:29 -0400 Subject: [PATCH 12/30] sync node-internal-modules-cjs-loader; rename back to old name --- .../node-internal-modules-cjs-loader-new.js | 862 ++++++++++++++++++ .../node-internal-modules-cjs-loader-old.js | 91 -- dist-raw/node-internal-modules-cjs-loader.js | 831 +---------------- raw/download-and-compare.sh | 28 +- 4 files changed, 915 insertions(+), 897 deletions(-) create mode 100644 dist-raw/node-internal-modules-cjs-loader-new.js delete mode 100644 dist-raw/node-internal-modules-cjs-loader-old.js diff --git a/dist-raw/node-internal-modules-cjs-loader-new.js b/dist-raw/node-internal-modules-cjs-loader-new.js new file mode 100644 index 000000000..216ad5906 --- /dev/null +++ b/dist-raw/node-internal-modules-cjs-loader-new.js @@ -0,0 +1,862 @@ +// 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; +} + +const _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; +} diff --git a/dist-raw/node-internal-modules-cjs-loader-old.js b/dist-raw/node-internal-modules-cjs-loader-old.js deleted file mode 100644 index f5600cda8..000000000 --- a/dist-raw/node-internal-modules-cjs-loader-old.js +++ /dev/null @@ -1,91 +0,0 @@ -// Copied from several files in node's source code. -// https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/modules/cjs/loader.js -// Each function and variable below must have a comment linking to the source in node's github repo. - -const {JSONParse} = require('./node-primordials'); -const path = require('path'); -const packageJsonReader = require('./node-internal-modules-package_json_reader'); -const {normalizeSlashes} = require('../dist/util'); -const {createErrRequireEsm} = require('./node-internal-errors'); - - - -// Copied from https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/modules/cjs/loader.js#L249 -const packageJsonCache = new Map(); - -// Copied from https://github.com/nodejs/node/blob/v15.3.0/lib/internal/modules/cjs/loader.js#L275-L304 -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; - } -} - -// Copied from https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/modules/cjs/loader.js#L285-L301 -function readPackageScope(checkPath) { - const rootSeparatorIndex = checkPath.indexOf(path.sep); - let separatorIndex; - while ( - (separatorIndex = checkPath.lastIndexOf(path.sep)) > rootSeparatorIndex - ) { - checkPath = checkPath.slice(0, separatorIndex); - if (checkPath.endsWith(path.sep + 'node_modules')) - return false; - const pjson = readPackage(checkPath); - if (pjson) return { - path: checkPath, - data: pjson - }; - } - return false; -} - -/** - * copied from Module._extensions['.js'] - * https://github.com/nodejs/node/blob/v15.3.0/lib/internal/modules/cjs/loader.js#L1113-L1120 - * @param {import('../src/index').Service} service - * @param {NodeJS.Module} module - * @param {string} filename - */ -function assertScriptCanLoadAsCJSImpl(service, module, filename) { - const pkg = readPackageScope(filename); - - // ts-node modification: allow our configuration to override - const tsNodeClassification = service.moduleTypeClassifier.classifyModule(normalizeSlashes(filename)); - if(tsNodeClassification.moduleType === 'cjs') return; - - // Function require shouldn't be used in ES modules. - if (tsNodeClassification.moduleType === 'esm' || (pkg && pkg.data && pkg.data.type === 'module')) { - const parentPath = module.parent && module.parent.filename; - const packageJsonPath = pkg ? path.resolve(pkg.path, 'package.json') : null; - throw createErrRequireEsm(filename, parentPath, packageJsonPath); - } -} - -module.exports = { - assertScriptCanLoadAsCJSImpl -}; diff --git a/dist-raw/node-internal-modules-cjs-loader.js b/dist-raw/node-internal-modules-cjs-loader.js index 8aa5202f6..e51a0eceb 100644 --- a/dist-raw/node-internal-modules-cjs-loader.js +++ b/dist-raw/node-internal-modules-cjs-loader.js @@ -1,292 +1,27 @@ -// 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'; +// Copied from several files in node's source code. +// https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/modules/cjs/loader.js +// Each function and variable below must have a comment linking to the source in node's github repo. 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'); +} = require('./node-primordials'); 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); - }, +const packageJsonReader = require('./node-internal-modules-package_json_reader'); +const {normalizeSlashes} = require('../dist/util'); +const {createErrRequireEsm} = require('./node-internal-errors'); - 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. +// Copied from https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/modules/cjs/loader.js#L249 const packageJsonCache = new SafeMap(); +// Copied from https://github.com/nodejs/node/blob/v15.3.0/lib/internal/modules/cjs/loader.js#L275-L304 function readPackage(requestPath) { const jsonPath = path.resolve(requestPath, 'package.json'); @@ -318,545 +53,47 @@ function readPackage(requestPath) { } } +// Copied from https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/modules/cjs/loader.js#L285-L301 function readPackageScope(checkPath) { const rootSeparatorIndex = StringPrototypeIndexOf(checkPath, sep); let separatorIndex; - do { - separatorIndex = StringPrototypeLastIndexOf(checkPath, sep); + while ( + (separatorIndex = StringPrototypeLastIndexOf(checkPath, sep)) > rootSeparatorIndex + ) { checkPath = StringPrototypeSlice(checkPath, 0, separatorIndex); if (StringPrototypeEndsWith(checkPath, sep + 'node_modules')) return false; - const pjson = readPackage(checkPath + sep); + const pjson = readPackage(checkPath); if (pjson) return { - data: pjson, path: checkPath, + data: pjson }; - } 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); - } +/** + * copied from Module._extensions['.js'] + * https://github.com/nodejs/node/blob/v15.3.0/lib/internal/modules/cjs/loader.js#L1113-L1120 + * @param {import('../src/index').Service} service + * @param {NodeJS.Module} module + * @param {string} filename + */ +function assertScriptCanLoadAsCJSImpl(service, module, filename) { + const pkg = readPackageScope(filename); - 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 = ['.']; + // ts-node modification: allow our configuration to override + const tsNodeClassification = service.moduleTypeClassifier.classifyModule(normalizeSlashes(filename)); + if(tsNodeClassification.moduleType === 'cjs') return; - debug('looking for %j in %j', request, mainPaths); - return mainPaths; + // Function require shouldn't be used in ES modules. + if (tsNodeClassification.moduleType === 'esm' || (pkg && pkg.data && pkg.data.type === 'module')) { + const parentPath = module.parent && module.parent.filename; + const packageJsonPath = pkg ? path.resolve(pkg.path, 'package.json') : null; + throw createErrRequireEsm(filename, parentPath, packageJsonPath); } - - 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; -} - -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; +module.exports = { + assertScriptCanLoadAsCJSImpl }; - -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; -} diff --git a/raw/download-and-compare.sh b/raw/download-and-compare.sh index 95156ca7a..ccd6f954c 100755 --- a/raw/download-and-compare.sh +++ b/raw/download-and-compare.sh @@ -3,7 +3,6 @@ set -euo pipefail shopt -s inherit_errexit __dirname="$(CDPATH= cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$__dirname" -set -x # This script serves as helpful documentation for where these files came from. @@ -14,6 +13,9 @@ function download() { compare() { diff "$local-$version$ext" "../dist-raw/$local$ext" || true } +assertStrippedIsOnlyDeletions() { + [ "$( diff "$1.js" "$1-stripped.js" | grep -E '^>' )" = '' ] +} ext=.js @@ -23,15 +25,15 @@ path=lib/internal/modules/cjs/loader local=node-internal-modules-cjs-loader version=v17.0.1 download -compare +# compare version=v15.3.0 download -compare +# compare version=2d5d77306f6dff9110c1f77fefab25f973415770 download -compare +# compare #### @@ -39,11 +41,11 @@ path=lib/internal/modules/esm/resolve local=node-internal-modules-esm-resolve version=v13.12.0 download -compare +# compare version=v15.3.0 download -compare +# compare #### @@ -51,11 +53,11 @@ path=lib/internal/repl/await local=node-internal-repl-await version=v17.0.0 download -compare +# compare version=88799930794045795e8abac874730f9eba7e2300 download -compare +# compare #### @@ -63,4 +65,12 @@ path=lib/internal/modules/package_json_reader local=node-internal-modules-package_json_reader version=v15.3.0 download -compare +# compare + +#### + +# Verify that -stripped.js files have only deletions, no other changes +set -x + +assertStrippedIsOnlyDeletions node-internal-modules-cjs-loader-v15.3.0 +assertStrippedIsOnlyDeletions node-internal-modules-cjs-loader-v17.0.1 From 9c9f67a0a49a851ed99e645013ee92692c2880bd Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 15:11:34 -0400 Subject: [PATCH 13/30] sync --- .../node-internal-modules-cjs-loader-new.js | 862 ------------------ 1 file changed, 862 deletions(-) delete mode 100644 dist-raw/node-internal-modules-cjs-loader-new.js diff --git a/dist-raw/node-internal-modules-cjs-loader-new.js b/dist-raw/node-internal-modules-cjs-loader-new.js deleted file mode 100644 index 216ad5906..000000000 --- a/dist-raw/node-internal-modules-cjs-loader-new.js +++ /dev/null @@ -1,862 +0,0 @@ -// 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; -} - -const _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; -} From 2b2d56850e0c21f1c9d13462f2b05d7179b9bda4 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 15:26:47 -0400 Subject: [PATCH 14/30] more sync of filenames --- dist-raw/node-esm-resolve-implementation.js | 2 +- dist-raw/{node-repl-await.js => node-internal-repl-await.js} | 0 raw/download-and-compare.sh | 3 +++ src/bin.ts | 2 +- src/index.ts | 2 +- src/repl.ts | 2 +- 6 files changed, 7 insertions(+), 4 deletions(-) rename dist-raw/{node-repl-await.js => node-internal-repl-await.js} (100%) diff --git a/dist-raw/node-esm-resolve-implementation.js b/dist-raw/node-esm-resolve-implementation.js index 04ea84668..97543bf74 100644 --- a/dist-raw/node-esm-resolve-implementation.js +++ b/dist-raw/node-esm-resolve-implementation.js @@ -95,7 +95,7 @@ const { const CJSModule = Module; // const packageJsonReader = require('internal/modules/package_json_reader'); -const packageJsonReader = require('./node-package-json-reader'); +const packageJsonReader = require('./node-internal-modules-package_json_reader'); const userConditions = getOptionValue('--conditions'); const DEFAULT_CONDITIONS = ObjectFreeze(['node', 'import', ...userConditions]); const DEFAULT_CONDITIONS_SET = new SafeSet(DEFAULT_CONDITIONS); diff --git a/dist-raw/node-repl-await.js b/dist-raw/node-internal-repl-await.js similarity index 100% rename from dist-raw/node-repl-await.js rename to dist-raw/node-internal-repl-await.js diff --git a/raw/download-and-compare.sh b/raw/download-and-compare.sh index ccd6f954c..9835a5364 100755 --- a/raw/download-and-compare.sh +++ b/raw/download-and-compare.sh @@ -6,6 +6,9 @@ cd "$__dirname" # This script serves as helpful documentation for where these files came from. +# TODO augment this script to update esm-resolver-diff branch +# https://github.com/TypeStrong/ts-node/compare/esm-resolver-diff..main + 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" diff --git a/src/bin.ts b/src/bin.ts index 3e972dd97..3256649e6 100644 --- a/src/bin.ts +++ b/src/bin.ts @@ -27,7 +27,7 @@ import { DEFAULTS, } from './index'; import type { TSInternal } from './ts-compiler-types'; -import { addBuiltinLibsToObject } from '../dist-raw/node-cjs-helpers'; +import { addBuiltinLibsToObject } from '../dist-raw/node-internal-modules-cjs-helpers'; import { callInChild } from './child/spawn-child'; import { findAndReadConfig } from './configuration'; diff --git a/src/index.ts b/src/index.ts index 70dfe109b..3ed919360 100644 --- a/src/index.ts +++ b/src/index.ts @@ -96,7 +96,7 @@ let assertScriptCanLoadAsCJS: ( module: NodeJS.Module, filename: string ) => void = engineSupportsPackageTypeField - ? require('../dist-raw/node-cjs-loader-utils').assertScriptCanLoadAsCJSImpl + ? require('../dist-raw/node-internal-modules-cjs-loader').assertScriptCanLoadAsCJSImpl : () => { /* noop */ }; diff --git a/src/repl.ts b/src/repl.ts index ae48c7816..ac3579817 100644 --- a/src/repl.ts +++ b/src/repl.ts @@ -22,7 +22,7 @@ function getProcessTopLevelAwait() { if (_processTopLevelAwait === undefined) { ({ processTopLevelAwait: _processTopLevelAwait, - } = require('../dist-raw/node-repl-await')); + } = require('../dist-raw/node-internal-repl-await')); } return _processTopLevelAwait; } From ccc738165e7025a358d5a26b1a9dd941f7c68a19 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 15:44:26 -0400 Subject: [PATCH 15/30] sync node-internal-errors --- dist-raw/node-errors.js | 2 + dist-raw/node-internal-errors.js | 52 +- raw/download-and-compare.sh | 14 + ...6dff9110c1f77fefab25f973415770-stripped.js | 99 ++ ...d5d77306f6dff9110c1f77fefab25f973415770.js | 1454 +++++++++++++++++ ...009e5f567cc776daba8fbf665386a6-stripped.js | 44 + ...533fb3508009e5f567cc776daba8fbf665386a6.js | 1454 +++++++++++++++++ 7 files changed, 3094 insertions(+), 25 deletions(-) create mode 100644 raw/node-internal-errors-2d5d77306f6dff9110c1f77fefab25f973415770-stripped.js create mode 100644 raw/node-internal-errors-2d5d77306f6dff9110c1f77fefab25f973415770.js create mode 100644 raw/node-internal-errors-b533fb3508009e5f567cc776daba8fbf665386a6-stripped.js create mode 100644 raw/node-internal-errors-b533fb3508009e5f567cc776daba8fbf665386a6.js diff --git a/dist-raw/node-errors.js b/dist-raw/node-errors.js index 73255a7d2..e327734a8 100644 --- a/dist-raw/node-errors.js +++ b/dist-raw/node-errors.js @@ -1,3 +1,5 @@ +// TODO Sync this with node-internal-errors? + exports.codes = { ERR_INPUT_TYPE_NOT_ALLOWED: createErrorCtor(joinArgs('ERR_INPUT_TYPE_NOT_ALLOWED')), ERR_INVALID_ARG_VALUE: createErrorCtor(joinArgs('ERR_INVALID_ARG_VALUE')), diff --git a/dist-raw/node-internal-errors.js b/dist-raw/node-internal-errors.js index 75148b85c..c5fd0e4f4 100644 --- a/dist-raw/node-internal-errors.js +++ b/dist-raw/node-internal-errors.js @@ -1,3 +1,9 @@ +'use strict'; + +module.exports = { + createErrRequireEsm +}; + // 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: @@ -7,7 +13,7 @@ // 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)) + const err = new Error(getErrRequireEsmMessage(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 }]` @@ -20,31 +26,27 @@ function createErrRequireEsm(filename, parentPath, packageJsonPath) { }) 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; - } +// 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 getErrRequireEsmMessage(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; } - -module.exports = { - createErrRequireEsm -}; diff --git a/raw/download-and-compare.sh b/raw/download-and-compare.sh index 9835a5364..f35a0fd25 100755 --- a/raw/download-and-compare.sh +++ b/raw/download-and-compare.sh @@ -72,8 +72,22 @@ download #### +path=lib/internal/errors +local=node-internal-errors +version=2d5d77306f6dff9110c1f77fefab25f973415770 +download +# compare + +version=b533fb3508009e5f567cc776daba8fbf665386a6 +download +# compare + +#### + # Verify that -stripped.js files have only deletions, no other changes set -x assertStrippedIsOnlyDeletions node-internal-modules-cjs-loader-v15.3.0 assertStrippedIsOnlyDeletions node-internal-modules-cjs-loader-v17.0.1 +assertStrippedIsOnlyDeletions node-internal-errors-2d5d77306f6dff9110c1f77fefab25f973415770 +assertStrippedIsOnlyDeletions node-internal-errors-b533fb3508009e5f567cc776daba8fbf665386a6 diff --git a/raw/node-internal-errors-2d5d77306f6dff9110c1f77fefab25f973415770-stripped.js b/raw/node-internal-errors-2d5d77306f6dff9110c1f77fefab25f973415770-stripped.js new file mode 100644 index 000000000..d15b8483e --- /dev/null +++ b/raw/node-internal-errors-2d5d77306f6dff9110c1f77fefab25f973415770-stripped.js @@ -0,0 +1,99 @@ +// Copied from https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/errors.js + +'use strict'; + +const codes = {}; + +function makeNodeErrorWithCode(Base, key) { + return class NodeError extends Base { + constructor(...args) { + if (excludedStackFn === undefined) { + super(); + } else { + const limit = Error.stackTraceLimit; + Error.stackTraceLimit = 0; + super(); + // Reset the limit and setting the name property. + Error.stackTraceLimit = limit; + } + const message = getMessage(key, args, this); + ObjectDefineProperty(this, 'message', { + value: message, + enumerable: false, + writable: true, + configurable: true + }); + addCodeToName(this, super.name, key); + this.code = key; + } + + toString() { + return `${this.name} [${key}]: ${this.message}`; + } + }; +} + +// Utility function for registering the error codes. Only used here. Exported +// *only* to allow for testing. +function E(sym, val, def, ...otherClasses) { + // Special case for SystemError that formats the error message differently + // The SystemErrors only have SystemError as their base classes. + messages.set(sym, val); + if (def === SystemError) { + def = makeSystemErrorWithCode(sym); + } else { + def = makeNodeErrorWithCode(def, sym); + } + + if (otherClasses.length !== 0) { + otherClasses.forEach((clazz) => { + def[clazz.name] = makeNodeErrorWithCode(clazz, sym); + }); + } + codes[sym] = def; +} + +module.exports = { + addCodeToName, // Exported for NghttpError + codes, + dnsException, + errnoException, + exceptionWithHostPort, + getMessage, + hideStackFrames, + isStackOverflowError, + connResetException, + uvErrmapGet, + uvException, + uvExceptionWithHostPort, + SystemError, + // This is exported only to facilitate testing. + E, + kNoOverride, + prepareStackTrace, + maybeOverridePrepareStackTrace, + overrideStackTrace, + kEnhanceStackBeforeInspector, + fatalExceptionStackEnhancers +}; + +E('ERR_REQUIRE_ESM', + (filename, parentPath = null, packageJsonPath = null) => { + 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 .js file whose nearest parent ' + + 'package.json contains "type": "module" which defines all .js ' + + 'files in that package scope as ES modules.\nInstead rename ' + + `${basename} to end in .cjs, change the requiring code to use ` + + 'import(), or remove "type": "module" from ' + + `${packageJsonPath}.\n`; + return msg; + } + return msg; + }, Error); diff --git a/raw/node-internal-errors-2d5d77306f6dff9110c1f77fefab25f973415770.js b/raw/node-internal-errors-2d5d77306f6dff9110c1f77fefab25f973415770.js new file mode 100644 index 000000000..15eca36b3 --- /dev/null +++ b/raw/node-internal-errors-2d5d77306f6dff9110c1f77fefab25f973415770.js @@ -0,0 +1,1454 @@ +// Copied from https://github.com/nodejs/node/blob/2d5d77306f6dff9110c1f77fefab25f973415770/lib/internal/errors.js + +/* eslint node-core/documented-errors: "error" */ +/* eslint node-core/alphabetize-errors: "error" */ +/* eslint node-core/prefer-util-format-errors: "error" */ + +'use strict'; + +// The whole point behind this internal module is to allow Node.js to no +// longer be forced to treat every error message change as a semver-major +// change. The NodeError classes here all expose a `code` property whose +// value statically and permanently identifies the error. While the error +// message may change, the code should not. + +const { + ArrayIsArray, + Error, + JSONStringify, + Map, + MathAbs, + NumberIsInteger, + ObjectDefineProperty, + ObjectKeys, + StringPrototypeSlice, + StringPrototypeStartsWith, + Symbol, + SymbolFor, + WeakMap, +} = primordials; + +const sep = process.platform === 'win32' ? '\\' : '/'; + +const messages = new Map(); +const codes = {}; + +const classRegExp = /^([A-Z][a-z0-9]*)+$/; +// Sorted by a rough estimate on most frequently used entries. +const kTypes = [ + 'string', + 'function', + 'number', + 'object', + // Accept 'Function' and 'Object' as alternative to the lower cased version. + 'Function', + 'Object', + 'boolean', + 'bigint', + 'symbol' +]; + +const { kMaxLength } = internalBinding('buffer'); + +const MainContextError = Error; +const ErrorToString = Error.prototype.toString; +const overrideStackTrace = new WeakMap(); +const kNoOverride = Symbol('kNoOverride'); +const prepareStackTrace = (globalThis, error, trace) => { + // API for node internals to override error stack formatting + // without interfering with userland code. + if (overrideStackTrace.has(error)) { + const f = overrideStackTrace.get(error); + overrideStackTrace.delete(error); + return f(error, trace); + } + + const globalOverride = + maybeOverridePrepareStackTrace(globalThis, error, trace); + if (globalOverride !== kNoOverride) return globalOverride; + + // Normal error formatting: + // + // Error: Message + // at function (file) + // at file + const errorString = ErrorToString.call(error); + if (trace.length === 0) { + return errorString; + } + return `${errorString}\n at ${trace.join('\n at ')}`; +}; + +const maybeOverridePrepareStackTrace = (globalThis, error, trace) => { + // Polyfill of V8's Error.prepareStackTrace API. + // https://crbug.com/v8/7848 + // `globalThis` is the global that contains the constructor which + // created `error`. + if (typeof globalThis.Error.prepareStackTrace === 'function') { + return globalThis.Error.prepareStackTrace(error, trace); + } + // We still have legacy usage that depends on the main context's `Error` + // being used, even when the error is from a different context. + // TODO(devsnek): evaluate if this can be eventually deprecated/removed. + if (typeof MainContextError.prepareStackTrace === 'function') { + return MainContextError.prepareStackTrace(error, trace); + } + + return kNoOverride; +}; + +let excludedStackFn; + +// Lazily loaded +let util; +let assert; + +let internalUtil = null; +function lazyInternalUtil() { + if (!internalUtil) { + internalUtil = require('internal/util'); + } + return internalUtil; +} + +let internalUtilInspect = null; +function lazyInternalUtilInspect() { + if (!internalUtilInspect) { + internalUtilInspect = require('internal/util/inspect'); + } + return internalUtilInspect; +} + +let buffer; +function lazyBuffer() { + if (buffer === undefined) + buffer = require('buffer').Buffer; + return buffer; +} + +// A specialized Error that includes an additional info property with +// additional information about the error condition. +// It has the properties present in a UVException but with a custom error +// message followed by the uv error code and uv error message. +// It also has its own error code with the original uv error context put into +// `err.info`. +// The context passed into this error must have .code, .syscall and .message, +// and may have .path and .dest. +class SystemError extends Error { + constructor(key, context) { + if (excludedStackFn === undefined) { + super(); + } else { + const limit = Error.stackTraceLimit; + Error.stackTraceLimit = 0; + super(); + // Reset the limit and setting the name property. + Error.stackTraceLimit = limit; + } + const prefix = getMessage(key, [], this); + let message = `${prefix}: ${context.syscall} returned ` + + `${context.code} (${context.message})`; + + if (context.path !== undefined) + message += ` ${context.path}`; + if (context.dest !== undefined) + message += ` => ${context.dest}`; + + ObjectDefineProperty(this, 'message', { + value: message, + enumerable: false, + writable: true, + configurable: true + }); + addCodeToName(this, 'SystemError', key); + + this.code = key; + + ObjectDefineProperty(this, 'info', { + value: context, + enumerable: true, + configurable: true, + writable: false + }); + + ObjectDefineProperty(this, 'errno', { + get() { + return context.errno; + }, + set: (value) => { + context.errno = value; + }, + enumerable: true, + configurable: true + }); + + ObjectDefineProperty(this, 'syscall', { + get() { + return context.syscall; + }, + set: (value) => { + context.syscall = value; + }, + enumerable: true, + configurable: true + }); + + if (context.path !== undefined) { + // TODO(BridgeAR): Investigate why and when the `.toString()` was + // introduced. The `path` and `dest` properties in the context seem to + // always be of type string. We should probably just remove the + // `.toString()` and `Buffer.from()` operations and set the value on the + // context as the user did. + ObjectDefineProperty(this, 'path', { + get() { + return context.path != null ? + context.path.toString() : context.path; + }, + set: (value) => { + context.path = value ? + lazyBuffer().from(value.toString()) : undefined; + }, + enumerable: true, + configurable: true + }); + } + + if (context.dest !== undefined) { + ObjectDefineProperty(this, 'dest', { + get() { + return context.dest != null ? + context.dest.toString() : context.dest; + }, + set: (value) => { + context.dest = value ? + lazyBuffer().from(value.toString()) : undefined; + }, + enumerable: true, + configurable: true + }); + } + } + + toString() { + return `${this.name} [${this.code}]: ${this.message}`; + } + + [SymbolFor('nodejs.util.inspect.custom')](recurseTimes, ctx) { + return lazyInternalUtilInspect().inspect(this, { + ...ctx, + getters: true, + customInspect: false + }); + } +} + +function makeSystemErrorWithCode(key) { + return class NodeError extends SystemError { + constructor(ctx) { + super(key, ctx); + } + }; +} + +function makeNodeErrorWithCode(Base, key) { + return class NodeError extends Base { + constructor(...args) { + if (excludedStackFn === undefined) { + super(); + } else { + const limit = Error.stackTraceLimit; + Error.stackTraceLimit = 0; + super(); + // Reset the limit and setting the name property. + Error.stackTraceLimit = limit; + } + const message = getMessage(key, args, this); + ObjectDefineProperty(this, 'message', { + value: message, + enumerable: false, + writable: true, + configurable: true + }); + addCodeToName(this, super.name, key); + this.code = key; + } + + toString() { + return `${this.name} [${key}]: ${this.message}`; + } + }; +} + +// This function removes unnecessary frames from Node.js core errors. +function hideStackFrames(fn) { + return function hidden(...args) { + // Make sure the most outer `hideStackFrames()` function is used. + let setStackFn = false; + if (excludedStackFn === undefined) { + excludedStackFn = hidden; + setStackFn = true; + } + try { + return fn(...args); + } finally { + if (setStackFn === true) { + excludedStackFn = undefined; + } + } + }; +} + +function addCodeToName(err, name, code) { + // Set the stack + if (excludedStackFn !== undefined) { + // eslint-disable-next-line no-restricted-syntax + Error.captureStackTrace(err, excludedStackFn); + } + // Add the error code to the name to include it in the stack trace. + err.name = `${name} [${code}]`; + // Access the stack to generate the error message including the error code + // from the name. + err.stack; + // Reset the name to the actual name. + if (name === 'SystemError') { + ObjectDefineProperty(err, 'name', { + value: name, + enumerable: false, + writable: true, + configurable: true + }); + } else { + delete err.name; + } +} + +// Utility function for registering the error codes. Only used here. Exported +// *only* to allow for testing. +function E(sym, val, def, ...otherClasses) { + // Special case for SystemError that formats the error message differently + // The SystemErrors only have SystemError as their base classes. + messages.set(sym, val); + if (def === SystemError) { + def = makeSystemErrorWithCode(sym); + } else { + def = makeNodeErrorWithCode(def, sym); + } + + if (otherClasses.length !== 0) { + otherClasses.forEach((clazz) => { + def[clazz.name] = makeNodeErrorWithCode(clazz, sym); + }); + } + codes[sym] = def; +} + +function getMessage(key, args, self) { + const msg = messages.get(key); + + if (assert === undefined) assert = require('internal/assert'); + + if (typeof msg === 'function') { + assert( + msg.length <= args.length, // Default options do not count. + `Code: ${key}; The provided arguments length (${args.length}) does not ` + + `match the required ones (${msg.length}).` + ); + return msg.apply(self, args); + } + + const expectedLength = (msg.match(/%[dfijoOs]/g) || []).length; + assert( + expectedLength === args.length, + `Code: ${key}; The provided arguments length (${args.length}) does not ` + + `match the required ones (${expectedLength}).` + ); + if (args.length === 0) + return msg; + + args.unshift(msg); + return lazyInternalUtilInspect().format.apply(null, args); +} + +let uvBinding; + +function lazyUv() { + if (!uvBinding) { + uvBinding = internalBinding('uv'); + } + return uvBinding; +} + +const uvUnmappedError = ['UNKNOWN', 'unknown error']; + +function uvErrmapGet(name) { + uvBinding = lazyUv(); + if (!uvBinding.errmap) { + uvBinding.errmap = uvBinding.getErrorMap(); + } + return uvBinding.errmap.get(name); +} + + +/** + * This creates an error compatible with errors produced in the C++ + * function UVException using a context object with data assembled in C++. + * The goal is to migrate them to ERR_* errors later when compatibility is + * not a concern. + * + * @param {Object} ctx + * @returns {Error} + */ +function uvException(ctx) { + const [ code, uvmsg ] = uvErrmapGet(ctx.errno) || uvUnmappedError; + let message = `${code}: ${ctx.message || uvmsg}, ${ctx.syscall}`; + + let path; + let dest; + if (ctx.path) { + path = ctx.path.toString(); + message += ` '${path}'`; + } + if (ctx.dest) { + dest = ctx.dest.toString(); + message += ` -> '${dest}'`; + } + + // Reducing the limit improves the performance significantly. We do not loose + // the stack frames due to the `captureStackTrace()` function that is called + // later. + const tmpLimit = Error.stackTraceLimit; + Error.stackTraceLimit = 0; + // Pass the message to the constructor instead of setting it on the object + // to make sure it is the same as the one created in C++ + // eslint-disable-next-line no-restricted-syntax + const err = new Error(message); + Error.stackTraceLimit = tmpLimit; + + for (const prop of ObjectKeys(ctx)) { + if (prop === 'message' || prop === 'path' || prop === 'dest') { + continue; + } + err[prop] = ctx[prop]; + } + + err.code = code; + if (path) { + err.path = path; + } + if (dest) { + err.dest = dest; + } + + // eslint-disable-next-line no-restricted-syntax + Error.captureStackTrace(err, excludedStackFn || uvException); + return err; +} + +/** + * This creates an error compatible with errors produced in the C++ + * This function should replace the deprecated + * `exceptionWithHostPort()` function. + * + * @param {number} err - A libuv error number + * @param {string} syscall + * @param {string} address + * @param {number} [port] + * @returns {Error} + */ +function uvExceptionWithHostPort(err, syscall, address, port) { + const [ code, uvmsg ] = uvErrmapGet(err) || uvUnmappedError; + const message = `${syscall} ${code}: ${uvmsg}`; + let details = ''; + + if (port && port > 0) { + details = ` ${address}:${port}`; + } else if (address) { + details = ` ${address}`; + } + + // Reducing the limit improves the performance significantly. We do not loose + // the stack frames due to the `captureStackTrace()` function that is called + // later. + const tmpLimit = Error.stackTraceLimit; + Error.stackTraceLimit = 0; + // eslint-disable-next-line no-restricted-syntax + const ex = new Error(`${message}${details}`); + Error.stackTraceLimit = tmpLimit; + ex.code = code; + ex.errno = err; + ex.syscall = syscall; + ex.address = address; + if (port) { + ex.port = port; + } + + // eslint-disable-next-line no-restricted-syntax + Error.captureStackTrace(ex, excludedStackFn || uvExceptionWithHostPort); + return ex; +} + +/** + * This used to be util._errnoException(). + * + * @param {number} err - A libuv error number + * @param {string} syscall + * @param {string} [original] + * @returns {Error} + */ +function errnoException(err, syscall, original) { + // TODO(joyeecheung): We have to use the type-checked + // getSystemErrorName(err) to guard against invalid arguments from users. + // This can be replaced with [ code ] = errmap.get(err) when this method + // is no longer exposed to user land. + if (util === undefined) util = require('util'); + const code = util.getSystemErrorName(err); + const message = original ? + `${syscall} ${code} ${original}` : `${syscall} ${code}`; + + // eslint-disable-next-line no-restricted-syntax + const ex = new Error(message); + ex.errno = err; + ex.code = code; + ex.syscall = syscall; + + // eslint-disable-next-line no-restricted-syntax + Error.captureStackTrace(ex, excludedStackFn || errnoException); + return ex; +} + +/** + * Deprecated, new function is `uvExceptionWithHostPort()` + * New function added the error description directly + * from C++. this method for backwards compatibility + * @param {number} err - A libuv error number + * @param {string} syscall + * @param {string} address + * @param {number} [port] + * @param {string} [additional] + * @returns {Error} + */ +function exceptionWithHostPort(err, syscall, address, port, additional) { + // TODO(joyeecheung): We have to use the type-checked + // getSystemErrorName(err) to guard against invalid arguments from users. + // This can be replaced with [ code ] = errmap.get(err) when this method + // is no longer exposed to user land. + if (util === undefined) util = require('util'); + const code = util.getSystemErrorName(err); + let details = ''; + if (port && port > 0) { + details = ` ${address}:${port}`; + } else if (address) { + details = ` ${address}`; + } + if (additional) { + details += ` - Local (${additional})`; + } + + // Reducing the limit improves the performance significantly. We do not loose + // the stack frames due to the `captureStackTrace()` function that is called + // later. + const tmpLimit = Error.stackTraceLimit; + Error.stackTraceLimit = 0; + // eslint-disable-next-line no-restricted-syntax + const ex = new Error(`${syscall} ${code}${details}`); + Error.stackTraceLimit = tmpLimit; + ex.errno = err; + ex.code = code; + ex.syscall = syscall; + ex.address = address; + if (port) { + ex.port = port; + } + + // eslint-disable-next-line no-restricted-syntax + Error.captureStackTrace(ex, excludedStackFn || exceptionWithHostPort); + return ex; +} + +/** + * @param {number|string} code - A libuv error number or a c-ares error code + * @param {string} syscall + * @param {string} [hostname] + * @returns {Error} + */ +function dnsException(code, syscall, hostname) { + let errno; + // If `code` is of type number, it is a libuv error number, else it is a + // c-ares error code. + // TODO(joyeecheung): translate c-ares error codes into numeric ones and + // make them available in a property that's not error.errno (since they + // can be in conflict with libuv error codes). Also make sure + // util.getSystemErrorName() can understand them when an being informed that + // the number is a c-ares error code. + if (typeof code === 'number') { + errno = code; + // ENOTFOUND is not a proper POSIX error, but this error has been in place + // long enough that it's not practical to remove it. + if (code === lazyUv().UV_EAI_NODATA || code === lazyUv().UV_EAI_NONAME) { + code = 'ENOTFOUND'; // Fabricated error name. + } else { + code = lazyInternalUtil().getSystemErrorName(code); + } + } + const message = `${syscall} ${code}${hostname ? ` ${hostname}` : ''}`; + // Reducing the limit improves the performance significantly. We do not loose + // the stack frames due to the `captureStackTrace()` function that is called + // later. + const tmpLimit = Error.stackTraceLimit; + Error.stackTraceLimit = 0; + // eslint-disable-next-line no-restricted-syntax + const ex = new Error(message); + Error.stackTraceLimit = tmpLimit; + ex.errno = errno; + ex.code = code; + ex.syscall = syscall; + if (hostname) { + ex.hostname = hostname; + } + + // eslint-disable-next-line no-restricted-syntax + Error.captureStackTrace(ex, excludedStackFn || dnsException); + return ex; +} + +function connResetException(msg) { + // eslint-disable-next-line no-restricted-syntax + const ex = new Error(msg); + ex.code = 'ECONNRESET'; + return ex; +} + +let maxStack_ErrorName; +let maxStack_ErrorMessage; +/** + * Returns true if `err.name` and `err.message` are equal to engine-specific + * values indicating max call stack size has been exceeded. + * "Maximum call stack size exceeded" in V8. + * + * @param {Error} err + * @returns {boolean} + */ +function isStackOverflowError(err) { + if (maxStack_ErrorMessage === undefined) { + try { + function overflowStack() { overflowStack(); } + overflowStack(); + } catch (err) { + maxStack_ErrorMessage = err.message; + maxStack_ErrorName = err.name; + } + } + + return err && err.name === maxStack_ErrorName && + err.message === maxStack_ErrorMessage; +} + +// Only use this for integers! Decimal numbers do not work with this function. +function addNumericalSeparator(val) { + let res = ''; + let i = val.length; + const start = val[0] === '-' ? 1 : 0; + for (; i >= start + 4; i -= 3) { + res = `_${val.slice(i - 3, i)}${res}`; + } + return `${val.slice(0, i)}${res}`; +} + +// Used to enhance the stack that will be picked up by the inspector +const kEnhanceStackBeforeInspector = Symbol('kEnhanceStackBeforeInspector'); + +// These are supposed to be called only on fatal exceptions before +// the process exits. +const fatalExceptionStackEnhancers = { + beforeInspector(error) { + if (typeof error[kEnhanceStackBeforeInspector] !== 'function') { + return error.stack; + } + + try { + // Set the error.stack here so it gets picked up by the + // inspector. + error.stack = error[kEnhanceStackBeforeInspector](); + } catch { + // We are just enhancing the error. If it fails, ignore it. + } + return error.stack; + }, + afterInspector(error) { + const originalStack = error.stack; + const { + inspect, + inspectDefaultOptions: { + colors: defaultColors + } + } = lazyInternalUtilInspect(); + const colors = (internalBinding('util').guessHandleType(2) === 'TTY' && + require('internal/tty').hasColors()) || + defaultColors; + try { + return inspect(error, { colors }); + } catch { + return originalStack; + } + } +}; + +module.exports = { + addCodeToName, // Exported for NghttpError + codes, + dnsException, + errnoException, + exceptionWithHostPort, + getMessage, + hideStackFrames, + isStackOverflowError, + connResetException, + uvErrmapGet, + uvException, + uvExceptionWithHostPort, + SystemError, + // This is exported only to facilitate testing. + E, + kNoOverride, + prepareStackTrace, + maybeOverridePrepareStackTrace, + overrideStackTrace, + kEnhanceStackBeforeInspector, + fatalExceptionStackEnhancers +}; + +// To declare an error message, use the E(sym, val, def) function above. The sym +// must be an upper case string. The val can be either a function or a string. +// The def must be an error class. +// The return value of the function must be a string. +// Examples: +// E('EXAMPLE_KEY1', 'This is the error value', Error); +// E('EXAMPLE_KEY2', (a, b) => return `${a} ${b}`, RangeError); +// +// Once an error code has been assigned, the code itself MUST NOT change and +// any given error code must never be reused to identify a different error. +// +// Any error code added here should also be added to the documentation +// +// Note: Please try to keep these in alphabetical order +// +// Note: Node.js specific errors must begin with the prefix ERR_ +E('ERR_AMBIGUOUS_ARGUMENT', 'The "%s" argument is ambiguous. %s', TypeError); +E('ERR_ARG_NOT_ITERABLE', '%s must be iterable', TypeError); +E('ERR_ASSERTION', '%s', Error); +E('ERR_ASYNC_CALLBACK', '%s must be a function', TypeError); +E('ERR_ASYNC_TYPE', 'Invalid name for async "type": %s', TypeError); +E('ERR_BROTLI_INVALID_PARAM', '%s is not a valid Brotli parameter', RangeError); +E('ERR_BUFFER_OUT_OF_BOUNDS', + // Using a default argument here is important so the argument is not counted + // towards `Function#length`. + (name = undefined) => { + if (name) { + return `"${name}" is outside of buffer bounds`; + } + return 'Attempt to access memory outside buffer bounds'; + }, RangeError); +E('ERR_BUFFER_TOO_LARGE', + `Cannot create a Buffer larger than 0x${kMaxLength.toString(16)} bytes`, + RangeError); +E('ERR_CANNOT_WATCH_SIGINT', 'Cannot watch for SIGINT signals', Error); +E('ERR_CHILD_CLOSED_BEFORE_REPLY', + 'Child closed before reply received', Error); +E('ERR_CHILD_PROCESS_IPC_REQUIRED', + "Forked processes must have an IPC channel, missing value 'ipc' in %s", + Error); +E('ERR_CHILD_PROCESS_STDIO_MAXBUFFER', '%s maxBuffer length exceeded', + RangeError); +E('ERR_CONSOLE_WRITABLE_STREAM', + 'Console expects a writable stream instance for %s', TypeError); +E('ERR_CONTEXT_NOT_INITIALIZED', 'context used is not initialized', Error); +E('ERR_CPU_USAGE', 'Unable to obtain cpu usage %s', Error); +E('ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED', + 'Custom engines not supported by this OpenSSL', Error); +E('ERR_CRYPTO_ECDH_INVALID_FORMAT', 'Invalid ECDH format: %s', TypeError); +E('ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY', + 'Public key is not valid for specified curve', Error); +E('ERR_CRYPTO_ENGINE_UNKNOWN', 'Engine "%s" was not found', Error); +E('ERR_CRYPTO_FIPS_FORCED', + 'Cannot set FIPS mode, it was forced with --force-fips at startup.', Error); +E('ERR_CRYPTO_FIPS_UNAVAILABLE', 'Cannot set FIPS mode in a non-FIPS build.', + Error); +E('ERR_CRYPTO_HASH_FINALIZED', 'Digest already called', Error); +E('ERR_CRYPTO_HASH_UPDATE_FAILED', 'Hash update failed', Error); +E('ERR_CRYPTO_INCOMPATIBLE_KEY', 'Incompatible %s: %s', Error); +E('ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS', 'The selected key encoding %s %s.', + Error); +E('ERR_CRYPTO_INVALID_DIGEST', 'Invalid digest: %s', TypeError); +E('ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE', + 'Invalid key object type %s, expected %s.', TypeError); +E('ERR_CRYPTO_INVALID_STATE', 'Invalid state for operation %s', Error); +E('ERR_CRYPTO_PBKDF2_ERROR', 'PBKDF2 error', Error); +E('ERR_CRYPTO_SCRYPT_INVALID_PARAMETER', 'Invalid scrypt parameter', Error); +E('ERR_CRYPTO_SCRYPT_NOT_SUPPORTED', 'Scrypt algorithm not supported', Error); +// Switch to TypeError. The current implementation does not seem right. +E('ERR_CRYPTO_SIGN_KEY_REQUIRED', 'No key provided to sign', Error); +E('ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH', + 'Input buffers must have the same byte length', RangeError); +E('ERR_DIR_CLOSED', 'Directory handle was closed', Error); +E('ERR_DNS_SET_SERVERS_FAILED', 'c-ares failed to set servers: "%s" [%s]', + Error); +E('ERR_DOMAIN_CALLBACK_NOT_AVAILABLE', + 'A callback was registered through ' + + 'process.setUncaughtExceptionCaptureCallback(), which is mutually ' + + 'exclusive with using the `domain` module', + Error); +E('ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE', + 'The `domain` module is in use, which is mutually exclusive with calling ' + + 'process.setUncaughtExceptionCaptureCallback()', + Error); +E('ERR_ENCODING_INVALID_ENCODED_DATA', function(encoding, ret) { + this.errno = ret; + return `The encoded data was not valid for encoding ${encoding}`; +}, TypeError); +E('ERR_ENCODING_NOT_SUPPORTED', 'The "%s" encoding is not supported', + RangeError); +E('ERR_EVAL_ESM_CANNOT_PRINT', '--print cannot be used with ESM input', Error); +E('ERR_FALSY_VALUE_REJECTION', function(reason) { + this.reason = reason; + return 'Promise was rejected with falsy value'; +}, Error); +E('ERR_FEATURE_UNAVAILABLE_ON_PLATFORM', + 'The feature %s is unavailable on the current platform' + + ', which is being used to run Node.js', + TypeError); +E('ERR_FS_FILE_TOO_LARGE', 'File size (%s) is greater than 2 GB', RangeError); +E('ERR_FS_INVALID_SYMLINK_TYPE', + 'Symlink type must be one of "dir", "file", or "junction". Received "%s"', + Error); // Switch to TypeError. The current implementation does not seem right +E('ERR_HTTP2_ALTSVC_INVALID_ORIGIN', + 'HTTP/2 ALTSVC frames require a valid origin', TypeError); +E('ERR_HTTP2_ALTSVC_LENGTH', + 'HTTP/2 ALTSVC frames are limited to 16382 bytes', TypeError); +E('ERR_HTTP2_CONNECT_AUTHORITY', + ':authority header is required for CONNECT requests', Error); +E('ERR_HTTP2_CONNECT_PATH', + 'The :path header is forbidden for CONNECT requests', Error); +E('ERR_HTTP2_CONNECT_SCHEME', + 'The :scheme header is forbidden for CONNECT requests', Error); +E('ERR_HTTP2_GOAWAY_SESSION', + 'New streams cannot be created after receiving a GOAWAY', Error); +E('ERR_HTTP2_HEADERS_AFTER_RESPOND', + 'Cannot specify additional headers after response initiated', Error); +E('ERR_HTTP2_HEADERS_SENT', 'Response has already been initiated.', Error); +E('ERR_HTTP2_HEADER_SINGLE_VALUE', + 'Header field "%s" must only have a single value', TypeError); +E('ERR_HTTP2_INFO_STATUS_NOT_ALLOWED', + 'Informational status codes cannot be used', RangeError); +E('ERR_HTTP2_INVALID_CONNECTION_HEADERS', + 'HTTP/1 Connection specific headers are forbidden: "%s"', TypeError); +E('ERR_HTTP2_INVALID_HEADER_VALUE', + 'Invalid value "%s" for header "%s"', TypeError); +E('ERR_HTTP2_INVALID_INFO_STATUS', + 'Invalid informational status code: %s', RangeError); +E('ERR_HTTP2_INVALID_ORIGIN', + 'HTTP/2 ORIGIN frames require a valid origin', TypeError); +E('ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH', + 'Packed settings length must be a multiple of six', RangeError); +E('ERR_HTTP2_INVALID_PSEUDOHEADER', + '"%s" is an invalid pseudoheader or is used incorrectly', TypeError); +E('ERR_HTTP2_INVALID_SESSION', 'The session has been destroyed', Error); +E('ERR_HTTP2_INVALID_SETTING_VALUE', + // Using default arguments here is important so the arguments are not counted + // towards `Function#length`. + function(name, actual, min = undefined, max = undefined) { + this.actual = actual; + if (min !== undefined) { + this.min = min; + this.max = max; + } + return `Invalid value for setting "${name}": ${actual}`; + }, TypeError, RangeError); +E('ERR_HTTP2_INVALID_STREAM', 'The stream has been destroyed', Error); +E('ERR_HTTP2_MAX_PENDING_SETTINGS_ACK', + 'Maximum number of pending settings acknowledgements', Error); +E('ERR_HTTP2_NESTED_PUSH', + 'A push stream cannot initiate another push stream.', Error); +E('ERR_HTTP2_NO_SOCKET_MANIPULATION', + 'HTTP/2 sockets should not be directly manipulated (e.g. read and written)', + Error); +E('ERR_HTTP2_ORIGIN_LENGTH', + 'HTTP/2 ORIGIN frames are limited to 16382 bytes', TypeError); +E('ERR_HTTP2_OUT_OF_STREAMS', + 'No stream ID is available because maximum stream ID has been reached', + Error); +E('ERR_HTTP2_PAYLOAD_FORBIDDEN', + 'Responses with %s status must not have a payload', Error); +E('ERR_HTTP2_PING_CANCEL', 'HTTP2 ping cancelled', Error); +E('ERR_HTTP2_PING_LENGTH', 'HTTP2 ping payload must be 8 bytes', RangeError); +E('ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED', + 'Cannot set HTTP/2 pseudo-headers', TypeError); +E('ERR_HTTP2_PUSH_DISABLED', 'HTTP/2 client has disabled push streams', Error); +E('ERR_HTTP2_SEND_FILE', 'Directories cannot be sent', Error); +E('ERR_HTTP2_SEND_FILE_NOSEEK', + 'Offset or length can only be specified for regular files', Error); +E('ERR_HTTP2_SESSION_ERROR', 'Session closed with error code %s', Error); +E('ERR_HTTP2_SETTINGS_CANCEL', 'HTTP2 session settings canceled', Error); +E('ERR_HTTP2_SOCKET_BOUND', + 'The socket is already bound to an Http2Session', Error); +E('ERR_HTTP2_SOCKET_UNBOUND', + 'The socket has been disconnected from the Http2Session', Error); +E('ERR_HTTP2_STATUS_101', + 'HTTP status code 101 (Switching Protocols) is forbidden in HTTP/2', Error); +E('ERR_HTTP2_STATUS_INVALID', 'Invalid status code: %s', RangeError); +E('ERR_HTTP2_STREAM_CANCEL', function(error) { + let msg = 'The pending stream has been canceled'; + if (error) { + this.cause = error; + if (typeof error.message === 'string') + msg += ` (caused by: ${error.message})`; + } + return msg; +}, Error); +E('ERR_HTTP2_STREAM_ERROR', 'Stream closed with error code %s', Error); +E('ERR_HTTP2_STREAM_SELF_DEPENDENCY', + 'A stream cannot depend on itself', Error); +E('ERR_HTTP2_TRAILERS_ALREADY_SENT', + 'Trailing headers have already been sent', Error); +E('ERR_HTTP2_TRAILERS_NOT_READY', + 'Trailing headers cannot be sent until after the wantTrailers event is ' + + 'emitted', Error); +E('ERR_HTTP2_UNSUPPORTED_PROTOCOL', 'protocol "%s" is unsupported.', Error); +E('ERR_HTTP_HEADERS_SENT', + 'Cannot %s headers after they are sent to the client', Error); +E('ERR_HTTP_INVALID_HEADER_VALUE', + 'Invalid value "%s" for header "%s"', TypeError); +E('ERR_HTTP_INVALID_STATUS_CODE', 'Invalid status code: %s', RangeError); +E('ERR_HTTP_TRAILER_INVALID', + 'Trailers are invalid with this transfer encoding', Error); +E('ERR_INCOMPATIBLE_OPTION_PAIR', + 'Option "%s" cannot be used in combination with option "%s"', TypeError); +E('ERR_INPUT_TYPE_NOT_ALLOWED', '--input-type can only be used with string ' + + 'input via --eval, --print, or STDIN', Error); +E('ERR_INSPECTOR_ALREADY_CONNECTED', '%s is already connected', Error); +E('ERR_INSPECTOR_CLOSED', 'Session was closed', Error); +E('ERR_INSPECTOR_COMMAND', 'Inspector error %d: %s', Error); +E('ERR_INSPECTOR_NOT_ACTIVE', 'Inspector is not active', Error); +E('ERR_INSPECTOR_NOT_AVAILABLE', 'Inspector is not available', Error); +E('ERR_INSPECTOR_NOT_CONNECTED', 'Session is not connected', Error); +E('ERR_INSPECTOR_NOT_WORKER', 'Current thread is not a worker', Error); +E('ERR_INTERNAL_ASSERTION', (message) => { + const suffix = 'This is caused by either a bug in Node.js ' + + 'or incorrect usage of Node.js internals.\n' + + 'Please open an issue with this stack trace at ' + + 'https://github.com/nodejs/node/issues\n'; + return message === undefined ? suffix : `${message}\n${suffix}`; +}, Error); +E('ERR_INVALID_ADDRESS_FAMILY', function(addressType, host, port) { + this.host = host; + this.port = port; + return `Invalid address family: ${addressType} ${host}:${port}`; +}, RangeError); +E('ERR_INVALID_ARG_TYPE', + (name, expected, actual) => { + assert(typeof name === 'string', "'name' must be a string"); + if (!ArrayIsArray(expected)) { + expected = [expected]; + } + + let msg = 'The '; + if (name.endsWith(' argument')) { + // For cases like 'first argument' + msg += `${name} `; + } else { + const type = name.includes('.') ? 'property' : 'argument'; + msg += `"${name}" ${type} `; + } + msg += 'must be '; + + const types = []; + const instances = []; + const other = []; + + for (const value of expected) { + assert(typeof value === 'string', + 'All expected entries have to be of type string'); + if (kTypes.includes(value)) { + types.push(value.toLowerCase()); + } else if (classRegExp.test(value)) { + instances.push(value); + } else { + assert(value !== 'object', + 'The value "object" should be written as "Object"'); + other.push(value); + } + } + + // Special handle `object` in case other instances are allowed to outline + // the differences between each other. + if (instances.length > 0) { + const pos = types.indexOf('object'); + if (pos !== -1) { + types.splice(pos, 1); + instances.push('Object'); + } + } + + if (types.length > 0) { + if (types.length > 2) { + const last = types.pop(); + msg += `one of type ${types.join(', ')}, or ${last}`; + } else if (types.length === 2) { + msg += `one of type ${types[0]} or ${types[1]}`; + } else { + msg += `of type ${types[0]}`; + } + if (instances.length > 0 || other.length > 0) + msg += ' or '; + } + + if (instances.length > 0) { + if (instances.length > 2) { + const last = instances.pop(); + msg += `an instance of ${instances.join(', ')}, or ${last}`; + } else { + msg += `an instance of ${instances[0]}`; + if (instances.length === 2) { + msg += ` or ${instances[1]}`; + } + } + if (other.length > 0) + msg += ' or '; + } + + if (other.length > 0) { + if (other.length > 2) { + const last = other.pop(); + msg += `one of ${other.join(', ')}, or ${last}`; + } else if (other.length === 2) { + msg += `one of ${other[0]} or ${other[1]}`; + } else { + if (other[0].toLowerCase() !== other[0]) + msg += 'an '; + msg += `${other[0]}`; + } + } + + if (actual == null) { + msg += `. Received ${actual}`; + } else if (typeof actual === 'function' && actual.name) { + msg += `. Received function ${actual.name}`; + } else if (typeof actual === 'object') { + if (actual.constructor && actual.constructor.name) { + msg += `. Received an instance of ${actual.constructor.name}`; + } else { + const inspected = lazyInternalUtilInspect() + .inspect(actual, { depth: -1 }); + msg += `. Received ${inspected}`; + } + } else { + let inspected = lazyInternalUtilInspect() + .inspect(actual, { colors: false }); + if (inspected.length > 25) + inspected = `${inspected.slice(0, 25)}...`; + msg += `. Received type ${typeof actual} (${inspected})`; + } + return msg; + }, TypeError); +E('ERR_INVALID_ARG_VALUE', (name, value, reason = 'is invalid') => { + let inspected = lazyInternalUtilInspect().inspect(value); + if (inspected.length > 128) { + inspected = `${inspected.slice(0, 128)}...`; + } + return `The argument '${name}' ${reason}. Received ${inspected}`; +}, TypeError, RangeError); +E('ERR_INVALID_ASYNC_ID', 'Invalid %s value: %s', RangeError); +E('ERR_INVALID_BUFFER_SIZE', + 'Buffer size must be a multiple of %s', RangeError); +E('ERR_INVALID_CALLBACK', + 'Callback must be a function. Received %O', TypeError); +E('ERR_INVALID_CHAR', + // Using a default argument here is important so the argument is not counted + // towards `Function#length`. + (name, field = undefined) => { + let msg = `Invalid character in ${name}`; + if (field !== undefined) { + msg += ` ["${field}"]`; + } + return msg; + }, TypeError); +E('ERR_INVALID_CURSOR_POS', + 'Cannot set cursor row without setting its column', TypeError); +E('ERR_INVALID_FD', + '"fd" must be a positive integer: %s', RangeError); +E('ERR_INVALID_FD_TYPE', 'Unsupported fd type: %s', TypeError); +E('ERR_INVALID_FILE_URL_HOST', + 'File URL host must be "localhost" or empty on %s', TypeError); +E('ERR_INVALID_FILE_URL_PATH', 'File URL path %s', TypeError); +E('ERR_INVALID_HANDLE_TYPE', 'This handle type cannot be sent', TypeError); +E('ERR_INVALID_HTTP_TOKEN', '%s must be a valid HTTP token ["%s"]', TypeError); +E('ERR_INVALID_IP_ADDRESS', 'Invalid IP address: %s', TypeError); +E('ERR_INVALID_MODULE_SPECIFIER', (pkgPath, subpath, base = undefined) => { + if (subpath === undefined) { + return `Invalid package name '${pkgPath}' imported from ${base}`; + } else if (base === undefined) { + assert(subpath !== '.'); + return `Package subpath '${subpath}' is not a valid module request for ` + + `the "exports" resolution of ${pkgPath}${sep}package.json`; + } else { + return `Package subpath '${subpath}' is not a valid module request for ` + + `the "exports" resolution of ${pkgPath} imported from ${base}`; + } +}, TypeError); +E('ERR_INVALID_OPT_VALUE', (name, value) => + `The value "${String(value)}" is invalid for option "${name}"`, + TypeError, + RangeError); +E('ERR_INVALID_OPT_VALUE_ENCODING', + 'The value "%s" is invalid for option "encoding"', TypeError); +E('ERR_INVALID_PACKAGE_CONFIG', (path, message, hasMessage = true) => { + if (hasMessage) + return `Invalid package config ${path}${sep}package.json, ${message}`; + else + return `Invalid JSON in ${path} imported from ${message}`; +}, Error); +E('ERR_INVALID_PACKAGE_TARGET', + (pkgPath, key, subpath, target, base = undefined) => { + const relError = typeof target === 'string' && + target.length && !StringPrototypeStartsWith(target, './'); + if (key === null) { + if (subpath !== '') { + return `Invalid "exports" target ${JSONStringify(target)} defined ` + + `for '${subpath}' in the package config ${pkgPath} imported from ` + + `${base}.${relError ? '; targets must start with "./"' : ''}`; + } else { + return `Invalid "exports" main target ${target} defined in the ` + + `package config ${pkgPath} imported from ${base}${relError ? + '; targets must start with "./"' : ''}`; + } + } else if (key === '.') { + return `Invalid "exports" main target ${JSONStringify(target)} defined ` + + `in the package config ${pkgPath}${sep}package.json${relError ? + '; targets must start with "./"' : ''}`; + } else if (relError) { + return `Invalid "exports" target ${JSONStringify(target)} defined for '${ + StringPrototypeSlice(key, 0, -subpath.length || key.length)}' in the ` + + `package config ${pkgPath}${sep}package.json; ` + + 'targets must start with "./"'; + } else { + return `Invalid "exports" target ${JSONStringify(target)} defined for '${ + StringPrototypeSlice(key, 0, -subpath.length || key.length)}' in the ` + + `package config ${pkgPath}${sep}package.json`; + } + }, Error); +E('ERR_INVALID_PERFORMANCE_MARK', + 'The "%s" performance mark has not been set', Error); +E('ERR_INVALID_PROTOCOL', + 'Protocol "%s" not supported. Expected "%s"', + TypeError); +E('ERR_INVALID_REPL_EVAL_CONFIG', + 'Cannot specify both "breakEvalOnSigint" and "eval" for REPL', TypeError); +E('ERR_INVALID_REPL_INPUT', '%s', TypeError); +E('ERR_INVALID_RETURN_PROPERTY', (input, name, prop, value) => { + return `Expected a valid ${input} to be returned for the "${prop}" from the` + + ` "${name}" function but got ${value}.`; +}, TypeError); +E('ERR_INVALID_RETURN_PROPERTY_VALUE', (input, name, prop, value) => { + let type; + if (value && value.constructor && value.constructor.name) { + type = `instance of ${value.constructor.name}`; + } else { + type = `type ${typeof value}`; + } + return `Expected ${input} to be returned for the "${prop}" from the` + + ` "${name}" function but got ${type}.`; +}, TypeError); +E('ERR_INVALID_RETURN_VALUE', (input, name, value) => { + let type; + if (value && value.constructor && value.constructor.name) { + type = `instance of ${value.constructor.name}`; + } else { + type = `type ${typeof value}`; + } + return `Expected ${input} to be returned from the "${name}"` + + ` function but got ${type}.`; +}, TypeError); +E('ERR_INVALID_SYNC_FORK_INPUT', + 'Asynchronous forks do not support ' + + 'Buffer, TypedArray, DataView or string input: %s', + TypeError); +E('ERR_INVALID_THIS', 'Value of "this" must be of type %s', TypeError); +E('ERR_INVALID_TUPLE', '%s must be an iterable %s tuple', TypeError); +E('ERR_INVALID_URI', 'URI malformed', URIError); +E('ERR_INVALID_URL', function(input) { + this.input = input; + return `Invalid URL: ${input}`; +}, TypeError); +E('ERR_INVALID_URL_SCHEME', + (expected) => { + if (typeof expected === 'string') + expected = [expected]; + assert(expected.length <= 2); + const res = expected.length === 2 ? + `one of scheme ${expected[0]} or ${expected[1]}` : + `of scheme ${expected[0]}`; + return `The URL must be ${res}`; + }, TypeError); +E('ERR_IPC_CHANNEL_CLOSED', 'Channel closed', Error); +E('ERR_IPC_DISCONNECTED', 'IPC channel is already disconnected', Error); +E('ERR_IPC_ONE_PIPE', 'Child process can have only one IPC pipe', Error); +E('ERR_IPC_SYNC_FORK', 'IPC cannot be used with synchronous forks', Error); +E('ERR_MANIFEST_ASSERT_INTEGRITY', + (moduleURL, realIntegrities) => { + let msg = `The content of "${ + moduleURL + }" does not match the expected integrity.`; + if (realIntegrities.size) { + const sri = [...realIntegrities.entries()].map(([alg, dgs]) => { + return `${alg}-${dgs}`; + }).join(' '); + msg += ` Integrities found are: ${sri}`; + } else { + msg += ' The resource was not found in the policy.'; + } + return msg; + }, Error); +E('ERR_MANIFEST_DEPENDENCY_MISSING', + 'Manifest resource %s does not list %s as a dependency specifier', + Error); +E('ERR_MANIFEST_INTEGRITY_MISMATCH', + 'Manifest resource %s has multiple entries but integrity lists do not match', + SyntaxError); +E('ERR_MANIFEST_INVALID_RESOURCE_FIELD', + 'Manifest resource %s has invalid property value for %s', + TypeError); +E('ERR_MANIFEST_TDZ', 'Manifest initialization has not yet run', Error); +E('ERR_MANIFEST_UNKNOWN_ONERROR', + 'Manifest specified unknown error behavior "%s".', + SyntaxError); +E('ERR_METHOD_NOT_IMPLEMENTED', 'The %s method is not implemented', Error); +E('ERR_MISSING_ARGS', + (...args) => { + assert(args.length > 0, 'At least one arg needs to be specified'); + let msg = 'The '; + const len = args.length; + args = args.map((a) => `"${a}"`); + switch (len) { + case 1: + msg += `${args[0]} argument`; + break; + case 2: + msg += `${args[0]} and ${args[1]} arguments`; + break; + default: + msg += args.slice(0, len - 1).join(', '); + msg += `, and ${args[len - 1]} arguments`; + break; + } + return `${msg} must be specified`; + }, TypeError); +E('ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK', + 'The ES Module loader may not return a format of \'dynamic\' when no ' + + 'dynamicInstantiate function was provided', Error); +E('ERR_MISSING_OPTION', '%s is required', TypeError); +E('ERR_MODULE_NOT_FOUND', (path, base, type = 'package') => { + return `Cannot find ${type} '${path}' imported from ${base}`; +}, Error); +E('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times', Error); +E('ERR_NAPI_CONS_FUNCTION', 'Constructor must be a function', TypeError); +E('ERR_NAPI_INVALID_DATAVIEW_ARGS', + 'byte_offset + byte_length should be less than or equal to the size in ' + + 'bytes of the array passed in', + RangeError); +E('ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT', + 'start offset of %s should be a multiple of %s', RangeError); +E('ERR_NAPI_INVALID_TYPEDARRAY_LENGTH', + 'Invalid typed array length', RangeError); +E('ERR_NO_CRYPTO', + 'Node.js is not compiled with OpenSSL crypto support', Error); +E('ERR_NO_ICU', + '%s is not supported on Node.js compiled without ICU', TypeError); +E('ERR_OUT_OF_RANGE', + (str, range, input, replaceDefaultBoolean = false) => { + assert(range, 'Missing "range" argument'); + let msg = replaceDefaultBoolean ? str : + `The value of "${str}" is out of range.`; + let received; + if (NumberIsInteger(input) && MathAbs(input) > 2 ** 32) { + received = addNumericalSeparator(String(input)); + } else if (typeof input === 'bigint') { + received = String(input); + if (input > 2n ** 32n || input < -(2n ** 32n)) { + received = addNumericalSeparator(received); + } + received += 'n'; + } else { + received = lazyInternalUtilInspect().inspect(input); + } + msg += ` It must be ${range}. Received ${received}`; + return msg; + }, RangeError); +E('ERR_PACKAGE_PATH_NOT_EXPORTED', (pkgPath, subpath, base = undefined) => { + if (subpath === '.') { + return `No "exports" main resolved in ${pkgPath}${sep}package.json`; + } else if (base === undefined) { + return `Package subpath '${subpath}' is not defined by "exports" in ${ + pkgPath}${sep}package.json`; + } else { + return `Package subpath '${subpath}' is not defined by "exports" in ${ + pkgPath} imported from ${base}`; + } +}, Error); +E('ERR_REQUIRE_ESM', + (filename, parentPath = null, packageJsonPath = null) => { + 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 .js file whose nearest parent ' + + 'package.json contains "type": "module" which defines all .js ' + + 'files in that package scope as ES modules.\nInstead rename ' + + `${basename} to end in .cjs, change the requiring code to use ` + + 'import(), or remove "type": "module" from ' + + `${packageJsonPath}.\n`; + return msg; + } + return msg; + }, Error); +E('ERR_SCRIPT_EXECUTION_INTERRUPTED', + 'Script execution was interrupted by `SIGINT`', Error); +E('ERR_SERVER_ALREADY_LISTEN', + 'Listen method has been called more than once without closing.', Error); +E('ERR_SERVER_NOT_RUNNING', 'Server is not running.', Error); +E('ERR_SOCKET_ALREADY_BOUND', 'Socket is already bound', Error); +E('ERR_SOCKET_BAD_BUFFER_SIZE', + 'Buffer size must be a positive integer', TypeError); +E('ERR_SOCKET_BAD_PORT', (name, port, allowZero = true) => { + assert(typeof allowZero === 'boolean', + "The 'allowZero' argument must be of type boolean."); + const operator = allowZero ? '>=' : '>'; + return `${name} should be ${operator} 0 and < 65536. Received ${port}.`; +}, RangeError); +E('ERR_SOCKET_BAD_TYPE', + 'Bad socket type specified. Valid types are: udp4, udp6', TypeError); +E('ERR_SOCKET_BUFFER_SIZE', + 'Could not get or set buffer size', + SystemError); +E('ERR_SOCKET_CLOSED', 'Socket is closed', Error); +E('ERR_SOCKET_DGRAM_IS_CONNECTED', 'Already connected', Error); +E('ERR_SOCKET_DGRAM_NOT_CONNECTED', 'Not connected', Error); +E('ERR_SOCKET_DGRAM_NOT_RUNNING', 'Not running', Error); +E('ERR_SRI_PARSE', + 'Subresource Integrity string %j had an unexpected %j at position %d', + SyntaxError); +E('ERR_STREAM_ALREADY_FINISHED', + 'Cannot call %s after a stream was finished', + Error); +E('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable', Error); +E('ERR_STREAM_DESTROYED', 'Cannot call %s after a stream was destroyed', Error); +E('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError); +E('ERR_STREAM_PREMATURE_CLOSE', 'Premature close', Error); +E('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF', Error); +E('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', + 'stream.unshift() after end event', Error); +E('ERR_STREAM_WRAP', 'Stream has StringDecoder set or is in objectMode', Error); +E('ERR_STREAM_WRITE_AFTER_END', 'write after end', Error); +E('ERR_SYNTHETIC', 'JavaScript Callstack', Error); +E('ERR_SYSTEM_ERROR', 'A system error occurred', SystemError); +E('ERR_TLS_CERT_ALTNAME_INVALID', function(reason, host, cert) { + this.reason = reason; + this.host = host; + this.cert = cert; + return `Hostname/IP does not match certificate's altnames: ${reason}`; +}, Error); +E('ERR_TLS_DH_PARAM_SIZE', 'DH parameter size %s is less than 2048', Error); +E('ERR_TLS_HANDSHAKE_TIMEOUT', 'TLS handshake timeout', Error); +E('ERR_TLS_INVALID_CONTEXT', '%s must be a SecureContext', TypeError), +E('ERR_TLS_INVALID_STATE', 'TLS socket connection must be securely established', + Error), +E('ERR_TLS_INVALID_PROTOCOL_VERSION', + '%j is not a valid %s TLS protocol version', TypeError); +E('ERR_TLS_PROTOCOL_VERSION_CONFLICT', + 'TLS protocol version %j conflicts with secureProtocol %j', TypeError); +E('ERR_TLS_RENEGOTIATION_DISABLED', + 'TLS session renegotiation disabled for this socket', Error); + +// This should probably be a `TypeError`. +E('ERR_TLS_REQUIRED_SERVER_NAME', + '"servername" is required parameter for Server.addContext', Error); +E('ERR_TLS_SESSION_ATTACK', 'TLS session renegotiation attack detected', Error); +E('ERR_TLS_SNI_FROM_SERVER', + 'Cannot issue SNI from a TLS server-side socket', Error); +E('ERR_TRACE_EVENTS_CATEGORY_REQUIRED', + 'At least one category is required', TypeError); +E('ERR_TRACE_EVENTS_UNAVAILABLE', 'Trace events are unavailable', Error); + +// This should probably be a `RangeError`. +E('ERR_TTY_INIT_FAILED', 'TTY initialization failed', SystemError); +E('ERR_UNAVAILABLE_DURING_EXIT', 'Cannot call function in process exit ' + + 'handler', Error); +E('ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET', + '`process.setupUncaughtExceptionCapture()` was called while a capture ' + + 'callback was already active', + Error); +E('ERR_UNESCAPED_CHARACTERS', '%s contains unescaped characters', TypeError); +E('ERR_UNHANDLED_ERROR', + // Using a default argument here is important so the argument is not counted + // towards `Function#length`. + (err = undefined) => { + const msg = 'Unhandled error.'; + if (err === undefined) return msg; + return `${msg} (${err})`; + }, Error); +E('ERR_UNKNOWN_BUILTIN_MODULE', 'No such built-in module: %s', Error); +E('ERR_UNKNOWN_CREDENTIAL', '%s identifier does not exist: %s', Error); +E('ERR_UNKNOWN_ENCODING', 'Unknown encoding: %s', TypeError); +E('ERR_UNKNOWN_FILE_EXTENSION', + 'Unknown file extension "%s" for %s', + TypeError); +E('ERR_UNKNOWN_MODULE_FORMAT', 'Unknown module format: %s', RangeError); +E('ERR_UNKNOWN_SIGNAL', 'Unknown signal: %s', TypeError); +E('ERR_UNSUPPORTED_ESM_URL_SCHEME', 'Only file and data URLs are supported ' + + 'by the default ESM loader', Error); + +E('ERR_V8BREAKITERATOR', + 'Full ICU data not installed. See https://github.com/nodejs/node/wiki/Intl', + Error); + +// This should probably be a `TypeError`. +E('ERR_VALID_PERFORMANCE_ENTRY_TYPE', + 'At least one valid performance entry type is required', Error); +E('ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING', + 'A dynamic import callback was not specified.', TypeError); +E('ERR_VM_MODULE_ALREADY_LINKED', 'Module has already been linked', Error); +E('ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA', + 'Cached data cannot be created for a module which has been evaluated', Error); +E('ERR_VM_MODULE_DIFFERENT_CONTEXT', + 'Linked modules must use the same context', Error); +E('ERR_VM_MODULE_LINKING_ERRORED', + 'Linking has already failed for the provided module', Error); +E('ERR_VM_MODULE_NOT_MODULE', + 'Provided module is not an instance of Module', Error); +E('ERR_VM_MODULE_STATUS', 'Module status %s', Error); +E('ERR_WASI_ALREADY_STARTED', 'WASI instance has already started', Error); +E('ERR_WORKER_INIT_FAILED', 'Worker initialization failure: %s', Error); +E('ERR_WORKER_INVALID_EXEC_ARGV', (errors, msg = 'invalid execArgv flags') => + `Initiated Worker with ${msg}: ${errors.join(', ')}`, + Error); +E('ERR_WORKER_NOT_RUNNING', 'Worker instance not running', Error); +E('ERR_WORKER_OUT_OF_MEMORY', + 'Worker terminated due to reaching memory limit: %s', Error); +E('ERR_WORKER_PATH', (filename) => + 'The worker script or module filename must be an absolute path or a ' + + 'relative path starting with \'./\' or \'../\'.' + + (filename.startsWith('file://') ? + ' Wrap file:// URLs with `new URL`.' : '' + ) + + ` Received "${filename}"`, + TypeError); +E('ERR_WORKER_UNSERIALIZABLE_ERROR', + 'Serializing an uncaught exception failed', Error); +E('ERR_WORKER_UNSUPPORTED_EXTENSION', + 'The worker script extension must be ".js", ".mjs", or ".cjs". Received "%s"', + TypeError); +E('ERR_WORKER_UNSUPPORTED_OPERATION', + '%s is not supported in workers', TypeError); +E('ERR_ZLIB_INITIALIZATION_FAILED', 'Initialization failed', Error); diff --git a/raw/node-internal-errors-b533fb3508009e5f567cc776daba8fbf665386a6-stripped.js b/raw/node-internal-errors-b533fb3508009e5f567cc776daba8fbf665386a6-stripped.js new file mode 100644 index 000000000..101c0b51f --- /dev/null +++ b/raw/node-internal-errors-b533fb3508009e5f567cc776daba8fbf665386a6-stripped.js @@ -0,0 +1,44 @@ +// Copied from https://github.com/nodejs/node/blob/b533fb3508009e5f567cc776daba8fbf665386a6/lib/internal/errors.js + +'use strict'; + +// Utility function for registering the error codes. Only used here. Exported +// *only* to allow for testing. +function E(sym, val, def, ...otherClasses) { + // Special case for SystemError that formats the error message differently + // The SystemErrors only have SystemError as their base classes. + messages.set(sym, val); + if (def === SystemError) { + def = makeSystemErrorWithCode(sym); + } else { + def = makeNodeErrorWithCode(def, sym); + } + + if (otherClasses.length !== 0) { + otherClasses.forEach((clazz) => { + def[clazz.name] = makeNodeErrorWithCode(clazz, sym); + }); + } + codes[sym] = def; +} + +E('ERR_REQUIRE_ESM', + (filename, parentPath = null, packageJsonPath = null) => { + 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 .js file whose nearest parent ' + + 'package.json contains "type": "module" which defines all .js ' + + 'files in that package scope as ES modules.\nInstead rename ' + + `${basename} to end in .cjs, change the requiring code to use ` + + 'import(), or remove "type": "module" from ' + + `${packageJsonPath}.\n`; + return msg; + } + return msg; + }, Error); diff --git a/raw/node-internal-errors-b533fb3508009e5f567cc776daba8fbf665386a6.js b/raw/node-internal-errors-b533fb3508009e5f567cc776daba8fbf665386a6.js new file mode 100644 index 000000000..c6b0e3824 --- /dev/null +++ b/raw/node-internal-errors-b533fb3508009e5f567cc776daba8fbf665386a6.js @@ -0,0 +1,1454 @@ +// Copied from https://github.com/nodejs/node/blob/b533fb3508009e5f567cc776daba8fbf665386a6/lib/internal/errors.js + +/* eslint node-core/documented-errors: "error" */ +/* eslint node-core/alphabetize-errors: "error" */ +/* eslint node-core/prefer-util-format-errors: "error" */ + +'use strict'; + +// The whole point behind this internal module is to allow Node.js to no +// longer be forced to treat every error message change as a semver-major +// change. The NodeError classes here all expose a `code` property whose +// value statically and permanently identifies the error. While the error +// message may change, the code should not. + +const { + ArrayIsArray, + Error, + JSONStringify, + Map, + MathAbs, + NumberIsInteger, + ObjectDefineProperty, + ObjectKeys, + StringPrototypeSlice, + StringPrototypeStartsWith, + Symbol, + SymbolFor, + WeakMap, +} = primordials; + +const sep = process.platform === 'win32' ? '\\' : '/'; + +const messages = new Map(); +const codes = {}; + +const classRegExp = /^([A-Z][a-z0-9]*)+$/; +// Sorted by a rough estimate on most frequently used entries. +const kTypes = [ + 'string', + 'function', + 'number', + 'object', + // Accept 'Function' and 'Object' as alternative to the lower cased version. + 'Function', + 'Object', + 'boolean', + 'bigint', + 'symbol' +]; + +const { kMaxLength } = internalBinding('buffer'); + +const MainContextError = Error; +const ErrorToString = Error.prototype.toString; +const overrideStackTrace = new WeakMap(); +const kNoOverride = Symbol('kNoOverride'); +const prepareStackTrace = (globalThis, error, trace) => { + // API for node internals to override error stack formatting + // without interfering with userland code. + if (overrideStackTrace.has(error)) { + const f = overrideStackTrace.get(error); + overrideStackTrace.delete(error); + return f(error, trace); + } + + const globalOverride = + maybeOverridePrepareStackTrace(globalThis, error, trace); + if (globalOverride !== kNoOverride) return globalOverride; + + // Normal error formatting: + // + // Error: Message + // at function (file) + // at file + const errorString = ErrorToString.call(error); + if (trace.length === 0) { + return errorString; + } + return `${errorString}\n at ${trace.join('\n at ')}`; +}; + +const maybeOverridePrepareStackTrace = (globalThis, error, trace) => { + // Polyfill of V8's Error.prepareStackTrace API. + // https://crbug.com/v8/7848 + // `globalThis` is the global that contains the constructor which + // created `error`. + if (typeof globalThis.Error.prepareStackTrace === 'function') { + return globalThis.Error.prepareStackTrace(error, trace); + } + // We still have legacy usage that depends on the main context's `Error` + // being used, even when the error is from a different context. + // TODO(devsnek): evaluate if this can be eventually deprecated/removed. + if (typeof MainContextError.prepareStackTrace === 'function') { + return MainContextError.prepareStackTrace(error, trace); + } + + return kNoOverride; +}; + +let excludedStackFn; + +// Lazily loaded +let util; +let assert; + +let internalUtil = null; +function lazyInternalUtil() { + if (!internalUtil) { + internalUtil = require('internal/util'); + } + return internalUtil; +} + +let internalUtilInspect = null; +function lazyInternalUtilInspect() { + if (!internalUtilInspect) { + internalUtilInspect = require('internal/util/inspect'); + } + return internalUtilInspect; +} + +let buffer; +function lazyBuffer() { + if (buffer === undefined) + buffer = require('buffer').Buffer; + return buffer; +} + +// A specialized Error that includes an additional info property with +// additional information about the error condition. +// It has the properties present in a UVException but with a custom error +// message followed by the uv error code and uv error message. +// It also has its own error code with the original uv error context put into +// `err.info`. +// The context passed into this error must have .code, .syscall and .message, +// and may have .path and .dest. +class SystemError extends Error { + constructor(key, context) { + if (excludedStackFn === undefined) { + super(); + } else { + const limit = Error.stackTraceLimit; + Error.stackTraceLimit = 0; + super(); + // Reset the limit and setting the name property. + Error.stackTraceLimit = limit; + } + const prefix = getMessage(key, [], this); + let message = `${prefix}: ${context.syscall} returned ` + + `${context.code} (${context.message})`; + + if (context.path !== undefined) + message += ` ${context.path}`; + if (context.dest !== undefined) + message += ` => ${context.dest}`; + + ObjectDefineProperty(this, 'message', { + value: message, + enumerable: false, + writable: true, + configurable: true + }); + addCodeToName(this, 'SystemError', key); + + this.code = key; + + ObjectDefineProperty(this, 'info', { + value: context, + enumerable: true, + configurable: true, + writable: false + }); + + ObjectDefineProperty(this, 'errno', { + get() { + return context.errno; + }, + set: (value) => { + context.errno = value; + }, + enumerable: true, + configurable: true + }); + + ObjectDefineProperty(this, 'syscall', { + get() { + return context.syscall; + }, + set: (value) => { + context.syscall = value; + }, + enumerable: true, + configurable: true + }); + + if (context.path !== undefined) { + // TODO(BridgeAR): Investigate why and when the `.toString()` was + // introduced. The `path` and `dest` properties in the context seem to + // always be of type string. We should probably just remove the + // `.toString()` and `Buffer.from()` operations and set the value on the + // context as the user did. + ObjectDefineProperty(this, 'path', { + get() { + return context.path != null ? + context.path.toString() : context.path; + }, + set: (value) => { + context.path = value ? + lazyBuffer().from(value.toString()) : undefined; + }, + enumerable: true, + configurable: true + }); + } + + if (context.dest !== undefined) { + ObjectDefineProperty(this, 'dest', { + get() { + return context.dest != null ? + context.dest.toString() : context.dest; + }, + set: (value) => { + context.dest = value ? + lazyBuffer().from(value.toString()) : undefined; + }, + enumerable: true, + configurable: true + }); + } + } + + toString() { + return `${this.name} [${this.code}]: ${this.message}`; + } + + [SymbolFor('nodejs.util.inspect.custom')](recurseTimes, ctx) { + return lazyInternalUtilInspect().inspect(this, { + ...ctx, + getters: true, + customInspect: false + }); + } +} + +function makeSystemErrorWithCode(key) { + return class NodeError extends SystemError { + constructor(ctx) { + super(key, ctx); + } + }; +} + +function makeNodeErrorWithCode(Base, key) { + return class NodeError extends Base { + constructor(...args) { + if (excludedStackFn === undefined) { + super(); + } else { + const limit = Error.stackTraceLimit; + Error.stackTraceLimit = 0; + super(); + // Reset the limit and setting the name property. + Error.stackTraceLimit = limit; + } + const message = getMessage(key, args, this); + ObjectDefineProperty(this, 'message', { + value: message, + enumerable: false, + writable: true, + configurable: true + }); + addCodeToName(this, super.name, key); + this.code = key; + } + + toString() { + return `${this.name} [${key}]: ${this.message}`; + } + }; +} + +// This function removes unnecessary frames from Node.js core errors. +function hideStackFrames(fn) { + return function hidden(...args) { + // Make sure the most outer `hideStackFrames()` function is used. + let setStackFn = false; + if (excludedStackFn === undefined) { + excludedStackFn = hidden; + setStackFn = true; + } + try { + return fn(...args); + } finally { + if (setStackFn === true) { + excludedStackFn = undefined; + } + } + }; +} + +function addCodeToName(err, name, code) { + // Set the stack + if (excludedStackFn !== undefined) { + // eslint-disable-next-line no-restricted-syntax + Error.captureStackTrace(err, excludedStackFn); + } + // Add the error code to the name to include it in the stack trace. + err.name = `${name} [${code}]`; + // Access the stack to generate the error message including the error code + // from the name. + err.stack; + // Reset the name to the actual name. + if (name === 'SystemError') { + ObjectDefineProperty(err, 'name', { + value: name, + enumerable: false, + writable: true, + configurable: true + }); + } else { + delete err.name; + } +} + +// Utility function for registering the error codes. Only used here. Exported +// *only* to allow for testing. +function E(sym, val, def, ...otherClasses) { + // Special case for SystemError that formats the error message differently + // The SystemErrors only have SystemError as their base classes. + messages.set(sym, val); + if (def === SystemError) { + def = makeSystemErrorWithCode(sym); + } else { + def = makeNodeErrorWithCode(def, sym); + } + + if (otherClasses.length !== 0) { + otherClasses.forEach((clazz) => { + def[clazz.name] = makeNodeErrorWithCode(clazz, sym); + }); + } + codes[sym] = def; +} + +function getMessage(key, args, self) { + const msg = messages.get(key); + + if (assert === undefined) assert = require('internal/assert'); + + if (typeof msg === 'function') { + assert( + msg.length <= args.length, // Default options do not count. + `Code: ${key}; The provided arguments length (${args.length}) does not ` + + `match the required ones (${msg.length}).` + ); + return msg.apply(self, args); + } + + const expectedLength = (msg.match(/%[dfijoOs]/g) || []).length; + assert( + expectedLength === args.length, + `Code: ${key}; The provided arguments length (${args.length}) does not ` + + `match the required ones (${expectedLength}).` + ); + if (args.length === 0) + return msg; + + args.unshift(msg); + return lazyInternalUtilInspect().format.apply(null, args); +} + +let uvBinding; + +function lazyUv() { + if (!uvBinding) { + uvBinding = internalBinding('uv'); + } + return uvBinding; +} + +const uvUnmappedError = ['UNKNOWN', 'unknown error']; + +function uvErrmapGet(name) { + uvBinding = lazyUv(); + if (!uvBinding.errmap) { + uvBinding.errmap = uvBinding.getErrorMap(); + } + return uvBinding.errmap.get(name); +} + + +/** + * This creates an error compatible with errors produced in the C++ + * function UVException using a context object with data assembled in C++. + * The goal is to migrate them to ERR_* errors later when compatibility is + * not a concern. + * + * @param {Object} ctx + * @returns {Error} + */ +function uvException(ctx) { + const [ code, uvmsg ] = uvErrmapGet(ctx.errno) || uvUnmappedError; + let message = `${code}: ${ctx.message || uvmsg}, ${ctx.syscall}`; + + let path; + let dest; + if (ctx.path) { + path = ctx.path.toString(); + message += ` '${path}'`; + } + if (ctx.dest) { + dest = ctx.dest.toString(); + message += ` -> '${dest}'`; + } + + // Reducing the limit improves the performance significantly. We do not loose + // the stack frames due to the `captureStackTrace()` function that is called + // later. + const tmpLimit = Error.stackTraceLimit; + Error.stackTraceLimit = 0; + // Pass the message to the constructor instead of setting it on the object + // to make sure it is the same as the one created in C++ + // eslint-disable-next-line no-restricted-syntax + const err = new Error(message); + Error.stackTraceLimit = tmpLimit; + + for (const prop of ObjectKeys(ctx)) { + if (prop === 'message' || prop === 'path' || prop === 'dest') { + continue; + } + err[prop] = ctx[prop]; + } + + err.code = code; + if (path) { + err.path = path; + } + if (dest) { + err.dest = dest; + } + + // eslint-disable-next-line no-restricted-syntax + Error.captureStackTrace(err, excludedStackFn || uvException); + return err; +} + +/** + * This creates an error compatible with errors produced in the C++ + * This function should replace the deprecated + * `exceptionWithHostPort()` function. + * + * @param {number} err - A libuv error number + * @param {string} syscall + * @param {string} address + * @param {number} [port] + * @returns {Error} + */ +function uvExceptionWithHostPort(err, syscall, address, port) { + const [ code, uvmsg ] = uvErrmapGet(err) || uvUnmappedError; + const message = `${syscall} ${code}: ${uvmsg}`; + let details = ''; + + if (port && port > 0) { + details = ` ${address}:${port}`; + } else if (address) { + details = ` ${address}`; + } + + // Reducing the limit improves the performance significantly. We do not loose + // the stack frames due to the `captureStackTrace()` function that is called + // later. + const tmpLimit = Error.stackTraceLimit; + Error.stackTraceLimit = 0; + // eslint-disable-next-line no-restricted-syntax + const ex = new Error(`${message}${details}`); + Error.stackTraceLimit = tmpLimit; + ex.code = code; + ex.errno = err; + ex.syscall = syscall; + ex.address = address; + if (port) { + ex.port = port; + } + + // eslint-disable-next-line no-restricted-syntax + Error.captureStackTrace(ex, excludedStackFn || uvExceptionWithHostPort); + return ex; +} + +/** + * This used to be util._errnoException(). + * + * @param {number} err - A libuv error number + * @param {string} syscall + * @param {string} [original] + * @returns {Error} + */ +function errnoException(err, syscall, original) { + // TODO(joyeecheung): We have to use the type-checked + // getSystemErrorName(err) to guard against invalid arguments from users. + // This can be replaced with [ code ] = errmap.get(err) when this method + // is no longer exposed to user land. + if (util === undefined) util = require('util'); + const code = util.getSystemErrorName(err); + const message = original ? + `${syscall} ${code} ${original}` : `${syscall} ${code}`; + + // eslint-disable-next-line no-restricted-syntax + const ex = new Error(message); + ex.errno = err; + ex.code = code; + ex.syscall = syscall; + + // eslint-disable-next-line no-restricted-syntax + Error.captureStackTrace(ex, excludedStackFn || errnoException); + return ex; +} + +/** + * Deprecated, new function is `uvExceptionWithHostPort()` + * New function added the error description directly + * from C++. this method for backwards compatibility + * @param {number} err - A libuv error number + * @param {string} syscall + * @param {string} address + * @param {number} [port] + * @param {string} [additional] + * @returns {Error} + */ +function exceptionWithHostPort(err, syscall, address, port, additional) { + // TODO(joyeecheung): We have to use the type-checked + // getSystemErrorName(err) to guard against invalid arguments from users. + // This can be replaced with [ code ] = errmap.get(err) when this method + // is no longer exposed to user land. + if (util === undefined) util = require('util'); + const code = util.getSystemErrorName(err); + let details = ''; + if (port && port > 0) { + details = ` ${address}:${port}`; + } else if (address) { + details = ` ${address}`; + } + if (additional) { + details += ` - Local (${additional})`; + } + + // Reducing the limit improves the performance significantly. We do not loose + // the stack frames due to the `captureStackTrace()` function that is called + // later. + const tmpLimit = Error.stackTraceLimit; + Error.stackTraceLimit = 0; + // eslint-disable-next-line no-restricted-syntax + const ex = new Error(`${syscall} ${code}${details}`); + Error.stackTraceLimit = tmpLimit; + ex.errno = err; + ex.code = code; + ex.syscall = syscall; + ex.address = address; + if (port) { + ex.port = port; + } + + // eslint-disable-next-line no-restricted-syntax + Error.captureStackTrace(ex, excludedStackFn || exceptionWithHostPort); + return ex; +} + +/** + * @param {number|string} code - A libuv error number or a c-ares error code + * @param {string} syscall + * @param {string} [hostname] + * @returns {Error} + */ +function dnsException(code, syscall, hostname) { + let errno; + // If `code` is of type number, it is a libuv error number, else it is a + // c-ares error code. + // TODO(joyeecheung): translate c-ares error codes into numeric ones and + // make them available in a property that's not error.errno (since they + // can be in conflict with libuv error codes). Also make sure + // util.getSystemErrorName() can understand them when an being informed that + // the number is a c-ares error code. + if (typeof code === 'number') { + errno = code; + // ENOTFOUND is not a proper POSIX error, but this error has been in place + // long enough that it's not practical to remove it. + if (code === lazyUv().UV_EAI_NODATA || code === lazyUv().UV_EAI_NONAME) { + code = 'ENOTFOUND'; // Fabricated error name. + } else { + code = lazyInternalUtil().getSystemErrorName(code); + } + } + const message = `${syscall} ${code}${hostname ? ` ${hostname}` : ''}`; + // Reducing the limit improves the performance significantly. We do not loose + // the stack frames due to the `captureStackTrace()` function that is called + // later. + const tmpLimit = Error.stackTraceLimit; + Error.stackTraceLimit = 0; + // eslint-disable-next-line no-restricted-syntax + const ex = new Error(message); + Error.stackTraceLimit = tmpLimit; + ex.errno = errno; + ex.code = code; + ex.syscall = syscall; + if (hostname) { + ex.hostname = hostname; + } + + // eslint-disable-next-line no-restricted-syntax + Error.captureStackTrace(ex, excludedStackFn || dnsException); + return ex; +} + +function connResetException(msg) { + // eslint-disable-next-line no-restricted-syntax + const ex = new Error(msg); + ex.code = 'ECONNRESET'; + return ex; +} + +let maxStack_ErrorName; +let maxStack_ErrorMessage; +/** + * Returns true if `err.name` and `err.message` are equal to engine-specific + * values indicating max call stack size has been exceeded. + * "Maximum call stack size exceeded" in V8. + * + * @param {Error} err + * @returns {boolean} + */ +function isStackOverflowError(err) { + if (maxStack_ErrorMessage === undefined) { + try { + function overflowStack() { overflowStack(); } + overflowStack(); + } catch (err) { + maxStack_ErrorMessage = err.message; + maxStack_ErrorName = err.name; + } + } + + return err && err.name === maxStack_ErrorName && + err.message === maxStack_ErrorMessage; +} + +// Only use this for integers! Decimal numbers do not work with this function. +function addNumericalSeparator(val) { + let res = ''; + let i = val.length; + const start = val[0] === '-' ? 1 : 0; + for (; i >= start + 4; i -= 3) { + res = `_${val.slice(i - 3, i)}${res}`; + } + return `${val.slice(0, i)}${res}`; +} + +// Used to enhance the stack that will be picked up by the inspector +const kEnhanceStackBeforeInspector = Symbol('kEnhanceStackBeforeInspector'); + +// These are supposed to be called only on fatal exceptions before +// the process exits. +const fatalExceptionStackEnhancers = { + beforeInspector(error) { + if (typeof error[kEnhanceStackBeforeInspector] !== 'function') { + return error.stack; + } + + try { + // Set the error.stack here so it gets picked up by the + // inspector. + error.stack = error[kEnhanceStackBeforeInspector](); + } catch { + // We are just enhancing the error. If it fails, ignore it. + } + return error.stack; + }, + afterInspector(error) { + const originalStack = error.stack; + const { + inspect, + inspectDefaultOptions: { + colors: defaultColors + } + } = lazyInternalUtilInspect(); + const colors = (internalBinding('util').guessHandleType(2) === 'TTY' && + require('internal/tty').hasColors()) || + defaultColors; + try { + return inspect(error, { colors }); + } catch { + return originalStack; + } + } +}; + +module.exports = { + addCodeToName, // Exported for NghttpError + codes, + dnsException, + errnoException, + exceptionWithHostPort, + getMessage, + hideStackFrames, + isStackOverflowError, + connResetException, + uvErrmapGet, + uvException, + uvExceptionWithHostPort, + SystemError, + // This is exported only to facilitate testing. + E, + kNoOverride, + prepareStackTrace, + maybeOverridePrepareStackTrace, + overrideStackTrace, + kEnhanceStackBeforeInspector, + fatalExceptionStackEnhancers +}; + +// To declare an error message, use the E(sym, val, def) function above. The sym +// must be an upper case string. The val can be either a function or a string. +// The def must be an error class. +// The return value of the function must be a string. +// Examples: +// E('EXAMPLE_KEY1', 'This is the error value', Error); +// E('EXAMPLE_KEY2', (a, b) => return `${a} ${b}`, RangeError); +// +// Once an error code has been assigned, the code itself MUST NOT change and +// any given error code must never be reused to identify a different error. +// +// Any error code added here should also be added to the documentation +// +// Note: Please try to keep these in alphabetical order +// +// Note: Node.js specific errors must begin with the prefix ERR_ +E('ERR_AMBIGUOUS_ARGUMENT', 'The "%s" argument is ambiguous. %s', TypeError); +E('ERR_ARG_NOT_ITERABLE', '%s must be iterable', TypeError); +E('ERR_ASSERTION', '%s', Error); +E('ERR_ASYNC_CALLBACK', '%s must be a function', TypeError); +E('ERR_ASYNC_TYPE', 'Invalid name for async "type": %s', TypeError); +E('ERR_BROTLI_INVALID_PARAM', '%s is not a valid Brotli parameter', RangeError); +E('ERR_BUFFER_OUT_OF_BOUNDS', + // Using a default argument here is important so the argument is not counted + // towards `Function#length`. + (name = undefined) => { + if (name) { + return `"${name}" is outside of buffer bounds`; + } + return 'Attempt to access memory outside buffer bounds'; + }, RangeError); +E('ERR_BUFFER_TOO_LARGE', + `Cannot create a Buffer larger than 0x${kMaxLength.toString(16)} bytes`, + RangeError); +E('ERR_CANNOT_WATCH_SIGINT', 'Cannot watch for SIGINT signals', Error); +E('ERR_CHILD_CLOSED_BEFORE_REPLY', + 'Child closed before reply received', Error); +E('ERR_CHILD_PROCESS_IPC_REQUIRED', + "Forked processes must have an IPC channel, missing value 'ipc' in %s", + Error); +E('ERR_CHILD_PROCESS_STDIO_MAXBUFFER', '%s maxBuffer length exceeded', + RangeError); +E('ERR_CONSOLE_WRITABLE_STREAM', + 'Console expects a writable stream instance for %s', TypeError); +E('ERR_CONTEXT_NOT_INITIALIZED', 'context used is not initialized', Error); +E('ERR_CPU_USAGE', 'Unable to obtain cpu usage %s', Error); +E('ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED', + 'Custom engines not supported by this OpenSSL', Error); +E('ERR_CRYPTO_ECDH_INVALID_FORMAT', 'Invalid ECDH format: %s', TypeError); +E('ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY', + 'Public key is not valid for specified curve', Error); +E('ERR_CRYPTO_ENGINE_UNKNOWN', 'Engine "%s" was not found', Error); +E('ERR_CRYPTO_FIPS_FORCED', + 'Cannot set FIPS mode, it was forced with --force-fips at startup.', Error); +E('ERR_CRYPTO_FIPS_UNAVAILABLE', 'Cannot set FIPS mode in a non-FIPS build.', + Error); +E('ERR_CRYPTO_HASH_FINALIZED', 'Digest already called', Error); +E('ERR_CRYPTO_HASH_UPDATE_FAILED', 'Hash update failed', Error); +E('ERR_CRYPTO_INCOMPATIBLE_KEY', 'Incompatible %s: %s', Error); +E('ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS', 'The selected key encoding %s %s.', + Error); +E('ERR_CRYPTO_INVALID_DIGEST', 'Invalid digest: %s', TypeError); +E('ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE', + 'Invalid key object type %s, expected %s.', TypeError); +E('ERR_CRYPTO_INVALID_STATE', 'Invalid state for operation %s', Error); +E('ERR_CRYPTO_PBKDF2_ERROR', 'PBKDF2 error', Error); +E('ERR_CRYPTO_SCRYPT_INVALID_PARAMETER', 'Invalid scrypt parameter', Error); +E('ERR_CRYPTO_SCRYPT_NOT_SUPPORTED', 'Scrypt algorithm not supported', Error); +// Switch to TypeError. The current implementation does not seem right. +E('ERR_CRYPTO_SIGN_KEY_REQUIRED', 'No key provided to sign', Error); +E('ERR_CRYPTO_TIMING_SAFE_EQUAL_LENGTH', + 'Input buffers must have the same byte length', RangeError); +E('ERR_DIR_CLOSED', 'Directory handle was closed', Error); +E('ERR_DIR_CONCURRENT_OPERATION', + 'Cannot do synchronous work on directory handle with concurrent ' + + 'asynchronous operations', Error); +E('ERR_DNS_SET_SERVERS_FAILED', 'c-ares failed to set servers: "%s" [%s]', + Error); +E('ERR_DOMAIN_CALLBACK_NOT_AVAILABLE', + 'A callback was registered through ' + + 'process.setUncaughtExceptionCaptureCallback(), which is mutually ' + + 'exclusive with using the `domain` module', + Error); +E('ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE', + 'The `domain` module is in use, which is mutually exclusive with calling ' + + 'process.setUncaughtExceptionCaptureCallback()', + Error); +E('ERR_ENCODING_INVALID_ENCODED_DATA', function(encoding, ret) { + this.errno = ret; + return `The encoded data was not valid for encoding ${encoding}`; +}, TypeError); +E('ERR_ENCODING_NOT_SUPPORTED', 'The "%s" encoding is not supported', + RangeError); +E('ERR_EVAL_ESM_CANNOT_PRINT', '--print cannot be used with ESM input', Error); +E('ERR_FALSY_VALUE_REJECTION', function(reason) { + this.reason = reason; + return 'Promise was rejected with falsy value'; +}, Error); +E('ERR_FEATURE_UNAVAILABLE_ON_PLATFORM', + 'The feature %s is unavailable on the current platform' + + ', which is being used to run Node.js', + TypeError); +E('ERR_FS_FILE_TOO_LARGE', 'File size (%s) is greater than 2 GB', RangeError); +E('ERR_FS_INVALID_SYMLINK_TYPE', + 'Symlink type must be one of "dir", "file", or "junction". Received "%s"', + Error); // Switch to TypeError. The current implementation does not seem right +E('ERR_HTTP2_ALTSVC_INVALID_ORIGIN', + 'HTTP/2 ALTSVC frames require a valid origin', TypeError); +E('ERR_HTTP2_ALTSVC_LENGTH', + 'HTTP/2 ALTSVC frames are limited to 16382 bytes', TypeError); +E('ERR_HTTP2_CONNECT_AUTHORITY', + ':authority header is required for CONNECT requests', Error); +E('ERR_HTTP2_CONNECT_PATH', + 'The :path header is forbidden for CONNECT requests', Error); +E('ERR_HTTP2_CONNECT_SCHEME', + 'The :scheme header is forbidden for CONNECT requests', Error); +E('ERR_HTTP2_GOAWAY_SESSION', + 'New streams cannot be created after receiving a GOAWAY', Error); +E('ERR_HTTP2_HEADERS_AFTER_RESPOND', + 'Cannot specify additional headers after response initiated', Error); +E('ERR_HTTP2_HEADERS_SENT', 'Response has already been initiated.', Error); +E('ERR_HTTP2_HEADER_SINGLE_VALUE', + 'Header field "%s" must only have a single value', TypeError); +E('ERR_HTTP2_INFO_STATUS_NOT_ALLOWED', + 'Informational status codes cannot be used', RangeError); +E('ERR_HTTP2_INVALID_CONNECTION_HEADERS', + 'HTTP/1 Connection specific headers are forbidden: "%s"', TypeError); +E('ERR_HTTP2_INVALID_HEADER_VALUE', + 'Invalid value "%s" for header "%s"', TypeError); +E('ERR_HTTP2_INVALID_INFO_STATUS', + 'Invalid informational status code: %s', RangeError); +E('ERR_HTTP2_INVALID_ORIGIN', + 'HTTP/2 ORIGIN frames require a valid origin', TypeError); +E('ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH', + 'Packed settings length must be a multiple of six', RangeError); +E('ERR_HTTP2_INVALID_PSEUDOHEADER', + '"%s" is an invalid pseudoheader or is used incorrectly', TypeError); +E('ERR_HTTP2_INVALID_SESSION', 'The session has been destroyed', Error); +E('ERR_HTTP2_INVALID_SETTING_VALUE', + // Using default arguments here is important so the arguments are not counted + // towards `Function#length`. + function(name, actual, min = undefined, max = undefined) { + this.actual = actual; + if (min !== undefined) { + this.min = min; + this.max = max; + } + return `Invalid value for setting "${name}": ${actual}`; + }, TypeError, RangeError); +E('ERR_HTTP2_INVALID_STREAM', 'The stream has been destroyed', Error); +E('ERR_HTTP2_MAX_PENDING_SETTINGS_ACK', + 'Maximum number of pending settings acknowledgements', Error); +E('ERR_HTTP2_NESTED_PUSH', + 'A push stream cannot initiate another push stream.', Error); +E('ERR_HTTP2_NO_SOCKET_MANIPULATION', + 'HTTP/2 sockets should not be directly manipulated (e.g. read and written)', + Error); +E('ERR_HTTP2_ORIGIN_LENGTH', + 'HTTP/2 ORIGIN frames are limited to 16382 bytes', TypeError); +E('ERR_HTTP2_OUT_OF_STREAMS', + 'No stream ID is available because maximum stream ID has been reached', + Error); +E('ERR_HTTP2_PAYLOAD_FORBIDDEN', + 'Responses with %s status must not have a payload', Error); +E('ERR_HTTP2_PING_CANCEL', 'HTTP2 ping cancelled', Error); +E('ERR_HTTP2_PING_LENGTH', 'HTTP2 ping payload must be 8 bytes', RangeError); +E('ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED', + 'Cannot set HTTP/2 pseudo-headers', TypeError); +E('ERR_HTTP2_PUSH_DISABLED', 'HTTP/2 client has disabled push streams', Error); +E('ERR_HTTP2_SEND_FILE', 'Directories cannot be sent', Error); +E('ERR_HTTP2_SEND_FILE_NOSEEK', + 'Offset or length can only be specified for regular files', Error); +E('ERR_HTTP2_SESSION_ERROR', 'Session closed with error code %s', Error); +E('ERR_HTTP2_SETTINGS_CANCEL', 'HTTP2 session settings canceled', Error); +E('ERR_HTTP2_SOCKET_BOUND', + 'The socket is already bound to an Http2Session', Error); +E('ERR_HTTP2_SOCKET_UNBOUND', + 'The socket has been disconnected from the Http2Session', Error); +E('ERR_HTTP2_STATUS_101', + 'HTTP status code 101 (Switching Protocols) is forbidden in HTTP/2', Error); +E('ERR_HTTP2_STATUS_INVALID', 'Invalid status code: %s', RangeError); +E('ERR_HTTP2_STREAM_CANCEL', function(error) { + let msg = 'The pending stream has been canceled'; + if (error) { + this.cause = error; + if (typeof error.message === 'string') + msg += ` (caused by: ${error.message})`; + } + return msg; +}, Error); +E('ERR_HTTP2_STREAM_ERROR', 'Stream closed with error code %s', Error); +E('ERR_HTTP2_STREAM_SELF_DEPENDENCY', + 'A stream cannot depend on itself', Error); +E('ERR_HTTP2_TRAILERS_ALREADY_SENT', + 'Trailing headers have already been sent', Error); +E('ERR_HTTP2_TRAILERS_NOT_READY', + 'Trailing headers cannot be sent until after the wantTrailers event is ' + + 'emitted', Error); +E('ERR_HTTP2_UNSUPPORTED_PROTOCOL', 'protocol "%s" is unsupported.', Error); +E('ERR_HTTP_HEADERS_SENT', + 'Cannot %s headers after they are sent to the client', Error); +E('ERR_HTTP_INVALID_HEADER_VALUE', + 'Invalid value "%s" for header "%s"', TypeError); +E('ERR_HTTP_INVALID_STATUS_CODE', 'Invalid status code: %s', RangeError); +E('ERR_HTTP_TRAILER_INVALID', + 'Trailers are invalid with this transfer encoding', Error); +E('ERR_INCOMPATIBLE_OPTION_PAIR', + 'Option "%s" cannot be used in combination with option "%s"', TypeError); +E('ERR_INPUT_TYPE_NOT_ALLOWED', '--input-type can only be used with string ' + + 'input via --eval, --print, or STDIN', Error); +E('ERR_INSPECTOR_ALREADY_CONNECTED', '%s is already connected', Error); +E('ERR_INSPECTOR_CLOSED', 'Session was closed', Error); +E('ERR_INSPECTOR_COMMAND', 'Inspector error %d: %s', Error); +E('ERR_INSPECTOR_NOT_ACTIVE', 'Inspector is not active', Error); +E('ERR_INSPECTOR_NOT_AVAILABLE', 'Inspector is not available', Error); +E('ERR_INSPECTOR_NOT_CONNECTED', 'Session is not connected', Error); +E('ERR_INSPECTOR_NOT_WORKER', 'Current thread is not a worker', Error); +E('ERR_INTERNAL_ASSERTION', (message) => { + const suffix = 'This is caused by either a bug in Node.js ' + + 'or incorrect usage of Node.js internals.\n' + + 'Please open an issue with this stack trace at ' + + 'https://github.com/nodejs/node/issues\n'; + return message === undefined ? suffix : `${message}\n${suffix}`; +}, Error); +E('ERR_INVALID_ADDRESS_FAMILY', function(addressType, host, port) { + this.host = host; + this.port = port; + return `Invalid address family: ${addressType} ${host}:${port}`; +}, RangeError); +E('ERR_INVALID_ARG_TYPE', + (name, expected, actual) => { + assert(typeof name === 'string', "'name' must be a string"); + if (!ArrayIsArray(expected)) { + expected = [expected]; + } + + let msg = 'The '; + if (name.endsWith(' argument')) { + // For cases like 'first argument' + msg += `${name} `; + } else { + const type = name.includes('.') ? 'property' : 'argument'; + msg += `"${name}" ${type} `; + } + msg += 'must be '; + + const types = []; + const instances = []; + const other = []; + + for (const value of expected) { + assert(typeof value === 'string', + 'All expected entries have to be of type string'); + if (kTypes.includes(value)) { + types.push(value.toLowerCase()); + } else if (classRegExp.test(value)) { + instances.push(value); + } else { + assert(value !== 'object', + 'The value "object" should be written as "Object"'); + other.push(value); + } + } + + // Special handle `object` in case other instances are allowed to outline + // the differences between each other. + if (instances.length > 0) { + const pos = types.indexOf('object'); + if (pos !== -1) { + types.splice(pos, 1); + instances.push('Object'); + } + } + + if (types.length > 0) { + if (types.length > 2) { + const last = types.pop(); + msg += `one of type ${types.join(', ')}, or ${last}`; + } else if (types.length === 2) { + msg += `one of type ${types[0]} or ${types[1]}`; + } else { + msg += `of type ${types[0]}`; + } + if (instances.length > 0 || other.length > 0) + msg += ' or '; + } + + if (instances.length > 0) { + if (instances.length > 2) { + const last = instances.pop(); + msg += `an instance of ${instances.join(', ')}, or ${last}`; + } else { + msg += `an instance of ${instances[0]}`; + if (instances.length === 2) { + msg += ` or ${instances[1]}`; + } + } + if (other.length > 0) + msg += ' or '; + } + + if (other.length > 0) { + if (other.length > 2) { + const last = other.pop(); + msg += `one of ${other.join(', ')}, or ${last}`; + } else if (other.length === 2) { + msg += `one of ${other[0]} or ${other[1]}`; + } else { + if (other[0].toLowerCase() !== other[0]) + msg += 'an '; + msg += `${other[0]}`; + } + } + + if (actual == null) { + msg += `. Received ${actual}`; + } else if (typeof actual === 'function' && actual.name) { + msg += `. Received function ${actual.name}`; + } else if (typeof actual === 'object') { + if (actual.constructor && actual.constructor.name) { + msg += `. Received an instance of ${actual.constructor.name}`; + } else { + const inspected = lazyInternalUtilInspect() + .inspect(actual, { depth: -1 }); + msg += `. Received ${inspected}`; + } + } else { + let inspected = lazyInternalUtilInspect() + .inspect(actual, { colors: false }); + if (inspected.length > 25) + inspected = `${inspected.slice(0, 25)}...`; + msg += `. Received type ${typeof actual} (${inspected})`; + } + return msg; + }, TypeError); +E('ERR_INVALID_ARG_VALUE', (name, value, reason = 'is invalid') => { + let inspected = lazyInternalUtilInspect().inspect(value); + if (inspected.length > 128) { + inspected = `${inspected.slice(0, 128)}...`; + } + return `The argument '${name}' ${reason}. Received ${inspected}`; +}, TypeError, RangeError); +E('ERR_INVALID_ASYNC_ID', 'Invalid %s value: %s', RangeError); +E('ERR_INVALID_BUFFER_SIZE', + 'Buffer size must be a multiple of %s', RangeError); +E('ERR_INVALID_CALLBACK', + 'Callback must be a function. Received %O', TypeError); +E('ERR_INVALID_CHAR', + // Using a default argument here is important so the argument is not counted + // towards `Function#length`. + (name, field = undefined) => { + let msg = `Invalid character in ${name}`; + if (field !== undefined) { + msg += ` ["${field}"]`; + } + return msg; + }, TypeError); +E('ERR_INVALID_CURSOR_POS', + 'Cannot set cursor row without setting its column', TypeError); +E('ERR_INVALID_FD', + '"fd" must be a positive integer: %s', RangeError); +E('ERR_INVALID_FD_TYPE', 'Unsupported fd type: %s', TypeError); +E('ERR_INVALID_FILE_URL_HOST', + 'File URL host must be "localhost" or empty on %s', TypeError); +E('ERR_INVALID_FILE_URL_PATH', 'File URL path %s', TypeError); +E('ERR_INVALID_HANDLE_TYPE', 'This handle type cannot be sent', TypeError); +E('ERR_INVALID_HTTP_TOKEN', '%s must be a valid HTTP token ["%s"]', TypeError); +E('ERR_INVALID_IP_ADDRESS', 'Invalid IP address: %s', TypeError); +E('ERR_INVALID_MODULE_SPECIFIER', (pkgPath, subpath, base = undefined) => { + if (subpath === undefined) { + return `Invalid package name '${pkgPath}' imported from ${base}`; + } else if (base === undefined) { + assert(subpath !== '.'); + return `Package subpath '${subpath}' is not a valid module request for ` + + `the "exports" resolution of ${pkgPath}${sep}package.json`; + } + return `Package subpath '${subpath}' is not a valid module request for ` + + `the "exports" resolution of ${pkgPath} imported from ${base}`; +}, TypeError); +E('ERR_INVALID_OPT_VALUE', (name, value) => + `The value "${String(value)}" is invalid for option "${name}"`, + TypeError, + RangeError); +E('ERR_INVALID_OPT_VALUE_ENCODING', + 'The value "%s" is invalid for option "encoding"', TypeError); +E('ERR_INVALID_PACKAGE_CONFIG', (path, message, hasMessage = true) => { + if (hasMessage) + return `Invalid package config ${path}${sep}package.json, ${message}`; + return `Invalid JSON in ${path} imported from ${message}`; +}, Error); +E('ERR_INVALID_PACKAGE_TARGET', + (pkgPath, key, subpath, target, base = undefined) => { + const relError = typeof target === 'string' && + target.length && !StringPrototypeStartsWith(target, './'); + if (key === null) { + if (subpath !== '') { + return `Invalid "exports" target ${JSONStringify(target)} defined ` + + `for '${subpath}' in the package config ${pkgPath} imported from ` + + `${base}.${relError ? '; targets must start with "./"' : ''}`; + } + return `Invalid "exports" main target ${target} defined in the ` + + `package config ${pkgPath} imported from ${base}${relError ? + '; targets must start with "./"' : ''}`; + } else if (key === '.') { + return `Invalid "exports" main target ${JSONStringify(target)} defined ` + + `in the package config ${pkgPath}${sep}package.json${relError ? + '; targets must start with "./"' : ''}`; + } else if (relError) { + return `Invalid "exports" target ${JSONStringify(target)} defined for '${ + StringPrototypeSlice(key, 0, -subpath.length || key.length)}' in the ` + + `package config ${pkgPath}${sep}package.json; ` + + 'targets must start with "./"'; + } + return `Invalid "exports" target ${JSONStringify(target)} defined for '${ + StringPrototypeSlice(key, 0, -subpath.length || key.length)}' in the ` + + `package config ${pkgPath}${sep}package.json`; + }, Error); +E('ERR_INVALID_PERFORMANCE_MARK', + 'The "%s" performance mark has not been set', Error); +E('ERR_INVALID_PROTOCOL', + 'Protocol "%s" not supported. Expected "%s"', + TypeError); +E('ERR_INVALID_REPL_EVAL_CONFIG', + 'Cannot specify both "breakEvalOnSigint" and "eval" for REPL', TypeError); +E('ERR_INVALID_REPL_INPUT', '%s', TypeError); +E('ERR_INVALID_RETURN_PROPERTY', (input, name, prop, value) => { + return `Expected a valid ${input} to be returned for the "${prop}" from the` + + ` "${name}" function but got ${value}.`; +}, TypeError); +E('ERR_INVALID_RETURN_PROPERTY_VALUE', (input, name, prop, value) => { + let type; + if (value && value.constructor && value.constructor.name) { + type = `instance of ${value.constructor.name}`; + } else { + type = `type ${typeof value}`; + } + return `Expected ${input} to be returned for the "${prop}" from the` + + ` "${name}" function but got ${type}.`; +}, TypeError); +E('ERR_INVALID_RETURN_VALUE', (input, name, value) => { + let type; + if (value && value.constructor && value.constructor.name) { + type = `instance of ${value.constructor.name}`; + } else { + type = `type ${typeof value}`; + } + return `Expected ${input} to be returned from the "${name}"` + + ` function but got ${type}.`; +}, TypeError); +E('ERR_INVALID_SYNC_FORK_INPUT', + 'Asynchronous forks do not support ' + + 'Buffer, TypedArray, DataView or string input: %s', + TypeError); +E('ERR_INVALID_THIS', 'Value of "this" must be of type %s', TypeError); +E('ERR_INVALID_TUPLE', '%s must be an iterable %s tuple', TypeError); +E('ERR_INVALID_URI', 'URI malformed', URIError); +E('ERR_INVALID_URL', function(input) { + this.input = input; + return `Invalid URL: ${input}`; +}, TypeError); +E('ERR_INVALID_URL_SCHEME', + (expected) => { + if (typeof expected === 'string') + expected = [expected]; + assert(expected.length <= 2); + const res = expected.length === 2 ? + `one of scheme ${expected[0]} or ${expected[1]}` : + `of scheme ${expected[0]}`; + return `The URL must be ${res}`; + }, TypeError); +E('ERR_IPC_CHANNEL_CLOSED', 'Channel closed', Error); +E('ERR_IPC_DISCONNECTED', 'IPC channel is already disconnected', Error); +E('ERR_IPC_ONE_PIPE', 'Child process can have only one IPC pipe', Error); +E('ERR_IPC_SYNC_FORK', 'IPC cannot be used with synchronous forks', Error); +E('ERR_MANIFEST_ASSERT_INTEGRITY', + (moduleURL, realIntegrities) => { + let msg = `The content of "${ + moduleURL + }" does not match the expected integrity.`; + if (realIntegrities.size) { + const sri = [...realIntegrities.entries()].map(([alg, dgs]) => { + return `${alg}-${dgs}`; + }).join(' '); + msg += ` Integrities found are: ${sri}`; + } else { + msg += ' The resource was not found in the policy.'; + } + return msg; + }, Error); +E('ERR_MANIFEST_DEPENDENCY_MISSING', + 'Manifest resource %s does not list %s as a dependency specifier', + Error); +E('ERR_MANIFEST_INTEGRITY_MISMATCH', + 'Manifest resource %s has multiple entries but integrity lists do not match', + SyntaxError); +E('ERR_MANIFEST_INVALID_RESOURCE_FIELD', + 'Manifest resource %s has invalid property value for %s', + TypeError); +E('ERR_MANIFEST_TDZ', 'Manifest initialization has not yet run', Error); +E('ERR_MANIFEST_UNKNOWN_ONERROR', + 'Manifest specified unknown error behavior "%s".', + SyntaxError); +E('ERR_METHOD_NOT_IMPLEMENTED', 'The %s method is not implemented', Error); +E('ERR_MISSING_ARGS', + (...args) => { + assert(args.length > 0, 'At least one arg needs to be specified'); + let msg = 'The '; + const len = args.length; + args = args.map((a) => `"${a}"`); + switch (len) { + case 1: + msg += `${args[0]} argument`; + break; + case 2: + msg += `${args[0]} and ${args[1]} arguments`; + break; + default: + msg += args.slice(0, len - 1).join(', '); + msg += `, and ${args[len - 1]} arguments`; + break; + } + return `${msg} must be specified`; + }, TypeError); +E('ERR_MISSING_DYNAMIC_INSTANTIATE_HOOK', + 'The ES Module loader may not return a format of \'dynamic\' when no ' + + 'dynamicInstantiate function was provided', Error); +E('ERR_MISSING_OPTION', '%s is required', TypeError); +E('ERR_MODULE_NOT_FOUND', (path, base, type = 'package') => { + return `Cannot find ${type} '${path}' imported from ${base}`; +}, Error); +E('ERR_MULTIPLE_CALLBACK', 'Callback called multiple times', Error); +E('ERR_NAPI_CONS_FUNCTION', 'Constructor must be a function', TypeError); +E('ERR_NAPI_INVALID_DATAVIEW_ARGS', + 'byte_offset + byte_length should be less than or equal to the size in ' + + 'bytes of the array passed in', + RangeError); +E('ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT', + 'start offset of %s should be a multiple of %s', RangeError); +E('ERR_NAPI_INVALID_TYPEDARRAY_LENGTH', + 'Invalid typed array length', RangeError); +E('ERR_NO_CRYPTO', + 'Node.js is not compiled with OpenSSL crypto support', Error); +E('ERR_NO_ICU', + '%s is not supported on Node.js compiled without ICU', TypeError); +E('ERR_OUT_OF_RANGE', + (str, range, input, replaceDefaultBoolean = false) => { + assert(range, 'Missing "range" argument'); + let msg = replaceDefaultBoolean ? str : + `The value of "${str}" is out of range.`; + let received; + if (NumberIsInteger(input) && MathAbs(input) > 2 ** 32) { + received = addNumericalSeparator(String(input)); + } else if (typeof input === 'bigint') { + received = String(input); + if (input > 2n ** 32n || input < -(2n ** 32n)) { + received = addNumericalSeparator(received); + } + received += 'n'; + } else { + received = lazyInternalUtilInspect().inspect(input); + } + msg += ` It must be ${range}. Received ${received}`; + return msg; + }, RangeError); +E('ERR_PACKAGE_PATH_NOT_EXPORTED', (pkgPath, subpath, base = undefined) => { + if (subpath === '.') { + return `No "exports" main resolved in ${pkgPath}${sep}package.json`; + } else if (base === undefined) { + return `Package subpath '${subpath}' is not defined by "exports" in ${ + pkgPath}${sep}package.json`; + } + return `Package subpath '${subpath}' is not defined by "exports" in ${ + pkgPath} imported from ${base}`; +}, Error); +E('ERR_REQUIRE_ESM', + (filename, parentPath = null, packageJsonPath = null) => { + 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 .js file whose nearest parent ' + + 'package.json contains "type": "module" which defines all .js ' + + 'files in that package scope as ES modules.\nInstead rename ' + + `${basename} to end in .cjs, change the requiring code to use ` + + 'import(), or remove "type": "module" from ' + + `${packageJsonPath}.\n`; + return msg; + } + return msg; + }, Error); +E('ERR_SCRIPT_EXECUTION_INTERRUPTED', + 'Script execution was interrupted by `SIGINT`', Error); +E('ERR_SERVER_ALREADY_LISTEN', + 'Listen method has been called more than once without closing.', Error); +E('ERR_SERVER_NOT_RUNNING', 'Server is not running.', Error); +E('ERR_SOCKET_ALREADY_BOUND', 'Socket is already bound', Error); +E('ERR_SOCKET_BAD_BUFFER_SIZE', + 'Buffer size must be a positive integer', TypeError); +E('ERR_SOCKET_BAD_PORT', (name, port, allowZero = true) => { + assert(typeof allowZero === 'boolean', + "The 'allowZero' argument must be of type boolean."); + const operator = allowZero ? '>=' : '>'; + return `${name} should be ${operator} 0 and < 65536. Received ${port}.`; +}, RangeError); +E('ERR_SOCKET_BAD_TYPE', + 'Bad socket type specified. Valid types are: udp4, udp6', TypeError); +E('ERR_SOCKET_BUFFER_SIZE', + 'Could not get or set buffer size', + SystemError); +E('ERR_SOCKET_CLOSED', 'Socket is closed', Error); +E('ERR_SOCKET_DGRAM_IS_CONNECTED', 'Already connected', Error); +E('ERR_SOCKET_DGRAM_NOT_CONNECTED', 'Not connected', Error); +E('ERR_SOCKET_DGRAM_NOT_RUNNING', 'Not running', Error); +E('ERR_SRI_PARSE', + 'Subresource Integrity string %j had an unexpected %j at position %d', + SyntaxError); +E('ERR_STREAM_ALREADY_FINISHED', + 'Cannot call %s after a stream was finished', + Error); +E('ERR_STREAM_CANNOT_PIPE', 'Cannot pipe, not readable', Error); +E('ERR_STREAM_DESTROYED', 'Cannot call %s after a stream was destroyed', Error); +E('ERR_STREAM_NULL_VALUES', 'May not write null values to stream', TypeError); +E('ERR_STREAM_PREMATURE_CLOSE', 'Premature close', Error); +E('ERR_STREAM_PUSH_AFTER_EOF', 'stream.push() after EOF', Error); +E('ERR_STREAM_UNSHIFT_AFTER_END_EVENT', + 'stream.unshift() after end event', Error); +E('ERR_STREAM_WRAP', 'Stream has StringDecoder set or is in objectMode', Error); +E('ERR_STREAM_WRITE_AFTER_END', 'write after end', Error); +E('ERR_SYNTHETIC', 'JavaScript Callstack', Error); +E('ERR_SYSTEM_ERROR', 'A system error occurred', SystemError); +E('ERR_TLS_CERT_ALTNAME_INVALID', function(reason, host, cert) { + this.reason = reason; + this.host = host; + this.cert = cert; + return `Hostname/IP does not match certificate's altnames: ${reason}`; +}, Error); +E('ERR_TLS_DH_PARAM_SIZE', 'DH parameter size %s is less than 2048', Error); +E('ERR_TLS_HANDSHAKE_TIMEOUT', 'TLS handshake timeout', Error); +E('ERR_TLS_INVALID_CONTEXT', '%s must be a SecureContext', TypeError), +E('ERR_TLS_INVALID_STATE', 'TLS socket connection must be securely established', + Error), +E('ERR_TLS_INVALID_PROTOCOL_VERSION', + '%j is not a valid %s TLS protocol version', TypeError); +E('ERR_TLS_PROTOCOL_VERSION_CONFLICT', + 'TLS protocol version %j conflicts with secureProtocol %j', TypeError); +E('ERR_TLS_RENEGOTIATION_DISABLED', + 'TLS session renegotiation disabled for this socket', Error); + +// This should probably be a `TypeError`. +E('ERR_TLS_REQUIRED_SERVER_NAME', + '"servername" is required parameter for Server.addContext', Error); +E('ERR_TLS_SESSION_ATTACK', 'TLS session renegotiation attack detected', Error); +E('ERR_TLS_SNI_FROM_SERVER', + 'Cannot issue SNI from a TLS server-side socket', Error); +E('ERR_TRACE_EVENTS_CATEGORY_REQUIRED', + 'At least one category is required', TypeError); +E('ERR_TRACE_EVENTS_UNAVAILABLE', 'Trace events are unavailable', Error); + +// This should probably be a `RangeError`. +E('ERR_TTY_INIT_FAILED', 'TTY initialization failed', SystemError); +E('ERR_UNAVAILABLE_DURING_EXIT', 'Cannot call function in process exit ' + + 'handler', Error); +E('ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET', + '`process.setupUncaughtExceptionCapture()` was called while a capture ' + + 'callback was already active', + Error); +E('ERR_UNESCAPED_CHARACTERS', '%s contains unescaped characters', TypeError); +E('ERR_UNHANDLED_ERROR', + // Using a default argument here is important so the argument is not counted + // towards `Function#length`. + (err = undefined) => { + const msg = 'Unhandled error.'; + if (err === undefined) return msg; + return `${msg} (${err})`; + }, Error); +E('ERR_UNKNOWN_BUILTIN_MODULE', 'No such built-in module: %s', Error); +E('ERR_UNKNOWN_CREDENTIAL', '%s identifier does not exist: %s', Error); +E('ERR_UNKNOWN_ENCODING', 'Unknown encoding: %s', TypeError); +E('ERR_UNKNOWN_FILE_EXTENSION', + 'Unknown file extension "%s" for %s', + TypeError); +E('ERR_UNKNOWN_MODULE_FORMAT', 'Unknown module format: %s', RangeError); +E('ERR_UNKNOWN_SIGNAL', 'Unknown signal: %s', TypeError); +E('ERR_UNSUPPORTED_DIR_IMPORT', "Directory import '%s' is not supported " + +'resolving ES modules, imported from %s', Error); +E('ERR_UNSUPPORTED_ESM_URL_SCHEME', 'Only file and data URLs are supported ' + + 'by the default ESM loader', Error); + +E('ERR_V8BREAKITERATOR', + 'Full ICU data not installed. See https://github.com/nodejs/node/wiki/Intl', + Error); + +// This should probably be a `TypeError`. +E('ERR_VALID_PERFORMANCE_ENTRY_TYPE', + 'At least one valid performance entry type is required', Error); +E('ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING', + 'A dynamic import callback was not specified.', TypeError); +E('ERR_VM_MODULE_ALREADY_LINKED', 'Module has already been linked', Error); +E('ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA', + 'Cached data cannot be created for a module which has been evaluated', Error); +E('ERR_VM_MODULE_DIFFERENT_CONTEXT', + 'Linked modules must use the same context', Error); +E('ERR_VM_MODULE_LINKING_ERRORED', + 'Linking has already failed for the provided module', Error); +E('ERR_VM_MODULE_NOT_MODULE', + 'Provided module is not an instance of Module', Error); +E('ERR_VM_MODULE_STATUS', 'Module status %s', Error); +E('ERR_WASI_ALREADY_STARTED', 'WASI instance has already started', Error); +E('ERR_WORKER_INIT_FAILED', 'Worker initialization failure: %s', Error); +E('ERR_WORKER_INVALID_EXEC_ARGV', (errors, msg = 'invalid execArgv flags') => + `Initiated Worker with ${msg}: ${errors.join(', ')}`, + Error); +E('ERR_WORKER_NOT_RUNNING', 'Worker instance not running', Error); +E('ERR_WORKER_OUT_OF_MEMORY', + 'Worker terminated due to reaching memory limit: %s', Error); +E('ERR_WORKER_PATH', (filename) => + 'The worker script or module filename must be an absolute path or a ' + + 'relative path starting with \'./\' or \'../\'.' + + (filename.startsWith('file://') ? + ' Wrap file:// URLs with `new URL`.' : '' + ) + + ` Received "${filename}"`, + TypeError); +E('ERR_WORKER_UNSERIALIZABLE_ERROR', + 'Serializing an uncaught exception failed', Error); +E('ERR_WORKER_UNSUPPORTED_EXTENSION', + 'The worker script extension must be ".js", ".mjs", or ".cjs". Received "%s"', + TypeError); +E('ERR_WORKER_UNSUPPORTED_OPERATION', + '%s is not supported in workers', TypeError); +E('ERR_ZLIB_INITIALIZATION_FAILED', 'Initialization failed', Error); From 97bd1d46c3274f197a3f67f815617c4d52c8b7a1 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 16:04:24 -0400 Subject: [PATCH 16/30] lint-fix --- src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 3ed919360..0eb1f10a4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -96,7 +96,8 @@ let assertScriptCanLoadAsCJS: ( module: NodeJS.Module, filename: string ) => void = engineSupportsPackageTypeField - ? require('../dist-raw/node-internal-modules-cjs-loader').assertScriptCanLoadAsCJSImpl + ? require('../dist-raw/node-internal-modules-cjs-loader') + .assertScriptCanLoadAsCJSImpl : () => { /* noop */ }; From ca0797b5fcc06ae1b550f2966a618d550fa0b6fc Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 16:46:45 -0400 Subject: [PATCH 17/30] fix --- ava.config.cjs | 5 +++-- dist-raw/node-internal-errors.js | 2 ++ src/test/helpers.ts | 10 +++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ava.config.cjs b/ava.config.cjs index aa04b33bf..99ba3545d 100644 --- a/ava.config.cjs +++ b/ava.config.cjs @@ -24,7 +24,7 @@ module.exports = { * ts-node, from either node_modules or tests/node_modules */ - const { existsSync } = require('fs'); + const { existsSync, rmSync } = require('fs'); const rimraf = require('rimraf'); const { resolve } = require('path'); @@ -35,6 +35,7 @@ module.exports = { expect(() => {createRequire(resolve(__dirname, 'tests/foo.js')).resolve('ts-node')}).toThrow(); function remove(p) { - if(existsSync(p)) rimraf.sync(p, {recursive: true}) + // Avoid node deprecation warning triggered by rimraf + if(existsSync(p)) (rmSync ?? rimraf.sync)(p, {recursive: true}) } } diff --git a/dist-raw/node-internal-errors.js b/dist-raw/node-internal-errors.js index c5fd0e4f4..244d32505 100644 --- a/dist-raw/node-internal-errors.js +++ b/dist-raw/node-internal-errors.js @@ -1,5 +1,7 @@ 'use strict'; +const path = require('path'); + module.exports = { createErrRequireEsm }; diff --git a/src/test/helpers.ts b/src/test/helpers.ts index bdddc21f4..2750528d6 100644 --- a/src/test/helpers.ts +++ b/src/test/helpers.ts @@ -224,7 +224,11 @@ export function resetNodeEnvironment() { function captureObjectState(object: any) { const descriptors = Object.getOwnPropertyDescriptors(object); - const values = mapValues(descriptors, (_d, key) => object[key]); + const values = mapValues(descriptors, (_d, key) => { + // Avoid node deprecation warning for accessing _channel + if (object === process && key === '_channel') return descriptors[key].value; + return object[key]; + }); return { descriptors, values, @@ -245,6 +249,10 @@ function resetObject( // Trigger nyc's setter functions for (const [key, value] of Object.entries(state.values)) { try { + // Avoid node deprecation warnings for setting process.config or accessing _channel + if (object === process && key === '_channel') continue; + if (object === process && key === 'config' && object[key] === value) + continue; object[key] = value; } catch {} } From 093dfc0a5605c9aa21f5ce3b3256b8a09046924f Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 16:55:17 -0400 Subject: [PATCH 18/30] fix --- ava.config.cjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ava.config.cjs b/ava.config.cjs index 99ba3545d..4286a1474 100644 --- a/ava.config.cjs +++ b/ava.config.cjs @@ -36,6 +36,6 @@ module.exports = { function remove(p) { // Avoid node deprecation warning triggered by rimraf - if(existsSync(p)) (rmSync ?? rimraf.sync)(p, {recursive: true}) + if(existsSync(p)) (rmSync || rimraf.sync)(p, {recursive: true}) } } From 431c01cca2e7ffe4decfd0362e90a87875ec06f2 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 20:35:45 -0400 Subject: [PATCH 19/30] further alignment --- ...> node-internal-modules-esm-get_format.js} | 3 - ...s => node-internal-modules-esm-resolve.js} | 6 -- raw/download-and-compare.sh | 8 ++ ...internal-modules-esm-get_format-v15.3.0.js | 80 +++++++++++++++++++ 4 files changed, 88 insertions(+), 9 deletions(-) rename dist-raw/{node-esm-default-get-format.js => node-internal-modules-esm-get_format.js} (93%) rename dist-raw/{node-esm-resolve-implementation.js => node-internal-modules-esm-resolve.js} (99%) create mode 100644 raw/node-internal-modules-esm-get_format-v15.3.0.js diff --git a/dist-raw/node-esm-default-get-format.js b/dist-raw/node-internal-modules-esm-get_format.js similarity index 93% rename from dist-raw/node-esm-default-get-format.js rename to dist-raw/node-internal-modules-esm-get_format.js index 65b0bf6dd..5f0a55157 100644 --- a/dist-raw/node-esm-default-get-format.js +++ b/dist-raw/node-internal-modules-esm-get_format.js @@ -1,7 +1,4 @@ // Copied from https://raw.githubusercontent.com/nodejs/node/v15.3.0/lib/internal/modules/esm/get_format.js -// Then modified to suite our needs. -// Formatting is intentionally bad to keep the diff as small as possible, to make it easier to merge -// upstream changes and understand our modifications. 'use strict'; const { diff --git a/dist-raw/node-esm-resolve-implementation.js b/dist-raw/node-internal-modules-esm-resolve.js similarity index 99% rename from dist-raw/node-esm-resolve-implementation.js rename to dist-raw/node-internal-modules-esm-resolve.js index 97543bf74..c5d5b38b9 100644 --- a/dist-raw/node-esm-resolve-implementation.js +++ b/dist-raw/node-internal-modules-esm-resolve.js @@ -1,10 +1,4 @@ // Copied from https://raw.githubusercontent.com/nodejs/node/v15.3.0/lib/internal/modules/esm/resolve.js -// Then modified to suite our needs. -// Formatting is intentionally bad to keep the diff as small as possible, to make it easier to merge -// upstream changes and understand our modifications. -// -// Github diff to easily view the changes: -// https://github.com/TypeStrong/ts-node/compare/esm-resolver-diff..main 'use strict'; const [nodeMajor, nodeMinor, nodePatch] = process.versions.node.split('.').map(s => parseInt(s, 10)) diff --git a/raw/download-and-compare.sh b/raw/download-and-compare.sh index f35a0fd25..62c3b7685 100755 --- a/raw/download-and-compare.sh +++ b/raw/download-and-compare.sh @@ -52,6 +52,14 @@ download #### +path=lib/internal/modules/esm/get_format +local=node-internal-modules-esm-get_format +version=v15.3.0 +download +# compare + +#### + path=lib/internal/repl/await local=node-internal-repl-await version=v17.0.0 diff --git a/raw/node-internal-modules-esm-get_format-v15.3.0.js b/raw/node-internal-modules-esm-get_format-v15.3.0.js new file mode 100644 index 000000000..71b749ed0 --- /dev/null +++ b/raw/node-internal-modules-esm-get_format-v15.3.0.js @@ -0,0 +1,80 @@ +// Copied from https://github.com/nodejs/node/blob/v15.3.0/lib/internal/modules/esm/get_format.js + +'use strict'; +const { + RegExpPrototypeExec, + StringPrototypeStartsWith, +} = primordials; +const { extname } = require('path'); +const { getOptionValue } = require('internal/options'); + +const experimentalJsonModules = getOptionValue('--experimental-json-modules'); +const experimentalSpeciferResolution = + getOptionValue('--experimental-specifier-resolution'); +const experimentalWasmModules = getOptionValue('--experimental-wasm-modules'); +const { getPackageType } = require('internal/modules/esm/resolve'); +const { URL, fileURLToPath } = require('internal/url'); +const { ERR_UNKNOWN_FILE_EXTENSION } = require('internal/errors').codes; + +const extensionFormatMap = { + '__proto__': null, + '.cjs': 'commonjs', + '.js': 'module', + '.mjs': 'module' +}; + +const legacyExtensionFormatMap = { + '__proto__': null, + '.cjs': 'commonjs', + '.js': 'commonjs', + '.json': 'commonjs', + '.mjs': 'module', + '.node': 'commonjs' +}; + +if (experimentalWasmModules) + extensionFormatMap['.wasm'] = legacyExtensionFormatMap['.wasm'] = 'wasm'; + +if (experimentalJsonModules) + extensionFormatMap['.json'] = legacyExtensionFormatMap['.json'] = 'json'; + +function defaultGetFormat(url, context, defaultGetFormatUnused) { + if (StringPrototypeStartsWith(url, 'node:')) { + return { format: 'builtin' }; + } + const parsed = new URL(url); + if (parsed.protocol === 'data:') { + const [ , mime ] = RegExpPrototypeExec( + /^([^/]+\/[^;,]+)(?:[^,]*?)(;base64)?,/, + parsed.pathname, + ) || [ null, null, null ]; + const format = ({ + '__proto__': null, + 'text/javascript': 'module', + 'application/json': experimentalJsonModules ? 'json' : null, + 'application/wasm': experimentalWasmModules ? 'wasm' : null + })[mime] || null; + return { format }; + } else if (parsed.protocol === 'file:') { + const ext = extname(parsed.pathname); + let format; + if (ext === '.js') { + format = getPackageType(parsed.href) === 'module' ? 'module' : 'commonjs'; + } else { + format = extensionFormatMap[ext]; + } + if (!format) { + if (experimentalSpeciferResolution === 'node') { + process.emitWarning( + 'The Node.js specifier resolution in ESM is experimental.', + 'ExperimentalWarning'); + format = legacyExtensionFormatMap[ext]; + } else { + throw new ERR_UNKNOWN_FILE_EXTENSION(ext, fileURLToPath(url)); + } + } + return { format: format || null }; + } + return { format: null }; +} +exports.defaultGetFormat = defaultGetFormat; From 9f588a3e8e43d5cfba127f9d710ff0afb0d8522a Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 20:50:17 -0400 Subject: [PATCH 20/30] fix --- dist-raw/node-internal-modules-esm-get_format.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dist-raw/node-internal-modules-esm-get_format.js b/dist-raw/node-internal-modules-esm-get_format.js index 5f0a55157..02bb65670 100644 --- a/dist-raw/node-internal-modules-esm-get_format.js +++ b/dist-raw/node-internal-modules-esm-get_format.js @@ -16,7 +16,7 @@ const experimentalJsonModules = const experimentalSpeciferResolution = getOptionValue('--experimental-specifier-resolution'); const experimentalWasmModules = getOptionValue('--experimental-wasm-modules'); -const { getPackageType } = require('./node-esm-resolve-implementation.js').createResolve({tsExtensions: [], jsExtensions: []}); +const { getPackageType } = require('./node-internal-modules-esm-resolve').createResolve({tsExtensions: [], jsExtensions: []}); const { URL, fileURLToPath } = require('url'); const { ERR_UNKNOWN_FILE_EXTENSION } = require('./node-errors').codes; From 0937c990aca645c899f30c1fa56dd08025e1580b Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 20:55:44 -0400 Subject: [PATCH 21/30] fix --- src/esm.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/esm.ts b/src/esm.ts index d61ee1584..a92210bb9 100644 --- a/src/esm.ts +++ b/src/esm.ts @@ -18,8 +18,10 @@ import { normalizeSlashes } from './util'; import { createRequire } from 'module'; const { createResolve, -} = require('../dist-raw/node-esm-resolve-implementation'); -const { defaultGetFormat } = require('../dist-raw/node-esm-default-get-format'); +} = require('../dist-raw/node-internal-modules-esm-resolve'); +const { + defaultGetFormat, +} = require('../dist-raw/node-internal-modules-esm-get_format'); // Note: On Windows, URLs look like this: file:///D:/dev/@TypeStrong/ts-node-examples/foo.ts From 6a5f7b944e52113ce25e7cfcd44e53dd88712ad2 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 21:06:15 -0400 Subject: [PATCH 22/30] upgrade v8-compile-cache-lib to get rid of eventemitter warning in tests --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 19319b310..a7b5e4273 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4817,9 +4817,9 @@ "dev": true }, "v8-compile-cache-lib": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz", - "integrity": "sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==" }, "validate-npm-package-license": { "version": "3.0.4", diff --git a/package.json b/package.json index f888cfc9f..e951c8487 100644 --- a/package.json +++ b/package.json @@ -170,7 +170,7 @@ "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.0", + "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" }, "prettier": { From 5f84d3c5f9aa412b5ea6cde33ca25686c03030df Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 22:05:27 -0400 Subject: [PATCH 23/30] Make tests faster --- src/test/helpers.ts | 6 + src/test/index.spec.ts | 127 ++++++++++------------ tests/cwd-and-script-mode/a/tsconfig.json | 3 + tests/cwd-and-script-mode/b/tsconfig.json | 3 + tests/esm-import-assertions/tsconfig.json | 3 + tests/recursive-fork/index.ts | 4 + tests/tsconfig-transpile-only.json | 13 +++ 7 files changed, 90 insertions(+), 69 deletions(-) create mode 100644 tests/tsconfig-transpile-only.json diff --git a/src/test/helpers.ts b/src/test/helpers.ts index 2750528d6..39e9f3e9f 100644 --- a/src/test/helpers.ts +++ b/src/test/helpers.ts @@ -22,6 +22,10 @@ export const ROOT_DIR = resolve(__dirname, '../..'); export const DIST_DIR = resolve(__dirname, '..'); export const TEST_DIR = join(__dirname, '../../tests'); export const PROJECT = join(TEST_DIR, 'tsconfig.json'); +export const PROJECT_TRANSPILE_ONLY = join( + TEST_DIR, + 'tsconfig-transpile-only.json' +); export const BIN_PATH = join(TEST_DIR, 'node_modules/.bin/ts-node'); export const BIN_PATH_JS = join(TEST_DIR, 'node_modules/ts-node/dist/bin.js'); export const BIN_SCRIPT_PATH = join( @@ -35,6 +39,8 @@ export const BIN_ESM_PATH = join(TEST_DIR, 'node_modules/.bin/ts-node-esm'); //#region command lines /** Default `ts-node --project` invocation */ export const CMD_TS_NODE_WITH_PROJECT_FLAG = `"${BIN_PATH}" --project "${PROJECT}"`; +/** Default `ts-node --project` invocation with transpile-only */ +export const CMD_TS_NODE_WITH_PROJECT_TRANSPILE_ONLY_FLAG = `"${BIN_PATH}" --project "${PROJECT_TRANSPILE_ONLY}"`; /** Default `ts-node` invocation without `--project` */ export const CMD_TS_NODE_WITHOUT_PROJECT_FLAG = `"${BIN_PATH}"`; export const EXPERIMENTAL_MODULES_FLAG = semver.gte(process.version, '12.17.0') diff --git a/src/test/index.spec.ts b/src/test/index.spec.ts index 72faf86ea..178779cef 100644 --- a/src/test/index.spec.ts +++ b/src/test/index.spec.ts @@ -5,6 +5,7 @@ import { tmpdir } from 'os'; import semver = require('semver'); import { BIN_PATH_JS, + CMD_TS_NODE_WITH_PROJECT_TRANSPILE_ONLY_FLAG, nodeSupportsEsmHooks, ts, tsSupportsShowConfig, @@ -377,7 +378,7 @@ test.suite('ts-node', (test) => { } test('should pipe into `ts-node` and evaluate', async () => { - const execPromise = exec(CMD_TS_NODE_WITH_PROJECT_FLAG); + const execPromise = exec(CMD_TS_NODE_WITH_PROJECT_TRANSPILE_ONLY_FLAG); execPromise.child.stdin!.end("console.log('hello')"); const { err, stdout } = await execPromise; expect(err).toBe(null); @@ -385,7 +386,9 @@ test.suite('ts-node', (test) => { }); test('should pipe into `ts-node`', async () => { - const execPromise = exec(`${CMD_TS_NODE_WITH_PROJECT_FLAG} -p`); + const execPromise = exec( + `${CMD_TS_NODE_WITH_PROJECT_TRANSPILE_ONLY_FLAG} -p` + ); execPromise.child.stdin!.end('true'); const { err, stdout } = await execPromise; expect(err).toBe(null); @@ -404,7 +407,7 @@ test.suite('ts-node', (test) => { test('should support require flags', async () => { const { err, stdout } = await exec( - `${CMD_TS_NODE_WITH_PROJECT_FLAG} -r ./hello-world -pe "console.log('success')"` + `${CMD_TS_NODE_WITH_PROJECT_TRANSPILE_ONLY_FLAG} -r ./hello-world -pe "console.log('success')"` ); expect(err).toBe(null); expect(stdout).toBe('Hello, world!\nsuccess\nundefined\n'); @@ -412,7 +415,7 @@ test.suite('ts-node', (test) => { test('should support require from node modules', async () => { const { err, stdout } = await exec( - `${CMD_TS_NODE_WITH_PROJECT_FLAG} -r typescript -e "console.log('success')"` + `${CMD_TS_NODE_WITH_PROJECT_TRANSPILE_ONLY_FLAG} -r typescript -e "console.log('success')"` ); expect(err).toBe(null); expect(stdout).toBe('success\n'); @@ -458,28 +461,17 @@ test.suite('ts-node', (test) => { ); }); - test('should preserve `ts-node` context with child process', async () => { - const { err, stdout } = await exec( - `${CMD_TS_NODE_WITH_PROJECT_FLAG} child-process` - ); - expect(err).toBe(null); - expect(stdout).toBe('Hello, world!\n'); - }); - test('should import js before ts by default', async () => { const { err, stdout } = await exec( - `${CMD_TS_NODE_WITH_PROJECT_FLAG} import-order/compiled` + `${CMD_TS_NODE_WITH_PROJECT_TRANSPILE_ONLY_FLAG} import-order/compiled` ); expect(err).toBe(null); expect(stdout).toBe('Hello, JavaScript!\n'); }); - const preferTsExtsEntrypoint = semver.gte(process.version, '12.0.0') - ? 'import-order/compiled' - : 'import-order/require-compiled'; test('should import ts before js when --prefer-ts-exts flag is present', async () => { const { err, stdout } = await exec( - `${CMD_TS_NODE_WITH_PROJECT_FLAG} --prefer-ts-exts ${preferTsExtsEntrypoint}` + `${CMD_TS_NODE_WITH_PROJECT_TRANSPILE_ONLY_FLAG} --prefer-ts-exts import-order/compiled` ); expect(err).toBe(null); expect(stdout).toBe('Hello, TypeScript!\n'); @@ -487,7 +479,7 @@ test.suite('ts-node', (test) => { test('should import ts before js when TS_NODE_PREFER_TS_EXTS env is present', async () => { const { err, stdout } = await exec( - `${CMD_TS_NODE_WITH_PROJECT_FLAG} ${preferTsExtsEntrypoint}`, + `${CMD_TS_NODE_WITH_PROJECT_TRANSPILE_ONLY_FLAG} import-order/compiled`, { env: { ...process.env, TS_NODE_PREFER_TS_EXTS: 'true' }, } @@ -498,7 +490,7 @@ test.suite('ts-node', (test) => { test('should ignore .d.ts files', async () => { const { err, stdout } = await exec( - `${CMD_TS_NODE_WITH_PROJECT_FLAG} import-order/importer` + `${CMD_TS_NODE_WITH_PROJECT_TRANSPILE_ONLY_FLAG} import-order/importer` ); expect(err).toBe(null); expect(stdout).toBe('Hello, World!\n'); @@ -538,63 +530,60 @@ test.suite('ts-node', (test) => { }); }); - if (semver.gte(ts.version, '2.7.0')) { - test('should locate tsconfig relative to entry-point by default', async () => { - const { err, stdout } = await exec(`${BIN_PATH} ../a/index`, { - cwd: join(TEST_DIR, 'cwd-and-script-mode/b'), - }); - expect(err).toBe(null); - expect(stdout).toMatch(/plugin-a/); + test('should locate tsconfig relative to entry-point by default', async () => { + const { err, stdout } = await exec(`${BIN_PATH} ../a/index`, { + cwd: join(TEST_DIR, 'cwd-and-script-mode/b'), }); - test('should locate tsconfig relative to entry-point via ts-node-script', async () => { - const { err, stdout } = await exec(`${BIN_SCRIPT_PATH} ../a/index`, { - cwd: join(TEST_DIR, 'cwd-and-script-mode/b'), - }); - expect(err).toBe(null); - expect(stdout).toMatch(/plugin-a/); - }); - test('should locate tsconfig relative to entry-point with --script-mode', async () => { - const { err, stdout } = await exec( - `${BIN_PATH} --script-mode ../a/index`, - { - cwd: join(TEST_DIR, 'cwd-and-script-mode/b'), - } - ); - expect(err).toBe(null); - expect(stdout).toMatch(/plugin-a/); + expect(err).toBe(null); + expect(stdout).toMatch(/plugin-a/); + }); + test('should locate tsconfig relative to entry-point via ts-node-script', async () => { + const { err, stdout } = await exec(`${BIN_SCRIPT_PATH} ../a/index`, { + cwd: join(TEST_DIR, 'cwd-and-script-mode/b'), }); - test('should locate tsconfig relative to cwd via ts-node-cwd', async () => { - const { err, stdout } = await exec(`${BIN_CWD_PATH} ../a/index`, { + expect(err).toBe(null); + expect(stdout).toMatch(/plugin-a/); + }); + test('should locate tsconfig relative to entry-point with --script-mode', async () => { + const { err, stdout } = await exec( + `${BIN_PATH} --script-mode ../a/index`, + { cwd: join(TEST_DIR, 'cwd-and-script-mode/b'), - }); - expect(err).toBe(null); - expect(stdout).toMatch(/plugin-b/); + } + ); + expect(err).toBe(null); + expect(stdout).toMatch(/plugin-a/); + }); + test('should locate tsconfig relative to cwd via ts-node-cwd', async () => { + const { err, stdout } = await exec(`${BIN_CWD_PATH} ../a/index`, { + cwd: join(TEST_DIR, 'cwd-and-script-mode/b'), }); - test('should locate tsconfig relative to cwd in --cwd-mode', async () => { + expect(err).toBe(null); + expect(stdout).toMatch(/plugin-b/); + }); + test('should locate tsconfig relative to cwd in --cwd-mode', async () => { + const { err, stdout } = await exec(`${BIN_PATH} --cwd-mode ../a/index`, { + cwd: join(TEST_DIR, 'cwd-and-script-mode/b'), + }); + expect(err).toBe(null); + expect(stdout).toMatch(/plugin-b/); + }); + test('should locate tsconfig relative to realpath, not symlink, when entrypoint is a symlink', async (t) => { + if ( + lstatSync( + join(TEST_DIR, 'main-realpath/symlink/symlink.tsx') + ).isSymbolicLink() + ) { const { err, stdout } = await exec( - `${BIN_PATH} --cwd-mode ../a/index`, - { cwd: join(TEST_DIR, 'cwd-and-script-mode/b') } + `${BIN_PATH} main-realpath/symlink/symlink.tsx` ); expect(err).toBe(null); - expect(stdout).toMatch(/plugin-b/); - }); - test('should locate tsconfig relative to realpath, not symlink, when entrypoint is a symlink', async (t) => { - if ( - lstatSync( - join(TEST_DIR, 'main-realpath/symlink/symlink.tsx') - ).isSymbolicLink() - ) { - const { err, stdout } = await exec( - `${BIN_PATH} main-realpath/symlink/symlink.tsx` - ); - expect(err).toBe(null); - expect(stdout).toBe(''); - } else { - t.log('Skipping'); - return; - } - }); - } + expect(stdout).toBe(''); + } else { + t.log('Skipping'); + return; + } + }); test.suite('should read ts-node options from tsconfig.json', (test) => { const BIN_EXEC = `"${BIN_PATH}" --project tsconfig-options/tsconfig.json`; diff --git a/tests/cwd-and-script-mode/a/tsconfig.json b/tests/cwd-and-script-mode/a/tsconfig.json index 196e2b444..377a86016 100644 --- a/tests/cwd-and-script-mode/a/tsconfig.json +++ b/tests/cwd-and-script-mode/a/tsconfig.json @@ -1,4 +1,7 @@ { + "ts-node": { + "transpileOnly": true + }, "compilerOptions": { "plugins": [ { diff --git a/tests/cwd-and-script-mode/b/tsconfig.json b/tests/cwd-and-script-mode/b/tsconfig.json index 3b22d468d..dd98865c1 100644 --- a/tests/cwd-and-script-mode/b/tsconfig.json +++ b/tests/cwd-and-script-mode/b/tsconfig.json @@ -1,4 +1,7 @@ { + "ts-node": { + "transpileOnly": true + }, "compilerOptions": { "plugins": [ { diff --git a/tests/esm-import-assertions/tsconfig.json b/tests/esm-import-assertions/tsconfig.json index d626b9278..731778e97 100644 --- a/tests/esm-import-assertions/tsconfig.json +++ b/tests/esm-import-assertions/tsconfig.json @@ -1,4 +1,7 @@ { + "ts-node": { + "transpileOnly": true + }, "compilerOptions": { "module": "ESNext", "target": "ESNext", diff --git a/tests/recursive-fork/index.ts b/tests/recursive-fork/index.ts index ed68a2080..b426d29bd 100644 --- a/tests/recursive-fork/index.ts +++ b/tests/recursive-fork/index.ts @@ -1,5 +1,9 @@ import { fork } from 'child_process'; +// Type syntax to prove its compiled, though the import above should also +// prove the same +const a = null as any; + console.log(JSON.stringify({ execArgv: process.execArgv, argv: process.argv })); if (process.env.generation !== 'grandchild') { const nextGeneration = diff --git a/tests/tsconfig-transpile-only.json b/tests/tsconfig-transpile-only.json new file mode 100644 index 000000000..1a80ab281 --- /dev/null +++ b/tests/tsconfig-transpile-only.json @@ -0,0 +1,13 @@ +{ + "ts-node": { + "transpileOnly": true + }, + "compilerOptions": { + "target": "es2015", + "jsx": "react", + "noEmit": true, + "strict": true, + // Global type definitions. + "typeRoots": ["./typings", "../node_modules/@types"] + } +} From 31d66449994a19293e7bf0b266d37d735c6ba6eb Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 22:14:45 -0400 Subject: [PATCH 24/30] make tests faster --- tests/main-realpath/target/tsconfig.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/main-realpath/target/tsconfig.json b/tests/main-realpath/target/tsconfig.json index 986627de2..93d434ff5 100644 --- a/tests/main-realpath/target/tsconfig.json +++ b/tests/main-realpath/target/tsconfig.json @@ -1,4 +1,7 @@ { + "ts-node": { + "transpileOnly": true + }, "compilerOptions": { "jsx": "react" } From cdd1f8d38920bd69fc19ba0fe55f0861ae21821b Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 22:40:25 -0400 Subject: [PATCH 25/30] more speed --- src/test/index.spec.ts | 27 +++++++++++--------------- src/test/register.spec.ts | 4 ++-- tests/import-order/require-compiled.js | 2 -- 3 files changed, 13 insertions(+), 20 deletions(-) delete mode 100644 tests/import-order/require-compiled.js diff --git a/src/test/index.spec.ts b/src/test/index.spec.ts index 178779cef..ca5e957ae 100644 --- a/src/test/index.spec.ts +++ b/src/test/index.spec.ts @@ -144,7 +144,10 @@ test.suite('ts-node', (test) => { test('should execute cli with absolute path', async () => { const { err, stdout } = await exec( - `${CMD_TS_NODE_WITH_PROJECT_FLAG} "${join(TEST_DIR, 'hello-world')}"` + `${CMD_TS_NODE_WITH_PROJECT_TRANSPILE_ONLY_FLAG} "${join( + TEST_DIR, + 'hello-world' + )}"` ); expect(err).toBe(null); expect(stdout).toBe('Hello, world!\n'); @@ -158,26 +161,18 @@ test.suite('ts-node', (test) => { expect(stdout).toBe('example\n'); }); - test('should provide registered information globally', async () => { + test("should expose ts-node Service as a symbol property on Node's `process` object", async () => { const { err, stdout } = await exec( - `${CMD_TS_NODE_WITH_PROJECT_FLAG} env` + `${CMD_TS_NODE_WITH_PROJECT_TRANSPILE_ONLY_FLAG} env` ); expect(err).toBe(null); expect(stdout).toBe('object\n'); }); - test('should provide registered information on register', async () => { - const { err, stdout } = await exec(`node -r ts-node/register env.ts`, { - cwd: TEST_DIR, - }); - expect(err).toBe(null); - expect(stdout).toBe('object\n'); - }); - test('should allow js', async () => { const { err, stdout } = await exec( [ - CMD_TS_NODE_WITH_PROJECT_FLAG, + CMD_TS_NODE_WITH_PROJECT_TRANSPILE_ONLY_FLAG, '-O "{\\"allowJs\\":true}"', '-pe "import { main } from \'./allow-js/run\';main()"', ].join(' ') @@ -189,7 +184,7 @@ test.suite('ts-node', (test) => { test('should include jsx when `allow-js` true', async () => { const { err, stdout } = await exec( [ - CMD_TS_NODE_WITH_PROJECT_FLAG, + CMD_TS_NODE_WITH_PROJECT_TRANSPILE_ONLY_FLAG, '-O "{\\"allowJs\\":true}"', '-pe "import { Foo2 } from \'./allow-js/with-jsx\'; Foo2.sayHi()"', ].join(' ') @@ -200,7 +195,7 @@ test.suite('ts-node', (test) => { test('should eval code', async () => { const { err, stdout } = await exec( - `${CMD_TS_NODE_WITH_PROJECT_FLAG} -e "import * as m from './module';console.log(m.example('test'))"` + `${CMD_TS_NODE_WITH_PROJECT_TRANSPILE_ONLY_FLAG} -e "import * as m from './module';console.log(m.example('test'))"` ); expect(err).toBe(null); expect(stdout).toBe('TEST\n'); @@ -208,13 +203,13 @@ test.suite('ts-node', (test) => { test('should import empty files', async () => { const { err, stdout } = await exec( - `${CMD_TS_NODE_WITH_PROJECT_FLAG} -e "import './empty'"` + `${CMD_TS_NODE_WITH_PROJECT_TRANSPILE_ONLY_FLAG} -e "import './empty'"` ); expect(err).toBe(null); expect(stdout).toBe(''); }); - test('should throw errors', async () => { + test('should throw typechecking errors', async () => { const { err } = await exec( `${CMD_TS_NODE_WITH_PROJECT_FLAG} -e "import * as m from './module';console.log(m.example(123))"` ); diff --git a/src/test/register.spec.ts b/src/test/register.spec.ts index 97769283c..f81809b29 100644 --- a/src/test/register.spec.ts +++ b/src/test/register.spec.ts @@ -1,7 +1,7 @@ import { once } from 'lodash'; import { contextTsNodeUnderTest, - PROJECT, + PROJECT_TRANSPILE_ONLY, resetNodeEnvironment, TEST_DIR, tsNodeTypes, @@ -16,7 +16,7 @@ const SOURCE_MAP_REGEXP = /\/\/# sourceMappingURL=data:application\/json;charset=utf\-8;base64,[\w\+]+=*$/; const createOptions: tsNodeTypes.CreateOptions = { - project: PROJECT, + project: PROJECT_TRANSPILE_ONLY, compilerOptions: { jsx: 'preserve', }, diff --git a/tests/import-order/require-compiled.js b/tests/import-order/require-compiled.js deleted file mode 100644 index b73d5a376..000000000 --- a/tests/import-order/require-compiled.js +++ /dev/null @@ -1,2 +0,0 @@ -// indirectly load ./compiled in node < 12 (soon to be end-of-life'd) -require('./compiled'); From 0ddb59d894de531230db71dd20c2b7b29056d756 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 23:14:48 -0400 Subject: [PATCH 26/30] try running fewer tests up-front to give quicker test feedback --- .github/workflows/continuous-integration.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index fba007e2d..335613a36 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -42,16 +42,16 @@ jobs: name: ts-node-packed.tgz path: tests/ts-node-packed.tgz - test: + test-1: &test-1 needs: lint-build name: "Test: ${{ matrix.os }}, node ${{ matrix.node }}, TS ${{ matrix.typescript }}" runs-on: ${{ matrix.os }}-latest - strategy: + strategy: &test-strategy fail-fast: false - matrix: + matrix: &test-strategy-matrix os: [ubuntu, windows] # Don't forget to add all new flavors to this list! - flavor: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] + flavor: [12] include: # Node 12.15 # TODO Add comments about why we test 12.15; I think git blame says it's because of an ESM behavioral change that happened at 12.16 @@ -206,3 +206,11 @@ jobs: uses: codecov/codecov-action@v1 with: flags: ${{ matrix.os }},node_${{ matrix.nodeFlag }},typescript_${{ matrix.typescriptFlag }} + test-2: + <<: *test-1 + needs: test-1 + strategy: + <<: *test-strategy + matrix: + <<: *test-strategy-matrix + flavor: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13] From c460c4504dffc2f27eb3f02ad34033d36da13157 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 23:19:00 -0400 Subject: [PATCH 27/30] try running fewer tests up-front to give quicker test feedback --- .github/workflows/continuous-integration.yml | 168 ++++++++++++++++++- 1 file changed, 162 insertions(+), 6 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 335613a36..0b290de5d 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -42,13 +42,13 @@ jobs: name: ts-node-packed.tgz path: tests/ts-node-packed.tgz - test-1: &test-1 + test-1: needs: lint-build name: "Test: ${{ matrix.os }}, node ${{ matrix.node }}, TS ${{ matrix.typescript }}" runs-on: ${{ matrix.os }}-latest - strategy: &test-strategy + strategy: fail-fast: false - matrix: &test-strategy-matrix + matrix: os: [ubuntu, windows] # Don't forget to add all new flavors to this list! flavor: [12] @@ -207,10 +207,166 @@ jobs: with: flags: ${{ matrix.os }},node_${{ matrix.nodeFlag }},typescript_${{ matrix.typescriptFlag }} test-2: - <<: *test-1 needs: test-1 + name: "Test: ${{ matrix.os }}, node ${{ matrix.node }}, TS ${{ matrix.typescript }}" + runs-on: ${{ matrix.os }}-latest strategy: - <<: *test-strategy + fail-fast: false matrix: - <<: *test-strategy-matrix + os: [ubuntu, windows] + # Don't forget to add all new flavors to this list! flavor: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13] + include: + # Node 12.15 + # TODO Add comments about why we test 12.15; I think git blame says it's because of an ESM behavioral change that happened at 12.16 + - flavor: 1 + node: 12.15 + nodeFlag: 12_15 + typescript: latest + typescriptFlag: latest + # Node 12.16 + # Earliest version that supports getFormat, etc hooks: https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V12.md#12.16.0 + - flavor: 2 + node: 12.16 + nodeFlag: 12_16 + typescript: latest + typescriptFlag: latest + # Node 12 + - flavor: 3 + node: 12 + nodeFlag: 12 + typescript: latest + typescriptFlag: latest + # Node 14.13.0 + # To test ESM builtin module resolution immediately before a node behavioral change: https://github.com/TypeStrong/ts-node/issues/1130 + - flavor: 4 + node: 14.13.0 + nodeFlag: 14_13_0 + typescript: latest + typescriptFlag: latest + # Node 14 + - flavor: 5 + node: 14 + nodeFlag: 14 + typescript: latest + typescriptFlag: latest + - flavor: 6 + node: 14 + nodeFlag: 14 + typescript: 2.7 + typescriptFlag: 2_7 + - flavor: 7 + node: 14 + nodeFlag: 14 + typescript: next + typescriptFlag: next + # Node 16 + # Node 16.11.1 + # Earliest version that supports old ESM Loader Hooks API: https://github.com/TypeStrong/ts-node/pull/1522 + - flavor: 8 + node: 16.11.1 + nodeFlag: 16_11_1 + typescript: latest + typescriptFlag: latest + - flavor: 9 + node: 16 + nodeFlag: 16 + typescript: latest + typescriptFlag: latest + downgradeNpm: true + - flavor: 10 + node: 16 + nodeFlag: 16 + typescript: 2.7 + typescriptFlag: 2_7 + downgradeNpm: true + - flavor: 11 + node: 16 + nodeFlag: 16 + typescript: next + typescriptFlag: next + downgradeNpm: true + # Node 17 + - flavor: 12 + node: 17 + nodeFlag: 17 + typescript: latest + typescriptFlag: latest + downgradeNpm: true + # Node nightly + - flavor: 13 + node: nightly + nodeFlag: nightly + typescript: latest + typescriptFlag: latest + downgradeNpm: true + steps: + # checkout code + - uses: actions/checkout@v2 + # install node + - name: Use Node.js ${{ matrix.node }} + if: matrix.node != 'nightly' + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node }} + - name: Use Node.js 16, will be subsequently overridden by download of nightly + if: matrix.node == 'nightly' + uses: actions/setup-node@v1 + with: + node-version: 16 + - name: Download Node.js nightly + if: matrix.node == 'nightly' && matrix.os == 'ubuntu' + run: | + export N_PREFIX=$(pwd)/n + npm install -g n + n nightly + sudo cp "${N_PREFIX}/bin/node" "$(which node)" + node --version + - name: Download Node.js nightly + if: matrix.node == 'nightly' && matrix.os == 'windows' + run: | + $version = (Invoke-WebRequest https://nodejs.org/download/nightly/index.json | ConvertFrom-json)[0].version + $url = "https://nodejs.org/download/nightly/$version/win-x64/node.exe" + $targetPath = (Get-Command node.exe).Source + Invoke-WebRequest -Uri $url -OutFile $targetPath -UserAgent ([Microsoft.PowerShell.Commands.PSUserAgent]::Chrome) + node --version + # lint, build, test + # Downgrade from npm 7 to 6 because 7 still seems buggy to me + - if: ${{ matrix.downgradeNpm }} + run: npm install -g npm@6 + - run: | + npm config set cache "$( node -p "process.cwd()" )/temp/npm-cache" + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: temp/npm-cache + key: npm-cache-${{ matrix.os }}-${{ hashFiles('package-lock.json') }} + restore-keys: npm-cache-${{matrix.os }}- + - run: npm ci --ignore-scripts + - name: Upload npm logs + if: ${{ failure() }} + uses: actions/upload-artifact@v1 + with: + name: npm-logs + path: temp/npm-cache/_logs + - run: npm run build-tsc + - name: Download package artifact + uses: actions/download-artifact@v1 + with: + name: ts-node-packed.tgz + path: tests/ + - run: npm install typescript@${{ matrix.typescript }} --force + - run: npm run test-cov + - name: Upload npm logs + if: ${{ failure() }} + uses: actions/upload-artifact@v1 + with: + name: npm-logs-${{ matrix.os }}-node-${{ matrix.nodeFlag }}-typescript-${{ matrix.typescriptFlag }} + path: temp/npm-cache/_logs + - run: npm run coverage-report + if: ${{ always() }} + - name: Codecov + if: ${{ always() }} + uses: codecov/codecov-action@v1 + with: + flags: ${{ matrix.os }},node_${{ matrix.nodeFlag }},typescript_${{ matrix.typescriptFlag }} From 6fd38b9e5f351f49ef00d79aeb0a62b1a29f5f82 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 23:20:50 -0400 Subject: [PATCH 28/30] try running fewer tests up-front to give quicker test feedback --- .github/workflows/continuous-integration.yml | 83 -------------------- 1 file changed, 83 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 0b290de5d..b20aded9c 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -53,75 +53,6 @@ jobs: # Don't forget to add all new flavors to this list! flavor: [12] include: - # Node 12.15 - # TODO Add comments about why we test 12.15; I think git blame says it's because of an ESM behavioral change that happened at 12.16 - - flavor: 1 - node: 12.15 - nodeFlag: 12_15 - typescript: latest - typescriptFlag: latest - # Node 12.16 - # Earliest version that supports getFormat, etc hooks: https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V12.md#12.16.0 - - flavor: 2 - node: 12.16 - nodeFlag: 12_16 - typescript: latest - typescriptFlag: latest - # Node 12 - - flavor: 3 - node: 12 - nodeFlag: 12 - typescript: latest - typescriptFlag: latest - # Node 14.13.0 - # To test ESM builtin module resolution immediately before a node behavioral change: https://github.com/TypeStrong/ts-node/issues/1130 - - flavor: 4 - node: 14.13.0 - nodeFlag: 14_13_0 - typescript: latest - typescriptFlag: latest - # Node 14 - - flavor: 5 - node: 14 - nodeFlag: 14 - typescript: latest - typescriptFlag: latest - - flavor: 6 - node: 14 - nodeFlag: 14 - typescript: 2.7 - typescriptFlag: 2_7 - - flavor: 7 - node: 14 - nodeFlag: 14 - typescript: next - typescriptFlag: next - # Node 16 - # Node 16.11.1 - # Earliest version that supports old ESM Loader Hooks API: https://github.com/TypeStrong/ts-node/pull/1522 - - flavor: 8 - node: 16.11.1 - nodeFlag: 16_11_1 - typescript: latest - typescriptFlag: latest - - flavor: 9 - node: 16 - nodeFlag: 16 - typescript: latest - typescriptFlag: latest - downgradeNpm: true - - flavor: 10 - node: 16 - nodeFlag: 16 - typescript: 2.7 - typescriptFlag: 2_7 - downgradeNpm: true - - flavor: 11 - node: 16 - nodeFlag: 16 - typescript: next - typescriptFlag: next - downgradeNpm: true # Node 17 - flavor: 12 node: 17 @@ -129,13 +60,6 @@ jobs: typescript: latest typescriptFlag: latest downgradeNpm: true - # Node nightly - - flavor: 13 - node: nightly - nodeFlag: nightly - typescript: latest - typescriptFlag: latest - downgradeNpm: true steps: # checkout code - uses: actions/checkout@v2 @@ -286,13 +210,6 @@ jobs: typescript: next typescriptFlag: next downgradeNpm: true - # Node 17 - - flavor: 12 - node: 17 - nodeFlag: 17 - typescript: latest - typescriptFlag: latest - downgradeNpm: true # Node nightly - flavor: 13 node: nightly From 1284f538e93df3c4fe9b623d34f658ed47ad39c9 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Fri, 15 Apr 2022 23:36:33 -0400 Subject: [PATCH 29/30] try running fewer tests up-front to give quicker test feedback --- .github/workflows/continuous-integration.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index b20aded9c..6e77314a4 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -97,6 +97,7 @@ jobs: - run: | npm config set cache "$( node -p "process.cwd()" )/temp/npm-cache" - name: Cache dependencies + if: ${{ matrix.os != 'windows' }} uses: actions/cache@v2 with: path: temp/npm-cache @@ -254,6 +255,7 @@ jobs: - run: | npm config set cache "$( node -p "process.cwd()" )/temp/npm-cache" - name: Cache dependencies + if: ${{ matrix.os != 'windows' }} uses: actions/cache@v2 with: path: temp/npm-cache From 7bffd851e2762a6a0c5ffd9b9f1502d13fa8ac35 Mon Sep 17 00:00:00 2001 From: Andrew Bradley Date: Sat, 16 Apr 2022 01:21:07 -0400 Subject: [PATCH 30/30] simplify changes to CI --- .github/workflows/continuous-integration.yml | 100 ++----------------- 1 file changed, 9 insertions(+), 91 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 6e77314a4..dc4f034ab 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -42,7 +42,7 @@ jobs: name: ts-node-packed.tgz path: tests/ts-node-packed.tgz - test-1: + test: needs: lint-build name: "Test: ${{ matrix.os }}, node ${{ matrix.node }}, TS ${{ matrix.typescript }}" runs-on: ${{ matrix.os }}-latest @@ -51,96 +51,7 @@ jobs: matrix: os: [ubuntu, windows] # Don't forget to add all new flavors to this list! - flavor: [12] - include: - # Node 17 - - flavor: 12 - node: 17 - nodeFlag: 17 - typescript: latest - typescriptFlag: latest - downgradeNpm: true - steps: - # checkout code - - uses: actions/checkout@v2 - # install node - - name: Use Node.js ${{ matrix.node }} - if: matrix.node != 'nightly' - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node }} - - name: Use Node.js 16, will be subsequently overridden by download of nightly - if: matrix.node == 'nightly' - uses: actions/setup-node@v1 - with: - node-version: 16 - - name: Download Node.js nightly - if: matrix.node == 'nightly' && matrix.os == 'ubuntu' - run: | - export N_PREFIX=$(pwd)/n - npm install -g n - n nightly - sudo cp "${N_PREFIX}/bin/node" "$(which node)" - node --version - - name: Download Node.js nightly - if: matrix.node == 'nightly' && matrix.os == 'windows' - run: | - $version = (Invoke-WebRequest https://nodejs.org/download/nightly/index.json | ConvertFrom-json)[0].version - $url = "https://nodejs.org/download/nightly/$version/win-x64/node.exe" - $targetPath = (Get-Command node.exe).Source - Invoke-WebRequest -Uri $url -OutFile $targetPath -UserAgent ([Microsoft.PowerShell.Commands.PSUserAgent]::Chrome) - node --version - # lint, build, test - # Downgrade from npm 7 to 6 because 7 still seems buggy to me - - if: ${{ matrix.downgradeNpm }} - run: npm install -g npm@6 - - run: | - npm config set cache "$( node -p "process.cwd()" )/temp/npm-cache" - - name: Cache dependencies - if: ${{ matrix.os != 'windows' }} - uses: actions/cache@v2 - with: - path: temp/npm-cache - key: npm-cache-${{ matrix.os }}-${{ hashFiles('package-lock.json') }} - restore-keys: npm-cache-${{matrix.os }}- - - run: npm ci --ignore-scripts - - name: Upload npm logs - if: ${{ failure() }} - uses: actions/upload-artifact@v1 - with: - name: npm-logs - path: temp/npm-cache/_logs - - run: npm run build-tsc - - name: Download package artifact - uses: actions/download-artifact@v1 - with: - name: ts-node-packed.tgz - path: tests/ - - run: npm install typescript@${{ matrix.typescript }} --force - - run: npm run test-cov - - name: Upload npm logs - if: ${{ failure() }} - uses: actions/upload-artifact@v1 - with: - name: npm-logs-${{ matrix.os }}-node-${{ matrix.nodeFlag }}-typescript-${{ matrix.typescriptFlag }} - path: temp/npm-cache/_logs - - run: npm run coverage-report - if: ${{ always() }} - - name: Codecov - if: ${{ always() }} - uses: codecov/codecov-action@v1 - with: - flags: ${{ matrix.os }},node_${{ matrix.nodeFlag }},typescript_${{ matrix.typescriptFlag }} - test-2: - needs: test-1 - name: "Test: ${{ matrix.os }}, node ${{ matrix.node }}, TS ${{ matrix.typescript }}" - runs-on: ${{ matrix.os }}-latest - strategy: - fail-fast: false - matrix: - os: [ubuntu, windows] - # Don't forget to add all new flavors to this list! - flavor: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13] + flavor: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] include: # Node 12.15 # TODO Add comments about why we test 12.15; I think git blame says it's because of an ESM behavioral change that happened at 12.16 @@ -211,6 +122,13 @@ jobs: typescript: next typescriptFlag: next downgradeNpm: true + # Node 17 + - flavor: 12 + node: 17 + nodeFlag: 17 + typescript: latest + typescriptFlag: latest + downgradeNpm: true # Node nightly - flavor: 13 node: nightly