From 32708213d5c8ce21765297f37bdf3a86248d530b Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Wed, 15 Jan 2025 18:48:10 -0800 Subject: [PATCH 01/12] fix(check/lsp): correctly resolve compilerOptions.types (#27686) Fixes https://github.com/denoland/deno/issues/27062 In the LSP we were passing `npm` specifiers to TSC as roots, but TSC needs fully resolved specifiers (like the actual file path). In `deno check` we were often excluding the specifiers entirely from the roots. In both cases, we need to resolve the specifiers fully and then pass them to tsc --- cli/lsp/tsc.rs | 20 +++++- cli/tools/check.rs | 64 ++++++++++++++++--- tests/integration/lsp_tests.rs | 49 ++++++++++++++ .../augments-global/1.0.0/index.d.ts | 1 + .../augments-global/1.0.0/other.d.ts | 6 ++ .../augments-global/1.0.0/package.json | 5 ++ .../compiler_options_types/__test__.jsonc | 53 +++++++++++++++ .../check/compiler_options_types/deno.json | 6 ++ .../check/compiler_options_types/main.ts | 2 + .../set_node_modules_dir.ts | 8 +++ tests/util/server/src/lsp.rs | 14 ++++ 11 files changed, 218 insertions(+), 10 deletions(-) create mode 100644 tests/registry/npm/@denotest/augments-global/1.0.0/index.d.ts create mode 100644 tests/registry/npm/@denotest/augments-global/1.0.0/other.d.ts create mode 100644 tests/registry/npm/@denotest/augments-global/1.0.0/package.json create mode 100644 tests/specs/check/compiler_options_types/__test__.jsonc create mode 100644 tests/specs/check/compiler_options_types/deno.json create mode 100644 tests/specs/check/compiler_options_types/main.ts create mode 100644 tests/specs/check/compiler_options_types/set_node_modules_dir.ts diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 0b53dc8506..482d3d6cf7 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -73,6 +73,7 @@ use super::documents::Document; use super::documents::DocumentsFilter; use super::language_server; use super::language_server::StateSnapshot; +use super::logging::lsp_log; use super::performance::Performance; use super::performance::PerformanceMark; use super::refactor::RefactorCodeActionData; @@ -4695,7 +4696,24 @@ fn op_script_names(state: &mut OpState) -> ScriptNames { .graph_imports_by_referrer(scope) { for specifier in specifiers { - script_names.insert(specifier.to_string()); + if let Ok(req_ref) = + deno_semver::npm::NpmPackageReqReference::from_specifier(specifier) + { + let Some((resolved, _)) = + state.state_snapshot.resolver.npm_to_file_url( + &req_ref, + scope, + ResolutionMode::Import, + Some(scope), + ) + else { + lsp_log!("failed to resolve {req_ref} to file URL"); + continue; + }; + script_names.insert(resolved.to_string()); + } else { + script_names.insert(specifier.to_string()); + } } } } diff --git a/cli/tools/check.rs b/cli/tools/check.rs index 8c584113cb..e850b1900f 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -13,6 +13,7 @@ use deno_graph::Module; use deno_graph::ModuleError; use deno_graph::ModuleGraph; use deno_graph::ModuleLoadError; +use deno_semver::npm::NpmPackageNvReference; use deno_terminal::colors; use once_cell::sync::Lazy; use regex::Regex; @@ -261,6 +262,8 @@ impl TypeChecker { maybe_check_hash, } = get_tsc_roots( &self.sys, + &self.npm_resolver, + &self.node_resolver, &graph, check_js, check_state_hash(&self.npm_resolver), @@ -373,8 +376,11 @@ struct TscRoots { /// redirects resolved. We need to include all the emittable files in /// the roots, so they get type checked and optionally emitted, /// otherwise they would be ignored if only imported into JavaScript. +#[allow(clippy::too_many_arguments)] fn get_tsc_roots( sys: &CliSys, + npm_resolver: &CliNpmResolver, + node_resolver: &CliNodeResolver, graph: &ModuleGraph, check_js: bool, npm_cache_state_hash: Option, @@ -457,6 +463,7 @@ fn get_tsc_roots( if let Some(hasher) = hasher { hasher.write_str(module.specifier.as_str()); } + None } } @@ -493,17 +500,33 @@ fn get_tsc_roots( let mut pending = VecDeque::new(); // put in the global types first so that they're resolved before anything else - let get_import_specifiers = || { - graph - .imports + for (referrer, import) in graph.imports.iter() { + for specifier in import + .dependencies .values() - .flat_map(|i| i.dependencies.values()) .filter_map(|dep| dep.get_type().or_else(|| dep.get_code())) - }; - for specifier in get_import_specifiers() { - let specifier = graph.resolve(specifier); - if seen.insert(specifier) { - pending.push_back((specifier, false)); + { + let specifier = graph.resolve(specifier); + if seen.insert(specifier) { + if let Ok(nv_ref) = NpmPackageNvReference::from_specifier(specifier) { + let Some(resolved) = + resolve_npm_nv_ref(npm_resolver, node_resolver, &nv_ref, referrer) + else { + result.missing_diagnostics.push( + tsc::Diagnostic::from_missing_error( + specifier, + None, + maybe_additional_sloppy_imports_message(sys, specifier), + ), + ); + continue; + }; + let mt = MediaType::from_specifier(&resolved); + result.roots.push((resolved, mt)); + } else { + pending.push_back((specifier, false)); + } + } } } @@ -624,6 +647,29 @@ fn get_tsc_roots( result } +fn resolve_npm_nv_ref( + npm_resolver: &CliNpmResolver, + node_resolver: &CliNodeResolver, + nv_ref: &NpmPackageNvReference, + referrer: &ModuleSpecifier, +) -> Option { + let pkg_dir = npm_resolver + .as_managed() + .unwrap() + .resolve_pkg_folder_from_deno_module(nv_ref.nv()) + .ok()?; + let resolved = node_resolver + .resolve_package_subpath_from_deno_module( + &pkg_dir, + nv_ref.sub_path(), + Some(referrer), + node_resolver::ResolutionMode::Import, + node_resolver::NodeResolutionKind::Types, + ) + .ok()?; + Some(resolved) +} + /// Matches the `@ts-check` pragma. static TS_CHECK_RE: Lazy = lazy_regex::lazy_regex!(r#"(?i)^\s*@ts-check(?:\s+|$)"#); diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index bbeb6e7c13..3c1aa1c4ad 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -17296,3 +17296,52 @@ fn wildcard_augment() { let diagnostics = client.did_open_file(&source); assert_eq!(diagnostics.all().len(), 0); } + +#[test] +fn compiler_options_types() { + let context = TestContextBuilder::for_npm().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); + let temp = context.temp_dir(); + let temp_dir = temp.path(); + let source = source_file( + temp_dir.join("index.ts"), + r#" + const foo = [1]; + foo.augmented(); + "#, + ); + + let deno_json = json!({ + "imports": { + "@denotest/augments-global": "npm:@denotest/augments-global@1" + }, + "compilerOptions": { "types": ["@denotest/augments-global"] }, + }); + + temp.write("deno.json", deno_json.to_string()); + + client.initialize_default(); + + for node_modules_dir in ["none", "auto", "manual"] { + let mut deno_json = deno_json.clone(); + deno_json["nodeModulesDir"] = json!(node_modules_dir); + temp.write("deno.json", deno_json.to_string()); + context + .new_command() + .args("install") + .run() + .skip_output_check() + .assert_exit_code(0); + client.did_change_watched_files(json!({ + "changes": [{ + "uri": temp.url().join("deno.json").unwrap(), + "type": 2, + }], + })); + + let diagnostics = client.did_open_file(&source); + eprintln!("{:#?}", diagnostics.all()); + assert_eq!(diagnostics.all().len(), 0); + client.did_close_file(&source); + } +} diff --git a/tests/registry/npm/@denotest/augments-global/1.0.0/index.d.ts b/tests/registry/npm/@denotest/augments-global/1.0.0/index.d.ts new file mode 100644 index 0000000000..f4e31e06dd --- /dev/null +++ b/tests/registry/npm/@denotest/augments-global/1.0.0/index.d.ts @@ -0,0 +1 @@ +import "./other.d.ts"; \ No newline at end of file diff --git a/tests/registry/npm/@denotest/augments-global/1.0.0/other.d.ts b/tests/registry/npm/@denotest/augments-global/1.0.0/other.d.ts new file mode 100644 index 0000000000..91dd7fa2d2 --- /dev/null +++ b/tests/registry/npm/@denotest/augments-global/1.0.0/other.d.ts @@ -0,0 +1,6 @@ +export {} +declare global { + interface Array { + augmented(): void + } +} \ No newline at end of file diff --git a/tests/registry/npm/@denotest/augments-global/1.0.0/package.json b/tests/registry/npm/@denotest/augments-global/1.0.0/package.json new file mode 100644 index 0000000000..33f20414ab --- /dev/null +++ b/tests/registry/npm/@denotest/augments-global/1.0.0/package.json @@ -0,0 +1,5 @@ +{ + "name": "@denotest/augments-global", + "version": "1.0.0", + "types": "./index.d.ts" +} \ No newline at end of file diff --git a/tests/specs/check/compiler_options_types/__test__.jsonc b/tests/specs/check/compiler_options_types/__test__.jsonc new file mode 100644 index 0000000000..f23081fef4 --- /dev/null +++ b/tests/specs/check/compiler_options_types/__test__.jsonc @@ -0,0 +1,53 @@ +{ + "tempDir": true, + "tests": { + "node_modules_dir_none": { + "steps": [ + { + "args": "run -A ./set_node_modules_dir.ts none", + "output": "" + }, + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "check ./main.ts", + "output": "Check [WILDCARD]main.ts\n" + } + ] + }, + "node_modules_dir_auto": { + "steps": [ + { + "args": "run -A ./set_node_modules_dir.ts auto", + "output": "" + }, + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "check ./main.ts", + "output": "Check [WILDCARD]main.ts\n" + } + ] + }, + "node_modules_dir_manual": { + "steps": [ + { + "args": "run -A ./set_node_modules_dir.ts auto", + "output": "" + }, + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "check ./main.ts", + "output": "Check [WILDCARD]main.ts\n" + } + ] + } + } +} diff --git a/tests/specs/check/compiler_options_types/deno.json b/tests/specs/check/compiler_options_types/deno.json new file mode 100644 index 0000000000..9a27ef33a8 --- /dev/null +++ b/tests/specs/check/compiler_options_types/deno.json @@ -0,0 +1,6 @@ +{ + "imports": { + "@denotest/augments-global": "npm:@denotest/augments-global@1" + }, + "compilerOptions": { "types": ["@denotest/augments-global"] } +} diff --git a/tests/specs/check/compiler_options_types/main.ts b/tests/specs/check/compiler_options_types/main.ts new file mode 100644 index 0000000000..ae30721279 --- /dev/null +++ b/tests/specs/check/compiler_options_types/main.ts @@ -0,0 +1,2 @@ +const foo = [1]; +foo.augmented(); diff --git a/tests/specs/check/compiler_options_types/set_node_modules_dir.ts b/tests/specs/check/compiler_options_types/set_node_modules_dir.ts new file mode 100644 index 0000000000..656f215890 --- /dev/null +++ b/tests/specs/check/compiler_options_types/set_node_modules_dir.ts @@ -0,0 +1,8 @@ +if (Deno.args.length !== 1) { + console.error("Usage: set_node_modules_dir.ts "); + Deno.exit(1); +} +const setting = Deno.args[0].trim(); +const denoJson = JSON.parse(Deno.readTextFileSync("./deno.json")); +denoJson["nodeModulesDir"] = setting; +Deno.writeTextFileSync("./deno.json", JSON.stringify(denoJson, null, 2)); diff --git a/tests/util/server/src/lsp.rs b/tests/util/server/src/lsp.rs index 12593578c3..3a7321db62 100644 --- a/tests/util/server/src/lsp.rs +++ b/tests/util/server/src/lsp.rs @@ -900,6 +900,20 @@ impl LspClient { self.read_diagnostics() } + pub fn did_close_file(&mut self, file: &SourceFile) { + self.did_close(json!({ + "textDocument": file.identifier(), + })) + } + + pub fn did_close(&mut self, params: Value) { + self.did_close_raw(params); + } + + pub fn did_close_raw(&mut self, params: Value) { + self.write_notification("textDocument/didClose", params); + } + pub fn did_open_raw(&mut self, params: Value) { self.write_notification("textDocument/didOpen", params); } From e49d6f2d45654a38531f0fc62d1d4802399468f6 Mon Sep 17 00:00:00 2001 From: Muthuraj Ramalingakumar Date: Wed, 15 Jan 2025 20:38:43 -0800 Subject: [PATCH 02/12] chore: add missing internal `core_import_map` file paths (#27691) Noted this when working locally, will help with vscode intellisense. fixes: https://github.com/denoland/deno/issues/27689 --- tools/core_import_map.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/core_import_map.json b/tools/core_import_map.json index 4843884015..0176f47e23 100644 --- a/tools/core_import_map.json +++ b/tools/core_import_map.json @@ -81,6 +81,7 @@ "node:https": "../ext/node/polyfills/https.ts", "node:inspector": "../ext/node/polyfills/inspector.ts", "ext:deno_node/inspector.ts": "../ext/node/polyfills/inspector.ts", + "ext:deno_node/internal/idna.ts": "../ext/node/polyfills/internal/idna.ts", "ext:deno_node/internal_binding/_libuv_winerror.ts": "../ext/node/polyfills/internal_binding/_libuv_winerror.ts", "ext:deno_node/internal_binding/_listen.ts": "../ext/node/polyfills/internal_binding/_listen.ts", "ext:deno_node/internal_binding/_node.ts": "../ext/node/polyfills/internal_binding/_node.ts", @@ -95,6 +96,8 @@ "ext:deno_node/internal_binding/crypto.ts": "../ext/node/polyfills/internal_binding/crypto.ts", "ext:deno_node/internal_binding/handle_wrap.ts": "../ext/node/polyfills/internal_binding/handle_wrap.ts", "ext:deno_node/internal_binding/mod.ts": "../ext/node/polyfills/internal_binding/mod.ts", + "ext:deno_node/internal_binding/node_file.ts": "../ext/node/polyfills/internal_binding/node_file.ts", + "ext:deno_node/internal_binding/node_options.ts": "../ext/node/polyfills/internal_binding/node_options.ts", "ext:deno_node/internal_binding/pipe_wrap.ts": "../ext/node/polyfills/internal_binding/pipe_wrap.ts", "ext:deno_node/internal_binding/stream_wrap.ts": "../ext/node/polyfills/internal_binding/stream_wrap.ts", "ext:deno_node/internal_binding/string_decoder.ts": "../ext/node/polyfills/internal_binding/string_decoder.ts", From e54d4678127a2d4124c359af73e3bfe76c71d234 Mon Sep 17 00:00:00 2001 From: Phil Hawksworth Date: Thu, 16 Jan 2025 04:33:08 -0800 Subject: [PATCH 03/12] docs:Adds examples in JSDocs for localStorage and sessionStorage (#27668) Improves docs for: - http://docs.deno.com/api/web/~/localStorage - http://docs.deno.com/api/web/~/sessionStorage --- cli/tsc/dts/lib.deno.window.d.ts | 81 +++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/cli/tsc/dts/lib.deno.window.d.ts b/cli/tsc/dts/lib.deno.window.d.ts index 251f338be6..6fc37ca08d 100644 --- a/cli/tsc/dts/lib.deno.window.d.ts +++ b/cli/tsc/dts/lib.deno.window.d.ts @@ -119,9 +119,86 @@ declare var onunload: ((this: Window, ev: Event) => any) | null; declare var onunhandledrejection: | ((this: Window, ev: PromiseRejectionEvent) => any) | null; -/** @category Storage */ +/** + * Deno's `localStorage` API provides a way to store key-value pairs in a + * web-like environment, similar to the Web Storage API found in browsers. + * It allows developers to persist data across sessions in a Deno application. + * This API is particularly useful for applications that require a simple + * and effective way to store data locally. + * + * - Key-Value Storage: Stores data as key-value pairs. + * - Persistent: Data is retained even after the application is closed. + * - Synchronous API: Operations are performed synchronously. + * + * `localStorage` is similar to {@linkcode sessionStorage}, and shares the same + * API methods, visible in the {@linkcode Storage} type. + * + * When using the `--location` flag, the origin for the location is used to + * uniquely store the data. That means a location of http://example.com/a.ts + * and http://example.com/b.ts and http://example.com:80/ would all share the + * same storage, but https://example.com/ would be different. + * + * For more information, see the reference guide for + * [Web Storage](https://docs.deno.com/runtime/reference/web_platform_apis/#web-storage) + * and using + * [the `--location` flag](https://docs.deno.com/runtime/reference/web_platform_apis/#location-flag). + * + * @example + * ```ts + * // Set a value in localStorage + * localStorage.setItem("key", "value"); + * + * // Get a value from localStorage + * const value = localStorage.getItem("key"); + * console.log(value); // Output: "value" + * + * // Remove a value from localStorage + * localStorage.removeItem("key"); + * + * // Clear all values from localStorage + * localStorage.clear(); + * ``` + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage + * @category Storage */ declare var localStorage: Storage; -/** @category Storage */ + +/** + * Deno's `sessionStorage` API operates similarly to the {@linkcode localStorage} API, + * but it is intended for storing data temporarily for the duration of a session. + * Data stored in sessionStorage is cleared when the application session or + * process ends. This makes it suitable for temporary data that you do not need + * to persist across user sessions. + * + * - Key-Value Storage: Stores data as key-value pairs. + * - Session-Based: Data is only available for the duration of the page session. + * - Synchronous API: Operations are performed synchronously. + * + * `sessionStorage` is similar to {@linkcode localStorage}, and shares the same API + * methods, visible in the {@linkcode Storage} type. + * + * For more information, see the reference guide for + * [Web Storage](https://docs.deno.com/runtime/reference/web_platform_apis/#web-storage) + * + * @example + * ```ts + * // Set a value in sessionStorage + * sessionStorage.setItem("key", "value"); + * + * // Get a value from sessionStorage + * const value = sessionStorage.getItem("key"); + * console.log(value); // Output: "value" + * + * // Remove a value from sessionStorage + * sessionStorage.removeItem("key"); + * + * // Clear all the values from sessionStorage + * sessionStorage.clear(); + * ``` + * + * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage + * @category Storage + */ declare var sessionStorage: Storage; /** @category Cache */ declare var caches: CacheStorage; From 8d2f76ae363b8d6f2ae39594c5c08552e76d37f7 Mon Sep 17 00:00:00 2001 From: Phil Hawksworth Date: Thu, 16 Jan 2025 06:20:45 -0800 Subject: [PATCH 04/12] docs: JSDocs examples for prompt, confirm, and alert (#27695) Adds examples --- cli/tsc/dts/lib.deno.window.d.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/cli/tsc/dts/lib.deno.window.d.ts b/cli/tsc/dts/lib.deno.window.d.ts index 6fc37ca08d..698b39c5df 100644 --- a/cli/tsc/dts/lib.deno.window.d.ts +++ b/cli/tsc/dts/lib.deno.window.d.ts @@ -226,6 +226,12 @@ declare var navigator: Navigator; * * If the stdin is not interactive, it does nothing. * + * @example + * ```ts + * // Displays the message "Acknowledge me! [Enter]" and waits for the enter key to be pressed before continuing. + * alert("Acknowledge me!"); + * ``` + * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/alert * @category Platform * * @param message @@ -239,6 +245,15 @@ declare function alert(message?: string): void; * * If the stdin is not interactive, it returns false. * + * @example + * ```ts + * const shouldProceed = confirm("Do you want to proceed?"); + * + * // If the user presses 'y' or 'Y', the result will be true + * // If the user presses 'n' or 'N', the result will be false + * console.log("Should proceed?", shouldProceed); + * ``` + * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm * @category Platform * * @param message @@ -256,6 +271,15 @@ declare function confirm(message?: string): boolean; * * If the stdin is not interactive, it returns null. * + * @example + * ```ts + * const pet = prompt("Cats or dogs?", "It's fine to love both!"); + * + * // Displays the user's input or the default value of "It's fine to love both!" + * console.log("Best pet:", pet); + * ``` + * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt + * * @category Platform * * @param message From 17d6e66ee336057954ab22544f30ea2761991a25 Mon Sep 17 00:00:00 2001 From: Jo Franchetti Date: Thu, 16 Jan 2025 14:48:13 +0000 Subject: [PATCH 05/12] docs: adding jsdocs info for console interface (#27666) Signed-off-by: Jo Franchetti Co-authored-by: Marvin Hagemeister --- cli/lsp/tsc.rs | 35 ++++- ext/console/lib.deno_console.d.ts | 224 +++++++++++++++++++++++++++++- 2 files changed, 255 insertions(+), 4 deletions(-) diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 482d3d6cf7..09e11380ac 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -6263,7 +6263,40 @@ mod tests { "kind": "keyword" } ], - "documentation": [] + "documentation": [ + { + "text": "Outputs a message to the console", + "kind": "text", + }, + ], + "tags": [ + { + "name": "param", + "text": [ + { + "text": "data", + "kind": "parameterName", + }, + { + "text": " ", + "kind": "space", + }, + { + "text": "Values to be printed to the console", + "kind": "text", + }, + ], + }, + { + "name": "example", + "text": [ + { + "text": "```ts\nconsole.log('Hello', 'World', 123);\n```", + "kind": "text", + }, + ], + }, + ] }) ); } diff --git a/ext/console/lib.deno_console.d.ts b/ext/console/lib.deno_console.d.ts index 54bbcab892..1f6c3fe682 100644 --- a/ext/console/lib.deno_console.d.ts +++ b/ext/console/lib.deno_console.d.ts @@ -6,33 +6,251 @@ /// /** @category I/O */ +/** + * The Console interface provides methods for logging information to the console, + * as well as other utility methods for debugging and inspecting code. + * @see https://developer.mozilla.org/en-US/docs/Web/API/console + */ +/** Interface representing the console object that provides methods for logging, debugging, and timing */ interface Console { + /** + * Tests that an expression is true. If not, logs an error message + * @param condition The expression to test for truthiness + * @param data Additional arguments to be printed if the assertion fails + * @example + * ```ts + * console.assert(1 === 1, "This won't show"); + * console.assert(1 === 2, "This will show an error"); + * ``` + */ assert(condition?: boolean, ...data: any[]): void; + + /** + * Clears the console if the environment allows it + * @example + * ```ts + * console.clear(); + * ``` + */ clear(): void; + + /** + * Maintains an internal counter for a given label, incrementing it each time the method is called + * @param label The label to count. Defaults to 'default' + * @example + * ```ts + * console.count('myCounter'); + * console.count('myCounter'); // Will show: myCounter: 2 + * ``` + */ count(label?: string): void; + + /** + * Resets the counter for a given label + * @param label The label to reset. Defaults to 'default' + * @example + * ```ts + * console.count('myCounter'); + * console.countReset('myCounter'); // Resets to 0 + * ``` + */ countReset(label?: string): void; + + /** + * Outputs a debugging message to the console + * @param data Values to be printed to the console + * @example + * ```ts + * console.debug('Debug message', { detail: 'some data' }); + * ``` + */ debug(...data: any[]): void; + + /** + * Displays a list of the properties of a specified object + * @param item Object to display + * @param options Formatting options + * @example + * ```ts + * console.dir({ name: 'object', value: 42 }, { depth: 1 }); + * ``` + */ dir(item?: any, options?: any): void; + + /** + * @ignore + */ dirxml(...data: any[]): void; + + /** + * Outputs an error message to the console. + * This method routes the output to stderr, + * unlike other console methods that route to stdout. + * @param data Values to be printed to the console + * @example + * ```ts + * console.error('Error occurred:', new Error('Something went wrong')); + * ``` + */ error(...data: any[]): void; + + /** + * Creates a new inline group in the console, indenting subsequent console messages + * @param data Labels for the group + * @example + * ```ts + * console.group('Group 1'); + * console.log('Inside group 1'); + * console.groupEnd(); + * ``` + */ group(...data: any[]): void; + + /** + * Creates a new inline group in the console that is initially collapsed + * @param data Labels for the group + * @example + * ```ts + * console.groupCollapsed('Details'); + * console.log('Hidden until expanded'); + * console.groupEnd(); + * ``` + */ groupCollapsed(...data: any[]): void; + + /** + * Exits the current inline group in the console + * @example + * ```ts + * console.group('Group'); + * console.log('Grouped message'); + * console.groupEnd(); + * ``` + */ groupEnd(): void; + + /** + * Outputs an informational message to the console + * @param data Values to be printed to the console + * @example + * ```ts + * console.info('Application started', { version: '1.0.0' }); + * ``` + */ info(...data: any[]): void; + + /** + * Outputs a message to the console + * @param data Values to be printed to the console + * @example + * ```ts + * console.log('Hello', 'World', 123); + * ``` + */ log(...data: any[]): void; + + /** + * Displays tabular data as a table + * @param tabularData Data to be displayed in table format + * @param properties Array of property names to be displayed + * @example + * ```ts + * console.table([ + * { name: 'John', age: 30 }, + * { name: 'Jane', age: 25 } + * ]); + * ``` + */ table(tabularData?: any, properties?: string[]): void; + + /** + * Starts a timer you can use to track how long an operation takes + * @param label Timer label. Defaults to 'default' + * @example + * ```ts + * console.time('operation'); + * // ... some code + * console.timeEnd('operation'); + * ``` + */ time(label?: string): void; + + /** + * Stops a timer that was previously started + * @param label Timer label to stop. Defaults to 'default' + * @example + * ```ts + * console.time('operation'); + * // ... some code + * console.timeEnd('operation'); // Prints: operation: 1234ms + * ``` + */ timeEnd(label?: string): void; + + /** + * Logs the current value of a timer that was previously started + * @param label Timer label + * @param data Additional data to log + * @example + * ```ts + * console.time('process'); + * // ... some code + * console.timeLog('process', 'Checkpoint A'); + * ``` + */ timeLog(label?: string, ...data: any[]): void; + + /** + * Outputs a stack trace to the console + * @param data Values to be printed to the console + * @example + * ```ts + * console.trace('Trace message'); + * ``` + */ trace(...data: any[]): void; + + /** + * Outputs a warning message to the console + * @param data Values to be printed to the console + * @example + * ```ts + * console.warn('Deprecated feature used'); + * ``` + */ warn(...data: any[]): void; - /** This method is a noop, unless used in inspector */ + /** + * Adds a marker to the DevTools Performance panel + * @param label Label for the timestamp + * @example + * ```ts + * console.timeStamp('Navigation Start'); + * ``` + */ timeStamp(label?: string): void; - /** This method is a noop, unless used in inspector */ + /** + * Starts recording a performance profile + * @param label Profile label + * @example + * ```ts + * console.profile('Performance Profile'); + * // ... code to profile + * console.profileEnd('Performance Profile'); + * ``` + */ profile(label?: string): void; - /** This method is a noop, unless used in inspector */ + /** + * Stops recording a performance profile + * @param label Profile label to stop + * @example + * ```ts + * console.profile('Performance Profile'); + * // ... code to profile + * console.profileEnd('Performance Profile'); + * ``` + */ profileEnd(label?: string): void; } From 2debe9c8dd06ec287034852dc41293cf851e8be6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Thu, 16 Jan 2025 18:27:54 +0000 Subject: [PATCH 06/12] fix(ext/console): change Temporal color (#27684) This commit changes output color of `Temporal` instances from "magenta" to "cyan" to discriminate them from `Date` instances. Closes https://github.com/denoland/deno/issues/27585 --- ext/console/01_console.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/console/01_console.js b/ext/console/01_console.js index a85faaccf1..09441a56a3 100644 --- a/ext/console/01_console.js +++ b/ext/console/01_console.js @@ -216,7 +216,7 @@ const styles = { regexp: "red", module: "underline", internalError: "red", - temporal: "magenta", + temporal: "cyan", }; const defaultFG = 39; From 464ee9155e9e3938ab460beb5c240f1d5492e67b Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:20:04 -0800 Subject: [PATCH 07/12] fix(check/lsp): fix bugs with tsc type resolution, allow npm packages to augment `ImportMeta` (#27690) Fixes #26224. Fixes #27042. There were three bugs here: - we were only resolving `/// ` directive in most of the vite templates) - the `$node_modules` workaround caused us to fail to read files for tsc. For instance tsc would construct new paths based on specifiers containing `$node_modules`, and since we hadn't created those we weren't mapping them back to the original (this broke some type resolution within `vite/client`) - our separation of `ImportMeta` across node and deno globals in tsc meant that npm packages couldn't augment `ImportMeta` (this broke `vite/client`'s augmentation to add `import.meta.env` and others) After this, the only remaining issue in the vanilla vite template is our error on `/vite.svg` (which is an ambient module), and I'll look into that next. --- cli/lsp/tsc.rs | 4 +- cli/tsc/99_main_compiler.js | 116 +++++++++++------- tests/integration/lsp_tests.rs | 79 +++++++++++- .../augments-global/1.0.0/import-meta.d.ts | 3 + .../augments-global/1.0.0/package.json | 10 +- .../1.0.0/real-import-meta.d.ts | 8 ++ .../import_meta_no_errors/__test__.jsonc | 53 ++++++++ .../check/import_meta_no_errors/deno.json | 5 + .../specs/check/import_meta_no_errors/main.ts | 3 + .../set_node_modules_dir.ts | 8 ++ .../type_reference_import_meta/__test__.jsonc | 53 ++++++++ .../type_reference_import_meta/deno.json | 6 + .../check/type_reference_import_meta/main.ts | 3 + .../set_node_modules_dir.ts | 8 ++ .../type_reference_import_meta/types.d.ts | 1 + 15 files changed, 311 insertions(+), 49 deletions(-) create mode 100644 tests/registry/npm/@denotest/augments-global/1.0.0/import-meta.d.ts create mode 100644 tests/registry/npm/@denotest/augments-global/1.0.0/real-import-meta.d.ts create mode 100644 tests/specs/check/import_meta_no_errors/__test__.jsonc create mode 100644 tests/specs/check/import_meta_no_errors/deno.json create mode 100644 tests/specs/check/import_meta_no_errors/main.ts create mode 100644 tests/specs/check/import_meta_no_errors/set_node_modules_dir.ts create mode 100644 tests/specs/check/type_reference_import_meta/__test__.jsonc create mode 100644 tests/specs/check/type_reference_import_meta/deno.json create mode 100644 tests/specs/check/type_reference_import_meta/main.ts create mode 100644 tests/specs/check/type_reference_import_meta/set_node_modules_dir.ts create mode 100644 tests/specs/check/type_reference_import_meta/types.d.ts diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index 09e11380ac..32352d9f26 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -4341,7 +4341,9 @@ impl TscSpecifierMap { if let Some(specifier) = self.normalized_specifiers.get(original) { return Ok(specifier.clone()); } - let specifier_str = original.replace(".d.ts.d.ts", ".d.ts"); + let specifier_str = original + .replace(".d.ts.d.ts", ".d.ts") + .replace("$node_modules", "node_modules"); let specifier = match ModuleSpecifier::parse(&specifier_str) { Ok(s) => s, Err(err) => return Err(err), diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index b3279f54ac..65319211fb 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -500,6 +500,8 @@ delete Object.prototype.__proto__; // Microsoft/TypeScript#26825 but that doesn't seem to be working here, // so we will ignore complaints about this compiler setting. 5070, + // TS6053: File '{0}' not found. + 6053, // TS7016: Could not find a declaration file for module '...'. '...' // implicitly has an 'any' type. This is due to `allowJs` being off by // default but importing of a JavaScript module. @@ -705,15 +707,14 @@ delete Object.prototype.__proto__; resolveTypeReferenceDirectiveReferences( typeDirectiveReferences, containingFilePath, - redirectedReference, + _redirectedReference, options, containingSourceFile, _reusedNames, ) { const isCjs = containingSourceFile?.impliedNodeFormat === ts.ModuleKind.CommonJS; - /** @type {Array} */ - const result = typeDirectiveReferences.map((arg) => { + const toResolve = typeDirectiveReferences.map((arg) => { /** @type {ts.FileReference} */ const fileReference = typeof arg === "string" ? { @@ -722,46 +723,50 @@ delete Object.prototype.__proto__; fileName: arg, } : arg; - if (fileReference.fileName.startsWith("npm:")) { - /** @type {[string, ts.Extension | null] | undefined} */ - const resolved = ops.op_resolve( - containingFilePath, - [ - [ - fileReference.resolutionMode == null - ? isCjs - : fileReference.resolutionMode === ts.ModuleKind.CommonJS, - fileReference.fileName, - ], - ], - )?.[0]; - if (resolved && resolved[1]) { - return { - resolvedTypeReferenceDirective: { - primary: true, - resolvedFileName: resolved[0], - // todo(dsherret): we should probably be setting this - isExternalLibraryImport: undefined, - }, - }; - } else { - return { - resolvedTypeReferenceDirective: undefined, - }; - } + return [ + fileReference.resolutionMode == null + ? isCjs + : fileReference.resolutionMode === ts.ModuleKind.CommonJS, + fileReference.fileName, + ]; + }); + + /** @type {Array<[string, ts.Extension | null] | undefined>} */ + const resolved = ops.op_resolve( + containingFilePath, + toResolve, + ); + + /** @type {Array} */ + const result = resolved.map((item) => { + if (item && item[1]) { + const [resolvedFileName, extension] = item; + return { + resolvedTypeReferenceDirective: { + primary: true, + resolvedFileName, + extension, + isExternalLibraryImport: false, + }, + }; } else { - return ts.resolveTypeReferenceDirective( - fileReference.fileName, - containingFilePath, - options, - host, - redirectedReference, - undefined, - containingSourceFile?.impliedNodeFormat ?? - fileReference.resolutionMode, - ); + return { + resolvedTypeReferenceDirective: undefined, + }; } }); + + if (logDebug) { + debug( + "resolveTypeReferenceDirectiveReferences ", + typeDirectiveReferences, + containingFilePath, + options, + containingSourceFile?.fileName, + " => ", + result, + ); + } return result; }, resolveModuleNameLiterals( @@ -1116,6 +1121,36 @@ delete Object.prototype.__proto__; if (IGNORED_DIAGNOSTICS.includes(diagnostic.code)) { return false; } + + // ignore diagnostics resulting from the `ImportMeta` declaration in deno merging with + // the one in @types/node. the types of the filename and dirname properties are different, + // which causes tsc to error. + const importMetaFilenameDirnameModifiersRe = + /^All declarations of '(filename|dirname)'/; + const importMetaFilenameDirnameTypesRe = + /^Subsequent property declarations must have the same type.\s+Property '(filename|dirname)'/; + // Declarations of X must have identical modifiers. + if (diagnostic.code === 2687) { + if ( + typeof diagnostic.messageText === "string" && + (importMetaFilenameDirnameModifiersRe.test(diagnostic.messageText)) && + (diagnostic.file?.fileName.startsWith("asset:///") || + diagnostic.file?.fileName?.includes("@types/node")) + ) { + return false; + } + } + // Subsequent property declarations must have the same type. + if (diagnostic.code === 2717) { + if ( + typeof diagnostic.messageText === "string" && + (importMetaFilenameDirnameTypesRe.test(diagnostic.messageText)) && + (diagnostic.file?.fileName.startsWith("asset:///") || + diagnostic.file?.fileName?.includes("@types/node")) + ) { + return false; + } + } // make the diagnostic for using an `export =` in an es module a warning if (diagnostic.code === 1203) { diagnostic.category = ts.DiagnosticCategory.Warning; @@ -1410,7 +1445,6 @@ delete Object.prototype.__proto__; "ErrorConstructor", "gc", "Global", - "ImportMeta", "localStorage", "queueMicrotask", "RequestInit", diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 3c1aa1c4ad..9607207163 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -17326,12 +17326,7 @@ fn compiler_options_types() { let mut deno_json = deno_json.clone(); deno_json["nodeModulesDir"] = json!(node_modules_dir); temp.write("deno.json", deno_json.to_string()); - context - .new_command() - .args("install") - .run() - .skip_output_check() - .assert_exit_code(0); + context.run_deno("install"); client.did_change_watched_files(json!({ "changes": [{ "uri": temp.url().join("deno.json").unwrap(), @@ -17345,3 +17340,75 @@ fn compiler_options_types() { client.did_close_file(&source); } } + +#[test] +fn type_reference_import_meta() { + let context = TestContextBuilder::for_npm().use_temp_cwd().build(); + let mut client = context.new_lsp_command().build(); + let temp = context.temp_dir(); + let temp_dir = temp.path(); + let source = source_file( + temp_dir.join("index.ts"), + r#" + const test = import.meta.env.TEST; + const bar = import.meta.bar; + console.log(test, bar); + "#, + ); + /* + tests type reference w/ bare specifier, type reference in an npm package, + and augmentation of `ImportMeta` (this combination modeled after the vanilla vite template, + which uses `vite/client`) + + @denotest/augments-global/import-meta: + ```dts + /// + + export type Foo = number; + ``` + + real-import-meta.d.ts: + ```dts + interface ImportMetaEnv { + TEST: string; + } + + interface ImportMeta { + env: ImportMetaEnv; + bar: number; + } + ``` + */ + temp.write( + "types.d.ts", + r#" + /// + "#, + ); + + let deno_json = json!({ + "imports": { + "@denotest/augments-global": "npm:@denotest/augments-global@1" + } + }); + temp.write("deno.json", deno_json.to_string()); + + client.initialize_default(); + + for node_modules_dir in ["none", "auto", "manual"] { + let mut deno_json = deno_json.clone(); + deno_json["nodeModulesDir"] = json!(node_modules_dir); + temp.write("deno.json", deno_json.to_string()); + context.run_deno("install"); + client.did_change_watched_files(json!({ + "changes": [{ + "uri": temp.url().join("deno.json").unwrap(), + "type": 2, + }], + })); + + let diagnostics = client.did_open_file(&source); + assert_eq!(diagnostics.all().len(), 0); + client.did_close_file(&source); + } +} diff --git a/tests/registry/npm/@denotest/augments-global/1.0.0/import-meta.d.ts b/tests/registry/npm/@denotest/augments-global/1.0.0/import-meta.d.ts new file mode 100644 index 0000000000..9dbe976c2f --- /dev/null +++ b/tests/registry/npm/@denotest/augments-global/1.0.0/import-meta.d.ts @@ -0,0 +1,3 @@ +/// + +export type Foo = number; \ No newline at end of file diff --git a/tests/registry/npm/@denotest/augments-global/1.0.0/package.json b/tests/registry/npm/@denotest/augments-global/1.0.0/package.json index 33f20414ab..a63e420d68 100644 --- a/tests/registry/npm/@denotest/augments-global/1.0.0/package.json +++ b/tests/registry/npm/@denotest/augments-global/1.0.0/package.json @@ -1,5 +1,13 @@ { "name": "@denotest/augments-global", "version": "1.0.0", - "types": "./index.d.ts" + "types": "./index.d.ts", + "exports": { + ".": { + "types": "./index.d.ts" + }, + "./import-meta": { + "types": "./import-meta.d.ts" + } + } } \ No newline at end of file diff --git a/tests/registry/npm/@denotest/augments-global/1.0.0/real-import-meta.d.ts b/tests/registry/npm/@denotest/augments-global/1.0.0/real-import-meta.d.ts new file mode 100644 index 0000000000..06875eeef3 --- /dev/null +++ b/tests/registry/npm/@denotest/augments-global/1.0.0/real-import-meta.d.ts @@ -0,0 +1,8 @@ +interface ImportMetaEnv { + TEST: string; +} + +interface ImportMeta { + env: ImportMetaEnv; + bar: number; +} \ No newline at end of file diff --git a/tests/specs/check/import_meta_no_errors/__test__.jsonc b/tests/specs/check/import_meta_no_errors/__test__.jsonc new file mode 100644 index 0000000000..e03edb297f --- /dev/null +++ b/tests/specs/check/import_meta_no_errors/__test__.jsonc @@ -0,0 +1,53 @@ +{ + "tempDir": true, + "tests": { + "node_modules_dir_none": { + "steps": [ + { + "args": "run -A ./set_node_modules_dir.ts none", + "output": "" + }, + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "check --all ./main.ts", + "output": "Check [WILDCARD]main.ts\n" + } + ] + }, + "node_modules_dir_auto": { + "steps": [ + { + "args": "run -A ./set_node_modules_dir.ts auto", + "output": "" + }, + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "check --all ./main.ts", + "output": "Check [WILDCARD]main.ts\n" + } + ] + }, + "node_modules_dir_manual": { + "steps": [ + { + "args": "run -A ./set_node_modules_dir.ts auto", + "output": "" + }, + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "check --all ./main.ts", + "output": "Check [WILDCARD]main.ts\n" + } + ] + } + } +} diff --git a/tests/specs/check/import_meta_no_errors/deno.json b/tests/specs/check/import_meta_no_errors/deno.json new file mode 100644 index 0000000000..4cc5c30d3a --- /dev/null +++ b/tests/specs/check/import_meta_no_errors/deno.json @@ -0,0 +1,5 @@ +{ + "imports": { + "@types/node": "npm:@types/node@*" + } +} diff --git a/tests/specs/check/import_meta_no_errors/main.ts b/tests/specs/check/import_meta_no_errors/main.ts new file mode 100644 index 0000000000..ff1b8e3629 --- /dev/null +++ b/tests/specs/check/import_meta_no_errors/main.ts @@ -0,0 +1,3 @@ +/// + +const _foo = import.meta.dirname; diff --git a/tests/specs/check/import_meta_no_errors/set_node_modules_dir.ts b/tests/specs/check/import_meta_no_errors/set_node_modules_dir.ts new file mode 100644 index 0000000000..656f215890 --- /dev/null +++ b/tests/specs/check/import_meta_no_errors/set_node_modules_dir.ts @@ -0,0 +1,8 @@ +if (Deno.args.length !== 1) { + console.error("Usage: set_node_modules_dir.ts "); + Deno.exit(1); +} +const setting = Deno.args[0].trim(); +const denoJson = JSON.parse(Deno.readTextFileSync("./deno.json")); +denoJson["nodeModulesDir"] = setting; +Deno.writeTextFileSync("./deno.json", JSON.stringify(denoJson, null, 2)); diff --git a/tests/specs/check/type_reference_import_meta/__test__.jsonc b/tests/specs/check/type_reference_import_meta/__test__.jsonc new file mode 100644 index 0000000000..f23081fef4 --- /dev/null +++ b/tests/specs/check/type_reference_import_meta/__test__.jsonc @@ -0,0 +1,53 @@ +{ + "tempDir": true, + "tests": { + "node_modules_dir_none": { + "steps": [ + { + "args": "run -A ./set_node_modules_dir.ts none", + "output": "" + }, + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "check ./main.ts", + "output": "Check [WILDCARD]main.ts\n" + } + ] + }, + "node_modules_dir_auto": { + "steps": [ + { + "args": "run -A ./set_node_modules_dir.ts auto", + "output": "" + }, + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "check ./main.ts", + "output": "Check [WILDCARD]main.ts\n" + } + ] + }, + "node_modules_dir_manual": { + "steps": [ + { + "args": "run -A ./set_node_modules_dir.ts auto", + "output": "" + }, + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "check ./main.ts", + "output": "Check [WILDCARD]main.ts\n" + } + ] + } + } +} diff --git a/tests/specs/check/type_reference_import_meta/deno.json b/tests/specs/check/type_reference_import_meta/deno.json new file mode 100644 index 0000000000..bea3f5b73d --- /dev/null +++ b/tests/specs/check/type_reference_import_meta/deno.json @@ -0,0 +1,6 @@ +{ + "imports": { + "@denotest/augments-global": "npm:@denotest/augments-global@1" + }, + "compilerOptions": { "types": ["./types.d.ts"] } +} diff --git a/tests/specs/check/type_reference_import_meta/main.ts b/tests/specs/check/type_reference_import_meta/main.ts new file mode 100644 index 0000000000..c0924e35ef --- /dev/null +++ b/tests/specs/check/type_reference_import_meta/main.ts @@ -0,0 +1,3 @@ +const test = import.meta.env.TEST; +const bar = import.meta.bar; +console.log(test, bar); diff --git a/tests/specs/check/type_reference_import_meta/set_node_modules_dir.ts b/tests/specs/check/type_reference_import_meta/set_node_modules_dir.ts new file mode 100644 index 0000000000..656f215890 --- /dev/null +++ b/tests/specs/check/type_reference_import_meta/set_node_modules_dir.ts @@ -0,0 +1,8 @@ +if (Deno.args.length !== 1) { + console.error("Usage: set_node_modules_dir.ts "); + Deno.exit(1); +} +const setting = Deno.args[0].trim(); +const denoJson = JSON.parse(Deno.readTextFileSync("./deno.json")); +denoJson["nodeModulesDir"] = setting; +Deno.writeTextFileSync("./deno.json", JSON.stringify(denoJson, null, 2)); diff --git a/tests/specs/check/type_reference_import_meta/types.d.ts b/tests/specs/check/type_reference_import_meta/types.d.ts new file mode 100644 index 0000000000..2df7d5371b --- /dev/null +++ b/tests/specs/check/type_reference_import_meta/types.d.ts @@ -0,0 +1 @@ +/// From 256950ddb6ff7029944f9da14147211d8cc20b7c Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:33:38 -0800 Subject: [PATCH 08/12] fix(outdated): retain strict semver specifier when updating (#27701) Fixes https://github.com/denoland/deno/issues/27697 If it's a strict bound (e.g. `1.0.0` as opposed to `^1.0.0` or other), retain the strictness when we update --- cli/tools/registry/pm/outdated.rs | 8 +++++++- tests/specs/update/deno_json/filtered/deno.json.out | 2 +- .../update/deno_json/update_latest/deno.json.out | 8 ++++---- .../update/deno_json/update_latest/deno.lock.out | 12 ++++++------ .../update/external_import_map/import_map.json.out | 2 +- .../filtered/member_b_package.json.out | 2 +- .../update_latest/subdir/member_a_deno.json.out | 4 ++-- .../update_latest/subdir/member_b_package.json.out | 4 ++-- .../update/package_json/update_latest/deno.lock.out | 4 ++-- .../package_json/update_latest/package.json.out | 2 +- 10 files changed, 27 insertions(+), 21 deletions(-) diff --git a/cli/tools/registry/pm/outdated.rs b/cli/tools/registry/pm/outdated.rs index 939c30b5c1..610ad48c1d 100644 --- a/cli/tools/registry/pm/outdated.rs +++ b/cli/tools/registry/pm/outdated.rs @@ -280,9 +280,15 @@ fn choose_new_version_req( if preferred.version <= resolved?.version { return None; } + let exact = if let Some(range) = dep.req.version_req.range() { + range.0[0].start == range.0[0].end + } else { + false + }; Some( VersionReq::parse_from_specifier( - format!("^{}", preferred.version).as_str(), + format!("{}{}", if exact { "" } else { "^" }, preferred.version) + .as_str(), ) .unwrap(), ) diff --git a/tests/specs/update/deno_json/filtered/deno.json.out b/tests/specs/update/deno_json/filtered/deno.json.out index 4458e2d037..2ad36ca1ec 100644 --- a/tests/specs/update/deno_json/filtered/deno.json.out +++ b/tests/specs/update/deno_json/filtered/deno.json.out @@ -5,7 +5,7 @@ "@denotest/subtract": "jsr:@denotest/subtract@^0.2.0", "@denotest/with-subpath": "jsr:@denotest/multiple-exports@0.5.0/data-json", "@denotest/breaking-change-between-versions": "npm:@denotest/breaking-change-between-versions@1.0.0", - "@denotest/bin": "npm:@denotest/bin@^1.0.0", + "@denotest/bin": "npm:@denotest/bin@1.0.0", "@denotest/has-patch-versions": "npm:@denotest/has-patch-versions@^0.1.0" }, "scopes": { diff --git a/tests/specs/update/deno_json/update_latest/deno.json.out b/tests/specs/update/deno_json/update_latest/deno.json.out index 5e4e99bd66..2b5d1f95d7 100644 --- a/tests/specs/update/deno_json/update_latest/deno.json.out +++ b/tests/specs/update/deno_json/update_latest/deno.json.out @@ -3,9 +3,9 @@ "@denotest/add": "jsr:@denotest/add@^1.0.0", "@denotest/add/": "jsr:/@denotest/add@^1.0.0/", "@denotest/subtract": "jsr:@denotest/subtract@^1.0.0", - "@denotest/with-subpath": "jsr:@denotest/multiple-exports@^1.0.0/data-json", - "@denotest/breaking-change-between-versions": "npm:@denotest/breaking-change-between-versions@^2.0.0", - "@denotest/bin": "npm:@denotest/bin@^1.0.0", + "@denotest/with-subpath": "jsr:@denotest/multiple-exports@1.0.0/data-json", + "@denotest/breaking-change-between-versions": "npm:@denotest/breaking-change-between-versions@2.0.0", + "@denotest/bin": "npm:@denotest/bin@1.0.0", "@denotest/has-patch-versions": "npm:@denotest/has-patch-versions@^0.2.0" }, "scopes": { @@ -13,7 +13,7 @@ "@denotest/add": "jsr:@denotest/add@^1.0.0", "@denotest/add/": "jsr:/@denotest/add@^1.0.0/", "@denotest/subtract": "jsr:@denotest/subtract@^1.0.0", - "@denotest/with-subpath": "jsr:@denotest/multiple-exports@^1.0.0/data-json" + "@denotest/with-subpath": "jsr:@denotest/multiple-exports@1.0.0/data-json" } } } diff --git a/tests/specs/update/deno_json/update_latest/deno.lock.out b/tests/specs/update/deno_json/update_latest/deno.lock.out index ad83546ab1..88403fc77e 100644 --- a/tests/specs/update/deno_json/update_latest/deno.lock.out +++ b/tests/specs/update/deno_json/update_latest/deno.lock.out @@ -2,10 +2,10 @@ "version": "4", "specifiers": { "jsr:@denotest/add@1": "1.0.0", - "jsr:@denotest/multiple-exports@1": "1.0.0", + "jsr:@denotest/multiple-exports@1.0.0": "1.0.0", "jsr:@denotest/subtract@1": "1.0.0", - "npm:@denotest/bin@1": "1.0.0", - "npm:@denotest/breaking-change-between-versions@2": "2.0.0", + "npm:@denotest/bin@1.0.0": "1.0.0", + "npm:@denotest/breaking-change-between-versions@2.0.0": "2.0.0", "npm:@denotest/has-patch-versions@0.2": "0.2.0" }, "jsr": { @@ -33,10 +33,10 @@ "workspace": { "dependencies": [ "jsr:@denotest/add@1", - "jsr:@denotest/multiple-exports@1", + "jsr:@denotest/multiple-exports@1.0.0", "jsr:@denotest/subtract@1", - "npm:@denotest/bin@1", - "npm:@denotest/breaking-change-between-versions@2", + "npm:@denotest/bin@1.0.0", + "npm:@denotest/breaking-change-between-versions@2.0.0", "npm:@denotest/has-patch-versions@0.2" ] } diff --git a/tests/specs/update/external_import_map/import_map.json.out b/tests/specs/update/external_import_map/import_map.json.out index b4e24decbc..998f49eec7 100644 --- a/tests/specs/update/external_import_map/import_map.json.out +++ b/tests/specs/update/external_import_map/import_map.json.out @@ -2,7 +2,7 @@ "imports": { "@denotest/add": "jsr:@denotest/add@^1.0.0", "@denotest/subtract": "jsr:@denotest/subtract@^1.0.0", - "@denotest/breaking-change-between-versions": "npm:@denotest/breaking-change-between-versions@^2.0.0", + "@denotest/breaking-change-between-versions": "npm:@denotest/breaking-change-between-versions@2.0.0", "@denotest/has-patch-versions": "npm:@denotest/has-patch-versions@^0.2.0" } } diff --git a/tests/specs/update/mixed_workspace/filtered/member_b_package.json.out b/tests/specs/update/mixed_workspace/filtered/member_b_package.json.out index 7e582feeab..ee3c0a8548 100644 --- a/tests/specs/update/mixed_workspace/filtered/member_b_package.json.out +++ b/tests/specs/update/mixed_workspace/filtered/member_b_package.json.out @@ -3,6 +3,6 @@ "version": "0.1.0", "dependencies": { "@denotest/has-patch-versions": "0.1.0", - "aliased": "npm:@denotest/bin@^1.0.0" + "aliased": "npm:@denotest/bin@1.0.0" } } diff --git a/tests/specs/update/mixed_workspace/update_latest/subdir/member_a_deno.json.out b/tests/specs/update/mixed_workspace/update_latest/subdir/member_a_deno.json.out index 9210123b82..bda55c6ec5 100644 --- a/tests/specs/update/mixed_workspace/update_latest/subdir/member_a_deno.json.out +++ b/tests/specs/update/mixed_workspace/update_latest/subdir/member_a_deno.json.out @@ -4,7 +4,7 @@ "imports": { "@denotest/add": "jsr:@denotest/add@^1.0.0", "@denotest/add/": "jsr:/@denotest/add@^1.0.0/", - "@denotest/with-subpath": "jsr:@denotest/multiple-exports@^1.0.0/data-json", - "@denotest/breaking-change-between-versions": "npm:@denotest/breaking-change-between-versions@^2.0.0" + "@denotest/with-subpath": "jsr:@denotest/multiple-exports@1.0.0/data-json", + "@denotest/breaking-change-between-versions": "npm:@denotest/breaking-change-between-versions@2.0.0" } } diff --git a/tests/specs/update/mixed_workspace/update_latest/subdir/member_b_package.json.out b/tests/specs/update/mixed_workspace/update_latest/subdir/member_b_package.json.out index 1426fcd7f8..9118e94654 100644 --- a/tests/specs/update/mixed_workspace/update_latest/subdir/member_b_package.json.out +++ b/tests/specs/update/mixed_workspace/update_latest/subdir/member_b_package.json.out @@ -2,7 +2,7 @@ "name": "@denotest/member-b", "version": "0.1.0", "dependencies": { - "@denotest/has-patch-versions": "^0.2.0", - "aliased": "npm:@denotest/bin@^1.0.0" + "@denotest/has-patch-versions": "0.2.0", + "aliased": "npm:@denotest/bin@1.0.0" } } diff --git a/tests/specs/update/package_json/update_latest/deno.lock.out b/tests/specs/update/package_json/update_latest/deno.lock.out index 9a9b1bad5e..6723c8d475 100644 --- a/tests/specs/update/package_json/update_latest/deno.lock.out +++ b/tests/specs/update/package_json/update_latest/deno.lock.out @@ -2,7 +2,7 @@ "version": "4", "specifiers": { "npm:@denotest/bin@1": "1.0.0", - "npm:@denotest/breaking-change-between-versions@2": "2.0.0", + "npm:@denotest/breaking-change-between-versions@2.0.0": "2.0.0", "npm:@denotest/has-patch-versions@0.2": "0.2.0" }, "npm": { @@ -20,7 +20,7 @@ "packageJson": { "dependencies": [ "npm:@denotest/bin@1", - "npm:@denotest/breaking-change-between-versions@2", + "npm:@denotest/breaking-change-between-versions@2.0.0", "npm:@denotest/has-patch-versions@0.2" ] } diff --git a/tests/specs/update/package_json/update_latest/package.json.out b/tests/specs/update/package_json/update_latest/package.json.out index fb483d78bd..ac3c3ff460 100644 --- a/tests/specs/update/package_json/update_latest/package.json.out +++ b/tests/specs/update/package_json/update_latest/package.json.out @@ -1,7 +1,7 @@ { "dependencies": { "@denotest/has-patch-versions": "^0.2.0", - "@denotest/breaking-change-between-versions": "^2.0.0" + "@denotest/breaking-change-between-versions": "2.0.0" }, "devDependencies": { "aliased": "npm:@denotest/bin@^1.0.0" From a5ba198b9aa189da58b015a6e4585aaf33208a46 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:03:25 -0800 Subject: [PATCH 09/12] fix(outdated): Use `latest` tag even when it's the same as the current version (#27699) Fixes https://github.com/denoland/deno/issues/27696. Just a `>` that should've been a `>=`. Also made sure to filter out deprecated versions. --- cli/tools/registry/pm/deps.rs | 15 +++++++++++++-- .../update/latest_not_pre_release/__test__.jsonc | 13 +++++++++++++ .../update/latest_not_pre_release/package.json | 5 +++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 tests/specs/update/latest_not_pre_release/__test__.jsonc create mode 100644 tests/specs/update/latest_not_pre_release/package.json diff --git a/cli/tools/registry/pm/deps.rs b/cli/tools/registry/pm/deps.rs index 3d152490e8..621dd4693d 100644 --- a/cli/tools/registry/pm/deps.rs +++ b/cli/tools/registry/pm/deps.rs @@ -683,10 +683,21 @@ impl DepManager { .and_then(|info| { let latest_tag = info.dist_tags.get("latest")?; let lower_bound = &semver_compatible.as_ref()?.version; - if latest_tag > lower_bound { + if latest_tag >= lower_bound { Some(latest_tag.clone()) } else { - latest_version(Some(latest_tag), info.versions.keys()) + latest_version( + Some(latest_tag), + info.versions.iter().filter_map( + |(version, version_info)| { + if version_info.deprecated.is_none() { + Some(version) + } else { + None + } + }, + ), + ) } }) .map(|version| PackageNv { diff --git a/tests/specs/update/latest_not_pre_release/__test__.jsonc b/tests/specs/update/latest_not_pre_release/__test__.jsonc new file mode 100644 index 0000000000..2bfb7a3aa5 --- /dev/null +++ b/tests/specs/update/latest_not_pre_release/__test__.jsonc @@ -0,0 +1,13 @@ +{ + "tempDir": true, + "steps": [ + { + "args": "install", + "output": "[WILDCARD]" + }, + { + "args": "outdated", + "output": "" + } + ] +} diff --git a/tests/specs/update/latest_not_pre_release/package.json b/tests/specs/update/latest_not_pre_release/package.json new file mode 100644 index 0000000000..581e10cce7 --- /dev/null +++ b/tests/specs/update/latest_not_pre_release/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "@denotest/has-pre-release": "1.0.0" + } +} From 94dc5b16f5c5ef28e4293d6edc0dd1b6338b41ec Mon Sep 17 00:00:00 2001 From: denobot <33910674+denobot@users.noreply.github.com> Date: Thu, 16 Jan 2025 20:09:13 -0500 Subject: [PATCH 10/12] chore: forward v2.1.6 release commit to main (#27705) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the release commit being forwarded back to main for 2.1.6 Co-authored-by: bartlomieju Co-authored-by: Bartek IwaƄczuk --- .github/workflows/ci.generate.ts | 2 +- .github/workflows/ci.yml | 8 ++-- Cargo.lock | 66 ++++++++++++++++---------------- Cargo.toml | 64 +++++++++++++++---------------- Releases.md | 26 +++++++++++++ bench_util/Cargo.toml | 2 +- cli/Cargo.toml | 2 +- cli/lib/Cargo.toml | 4 +- ext/broadcast_channel/Cargo.toml | 2 +- ext/cache/Cargo.toml | 2 +- ext/canvas/Cargo.toml | 2 +- ext/console/Cargo.toml | 2 +- ext/cron/Cargo.toml | 2 +- ext/crypto/Cargo.toml | 2 +- ext/fetch/Cargo.toml | 2 +- ext/ffi/Cargo.toml | 2 +- ext/fs/Cargo.toml | 2 +- ext/http/Cargo.toml | 2 +- ext/io/Cargo.toml | 2 +- ext/kv/Cargo.toml | 2 +- ext/napi/Cargo.toml | 2 +- ext/napi/sym/Cargo.toml | 2 +- ext/net/Cargo.toml | 2 +- ext/node/Cargo.toml | 2 +- ext/os/Cargo.toml | 2 +- ext/telemetry/Cargo.toml | 2 +- ext/tls/Cargo.toml | 2 +- ext/url/Cargo.toml | 2 +- ext/web/Cargo.toml | 2 +- ext/webgpu/Cargo.toml | 2 +- ext/webidl/Cargo.toml | 2 +- ext/websocket/Cargo.toml | 2 +- ext/webstorage/Cargo.toml | 2 +- resolvers/deno/Cargo.toml | 2 +- resolvers/node/Cargo.toml | 2 +- resolvers/npm_cache/Cargo.toml | 2 +- runtime/Cargo.toml | 2 +- runtime/permissions/Cargo.toml | 2 +- 38 files changed, 130 insertions(+), 104 deletions(-) diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index d0924f79ac..d302f92c98 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -5,7 +5,7 @@ import { stringify } from "jsr:@std/yaml@^0.221/stringify"; // Bump this number when you want to purge the cache. // Note: the tools/release/01_bump_crate_versions.ts script will update this version // automatically via regex, so ensure that this line maintains this format. -const cacheVersion = 35; +const cacheVersion = 36; const ubuntuX86Runner = "ubuntu-24.04"; const ubuntuX86XlRunner = "ubuntu-24.04-xl"; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75ded6fe53..501c23212a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -184,8 +184,8 @@ jobs: ~/.cargo/registry/index ~/.cargo/registry/cache ~/.cargo/git/db - key: '35-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' - restore-keys: '35-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-' + key: '36-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' + restore-keys: '36-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-' if: '!(matrix.skip)' - uses: dsherret/rust-toolchain-file@v1 if: '!(matrix.skip)' @@ -379,7 +379,7 @@ jobs: !./target/*/*.zip !./target/*/*.tar.gz key: never_saved - restore-keys: '35-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-' + restore-keys: '36-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-' - name: Apply and update mtime cache if: '!(matrix.skip) && (!startsWith(github.ref, ''refs/tags/''))' uses: ./.github/mtime_cache @@ -689,7 +689,7 @@ jobs: !./target/*/gn_root !./target/*/*.zip !./target/*/*.tar.gz - key: '35-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' + key: '36-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' publish-canary: name: publish canary runs-on: ubuntu-24.04 diff --git a/Cargo.lock b/Cargo.lock index 7ec72aa3cd..3e571a2c2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1244,7 +1244,7 @@ dependencies = [ [[package]] name = "deno" -version = "2.1.5" +version = "2.1.6" dependencies = [ "anstream", "async-trait", @@ -1422,7 +1422,7 @@ dependencies = [ [[package]] name = "deno_bench_util" -version = "0.179.0" +version = "0.180.0" dependencies = [ "bencher", "deno_core", @@ -1431,7 +1431,7 @@ dependencies = [ [[package]] name = "deno_broadcast_channel" -version = "0.179.0" +version = "0.180.0" dependencies = [ "async-trait", "deno_core", @@ -1443,7 +1443,7 @@ dependencies = [ [[package]] name = "deno_cache" -version = "0.117.0" +version = "0.118.0" dependencies = [ "async-trait", "deno_core", @@ -1486,7 +1486,7 @@ dependencies = [ [[package]] name = "deno_canvas" -version = "0.54.0" +version = "0.55.0" dependencies = [ "deno_core", "deno_error", @@ -1525,7 +1525,7 @@ dependencies = [ [[package]] name = "deno_console" -version = "0.185.0" +version = "0.186.0" dependencies = [ "deno_core", ] @@ -1576,7 +1576,7 @@ checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695" [[package]] name = "deno_cron" -version = "0.65.0" +version = "0.66.0" dependencies = [ "anyhow", "async-trait", @@ -1590,7 +1590,7 @@ dependencies = [ [[package]] name = "deno_crypto" -version = "0.199.0" +version = "0.200.0" dependencies = [ "aes", "aes-gcm", @@ -1683,7 +1683,7 @@ dependencies = [ [[package]] name = "deno_fetch" -version = "0.209.0" +version = "0.210.0" dependencies = [ "base64 0.21.7", "bytes", @@ -1720,7 +1720,7 @@ dependencies = [ [[package]] name = "deno_ffi" -version = "0.172.0" +version = "0.173.0" dependencies = [ "deno_core", "deno_error", @@ -1741,7 +1741,7 @@ dependencies = [ [[package]] name = "deno_fs" -version = "0.95.0" +version = "0.96.0" dependencies = [ "async-trait", "base32", @@ -1799,7 +1799,7 @@ dependencies = [ [[package]] name = "deno_http" -version = "0.183.0" +version = "0.184.0" dependencies = [ "async-compression", "async-trait", @@ -1839,7 +1839,7 @@ dependencies = [ [[package]] name = "deno_io" -version = "0.95.0" +version = "0.96.0" dependencies = [ "async-trait", "deno_core", @@ -1861,7 +1861,7 @@ dependencies = [ [[package]] name = "deno_kv" -version = "0.93.0" +version = "0.94.0" dependencies = [ "anyhow", "async-trait", @@ -1894,7 +1894,7 @@ dependencies = [ [[package]] name = "deno_lib" -version = "0.1.1" +version = "0.2.0" dependencies = [ "deno_cache_dir", "deno_error", @@ -1960,7 +1960,7 @@ dependencies = [ [[package]] name = "deno_napi" -version = "0.116.0" +version = "0.117.0" dependencies = [ "deno_core", "deno_error", @@ -1989,7 +1989,7 @@ dependencies = [ [[package]] name = "deno_net" -version = "0.177.0" +version = "0.178.0" dependencies = [ "deno_core", "deno_error", @@ -2008,7 +2008,7 @@ dependencies = [ [[package]] name = "deno_node" -version = "0.123.0" +version = "0.124.0" dependencies = [ "aead-gcm-stream", "aes", @@ -2121,7 +2121,7 @@ dependencies = [ [[package]] name = "deno_npm_cache" -version = "0.4.0" +version = "0.5.0" dependencies = [ "async-trait", "base64 0.21.7", @@ -2168,7 +2168,7 @@ dependencies = [ [[package]] name = "deno_os" -version = "0.1.0" +version = "0.3.0" dependencies = [ "deno_core", "deno_error", @@ -2220,7 +2220,7 @@ dependencies = [ [[package]] name = "deno_permissions" -version = "0.44.0" +version = "0.45.0" dependencies = [ "capacity_builder 0.5.0", "deno_core", @@ -2240,7 +2240,7 @@ dependencies = [ [[package]] name = "deno_resolver" -version = "0.16.0" +version = "0.17.0" dependencies = [ "anyhow", "async-trait", @@ -2266,7 +2266,7 @@ dependencies = [ [[package]] name = "deno_runtime" -version = "0.193.0" +version = "0.194.0" dependencies = [ "color-print", "deno_ast", @@ -2371,7 +2371,7 @@ dependencies = [ [[package]] name = "deno_telemetry" -version = "0.7.0" +version = "0.8.0" dependencies = [ "async-trait", "deno_core", @@ -2414,7 +2414,7 @@ dependencies = [ [[package]] name = "deno_tls" -version = "0.172.0" +version = "0.173.0" dependencies = [ "deno_core", "deno_error", @@ -2465,7 +2465,7 @@ dependencies = [ [[package]] name = "deno_url" -version = "0.185.0" +version = "0.186.0" dependencies = [ "deno_bench_util", "deno_console", @@ -2478,7 +2478,7 @@ dependencies = [ [[package]] name = "deno_web" -version = "0.216.0" +version = "0.217.0" dependencies = [ "async-trait", "base64-simd 0.8.0", @@ -2501,7 +2501,7 @@ dependencies = [ [[package]] name = "deno_webgpu" -version = "0.152.0" +version = "0.153.0" dependencies = [ "deno_core", "deno_error", @@ -2515,7 +2515,7 @@ dependencies = [ [[package]] name = "deno_webidl" -version = "0.185.0" +version = "0.186.0" dependencies = [ "deno_bench_util", "deno_core", @@ -2523,7 +2523,7 @@ dependencies = [ [[package]] name = "deno_websocket" -version = "0.190.0" +version = "0.191.0" dependencies = [ "bytes", "deno_core", @@ -2546,7 +2546,7 @@ dependencies = [ [[package]] name = "deno_webstorage" -version = "0.180.0" +version = "0.181.0" dependencies = [ "deno_core", "deno_error", @@ -5118,7 +5118,7 @@ dependencies = [ [[package]] name = "napi_sym" -version = "0.115.0" +version = "0.116.0" dependencies = [ "quote", "serde", @@ -5173,7 +5173,7 @@ dependencies = [ [[package]] name = "node_resolver" -version = "0.23.0" +version = "0.24.0" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 0c11ff9a69..46318bb828 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,17 +51,17 @@ repository = "https://github.com/denoland/deno" deno_ast = { version = "=0.44.0", features = ["transpiling"] } deno_core = { version = "0.330.0" } -deno_bench_util = { version = "0.179.0", path = "./bench_util" } +deno_bench_util = { version = "0.180.0", path = "./bench_util" } deno_config = { version = "=0.45.0", features = ["workspace", "sync"] } deno_lockfile = "=0.24.0" deno_media_type = { version = "0.2.3", features = ["module_specifier"] } deno_npm = "=0.27.2" deno_path_util = "=0.3.0" -deno_permissions = { version = "0.44.0", path = "./runtime/permissions" } -deno_runtime = { version = "0.193.0", path = "./runtime" } +deno_permissions = { version = "0.45.0", path = "./runtime/permissions" } +deno_runtime = { version = "0.194.0", path = "./runtime" } deno_semver = "=0.7.1" deno_terminal = "0.2.0" -napi_sym = { version = "0.115.0", path = "./ext/napi/sym" } +napi_sym = { version = "0.116.0", path = "./ext/napi/sym" } test_util = { package = "test_server", path = "./tests/util/server" } denokv_proto = "0.9.0" @@ -70,36 +70,36 @@ denokv_remote = "0.9.0" denokv_sqlite = { default-features = false, version = "0.9.0" } # exts -deno_broadcast_channel = { version = "0.179.0", path = "./ext/broadcast_channel" } -deno_cache = { version = "0.117.0", path = "./ext/cache" } -deno_canvas = { version = "0.54.0", path = "./ext/canvas" } -deno_console = { version = "0.185.0", path = "./ext/console" } -deno_cron = { version = "0.65.0", path = "./ext/cron" } -deno_crypto = { version = "0.199.0", path = "./ext/crypto" } -deno_fetch = { version = "0.209.0", path = "./ext/fetch" } -deno_ffi = { version = "0.172.0", path = "./ext/ffi" } -deno_fs = { version = "0.95.0", path = "./ext/fs" } -deno_http = { version = "0.183.0", path = "./ext/http" } -deno_io = { version = "0.95.0", path = "./ext/io" } -deno_kv = { version = "0.93.0", path = "./ext/kv" } -deno_napi = { version = "0.116.0", path = "./ext/napi" } -deno_net = { version = "0.177.0", path = "./ext/net" } -deno_node = { version = "0.123.0", path = "./ext/node" } -deno_os = { version = "0.1.0", path = "./ext/os" } -deno_telemetry = { version = "0.7.0", path = "./ext/telemetry" } -deno_tls = { version = "0.172.0", path = "./ext/tls" } -deno_url = { version = "0.185.0", path = "./ext/url" } -deno_web = { version = "0.216.0", path = "./ext/web" } -deno_webgpu = { version = "0.152.0", path = "./ext/webgpu" } -deno_webidl = { version = "0.185.0", path = "./ext/webidl" } -deno_websocket = { version = "0.190.0", path = "./ext/websocket" } -deno_webstorage = { version = "0.180.0", path = "./ext/webstorage" } +deno_broadcast_channel = { version = "0.180.0", path = "./ext/broadcast_channel" } +deno_cache = { version = "0.118.0", path = "./ext/cache" } +deno_canvas = { version = "0.55.0", path = "./ext/canvas" } +deno_console = { version = "0.186.0", path = "./ext/console" } +deno_cron = { version = "0.66.0", path = "./ext/cron" } +deno_crypto = { version = "0.200.0", path = "./ext/crypto" } +deno_fetch = { version = "0.210.0", path = "./ext/fetch" } +deno_ffi = { version = "0.173.0", path = "./ext/ffi" } +deno_fs = { version = "0.96.0", path = "./ext/fs" } +deno_http = { version = "0.184.0", path = "./ext/http" } +deno_io = { version = "0.96.0", path = "./ext/io" } +deno_kv = { version = "0.94.0", path = "./ext/kv" } +deno_napi = { version = "0.117.0", path = "./ext/napi" } +deno_net = { version = "0.178.0", path = "./ext/net" } +deno_node = { version = "0.124.0", path = "./ext/node" } +deno_os = { version = "0.3.0", path = "./ext/os" } +deno_telemetry = { version = "0.8.0", path = "./ext/telemetry" } +deno_tls = { version = "0.173.0", path = "./ext/tls" } +deno_url = { version = "0.186.0", path = "./ext/url" } +deno_web = { version = "0.217.0", path = "./ext/web" } +deno_webgpu = { version = "0.153.0", path = "./ext/webgpu" } +deno_webidl = { version = "0.186.0", path = "./ext/webidl" } +deno_websocket = { version = "0.191.0", path = "./ext/websocket" } +deno_webstorage = { version = "0.181.0", path = "./ext/webstorage" } # workspace libraries -deno_lib = { version = "=0.1.1", path = "./cli/lib" } -deno_npm_cache = { version = "0.4.0", path = "./resolvers/npm_cache" } -deno_resolver = { version = "0.16.0", path = "./resolvers/deno" } -node_resolver = { version = "0.23.0", path = "./resolvers/node" } +deno_lib = { version = "0.2.0", path = "./cli/lib" } +deno_npm_cache = { version = "0.5.0", path = "./resolvers/npm_cache" } +deno_resolver = { version = "0.17.0", path = "./resolvers/deno" } +node_resolver = { version = "0.24.0", path = "./resolvers/node" } aes = "=0.8.3" anyhow = "1.0.57" diff --git a/Releases.md b/Releases.md index 71cf524ca2..1fc6ebd1df 100644 --- a/Releases.md +++ b/Releases.md @@ -6,6 +6,32 @@ https://github.com/denoland/deno/releases We also have one-line install commands at: https://github.com/denoland/deno_install +### 2.1.6 / 2025.01.16 + +- fix(check/lsp): correctly resolve compilerOptions.types (#27686) +- fix(check/lsp): fix bugs with tsc type resolution, allow npm packages to + augment `ImportMeta` (#27690) +- fix(compile): store embedded fs case sensitivity (#27653) +- fix(compile/windows): better handling of deno_dir on different drive letter + than code (#27654) +- fix(ext/console): change Temporal color (#27684) +- fix(ext/node): add `writev` method to `FileHandle` (#27563) +- fix(ext/node): add chown method to FileHandle class (#27638) +- fix(ext/node): apply `@npmcli/agent` workaround to `npm-check-updates` + (#27639) +- fix(ext/node): fix playwright http client (#27662) +- fix(ext/node): show bare-node-builtin hint when using an import map (#27632) +- fix(ext/node): use primordials in `ext/node/polyfills/_fs_common.ts` (#27589) +- fix(lsp): handle pathless untitled URIs (#27637) +- fix(lsp/check): don't resolve unknown media types to a `.js` extension + (#27631) +- fix(node): Prevent node:child_process from always inheriting the parent + environment (#27343) (#27340) +- fix(node/fs): add utimes method to the FileHandle class (#27582) +- fix(outdated): Use `latest` tag even when it's the same as the current version + (#27699) +- fix(outdated): retain strict semver specifier when updating (#27701) + ### 2.1.5 / 2025.01.09 - feat(unstable): implement QUIC (#21942) diff --git a/bench_util/Cargo.toml b/bench_util/Cargo.toml index 30a88e931e..e2f1204eb2 100644 --- a/bench_util/Cargo.toml +++ b/bench_util/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_bench_util" -version = "0.179.0" +version = "0.180.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 4525a1bab4..d71047cc63 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno" -version = "2.1.5" +version = "2.1.6" authors.workspace = true default-run = "deno" edition.workspace = true diff --git a/cli/lib/Cargo.toml b/cli/lib/Cargo.toml index 6a74b22c2b..67caf6e944 100644 --- a/cli/lib/Cargo.toml +++ b/cli/lib/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_lib" -version = "0.1.1" +version = "0.2.0" authors.workspace = true edition.workspace = true license.workspace = true @@ -28,7 +28,7 @@ node_resolver = { workspace = true, features = ["sync"] } parking_lot.workspace = true ring.workspace = true serde = { workspace = true, features = ["derive"] } -sys_traits.workspace = true +sys_traits = { workspace = true, features = ["getrandom"] } thiserror.workspace = true tokio.workspace = true url.workspace = true diff --git a/ext/broadcast_channel/Cargo.toml b/ext/broadcast_channel/Cargo.toml index 0e1e0c3fbd..ff0034f0a8 100644 --- a/ext/broadcast_channel/Cargo.toml +++ b/ext/broadcast_channel/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_broadcast_channel" -version = "0.179.0" +version = "0.180.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/cache/Cargo.toml b/ext/cache/Cargo.toml index 18fbe23a23..4d15c8861b 100644 --- a/ext/cache/Cargo.toml +++ b/ext/cache/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_cache" -version = "0.117.0" +version = "0.118.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/canvas/Cargo.toml b/ext/canvas/Cargo.toml index e5c70b0054..0b89842f09 100644 --- a/ext/canvas/Cargo.toml +++ b/ext/canvas/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_canvas" -version = "0.54.0" +version = "0.55.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/console/Cargo.toml b/ext/console/Cargo.toml index 24cdb040a7..87ab697e36 100644 --- a/ext/console/Cargo.toml +++ b/ext/console/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_console" -version = "0.185.0" +version = "0.186.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/cron/Cargo.toml b/ext/cron/Cargo.toml index 7b41593841..7a5af4fc02 100644 --- a/ext/cron/Cargo.toml +++ b/ext/cron/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_cron" -version = "0.65.0" +version = "0.66.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml index c9876105e5..b1c0e8a24b 100644 --- a/ext/crypto/Cargo.toml +++ b/ext/crypto/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_crypto" -version = "0.199.0" +version = "0.200.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/fetch/Cargo.toml b/ext/fetch/Cargo.toml index a0d29291b0..0b98358104 100644 --- a/ext/fetch/Cargo.toml +++ b/ext/fetch/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_fetch" -version = "0.209.0" +version = "0.210.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/ffi/Cargo.toml b/ext/ffi/Cargo.toml index d71c04f9ff..f41ee2d644 100644 --- a/ext/ffi/Cargo.toml +++ b/ext/ffi/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_ffi" -version = "0.172.0" +version = "0.173.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/fs/Cargo.toml b/ext/fs/Cargo.toml index df9d140ac3..349c0a3be4 100644 --- a/ext/fs/Cargo.toml +++ b/ext/fs/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_fs" -version = "0.95.0" +version = "0.96.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/http/Cargo.toml b/ext/http/Cargo.toml index 01198cb8e6..103af9a27b 100644 --- a/ext/http/Cargo.toml +++ b/ext/http/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_http" -version = "0.183.0" +version = "0.184.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/io/Cargo.toml b/ext/io/Cargo.toml index 0de9dfca84..35f138376d 100644 --- a/ext/io/Cargo.toml +++ b/ext/io/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_io" -version = "0.95.0" +version = "0.96.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/kv/Cargo.toml b/ext/kv/Cargo.toml index 726dccdfba..6f7fcf8f54 100644 --- a/ext/kv/Cargo.toml +++ b/ext/kv/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_kv" -version = "0.93.0" +version = "0.94.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/napi/Cargo.toml b/ext/napi/Cargo.toml index ff69d6a47e..916f0c4da5 100644 --- a/ext/napi/Cargo.toml +++ b/ext/napi/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_napi" -version = "0.116.0" +version = "0.117.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/napi/sym/Cargo.toml b/ext/napi/sym/Cargo.toml index f845b639ba..198e52474b 100644 --- a/ext/napi/sym/Cargo.toml +++ b/ext/napi/sym/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "napi_sym" -version = "0.115.0" +version = "0.116.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/net/Cargo.toml b/ext/net/Cargo.toml index ba02fe8708..348d8682ae 100644 --- a/ext/net/Cargo.toml +++ b/ext/net/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_net" -version = "0.177.0" +version = "0.178.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 6cd2b32866..e9226163d6 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_node" -version = "0.123.0" +version = "0.124.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/os/Cargo.toml b/ext/os/Cargo.toml index fb553d1532..bc809f3485 100644 --- a/ext/os/Cargo.toml +++ b/ext/os/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_os" -version = "0.1.0" +version = "0.3.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/telemetry/Cargo.toml b/ext/telemetry/Cargo.toml index a83a08c3fe..4d00b82909 100644 --- a/ext/telemetry/Cargo.toml +++ b/ext/telemetry/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_telemetry" -version = "0.7.0" +version = "0.8.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/tls/Cargo.toml b/ext/tls/Cargo.toml index 39b3d17c86..8a9fdb00c0 100644 --- a/ext/tls/Cargo.toml +++ b/ext/tls/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_tls" -version = "0.172.0" +version = "0.173.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/url/Cargo.toml b/ext/url/Cargo.toml index 2078571e39..b4500aad3c 100644 --- a/ext/url/Cargo.toml +++ b/ext/url/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_url" -version = "0.185.0" +version = "0.186.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/web/Cargo.toml b/ext/web/Cargo.toml index 065f1a12b1..dda1e98b4b 100644 --- a/ext/web/Cargo.toml +++ b/ext/web/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_web" -version = "0.216.0" +version = "0.217.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/webgpu/Cargo.toml b/ext/webgpu/Cargo.toml index 369fdb02e6..7e8973cadd 100644 --- a/ext/webgpu/Cargo.toml +++ b/ext/webgpu/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webgpu" -version = "0.152.0" +version = "0.153.0" authors = ["the Deno authors"] edition.workspace = true license = "MIT" diff --git a/ext/webidl/Cargo.toml b/ext/webidl/Cargo.toml index adb072ff45..07ceb492b5 100644 --- a/ext/webidl/Cargo.toml +++ b/ext/webidl/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webidl" -version = "0.185.0" +version = "0.186.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/websocket/Cargo.toml b/ext/websocket/Cargo.toml index a94da90484..39b400a7ac 100644 --- a/ext/websocket/Cargo.toml +++ b/ext/websocket/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_websocket" -version = "0.190.0" +version = "0.191.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/webstorage/Cargo.toml b/ext/webstorage/Cargo.toml index 8a900f662b..04e6381b00 100644 --- a/ext/webstorage/Cargo.toml +++ b/ext/webstorage/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webstorage" -version = "0.180.0" +version = "0.181.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/resolvers/deno/Cargo.toml b/resolvers/deno/Cargo.toml index 534a07ccf2..bf4d68fa91 100644 --- a/resolvers/deno/Cargo.toml +++ b/resolvers/deno/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_resolver" -version = "0.16.0" +version = "0.17.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/resolvers/node/Cargo.toml b/resolvers/node/Cargo.toml index 1c2a342a9f..ee3a783860 100644 --- a/resolvers/node/Cargo.toml +++ b/resolvers/node/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "node_resolver" -version = "0.23.0" +version = "0.24.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/resolvers/npm_cache/Cargo.toml b/resolvers/npm_cache/Cargo.toml index d73cee9cd6..4c6cca2416 100644 --- a/resolvers/npm_cache/Cargo.toml +++ b/resolvers/npm_cache/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_npm_cache" -version = "0.4.0" +version = "0.5.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 808f79ab7b..b56b4a2b50 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_runtime" -version = "0.193.0" +version = "0.194.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/runtime/permissions/Cargo.toml b/runtime/permissions/Cargo.toml index 52501ee197..bdca6b379c 100644 --- a/runtime/permissions/Cargo.toml +++ b/runtime/permissions/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_permissions" -version = "0.44.0" +version = "0.45.0" authors.workspace = true edition.workspace = true license.workspace = true From 339bc44c58635f5a4b8b7410ae9fd256c13fe134 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Fri, 17 Jan 2025 12:30:00 +0900 Subject: [PATCH 11/12] fix(ext/node): propagate socket error to client request object (#27678) Co-authored-by: Satya Rohith --- ext/node/polyfills/http.ts | 15 +++++++++++++-- tests/unit_node/http_test.ts | 11 +++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/ext/node/polyfills/http.ts b/ext/node/polyfills/http.ts index f698ca01b3..ff85a61531 100644 --- a/ext/node/polyfills/http.ts +++ b/ext/node/polyfills/http.ts @@ -455,8 +455,13 @@ class ClientRequest extends OutgoingMessage { (async () => { try { const parsedUrl = new URL(url); - let baseConnRid = - this.socket._handle[kStreamBaseField][internalRidSymbol]; + const handle = this.socket._handle; + if (!handle) { + // Using non-standard socket. There's no way to handle this type of socket. + // This should be only happening in artificial test cases + return; + } + let baseConnRid = handle[kStreamBaseField][internalRidSymbol]; if (this._encrypted) { [baseConnRid] = op_tls_start({ rid: baseConnRid, @@ -637,6 +642,12 @@ class ClientRequest extends OutgoingMessage { }; this.socket = socket; this.emit("socket", socket); + socket.once("error", (err) => { + // This callback loosely follow `socketErrorListener` in Node.js + // https://github.com/nodejs/node/blob/f16cd10946ca9ad272f42b94f00cf960571c9181/lib/_http_client.js#L509 + emitErrorEvent(this, err); + socket.destroy(err); + }); if (socket.readyState === "opening") { socket.on("connect", onConnect); } else { diff --git a/tests/unit_node/http_test.ts b/tests/unit_node/http_test.ts index 7478546617..b4f0d260aa 100644 --- a/tests/unit_node/http_test.ts +++ b/tests/unit_node/http_test.ts @@ -1881,3 +1881,14 @@ Deno.test("[node/http] decompress brotli response", { "localhost:3000", ], ["user-agent", "Deno/2.1.1"]]); }); + +Deno.test("[node/http] an error with DNS propagates to request object", async () => { + const { resolve, promise } = Promise.withResolvers(); + const req = http.request("http://invalid-hostname.test", () => {}); + req.on("error", (err) => { + assertEquals(err.name, "Error"); + assertEquals(err.message, "getaddrinfo ENOTFOUND invalid-hostname.test"); + resolve(); + }); + await promise; +}); From 0050857f511e886e4af0a811fef16a43eaeb6e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Fri, 17 Jan 2025 12:30:14 +0000 Subject: [PATCH 12/12] refactor: add 'deno_process' crate (#27680) Untangled the whole `runtime/ops/process.rs` from `ext/node/` and moved to a separate `ext/process` crate. --- Cargo.lock | 30 +- Cargo.toml | 1 + cli/args/mod.rs | 2 +- cli/lib/worker.rs | 2 +- cli/npm/byonm.rs | 2 +- cli/npm/installer/common/lifecycle_scripts.rs | 4 +- cli/npm/managed.rs | 2 +- cli/npm/mod.rs | 2 +- ext/node/Cargo.toml | 2 +- ext/node/lib.rs | 2 - ext/node/ops/ipc.rs | 544 +---------------- ext/node/polyfills/child_process.ts | 2 +- ext/node/polyfills/internal/child_process.ts | 2 +- {runtime/js => ext/process}/40_process.js | 0 ext/process/Cargo.toml | 41 ++ ext/process/README.md | 3 + ext/process/ipc.rs | 558 ++++++++++++++++++ runtime/ops/process.rs => ext/process/lib.rs | 25 +- runtime/Cargo.toml | 2 + runtime/js/90_deno_ns.js | 2 +- runtime/lib.rs | 3 +- runtime/ops/mod.rs | 1 - runtime/ops/tty.rs | 5 +- runtime/shared.rs | 1 - runtime/snapshot.rs | 2 +- runtime/web_worker.rs | 8 +- runtime/worker.rs | 8 +- tools/core_import_map.json | 2 +- 28 files changed, 676 insertions(+), 582 deletions(-) rename {runtime/js => ext/process}/40_process.js (100%) create mode 100644 ext/process/Cargo.toml create mode 100644 ext/process/README.md create mode 100644 ext/process/ipc.rs rename runtime/ops/process.rs => ext/process/lib.rs (98%) diff --git a/Cargo.lock b/Cargo.lock index 3e571a2c2a..392ff5ef46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2031,6 +2031,7 @@ dependencies = [ "deno_package_json", "deno_path_util", "deno_permissions", + "deno_process", "deno_whoami", "der", "digest", @@ -2068,7 +2069,6 @@ dependencies = [ "p384", "path-clean", "pbkdf2", - "pin-project-lite", "pkcs8", "rand", "regex", @@ -2238,6 +2238,33 @@ dependencies = [ "winapi", ] +[[package]] +name = "deno_process" +version = "0.1.0" +dependencies = [ + "deno_core", + "deno_error", + "deno_fs", + "deno_io", + "deno_os", + "deno_path_util", + "deno_permissions", + "libc", + "log", + "memchr", + "nix", + "pin-project-lite", + "rand", + "serde", + "simd-json", + "tempfile", + "thiserror 2.0.3", + "tokio", + "which", + "winapi", + "windows-sys 0.59.0", +] + [[package]] name = "deno_resolver" version = "0.17.0" @@ -2290,6 +2317,7 @@ dependencies = [ "deno_os", "deno_path_util", "deno_permissions", + "deno_process", "deno_resolver", "deno_telemetry", "deno_terminal 0.2.0", diff --git a/Cargo.toml b/Cargo.toml index 46318bb828..42c2970e05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -86,6 +86,7 @@ deno_napi = { version = "0.117.0", path = "./ext/napi" } deno_net = { version = "0.178.0", path = "./ext/net" } deno_node = { version = "0.124.0", path = "./ext/node" } deno_os = { version = "0.3.0", path = "./ext/os" } +deno_process = { version = "0.1.0", path = "./ext/process" } deno_telemetry = { version = "0.8.0", path = "./ext/telemetry" } deno_tls = { version = "0.173.0", path = "./ext/tls" } deno_url = { version = "0.186.0", path = "./ext/url" } diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 29b493046f..f77eedc594 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -719,7 +719,7 @@ pub enum NpmProcessStateKind { } static NPM_PROCESS_STATE: Lazy> = Lazy::new(|| { - use deno_runtime::ops::process::NPM_RESOLUTION_STATE_FD_ENV_VAR_NAME; + use deno_runtime::deno_process::NPM_RESOLUTION_STATE_FD_ENV_VAR_NAME; let fd = std::env::var(NPM_RESOLUTION_STATE_FD_ENV_VAR_NAME).ok()?; std::env::remove_var(NPM_RESOLUTION_STATE_FD_ENV_VAR_NAME); let fd = fd.parse::().ok()?; diff --git a/cli/lib/worker.rs b/cli/lib/worker.rs index 7c9071d0ba..180d3eef8c 100644 --- a/cli/lib/worker.rs +++ b/cli/lib/worker.rs @@ -25,12 +25,12 @@ use deno_runtime::deno_node::NodeExtInitServices; use deno_runtime::deno_node::NodeRequireLoader; use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_permissions::PermissionsContainer; +use deno_runtime::deno_process::NpmProcessStateProviderRc; use deno_runtime::deno_telemetry::OtelConfig; use deno_runtime::deno_tls::RootCertStoreProvider; use deno_runtime::deno_web::BlobStore; use deno_runtime::fmt_errors::format_js_error; use deno_runtime::inspector_server::InspectorServer; -use deno_runtime::ops::process::NpmProcessStateProviderRc; use deno_runtime::ops::worker_host::CreateWebWorkerCb; use deno_runtime::web_worker::WebWorker; use deno_runtime::web_worker::WebWorkerOptions; diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs index 8dc498bb04..d52b222074 100644 --- a/cli/npm/byonm.rs +++ b/cli/npm/byonm.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use deno_core::serde_json; use deno_resolver::npm::ByonmNpmResolver; use deno_resolver::npm::ByonmNpmResolverCreateOptions; -use deno_runtime::ops::process::NpmProcessStateProvider; +use deno_runtime::deno_process::NpmProcessStateProvider; use crate::args::NpmProcessState; use crate::args::NpmProcessStateKind; diff --git a/cli/npm/installer/common/lifecycle_scripts.rs b/cli/npm/installer/common/lifecycle_scripts.rs index a0d821cdfc..3238b8d023 100644 --- a/cli/npm/installer/common/lifecycle_scripts.rs +++ b/cli/npm/installer/common/lifecycle_scripts.rs @@ -240,7 +240,7 @@ impl<'a> LifecycleScripts<'a> { // However, if we concurrently run scripts in the future we will // have to have multiple temp files. let temp_file_fd = - deno_runtime::ops::process::npm_process_state_tempfile( + deno_runtime::deno_process::npm_process_state_tempfile( process_state.as_bytes(), ) .map_err(LifecycleScriptsError::CreateNpmProcessState)?; @@ -248,7 +248,7 @@ impl<'a> LifecycleScripts<'a> { let _temp_file = unsafe { std::fs::File::from_raw_io_handle(temp_file_fd) }; // make sure the file gets closed env_vars.insert( - deno_runtime::ops::process::NPM_RESOLUTION_STATE_FD_ENV_VAR_NAME + deno_runtime::deno_process::NPM_RESOLUTION_STATE_FD_ENV_VAR_NAME .to_string(), (temp_file_fd as usize).to_string(), ); diff --git a/cli/npm/managed.rs b/cli/npm/managed.rs index 4122c881f1..049e3541db 100644 --- a/cli/npm/managed.rs +++ b/cli/npm/managed.rs @@ -14,7 +14,7 @@ use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; use deno_resolver::npm::managed::ManagedNpmResolverCreateOptions; use deno_resolver::npm::managed::NpmResolutionCell; use deno_resolver::npm::ManagedNpmResolverRc; -use deno_runtime::ops::process::NpmProcessStateProvider; +use deno_runtime::deno_process::NpmProcessStateProvider; use thiserror::Error; use super::CliNpmRegistryInfoProvider; diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs index fc0916cc18..a2cbd81d5b 100644 --- a/cli/npm/mod.rs +++ b/cli/npm/mod.rs @@ -12,7 +12,7 @@ use deno_core::url::Url; use deno_error::JsErrorBox; use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::registry::NpmPackageInfo; -use deno_runtime::ops::process::NpmProcessStateProviderRc; +use deno_runtime::deno_process::NpmProcessStateProviderRc; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use http::HeaderName; diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index e9226163d6..d4152ce58c 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -38,6 +38,7 @@ deno_net.workspace = true deno_package_json.workspace = true deno_path_util.workspace = true deno_permissions.workspace = true +deno_process.workspace = true deno_whoami = "0.1.0" der = { version = "0.7.9", features = ["derive"] } digest = { version = "0.10.5", features = ["core-api", "std"] } @@ -75,7 +76,6 @@ p256.workspace = true p384.workspace = true path-clean = "=0.1.0" pbkdf2 = "0.12.1" -pin-project-lite = "0.2.13" pkcs8 = { version = "0.10.2", features = ["std", "pkcs5", "encryption"] } rand.workspace = true regex.workspace = true diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 325cec6f5b..702a01e447 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -31,8 +31,6 @@ pub use deno_package_json::PackageJson; use deno_permissions::PermissionCheckError; pub use node_resolver::PathClean; pub use ops::ipc::ChildPipeFd; -pub use ops::ipc::IpcJsonStreamResource; -pub use ops::ipc::IpcRefTracker; use ops::vm; pub use ops::vm::create_v8_context; pub use ops::vm::init_global_template; diff --git a/ext/node/ops/ipc.rs b/ext/node/ops/ipc.rs index cf5e1e97ef..0213295c5a 100644 --- a/ext/node/ops/ipc.rs +++ b/ext/node/ops/ipc.rs @@ -8,38 +8,24 @@ mod impl_ { use std::cell::RefCell; use std::future::Future; use std::io; - use std::mem; - use std::pin::Pin; use std::rc::Rc; - use std::sync::atomic::AtomicBool; - use std::sync::atomic::AtomicUsize; - use std::task::ready; - use std::task::Context; - use std::task::Poll; use deno_core::op2; use deno_core::serde; use deno_core::serde::Serializer; use deno_core::serde_json; use deno_core::v8; - use deno_core::AsyncRefCell; use deno_core::CancelFuture; - use deno_core::CancelHandle; - use deno_core::ExternalOpsTracker; use deno_core::OpState; use deno_core::RcRef; use deno_core::ResourceId; use deno_core::ToV8; use deno_error::JsErrorBox; - use deno_io::BiPipe; - use deno_io::BiPipeRead; - use deno_io::BiPipeWrite; - use memchr::memchr; - use pin_project_lite::pin_project; + use deno_process::ipc::IpcJsonStreamError; + pub use deno_process::ipc::IpcJsonStreamResource; + pub use deno_process::ipc::IpcRefTracker; + pub use deno_process::ipc::INITIAL_CAPACITY; use serde::Serialize; - use tokio::io::AsyncRead; - use tokio::io::AsyncWriteExt; - use tokio::io::ReadBuf; /// Wrapper around v8 value that implements Serialize. struct SerializeWrapper<'a, 'b>( @@ -289,534 +275,12 @@ mod impl_ { stream.ref_tracker.unref(); } - /// Tracks whether the IPC resources is currently - /// refed, and allows refing/unrefing it. - pub struct IpcRefTracker { - refed: AtomicBool, - tracker: OpsTracker, - } - - /// A little wrapper so we don't have to get an - /// `ExternalOpsTracker` for tests. When we aren't - /// cfg(test), this will get optimized out. - enum OpsTracker { - External(ExternalOpsTracker), - #[cfg(test)] - Test, - } - - impl OpsTracker { - fn ref_(&self) { - match self { - Self::External(tracker) => tracker.ref_op(), - #[cfg(test)] - Self::Test => {} - } - } - - fn unref(&self) { - match self { - Self::External(tracker) => tracker.unref_op(), - #[cfg(test)] - Self::Test => {} - } - } - } - - impl IpcRefTracker { - pub fn new(tracker: ExternalOpsTracker) -> Self { - Self { - refed: AtomicBool::new(false), - tracker: OpsTracker::External(tracker), - } - } - - #[cfg(test)] - fn new_test() -> Self { - Self { - refed: AtomicBool::new(false), - tracker: OpsTracker::Test, - } - } - - fn ref_(&self) { - if !self.refed.swap(true, std::sync::atomic::Ordering::AcqRel) { - self.tracker.ref_(); - } - } - - fn unref(&self) { - if self.refed.swap(false, std::sync::atomic::Ordering::AcqRel) { - self.tracker.unref(); - } - } - } - - pub struct IpcJsonStreamResource { - read_half: AsyncRefCell, - write_half: AsyncRefCell, - cancel: Rc, - queued_bytes: AtomicUsize, - ref_tracker: IpcRefTracker, - } - - impl deno_core::Resource for IpcJsonStreamResource { - fn close(self: Rc) { - self.cancel.cancel(); - } - } - - impl IpcJsonStreamResource { - pub fn new( - stream: i64, - ref_tracker: IpcRefTracker, - ) -> Result { - let (read_half, write_half) = BiPipe::from_raw(stream as _)?.split(); - Ok(Self { - read_half: AsyncRefCell::new(IpcJsonStream::new(read_half)), - write_half: AsyncRefCell::new(write_half), - cancel: Default::default(), - queued_bytes: Default::default(), - ref_tracker, - }) - } - - #[cfg(all(unix, test))] - fn from_stream( - stream: tokio::net::UnixStream, - ref_tracker: IpcRefTracker, - ) -> Self { - let (read_half, write_half) = stream.into_split(); - Self { - read_half: AsyncRefCell::new(IpcJsonStream::new(read_half.into())), - write_half: AsyncRefCell::new(write_half.into()), - cancel: Default::default(), - queued_bytes: Default::default(), - ref_tracker, - } - } - - #[cfg(all(windows, test))] - fn from_stream( - pipe: tokio::net::windows::named_pipe::NamedPipeClient, - ref_tracker: IpcRefTracker, - ) -> Self { - let (read_half, write_half) = tokio::io::split(pipe); - Self { - read_half: AsyncRefCell::new(IpcJsonStream::new(read_half.into())), - write_half: AsyncRefCell::new(write_half.into()), - cancel: Default::default(), - queued_bytes: Default::default(), - ref_tracker, - } - } - - /// writes _newline terminated_ JSON message to the IPC pipe. - async fn write_msg_bytes( - self: Rc, - msg: &[u8], - ) -> Result<(), io::Error> { - let mut write_half = - RcRef::map(self, |r| &r.write_half).borrow_mut().await; - write_half.write_all(msg).await?; - Ok(()) - } - } - - // Initial capacity of the buffered reader and the JSON backing buffer. - // - // This is a tradeoff between memory usage and performance on large messages. - // - // 64kb has been chosen after benchmarking 64 to 66536 << 6 - 1 bytes per message. - const INITIAL_CAPACITY: usize = 1024 * 64; - - /// A buffer for reading from the IPC pipe. - /// Similar to the internal buffer of `tokio::io::BufReader`. - /// - /// This exists to provide buffered reading while granting mutable access - /// to the internal buffer (which isn't exposed through `tokio::io::BufReader` - /// or the `AsyncBufRead` trait). `simd_json` requires mutable access to an input - /// buffer for parsing, so this allows us to use the read buffer directly as the - /// input buffer without a copy (provided the message fits). - struct ReadBuffer { - buffer: Box<[u8]>, - pos: usize, - cap: usize, - } - - impl ReadBuffer { - fn new() -> Self { - Self { - buffer: vec![0; INITIAL_CAPACITY].into_boxed_slice(), - pos: 0, - cap: 0, - } - } - - fn get_mut(&mut self) -> &mut [u8] { - &mut self.buffer - } - - fn available_mut(&mut self) -> &mut [u8] { - &mut self.buffer[self.pos..self.cap] - } - - fn consume(&mut self, n: usize) { - self.pos = std::cmp::min(self.pos + n, self.cap); - } - - fn needs_fill(&self) -> bool { - self.pos >= self.cap - } - } - - #[derive(Debug, thiserror::Error, deno_error::JsError)] - pub enum IpcJsonStreamError { - #[class(inherit)] - #[error("{0}")] - Io(#[source] std::io::Error), - #[class(generic)] - #[error("{0}")] - SimdJson(#[source] simd_json::Error), - } - - // JSON serialization stream over IPC pipe. - // - // `\n` is used as a delimiter between messages. - struct IpcJsonStream { - pipe: BiPipeRead, - buffer: Vec, - read_buffer: ReadBuffer, - } - - impl IpcJsonStream { - fn new(pipe: BiPipeRead) -> Self { - Self { - pipe, - buffer: Vec::with_capacity(INITIAL_CAPACITY), - read_buffer: ReadBuffer::new(), - } - } - - async fn read_msg( - &mut self, - ) -> Result, IpcJsonStreamError> { - let mut json = None; - let nread = read_msg_inner( - &mut self.pipe, - &mut self.buffer, - &mut json, - &mut self.read_buffer, - ) - .await - .map_err(IpcJsonStreamError::Io)?; - if nread == 0 { - // EOF. - return Ok(None); - } - - let json = match json { - Some(v) => v, - None => { - // Took more than a single read and some buffering. - simd_json::from_slice(&mut self.buffer[..nread]) - .map_err(IpcJsonStreamError::SimdJson)? - } - }; - - // Safety: Same as `Vec::clear` but without the `drop_in_place` for - // each element (nop for u8). Capacity remains the same. - unsafe { - self.buffer.set_len(0); - } - - Ok(Some(json)) - } - } - - pin_project! { - #[must_use = "futures do nothing unless you `.await` or poll them"] - struct ReadMsgInner<'a, R: ?Sized> { - reader: &'a mut R, - buf: &'a mut Vec, - json: &'a mut Option, - // The number of bytes appended to buf. This can be less than buf.len() if - // the buffer was not empty when the operation was started. - read: usize, - read_buffer: &'a mut ReadBuffer, - } - } - - fn read_msg_inner<'a, R>( - reader: &'a mut R, - buf: &'a mut Vec, - json: &'a mut Option, - read_buffer: &'a mut ReadBuffer, - ) -> ReadMsgInner<'a, R> - where - R: AsyncRead + ?Sized + Unpin, - { - ReadMsgInner { - reader, - buf, - json, - read: 0, - read_buffer, - } - } - - fn read_msg_internal( - mut reader: Pin<&mut R>, - cx: &mut Context<'_>, - buf: &mut Vec, - read_buffer: &mut ReadBuffer, - json: &mut Option, - read: &mut usize, - ) -> Poll> { - loop { - let (done, used) = { - // effectively a tiny `poll_fill_buf`, but allows us to get a mutable reference to the buffer. - if read_buffer.needs_fill() { - let mut read_buf = ReadBuf::new(read_buffer.get_mut()); - ready!(reader.as_mut().poll_read(cx, &mut read_buf))?; - read_buffer.cap = read_buf.filled().len(); - read_buffer.pos = 0; - } - let available = read_buffer.available_mut(); - if let Some(i) = memchr(b'\n', available) { - if *read == 0 { - // Fast path: parse and put into the json slot directly. - json.replace( - simd_json::from_slice(&mut available[..i + 1]) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?, - ); - } else { - // This is not the first read, so we have to copy the data - // to make it contiguous. - buf.extend_from_slice(&available[..=i]); - } - (true, i + 1) - } else { - buf.extend_from_slice(available); - (false, available.len()) - } - }; - - read_buffer.consume(used); - *read += used; - if done || used == 0 { - return Poll::Ready(Ok(mem::replace(read, 0))); - } - } - } - - impl Future for ReadMsgInner<'_, R> { - type Output = io::Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let me = self.project(); - read_msg_internal( - Pin::new(*me.reader), - cx, - me.buf, - me.read_buffer, - me.json, - me.read, - ) - } - } - #[cfg(test)] mod tests { - use std::rc::Rc; - - use deno_core::serde_json::json; use deno_core::v8; use deno_core::JsRuntime; - use deno_core::RcRef; use deno_core::RuntimeOptions; - use super::IpcJsonStreamResource; - - #[allow(clippy::unused_async)] - #[cfg(unix)] - pub async fn pair() -> (Rc, tokio::net::UnixStream) { - let (a, b) = tokio::net::UnixStream::pair().unwrap(); - - /* Similar to how ops would use the resource */ - let a = Rc::new(IpcJsonStreamResource::from_stream( - a, - super::IpcRefTracker::new_test(), - )); - (a, b) - } - - #[cfg(windows)] - pub async fn pair() -> ( - Rc, - tokio::net::windows::named_pipe::NamedPipeServer, - ) { - use tokio::net::windows::named_pipe::ClientOptions; - use tokio::net::windows::named_pipe::ServerOptions; - - let name = - format!(r"\\.\pipe\deno-named-pipe-test-{}", rand::random::()); - - let server = ServerOptions::new().create(name.clone()).unwrap(); - let client = ClientOptions::new().open(name).unwrap(); - - server.connect().await.unwrap(); - /* Similar to how ops would use the resource */ - let client = Rc::new(IpcJsonStreamResource::from_stream( - client, - super::IpcRefTracker::new_test(), - )); - (client, server) - } - - #[allow(clippy::print_stdout)] - #[tokio::test] - async fn bench_ipc() -> Result<(), Box> { - // A simple round trip benchmark for quick dev feedback. - // - // Only ran when the env var is set. - if std::env::var_os("BENCH_IPC_DENO").is_none() { - return Ok(()); - } - - let (ipc, mut fd2) = pair().await; - let child = tokio::spawn(async move { - use tokio::io::AsyncWriteExt; - - let size = 1024 * 1024; - - let stri = "x".repeat(size); - let data = format!("\"{}\"\n", stri); - for _ in 0..100 { - fd2.write_all(data.as_bytes()).await?; - } - Ok::<_, std::io::Error>(()) - }); - - let start = std::time::Instant::now(); - let mut bytes = 0; - - let mut ipc = RcRef::map(ipc, |r| &r.read_half).borrow_mut().await; - loop { - let Some(msgs) = ipc.read_msg().await? else { - break; - }; - bytes += msgs.as_str().unwrap().len(); - if start.elapsed().as_secs() > 5 { - break; - } - } - let elapsed = start.elapsed(); - let mb = bytes as f64 / 1024.0 / 1024.0; - println!("{} mb/s", mb / elapsed.as_secs_f64()); - - child.await??; - - Ok(()) - } - - #[tokio::test] - async fn unix_ipc_json() -> Result<(), Box> { - let (ipc, mut fd2) = pair().await; - let child = tokio::spawn(async move { - use tokio::io::AsyncReadExt; - use tokio::io::AsyncWriteExt; - - const EXPECTED: &[u8] = b"\"hello\"\n"; - let mut buf = [0u8; EXPECTED.len()]; - let n = fd2.read_exact(&mut buf).await?; - assert_eq!(&buf[..n], EXPECTED); - fd2.write_all(b"\"world\"\n").await?; - - Ok::<_, std::io::Error>(()) - }); - - ipc - .clone() - .write_msg_bytes(&json_to_bytes(json!("hello"))) - .await?; - - let mut ipc = RcRef::map(ipc, |r| &r.read_half).borrow_mut().await; - let msgs = ipc.read_msg().await?.unwrap(); - assert_eq!(msgs, json!("world")); - - child.await??; - - Ok(()) - } - - fn json_to_bytes(v: deno_core::serde_json::Value) -> Vec { - let mut buf = deno_core::serde_json::to_vec(&v).unwrap(); - buf.push(b'\n'); - buf - } - - #[tokio::test] - async fn unix_ipc_json_multi() -> Result<(), Box> { - let (ipc, mut fd2) = pair().await; - let child = tokio::spawn(async move { - use tokio::io::AsyncReadExt; - use tokio::io::AsyncWriteExt; - - const EXPECTED: &[u8] = b"\"hello\"\n\"world\"\n"; - let mut buf = [0u8; EXPECTED.len()]; - let n = fd2.read_exact(&mut buf).await?; - assert_eq!(&buf[..n], EXPECTED); - fd2.write_all(b"\"foo\"\n\"bar\"\n").await?; - Ok::<_, std::io::Error>(()) - }); - - ipc - .clone() - .write_msg_bytes(&json_to_bytes(json!("hello"))) - .await?; - ipc - .clone() - .write_msg_bytes(&json_to_bytes(json!("world"))) - .await?; - - let mut ipc = RcRef::map(ipc, |r| &r.read_half).borrow_mut().await; - let msgs = ipc.read_msg().await?.unwrap(); - assert_eq!(msgs, json!("foo")); - - child.await??; - - Ok(()) - } - - #[tokio::test] - async fn unix_ipc_json_invalid() -> Result<(), Box> { - let (ipc, mut fd2) = pair().await; - let child = tokio::spawn(async move { - tokio::io::AsyncWriteExt::write_all(&mut fd2, b"\n\n").await?; - Ok::<_, std::io::Error>(()) - }); - - let mut ipc = RcRef::map(ipc, |r| &r.read_half).borrow_mut().await; - let _err = ipc.read_msg().await.unwrap_err(); - - child.await??; - - Ok(()) - } - - #[test] - fn memchr() { - let str = b"hello world"; - assert_eq!(super::memchr(b'h', str), Some(0)); - assert_eq!(super::memchr(b'w', str), Some(6)); - assert_eq!(super::memchr(b'd', str), Some(10)); - assert_eq!(super::memchr(b'x', str), None); - - let empty = b""; - assert_eq!(super::memchr(b'\n', empty), None); - } - fn wrap_expr(s: &str) -> String { format!("(function () {{ return {s}; }})()") } diff --git a/ext/node/polyfills/child_process.ts b/ext/node/polyfills/child_process.ts index 184b29bd2b..2bd8614275 100644 --- a/ext/node/polyfills/child_process.ts +++ b/ext/node/polyfills/child_process.ts @@ -53,7 +53,7 @@ import { convertToValidSignal, kEmptyObject, } from "ext:deno_node/internal/util.mjs"; -import { kNeedsNpmProcessState } from "ext:runtime/40_process.js"; +import { kNeedsNpmProcessState } from "ext:deno_process/40_process.js"; const MAX_BUFFER = 1024 * 1024; diff --git a/ext/node/polyfills/internal/child_process.ts b/ext/node/polyfills/internal/child_process.ts index 569ee7d328..9c9e084787 100644 --- a/ext/node/polyfills/internal/child_process.ts +++ b/ext/node/polyfills/internal/child_process.ts @@ -61,7 +61,7 @@ import { kExtraStdio, kIpc, kNeedsNpmProcessState, -} from "ext:runtime/40_process.js"; +} from "ext:deno_process/40_process.js"; export function mapValues( record: Readonly>, diff --git a/runtime/js/40_process.js b/ext/process/40_process.js similarity index 100% rename from runtime/js/40_process.js rename to ext/process/40_process.js diff --git a/ext/process/Cargo.toml b/ext/process/Cargo.toml new file mode 100644 index 0000000000..26f7a41a5e --- /dev/null +++ b/ext/process/Cargo.toml @@ -0,0 +1,41 @@ +# Copyright 2018-2025 the Deno authors. MIT license. + +[package] +name = "deno_process" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +readme = "README.md" +repository.workspace = true +description = "Subprocess APIs for Deno" + +[lib] +path = "lib.rs" + +[dependencies] +deno_core.workspace = true +deno_error.workspace = true +deno_fs.workspace = true +deno_io.workspace = true +deno_os.workspace = true +deno_path_util.workspace = true +deno_permissions.workspace = true +libc.workspace = true +log.workspace = true +memchr = "2.7.4" +pin-project-lite = "0.2.13" +rand.workspace = true +serde.workspace = true +simd-json = "0.14.0" +tempfile.workspace = true +thiserror.workspace = true +tokio.workspace = true +which.workspace = true + +[target.'cfg(unix)'.dependencies] +nix = { workspace = true, features = ["signal", "process"] } + +[target.'cfg(windows)'.dependencies] +winapi = { workspace = true, features = [] } +windows-sys.workspace = true diff --git a/ext/process/README.md b/ext/process/README.md new file mode 100644 index 0000000000..ac39ab83d6 --- /dev/null +++ b/ext/process/README.md @@ -0,0 +1,3 @@ +# deno_process + +This crate implements subprocess APIs for Deno diff --git a/ext/process/ipc.rs b/ext/process/ipc.rs new file mode 100644 index 0000000000..3728943457 --- /dev/null +++ b/ext/process/ipc.rs @@ -0,0 +1,558 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +#![allow(unused)] + +use std::cell::RefCell; +use std::future::Future; +use std::io; +use std::mem; +use std::pin::Pin; +use std::rc::Rc; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::AtomicUsize; +use std::task::ready; +use std::task::Context; +use std::task::Poll; + +use deno_core::serde; +use deno_core::serde_json; +use deno_core::AsyncRefCell; +use deno_core::CancelHandle; +use deno_core::ExternalOpsTracker; +use deno_core::RcRef; +use deno_io::BiPipe; +use deno_io::BiPipeRead; +use deno_io::BiPipeWrite; +use memchr::memchr; +use pin_project_lite::pin_project; +use tokio::io::AsyncRead; +use tokio::io::AsyncWriteExt; +use tokio::io::ReadBuf; + +/// Tracks whether the IPC resources is currently +/// refed, and allows refing/unrefing it. +pub struct IpcRefTracker { + refed: AtomicBool, + tracker: OpsTracker, +} + +/// A little wrapper so we don't have to get an +/// `ExternalOpsTracker` for tests. When we aren't +/// cfg(test), this will get optimized out. +enum OpsTracker { + External(ExternalOpsTracker), + #[cfg(test)] + Test, +} + +impl OpsTracker { + fn ref_(&self) { + match self { + Self::External(tracker) => tracker.ref_op(), + #[cfg(test)] + Self::Test => {} + } + } + + fn unref(&self) { + match self { + Self::External(tracker) => tracker.unref_op(), + #[cfg(test)] + Self::Test => {} + } + } +} + +impl IpcRefTracker { + pub fn new(tracker: ExternalOpsTracker) -> Self { + Self { + refed: AtomicBool::new(false), + tracker: OpsTracker::External(tracker), + } + } + + #[cfg(test)] + fn new_test() -> Self { + Self { + refed: AtomicBool::new(false), + tracker: OpsTracker::Test, + } + } + + pub fn ref_(&self) { + if !self.refed.swap(true, std::sync::atomic::Ordering::AcqRel) { + self.tracker.ref_(); + } + } + + pub fn unref(&self) { + if self.refed.swap(false, std::sync::atomic::Ordering::AcqRel) { + self.tracker.unref(); + } + } +} + +pub struct IpcJsonStreamResource { + pub read_half: AsyncRefCell, + pub write_half: AsyncRefCell, + pub cancel: Rc, + pub queued_bytes: AtomicUsize, + pub ref_tracker: IpcRefTracker, +} + +impl deno_core::Resource for IpcJsonStreamResource { + fn close(self: Rc) { + self.cancel.cancel(); + } +} + +impl IpcJsonStreamResource { + pub fn new( + stream: i64, + ref_tracker: IpcRefTracker, + ) -> Result { + let (read_half, write_half) = BiPipe::from_raw(stream as _)?.split(); + Ok(Self { + read_half: AsyncRefCell::new(IpcJsonStream::new(read_half)), + write_half: AsyncRefCell::new(write_half), + cancel: Default::default(), + queued_bytes: Default::default(), + ref_tracker, + }) + } + + #[cfg(all(unix, test))] + pub fn from_stream( + stream: tokio::net::UnixStream, + ref_tracker: IpcRefTracker, + ) -> Self { + let (read_half, write_half) = stream.into_split(); + Self { + read_half: AsyncRefCell::new(IpcJsonStream::new(read_half.into())), + write_half: AsyncRefCell::new(write_half.into()), + cancel: Default::default(), + queued_bytes: Default::default(), + ref_tracker, + } + } + + #[cfg(all(windows, test))] + pub fn from_stream( + pipe: tokio::net::windows::named_pipe::NamedPipeClient, + ref_tracker: IpcRefTracker, + ) -> Self { + let (read_half, write_half) = tokio::io::split(pipe); + Self { + read_half: AsyncRefCell::new(IpcJsonStream::new(read_half.into())), + write_half: AsyncRefCell::new(write_half.into()), + cancel: Default::default(), + queued_bytes: Default::default(), + ref_tracker, + } + } + + /// writes _newline terminated_ JSON message to the IPC pipe. + pub async fn write_msg_bytes( + self: Rc, + msg: &[u8], + ) -> Result<(), io::Error> { + let mut write_half = RcRef::map(self, |r| &r.write_half).borrow_mut().await; + write_half.write_all(msg).await?; + Ok(()) + } +} + +// Initial capacity of the buffered reader and the JSON backing buffer. +// +// This is a tradeoff between memory usage and performance on large messages. +// +// 64kb has been chosen after benchmarking 64 to 66536 << 6 - 1 bytes per message. +pub const INITIAL_CAPACITY: usize = 1024 * 64; + +/// A buffer for reading from the IPC pipe. +/// Similar to the internal buffer of `tokio::io::BufReader`. +/// +/// This exists to provide buffered reading while granting mutable access +/// to the internal buffer (which isn't exposed through `tokio::io::BufReader` +/// or the `AsyncBufRead` trait). `simd_json` requires mutable access to an input +/// buffer for parsing, so this allows us to use the read buffer directly as the +/// input buffer without a copy (provided the message fits). +struct ReadBuffer { + buffer: Box<[u8]>, + pos: usize, + cap: usize, +} + +impl ReadBuffer { + fn new() -> Self { + Self { + buffer: vec![0; INITIAL_CAPACITY].into_boxed_slice(), + pos: 0, + cap: 0, + } + } + + fn get_mut(&mut self) -> &mut [u8] { + &mut self.buffer + } + + fn available_mut(&mut self) -> &mut [u8] { + &mut self.buffer[self.pos..self.cap] + } + + fn consume(&mut self, n: usize) { + self.pos = std::cmp::min(self.pos + n, self.cap); + } + + fn needs_fill(&self) -> bool { + self.pos >= self.cap + } +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum IpcJsonStreamError { + #[class(inherit)] + #[error("{0}")] + Io(#[source] std::io::Error), + #[class(generic)] + #[error("{0}")] + SimdJson(#[source] simd_json::Error), +} + +// JSON serialization stream over IPC pipe. +// +// `\n` is used as a delimiter between messages. +pub struct IpcJsonStream { + pipe: BiPipeRead, + buffer: Vec, + read_buffer: ReadBuffer, +} + +impl IpcJsonStream { + fn new(pipe: BiPipeRead) -> Self { + Self { + pipe, + buffer: Vec::with_capacity(INITIAL_CAPACITY), + read_buffer: ReadBuffer::new(), + } + } + + pub async fn read_msg( + &mut self, + ) -> Result, IpcJsonStreamError> { + let mut json = None; + let nread = read_msg_inner( + &mut self.pipe, + &mut self.buffer, + &mut json, + &mut self.read_buffer, + ) + .await + .map_err(IpcJsonStreamError::Io)?; + if nread == 0 { + // EOF. + return Ok(None); + } + + let json = match json { + Some(v) => v, + None => { + // Took more than a single read and some buffering. + simd_json::from_slice(&mut self.buffer[..nread]) + .map_err(IpcJsonStreamError::SimdJson)? + } + }; + + // Safety: Same as `Vec::clear` but without the `drop_in_place` for + // each element (nop for u8). Capacity remains the same. + unsafe { + self.buffer.set_len(0); + } + + Ok(Some(json)) + } +} + +pin_project! { + #[must_use = "futures do nothing unless you `.await` or poll them"] + struct ReadMsgInner<'a, R: ?Sized> { + reader: &'a mut R, + buf: &'a mut Vec, + json: &'a mut Option, + // The number of bytes appended to buf. This can be less than buf.len() if + // the buffer was not empty when the operation was started. + read: usize, + read_buffer: &'a mut ReadBuffer, + } +} + +fn read_msg_inner<'a, R>( + reader: &'a mut R, + buf: &'a mut Vec, + json: &'a mut Option, + read_buffer: &'a mut ReadBuffer, +) -> ReadMsgInner<'a, R> +where + R: AsyncRead + ?Sized + Unpin, +{ + ReadMsgInner { + reader, + buf, + json, + read: 0, + read_buffer, + } +} + +fn read_msg_internal( + mut reader: Pin<&mut R>, + cx: &mut Context<'_>, + buf: &mut Vec, + read_buffer: &mut ReadBuffer, + json: &mut Option, + read: &mut usize, +) -> Poll> { + loop { + let (done, used) = { + // effectively a tiny `poll_fill_buf`, but allows us to get a mutable reference to the buffer. + if read_buffer.needs_fill() { + let mut read_buf = ReadBuf::new(read_buffer.get_mut()); + ready!(reader.as_mut().poll_read(cx, &mut read_buf))?; + read_buffer.cap = read_buf.filled().len(); + read_buffer.pos = 0; + } + let available = read_buffer.available_mut(); + if let Some(i) = memchr(b'\n', available) { + if *read == 0 { + // Fast path: parse and put into the json slot directly. + json.replace( + simd_json::from_slice(&mut available[..i + 1]) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?, + ); + } else { + // This is not the first read, so we have to copy the data + // to make it contiguous. + buf.extend_from_slice(&available[..=i]); + } + (true, i + 1) + } else { + buf.extend_from_slice(available); + (false, available.len()) + } + }; + + read_buffer.consume(used); + *read += used; + if done || used == 0 { + return Poll::Ready(Ok(mem::replace(read, 0))); + } + } +} + +impl Future for ReadMsgInner<'_, R> { + type Output = io::Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let me = self.project(); + read_msg_internal( + Pin::new(*me.reader), + cx, + me.buf, + me.read_buffer, + me.json, + me.read, + ) + } +} + +#[cfg(test)] +mod tests { + use std::rc::Rc; + + use deno_core::serde_json::json; + use deno_core::v8; + use deno_core::JsRuntime; + use deno_core::RcRef; + use deno_core::RuntimeOptions; + + use super::IpcJsonStreamResource; + + #[allow(clippy::unused_async)] + #[cfg(unix)] + pub async fn pair() -> (Rc, tokio::net::UnixStream) { + let (a, b) = tokio::net::UnixStream::pair().unwrap(); + + /* Similar to how ops would use the resource */ + let a = Rc::new(IpcJsonStreamResource::from_stream( + a, + super::IpcRefTracker::new_test(), + )); + (a, b) + } + + #[cfg(windows)] + pub async fn pair() -> ( + Rc, + tokio::net::windows::named_pipe::NamedPipeServer, + ) { + use tokio::net::windows::named_pipe::ClientOptions; + use tokio::net::windows::named_pipe::ServerOptions; + + let name = + format!(r"\\.\pipe\deno-named-pipe-test-{}", rand::random::()); + + let server = ServerOptions::new().create(name.clone()).unwrap(); + let client = ClientOptions::new().open(name).unwrap(); + + server.connect().await.unwrap(); + /* Similar to how ops would use the resource */ + let client = Rc::new(IpcJsonStreamResource::from_stream( + client, + super::IpcRefTracker::new_test(), + )); + (client, server) + } + + #[allow(clippy::print_stdout)] + #[tokio::test] + async fn bench_ipc() -> Result<(), Box> { + // A simple round trip benchmark for quick dev feedback. + // + // Only ran when the env var is set. + if std::env::var_os("BENCH_IPC_DENO").is_none() { + return Ok(()); + } + + let (ipc, mut fd2) = pair().await; + let child = tokio::spawn(async move { + use tokio::io::AsyncWriteExt; + + let size = 1024 * 1024; + + let stri = "x".repeat(size); + let data = format!("\"{}\"\n", stri); + for _ in 0..100 { + fd2.write_all(data.as_bytes()).await?; + } + Ok::<_, std::io::Error>(()) + }); + + let start = std::time::Instant::now(); + let mut bytes = 0; + + let mut ipc = RcRef::map(ipc, |r| &r.read_half).borrow_mut().await; + loop { + let Some(msgs) = ipc.read_msg().await? else { + break; + }; + bytes += msgs.as_str().unwrap().len(); + if start.elapsed().as_secs() > 5 { + break; + } + } + let elapsed = start.elapsed(); + let mb = bytes as f64 / 1024.0 / 1024.0; + println!("{} mb/s", mb / elapsed.as_secs_f64()); + + child.await??; + + Ok(()) + } + + #[tokio::test] + async fn unix_ipc_json() -> Result<(), Box> { + let (ipc, mut fd2) = pair().await; + let child = tokio::spawn(async move { + use tokio::io::AsyncReadExt; + use tokio::io::AsyncWriteExt; + + const EXPECTED: &[u8] = b"\"hello\"\n"; + let mut buf = [0u8; EXPECTED.len()]; + let n = fd2.read_exact(&mut buf).await?; + assert_eq!(&buf[..n], EXPECTED); + fd2.write_all(b"\"world\"\n").await?; + + Ok::<_, std::io::Error>(()) + }); + + ipc + .clone() + .write_msg_bytes(&json_to_bytes(json!("hello"))) + .await?; + + let mut ipc = RcRef::map(ipc, |r| &r.read_half).borrow_mut().await; + let msgs = ipc.read_msg().await?.unwrap(); + assert_eq!(msgs, json!("world")); + + child.await??; + + Ok(()) + } + + fn json_to_bytes(v: deno_core::serde_json::Value) -> Vec { + let mut buf = deno_core::serde_json::to_vec(&v).unwrap(); + buf.push(b'\n'); + buf + } + + #[tokio::test] + async fn unix_ipc_json_multi() -> Result<(), Box> { + let (ipc, mut fd2) = pair().await; + let child = tokio::spawn(async move { + use tokio::io::AsyncReadExt; + use tokio::io::AsyncWriteExt; + + const EXPECTED: &[u8] = b"\"hello\"\n\"world\"\n"; + let mut buf = [0u8; EXPECTED.len()]; + let n = fd2.read_exact(&mut buf).await?; + assert_eq!(&buf[..n], EXPECTED); + fd2.write_all(b"\"foo\"\n\"bar\"\n").await?; + Ok::<_, std::io::Error>(()) + }); + + ipc + .clone() + .write_msg_bytes(&json_to_bytes(json!("hello"))) + .await?; + ipc + .clone() + .write_msg_bytes(&json_to_bytes(json!("world"))) + .await?; + + let mut ipc = RcRef::map(ipc, |r| &r.read_half).borrow_mut().await; + let msgs = ipc.read_msg().await?.unwrap(); + assert_eq!(msgs, json!("foo")); + + child.await??; + + Ok(()) + } + + #[tokio::test] + async fn unix_ipc_json_invalid() -> Result<(), Box> { + let (ipc, mut fd2) = pair().await; + let child = tokio::spawn(async move { + tokio::io::AsyncWriteExt::write_all(&mut fd2, b"\n\n").await?; + Ok::<_, std::io::Error>(()) + }); + + let mut ipc = RcRef::map(ipc, |r| &r.read_half).borrow_mut().await; + let _err = ipc.read_msg().await.unwrap_err(); + + child.await??; + + Ok(()) + } + + #[test] + fn memchr() { + let str = b"hello world"; + assert_eq!(super::memchr(b'h', str), Some(0)); + assert_eq!(super::memchr(b'w', str), Some(6)); + assert_eq!(super::memchr(b'd', str), Some(10)); + assert_eq!(super::memchr(b'x', str), None); + + let empty = b""; + assert_eq!(super::memchr(b'\n', empty), None); + } +} diff --git a/runtime/ops/process.rs b/ext/process/lib.rs similarity index 98% rename from runtime/ops/process.rs rename to ext/process/lib.rs index fc32f7e066..24985a8048 100644 --- a/runtime/ops/process.rs +++ b/ext/process/lib.rs @@ -38,6 +38,10 @@ use serde::Deserialize; use serde::Serialize; use tokio::process::Command; +pub mod ipc; +use ipc::IpcJsonStreamResource; +use ipc::IpcRefTracker; + pub const UNSTABLE_FEATURE_NAME: &str = "process"; #[derive(Copy, Clone, Eq, PartialEq, Deserialize)] @@ -153,6 +157,7 @@ deno_core::extension!( deprecated::op_run_status, deprecated::op_kill, ], + esm = ["40_process.js"], options = { get_npm_process_state: Option }, state = |state, options| { state.put::(options.get_npm_process_state.unwrap_or(deno_fs::sync::MaybeArc::new(EmptyNpmProcessStateProvider))); @@ -462,13 +467,10 @@ fn create_command( fds_to_dup.push((ipc_fd2, ipc)); fds_to_close.push(ipc_fd2); /* One end returned to parent process (this) */ - let pipe_rid = - state - .resource_table - .add(deno_node::IpcJsonStreamResource::new( - ipc_fd1 as _, - deno_node::IpcRefTracker::new(state.external_ops_tracker.clone()), - )?); + let pipe_rid = state.resource_table.add(IpcJsonStreamResource::new( + ipc_fd1 as _, + IpcRefTracker::new(state.external_ops_tracker.clone()), + )?); /* The other end passed to child process via NODE_CHANNEL_FD */ command.env("NODE_CHANNEL_FD", format!("{}", ipc)); ipc_rid = Some(pipe_rid); @@ -532,12 +534,11 @@ fn create_command( let (hd1, hd2) = deno_io::bi_pipe_pair_raw()?; /* One end returned to parent process (this) */ - let pipe_rid = Some(state.resource_table.add( - deno_node::IpcJsonStreamResource::new( + let pipe_rid = + Some(state.resource_table.add(IpcJsonStreamResource::new( hd1 as i64, - deno_node::IpcRefTracker::new(state.external_ops_tracker.clone()), - )?, - )); + IpcRefTracker::new(state.external_ops_tracker.clone()), + )?)); /* The other end passed to child process via NODE_CHANNEL_FD */ command.env("NODE_CHANNEL_FD", format!("{}", hd2 as i64)); diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index b56b4a2b50..b87d4cfbdf 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -60,6 +60,7 @@ deno_kv.workspace = true deno_tls.workspace = true deno_url.workspace = true deno_web.workspace = true +deno_process.workspace = true deno_webgpu.workspace = true deno_webidl.workspace = true deno_websocket.workspace = true @@ -93,6 +94,7 @@ deno_node.workspace = true deno_os.workspace = true deno_path_util.workspace = true deno_permissions.workspace = true +deno_process.workspace = true deno_resolver.workspace = true deno_telemetry.workspace = true deno_terminal.workspace = true diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index b241028077..5aaf0614dc 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -23,7 +23,7 @@ import * as io from "ext:deno_io/12_io.js"; import * as fs from "ext:deno_fs/30_fs.js"; import * as os from "ext:deno_os/30_os.js"; import * as fsEvents from "ext:runtime/40_fs_events.js"; -import * as process from "ext:runtime/40_process.js"; +import * as process from "ext:deno_process/40_process.js"; import * as signals from "ext:deno_os/40_signals.js"; import * as tty from "ext:runtime/40_tty.js"; import * as kv from "ext:deno_kv/01_db.ts"; diff --git a/runtime/lib.rs b/runtime/lib.rs index 65d3e88bae..c83fe5d60b 100644 --- a/runtime/lib.rs +++ b/runtime/lib.rs @@ -18,6 +18,7 @@ pub use deno_net; pub use deno_node; pub use deno_os; pub use deno_permissions; +pub use deno_process; pub use deno_telemetry; pub use deno_terminal::colors; pub use deno_tls; @@ -115,7 +116,7 @@ pub static UNSTABLE_GRANULAR_FLAGS: &[UnstableGranularFlag] = &[ }, // TODO(bartlomieju): consider removing it UnstableGranularFlag { - name: ops::process::UNSTABLE_FEATURE_NAME, + name: deno_process::UNSTABLE_FEATURE_NAME, help_text: "Enable unstable process APIs", show_in_help: false, id: 10, diff --git a/runtime/ops/mod.rs b/runtime/ops/mod.rs index d131e9aab5..04065ff2f8 100644 --- a/runtime/ops/mod.rs +++ b/runtime/ops/mod.rs @@ -4,7 +4,6 @@ pub mod bootstrap; pub mod fs_events; pub mod http; pub mod permissions; -pub mod process; pub mod runtime; pub mod tty; pub mod web_worker; diff --git a/runtime/ops/tty.rs b/runtime/ops/tty.rs index d9912839b8..a929b7d18a 100644 --- a/runtime/ops/tty.rs +++ b/runtime/ops/tty.rs @@ -50,14 +50,13 @@ impl TtyModeStore { } } +#[cfg(unix)] +use deno_process::JsNixError; #[cfg(windows)] use winapi::shared::minwindef::DWORD; #[cfg(windows)] use winapi::um::wincon; -#[cfg(unix)] -use crate::ops::process::JsNixError; - deno_core::extension!( deno_tty, ops = [op_set_raw, op_console_size, op_read_line_prompt], diff --git a/runtime/shared.rs b/runtime/shared.rs index 0e747b0565..ecf2088fe1 100644 --- a/runtime/shared.rs +++ b/runtime/shared.rs @@ -43,7 +43,6 @@ extension!(runtime, "10_permissions.js", "11_workers.js", "40_fs_events.js", - "40_process.js", "40_tty.js", "41_prompt.js", "90_deno_ns.js", diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs index 08ea17a986..eec8579e59 100644 --- a/runtime/snapshot.rs +++ b/runtime/snapshot.rs @@ -311,6 +311,7 @@ pub fn create_runtime_snapshot( deno_io::deno_io::init_ops_and_esm(Default::default()), deno_fs::deno_fs::init_ops_and_esm::(fs.clone()), deno_os::deno_os::init_ops_and_esm(Default::default()), + deno_process::deno_process::init_ops_and_esm(Default::default()), deno_node::deno_node::init_ops_and_esm::< Permissions, DenoInNpmPackageChecker, @@ -325,7 +326,6 @@ pub fn create_runtime_snapshot( ), ops::fs_events::deno_fs_events::init_ops(), ops::permissions::deno_permissions::init_ops(), - ops::process::deno_process::init_ops(None), ops::tty::deno_tty::init_ops(), ops::http::deno_http_runtime::init_ops(), ops::bootstrap::deno_bootstrap::init_ops(Some(snapshot_options)), diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index e4ea42a2f7..bb769c46a9 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -44,6 +44,7 @@ use deno_kv::dynamic::MultiBackendDbHandler; use deno_node::ExtNodeSys; use deno_node::NodeExtInitServices; use deno_permissions::PermissionsContainer; +use deno_process::NpmProcessStateProviderRc; use deno_terminal::colors; use deno_tls::RootCertStoreProvider; use deno_tls::TlsKeys; @@ -59,7 +60,6 @@ use node_resolver::NpmPackageFolderResolver; use crate::inspector_server::InspectorServer; use crate::ops; -use crate::ops::process::NpmProcessStateProviderRc; use crate::shared::maybe_transpile_source; use crate::shared::runtime; use crate::tokio_util::create_and_run_current_thread; @@ -529,6 +529,9 @@ impl WebWorker { services.fs.clone(), ), deno_os::deno_os_worker::init_ops_and_esm(), + deno_process::deno_process::init_ops_and_esm( + services.npm_process_state_provider, + ), deno_node::deno_node::init_ops_and_esm::< PermissionsContainer, TInNpmPackageChecker, @@ -543,9 +546,6 @@ impl WebWorker { ), ops::fs_events::deno_fs_events::init_ops_and_esm(), ops::permissions::deno_permissions::init_ops_and_esm(), - ops::process::deno_process::init_ops_and_esm( - services.npm_process_state_provider, - ), ops::tty::deno_tty::init_ops_and_esm(), ops::http::deno_http_runtime::init_ops_and_esm(), ops::bootstrap::deno_bootstrap::init_ops_and_esm( diff --git a/runtime/worker.rs b/runtime/worker.rs index 426383a19e..72eb54ec47 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -40,6 +40,7 @@ use deno_node::ExtNodeSys; use deno_node::NodeExtInitServices; use deno_os::ExitCode; use deno_permissions::PermissionsContainer; +use deno_process::NpmProcessStateProviderRc; use deno_tls::RootCertStoreProvider; use deno_tls::TlsKeys; use deno_web::BlobStore; @@ -51,7 +52,6 @@ use crate::code_cache::CodeCache; use crate::code_cache::CodeCacheType; use crate::inspector_server::InspectorServer; use crate::ops; -use crate::ops::process::NpmProcessStateProviderRc; use crate::shared::maybe_transpile_source; use crate::shared::runtime; use crate::BootstrapOptions; @@ -428,6 +428,9 @@ impl MainWorker { services.fs.clone(), ), deno_os::deno_os::init_ops_and_esm(exit_code.clone()), + deno_process::deno_process::init_ops_and_esm( + services.npm_process_state_provider, + ), deno_node::deno_node::init_ops_and_esm::< PermissionsContainer, TInNpmPackageChecker, @@ -442,9 +445,6 @@ impl MainWorker { ), ops::fs_events::deno_fs_events::init_ops_and_esm(), ops::permissions::deno_permissions::init_ops_and_esm(), - ops::process::deno_process::init_ops_and_esm( - services.npm_process_state_provider, - ), ops::tty::deno_tty::init_ops_and_esm(), ops::http::deno_http_runtime::init_ops_and_esm(), ops::bootstrap::deno_bootstrap::init_ops_and_esm( diff --git a/tools/core_import_map.json b/tools/core_import_map.json index 0176f47e23..935c7179a1 100644 --- a/tools/core_import_map.json +++ b/tools/core_import_map.json @@ -244,7 +244,7 @@ "ext:runtime/11_workers.js": "../runtime/js/11_workers.js", "ext:deno_os/30_os.js": "../ext/os/30_os.js", "ext:runtime/40_fs_events.js": "../runtime/js/40_fs_events.js", - "ext:runtime/40_process.js": "../runtime/js/40_process.js", + "ext:deno_process/40_process.js": "../ext/process/40_process.js", "ext:deno_os/40_signals.js": "../ext/os/40_signals.js", "ext:runtime/40_tty.js": "../runtime/js/40_tty.js", "ext:runtime/41_prompt.js": "../runtime/js/41_prompt.js",