mirror of
https://github.com/denoland/deno.git
synced 2024-12-11 10:07:54 -05:00
21736392dc
Phase 1 node-api rewrite
1292 lines
36 KiB
JavaScript
1292 lines
36 KiB
JavaScript
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
|
|
|
// deno-lint-ignore-file
|
|
|
|
import { core, internals, primordials } from "ext:core/mod.js";
|
|
import {
|
|
op_napi_open,
|
|
op_require_as_file_path,
|
|
op_require_break_on_next_statement,
|
|
op_require_init_paths,
|
|
op_require_is_deno_dir_package,
|
|
op_require_is_request_relative,
|
|
op_require_node_module_paths,
|
|
op_require_package_imports_resolve,
|
|
op_require_path_basename,
|
|
op_require_path_dirname,
|
|
op_require_path_is_absolute,
|
|
op_require_path_resolve,
|
|
op_require_proxy_path,
|
|
op_require_read_closest_package_json,
|
|
op_require_read_file,
|
|
op_require_read_package_scope,
|
|
op_require_real_path,
|
|
op_require_resolve_deno_dir,
|
|
op_require_resolve_exports,
|
|
op_require_resolve_lookup_paths,
|
|
op_require_stat,
|
|
op_require_try_self,
|
|
op_require_try_self_parent_path,
|
|
} from "ext:core/ops";
|
|
const {
|
|
ArrayIsArray,
|
|
ArrayPrototypeIncludes,
|
|
ArrayPrototypeIndexOf,
|
|
ArrayPrototypeJoin,
|
|
ArrayPrototypePush,
|
|
ArrayPrototypeSlice,
|
|
ArrayPrototypeSplice,
|
|
Error,
|
|
JSONParse,
|
|
ObjectCreate,
|
|
ObjectEntries,
|
|
ObjectGetOwnPropertyDescriptor,
|
|
ObjectGetPrototypeOf,
|
|
ObjectHasOwn,
|
|
ObjectKeys,
|
|
ObjectPrototype,
|
|
ObjectSetPrototypeOf,
|
|
Proxy,
|
|
RegExpPrototypeTest,
|
|
SafeArrayIterator,
|
|
SafeMap,
|
|
SafeWeakMap,
|
|
String,
|
|
StringPrototypeCharCodeAt,
|
|
StringPrototypeEndsWith,
|
|
StringPrototypeIncludes,
|
|
StringPrototypeIndexOf,
|
|
StringPrototypeMatch,
|
|
StringPrototypeSlice,
|
|
StringPrototypeSplit,
|
|
StringPrototypeStartsWith,
|
|
TypeError,
|
|
} = primordials;
|
|
|
|
import { nodeGlobals } from "ext:deno_node/00_globals.js";
|
|
|
|
import _httpAgent from "ext:deno_node/_http_agent.mjs";
|
|
import _httpOutgoing from "ext:deno_node/_http_outgoing.ts";
|
|
import _streamDuplex from "ext:deno_node/internal/streams/duplex.mjs";
|
|
import _streamPassthrough from "ext:deno_node/internal/streams/passthrough.mjs";
|
|
import _streamReadable from "ext:deno_node/internal/streams/readable.mjs";
|
|
import _streamTransform from "ext:deno_node/internal/streams/transform.mjs";
|
|
import _streamWritable from "ext:deno_node/internal/streams/writable.mjs";
|
|
import assert from "node:assert";
|
|
import assertStrict from "node:assert/strict";
|
|
import asyncHooks from "node:async_hooks";
|
|
import buffer from "node:buffer";
|
|
import childProcess from "node:child_process";
|
|
import cluster from "node:cluster";
|
|
import console from "node:console";
|
|
import constants from "node:constants";
|
|
import crypto from "node:crypto";
|
|
import dgram from "node:dgram";
|
|
import diagnosticsChannel from "node:diagnostics_channel";
|
|
import dns from "node:dns";
|
|
import dnsPromises from "node:dns/promises";
|
|
import domain from "node:domain";
|
|
import events from "node:events";
|
|
import fs from "node:fs";
|
|
import fsPromises from "node:fs/promises";
|
|
import http from "node:http";
|
|
import http2 from "node:http2";
|
|
import https from "node:https";
|
|
import inspector from "ext:deno_node/inspector.ts";
|
|
import internalCp from "ext:deno_node/internal/child_process.ts";
|
|
import internalCryptoCertificate from "ext:deno_node/internal/crypto/certificate.ts";
|
|
import internalCryptoCipher from "ext:deno_node/internal/crypto/cipher.ts";
|
|
import internalCryptoDiffiehellman from "ext:deno_node/internal/crypto/diffiehellman.ts";
|
|
import internalCryptoHash from "ext:deno_node/internal/crypto/hash.ts";
|
|
import internalCryptoHkdf from "ext:deno_node/internal/crypto/hkdf.ts";
|
|
import internalCryptoKeygen from "ext:deno_node/internal/crypto/keygen.ts";
|
|
import internalCryptoKeys from "ext:deno_node/internal/crypto/keys.ts";
|
|
import internalCryptoPbkdf2 from "ext:deno_node/internal/crypto/pbkdf2.ts";
|
|
import internalCryptoRandom from "ext:deno_node/internal/crypto/random.ts";
|
|
import internalCryptoScrypt from "ext:deno_node/internal/crypto/scrypt.ts";
|
|
import internalCryptoSig from "ext:deno_node/internal/crypto/sig.ts";
|
|
import internalCryptoUtil from "ext:deno_node/internal/crypto/util.ts";
|
|
import internalCryptoX509 from "ext:deno_node/internal/crypto/x509.ts";
|
|
import internalDgram from "ext:deno_node/internal/dgram.ts";
|
|
import internalDnsPromises from "ext:deno_node/internal/dns/promises.ts";
|
|
import internalErrors from "ext:deno_node/internal/errors.ts";
|
|
import internalEventTarget from "ext:deno_node/internal/event_target.mjs";
|
|
import internalFsUtils from "ext:deno_node/internal/fs/utils.mjs";
|
|
import internalHttp from "ext:deno_node/internal/http.ts";
|
|
import internalReadlineUtils from "ext:deno_node/internal/readline/utils.mjs";
|
|
import internalStreamsAddAbortSignal from "ext:deno_node/internal/streams/add-abort-signal.mjs";
|
|
import internalStreamsBufferList from "ext:deno_node/internal/streams/buffer_list.mjs";
|
|
import internalStreamsLazyTransform from "ext:deno_node/internal/streams/lazy_transform.mjs";
|
|
import internalStreamsState from "ext:deno_node/internal/streams/state.mjs";
|
|
import internalTestBinding from "ext:deno_node/internal/test/binding.ts";
|
|
import internalTimers from "ext:deno_node/internal/timers.mjs";
|
|
import internalUtil from "ext:deno_node/internal/util.mjs";
|
|
import internalUtilInspect from "ext:deno_node/internal/util/inspect.mjs";
|
|
import net from "node:net";
|
|
import os from "node:os";
|
|
import pathPosix from "node:path/posix";
|
|
import pathWin32 from "node:path/win32";
|
|
import path from "node:path";
|
|
import perfHooks from "node:perf_hooks";
|
|
import punycode from "node:punycode";
|
|
import process from "node:process";
|
|
import querystring from "node:querystring";
|
|
import readline from "node:readline";
|
|
import readlinePromises from "ext:deno_node/readline/promises.ts";
|
|
import repl from "node:repl";
|
|
import stream from "node:stream";
|
|
import streamConsumers from "node:stream/consumers";
|
|
import streamPromises from "node:stream/promises";
|
|
import streamWeb from "node:stream/web";
|
|
import stringDecoder from "node:string_decoder";
|
|
import sys from "node:sys";
|
|
import test from "node:test";
|
|
import timers from "node:timers";
|
|
import timersPromises from "node:timers/promises";
|
|
import tls from "node:tls";
|
|
import tty from "node:tty";
|
|
import url from "node:url";
|
|
import utilTypes from "node:util/types";
|
|
import util from "node:util";
|
|
import v8 from "node:v8";
|
|
import vm from "node:vm";
|
|
import workerThreads from "node:worker_threads";
|
|
import wasi from "ext:deno_node/wasi.ts";
|
|
import zlib from "node:zlib";
|
|
|
|
const nativeModuleExports = ObjectCreate(null);
|
|
const builtinModules = [];
|
|
|
|
// NOTE(bartlomieju): keep this list in sync with `ext/node/polyfill.rs`
|
|
function setupBuiltinModules() {
|
|
const nodeModules = {
|
|
"_http_agent": _httpAgent,
|
|
"_http_outgoing": _httpOutgoing,
|
|
"_stream_duplex": _streamDuplex,
|
|
"_stream_passthrough": _streamPassthrough,
|
|
"_stream_readable": _streamReadable,
|
|
"_stream_transform": _streamTransform,
|
|
"_stream_writable": _streamWritable,
|
|
assert,
|
|
"assert/strict": assertStrict,
|
|
"async_hooks": asyncHooks,
|
|
buffer,
|
|
crypto,
|
|
console,
|
|
constants,
|
|
child_process: childProcess,
|
|
cluster,
|
|
dgram,
|
|
diagnostics_channel: diagnosticsChannel,
|
|
dns,
|
|
"dns/promises": dnsPromises,
|
|
domain,
|
|
events,
|
|
fs,
|
|
"fs/promises": fsPromises,
|
|
http,
|
|
http2,
|
|
https,
|
|
inspector,
|
|
"internal/child_process": internalCp,
|
|
"internal/crypto/certificate": internalCryptoCertificate,
|
|
"internal/crypto/cipher": internalCryptoCipher,
|
|
"internal/crypto/diffiehellman": internalCryptoDiffiehellman,
|
|
"internal/crypto/hash": internalCryptoHash,
|
|
"internal/crypto/hkdf": internalCryptoHkdf,
|
|
"internal/crypto/keygen": internalCryptoKeygen,
|
|
"internal/crypto/keys": internalCryptoKeys,
|
|
"internal/crypto/pbkdf2": internalCryptoPbkdf2,
|
|
"internal/crypto/random": internalCryptoRandom,
|
|
"internal/crypto/scrypt": internalCryptoScrypt,
|
|
"internal/crypto/sig": internalCryptoSig,
|
|
"internal/crypto/util": internalCryptoUtil,
|
|
"internal/crypto/x509": internalCryptoX509,
|
|
"internal/dgram": internalDgram,
|
|
"internal/dns/promises": internalDnsPromises,
|
|
"internal/errors": internalErrors,
|
|
"internal/event_target": internalEventTarget,
|
|
"internal/fs/utils": internalFsUtils,
|
|
"internal/http": internalHttp,
|
|
"internal/readline/utils": internalReadlineUtils,
|
|
"internal/streams/add-abort-signal": internalStreamsAddAbortSignal,
|
|
"internal/streams/buffer_list": internalStreamsBufferList,
|
|
"internal/streams/lazy_transform": internalStreamsLazyTransform,
|
|
"internal/streams/state": internalStreamsState,
|
|
"internal/test/binding": internalTestBinding,
|
|
"internal/timers": internalTimers,
|
|
"internal/util/inspect": internalUtilInspect,
|
|
"internal/util": internalUtil,
|
|
net,
|
|
module: Module,
|
|
os,
|
|
"path/posix": pathPosix,
|
|
"path/win32": pathWin32,
|
|
path,
|
|
perf_hooks: perfHooks,
|
|
process,
|
|
get punycode() {
|
|
process.emitWarning(
|
|
"The `punycode` module is deprecated. Please use a userland " +
|
|
"alternative instead.",
|
|
"DeprecationWarning",
|
|
"DEP0040",
|
|
);
|
|
return punycode;
|
|
},
|
|
querystring,
|
|
readline,
|
|
"readline/promises": readlinePromises,
|
|
repl,
|
|
stream,
|
|
"stream/consumers": streamConsumers,
|
|
"stream/promises": streamPromises,
|
|
"stream/web": streamWeb,
|
|
string_decoder: stringDecoder,
|
|
sys,
|
|
test,
|
|
timers,
|
|
"timers/promises": timersPromises,
|
|
tls,
|
|
tty,
|
|
url,
|
|
util,
|
|
"util/types": utilTypes,
|
|
v8,
|
|
vm,
|
|
wasi,
|
|
worker_threads: workerThreads,
|
|
zlib,
|
|
};
|
|
for (const [name, moduleExports] of ObjectEntries(nodeModules)) {
|
|
nativeModuleExports[name] = moduleExports;
|
|
ArrayPrototypePush(builtinModules, name);
|
|
}
|
|
}
|
|
setupBuiltinModules();
|
|
|
|
function pathDirname(filepath) {
|
|
if (filepath == null) {
|
|
throw new Error("Empty filepath.");
|
|
} else if (filepath === "") {
|
|
return ".";
|
|
}
|
|
return op_require_path_dirname(filepath);
|
|
}
|
|
|
|
function pathResolve(...args) {
|
|
return op_require_path_resolve(args);
|
|
}
|
|
|
|
const nativeModulePolyfill = new SafeMap();
|
|
|
|
const relativeResolveCache = ObjectCreate(null);
|
|
let requireDepth = 0;
|
|
let statCache = null;
|
|
let mainModule = null;
|
|
let hasBrokenOnInspectBrk = false;
|
|
let hasInspectBrk = false;
|
|
// Are we running with --node-modules-dir flag or byonm?
|
|
let usesLocalNodeModulesDir = false;
|
|
|
|
function stat(filename) {
|
|
// TODO: required only on windows
|
|
// filename = path.toNamespacedPath(filename);
|
|
if (statCache !== null) {
|
|
const result = statCache.get(filename);
|
|
if (result !== undefined) {
|
|
return result;
|
|
}
|
|
}
|
|
const result = op_require_stat(filename);
|
|
if (statCache !== null && result >= 0) {
|
|
statCache.set(filename, result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function updateChildren(parent, child, scan) {
|
|
if (!parent) {
|
|
return;
|
|
}
|
|
|
|
const children = parent.children;
|
|
if (children && !(scan && ArrayPrototypeIncludes(children, child))) {
|
|
ArrayPrototypePush(children, child);
|
|
}
|
|
}
|
|
|
|
function tryFile(requestPath, _isMain) {
|
|
const rc = stat(requestPath);
|
|
if (rc !== 0) return;
|
|
return toRealPath(requestPath);
|
|
}
|
|
|
|
function tryPackage(requestPath, exts, isMain, originalPath) {
|
|
const packageJsonPath = pathResolve(
|
|
requestPath,
|
|
"package.json",
|
|
);
|
|
const pkg = op_require_read_package_scope(packageJsonPath)?.main;
|
|
if (!pkg) {
|
|
return tryExtensions(
|
|
pathResolve(requestPath, "index"),
|
|
exts,
|
|
isMain,
|
|
);
|
|
}
|
|
|
|
const filename = pathResolve(requestPath, pkg);
|
|
let actual = tryFile(filename, isMain) ||
|
|
tryExtensions(filename, exts, isMain) ||
|
|
tryExtensions(
|
|
pathResolve(filename, "index"),
|
|
exts,
|
|
isMain,
|
|
);
|
|
if (actual === false) {
|
|
actual = tryExtensions(
|
|
pathResolve(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 = pathResolve(
|
|
requestPath,
|
|
"package.json",
|
|
);
|
|
err.requestPath = originalPath;
|
|
throw err;
|
|
} else {
|
|
process.emitWarning(
|
|
`Invalid 'main' field in '${packageJsonPath}' of '${pkg}'. ` +
|
|
"Please either fix that or report it to the module author",
|
|
"DeprecationWarning",
|
|
"DEP0128",
|
|
);
|
|
}
|
|
}
|
|
return actual;
|
|
}
|
|
|
|
const realpathCache = new SafeMap();
|
|
function toRealPath(requestPath) {
|
|
const maybeCached = realpathCache.get(requestPath);
|
|
if (maybeCached) {
|
|
return maybeCached;
|
|
}
|
|
const rp = op_require_real_path(requestPath);
|
|
realpathCache.set(requestPath, rp);
|
|
return rp;
|
|
}
|
|
|
|
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 = op_require_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 getExportsForCircularRequire(module) {
|
|
if (
|
|
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;
|
|
}
|
|
|
|
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 (
|
|
ObjectHasOwn(target, prop) || prop === "__esModule"
|
|
) {
|
|
return ObjectGetOwnPropertyDescriptor(target, prop);
|
|
}
|
|
emitCircularRequireWarning(prop);
|
|
return undefined;
|
|
},
|
|
});
|
|
|
|
const moduleParentCache = new SafeWeakMap();
|
|
function Module(id = "", parent) {
|
|
this.id = id;
|
|
this.path = pathDirname(id);
|
|
this.exports = {};
|
|
moduleParentCache.set(this, parent);
|
|
updateChildren(parent, this, false);
|
|
this.filename = null;
|
|
this.loaded = false;
|
|
this.children = [];
|
|
}
|
|
|
|
Module.builtinModules = builtinModules;
|
|
|
|
Module._extensions = ObjectCreate(null);
|
|
Module._cache = ObjectCreate(null);
|
|
Module._pathCache = ObjectCreate(null);
|
|
let modulePaths = [];
|
|
Module.globalPaths = modulePaths;
|
|
|
|
const CHAR_FORWARD_SLASH = 47;
|
|
const TRAILING_SLASH_REGEX = /(?:^|\/)\.?\.$/;
|
|
|
|
// This only applies to requests of a specific form:
|
|
// 1. name/.*
|
|
// 2. @scope/name/.*
|
|
const EXPORTS_PATTERN = /^((?:@[^/\\%]+\/)?[^./\\%][^/\\%]*)(\/.*)?$/;
|
|
function resolveExports(
|
|
modulesPath,
|
|
request,
|
|
parentPath,
|
|
usesLocalNodeModulesDir,
|
|
) {
|
|
// The implementation's behavior is meant to mirror resolution in ESM.
|
|
const [, name, expansion = ""] =
|
|
StringPrototypeMatch(request, EXPORTS_PATTERN) || [];
|
|
if (!name) {
|
|
return;
|
|
}
|
|
|
|
if (!parentPath) {
|
|
return false;
|
|
}
|
|
|
|
return op_require_resolve_exports(
|
|
usesLocalNodeModulesDir,
|
|
modulesPath,
|
|
request,
|
|
name,
|
|
expansion,
|
|
parentPath,
|
|
) ?? false;
|
|
}
|
|
|
|
Module._findPath = function (request, paths, isMain, parentPath) {
|
|
const absoluteRequest = op_require_path_is_absolute(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(TRAILING_SLASH_REGEX, 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,
|
|
parentPath,
|
|
usesLocalNodeModulesDir,
|
|
);
|
|
if (exportsResolved) {
|
|
return exportsResolved;
|
|
}
|
|
}
|
|
|
|
let basePath;
|
|
|
|
if (usesLocalNodeModulesDir) {
|
|
basePath = pathResolve(curPath, request);
|
|
} else {
|
|
const isDenoDirPackage = op_require_is_deno_dir_package(
|
|
curPath,
|
|
);
|
|
const isRelative = op_require_is_request_relative(
|
|
request,
|
|
);
|
|
basePath = (isDenoDirPackage && !isRelative)
|
|
? pathResolve(curPath, packageSpecifierSubPath(request))
|
|
: pathResolve(curPath, request);
|
|
}
|
|
let filename;
|
|
|
|
const rc = stat(basePath);
|
|
if (!trailingSlash) {
|
|
if (rc === 0) { // File.
|
|
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;
|
|
};
|
|
|
|
/**
|
|
* Get a list of potential module directories
|
|
* @param {string} fromPath The directory name of the module
|
|
* @returns {string[]} List of module directories
|
|
*/
|
|
Module._nodeModulePaths = function (fromPath) {
|
|
return op_require_node_module_paths(fromPath);
|
|
};
|
|
|
|
Module._resolveLookupPaths = function (request, parent) {
|
|
const paths = [];
|
|
|
|
if (op_require_is_request_relative(request)) {
|
|
ArrayPrototypePush(
|
|
paths,
|
|
parent?.filename ? op_require_path_dirname(parent.filename) : ".",
|
|
);
|
|
return paths;
|
|
}
|
|
|
|
if (
|
|
!usesLocalNodeModulesDir && parent?.filename && parent.filename.length > 0
|
|
) {
|
|
const denoDirPath = op_require_resolve_deno_dir(
|
|
request,
|
|
parent.filename,
|
|
);
|
|
if (denoDirPath) {
|
|
ArrayPrototypePush(paths, denoDirPath);
|
|
}
|
|
}
|
|
const lookupPathsResult = op_require_resolve_lookup_paths(
|
|
request,
|
|
parent?.paths,
|
|
parent?.filename ?? "",
|
|
);
|
|
if (lookupPathsResult) {
|
|
ArrayPrototypePush(paths, ...new SafeArrayIterator(lookupPathsResult));
|
|
}
|
|
return paths;
|
|
};
|
|
|
|
Module._load = function (request, parent, isMain) {
|
|
let relResolveCacheIdentifier;
|
|
if (parent) {
|
|
// 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, id);
|
|
if (!module) {
|
|
// TODO:
|
|
// throw new ERR_UNKNOWN_BUILTIN_MODULE(filename);
|
|
throw new Error("Unknown built-in module");
|
|
}
|
|
|
|
return module.exports;
|
|
}
|
|
|
|
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
|
|
) {
|
|
return mod.exports;
|
|
}
|
|
// Don't call updateChildren(), Module constructor already does.
|
|
const module = cachedModule || new Module(filename, parent);
|
|
|
|
if (isMain) {
|
|
process.mainModule = module;
|
|
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 &&
|
|
ObjectGetPrototypeOf(module.exports) ===
|
|
CircularRequirePrototypeWarningProxy
|
|
) {
|
|
ObjectSetPrototypeOf(module.exports, ObjectPrototype);
|
|
}
|
|
}
|
|
|
|
return module.exports;
|
|
};
|
|
|
|
Module._resolveFilename = function (
|
|
request,
|
|
parent,
|
|
isMain,
|
|
options,
|
|
) {
|
|
if (
|
|
StringPrototypeStartsWith(request, "node:") ||
|
|
nativeModuleCanBeRequiredByUsers(request)
|
|
) {
|
|
return request;
|
|
}
|
|
|
|
let paths;
|
|
|
|
if (typeof options === "object" && options !== null) {
|
|
if (ArrayIsArray(options.paths)) {
|
|
const isRelative = op_require_is_request_relative(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 {
|
|
// TODO:
|
|
// throw new ERR_INVALID_ARG_VALUE("options.paths", options.paths);
|
|
throw new Error("Invalid arg value options.paths", options.path);
|
|
}
|
|
} else {
|
|
paths = Module._resolveLookupPaths(request, parent);
|
|
}
|
|
|
|
if (parent?.filename) {
|
|
if (request[0] === "#") {
|
|
const maybeResolved = op_require_package_imports_resolve(
|
|
parent.filename,
|
|
request,
|
|
);
|
|
if (maybeResolved) {
|
|
return maybeResolved;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try module self resolution first
|
|
const parentPath = op_require_try_self_parent_path(
|
|
!!parent,
|
|
parent?.filename,
|
|
parent?.id,
|
|
);
|
|
const selfResolved = op_require_try_self(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,
|
|
parentPath,
|
|
);
|
|
if (filename) {
|
|
return op_require_real_path(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;
|
|
|
|
// fallback and attempt to resolve bare specifiers using
|
|
// the global cache when not using --node-modules-dir
|
|
if (
|
|
!usesLocalNodeModulesDir &&
|
|
ArrayIsArray(options?.paths) &&
|
|
request[0] !== "." &&
|
|
request[0] !== "#" &&
|
|
!request.startsWith("file:///") &&
|
|
!op_require_is_request_relative(request) &&
|
|
!op_require_path_is_absolute(request)
|
|
) {
|
|
try {
|
|
return Module._resolveFilename(request, parent, isMain, {
|
|
...options,
|
|
paths: undefined,
|
|
});
|
|
} catch {
|
|
// ignore
|
|
}
|
|
}
|
|
|
|
// throw the original error
|
|
throw err;
|
|
};
|
|
|
|
/**
|
|
* Internal CommonJS API to always require modules before requiring the actual
|
|
* one when calling `require("my-module")`. This is used by require hooks such
|
|
* as `ts-node/register`.
|
|
* @param {string[]} requests List of modules to preload
|
|
*/
|
|
Module._preloadModules = function (requests) {
|
|
if (!ArrayIsArray(requests) || requests.length === 0) {
|
|
return;
|
|
}
|
|
|
|
const parent = new Module("internal/preload", null);
|
|
// All requested files must be resolved against cwd
|
|
parent.paths = Module._nodeModulePaths(process.cwd());
|
|
for (let i = 0; i < requests.length; i++) {
|
|
parent.require(requests[i]);
|
|
}
|
|
};
|
|
|
|
Module.prototype.load = function (filename) {
|
|
if (this.loaded) {
|
|
throw Error("Module already loaded");
|
|
}
|
|
|
|
// Canonicalize the path so it's not pointing to the symlinked directory
|
|
// in `node_modules` directory of the referrer.
|
|
this.filename = op_require_real_path(filename);
|
|
this.paths = Module._nodeModulePaths(
|
|
pathDirname(this.filename),
|
|
);
|
|
const extension = findLongestRegisteredExtension(filename);
|
|
// allow .mjs to be overridden
|
|
if (
|
|
StringPrototypeEndsWith(filename, ".mjs") && !Module._extensions[".mjs"]
|
|
) {
|
|
throw createRequireEsmError(
|
|
filename,
|
|
moduleParentCache.get(this)?.filename,
|
|
);
|
|
}
|
|
|
|
Module._extensions[extension](this, this.filename);
|
|
this.loaded = true;
|
|
|
|
// TODO: do caching
|
|
};
|
|
|
|
// Loads a module at the given file path. Returns that module's
|
|
// `exports` property.
|
|
Module.prototype.require = function (id) {
|
|
if (typeof id !== "string") {
|
|
// TODO(bartlomieju): it should use different error type
|
|
// ("ERR_INVALID_ARG_VALUE")
|
|
throw new TypeError("Invalid argument type");
|
|
}
|
|
|
|
if (id === "") {
|
|
// TODO(bartlomieju): it should use different error type
|
|
// ("ERR_INVALID_ARG_VALUE")
|
|
throw new TypeError("id must be non empty");
|
|
}
|
|
requireDepth++;
|
|
try {
|
|
return Module._load(id, this, /* isMain */ false);
|
|
} finally {
|
|
requireDepth--;
|
|
}
|
|
};
|
|
|
|
// The module wrapper looks slightly different to Node. Instead of using one
|
|
// wrapper function, we use two. The first one exists to performance optimize
|
|
// access to magic node globals, like `Buffer` or `process`. The second one
|
|
// is the actual wrapper function we run the users code in.
|
|
// The only observable difference is that in Deno `arguments.callee` is not
|
|
// null.
|
|
Module.wrapper = [
|
|
"(function (exports, require, module, __filename, __dirname, Buffer, clearImmediate, clearInterval, clearTimeout, console, global, process, setImmediate, setInterval, setTimeout, performance) { (function (exports, require, module, __filename, __dirname) {",
|
|
"\n}).call(this, exports, require, module, __filename, __dirname); })",
|
|
];
|
|
Module.wrap = function (script) {
|
|
script = script.replace(/^#!.*?\n/, "");
|
|
return `${Module.wrapper[0]}${script}${Module.wrapper[1]}`;
|
|
};
|
|
|
|
function isEsmSyntaxError(error) {
|
|
return error instanceof SyntaxError && (
|
|
StringPrototypeIncludes(
|
|
error.message,
|
|
"Cannot use import statement outside a module",
|
|
) ||
|
|
StringPrototypeIncludes(error.message, "Unexpected token 'export'")
|
|
);
|
|
}
|
|
|
|
function enrichCJSError(error) {
|
|
if (isEsmSyntaxError(error)) {
|
|
console.error(
|
|
'To load an ES module, set "type": "module" in the package.json or use ' +
|
|
"the .mjs extension.",
|
|
);
|
|
}
|
|
}
|
|
|
|
function wrapSafe(
|
|
filename,
|
|
content,
|
|
cjsModuleInstance,
|
|
) {
|
|
const wrapper = Module.wrap(content);
|
|
const [f, err] = core.evalContext(wrapper, `file://${filename}`);
|
|
if (err) {
|
|
if (process.mainModule === cjsModuleInstance) {
|
|
enrichCJSError(err.thrown);
|
|
}
|
|
if (isEsmSyntaxError(err.thrown)) {
|
|
throw createRequireEsmError(
|
|
filename,
|
|
moduleParentCache.get(cjsModuleInstance)?.filename,
|
|
);
|
|
} else {
|
|
throw err.thrown;
|
|
}
|
|
}
|
|
return f;
|
|
}
|
|
|
|
Module.prototype._compile = function (content, filename) {
|
|
const compiledWrapper = wrapSafe(filename, content, this);
|
|
|
|
const dirname = pathDirname(filename);
|
|
const require = makeRequireFunction(this);
|
|
const exports = this.exports;
|
|
const thisValue = exports;
|
|
if (requireDepth === 0) {
|
|
statCache = new SafeMap();
|
|
}
|
|
|
|
if (hasInspectBrk && !hasBrokenOnInspectBrk) {
|
|
hasBrokenOnInspectBrk = true;
|
|
op_require_break_on_next_statement();
|
|
}
|
|
|
|
const {
|
|
Buffer,
|
|
clearImmediate,
|
|
clearInterval,
|
|
clearTimeout,
|
|
console,
|
|
global,
|
|
process,
|
|
setImmediate,
|
|
setInterval,
|
|
setTimeout,
|
|
performance,
|
|
} = nodeGlobals;
|
|
|
|
const result = compiledWrapper.call(
|
|
thisValue,
|
|
exports,
|
|
require,
|
|
this,
|
|
filename,
|
|
dirname,
|
|
Buffer,
|
|
clearImmediate,
|
|
clearInterval,
|
|
clearTimeout,
|
|
console,
|
|
global,
|
|
process,
|
|
setImmediate,
|
|
setInterval,
|
|
setTimeout,
|
|
performance,
|
|
);
|
|
if (requireDepth === 0) {
|
|
statCache = null;
|
|
}
|
|
return result;
|
|
};
|
|
|
|
Module._extensions[".js"] = function (module, filename) {
|
|
const content = op_require_read_file(filename);
|
|
|
|
if (StringPrototypeEndsWith(filename, ".js")) {
|
|
const pkg = op_require_read_closest_package_json(filename);
|
|
if (pkg && pkg.exists && pkg.typ === "module") {
|
|
throw createRequireEsmError(
|
|
filename,
|
|
moduleParentCache.get(module)?.filename,
|
|
);
|
|
}
|
|
}
|
|
|
|
module._compile(content, filename);
|
|
};
|
|
|
|
function createRequireEsmError(filename, parent) {
|
|
let message = `require() of ES Module ${filename}`;
|
|
|
|
if (parent) {
|
|
message += ` from ${parent}`;
|
|
}
|
|
|
|
message +=
|
|
` not supported. Instead change the require to a dynamic import() which is available in all CommonJS modules.`;
|
|
const err = new Error(message);
|
|
err.code = "ERR_REQUIRE_ESM";
|
|
return err;
|
|
}
|
|
|
|
function stripBOM(content) {
|
|
if (StringPrototypeCharCodeAt(content, 0) === 0xfeff) {
|
|
content = StringPrototypeSlice(content, 1);
|
|
}
|
|
return content;
|
|
}
|
|
|
|
// Native extension for .json
|
|
Module._extensions[".json"] = function (module, filename) {
|
|
const content = op_require_read_file(filename);
|
|
|
|
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 (filename.endsWith("fsevents.node")) {
|
|
throw new Error("Using fsevents module is currently not supported");
|
|
}
|
|
module.exports = op_napi_open(
|
|
filename,
|
|
globalThis,
|
|
nodeGlobals.Buffer,
|
|
reportError,
|
|
);
|
|
};
|
|
|
|
function createRequireFromPath(filename) {
|
|
const proxyPath = op_require_proxy_path(filename);
|
|
const mod = new Module(proxyPath);
|
|
mod.filename = proxyPath;
|
|
mod.paths = Module._nodeModulePaths(mod.path);
|
|
return makeRequireFunction(mod);
|
|
}
|
|
|
|
function makeRequireFunction(mod) {
|
|
const require = function require(path) {
|
|
return mod.require(path);
|
|
};
|
|
|
|
function resolve(request, options) {
|
|
return Module._resolveFilename(request, mod, false, options);
|
|
}
|
|
|
|
require.resolve = resolve;
|
|
|
|
function paths(request) {
|
|
return Module._resolveLookupPaths(request, mod);
|
|
}
|
|
|
|
resolve.paths = paths;
|
|
require.main = mainModule;
|
|
// Enable support to add extra extension types.
|
|
require.extensions = Module._extensions;
|
|
require.cache = Module._cache;
|
|
|
|
return require;
|
|
}
|
|
|
|
// Matches to:
|
|
// - /foo/...
|
|
// - \foo\...
|
|
// - C:/foo/...
|
|
// - C:\foo\...
|
|
const RE_START_OF_ABS_PATH = /^([/\\]|[a-zA-Z]:[/\\])/;
|
|
|
|
function isAbsolute(filenameOrUrl) {
|
|
return RE_START_OF_ABS_PATH.test(filenameOrUrl);
|
|
}
|
|
|
|
function createRequire(filenameOrUrl) {
|
|
let fileUrlStr;
|
|
if (filenameOrUrl instanceof URL) {
|
|
if (filenameOrUrl.protocol !== "file:") {
|
|
throw new Error(
|
|
`The argument 'filename' must be a file URL object, file URL string, or absolute path string. Received ${filenameOrUrl}`,
|
|
);
|
|
}
|
|
fileUrlStr = filenameOrUrl.toString();
|
|
} else if (typeof filenameOrUrl === "string") {
|
|
if (!filenameOrUrl.startsWith("file:") && !isAbsolute(filenameOrUrl)) {
|
|
throw new Error(
|
|
`The argument 'filename' must be a file URL object, file URL string, or absolute path string. Received ${filenameOrUrl}`,
|
|
);
|
|
}
|
|
fileUrlStr = filenameOrUrl;
|
|
} else {
|
|
throw new Error(
|
|
`The argument 'filename' must be a file URL object, file URL string, or absolute path string. Received ${filenameOrUrl}`,
|
|
);
|
|
}
|
|
const filename = op_require_as_file_path(fileUrlStr);
|
|
return createRequireFromPath(filename);
|
|
}
|
|
|
|
function isBuiltin(moduleName) {
|
|
if (typeof moduleName !== "string") {
|
|
return false;
|
|
}
|
|
|
|
if (StringPrototypeStartsWith(moduleName, "node:")) {
|
|
moduleName = StringPrototypeSlice(moduleName, 5);
|
|
} else if (moduleName === "test") {
|
|
// test is only a builtin if it has the "node:" scheme
|
|
// see https://github.com/nodejs/node/blob/73025c4dec042e344eeea7912ed39f7b7c4a3991/test/parallel/test-module-isBuiltin.js#L14
|
|
return false;
|
|
}
|
|
|
|
return moduleName in nativeModuleExports &&
|
|
!StringPrototypeStartsWith(moduleName, "internal/");
|
|
}
|
|
|
|
Module.isBuiltin = isBuiltin;
|
|
|
|
Module.createRequire = createRequire;
|
|
|
|
Module._initPaths = function () {
|
|
const paths = op_require_init_paths();
|
|
modulePaths = paths;
|
|
Module.globalPaths = ArrayPrototypeSlice(modulePaths);
|
|
};
|
|
|
|
Module.syncBuiltinESMExports = function syncBuiltinESMExports() {
|
|
throw new Error("not implemented");
|
|
};
|
|
|
|
// Mostly used by tools like ts-node.
|
|
Module.runMain = function () {
|
|
Module._load(process.argv[1], null, true);
|
|
};
|
|
|
|
Module.Module = Module;
|
|
|
|
nativeModuleExports.module = Module;
|
|
|
|
function loadNativeModule(_id, request) {
|
|
if (nativeModulePolyfill.has(request)) {
|
|
return nativeModulePolyfill.get(request);
|
|
}
|
|
const modExports = nativeModuleExports[request];
|
|
if (modExports) {
|
|
const nodeMod = new Module(request);
|
|
nodeMod.exports = modExports;
|
|
nodeMod.loaded = true;
|
|
nativeModulePolyfill.set(request, nodeMod);
|
|
return nodeMod;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function nativeModuleCanBeRequiredByUsers(request) {
|
|
return !!nativeModuleExports[request];
|
|
}
|
|
|
|
function readPackageScope() {
|
|
throw new Error("not implemented");
|
|
}
|
|
|
|
/** @param specifier {string} */
|
|
function packageSpecifierSubPath(specifier) {
|
|
let parts = StringPrototypeSplit(specifier, "/");
|
|
if (StringPrototypeStartsWith(parts[0], "@")) {
|
|
parts = ArrayPrototypeSlice(parts, 2);
|
|
} else {
|
|
parts = ArrayPrototypeSlice(parts, 1);
|
|
}
|
|
return ArrayPrototypeJoin(parts, "/");
|
|
}
|
|
|
|
// This is a temporary namespace, that will be removed when initializing
|
|
// in `02_init.js`.
|
|
internals.requireImpl = {
|
|
setUsesLocalNodeModulesDir() {
|
|
usesLocalNodeModulesDir = true;
|
|
},
|
|
setInspectBrk() {
|
|
hasInspectBrk = true;
|
|
},
|
|
Module,
|
|
nativeModuleExports,
|
|
};
|
|
|
|
/**
|
|
* @param {string} path
|
|
* @returns {SourceMap | undefined}
|
|
*/
|
|
export function findSourceMap(_path) {
|
|
// TODO(@marvinhagemeister): Stub implementation for now to unblock ava
|
|
return undefined;
|
|
}
|
|
|
|
export { builtinModules, createRequire, isBuiltin, Module };
|
|
export const _cache = Module._cache;
|
|
export const _extensions = Module._extensions;
|
|
export const _findPath = Module._findPath;
|
|
export const _initPaths = Module._initPaths;
|
|
export const _load = Module._load;
|
|
export const _nodeModulePaths = Module._nodeModulePaths;
|
|
export const _pathCache = Module._pathCache;
|
|
export const _preloadModules = Module._preloadModules;
|
|
export const _resolveFilename = Module._resolveFilename;
|
|
export const _resolveLookupPaths = Module._resolveLookupPaths;
|
|
export const globalPaths = Module.globalPaths;
|
|
export const wrap = Module.wrap;
|
|
|
|
export default Module;
|