From 241fa7bc97c678a8021a136cf9abc142cb0e21a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 3 Sep 2024 11:43:10 +0100 Subject: [PATCH 01/45] test: reenable frozen_lockfile tests (#25381) Ref https://github.com/denoland/deno/issues/25241 --- tests/specs/lockfile/frozen_lockfile/__test__.jsonc | 4 +--- tests/specs/lockfile/frozen_lockfile/deno.json | 2 +- .../specs/lockfile/frozen_lockfile/frozen_new_dep_cache.out | 2 +- .../frozen_lockfile/frozen_new_dep_dynamic_http.out | 2 +- .../lockfile/frozen_lockfile/frozen_new_dep_dynamic_jsr.out | 2 +- .../lockfile/frozen_lockfile/frozen_new_dep_dynamic_npm.out | 2 +- .../lockfile/frozen_lockfile/frozen_new_dep_jsr_cache.out | 2 +- .../lockfile/frozen_lockfile/frozen_new_dep_jsr_run.out | 2 +- tests/specs/lockfile/frozen_lockfile/frozen_new_dep_run.out | 2 +- tests/specs/lockfile/frozen_lockfile/no_lockfile_run.out | 2 +- tests/specs/lockfile/only_package_json/__test__.jsonc | 6 ++---- .../lockfile/only_package_json/{cache.out => install.out} | 0 12 files changed, 12 insertions(+), 16 deletions(-) rename tests/specs/lockfile/only_package_json/{cache.out => install.out} (100%) diff --git a/tests/specs/lockfile/frozen_lockfile/__test__.jsonc b/tests/specs/lockfile/frozen_lockfile/__test__.jsonc index 52cb6321b1..36a1fc71ef 100644 --- a/tests/specs/lockfile/frozen_lockfile/__test__.jsonc +++ b/tests/specs/lockfile/frozen_lockfile/__test__.jsonc @@ -1,7 +1,5 @@ { "tempDir": true, - // TODO(2.0): re-enable after DENO_FUTURE=1 by default lands - "ignore": true, "tests": { "error_with_new_npm_dep": { "steps": [ @@ -64,7 +62,7 @@ { "args": [ "eval", - "Deno.writeTextFileSync('deno.json', `{ \"nodeModules\": \"local-auto\" }`)" + "Deno.writeTextFileSync('deno.json', `{ \"nodeModulesDir\": \"auto\" }`)" ], "output": "[WILDCARD]" }, diff --git a/tests/specs/lockfile/frozen_lockfile/deno.json b/tests/specs/lockfile/frozen_lockfile/deno.json index 176354f98f..fbd70ec480 100644 --- a/tests/specs/lockfile/frozen_lockfile/deno.json +++ b/tests/specs/lockfile/frozen_lockfile/deno.json @@ -1,3 +1,3 @@ { - "nodeModulesDir": true + "nodeModulesDir": "auto" } diff --git a/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_cache.out b/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_cache.out index 55ada3dc3e..9a25fd413f 100644 --- a/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_cache.out +++ b/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_cache.out @@ -1,4 +1,4 @@ -error: The lockfile is out of date. Run `deno cache --frozen=false` or rerun with `--frozen=false` to update it. +error: The lockfile is out of date. Run `deno cache --frozen=false`, `deno install --frozen=false`, or rerun with `--frozen=false` to update it. changes: 4 | - "npm:@denotest/add@1": "1.0.0" 4 | + "npm:@denotest/add@1": "1.0.0", diff --git a/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_dynamic_http.out b/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_dynamic_http.out index 3ec45581aa..2e10aaca54 100644 --- a/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_dynamic_http.out +++ b/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_dynamic_http.out @@ -1,5 +1,5 @@ Download http://localhost:4545/welcome.ts -error: Uncaught (in promise) TypeError: The lockfile is out of date. Run `deno cache --frozen=false` or rerun with `--frozen=false` to update it. +error: Uncaught (in promise) TypeError: The lockfile is out of date. Run `deno cache --frozen=false`, `deno install --frozen=false`, or rerun with `--frozen=false` to update it. changes: 10 | - } 10 | + }, diff --git a/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_dynamic_jsr.out b/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_dynamic_jsr.out index e2b29706c0..6a3dcf5c4b 100644 --- a/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_dynamic_jsr.out +++ b/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_dynamic_jsr.out @@ -1,7 +1,7 @@ Download http://127.0.0.1:4250/@denotest/add/meta.json Download http://127.0.0.1:4250/@denotest/add/1.0.0_meta.json Download http://127.0.0.1:4250/@denotest/add/1.0.0/mod.ts -error: Uncaught (in promise) TypeError: The lockfile is out of date. Run `deno cache --frozen=false` or rerun with `--frozen=false` to update it. +error: Uncaught (in promise) TypeError: The lockfile is out of date. Run `deno cache --frozen=false`, `deno install --frozen=false`, or rerun with `--frozen=false` to update it. changes: 4 | - "npm:@denotest/add@1": "1.0.0" 4 | + "jsr:@denotest/add@1": "1.0.0", diff --git a/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_dynamic_npm.out b/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_dynamic_npm.out index 368d8de5e1..1f866f24e8 100644 --- a/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_dynamic_npm.out +++ b/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_dynamic_npm.out @@ -1,5 +1,5 @@ Download http://localhost:4260/@denotest/subtract -error: Uncaught (in promise) TypeError: The lockfile is out of date. Run `deno cache --frozen=false` or rerun with `--frozen=false` to update it. +error: Uncaught (in promise) TypeError: The lockfile is out of date. Run `deno cache --frozen=false`, `deno install --frozen=false`, or rerun with `--frozen=false` to update it. changes: 4 | - "npm:@denotest/add@1": "1.0.0" 4 | + "npm:@denotest/add@1": "1.0.0", diff --git a/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_jsr_cache.out b/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_jsr_cache.out index 5265400ec3..0ed46a9491 100644 --- a/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_jsr_cache.out +++ b/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_jsr_cache.out @@ -1,4 +1,4 @@ -error: The lockfile is out of date. Run `deno cache --frozen=false` or rerun with `--frozen=false` to update it. +error: The lockfile is out of date. Run `deno cache --frozen=false`, `deno install --frozen=false`, or rerun with `--frozen=false` to update it. changes: 4 | - "jsr:@denotest/add@1": "1.0.0" 5 | - }, diff --git a/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_jsr_run.out b/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_jsr_run.out index 215427a0d0..6e7a1462f8 100644 --- a/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_jsr_run.out +++ b/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_jsr_run.out @@ -1,6 +1,6 @@ Download http://127.0.0.1:4250/@denotest/add/0.2.0_meta.json Download http://127.0.0.1:4250/@denotest/add/0.2.0/mod.ts -error: The lockfile is out of date. Run `deno cache --frozen=false` or rerun with `--frozen=false` to update it. +error: The lockfile is out of date. Run `deno cache --frozen=false`, `deno install --frozen=false`, or rerun with `--frozen=false` to update it. changes: 4 | - "jsr:@denotest/add@1": "1.0.0" 5 | - }, diff --git a/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_run.out b/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_run.out index 351afbae7c..6645c913e0 100644 --- a/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_run.out +++ b/tests/specs/lockfile/frozen_lockfile/frozen_new_dep_run.out @@ -1,5 +1,5 @@ Download http://localhost:4260/@denotest/subtract -error: The lockfile is out of date. Run `deno cache --frozen=false` or rerun with `--frozen=false` to update it. +error: The lockfile is out of date. Run `deno cache --frozen=false`, `deno install --frozen=false`, or rerun with `--frozen=false` to update it. changes: 4 | - "npm:@denotest/add@1": "1.0.0" 4 | + "npm:@denotest/add@1": "1.0.0", diff --git a/tests/specs/lockfile/frozen_lockfile/no_lockfile_run.out b/tests/specs/lockfile/frozen_lockfile/no_lockfile_run.out index f04aea55cb..ba63f475a6 100644 --- a/tests/specs/lockfile/frozen_lockfile/no_lockfile_run.out +++ b/tests/specs/lockfile/frozen_lockfile/no_lockfile_run.out @@ -1,5 +1,5 @@ Download http://localhost:4260/@denotest/add -error: The lockfile is out of date. Run `deno cache --frozen=false` or rerun with `--frozen=false` to update it. +error: The lockfile is out of date. Run `deno cache --frozen=false`, `deno install --frozen=false`, or rerun with `--frozen=false` to update it. changes: 1 | - 1 | +{ diff --git a/tests/specs/lockfile/only_package_json/__test__.jsonc b/tests/specs/lockfile/only_package_json/__test__.jsonc index 5d79d7a872..f53d68f7df 100644 --- a/tests/specs/lockfile/only_package_json/__test__.jsonc +++ b/tests/specs/lockfile/only_package_json/__test__.jsonc @@ -1,11 +1,9 @@ { "tempDir": true, - // TODO(2.0): re-enable after DENO_FUTURE=1 by default lands - "ignore": true, "steps": [ { - "args": "cache index.js", - "output": "cache.out" + "args": "install", + "output": "install.out" }, { "args": [ diff --git a/tests/specs/lockfile/only_package_json/cache.out b/tests/specs/lockfile/only_package_json/install.out similarity index 100% rename from tests/specs/lockfile/only_package_json/cache.out rename to tests/specs/lockfile/only_package_json/install.out From 03d8e474d7c2a2f28c266a1c6d2199f6f16f5afe Mon Sep 17 00:00:00 2001 From: Asher Gomez Date: Tue, 3 Sep 2024 20:46:13 +1000 Subject: [PATCH 02/45] BREAKING(io): remove `Deno.copy()` (#25345) Towards #22079 --------- Signed-off-by: Asher Gomez --- cli/tsc/dts/lib.deno.ns.d.ts | 21 ------- ext/io/12_io.js | 33 ---------- runtime/js/99_main.js | 2 - tests/integration/js_unit_tests.rs | 1 - tests/specs/future/runtime_api/main.js | 1 - tests/specs/future/runtime_api/main.out | 1 - tests/unit/io_test.ts | 80 ------------------------- 7 files changed, 139 deletions(-) delete mode 100644 tests/unit/io_test.ts diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index dd245b613d..893dd7a5e5 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -1833,27 +1833,6 @@ declare namespace Deno { seekSync(offset: number | bigint, whence: SeekMode): number; } - /** - * Copies from `src` to `dst` until either EOF (`null`) is read from `src` or - * an error occurs. It resolves to the number of bytes copied or rejects with - * the first error encountered while copying. - * - * @deprecated This will be removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @category I/O - * - * @param src The source to copy from - * @param dst The destination to copy to - * @param options Can be used to tune size of the buffer. Default size is 32kB - */ - export function copy( - src: Reader, - dst: Writer, - options?: { bufSize?: number }, - ): Promise; - /** Open a file and resolve to an instance of {@linkcode Deno.FsFile}. The * file does not need to previously exist if using the `create` or `createNew` * open options. The caller may have the resulting file automatically closed diff --git a/ext/io/12_io.js b/ext/io/12_io.js index 6e95a9bca5..1caf6fe06f 100644 --- a/ext/io/12_io.js +++ b/ext/io/12_io.js @@ -20,7 +20,6 @@ import { writableStreamForRid, } from "ext:deno_web/06_streams.js"; -const DEFAULT_BUFFER_SIZE = 32 * 1024; // Seek whence values. // https://golang.org/pkg/io/#pkg-constants const SeekMode = { @@ -33,37 +32,6 @@ const SeekMode = { End: 2, }; -async function copy( - src, - dst, - options, -) { - internals.warnOnDeprecatedApi( - "Deno.copy()", - new Error().stack, - "Use `copy()` from `https://jsr.io/@std/io/doc/copy/~` instead.", - ); - let n = 0; - const bufSize = options?.bufSize ?? DEFAULT_BUFFER_SIZE; - const b = new Uint8Array(bufSize); - let gotEOF = false; - while (gotEOF === false) { - const result = await src.read(b); - if (result === null) { - gotEOF = true; - } else { - let nwritten = 0; - while (nwritten < result) { - nwritten += await dst.write( - TypedArrayPrototypeSubarray(b, nwritten, result), - ); - } - n += nwritten; - } - } - return n; -} - function readSync(rid, buffer) { if (buffer.length === 0) return 0; const nread = core.readSync(rid, buffer); @@ -295,7 +263,6 @@ const stdout = new Stdout(); const stderr = new Stderr(); export { - copy, read, readAll, readAllSync, diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index 1854a73fad..1746128513 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -801,7 +801,6 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) { if (future) { delete globalThis.window; delete Deno.Buffer; - delete Deno.copy; delete Deno.File; delete Deno.fstat; delete Deno.fstatSync; @@ -979,7 +978,6 @@ function bootstrapWorkerRuntime( if (future) { delete Deno.Buffer; - delete Deno.copy; delete Deno.File; delete Deno.fstat; delete Deno.fstatSync; diff --git a/tests/integration/js_unit_tests.rs b/tests/integration/js_unit_tests.rs index 9f1ee7394a..0baa114b44 100644 --- a/tests/integration/js_unit_tests.rs +++ b/tests/integration/js_unit_tests.rs @@ -47,7 +47,6 @@ util::unit_test_factory!( image_data_test, internals_test, intl_test, - io_test, jupyter_test, kv_test, kv_queue_test_no_db_close, diff --git a/tests/specs/future/runtime_api/main.js b/tests/specs/future/runtime_api/main.js index a12b2f1fcf..ab53a809bc 100644 --- a/tests/specs/future/runtime_api/main.js +++ b/tests/specs/future/runtime_api/main.js @@ -1,6 +1,5 @@ console.log("window is", globalThis.window); console.log("Deno.Buffer is", Deno.Buffer); -console.log("Deno.copy is", Deno.copy); console.log("Deno.File is", Deno.File); console.log("Deno.fstat is", Deno.fstat); console.log("Deno.fstatSync is", Deno.fstatSync); diff --git a/tests/specs/future/runtime_api/main.out b/tests/specs/future/runtime_api/main.out index 398922749a..08b62ea3a9 100644 --- a/tests/specs/future/runtime_api/main.out +++ b/tests/specs/future/runtime_api/main.out @@ -1,6 +1,5 @@ window is undefined Deno.Buffer is undefined -Deno.copy is undefined Deno.File is undefined Deno.fstat is undefined Deno.fstatSync is undefined diff --git a/tests/unit/io_test.ts b/tests/unit/io_test.ts deleted file mode 100644 index 44a04698c9..0000000000 --- a/tests/unit/io_test.ts +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import { assertEquals, DENO_FUTURE } from "./test_util.ts"; -import { Buffer } from "@std/io/buffer"; - -const DEFAULT_BUF_SIZE = 32 * 1024; - -type Spy = { calls: number }; - -function repeat(c: string, bytes: number): Uint8Array { - assertEquals(c.length, 1); - const ui8 = new Uint8Array(bytes); - ui8.fill(c.charCodeAt(0)); - return ui8; -} - -function spyRead(obj: Buffer): Spy { - const spy: Spy = { - calls: 0, - }; - - const orig = obj.read.bind(obj); - - obj.read = (p: Uint8Array): Promise => { - spy.calls++; - return orig(p); - }; - - return spy; -} - -Deno.test({ ignore: DENO_FUTURE }, async function copyWithDefaultBufferSize() { - const xBytes = repeat("b", DEFAULT_BUF_SIZE); - const reader = new Buffer(xBytes.buffer as ArrayBuffer); - const write = new Buffer(); - - const readSpy = spyRead(reader); - - // deno-lint-ignore no-deprecated-deno-api - const n = await Deno.copy(reader, write); - - assertEquals(n, xBytes.length); - assertEquals(write.length, xBytes.length); - assertEquals(readSpy.calls, 2); // read with DEFAULT_BUF_SIZE bytes + read with 0 bytes -}); - -Deno.test({ ignore: DENO_FUTURE }, async function copyWithCustomBufferSize() { - const bufSize = 1024; - const xBytes = repeat("b", DEFAULT_BUF_SIZE); - const reader = new Buffer(xBytes.buffer as ArrayBuffer); - const write = new Buffer(); - - const readSpy = spyRead(reader); - - // deno-lint-ignore no-deprecated-deno-api - const n = await Deno.copy(reader, write, { bufSize }); - - assertEquals(n, xBytes.length); - assertEquals(write.length, xBytes.length); - assertEquals(readSpy.calls, DEFAULT_BUF_SIZE / bufSize + 1); -}); - -Deno.test( - { ignore: DENO_FUTURE, permissions: { write: true } }, - async function copyBufferToFile() { - const filePath = "test-file.txt"; - // bigger than max File possible buffer 16kb - const bufSize = 32 * 1024; - const xBytes = repeat("b", bufSize); - const reader = new Buffer(xBytes.buffer as ArrayBuffer); - const write = await Deno.open(filePath, { write: true, create: true }); - - // deno-lint-ignore no-deprecated-deno-api - const n = await Deno.copy(reader, write, { bufSize }); - - assertEquals(n, xBytes.length); - - write.close(); - await Deno.remove(filePath); - }, -); From 7c594023256ffdc8a16db017676319da448dd926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 3 Sep 2024 14:18:51 +0100 Subject: [PATCH 03/45] test: fix info::info_import_map test (#25389) esm.sh provided a fix for `window` not existing in Deno anymore. --- tests/testdata/info/with_import_map/deno.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/testdata/info/with_import_map/deno.lock b/tests/testdata/info/with_import_map/deno.lock index 540a44063a..cb5c6ca45d 100644 --- a/tests/testdata/info/with_import_map/deno.lock +++ b/tests/testdata/info/with_import_map/deno.lock @@ -1,10 +1,10 @@ { "version": "3", "remote": { - "https://esm.sh/preact@10.15.1": "2b79349676a4942fbcf835c4efa909791c2f0aeca195225bf22bac9866e94b4e", - "https://esm.sh/preact@10.15.1/debug": "eb12af10d41f793ab3a8cf90bff89a9cd8efab57b541d43dada6efc5e3fa8e3c", + "https://esm.sh/preact@10.15.1": "4bfd0b2c5a2d432e0c8cda295d6b7304152ae08c85f7d0a22f91289c97085b89", + "https://esm.sh/preact@10.15.1/debug": "4bfd0b2c5a2d432e0c8cda295d6b7304152ae08c85f7d0a22f91289c97085b89", "https://esm.sh/stable/preact@10.15.1/denonext/debug.js": "e8e5e198bd48c93d484c91c4c78af1900bd81d9bfcfd543e8ac75216f5404c10", - "https://esm.sh/stable/preact@10.15.1/denonext/devtools.js": "7e3009ee2208a6cc8bbf747b61e9468d177ef55d94cf9b774ad2a6c926ae51cb", + "https://esm.sh/stable/preact@10.15.1/denonext/devtools.js": "f61430e179a84483f8ea8dc098d7d0d46b2f0546de4027518bfcef197cd665c9", "https://esm.sh/stable/preact@10.15.1/denonext/preact.mjs": "30710ac1d5ff3711ae0c04eddbeb706f34f82d97489f61aaf09897bc75d2a628" } } From b8ed6f822174b2322fbe9f8b58698b76b5505b4a Mon Sep 17 00:00:00 2001 From: David Sherret Date: Tue, 3 Sep 2024 15:56:39 +0200 Subject: [PATCH 04/45] fix(task): correct name for scoped npm package binaries (#25390) This is for when not using a node_modules directory or when using auto install for a node_modules dir. --- cli/tools/task.rs | 41 +++++++++---------- ext/node_resolver/resolution.rs | 1 + .../bin_pkg_with_scope_auto/__test__.jsonc | 5 +++ .../task/bin_pkg_with_scope_auto/bin_auto.out | 14 +++++++ .../task/bin_pkg_with_scope_auto/bin_none.out | 12 ++++++ .../task/bin_pkg_with_scope_auto/deno.json | 3 ++ .../task/bin_pkg_with_scope_auto/package.json | 9 ++++ .../bin_pkg_with_scope_none/__test__.jsonc | 5 +++ .../task/bin_pkg_with_scope_none/bin_none.out | 12 ++++++ .../task/bin_pkg_with_scope_none/deno.json | 3 ++ .../task/bin_pkg_with_scope_none/package.json | 9 ++++ 11 files changed, 93 insertions(+), 21 deletions(-) create mode 100644 tests/specs/task/bin_pkg_with_scope_auto/__test__.jsonc create mode 100644 tests/specs/task/bin_pkg_with_scope_auto/bin_auto.out create mode 100644 tests/specs/task/bin_pkg_with_scope_auto/bin_none.out create mode 100644 tests/specs/task/bin_pkg_with_scope_auto/deno.json create mode 100644 tests/specs/task/bin_pkg_with_scope_auto/package.json create mode 100644 tests/specs/task/bin_pkg_with_scope_none/__test__.jsonc create mode 100644 tests/specs/task/bin_pkg_with_scope_none/bin_none.out create mode 100644 tests/specs/task/bin_pkg_with_scope_none/deno.json create mode 100644 tests/specs/task/bin_pkg_with_scope_none/package.json diff --git a/cli/tools/task.rs b/cli/tools/task.rs index 23da5b4fb9..cc16bb9a3f 100644 --- a/cli/tools/task.rs +++ b/cli/tools/task.rs @@ -1,13 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::args::CliOptions; -use crate::args::Flags; -use crate::args::TaskFlags; -use crate::colors; -use crate::factory::CliFactory; -use crate::npm::CliNpmResolver; -use crate::task_runner; -use crate::util::fs::canonicalize_path; +use std::borrow::Cow; +use std::collections::HashMap; +use std::collections::HashSet; +use std::path::Path; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Arc; + use deno_config::deno_json::Task; use deno_config::workspace::TaskOrScript; use deno_config::workspace::WorkspaceDirectory; @@ -18,13 +18,15 @@ use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::normalize_path; use deno_task_shell::ShellCommand; -use std::borrow::Cow; -use std::collections::HashMap; -use std::collections::HashSet; -use std::path::Path; -use std::path::PathBuf; -use std::rc::Rc; -use std::sync::Arc; + +use crate::args::CliOptions; +use crate::args::Flags; +use crate::args::TaskFlags; +use crate::colors; +use crate::factory::CliFactory; +use crate::npm::CliNpmResolver; +use crate::task_runner; +use crate::util::fs::canonicalize_path; pub async fn execute_script( flags: Arc, @@ -106,12 +108,9 @@ See https://docs.deno.com/go/config"# .await } TaskOrScript::Script(scripts, _script) => { - // ensure the npm packages are installed if using a node_modules - // directory and managed resolver - if cli_options.has_node_modules_dir() { - if let Some(npm_resolver) = npm_resolver.as_managed() { - npm_resolver.ensure_top_level_package_json_install().await?; - } + // ensure the npm packages are installed if using a managed resolver + if let Some(npm_resolver) = npm_resolver.as_managed() { + npm_resolver.ensure_top_level_package_json_install().await?; } let cwd = match task_flags.cwd { diff --git a/ext/node_resolver/resolution.rs b/ext/node_resolver/resolution.rs index ae791e312b..ad9dbb7100 100644 --- a/ext/node_resolver/resolution.rs +++ b/ext/node_resolver/resolution.rs @@ -367,6 +367,7 @@ impl NodeResolver { pkg_json_path, }); }; + let name = name.split("/").last().unwrap(); vec![name.to_string()] } Some(Value::Object(o)) => { diff --git a/tests/specs/task/bin_pkg_with_scope_auto/__test__.jsonc b/tests/specs/task/bin_pkg_with_scope_auto/__test__.jsonc new file mode 100644 index 0000000000..2e126f6b16 --- /dev/null +++ b/tests/specs/task/bin_pkg_with_scope_auto/__test__.jsonc @@ -0,0 +1,5 @@ +{ + "tempDir": true, + "args": "task bin extra", + "output": "bin_auto.out" +} diff --git a/tests/specs/task/bin_pkg_with_scope_auto/bin_auto.out b/tests/specs/task/bin_pkg_with_scope_auto/bin_auto.out new file mode 100644 index 0000000000..20865898e3 --- /dev/null +++ b/tests/specs/task/bin_pkg_with_scope_auto/bin_auto.out @@ -0,0 +1,14 @@ +Download http://localhost:4260/@denotest/bin +[UNORDERED_START] +Download http://localhost:4260/@denotest/bin/0.5.0.tgz +Initialize @denotest/bin@0.5.0 +Download http://localhost:4260/@denotest/bin/1.0.0.tgz +Initialize @denotest/bin@1.0.0 +[UNORDERED_END] +Task bin bin hi && cli-esm testing this out && npx cli-cjs test "extra" +hi +testing +this +out +test +extra diff --git a/tests/specs/task/bin_pkg_with_scope_auto/bin_none.out b/tests/specs/task/bin_pkg_with_scope_auto/bin_none.out new file mode 100644 index 0000000000..066c67bd8a --- /dev/null +++ b/tests/specs/task/bin_pkg_with_scope_auto/bin_none.out @@ -0,0 +1,12 @@ +Download http://localhost:4260/@denotest/bin +[UNORDERED_START] +Download http://localhost:4260/@denotest/bin/0.5.0.tgz +Download http://localhost:4260/@denotest/bin/1.0.0.tgz +[UNORDERED_END] +Task bin bin hi && cli-esm testing this out && npx cli-cjs test "extra" +hi +testing +this +out +test +extra diff --git a/tests/specs/task/bin_pkg_with_scope_auto/deno.json b/tests/specs/task/bin_pkg_with_scope_auto/deno.json new file mode 100644 index 0000000000..fbd70ec480 --- /dev/null +++ b/tests/specs/task/bin_pkg_with_scope_auto/deno.json @@ -0,0 +1,3 @@ +{ + "nodeModulesDir": "auto" +} diff --git a/tests/specs/task/bin_pkg_with_scope_auto/package.json b/tests/specs/task/bin_pkg_with_scope_auto/package.json new file mode 100644 index 0000000000..2ee4c2bbb1 --- /dev/null +++ b/tests/specs/task/bin_pkg_with_scope_auto/package.json @@ -0,0 +1,9 @@ +{ + "scripts": { + "bin": "bin hi && cli-esm testing this out && npx cli-cjs test" + }, + "dependencies": { + "@denotest/bin": "0.5", + "other": "npm:@denotest/bin@1.0" + } +} diff --git a/tests/specs/task/bin_pkg_with_scope_none/__test__.jsonc b/tests/specs/task/bin_pkg_with_scope_none/__test__.jsonc new file mode 100644 index 0000000000..9f88b275fd --- /dev/null +++ b/tests/specs/task/bin_pkg_with_scope_none/__test__.jsonc @@ -0,0 +1,5 @@ +{ + "tempDir": true, + "args": "task bin extra", + "output": "bin_none.out" +} diff --git a/tests/specs/task/bin_pkg_with_scope_none/bin_none.out b/tests/specs/task/bin_pkg_with_scope_none/bin_none.out new file mode 100644 index 0000000000..066c67bd8a --- /dev/null +++ b/tests/specs/task/bin_pkg_with_scope_none/bin_none.out @@ -0,0 +1,12 @@ +Download http://localhost:4260/@denotest/bin +[UNORDERED_START] +Download http://localhost:4260/@denotest/bin/0.5.0.tgz +Download http://localhost:4260/@denotest/bin/1.0.0.tgz +[UNORDERED_END] +Task bin bin hi && cli-esm testing this out && npx cli-cjs test "extra" +hi +testing +this +out +test +extra diff --git a/tests/specs/task/bin_pkg_with_scope_none/deno.json b/tests/specs/task/bin_pkg_with_scope_none/deno.json new file mode 100644 index 0000000000..38af4024b0 --- /dev/null +++ b/tests/specs/task/bin_pkg_with_scope_none/deno.json @@ -0,0 +1,3 @@ +{ + "nodeModulesDir": "none" +} diff --git a/tests/specs/task/bin_pkg_with_scope_none/package.json b/tests/specs/task/bin_pkg_with_scope_none/package.json new file mode 100644 index 0000000000..2ee4c2bbb1 --- /dev/null +++ b/tests/specs/task/bin_pkg_with_scope_none/package.json @@ -0,0 +1,9 @@ +{ + "scripts": { + "bin": "bin hi && cli-esm testing this out && npx cli-cjs test" + }, + "dependencies": { + "@denotest/bin": "0.5", + "other": "npm:@denotest/bin@1.0" + } +} From 1d04c84c8f36a88697c82cf98587a5728952314f Mon Sep 17 00:00:00 2001 From: HasanAlrimawi <141642411+HasanAlrimawi@users.noreply.github.com> Date: Tue, 3 Sep 2024 18:14:19 +0300 Subject: [PATCH 05/45] chore: deprecate eval itests (#25382) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR serves as a part of #22907 . --------- Signed-off-by: David Sherret Co-authored-by: Bartek Iwańczuk Co-authored-by: David Sherret --- tests/integration/eval_tests.rs | 63 ------------------- tests/specs/eval/check_local/__test__.jsonc | 4 ++ .../eval/check_local/main.out} | 0 .../check_local_by_default2/__test__.jsonc | 4 ++ .../check_local_by_default2.ts | 0 .../eval/check_local_by_default2/main.out} | 0 .../specs/eval/dyn_import_eval/__test__.jsonc | 4 ++ .../eval/dyn_import_eval/main.out} | 0 tests/specs/eval/dyn_import_eval/mod4.js | 1 + tests/specs/eval/env_file/__test__.jsonc | 4 ++ tests/specs/eval/env_file/env_file | 4 ++ tests/specs/eval/env_file/main.out | 1 + .../eval/env_file_missing/__test__.jsonc | 4 ++ .../eval/env_file_missing/main.out} | 0 .../eval/env_unparsable_file/__test__.jsonc | 2 +- .../eval/env_unparsable_file/env_unparsable | 4 ++ tests/specs/eval/env_unparsable_file/main.out | 2 +- tests/specs/eval/eval_basic/__test__.jsonc | 4 ++ tests/specs/eval/eval_basic/main.out | 1 + tests/specs/eval/eval_p/__test__.jsonc | 4 ++ tests/specs/eval/eval_p/main.out | 1 + tests/specs/eval/eval_ts/__test__.jsonc | 4 ++ tests/specs/eval/eval_ts/main.out | 1 + tests/specs/eval/v8_flags_eval/__test__.jsonc | 4 ++ tests/specs/eval/v8_flags_eval/main.out | 1 + tools/lint.js | 2 +- 26 files changed, 53 insertions(+), 66 deletions(-) create mode 100644 tests/specs/eval/check_local/__test__.jsonc rename tests/{testdata/eval/check_local_by_default.out => specs/eval/check_local/main.out} (100%) create mode 100644 tests/specs/eval/check_local_by_default2/__test__.jsonc rename tests/{testdata/eval => specs/eval/check_local_by_default2}/check_local_by_default2.ts (100%) rename tests/{testdata/eval/check_local_by_default2.out => specs/eval/check_local_by_default2/main.out} (100%) create mode 100644 tests/specs/eval/dyn_import_eval/__test__.jsonc rename tests/{testdata/eval/dyn_import_eval.out => specs/eval/dyn_import_eval/main.out} (100%) create mode 100644 tests/specs/eval/dyn_import_eval/mod4.js create mode 100644 tests/specs/eval/env_file/__test__.jsonc create mode 100644 tests/specs/eval/env_file/env_file create mode 100644 tests/specs/eval/env_file/main.out create mode 100644 tests/specs/eval/env_file_missing/__test__.jsonc rename tests/{testdata/eval/env_file_missing.out => specs/eval/env_file_missing/main.out} (100%) create mode 100644 tests/specs/eval/env_unparsable_file/env_unparsable create mode 100644 tests/specs/eval/eval_basic/__test__.jsonc create mode 100644 tests/specs/eval/eval_basic/main.out create mode 100644 tests/specs/eval/eval_p/__test__.jsonc create mode 100644 tests/specs/eval/eval_p/main.out create mode 100644 tests/specs/eval/eval_ts/__test__.jsonc create mode 100644 tests/specs/eval/eval_ts/main.out create mode 100644 tests/specs/eval/v8_flags_eval/__test__.jsonc create mode 100644 tests/specs/eval/v8_flags_eval/main.out diff --git a/tests/integration/eval_tests.rs b/tests/integration/eval_tests.rs index 3f4c6a3a6d..198be3a4e8 100644 --- a/tests/integration/eval_tests.rs +++ b/tests/integration/eval_tests.rs @@ -1,24 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use test_util as util; -use test_util::itest; - -#[test] -fn eval_p() { - let output = util::deno_cmd() - .arg("eval") - .arg("-p") - .arg("1+2") - .stdout_piped() - .spawn() - .unwrap() - .wait_with_output() - .unwrap(); - assert!(output.status.success()); - let stdout_str = - util::strip_ansi_codes(std::str::from_utf8(&output.stdout).unwrap().trim()); - assert_eq!("3", stdout_str); -} // Make sure that snapshot flags don't affect runtime. #[test] @@ -43,48 +25,3 @@ fn eval_randomness() { numbers.dedup(); assert!(numbers.len() > 1); } - -itest!(eval_basic { - args: "eval console.log(\"hello\")", - output_str: Some("hello\n"), -}); - -// Ugly parentheses due to whitespace delimiting problem. -itest!(eval_ts { - args: "eval --quiet --ext=ts console.log((123)as(number))", // 'as' is a TS keyword only - output_str: Some("123\n"), -}); - -itest!(dyn_import_eval { - args: "eval import('./subdir/mod4.js').then(console.log)", - output: "eval/dyn_import_eval.out", -}); - -// Cannot write the expression to evaluate as "console.log(typeof gc)" -// because itest! splits args on whitespace. -itest!(v8_flags_eval { - args: "eval --v8-flags=--expose-gc console.log(typeof(gc))", - output: "run/v8_flags.js.out", -}); - -itest!(check_local_by_default { - args: "eval --quiet import('http://localhost:4545/subdir/type_error.ts').then(console.log);", - output: "eval/check_local_by_default.out", - http_server: true, -}); - -itest!(check_local_by_default2 { - args: "eval --quiet import('./eval/check_local_by_default2.ts').then(console.log);", - output: "eval/check_local_by_default2.out", - http_server: true, -}); - -itest!(env_file { - args: "eval --env=env console.log(Deno.env.get(\"ANOTHER_FOO\"))", - output_str: Some("ANOTHER_BAR\n"), -}); - -itest!(env_file_missing { - args: "eval --env=missing console.log(Deno.env.get(\"ANOTHER_FOO\"))", - output: "eval/env_file_missing.out", -}); diff --git a/tests/specs/eval/check_local/__test__.jsonc b/tests/specs/eval/check_local/__test__.jsonc new file mode 100644 index 0000000000..e9253803c0 --- /dev/null +++ b/tests/specs/eval/check_local/__test__.jsonc @@ -0,0 +1,4 @@ +{ + "args": "eval --quiet import('http://localhost:4545/subdir/type_error.ts').then(console.log);", + "output": "main.out" +} diff --git a/tests/testdata/eval/check_local_by_default.out b/tests/specs/eval/check_local/main.out similarity index 100% rename from tests/testdata/eval/check_local_by_default.out rename to tests/specs/eval/check_local/main.out diff --git a/tests/specs/eval/check_local_by_default2/__test__.jsonc b/tests/specs/eval/check_local_by_default2/__test__.jsonc new file mode 100644 index 0000000000..b6188b8cee --- /dev/null +++ b/tests/specs/eval/check_local_by_default2/__test__.jsonc @@ -0,0 +1,4 @@ +{ + "args": "eval --quiet import('./check_local_by_default2.ts').then(console.log);", + "output": "main.out" +} diff --git a/tests/testdata/eval/check_local_by_default2.ts b/tests/specs/eval/check_local_by_default2/check_local_by_default2.ts similarity index 100% rename from tests/testdata/eval/check_local_by_default2.ts rename to tests/specs/eval/check_local_by_default2/check_local_by_default2.ts diff --git a/tests/testdata/eval/check_local_by_default2.out b/tests/specs/eval/check_local_by_default2/main.out similarity index 100% rename from tests/testdata/eval/check_local_by_default2.out rename to tests/specs/eval/check_local_by_default2/main.out diff --git a/tests/specs/eval/dyn_import_eval/__test__.jsonc b/tests/specs/eval/dyn_import_eval/__test__.jsonc new file mode 100644 index 0000000000..34e5734936 --- /dev/null +++ b/tests/specs/eval/dyn_import_eval/__test__.jsonc @@ -0,0 +1,4 @@ +{ + "args": "eval import('./mod4.js').then(console.log)", + "output": "main.out" +} diff --git a/tests/testdata/eval/dyn_import_eval.out b/tests/specs/eval/dyn_import_eval/main.out similarity index 100% rename from tests/testdata/eval/dyn_import_eval.out rename to tests/specs/eval/dyn_import_eval/main.out diff --git a/tests/specs/eval/dyn_import_eval/mod4.js b/tests/specs/eval/dyn_import_eval/mod4.js new file mode 100644 index 0000000000..71332dbc42 --- /dev/null +++ b/tests/specs/eval/dyn_import_eval/mod4.js @@ -0,0 +1 @@ +export const isMod4 = true; diff --git a/tests/specs/eval/env_file/__test__.jsonc b/tests/specs/eval/env_file/__test__.jsonc new file mode 100644 index 0000000000..b2e8f87b8a --- /dev/null +++ b/tests/specs/eval/env_file/__test__.jsonc @@ -0,0 +1,4 @@ +{ + "args": "eval --env-file=env_file console.log(Deno.env.get(\"ANOTHER_FOO\"));", + "output": "main.out" +} diff --git a/tests/specs/eval/env_file/env_file b/tests/specs/eval/env_file/env_file new file mode 100644 index 0000000000..c41732d30b --- /dev/null +++ b/tests/specs/eval/env_file/env_file @@ -0,0 +1,4 @@ +FOO=BAR +ANOTHER_FOO=ANOTHER_${FOO} +MULTILINE="First Line +Second Line" \ No newline at end of file diff --git a/tests/specs/eval/env_file/main.out b/tests/specs/eval/env_file/main.out new file mode 100644 index 0000000000..01fcfbd290 --- /dev/null +++ b/tests/specs/eval/env_file/main.out @@ -0,0 +1 @@ +ANOTHER_BAR diff --git a/tests/specs/eval/env_file_missing/__test__.jsonc b/tests/specs/eval/env_file_missing/__test__.jsonc new file mode 100644 index 0000000000..fc4e463807 --- /dev/null +++ b/tests/specs/eval/env_file_missing/__test__.jsonc @@ -0,0 +1,4 @@ +{ + "args": "eval --env-file=missing console.log(Deno.env.get(\"ANOTHER_FOO\"));", + "output": "main.out" +} diff --git a/tests/testdata/eval/env_file_missing.out b/tests/specs/eval/env_file_missing/main.out similarity index 100% rename from tests/testdata/eval/env_file_missing.out rename to tests/specs/eval/env_file_missing/main.out diff --git a/tests/specs/eval/env_unparsable_file/__test__.jsonc b/tests/specs/eval/env_unparsable_file/__test__.jsonc index cf5e9a99b2..8c8dbf5a94 100644 --- a/tests/specs/eval/env_unparsable_file/__test__.jsonc +++ b/tests/specs/eval/env_unparsable_file/__test__.jsonc @@ -1,4 +1,4 @@ { - "args": "eval --env=../../../testdata/env_unparsable console.log(Deno.env.get(\"Another_FOO\"))", + "args": "eval --env-file=env_unparsable console.log(Deno.env.get(\"ANOTHER_FOO\"));", "output": "main.out" } diff --git a/tests/specs/eval/env_unparsable_file/env_unparsable b/tests/specs/eval/env_unparsable_file/env_unparsable new file mode 100644 index 0000000000..5542b80bc3 --- /dev/null +++ b/tests/specs/eval/env_unparsable_file/env_unparsable @@ -0,0 +1,4 @@ +FOO=valid +ANOTHER_FOO=c:\path +MULTILINE="First Line +Second Line" \ No newline at end of file diff --git a/tests/specs/eval/env_unparsable_file/main.out b/tests/specs/eval/env_unparsable_file/main.out index 18d7856b45..1b3f7047a7 100644 --- a/tests/specs/eval/env_unparsable_file/main.out +++ b/tests/specs/eval/env_unparsable_file/main.out @@ -1,2 +1,2 @@ -Warning Parsing failed within the specified environment file: ../../../testdata/env_unparsable at index: 3 of the value: c:\path +Warning Parsing failed within the specified environment file: env_unparsable at index: 3 of the value: c:\path undefined diff --git a/tests/specs/eval/eval_basic/__test__.jsonc b/tests/specs/eval/eval_basic/__test__.jsonc new file mode 100644 index 0000000000..ebc9732361 --- /dev/null +++ b/tests/specs/eval/eval_basic/__test__.jsonc @@ -0,0 +1,4 @@ +{ + "args": "eval console.log(\"hello\")", + "output": "main.out" +} diff --git a/tests/specs/eval/eval_basic/main.out b/tests/specs/eval/eval_basic/main.out new file mode 100644 index 0000000000..ce01362503 --- /dev/null +++ b/tests/specs/eval/eval_basic/main.out @@ -0,0 +1 @@ +hello diff --git a/tests/specs/eval/eval_p/__test__.jsonc b/tests/specs/eval/eval_p/__test__.jsonc new file mode 100644 index 0000000000..bd34d67e3c --- /dev/null +++ b/tests/specs/eval/eval_p/__test__.jsonc @@ -0,0 +1,4 @@ +{ + "args": "eval -p 1+2", + "output": "main.out" +} diff --git a/tests/specs/eval/eval_p/main.out b/tests/specs/eval/eval_p/main.out new file mode 100644 index 0000000000..00750edc07 --- /dev/null +++ b/tests/specs/eval/eval_p/main.out @@ -0,0 +1 @@ +3 diff --git a/tests/specs/eval/eval_ts/__test__.jsonc b/tests/specs/eval/eval_ts/__test__.jsonc new file mode 100644 index 0000000000..4e8b9c2ae2 --- /dev/null +++ b/tests/specs/eval/eval_ts/__test__.jsonc @@ -0,0 +1,4 @@ +{ + "args": "eval --quiet --ext=ts console.log((123)as(number))", + "output": "main.out" +} diff --git a/tests/specs/eval/eval_ts/main.out b/tests/specs/eval/eval_ts/main.out new file mode 100644 index 0000000000..190a18037c --- /dev/null +++ b/tests/specs/eval/eval_ts/main.out @@ -0,0 +1 @@ +123 diff --git a/tests/specs/eval/v8_flags_eval/__test__.jsonc b/tests/specs/eval/v8_flags_eval/__test__.jsonc new file mode 100644 index 0000000000..23b59d482d --- /dev/null +++ b/tests/specs/eval/v8_flags_eval/__test__.jsonc @@ -0,0 +1,4 @@ +{ + "args": "eval --v8-flags=--expose-gc console.log(typeof(gc))", + "output": "main.out" +} diff --git a/tests/specs/eval/v8_flags_eval/main.out b/tests/specs/eval/v8_flags_eval/main.out new file mode 100644 index 0000000000..e2dbde096f --- /dev/null +++ b/tests/specs/eval/v8_flags_eval/main.out @@ -0,0 +1 @@ +function diff --git a/tools/lint.js b/tools/lint.js index 6784ec6300..d40b1b1fd9 100755 --- a/tools/lint.js +++ b/tools/lint.js @@ -202,7 +202,7 @@ async function ensureNoNewITests() { "compile_tests.rs": 0, "coverage_tests.rs": 0, "doc_tests.rs": 15, - "eval_tests.rs": 9, + "eval_tests.rs": 0, "flags_tests.rs": 0, "fmt_tests.rs": 17, "info_tests.rs": 18, From 203428ea14d3630f537f0e05f15bb8d56474fe36 Mon Sep 17 00:00:00 2001 From: Caleb Cox Date: Tue, 3 Sep 2024 10:53:42 -0500 Subject: [PATCH 06/45] docs(serve): fix default `Deno.serve` hostname (#25392) Update the docs for `Deno.serve` to reflect that the default hostname is `0.0.0.0`, not `127.0.0.1`. --- cli/tsc/dts/lib.deno.ns.d.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index 893dd7a5e5..8489a8de87 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -6234,14 +6234,14 @@ declare namespace Deno { /** Serves HTTP requests with the given option bag and handler. * * You can specify an object with a port and hostname option, which is the - * address to listen on. The default is port `8000` on hostname `"127.0.0.1"`. + * address to listen on. The default is port `8000` on hostname `"0.0.0.0"`. * * You can change the address to listen on using the `hostname` and `port` - * options. The below example serves on port `3000` and hostname `"0.0.0.0"`. + * options. The below example serves on port `3000` and hostname `"127.0.0.1"`. * * ```ts * Deno.serve( - * { port: 3000, hostname: "0.0.0.0" }, + * { port: 3000, hostname: "127.0.0.1" }, * (_req) => new Response("Hello, world") * ); * ``` @@ -6323,14 +6323,14 @@ declare namespace Deno { /** Serves HTTP requests with the given option bag. * * You can specify an object with a port and hostname option, which is the - * address to listen on. The default is port `8000` on hostname `"127.0.0.1"`. + * address to listen on. The default is port `8000` on hostname `"0.0.0.0"`. * * ```ts * const ac = new AbortController(); * * const server = Deno.serve({ * port: 3000, - * hostname: "0.0.0.0", + * hostname: "127.0.0.1", * handler: (_req) => new Response("Hello, world"), * signal: ac.signal, * onListen({ port, hostname }) { From 81e941bc92aac37bbf2f385eeceec9e4c8cfb13d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 3 Sep 2024 16:55:29 +0100 Subject: [PATCH 07/45] fix(install): recommend using `deno install -g` when using a single http url (#25388) Closes https://github.com/denoland/deno/issues/25361 --- cli/args/flags.rs | 1 - cli/tools/installer.rs | 20 +++++++++++++++++++ .../__test__.jsonc | 14 +++++++++++++ .../install_http.out | 2 ++ .../install_https.out | 2 ++ 5 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/specs/install/install_single_http_url_without_global_flag/__test__.jsonc create mode 100644 tests/specs/install/install_single_http_url_without_global_flag/install_http.out create mode 100644 tests/specs/install/install_single_http_url_without_global_flag/install_https.out diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 44f97010f0..2467de88f3 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -237,7 +237,6 @@ pub struct InstallFlagsGlobal { #[derive(Clone, Debug, Eq, PartialEq)] pub enum InstallKind { - #[allow(unused)] Local(Option), Global(InstallFlagsGlobal), } diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index c3f415dc7f..6965d5c6d3 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -15,6 +15,7 @@ use crate::factory::CliFactory; use crate::http_util::HttpClientProvider; use crate::util::fs::canonicalize_path_maybe_not_exists; +use deno_core::anyhow::bail; use deno_core::anyhow::Context; use deno_core::error::generic_error; use deno_core::error::AnyError; @@ -284,6 +285,24 @@ async fn install_local( Ok(()) } +fn check_if_installs_a_single_package_globally( + maybe_add_flags: Option<&AddFlags>, +) -> Result<(), AnyError> { + let Some(add_flags) = maybe_add_flags else { + return Ok(()); + }; + if add_flags.packages.len() != 1 { + return Ok(()); + } + let Ok(url) = Url::parse(&add_flags.packages[0]) else { + return Ok(()); + }; + if matches!(url.scheme(), "http" | "https") { + bail!("Failed to install \"{}\" specifier. If you are trying to install {} globally, run again with `-g` flag:\n deno install -g {}", url.scheme(), url.as_str(), url.as_str()); + } + Ok(()) +} + pub async fn install_command( flags: Arc, install_flags: InstallFlags, @@ -297,6 +316,7 @@ pub async fn install_command( install_global(flags, global_flags).await } InstallKind::Local(maybe_add_flags) => { + check_if_installs_a_single_package_globally(maybe_add_flags.as_ref())?; install_local(flags, maybe_add_flags).await } } diff --git a/tests/specs/install/install_single_http_url_without_global_flag/__test__.jsonc b/tests/specs/install/install_single_http_url_without_global_flag/__test__.jsonc new file mode 100644 index 0000000000..b906a846d0 --- /dev/null +++ b/tests/specs/install/install_single_http_url_without_global_flag/__test__.jsonc @@ -0,0 +1,14 @@ +{ + "steps": [ + { + "args": "install http://example.com", + "output": "install_http.out", + "exitCode": 1 + }, + { + "args": "install https://example.com", + "output": "install_https.out", + "exitCode": 1 + } + ] +} diff --git a/tests/specs/install/install_single_http_url_without_global_flag/install_http.out b/tests/specs/install/install_single_http_url_without_global_flag/install_http.out new file mode 100644 index 0000000000..17f4b286d0 --- /dev/null +++ b/tests/specs/install/install_single_http_url_without_global_flag/install_http.out @@ -0,0 +1,2 @@ +error: Failed to install "http" specifier. If you are trying to install http://example.com/ globally, run again with `-g` flag: + deno install -g http://example.com/ diff --git a/tests/specs/install/install_single_http_url_without_global_flag/install_https.out b/tests/specs/install/install_single_http_url_without_global_flag/install_https.out new file mode 100644 index 0000000000..87597de128 --- /dev/null +++ b/tests/specs/install/install_single_http_url_without_global_flag/install_https.out @@ -0,0 +1,2 @@ +error: Failed to install "https" specifier. If you are trying to install https://example.com/ globally, run again with `-g` flag: + deno install -g https://example.com/ From 9a36b6fb04764f49cc617be0c5bc7cdbd7fd5956 Mon Sep 17 00:00:00 2001 From: Leo Kettmeir Date: Tue, 3 Sep 2024 12:40:50 -0700 Subject: [PATCH 08/45] fix(flags): require global flag for permission flags in install subcommand (#25391) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also rewrites some of the subcommands help text Closes https://github.com/denoland/deno/issues/25362 --------- Co-authored-by: Bartek Iwańczuk --- Cargo.lock | 16 +- cli/Cargo.toml | 4 +- cli/args/flags.rs | 973 ++++++++++-------- .../__test__.jsonc | 2 +- 4 files changed, 568 insertions(+), 427 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ea3ea416b6..195f55069c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -167,9 +167,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" [[package]] name = "anstyle-parse" @@ -693,18 +693,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.13" +version = "4.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" +checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.13" +version = "4.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" +checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" dependencies = [ "anstream", "anstyle", @@ -715,9 +715,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.12" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8670053e87c316345e384ca1f3eba3006fc6355ed8b8a1140d104e109e3df34" +checksum = "6d7db6eca8c205649e8d3ccd05aa5042b1800a784e56bc7c43524fde8abbfa9b" dependencies = [ "clap", ] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 9c62406ffe..a1d8f31dd0 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -91,8 +91,8 @@ bincode = "=1.3.3" bytes.workspace = true cache_control.workspace = true chrono = { workspace = true, features = ["now"] } -clap = { version = "=4.5.13", features = ["env", "string", "wrap_help"] } -clap_complete = "=4.5.12" +clap = { version = "=4.5.16", features = ["env", "string", "wrap_help", "error-context"] } +clap_complete = "=4.5.24" clap_complete_fig = "=4.5.2" color-print = "0.3.5" console_static_text.workspace = true diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 2467de88f3..257bf81785 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -1,7 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use crate::args::resolve_no_prompt; +use crate::util::fs::canonicalize_path; use clap::builder::styling::AnsiColor; use clap::builder::FalseyValueParser; +use clap::error::ErrorKind; use clap::value_parser; use clap::Arg; use clap::ArgAction; @@ -37,9 +40,6 @@ use std::path::Path; use std::path::PathBuf; use std::str::FromStr; -use crate::args::resolve_no_prompt; -use crate::util::fs::canonicalize_path; - use super::flags_net; #[derive(Clone, Debug, Default, Eq, PartialEq)] @@ -1118,6 +1118,7 @@ static DENO_HELP: &str = cstr!( deno bench bench.ts cache Cache the dependencies check Type-check the dependencies + clean Remove the cache directory compile Compile the script into a self contained executable deno compile main.ts | deno compile --target=x86_64-unknown-linux-gnu coverage Print coverage reports @@ -1145,7 +1146,32 @@ static DENO_HELP: &str = cstr!( /// Main entry point for parsing deno's command line flags. pub fn flags_from_vec(args: Vec) -> clap::error::Result { let mut app = clap_root(); - let mut matches = app.try_get_matches_from_mut(&args)?; + let mut matches = + app + .try_get_matches_from_mut(&args) + .map_err(|mut e| match e.kind() { + ErrorKind::MissingRequiredArgument => { + if let Some(clap::error::ContextValue::Strings(s)) = + e.get(clap::error::ContextKind::InvalidArg) + { + if s.len() == 1 + && s[0] == "--global" + && args.iter().any(|arg| arg == "install") + { + e.insert( + clap::error::ContextKind::Usage, + clap::error::ContextValue::StyledStr( + "Note: Permission flags can only be used in a global setting" + .into(), + ), + ); + } + } + + e + } + _ => e, + })?; let mut flags = Flags::default(); @@ -1463,7 +1489,7 @@ pub fn clap_root() -> Command { .subcommand(fmt_subcommand()) .subcommand(init_subcommand()) .subcommand(info_subcommand()) - .subcommand(future_install_subcommand()) + .subcommand(install_subcommand()) .subcommand(json_reference_subcommand()) .subcommand(jupyter_subcommand()) .subcommand(uninstall_subcommand()) @@ -1510,13 +1536,13 @@ fn help_subcommand(app: &Command) -> Command { fn add_subcommand() -> Command { command( "add", - "Add dependencies to the configuration file. - - deno add @std/path + cstr!( + "Add dependencies to your configuration file. + deno add @std/path You can add multiple dependencies at once: - deno add @std/path @std/assert -", + deno add @std/path @std/assert" + ), UnstableArgsConfig::None, ) .defer(|cmd| { @@ -1535,8 +1561,7 @@ fn remove_subcommand() -> Command { "remove", cstr!( "Remove dependencies from the configuration file. - - deno remove @std/path + deno remove @std/path You can remove multiple dependencies at once: deno remove @std/path @std/assert @@ -1558,15 +1583,15 @@ You can remove multiple dependencies at once: fn bench_subcommand() -> Command { command( "bench", - "Run benchmarks using Deno's built-in bench tool. + cstr!("Run benchmarks using Deno's built-in bench tool. -Evaluate the given modules, run all benches declared with 'Deno.bench()' -and report results to standard output: - deno bench src/fetch_bench.ts src/signal_bench.ts +Evaluate the given files, run all benches declared with 'Deno.bench()' and report results to standard output: + deno bench src/fetch_bench.ts src/signal_bench.ts -Directory arguments are expanded to all contained files matching the -glob {*_,*.,}bench.{js,mjs,ts,mts,jsx,tsx}: - deno bench src/", +If you specify a directory instead of a file, the path is expanded to all contained files matching the glob {*_,*.,}bench.{js,mjs,ts,mts,jsx,tsx}: + deno bench src/ + +Read more: https://docs.deno.com/go/cmd/bench"), UnstableArgsConfig::ResolutionAndRuntime, ) .defer(|cmd| { @@ -1624,14 +1649,14 @@ See the Deno 1.x to 2.x Migration Guide for migration instructions: https://docs fn cache_subcommand() -> Command { command( "cache", - "Cache and compile remote dependencies recursively. + cstr!("Cache and compile remote dependencies. -Download and compile a module with all of its static dependencies and save -them in the local cache, without running any code: - deno cache jsr:@std/http/file-server +Download and compile a module with all of its static dependencies and save them in the local cache, without running any code: + deno cache jsr:@std/http/file-server -Future runs of this module will trigger no downloads or compilation unless ---reload is specified.", +Future runs of this module will trigger no downloads or compilation unless --reload is specified + +Read more: https://docs.deno.com/go/cmd/cache"), UnstableArgsConfig::ResolutionOnly, ) .defer(|cmd| { @@ -1651,18 +1676,20 @@ Future runs of this module will trigger no downloads or compilation unless fn clean_subcommand() -> Command { command( "clean", - "Remove the cache directory ($DENO_DIR)", + cstr!("Remove the cache directory ($DENO_DIR)"), UnstableArgsConfig::None, ) } fn check_subcommand() -> Command { command("check", - "Download and type-check without execution. + cstr!("Download and type-check without execution. - deno check jsr:@std/http/file-server + deno check jsr:@std/http/file-server -Unless --reload is specified, this command will not re-download already cached dependencies.", +Unless --reload is specified, this command will not re-download already cached dependencies + +Read more: https://docs.deno.com/go/cmd/check"), UnstableArgsConfig::ResolutionAndRuntime ) .defer(|cmd| { @@ -1696,26 +1723,18 @@ Unless --reload is specified, this command will not re-download already cached d fn compile_subcommand() -> Command { command( "compile", - "Compiles the given script into a self contained executable. + cstr!("Compiles the given script into a self contained executable. - deno compile -A jsr:@std/http/file-server - deno compile --output file_server jsr:@std/http/file-server + deno compile -A jsr:@std/http/file-server + deno compile --output file_server jsr:@std/http/file-server -Any flags passed which affect runtime behavior, such as '--unstable', -'--allow-*', '--v8-flags', etc. are encoded into the output executable and -used at runtime as if they were passed to a similar 'deno run' command. +Any flags specified which affect runtime behavior will be applied to the resulting binary. -The executable name is inferred by default: Attempt to take the file stem of -the URL path. The above example would become 'file_server'. If the file stem -is something generic like 'main', 'mod', 'index' or 'cli', and the path has no -parent, take the file name of the parent path. Otherwise settle with the -generic name. If the resulting name has an '@...' suffix, strip it. +Cross-compiling to different target architectures is supported using the --target flag. +On the first invocation with deno will download the proper binary and cache it in $DENO_DIR. -Cross-compiling to different target architectures is supported using the -`--target` flag. On the first invocation with deno will download proper -binary and cache it in $DENO_DIR. The aarch64-apple-darwin target is not -supported in canary. -", +Read more: https://docs.deno.com/go/cmd/compile +"), UnstableArgsConfig::ResolutionAndRuntime, ) .defer(|cmd| { @@ -1783,10 +1802,12 @@ supported in canary. fn completions_subcommand() -> Command { command( "completions", - "Output shell completion script to standard output. + cstr!( + "Output shell completion script to standard output. - deno completions bash > /usr/local/etc/bash_completion.d/deno.bash - source /usr/local/etc/bash_completion.d/deno.bash", + deno completions bash > /usr/local/etc/bash_completion.d/deno.bash + source /usr/local/etc/bash_completion.d/deno.bash" + ), UnstableArgsConfig::None, ) .defer(|cmd| { @@ -1801,31 +1822,25 @@ fn completions_subcommand() -> Command { fn coverage_subcommand() -> Command { command( "coverage", - "Print coverage reports from coverage profiles. + cstr!("Print coverage reports from coverage profiles. Collect a coverage profile with deno test: - deno test --coverage=cov_profile + deno test --coverage=cov_profile Print a report to stdout: - deno coverage cov_profile + deno coverage cov_profile -Include urls that start with the file schema: - deno coverage --include=\"^file:\" cov_profile - -Exclude urls ending with test.ts and test.js: - deno coverage --exclude=\"test\\.(ts|js)\" cov_profile - -Include urls that start with the file schema and exclude files ending with -test.ts and test.js, for an url to match it must match the include pattern and -not match the exclude pattern: - deno coverage --include=\"^file:\" --exclude=\"test\\.(ts|js)\" cov_profile +Include urls that start with the file schema and exclude files ending with test.ts and test.js, +for an url to match it must match the include pattern and not match the exclude pattern: + deno coverage --include=\"^file:\" --exclude=\"test\\.(ts|js)\" cov_profile Write a report using the lcov format: - deno coverage --lcov --output=cov.lcov cov_profile/ + deno coverage --lcov --output=cov.lcov cov_profile/ Generate html reports from lcov: - genhtml -o html_cov cov.lcov -", + genhtml -o html_cov cov.lcov + +Read more: https://docs.deno.com/go/cmd/coverage"), UnstableArgsConfig::None, ) .defer(|cmd| { @@ -1900,31 +1915,25 @@ Generate html reports from lcov: fn doc_subcommand() -> Command { command("doc", - "Show documentation for a module. + cstr!("Show documentation for a module. Output documentation to standard output: - deno doc ./path/to/module.ts + deno doc ./path/to/module.ts Output documentation in HTML format: - deno doc --html --name=\"My library\" ./path/to/module.ts - deno doc --html --name=\"My library\" ./main.ts ./dev.ts - deno doc --html --name=\"My library\" --output=./documentation/ ./path/to/module.ts - -Output private documentation to standard output: - deno doc --private ./path/to/module.ts - -Output documentation in JSON format: - deno doc --json ./path/to/module.ts + deno doc --html --name=\"My library\" ./path/to/module.ts Lint a module for documentation diagnostics: - deno doc --lint ./path/to/module.ts + deno doc --lint ./path/to/module.ts Target a specific symbol: - deno doc ./path/to/module.ts MyClass.someField + deno doc ./path/to/module.ts MyClass.someField Show documentation for runtime built-ins: - deno doc - deno doc --filter Deno.Listener", + deno doc + deno doc --filter Deno.Listener + +Read more: https://docs.deno.com/go/cmd/doc"), UnstableArgsConfig::ResolutionOnly ) .defer(|cmd| { @@ -2035,14 +2044,17 @@ Show documentation for runtime built-ins: fn eval_subcommand() -> Command { command( "eval", - "Evaluate JavaScript from the command line. - - deno eval \"console.log('hello world')\" + cstr!( + "Evaluate JavaScript from the command line. + deno eval \"console.log('hello world')\" To evaluate as TypeScript: - deno eval --ext=ts \"const v: string = 'hello'; console.log(v)\" + deno eval --ext=ts \"const v: string = 'hello'; console.log(v)\" -This command has implicit access to all permissions (--allow-all).", +This command has implicit access to all permissions. + +Read more: https://docs.deno.com/go/cmd/eval" + ), UnstableArgsConfig::ResolutionAndRuntime, ) .defer(|cmd| { @@ -2071,20 +2083,25 @@ This command has implicit access to all permissions (--allow-all).", fn fmt_subcommand() -> Command { command( "fmt", - "Auto-format JavaScript, TypeScript, Markdown, and JSON files. + cstr!("Auto-format various file types. + deno fmt myfile1.ts myfile2.ts - deno fmt - deno fmt myfile1.ts myfile2.ts - deno fmt --check +Supported file types are: + JavaScript, TypeScript, Markdown, JSON(C) and Jupyter Notebooks + +Supported file types which are behind corresponding unstable flags (see formatting options): + HTML, CSS, SCSS, SASS, LESS, YAML, Svelte, Vue, Astro and Angular Format stdin and write to stdout: - cat file.ts | deno fmt - + cat file.ts | deno fmt - Ignore formatting code by preceding it with an ignore comment: - // deno-fmt-ignore + // deno-fmt-ignore Ignore formatting a file by adding an ignore comment at the top of the file: - // deno-fmt-ignore-file", + // deno-fmt-ignore-file + +Read more: https://docs.deno.com/go/cmd/fmt"), UnstableArgsConfig::None, ) .defer(|cmd| { @@ -2104,6 +2121,7 @@ Ignore formatting a file by adding an ignore comment at the top of the file: .help("Set content type of the supplied file") // prefer using ts for formatting instead of js because ts works in more scenarios .default_value("ts") + .hide_default_value(true) .value_parser([ "ts", "tsx", "js", "jsx", "md", "json", "jsonc", "css", "scss", "sass", "less", "html", "svelte", "vue", "astro", "yml", "yaml", @@ -2250,23 +2268,18 @@ fn init_subcommand() -> Command { fn info_subcommand() -> Command { command("info", - "Information about a module or the cache directories. + cstr!("Show information about a module or the cache directories. Get information about a module: - deno info jsr:@std/http/file-server + deno info jsr:@std/http/file-server The following information is shown: + local: Local path of the file + type: JavaScript, TypeScript, or JSON + emit: Local path of compiled source code (TypeScript only) + dependencies: Dependency tree of the source file -local: Local path of the file. -type: JavaScript, TypeScript, or JSON. -emit: Local path of compiled source code. (TypeScript only.) -dependencies: Dependency tree of the source file. - -Without any additional arguments, 'deno info' shows: - -DENO_DIR: Directory containing Deno-managed files. -Remote modules cache: Subdirectory containing downloaded remote modules. -TypeScript compiler cache: Subdirectory containing TS compiler output.", +Read more: https://docs.deno.com/go/cmd/info"), UnstableArgsConfig::ResolutionOnly ) .defer(|cmd| cmd @@ -2297,89 +2310,83 @@ TypeScript compiler cache: Subdirectory containing TS compiler output.", )) } -fn install_args(cmd: Command) -> Command { - let cmd = cmd.arg( - Arg::new("cmd") - .required_if_eq("global", "true") - .num_args(1..) - .value_hint(ValueHint::FilePath), - ); - cmd - .arg( - Arg::new("name") - .long("name") - .short('n') - .help("Executable file name"), - ) - .arg( - Arg::new("root") - .long("root") - .help("Installation root") - .value_hint(ValueHint::DirPath), - ) - .arg( - Arg::new("force") - .long("force") - .short('f') - .help("Forcefully overwrite existing installation") - .action(ArgAction::SetTrue), - ) - .arg( - Arg::new("global") - .long("global") - .short('g') - .help("Install a package or script as a globally available executable") - .action(ArgAction::SetTrue), - ) - .arg(env_file_arg()) -} +fn install_subcommand() -> Command { + command("install", cstr!("Installs dependencies either in the local project or globally to a bin directory. -fn future_install_subcommand() -> Command { - command("install", "Installs dependencies either in the local project or globally to a bin directory. +Local installation -Local installation -------------------- -If the --global flag is not set, adds dependencies to the local project's configuration -(package.json / deno.json) and installs them in the package cache. If no dependency -is specified, installs all dependencies listed in package.json. +Add dependencies to the local project's configuration (deno.json / package.json) and installs them +in the package cache. If no dependency is specified, installs all dependencies listed in the config file. - deno install - deno install @std/bytes - deno install npm:chalk + deno install + deno install @std/bytes + deno install npm:chalk -Global installation -------------------- -If the --global flag is set, installs a script as an executable in the installation root's bin directory. +Global installation - deno install --global --allow-net --allow-read jsr:@std/http/file-server - deno install -g https://examples.deno.land/color-logging.ts +If the --global flag is set, installs a script as an executable in the installation root's bin directory. + + deno install --global --allow-net --allow-read jsr:@std/http/file-server + deno install -g https://examples.deno.land/color-logging.ts To change the executable name, use -n/--name: - - deno install -g --allow-net --allow-read -n serve jsr:@std/http/file-server + deno install -g --allow-net --allow-read -n serve jsr:@std/http/file-server The executable name is inferred by default: - Attempt to take the file stem of the URL path. The above example would - become 'file_server'. - - If the file stem is something generic like 'main', 'mod', 'index' or 'cli', + become file_server. + - If the file stem is something generic like main, mod, index or cli, and the path has no parent, take the file name of the parent path. Otherwise settle with the generic name. - - If the resulting name has an '@...' suffix, strip it. + - If the resulting name has an @... suffix, strip it. -To change the installation root, use --root: - - deno install -g --allow-net --allow-read --root /usr/local jsr:@std/http/file-server +To change the installation root, use --root: + deno install -g --allow-net --allow-read --root /usr/local jsr:@std/http/file-server The installation root is determined, in order of precedence: - - --root option - - DENO_INSTALL_ROOT environment variable - - $HOME/.deno + - --root option + - DENO_INSTALL_ROOT environment variable + - $HOME/.deno -These must be added to the path manually if required.", UnstableArgsConfig::ResolutionAndRuntime) +These must be added to the path manually if required."), UnstableArgsConfig::ResolutionAndRuntime) .visible_alias("i") .defer(|cmd| { - let cmd = runtime_args(cmd, true, true).arg(check_arg(true)).arg(allow_scripts_arg()); - install_args(cmd) + permission_args(runtime_args(cmd, false, true), Some("global")) + .arg(check_arg(true)) + .arg(allow_scripts_arg()) + .arg( + Arg::new("cmd") + .required_if_eq("global", "true") + .num_args(1..) + .value_hint(ValueHint::FilePath), + ) + .arg( + Arg::new("name") + .long("name") + .short('n') + .help("Executable file name"), + ) + .arg( + Arg::new("root") + .long("root") + .help("Installation root") + .value_hint(ValueHint::DirPath), + ) + .arg( + Arg::new("force") + .long("force") + .short('f') + .help("Forcefully overwrite existing installation") + .action(ArgAction::SetTrue), + ) + .arg( + Arg::new("global") + .long("global") + .short('g') + .help("Install a package or script as a globally available executable") + .action(ArgAction::SetTrue), + ) + .arg(env_file_arg()) }) } @@ -2417,17 +2424,16 @@ fn jupyter_subcommand() -> Command { fn uninstall_subcommand() -> Command { command( "uninstall", - "Uninstalls an executable script in the installation root's bin directory. + cstr!("Uninstalls an executable script in the installation root's bin directory. + deno uninstall serve - deno uninstall serve - -To change the installation root, use --root: - deno uninstall --root /usr/local serve +To change the installation root, use --root flag: + deno uninstall --root /usr/local serve The installation root is determined, in order of precedence: - - --root option - - DENO_INSTALL_ROOT environment variable - - $HOME/.deno", + - --root option + - DENO_INSTALL_ROOT environment variable + - $HOME/.deno"), UnstableArgsConfig::None, ) .defer(|cmd| { @@ -2451,44 +2457,41 @@ The installation root is determined, in order of precedence: fn lsp_subcommand() -> Command { Command::new("lsp").about( - "The 'deno lsp' subcommand provides a way for code editors and IDEs to -interact with Deno using the Language Server Protocol. Usually humans do not -use this subcommand directly. For example, 'deno lsp' can provide IDEs with -go-to-definition support and automatic code formatting. + "The 'deno lsp' subcommand provides a way for code editors and IDEs to interact with Deno +using the Language Server Protocol. Usually humans do not use this subcommand directly. +For example, 'deno lsp' can provide IDEs with go-to-definition support and automatic code formatting. -How to connect various editors and IDEs to 'deno lsp': -https://docs.deno.com/go/lsp", +How to connect various editors and IDEs to 'deno lsp': https://docs.deno.com/go/lsp", ) } fn lint_subcommand() -> Command { command( "lint", - "Lint JavaScript/TypeScript source code. + cstr!("Lint JavaScript/TypeScript source code. - deno lint - deno lint myfile1.ts myfile2.js + deno lint + deno lint myfile1.ts myfile2.js Print result as JSON: - deno lint --json + deno lint --json Read from stdin: - cat file.ts | deno lint - - cat file.ts | deno lint --json - + cat file.ts | deno lint - + cat file.ts | deno lint --json - List available rules: - deno lint --rules + deno lint --rules -Ignore diagnostics on the next line by preceding it with an ignore comment and -rule name: - // deno-lint-ignore no-explicit-any - // deno-lint-ignore require-await no-empty +To ignore specific diagnostics, you can write an ignore comment on the preceding line with a rule name (or multiple): + // deno-lint-ignore no-explicit-any + // deno-lint-ignore require-await no-empty -Names of rules to ignore must be specified after ignore comment. +To ignore linting on an entire file, you can add an ignore comment at the top of the file: + // deno-lint-ignore-file -Ignore linting a file by adding an ignore comment at the top of the file: - // deno-lint-ignore-file -", +Read more: https://docs.deno.com/go/cmd/lint +"), UnstableArgsConfig::ResolutionOnly, ) .defer(|cmd| { @@ -2645,7 +2648,9 @@ Grant all permissions: deno run -A jsr:@std/http/file-server Specifying the filename '-' to read the file from stdin. - curl https://examples.deno.land/hello-world.ts | deno run -"), UnstableArgsConfig::ResolutionAndRuntime), false) + curl https://examples.deno.land/hello-world.ts | deno run - + +Read more: https://docs.deno.com/go/cmd/run"), UnstableArgsConfig::ResolutionAndRuntime), false) } fn serve_host_validator(host: &str) -> Result { @@ -2657,7 +2662,19 @@ fn serve_host_validator(host: &str) -> Result { } fn serve_subcommand() -> Command { - runtime_args(command("serve", None, UnstableArgsConfig::ResolutionAndRuntime), true, true) + runtime_args(command("serve", cstr!("Run a server defined in a main module + +The serve command uses the default exports of the main module to determine which servers to start. + +See https://docs.deno.com/runtime/manual/tools/serve for more detailed information. + +Start a server defined in server.ts: + deno serve server.ts + +Start a server defined in server.ts, watching for changes and running on port 5050: + deno serve --watch --port 5050 server.ts + +Read more: https://docs.deno.com/go/cmd/serve"), UnstableArgsConfig::ResolutionAndRuntime), true, true) .arg( Arg::new("port") .long("port") @@ -2686,29 +2703,18 @@ fn serve_subcommand() -> Command { ) .arg(env_file_arg()) .arg(no_code_cache_arg()) - .about("Run a server defined in a main module - -The serve command uses the default exports of the main module to determine which -servers to start. - -See https://docs.deno.com/runtime/manual/tools/serve for -more detailed information. - -Start a server defined in server.ts: - - deno serve server.ts - -Start a server defined in server.ts, watching for changes and running on port 5050: - - deno serve --watch --port 5050 server.ts") } fn task_subcommand() -> Command { command( "task", - "Run a task defined in the configuration file - - deno task build", + cstr!( + "Run a task defined in the configuration file. + deno task build + +List all available tasks: + deno task" + ), UnstableArgsConfig::ResolutionAndRuntime, ) .defer(|cmd| { @@ -2728,15 +2734,16 @@ fn task_subcommand() -> Command { fn test_subcommand() -> Command { command("test", - "Run tests using Deno's built-in test runner. + cstr!("Run tests using Deno's built-in test runner. -Evaluate the given modules, run all tests declared with 'Deno.test()' and -report results to standard output: - deno test src/fetch_test.ts src/signal_test.ts +Evaluate the given modules, run all tests declared with Deno.test() and report results to standard output: + deno test src/fetch_test.ts src/signal_test.ts -Directory arguments are expanded to all contained files matching the glob -{*_,*.,}test.{js,mjs,ts,mts,jsx,tsx} or **/__tests__/**: - deno test src/", +Directory arguments are expanded to all contained files matching the glob {*_,*.,}test.{js,mjs,ts,mts,jsx,tsx} +or **/__tests__/**: + deno test src/ + +Read more: https://docs.deno.com/go/cmd/test"), UnstableArgsConfig::ResolutionAndRuntime ) .defer(|cmd| @@ -2882,7 +2889,7 @@ fn types_subcommand() -> Command { "types", "Print runtime TypeScript declarations. - deno types > lib.deno.d.ts + deno types > lib.deno.d.ts The declaration file could be saved and used for typing information.", UnstableArgsConfig::None, @@ -2892,31 +2899,28 @@ The declaration file could be saved and used for typing information.", fn upgrade_subcommand() -> Command { command( "upgrade", - color_print::cstr!("Upgrade deno executable to the given version. + cstr!("Upgrade deno executable to the given version. Latest - - deno upgrade + deno upgrade Specific version - - deno upgrade 1.45.0 - deno upgrade 1.46.0-rc.1 - deno upgrade 9bc2dd29ad6ba334fd57a20114e367d3c04763d4 + deno upgrade 1.45.0 + deno upgrade 1.46.0-rc.1 + deno upgrade 9bc2dd29ad6ba334fd57a20114e367d3c04763d4 Channel + deno upgrade stable + deno upgrade rc + deno upgrade canary - deno upgrade stable - deno upgrade rc - deno upgrade canary +The version is downloaded from https://dl.deno.land and is used to replace the current executable. -The version is downloaded from -https://github.com/denoland/deno/releases -and is used to replace the current executable. +If you want to not replace the current Deno executable but instead download an update to a +different location, use the --output flag: + deno upgrade --output $HOME/my_deno -If you want to not replace the current Deno executable but instead download an -update to a different location, use the --output flag: - deno upgrade --output $HOME/my_deno"), +Read more: https://docs.deno.com/go/cmd/upgrade"), UnstableArgsConfig::None, ) .hide(cfg!(not(feature = "upgrade"))) @@ -2972,7 +2976,7 @@ update to a different location, use the --output flag: ) .arg( Arg::new("version-or-hash-or-channel") - .help(color_print::cstr!("Version (v1.46.0), channel (rc, canary) or commit hash (9bc2dd29ad6ba334fd57a20114e367d3c04763d4)")) + .help(cstr!("Version (v1.46.0), channel (rc, canary) or commit hash (9bc2dd29ad6ba334fd57a20114e367d3c04763d4)")) .value_name("VERSION") .action(ArgAction::Append) .trailing_var_arg(true), @@ -3057,7 +3061,7 @@ fn compile_args_without_check_args(app: Command) -> Command { .arg(unsafely_ignore_certificate_errors_arg()) } -fn permission_args(app: Command) -> Command { +fn permission_args(app: Command, requires: Option<&'static str>) -> Command { app .after_help(cstr!(r#"Permission options: Docs: https://docs.deno.com/go/permissions @@ -3095,216 +3099,342 @@ Docs: https://docs.deno.com/go/permissions --deny-ffi | --deny-ffi="./libfoo.so" "#)) .arg( - Arg::new("allow-all") - .short('A') - .long("allow-all") - .action(ArgAction::SetTrue) - .help("Allow all permissions") - .hide(true), + { + let mut arg = Arg::new("allow-all") + .short('A') + .long("allow-all") + .action(ArgAction::SetTrue) + .help("Allow all permissions") + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) .arg( - Arg::new("allow-read") - .long("allow-read") - .short('R') - .num_args(0..) - .use_value_delimiter(true) - .require_equals(true) - .value_name("PATH") - .help("Allow file system read access. Optionally specify allowed paths") - .value_parser(value_parser!(String)) - .value_hint(ValueHint::AnyPath) - .hide(true), + { + let mut arg = Arg::new("allow-read") + .long("allow-read") + .short('R') + .num_args(0..) + .use_value_delimiter(true) + .require_equals(true) + .value_name("PATH") + .help("Allow file system read access. Optionally specify allowed paths") + .value_parser(value_parser!(String)) + .value_hint(ValueHint::AnyPath) + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) .arg( - Arg::new("deny-read") - .long("deny-read") - .num_args(0..) - .use_value_delimiter(true) - .require_equals(true) - .value_name("PATH") - .help("Deny file system read access. Optionally specify denied paths") - .value_parser(value_parser!(String)) - .value_hint(ValueHint::AnyPath) - .hide(true), + { + let mut arg = Arg::new("deny-read") + .long("deny-read") + .num_args(0..) + .use_value_delimiter(true) + .require_equals(true) + .value_name("PATH") + .help("Deny file system read access. Optionally specify denied paths") + .value_parser(value_parser!(String)) + .value_hint(ValueHint::AnyPath) + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) .arg( - Arg::new("allow-write") - .long("allow-write") - .short('W') - .num_args(0..) - .use_value_delimiter(true) - .require_equals(true) - .value_name("PATH") - .help("Allow file system write access. Optionally specify allowed paths") - .value_parser(value_parser!(String)) - .value_hint(ValueHint::AnyPath) - .hide(true), + { + let mut arg = Arg::new("allow-write") + .long("allow-write") + .short('W') + .num_args(0..) + .use_value_delimiter(true) + .require_equals(true) + .value_name("PATH") + .help("Allow file system write access. Optionally specify allowed paths") + .value_parser(value_parser!(String)) + .value_hint(ValueHint::AnyPath) + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) .arg( - Arg::new("deny-write") - .long("deny-write") - .num_args(0..) - .use_value_delimiter(true) - .require_equals(true) - .value_name("PATH") - .help("Deny file system write access. Optionally specify denied paths") - .value_parser(value_parser!(String)) - .value_hint(ValueHint::AnyPath) - .hide(true), + { + let mut arg = Arg::new("deny-write") + .long("deny-write") + .num_args(0..) + .use_value_delimiter(true) + .require_equals(true) + .value_name("PATH") + .help("Deny file system write access. Optionally specify denied paths") + .value_parser(value_parser!(String)) + .value_hint(ValueHint::AnyPath) + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) .arg( - Arg::new("allow-net") - .long("allow-net") - .short('N') - .num_args(0..) - .use_value_delimiter(true) - .require_equals(true) - .value_name("IP_OR_HOSTNAME") - .help("Allow network access. Optionally specify allowed IP addresses and host names, with ports as necessary") - .value_parser(flags_net::validator) - .hide(true), + { + let mut arg = Arg::new("allow-net") + .long("allow-net") + .short('N') + .num_args(0..) + .use_value_delimiter(true) + .require_equals(true) + .value_name("IP_OR_HOSTNAME") + .help("Allow network access. Optionally specify allowed IP addresses and host names, with ports as necessary") + .value_parser(flags_net::validator) + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) .arg( - Arg::new("deny-net") - .long("deny-net") - .num_args(0..) - .use_value_delimiter(true) - .require_equals(true) - .value_name("IP_OR_HOSTNAME") - .help("Deny network access. Optionally specify denied IP addresses and host names, with ports as necessary") - .value_parser(flags_net::validator) - .hide(true), + { + let mut arg = Arg::new("deny-net") + .long("deny-net") + .num_args(0..) + .use_value_delimiter(true) + .require_equals(true) + .value_name("IP_OR_HOSTNAME") + .help("Deny network access. Optionally specify denied IP addresses and host names, with ports as necessary") + .value_parser(flags_net::validator) + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) .arg( - Arg::new("allow-env") - .long("allow-env") - .short('E') - .num_args(0..) - .use_value_delimiter(true) - .require_equals(true) - .value_name("VARIABLE_NAME") - .help("Allow access to system environment information. Optionally specify accessible environment variables") - .value_parser(|key: &str| { - if key.is_empty() || key.contains(&['=', '\0'] as &[char]) { - return Err(format!("invalid key \"{key}\"")); - } + { + let mut arg = Arg::new("allow-env") + .long("allow-env") + .short('E') + .num_args(0..) + .use_value_delimiter(true) + .require_equals(true) + .value_name("VARIABLE_NAME") + .help("Allow access to system environment information. Optionally specify accessible environment variables") + .value_parser(|key: &str| { + if key.is_empty() || key.contains(&['=', '\0'] as &[char]) { + return Err(format!("invalid key \"{key}\"")); + } - Ok(if cfg!(windows) { - key.to_uppercase() - } else { - key.to_string() + Ok(if cfg!(windows) { + key.to_uppercase() + } else { + key.to_string() + }) }) - }) - .hide(true), + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) .arg( - Arg::new("deny-env") - .long("deny-env") - .num_args(0..) - .use_value_delimiter(true) - .require_equals(true) - .value_name("VARIABLE_NAME") - .help("Deny access to system environment information. Optionally specify accessible environment variables") - .value_parser(|key: &str| { - if key.is_empty() || key.contains(&['=', '\0'] as &[char]) { - return Err(format!("invalid key \"{key}\"")); - } + { + let mut arg = Arg::new("deny-env") + .long("deny-env") + .num_args(0..) + .use_value_delimiter(true) + .require_equals(true) + .value_name("VARIABLE_NAME") + .help("Deny access to system environment information. Optionally specify accessible environment variables") + .value_parser(|key: &str| { + if key.is_empty() || key.contains(&['=', '\0'] as &[char]) { + return Err(format!("invalid key \"{key}\"")); + } - Ok(if cfg!(windows) { - key.to_uppercase() - } else { - key.to_string() + Ok(if cfg!(windows) { + key.to_uppercase() + } else { + key.to_string() + }) }) - }) - .hide(true), + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) .arg( - Arg::new("allow-sys") - .long("allow-sys") - .short('S') - .num_args(0..) - .use_value_delimiter(true) - .require_equals(true) - .value_name("API_NAME") - .help("Allow access to OS information. Optionally allow specific APIs by function name") - .value_parser(|key: &str| parse_sys_kind(key).map(ToString::to_string)) - .hide(true), + { + let mut arg = Arg::new("allow-sys") + .long("allow-sys") + .short('S') + .num_args(0..) + .use_value_delimiter(true) + .require_equals(true) + .value_name("API_NAME") + .help("Allow access to OS information. Optionally allow specific APIs by function name") + .value_parser(|key: &str| parse_sys_kind(key).map(ToString::to_string)) + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) .arg( - Arg::new("deny-sys") - .long("deny-sys") - .num_args(0..) - .use_value_delimiter(true) - .require_equals(true) - .value_name("API_NAME") - .help("Deny access to OS information. Optionally deny specific APIs by function name") - .value_parser(|key: &str| parse_sys_kind(key).map(ToString::to_string)) - .hide(true), + { + let mut arg = Arg::new("deny-sys") + .long("deny-sys") + .num_args(0..) + .use_value_delimiter(true) + .require_equals(true) + .value_name("API_NAME") + .help("Deny access to OS information. Optionally deny specific APIs by function name") + .value_parser(|key: &str| parse_sys_kind(key).map(ToString::to_string)) + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) .arg( - Arg::new("allow-run") - .long("allow-run") - .num_args(0..) - .use_value_delimiter(true) - .require_equals(true) - .value_name("PROGRAM_NAME") - .help("Allow running subprocesses. Optionally specify allowed runnable program names") - .hide(true), + { + let mut arg = Arg::new("allow-run") + .long("allow-run") + .num_args(0..) + .use_value_delimiter(true) + .require_equals(true) + .value_name("PROGRAM_NAME") + .help("Allow running subprocesses. Optionally specify allowed runnable program names") + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) .arg( - Arg::new("deny-run") - .long("deny-run") - .num_args(0..) - .use_value_delimiter(true) - .require_equals(true) - .value_name("PROGRAM_NAME") - .help("Deny running subprocesses. Optionally specify denied runnable program names") - .hide(true), + { + let mut arg = Arg::new("deny-run") + .long("deny-run") + .num_args(0..) + .use_value_delimiter(true) + .require_equals(true) + .value_name("PROGRAM_NAME") + .help("Deny running subprocesses. Optionally specify denied runnable program names") + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + + } ) .arg( - Arg::new("allow-ffi") - .long("allow-ffi") - .num_args(0..) - .use_value_delimiter(true) - .require_equals(true) - .value_name("PATH") - .help("(Unstable) Allow loading dynamic libraries. Optionally specify allowed directories or files") - .value_parser(value_parser!(String)) - .value_hint(ValueHint::AnyPath) - .hide(true), + { + let mut arg = Arg::new("allow-ffi") + .long("allow-ffi") + .num_args(0..) + .use_value_delimiter(true) + .require_equals(true) + .value_name("PATH") + .help("(Unstable) Allow loading dynamic libraries. Optionally specify allowed directories or files") + .value_parser(value_parser!(String)) + .value_hint(ValueHint::AnyPath) + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) .arg( - Arg::new("deny-ffi") - .long("deny-ffi") - .num_args(0..) - .use_value_delimiter(true) - .require_equals(true) - .value_name("PATH") - .help("(Unstable) Deny loading dynamic libraries. Optionally specify denied directories or files") - .value_parser(value_parser!(String)) - .value_hint(ValueHint::AnyPath) - .hide(true), + { + let mut arg = Arg::new("deny-ffi") + .long("deny-ffi") + .num_args(0..) + .use_value_delimiter(true) + .require_equals(true) + .value_name("PATH") + .help("(Unstable) Deny loading dynamic libraries. Optionally specify denied directories or files") + .value_parser(value_parser!(String)) + .value_hint(ValueHint::AnyPath) + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) .arg( - Arg::new("allow-hrtime") - .long("allow-hrtime") - .action(ArgAction::SetTrue) - .help("REMOVED in Deno 2.0") - .hide(true), + { + let mut arg = Arg::new("allow-hrtime") + .long("allow-hrtime") + .action(ArgAction::SetTrue) + .help("REMOVED in Deno 2.0") + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) .arg( - Arg::new("deny-hrtime") - .long("deny-hrtime") - .action(ArgAction::SetTrue) - .help("REMOVED in Deno 2.0") - .hide(true), + { + let mut arg = Arg::new("deny-hrtime") + .long("deny-hrtime") + .action(ArgAction::SetTrue) + .help("REMOVED in Deno 2.0") + .hide(true) + ; + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) .arg( - Arg::new("no-prompt") - .long("no-prompt") - .action(ArgAction::SetTrue) - .hide(true) - .help("Always throw if required permission wasn't passed"), + { + let mut arg = Arg::new("no-prompt") + .long("no-prompt") + .action(ArgAction::SetTrue) + .hide(true) + .help("Always throw if required permission wasn't passed"); + if let Some(requires) = requires { + arg = arg.requires(requires) + } + arg + } ) } @@ -3315,7 +3445,7 @@ fn runtime_args( ) -> Command { let app = compile_args(app); let app = if include_perms { - permission_args(app) + permission_args(app, None) } else { app }; @@ -10295,4 +10425,15 @@ mod tests { assert_eq!(long_flag, subcommand, "{} subcommand", command.get_name()); } } + + #[test] + fn install_permissions_non_global() { + let r = + flags_from_vec(svec!["deno", "install", "--allow-net", "jsr:@std/fs"]); + + assert!(r + .unwrap_err() + .to_string() + .contains("Note: Permission flags can only be used in a global setting")); + } } diff --git a/tests/specs/npm/npmrc_not_next_to_package_json/__test__.jsonc b/tests/specs/npm/npmrc_not_next_to_package_json/__test__.jsonc index 68fa8fdf0d..1f4952ed9b 100644 --- a/tests/specs/npm/npmrc_not_next_to_package_json/__test__.jsonc +++ b/tests/specs/npm/npmrc_not_next_to_package_json/__test__.jsonc @@ -4,7 +4,7 @@ "HOME": "$DENO_DIR" }, "tempDir": true, - "args": "install -A -L debug", + "args": "install -L debug", "output": "main.out", "cwd": "subdir" } From 105c571fb6ba4d9b9404b5998eb6e17f0efdd79f Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:42:35 -0700 Subject: [PATCH 09/45] fix(cli): Map error kind to `PermissionDenied` when symlinking fails due to permissions (#25398) Fixes https://github.com/denoland/deno/issues/25333. We fall back to junctions if the error kind is `PermissionDenied` but the std library actually sets the kind to `Uncategorized` if the symlink fails due to insufficient privileges. This was causing the fallback to not actually fall back in this case. --- cli/util/fs.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/cli/util/fs.rs b/cli/util/fs.rs index d723d24e1f..fdf6035ecd 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -496,9 +496,9 @@ pub fn hard_link_dir_recursive(from: &Path, to: &Path) -> Result<(), AnyError> { } pub fn symlink_dir(oldpath: &Path, newpath: &Path) -> Result<(), Error> { - let err_mapper = |err: Error| { + let err_mapper = |err: Error, kind: Option| { Error::new( - err.kind(), + kind.unwrap_or_else(|| err.kind()), format!( "{}, symlink '{}' -> '{}'", err, @@ -510,12 +510,19 @@ pub fn symlink_dir(oldpath: &Path, newpath: &Path) -> Result<(), Error> { #[cfg(unix)] { use std::os::unix::fs::symlink; - symlink(oldpath, newpath).map_err(err_mapper)?; + symlink(oldpath, newpath).map_err(|e| err_mapper(e, None))?; } #[cfg(not(unix))] { use std::os::windows::fs::symlink_dir; - symlink_dir(oldpath, newpath).map_err(err_mapper)?; + symlink_dir(oldpath, newpath).map_err(|err| { + if let Some(code) = err.raw_os_error() { + if code as u32 == winapi::shared::winerror::ERROR_PRIVILEGE_NOT_HELD { + return err_mapper(err, Some(ErrorKind::PermissionDenied)); + } + } + err_mapper(err, None) + })?; } Ok(()) } From b72d1a7256fd141c644024b8d9e15fa11234109e Mon Sep 17 00:00:00 2001 From: Asher Gomez Date: Wed, 4 Sep 2024 11:28:15 +1000 Subject: [PATCH 10/45] BREAKING(fs): remove `Deno.fstat[Sync]()` (#25351) Towards #22079 Signed-off-by: Asher Gomez --- cli/tools/test/fmt.rs | 2 +- cli/tsc/dts/lib.deno.ns.d.ts | 39 ------------------------- ext/fs/30_fs.js | 18 +++--------- runtime/js/90_deno_ns.js | 16 ---------- runtime/js/99_main.js | 4 --- tests/specs/future/runtime_api/main.js | 2 -- tests/specs/future/runtime_api/main.out | 2 -- tests/unit/stat_test.ts | 35 ---------------------- 8 files changed, 5 insertions(+), 113 deletions(-) diff --git a/cli/tools/test/fmt.rs b/cli/tools/test/fmt.rs index 0cdb3f05ab..bfaac0616e 100644 --- a/cli/tools/test/fmt.rs +++ b/cli/tools/test/fmt.rs @@ -319,7 +319,7 @@ pub const OP_DETAILS: phf::Map<&'static str, [&'static str; 2]> = phf_map! { "op_fs_copy_file_async" => ["copy a file", "awaiting the result of a `Deno.copyFile` call"], "op_fs_events_poll" => ["get the next file system event", "breaking out of a for await loop looping over `Deno.FsEvents`"], "op_fs_fdatasync_async" => ["flush pending data operations for a file to disk", "awaiting the result of a `Deno.fdatasync` or `Deno.FsFile.syncData` call"], - "op_fs_file_stat_async" => ["get file metadata", "awaiting the result of a `Deno.fstat` or `Deno.FsFile.stat` call"], + "op_fs_file_stat_async" => ["get file metadata", "awaiting the result of a `Deno.FsFile.prototype.stat` call"], "op_fs_flock_async" => ["lock a file", "awaiting the result of a `Deno.FsFile.lock` call"], "op_fs_fsync_async" => ["flush pending data operations for a file to disk", "awaiting the result of a `Deno.fsync` or `Deno.FsFile.sync` call"], "op_fs_ftruncate_async" => ["truncate a file", "awaiting the result of a `Deno.ftruncate` or `Deno.FsFile.truncate` call"], diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index 8489a8de87..0b7ac7cc9d 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -5326,45 +5326,6 @@ declare namespace Deno { */ export function ftruncateSync(rid: number, len?: number): void; - /** - * Returns a `Deno.FileInfo` for the given file stream. - * - * ```ts - * import { assert } from "jsr:@std/assert"; - * - * const file = await Deno.open("file.txt", { read: true }); - * const fileInfo = await Deno.fstat(file.rid); - * assert(fileInfo.isFile); - * ``` - * - * @deprecated This will be removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @category File System - */ - export function fstat(rid: number): Promise; - - /** - * Synchronously returns a {@linkcode Deno.FileInfo} for the given file - * stream. - * - * ```ts - * import { assert } from "jsr:@std/assert"; - * - * const file = Deno.openSync("file.txt", { read: true }); - * const fileInfo = Deno.fstatSync(file.rid); - * assert(fileInfo.isFile); - * ``` - * - * @deprecated This will be removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @category File System - */ - export function fstatSync(rid: number): FileInfo; - /** * Synchronously changes the access (`atime`) and modification (`mtime`) times * of a file system object referenced by `path`. Given times are either in diff --git a/ext/fs/30_fs.js b/ext/fs/30_fs.js index b95fe623ae..87607ca28c 100644 --- a/ext/fs/30_fs.js +++ b/ext/fs/30_fs.js @@ -395,15 +395,6 @@ function parseFileInfo(response) { }; } -function fstatSync(rid) { - op_fs_file_stat_sync(rid, statBuf); - return statStruct(statBuf); -} - -async function fstat(rid) { - return parseFileInfo(await op_fs_file_stat_async(rid)); -} - async function lstat(path) { const res = await op_fs_lstat_async(pathFromURL(path)); return parseFileInfo(res); @@ -687,12 +678,13 @@ class FsFile { return seekSync(this.#rid, offset, whence); } - stat() { - return fstat(this.#rid); + async stat() { + return parseFileInfo(await op_fs_file_stat_async(this.#rid)); } statSync() { - return fstatSync(this.#rid); + op_fs_file_stat_sync(this.#rid, statBuf); + return statStruct(statBuf); } async syncData() { @@ -968,8 +960,6 @@ export { fdatasyncSync, File, FsFile, - fstat, - fstatSync, fsync, fsyncSync, ftruncate, diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index 7c805e6476..3fdab78003 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -168,22 +168,6 @@ const denoNs = { connectTls: tls.connectTls, listenTls: tls.listenTls, startTls: tls.startTls, - fstatSync(rid) { - internals.warnOnDeprecatedApi( - "Deno.fstatSync()", - new Error().stack, - "Use `Deno.FsFile.statSync()` instead.", - ); - return fs.fstatSync(rid); - }, - fstat(rid) { - internals.warnOnDeprecatedApi( - "Deno.fstat()", - new Error().stack, - "Use `Deno.FsFile.stat()` instead.", - ); - return fs.fstat(rid); - }, fsyncSync: fs.fsyncSync, fsync: fs.fsync, fdatasyncSync: fs.fdatasyncSync, diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index 1746128513..a305614335 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -802,8 +802,6 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) { delete globalThis.window; delete Deno.Buffer; delete Deno.File; - delete Deno.fstat; - delete Deno.fstatSync; delete Deno.ftruncate; delete Deno.ftruncateSync; delete Deno.FsFile.prototype.rid; @@ -979,8 +977,6 @@ function bootstrapWorkerRuntime( if (future) { delete Deno.Buffer; delete Deno.File; - delete Deno.fstat; - delete Deno.fstatSync; delete Deno.ftruncate; delete Deno.ftruncateSync; delete Deno.FsFile.prototype.rid; diff --git a/tests/specs/future/runtime_api/main.js b/tests/specs/future/runtime_api/main.js index ab53a809bc..4e22716f02 100644 --- a/tests/specs/future/runtime_api/main.js +++ b/tests/specs/future/runtime_api/main.js @@ -1,8 +1,6 @@ console.log("window is", globalThis.window); console.log("Deno.Buffer is", Deno.Buffer); console.log("Deno.File is", Deno.File); -console.log("Deno.fstat is", Deno.fstat); -console.log("Deno.fstatSync is", Deno.fstatSync); console.log("Deno.ftruncate is", Deno.ftruncate); console.log("Deno.ftruncateSync is", Deno.ftruncateSync); console.log( diff --git a/tests/specs/future/runtime_api/main.out b/tests/specs/future/runtime_api/main.out index 08b62ea3a9..922f4afad3 100644 --- a/tests/specs/future/runtime_api/main.out +++ b/tests/specs/future/runtime_api/main.out @@ -1,8 +1,6 @@ window is undefined Deno.Buffer is undefined Deno.File is undefined -Deno.fstat is undefined -Deno.fstatSync is undefined Deno.ftruncate is undefined Deno.ftruncateSync is undefined Deno.FsFile.prototype.rid is undefined diff --git a/tests/unit/stat_test.ts b/tests/unit/stat_test.ts index f9d7800312..950ffa81bc 100644 --- a/tests/unit/stat_test.ts +++ b/tests/unit/stat_test.ts @@ -1,48 +1,13 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -// deno-lint-ignore-file no-deprecated-deno-api - import { assert, assertEquals, assertRejects, assertThrows, - DENO_FUTURE, pathToAbsoluteFileUrl, } from "./test_util.ts"; -Deno.test( - { ignore: DENO_FUTURE, permissions: { read: true } }, - function fstatSyncSuccess() { - using file = Deno.openSync("README.md"); - const fileInfo = Deno.fstatSync(file.rid); - assert(fileInfo.isFile); - assert(!fileInfo.isSymlink); - assert(!fileInfo.isDirectory); - assert(fileInfo.size); - assert(fileInfo.atime); - assert(fileInfo.mtime); - // The `birthtime` field is not available on Linux before kernel version 4.11. - assert(fileInfo.birthtime || Deno.build.os === "linux"); - }, -); - -Deno.test( - { ignore: DENO_FUTURE, permissions: { read: true } }, - async function fstatSuccess() { - using file = await Deno.open("README.md"); - const fileInfo = await Deno.fstat(file.rid); - assert(fileInfo.isFile); - assert(!fileInfo.isSymlink); - assert(!fileInfo.isDirectory); - assert(fileInfo.size); - assert(fileInfo.atime); - assert(fileInfo.mtime); - // The `birthtime` field is not available on Linux before kernel version 4.11. - assert(fileInfo.birthtime || Deno.build.os === "linux"); - }, -); - Deno.test( { permissions: { read: true, write: true } }, function statSyncSuccess() { From 7079acd74d583a14f3c09e16f196a6851381439b Mon Sep 17 00:00:00 2001 From: Asher Gomez Date: Wed, 4 Sep 2024 16:15:34 +1000 Subject: [PATCH 11/45] chore: update WPT (#25250) Co-authored-by: crowlkats --- ext/web/03_abort_signal.js | 40 +++++++--- tests/wpt/runner/expectation.json | 120 +++++++++++++++++++++++------- tests/wpt/suite | 2 +- 3 files changed, 123 insertions(+), 39 deletions(-) diff --git a/ext/web/03_abort_signal.js b/ext/web/03_abort_signal.js index 81844d53fc..ae0701451b 100644 --- a/ext/web/03_abort_signal.js +++ b/ext/web/03_abort_signal.js @@ -71,6 +71,7 @@ class WeakRefSet { const add = Symbol("[[add]]"); const signalAbort = Symbol("[[signalAbort]]"); const remove = Symbol("[[remove]]"); +const runAbortSteps = Symbol("[[runAbortSteps]]"); const abortReason = Symbol("[[abortReason]]"); const abortAlgos = Symbol("[[abortAlgos]]"); const dependent = Symbol("[[dependent]]"); @@ -149,26 +150,43 @@ class AbortSignal extends EventTarget { return; } this[abortReason] = reason; + + const dependentSignalsToAbort = []; + if (this[dependentSignals] !== null) { + const dependentSignalArray = this[dependentSignals].toArray(); + for (let i = 0; i < dependentSignalArray.length; ++i) { + const dependentSignal = dependentSignalArray[i]; + if (dependentSignal[abortReason] === undefined) { + dependentSignal[abortReason] = this[abortReason]; + ArrayPrototypePush(dependentSignalsToAbort, dependentSignal); + } + } + } + + this[runAbortSteps](); + + if (dependentSignalsToAbort.length !== 0) { + for (let i = 0; i < dependentSignalsToAbort.length; ++i) { + const dependentSignal = dependentSignalsToAbort[i]; + dependentSignal[runAbortSteps](); + } + } + } + + [runAbortSteps]() { const algos = this[abortAlgos]; this[abortAlgos] = null; - if (listenerCount(this, "abort") > 0) { - const event = new Event("abort"); - setIsTrusted(event, true); - super.dispatchEvent(event); - } if (algos !== null) { for (const algorithm of new SafeSetIterator(algos)) { algorithm(); } } - if (this[dependentSignals] !== null) { - const dependentSignalArray = this[dependentSignals].toArray(); - for (let i = 0; i < dependentSignalArray.length; ++i) { - const dependentSignal = dependentSignalArray[i]; - dependentSignal[signalAbort](reason); - } + if (listenerCount(this, "abort") > 0) { + const event = new Event("abort"); + setIsTrusted(event, true); + super.dispatchEvent(event); } } diff --git a/tests/wpt/runner/expectation.json b/tests/wpt/runner/expectation.json index 6dfcccf164..ef22d1dd79 100644 --- a/tests/wpt/runner/expectation.json +++ b/tests/wpt/runner/expectation.json @@ -2554,9 +2554,9 @@ "Document interface: attribute firstElementChild", "Document interface: attribute lastElementChild", "Document interface: attribute childElementCount", - "Document interface: operation prepend((Node or TrustedScript or DOMString)...)", - "Document interface: operation append((Node or TrustedScript or DOMString)...)", - "Document interface: operation replaceChildren((Node or TrustedScript or DOMString)...)", + "Document interface: operation prepend((Node or DOMString)...)", + "Document interface: operation append((Node or DOMString)...)", + "Document interface: operation replaceChildren((Node or DOMString)...)", "Document interface: operation querySelector(DOMString)", "Document interface: operation querySelectorAll(DOMString)", "Document interface: operation createExpression(DOMString, optional XPathNSResolver?)", @@ -2587,9 +2587,9 @@ "DocumentType interface: attribute name", "DocumentType interface: attribute publicId", "DocumentType interface: attribute systemId", - "DocumentType interface: operation before((Node or TrustedScript or DOMString)...)", - "DocumentType interface: operation after((Node or TrustedScript or DOMString)...)", - "DocumentType interface: operation replaceWith((Node or TrustedScript or DOMString)...)", + "DocumentType interface: operation before((Node or DOMString)...)", + "DocumentType interface: operation after((Node or DOMString)...)", + "DocumentType interface: operation replaceWith((Node or DOMString)...)", "DocumentType interface: operation remove()", "DocumentFragment interface: existence and properties of interface object", "DocumentFragment interface object length", @@ -2602,9 +2602,9 @@ "DocumentFragment interface: attribute firstElementChild", "DocumentFragment interface: attribute lastElementChild", "DocumentFragment interface: attribute childElementCount", - "DocumentFragment interface: operation prepend((Node or TrustedScript or DOMString)...)", - "DocumentFragment interface: operation append((Node or TrustedScript or DOMString)...)", - "DocumentFragment interface: operation replaceChildren((Node or TrustedScript or DOMString)...)", + "DocumentFragment interface: operation prepend((Node or DOMString)...)", + "DocumentFragment interface: operation append((Node or DOMString)...)", + "DocumentFragment interface: operation replaceChildren((Node or DOMString)...)", "DocumentFragment interface: operation querySelector(DOMString)", "DocumentFragment interface: operation querySelectorAll(DOMString)", "ShadowRoot interface: existence and properties of interface object", @@ -2669,16 +2669,16 @@ "Element interface: attribute firstElementChild", "Element interface: attribute lastElementChild", "Element interface: attribute childElementCount", - "Element interface: operation prepend((Node or TrustedScript or DOMString)...)", - "Element interface: operation append((Node or TrustedScript or DOMString)...)", - "Element interface: operation replaceChildren((Node or TrustedScript or DOMString)...)", + "Element interface: operation prepend((Node or DOMString)...)", + "Element interface: operation append((Node or DOMString)...)", + "Element interface: operation replaceChildren((Node or DOMString)...)", "Element interface: operation querySelector(DOMString)", "Element interface: operation querySelectorAll(DOMString)", "Element interface: attribute previousElementSibling", "Element interface: attribute nextElementSibling", - "Element interface: operation before((Node or TrustedScript or DOMString)...)", - "Element interface: operation after((Node or TrustedScript or DOMString)...)", - "Element interface: operation replaceWith((Node or TrustedScript or DOMString)...)", + "Element interface: operation before((Node or DOMString)...)", + "Element interface: operation after((Node or DOMString)...)", + "Element interface: operation replaceWith((Node or DOMString)...)", "Element interface: operation remove()", "Element interface: attribute assignedSlot", "NamedNodeMap interface: existence and properties of interface object", @@ -2723,9 +2723,9 @@ "CharacterData interface: operation replaceData(unsigned long, unsigned long, DOMString)", "CharacterData interface: attribute previousElementSibling", "CharacterData interface: attribute nextElementSibling", - "CharacterData interface: operation before((Node or TrustedScript or DOMString)...)", - "CharacterData interface: operation after((Node or TrustedScript or DOMString)...)", - "CharacterData interface: operation replaceWith((Node or TrustedScript or DOMString)...)", + "CharacterData interface: operation before((Node or DOMString)...)", + "CharacterData interface: operation after((Node or DOMString)...)", + "CharacterData interface: operation replaceWith((Node or DOMString)...)", "CharacterData interface: operation remove()", "Text interface: existence and properties of interface object", "Text interface object length", @@ -3089,7 +3089,16 @@ "Node member must be removed: getFeature", "Node member must be removed: getUserData", "Node member must be removed: setUserData", - "Node member must be removed: rootNode" + "Node member must be removed: rootNode", + "The DOMSubtreeModified mutation event must not be fired.", + "The DOMNodeInserted mutation event must not be fired.", + "The DOMNodeRemoved mutation event must not be fired.", + "The DOMNodeRemovedFromDocument mutation event must not be fired.", + "The DOMNodeInsertedIntoDocument mutation event must not be fired.", + "The DOMCharacterDataModified mutation event must not be fired.", + "The DOMAttrModified mutation event must not be fired.", + "The DOMAttributeNameChanged mutation event must not be fired.", + "The DOMElementNameChanged mutation event must not be fired." ], "idlharness.any.serviceworker.html": false, "idlharness.any.sharedworker.html": false, @@ -4577,7 +4586,31 @@ "≯ (using .host)", "≯ (using .hostname)", "≯ (using .host)", - "≯ (using .hostname)" + "≯ (using .hostname)", + "≠ (using .host)", + "≠ (using .hostname)", + "≠ (using .host)", + "≠ (using .hostname)", + "≮ (using .host)", + "≮ (using .hostname)", + "≮ (using .host)", + "≮ (using .hostname)", + "≯ (using .host)", + "≯ (using .hostname)", + "≯ (using .host)", + "≯ (using .hostname)", + "=­̸ (using .host)", + "=­̸ (using .hostname)", + "=­̸ (using .host)", + "=­̸ (using .hostname)", + "<­̸ (using .host)", + "<­̸ (using .hostname)", + "<­̸ (using .host)", + "<­̸ (using .hostname)", + ">­̸ (using .host)", + ">­̸ (using .hostname)", + ">­̸ (using .host)", + ">­̸ (using .hostname)" ], "url-origin.any.html": [ "Origin parsing: without base", @@ -4850,6 +4883,7 @@ "Parsing: against " ], "url-constructor.any.html?include=file": [ + "Parsing: without base", "Parsing: against ", "Parsing: without base", "Parsing: without base", @@ -4912,6 +4946,7 @@ "Parsing: against " ], "url-constructor.any.worker.html?include=file": [ + "Parsing: without base", "Parsing: against ", "Parsing: without base", "Parsing: without base", @@ -5790,7 +5825,6 @@ "Parsing origin: against ", "Parsing origin: against ", "Parsing origin: against ", - "Parsing origin: against ", "Parsing origin: against ", "Parsing origin: against ", "Parsing origin: against ", @@ -5891,7 +5925,21 @@ "Parsing origin: against ", "Parsing origin: against ", "Parsing origin: against ", - "Parsing origin: against " + "Parsing origin: against ", + "Parsing origin: against ", + "Parsing origin: against ", + "Parsing origin: against ", + "Parsing origin: against ", + "Parsing origin: against ", + "Parsing origin: against ", + "Parsing origin: against ", + "Parsing origin: against ", + "Parsing origin: against ", + "Parsing origin: against ", + "Parsing origin: against ", + "Parsing origin: against ", + "Parsing origin: against ", + "Parsing origin: against " ], "a-element.html?exclude=(file|javascript|mailto)": [ "Test that embedded 0x0A is stripped", @@ -6222,7 +6270,6 @@ "Parsing: against ", "Parsing: against ", "Parsing: against ", - "Parsing: against ", "Parsing: against ", "Parsing: against ", "Parsing: against ", @@ -6568,6 +6615,19 @@ "Parsing: against ", "Parsing: against ", "Parsing: against ", + "Parsing: against ", + "Parsing: against ", + "Parsing: against ", + "Parsing: against ", + "Parsing: against ", + "Parsing: against ", + "Parsing: against ", + "Parsing: against ", + "Parsing: against ", + "Parsing: against ", + "Parsing: against ", + "Parsing: against ", + "Parsing: against ", "Parsing: against ", "Parsing: against ", "Parsing: against ", @@ -6575,7 +6635,8 @@ "Parsing: against ", "Parsing: against ", "Parsing: against ", - "Parsing: against " + "Parsing: against ", + "Parsing: against " ], "a-element.html?include=file": [ "Test that embedded 0x0A is stripped", @@ -6603,6 +6664,11 @@ "Parsing: against ", "Parsing: against ", "Parsing: against ", + "Parsing: against ", + "Parsing: against ", + "Parsing: against ", + "Parsing: against ", + "Parsing: against ", "Parsing: against ", "Parsing: against ", "Parsing: against ", @@ -10323,9 +10389,9 @@ "import() should not drain the microtask queue if it fails during specifier resolution", "import() should not drain the microtask queue when loading an already loaded module" ], - "css-import-in-worker.any.worker.html": false, - "with-import-assertions.any.html": false, - "with-import-assertions.any.worker.html": false + "css-import-in-worker.any.worker.html": true, + "with-import-assertions.any.html": true, + "with-import-assertions.any.worker.html": true } }, "import-meta": { diff --git a/tests/wpt/suite b/tests/wpt/suite index a7b5eac8f2..e78446e34a 160000 --- a/tests/wpt/suite +++ b/tests/wpt/suite @@ -1 +1 @@ -Subproject commit a7b5eac8f2cfac28bb12beeea15a9e2b126a568e +Subproject commit e78446e34a1921371658a5df08c71d83f50a2a2f From ce6b6751028bfe08b49628a30e660ff479eded02 Mon Sep 17 00:00:00 2001 From: Ian Bull Date: Wed, 4 Sep 2024 00:05:29 -0700 Subject: [PATCH 12/45] refactor(ext/fetch): align error messages (#25374) Aligns the error messages in the ext/fetch folder to be in-line with the Deno style guide. https://github.com/denoland/deno/issues/25269 --- ext/fetch/20_headers.js | 22 ++++++++--------- ext/fetch/21_formdata.js | 6 +++-- ext/fetch/22_body.js | 4 ++-- ext/fetch/23_response.js | 10 ++++---- ext/fetch/26_fetch.js | 10 ++++---- ext/fetch/fs_fetch_handler.rs | 2 +- ext/fetch/lib.rs | 24 +++++++++++-------- .../run/wasm_streaming_panic_test.js.out | 2 +- tests/unit/cache_api_test.ts | 2 +- tests/unit/fetch_test.ts | 4 ++-- tests/unit/headers_test.ts | 4 ++-- tests/unit/wasm_test.ts | 2 +- 12 files changed, 51 insertions(+), 41 deletions(-) diff --git a/ext/fetch/20_headers.js b/ext/fetch/20_headers.js index 1690d9f7d6..e56a74c423 100644 --- a/ext/fetch/20_headers.js +++ b/ext/fetch/20_headers.js @@ -73,7 +73,7 @@ function fillHeaders(headers, object) { const header = object[i]; if (header.length !== 2) { throw new TypeError( - `Invalid header. Length must be 2, but is ${header.length}`, + `Invalid header: length must be 2, but is ${header.length}`, ); } appendHeader(headers, header[0], header[1]); @@ -133,15 +133,15 @@ function appendHeader(headers, name, value) { // 2. if (!checkHeaderNameForHttpTokenCodePoint(name)) { - throw new TypeError("Header name is not valid."); + throw new TypeError(`Invalid header name: "${name}"`); } if (!checkForInvalidValueChars(value)) { - throw new TypeError("Header value is not valid."); + throw new TypeError(`Invalid header value: "${value}"`); } // 3. if (headers[_guard] == "immutable") { - throw new TypeError("Headers are immutable."); + throw new TypeError("Cannot change header: headers are immutable"); } // 7. @@ -330,10 +330,10 @@ class Headers { name = webidl.converters["ByteString"](name, prefix, "Argument 1"); if (!checkHeaderNameForHttpTokenCodePoint(name)) { - throw new TypeError("Header name is not valid."); + throw new TypeError(`Invalid header name: "${name}"`); } if (this[_guard] == "immutable") { - throw new TypeError("Headers are immutable."); + throw new TypeError("Cannot change headers: headers are immutable"); } const list = this[_headerList]; @@ -356,7 +356,7 @@ class Headers { name = webidl.converters["ByteString"](name, prefix, "Argument 1"); if (!checkHeaderNameForHttpTokenCodePoint(name)) { - throw new TypeError("Header name is not valid."); + throw new TypeError(`Invalid header name: "${name}"`); } const list = this[_headerList]; @@ -387,7 +387,7 @@ class Headers { name = webidl.converters["ByteString"](name, prefix, "Argument 1"); if (!checkHeaderNameForHttpTokenCodePoint(name)) { - throw new TypeError("Header name is not valid."); + throw new TypeError(`Invalid header name: "${name}"`); } const list = this[_headerList]; @@ -415,14 +415,14 @@ class Headers { // 2. if (!checkHeaderNameForHttpTokenCodePoint(name)) { - throw new TypeError("Header name is not valid."); + throw new TypeError(`Invalid header name: "${name}"`); } if (!checkForInvalidValueChars(value)) { - throw new TypeError("Header value is not valid."); + throw new TypeError(`Invalid header value: "${value}"`); } if (this[_guard] == "immutable") { - throw new TypeError("Headers are immutable."); + throw new TypeError("Cannot change headers: headers are immutable"); } const list = this[_headerList]; diff --git a/ext/fetch/21_formdata.js b/ext/fetch/21_formdata.js index bb33ea2d47..7d466b8e25 100644 --- a/ext/fetch/21_formdata.js +++ b/ext/fetch/21_formdata.js @@ -396,7 +396,9 @@ class MultipartParser { */ constructor(body, boundary) { if (!boundary) { - throw new TypeError("multipart/form-data must provide a boundary"); + throw new TypeError( + "Cannot construct MultipartParser: multipart/form-data must provide a boundary", + ); } this.boundary = `--${boundary}`; @@ -445,7 +447,7 @@ class MultipartParser { ) { return new FormData(); } - throw new TypeError("Unable to parse body as form data."); + throw new TypeError("Unable to parse body as form data"); } const formData = new FormData(); diff --git a/ext/fetch/22_body.js b/ext/fetch/22_body.js index 82f41411d8..a8f5deac9c 100644 --- a/ext/fetch/22_body.js +++ b/ext/fetch/22_body.js @@ -151,7 +151,7 @@ class InnerBody { * @returns {Promise} */ consume() { - if (this.unusable()) throw new TypeError("Body already consumed."); + if (this.unusable()) throw new TypeError("Body already consumed"); if ( ObjectPrototypeIsPrototypeOf( ReadableStreamPrototype, @@ -372,7 +372,7 @@ function packageData(bytes, type, mimeType) { const boundary = mimeType.parameters.get("boundary"); if (boundary === null) { throw new TypeError( - "Missing boundary parameter in mime type of multipart formdata.", + "Cannot turn into form data: missing boundary parameter in mime type of multipart form data", ); } return parseFormData(chunkToU8(bytes), boundary); diff --git a/ext/fetch/23_response.js b/ext/fetch/23_response.js index 94fc69a986..7dad8c0472 100644 --- a/ext/fetch/23_response.js +++ b/ext/fetch/23_response.js @@ -172,7 +172,7 @@ function initializeAResponse(response, init, bodyWithType) { // 1. if ((init.status < 200 || init.status > 599) && init.status != 101) { throw new RangeError( - `The status provided (${init.status}) is not equal to 101 and outside the range [200, 599].`, + `The status provided (${init.status}) is not equal to 101 and outside the range [200, 599]`, ); } @@ -181,7 +181,9 @@ function initializeAResponse(response, init, bodyWithType) { init.statusText && RegExpPrototypeExec(REASON_PHRASE_RE, init.statusText) === null ) { - throw new TypeError("Status text is not valid."); + throw new TypeError( + `Invalid status text: "${init.statusText}"`, + ); } // 3. @@ -263,7 +265,7 @@ class Response { const baseURL = getLocationHref(); const parsedURL = new URL(url, baseURL); if (!redirectStatus(status)) { - throw new RangeError("Invalid redirect status code."); + throw new RangeError(`Invalid redirect status code: ${status}`); } const inner = newInnerResponse(status); inner.type = "default"; @@ -395,7 +397,7 @@ class Response { clone() { webidl.assertBranded(this, ResponsePrototype); if (this[_body] && this[_body].unusable()) { - throw new TypeError("Body is unusable."); + throw new TypeError("Body is unusable"); } const second = webidl.createBranded(Response); const newRes = cloneInnerResponse(this[_response]); diff --git a/ext/fetch/26_fetch.js b/ext/fetch/26_fetch.js index 47186fb2dd..8ac364a931 100644 --- a/ext/fetch/26_fetch.js +++ b/ext/fetch/26_fetch.js @@ -99,7 +99,7 @@ function createResponseBodyStream(responseBodyRid, terminator) { async function mainFetch(req, recursive, terminator) { if (req.blobUrlEntry !== null) { if (req.method !== "GET") { - throw new TypeError("Blob URL fetch only supports GET method."); + throw new TypeError("Blob URL fetch only supports GET method"); } const body = new InnerBody(req.blobUrlEntry.stream()); @@ -145,7 +145,7 @@ async function mainFetch(req, recursive, terminator) { reqRid = resourceForReadableStream(stream, req.body.length); } } else { - throw new TypeError("invalid body"); + throw new TypeError("Invalid body"); } } @@ -441,13 +441,15 @@ function handleWasmStreaming(source, rid) { typeof contentType !== "string" || StringPrototypeToLowerCase(contentType) !== "application/wasm" ) { - throw new TypeError("Invalid WebAssembly content type."); + throw new TypeError("Invalid WebAssembly content type"); } } // 2.5. if (!res.ok) { - throw new TypeError(`HTTP status code ${res.status}`); + throw new TypeError( + `Failed to receive WebAssembly content: HTTP status code ${res.status}`, + ); } // Pass the resolved URL to v8. diff --git a/ext/fetch/fs_fetch_handler.rs b/ext/fetch/fs_fetch_handler.rs index 6f45ee664e..4c2b81f356 100644 --- a/ext/fetch/fs_fetch_handler.rs +++ b/ext/fetch/fs_fetch_handler.rs @@ -43,7 +43,7 @@ impl FetchHandler for FsFetchHandler { Ok::<_, ()>(response) } .map_err(move |_| { - type_error("NetworkError when attempting to fetch resource.") + type_error("NetworkError when attempting to fetch resource") }) .or_cancel(&cancel_handle) .boxed_local(); diff --git a/ext/fetch/lib.rs b/ext/fetch/lib.rs index fa85824f44..a3f9eeb4fb 100644 --- a/ext/fetch/lib.rs +++ b/ext/fetch/lib.rs @@ -177,7 +177,7 @@ impl FetchHandler for DefaultFileFetchHandler { ) -> (CancelableResponseFuture, Option>) { let fut = async move { Ok(Err(type_error( - "NetworkError when attempting to fetch resource.", + "NetworkError when attempting to fetch resource", ))) }; (Box::pin(fut), None) @@ -361,14 +361,14 @@ where let (request_rid, cancel_handle_rid) = match scheme { "file" => { let path = url.to_file_path().map_err(|_| { - type_error("NetworkError when attempting to fetch resource.") + type_error("NetworkError when attempting to fetch resource") })?; let permissions = state.borrow_mut::(); permissions.check_read(&path, "fetch()")?; if method != Method::GET { return Err(type_error(format!( - "Fetching files only supports the GET method. Received {method}." + "Fetching files only supports the GET method: received {method}" ))); } @@ -394,7 +394,7 @@ where let uri = url .as_str() .parse::() - .map_err(|_| type_error("Invalid URL"))?; + .map_err(|_| type_error(format!("Invalid URL {url}")))?; let mut con_len = None; let body = if has_body { @@ -522,7 +522,9 @@ where // because the URL isn't an object URL. return Err(type_error("Blob for the given URL not found.")); } - _ => return Err(type_error(format!("scheme '{scheme}' not supported"))), + _ => { + return Err(type_error(format!("Url scheme '{scheme}' not supported"))) + } }; Ok(FetchReturn { @@ -586,7 +588,7 @@ pub async fn op_fetch_send( return Err(type_error(err.to_string())); } - Err(_) => return Err(type_error("request was cancelled")), + Err(_) => return Err(type_error("Request was cancelled")), }; let status = res.status(); @@ -1016,9 +1018,11 @@ pub fn create_http_client( let mut http_connector = HttpConnector::new(); http_connector.enforce_http(false); - let user_agent = user_agent - .parse::() - .map_err(|_| type_error("illegal characters in User-Agent"))?; + let user_agent = user_agent.parse::().map_err(|_| { + type_error(format!( + "Illegal characters in User-Agent: received {user_agent}" + )) + })?; let mut builder = hyper_util::client::legacy::Builder::new(TokioExecutor::new()); @@ -1060,7 +1064,7 @@ pub fn create_http_client( } (true, true) => {} (false, false) => { - return Err(type_error("Either `http1` or `http2` needs to be true")) + return Err(type_error("Cannot create Http Client: either `http1` or `http2` needs to be set to true")) } } diff --git a/tests/testdata/run/wasm_streaming_panic_test.js.out b/tests/testdata/run/wasm_streaming_panic_test.js.out index 8a3c68e375..4ec523f1d0 100644 --- a/tests/testdata/run/wasm_streaming_panic_test.js.out +++ b/tests/testdata/run/wasm_streaming_panic_test.js.out @@ -1,2 +1,2 @@ -error: Uncaught (in promise) TypeError: Invalid WebAssembly content type. +error: Uncaught (in promise) TypeError: Invalid WebAssembly content type at handleWasmStreaming (ext:deno_fetch/26_fetch.js:[WILDCARD]) diff --git a/tests/unit/cache_api_test.ts b/tests/unit/cache_api_test.ts index 792929870d..08f768e334 100644 --- a/tests/unit/cache_api_test.ts +++ b/tests/unit/cache_api_test.ts @@ -118,7 +118,7 @@ Deno.test(async function cachePutReaderLock() { await response.arrayBuffer(); }, TypeError, - "Body already consumed.", + "Body already consumed", ); await promise; diff --git a/tests/unit/fetch_test.ts b/tests/unit/fetch_test.ts index 721b6912d5..35d5e563f4 100644 --- a/tests/unit/fetch_test.ts +++ b/tests/unit/fetch_test.ts @@ -1131,7 +1131,7 @@ Deno.test(function fetchResponseConstructorInvalidStatus() { assert(e instanceof RangeError); assert( e.message.endsWith( - "is not equal to 101 and outside the range [200, 599].", + "is not equal to 101 and outside the range [200, 599]", ), ); } @@ -1662,7 +1662,7 @@ Deno.test( ); }, TypeError, - "Fetching files only supports the GET method. Received POST.", + "Fetching files only supports the GET method: received POST", ); }, ); diff --git a/tests/unit/headers_test.ts b/tests/unit/headers_test.ts index ad453b67f6..ea72f784b5 100644 --- a/tests/unit/headers_test.ts +++ b/tests/unit/headers_test.ts @@ -406,11 +406,11 @@ Deno.test(function invalidHeadersFlaky() { assertThrows( () => new Headers([["x", "\u0000x"]]), TypeError, - "Header value is not valid.", + 'Invalid header value: "\u0000x"', ); assertThrows( () => new Headers([["x", "\u0000x"]]), TypeError, - "Header value is not valid.", + 'Invalid header value: "\u0000x"', ); }); diff --git a/tests/unit/wasm_test.ts b/tests/unit/wasm_test.ts index fab9c9308f..e0db41ed0e 100644 --- a/tests/unit/wasm_test.ts +++ b/tests/unit/wasm_test.ts @@ -53,7 +53,7 @@ Deno.test( await assertRejects( () => wasmPromise, TypeError, - "Invalid WebAssembly content type.", + "Invalid WebAssembly content type", ); }, ); From 072bf5d379b691c4decd3c167a51801bad8ca2f6 Mon Sep 17 00:00:00 2001 From: Asher Gomez Date: Wed, 4 Sep 2024 17:16:48 +1000 Subject: [PATCH 13/45] BREAKING(buffer): remove `Deno.writeAll[Sync]()` (#25407) --- cli/tsc/dts/lib.deno.ns.d.ts | 23 ---------------------- runtime/js/13_buffer.js | 26 +------------------------ runtime/js/90_deno_ns.js | 2 -- runtime/js/99_main.js | 4 ---- tests/specs/future/runtime_api/main.js | 2 -- tests/specs/future/runtime_api/main.out | 2 -- tests/unit/buffer_test.ts | 24 ----------------------- 7 files changed, 1 insertion(+), 82 deletions(-) diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index 0b7ac7cc9d..8ba41eae01 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -2993,29 +2993,6 @@ declare namespace Deno { */ export function readAllSync(r: ReaderSync): Uint8Array; - /** - * Write all the content of the array buffer (`arr`) to the writer (`w`). - * - * @deprecated This will be removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @category I/O - */ - export function writeAll(w: Writer, arr: Uint8Array): Promise; - - /** - * Synchronously write all the content of the array buffer (`arr`) to the - * writer (`w`). - * - * @deprecated This will be removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @category I/O - */ - export function writeAllSync(w: WriterSync, arr: Uint8Array): void; - /** * Options which can be set when using {@linkcode Deno.mkdir} and * {@linkcode Deno.mkdirSync}. diff --git a/runtime/js/13_buffer.js b/runtime/js/13_buffer.js index ae57fcb163..2dbe5bbf60 100644 --- a/runtime/js/13_buffer.js +++ b/runtime/js/13_buffer.js @@ -256,28 +256,4 @@ function readAllSync(r) { return buf.bytes(); } -async function writeAll(w, arr) { - internals.warnOnDeprecatedApi( - "Deno.writeAll()", - new Error().stack, - "Use `writeAll()` from `https://jsr.io/@std/io/doc/write-all/~` instead.", - ); - let nwritten = 0; - while (nwritten < arr.length) { - nwritten += await w.write(TypedArrayPrototypeSubarray(arr, nwritten)); - } -} - -function writeAllSync(w, arr) { - internals.warnOnDeprecatedApi( - "Deno.writeAllSync()", - new Error().stack, - "Use `writeAllSync()` from `https://jsr.io/@std/io/doc/write-all/~` instead.", - ); - let nwritten = 0; - while (nwritten < arr.length) { - nwritten += w.writeSync(TypedArrayPrototypeSubarray(arr, nwritten)); - } -} - -export { Buffer, readAll, readAllSync, writeAll, writeAllSync }; +export { Buffer, readAll, readAllSync }; diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index 3fdab78003..f453563eae 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -101,8 +101,6 @@ const denoNs = { Buffer: buffer.Buffer, readAll: buffer.readAll, readAllSync: buffer.readAllSync, - writeAll: buffer.writeAll, - writeAllSync: buffer.writeAllSync, copy: io.copy, SeekMode: io.SeekMode, read(rid, buffer) { diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index a305614335..ba010e368f 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -813,8 +813,6 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) { delete Deno.readSync; delete Deno.seek; delete Deno.seekSync; - delete Deno.writeAll; - delete Deno.writeAllSync; delete Deno.write; delete Deno.writeSync; } @@ -988,8 +986,6 @@ function bootstrapWorkerRuntime( delete Deno.readSync; delete Deno.seek; delete Deno.seekSync; - delete Deno.writeAll; - delete Deno.writeAllSync; delete Deno.write; delete Deno.writeSync; } diff --git a/tests/specs/future/runtime_api/main.js b/tests/specs/future/runtime_api/main.js index 4e22716f02..a2c5303243 100644 --- a/tests/specs/future/runtime_api/main.js +++ b/tests/specs/future/runtime_api/main.js @@ -15,8 +15,6 @@ console.log("Deno.read is", Deno.read); console.log("Deno.readSync is", Deno.readSync); console.log("Deno.seek is", Deno.seek); console.log("Deno.seekSync is", Deno.seekSync); -console.log("Deno.writeAll is", Deno.writeAll); -console.log("Deno.writeAllSync is", Deno.writeAllSync); console.log("Deno.write is", Deno.write); console.log("Deno.writeSync is", Deno.writeSync); diff --git a/tests/specs/future/runtime_api/main.out b/tests/specs/future/runtime_api/main.out index 922f4afad3..8175777e11 100644 --- a/tests/specs/future/runtime_api/main.out +++ b/tests/specs/future/runtime_api/main.out @@ -12,8 +12,6 @@ Deno.read is undefined Deno.readSync is undefined Deno.seek is undefined Deno.seekSync is undefined -Deno.writeAll is undefined -Deno.writeAllSync is undefined Deno.write is undefined Deno.writeSync is undefined Deno.Listener.prototype.rid is undefined diff --git a/tests/unit/buffer_test.ts b/tests/unit/buffer_test.ts index 2295aa5ae4..5b788a45c7 100644 --- a/tests/unit/buffer_test.ts +++ b/tests/unit/buffer_test.ts @@ -376,30 +376,6 @@ Deno.test({ ignore: DENO_FUTURE }, function testReadAllSync() { } }); -Deno.test({ ignore: DENO_FUTURE }, async function testWriteAll() { - init(); - assert(testBytes); - const writer = new Deno.Buffer(); - await Deno.writeAll(writer, testBytes); - const actualBytes = writer.bytes(); - assertEquals(testBytes.byteLength, actualBytes.byteLength); - for (let i = 0; i < testBytes.length; ++i) { - assertEquals(testBytes[i], actualBytes[i]); - } -}); - -Deno.test({ ignore: DENO_FUTURE }, function testWriteAllSync() { - init(); - assert(testBytes); - const writer = new Deno.Buffer(); - Deno.writeAllSync(writer, testBytes); - const actualBytes = writer.bytes(); - assertEquals(testBytes.byteLength, actualBytes.byteLength); - for (let i = 0; i < testBytes.length; ++i) { - assertEquals(testBytes[i], actualBytes[i]); - } -}); - Deno.test({ ignore: DENO_FUTURE }, function testBufferBytesArrayBufferLength() { // defaults to copy const args = [{}, { copy: undefined }, undefined, { copy: true }]; From 939c23f9fe332797f4b2c8a80359fd36a3e7c105 Mon Sep 17 00:00:00 2001 From: Ian Bull Date: Wed, 4 Sep 2024 00:19:55 -0700 Subject: [PATCH 14/45] refactor(cli/js): align error messages (#25406) Aligns the error messages in the cli/js folder to be in-line with the Deno style guide. --- cli/js/40_bench.js | 16 ++++++++-------- cli/js/40_test.js | 16 ++++++++-------- .../explicit_start_and_end.out | 6 +++--- tests/specs/test/exit_code/main.out | 2 +- tests/specs/test/exit_code2/main.out | 2 +- tests/specs/test/exit_code3/main.out | 2 +- .../testdata/bench/explicit_start_and_end.out | 6 +++--- tests/unit/testing_test.ts | 18 +++++++++--------- 8 files changed, 34 insertions(+), 34 deletions(-) diff --git a/cli/js/40_bench.js b/cli/js/40_bench.js index a94c782fc5..b07df3993c 100644 --- a/cli/js/40_bench.js +++ b/cli/js/40_bench.js @@ -104,12 +104,12 @@ function bench( } if (optionsOrFn.fn != undefined) { throw new TypeError( - "Unexpected 'fn' field in options, bench function is already provided as the third argument.", + "Unexpected 'fn' field in options, bench function is already provided as the third argument", ); } if (optionsOrFn.name != undefined) { throw new TypeError( - "Unexpected 'name' field in options, bench name is already provided as the first argument.", + "Unexpected 'name' field in options, bench name is already provided as the first argument", ); } benchDesc = { @@ -141,7 +141,7 @@ function bench( fn = optionsOrFn; if (nameOrFnOrOptions.fn != undefined) { throw new TypeError( - "Unexpected 'fn' field in options, bench function is already provided as the second argument.", + "Unexpected 'fn' field in options, bench function is already provided as the second argument", ); } name = nameOrFnOrOptions.name ?? fn.name; @@ -150,7 +150,7 @@ function bench( !nameOrFnOrOptions.fn || typeof nameOrFnOrOptions.fn !== "function" ) { throw new TypeError( - "Expected 'fn' field in the first argument to be a bench function.", + "Expected 'fn' field in the first argument to be a bench function", ); } fn = nameOrFnOrOptions.fn; @@ -385,12 +385,12 @@ function createBenchContext(desc) { start() { if (currentBenchId !== desc.id) { throw new TypeError( - "The benchmark which this context belongs to is not being executed.", + "The benchmark which this context belongs to is not being executed", ); } if (currentBenchUserExplicitStart != null) { throw new TypeError( - "BenchContext::start() has already been invoked.", + "BenchContext::start() has already been invoked", ); } currentBenchUserExplicitStart = benchNow(); @@ -399,11 +399,11 @@ function createBenchContext(desc) { const end = benchNow(); if (currentBenchId !== desc.id) { throw new TypeError( - "The benchmark which this context belongs to is not being executed.", + "The benchmark which this context belongs to is not being executed", ); } if (currentBenchUserExplicitEnd != null) { - throw new TypeError("BenchContext::end() has already been invoked."); + throw new TypeError("BenchContext::end() has already been invoked"); } currentBenchUserExplicitEnd = end; }, diff --git a/cli/js/40_test.js b/cli/js/40_test.js index 5a081e2175..ea526a17d4 100644 --- a/cli/js/40_test.js +++ b/cli/js/40_test.js @@ -113,7 +113,7 @@ function assertExit(fn, isTest) { throw new Error( `${ isTest ? "Test case" : "Bench" - } finished with exit code set to ${exitCode}.`, + } finished with exit code set to ${exitCode}`, ); } if (innerResult) { @@ -242,12 +242,12 @@ function testInner( } if (optionsOrFn.fn != undefined) { throw new TypeError( - "Unexpected 'fn' field in options, test function is already provided as the third argument.", + "Unexpected 'fn' field in options, test function is already provided as the third argument", ); } if (optionsOrFn.name != undefined) { throw new TypeError( - "Unexpected 'name' field in options, test name is already provided as the first argument.", + "Unexpected 'name' field in options, test name is already provided as the first argument", ); } testDesc = { @@ -279,7 +279,7 @@ function testInner( fn = optionsOrFn; if (nameOrFnOrOptions.fn != undefined) { throw new TypeError( - "Unexpected 'fn' field in options, test function is already provided as the second argument.", + "Unexpected 'fn' field in options, test function is already provided as the second argument", ); } name = nameOrFnOrOptions.name ?? fn.name; @@ -288,7 +288,7 @@ function testInner( !nameOrFnOrOptions.fn || typeof nameOrFnOrOptions.fn !== "function" ) { throw new TypeError( - "Expected 'fn' field in the first argument to be a test function.", + "Expected 'fn' field in the first argument to be a test function", ); } fn = nameOrFnOrOptions.fn; @@ -426,7 +426,7 @@ function createTestContext(desc) { let stepDesc; if (typeof nameOrFnOrOptions === "string") { if (typeof maybeFn !== "function") { - throw new TypeError("Expected function for second argument."); + throw new TypeError("Expected function for second argument"); } stepDesc = { name: nameOrFnOrOptions, @@ -434,7 +434,7 @@ function createTestContext(desc) { }; } else if (typeof nameOrFnOrOptions === "function") { if (!nameOrFnOrOptions.name) { - throw new TypeError("The step function must have a name."); + throw new TypeError("The step function must have a name"); } if (maybeFn != undefined) { throw new TypeError( @@ -449,7 +449,7 @@ function createTestContext(desc) { stepDesc = nameOrFnOrOptions; } else { throw new TypeError( - "Expected a test definition or name and function.", + "Expected a test definition or name and function", ); } stepDesc.ignore ??= false; diff --git a/tests/specs/bench/bench_explicit_start_end/explicit_start_and_end.out b/tests/specs/bench/bench_explicit_start_end/explicit_start_and_end.out index 4dc2b6179f..ae1b058475 100644 --- a/tests/specs/bench/bench_explicit_start_end/explicit_start_and_end.out +++ b/tests/specs/bench/bench_explicit_start_end/explicit_start_and_end.out @@ -8,17 +8,17 @@ benchmark time/iter (avg) iter/s (min … max) p75 start and end [WILDCARD] [WILDCARD] [WILDCARD] ([WILDCARD] … [WILDCARD]) [WILDCARD] start only [WILDCARD] [WILDCARD] [WILDCARD] ([WILDCARD] … [WILDCARD]) [WILDCARD] end only [WILDCARD] [WILDCARD] [WILDCARD] ([WILDCARD] … [WILDCARD]) [WILDCARD] -double start error: TypeError: BenchContext::start() has already been invoked. +double start error: TypeError: BenchContext::start() has already been invoked t.start(); ^ at BenchContext.start ([WILDCARD]) at [WILDCARD]/explicit_start_and_end.ts:[WILDCARD] -double end error: TypeError: BenchContext::end() has already been invoked. +double end error: TypeError: BenchContext::end() has already been invoked t.end(); ^ at BenchContext.end ([WILDCARD]) at [WILDCARD]/explicit_start_and_end.ts:[WILDCARD] -captured error: TypeError: The benchmark which this context belongs to is not being executed. +captured error: TypeError: The benchmark which this context belongs to is not being executed captured!.start(); ^ at BenchContext.start ([WILDCARD]) diff --git a/tests/specs/test/exit_code/main.out b/tests/specs/test/exit_code/main.out index 2562695a02..d5fe6c4751 100644 --- a/tests/specs/test/exit_code/main.out +++ b/tests/specs/test/exit_code/main.out @@ -4,7 +4,7 @@ Deno.exitCode ... FAILED ([WILDCARD]) ERRORS Deno.exitCode => ./main.js:1:6 -error: Error: Test case finished with exit code set to 42. +error: Error: Test case finished with exit code set to 42 at exitSanitizer (ext:cli/40_test.js:113:15) at async outerWrapped (ext:cli/40_test.js:134:14) diff --git a/tests/specs/test/exit_code2/main.out b/tests/specs/test/exit_code2/main.out index adc9cb5775..4943846520 100644 --- a/tests/specs/test/exit_code2/main.out +++ b/tests/specs/test/exit_code2/main.out @@ -11,7 +11,7 @@ error: Error at [WILDCARD]/exit_code2/main.js:3:9 success => ./main.js:6:6 -error: Error: Test case finished with exit code set to 5. +error: Error: Test case finished with exit code set to 5 at exitSanitizer (ext:cli/40_test.js:113:15) at async outerWrapped (ext:cli/40_test.js:134:14) diff --git a/tests/specs/test/exit_code3/main.out b/tests/specs/test/exit_code3/main.out index 6e333bf425..a461db2f6f 100644 --- a/tests/specs/test/exit_code3/main.out +++ b/tests/specs/test/exit_code3/main.out @@ -5,7 +5,7 @@ success ... ok ([WILDCARD]) ERRORS Deno.exitCode => ./main.js:1:6 -error: Error: Test case finished with exit code set to 42. +error: Error: Test case finished with exit code set to 42 at exitSanitizer (ext:cli/40_test.js:113:15) at async outerWrapped (ext:cli/40_test.js:134:14) diff --git a/tests/testdata/bench/explicit_start_and_end.out b/tests/testdata/bench/explicit_start_and_end.out index 7248464e92..6759d39cad 100644 --- a/tests/testdata/bench/explicit_start_and_end.out +++ b/tests/testdata/bench/explicit_start_and_end.out @@ -8,17 +8,17 @@ benchmark time/iter (avg) iter/s (min … max) p75 start and end [WILDCARD] [WILDCARD] [WILDCARD] ([WILDCARD] … [WILDCARD]) [WILDCARD] start only [WILDCARD] [WILDCARD] [WILDCARD] ([WILDCARD] … [WILDCARD]) [WILDCARD] end only [WILDCARD] [WILDCARD] [WILDCARD] ([WILDCARD] … [WILDCARD]) [WILDCARD] -double start error: Type error: BenchContext::start() has already been invoked. +double start error: Type error: BenchContext::start() has already been invoked t.start(); ^ at BenchContext.start ([WILDCARD]) at [WILDCARD]/explicit_start_and_end.ts:[WILDCARD] -double end error: Type error: BenchContext::end() has already been invoked. +double end error: Type error: BenchContext::end() has already been invoked t.end(); ^ at BenchContext.end ([WILDCARD]) at [WILDCARD]/explicit_start_and_end.ts:[WILDCARD] -captured error: Type error: The benchmark which this context belongs to is not being executed. +captured error: Type error: The benchmark which this context belongs to is not being executed captured!.start(); ^ at BenchContext.start ([WILDCARD]) diff --git a/tests/unit/testing_test.ts b/tests/unit/testing_test.ts index e04ab921c7..51372c42b0 100644 --- a/tests/unit/testing_test.ts +++ b/tests/unit/testing_test.ts @@ -8,7 +8,7 @@ Deno.test(function testWrongOverloads() { Deno.test("some name", { fn: () => {} }, () => {}); }, TypeError, - "Unexpected 'fn' field in options, test function is already provided as the third argument.", + "Unexpected 'fn' field in options, test function is already provided as the third argument", ); assertThrows( () => { @@ -16,7 +16,7 @@ Deno.test(function testWrongOverloads() { Deno.test("some name", { name: "some name2" }, () => {}); }, TypeError, - "Unexpected 'name' field in options, test name is already provided as the first argument.", + "Unexpected 'name' field in options, test name is already provided as the first argument", ); assertThrows( () => { @@ -40,7 +40,7 @@ Deno.test(function testWrongOverloads() { Deno.test({ fn: () => {} }, function foo() {}); }, TypeError, - "Unexpected 'fn' field in options, test function is already provided as the second argument.", + "Unexpected 'fn' field in options, test function is already provided as the second argument", ); assertThrows( () => { @@ -48,7 +48,7 @@ Deno.test(function testWrongOverloads() { Deno.test({}); }, TypeError, - "Expected 'fn' field in the first argument to be a test function.", + "Expected 'fn' field in the first argument to be a test function", ); assertThrows( () => { @@ -56,7 +56,7 @@ Deno.test(function testWrongOverloads() { Deno.test({ fn: "boo!" }); }, TypeError, - "Expected 'fn' field in the first argument to be a test function.", + "Expected 'fn' field in the first argument to be a test function", ); }); @@ -87,7 +87,7 @@ Deno.test(async function invalidStepArguments(t) { await (t as any).step("test"); }, TypeError, - "Expected function for second argument.", + "Expected function for second argument", ); await assertRejects( @@ -96,7 +96,7 @@ Deno.test(async function invalidStepArguments(t) { await (t as any).step("test", "not a function"); }, TypeError, - "Expected function for second argument.", + "Expected function for second argument", ); await assertRejects( @@ -105,7 +105,7 @@ Deno.test(async function invalidStepArguments(t) { await (t as any).step(); }, TypeError, - "Expected a test definition or name and function.", + "Expected a test definition or name and function", ); await assertRejects( @@ -114,7 +114,7 @@ Deno.test(async function invalidStepArguments(t) { await (t as any).step(() => {}); }, TypeError, - "The step function must have a name.", + "The step function must have a name", ); }); From 1ec911f1734b5e7190cc65876c5f03011e592d7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Sep 2024 09:20:46 +0200 Subject: [PATCH 15/45] build(deps): bump quinn-proto from 0.11.3 to 0.11.8 (#25400) --- Cargo.lock | 54 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 195f55069c..dcdcca3141 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -465,7 +465,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", "syn 2.0.72", "which 4.4.2", @@ -2475,7 +2475,7 @@ dependencies = [ "bumpalo", "hashbrown", "indexmap", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "unicode-width", ] @@ -2543,7 +2543,7 @@ dependencies = [ "dprint-core", "dprint-core-macros", "percent-encoding", - "rustc-hash", + "rustc-hash 1.1.0", "serde", ] @@ -2556,7 +2556,7 @@ dependencies = [ "allocator-api2", "bumpalo", "num-bigint", - "rustc-hash", + "rustc-hash 1.1.0", "swc_atoms", "swc_common", "swc_ecma_ast", @@ -3551,7 +3551,7 @@ dependencies = [ "new_debug_unreachable", "once_cell", "phf 0.11.2", - "rustc-hash", + "rustc-hash 1.1.0", "triomphe", ] @@ -4208,7 +4208,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.52.4", + "windows-targets 0.48.5", ] [[package]] @@ -4523,7 +4523,7 @@ dependencies = [ "indexmap", "log", "num-traits", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "spirv", "termcolor", @@ -5534,7 +5534,7 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash", + "rustc-hash 1.1.0", "rustls", "thiserror", "tokio", @@ -5543,14 +5543,14 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.3" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" dependencies = [ "bytes", "rand", "ring", - "rustc-hash", + "rustc-hash 2.0.0", "rustls", "slab", "thiserror", @@ -5881,7 +5881,7 @@ dependencies = [ "countme", "hashbrown", "memoffset 0.9.1", - "rustc-hash", + "rustc-hash 1.1.0", "text-size", ] @@ -5955,6 +5955,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustc_version" version = "0.2.3" @@ -6546,7 +6552,7 @@ dependencies = [ "data-encoding", "debugid", "if_chain", - "rustc-hash", + "rustc-hash 1.1.0", "rustc_version 0.2.3", "serde", "serde_json", @@ -6565,7 +6571,7 @@ dependencies = [ "data-encoding", "debugid", "if_chain", - "rustc-hash", + "rustc-hash 1.1.0", "rustc_version 0.2.3", "serde", "serde_json", @@ -6713,7 +6719,7 @@ dependencies = [ "bumpalo", "hashbrown", "ptr_meta", - "rustc-hash", + "rustc-hash 1.1.0", "triomphe", ] @@ -6725,7 +6731,7 @@ checksum = "bb6567e4e67485b3e7662b486f1565bdae54bd5b9d6b16b2ba1a9babb1e42125" dependencies = [ "hstr", "once_cell", - "rustc-hash", + "rustc-hash 1.1.0", "serde", ] @@ -6787,7 +6793,7 @@ dependencies = [ "new_debug_unreachable", "num-bigint", "once_cell", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "siphasher", "sourcemap 9.0.0", @@ -6922,7 +6928,7 @@ dependencies = [ "indexmap", "once_cell", "phf 0.11.2", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "smallvec", "swc_atoms", @@ -6970,7 +6976,7 @@ dependencies = [ "indexmap", "once_cell", "petgraph", - "rustc-hash", + "rustc-hash 1.1.0", "serde_json", "swc_atoms", "swc_common", @@ -6991,7 +6997,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79938ff510fc647febd8c6c3ef4143d099fdad87a223680e632623d056dae2dd" dependencies = [ "either", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "smallvec", "swc_atoms", @@ -7055,7 +7061,7 @@ dependencies = [ "indexmap", "num_cpus", "once_cell", - "rustc-hash", + "rustc-hash 1.1.0", "ryu-js", "swc_atoms", "swc_common", @@ -7099,7 +7105,7 @@ checksum = "357e2c97bb51431d65080f25b436bc4e2fc1a7f64a643bc21a8353e478dc799f" dependencies = [ "indexmap", "petgraph", - "rustc-hash", + "rustc-hash 1.1.0", "swc_common", ] @@ -8205,7 +8211,7 @@ dependencies = [ "profiling", "raw-window-handle", "ron", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "smallvec", "thiserror", @@ -8247,7 +8253,7 @@ dependencies = [ "profiling", "range-alloc", "raw-window-handle", - "rustc-hash", + "rustc-hash 1.1.0", "smallvec", "thiserror", "wasm-bindgen", From cb454351d497a4387775f3c9d64114cb8b32c08d Mon Sep 17 00:00:00 2001 From: Ian Bull Date: Wed, 4 Sep 2024 00:23:19 -0700 Subject: [PATCH 16/45] refactor(ext): align error messages (#25310) --- ext/cache/01_cache.js | 2 +- ext/cache/lib.rs | 2 +- ext/canvas/01_image.js | 4 +++- ext/ffi/00_ffi.js | 12 +++++++----- ext/ffi/call.rs | 4 +++- ext/ffi/callback.rs | 2 +- tests/ffi/tests/integration_tests.rs | 2 +- 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/ext/cache/01_cache.js b/ext/cache/01_cache.js index a5bcde598f..269261f401 100644 --- a/ext/cache/01_cache.js +++ b/ext/cache/01_cache.js @@ -105,7 +105,7 @@ class Cache { const reqUrl = new URL(innerRequest.url()); if (reqUrl.protocol !== "http:" && reqUrl.protocol !== "https:") { throw new TypeError( - "Request url protocol must be 'http:' or 'https:'", + `Request url protocol must be 'http:' or 'https:': received '${reqUrl.protocol}'`, ); } if (innerRequest.method !== "GET") { diff --git a/ext/cache/lib.rs b/ext/cache/lib.rs index e399b08e07..f6d758b95c 100644 --- a/ext/cache/lib.rs +++ b/ext/cache/lib.rs @@ -211,7 +211,7 @@ where state.put(cache); Ok(state.borrow::().clone()) } else { - Err(type_error("CacheStorage is not available in this context.")) + Err(type_error("CacheStorage is not available in this context")) } } diff --git a/ext/canvas/01_image.js b/ext/canvas/01_image.js index 6fb1ee62fc..3ea72db6ac 100644 --- a/ext/canvas/01_image.js +++ b/ext/canvas/01_image.js @@ -307,7 +307,9 @@ function processImage(input, width, height, sx, sy, sw, sh, options) { } if (options.colorSpaceConversion === "none") { - throw new TypeError("options.colorSpaceConversion 'none' is not supported"); + throw new TypeError( + "Cannot create image: invalid colorSpaceConversion option, 'none' is not supported", + ); } /* diff --git a/ext/ffi/00_ffi.js b/ext/ffi/00_ffi.js index 06caf7c6cc..1475f8d3fc 100644 --- a/ext/ffi/00_ffi.js +++ b/ext/ffi/00_ffi.js @@ -250,7 +250,7 @@ class UnsafePointer { } } else { throw new TypeError( - "Expected ArrayBuffer, ArrayBufferView or UnsafeCallbackPrototype", + `Cannot access pointer: expected 'ArrayBuffer', 'ArrayBufferView' or 'UnsafeCallbackPrototype', received ${typeof value}`, ); } if (pointer) { @@ -335,7 +335,9 @@ function getTypeSizeAndAlignment(type, cache = new SafeMap()) { const cached = cache.get(type); if (cached !== undefined) { if (cached === null) { - throw new TypeError("Recursive struct definition"); + throw new TypeError( + "Cannot get pointer size: found recursive struct", + ); } return cached; } @@ -379,7 +381,7 @@ function getTypeSizeAndAlignment(type, cache = new SafeMap()) { case "isize": return [8, 8]; default: - throw new TypeError(`Unsupported type: ${type}`); + throw new TypeError(`Cannot get pointer size, unsupported type: ${type}`); } } @@ -395,7 +397,7 @@ class UnsafeCallback { constructor(definition, callback) { if (definition.nonblocking) { throw new TypeError( - "Invalid UnsafeCallback, cannot be nonblocking", + "Cannot construct UnsafeCallback: cannot be nonblocking", ); } const { 0: rid, 1: pointer } = op_ffi_unsafe_callback_create( @@ -467,7 +469,7 @@ class DynamicLibrary { const type = symbols[symbol].type; if (type === "void") { throw new TypeError( - "Foreign symbol of type 'void' is not supported.", + "Foreign symbol of type 'void' is not supported", ); } diff --git a/ext/ffi/call.rs b/ext/ffi/call.rs index 726f17fad0..9165972935 100644 --- a/ext/ffi/call.rs +++ b/ext/ffi/call.rs @@ -334,7 +334,9 @@ pub fn op_ffi_call_nonblocking( let symbols = &resource.symbols; *symbols .get(&symbol) - .ok_or_else(|| type_error("Invalid FFI symbol name"))? + .ok_or_else(|| { + type_error(format!("Invalid FFI symbol name: '{symbol}'")) + })? .clone() }; diff --git a/ext/ffi/callback.rs b/ext/ffi/callback.rs index 39321aa846..281208322b 100644 --- a/ext/ffi/callback.rs +++ b/ext/ffi/callback.rs @@ -174,7 +174,7 @@ unsafe extern "C" fn deno_ffi_callback( let tc_scope = &mut TryCatch::new(scope); args.run(tc_scope); if tc_scope.exception().is_some() { - log::error!("Illegal unhandled exception in nonblocking callback."); + log::error!("Illegal unhandled exception in nonblocking callback"); } }); } diff --git a/tests/ffi/tests/integration_tests.rs b/tests/ffi/tests/integration_tests.rs index 2235405a8a..c84a1b820d 100644 --- a/tests/ffi/tests/integration_tests.rs +++ b/tests/ffi/tests/integration_tests.rs @@ -300,6 +300,6 @@ fn ffi_callback_errors_test() { assert_eq!(stdout, expected); assert_eq!( stderr, - "Illegal unhandled exception in nonblocking callback.\n".repeat(3) + "Illegal unhandled exception in nonblocking callback\n".repeat(3) ); } From ac33fc2892f8faf9484c31c2aabfdc3744de0314 Mon Sep 17 00:00:00 2001 From: Asher Gomez Date: Wed, 4 Sep 2024 18:12:11 +1000 Subject: [PATCH 17/45] chore(tty): soft-remove `Deno.isatty()` (#25410) Towards #22079 --- cli/tsc/dts/lib.deno.ns.d.ts | 19 ------------------- runtime/js/40_tty.js | 9 +++------ tests/unit/tty_test.ts | 2 ++ 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index 8ba41eae01..e861b876eb 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -2875,25 +2875,6 @@ declare namespace Deno { signal?: AbortSignal; } - /** - * Check if a given resource id (`rid`) is a TTY (a terminal). - * - * ```ts - * // This example is system and context specific - * const nonTTYRid = Deno.openSync("my_file.txt").rid; - * const ttyRid = Deno.openSync("/dev/tty6").rid; - * console.log(Deno.isatty(nonTTYRid)); // false - * console.log(Deno.isatty(ttyRid)); // true - * ``` - * - * @deprecated This will be soft-removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @category I/O - */ - export function isatty(rid: number): boolean; - /** * A variable-sized buffer of bytes with `read()` and `write()` methods. * diff --git a/runtime/js/40_tty.js b/runtime/js/40_tty.js index 7cf1e4f5d9..72e7b68846 100644 --- a/runtime/js/40_tty.js +++ b/runtime/js/40_tty.js @@ -1,5 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import { core, internals, primordials } from "ext:core/mod.js"; +import { core, primordials } from "ext:core/mod.js"; import { op_console_size } from "ext:core/ops"; const { Uint32Array, @@ -15,12 +15,9 @@ function consoleSize() { return { columns: size[0], rows: size[1] }; } +// Note: This function was soft-removed in Deno 2. Its types have been removed, +// but its implementation has been kept to avoid breaking changes. function isatty(rid) { - internals.warnOnDeprecatedApi( - "Deno.isatty()", - new Error().stack, - "Use `Deno.stdin.isTerminal()`, `Deno.stdout.isTerminal()`, `Deno.stderr.isTerminal()` or `Deno.FsFile.isTerminal()` instead.", - ); return isTerminal(rid); } diff --git a/tests/unit/tty_test.ts b/tests/unit/tty_test.ts index 35e7dd7831..4183fe530e 100644 --- a/tests/unit/tty_test.ts +++ b/tests/unit/tty_test.ts @@ -20,6 +20,7 @@ Deno.test( function isatty() { // CI not under TTY, so cannot test stdin/stdout/stderr. const f = Deno.openSync("tests/testdata/assets/hello.txt"); + // @ts-ignore `Deno.isatty()` was soft-removed in Deno 2. assert(!Deno.isatty(f.rid)); f.close(); }, @@ -29,6 +30,7 @@ Deno.test(function isattyError() { let caught = false; try { // Absurdly large rid. + // @ts-ignore `Deno.isatty()` was soft-removed in Deno 2. Deno.isatty(0x7fffffff); } catch (e) { caught = true; From 3d36cbd056292e0a89d282fa84b7198ae38be4cc Mon Sep 17 00:00:00 2001 From: Asher Gomez Date: Wed, 4 Sep 2024 18:53:43 +1000 Subject: [PATCH 18/45] BREAKING(fs): remove `Deno.ftruncate[Sync]()` (#25412) Towards #22079 Signed-off-by: Asher Gomez --- cli/tools/test/fmt.rs | 2 +- cli/tsc/dts/lib.deno.ns.d.ts | 91 ------------------------- ext/fs/30_fs.js | 16 +---- ext/fs/lib.rs | 2 +- ext/fs/ops.rs | 2 +- runtime/js/90_deno_ns.js | 16 ----- runtime/js/99_main.js | 4 -- tests/specs/future/runtime_api/main.js | 2 - tests/specs/future/runtime_api/main.out | 2 - 9 files changed, 6 insertions(+), 131 deletions(-) diff --git a/cli/tools/test/fmt.rs b/cli/tools/test/fmt.rs index bfaac0616e..5201bead65 100644 --- a/cli/tools/test/fmt.rs +++ b/cli/tools/test/fmt.rs @@ -322,7 +322,7 @@ pub const OP_DETAILS: phf::Map<&'static str, [&'static str; 2]> = phf_map! { "op_fs_file_stat_async" => ["get file metadata", "awaiting the result of a `Deno.FsFile.prototype.stat` call"], "op_fs_flock_async" => ["lock a file", "awaiting the result of a `Deno.FsFile.lock` call"], "op_fs_fsync_async" => ["flush pending data operations for a file to disk", "awaiting the result of a `Deno.fsync` or `Deno.FsFile.sync` call"], - "op_fs_ftruncate_async" => ["truncate a file", "awaiting the result of a `Deno.ftruncate` or `Deno.FsFile.truncate` call"], + "op_fs_file_truncate_async" => ["truncate a file", "awaiting the result of a `Deno.FsFile.prototype.truncate` call"], "op_fs_funlock_async_unstable" => ["unlock a file", "awaiting the result of a `Deno.funlock` call"], "op_fs_funlock_async" => ["unlock a file", "awaiting the result of a `Deno.FsFile.unlock` call"], "op_fs_link_async" => ["create a hard link", "awaiting the result of a `Deno.link` call"], diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index e861b876eb..d118aca6e2 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -5193,97 +5193,6 @@ declare namespace Deno { options?: SymlinkOptions, ): void; - /** - * Truncates or extends the specified file stream, to reach the specified - * `len`. - * - * If `len` is not specified then the entire file contents are truncated as if - * `len` was set to `0`. - * - * If the file previously was larger than this new length, the extra data is - * lost. - * - * If the file previously was shorter, it is extended, and the extended part - * reads as null bytes ('\0'). - * - * ### Truncate the entire file - * - * ```ts - * const file = await Deno.open( - * "my_file.txt", - * { read: true, write: true, create: true } - * ); - * await Deno.ftruncate(file.rid); - * ``` - * - * ### Truncate part of the file - * - * ```ts - * const file = await Deno.open( - * "my_file.txt", - * { read: true, write: true, create: true } - * ); - * await file.write(new TextEncoder().encode("Hello World")); - * await Deno.ftruncate(file.rid, 7); - * const data = new Uint8Array(32); - * await Deno.read(file.rid, data); - * console.log(new TextDecoder().decode(data)); // Hello W - * ``` - * - * @deprecated This will be removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @category File System - */ - export function ftruncate(rid: number, len?: number): Promise; - - /** - * Synchronously truncates or extends the specified file stream, to reach the - * specified `len`. - * - * If `len` is not specified then the entire file contents are truncated as if - * `len` was set to `0`. - * - * If the file previously was larger than this new length, the extra data is - * lost. - * - * If the file previously was shorter, it is extended, and the extended part - * reads as null bytes ('\0'). - * - * ### Truncate the entire file - * - * ```ts - * const file = Deno.openSync( - * "my_file.txt", - * { read: true, write: true, truncate: true, create: true } - * ); - * Deno.ftruncateSync(file.rid); - * ``` - * - * ### Truncate part of the file - * - * ```ts - * const file = Deno.openSync( - * "my_file.txt", - * { read: true, write: true, create: true } - * ); - * file.writeSync(new TextEncoder().encode("Hello World")); - * Deno.ftruncateSync(file.rid, 7); - * Deno.seekSync(file.rid, 0, Deno.SeekMode.Start); - * const data = new Uint8Array(32); - * Deno.readSync(file.rid, data); - * console.log(new TextDecoder().decode(data)); // Hello W - * ``` - * - * @deprecated This will be removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @category File System - */ - export function ftruncateSync(rid: number, len?: number): void; - /** * Synchronously changes the access (`atime`) and modification (`mtime`) times * of a file system object referenced by `path`. Given times are either in diff --git a/ext/fs/30_fs.js b/ext/fs/30_fs.js index 87607ca28c..35bf0e712e 100644 --- a/ext/fs/30_fs.js +++ b/ext/fs/30_fs.js @@ -19,11 +19,11 @@ import { op_fs_fdatasync_sync, op_fs_file_stat_async, op_fs_file_stat_sync, + op_fs_file_truncate_async, op_fs_flock_async, op_fs_flock_sync, op_fs_fsync_async, op_fs_fsync_sync, - op_fs_ftruncate_async, op_fs_ftruncate_sync, op_fs_funlock_async, op_fs_funlock_async_unstable, @@ -422,14 +422,6 @@ function coerceLen(len) { return len; } -function ftruncateSync(rid, len) { - op_fs_ftruncate_sync(rid, coerceLen(len)); -} - -async function ftruncate(rid, len) { - await op_fs_ftruncate_async(rid, coerceLen(len)); -} - function truncateSync(path, len) { op_fs_truncate_sync(path, coerceLen(len)); } @@ -655,11 +647,11 @@ class FsFile { } truncate(len) { - return ftruncate(this.#rid, len); + return op_fs_file_truncate_async(this.#rid, coerceLen(len)); } truncateSync(len) { - return ftruncateSync(this.#rid, len); + return op_fs_ftruncate_sync(this.#rid, coerceLen(len)); } read(p) { @@ -962,8 +954,6 @@ export { FsFile, fsync, fsyncSync, - ftruncate, - ftruncateSync, funlock, funlockSync, link, diff --git a/ext/fs/lib.rs b/ext/fs/lib.rs index be43c81d9e..7cf5f6cb31 100644 --- a/ext/fs/lib.rs +++ b/ext/fs/lib.rs @@ -242,7 +242,7 @@ deno_core::extension!(deno_fs, op_fs_funlock_async, op_fs_funlock_sync, op_fs_ftruncate_sync, - op_fs_ftruncate_async, + op_fs_file_truncate_async, op_fs_futime_sync, op_fs_futime_async, diff --git a/ext/fs/ops.rs b/ext/fs/ops.rs index 7d28b56b02..acf2c216fc 100644 --- a/ext/fs/ops.rs +++ b/ext/fs/ops.rs @@ -1569,7 +1569,7 @@ pub fn op_fs_ftruncate_sync( } #[op2(async)] -pub async fn op_fs_ftruncate_async( +pub async fn op_fs_file_truncate_async( state: Rc>, #[smi] rid: ResourceId, #[number] len: u64, diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index f453563eae..a4baedb499 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -77,22 +77,6 @@ const denoNs = { lstat: fs.lstat, truncateSync: fs.truncateSync, truncate: fs.truncate, - ftruncateSync(rid, len) { - internals.warnOnDeprecatedApi( - "Deno.ftruncateSync()", - new Error().stack, - "Use `Deno.FsFile.truncateSync()` instead.", - ); - return fs.ftruncateSync(rid, len); - }, - ftruncate(rid, len) { - internals.warnOnDeprecatedApi( - "Deno.ftruncate()", - new Error().stack, - "Use `Deno.FsFile.truncate()` instead.", - ); - return fs.ftruncate(rid, len); - }, errors: errors.errors, inspect: console.inspect, env: os.env, diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index ba010e368f..e2b1559e72 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -802,8 +802,6 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) { delete globalThis.window; delete Deno.Buffer; delete Deno.File; - delete Deno.ftruncate; - delete Deno.ftruncateSync; delete Deno.FsFile.prototype.rid; delete Deno.funlock; delete Deno.funlockSync; @@ -975,8 +973,6 @@ function bootstrapWorkerRuntime( if (future) { delete Deno.Buffer; delete Deno.File; - delete Deno.ftruncate; - delete Deno.ftruncateSync; delete Deno.FsFile.prototype.rid; delete Deno.funlock; delete Deno.funlockSync; diff --git a/tests/specs/future/runtime_api/main.js b/tests/specs/future/runtime_api/main.js index a2c5303243..9ab96e582e 100644 --- a/tests/specs/future/runtime_api/main.js +++ b/tests/specs/future/runtime_api/main.js @@ -1,8 +1,6 @@ console.log("window is", globalThis.window); console.log("Deno.Buffer is", Deno.Buffer); console.log("Deno.File is", Deno.File); -console.log("Deno.ftruncate is", Deno.ftruncate); -console.log("Deno.ftruncateSync is", Deno.ftruncateSync); console.log( "Deno.FsFile.prototype.rid is", Deno.openSync(import.meta.filename).rid, diff --git a/tests/specs/future/runtime_api/main.out b/tests/specs/future/runtime_api/main.out index 8175777e11..7cc4cdf15d 100644 --- a/tests/specs/future/runtime_api/main.out +++ b/tests/specs/future/runtime_api/main.out @@ -1,8 +1,6 @@ window is undefined Deno.Buffer is undefined Deno.File is undefined -Deno.ftruncate is undefined -Deno.ftruncateSync is undefined Deno.FsFile.prototype.rid is undefined Deno.funlock is undefined Deno.funlockSync is undefined From 7e11dbb3ac6b3622fc032f1342406603f7a2a9d6 Mon Sep 17 00:00:00 2001 From: Asher Gomez Date: Wed, 4 Sep 2024 18:54:50 +1000 Subject: [PATCH 19/45] BEAKING(buffer): remove `Deno.readAll[Sync]()` (#25386) Towards #22079 Signed-off-by: Asher Gomez --- runtime/js/13_buffer.js | 24 +----------------------- runtime/js/99_main.js | 4 ---- tests/specs/future/runtime_api/main.js | 2 -- tests/specs/future/runtime_api/main.out | 2 -- tests/unit/buffer_test.ts | 22 ---------------------- 5 files changed, 1 insertion(+), 53 deletions(-) diff --git a/runtime/js/13_buffer.js b/runtime/js/13_buffer.js index 2dbe5bbf60..4d3f08d4da 100644 --- a/runtime/js/13_buffer.js +++ b/runtime/js/13_buffer.js @@ -234,26 +234,4 @@ class Buffer { } } -async function readAll(r) { - internals.warnOnDeprecatedApi( - "Deno.readAll()", - new Error().stack, - "Use `readAll()` from `https://jsr.io/@std/io/doc/read-all/~` instead.", - ); - const buf = new Buffer(); - await buf.readFrom(r); - return buf.bytes(); -} - -function readAllSync(r) { - internals.warnOnDeprecatedApi( - "Deno.readAllSync()", - new Error().stack, - "Use `readAllSync()` from `https://jsr.io/@std/io/doc/read-all/~` instead.", - ); - const buf = new Buffer(); - buf.readFromSync(r); - return buf.bytes(); -} - -export { Buffer, readAll, readAllSync }; +export { Buffer }; diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index e2b1559e72..fba264af6e 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -805,8 +805,6 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) { delete Deno.FsFile.prototype.rid; delete Deno.funlock; delete Deno.funlockSync; - delete Deno.readAll; - delete Deno.readAllSync; delete Deno.read; delete Deno.readSync; delete Deno.seek; @@ -976,8 +974,6 @@ function bootstrapWorkerRuntime( delete Deno.FsFile.prototype.rid; delete Deno.funlock; delete Deno.funlockSync; - delete Deno.readAll; - delete Deno.readAllSync; delete Deno.read; delete Deno.readSync; delete Deno.seek; diff --git a/tests/specs/future/runtime_api/main.js b/tests/specs/future/runtime_api/main.js index 9ab96e582e..f5e17a6dea 100644 --- a/tests/specs/future/runtime_api/main.js +++ b/tests/specs/future/runtime_api/main.js @@ -7,8 +7,6 @@ console.log( ); console.log("Deno.funlock is", Deno.funlock); console.log("Deno.funlockSync is", Deno.funlockSync); -console.log("Deno.readAll is", Deno.readAll); -console.log("Deno.readAllSync is", Deno.readAllSync); console.log("Deno.read is", Deno.read); console.log("Deno.readSync is", Deno.readSync); console.log("Deno.seek is", Deno.seek); diff --git a/tests/specs/future/runtime_api/main.out b/tests/specs/future/runtime_api/main.out index 7cc4cdf15d..e76a20265f 100644 --- a/tests/specs/future/runtime_api/main.out +++ b/tests/specs/future/runtime_api/main.out @@ -4,8 +4,6 @@ Deno.File is undefined Deno.FsFile.prototype.rid is undefined Deno.funlock is undefined Deno.funlockSync is undefined -Deno.readAll is undefined -Deno.readAllSync is undefined Deno.read is undefined Deno.readSync is undefined Deno.seek is undefined diff --git a/tests/unit/buffer_test.ts b/tests/unit/buffer_test.ts index 5b788a45c7..fcbeb4bc9c 100644 --- a/tests/unit/buffer_test.ts +++ b/tests/unit/buffer_test.ts @@ -354,28 +354,6 @@ Deno.test({ ignore: DENO_FUTURE }, async function bufferTestGrow() { } }); -Deno.test({ ignore: DENO_FUTURE }, async function testReadAll() { - init(); - assert(testBytes); - const reader = new Deno.Buffer(testBytes.buffer as ArrayBuffer); - const actualBytes = await Deno.readAll(reader); - assertEquals(testBytes.byteLength, actualBytes.byteLength); - for (let i = 0; i < testBytes.length; ++i) { - assertEquals(testBytes[i], actualBytes[i]); - } -}); - -Deno.test({ ignore: DENO_FUTURE }, function testReadAllSync() { - init(); - assert(testBytes); - const reader = new Deno.Buffer(testBytes.buffer as ArrayBuffer); - const actualBytes = Deno.readAllSync(reader); - assertEquals(testBytes.byteLength, actualBytes.byteLength); - for (let i = 0; i < testBytes.length; ++i) { - assertEquals(testBytes[i], actualBytes[i]); - } -}); - Deno.test({ ignore: DENO_FUTURE }, function testBufferBytesArrayBufferLength() { // defaults to copy const args = [{}, { copy: undefined }, undefined, { copy: true }]; From 4c3b17b54703b455d8ae4b51354d18838c090658 Mon Sep 17 00:00:00 2001 From: Asher Gomez Date: Wed, 4 Sep 2024 18:57:34 +1000 Subject: [PATCH 20/45] BREAKING(io): remove `Deno.write[Sync]()` (#25408) Towards #22079 Signed-off-by: Asher Gomez --- cli/tsc/dts/lib.deno.ns.d.ts | 51 ------------------------- runtime/js/90_deno_ns.js | 16 -------- runtime/js/99_main.js | 4 -- tests/specs/future/runtime_api/main.js | 2 - tests/specs/future/runtime_api/main.out | 2 - 5 files changed, 75 deletions(-) diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index d118aca6e2..bfdb80af22 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -1984,57 +1984,6 @@ declare namespace Deno { */ export function readSync(rid: number, buffer: Uint8Array): number | null; - /** Write to the resource ID (`rid`) the contents of the array buffer (`data`). - * - * Resolves to the number of bytes written. This function is one of the lowest - * level APIs and most users should not work with this directly, but rather - * use {@linkcode WritableStream}, {@linkcode ReadableStream.from} and - * {@linkcode ReadableStream.pipeTo}. - * - * **It is not guaranteed that the full buffer will be written in a single - * call.** - * - * ```ts - * const encoder = new TextEncoder(); - * const data = encoder.encode("Hello world"); - * using file = await Deno.open("/foo/bar.txt", { write: true }); - * const bytesWritten = await Deno.write(file.rid, data); // 11 - * ``` - * - * @deprecated This will be removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @category I/O - */ - export function write(rid: number, data: Uint8Array): Promise; - - /** Synchronously write to the resource ID (`rid`) the contents of the array - * buffer (`data`). - * - * Returns the number of bytes written. This function is one of the lowest - * level APIs and most users should not work with this directly, but rather - * use {@linkcode WritableStream}, {@linkcode ReadableStream.from} and - * {@linkcode ReadableStream.pipeTo}. - * - * **It is not guaranteed that the full buffer will be written in a single - * call.** - * - * ```ts - * const encoder = new TextEncoder(); - * const data = encoder.encode("Hello world"); - * using file = Deno.openSync("/foo/bar.txt", { write: true }); - * const bytesWritten = Deno.writeSync(file.rid, data); // 11 - * ``` - * - * @deprecated This will be removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @category I/O - */ - export function writeSync(rid: number, data: Uint8Array): number; - /** Seek a resource ID (`rid`) to the given `offset` under mode given by `whence`. * The call resolves to the new position within the resource (bytes from the start). * diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index a4baedb499..9432e0719b 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -103,22 +103,6 @@ const denoNs = { ); return io.readSync(rid, buffer); }, - write(rid, data) { - internals.warnOnDeprecatedApi( - "Deno.write()", - new Error().stack, - "Use `writer.write()` instead.", - ); - return io.write(rid, data); - }, - writeSync(rid, data) { - internals.warnOnDeprecatedApi( - "Deno.writeSync()", - new Error().stack, - "Use `writer.writeSync()` instead.", - ); - return io.writeSync(rid, data); - }, File: fs.File, FsFile: fs.FsFile, open: fs.open, diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index fba264af6e..d771aec2ba 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -809,8 +809,6 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) { delete Deno.readSync; delete Deno.seek; delete Deno.seekSync; - delete Deno.write; - delete Deno.writeSync; } } else { // Warmup @@ -978,8 +976,6 @@ function bootstrapWorkerRuntime( delete Deno.readSync; delete Deno.seek; delete Deno.seekSync; - delete Deno.write; - delete Deno.writeSync; } } else { // Warmup diff --git a/tests/specs/future/runtime_api/main.js b/tests/specs/future/runtime_api/main.js index f5e17a6dea..a5ae5ec252 100644 --- a/tests/specs/future/runtime_api/main.js +++ b/tests/specs/future/runtime_api/main.js @@ -11,8 +11,6 @@ console.log("Deno.read is", Deno.read); console.log("Deno.readSync is", Deno.readSync); console.log("Deno.seek is", Deno.seek); console.log("Deno.seekSync is", Deno.seekSync); -console.log("Deno.write is", Deno.write); -console.log("Deno.writeSync is", Deno.writeSync); // TCP // Since these tests may run in parallel, ensure this port is unique to this file diff --git a/tests/specs/future/runtime_api/main.out b/tests/specs/future/runtime_api/main.out index e76a20265f..b5886ec289 100644 --- a/tests/specs/future/runtime_api/main.out +++ b/tests/specs/future/runtime_api/main.out @@ -8,8 +8,6 @@ Deno.read is undefined Deno.readSync is undefined Deno.seek is undefined Deno.seekSync is undefined -Deno.write is undefined -Deno.writeSync is undefined Deno.Listener.prototype.rid is undefined Deno.Conn.prototype.rid is undefined Deno.UnixConn.prototype.rid is undefined From b333dccee82f4328a65d0c3c45c7aa5c19255220 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Wed, 4 Sep 2024 11:04:06 +0200 Subject: [PATCH 21/45] feat(cli): give access to `process` global everywhere (#25291) --- ext/node/global.rs | 4 +--- ext/node/polyfills/01_require.js | 11 ++++------- ext/node/polyfills/_events.mjs | 8 ++++++-- ext/node/polyfills/process.ts | 3 +++ runtime/js/98_global_scope_shared.js | 2 ++ tests/node_compat/polyfill_globals.js | 4 ---- tests/registry/npm/@denotest/globals/1.0.0/index.d.ts | 1 - tests/registry/npm/@denotest/globals/1.0.0/index.js | 5 ----- .../node_globals_no_duplicate_imports/__test__.jsonc | 3 +-- .../lint/node_globals_no_duplicate_imports/error.out | 4 ---- tests/testdata/npm/compare_globals/main.out | 4 ---- tests/testdata/npm/compare_globals/main.ts | 8 -------- 12 files changed, 17 insertions(+), 40 deletions(-) delete mode 100644 tests/specs/lint/node_globals_no_duplicate_imports/error.out diff --git a/ext/node/global.rs b/ext/node/global.rs index 61bcc33434..4d6695431d 100644 --- a/ext/node/global.rs +++ b/ext/node/global.rs @@ -54,7 +54,6 @@ const fn str_to_utf16(s: &str) -> [u16; N] { // - clearTimeout (both, but different implementation) // - global (node only) // - performance (both, but different implementation) -// - process (node only) // - setImmediate (node only) // - setInterval (both, but different implementation) // - setTimeout (both, but different implementation) @@ -62,7 +61,7 @@ const fn str_to_utf16(s: &str) -> [u16; N] { // UTF-16 encodings of the managed globals. THIS LIST MUST BE SORTED. #[rustfmt::skip] -const MANAGED_GLOBALS: [&[u16]; 13] = [ +const MANAGED_GLOBALS: [&[u16]; 12] = [ &str_to_utf16::<6>("Buffer"), &str_to_utf16::<17>("WorkerGlobalScope"), &str_to_utf16::<14>("clearImmediate"), @@ -70,7 +69,6 @@ const MANAGED_GLOBALS: [&[u16]; 13] = [ &str_to_utf16::<12>("clearTimeout"), &str_to_utf16::<6>("global"), &str_to_utf16::<11>("performance"), - &str_to_utf16::<7>("process"), &str_to_utf16::<4>("self"), &str_to_utf16::<12>("setImmediate"), &str_to_utf16::<11>("setInterval"), diff --git a/ext/node/polyfills/01_require.js b/ext/node/polyfills/01_require.js index d666b39274..e4a781cc44 100644 --- a/ext/node/polyfills/01_require.js +++ b/ext/node/polyfills/01_require.js @@ -940,12 +940,11 @@ Module.prototype.require = function (id) { // The module wrapper looks slightly different to Node. Instead of using one // wrapper function, we use two. The first one exists to performance optimize -// access to magic node globals, like `Buffer` or `process`. The second one -// is the actual wrapper function we run the users code in. -// The only observable difference is that in Deno `arguments.callee` is not -// null. +// access to magic node globals, like `Buffer`. The second one is the actual +// wrapper function we run the users code in. The only observable difference is +// that in Deno `arguments.callee` is not null. Module.wrapper = [ - "(function (exports, require, module, __filename, __dirname, Buffer, clearImmediate, clearInterval, clearTimeout, global, process, setImmediate, setInterval, setTimeout, performance) { (function (exports, require, module, __filename, __dirname) {", + "(function (exports, require, module, __filename, __dirname, Buffer, clearImmediate, clearInterval, clearTimeout, global, setImmediate, setInterval, setTimeout, performance) { (function (exports, require, module, __filename, __dirname) {", "\n}).call(this, exports, require, module, __filename, __dirname); })", ]; Module.wrap = function (script) { @@ -1030,7 +1029,6 @@ Module.prototype._compile = function (content, filename, format) { clearInterval, clearTimeout, global, - process, setImmediate, setInterval, setTimeout, @@ -1049,7 +1047,6 @@ Module.prototype._compile = function (content, filename, format) { clearInterval, clearTimeout, global, - process, setImmediate, setInterval, setTimeout, diff --git a/ext/node/polyfills/_events.mjs b/ext/node/polyfills/_events.mjs index bb34985940..12b0935e61 100644 --- a/ext/node/polyfills/_events.mjs +++ b/ext/node/polyfills/_events.mjs @@ -46,7 +46,6 @@ import { } from "ext:deno_node/internal/validators.mjs"; import { spliceOne } from "ext:deno_node/_utils.ts"; import { nextTick } from "ext:deno_node/_process/process.ts"; -import { nodeGlobals } from "ext:deno_node/00_globals.js"; const kCapture = Symbol("kCapture"); const kErrorMonitor = Symbol("events.errorMonitor"); @@ -55,6 +54,11 @@ const kMaxEventTargetListenersWarned = Symbol( "events.maxEventTargetListenersWarned", ); +let process; +export function setProcess(p) { + process = p; +} + /** * Creates a new `EventEmitter` instance. * @param {{ captureRejections?: boolean; }} [opts] @@ -469,7 +473,7 @@ function _addListener(target, type, listener, prepend) { w.emitter = target; w.type = type; w.count = existing.length; - nodeGlobals.process.emitWarning(w); + process.emitWarning(w); } } diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 8d54429354..2130087fb6 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -69,6 +69,7 @@ import * as constants from "ext:deno_node/internal_binding/constants.ts"; import * as uv from "ext:deno_node/internal_binding/uv.ts"; import type { BindingName } from "ext:deno_node/internal_binding/mod.ts"; import { buildAllowedFlags } from "ext:deno_node/internal/process/per_thread.mjs"; +import { setProcess } from "ext:deno_node/_events.mjs"; const notImplementedEvents = [ "multipleResolves", @@ -960,4 +961,6 @@ internals.__bootstrapNodeProcess = function ( } }; +setProcess(process); + export default process; diff --git a/runtime/js/98_global_scope_shared.js b/runtime/js/98_global_scope_shared.js index b6e4802163..41df35c609 100644 --- a/runtime/js/98_global_scope_shared.js +++ b/runtime/js/98_global_scope_shared.js @@ -31,6 +31,7 @@ import * as webidl from "ext:deno_webidl/00_webidl.js"; import { DOMException } from "ext:deno_web/01_dom_exception.js"; import * as abortSignal from "ext:deno_web/03_abort_signal.js"; import * as imageData from "ext:deno_web/16_image_data.js"; +import process from "node:process"; import { loadWebGPU } from "ext:deno_webgpu/00_init.js"; import * as webgpuSurface from "ext:deno_webgpu/02_surface.js"; import { unstableIds } from "ext:runtime/90_deno_ns.js"; @@ -137,6 +138,7 @@ const windowOrWorkerGlobalScope = { fetch: core.propWritable(fetch.fetch), EventSource: core.propWritable(eventSource.EventSource), performance: core.propWritable(performance.performance), + process: core.propWritable(process), reportError: core.propWritable(event.reportError), setInterval: core.propWritable(timers.setInterval), setTimeout: core.propWritable(timers.setTimeout), diff --git a/tests/node_compat/polyfill_globals.js b/tests/node_compat/polyfill_globals.js index 93246d2ef2..79e1cc3f9b 100644 --- a/tests/node_compat/polyfill_globals.js +++ b/tests/node_compat/polyfill_globals.js @@ -1,5 +1,4 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import process from "node:process"; import { Buffer } from "node:buffer"; import { clearImmediate, @@ -10,15 +9,12 @@ import { setTimeout, } from "node:timers"; import { performance } from "node:perf_hooks"; -import console from "node:console"; globalThis.Buffer = Buffer; globalThis.clearImmediate = clearImmediate; globalThis.clearInterval = clearInterval; globalThis.clearTimeout = clearTimeout; -globalThis.console = console; globalThis.global = globalThis; globalThis.performance = performance; -globalThis.process = process; globalThis.setImmediate = setImmediate; globalThis.setInterval = setInterval; globalThis.setTimeout = setTimeout; diff --git a/tests/registry/npm/@denotest/globals/1.0.0/index.d.ts b/tests/registry/npm/@denotest/globals/1.0.0/index.d.ts index 76dd781db6..548633db12 100644 --- a/tests/registry/npm/@denotest/globals/1.0.0/index.d.ts +++ b/tests/registry/npm/@denotest/globals/1.0.0/index.d.ts @@ -15,7 +15,6 @@ type _TestHasProcessGlobal = AssertTrue< export function deleteSetTimeout(): void; export function getSetTimeout(): typeof setTimeout; -export function checkProcessGlobal(): void; export function checkWindowGlobal(): void; export function checkSelfGlobal(): void; diff --git a/tests/registry/npm/@denotest/globals/1.0.0/index.js b/tests/registry/npm/@denotest/globals/1.0.0/index.js index 64f913b37a..542cb63d2b 100644 --- a/tests/registry/npm/@denotest/globals/1.0.0/index.js +++ b/tests/registry/npm/@denotest/globals/1.0.0/index.js @@ -10,11 +10,6 @@ exports.getSetTimeout = function () { return globalThis.setTimeout; }; -exports.checkProcessGlobal = function () { - console.log("process" in globalThis); - console.log(Object.getOwnPropertyDescriptor(globalThis, "process") !== undefined); -}; - exports.checkWindowGlobal = function () { console.log("window" in globalThis); console.log(Object.getOwnPropertyDescriptor(globalThis, "window") !== undefined); diff --git a/tests/specs/lint/node_globals_no_duplicate_imports/__test__.jsonc b/tests/specs/lint/node_globals_no_duplicate_imports/__test__.jsonc index d8e00f47ad..a632e896ef 100644 --- a/tests/specs/lint/node_globals_no_duplicate_imports/__test__.jsonc +++ b/tests/specs/lint/node_globals_no_duplicate_imports/__test__.jsonc @@ -3,8 +3,7 @@ "steps": [ { "args": "run main.ts", - "output": "error.out", - "exitCode": 1 + "output": "" }, { "args": "lint main.ts", diff --git a/tests/specs/lint/node_globals_no_duplicate_imports/error.out b/tests/specs/lint/node_globals_no_duplicate_imports/error.out deleted file mode 100644 index 5671b17b46..0000000000 --- a/tests/specs/lint/node_globals_no_duplicate_imports/error.out +++ /dev/null @@ -1,4 +0,0 @@ -error: Uncaught (in promise) ReferenceError: process is not defined -const _foo = process.env.FOO; - ^ - at [WILDCARD]main.ts:3:14 diff --git a/tests/testdata/npm/compare_globals/main.out b/tests/testdata/npm/compare_globals/main.out index 5b1d264646..f6f90d533f 100644 --- a/tests/testdata/npm/compare_globals/main.out +++ b/tests/testdata/npm/compare_globals/main.out @@ -15,10 +15,6 @@ setTimeout 2 function setTimeout 3 function setTimeout 4 function setTimeout 5 undefined -process 1 false -process 2 false -true -true window 1 false window 2 false false diff --git a/tests/testdata/npm/compare_globals/main.ts b/tests/testdata/npm/compare_globals/main.ts index 5019a5fd5c..9482798d8c 100644 --- a/tests/testdata/npm/compare_globals/main.ts +++ b/tests/testdata/npm/compare_globals/main.ts @@ -30,14 +30,6 @@ globals.deleteSetTimeout(); console.log("setTimeout 4", typeof globalThis.setTimeout); console.log("setTimeout 5", typeof globals.getSetTimeout()); -// In Deno, the process global is not defined, but in Node it is. -console.log("process 1", "process" in globalThis); -console.log( - "process 2", - Object.getOwnPropertyDescriptor(globalThis, "process") !== undefined, -); -globals.checkProcessGlobal(); - // In Deno 2 and Node.js, the window global is not defined. console.log("window 1", "window" in globalThis); console.log( From 31ecc09b5ae38531cb63680cc40b89d01d8635df Mon Sep 17 00:00:00 2001 From: Asher Gomez Date: Wed, 4 Sep 2024 19:34:40 +1000 Subject: [PATCH 22/45] BREAKING(io): remove `Deno.read[Sync]()` (#25409) Towards #22079 Signed-off-by: Asher Gomez --- cli/bench/deno_common.js | 3 +- cli/tsc/dts/lib.deno.ns.d.ts | 64 ------------------------- runtime/js/90_deno_ns.js | 16 ------- runtime/js/99_main.js | 4 -- tests/specs/future/runtime_api/main.js | 2 - tests/specs/future/runtime_api/main.out | 2 - 6 files changed, 1 insertion(+), 90 deletions(-) diff --git a/cli/bench/deno_common.js b/cli/bench/deno_common.js index ba88c79ca4..3693333915 100644 --- a/cli/bench/deno_common.js +++ b/cli/bench/deno_common.js @@ -46,8 +46,7 @@ Deno.bench("b64_rt_short", { n: 1e6 }, () => { const buf = new Uint8Array(100); const file = Deno.openSync("/dev/zero"); Deno.bench("read_zero", { n: 5e5 }, () => { - // deno-lint-ignore no-deprecated-deno-api - Deno.readSync(file.rid, buf); + file.readSync(buf); }); } diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index bfdb80af22..495ece36db 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -1920,70 +1920,6 @@ declare namespace Deno { */ export function createSync(path: string | URL): FsFile; - /** Read from a resource ID (`rid`) into an array buffer (`buffer`). - * - * Resolves to either the number of bytes read during the operation or EOF - * (`null`) if there was nothing more to read. - * - * It is possible for a read to successfully return with `0` bytes. This does - * not indicate EOF. - * - * This function is one of the lowest level APIs and most users should not - * work with this directly, but rather use {@linkcode ReadableStream} and - * {@linkcode https://jsr.io/@std/streams/doc/to-array-buffer/~/toArrayBuffer | toArrayBuffer} - * instead. - * - * **It is not guaranteed that the full buffer will be read in a single call.** - * - * ```ts - * // if "/foo/bar.txt" contains the text "hello world": - * using file = await Deno.open("/foo/bar.txt"); - * const buf = new Uint8Array(100); - * const numberOfBytesRead = await Deno.read(file.rid, buf); // 11 bytes - * const text = new TextDecoder().decode(buf); // "hello world" - * ``` - * - * @deprecated This will be removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @category I/O - */ - export function read(rid: number, buffer: Uint8Array): Promise; - - /** Synchronously read from a resource ID (`rid`) into an array buffer - * (`buffer`). - * - * Returns either the number of bytes read during the operation or EOF - * (`null`) if there was nothing more to read. - * - * It is possible for a read to successfully return with `0` bytes. This does - * not indicate EOF. - * - * This function is one of the lowest level APIs and most users should not - * work with this directly, but rather use {@linkcode ReadableStream} and - * {@linkcode https://jsr.io/@std/streams/doc/to-array-buffer/~/toArrayBuffer | toArrayBuffer} - * instead. - * - * **It is not guaranteed that the full buffer will be read in a single - * call.** - * - * ```ts - * // if "/foo/bar.txt" contains the text "hello world": - * using file = Deno.openSync("/foo/bar.txt"); - * const buf = new Uint8Array(100); - * const numberOfBytesRead = Deno.readSync(file.rid, buf); // 11 bytes - * const text = new TextDecoder().decode(buf); // "hello world" - * ``` - * - * @deprecated This will be removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @category I/O - */ - export function readSync(rid: number, buffer: Uint8Array): number | null; - /** Seek a resource ID (`rid`) to the given `offset` under mode given by `whence`. * The call resolves to the new position within the resource (bytes from the start). * diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js index 9432e0719b..6f5d85da2c 100644 --- a/runtime/js/90_deno_ns.js +++ b/runtime/js/90_deno_ns.js @@ -87,22 +87,6 @@ const denoNs = { readAllSync: buffer.readAllSync, copy: io.copy, SeekMode: io.SeekMode, - read(rid, buffer) { - internals.warnOnDeprecatedApi( - "Deno.read()", - new Error().stack, - "Use `reader.read()` instead.", - ); - return io.read(rid, buffer); - }, - readSync(rid, buffer) { - internals.warnOnDeprecatedApi( - "Deno.readSync()", - new Error().stack, - "Use `reader.readSync()` instead.", - ); - return io.readSync(rid, buffer); - }, File: fs.File, FsFile: fs.FsFile, open: fs.open, diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index d771aec2ba..1ffb4f6e3e 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -805,8 +805,6 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) { delete Deno.FsFile.prototype.rid; delete Deno.funlock; delete Deno.funlockSync; - delete Deno.read; - delete Deno.readSync; delete Deno.seek; delete Deno.seekSync; } @@ -972,8 +970,6 @@ function bootstrapWorkerRuntime( delete Deno.FsFile.prototype.rid; delete Deno.funlock; delete Deno.funlockSync; - delete Deno.read; - delete Deno.readSync; delete Deno.seek; delete Deno.seekSync; } diff --git a/tests/specs/future/runtime_api/main.js b/tests/specs/future/runtime_api/main.js index a5ae5ec252..26a75373ea 100644 --- a/tests/specs/future/runtime_api/main.js +++ b/tests/specs/future/runtime_api/main.js @@ -7,8 +7,6 @@ console.log( ); console.log("Deno.funlock is", Deno.funlock); console.log("Deno.funlockSync is", Deno.funlockSync); -console.log("Deno.read is", Deno.read); -console.log("Deno.readSync is", Deno.readSync); console.log("Deno.seek is", Deno.seek); console.log("Deno.seekSync is", Deno.seekSync); diff --git a/tests/specs/future/runtime_api/main.out b/tests/specs/future/runtime_api/main.out index b5886ec289..0aa4e1f7c9 100644 --- a/tests/specs/future/runtime_api/main.out +++ b/tests/specs/future/runtime_api/main.out @@ -4,8 +4,6 @@ Deno.File is undefined Deno.FsFile.prototype.rid is undefined Deno.funlock is undefined Deno.funlockSync is undefined -Deno.read is undefined -Deno.readSync is undefined Deno.seek is undefined Deno.seekSync is undefined Deno.Listener.prototype.rid is undefined From 5ee671311a174b5461483db788f732fe736b6549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 4 Sep 2024 11:49:31 +0100 Subject: [PATCH 23/45] chore: remove some dead code around DENO_FUTURE env var (#25418) These codepaths were not used anymore. --- cli/worker.rs | 5 ----- runtime/js/99_main.js | 20 +++++++++----------- runtime/lib.rs | 1 - runtime/shared.rs | 23 ----------------------- runtime/web_worker.rs | 9 +-------- runtime/worker.rs | 10 +--------- runtime/worker_bootstrap.rs | 5 ----- 7 files changed, 11 insertions(+), 62 deletions(-) diff --git a/cli/worker.rs b/cli/worker.rs index 7000fd2040..64400af205 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -138,7 +138,6 @@ struct SharedWorkerState { maybe_inspector_server: Option>, maybe_lockfile: Option>, feature_checker: Arc, - enable_future_features: bool, code_cache: Option>, } @@ -453,8 +452,6 @@ impl CliMainWorkerFactory { maybe_inspector_server, maybe_lockfile, feature_checker, - // TODO(2.0): remove? - enable_future_features: true, code_cache, }), } @@ -591,7 +588,6 @@ impl CliMainWorkerFactory { argv0: shared.options.argv0.clone(), node_debug: shared.options.node_debug.clone(), node_ipc_fd: shared.options.node_ipc, - future: shared.enable_future_features, mode, serve_port: shared.options.serve_port, serve_host: shared.options.serve_host.clone(), @@ -787,7 +783,6 @@ fn create_web_worker_callback( argv0: shared.options.argv0.clone(), node_debug: shared.options.node_debug.clone(), node_ipc_fd: None, - future: shared.enable_future_features, mode: WorkerExecutionMode::Worker, serve_port: shared.options.serve_port, serve_host: shared.options.serve_host.clone(), diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index 1ffb4f6e3e..6dd75b415e 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -576,12 +576,11 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) { 6: hasNodeModulesDir, 7: argv0, 8: nodeDebug, - 9: future, - 10: mode, - 11: servePort, - 12: serveHost, - 13: serveIsMain, - 14: serveWorkerCount, + 9: mode, + 10: servePort, + 11: serveHost, + 12: serveIsMain, + 13: serveWorkerCount, } = runtimeOptions; if (mode === executionModes.serve) { @@ -668,7 +667,7 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) { // TODO(iuioiua): remove in Deno v2. This allows us to dynamically delete // class properties within constructors for classes that are not defined // within the Deno namespace. - internals.future = future; + internals.future = true; removeImportedOps(); @@ -798,7 +797,7 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) { nodeDebug, }); } - if (future) { + if (internals.future) { delete globalThis.window; delete Deno.Buffer; delete Deno.File; @@ -835,13 +834,12 @@ function bootstrapWorkerRuntime( 6: hasNodeModulesDir, 7: argv0, 8: nodeDebug, - 9: future, } = runtimeOptions; // TODO(iuioiua): remove in Deno v2. This allows us to dynamically delete // class properties within constructors for classes that are not defined // within the Deno namespace. - internals.future = future; + internals.future = true; performance.setTimeOrigin(DateNow()); globalThis_ = globalThis; @@ -964,7 +962,7 @@ function bootstrapWorkerRuntime( }); } - if (future) { + if (internals.future) { delete Deno.Buffer; delete Deno.File; delete Deno.FsFile.prototype.rid; diff --git a/runtime/lib.rs b/runtime/lib.rs index daa55f773f..ed3f9fbc6a 100644 --- a/runtime/lib.rs +++ b/runtime/lib.rs @@ -44,7 +44,6 @@ pub use worker_bootstrap::WorkerExecutionMode; pub use worker_bootstrap::WorkerLogLevel; mod shared; -pub use shared::import_assertion_callback; pub use shared::runtime; pub struct UnstableGranularFlag { diff --git a/runtime/shared.rs b/runtime/shared.rs index c52521690c..1b2136c638 100644 --- a/runtime/shared.rs +++ b/runtime/shared.rs @@ -116,26 +116,3 @@ pub fn maybe_transpile_source( Ok((source_text.into(), maybe_source_map)) } - -pub fn import_assertion_callback( - args: deno_core::ImportAssertionsSupportCustomCallbackArgs, -) { - let mut msg = deno_terminal::colors::yellow("⚠️ Import assertions are deprecated. Use `with` keyword, instead of 'assert' keyword.").to_string(); - if let Some(specifier) = args.maybe_specifier { - if let Some(source_line) = args.maybe_source_line { - msg.push_str("\n\n"); - msg.push_str(&source_line); - msg.push_str("\n\n"); - } - msg.push_str(&format!( - " at {}:{}:{}\n", - specifier, - args.maybe_line_number.unwrap(), - args.column_number - )); - #[allow(clippy::print_stderr)] - { - eprintln!("{}", msg); - } - } -} diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index ad0ac5a3f2..e143288618 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -545,13 +545,6 @@ impl WebWorker { options.bootstrap.enable_op_summary_metrics, options.strace_ops, ); - let import_assertions_support = if options.bootstrap.future { - deno_core::ImportAssertionsSupport::Error - } else { - deno_core::ImportAssertionsSupport::CustomCallback(Box::new( - crate::shared::import_assertion_callback, - )) - }; let mut js_runtime = JsRuntime::new(RuntimeOptions { module_loader: Some(options.module_loader.clone()), @@ -572,7 +565,7 @@ impl WebWorker { validate_import_attributes_cb: Some(Box::new( validate_import_attributes_callback, )), - import_assertions_support, + import_assertions_support: deno_core::ImportAssertionsSupport::Error, ..Default::default() }); diff --git a/runtime/worker.rs b/runtime/worker.rs index c0d8391667..02749e7c18 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -477,14 +477,6 @@ impl MainWorker { } }); - let import_assertions_support = if options.bootstrap.future { - deno_core::ImportAssertionsSupport::Error - } else { - deno_core::ImportAssertionsSupport::CustomCallback(Box::new( - crate::shared::import_assertion_callback, - )) - }; - let mut js_runtime = JsRuntime::new(RuntimeOptions { module_loader: Some(options.module_loader.clone()), startup_snapshot: options.startup_snapshot, @@ -510,7 +502,7 @@ impl MainWorker { validate_import_attributes_cb: Some(Box::new( validate_import_attributes_callback, )), - import_assertions_support, + import_assertions_support: deno_core::ImportAssertionsSupport::Error, eval_context_code_cache_cbs: options.v8_code_cache.map(|cache| { let cache_clone = cache.clone(); ( diff --git a/runtime/worker_bootstrap.rs b/runtime/worker_bootstrap.rs index b6ede466e1..b137efae22 100644 --- a/runtime/worker_bootstrap.rs +++ b/runtime/worker_bootstrap.rs @@ -116,7 +116,6 @@ pub struct BootstrapOptions { pub argv0: Option, pub node_debug: Option, pub node_ipc_fd: Option, - pub future: bool, pub mode: WorkerExecutionMode, // Used by `deno serve` pub serve_port: Option, @@ -153,7 +152,6 @@ impl Default for BootstrapOptions { argv0: None, node_debug: None, node_ipc_fd: None, - future: false, mode: WorkerExecutionMode::None, serve_port: Default::default(), serve_host: Default::default(), @@ -190,8 +188,6 @@ struct BootstrapV8<'a>( Option<&'a str>, // node_debug Option<&'a str>, - // future - bool, // mode i32, // serve port @@ -224,7 +220,6 @@ impl BootstrapOptions { self.has_node_modules_dir, self.argv0.as_deref(), self.node_debug.as_deref(), - self.future, self.mode.discriminant() as _, self.serve_port.unwrap_or_default(), self.serve_host.as_deref(), From 0e0a5c24ea8b17d410c30bf3fcb70c749b35f8b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 4 Sep 2024 13:21:02 +0100 Subject: [PATCH 24/45] test: run js_unit_tests with `--unstable-*` flags (#25394) --- runtime/js/99_main.js | 35 +++++++++++++++--------------- runtime/lib.rs | 2 +- tests/integration/js_unit_tests.rs | 32 +++++++++++++++++++++------ 3 files changed, 44 insertions(+), 25 deletions(-) diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index 6dd75b415e..d0171acd27 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -722,26 +722,27 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) { target, ); + // TODO(bartlomieju): this is not ideal, but because we use `ObjectAssign` + // above any properties that are defined elsewhere using `Object.defineProperty` + // are lost. + let jupyterNs = undefined; + ObjectDefineProperty(finalDenoNs, "jupyter", { + get() { + if (jupyterNs) { + return jupyterNs; + } + throw new Error( + "Deno.jupyter is only available in `deno jupyter` subcommand.", + ); + }, + set(val) { + jupyterNs = val; + }, + }); + // TODO(bartlomieju): deprecate --unstable if (unstableFlag) { ObjectAssign(finalDenoNs, denoNsUnstable); - // TODO(bartlomieju): this is not ideal, but because we use `ObjectAssign` - // above any properties that are defined elsewhere using `Object.defineProperty` - // are lost. - let jupyterNs = undefined; - ObjectDefineProperty(finalDenoNs, "jupyter", { - get() { - if (jupyterNs) { - return jupyterNs; - } - throw new Error( - "Deno.jupyter is only available in `deno jupyter` subcommand.", - ); - }, - set(val) { - jupyterNs = val; - }, - }); } else { for (let i = 0; i <= unstableFeatures.length; i++) { const id = unstableFeatures[i]; diff --git a/runtime/lib.rs b/runtime/lib.rs index ed3f9fbc6a..c8ab099f18 100644 --- a/runtime/lib.rs +++ b/runtime/lib.rs @@ -83,7 +83,7 @@ pub static UNSTABLE_GRANULAR_FLAGS: &[UnstableGranularFlag] = &[ UnstableGranularFlag { name: ops::http::UNSTABLE_FEATURE_NAME, help_text: "Enable unstable HTTP APIs", - show_in_help: false, + show_in_help: true, id: 5, }, UnstableGranularFlag { diff --git a/tests/integration/js_unit_tests.rs b/tests/integration/js_unit_tests.rs index 0baa114b44..b6a204157b 100644 --- a/tests/integration/js_unit_tests.rs +++ b/tests/integration/js_unit_tests.rs @@ -116,22 +116,40 @@ util::unit_test_factory!( fn js_unit_test(test: String) { let _g = util::http_server(); - let deno = util::deno_cmd() + let mut deno = util::deno_cmd() .current_dir(util::root_path()) .arg("test") .arg("--config") .arg(util::deno_config_path()) .arg("--no-lock") - .arg("--unstable") + // TODO(bartlomieju): would be better if we could apply this unstable + // flag to particular files, but there's many of them that rely on unstable + // net APIs (`reusePort` in `listen` and `listenTls`; `listenDatagram`, `createHttpClient`) + .arg("--unstable-net") + .arg("--unstable-http") .arg("--location=http://127.0.0.1:4545/") .arg("--no-prompt"); + if test == "broadcast_channel_test" { + deno = deno.arg("--unstable-broadcast-channel"); + } + + if test == "cron_test" { + deno = deno.arg("--unstable-cron"); + } + + if test.contains("kv_") { + deno = deno.arg("--unstable-kv"); + } + + if test == "worker_permissions_test" || test == "worker_test" { + deno = deno.arg("--unstable-worker-options"); + } + // TODO(mmastrac): it would be better to just load a test CA for all tests - let deno = if test == "websocket_test" || test == "tls_sni_test" { - deno.arg("--unsafely-ignore-certificate-errors") - } else { - deno - }; + if test == "websocket_test" || test == "tls_sni_test" { + deno = deno.arg("--unsafely-ignore-certificate-errors"); + } let mut deno = deno .arg("-A") From 84dc375b2d28a0ba9ddf0fbc5168505c19b1adea Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Wed, 4 Sep 2024 13:22:30 +0100 Subject: [PATCH 25/45] fix(lsp): update diagnostics on npm install (#25352) --- cli/lsp/config.rs | 11 +++++--- tests/integration/lsp_tests.rs | 46 +++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index d204dce646..f9262d12e8 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -1697,9 +1697,14 @@ impl ConfigTree { } pub fn is_watched_file(&self, specifier: &ModuleSpecifier) -> bool { - if specifier.path().ends_with("/deno.json") - || specifier.path().ends_with("/deno.jsonc") - || specifier.path().ends_with("/package.json") + let path = specifier.path(); + if path.ends_with("/deno.json") + || path.ends_with("/deno.jsonc") + || path.ends_with("/package.json") + || path.ends_with("/node_modules/.package-lock.json") + || path.ends_with("/node_modules/.yarn-integrity.json") + || path.ends_with("/node_modules/.modules.yaml") + || path.ends_with("/node_modules/.deno/.setup-cache.bin") { return true; } diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 7c7e9315ce..4693b72223 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -14767,7 +14767,6 @@ fn lsp_byonm() { "@denotest/esm-basic": "*", }, })); - context.run_npm("install"); let mut client = context.new_lsp_command().build(); client.initialize_default(); let diagnostics = client.did_open(json!({ @@ -14781,6 +14780,51 @@ fn lsp_byonm() { "#, }, })); + assert_eq!( + json!(diagnostics.all()), + json!([ + { + "range": { + "start": { + "line": 1, + "character": 15, + }, + "end": { + "line": 1, + "character": 26, + }, + }, + "severity": 1, + "code": "resolver-error", + "source": "deno", + "message": "Could not find a matching package for 'npm:chalk' in a package.json file. You must specify this as a package.json dependency when the node_modules folder is not managed by Deno.", + }, + { + "range": { + "start": { + "line": 2, + "character": 15, + }, + "end": { + "line": 2, + "character": 36, + }, + }, + "severity": 1, + "code": "resolver-error", + "source": "deno", + "message": "Could not resolve \"@denotest/esm-basic\", but found it in a package.json. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?", + }, + ]) + ); + context.run_npm("install"); + client.did_change_watched_files(json!({ + "changes": [{ + "uri": temp_dir.url().join("node_modules/.package-lock.json").unwrap(), + "type": 1, + }], + })); + let diagnostics = client.read_diagnostics(); assert_eq!( json!(diagnostics.all()), json!([ From 334c842392e2587b8ca1d7cc7cc7d9231fc15286 Mon Sep 17 00:00:00 2001 From: Satya Rohith Date: Wed, 4 Sep 2024 18:10:28 +0530 Subject: [PATCH 26/45] chore(ext/fetch): remove op_fetch_response_upgrade (#25421) --- ext/fetch/README.md | 1 - ext/fetch/lib.rs | 113 -------------------------------------------- 2 files changed, 114 deletions(-) diff --git a/ext/fetch/README.md b/ext/fetch/README.md index d088a6147c..3af8110a6f 100644 --- a/ext/fetch/README.md +++ b/ext/fetch/README.md @@ -78,6 +78,5 @@ Following ops are provided, which can be accessed through `Deno.ops`: - op_fetch - op_fetch_send -- op_fetch_response_upgrade - op_utf8_to_byte_string - op_fetch_custom_client diff --git a/ext/fetch/lib.rs b/ext/fetch/lib.rs index a3f9eeb4fb..79659771e6 100644 --- a/ext/fetch/lib.rs +++ b/ext/fetch/lib.rs @@ -28,7 +28,6 @@ use deno_core::futures::Stream; use deno_core::futures::StreamExt; use deno_core::futures::TryFutureExt; use deno_core::op2; -use deno_core::unsync::spawn; use deno_core::url::Url; use deno_core::AsyncRefCell; use deno_core::AsyncResult; @@ -70,12 +69,9 @@ use hyper::body::Frame; use hyper_util::client::legacy::connect::HttpConnector; use hyper_util::client::legacy::connect::HttpInfo; use hyper_util::rt::TokioExecutor; -use hyper_util::rt::TokioIo; use hyper_util::rt::TokioTimer; use serde::Deserialize; use serde::Serialize; -use tokio::io::AsyncReadExt; -use tokio::io::AsyncWriteExt; use tower::ServiceExt; use tower_http::decompression::Decompression; @@ -127,7 +123,6 @@ deno_core::extension!(deno_fetch, ops = [ op_fetch, op_fetch_send, - op_fetch_response_upgrade, op_utf8_to_byte_string, op_fetch_custom_client, ], @@ -627,114 +622,6 @@ pub async fn op_fetch_send( }) } -#[op2(async)] -#[smi] -pub async fn op_fetch_response_upgrade( - state: Rc>, - #[smi] rid: ResourceId, -) -> Result { - let raw_response = state - .borrow_mut() - .resource_table - .take::(rid)?; - let raw_response = Rc::try_unwrap(raw_response) - .expect("Someone is holding onto FetchResponseResource"); - - let (read, write) = tokio::io::duplex(1024); - let (read_rx, write_tx) = tokio::io::split(read); - let (mut write_rx, mut read_tx) = tokio::io::split(write); - let upgraded = raw_response.upgrade().await?; - { - // Stage 3: Pump the data - let (mut upgraded_rx, mut upgraded_tx) = - tokio::io::split(TokioIo::new(upgraded)); - - spawn(async move { - let mut buf = [0; 1024]; - loop { - let read = upgraded_rx.read(&mut buf).await?; - if read == 0 { - break; - } - read_tx.write_all(&buf[..read]).await?; - } - Ok::<_, AnyError>(()) - }); - spawn(async move { - let mut buf = [0; 1024]; - loop { - let read = write_rx.read(&mut buf).await?; - if read == 0 { - break; - } - upgraded_tx.write_all(&buf[..read]).await?; - } - Ok::<_, AnyError>(()) - }); - } - - Ok( - state - .borrow_mut() - .resource_table - .add(UpgradeStream::new(read_rx, write_tx)), - ) -} - -struct UpgradeStream { - read: AsyncRefCell>, - write: AsyncRefCell>, - cancel_handle: CancelHandle, -} - -impl UpgradeStream { - pub fn new( - read: tokio::io::ReadHalf, - write: tokio::io::WriteHalf, - ) -> Self { - Self { - read: AsyncRefCell::new(read), - write: AsyncRefCell::new(write), - cancel_handle: CancelHandle::new(), - } - } - - async fn read(self: Rc, buf: &mut [u8]) -> Result { - let cancel_handle = RcRef::map(self.clone(), |this| &this.cancel_handle); - async { - let read = RcRef::map(self, |this| &this.read); - let mut read = read.borrow_mut().await; - Ok(Pin::new(&mut *read).read(buf).await?) - } - .try_or_cancel(cancel_handle) - .await - } - - async fn write(self: Rc, buf: &[u8]) -> Result { - let cancel_handle = RcRef::map(self.clone(), |this| &this.cancel_handle); - async { - let write = RcRef::map(self, |this| &this.write); - let mut write = write.borrow_mut().await; - Ok(Pin::new(&mut *write).write(buf).await?) - } - .try_or_cancel(cancel_handle) - .await - } -} - -impl Resource for UpgradeStream { - fn name(&self) -> Cow { - "fetchUpgradedStream".into() - } - - deno_core::impl_readable_byob!(); - deno_core::impl_writable!(); - - fn close(self: Rc) { - self.cancel_handle.cancel(); - } -} - type CancelableResponseResult = Result, AnyError>, Canceled>; From 74fc66da110ec20d12751e7a0922cea300314399 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 4 Sep 2024 14:51:24 +0200 Subject: [PATCH 27/45] fix: lock down allow-run permissions more (#25370) `--allow-run` even with an allow list has essentially been `--allow-all`... this locks it down more. 1. Resolves allow list for `--allow-run=` on startup to an absolute path, then uses these paths when evaluating if a command can execute. Also, adds these paths to `--deny-write` 1. Resolves the environment (cwd and env vars) before evaluating permissions and before executing a command. Then uses this environment to evaluate the permissions and then evaluate the command. --- cli/args/flags.rs | 82 +++- cli/task_runner.rs | 4 +- runtime/ops/process.rs | 235 +++++++--- runtime/permissions/lib.rs | 433 ++++++++++-------- tests/integration/run_tests.rs | 41 +- .../compile/permissions_denied/__test__.jsonc | 4 + .../specs/compile/permissions_denied/main.out | 2 +- .../lifecycle_scripts/node_gyp_not_found.out | 2 +- .../path_not_permitted/__test__.jsonc | 10 + .../permission/path_not_permitted/main.out | 11 + .../permission/path_not_permitted/main.ts | 18 + .../permission/path_not_permitted/sub.ts | 34 ++ .../write_allow_binary/__test__.jsonc | 5 + .../permission/write_allow_binary/main.out | 6 + .../permission/write_allow_binary/main.ts | 14 + .../permission/write_allow_binary/sub.ts | 3 + .../__test__.jsonc | 8 + .../allow_run_allowlist_resolution/main.out} | 22 +- .../allow_run_allowlist_resolution/main.ts} | 39 +- tests/specs/run/ld_preload/__test__.jsonc | 6 +- tests/specs/run/ld_preload/env_arg.out | 12 +- tests/specs/run/ld_preload/env_arg.ts | 25 +- .../run/ld_preload/set_with_allow_env.out | 12 +- .../run/ld_preload/set_with_allow_env.ts | 14 +- tests/testdata/run/089_run_allow_list.ts.out | 2 +- tests/unit/process_test.ts | 2 +- tools/lint.js | 2 +- 27 files changed, 684 insertions(+), 364 deletions(-) create mode 100644 tests/specs/permission/path_not_permitted/__test__.jsonc create mode 100644 tests/specs/permission/path_not_permitted/main.out create mode 100644 tests/specs/permission/path_not_permitted/main.ts create mode 100644 tests/specs/permission/path_not_permitted/sub.ts create mode 100644 tests/specs/permission/write_allow_binary/__test__.jsonc create mode 100644 tests/specs/permission/write_allow_binary/main.out create mode 100644 tests/specs/permission/write_allow_binary/main.ts create mode 100644 tests/specs/permission/write_allow_binary/sub.ts create mode 100644 tests/specs/run/allow_run_allowlist_resolution/__test__.jsonc rename tests/{testdata/allow_run_allowlist_resolution.ts.out => specs/run/allow_run_allowlist_resolution/main.out} (67%) rename tests/{testdata/allow_run_allowlist_resolution.ts => specs/run/allow_run_allowlist_resolution/main.ts} (71%) diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 257bf81785..5ea8b8ecf3 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -1,7 +1,16 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use crate::args::resolve_no_prompt; -use crate::util::fs::canonicalize_path; +use std::collections::HashSet; +use std::env; +use std::ffi::OsString; +use std::net::SocketAddr; +use std::num::NonZeroU32; +use std::num::NonZeroU8; +use std::num::NonZeroUsize; +use std::path::Path; +use std::path::PathBuf; +use std::str::FromStr; + use clap::builder::styling::AnsiColor; use clap::builder::FalseyValueParser; use clap::error::ErrorKind; @@ -23,22 +32,16 @@ use deno_core::normalize_path; use deno_core::resolve_url_or_path; use deno_core::url::Url; use deno_graph::GraphKind; +use deno_runtime::colors; use deno_runtime::deno_permissions::parse_sys_kind; use deno_runtime::deno_permissions::PermissionsOptions; use log::debug; use log::Level; use serde::Deserialize; use serde::Serialize; -use std::collections::HashSet; -use std::env; -use std::ffi::OsString; -use std::net::SocketAddr; -use std::num::NonZeroU32; -use std::num::NonZeroU8; -use std::num::NonZeroUsize; -use std::path::Path; -use std::path::PathBuf; -use std::str::FromStr; + +use crate::args::resolve_no_prompt; +use crate::util::fs::canonicalize_path; use super::flags_net; @@ -681,6 +684,54 @@ impl PermissionFlags { Ok(Some(new_paths)) } + fn resolve_allow_run( + allow_run: &[String], + ) -> Result, AnyError> { + let mut new_allow_run = Vec::with_capacity(allow_run.len()); + for command_name in allow_run { + if command_name.is_empty() { + bail!("Empty command name not allowed in --allow-run=...") + } + let command_path_result = which::which(command_name); + match command_path_result { + Ok(command_path) => new_allow_run.push(command_path), + Err(err) => { + log::info!( + "{} Failed to resolve '{}' for allow-run: {}", + colors::gray("Info"), + command_name, + err + ); + } + } + } + Ok(new_allow_run) + } + + let mut deny_write = + convert_option_str_to_path_buf(&self.deny_write, initial_cwd)?; + let allow_run = self + .allow_run + .as_ref() + .and_then(|raw_allow_run| match resolve_allow_run(raw_allow_run) { + Ok(resolved_allow_run) => { + if resolved_allow_run.is_empty() && !raw_allow_run.is_empty() { + None // convert to no permissions if now empty + } else { + Some(Ok(resolved_allow_run)) + } + } + Err(err) => Some(Err(err)), + }) + .transpose()?; + // add the allow_run list to deno_write + if let Some(allow_run_vec) = &allow_run { + if !allow_run_vec.is_empty() { + let deno_write = deny_write.get_or_insert_with(Vec::new); + deno_write.extend(allow_run_vec.iter().cloned()); + } + } + Ok(PermissionsOptions { allow_all: self.allow_all, allow_env: self.allow_env.clone(), @@ -694,7 +745,7 @@ impl PermissionFlags { initial_cwd, )?, deny_read: convert_option_str_to_path_buf(&self.deny_read, initial_cwd)?, - allow_run: self.allow_run.clone(), + allow_run, deny_run: self.deny_run.clone(), allow_sys: self.allow_sys.clone(), deny_sys: self.deny_sys.clone(), @@ -702,10 +753,7 @@ impl PermissionFlags { &self.allow_write, initial_cwd, )?, - deny_write: convert_option_str_to_path_buf( - &self.deny_write, - initial_cwd, - )?, + deny_write, prompt: !resolve_no_prompt(self), }) } diff --git a/cli/task_runner.rs b/cli/task_runner.rs index e8937590db..ab7163bc93 100644 --- a/cli/task_runner.rs +++ b/cli/task_runner.rs @@ -213,8 +213,8 @@ impl ShellCommand for NodeGypCommand { ) -> LocalBoxFuture<'static, ExecuteResult> { // at the moment this shell command is just to give a warning if node-gyp is not found // in the future, we could try to run/install node-gyp for the user with deno - if which::which("node-gyp").is_err() { - log::warn!("{}: node-gyp was used in a script, but was not listed as a dependency. Either add it as a dependency or install it globally (e.g. `npm install -g node-gyp`)", crate::colors::yellow("warning")); + if context.state.resolve_command_path("node-gyp").is_err() { + log::warn!("{} node-gyp was used in a script, but was not listed as a dependency. Either add it as a dependency or install it globally (e.g. `npm install -g node-gyp`)", crate::colors::yellow("Warning")); } ExecutableCommand::new( "node-gyp".to_string(), diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs index 11e4390513..eb53151ced 100644 --- a/runtime/ops/process.rs +++ b/runtime/ops/process.rs @@ -21,6 +21,9 @@ use serde::Deserialize; use serde::Serialize; use std::borrow::Cow; use std::cell::RefCell; +use std::collections::HashMap; +use std::path::Path; +use std::path::PathBuf; use std::process::ExitStatus; use std::rc::Rc; use tokio::process::Command; @@ -228,63 +231,15 @@ fn create_command( mut args: SpawnArgs, api_name: &str, ) -> Result { - fn get_requires_allow_all_env_var(args: &SpawnArgs) -> Option> { - fn requires_allow_all(key: &str) -> bool { - let key = key.trim(); - // we could be more targted here, but there are quite a lot of - // LD_* and DYLD_* env variables - key.starts_with("LD_") || key.starts_with("DYLD_") - } - - /// Checks if the user set this env var to an empty - /// string in order to clear it. - fn args_has_empty_env_value(args: &SpawnArgs, key_name: &str) -> bool { - args - .env - .iter() - .find(|(k, _)| k == key_name) - .map(|(_, v)| v.trim().is_empty()) - .unwrap_or(false) - } - - if let Some((key, _)) = args - .env - .iter() - .find(|(k, v)| requires_allow_all(k) && !v.trim().is_empty()) - { - return Some(key.into()); - } - - if !args.clear_env { - if let Some((key, _)) = std::env::vars().find(|(k, v)| { - requires_allow_all(k) - && !v.trim().is_empty() - && !args_has_empty_env_value(args, k) - }) { - return Some(key.into()); - } - } - - None - } - - { - let permissions = state.borrow_mut::(); - permissions.check_run(&args.cmd, api_name)?; - if permissions.check_run_all(api_name).is_err() { - // error the same on all platforms - if let Some(name) = get_requires_allow_all_env_var(&args) { - // we don't allow users to launch subprocesses with any LD_ or DYLD_* - // env vars set because this allows executing code (ex. LD_PRELOAD) - return Err(deno_core::error::custom_error( - "PermissionDenied", - format!("Requires --allow-all permissions to spawn subprocess with {} environment variable.", name) - )); - } - } - } - - let mut command = std::process::Command::new(args.cmd); + let (cmd, run_env) = compute_run_cmd_and_check_permissions( + &args.cmd, + args.cwd.as_deref(), + &args.env, + args.clear_env, + state, + api_name, + )?; + let mut command = std::process::Command::new(cmd); #[cfg(windows)] if args.windows_raw_arguments { @@ -298,14 +253,9 @@ fn create_command( #[cfg(not(windows))] command.args(args.args); - if let Some(cwd) = args.cwd { - command.current_dir(cwd); - } - - if args.clear_env { - command.env_clear(); - } - command.envs(args.env); + command.current_dir(run_env.cwd); + command.env_clear(); + command.envs(run_env.envs); #[cfg(unix)] if let Some(gid) = args.gid { @@ -554,6 +504,133 @@ fn close_raw_handle(handle: deno_io::RawBiPipeHandle) { } } +fn compute_run_cmd_and_check_permissions( + arg_cmd: &str, + arg_cwd: Option<&str>, + arg_envs: &[(String, String)], + arg_clear_env: bool, + state: &mut OpState, + api_name: &str, +) -> Result<(PathBuf, RunEnv), AnyError> { + let run_env = compute_run_env(arg_cwd, arg_envs, arg_clear_env) + .with_context(|| format!("Failed to spawn '{}'", arg_cmd))?; + let cmd = resolve_cmd(arg_cmd, &run_env) + .with_context(|| format!("Failed to spawn '{}'", arg_cmd))?; + check_run_permission(state, &cmd, &run_env, api_name)?; + Ok((cmd, run_env)) +} + +struct RunEnv { + envs: HashMap, + cwd: PathBuf, +} + +/// Computes the current environment, which will then be used to inform +/// permissions and finally spawning. This is very important to compute +/// ahead of time so that the environment used to verify permissions is +/// the same environment used to spawn the sub command. This protects against +/// someone doing timing attacks by changing the environment on a worker. +fn compute_run_env( + arg_cwd: Option<&str>, + arg_envs: &[(String, String)], + arg_clear_env: bool, +) -> Result { + #[allow(clippy::disallowed_methods)] + let cwd = std::env::current_dir().context("failed resolving cwd")?; + let cwd = arg_cwd + .map(|cwd_arg| resolve_path(cwd_arg, &cwd)) + .unwrap_or(cwd); + let envs = if arg_clear_env { + arg_envs.iter().cloned().collect() + } else { + let mut envs = std::env::vars().collect::>(); + for (key, value) in arg_envs { + envs.insert(key.clone(), value.clone()); + } + envs + }; + Ok(RunEnv { envs, cwd }) +} + +fn resolve_cmd(cmd: &str, env: &RunEnv) -> Result { + let is_path = cmd.contains('/'); + #[cfg(windows)] + let is_path = is_path || cmd.contains('\\') || Path::new(&cmd).is_absolute(); + if is_path { + Ok(resolve_path(cmd, &env.cwd)) + } else { + let path = env.envs.get("PATH").or_else(|| { + if cfg!(windows) { + env.envs.iter().find_map(|(k, v)| { + if k.to_uppercase() == "PATH" { + Some(v) + } else { + None + } + }) + } else { + None + } + }); + match which::which_in(cmd, path, &env.cwd) { + Ok(cmd) => Ok(cmd), + Err(which::Error::CannotFindBinaryPath) => { + Err(std::io::Error::from(std::io::ErrorKind::NotFound).into()) + } + Err(err) => Err(err.into()), + } + } +} + +fn resolve_path(path: &str, cwd: &Path) -> PathBuf { + deno_core::normalize_path(cwd.join(path)) +} + +fn check_run_permission( + state: &mut OpState, + cmd: &Path, + run_env: &RunEnv, + api_name: &str, +) -> Result<(), AnyError> { + let permissions = state.borrow_mut::(); + if !permissions.query_run_all(api_name) { + // error the same on all platforms + let env_var_names = get_requires_allow_all_env_vars(run_env); + if !env_var_names.is_empty() { + // we don't allow users to launch subprocesses with any LD_ or DYLD_* + // env vars set because this allows executing code (ex. LD_PRELOAD) + return Err(deno_core::error::custom_error( + "PermissionDenied", + format!( + "Requires --allow-all permissions to spawn subprocess with {} environment variable{}.", + env_var_names.join(", "), + if env_var_names.len() != 1 { "s" } else { "" } + ) + )); + } + permissions.check_run(cmd, api_name)?; + } + Ok(()) +} + +fn get_requires_allow_all_env_vars(env: &RunEnv) -> Vec<&str> { + fn requires_allow_all(key: &str) -> bool { + let key = key.trim(); + // we could be more targted here, but there are quite a lot of + // LD_* and DYLD_* env variables + key.starts_with("LD_") || key.starts_with("DYLD_") + } + + let mut found_envs = env + .envs + .iter() + .filter(|(k, v)| requires_allow_all(k) && !v.trim().is_empty()) + .map(|(k, _)| k.as_str()) + .collect::>(); + found_envs.sort(); + found_envs +} + #[op2] #[serde] fn op_spawn_child( @@ -634,6 +711,8 @@ fn op_spawn_kill( } mod deprecated { + use deno_core::anyhow; + use super::*; #[derive(Deserialize)] @@ -681,20 +760,24 @@ mod deprecated { #[serde] run_args: RunArgs, ) -> Result { let args = run_args.cmd; - state - .borrow_mut::() - .check_run(&args[0], "Deno.run()")?; - let env = run_args.env; - let cwd = run_args.cwd; + let cmd = args.first().ok_or_else(|| anyhow::anyhow!("Missing cmd"))?; + let (cmd, run_env) = compute_run_cmd_and_check_permissions( + cmd, + run_args.cwd.as_deref(), + &run_args.env, + /* clear env */ false, + state, + "Deno.run()", + )?; - let mut c = Command::new(args.first().unwrap()); - (1..args.len()).for_each(|i| { - let arg = args.get(i).unwrap(); + let mut c = Command::new(cmd); + for arg in args.iter().skip(1) { c.arg(arg); - }); - cwd.map(|d| c.current_dir(d)); + } + c.current_dir(run_env.cwd); - for (key, value) in &env { + c.env_clear(); + for (key, value) in run_env.envs { c.env(key, value); } diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs index 7227bebf8c..2eacd8bcc4 100644 --- a/runtime/permissions/lib.rs +++ b/runtime/permissions/lib.rs @@ -32,7 +32,6 @@ use std::path::PathBuf; use std::str::FromStr; use std::string::ToString; use std::sync::Arc; -use which::which; pub mod prompter; use prompter::permission_prompt; @@ -317,7 +316,7 @@ pub trait Descriptor: Eq + Clone + Hash { /// Parse this descriptor from a list of Self::Arg, which may have been converted from /// command-line strings. - fn parse(list: &Option>) -> Result, AnyError>; + fn parse(list: Option<&[Self::Arg]>) -> Result, AnyError>; /// Generic check function to check this descriptor against a `UnaryPermission`. fn check_in_permission( @@ -333,9 +332,6 @@ pub trait Descriptor: Eq + Clone + Hash { fn stronger_than(&self, other: &Self) -> bool { self == other } - fn aliases(&self) -> Vec { - vec![] - } } #[derive(Clone, Debug, Eq, PartialEq)] @@ -423,43 +419,33 @@ impl UnaryPermission { desc: Option<&T>, allow_partial: AllowPartial, ) -> PermissionState { - let aliases = desc.map_or(vec![], T::aliases); - for desc in [desc] - .into_iter() - .chain(aliases.iter().map(Some).collect::>()) - { - let state = if self.is_flag_denied(desc) || self.is_prompt_denied(desc) { - PermissionState::Denied - } else if self.is_granted(desc) { - match allow_partial { - AllowPartial::TreatAsGranted => PermissionState::Granted, - AllowPartial::TreatAsDenied => { - if self.is_partial_flag_denied(desc) { - PermissionState::Denied - } else { - PermissionState::Granted - } - } - AllowPartial::TreatAsPartialGranted => { - if self.is_partial_flag_denied(desc) { - PermissionState::GrantedPartial - } else { - PermissionState::Granted - } + if self.is_flag_denied(desc) || self.is_prompt_denied(desc) { + PermissionState::Denied + } else if self.is_granted(desc) { + match allow_partial { + AllowPartial::TreatAsGranted => PermissionState::Granted, + AllowPartial::TreatAsDenied => { + if self.is_partial_flag_denied(desc) { + PermissionState::Denied + } else { + PermissionState::Granted + } + } + AllowPartial::TreatAsPartialGranted => { + if self.is_partial_flag_denied(desc) { + PermissionState::GrantedPartial + } else { + PermissionState::Granted } } - } else if matches!(allow_partial, AllowPartial::TreatAsDenied) - && self.is_partial_flag_denied(desc) - { - PermissionState::Denied - } else { - PermissionState::Prompt - }; - if state != PermissionState::Prompt { - return state; } + } else if matches!(allow_partial, AllowPartial::TreatAsDenied) + && self.is_partial_flag_denied(desc) + { + PermissionState::Denied + } else { + PermissionState::Prompt } - PermissionState::Prompt } fn request_desc( @@ -512,9 +498,6 @@ impl UnaryPermission { match desc { Some(desc) => { self.granted_list.retain(|v| !v.stronger_than(desc)); - for alias in desc.aliases() { - self.granted_list.retain(|v| !v.stronger_than(&alias)); - } } None => { self.granted_global = false; @@ -582,11 +565,7 @@ impl UnaryPermission { ) { match desc { Some(desc) => { - let aliases = desc.aliases(); list.insert(desc); - for alias in aliases { - list.insert(alias); - } } None => *list_global = true, } @@ -612,7 +591,7 @@ impl UnaryPermission { ChildUnaryPermissionArg::GrantedList(granted_list) => { let granted: Vec = granted_list.into_iter().map(From::from).collect(); - perms.granted_list = T::parse(&Some(granted))?; + perms.granted_list = T::parse(Some(&granted))?; if !perms .granted_list .iter() @@ -649,7 +628,7 @@ impl Descriptor for ReadDescriptor { perm.check_desc(Some(self), true, api_name, || None) } - fn parse(args: &Option>) -> Result, AnyError> { + fn parse(args: Option<&[Self::Arg]>) -> Result, AnyError> { parse_path_list(args, ReadDescriptor) } @@ -681,7 +660,7 @@ impl Descriptor for WriteDescriptor { perm.check_desc(Some(self), true, api_name, || None) } - fn parse(args: &Option>) -> Result, AnyError> { + fn parse(args: Option<&[Self::Arg]>) -> Result, AnyError> { parse_path_list(args, WriteDescriptor) } @@ -754,7 +733,7 @@ impl Descriptor for NetDescriptor { perm.check_desc(Some(self), false, api_name, || None) } - fn parse(args: &Option>) -> Result, AnyError> { + fn parse(args: Option<&[Self::Arg]>) -> Result, AnyError> { parse_net_list(args) } @@ -864,7 +843,7 @@ impl Descriptor for EnvDescriptor { perm.check_desc(Some(self), false, api_name, || None) } - fn parse(list: &Option>) -> Result, AnyError> { + fn parse(list: Option<&[Self::Arg]>) -> Result, AnyError> { parse_env_list(list) } @@ -883,6 +862,11 @@ impl AsRef for EnvDescriptor { } } +pub enum RunDescriptorArg { + Name(String), + Path(PathBuf), +} + #[derive(Clone, Eq, PartialEq, Hash, Debug)] pub enum RunDescriptor { /// Warning: You may want to construct with `RunDescriptor::from()` for case @@ -893,8 +877,26 @@ pub enum RunDescriptor { Path(PathBuf), } +impl From for RunDescriptorArg { + fn from(s: String) -> Self { + #[cfg(windows)] + let s = s.to_lowercase(); + let is_path = s.contains('/'); + #[cfg(windows)] + let is_path = is_path || s.contains('\\') || Path::new(&s).is_absolute(); + if is_path { + Self::Path(resolve_from_cwd(Path::new(&s)).unwrap()) + } else { + match which::which(&s) { + Ok(path) => Self::Path(path), + Err(_) => Self::Name(s), + } + } + } +} + impl Descriptor for RunDescriptor { - type Arg = String; + type Arg = RunDescriptorArg; fn check_in_permission( &self, @@ -905,7 +907,7 @@ impl Descriptor for RunDescriptor { perm.check_desc(Some(self), false, api_name, || None) } - fn parse(args: &Option>) -> Result, AnyError> { + fn parse(args: Option<&[Self::Arg]>) -> Result, AnyError> { parse_run_list(args) } @@ -916,16 +918,6 @@ impl Descriptor for RunDescriptor { fn name(&self) -> Cow { Cow::from(self.to_string()) } - - fn aliases(&self) -> Vec { - match self { - RunDescriptor::Name(name) => match which(name) { - Ok(path) => vec![RunDescriptor::Path(path)], - Err(_) => vec![], - }, - RunDescriptor::Path(_) => vec![], - } - } } impl From for RunDescriptor { @@ -938,7 +930,10 @@ impl From for RunDescriptor { if is_path { Self::Path(resolve_from_cwd(Path::new(&s)).unwrap()) } else { - Self::Name(s) + match which::which(&s) { + Ok(path) => Self::Path(path), + Err(_) => Self::Name(s), + } } } } @@ -947,11 +942,7 @@ impl From for RunDescriptor { fn from(p: PathBuf) -> Self { #[cfg(windows)] let p = PathBuf::from(p.to_string_lossy().to_string().to_lowercase()); - if p.is_absolute() { - Self::Path(p) - } else { - Self::Path(resolve_from_cwd(&p).unwrap()) - } + Self::Path(resolve_from_cwd(&p).unwrap()) } } @@ -988,7 +979,7 @@ impl Descriptor for SysDescriptor { perm.check_desc(Some(self), false, api_name, || None) } - fn parse(list: &Option>) -> Result, AnyError> { + fn parse(list: Option<&[Self::Arg]>) -> Result, AnyError> { parse_sys_list(list) } @@ -1025,7 +1016,7 @@ impl Descriptor for FfiDescriptor { perm.check_desc(Some(self), true, api_name, || None) } - fn parse(list: &Option>) -> Result, AnyError> { + fn parse(list: Option<&[Self::Arg]>) -> Result, AnyError> { parse_path_list(list, FfiDescriptor) } @@ -1330,15 +1321,16 @@ impl UnaryPermission { pub fn check( &mut self, - cmd: &str, + cmd: &Path, api_name: Option<&str>, ) -> Result<(), AnyError> { + debug_assert!(cmd.is_absolute()); skip_check_if_is_permission_fully_granted!(self); self.check_desc( - Some(&RunDescriptor::from(cmd.to_string())), + Some(&RunDescriptor::Path(cmd.to_path_buf())), false, api_name, - || Some(format!("\"{}\"", cmd)), + || Some(format!("\"{}\"", cmd.display())), ) } @@ -1346,6 +1338,21 @@ impl UnaryPermission { skip_check_if_is_permission_fully_granted!(self); self.check_desc(None, false, api_name, || None) } + + /// Queries without prompting + pub fn query_all(&mut self, api_name: Option<&str>) -> bool { + if self.is_allow_all() { + return true; + } + let (result, _prompted, _is_allow_all) = + self.query_desc(None, AllowPartial::TreatAsDenied).check2( + RunDescriptor::flag_name(), + api_name, + || None, + /* prompt */ false, + ); + result.is_ok() + } } impl UnaryPermission { @@ -1429,7 +1436,7 @@ pub struct PermissionsOptions { pub deny_ffi: Option>, pub allow_read: Option>, pub deny_read: Option>, - pub allow_run: Option>, + pub allow_run: Option>, pub deny_run: Option>, pub allow_sys: Option>, pub deny_sys: Option>, @@ -1440,8 +1447,8 @@ pub struct PermissionsOptions { impl Permissions { pub fn new_unary( - allow_list: &Option>, - deny_list: &Option>, + allow_list: Option<&[T::Arg]>, + deny_list: Option<&[T::Arg]>, prompt: bool, ) -> Result, AnyError> where @@ -1470,38 +1477,54 @@ impl Permissions { pub fn from_options(opts: &PermissionsOptions) -> Result { Ok(Self { read: Permissions::new_unary( - &opts.allow_read, - &opts.deny_read, + opts.allow_read.as_deref(), + opts.deny_read.as_deref(), opts.prompt, )?, write: Permissions::new_unary( - &opts.allow_write, - &opts.deny_write, + opts.allow_write.as_deref(), + opts.deny_write.as_deref(), opts.prompt, )?, net: Permissions::new_unary( - &opts.allow_net, - &opts.deny_net, + opts.allow_net.as_deref(), + opts.deny_net.as_deref(), opts.prompt, )?, env: Permissions::new_unary( - &opts.allow_env, - &opts.deny_env, + opts.allow_env.as_deref(), + opts.deny_env.as_deref(), opts.prompt, )?, sys: Permissions::new_unary( - &opts.allow_sys, - &opts.deny_sys, + opts.allow_sys.as_deref(), + opts.deny_sys.as_deref(), opts.prompt, )?, run: Permissions::new_unary( - &opts.allow_run, - &opts.deny_run, + opts + .allow_run + .as_ref() + .map(|d| { + d.iter() + .map(|s| RunDescriptorArg::Path(s.clone())) + .collect::>() + }) + .as_deref(), + opts + .deny_run + .as_ref() + .map(|d| { + d.iter() + .map(|s| RunDescriptorArg::from(s.clone())) + .collect::>() + }) + .as_deref(), opts.prompt, )?, ffi: Permissions::new_unary( - &opts.allow_ffi, - &opts.deny_ffi, + opts.allow_ffi.as_deref(), + opts.deny_ffi.as_deref(), opts.prompt, )?, all: Permissions::new_all(opts.allow_all), @@ -1534,13 +1557,13 @@ impl Permissions { fn none(prompt: bool) -> Self { Self { - read: Permissions::new_unary(&None, &None, prompt).unwrap(), - write: Permissions::new_unary(&None, &None, prompt).unwrap(), - net: Permissions::new_unary(&None, &None, prompt).unwrap(), - env: Permissions::new_unary(&None, &None, prompt).unwrap(), - sys: Permissions::new_unary(&None, &None, prompt).unwrap(), - run: Permissions::new_unary(&None, &None, prompt).unwrap(), - ffi: Permissions::new_unary(&None, &None, prompt).unwrap(), + read: Permissions::new_unary(None, None, prompt).unwrap(), + write: Permissions::new_unary(None, None, prompt).unwrap(), + net: Permissions::new_unary(None, None, prompt).unwrap(), + env: Permissions::new_unary(None, None, prompt).unwrap(), + sys: Permissions::new_unary(None, None, prompt).unwrap(), + run: Permissions::new_unary(None, None, prompt).unwrap(), + ffi: Permissions::new_unary(None, None, prompt).unwrap(), all: Permissions::new_all(false), } } @@ -1669,7 +1692,7 @@ impl PermissionsContainer { #[inline(always)] pub fn check_run( &mut self, - cmd: &str, + cmd: &Path, api_name: &str, ) -> Result<(), AnyError> { self.0.lock().run.check(cmd, Some(api_name)) @@ -1680,6 +1703,11 @@ impl PermissionsContainer { self.0.lock().run.check_all(Some(api_name)) } + #[inline(always)] + pub fn query_run_all(&mut self, api_name: &str) -> bool { + self.0.lock().run.query_all(Some(api_name)) + } + #[inline(always)] pub fn check_sys(&self, kind: &str, api_name: &str) -> Result<(), AnyError> { self.0.lock().sys.check(kind, Some(api_name)) @@ -1871,12 +1899,12 @@ const fn unit_permission_from_flag_bools( } } -fn global_from_option(flag: &Option>) -> bool { +fn global_from_option(flag: Option<&[T]>) -> bool { matches!(flag, Some(v) if v.is_empty()) } fn parse_net_list( - list: &Option>, + list: Option<&[String]>, ) -> Result, AnyError> { if let Some(v) = list { v.iter() @@ -1888,7 +1916,7 @@ fn parse_net_list( } fn parse_env_list( - list: &Option>, + list: Option<&[String]>, ) -> Result, AnyError> { if let Some(v) = list { v.iter() @@ -1906,7 +1934,7 @@ fn parse_env_list( } fn parse_path_list( - list: &Option>, + list: Option<&[PathBuf]>, f: fn(PathBuf) -> T, ) -> Result, AnyError> { if let Some(v) = list { @@ -1925,7 +1953,7 @@ fn parse_path_list( } fn parse_sys_list( - list: &Option>, + list: Option<&[String]>, ) -> Result, AnyError> { if let Some(v) = list { v.iter() @@ -1943,22 +1971,19 @@ fn parse_sys_list( } fn parse_run_list( - list: &Option>, + list: Option<&[RunDescriptorArg]>, ) -> Result, AnyError> { - let mut result = HashSet::new(); - if let Some(v) = list { - for s in v { - if s.is_empty() { - return Err(AnyError::msg("Empty path is not allowed")); - } else { - let desc = RunDescriptor::from(s.to_string()); - let aliases = desc.aliases(); - result.insert(desc); - result.extend(aliases); - } - } - } - Ok(result) + let Some(v) = list else { + return Ok(HashSet::new()); + }; + Ok( + v.iter() + .map(|arg| match arg { + RunDescriptorArg::Name(s) => RunDescriptor::Name(s.clone()), + RunDescriptorArg::Path(l) => RunDescriptor::Path(l.clone()), + }) + .collect(), + ) } fn escalation_error() -> AnyError { @@ -2298,6 +2323,9 @@ mod tests { macro_rules! svec { ($($x:expr),*) => (vec![$($x.to_string()),*]); } + macro_rules! sarr { + ($($x:expr),*) => ([$($x.to_string()),*]); + } #[test] fn check_paths() { @@ -2678,94 +2706,88 @@ mod tests { set_prompter(Box::new(TestPrompter)); let perms1 = Permissions::allow_all(); let perms2 = Permissions { - read: Permissions::new_unary( - &Some(vec![PathBuf::from("/foo")]), - &None, - false, - ) - .unwrap(), + read: Permissions::new_unary(Some(&[PathBuf::from("/foo")]), None, false) + .unwrap(), write: Permissions::new_unary( - &Some(vec![PathBuf::from("/foo")]), - &None, + Some(&[PathBuf::from("/foo")]), + None, false, ) .unwrap(), - ffi: Permissions::new_unary( - &Some(vec![PathBuf::from("/foo")]), - &None, + ffi: Permissions::new_unary(Some(&[PathBuf::from("/foo")]), None, false) + .unwrap(), + net: Permissions::new_unary(Some(&sarr!["127.0.0.1:8000"]), None, false) + .unwrap(), + env: Permissions::new_unary(Some(&sarr!["HOME"]), None, false).unwrap(), + sys: Permissions::new_unary(Some(&sarr!["hostname"]), None, false) + .unwrap(), + run: Permissions::new_unary( + Some(&["deno".to_string().into()]), + None, false, ) .unwrap(), - net: Permissions::new_unary(&Some(svec!["127.0.0.1:8000"]), &None, false) - .unwrap(), - env: Permissions::new_unary(&Some(svec!["HOME"]), &None, false).unwrap(), - sys: Permissions::new_unary(&Some(svec!["hostname"]), &None, false) - .unwrap(), - run: Permissions::new_unary(&Some(svec!["deno"]), &None, false).unwrap(), all: Permissions::new_all(false), }; let perms3 = Permissions { - read: Permissions::new_unary( - &None, - &Some(vec![PathBuf::from("/foo")]), - false, - ) - .unwrap(), + read: Permissions::new_unary(None, Some(&[PathBuf::from("/foo")]), false) + .unwrap(), write: Permissions::new_unary( - &None, - &Some(vec![PathBuf::from("/foo")]), + None, + Some(&[PathBuf::from("/foo")]), false, ) .unwrap(), - ffi: Permissions::new_unary( - &None, - &Some(vec![PathBuf::from("/foo")]), + ffi: Permissions::new_unary(None, Some(&[PathBuf::from("/foo")]), false) + .unwrap(), + net: Permissions::new_unary(None, Some(&sarr!["127.0.0.1:8000"]), false) + .unwrap(), + env: Permissions::new_unary(None, Some(&sarr!["HOME"]), false).unwrap(), + sys: Permissions::new_unary(None, Some(&sarr!["hostname"]), false) + .unwrap(), + run: Permissions::new_unary( + None, + Some(&["deno".to_string().into()]), false, ) .unwrap(), - net: Permissions::new_unary(&None, &Some(svec!["127.0.0.1:8000"]), false) - .unwrap(), - env: Permissions::new_unary(&None, &Some(svec!["HOME"]), false).unwrap(), - sys: Permissions::new_unary(&None, &Some(svec!["hostname"]), false) - .unwrap(), - run: Permissions::new_unary(&None, &Some(svec!["deno"]), false).unwrap(), all: Permissions::new_all(false), }; let perms4 = Permissions { read: Permissions::new_unary( - &Some(vec![]), - &Some(vec![PathBuf::from("/foo")]), + Some(&[]), + Some(&[PathBuf::from("/foo")]), false, ) .unwrap(), write: Permissions::new_unary( - &Some(vec![]), - &Some(vec![PathBuf::from("/foo")]), + Some(&[]), + Some(&[PathBuf::from("/foo")]), false, ) .unwrap(), ffi: Permissions::new_unary( - &Some(vec![]), - &Some(vec![PathBuf::from("/foo")]), + Some(&[]), + Some(&[PathBuf::from("/foo")]), false, ) .unwrap(), net: Permissions::new_unary( - &Some(vec![]), - &Some(svec!["127.0.0.1:8000"]), + Some(&[]), + Some(&sarr!["127.0.0.1:8000"]), false, ) .unwrap(), - env: Permissions::new_unary(&Some(vec![]), &Some(svec!["HOME"]), false) + env: Permissions::new_unary(Some(&[]), Some(&sarr!["HOME"]), false) .unwrap(), - sys: Permissions::new_unary( - &Some(vec![]), - &Some(svec!["hostname"]), + sys: Permissions::new_unary(Some(&[]), Some(&sarr!["hostname"]), false) + .unwrap(), + run: Permissions::new_unary( + Some(&[]), + Some(&["deno".to_string().into()]), false, ) .unwrap(), - run: Permissions::new_unary(&Some(vec![]), &Some(svec!["deno"]), false) - .unwrap(), all: Permissions::new_all(false), }; #[rustfmt::skip] @@ -2894,33 +2916,38 @@ mod tests { set_prompter(Box::new(TestPrompter)); let mut perms = Permissions { read: Permissions::new_unary( - &Some(vec![PathBuf::from("/foo"), PathBuf::from("/foo/baz")]), - &None, + Some(&[PathBuf::from("/foo"), PathBuf::from("/foo/baz")]), + None, false, ) .unwrap(), write: Permissions::new_unary( - &Some(vec![PathBuf::from("/foo"), PathBuf::from("/foo/baz")]), - &None, + Some(&[PathBuf::from("/foo"), PathBuf::from("/foo/baz")]), + None, false, ) .unwrap(), ffi: Permissions::new_unary( - &Some(vec![PathBuf::from("/foo"), PathBuf::from("/foo/baz")]), - &None, + Some(&[PathBuf::from("/foo"), PathBuf::from("/foo/baz")]), + None, false, ) .unwrap(), net: Permissions::new_unary( - &Some(svec!["127.0.0.1", "127.0.0.1:8000"]), - &None, + Some(&sarr!["127.0.0.1", "127.0.0.1:8000"]), + None, false, ) .unwrap(), - env: Permissions::new_unary(&Some(svec!["HOME"]), &None, false).unwrap(), - sys: Permissions::new_unary(&Some(svec!["hostname"]), &None, false) + env: Permissions::new_unary(Some(&sarr!["HOME"]), None, false).unwrap(), + sys: Permissions::new_unary(Some(&sarr!["hostname"]), None, false) .unwrap(), - run: Permissions::new_unary(&Some(svec!["deno"]), &None, false).unwrap(), + run: Permissions::new_unary( + Some(&["deno".to_string().into()]), + None, + false, + ) + .unwrap(), all: Permissions::new_all(false), }; #[rustfmt::skip] @@ -3006,11 +3033,13 @@ mod tests { .check(&NetDescriptor("deno.land".parse().unwrap(), None), None) .is_err()); + #[allow(clippy::disallowed_methods)] + let cwd = std::env::current_dir().unwrap(); prompt_value.set(true); - assert!(perms.run.check("cat", None).is_ok()); + assert!(perms.run.check(&cwd.join("cat"), None).is_ok()); prompt_value.set(false); - assert!(perms.run.check("cat", None).is_ok()); - assert!(perms.run.check("ls", None).is_err()); + assert!(perms.run.check(&cwd.join("cat"), None).is_ok()); + assert!(perms.run.check(&cwd.join("ls"), None).is_err()); prompt_value.set(true); assert!(perms.env.check("HOME", None).is_ok()); @@ -3102,12 +3131,14 @@ mod tests { .is_ok()); prompt_value.set(false); - assert!(perms.run.check("cat", None).is_err()); + #[allow(clippy::disallowed_methods)] + let cwd = std::env::current_dir().unwrap(); + assert!(perms.run.check(&cwd.join("cat"), None).is_err()); prompt_value.set(true); - assert!(perms.run.check("cat", None).is_err()); - assert!(perms.run.check("ls", None).is_ok()); + assert!(perms.run.check(&cwd.join("cat"), None).is_err()); + assert!(perms.run.check(&cwd.join("ls"), None).is_ok()); prompt_value.set(false); - assert!(perms.run.check("ls", None).is_ok()); + assert!(perms.run.check(&cwd.join("ls"), None).is_ok()); prompt_value.set(false); assert!(perms.env.check("HOME", None).is_err()); @@ -3134,7 +3165,7 @@ mod tests { let mut perms = Permissions::allow_all(); perms.env = UnaryPermission { granted_global: false, - ..Permissions::new_unary(&Some(svec!["HOME"]), &None, false).unwrap() + ..Permissions::new_unary(Some(&sarr!["HOME"]), None, false).unwrap() }; prompt_value.set(true); @@ -3150,14 +3181,14 @@ mod tests { fn test_check_partial_denied() { let mut perms = Permissions { read: Permissions::new_unary( - &Some(vec![]), - &Some(vec![PathBuf::from("/foo/bar")]), + Some(&[]), + Some(&[PathBuf::from("/foo/bar")]), false, ) .unwrap(), write: Permissions::new_unary( - &Some(vec![]), - &Some(vec![PathBuf::from("/foo/bar")]), + Some(&[]), + Some(&[PathBuf::from("/foo/bar")]), false, ) .unwrap(), @@ -3175,8 +3206,8 @@ mod tests { fn test_net_fully_qualified_domain_name() { let mut perms = Permissions { net: Permissions::new_unary( - &Some(vec!["allowed.domain".to_string(), "1.1.1.1".to_string()]), - &Some(vec!["denied.domain".to_string(), "2.2.2.2".to_string()]), + Some(&["allowed.domain".to_string(), "1.1.1.1".to_string()]), + Some(&["denied.domain".to_string(), "2.2.2.2".to_string()]), false, ) .unwrap(), @@ -3341,8 +3372,8 @@ mod tests { fn test_create_child_permissions() { set_prompter(Box::new(TestPrompter)); let mut main_perms = Permissions { - env: Permissions::new_unary(&Some(vec![]), &None, false).unwrap(), - net: Permissions::new_unary(&Some(svec!["foo", "bar"]), &None, false) + env: Permissions::new_unary(Some(&[]), None, false).unwrap(), + net: Permissions::new_unary(Some(&sarr!["foo", "bar"]), None, false) .unwrap(), ..Permissions::none_without_prompt() }; @@ -3358,8 +3389,8 @@ mod tests { ) .unwrap(), Permissions { - env: Permissions::new_unary(&Some(vec![]), &None, false).unwrap(), - net: Permissions::new_unary(&Some(svec!["foo"]), &None, false).unwrap(), + env: Permissions::new_unary(Some(&[]), None, false).unwrap(), + net: Permissions::new_unary(Some(&sarr!["foo"]), None, false).unwrap(), ..Permissions::none_without_prompt() } ); @@ -3445,20 +3476,20 @@ mod tests { set_prompter(Box::new(TestPrompter)); assert!(Permissions::new_unary::( - &Some(vec![Default::default()]), - &None, + Some(&[Default::default()]), + None, false ) .is_err()); assert!(Permissions::new_unary::( - &Some(vec![Default::default()]), - &None, + Some(&[Default::default()]), + None, false ) .is_err()); assert!(Permissions::new_unary::( - &Some(vec![Default::default()]), - &None, + Some(&[Default::default()]), + None, false ) .is_err()); diff --git a/tests/integration/run_tests.rs b/tests/integration/run_tests.rs index 841ef2d182..47fcdb657b 100644 --- a/tests/integration/run_tests.rs +++ b/tests/integration/run_tests.rs @@ -3683,11 +3683,6 @@ itest!(followup_dyn_import_resolved { output: "run/followup_dyn_import_resolves/main.ts.out", }); -itest!(allow_run_allowlist_resolution { - args: "run --quiet -A allow_run_allowlist_resolution.ts", - output: "allow_run_allowlist_resolution.ts.out", -}); - itest!(unhandled_rejection { args: "run --check run/unhandled_rejection.ts", output: "run/unhandled_rejection.ts.out", @@ -4592,16 +4587,32 @@ fn permission_prompt_escapes_ansi_codes_and_control_chars() { )) }); - util::with_pty(&["repl"], |mut console| { - console.write_line_raw(r#"const boldANSI = "\u001b[1m";"#); - console.expect("undefined"); - console.write_line_raw(r#"const unboldANSI = "\u001b[22m";"#); - console.expect("undefined"); - console.write_line_raw( - r#"new Deno.Command(`${boldANSI}cat${unboldANSI}`).spawn();"#, - ); - console.expect("\u{250f} \u{26a0}\u{fe0f} Deno requests run access to \"\\u{1b}[1mcat\\u{1b}[22m\"."); - }); + // windows doesn't support backslashes in paths, so just try this on unix + if cfg!(unix) { + let context = TestContextBuilder::default().use_temp_cwd().build(); + context + .new_command() + .env("PATH", context.temp_dir().path()) + .env("DYLD_FALLBACK_LIBRARY_PATH", "") + .env("LD_LIBRARY_PATH", "") + .args_vec(["repl", "--allow-write=."]) + .with_pty(|mut console| { + console.write_line_raw(r#"const boldANSI = "\u001b[1m";"#); + console.expect("undefined"); + console.write_line_raw(r#"const unboldANSI = "\u001b[22m";"#); + console.expect("undefined"); + console.write_line_raw( + r#"Deno.writeTextFileSync(`${boldANSI}cat${unboldANSI}`, "");"#, + ); + console.expect("undefined"); + console.write_line_raw( + r#"new Deno.Command(`./${boldANSI}cat${unboldANSI}`).spawn();"#, + ); + console + .expect("\u{250f} \u{26a0}\u{fe0f} Deno requests run access to \""); + console.expect("\\u{1b}[1mcat\\u{1b}[22m\"."); // ensure escaped + }); + } } itest!(node_builtin_modules_ts { diff --git a/tests/specs/compile/permissions_denied/__test__.jsonc b/tests/specs/compile/permissions_denied/__test__.jsonc index 8f85901628..ec683ea62e 100644 --- a/tests/specs/compile/permissions_denied/__test__.jsonc +++ b/tests/specs/compile/permissions_denied/__test__.jsonc @@ -1,5 +1,9 @@ { "tempDir": true, + "envs": { + "DYLD_FALLBACK_LIBRARY_PATH": "", + "LD_LIBRARY_PATH": "" + }, "steps": [{ "if": "unix", "args": "compile --output main main.ts", diff --git a/tests/specs/compile/permissions_denied/main.out b/tests/specs/compile/permissions_denied/main.out index e9ea45c812..47a4707cc7 100644 --- a/tests/specs/compile/permissions_denied/main.out +++ b/tests/specs/compile/permissions_denied/main.out @@ -1,2 +1,2 @@ -error: Uncaught (in promise) PermissionDenied: Requires run access to "deno", specify the required permissions during compilation using `deno compile --allow-run` +error: Uncaught (in promise) PermissionDenied: Requires run access to "[WILDLINE]deno[WILDLINE]", specify the required permissions during compilation using `deno compile --allow-run` [WILDCARD] \ No newline at end of file diff --git a/tests/specs/npm/lifecycle_scripts/node_gyp_not_found.out b/tests/specs/npm/lifecycle_scripts/node_gyp_not_found.out index 65ea53d586..2f0ff11e28 100644 --- a/tests/specs/npm/lifecycle_scripts/node_gyp_not_found.out +++ b/tests/specs/npm/lifecycle_scripts/node_gyp_not_found.out @@ -3,6 +3,6 @@ Download http://localhost:4260/@denotest/node-addon-implicit-node-gyp Download http://localhost:4260/@denotest/node-addon-implicit-node-gyp/1.0.0.tgz Initialize @denotest/node-addon-implicit-node-gyp@1.0.0 [UNORDERED_END] -warning: node-gyp was used in a script, but was not listed as a dependency. Either add it as a dependency or install it globally (e.g. `npm install -g node-gyp`) +Warning node-gyp was used in a script, but was not listed as a dependency. Either add it as a dependency or install it globally (e.g. `npm install -g node-gyp`) [WILDCARD] error: script 'install' in '@denotest/node-addon-implicit-node-gyp@1.0.0' failed with exit code 1 diff --git a/tests/specs/permission/path_not_permitted/__test__.jsonc b/tests/specs/permission/path_not_permitted/__test__.jsonc new file mode 100644 index 0000000000..f10e8b389e --- /dev/null +++ b/tests/specs/permission/path_not_permitted/__test__.jsonc @@ -0,0 +1,10 @@ +{ + "tempDir": true, + "envs": { + "LD_LIBRARY_PATH": "", + "LD_PRELOAD": "", + "DYLD_FALLBACK_LIBRARY_PATH": "" + }, + "args": "run -A main.ts", + "output": "main.out" +} diff --git a/tests/specs/permission/path_not_permitted/main.out b/tests/specs/permission/path_not_permitted/main.out new file mode 100644 index 0000000000..3817c2ca51 --- /dev/null +++ b/tests/specs/permission/path_not_permitted/main.out @@ -0,0 +1,11 @@ +Running... +PermissionDenied: Requires run access to "[WILDLINE]deno[WILDLINE]", run again with the --allow-run flag + [WILDCARD] + at file:///[WILDLINE]/sub.ts:15:5 { + name: "PermissionDenied" +} +PermissionDenied: Requires run access to "[WILDLINE]deno[WILDLINE]", run again with the --allow-run flag + [WILDCARD] + at file:///[WILDLINE]/sub.ts:23:22 { + name: "PermissionDenied" +} diff --git a/tests/specs/permission/path_not_permitted/main.ts b/tests/specs/permission/path_not_permitted/main.ts new file mode 100644 index 0000000000..9e8d627f2a --- /dev/null +++ b/tests/specs/permission/path_not_permitted/main.ts @@ -0,0 +1,18 @@ +const binaryName = Deno.build.os === "windows" ? "deno.exe" : "deno"; +Deno.copyFileSync(Deno.execPath(), binaryName); + +console.log("Running..."); +new Deno.Command( + Deno.execPath(), + { + args: [ + "run", + "--allow-write", + "--allow-read", + `--allow-run=${binaryName}`, + "sub.ts", + ], + stderr: "inherit", + stdout: "inherit", + }, +).outputSync(); diff --git a/tests/specs/permission/path_not_permitted/sub.ts b/tests/specs/permission/path_not_permitted/sub.ts new file mode 100644 index 0000000000..f2b6d6b37c --- /dev/null +++ b/tests/specs/permission/path_not_permitted/sub.ts @@ -0,0 +1,34 @@ +const binaryName = Deno.build.os === "windows" ? "deno.exe" : "deno"; +const pathSep = Deno.build.os === "windows" ? "\\" : "/"; + +Deno.mkdirSync("subdir"); +Deno.copyFileSync(binaryName, "subdir/" + binaryName); + +try { + const commandResult = new Deno.Command( + binaryName, + { + env: { "PATH": Deno.cwd() + pathSep + "subdir" }, + stdout: "inherit", + stderr: "inherit", + }, + ).outputSync(); + + console.log(commandResult.code); +} catch (err) { + console.log(err); +} + +try { + const child = Deno.run( + { + cmd: [binaryName], + env: { "PATH": Deno.cwd() + pathSep + "subdir" }, + stdout: "inherit", + stderr: "inherit", + }, + ); + console.log((await child.status()).code); +} catch (err) { + console.log(err); +} diff --git a/tests/specs/permission/write_allow_binary/__test__.jsonc b/tests/specs/permission/write_allow_binary/__test__.jsonc new file mode 100644 index 0000000000..a47fed572d --- /dev/null +++ b/tests/specs/permission/write_allow_binary/__test__.jsonc @@ -0,0 +1,5 @@ +{ + "tempDir": true, + "args": "run -A main.ts", + "output": "main.out" +} diff --git a/tests/specs/permission/write_allow_binary/main.out b/tests/specs/permission/write_allow_binary/main.out new file mode 100644 index 0000000000..e7c47f2883 --- /dev/null +++ b/tests/specs/permission/write_allow_binary/main.out @@ -0,0 +1,6 @@ +Running... +error: Uncaught (in promise) PermissionDenied: Requires write access to "binary[WILDLINE]", run again with the --allow-write flag +Deno.writeTextFileSync(binaryName, ""); + ^ + at [WILDCARD] + at file:///[WILDLINE]sub.ts:3:6 diff --git a/tests/specs/permission/write_allow_binary/main.ts b/tests/specs/permission/write_allow_binary/main.ts new file mode 100644 index 0000000000..73deeab9a7 --- /dev/null +++ b/tests/specs/permission/write_allow_binary/main.ts @@ -0,0 +1,14 @@ +const binaryName = Deno.build.os === "windows" ? "binary.exe" : "binary"; +Deno.copyFileSync(Deno.execPath(), binaryName); + +console.log("Running..."); +const result = new Deno.Command( + Deno.execPath(), + { + args: ["run", "--allow-write", `--allow-run=./${binaryName}`, "sub.ts"], + stderr: "inherit", + stdout: "inherit", + }, +).outputSync(); + +console.assert(result.code == 1, "Expected failure"); diff --git a/tests/specs/permission/write_allow_binary/sub.ts b/tests/specs/permission/write_allow_binary/sub.ts new file mode 100644 index 0000000000..e865597b15 --- /dev/null +++ b/tests/specs/permission/write_allow_binary/sub.ts @@ -0,0 +1,3 @@ +const binaryName = Deno.build.os === "windows" ? "binary.exe" : "binary"; + +Deno.writeTextFileSync(binaryName, ""); diff --git a/tests/specs/run/allow_run_allowlist_resolution/__test__.jsonc b/tests/specs/run/allow_run_allowlist_resolution/__test__.jsonc new file mode 100644 index 0000000000..173e13027f --- /dev/null +++ b/tests/specs/run/allow_run_allowlist_resolution/__test__.jsonc @@ -0,0 +1,8 @@ +{ + "args": "run --quiet -A main.ts", + "output": "main.out", + "envs": { + "DYLD_FALLBACK_LIBRARY_PATH": "", + "LD_LIBRARY_PATH": "" + } +} diff --git a/tests/testdata/allow_run_allowlist_resolution.ts.out b/tests/specs/run/allow_run_allowlist_resolution/main.out similarity index 67% rename from tests/testdata/allow_run_allowlist_resolution.ts.out rename to tests/specs/run/allow_run_allowlist_resolution/main.out index 16ba6754a9..f61f9b5503 100644 --- a/tests/testdata/allow_run_allowlist_resolution.ts.out +++ b/tests/specs/run/allow_run_allowlist_resolution/main.out @@ -1,15 +1,15 @@ -PermissionStatus { state: "granted", onchange: null } -PermissionStatus { state: "granted", onchange: null } -PermissionStatus { state: "granted", onchange: null } -PermissionStatus { state: "granted", onchange: null } - -PermissionStatus { state: "granted", onchange: null } -PermissionStatus { state: "prompt", onchange: null } -PermissionStatus { state: "granted", onchange: null } -PermissionStatus { state: "prompt", onchange: null } - PermissionStatus { state: "granted", onchange: null } PermissionStatus { state: "granted", onchange: null } PermissionStatus { state: "prompt", onchange: null } PermissionStatus { state: "granted", onchange: null } - +--- +Info Failed to resolve 'deno' for allow-run: cannot find binary path +PermissionStatus { state: "prompt", onchange: null } +PermissionStatus { state: "prompt", onchange: null } +PermissionStatus { state: "prompt", onchange: null } +PermissionStatus { state: "prompt", onchange: null } +--- +PermissionStatus { state: "granted", onchange: null } +PermissionStatus { state: "granted", onchange: null } +PermissionStatus { state: "prompt", onchange: null } +PermissionStatus { state: "granted", onchange: null } diff --git a/tests/testdata/allow_run_allowlist_resolution.ts b/tests/specs/run/allow_run_allowlist_resolution/main.ts similarity index 71% rename from tests/testdata/allow_run_allowlist_resolution.ts rename to tests/specs/run/allow_run_allowlist_resolution/main.ts index c7369d928a..bf33d8cbe1 100644 --- a/tests/testdata/allow_run_allowlist_resolution.ts +++ b/tests/specs/run/allow_run_allowlist_resolution/main.ts @@ -1,26 +1,26 @@ // Testing the following (but with `deno` instead of `echo`): // | `deno run --allow-run=echo` | `which path == "/usr/bin/echo"` at startup | `which path != "/usr/bin/echo"` at startup | // |-------------------------------------|--------------------------------------------|--------------------------------------------| -// | **`Deno.Command("echo")`** | ✅ | ✅ | -// | **`Deno.Command("/usr/bin/echo")`** | ✅ | ❌ | +// | **`Deno.Command("echo")`** | ✅ | ✅ | +// | **`Deno.Command("/usr/bin/echo")`** | ✅ | ❌ | // | `deno run --allow-run=/usr/bin/echo | `which path == "/usr/bin/echo"` at runtime | `which path != "/usr/bin/echo"` at runtime | // |-------------------------------------|--------------------------------------------|--------------------------------------------| -// | **`Deno.Command("echo")`** | ✅ | ❌ | -// | **`Deno.Command("/usr/bin/echo")`** | ✅ | ✅ | +// | **`Deno.Command("echo")`** | ✅ | ❌ | +// | **`Deno.Command("/usr/bin/echo")`** | ✅ | ✅ | const execPath = Deno.execPath(); const execPathParent = execPath.replace(/[/\\][^/\\]+$/, ""); const testUrl = `data:application/typescript;base64,${ btoa(` - console.log(await Deno.permissions.query({ name: "run", command: "deno" })); - console.log(await Deno.permissions.query({ name: "run", command: "${ + console.error(await Deno.permissions.query({ name: "run", command: "deno" })); + console.error(await Deno.permissions.query({ name: "run", command: "${ execPath.replaceAll("\\", "\\\\") }" })); Deno.env.set("PATH", ""); - console.log(await Deno.permissions.query({ name: "run", command: "deno" })); - console.log(await Deno.permissions.query({ name: "run", command: "${ + console.error(await Deno.permissions.query({ name: "run", command: "deno" })); + console.error(await Deno.permissions.query({ name: "run", command: "${ execPath.replaceAll("\\", "\\\\") }" })); `) @@ -29,38 +29,39 @@ const testUrl = `data:application/typescript;base64,${ const process1 = await new Deno.Command(Deno.execPath(), { args: [ "run", - "--quiet", "--allow-env", "--allow-run=deno", testUrl, ], - stderr: "null", + stdout: "inherit", + stderr: "inherit", env: { "PATH": execPathParent }, }).output(); -console.log(new TextDecoder().decode(process1.stdout)); -const process2 = await new Deno.Command(Deno.execPath(), { +console.error("---"); + +await new Deno.Command(Deno.execPath(), { args: [ "run", - "--quiet", "--allow-env", "--allow-run=deno", testUrl, ], - stderr: "null", + stderr: "inherit", + stdout: "inherit", env: { "PATH": "" }, }).output(); -console.log(new TextDecoder().decode(process2.stdout)); -const process3 = await new Deno.Command(Deno.execPath(), { +console.error("---"); + +await new Deno.Command(Deno.execPath(), { args: [ "run", - "--quiet", "--allow-env", `--allow-run=${execPath}`, testUrl, ], - stderr: "null", + stderr: "inherit", + stdout: "inherit", env: { "PATH": execPathParent }, }).output(); -console.log(new TextDecoder().decode(process3.stdout)); diff --git a/tests/specs/run/ld_preload/__test__.jsonc b/tests/specs/run/ld_preload/__test__.jsonc index 767e423d06..882f157e9e 100644 --- a/tests/specs/run/ld_preload/__test__.jsonc +++ b/tests/specs/run/ld_preload/__test__.jsonc @@ -7,13 +7,11 @@ "tests": { "env_arg": { "args": "run --allow-run=echo env_arg.ts", - "output": "env_arg.out", - "exitCode": 1 + "output": "env_arg.out" }, "set_with_allow_env": { "args": "run --allow-run=echo --allow-env set_with_allow_env.ts", - "output": "set_with_allow_env.out", - "exitCode": 1 + "output": "set_with_allow_env.out" } } } diff --git a/tests/specs/run/ld_preload/env_arg.out b/tests/specs/run/ld_preload/env_arg.out index fbf37014ae..3df781a8e6 100644 --- a/tests/specs/run/ld_preload/env_arg.out +++ b/tests/specs/run/ld_preload/env_arg.out @@ -1,4 +1,8 @@ -error: Uncaught (in promise) PermissionDenied: Requires --allow-all permissions to spawn subprocess with LD_PRELOAD environment variable. -}).spawn(); - ^ - at [WILDCARD] +PermissionDenied: Requires --allow-all permissions to spawn subprocess with LD_PRELOAD environment variable. + [WILDCARD] + name: "PermissionDenied" +} +PermissionDenied: Requires --allow-all permissions to spawn subprocess with LD_PRELOAD environment variable. + [WILDCARD] + name: "PermissionDenied" +} diff --git a/tests/specs/run/ld_preload/env_arg.ts b/tests/specs/run/ld_preload/env_arg.ts index 0b236619e1..d7ca1073df 100644 --- a/tests/specs/run/ld_preload/env_arg.ts +++ b/tests/specs/run/ld_preload/env_arg.ts @@ -1,5 +1,20 @@ -const output = new Deno.Command("echo", { - env: { - "LD_PRELOAD": "./libpreload.so", - }, -}).spawn(); +try { + new Deno.Command("echo", { + env: { + "LD_PRELOAD": "./libpreload.so", + }, + }).spawn(); +} catch (err) { + console.log(err); +} + +try { + Deno.run({ + cmd: ["echo"], + env: { + "LD_PRELOAD": "./libpreload.so", + }, + }); +} catch (err) { + console.log(err); +} diff --git a/tests/specs/run/ld_preload/set_with_allow_env.out b/tests/specs/run/ld_preload/set_with_allow_env.out index 2e92763dda..60dba7cff1 100644 --- a/tests/specs/run/ld_preload/set_with_allow_env.out +++ b/tests/specs/run/ld_preload/set_with_allow_env.out @@ -1,4 +1,8 @@ -error: Uncaught (in promise) PermissionDenied: Requires --allow-all permissions to spawn subprocess with LD_PRELOAD environment variable. -const output = new Deno.Command("echo").spawn(); - ^ - at [WILDCARD] +PermissionDenied: Requires --allow-all permissions to spawn subprocess with LD_PRELOAD environment variable. + [WILDCARD] + name: "PermissionDenied" +} +PermissionDenied: Requires --allow-all permissions to spawn subprocess with DYLD_FALLBACK_LIBRARY_PATH, LD_PRELOAD environment variables. + [WILDCARD] + name: "PermissionDenied" +} diff --git a/tests/specs/run/ld_preload/set_with_allow_env.ts b/tests/specs/run/ld_preload/set_with_allow_env.ts index 9530f4478c..79004aa165 100644 --- a/tests/specs/run/ld_preload/set_with_allow_env.ts +++ b/tests/specs/run/ld_preload/set_with_allow_env.ts @@ -1,3 +1,15 @@ Deno.env.set("LD_PRELOAD", "./libpreload.so"); -const output = new Deno.Command("echo").spawn(); +try { + new Deno.Command("echo").spawn(); +} catch (err) { + console.log(err); +} + +Deno.env.set("DYLD_FALLBACK_LIBRARY_PATH", "./libpreload.so"); + +try { + Deno.run({ cmd: ["echo"] }).spawnSync(); +} catch (err) { + console.log(err); +} diff --git a/tests/testdata/run/089_run_allow_list.ts.out b/tests/testdata/run/089_run_allow_list.ts.out index 68a4a2ac57..0fc1c80c2a 100644 --- a/tests/testdata/run/089_run_allow_list.ts.out +++ b/tests/testdata/run/089_run_allow_list.ts.out @@ -1,3 +1,3 @@ -[WILDCARD]PermissionDenied: Requires run access to "ls", run again with the --allow-run flag +[WILDCARD]PermissionDenied: Requires run access to "[WILDLINE]ls[WILDLINE]", run again with the --allow-run flag [WILDCARD] true diff --git a/tests/unit/process_test.ts b/tests/unit/process_test.ts index a35362d090..383f17f384 100644 --- a/tests/unit/process_test.ts +++ b/tests/unit/process_test.ts @@ -611,6 +611,6 @@ Deno.test( p.close(); p.stdout.close(); assertStrictEquals(code, 1); - assertStringIncludes(stderr, "Failed getting cwd."); + assertStringIncludes(stderr, "failed resolving cwd:"); }, ); diff --git a/tools/lint.js b/tools/lint.js index d40b1b1fd9..08b551e984 100755 --- a/tools/lint.js +++ b/tools/lint.js @@ -221,7 +221,7 @@ async function ensureNoNewITests() { "pm_tests.rs": 0, "publish_tests.rs": 0, "repl_tests.rs": 0, - "run_tests.rs": 351, + "run_tests.rs": 350, "shared_library_tests.rs": 0, "task_tests.rs": 30, "test_tests.rs": 75, From c58a628e2ff449f8cbbdcd6bb8baeaba1ea95a4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Wed, 4 Sep 2024 13:55:30 +0100 Subject: [PATCH 28/45] feat(add): strip package subpath when adding a package (#25419) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These now works: ``` $ deno add @std/dotenv/load $ deno add npm:preact/hooks ``` Previously we were erroring out, because this is a "package reference" including a subpath. Closes https://github.com/denoland/deno/issues/25385 --------- Signed-off-by: Bartek Iwańczuk Co-authored-by: David Sherret --- cli/tools/registry/pm.rs | 10 ++++++++-- .../specs/add/add_with_subpath/__test__.jsonc | 19 +++++++++++++++++++ tests/specs/add/add_with_subpath/add.out | 8 ++++++++ tests/specs/add/add_with_subpath/deno.json | 0 .../add_with_subpath/wrong_constraint_jsr.out | 4 ++++ .../add_with_subpath/wrong_constraint_npm.out | 4 ++++ 6 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 tests/specs/add/add_with_subpath/__test__.jsonc create mode 100644 tests/specs/add/add_with_subpath/add.out create mode 100644 tests/specs/add/add_with_subpath/deno.json create mode 100644 tests/specs/add/add_with_subpath/wrong_constraint_jsr.out create mode 100644 tests/specs/add/add_with_subpath/wrong_constraint_npm.out diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index 4a36b459a8..e61da31d58 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -3,6 +3,8 @@ mod cache_deps; pub use cache_deps::cache_top_level_deps; +use deno_semver::jsr::JsrPackageReqReference; +use deno_semver::npm::NpmPackageReqReference; use std::borrow::Cow; use std::path::Path; @@ -501,14 +503,18 @@ impl AddPackageReq { match prefix { Prefix::Jsr => { - let package_req = PackageReq::from_str(entry_text)?; + let req_ref = + JsrPackageReqReference::from_str(&format!("jsr:{}", entry_text))?; + let package_req = req_ref.into_inner().req; Ok(AddPackageReq { alias: maybe_alias.unwrap_or_else(|| package_req.name.to_string()), value: AddPackageReqValue::Jsr(package_req), }) } Prefix::Npm => { - let package_req = PackageReq::from_str(entry_text)?; + let req_ref = + NpmPackageReqReference::from_str(&format!("npm:{}", entry_text))?; + let package_req = req_ref.into_inner().req; Ok(AddPackageReq { alias: maybe_alias.unwrap_or_else(|| package_req.name.to_string()), value: AddPackageReqValue::Npm(package_req), diff --git a/tests/specs/add/add_with_subpath/__test__.jsonc b/tests/specs/add/add_with_subpath/__test__.jsonc new file mode 100644 index 0000000000..b051bd2654 --- /dev/null +++ b/tests/specs/add/add_with_subpath/__test__.jsonc @@ -0,0 +1,19 @@ +{ + "tempDir": true, + "steps": [ + { + "args": "add @std/testing/bdd npm:preact/hooks", + "output": "add.out" + }, + { + "args": "add @std/testing/bdd@1 npm:preact/hooks@10", + "output": "wrong_constraint_jsr.out", + "exitCode": 1 + }, + { + "args": "add npm:preact/hooks@10", + "output": "wrong_constraint_npm.out", + "exitCode": 1 + } + ] +} diff --git a/tests/specs/add/add_with_subpath/add.out b/tests/specs/add/add_with_subpath/add.out new file mode 100644 index 0000000000..02b286ba38 --- /dev/null +++ b/tests/specs/add/add_with_subpath/add.out @@ -0,0 +1,8 @@ +[UNORDERED_START] +Add jsr:@std/testing@1.0.0 +Add npm:preact@10.19.6 +Download http://127.0.0.1:4250/@std/testing/1.0.0/bdd.ts +Download http://127.0.0.1:4250/@std/testing/1.0.0/types.ts +Download http://localhost:4260/preact +Download http://localhost:4260/preact/preact-10.19.6.tgz +[UNORDERED_END] diff --git a/tests/specs/add/add_with_subpath/deno.json b/tests/specs/add/add_with_subpath/deno.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/specs/add/add_with_subpath/wrong_constraint_jsr.out b/tests/specs/add/add_with_subpath/wrong_constraint_jsr.out new file mode 100644 index 0000000000..2b218407d2 --- /dev/null +++ b/tests/specs/add/add_with_subpath/wrong_constraint_jsr.out @@ -0,0 +1,4 @@ +error: Failed to parse package required: @std/testing/bdd@1 + +Caused by: + Invalid package specifier 'jsr:@std/testing/bdd@1'. Did you mean to write 'jsr:@std/testing@1/bdd'? diff --git a/tests/specs/add/add_with_subpath/wrong_constraint_npm.out b/tests/specs/add/add_with_subpath/wrong_constraint_npm.out new file mode 100644 index 0000000000..4adcf9ef6a --- /dev/null +++ b/tests/specs/add/add_with_subpath/wrong_constraint_npm.out @@ -0,0 +1,4 @@ +error: Failed to parse package required: npm:preact/hooks@10 + +Caused by: + Invalid package specifier 'npm:preact/hooks@10'. Did you mean to write 'npm:preact@10/hooks'? From 13911eb8efb77bd14a80412072aecb664aa55fd5 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Wed, 4 Sep 2024 15:43:32 +0200 Subject: [PATCH 29/45] chore(cli): remove deno_emit dependency (#25427) --- Cargo.lock | 110 +++++++++------------------------------------- cli/Cargo.toml | 1 - cli/graph_util.rs | 2 +- 3 files changed, 21 insertions(+), 92 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dcdcca3141..7ab6733dca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1113,7 +1113,7 @@ dependencies = [ "hashbrown", "lock_api", "once_cell", - "parking_lot_core 0.9.9", + "parking_lot_core", ] [[package]] @@ -1169,7 +1169,6 @@ dependencies = [ "deno_config", "deno_core", "deno_doc", - "deno_emit", "deno_graph", "deno_lint", "deno_lockfile", @@ -1355,7 +1354,7 @@ dependencies = [ "indexmap", "log", "once_cell", - "parking_lot 0.12.3", + "parking_lot", "serde", "serde_json", "sha2", @@ -1420,7 +1419,7 @@ dependencies = [ "futures", "libc", "memoffset 0.9.1", - "parking_lot 0.12.3", + "parking_lot", "percent-encoding", "pin-project", "serde", @@ -1511,23 +1510,6 @@ dependencies = [ "termcolor", ] -[[package]] -name = "deno_emit" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33aca9546e36a1b85efb630add94a4c2ac13c2333bb48df4439002c002f4c5b2" -dependencies = [ - "anyhow", - "base64 0.21.7", - "deno_ast", - "deno_graph", - "escape8259", - "futures", - "import_map", - "parking_lot 0.11.2", - "url", -] - [[package]] name = "deno_fetch" version = "0.191.0" @@ -1616,7 +1598,7 @@ dependencies = [ "log", "monch", "once_cell", - "parking_lot 0.12.3", + "parking_lot", "regex", "serde", "serde_json", @@ -1677,7 +1659,7 @@ dependencies = [ "log", "once_cell", "os_pipe", - "parking_lot 0.12.3", + "parking_lot", "pin-project", "rand", "tokio", @@ -2102,7 +2084,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f36b4ef61a04ce201b925a5dffa90f88437d37fee4836c758470dd15ba7f05e" dependencies = [ - "parking_lot 0.12.3", + "parking_lot", "tokio", ] @@ -2808,15 +2790,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31ae425815400e5ed474178a7a22e275a9687086a12ca63ec793ff292d8fdae8" -[[package]] -name = "escape8259" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4911e3666fcd7826997b4745c8224295a6f3072f1418c3067b97a67557ee" -dependencies = [ - "rustversion", -] - [[package]] name = "eszip" version = "0.78.0" @@ -2956,7 +2929,7 @@ dependencies = [ "anyhow", "crossbeam-channel", "deno_terminal 0.1.1", - "parking_lot 0.12.3", + "parking_lot", "regex", "thiserror", ] @@ -2969,7 +2942,7 @@ checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall", "windows-sys 0.52.0", ] @@ -3853,15 +3826,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - [[package]] name = "ipconfig" version = "0.3.2" @@ -4208,7 +4172,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.4", ] [[package]] @@ -4924,17 +4888,6 @@ dependencies = [ "sha2", ] -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.3" @@ -4942,21 +4895,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core 0.9.9", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -4967,7 +4906,7 @@ checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall", "smallvec", "windows-targets 0.48.5", ] @@ -5685,15 +5624,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -6637,7 +6567,7 @@ checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" dependencies = [ "new_debug_unreachable", "once_cell", - "parking_lot 0.12.3", + "parking_lot", "phf_shared 0.10.0", "precomputed-hash", "serde", @@ -6746,7 +6676,7 @@ dependencies = [ "indexmap", "is-macro", "once_cell", - "parking_lot 0.12.3", + "parking_lot", "petgraph", "radix_fmt", "relative-path", @@ -7331,7 +7261,7 @@ dependencies = [ "nix 0.26.2", "once_cell", "os_pipe", - "parking_lot 0.12.3", + "parking_lot", "pretty_assertions", "prost", "prost-build", @@ -7463,7 +7393,7 @@ dependencies = [ "libc", "mio", "num_cpus", - "parking_lot 0.12.3", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", @@ -7734,7 +7664,7 @@ dependencies = [ "ipconfig", "lru-cache", "once_cell", - "parking_lot 0.12.3", + "parking_lot", "rand", "resolv-conf", "serde", @@ -8207,7 +8137,7 @@ dependencies = [ "log", "naga", "once_cell", - "parking_lot 0.12.3", + "parking_lot", "profiling", "raw-window-handle", "ron", @@ -8249,7 +8179,7 @@ dependencies = [ "ndk-sys", "objc", "once_cell", - "parking_lot 0.12.3", + "parking_lot", "profiling", "range-alloc", "raw-window-handle", @@ -8304,7 +8234,7 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44ab49fad634e88f55bf8f9bb3abd2f27d7204172a112c7c9987e01c1c94ea9" dependencies = [ - "redox_syscall 0.4.1", + "redox_syscall", "wasite", "web-sys", ] @@ -8741,7 +8671,7 @@ dependencies = [ "log", "num-traits", "once_cell", - "parking_lot 0.12.3", + "parking_lot", "rand", "regex", "thiserror", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index a1d8f31dd0..d7d8eeb7c6 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -68,7 +68,6 @@ deno_cache_dir = { workspace = true } deno_config = { version = "=0.32.0", features = ["workspace", "sync"] } deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_doc = { version = "0.148.0", features = ["html", "syntect"] } -deno_emit = "=0.45.0" deno_graph = { version = "=0.82.0" } deno_lint = { version = "=0.64.0", features = ["docs"] } deno_lockfile.workspace = true diff --git a/cli/graph_util.rs b/cli/graph_util.rs index bfd547a124..a9fcfd83db 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -20,7 +20,7 @@ use crate::tools::check::TypeChecker; use crate::util::file_watcher::WatcherCommunicator; use crate::util::fs::canonicalize_path; use deno_config::workspace::JsrPackageConfig; -use deno_emit::LoaderChecksum; +use deno_graph::source::LoaderChecksum; use deno_graph::JsrLoadError; use deno_graph::ModuleLoadError; use deno_graph::WorkspaceFastCheckOption; From c6d1b0a1ccf45b7819b1e6f1efe8687b240f495a Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 4 Sep 2024 16:00:44 +0200 Subject: [PATCH 30/45] fix(byonm): resolve npm deps of jsr deps (#25399) This allows using npm deps of jsr deps without having to add them to the root package.json. Works by taking the package requirement and scanning the `node_modules/.deno` directory for the best matching package, so it relies on deno's node_modules structure. Additionally to make the transition from package.json to deno.json easier, Deno now: 1. Installs npm deps in a deno.json at the same time as installing npm deps from a package.json. 2. Uses the alias in the import map for `node_modules/` for better package.json compatiblity. --- cli/args/mod.rs | 2 +- cli/args/package_json.rs | 129 +++++++++++----- cli/factory.rs | 6 +- cli/lsp/documents.rs | 12 +- cli/lsp/resolver.rs | 6 +- cli/module_loader.rs | 8 +- cli/npm/byonm.rs | 143 ++++++++++++++---- cli/npm/managed/mod.rs | 27 ++-- cli/npm/managed/resolvers/local.rs | 50 +++--- cli/npm/managed/resolvers/mod.rs | 7 +- cli/resolver.rs | 8 +- cli/standalone/binary.rs | 2 +- cli/standalone/mod.rs | 22 +-- cli/tsc/mod.rs | 8 +- ext/node_resolver/lib.rs | 1 + tests/integration/lsp_tests.rs | 4 +- tests/integration/npm_tests.rs | 4 +- .../jsr/@denotest/npm-add/0.5.0/mod.ts | 5 + .../jsr/@denotest/npm-add/0.5.0_meta.json | 5 + .../jsr/@denotest/npm-add/1.0.0/mod.ts | 5 + .../jsr/@denotest/npm-add/1.0.0_meta.json | 5 + .../registry/jsr/@denotest/npm-add/meta.json | 6 + .../install/alias_deno_json/__test__.jsonc | 10 ++ tests/specs/install/alias_deno_json/deno.json | 5 + .../install/alias_deno_json/package.json | 2 + tests/specs/install/alias_deno_json/verify.ts | 2 + .../alias_invalid_path_char/__test__.jsonc | 10 ++ .../alias_invalid_path_char/deno.jsonc | 7 + .../alias_invalid_path_char/package.json | 2 + .../install/alias_invalid_path_char/verify.ts | 10 ++ .../__test__.jsonc | 10 ++ .../deno.json | 5 + .../package.json | 5 + .../verify.out | 2 + .../verify.ts | 13 ++ .../__test__.jsonc | 10 ++ .../deno.json | 5 + .../package.json | 5 + .../verify.out | 5 + .../verify.ts | 10 ++ .../install/byonm_jsr_npm_dep/__test__.jsonc | 10 ++ .../specs/install/byonm_jsr_npm_dep/deno.json | 6 + tests/specs/install/byonm_jsr_npm_dep/main.ts | 5 + .../install/byonm_jsr_npm_dep/package.json | 2 + .../__test__.jsonc | 13 ++ .../nested_deno_pkg_npm_conflict/deno.json | 7 + .../nested_deno_pkg_npm_conflict/main.js | 9 ++ .../member/deno.json | 5 + .../member/main.js | 9 ++ 49 files changed, 504 insertions(+), 145 deletions(-) create mode 100644 tests/registry/jsr/@denotest/npm-add/0.5.0/mod.ts create mode 100644 tests/registry/jsr/@denotest/npm-add/0.5.0_meta.json create mode 100644 tests/registry/jsr/@denotest/npm-add/1.0.0/mod.ts create mode 100644 tests/registry/jsr/@denotest/npm-add/1.0.0_meta.json create mode 100644 tests/registry/jsr/@denotest/npm-add/meta.json create mode 100644 tests/specs/install/alias_deno_json/__test__.jsonc create mode 100644 tests/specs/install/alias_deno_json/deno.json create mode 100644 tests/specs/install/alias_deno_json/package.json create mode 100644 tests/specs/install/alias_deno_json/verify.ts create mode 100644 tests/specs/install/alias_invalid_path_char/__test__.jsonc create mode 100644 tests/specs/install/alias_invalid_path_char/deno.jsonc create mode 100644 tests/specs/install/alias_invalid_path_char/package.json create mode 100644 tests/specs/install/alias_invalid_path_char/verify.ts create mode 100644 tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/__test__.jsonc create mode 100644 tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/deno.json create mode 100644 tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/package.json create mode 100644 tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/verify.out create mode 100644 tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/verify.ts create mode 100644 tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/__test__.jsonc create mode 100644 tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/deno.json create mode 100644 tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/package.json create mode 100644 tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/verify.out create mode 100644 tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/verify.ts create mode 100644 tests/specs/install/byonm_jsr_npm_dep/__test__.jsonc create mode 100644 tests/specs/install/byonm_jsr_npm_dep/deno.json create mode 100644 tests/specs/install/byonm_jsr_npm_dep/main.ts create mode 100644 tests/specs/install/byonm_jsr_npm_dep/package.json create mode 100644 tests/specs/workspaces/nested_deno_pkg_npm_conflict/__test__.jsonc create mode 100644 tests/specs/workspaces/nested_deno_pkg_npm_conflict/deno.json create mode 100644 tests/specs/workspaces/nested_deno_pkg_npm_conflict/main.js create mode 100644 tests/specs/workspaces/nested_deno_pkg_npm_conflict/member/deno.json create mode 100644 tests/specs/workspaces/nested_deno_pkg_npm_conflict/member/main.js diff --git a/cli/args/mod.rs b/cli/args/mod.rs index be8eccd6c3..4927cf7d02 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -44,7 +44,7 @@ pub use deno_config::deno_json::TsTypeLib; pub use deno_config::glob::FilePatterns; pub use flags::*; pub use lockfile::CliLockfile; -pub use package_json::PackageJsonInstallDepsProvider; +pub use package_json::NpmInstallDepsProvider; use deno_ast::ModuleSpecifier; use deno_core::anyhow::bail; diff --git a/cli/args/package_json.rs b/cli/args/package_json.rs index eedd0a1941..b9f0919d5d 100644 --- a/cli/args/package_json.rs +++ b/cli/args/package_json.rs @@ -1,17 +1,20 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use std::collections::HashSet; use std::path::PathBuf; use std::sync::Arc; use deno_config::workspace::Workspace; +use deno_core::serde_json; use deno_package_json::PackageJsonDepValue; +use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageReq; +use crate::util::path::is_banned_path_char; + #[derive(Debug)] pub struct InstallNpmRemotePkg { pub alias: String, - // todo(24419): use this when setting up the node_modules dir - #[allow(dead_code)] pub base_dir: PathBuf, pub req: PackageReq, } @@ -19,74 +22,126 @@ pub struct InstallNpmRemotePkg { #[derive(Debug)] pub struct InstallNpmWorkspacePkg { pub alias: String, - // todo(24419): use this when setting up the node_modules dir - #[allow(dead_code)] - pub base_dir: PathBuf, pub target_dir: PathBuf, } #[derive(Debug, Default)] -pub struct PackageJsonInstallDepsProvider { +pub struct NpmInstallDepsProvider { remote_pkgs: Vec, workspace_pkgs: Vec, } -impl PackageJsonInstallDepsProvider { +impl NpmInstallDepsProvider { pub fn empty() -> Self { Self::default() } pub fn from_workspace(workspace: &Arc) -> Self { + // todo(dsherret): estimate capacity? let mut workspace_pkgs = Vec::new(); let mut remote_pkgs = Vec::new(); let workspace_npm_pkgs = workspace.npm_packages(); - for pkg_json in workspace.package_jsons() { - let deps = pkg_json.resolve_local_package_json_deps(); - let mut pkg_pkgs = Vec::with_capacity(deps.len()); - for (alias, dep) in deps { - let Ok(dep) = dep else { - continue; - }; - match dep { - PackageJsonDepValue::Req(pkg_req) => { - let workspace_pkg = workspace_npm_pkgs.iter().find(|pkg| { - pkg.matches_req(&pkg_req) - // do not resolve to the current package - && pkg.pkg_json.path != pkg_json.path - }); + + for (_, folder) in workspace.config_folders() { + let mut deno_json_aliases = HashSet::new(); + + // deal with the deno.json first because it takes precedence during resolution + if let Some(deno_json) = &folder.deno_json { + // don't bother with externally referenced import maps as users + // should inline their import map to get this behaviour + if let Some(serde_json::Value::Object(obj)) = &deno_json.json.imports { + deno_json_aliases.reserve(obj.len()); + let mut pkg_pkgs = Vec::with_capacity(obj.len()); + for (alias, value) in obj { + let serde_json::Value::String(specifier) = value else { + continue; + }; + let Ok(npm_req_ref) = NpmPackageReqReference::from_str(specifier) + else { + continue; + }; + // skip any aliases with banned characters + if alias.chars().any(|c| c == '\\' || is_banned_path_char(c)) { + continue; + } + deno_json_aliases.insert(alias.to_lowercase()); + let pkg_req = npm_req_ref.into_inner().req; + let workspace_pkg = workspace_npm_pkgs + .iter() + .find(|pkg| pkg.matches_req(&pkg_req)); if let Some(pkg) = workspace_pkg { workspace_pkgs.push(InstallNpmWorkspacePkg { - alias, - base_dir: pkg_json.dir_path().to_path_buf(), + alias: alias.to_string(), target_dir: pkg.pkg_json.dir_path().to_path_buf(), }); } else { pkg_pkgs.push(InstallNpmRemotePkg { - alias, - base_dir: pkg_json.dir_path().to_path_buf(), + alias: alias.to_string(), + base_dir: deno_json.dir_path(), req: pkg_req, }); } } - PackageJsonDepValue::Workspace(version_req) => { - if let Some(pkg) = workspace_npm_pkgs.iter().find(|pkg| { - pkg.matches_name_and_version_req(&alias, &version_req) - }) { - workspace_pkgs.push(InstallNpmWorkspacePkg { - alias, - base_dir: pkg_json.dir_path().to_path_buf(), - target_dir: pkg.pkg_json.dir_path().to_path_buf(), + + // sort within each package (more like npm resolution) + pkg_pkgs.sort_by(|a, b| a.alias.cmp(&b.alias)); + remote_pkgs.extend(pkg_pkgs); + } + } + + if let Some(pkg_json) = &folder.pkg_json { + let deps = pkg_json.resolve_local_package_json_deps(); + let mut pkg_pkgs = Vec::with_capacity(deps.len()); + for (alias, dep) in deps { + let Ok(dep) = dep else { + continue; + }; + if deno_json_aliases.contains(&alias.to_lowercase()) { + // aliases in deno.json take precedence over package.json, so + // since this can't be resolved don't bother installing it + continue; + } + match dep { + PackageJsonDepValue::Req(pkg_req) => { + let workspace_pkg = workspace_npm_pkgs.iter().find(|pkg| { + pkg.matches_req(&pkg_req) + // do not resolve to the current package + && pkg.pkg_json.path != pkg_json.path }); + + if let Some(pkg) = workspace_pkg { + workspace_pkgs.push(InstallNpmWorkspacePkg { + alias, + target_dir: pkg.pkg_json.dir_path().to_path_buf(), + }); + } else { + pkg_pkgs.push(InstallNpmRemotePkg { + alias, + base_dir: pkg_json.dir_path().to_path_buf(), + req: pkg_req, + }); + } + } + PackageJsonDepValue::Workspace(version_req) => { + if let Some(pkg) = workspace_npm_pkgs.iter().find(|pkg| { + pkg.matches_name_and_version_req(&alias, &version_req) + }) { + workspace_pkgs.push(InstallNpmWorkspacePkg { + alias, + target_dir: pkg.pkg_json.dir_path().to_path_buf(), + }); + } } } } - } - // sort within each package - pkg_pkgs.sort_by(|a, b| a.alias.cmp(&b.alias)); - remote_pkgs.extend(pkg_pkgs); + // sort within each package as npm does + pkg_pkgs.sort_by(|a, b| a.alias.cmp(&b.alias)); + remote_pkgs.extend(pkg_pkgs); + } } + remote_pkgs.shrink_to_fit(); workspace_pkgs.shrink_to_fit(); Self { diff --git a/cli/factory.rs b/cli/factory.rs index d1ccac6ceb..4215530584 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -5,7 +5,7 @@ use crate::args::CaData; use crate::args::CliOptions; use crate::args::DenoSubcommand; use crate::args::Flags; -use crate::args::PackageJsonInstallDepsProvider; +use crate::args::NpmInstallDepsProvider; use crate::args::StorageKeyResolver; use crate::args::TsConfigType; use crate::cache::Caches; @@ -386,9 +386,7 @@ impl CliFactory { cache_setting: cli_options.cache_setting(), text_only_progress_bar: self.text_only_progress_bar().clone(), maybe_node_modules_path: cli_options.node_modules_dir_path().cloned(), - package_json_deps_provider: Arc::new(PackageJsonInstallDepsProvider::from_workspace( - cli_options.workspace(), - )), + npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::from_workspace(cli_options.workspace())), npm_system_info: cli_options.npm_system_info(), npmrc: cli_options.npmrc().clone(), lifecycle_scripts: cli_options.lifecycle_scripts_config(), diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index b58d7292b0..ee9a522974 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -1251,7 +1251,7 @@ impl Documents { /// tsc when type checking. pub fn resolve( &self, - specifiers: &[String], + raw_specifiers: &[String], referrer: &ModuleSpecifier, file_referrer: Option<&ModuleSpecifier>, ) -> Vec> { @@ -1262,16 +1262,16 @@ impl Documents { .or(file_referrer); let dependencies = document.as_ref().map(|d| d.dependencies()); let mut results = Vec::new(); - for specifier in specifiers { - if specifier.starts_with("asset:") { - if let Ok(specifier) = ModuleSpecifier::parse(specifier) { + for raw_specifier in raw_specifiers { + if raw_specifier.starts_with("asset:") { + if let Ok(specifier) = ModuleSpecifier::parse(raw_specifier) { let media_type = MediaType::from_specifier(&specifier); results.push(Some((specifier, media_type))); } else { results.push(None); } } else if let Some(dep) = - dependencies.as_ref().and_then(|d| d.get(specifier)) + dependencies.as_ref().and_then(|d| d.get(raw_specifier)) { if let Some(specifier) = dep.maybe_type.maybe_specifier() { results.push(self.resolve_dependency( @@ -1290,7 +1290,7 @@ impl Documents { } } else if let Ok(specifier) = self.resolver.as_graph_resolver(file_referrer).resolve( - specifier, + raw_specifier, &deno_graph::Range { specifier: referrer.clone(), start: deno_graph::Position::zeroed(), diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index d279031e32..1c3d5c88b2 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -3,7 +3,7 @@ use crate::args::create_default_npmrc; use crate::args::CacheSetting; use crate::args::CliLockfile; -use crate::args::PackageJsonInstallDepsProvider; +use crate::args::NpmInstallDepsProvider; use crate::graph_util::CliJsrUrlProvider; use crate::http_util::HttpClientProvider; use crate::lsp::config::Config; @@ -474,9 +474,7 @@ async fn create_npm_resolver( maybe_node_modules_path: config_data .and_then(|d| d.node_modules_dir.clone()), // only used for top level install, so we can ignore this - package_json_deps_provider: Arc::new( - PackageJsonInstallDepsProvider::empty(), - ), + npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()), npmrc: config_data .and_then(|d| d.npmrc.clone()) .unwrap_or_else(create_default_npmrc), diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 3208d13657..186eb02101 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -401,7 +401,7 @@ impl fn inner_resolve( &self, - specifier: &str, + raw_specifier: &str, referrer: &ModuleSpecifier, ) -> Result { if self.shared.node_resolver.in_npm_package(referrer) { @@ -409,7 +409,7 @@ impl self .shared .node_resolver - .resolve(specifier, referrer, NodeResolutionMode::Execution)? + .resolve(raw_specifier, referrer, NodeResolutionMode::Execution)? .into_url(), ); } @@ -418,7 +418,7 @@ impl let resolution = match graph.get(referrer) { Some(Module::Js(module)) => module .dependencies - .get(specifier) + .get(raw_specifier) .map(|d| &d.maybe_code) .unwrap_or(&Resolution::None), _ => &Resolution::None, @@ -433,7 +433,7 @@ impl )); } Resolution::None => Cow::Owned(self.shared.resolver.resolve( - specifier, + raw_specifier, &deno_graph::Range { specifier: referrer.clone(), start: deno_graph::Position::zeroed(), diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs index e62650d8a9..3249b2ed19 100644 --- a/cli/npm/byonm.rs +++ b/cli/npm/byonm.rs @@ -17,6 +17,7 @@ use deno_runtime::deno_node::NodeRequireResolver; use deno_runtime::deno_node::NpmProcessStateProvider; use deno_runtime::deno_node::PackageJson; use deno_semver::package::PackageReq; +use deno_semver::Version; use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::errors::PackageJsonLoadError; @@ -29,6 +30,7 @@ use crate::args::NpmProcessStateKind; use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs; use deno_runtime::fs_util::specifier_to_file_path; +use super::managed::normalize_pkg_name_for_node_modules_deno_folder; use super::CliNpmResolver; use super::InnerCliNpmResolverRef; @@ -60,9 +62,7 @@ impl ByonmCliNpmResolver { ) -> Result>, PackageJsonLoadError> { load_pkg_json(&DenoPkgJsonFsAdapter(self.fs.as_ref()), path) } -} -impl ByonmCliNpmResolver { /// Finds the ancestor package.json that contains the specified dependency. pub fn find_ancestor_package_json_with_dep( &self, @@ -98,7 +98,7 @@ impl ByonmCliNpmResolver { &self, req: &PackageReq, referrer: &ModuleSpecifier, - ) -> Result<(Arc, String), AnyError> { + ) -> Result, String)>, AnyError> { fn resolve_alias_from_pkg_json( req: &PackageReq, pkg_json: &PackageJson, @@ -134,7 +134,7 @@ impl ByonmCliNpmResolver { if let Some(alias) = resolve_alias_from_pkg_json(req, pkg_json.as_ref()) { - return Ok((pkg_json, alias)); + return Ok(Some((pkg_json, alias))); } } current_path = dir_path; @@ -148,19 +148,65 @@ impl ByonmCliNpmResolver { if let Some(pkg_json) = self.load_pkg_json(&root_pkg_json_path)? { if let Some(alias) = resolve_alias_from_pkg_json(req, pkg_json.as_ref()) { - return Ok((pkg_json, alias)); + return Ok(Some((pkg_json, alias))); } } } - bail!( - concat!( - "Could not find a matching package for 'npm:{}' in a package.json file. ", - "You must specify this as a package.json dependency when the ", - "node_modules folder is not managed by Deno.", - ), - req, + Ok(None) + } + + fn resolve_folder_in_root_node_modules( + &self, + req: &PackageReq, + ) -> Option { + // now check if node_modules/.deno/ matches this constraint + let root_node_modules_dir = self.root_node_modules_dir.as_ref()?; + let node_modules_deno_dir = root_node_modules_dir.join(".deno"); + let Ok(entries) = self.fs.read_dir_sync(&node_modules_deno_dir) else { + return None; + }; + let search_prefix = format!( + "{}@", + normalize_pkg_name_for_node_modules_deno_folder(&req.name) ); + let mut best_version = None; + + // example entries: + // - @denotest+add@1.0.0 + // - @denotest+add@1.0.0_1 + for entry in entries { + if !entry.is_directory { + continue; + } + let Some(version_and_copy_idx) = entry.name.strip_prefix(&search_prefix) + else { + continue; + }; + let version = version_and_copy_idx + .rsplit_once('_') + .map(|(v, _)| v) + .unwrap_or(version_and_copy_idx); + let Ok(version) = Version::parse_from_npm(version) else { + continue; + }; + if req.version_req.matches(&version) { + if let Some((best_version_version, _)) = &best_version { + if version > *best_version_version { + best_version = Some((version, entry.name)); + } + } else { + best_version = Some((version, entry.name)); + } + } + } + + best_version.map(|(_version, entry_name)| { + join_package_name( + &node_modules_deno_dir.join(entry_name).join("node_modules"), + &req.name, + ) + }) } } @@ -288,29 +334,62 @@ impl CliNpmResolver for ByonmCliNpmResolver { req: &PackageReq, referrer: &ModuleSpecifier, ) -> Result { - // resolve the pkg json and alias - let (pkg_json, alias) = - self.resolve_pkg_json_and_alias_for_req(req, referrer)?; - // now try node resolution - for ancestor in pkg_json.path.parent().unwrap().ancestors() { - let node_modules_folder = ancestor.join("node_modules"); - let sub_dir = join_package_name(&node_modules_folder, &alias); - if self.fs.is_dir_sync(&sub_dir) { - return Ok(canonicalize_path_maybe_not_exists_with_fs( - &sub_dir, - self.fs.as_ref(), - )?); + fn node_resolve_dir( + fs: &dyn FileSystem, + alias: &str, + start_dir: &Path, + ) -> Result, AnyError> { + for ancestor in start_dir.ancestors() { + let node_modules_folder = ancestor.join("node_modules"); + let sub_dir = join_package_name(&node_modules_folder, alias); + if fs.is_dir_sync(&sub_dir) { + return Ok(Some(canonicalize_path_maybe_not_exists_with_fs( + &sub_dir, fs, + )?)); + } } + Ok(None) } - bail!( - concat!( - "Could not find \"{}\" in a node_modules folder. ", - "Deno expects the node_modules/ directory to be up to date. ", - "Did you forget to run `deno install`?" - ), - alias, - ); + // now attempt to resolve if it's found in any package.json + let maybe_pkg_json_and_alias = + self.resolve_pkg_json_and_alias_for_req(req, referrer)?; + match maybe_pkg_json_and_alias { + Some((pkg_json, alias)) => { + // now try node resolution + if let Some(resolved) = + node_resolve_dir(self.fs.as_ref(), &alias, pkg_json.dir_path())? + { + return Ok(resolved); + } + + bail!( + concat!( + "Could not find \"{}\" in a node_modules folder. ", + "Deno expects the node_modules/ directory to be up to date. ", + "Did you forget to run `deno install`?" + ), + alias, + ); + } + None => { + // now check if node_modules/.deno/ matches this constraint + if let Some(folder) = self.resolve_folder_in_root_node_modules(req) { + return Ok(folder); + } + + bail!( + concat!( + "Could not find a matching package for 'npm:{}' in the node_modules ", + "directory. Ensure you have all your JSR and npm dependencies listed ", + "in your deno.json or package.json, then run `deno install`. Alternatively, ", + r#"turn on auto-install by specifying `"nodeModulesDir": "auto"` in your "#, + "deno.json file." + ), + req, + ); + } + } } fn check_state_hash(&self) -> Option { diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs index a0148c6482..0be26b7857 100644 --- a/cli/npm/managed/mod.rs +++ b/cli/npm/managed/mod.rs @@ -32,9 +32,9 @@ use resolution::AddPkgReqsResult; use crate::args::CliLockfile; use crate::args::LifecycleScriptsConfig; +use crate::args::NpmInstallDepsProvider; use crate::args::NpmProcessState; use crate::args::NpmProcessStateKind; -use crate::args::PackageJsonInstallDepsProvider; use crate::cache::FastInsecureHasher; use crate::http_util::HttpClientProvider; use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs; @@ -45,6 +45,7 @@ use self::cache::NpmCache; use self::registry::CliNpmRegistryApi; use self::resolution::NpmResolution; use self::resolvers::create_npm_fs_resolver; +pub use self::resolvers::normalize_pkg_name_for_node_modules_deno_folder; use self::resolvers::NpmPackageFsResolver; use super::CliNpmResolver; @@ -71,7 +72,7 @@ pub struct CliNpmResolverManagedCreateOptions { pub text_only_progress_bar: crate::util::progress_bar::ProgressBar, pub maybe_node_modules_path: Option, pub npm_system_info: NpmSystemInfo, - pub package_json_deps_provider: Arc, + pub npm_install_deps_provider: Arc, pub npmrc: Arc, pub lifecycle_scripts: LifecycleScriptsConfig, } @@ -97,7 +98,7 @@ pub async fn create_managed_npm_resolver_for_lsp( npm_api, npm_cache, options.npmrc, - options.package_json_deps_provider, + options.npm_install_deps_provider, options.text_only_progress_bar, options.maybe_node_modules_path, options.npm_system_info, @@ -122,7 +123,7 @@ pub async fn create_managed_npm_resolver( npm_api, npm_cache, options.npmrc, - options.package_json_deps_provider, + options.npm_install_deps_provider, options.text_only_progress_bar, options.maybe_node_modules_path, options.npm_system_info, @@ -139,7 +140,7 @@ fn create_inner( npm_api: Arc, npm_cache: Arc, npm_rc: Arc, - package_json_deps_provider: Arc, + npm_install_deps_provider: Arc, text_only_progress_bar: crate::util::progress_bar::ProgressBar, node_modules_dir_path: Option, npm_system_info: NpmSystemInfo, @@ -161,7 +162,7 @@ fn create_inner( let fs_resolver = create_npm_fs_resolver( fs.clone(), npm_cache.clone(), - &package_json_deps_provider, + &npm_install_deps_provider, &text_only_progress_bar, resolution.clone(), tarball_cache.clone(), @@ -175,7 +176,7 @@ fn create_inner( maybe_lockfile, npm_api, npm_cache, - package_json_deps_provider, + npm_install_deps_provider, resolution, tarball_cache, text_only_progress_bar, @@ -261,7 +262,7 @@ pub struct ManagedCliNpmResolver { maybe_lockfile: Option>, npm_api: Arc, npm_cache: Arc, - package_json_deps_provider: Arc, + npm_install_deps_provider: Arc, resolution: Arc, tarball_cache: Arc, text_only_progress_bar: ProgressBar, @@ -286,7 +287,7 @@ impl ManagedCliNpmResolver { maybe_lockfile: Option>, npm_api: Arc, npm_cache: Arc, - package_json_deps_provider: Arc, + npm_install_deps_provider: Arc, resolution: Arc, tarball_cache: Arc, text_only_progress_bar: ProgressBar, @@ -299,7 +300,7 @@ impl ManagedCliNpmResolver { maybe_lockfile, npm_api, npm_cache, - package_json_deps_provider, + npm_install_deps_provider, text_only_progress_bar, resolution, tarball_cache, @@ -476,7 +477,7 @@ impl ManagedCliNpmResolver { if !self.top_level_install_flag.raise() { return Ok(false); // already did this } - let pkg_json_remote_pkgs = self.package_json_deps_provider.remote_pkgs(); + let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs(); if pkg_json_remote_pkgs.is_empty() { return Ok(false); } @@ -605,7 +606,7 @@ impl CliNpmResolver for ManagedCliNpmResolver { create_npm_fs_resolver( self.fs.clone(), self.npm_cache.clone(), - &self.package_json_deps_provider, + &self.npm_install_deps_provider, &self.text_only_progress_bar, npm_resolution.clone(), self.tarball_cache.clone(), @@ -616,7 +617,7 @@ impl CliNpmResolver for ManagedCliNpmResolver { self.maybe_lockfile.clone(), self.npm_api.clone(), self.npm_cache.clone(), - self.package_json_deps_provider.clone(), + self.npm_install_deps_provider.clone(), npm_resolution, self.tarball_cache.clone(), self.text_only_progress_bar.clone(), diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs index 6ac83ee949..eb6f9de9b5 100644 --- a/cli/npm/managed/resolvers/local.rs +++ b/cli/npm/managed/resolvers/local.rs @@ -41,7 +41,7 @@ use node_resolver::errors::ReferrerNotFoundError; use serde::Deserialize; use serde::Serialize; -use crate::args::PackageJsonInstallDepsProvider; +use crate::args::NpmInstallDepsProvider; use crate::cache::CACHE_PERM; use crate::npm::cache_dir::mixed_case_package_name_decode; use crate::npm::cache_dir::mixed_case_package_name_encode; @@ -65,7 +65,7 @@ use super::common::RegistryReadPermissionChecker; pub struct LocalNpmPackageResolver { cache: Arc, fs: Arc, - pkg_json_deps_provider: Arc, + npm_install_deps_provider: Arc, progress_bar: ProgressBar, resolution: Arc, tarball_cache: Arc, @@ -81,7 +81,7 @@ impl LocalNpmPackageResolver { pub fn new( cache: Arc, fs: Arc, - pkg_json_deps_provider: Arc, + npm_install_deps_provider: Arc, progress_bar: ProgressBar, resolution: Arc, tarball_cache: Arc, @@ -92,7 +92,7 @@ impl LocalNpmPackageResolver { Self { cache, fs: fs.clone(), - pkg_json_deps_provider, + npm_install_deps_provider, progress_bar, resolution, tarball_cache, @@ -248,7 +248,7 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver { sync_resolution_with_fs( &self.resolution.snapshot(), &self.cache, - &self.pkg_json_deps_provider, + &self.npm_install_deps_provider, &self.progress_bar, &self.tarball_cache, &self.root_node_modules_path, @@ -412,14 +412,16 @@ fn has_lifecycle_scripts( async fn sync_resolution_with_fs( snapshot: &NpmResolutionSnapshot, cache: &Arc, - pkg_json_deps_provider: &PackageJsonInstallDepsProvider, + npm_install_deps_provider: &NpmInstallDepsProvider, progress_bar: &ProgressBar, tarball_cache: &Arc, root_node_modules_dir_path: &Path, system_info: &NpmSystemInfo, lifecycle_scripts: &LifecycleScriptsConfig, ) -> Result<(), AnyError> { - if snapshot.is_empty() && pkg_json_deps_provider.workspace_pkgs().is_empty() { + if snapshot.is_empty() + && npm_install_deps_provider.workspace_pkgs().is_empty() + { return Ok(()); // don't create the directory } @@ -620,7 +622,7 @@ async fn sync_resolution_with_fs( // 4. Create symlinks for package json dependencies { - for remote in pkg_json_deps_provider.remote_pkgs() { + for remote in npm_install_deps_provider.remote_pkgs() { let remote_pkg = if let Ok(remote_pkg) = snapshot.resolve_pkg_from_pkg_req(&remote.req) { @@ -684,7 +686,7 @@ async fn sync_resolution_with_fs( } // 5. Create symlinks for the remaining top level packages in the node_modules folder. - // (These may be present if they are not in the package.json dependencies, such as ) + // (These may be present if they are not in the package.json dependencies) // Symlink node_modules/.deno//node_modules/ to // node_modules/ let mut ids = snapshot @@ -757,10 +759,10 @@ async fn sync_resolution_with_fs( // 8. Create symlinks for the workspace packages { - // todo(#24419): this is not exactly correct because it should + // todo(dsherret): this is not exactly correct because it should // install correctly for a workspace (potentially in sub directories), // but this is good enough for a first pass - for workspace in pkg_json_deps_provider.workspace_pkgs() { + for workspace in npm_install_deps_provider.workspace_pkgs() { symlink_package_dir( &workspace.target_dir, &root_node_modules_dir_path.join(&workspace.alias), @@ -985,21 +987,31 @@ impl SetupCache { } } +/// Normalizes a package name for use at `node_modules/.deno/@[_]` +pub fn normalize_pkg_name_for_node_modules_deno_folder(name: &str) -> Cow { + let name = if name.to_lowercase() == name { + Cow::Borrowed(name) + } else { + Cow::Owned(format!("_{}", mixed_case_package_name_encode(name))) + }; + if name.starts_with('@') { + name.replace('/', "+").into() + } else { + name + } +} + fn get_package_folder_id_folder_name( folder_id: &NpmPackageCacheFolderId, ) -> String { let copy_str = if folder_id.copy_index == 0 { - "".to_string() + Cow::Borrowed("") } else { - format!("_{}", folder_id.copy_index) + Cow::Owned(format!("_{}", folder_id.copy_index)) }; let nv = &folder_id.nv; - let name = if nv.name.to_lowercase() == nv.name { - Cow::Borrowed(&nv.name) - } else { - Cow::Owned(format!("_{}", mixed_case_package_name_encode(&nv.name))) - }; - format!("{}@{}{}", name, nv.version, copy_str).replace('/', "+") + let name = normalize_pkg_name_for_node_modules_deno_folder(&nv.name); + format!("{}@{}{}", name, nv.version, copy_str) } fn get_package_folder_id_from_folder_name( diff --git a/cli/npm/managed/resolvers/mod.rs b/cli/npm/managed/resolvers/mod.rs index 2dfc323e91..f5d9e4b057 100644 --- a/cli/npm/managed/resolvers/mod.rs +++ b/cli/npm/managed/resolvers/mod.rs @@ -11,10 +11,11 @@ use deno_npm::NpmSystemInfo; use deno_runtime::deno_fs::FileSystem; use crate::args::LifecycleScriptsConfig; -use crate::args::PackageJsonInstallDepsProvider; +use crate::args::NpmInstallDepsProvider; use crate::util::progress_bar::ProgressBar; pub use self::common::NpmPackageFsResolver; +pub use self::local::normalize_pkg_name_for_node_modules_deno_folder; use self::global::GlobalNpmPackageResolver; use self::local::LocalNpmPackageResolver; @@ -27,7 +28,7 @@ use super::resolution::NpmResolution; pub fn create_npm_fs_resolver( fs: Arc, npm_cache: Arc, - pkg_json_deps_provider: &Arc, + npm_install_deps_provider: &Arc, progress_bar: &ProgressBar, resolution: Arc, tarball_cache: Arc, @@ -39,7 +40,7 @@ pub fn create_npm_fs_resolver( Some(node_modules_folder) => Arc::new(LocalNpmPackageResolver::new( npm_cache, fs, - pkg_json_deps_provider.clone(), + npm_install_deps_provider.clone(), progress_bar.clone(), resolution, tarball_cache, diff --git a/cli/resolver.rs b/cli/resolver.rs index 3f5f79f778..987e23ee11 100644 --- a/cli/resolver.rs +++ b/cli/resolver.rs @@ -502,7 +502,7 @@ impl Resolver for CliGraphResolver { fn resolve( &self, - specifier: &str, + raw_specifier: &str, referrer_range: &deno_graph::Range, mode: ResolutionMode, ) -> Result { @@ -519,7 +519,7 @@ impl Resolver for CliGraphResolver { if let Some(node_resolver) = self.node_resolver.as_ref() { if referrer.scheme() == "file" && node_resolver.in_npm_package(referrer) { return node_resolver - .resolve(specifier, referrer, to_node_mode(mode)) + .resolve(raw_specifier, referrer, to_node_mode(mode)) .map(|res| res.into_url()) .map_err(|e| ResolveError::Other(e.into())); } @@ -528,7 +528,7 @@ impl Resolver for CliGraphResolver { // Attempt to resolve with the workspace resolver let result: Result<_, ResolveError> = self .workspace_resolver - .resolve(specifier, referrer) + .resolve(raw_specifier, referrer) .map_err(|err| match err { MappedResolutionError::Specifier(err) => ResolveError::Specifier(err), MappedResolutionError::ImportMap(err) => { @@ -700,7 +700,7 @@ impl Resolver for CliGraphResolver { // If byonm, check if the bare specifier resolves to an npm package if is_byonm && referrer.scheme() == "file" { let maybe_resolution = node_resolver - .resolve_if_for_npm_pkg(specifier, referrer, to_node_mode(mode)) + .resolve_if_for_npm_pkg(raw_specifier, referrer, to_node_mode(mode)) .map_err(ResolveError::Other)?; if let Some(res) = maybe_resolution { return Ok(res.into_url()); diff --git a/cli/standalone/binary.rs b/cli/standalone/binary.rs index 45d9b7c63b..27308c9013 100644 --- a/cli/standalone/binary.rs +++ b/cli/standalone/binary.rs @@ -45,7 +45,7 @@ use serde::Serialize; use crate::args::CaData; use crate::args::CliOptions; use crate::args::CompileFlags; -use crate::args::PackageJsonInstallDepsProvider; +use crate::args::NpmInstallDepsProvider; use crate::args::PermissionFlags; use crate::args::UnstableConfig; use crate::cache::DenoDir; diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs index a0a312ad9e..f1f687eed5 100644 --- a/cli/standalone/mod.rs +++ b/cli/standalone/mod.rs @@ -48,7 +48,7 @@ use crate::args::get_root_cert_store; use crate::args::npm_pkg_req_ref_to_binary_command; use crate::args::CaData; use crate::args::CacheSetting; -use crate::args::PackageJsonInstallDepsProvider; +use crate::args::NpmInstallDepsProvider; use crate::args::StorageKeyResolver; use crate::cache::Caches; use crate::cache::DenoDirProvider; @@ -138,7 +138,7 @@ pub const UNSUPPORTED_SCHEME: &str = "Unsupported scheme"; impl ModuleLoader for EmbeddedModuleLoader { fn resolve( &self, - specifier: &str, + raw_specifier: &str, referrer: &str, kind: ResolutionKind, ) -> Result { @@ -162,13 +162,15 @@ impl ModuleLoader for EmbeddedModuleLoader { self .shared .node_resolver - .resolve(specifier, &referrer, NodeResolutionMode::Execution)? + .resolve(raw_specifier, &referrer, NodeResolutionMode::Execution)? .into_url(), ); } - let mapped_resolution = - self.shared.workspace_resolver.resolve(specifier, &referrer); + let mapped_resolution = self + .shared + .workspace_resolver + .resolve(raw_specifier, &referrer); match mapped_resolution { Ok(MappedResolution::WorkspaceJsrPackage { specifier, .. }) => { @@ -262,7 +264,7 @@ impl ModuleLoader for EmbeddedModuleLoader { if err.is_unmapped_bare_specifier() && referrer.scheme() == "file" => { let maybe_res = self.shared.node_resolver.resolve_if_for_npm_pkg( - specifier, + raw_specifier, &referrer, NodeResolutionMode::Execution, )?; @@ -502,9 +504,9 @@ pub async fn run( text_only_progress_bar: progress_bar, maybe_node_modules_path, npm_system_info: Default::default(), - package_json_deps_provider: Arc::new( + npm_install_deps_provider: Arc::new( // this is only used for installing packages, which isn't necessary with deno compile - PackageJsonInstallDepsProvider::empty(), + NpmInstallDepsProvider::empty(), ), // create an npmrc that uses the fake npm_registry_url to resolve packages npmrc: Arc::new(ResolvedNpmRc { @@ -554,9 +556,9 @@ pub async fn run( text_only_progress_bar: progress_bar, maybe_node_modules_path: None, npm_system_info: Default::default(), - package_json_deps_provider: Arc::new( + npm_install_deps_provider: Arc::new( // this is only used for installing packages, which isn't necessary with deno compile - PackageJsonInstallDepsProvider::empty(), + NpmInstallDepsProvider::empty(), ), // Packages from different registries are already inlined in the ESZip, // so no need to create actual `.npmrc` configuration. diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index cf7a55d8c8..1b443cafd3 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -795,7 +795,7 @@ fn resolve_graph_specifier_types( } fn resolve_non_graph_specifier_types( - specifier: &str, + raw_specifier: &str, referrer: &ModuleSpecifier, referrer_kind: NodeModuleKind, state: &State, @@ -810,14 +810,16 @@ fn resolve_non_graph_specifier_types( Ok(Some(NodeResolution::into_specifier_and_media_type( node_resolver .resolve( - specifier, + raw_specifier, referrer, referrer_kind, NodeResolutionMode::Types, ) .ok(), ))) - } else if let Ok(npm_req_ref) = NpmPackageReqReference::from_str(specifier) { + } else if let Ok(npm_req_ref) = + NpmPackageReqReference::from_str(raw_specifier) + { debug_assert_eq!(referrer_kind, NodeModuleKind::Esm); // todo(dsherret): add support for injecting this in the graph so // we don't need this special code here. diff --git a/ext/node_resolver/lib.rs b/ext/node_resolver/lib.rs index 1ab972ccfd..f03f770486 100644 --- a/ext/node_resolver/lib.rs +++ b/ext/node_resolver/lib.rs @@ -18,6 +18,7 @@ pub use npm::NpmResolverRc; pub use package_json::load_pkg_json; pub use package_json::PackageJsonThreadLocalCache; pub use path::PathClean; +pub use resolution::parse_npm_pkg_name; pub use resolution::NodeModuleKind; pub use resolution::NodeResolution; pub use resolution::NodeResolutionMode; diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 4693b72223..4f0f8a9850 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -14797,7 +14797,7 @@ fn lsp_byonm() { "severity": 1, "code": "resolver-error", "source": "deno", - "message": "Could not find a matching package for 'npm:chalk' in a package.json file. You must specify this as a package.json dependency when the node_modules folder is not managed by Deno.", + "message": "Could not find a matching package for 'npm:chalk' in the node_modules directory. Ensure you have all your JSR and npm dependencies listed in your deno.json or package.json, then run `deno install`. Alternatively, turn on auto-install by specifying `\"nodeModulesDir\": \"auto\"` in your deno.json file.", }, { "range": { @@ -14842,7 +14842,7 @@ fn lsp_byonm() { "severity": 1, "code": "resolver-error", "source": "deno", - "message": "Could not find a matching package for 'npm:chalk' in a package.json file. You must specify this as a package.json dependency when the node_modules folder is not managed by Deno.", + "message": "Could not find a matching package for 'npm:chalk' in the node_modules directory. Ensure you have all your JSR and npm dependencies listed in your deno.json or package.json, then run `deno install`. Alternatively, turn on auto-install by specifying `\"nodeModulesDir\": \"auto\"` in your deno.json file.", }, ]) ); diff --git a/tests/integration/npm_tests.rs b/tests/integration/npm_tests.rs index db63d4533f..61ef0b22d3 100644 --- a/tests/integration/npm_tests.rs +++ b/tests/integration/npm_tests.rs @@ -2195,7 +2195,7 @@ console.log(getKind()); .args("run --allow-read chalk.ts") .run(); output.assert_matches_text( - r#"error: Could not find a matching package for 'npm:chalk@5' in a package.json file. You must specify this as a package.json dependency when the node_modules folder is not managed by Deno. + r#"error: Could not find a matching package for 'npm:chalk@5' in the node_modules directory. Ensure you have all your JSR and npm dependencies listed in your deno.json or package.json, then run `deno install`. Alternatively, turn on auto-install by specifying `"nodeModulesDir": "auto"` in your deno.json file. at file:///[WILDCARD]chalk.ts:1:19 "#); output.assert_exit_code(1); @@ -2277,7 +2277,7 @@ console.log(getKind()); .args("run --allow-read chalk.ts") .run(); output.assert_matches_text( - r#"error: Could not find a matching package for 'npm:chalk@5' in a package.json file. You must specify this as a package.json dependency when the node_modules folder is not managed by Deno. + r#"error: Could not find a matching package for 'npm:chalk@5' in the node_modules directory. Ensure you have all your JSR and npm dependencies listed in your deno.json or package.json, then run `deno install`. Alternatively, turn on auto-install by specifying `"nodeModulesDir": "auto"` in your deno.json file. at file:///[WILDCARD]chalk.ts:1:19 "#); output.assert_exit_code(1); diff --git a/tests/registry/jsr/@denotest/npm-add/0.5.0/mod.ts b/tests/registry/jsr/@denotest/npm-add/0.5.0/mod.ts new file mode 100644 index 0000000000..733997e7be --- /dev/null +++ b/tests/registry/jsr/@denotest/npm-add/0.5.0/mod.ts @@ -0,0 +1,5 @@ +import * as npmAdd from "npm:@denotest/add@0.5"; + +export function sum(a: number, b: number): number { + return npmAdd.sum(a, b); +} diff --git a/tests/registry/jsr/@denotest/npm-add/0.5.0_meta.json b/tests/registry/jsr/@denotest/npm-add/0.5.0_meta.json new file mode 100644 index 0000000000..631a18d0e5 --- /dev/null +++ b/tests/registry/jsr/@denotest/npm-add/0.5.0_meta.json @@ -0,0 +1,5 @@ +{ + "exports": { + ".": "./mod.ts" + } +} diff --git a/tests/registry/jsr/@denotest/npm-add/1.0.0/mod.ts b/tests/registry/jsr/@denotest/npm-add/1.0.0/mod.ts new file mode 100644 index 0000000000..16663bab61 --- /dev/null +++ b/tests/registry/jsr/@denotest/npm-add/1.0.0/mod.ts @@ -0,0 +1,5 @@ +import * as npmAdd from "npm:@denotest/add@1"; + +export function add(a: number, b: number): number { + return npmAdd.add(a, b); +} diff --git a/tests/registry/jsr/@denotest/npm-add/1.0.0_meta.json b/tests/registry/jsr/@denotest/npm-add/1.0.0_meta.json new file mode 100644 index 0000000000..631a18d0e5 --- /dev/null +++ b/tests/registry/jsr/@denotest/npm-add/1.0.0_meta.json @@ -0,0 +1,5 @@ +{ + "exports": { + ".": "./mod.ts" + } +} diff --git a/tests/registry/jsr/@denotest/npm-add/meta.json b/tests/registry/jsr/@denotest/npm-add/meta.json new file mode 100644 index 0000000000..1f9cb61845 --- /dev/null +++ b/tests/registry/jsr/@denotest/npm-add/meta.json @@ -0,0 +1,6 @@ +{ + "versions": { + "0.5.0": {}, + "1.0.0": {} + } +} diff --git a/tests/specs/install/alias_deno_json/__test__.jsonc b/tests/specs/install/alias_deno_json/__test__.jsonc new file mode 100644 index 0000000000..0398e2eeb5 --- /dev/null +++ b/tests/specs/install/alias_deno_json/__test__.jsonc @@ -0,0 +1,10 @@ +{ + "tempDir": true, + "steps": [{ + "args": "install", + "output": "[WILDCARD]" + }, { + "args": "run --allow-read=. verify.ts", + "output": "true\n" + }] +} diff --git a/tests/specs/install/alias_deno_json/deno.json b/tests/specs/install/alias_deno_json/deno.json new file mode 100644 index 0000000000..a1adfb35e2 --- /dev/null +++ b/tests/specs/install/alias_deno_json/deno.json @@ -0,0 +1,5 @@ +{ + "imports": { + "alias": "npm:@denotest/add" + } +} diff --git a/tests/specs/install/alias_deno_json/package.json b/tests/specs/install/alias_deno_json/package.json new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/specs/install/alias_deno_json/package.json @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/specs/install/alias_deno_json/verify.ts b/tests/specs/install/alias_deno_json/verify.ts new file mode 100644 index 0000000000..ea6cadb709 --- /dev/null +++ b/tests/specs/install/alias_deno_json/verify.ts @@ -0,0 +1,2 @@ +const stat = Deno.statSync(new URL("./node_modules/alias", import.meta.url)); +console.log(stat.isDirectory); diff --git a/tests/specs/install/alias_invalid_path_char/__test__.jsonc b/tests/specs/install/alias_invalid_path_char/__test__.jsonc new file mode 100644 index 0000000000..4a30586350 --- /dev/null +++ b/tests/specs/install/alias_invalid_path_char/__test__.jsonc @@ -0,0 +1,10 @@ +{ + "tempDir": true, + "steps": [{ + "args": "install", + "output": "[WILDCARD]" + }, { + "args": "run --allow-read=. verify.ts", + "output": ".bin\n.deno\n@denotest\n" + }] +} diff --git a/tests/specs/install/alias_invalid_path_char/deno.jsonc b/tests/specs/install/alias_invalid_path_char/deno.jsonc new file mode 100644 index 0000000000..85befb55e8 --- /dev/null +++ b/tests/specs/install/alias_invalid_path_char/deno.jsonc @@ -0,0 +1,7 @@ +{ + "imports": { + // alias*test is an invalid path char on windows, so + // don't create an alias for this + "alias*test": "npm:@denotest/add" + } +} diff --git a/tests/specs/install/alias_invalid_path_char/package.json b/tests/specs/install/alias_invalid_path_char/package.json new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/specs/install/alias_invalid_path_char/package.json @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/specs/install/alias_invalid_path_char/verify.ts b/tests/specs/install/alias_invalid_path_char/verify.ts new file mode 100644 index 0000000000..497e1d8dd9 --- /dev/null +++ b/tests/specs/install/alias_invalid_path_char/verify.ts @@ -0,0 +1,10 @@ +const entries = Array.from( + Deno.readDirSync(new URL("./node_modules", import.meta.url)), +); +const names = entries.map((entry) => entry.name); +names.sort(); + +// won't have the invalid path alias +for (const name of names) { + console.log(name); +} diff --git a/tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/__test__.jsonc b/tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/__test__.jsonc new file mode 100644 index 0000000000..9a149219db --- /dev/null +++ b/tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/__test__.jsonc @@ -0,0 +1,10 @@ +{ + "tempDir": true, + "steps": [{ + "args": "install", + "output": "[WILDCARD]" + }, { + "args": "run --allow-read=. verify.ts", + "output": "verify.out" + }] +} diff --git a/tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/deno.json b/tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/deno.json new file mode 100644 index 0000000000..bbd5b9946a --- /dev/null +++ b/tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/deno.json @@ -0,0 +1,5 @@ +{ + "imports": { + "alias": "jsr:@denotest/add" + } +} diff --git a/tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/package.json b/tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/package.json new file mode 100644 index 0000000000..01835ee933 --- /dev/null +++ b/tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "alias": "npm:@denotest/esm-basic" + } +} diff --git a/tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/verify.out b/tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/verify.out new file mode 100644 index 0000000000..dad0ca2d8f --- /dev/null +++ b/tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/verify.out @@ -0,0 +1,2 @@ +@denotest/esm-basic +[Module: null prototype] { add: [Function: add] } diff --git a/tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/verify.ts b/tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/verify.ts new file mode 100644 index 0000000000..835442322e --- /dev/null +++ b/tests/specs/install/alias_pkg_json_and_deno_json_jsr_pkg/verify.ts @@ -0,0 +1,13 @@ +import * as mod from "alias"; + +const data = JSON.parse( + Deno.readTextFileSync( + new URL("./node_modules/alias/package.json", import.meta.url), + ), +); + +// this should just setup the npm package anyway, even though the alias +// will resolve to the jsr package +console.log(data.name); + +console.log(mod); diff --git a/tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/__test__.jsonc b/tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/__test__.jsonc new file mode 100644 index 0000000000..9a149219db --- /dev/null +++ b/tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/__test__.jsonc @@ -0,0 +1,10 @@ +{ + "tempDir": true, + "steps": [{ + "args": "install", + "output": "[WILDCARD]" + }, { + "args": "run --allow-read=. verify.ts", + "output": "verify.out" + }] +} diff --git a/tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/deno.json b/tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/deno.json new file mode 100644 index 0000000000..a1adfb35e2 --- /dev/null +++ b/tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/deno.json @@ -0,0 +1,5 @@ +{ + "imports": { + "alias": "npm:@denotest/add" + } +} diff --git a/tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/package.json b/tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/package.json new file mode 100644 index 0000000000..01835ee933 --- /dev/null +++ b/tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "alias": "npm:@denotest/esm-basic" + } +} diff --git a/tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/verify.out b/tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/verify.out new file mode 100644 index 0000000000..bc6cb31f7c --- /dev/null +++ b/tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/verify.out @@ -0,0 +1,5 @@ +@denotest/add +[Module: null prototype] { + add: [Function (anonymous)], + default: { add: [Function (anonymous)] } +} diff --git a/tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/verify.ts b/tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/verify.ts new file mode 100644 index 0000000000..bcc0a5d877 --- /dev/null +++ b/tests/specs/install/alias_pkg_json_and_deno_json_npm_pkg/verify.ts @@ -0,0 +1,10 @@ +import * as mod from "alias"; + +const data = JSON.parse( + Deno.readTextFileSync( + new URL("./node_modules/alias/package.json", import.meta.url), + ), +); + +console.log(data.name); +console.log(mod); diff --git a/tests/specs/install/byonm_jsr_npm_dep/__test__.jsonc b/tests/specs/install/byonm_jsr_npm_dep/__test__.jsonc new file mode 100644 index 0000000000..ca7064aaf3 --- /dev/null +++ b/tests/specs/install/byonm_jsr_npm_dep/__test__.jsonc @@ -0,0 +1,10 @@ +{ + "tempDir": true, + "steps": [{ + "args": "install", + "output": "[WILDCARD]" + }, { + "args": "run main.ts", + "output": "3\n4\n" + }] +} diff --git a/tests/specs/install/byonm_jsr_npm_dep/deno.json b/tests/specs/install/byonm_jsr_npm_dep/deno.json new file mode 100644 index 0000000000..2127bd2860 --- /dev/null +++ b/tests/specs/install/byonm_jsr_npm_dep/deno.json @@ -0,0 +1,6 @@ +{ + "imports": { + "@denotest/npm-add": "jsr:@denotest/npm-add@1", + "@denotest/npm-add-0-5": "jsr:@denotest/npm-add@0.5" + } +} diff --git a/tests/specs/install/byonm_jsr_npm_dep/main.ts b/tests/specs/install/byonm_jsr_npm_dep/main.ts new file mode 100644 index 0000000000..a75e65bd88 --- /dev/null +++ b/tests/specs/install/byonm_jsr_npm_dep/main.ts @@ -0,0 +1,5 @@ +import { add } from "@denotest/npm-add"; +import { sum } from "@denotest/npm-add-0-5"; + +console.log(add(1, 2)); +console.log(sum(2, 2)); diff --git a/tests/specs/install/byonm_jsr_npm_dep/package.json b/tests/specs/install/byonm_jsr_npm_dep/package.json new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/tests/specs/install/byonm_jsr_npm_dep/package.json @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/specs/workspaces/nested_deno_pkg_npm_conflict/__test__.jsonc b/tests/specs/workspaces/nested_deno_pkg_npm_conflict/__test__.jsonc new file mode 100644 index 0000000000..1bad68fd4e --- /dev/null +++ b/tests/specs/workspaces/nested_deno_pkg_npm_conflict/__test__.jsonc @@ -0,0 +1,13 @@ +{ + "tempDir": true, + "steps": [{ + "args": "install", + "output": "[WILDCARD]" + }, { + "args": "run --allow-read main.js", + "output": "4\n0.5.0\n" + }, { + "args": "run --allow-read member/main.js", + "output": "3\n1.0.0\n" + }] +} diff --git a/tests/specs/workspaces/nested_deno_pkg_npm_conflict/deno.json b/tests/specs/workspaces/nested_deno_pkg_npm_conflict/deno.json new file mode 100644 index 0000000000..f03a591e23 --- /dev/null +++ b/tests/specs/workspaces/nested_deno_pkg_npm_conflict/deno.json @@ -0,0 +1,7 @@ +{ + "nodeModulesDir": "manual", + "workspace": ["./member"], + "imports": { + "@denotest/add": "npm:@denotest/add@0.5" + } +} diff --git a/tests/specs/workspaces/nested_deno_pkg_npm_conflict/main.js b/tests/specs/workspaces/nested_deno_pkg_npm_conflict/main.js new file mode 100644 index 0000000000..188da6f975 --- /dev/null +++ b/tests/specs/workspaces/nested_deno_pkg_npm_conflict/main.js @@ -0,0 +1,9 @@ +import { sum } from "@denotest/add"; + +console.log(sum(2, 2)); + +console.log( + JSON.parse(Deno.readTextFileSync( + new URL("node_modules/@denotest/add/package.json", import.meta.url), + )).version, +); diff --git a/tests/specs/workspaces/nested_deno_pkg_npm_conflict/member/deno.json b/tests/specs/workspaces/nested_deno_pkg_npm_conflict/member/deno.json new file mode 100644 index 0000000000..1d36d0aab7 --- /dev/null +++ b/tests/specs/workspaces/nested_deno_pkg_npm_conflict/member/deno.json @@ -0,0 +1,5 @@ +{ + "imports": { + "@denotest/add": "npm:@denotest/add@1" + } +} diff --git a/tests/specs/workspaces/nested_deno_pkg_npm_conflict/member/main.js b/tests/specs/workspaces/nested_deno_pkg_npm_conflict/member/main.js new file mode 100644 index 0000000000..0f64cf5532 --- /dev/null +++ b/tests/specs/workspaces/nested_deno_pkg_npm_conflict/member/main.js @@ -0,0 +1,9 @@ +import { add } from "@denotest/add"; + +console.log(add(1, 2)); + +console.log( + JSON.parse(Deno.readTextFileSync( + new URL("node_modules/@denotest/add/package.json", import.meta.url), + )).version, +); From 5400f1af6cdca6a71de80093c9d49cdde1f34ce5 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 4 Sep 2024 17:03:09 +0200 Subject: [PATCH 31/45] fix(windows): Deno.Command - align binary resolution with linux and mac (#25429) --- tests/unit/command_test.ts | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/unit/command_test.ts b/tests/unit/command_test.ts index dd83525473..6352e1fc7b 100644 --- a/tests/unit/command_test.ts +++ b/tests/unit/command_test.ts @@ -975,3 +975,35 @@ Deno.test( ); }, ); + +Deno.test( + { permissions: { write: true, run: true, read: true } }, + async function commandWithCwdOrPath() { + const cwd = Deno.makeTempDirSync({ prefix: "deno_command_test" }); + try { + const suffix = Deno.build.os === "windows" ? ".exe" : ""; + Deno.mkdirSync(`${cwd}/subdir`); + Deno.copyFileSync(Deno.execPath(), `${cwd}/subdir/my_binary${suffix}`); + // cwd + { + const output = await new Deno.Command(`./my_binary${suffix}`, { + cwd: `${cwd}/subdir`, + args: ["-v"], + }).output(); + assertEquals(output.success, true); + } + // path + { + const output = await new Deno.Command(`my_binary${suffix}`, { + env: { + PATH: `${cwd}/subdir`, + }, + args: ["-v"], + }).output(); + assertEquals(output.success, true); + } + } finally { + Deno.removeSync(cwd, { recursive: true }); + } + }, +); From 07ad47da53d1fbabf6fc5d26c89dc6ba683d27fd Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 4 Sep 2024 17:39:30 +0200 Subject: [PATCH 32/45] BREAKING(config): make supported compilerOptions an allow list (#25432) Deno has been using a deny list, which doesn't make sense because a lot of these options don't even work. Closes #25363 --- Cargo.lock | 5 +++-- cli/Cargo.toml | 2 +- cli/args/deno_json.rs | 16 ++++++++++++++++ cli/args/mod.rs | 5 +++-- cli/factory.rs | 5 ++--- cli/lsp/config.rs | 8 +++----- cli/lsp/language_server.rs | 5 ++--- cli/tools/check.rs | 5 ++--- cli/tools/compile.rs | 2 ++ tests/integration/run_tests.rs | 5 ----- tests/testdata/run/checkjs.tsconfig.json | 1 - .../run/decorators/experimental/ts/main.out | 1 + .../no_check_imports_not_used_as_values/hello.ts | 2 -- .../no_check_imports_not_used_as_values/main.out | 2 -- .../no_check_imports_not_used_as_values/main.ts | 4 ---- .../preserve_imports.tsconfig.json | 5 ----- tools/lint.js | 2 +- 17 files changed, 36 insertions(+), 39 deletions(-) delete mode 100644 tests/testdata/run/no_check_imports_not_used_as_values/hello.ts delete mode 100644 tests/testdata/run/no_check_imports_not_used_as_values/main.out delete mode 100644 tests/testdata/run/no_check_imports_not_used_as_values/main.ts delete mode 100644 tests/testdata/run/no_check_imports_not_used_as_values/preserve_imports.tsconfig.json diff --git a/Cargo.lock b/Cargo.lock index 7ab6733dca..a4eae70e48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1374,9 +1374,9 @@ dependencies = [ [[package]] name = "deno_config" -version = "0.32.0" +version = "0.33.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c774f2e49b4ca47f1fe5c39e1775d1434280a4f168252fed8f4a3f2230868448" +checksum = "495df7ebed4feee5c0eb7631b0b86432bb6370638cf81d5eeb5769aab55fb2de" dependencies = [ "anyhow", "deno_package_json", @@ -1388,6 +1388,7 @@ dependencies = [ "jsonc-parser", "log", "percent-encoding", + "phf 0.11.2", "serde", "serde_json", "thiserror", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index d7d8eeb7c6..016488e5c2 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -65,7 +65,7 @@ winres.workspace = true [dependencies] deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] } deno_cache_dir = { workspace = true } -deno_config = { version = "=0.32.0", features = ["workspace", "sync"] } +deno_config = { version = "=0.33.1", features = ["workspace", "sync"] } deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_doc = { version = "0.148.0", features = ["html", "syntect"] } deno_graph = { version = "=0.82.0" } diff --git a/cli/args/deno_json.rs b/cli/args/deno_json.rs index cbc33aa0b2..e9ab0189f5 100644 --- a/cli/args/deno_json.rs +++ b/cli/args/deno_json.rs @@ -2,6 +2,7 @@ use std::collections::HashSet; +use deno_config::deno_json::TsConfigForEmit; use deno_core::serde_json; use deno_semver::jsr::JsrDepPackageReq; use deno_semver::jsr::JsrPackageReqReference; @@ -105,3 +106,18 @@ fn values_to_set<'a>( } entries } + +pub fn check_warn_tsconfig(ts_config: &TsConfigForEmit) { + if let Some(ignored_options) = &ts_config.maybe_ignored_options { + log::warn!("{}", ignored_options); + } + let serde_json::Value::Object(obj) = &ts_config.ts_config.0 else { + return; + }; + if obj.get("experimentalDecorators") == Some(&serde_json::Value::Bool(true)) { + log::warn!( + "{} experimentalDecorators compiler option is deprecated and may be removed at any time", + deno_runtime::colors::yellow("Warning"), + ); + } +} diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 4927cf7d02..f7623323fe 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -42,6 +42,7 @@ pub use deno_config::deno_json::TsConfigForEmit; pub use deno_config::deno_json::TsConfigType; pub use deno_config::deno_json::TsTypeLib; pub use deno_config::glob::FilePatterns; +pub use deno_json::check_warn_tsconfig; pub use flags::*; pub use lockfile::CliLockfile; pub use package_json::NpmInstallDepsProvider; @@ -1220,7 +1221,7 @@ impl CliOptions { if let Some(flag) = self.flags.node_modules_dir { return Ok(Some(flag)); } - self.workspace().node_modules_dir_mode().map_err(Into::into) + self.workspace().node_modules_dir().map_err(Into::into) } pub fn vendor_dir_path(&self) -> Option<&PathBuf> { @@ -1731,7 +1732,7 @@ fn resolve_node_modules_folder( Some(mode.uses_node_modules_dir()) } else { workspace - .node_modules_dir_mode()? + .node_modules_dir()? .map(|m| m.uses_node_modules_dir()) .or(flags.vendor) .or_else(|| root_folder.deno_json.as_ref().and_then(|c| c.json.vendor)) diff --git a/cli/factory.rs b/cli/factory.rs index 4215530584..81212c7885 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -1,5 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use crate::args::check_warn_tsconfig; use crate::args::get_root_cert_store; use crate::args::CaData; use crate::args::CliOptions; @@ -520,9 +521,7 @@ impl CliFactory { let cli_options = self.cli_options()?; let ts_config_result = cli_options.resolve_ts_config_for_emit(TsConfigType::Emit)?; - if let Some(ignored_options) = ts_config_result.maybe_ignored_options { - warn!("{}", ignored_options); - } + check_warn_tsconfig(&ts_config_result); let (transpile_options, emit_options) = crate::args::ts_config_to_transpile_and_emit_options( ts_config_result.ts_config, diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index f9262d12e8..fcea96aa5d 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -1387,10 +1387,8 @@ impl ConfigData { } } - let node_modules_dir = member_dir - .workspace - .node_modules_dir_mode() - .unwrap_or_default(); + let node_modules_dir = + member_dir.workspace.node_modules_dir().unwrap_or_default(); let byonm = match node_modules_dir { Some(mode) => mode == NodeModulesDirMode::Manual, None => member_dir.workspace.root_pkg_json().is_some(), @@ -1873,7 +1871,7 @@ fn resolve_node_modules_dir( // `nodeModulesDir: true` setting in the deno.json file. This is to // reduce the chance of modifying someone's node_modules directory // without them having asked us to do so. - let node_modules_mode = workspace.node_modules_dir_mode().ok().flatten(); + let node_modules_mode = workspace.node_modules_dir().ok().flatten(); let explicitly_disabled = node_modules_mode == Some(NodeModulesDirMode::None); if explicitly_disabled { return None; diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 05c54dfc67..2bb13e5e4c 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -966,9 +966,8 @@ impl Inner { .await; for config_file in self.config.tree.config_files() { (|| { - let compiler_options = config_file.to_compiler_options().ok()?.0; - let compiler_options_obj = compiler_options.as_object()?; - let jsx_import_source = compiler_options_obj.get("jsxImportSource")?; + let compiler_options = config_file.to_compiler_options().ok()?.options; + let jsx_import_source = compiler_options.get("jsxImportSource")?; let jsx_import_source = jsx_import_source.as_str()?; let referrer = config_file.specifier.clone(); let specifier = Url::parse(&format!( diff --git a/cli/tools/check.rs b/cli/tools/check.rs index a2bfb9d9bb..9232f324be 100644 --- a/cli/tools/check.rs +++ b/cli/tools/check.rs @@ -14,6 +14,7 @@ use deno_terminal::colors; use once_cell::sync::Lazy; use regex::Regex; +use crate::args::check_warn_tsconfig; use crate::args::CliOptions; use crate::args::TsConfig; use crate::args::TsConfigType; @@ -118,9 +119,7 @@ impl TypeChecker { .cli_options .resolve_ts_config_for_emit(TsConfigType::Check { lib: options.lib })?; if options.log_ignored_options { - if let Some(ignored_options) = ts_config_result.maybe_ignored_options { - log::warn!("{}", ignored_options); - } + check_warn_tsconfig(&ts_config_result); } let type_check_mode = options.type_check_mode; diff --git a/cli/tools/compile.rs b/cli/tools/compile.rs index 90ee0e2706..b9620cfded 100644 --- a/cli/tools/compile.rs +++ b/cli/tools/compile.rs @@ -1,5 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use crate::args::check_warn_tsconfig; use crate::args::CompileFlags; use crate::args::Flags; use crate::factory::CliFactory; @@ -79,6 +80,7 @@ pub async fn compile( let ts_config_for_emit = cli_options .resolve_ts_config_for_emit(deno_config::deno_json::TsConfigType::Emit)?; + check_warn_tsconfig(&ts_config_for_emit); let (transpile_options, emit_options) = crate::args::ts_config_to_transpile_and_emit_options( ts_config_for_emit.ts_config, diff --git a/tests/integration/run_tests.rs b/tests/integration/run_tests.rs index 47fcdb657b..117e5709e4 100644 --- a/tests/integration/run_tests.rs +++ b/tests/integration/run_tests.rs @@ -483,11 +483,6 @@ itest!(dynamic_import_concurrent_non_statically_analyzable { http_server: true, }); -itest!(no_check_imports_not_used_as_values { - args: "run --config run/no_check_imports_not_used_as_values/preserve_imports.tsconfig.json --no-check run/no_check_imports_not_used_as_values/main.ts", - output: "run/no_check_imports_not_used_as_values/main.out", - }); - itest!(_088_dynamic_import_already_evaluating { args: "run --allow-read run/088_dynamic_import_already_evaluating.ts", output: "run/088_dynamic_import_already_evaluating.ts.out", diff --git a/tests/testdata/run/checkjs.tsconfig.json b/tests/testdata/run/checkjs.tsconfig.json index 46d96db9ed..08ac60b6cd 100644 --- a/tests/testdata/run/checkjs.tsconfig.json +++ b/tests/testdata/run/checkjs.tsconfig.json @@ -1,6 +1,5 @@ { "compilerOptions": { - "allowJs": true, "checkJs": true } } diff --git a/tests/testdata/run/decorators/experimental/ts/main.out b/tests/testdata/run/decorators/experimental/ts/main.out index ee77417cf2..ea64fbaa63 100644 --- a/tests/testdata/run/decorators/experimental/ts/main.out +++ b/tests/testdata/run/decorators/experimental/ts/main.out @@ -1,2 +1,3 @@ +Warning experimentalDecorators compiler option is deprecated and may be removed at any time Check [WILDCARD] SomeClass { someField: "asdf" } diff --git a/tests/testdata/run/no_check_imports_not_used_as_values/hello.ts b/tests/testdata/run/no_check_imports_not_used_as_values/hello.ts deleted file mode 100644 index 1a9d8f114f..0000000000 --- a/tests/testdata/run/no_check_imports_not_used_as_values/hello.ts +++ /dev/null @@ -1,2 +0,0 @@ -export type SomeType = unknown; -console.log("Hello, world!"); diff --git a/tests/testdata/run/no_check_imports_not_used_as_values/main.out b/tests/testdata/run/no_check_imports_not_used_as_values/main.out deleted file mode 100644 index f744c41839..0000000000 --- a/tests/testdata/run/no_check_imports_not_used_as_values/main.out +++ /dev/null @@ -1,2 +0,0 @@ -[WILDCARD]Hello, world! -Hi! diff --git a/tests/testdata/run/no_check_imports_not_used_as_values/main.ts b/tests/testdata/run/no_check_imports_not_used_as_values/main.ts deleted file mode 100644 index 80e17aa35d..0000000000 --- a/tests/testdata/run/no_check_imports_not_used_as_values/main.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { SomeType } from "./hello.ts"; - -const string: SomeType = "Hi!"; -console.log(string); diff --git a/tests/testdata/run/no_check_imports_not_used_as_values/preserve_imports.tsconfig.json b/tests/testdata/run/no_check_imports_not_used_as_values/preserve_imports.tsconfig.json deleted file mode 100644 index 9b19291aa0..0000000000 --- a/tests/testdata/run/no_check_imports_not_used_as_values/preserve_imports.tsconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "compilerOptions": { - "importsNotUsedAsValues": "preserve" - } -} diff --git a/tools/lint.js b/tools/lint.js index 08b551e984..4bffead0cc 100755 --- a/tools/lint.js +++ b/tools/lint.js @@ -221,7 +221,7 @@ async function ensureNoNewITests() { "pm_tests.rs": 0, "publish_tests.rs": 0, "repl_tests.rs": 0, - "run_tests.rs": 350, + "run_tests.rs": 349, "shared_library_tests.rs": 0, "task_tests.rs": 30, "test_tests.rs": 75, From e27a19c02c537626d7874f7521f4e39d6dfad0af Mon Sep 17 00:00:00 2001 From: denobot <33910674+denobot@users.noreply.github.com> Date: Wed, 4 Sep 2024 13:16:24 -0400 Subject: [PATCH 33/45] chore: forward v1.46.3 release commit to main (#25425) This is the release commit being forwarded back to main for 1.46.3 --- .github/workflows/ci.generate.ts | 2 +- .github/workflows/ci.yml | 8 ++--- Cargo.lock | 54 ++++++++++++++++---------------- Cargo.toml | 54 ++++++++++++++++---------------- Releases.md | 11 +++++++ bench_util/Cargo.toml | 2 +- cli/napi/sym/Cargo.toml | 2 +- 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/net/Cargo.toml | 2 +- ext/node/Cargo.toml | 2 +- ext/node_resolver/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 +- runtime/Cargo.toml | 2 +- runtime/permissions/Cargo.toml | 2 +- 32 files changed, 97 insertions(+), 86 deletions(-) diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index cbf3a92d3a..02fd070d49 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 = 14; +const cacheVersion = 15; const ubuntuX86Runner = "ubuntu-22.04"; const ubuntuX86XlRunner = "ubuntu-22.04-xl"; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4aa0abdd7..3e8c6248b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -367,8 +367,8 @@ jobs: path: |- ~/.cargo/registry/index ~/.cargo/registry/cache - key: '14-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' - restore-keys: '14-cargo-home-${{ matrix.os }}-${{ matrix.arch }}' + key: '15-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' + restore-keys: '15-cargo-home-${{ matrix.os }}-${{ matrix.arch }}' if: '!(matrix.skip)' - name: Restore cache build output (PR) uses: actions/cache/restore@v4 @@ -381,7 +381,7 @@ jobs: !./target/*/*.zip !./target/*/*.tar.gz key: never_saved - restore-keys: '14-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-' + restore-keys: '15-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 @@ -670,7 +670,7 @@ jobs: !./target/*/gn_out !./target/*/*.zip !./target/*/*.tar.gz - key: '14-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' + key: '15-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' publish-canary: name: publish canary runs-on: ubuntu-22.04 diff --git a/Cargo.lock b/Cargo.lock index a4eae70e48..e45a739807 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1315,7 +1315,7 @@ dependencies = [ [[package]] name = "deno_bench_util" -version = "0.161.0" +version = "0.162.0" dependencies = [ "bencher", "deno_core", @@ -1324,7 +1324,7 @@ dependencies = [ [[package]] name = "deno_broadcast_channel" -version = "0.161.0" +version = "0.162.0" dependencies = [ "async-trait", "deno_core", @@ -1334,7 +1334,7 @@ dependencies = [ [[package]] name = "deno_cache" -version = "0.99.0" +version = "0.100.0" dependencies = [ "async-trait", "deno_core", @@ -1364,7 +1364,7 @@ dependencies = [ [[package]] name = "deno_canvas" -version = "0.36.0" +version = "0.37.0" dependencies = [ "deno_core", "deno_webgpu", @@ -1397,7 +1397,7 @@ dependencies = [ [[package]] name = "deno_console" -version = "0.167.0" +version = "0.168.0" dependencies = [ "deno_core", ] @@ -1442,7 +1442,7 @@ checksum = "a13951ea98c0a4c372f162d669193b4c9d991512de9f2381dd161027f34b26b1" [[package]] name = "deno_cron" -version = "0.47.0" +version = "0.48.0" dependencies = [ "anyhow", "async-trait", @@ -1454,7 +1454,7 @@ dependencies = [ [[package]] name = "deno_crypto" -version = "0.181.0" +version = "0.182.0" dependencies = [ "aes", "aes-gcm", @@ -1513,7 +1513,7 @@ dependencies = [ [[package]] name = "deno_fetch" -version = "0.191.0" +version = "0.192.0" dependencies = [ "base64 0.21.7", "bytes", @@ -1545,7 +1545,7 @@ dependencies = [ [[package]] name = "deno_ffi" -version = "0.154.0" +version = "0.155.0" dependencies = [ "deno_core", "deno_permissions", @@ -1562,7 +1562,7 @@ dependencies = [ [[package]] name = "deno_fs" -version = "0.77.0" +version = "0.78.0" dependencies = [ "async-trait", "base32", @@ -1611,7 +1611,7 @@ dependencies = [ [[package]] name = "deno_http" -version = "0.165.0" +version = "0.166.0" dependencies = [ "async-compression", "async-trait", @@ -1650,7 +1650,7 @@ dependencies = [ [[package]] name = "deno_io" -version = "0.77.0" +version = "0.78.0" dependencies = [ "async-trait", "deno_core", @@ -1671,7 +1671,7 @@ dependencies = [ [[package]] name = "deno_kv" -version = "0.75.0" +version = "0.76.0" dependencies = [ "anyhow", "async-trait", @@ -1741,7 +1741,7 @@ dependencies = [ [[package]] name = "deno_napi" -version = "0.98.0" +version = "0.99.0" dependencies = [ "deno_core", "deno_permissions", @@ -1763,7 +1763,7 @@ dependencies = [ [[package]] name = "deno_net" -version = "0.159.0" +version = "0.160.0" dependencies = [ "deno_core", "deno_permissions", @@ -1779,7 +1779,7 @@ dependencies = [ [[package]] name = "deno_node" -version = "0.104.0" +version = "0.105.0" dependencies = [ "aead-gcm-stream", "aes", @@ -1915,7 +1915,7 @@ dependencies = [ [[package]] name = "deno_permissions" -version = "0.27.0" +version = "0.28.0" dependencies = [ "deno_core", "deno_terminal 0.2.0", @@ -1930,7 +1930,7 @@ dependencies = [ [[package]] name = "deno_runtime" -version = "0.176.0" +version = "0.177.0" dependencies = [ "deno_ast", "deno_broadcast_channel", @@ -2043,7 +2043,7 @@ dependencies = [ [[package]] name = "deno_tls" -version = "0.154.0" +version = "0.155.0" dependencies = [ "deno_core", "deno_native_certs", @@ -2091,7 +2091,7 @@ dependencies = [ [[package]] name = "deno_url" -version = "0.167.0" +version = "0.168.0" dependencies = [ "deno_bench_util", "deno_console", @@ -2102,7 +2102,7 @@ dependencies = [ [[package]] name = "deno_web" -version = "0.198.0" +version = "0.199.0" dependencies = [ "async-trait", "base64-simd 0.8.0", @@ -2123,7 +2123,7 @@ dependencies = [ [[package]] name = "deno_webgpu" -version = "0.134.0" +version = "0.135.0" dependencies = [ "deno_core", "raw-window-handle", @@ -2135,7 +2135,7 @@ dependencies = [ [[package]] name = "deno_webidl" -version = "0.167.0" +version = "0.168.0" dependencies = [ "deno_bench_util", "deno_core", @@ -2143,7 +2143,7 @@ dependencies = [ [[package]] name = "deno_websocket" -version = "0.172.0" +version = "0.173.0" dependencies = [ "bytes", "deno_core", @@ -2164,7 +2164,7 @@ dependencies = [ [[package]] name = "deno_webstorage" -version = "0.162.0" +version = "0.163.0" dependencies = [ "deno_core", "deno_web", @@ -4513,7 +4513,7 @@ dependencies = [ [[package]] name = "napi_sym" -version = "0.97.0" +version = "0.98.0" dependencies = [ "quote", "serde", @@ -4582,7 +4582,7 @@ dependencies = [ [[package]] name = "node_resolver" -version = "0.6.0" +version = "0.7.0" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 70d74eb9a9..a33fa6d94c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,14 +47,14 @@ repository = "https://github.com/denoland/deno" deno_ast = { version = "=0.42.0", features = ["transpiling"] } deno_core = { version = "0.307.0" } -deno_bench_util = { version = "0.161.0", path = "./bench_util" } +deno_bench_util = { version = "0.162.0", path = "./bench_util" } deno_lockfile = "=0.23.0" deno_media_type = { version = "0.1.4", features = ["module_specifier"] } -deno_permissions = { version = "0.27.0", path = "./runtime/permissions" } -deno_runtime = { version = "0.176.0", path = "./runtime" } +deno_permissions = { version = "0.28.0", path = "./runtime/permissions" } +deno_runtime = { version = "0.177.0", path = "./runtime" } deno_semver = "=0.5.13" deno_terminal = "0.2.0" -napi_sym = { version = "0.97.0", path = "./cli/napi/sym" } +napi_sym = { version = "0.98.0", path = "./cli/napi/sym" } test_util = { package = "test_server", path = "./tests/util/server" } denokv_proto = "0.8.1" @@ -63,29 +63,29 @@ denokv_remote = "0.8.1" denokv_sqlite = { default-features = false, version = "0.8.2" } # exts -deno_broadcast_channel = { version = "0.161.0", path = "./ext/broadcast_channel" } -deno_cache = { version = "0.99.0", path = "./ext/cache" } -deno_canvas = { version = "0.36.0", path = "./ext/canvas" } -deno_console = { version = "0.167.0", path = "./ext/console" } -deno_cron = { version = "0.47.0", path = "./ext/cron" } -deno_crypto = { version = "0.181.0", path = "./ext/crypto" } -deno_fetch = { version = "0.191.0", path = "./ext/fetch" } -deno_ffi = { version = "0.154.0", path = "./ext/ffi" } -deno_fs = { version = "0.77.0", path = "./ext/fs" } -deno_http = { version = "0.165.0", path = "./ext/http" } -deno_io = { version = "0.77.0", path = "./ext/io" } -deno_kv = { version = "0.75.0", path = "./ext/kv" } -deno_napi = { version = "0.98.0", path = "./ext/napi" } -deno_net = { version = "0.159.0", path = "./ext/net" } -deno_node = { version = "0.104.0", path = "./ext/node" } -deno_tls = { version = "0.154.0", path = "./ext/tls" } -deno_url = { version = "0.167.0", path = "./ext/url" } -deno_web = { version = "0.198.0", path = "./ext/web" } -deno_webgpu = { version = "0.134.0", path = "./ext/webgpu" } -deno_webidl = { version = "0.167.0", path = "./ext/webidl" } -deno_websocket = { version = "0.172.0", path = "./ext/websocket" } -deno_webstorage = { version = "0.162.0", path = "./ext/webstorage" } -node_resolver = { version = "0.6.0", path = "./ext/node_resolver" } +deno_broadcast_channel = { version = "0.162.0", path = "./ext/broadcast_channel" } +deno_cache = { version = "0.100.0", path = "./ext/cache" } +deno_canvas = { version = "0.37.0", path = "./ext/canvas" } +deno_console = { version = "0.168.0", path = "./ext/console" } +deno_cron = { version = "0.48.0", path = "./ext/cron" } +deno_crypto = { version = "0.182.0", path = "./ext/crypto" } +deno_fetch = { version = "0.192.0", path = "./ext/fetch" } +deno_ffi = { version = "0.155.0", path = "./ext/ffi" } +deno_fs = { version = "0.78.0", path = "./ext/fs" } +deno_http = { version = "0.166.0", path = "./ext/http" } +deno_io = { version = "0.78.0", path = "./ext/io" } +deno_kv = { version = "0.76.0", path = "./ext/kv" } +deno_napi = { version = "0.99.0", path = "./ext/napi" } +deno_net = { version = "0.160.0", path = "./ext/net" } +deno_node = { version = "0.105.0", path = "./ext/node" } +deno_tls = { version = "0.155.0", path = "./ext/tls" } +deno_url = { version = "0.168.0", path = "./ext/url" } +deno_web = { version = "0.199.0", path = "./ext/web" } +deno_webgpu = { version = "0.135.0", path = "./ext/webgpu" } +deno_webidl = { version = "0.168.0", path = "./ext/webidl" } +deno_websocket = { version = "0.173.0", path = "./ext/websocket" } +deno_webstorage = { version = "0.163.0", path = "./ext/webstorage" } +node_resolver = { version = "0.7.0", path = "./ext/node_resolver" } aes = "=0.8.3" anyhow = "1.0.57" diff --git a/Releases.md b/Releases.md index 87460ddc7b..a6e4d84e7b 100644 --- a/Releases.md +++ b/Releases.md @@ -6,6 +6,17 @@ https://github.com/denoland/deno/releases We also have one-line install commands at: https://github.com/denoland/deno_install +### 1.46.3 / 2024.09.04 + +- feat(upgrade): print info links for Deno 2 RC releases (#25225) +- fix(cli): Map error kind to `PermissionDenied` when symlinking fails due to + permissions (#25398) +- fix(cli/tools): correct `deno init --serve` template behavior (#25318) +- fix(ext/node): session close during stream setup (#25170) +- fix(publish): ensure provenance is spec compliant (#25200) +- fix(upgrade): more informative information on invalid version (#25319) +- fix: fix jupyter display function type (#25326) + ### 1.46.2 / 2024.08.29 - Revert "feat(fetch): accept async iterables for body" (#25207) diff --git a/bench_util/Cargo.toml b/bench_util/Cargo.toml index 37c8b0459c..5a04ab5355 100644 --- a/bench_util/Cargo.toml +++ b/bench_util/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_bench_util" -version = "0.161.0" +version = "0.162.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/cli/napi/sym/Cargo.toml b/cli/napi/sym/Cargo.toml index 909421adbb..eba5fa41cf 100644 --- a/cli/napi/sym/Cargo.toml +++ b/cli/napi/sym/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "napi_sym" -version = "0.97.0" +version = "0.98.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/broadcast_channel/Cargo.toml b/ext/broadcast_channel/Cargo.toml index fc12fda6d7..8b8c9a1883 100644 --- a/ext/broadcast_channel/Cargo.toml +++ b/ext/broadcast_channel/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_broadcast_channel" -version = "0.161.0" +version = "0.162.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/cache/Cargo.toml b/ext/cache/Cargo.toml index 12491f2806..5c6f162f17 100644 --- a/ext/cache/Cargo.toml +++ b/ext/cache/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_cache" -version = "0.99.0" +version = "0.100.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/canvas/Cargo.toml b/ext/canvas/Cargo.toml index 9b2fd1008f..96881682a4 100644 --- a/ext/canvas/Cargo.toml +++ b/ext/canvas/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_canvas" -version = "0.36.0" +version = "0.37.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/console/Cargo.toml b/ext/console/Cargo.toml index 4d1f1aef61..ef64398584 100644 --- a/ext/console/Cargo.toml +++ b/ext/console/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_console" -version = "0.167.0" +version = "0.168.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/cron/Cargo.toml b/ext/cron/Cargo.toml index 19aef63b2c..966ac450e9 100644 --- a/ext/cron/Cargo.toml +++ b/ext/cron/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_cron" -version = "0.47.0" +version = "0.48.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/crypto/Cargo.toml b/ext/crypto/Cargo.toml index 88dfd43661..beea81d1e8 100644 --- a/ext/crypto/Cargo.toml +++ b/ext/crypto/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_crypto" -version = "0.181.0" +version = "0.182.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/fetch/Cargo.toml b/ext/fetch/Cargo.toml index 0b74f7847a..2ec1c25f0c 100644 --- a/ext/fetch/Cargo.toml +++ b/ext/fetch/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_fetch" -version = "0.191.0" +version = "0.192.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/ffi/Cargo.toml b/ext/ffi/Cargo.toml index 69dc80bf1b..8c4ccdc9ba 100644 --- a/ext/ffi/Cargo.toml +++ b/ext/ffi/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_ffi" -version = "0.154.0" +version = "0.155.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/fs/Cargo.toml b/ext/fs/Cargo.toml index 62454de968..894b307efa 100644 --- a/ext/fs/Cargo.toml +++ b/ext/fs/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_fs" -version = "0.77.0" +version = "0.78.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/http/Cargo.toml b/ext/http/Cargo.toml index 149bdaa5fe..ba21e3f30d 100644 --- a/ext/http/Cargo.toml +++ b/ext/http/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_http" -version = "0.165.0" +version = "0.166.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/io/Cargo.toml b/ext/io/Cargo.toml index f2b6b4e235..22c346764b 100644 --- a/ext/io/Cargo.toml +++ b/ext/io/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_io" -version = "0.77.0" +version = "0.78.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/kv/Cargo.toml b/ext/kv/Cargo.toml index d7779ee65d..eee4762eb4 100644 --- a/ext/kv/Cargo.toml +++ b/ext/kv/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_kv" -version = "0.75.0" +version = "0.76.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/napi/Cargo.toml b/ext/napi/Cargo.toml index 3b54594735..aabf6d31f5 100644 --- a/ext/napi/Cargo.toml +++ b/ext/napi/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_napi" -version = "0.98.0" +version = "0.99.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/net/Cargo.toml b/ext/net/Cargo.toml index 8f952dc8f7..02e7a16818 100644 --- a/ext/net/Cargo.toml +++ b/ext/net/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_net" -version = "0.159.0" +version = "0.160.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index 783ad45c4b..d539d48516 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_node" -version = "0.104.0" +version = "0.105.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/node_resolver/Cargo.toml b/ext/node_resolver/Cargo.toml index 704a5af843..1042045692 100644 --- a/ext/node_resolver/Cargo.toml +++ b/ext/node_resolver/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "node_resolver" -version = "0.6.0" +version = "0.7.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/tls/Cargo.toml b/ext/tls/Cargo.toml index 7fbfeffd12..08a9802df5 100644 --- a/ext/tls/Cargo.toml +++ b/ext/tls/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_tls" -version = "0.154.0" +version = "0.155.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/url/Cargo.toml b/ext/url/Cargo.toml index 991a28ce42..7f86e14456 100644 --- a/ext/url/Cargo.toml +++ b/ext/url/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_url" -version = "0.167.0" +version = "0.168.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/web/Cargo.toml b/ext/web/Cargo.toml index 717a4a7587..7fd12632d7 100644 --- a/ext/web/Cargo.toml +++ b/ext/web/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_web" -version = "0.198.0" +version = "0.199.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/webgpu/Cargo.toml b/ext/webgpu/Cargo.toml index 22c9d00334..b5cff59855 100644 --- a/ext/webgpu/Cargo.toml +++ b/ext/webgpu/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webgpu" -version = "0.134.0" +version = "0.135.0" authors = ["the Deno authors"] edition.workspace = true license = "MIT" diff --git a/ext/webidl/Cargo.toml b/ext/webidl/Cargo.toml index 1c259690ad..2f87dc2197 100644 --- a/ext/webidl/Cargo.toml +++ b/ext/webidl/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webidl" -version = "0.167.0" +version = "0.168.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/websocket/Cargo.toml b/ext/websocket/Cargo.toml index 932451b127..3fc9025466 100644 --- a/ext/websocket/Cargo.toml +++ b/ext/websocket/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_websocket" -version = "0.172.0" +version = "0.173.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/ext/webstorage/Cargo.toml b/ext/webstorage/Cargo.toml index 6cf152cc84..fbcd17edf9 100644 --- a/ext/webstorage/Cargo.toml +++ b/ext/webstorage/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webstorage" -version = "0.162.0" +version = "0.163.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 1299257e80..6d24781cfa 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_runtime" -version = "0.176.0" +version = "0.177.0" authors.workspace = true edition.workspace = true license.workspace = true diff --git a/runtime/permissions/Cargo.toml b/runtime/permissions/Cargo.toml index 5ed58287c2..b435748965 100644 --- a/runtime/permissions/Cargo.toml +++ b/runtime/permissions/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_permissions" -version = "0.27.0" +version = "0.28.0" authors.workspace = true edition.workspace = true license.workspace = true From c32d692a8f37c50fd700bb320571f76a107a44c2 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Wed, 4 Sep 2024 13:06:16 -0700 Subject: [PATCH 34/45] feat(install): deno install with entrypoint (#25411) ``` deno install --entrypoint one.ts two.ts ``` effectively equivalent to `deno cache` --- cli/args/flags.rs | 52 +++++++++++++-- cli/main.rs | 10 +-- cli/tools/installer.rs | 65 +++++++++++++------ .../install/install_entrypoint/__test__.jsonc | 21 ++++++ .../install/install_entrypoint/install.out | 7 ++ .../install/install_entrypoint/lifecycle.out | 22 +++++++ .../install/install_entrypoint/lifecycle.ts | 3 + .../specs/install/install_entrypoint/main.ts | 4 ++ .../install/install_entrypoint/second.ts | 1 + 9 files changed, 149 insertions(+), 36 deletions(-) create mode 100644 tests/specs/install/install_entrypoint/__test__.jsonc create mode 100644 tests/specs/install/install_entrypoint/install.out create mode 100644 tests/specs/install/install_entrypoint/lifecycle.out create mode 100644 tests/specs/install/install_entrypoint/lifecycle.ts create mode 100644 tests/specs/install/install_entrypoint/main.ts create mode 100644 tests/specs/install/install_entrypoint/second.ts diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 5ea8b8ecf3..9397f21807 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -240,10 +240,17 @@ pub struct InstallFlagsGlobal { #[derive(Clone, Debug, Eq, PartialEq)] pub enum InstallKind { - Local(Option), + Local(InstallFlagsLocal), Global(InstallFlagsGlobal), } +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum InstallFlagsLocal { + Add(AddFlags), + TopLevel, + Entrypoints(Vec), +} + #[derive(Clone, Debug, Eq, PartialEq)] pub struct InstallFlags { pub global: bool, @@ -2365,10 +2372,12 @@ fn install_subcommand() -> Command { Add dependencies to the local project's configuration (deno.json / package.json) and installs them in the package cache. If no dependency is specified, installs all dependencies listed in the config file. +If the --entrypoint flag is passed, installs the dependencies of the specified entrypoint(s). deno install deno install @std/bytes deno install npm:chalk + deno install --entrypoint entry1.ts entry2.ts Global installation @@ -2405,6 +2414,7 @@ These must be added to the path manually if required."), UnstableArgsConfig::Res .arg( Arg::new("cmd") .required_if_eq("global", "true") + .required_if_eq("entrypoint", "true") .num_args(1..) .value_hint(ValueHint::FilePath), ) @@ -2412,17 +2422,20 @@ These must be added to the path manually if required."), UnstableArgsConfig::Res Arg::new("name") .long("name") .short('n') + .requires("global") .help("Executable file name"), ) .arg( Arg::new("root") .long("root") + .requires("global") .help("Installation root") .value_hint(ValueHint::DirPath), ) .arg( Arg::new("force") .long("force") + .requires("global") .short('f') .help("Forcefully overwrite existing installation") .action(ArgAction::SetTrue), @@ -2434,6 +2447,14 @@ These must be added to the path manually if required."), UnstableArgsConfig::Res .help("Install a package or script as a globally available executable") .action(ArgAction::SetTrue), ) + .arg( + Arg::new("entrypoint") + .long("entrypoint") + .short('e') + .conflicts_with("global") + .action(ArgAction::SetTrue) + .help("Install dependents of the specified entrypoint(s)"), + ) .arg(env_file_arg()) }) } @@ -4419,15 +4440,32 @@ fn install_parse(flags: &mut Flags, matches: &mut ArgMatches) { force, }), }); - } else { - let local_flags = matches - .remove_many("cmd") - .map(|packages| add_parse_inner(matches, Some(packages))); - allow_scripts_arg_parse(flags, matches); + return; + } + + // allow scripts only applies to local install + allow_scripts_arg_parse(flags, matches); + if matches.get_flag("entrypoint") { + let entrypoints = matches.remove_many::("cmd").unwrap_or_default(); flags.subcommand = DenoSubcommand::Install(InstallFlags { global, - kind: InstallKind::Local(local_flags), + kind: InstallKind::Local(InstallFlagsLocal::Entrypoints( + entrypoints.collect(), + )), + }); + } else if let Some(add_files) = matches + .remove_many("cmd") + .map(|packages| add_parse_inner(matches, Some(packages))) + { + flags.subcommand = DenoSubcommand::Install(InstallFlags { + global, + kind: InstallKind::Local(InstallFlagsLocal::Add(add_files)), }) + } else { + flags.subcommand = DenoSubcommand::Install(InstallFlags { + global, + kind: InstallKind::Local(InstallFlagsLocal::TopLevel), + }); } } diff --git a/cli/main.rs b/cli/main.rs index c963cb21ca..c0899ee100 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -32,7 +32,6 @@ mod worker; use crate::args::flags_from_vec; use crate::args::DenoSubcommand; use crate::args::Flags; -use crate::graph_container::ModuleGraphContainer; use crate::util::display; use crate::util::v8::get_v8_flags_from_env; use crate::util::v8::init_v8_flags; @@ -118,14 +117,7 @@ async fn run_subcommand(flags: Arc) -> Result { tools::run::eval_command(flags, eval_flags).await }), DenoSubcommand::Cache(cache_flags) => spawn_subcommand(async move { - let factory = CliFactory::from_flags(flags); - let emitter = factory.emitter()?; - let main_graph_container = - factory.main_module_graph_container().await?; - main_graph_container - .load_and_type_check_files(&cache_flags.files) - .await?; - emitter.cache_module_emits(&main_graph_container.graph()).await + tools::installer::install_from_entrypoints(flags, &cache_flags.files).await }), DenoSubcommand::Check(check_flags) => spawn_subcommand(async move { let factory = CliFactory::from_flags(flags); diff --git a/cli/tools/installer.rs b/cli/tools/installer.rs index 6965d5c6d3..987f3c0694 100644 --- a/cli/tools/installer.rs +++ b/cli/tools/installer.rs @@ -7,11 +7,13 @@ use crate::args::ConfigFlag; use crate::args::Flags; use crate::args::InstallFlags; use crate::args::InstallFlagsGlobal; +use crate::args::InstallFlagsLocal; use crate::args::InstallKind; use crate::args::TypeCheckMode; use crate::args::UninstallFlags; use crate::args::UninstallKind; use crate::factory::CliFactory; +use crate::graph_container::ModuleGraphContainer; use crate::http_util::HttpClientProvider; use crate::util::fs::canonicalize_path_maybe_not_exists; @@ -262,27 +264,48 @@ pub fn uninstall(uninstall_flags: UninstallFlags) -> Result<(), AnyError> { Ok(()) } +pub(crate) async fn install_from_entrypoints( + flags: Arc, + entrypoints: &[String], +) -> Result<(), AnyError> { + let factory = CliFactory::from_flags(flags.clone()); + let emitter = factory.emitter()?; + let main_graph_container = factory.main_module_graph_container().await?; + main_graph_container + .load_and_type_check_files(entrypoints) + .await?; + emitter + .cache_module_emits(&main_graph_container.graph()) + .await +} + async fn install_local( flags: Arc, - maybe_add_flags: Option, + install_flags: InstallFlagsLocal, ) -> Result<(), AnyError> { - if let Some(add_flags) = maybe_add_flags { - return super::registry::add( - flags, - add_flags, - super::registry::AddCommandName::Install, - ) - .await; + match install_flags { + InstallFlagsLocal::Add(add_flags) => { + super::registry::add( + flags, + add_flags, + super::registry::AddCommandName::Install, + ) + .await + } + InstallFlagsLocal::Entrypoints(entrypoints) => { + install_from_entrypoints(flags, &entrypoints).await + } + InstallFlagsLocal::TopLevel => { + let factory = CliFactory::from_flags(flags); + crate::tools::registry::cache_top_level_deps(&factory, None).await?; + + if let Some(lockfile) = factory.cli_options()?.maybe_lockfile() { + lockfile.write_if_changed()?; + } + + Ok(()) + } } - - let factory = CliFactory::from_flags(flags); - crate::tools::registry::cache_top_level_deps(&factory, None).await?; - - if let Some(lockfile) = factory.cli_options()?.maybe_lockfile() { - lockfile.write_if_changed()?; - } - - Ok(()) } fn check_if_installs_a_single_package_globally( @@ -315,9 +338,11 @@ pub async fn install_command( install_global(flags, global_flags).await } - InstallKind::Local(maybe_add_flags) => { - check_if_installs_a_single_package_globally(maybe_add_flags.as_ref())?; - install_local(flags, maybe_add_flags).await + InstallKind::Local(local_flags) => { + if let InstallFlagsLocal::Add(add_flags) = &local_flags { + check_if_installs_a_single_package_globally(Some(add_flags))?; + } + install_local(flags, local_flags).await } } } diff --git a/tests/specs/install/install_entrypoint/__test__.jsonc b/tests/specs/install/install_entrypoint/__test__.jsonc new file mode 100644 index 0000000000..1afe195e28 --- /dev/null +++ b/tests/specs/install/install_entrypoint/__test__.jsonc @@ -0,0 +1,21 @@ +{ + "tempDir": true, + "tests": { + "basic": { + "steps": [ + { + "args": "install --entrypoint main.ts second.ts", + "output": "install.out" + } + ] + }, + "allow_scripts": { + "steps": [ + { + "args": "install --allow-scripts --node-modules-dir=auto --entrypoint lifecycle.ts", + "output": "lifecycle.out" + } + ] + } + } +} diff --git a/tests/specs/install/install_entrypoint/install.out b/tests/specs/install/install_entrypoint/install.out new file mode 100644 index 0000000000..d702cf45ac --- /dev/null +++ b/tests/specs/install/install_entrypoint/install.out @@ -0,0 +1,7 @@ +[UNORDERED_START] +Download http://127.0.0.1:4250/@denotest/add/meta.json +Download http://localhost:4260/@denotest/esm-basic +Download http://127.0.0.1:4250/@denotest/add/1.0.0_meta.json +Download http://127.0.0.1:4250/@denotest/add/1.0.0/mod.ts +Download http://localhost:4260/@denotest/esm-basic/1.0.0.tgz +[UNORDERED_END] diff --git a/tests/specs/install/install_entrypoint/lifecycle.out b/tests/specs/install/install_entrypoint/lifecycle.out new file mode 100644 index 0000000000..8eae8ee120 --- /dev/null +++ b/tests/specs/install/install_entrypoint/lifecycle.out @@ -0,0 +1,22 @@ +[UNORDERED_START] +Download http://localhost:4260/@denotest/node-lifecycle-scripts +Download http://localhost:4260/@denotest/bin +Download http://localhost:4260/@denotest/node-lifecycle-scripts/1.0.0.tgz +Download http://localhost:4260/@denotest/bin/1.0.0.tgz +Initialize @denotest/node-lifecycle-scripts@1.0.0 +Initialize @denotest/bin@1.0.0 +[UNORDERED_END] +preinstall +deno preinstall.js +node preinstall.js +install +hello from install script +postinstall[WILDCARD] + _____________ +< postinstall > + ------------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || diff --git a/tests/specs/install/install_entrypoint/lifecycle.ts b/tests/specs/install/install_entrypoint/lifecycle.ts new file mode 100644 index 0000000000..e9fb2cf9cc --- /dev/null +++ b/tests/specs/install/install_entrypoint/lifecycle.ts @@ -0,0 +1,3 @@ +import { value } from "npm:@denotest/node-lifecycle-scripts"; + +console.log(`value is ${value}`); diff --git a/tests/specs/install/install_entrypoint/main.ts b/tests/specs/install/install_entrypoint/main.ts new file mode 100644 index 0000000000..aceb2b3c88 --- /dev/null +++ b/tests/specs/install/install_entrypoint/main.ts @@ -0,0 +1,4 @@ +import { getValue, setValue } from "npm:@denotest/esm-basic"; + +setValue(5); +console.log(getValue()); diff --git a/tests/specs/install/install_entrypoint/second.ts b/tests/specs/install/install_entrypoint/second.ts new file mode 100644 index 0000000000..2c781286f8 --- /dev/null +++ b/tests/specs/install/install_entrypoint/second.ts @@ -0,0 +1 @@ +import { add } from "jsr:@denotest/add@1.0.0"; From 195b17ae1298f80209e3c2c5ef4d133e6975ff58 Mon Sep 17 00:00:00 2001 From: Asher Gomez Date: Thu, 5 Sep 2024 08:45:55 +1000 Subject: [PATCH 35/45] BREAKING(types): soft-remove `Deno.run()` (#25403) Towards #22079 --- cli/tsc/dts/lib.deno.ns.d.ts | 231 +----------------- cli/tsc/dts/lib.deno.unstable.d.ts | 74 ------ runtime/js/40_process.js | 7 +- tests/testdata/coverage/complex_test.ts | 29 +-- .../run/warn_on_deprecated_api/main.js | 2 + .../run/warn_on_deprecated_api/main.out | 1 - .../warn_on_deprecated_api/main.verbose.out | 5 - tests/unit/http_test.ts | 25 +- tests/unit/process_test.ts | 52 ++-- 9 files changed, 58 insertions(+), 368 deletions(-) diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index 495ece36db..e47a2d942d 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -3925,175 +3925,6 @@ declare namespace Deno { options?: { recursive: boolean }, ): FsWatcher; - /** - * Options which can be used with {@linkcode Deno.run}. - * - * @deprecated This will be removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @category Subprocess */ - export interface RunOptions { - /** Arguments to pass. - * - * _Note_: the first element needs to be a path to the executable that is - * being run. */ - cmd: readonly string[] | [string | URL, ...string[]]; - /** The current working directory that should be used when running the - * sub-process. */ - cwd?: string; - /** Any environment variables to be set when running the sub-process. */ - env?: Record; - /** By default subprocess inherits `stdout` of parent process. To change - * this this option can be set to a resource ID (_rid_) of an open file, - * `"inherit"`, `"piped"`, or `"null"`: - * - * - _number_: the resource ID of an open file/resource. This allows you to - * write to a file. - * - `"inherit"`: The default if unspecified. The subprocess inherits from the - * parent. - * - `"piped"`: A new pipe should be arranged to connect the parent and child - * sub-process. - * - `"null"`: This stream will be ignored. This is the equivalent of attaching - * the stream to `/dev/null`. - */ - stdout?: "inherit" | "piped" | "null" | number; - /** By default subprocess inherits `stderr` of parent process. To change - * this this option can be set to a resource ID (_rid_) of an open file, - * `"inherit"`, `"piped"`, or `"null"`: - * - * - _number_: the resource ID of an open file/resource. This allows you to - * write to a file. - * - `"inherit"`: The default if unspecified. The subprocess inherits from the - * parent. - * - `"piped"`: A new pipe should be arranged to connect the parent and child - * sub-process. - * - `"null"`: This stream will be ignored. This is the equivalent of attaching - * the stream to `/dev/null`. - */ - stderr?: "inherit" | "piped" | "null" | number; - /** By default subprocess inherits `stdin` of parent process. To change - * this this option can be set to a resource ID (_rid_) of an open file, - * `"inherit"`, `"piped"`, or `"null"`: - * - * - _number_: the resource ID of an open file/resource. This allows you to - * read from a file. - * - `"inherit"`: The default if unspecified. The subprocess inherits from the - * parent. - * - `"piped"`: A new pipe should be arranged to connect the parent and child - * sub-process. - * - `"null"`: This stream will be ignored. This is the equivalent of attaching - * the stream to `/dev/null`. - */ - stdin?: "inherit" | "piped" | "null" | number; - } - - /** - * The status resolved from the `.status()` method of a - * {@linkcode Deno.Process} instance. - * - * If `success` is `true`, then `code` will be `0`, but if `success` is - * `false`, the sub-process exit code will be set in `code`. - * - * @deprecated This will be removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @category Subprocess */ - export type ProcessStatus = - | { - success: true; - code: 0; - signal?: undefined; - } - | { - success: false; - code: number; - signal?: number; - }; - - /** - * Represents an instance of a sub process that is returned from - * {@linkcode Deno.run} which can be used to manage the sub-process. - * - * @deprecated This will be removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @category Subprocess */ - export class Process { - /** The resource ID of the sub-process. */ - readonly rid: number; - /** The operating system's process ID for the sub-process. */ - readonly pid: number; - /** A reference to the sub-processes `stdin`, which allows interacting with - * the sub-process at a low level. */ - readonly stdin: T["stdin"] extends "piped" ? Writer & Closer & { - writable: WritableStream; - } - : (Writer & Closer & { writable: WritableStream }) | null; - /** A reference to the sub-processes `stdout`, which allows interacting with - * the sub-process at a low level. */ - readonly stdout: T["stdout"] extends "piped" ? Reader & Closer & { - readable: ReadableStream; - } - : (Reader & Closer & { readable: ReadableStream }) | null; - /** A reference to the sub-processes `stderr`, which allows interacting with - * the sub-process at a low level. */ - readonly stderr: T["stderr"] extends "piped" ? Reader & Closer & { - readable: ReadableStream; - } - : (Reader & Closer & { readable: ReadableStream }) | null; - /** Wait for the process to exit and return its exit status. - * - * Calling this function multiple times will return the same status. - * - * The `stdin` reference to the process will be closed before waiting to - * avoid a deadlock. - * - * If `stdout` and/or `stderr` were set to `"piped"`, they must be closed - * manually before the process can exit. - * - * To run process to completion and collect output from both `stdout` and - * `stderr` use: - * - * ```ts - * const p = Deno.run({ cmd: [ "echo", "hello world" ], stderr: 'piped', stdout: 'piped' }); - * const [status, stdout, stderr] = await Promise.all([ - * p.status(), - * p.output(), - * p.stderrOutput() - * ]); - * p.close(); - * ``` - */ - status(): Promise; - /** Buffer the stdout until EOF and return it as `Uint8Array`. - * - * You must set `stdout` to `"piped"` when creating the process. - * - * This calls `close()` on stdout after its done. */ - output(): Promise; - /** Buffer the stderr until EOF and return it as `Uint8Array`. - * - * You must set `stderr` to `"piped"` when creating the process. - * - * This calls `close()` on stderr after its done. */ - stderrOutput(): Promise; - /** Clean up resources associated with the sub-process instance. */ - close(): void; - /** Send a signal to process. - * Default signal is `"SIGTERM"`. - * - * ```ts - * const p = Deno.run({ cmd: [ "sleep", "20" ]}); - * p.kill("SIGTERM"); - * p.close(); - * ``` - */ - kill(signo?: Signal): void; - } - /** Operating signals which can be listened for or sent to sub-processes. What * signals and what their standard behaviors are OS dependent. * @@ -4175,61 +4006,6 @@ declare namespace Deno { handler: () => void, ): void; - /** - * Spawns new subprocess. RunOptions must contain at a minimum the `opt.cmd`, - * an array of program arguments, the first of which is the binary. - * - * ```ts - * const p = Deno.run({ - * cmd: ["curl", "https://example.com"], - * }); - * const status = await p.status(); - * ``` - * - * Subprocess uses same working directory as parent process unless `opt.cwd` - * is specified. - * - * Environmental variables from parent process can be cleared using `opt.clearEnv`. - * Doesn't guarantee that only `opt.env` variables are present, - * as the OS may set environmental variables for processes. - * - * Environmental variables for subprocess can be specified using `opt.env` - * mapping. - * - * `opt.uid` sets the child process’s user ID. This translates to a setuid call - * in the child process. Failure in the setuid call will cause the spawn to fail. - * - * `opt.gid` is similar to `opt.uid`, but sets the group ID of the child process. - * This has the same semantics as the uid field. - * - * By default subprocess inherits stdio of parent process. To change - * this this, `opt.stdin`, `opt.stdout`, and `opt.stderr` can be set - * independently to a resource ID (_rid_) of an open file, `"inherit"`, - * `"piped"`, or `"null"`: - * - * - _number_: the resource ID of an open file/resource. This allows you to - * read or write to a file. - * - `"inherit"`: The default if unspecified. The subprocess inherits from the - * parent. - * - `"piped"`: A new pipe should be arranged to connect the parent and child - * sub-process. - * - `"null"`: This stream will be ignored. This is the equivalent of attaching - * the stream to `/dev/null`. - * - * Details of the spawned process are returned as an instance of - * {@linkcode Deno.Process}. - * - * Requires `allow-run` permission. - * - * @deprecated This will be soft-removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @tags allow-run - * @category Subprocess - */ - export function run(opt: T): Process; - /** Create a child process. * * If any stdio options are not set to `"piped"`, accessing the corresponding @@ -5308,11 +5084,10 @@ declare namespace Deno { * Windows. * * ```ts - * const p = Deno.run({ - * cmd: ["sleep", "10000"] - * }); + * const command = new Deno.Command("sleep", { args: ["10000"] }); + * const child = command.spawn(); * - * Deno.kill(p.pid, "SIGINT"); + * Deno.kill(child.pid, "SIGINT"); * ``` * * Requires `allow-run` permission. diff --git a/cli/tsc/dts/lib.deno.unstable.d.ts b/cli/tsc/dts/lib.deno.unstable.d.ts index 413abe42d3..9dee14f7fe 100644 --- a/cli/tsc/dts/lib.deno.unstable.d.ts +++ b/cli/tsc/dts/lib.deno.unstable.d.ts @@ -840,80 +840,6 @@ declare namespace Deno { present(): void; } - /** **UNSTABLE**: New API, yet to be vetted. - * - * These are unstable options which can be used with {@linkcode Deno.run}. - * - * @category Subprocess - * @experimental - */ - export interface UnstableRunOptions extends RunOptions { - /** If `true`, clears the environment variables before executing the - * sub-process. - * - * @default {false} */ - clearEnv?: boolean; - /** For POSIX systems, sets the group ID for the sub process. */ - gid?: number; - /** For POSIX systems, sets the user ID for the sub process. */ - uid?: number; - } - - /** **UNSTABLE**: New API, yet to be vetted. - * - * Spawns new subprocess. RunOptions must contain at a minimum the `opt.cmd`, - * an array of program arguments, the first of which is the binary. - * - * ```ts - * const p = Deno.run({ - * cmd: ["curl", "https://example.com"], - * }); - * const status = await p.status(); - * ``` - * - * Subprocess uses same working directory as parent process unless `opt.cwd` - * is specified. - * - * Environmental variables from parent process can be cleared using `opt.clearEnv`. - * Doesn't guarantee that only `opt.env` variables are present, - * as the OS may set environmental variables for processes. - * - * Environmental variables for subprocess can be specified using `opt.env` - * mapping. - * - * `opt.uid` sets the child process’s user ID. This translates to a setuid call - * in the child process. Failure in the setuid call will cause the spawn to fail. - * - * `opt.gid` is similar to `opt.uid`, but sets the group ID of the child process. - * This has the same semantics as the uid field. - * - * By default subprocess inherits stdio of parent process. To change - * this this, `opt.stdin`, `opt.stdout`, and `opt.stderr` can be set - * independently to a resource ID (_rid_) of an open file, `"inherit"`, - * `"piped"`, or `"null"`: - * - * - _number_: the resource ID of an open file/resource. This allows you to - * read or write to a file. - * - `"inherit"`: The default if unspecified. The subprocess inherits from the - * parent. - * - `"piped"`: A new pipe should be arranged to connect the parent and child - * sub-process. - * - `"null"`: This stream will be ignored. This is the equivalent of attaching - * the stream to `/dev/null`. - * - * Details of the spawned process are returned as an instance of - * {@linkcode Deno.Process}. - * - * Requires `allow-run` permission. - * - * @tags allow-run - * @category Subprocess - * @experimental - */ - export function run( - opt: T, - ): Process; - /** **UNSTABLE**: New API, yet to be vetted. * * A custom `HttpClient` for use with {@linkcode fetch} function. This is diff --git a/runtime/js/40_process.js b/runtime/js/40_process.js index b2269ffd68..954d8d00b3 100644 --- a/runtime/js/40_process.js +++ b/runtime/js/40_process.js @@ -130,6 +130,8 @@ class Process { } } +// Note: This function was soft-removed in Deno 2. Its types have been removed, +// but its implementation has been kept to avoid breaking changes. function run({ cmd, cwd = undefined, @@ -144,11 +146,6 @@ function run({ ...new SafeArrayIterator(ArrayPrototypeSlice(cmd, 1)), ]; } - internals.warnOnDeprecatedApi( - "Deno.run()", - (new Error()).stack, - `Use "Deno.Command()" API instead.`, - ); const res = opRun({ cmd: ArrayPrototypeMap(cmd, String), cwd, diff --git a/tests/testdata/coverage/complex_test.ts b/tests/testdata/coverage/complex_test.ts index d6e9c26910..6a711a91a0 100644 --- a/tests/testdata/coverage/complex_test.ts +++ b/tests/testdata/coverage/complex_test.ts @@ -7,32 +7,29 @@ Deno.test("complex", function () { Deno.test("sub process with stdin", async () => { // ensure launching deno run with stdin doesn't affect coverage const code = "console.log('5')"; - // deno-lint-ignore no-deprecated-deno-api - const p = await Deno.run({ - cmd: [Deno.execPath(), "run", "-"], + const command = new Deno.Command(Deno.execPath(), { + args: ["run", "-"], stdin: "piped", stdout: "piped", }); - const encoder = new TextEncoder(); - await p.stdin.write(encoder.encode(code)); - await p.stdin.close(); - const output = new TextDecoder().decode(await p.output()); - p.close(); + await using child = command.spawn(); + await ReadableStream.from([code]) + .pipeThrough(new TextEncoderStream()) + .pipeTo(child.stdin); + const { stdout } = await child.output(); + const output = new TextDecoder().decode(stdout); if (output.trim() !== "5") { throw new Error("Failed"); } }); -Deno.test("sub process with deno eval", async () => { +Deno.test("sub process with deno eval", () => { // ensure launching deno eval doesn't affect coverage const code = "console.log('5')"; - // deno-lint-ignore no-deprecated-deno-api - const p = await Deno.run({ - cmd: [Deno.execPath(), "eval", code], - stdout: "piped", - }); - const output = new TextDecoder().decode(await p.output()); - p.close(); + const { stdout } = new Deno.Command(Deno.execPath(), { + args: ["eval", code], + }).outputSync(); + const output = new TextDecoder().decode(stdout); if (output.trim() !== "5") { throw new Error("Failed"); } diff --git a/tests/testdata/run/warn_on_deprecated_api/main.js b/tests/testdata/run/warn_on_deprecated_api/main.js index a464be60a3..8811df78de 100644 --- a/tests/testdata/run/warn_on_deprecated_api/main.js +++ b/tests/testdata/run/warn_on_deprecated_api/main.js @@ -1,5 +1,6 @@ import { runEcho as runEcho2 } from "http://localhost:4545/run/warn_on_deprecated_api/mod.ts"; +// @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [ Deno.execPath(), @@ -11,6 +12,7 @@ await p.status(); p.close(); async function runEcho() { + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [ Deno.execPath(), diff --git a/tests/testdata/run/warn_on_deprecated_api/main.out b/tests/testdata/run/warn_on_deprecated_api/main.out index ff44c885f1..ef85a6f99b 100644 --- a/tests/testdata/run/warn_on_deprecated_api/main.out +++ b/tests/testdata/run/warn_on_deprecated_api/main.out @@ -1,5 +1,4 @@ Download http://localhost:4545/run/warn_on_deprecated_api/mod.ts -warning: Use of deprecated "Deno.run()" API. This API will be removed in Deno 2. Run again with DENO_VERBOSE_WARNINGS=1 to get more details. hello world hello world hello world diff --git a/tests/testdata/run/warn_on_deprecated_api/main.verbose.out b/tests/testdata/run/warn_on_deprecated_api/main.verbose.out index 184051de12..e17562eef3 100644 --- a/tests/testdata/run/warn_on_deprecated_api/main.verbose.out +++ b/tests/testdata/run/warn_on_deprecated_api/main.verbose.out @@ -1,5 +1,4 @@ Download http://localhost:4545/run/warn_on_deprecated_api/mod.ts -warning: Use of deprecated "Deno.run()" API. This API will be removed in Deno 2. See the Deno 1 to 2 Migration Guide for more information at https://docs.deno.com/runtime/manual/advanced/migrate_deprecations @@ -9,7 +8,6 @@ Stack trace: hint: Use "Deno.Command()" API instead. hello world -warning: Use of deprecated "Deno.run()" API. This API will be removed in Deno 2. See the Deno 1 to 2 Migration Guide for more information at https://docs.deno.com/runtime/manual/advanced/migrate_deprecations @@ -20,7 +18,6 @@ Stack trace: hint: Use "Deno.Command()" API instead. hello world -warning: Use of deprecated "Deno.run()" API. This API will be removed in Deno 2. See the Deno 1 to 2 Migration Guide for more information at https://docs.deno.com/runtime/manual/advanced/migrate_deprecations @@ -31,7 +28,6 @@ Stack trace: hint: Use "Deno.Command()" API instead. hello world -warning: Use of deprecated "Deno.run()" API. This API will be removed in Deno 2. See the Deno 1 to 2 Migration Guide for more information at https://docs.deno.com/runtime/manual/advanced/migrate_deprecations @@ -51,7 +47,6 @@ hello world hello world hello world hello world -warning: Use of deprecated "Deno.run()" API. This API will be removed in Deno 2. See the Deno 1 to 2 Migration Guide for more information at https://docs.deno.com/runtime/manual/advanced/migrate_deprecations diff --git a/tests/unit/http_test.ts b/tests/unit/http_test.ts index 6d3543e444..778cc98fde 100644 --- a/tests/unit/http_test.ts +++ b/tests/unit/http_test.ts @@ -2087,7 +2087,6 @@ Deno.test({ async function client() { const url = `http://${hostname}:${port}/`; const cmd = [ - "curl", "-i", "--request", "GET", @@ -2097,16 +2096,17 @@ Deno.test({ "--header", "Accept-Encoding: deflate, gzip", ]; - const proc = Deno.run({ cmd, stdout: "piped", stderr: "null" }); - const status = await proc.status(); - assert(status.success); - const output = decoder.decode(await proc.output()); + const { success, stdout } = await new Deno.Command("curl", { + args: cmd, + stderr: "null", + }).output(); + assert(success); + const output = decoder.decode(stdout); assert(output.includes("vary: Accept-Encoding\r\n")); assert(output.includes("content-encoding: gzip\r\n")); // Ensure the content-length header is updated. assert(!output.includes(`content-length: ${contentLength}\r\n`)); assert(output.includes("content-length: ")); - proc.close(); } await Promise.all([server(), client()]); @@ -2149,7 +2149,6 @@ Deno.test({ async function client() { const url = `http://${hostname}:${port}/`; const cmd = [ - "curl", "-i", "--request", "GET", @@ -2159,13 +2158,15 @@ Deno.test({ "--header", "Accept-Encoding: deflate, gzip", ]; - const proc = Deno.run({ cmd, stdout: "piped", stderr: "null" }); - const status = await proc.status(); - assert(status.success); - const output = decoder.decode(await proc.output()); + const { success, stdout } = await new Deno.Command("curl", { + args: cmd, + stderr: "null", + stdout: "piped", + }).output(); + assert(success); + const output = decoder.decode(stdout); assert(output.includes("vary: Accept-Encoding\r\n")); assert(output.includes("content-encoding: arbitrary\r\n")); - proc.close(); } await Promise.all([server(), client()]); diff --git a/tests/unit/process_test.ts b/tests/unit/process_test.ts index 383f17f384..0d14c9ce7c 100644 --- a/tests/unit/process_test.ts +++ b/tests/unit/process_test.ts @@ -1,3 +1,4 @@ +// deno-lint-ignore-file no-deprecated-deno-api // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. import { assert, @@ -12,7 +13,7 @@ Deno.test( { permissions: { read: true, run: false } }, function runPermissions() { assertThrows(() => { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. Deno.run({ cmd: [Deno.execPath(), "eval", "console.log('hello world')"], }); @@ -23,7 +24,7 @@ Deno.test( Deno.test( { permissions: { run: true, read: true } }, async function runSuccess() { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ // freeze the array to ensure it's not modified cmd: Object.freeze([ @@ -46,7 +47,7 @@ Deno.test( Deno.test( { permissions: { run: true, read: true } }, async function runUrl() { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [ new URL(`file:///${Deno.execPath()}`), @@ -70,7 +71,7 @@ Deno.test( async function runStdinRid0(): Promise< void > { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [Deno.execPath(), "eval", "console.log('hello world')"], stdin: 0, @@ -90,26 +91,23 @@ Deno.test( { permissions: { run: true, read: true } }, function runInvalidStdio() { assertThrows(() => - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. Deno.run({ cmd: [Deno.execPath(), "eval", "console.log('hello world')"], - // @ts-expect-error because Deno.run should throw on invalid stdin. stdin: "a", }) ); assertThrows(() => - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. Deno.run({ cmd: [Deno.execPath(), "eval", "console.log('hello world')"], - // @ts-expect-error because Deno.run should throw on invalid stdout. stdout: "b", }) ); assertThrows(() => - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. Deno.run({ cmd: [Deno.execPath(), "eval", "console.log('hello world')"], - // @ts-expect-error because Deno.run should throw on invalid stderr. stderr: "c", }) ); @@ -119,7 +117,7 @@ Deno.test( Deno.test( { permissions: { run: true, read: true } }, async function runCommandFailedWithCode() { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [Deno.execPath(), "eval", "Deno.exit(41 + 1)"], }); @@ -136,7 +134,7 @@ Deno.test( permissions: { run: true, read: true }, }, async function runCommandFailedWithSignal() { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [ Deno.execPath(), @@ -160,7 +158,7 @@ Deno.test( Deno.test({ permissions: { run: true } }, function runNotFound() { let error; try { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. Deno.run({ cmd: ["this file hopefully doesn't exist"] }); } catch (e) { error = e; @@ -192,7 +190,7 @@ tryExit(); `; Deno.writeFileSync(`${cwd}/${programFile}`, enc.encode(program)); - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cwd, cmd: [Deno.execPath(), "run", "--allow-read", programFile], @@ -216,7 +214,7 @@ Deno.test( async function runStdinPiped(): Promise< void > { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [ Deno.execPath(), @@ -254,7 +252,7 @@ Deno.test( async function runStdoutPiped(): Promise< void > { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [ Deno.execPath(), @@ -291,7 +289,7 @@ Deno.test( async function runStderrPiped(): Promise< void > { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [ Deno.execPath(), @@ -326,7 +324,7 @@ Deno.test( Deno.test( { permissions: { run: true, read: true } }, async function runOutput() { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [ Deno.execPath(), @@ -347,7 +345,7 @@ Deno.test( async function runStderrOutput(): Promise< void > { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [ Deno.execPath(), @@ -377,7 +375,7 @@ Deno.test( write: true, }); - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [ Deno.execPath(), @@ -414,7 +412,7 @@ Deno.test( await Deno.writeTextFile(fileName, "hello"); using file = await Deno.open(fileName); - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [ Deno.execPath(), @@ -439,7 +437,7 @@ Deno.test( Deno.test( { permissions: { run: true, read: true } }, async function runEnv() { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [ Deno.execPath(), @@ -462,7 +460,7 @@ Deno.test( Deno.test( { permissions: { run: true, read: true } }, async function runClose() { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [ Deno.execPath(), @@ -486,7 +484,7 @@ Deno.test( Deno.test( { permissions: { run: true, read: true } }, async function runKillAfterStatus() { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [Deno.execPath(), "eval", 'console.log("hello")'], }); @@ -543,7 +541,7 @@ Deno.test( Deno.test( { permissions: { run: true, read: true } }, async function killSuccess() { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [Deno.execPath(), "eval", "setTimeout(() => {}, 10000)"], }); @@ -567,7 +565,7 @@ Deno.test( ); Deno.test({ permissions: { run: true, read: true } }, function killFailed() { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [Deno.execPath(), "eval", "setTimeout(() => {}, 10000)"], }); @@ -588,7 +586,7 @@ Deno.test( ignore: Deno.build.os === "windows", }, async function non_existent_cwd(): Promise { - // deno-lint-ignore no-deprecated-deno-api + // @ts-ignore `Deno.run()` was soft-removed in Deno 2. const p = Deno.run({ cmd: [ Deno.execPath(), From dd208a6df02e99dbd7e1cb7b197fde8ccfeb0f88 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 5 Sep 2024 00:57:49 +0200 Subject: [PATCH 36/45] fix(regression): do not expose resolved path in Deno.Command permission denied error (#25434) Regression from https://github.com/denoland/deno/pull/25370 --- runtime/ops/process.rs | 13 ++- runtime/permissions/lib.rs | 93 ++++++++++++++++--- .../specs/compile/permissions_denied/main.out | 2 +- .../permission/path_not_permitted/main.out | 4 +- .../permission/path_not_permitted/main.ts | 2 +- .../permission/path_not_permitted/sub.ts | 4 +- tests/testdata/run/089_run_allow_list.ts.out | 2 +- 7 files changed, 99 insertions(+), 21 deletions(-) diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs index eb53151ced..c2fa212d3a 100644 --- a/runtime/ops/process.rs +++ b/runtime/ops/process.rs @@ -17,6 +17,7 @@ use deno_io::ChildStderrResource; use deno_io::ChildStdinResource; use deno_io::ChildStdoutResource; use deno_permissions::PermissionsContainer; +use deno_permissions::RunPathQuery; use serde::Deserialize; use serde::Serialize; use std::borrow::Cow; @@ -516,7 +517,15 @@ fn compute_run_cmd_and_check_permissions( .with_context(|| format!("Failed to spawn '{}'", arg_cmd))?; let cmd = resolve_cmd(arg_cmd, &run_env) .with_context(|| format!("Failed to spawn '{}'", arg_cmd))?; - check_run_permission(state, &cmd, &run_env, api_name)?; + check_run_permission( + state, + RunPathQuery { + requested: arg_cmd, + resolved: &cmd, + }, + &run_env, + api_name, + )?; Ok((cmd, run_env)) } @@ -588,7 +597,7 @@ fn resolve_path(path: &str, cwd: &Path) -> PathBuf { fn check_run_permission( state: &mut OpState, - cmd: &Path, + cmd: RunPathQuery, run_env: &RunEnv, api_name: &str, ) -> Result<(), AnyError> { diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs index 2eacd8bcc4..da9e493d3f 100644 --- a/runtime/permissions/lib.rs +++ b/runtime/permissions/lib.rs @@ -862,6 +862,12 @@ impl AsRef for EnvDescriptor { } } +#[derive(Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize)] +pub struct RunPathQuery<'a> { + pub requested: &'a str, + pub resolved: &'a Path, +} + pub enum RunDescriptorArg { Name(String), Path(PathBuf), @@ -1321,16 +1327,16 @@ impl UnaryPermission { pub fn check( &mut self, - cmd: &Path, + cmd: RunPathQuery, api_name: Option<&str>, ) -> Result<(), AnyError> { - debug_assert!(cmd.is_absolute()); + debug_assert!(cmd.resolved.is_absolute()); skip_check_if_is_permission_fully_granted!(self); self.check_desc( - Some(&RunDescriptor::Path(cmd.to_path_buf())), + Some(&RunDescriptor::Path(cmd.resolved.to_path_buf())), false, api_name, - || Some(format!("\"{}\"", cmd.display())), + || Some(format!("\"{}\"", cmd.requested)), ) } @@ -1692,7 +1698,7 @@ impl PermissionsContainer { #[inline(always)] pub fn check_run( &mut self, - cmd: &Path, + cmd: RunPathQuery, api_name: &str, ) -> Result<(), AnyError> { self.0.lock().run.check(cmd, Some(api_name)) @@ -3036,10 +3042,37 @@ mod tests { #[allow(clippy::disallowed_methods)] let cwd = std::env::current_dir().unwrap(); prompt_value.set(true); - assert!(perms.run.check(&cwd.join("cat"), None).is_ok()); + assert!(perms + .run + .check( + RunPathQuery { + requested: "cat", + resolved: &cwd.join("cat") + }, + None + ) + .is_ok()); prompt_value.set(false); - assert!(perms.run.check(&cwd.join("cat"), None).is_ok()); - assert!(perms.run.check(&cwd.join("ls"), None).is_err()); + assert!(perms + .run + .check( + RunPathQuery { + requested: "cat", + resolved: &cwd.join("cat") + }, + None + ) + .is_ok()); + assert!(perms + .run + .check( + RunPathQuery { + requested: "ls", + resolved: &cwd.join("ls") + }, + None + ) + .is_err()); prompt_value.set(true); assert!(perms.env.check("HOME", None).is_ok()); @@ -3133,12 +3166,48 @@ mod tests { prompt_value.set(false); #[allow(clippy::disallowed_methods)] let cwd = std::env::current_dir().unwrap(); - assert!(perms.run.check(&cwd.join("cat"), None).is_err()); + assert!(perms + .run + .check( + RunPathQuery { + requested: "cat", + resolved: &cwd.join("cat") + }, + None + ) + .is_err()); prompt_value.set(true); - assert!(perms.run.check(&cwd.join("cat"), None).is_err()); - assert!(perms.run.check(&cwd.join("ls"), None).is_ok()); + assert!(perms + .run + .check( + RunPathQuery { + requested: "cat", + resolved: &cwd.join("cat") + }, + None + ) + .is_err()); + assert!(perms + .run + .check( + RunPathQuery { + requested: "ls", + resolved: &cwd.join("ls") + }, + None + ) + .is_ok()); prompt_value.set(false); - assert!(perms.run.check(&cwd.join("ls"), None).is_ok()); + assert!(perms + .run + .check( + RunPathQuery { + requested: "ls", + resolved: &cwd.join("ls") + }, + None + ) + .is_ok()); prompt_value.set(false); assert!(perms.env.check("HOME", None).is_err()); diff --git a/tests/specs/compile/permissions_denied/main.out b/tests/specs/compile/permissions_denied/main.out index 47a4707cc7..e9ea45c812 100644 --- a/tests/specs/compile/permissions_denied/main.out +++ b/tests/specs/compile/permissions_denied/main.out @@ -1,2 +1,2 @@ -error: Uncaught (in promise) PermissionDenied: Requires run access to "[WILDLINE]deno[WILDLINE]", specify the required permissions during compilation using `deno compile --allow-run` +error: Uncaught (in promise) PermissionDenied: Requires run access to "deno", specify the required permissions during compilation using `deno compile --allow-run` [WILDCARD] \ No newline at end of file diff --git a/tests/specs/permission/path_not_permitted/main.out b/tests/specs/permission/path_not_permitted/main.out index 3817c2ca51..77f8001586 100644 --- a/tests/specs/permission/path_not_permitted/main.out +++ b/tests/specs/permission/path_not_permitted/main.out @@ -1,10 +1,10 @@ Running... -PermissionDenied: Requires run access to "[WILDLINE]deno[WILDLINE]", run again with the --allow-run flag +PermissionDenied: Requires run access to "deno", run again with the --allow-run flag [WILDCARD] at file:///[WILDLINE]/sub.ts:15:5 { name: "PermissionDenied" } -PermissionDenied: Requires run access to "[WILDLINE]deno[WILDLINE]", run again with the --allow-run flag +PermissionDenied: Requires run access to "deno", run again with the --allow-run flag [WILDCARD] at file:///[WILDLINE]/sub.ts:23:22 { name: "PermissionDenied" diff --git a/tests/specs/permission/path_not_permitted/main.ts b/tests/specs/permission/path_not_permitted/main.ts index 9e8d627f2a..0cc141e7ac 100644 --- a/tests/specs/permission/path_not_permitted/main.ts +++ b/tests/specs/permission/path_not_permitted/main.ts @@ -9,7 +9,7 @@ new Deno.Command( "run", "--allow-write", "--allow-read", - `--allow-run=${binaryName}`, + `--allow-run=deno`, "sub.ts", ], stderr: "inherit", diff --git a/tests/specs/permission/path_not_permitted/sub.ts b/tests/specs/permission/path_not_permitted/sub.ts index f2b6d6b37c..ea527a938b 100644 --- a/tests/specs/permission/path_not_permitted/sub.ts +++ b/tests/specs/permission/path_not_permitted/sub.ts @@ -6,7 +6,7 @@ Deno.copyFileSync(binaryName, "subdir/" + binaryName); try { const commandResult = new Deno.Command( - binaryName, + "deno", { env: { "PATH": Deno.cwd() + pathSep + "subdir" }, stdout: "inherit", @@ -22,7 +22,7 @@ try { try { const child = Deno.run( { - cmd: [binaryName], + cmd: ["deno"], env: { "PATH": Deno.cwd() + pathSep + "subdir" }, stdout: "inherit", stderr: "inherit", diff --git a/tests/testdata/run/089_run_allow_list.ts.out b/tests/testdata/run/089_run_allow_list.ts.out index 0fc1c80c2a..68a4a2ac57 100644 --- a/tests/testdata/run/089_run_allow_list.ts.out +++ b/tests/testdata/run/089_run_allow_list.ts.out @@ -1,3 +1,3 @@ -[WILDCARD]PermissionDenied: Requires run access to "[WILDLINE]ls[WILDLINE]", run again with the --allow-run flag +[WILDCARD]PermissionDenied: Requires run access to "ls", run again with the --allow-run flag [WILDCARD] true From 186f7484da3116aeda474f7f529d417ee542b450 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Thu, 5 Sep 2024 13:30:18 +0900 Subject: [PATCH 37/45] fix(ext/node): close upgraded socket when the underlying http connection is closed (#25387) This change fixes the handling of upgraded socket from `node:http` module. In `op_node_http_fetch_response_upgrade`, we create DuplexStream paired with `hyper::upgrade::Upgraded`. When the connection is closed from the server, the read result from `Upgraded` becomes 0. However because we don't close the paired DuplexStream at that point, the Socket object in JS side keeps alive even after the server closed. That caused the issue #20179 This change fixes it by closing the paired DuplexStream when the `Upgraded` stream returns 0 read result. closes #20179 --- ext/node/ops/http.rs | 1 + tests/unit_node/http_test.ts | 68 ++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/ext/node/ops/http.rs b/ext/node/ops/http.rs index 4b1f99ec04..773902dedf 100644 --- a/ext/node/ops/http.rs +++ b/ext/node/ops/http.rs @@ -272,6 +272,7 @@ pub async fn op_node_http_fetch_response_upgrade( loop { let read = upgraded_rx.read(&mut buf).await?; if read == 0 { + read_tx.shutdown().await?; break; } read_tx.write_all(&buf[..read]).await?; diff --git a/tests/unit_node/http_test.ts b/tests/unit_node/http_test.ts index 7f5e74bf5d..f85b1466b5 100644 --- a/tests/unit_node/http_test.ts +++ b/tests/unit_node/http_test.ts @@ -13,6 +13,7 @@ import { text } from "node:stream/consumers"; import { assert, assertEquals, fail } from "@std/assert"; import { assertSpyCalls, spy } from "@std/testing/mock"; import { fromFileUrl, relative } from "@std/path"; +import { retry } from "@std/async/retry"; import { gzip } from "node:zlib"; import { Buffer } from "node:buffer"; @@ -1604,3 +1605,70 @@ Deno.test("[node/http] In ClientRequest, option.hostname has precedence over opt await responseReceived.promise; }); + +Deno.test("[node/http] upgraded socket closes when the server closed without closing handshake", async () => { + const clientSocketClosed = Promise.withResolvers(); + const serverProcessClosed = Promise.withResolvers(); + + // Uses the server in different process to shutdown it without closing handshake + const server = ` + Deno.serve({ port: 1337 }, (req) => { + if (req.headers.get("upgrade") != "websocket") { + return new Response("ok"); + } + console.log("upgrade on server"); + const { socket, response } = Deno.upgradeWebSocket(req); + socket.addEventListener("message", (event) => { + console.log("server received", event.data); + socket.send("pong"); + }); + return response; + }); + `; + + const p = new Deno.Command("deno", { args: ["eval", server] }).spawn(); + + // Wait for the server to respond + await retry(async () => { + const resp = await fetch("http://localhost:1337"); + const _text = await resp.text(); + }); + + const options = { + port: 1337, + host: "127.0.0.1", + headers: { + "Connection": "Upgrade", + "Upgrade": "websocket", + "Sec-WebSocket-Key": "dGhlIHNhbXBsZSBub25jZQ==", + }, + }; + + http.request(options).on("upgrade", (_res, socket) => { + socket.on("close", () => { + console.log("client socket closed"); + clientSocketClosed.resolve(); + }); + socket.on("data", async (data) => { + // receives pong message + assertEquals(data, Buffer.from("8104706f6e67", "hex")); + + p.kill(); + await p.status; + + console.log("process closed"); + serverProcessClosed.resolve(); + + // sending some additional message + socket.write(Buffer.from("81847de88e01", "hex")); + socket.write(Buffer.from("0d81e066", "hex")); + }); + + // sending ping message + socket.write(Buffer.from("81847de88e01", "hex")); + socket.write(Buffer.from("0d81e066", "hex")); + }).end(); + + await clientSocketClosed.promise; + await serverProcessClosed.promise; +}); From e799c2857c81f7656811aaf6a248432fe88e8169 Mon Sep 17 00:00:00 2001 From: Yoshiya Hinosawa Date: Thu, 5 Sep 2024 14:13:06 +0900 Subject: [PATCH 38/45] fix(ext/http): do not set localhost to hostname unnecessarily (#24777) This commit changes when to cause the hostname substition of `0.0.0.0` -> `localhost`. Currently we substitute `localhost` to the hostname on windows before calling `options.onListen`, which prevents the users to do more advanced thing using hostname string like https://github.com/denoland/std/issues/5558. This PR changes it not to substitute it when the user provide `onListen` callback. closes #24776 unblocks https://github.com/denoland/std/issues/5558 --- ext/http/00_serve.ts | 31 +++++++++++++++++++------------ tests/unit/serve_test.ts | 16 ++++++++++++++++ tests/unit_node/net_test.ts | 1 + 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/ext/http/00_serve.ts b/ext/http/00_serve.ts index c8f4e0604b..c8ddaa64be 100644 --- a/ext/http/00_serve.ts +++ b/ext/http/00_serve.ts @@ -583,6 +583,19 @@ type RawServeOptions = { const kLoadBalanced = Symbol("kLoadBalanced"); +function mapAnyAddrToLocalhostForWindows(hostname: string) { + // If the hostname is "0.0.0.0", we display "localhost" in console + // because browsers in Windows don't resolve "0.0.0.0". + // See the discussion in https://github.com/denoland/deno_std/issues/1165 + if ( + (Deno.build.os === "windows") && + (hostname == "0.0.0.0" || hostname == "::") + ) { + return "localhost"; + } + return hostname; +} + function serve(arg1, arg2) { let options: RawServeOptions | undefined; let handler: RawHandler | undefined; @@ -672,22 +685,15 @@ function serve(arg1, arg2) { } const addr = listener.addr; - // If the hostname is "0.0.0.0", we display "localhost" in console - // because browsers in Windows don't resolve "0.0.0.0". - // See the discussion in https://github.com/denoland/deno_std/issues/1165 - const hostname = (addr.hostname == "0.0.0.0" || addr.hostname == "::") && - (Deno.build.os === "windows") - ? "localhost" - : addr.hostname; - addr.hostname = hostname; const onListen = (scheme) => { if (options.onListen) { options.onListen(addr); } else { - const host = StringPrototypeIncludes(addr.hostname, ":") - ? `[${addr.hostname}]` - : addr.hostname; + const hostname = mapAnyAddrToLocalhostForWindows(addr.hostname); + const host = StringPrototypeIncludes(hostname, ":") + ? `[${hostname}]` + : hostname; // deno-lint-ignore no-console console.log(`Listening on ${scheme}${host}:${addr.port}/`); } @@ -862,9 +868,10 @@ function registerDeclarativeServer(exports) { const nThreads = serveWorkerCount > 1 ? ` with ${serveWorkerCount} threads` : ""; + const hostname_ = mapAnyAddrToLocalhostForWindows(hostname); // deno-lint-ignore no-console console.debug( - `%cdeno serve%c: Listening on %chttp://${hostname}:${port}/%c${nThreads}`, + `%cdeno serve%c: Listening on %chttp://${hostname_}:${port}/%c${nThreads}`, "color: green", "color: inherit", "color: yellow", diff --git a/tests/unit/serve_test.ts b/tests/unit/serve_test.ts index fde6c7bee6..19a8ccad1b 100644 --- a/tests/unit/serve_test.ts +++ b/tests/unit/serve_test.ts @@ -4191,3 +4191,19 @@ Deno.test({ 'Operation `"op_net_listen_unix"` not supported on non-unix platforms.', ); }); + +Deno.test({ + name: "onListen callback gets 0.0.0.0 hostname as is", +}, async () => { + const { promise, resolve } = Promise.withResolvers<{ hostname: string }>(); + + const server = Deno.serve({ + handler: (_) => new Response("ok"), + hostname: "0.0.0.0", + port: 0, + onListen: resolve, + }); + const { hostname } = await promise; + assertEquals(hostname, "0.0.0.0"); + await server.shutdown(); +}); diff --git a/tests/unit_node/net_test.ts b/tests/unit_node/net_test.ts index f49ff0ef08..708d06386c 100644 --- a/tests/unit_node/net_test.ts +++ b/tests/unit_node/net_test.ts @@ -79,6 +79,7 @@ Deno.test("[node/net] net.connect().unref() works", async () => { port: 0, // any available port will do handler: () => new Response("hello"), onListen: async ({ port, hostname }) => { + hostname = Deno.build.os === "windows" ? "localhost" : hostname; const { stdout, stderr } = await new Deno.Command(Deno.execPath(), { args: [ "eval", From 0450c12df5deee18407b940ffedeb1a16bab35a1 Mon Sep 17 00:00:00 2001 From: Ian Bull Date: Wed, 4 Sep 2024 23:20:27 -0700 Subject: [PATCH 39/45] refactor(ext/crypto): align error messages (#25440) Aligns the error messages in the ext/crypto folder to be in-line with the Deno style guide. https://github.com/denoland/deno/issues/25269 --- ext/crypto/00_crypto.js | 168 +++++++++++++++++++++------------------- 1 file changed, 90 insertions(+), 78 deletions(-) diff --git a/ext/crypto/00_crypto.js b/ext/crypto/00_crypto.js index 80a2e11c7c..f7dfe488fd 100644 --- a/ext/crypto/00_crypto.js +++ b/ext/crypto/00_crypto.js @@ -292,7 +292,7 @@ function normalizeAlgorithm(algorithm, op) { normalizedAlgorithm[member] = normalizeAlgorithm(idlValue, "digest"); } else if (idlType === "AlgorithmIdentifier") { // TODO(lucacasonato): implement - throw new TypeError("unimplemented"); + throw new TypeError("Unimplemented"); } } @@ -443,7 +443,7 @@ function getKeyLength(algorithm) { // 1. if (!ArrayPrototypeIncludes([128, 192, 256], algorithm.length)) { throw new DOMException( - "length must be 128, 192, or 256", + `Length must be 128, 192, or 256: received ${algorithm.length}`, "OperationError", ); } @@ -470,14 +470,14 @@ function getKeyLength(algorithm) { break; default: throw new DOMException( - "Unrecognized hash algorithm", + `Unrecognized hash algorithm: ${algorithm.hash.name}`, "NotSupportedError", ); } } else if (algorithm.length !== 0) { length = algorithm.length; } else { - throw new TypeError("Invalid length."); + throw new TypeError(`Invalid length: ${algorithm.length}`); } // 2. @@ -492,7 +492,7 @@ function getKeyLength(algorithm) { return null; } default: - throw new TypeError("unreachable"); + throw new TypeError("Unreachable"); } } @@ -556,7 +556,7 @@ class SubtleCrypto { // 8. if (normalizedAlgorithm.name !== key[_algorithm].name) { throw new DOMException( - "Encryption algorithm doesn't match key algorithm.", + `Encryption algorithm '${normalizedAlgorithm.name}' does not match key algorithm`, "InvalidAccessError", ); } @@ -564,7 +564,7 @@ class SubtleCrypto { // 9. if (!ArrayPrototypeIncludes(key[_usages], "encrypt")) { throw new DOMException( - "Key does not support the 'encrypt' operation.", + "Key does not support the 'encrypt' operation", "InvalidAccessError", ); } @@ -599,7 +599,7 @@ class SubtleCrypto { // 8. if (normalizedAlgorithm.name !== key[_algorithm].name) { throw new DOMException( - "Decryption algorithm doesn't match key algorithm.", + `Decryption algorithm "${normalizedAlgorithm.name}" does not match key algorithm`, "OperationError", ); } @@ -607,7 +607,7 @@ class SubtleCrypto { // 9. if (!ArrayPrototypeIncludes(key[_usages], "decrypt")) { throw new DOMException( - "Key does not support the 'decrypt' operation.", + "Key does not support the 'decrypt' operation", "InvalidAccessError", ); } @@ -683,7 +683,7 @@ class SubtleCrypto { normalizedAlgorithm.length === 0 || normalizedAlgorithm.length > 128 ) { throw new DOMException( - "Counter length must not be 0 or greater than 128", + `Counter length must not be 0 or greater than 128: received ${normalizedAlgorithm.length}`, "OperationError", ); } @@ -713,7 +713,7 @@ class SubtleCrypto { ) ) { throw new DOMException( - "Invalid tag length", + `Invalid tag length: ${normalizedAlgorithm.tagLength}`, "OperationError", ); } @@ -805,7 +805,7 @@ class SubtleCrypto { // 8. if (normalizedAlgorithm.name !== key[_algorithm].name) { throw new DOMException( - "Signing algorithm doesn't match key algorithm.", + "Signing algorithm does not match key algorithm", "InvalidAccessError", ); } @@ -813,7 +813,7 @@ class SubtleCrypto { // 9. if (!ArrayPrototypeIncludes(key[_usages], "sign")) { throw new DOMException( - "Key does not support the 'sign' operation.", + "Key does not support the 'sign' operation", "InvalidAccessError", ); } @@ -928,7 +928,7 @@ class SubtleCrypto { } } - throw new TypeError("unreachable"); + throw new TypeError("Unreachable"); } /** @@ -967,11 +967,11 @@ class SubtleCrypto { if (ArrayBufferIsView(keyData) || isArrayBuffer(keyData)) { keyData = copyBuffer(keyData); } else { - throw new TypeError("keyData is a JsonWebKey"); + throw new TypeError("Cannot import key: 'keyData' is a JsonWebKey"); } } else { if (ArrayBufferIsView(keyData) || isArrayBuffer(keyData)) { - throw new TypeError("keyData is not a JsonWebKey"); + throw new TypeError("Cannot import key: 'keyData' is not a JsonWebKey"); } } @@ -1156,7 +1156,7 @@ class SubtleCrypto { // 8. if (!ArrayPrototypeIncludes(baseKey[_usages], "deriveBits")) { throw new DOMException( - "baseKey usages does not contain `deriveBits`", + "'baseKey' usages does not contain 'deriveBits'", "InvalidAccessError", ); } @@ -1222,7 +1222,7 @@ class SubtleCrypto { // 11. if (normalizedAlgorithm.name !== baseKey[_algorithm].name) { throw new DOMException( - "Invalid algorithm name", + `Invalid algorithm name: ${normalizedAlgorithm.name}`, "InvalidAccessError", ); } @@ -1230,7 +1230,7 @@ class SubtleCrypto { // 12. if (!ArrayPrototypeIncludes(baseKey[_usages], "deriveKey")) { throw new DOMException( - "baseKey usages does not contain `deriveKey`", + "'baseKey' usages does not contain 'deriveKey'", "InvalidAccessError", ); } @@ -1259,7 +1259,7 @@ class SubtleCrypto { ArrayPrototypeIncludes(["private", "secret"], result[_type]) && keyUsages.length == 0 ) { - throw new SyntaxError("Invalid key usages"); + throw new SyntaxError("Invalid key usage"); } // 17. return result; @@ -1298,14 +1298,14 @@ class SubtleCrypto { if (normalizedAlgorithm.name !== key[_algorithm].name) { throw new DOMException( - "Verifying algorithm doesn't match key algorithm.", + "Verifying algorithm does not match key algorithm", "InvalidAccessError", ); } if (!ArrayPrototypeIncludes(key[_usages], "verify")) { throw new DOMException( - "Key does not support the 'verify' operation.", + "Key does not support the 'verify' operation", "InvalidAccessError", ); } @@ -1396,7 +1396,7 @@ class SubtleCrypto { } } - throw new TypeError("unreachable"); + throw new TypeError("Unreachable"); } /** @@ -1435,7 +1435,7 @@ class SubtleCrypto { // 8. if (normalizedAlgorithm.name !== wrappingKey[_algorithm].name) { throw new DOMException( - "Wrapping algorithm doesn't match key algorithm.", + "Wrapping algorithm does not match key algorithm", "InvalidAccessError", ); } @@ -1443,7 +1443,7 @@ class SubtleCrypto { // 9. if (!ArrayPrototypeIncludes(wrappingKey[_usages], "wrapKey")) { throw new DOMException( - "Key does not support the 'wrapKey' operation.", + "Key does not support the 'wrapKey' operation", "InvalidAccessError", ); } @@ -1591,7 +1591,7 @@ class SubtleCrypto { // 11. if (normalizedAlgorithm.name !== unwrappingKey[_algorithm].name) { throw new DOMException( - "Unwrapping algorithm doesn't match key algorithm.", + "Unwrapping algorithm does not match key algorithm", "InvalidAccessError", ); } @@ -1599,7 +1599,7 @@ class SubtleCrypto { // 12. if (!ArrayPrototypeIncludes(unwrappingKey[_usages], "unwrapKey")) { throw new DOMException( - "Key does not support the 'unwrapKey' operation.", + "Key does not support the 'unwrapKey' operation", "InvalidAccessError", ); } @@ -1678,7 +1678,7 @@ class SubtleCrypto { (result[_type] == "secret" || result[_type] == "private") && keyUsages.length == 0 ) { - throw new SyntaxError("Invalid key type."); + throw new SyntaxError("Invalid key type"); } // 17. result[_extractable] = extractable; @@ -1726,13 +1726,13 @@ class SubtleCrypto { if (ObjectPrototypeIsPrototypeOf(CryptoKeyPrototype, result)) { const type = result[_type]; if ((type === "secret" || type === "private") && usages.length === 0) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } } else if ( ObjectPrototypeIsPrototypeOf(CryptoKeyPrototype, result.privateKey) ) { if (result.privateKey[_usages].length === 0) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } } @@ -1758,7 +1758,7 @@ async function generateKey(normalizedAlgorithm, extractable, usages) { (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } // 2. @@ -1817,7 +1817,7 @@ async function generateKey(normalizedAlgorithm, extractable, usages) { ], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } // 2. @@ -1873,7 +1873,7 @@ async function generateKey(normalizedAlgorithm, extractable, usages) { (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } // 2-3. @@ -1933,7 +1933,7 @@ async function generateKey(normalizedAlgorithm, extractable, usages) { (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } // 2-3. @@ -1999,7 +1999,7 @@ async function generateKey(normalizedAlgorithm, extractable, usages) { ], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } return generateKeyAES(normalizedAlgorithm, extractable, usages); @@ -2012,7 +2012,7 @@ async function generateKey(normalizedAlgorithm, extractable, usages) { (u) => !ArrayPrototypeIncludes(["wrapKey", "unwrapKey"], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } return generateKeyAES(normalizedAlgorithm, extractable, usages); @@ -2024,7 +2024,7 @@ async function generateKey(normalizedAlgorithm, extractable, usages) { (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } const privateKeyData = new Uint8Array(32); const publicKeyData = new Uint8Array(32); @@ -2065,7 +2065,7 @@ async function generateKey(normalizedAlgorithm, extractable, usages) { (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } const ED25519_SEED_LEN = 32; @@ -2114,7 +2114,7 @@ async function generateKey(normalizedAlgorithm, extractable, usages) { (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } // 2. @@ -2178,7 +2178,7 @@ function importKeyEd25519( (u) => !ArrayPrototypeIncludes(["verify"], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } const handle = {}; @@ -2206,7 +2206,7 @@ function importKeyEd25519( (u) => !ArrayPrototypeIncludes(["verify"], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } const publicKeyData = new Uint8Array(32); @@ -2237,7 +2237,7 @@ function importKeyEd25519( (u) => !ArrayPrototypeIncludes(["sign"], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } const privateKeyData = new Uint8Array(32); @@ -2276,7 +2276,7 @@ function importKeyEd25519( ), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } } else { if ( @@ -2289,7 +2289,7 @@ function importKeyEd25519( ), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } } @@ -2349,7 +2349,7 @@ function importKeyEd25519( try { privateKeyData = op_crypto_base64url_decode(jwk.d); } catch (_) { - throw new DOMException("invalid private key data", "DataError"); + throw new DOMException("Invalid private key data", "DataError"); } const handle = {}; @@ -2372,7 +2372,7 @@ function importKeyEd25519( try { publicKeyData = op_crypto_base64url_decode(jwk.x); } catch (_) { - throw new DOMException("invalid public key data", "DataError"); + throw new DOMException("Invalid public key data", "DataError"); } const handle = {}; @@ -2406,7 +2406,7 @@ function importKeyX25519( case "raw": { // 1. if (keyUsages.length > 0) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } const handle = {}; @@ -2429,7 +2429,7 @@ function importKeyX25519( case "spki": { // 1. if (keyUsages.length > 0) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } const publicKeyData = new Uint8Array(32); @@ -2460,7 +2460,7 @@ function importKeyX25519( (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } const privateKeyData = new Uint8Array(32); @@ -2499,13 +2499,13 @@ function importKeyX25519( ), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } } // 3. if (jwk.d === undefined && keyUsages.length > 0) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } // 4. @@ -2641,7 +2641,7 @@ function exportKeyAES( break; default: throw new DOMException( - "Invalid key length", + `Invalid key length: ${algorithm.length}`, "NotSupportedError", ); } @@ -2675,7 +2675,7 @@ function importKeyAES( (u) => !ArrayPrototypeIncludes(supportedKeyUsages, u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } const algorithmName = normalizedAlgorithm.name; @@ -2731,7 +2731,10 @@ function importKeyAES( jwk.alg !== undefined && jwk.alg !== aesJwkAlg[algorithmName][128] ) { - throw new DOMException("Invalid algorithm", "DataError"); + throw new DOMException( + `Invalid algorithm: ${jwk.alg}`, + "DataError", + ); } break; case 192: @@ -2739,7 +2742,10 @@ function importKeyAES( jwk.alg !== undefined && jwk.alg !== aesJwkAlg[algorithmName][192] ) { - throw new DOMException("Invalid algorithm", "DataError"); + throw new DOMException( + `Invalid algorithm: ${jwk.alg}`, + "DataError", + ); } break; case 256: @@ -2747,7 +2753,10 @@ function importKeyAES( jwk.alg !== undefined && jwk.alg !== aesJwkAlg[algorithmName][256] ) { - throw new DOMException("Invalid algorithm", "DataError"); + throw new DOMException( + `Invalid algorithm: ${jwk.alg}`, + "DataError", + ); } break; default: @@ -2761,7 +2770,7 @@ function importKeyAES( if ( keyUsages.length > 0 && jwk.use !== undefined && jwk.use !== "enc" ) { - throw new DOMException("Invalid key usages", "DataError"); + throw new DOMException("Invalid key usage", "DataError"); } // 7. @@ -2844,7 +2853,7 @@ function importKeyHMAC( (u) => !ArrayPrototypeIncludes(["sign", "verify"], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } // 3. @@ -2926,7 +2935,7 @@ function importKeyHMAC( break; } default: - throw new TypeError("unreachable"); + throw new TypeError("Unreachable"); } // 7. @@ -3059,7 +3068,7 @@ function importKeyEC( ), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } // 3. @@ -3100,7 +3109,7 @@ function importKeyEC( ), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } // 2-9. @@ -3140,7 +3149,7 @@ function importKeyEC( ), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } } else if (keyUsages.length != 0) { throw new DOMException("Key usage must be empty", "SyntaxError"); @@ -3183,7 +3192,7 @@ function importKeyEC( (u) => !ArrayPrototypeIncludes(supportedUsages[keyType], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } // 3. @@ -3391,7 +3400,7 @@ function importKeyRSA( ), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } // 2-9. @@ -3436,7 +3445,7 @@ function importKeyRSA( ), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } // 2-9. @@ -3485,7 +3494,7 @@ function importKeyRSA( ), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } } else if ( ArrayPrototypeFind( @@ -3497,7 +3506,7 @@ function importKeyRSA( ), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } // 3. @@ -3579,7 +3588,7 @@ function importKeyRSA( break; default: throw new DOMException( - `'alg' property of JsonWebKey must be one of 'RS1', 'RS256', 'RS384', 'RS512'`, + `'alg' property of JsonWebKey must be one of 'RS1', 'RS256', 'RS384', 'RS512': received ${jwk.alg}`, "DataError", ); } @@ -3602,7 +3611,7 @@ function importKeyRSA( break; default: throw new DOMException( - `'alg' property of JsonWebKey must be one of 'PS1', 'PS256', 'PS384', 'PS512'`, + `'alg' property of JsonWebKey must be one of 'PS1', 'PS256', 'PS384', 'PS512': received ${jwk.alg}`, "DataError", ); } @@ -3625,7 +3634,7 @@ function importKeyRSA( break; default: throw new DOMException( - `'alg' property of JsonWebKey must be one of 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', or 'RSA-OAEP-512'`, + `'alg' property of JsonWebKey must be one of 'RSA-OAEP', 'RSA-OAEP-256', 'RSA-OAEP-384', or 'RSA-OAEP-512': received ${jwk.alg}`, "DataError", ); } @@ -3639,7 +3648,7 @@ function importKeyRSA( // 9.2. if (normalizedHash.name !== normalizedAlgorithm.hash.name) { throw new DOMException( - `'alg' property of JsonWebKey must be '${normalizedAlgorithm.name}'`, + `'alg' property of JsonWebKey must be '${normalizedAlgorithm.name}': received ${jwk.alg}`, "DataError", ); } @@ -3684,7 +3693,7 @@ function importKeyRSA( } } else { throw new DOMException( - "only optimized private keys are supported", + "Only optimized private keys are supported", "NotSupportedError", ); } @@ -3782,7 +3791,7 @@ function importKeyHKDF( (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } // 2. @@ -3834,7 +3843,7 @@ function importKeyPBKDF2( (u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u), ) !== undefined ) { - throw new DOMException("Invalid key usages", "SyntaxError"); + throw new DOMException("Invalid key usage", "SyntaxError"); } // 3. @@ -3878,7 +3887,7 @@ function exportKeyHMAC(format, key, innerKey) { // 3. case "raw": { const bits = innerKey.data; - // TODO(petamoriken): Uint8Array doesn't have push method + // TODO(petamoriken): Uint8Array does not have push method // for (let _i = 7 & (8 - bits.length % 8); _i > 0; _i--) { // bits.push(0); // } @@ -4331,7 +4340,10 @@ async function generateKeyAES(normalizedAlgorithm, extractable, usages) { // 2. if (!ArrayPrototypeIncludes([128, 192, 256], normalizedAlgorithm.length)) { - throw new DOMException("Invalid key length", "OperationError"); + throw new DOMException( + `Invalid key length: ${normalizedAlgorithm.length}`, + "OperationError", + ); } // 3. @@ -4417,7 +4429,7 @@ async function deriveBits(normalizedAlgorithm, baseKey, length) { publicKey[_algorithm].namedCurve !== baseKey[_algorithm].namedCurve ) { throw new DOMException( - "namedCurve mismatch", + "'namedCurve' mismatch", "InvalidAccessError", ); } @@ -4670,7 +4682,7 @@ async function encrypt(normalizedAlgorithm, key, data) { ) ) { throw new DOMException( - "Invalid tag length", + `Invalid tag length: ${normalizedAlgorithm.tagLength}`, "OperationError", ); } From 713ed065e7cd1013e525a8e571cef08a30de87be Mon Sep 17 00:00:00 2001 From: Asher Gomez Date: Thu, 5 Sep 2024 16:22:47 +1000 Subject: [PATCH 40/45] BREAKING(fs): remove `Deno.File` (#25447) Towards #22079 --- cli/tsc/dts/lib.deno.ns.d.ts | 11 ----------- ext/fs/30_fs.js | 3 --- runtime/js/99_main.js | 2 -- tests/specs/future/runtime_api/main.js | 1 - tests/specs/future/runtime_api/main.out | 1 - tests/unit/files_test.ts | 3 --- 6 files changed, 21 deletions(-) diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index e47a2d942d..319b1da455 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -2530,17 +2530,6 @@ declare namespace Deno { [Symbol.dispose](): void; } - /** - * The Deno abstraction for reading and writing files. - * - * @deprecated This will be removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - * - * @category File System - */ - export const File: typeof FsFile; - /** Gets the size of the console as columns/rows. * * ```ts diff --git a/ext/fs/30_fs.js b/ext/fs/30_fs.js index 35bf0e712e..f979badb97 100644 --- a/ext/fs/30_fs.js +++ b/ext/fs/30_fs.js @@ -786,8 +786,6 @@ function checkOpenOptions(options) { } } -const File = FsFile; - function readFileSync(path) { return op_fs_read_file_sync(pathFromURL(path)); } @@ -950,7 +948,6 @@ export { cwd, fdatasync, fdatasyncSync, - File, FsFile, fsync, fsyncSync, diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index d0171acd27..913761f6a8 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -801,7 +801,6 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) { if (internals.future) { delete globalThis.window; delete Deno.Buffer; - delete Deno.File; delete Deno.FsFile.prototype.rid; delete Deno.funlock; delete Deno.funlockSync; @@ -965,7 +964,6 @@ function bootstrapWorkerRuntime( if (internals.future) { delete Deno.Buffer; - delete Deno.File; delete Deno.FsFile.prototype.rid; delete Deno.funlock; delete Deno.funlockSync; diff --git a/tests/specs/future/runtime_api/main.js b/tests/specs/future/runtime_api/main.js index 26a75373ea..afaa4e57bb 100644 --- a/tests/specs/future/runtime_api/main.js +++ b/tests/specs/future/runtime_api/main.js @@ -1,6 +1,5 @@ console.log("window is", globalThis.window); console.log("Deno.Buffer is", Deno.Buffer); -console.log("Deno.File is", Deno.File); console.log( "Deno.FsFile.prototype.rid is", Deno.openSync(import.meta.filename).rid, diff --git a/tests/specs/future/runtime_api/main.out b/tests/specs/future/runtime_api/main.out index 0aa4e1f7c9..d629b13b40 100644 --- a/tests/specs/future/runtime_api/main.out +++ b/tests/specs/future/runtime_api/main.out @@ -1,6 +1,5 @@ window is undefined Deno.Buffer is undefined -Deno.File is undefined Deno.FsFile.prototype.rid is undefined Deno.funlock is undefined Deno.funlockSync is undefined diff --git a/tests/unit/files_test.ts b/tests/unit/files_test.ts index c9c3c01100..7b939d1ece 100644 --- a/tests/unit/files_test.ts +++ b/tests/unit/files_test.ts @@ -1,7 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -// deno-lint-ignore-file no-deprecated-deno-api - import { assert, assertEquals, @@ -24,7 +22,6 @@ Deno.test( async function filesCopyToStdout() { const filename = "tests/testdata/assets/fixture.json"; using file = await Deno.open(filename); - assert(file instanceof Deno.File); assert(file instanceof Deno.FsFile); assert(file.rid > 2); const bytesWritten = await copy(file, Deno.stdout); From 105c2e336a4312ed1714ab893c57eae2a19068c7 Mon Sep 17 00:00:00 2001 From: Asher Gomez Date: Thu, 5 Sep 2024 16:23:28 +1000 Subject: [PATCH 41/45] BREAKING(fs): remove `Deno.FsWatcher.prototype.rid` (#25444) Towards #22079 --- cli/tsc/dts/lib.deno.ns.d.ts | 8 -------- runtime/js/40_fs_events.js | 9 --------- tests/specs/future/runtime_api/main.js | 4 ---- tests/specs/future/runtime_api/main.out | 1 - 4 files changed, 22 deletions(-) diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index 319b1da455..cc6a1d4a88 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -3849,14 +3849,6 @@ declare namespace Deno { * @category File System */ export interface FsWatcher extends AsyncIterable, Disposable { - /** - * The resource id. - * - * @deprecated This will be removed in Deno 2.0. See the - * {@link https://docs.deno.com/runtime/manual/advanced/migrate_deprecations | Deno 1.x to 2.x Migration Guide} - * for migration instructions. - */ - readonly rid: number; /** Stops watching the file system and closes the watcher resource. */ close(): void; /** diff --git a/runtime/js/40_fs_events.js b/runtime/js/40_fs_events.js index d7aa5de05c..a0495540c6 100644 --- a/runtime/js/40_fs_events.js +++ b/runtime/js/40_fs_events.js @@ -31,15 +31,6 @@ class FsWatcher { this.#rid = op_fs_events_open({ recursive, paths }); } - get rid() { - internals.warnOnDeprecatedApi( - "Deno.FsWatcher.rid", - new Error().stack, - "Use `Deno.FsWatcher` instance methods instead.", - ); - return this.#rid; - } - unref() { core.unrefOpPromise(this.#promise); } diff --git a/tests/specs/future/runtime_api/main.js b/tests/specs/future/runtime_api/main.js index afaa4e57bb..95b10eecd0 100644 --- a/tests/specs/future/runtime_api/main.js +++ b/tests/specs/future/runtime_api/main.js @@ -53,10 +53,6 @@ console.log("Deno.TlsConn.prototype.rid is", tlsConn.rid); tlsConn.close(); -const watcher = Deno.watchFs("."); -console.log("Deno.FsWatcher.prototype.rid is", watcher.rid); -watcher.close(); - try { new Deno.FsFile(0); } catch (error) { diff --git a/tests/specs/future/runtime_api/main.out b/tests/specs/future/runtime_api/main.out index d629b13b40..489dd4636b 100644 --- a/tests/specs/future/runtime_api/main.out +++ b/tests/specs/future/runtime_api/main.out @@ -10,7 +10,6 @@ Deno.Conn.prototype.rid is undefined Deno.UnixConn.prototype.rid is undefined Deno.TlsListener.prototype.rid is undefined Deno.TlsConn.prototype.rid is undefined -Deno.FsWatcher.prototype.rid is undefined Deno.FsFile constructor is illegal Deno.ConnectTlsOptions.(certFile|keyFile) do nothing Deno.ConnectTlsOptions.(certChain|privateKey) do nothing From acd01eb2b41e37a480943f79cfa28fc220c4baca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Thu, 5 Sep 2024 07:25:56 +0100 Subject: [PATCH 42/45] feat: Add a hint on error about 'Relative import path ... not prefixed with ...' (#25430) Running a file like: ``` import "@std/dotenv/load"; ``` Without a mapping in `imports` field of `deno.json` or `dependencies` of `package.json` will now error out with a hint: ``` error: Relative import path "@std/dotenv/load" not prefixed with / or ./ or ../ hint: Try running `deno add @std/dotenv/load` at [WILDCARD]bare_specifier_without_import/main.ts:1:8 ``` Closes https://github.com/denoland/deno/issues/24699 --------- Co-authored-by: David Sherret --- cli/graph_util.rs | 60 +++++++++++++++++-- tests/integration/lsp_tests.rs | 2 +- .../095_cache_with_bare_import.ts.out | 1 + .../095_cache_with_bare_import.ts.out | 1 + .../__test__.jsonc | 5 ++ .../bare_specifier_without_import/main.out | 3 + .../run/bare_specifier_without_import/main.ts | 3 + .../run/error_type_definitions.ts.out | 1 + .../run/node_prefix_missing/main.ts.out | 2 +- .../run/with_package_json/with_stop/main.out | 1 + 10 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 tests/specs/run/bare_specifier_without_import/__test__.jsonc create mode 100644 tests/specs/run/bare_specifier_without_import/main.out create mode 100644 tests/specs/run/bare_specifier_without_import/main.ts diff --git a/cli/graph_util.rs b/cli/graph_util.rs index a9fcfd83db..f3ac64a434 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -724,12 +724,25 @@ impl ModuleGraphBuilder { pub fn enhanced_resolution_error_message(error: &ResolutionError) -> String { let mut message = format_deno_graph_error(error); - if let Some(specifier) = get_resolution_error_bare_node_specifier(error) { + let maybe_hint = if let Some(specifier) = + get_resolution_error_bare_node_specifier(error) + { if !*DENO_DISABLE_PEDANTIC_NODE_WARNINGS { - message.push_str(&format!( - "\nIf you want to use a built-in Node module, add a \"node:\" prefix (ex. \"node:{specifier}\")." - )); + Some(format!("If you want to use a built-in Node module, add a \"node:\" prefix (ex. \"node:{specifier}\").")) + } else { + None } + } else { + get_import_prefix_missing_error(error).map(|specifier| { + format!( + "If you want to use a JSR or npm package, try running `deno add {}`", + specifier + ) + }) + }; + + if let Some(hint) = maybe_hint { + message.push_str(&format!("\n {} {}", colors::cyan("hint:"), hint)); } message @@ -864,6 +877,45 @@ fn get_resolution_error_bare_specifier( } } +fn get_import_prefix_missing_error(error: &ResolutionError) -> Option<&str> { + let mut maybe_specifier = None; + if let ResolutionError::InvalidSpecifier { + error: SpecifierError::ImportPrefixMissing { specifier, .. }, + .. + } = error + { + maybe_specifier = Some(specifier); + } else if let ResolutionError::ResolverError { error, .. } = error { + match error.as_ref() { + ResolveError::Specifier(specifier_error) => { + if let SpecifierError::ImportPrefixMissing { specifier, .. } = + specifier_error + { + maybe_specifier = Some(specifier); + } + } + ResolveError::Other(other_error) => { + if let Some(SpecifierError::ImportPrefixMissing { specifier, .. }) = + other_error.downcast_ref::() + { + maybe_specifier = Some(specifier); + } + } + } + } + + // NOTE(bartlomieju): For now, return None if a specifier contains a dot or a space. This is because + // suggesting to `deno add bad-module.ts` makes no sense and is worse than not providing + // a suggestion at all. This should be improved further in the future + if let Some(specifier) = maybe_specifier { + if specifier.contains('.') || specifier.contains(' ') { + return None; + } + } + + maybe_specifier.map(|s| s.as_str()) +} + /// Gets if any of the specified root's "file:" dependents are in the /// provided changed set. pub fn has_graph_root_local_dependent_changed( diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 4f0f8a9850..78e5260851 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -8905,7 +8905,7 @@ fn lsp_completions_node_builtin() { "severity": 1, "code": "import-node-prefix-missing", "source": "deno", - "message": "Relative import path \"fs\" not prefixed with / or ./ or ../\nIf you want to use a built-in Node module, add a \"node:\" prefix (ex. \"node:fs\").", + "message": "Relative import path \"fs\" not prefixed with / or ./ or ../\n \u{1b}[0m\u{1b}[36mhint:\u{1b}[0m If you want to use a built-in Node module, add a \"node:\" prefix (ex. \"node:fs\").", "data": { "specifier": "fs" }, diff --git a/tests/specs/cache/with_bare_import/095_cache_with_bare_import.ts.out b/tests/specs/cache/with_bare_import/095_cache_with_bare_import.ts.out index 2668a6e083..50daf80419 100644 --- a/tests/specs/cache/with_bare_import/095_cache_with_bare_import.ts.out +++ b/tests/specs/cache/with_bare_import/095_cache_with_bare_import.ts.out @@ -1,2 +1,3 @@ [WILDCARD]error: Relative import path "foo" not prefixed with / or ./ or ../ + hint: If you want to use a JSR or npm package, try running `deno add foo` at file:///[WILDCARD]/095_cache_with_bare_import.ts:[WILDCARD] diff --git a/tests/specs/check/with_bare_import/095_cache_with_bare_import.ts.out b/tests/specs/check/with_bare_import/095_cache_with_bare_import.ts.out index 2668a6e083..50daf80419 100644 --- a/tests/specs/check/with_bare_import/095_cache_with_bare_import.ts.out +++ b/tests/specs/check/with_bare_import/095_cache_with_bare_import.ts.out @@ -1,2 +1,3 @@ [WILDCARD]error: Relative import path "foo" not prefixed with / or ./ or ../ + hint: If you want to use a JSR or npm package, try running `deno add foo` at file:///[WILDCARD]/095_cache_with_bare_import.ts:[WILDCARD] diff --git a/tests/specs/run/bare_specifier_without_import/__test__.jsonc b/tests/specs/run/bare_specifier_without_import/__test__.jsonc new file mode 100644 index 0000000000..7b5c5e1b67 --- /dev/null +++ b/tests/specs/run/bare_specifier_without_import/__test__.jsonc @@ -0,0 +1,5 @@ +{ + "args": "run main.ts", + "output": "main.out", + "exitCode": 1 +} diff --git a/tests/specs/run/bare_specifier_without_import/main.out b/tests/specs/run/bare_specifier_without_import/main.out new file mode 100644 index 0000000000..a873f2727c --- /dev/null +++ b/tests/specs/run/bare_specifier_without_import/main.out @@ -0,0 +1,3 @@ +error: Relative import path "@std/dotenv/load" not prefixed with / or ./ or ../ + hint: If you want to use a JSR or npm package, try running `deno add @std/dotenv/load` + at [WILDCARD]bare_specifier_without_import/main.ts:1:8 diff --git a/tests/specs/run/bare_specifier_without_import/main.ts b/tests/specs/run/bare_specifier_without_import/main.ts new file mode 100644 index 0000000000..67e57b26b3 --- /dev/null +++ b/tests/specs/run/bare_specifier_without_import/main.ts @@ -0,0 +1,3 @@ +import "@std/dotenv/load"; + +console.log(Deno.env.get("GREETING")); // hello world diff --git a/tests/testdata/run/error_type_definitions.ts.out b/tests/testdata/run/error_type_definitions.ts.out index d60d4d483c..7a71b4ebe8 100644 --- a/tests/testdata/run/error_type_definitions.ts.out +++ b/tests/testdata/run/error_type_definitions.ts.out @@ -1,2 +1,3 @@ [WILDCARD]error: Failed resolving types. Relative import path "baz" not prefixed with / or ./ or ../ + hint: If you want to use a JSR or npm package, try running `deno add baz` at [WILDCARD]/type_definitions/bar.d.ts:[WILDCARD] diff --git a/tests/testdata/run/node_prefix_missing/main.ts.out b/tests/testdata/run/node_prefix_missing/main.ts.out index fd19ed55f7..48b4e37e27 100644 --- a/tests/testdata/run/node_prefix_missing/main.ts.out +++ b/tests/testdata/run/node_prefix_missing/main.ts.out @@ -1,3 +1,3 @@ error: Relative import path "fs" not prefixed with / or ./ or ../ -If you want to use a built-in Node module, add a "node:" prefix (ex. "node:fs"). + hint: If you want to use a built-in Node module, add a "node:" prefix (ex. "node:fs"). at file:///[WILDCARD]/main.ts:1:16 diff --git a/tests/testdata/run/with_package_json/with_stop/main.out b/tests/testdata/run/with_package_json/with_stop/main.out index f5eb79ca6d..afab4910c6 100644 --- a/tests/testdata/run/with_package_json/with_stop/main.out +++ b/tests/testdata/run/with_package_json/with_stop/main.out @@ -1,4 +1,5 @@ [WILDCARD]Config file found at '[WILDCARD]with_package_json[WILDCARD]with_stop[WILDCARD]some[WILDCARD]nested[WILDCARD]deno.json' [WILDCARD] error: Relative import path "chalk" not prefixed with / or ./ or ../ + hint: If you want to use a JSR or npm package, try running `deno add chalk` at file:///[WILDCARD]with_package_json/with_stop/some/nested/dir/main.ts:3:19 From 17b5e98b822dc23407a0292dcf61e624fbf2a4b1 Mon Sep 17 00:00:00 2001 From: Ian Bull Date: Wed, 4 Sep 2024 23:27:58 -0700 Subject: [PATCH 43/45] refactor(ext/cron): align error messages (#25300) Aligns the error messages in the cron extension to be in-line with the Deno style guide. https://github.com/denoland/deno/issues/25269 --- ext/cron/01_cron.ts | 18 +++++++++++++----- ext/cron/lib.rs | 7 +++++-- tests/unit/cron_test.ts | 10 +++++----- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/ext/cron/01_cron.ts b/ext/cron/01_cron.ts index 017da8ae2f..b5c556a677 100644 --- a/ext/cron/01_cron.ts +++ b/ext/cron/01_cron.ts @@ -41,7 +41,9 @@ export function formatToCronSchedule( } else if (end === undefined && every !== undefined) { return "*/" + every; } else { - throw new TypeError("Invalid cron schedule"); + throw new TypeError( + `Invalid cron schedule: start=${start}, end=${end}, every=${every}`, + ); } } else { if (typeof exact === "number") { @@ -103,10 +105,14 @@ function cron( handler2?: () => Promise | void, ) { if (name === undefined) { - throw new TypeError("Deno.cron requires a unique name"); + throw new TypeError( + "Cannot create cron job, a unique name is required: received 'undefined'", + ); } if (schedule === undefined) { - throw new TypeError("Deno.cron requires a valid schedule"); + throw new TypeError( + "Cannot create cron job, a schedule is required: received 'undefined'", + ); } schedule = parseScheduleToString(schedule); @@ -119,13 +125,15 @@ function cron( if (typeof handlerOrOptions1 === "function") { handler = handlerOrOptions1; if (handler2 !== undefined) { - throw new TypeError("Deno.cron requires a single handler"); + throw new TypeError( + "Cannot create cron job, a single handler is required: two handlers were specified", + ); } } else if (typeof handler2 === "function") { handler = handler2; options = handlerOrOptions1; } else { - throw new TypeError("Deno.cron requires a handler"); + throw new TypeError("Cannot create cron job: a handler is required"); } const rid = op_cron_create( diff --git a/ext/cron/lib.rs b/ext/cron/lib.rs index ede01ba45f..95e38179db 100644 --- a/ext/cron/lib.rs +++ b/ext/cron/lib.rs @@ -116,12 +116,15 @@ where fn validate_cron_name(name: &str) -> Result<(), AnyError> { if name.len() > 64 { - return Err(type_error("Cron name is too long")); + return Err(type_error(format!( + "Cron name cannot exceed 64 characters: current length {}", + name.len() + ))); } if !name.chars().all(|c| { c.is_ascii_whitespace() || c.is_ascii_alphanumeric() || c == '_' || c == '-' }) { - return Err(type_error("Invalid cron name. Only alphanumeric characters, whitespace, hyphens, and underscores are allowed")); + return Err(type_error("Invalid cron name: only alphanumeric characters, whitespace, hyphens, and underscores are allowed")); } Ok(()) } diff --git a/tests/unit/cron_test.ts b/tests/unit/cron_test.ts index 02573a898d..5f14f6c784 100644 --- a/tests/unit/cron_test.ts +++ b/tests/unit/cron_test.ts @@ -15,7 +15,7 @@ Deno.test(function noNameTest() { // @ts-ignore test () => Deno.cron(), TypeError, - "Deno.cron requires a unique name", + "Cannot create cron job, a unique name is required: received 'undefined'", ); }); @@ -24,7 +24,7 @@ Deno.test(function noSchedule() { // @ts-ignore test () => Deno.cron("foo"), TypeError, - "Deno.cron requires a valid schedule", + "Cannot create cron job, a schedule is required: received 'undefined'", ); }); @@ -33,7 +33,7 @@ Deno.test(function noHandler() { // @ts-ignore test () => Deno.cron("foo", "*/1 * * * *"), TypeError, - "Deno.cron requires a handler", + "Cannot create cron job: a handler is required", ); }); @@ -66,7 +66,7 @@ Deno.test(function invalidNameTest() { () => {}, ), TypeError, - "Cron name is too long", + "Cron name cannot exceed 64 characters: current length 70", ); }); @@ -388,7 +388,7 @@ Deno.test("error on two handlers", () => { Deno.cron("abc", "* * * * *", () => {}, () => {}); }, TypeError, - "Deno.cron requires a single handler", + "Cannot create cron job, a single handler is required: two handlers were specified", ); }); From 49e3ee010c7d4423fbab89bf12749235e156c9be Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Thu, 5 Sep 2024 09:22:52 +0200 Subject: [PATCH 44/45] feat(ext/node): add abort helpers, process & streams fix (#25262) This commit adds: - `addAbortListener` in `node:events` - `aborted` in `node:util` - `execPath` and `execvArgs` named export from `node:process` - `getDefaultHighWaterMark` from `node:stream` The `execPath` is very hacky - because module namespaces can not have real getters, `execPath` is an object with a `toString()` method that on call returns the actual `execPath`, and replaces the `execPath` binding with the string. This is done so that we don't require the `execPath` permission on startup. --- ext/node/lib.rs | 1 + ext/node/polyfills/_events.mjs | 2 + ext/node/polyfills/events.ts | 1 + .../internal/events/abort_listener.mjs | 44 +++++++++++++++++++ ext/node/polyfills/process.ts | 25 ++++++++--- ext/node/polyfills/stream.ts | 4 ++ ext/node/polyfills/util.ts | 32 +++++++++++++- tests/unit_node/events_test.ts | 13 +++++- tests/unit_node/process_test.ts | 10 +++++ tests/unit_node/stream_test.ts | 9 +++- tests/unit_node/util_test.ts | 18 ++++++++ 11 files changed, 148 insertions(+), 11 deletions(-) create mode 100644 ext/node/polyfills/internal/events/abort_listener.mjs diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 17fd7ab5a6..39b06e9fdd 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -502,6 +502,7 @@ deno_core::extension!(deno_node, "internal/error_codes.ts", "internal/errors.ts", "internal/event_target.mjs", + "internal/events/abort_listener.mjs", "internal/fixed_queue.ts", "internal/fs/streams.mjs", "internal/fs/utils.mjs", diff --git a/ext/node/polyfills/_events.mjs b/ext/node/polyfills/_events.mjs index 12b0935e61..ce7a8ebf24 100644 --- a/ext/node/polyfills/_events.mjs +++ b/ext/node/polyfills/_events.mjs @@ -47,6 +47,8 @@ import { import { spliceOne } from "ext:deno_node/_utils.ts"; import { nextTick } from "ext:deno_node/_process/process.ts"; +export { addAbortListener } from "./internal/events/abort_listener.mjs"; + const kCapture = Symbol("kCapture"); const kErrorMonitor = Symbol("events.errorMonitor"); const kMaxEventTargetListeners = Symbol("events.maxEventTargetListeners"); diff --git a/ext/node/polyfills/events.ts b/ext/node/polyfills/events.ts index 3b73814109..78f3d8768c 100644 --- a/ext/node/polyfills/events.ts +++ b/ext/node/polyfills/events.ts @@ -1,6 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // @deno-types="./_events.d.ts" export { + addAbortListener, captureRejectionSymbol, default, defaultMaxListeners, diff --git a/ext/node/polyfills/internal/events/abort_listener.mjs b/ext/node/polyfills/internal/events/abort_listener.mjs new file mode 100644 index 0000000000..f1430489fa --- /dev/null +++ b/ext/node/polyfills/internal/events/abort_listener.mjs @@ -0,0 +1,44 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { primordials } from "ext:deno_node/internal/test/binding.ts"; +const { queueMicrotask } = primordials; +import { SymbolDispose } from "ext:deno_web/00_infra.js"; +import * as abortSignal from "ext:deno_web/03_abort_signal.js"; +import { validateAbortSignal, validateFunction } from "../validators.mjs"; +import { codes } from "../errors.ts"; +const { ERR_INVALID_ARG_TYPE } = codes; + +/** + * @param {AbortSignal} signal + * @param {EventListener} listener + * @returns {Disposable} + */ +function addAbortListener(signal, listener) { + if (signal === undefined) { + throw new ERR_INVALID_ARG_TYPE("signal", "AbortSignal", signal); + } + validateAbortSignal(signal, "signal"); + validateFunction(listener, "listener"); + + let removeEventListener; + if (signal.aborted) { + queueMicrotask(() => listener()); + } else { + signal[abortSignal.add](() => { + removeEventListener?.(); + listener(); + }); + removeEventListener = () => { + signal[abortSignal.remove](listener); + }; + } + return { + __proto__: null, + [SymbolDispose]() { + removeEventListener?.(); + }, + }; +} + +export { addAbortListener }; diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts index 2130087fb6..f09d7ceede 100644 --- a/ext/node/polyfills/process.ts +++ b/ext/node/polyfills/process.ts @@ -82,6 +82,8 @@ export const argv: string[] = ["", ""]; // And retains any value as long as it's nullish or number-ish. let ProcessExitCode: undefined | null | string | number; +export const execArgv: string[] = []; + /** https://nodejs.org/api/process.html#process_process_exit_code */ export const exit = (code?: number | string) => { if (code || code === 0) { @@ -337,7 +339,20 @@ function uncaughtExceptionHandler(err: any, origin: string) { process.emit("uncaughtException", err, origin); } -let execPath: string | null = null; +export let execPath: string = Object.freeze({ + __proto__: String.prototype, + toString() { + execPath = Deno.execPath(); + return execPath; + }, + get length() { + return this.toString().length; + }, + [Symbol.for("Deno.customInspect")](inspect, options) { + return inspect(this.toString(), options); + }, + // deno-lint-ignore no-explicit-any +}) as any as string; // The process class needs to be an ES5 class because it can be instantiated // in Node without the `new` keyword. It's not a true class in Node. Popular @@ -425,7 +440,7 @@ Process.prototype.cwd = cwd; Process.prototype.env = env; /** https://nodejs.org/api/process.html#process_process_execargv */ -Process.prototype.execArgv = []; +Process.prototype.execArgv = execArgv; /** https://nodejs.org/api/process.html#process_process_exit_code */ Process.prototype.exit = exit; @@ -704,11 +719,7 @@ Process.prototype._eval = undefined; Object.defineProperty(Process.prototype, "execPath", { get() { - if (execPath) { - return execPath; - } - execPath = Deno.execPath(); - return execPath; + return String(execPath); }, set(path: string) { execPath = path; diff --git a/ext/node/polyfills/stream.ts b/ext/node/polyfills/stream.ts index 42f83fa289..618e5ba113 100644 --- a/ext/node/polyfills/stream.ts +++ b/ext/node/polyfills/stream.ts @@ -19,6 +19,9 @@ import { Transform, Writable, } from "ext:deno_node/_stream.mjs"; +import { + getDefaultHighWaterMark, +} from "ext:deno_node/internal/streams/state.mjs"; export { _isUint8Array, @@ -26,6 +29,7 @@ export { addAbortSignal, Duplex, finished, + getDefaultHighWaterMark, PassThrough, pipeline, Readable, diff --git a/ext/node/polyfills/util.ts b/ext/node/polyfills/util.ts index cb4e6498a4..c94d0f14b5 100644 --- a/ext/node/polyfills/util.ts +++ b/ext/node/polyfills/util.ts @@ -25,9 +25,13 @@ const { StringPrototypeIsWellFormed, StringPrototypePadStart, StringPrototypeToWellFormed, + PromiseResolve, } = primordials; -import { promisify } from "ext:deno_node/internal/util.mjs"; +import { + createDeferredPromise, + promisify, +} from "ext:deno_node/internal/util.mjs"; import { callbackify } from "ext:deno_node/_util/_util_callbackify.js"; import { debuglog } from "ext:deno_node/internal/util/debuglog.ts"; import { @@ -41,8 +45,13 @@ import types from "node:util/types"; import { Buffer } from "node:buffer"; import { isDeepStrictEqual } from "ext:deno_node/internal/util/comparisons.ts"; import process from "node:process"; -import { validateString } from "ext:deno_node/internal/validators.mjs"; +import { + validateAbortSignal, + validateString, +} from "ext:deno_node/internal/validators.mjs"; import { parseArgs } from "ext:deno_node/internal/util/parse_args/parse_args.js"; +import * as abortSignal from "ext:deno_web/03_abort_signal.js"; +import { ERR_INVALID_ARG_TYPE } from "ext:deno_node/internal/errors.ts"; export { callbackify, @@ -288,6 +297,24 @@ export function deprecate(fn: any, msg: string, code?: any) { return deprecated; } +// deno-lint-ignore require-await +export async function aborted( + signal: AbortSignal, + // deno-lint-ignore no-explicit-any + _resource: any, +): Promise { + if (signal === undefined) { + throw new ERR_INVALID_ARG_TYPE("signal", "AbortSignal", signal); + } + validateAbortSignal(signal, "signal"); + if (signal.aborted) { + return PromiseResolve(); + } + const abortPromise = createDeferredPromise(); + signal[abortSignal.add](abortPromise.resolve); + return abortPromise.promise; +} + export { getSystemErrorName, isDeepStrictEqual }; export default { @@ -311,6 +338,7 @@ export default { isBuffer, _extend, getSystemErrorName, + aborted, deprecate, callbackify, parseArgs, diff --git a/tests/unit_node/events_test.ts b/tests/unit_node/events_test.ts index 1fc7ad1e37..82808d5238 100644 --- a/tests/unit_node/events_test.ts +++ b/tests/unit_node/events_test.ts @@ -1,6 +1,7 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import events, { EventEmitter } from "node:events"; +// @ts-expect-error: @types/node is outdated +import events, { addAbortListener, EventEmitter } from "node:events"; EventEmitter.captureRejections = true; @@ -34,3 +35,13 @@ Deno.test("eventemitter async resource", () => { // @ts-ignore: @types/node is outdated foo.emit("bar"); }); + +Deno.test("addAbortListener", async () => { + const { promise, resolve } = Promise.withResolvers(); + const abortController = new AbortController(); + addAbortListener(abortController.signal, () => { + resolve(); + }); + abortController.abort(); + await promise; +}); diff --git a/tests/unit_node/process_test.ts b/tests/unit_node/process_test.ts index a647b0369e..962877935b 100644 --- a/tests/unit_node/process_test.ts +++ b/tests/unit_node/process_test.ts @@ -7,6 +7,8 @@ import process, { argv, argv0 as importedArgv0, env, + execArgv as importedExecArgv, + execPath as importedExecPath, geteuid, pid as importedPid, platform as importedPlatform, @@ -1121,3 +1123,11 @@ Deno.test("process.listeners - include SIG* events", () => { Deno.test(function processVersionsOwnProperty() { assert(Object.prototype.hasOwnProperty.call(process, "versions")); }); + +Deno.test(function importedExecArgvTest() { + assert(Array.isArray(importedExecArgv)); +}); + +Deno.test(function importedExecPathTest() { + assertEquals(importedExecPath, Deno.execPath()); +}); diff --git a/tests/unit_node/stream_test.ts b/tests/unit_node/stream_test.ts index a54f8b421b..8d49701464 100644 --- a/tests/unit_node/stream_test.ts +++ b/tests/unit_node/stream_test.ts @@ -1,8 +1,10 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -import { assert } from "@std/assert"; +import { assert, assertEquals } from "@std/assert"; import { fromFileUrl, relative } from "@std/path"; import { pipeline } from "node:stream/promises"; +// @ts-expect-error: @types/node is outdated +import { getDefaultHighWaterMark } from "node:stream"; import { createReadStream, createWriteStream } from "node:fs"; Deno.test("stream/promises pipeline", async () => { @@ -23,3 +25,8 @@ Deno.test("stream/promises pipeline", async () => { // pass } }); + +Deno.test("stream getDefaultHighWaterMark", () => { + assertEquals(getDefaultHighWaterMark(false), 16 * 1024); + assertEquals(getDefaultHighWaterMark(true), 16); +}); diff --git a/tests/unit_node/util_test.ts b/tests/unit_node/util_test.ts index 2b639538cb..edd5002623 100644 --- a/tests/unit_node/util_test.ts +++ b/tests/unit_node/util_test.ts @@ -330,3 +330,21 @@ Deno.test("[util] debuglog() and debug()", () => { assertEquals(util.debuglog, util.debug); assertEquals(utilDefault.debuglog, utilDefault.debug); }); + +Deno.test("[util] aborted()", async () => { + const abortController = new AbortController(); + let done = false; + const promise = util.aborted( + // deno-lint-ignore no-explicit-any + abortController.signal as any, + abortController.signal, + ); + promise.then(() => { + done = true; + }); + await new Promise((r) => setTimeout(r, 100)); + assertEquals(done, false); + abortController.abort(); + await promise; + assertEquals(done, true); +}); From 4554ab6aefb158f2d0a8a5ae5bb3d748e123fb00 Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Thu, 5 Sep 2024 09:50:04 +0200 Subject: [PATCH 45/45] fix(config): validate export names (#25436) The property names of the `exports` field in `deno.json` was never validated. The `patternProperties` only validates values, whose property name matches the regex. It doesn't validate the property names themselves. That's what `propertyNames` is for. Related https://github.com/denoland/deno/issues/25435 --- cli/schemas/config-file.v1.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cli/schemas/config-file.v1.json b/cli/schemas/config-file.v1.json index cc2c1eb6ff..df8177af29 100644 --- a/cli/schemas/config-file.v1.json +++ b/cli/schemas/config-file.v1.json @@ -622,6 +622,11 @@ { "type": "object", "description": "A map of package exports to files in this JSR package.", + "propertyNames": { + "description": "Package export name", + "examples": [".", "./foo", "./bar"], + "pattern": "^\\.(/.*)?$" + }, "patternProperties": { "^\\.(/.*)?$": { "type": "string",