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: [PATCH] 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. --- cli/build.rs | 3 +- cli/node/mod.rs | 4 +- cli/tests/testdata/commonjs/init.js | 10 - cli/tools/repl/session.rs | 1 + cli/worker.rs | 19 +- ext/node/lib.rs | 106 ++++---- ext/node/module_es_shim.js | 20 -- ext/node/polyfill.rs | 2 +- .../{01_node.js => polyfills/00_globals.js} | 51 +--- .../01_require.js} | 254 ++++++++++++++++-- ext/node/polyfills/02_init.js | 62 +++++ ext/node/polyfills/module_all.ts | 191 ------------- ext/node/polyfills/process.ts | 48 ++-- runtime/build.rs | 3 +- runtime/web_worker.rs | 3 +- runtime/worker.rs | 3 +- 16 files changed, 385 insertions(+), 395 deletions(-) delete mode 100644 cli/tests/testdata/commonjs/init.js delete mode 100644 ext/node/module_es_shim.js rename ext/node/{01_node.js => polyfills/00_globals.js} (53%) rename ext/node/{02_require.js => polyfills/01_require.js} (71%) create mode 100644 ext/node/polyfills/02_init.js delete mode 100644 ext/node/polyfills/module_all.ts diff --git a/cli/build.rs b/cli/build.rs index 173c8b85d4..4943b729a1 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -362,8 +362,7 @@ fn create_cli_snapshot(snapshot_path: PathBuf) { deno_io::deno_io::init_ops(Default::default()), deno_fs::deno_fs::init_ops::(false), deno_flash::deno_flash::init_ops::(false), // No --unstable - deno_node::deno_node_loading::init_ops::(None), // No --unstable. - deno_node::deno_node::init_ops(), + deno_node::deno_node::init_ops::(None), cli::init_ops_and_esm(), // NOTE: This needs to be init_ops_and_esm! ]; diff --git a/cli/node/mod.rs b/cli/node/mod.rs index 8a2f988e1a..4cbb1ef055 100644 --- a/cli/node/mod.rs +++ b/cli/node/mod.rs @@ -680,7 +680,9 @@ pub fn translate_cjs_to_esm( let mut handled_reexports: HashSet = HashSet::default(); let mut source = vec![ - r#"const require = Deno[Deno.internal].require.Module.createRequire(import.meta.url);"#.to_string(), + r#"import {createRequire as __internalCreateRequire} from "node:module"; + const require = __internalCreateRequire(import.meta.url);"# + .to_string(), ]; let analysis = perform_cjs_analysis( diff --git a/cli/tests/testdata/commonjs/init.js b/cli/tests/testdata/commonjs/init.js deleted file mode 100644 index 77992a0ad7..0000000000 --- a/cli/tests/testdata/commonjs/init.js +++ /dev/null @@ -1,10 +0,0 @@ -import { fromFileUrl } from "../../../../test_util/std/path/mod.ts"; - -const DENO_NODE_COMPAT_URL = Deno.env.get("DENO_NODE_COMPAT_URL"); -const moduleAllUrl = `${DENO_NODE_COMPAT_URL}node/module_all.ts`; -let moduleName = import.meta.resolve(Deno.args[0]); -moduleName = fromFileUrl(moduleName); - -const moduleAll = await import(moduleAllUrl); -Deno[Deno.internal].node.initialize(moduleAll.default); -Deno[Deno.internal].require.Module._load(moduleName, null, true); diff --git a/cli/tools/repl/session.rs b/cli/tools/repl/session.rs index 5fa3e4b059..350b38bb96 100644 --- a/cli/tools/repl/session.rs +++ b/cli/tools/repl/session.rs @@ -507,6 +507,7 @@ impl ReplSession { deno_node::initialize_runtime( &mut self.worker.js_runtime, self.proc_state.options.has_node_modules_dir(), + None, )?; self.has_initialized_node_runtime = true; } diff --git a/cli/worker.rs b/cli/worker.rs index 2d467e01d5..c505516a07 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -301,10 +301,8 @@ impl CliMainWorker { } fn initialize_main_module_for_node(&mut self) -> Result<(), AnyError> { - deno_node::initialize_runtime( - &mut self.worker.js_runtime, - self.ps.options.has_node_modules_dir(), - )?; + let mut maybe_binary_command_name = None; + if let DenoSubcommand::Run(flags) = self.ps.options.sub_command() { if let Ok(pkg_ref) = NpmPackageReqReference::from_str(&flags.script) { // if the user ran a binary command, we'll need to set process.argv[0] @@ -313,12 +311,16 @@ impl CliMainWorker { .sub_path .as_deref() .unwrap_or(pkg_ref.req.name.as_str()); - deno_node::initialize_binary_command( - &mut self.worker.js_runtime, - binary_name, - )?; + maybe_binary_command_name = Some(binary_name.to_string()); } } + + deno_node::initialize_runtime( + &mut self.worker.js_runtime, + self.ps.options.has_node_modules_dir(), + maybe_binary_command_name, + )?; + Ok(()) } @@ -627,6 +629,7 @@ fn create_web_worker_pre_execute_module_callback( deno_node::initialize_runtime( &mut worker.js_runtime, ps.options.has_node_modules_dir(), + None, )?; } diff --git a/ext/node/lib.rs b/ext/node/lib.rs index e9ac8c060e..5684bf1727 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 2b7c20e26d..0000000000 --- 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 8577fccad7..1fbb4afa3d 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/01_node.js b/ext/node/polyfills/00_globals.js similarity index 53% rename from ext/node/01_node.js rename to ext/node/polyfills/00_globals.js index 85346a44ba..9952d86aa7 100644 --- a/ext/node/01_node.js +++ b/ext/node/polyfills/00_globals.js @@ -2,14 +2,9 @@ // deno-lint-ignore-file -const internals = globalThis.__bootstrap.internals; const primordials = globalThis.__bootstrap.primordials; const { - ArrayPrototypePush, ArrayPrototypeFilter, - ObjectEntries, - ObjectCreate, - ObjectDefineProperty, Proxy, ReflectDefineProperty, ReflectDeleteProperty, @@ -22,13 +17,6 @@ const { SetPrototypeHas, } = primordials; -function assert(cond) { - if (!cond) { - throw Error("assert"); - } -} - -let initialized = false; const nodeGlobals = {}; const nodeGlobalThis = new Proxy(globalThis, { get(target, prop) { @@ -81,41 +69,4 @@ const nodeGlobalThis = new Proxy(globalThis, { }, }); -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, -}; +export { nodeGlobals, nodeGlobalThis }; diff --git a/ext/node/02_require.js b/ext/node/polyfills/01_require.js similarity index 71% rename from ext/node/02_require.js rename to ext/node/polyfills/01_require.js index 43343a21ae..68398df690 100644 --- a/ext/node/02_require.js +++ b/ext/node/polyfills/01_require.js @@ -19,6 +19,7 @@ const { ObjectPrototypeHasOwnProperty, ObjectSetPrototypeOf, ObjectKeys, + ObjectEntries, ObjectPrototype, ObjectCreate, Proxy, @@ -39,7 +40,202 @@ const { Error, TypeError, } = primordials; -const node = internals.node; +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(); @@ -55,12 +251,6 @@ 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); @@ -149,7 +339,7 @@ function tryPackage(requestPath, exts, isMain, originalPath) { err.requestPath = originalPath; throw err; } else { - node.globalThis.process.emitWarning( + nodeGlobalThis.process.emitWarning( `Invalid 'main' field in '${packageJsonPath}' of '${pkg}'. ` + "Please either fix that or report it to the module author", "DeprecationWarning", @@ -221,7 +411,7 @@ function getExportsForCircularRequire(module) { } function emitCircularRequireWarning(prop) { - node.globalThis.process.emitWarning( + nodeGlobalThis.process.emitWarning( `Accessing non-existent property '${String(prop)}' of module exports ` + "inside circular dependency", ); @@ -262,7 +452,7 @@ function Module(id = "", parent) { this.children = []; } -Module.builtinModules = node.builtinModules; +Module.builtinModules = builtinModules; Module._extensions = ObjectCreate(null); Module._cache = ObjectCreate(null); @@ -498,7 +688,7 @@ Module._load = function (request, parent, isMain) { const module = cachedModule || new Module(filename, parent); if (isMain) { - node.globalThis.process.mainModule = module; + nodeGlobalThis.process.mainModule = module; mainModule = module; module.id = "."; } @@ -639,7 +829,10 @@ Module._resolveFilename = function ( }; Module.prototype.load = function (filename) { - assert(!this.loaded); + if (this.loaded) { + throw Error("Module already loaded"); + } + this.filename = filename; this.paths = Module._nodeModulePaths( pathDirname(filename), @@ -717,7 +910,7 @@ function wrapSafe( const wrapper = Module.wrap(content); const [f, err] = core.evalContext(wrapper, `file://${filename}`); if (err) { - if (node.globalThis.process.mainModule === cjsModuleInstance) { + if (nodeGlobalThis.process.mainModule === cjsModuleInstance) { enrichCJSError(err.thrown); } throw err.thrown; @@ -749,7 +942,7 @@ Module.prototype._compile = function (content, filename) { this, filename, dirname, - node.globalThis, + nodeGlobalThis, ); if (requireDepth === 0) { statCache = null; @@ -802,7 +995,7 @@ 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); + module.exports = ops.op_napi_open(filename, nodeGlobalThis); }; function createRequireFromPath(filename) { @@ -887,13 +1080,13 @@ Module.syncBuiltinESMExports = function syncBuiltinESMExports() { Module.Module = Module; -node.nativeModuleExports.module = Module; +nativeModuleExports.module = Module; function loadNativeModule(_id, request) { if (nativeModulePolyfill.has(request)) { return nativeModulePolyfill.get(request); } - const modExports = node.nativeModuleExports[request]; + const modExports = nativeModuleExports[request]; if (modExports) { const nodeMod = new Module(request); nodeMod.exports = modExports; @@ -905,7 +1098,7 @@ function loadNativeModule(_id, request) { } function nativeModuleCanBeRequiredByUsers(request) { - return !!node.nativeModuleExports[request]; + return !!nativeModuleExports[request]; } function readPackageScope() { @@ -923,7 +1116,9 @@ function packageSpecifierSubPath(specifier) { return ArrayPrototypeJoin(parts, "/"); } -internals.require = { +// This is a temporary namespace, that will be removed when initializing +// in `02_init.js`. +internals.requireImpl = { setUsesLocalNodeModulesDir() { usesLocalNodeModulesDir = true; }, @@ -931,8 +1126,21 @@ internals.require = { hasInspectBrk = true; }, Module, - wrapSafe, - toRealPath, - cjsParseCache, - readPackageScope, + 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 0000000000..d419c3bcaa --- /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 b48fead0b0..0000000000 --- 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 1f1702785c..eb5a491ae0 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]; } diff --git a/runtime/build.rs b/runtime/build.rs index e655856b5c..346dbe439c 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -296,8 +296,7 @@ mod startup_snapshot { runtime::init_ops_and_esm(), // FIXME(bartlomieju): these extensions are specified last, because they // depend on `runtime`, even though it should be other way around - deno_node::deno_node_loading::init_ops_and_esm::(None), - deno_node::deno_node::init_ops_and_esm(), + deno_node::deno_node::init_ops_and_esm::(None), #[cfg(not(feature = "snapshot_from_snapshot"))] runtime_main::init_ops_and_esm(), ]; diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 1a8d02a1dc..c6fbb83707 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -435,10 +435,9 @@ impl WebWorker { deno_io::deno_io::init_ops(Some(options.stdio)), deno_fs::deno_fs::init_ops::(unstable), deno_flash::deno_flash::init_ops::(unstable), - deno_node::deno_node_loading::init_ops::( + deno_node::deno_node::init_ops::( options.npm_resolver, ), - deno_node::deno_node::init_ops(), // Runtime ops that are always initialized for WebWorkers ops::web_worker::deno_web_worker::init_ops(), ops::runtime::deno_runtime::init_ops(main_module.clone()), diff --git a/runtime/worker.rs b/runtime/worker.rs index ed3478ff01..a995861c5f 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -257,10 +257,9 @@ impl MainWorker { deno_io::deno_io::init_ops(Some(options.stdio)), deno_fs::deno_fs::init_ops::(unstable), deno_flash::deno_flash::init_ops::(unstable), - deno_node::deno_node_loading::init_ops::( + deno_node::deno_node::init_ops::( options.npm_resolver, ), - deno_node::deno_node::init_ops(), // Ops from this crate ops::runtime::deno_runtime::init_ops(main_module.clone()), ops::worker_host::deno_worker_host::init_ops(