123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593 |
- // 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.
- 'use strict';
- const {
- ArrayIsArray,
- ArrayPrototypeIncludes,
- ArrayPrototypeJoin,
- ArrayPrototypePush,
- JSONParse,
- ObjectKeys,
- RegExpPrototypeTest,
- SafeMap,
- SafeWeakMap,
- StringPrototypeCharCodeAt,
- StringPrototypeEndsWith,
- StringPrototypeLastIndexOf,
- StringPrototypeIndexOf,
- StringPrototypeMatch,
- StringPrototypeSlice,
- StringPrototypeStartsWith,
- } = require('./node-primordials');
- const { NativeModule } = require('./node-nativemodule');
- const { pathToFileURL, fileURLToPath } = require('url');
- const fs = require('fs');
- const path = require('path');
- const { sep } = path;
- const { internalModuleStat } = require('./node-internalBinding-fs');
- const packageJsonReader = require('./node-internal-modules-package_json_reader');
- const {
- cjsConditions,
- } = require('./node-internal-modules-cjs-helpers');
- const { getOptionValue } = require('./node-options');
- const preserveSymlinks = getOptionValue('--preserve-symlinks');
- const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
- const {normalizeSlashes} = require('../dist/util');
- const {createErrRequireEsm} = require('./node-internal-errors');
- const {
- codes: {
- ERR_INVALID_MODULE_SPECIFIER,
- },
- } = require('./node-internal-errors');
- const {
- CHAR_FORWARD_SLASH,
- } = require('./node-internal-constants');
- const Module = require('module');
- const isWindows = process.platform === 'win32';
- 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 && result >= 0) {
- // Only set cache when `internalModuleStat(filename)` succeeds.
- statCache.set(filename, result);
- }
- return result;
- }
- // Note:
- // we cannot get access to node's internal cache, which is populated from
- // within node's Module constructor. So the cache here will always be empty.
- // It's possible we could approximate our own cache by building it up with
- // hacky workarounds, but it's not worth the complexity and flakiness.
- const moduleParentCache = new SafeWeakMap();
- // Given a module name, and a list of paths to test, returns the first
- // matching file in the following precedence.
- //
- // require("a.<ext>")
- // -> a.<ext>
- //
- // require("a")
- // -> a
- // -> a.<ext>
- // -> a/index.<ext>
- 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;
- }
- /**
- * @param {{
- * nodeEsmResolver: ReturnType<typeof import('./node-internal-modules-esm-resolve').createResolve>,
- * extensions: import('../src/file-extensions').Extensions,
- * preferTsExts
- * }} opts
- */
- function createCjsLoader(opts) {
- const {nodeEsmResolver, preferTsExts} = opts;
- const {replacementsForCjs, replacementsForJs, replacementsForMjs, replacementsForJsx} = opts.extensions;
- const {
- encodedSepRegEx,
- packageExportsResolve,
- packageImportsResolve
- } = nodeEsmResolver;
- function tryPackage(requestPath, exts, isMain, originalPath) {
- // const pkg = readPackage(requestPath)?.main;
- const tmp = readPackage(requestPath)
- const pkg = tmp != null ? tmp.main : undefined;
- if (!pkg) {
- return tryExtensions(path.resolve(requestPath, 'index'), exts, isMain);
- }
- const filename = path.resolve(requestPath, pkg);
- let actual = tryReplacementExtensions(filename, isMain) ||
- 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
- });
- }
- function statReplacementExtensions(p) {
- const lastDotIndex = p.lastIndexOf('.');
- if(lastDotIndex >= 0) {
- const ext = p.slice(lastDotIndex);
- if (ext === '.js' || ext === '.jsx' || ext === '.mjs' || ext === '.cjs') {
- const pathnameWithoutExtension = p.slice(0, lastDotIndex);
- const replacementExts =
- ext === '.js' ? replacementsForJs
- : ext === '.jsx' ? replacementsForJsx
- : ext === '.mjs' ? replacementsForMjs
- : replacementsForCjs;
- for (let i = 0; i < replacementExts.length; i++) {
- const filename = pathnameWithoutExtension + replacementExts[i];
- const rc = stat(filename);
- if (rc === 0) {
- return [rc, filename];
- }
- }
- }
- }
- return [stat(p), p];
- }
- function tryReplacementExtensions(p, isMain) {
- const lastDotIndex = p.lastIndexOf('.');
- if(lastDotIndex >= 0) {
- const ext = p.slice(lastDotIndex);
- if (ext === '.js' || ext === '.jsx' || ext === '.mjs' || ext === '.cjs') {
- const pathnameWithoutExtension = p.slice(0, lastDotIndex);
- const replacementExts =
- ext === '.js' ? replacementsForJs
- : ext === '.jsx' ? replacementsForJsx
- : ext === '.mjs' ? replacementsForMjs
- : replacementsForCjs;
- for (let i = 0; i < replacementExts.length; i++) {
- const filename = tryFile(pathnameWithoutExtension + replacementExts[i], isMain);
- if (filename) {
- return filename;
- }
- }
- }
- }
- return false;
- }
- // 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;
- }
- function trySelfParentPath(parent) {
- if (!parent) return false;
- if (parent.filename) {
- return parent.filename;
- } else if (parent.id === '<repl>' || 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).resolved, 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) {
- if (pkg != null && pkg.exports != null) {
- try {
- return finalizeEsmResolution(packageExportsResolve(
- pathToFileURL(pkgPath + '/package.json'), '.' + expansion, pkg, null,
- cjsConditions).resolved, null, pkgPath);
- } catch (e) {
- if (e.code === 'ERR_MODULE_NOT_FOUND')
- throw createEsmNotFoundErr(request, pkgPath + '/package.json');
- throw e;
- }
- }
- }
- // Backwards compat for old node versions
- const hasModulePathCache = !!require('module')._pathCache;
- const Module_pathCache = Object.create(null);
- const Module_pathCache_get = hasModulePathCache ? (cacheKey) => Module._pathCache[cacheKey] : (cacheKey) => Module_pathCache[cacheKey];
- const Module_pathCache_set = hasModulePathCache ? (cacheKey, value) => (Module._pathCache[cacheKey] = value) : (cacheKey) => (Module_pathCache[cacheKey] = value);
- const trailingSlashRegex = /(?:^|\/)\.?\.$/;
- const Module_findPath = function _findPath(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_get(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, basePath] = statReplacementExtensions(_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_set(cacheKey, filename);
- return filename;
- }
- }
- return false;
- };
- const Module_resolveFilename = function _resolveFilename(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) {
- // node 12 hack
- if (parent != null && parent.filename) {
- if (request[0] === '#') {
- const pkg = readPackageScope(parent.filename) || {};
- // if (pkg.data?.imports != null) {
- // node 12 hack
- if (pkg.data != null && 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 = tryReplacementExtensions(filename) || 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;
- }
- return {
- Module_findPath,
- Module_resolveFilename
- }
- }
- /**
- * 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.classifyModuleByModuleTypeOverrides(normalizeSlashes(filename));
- if(tsNodeClassification.moduleType === 'cjs') return;
- // ignore package.json when file extension is ESM-only or CJS-only
- // [MUST_UPDATE_FOR_NEW_FILE_EXTENSIONS]
- const lastDotIndex = filename.lastIndexOf('.');
- const ext = lastDotIndex >= 0 ? filename.slice(lastDotIndex) : '';
- if((ext === '.cts' || ext === '.cjs') && tsNodeClassification.moduleType === 'auto') return;
- // Function require shouldn't be used in ES modules.
- if (ext === '.mts' || ext === '.mjs' || 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 = {
- createCjsLoader,
- assertScriptCanLoadAsCJSImpl,
- readPackageScope
- };
|