From cd53ab5427811bddbed1c30d3733e1df87bb23f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 20 Mar 2023 14:05:13 -0400 Subject: refactor(ext/node): untangle dependencies between js files (#18284) Moving some code around in `ext/node` is it's a bit better well defined and makes it possible for others to embed it. I expect to see no difference in startup perf with this change. --- ext/node/01_node.js | 121 ---- ext/node/02_require.js | 938 ------------------------------- ext/node/lib.rs | 106 ++-- ext/node/module_es_shim.js | 20 - ext/node/polyfill.rs | 2 +- ext/node/polyfills/00_globals.js | 72 +++ ext/node/polyfills/01_require.js | 1146 ++++++++++++++++++++++++++++++++++++++ ext/node/polyfills/02_init.js | 62 +++ ext/node/polyfills/module_all.ts | 191 ------- ext/node/polyfills/process.ts | 48 +- 10 files changed, 1352 insertions(+), 1354 deletions(-) delete mode 100644 ext/node/01_node.js delete mode 100644 ext/node/02_require.js delete mode 100644 ext/node/module_es_shim.js create mode 100644 ext/node/polyfills/00_globals.js create mode 100644 ext/node/polyfills/01_require.js create mode 100644 ext/node/polyfills/02_init.js delete mode 100644 ext/node/polyfills/module_all.ts (limited to 'ext/node') diff --git a/ext/node/01_node.js b/ext/node/01_node.js deleted file mode 100644 index 85346a44b..000000000 --- a/ext/node/01_node.js +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -// deno-lint-ignore-file - -const internals = globalThis.__bootstrap.internals; -const primordials = globalThis.__bootstrap.primordials; -const { - ArrayPrototypePush, - ArrayPrototypeFilter, - ObjectEntries, - ObjectCreate, - ObjectDefineProperty, - Proxy, - ReflectDefineProperty, - ReflectDeleteProperty, - ReflectGet, - ReflectGetOwnPropertyDescriptor, - ReflectHas, - ReflectOwnKeys, - ReflectSet, - Set, - SetPrototypeHas, -} = primordials; - -function assert(cond) { - if (!cond) { - throw Error("assert"); - } -} - -let initialized = false; -const nodeGlobals = {}; -const nodeGlobalThis = new Proxy(globalThis, { - get(target, prop) { - if (ReflectHas(nodeGlobals, prop)) { - return ReflectGet(nodeGlobals, prop); - } else { - return ReflectGet(target, prop); - } - }, - set(target, prop, value) { - if (ReflectHas(nodeGlobals, prop)) { - return ReflectSet(nodeGlobals, prop, value); - } else { - return ReflectSet(target, prop, value); - } - }, - has(target, prop) { - return ReflectHas(nodeGlobals, prop) || ReflectHas(target, prop); - }, - deleteProperty(target, prop) { - const nodeDeleted = ReflectDeleteProperty(nodeGlobals, prop); - const targetDeleted = ReflectDeleteProperty(target, prop); - return nodeDeleted || targetDeleted; - }, - ownKeys(target) { - const targetKeys = ReflectOwnKeys(target); - const nodeGlobalsKeys = ReflectOwnKeys(nodeGlobals); - const nodeGlobalsKeySet = new Set(nodeGlobalsKeys); - return [ - ...ArrayPrototypeFilter( - targetKeys, - (k) => !SetPrototypeHas(nodeGlobalsKeySet, k), - ), - ...nodeGlobalsKeys, - ]; - }, - defineProperty(target, prop, desc) { - if (ReflectHas(nodeGlobals, prop)) { - return ReflectDefineProperty(nodeGlobals, prop, desc); - } else { - return ReflectDefineProperty(target, prop, desc); - } - }, - getOwnPropertyDescriptor(target, prop) { - if (ReflectHas(nodeGlobals, prop)) { - return ReflectGetOwnPropertyDescriptor(nodeGlobals, prop); - } else { - return ReflectGetOwnPropertyDescriptor(target, prop); - } - }, -}); - -const nativeModuleExports = ObjectCreate(null); -const builtinModules = []; - -function initialize(nodeModules, nodeGlobalThisName) { - assert(!initialized); - initialized = true; - for (const [name, exports] of ObjectEntries(nodeModules)) { - nativeModuleExports[name] = exports; - ArrayPrototypePush(builtinModules, name); - } - nodeGlobals.Buffer = nativeModuleExports["buffer"].Buffer; - nodeGlobals.clearImmediate = nativeModuleExports["timers"].clearImmediate; - nodeGlobals.clearInterval = nativeModuleExports["timers"].clearInterval; - nodeGlobals.clearTimeout = nativeModuleExports["timers"].clearTimeout; - nodeGlobals.console = nativeModuleExports["console"]; - nodeGlobals.global = nodeGlobalThis; - nodeGlobals.process = nativeModuleExports["process"]; - nodeGlobals.setImmediate = nativeModuleExports["timers"].setImmediate; - nodeGlobals.setInterval = nativeModuleExports["timers"].setInterval; - nodeGlobals.setTimeout = nativeModuleExports["timers"].setTimeout; - - // add a hidden global for the esm code to use in order to reliably - // get node's globalThis - ObjectDefineProperty(globalThis, nodeGlobalThisName, { - enumerable: false, - writable: false, - value: nodeGlobalThis, - }); - // FIXME(bartlomieju): not nice to depend on `Deno` namespace here - internals.__bootstrapNodeProcess(Deno.args, Deno.version); -} - -internals.node = { - globalThis: nodeGlobalThis, - initialize, - nativeModuleExports, - builtinModules, -}; diff --git a/ext/node/02_require.js b/ext/node/02_require.js deleted file mode 100644 index 43343a21a..000000000 --- a/ext/node/02_require.js +++ /dev/null @@ -1,938 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -// deno-lint-ignore-file - -const core = globalThis.Deno.core; -const ops = core.ops; -const internals = globalThis.__bootstrap.internals; -const primordials = globalThis.__bootstrap.primordials; -const { - ArrayIsArray, - ArrayPrototypeIncludes, - ArrayPrototypeIndexOf, - ArrayPrototypeJoin, - ArrayPrototypePush, - ArrayPrototypeSlice, - ArrayPrototypeSplice, - ObjectGetOwnPropertyDescriptor, - ObjectGetPrototypeOf, - ObjectPrototypeHasOwnProperty, - ObjectSetPrototypeOf, - ObjectKeys, - ObjectPrototype, - ObjectCreate, - Proxy, - SafeMap, - SafeWeakMap, - SafeArrayIterator, - JSONParse, - String, - StringPrototypeEndsWith, - StringPrototypeIndexOf, - StringPrototypeIncludes, - StringPrototypeMatch, - StringPrototypeSlice, - StringPrototypeSplit, - StringPrototypeStartsWith, - StringPrototypeCharCodeAt, - RegExpPrototypeTest, - Error, - TypeError, -} = primordials; -const node = internals.node; - -// Map used to store CJS parsing data. -const cjsParseCache = new SafeWeakMap(); - -function pathDirname(filepath) { - if (filepath == null || filepath === "") { - throw new Error("Empty filepath."); - } - return ops.op_require_path_dirname(filepath); -} - -function pathResolve(...args) { - return ops.op_require_path_resolve(args); -} - -function assert(cond) { - if (!cond) { - throw Error("assert"); - } -} - -const nativeModulePolyfill = new SafeMap(); - -const relativeResolveCache = ObjectCreate(null); -let requireDepth = 0; -let statCache = null; -let isPreloading = false; -let mainModule = null; -let hasBrokenOnInspectBrk = false; -let hasInspectBrk = false; -// Are we running with --node-modules-dir flag? -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 = ops.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 = ops.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 { - node.globalThis.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 = ops.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 = ops.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) { - node.globalThis.process.emitWarning( - `Accessing non-existent property '${String(prop)}' of module exports ` + - "inside circular dependency", - ); -} - -// A Proxy that can be used as the prototype of a module.exports object and -// warns when non-existent properties are accessed. -const CircularRequirePrototypeWarningProxy = new Proxy({}, { - get(target, prop) { - // Allow __esModule access in any case because it is used in the output - // of transpiled code to determine whether something comes from an - // ES module, and is not used as a regular key of `module.exports`. - if (prop in target || prop === "__esModule") return target[prop]; - emitCircularRequireWarning(prop); - return undefined; - }, - - getOwnPropertyDescriptor(target, prop) { - if ( - ObjectPrototypeHasOwnProperty(target, prop) || prop === "__esModule" - ) { - return ObjectGetOwnPropertyDescriptor(target, prop); - } - emitCircularRequireWarning(prop); - return undefined; - }, -}); - -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 = node.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 = /(?:^|\/)\.?\.$/; -const encodedSepRegEx = /%2F|%2C/i; - -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 filename = resolved; - const actual = tryFile(filename, false); - if (actual) { - return actual; - } - throw new ERR_MODULE_NOT_FOUND( - filename, - path.resolve(pkgPath, "package.json"), - ); -} - -// 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; - } - - return ops.op_require_resolve_exports( - usesLocalNodeModulesDir, - modulesPath, - request, - name, - expansion, - parentPath, - ) ?? false; -} - -Module._findPath = function (request, paths, isMain, parentPath) { - const absoluteRequest = ops.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; - } - } - - const isDenoDirPackage = ops.op_require_is_deno_dir_package( - curPath, - ); - const isRelative = ops.op_require_is_request_relative( - request, - ); - const 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; -}; - -Module._nodeModulePaths = function (fromPath) { - return ops.op_require_node_module_paths(fromPath); -}; - -Module._resolveLookupPaths = function (request, parent) { - const paths = []; - - if (ops.op_require_is_request_relative(request) && parent?.filename) { - ArrayPrototypePush( - paths, - ops.op_require_path_dirname(parent.filename), - ); - return paths; - } - - if (parent?.filename && parent.filename.length > 0) { - const denoDirPath = ops.op_require_resolve_deno_dir( - request, - parent.filename, - ); - if (denoDirPath) { - ArrayPrototypePush(paths, denoDirPath); - } - } - const lookupPathsResult = ops.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) { - node.globalThis.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 = ops.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 = ops.op_require_package_imports_resolve( - parent.filename, - request, - ); - if (maybeResolved) { - return maybeResolved; - } - } - } - - // Try module self resolution first - const parentPath = ops.op_require_try_self_parent_path( - !!parent, - parent?.filename, - parent?.id, - ); - const selfResolved = ops.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 filename; - const requireStack = []; - for (let cursor = parent; cursor; cursor = moduleParentCache.get(cursor)) { - ArrayPrototypePush(requireStack, cursor.filename || cursor.id); - } - let message = `Cannot find module '${request}'`; - if (requireStack.length > 0) { - message = message + "\nRequire stack:\n- " + - ArrayPrototypeJoin(requireStack, "\n- "); - } - // eslint-disable-next-line no-restricted-syntax - const err = new Error(message); - err.code = "MODULE_NOT_FOUND"; - err.requireStack = requireStack; - throw err; -}; - -Module.prototype.load = function (filename) { - assert(!this.loaded); - this.filename = filename; - this.paths = Module._nodeModulePaths( - pathDirname(filename), - ); - const extension = findLongestRegisteredExtension(filename); - // allow .mjs to be overriden - if ( - StringPrototypeEndsWith(filename, ".mjs") && !Module._extensions[".mjs"] - ) { - // TODO: use proper error class - throw new Error("require ESM", filename); - } - - Module._extensions[extension](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--; - } -}; - -Module.wrapper = [ - // We provide the non-standard APIs in the CommonJS wrapper - // to avoid exposing them in global namespace. - "(function (exports, require, module, __filename, __dirname, globalThis) { const { Buffer, clearImmediate, clearInterval, clearTimeout, console, global, process, setImmediate, setInterval, setTimeout} = globalThis; var window = undefined; (function () {", - "\n}).call(this); })", -]; -Module.wrap = function (script) { - script = script.replace(/^#!.*?\n/, ""); - return `${Module.wrapper[0]}${script}${Module.wrapper[1]}`; -}; - -function enrichCJSError(error) { - if (error instanceof SyntaxError) { - if ( - StringPrototypeIncludes( - error.message, - "Cannot use import statement outside a module", - ) || - StringPrototypeIncludes(error.message, "Unexpected token 'export'") - ) { - 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 (node.globalThis.process.mainModule === cjsModuleInstance) { - enrichCJSError(err.thrown); - } - 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; - const module = this; - if (requireDepth === 0) { - statCache = new SafeMap(); - } - - if (hasInspectBrk && !hasBrokenOnInspectBrk) { - hasBrokenOnInspectBrk = true; - ops.op_require_break_on_next_statement(); - } - - const result = compiledWrapper.call( - thisValue, - exports, - require, - this, - filename, - dirname, - node.globalThis, - ); - if (requireDepth === 0) { - statCache = null; - } - return result; -}; - -Module._extensions[".js"] = function (module, filename) { - const content = ops.op_require_read_file(filename); - - if (StringPrototypeEndsWith(filename, ".js")) { - const pkg = ops.op_require_read_closest_package_json(filename); - if (pkg && pkg.exists && pkg.typ == "module") { - let message = `Trying to import ESM module: ${filename}`; - - if (module.parent) { - message += ` from ${module.parent.filename}`; - } - - message += ` using require()`; - - throw new Error(message); - } - } - - module._compile(content, filename); -}; - -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 = ops.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 = ops.op_napi_open(filename, node.globalThis); -}; - -function createRequireFromPath(filename) { - const proxyPath = ops.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 = ops.op_require_as_file_path(fileUrlStr); - return createRequireFromPath(filename); -} - -Module.createRequire = createRequire; - -Module._initPaths = function () { - const paths = ops.op_require_init_paths(); - modulePaths = paths; - Module.globalPaths = ArrayPrototypeSlice(modulePaths); -}; - -Module.syncBuiltinESMExports = function syncBuiltinESMExports() { - throw new Error("not implemented"); -}; - -Module.Module = Module; - -node.nativeModuleExports.module = Module; - -function loadNativeModule(_id, request) { - if (nativeModulePolyfill.has(request)) { - return nativeModulePolyfill.get(request); - } - const modExports = node.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 !!node.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, "/"); -} - -internals.require = { - setUsesLocalNodeModulesDir() { - usesLocalNodeModulesDir = true; - }, - setInspectBrk() { - hasInspectBrk = true; - }, - Module, - wrapSafe, - toRealPath, - cjsParseCache, - readPackageScope, -}; diff --git a/ext/node/lib.rs b/ext/node/lib.rs index e9ac8c060..5684bf172 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -95,6 +95,7 @@ fn op_node_build_os() -> String { deno_core::extension!(deno_node, deps = [ deno_io, deno_fs ], + parameters = [P: NodePermissions], ops = [ crypto::op_node_create_decipheriv, crypto::op_node_cipheriv_encrypt, @@ -119,10 +120,36 @@ deno_core::extension!(deno_node, idna::op_node_idna_punycode_decode, idna::op_node_idna_punycode_encode, op_node_build_os, + + ops::op_require_init_paths, + ops::op_require_node_module_paths

, + ops::op_require_proxy_path, + ops::op_require_is_deno_dir_package, + ops::op_require_resolve_deno_dir, + ops::op_require_is_request_relative, + ops::op_require_resolve_lookup_paths, + ops::op_require_try_self_parent_path

, + ops::op_require_try_self

, + ops::op_require_real_path

, + ops::op_require_path_is_absolute, + ops::op_require_path_dirname, + ops::op_require_stat

, + ops::op_require_path_resolve, + ops::op_require_path_basename, + ops::op_require_read_file

, + ops::op_require_as_file_path, + ops::op_require_resolve_exports

, + ops::op_require_read_closest_package_json

, + ops::op_require_read_package_scope

, + ops::op_require_package_imports_resolve

, + ops::op_require_break_on_next_statement, ], - esm_entry_point = "ext:deno_node/module_all.ts", + esm_entry_point = "ext:deno_node/02_init.js", esm = [ dir "polyfills", + "00_globals.js", + "01_require.js", + "02_init.js", "_core.ts", "_events.mjs", "_fs/_fs_access.ts", @@ -305,7 +332,6 @@ deno_core::extension!(deno_node, "internal/util/inspect.mjs", "internal/util/types.ts", "internal/validators.mjs", - "module_all.ts", "net.ts", "os.ts", "path.ts", @@ -344,35 +370,6 @@ deno_core::extension!(deno_node, "worker_threads.ts", "zlib.ts", ], -); - -deno_core::extension!(deno_node_loading, - parameters = [P: NodePermissions], - ops = [ - ops::op_require_init_paths, - ops::op_require_node_module_paths

, - ops::op_require_proxy_path, - ops::op_require_is_deno_dir_package, - ops::op_require_resolve_deno_dir, - ops::op_require_is_request_relative, - ops::op_require_resolve_lookup_paths, - ops::op_require_try_self_parent_path

, - ops::op_require_try_self

, - ops::op_require_real_path

, - ops::op_require_path_is_absolute, - ops::op_require_path_dirname, - ops::op_require_stat

, - ops::op_require_path_resolve, - ops::op_require_path_basename, - ops::op_require_read_file

, - ops::op_require_as_file_path, - ops::op_require_resolve_exports

, - ops::op_require_read_closest_package_json

, - ops::op_require_read_package_scope

, - ops::op_require_package_imports_resolve

, - ops::op_require_break_on_next_statement, - ], - esm = ["01_node.js", "02_require.js", "module_es_shim.js"], options = { maybe_npm_resolver: Option>, }, @@ -386,16 +383,24 @@ deno_core::extension!(deno_node_loading, pub fn initialize_runtime( js_runtime: &mut JsRuntime, uses_local_node_modules_dir: bool, + maybe_binary_command_name: Option, ) -> Result<(), AnyError> { + let argv0 = if let Some(binary_command_name) = maybe_binary_command_name { + format!("\"{}\"", binary_command_name) + } else { + "undefined".to_string() + }; let source_code = &format!( - r#"(function loadBuiltinNodeModules(nodeGlobalThisName, usesLocalNodeModulesDir) {{ - Deno[Deno.internal].node.initialize(Deno[Deno.internal].nodeModuleAll, nodeGlobalThisName); - if (usesLocalNodeModulesDir) {{ - Deno[Deno.internal].require.setUsesLocalNodeModulesDir(); - }} - }})('{}', {});"#, + r#"(function loadBuiltinNodeModules(nodeGlobalThisName, usesLocalNodeModulesDir, argv0) {{ + Deno[Deno.internal].node.initialize( + nodeGlobalThisName, + usesLocalNodeModulesDir, + argv0 + ); + }})('{}', {}, {});"#, NODE_GLOBAL_THIS_NAME.as_str(), uses_local_node_modules_dir, + argv0 ); js_runtime.execute_script(&located_script_name!(), source_code)?; @@ -413,12 +418,9 @@ pub fn load_cjs_module( } let source_code = &format!( - r#"(function loadCjsModule(module, inspectBrk) {{ - if (inspectBrk) {{ - Deno[Deno.internal].require.setInspectBrk(); - }} - Deno[Deno.internal].require.Module._load(module, null, {main}); - }})('{module}', {inspect_brk});"#, + r#"(function loadCjsModule(moduleName, isMain, inspectBrk) {{ + Deno[Deno.internal].node.loadCjsModule(moduleName, isMain, inspectBrk); + }})('{module}', {main}, {inspect_brk});"#, main = main, module = escape_for_single_quote_string(module), inspect_brk = inspect_brk, @@ -427,21 +429,3 @@ pub fn load_cjs_module( js_runtime.execute_script(&located_script_name!(), source_code)?; Ok(()) } - -pub fn initialize_binary_command( - js_runtime: &mut JsRuntime, - binary_name: &str, -) -> Result<(), AnyError> { - // overwrite what's done in deno_std in order to set the binary arg name - let source_code = &format!( - r#"(function initializeBinaryCommand(binaryName) {{ - const process = Deno[Deno.internal].node.globalThis.process; - Object.defineProperty(process.argv, "0", {{ - get: () => binaryName, - }}); - }})('{binary_name}');"#, - ); - - js_runtime.execute_script(&located_script_name!(), source_code)?; - Ok(()) -} diff --git a/ext/node/module_es_shim.js b/ext/node/module_es_shim.js deleted file mode 100644 index 2b7c20e26..000000000 --- a/ext/node/module_es_shim.js +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. - -const internals = globalThis.__bootstrap.internals; -const m = internals.require.Module; -export const _cache = m._cache; -export const _extensions = m._extensions; -export const _findPath = m._findPath; -export const _initPaths = m._initPaths; -export const _load = m._load; -export const _nodeModulePaths = m._nodeModulePaths; -export const _pathCache = m._pathCache; -export const _preloadModules = m._preloadModules; -export const _resolveFilename = m._resolveFilename; -export const _resolveLookupPaths = m._resolveLookupPaths; -export const builtinModules = m.builtinModules; -export const createRequire = m.createRequire; -export const globalPaths = m.globalPaths; -export const Module = m.Module; -export const wrap = m.wrap; -export default m; diff --git a/ext/node/polyfill.rs b/ext/node/polyfill.rs index 8577fccad..1fbb4afa3 100644 --- a/ext/node/polyfill.rs +++ b/ext/node/polyfill.rs @@ -93,7 +93,7 @@ pub static SUPPORTED_BUILTIN_NODE_MODULES: &[NodeModulePolyfill] = &[ }, NodeModulePolyfill { name: "module", - specifier: "ext:deno_node_loading/module_es_shim.js", + specifier: "ext:deno_node/01_require.js", }, NodeModulePolyfill { name: "net", diff --git a/ext/node/polyfills/00_globals.js b/ext/node/polyfills/00_globals.js new file mode 100644 index 000000000..9952d86aa --- /dev/null +++ b/ext/node/polyfills/00_globals.js @@ -0,0 +1,72 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// deno-lint-ignore-file + +const primordials = globalThis.__bootstrap.primordials; +const { + ArrayPrototypeFilter, + Proxy, + ReflectDefineProperty, + ReflectDeleteProperty, + ReflectGet, + ReflectGetOwnPropertyDescriptor, + ReflectHas, + ReflectOwnKeys, + ReflectSet, + Set, + SetPrototypeHas, +} = primordials; + +const nodeGlobals = {}; +const nodeGlobalThis = new Proxy(globalThis, { + get(target, prop) { + if (ReflectHas(nodeGlobals, prop)) { + return ReflectGet(nodeGlobals, prop); + } else { + return ReflectGet(target, prop); + } + }, + set(target, prop, value) { + if (ReflectHas(nodeGlobals, prop)) { + return ReflectSet(nodeGlobals, prop, value); + } else { + return ReflectSet(target, prop, value); + } + }, + has(target, prop) { + return ReflectHas(nodeGlobals, prop) || ReflectHas(target, prop); + }, + deleteProperty(target, prop) { + const nodeDeleted = ReflectDeleteProperty(nodeGlobals, prop); + const targetDeleted = ReflectDeleteProperty(target, prop); + return nodeDeleted || targetDeleted; + }, + ownKeys(target) { + const targetKeys = ReflectOwnKeys(target); + const nodeGlobalsKeys = ReflectOwnKeys(nodeGlobals); + const nodeGlobalsKeySet = new Set(nodeGlobalsKeys); + return [ + ...ArrayPrototypeFilter( + targetKeys, + (k) => !SetPrototypeHas(nodeGlobalsKeySet, k), + ), + ...nodeGlobalsKeys, + ]; + }, + defineProperty(target, prop, desc) { + if (ReflectHas(nodeGlobals, prop)) { + return ReflectDefineProperty(nodeGlobals, prop, desc); + } else { + return ReflectDefineProperty(target, prop, desc); + } + }, + getOwnPropertyDescriptor(target, prop) { + if (ReflectHas(nodeGlobals, prop)) { + return ReflectGetOwnPropertyDescriptor(nodeGlobals, prop); + } else { + return ReflectGetOwnPropertyDescriptor(target, prop); + } + }, +}); + +export { nodeGlobals, nodeGlobalThis }; diff --git a/ext/node/polyfills/01_require.js b/ext/node/polyfills/01_require.js new file mode 100644 index 000000000..68398df69 --- /dev/null +++ b/ext/node/polyfills/01_require.js @@ -0,0 +1,1146 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// deno-lint-ignore-file + +const core = globalThis.Deno.core; +const ops = core.ops; +const internals = globalThis.__bootstrap.internals; +const primordials = globalThis.__bootstrap.primordials; +const { + ArrayIsArray, + ArrayPrototypeIncludes, + ArrayPrototypeIndexOf, + ArrayPrototypeJoin, + ArrayPrototypePush, + ArrayPrototypeSlice, + ArrayPrototypeSplice, + ObjectGetOwnPropertyDescriptor, + ObjectGetPrototypeOf, + ObjectPrototypeHasOwnProperty, + ObjectSetPrototypeOf, + ObjectKeys, + ObjectEntries, + ObjectPrototype, + ObjectCreate, + Proxy, + SafeMap, + SafeWeakMap, + SafeArrayIterator, + JSONParse, + String, + StringPrototypeEndsWith, + StringPrototypeIndexOf, + StringPrototypeIncludes, + StringPrototypeMatch, + StringPrototypeSlice, + StringPrototypeSplit, + StringPrototypeStartsWith, + StringPrototypeCharCodeAt, + RegExpPrototypeTest, + Error, + TypeError, +} = primordials; +import { nodeGlobalThis } 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 "ext:deno_node/assert.ts"; +import assertStrict from "ext:deno_node/assert/strict.ts"; +import asyncHooks from "ext:deno_node/async_hooks.ts"; +import buffer from "ext:deno_node/buffer.ts"; +import childProcess from "ext:deno_node/child_process.ts"; +import cluster from "ext:deno_node/cluster.ts"; +import console from "ext:deno_node/console.ts"; +import constants from "ext:deno_node/constants.ts"; +import crypto from "ext:deno_node/crypto.ts"; +import dgram from "ext:deno_node/dgram.ts"; +import diagnosticsChannel from "ext:deno_node/diagnostics_channel.ts"; +import dns from "ext:deno_node/dns.ts"; +import dnsPromises from "ext:deno_node/dns/promises.ts"; +import domain from "ext:deno_node/domain.ts"; +import events from "ext:deno_node/events.ts"; +import fs from "ext:deno_node/fs.ts"; +import fsPromises from "ext:deno_node/fs/promises.ts"; +import http from "ext:deno_node/http.ts"; +import http2 from "ext:deno_node/http2.ts"; +import https from "ext:deno_node/https.ts"; +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 "ext:deno_node/net.ts"; +import os from "ext:deno_node/os.ts"; +import pathPosix from "ext:deno_node/path/posix.ts"; +import pathWin32 from "ext:deno_node/path/win32.ts"; +import path from "ext:deno_node/path.ts"; +import perfHooks from "ext:deno_node/perf_hooks.ts"; +import punycode from "ext:deno_node/punycode.ts"; +import process from "ext:deno_node/process.ts"; +import querystring from "ext:deno_node/querystring.ts"; +import readline from "ext:deno_node/readline.ts"; +import readlinePromises from "ext:deno_node/readline/promises.ts"; +import repl from "ext:deno_node/repl.ts"; +import stream from "ext:deno_node/stream.ts"; +import streamConsumers from "ext:deno_node/stream/consumers.mjs"; +import streamPromises from "ext:deno_node/stream/promises.mjs"; +import streamWeb from "ext:deno_node/stream/web.ts"; +import stringDecoder from "ext:deno_node/string_decoder.ts"; +import sys from "ext:deno_node/sys.ts"; +import timers from "ext:deno_node/timers.ts"; +import timersPromises from "ext:deno_node/timers/promises.ts"; +import tls from "ext:deno_node/tls.ts"; +import tty from "ext:deno_node/tty.ts"; +import url from "ext:deno_node/url.ts"; +import utilTypes from "ext:deno_node/util/types.ts"; +import util from "ext:deno_node/util.ts"; +import v8 from "ext:deno_node/v8.ts"; +import vm from "ext:deno_node/vm.ts"; +import workerThreads from "ext:deno_node/worker_threads.ts"; +import wasi from "ext:deno_node/wasi.ts"; +import zlib from "ext:deno_node/zlib.ts"; + +const nativeModuleExports = ObjectCreate(null); +const builtinModules = []; + +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, + 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, + 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(); + +// Map used to store CJS parsing data. +const cjsParseCache = new SafeWeakMap(); + +function pathDirname(filepath) { + if (filepath == null || filepath === "") { + throw new Error("Empty filepath."); + } + return ops.op_require_path_dirname(filepath); +} + +function pathResolve(...args) { + return ops.op_require_path_resolve(args); +} + +const nativeModulePolyfill = new SafeMap(); + +const relativeResolveCache = ObjectCreate(null); +let requireDepth = 0; +let statCache = null; +let isPreloading = false; +let mainModule = null; +let hasBrokenOnInspectBrk = false; +let hasInspectBrk = false; +// Are we running with --node-modules-dir flag? +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 = ops.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 = ops.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 { + nodeGlobalThis.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 = ops.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 = ops.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) { + nodeGlobalThis.process.emitWarning( + `Accessing non-existent property '${String(prop)}' of module exports ` + + "inside circular dependency", + ); +} + +// A Proxy that can be used as the prototype of a module.exports object and +// warns when non-existent properties are accessed. +const CircularRequirePrototypeWarningProxy = new Proxy({}, { + get(target, prop) { + // Allow __esModule access in any case because it is used in the output + // of transpiled code to determine whether something comes from an + // ES module, and is not used as a regular key of `module.exports`. + if (prop in target || prop === "__esModule") return target[prop]; + emitCircularRequireWarning(prop); + return undefined; + }, + + getOwnPropertyDescriptor(target, prop) { + if ( + ObjectPrototypeHasOwnProperty(target, prop) || prop === "__esModule" + ) { + return ObjectGetOwnPropertyDescriptor(target, prop); + } + emitCircularRequireWarning(prop); + return undefined; + }, +}); + +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 = /(?:^|\/)\.?\.$/; +const encodedSepRegEx = /%2F|%2C/i; + +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 filename = resolved; + const actual = tryFile(filename, false); + if (actual) { + return actual; + } + throw new ERR_MODULE_NOT_FOUND( + filename, + path.resolve(pkgPath, "package.json"), + ); +} + +// 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; + } + + return ops.op_require_resolve_exports( + usesLocalNodeModulesDir, + modulesPath, + request, + name, + expansion, + parentPath, + ) ?? false; +} + +Module._findPath = function (request, paths, isMain, parentPath) { + const absoluteRequest = ops.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; + } + } + + const isDenoDirPackage = ops.op_require_is_deno_dir_package( + curPath, + ); + const isRelative = ops.op_require_is_request_relative( + request, + ); + const 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; +}; + +Module._nodeModulePaths = function (fromPath) { + return ops.op_require_node_module_paths(fromPath); +}; + +Module._resolveLookupPaths = function (request, parent) { + const paths = []; + + if (ops.op_require_is_request_relative(request) && parent?.filename) { + ArrayPrototypePush( + paths, + ops.op_require_path_dirname(parent.filename), + ); + return paths; + } + + if (parent?.filename && parent.filename.length > 0) { + const denoDirPath = ops.op_require_resolve_deno_dir( + request, + parent.filename, + ); + if (denoDirPath) { + ArrayPrototypePush(paths, denoDirPath); + } + } + const lookupPathsResult = ops.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) { + nodeGlobalThis.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 = ops.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 = ops.op_require_package_imports_resolve( + parent.filename, + request, + ); + if (maybeResolved) { + return maybeResolved; + } + } + } + + // Try module self resolution first + const parentPath = ops.op_require_try_self_parent_path( + !!parent, + parent?.filename, + parent?.id, + ); + const selfResolved = ops.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 filename; + const requireStack = []; + for (let cursor = parent; cursor; cursor = moduleParentCache.get(cursor)) { + ArrayPrototypePush(requireStack, cursor.filename || cursor.id); + } + let message = `Cannot find module '${request}'`; + if (requireStack.length > 0) { + message = message + "\nRequire stack:\n- " + + ArrayPrototypeJoin(requireStack, "\n- "); + } + // eslint-disable-next-line no-restricted-syntax + const err = new Error(message); + err.code = "MODULE_NOT_FOUND"; + err.requireStack = requireStack; + throw err; +}; + +Module.prototype.load = function (filename) { + if (this.loaded) { + throw Error("Module already loaded"); + } + + this.filename = filename; + this.paths = Module._nodeModulePaths( + pathDirname(filename), + ); + const extension = findLongestRegisteredExtension(filename); + // allow .mjs to be overriden + if ( + StringPrototypeEndsWith(filename, ".mjs") && !Module._extensions[".mjs"] + ) { + // TODO: use proper error class + throw new Error("require ESM", filename); + } + + Module._extensions[extension](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--; + } +}; + +Module.wrapper = [ + // We provide the non-standard APIs in the CommonJS wrapper + // to avoid exposing them in global namespace. + "(function (exports, require, module, __filename, __dirname, globalThis) { const { Buffer, clearImmediate, clearInterval, clearTimeout, console, global, process, setImmediate, setInterval, setTimeout} = globalThis; var window = undefined; (function () {", + "\n}).call(this); })", +]; +Module.wrap = function (script) { + script = script.replace(/^#!.*?\n/, ""); + return `${Module.wrapper[0]}${script}${Module.wrapper[1]}`; +}; + +function enrichCJSError(error) { + if (error instanceof SyntaxError) { + if ( + StringPrototypeIncludes( + error.message, + "Cannot use import statement outside a module", + ) || + StringPrototypeIncludes(error.message, "Unexpected token 'export'") + ) { + 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 (nodeGlobalThis.process.mainModule === cjsModuleInstance) { + enrichCJSError(err.thrown); + } + 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; + const module = this; + if (requireDepth === 0) { + statCache = new SafeMap(); + } + + if (hasInspectBrk && !hasBrokenOnInspectBrk) { + hasBrokenOnInspectBrk = true; + ops.op_require_break_on_next_statement(); + } + + const result = compiledWrapper.call( + thisValue, + exports, + require, + this, + filename, + dirname, + nodeGlobalThis, + ); + if (requireDepth === 0) { + statCache = null; + } + return result; +}; + +Module._extensions[".js"] = function (module, filename) { + const content = ops.op_require_read_file(filename); + + if (StringPrototypeEndsWith(filename, ".js")) { + const pkg = ops.op_require_read_closest_package_json(filename); + if (pkg && pkg.exists && pkg.typ == "module") { + let message = `Trying to import ESM module: ${filename}`; + + if (module.parent) { + message += ` from ${module.parent.filename}`; + } + + message += ` using require()`; + + throw new Error(message); + } + } + + module._compile(content, filename); +}; + +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 = ops.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 = ops.op_napi_open(filename, nodeGlobalThis); +}; + +function createRequireFromPath(filename) { + const proxyPath = ops.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 = ops.op_require_as_file_path(fileUrlStr); + return createRequireFromPath(filename); +} + +Module.createRequire = createRequire; + +Module._initPaths = function () { + const paths = ops.op_require_init_paths(); + modulePaths = paths; + Module.globalPaths = ArrayPrototypeSlice(modulePaths); +}; + +Module.syncBuiltinESMExports = function syncBuiltinESMExports() { + throw new Error("not implemented"); +}; + +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, +}; + +export { builtinModules, createRequire, 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; diff --git a/ext/node/polyfills/02_init.js b/ext/node/polyfills/02_init.js new file mode 100644 index 000000000..d419c3bca --- /dev/null +++ b/ext/node/polyfills/02_init.js @@ -0,0 +1,62 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// deno-lint-ignore-file + +const internals = globalThis.__bootstrap.internals; +const requireImpl = internals.requireImpl; +const primordials = globalThis.__bootstrap.primordials; +const { ObjectDefineProperty } = primordials; +import { nodeGlobals, nodeGlobalThis } from "ext:deno_node/00_globals.js"; +import "ext:deno_node/01_require.js"; + +let initialized = false; + +function initialize( + nodeGlobalThisName, + usesLocalNodeModulesDir, + argv0, +) { + if (initialized) { + throw Error("Node runtime already initialized"); + } + initialized = true; + if (usesLocalNodeModulesDir) { + requireImpl.setUsesLocalNodeModulesDir(); + } + const nativeModuleExports = requireImpl.nativeModuleExports; + nodeGlobals.Buffer = nativeModuleExports["buffer"].Buffer; + nodeGlobals.clearImmediate = nativeModuleExports["timers"].clearImmediate; + nodeGlobals.clearInterval = nativeModuleExports["timers"].clearInterval; + nodeGlobals.clearTimeout = nativeModuleExports["timers"].clearTimeout; + nodeGlobals.console = nativeModuleExports["console"]; + nodeGlobals.global = nodeGlobalThis; + nodeGlobals.process = nativeModuleExports["process"]; + nodeGlobals.setImmediate = nativeModuleExports["timers"].setImmediate; + nodeGlobals.setInterval = nativeModuleExports["timers"].setInterval; + nodeGlobals.setTimeout = nativeModuleExports["timers"].setTimeout; + + // add a hidden global for the esm code to use in order to reliably + // get node's globalThis + ObjectDefineProperty(globalThis, nodeGlobalThisName, { + enumerable: false, + writable: false, + value: nodeGlobalThis, + }); + // FIXME(bartlomieju): not nice to depend on `Deno` namespace here + // but it's the only way to get `args` and `version` and this point. + internals.__bootstrapNodeProcess(argv0, Deno.args, Deno.version); + // `Deno[Deno.internal].requireImpl` will be unreachable after this line. + delete internals.requireImpl; +} + +function loadCjsModule(moduleName, isMain, inspectBrk) { + if (inspectBrk) { + requireImpl.setInspectBrk(); + } + requireImpl.Module._load(moduleName, null, { main: isMain }); +} + +internals.node = { + initialize, + loadCjsModule, +}; diff --git a/ext/node/polyfills/module_all.ts b/ext/node/polyfills/module_all.ts deleted file mode 100644 index b48fead0b..000000000 --- a/ext/node/polyfills/module_all.ts +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. -const internals = globalThis.__bootstrap.internals; -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 "ext:deno_node/assert.ts"; -import assertStrict from "ext:deno_node/assert/strict.ts"; -import asyncHooks from "ext:deno_node/async_hooks.ts"; -import buffer from "ext:deno_node/buffer.ts"; -import childProcess from "ext:deno_node/child_process.ts"; -import cluster from "ext:deno_node/cluster.ts"; -import console from "ext:deno_node/console.ts"; -import constants from "ext:deno_node/constants.ts"; -import crypto from "ext:deno_node/crypto.ts"; -import dgram from "ext:deno_node/dgram.ts"; -import diagnosticsChannel from "ext:deno_node/diagnostics_channel.ts"; -import dns from "ext:deno_node/dns.ts"; -import dnsPromises from "ext:deno_node/dns/promises.ts"; -import domain from "ext:deno_node/domain.ts"; -import events from "ext:deno_node/events.ts"; -import fs from "ext:deno_node/fs.ts"; -import fsPromises from "ext:deno_node/fs/promises.ts"; -import http from "ext:deno_node/http.ts"; -import http2 from "ext:deno_node/http2.ts"; -import https from "ext:deno_node/https.ts"; -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 "ext:deno_node/net.ts"; -import os from "ext:deno_node/os.ts"; -import pathPosix from "ext:deno_node/path/posix.ts"; -import pathWin32 from "ext:deno_node/path/win32.ts"; -import path from "ext:deno_node/path.ts"; -import perfHooks from "ext:deno_node/perf_hooks.ts"; -import punycode from "ext:deno_node/punycode.ts"; -import process from "ext:deno_node/process.ts"; -import querystring from "ext:deno_node/querystring.ts"; -import readline from "ext:deno_node/readline.ts"; -import readlinePromises from "ext:deno_node/readline/promises.ts"; -import repl from "ext:deno_node/repl.ts"; -import stream from "ext:deno_node/stream.ts"; -import streamConsumers from "ext:deno_node/stream/consumers.mjs"; -import streamPromises from "ext:deno_node/stream/promises.mjs"; -import streamWeb from "ext:deno_node/stream/web.ts"; -import stringDecoder from "ext:deno_node/string_decoder.ts"; -import sys from "ext:deno_node/sys.ts"; -import timers from "ext:deno_node/timers.ts"; -import timersPromises from "ext:deno_node/timers/promises.ts"; -import tls from "ext:deno_node/tls.ts"; -import tty from "ext:deno_node/tty.ts"; -import url from "ext:deno_node/url.ts"; -import utilTypes from "ext:deno_node/util/types.ts"; -import util from "ext:deno_node/util.ts"; -import v8 from "ext:deno_node/v8.ts"; -import vm from "ext:deno_node/vm.ts"; -import workerThreads from "ext:deno_node/worker_threads.ts"; -import wasi from "ext:deno_node/wasi.ts"; -import zlib from "ext:deno_node/zlib.ts"; - -// Canonical mapping of supported modules -const moduleAll = { - "_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, - 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, - timers, - "timers/promises": timersPromises, - tls, - tty, - url, - util, - "util/types": utilTypes, - v8, - vm, - wasi, - worker_threads: workerThreads, - zlib, -} as Record; - -internals.nodeModuleAll = moduleAll; -export default moduleAll; diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 1f1702785..eb5a491ae 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -72,28 +72,6 @@ const notImplementedEvents = [ export const argv: string[] = []; -// Overwrites the 1st item with getter. -// TODO(bartlomieju): added "configurable: true" to make this work for binary -// commands, but that is probably a wrong solution -// TODO(bartlomieju): move the configuration for all "argv" to -// "internals.__bootstrapNodeProcess" -Object.defineProperty(argv, "0", { - get: () => { - return Deno.execPath(); - }, - configurable: true, -}); -// Overwrites the 2st item with getter. -Object.defineProperty(argv, "1", { - get: () => { - if (Deno.mainModule.startsWith("file:")) { - return fromFileUrl(Deno.mainModule); - } else { - return join(Deno.cwd(), "$deno$node.js"); - } - }, -}); - /** https://nodejs.org/api/process.html#process_process_exit_code */ export const exit = (code?: number | string) => { if (code || code === 0) { @@ -686,9 +664,35 @@ export const removeAllListeners = process.removeAllListeners; // Should be called only once, in `runtime/js/99_main.js` when the runtime is // bootstrapped. internals.__bootstrapNodeProcess = function ( + argv0: string | undefined, args: string[], denoVersions: Record, ) { + // Overwrites the 1st item with getter. + if (typeof argv0 === "string") { + Object.defineProperty(argv, "0", { + get: () => { + return argv0; + }, + }); + } else { + Object.defineProperty(argv, "0", { + get: () => { + return Deno.execPath(); + }, + }); + } + + // Overwrites the 2st item with getter. + Object.defineProperty(argv, "1", { + get: () => { + if (Deno.mainModule.startsWith("file:")) { + return fromFileUrl(Deno.mainModule); + } else { + return join(Deno.cwd(), "$deno$node.js"); + } + }, + }); for (let i = 0; i < args.length; i++) { argv[i + 2] = args[i]; } -- cgit v1.2.3