mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 04:52:26 -05:00
Merge remote-tracking branch 'upstream/main' into update-imagebitmap
This commit is contained in:
commit
dbac50db8f
864 changed files with 8469 additions and 15627 deletions
|
@ -65,7 +65,7 @@
|
|||
"third_party"
|
||||
],
|
||||
"plugins": [
|
||||
"https://plugins.dprint.dev/typescript-0.91.6.wasm",
|
||||
"https://plugins.dprint.dev/typescript-0.91.7.wasm",
|
||||
"https://plugins.dprint.dev/json-0.19.3.wasm",
|
||||
"https://plugins.dprint.dev/markdown-0.17.8.wasm",
|
||||
"https://plugins.dprint.dev/toml-0.6.2.wasm",
|
||||
|
|
2
.github/workflows/ci.generate.ts
vendored
2
.github/workflows/ci.generate.ts
vendored
|
@ -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 = 13;
|
||||
const cacheVersion = 15;
|
||||
|
||||
const ubuntuX86Runner = "ubuntu-22.04";
|
||||
const ubuntuX86XlRunner = "ubuntu-22.04-xl";
|
||||
|
|
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
|
@ -367,8 +367,8 @@ jobs:
|
|||
path: |-
|
||||
~/.cargo/registry/index
|
||||
~/.cargo/registry/cache
|
||||
key: '13-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
|
||||
restore-keys: '13-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: '13-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: '13-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
|
||||
|
|
508
Cargo.lock
generated
508
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
74
Cargo.toml
74
Cargo.toml
|
@ -44,47 +44,48 @@ license = "MIT"
|
|||
repository = "https://github.com/denoland/deno"
|
||||
|
||||
[workspace.dependencies]
|
||||
deno_ast = { version = "=0.41.2", features = ["transpiling"] }
|
||||
deno_core = { version = "0.306.0" }
|
||||
deno_ast = { version = "=0.42.0", features = ["transpiling"] }
|
||||
deno_core = { version = "0.307.0" }
|
||||
|
||||
deno_bench_util = { version = "0.160.0", path = "./bench_util" }
|
||||
deno_lockfile = "=0.22.0"
|
||||
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.26.0", path = "./runtime/permissions" }
|
||||
deno_runtime = { version = "0.175.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.96.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"
|
||||
denokv_remote = "0.8.1"
|
||||
# denokv_sqlite brings in bundled sqlite if we don't disable the default features
|
||||
denokv_sqlite = { default-features = false, version = "0.8.1" }
|
||||
denokv_sqlite = { default-features = false, version = "0.8.2" }
|
||||
|
||||
# exts
|
||||
deno_broadcast_channel = { version = "0.160.0", path = "./ext/broadcast_channel" }
|
||||
deno_cache = { version = "0.98.0", path = "./ext/cache" }
|
||||
deno_canvas = { version = "0.35.0", path = "./ext/canvas" }
|
||||
deno_console = { version = "0.166.0", path = "./ext/console" }
|
||||
deno_cron = { version = "0.46.0", path = "./ext/cron" }
|
||||
deno_crypto = { version = "0.180.0", path = "./ext/crypto" }
|
||||
deno_fetch = { version = "0.190.0", path = "./ext/fetch" }
|
||||
deno_ffi = { version = "0.153.0", path = "./ext/ffi" }
|
||||
deno_fs = { version = "0.76.0", path = "./ext/fs" }
|
||||
deno_http = { version = "0.164.0", path = "./ext/http" }
|
||||
deno_io = { version = "0.76.0", path = "./ext/io" }
|
||||
deno_kv = { version = "0.74.0", path = "./ext/kv" }
|
||||
deno_napi = { version = "0.97.0", path = "./ext/napi" }
|
||||
deno_net = { version = "0.158.0", path = "./ext/net" }
|
||||
deno_node = { version = "0.103.0", path = "./ext/node" }
|
||||
deno_tls = { version = "0.153.0", path = "./ext/tls" }
|
||||
deno_url = { version = "0.166.0", path = "./ext/url" }
|
||||
deno_web = { version = "0.197.0", path = "./ext/web" }
|
||||
deno_webgpu = { version = "0.133.0", path = "./ext/webgpu" }
|
||||
deno_webidl = { version = "0.166.0", path = "./ext/webidl" }
|
||||
deno_websocket = { version = "0.171.0", path = "./ext/websocket" }
|
||||
deno_webstorage = { version = "0.161.0", path = "./ext/webstorage" }
|
||||
node_resolver = { version = "0.5.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"
|
||||
|
@ -102,11 +103,11 @@ chrono = { version = "0.4", default-features = false, features = ["std", "serde"
|
|||
console_static_text = "=0.8.1"
|
||||
data-encoding = "2.3.3"
|
||||
data-url = "=0.3.0"
|
||||
deno_cache_dir = "=0.11.0"
|
||||
deno_cache_dir = "=0.11.1"
|
||||
deno_package_json = { version = "=0.1.1", default-features = false }
|
||||
dlopen2 = "0.6.1"
|
||||
ecb = "=0.1.2"
|
||||
elliptic-curve = { version = "0.13.4", features = ["alloc", "arithmetic", "ecdh", "std", "pem"] }
|
||||
elliptic-curve = { version = "0.13.4", features = ["alloc", "arithmetic", "ecdh", "std", "pem", "jwk"] }
|
||||
encoding_rs = "=0.8.33"
|
||||
fast-socks5 = "0.9.6"
|
||||
faster-hex = "0.9"
|
||||
|
@ -141,8 +142,8 @@ num-bigint = { version = "0.4", features = ["rand"] }
|
|||
once_cell = "1.17.1"
|
||||
os_pipe = { version = "=1.1.5", features = ["io_safety"] }
|
||||
p224 = { version = "0.13.0", features = ["ecdh"] }
|
||||
p256 = { version = "0.13.2", features = ["ecdh"] }
|
||||
p384 = { version = "0.13.0", features = ["ecdh"] }
|
||||
p256 = { version = "0.13.2", features = ["ecdh", "jwk"] }
|
||||
p384 = { version = "0.13.0", features = ["ecdh", "jwk"] }
|
||||
parking_lot = "0.12.0"
|
||||
percent-encoding = "2.3.0"
|
||||
phf = { version = "0.11", features = ["macros"] }
|
||||
|
@ -154,7 +155,7 @@ rand = "=0.8.5"
|
|||
regex = "^1.7.0"
|
||||
reqwest = { version = "=0.12.5", default-features = false, features = ["rustls-tls", "stream", "gzip", "brotli", "socks", "json", "http2"] } # pinned because of https://github.com/seanmonstar/reqwest/pull/1955
|
||||
ring = "^0.17.0"
|
||||
rusqlite = { version = "=0.29.0", features = ["unlock_notify", "bundled"] }
|
||||
rusqlite = { version = "0.32.0", features = ["unlock_notify", "bundled"] }
|
||||
rustls = { version = "0.23.11", default-features = false, features = ["logging", "std", "tls12", "ring"] }
|
||||
rustls-pemfile = "2"
|
||||
rustls-tokio-stream = "=0.3.0"
|
||||
|
@ -192,6 +193,7 @@ url = { version = "< 2.5.0", features = ["serde", "expose_internals"] }
|
|||
uuid = { version = "1.3.0", features = ["v4"] }
|
||||
webpki-roots = "0.26"
|
||||
which = "4.2.5"
|
||||
yoke = { version = "0.7.4", features = ["derive"] }
|
||||
zeromq = { version = "=0.4.0", default-features = false, features = ["tcp-transport", "tokio-runtime"] }
|
||||
zstd = "=0.12.4"
|
||||
|
||||
|
|
44
Releases.md
44
Releases.md
|
@ -6,6 +6,50 @@ 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)
|
||||
- fix(bench): Fix table column alignments and NO_COLOR=1 (#25190)
|
||||
- fix(ext/crypto): throw DataError for invalid EC key import (#25181)
|
||||
- fix(ext/fetch): percent decode userinfo when parsing proxies (#25229)
|
||||
- fix(ext/node): emit `online` event after worker thread is initialized (#25243)
|
||||
- fix(ext/node): export JWK public key (#25239)
|
||||
- fix(ext/node): import EC JWK keys (#25266)
|
||||
- fix(ext/node): import JWK octet key pairs (#25180)
|
||||
- fix(ext/node): import RSA JWK keys (#25267)
|
||||
- fix(ext/node): throw when loading `cpu-features` module (#25257)
|
||||
- fix(ext/node): update aead-gcm-stream to 0.3 (#25261)
|
||||
- fix(ext/webgpu): allow to build on unsupported platforms (#25202)
|
||||
- fix(fmt): fix incorrect quotes in components (#25249)
|
||||
- fix(fmt/markdown): fix regression with multi-line footnotes and inline math
|
||||
(#25222)
|
||||
- fix(install): Use relative symlinks in deno install (#25164)
|
||||
- fix(lsp): panic on url_to_uri() (#25238)
|
||||
- fix(napi): Don't run microtasks in napi_resolve_deferred (#25246)
|
||||
- fix(napi): Fix worker threads importing already-loaded NAPI addon (#25245)
|
||||
- fix(node/cluster): improve stubs to make log4js work (#25146)
|
||||
- fix(runtime/web_worker): populate `SnapshotOptions` for `WebWorker` when
|
||||
instantiated without snapshot (#25280)
|
||||
- fix(task): support tasks with colons in name in `deno run` (#25233)
|
||||
- fix: handle showing warnings while the progress bar is shown (#25187)
|
||||
- fix: reland async context (#25140)
|
||||
- fix: removed unstable-htttp from deno help (#25216)
|
||||
- fix: replace `npm install` hint with `deno install` hint (#25244)
|
||||
- fix: update deno_doc (#25290)
|
||||
- fix: upgrade deno_core to 0.307.0 (#25287)
|
||||
- perf(ext/node): reduce some allocations in require (#25197)
|
||||
|
||||
### 1.46.1 / 2024.08.22
|
||||
|
||||
- fix(ext/node): http2session ready state (#25143)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_bench_util"
|
||||
version = "0.160.0"
|
||||
version = "0.162.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno"
|
||||
version = "1.46.1"
|
||||
version = "2.0.0-rc.1"
|
||||
authors.workspace = true
|
||||
default-run = "deno"
|
||||
edition.workspace = true
|
||||
|
@ -65,20 +65,19 @@ 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.30.1", features = ["workspace", "sync"] }
|
||||
deno_config = { version = "=0.33.2", features = ["workspace", "sync"] }
|
||||
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
|
||||
deno_doc = { version = "0.146.0", features = ["html", "syntect"] }
|
||||
deno_emit = "=0.44.0"
|
||||
deno_graph = { version = "=0.81.3" }
|
||||
deno_lint = { version = "=0.63.1", features = ["docs"] }
|
||||
deno_doc = { version = "0.148.0", features = ["html", "syntect"] }
|
||||
deno_graph = { version = "=0.82.1" }
|
||||
deno_lint = { version = "=0.65.0", features = ["docs"] }
|
||||
deno_lockfile.workspace = true
|
||||
deno_npm = "=0.24.0"
|
||||
deno_npm = "=0.25.0"
|
||||
deno_package_json.workspace = true
|
||||
deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] }
|
||||
deno_semver = "=0.5.10"
|
||||
deno_semver.workspace = true
|
||||
deno_task_shell = "=0.17.0"
|
||||
deno_terminal.workspace = true
|
||||
eszip = "=0.76.0"
|
||||
eszip = "=0.78.0"
|
||||
libsui = "0.3.0"
|
||||
napi_sym.workspace = true
|
||||
node_resolver.workspace = true
|
||||
|
@ -91,8 +90,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
|
||||
|
@ -103,7 +102,7 @@ dotenvy = "0.15.7"
|
|||
dprint-plugin-json = "=0.19.3"
|
||||
dprint-plugin-jupyter = "=0.1.3"
|
||||
dprint-plugin-markdown = "=0.17.8"
|
||||
dprint-plugin-typescript = "=0.91.6"
|
||||
dprint-plugin-typescript = "=0.91.7"
|
||||
env_logger = "=0.10.0"
|
||||
fancy-regex = "=0.10.0"
|
||||
faster-hex.workspace = true
|
||||
|
@ -115,7 +114,7 @@ http.workspace = true
|
|||
http-body.workspace = true
|
||||
http-body-util.workspace = true
|
||||
hyper-util.workspace = true
|
||||
import_map = { version = "=0.20.0", features = ["ext"] }
|
||||
import_map = { version = "=0.20.1", features = ["ext"] }
|
||||
indexmap.workspace = true
|
||||
jsonc-parser.workspace = true
|
||||
jupyter_runtime = { package = "runtimelib", version = "=0.14.0" }
|
||||
|
@ -162,6 +161,7 @@ typed-arena = "=2.0.2"
|
|||
uuid = { workspace = true, features = ["serde"] }
|
||||
walkdir = "=2.3.2"
|
||||
which.workspace = true
|
||||
yoke.workspace = true
|
||||
zeromq.workspace = true
|
||||
zip = { version = "2.1.6", default-features = false, features = ["deflate-flate2"] }
|
||||
zstd.workspace = true
|
||||
|
|
|
@ -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"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
2803
cli/args/flags.rs
2803
cli/args/flags.rs
File diff suppressed because it is too large
Load diff
|
@ -50,7 +50,7 @@ pub fn parse(paths: Vec<String>) -> clap::error::Result<Vec<String>> {
|
|||
out.push(format!("{}:{}", host, port.0));
|
||||
}
|
||||
} else {
|
||||
host_and_port.parse::<NetDescriptor>().map_err(|e| {
|
||||
NetDescriptor::parse(&host_and_port).map_err(|e| {
|
||||
clap::Error::raw(clap::error::ErrorKind::InvalidValue, format!("{e:?}"))
|
||||
})?;
|
||||
out.push(host_and_port)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use deno_config::deno_json::ConfigFile;
|
||||
|
@ -12,6 +12,7 @@ use deno_core::parking_lot::MutexGuard;
|
|||
use deno_lockfile::WorkspaceMemberConfig;
|
||||
use deno_package_json::PackageJsonDepValue;
|
||||
use deno_runtime::deno_node::PackageJson;
|
||||
use deno_semver::jsr::JsrDepPackageReq;
|
||||
|
||||
use crate::cache;
|
||||
use crate::util::fs::atomic_write_file_with_retries;
|
||||
|
@ -98,7 +99,9 @@ impl CliLockfile {
|
|||
flags: &Flags,
|
||||
workspace: &Workspace,
|
||||
) -> Result<Option<CliLockfile>, AnyError> {
|
||||
fn pkg_json_deps(maybe_pkg_json: Option<&PackageJson>) -> BTreeSet<String> {
|
||||
fn pkg_json_deps(
|
||||
maybe_pkg_json: Option<&PackageJson>,
|
||||
) -> HashSet<JsrDepPackageReq> {
|
||||
let Some(pkg_json) = maybe_pkg_json else {
|
||||
return Default::default();
|
||||
};
|
||||
|
@ -107,21 +110,21 @@ impl CliLockfile {
|
|||
.values()
|
||||
.filter_map(|dep| dep.as_ref().ok())
|
||||
.filter_map(|dep| match dep {
|
||||
PackageJsonDepValue::Req(req) => Some(req),
|
||||
PackageJsonDepValue::Req(req) => {
|
||||
Some(JsrDepPackageReq::npm(req.clone()))
|
||||
}
|
||||
PackageJsonDepValue::Workspace(_) => None,
|
||||
})
|
||||
.map(|r| format!("npm:{}", r))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn deno_json_deps(
|
||||
maybe_deno_json: Option<&ConfigFile>,
|
||||
) -> BTreeSet<String> {
|
||||
) -> HashSet<JsrDepPackageReq> {
|
||||
maybe_deno_json
|
||||
.map(|c| {
|
||||
crate::args::deno_json::deno_json_deps(c)
|
||||
.into_iter()
|
||||
.map(|req| req.to_string())
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
|
@ -157,15 +160,7 @@ impl CliLockfile {
|
|||
.unwrap_or(false)
|
||||
});
|
||||
|
||||
let lockfile = if flags.lock_write {
|
||||
log::warn!(
|
||||
"{} \"--lock-write\" flag is deprecated and will be removed in Deno 2.",
|
||||
crate::colors::yellow("Warning")
|
||||
);
|
||||
CliLockfile::new(Lockfile::new_empty(filename, true), frozen)
|
||||
} else {
|
||||
Self::read_from_path(filename, frozen)?
|
||||
};
|
||||
let lockfile = Self::read_from_path(filename, frozen)?;
|
||||
|
||||
// initialize the lockfile with the workspace's configuration
|
||||
let root_url = workspace.root_dir();
|
||||
|
@ -215,6 +210,7 @@ impl CliLockfile {
|
|||
|
||||
Ok(Some(lockfile))
|
||||
}
|
||||
|
||||
pub fn read_from_path(
|
||||
file_path: PathBuf,
|
||||
frozen: bool,
|
||||
|
@ -243,12 +239,6 @@ impl CliLockfile {
|
|||
}
|
||||
let lockfile = self.lockfile.lock();
|
||||
if lockfile.has_content_changed {
|
||||
let suggested = if *super::DENO_FUTURE {
|
||||
"`deno cache --frozen=false`, `deno install --frozen=false`,"
|
||||
} else {
|
||||
"`deno cache --frozen=false`"
|
||||
};
|
||||
|
||||
let contents =
|
||||
std::fs::read_to_string(&lockfile.filename).unwrap_or_default();
|
||||
let new_contents = lockfile.as_json_string();
|
||||
|
@ -256,7 +246,7 @@ impl CliLockfile {
|
|||
// has an extra newline at the end
|
||||
let diff = diff.trim_end();
|
||||
Err(deno_core::anyhow::anyhow!(
|
||||
"The lockfile is out of date. Run {suggested} or rerun with `--frozen=false` to update it.\nchanges:\n{diff}"
|
||||
"The lockfile is out of date. Run `deno cache --frozen=false`, `deno install --frozen=false`, or rerun with `--frozen=false` to update it.\nchanges:\n{diff}"
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
|
|
196
cli/args/mod.rs
196
cli/args/mod.rs
|
@ -8,7 +8,9 @@ mod lockfile;
|
|||
mod package_json;
|
||||
|
||||
use deno_ast::SourceMapOption;
|
||||
use deno_config::deno_json::NodeModulesDirMode;
|
||||
use deno_config::workspace::CreateResolverOptions;
|
||||
use deno_config::workspace::FolderConfigs;
|
||||
use deno_config::workspace::PackageJsonDepResolution;
|
||||
use deno_config::workspace::VendorEnablement;
|
||||
use deno_config::workspace::Workspace;
|
||||
|
@ -40,9 +42,10 @@ 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::PackageJsonInstallDepsProvider;
|
||||
pub use package_json::NpmInstallDepsProvider;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::anyhow::bail;
|
||||
|
@ -50,7 +53,6 @@ use deno_core::anyhow::Context;
|
|||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::url::Url;
|
||||
use deno_runtime::deno_node::PackageJson;
|
||||
use deno_runtime::deno_permissions::PermissionsOptions;
|
||||
use deno_runtime::deno_tls::deno_native_certs::load_native_certs;
|
||||
use deno_runtime::deno_tls::rustls;
|
||||
|
@ -63,6 +65,7 @@ use dotenvy::from_filename;
|
|||
use once_cell::sync::Lazy;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::io::BufReader;
|
||||
|
@ -116,9 +119,6 @@ pub static DENO_DISABLE_PEDANTIC_NODE_WARNINGS: Lazy<bool> = Lazy::new(|| {
|
|||
.is_some()
|
||||
});
|
||||
|
||||
pub static DENO_FUTURE: Lazy<bool> =
|
||||
Lazy::new(|| std::env::var("DENO_FUTURE").ok().is_some());
|
||||
|
||||
pub fn jsr_url() -> &'static Url {
|
||||
static JSR_URL: Lazy<Url> = Lazy::new(|| {
|
||||
let env_var_name = "JSR_URL";
|
||||
|
@ -370,7 +370,7 @@ pub struct WorkspaceTestOptions {
|
|||
pub doc: bool,
|
||||
pub no_run: bool,
|
||||
pub fail_fast: Option<NonZeroUsize>,
|
||||
pub allow_none: bool,
|
||||
pub permit_no_files: bool,
|
||||
pub filter: Option<String>,
|
||||
pub shuffle: Option<u64>,
|
||||
pub concurrent_jobs: NonZeroUsize,
|
||||
|
@ -383,7 +383,7 @@ pub struct WorkspaceTestOptions {
|
|||
impl WorkspaceTestOptions {
|
||||
pub fn resolve(test_flags: &TestFlags) -> Self {
|
||||
Self {
|
||||
allow_none: test_flags.allow_none,
|
||||
permit_no_files: test_flags.permit_no_files,
|
||||
concurrent_jobs: test_flags
|
||||
.concurrent_jobs
|
||||
.unwrap_or_else(|| NonZeroUsize::new(1).unwrap()),
|
||||
|
@ -781,8 +781,6 @@ pub struct CliOptions {
|
|||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
overrides: CliOptionOverrides,
|
||||
pub start_dir: Arc<WorkspaceDirectory>,
|
||||
pub disable_deprecated_api_warning: bool,
|
||||
pub verbose_deprecated_api_warning: bool,
|
||||
pub deno_dir_provider: Arc<DenoDirProvider>,
|
||||
}
|
||||
|
||||
|
@ -813,27 +811,18 @@ impl CliOptions {
|
|||
}
|
||||
|
||||
let maybe_lockfile = maybe_lockfile.filter(|_| !force_global_cache);
|
||||
let root_folder = start_dir.workspace.root_folder_configs();
|
||||
let deno_dir_provider =
|
||||
Arc::new(DenoDirProvider::new(flags.cache_path.clone()));
|
||||
let maybe_node_modules_folder = resolve_node_modules_folder(
|
||||
&initial_cwd,
|
||||
&flags,
|
||||
root_folder.deno_json.as_deref(),
|
||||
root_folder.pkg_json.as_deref(),
|
||||
&start_dir.workspace,
|
||||
&deno_dir_provider,
|
||||
)
|
||||
.with_context(|| "Resolving node_modules folder.")?;
|
||||
|
||||
load_env_variables_from_env_file(flags.env_file.as_ref());
|
||||
|
||||
let disable_deprecated_api_warning = flags.log_level
|
||||
== Some(log::Level::Error)
|
||||
|| std::env::var("DENO_NO_DEPRECATION_WARNINGS").ok().is_some();
|
||||
|
||||
let verbose_deprecated_api_warning =
|
||||
std::env::var("DENO_VERBOSE_WARNINGS").ok().is_some();
|
||||
|
||||
Ok(Self {
|
||||
flags,
|
||||
initial_cwd,
|
||||
|
@ -842,8 +831,6 @@ impl CliOptions {
|
|||
maybe_node_modules_folder,
|
||||
overrides: Default::default(),
|
||||
start_dir,
|
||||
disable_deprecated_api_warning,
|
||||
verbose_deprecated_api_warning,
|
||||
deno_dir_provider,
|
||||
})
|
||||
}
|
||||
|
@ -1137,15 +1124,8 @@ impl CliOptions {
|
|||
self.flags.env_file.as_ref()
|
||||
}
|
||||
|
||||
pub fn enable_future_features(&self) -> bool {
|
||||
*DENO_FUTURE
|
||||
}
|
||||
|
||||
pub fn resolve_main_module(&self) -> Result<ModuleSpecifier, AnyError> {
|
||||
let main_module = match &self.flags.subcommand {
|
||||
DenoSubcommand::Bundle(bundle_flags) => {
|
||||
resolve_url_or_path(&bundle_flags.source_file, self.initial_cwd())?
|
||||
}
|
||||
DenoSubcommand::Compile(compile_flags) => {
|
||||
resolve_url_or_path(&compile_flags.source_file, self.initial_cwd())?
|
||||
}
|
||||
|
@ -1227,11 +1207,6 @@ impl CliOptions {
|
|||
NPM_PROCESS_STATE.is_some()
|
||||
}
|
||||
|
||||
/// Overrides the import map specifier to use.
|
||||
pub fn set_import_map_specifier(&mut self, path: Option<ModuleSpecifier>) {
|
||||
self.overrides.import_map_specifier = Some(path);
|
||||
}
|
||||
|
||||
pub fn has_node_modules_dir(&self) -> bool {
|
||||
self.maybe_node_modules_folder.is_some()
|
||||
}
|
||||
|
@ -1240,26 +1215,13 @@ impl CliOptions {
|
|||
self.maybe_node_modules_folder.as_ref()
|
||||
}
|
||||
|
||||
pub fn with_node_modules_dir_path(&self, path: PathBuf) -> Self {
|
||||
Self {
|
||||
flags: self.flags.clone(),
|
||||
initial_cwd: self.initial_cwd.clone(),
|
||||
maybe_node_modules_folder: Some(path),
|
||||
npmrc: self.npmrc.clone(),
|
||||
maybe_lockfile: self.maybe_lockfile.clone(),
|
||||
start_dir: self.start_dir.clone(),
|
||||
overrides: self.overrides.clone(),
|
||||
disable_deprecated_api_warning: self.disable_deprecated_api_warning,
|
||||
verbose_deprecated_api_warning: self.verbose_deprecated_api_warning,
|
||||
deno_dir_provider: self.deno_dir_provider.clone(),
|
||||
pub fn node_modules_dir(
|
||||
&self,
|
||||
) -> Result<Option<NodeModulesDirMode>, AnyError> {
|
||||
if let Some(flag) = self.flags.node_modules_dir {
|
||||
return Ok(Some(flag));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_modules_dir_enablement(&self) -> Option<bool> {
|
||||
self
|
||||
.flags
|
||||
.node_modules_dir
|
||||
.or_else(|| self.workspace().node_modules_dir())
|
||||
self.workspace().node_modules_dir().map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn vendor_dir_path(&self) -> Option<&PathBuf> {
|
||||
|
@ -1270,23 +1232,7 @@ impl CliOptions {
|
|||
&self,
|
||||
config_type: TsConfigType,
|
||||
) -> Result<TsConfigForEmit, AnyError> {
|
||||
let result = self.workspace().resolve_ts_config_for_emit(config_type);
|
||||
|
||||
match result {
|
||||
Ok(mut ts_config_for_emit) => {
|
||||
if matches!(self.flags.subcommand, DenoSubcommand::Bundle(..)) {
|
||||
// For backwards compatibility, force `experimentalDecorators` setting
|
||||
// to true.
|
||||
*ts_config_for_emit
|
||||
.ts_config
|
||||
.0
|
||||
.get_mut("experimentalDecorators")
|
||||
.unwrap() = serde_json::Value::Bool(true);
|
||||
}
|
||||
Ok(ts_config_for_emit)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
self.workspace().resolve_ts_config_for_emit(config_type)
|
||||
}
|
||||
|
||||
pub fn resolve_inspector_server(
|
||||
|
@ -1611,9 +1557,18 @@ impl CliOptions {
|
|||
|| self.workspace().has_unstable("bare-node-builtins")
|
||||
}
|
||||
|
||||
fn byonm_enabled(&self) -> bool {
|
||||
// check if enabled via unstable
|
||||
self.node_modules_dir().ok().flatten() == Some(NodeModulesDirMode::Manual)
|
||||
|| NPM_PROCESS_STATE
|
||||
.as_ref()
|
||||
.map(|s| matches!(s.kind, NpmProcessStateKind::Byonm))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn use_byonm(&self) -> bool {
|
||||
if self.enable_future_features()
|
||||
&& self.node_modules_dir_enablement().is_none()
|
||||
if self.node_modules_dir().ok().flatten().is_none()
|
||||
&& self.maybe_node_modules_folder.is_some()
|
||||
&& self
|
||||
.workspace()
|
||||
.config_folders()
|
||||
|
@ -1623,13 +1578,7 @@ impl CliOptions {
|
|||
return true;
|
||||
}
|
||||
|
||||
// check if enabled via unstable
|
||||
self.flags.unstable_config.byonm
|
||||
|| NPM_PROCESS_STATE
|
||||
.as_ref()
|
||||
.map(|s| matches!(s.kind, NpmProcessStateKind::Byonm))
|
||||
.unwrap_or(false)
|
||||
|| self.workspace().has_unstable("byonm")
|
||||
self.byonm_enabled()
|
||||
}
|
||||
|
||||
pub fn unstable_sloppy_imports(&self) -> bool {
|
||||
|
@ -1651,18 +1600,17 @@ impl CliOptions {
|
|||
}
|
||||
});
|
||||
|
||||
if *DENO_FUTURE {
|
||||
let future_features = [
|
||||
deno_runtime::deno_ffi::UNSTABLE_FEATURE_NAME.to_string(),
|
||||
deno_runtime::deno_fs::UNSTABLE_FEATURE_NAME.to_string(),
|
||||
deno_runtime::deno_webgpu::UNSTABLE_FEATURE_NAME.to_string(),
|
||||
];
|
||||
future_features.iter().for_each(|future_feature| {
|
||||
if !from_config_file.contains(future_feature) {
|
||||
from_config_file.push(future_feature.to_string());
|
||||
}
|
||||
});
|
||||
}
|
||||
// TODO(2.0): remove this code and enable these features in `99_main.js` by default.
|
||||
let future_features = [
|
||||
deno_runtime::deno_ffi::UNSTABLE_FEATURE_NAME.to_string(),
|
||||
deno_runtime::deno_fs::UNSTABLE_FEATURE_NAME.to_string(),
|
||||
deno_runtime::deno_webgpu::UNSTABLE_FEATURE_NAME.to_string(),
|
||||
];
|
||||
future_features.iter().for_each(|future_feature| {
|
||||
if !from_config_file.contains(future_feature) {
|
||||
from_config_file.push(future_feature.to_string());
|
||||
}
|
||||
});
|
||||
|
||||
if !from_config_file.is_empty() {
|
||||
// collect unstable granular flags
|
||||
|
@ -1761,42 +1709,55 @@ impl CliOptions {
|
|||
fn resolve_node_modules_folder(
|
||||
cwd: &Path,
|
||||
flags: &Flags,
|
||||
maybe_config_file: Option<&ConfigFile>,
|
||||
maybe_package_json: Option<&PackageJson>,
|
||||
workspace: &Workspace,
|
||||
deno_dir_provider: &Arc<DenoDirProvider>,
|
||||
) -> Result<Option<PathBuf>, AnyError> {
|
||||
let use_node_modules_dir = flags
|
||||
.node_modules_dir
|
||||
.or_else(|| maybe_config_file.and_then(|c| c.json.node_modules_dir))
|
||||
.or(flags.vendor)
|
||||
.or_else(|| maybe_config_file.and_then(|c| c.json.vendor));
|
||||
fn resolve_from_root(root_folder: &FolderConfigs, cwd: &Path) -> PathBuf {
|
||||
root_folder
|
||||
.deno_json
|
||||
.as_ref()
|
||||
.map(|c| Cow::Owned(c.dir_path()))
|
||||
.or_else(|| {
|
||||
root_folder
|
||||
.pkg_json
|
||||
.as_ref()
|
||||
.map(|c| Cow::Borrowed(c.dir_path()))
|
||||
})
|
||||
.unwrap_or(Cow::Borrowed(cwd))
|
||||
.join("node_modules")
|
||||
}
|
||||
|
||||
let root_folder = workspace.root_folder_configs();
|
||||
let use_node_modules_dir = if let Some(mode) = flags.node_modules_dir {
|
||||
Some(mode.uses_node_modules_dir())
|
||||
} else {
|
||||
workspace
|
||||
.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))
|
||||
};
|
||||
let path = if use_node_modules_dir == Some(false) {
|
||||
return Ok(None);
|
||||
} else if let Some(state) = &*NPM_PROCESS_STATE {
|
||||
return Ok(state.local_node_modules_path.as_ref().map(PathBuf::from));
|
||||
} else if let Some(package_json_path) = maybe_package_json.map(|c| &c.path) {
|
||||
} else if root_folder.pkg_json.is_some() {
|
||||
let node_modules_dir = resolve_from_root(root_folder, cwd);
|
||||
if let Ok(deno_dir) = deno_dir_provider.get_or_create() {
|
||||
// `deno_dir.root` can be symlink in macOS
|
||||
if let Ok(root) = canonicalize_path_maybe_not_exists(&deno_dir.root) {
|
||||
if package_json_path.starts_with(root) {
|
||||
if node_modules_dir.starts_with(root) {
|
||||
// if the package.json is in deno_dir, then do not use node_modules
|
||||
// next to it as local node_modules dir
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
// auto-discover the local_node_modules_folder when a package.json exists
|
||||
// and it's not in deno_dir
|
||||
package_json_path.parent().unwrap().join("node_modules")
|
||||
node_modules_dir
|
||||
} else if use_node_modules_dir.is_none() {
|
||||
return Ok(None);
|
||||
} else if let Some(config_path) = maybe_config_file
|
||||
.as_ref()
|
||||
.and_then(|c| c.specifier.to_file_path().ok())
|
||||
{
|
||||
config_path.parent().unwrap().join("node_modules")
|
||||
} else {
|
||||
cwd.join("node_modules")
|
||||
resolve_from_root(root_folder, cwd)
|
||||
};
|
||||
Ok(Some(canonicalize_path_maybe_not_exists(&path)?))
|
||||
}
|
||||
|
@ -1886,19 +1847,18 @@ pub fn npm_pkg_req_ref_to_binary_command(
|
|||
pub fn config_to_deno_graph_workspace_member(
|
||||
config: &ConfigFile,
|
||||
) -> Result<deno_graph::WorkspaceMember, AnyError> {
|
||||
let nv = deno_semver::package::PackageNv {
|
||||
name: match &config.json.name {
|
||||
Some(name) => name.clone(),
|
||||
None => bail!("Missing 'name' field in config file."),
|
||||
},
|
||||
version: match &config.json.version {
|
||||
Some(name) => deno_semver::Version::parse_standard(name)?,
|
||||
None => bail!("Missing 'version' field in config file."),
|
||||
},
|
||||
let name = match &config.json.name {
|
||||
Some(name) => name.clone(),
|
||||
None => bail!("Missing 'name' field in config file."),
|
||||
};
|
||||
let version = match &config.json.version {
|
||||
Some(name) => Some(deno_semver::Version::parse_standard(name)?),
|
||||
None => None,
|
||||
};
|
||||
Ok(deno_graph::WorkspaceMember {
|
||||
base: config.specifier.join("./").unwrap(),
|
||||
nv,
|
||||
name,
|
||||
version,
|
||||
exports: config.to_exports_config()?.into_map(),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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<InstallNpmRemotePkg>,
|
||||
workspace_pkgs: Vec<InstallNpmWorkspacePkg>,
|
||||
}
|
||||
|
||||
impl PackageJsonInstallDepsProvider {
|
||||
impl NpmInstallDepsProvider {
|
||||
pub fn empty() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn from_workspace(workspace: &Arc<Workspace>) -> 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 {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -143,29 +143,6 @@ const EXEC_TIME_BENCHMARKS: &[(&str, &[&str], Option<i32>)] = &[
|
|||
],
|
||||
None,
|
||||
),
|
||||
(
|
||||
"bundle",
|
||||
&[
|
||||
"bundle",
|
||||
"--unstable",
|
||||
"--config",
|
||||
"tests/config/deno.json",
|
||||
"tests/util/std/http/file_server_test.ts",
|
||||
],
|
||||
None,
|
||||
),
|
||||
(
|
||||
"bundle_no_check",
|
||||
&[
|
||||
"bundle",
|
||||
"--no-check",
|
||||
"--unstable",
|
||||
"--config",
|
||||
"tests/config/deno.json",
|
||||
"tests/util/std/http/file_server_test.ts",
|
||||
],
|
||||
None,
|
||||
),
|
||||
];
|
||||
|
||||
const RESULT_KEYS: &[&str] =
|
||||
|
@ -314,40 +291,6 @@ fn get_binary_sizes(target_dir: &Path) -> Result<HashMap<String, i64>> {
|
|||
Ok(sizes)
|
||||
}
|
||||
|
||||
const BUNDLES: &[(&str, &str)] = &[
|
||||
("file_server", "./tests/util/std/http/file_server.ts"),
|
||||
("welcome", "./tests/testdata/welcome.ts"),
|
||||
];
|
||||
fn bundle_benchmark(deno_exe: &Path) -> Result<HashMap<String, i64>> {
|
||||
let mut sizes = HashMap::<String, i64>::new();
|
||||
|
||||
for (name, url) in BUNDLES {
|
||||
let path = format!("{name}.bundle.js");
|
||||
test_util::run(
|
||||
&[
|
||||
deno_exe.to_str().unwrap(),
|
||||
"bundle",
|
||||
"--unstable",
|
||||
"--config",
|
||||
"tests/config/deno.json",
|
||||
url,
|
||||
&path,
|
||||
],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
|
||||
let file = PathBuf::from(path);
|
||||
assert!(file.is_file());
|
||||
sizes.insert(name.to_string(), file.metadata()?.len() as i64);
|
||||
let _ = fs::remove_file(file);
|
||||
}
|
||||
|
||||
Ok(sizes)
|
||||
}
|
||||
|
||||
fn run_max_mem_benchmark(deno_exe: &Path) -> Result<HashMap<String, i64>> {
|
||||
let mut results = HashMap::<String, i64>::new();
|
||||
|
||||
|
@ -415,7 +358,6 @@ async fn main() -> Result<()> {
|
|||
let mut args = env::args();
|
||||
|
||||
let mut benchmarks = vec![
|
||||
"bundle",
|
||||
"exec_time",
|
||||
"binary_size",
|
||||
"cargo_deps",
|
||||
|
@ -465,11 +407,6 @@ async fn main() -> Result<()> {
|
|||
..Default::default()
|
||||
};
|
||||
|
||||
if benchmarks.contains(&"bundle") {
|
||||
let bundle_size = bundle_benchmark(&deno_exe)?;
|
||||
new_data.bundle_size = bundle_size;
|
||||
}
|
||||
|
||||
if benchmarks.contains(&"exec_time") {
|
||||
let exec_times = run_exec_time(&deno_exe, &target_dir)?;
|
||||
new_data.benchmark = exec_times;
|
||||
|
|
13
cli/cache/emit.rs
vendored
13
cli/cache/emit.rs
vendored
|
@ -82,19 +82,6 @@ impl EmitCache {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Gets the filepath which stores the emit.
|
||||
pub fn get_emit_filepath(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<PathBuf> {
|
||||
Some(
|
||||
self
|
||||
.disk_cache
|
||||
.location
|
||||
.join(self.get_emit_filename(specifier)?),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_emit_filename(&self, specifier: &ModuleSpecifier) -> Option<PathBuf> {
|
||||
self
|
||||
.disk_cache
|
||||
|
|
64
cli/cache/mod.rs
vendored
64
cli/cache/mod.rs
vendored
|
@ -8,6 +8,7 @@ use crate::file_fetcher::FileFetcher;
|
|||
use crate::file_fetcher::FileOrRedirect;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::util::fs::atomic_write_file_with_retries;
|
||||
use crate::util::path::specifier_has_extension;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::futures;
|
||||
|
@ -74,6 +75,10 @@ impl deno_cache_dir::DenoCacheEnv for RealDenoCacheEnv {
|
|||
atomic_write_file_with_retries(path, bytes, CACHE_PERM)
|
||||
}
|
||||
|
||||
fn remove_file(&self, path: &Path) -> std::io::Result<()> {
|
||||
std::fs::remove_file(path)
|
||||
}
|
||||
|
||||
fn modified(&self, path: &Path) -> std::io::Result<Option<SystemTime>> {
|
||||
match std::fs::metadata(path) {
|
||||
Ok(metadata) => Ok(Some(
|
||||
|
@ -102,7 +107,6 @@ pub use deno_cache_dir::HttpCache;
|
|||
/// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides
|
||||
/// a concise interface to the DENO_DIR when building module graphs.
|
||||
pub struct FetchCacher {
|
||||
emit_cache: Arc<EmitCache>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
|
@ -114,7 +118,6 @@ pub struct FetchCacher {
|
|||
|
||||
impl FetchCacher {
|
||||
pub fn new(
|
||||
emit_cache: Arc<EmitCache>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
|
@ -123,7 +126,6 @@ impl FetchCacher {
|
|||
permissions: PermissionsContainer,
|
||||
) -> Self {
|
||||
Self {
|
||||
emit_cache,
|
||||
file_fetcher,
|
||||
file_header_overrides,
|
||||
global_http_cache,
|
||||
|
@ -140,15 +142,7 @@ impl FetchCacher {
|
|||
self.cache_info_enabled = true;
|
||||
}
|
||||
|
||||
// DEPRECATED: Where the file is stored and how it's stored should be an implementation
|
||||
// detail of the cache.
|
||||
//
|
||||
// todo(dsheret): remove once implementing
|
||||
// * https://github.com/denoland/deno/issues/17707
|
||||
// * https://github.com/denoland/deno/issues/17703
|
||||
#[deprecated(
|
||||
note = "There should not be a way to do this because the file may not be cached at a local path in the future."
|
||||
)]
|
||||
/// Only use this for `deno info`.
|
||||
fn get_local_path(&self, specifier: &ModuleSpecifier) -> Option<PathBuf> {
|
||||
// TODO(@kitsonk) fix when deno_graph does not query cache for synthetic
|
||||
// modules
|
||||
|
@ -175,15 +169,7 @@ impl Loader for FetchCacher {
|
|||
#[allow(deprecated)]
|
||||
let local = self.get_local_path(specifier)?;
|
||||
if local.is_file() {
|
||||
let emit = self
|
||||
.emit_cache
|
||||
.get_emit_filepath(specifier)
|
||||
.filter(|p| p.is_file());
|
||||
Some(CacheInfo {
|
||||
local: Some(local),
|
||||
emit,
|
||||
map: None,
|
||||
})
|
||||
Some(CacheInfo { local: Some(local) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -196,19 +182,28 @@ impl Loader for FetchCacher {
|
|||
) -> LoadFuture {
|
||||
use deno_graph::source::CacheSetting as LoaderCacheSetting;
|
||||
|
||||
if specifier.scheme() == "file"
|
||||
&& specifier.path().contains("/node_modules/")
|
||||
{
|
||||
// The specifier might be in a completely different symlinked tree than
|
||||
// what the node_modules url is in (ex. `/my-project-1/node_modules`
|
||||
// symlinked to `/my-project-2/node_modules`), so first we checked if the path
|
||||
// is in a node_modules dir to avoid needlessly canonicalizing, then now compare
|
||||
// against the canonicalized specifier.
|
||||
let specifier =
|
||||
crate::node::resolve_specifier_into_node_modules(specifier);
|
||||
if self.npm_resolver.in_npm_package(&specifier) {
|
||||
if specifier.scheme() == "file" {
|
||||
if specifier.path().contains("/node_modules/") {
|
||||
// The specifier might be in a completely different symlinked tree than
|
||||
// what the node_modules url is in (ex. `/my-project-1/node_modules`
|
||||
// symlinked to `/my-project-2/node_modules`), so first we checked if the path
|
||||
// is in a node_modules dir to avoid needlessly canonicalizing, then now compare
|
||||
// against the canonicalized specifier.
|
||||
let specifier =
|
||||
crate::node::resolve_specifier_into_node_modules(specifier);
|
||||
if self.npm_resolver.in_npm_package(&specifier) {
|
||||
return Box::pin(futures::future::ready(Ok(Some(
|
||||
LoadResponse::External { specifier },
|
||||
))));
|
||||
}
|
||||
}
|
||||
|
||||
// make local CJS modules external to the graph
|
||||
if specifier_has_extension(specifier, "cjs") {
|
||||
return Box::pin(futures::future::ready(Ok(Some(
|
||||
LoadResponse::External { specifier },
|
||||
LoadResponse::External {
|
||||
specifier: specifier.clone(),
|
||||
},
|
||||
))));
|
||||
}
|
||||
}
|
||||
|
@ -289,6 +284,7 @@ impl Loader for FetchCacher {
|
|||
fn cache_module_info(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
source: &Arc<[u8]>,
|
||||
module_info: &deno_graph::ModuleInfo,
|
||||
) {
|
||||
|
@ -296,7 +292,7 @@ impl Loader for FetchCacher {
|
|||
let source_hash = CacheDBHash::from_source(source);
|
||||
let result = self.module_info_cache.set_module_info(
|
||||
specifier,
|
||||
MediaType::from_specifier(specifier),
|
||||
media_type,
|
||||
source_hash,
|
||||
module_info,
|
||||
);
|
||||
|
|
118
cli/emit.rs
118
cli/emit.rs
|
@ -112,28 +112,26 @@ impl Emitter {
|
|||
let parsed_source_cache = self.parsed_source_cache.clone();
|
||||
let transpile_and_emit_options =
|
||||
self.transpile_and_emit_options.clone();
|
||||
let (should_cache, transpile_result) =
|
||||
deno_core::unsync::spawn_blocking({
|
||||
let specifier = specifier.clone();
|
||||
let source = source.clone();
|
||||
move || -> Result<_, AnyError> {
|
||||
EmitParsedSourceHelper::transpile(
|
||||
&parsed_source_cache,
|
||||
&specifier,
|
||||
source.clone(),
|
||||
media_type,
|
||||
&transpile_and_emit_options.0,
|
||||
&transpile_and_emit_options.1,
|
||||
)
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap()?;
|
||||
let transpile_result = deno_core::unsync::spawn_blocking({
|
||||
let specifier = specifier.clone();
|
||||
let source = source.clone();
|
||||
move || -> Result<_, AnyError> {
|
||||
EmitParsedSourceHelper::transpile(
|
||||
&parsed_source_cache,
|
||||
&specifier,
|
||||
source.clone(),
|
||||
media_type,
|
||||
&transpile_and_emit_options.0,
|
||||
&transpile_and_emit_options.1,
|
||||
)
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap()?;
|
||||
Ok(helper.post_emit_parsed_source(
|
||||
specifier,
|
||||
transpile_result,
|
||||
source_hash,
|
||||
should_cache,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -150,20 +148,18 @@ impl Emitter {
|
|||
match helper.pre_emit_parsed_source(specifier, source) {
|
||||
PreEmitResult::Cached(emitted_text) => Ok(emitted_text),
|
||||
PreEmitResult::NotCached { source_hash } => {
|
||||
let (should_cache, transpile_result) =
|
||||
EmitParsedSourceHelper::transpile(
|
||||
&self.parsed_source_cache,
|
||||
specifier,
|
||||
source.clone(),
|
||||
media_type,
|
||||
&self.transpile_and_emit_options.0,
|
||||
&self.transpile_and_emit_options.1,
|
||||
)?;
|
||||
let transpile_result = EmitParsedSourceHelper::transpile(
|
||||
&self.parsed_source_cache,
|
||||
specifier,
|
||||
source.clone(),
|
||||
media_type,
|
||||
&self.transpile_and_emit_options.0,
|
||||
&self.transpile_and_emit_options.1,
|
||||
)?;
|
||||
Ok(helper.post_emit_parsed_source(
|
||||
specifier,
|
||||
transpile_result,
|
||||
source_hash,
|
||||
should_cache,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -261,16 +257,13 @@ impl<'a> EmitParsedSourceHelper<'a> {
|
|||
media_type: MediaType,
|
||||
transpile_options: &deno_ast::TranspileOptions,
|
||||
emit_options: &deno_ast::EmitOptions,
|
||||
) -> Result<(bool, TranspileResult), AnyError> {
|
||||
) -> Result<TranspileResult, AnyError> {
|
||||
// nothing else needs the parsed source at this point, so remove from
|
||||
// the cache in order to not transpile owned
|
||||
let parsed_source = parsed_source_cache
|
||||
.remove_or_parse_module(specifier, source, media_type)?;
|
||||
let should_cache = !has_import_assertion(&parsed_source);
|
||||
Ok((
|
||||
should_cache,
|
||||
parsed_source.transpile(transpile_options, emit_options)?,
|
||||
))
|
||||
ensure_no_import_assertion(&parsed_source)?;
|
||||
Ok(parsed_source.transpile(transpile_options, emit_options)?)
|
||||
}
|
||||
|
||||
pub fn post_emit_parsed_source(
|
||||
|
@ -278,8 +271,6 @@ impl<'a> EmitParsedSourceHelper<'a> {
|
|||
specifier: &ModuleSpecifier,
|
||||
transpile_result: TranspileResult,
|
||||
source_hash: u64,
|
||||
// todo(dsherret): remove after Deno 2.0
|
||||
should_cache: bool,
|
||||
) -> ModuleCodeBytes {
|
||||
let transpiled_source = match transpile_result {
|
||||
TranspileResult::Owned(source) => source,
|
||||
|
@ -289,44 +280,47 @@ impl<'a> EmitParsedSourceHelper<'a> {
|
|||
}
|
||||
};
|
||||
debug_assert!(transpiled_source.source_map.is_none());
|
||||
if should_cache {
|
||||
self.0.emit_cache.set_emit_code(
|
||||
specifier,
|
||||
source_hash,
|
||||
&transpiled_source.source,
|
||||
);
|
||||
}
|
||||
self.0.emit_cache.set_emit_code(
|
||||
specifier,
|
||||
source_hash,
|
||||
&transpiled_source.source,
|
||||
);
|
||||
transpiled_source.source.into_boxed_slice().into()
|
||||
}
|
||||
}
|
||||
|
||||
fn has_import_assertion(parsed_source: &deno_ast::ParsedSource) -> bool {
|
||||
// todo(dsherret): this is a temporary measure until we have swc erroring for this
|
||||
fn ensure_no_import_assertion(
|
||||
parsed_source: &deno_ast::ParsedSource,
|
||||
) -> Result<(), AnyError> {
|
||||
fn has_import_assertion(text: &str) -> bool {
|
||||
// good enough
|
||||
text.contains(" assert ") && !text.contains(" with ")
|
||||
}
|
||||
|
||||
fn warn_import_attribute(
|
||||
fn create_err(
|
||||
parsed_source: &deno_ast::ParsedSource,
|
||||
range: SourceRange,
|
||||
) {
|
||||
) -> AnyError {
|
||||
let text_info = parsed_source.text_info_lazy();
|
||||
let loc = text_info.line_and_column_display(range.start);
|
||||
deno_runtime::import_assertion_callback(
|
||||
deno_core::ImportAssertionsSupportCustomCallbackArgs {
|
||||
maybe_specifier: Some(parsed_source.specifier().to_string()),
|
||||
maybe_line_number: Some(loc.line_number),
|
||||
column_number: loc.column_number,
|
||||
maybe_source_line: Some(range.text_fast(text_info).to_string()),
|
||||
},
|
||||
)
|
||||
let mut msg = "Import assertions are deprecated. Use `with` keyword, instead of 'assert' keyword.".to_string();
|
||||
msg.push_str("\n\n");
|
||||
msg.push_str(range.text_fast(text_info));
|
||||
msg.push_str("\n\n");
|
||||
msg.push_str(&format!(
|
||||
" at {}:{}:{}\n",
|
||||
parsed_source.specifier(),
|
||||
loc.line_number,
|
||||
loc.column_number,
|
||||
));
|
||||
deno_core::anyhow::anyhow!("{}", msg)
|
||||
}
|
||||
|
||||
let Some(module) = parsed_source.program_ref().as_module() else {
|
||||
return false;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let mut had_import_assertion = false;
|
||||
for item in &module.body {
|
||||
match item {
|
||||
deno_ast::swc::ast::ModuleItem::ModuleDecl(decl) => match decl {
|
||||
|
@ -334,24 +328,21 @@ fn has_import_assertion(parsed_source: &deno_ast::ParsedSource) -> bool {
|
|||
if n.with.is_some()
|
||||
&& has_import_assertion(n.text_fast(parsed_source.text_info_lazy()))
|
||||
{
|
||||
had_import_assertion = true;
|
||||
warn_import_attribute(parsed_source, n.range());
|
||||
return Err(create_err(parsed_source, n.range()));
|
||||
}
|
||||
}
|
||||
deno_ast::swc::ast::ModuleDecl::ExportAll(n) => {
|
||||
if n.with.is_some()
|
||||
&& has_import_assertion(n.text_fast(parsed_source.text_info_lazy()))
|
||||
{
|
||||
had_import_assertion = true;
|
||||
warn_import_attribute(parsed_source, n.range());
|
||||
return Err(create_err(parsed_source, n.range()));
|
||||
}
|
||||
}
|
||||
deno_ast::swc::ast::ModuleDecl::ExportNamed(n) => {
|
||||
if n.with.is_some()
|
||||
&& has_import_assertion(n.text_fast(parsed_source.text_info_lazy()))
|
||||
{
|
||||
had_import_assertion = true;
|
||||
warn_import_attribute(parsed_source, n.range());
|
||||
return Err(create_err(parsed_source, n.range()));
|
||||
}
|
||||
}
|
||||
deno_ast::swc::ast::ModuleDecl::ExportDecl(_)
|
||||
|
@ -364,5 +355,6 @@ fn has_import_assertion(parsed_source: &deno_ast::ParsedSource) -> bool {
|
|||
deno_ast::swc::ast::ModuleItem::Stmt(_) => {}
|
||||
}
|
||||
}
|
||||
had_import_assertion
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
// 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;
|
||||
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 +387,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(),
|
||||
|
@ -522,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,
|
||||
|
@ -619,7 +616,6 @@ impl CliFactory {
|
|||
self.parsed_source_cache().clone(),
|
||||
cli_options.maybe_lockfile().cloned(),
|
||||
self.maybe_file_watcher_reporter().clone(),
|
||||
self.emit_cache()?.clone(),
|
||||
self.file_fetcher()?.clone(),
|
||||
self.global_http_cache()?.clone(),
|
||||
)))
|
||||
|
@ -793,20 +789,12 @@ impl CliFactory {
|
|||
self.maybe_inspector_server()?.clone(),
|
||||
cli_options.maybe_lockfile().cloned(),
|
||||
self.feature_checker()?.clone(),
|
||||
self.create_cli_main_worker_options()?,
|
||||
cli_options.node_ipc_fd(),
|
||||
cli_options.serve_port(),
|
||||
cli_options.serve_host(),
|
||||
cli_options.enable_future_features(),
|
||||
// TODO(bartlomieju): temporarily disabled
|
||||
// cli_options.disable_deprecated_api_warning,
|
||||
true,
|
||||
cli_options.verbose_deprecated_api_warning,
|
||||
if cli_options.code_cache_enabled() {
|
||||
Some(self.code_cache()?.clone())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
self.create_cli_main_worker_options()?,
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -871,6 +859,9 @@ impl CliFactory {
|
|||
unstable: cli_options.legacy_unstable_flag(),
|
||||
create_hmr_runner,
|
||||
create_coverage_collector,
|
||||
node_ipc: cli_options.node_ipc_fd(),
|
||||
serve_port: cli_options.serve_port(),
|
||||
serve_host: cli_options.serve_host(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,13 +20,12 @@ 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;
|
||||
use deno_runtime::fs_util::specifier_to_file_path;
|
||||
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
|
@ -35,7 +34,6 @@ use deno_graph::source::Loader;
|
|||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::source::ResolveError;
|
||||
use deno_graph::GraphKind;
|
||||
use deno_graph::Module;
|
||||
use deno_graph::ModuleError;
|
||||
use deno_graph::ModuleGraph;
|
||||
use deno_graph::ModuleGraphError;
|
||||
|
@ -44,10 +42,13 @@ use deno_graph::SpecifierError;
|
|||
use deno_runtime::deno_fs::FileSystem;
|
||||
use deno_runtime::deno_node;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use deno_semver::jsr::JsrDepPackageReq;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use deno_semver::Version;
|
||||
use import_map::ImportMapError;
|
||||
use std::collections::HashSet;
|
||||
use std::error::Error;
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -110,7 +111,7 @@ pub fn graph_valid(
|
|||
ModuleGraphError::ModuleError(error) => {
|
||||
enhanced_lockfile_error_message(error)
|
||||
.or_else(|| enhanced_sloppy_imports_error_message(fs, error))
|
||||
.unwrap_or_else(|| format!("{}", error))
|
||||
.unwrap_or_else(|| format_deno_graph_error(error))
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -164,7 +165,10 @@ pub fn graph_valid(
|
|||
} else {
|
||||
// finally surface the npm resolution result
|
||||
if let Err(err) = &graph.npm_dep_graph_result {
|
||||
return Err(custom_error(get_error_class_name(err), format!("{}", err)));
|
||||
return Err(custom_error(
|
||||
get_error_class_name(err),
|
||||
format_deno_graph_error(err.as_ref().deref()),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -364,7 +368,6 @@ pub struct ModuleGraphBuilder {
|
|||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
lockfile: Option<Arc<CliLockfile>>,
|
||||
maybe_file_watcher_reporter: Option<FileWatcherReporter>,
|
||||
emit_cache: Arc<cache::EmitCache>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
}
|
||||
|
@ -381,7 +384,6 @@ impl ModuleGraphBuilder {
|
|||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
lockfile: Option<Arc<CliLockfile>>,
|
||||
maybe_file_watcher_reporter: Option<FileWatcherReporter>,
|
||||
emit_cache: Arc<cache::EmitCache>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
) -> Self {
|
||||
|
@ -395,7 +397,6 @@ impl ModuleGraphBuilder {
|
|||
parsed_source_cache,
|
||||
lockfile,
|
||||
maybe_file_watcher_reporter,
|
||||
emit_cache,
|
||||
file_fetcher,
|
||||
global_http_cache,
|
||||
}
|
||||
|
@ -463,7 +464,7 @@ impl ModuleGraphBuilder {
|
|||
.content
|
||||
.packages
|
||||
.jsr
|
||||
.get(&package_nv.to_string())
|
||||
.get(package_nv)
|
||||
.map(|s| LoaderChecksum::new(s.integrity.clone()))
|
||||
}
|
||||
|
||||
|
@ -477,7 +478,7 @@ impl ModuleGraphBuilder {
|
|||
self
|
||||
.0
|
||||
.lock()
|
||||
.insert_package(package_nv.to_string(), checksum.into_string());
|
||||
.insert_package(package_nv.clone(), checksum.into_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -535,7 +536,12 @@ impl ModuleGraphBuilder {
|
|||
) -> Result<(), AnyError> {
|
||||
// ensure an "npm install" is done if the user has explicitly
|
||||
// opted into using a node_modules directory
|
||||
if self.options.node_modules_dir_enablement() == Some(true) {
|
||||
if self
|
||||
.options
|
||||
.node_modules_dir()?
|
||||
.map(|m| m.uses_node_modules_dir())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
}
|
||||
|
@ -556,16 +562,21 @@ impl ModuleGraphBuilder {
|
|||
}
|
||||
}
|
||||
}
|
||||
for (key, value) in &lockfile.content.packages.specifiers {
|
||||
if let Some(key) = key
|
||||
.strip_prefix("jsr:")
|
||||
.and_then(|key| PackageReq::from_str(key).ok())
|
||||
{
|
||||
if let Some(value) = value
|
||||
.strip_prefix("jsr:")
|
||||
.and_then(|value| PackageNv::from_str(value).ok())
|
||||
{
|
||||
graph.packages.add_nv(key, value);
|
||||
for (req_dep, value) in &lockfile.content.packages.specifiers {
|
||||
match req_dep.kind {
|
||||
deno_semver::package::PackageKind::Jsr => {
|
||||
if let Ok(version) = Version::parse_standard(value) {
|
||||
graph.packages.add_nv(
|
||||
req_dep.req.clone(),
|
||||
PackageNv {
|
||||
name: req_dep.req.name.clone(),
|
||||
version,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
deno_semver::package::PackageKind::Npm => {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -603,16 +614,15 @@ impl ModuleGraphBuilder {
|
|||
if has_jsr_package_mappings_changed {
|
||||
for (from, to) in graph.packages.mappings() {
|
||||
lockfile.insert_package_specifier(
|
||||
format!("jsr:{}", from),
|
||||
format!("jsr:{}", to),
|
||||
JsrDepPackageReq::jsr(from.clone()),
|
||||
to.version.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
// jsr packages
|
||||
if has_jsr_package_deps_changed {
|
||||
for (name, deps) in graph.packages.packages_with_deps() {
|
||||
lockfile
|
||||
.add_package_deps(&name.to_string(), deps.map(|s| s.to_string()));
|
||||
for (nv, deps) in graph.packages.packages_with_deps() {
|
||||
lockfile.add_package_deps(nv, deps.cloned());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -668,7 +678,6 @@ impl ModuleGraphBuilder {
|
|||
permissions: PermissionsContainer,
|
||||
) -> cache::FetchCacher {
|
||||
cache::FetchCacher::new(
|
||||
self.emit_cache.clone(),
|
||||
self.file_fetcher.clone(),
|
||||
self.options.resolve_file_header_overrides(),
|
||||
self.global_http_cache.clone(),
|
||||
|
@ -707,33 +716,29 @@ impl ModuleGraphBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn error_for_any_npm_specifier(
|
||||
graph: &ModuleGraph,
|
||||
) -> Result<(), AnyError> {
|
||||
for module in graph.modules() {
|
||||
match module {
|
||||
Module::Npm(module) => {
|
||||
bail!("npm specifiers have not yet been implemented for this subcommand (https://github.com/denoland/deno/issues/15960). Found: {}", module.specifier)
|
||||
}
|
||||
Module::Node(module) => {
|
||||
bail!("Node specifiers have not yet been implemented for this subcommand (https://github.com/denoland/deno/issues/15960). Found: node:{}", module.module_name)
|
||||
}
|
||||
Module::Js(_) | Module::Json(_) | Module::External(_) => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds more explanatory information to a resolution error.
|
||||
pub fn enhanced_resolution_error_message(error: &ResolutionError) -> String {
|
||||
let mut message = format!("{error}");
|
||||
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 jsr:{}` or `deno add npm:{}`",
|
||||
specifier, specifier
|
||||
)
|
||||
})
|
||||
};
|
||||
|
||||
if let Some(hint) = maybe_hint {
|
||||
message.push_str(&format!("\n {} {}", colors::cyan("hint:"), hint));
|
||||
}
|
||||
|
||||
message
|
||||
|
@ -868,6 +873,50 @@ 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, .. },
|
||||
range,
|
||||
} = error
|
||||
{
|
||||
if range.specifier.scheme() == "file" {
|
||||
maybe_specifier = Some(specifier);
|
||||
}
|
||||
} else if let ResolutionError::ResolverError { error, range, .. } = error {
|
||||
if range.specifier.scheme() == "file" {
|
||||
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::<SpecifierError>()
|
||||
{
|
||||
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(
|
||||
|
@ -1022,6 +1071,49 @@ impl deno_graph::source::JsrUrlProvider for CliJsrUrlProvider {
|
|||
}
|
||||
}
|
||||
|
||||
// todo(dsherret): We should change ModuleError to use thiserror so that
|
||||
// we don't need to do this.
|
||||
fn format_deno_graph_error(err: &dyn Error) -> String {
|
||||
use std::fmt::Write;
|
||||
|
||||
let mut message = format!("{}", err);
|
||||
let mut maybe_source = err.source();
|
||||
|
||||
if maybe_source.is_some() {
|
||||
let mut past_message = message.clone();
|
||||
let mut count = 0;
|
||||
let mut display_count = 0;
|
||||
while let Some(source) = maybe_source {
|
||||
let current_message = format!("{}", source);
|
||||
maybe_source = source.source();
|
||||
|
||||
// sometimes an error might be repeated due to
|
||||
// being boxed multiple times in another AnyError
|
||||
if current_message != past_message {
|
||||
write!(message, "\n {}: ", display_count,).unwrap();
|
||||
for (i, line) in current_message.lines().enumerate() {
|
||||
if i > 0 {
|
||||
write!(message, "\n {}", line).unwrap();
|
||||
} else {
|
||||
write!(message, "{}", line).unwrap();
|
||||
}
|
||||
}
|
||||
display_count += 1;
|
||||
}
|
||||
|
||||
if count > 8 {
|
||||
write!(message, "\n {}: ...", count).unwrap();
|
||||
break;
|
||||
}
|
||||
|
||||
past_message = current_message;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
message
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::sync::Arc;
|
||||
|
|
|
@ -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;
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -751,7 +751,7 @@ impl CodeActionCollection {
|
|||
.as_ref()
|
||||
.and_then(|d| serde_json::from_value::<Vec<DataQuickFix>>(d.clone()).ok())
|
||||
{
|
||||
let uri = url_to_uri(specifier);
|
||||
let uri = url_to_uri(specifier)?;
|
||||
for quick_fix in data_quick_fixes {
|
||||
let mut changes = HashMap::new();
|
||||
changes.insert(
|
||||
|
@ -797,7 +797,7 @@ impl CodeActionCollection {
|
|||
maybe_text_info: Option<&SourceTextInfo>,
|
||||
maybe_parsed_source: Option<&deno_ast::ParsedSource>,
|
||||
) -> Result<(), AnyError> {
|
||||
let uri = url_to_uri(specifier);
|
||||
let uri = url_to_uri(specifier)?;
|
||||
let code = diagnostic
|
||||
.code
|
||||
.as_ref()
|
||||
|
|
|
@ -8,6 +8,7 @@ use deno_core::anyhow::bail;
|
|||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::unsync::spawn;
|
||||
use lsp_types::Uri;
|
||||
use tower_lsp::lsp_types as lsp;
|
||||
use tower_lsp::lsp_types::ConfigurationItem;
|
||||
|
||||
|
@ -17,7 +18,6 @@ use super::config::WorkspaceSettings;
|
|||
use super::config::SETTINGS_SECTION;
|
||||
use super::lsp_custom;
|
||||
use super::testing::lsp_custom as testing_lsp_custom;
|
||||
use super::urls::LspClientUrl;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TestingNotification {
|
||||
|
@ -52,14 +52,11 @@ impl Client {
|
|||
|
||||
pub async fn publish_diagnostics(
|
||||
&self,
|
||||
uri: LspClientUrl,
|
||||
uri: Uri,
|
||||
diags: Vec<lsp::Diagnostic>,
|
||||
version: Option<i32>,
|
||||
) {
|
||||
self
|
||||
.0
|
||||
.publish_diagnostics(uri.to_uri(), diags, version)
|
||||
.await;
|
||||
self.0.publish_diagnostics(uri, diags, version).await;
|
||||
}
|
||||
|
||||
pub fn send_registry_state_notification(
|
||||
|
|
|
@ -5,6 +5,7 @@ use deno_config::deno_json::DenoJsonCache;
|
|||
use deno_config::deno_json::FmtConfig;
|
||||
use deno_config::deno_json::FmtOptionsConfig;
|
||||
use deno_config::deno_json::LintConfig;
|
||||
use deno_config::deno_json::NodeModulesDirMode;
|
||||
use deno_config::deno_json::TestConfig;
|
||||
use deno_config::deno_json::TsConfig;
|
||||
use deno_config::fs::DenoConfigFs;
|
||||
|
@ -54,7 +55,6 @@ use crate::args::CliLockfile;
|
|||
use crate::args::ConfigFile;
|
||||
use crate::args::LintFlags;
|
||||
use crate::args::LintOptions;
|
||||
use crate::args::DENO_FUTURE;
|
||||
use crate::cache::FastInsecureHasher;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::lsp::logging::lsp_warn;
|
||||
|
@ -850,7 +850,7 @@ impl Config {
|
|||
let mut config = Self::default();
|
||||
let mut folders = vec![];
|
||||
for root_url in root_urls {
|
||||
let root_uri = url_to_uri(&root_url);
|
||||
let root_uri = url_to_uri(&root_url).unwrap();
|
||||
let name = root_url.path_segments().and_then(|s| s.last());
|
||||
let name = name.unwrap_or_default().to_string();
|
||||
folders.push((
|
||||
|
@ -1084,7 +1084,6 @@ impl Default for LspTsConfig {
|
|||
"strict": true,
|
||||
"target": "esnext",
|
||||
"useDefineForClassFields": true,
|
||||
"useUnknownInCatchVariables": false,
|
||||
"jsx": "react",
|
||||
"jsxFactory": "React.createElement",
|
||||
"jsxFragmentFactory": "React.Fragment",
|
||||
|
@ -1387,11 +1386,12 @@ impl ConfigData {
|
|||
}
|
||||
}
|
||||
|
||||
let byonm = std::env::var("DENO_UNSTABLE_BYONM").is_ok()
|
||||
|| member_dir.workspace.has_unstable("byonm")
|
||||
|| (*DENO_FUTURE
|
||||
&& member_dir.workspace.package_jsons().next().is_some()
|
||||
&& member_dir.workspace.node_modules_dir().is_none());
|
||||
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(),
|
||||
};
|
||||
if byonm {
|
||||
lsp_log!(" Enabled 'bring your own node_modules'.");
|
||||
}
|
||||
|
@ -1694,9 +1694,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;
|
||||
}
|
||||
|
@ -1865,13 +1870,17 @@ 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 explicitly_disabled = workspace.node_modules_dir() == Some(false);
|
||||
let node_modules_mode = workspace.node_modules_dir().ok().flatten();
|
||||
let explicitly_disabled = node_modules_mode == Some(NodeModulesDirMode::None);
|
||||
if explicitly_disabled {
|
||||
return None;
|
||||
}
|
||||
let enabled = byonm
|
||||
|| workspace.node_modules_dir() == Some(true)
|
||||
|| node_modules_mode
|
||||
.map(|m| m.uses_node_modules_dir())
|
||||
.unwrap_or(false)
|
||||
|| workspace.vendor_dir_path().is_some();
|
||||
|
||||
if !enabled {
|
||||
return None;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ use super::performance::Performance;
|
|||
use super::tsc;
|
||||
use super::tsc::TsServer;
|
||||
use super::urls::url_to_uri;
|
||||
use super::urls::LspClientUrl;
|
||||
use super::urls::LspUrlMap;
|
||||
|
||||
use crate::graph_util;
|
||||
|
@ -164,15 +163,14 @@ impl DiagnosticsPublisher {
|
|||
.state
|
||||
.update(&record.specifier, version, &all_specifier_diagnostics);
|
||||
let file_referrer = documents.get_file_referrer(&record.specifier);
|
||||
let Ok(uri) =
|
||||
url_map.specifier_to_uri(&record.specifier, file_referrer.as_deref())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
self
|
||||
.client
|
||||
.publish_diagnostics(
|
||||
url_map
|
||||
.normalize_specifier(&record.specifier, file_referrer.as_deref())
|
||||
.unwrap_or(LspClientUrl::new(record.specifier)),
|
||||
all_specifier_diagnostics,
|
||||
version,
|
||||
)
|
||||
.publish_diagnostics(uri, all_specifier_diagnostics, version)
|
||||
.await;
|
||||
messages_sent += 1;
|
||||
}
|
||||
|
@ -195,15 +193,14 @@ impl DiagnosticsPublisher {
|
|||
// clear out any diagnostics for this specifier
|
||||
self.state.update(specifier, removed_value.version, &[]);
|
||||
let file_referrer = documents.get_file_referrer(specifier);
|
||||
let Ok(uri) =
|
||||
url_map.specifier_to_uri(specifier, file_referrer.as_deref())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
self
|
||||
.client
|
||||
.publish_diagnostics(
|
||||
url_map
|
||||
.normalize_specifier(specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| LspClientUrl::new(specifier.clone())),
|
||||
Vec::new(),
|
||||
removed_value.version,
|
||||
)
|
||||
.publish_diagnostics(uri, Vec::new(), removed_value.version)
|
||||
.await;
|
||||
messages_sent += 1;
|
||||
}
|
||||
|
@ -1074,7 +1071,7 @@ impl DenoDiagnostic {
|
|||
diagnostics: Some(vec![diagnostic.clone()]),
|
||||
edit: Some(lsp::WorkspaceEdit {
|
||||
changes: Some(HashMap::from([(
|
||||
url_to_uri(specifier),
|
||||
url_to_uri(specifier)?,
|
||||
vec![lsp::TextEdit {
|
||||
new_text: format!("\"{to}\""),
|
||||
range: diagnostic.range,
|
||||
|
@ -1091,7 +1088,7 @@ impl DenoDiagnostic {
|
|||
diagnostics: Some(vec![diagnostic.clone()]),
|
||||
edit: Some(lsp::WorkspaceEdit {
|
||||
changes: Some(HashMap::from([(
|
||||
url_to_uri(specifier),
|
||||
url_to_uri(specifier)?,
|
||||
vec![lsp::TextEdit {
|
||||
new_text: " with { type: \"json\" }".to_string(),
|
||||
range: lsp::Range {
|
||||
|
@ -1142,7 +1139,7 @@ impl DenoDiagnostic {
|
|||
diagnostics: Some(vec![diagnostic.clone()]),
|
||||
edit: Some(lsp::WorkspaceEdit {
|
||||
changes: Some(HashMap::from([(
|
||||
url_to_uri(specifier),
|
||||
url_to_uri(specifier)?,
|
||||
vec![lsp::TextEdit {
|
||||
new_text: format!(
|
||||
"\"{}\"",
|
||||
|
@ -1168,7 +1165,7 @@ impl DenoDiagnostic {
|
|||
diagnostics: Some(vec![diagnostic.clone()]),
|
||||
edit: Some(lsp::WorkspaceEdit {
|
||||
changes: Some(HashMap::from([(
|
||||
url_to_uri(specifier),
|
||||
url_to_uri(specifier)?,
|
||||
vec![lsp::TextEdit {
|
||||
new_text: format!(
|
||||
"\"{}\"",
|
||||
|
@ -1194,7 +1191,7 @@ impl DenoDiagnostic {
|
|||
diagnostics: Some(vec![diagnostic.clone()]),
|
||||
edit: Some(lsp::WorkspaceEdit {
|
||||
changes: Some(HashMap::from([(
|
||||
url_to_uri(specifier),
|
||||
url_to_uri(specifier)?,
|
||||
vec![lsp::TextEdit {
|
||||
new_text: format!("\"node:{}\"", data.specifier),
|
||||
range: diagnostic.range,
|
||||
|
@ -1642,7 +1639,7 @@ mod tests {
|
|||
|
||||
fn mock_config() -> Config {
|
||||
let root_url = resolve_url("file:///").unwrap();
|
||||
let root_uri = url_to_uri(&root_url);
|
||||
let root_uri = url_to_uri(&root_url).unwrap();
|
||||
Config {
|
||||
settings: Arc::new(Settings {
|
||||
unscoped: Arc::new(WorkspaceSettings {
|
||||
|
|
|
@ -60,6 +60,9 @@ pub enum LanguageId {
|
|||
Json,
|
||||
JsonC,
|
||||
Markdown,
|
||||
Html,
|
||||
Css,
|
||||
Yaml,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
|
@ -73,6 +76,9 @@ impl LanguageId {
|
|||
LanguageId::Json => Some("json"),
|
||||
LanguageId::JsonC => Some("jsonc"),
|
||||
LanguageId::Markdown => Some("md"),
|
||||
LanguageId::Html => Some("html"),
|
||||
LanguageId::Css => Some("css"),
|
||||
LanguageId::Yaml => Some("yaml"),
|
||||
LanguageId::Unknown => None,
|
||||
}
|
||||
}
|
||||
|
@ -85,6 +91,9 @@ impl LanguageId {
|
|||
LanguageId::Tsx => Some("text/tsx"),
|
||||
LanguageId::Json | LanguageId::JsonC => Some("application/json"),
|
||||
LanguageId::Markdown => Some("text/markdown"),
|
||||
LanguageId::Html => Some("text/html"),
|
||||
LanguageId::Css => Some("text/css"),
|
||||
LanguageId::Yaml => Some("application/yaml"),
|
||||
LanguageId::Unknown => None,
|
||||
}
|
||||
}
|
||||
|
@ -109,6 +118,9 @@ impl FromStr for LanguageId {
|
|||
"json" => Ok(Self::Json),
|
||||
"jsonc" => Ok(Self::JsonC),
|
||||
"markdown" => Ok(Self::Markdown),
|
||||
"html" => Ok(Self::Html),
|
||||
"css" => Ok(Self::Css),
|
||||
"yaml" => Ok(Self::Yaml),
|
||||
_ => Ok(Self::Unknown),
|
||||
}
|
||||
}
|
||||
|
@ -1239,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<Option<(ModuleSpecifier, MediaType)>> {
|
||||
|
@ -1250,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(
|
||||
|
@ -1278,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(),
|
||||
|
@ -1410,11 +1422,9 @@ impl Documents {
|
|||
if let Some(lockfile) = config_data.lockfile.as_ref() {
|
||||
let reqs = npm_reqs_by_scope.entry(Some(scope.clone())).or_default();
|
||||
let lockfile = lockfile.lock();
|
||||
for key in lockfile.content.packages.specifiers.keys() {
|
||||
if let Some(key) = key.strip_prefix("npm:") {
|
||||
if let Ok(req) = PackageReq::from_str(key) {
|
||||
reqs.insert(req);
|
||||
}
|
||||
for dep_req in lockfile.content.packages.specifiers.keys() {
|
||||
if dep_req.kind == deno_semver::package::PackageKind::Npm {
|
||||
reqs.insert(dep_req.req.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1514,12 +1524,16 @@ impl<'a> deno_graph::source::Loader for OpenDocumentsGraphLoader<'a> {
|
|||
fn cache_module_info(
|
||||
&self,
|
||||
specifier: &deno_ast::ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
source: &Arc<[u8]>,
|
||||
module_info: &deno_graph::ModuleInfo,
|
||||
) {
|
||||
self
|
||||
.inner_loader
|
||||
.cache_module_info(specifier, source, module_info)
|
||||
self.inner_loader.cache_module_info(
|
||||
specifier,
|
||||
media_type,
|
||||
source,
|
||||
module_info,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -92,20 +92,23 @@ impl JsrCacheResolver {
|
|||
}
|
||||
}
|
||||
if let Some(lockfile) = config_data.and_then(|d| d.lockfile.as_ref()) {
|
||||
for (req_url, nv_url) in &lockfile.lock().content.packages.specifiers {
|
||||
let Some(req) = req_url.strip_prefix("jsr:") else {
|
||||
for (dep_req, version) in &lockfile.lock().content.packages.specifiers {
|
||||
let req = match dep_req.kind {
|
||||
deno_semver::package::PackageKind::Jsr => &dep_req.req,
|
||||
deno_semver::package::PackageKind::Npm => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let Ok(version) = Version::parse_standard(version) else {
|
||||
continue;
|
||||
};
|
||||
let Some(nv) = nv_url.strip_prefix("jsr:") else {
|
||||
continue;
|
||||
};
|
||||
let Ok(req) = PackageReq::from_str(req) else {
|
||||
continue;
|
||||
};
|
||||
let Ok(nv) = PackageNv::from_str(nv) else {
|
||||
continue;
|
||||
};
|
||||
nv_by_req.insert(req, Some(nv));
|
||||
nv_by_req.insert(
|
||||
req.clone(),
|
||||
Some(PackageNv {
|
||||
name: req.name.clone(),
|
||||
version,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
Self {
|
||||
|
|
|
@ -32,6 +32,7 @@ use std::collections::VecDeque;
|
|||
use std::env;
|
||||
use std::fmt::Write as _;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc::unbounded_channel;
|
||||
use tokio::sync::mpsc::UnboundedReceiver;
|
||||
|
@ -723,7 +724,9 @@ impl Inner {
|
|||
.into_iter()
|
||||
.map(|folder| {
|
||||
(
|
||||
self.url_map.normalize_url(&folder.uri, LspUrlKind::Folder),
|
||||
self
|
||||
.url_map
|
||||
.uri_to_specifier(&folder.uri, LspUrlKind::Folder),
|
||||
folder,
|
||||
)
|
||||
})
|
||||
|
@ -735,7 +738,7 @@ impl Inner {
|
|||
if let Some(root_uri) = params.root_uri {
|
||||
if !workspace_folders.iter().any(|(_, f)| f.uri == root_uri) {
|
||||
let root_url =
|
||||
self.url_map.normalize_url(&root_uri, LspUrlKind::Folder);
|
||||
self.url_map.uri_to_specifier(&root_uri, LspUrlKind::Folder);
|
||||
let name = root_url.path_segments().and_then(|s| s.last());
|
||||
let name = name.unwrap_or_default().to_string();
|
||||
workspace_folders.insert(
|
||||
|
@ -963,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!(
|
||||
|
@ -1043,7 +1045,7 @@ impl Inner {
|
|||
.filter(|s| self.documents.is_valid_file_referrer(s));
|
||||
let specifier = self
|
||||
.url_map
|
||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
.uri_to_specifier(¶ms.text_document.uri, LspUrlKind::File);
|
||||
let document = self.documents.open(
|
||||
specifier.clone(),
|
||||
params.text_document.version,
|
||||
|
@ -1065,7 +1067,7 @@ impl Inner {
|
|||
let mark = self.performance.mark_with_args("lsp.did_change", ¶ms);
|
||||
let specifier = self
|
||||
.url_map
|
||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
.uri_to_specifier(¶ms.text_document.uri, LspUrlKind::File);
|
||||
match self.documents.change(
|
||||
&specifier,
|
||||
params.text_document.version,
|
||||
|
@ -1102,7 +1104,7 @@ impl Inner {
|
|||
let _mark = self.performance.measure_scope("lsp.did_save");
|
||||
let specifier = self
|
||||
.url_map
|
||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
.uri_to_specifier(¶ms.text_document.uri, LspUrlKind::File);
|
||||
self.documents.save(&specifier);
|
||||
if !self
|
||||
.config
|
||||
|
@ -1148,7 +1150,7 @@ impl Inner {
|
|||
}
|
||||
let specifier = self
|
||||
.url_map
|
||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
.uri_to_specifier(¶ms.text_document.uri, LspUrlKind::File);
|
||||
self.diagnostics_state.clear(&specifier);
|
||||
if self.is_diagnosable(&specifier) {
|
||||
self.refresh_npm_specifiers().await;
|
||||
|
@ -1202,7 +1204,7 @@ impl Inner {
|
|||
let changes = params
|
||||
.changes
|
||||
.into_iter()
|
||||
.map(|e| (self.url_map.normalize_url(&e.uri, LspUrlKind::File), e))
|
||||
.map(|e| (self.url_map.uri_to_specifier(&e.uri, LspUrlKind::File), e))
|
||||
.collect::<Vec<_>>();
|
||||
if changes
|
||||
.iter()
|
||||
|
@ -1221,7 +1223,7 @@ impl Inner {
|
|||
_ => return None,
|
||||
};
|
||||
Some(lsp_custom::DenoConfigurationChangeEvent {
|
||||
scope_uri: url_to_uri(t.0),
|
||||
scope_uri: url_to_uri(t.0).ok()?,
|
||||
file_uri: e.uri.clone(),
|
||||
typ: lsp_custom::DenoConfigurationChangeType::from_file_change_type(
|
||||
e.typ,
|
||||
|
@ -1256,7 +1258,7 @@ impl Inner {
|
|||
_ => return None,
|
||||
};
|
||||
Some(lsp_custom::DenoConfigurationChangeEvent {
|
||||
scope_uri: url_to_uri(t.0),
|
||||
scope_uri: url_to_uri(t.0).ok()?,
|
||||
file_uri: e.uri.clone(),
|
||||
typ: lsp_custom::DenoConfigurationChangeType::from_file_change_type(
|
||||
e.typ,
|
||||
|
@ -1282,7 +1284,7 @@ impl Inner {
|
|||
) -> LspResult<Option<DocumentSymbolResponse>> {
|
||||
let specifier = self
|
||||
.url_map
|
||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
.uri_to_specifier(¶ms.text_document.uri, LspUrlKind::File);
|
||||
if !self.is_diagnosable(&specifier)
|
||||
|| !self.config.specifier_enabled(&specifier)
|
||||
{
|
||||
|
@ -1326,7 +1328,7 @@ impl Inner {
|
|||
.filter(|s| self.documents.is_valid_file_referrer(s));
|
||||
let mut specifier = self
|
||||
.url_map
|
||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
.uri_to_specifier(¶ms.text_document.uri, LspUrlKind::File);
|
||||
// skip formatting any files ignored by the config file
|
||||
if !self
|
||||
.config
|
||||
|
@ -1441,7 +1443,7 @@ impl Inner {
|
|||
}
|
||||
|
||||
async fn hover(&self, params: HoverParams) -> LspResult<Option<Hover>> {
|
||||
let specifier = self.url_map.normalize_url(
|
||||
let specifier = self.url_map.uri_to_specifier(
|
||||
¶ms.text_document_position_params.text_document.uri,
|
||||
LspUrlKind::File,
|
||||
);
|
||||
|
@ -1574,7 +1576,7 @@ impl Inner {
|
|||
) -> LspResult<Option<CodeActionResponse>> {
|
||||
let specifier = self
|
||||
.url_map
|
||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
.uri_to_specifier(¶ms.text_document.uri, LspUrlKind::File);
|
||||
if !self.is_diagnosable(&specifier)
|
||||
|| !self.config.specifier_enabled(&specifier)
|
||||
{
|
||||
|
@ -1918,7 +1920,7 @@ impl Inner {
|
|||
) -> LspResult<Option<Vec<CodeLens>>> {
|
||||
let specifier = self
|
||||
.url_map
|
||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
.uri_to_specifier(¶ms.text_document.uri, LspUrlKind::File);
|
||||
if !self.is_diagnosable(&specifier)
|
||||
|| !self.config.specifier_enabled(&specifier)
|
||||
{
|
||||
|
@ -2004,7 +2006,7 @@ impl Inner {
|
|||
&self,
|
||||
params: DocumentHighlightParams,
|
||||
) -> LspResult<Option<Vec<DocumentHighlight>>> {
|
||||
let specifier = self.url_map.normalize_url(
|
||||
let specifier = self.url_map.uri_to_specifier(
|
||||
¶ms.text_document_position_params.text_document.uri,
|
||||
LspUrlKind::File,
|
||||
);
|
||||
|
@ -2048,7 +2050,7 @@ impl Inner {
|
|||
&self,
|
||||
params: ReferenceParams,
|
||||
) -> LspResult<Option<Vec<Location>>> {
|
||||
let specifier = self.url_map.normalize_url(
|
||||
let specifier = self.url_map.uri_to_specifier(
|
||||
¶ms.text_document_position.text_document.uri,
|
||||
LspUrlKind::File,
|
||||
);
|
||||
|
@ -2104,7 +2106,7 @@ impl Inner {
|
|||
&self,
|
||||
params: GotoDefinitionParams,
|
||||
) -> LspResult<Option<GotoDefinitionResponse>> {
|
||||
let specifier = self.url_map.normalize_url(
|
||||
let specifier = self.url_map.uri_to_specifier(
|
||||
¶ms.text_document_position_params.text_document.uri,
|
||||
LspUrlKind::File,
|
||||
);
|
||||
|
@ -2143,7 +2145,7 @@ impl Inner {
|
|||
&self,
|
||||
params: GotoTypeDefinitionParams,
|
||||
) -> LspResult<Option<GotoTypeDefinitionResponse>> {
|
||||
let specifier = self.url_map.normalize_url(
|
||||
let specifier = self.url_map.uri_to_specifier(
|
||||
¶ms.text_document_position_params.text_document.uri,
|
||||
LspUrlKind::File,
|
||||
);
|
||||
|
@ -2189,7 +2191,7 @@ impl Inner {
|
|||
&self,
|
||||
params: CompletionParams,
|
||||
) -> LspResult<Option<CompletionResponse>> {
|
||||
let specifier = self.url_map.normalize_url(
|
||||
let specifier = self.url_map.uri_to_specifier(
|
||||
¶ms.text_document_position.text_document.uri,
|
||||
LspUrlKind::File,
|
||||
);
|
||||
|
@ -2378,7 +2380,7 @@ impl Inner {
|
|||
&self,
|
||||
params: GotoImplementationParams,
|
||||
) -> LspResult<Option<GotoImplementationResponse>> {
|
||||
let specifier = self.url_map.normalize_url(
|
||||
let specifier = self.url_map.uri_to_specifier(
|
||||
¶ms.text_document_position_params.text_document.uri,
|
||||
LspUrlKind::File,
|
||||
);
|
||||
|
@ -2429,7 +2431,7 @@ impl Inner {
|
|||
) -> LspResult<Option<Vec<FoldingRange>>> {
|
||||
let specifier = self
|
||||
.url_map
|
||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
.uri_to_specifier(¶ms.text_document.uri, LspUrlKind::File);
|
||||
if !self.is_diagnosable(&specifier)
|
||||
|| !self.config.specifier_enabled(&specifier)
|
||||
{
|
||||
|
@ -2476,7 +2478,7 @@ impl Inner {
|
|||
) -> LspResult<Option<Vec<CallHierarchyIncomingCall>>> {
|
||||
let specifier = self
|
||||
.url_map
|
||||
.normalize_url(¶ms.item.uri, LspUrlKind::File);
|
||||
.uri_to_specifier(¶ms.item.uri, LspUrlKind::File);
|
||||
if !self.is_diagnosable(&specifier)
|
||||
|| !self.config.specifier_enabled(&specifier)
|
||||
{
|
||||
|
@ -2525,7 +2527,7 @@ impl Inner {
|
|||
) -> LspResult<Option<Vec<CallHierarchyOutgoingCall>>> {
|
||||
let specifier = self
|
||||
.url_map
|
||||
.normalize_url(¶ms.item.uri, LspUrlKind::File);
|
||||
.uri_to_specifier(¶ms.item.uri, LspUrlKind::File);
|
||||
if !self.is_diagnosable(&specifier)
|
||||
|| !self.config.specifier_enabled(&specifier)
|
||||
{
|
||||
|
@ -2570,7 +2572,7 @@ impl Inner {
|
|||
&self,
|
||||
params: CallHierarchyPrepareParams,
|
||||
) -> LspResult<Option<Vec<CallHierarchyItem>>> {
|
||||
let specifier = self.url_map.normalize_url(
|
||||
let specifier = self.url_map.uri_to_specifier(
|
||||
¶ms.text_document_position_params.text_document.uri,
|
||||
LspUrlKind::File,
|
||||
);
|
||||
|
@ -2634,7 +2636,7 @@ impl Inner {
|
|||
&self,
|
||||
params: RenameParams,
|
||||
) -> LspResult<Option<WorkspaceEdit>> {
|
||||
let specifier = self.url_map.normalize_url(
|
||||
let specifier = self.url_map.uri_to_specifier(
|
||||
¶ms.text_document_position.text_document.uri,
|
||||
LspUrlKind::File,
|
||||
);
|
||||
|
@ -2683,7 +2685,7 @@ impl Inner {
|
|||
) -> LspResult<Option<Vec<SelectionRange>>> {
|
||||
let specifier = self
|
||||
.url_map
|
||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
.uri_to_specifier(¶ms.text_document.uri, LspUrlKind::File);
|
||||
if !self.is_diagnosable(&specifier)
|
||||
|| !self.config.specifier_enabled(&specifier)
|
||||
{
|
||||
|
@ -2721,7 +2723,7 @@ impl Inner {
|
|||
) -> LspResult<Option<SemanticTokensResult>> {
|
||||
let specifier = self
|
||||
.url_map
|
||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
.uri_to_specifier(¶ms.text_document.uri, LspUrlKind::File);
|
||||
if !self.is_diagnosable(&specifier) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
@ -2774,7 +2776,7 @@ impl Inner {
|
|||
) -> LspResult<Option<SemanticTokensRangeResult>> {
|
||||
let specifier = self
|
||||
.url_map
|
||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
.uri_to_specifier(¶ms.text_document.uri, LspUrlKind::File);
|
||||
if !self.is_diagnosable(&specifier) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
@ -2823,7 +2825,7 @@ impl Inner {
|
|||
&self,
|
||||
params: SignatureHelpParams,
|
||||
) -> LspResult<Option<SignatureHelp>> {
|
||||
let specifier = self.url_map.normalize_url(
|
||||
let specifier = self.url_map.uri_to_specifier(
|
||||
¶ms.text_document_position_params.text_document.uri,
|
||||
LspUrlKind::File,
|
||||
);
|
||||
|
@ -2877,8 +2879,8 @@ impl Inner {
|
|||
) -> LspResult<Option<WorkspaceEdit>> {
|
||||
let mut changes = vec![];
|
||||
for rename in params.files {
|
||||
let old_specifier = self.url_map.normalize_url(
|
||||
&url_to_uri(&resolve_url(&rename.old_uri).unwrap()),
|
||||
let old_specifier = self.url_map.uri_to_specifier(
|
||||
&Uri::from_str(&rename.old_uri).unwrap(),
|
||||
LspUrlKind::File,
|
||||
);
|
||||
let options = self
|
||||
|
@ -2903,8 +2905,8 @@ impl Inner {
|
|||
.get_edits_for_file_rename(
|
||||
self.snapshot(),
|
||||
old_specifier,
|
||||
self.url_map.normalize_url(
|
||||
&url_to_uri(&resolve_url(&rename.new_uri).unwrap()),
|
||||
self.url_map.uri_to_specifier(
|
||||
&Uri::from_str(&rename.new_uri).unwrap(),
|
||||
LspUrlKind::File,
|
||||
),
|
||||
format_code_settings,
|
||||
|
@ -3503,22 +3505,28 @@ impl Inner {
|
|||
|
||||
let mut config_events = vec![];
|
||||
for (scope_url, config_data) in self.config.tree.data_by_scope().iter() {
|
||||
let scope_uri = url_to_uri(scope_url);
|
||||
let Ok(scope_uri) = url_to_uri(scope_url) else {
|
||||
continue;
|
||||
};
|
||||
if let Some(config_file) = config_data.maybe_deno_json() {
|
||||
config_events.push(lsp_custom::DenoConfigurationChangeEvent {
|
||||
scope_uri: scope_uri.clone(),
|
||||
file_uri: url_to_uri(&config_file.specifier),
|
||||
typ: lsp_custom::DenoConfigurationChangeType::Added,
|
||||
configuration_type: lsp_custom::DenoConfigurationType::DenoJson,
|
||||
});
|
||||
if let Ok(file_uri) = url_to_uri(&config_file.specifier) {
|
||||
config_events.push(lsp_custom::DenoConfigurationChangeEvent {
|
||||
scope_uri: scope_uri.clone(),
|
||||
file_uri,
|
||||
typ: lsp_custom::DenoConfigurationChangeType::Added,
|
||||
configuration_type: lsp_custom::DenoConfigurationType::DenoJson,
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(package_json) = config_data.maybe_pkg_json() {
|
||||
config_events.push(lsp_custom::DenoConfigurationChangeEvent {
|
||||
scope_uri,
|
||||
file_uri: url_to_uri(&package_json.specifier()),
|
||||
typ: lsp_custom::DenoConfigurationChangeType::Added,
|
||||
configuration_type: lsp_custom::DenoConfigurationType::PackageJson,
|
||||
});
|
||||
if let Ok(file_uri) = url_to_uri(&package_json.specifier()) {
|
||||
config_events.push(lsp_custom::DenoConfigurationChangeEvent {
|
||||
scope_uri,
|
||||
file_uri,
|
||||
typ: lsp_custom::DenoConfigurationChangeType::Added,
|
||||
configuration_type: lsp_custom::DenoConfigurationType::PackageJson,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if !config_events.is_empty() {
|
||||
|
@ -3602,11 +3610,6 @@ impl Inner {
|
|||
.as_ref()
|
||||
.map(|url| url.to_string())
|
||||
}),
|
||||
node_modules_dir: Some(
|
||||
config_data
|
||||
.and_then(|d| d.node_modules_dir.as_ref())
|
||||
.is_some(),
|
||||
),
|
||||
// bit of a hack to force the lsp to cache the @types/node package
|
||||
type_check_mode: crate::args::TypeCheckMode::Local,
|
||||
..Default::default()
|
||||
|
@ -3648,7 +3651,9 @@ impl Inner {
|
|||
.into_iter()
|
||||
.map(|folder| {
|
||||
(
|
||||
self.url_map.normalize_url(&folder.uri, LspUrlKind::Folder),
|
||||
self
|
||||
.url_map
|
||||
.uri_to_specifier(&folder.uri, LspUrlKind::Folder),
|
||||
folder,
|
||||
)
|
||||
})
|
||||
|
@ -3724,7 +3729,8 @@ impl Inner {
|
|||
result.push(TaskDefinition {
|
||||
name: name.clone(),
|
||||
command: command.to_string(),
|
||||
source_uri: url_to_uri(&config_file.specifier),
|
||||
source_uri: url_to_uri(&config_file.specifier)
|
||||
.map_err(|_| LspError::internal_error())?,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -3735,7 +3741,8 @@ impl Inner {
|
|||
result.push(TaskDefinition {
|
||||
name: name.clone(),
|
||||
command: command.clone(),
|
||||
source_uri: url_to_uri(&package_json.specifier()),
|
||||
source_uri: url_to_uri(&package_json.specifier())
|
||||
.map_err(|_| LspError::internal_error())?,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -3750,7 +3757,7 @@ impl Inner {
|
|||
) -> LspResult<Option<Vec<InlayHint>>> {
|
||||
let specifier = self
|
||||
.url_map
|
||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
.uri_to_specifier(¶ms.text_document.uri, LspUrlKind::File);
|
||||
if !self.is_diagnosable(&specifier)
|
||||
|| !self.config.specifier_enabled(&specifier)
|
||||
|| !self.config.enabled_inlay_hints_for_specifier(&specifier)
|
||||
|
@ -3813,7 +3820,7 @@ impl Inner {
|
|||
.mark_with_args("lsp.virtual_text_document", ¶ms);
|
||||
let specifier = self
|
||||
.url_map
|
||||
.normalize_url(¶ms.text_document.uri, LspUrlKind::File);
|
||||
.uri_to_specifier(¶ms.text_document.uri, LspUrlKind::File);
|
||||
let contents = if specifier.scheme() == "deno"
|
||||
&& specifier.path() == "/status.md"
|
||||
{
|
||||
|
|
|
@ -76,7 +76,7 @@ impl ReplLanguageServer {
|
|||
.initialize(InitializeParams {
|
||||
process_id: None,
|
||||
root_path: None,
|
||||
root_uri: Some(url_to_uri(&cwd_uri)),
|
||||
root_uri: Some(url_to_uri(&cwd_uri).unwrap()),
|
||||
initialization_options: Some(
|
||||
serde_json::to_value(get_repl_workspace_settings()).unwrap(),
|
||||
),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::tools::test::TestDescription;
|
|||
use crate::tools::test::TestStepDescription;
|
||||
use crate::util::checksum;
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use lsp::Range;
|
||||
use std::collections::HashMap;
|
||||
|
@ -144,21 +145,23 @@ impl TestModule {
|
|||
pub fn as_replace_notification(
|
||||
&self,
|
||||
maybe_root_uri: Option<&ModuleSpecifier>,
|
||||
) -> TestingNotification {
|
||||
) -> Result<TestingNotification, AnyError> {
|
||||
let label = self.label(maybe_root_uri);
|
||||
TestingNotification::Module(lsp_custom::TestModuleNotificationParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(&self.specifier),
|
||||
Ok(TestingNotification::Module(
|
||||
lsp_custom::TestModuleNotificationParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(&self.specifier)?,
|
||||
},
|
||||
kind: lsp_custom::TestModuleNotificationKind::Replace,
|
||||
label,
|
||||
tests: self
|
||||
.defs
|
||||
.iter()
|
||||
.filter(|(_, def)| def.parent_id.is_none())
|
||||
.map(|(id, _)| self.get_test_data(id))
|
||||
.collect(),
|
||||
},
|
||||
kind: lsp_custom::TestModuleNotificationKind::Replace,
|
||||
label,
|
||||
tests: self
|
||||
.defs
|
||||
.iter()
|
||||
.filter(|(_, def)| def.parent_id.is_none())
|
||||
.map(|(id, _)| self.get_test_data(id))
|
||||
.collect(),
|
||||
})
|
||||
))
|
||||
}
|
||||
|
||||
pub fn label(&self, maybe_root_uri: Option<&ModuleSpecifier>) -> String {
|
||||
|
|
|
@ -186,7 +186,7 @@ impl TestRun {
|
|||
self
|
||||
.queue
|
||||
.iter()
|
||||
.map(|s| {
|
||||
.filter_map(|s| {
|
||||
let ids = if let Some((test_module, _)) = tests.get(s) {
|
||||
if let Some(filter) = self.filters.get(s) {
|
||||
filter.as_ids(test_module)
|
||||
|
@ -196,10 +196,12 @@ impl TestRun {
|
|||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
lsp_custom::EnqueuedTestModule {
|
||||
text_document: lsp::TextDocumentIdentifier { uri: url_to_uri(s) },
|
||||
Some(lsp_custom::EnqueuedTestModule {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(s).ok()?,
|
||||
},
|
||||
ids,
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
@ -591,6 +593,9 @@ impl LspTestReporter {
|
|||
let (test_module, _) = files
|
||||
.entry(specifier.clone())
|
||||
.or_insert_with(|| (TestModule::new(specifier), "1".to_string()));
|
||||
let Ok(uri) = url_to_uri(&test_module.specifier) else {
|
||||
return;
|
||||
};
|
||||
let (static_id, is_new) = test_module.register_dynamic(desc);
|
||||
self.tests.insert(
|
||||
desc.id,
|
||||
|
@ -601,9 +606,7 @@ impl LspTestReporter {
|
|||
.client
|
||||
.send_test_notification(TestingNotification::Module(
|
||||
lsp_custom::TestModuleNotificationParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(&test_module.specifier),
|
||||
},
|
||||
text_document: lsp::TextDocumentIdentifier { uri },
|
||||
kind: lsp_custom::TestModuleNotificationKind::Insert,
|
||||
label: test_module.label(self.maybe_root_uri.as_ref()),
|
||||
tests: vec![test_module.get_test_data(&static_id)],
|
||||
|
@ -701,6 +704,9 @@ impl LspTestReporter {
|
|||
let (test_module, _) = files
|
||||
.entry(specifier.clone())
|
||||
.or_insert_with(|| (TestModule::new(specifier), "1".to_string()));
|
||||
let Ok(uri) = url_to_uri(&test_module.specifier) else {
|
||||
return;
|
||||
};
|
||||
let (static_id, is_new) = test_module.register_step_dynamic(
|
||||
desc,
|
||||
self.tests.get(&desc.parent_id).unwrap().static_id(),
|
||||
|
@ -714,9 +720,7 @@ impl LspTestReporter {
|
|||
.client
|
||||
.send_test_notification(TestingNotification::Module(
|
||||
lsp_custom::TestModuleNotificationParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(&test_module.specifier),
|
||||
},
|
||||
text_document: lsp::TextDocumentIdentifier { uri },
|
||||
kind: lsp_custom::TestModuleNotificationKind::Insert,
|
||||
label: test_module.label(self.maybe_root_uri.as_ref()),
|
||||
tests: vec![test_module.get_test_data(&static_id)],
|
||||
|
@ -800,14 +804,14 @@ mod tests {
|
|||
include: Some(vec![
|
||||
lsp_custom::TestIdentifier {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(&specifier),
|
||||
uri: url_to_uri(&specifier).unwrap(),
|
||||
},
|
||||
id: None,
|
||||
step_id: None,
|
||||
},
|
||||
lsp_custom::TestIdentifier {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(&non_test_specifier),
|
||||
uri: url_to_uri(&non_test_specifier).unwrap(),
|
||||
},
|
||||
id: None,
|
||||
step_id: None,
|
||||
|
@ -815,7 +819,7 @@ mod tests {
|
|||
]),
|
||||
exclude: vec![lsp_custom::TestIdentifier {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(&specifier),
|
||||
uri: url_to_uri(&specifier).unwrap(),
|
||||
},
|
||||
id: Some(
|
||||
"69d9fe87f64f5b66cb8b631d4fd2064e8224b8715a049be54276c42189ff8f9f"
|
||||
|
|
|
@ -27,14 +27,16 @@ use tower_lsp::jsonrpc::Error as LspError;
|
|||
use tower_lsp::jsonrpc::Result as LspResult;
|
||||
use tower_lsp::lsp_types as lsp;
|
||||
|
||||
fn as_delete_notification(url: ModuleSpecifier) -> TestingNotification {
|
||||
TestingNotification::DeleteModule(
|
||||
fn as_delete_notification(
|
||||
url: &ModuleSpecifier,
|
||||
) -> Result<TestingNotification, AnyError> {
|
||||
Ok(TestingNotification::DeleteModule(
|
||||
lsp_custom::TestModuleDeleteNotificationParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(&url),
|
||||
uri: url_to_uri(url)?,
|
||||
},
|
||||
},
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
pub type TestServerTests =
|
||||
|
@ -126,20 +128,24 @@ impl TestServer {
|
|||
.map(|tm| tm.as_ref().clone())
|
||||
.unwrap_or_else(|| TestModule::new(specifier.clone()));
|
||||
if !test_module.is_empty() {
|
||||
client.send_test_notification(
|
||||
test_module.as_replace_notification(mru.as_ref()),
|
||||
);
|
||||
if let Ok(params) =
|
||||
test_module.as_replace_notification(mru.as_ref())
|
||||
{
|
||||
client.send_test_notification(params);
|
||||
}
|
||||
} else if !was_empty {
|
||||
client.send_test_notification(as_delete_notification(
|
||||
specifier.clone(),
|
||||
));
|
||||
if let Ok(params) = as_delete_notification(specifier) {
|
||||
client.send_test_notification(params);
|
||||
}
|
||||
}
|
||||
tests
|
||||
.insert(specifier.clone(), (test_module, script_version));
|
||||
}
|
||||
}
|
||||
for key in keys {
|
||||
client.send_test_notification(as_delete_notification(key));
|
||||
for key in &keys {
|
||||
if let Ok(params) = as_delete_notification(key) {
|
||||
client.send_test_notification(params);
|
||||
}
|
||||
}
|
||||
performance.measure(mark);
|
||||
}
|
||||
|
|
|
@ -19,9 +19,10 @@ use super::refactor::EXTRACT_TYPE;
|
|||
use super::semantic_tokens;
|
||||
use super::semantic_tokens::SemanticTokensBuilder;
|
||||
use super::text::LineIndex;
|
||||
use super::urls::uri_to_url;
|
||||
use super::urls::url_to_uri;
|
||||
use super::urls::LspClientUrl;
|
||||
use super::urls::INVALID_SPECIFIER;
|
||||
use super::urls::INVALID_URI;
|
||||
|
||||
use crate::args::jsr_url;
|
||||
use crate::args::FmtOptionsConfig;
|
||||
|
@ -2047,7 +2048,7 @@ impl DocumentSpan {
|
|||
let file_referrer = target_asset_or_doc.file_referrer();
|
||||
let target_uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&target_specifier, file_referrer)
|
||||
.specifier_to_uri(&target_specifier, file_referrer)
|
||||
.ok()?;
|
||||
let (target_range, target_selection_range) =
|
||||
if let Some(context_span) = &self.context_span {
|
||||
|
@ -2072,7 +2073,7 @@ impl DocumentSpan {
|
|||
};
|
||||
let link = lsp::LocationLink {
|
||||
origin_selection_range,
|
||||
target_uri: target_uri.to_uri(),
|
||||
target_uri,
|
||||
target_range,
|
||||
target_selection_range,
|
||||
};
|
||||
|
@ -2092,11 +2093,11 @@ impl DocumentSpan {
|
|||
let line_index = asset_or_doc.line_index();
|
||||
let range = self.text_span.to_range(line_index);
|
||||
let file_referrer = asset_or_doc.file_referrer();
|
||||
let mut target = language_server
|
||||
let target_uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&specifier, file_referrer)
|
||||
.ok()?
|
||||
.into_url();
|
||||
.specifier_to_uri(&specifier, file_referrer)
|
||||
.ok()?;
|
||||
let mut target = uri_to_url(&target_uri);
|
||||
target.set_fragment(Some(&format!(
|
||||
"L{},{}",
|
||||
range.start.line + 1,
|
||||
|
@ -2155,13 +2156,10 @@ impl NavigateToItem {
|
|||
let file_referrer = asset_or_doc.file_referrer();
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&specifier, file_referrer)
|
||||
.specifier_to_uri(&specifier, file_referrer)
|
||||
.ok()?;
|
||||
let range = self.text_span.to_range(line_index);
|
||||
let location = lsp::Location {
|
||||
uri: uri.to_uri(),
|
||||
range,
|
||||
};
|
||||
let location = lsp::Location { uri, range };
|
||||
|
||||
let mut tags: Option<Vec<lsp::SymbolTag>> = None;
|
||||
let kind_modifiers = parse_kind_modifier(&self.kind_modifiers);
|
||||
|
@ -2414,12 +2412,10 @@ impl ImplementationLocation {
|
|||
let file_referrer = language_server.documents.get_file_referrer(&specifier);
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| {
|
||||
LspClientUrl::new(ModuleSpecifier::parse("deno://invalid").unwrap())
|
||||
});
|
||||
.specifier_to_uri(&specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| INVALID_URI.clone());
|
||||
lsp::Location {
|
||||
uri: uri.to_uri(),
|
||||
uri,
|
||||
range: self.document_span.text_span.to_range(line_index),
|
||||
}
|
||||
}
|
||||
|
@ -2475,7 +2471,7 @@ impl RenameLocations {
|
|||
language_server.documents.get_file_referrer(&specifier);
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&specifier, file_referrer.as_deref())?;
|
||||
.specifier_to_uri(&specifier, file_referrer.as_deref())?;
|
||||
let asset_or_doc = language_server.get_asset_or_document(&specifier)?;
|
||||
|
||||
// ensure TextDocumentEdit for `location.file_name`.
|
||||
|
@ -2484,7 +2480,7 @@ impl RenameLocations {
|
|||
uri.clone(),
|
||||
lsp::TextDocumentEdit {
|
||||
text_document: lsp::OptionalVersionedTextDocumentIdentifier {
|
||||
uri: uri.to_uri(),
|
||||
uri: uri.clone(),
|
||||
version: asset_or_doc.document_lsp_version(),
|
||||
},
|
||||
edits:
|
||||
|
@ -2686,7 +2682,7 @@ impl FileTextChanges {
|
|||
.collect();
|
||||
Ok(lsp::TextDocumentEdit {
|
||||
text_document: lsp::OptionalVersionedTextDocumentIdentifier {
|
||||
uri: url_to_uri(&specifier),
|
||||
uri: url_to_uri(&specifier)?,
|
||||
version: asset_or_doc.document_lsp_version(),
|
||||
},
|
||||
edits,
|
||||
|
@ -2713,7 +2709,7 @@ impl FileTextChanges {
|
|||
if self.is_new_file.unwrap_or(false) {
|
||||
ops.push(lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Create(
|
||||
lsp::CreateFile {
|
||||
uri: url_to_uri(&specifier),
|
||||
uri: url_to_uri(&specifier)?,
|
||||
options: Some(lsp::CreateFileOptions {
|
||||
ignore_if_exists: Some(true),
|
||||
overwrite: None,
|
||||
|
@ -2730,7 +2726,7 @@ impl FileTextChanges {
|
|||
.collect();
|
||||
ops.push(lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit {
|
||||
text_document: lsp::OptionalVersionedTextDocumentIdentifier {
|
||||
uri: url_to_uri(&specifier),
|
||||
uri: url_to_uri(&specifier)?,
|
||||
version: maybe_asset_or_document.and_then(|d| d.document_lsp_version()),
|
||||
},
|
||||
edits,
|
||||
|
@ -3128,10 +3124,10 @@ impl ReferenceEntry {
|
|||
let file_referrer = language_server.documents.get_file_referrer(&specifier);
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| LspClientUrl::new(INVALID_SPECIFIER.clone()));
|
||||
.specifier_to_uri(&specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| INVALID_URI.clone());
|
||||
lsp::Location {
|
||||
uri: uri.to_uri(),
|
||||
uri,
|
||||
range: self.document_span.text_span.to_range(line_index),
|
||||
}
|
||||
}
|
||||
|
@ -3189,12 +3185,13 @@ impl CallHierarchyItem {
|
|||
.get_file_referrer(&target_specifier);
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&target_specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| LspClientUrl::new(INVALID_SPECIFIER.clone()));
|
||||
.specifier_to_uri(&target_specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| INVALID_URI.clone());
|
||||
|
||||
let use_file_name = self.is_source_file_item();
|
||||
let maybe_file_path = if uri.as_url().scheme() == "file" {
|
||||
specifier_to_file_path(uri.as_url()).ok()
|
||||
let maybe_file_path = if uri.scheme().is_some_and(|s| s.as_str() == "file")
|
||||
{
|
||||
specifier_to_file_path(&uri_to_url(&uri)).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -3238,7 +3235,7 @@ impl CallHierarchyItem {
|
|||
lsp::CallHierarchyItem {
|
||||
name,
|
||||
tags,
|
||||
uri: uri.to_uri(),
|
||||
uri,
|
||||
detail: Some(detail),
|
||||
kind: self.kind.clone().into(),
|
||||
range: self.span.to_range(line_index.clone()),
|
||||
|
|
166
cli/lsp/urls.rs
166
cli/lsp/urls.rs
|
@ -13,12 +13,18 @@ use std::str::FromStr;
|
|||
use std::sync::Arc;
|
||||
|
||||
use super::cache::LspCache;
|
||||
use super::logging::lsp_warn;
|
||||
|
||||
/// Used in situations where a default URL needs to be used where otherwise a
|
||||
/// panic is undesired.
|
||||
pub static INVALID_SPECIFIER: Lazy<ModuleSpecifier> =
|
||||
Lazy::new(|| ModuleSpecifier::parse("deno://invalid").unwrap());
|
||||
|
||||
/// Used in situations where a default URL needs to be used where otherwise a
|
||||
/// panic is undesired.
|
||||
pub static INVALID_URI: Lazy<Uri> =
|
||||
Lazy::new(|| Uri::from_str("deno://invalid").unwrap());
|
||||
|
||||
/// Matches the `encodeURIComponent()` encoding from JavaScript, which matches
|
||||
/// the component percent encoding set.
|
||||
///
|
||||
|
@ -58,7 +64,7 @@ fn hash_data_specifier(specifier: &ModuleSpecifier) -> String {
|
|||
crate::util::checksum::gen(&[file_name_str.as_bytes()])
|
||||
}
|
||||
|
||||
fn to_deno_url(specifier: &Url) -> String {
|
||||
fn to_deno_uri(specifier: &Url) -> String {
|
||||
let mut string = String::with_capacity(specifier.as_str().len() + 6);
|
||||
string.push_str("deno:/");
|
||||
string.push_str(specifier.scheme());
|
||||
|
@ -95,64 +101,31 @@ fn from_deno_url(url: &Url) -> Option<Url> {
|
|||
Url::parse(&string).ok()
|
||||
}
|
||||
|
||||
/// This exists to make it a little bit harder to accidentally use a `Url`
|
||||
/// in the wrong place where a client url should be used.
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
|
||||
pub struct LspClientUrl(Url);
|
||||
|
||||
impl LspClientUrl {
|
||||
pub fn new(url: Url) -> Self {
|
||||
Self(url)
|
||||
}
|
||||
|
||||
pub fn as_url(&self) -> &Url {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn into_url(self) -> Url {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn to_uri(&self) -> Uri {
|
||||
url_to_uri(&self.0)
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.0.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for LspClientUrl {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct LspUrlMapInner {
|
||||
specifier_to_url: HashMap<ModuleSpecifier, LspClientUrl>,
|
||||
url_to_specifier: HashMap<Url, ModuleSpecifier>,
|
||||
specifier_to_uri: HashMap<ModuleSpecifier, Uri>,
|
||||
uri_to_specifier: HashMap<Uri, ModuleSpecifier>,
|
||||
}
|
||||
|
||||
impl LspUrlMapInner {
|
||||
fn put(&mut self, specifier: ModuleSpecifier, url: LspClientUrl) {
|
||||
self
|
||||
.url_to_specifier
|
||||
.insert(url.as_url().clone(), specifier.clone());
|
||||
self.specifier_to_url.insert(specifier, url);
|
||||
fn put(&mut self, specifier: ModuleSpecifier, uri: Uri) {
|
||||
self.uri_to_specifier.insert(uri.clone(), specifier.clone());
|
||||
self.specifier_to_uri.insert(specifier, uri);
|
||||
}
|
||||
|
||||
fn get_url(&self, specifier: &ModuleSpecifier) -> Option<&LspClientUrl> {
|
||||
self.specifier_to_url.get(specifier)
|
||||
fn get_uri(&self, specifier: &ModuleSpecifier) -> Option<&Uri> {
|
||||
self.specifier_to_uri.get(specifier)
|
||||
}
|
||||
|
||||
fn get_specifier(&self, url: &Url) -> Option<&ModuleSpecifier> {
|
||||
self.url_to_specifier.get(url)
|
||||
fn get_specifier(&self, uri: &Uri) -> Option<&ModuleSpecifier> {
|
||||
self.uri_to_specifier.get(uri)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn url_to_uri(url: &Url) -> Uri {
|
||||
Uri::from_str(url.as_str()).unwrap()
|
||||
pub fn url_to_uri(url: &Url) -> Result<Uri, AnyError> {
|
||||
Ok(Uri::from_str(url.as_str()).inspect_err(|err| {
|
||||
lsp_warn!("Could not convert URL \"{url}\" to URI: {err}")
|
||||
})?)
|
||||
}
|
||||
|
||||
pub fn uri_to_url(uri: &Uri) -> Url {
|
||||
|
@ -181,24 +154,24 @@ impl LspUrlMap {
|
|||
|
||||
/// Normalize a specifier that is used internally within Deno (or tsc) to a
|
||||
/// URL that can be handled as a "virtual" document by an LSP client.
|
||||
pub fn normalize_specifier(
|
||||
pub fn specifier_to_uri(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Result<LspClientUrl, AnyError> {
|
||||
) -> Result<Uri, AnyError> {
|
||||
if let Some(file_url) =
|
||||
self.cache.vendored_specifier(specifier, file_referrer)
|
||||
{
|
||||
return Ok(LspClientUrl(file_url));
|
||||
return url_to_uri(&file_url);
|
||||
}
|
||||
let mut inner = self.inner.lock();
|
||||
if let Some(url) = inner.get_url(specifier).cloned() {
|
||||
Ok(url)
|
||||
if let Some(uri) = inner.get_uri(specifier).cloned() {
|
||||
Ok(uri)
|
||||
} else {
|
||||
let url = if specifier.scheme() == "file" {
|
||||
LspClientUrl(specifier.clone())
|
||||
let uri = if specifier.scheme() == "file" {
|
||||
url_to_uri(specifier)?
|
||||
} else {
|
||||
let specifier_str = if specifier.scheme() == "asset" {
|
||||
let uri_str = if specifier.scheme() == "asset" {
|
||||
format!("deno:/asset{}", specifier.path())
|
||||
} else if specifier.scheme() == "data" {
|
||||
let data_url = deno_graph::source::RawDataUrl::parse(specifier)?;
|
||||
|
@ -214,13 +187,13 @@ impl LspUrlMap {
|
|||
extension
|
||||
)
|
||||
} else {
|
||||
to_deno_url(specifier)
|
||||
to_deno_uri(specifier)
|
||||
};
|
||||
let url = LspClientUrl(Url::parse(&specifier_str)?);
|
||||
inner.put(specifier.clone(), url.clone());
|
||||
url
|
||||
let uri = Uri::from_str(&uri_str)?;
|
||||
inner.put(specifier.clone(), uri.clone());
|
||||
uri
|
||||
};
|
||||
Ok(url)
|
||||
Ok(uri)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,13 +205,17 @@ impl LspUrlMap {
|
|||
/// Note: Sometimes the url provided by the client may not have a trailing slash,
|
||||
/// so we need to force it to in the mapping and nee to explicitly state whether
|
||||
/// this is a file or directory url.
|
||||
pub fn normalize_url(&self, uri: &Uri, kind: LspUrlKind) -> ModuleSpecifier {
|
||||
pub fn uri_to_specifier(
|
||||
&self,
|
||||
uri: &Uri,
|
||||
kind: LspUrlKind,
|
||||
) -> ModuleSpecifier {
|
||||
let url = uri_to_url(uri);
|
||||
if let Some(remote_url) = self.cache.unvendored_specifier(&url) {
|
||||
return remote_url;
|
||||
}
|
||||
let mut inner = self.inner.lock();
|
||||
if let Some(specifier) = inner.get_specifier(&url).cloned() {
|
||||
if let Some(specifier) = inner.get_specifier(uri).cloned() {
|
||||
return specifier;
|
||||
}
|
||||
let mut specifier = None;
|
||||
|
@ -255,7 +232,7 @@ impl LspUrlMap {
|
|||
specifier = Some(s);
|
||||
}
|
||||
let specifier = specifier.unwrap_or_else(|| url.clone());
|
||||
inner.put(specifier.clone(), LspClientUrl(url));
|
||||
inner.put(specifier.clone(), uri.clone());
|
||||
specifier
|
||||
}
|
||||
}
|
||||
|
@ -303,15 +280,14 @@ mod tests {
|
|||
fn test_lsp_url_map() {
|
||||
let map = LspUrlMap::default();
|
||||
let fixture = resolve_url("https://deno.land/x/pkg@1.0.0/mod.ts").unwrap();
|
||||
let actual_url = map
|
||||
.normalize_specifier(&fixture, None)
|
||||
let actual_uri = map
|
||||
.specifier_to_uri(&fixture, None)
|
||||
.expect("could not handle specifier");
|
||||
let expected_url =
|
||||
Url::parse("deno:/https/deno.land/x/pkg%401.0.0/mod.ts").unwrap();
|
||||
assert_eq!(actual_url.as_url(), &expected_url);
|
||||
|
||||
let actual_specifier =
|
||||
map.normalize_url(&actual_url.to_uri(), LspUrlKind::File);
|
||||
assert_eq!(
|
||||
actual_uri.as_str(),
|
||||
"deno:/https/deno.land/x/pkg%401.0.0/mod.ts"
|
||||
);
|
||||
let actual_specifier = map.uri_to_specifier(&actual_uri, LspUrlKind::File);
|
||||
assert_eq!(actual_specifier, fixture);
|
||||
}
|
||||
|
||||
|
@ -320,17 +296,13 @@ mod tests {
|
|||
let map = LspUrlMap::default();
|
||||
let fixture =
|
||||
Uri::from_str("deno:/https/deno.land/x/pkg%401.0.0/mod.ts").unwrap();
|
||||
let actual_specifier = map.normalize_url(&fixture, LspUrlKind::File);
|
||||
let actual_specifier = map.uri_to_specifier(&fixture, LspUrlKind::File);
|
||||
let expected_specifier =
|
||||
Url::parse("https://deno.land/x/pkg@1.0.0/mod.ts").unwrap();
|
||||
assert_eq!(&actual_specifier, &expected_specifier);
|
||||
|
||||
let actual_url = map
|
||||
.normalize_specifier(&actual_specifier, None)
|
||||
.unwrap()
|
||||
.to_uri()
|
||||
.clone();
|
||||
assert_eq!(actual_url, fixture);
|
||||
let actual_uri = map.specifier_to_uri(&actual_specifier, None).unwrap();
|
||||
assert_eq!(actual_uri, fixture);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -338,14 +310,11 @@ mod tests {
|
|||
// Test fix for #9741 - not properly encoding certain URLs
|
||||
let map = LspUrlMap::default();
|
||||
let fixture = resolve_url("https://cdn.skypack.dev/-/postcss@v8.2.9-E4SktPp9c0AtxrJHp8iV/dist=es2020,mode=types/lib/postcss.d.ts").unwrap();
|
||||
let actual_url = map
|
||||
.normalize_specifier(&fixture, None)
|
||||
let actual_uri = map
|
||||
.specifier_to_uri(&fixture, None)
|
||||
.expect("could not handle specifier");
|
||||
let expected_url = Url::parse("deno:/https/cdn.skypack.dev/-/postcss%40v8.2.9-E4SktPp9c0AtxrJHp8iV/dist%3Des2020%2Cmode%3Dtypes/lib/postcss.d.ts").unwrap();
|
||||
assert_eq!(actual_url.as_url(), &expected_url);
|
||||
|
||||
let actual_specifier =
|
||||
map.normalize_url(&actual_url.to_uri(), LspUrlKind::File);
|
||||
assert_eq!(actual_uri.as_str(), "deno:/https/cdn.skypack.dev/-/postcss%40v8.2.9-E4SktPp9c0AtxrJHp8iV/dist%3Des2020%2Cmode%3Dtypes/lib/postcss.d.ts");
|
||||
let actual_specifier = map.uri_to_specifier(&actual_uri, LspUrlKind::File);
|
||||
assert_eq!(actual_specifier, fixture);
|
||||
}
|
||||
|
||||
|
@ -353,14 +322,13 @@ mod tests {
|
|||
fn test_lsp_url_map_data() {
|
||||
let map = LspUrlMap::default();
|
||||
let fixture = resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap();
|
||||
let actual_url = map
|
||||
.normalize_specifier(&fixture, None)
|
||||
let actual_uri = map
|
||||
.specifier_to_uri(&fixture, None)
|
||||
.expect("could not handle specifier");
|
||||
let expected_url = Url::parse("deno:/c21c7fc382b2b0553dc0864aa81a3acacfb7b3d1285ab5ae76da6abec213fb37/data_url.ts").unwrap();
|
||||
assert_eq!(actual_url.as_url(), &expected_url);
|
||||
assert_eq!(&uri_to_url(&actual_uri), &expected_url);
|
||||
|
||||
let actual_specifier =
|
||||
map.normalize_url(&actual_url.to_uri(), LspUrlKind::File);
|
||||
let actual_specifier = map.uri_to_specifier(&actual_uri, LspUrlKind::File);
|
||||
assert_eq!(actual_specifier, fixture);
|
||||
}
|
||||
|
||||
|
@ -368,15 +336,11 @@ mod tests {
|
|||
fn test_lsp_url_map_host_with_port() {
|
||||
let map = LspUrlMap::default();
|
||||
let fixture = resolve_url("http://localhost:8000/mod.ts").unwrap();
|
||||
let actual_url = map
|
||||
.normalize_specifier(&fixture, None)
|
||||
let actual_uri = map
|
||||
.specifier_to_uri(&fixture, None)
|
||||
.expect("could not handle specifier");
|
||||
let expected_url =
|
||||
Url::parse("deno:/http/localhost%3A8000/mod.ts").unwrap();
|
||||
assert_eq!(actual_url.as_url(), &expected_url);
|
||||
|
||||
let actual_specifier =
|
||||
map.normalize_url(&actual_url.to_uri(), LspUrlKind::File);
|
||||
assert_eq!(actual_uri.as_str(), "deno:/http/localhost%3A8000/mod.ts");
|
||||
let actual_specifier = map.uri_to_specifier(&actual_uri, LspUrlKind::File);
|
||||
assert_eq!(actual_specifier, fixture);
|
||||
}
|
||||
|
||||
|
@ -388,7 +352,7 @@ mod tests {
|
|||
"file:///c%3A/Users/deno/Desktop/file%20with%20spaces%20in%20name.txt",
|
||||
)
|
||||
.unwrap();
|
||||
let actual = map.normalize_url(&fixture, LspUrlKind::File);
|
||||
let actual = map.uri_to_specifier(&fixture, LspUrlKind::File);
|
||||
let expected =
|
||||
Url::parse("file:///C:/Users/deno/Desktop/file with spaces in name.txt")
|
||||
.unwrap();
|
||||
|
@ -403,7 +367,7 @@ mod tests {
|
|||
"file:///Users/deno/Desktop/file%20with%20spaces%20in%20name.txt",
|
||||
)
|
||||
.unwrap();
|
||||
let actual = map.normalize_url(&fixture, LspUrlKind::File);
|
||||
let actual = map.uri_to_specifier(&fixture, LspUrlKind::File);
|
||||
let expected =
|
||||
Url::parse("file:///Users/deno/Desktop/file with spaces in name.txt")
|
||||
.unwrap();
|
||||
|
@ -414,7 +378,7 @@ mod tests {
|
|||
fn test_normalize_deno_status() {
|
||||
let map = LspUrlMap::default();
|
||||
let fixture = Uri::from_str("deno:/status.md").unwrap();
|
||||
let actual = map.normalize_url(&fixture, LspUrlKind::File);
|
||||
let actual = map.uri_to_specifier(&fixture, LspUrlKind::File);
|
||||
assert_eq!(actual.as_str(), fixture.as_str());
|
||||
}
|
||||
|
||||
|
|
71
cli/main.rs
71
cli/main.rs
|
@ -32,8 +32,6 @@ mod worker;
|
|||
use crate::args::flags_from_vec;
|
||||
use crate::args::DenoSubcommand;
|
||||
use crate::args::Flags;
|
||||
use crate::args::DENO_FUTURE;
|
||||
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;
|
||||
|
@ -48,7 +46,8 @@ use deno_core::error::JsError;
|
|||
use deno_core::futures::FutureExt;
|
||||
use deno_core::unsync::JoinHandle;
|
||||
use deno_npm::resolution::SnapshotFromLockfileError;
|
||||
use deno_runtime::fmt_errors::format_js_error;
|
||||
use deno_runtime::fmt_errors::format_js_error_with_suggestions;
|
||||
use deno_runtime::fmt_errors::FixSuggestion;
|
||||
use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics;
|
||||
use deno_terminal::colors;
|
||||
use factory::CliFactory;
|
||||
|
@ -111,9 +110,7 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
|||
tools::bench::run_benchmarks(flags, bench_flags).await
|
||||
}
|
||||
}),
|
||||
DenoSubcommand::Bundle(bundle_flags) => spawn_subcommand(async {
|
||||
tools::bundle::bundle(flags, bundle_flags).await
|
||||
}),
|
||||
DenoSubcommand::Bundle => exit_with_message("⚠️ `deno bundle` was removed in Deno 2.\n\nSee the Deno 1.x to 2.x Migration Guide for migration instructions: https://docs.deno.com/runtime/manual/advanced/migrate_deprecations", 1),
|
||||
DenoSubcommand::Doc(doc_flags) => {
|
||||
spawn_subcommand(async { tools::doc::doc(flags, doc_flags).await })
|
||||
}
|
||||
|
@ -121,14 +118,7 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
|||
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);
|
||||
|
@ -172,7 +162,7 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
|||
tools::jupyter::kernel(flags, jupyter_flags).await
|
||||
}),
|
||||
DenoSubcommand::Uninstall(uninstall_flags) => spawn_subcommand(async {
|
||||
tools::installer::uninstall(uninstall_flags)
|
||||
tools::installer::uninstall(flags, uninstall_flags).await
|
||||
}),
|
||||
DenoSubcommand::Lsp => spawn_subcommand(async { lsp::start().await }),
|
||||
DenoSubcommand::Lint(lint_flags) => spawn_subcommand(async {
|
||||
|
@ -286,9 +276,7 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
|||
"This deno was built without the \"upgrade\" feature. Please upgrade using the installation method originally used to install Deno.",
|
||||
1,
|
||||
),
|
||||
DenoSubcommand::Vendor(vendor_flags) => spawn_subcommand(async {
|
||||
tools::vendor::vendor(flags, vendor_flags).await
|
||||
}),
|
||||
DenoSubcommand::Vendor => exit_with_message("⚠️ `deno vendor` was removed in Deno 2.\n\nSee the Deno 1.x to 2.x Migration Guide for migration instructions: https://docs.deno.com/runtime/manual/advanced/migrate_deprecations", 1),
|
||||
DenoSubcommand::Publish(publish_flags) => spawn_subcommand(async {
|
||||
tools::registry::publish(flags, publish_flags).await
|
||||
}),
|
||||
|
@ -349,12 +337,30 @@ fn exit_with_message(message: &str, code: i32) -> ! {
|
|||
std::process::exit(code);
|
||||
}
|
||||
|
||||
fn get_suggestions_for_commonjs_error(e: &JsError) -> Vec<FixSuggestion> {
|
||||
if e.name.as_deref() == Some("ReferenceError") {
|
||||
if let Some(msg) = &e.message {
|
||||
if msg.contains("module is not defined")
|
||||
|| msg.contains("exports is not defined")
|
||||
{
|
||||
return vec![
|
||||
FixSuggestion::info("Deno does not support CommonJS modules without `.cjs` extension."),
|
||||
FixSuggestion::hint("Rewrite this module to ESM or change the file extension to `.cjs`."),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn exit_for_error(error: AnyError) -> ! {
|
||||
let mut error_string = format!("{error:?}");
|
||||
let mut error_code = 1;
|
||||
|
||||
if let Some(e) = error.downcast_ref::<JsError>() {
|
||||
error_string = format_js_error(e);
|
||||
let suggestions = get_suggestions_for_commonjs_error(e);
|
||||
error_string = format_js_error_with_suggestions(e, suggestions);
|
||||
} else if let Some(SnapshotFromLockfileError::IntegrityCheckFailed(e)) =
|
||||
error.downcast_ref::<SnapshotFromLockfileError>()
|
||||
{
|
||||
|
@ -454,30 +460,19 @@ fn resolve_flags_and_init(
|
|||
// https://github.com/microsoft/vscode/blob/48d4ba271686e8072fc6674137415bc80d936bc7/extensions/typescript-language-features/src/configuration/configuration.ts#L213-L214
|
||||
DenoSubcommand::Lsp => vec!["--max-old-space-size=3072".to_string()],
|
||||
_ => {
|
||||
if *DENO_FUTURE {
|
||||
// TODO(bartlomieju): I think this can be removed as it's handled by `deno_core`
|
||||
// and its settings.
|
||||
// deno_ast removes TypeScript `assert` keywords, so this flag only affects JavaScript
|
||||
// TODO(petamoriken): Need to check TypeScript `assert` keywords in deno_ast
|
||||
vec!["--no-harmony-import-assertions".to_string()]
|
||||
} else {
|
||||
vec![
|
||||
// TODO(bartlomieju): I think this can be removed as it's handled by `deno_core`
|
||||
// and its settings.
|
||||
// If we're still in v1.X version we want to support import assertions.
|
||||
// V8 12.6 unshipped the support by default, so force it by passing a
|
||||
// flag.
|
||||
"--harmony-import-assertions".to_string(),
|
||||
// Verify with DENO_FUTURE for now.
|
||||
"--no-maglev".to_string(),
|
||||
]
|
||||
}
|
||||
// TODO(bartlomieju): I think this can be removed as it's handled by `deno_core`
|
||||
// and its settings.
|
||||
// deno_ast removes TypeScript `assert` keywords, so this flag only affects JavaScript
|
||||
// TODO(petamoriken): Need to check TypeScript `assert` keywords in deno_ast
|
||||
vec!["--no-harmony-import-assertions".to_string()]
|
||||
}
|
||||
};
|
||||
|
||||
init_v8_flags(&default_v8_flags, &flags.v8_flags, get_v8_flags_from_env());
|
||||
// TODO(bartlomieju): remove last argument in Deno 2.
|
||||
deno_core::JsRuntime::init_platform(None, !*DENO_FUTURE);
|
||||
deno_core::JsRuntime::init_platform(
|
||||
None, /* import assertions enabled */ false,
|
||||
);
|
||||
util::logger::init(flags.log_level);
|
||||
|
||||
Ok(flags)
|
||||
|
|
|
@ -401,7 +401,7 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
|
||||
fn inner_resolve(
|
||||
&self,
|
||||
specifier: &str,
|
||||
raw_specifier: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> Result<ModuleSpecifier, AnyError> {
|
||||
if self.shared.node_resolver.in_npm_package(referrer) {
|
||||
|
@ -409,7 +409,7 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
self
|
||||
.shared
|
||||
.node_resolver
|
||||
.resolve(specifier, referrer, NodeResolutionMode::Execution)?
|
||||
.resolve(raw_specifier, referrer, NodeResolutionMode::Execution)?
|
||||
.into_url(),
|
||||
);
|
||||
}
|
||||
|
@ -418,7 +418,7 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
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<TGraphContainer: ModuleGraphContainer>
|
|||
));
|
||||
}
|
||||
Resolution::None => Cow::Owned(self.shared.resolver.resolve(
|
||||
specifier,
|
||||
raw_specifier,
|
||||
&deno_graph::Range {
|
||||
specifier: referrer.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
|
|
|
@ -3307,19 +3307,30 @@ fn napi_resolve_deferred(
|
|||
check_arg!(env, result);
|
||||
check_arg!(env, deferred);
|
||||
|
||||
// Make sure microtasks don't run and call back into JS
|
||||
env
|
||||
.scope()
|
||||
.set_microtasks_policy(v8::MicrotasksPolicy::Explicit);
|
||||
|
||||
let deferred_ptr =
|
||||
unsafe { NonNull::new_unchecked(deferred as *mut v8::PromiseResolver) };
|
||||
let global = unsafe { v8::Global::from_raw(env.isolate(), deferred_ptr) };
|
||||
let resolver = v8::Local::new(&mut env.scope(), global);
|
||||
|
||||
if !resolver
|
||||
let success = resolver
|
||||
.resolve(&mut env.scope(), result.unwrap())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return napi_generic_failure;
|
||||
}
|
||||
.unwrap_or(false);
|
||||
|
||||
napi_ok
|
||||
// Restore policy
|
||||
env
|
||||
.scope()
|
||||
.set_microtasks_policy(v8::MicrotasksPolicy::Auto);
|
||||
|
||||
if success {
|
||||
napi_ok
|
||||
} else {
|
||||
napi_generic_failure
|
||||
}
|
||||
}
|
||||
|
||||
#[napi_sym]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "napi_sym"
|
||||
version = "0.96.0"
|
||||
version = "0.98.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
146
cli/npm/byonm.rs
146
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<Option<Arc<PackageJson>>, 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<PackageJson>, String), AnyError> {
|
||||
) -> Result<Option<(Arc<PackageJson>, 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<PathBuf> {
|
||||
// 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,34 +334,62 @@ impl CliNpmResolver for ByonmCliNpmResolver {
|
|||
req: &PackageReq,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> Result<PathBuf, AnyError> {
|
||||
// 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<Option<PathBuf>, 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 `{}`?"
|
||||
),
|
||||
alias,
|
||||
if *crate::args::DENO_FUTURE {
|
||||
"deno install"
|
||||
} else {
|
||||
"npm install"
|
||||
// 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<u64> {
|
||||
|
|
|
@ -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<PathBuf>,
|
||||
pub npm_system_info: NpmSystemInfo,
|
||||
pub package_json_deps_provider: Arc<PackageJsonInstallDepsProvider>,
|
||||
pub npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
pub npmrc: Arc<ResolvedNpmRc>,
|
||||
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<CliNpmRegistryApi>,
|
||||
npm_cache: Arc<NpmCache>,
|
||||
npm_rc: Arc<ResolvedNpmRc>,
|
||||
package_json_deps_provider: Arc<PackageJsonInstallDepsProvider>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
text_only_progress_bar: crate::util::progress_bar::ProgressBar,
|
||||
node_modules_dir_path: Option<PathBuf>,
|
||||
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<Arc<CliLockfile>>,
|
||||
npm_api: Arc<CliNpmRegistryApi>,
|
||||
npm_cache: Arc<NpmCache>,
|
||||
package_json_deps_provider: Arc<PackageJsonInstallDepsProvider>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
tarball_cache: Arc<TarballCache>,
|
||||
text_only_progress_bar: ProgressBar,
|
||||
|
@ -286,7 +287,7 @@ impl ManagedCliNpmResolver {
|
|||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
npm_api: Arc<CliNpmRegistryApi>,
|
||||
npm_cache: Arc<NpmCache>,
|
||||
package_json_deps_provider: Arc<PackageJsonInstallDepsProvider>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
tarball_cache: Arc<TarballCache>,
|
||||
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,
|
||||
|
@ -406,8 +407,7 @@ impl ManagedCliNpmResolver {
|
|||
}
|
||||
}
|
||||
if result.dependencies_result.is_ok() {
|
||||
result.dependencies_result =
|
||||
self.cache_packages().await.map_err(AnyError::from);
|
||||
result.dependencies_result = self.cache_packages().await;
|
||||
}
|
||||
|
||||
result
|
||||
|
@ -477,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);
|
||||
}
|
||||
|
@ -606,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(),
|
||||
|
@ -617,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(),
|
||||
|
|
|
@ -22,6 +22,7 @@ use deno_npm::NpmPackageCacheFolderId;
|
|||
use deno_npm::NpmPackageId;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_semver::jsr::JsrDepPackageReq;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use deno_semver::VersionReq;
|
||||
|
@ -329,16 +330,10 @@ fn populate_lockfile_from_snapshot(
|
|||
) {
|
||||
let mut lockfile = lockfile.lock();
|
||||
for (package_req, nv) in snapshot.package_reqs() {
|
||||
let id = &snapshot.resolve_package_from_deno_module(nv).unwrap().id;
|
||||
lockfile.insert_package_specifier(
|
||||
format!("npm:{}", package_req),
|
||||
format!(
|
||||
"npm:{}",
|
||||
snapshot
|
||||
.resolve_package_from_deno_module(nv)
|
||||
.unwrap()
|
||||
.id
|
||||
.as_serialized()
|
||||
),
|
||||
JsrDepPackageReq::npm(package_req.clone()),
|
||||
format!("{}{}", id.nv.version, id.peer_deps_serialized()),
|
||||
);
|
||||
}
|
||||
for package in snapshot.all_packages_for_every_system() {
|
||||
|
|
|
@ -10,6 +10,7 @@ use std::cmp::Ordering;
|
|||
use std::collections::hash_map::Entry;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
@ -41,7 +42,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 +66,7 @@ use super::common::RegistryReadPermissionChecker;
|
|||
pub struct LocalNpmPackageResolver {
|
||||
cache: Arc<NpmCache>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
pkg_json_deps_provider: Arc<PackageJsonInstallDepsProvider>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
progress_bar: ProgressBar,
|
||||
resolution: Arc<NpmResolution>,
|
||||
tarball_cache: Arc<TarballCache>,
|
||||
|
@ -81,7 +82,7 @@ impl LocalNpmPackageResolver {
|
|||
pub fn new(
|
||||
cache: Arc<NpmCache>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
pkg_json_deps_provider: Arc<PackageJsonInstallDepsProvider>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
progress_bar: ProgressBar,
|
||||
resolution: Arc<NpmResolution>,
|
||||
tarball_cache: Arc<TarballCache>,
|
||||
|
@ -92,7 +93,7 @@ impl LocalNpmPackageResolver {
|
|||
Self {
|
||||
cache,
|
||||
fs: fs.clone(),
|
||||
pkg_json_deps_provider,
|
||||
npm_install_deps_provider,
|
||||
progress_bar,
|
||||
resolution,
|
||||
tarball_cache,
|
||||
|
@ -248,7 +249,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 +413,16 @@ fn has_lifecycle_scripts(
|
|||
async fn sync_resolution_with_fs(
|
||||
snapshot: &NpmResolutionSnapshot,
|
||||
cache: &Arc<NpmCache>,
|
||||
pkg_json_deps_provider: &PackageJsonInstallDepsProvider,
|
||||
npm_install_deps_provider: &NpmInstallDepsProvider,
|
||||
progress_bar: &ProgressBar,
|
||||
tarball_cache: &Arc<TarballCache>,
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -618,9 +621,12 @@ async fn sync_resolution_with_fs(
|
|||
|
||||
let mut found_names: HashMap<&String, &PackageNv> = HashMap::new();
|
||||
|
||||
// set of node_modules in workspace packages that we've already ensured exist
|
||||
let mut existing_child_node_modules_dirs: HashSet<PathBuf> = HashSet::new();
|
||||
|
||||
// 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)
|
||||
{
|
||||
|
@ -665,8 +671,15 @@ async fn sync_resolution_with_fs(
|
|||
);
|
||||
if install_in_child {
|
||||
// symlink the dep into the package's child node_modules folder
|
||||
let dest_path =
|
||||
remote.base_dir.join("node_modules").join(&remote.alias);
|
||||
let dest_node_modules = remote.base_dir.join("node_modules");
|
||||
if !existing_child_node_modules_dirs.contains(&dest_node_modules) {
|
||||
fs::create_dir_all(&dest_node_modules).with_context(|| {
|
||||
format!("Creating '{}'", dest_node_modules.display())
|
||||
})?;
|
||||
existing_child_node_modules_dirs.insert(dest_node_modules.clone());
|
||||
}
|
||||
let mut dest_path = dest_node_modules;
|
||||
dest_path.push(&remote.alias);
|
||||
|
||||
symlink_package_dir(&local_registry_package_path, &dest_path)?;
|
||||
} else {
|
||||
|
@ -684,7 +697,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/<package_id>/node_modules/<package_name> to
|
||||
// node_modules/<package_name>
|
||||
let mut ids = snapshot
|
||||
|
@ -757,10 +770,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),
|
||||
|
@ -831,22 +844,14 @@ async fn sync_resolution_with_fs(
|
|||
}
|
||||
|
||||
if !packages_with_scripts_not_run.is_empty() {
|
||||
let (maybe_install, maybe_install_example) = if *crate::args::DENO_FUTURE {
|
||||
(
|
||||
" or `deno install`",
|
||||
" or `deno install --allow-scripts=pkg1,pkg2`",
|
||||
)
|
||||
} else {
|
||||
("", "")
|
||||
};
|
||||
let packages = packages_with_scripts_not_run
|
||||
.iter()
|
||||
.map(|(_, p)| format!("npm:{p}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
log::warn!("{}: Packages contained npm lifecycle scripts (preinstall/install/postinstall) that were not executed.
|
||||
This may cause the packages to not work correctly. To run them, use the `--allow-scripts` flag with `deno cache`{maybe_install}
|
||||
(e.g. `deno cache --allow-scripts=pkg1,pkg2 <entrypoint>`{maybe_install_example}):\n {packages}", crate::colors::yellow("warning"));
|
||||
log::warn!("{} Packages contained npm lifecycle scripts (preinstall/install/postinstall) that were not executed.
|
||||
This may cause the packages to not work correctly. To run them, use the `--allow-scripts` flag with `deno cache` or `deno install`
|
||||
(e.g. `deno cache --allow-scripts=pkg1,pkg2 <entrypoint>` or `deno install --allow-scripts=pkg1,pkg2`):\n {packages}", crate::colors::yellow("Warning"));
|
||||
for (scripts_warned_path, _) in packages_with_scripts_not_run {
|
||||
let _ignore_err = fs::write(scripts_warned_path, "");
|
||||
}
|
||||
|
@ -993,21 +998,31 @@ impl SetupCache {
|
|||
}
|
||||
}
|
||||
|
||||
/// Normalizes a package name for use at `node_modules/.deno/<pkg-name>@<version>[_<copy_index>]`
|
||||
pub fn normalize_pkg_name_for_node_modules_deno_folder(name: &str) -> Cow<str> {
|
||||
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(
|
||||
|
|
|
@ -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<dyn FileSystem>,
|
||||
npm_cache: Arc<NpmCache>,
|
||||
pkg_json_deps_provider: &Arc<PackageJsonInstallDepsProvider>,
|
||||
npm_install_deps_provider: &Arc<NpmInstallDepsProvider>,
|
||||
progress_bar: &ProgressBar,
|
||||
resolution: Arc<NpmResolution>,
|
||||
tarball_cache: Arc<TarballCache>,
|
||||
|
@ -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,
|
||||
|
|
|
@ -146,7 +146,7 @@ impl CliNodeResolver {
|
|||
concat!(
|
||||
"Could not resolve \"{}\", but found it in a package.json. ",
|
||||
"Deno expects the node_modules/ directory to be up to date. ",
|
||||
"Did you forget to run `npm install`?"
|
||||
"Did you forget to run `deno install`?"
|
||||
),
|
||||
specifier
|
||||
));
|
||||
|
@ -225,13 +225,8 @@ impl CliNodeResolver {
|
|||
let package_json_path = package_folder.join("package.json");
|
||||
if !self.fs.exists_sync(&package_json_path) {
|
||||
return Err(anyhow!(
|
||||
"Could not find '{}'. Deno expects the node_modules/ directory to be up to date. Did you forget to run `{}`?",
|
||||
"Could not find '{}'. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?",
|
||||
package_json_path.display(),
|
||||
if *crate::args::DENO_FUTURE {
|
||||
"deno install"
|
||||
} else {
|
||||
"npm install"
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -332,7 +327,9 @@ impl NpmModuleLoader {
|
|||
specifier: &ModuleSpecifier,
|
||||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<Result<ModuleCodeStringSource, AnyError>> {
|
||||
if self.node_resolver.in_npm_package(specifier) {
|
||||
if self.node_resolver.in_npm_package(specifier)
|
||||
|| (specifier.scheme() == "file" && specifier.path().ends_with(".cjs"))
|
||||
{
|
||||
Some(self.load(specifier, maybe_referrer).await)
|
||||
} else {
|
||||
None
|
||||
|
@ -381,7 +378,9 @@ impl NpmModuleLoader {
|
|||
}
|
||||
})?;
|
||||
|
||||
let code = if self.cjs_resolutions.contains(specifier) {
|
||||
let code = if self.cjs_resolutions.contains(specifier)
|
||||
|| (specifier.scheme() == "file" && specifier.path().ends_with(".cjs"))
|
||||
{
|
||||
// translate cjs to esm if it's cjs and inject node globals
|
||||
let code = match String::from_utf8_lossy(&code) {
|
||||
Cow::Owned(code) => code,
|
||||
|
@ -507,7 +506,7 @@ impl Resolver for CliGraphResolver {
|
|||
|
||||
fn resolve(
|
||||
&self,
|
||||
specifier: &str,
|
||||
raw_specifier: &str,
|
||||
referrer_range: &deno_graph::Range,
|
||||
mode: ResolutionMode,
|
||||
) -> Result<ModuleSpecifier, ResolveError> {
|
||||
|
@ -524,7 +523,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()));
|
||||
}
|
||||
|
@ -533,7 +532,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) => {
|
||||
|
@ -705,7 +704,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());
|
||||
|
@ -754,7 +753,7 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> {
|
|||
let line = start.line + 1;
|
||||
let column = start.character + 1;
|
||||
if !*DENO_DISABLE_PEDANTIC_NODE_WARNINGS {
|
||||
log::warn!("Warning: Resolving \"{module_name}\" as \"node:{module_name}\" at {specifier}:{line}:{column}. If you want to use a built-in Node module, add a \"node:\" prefix.")
|
||||
log::warn!("{} Resolving \"{module_name}\" as \"node:{module_name}\" at {specifier}:{line}:{column}. If you want to use a built-in Node module, add a \"node:\" prefix.", colors::yellow("Warning"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -223,7 +223,7 @@
|
|||
"useUnknownInCatchVariables": {
|
||||
"description": "Default catch clause variables as `unknown` instead of `any`.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"default": true,
|
||||
"markdownDescription": "Default catch clause variables as `unknown` instead of `any`.\n\nSee more: https://www.typescriptlang.org/tsconfig#useUnknownInCatchVariables"
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
|
|
@ -83,14 +83,6 @@
|
|||
"type": "string",
|
||||
"description": "The checksum of the local source file. This can be used to validate if the current on disk version matches the version described here."
|
||||
},
|
||||
"emit": {
|
||||
"type": "string",
|
||||
"description": "The path to an emitted version of the module, if the module requires transpilation to be loaded into the Deno runtime."
|
||||
},
|
||||
"map": {
|
||||
"type": "string",
|
||||
"description": "The path to an optionally emitted source map between the original and emitted version of the file."
|
||||
},
|
||||
"error": {
|
||||
"type": "string",
|
||||
"description": "If when resolving the module, Deno encountered an error and the module is unavailable, the text of that error will be indicated here."
|
||||
|
|
|
@ -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;
|
||||
|
@ -117,7 +117,6 @@ pub struct Metadata {
|
|||
pub workspace_resolver: SerializedWorkspaceResolver,
|
||||
pub entrypoint_key: String,
|
||||
pub node_modules: Option<NodeModules>,
|
||||
pub disable_deprecated_api_warning: bool,
|
||||
pub unstable_config: UnstableConfig,
|
||||
}
|
||||
|
||||
|
@ -624,12 +623,9 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
pkg_json_resolution: self.workspace_resolver.pkg_json_dep_resolution(),
|
||||
},
|
||||
node_modules,
|
||||
disable_deprecated_api_warning: cli_options
|
||||
.disable_deprecated_api_warning,
|
||||
unstable_config: UnstableConfig {
|
||||
legacy_flag_enabled: cli_options.legacy_unstable_flag(),
|
||||
bare_node_builtins: cli_options.unstable_bare_node_builtins(),
|
||||
byonm: cli_options.use_byonm(),
|
||||
sloppy_imports: cli_options.unstable_sloppy_imports(),
|
||||
features: cli_options.unstable_features(),
|
||||
},
|
||||
|
|
|
@ -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<ModuleSpecifier, AnyError> {
|
||||
|
@ -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.
|
||||
|
@ -706,6 +708,8 @@ pub async fn run(
|
|||
None,
|
||||
None,
|
||||
feature_checker,
|
||||
// Code cache is not supported for standalone binary yet.
|
||||
None,
|
||||
CliMainWorkerOptions {
|
||||
argv: metadata.argv,
|
||||
log_level: WorkerLogLevel::Info,
|
||||
|
@ -732,17 +736,10 @@ pub async fn run(
|
|||
unstable: metadata.unstable_config.legacy_flag_enabled,
|
||||
create_hmr_runner: None,
|
||||
create_coverage_collector: None,
|
||||
node_ipc: None,
|
||||
serve_port: None,
|
||||
serve_host: None,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
false,
|
||||
// TODO(bartlomieju): temporarily disabled
|
||||
// metadata.disable_deprecated_api_warning,
|
||||
true,
|
||||
false,
|
||||
// Code cache is not supported for standalone binary yet.
|
||||
None,
|
||||
);
|
||||
|
||||
// Initialize v8 once from the main thread.
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -18,8 +18,11 @@ pub trait BenchReporter {
|
|||
fn report_uncaught_error(&mut self, origin: &str, error: Box<JsError>);
|
||||
}
|
||||
|
||||
const JSON_SCHEMA_VERSION: u8 = 1;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct JsonReporterOutput {
|
||||
version: u8,
|
||||
runtime: String,
|
||||
cpu: String,
|
||||
benches: Vec<JsonReporterBench>,
|
||||
|
@ -28,6 +31,7 @@ struct JsonReporterOutput {
|
|||
impl Default for JsonReporterOutput {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
version: JSON_SCHEMA_VERSION,
|
||||
runtime: format!(
|
||||
"{} {}",
|
||||
version::DENO_VERSION_INFO.user_agent,
|
||||
|
|
|
@ -1,164 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_graph::Module;
|
||||
use deno_terminal::colors;
|
||||
|
||||
use crate::args::BundleFlags;
|
||||
use crate::args::CliOptions;
|
||||
use crate::args::Flags;
|
||||
use crate::args::TsConfigType;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::graph_util::error_for_any_npm_specifier;
|
||||
use crate::util;
|
||||
use crate::util::display;
|
||||
|
||||
pub async fn bundle(
|
||||
flags: Arc<Flags>,
|
||||
bundle_flags: BundleFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
log::info!(
|
||||
"{}",
|
||||
colors::yellow("⚠️ Warning: `deno bundle` is deprecated and will be removed in Deno 2.0.\nUse an alternative bundler like \"deno_emit\", \"esbuild\" or \"rollup\" instead."),
|
||||
);
|
||||
|
||||
if let Some(watch_flags) = &bundle_flags.watch {
|
||||
util::file_watcher::watch_func(
|
||||
flags,
|
||||
util::file_watcher::PrintConfig::new(
|
||||
"Bundle",
|
||||
!watch_flags.no_clear_screen,
|
||||
),
|
||||
move |flags, watcher_communicator, _changed_paths| {
|
||||
let bundle_flags = bundle_flags.clone();
|
||||
Ok(async move {
|
||||
let factory = CliFactory::from_flags_for_watcher(
|
||||
flags,
|
||||
watcher_communicator.clone(),
|
||||
);
|
||||
let cli_options = factory.cli_options()?;
|
||||
let _ = watcher_communicator.watch_paths(cli_options.watch_paths());
|
||||
bundle_action(factory, &bundle_flags).await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
} else {
|
||||
let factory = CliFactory::from_flags(flags);
|
||||
bundle_action(factory, &bundle_flags).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn bundle_action(
|
||||
factory: CliFactory,
|
||||
bundle_flags: &BundleFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let cli_options = factory.cli_options()?;
|
||||
let module_specifier = cli_options.resolve_main_module()?;
|
||||
log::debug!(">>>>> bundle START");
|
||||
let module_graph_creator = factory.module_graph_creator().await?;
|
||||
let cli_options = factory.cli_options()?;
|
||||
|
||||
let graph = module_graph_creator
|
||||
.create_graph_and_maybe_check(vec![module_specifier.clone()])
|
||||
.await?;
|
||||
|
||||
let mut paths_to_watch: Vec<PathBuf> = graph
|
||||
.specifiers()
|
||||
.filter_map(|(_, r)| {
|
||||
r.ok().and_then(|module| match module {
|
||||
Module::Js(m) => m.specifier.to_file_path().ok(),
|
||||
Module::Json(m) => m.specifier.to_file_path().ok(),
|
||||
// nothing to watch
|
||||
Module::Node(_) | Module::Npm(_) | Module::External(_) => None,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
if let Ok(Some(import_map_path)) = cli_options
|
||||
.resolve_specified_import_map_specifier()
|
||||
.map(|ms| ms.and_then(|ref s| s.to_file_path().ok()))
|
||||
{
|
||||
paths_to_watch.push(import_map_path);
|
||||
}
|
||||
|
||||
// at the moment, we don't support npm specifiers in deno bundle, so show an error
|
||||
error_for_any_npm_specifier(&graph)?;
|
||||
|
||||
let bundle_output = bundle_module_graph(graph.as_ref(), cli_options)?;
|
||||
log::debug!(">>>>> bundle END");
|
||||
let out_file = &bundle_flags.out_file;
|
||||
|
||||
if let Some(out_file) = out_file {
|
||||
let out_file = cli_options.initial_cwd().join(out_file);
|
||||
let output_bytes = bundle_output.code.as_bytes();
|
||||
let output_len = output_bytes.len();
|
||||
util::fs::write_file(&out_file, output_bytes, 0o644)?;
|
||||
log::info!(
|
||||
"{} {:?} ({})",
|
||||
colors::green("Emit"),
|
||||
out_file,
|
||||
colors::gray(display::human_size(output_len as f64))
|
||||
);
|
||||
if let Some(bundle_map) = bundle_output.maybe_map {
|
||||
let map_bytes = bundle_map.as_bytes();
|
||||
let map_len = map_bytes.len();
|
||||
let ext = if let Some(curr_ext) = out_file.extension() {
|
||||
format!("{}.map", curr_ext.to_string_lossy())
|
||||
} else {
|
||||
"map".to_string()
|
||||
};
|
||||
let map_out_file = out_file.with_extension(ext);
|
||||
util::fs::write_file(&map_out_file, map_bytes, 0o644)?;
|
||||
log::info!(
|
||||
"{} {:?} ({})",
|
||||
colors::green("Emit"),
|
||||
map_out_file,
|
||||
colors::gray(display::human_size(map_len as f64))
|
||||
);
|
||||
}
|
||||
} else {
|
||||
#[allow(clippy::print_stdout)]
|
||||
{
|
||||
println!("{}", bundle_output.code);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bundle_module_graph(
|
||||
graph: &deno_graph::ModuleGraph,
|
||||
cli_options: &CliOptions,
|
||||
) -> Result<deno_emit::BundleEmit, AnyError> {
|
||||
log::info!("{} {}", colors::green("Bundle"), graph.roots[0]);
|
||||
|
||||
let ts_config_result =
|
||||
cli_options.resolve_ts_config_for_emit(TsConfigType::Bundle)?;
|
||||
if !cli_options.type_check_mode().is_true() {
|
||||
if let Some(ignored_options) = ts_config_result.maybe_ignored_options {
|
||||
log::warn!("{}", ignored_options);
|
||||
}
|
||||
}
|
||||
|
||||
let (transpile_options, emit_options) =
|
||||
crate::args::ts_config_to_transpile_and_emit_options(
|
||||
ts_config_result.ts_config,
|
||||
)?;
|
||||
deno_emit::bundle_graph(
|
||||
graph,
|
||||
deno_emit::BundleOptions {
|
||||
minify: false,
|
||||
bundle_type: deno_emit::BundleType::Module,
|
||||
emit_options,
|
||||
emit_ignore_directives: true,
|
||||
transpile_options,
|
||||
},
|
||||
)
|
||||
}
|
|
@ -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;
|
||||
|
@ -427,7 +426,7 @@ fn get_tsc_roots(
|
|||
|
||||
// now walk the graph that only includes the fast check dependencies
|
||||
while let Some(specifier) = pending.pop_front() {
|
||||
let Some(module) = graph.get(&specifier) else {
|
||||
let Some(module) = graph.get(specifier) else {
|
||||
continue;
|
||||
};
|
||||
if let Some(entry) = maybe_get_check_entry(module, check_js) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -5,8 +5,7 @@ use crate::args::DocHtmlFlag;
|
|||
use crate::args::DocSourceFileFlag;
|
||||
use crate::args::Flags;
|
||||
use crate::colors;
|
||||
use crate::display::write_json_to_stdout;
|
||||
use crate::display::write_to_stdout_ignore_sigpipe;
|
||||
use crate::display;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::graph_util::graph_exit_lock_errors;
|
||||
use crate::tsc::get_types_declaration_file_text;
|
||||
|
@ -17,6 +16,7 @@ use deno_config::glob::PathOrPatternSet;
|
|||
use deno_core::anyhow::bail;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use deno_doc as doc;
|
||||
use deno_doc::html::UrlResolveKind;
|
||||
use deno_graph::source::NullFileSystem;
|
||||
|
@ -31,6 +31,8 @@ use std::collections::BTreeMap;
|
|||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
const JSON_SCHEMA_VERSION: u8 = 1;
|
||||
|
||||
async fn generate_doc_nodes_for_builtin_types(
|
||||
doc_flags: DocFlags,
|
||||
parser: &dyn ModuleParser,
|
||||
|
@ -228,7 +230,11 @@ pub async fn doc(
|
|||
doc_nodes_by_url.into_values().flatten().collect::<Vec<_>>();
|
||||
|
||||
if doc_flags.json {
|
||||
write_json_to_stdout(&doc_nodes)
|
||||
let json_output = serde_json::json!({
|
||||
"version": JSON_SCHEMA_VERSION,
|
||||
"nodes": &doc_nodes
|
||||
});
|
||||
display::write_json_to_stdout(&json_output)
|
||||
} else if doc_flags.lint {
|
||||
// don't output docs if running with only the --lint flag
|
||||
log::info!(
|
||||
|
@ -553,7 +559,8 @@ fn print_docs_to_stdout(
|
|||
)
|
||||
};
|
||||
|
||||
write_to_stdout_ignore_sigpipe(details.as_bytes()).map_err(AnyError::from)
|
||||
display::write_to_stdout_ignore_sigpipe(details.as_bytes())
|
||||
.map_err(AnyError::from)
|
||||
}
|
||||
|
||||
fn check_diagnostics(diagnostics: &[DocDiagnostic]) -> Result<(), AnyError> {
|
||||
|
|
|
@ -403,15 +403,6 @@ pub fn format_html(
|
|||
let mut typescript_config =
|
||||
get_resolved_typescript_config(fmt_options);
|
||||
typescript_config.line_width = hints.print_width as u32;
|
||||
if hints.attr {
|
||||
typescript_config.quote_style = if let Some(true) =
|
||||
fmt_options.single_quote
|
||||
{
|
||||
dprint_plugin_typescript::configuration::QuoteStyle::AlwaysDouble
|
||||
} else {
|
||||
dprint_plugin_typescript::configuration::QuoteStyle::AlwaysSingle
|
||||
};
|
||||
}
|
||||
dprint_plugin_typescript::format_text(
|
||||
&path,
|
||||
text.to_string(),
|
||||
|
|
|
@ -11,7 +11,6 @@ use deno_core::anyhow::bail;
|
|||
use deno_core::error::AnyError;
|
||||
use deno_core::resolve_url_or_path;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_graph::Dependency;
|
||||
use deno_graph::GraphKind;
|
||||
use deno_graph::Module;
|
||||
|
@ -35,6 +34,8 @@ use crate::npm::CliNpmResolver;
|
|||
use crate::npm::ManagedCliNpmResolver;
|
||||
use crate::util::checksum;
|
||||
|
||||
const JSON_SCHEMA_VERSION: u8 = 1;
|
||||
|
||||
pub async fn info(
|
||||
flags: Arc<Flags>,
|
||||
info_flags: InfoFlags,
|
||||
|
@ -79,7 +80,10 @@ pub async fn info(
|
|||
}
|
||||
|
||||
if info_flags.json {
|
||||
let mut json_graph = json!(graph);
|
||||
let mut json_graph = serde_json::json!(graph);
|
||||
if let Some(output) = json_graph.as_object_mut() {
|
||||
output.insert("version".to_string(), JSON_SCHEMA_VERSION.into());
|
||||
}
|
||||
add_npm_packages_to_json(&mut json_graph, npm_resolver.as_ref());
|
||||
display::write_json_to_stdout(&json_graph)?;
|
||||
} else {
|
||||
|
@ -121,7 +125,8 @@ fn print_cache_info(
|
|||
let local_storage_dir = origin_dir.join("local_storage");
|
||||
|
||||
if json {
|
||||
let mut output = json!({
|
||||
let mut json_output = serde_json::json!({
|
||||
"version": JSON_SCHEMA_VERSION,
|
||||
"denoDir": deno_dir,
|
||||
"modulesCache": modules_cache,
|
||||
"npmCache": npm_cache,
|
||||
|
@ -131,10 +136,10 @@ fn print_cache_info(
|
|||
});
|
||||
|
||||
if location.is_some() {
|
||||
output["localStorage"] = serde_json::to_value(local_storage_dir)?;
|
||||
json_output["localStorage"] = serde_json::to_value(local_storage_dir)?;
|
||||
}
|
||||
|
||||
display::write_json_to_stdout(&output)
|
||||
display::write_json_to_stdout(&json_output)
|
||||
} else {
|
||||
println!("{} {}", colors::bold("DENO_DIR location:"), deno_dir);
|
||||
println!(
|
||||
|
@ -440,7 +445,7 @@ impl<'a> GraphDisplayContext<'a> {
|
|||
}
|
||||
|
||||
let root_specifier = self.graph.resolve(&self.graph.roots[0]);
|
||||
match self.graph.try_get(&root_specifier) {
|
||||
match self.graph.try_get(root_specifier) {
|
||||
Ok(Some(root)) => {
|
||||
let maybe_cache_info = match root {
|
||||
Module::Js(module) => module.maybe_cache_info.as_ref(),
|
||||
|
@ -456,22 +461,6 @@ impl<'a> GraphDisplayContext<'a> {
|
|||
local.to_string_lossy()
|
||||
)?;
|
||||
}
|
||||
if let Some(emit) = &cache_info.emit {
|
||||
writeln!(
|
||||
writer,
|
||||
"{} {}",
|
||||
colors::bold("emit:"),
|
||||
emit.to_string_lossy()
|
||||
)?;
|
||||
}
|
||||
if let Some(map) = &cache_info.map {
|
||||
writeln!(
|
||||
writer,
|
||||
"{} {}",
|
||||
colors::bold("map:"),
|
||||
map.to_string_lossy()
|
||||
)?;
|
||||
}
|
||||
}
|
||||
if let Some(module) = root.js() {
|
||||
writeln!(writer, "{} {}", colors::bold("type:"), module.media_type)?;
|
||||
|
@ -694,9 +683,9 @@ impl<'a> GraphDisplayContext<'a> {
|
|||
Resolution::Ok(resolved) => {
|
||||
let specifier = &resolved.specifier;
|
||||
let resolved_specifier = self.graph.resolve(specifier);
|
||||
Some(match self.graph.try_get(&resolved_specifier) {
|
||||
Some(match self.graph.try_get(resolved_specifier) {
|
||||
Ok(Some(module)) => self.build_module_info(module, type_dep),
|
||||
Err(err) => self.build_error_info(err, &resolved_specifier),
|
||||
Err(err) => self.build_error_info(err, resolved_specifier),
|
||||
Ok(None) => TreeNode::from_text(format!(
|
||||
"{} {}",
|
||||
colors::red(specifier),
|
||||
|
|
|
@ -37,7 +37,7 @@ const routes: Route[] = [
|
|||
},
|
||||
{
|
||||
pattern: new URLPattern({ pathname: "/static/*" }),
|
||||
handler: (req) => serveDir(req, { urlRoot: "./" }),
|
||||
handler: (req) => serveDir(req),
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -52,7 +52,6 @@ export default {
|
|||
return handler(req);
|
||||
},
|
||||
} satisfies Deno.ServeDefaultExport;
|
||||
|
||||
"#,
|
||||
)?;
|
||||
create_file(
|
||||
|
@ -80,13 +79,23 @@ Deno.test(async function serverFetchUsers() {
|
|||
});
|
||||
|
||||
Deno.test(async function serverFetchStatic() {
|
||||
const req = new Request("https://deno.land/static/main.ts");
|
||||
const req = new Request("https://deno.land/static/hello.js");
|
||||
const res = await server.fetch(req);
|
||||
assertEquals(res.headers.get("content-type"), "text/plain;charset=UTF-8");
|
||||
assertEquals(await res.text(), 'console.log("Hello, world!");\n');
|
||||
assertEquals(res.headers.get("content-type"), "text/javascript; charset=UTF-8");
|
||||
});
|
||||
"#,
|
||||
)?;
|
||||
|
||||
let static_dir = dir.join("static");
|
||||
std::fs::create_dir_all(&static_dir)?;
|
||||
create_file(
|
||||
&static_dir,
|
||||
"hello.js",
|
||||
r#"console.log("Hello, world!");
|
||||
"#,
|
||||
)?;
|
||||
|
||||
create_json_file(
|
||||
&dir,
|
||||
"deno.json",
|
||||
|
@ -203,7 +212,7 @@ Deno.test(function addTest() {
|
|||
info!(" deno task dev");
|
||||
info!("");
|
||||
info!(" {}", colors::gray("# Run the tests"));
|
||||
info!(" deno -R test");
|
||||
info!(" deno test -R");
|
||||
} else if init_flags.lib {
|
||||
info!(" {}", colors::gray("# Run the tests"));
|
||||
info!(" deno test");
|
||||
|
|
|
@ -7,14 +7,17 @@ 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;
|
||||
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::generic_error;
|
||||
use deno_core::error::AnyError;
|
||||
|
@ -195,14 +198,15 @@ pub async fn infer_name_from_url(
|
|||
Some(stem.to_string())
|
||||
}
|
||||
|
||||
pub fn uninstall(uninstall_flags: UninstallFlags) -> Result<(), AnyError> {
|
||||
if !uninstall_flags.global {
|
||||
log::warn!("⚠️ `deno install` behavior will change in Deno 2. To preserve the current behavior use the `-g` or `--global` flag.");
|
||||
}
|
||||
|
||||
pub async fn uninstall(
|
||||
flags: Arc<Flags>,
|
||||
uninstall_flags: UninstallFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let uninstall_flags = match uninstall_flags.kind {
|
||||
UninstallKind::Global(flags) => flags,
|
||||
UninstallKind::Local => unreachable!(),
|
||||
UninstallKind::Local(remove_flags) => {
|
||||
return super::registry::remove(flags, remove_flags).await;
|
||||
}
|
||||
};
|
||||
|
||||
let cwd = std::env::current_dir().context("Unable to get CWD")?;
|
||||
|
@ -261,26 +265,65 @@ pub fn uninstall(uninstall_flags: UninstallFlags) -> Result<(), AnyError> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn install_from_entrypoints(
|
||||
flags: Arc<Flags>,
|
||||
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<Flags>,
|
||||
maybe_add_flags: Option<AddFlags>,
|
||||
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()?;
|
||||
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(())
|
||||
}
|
||||
|
||||
|
@ -290,14 +333,13 @@ pub async fn install_command(
|
|||
) -> Result<(), AnyError> {
|
||||
match install_flags.kind {
|
||||
InstallKind::Global(global_flags) => {
|
||||
if !install_flags.global {
|
||||
log::warn!("⚠️ `deno install` behavior will change in Deno 2. To preserve the current behavior use the `-g` or `--global` flag.");
|
||||
}
|
||||
|
||||
install_global(flags, global_flags).await
|
||||
}
|
||||
InstallKind::Local(maybe_add_flags) => {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -464,10 +506,6 @@ async fn resolve_shim_data(
|
|||
executable_args.push("--no-npm".to_string());
|
||||
}
|
||||
|
||||
if flags.lock_write {
|
||||
executable_args.push("--lock-write".to_string());
|
||||
}
|
||||
|
||||
if flags.cached_only {
|
||||
executable_args.push("--cached-only".to_string());
|
||||
}
|
||||
|
@ -1471,8 +1509,8 @@ mod tests {
|
|||
assert!(content.contains(&expected_string));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uninstall_basic() {
|
||||
#[tokio::test]
|
||||
async fn uninstall_basic() {
|
||||
let temp_dir = TempDir::new();
|
||||
let bin_dir = temp_dir.path().join("bin");
|
||||
std::fs::create_dir(&bin_dir).unwrap();
|
||||
|
@ -1499,13 +1537,16 @@ mod tests {
|
|||
File::create(file_path).unwrap();
|
||||
}
|
||||
|
||||
uninstall(UninstallFlags {
|
||||
kind: UninstallKind::Global(UninstallFlagsGlobal {
|
||||
name: "echo_test".to_string(),
|
||||
root: Some(temp_dir.path().to_string()),
|
||||
}),
|
||||
global: false,
|
||||
})
|
||||
uninstall(
|
||||
Default::default(),
|
||||
UninstallFlags {
|
||||
kind: UninstallKind::Global(UninstallFlagsGlobal {
|
||||
name: "echo_test".to_string(),
|
||||
root: Some(temp_dir.path().to_string()),
|
||||
}),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(!file_path.exists());
|
||||
|
|
|
@ -45,6 +45,7 @@ use crate::colors;
|
|||
use crate::factory::CliFactory;
|
||||
use crate::graph_util::ModuleGraphCreator;
|
||||
use crate::tools::fmt::run_parallelized;
|
||||
use crate::util::display;
|
||||
use crate::util::file_watcher;
|
||||
use crate::util::fs::canonicalize_path;
|
||||
use crate::util::path::is_script_ext;
|
||||
|
@ -60,6 +61,8 @@ pub use rules::collect_no_slow_type_diagnostics;
|
|||
pub use rules::ConfiguredRules;
|
||||
pub use rules::LintRuleProvider;
|
||||
|
||||
const JSON_SCHEMA_VERSION: u8 = 1;
|
||||
|
||||
static STDIN_FILE_NAME: &str = "$deno$stdin.ts";
|
||||
|
||||
pub async fn lint(
|
||||
|
@ -440,18 +443,20 @@ pub fn print_rules_list(json: bool, maybe_rules_tags: Option<Vec<String>>) {
|
|||
.rules;
|
||||
|
||||
if json {
|
||||
let json_rules: Vec<serde_json::Value> = lint_rules
|
||||
.iter()
|
||||
.map(|rule| {
|
||||
serde_json::json!({
|
||||
"code": rule.code(),
|
||||
"tags": rule.tags(),
|
||||
"docs": rule.docs(),
|
||||
let json_output = serde_json::json!({
|
||||
"version": JSON_SCHEMA_VERSION,
|
||||
"rules": lint_rules
|
||||
.iter()
|
||||
.map(|rule| {
|
||||
serde_json::json!({
|
||||
"code": rule.code(),
|
||||
"tags": rule.tags(),
|
||||
"docs": rule.docs(),
|
||||
})
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let json_str = serde_json::to_string_pretty(&json_rules).unwrap();
|
||||
println!("{json_str}");
|
||||
.collect::<Vec<serde_json::Value>>(),
|
||||
});
|
||||
display::write_json_to_stdout(&json_output).unwrap();
|
||||
} else {
|
||||
// The rules should still be printed even if `--quiet` option is enabled,
|
||||
// so use `println!` here instead of `info!`.
|
||||
|
|
|
@ -12,6 +12,8 @@ use crate::args::LintReporterKind;
|
|||
|
||||
use super::LintError;
|
||||
|
||||
const JSON_SCHEMA_VERSION: u8 = 1;
|
||||
|
||||
pub fn create_reporter(kind: LintReporterKind) -> Box<dyn LintReporter + Send> {
|
||||
match kind {
|
||||
LintReporterKind::Pretty => Box::new(PrettyLintReporter::new()),
|
||||
|
@ -170,6 +172,7 @@ struct JsonLintDiagnostic {
|
|||
|
||||
#[derive(Serialize)]
|
||||
struct JsonLintReporter {
|
||||
version: u8,
|
||||
diagnostics: Vec<JsonLintDiagnostic>,
|
||||
errors: Vec<LintError>,
|
||||
}
|
||||
|
@ -177,6 +180,7 @@ struct JsonLintReporter {
|
|||
impl JsonLintReporter {
|
||||
fn new() -> JsonLintReporter {
|
||||
JsonLintReporter {
|
||||
version: JSON_SCHEMA_VERSION,
|
||||
diagnostics: Vec::new(),
|
||||
errors: Vec::new(),
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
pub mod bench;
|
||||
pub mod bundle;
|
||||
pub mod check;
|
||||
pub mod clean;
|
||||
pub mod compile;
|
||||
|
@ -20,4 +19,3 @@ pub mod serve;
|
|||
pub mod task;
|
||||
pub mod test;
|
||||
pub mod upgrade;
|
||||
pub mod vendor;
|
||||
|
|
|
@ -1049,7 +1049,8 @@ async fn publish_package(
|
|||
sha256: faster_hex::hex_string(&sha2::Sha256::digest(&meta_bytes)),
|
||||
},
|
||||
};
|
||||
let bundle = provenance::generate_provenance(http_client, subject).await?;
|
||||
let bundle =
|
||||
provenance::generate_provenance(http_client, vec![subject]).await?;
|
||||
|
||||
let tlog_entry = &bundle.verification_material.tlog_entries[0];
|
||||
log::info!("{}",
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
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;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -24,9 +25,11 @@ use deno_semver::package::PackageReq;
|
|||
use indexmap::IndexMap;
|
||||
use jsonc_parser::ast::ObjectProp;
|
||||
use jsonc_parser::ast::Value;
|
||||
use yoke::Yoke;
|
||||
|
||||
use crate::args::AddFlags;
|
||||
use crate::args::CacheSetting;
|
||||
use crate::args::CliOptions;
|
||||
use crate::args::Flags;
|
||||
use crate::args::RemoveFlags;
|
||||
use crate::factory::CliFactory;
|
||||
|
@ -54,115 +57,303 @@ impl DenoConfigFormat {
|
|||
}
|
||||
}
|
||||
|
||||
struct DenoConfig {
|
||||
config: Arc<deno_config::deno_json::ConfigFile>,
|
||||
format: DenoConfigFormat,
|
||||
imports: IndexMap<String, String>,
|
||||
}
|
||||
|
||||
fn deno_json_imports(
|
||||
config: &deno_config::deno_json::ConfigFile,
|
||||
) -> Result<IndexMap<String, String>, AnyError> {
|
||||
Ok(
|
||||
config
|
||||
.json
|
||||
.imports
|
||||
.clone()
|
||||
.map(|imports| {
|
||||
serde_json::from_value(imports)
|
||||
.map_err(|err| anyhow!("Malformed \"imports\" configuration: {err}"))
|
||||
})
|
||||
.transpose()?
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
impl DenoConfig {
|
||||
fn from_options(options: &CliOptions) -> Result<Option<Self>, AnyError> {
|
||||
let start_dir = &options.start_dir;
|
||||
if let Some(config) = start_dir.maybe_deno_json() {
|
||||
Ok(Some(Self {
|
||||
imports: deno_json_imports(config)?,
|
||||
config: config.clone(),
|
||||
format: DenoConfigFormat::from_specifier(&config.specifier)?,
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn add(&mut self, selected: SelectedPackage) {
|
||||
self.imports.insert(
|
||||
selected.import_name,
|
||||
format!("{}@{}", selected.package_name, selected.version_req),
|
||||
);
|
||||
}
|
||||
|
||||
fn remove(&mut self, package: &str) -> bool {
|
||||
self.imports.shift_remove(package).is_some()
|
||||
}
|
||||
|
||||
fn take_import_fields(
|
||||
&mut self,
|
||||
) -> Vec<(&'static str, IndexMap<String, String>)> {
|
||||
vec![("imports", std::mem::take(&mut self.imports))]
|
||||
}
|
||||
}
|
||||
|
||||
impl NpmConfig {
|
||||
fn from_options(options: &CliOptions) -> Result<Option<Self>, AnyError> {
|
||||
let start_dir = &options.start_dir;
|
||||
if let Some(pkg_json) = start_dir.maybe_pkg_json() {
|
||||
Ok(Some(Self {
|
||||
dependencies: pkg_json.dependencies.clone().unwrap_or_default(),
|
||||
dev_dependencies: pkg_json.dev_dependencies.clone().unwrap_or_default(),
|
||||
config: pkg_json.clone(),
|
||||
fmt_options: None,
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn add(&mut self, selected: SelectedPackage, dev: bool) {
|
||||
let (name, version) = package_json_dependency_entry(selected);
|
||||
if dev {
|
||||
self.dev_dependencies.insert(name, version);
|
||||
} else {
|
||||
self.dependencies.insert(name, version);
|
||||
}
|
||||
}
|
||||
|
||||
fn remove(&mut self, package: &str) -> bool {
|
||||
let in_deps = self.dependencies.shift_remove(package).is_some();
|
||||
let in_dev_deps = self.dev_dependencies.shift_remove(package).is_some();
|
||||
in_deps || in_dev_deps
|
||||
}
|
||||
|
||||
fn take_import_fields(
|
||||
&mut self,
|
||||
) -> Vec<(&'static str, IndexMap<String, String>)> {
|
||||
vec![
|
||||
("dependencies", std::mem::take(&mut self.dependencies)),
|
||||
(
|
||||
"devDependencies",
|
||||
std::mem::take(&mut self.dev_dependencies),
|
||||
),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
struct NpmConfig {
|
||||
config: Arc<deno_node::PackageJson>,
|
||||
fmt_options: Option<FmtOptionsConfig>,
|
||||
dependencies: IndexMap<String, String>,
|
||||
dev_dependencies: IndexMap<String, String>,
|
||||
}
|
||||
|
||||
enum DenoOrPackageJson {
|
||||
Deno(Arc<deno_config::deno_json::ConfigFile>, DenoConfigFormat),
|
||||
Npm(Arc<deno_node::PackageJson>, Option<FmtOptionsConfig>),
|
||||
Deno(DenoConfig),
|
||||
Npm(NpmConfig),
|
||||
}
|
||||
|
||||
impl From<DenoConfig> for DenoOrPackageJson {
|
||||
fn from(config: DenoConfig) -> Self {
|
||||
Self::Deno(config)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NpmConfig> for DenoOrPackageJson {
|
||||
fn from(config: NpmConfig) -> Self {
|
||||
Self::Npm(config)
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around `jsonc_parser::ast::Object` that can be stored in a `Yoke`
|
||||
#[derive(yoke::Yokeable)]
|
||||
struct JsoncObjectView<'a>(jsonc_parser::ast::Object<'a>);
|
||||
|
||||
struct ConfigUpdater {
|
||||
config: DenoOrPackageJson,
|
||||
// the `Yoke` is so we can carry the parsed object (which borrows from
|
||||
// the source) along with the source itself
|
||||
ast: Yoke<JsoncObjectView<'static>, String>,
|
||||
path: PathBuf,
|
||||
modified: bool,
|
||||
}
|
||||
|
||||
impl ConfigUpdater {
|
||||
fn obj(&self) -> &jsonc_parser::ast::Object<'_> {
|
||||
&self.ast.get().0
|
||||
}
|
||||
fn contents(&self) -> &str {
|
||||
self.ast.backing_cart()
|
||||
}
|
||||
async fn maybe_new(
|
||||
config: Option<impl Into<DenoOrPackageJson>>,
|
||||
) -> Result<Option<Self>, AnyError> {
|
||||
if let Some(config) = config {
|
||||
Ok(Some(Self::new(config.into()).await?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
async fn new(config: DenoOrPackageJson) -> Result<Self, AnyError> {
|
||||
let specifier = config.specifier();
|
||||
if specifier.scheme() != "file" {
|
||||
bail!("Can't update a remote configuration file");
|
||||
}
|
||||
let config_file_path = specifier.to_file_path().map_err(|_| {
|
||||
anyhow!("Specifier {specifier:?} is an invalid file path")
|
||||
})?;
|
||||
let config_file_contents = {
|
||||
let contents = tokio::fs::read_to_string(&config_file_path)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("Reading config file at: {}", config_file_path.display())
|
||||
})?;
|
||||
if contents.trim().is_empty() {
|
||||
"{}\n".into()
|
||||
} else {
|
||||
contents
|
||||
}
|
||||
};
|
||||
let ast = Yoke::try_attach_to_cart(config_file_contents, |contents| {
|
||||
let ast = jsonc_parser::parse_to_ast(
|
||||
contents,
|
||||
&Default::default(),
|
||||
&Default::default(),
|
||||
)
|
||||
.with_context(|| {
|
||||
format!("Failed to parse config file at {}", specifier)
|
||||
})?;
|
||||
let obj = match ast.value {
|
||||
Some(Value::Object(obj)) => obj,
|
||||
_ => bail!(
|
||||
"Failed to update config file at {}, expected an object",
|
||||
specifier
|
||||
),
|
||||
};
|
||||
Ok(JsoncObjectView(obj))
|
||||
})?;
|
||||
Ok(Self {
|
||||
config,
|
||||
ast,
|
||||
path: config_file_path,
|
||||
modified: false,
|
||||
})
|
||||
}
|
||||
|
||||
fn add(&mut self, selected: SelectedPackage, dev: bool) {
|
||||
match &mut self.config {
|
||||
DenoOrPackageJson::Deno(deno) => deno.add(selected),
|
||||
DenoOrPackageJson::Npm(npm) => npm.add(selected, dev),
|
||||
}
|
||||
self.modified = true;
|
||||
}
|
||||
|
||||
fn remove(&mut self, package: &str) -> bool {
|
||||
let removed = match &mut self.config {
|
||||
DenoOrPackageJson::Deno(deno) => deno.remove(package),
|
||||
DenoOrPackageJson::Npm(npm) => npm.remove(package),
|
||||
};
|
||||
if removed {
|
||||
self.modified = true;
|
||||
}
|
||||
removed
|
||||
}
|
||||
|
||||
async fn commit(mut self) -> Result<(), AnyError> {
|
||||
if !self.modified {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let import_fields = self.config.take_import_fields();
|
||||
|
||||
let fmt_config_options = self.config.fmt_options();
|
||||
|
||||
let new_text = update_config_file_content(
|
||||
self.obj(),
|
||||
self.contents(),
|
||||
fmt_config_options,
|
||||
import_fields.into_iter().map(|(k, v)| {
|
||||
(
|
||||
k,
|
||||
if v.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(generate_imports(v.into_iter().collect()))
|
||||
},
|
||||
)
|
||||
}),
|
||||
self.config.file_name(),
|
||||
);
|
||||
|
||||
tokio::fs::write(&self.path, new_text).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DenoOrPackageJson {
|
||||
fn specifier(&self) -> Cow<ModuleSpecifier> {
|
||||
match self {
|
||||
Self::Deno(d, ..) => Cow::Borrowed(&d.specifier),
|
||||
Self::Npm(n, ..) => Cow::Owned(n.specifier()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the existing imports/dependencies from the config.
|
||||
fn existing_imports(&self) -> Result<IndexMap<String, String>, AnyError> {
|
||||
match self {
|
||||
DenoOrPackageJson::Deno(deno, ..) => {
|
||||
if let Some(imports) = deno.json.imports.clone() {
|
||||
match serde_json::from_value(imports) {
|
||||
Ok(map) => Ok(map),
|
||||
Err(err) => {
|
||||
bail!("Malformed \"imports\" configuration: {err}")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Ok(Default::default())
|
||||
}
|
||||
}
|
||||
DenoOrPackageJson::Npm(npm, ..) => {
|
||||
Ok(npm.dependencies.clone().unwrap_or_default())
|
||||
}
|
||||
Self::Deno(d, ..) => Cow::Borrowed(&d.config.specifier),
|
||||
Self::Npm(n, ..) => Cow::Owned(n.config.specifier()),
|
||||
}
|
||||
}
|
||||
|
||||
fn fmt_options(&self) -> FmtOptionsConfig {
|
||||
match self {
|
||||
DenoOrPackageJson::Deno(deno, ..) => deno
|
||||
.config
|
||||
.to_fmt_config()
|
||||
.ok()
|
||||
.map(|f| f.options)
|
||||
.unwrap_or_default(),
|
||||
DenoOrPackageJson::Npm(_, config) => config.clone().unwrap_or_default(),
|
||||
DenoOrPackageJson::Npm(config) => {
|
||||
config.fmt_options.clone().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn imports_key(&self) -> &'static str {
|
||||
fn take_import_fields(
|
||||
&mut self,
|
||||
) -> Vec<(&'static str, IndexMap<String, String>)> {
|
||||
match self {
|
||||
DenoOrPackageJson::Deno(..) => "imports",
|
||||
DenoOrPackageJson::Npm(..) => "dependencies",
|
||||
Self::Deno(d) => d.take_import_fields(),
|
||||
Self::Npm(n) => n.take_import_fields(),
|
||||
}
|
||||
}
|
||||
|
||||
fn file_name(&self) -> &'static str {
|
||||
match self {
|
||||
DenoOrPackageJson::Deno(_, format) => match format {
|
||||
DenoOrPackageJson::Deno(config) => match config.format {
|
||||
DenoConfigFormat::Json => "deno.json",
|
||||
DenoConfigFormat::Jsonc => "deno.jsonc",
|
||||
},
|
||||
DenoOrPackageJson::Npm(..) => "package.json",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_npm(&self) -> bool {
|
||||
matches!(self, Self::Npm(..))
|
||||
}
|
||||
|
||||
/// Get the preferred config file to operate on
|
||||
/// given the flags. If no config file is present,
|
||||
/// creates a `deno.json` file - in this case
|
||||
/// we also return a new `CliFactory` that knows about
|
||||
/// the new config
|
||||
fn from_flags(flags: Arc<Flags>) -> Result<(Self, CliFactory), AnyError> {
|
||||
let factory = CliFactory::from_flags(flags.clone());
|
||||
let options = factory.cli_options()?;
|
||||
let start_dir = &options.start_dir;
|
||||
|
||||
match (start_dir.maybe_deno_json(), start_dir.maybe_pkg_json()) {
|
||||
// when both are present, for now,
|
||||
// default to deno.json
|
||||
(Some(deno), Some(_) | None) => Ok((
|
||||
DenoOrPackageJson::Deno(
|
||||
deno.clone(),
|
||||
DenoConfigFormat::from_specifier(&deno.specifier)?,
|
||||
),
|
||||
factory,
|
||||
)),
|
||||
(None, Some(package_json)) if options.enable_future_features() => {
|
||||
Ok((DenoOrPackageJson::Npm(package_json.clone(), None), factory))
|
||||
}
|
||||
(None, Some(_) | None) => {
|
||||
std::fs::write(options.initial_cwd().join("deno.json"), "{}\n")
|
||||
.context("Failed to create deno.json file")?;
|
||||
drop(factory); // drop to prevent use
|
||||
log::info!("Created deno.json configuration file.");
|
||||
let factory = CliFactory::from_flags(flags.clone());
|
||||
let options = factory.cli_options()?.clone();
|
||||
let start_dir = &options.start_dir;
|
||||
Ok((
|
||||
DenoOrPackageJson::Deno(
|
||||
start_dir.maybe_deno_json().cloned().ok_or_else(|| {
|
||||
anyhow!("config not found, but it was just created")
|
||||
})?,
|
||||
DenoConfigFormat::Json,
|
||||
),
|
||||
factory,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
fn create_deno_json(
|
||||
flags: &Arc<Flags>,
|
||||
options: &CliOptions,
|
||||
) -> Result<CliFactory, AnyError> {
|
||||
std::fs::write(options.initial_cwd().join("deno.json"), "{}\n")
|
||||
.context("Failed to create deno.json file")?;
|
||||
log::info!("Created deno.json configuration file.");
|
||||
let factory = CliFactory::from_flags(flags.clone());
|
||||
Ok(factory)
|
||||
}
|
||||
|
||||
fn package_json_dependency_entry(
|
||||
|
@ -197,19 +388,53 @@ impl std::fmt::Display for AddCommandName {
|
|||
}
|
||||
}
|
||||
|
||||
fn load_configs(
|
||||
flags: &Arc<Flags>,
|
||||
) -> Result<(CliFactory, Option<NpmConfig>, Option<DenoConfig>), AnyError> {
|
||||
let cli_factory = CliFactory::from_flags(flags.clone());
|
||||
let options = cli_factory.cli_options()?;
|
||||
let npm_config = NpmConfig::from_options(options)?;
|
||||
let (cli_factory, deno_config) = match DenoConfig::from_options(options)? {
|
||||
Some(config) => (cli_factory, Some(config)),
|
||||
None if npm_config.is_some() => (cli_factory, None),
|
||||
None => {
|
||||
let factory = create_deno_json(flags, options)?;
|
||||
let options = factory.cli_options()?.clone();
|
||||
(
|
||||
factory,
|
||||
Some(
|
||||
DenoConfig::from_options(&options)?.expect("Just created deno.json"),
|
||||
),
|
||||
)
|
||||
}
|
||||
};
|
||||
assert!(deno_config.is_some() || npm_config.is_some());
|
||||
Ok((cli_factory, npm_config, deno_config))
|
||||
}
|
||||
|
||||
pub async fn add(
|
||||
flags: Arc<Flags>,
|
||||
add_flags: AddFlags,
|
||||
cmd_name: AddCommandName,
|
||||
) -> Result<(), AnyError> {
|
||||
let (config_file, cli_factory) =
|
||||
DenoOrPackageJson::from_flags(flags.clone())?;
|
||||
let (cli_factory, npm_config, deno_config) = load_configs(&flags)?;
|
||||
let mut npm_config = ConfigUpdater::maybe_new(npm_config).await?;
|
||||
let mut deno_config = ConfigUpdater::maybe_new(deno_config).await?;
|
||||
|
||||
let config_specifier = config_file.specifier();
|
||||
if config_specifier.scheme() != "file" {
|
||||
bail!("Can't add dependencies to a remote configuration file");
|
||||
if let Some(deno) = &deno_config {
|
||||
let specifier = deno.config.specifier();
|
||||
if deno.obj().get_string("importMap").is_some() {
|
||||
bail!(
|
||||
concat!(
|
||||
"`deno {}` is not supported when configuration file contains an \"importMap\" field. ",
|
||||
"Inline the import map into the Deno configuration file.\n",
|
||||
" at {}",
|
||||
),
|
||||
cmd_name,
|
||||
specifier
|
||||
);
|
||||
}
|
||||
}
|
||||
let config_file_path = config_specifier.to_file_path().unwrap();
|
||||
|
||||
let http_client = cli_factory.http_client_provider();
|
||||
|
||||
|
@ -277,39 +502,7 @@ pub async fn add(
|
|||
}
|
||||
}
|
||||
|
||||
let config_file_contents = {
|
||||
let contents = tokio::fs::read_to_string(&config_file_path).await.unwrap();
|
||||
if contents.trim().is_empty() {
|
||||
"{}\n".into()
|
||||
} else {
|
||||
contents
|
||||
}
|
||||
};
|
||||
let ast = jsonc_parser::parse_to_ast(
|
||||
&config_file_contents,
|
||||
&Default::default(),
|
||||
&Default::default(),
|
||||
)?;
|
||||
|
||||
let obj = match ast.value {
|
||||
Some(Value::Object(obj)) => obj,
|
||||
_ => bail!("Failed updating config file due to no object."),
|
||||
};
|
||||
|
||||
if obj.get_string("importMap").is_some() {
|
||||
bail!(
|
||||
concat!(
|
||||
"`deno add` is not supported when configuration file contains an \"importMap\" field. ",
|
||||
"Inline the import map into the Deno configuration file.\n",
|
||||
" at {}",
|
||||
),
|
||||
config_specifier
|
||||
);
|
||||
}
|
||||
|
||||
let mut existing_imports = config_file.existing_imports()?;
|
||||
|
||||
let is_npm = config_file.is_npm();
|
||||
let dev = add_flags.dev;
|
||||
for selected_package in selected_packages {
|
||||
log::info!(
|
||||
"Add {}{}{}",
|
||||
|
@ -318,39 +511,32 @@ pub async fn add(
|
|||
selected_package.selected_version
|
||||
);
|
||||
|
||||
if is_npm {
|
||||
let (name, version) = package_json_dependency_entry(selected_package);
|
||||
existing_imports.insert(name, version)
|
||||
if selected_package.package_name.starts_with("npm:") {
|
||||
if let Some(npm) = &mut npm_config {
|
||||
npm.add(selected_package, dev);
|
||||
} else {
|
||||
deno_config.as_mut().unwrap().add(selected_package, dev);
|
||||
}
|
||||
} else if let Some(deno) = &mut deno_config {
|
||||
deno.add(selected_package, dev);
|
||||
} else {
|
||||
existing_imports.insert(
|
||||
selected_package.import_name,
|
||||
format!(
|
||||
"{}@{}",
|
||||
selected_package.package_name, selected_package.version_req
|
||||
),
|
||||
)
|
||||
};
|
||||
npm_config.as_mut().unwrap().add(selected_package, dev);
|
||||
}
|
||||
}
|
||||
let mut import_list: Vec<(String, String)> =
|
||||
existing_imports.into_iter().collect();
|
||||
|
||||
import_list.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
|
||||
let generated_imports = generate_imports(import_list);
|
||||
let mut commit_futures = vec![];
|
||||
if let Some(npm) = npm_config {
|
||||
commit_futures.push(npm.commit());
|
||||
}
|
||||
if let Some(deno) = deno_config {
|
||||
commit_futures.push(deno.commit());
|
||||
}
|
||||
let commit_futures =
|
||||
deno_core::futures::future::join_all(commit_futures).await;
|
||||
|
||||
let fmt_config_options = config_file.fmt_options();
|
||||
|
||||
let new_text = update_config_file_content(
|
||||
obj,
|
||||
&config_file_contents,
|
||||
generated_imports,
|
||||
fmt_config_options,
|
||||
config_file.imports_key(),
|
||||
config_file.file_name(),
|
||||
);
|
||||
|
||||
tokio::fs::write(&config_file_path, new_text)
|
||||
.await
|
||||
.context("Failed to update configuration file")?;
|
||||
for result in commit_futures {
|
||||
result.context("Failed to update configuration file")?;
|
||||
}
|
||||
|
||||
// clear the previously cached package.json from memory before reloading it
|
||||
node_resolver::PackageJsonThreadLocalCache::clear();
|
||||
|
@ -501,14 +687,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),
|
||||
|
@ -518,7 +708,8 @@ impl AddPackageReq {
|
|||
}
|
||||
}
|
||||
|
||||
fn generate_imports(packages_to_version: Vec<(String, String)>) -> String {
|
||||
fn generate_imports(mut packages_to_version: Vec<(String, String)>) -> String {
|
||||
packages_to_version.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
|
||||
let mut contents = vec![];
|
||||
let len = packages_to_version.len();
|
||||
for (index, (package, version)) in packages_to_version.iter().enumerate() {
|
||||
|
@ -531,68 +722,27 @@ fn generate_imports(packages_to_version: Vec<(String, String)>) -> String {
|
|||
contents.join("\n")
|
||||
}
|
||||
|
||||
fn remove_from_config(
|
||||
config_path: &Path,
|
||||
keys: &[&'static str],
|
||||
packages_to_remove: &[String],
|
||||
removed_packages: &mut Vec<String>,
|
||||
fmt_options: &FmtOptionsConfig,
|
||||
) -> Result<(), AnyError> {
|
||||
let mut json: serde_json::Value =
|
||||
serde_json::from_slice(&std::fs::read(config_path)?)?;
|
||||
for key in keys {
|
||||
let Some(obj) = json.get_mut(*key).and_then(|v| v.as_object_mut()) else {
|
||||
continue;
|
||||
};
|
||||
for package in packages_to_remove {
|
||||
if obj.shift_remove(package).is_some() {
|
||||
removed_packages.push(package.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let config = serde_json::to_string_pretty(&json)?;
|
||||
let config =
|
||||
crate::tools::fmt::format_json(config_path, &config, fmt_options)
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or(config);
|
||||
|
||||
std::fs::write(config_path, config)
|
||||
.context("Failed to update configuration file")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn remove(
|
||||
flags: Arc<Flags>,
|
||||
remove_flags: RemoveFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let (config_file, factory) = DenoOrPackageJson::from_flags(flags.clone())?;
|
||||
let options = factory.cli_options()?;
|
||||
let start_dir = &options.start_dir;
|
||||
let fmt_config_options = config_file.fmt_options();
|
||||
let (_, npm_config, deno_config) = load_configs(&flags)?;
|
||||
|
||||
let mut removed_packages = Vec::new();
|
||||
let mut configs = [
|
||||
ConfigUpdater::maybe_new(npm_config).await?,
|
||||
ConfigUpdater::maybe_new(deno_config).await?,
|
||||
];
|
||||
|
||||
if let Some(deno_json) = start_dir.maybe_deno_json() {
|
||||
remove_from_config(
|
||||
&deno_json.specifier.to_file_path().unwrap(),
|
||||
&["imports"],
|
||||
&remove_flags.packages,
|
||||
&mut removed_packages,
|
||||
&fmt_config_options,
|
||||
)?;
|
||||
}
|
||||
let mut removed_packages = vec![];
|
||||
|
||||
if let Some(pkg_json) = start_dir.maybe_pkg_json() {
|
||||
remove_from_config(
|
||||
&pkg_json.path,
|
||||
&["dependencies", "devDependencies"],
|
||||
&remove_flags.packages,
|
||||
&mut removed_packages,
|
||||
&fmt_config_options,
|
||||
)?;
|
||||
for package in &remove_flags.packages {
|
||||
let mut removed = false;
|
||||
for config in configs.iter_mut().flatten() {
|
||||
removed |= config.remove(package);
|
||||
}
|
||||
if removed {
|
||||
removed_packages.push(package.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if removed_packages.is_empty() {
|
||||
|
@ -601,6 +751,10 @@ pub async fn remove(
|
|||
for package in &removed_packages {
|
||||
log::info!("Removed {}", crate::colors::green(package));
|
||||
}
|
||||
for config in configs.into_iter().flatten() {
|
||||
config.commit().await?;
|
||||
}
|
||||
|
||||
// Update deno.lock
|
||||
node_resolver::PackageJsonThreadLocalCache::clear();
|
||||
let cli_factory = CliFactory::from_flags(flags);
|
||||
|
@ -610,41 +764,72 @@ pub async fn remove(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn update_config_file_content(
|
||||
obj: jsonc_parser::ast::Object,
|
||||
fn update_config_file_content<
|
||||
I: IntoIterator<Item = (&'static str, Option<String>)>,
|
||||
>(
|
||||
obj: &jsonc_parser::ast::Object,
|
||||
config_file_contents: &str,
|
||||
generated_imports: String,
|
||||
fmt_options: FmtOptionsConfig,
|
||||
imports_key: &str,
|
||||
entries: I,
|
||||
file_name: &str,
|
||||
) -> String {
|
||||
let mut text_changes = vec![];
|
||||
for (key, value) in entries {
|
||||
match obj.properties.iter().enumerate().find_map(|(idx, k)| {
|
||||
if k.name.as_str() == key {
|
||||
Some((idx, k))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Some((
|
||||
idx,
|
||||
ObjectProp {
|
||||
value: Value::Object(lit),
|
||||
range,
|
||||
..
|
||||
},
|
||||
)) => {
|
||||
if let Some(value) = value {
|
||||
text_changes.push(TextChange {
|
||||
range: (lit.range.start + 1)..(lit.range.end - 1),
|
||||
new_text: value,
|
||||
})
|
||||
} else {
|
||||
text_changes.push(TextChange {
|
||||
// remove field entirely, making sure to
|
||||
// remove the comma if it's not the last field
|
||||
range: range.start..(if idx == obj.properties.len() - 1 {
|
||||
range.end
|
||||
} else {
|
||||
obj.properties[idx + 1].range.start
|
||||
}),
|
||||
new_text: "".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
match obj.get(imports_key) {
|
||||
Some(ObjectProp {
|
||||
value: Value::Object(lit),
|
||||
..
|
||||
}) => text_changes.push(TextChange {
|
||||
range: (lit.range.start + 1)..(lit.range.end - 1),
|
||||
new_text: generated_imports,
|
||||
}),
|
||||
None => {
|
||||
let insert_position = obj.range.end - 1;
|
||||
text_changes.push(TextChange {
|
||||
range: insert_position..insert_position,
|
||||
// NOTE(bartlomieju): adding `\n` here to force the formatter to always
|
||||
// produce a config file that is multiline, like so:
|
||||
// ```
|
||||
// {
|
||||
// "imports": {
|
||||
// "<package_name>": "<registry>:<package_name>@<semver>"
|
||||
// }
|
||||
// }
|
||||
new_text: format!("\"{imports_key}\": {{\n {generated_imports} }}"),
|
||||
})
|
||||
// need to add field
|
||||
None => {
|
||||
if let Some(value) = value {
|
||||
let insert_position = obj.range.end - 1;
|
||||
text_changes.push(TextChange {
|
||||
range: insert_position..insert_position,
|
||||
// NOTE(bartlomieju): adding `\n` here to force the formatter to always
|
||||
// produce a config file that is multiline, like so:
|
||||
// ```
|
||||
// {
|
||||
// "imports": {
|
||||
// "<package_name>": "<registry>:<package_name>@<semver>"
|
||||
// }
|
||||
// }
|
||||
new_text: format!("\"{key}\": {{\n {value} }}"),
|
||||
})
|
||||
}
|
||||
}
|
||||
// we verified the shape of `imports`/`dependencies` above
|
||||
Some(_) => unreachable!(),
|
||||
}
|
||||
// we verified the shape of `imports`/`dependencies` above
|
||||
Some(_) => unreachable!(),
|
||||
}
|
||||
|
||||
let new_text =
|
||||
|
|
|
@ -229,16 +229,16 @@ impl Predicate {
|
|||
struct ProvenanceAttestation {
|
||||
#[serde(rename = "type")]
|
||||
_type: &'static str,
|
||||
subject: Subject,
|
||||
subject: Vec<Subject>,
|
||||
predicate_type: &'static str,
|
||||
predicate: Predicate,
|
||||
}
|
||||
|
||||
impl ProvenanceAttestation {
|
||||
pub fn new_github_actions(subject: Subject) -> Self {
|
||||
pub fn new_github_actions(subjects: Vec<Subject>) -> Self {
|
||||
Self {
|
||||
_type: INTOTO_STATEMENT_TYPE,
|
||||
subject,
|
||||
subject: subjects,
|
||||
predicate_type: SLSA_PREDICATE_TYPE,
|
||||
predicate: Predicate::new_github_actions(),
|
||||
}
|
||||
|
@ -296,7 +296,7 @@ pub struct ProvenanceBundle {
|
|||
|
||||
pub async fn generate_provenance(
|
||||
http_client: &HttpClient,
|
||||
subject: Subject,
|
||||
subjects: Vec<Subject>,
|
||||
) -> Result<ProvenanceBundle, AnyError> {
|
||||
if !is_gha() {
|
||||
bail!("Automatic provenance is only available in GitHub Actions");
|
||||
|
@ -308,7 +308,7 @@ pub async fn generate_provenance(
|
|||
);
|
||||
};
|
||||
|
||||
let slsa = ProvenanceAttestation::new_github_actions(subject);
|
||||
let slsa = ProvenanceAttestation::new_github_actions(subjects);
|
||||
|
||||
let attestation = serde_json::to_string(&slsa)?;
|
||||
let bundle = attest(http_client, &attestation, INTOTO_PAYLOAD_TYPE).await?;
|
||||
|
@ -738,8 +738,13 @@ mod tests {
|
|||
sha256: "yourmom".to_string(),
|
||||
},
|
||||
};
|
||||
let slsa = ProvenanceAttestation::new_github_actions(subject);
|
||||
assert_eq!(slsa.subject.name, "jsr:@divy/sdl2@0.0.1");
|
||||
assert_eq!(slsa.subject.digest.sha256, "yourmom");
|
||||
let slsa = ProvenanceAttestation::new_github_actions(vec![subject]);
|
||||
assert_eq!(
|
||||
slsa.subject.len(),
|
||||
1,
|
||||
"Subject should be an array per the in-toto specification"
|
||||
);
|
||||
assert_eq!(slsa.subject[0].name, "jsr:@divy/sdl2@0.0.1");
|
||||
assert_eq!(slsa.subject[0].digest.sha256, "yourmom");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use std::io::Read;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_config::deno_json::NodeModulesDirMode;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_runtime::deno_permissions::Permissions;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
|
@ -194,7 +195,9 @@ pub async fn eval_command(
|
|||
pub async fn maybe_npm_install(factory: &CliFactory) -> Result<(), AnyError> {
|
||||
// ensure an "npm install" is done if the user has explicitly
|
||||
// opted into using a managed node_modules directory
|
||||
if factory.cli_options()?.node_modules_dir_enablement() == Some(true) {
|
||||
if factory.cli_options()?.node_modules_dir()?
|
||||
== Some(NodeModulesDirMode::Auto)
|
||||
{
|
||||
if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() {
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
}
|
||||
|
|
|
@ -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<Flags>,
|
||||
|
@ -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 {
|
||||
|
|
|
@ -319,14 +319,11 @@ 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_flock_async_unstable" => ["lock a file", "awaiting the result of a `Deno.flock` 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"],
|
||||
"op_fs_funlock_async_unstable" => ["unlock a file", "awaiting the result of a `Deno.funlock` call"],
|
||||
"op_fs_file_truncate_async" => ["truncate a file", "awaiting the result of a `Deno.FsFile.prototype.truncate` call"],
|
||||
"op_fs_funlock_async" => ["unlock a file", "awaiting the result of a `Deno.FsFile.unlock` call"],
|
||||
"op_fs_futime_async" => ["change file timestamps", "awaiting the result of a `Deno.futime` or `Deno.FsFile.utime` call"],
|
||||
"op_fs_link_async" => ["create a hard link", "awaiting the result of a `Deno.link` call"],
|
||||
"op_fs_lstat_async" => ["get file metadata", "awaiting the result of a `Deno.lstat` call"],
|
||||
"op_fs_make_temp_dir_async" => ["create a temporary directory", "awaiting the result of a `Deno.makeTempDir` call"],
|
||||
|
@ -340,7 +337,7 @@ pub const OP_DETAILS: phf::Map<&'static str, [&'static str; 2]> = phf_map! {
|
|||
"op_fs_realpath_async" => ["resolve a path", "awaiting the result of a `Deno.realpath` call"],
|
||||
"op_fs_remove_async" => ["remove a file or directory", "awaiting the result of a `Deno.remove` call"],
|
||||
"op_fs_rename_async" => ["rename a file or directory", "awaiting the result of a `Deno.rename` call"],
|
||||
"op_fs_seek_async" => ["seek in a file", "awaiting the result of a `Deno.seek` or `Deno.FsFile.seek` call"],
|
||||
"op_fs_seek_async" => ["seek in a file", "awaiting the result of a `Deno.FsFile.prototype.seek` call"],
|
||||
"op_fs_stat_async" => ["get file metadata", "awaiting the result of a `Deno.stat` call"],
|
||||
"op_fs_symlink_async" => ["create a symlink", "awaiting the result of a `Deno.symlink` call"],
|
||||
"op_fs_truncate_async" => ["truncate a file", "awaiting the result of a `Deno.truncate` call"],
|
||||
|
|
|
@ -1778,7 +1778,8 @@ pub async fn run_tests(
|
|||
)
|
||||
.await?;
|
||||
|
||||
if !workspace_test_options.allow_none && specifiers_with_mode.is_empty() {
|
||||
if !workspace_test_options.permit_no_files && specifiers_with_mode.is_empty()
|
||||
{
|
||||
return Err(generic_error("No test modules found"));
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ use crate::util::progress_bar::ProgressBarStyle;
|
|||
use crate::version;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use color_print::cstr;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
|
@ -37,6 +38,8 @@ const RELEASE_URL: &str = "https://github.com/denoland/deno/releases";
|
|||
const CANARY_URL: &str = "https://dl.deno.land/canary";
|
||||
const RC_URL: &str = "https://dl.deno.land/release";
|
||||
|
||||
static EXAMPLE_USAGE: &str = cstr!("Example usage:\n <p(245)>deno upgrade | deno upgrade 1.46 | deno upgrade canary</>");
|
||||
|
||||
pub static ARCHIVE_NAME: Lazy<String> =
|
||||
Lazy::new(|| format!("deno-{}.zip", env!("TARGET")));
|
||||
|
||||
|
@ -226,15 +229,70 @@ impl<
|
|||
}
|
||||
}
|
||||
|
||||
fn get_minor_version(version: &str) -> &str {
|
||||
version.rsplitn(2, '.').collect::<Vec<&str>>()[1]
|
||||
fn get_minor_version_blog_post_url(semver: &Version) -> String {
|
||||
format!("https://deno.com/blog/v{}.{}", semver.major, semver.minor)
|
||||
}
|
||||
|
||||
fn print_release_notes(current_version: &str, new_version: &str) {
|
||||
// TODO(bartlomieju): we might want to reconsider this one for RC releases.
|
||||
// TODO(bartlomieju): also maybe just parse using `Version::standard` instead
|
||||
// of using `get_minor_version`?
|
||||
if get_minor_version(current_version) == get_minor_version(new_version) {
|
||||
fn get_rc_version_blog_post_url(semver: &Version) -> String {
|
||||
format!(
|
||||
"https://deno.com/blog/v{}.{}-rc-{}",
|
||||
semver.major, semver.minor, semver.pre[1]
|
||||
)
|
||||
}
|
||||
|
||||
async fn print_release_notes(
|
||||
current_version: &str,
|
||||
new_version: &str,
|
||||
client: &HttpClient,
|
||||
) {
|
||||
let Ok(current_semver) = Version::parse_standard(current_version) else {
|
||||
return;
|
||||
};
|
||||
let Ok(new_semver) = Version::parse_standard(new_version) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let is_switching_from_deno1_to_deno2 =
|
||||
new_semver.major == 2 && current_semver.major == 1;
|
||||
let is_deno_2_rc = new_semver.major == 2
|
||||
&& new_semver.minor == 0
|
||||
&& new_semver.patch == 0
|
||||
&& new_semver.pre.first() == Some(&"rc".to_string());
|
||||
|
||||
if is_deno_2_rc || is_switching_from_deno1_to_deno2 {
|
||||
log::info!(
|
||||
"{}\n\n {}\n",
|
||||
colors::gray("Migration guide:"),
|
||||
colors::bold(
|
||||
"https://docs.deno.com/runtime/manual/advanced/migrate_deprecations"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if is_deno_2_rc {
|
||||
log::info!(
|
||||
"{}\n\n {}\n",
|
||||
colors::gray("If you find a bug, please report to:"),
|
||||
colors::bold("https://github.com/denoland/deno/issues/new")
|
||||
);
|
||||
|
||||
// Check if there's blog post entry for this release
|
||||
let blog_url_str = get_rc_version_blog_post_url(&new_semver);
|
||||
let blog_url = Url::parse(&blog_url_str).unwrap();
|
||||
if client.download(blog_url).await.is_ok() {
|
||||
log::info!(
|
||||
"{}\n\n {}\n",
|
||||
colors::gray("Blog post:"),
|
||||
colors::bold(blog_url_str)
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let should_print = current_semver.major != new_semver.major
|
||||
|| current_semver.minor != new_semver.minor;
|
||||
|
||||
if !should_print {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -249,10 +307,7 @@ fn print_release_notes(current_version: &str, new_version: &str) {
|
|||
log::info!(
|
||||
"{}\n\n {}\n",
|
||||
colors::gray("Blog post:"),
|
||||
colors::bold(format!(
|
||||
"https://deno.com/blog/v{}",
|
||||
get_minor_version(new_version)
|
||||
))
|
||||
colors::bold(get_minor_version_blog_post_url(&new_semver))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -320,14 +375,14 @@ pub fn check_for_upgrades(
|
|||
log::info!(
|
||||
"{} {}",
|
||||
colors::green("A new canary release of Deno is available."),
|
||||
colors::italic_gray("Run `deno upgrade --canary` to install it.")
|
||||
colors::italic_gray("Run `deno upgrade canary` to install it.")
|
||||
);
|
||||
}
|
||||
ReleaseChannel::Rc => {
|
||||
log::info!(
|
||||
"{} {}",
|
||||
colors::green("A new release candidate of Deno is available."),
|
||||
colors::italic_gray("Run `deno upgrade --rc` to install it.")
|
||||
colors::italic_gray("Run `deno upgrade rc` to install it.")
|
||||
);
|
||||
}
|
||||
// TODO(bartlomieju)
|
||||
|
@ -512,7 +567,9 @@ pub async fn upgrade(
|
|||
print_release_notes(
|
||||
version::DENO_VERSION_INFO.deno,
|
||||
&selected_version_to_upgrade.version_or_hash,
|
||||
);
|
||||
&client,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
drop(temp_dir);
|
||||
return Ok(());
|
||||
|
@ -540,7 +597,9 @@ pub async fn upgrade(
|
|||
print_release_notes(
|
||||
version::DENO_VERSION_INFO.deno,
|
||||
&selected_version_to_upgrade.version_or_hash,
|
||||
);
|
||||
&client,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
drop(temp_dir); // delete the temp dir
|
||||
|
@ -591,12 +650,20 @@ impl RequestedVersion {
|
|||
|
||||
let (channel, passed_version) = if is_canary {
|
||||
if !re_hash.is_match(&passed_version) {
|
||||
bail!("Invalid commit hash passed");
|
||||
bail!(
|
||||
"Invalid commit hash passed ({})\n\n{}",
|
||||
colors::gray(passed_version),
|
||||
EXAMPLE_USAGE
|
||||
);
|
||||
}
|
||||
(ReleaseChannel::Canary, passed_version)
|
||||
} else {
|
||||
let Ok(semver) = Version::parse_standard(&passed_version) else {
|
||||
bail!("Invalid version passed");
|
||||
bail!(
|
||||
"Invalid version passed ({})\n\n{}",
|
||||
colors::gray(passed_version),
|
||||
EXAMPLE_USAGE
|
||||
);
|
||||
};
|
||||
|
||||
if semver.pre.contains(&"rc".to_string()) {
|
||||
|
@ -1692,4 +1759,31 @@ mod test {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blog_post_links() {
|
||||
let version = Version::parse_standard("1.46.0").unwrap();
|
||||
assert_eq!(
|
||||
get_minor_version_blog_post_url(&version),
|
||||
"https://deno.com/blog/v1.46"
|
||||
);
|
||||
|
||||
let version = Version::parse_standard("2.1.1").unwrap();
|
||||
assert_eq!(
|
||||
get_minor_version_blog_post_url(&version),
|
||||
"https://deno.com/blog/v2.1"
|
||||
);
|
||||
|
||||
let version = Version::parse_standard("2.0.0-rc.0").unwrap();
|
||||
assert_eq!(
|
||||
get_rc_version_blog_post_url(&version),
|
||||
"https://deno.com/blog/v2.0-rc-0"
|
||||
);
|
||||
|
||||
let version = Version::parse_standard("2.0.0-rc.2").unwrap();
|
||||
assert_eq!(
|
||||
get_rc_version_blog_post_url(&version),
|
||||
"https://deno.com/blog/v2.0-rc-2"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
113
cli/tools/vendor/analyze.rs
vendored
113
cli/tools/vendor/analyze.rs
vendored
|
@ -1,113 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_ast::swc::ast::ExportDefaultDecl;
|
||||
use deno_ast::swc::ast::ExportSpecifier;
|
||||
use deno_ast::swc::ast::ModuleExportName;
|
||||
use deno_ast::swc::ast::NamedExport;
|
||||
use deno_ast::swc::ast::Program;
|
||||
use deno_ast::swc::visit::noop_visit_type;
|
||||
use deno_ast::swc::visit::Visit;
|
||||
use deno_ast::swc::visit::VisitWith;
|
||||
use deno_ast::ParsedSource;
|
||||
|
||||
/// Gets if the parsed source has a default export.
|
||||
pub fn has_default_export(source: &ParsedSource) -> bool {
|
||||
let mut visitor = DefaultExportFinder {
|
||||
has_default_export: false,
|
||||
};
|
||||
let program = source.program();
|
||||
let program: &Program = &program;
|
||||
program.visit_with(&mut visitor);
|
||||
visitor.has_default_export
|
||||
}
|
||||
|
||||
struct DefaultExportFinder {
|
||||
has_default_export: bool,
|
||||
}
|
||||
|
||||
impl Visit for DefaultExportFinder {
|
||||
noop_visit_type!();
|
||||
|
||||
fn visit_export_default_decl(&mut self, _: &ExportDefaultDecl) {
|
||||
self.has_default_export = true;
|
||||
}
|
||||
|
||||
fn visit_named_export(&mut self, named_export: &NamedExport) {
|
||||
if named_export
|
||||
.specifiers
|
||||
.iter()
|
||||
.any(export_specifier_has_default)
|
||||
{
|
||||
self.has_default_export = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn export_specifier_has_default(s: &ExportSpecifier) -> bool {
|
||||
match s {
|
||||
ExportSpecifier::Default(_) => true,
|
||||
ExportSpecifier::Namespace(_) => false,
|
||||
ExportSpecifier::Named(named) => {
|
||||
let export_name = named.exported.as_ref().unwrap_or(&named.orig);
|
||||
|
||||
match export_name {
|
||||
ModuleExportName::Str(_) => false,
|
||||
ModuleExportName::Ident(ident) => &*ident.sym == "default",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use deno_ast::MediaType;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_ast::ParseParams;
|
||||
use deno_ast::ParsedSource;
|
||||
|
||||
use super::has_default_export;
|
||||
|
||||
#[test]
|
||||
fn has_default_when_export_default_decl() {
|
||||
let parsed_source = parse_module("export default class Class {}");
|
||||
assert!(has_default_export(&parsed_source));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_default_when_named_export() {
|
||||
let parsed_source = parse_module("export {default} from './test.ts';");
|
||||
assert!(has_default_export(&parsed_source));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_default_when_named_export_alias() {
|
||||
let parsed_source =
|
||||
parse_module("export {test as default} from './test.ts';");
|
||||
assert!(has_default_export(&parsed_source));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_has_default_when_named_export_not_exported() {
|
||||
let parsed_source =
|
||||
parse_module("export {default as test} from './test.ts';");
|
||||
assert!(!has_default_export(&parsed_source));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_has_default_when_not() {
|
||||
let parsed_source = parse_module("export {test} from './test.ts'; export class Test{} export * from './test';");
|
||||
assert!(!has_default_export(&parsed_source));
|
||||
}
|
||||
|
||||
fn parse_module(text: &str) -> ParsedSource {
|
||||
deno_ast::parse_module(ParseParams {
|
||||
specifier: ModuleSpecifier::parse("file:///mod.ts").unwrap(),
|
||||
capture_tokens: false,
|
||||
maybe_syntax: None,
|
||||
media_type: MediaType::TypeScript,
|
||||
scope_analysis: false,
|
||||
text: text.into(),
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
}
|
1330
cli/tools/vendor/build.rs
vendored
1330
cli/tools/vendor/build.rs
vendored
File diff suppressed because it is too large
Load diff
508
cli/tools/vendor/import_map.rs
vendored
508
cli/tools/vendor/import_map.rs
vendored
|
@ -1,508 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_ast::LineAndColumnIndex;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_ast::SourceTextInfo;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::Module;
|
||||
use deno_graph::ModuleGraph;
|
||||
use deno_graph::Position;
|
||||
use deno_graph::Range;
|
||||
use deno_graph::Resolution;
|
||||
use import_map::ImportMap;
|
||||
use import_map::SpecifierMap;
|
||||
use indexmap::IndexMap;
|
||||
use log::warn;
|
||||
|
||||
use crate::args::JsxImportSourceConfig;
|
||||
use crate::cache::ParsedSourceCache;
|
||||
|
||||
use super::mappings::Mappings;
|
||||
use super::specifiers::is_remote_specifier;
|
||||
use super::specifiers::is_remote_specifier_text;
|
||||
|
||||
struct ImportMapBuilder<'a> {
|
||||
base_dir: &'a ModuleSpecifier,
|
||||
mappings: &'a Mappings,
|
||||
imports: ImportsBuilder<'a>,
|
||||
scopes: IndexMap<String, ImportsBuilder<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> ImportMapBuilder<'a> {
|
||||
pub fn new(base_dir: &'a ModuleSpecifier, mappings: &'a Mappings) -> Self {
|
||||
ImportMapBuilder {
|
||||
base_dir,
|
||||
mappings,
|
||||
imports: ImportsBuilder::new(base_dir, mappings),
|
||||
scopes: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn base_dir(&self) -> &ModuleSpecifier {
|
||||
self.base_dir
|
||||
}
|
||||
|
||||
pub fn scope(
|
||||
&mut self,
|
||||
base_specifier: &ModuleSpecifier,
|
||||
) -> &mut ImportsBuilder<'a> {
|
||||
self
|
||||
.scopes
|
||||
.entry(
|
||||
self
|
||||
.mappings
|
||||
.relative_specifier_text(self.base_dir, base_specifier),
|
||||
)
|
||||
.or_insert_with(|| ImportsBuilder::new(self.base_dir, self.mappings))
|
||||
}
|
||||
|
||||
pub fn into_import_map(
|
||||
self,
|
||||
maybe_original_import_map: Option<&ImportMap>,
|
||||
) -> ImportMap {
|
||||
fn get_local_imports(
|
||||
new_relative_path: &str,
|
||||
original_imports: &SpecifierMap,
|
||||
) -> Vec<(String, String)> {
|
||||
let mut result = Vec::new();
|
||||
for entry in original_imports.entries() {
|
||||
if let Some(raw_value) = entry.raw_value {
|
||||
if raw_value.starts_with("./") || raw_value.starts_with("../") {
|
||||
let sub_index = raw_value.find('/').unwrap() + 1;
|
||||
result.push((
|
||||
entry.raw_key.to_string(),
|
||||
format!("{}{}", new_relative_path, &raw_value[sub_index..]),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn add_local_imports<'a>(
|
||||
new_relative_path: &str,
|
||||
original_imports: &SpecifierMap,
|
||||
get_new_imports: impl FnOnce() -> &'a mut SpecifierMap,
|
||||
) {
|
||||
let local_imports =
|
||||
get_local_imports(new_relative_path, original_imports);
|
||||
if !local_imports.is_empty() {
|
||||
let new_imports = get_new_imports();
|
||||
for (key, value) in local_imports {
|
||||
if let Err(warning) = new_imports.append(key, value) {
|
||||
warn!("{}", warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut import_map = ImportMap::new(self.base_dir.clone());
|
||||
|
||||
if let Some(original_im) = maybe_original_import_map {
|
||||
let original_base_dir = ModuleSpecifier::from_directory_path(
|
||||
original_im
|
||||
.base_url()
|
||||
.to_file_path()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let new_relative_path = self
|
||||
.mappings
|
||||
.relative_specifier_text(self.base_dir, &original_base_dir);
|
||||
// add the imports
|
||||
add_local_imports(&new_relative_path, original_im.imports(), || {
|
||||
import_map.imports_mut()
|
||||
});
|
||||
|
||||
for scope in original_im.scopes() {
|
||||
if scope.raw_key.starts_with("./") || scope.raw_key.starts_with("../") {
|
||||
let sub_index = scope.raw_key.find('/').unwrap() + 1;
|
||||
let new_key =
|
||||
format!("{}{}", new_relative_path, &scope.raw_key[sub_index..]);
|
||||
add_local_imports(&new_relative_path, scope.imports, || {
|
||||
import_map.get_or_append_scope_mut(&new_key).unwrap()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let imports = import_map.imports_mut();
|
||||
for (key, value) in self.imports.imports {
|
||||
if !imports.contains(&key) {
|
||||
imports.append(key, value).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
for (scope_key, scope_value) in self.scopes {
|
||||
if !scope_value.imports.is_empty() {
|
||||
let imports = import_map.get_or_append_scope_mut(&scope_key).unwrap();
|
||||
for (key, value) in scope_value.imports {
|
||||
if !imports.contains(&key) {
|
||||
imports.append(key, value).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import_map
|
||||
}
|
||||
}
|
||||
|
||||
struct ImportsBuilder<'a> {
|
||||
base_dir: &'a ModuleSpecifier,
|
||||
mappings: &'a Mappings,
|
||||
imports: IndexMap<String, String>,
|
||||
}
|
||||
|
||||
impl<'a> ImportsBuilder<'a> {
|
||||
pub fn new(base_dir: &'a ModuleSpecifier, mappings: &'a Mappings) -> Self {
|
||||
Self {
|
||||
base_dir,
|
||||
mappings,
|
||||
imports: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add(&mut self, key: String, specifier: &ModuleSpecifier) {
|
||||
let value = self
|
||||
.mappings
|
||||
.relative_specifier_text(self.base_dir, specifier);
|
||||
|
||||
// skip creating identity entries
|
||||
if key != value {
|
||||
self.imports.insert(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BuildImportMapInput<'a> {
|
||||
pub base_dir: &'a ModuleSpecifier,
|
||||
pub modules: &'a [&'a Module],
|
||||
pub graph: &'a ModuleGraph,
|
||||
pub mappings: &'a Mappings,
|
||||
pub maybe_original_import_map: Option<&'a ImportMap>,
|
||||
pub maybe_jsx_import_source: Option<&'a JsxImportSourceConfig>,
|
||||
pub resolver: &'a dyn deno_graph::source::Resolver,
|
||||
pub parsed_source_cache: &'a ParsedSourceCache,
|
||||
}
|
||||
|
||||
pub fn build_import_map(
|
||||
input: BuildImportMapInput<'_>,
|
||||
) -> Result<String, AnyError> {
|
||||
let BuildImportMapInput {
|
||||
base_dir,
|
||||
modules,
|
||||
graph,
|
||||
mappings,
|
||||
maybe_original_import_map,
|
||||
maybe_jsx_import_source,
|
||||
resolver,
|
||||
parsed_source_cache,
|
||||
} = input;
|
||||
let mut builder = ImportMapBuilder::new(base_dir, mappings);
|
||||
visit_modules(graph, modules, mappings, &mut builder, parsed_source_cache)?;
|
||||
|
||||
for base_specifier in mappings.base_specifiers() {
|
||||
builder
|
||||
.imports
|
||||
.add(base_specifier.to_string(), base_specifier);
|
||||
}
|
||||
|
||||
// add the jsx import source to the destination import map, if mapped in the original import map
|
||||
if let Some(jsx_import_source) = maybe_jsx_import_source {
|
||||
if let Some(specifier_text) = jsx_import_source.maybe_specifier_text() {
|
||||
if let Ok(resolved_url) = resolver.resolve(
|
||||
&specifier_text,
|
||||
&deno_graph::Range {
|
||||
specifier: jsx_import_source.base_url.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
},
|
||||
ResolutionMode::Execution,
|
||||
) {
|
||||
builder.imports.add(specifier_text, &resolved_url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(builder.into_import_map(maybe_original_import_map).to_json())
|
||||
}
|
||||
|
||||
fn visit_modules(
|
||||
graph: &ModuleGraph,
|
||||
modules: &[&Module],
|
||||
mappings: &Mappings,
|
||||
import_map: &mut ImportMapBuilder,
|
||||
parsed_source_cache: &ParsedSourceCache,
|
||||
) -> Result<(), AnyError> {
|
||||
for module in modules {
|
||||
let module = match module {
|
||||
Module::Js(module) => module,
|
||||
// skip visiting Json modules as they are leaves
|
||||
Module::Json(_)
|
||||
| Module::Npm(_)
|
||||
| Module::Node(_)
|
||||
| Module::External(_) => continue,
|
||||
};
|
||||
|
||||
let parsed_source =
|
||||
parsed_source_cache.get_parsed_source_from_js_module(module)?;
|
||||
let text_info = parsed_source.text_info_lazy().clone();
|
||||
|
||||
for dep in module.dependencies.values() {
|
||||
visit_resolution(
|
||||
&dep.maybe_code,
|
||||
graph,
|
||||
import_map,
|
||||
&module.specifier,
|
||||
mappings,
|
||||
&text_info,
|
||||
&module.source,
|
||||
);
|
||||
visit_resolution(
|
||||
&dep.maybe_type,
|
||||
graph,
|
||||
import_map,
|
||||
&module.specifier,
|
||||
mappings,
|
||||
&text_info,
|
||||
&module.source,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(types_dep) = &module.maybe_types_dependency {
|
||||
visit_resolution(
|
||||
&types_dep.dependency,
|
||||
graph,
|
||||
import_map,
|
||||
&module.specifier,
|
||||
mappings,
|
||||
&text_info,
|
||||
&module.source,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_resolution(
|
||||
resolution: &Resolution,
|
||||
graph: &ModuleGraph,
|
||||
import_map: &mut ImportMapBuilder,
|
||||
referrer: &ModuleSpecifier,
|
||||
mappings: &Mappings,
|
||||
text_info: &SourceTextInfo,
|
||||
source_text: &str,
|
||||
) {
|
||||
if let Some(resolved) = resolution.ok() {
|
||||
let text = text_from_range(text_info, source_text, &resolved.range);
|
||||
// if the text is empty then it's probably an x-TypeScript-types
|
||||
if !text.is_empty() {
|
||||
handle_dep_specifier(
|
||||
text,
|
||||
&resolved.specifier,
|
||||
graph,
|
||||
import_map,
|
||||
referrer,
|
||||
mappings,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_dep_specifier(
|
||||
text: &str,
|
||||
unresolved_specifier: &ModuleSpecifier,
|
||||
graph: &ModuleGraph,
|
||||
import_map: &mut ImportMapBuilder,
|
||||
referrer: &ModuleSpecifier,
|
||||
mappings: &Mappings,
|
||||
) {
|
||||
let specifier = match graph.get(unresolved_specifier) {
|
||||
Some(module) => module.specifier().clone(),
|
||||
// Ignore when None. The graph was previous validated so this is a
|
||||
// dynamic import that was missing and is ignored for vendoring
|
||||
None => return,
|
||||
};
|
||||
// check if it's referencing a remote module
|
||||
if is_remote_specifier(&specifier) {
|
||||
handle_remote_dep_specifier(
|
||||
text,
|
||||
unresolved_specifier,
|
||||
&specifier,
|
||||
import_map,
|
||||
referrer,
|
||||
mappings,
|
||||
)
|
||||
} else if specifier.scheme() == "file" {
|
||||
handle_local_dep_specifier(
|
||||
text,
|
||||
unresolved_specifier,
|
||||
&specifier,
|
||||
import_map,
|
||||
referrer,
|
||||
mappings,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_remote_dep_specifier(
|
||||
text: &str,
|
||||
unresolved_specifier: &ModuleSpecifier,
|
||||
specifier: &ModuleSpecifier,
|
||||
import_map: &mut ImportMapBuilder,
|
||||
referrer: &ModuleSpecifier,
|
||||
mappings: &Mappings,
|
||||
) {
|
||||
if is_remote_specifier_text(text) {
|
||||
let base_specifier = mappings.base_specifier(specifier);
|
||||
if text.starts_with(base_specifier.as_str()) {
|
||||
let sub_path = &text[base_specifier.as_str().len()..];
|
||||
let relative_text =
|
||||
mappings.relative_specifier_text(base_specifier, specifier);
|
||||
let expected_sub_path = relative_text.trim_start_matches("./");
|
||||
if expected_sub_path != sub_path {
|
||||
import_map.imports.add(text.to_string(), specifier);
|
||||
}
|
||||
} else {
|
||||
// it's probably a redirect. Add it explicitly to the import map
|
||||
import_map.imports.add(text.to_string(), specifier);
|
||||
}
|
||||
} else {
|
||||
let expected_relative_specifier_text =
|
||||
mappings.relative_specifier_text(referrer, specifier);
|
||||
if expected_relative_specifier_text == text {
|
||||
return;
|
||||
}
|
||||
|
||||
if !is_remote_specifier(referrer) {
|
||||
// local module referencing a remote module using
|
||||
// non-remote specifier text means it was something in
|
||||
// the original import map, so add a mapping to it
|
||||
import_map.imports.add(text.to_string(), specifier);
|
||||
return;
|
||||
}
|
||||
|
||||
let base_referrer = mappings.base_specifier(referrer);
|
||||
let base_dir = import_map.base_dir().clone();
|
||||
let imports = import_map.scope(base_referrer);
|
||||
if text.starts_with("./") || text.starts_with("../") {
|
||||
// resolve relative specifier key
|
||||
let mut local_base_specifier = mappings.local_uri(base_referrer);
|
||||
local_base_specifier = local_base_specifier
|
||||
// path includes "/" so make it relative
|
||||
.join(&format!(".{}", unresolved_specifier.path()))
|
||||
.unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"Error joining {} to {}",
|
||||
unresolved_specifier.path(),
|
||||
local_base_specifier
|
||||
)
|
||||
});
|
||||
local_base_specifier.set_query(unresolved_specifier.query());
|
||||
|
||||
imports.add(
|
||||
mappings.relative_specifier_text(&base_dir, &local_base_specifier),
|
||||
specifier,
|
||||
);
|
||||
|
||||
// add a mapping that uses the local directory name and the remote
|
||||
// filename in order to support files importing this relatively
|
||||
imports.add(
|
||||
{
|
||||
let local_path = mappings.local_path(specifier);
|
||||
let mut value =
|
||||
ModuleSpecifier::from_directory_path(local_path.parent().unwrap())
|
||||
.unwrap();
|
||||
value.set_query(specifier.query());
|
||||
value.set_path(&format!(
|
||||
"{}{}",
|
||||
value.path(),
|
||||
specifier.path_segments().unwrap().last().unwrap(),
|
||||
));
|
||||
mappings.relative_specifier_text(&base_dir, &value)
|
||||
},
|
||||
specifier,
|
||||
);
|
||||
} else {
|
||||
// absolute (`/`) or bare specifier should be left as-is
|
||||
imports.add(text.to_string(), specifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_local_dep_specifier(
|
||||
text: &str,
|
||||
unresolved_specifier: &ModuleSpecifier,
|
||||
specifier: &ModuleSpecifier,
|
||||
import_map: &mut ImportMapBuilder,
|
||||
referrer: &ModuleSpecifier,
|
||||
mappings: &Mappings,
|
||||
) {
|
||||
if !is_remote_specifier(referrer) {
|
||||
// do not handle local modules referencing local modules
|
||||
return;
|
||||
}
|
||||
|
||||
// The remote module is referencing a local file. This could occur via an
|
||||
// existing import map. In this case, we'll have to add an import map
|
||||
// entry in order to map the path back to the local path once vendored.
|
||||
let base_dir = import_map.base_dir().clone();
|
||||
let base_specifier = mappings.base_specifier(referrer);
|
||||
let imports = import_map.scope(base_specifier);
|
||||
|
||||
if text.starts_with("./") || text.starts_with("../") {
|
||||
let referrer_local_uri = mappings.local_uri(referrer);
|
||||
let mut specifier_local_uri =
|
||||
referrer_local_uri.join(text).unwrap_or_else(|_| {
|
||||
panic!(
|
||||
"Error joining {} to {}",
|
||||
unresolved_specifier.path(),
|
||||
referrer_local_uri
|
||||
)
|
||||
});
|
||||
specifier_local_uri.set_query(unresolved_specifier.query());
|
||||
|
||||
imports.add(
|
||||
mappings.relative_specifier_text(&base_dir, &specifier_local_uri),
|
||||
specifier,
|
||||
);
|
||||
} else {
|
||||
imports.add(text.to_string(), specifier);
|
||||
}
|
||||
}
|
||||
|
||||
fn text_from_range<'a>(
|
||||
text_info: &SourceTextInfo,
|
||||
text: &'a str,
|
||||
range: &Range,
|
||||
) -> &'a str {
|
||||
let result = &text[byte_range(text_info, range)];
|
||||
if result.starts_with('"') || result.starts_with('\'') {
|
||||
// remove the quotes
|
||||
&result[1..result.len() - 1]
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
fn byte_range(
|
||||
text_info: &SourceTextInfo,
|
||||
range: &Range,
|
||||
) -> std::ops::Range<usize> {
|
||||
let start = byte_index(text_info, &range.start);
|
||||
let end = byte_index(text_info, &range.end);
|
||||
start..end
|
||||
}
|
||||
|
||||
fn byte_index(text_info: &SourceTextInfo, pos: &Position) -> usize {
|
||||
// todo(https://github.com/denoland/deno_graph/issues/79): use byte indexes all the way down
|
||||
text_info.loc_to_source_pos(LineAndColumnIndex {
|
||||
line_index: pos.line,
|
||||
column_index: pos.character,
|
||||
}) - text_info.range().start
|
||||
}
|
255
cli/tools/vendor/mappings.rs
vendored
255
cli/tools/vendor/mappings.rs
vendored
|
@ -1,255 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_graph::Module;
|
||||
use deno_graph::ModuleGraph;
|
||||
use deno_graph::Position;
|
||||
|
||||
use crate::util::path::path_with_stem_suffix;
|
||||
use crate::util::path::relative_specifier;
|
||||
|
||||
use super::specifiers::dir_name_for_root;
|
||||
use super::specifiers::get_unique_path;
|
||||
use super::specifiers::make_url_relative;
|
||||
use super::specifiers::partition_by_root_specifiers;
|
||||
use super::specifiers::sanitize_filepath;
|
||||
|
||||
pub struct ProxiedModule {
|
||||
pub output_path: PathBuf,
|
||||
pub declaration_specifier: ModuleSpecifier,
|
||||
}
|
||||
|
||||
/// Constructs and holds the remote specifier to local path mappings.
|
||||
pub struct Mappings {
|
||||
mappings: HashMap<ModuleSpecifier, PathBuf>,
|
||||
base_specifiers: Vec<ModuleSpecifier>,
|
||||
proxies: HashMap<ModuleSpecifier, ProxiedModule>,
|
||||
}
|
||||
|
||||
impl Mappings {
|
||||
pub fn from_remote_modules(
|
||||
graph: &ModuleGraph,
|
||||
remote_modules: &[&Module],
|
||||
output_dir: &Path,
|
||||
) -> Result<Self, AnyError> {
|
||||
let partitioned_specifiers = partition_by_root_specifiers(
|
||||
remote_modules.iter().map(|m| m.specifier()),
|
||||
);
|
||||
let mut mapped_paths = HashSet::new();
|
||||
let mut mappings = HashMap::new();
|
||||
let mut proxies = HashMap::new();
|
||||
let mut base_specifiers = Vec::new();
|
||||
|
||||
for (root, specifiers) in partitioned_specifiers.into_iter() {
|
||||
let base_dir = get_unique_path(
|
||||
output_dir.join(dir_name_for_root(&root)),
|
||||
&mut mapped_paths,
|
||||
);
|
||||
for specifier in specifiers {
|
||||
let module = graph.get(&specifier).unwrap();
|
||||
let media_type = match module {
|
||||
Module::Js(module) => module.media_type,
|
||||
Module::Json(_) => MediaType::Json,
|
||||
Module::Node(_) | Module::Npm(_) | Module::External(_) => continue,
|
||||
};
|
||||
let sub_path = sanitize_filepath(&make_url_relative(&root, &{
|
||||
let mut specifier = specifier.clone();
|
||||
specifier.set_query(None);
|
||||
specifier
|
||||
})?);
|
||||
let new_path = path_with_extension(
|
||||
&base_dir.join(if cfg!(windows) {
|
||||
sub_path.replace('/', "\\")
|
||||
} else {
|
||||
sub_path
|
||||
}),
|
||||
&media_type.as_ts_extension()[1..],
|
||||
);
|
||||
mappings
|
||||
.insert(specifier, get_unique_path(new_path, &mut mapped_paths));
|
||||
}
|
||||
base_specifiers.push(root.clone());
|
||||
mappings.insert(root, base_dir);
|
||||
}
|
||||
|
||||
// resolve all the "proxy" paths to use for when an x-typescript-types header is specified
|
||||
for module in remote_modules {
|
||||
if let Some(module) = module.js() {
|
||||
if let Some(resolved) = &module
|
||||
.maybe_types_dependency
|
||||
.as_ref()
|
||||
.and_then(|d| d.dependency.ok())
|
||||
{
|
||||
let range = &resolved.range;
|
||||
// hack to tell if it's an x-typescript-types header
|
||||
let is_ts_types_header = range.start == Position::zeroed()
|
||||
&& range.end == Position::zeroed();
|
||||
if is_ts_types_header {
|
||||
let module_path = mappings.get(&module.specifier).unwrap();
|
||||
let proxied_path = get_unique_path(
|
||||
path_with_stem_suffix(module_path, ".proxied"),
|
||||
&mut mapped_paths,
|
||||
);
|
||||
proxies.insert(
|
||||
module.specifier.clone(),
|
||||
ProxiedModule {
|
||||
output_path: proxied_path,
|
||||
declaration_specifier: resolved.specifier.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
mappings,
|
||||
base_specifiers,
|
||||
proxies,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn local_uri(&self, specifier: &ModuleSpecifier) -> ModuleSpecifier {
|
||||
if specifier.scheme() == "file" {
|
||||
specifier.clone()
|
||||
} else {
|
||||
let local_path = self.local_path(specifier);
|
||||
if specifier.path().ends_with('/') {
|
||||
ModuleSpecifier::from_directory_path(&local_path)
|
||||
} else {
|
||||
ModuleSpecifier::from_file_path(&local_path)
|
||||
}
|
||||
.unwrap_or_else(|_| {
|
||||
panic!("Could not convert {} to uri.", local_path.display())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_path(&self, specifier: &ModuleSpecifier) -> PathBuf {
|
||||
if specifier.scheme() == "file" {
|
||||
specifier.to_file_path().unwrap()
|
||||
} else {
|
||||
self
|
||||
.mappings
|
||||
.get(specifier)
|
||||
.unwrap_or_else(|| panic!("Could not find local path for {specifier}"))
|
||||
.to_path_buf()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn relative_specifier_text(
|
||||
&self,
|
||||
from: &ModuleSpecifier,
|
||||
to: &ModuleSpecifier,
|
||||
) -> String {
|
||||
let from = self.local_uri(from);
|
||||
let to = self.local_uri(to);
|
||||
relative_specifier(&from, &to).unwrap()
|
||||
}
|
||||
|
||||
pub fn base_specifiers(&self) -> &Vec<ModuleSpecifier> {
|
||||
&self.base_specifiers
|
||||
}
|
||||
|
||||
pub fn base_specifier(
|
||||
&self,
|
||||
child_specifier: &ModuleSpecifier,
|
||||
) -> &ModuleSpecifier {
|
||||
self
|
||||
.base_specifiers
|
||||
.iter()
|
||||
.find(|s| child_specifier.as_str().starts_with(s.as_str()))
|
||||
.unwrap_or_else(|| {
|
||||
panic!("Could not find base specifier for {child_specifier}")
|
||||
})
|
||||
}
|
||||
|
||||
pub fn proxied_path(&self, specifier: &ModuleSpecifier) -> Option<PathBuf> {
|
||||
self.proxies.get(specifier).map(|s| s.output_path.clone())
|
||||
}
|
||||
|
||||
pub fn proxied_modules(
|
||||
&self,
|
||||
) -> std::collections::hash_map::Iter<'_, ModuleSpecifier, ProxiedModule> {
|
||||
self.proxies.iter()
|
||||
}
|
||||
}
|
||||
|
||||
fn path_with_extension(path: &Path, new_ext: &str) -> PathBuf {
|
||||
if let Some(file_stem) = path.file_stem().map(|f| f.to_string_lossy()) {
|
||||
if let Some(old_ext) = path.extension().map(|f| f.to_string_lossy()) {
|
||||
if file_stem.to_lowercase().ends_with(".d") {
|
||||
if new_ext.to_lowercase() == format!("d.{}", old_ext.to_lowercase()) {
|
||||
// maintain casing
|
||||
return path.to_path_buf();
|
||||
}
|
||||
return path.with_file_name(format!(
|
||||
"{}.{}",
|
||||
&file_stem[..file_stem.len() - ".d".len()],
|
||||
new_ext
|
||||
));
|
||||
}
|
||||
if new_ext.to_lowercase() == old_ext.to_lowercase() {
|
||||
// maintain casing
|
||||
return path.to_path_buf();
|
||||
}
|
||||
let media_type = MediaType::from_path(path);
|
||||
if media_type == MediaType::Unknown {
|
||||
return path.with_file_name(format!(
|
||||
"{}.{}",
|
||||
path.file_name().unwrap().to_string_lossy(),
|
||||
new_ext
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
path.with_extension(new_ext)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_path_with_extension() {
|
||||
assert_eq!(
|
||||
path_with_extension(&PathBuf::from("/test.D.TS"), "ts"),
|
||||
PathBuf::from("/test.ts")
|
||||
);
|
||||
assert_eq!(
|
||||
path_with_extension(&PathBuf::from("/test.D.MTS"), "js"),
|
||||
PathBuf::from("/test.js")
|
||||
);
|
||||
assert_eq!(
|
||||
path_with_extension(&PathBuf::from("/test.D.TS"), "d.ts"),
|
||||
// maintains casing
|
||||
PathBuf::from("/test.D.TS"),
|
||||
);
|
||||
assert_eq!(
|
||||
path_with_extension(&PathBuf::from("/test.TS"), "ts"),
|
||||
// maintains casing
|
||||
PathBuf::from("/test.TS"),
|
||||
);
|
||||
assert_eq!(
|
||||
path_with_extension(&PathBuf::from("/test.ts"), "js"),
|
||||
PathBuf::from("/test.js")
|
||||
);
|
||||
assert_eq!(
|
||||
path_with_extension(&PathBuf::from("/test.js"), "js"),
|
||||
PathBuf::from("/test.js")
|
||||
);
|
||||
assert_eq!(
|
||||
path_with_extension(&PathBuf::from("/chai@1.2.3"), "js"),
|
||||
PathBuf::from("/chai@1.2.3.js")
|
||||
);
|
||||
}
|
||||
}
|
575
cli/tools/vendor/mod.rs
vendored
575
cli/tools/vendor/mod.rs
vendored
|
@ -1,575 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_ast::TextChange;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::resolve_url_or_path;
|
||||
use deno_graph::GraphKind;
|
||||
use deno_runtime::colors;
|
||||
use log::warn;
|
||||
|
||||
use crate::args::CliOptions;
|
||||
use crate::args::ConfigFile;
|
||||
use crate::args::Flags;
|
||||
use crate::args::FmtOptionsConfig;
|
||||
use crate::args::VendorFlags;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::tools::fmt::format_json;
|
||||
use crate::util::fs::canonicalize_path;
|
||||
use crate::util::fs::resolve_from_cwd;
|
||||
use crate::util::path::relative_specifier;
|
||||
use deno_runtime::fs_util::specifier_to_file_path;
|
||||
|
||||
mod analyze;
|
||||
mod build;
|
||||
mod import_map;
|
||||
mod mappings;
|
||||
mod specifiers;
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
pub async fn vendor(
|
||||
flags: Arc<Flags>,
|
||||
vendor_flags: VendorFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
log::info!(
|
||||
"{}",
|
||||
colors::yellow("⚠️ Warning: `deno vendor` is deprecated and will be removed in Deno 2.0.\nAdd `\"vendor\": true` to your `deno.json` or use the `--vendor` flag instead."),
|
||||
);
|
||||
let mut cli_options = CliOptions::from_flags(flags)?;
|
||||
let raw_output_dir = match &vendor_flags.output_path {
|
||||
Some(output_path) => PathBuf::from(output_path).to_owned(),
|
||||
None => PathBuf::from("vendor/"),
|
||||
};
|
||||
let output_dir = resolve_from_cwd(&raw_output_dir)?;
|
||||
validate_output_dir(&output_dir, &vendor_flags)?;
|
||||
validate_options(&mut cli_options, &output_dir)?;
|
||||
let factory = CliFactory::from_cli_options(Arc::new(cli_options));
|
||||
let cli_options = factory.cli_options()?;
|
||||
if cli_options.workspace().config_folders().len() > 1 {
|
||||
bail!("deno vendor is not supported in a workspace. Set `\"vendor\": true` in the workspace deno.json file instead");
|
||||
}
|
||||
let entry_points =
|
||||
resolve_entry_points(&vendor_flags, cli_options.initial_cwd())?;
|
||||
let jsx_import_source = cli_options
|
||||
.workspace()
|
||||
.to_maybe_jsx_import_source_config()?;
|
||||
let module_graph_creator = factory.module_graph_creator().await?.clone();
|
||||
let workspace_resolver = factory.workspace_resolver().await?;
|
||||
let root_folder = cli_options.workspace().root_folder_configs();
|
||||
let maybe_config_file = root_folder.deno_json.as_ref();
|
||||
let output = build::build(build::BuildInput {
|
||||
entry_points,
|
||||
build_graph: move |entry_points| {
|
||||
async move {
|
||||
module_graph_creator
|
||||
.create_graph(GraphKind::All, entry_points)
|
||||
.await
|
||||
}
|
||||
.boxed_local()
|
||||
},
|
||||
parsed_source_cache: factory.parsed_source_cache(),
|
||||
output_dir: &output_dir,
|
||||
maybe_original_import_map: workspace_resolver.maybe_import_map(),
|
||||
maybe_jsx_import_source: jsx_import_source.as_ref(),
|
||||
resolver: factory.resolver().await?.as_graph_resolver(),
|
||||
environment: &build::RealVendorEnvironment,
|
||||
})
|
||||
.await?;
|
||||
|
||||
let vendored_count = output.vendored_count;
|
||||
let graph = output.graph;
|
||||
let npm_package_count = graph.npm_packages.len();
|
||||
let try_add_node_modules_dir = npm_package_count > 0
|
||||
&& cli_options.node_modules_dir_enablement().unwrap_or(true);
|
||||
|
||||
log::info!(
|
||||
concat!("Vendored {} {} into {} directory.",),
|
||||
vendored_count,
|
||||
if vendored_count == 1 {
|
||||
"module"
|
||||
} else {
|
||||
"modules"
|
||||
},
|
||||
raw_output_dir.display(),
|
||||
);
|
||||
|
||||
let try_add_import_map = vendored_count > 0;
|
||||
let modified_result = maybe_update_config_file(
|
||||
&output_dir,
|
||||
maybe_config_file,
|
||||
try_add_import_map,
|
||||
try_add_node_modules_dir,
|
||||
);
|
||||
|
||||
// cache the node_modules folder when it's been added to the config file
|
||||
if modified_result.added_node_modules_dir {
|
||||
let node_modules_path =
|
||||
cli_options.node_modules_dir_path().cloned().or_else(|| {
|
||||
maybe_config_file
|
||||
.as_ref()
|
||||
.map(|d| &d.specifier)
|
||||
.filter(|c| c.scheme() == "file")
|
||||
.and_then(|c| c.to_file_path().ok())
|
||||
.map(|config_path| config_path.parent().unwrap().join("node_modules"))
|
||||
});
|
||||
if let Some(node_modules_path) = node_modules_path {
|
||||
let cli_options =
|
||||
cli_options.with_node_modules_dir_path(node_modules_path);
|
||||
let factory = CliFactory::from_cli_options(Arc::new(cli_options));
|
||||
if let Some(managed) = factory.npm_resolver().await?.as_managed() {
|
||||
managed.cache_packages().await?;
|
||||
}
|
||||
}
|
||||
log::info!(
|
||||
concat!(
|
||||
"Vendored {} npm {} into node_modules directory. Set `nodeModulesDir: false` ",
|
||||
"in the Deno configuration file to disable vendoring npm packages in the future.",
|
||||
),
|
||||
npm_package_count,
|
||||
if npm_package_count == 1 {
|
||||
"package"
|
||||
} else {
|
||||
"packages"
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if vendored_count > 0 {
|
||||
let import_map_path = raw_output_dir.join("import_map.json");
|
||||
if modified_result.updated_import_map {
|
||||
log::info!(
|
||||
concat!(
|
||||
"\nUpdated your local Deno configuration file with a reference to the ",
|
||||
"new vendored import map at {}. Invoking Deno subcommands will now ",
|
||||
"automatically resolve using the vendored modules. You may override ",
|
||||
"this by providing the `--import-map <other-import-map>` flag or by ",
|
||||
"manually editing your Deno configuration file.",
|
||||
),
|
||||
import_map_path.display(),
|
||||
);
|
||||
} else {
|
||||
log::info!(
|
||||
concat!(
|
||||
"\nTo use vendored modules, specify the `--import-map {}` flag when ",
|
||||
r#"invoking Deno subcommands or add an `"importMap": "<path_to_vendored_import_map>"` "#,
|
||||
"entry to a deno.json file.",
|
||||
),
|
||||
import_map_path.display(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_output_dir(
|
||||
output_dir: &Path,
|
||||
flags: &VendorFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
if !flags.force && !is_dir_empty(output_dir)? {
|
||||
bail!(concat!(
|
||||
"Output directory was not empty. Please specify an empty directory or use ",
|
||||
"--force to ignore this error and potentially overwrite its contents.",
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_options(
|
||||
options: &mut CliOptions,
|
||||
output_dir: &Path,
|
||||
) -> Result<(), AnyError> {
|
||||
let import_map_specifier = options
|
||||
.resolve_specified_import_map_specifier()?
|
||||
.or_else(|| {
|
||||
let config_file = options.workspace().root_deno_json()?;
|
||||
config_file
|
||||
.to_import_map_specifier()
|
||||
.ok()
|
||||
.flatten()
|
||||
.or_else(|| {
|
||||
if config_file.is_an_import_map() {
|
||||
Some(config_file.specifier.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
});
|
||||
// check the import map
|
||||
if let Some(import_map_path) = import_map_specifier
|
||||
.and_then(|p| specifier_to_file_path(&p).ok())
|
||||
.and_then(|p| canonicalize_path(&p).ok())
|
||||
{
|
||||
// make the output directory in order to canonicalize it for the check below
|
||||
std::fs::create_dir_all(output_dir)?;
|
||||
let output_dir = canonicalize_path(output_dir).with_context(|| {
|
||||
format!("Failed to canonicalize: {}", output_dir.display())
|
||||
})?;
|
||||
|
||||
if import_map_path.starts_with(output_dir) {
|
||||
// canonicalize to make the test for this pass on the CI
|
||||
let cwd = canonicalize_path(&std::env::current_dir()?)?;
|
||||
// We don't allow using the output directory to help generate the
|
||||
// new state because this may lead to cryptic error messages.
|
||||
log::warn!(
|
||||
concat!(
|
||||
"Ignoring import map. Specifying an import map file ({}) in the ",
|
||||
"deno vendor output directory is not supported. If you wish to use ",
|
||||
"an import map while vendoring, please specify one located outside ",
|
||||
"this directory."
|
||||
),
|
||||
import_map_path
|
||||
.strip_prefix(&cwd)
|
||||
.unwrap_or(&import_map_path)
|
||||
.display()
|
||||
.to_string(),
|
||||
);
|
||||
|
||||
// don't use an import map in the config
|
||||
options.set_import_map_specifier(None);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn maybe_update_config_file(
|
||||
output_dir: &Path,
|
||||
maybe_config_file: Option<&Arc<ConfigFile>>,
|
||||
try_add_import_map: bool,
|
||||
try_add_node_modules_dir: bool,
|
||||
) -> ModifiedResult {
|
||||
assert!(output_dir.is_absolute());
|
||||
let config_file = match maybe_config_file {
|
||||
Some(config_file) => config_file,
|
||||
None => return ModifiedResult::default(),
|
||||
};
|
||||
if config_file.specifier.scheme() != "file" {
|
||||
return ModifiedResult::default();
|
||||
}
|
||||
|
||||
let fmt_config_options = config_file
|
||||
.to_fmt_config()
|
||||
.ok()
|
||||
.map(|config| config.options)
|
||||
.unwrap_or_default();
|
||||
let result = update_config_file(
|
||||
config_file,
|
||||
&fmt_config_options,
|
||||
if try_add_import_map {
|
||||
Some(
|
||||
ModuleSpecifier::from_file_path(output_dir.join("import_map.json"))
|
||||
.unwrap(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
try_add_node_modules_dir,
|
||||
);
|
||||
match result {
|
||||
Ok(modified_result) => modified_result,
|
||||
Err(err) => {
|
||||
warn!("Error updating config file. {:#}", err);
|
||||
ModifiedResult::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_config_file(
|
||||
config_file: &ConfigFile,
|
||||
fmt_options: &FmtOptionsConfig,
|
||||
import_map_specifier: Option<ModuleSpecifier>,
|
||||
try_add_node_modules_dir: bool,
|
||||
) -> Result<ModifiedResult, AnyError> {
|
||||
let config_path = specifier_to_file_path(&config_file.specifier)?;
|
||||
let config_text = std::fs::read_to_string(&config_path)?;
|
||||
let import_map_specifier =
|
||||
import_map_specifier.and_then(|import_map_specifier| {
|
||||
relative_specifier(&config_file.specifier, &import_map_specifier)
|
||||
});
|
||||
let modified_result = update_config_text(
|
||||
&config_text,
|
||||
fmt_options,
|
||||
import_map_specifier.as_deref(),
|
||||
try_add_node_modules_dir,
|
||||
)?;
|
||||
if let Some(new_text) = &modified_result.new_text {
|
||||
std::fs::write(config_path, new_text)?;
|
||||
}
|
||||
Ok(modified_result)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ModifiedResult {
|
||||
updated_import_map: bool,
|
||||
added_node_modules_dir: bool,
|
||||
new_text: Option<String>,
|
||||
}
|
||||
|
||||
fn update_config_text(
|
||||
text: &str,
|
||||
fmt_options: &FmtOptionsConfig,
|
||||
import_map_specifier: Option<&str>,
|
||||
try_add_node_modules_dir: bool,
|
||||
) -> Result<ModifiedResult, AnyError> {
|
||||
use jsonc_parser::ast::ObjectProp;
|
||||
use jsonc_parser::ast::Value;
|
||||
let text = if text.trim().is_empty() { "{}\n" } else { text };
|
||||
let ast =
|
||||
jsonc_parser::parse_to_ast(text, &Default::default(), &Default::default())?;
|
||||
let obj = match ast.value {
|
||||
Some(Value::Object(obj)) => obj,
|
||||
_ => bail!("Failed updating config file due to no object."),
|
||||
};
|
||||
let mut modified_result = ModifiedResult::default();
|
||||
let mut text_changes = Vec::new();
|
||||
let mut should_format = false;
|
||||
|
||||
if try_add_node_modules_dir {
|
||||
// Only modify the nodeModulesDir property if it's not set
|
||||
// as this allows people to opt-out of this when vendoring
|
||||
// by specifying `nodeModulesDir: false`
|
||||
if obj.get("nodeModulesDir").is_none() {
|
||||
let insert_position = obj.range.end - 1;
|
||||
text_changes.push(TextChange {
|
||||
range: insert_position..insert_position,
|
||||
new_text: r#""nodeModulesDir": true"#.to_string(),
|
||||
});
|
||||
should_format = true;
|
||||
modified_result.added_node_modules_dir = true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(import_map_specifier) = import_map_specifier {
|
||||
let import_map_specifier = import_map_specifier.replace('\"', "\\\"");
|
||||
match obj.get("importMap") {
|
||||
Some(ObjectProp {
|
||||
value: Value::StringLit(lit),
|
||||
..
|
||||
}) => {
|
||||
text_changes.push(TextChange {
|
||||
range: lit.range.start..lit.range.end,
|
||||
new_text: format!("\"{}\"", import_map_specifier),
|
||||
});
|
||||
modified_result.updated_import_map = true;
|
||||
}
|
||||
None => {
|
||||
// insert it crudely at a position that won't cause any issues
|
||||
// with comments and format after to make it look nice
|
||||
let insert_position = obj.range.end - 1;
|
||||
text_changes.push(TextChange {
|
||||
range: insert_position..insert_position,
|
||||
new_text: format!(r#""importMap": "{}""#, import_map_specifier),
|
||||
});
|
||||
should_format = true;
|
||||
modified_result.updated_import_map = true;
|
||||
}
|
||||
// shouldn't happen
|
||||
Some(_) => {
|
||||
bail!("Failed updating importMap in config file due to invalid type.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if text_changes.is_empty() {
|
||||
return Ok(modified_result);
|
||||
}
|
||||
|
||||
let new_text = deno_ast::apply_text_changes(text, text_changes);
|
||||
modified_result.new_text = if should_format {
|
||||
format_json(&PathBuf::from("deno.json"), &new_text, fmt_options)
|
||||
.ok()
|
||||
.map(|formatted_text| formatted_text.unwrap_or(new_text))
|
||||
} else {
|
||||
Some(new_text)
|
||||
};
|
||||
Ok(modified_result)
|
||||
}
|
||||
|
||||
fn is_dir_empty(dir_path: &Path) -> Result<bool, AnyError> {
|
||||
match std::fs::read_dir(dir_path) {
|
||||
Ok(mut dir) => Ok(dir.next().is_none()),
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(true),
|
||||
Err(err) => {
|
||||
bail!("Error reading directory {}: {}", dir_path.display(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_entry_points(
|
||||
flags: &VendorFlags,
|
||||
initial_cwd: &Path,
|
||||
) -> Result<Vec<ModuleSpecifier>, AnyError> {
|
||||
flags
|
||||
.specifiers
|
||||
.iter()
|
||||
.map(|p| resolve_url_or_path(p, initial_cwd).map_err(|e| e.into()))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod internal_test {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn update_config_text_no_existing_props_add_prop() {
|
||||
let result = update_config_text(
|
||||
"{\n}",
|
||||
&Default::default(),
|
||||
Some("./vendor/import_map.json"),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.updated_import_map);
|
||||
assert!(!result.added_node_modules_dir);
|
||||
assert_eq!(
|
||||
result.new_text.unwrap(),
|
||||
r#"{
|
||||
"importMap": "./vendor/import_map.json"
|
||||
}
|
||||
"#
|
||||
);
|
||||
|
||||
let result = update_config_text(
|
||||
"{\n}",
|
||||
&Default::default(),
|
||||
Some("./vendor/import_map.json"),
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(result.updated_import_map);
|
||||
assert!(result.added_node_modules_dir);
|
||||
assert_eq!(
|
||||
result.new_text.unwrap(),
|
||||
r#"{
|
||||
"nodeModulesDir": true,
|
||||
"importMap": "./vendor/import_map.json"
|
||||
}
|
||||
"#
|
||||
);
|
||||
|
||||
let result =
|
||||
update_config_text("{\n}", &Default::default(), None, true).unwrap();
|
||||
assert!(!result.updated_import_map);
|
||||
assert!(result.added_node_modules_dir);
|
||||
assert_eq!(
|
||||
result.new_text.unwrap(),
|
||||
r#"{
|
||||
"nodeModulesDir": true
|
||||
}
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_config_text_existing_props_add_prop() {
|
||||
let result = update_config_text(
|
||||
r#"{
|
||||
"tasks": {
|
||||
"task1": "other"
|
||||
}
|
||||
}
|
||||
"#,
|
||||
&Default::default(),
|
||||
Some("./vendor/import_map.json"),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
result.new_text.unwrap(),
|
||||
r#"{
|
||||
"tasks": {
|
||||
"task1": "other"
|
||||
},
|
||||
"importMap": "./vendor/import_map.json"
|
||||
}
|
||||
"#
|
||||
);
|
||||
|
||||
// trailing comma
|
||||
let result = update_config_text(
|
||||
r#"{
|
||||
"tasks": {
|
||||
"task1": "other"
|
||||
},
|
||||
}
|
||||
"#,
|
||||
&Default::default(),
|
||||
Some("./vendor/import_map.json"),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
result.new_text.unwrap(),
|
||||
r#"{
|
||||
"tasks": {
|
||||
"task1": "other"
|
||||
},
|
||||
"importMap": "./vendor/import_map.json"
|
||||
}
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_config_text_update_prop() {
|
||||
let result = update_config_text(
|
||||
r#"{
|
||||
"importMap": "./local.json"
|
||||
}
|
||||
"#,
|
||||
&Default::default(),
|
||||
Some("./vendor/import_map.json"),
|
||||
false,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
result.new_text.unwrap(),
|
||||
r#"{
|
||||
"importMap": "./vendor/import_map.json"
|
||||
}
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_update_node_modules_dir() {
|
||||
// will not update if this is already set (even if it's false)
|
||||
let result = update_config_text(
|
||||
r#"{
|
||||
"nodeModulesDir": false
|
||||
}
|
||||
"#,
|
||||
&Default::default(),
|
||||
None,
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!result.added_node_modules_dir);
|
||||
assert!(!result.updated_import_map);
|
||||
assert_eq!(result.new_text, None);
|
||||
|
||||
let result = update_config_text(
|
||||
r#"{
|
||||
"nodeModulesDir": true
|
||||
}
|
||||
"#,
|
||||
&Default::default(),
|
||||
None,
|
||||
true,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!result.added_node_modules_dir);
|
||||
assert!(!result.updated_import_map);
|
||||
assert_eq!(result.new_text, None);
|
||||
}
|
||||
}
|
208
cli/tools/vendor/specifiers.rs
vendored
208
cli/tools/vendor/specifiers.rs
vendored
|
@ -1,208 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::anyhow::anyhow;
|
||||
use deno_core::error::AnyError;
|
||||
|
||||
use crate::util::path::is_banned_path_char;
|
||||
use crate::util::path::path_with_stem_suffix;
|
||||
use crate::util::path::root_url_to_safe_local_dirname;
|
||||
|
||||
/// Partitions the provided specifiers by the non-path and non-query parts of a specifier.
|
||||
pub fn partition_by_root_specifiers<'a>(
|
||||
specifiers: impl Iterator<Item = &'a ModuleSpecifier>,
|
||||
) -> BTreeMap<ModuleSpecifier, Vec<ModuleSpecifier>> {
|
||||
let mut root_specifiers: BTreeMap<ModuleSpecifier, Vec<ModuleSpecifier>> =
|
||||
Default::default();
|
||||
for remote_specifier in specifiers {
|
||||
let mut root_specifier = remote_specifier.clone();
|
||||
root_specifier.set_query(None);
|
||||
root_specifier.set_path("/");
|
||||
|
||||
let specifiers = root_specifiers.entry(root_specifier).or_default();
|
||||
specifiers.push(remote_specifier.clone());
|
||||
}
|
||||
root_specifiers
|
||||
}
|
||||
|
||||
/// Gets the directory name to use for the provided root.
|
||||
pub fn dir_name_for_root(root: &ModuleSpecifier) -> PathBuf {
|
||||
root_url_to_safe_local_dirname(root)
|
||||
}
|
||||
|
||||
/// Gets a unique file path given the provided file path
|
||||
/// and the set of existing file paths. Inserts to the
|
||||
/// set when finding a unique path.
|
||||
pub fn get_unique_path(
|
||||
mut path: PathBuf,
|
||||
unique_set: &mut HashSet<String>,
|
||||
) -> PathBuf {
|
||||
let original_path = path.clone();
|
||||
let mut count = 2;
|
||||
// case insensitive comparison so the output works on case insensitive file systems
|
||||
while !unique_set.insert(path.to_string_lossy().to_lowercase()) {
|
||||
path = path_with_stem_suffix(&original_path, &format!("_{count}"));
|
||||
count += 1;
|
||||
}
|
||||
path
|
||||
}
|
||||
|
||||
pub fn make_url_relative(
|
||||
root: &ModuleSpecifier,
|
||||
url: &ModuleSpecifier,
|
||||
) -> Result<String, AnyError> {
|
||||
root.make_relative(url).ok_or_else(|| {
|
||||
anyhow!(
|
||||
"Error making url ({}) relative to root: {}",
|
||||
url.to_string(),
|
||||
root.to_string()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_remote_specifier(specifier: &ModuleSpecifier) -> bool {
|
||||
matches!(specifier.scheme().to_lowercase().as_str(), "http" | "https")
|
||||
}
|
||||
|
||||
pub fn is_remote_specifier_text(text: &str) -> bool {
|
||||
let text = text.trim_start().to_lowercase();
|
||||
text.starts_with("http:") || text.starts_with("https:")
|
||||
}
|
||||
|
||||
pub fn sanitize_filepath(text: &str) -> String {
|
||||
text
|
||||
.chars()
|
||||
.map(|c| if is_banned_path_char(c) { '_' } else { c })
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn partition_by_root_specifiers_same_sub_folder() {
|
||||
run_partition_by_root_specifiers_test(
|
||||
vec![
|
||||
"https://deno.land/x/mod/A.ts",
|
||||
"https://deno.land/x/mod/other/A.ts",
|
||||
],
|
||||
vec![(
|
||||
"https://deno.land/",
|
||||
vec![
|
||||
"https://deno.land/x/mod/A.ts",
|
||||
"https://deno.land/x/mod/other/A.ts",
|
||||
],
|
||||
)],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partition_by_root_specifiers_different_sub_folder() {
|
||||
run_partition_by_root_specifiers_test(
|
||||
vec![
|
||||
"https://deno.land/x/mod/A.ts",
|
||||
"https://deno.land/x/other/A.ts",
|
||||
],
|
||||
vec![(
|
||||
"https://deno.land/",
|
||||
vec![
|
||||
"https://deno.land/x/mod/A.ts",
|
||||
"https://deno.land/x/other/A.ts",
|
||||
],
|
||||
)],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partition_by_root_specifiers_different_hosts() {
|
||||
run_partition_by_root_specifiers_test(
|
||||
vec![
|
||||
"https://deno.land/mod/A.ts",
|
||||
"http://deno.land/B.ts",
|
||||
"https://deno.land:8080/C.ts",
|
||||
"https://localhost/mod/A.ts",
|
||||
"https://other/A.ts",
|
||||
],
|
||||
vec![
|
||||
("http://deno.land/", vec!["http://deno.land/B.ts"]),
|
||||
("https://deno.land/", vec!["https://deno.land/mod/A.ts"]),
|
||||
(
|
||||
"https://deno.land:8080/",
|
||||
vec!["https://deno.land:8080/C.ts"],
|
||||
),
|
||||
("https://localhost/", vec!["https://localhost/mod/A.ts"]),
|
||||
("https://other/", vec!["https://other/A.ts"]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
fn run_partition_by_root_specifiers_test(
|
||||
input: Vec<&str>,
|
||||
expected: Vec<(&str, Vec<&str>)>,
|
||||
) {
|
||||
let input = input
|
||||
.iter()
|
||||
.map(|s| ModuleSpecifier::parse(s).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let output = partition_by_root_specifiers(input.iter());
|
||||
// the assertion is much easier to compare when everything is strings
|
||||
let output = output
|
||||
.into_iter()
|
||||
.map(|(s, vec)| {
|
||||
(
|
||||
s.to_string(),
|
||||
vec.into_iter().map(|s| s.to_string()).collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let expected = expected
|
||||
.into_iter()
|
||||
.map(|(s, vec)| {
|
||||
(
|
||||
s.to_string(),
|
||||
vec.into_iter().map(|s| s.to_string()).collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(output, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unique_path() {
|
||||
let mut paths = HashSet::new();
|
||||
assert_eq!(
|
||||
get_unique_path(PathBuf::from("/test"), &mut paths),
|
||||
PathBuf::from("/test")
|
||||
);
|
||||
assert_eq!(
|
||||
get_unique_path(PathBuf::from("/test"), &mut paths),
|
||||
PathBuf::from("/test_2")
|
||||
);
|
||||
assert_eq!(
|
||||
get_unique_path(PathBuf::from("/test"), &mut paths),
|
||||
PathBuf::from("/test_3")
|
||||
);
|
||||
assert_eq!(
|
||||
get_unique_path(PathBuf::from("/TEST"), &mut paths),
|
||||
PathBuf::from("/TEST_4")
|
||||
);
|
||||
assert_eq!(
|
||||
get_unique_path(PathBuf::from("/test.txt"), &mut paths),
|
||||
PathBuf::from("/test.txt")
|
||||
);
|
||||
assert_eq!(
|
||||
get_unique_path(PathBuf::from("/test.txt"), &mut paths),
|
||||
PathBuf::from("/test_2.txt")
|
||||
);
|
||||
assert_eq!(
|
||||
get_unique_path(PathBuf::from("/TEST.TXT"), &mut paths),
|
||||
PathBuf::from("/TEST_3.TXT")
|
||||
);
|
||||
}
|
||||
}
|
357
cli/tools/vendor/test.rs
vendored
357
cli/tools/vendor/test.rs
vendored
|
@ -1,357 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_config::workspace::WorkspaceResolver;
|
||||
use deno_core::anyhow::anyhow;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::serde_json;
|
||||
use deno_graph::source::LoadFuture;
|
||||
use deno_graph::source::LoadResponse;
|
||||
use deno_graph::source::Loader;
|
||||
use deno_graph::DefaultModuleAnalyzer;
|
||||
use deno_graph::GraphKind;
|
||||
use deno_graph::ModuleGraph;
|
||||
use import_map::ImportMap;
|
||||
|
||||
use crate::args::JsxImportSourceConfig;
|
||||
use crate::cache::ParsedSourceCache;
|
||||
use crate::resolver::CliGraphResolver;
|
||||
use crate::resolver::CliGraphResolverOptions;
|
||||
|
||||
use super::build::VendorEnvironment;
|
||||
|
||||
// Utilities that help `deno vendor` get tested in memory.
|
||||
|
||||
type RemoteFileText = String;
|
||||
type RemoteFileHeaders = Option<HashMap<String, String>>;
|
||||
type RemoteFileResult = Result<(RemoteFileText, RemoteFileHeaders), String>;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct TestLoader {
|
||||
files: HashMap<ModuleSpecifier, RemoteFileResult>,
|
||||
redirects: HashMap<ModuleSpecifier, ModuleSpecifier>,
|
||||
}
|
||||
|
||||
impl TestLoader {
|
||||
pub fn add(
|
||||
&mut self,
|
||||
path_or_specifier: impl AsRef<str>,
|
||||
text: impl AsRef<str>,
|
||||
) -> &mut Self {
|
||||
self.add_result(path_or_specifier, Ok((text.as_ref().to_string(), None)))
|
||||
}
|
||||
|
||||
pub fn add_failure(
|
||||
&mut self,
|
||||
path_or_specifier: impl AsRef<str>,
|
||||
message: impl AsRef<str>,
|
||||
) -> &mut Self {
|
||||
self.add_result(path_or_specifier, Err(message.as_ref().to_string()))
|
||||
}
|
||||
|
||||
fn add_result(
|
||||
&mut self,
|
||||
path_or_specifier: impl AsRef<str>,
|
||||
result: RemoteFileResult,
|
||||
) -> &mut Self {
|
||||
if path_or_specifier
|
||||
.as_ref()
|
||||
.to_lowercase()
|
||||
.starts_with("http")
|
||||
{
|
||||
self.files.insert(
|
||||
ModuleSpecifier::parse(path_or_specifier.as_ref()).unwrap(),
|
||||
result,
|
||||
);
|
||||
} else {
|
||||
let path = make_path(path_or_specifier.as_ref());
|
||||
let specifier = ModuleSpecifier::from_file_path(path).unwrap();
|
||||
self.files.insert(specifier, result);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_with_headers(
|
||||
&mut self,
|
||||
specifier: impl AsRef<str>,
|
||||
text: impl AsRef<str>,
|
||||
headers: &[(&str, &str)],
|
||||
) -> &mut Self {
|
||||
let headers = headers
|
||||
.iter()
|
||||
.map(|(key, value)| (key.to_string(), value.to_string()))
|
||||
.collect();
|
||||
self.files.insert(
|
||||
ModuleSpecifier::parse(specifier.as_ref()).unwrap(),
|
||||
Ok((text.as_ref().to_string(), Some(headers))),
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_redirect(
|
||||
&mut self,
|
||||
from: impl AsRef<str>,
|
||||
to: impl AsRef<str>,
|
||||
) -> &mut Self {
|
||||
self.redirects.insert(
|
||||
ModuleSpecifier::parse(from.as_ref()).unwrap(),
|
||||
ModuleSpecifier::parse(to.as_ref()).unwrap(),
|
||||
);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Loader for TestLoader {
|
||||
fn load(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
_options: deno_graph::source::LoadOptions,
|
||||
) -> LoadFuture {
|
||||
if let Some(redirect) = self.redirects.get(specifier) {
|
||||
return Box::pin(futures::future::ready(Ok(Some(
|
||||
LoadResponse::Redirect {
|
||||
specifier: redirect.clone(),
|
||||
},
|
||||
))));
|
||||
}
|
||||
let result = self.files.get(specifier).map(|result| match result {
|
||||
Ok(result) => Ok(LoadResponse::Module {
|
||||
specifier: specifier.clone(),
|
||||
content: result.0.clone().into_bytes().into(),
|
||||
maybe_headers: result.1.clone(),
|
||||
}),
|
||||
Err(err) => Err(err),
|
||||
});
|
||||
let result = match result {
|
||||
Some(Ok(result)) => Ok(Some(result)),
|
||||
Some(Err(err)) => Err(anyhow!("{}", err)),
|
||||
None if specifier.scheme() == "data" => {
|
||||
deno_graph::source::load_data_url(specifier)
|
||||
}
|
||||
None => Ok(None),
|
||||
};
|
||||
Box::pin(futures::future::ready(result))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct TestVendorEnvironment {
|
||||
directories: RefCell<HashSet<PathBuf>>,
|
||||
files: RefCell<HashMap<PathBuf, String>>,
|
||||
}
|
||||
|
||||
impl VendorEnvironment for TestVendorEnvironment {
|
||||
fn create_dir_all(&self, dir_path: &Path) -> Result<(), AnyError> {
|
||||
let mut directories = self.directories.borrow_mut();
|
||||
for path in dir_path.ancestors() {
|
||||
if !directories.insert(path.to_path_buf()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_file(&self, file_path: &Path, text: &[u8]) -> Result<(), AnyError> {
|
||||
let parent = file_path.parent().unwrap();
|
||||
if !self.directories.borrow().contains(parent) {
|
||||
bail!("Directory not found: {}", parent.display());
|
||||
}
|
||||
self.files.borrow_mut().insert(
|
||||
file_path.to_path_buf(),
|
||||
String::from_utf8(text.to_vec()).unwrap(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VendorOutput {
|
||||
pub files: Vec<(String, String)>,
|
||||
pub import_map: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct VendorTestBuilder {
|
||||
entry_points: Vec<ModuleSpecifier>,
|
||||
loader: TestLoader,
|
||||
maybe_original_import_map: Option<ImportMap>,
|
||||
environment: TestVendorEnvironment,
|
||||
jsx_import_source_config: Option<JsxImportSourceConfig>,
|
||||
}
|
||||
|
||||
impl VendorTestBuilder {
|
||||
pub fn with_default_setup() -> Self {
|
||||
let mut builder = VendorTestBuilder::default();
|
||||
builder.add_entry_point("/mod.ts");
|
||||
builder
|
||||
}
|
||||
|
||||
pub fn resolve_to_url(&self, path: &str) -> ModuleSpecifier {
|
||||
ModuleSpecifier::from_file_path(make_path(path)).unwrap()
|
||||
}
|
||||
|
||||
pub fn new_import_map(&self, base_path: &str) -> ImportMap {
|
||||
let base = self.resolve_to_url(base_path);
|
||||
ImportMap::new(base)
|
||||
}
|
||||
|
||||
pub fn set_original_import_map(
|
||||
&mut self,
|
||||
import_map: ImportMap,
|
||||
) -> &mut Self {
|
||||
self.maybe_original_import_map = Some(import_map);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add_entry_point(&mut self, entry_point: impl AsRef<str>) -> &mut Self {
|
||||
let entry_point = make_path(entry_point.as_ref());
|
||||
self
|
||||
.entry_points
|
||||
.push(ModuleSpecifier::from_file_path(entry_point).unwrap());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_jsx_import_source_config(
|
||||
&mut self,
|
||||
jsx_import_source_config: JsxImportSourceConfig,
|
||||
) -> &mut Self {
|
||||
self.jsx_import_source_config = Some(jsx_import_source_config);
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn build(&mut self) -> Result<VendorOutput, AnyError> {
|
||||
let output_dir = make_path("/vendor");
|
||||
let entry_points = self.entry_points.clone();
|
||||
let loader = self.loader.clone();
|
||||
let parsed_source_cache = ParsedSourceCache::default();
|
||||
let resolver = Arc::new(build_resolver(
|
||||
output_dir.parent().unwrap(),
|
||||
self.jsx_import_source_config.clone(),
|
||||
self.maybe_original_import_map.clone(),
|
||||
));
|
||||
super::build::build(super::build::BuildInput {
|
||||
entry_points,
|
||||
build_graph: {
|
||||
let resolver = resolver.clone();
|
||||
move |entry_points| {
|
||||
async move {
|
||||
Ok(
|
||||
build_test_graph(
|
||||
entry_points,
|
||||
loader,
|
||||
resolver.as_graph_resolver(),
|
||||
&DefaultModuleAnalyzer,
|
||||
)
|
||||
.await,
|
||||
)
|
||||
}
|
||||
.boxed_local()
|
||||
}
|
||||
},
|
||||
parsed_source_cache: &parsed_source_cache,
|
||||
output_dir: &output_dir,
|
||||
maybe_original_import_map: self.maybe_original_import_map.as_ref(),
|
||||
maybe_jsx_import_source: self.jsx_import_source_config.as_ref(),
|
||||
resolver: resolver.as_graph_resolver(),
|
||||
environment: &self.environment,
|
||||
})
|
||||
.await?;
|
||||
|
||||
let mut files = self.environment.files.borrow_mut();
|
||||
let import_map = files.remove(&output_dir.join("import_map.json"));
|
||||
let mut files = files
|
||||
.iter()
|
||||
.map(|(path, text)| (path_to_string(path), text.to_string()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
files.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
|
||||
Ok(VendorOutput {
|
||||
import_map: import_map.map(|text| serde_json::from_str(&text).unwrap()),
|
||||
files,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn with_loader(&mut self, action: impl Fn(&mut TestLoader)) -> &mut Self {
|
||||
action(&mut self.loader);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
fn build_resolver(
|
||||
root_dir: &Path,
|
||||
maybe_jsx_import_source_config: Option<JsxImportSourceConfig>,
|
||||
maybe_original_import_map: Option<ImportMap>,
|
||||
) -> CliGraphResolver {
|
||||
CliGraphResolver::new(CliGraphResolverOptions {
|
||||
node_resolver: None,
|
||||
npm_resolver: None,
|
||||
sloppy_imports_resolver: None,
|
||||
workspace_resolver: Arc::new(WorkspaceResolver::new_raw(
|
||||
Arc::new(ModuleSpecifier::from_directory_path(root_dir).unwrap()),
|
||||
maybe_original_import_map,
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
deno_config::workspace::PackageJsonDepResolution::Enabled,
|
||||
)),
|
||||
maybe_jsx_import_source_config,
|
||||
maybe_vendor_dir: None,
|
||||
bare_node_builtins_enabled: false,
|
||||
})
|
||||
}
|
||||
|
||||
async fn build_test_graph(
|
||||
roots: Vec<ModuleSpecifier>,
|
||||
loader: TestLoader,
|
||||
resolver: &dyn deno_graph::source::Resolver,
|
||||
analyzer: &dyn deno_graph::ModuleAnalyzer,
|
||||
) -> ModuleGraph {
|
||||
let mut graph = ModuleGraph::new(GraphKind::All);
|
||||
graph
|
||||
.build(
|
||||
roots,
|
||||
&loader,
|
||||
deno_graph::BuildOptions {
|
||||
resolver: Some(resolver),
|
||||
module_analyzer: analyzer,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await;
|
||||
graph
|
||||
}
|
||||
|
||||
fn make_path(text: &str) -> PathBuf {
|
||||
// This should work all in memory. We're waiting on
|
||||
// https://github.com/servo/rust-url/issues/730 to provide
|
||||
// a cross platform path here
|
||||
assert!(text.starts_with('/'));
|
||||
if cfg!(windows) {
|
||||
PathBuf::from(format!("C:{}", text.replace('/', "\\")))
|
||||
} else {
|
||||
PathBuf::from(text)
|
||||
}
|
||||
}
|
||||
|
||||
fn path_to_string<P>(path: P) -> String
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let path = path.as_ref();
|
||||
// inverse of the function above
|
||||
let path = path.to_string_lossy();
|
||||
if cfg!(windows) {
|
||||
path.replace("C:\\", "\\").replace('\\', "/")
|
||||
} else {
|
||||
path.to_string()
|
||||
}
|
||||
}
|
|
@ -46,10 +46,6 @@ delete Object.prototype.__proto__;
|
|||
"UnixListenOptions",
|
||||
"createHttpClient",
|
||||
"dlopen",
|
||||
"flock",
|
||||
"flockSync",
|
||||
"funlock",
|
||||
"funlockSync",
|
||||
"listen",
|
||||
"listenDatagram",
|
||||
"openKv",
|
||||
|
@ -1156,7 +1152,6 @@ delete Object.prototype.__proto__;
|
|||
"strict": true,
|
||||
"target": "esnext",
|
||||
"useDefineForClassFields": true,
|
||||
"useUnknownInCatchVariables": false,
|
||||
"jsx": "react",
|
||||
"jsxFactory": "React.createElement",
|
||||
"jsxFragmentFactory": "React.Fragment",
|
||||
|
|
1394
cli/tsc/dts/lib.deno.ns.d.ts
vendored
1394
cli/tsc/dts/lib.deno.ns.d.ts
vendored
File diff suppressed because it is too large
Load diff
6
cli/tsc/dts/lib.deno.shared_globals.d.ts
vendored
6
cli/tsc/dts/lib.deno.shared_globals.d.ts
vendored
|
@ -593,16 +593,12 @@ declare interface Performance extends EventTarget {
|
|||
endMark?: string,
|
||||
): PerformanceMeasure;
|
||||
|
||||
/** Returns a current time from Deno's start in milliseconds.
|
||||
*
|
||||
* Use the permission flag `--allow-hrtime` to return a precise value.
|
||||
/** Returns a current time from Deno's start in fractional milliseconds.
|
||||
*
|
||||
* ```ts
|
||||
* const t = performance.now();
|
||||
* console.log(`${t} ms since start!`);
|
||||
* ```
|
||||
*
|
||||
* @tags allow-hrtime
|
||||
*/
|
||||
now(): number;
|
||||
|
||||
|
|
133
cli/tsc/dts/lib.deno.unstable.d.ts
vendored
133
cli/tsc/dts/lib.deno.unstable.d.ts
vendored
|
@ -10,18 +10,6 @@
|
|||
declare namespace Deno {
|
||||
export {}; // stop default export type behavior
|
||||
|
||||
/** Information for a HTTP request.
|
||||
*
|
||||
* @category HTTP Server
|
||||
* @experimental
|
||||
*/
|
||||
export interface ServeHandlerInfo {
|
||||
/** The remote address of the connection. */
|
||||
remoteAddr: Deno.NetAddr;
|
||||
/** The completion promise */
|
||||
completed: Promise<void>;
|
||||
}
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* Retrieve the process umask. If `mask` is provided, sets the process umask.
|
||||
|
@ -852,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<T extends UnstableRunOptions = UnstableRunOptions>(
|
||||
opt: T,
|
||||
): Process<T>;
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* A custom `HttpClient` for use with {@linkcode fetch} function. This is
|
||||
|
@ -1216,44 +1130,6 @@ declare namespace Deno {
|
|||
options: UnixListenOptions & { transport: "unixpacket" },
|
||||
): DatagramConn;
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* Acquire an advisory file-system lock for the provided file.
|
||||
*
|
||||
* @param [exclusive=false]
|
||||
* @category File System
|
||||
* @experimental
|
||||
*/
|
||||
export function flock(rid: number, exclusive?: boolean): Promise<void>;
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* Acquire an advisory file-system lock synchronously for the provided file.
|
||||
*
|
||||
* @param [exclusive=false]
|
||||
* @category File System
|
||||
* @experimental
|
||||
*/
|
||||
export function flockSync(rid: number, exclusive?: boolean): void;
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* Release an advisory file-system lock for the provided file.
|
||||
*
|
||||
* @category File System
|
||||
* @experimental
|
||||
*/
|
||||
export function funlock(rid: number): Promise<void>;
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* Release an advisory file-system lock for the provided file synchronously.
|
||||
*
|
||||
* @category File System
|
||||
* @experimental
|
||||
*/
|
||||
export function funlockSync(rid: number): void;
|
||||
|
||||
/** **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* Open a new {@linkcode Deno.Kv} connection to persist data.
|
||||
|
@ -2163,7 +2039,10 @@ declare namespace Deno {
|
|||
* @category Jupyter
|
||||
* @experimental
|
||||
*/
|
||||
export function display(obj: unknown, options?: DisplayOptions): void;
|
||||
export function display(
|
||||
obj: unknown,
|
||||
options?: DisplayOptions,
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Show Markdown in Jupyter frontends with a tagged template function.
|
||||
|
@ -2236,12 +2115,12 @@ declare namespace Deno {
|
|||
* Format an object for displaying in Deno
|
||||
*
|
||||
* @param obj - The object to be displayed
|
||||
* @returns MediaBundle
|
||||
* @returns Promise<MediaBundle>
|
||||
*
|
||||
* @category Jupyter
|
||||
* @experimental
|
||||
*/
|
||||
export function format(obj: unknown): MediaBundle;
|
||||
export function format(obj: unknown): Promise<MediaBundle>;
|
||||
|
||||
/**
|
||||
* Broadcast a message on IO pub channel.
|
||||
|
|
6
cli/tsc/dts/lib.dom.d.ts
vendored
6
cli/tsc/dts/lib.dom.d.ts
vendored
|
@ -17550,7 +17550,7 @@ declare var PerformanceServerTiming: {
|
|||
};
|
||||
|
||||
/**
|
||||
* A legacy interface kept for backwards compatibility and contains properties that offer performance timing information for various events which occur during the loading and use of the current page. You get a PerformanceTiming object describing your page using the window.performance.timing property.
|
||||
* A legacy interface kept for backwards compatibility and contains properties that offer performance timing information for various events which occur during the loading and use of the current page. You get a PerformanceTiming object describing your page using the globalThis.performance.timing property.
|
||||
* @deprecated This interface is deprecated in the Navigation Timing Level 2 specification. Please use the PerformanceNavigationTiming interface instead.
|
||||
*
|
||||
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PerformanceTiming)
|
||||
|
@ -17833,7 +17833,7 @@ declare var Plugin: {
|
|||
};
|
||||
|
||||
/**
|
||||
* Used to store a list of Plugin objects describing the available plugins; it's returned by the window.navigator.plugins property. The PluginArray is not a JavaScript array, but has the length property and supports accessing individual items using bracket notation (plugins[2]), as well as via item(index) and namedItem("name") methods.
|
||||
* Used to store a list of Plugin objects describing the available plugins; it's returned by the globalThis.navigator.plugins property. The PluginArray is not a JavaScript array, but has the length property and supports accessing individual items using bracket notation (plugins[2]), as well as via item(index) and namedItem("name") methods.
|
||||
* @deprecated
|
||||
*
|
||||
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/PluginArray)
|
||||
|
@ -22155,7 +22155,7 @@ declare var SubmitEvent: {
|
|||
};
|
||||
|
||||
/**
|
||||
* This Web Crypto API interface provides a number of low-level cryptographic functions. It is accessed via the Crypto.subtle properties available in a window context (via Window.crypto).
|
||||
* This Web Crypto API interface provides a number of low-level cryptographic functions. It is accessed via the Crypto.subtle properties available in a window context (via globalThis.crypto).
|
||||
* Available only in secure contexts.
|
||||
*
|
||||
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto)
|
||||
|
|
4
cli/tsc/dts/lib.webworker.d.ts
vendored
4
cli/tsc/dts/lib.webworker.d.ts
vendored
|
@ -5407,7 +5407,7 @@ declare var StylePropertyMapReadOnly: {
|
|||
};
|
||||
|
||||
/**
|
||||
* This Web Crypto API interface provides a number of low-level cryptographic functions. It is accessed via the Crypto.subtle properties available in a window context (via Window.crypto).
|
||||
* This Web Crypto API interface provides a number of low-level cryptographic functions. It is accessed via the Crypto.subtle properties available in a window context (via globalThis.crypto).
|
||||
* Available only in secure contexts.
|
||||
*
|
||||
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/SubtleCrypto)
|
||||
|
@ -8641,7 +8641,7 @@ declare var WorkerLocation: {
|
|||
};
|
||||
|
||||
/**
|
||||
* A subset of the Navigator interface allowed to be accessed from a Worker. Such an object is initialized for each worker and is available via the WorkerGlobalScope.navigator property obtained by calling window.self.navigator.
|
||||
* A subset of the Navigator interface allowed to be accessed from a Worker. Such an object is initialized for each worker and is available via the WorkerGlobalScope.navigator property obtained by calling globalThis.self.navigator.
|
||||
*
|
||||
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/WorkerNavigator)
|
||||
*/
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::env::current_dir;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Error;
|
||||
use std::io::ErrorKind;
|
||||
|
@ -18,7 +17,6 @@ use deno_config::glob::WalkEntry;
|
|||
use deno_core::anyhow::anyhow;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
pub use deno_core::normalize_path;
|
||||
use deno_core::unsync::spawn_blocking;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_runtime::deno_fs::FileSystem;
|
||||
|
@ -255,18 +253,6 @@ fn canonicalize_path_maybe_not_exists_with_custom_fn(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn resolve_from_cwd(path: &Path) -> Result<PathBuf, AnyError> {
|
||||
let resolved_path = if path.is_absolute() {
|
||||
path.to_owned()
|
||||
} else {
|
||||
let cwd =
|
||||
current_dir().context("Failed to get current working directory")?;
|
||||
cwd.join(path)
|
||||
};
|
||||
|
||||
Ok(normalize_path(resolved_path))
|
||||
}
|
||||
|
||||
/// Collects module specifiers that satisfy the given predicate as a file path, by recursively walking `include`.
|
||||
/// Specifiers that start with http and https are left intact.
|
||||
/// Note: This ignores all .git and node_modules folders.
|
||||
|
@ -510,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<ErrorKind>| {
|
||||
Error::new(
|
||||
err.kind(),
|
||||
kind.unwrap_or_else(|| err.kind()),
|
||||
format!(
|
||||
"{}, symlink '{}' -> '{}'",
|
||||
err,
|
||||
|
@ -524,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(())
|
||||
}
|
||||
|
@ -715,30 +708,13 @@ pub fn specifier_from_file_path(
|
|||
mod tests {
|
||||
use super::*;
|
||||
use deno_core::futures;
|
||||
use deno_core::normalize_path;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use pretty_assertions::assert_eq;
|
||||
use test_util::PathRef;
|
||||
use test_util::TempDir;
|
||||
use tokio::sync::Notify;
|
||||
|
||||
#[test]
|
||||
fn resolve_from_cwd_child() {
|
||||
let cwd = current_dir().unwrap();
|
||||
assert_eq!(resolve_from_cwd(Path::new("a")).unwrap(), cwd.join("a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_from_cwd_dot() {
|
||||
let cwd = current_dir().unwrap();
|
||||
assert_eq!(resolve_from_cwd(Path::new(".")).unwrap(), cwd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_from_cwd_parent() {
|
||||
let cwd = current_dir().unwrap();
|
||||
assert_eq!(resolve_from_cwd(Path::new("a/..")).unwrap(), cwd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_path() {
|
||||
assert_eq!(normalize_path(Path::new("a/../b")), PathBuf::from("b"));
|
||||
|
@ -756,14 +732,6 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_from_cwd_absolute() {
|
||||
let expected = Path::new("a");
|
||||
let cwd = current_dir().unwrap();
|
||||
let absolute_expected = cwd.join(expected);
|
||||
assert_eq!(resolve_from_cwd(expected).unwrap(), absolute_expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_collect_specifiers() {
|
||||
fn create_files(dir_path: &PathRef, files: &[&str]) {
|
||||
|
|
|
@ -41,8 +41,10 @@ impl log::Log for CliLogger {
|
|||
pub fn init(maybe_level: Option<log::Level>) {
|
||||
let log_level = maybe_level.unwrap_or(log::Level::Info);
|
||||
let logger = env_logger::Builder::from_env(
|
||||
env_logger::Env::default()
|
||||
.default_filter_or(log_level.to_level_filter().to_string()),
|
||||
env_logger::Env::new()
|
||||
// Use `DENO_LOG` and `DENO_LOG_STYLE` instead of `RUST_` prefix
|
||||
.filter_or("DENO_LOG", log_level.to_level_filter().to_string())
|
||||
.write_style("DENO_LOG_STYLE"),
|
||||
)
|
||||
// https://github.com/denoland/deno/issues/6641
|
||||
.filter_module("rustyline", log::LevelFilter::Off)
|
||||
|
|
|
@ -42,6 +42,21 @@ pub fn get_extension(file_path: &Path) -> Option<String> {
|
|||
.map(|e| e.to_lowercase());
|
||||
}
|
||||
|
||||
pub fn specifier_has_extension(
|
||||
specifier: &ModuleSpecifier,
|
||||
searching_ext: &str,
|
||||
) -> bool {
|
||||
let Some((_, ext)) = specifier.path().rsplit_once('.') else {
|
||||
return false;
|
||||
};
|
||||
let searching_ext = searching_ext.strip_prefix('.').unwrap_or(searching_ext);
|
||||
debug_assert!(!searching_ext.contains('.')); // exts like .d.ts are not implemented here
|
||||
if ext.len() != searching_ext.len() {
|
||||
return false;
|
||||
}
|
||||
ext.eq_ignore_ascii_case(searching_ext)
|
||||
}
|
||||
|
||||
pub fn get_atomic_dir_path(file_path: &Path) -> PathBuf {
|
||||
let rand = gen_rand_path_component();
|
||||
let new_file_name = format!(
|
||||
|
@ -145,34 +160,6 @@ pub fn relative_specifier(
|
|||
Some(to_percent_decoded_str(&text))
|
||||
}
|
||||
|
||||
/// Gets a path with the specified file stem suffix.
|
||||
///
|
||||
/// Ex. `file.ts` with suffix `_2` returns `file_2.ts`
|
||||
pub fn path_with_stem_suffix(path: &Path, suffix: &str) -> PathBuf {
|
||||
if let Some(file_name) = path.file_name().map(|f| f.to_string_lossy()) {
|
||||
if let Some(file_stem) = path.file_stem().map(|f| f.to_string_lossy()) {
|
||||
if let Some(ext) = path.extension().map(|f| f.to_string_lossy()) {
|
||||
return if file_stem.to_lowercase().ends_with(".d") {
|
||||
path.with_file_name(format!(
|
||||
"{}{}.{}.{}",
|
||||
&file_stem[..file_stem.len() - ".d".len()],
|
||||
suffix,
|
||||
// maintain casing
|
||||
&file_stem[file_stem.len() - "d".len()..],
|
||||
ext
|
||||
))
|
||||
} else {
|
||||
path.with_file_name(format!("{file_stem}{suffix}.{ext}"))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
path.with_file_name(format!("{file_name}{suffix}"))
|
||||
} else {
|
||||
path.with_file_name(suffix)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(windows, allow(dead_code))]
|
||||
pub fn relative_path(from: &Path, to: &Path) -> Option<PathBuf> {
|
||||
pathdiff::diff_paths(to, from)
|
||||
|
@ -406,43 +393,15 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_path_with_stem_suffix() {
|
||||
assert_eq!(
|
||||
path_with_stem_suffix(&PathBuf::from("/"), "_2"),
|
||||
PathBuf::from("/_2")
|
||||
);
|
||||
assert_eq!(
|
||||
path_with_stem_suffix(&PathBuf::from("/test"), "_2"),
|
||||
PathBuf::from("/test_2")
|
||||
);
|
||||
assert_eq!(
|
||||
path_with_stem_suffix(&PathBuf::from("/test.txt"), "_2"),
|
||||
PathBuf::from("/test_2.txt")
|
||||
);
|
||||
assert_eq!(
|
||||
path_with_stem_suffix(&PathBuf::from("/test/subdir"), "_2"),
|
||||
PathBuf::from("/test/subdir_2")
|
||||
);
|
||||
assert_eq!(
|
||||
path_with_stem_suffix(&PathBuf::from("/test/subdir.other.txt"), "_2"),
|
||||
PathBuf::from("/test/subdir.other_2.txt")
|
||||
);
|
||||
assert_eq!(
|
||||
path_with_stem_suffix(&PathBuf::from("/test.d.ts"), "_2"),
|
||||
PathBuf::from("/test_2.d.ts")
|
||||
);
|
||||
assert_eq!(
|
||||
path_with_stem_suffix(&PathBuf::from("/test.D.TS"), "_2"),
|
||||
PathBuf::from("/test_2.D.TS")
|
||||
);
|
||||
assert_eq!(
|
||||
path_with_stem_suffix(&PathBuf::from("/test.d.mts"), "_2"),
|
||||
PathBuf::from("/test_2.d.mts")
|
||||
);
|
||||
assert_eq!(
|
||||
path_with_stem_suffix(&PathBuf::from("/test.d.cts"), "_2"),
|
||||
PathBuf::from("/test_2.d.cts")
|
||||
);
|
||||
fn test_specifier_has_extension() {
|
||||
fn get(specifier: &str, ext: &str) -> bool {
|
||||
specifier_has_extension(&ModuleSpecifier::parse(specifier).unwrap(), ext)
|
||||
}
|
||||
|
||||
assert!(get("file:///a/b/c.ts", "ts"));
|
||||
assert!(get("file:///a/b/c.ts", ".ts"));
|
||||
assert!(!get("file:///a/b/c.ts", ".cts"));
|
||||
assert!(get("file:///a/b/c.CtS", ".cts"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -9,6 +9,8 @@ const TYPESCRIPT: &str = env!("TS_VERSION");
|
|||
const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
// TODO(bartlomieju): ideally we could remove this const.
|
||||
const IS_CANARY: bool = option_env!("DENO_CANARY").is_some();
|
||||
// TODO(bartlomieju): this is temporary, to allow Homebrew to cut RC releases as well
|
||||
const IS_RC: bool = option_env!("DENO_RC").is_some();
|
||||
|
||||
pub static DENO_VERSION_INFO: Lazy<DenoVersionInfo> = Lazy::new(|| {
|
||||
let release_channel = libsui::find_section("denover")
|
||||
|
@ -17,6 +19,8 @@ pub static DENO_VERSION_INFO: Lazy<DenoVersionInfo> = Lazy::new(|| {
|
|||
.unwrap_or({
|
||||
if IS_CANARY {
|
||||
ReleaseChannel::Canary
|
||||
} else if IS_RC {
|
||||
ReleaseChannel::Rc
|
||||
} else {
|
||||
ReleaseChannel::Stable
|
||||
}
|
||||
|
|
|
@ -116,6 +116,9 @@ pub struct CliMainWorkerOptions {
|
|||
pub skip_op_registration: bool,
|
||||
pub create_hmr_runner: Option<CreateHmrRunnerCb>,
|
||||
pub create_coverage_collector: Option<CreateCoverageCollectorCb>,
|
||||
pub node_ipc: Option<i64>,
|
||||
pub serve_port: Option<u16>,
|
||||
pub serve_host: Option<String>,
|
||||
}
|
||||
|
||||
struct SharedWorkerState {
|
||||
|
@ -135,13 +138,7 @@ struct SharedWorkerState {
|
|||
maybe_inspector_server: Option<Arc<InspectorServer>>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
feature_checker: Arc<FeatureChecker>,
|
||||
node_ipc: Option<i64>,
|
||||
enable_future_features: bool,
|
||||
disable_deprecated_api_warning: bool,
|
||||
verbose_deprecated_api_warning: bool,
|
||||
code_cache: Option<Arc<dyn code_cache::CodeCache>>,
|
||||
serve_port: Option<u16>,
|
||||
serve_host: Option<String>,
|
||||
}
|
||||
|
||||
impl SharedWorkerState {
|
||||
|
@ -434,14 +431,8 @@ impl CliMainWorkerFactory {
|
|||
maybe_inspector_server: Option<Arc<InspectorServer>>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
feature_checker: Arc<FeatureChecker>,
|
||||
options: CliMainWorkerOptions,
|
||||
node_ipc: Option<i64>,
|
||||
serve_port: Option<u16>,
|
||||
serve_host: Option<String>,
|
||||
enable_future_features: bool,
|
||||
disable_deprecated_api_warning: bool,
|
||||
verbose_deprecated_api_warning: bool,
|
||||
code_cache: Option<Arc<dyn code_cache::CodeCache>>,
|
||||
options: CliMainWorkerOptions,
|
||||
) -> Self {
|
||||
Self {
|
||||
shared: Arc::new(SharedWorkerState {
|
||||
|
@ -461,12 +452,6 @@ impl CliMainWorkerFactory {
|
|||
maybe_inspector_server,
|
||||
maybe_lockfile,
|
||||
feature_checker,
|
||||
node_ipc,
|
||||
serve_port,
|
||||
serve_host,
|
||||
enable_future_features,
|
||||
disable_deprecated_api_warning,
|
||||
verbose_deprecated_api_warning,
|
||||
code_cache,
|
||||
}),
|
||||
}
|
||||
|
@ -602,13 +587,10 @@ impl CliMainWorkerFactory {
|
|||
has_node_modules_dir: shared.options.has_node_modules_dir,
|
||||
argv0: shared.options.argv0.clone(),
|
||||
node_debug: shared.options.node_debug.clone(),
|
||||
node_ipc_fd: shared.node_ipc,
|
||||
disable_deprecated_api_warning: shared.disable_deprecated_api_warning,
|
||||
verbose_deprecated_api_warning: shared.verbose_deprecated_api_warning,
|
||||
future: shared.enable_future_features,
|
||||
node_ipc_fd: shared.options.node_ipc,
|
||||
mode,
|
||||
serve_port: shared.serve_port,
|
||||
serve_host: shared.serve_host.clone(),
|
||||
serve_port: shared.options.serve_port,
|
||||
serve_host: shared.options.serve_host.clone(),
|
||||
},
|
||||
extensions: custom_extensions,
|
||||
startup_snapshot: crate::js::deno_isolate_init(),
|
||||
|
@ -801,12 +783,9 @@ fn create_web_worker_callback(
|
|||
argv0: shared.options.argv0.clone(),
|
||||
node_debug: shared.options.node_debug.clone(),
|
||||
node_ipc_fd: None,
|
||||
disable_deprecated_api_warning: shared.disable_deprecated_api_warning,
|
||||
verbose_deprecated_api_warning: shared.verbose_deprecated_api_warning,
|
||||
future: shared.enable_future_features,
|
||||
mode: WorkerExecutionMode::Worker,
|
||||
serve_port: shared.serve_port,
|
||||
serve_host: shared.serve_host.clone(),
|
||||
serve_port: shared.options.serve_port,
|
||||
serve_host: shared.options.serve_host.clone(),
|
||||
},
|
||||
extensions: vec![],
|
||||
startup_snapshot: crate::js::deno_isolate_init(),
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_broadcast_channel"
|
||||
version = "0.160.0"
|
||||
version = "0.162.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
2
ext/cache/01_cache.js
vendored
2
ext/cache/01_cache.js
vendored
|
@ -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") {
|
||||
|
|
2
ext/cache/Cargo.toml
vendored
2
ext/cache/Cargo.toml
vendored
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_cache"
|
||||
version = "0.98.0"
|
||||
version = "0.100.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
2
ext/cache/lib.rs
vendored
2
ext/cache/lib.rs
vendored
|
@ -211,7 +211,7 @@ where
|
|||
state.put(cache);
|
||||
Ok(state.borrow::<CA>().clone())
|
||||
} else {
|
||||
Err(type_error("CacheStorage is not available in this context."))
|
||||
Err(type_error("CacheStorage is not available in this context"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_canvas"
|
||||
version = "0.35.0"
|
||||
version = "0.37.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -179,7 +179,7 @@ class AssertionError extends Error {
|
|||
}
|
||||
}
|
||||
|
||||
function assert(cond, msg = "Assertion failed.") {
|
||||
function assert(cond, msg = "Assertion failed") {
|
||||
if (!cond) {
|
||||
throw new AssertionError(msg);
|
||||
}
|
||||
|
@ -260,6 +260,7 @@ const colors = {
|
|||
|
||||
function defineColorAlias(target, alias) {
|
||||
ObjectDefineProperty(colors, alias, {
|
||||
__proto__: null,
|
||||
get() {
|
||||
return this[target];
|
||||
},
|
||||
|
@ -3236,8 +3237,8 @@ class Console {
|
|||
table = (data = undefined, properties = undefined) => {
|
||||
if (properties !== undefined && !ArrayIsArray(properties)) {
|
||||
throw new Error(
|
||||
"The 'properties' argument must be of type Array. " +
|
||||
"Received type " + typeof properties,
|
||||
"The 'properties' argument must be of type Array: " +
|
||||
"received type " + typeof properties,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3447,7 +3448,10 @@ function inspect(
|
|||
function createFilteredInspectProxy({ object, keys, evaluate }) {
|
||||
const obj = class {};
|
||||
if (object.constructor?.name) {
|
||||
ObjectDefineProperty(obj, "name", { value: object.constructor.name });
|
||||
ObjectDefineProperty(obj, "name", {
|
||||
__proto__: null,
|
||||
value: object.constructor.name,
|
||||
});
|
||||
}
|
||||
|
||||
return new Proxy(new obj(), {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_console"
|
||||
version = "0.166.0"
|
||||
version = "0.168.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -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> | 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(
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_cron"
|
||||
version = "0.46.0"
|
||||
version = "0.48.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue