summaryrefslogtreecommitdiff
path: root/std/node/module.ts
diff options
context:
space:
mode:
authorKevin (Kun) "Kassimo" Qian <kevinkassimo@gmail.com>2019-11-19 13:44:59 -0800
committerRy Dahl <ry@tinyclouds.org>2019-11-19 16:44:59 -0500
commit6708fcc38688b80d4e052f755f02efb09a2071d1 (patch)
tree4ddf5ec5cb7d467f017c36fa0621e84f4d72f4aa /std/node/module.ts
parent2ac107f54848e44144c0ee2bdbdfb40732e20b2b (diff)
std/node: add some Node.js polyfill to require() (#3382)
Diffstat (limited to 'std/node/module.ts')
-rw-r--r--std/node/module.ts1227
1 files changed, 1227 insertions, 0 deletions
diff --git a/std/node/module.ts b/std/node/module.ts
new file mode 100644
index 000000000..3d51bf641
--- /dev/null
+++ b/std/node/module.ts
@@ -0,0 +1,1227 @@
+// 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.
+
+import "./global.ts";
+
+import * as nodeFS from "./fs.ts";
+import * as nodeUtil from "./util.ts";
+import * as nodePath from "./path.ts";
+
+import * as path from "../path/mod.ts";
+import { assert } from "../testing/asserts.ts";
+
+const CHAR_FORWARD_SLASH = "/".charCodeAt(0);
+const CHAR_BACKWARD_SLASH = "\\".charCodeAt(0);
+const CHAR_COLON = ":".charCodeAt(0);
+
+const isWindows = path.isWindows;
+
+const relativeResolveCache = Object.create(null);
+
+let requireDepth = 0;
+let statCache = null;
+
+type StatResult = -1 | 0 | 1;
+// Returns 0 if the path refers to
+// a file, 1 when it's a directory or < 0 on error.
+function stat(filename: string): StatResult {
+ filename = path.toNamespacedPath(filename);
+ if (statCache !== null) {
+ const result = statCache.get(filename);
+ if (result !== undefined) return result;
+ }
+ try {
+ const info = Deno.statSync(filename);
+ const result = info.isFile() ? 0 : 1;
+ if (statCache !== null) statCache.set(filename, result);
+ return result;
+ } catch (e) {
+ return -1;
+ }
+}
+
+function updateChildren(parent: Module, child: Module, scan: boolean): void {
+ const children = parent && parent.children;
+ if (children && !(scan && children.includes(child))) {
+ children.push(child);
+ }
+}
+
+class Module {
+ id: string;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ exports: any;
+ parent?: Module;
+ filename: string;
+ loaded: boolean;
+ children: Module[];
+ paths: string[];
+ path: string;
+ constructor(id = "", parent?: Module) {
+ this.id = id;
+ this.exports = {};
+ this.parent = parent;
+ updateChildren(parent, this, false);
+ this.filename = null;
+ this.loaded = false;
+ this.children = [];
+ this.paths = [];
+ this.path = path.dirname(id);
+ }
+ static builtinModules: string[] = [];
+ static _extensions: {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ [key: string]: (module: Module, filename: string) => any;
+ } = Object.create(null);
+ static _cache: { [key: string]: Module } = Object.create(null);
+ static _pathCache = Object.create(null);
+ static globalPaths: string[] = [];
+ // Proxy related code removed.
+ static wrapper = [
+ "(function (exports, require, module, __filename, __dirname) { ",
+ "\n});"
+ ];
+
+ // Loads a module at the given file path. Returns that module's
+ // `exports` property.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ require(id: string): any {
+ if (id === "") {
+ throw new Error(`id '${id}' must be a non-empty string`);
+ }
+ requireDepth++;
+ try {
+ return Module._load(id, this, /* isMain */ false);
+ } finally {
+ requireDepth--;
+ }
+ }
+
+ // Given a file name, pass it to the proper extension handler.
+ load(filename: string): void {
+ assert(!this.loaded);
+ this.filename = filename;
+ this.paths = Module._nodeModulePaths(path.dirname(filename));
+
+ const extension = findLongestRegisteredExtension(filename);
+ // Removed ESM code
+ Module._extensions[extension](this, filename);
+ this.loaded = true;
+ // Removed ESM code
+ }
+
+ // 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.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ _compile(content: string, filename: string): any {
+ // manifest code removed
+ const compiledWrapper = wrapSafe(filename, content);
+ // inspector code remove
+ const dirname = path.dirname(filename);
+ const require = makeRequireFunction(this);
+ const exports = this.exports;
+ const thisValue = exports;
+ if (requireDepth === 0) {
+ statCache = new Map();
+ }
+ const result = compiledWrapper.call(
+ thisValue,
+ exports,
+ require,
+ this,
+ filename,
+ dirname
+ );
+ if (requireDepth === 0) {
+ statCache = null;
+ }
+ return result;
+ }
+
+ static _resolveLookupPaths(
+ request: string,
+ parent: Module | null
+ ): string[] | 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);
+ }
+
+ return paths.length > 0 ? paths : null;
+ }
+
+ // With --eval, parent.id is not set and 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 with eval there is no filename
+ const mainPaths = ["."].concat(Module._nodeModulePaths("."), modulePaths);
+ return mainPaths;
+ }
+
+ const parentDir = [path.dirname(parent.filename)];
+ return parentDir;
+ }
+
+ static _resolveFilename(
+ request: string,
+ parent: Module,
+ isMain: boolean,
+ options?: { paths: string[] }
+ ): string {
+ // Polyfills.
+ if (nativeModuleCanBeRequiredByUsers(request)) {
+ return request;
+ }
+
+ let paths: string[];
+
+ if (typeof options === "object" && options !== null) {
+ if (Array.isArray(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 Error("options.paths is invalid");
+ }
+ } else {
+ paths = Module._resolveLookupPaths(request, parent);
+ }
+
+ // Look up the filename first, since that's the cache key.
+ const filename = Module._findPath(request, paths, isMain);
+ if (!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- ");
+ }
+ const err = new Error(message);
+ // @ts-ignore
+ err.code = "MODULE_NOT_FOUND";
+ // @ts-ignore
+ err.requireStack = requireStack;
+ throw err;
+ }
+ return filename as string;
+ }
+
+ static _findPath(
+ request: string,
+ paths: string[],
+ isMain: boolean
+ ): string | boolean {
+ 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 = /(?:^|\/)\.?\.$/.test(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;
+ const basePath = resolveExports(curPath, request, absoluteRequest);
+ let filename;
+
+ const rc = stat(basePath);
+ if (!trailingSlash) {
+ if (rc === 0) {
+ // File.
+ // preserveSymlinks removed
+ filename = toRealPath(basePath);
+ }
+
+ if (!filename) {
+ // Try it with each of the extensions
+ if (exts === undefined) exts = Object.keys(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 = Object.keys(Module._extensions);
+ filename = tryPackage(basePath, exts, isMain, request);
+ }
+
+ if (filename) {
+ Module._pathCache[cacheKey] = filename;
+ return filename;
+ }
+ }
+ // trySelf removed.
+
+ return false;
+ }
+
+ // 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.
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ static _load(request: string, parent: Module, isMain: boolean): any {
+ 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);
+
+ const cachedModule = Module._cache[filename];
+ if (cachedModule !== undefined) {
+ updateChildren(parent, cachedModule, true);
+ if (!cachedModule.loaded)
+ return getExportsForCircularRequire(cachedModule);
+ return cachedModule.exports;
+ }
+
+ // Native module polyfills
+ const mod = loadNativeModule(filename, request);
+ if (mod) return mod.exports;
+
+ // Don't call updateChildren(), Module constructor already does.
+ const module = new Module(filename, parent);
+
+ if (isMain) {
+ // TODO: set process info
+ // process.mainModule = module;
+ module.id = ".";
+ }
+
+ Module._cache[filename] = module;
+ if (parent !== undefined) {
+ relativeResolveCache[relResolveCacheIdentifier] = filename;
+ }
+
+ let threw = true;
+ try {
+ // Source map code removed
+ module.load(filename);
+ threw = false;
+ } finally {
+ if (threw) {
+ delete Module._cache[filename];
+ if (parent !== undefined) {
+ delete relativeResolveCache[relResolveCacheIdentifier];
+ }
+ } else if (
+ module.exports &&
+ Object.getPrototypeOf(module.exports) ===
+ CircularRequirePrototypeWarningProxy
+ ) {
+ Object.setPrototypeOf(module.exports, PublicObjectPrototype);
+ }
+ }
+
+ return module.exports;
+ }
+
+ static wrap(script: string): string {
+ return `${Module.wrapper[0]}${script}${Module.wrapper[1]}`;
+ }
+
+ static _nodeModulePaths(from: string): string[] {
+ if (isWindows) {
+ // 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
+ // 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;
+ }
+ }
+
+ /**
+ * Create a `require` function that can be used to import CJS modules.
+ * Follows CommonJS resolution similar to that of Node.js,
+ * with `node_modules` lookup and `index.js` lookup support.
+ * Also injects available Node.js builtin module polyfills.
+ *
+ * const require_ = createRequire(import.meta.url);
+ * const fs = require_("fs");
+ * const leftPad = require_("left-pad");
+ * const cjsModule = require_("./cjs_mod");
+ *
+ * @param filename path or URL to current module
+ * @return Require function to import CJS modules
+ */
+ static createRequire(filename: string | URL): RequireFunction {
+ let filepath: string;
+ if (
+ filename instanceof URL ||
+ (typeof filename === "string" && !path.isAbsolute(filename))
+ ) {
+ filepath = fileURLToPath(filename);
+ } else if (typeof filename !== "string") {
+ throw new Error("filename should be a string");
+ } else {
+ filepath = filename;
+ }
+ return createRequireFromPath(filepath);
+ }
+
+ static _initPaths(): void {
+ const homeDir = Deno.env("HOME");
+ const nodePath = Deno.env("NODE_PATH");
+
+ // Removed $PREFIX/bin/node case
+
+ let paths = [];
+
+ 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);
+ }
+
+ static _preloadModules(requests: string[]): void {
+ if (!Array.isArray(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(Deno.cwd());
+ } catch (e) {
+ if (e.code !== "ENOENT") {
+ throw e;
+ }
+ }
+ for (let n = 0; n < requests.length; n++) {
+ parent.require(requests[n]);
+ }
+ }
+}
+
+// Polyfills.
+const nativeModulePolyfill = new Map<string, Module>();
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function createNativeModule(id: string, exports: any): Module {
+ const mod = new Module(id);
+ mod.exports = exports;
+ mod.loaded = true;
+ return mod;
+}
+nativeModulePolyfill.set("fs", createNativeModule("fs", nodeFS));
+nativeModulePolyfill.set("util", createNativeModule("util", nodeUtil));
+nativeModulePolyfill.set("path", createNativeModule("path", nodePath));
+function loadNativeModule(
+ _filename: string,
+ request: string
+): Module | undefined {
+ return nativeModulePolyfill.get(request);
+}
+function nativeModuleCanBeRequiredByUsers(request: string): boolean {
+ return nativeModulePolyfill.has(request);
+}
+// Populate with polyfill names
+for (const id of nativeModulePolyfill.keys()) {
+ Module.builtinModules.push(id);
+}
+
+let modulePaths = [];
+
+// 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 Map<string, PackageInfo | null>();
+
+interface PackageInfo {
+ name?: string;
+ main?: string;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ exports?: any;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ type?: any;
+}
+
+function readPackage(requestPath: string): PackageInfo | null {
+ const jsonPath = path.resolve(requestPath, "package.json");
+
+ const existing = packageJsonCache.get(jsonPath);
+ if (existing !== undefined) {
+ return existing;
+ }
+
+ let json: string | undefined;
+ try {
+ json = new TextDecoder().decode(
+ Deno.readFileSync(path.toNamespacedPath(jsonPath))
+ );
+ } catch {}
+
+ if (json === undefined) {
+ packageJsonCache.set(jsonPath, null);
+ return null;
+ }
+
+ try {
+ const parsed = JSON.parse(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
+): { path: string; data: PackageInfo } | false {
+ 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: string): string | undefined {
+ const pkg = readPackage(requestPath);
+ return pkg ? pkg.main : undefined;
+}
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function readPackageExports(requestPath: string): any | undefined {
+ const pkg = readPackage(requestPath);
+ return pkg ? pkg.exports : undefined;
+}
+
+function tryPackage(
+ requestPath: string,
+ exts: string[],
+ isMain: boolean,
+ _originalPath: string
+): string | false {
+ 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'
+ );
+ // @ts-ignore
+ err.code = "MODULE_NOT_FOUND";
+ throw err;
+ }
+ }
+ return actual;
+}
+
+// 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: string, _isMain: boolean): string | false {
+ const rc = stat(requestPath);
+ return rc === 0 && toRealPath(requestPath);
+}
+
+function toRealPath(requestPath: string): string {
+ // Deno does not have realpath implemented yet.
+ let fullPath = requestPath;
+ while (true) {
+ try {
+ fullPath = Deno.readlinkSync(fullPath);
+ } catch {
+ break;
+ }
+ }
+ return path.resolve(requestPath);
+}
+
+// Given a path, check if the file exists with any of the set extensions
+function tryExtensions(
+ p: string,
+ exts: string[],
+ isMain: boolean
+): string | false {
+ 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: string): string {
+ 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";
+}
+
+// --experimental-resolve-self trySelf() support removed.
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function isConditionalDotExportSugar(exports: any, _basePath: string): boolean {
+ if (typeof exports === "string") return true;
+ if (Array.isArray(exports)) return true;
+ if (typeof exports !== "object") return false;
+ let isConditional = false;
+ let firstCheck = true;
+ for (const key of Object.keys(exports)) {
+ const curIsConditional = key[0] !== ".";
+ if (firstCheck) {
+ firstCheck = false;
+ isConditional = curIsConditional;
+ } else if (isConditional !== curIsConditional) {
+ throw new Error(
+ '"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: string, expansion: string): string {
+ const mappingKey = `.${expansion}`;
+
+ let pkgExports = readPackageExports(basePath);
+ if (pkgExports === undefined || pkgExports === null)
+ return path.resolve(basePath, mappingKey);
+
+ if (isConditionalDotExportSugar(pkgExports, basePath))
+ pkgExports = { ".": pkgExports };
+
+ if (typeof pkgExports === "object") {
+ if (pkgExports.hasOwnProperty(mappingKey)) {
+ const mapping = pkgExports[mappingKey];
+ return resolveExportsTarget(
+ pathToFileURL(basePath + "/"),
+ mapping,
+ "",
+ basePath,
+ mappingKey
+ );
+ }
+
+ // Fallback to CJS main lookup when no main export is defined
+ if (mappingKey === ".") return basePath;
+
+ let dirMatch = "";
+ for (const candidateKey of Object.keys(pkgExports)) {
+ if (candidateKey[candidateKey.length - 1] !== "/") continue;
+ if (
+ candidateKey.length > dirMatch.length &&
+ mappingKey.startsWith(candidateKey)
+ ) {
+ dirMatch = candidateKey;
+ }
+ }
+
+ if (dirMatch !== "") {
+ const mapping = pkgExports[dirMatch];
+ const subpath = mappingKey.slice(dirMatch.length);
+ return resolveExportsTarget(
+ pathToFileURL(basePath + "/"),
+ mapping,
+ subpath,
+ basePath,
+ mappingKey
+ );
+ }
+ }
+ // Fallback to CJS main lookup when no main export is defined
+ if (mappingKey === ".") return basePath;
+
+ const e = new Error(
+ `Package exports for '${basePath}' do not define ` +
+ `a '${mappingKey}' subpath`
+ );
+ // @ts-ignore
+ e.code = "MODULE_NOT_FOUND";
+ throw e;
+}
+
+// This only applies to requests of a specific form:
+// 1. name/.*
+// 2. @scope/name/.*
+const EXPORTS_PATTERN = /^((?:@[^/\\%]+\/)?[^./\\%][^/\\%]*)(\/.*)?$/;
+function resolveExports(
+ nmPath: string,
+ request: string,
+ absoluteRequest: boolean
+): string {
+ // The implementation's behavior is meant to mirror resolution in ESM.
+ if (!absoluteRequest) {
+ const [, name, expansion = ""] = request.match(EXPORTS_PATTERN) || [];
+ if (!name) {
+ return path.resolve(nmPath, request);
+ }
+
+ const basePath = path.resolve(nmPath, name);
+ return applyExports(basePath, expansion);
+ }
+
+ return path.resolve(nmPath, request);
+}
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function resolveExportsTarget(
+ pkgPath: URL,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ target: any,
+ subpath: string,
+ basePath: string,
+ mappingKey: string
+): string {
+ if (typeof target === "string") {
+ if (
+ target.startsWith("./") &&
+ (subpath.length === 0 || target.endsWith("/"))
+ ) {
+ const resolvedTarget = new URL(target, pkgPath);
+ const pkgPathPath = pkgPath.pathname;
+ const resolvedTargetPath = resolvedTarget.pathname;
+ if (
+ resolvedTargetPath.startsWith(pkgPathPath) &&
+ resolvedTargetPath.indexOf("/node_modules/", pkgPathPath.length - 1) ===
+ -1
+ ) {
+ const resolved = new URL(subpath, resolvedTarget);
+ const resolvedPath = resolved.pathname;
+ if (
+ resolvedPath.startsWith(resolvedTargetPath) &&
+ resolvedPath.indexOf("/node_modules/", pkgPathPath.length - 1) === -1
+ ) {
+ return fileURLToPath(resolved);
+ }
+ }
+ }
+ } else if (Array.isArray(target)) {
+ for (const targetValue of target) {
+ if (Array.isArray(targetValue)) continue;
+ try {
+ return resolveExportsTarget(
+ pkgPath,
+ targetValue,
+ subpath,
+ basePath,
+ mappingKey
+ );
+ } catch (e) {
+ if (e.code !== "MODULE_NOT_FOUND") throw e;
+ }
+ }
+ } else if (typeof target === "object" && target !== null) {
+ // removed experimentalConditionalExports
+ if (target.hasOwnProperty("default")) {
+ try {
+ return resolveExportsTarget(
+ pkgPath,
+ target.default,
+ subpath,
+ basePath,
+ mappingKey
+ );
+ } catch (e) {
+ if (e.code !== "MODULE_NOT_FOUND") throw e;
+ }
+ }
+ }
+ let e: Error;
+ if (mappingKey !== ".") {
+ e = new Error(
+ `Package exports for '${basePath}' do not define a ` +
+ `valid '${mappingKey}' target${subpath ? " for " + subpath : ""}`
+ );
+ } else {
+ e = new Error(`No valid exports main found for '${basePath}'`);
+ }
+ // @ts-ignore
+ e.code = "MODULE_NOT_FOUND";
+ throw e;
+}
+
+// 'node_modules' character codes reversed
+const nmChars = [115, 101, 108, 117, 100, 111, 109, 95, 101, 100, 111, 110];
+const nmLen = nmChars.length;
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function emitCircularRequireWarning(prop: any): void {
+ console.error(
+ `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-existend properties are accessed.
+const CircularRequirePrototypeWarningProxy = new Proxy(
+ {},
+ {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ get(target, prop): any {
+ if (prop in target) return target[prop];
+ emitCircularRequireWarning(prop);
+ return undefined;
+ },
+
+ getOwnPropertyDescriptor(target, prop): PropertyDescriptor | undefined {
+ if (target.hasOwnProperty(prop))
+ return Object.getOwnPropertyDescriptor(target, prop);
+ emitCircularRequireWarning(prop);
+ return undefined;
+ }
+ }
+);
+
+// Object.prototype and ObjectProtoype refer to our 'primordials' versions
+// and are not identical to the versions on the global object.
+const PublicObjectPrototype = window.Object.prototype;
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function getExportsForCircularRequire(module: Module): any {
+ if (
+ module.exports &&
+ Object.getPrototypeOf(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.
+ Object.setPrototypeOf(module.exports, CircularRequirePrototypeWarningProxy);
+ }
+
+ return module.exports;
+}
+
+type RequireWrapper = (
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ exports: any,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ require: any,
+ module: Module,
+ __filename: string,
+ __dirname: string
+) => void;
+
+function wrapSafe(filename_: string, content: string): RequireWrapper {
+ // TODO: fix this
+ const wrapper = Module.wrap(content);
+ // @ts-ignore
+ const [f, err] = Deno.core.evalContext(wrapper);
+ if (err) {
+ throw err;
+ }
+ return f;
+ // ESM code removed.
+}
+
+// Native extension for .js
+Module._extensions[".js"] = (module: Module, filename: string): void => {
+ if (filename.endsWith(".js")) {
+ const pkg = readPackageScope(filename);
+ if (pkg !== false && pkg.data && pkg.data.type === "module") {
+ throw new Error("Importing ESM module");
+ }
+ }
+ const content = new TextDecoder().decode(Deno.readFileSync(filename));
+ module._compile(content, filename);
+};
+
+// Native extension for .json
+Module._extensions[".json"] = (module: Module, filename: string): void => {
+ const content = new TextDecoder().decode(Deno.readFileSync(filename));
+ // manifest code removed
+ try {
+ module.exports = JSON.parse(stripBOM(content));
+ } catch (err) {
+ err.message = filename + ": " + err.message;
+ throw err;
+ }
+};
+
+// .node extension is not supported
+
+function createRequireFromPath(filename: string): RequireFunction {
+ // 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);
+}
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+type Require = (id: string) => any;
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+type RequireResolve = (request: string, options: any) => string;
+interface RequireResolveFunction extends RequireResolve {
+ paths: (request: string) => string[] | null;
+}
+
+interface RequireFunction extends Require {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ resolve: RequireResolveFunction;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ extensions: { [key: string]: (module: Module, filename: string) => any };
+ cache: { [key: string]: Module };
+}
+
+function makeRequireFunction(mod: Module): RequireFunction {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ const require = function require(path: string): any {
+ return mod.require(path);
+ };
+
+ function resolve(request: string, options?: { paths: string[] }): string {
+ return Module._resolveFilename(request, mod, false, options);
+ }
+
+ require.resolve = resolve;
+
+ function paths(request: string): string[] | null {
+ return Module._resolveLookupPaths(request, mod);
+ }
+
+ resolve.paths = paths;
+ // TODO: set main
+ // require.main = process.mainModule;
+
+ // Enable support to add extra extension types.
+ require.extensions = Module._extensions;
+
+ require.cache = Module._cache;
+
+ return require;
+}
+
+/**
+ * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
+ * because the buffer-to-string conversion in `fs.readFileSync()`
+ * translates it to FEFF, the UTF-16 BOM.
+ */
+function stripBOM(content: string): string {
+ if (content.charCodeAt(0) === 0xfeff) {
+ content = content.slice(1);
+ }
+ return content;
+}
+
+const forwardSlashRegEx = /\//g;
+const CHAR_LOWERCASE_A = "a".charCodeAt(0);
+const CHAR_LOWERCASE_Z = "z".charCodeAt(0);
+
+function getPathFromURLWin32(url: URL): string {
+ // const hostname = url.hostname;
+ let pathname = url.pathname;
+ for (let n = 0; n < pathname.length; n++) {
+ if (pathname[n] === "%") {
+ const third = pathname.codePointAt(n + 2) | 0x20;
+ if (
+ (pathname[n + 1] === "2" && third === 102) || // 2f 2F /
+ (pathname[n + 1] === "5" && third === 99)
+ ) {
+ // 5c 5C \
+ throw new Error(
+ "Invalid file url path: must not include encoded \\ or / characters"
+ );
+ }
+ }
+ }
+ pathname = pathname.replace(forwardSlashRegEx, "\\");
+ pathname = decodeURIComponent(pathname);
+ // TODO: handle windows hostname case (needs bindings)
+ const letter = pathname.codePointAt(1) | 0x20;
+ const sep = pathname[2];
+ if (
+ letter < CHAR_LOWERCASE_A ||
+ letter > CHAR_LOWERCASE_Z || // a..z A..Z
+ sep !== ":"
+ ) {
+ throw new Error("Invalid file URL path: must be absolute");
+ }
+ return pathname.slice(1);
+}
+
+function getPathFromURLPosix(url: URL): string {
+ if (url.hostname !== "") {
+ throw new Error("Invalid file URL host");
+ }
+ const pathname = url.pathname;
+ for (let n = 0; n < pathname.length; n++) {
+ if (pathname[n] === "%") {
+ const third = pathname.codePointAt(n + 2) | 0x20;
+ if (pathname[n + 1] === "2" && third === 102) {
+ throw new Error(
+ "Invalid file URL path: must not include encoded / characters"
+ );
+ }
+ }
+ }
+ return decodeURIComponent(pathname);
+}
+
+function fileURLToPath(path: string | URL): string {
+ if (typeof path === "string") {
+ path = new URL(path);
+ }
+ if (path.protocol !== "file:") {
+ throw new Error("Protocol has to be file://");
+ }
+ return isWindows ? getPathFromURLWin32(path) : getPathFromURLPosix(path);
+}
+
+const percentRegEx = /%/g;
+const backslashRegEx = /\\/g;
+const newlineRegEx = /\n/g;
+const carriageReturnRegEx = /\r/g;
+const tabRegEx = /\t/g;
+function pathToFileURL(filepath: string): URL {
+ let resolved = path.resolve(filepath);
+ // path.resolve strips trailing slashes so we must add them back
+ const filePathLast = filepath.charCodeAt(filepath.length - 1);
+ if (
+ (filePathLast === CHAR_FORWARD_SLASH ||
+ (isWindows && filePathLast === CHAR_BACKWARD_SLASH)) &&
+ resolved[resolved.length - 1] !== path.sep
+ )
+ resolved += "/";
+ const outURL = new URL("file://");
+ if (resolved.includes("%")) resolved = resolved.replace(percentRegEx, "%25");
+ // In posix, "/" is a valid character in paths
+ if (!isWindows && resolved.includes("\\"))
+ resolved = resolved.replace(backslashRegEx, "%5C");
+ if (resolved.includes("\n")) resolved = resolved.replace(newlineRegEx, "%0A");
+ if (resolved.includes("\r"))
+ resolved = resolved.replace(carriageReturnRegEx, "%0D");
+ if (resolved.includes("\t")) resolved = resolved.replace(tabRegEx, "%09");
+ outURL.pathname = resolved;
+ return outURL;
+}
+
+export const builtinModules = Module.builtinModules;
+export const createRequire = Module.createRequire;
+export default Module;