mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 04:52:26 -05:00
Merge branch 'main' into patch-1
This commit is contained in:
commit
104a07ffe7
2245 changed files with 41864 additions and 12809 deletions
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 = 27;
|
||||
const cacheVersion = 28;
|
||||
|
||||
const ubuntuX86Runner = "ubuntu-24.04";
|
||||
const ubuntuX86XlRunner = "ubuntu-24.04-xl";
|
||||
|
|
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
|
@ -361,8 +361,8 @@ jobs:
|
|||
path: |-
|
||||
~/.cargo/registry/index
|
||||
~/.cargo/registry/cache
|
||||
key: '27-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
|
||||
restore-keys: '27-cargo-home-${{ matrix.os }}-${{ matrix.arch }}'
|
||||
key: '28-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
|
||||
restore-keys: '28-cargo-home-${{ matrix.os }}-${{ matrix.arch }}'
|
||||
if: '!(matrix.skip)'
|
||||
- name: Restore cache build output (PR)
|
||||
uses: actions/cache/restore@v4
|
||||
|
@ -375,7 +375,7 @@ jobs:
|
|||
!./target/*/*.zip
|
||||
!./target/*/*.tar.gz
|
||||
key: never_saved
|
||||
restore-keys: '27-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-'
|
||||
restore-keys: '28-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
|
||||
|
@ -685,7 +685,7 @@ jobs:
|
|||
!./target/*/*.zip
|
||||
!./target/*/*.sha256sum
|
||||
!./target/*/*.tar.gz
|
||||
key: '27-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
|
||||
key: '28-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
|
||||
publish-canary:
|
||||
name: publish canary
|
||||
runs-on: ubuntu-24.04
|
||||
|
|
765
Cargo.lock
generated
765
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
83
Cargo.toml
83
Cargo.toml
|
@ -21,6 +21,7 @@ members = [
|
|||
"ext/napi/sym",
|
||||
"ext/net",
|
||||
"ext/node",
|
||||
"ext/telemetry",
|
||||
"ext/url",
|
||||
"ext/web",
|
||||
"ext/webgpu",
|
||||
|
@ -29,6 +30,7 @@ members = [
|
|||
"ext/webstorage",
|
||||
"resolvers/deno",
|
||||
"resolvers/node",
|
||||
"resolvers/npm_cache",
|
||||
"runtime",
|
||||
"runtime/permissions",
|
||||
"tests",
|
||||
|
@ -45,20 +47,20 @@ license = "MIT"
|
|||
repository = "https://github.com/denoland/deno"
|
||||
|
||||
[workspace.dependencies]
|
||||
deno_ast = { version = "=0.43.3", features = ["transpiling"] }
|
||||
deno_core = { version = "0.322.0" }
|
||||
deno_ast = { version = "=0.44.0", features = ["transpiling"] }
|
||||
deno_core = { version = "0.324.0" }
|
||||
|
||||
deno_bench_util = { version = "0.173.0", path = "./bench_util" }
|
||||
deno_config = { version = "=0.39.2", features = ["workspace", "sync"] }
|
||||
deno_lockfile = "=0.23.1"
|
||||
deno_bench_util = { version = "0.174.0", path = "./bench_util" }
|
||||
deno_config = { version = "=0.39.3", features = ["workspace", "sync"] }
|
||||
deno_lockfile = "=0.23.2"
|
||||
deno_media_type = { version = "0.2.0", features = ["module_specifier"] }
|
||||
deno_npm = "=0.25.4"
|
||||
deno_npm = "=0.26.0"
|
||||
deno_path_util = "=0.2.1"
|
||||
deno_permissions = { version = "0.39.0", path = "./runtime/permissions" }
|
||||
deno_runtime = { version = "0.188.0", path = "./runtime" }
|
||||
deno_semver = "=0.5.16"
|
||||
deno_permissions = { version = "0.40.0", path = "./runtime/permissions" }
|
||||
deno_runtime = { version = "0.189.0", path = "./runtime" }
|
||||
deno_semver = "=0.6.0"
|
||||
deno_terminal = "0.2.0"
|
||||
napi_sym = { version = "0.109.0", path = "./ext/napi/sym" }
|
||||
napi_sym = { version = "0.110.0", path = "./ext/napi/sym" }
|
||||
test_util = { package = "test_server", path = "./tests/util/server" }
|
||||
|
||||
denokv_proto = "0.8.4"
|
||||
|
@ -67,32 +69,34 @@ denokv_remote = "0.8.4"
|
|||
denokv_sqlite = { default-features = false, version = "0.8.4" }
|
||||
|
||||
# exts
|
||||
deno_broadcast_channel = { version = "0.173.0", path = "./ext/broadcast_channel" }
|
||||
deno_cache = { version = "0.111.0", path = "./ext/cache" }
|
||||
deno_canvas = { version = "0.48.0", path = "./ext/canvas" }
|
||||
deno_console = { version = "0.179.0", path = "./ext/console" }
|
||||
deno_cron = { version = "0.59.0", path = "./ext/cron" }
|
||||
deno_crypto = { version = "0.193.0", path = "./ext/crypto" }
|
||||
deno_fetch = { version = "0.203.0", path = "./ext/fetch" }
|
||||
deno_ffi = { version = "0.166.0", path = "./ext/ffi" }
|
||||
deno_fs = { version = "0.89.0", path = "./ext/fs" }
|
||||
deno_http = { version = "0.177.0", path = "./ext/http" }
|
||||
deno_io = { version = "0.89.0", path = "./ext/io" }
|
||||
deno_kv = { version = "0.87.0", path = "./ext/kv" }
|
||||
deno_napi = { version = "0.110.0", path = "./ext/napi" }
|
||||
deno_net = { version = "0.171.0", path = "./ext/net" }
|
||||
deno_node = { version = "0.116.0", path = "./ext/node" }
|
||||
deno_tls = { version = "0.166.0", path = "./ext/tls" }
|
||||
deno_url = { version = "0.179.0", path = "./ext/url" }
|
||||
deno_web = { version = "0.210.0", path = "./ext/web" }
|
||||
deno_webgpu = { version = "0.146.0", path = "./ext/webgpu" }
|
||||
deno_webidl = { version = "0.179.0", path = "./ext/webidl" }
|
||||
deno_websocket = { version = "0.184.0", path = "./ext/websocket" }
|
||||
deno_webstorage = { version = "0.174.0", path = "./ext/webstorage" }
|
||||
deno_broadcast_channel = { version = "0.174.0", path = "./ext/broadcast_channel" }
|
||||
deno_cache = { version = "0.112.0", path = "./ext/cache" }
|
||||
deno_canvas = { version = "0.49.0", path = "./ext/canvas" }
|
||||
deno_console = { version = "0.180.0", path = "./ext/console" }
|
||||
deno_cron = { version = "0.60.0", path = "./ext/cron" }
|
||||
deno_crypto = { version = "0.194.0", path = "./ext/crypto" }
|
||||
deno_fetch = { version = "0.204.0", path = "./ext/fetch" }
|
||||
deno_ffi = { version = "0.167.0", path = "./ext/ffi" }
|
||||
deno_fs = { version = "0.90.0", path = "./ext/fs" }
|
||||
deno_http = { version = "0.178.0", path = "./ext/http" }
|
||||
deno_io = { version = "0.90.0", path = "./ext/io" }
|
||||
deno_kv = { version = "0.88.0", path = "./ext/kv" }
|
||||
deno_napi = { version = "0.111.0", path = "./ext/napi" }
|
||||
deno_net = { version = "0.172.0", path = "./ext/net" }
|
||||
deno_node = { version = "0.117.0", path = "./ext/node" }
|
||||
deno_telemetry = { version = "0.2.0", path = "./ext/telemetry" }
|
||||
deno_tls = { version = "0.167.0", path = "./ext/tls" }
|
||||
deno_url = { version = "0.180.0", path = "./ext/url" }
|
||||
deno_web = { version = "0.211.0", path = "./ext/web" }
|
||||
deno_webgpu = { version = "0.147.0", path = "./ext/webgpu" }
|
||||
deno_webidl = { version = "0.180.0", path = "./ext/webidl" }
|
||||
deno_websocket = { version = "0.185.0", path = "./ext/websocket" }
|
||||
deno_webstorage = { version = "0.175.0", path = "./ext/webstorage" }
|
||||
|
||||
# resolvers
|
||||
deno_resolver = { version = "0.11.0", path = "./resolvers/deno" }
|
||||
node_resolver = { version = "0.18.0", path = "./resolvers/node" }
|
||||
deno_npm_cache = { version = "0.0.1", path = "./resolvers/npm_cache" }
|
||||
deno_resolver = { version = "0.12.0", path = "./resolvers/deno" }
|
||||
node_resolver = { version = "0.19.0", path = "./resolvers/node" }
|
||||
|
||||
aes = "=0.8.3"
|
||||
anyhow = "1.0.57"
|
||||
|
@ -113,8 +117,9 @@ console_static_text = "=0.8.1"
|
|||
dashmap = "5.5.3"
|
||||
data-encoding = "2.3.3"
|
||||
data-url = "=0.3.0"
|
||||
deno_cache_dir = "=0.13.2"
|
||||
deno_package_json = { version = "0.1.2", default-features = false }
|
||||
deno_cache_dir = "=0.14.0"
|
||||
deno_package_json = { version = "0.2.1", default-features = false }
|
||||
deno_unsync = "0.4.2"
|
||||
dlopen2 = "0.6.1"
|
||||
ecb = "=0.1.2"
|
||||
elliptic-curve = { version = "0.13.4", features = ["alloc", "arithmetic", "ecdh", "std", "pem", "jwk"] }
|
||||
|
@ -128,7 +133,7 @@ fs3 = "0.5.0"
|
|||
futures = "0.3.21"
|
||||
glob = "0.3.1"
|
||||
h2 = "0.4.4"
|
||||
hickory-resolver = { version = "0.24", features = ["tokio-runtime", "serde-config"] }
|
||||
hickory-resolver = { version = "0.25.0-alpha.4", features = ["tokio-runtime", "serde"] }
|
||||
http = "1.0"
|
||||
http-body = "1.0"
|
||||
http-body-util = "0.1.2"
|
||||
|
@ -189,7 +194,7 @@ spki = "0.7.2"
|
|||
tar = "=0.4.40"
|
||||
tempfile = "3.4.0"
|
||||
termcolor = "1.1.3"
|
||||
thiserror = "1.0.61"
|
||||
thiserror = "2.0.3"
|
||||
tokio = { version = "1.36.0", features = ["full"] }
|
||||
tokio-metrics = { version = "0.3.0", features = ["rt"] }
|
||||
tokio-rustls = { version = "0.26.0", default-features = false, features = ["ring", "tls12"] }
|
||||
|
@ -234,7 +239,7 @@ nix = "=0.27.1"
|
|||
# windows deps
|
||||
junction = "=0.2.0"
|
||||
winapi = "=0.3.9"
|
||||
windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_Media", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_WindowsProgramming", "Wdk", "Wdk_System", "Wdk_System_SystemInformation", "Win32_Security", "Win32_System_Pipes", "Wdk_Storage_FileSystem", "Win32_System_Registry", "Win32_System_Kernel"] }
|
||||
windows-sys = { version = "0.59.0", features = ["Win32_Foundation", "Win32_Media", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_WindowsProgramming", "Wdk", "Wdk_System", "Wdk_System_SystemInformation", "Win32_Security", "Win32_System_Pipes", "Wdk_Storage_FileSystem", "Win32_System_Registry", "Win32_System_Kernel", "Win32_System_Threading", "Win32_UI", "Win32_UI_Shell"] }
|
||||
winres = "=0.1.12"
|
||||
|
||||
[profile.release]
|
||||
|
|
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
|
||||
|
||||
### 2.1.2 / 2024.11.28
|
||||
|
||||
- feat(unstable): Instrument Deno.serve (#26964)
|
||||
- feat(unstable): Instrument fetch (#27057)
|
||||
- feat(unstable): repurpose `--unstable-detect-cjs` to attempt loading more
|
||||
modules as cjs (#27094)
|
||||
- fix(check): support jsdoc `@import` tag (#26991)
|
||||
- fix(compile): correct buffered reading of assets and files (#27008)
|
||||
- fix(compile): do not error embedding same symlink via multiple methods
|
||||
(#27015)
|
||||
- fix(compile): handle TypeScript file included as asset (#27032)
|
||||
- fix(ext/fetch): don't throw when `bodyUsed` inspect after upgrade (#27088)
|
||||
- fix(ext/node): `tls.connect` socket upgrades (#27125)
|
||||
- fix(ext/node): add `fs.promises.fstat` and `FileHandle#stat` (#26719)
|
||||
- fix(ext/webgpu): normalize limits to number (#27072)
|
||||
- fix(ext/webgpu): use correct variable name (#27108)
|
||||
- fix(ext/websocket): don't throw exception when sending to closed socket
|
||||
(#26932)
|
||||
- fix(fmt): return `None` if sql fmt result is the same (#27014)
|
||||
- fix(info): resolve bare specifier pointing to workspace member (#27020)
|
||||
- fix(init): always force managed node modules (#27047)
|
||||
- fix(init): support scoped npm packages (#27128)
|
||||
- fix(install): don't re-set up node_modules if running lifecycle script
|
||||
(#26984)
|
||||
- fix(lsp): remove stray debug output (#27010)
|
||||
- fix(lsp): support task object notation for tasks request (#27076)
|
||||
- fix(lsp): wasm file import completions (#27018)
|
||||
- fix(node): correct resolution of dynamic import of esm from cjs (#27071)
|
||||
- fix(node/fs): add missing stat path argument validation (#27086)
|
||||
- fix(node/fs): missing uv error context for readFile (#27011)
|
||||
- fix(node/http): casing ignored in ServerResponse.hasHeader() (#27105)
|
||||
- fix(node/timers): error when passing id to clearTimeout/clearInterval (#27130)
|
||||
- fix(runtime/ops): Fix watchfs remove event (#27041)
|
||||
- fix(streams): reject `string` in `ReadableStream.from` type (#25116)
|
||||
- fix(task): handle carriage return in task description (#27099)
|
||||
- fix(task): handle multiline descriptions properly (#27069)
|
||||
- fix(task): strip ansi codes and control chars when printing tasks (#27100)
|
||||
- fix(tools/doc): HTML resolve main entrypoint from config file (#27103)
|
||||
- fix: support bun specifiers in JSR publish (#24588)
|
||||
- fix: support non-function exports in Wasm modules (#26992)
|
||||
- perf(compile): read embedded files as static references when UTF-8 and reading
|
||||
as strings (#27033)
|
||||
- perf(ext/webstorage): use object wrap for `Storage` (#26931)
|
||||
|
||||
### 2.1.1 / 2024.11.21
|
||||
|
||||
- docs(add): clarification to add command (#26968)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_bench_util"
|
||||
version = "0.173.0"
|
||||
version = "0.174.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno"
|
||||
version = "2.1.1"
|
||||
version = "2.1.2"
|
||||
authors.workspace = true
|
||||
default-run = "deno"
|
||||
edition.workspace = true
|
||||
|
@ -72,17 +72,19 @@ deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposa
|
|||
deno_cache_dir.workspace = true
|
||||
deno_config.workspace = true
|
||||
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
|
||||
deno_doc = { version = "0.160.0", features = ["rust", "comrak"] }
|
||||
deno_graph = { version = "=0.85.0" }
|
||||
deno_lint = { version = "=0.68.0", features = ["docs"] }
|
||||
deno_doc = { version = "=0.161.2", features = ["rust", "comrak"] }
|
||||
deno_graph = { version = "=0.86.3" }
|
||||
deno_lint = { version = "=0.68.2", features = ["docs"] }
|
||||
deno_lockfile.workspace = true
|
||||
deno_npm.workspace = true
|
||||
deno_npm_cache.workspace = true
|
||||
deno_package_json.workspace = true
|
||||
deno_path_util.workspace = true
|
||||
deno_resolver.workspace = true
|
||||
deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] }
|
||||
deno_semver.workspace = true
|
||||
deno_task_shell = "=0.18.1"
|
||||
deno_task_shell = "=0.20.2"
|
||||
deno_telemetry.workspace = true
|
||||
deno_terminal.workspace = true
|
||||
libsui = "0.5.0"
|
||||
node_resolver.workspace = true
|
||||
|
@ -107,7 +109,7 @@ dotenvy = "0.15.7"
|
|||
dprint-plugin-json = "=0.19.4"
|
||||
dprint-plugin-jupyter = "=0.1.5"
|
||||
dprint-plugin-markdown = "=0.17.8"
|
||||
dprint-plugin-typescript = "=0.93.2"
|
||||
dprint-plugin-typescript = "=0.93.3"
|
||||
env_logger = "=0.10.0"
|
||||
fancy-regex = "=0.10.0"
|
||||
faster-hex.workspace = true
|
||||
|
@ -129,7 +131,7 @@ libz-sys.workspace = true
|
|||
log = { workspace = true, features = ["serde"] }
|
||||
lsp-types.workspace = true
|
||||
malva = "=0.11.0"
|
||||
markup_fmt = "=0.16.0"
|
||||
markup_fmt = "=0.18.0"
|
||||
memmem.workspace = true
|
||||
monch.workspace = true
|
||||
notify.workspace = true
|
||||
|
@ -151,8 +153,7 @@ serde_repr.workspace = true
|
|||
sha2.workspace = true
|
||||
shell-escape = "=0.1.5"
|
||||
spki = { version = "0.7", features = ["pem"] }
|
||||
# NOTE(bartlomieju): using temporary fork for now, revert back to `sqlformat-rs` later
|
||||
sqlformat = { package = "deno_sqlformat", version = "0.3.2" }
|
||||
sqlformat = "=0.3.2"
|
||||
strsim = "0.11.1"
|
||||
tar.workspace = true
|
||||
tempfile.workspace = true
|
||||
|
|
|
@ -18,7 +18,7 @@ impl<'a> deno_config::fs::DenoConfigFs for DenoConfigFsAdapter<'a> {
|
|||
fn read_to_string_lossy(
|
||||
&self,
|
||||
path: &std::path::Path,
|
||||
) -> Result<String, std::io::Error> {
|
||||
) -> Result<std::borrow::Cow<'static, str>, std::io::Error> {
|
||||
self
|
||||
.0
|
||||
.read_text_file_lossy_sync(path, None)
|
||||
|
|
|
@ -36,7 +36,7 @@ use deno_path_util::normalize_path;
|
|||
use deno_path_util::url_to_file_path;
|
||||
use deno_runtime::deno_permissions::PermissionsOptions;
|
||||
use deno_runtime::deno_permissions::SysDescriptor;
|
||||
use deno_runtime::ops::otel::OtelConfig;
|
||||
use deno_telemetry::OtelConfig;
|
||||
use log::debug;
|
||||
use log::Level;
|
||||
use serde::Deserialize;
|
||||
|
@ -598,6 +598,7 @@ pub struct UnstableConfig {
|
|||
// TODO(bartlomieju): remove in Deno 2.5
|
||||
pub legacy_flag_enabled: bool, // --unstable
|
||||
pub bare_node_builtins: bool,
|
||||
pub detect_cjs: bool,
|
||||
pub sloppy_imports: bool,
|
||||
pub features: Vec<String>, // --unstabe-kv --unstable-cron
|
||||
}
|
||||
|
@ -2662,11 +2663,11 @@ By default, outdated dependencies are only displayed.
|
|||
Display outdated dependencies:
|
||||
<p(245)>deno outdated</>
|
||||
<p(245)>deno outdated --compatible</>
|
||||
|
||||
Update dependencies:
|
||||
|
||||
Update dependencies to latest semver compatible versions:
|
||||
<p(245)>deno outdated --update</>
|
||||
Update dependencies to latest versions, ignoring semver requirements:
|
||||
<p(245)>deno outdated --update --latest</>
|
||||
<p(245)>deno outdated --update</>
|
||||
|
||||
Filters can be used to select which packages to act on. Filters can include wildcards (*) to match multiple packages.
|
||||
<p(245)>deno outdated --update --latest \"@std/*\"</>
|
||||
|
@ -2702,7 +2703,6 @@ Specific version requirements to update to can be specified:
|
|||
.help(
|
||||
"Update to the latest version, regardless of semver constraints",
|
||||
)
|
||||
.requires("update")
|
||||
.conflicts_with("compatible"),
|
||||
)
|
||||
.arg(
|
||||
|
@ -3047,7 +3047,7 @@ fn task_subcommand() -> Command {
|
|||
|
||||
List all available tasks:
|
||||
<p(245)>deno task</>
|
||||
|
||||
|
||||
Evaluate a task from string
|
||||
<p(245)>deno task --eval \"echo $(pwd)\"</>"
|
||||
),
|
||||
|
@ -4373,7 +4373,7 @@ impl CommandExt for Command {
|
|||
).arg(
|
||||
Arg::new("unstable-detect-cjs")
|
||||
.long("unstable-detect-cjs")
|
||||
.help("Reads the package.json type field in a project to treat .js files as .cjs")
|
||||
.help("Treats ambiguous .js, .jsx, .ts, .tsx files as CommonJS modules in more cases")
|
||||
.value_parser(FalseyValueParser::new())
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(true)
|
||||
|
@ -5277,8 +5277,15 @@ fn task_parse(
|
|||
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime);
|
||||
node_modules_arg_parse(flags, matches);
|
||||
|
||||
let filter = matches.remove_one::<String>("filter");
|
||||
let recursive = matches.get_flag("recursive") || filter.is_some();
|
||||
let mut recursive = matches.get_flag("recursive");
|
||||
let filter = if let Some(filter) = matches.remove_one::<String>("filter") {
|
||||
recursive = false;
|
||||
Some(filter)
|
||||
} else if recursive {
|
||||
Some("*".to_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut task_flags = TaskFlags {
|
||||
cwd: matches.remove_one::<String>("cwd"),
|
||||
|
@ -5986,6 +5993,7 @@ fn unstable_args_parse(
|
|||
|
||||
flags.unstable_config.bare_node_builtins =
|
||||
matches.get_flag("unstable-bare-node-builtins");
|
||||
flags.unstable_config.detect_cjs = matches.get_flag("unstable-detect-cjs");
|
||||
flags.unstable_config.sloppy_imports =
|
||||
matches.get_flag("unstable-sloppy-imports");
|
||||
|
||||
|
@ -10537,7 +10545,7 @@ mod tests {
|
|||
cwd: None,
|
||||
task: Some("build".to_string()),
|
||||
is_run: false,
|
||||
recursive: true,
|
||||
recursive: false,
|
||||
filter: Some("*".to_string()),
|
||||
eval: false,
|
||||
}),
|
||||
|
@ -10554,7 +10562,7 @@ mod tests {
|
|||
task: Some("build".to_string()),
|
||||
is_run: false,
|
||||
recursive: true,
|
||||
filter: None,
|
||||
filter: Some("*".to_string()),
|
||||
eval: false,
|
||||
}),
|
||||
..Flags::default()
|
||||
|
@ -10570,7 +10578,7 @@ mod tests {
|
|||
task: Some("build".to_string()),
|
||||
is_run: false,
|
||||
recursive: true,
|
||||
filter: None,
|
||||
filter: Some("*".to_string()),
|
||||
eval: false,
|
||||
}),
|
||||
..Flags::default()
|
||||
|
@ -11685,6 +11693,14 @@ Usage: deno repl [OPTIONS] [-- [ARGS]...]\n"
|
|||
recursive: false,
|
||||
},
|
||||
),
|
||||
(
|
||||
svec!["--latest"],
|
||||
OutdatedFlags {
|
||||
filters: svec![],
|
||||
kind: OutdatedKind::PrintOutdated { compatible: false },
|
||||
recursive: false,
|
||||
},
|
||||
),
|
||||
];
|
||||
for (input, expected) in cases {
|
||||
let mut args = svec!["deno", "outdated"];
|
||||
|
|
|
@ -109,9 +109,12 @@ impl CliLockfile {
|
|||
let Some(pkg_json) = maybe_pkg_json else {
|
||||
return Default::default();
|
||||
};
|
||||
pkg_json
|
||||
.resolve_local_package_json_deps()
|
||||
let deps = pkg_json.resolve_local_package_json_deps();
|
||||
|
||||
deps
|
||||
.dependencies
|
||||
.values()
|
||||
.chain(deps.dev_dependencies.values())
|
||||
.filter_map(|dep| dep.as_ref().ok())
|
||||
.filter_map(|dep| match dep {
|
||||
PackageJsonDepValue::Req(req) => {
|
||||
|
|
|
@ -27,9 +27,10 @@ use deno_npm::npm_rc::NpmRc;
|
|||
use deno_npm::npm_rc::ResolvedNpmRc;
|
||||
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_npm_cache::NpmCacheSetting;
|
||||
use deno_path_util::normalize_path;
|
||||
use deno_runtime::ops::otel::OtelConfig;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use deno_telemetry::OtelConfig;
|
||||
use import_map::resolve_import_map_value_from_specifier;
|
||||
|
||||
pub use deno_config::deno_json::BenchConfig;
|
||||
|
@ -238,20 +239,25 @@ pub enum CacheSetting {
|
|||
}
|
||||
|
||||
impl CacheSetting {
|
||||
pub fn should_use_for_npm_package(&self, package_name: &str) -> bool {
|
||||
pub fn as_npm_cache_setting(&self) -> NpmCacheSetting {
|
||||
match self {
|
||||
CacheSetting::ReloadAll => false,
|
||||
CacheSetting::ReloadSome(list) => {
|
||||
if list.iter().any(|i| i == "npm:") {
|
||||
return false;
|
||||
CacheSetting::Only => NpmCacheSetting::Only,
|
||||
CacheSetting::ReloadAll => NpmCacheSetting::ReloadAll,
|
||||
CacheSetting::ReloadSome(values) => {
|
||||
if values.iter().any(|v| v == "npm:") {
|
||||
NpmCacheSetting::ReloadAll
|
||||
} else {
|
||||
NpmCacheSetting::ReloadSome {
|
||||
npm_package_names: values
|
||||
.iter()
|
||||
.filter_map(|v| v.strip_prefix("npm:"))
|
||||
.map(|n| n.to_string())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
let specifier = format!("npm:{package_name}");
|
||||
if list.contains(&specifier) {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
_ => true,
|
||||
CacheSetting::RespectHeaders => unreachable!(), // not supported
|
||||
CacheSetting::Use => NpmCacheSetting::Use,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1606,6 +1612,11 @@ impl CliOptions {
|
|||
|| self.workspace().has_unstable("bare-node-builtins")
|
||||
}
|
||||
|
||||
pub fn unstable_detect_cjs(&self) -> bool {
|
||||
self.flags.unstable_config.detect_cjs
|
||||
|| self.workspace().has_unstable("detect-cjs")
|
||||
}
|
||||
|
||||
pub fn detect_cjs(&self) -> bool {
|
||||
// only enabled when there's a package.json in order to not have a
|
||||
// perf penalty for non-npm Deno projects of searching for the closest
|
||||
|
@ -1675,6 +1686,7 @@ impl CliOptions {
|
|||
"sloppy-imports",
|
||||
"byonm",
|
||||
"bare-node-builtins",
|
||||
"detect-cjs",
|
||||
"fmt-component",
|
||||
"fmt-sql",
|
||||
])
|
||||
|
|
|
@ -8,8 +8,10 @@ use deno_core::serde_json;
|
|||
use deno_core::url::Url;
|
||||
use deno_package_json::PackageJsonDepValue;
|
||||
use deno_package_json::PackageJsonDepValueParseError;
|
||||
use deno_package_json::PackageJsonDepWorkspaceReq;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use deno_semver::package::PackageReq;
|
||||
use deno_semver::VersionReq;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -95,8 +97,14 @@ impl NpmInstallDepsProvider {
|
|||
|
||||
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 mut pkg_pkgs = Vec::with_capacity(
|
||||
deps.dependencies.len() + deps.dev_dependencies.len(),
|
||||
);
|
||||
for (alias, dep) in deps
|
||||
.dependencies
|
||||
.into_iter()
|
||||
.chain(deps.dev_dependencies.into_iter())
|
||||
{
|
||||
let dep = match dep {
|
||||
Ok(dep) => dep,
|
||||
Err(err) => {
|
||||
|
@ -131,7 +139,16 @@ impl NpmInstallDepsProvider {
|
|||
});
|
||||
}
|
||||
}
|
||||
PackageJsonDepValue::Workspace(version_req) => {
|
||||
PackageJsonDepValue::Workspace(workspace_version_req) => {
|
||||
let version_req = match workspace_version_req {
|
||||
PackageJsonDepWorkspaceReq::VersionReq(version_req) => {
|
||||
version_req
|
||||
}
|
||||
PackageJsonDepWorkspaceReq::Tilde
|
||||
| PackageJsonDepWorkspaceReq::Caret => {
|
||||
VersionReq::parse_from_npm("*").unwrap()
|
||||
}
|
||||
};
|
||||
if let Some(pkg) = workspace_npm_pkgs.iter().find(|pkg| {
|
||||
pkg.matches_name_and_version_req(&alias, &version_req)
|
||||
}) {
|
||||
|
|
13
cli/cache/mod.rs
vendored
13
cli/cache/mod.rs
vendored
|
@ -23,6 +23,7 @@ use deno_graph::source::Loader;
|
|||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
@ -67,8 +68,11 @@ pub const CACHE_PERM: u32 = 0o644;
|
|||
pub struct RealDenoCacheEnv;
|
||||
|
||||
impl deno_cache_dir::DenoCacheEnv for RealDenoCacheEnv {
|
||||
fn read_file_bytes(&self, path: &Path) -> std::io::Result<Vec<u8>> {
|
||||
std::fs::read(path)
|
||||
fn read_file_bytes(
|
||||
&self,
|
||||
path: &Path,
|
||||
) -> std::io::Result<Cow<'static, [u8]>> {
|
||||
std::fs::read(path).map(Cow::Owned)
|
||||
}
|
||||
|
||||
fn atomic_write_file(
|
||||
|
@ -112,7 +116,10 @@ pub struct DenoCacheEnvFsAdapter<'a>(
|
|||
);
|
||||
|
||||
impl<'a> deno_cache_dir::DenoCacheEnv for DenoCacheEnvFsAdapter<'a> {
|
||||
fn read_file_bytes(&self, path: &Path) -> std::io::Result<Vec<u8>> {
|
||||
fn read_file_bytes(
|
||||
&self,
|
||||
path: &Path,
|
||||
) -> std::io::Result<Cow<'static, [u8]>> {
|
||||
self
|
||||
.0
|
||||
.read_file_sync(path, None)
|
||||
|
|
24
cli/cache/module_info.rs
vendored
24
cli/cache/module_info.rs
vendored
|
@ -284,6 +284,7 @@ fn serialize_media_type(media_type: MediaType) -> i64 {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use deno_graph::JsDocImportInfo;
|
||||
use deno_graph::PositionRange;
|
||||
use deno_graph::SpecifierWithRange;
|
||||
|
||||
|
@ -308,18 +309,21 @@ mod test {
|
|||
);
|
||||
|
||||
let mut module_info = ModuleInfo::default();
|
||||
module_info.jsdoc_imports.push(SpecifierWithRange {
|
||||
range: PositionRange {
|
||||
start: deno_graph::Position {
|
||||
line: 0,
|
||||
character: 3,
|
||||
},
|
||||
end: deno_graph::Position {
|
||||
line: 1,
|
||||
character: 2,
|
||||
module_info.jsdoc_imports.push(JsDocImportInfo {
|
||||
specifier: SpecifierWithRange {
|
||||
range: PositionRange {
|
||||
start: deno_graph::Position {
|
||||
line: 0,
|
||||
character: 3,
|
||||
},
|
||||
end: deno_graph::Position {
|
||||
line: 1,
|
||||
character: 2,
|
||||
},
|
||||
},
|
||||
text: "test".to_string(),
|
||||
},
|
||||
text: "test".to_string(),
|
||||
resolution_mode: None,
|
||||
});
|
||||
cache
|
||||
.set_module_info(
|
||||
|
|
10
cli/cache/parsed_source.rs
vendored
10
cli/cache/parsed_source.rs
vendored
|
@ -95,11 +95,21 @@ impl ParsedSourceCache {
|
|||
self.sources.lock().remove(specifier);
|
||||
}
|
||||
|
||||
/// Fress all parsed sources from memory.
|
||||
pub fn free_all(&self) {
|
||||
self.sources.lock().clear();
|
||||
}
|
||||
|
||||
/// Creates a parser that will reuse a ParsedSource from the store
|
||||
/// if it exists, or else parse.
|
||||
pub fn as_capturing_parser(&self) -> CapturingEsParser {
|
||||
CapturingEsParser::new(None, self)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn len(&self) -> usize {
|
||||
self.sources.lock().len()
|
||||
}
|
||||
}
|
||||
|
||||
/// It's ok that this is racy since in non-LSP situations
|
||||
|
|
|
@ -48,7 +48,6 @@ use crate::resolver::CliNpmReqResolver;
|
|||
use crate::resolver::CliResolver;
|
||||
use crate::resolver::CliResolverOptions;
|
||||
use crate::resolver::CliSloppyImportsResolver;
|
||||
use crate::resolver::IsCjsResolverOptions;
|
||||
use crate::resolver::NpmModuleLoader;
|
||||
use crate::resolver::SloppyImportsCachedFs;
|
||||
use crate::standalone::DenoCompileBinaryWriter;
|
||||
|
@ -72,6 +71,7 @@ use deno_core::error::AnyError;
|
|||
use deno_core::futures::FutureExt;
|
||||
use deno_core::FeatureChecker;
|
||||
|
||||
use deno_resolver::cjs::IsCjsResolutionMode;
|
||||
use deno_resolver::npm::NpmReqResolverOptions;
|
||||
use deno_resolver::DenoResolverOptions;
|
||||
use deno_resolver::NodeAndNpmReqResolver;
|
||||
|
@ -504,7 +504,12 @@ impl CliFactory {
|
|||
let resolver = cli_options
|
||||
.create_workspace_resolver(
|
||||
self.file_fetcher()?,
|
||||
if cli_options.use_byonm() {
|
||||
if cli_options.use_byonm()
|
||||
&& !matches!(
|
||||
cli_options.sub_command(),
|
||||
DenoSubcommand::Publish(_)
|
||||
)
|
||||
{
|
||||
PackageJsonDepResolution::Disabled
|
||||
} else {
|
||||
// todo(dsherret): this should be false for nodeModulesDir: true
|
||||
|
@ -845,9 +850,12 @@ impl CliFactory {
|
|||
Ok(Arc::new(CjsTracker::new(
|
||||
self.in_npm_pkg_checker()?.clone(),
|
||||
self.pkg_json_resolver().clone(),
|
||||
IsCjsResolverOptions {
|
||||
detect_cjs: options.detect_cjs(),
|
||||
is_node_main: options.is_node_main(),
|
||||
if options.is_node_main() || options.unstable_detect_cjs() {
|
||||
IsCjsResolutionMode::ImplicitTypeCommonJs
|
||||
} else if options.detect_cjs() {
|
||||
IsCjsResolutionMode::ExplicitTypeCommonJs
|
||||
} else {
|
||||
IsCjsResolutionMode::Disabled
|
||||
},
|
||||
)))
|
||||
})
|
||||
|
|
|
@ -59,8 +59,7 @@ impl FileOrRedirect {
|
|||
cache_entry: deno_cache_dir::CacheEntry,
|
||||
) -> Result<Self, AnyError> {
|
||||
if let Some(redirect_to) = cache_entry.metadata.headers.get("location") {
|
||||
let redirect =
|
||||
deno_core::resolve_import(redirect_to, specifier.as_str())?;
|
||||
let redirect = specifier.join(redirect_to)?;
|
||||
Ok(FileOrRedirect::Redirect(redirect))
|
||||
} else {
|
||||
Ok(FileOrRedirect::File(File {
|
||||
|
@ -1540,7 +1539,7 @@ mod tests {
|
|||
.unwrap()
|
||||
.unwrap()
|
||||
.content;
|
||||
String::from_utf8(bytes).unwrap()
|
||||
String::from_utf8(bytes.into_owned()).unwrap()
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
|
|
@ -6,6 +6,7 @@ use crate::args::CliLockfile;
|
|||
use crate::args::CliOptions;
|
||||
use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
|
||||
use crate::cache;
|
||||
use crate::cache::FetchCacher;
|
||||
use crate::cache::GlobalHttpCache;
|
||||
use crate::cache::ModuleInfoCache;
|
||||
use crate::cache::ParsedSourceCache;
|
||||
|
@ -25,7 +26,7 @@ use deno_config::deno_json::JsxImportSourceConfig;
|
|||
use deno_config::workspace::JsrPackageConfig;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_graph::source::LoaderChecksum;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::source::ResolutionKind;
|
||||
use deno_graph::FillFromLockfileOptions;
|
||||
use deno_graph::JsrLoadError;
|
||||
use deno_graph::ModuleLoadError;
|
||||
|
@ -44,7 +45,7 @@ use deno_graph::ModuleGraphError;
|
|||
use deno_graph::ResolutionError;
|
||||
use deno_graph::SpecifierError;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolutionMode;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolutionKind;
|
||||
use deno_runtime::deno_fs::FileSystem;
|
||||
use deno_runtime::deno_node;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
|
@ -108,6 +109,25 @@ pub fn graph_valid(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn fill_graph_from_lockfile(
|
||||
graph: &mut ModuleGraph,
|
||||
lockfile: &deno_lockfile::Lockfile,
|
||||
) {
|
||||
graph.fill_from_lockfile(FillFromLockfileOptions {
|
||||
redirects: lockfile
|
||||
.content
|
||||
.redirects
|
||||
.iter()
|
||||
.map(|(from, to)| (from.as_str(), to.as_str())),
|
||||
package_specifiers: lockfile
|
||||
.content
|
||||
.packages
|
||||
.specifiers
|
||||
.iter()
|
||||
.map(|(dep, id)| (dep, id.as_str())),
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GraphWalkErrorsOptions {
|
||||
pub check_js: bool,
|
||||
|
@ -254,6 +274,23 @@ impl ModuleGraphCreator {
|
|||
package_configs: &[JsrPackageConfig],
|
||||
build_fast_check_graph: bool,
|
||||
) -> Result<ModuleGraph, AnyError> {
|
||||
struct PublishLoader(FetchCacher);
|
||||
impl Loader for PublishLoader {
|
||||
fn load(
|
||||
&self,
|
||||
specifier: &deno_ast::ModuleSpecifier,
|
||||
options: deno_graph::source::LoadOptions,
|
||||
) -> deno_graph::source::LoadFuture {
|
||||
if specifier.scheme() == "bun" {
|
||||
return Box::pin(std::future::ready(Ok(Some(
|
||||
deno_graph::source::LoadResponse::External {
|
||||
specifier: specifier.clone(),
|
||||
},
|
||||
))));
|
||||
}
|
||||
self.0.load(specifier, options)
|
||||
}
|
||||
}
|
||||
fn graph_has_external_remote(graph: &ModuleGraph) -> bool {
|
||||
// Earlier on, we marked external non-JSR modules as external.
|
||||
// If the graph contains any of those, it would cause type checking
|
||||
|
@ -271,12 +308,15 @@ impl ModuleGraphCreator {
|
|||
for package_config in package_configs {
|
||||
roots.extend(package_config.config_file.resolve_export_value_urls()?);
|
||||
}
|
||||
|
||||
let loader = self.module_graph_builder.create_graph_loader();
|
||||
let mut publish_loader = PublishLoader(loader);
|
||||
let mut graph = self
|
||||
.create_graph_with_options(CreateGraphOptions {
|
||||
is_dynamic: false,
|
||||
graph_kind: deno_graph::GraphKind::All,
|
||||
roots,
|
||||
loader: None,
|
||||
loader: Some(&mut publish_loader),
|
||||
})
|
||||
.await?;
|
||||
self.graph_valid(&graph)?;
|
||||
|
@ -582,19 +622,7 @@ impl ModuleGraphBuilder {
|
|||
// populate the information from the lockfile
|
||||
if let Some(lockfile) = &self.lockfile {
|
||||
let lockfile = lockfile.lock();
|
||||
graph.fill_from_lockfile(FillFromLockfileOptions {
|
||||
redirects: lockfile
|
||||
.content
|
||||
.redirects
|
||||
.iter()
|
||||
.map(|(from, to)| (from.as_str(), to.as_str())),
|
||||
package_specifiers: lockfile
|
||||
.content
|
||||
.packages
|
||||
.specifiers
|
||||
.iter()
|
||||
.map(|(dep, id)| (dep, id.as_str())),
|
||||
});
|
||||
fill_graph_from_lockfile(graph, &lockfile);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -795,7 +823,7 @@ fn enhanced_sloppy_imports_error_message(
|
|||
ModuleError::LoadingErr(specifier, _, ModuleLoadError::Loader(_)) // ex. "Is a directory" error
|
||||
| ModuleError::Missing(specifier, _) => {
|
||||
let additional_message = CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(fs.clone()))
|
||||
.resolve(specifier, SloppyImportsResolutionMode::Execution)?
|
||||
.resolve(specifier, SloppyImportsResolutionKind::Execution)?
|
||||
.as_suggestion_message();
|
||||
Some(format!(
|
||||
"{} {} or run with --unstable-sloppy-imports",
|
||||
|
@ -1100,12 +1128,12 @@ impl<'a> deno_graph::source::FileSystem for DenoGraphFsAdapter<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn format_range_with_colors(range: &deno_graph::Range) -> String {
|
||||
pub fn format_range_with_colors(referrer: &deno_graph::Range) -> String {
|
||||
format!(
|
||||
"{}:{}:{}",
|
||||
colors::cyan(range.specifier.as_str()),
|
||||
colors::yellow(&(range.start.line + 1).to_string()),
|
||||
colors::yellow(&(range.start.character + 1).to_string())
|
||||
colors::cyan(referrer.specifier.as_str()),
|
||||
colors::yellow(&(referrer.range.start.line + 1).to_string()),
|
||||
colors::yellow(&(referrer.range.start.character + 1).to_string())
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1195,26 +1223,54 @@ impl<'a> deno_graph::source::Resolver for CliGraphResolver<'a> {
|
|||
&self,
|
||||
raw_specifier: &str,
|
||||
referrer_range: &deno_graph::Range,
|
||||
mode: ResolutionMode,
|
||||
resolution_kind: ResolutionKind,
|
||||
) -> Result<ModuleSpecifier, ResolveError> {
|
||||
self.resolver.resolve(
|
||||
raw_specifier,
|
||||
referrer_range,
|
||||
self
|
||||
.cjs_tracker
|
||||
.get_referrer_kind(&referrer_range.specifier),
|
||||
mode,
|
||||
&referrer_range.specifier,
|
||||
referrer_range.range.start,
|
||||
referrer_range
|
||||
.resolution_mode
|
||||
.map(to_node_resolution_mode)
|
||||
.unwrap_or_else(|| {
|
||||
self
|
||||
.cjs_tracker
|
||||
.get_referrer_kind(&referrer_range.specifier)
|
||||
}),
|
||||
to_node_resolution_kind(resolution_kind),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_node_resolution_kind(
|
||||
kind: ResolutionKind,
|
||||
) -> node_resolver::NodeResolutionKind {
|
||||
match kind {
|
||||
ResolutionKind::Execution => node_resolver::NodeResolutionKind::Execution,
|
||||
ResolutionKind::Types => node_resolver::NodeResolutionKind::Types,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_node_resolution_mode(
|
||||
mode: deno_graph::source::ResolutionMode,
|
||||
) -> node_resolver::ResolutionMode {
|
||||
match mode {
|
||||
deno_graph::source::ResolutionMode::Import => {
|
||||
node_resolver::ResolutionMode::Import
|
||||
}
|
||||
deno_graph::source::ResolutionMode::Require => {
|
||||
node_resolver::ResolutionMode::Require
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_graph::source::ResolveError;
|
||||
use deno_graph::Position;
|
||||
use deno_graph::PositionRange;
|
||||
use deno_graph::Range;
|
||||
use deno_graph::ResolutionError;
|
||||
use deno_graph::SpecifierError;
|
||||
|
@ -1235,8 +1291,8 @@ mod test {
|
|||
specifier: input.to_string(),
|
||||
range: Range {
|
||||
specifier,
|
||||
start: Position::zeroed(),
|
||||
end: Position::zeroed(),
|
||||
resolution_mode: None,
|
||||
range: PositionRange::zeroed(),
|
||||
},
|
||||
};
|
||||
assert_eq!(get_resolution_error_bare_node_specifier(&err), output);
|
||||
|
@ -1251,8 +1307,8 @@ mod test {
|
|||
let err = ResolutionError::InvalidSpecifier {
|
||||
range: Range {
|
||||
specifier,
|
||||
start: Position::zeroed(),
|
||||
end: Position::zeroed(),
|
||||
resolution_mode: None,
|
||||
range: PositionRange::zeroed(),
|
||||
},
|
||||
error: SpecifierError::ImportPrefixMissing {
|
||||
specifier: input.to_string(),
|
||||
|
|
|
@ -15,7 +15,6 @@ use crate::lsp::search::PackageSearchApi;
|
|||
use crate::tools::lint::CliLinter;
|
||||
use crate::util::path::relative_specifier;
|
||||
use deno_config::workspace::MappedResolution;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_lint::diagnostic::LintDiagnosticRange;
|
||||
|
||||
use deno_ast::SourceRange;
|
||||
|
@ -39,7 +38,8 @@ use deno_semver::package::PackageReq;
|
|||
use deno_semver::package::PackageReqReference;
|
||||
use deno_semver::Version;
|
||||
use import_map::ImportMap;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use node_resolver::NodeResolutionKind;
|
||||
use node_resolver::ResolutionMode;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use std::borrow::Cow;
|
||||
|
@ -353,7 +353,12 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
let pkg_reqs = npm_resolver.resolve_pkg_reqs_from_pkg_id(&pkg_id);
|
||||
// check if any pkg reqs match what is found in an import map
|
||||
if !pkg_reqs.is_empty() {
|
||||
let sub_path = self.resolve_package_path(specifier);
|
||||
let sub_path = npm_resolver
|
||||
.resolve_pkg_folder_from_pkg_id(&pkg_id)
|
||||
.ok()
|
||||
.and_then(|pkg_folder| {
|
||||
self.resolve_package_path(specifier, &pkg_folder)
|
||||
});
|
||||
if let Some(import_map) = self.maybe_import_map {
|
||||
let pkg_reqs = pkg_reqs.iter().collect::<HashSet<_>>();
|
||||
let mut matches = Vec::new();
|
||||
|
@ -368,8 +373,13 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
if let Some(key_sub_path) =
|
||||
sub_path.strip_prefix(value_sub_path)
|
||||
{
|
||||
matches
|
||||
.push(format!("{}{}", entry.raw_key, key_sub_path));
|
||||
// keys that don't end in a slash can't be mapped to a subpath
|
||||
if entry.raw_key.ends_with('/')
|
||||
|| key_sub_path.is_empty()
|
||||
{
|
||||
matches
|
||||
.push(format!("{}{}", entry.raw_key, key_sub_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -413,10 +423,16 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
fn resolve_package_path(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
package_root_folder: &Path,
|
||||
) -> Option<String> {
|
||||
let package_json = self
|
||||
.resolver
|
||||
.get_closest_package_json(specifier)
|
||||
.pkg_json_resolver(specifier)
|
||||
// the specifier might have a closer package.json, but we
|
||||
// want the root of the package's package.json
|
||||
.get_closest_package_json_from_file_path(
|
||||
&package_root_folder.join("package.json"),
|
||||
)
|
||||
.ok()
|
||||
.flatten()?;
|
||||
let root_folder = package_json.path.parent()?;
|
||||
|
@ -467,7 +483,7 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
&self,
|
||||
specifier: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
resolution_mode: ResolutionMode,
|
||||
) -> Option<String> {
|
||||
let specifier_stem = specifier.strip_suffix(".js").unwrap_or(specifier);
|
||||
let specifiers = std::iter::once(Cow::Borrowed(specifier)).chain(
|
||||
|
@ -481,13 +497,10 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
.as_cli_resolver(Some(&self.file_referrer))
|
||||
.resolve(
|
||||
&specifier,
|
||||
&deno_graph::Range {
|
||||
specifier: referrer.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
},
|
||||
referrer_kind,
|
||||
ResolutionMode::Types,
|
||||
referrer,
|
||||
deno_graph::Position::zeroed(),
|
||||
resolution_mode,
|
||||
NodeResolutionKind::Types,
|
||||
)
|
||||
.ok()
|
||||
.and_then(|s| self.tsc_specifier_map.normalize(s.as_str()).ok())
|
||||
|
@ -509,20 +522,17 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
&self,
|
||||
specifier_text: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
resolution_mode: ResolutionMode,
|
||||
) -> bool {
|
||||
self
|
||||
.resolver
|
||||
.as_cli_resolver(Some(&self.file_referrer))
|
||||
.resolve(
|
||||
specifier_text,
|
||||
&deno_graph::Range {
|
||||
specifier: referrer.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
},
|
||||
referrer_kind,
|
||||
deno_graph::source::ResolutionMode::Types,
|
||||
referrer,
|
||||
deno_graph::Position::zeroed(),
|
||||
resolution_mode,
|
||||
NodeResolutionKind::Types,
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
|
@ -590,7 +600,7 @@ fn try_reverse_map_package_json_exports(
|
|||
/// like an import and rewrite the import specifier to include the extension
|
||||
pub fn fix_ts_import_changes(
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
resolution_mode: ResolutionMode,
|
||||
changes: &[tsc::FileTextChanges],
|
||||
language_server: &language_server::Inner,
|
||||
) -> Result<Vec<tsc::FileTextChanges>, AnyError> {
|
||||
|
@ -608,7 +618,7 @@ pub fn fix_ts_import_changes(
|
|||
let specifier =
|
||||
captures.iter().skip(1).find_map(|s| s).unwrap().as_str();
|
||||
if let Some(new_specifier) = import_mapper
|
||||
.check_unresolved_specifier(specifier, referrer, referrer_kind)
|
||||
.check_unresolved_specifier(specifier, referrer, resolution_mode)
|
||||
{
|
||||
line.replace(specifier, &new_specifier)
|
||||
} else {
|
||||
|
@ -638,7 +648,7 @@ pub fn fix_ts_import_changes(
|
|||
/// resolution by Deno (includes the extension).
|
||||
fn fix_ts_import_action<'a>(
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
resolution_mode: ResolutionMode,
|
||||
action: &'a tsc::CodeFixAction,
|
||||
language_server: &language_server::Inner,
|
||||
) -> Option<Cow<'a, tsc::CodeFixAction>> {
|
||||
|
@ -657,9 +667,11 @@ fn fix_ts_import_action<'a>(
|
|||
return Some(Cow::Borrowed(action));
|
||||
};
|
||||
let import_mapper = language_server.get_ts_response_import_mapper(referrer);
|
||||
if let Some(new_specifier) =
|
||||
import_mapper.check_unresolved_specifier(specifier, referrer, referrer_kind)
|
||||
{
|
||||
if let Some(new_specifier) = import_mapper.check_unresolved_specifier(
|
||||
specifier,
|
||||
referrer,
|
||||
resolution_mode,
|
||||
) {
|
||||
let description = action.description.replace(specifier, &new_specifier);
|
||||
let changes = action
|
||||
.changes
|
||||
|
@ -689,7 +701,8 @@ fn fix_ts_import_action<'a>(
|
|||
fix_id: None,
|
||||
fix_all_description: None,
|
||||
}))
|
||||
} else if !import_mapper.is_valid_import(specifier, referrer, referrer_kind) {
|
||||
} else if !import_mapper.is_valid_import(specifier, referrer, resolution_mode)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(Cow::Borrowed(action))
|
||||
|
@ -1023,7 +1036,7 @@ impl CodeActionCollection {
|
|||
pub fn add_ts_fix_action(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
specifier_kind: NodeModuleKind,
|
||||
resolution_mode: ResolutionMode,
|
||||
action: &tsc::CodeFixAction,
|
||||
diagnostic: &lsp::Diagnostic,
|
||||
language_server: &language_server::Inner,
|
||||
|
@ -1042,7 +1055,7 @@ impl CodeActionCollection {
|
|||
));
|
||||
}
|
||||
let Some(action) =
|
||||
fix_ts_import_action(specifier, specifier_kind, action, language_server)
|
||||
fix_ts_import_action(specifier, resolution_mode, action, language_server)
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
|
@ -1237,12 +1250,12 @@ impl CodeActionCollection {
|
|||
let text_info = parsed_source.text_info_lazy();
|
||||
let specifier_range = SourceRange::new(
|
||||
text_info.loc_to_source_pos(LineAndColumnIndex {
|
||||
line_index: import.specifier_range.start.line,
|
||||
column_index: import.specifier_range.start.character,
|
||||
line_index: import.specifier_range.range.start.line,
|
||||
column_index: import.specifier_range.range.start.character,
|
||||
}),
|
||||
text_info.loc_to_source_pos(LineAndColumnIndex {
|
||||
line_index: import.specifier_range.end.line,
|
||||
column_index: import.specifier_range.end.character,
|
||||
line_index: import.specifier_range.range.end.line,
|
||||
column_index: import.specifier_range.range.end.character,
|
||||
}),
|
||||
);
|
||||
|
||||
|
@ -1277,16 +1290,14 @@ impl CodeActionCollection {
|
|||
if json!(i.kind) != json!("es") && json!(i.kind) != json!("tsType") {
|
||||
return None;
|
||||
}
|
||||
if !i.specifier_range.includes(&position) {
|
||||
if !i.specifier_range.includes(position) {
|
||||
return None;
|
||||
}
|
||||
|
||||
import_start_from_specifier(document, i)
|
||||
})?;
|
||||
let referrer = document.specifier();
|
||||
let referrer_kind = language_server
|
||||
.is_cjs_resolver
|
||||
.get_doc_module_kind(document);
|
||||
let resolution_mode = document.resolution_mode();
|
||||
let file_referrer = document.file_referrer();
|
||||
let config_data = language_server
|
||||
.config
|
||||
|
@ -1312,7 +1323,7 @@ impl CodeActionCollection {
|
|||
if !language_server.resolver.is_bare_package_json_dep(
|
||||
&dep_key,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
resolution_mode,
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
|
@ -1332,7 +1343,7 @@ impl CodeActionCollection {
|
|||
}
|
||||
if language_server
|
||||
.resolver
|
||||
.npm_to_file_url(&npm_ref, referrer, referrer_kind, file_referrer)
|
||||
.npm_to_file_url(&npm_ref, referrer, resolution_mode, file_referrer)
|
||||
.is_some()
|
||||
{
|
||||
// The package import has types.
|
||||
|
|
|
@ -9,16 +9,14 @@ use super::jsr::CliJsrSearchApi;
|
|||
use super::lsp_custom;
|
||||
use super::npm::CliNpmSearchApi;
|
||||
use super::registries::ModuleRegistry;
|
||||
use super::resolver::LspIsCjsResolver;
|
||||
use super::resolver::LspResolver;
|
||||
use super::search::PackageSearchApi;
|
||||
use super::tsc;
|
||||
|
||||
use crate::graph_util::to_node_resolution_mode;
|
||||
use crate::jsr::JsrFetchResolver;
|
||||
use crate::util::path::is_importable_ext;
|
||||
use crate::util::path::relative_specifier;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::Range;
|
||||
use deno_runtime::deno_node::SUPPORTED_BUILTIN_NODE_MODULES;
|
||||
|
||||
use deno_ast::LineAndColumnIndex;
|
||||
|
@ -36,7 +34,8 @@ use deno_semver::package::PackageNv;
|
|||
use import_map::ImportMap;
|
||||
use indexmap::IndexSet;
|
||||
use lsp_types::CompletionList;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use node_resolver::NodeResolutionKind;
|
||||
use node_resolver::ResolutionMode;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use tower_lsp::lsp_types as lsp;
|
||||
|
@ -113,7 +112,7 @@ async fn check_auto_config_registry(
|
|||
/// which we want to ignore when replacing text.
|
||||
fn to_narrow_lsp_range(
|
||||
text_info: &SourceTextInfo,
|
||||
range: &deno_graph::Range,
|
||||
range: deno_graph::PositionRange,
|
||||
) -> lsp::Range {
|
||||
let end_byte_index = text_info
|
||||
.loc_to_source_pos(LineAndColumnIndex {
|
||||
|
@ -161,26 +160,25 @@ pub async fn get_import_completions(
|
|||
jsr_search_api: &CliJsrSearchApi,
|
||||
npm_search_api: &CliNpmSearchApi,
|
||||
documents: &Documents,
|
||||
is_cjs_resolver: &LspIsCjsResolver,
|
||||
resolver: &LspResolver,
|
||||
maybe_import_map: Option<&ImportMap>,
|
||||
) -> Option<lsp::CompletionResponse> {
|
||||
let document = documents.get(specifier)?;
|
||||
let specifier_kind = is_cjs_resolver.get_doc_module_kind(&document);
|
||||
let file_referrer = document.file_referrer();
|
||||
let (text, _, range) = document.get_maybe_dependency(position)?;
|
||||
let range = to_narrow_lsp_range(document.text_info(), &range);
|
||||
let (text, _, graph_range) = document.get_maybe_dependency(position)?;
|
||||
let resolution_mode = graph_range
|
||||
.resolution_mode
|
||||
.map(to_node_resolution_mode)
|
||||
.unwrap_or_else(|| document.resolution_mode());
|
||||
let range = to_narrow_lsp_range(document.text_info(), graph_range.range);
|
||||
let resolved = resolver
|
||||
.as_cli_resolver(file_referrer)
|
||||
.resolve(
|
||||
&text,
|
||||
&Range {
|
||||
specifier: specifier.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
},
|
||||
specifier_kind,
|
||||
ResolutionMode::Execution,
|
||||
specifier,
|
||||
deno_graph::Position::zeroed(),
|
||||
resolution_mode,
|
||||
NodeResolutionKind::Execution,
|
||||
)
|
||||
.ok();
|
||||
if let Some(completion_list) = get_jsr_completions(
|
||||
|
@ -206,7 +204,7 @@ pub async fn get_import_completions(
|
|||
// completions for import map specifiers
|
||||
Some(lsp::CompletionResponse::List(completion_list))
|
||||
} else if let Some(completion_list) =
|
||||
get_local_completions(specifier, specifier_kind, &text, &range, resolver)
|
||||
get_local_completions(specifier, resolution_mode, &text, &range, resolver)
|
||||
{
|
||||
// completions for local relative modules
|
||||
Some(lsp::CompletionResponse::List(completion_list))
|
||||
|
@ -361,7 +359,7 @@ fn get_import_map_completions(
|
|||
/// Return local completions that are relative to the base specifier.
|
||||
fn get_local_completions(
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
resolution_mode: ResolutionMode,
|
||||
text: &str,
|
||||
range: &lsp::Range,
|
||||
resolver: &LspResolver,
|
||||
|
@ -374,13 +372,10 @@ fn get_local_completions(
|
|||
.as_cli_resolver(Some(referrer))
|
||||
.resolve(
|
||||
parent,
|
||||
&Range {
|
||||
specifier: referrer.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
},
|
||||
referrer_kind,
|
||||
ResolutionMode::Execution,
|
||||
referrer,
|
||||
deno_graph::Position::zeroed(),
|
||||
resolution_mode,
|
||||
NodeResolutionKind::Execution,
|
||||
)
|
||||
.ok()?;
|
||||
let resolved_parent_path = url_to_file_path(&resolved_parent).ok()?;
|
||||
|
@ -831,7 +826,6 @@ mod tests {
|
|||
use crate::lsp::documents::LanguageId;
|
||||
use crate::lsp::search::tests::TestPackageSearchApi;
|
||||
use deno_core::resolve_url;
|
||||
use deno_graph::Range;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::collections::HashMap;
|
||||
use test_util::TempDir;
|
||||
|
@ -912,7 +906,7 @@ mod tests {
|
|||
ModuleSpecifier::from_file_path(file_c).expect("could not create");
|
||||
let actual = get_local_completions(
|
||||
&specifier,
|
||||
NodeModuleKind::Esm,
|
||||
ResolutionMode::Import,
|
||||
"./",
|
||||
&lsp::Range {
|
||||
start: lsp::Position {
|
||||
|
@ -1608,8 +1602,7 @@ mod tests {
|
|||
let text_info = SourceTextInfo::from_string(r#""te""#.to_string());
|
||||
let range = to_narrow_lsp_range(
|
||||
&text_info,
|
||||
&Range {
|
||||
specifier: ModuleSpecifier::parse("https://deno.land").unwrap(),
|
||||
deno_graph::PositionRange {
|
||||
start: deno_graph::Position {
|
||||
line: 0,
|
||||
character: 0,
|
||||
|
@ -1632,8 +1625,7 @@ mod tests {
|
|||
let text_info = SourceTextInfo::from_string(r#""te"#.to_string());
|
||||
let range = to_narrow_lsp_range(
|
||||
&text_info,
|
||||
&Range {
|
||||
specifier: ModuleSpecifier::parse("https://deno.land").unwrap(),
|
||||
deno_graph::PositionRange {
|
||||
start: deno_graph::Position {
|
||||
line: 0,
|
||||
character: 0,
|
||||
|
|
|
@ -41,6 +41,7 @@ use deno_path_util::url_to_file_path;
|
|||
use deno_runtime::deno_node::PackageJson;
|
||||
use indexmap::IndexSet;
|
||||
use lsp_types::ClientCapabilities;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashMap;
|
||||
|
@ -2092,7 +2093,7 @@ impl<T: Clone> CachedFsItems<T> {
|
|||
#[derive(Default)]
|
||||
struct InnerData {
|
||||
stat_calls: CachedFsItems<deno_config::fs::FsMetadata>,
|
||||
read_to_string_calls: CachedFsItems<String>,
|
||||
read_to_string_calls: CachedFsItems<Cow<'static, str>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -2113,7 +2114,7 @@ impl DenoConfigFs for CachedDenoConfigFs {
|
|||
fn read_to_string_lossy(
|
||||
&self,
|
||||
path: &Path,
|
||||
) -> Result<String, std::io::Error> {
|
||||
) -> Result<Cow<'static, str>, std::io::Error> {
|
||||
self
|
||||
.0
|
||||
.lock()
|
||||
|
|
|
@ -45,7 +45,7 @@ use deno_graph::Resolution;
|
|||
use deno_graph::ResolutionError;
|
||||
use deno_graph::SpecifierError;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolution;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolutionMode;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolutionKind;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node;
|
||||
use deno_runtime::tokio_util::create_basic_runtime;
|
||||
|
@ -1262,11 +1262,11 @@ impl DenoDiagnostic {
|
|||
Self::NoAttributeType => (lsp::DiagnosticSeverity::ERROR, "The module is a JSON module and not being imported with an import attribute. Consider adding `with { type: \"json\" }` to the import statement.".to_string(), None),
|
||||
Self::NoCache(specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Uncached or missing remote URL: {specifier}"), Some(json!({ "specifier": specifier }))),
|
||||
Self::NotInstalledJsr(pkg_req, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("JSR package \"{pkg_req}\" is not installed or doesn't exist."), Some(json!({ "specifier": specifier }))),
|
||||
Self::NotInstalledNpm(pkg_req, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("NPM package \"{pkg_req}\" is not installed or doesn't exist."), Some(json!({ "specifier": specifier }))),
|
||||
Self::NotInstalledNpm(pkg_req, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("npm package \"{pkg_req}\" is not installed or doesn't exist."), Some(json!({ "specifier": specifier }))),
|
||||
Self::NoLocal(specifier) => {
|
||||
let maybe_sloppy_resolution = CliSloppyImportsResolver::new(
|
||||
SloppyImportsCachedFs::new(Arc::new(deno_fs::RealFs))
|
||||
).resolve(specifier, SloppyImportsResolutionMode::Execution);
|
||||
).resolve(specifier, SloppyImportsResolutionKind::Execution);
|
||||
let data = maybe_sloppy_resolution.as_ref().map(|res| {
|
||||
json!({
|
||||
"specifier": specifier,
|
||||
|
@ -1531,7 +1531,7 @@ fn diagnose_dependency(
|
|||
&& !dependency.imports.iter().any(|i| {
|
||||
dependency
|
||||
.maybe_type
|
||||
.includes(&i.specifier_range.start)
|
||||
.includes(i.specifier_range.range.start)
|
||||
.is_some()
|
||||
});
|
||||
|
||||
|
@ -1707,7 +1707,6 @@ mod tests {
|
|||
documents: Arc::new(documents),
|
||||
assets: Default::default(),
|
||||
config: Arc::new(config),
|
||||
is_cjs_resolver: Default::default(),
|
||||
resolver,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
use super::cache::calculate_fs_version;
|
||||
use super::cache::LspCache;
|
||||
use super::config::Config;
|
||||
use super::resolver::LspIsCjsResolver;
|
||||
use super::resolver::LspResolver;
|
||||
use super::resolver::ScopeDepInfo;
|
||||
use super::resolver::SingleReferrerGraphResolver;
|
||||
|
@ -27,7 +26,6 @@ use deno_core::futures::future::Shared;
|
|||
use deno_core::futures::FutureExt;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::Resolution;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_runtime::deno_node;
|
||||
|
@ -36,7 +34,8 @@ use deno_semver::npm::NpmPackageReqReference;
|
|||
use deno_semver::package::PackageReq;
|
||||
use indexmap::IndexMap;
|
||||
use indexmap::IndexSet;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use node_resolver::NodeResolutionKind;
|
||||
use node_resolver::ResolutionMode;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
|
@ -313,6 +312,7 @@ pub struct Document {
|
|||
media_type: MediaType,
|
||||
/// Present if and only if this is an open document.
|
||||
open_data: Option<DocumentOpenData>,
|
||||
resolution_mode: ResolutionMode,
|
||||
resolver: Arc<LspResolver>,
|
||||
specifier: ModuleSpecifier,
|
||||
text: Arc<str>,
|
||||
|
@ -328,7 +328,6 @@ impl Document {
|
|||
maybe_lsp_version: Option<i32>,
|
||||
maybe_language_id: Option<LanguageId>,
|
||||
maybe_headers: Option<HashMap<String, String>>,
|
||||
is_cjs_resolver: &LspIsCjsResolver,
|
||||
resolver: Arc<LspResolver>,
|
||||
config: Arc<Config>,
|
||||
cache: &Arc<LspCache>,
|
||||
|
@ -340,7 +339,7 @@ impl Document {
|
|||
.or(file_referrer);
|
||||
let media_type =
|
||||
resolve_media_type(&specifier, maybe_headers.as_ref(), maybe_language_id);
|
||||
let (maybe_parsed_source, maybe_module) =
|
||||
let (maybe_parsed_source, maybe_module, resolution_mode) =
|
||||
if media_type_is_diagnosable(media_type) {
|
||||
parse_and_analyze_module(
|
||||
specifier.clone(),
|
||||
|
@ -348,11 +347,10 @@ impl Document {
|
|||
maybe_headers.as_ref(),
|
||||
media_type,
|
||||
file_referrer.as_ref(),
|
||||
is_cjs_resolver,
|
||||
&resolver,
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
(None, None, ResolutionMode::Import)
|
||||
};
|
||||
let maybe_module = maybe_module.and_then(Result::ok);
|
||||
let dependencies = maybe_module
|
||||
|
@ -387,6 +385,7 @@ impl Document {
|
|||
maybe_parsed_source,
|
||||
maybe_semantic_tokens: Default::default(),
|
||||
}),
|
||||
resolution_mode,
|
||||
resolver,
|
||||
specifier,
|
||||
text,
|
||||
|
@ -396,7 +395,6 @@ impl Document {
|
|||
|
||||
fn with_new_config(
|
||||
&self,
|
||||
is_cjs_resolver: &LspIsCjsResolver,
|
||||
resolver: Arc<LspResolver>,
|
||||
config: Arc<Config>,
|
||||
) -> Arc<Self> {
|
||||
|
@ -408,20 +406,20 @@ impl Document {
|
|||
let dependencies;
|
||||
let maybe_types_dependency;
|
||||
let maybe_parsed_source;
|
||||
let found_resolution_mode;
|
||||
let is_script;
|
||||
let maybe_test_module_fut;
|
||||
if media_type != self.media_type {
|
||||
let parsed_source_result =
|
||||
parse_source(self.specifier.clone(), self.text.clone(), media_type);
|
||||
let maybe_module = analyze_module(
|
||||
let (maybe_module_result, resolution_mode) = analyze_module(
|
||||
self.specifier.clone(),
|
||||
&parsed_source_result,
|
||||
self.maybe_headers.as_ref(),
|
||||
self.file_referrer.as_ref(),
|
||||
is_cjs_resolver,
|
||||
&resolver,
|
||||
)
|
||||
.ok();
|
||||
);
|
||||
let maybe_module = maybe_module_result.ok();
|
||||
dependencies = maybe_module
|
||||
.as_ref()
|
||||
.map(|m| Arc::new(m.dependencies.clone()))
|
||||
|
@ -433,17 +431,21 @@ impl Document {
|
|||
maybe_parsed_source = Some(parsed_source_result);
|
||||
maybe_test_module_fut =
|
||||
get_maybe_test_module_fut(maybe_parsed_source.as_ref(), &config);
|
||||
found_resolution_mode = resolution_mode;
|
||||
} else {
|
||||
let cli_resolver = resolver.as_cli_resolver(self.file_referrer.as_ref());
|
||||
let is_cjs_resolver =
|
||||
resolver.as_is_cjs_resolver(self.file_referrer.as_ref());
|
||||
let npm_resolver =
|
||||
resolver.create_graph_npm_resolver(self.file_referrer.as_ref());
|
||||
let config_data = resolver.as_config_data(self.file_referrer.as_ref());
|
||||
let jsx_import_source_config =
|
||||
config_data.and_then(|d| d.maybe_jsx_import_source_config());
|
||||
found_resolution_mode = is_cjs_resolver
|
||||
.get_lsp_resolution_mode(&self.specifier, self.is_script);
|
||||
let resolver = SingleReferrerGraphResolver {
|
||||
valid_referrer: &self.specifier,
|
||||
referrer_kind: is_cjs_resolver
|
||||
.get_lsp_referrer_kind(&self.specifier, self.is_script),
|
||||
module_resolution_mode: found_resolution_mode,
|
||||
cli_resolver,
|
||||
jsx_import_source_config: jsx_import_source_config.as_ref(),
|
||||
};
|
||||
|
@ -493,6 +495,7 @@ impl Document {
|
|||
maybe_language_id: self.maybe_language_id,
|
||||
maybe_test_module_fut,
|
||||
media_type,
|
||||
resolution_mode: found_resolution_mode,
|
||||
open_data: self.open_data.as_ref().map(|d| DocumentOpenData {
|
||||
lsp_version: d.lsp_version,
|
||||
maybe_parsed_source,
|
||||
|
@ -508,7 +511,6 @@ impl Document {
|
|||
|
||||
fn with_change(
|
||||
&self,
|
||||
is_cjs_resolver: &LspIsCjsResolver,
|
||||
version: i32,
|
||||
changes: Vec<lsp::TextDocumentContentChangeEvent>,
|
||||
) -> Result<Arc<Self>, AnyError> {
|
||||
|
@ -530,7 +532,7 @@ impl Document {
|
|||
}
|
||||
let text: Arc<str> = content.into();
|
||||
let media_type = self.media_type;
|
||||
let (maybe_parsed_source, maybe_module) = if self
|
||||
let (maybe_parsed_source, maybe_module, resolution_mode) = if self
|
||||
.maybe_language_id
|
||||
.as_ref()
|
||||
.map(|li| li.is_diagnosable())
|
||||
|
@ -542,11 +544,10 @@ impl Document {
|
|||
self.maybe_headers.as_ref(),
|
||||
media_type,
|
||||
self.file_referrer.as_ref(),
|
||||
is_cjs_resolver,
|
||||
self.resolver.as_ref(),
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
(None, None, ResolutionMode::Import)
|
||||
};
|
||||
let maybe_module = maybe_module.and_then(Result::ok);
|
||||
let dependencies = maybe_module
|
||||
|
@ -580,6 +581,7 @@ impl Document {
|
|||
maybe_navigation_tree: Mutex::new(None),
|
||||
maybe_test_module_fut,
|
||||
media_type,
|
||||
resolution_mode,
|
||||
open_data: self.open_data.is_some().then_some(DocumentOpenData {
|
||||
lsp_version: version,
|
||||
maybe_parsed_source,
|
||||
|
@ -613,6 +615,7 @@ impl Document {
|
|||
maybe_test_module_fut: self.maybe_test_module_fut.clone(),
|
||||
media_type: self.media_type,
|
||||
open_data: None,
|
||||
resolution_mode: self.resolution_mode,
|
||||
resolver: self.resolver.clone(),
|
||||
})
|
||||
}
|
||||
|
@ -641,6 +644,7 @@ impl Document {
|
|||
maybe_test_module_fut: self.maybe_test_module_fut.clone(),
|
||||
media_type: self.media_type,
|
||||
open_data: self.open_data.clone(),
|
||||
resolution_mode: self.resolution_mode,
|
||||
resolver: self.resolver.clone(),
|
||||
})
|
||||
}
|
||||
|
@ -664,6 +668,10 @@ impl Document {
|
|||
&self.text
|
||||
}
|
||||
|
||||
pub fn resolution_mode(&self) -> ResolutionMode {
|
||||
self.resolution_mode
|
||||
}
|
||||
|
||||
pub fn text_info(&self) -> &SourceTextInfo {
|
||||
// try to get the text info from the parsed source and if
|
||||
// not then create one in the cell
|
||||
|
@ -677,14 +685,6 @@ impl Document {
|
|||
.get_or_init(|| SourceTextInfo::new(self.text.clone()))
|
||||
})
|
||||
}
|
||||
|
||||
/// If this is maybe a CJS script and maybe not an ES module.
|
||||
///
|
||||
/// Use `LspIsCjsResolver` to determine for sure.
|
||||
pub fn is_script(&self) -> Option<bool> {
|
||||
self.is_script
|
||||
}
|
||||
|
||||
pub fn line_index(&self) -> Arc<LineIndex> {
|
||||
self.line_index.clone()
|
||||
}
|
||||
|
@ -768,7 +768,7 @@ impl Document {
|
|||
};
|
||||
self.dependencies().iter().find_map(|(s, dep)| {
|
||||
dep
|
||||
.includes(&position)
|
||||
.includes(position)
|
||||
.map(|r| (s.clone(), dep.clone(), r.clone()))
|
||||
})
|
||||
}
|
||||
|
@ -809,15 +809,15 @@ fn resolve_media_type(
|
|||
MediaType::from_specifier(specifier)
|
||||
}
|
||||
|
||||
pub fn to_lsp_range(range: &deno_graph::Range) -> lsp::Range {
|
||||
pub fn to_lsp_range(referrer: &deno_graph::Range) -> lsp::Range {
|
||||
lsp::Range {
|
||||
start: lsp::Position {
|
||||
line: range.start.line as u32,
|
||||
character: range.start.character as u32,
|
||||
line: referrer.range.start.line as u32,
|
||||
character: referrer.range.start.character as u32,
|
||||
},
|
||||
end: lsp::Position {
|
||||
line: range.end.line as u32,
|
||||
character: range.end.character as u32,
|
||||
line: referrer.range.end.line as u32,
|
||||
character: referrer.range.end.character as u32,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -832,7 +832,6 @@ impl FileSystemDocuments {
|
|||
pub fn get(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
is_cjs_resolver: &LspIsCjsResolver,
|
||||
resolver: &Arc<LspResolver>,
|
||||
config: &Arc<Config>,
|
||||
cache: &Arc<LspCache>,
|
||||
|
@ -856,14 +855,7 @@ impl FileSystemDocuments {
|
|||
};
|
||||
if dirty {
|
||||
// attempt to update the file on the file system
|
||||
self.refresh_document(
|
||||
specifier,
|
||||
is_cjs_resolver,
|
||||
resolver,
|
||||
config,
|
||||
cache,
|
||||
file_referrer,
|
||||
)
|
||||
self.refresh_document(specifier, resolver, config, cache, file_referrer)
|
||||
} else {
|
||||
old_doc
|
||||
}
|
||||
|
@ -874,7 +866,6 @@ impl FileSystemDocuments {
|
|||
fn refresh_document(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
is_cjs_resolver: &LspIsCjsResolver,
|
||||
resolver: &Arc<LspResolver>,
|
||||
config: &Arc<Config>,
|
||||
cache: &Arc<LspCache>,
|
||||
|
@ -896,7 +887,6 @@ impl FileSystemDocuments {
|
|||
None,
|
||||
None,
|
||||
None,
|
||||
is_cjs_resolver,
|
||||
resolver.clone(),
|
||||
config.clone(),
|
||||
cache,
|
||||
|
@ -913,7 +903,6 @@ impl FileSystemDocuments {
|
|||
None,
|
||||
None,
|
||||
None,
|
||||
is_cjs_resolver,
|
||||
resolver.clone(),
|
||||
config.clone(),
|
||||
cache,
|
||||
|
@ -936,7 +925,7 @@ impl FileSystemDocuments {
|
|||
let content = bytes_to_content(
|
||||
specifier,
|
||||
media_type,
|
||||
cached_file.content,
|
||||
cached_file.content.into_owned(),
|
||||
maybe_charset,
|
||||
)
|
||||
.ok()?;
|
||||
|
@ -946,7 +935,6 @@ impl FileSystemDocuments {
|
|||
None,
|
||||
None,
|
||||
Some(cached_file.metadata.headers),
|
||||
is_cjs_resolver,
|
||||
resolver.clone(),
|
||||
config.clone(),
|
||||
cache,
|
||||
|
@ -987,8 +975,6 @@ pub struct Documents {
|
|||
/// The DENO_DIR that the documents looks for non-file based modules.
|
||||
cache: Arc<LspCache>,
|
||||
config: Arc<Config>,
|
||||
/// Resolver for detecting if a document is CJS or ESM.
|
||||
is_cjs_resolver: Arc<LspIsCjsResolver>,
|
||||
/// A resolver that takes into account currently loaded import map and JSX
|
||||
/// settings.
|
||||
resolver: Arc<LspResolver>,
|
||||
|
@ -1024,7 +1010,6 @@ impl Documents {
|
|||
// the cache for remote modules here in order to get the
|
||||
// x-typescript-types?
|
||||
None,
|
||||
&self.is_cjs_resolver,
|
||||
self.resolver.clone(),
|
||||
self.config.clone(),
|
||||
&self.cache,
|
||||
|
@ -1059,7 +1044,7 @@ impl Documents {
|
|||
))
|
||||
})?;
|
||||
self.dirty = true;
|
||||
let doc = doc.with_change(&self.is_cjs_resolver, version, changes)?;
|
||||
let doc = doc.with_change(version, changes)?;
|
||||
self.open_docs.insert(doc.specifier().clone(), doc.clone());
|
||||
Ok(doc)
|
||||
}
|
||||
|
@ -1191,7 +1176,6 @@ impl Documents {
|
|||
if let Some(old_doc) = old_doc {
|
||||
self.file_system_docs.get(
|
||||
specifier,
|
||||
&self.is_cjs_resolver,
|
||||
&self.resolver,
|
||||
&self.config,
|
||||
&self.cache,
|
||||
|
@ -1216,7 +1200,6 @@ impl Documents {
|
|||
} else {
|
||||
self.file_system_docs.get(
|
||||
&specifier,
|
||||
&self.is_cjs_resolver,
|
||||
&self.resolver,
|
||||
&self.config,
|
||||
&self.cache,
|
||||
|
@ -1271,7 +1254,8 @@ impl Documents {
|
|||
/// tsc when type checking.
|
||||
pub fn resolve(
|
||||
&self,
|
||||
raw_specifiers: &[String],
|
||||
// (is_cjs: bool, raw_specifier: String)
|
||||
raw_specifiers: &[(bool, String)],
|
||||
referrer: &ModuleSpecifier,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Vec<Option<(ModuleSpecifier, MediaType)>> {
|
||||
|
@ -1281,11 +1265,12 @@ impl Documents {
|
|||
.and_then(|d| d.file_referrer())
|
||||
.or(file_referrer);
|
||||
let dependencies = referrer_doc.as_ref().map(|d| d.dependencies());
|
||||
let referrer_kind = self
|
||||
.is_cjs_resolver
|
||||
.get_maybe_doc_module_kind(referrer, referrer_doc.as_deref());
|
||||
let mut results = Vec::new();
|
||||
for raw_specifier in raw_specifiers {
|
||||
for (is_cjs, raw_specifier) in raw_specifiers {
|
||||
let resolution_mode = match is_cjs {
|
||||
true => ResolutionMode::Require,
|
||||
false => ResolutionMode::Import,
|
||||
};
|
||||
if raw_specifier.starts_with("asset:") {
|
||||
if let Ok(specifier) = ModuleSpecifier::parse(raw_specifier) {
|
||||
let media_type = MediaType::from_specifier(&specifier);
|
||||
|
@ -1300,14 +1285,14 @@ impl Documents {
|
|||
results.push(self.resolve_dependency(
|
||||
specifier,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
resolution_mode,
|
||||
file_referrer,
|
||||
));
|
||||
} else if let Some(specifier) = dep.maybe_code.maybe_specifier() {
|
||||
results.push(self.resolve_dependency(
|
||||
specifier,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
resolution_mode,
|
||||
file_referrer,
|
||||
));
|
||||
} else {
|
||||
|
@ -1316,19 +1301,16 @@ impl Documents {
|
|||
} else if let Ok(specifier) =
|
||||
self.resolver.as_cli_resolver(file_referrer).resolve(
|
||||
raw_specifier,
|
||||
&deno_graph::Range {
|
||||
specifier: referrer.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
},
|
||||
referrer_kind,
|
||||
ResolutionMode::Types,
|
||||
referrer,
|
||||
deno_graph::Position::zeroed(),
|
||||
resolution_mode,
|
||||
NodeResolutionKind::Types,
|
||||
)
|
||||
{
|
||||
results.push(self.resolve_dependency(
|
||||
&specifier,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
resolution_mode,
|
||||
file_referrer,
|
||||
));
|
||||
} else {
|
||||
|
@ -1347,7 +1329,6 @@ impl Documents {
|
|||
) {
|
||||
self.config = Arc::new(config.clone());
|
||||
self.cache = Arc::new(cache.clone());
|
||||
self.is_cjs_resolver = Arc::new(LspIsCjsResolver::new(cache));
|
||||
self.resolver = resolver.clone();
|
||||
|
||||
node_resolver::PackageJsonThreadLocalCache::clear();
|
||||
|
@ -1371,21 +1352,14 @@ impl Documents {
|
|||
if !config.specifier_enabled(doc.specifier()) {
|
||||
continue;
|
||||
}
|
||||
*doc = doc.with_new_config(
|
||||
&self.is_cjs_resolver,
|
||||
self.resolver.clone(),
|
||||
self.config.clone(),
|
||||
);
|
||||
*doc = doc.with_new_config(self.resolver.clone(), self.config.clone());
|
||||
}
|
||||
for mut doc in self.file_system_docs.docs.iter_mut() {
|
||||
if !config.specifier_enabled(doc.specifier()) {
|
||||
continue;
|
||||
}
|
||||
*doc.value_mut() = doc.with_new_config(
|
||||
&self.is_cjs_resolver,
|
||||
self.resolver.clone(),
|
||||
self.config.clone(),
|
||||
);
|
||||
*doc.value_mut() =
|
||||
doc.with_new_config(self.resolver.clone(), self.config.clone());
|
||||
}
|
||||
self.open_docs = open_docs;
|
||||
let mut preload_count = 0;
|
||||
|
@ -1402,7 +1376,6 @@ impl Documents {
|
|||
{
|
||||
fs_docs.refresh_document(
|
||||
specifier,
|
||||
&self.is_cjs_resolver,
|
||||
&self.resolver,
|
||||
&self.config,
|
||||
&self.cache,
|
||||
|
@ -1477,27 +1450,24 @@ impl Documents {
|
|||
let type_specifier = jsx_config.default_types_specifier.as_ref()?;
|
||||
let code_specifier = jsx_config.default_specifier.as_ref()?;
|
||||
let cli_resolver = self.resolver.as_cli_resolver(Some(scope));
|
||||
let range = deno_graph::Range {
|
||||
specifier: jsx_config.base_url.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
};
|
||||
let type_specifier = cli_resolver
|
||||
.resolve(
|
||||
type_specifier,
|
||||
&range,
|
||||
&jsx_config.base_url,
|
||||
deno_graph::Position::zeroed(),
|
||||
// todo(dsherret): this is wrong because it doesn't consider CJS referrers
|
||||
deno_package_json::NodeModuleKind::Esm,
|
||||
ResolutionMode::Types,
|
||||
ResolutionMode::Import,
|
||||
NodeResolutionKind::Types,
|
||||
)
|
||||
.ok()?;
|
||||
let code_specifier = cli_resolver
|
||||
.resolve(
|
||||
code_specifier,
|
||||
&range,
|
||||
&jsx_config.base_url,
|
||||
deno_graph::Position::zeroed(),
|
||||
// todo(dsherret): this is wrong because it doesn't consider CJS referrers
|
||||
deno_package_json::NodeModuleKind::Esm,
|
||||
ResolutionMode::Execution,
|
||||
ResolutionMode::Import,
|
||||
NodeResolutionKind::Execution,
|
||||
)
|
||||
.ok()?;
|
||||
dep_info
|
||||
|
@ -1542,7 +1512,7 @@ impl Documents {
|
|||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
resolution_mode: ResolutionMode,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<(ModuleSpecifier, MediaType)> {
|
||||
if let Some(module_name) = specifier.as_str().strip_prefix("node:") {
|
||||
|
@ -1559,7 +1529,7 @@ impl Documents {
|
|||
let (s, mt) = self.resolver.npm_to_file_url(
|
||||
&npm_ref,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
resolution_mode,
|
||||
file_referrer,
|
||||
)?;
|
||||
specifier = s;
|
||||
|
@ -1571,8 +1541,12 @@ impl Documents {
|
|||
return Some((specifier, media_type));
|
||||
};
|
||||
if let Some(types) = doc.maybe_types_dependency().maybe_specifier() {
|
||||
let specifier_kind = self.is_cjs_resolver.get_doc_module_kind(&doc);
|
||||
self.resolve_dependency(types, &specifier, specifier_kind, file_referrer)
|
||||
self.resolve_dependency(
|
||||
types,
|
||||
&specifier,
|
||||
doc.resolution_mode(),
|
||||
file_referrer,
|
||||
)
|
||||
} else {
|
||||
Some((doc.specifier().clone(), doc.media_type()))
|
||||
}
|
||||
|
@ -1640,19 +1614,25 @@ fn parse_and_analyze_module(
|
|||
maybe_headers: Option<&HashMap<String, String>>,
|
||||
media_type: MediaType,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
is_cjs_resolver: &LspIsCjsResolver,
|
||||
resolver: &LspResolver,
|
||||
) -> (Option<ParsedSourceResult>, Option<ModuleResult>) {
|
||||
) -> (
|
||||
Option<ParsedSourceResult>,
|
||||
Option<ModuleResult>,
|
||||
ResolutionMode,
|
||||
) {
|
||||
let parsed_source_result = parse_source(specifier.clone(), text, media_type);
|
||||
let module_result = analyze_module(
|
||||
let (module_result, resolution_mode) = analyze_module(
|
||||
specifier,
|
||||
&parsed_source_result,
|
||||
maybe_headers,
|
||||
file_referrer,
|
||||
is_cjs_resolver,
|
||||
resolver,
|
||||
);
|
||||
(Some(parsed_source_result), Some(module_result))
|
||||
(
|
||||
Some(parsed_source_result),
|
||||
Some(module_result),
|
||||
resolution_mode,
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_source(
|
||||
|
@ -1675,44 +1655,51 @@ fn analyze_module(
|
|||
parsed_source_result: &ParsedSourceResult,
|
||||
maybe_headers: Option<&HashMap<String, String>>,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
is_cjs_resolver: &LspIsCjsResolver,
|
||||
resolver: &LspResolver,
|
||||
) -> ModuleResult {
|
||||
) -> (ModuleResult, ResolutionMode) {
|
||||
match parsed_source_result {
|
||||
Ok(parsed_source) => {
|
||||
let npm_resolver = resolver.create_graph_npm_resolver(file_referrer);
|
||||
let cli_resolver = resolver.as_cli_resolver(file_referrer);
|
||||
let is_cjs_resolver = resolver.as_is_cjs_resolver(file_referrer);
|
||||
let config_data = resolver.as_config_data(file_referrer);
|
||||
let valid_referrer = specifier.clone();
|
||||
let jsx_import_source_config =
|
||||
config_data.and_then(|d| d.maybe_jsx_import_source_config());
|
||||
let module_resolution_mode = is_cjs_resolver.get_lsp_resolution_mode(
|
||||
&specifier,
|
||||
Some(parsed_source.compute_is_script()),
|
||||
);
|
||||
let resolver = SingleReferrerGraphResolver {
|
||||
valid_referrer: &valid_referrer,
|
||||
referrer_kind: is_cjs_resolver.get_lsp_referrer_kind(
|
||||
&specifier,
|
||||
Some(parsed_source.compute_is_script()),
|
||||
),
|
||||
module_resolution_mode,
|
||||
cli_resolver,
|
||||
jsx_import_source_config: jsx_import_source_config.as_ref(),
|
||||
};
|
||||
Ok(deno_graph::parse_module_from_ast(
|
||||
deno_graph::ParseModuleFromAstOptions {
|
||||
graph_kind: deno_graph::GraphKind::TypesOnly,
|
||||
specifier,
|
||||
maybe_headers,
|
||||
parsed_source,
|
||||
// use a null file system because there's no need to bother resolving
|
||||
// dynamic imports like import(`./dir/${something}`) in the LSP
|
||||
file_system: &deno_graph::source::NullFileSystem,
|
||||
jsr_url_provider: &CliJsrUrlProvider,
|
||||
maybe_resolver: Some(&resolver),
|
||||
maybe_npm_resolver: Some(&npm_resolver),
|
||||
},
|
||||
))
|
||||
(
|
||||
Ok(deno_graph::parse_module_from_ast(
|
||||
deno_graph::ParseModuleFromAstOptions {
|
||||
graph_kind: deno_graph::GraphKind::TypesOnly,
|
||||
specifier,
|
||||
maybe_headers,
|
||||
parsed_source,
|
||||
// use a null file system because there's no need to bother resolving
|
||||
// dynamic imports like import(`./dir/${something}`) in the LSP
|
||||
file_system: &deno_graph::source::NullFileSystem,
|
||||
jsr_url_provider: &CliJsrUrlProvider,
|
||||
maybe_resolver: Some(&resolver),
|
||||
maybe_npm_resolver: Some(&npm_resolver),
|
||||
},
|
||||
)),
|
||||
module_resolution_mode,
|
||||
)
|
||||
}
|
||||
Err(err) => Err(deno_graph::ModuleGraphError::ModuleError(
|
||||
deno_graph::ModuleError::ParseErr(specifier, err.clone()),
|
||||
)),
|
||||
Err(err) => (
|
||||
Err(deno_graph::ModuleGraphError::ModuleError(
|
||||
deno_graph::ModuleError::ParseErr(specifier, err.clone()),
|
||||
)),
|
||||
ResolutionMode::Import,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -262,7 +262,7 @@ fn read_cached_url(
|
|||
cache
|
||||
.get(&cache.cache_item_key(url).ok()?, None)
|
||||
.ok()?
|
||||
.map(|f| f.content)
|
||||
.map(|f| f.content.into_owned())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -22,7 +22,8 @@ use deno_semver::jsr::JsrPackageReqReference;
|
|||
use indexmap::Equivalent;
|
||||
use indexmap::IndexSet;
|
||||
use log::error;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use node_resolver::NodeResolutionKind;
|
||||
use node_resolver::ResolutionMode;
|
||||
use serde::Deserialize;
|
||||
use serde_json::from_value;
|
||||
use std::collections::BTreeMap;
|
||||
|
@ -78,7 +79,6 @@ use super::parent_process_checker;
|
|||
use super::performance::Performance;
|
||||
use super::refactor;
|
||||
use super::registries::ModuleRegistry;
|
||||
use super::resolver::LspIsCjsResolver;
|
||||
use super::resolver::LspResolver;
|
||||
use super::testing;
|
||||
use super::text;
|
||||
|
@ -146,7 +146,6 @@ pub struct StateSnapshot {
|
|||
pub project_version: usize,
|
||||
pub assets: AssetsSnapshot,
|
||||
pub config: Arc<Config>,
|
||||
pub is_cjs_resolver: Arc<LspIsCjsResolver>,
|
||||
pub documents: Arc<Documents>,
|
||||
pub resolver: Arc<LspResolver>,
|
||||
}
|
||||
|
@ -206,7 +205,6 @@ pub struct Inner {
|
|||
pub documents: Documents,
|
||||
http_client_provider: Arc<HttpClientProvider>,
|
||||
initial_cwd: PathBuf,
|
||||
pub is_cjs_resolver: Arc<LspIsCjsResolver>,
|
||||
jsr_search_api: CliJsrSearchApi,
|
||||
/// Handles module registries, which allow discovery of modules
|
||||
module_registry: ModuleRegistry,
|
||||
|
@ -484,7 +482,6 @@ impl Inner {
|
|||
let initial_cwd = std::env::current_dir().unwrap_or_else(|_| {
|
||||
panic!("Could not resolve current working directory")
|
||||
});
|
||||
let is_cjs_resolver = Arc::new(LspIsCjsResolver::new(&cache));
|
||||
|
||||
Self {
|
||||
assets,
|
||||
|
@ -496,7 +493,6 @@ impl Inner {
|
|||
documents,
|
||||
http_client_provider,
|
||||
initial_cwd: initial_cwd.clone(),
|
||||
is_cjs_resolver,
|
||||
jsr_search_api,
|
||||
project_version: 0,
|
||||
task_queue: Default::default(),
|
||||
|
@ -607,7 +603,6 @@ impl Inner {
|
|||
project_version: self.project_version,
|
||||
assets: self.assets.snapshot(),
|
||||
config: Arc::new(self.config.clone()),
|
||||
is_cjs_resolver: self.is_cjs_resolver.clone(),
|
||||
documents: Arc::new(self.documents.clone()),
|
||||
resolver: self.resolver.snapshot(),
|
||||
})
|
||||
|
@ -629,7 +624,6 @@ impl Inner {
|
|||
}
|
||||
});
|
||||
self.cache = LspCache::new(global_cache_url);
|
||||
self.is_cjs_resolver = Arc::new(LspIsCjsResolver::new(&self.cache));
|
||||
let deno_dir = self.cache.deno_dir();
|
||||
let workspace_settings = self.config.workspace_settings();
|
||||
let maybe_root_path = self
|
||||
|
@ -993,13 +987,10 @@ impl Inner {
|
|||
let resolver = inner.resolver.as_cli_resolver(Some(&referrer));
|
||||
let Ok(specifier) = resolver.resolve(
|
||||
&specifier,
|
||||
&deno_graph::Range {
|
||||
specifier: referrer.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
},
|
||||
NodeModuleKind::Esm,
|
||||
deno_graph::source::ResolutionMode::Types,
|
||||
&referrer,
|
||||
deno_graph::Position::zeroed(),
|
||||
ResolutionMode::Import,
|
||||
NodeResolutionKind::Types,
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
|
@ -1640,8 +1631,8 @@ impl Inner {
|
|||
.get_ts_diagnostics(&specifier, asset_or_doc.document_lsp_version());
|
||||
let specifier_kind = asset_or_doc
|
||||
.document()
|
||||
.map(|d| self.is_cjs_resolver.get_doc_module_kind(d))
|
||||
.unwrap_or(NodeModuleKind::Esm);
|
||||
.map(|d| d.resolution_mode())
|
||||
.unwrap_or(ResolutionMode::Import);
|
||||
let mut includes_no_cache = false;
|
||||
for diagnostic in &fixable_diagnostics {
|
||||
match diagnostic.source.as_deref() {
|
||||
|
@ -1864,8 +1855,8 @@ impl Inner {
|
|||
maybe_asset_or_doc
|
||||
.as_ref()
|
||||
.and_then(|d| d.document())
|
||||
.map(|d| self.is_cjs_resolver.get_doc_module_kind(d))
|
||||
.unwrap_or(NodeModuleKind::Esm),
|
||||
.map(|d| d.resolution_mode())
|
||||
.unwrap_or(ResolutionMode::Import),
|
||||
&combined_code_actions.changes,
|
||||
self,
|
||||
)
|
||||
|
@ -1921,8 +1912,8 @@ impl Inner {
|
|||
&action_data.specifier,
|
||||
asset_or_doc
|
||||
.document()
|
||||
.map(|d| self.is_cjs_resolver.get_doc_module_kind(d))
|
||||
.unwrap_or(NodeModuleKind::Esm),
|
||||
.map(|d| d.resolution_mode())
|
||||
.unwrap_or(ResolutionMode::Import),
|
||||
&refactor_edit_info.edits,
|
||||
self,
|
||||
)
|
||||
|
@ -2272,7 +2263,6 @@ impl Inner {
|
|||
&self.jsr_search_api,
|
||||
&self.npm_search_api,
|
||||
&self.documents,
|
||||
&self.is_cjs_resolver,
|
||||
self.resolver.as_ref(),
|
||||
self
|
||||
.config
|
||||
|
@ -3781,14 +3771,11 @@ impl Inner {
|
|||
fn task_definitions(&self) -> LspResult<Vec<TaskDefinition>> {
|
||||
let mut result = vec![];
|
||||
for config_file in self.config.tree.config_files() {
|
||||
if let Some(tasks) = json!(&config_file.json.tasks).as_object() {
|
||||
for (name, value) in tasks {
|
||||
let Some(command) = value.as_str() else {
|
||||
continue;
|
||||
};
|
||||
if let Some(tasks) = config_file.to_tasks_config().ok().flatten() {
|
||||
for (name, def) in tasks {
|
||||
result.push(TaskDefinition {
|
||||
name: name.clone(),
|
||||
command: command.to_string(),
|
||||
command: def.command.clone(),
|
||||
source_uri: url_to_uri(&config_file.specifier)
|
||||
.map_err(|_| LspError::internal_error())?,
|
||||
});
|
||||
|
|
|
@ -14,8 +14,6 @@ pub const LATEST_DIAGNOSTIC_BATCH_INDEX: &str =
|
|||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TaskDefinition {
|
||||
pub name: String,
|
||||
// TODO(nayeemrmn): Rename this to `command` in vscode_deno.
|
||||
#[serde(rename = "detail")]
|
||||
pub command: String,
|
||||
pub source_uri: lsp::Uri,
|
||||
}
|
||||
|
|
|
@ -56,9 +56,6 @@ pub async fn start() -> Result<(), AnyError> {
|
|||
LanguageServer::performance_request,
|
||||
)
|
||||
.custom_method(lsp_custom::TASK_REQUEST, LanguageServer::task_definitions)
|
||||
// TODO(nayeemrmn): Rename this to `deno/taskDefinitions` in vscode_deno and
|
||||
// remove this alias.
|
||||
.custom_method("deno/task", LanguageServer::task_definitions)
|
||||
.custom_method(testing::TEST_RUN_REQUEST, LanguageServer::test_run_request)
|
||||
.custom_method(
|
||||
testing::TEST_RUN_CANCEL_REQUEST,
|
||||
|
|
|
@ -9,29 +9,26 @@ use deno_config::workspace::PackageJsonDepResolution;
|
|||
use deno_config::workspace::WorkspaceResolver;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::url::Url;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::GraphImport;
|
||||
use deno_graph::ModuleSpecifier;
|
||||
use deno_graph::Range;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_path_util::url_from_directory_path;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_resolver::cjs::IsCjsResolutionMode;
|
||||
use deno_resolver::npm::NpmReqResolverOptions;
|
||||
use deno_resolver::DenoResolverOptions;
|
||||
use deno_resolver::NodeAndNpmReqResolver;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node::NodeResolver;
|
||||
use deno_runtime::deno_node::PackageJson;
|
||||
use deno_runtime::deno_node::PackageJsonResolver;
|
||||
use deno_semver::jsr::JsrPackageReqReference;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use indexmap::IndexMap;
|
||||
use node_resolver::errors::ClosestPkgJsonError;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use node_resolver::NodeResolutionMode;
|
||||
use node_resolver::NodeResolutionKind;
|
||||
use node_resolver::ResolutionMode;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
|
@ -40,7 +37,6 @@ use std::collections::HashSet;
|
|||
use std::sync::Arc;
|
||||
|
||||
use super::cache::LspCache;
|
||||
use super::documents::Document;
|
||||
use super::jsr::JsrCacheResolver;
|
||||
use crate::args::create_default_npmrc;
|
||||
use crate::args::CacheSetting;
|
||||
|
@ -48,6 +44,8 @@ use crate::args::CliLockfile;
|
|||
use crate::args::NpmInstallDepsProvider;
|
||||
use crate::cache::DenoCacheEnvFsAdapter;
|
||||
use crate::factory::Deferred;
|
||||
use crate::graph_util::to_node_resolution_kind;
|
||||
use crate::graph_util::to_node_resolution_mode;
|
||||
use crate::graph_util::CliJsrUrlProvider;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
use crate::lsp::config::Config;
|
||||
|
@ -70,7 +68,6 @@ use crate::resolver::CliResolverOptions;
|
|||
use crate::resolver::IsCjsResolver;
|
||||
use crate::resolver::WorkerCliNpmGraphResolver;
|
||||
use crate::tsc::into_specifier_and_media_type;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
use crate::util::progress_bar::ProgressBarStyle;
|
||||
|
||||
|
@ -78,6 +75,7 @@ use crate::util::progress_bar::ProgressBarStyle;
|
|||
struct LspScopeResolver {
|
||||
resolver: Arc<CliResolver>,
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
is_cjs_resolver: Arc<IsCjsResolver>,
|
||||
jsr_resolver: Option<Arc<JsrCacheResolver>>,
|
||||
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
node_resolver: Option<Arc<NodeResolver>>,
|
||||
|
@ -96,6 +94,7 @@ impl Default for LspScopeResolver {
|
|||
Self {
|
||||
resolver: factory.cli_resolver().clone(),
|
||||
in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(),
|
||||
is_cjs_resolver: factory.is_cjs_resolver().clone(),
|
||||
jsr_resolver: None,
|
||||
npm_resolver: None,
|
||||
node_resolver: None,
|
||||
|
@ -146,7 +145,7 @@ impl LspScopeResolver {
|
|||
.map(|(referrer, imports)| {
|
||||
let resolver = SingleReferrerGraphResolver {
|
||||
valid_referrer: &referrer,
|
||||
referrer_kind: NodeModuleKind::Esm,
|
||||
module_resolution_mode: ResolutionMode::Import,
|
||||
cli_resolver: &cli_resolver,
|
||||
jsx_import_source_config: maybe_jsx_import_source_config
|
||||
.as_ref(),
|
||||
|
@ -180,16 +179,16 @@ impl LspScopeResolver {
|
|||
&req_ref,
|
||||
&referrer,
|
||||
// todo(dsherret): this is wrong because it doesn't consider CJS referrers
|
||||
NodeModuleKind::Esm,
|
||||
NodeResolutionMode::Types,
|
||||
ResolutionMode::Import,
|
||||
NodeResolutionKind::Types,
|
||||
)
|
||||
.or_else(|_| {
|
||||
npm_pkg_req_resolver.resolve_req_reference(
|
||||
&req_ref,
|
||||
&referrer,
|
||||
// todo(dsherret): this is wrong because it doesn't consider CJS referrers
|
||||
NodeModuleKind::Esm,
|
||||
NodeResolutionMode::Execution,
|
||||
ResolutionMode::Import,
|
||||
NodeResolutionKind::Execution,
|
||||
)
|
||||
})
|
||||
.ok()?,
|
||||
|
@ -205,6 +204,7 @@ impl LspScopeResolver {
|
|||
Self {
|
||||
resolver: cli_resolver,
|
||||
in_npm_pkg_checker,
|
||||
is_cjs_resolver: factory.is_cjs_resolver().clone(),
|
||||
jsr_resolver,
|
||||
npm_pkg_req_resolver,
|
||||
npm_resolver,
|
||||
|
@ -228,6 +228,7 @@ impl LspScopeResolver {
|
|||
Arc::new(Self {
|
||||
resolver: factory.cli_resolver().clone(),
|
||||
in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(),
|
||||
is_cjs_resolver: factory.is_cjs_resolver().clone(),
|
||||
jsr_resolver: self.jsr_resolver.clone(),
|
||||
npm_pkg_req_resolver: factory.npm_pkg_req_resolver().cloned(),
|
||||
npm_resolver: factory.npm_resolver().cloned(),
|
||||
|
@ -345,6 +346,14 @@ impl LspResolver {
|
|||
resolver.resolver.create_graph_npm_resolver()
|
||||
}
|
||||
|
||||
pub fn as_is_cjs_resolver(
|
||||
&self,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> &IsCjsResolver {
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
resolver.is_cjs_resolver.as_ref()
|
||||
}
|
||||
|
||||
pub fn as_config_data(
|
||||
&self,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
|
@ -369,6 +378,14 @@ impl LspResolver {
|
|||
resolver.npm_resolver.as_ref().and_then(|r| r.as_managed())
|
||||
}
|
||||
|
||||
pub fn pkg_json_resolver(
|
||||
&self,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> &Arc<PackageJsonResolver> {
|
||||
let resolver = self.get_scope_resolver(Some(referrer));
|
||||
&resolver.pkg_json_resolver
|
||||
}
|
||||
|
||||
pub fn graph_imports_by_referrer(
|
||||
&self,
|
||||
file_referrer: &ModuleSpecifier,
|
||||
|
@ -424,7 +441,7 @@ impl LspResolver {
|
|||
&self,
|
||||
req_ref: &NpmPackageReqReference,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
resolution_mode: ResolutionMode,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<(ModuleSpecifier, MediaType)> {
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
|
@ -434,8 +451,8 @@ impl LspResolver {
|
|||
.resolve_req_reference(
|
||||
req_ref,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
NodeResolutionMode::Types,
|
||||
resolution_mode,
|
||||
NodeResolutionKind::Types,
|
||||
)
|
||||
.ok()?,
|
||||
)))
|
||||
|
@ -492,7 +509,7 @@ impl LspResolver {
|
|||
&self,
|
||||
specifier_text: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
resolution_mode: ResolutionMode,
|
||||
) -> bool {
|
||||
let resolver = self.get_scope_resolver(Some(referrer));
|
||||
let Some(npm_pkg_req_resolver) = resolver.npm_pkg_req_resolver.as_ref()
|
||||
|
@ -503,24 +520,14 @@ impl LspResolver {
|
|||
.resolve_if_for_npm_pkg(
|
||||
specifier_text,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
NodeResolutionMode::Types,
|
||||
resolution_mode,
|
||||
NodeResolutionKind::Types,
|
||||
)
|
||||
.ok()
|
||||
.flatten()
|
||||
.is_some()
|
||||
}
|
||||
|
||||
pub fn get_closest_package_json(
|
||||
&self,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> Result<Option<Arc<PackageJson>>, ClosestPkgJsonError> {
|
||||
let resolver = self.get_scope_resolver(Some(referrer));
|
||||
resolver
|
||||
.pkg_json_resolver
|
||||
.get_closest_package_json(referrer)
|
||||
}
|
||||
|
||||
pub fn resolve_redirects(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
|
@ -581,6 +588,7 @@ pub struct ScopeDepInfo {
|
|||
struct ResolverFactoryServices {
|
||||
cli_resolver: Deferred<Arc<CliResolver>>,
|
||||
in_npm_pkg_checker: Deferred<Arc<dyn InNpmPackageChecker>>,
|
||||
is_cjs_resolver: Deferred<Arc<IsCjsResolver>>,
|
||||
node_resolver: Deferred<Option<Arc<NodeResolver>>>,
|
||||
npm_pkg_req_resolver: Deferred<Option<Arc<CliNpmReqResolver>>>,
|
||||
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
|
@ -744,6 +752,23 @@ impl<'a> ResolverFactory<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn is_cjs_resolver(&self) -> &Arc<IsCjsResolver> {
|
||||
self.services.is_cjs_resolver.get_or_init(|| {
|
||||
Arc::new(IsCjsResolver::new(
|
||||
self.in_npm_pkg_checker().clone(),
|
||||
self.pkg_json_resolver().clone(),
|
||||
if self
|
||||
.config_data
|
||||
.is_some_and(|d| d.unstable.contains("detect-cjs"))
|
||||
{
|
||||
IsCjsResolutionMode::ImplicitTypeCommonJs
|
||||
} else {
|
||||
IsCjsResolutionMode::ExplicitTypeCommonJs
|
||||
},
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn node_resolver(&self) -> Option<&Arc<NodeResolver>> {
|
||||
self
|
||||
.services
|
||||
|
@ -803,99 +828,10 @@ impl std::fmt::Debug for RedirectResolver {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LspIsCjsResolver {
|
||||
inner: IsCjsResolver,
|
||||
}
|
||||
|
||||
impl Default for LspIsCjsResolver {
|
||||
fn default() -> Self {
|
||||
LspIsCjsResolver::new(&Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl LspIsCjsResolver {
|
||||
pub fn new(cache: &LspCache) -> Self {
|
||||
#[derive(Debug)]
|
||||
struct LspInNpmPackageChecker {
|
||||
global_cache_dir: ModuleSpecifier,
|
||||
}
|
||||
|
||||
impl LspInNpmPackageChecker {
|
||||
pub fn new(cache: &LspCache) -> Self {
|
||||
let npm_folder_path = cache.deno_dir().npm_folder_path();
|
||||
Self {
|
||||
global_cache_dir: url_from_directory_path(
|
||||
&canonicalize_path_maybe_not_exists(&npm_folder_path)
|
||||
.unwrap_or(npm_folder_path),
|
||||
)
|
||||
.unwrap_or_else(|_| {
|
||||
ModuleSpecifier::parse("file:///invalid/").unwrap()
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InNpmPackageChecker for LspInNpmPackageChecker {
|
||||
fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
if specifier.scheme() != "file" {
|
||||
return false;
|
||||
}
|
||||
if specifier
|
||||
.as_str()
|
||||
.starts_with(self.global_cache_dir.as_str())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
specifier.as_str().contains("/node_modules/")
|
||||
}
|
||||
}
|
||||
|
||||
let fs = Arc::new(deno_fs::RealFs);
|
||||
let pkg_json_resolver = Arc::new(PackageJsonResolver::new(
|
||||
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
|
||||
));
|
||||
|
||||
LspIsCjsResolver {
|
||||
inner: IsCjsResolver::new(
|
||||
Arc::new(LspInNpmPackageChecker::new(cache)),
|
||||
pkg_json_resolver,
|
||||
crate::resolver::IsCjsResolverOptions {
|
||||
detect_cjs: true,
|
||||
is_node_main: false,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_maybe_doc_module_kind(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_document: Option<&Document>,
|
||||
) -> NodeModuleKind {
|
||||
self.get_lsp_referrer_kind(
|
||||
specifier,
|
||||
maybe_document.and_then(|d| d.is_script()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_doc_module_kind(&self, document: &Document) -> NodeModuleKind {
|
||||
self.get_lsp_referrer_kind(document.specifier(), document.is_script())
|
||||
}
|
||||
|
||||
pub fn get_lsp_referrer_kind(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
is_script: Option<bool>,
|
||||
) -> NodeModuleKind {
|
||||
self.inner.get_lsp_referrer_kind(specifier, is_script)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SingleReferrerGraphResolver<'a> {
|
||||
pub valid_referrer: &'a ModuleSpecifier,
|
||||
pub referrer_kind: NodeModuleKind,
|
||||
pub module_resolution_mode: ResolutionMode,
|
||||
pub cli_resolver: &'a CliResolver,
|
||||
pub jsx_import_source_config: Option<&'a JsxImportSourceConfig>,
|
||||
}
|
||||
|
@ -924,16 +860,20 @@ impl<'a> deno_graph::source::Resolver for SingleReferrerGraphResolver<'a> {
|
|||
&self,
|
||||
specifier_text: &str,
|
||||
referrer_range: &Range,
|
||||
mode: ResolutionMode,
|
||||
resolution_kind: deno_graph::source::ResolutionKind,
|
||||
) -> Result<ModuleSpecifier, deno_graph::source::ResolveError> {
|
||||
// this resolver assumes it will only be used with a single referrer
|
||||
// with the provided referrer kind
|
||||
debug_assert_eq!(referrer_range.specifier, *self.valid_referrer);
|
||||
self.cli_resolver.resolve(
|
||||
specifier_text,
|
||||
referrer_range,
|
||||
self.referrer_kind,
|
||||
mode,
|
||||
&referrer_range.specifier,
|
||||
referrer_range.range.start,
|
||||
referrer_range
|
||||
.resolution_mode
|
||||
.map(to_node_resolution_mode)
|
||||
.unwrap_or(self.module_resolution_mode),
|
||||
to_node_resolution_kind(resolution_kind),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1001,9 +941,7 @@ impl RedirectResolver {
|
|||
if chain.len() > 10 {
|
||||
break None;
|
||||
}
|
||||
let Ok(target) =
|
||||
deno_core::resolve_import(location, specifier.as_str())
|
||||
else {
|
||||
let Ok(target) = specifier.join(location) else {
|
||||
break None;
|
||||
};
|
||||
chain.push((
|
||||
|
|
|
@ -70,7 +70,7 @@ use indexmap::IndexMap;
|
|||
use indexmap::IndexSet;
|
||||
use lazy_regex::lazy_regex;
|
||||
use log::error;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use node_resolver::ResolutionMode;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Captures;
|
||||
use regex::Regex;
|
||||
|
@ -1297,16 +1297,10 @@ impl TsServer {
|
|||
{
|
||||
// When an LSP request is cancelled by the client, the future this is being
|
||||
// executed under and any local variables here will be dropped at the next
|
||||
// await point. To pass on that cancellation to the TS thread, we make this
|
||||
// wrapper which cancels the request's token on drop.
|
||||
struct DroppableToken(CancellationToken);
|
||||
impl Drop for DroppableToken {
|
||||
fn drop(&mut self) {
|
||||
self.0.cancel();
|
||||
}
|
||||
}
|
||||
// await point. To pass on that cancellation to the TS thread, we use drop_guard
|
||||
// which cancels the request's token on drop.
|
||||
let token = token.child_token();
|
||||
let droppable_token = DroppableToken(token.clone());
|
||||
let droppable_token = token.clone().drop_guard();
|
||||
let (tx, mut rx) = oneshot::channel::<Result<String, AnyError>>();
|
||||
let change = self.pending_change.lock().take();
|
||||
|
||||
|
@ -1320,7 +1314,7 @@ impl TsServer {
|
|||
tokio::select! {
|
||||
value = &mut rx => {
|
||||
let value = value??;
|
||||
drop(droppable_token);
|
||||
droppable_token.disarm();
|
||||
Ok(serde_json::from_str(&value)?)
|
||||
}
|
||||
_ = token.cancelled() => {
|
||||
|
@ -4449,9 +4443,9 @@ fn op_load<'s>(
|
|||
version: state.script_version(&specifier),
|
||||
is_cjs: doc
|
||||
.document()
|
||||
.map(|d| state.state_snapshot.is_cjs_resolver.get_doc_module_kind(d))
|
||||
.unwrap_or(NodeModuleKind::Esm)
|
||||
== NodeModuleKind::Cjs,
|
||||
.map(|d| d.resolution_mode())
|
||||
.unwrap_or(ResolutionMode::Import)
|
||||
== ResolutionMode::Require,
|
||||
})
|
||||
};
|
||||
let serialized = serde_v8::to_v8(scope, maybe_load_response)?;
|
||||
|
@ -4479,17 +4473,9 @@ fn op_release(
|
|||
fn op_resolve(
|
||||
state: &mut OpState,
|
||||
#[string] base: String,
|
||||
is_base_cjs: bool,
|
||||
#[serde] specifiers: Vec<String>,
|
||||
#[serde] specifiers: Vec<(bool, String)>,
|
||||
) -> Result<Vec<Option<(String, String)>>, AnyError> {
|
||||
op_resolve_inner(
|
||||
state,
|
||||
ResolveArgs {
|
||||
base,
|
||||
is_base_cjs,
|
||||
specifiers,
|
||||
},
|
||||
)
|
||||
op_resolve_inner(state, ResolveArgs { base, specifiers })
|
||||
}
|
||||
|
||||
struct TscRequestArray {
|
||||
|
@ -4692,10 +4678,7 @@ fn op_script_names(state: &mut OpState) -> ScriptNames {
|
|||
let (types, _) = documents.resolve_dependency(
|
||||
types,
|
||||
specifier,
|
||||
state
|
||||
.state_snapshot
|
||||
.is_cjs_resolver
|
||||
.get_doc_module_kind(doc),
|
||||
doc.resolution_mode(),
|
||||
doc.file_referrer(),
|
||||
)?;
|
||||
let types_doc = documents.get_or_load(&types, doc.file_referrer())?;
|
||||
|
@ -5579,7 +5562,6 @@ mod tests {
|
|||
documents: Arc::new(documents),
|
||||
assets: Default::default(),
|
||||
config: Arc::new(config),
|
||||
is_cjs_resolver: Default::default(),
|
||||
resolver,
|
||||
});
|
||||
let performance = Arc::new(Performance::default());
|
||||
|
@ -6430,8 +6412,7 @@ mod tests {
|
|||
&mut state,
|
||||
ResolveArgs {
|
||||
base: temp_dir.url().join("a.ts").unwrap().to_string(),
|
||||
is_base_cjs: false,
|
||||
specifiers: vec!["./b.ts".to_string()],
|
||||
specifiers: vec![(false, "./b.ts".to_string())],
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
|
|
@ -448,7 +448,7 @@ fn resolve_flags_and_init(
|
|||
};
|
||||
|
||||
if let Some(otel_config) = flags.otel_config() {
|
||||
deno_runtime::ops::otel::init(otel_config)?;
|
||||
deno_telemetry::init(otel_config)?;
|
||||
}
|
||||
util::logger::init(flags.log_level);
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ fn main() {
|
|||
match standalone {
|
||||
Ok(Some(data)) => {
|
||||
if let Some(otel_config) = data.metadata.otel_config.clone() {
|
||||
deno_runtime::ops::otel::init(otel_config)?;
|
||||
deno_telemetry::init(otel_config)?;
|
||||
}
|
||||
util::logger::init(data.metadata.log_level);
|
||||
load_env_vars(&data.metadata.env_vars_from_env_file);
|
||||
|
|
|
@ -7,6 +7,8 @@ use std::path::PathBuf;
|
|||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
use std::str;
|
||||
use std::sync::atomic::AtomicU16;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::args::jsr_url;
|
||||
|
@ -49,6 +51,7 @@ use deno_core::error::generic_error;
|
|||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::future::FutureExt;
|
||||
use deno_core::futures::Future;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::resolve_url;
|
||||
use deno_core::ModuleCodeString;
|
||||
use deno_core::ModuleLoader;
|
||||
|
@ -57,9 +60,7 @@ use deno_core::ModuleSourceCode;
|
|||
use deno_core::ModuleSpecifier;
|
||||
use deno_core::ModuleType;
|
||||
use deno_core::RequestedModuleType;
|
||||
use deno_core::ResolutionKind;
|
||||
use deno_core::SourceCodeCacheInfo;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::GraphKind;
|
||||
use deno_graph::JsModule;
|
||||
use deno_graph::JsonModule;
|
||||
|
@ -76,7 +77,8 @@ use deno_runtime::deno_permissions::PermissionsContainer;
|
|||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use node_resolver::errors::ClosestPkgJsonError;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
use node_resolver::NodeResolutionMode;
|
||||
use node_resolver::NodeResolutionKind;
|
||||
use node_resolver::ResolutionMode;
|
||||
|
||||
pub struct ModuleLoadPreparer {
|
||||
options: Arc<CliOptions>,
|
||||
|
@ -223,6 +225,42 @@ struct SharedCliModuleLoaderState {
|
|||
npm_module_loader: NpmModuleLoader,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
resolver: Arc<CliResolver>,
|
||||
in_flight_loads_tracker: InFlightModuleLoadsTracker,
|
||||
}
|
||||
|
||||
struct InFlightModuleLoadsTracker {
|
||||
loads_number: Arc<AtomicU16>,
|
||||
cleanup_task_timeout: u64,
|
||||
cleanup_task_handle: Arc<Mutex<Option<tokio::task::JoinHandle<()>>>>,
|
||||
}
|
||||
|
||||
impl InFlightModuleLoadsTracker {
|
||||
pub fn increase(&self) {
|
||||
self.loads_number.fetch_add(1, Ordering::Relaxed);
|
||||
if let Some(task) = self.cleanup_task_handle.lock().take() {
|
||||
task.abort();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrease(&self, parsed_source_cache: &Arc<ParsedSourceCache>) {
|
||||
let prev = self.loads_number.fetch_sub(1, Ordering::Relaxed);
|
||||
if prev == 1 {
|
||||
let parsed_source_cache = parsed_source_cache.clone();
|
||||
let timeout = self.cleanup_task_timeout;
|
||||
let task_handle = tokio::spawn(async move {
|
||||
// We use a timeout here, which is defined to 10s,
|
||||
// so that in situations when dynamic imports are loaded after the startup,
|
||||
// we don't need to recompute and analyze multiple modules.
|
||||
tokio::time::sleep(std::time::Duration::from_millis(timeout)).await;
|
||||
parsed_source_cache.free_all();
|
||||
});
|
||||
let maybe_prev_task =
|
||||
self.cleanup_task_handle.lock().replace(task_handle);
|
||||
if let Some(prev_task) = maybe_prev_task {
|
||||
prev_task.abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CliModuleLoaderFactory {
|
||||
|
@ -273,6 +311,11 @@ impl CliModuleLoaderFactory {
|
|||
npm_module_loader,
|
||||
parsed_source_cache,
|
||||
resolver,
|
||||
in_flight_loads_tracker: InFlightModuleLoadsTracker {
|
||||
loads_number: Arc::new(AtomicU16::new(0)),
|
||||
cleanup_task_timeout: 10_000,
|
||||
cleanup_task_handle: Arc::new(Mutex::new(None)),
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -498,13 +541,11 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
}
|
||||
Resolution::None => Cow::Owned(self.shared.resolver.resolve(
|
||||
raw_specifier,
|
||||
&deno_graph::Range {
|
||||
specifier: referrer.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
},
|
||||
self.shared.cjs_tracker.get_referrer_kind(referrer),
|
||||
ResolutionMode::Execution,
|
||||
referrer,
|
||||
deno_graph::Position::zeroed(),
|
||||
// if we're here, that means it's resolving a dynamic import
|
||||
ResolutionMode::Import,
|
||||
NodeResolutionKind::Execution,
|
||||
)?),
|
||||
};
|
||||
|
||||
|
@ -517,8 +558,8 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
.resolve_req_reference(
|
||||
&reference,
|
||||
referrer,
|
||||
self.shared.cjs_tracker.get_referrer_kind(referrer),
|
||||
NodeResolutionMode::Execution,
|
||||
ResolutionMode::Import,
|
||||
NodeResolutionKind::Execution,
|
||||
)
|
||||
.map_err(AnyError::from);
|
||||
}
|
||||
|
@ -539,8 +580,8 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
&package_folder,
|
||||
module.nv_reference.sub_path(),
|
||||
Some(referrer),
|
||||
self.shared.cjs_tracker.get_referrer_kind(referrer),
|
||||
NodeResolutionMode::Execution,
|
||||
ResolutionMode::Import,
|
||||
NodeResolutionKind::Execution,
|
||||
)
|
||||
.with_context(|| {
|
||||
format!("Could not resolve '{}'.", module.nv_reference)
|
||||
|
@ -806,7 +847,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
|
|||
&self,
|
||||
specifier: &str,
|
||||
referrer: &str,
|
||||
_kind: ResolutionKind,
|
||||
_kind: deno_core::ResolutionKind,
|
||||
) -> Result<ModuleSpecifier, AnyError> {
|
||||
fn ensure_not_jsr_non_jsr_remote_import(
|
||||
specifier: &ModuleSpecifier,
|
||||
|
@ -870,6 +911,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
|
|||
_maybe_referrer: Option<String>,
|
||||
is_dynamic: bool,
|
||||
) -> Pin<Box<dyn Future<Output = Result<(), AnyError>>>> {
|
||||
self.0.shared.in_flight_loads_tracker.increase();
|
||||
if self.0.shared.in_npm_pkg_checker.in_npm_package(specifier) {
|
||||
return Box::pin(deno_core::futures::future::ready(Ok(())));
|
||||
}
|
||||
|
@ -924,6 +966,14 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
|
|||
.boxed_local()
|
||||
}
|
||||
|
||||
fn finish_load(&self) {
|
||||
self
|
||||
.0
|
||||
.shared
|
||||
.in_flight_loads_tracker
|
||||
.decrease(&self.0.shared.parsed_source_cache);
|
||||
}
|
||||
|
||||
fn code_cache_ready(
|
||||
&self,
|
||||
specifier: ModuleSpecifier,
|
||||
|
@ -1063,7 +1113,10 @@ impl<TGraphContainer: ModuleGraphContainer> NodeRequireLoader
|
|||
self.npm_resolver.ensure_read_permission(permissions, path)
|
||||
}
|
||||
|
||||
fn load_text_file_lossy(&self, path: &Path) -> Result<String, AnyError> {
|
||||
fn load_text_file_lossy(
|
||||
&self,
|
||||
path: &Path,
|
||||
) -> Result<Cow<'static, str>, AnyError> {
|
||||
// todo(dsherret): use the preloaded module from the graph if available?
|
||||
let media_type = MediaType::from_path(path);
|
||||
let text = self.fs.read_text_file_lossy_sync(path, None)?;
|
||||
|
@ -1078,15 +1131,18 @@ impl<TGraphContainer: ModuleGraphContainer> NodeRequireLoader
|
|||
.into(),
|
||||
);
|
||||
}
|
||||
self.emitter.emit_parsed_source_sync(
|
||||
&specifier,
|
||||
media_type,
|
||||
// this is probably not super accurate due to require esm, but probably ok.
|
||||
// If we find this causes a lot of churn in the emit cache then we should
|
||||
// investigate how we can make this better
|
||||
ModuleKind::Cjs,
|
||||
&text.into(),
|
||||
)
|
||||
self
|
||||
.emitter
|
||||
.emit_parsed_source_sync(
|
||||
&specifier,
|
||||
media_type,
|
||||
// this is probably not super accurate due to require esm, but probably ok.
|
||||
// If we find this causes a lot of churn in the emit cache then we should
|
||||
// investigate how we can make this better
|
||||
ModuleKind::Cjs,
|
||||
&text.into(),
|
||||
)
|
||||
.map(Cow::Owned)
|
||||
} else {
|
||||
Ok(text)
|
||||
}
|
||||
|
@ -1100,3 +1156,44 @@ impl<TGraphContainer: ModuleGraphContainer> NodeRequireLoader
|
|||
self.cjs_tracker.is_maybe_cjs(specifier, media_type)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use deno_graph::ParsedSourceStore;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_inflight_module_loads_tracker() {
|
||||
let tracker = InFlightModuleLoadsTracker {
|
||||
loads_number: Default::default(),
|
||||
cleanup_task_timeout: 10,
|
||||
cleanup_task_handle: Default::default(),
|
||||
};
|
||||
|
||||
let specifier = ModuleSpecifier::parse("file:///a.js").unwrap();
|
||||
let source = "const a = 'hello';";
|
||||
let parsed_source_cache = Arc::new(ParsedSourceCache::default());
|
||||
let parsed_source = parsed_source_cache
|
||||
.remove_or_parse_module(&specifier, source.into(), MediaType::JavaScript)
|
||||
.unwrap();
|
||||
parsed_source_cache.set_parsed_source(specifier, parsed_source);
|
||||
|
||||
assert_eq!(parsed_source_cache.len(), 1);
|
||||
assert!(tracker.cleanup_task_handle.lock().is_none());
|
||||
tracker.increase();
|
||||
tracker.increase();
|
||||
assert!(tracker.cleanup_task_handle.lock().is_none());
|
||||
tracker.decrease(&parsed_source_cache);
|
||||
assert!(tracker.cleanup_task_handle.lock().is_none());
|
||||
tracker.decrease(&parsed_source_cache);
|
||||
assert!(tracker.cleanup_task_handle.lock().is_some());
|
||||
assert_eq!(parsed_source_cache.len(), 1);
|
||||
tracker.increase();
|
||||
assert!(tracker.cleanup_task_handle.lock().is_none());
|
||||
assert_eq!(parsed_source_cache.len(), 1);
|
||||
tracker.decrease(&parsed_source_cache);
|
||||
// Rather long timeout, but to make sure CI is not flaking on it.
|
||||
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
|
||||
assert_eq!(parsed_source_cache.len(), 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,7 +160,7 @@ impl CjsCodeAnalyzer for CliCjsCodeAnalyzer {
|
|||
if let Ok(source_from_file) =
|
||||
self.fs.read_text_file_lossy_async(path, None).await
|
||||
{
|
||||
Cow::Owned(source_from_file)
|
||||
source_from_file
|
||||
} else {
|
||||
return Ok(ExtNodeCjsAnalysis::Cjs(CjsAnalysisExports {
|
||||
exports: vec![],
|
||||
|
|
|
@ -5,8 +5,6 @@ use std::path::Path;
|
|||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use cache::RegistryInfoDownloader;
|
||||
use cache::TarballCache;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_cache_dir::npm::NpmCacheDir;
|
||||
use deno_core::anyhow::Context;
|
||||
|
@ -42,23 +40,22 @@ use crate::args::NpmProcessState;
|
|||
use crate::args::NpmProcessStateKind;
|
||||
use crate::args::PackageJsonDepValueParseWithLocationError;
|
||||
use crate::cache::FastInsecureHasher;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
use crate::util::sync::AtomicFlag;
|
||||
|
||||
use self::cache::NpmCache;
|
||||
use self::registry::CliNpmRegistryApi;
|
||||
use self::resolution::NpmResolution;
|
||||
use self::resolvers::create_npm_fs_resolver;
|
||||
use self::resolvers::NpmPackageFsResolver;
|
||||
|
||||
use super::CliNpmCache;
|
||||
use super::CliNpmCacheEnv;
|
||||
use super::CliNpmRegistryInfoProvider;
|
||||
use super::CliNpmResolver;
|
||||
use super::CliNpmTarballCache;
|
||||
use super::InnerCliNpmResolverRef;
|
||||
use super::ResolvePkgFolderFromDenoReqError;
|
||||
|
||||
pub mod cache;
|
||||
mod registry;
|
||||
mod resolution;
|
||||
mod resolvers;
|
||||
|
||||
|
@ -85,8 +82,9 @@ pub struct CliManagedNpmResolverCreateOptions {
|
|||
pub async fn create_managed_npm_resolver_for_lsp(
|
||||
options: CliManagedNpmResolverCreateOptions,
|
||||
) -> Arc<dyn CliNpmResolver> {
|
||||
let npm_cache = create_cache(&options);
|
||||
let npm_api = create_api(&options, npm_cache.clone());
|
||||
let cache_env = create_cache_env(&options);
|
||||
let npm_cache = create_cache(cache_env.clone(), &options);
|
||||
let npm_api = create_api(npm_cache.clone(), cache_env.clone(), &options);
|
||||
// spawn due to the lsp's `Send` requirement
|
||||
deno_core::unsync::spawn(async move {
|
||||
let snapshot = match resolve_snapshot(&npm_api, options.snapshot).await {
|
||||
|
@ -97,8 +95,8 @@ pub async fn create_managed_npm_resolver_for_lsp(
|
|||
}
|
||||
};
|
||||
create_inner(
|
||||
cache_env,
|
||||
options.fs,
|
||||
options.http_client_provider,
|
||||
options.maybe_lockfile,
|
||||
npm_api,
|
||||
npm_cache,
|
||||
|
@ -118,14 +116,15 @@ pub async fn create_managed_npm_resolver_for_lsp(
|
|||
pub async fn create_managed_npm_resolver(
|
||||
options: CliManagedNpmResolverCreateOptions,
|
||||
) -> Result<Arc<dyn CliNpmResolver>, AnyError> {
|
||||
let npm_cache = create_cache(&options);
|
||||
let npm_api = create_api(&options, npm_cache.clone());
|
||||
let snapshot = resolve_snapshot(&npm_api, options.snapshot).await?;
|
||||
let npm_cache_env = create_cache_env(&options);
|
||||
let npm_cache = create_cache(npm_cache_env.clone(), &options);
|
||||
let api = create_api(npm_cache.clone(), npm_cache_env.clone(), &options);
|
||||
let snapshot = resolve_snapshot(&api, options.snapshot).await?;
|
||||
Ok(create_inner(
|
||||
npm_cache_env,
|
||||
options.fs,
|
||||
options.http_client_provider,
|
||||
options.maybe_lockfile,
|
||||
npm_api,
|
||||
api,
|
||||
npm_cache,
|
||||
options.npmrc,
|
||||
options.npm_install_deps_provider,
|
||||
|
@ -139,11 +138,11 @@ pub async fn create_managed_npm_resolver(
|
|||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn create_inner(
|
||||
env: Arc<CliNpmCacheEnv>,
|
||||
fs: Arc<dyn deno_runtime::deno_fs::FileSystem>,
|
||||
http_client_provider: Arc<HttpClientProvider>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
npm_api: Arc<CliNpmRegistryApi>,
|
||||
npm_cache: Arc<NpmCache>,
|
||||
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
npm_cache: Arc<CliNpmCache>,
|
||||
npm_rc: Arc<ResolvedNpmRc>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
text_only_progress_bar: crate::util::progress_bar::ProgressBar,
|
||||
|
@ -153,16 +152,14 @@ fn create_inner(
|
|||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
) -> Arc<dyn CliNpmResolver> {
|
||||
let resolution = Arc::new(NpmResolution::from_serialized(
|
||||
npm_api.clone(),
|
||||
registry_info_provider.clone(),
|
||||
snapshot,
|
||||
maybe_lockfile.clone(),
|
||||
));
|
||||
let tarball_cache = Arc::new(TarballCache::new(
|
||||
let tarball_cache = Arc::new(CliNpmTarballCache::new(
|
||||
npm_cache.clone(),
|
||||
fs.clone(),
|
||||
http_client_provider.clone(),
|
||||
env,
|
||||
npm_rc.clone(),
|
||||
text_only_progress_bar.clone(),
|
||||
));
|
||||
let fs_resolver = create_npm_fs_resolver(
|
||||
fs.clone(),
|
||||
|
@ -179,7 +176,7 @@ fn create_inner(
|
|||
fs,
|
||||
fs_resolver,
|
||||
maybe_lockfile,
|
||||
npm_api,
|
||||
registry_info_provider,
|
||||
npm_cache,
|
||||
npm_install_deps_provider,
|
||||
resolution,
|
||||
|
@ -190,41 +187,55 @@ fn create_inner(
|
|||
))
|
||||
}
|
||||
|
||||
fn create_cache(options: &CliManagedNpmResolverCreateOptions) -> Arc<NpmCache> {
|
||||
Arc::new(NpmCache::new(
|
||||
fn create_cache_env(
|
||||
options: &CliManagedNpmResolverCreateOptions,
|
||||
) -> Arc<CliNpmCacheEnv> {
|
||||
Arc::new(CliNpmCacheEnv::new(
|
||||
options.fs.clone(),
|
||||
options.http_client_provider.clone(),
|
||||
options.text_only_progress_bar.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn create_cache(
|
||||
env: Arc<CliNpmCacheEnv>,
|
||||
options: &CliManagedNpmResolverCreateOptions,
|
||||
) -> Arc<CliNpmCache> {
|
||||
Arc::new(CliNpmCache::new(
|
||||
options.npm_cache_dir.clone(),
|
||||
options.cache_setting.clone(),
|
||||
options.cache_setting.as_npm_cache_setting(),
|
||||
env,
|
||||
options.npmrc.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
fn create_api(
|
||||
cache: Arc<CliNpmCache>,
|
||||
env: Arc<CliNpmCacheEnv>,
|
||||
options: &CliManagedNpmResolverCreateOptions,
|
||||
npm_cache: Arc<NpmCache>,
|
||||
) -> Arc<CliNpmRegistryApi> {
|
||||
Arc::new(CliNpmRegistryApi::new(
|
||||
npm_cache.clone(),
|
||||
Arc::new(RegistryInfoDownloader::new(
|
||||
npm_cache,
|
||||
options.http_client_provider.clone(),
|
||||
options.npmrc.clone(),
|
||||
options.text_only_progress_bar.clone(),
|
||||
)),
|
||||
) -> Arc<CliNpmRegistryInfoProvider> {
|
||||
Arc::new(CliNpmRegistryInfoProvider::new(
|
||||
cache,
|
||||
env,
|
||||
options.npmrc.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn resolve_snapshot(
|
||||
api: &CliNpmRegistryApi,
|
||||
registry_info_provider: &Arc<CliNpmRegistryInfoProvider>,
|
||||
snapshot: CliNpmResolverManagedSnapshotOption,
|
||||
) -> Result<Option<ValidSerializedNpmResolutionSnapshot>, AnyError> {
|
||||
match snapshot {
|
||||
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(lockfile) => {
|
||||
if !lockfile.overwrite() {
|
||||
let snapshot = snapshot_from_lockfile(lockfile.clone(), api)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("failed reading lockfile '{}'", lockfile.filename.display())
|
||||
})?;
|
||||
let snapshot = snapshot_from_lockfile(
|
||||
lockfile.clone(),
|
||||
®istry_info_provider.as_npm_registry_api(),
|
||||
)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("failed reading lockfile '{}'", lockfile.filename.display())
|
||||
})?;
|
||||
Ok(Some(snapshot))
|
||||
} else {
|
||||
Ok(None)
|
||||
|
@ -291,11 +302,11 @@ pub struct ManagedCliNpmResolver {
|
|||
fs: Arc<dyn FileSystem>,
|
||||
fs_resolver: Arc<dyn NpmPackageFsResolver>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
npm_api: Arc<CliNpmRegistryApi>,
|
||||
npm_cache: Arc<NpmCache>,
|
||||
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
npm_cache: Arc<CliNpmCache>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
tarball_cache: Arc<TarballCache>,
|
||||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
text_only_progress_bar: ProgressBar,
|
||||
npm_system_info: NpmSystemInfo,
|
||||
top_level_install_flag: AtomicFlag,
|
||||
|
@ -316,11 +327,11 @@ impl ManagedCliNpmResolver {
|
|||
fs: Arc<dyn FileSystem>,
|
||||
fs_resolver: Arc<dyn NpmPackageFsResolver>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
npm_api: Arc<CliNpmRegistryApi>,
|
||||
npm_cache: Arc<NpmCache>,
|
||||
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
npm_cache: Arc<CliNpmCache>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
tarball_cache: Arc<TarballCache>,
|
||||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
text_only_progress_bar: ProgressBar,
|
||||
npm_system_info: NpmSystemInfo,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
|
@ -329,7 +340,7 @@ impl ManagedCliNpmResolver {
|
|||
fs,
|
||||
fs_resolver,
|
||||
maybe_lockfile,
|
||||
npm_api,
|
||||
registry_info_provider,
|
||||
npm_cache,
|
||||
npm_install_deps_provider,
|
||||
text_only_progress_bar,
|
||||
|
@ -575,7 +586,7 @@ impl ManagedCliNpmResolver {
|
|||
) -> Result<Arc<NpmPackageInfo>, AnyError> {
|
||||
// this will internally cache the package information
|
||||
self
|
||||
.npm_api
|
||||
.registry_info_provider
|
||||
.package_info(package_name)
|
||||
.await
|
||||
.map_err(|err| err.into())
|
||||
|
@ -671,7 +682,7 @@ impl CliNpmResolver for ManagedCliNpmResolver {
|
|||
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver> {
|
||||
// create a new snapshotted npm resolution and resolver
|
||||
let npm_resolution = Arc::new(NpmResolution::new(
|
||||
self.npm_api.clone(),
|
||||
self.registry_info_provider.clone(),
|
||||
self.resolution.snapshot(),
|
||||
self.maybe_lockfile.clone(),
|
||||
));
|
||||
|
@ -690,7 +701,7 @@ impl CliNpmResolver for ManagedCliNpmResolver {
|
|||
self.lifecycle_scripts.clone(),
|
||||
),
|
||||
self.maybe_lockfile.clone(),
|
||||
self.npm_api.clone(),
|
||||
self.registry_info_provider.clone(),
|
||||
self.npm_cache.clone(),
|
||||
self.npm_install_deps_provider.clone(),
|
||||
npm_resolution,
|
||||
|
|
|
@ -1,200 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use deno_core::anyhow::anyhow;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::future::BoxFuture;
|
||||
use deno_core::futures::future::Shared;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_npm::registry::NpmPackageInfo;
|
||||
use deno_npm::registry::NpmRegistryApi;
|
||||
use deno_npm::registry::NpmRegistryPackageInfoLoadError;
|
||||
|
||||
use crate::args::CacheSetting;
|
||||
use crate::util::sync::AtomicFlag;
|
||||
|
||||
use super::cache::NpmCache;
|
||||
use super::cache::RegistryInfoDownloader;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CliNpmRegistryApi(Option<Arc<CliNpmRegistryApiInner>>);
|
||||
|
||||
impl CliNpmRegistryApi {
|
||||
pub fn new(
|
||||
cache: Arc<NpmCache>,
|
||||
registry_info_downloader: Arc<RegistryInfoDownloader>,
|
||||
) -> Self {
|
||||
Self(Some(Arc::new(CliNpmRegistryApiInner {
|
||||
cache,
|
||||
force_reload_flag: Default::default(),
|
||||
mem_cache: Default::default(),
|
||||
previously_reloaded_packages: Default::default(),
|
||||
registry_info_downloader,
|
||||
})))
|
||||
}
|
||||
|
||||
/// Clears the internal memory cache.
|
||||
pub fn clear_memory_cache(&self) {
|
||||
self.inner().clear_memory_cache();
|
||||
}
|
||||
|
||||
fn inner(&self) -> &Arc<CliNpmRegistryApiInner> {
|
||||
// this panicking indicates a bug in the code where this
|
||||
// wasn't initialized
|
||||
self.0.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl NpmRegistryApi for CliNpmRegistryApi {
|
||||
async fn package_info(
|
||||
&self,
|
||||
name: &str,
|
||||
) -> Result<Arc<NpmPackageInfo>, NpmRegistryPackageInfoLoadError> {
|
||||
match self.inner().maybe_package_info(name).await {
|
||||
Ok(Some(info)) => Ok(info),
|
||||
Ok(None) => Err(NpmRegistryPackageInfoLoadError::PackageNotExists {
|
||||
package_name: name.to_string(),
|
||||
}),
|
||||
Err(err) => {
|
||||
Err(NpmRegistryPackageInfoLoadError::LoadError(Arc::new(err)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_force_reload(&self) -> bool {
|
||||
self.inner().mark_force_reload()
|
||||
}
|
||||
}
|
||||
|
||||
type CacheItemPendingResult =
|
||||
Result<Option<Arc<NpmPackageInfo>>, Arc<AnyError>>;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum CacheItem {
|
||||
Pending(Shared<BoxFuture<'static, CacheItemPendingResult>>),
|
||||
Resolved(Option<Arc<NpmPackageInfo>>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CliNpmRegistryApiInner {
|
||||
cache: Arc<NpmCache>,
|
||||
force_reload_flag: AtomicFlag,
|
||||
mem_cache: Mutex<HashMap<String, CacheItem>>,
|
||||
previously_reloaded_packages: Mutex<HashSet<String>>,
|
||||
registry_info_downloader: Arc<RegistryInfoDownloader>,
|
||||
}
|
||||
|
||||
impl CliNpmRegistryApiInner {
|
||||
pub async fn maybe_package_info(
|
||||
self: &Arc<Self>,
|
||||
name: &str,
|
||||
) -> Result<Option<Arc<NpmPackageInfo>>, AnyError> {
|
||||
let (created, future) = {
|
||||
let mut mem_cache = self.mem_cache.lock();
|
||||
match mem_cache.get(name) {
|
||||
Some(CacheItem::Resolved(maybe_info)) => {
|
||||
return Ok(maybe_info.clone());
|
||||
}
|
||||
Some(CacheItem::Pending(future)) => (false, future.clone()),
|
||||
None => {
|
||||
let future = {
|
||||
let api = self.clone();
|
||||
let name = name.to_string();
|
||||
async move {
|
||||
if (api.cache.cache_setting().should_use_for_npm_package(&name) && !api.force_reload_flag.is_raised())
|
||||
// if this has been previously reloaded, then try loading from the
|
||||
// file system cache
|
||||
|| !api.previously_reloaded_packages.lock().insert(name.to_string())
|
||||
{
|
||||
// attempt to load from the file cache
|
||||
if let Some(info) = api.load_file_cached_package_info(&name).await {
|
||||
let result = Some(Arc::new(info));
|
||||
return Ok(result);
|
||||
}
|
||||
}
|
||||
api.registry_info_downloader
|
||||
.load_package_info(&name)
|
||||
.await
|
||||
.map_err(Arc::new)
|
||||
}
|
||||
.boxed()
|
||||
.shared()
|
||||
};
|
||||
mem_cache
|
||||
.insert(name.to_string(), CacheItem::Pending(future.clone()));
|
||||
(true, future)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if created {
|
||||
match future.await {
|
||||
Ok(maybe_info) => {
|
||||
// replace the cache item to say it's resolved now
|
||||
self
|
||||
.mem_cache
|
||||
.lock()
|
||||
.insert(name.to_string(), CacheItem::Resolved(maybe_info.clone()));
|
||||
Ok(maybe_info)
|
||||
}
|
||||
Err(err) => {
|
||||
// purge the item from the cache so it loads next time
|
||||
self.mem_cache.lock().remove(name);
|
||||
Err(anyhow!("{:#}", err))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Ok(future.await.map_err(|err| anyhow!("{:#}", err))?)
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_force_reload(&self) -> bool {
|
||||
// never force reload the registry information if reloading
|
||||
// is disabled or if we're already reloading
|
||||
if matches!(
|
||||
self.cache.cache_setting(),
|
||||
CacheSetting::Only | CacheSetting::ReloadAll
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if self.force_reload_flag.raise() {
|
||||
self.clear_memory_cache();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
async fn load_file_cached_package_info(
|
||||
&self,
|
||||
name: &str,
|
||||
) -> Option<NpmPackageInfo> {
|
||||
let result = deno_core::unsync::spawn_blocking({
|
||||
let cache = self.cache.clone();
|
||||
let name = name.to_string();
|
||||
move || cache.load_package_info(&name)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
match result {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
if cfg!(debug_assertions) {
|
||||
panic!("error loading cached npm package info for {name}: {err:#}");
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_memory_cache(&self) {
|
||||
self.mem_cache.lock().clear();
|
||||
}
|
||||
}
|
|
@ -8,11 +8,10 @@ use deno_core::error::AnyError;
|
|||
use deno_lockfile::NpmPackageDependencyLockfileInfo;
|
||||
use deno_lockfile::NpmPackageLockfileInfo;
|
||||
use deno_npm::registry::NpmRegistryApi;
|
||||
use deno_npm::resolution::AddPkgReqsOptions;
|
||||
use deno_npm::resolution::NpmPackagesPartitioned;
|
||||
use deno_npm::resolution::NpmResolutionError;
|
||||
use deno_npm::resolution::NpmResolutionSnapshot;
|
||||
use deno_npm::resolution::NpmResolutionSnapshotPendingResolver;
|
||||
use deno_npm::resolution::NpmResolutionSnapshotPendingResolverOptions;
|
||||
use deno_npm::resolution::PackageCacheFolderIdNotFoundError;
|
||||
use deno_npm::resolution::PackageNotFoundFromReferrerError;
|
||||
use deno_npm::resolution::PackageNvNotFoundError;
|
||||
|
@ -28,10 +27,9 @@ use deno_semver::package::PackageReq;
|
|||
use deno_semver::VersionReq;
|
||||
|
||||
use crate::args::CliLockfile;
|
||||
use crate::npm::CliNpmRegistryInfoProvider;
|
||||
use crate::util::sync::SyncReadAsyncWriteLock;
|
||||
|
||||
use super::CliNpmRegistryApi;
|
||||
|
||||
pub struct AddPkgReqsResult {
|
||||
/// Results from adding the individual packages.
|
||||
///
|
||||
|
@ -48,7 +46,7 @@ pub struct AddPkgReqsResult {
|
|||
///
|
||||
/// This does not interact with the file system.
|
||||
pub struct NpmResolution {
|
||||
api: Arc<CliNpmRegistryApi>,
|
||||
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
snapshot: SyncReadAsyncWriteLock<NpmResolutionSnapshot>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
}
|
||||
|
@ -64,22 +62,22 @@ impl std::fmt::Debug for NpmResolution {
|
|||
|
||||
impl NpmResolution {
|
||||
pub fn from_serialized(
|
||||
api: Arc<CliNpmRegistryApi>,
|
||||
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
initial_snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
) -> Self {
|
||||
let snapshot =
|
||||
NpmResolutionSnapshot::new(initial_snapshot.unwrap_or_default());
|
||||
Self::new(api, snapshot, maybe_lockfile)
|
||||
Self::new(registry_info_provider, snapshot, maybe_lockfile)
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
api: Arc<CliNpmRegistryApi>,
|
||||
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
initial_snapshot: NpmResolutionSnapshot,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
api,
|
||||
registry_info_provider,
|
||||
snapshot: SyncReadAsyncWriteLock::new(initial_snapshot),
|
||||
maybe_lockfile,
|
||||
}
|
||||
|
@ -92,7 +90,7 @@ impl NpmResolution {
|
|||
// only allow one thread in here at a time
|
||||
let snapshot_lock = self.snapshot.acquire().await;
|
||||
let result = add_package_reqs_to_snapshot(
|
||||
&self.api,
|
||||
&self.registry_info_provider,
|
||||
package_reqs,
|
||||
self.maybe_lockfile.clone(),
|
||||
|| snapshot_lock.read().clone(),
|
||||
|
@ -120,7 +118,7 @@ impl NpmResolution {
|
|||
|
||||
let reqs_set = package_reqs.iter().collect::<HashSet<_>>();
|
||||
let snapshot = add_package_reqs_to_snapshot(
|
||||
&self.api,
|
||||
&self.registry_info_provider,
|
||||
package_reqs,
|
||||
self.maybe_lockfile.clone(),
|
||||
|| {
|
||||
|
@ -260,7 +258,7 @@ impl NpmResolution {
|
|||
}
|
||||
|
||||
async fn add_package_reqs_to_snapshot(
|
||||
api: &CliNpmRegistryApi,
|
||||
registry_info_provider: &Arc<CliNpmRegistryInfoProvider>,
|
||||
package_reqs: &[PackageReq],
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
get_new_snapshot: impl Fn() -> NpmResolutionSnapshot,
|
||||
|
@ -283,23 +281,28 @@ async fn add_package_reqs_to_snapshot(
|
|||
/* this string is used in tests */
|
||||
"Running npm resolution."
|
||||
);
|
||||
let pending_resolver = get_npm_pending_resolver(api);
|
||||
let result = pending_resolver.add_pkg_reqs(snapshot, package_reqs).await;
|
||||
api.clear_memory_cache();
|
||||
let npm_registry_api = registry_info_provider.as_npm_registry_api();
|
||||
let result = snapshot
|
||||
.add_pkg_reqs(&npm_registry_api, get_add_pkg_reqs_options(package_reqs))
|
||||
.await;
|
||||
let result = match &result.dep_graph_result {
|
||||
Err(NpmResolutionError::Resolution(err)) if api.mark_force_reload() => {
|
||||
Err(NpmResolutionError::Resolution(err))
|
||||
if npm_registry_api.mark_force_reload() =>
|
||||
{
|
||||
log::debug!("{err:#}");
|
||||
log::debug!("npm resolution failed. Trying again...");
|
||||
|
||||
// try again
|
||||
// try again with forced reloading
|
||||
let snapshot = get_new_snapshot();
|
||||
let result = pending_resolver.add_pkg_reqs(snapshot, package_reqs).await;
|
||||
api.clear_memory_cache();
|
||||
result
|
||||
snapshot
|
||||
.add_pkg_reqs(&npm_registry_api, get_add_pkg_reqs_options(package_reqs))
|
||||
.await
|
||||
}
|
||||
_ => result,
|
||||
};
|
||||
|
||||
registry_info_provider.clear_memory_cache();
|
||||
|
||||
if let Ok(snapshot) = &result.dep_graph_result {
|
||||
if let Some(lockfile) = maybe_lockfile {
|
||||
populate_lockfile_from_snapshot(&lockfile, snapshot);
|
||||
|
@ -309,19 +312,15 @@ async fn add_package_reqs_to_snapshot(
|
|||
result
|
||||
}
|
||||
|
||||
fn get_npm_pending_resolver(
|
||||
api: &CliNpmRegistryApi,
|
||||
) -> NpmResolutionSnapshotPendingResolver<CliNpmRegistryApi> {
|
||||
NpmResolutionSnapshotPendingResolver::new(
|
||||
NpmResolutionSnapshotPendingResolverOptions {
|
||||
api,
|
||||
// WARNING: When bumping this version, check if anything needs to be
|
||||
// updated in the `setNodeOnlyGlobalNames` call in 99_main_compiler.js
|
||||
types_node_version_req: Some(
|
||||
VersionReq::parse_from_npm("22.0.0 - 22.5.4").unwrap(),
|
||||
),
|
||||
},
|
||||
)
|
||||
fn get_add_pkg_reqs_options(package_reqs: &[PackageReq]) -> AddPkgReqsOptions {
|
||||
AddPkgReqsOptions {
|
||||
package_reqs,
|
||||
// WARNING: When bumping this version, check if anything needs to be
|
||||
// updated in the `setNodeOnlyGlobalNames` call in 99_main_compiler.js
|
||||
types_node_version_req: Some(
|
||||
VersionReq::parse_from_npm("22.0.0 - 22.5.4").unwrap(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn populate_lockfile_from_snapshot(
|
||||
|
|
|
@ -24,7 +24,7 @@ use deno_runtime::deno_fs::FileSystem;
|
|||
use deno_runtime::deno_node::NodePermissions;
|
||||
use node_resolver::errors::PackageFolderResolveError;
|
||||
|
||||
use crate::npm::managed::cache::TarballCache;
|
||||
use crate::npm::CliNpmTarballCache;
|
||||
|
||||
/// Part of the resolution that interacts with the file system.
|
||||
#[async_trait(?Send)]
|
||||
|
@ -140,7 +140,7 @@ impl RegistryReadPermissionChecker {
|
|||
/// Caches all the packages in parallel.
|
||||
pub async fn cache_packages(
|
||||
packages: &[NpmResolutionPackage],
|
||||
tarball_cache: &Arc<TarballCache>,
|
||||
tarball_cache: &Arc<CliNpmTarballCache>,
|
||||
) -> Result<(), AnyError> {
|
||||
let mut futures_unordered = futures::stream::FuturesUnordered::new();
|
||||
for package in packages {
|
||||
|
|
|
@ -9,6 +9,7 @@ use deno_npm::resolution::NpmResolutionSnapshot;
|
|||
use deno_runtime::deno_io::FromRawIoHandle;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::Version;
|
||||
use deno_task_shell::KillSignal;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashSet;
|
||||
use std::rc::Rc;
|
||||
|
@ -155,6 +156,29 @@ impl<'a> LifecycleScripts<'a> {
|
|||
packages: &[NpmResolutionPackage],
|
||||
root_node_modules_dir_path: &Path,
|
||||
progress_bar: &ProgressBar,
|
||||
) -> Result<(), AnyError> {
|
||||
let kill_signal = KillSignal::default();
|
||||
let _drop_signal = kill_signal.clone().drop_guard();
|
||||
// we don't run with signals forwarded because once signals
|
||||
// are setup then they're process wide.
|
||||
self
|
||||
.finish_with_cancellation(
|
||||
snapshot,
|
||||
packages,
|
||||
root_node_modules_dir_path,
|
||||
progress_bar,
|
||||
kill_signal,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn finish_with_cancellation(
|
||||
self,
|
||||
snapshot: &NpmResolutionSnapshot,
|
||||
packages: &[NpmResolutionPackage],
|
||||
root_node_modules_dir_path: &Path,
|
||||
progress_bar: &ProgressBar,
|
||||
kill_signal: KillSignal,
|
||||
) -> Result<(), AnyError> {
|
||||
self.warn_not_run_scripts()?;
|
||||
let get_package_path =
|
||||
|
@ -182,6 +206,12 @@ impl<'a> LifecycleScripts<'a> {
|
|||
);
|
||||
|
||||
let mut env_vars = crate::task_runner::real_env_vars();
|
||||
// so the subprocess can detect that it is running as part of a lifecycle script,
|
||||
// and avoid trying to set up node_modules again
|
||||
env_vars.insert(
|
||||
LIFECYCLE_SCRIPTS_RUNNING_ENV_VAR.to_string(),
|
||||
"1".to_string(),
|
||||
);
|
||||
// we want to pass the current state of npm resolution down to the deno subprocess
|
||||
// (that may be running as part of the script). we do this with an inherited temp file
|
||||
//
|
||||
|
@ -240,6 +270,7 @@ impl<'a> LifecycleScripts<'a> {
|
|||
stderr: TaskStdio::piped(),
|
||||
stdout: TaskStdio::piped(),
|
||||
}),
|
||||
kill_signal: kill_signal.clone(),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
@ -303,6 +334,13 @@ impl<'a> LifecycleScripts<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
const LIFECYCLE_SCRIPTS_RUNNING_ENV_VAR: &str =
|
||||
"DENO_INTERNAL_IS_LIFECYCLE_SCRIPT";
|
||||
|
||||
pub fn is_running_lifecycle_script() -> bool {
|
||||
std::env::var(LIFECYCLE_SCRIPTS_RUNNING_ENV_VAR).is_ok()
|
||||
}
|
||||
|
||||
// take in all (non copy) packages from snapshot,
|
||||
// and resolve the set of available binaries to create
|
||||
// custom commands available to the task runner
|
||||
|
|
|
@ -8,6 +8,8 @@ use std::path::PathBuf;
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::colors;
|
||||
use crate::npm::CliNpmCache;
|
||||
use crate::npm::CliNpmTarballCache;
|
||||
use async_trait::async_trait;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::error::AnyError;
|
||||
|
@ -24,8 +26,6 @@ use node_resolver::errors::ReferrerNotFoundError;
|
|||
use crate::args::LifecycleScriptsConfig;
|
||||
use crate::cache::FastInsecureHasher;
|
||||
|
||||
use super::super::cache::NpmCache;
|
||||
use super::super::cache::TarballCache;
|
||||
use super::super::resolution::NpmResolution;
|
||||
use super::common::cache_packages;
|
||||
use super::common::lifecycle_scripts::LifecycleScriptsStrategy;
|
||||
|
@ -35,8 +35,8 @@ use super::common::RegistryReadPermissionChecker;
|
|||
/// Resolves packages from the global npm cache.
|
||||
#[derive(Debug)]
|
||||
pub struct GlobalNpmPackageResolver {
|
||||
cache: Arc<NpmCache>,
|
||||
tarball_cache: Arc<TarballCache>,
|
||||
cache: Arc<CliNpmCache>,
|
||||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
system_info: NpmSystemInfo,
|
||||
registry_read_permission_checker: RegistryReadPermissionChecker,
|
||||
|
@ -45,9 +45,9 @@ pub struct GlobalNpmPackageResolver {
|
|||
|
||||
impl GlobalNpmPackageResolver {
|
||||
pub fn new(
|
||||
cache: Arc<NpmCache>,
|
||||
cache: Arc<CliNpmCache>,
|
||||
fs: Arc<dyn FileSystem>,
|
||||
tarball_cache: Arc<TarballCache>,
|
||||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
system_info: NpmSystemInfo,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
|
|
|
@ -17,6 +17,8 @@ use std::sync::Arc;
|
|||
|
||||
use crate::args::LifecycleScriptsConfig;
|
||||
use crate::colors;
|
||||
use crate::npm::CliNpmCache;
|
||||
use crate::npm::CliNpmTarballCache;
|
||||
use async_trait::async_trait;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_cache_dir::npm::mixed_case_package_name_decode;
|
||||
|
@ -52,8 +54,6 @@ use crate::util::fs::LaxSingleProcessFsFlag;
|
|||
use crate::util::progress_bar::ProgressBar;
|
||||
use crate::util::progress_bar::ProgressMessagePrompt;
|
||||
|
||||
use super::super::cache::NpmCache;
|
||||
use super::super::cache::TarballCache;
|
||||
use super::super::resolution::NpmResolution;
|
||||
use super::common::bin_entries;
|
||||
use super::common::NpmPackageFsResolver;
|
||||
|
@ -63,12 +63,12 @@ use super::common::RegistryReadPermissionChecker;
|
|||
/// and resolves packages from it.
|
||||
#[derive(Debug)]
|
||||
pub struct LocalNpmPackageResolver {
|
||||
cache: Arc<NpmCache>,
|
||||
cache: Arc<CliNpmCache>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
progress_bar: ProgressBar,
|
||||
resolution: Arc<NpmResolution>,
|
||||
tarball_cache: Arc<TarballCache>,
|
||||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
root_node_modules_path: PathBuf,
|
||||
root_node_modules_url: Url,
|
||||
system_info: NpmSystemInfo,
|
||||
|
@ -79,12 +79,12 @@ pub struct LocalNpmPackageResolver {
|
|||
impl LocalNpmPackageResolver {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
cache: Arc<NpmCache>,
|
||||
cache: Arc<CliNpmCache>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
progress_bar: ProgressBar,
|
||||
resolution: Arc<NpmResolution>,
|
||||
tarball_cache: Arc<TarballCache>,
|
||||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
node_modules_folder: PathBuf,
|
||||
system_info: NpmSystemInfo,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
|
@ -236,8 +236,21 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver {
|
|||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let folder_name = folder_path.parent().unwrap().to_string_lossy();
|
||||
Ok(get_package_folder_id_from_folder_name(&folder_name))
|
||||
// ex. project/node_modules/.deno/preact@10.24.3/node_modules/preact/
|
||||
let Some(node_modules_ancestor) = folder_path
|
||||
.ancestors()
|
||||
.find(|ancestor| ancestor.ends_with("node_modules"))
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(folder_name) =
|
||||
node_modules_ancestor.parent().and_then(|p| p.file_name())
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
Ok(get_package_folder_id_from_folder_name(
|
||||
&folder_name.to_string_lossy(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn cache_packages(&self) -> Result<(), AnyError> {
|
||||
|
@ -284,10 +297,10 @@ fn local_node_modules_package_contents_path(
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
async fn sync_resolution_with_fs(
|
||||
snapshot: &NpmResolutionSnapshot,
|
||||
cache: &Arc<NpmCache>,
|
||||
cache: &Arc<CliNpmCache>,
|
||||
npm_install_deps_provider: &NpmInstallDepsProvider,
|
||||
progress_bar: &ProgressBar,
|
||||
tarball_cache: &Arc<TarballCache>,
|
||||
tarball_cache: &Arc<CliNpmTarballCache>,
|
||||
root_node_modules_dir_path: &Path,
|
||||
system_info: &NpmSystemInfo,
|
||||
lifecycle_scripts: &LifecycleScriptsConfig,
|
||||
|
@ -298,6 +311,12 @@ async fn sync_resolution_with_fs(
|
|||
return Ok(()); // don't create the directory
|
||||
}
|
||||
|
||||
// don't set up node_modules (and more importantly try to acquire the file lock)
|
||||
// if we're running as part of a lifecycle script
|
||||
if super::common::lifecycle_scripts::is_running_lifecycle_script() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let deno_local_registry_dir = root_node_modules_dir_path.join(".deno");
|
||||
let deno_node_modules_dir = deno_local_registry_dir.join("node_modules");
|
||||
fs::create_dir_all(&deno_node_modules_dir).with_context(|| {
|
||||
|
|
|
@ -12,6 +12,8 @@ use deno_runtime::deno_fs::FileSystem;
|
|||
|
||||
use crate::args::LifecycleScriptsConfig;
|
||||
use crate::args::NpmInstallDepsProvider;
|
||||
use crate::npm::CliNpmCache;
|
||||
use crate::npm::CliNpmTarballCache;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
|
||||
pub use self::common::NpmPackageFsResolver;
|
||||
|
@ -19,18 +21,16 @@ pub use self::common::NpmPackageFsResolver;
|
|||
use self::global::GlobalNpmPackageResolver;
|
||||
use self::local::LocalNpmPackageResolver;
|
||||
|
||||
use super::cache::NpmCache;
|
||||
use super::cache::TarballCache;
|
||||
use super::resolution::NpmResolution;
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn create_npm_fs_resolver(
|
||||
fs: Arc<dyn FileSystem>,
|
||||
npm_cache: Arc<NpmCache>,
|
||||
npm_cache: Arc<CliNpmCache>,
|
||||
npm_install_deps_provider: &Arc<NpmInstallDepsProvider>,
|
||||
progress_bar: &ProgressBar,
|
||||
resolution: Arc<NpmResolution>,
|
||||
tarball_cache: Arc<TarballCache>,
|
||||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
maybe_node_modules_path: Option<PathBuf>,
|
||||
system_info: NpmSystemInfo,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
|
|
111
cli/npm/mod.rs
111
cli/npm/mod.rs
|
@ -1,33 +1,39 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
mod byonm;
|
||||
mod common;
|
||||
mod managed;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use common::maybe_auth_header_for_npm_registry;
|
||||
use dashmap::DashMap;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::url::Url;
|
||||
use deno_npm::npm_rc::ResolvedNpmRc;
|
||||
use deno_npm::registry::NpmPackageInfo;
|
||||
use deno_resolver::npm::ByonmInNpmPackageChecker;
|
||||
use deno_resolver::npm::ByonmNpmResolver;
|
||||
use deno_resolver::npm::CliNpmReqResolver;
|
||||
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
|
||||
use deno_runtime::deno_fs::FileSystem;
|
||||
use deno_runtime::deno_node::NodePermissions;
|
||||
use deno_runtime::ops::process::NpmProcessStateProvider;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use managed::cache::registry_info::get_package_url;
|
||||
use http::HeaderName;
|
||||
use http::HeaderValue;
|
||||
use managed::create_managed_in_npm_pkg_checker;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
use node_resolver::NpmPackageFolderResolver;
|
||||
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
use crate::util::fs::atomic_write_file_with_retries_and_fs;
|
||||
use crate::util::fs::hard_link_dir_recursive;
|
||||
use crate::util::fs::AtomicWriteFileFsAdapter;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
|
||||
pub use self::byonm::CliByonmNpmResolver;
|
||||
pub use self::byonm::CliByonmNpmResolverCreateOptions;
|
||||
|
@ -36,6 +42,99 @@ pub use self::managed::CliManagedNpmResolverCreateOptions;
|
|||
pub use self::managed::CliNpmResolverManagedSnapshotOption;
|
||||
pub use self::managed::ManagedCliNpmResolver;
|
||||
|
||||
pub type CliNpmTarballCache = deno_npm_cache::TarballCache<CliNpmCacheEnv>;
|
||||
pub type CliNpmCache = deno_npm_cache::NpmCache<CliNpmCacheEnv>;
|
||||
pub type CliNpmRegistryInfoProvider =
|
||||
deno_npm_cache::RegistryInfoProvider<CliNpmCacheEnv>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CliNpmCacheEnv {
|
||||
fs: Arc<dyn FileSystem>,
|
||||
http_client_provider: Arc<HttpClientProvider>,
|
||||
progress_bar: ProgressBar,
|
||||
}
|
||||
|
||||
impl CliNpmCacheEnv {
|
||||
pub fn new(
|
||||
fs: Arc<dyn FileSystem>,
|
||||
http_client_provider: Arc<HttpClientProvider>,
|
||||
progress_bar: ProgressBar,
|
||||
) -> Self {
|
||||
Self {
|
||||
fs,
|
||||
http_client_provider,
|
||||
progress_bar,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl deno_npm_cache::NpmCacheEnv for CliNpmCacheEnv {
|
||||
fn exists(&self, path: &Path) -> bool {
|
||||
self.fs.exists_sync(path)
|
||||
}
|
||||
|
||||
fn hard_link_dir_recursive(
|
||||
&self,
|
||||
from: &Path,
|
||||
to: &Path,
|
||||
) -> Result<(), AnyError> {
|
||||
// todo(dsherret): use self.fs here instead
|
||||
hard_link_dir_recursive(from, to)
|
||||
}
|
||||
|
||||
fn atomic_write_file_with_retries(
|
||||
&self,
|
||||
file_path: &Path,
|
||||
data: &[u8],
|
||||
) -> std::io::Result<()> {
|
||||
atomic_write_file_with_retries_and_fs(
|
||||
&AtomicWriteFileFsAdapter {
|
||||
fs: self.fs.as_ref(),
|
||||
write_mode: crate::cache::CACHE_PERM,
|
||||
},
|
||||
file_path,
|
||||
data,
|
||||
)
|
||||
}
|
||||
|
||||
async fn download_with_retries_on_any_tokio_runtime(
|
||||
&self,
|
||||
url: Url,
|
||||
maybe_auth_header: Option<(HeaderName, HeaderValue)>,
|
||||
) -> Result<Option<Vec<u8>>, deno_npm_cache::DownloadError> {
|
||||
let guard = self.progress_bar.update(url.as_str());
|
||||
let client = self.http_client_provider.get_or_create().map_err(|err| {
|
||||
deno_npm_cache::DownloadError {
|
||||
status_code: None,
|
||||
error: err,
|
||||
}
|
||||
})?;
|
||||
client
|
||||
.download_with_progress_and_retries(url, maybe_auth_header, &guard)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
use crate::http_util::DownloadError::*;
|
||||
let status_code = match &err {
|
||||
Fetch { .. }
|
||||
| UrlParse { .. }
|
||||
| HttpParse { .. }
|
||||
| Json { .. }
|
||||
| ToStr { .. }
|
||||
| NoRedirectHeader { .. }
|
||||
| TooManyRedirects => None,
|
||||
BadResponse(bad_response_error) => {
|
||||
Some(bad_response_error.status_code)
|
||||
}
|
||||
};
|
||||
deno_npm_cache::DownloadError {
|
||||
status_code,
|
||||
error: err.into(),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub enum CliNpmResolverCreateOptions {
|
||||
Managed(CliManagedNpmResolverCreateOptions),
|
||||
Byonm(CliByonmNpmResolverCreateOptions),
|
||||
|
@ -179,13 +278,15 @@ impl NpmFetchResolver {
|
|||
if let Some(info) = self.info_by_name.get(name) {
|
||||
return info.value().clone();
|
||||
}
|
||||
// todo(#27198): use RegistryInfoProvider instead
|
||||
let fetch_package_info = || async {
|
||||
let info_url = get_package_url(&self.npmrc, name);
|
||||
let info_url = deno_npm_cache::get_package_url(&self.npmrc, name);
|
||||
let file_fetcher = self.file_fetcher.clone();
|
||||
let registry_config = self.npmrc.get_registry_config(name);
|
||||
// TODO(bartlomieju): this should error out, not use `.ok()`.
|
||||
let maybe_auth_header =
|
||||
maybe_auth_header_for_npm_registry(registry_config).ok()?;
|
||||
deno_npm_cache::maybe_auth_header_for_npm_registry(registry_config)
|
||||
.ok()?;
|
||||
// spawn due to the lsp's `Send` requirement
|
||||
let file = deno_core::unsync::spawn(async move {
|
||||
file_fetcher
|
||||
|
|
|
@ -12,7 +12,6 @@ use deno_core::error::AnyError;
|
|||
use deno_core::url::Url;
|
||||
use deno_core::ModuleSourceCode;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::source::ResolveError;
|
||||
use deno_graph::source::UnknownBuiltInNodeModuleError;
|
||||
use deno_graph::NpmLoadError;
|
||||
|
@ -25,8 +24,8 @@ use deno_runtime::deno_fs::FileSystem;
|
|||
use deno_runtime::deno_node::is_builtin_node_module;
|
||||
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use node_resolver::NodeResolutionMode;
|
||||
use node_resolver::NodeResolutionKind;
|
||||
use node_resolver::ResolutionMode;
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
@ -38,12 +37,11 @@ use crate::node::CliNodeCodeTranslator;
|
|||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::InnerCliNpmResolverRef;
|
||||
use crate::util::sync::AtomicFlag;
|
||||
use crate::util::text_encoding::from_utf8_lossy_owned;
|
||||
use crate::util::text_encoding::from_utf8_lossy_cow;
|
||||
|
||||
pub type CjsTracker = deno_resolver::cjs::CjsTracker<DenoFsNodeResolverEnv>;
|
||||
pub type IsCjsResolver =
|
||||
deno_resolver::cjs::IsCjsResolver<DenoFsNodeResolverEnv>;
|
||||
pub type IsCjsResolverOptions = deno_resolver::cjs::IsCjsResolverOptions;
|
||||
pub type CliSloppyImportsResolver =
|
||||
SloppyImportsResolver<SloppyImportsCachedFs>;
|
||||
pub type CliDenoResolver = deno_resolver::DenoResolver<
|
||||
|
@ -64,7 +62,10 @@ pub struct ModuleCodeStringSource {
|
|||
pub struct CliDenoResolverFs(pub Arc<dyn FileSystem>);
|
||||
|
||||
impl deno_resolver::fs::DenoResolverFs for CliDenoResolverFs {
|
||||
fn read_to_string_lossy(&self, path: &Path) -> std::io::Result<String> {
|
||||
fn read_to_string_lossy(
|
||||
&self,
|
||||
path: &Path,
|
||||
) -> std::io::Result<Cow<'static, str>> {
|
||||
self
|
||||
.0
|
||||
.read_text_file_lossy_sync(path, None)
|
||||
|
@ -184,18 +185,21 @@ impl NpmModuleLoader {
|
|||
|
||||
let code = if self.cjs_tracker.is_maybe_cjs(specifier, media_type)? {
|
||||
// translate cjs to esm if it's cjs and inject node globals
|
||||
let code = from_utf8_lossy_owned(code);
|
||||
let code = from_utf8_lossy_cow(code);
|
||||
ModuleSourceCode::String(
|
||||
self
|
||||
.node_code_translator
|
||||
.translate_cjs_to_esm(specifier, Some(Cow::Owned(code)))
|
||||
.translate_cjs_to_esm(specifier, Some(code))
|
||||
.await?
|
||||
.into_owned()
|
||||
.into(),
|
||||
)
|
||||
} else {
|
||||
// esm and json code is untouched
|
||||
ModuleSourceCode::Bytes(code.into_boxed_slice().into())
|
||||
ModuleSourceCode::Bytes(match code {
|
||||
Cow::Owned(bytes) => bytes.into_boxed_slice().into(),
|
||||
Cow::Borrowed(bytes) => bytes.into(),
|
||||
})
|
||||
};
|
||||
|
||||
Ok(ModuleCodeStringSource {
|
||||
|
@ -247,25 +251,14 @@ impl CliResolver {
|
|||
pub fn resolve(
|
||||
&self,
|
||||
raw_specifier: &str,
|
||||
referrer_range: &deno_graph::Range,
|
||||
referrer_kind: NodeModuleKind,
|
||||
mode: ResolutionMode,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_range_start: deno_graph::Position,
|
||||
resolution_mode: ResolutionMode,
|
||||
resolution_kind: NodeResolutionKind,
|
||||
) -> Result<ModuleSpecifier, ResolveError> {
|
||||
fn to_node_mode(mode: ResolutionMode) -> NodeResolutionMode {
|
||||
match mode {
|
||||
ResolutionMode::Execution => NodeResolutionMode::Execution,
|
||||
ResolutionMode::Types => NodeResolutionMode::Types,
|
||||
}
|
||||
}
|
||||
|
||||
let resolution = self
|
||||
.deno_resolver
|
||||
.resolve(
|
||||
raw_specifier,
|
||||
&referrer_range.specifier,
|
||||
referrer_kind,
|
||||
to_node_mode(mode),
|
||||
)
|
||||
.resolve(raw_specifier, referrer, resolution_mode, resolution_kind)
|
||||
.map_err(|err| match err.into_kind() {
|
||||
deno_resolver::DenoResolveErrorKind::MappedResolution(
|
||||
mapped_resolution_error,
|
||||
|
@ -291,10 +284,11 @@ impl CliResolver {
|
|||
} => {
|
||||
if self.warned_pkgs.insert(reference.req().clone()) {
|
||||
log::warn!(
|
||||
"{} {}\n at {}",
|
||||
"{} {}\n at {}:{}",
|
||||
colors::yellow("Warning"),
|
||||
diagnostic,
|
||||
referrer_range
|
||||
referrer,
|
||||
referrer_range_start,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -335,13 +329,10 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> {
|
|||
module_name: &str,
|
||||
range: &deno_graph::Range,
|
||||
) {
|
||||
let deno_graph::Range {
|
||||
start, specifier, ..
|
||||
} = range;
|
||||
let line = start.line + 1;
|
||||
let column = start.character + 1;
|
||||
let start = range.range.start;
|
||||
let specifier = &range.specifier;
|
||||
if !*DENO_DISABLE_PEDANTIC_NODE_WARNINGS {
|
||||
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"))
|
||||
log::warn!("{} Resolving \"{module_name}\" as \"node:{module_name}\" at {specifier}:{start}. If you want to use a built-in Node module, add a \"node:\" prefix.", colors::yellow("Warning"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -554,6 +554,7 @@
|
|||
"bare-node-builtins",
|
||||
"byonm",
|
||||
"cron",
|
||||
"detect-cjs",
|
||||
"ffi",
|
||||
"fs",
|
||||
"fmt-component",
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::borrow::Cow;
|
|||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::VecDeque;
|
||||
use std::env;
|
||||
use std::env::current_exe;
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
|
@ -15,6 +16,7 @@ use std::io::Seek;
|
|||
use std::io::SeekFrom;
|
||||
use std::io::Write;
|
||||
use std::ops::Range;
|
||||
use std::path::Component;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
@ -47,11 +49,11 @@ use deno_runtime::deno_fs::FileSystem;
|
|||
use deno_runtime::deno_fs::RealFs;
|
||||
use deno_runtime::deno_io::fs::FsError;
|
||||
use deno_runtime::deno_node::PackageJson;
|
||||
use deno_runtime::ops::otel::OtelConfig;
|
||||
use deno_semver::npm::NpmVersionReqParseError;
|
||||
use deno_semver::package::PackageReq;
|
||||
use deno_semver::Version;
|
||||
use deno_semver::VersionReqSpecifierParseError;
|
||||
use deno_telemetry::OtelConfig;
|
||||
use indexmap::IndexMap;
|
||||
use log::Level;
|
||||
use serde::Deserialize;
|
||||
|
@ -87,6 +89,7 @@ use super::serialization::RemoteModulesStore;
|
|||
use super::serialization::RemoteModulesStoreBuilder;
|
||||
use super::virtual_fs::FileBackedVfs;
|
||||
use super::virtual_fs::VfsBuilder;
|
||||
use super::virtual_fs::VfsFileSubDataKind;
|
||||
use super::virtual_fs::VfsRoot;
|
||||
use super::virtual_fs::VirtualDirectory;
|
||||
|
||||
|
@ -275,16 +278,17 @@ impl StandaloneModules {
|
|||
if specifier.scheme() == "file" {
|
||||
let path = deno_path_util::url_to_file_path(specifier)?;
|
||||
let bytes = match self.vfs.file_entry(&path) {
|
||||
Ok(entry) => self.vfs.read_file_all(entry)?,
|
||||
Ok(entry) => self
|
||||
.vfs
|
||||
.read_file_all(entry, VfsFileSubDataKind::ModuleGraph)?,
|
||||
Err(err) if err.kind() == ErrorKind::NotFound => {
|
||||
let bytes = match RealFs.read_file_sync(&path, None) {
|
||||
match RealFs.read_file_sync(&path, None) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(FsError::Io(err)) if err.kind() == ErrorKind::NotFound => {
|
||||
return Ok(None)
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
Cow::Owned(bytes)
|
||||
}
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
|
@ -454,7 +458,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
//
|
||||
// Phase 2 of the 'min sized' deno compile RFC talks
|
||||
// about adding this as a flag.
|
||||
if let Some(path) = std::env::var_os("DENORT_BIN") {
|
||||
if let Some(path) = get_dev_binary_path() {
|
||||
return std::fs::read(&path).with_context(|| {
|
||||
format!("Could not find denort at '{}'", path.to_string_lossy())
|
||||
});
|
||||
|
@ -689,8 +693,9 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
&file_path,
|
||||
match maybe_source {
|
||||
Some(source) => source,
|
||||
None => RealFs.read_file_sync(&file_path, None)?,
|
||||
None => RealFs.read_file_sync(&file_path, None)?.into_owned(),
|
||||
},
|
||||
VfsFileSubDataKind::ModuleGraph,
|
||||
)
|
||||
.with_context(|| {
|
||||
format!("Failed adding '{}'", file_path.display())
|
||||
|
@ -771,6 +776,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
unstable_config: UnstableConfig {
|
||||
legacy_flag_enabled: false,
|
||||
bare_node_builtins: self.cli_options.unstable_bare_node_builtins(),
|
||||
detect_cjs: self.cli_options.unstable_detect_cjs(),
|
||||
sloppy_imports: self.cli_options.unstable_sloppy_imports(),
|
||||
features: self.cli_options.unstable_features(),
|
||||
},
|
||||
|
@ -904,6 +910,31 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_denort_path(deno_exe: PathBuf) -> Option<OsString> {
|
||||
let mut denort = deno_exe;
|
||||
denort.set_file_name(if cfg!(windows) {
|
||||
"denort.exe"
|
||||
} else {
|
||||
"denort"
|
||||
});
|
||||
denort.exists().then(|| denort.into_os_string())
|
||||
}
|
||||
|
||||
fn get_dev_binary_path() -> Option<OsString> {
|
||||
env::var_os("DENORT_BIN").or_else(|| {
|
||||
env::current_exe().ok().and_then(|exec_path| {
|
||||
if exec_path
|
||||
.components()
|
||||
.any(|component| component == Component::Normal("target".as_ref()))
|
||||
{
|
||||
get_denort_path(exec_path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// This function returns the environment variables specified
|
||||
/// in the passed environment file.
|
||||
fn get_file_env_vars(
|
||||
|
|
|
@ -17,6 +17,7 @@ use deno_runtime::deno_io::fs::FsResult;
|
|||
use deno_runtime::deno_io::fs::FsStat;
|
||||
|
||||
use super::virtual_fs::FileBackedVfs;
|
||||
use super::virtual_fs::VfsFileSubDataKind;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DenoCompileFileSystem(Arc<FileBackedVfs>);
|
||||
|
@ -36,7 +37,8 @@ impl DenoCompileFileSystem {
|
|||
|
||||
fn copy_to_real_path(&self, oldpath: &Path, newpath: &Path) -> FsResult<()> {
|
||||
let old_file = self.0.file_entry(oldpath)?;
|
||||
let old_file_bytes = self.0.read_file_all(old_file)?;
|
||||
let old_file_bytes =
|
||||
self.0.read_file_all(old_file, VfsFileSubDataKind::Raw)?;
|
||||
RealFs.write_file_sync(
|
||||
newpath,
|
||||
OpenOptions {
|
||||
|
|
|
@ -32,6 +32,7 @@ use deno_core::ResolutionKind;
|
|||
use deno_core::SourceCodeCacheInfo;
|
||||
use deno_npm::npm_rc::ResolvedNpmRc;
|
||||
use deno_package_json::PackageJsonDepValue;
|
||||
use deno_resolver::cjs::IsCjsResolutionMode;
|
||||
use deno_resolver::npm::NpmReqResolverOptions;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node::create_host_defined_options;
|
||||
|
@ -50,12 +51,14 @@ use deno_semver::npm::NpmPackageReqReference;
|
|||
use import_map::parse_from_json;
|
||||
use node_resolver::analyze::NodeCodeTranslator;
|
||||
use node_resolver::errors::ClosestPkgJsonError;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use node_resolver::NodeResolutionMode;
|
||||
use node_resolver::NodeResolutionKind;
|
||||
use node_resolver::ResolutionMode;
|
||||
use serialization::DenoCompileModuleSource;
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use virtual_fs::FileBackedVfs;
|
||||
use virtual_fs::VfsFileSubDataKind;
|
||||
|
||||
use crate::args::create_default_npmrc;
|
||||
use crate::args::get_root_cert_store;
|
||||
|
@ -85,10 +88,10 @@ use crate::npm::CreateInNpmPkgCheckerOptions;
|
|||
use crate::resolver::CjsTracker;
|
||||
use crate::resolver::CliDenoResolverFs;
|
||||
use crate::resolver::CliNpmReqResolver;
|
||||
use crate::resolver::IsCjsResolverOptions;
|
||||
use crate::resolver::NpmModuleLoader;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
use crate::util::progress_bar::ProgressBarStyle;
|
||||
use crate::util::text_encoding::from_utf8_lossy_cow;
|
||||
use crate::util::v8::construct_v8_flags;
|
||||
use crate::worker::CliCodeCache;
|
||||
use crate::worker::CliMainWorkerFactory;
|
||||
|
@ -111,6 +114,7 @@ use self::file_system::DenoCompileFileSystem;
|
|||
|
||||
struct SharedModuleLoaderState {
|
||||
cjs_tracker: Arc<CjsTracker>,
|
||||
code_cache: Option<Arc<dyn CliCodeCache>>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
modules: StandaloneModules,
|
||||
node_code_translator: Arc<CliNodeCodeTranslator>,
|
||||
|
@ -118,8 +122,8 @@ struct SharedModuleLoaderState {
|
|||
npm_module_loader: Arc<NpmModuleLoader>,
|
||||
npm_req_resolver: Arc<CliNpmReqResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
vfs: Arc<FileBackedVfs>,
|
||||
workspace_resolver: WorkspaceResolver,
|
||||
code_cache: Option<Arc<dyn CliCodeCache>>,
|
||||
}
|
||||
|
||||
impl SharedModuleLoaderState {
|
||||
|
@ -190,9 +194,9 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||
.cjs_tracker
|
||||
.is_maybe_cjs(&referrer, MediaType::from_specifier(&referrer))?
|
||||
{
|
||||
NodeModuleKind::Cjs
|
||||
ResolutionMode::Require
|
||||
} else {
|
||||
NodeModuleKind::Esm
|
||||
ResolutionMode::Import
|
||||
};
|
||||
|
||||
if self.shared.node_resolver.in_npm_package(&referrer) {
|
||||
|
@ -204,7 +208,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||
raw_specifier,
|
||||
&referrer,
|
||||
referrer_kind,
|
||||
NodeResolutionMode::Execution,
|
||||
NodeResolutionKind::Execution,
|
||||
)?
|
||||
.into_url(),
|
||||
);
|
||||
|
@ -232,7 +236,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||
sub_path.as_deref(),
|
||||
Some(&referrer),
|
||||
referrer_kind,
|
||||
NodeResolutionMode::Execution,
|
||||
NodeResolutionKind::Execution,
|
||||
)?,
|
||||
),
|
||||
Ok(MappedResolution::PackageJson {
|
||||
|
@ -249,7 +253,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||
sub_path.as_deref(),
|
||||
&referrer,
|
||||
referrer_kind,
|
||||
NodeResolutionMode::Execution,
|
||||
NodeResolutionKind::Execution,
|
||||
)
|
||||
.map_err(AnyError::from),
|
||||
PackageJsonDepValue::Workspace(version_req) => {
|
||||
|
@ -269,7 +273,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||
sub_path.as_deref(),
|
||||
Some(&referrer),
|
||||
referrer_kind,
|
||||
NodeResolutionMode::Execution,
|
||||
NodeResolutionKind::Execution,
|
||||
)?,
|
||||
)
|
||||
}
|
||||
|
@ -283,7 +287,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||
&reference,
|
||||
&referrer,
|
||||
referrer_kind,
|
||||
NodeResolutionMode::Execution,
|
||||
NodeResolutionKind::Execution,
|
||||
)?);
|
||||
}
|
||||
|
||||
|
@ -310,7 +314,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||
raw_specifier,
|
||||
&referrer,
|
||||
referrer_kind,
|
||||
NodeResolutionMode::Execution,
|
||||
NodeResolutionKind::Execution,
|
||||
)?;
|
||||
if let Some(res) = maybe_res {
|
||||
return Ok(res.into_url());
|
||||
|
@ -513,8 +517,13 @@ impl NodeRequireLoader for EmbeddedModuleLoader {
|
|||
fn load_text_file_lossy(
|
||||
&self,
|
||||
path: &std::path::Path,
|
||||
) -> Result<String, AnyError> {
|
||||
Ok(self.shared.fs.read_text_file_lossy_sync(path, None)?)
|
||||
) -> Result<Cow<'static, str>, AnyError> {
|
||||
let file_entry = self.shared.vfs.file_entry(path)?;
|
||||
let file_bytes = self
|
||||
.shared
|
||||
.vfs
|
||||
.read_file_all(file_entry, VfsFileSubDataKind::ModuleGraph)?;
|
||||
Ok(from_utf8_lossy_cow(file_bytes))
|
||||
}
|
||||
|
||||
fn is_maybe_cjs(
|
||||
|
@ -723,9 +732,12 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
|||
let cjs_tracker = Arc::new(CjsTracker::new(
|
||||
in_npm_pkg_checker.clone(),
|
||||
pkg_json_resolver.clone(),
|
||||
IsCjsResolverOptions {
|
||||
detect_cjs: !metadata.workspace_resolver.package_jsons.is_empty(),
|
||||
is_node_main: false,
|
||||
if metadata.unstable_config.detect_cjs {
|
||||
IsCjsResolutionMode::ImplicitTypeCommonJs
|
||||
} else if metadata.workspace_resolver.package_jsons.is_empty() {
|
||||
IsCjsResolutionMode::Disabled
|
||||
} else {
|
||||
IsCjsResolutionMode::ExplicitTypeCommonJs
|
||||
},
|
||||
));
|
||||
let cache_db = Caches::new(deno_dir_provider.clone());
|
||||
|
@ -817,6 +829,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
|||
let module_loader_factory = StandaloneModuleLoaderFactory {
|
||||
shared: Arc::new(SharedModuleLoaderState {
|
||||
cjs_tracker: cjs_tracker.clone(),
|
||||
code_cache: code_cache.clone(),
|
||||
fs: fs.clone(),
|
||||
modules,
|
||||
node_code_translator: node_code_translator.clone(),
|
||||
|
@ -826,10 +839,10 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
|||
fs.clone(),
|
||||
node_code_translator,
|
||||
)),
|
||||
code_cache: code_cache.clone(),
|
||||
npm_resolver: npm_resolver.clone(),
|
||||
workspace_resolver,
|
||||
npm_req_resolver,
|
||||
vfs,
|
||||
workspace_resolver,
|
||||
}),
|
||||
};
|
||||
|
||||
|
|
|
@ -32,6 +32,15 @@ use thiserror::Error;
|
|||
use crate::util;
|
||||
use crate::util::fs::canonicalize_path;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum VfsFileSubDataKind {
|
||||
/// Raw bytes of the file.
|
||||
Raw,
|
||||
/// Bytes to use for module loading. For example, for TypeScript
|
||||
/// files this will be the transpiled JavaScript source.
|
||||
ModuleGraph,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
#[error(
|
||||
"Failed to strip prefix '{}' from '{}'", root_path.display(), target.display()
|
||||
|
@ -141,7 +150,11 @@ impl VfsBuilder {
|
|||
// inline the symlink and make the target file
|
||||
let file_bytes = std::fs::read(&target)
|
||||
.with_context(|| format!("Reading {}", path.display()))?;
|
||||
self.add_file_with_data_inner(&path, file_bytes)?;
|
||||
self.add_file_with_data_inner(
|
||||
&path,
|
||||
file_bytes,
|
||||
VfsFileSubDataKind::Raw,
|
||||
)?;
|
||||
} else {
|
||||
log::warn!(
|
||||
"{} Symlink target is outside '{}'. Excluding symlink at '{}' with target '{}'.",
|
||||
|
@ -219,25 +232,27 @@ impl VfsBuilder {
|
|||
) -> Result<(), AnyError> {
|
||||
let file_bytes = std::fs::read(path)
|
||||
.with_context(|| format!("Reading {}", path.display()))?;
|
||||
self.add_file_with_data_inner(path, file_bytes)
|
||||
self.add_file_with_data_inner(path, file_bytes, VfsFileSubDataKind::Raw)
|
||||
}
|
||||
|
||||
pub fn add_file_with_data(
|
||||
&mut self,
|
||||
path: &Path,
|
||||
data: Vec<u8>,
|
||||
sub_data_kind: VfsFileSubDataKind,
|
||||
) -> Result<(), AnyError> {
|
||||
let target_path = canonicalize_path(path)?;
|
||||
if target_path != path {
|
||||
self.add_symlink(path, &target_path)?;
|
||||
}
|
||||
self.add_file_with_data_inner(&target_path, data)
|
||||
self.add_file_with_data_inner(&target_path, data, sub_data_kind)
|
||||
}
|
||||
|
||||
fn add_file_with_data_inner(
|
||||
&mut self,
|
||||
path: &Path,
|
||||
data: Vec<u8>,
|
||||
sub_data_kind: VfsFileSubDataKind,
|
||||
) -> Result<(), AnyError> {
|
||||
log::debug!("Adding file '{}'", path.display());
|
||||
let checksum = util::checksum::gen(&[&data]);
|
||||
|
@ -253,8 +268,19 @@ impl VfsBuilder {
|
|||
let name = path.file_name().unwrap().to_string_lossy();
|
||||
let data_len = data.len();
|
||||
match dir.entries.binary_search_by(|e| e.name().cmp(&name)) {
|
||||
Ok(_) => {
|
||||
// already added, just ignore
|
||||
Ok(index) => {
|
||||
let entry = &mut dir.entries[index];
|
||||
match entry {
|
||||
VfsEntry::File(virtual_file) => match sub_data_kind {
|
||||
VfsFileSubDataKind::Raw => {
|
||||
virtual_file.offset = offset;
|
||||
}
|
||||
VfsFileSubDataKind::ModuleGraph => {
|
||||
virtual_file.module_graph_offset = offset;
|
||||
}
|
||||
},
|
||||
VfsEntry::Dir(_) | VfsEntry::Symlink(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
Err(insert_index) => {
|
||||
dir.entries.insert(
|
||||
|
@ -262,6 +288,7 @@ impl VfsBuilder {
|
|||
VfsEntry::File(VirtualFile {
|
||||
name: name.to_string(),
|
||||
offset,
|
||||
module_graph_offset: offset,
|
||||
len: data.len() as u64,
|
||||
}),
|
||||
);
|
||||
|
@ -302,7 +329,7 @@ impl VfsBuilder {
|
|||
let dir = self.add_dir(path.parent().unwrap())?;
|
||||
let name = path.file_name().unwrap().to_string_lossy();
|
||||
match dir.entries.binary_search_by(|e| e.name().cmp(&name)) {
|
||||
Ok(_) => unreachable!(),
|
||||
Ok(_) => Ok(()), // previously inserted
|
||||
Err(insert_index) => {
|
||||
dir.entries.insert(
|
||||
insert_index,
|
||||
|
@ -314,9 +341,9 @@ impl VfsBuilder {
|
|||
.collect::<Vec<_>>(),
|
||||
}),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn into_dir_and_files(self) -> (VirtualDirectory, Vec<Vec<u8>>) {
|
||||
|
@ -454,6 +481,12 @@ pub struct VirtualDirectory {
|
|||
pub struct VirtualFile {
|
||||
pub name: String,
|
||||
pub offset: u64,
|
||||
/// Offset file to use for module loading when it differs from the
|
||||
/// raw file. Often this will be the same offset as above for data
|
||||
/// such as JavaScript files, but for TypeScript files the `offset`
|
||||
/// will be the original raw bytes when included as an asset and this
|
||||
/// offset will be to the transpiled JavaScript source.
|
||||
pub module_graph_offset: u64,
|
||||
pub len: u64,
|
||||
}
|
||||
|
||||
|
@ -647,7 +680,7 @@ impl FileBackedVfsFile {
|
|||
.map_err(|err| err.into())
|
||||
}
|
||||
|
||||
fn read_to_end(&self) -> FsResult<Vec<u8>> {
|
||||
fn read_to_end(&self) -> FsResult<Cow<'static, [u8]>> {
|
||||
let read_pos = {
|
||||
let mut pos = self.pos.lock();
|
||||
let read_pos = *pos;
|
||||
|
@ -659,12 +692,20 @@ impl FileBackedVfsFile {
|
|||
read_pos
|
||||
};
|
||||
if read_pos > self.file.len {
|
||||
return Ok(Vec::new());
|
||||
return Ok(Cow::Borrowed(&[]));
|
||||
}
|
||||
if read_pos == 0 {
|
||||
Ok(
|
||||
self
|
||||
.vfs
|
||||
.read_file_all(&self.file, VfsFileSubDataKind::Raw)?,
|
||||
)
|
||||
} else {
|
||||
let size = (self.file.len - read_pos) as usize;
|
||||
let mut buf = vec![0; size];
|
||||
self.vfs.read_file(&self.file, read_pos, &mut buf)?;
|
||||
Ok(Cow::Owned(buf))
|
||||
}
|
||||
let size = (self.file.len - read_pos) as usize;
|
||||
let mut buf = vec![0; size];
|
||||
self.vfs.read_file(&self.file, read_pos, &mut buf)?;
|
||||
Ok(buf)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -702,10 +743,10 @@ impl deno_io::fs::File for FileBackedVfsFile {
|
|||
Err(FsError::NotSupported)
|
||||
}
|
||||
|
||||
fn read_all_sync(self: Rc<Self>) -> FsResult<Vec<u8>> {
|
||||
fn read_all_sync(self: Rc<Self>) -> FsResult<Cow<'static, [u8]>> {
|
||||
self.read_to_end()
|
||||
}
|
||||
async fn read_all_async(self: Rc<Self>) -> FsResult<Vec<u8>> {
|
||||
async fn read_all_async(self: Rc<Self>) -> FsResult<Cow<'static, [u8]>> {
|
||||
let inner = (*self).clone();
|
||||
tokio::task::spawn_blocking(move || inner.read_to_end()).await?
|
||||
}
|
||||
|
@ -878,8 +919,9 @@ impl FileBackedVfs {
|
|||
pub fn read_file_all(
|
||||
&self,
|
||||
file: &VirtualFile,
|
||||
sub_data_kind: VfsFileSubDataKind,
|
||||
) -> std::io::Result<Cow<'static, [u8]>> {
|
||||
let read_range = self.get_read_range(file, 0, file.len)?;
|
||||
let read_range = self.get_read_range(file, sub_data_kind, 0, file.len)?;
|
||||
match &self.vfs_data {
|
||||
Cow::Borrowed(data) => Ok(Cow::Borrowed(&data[read_range])),
|
||||
Cow::Owned(data) => Ok(Cow::Owned(data[read_range].to_vec())),
|
||||
|
@ -892,7 +934,12 @@ impl FileBackedVfs {
|
|||
pos: u64,
|
||||
buf: &mut [u8],
|
||||
) -> std::io::Result<usize> {
|
||||
let read_range = self.get_read_range(file, pos, buf.len() as u64)?;
|
||||
let read_range = self.get_read_range(
|
||||
file,
|
||||
VfsFileSubDataKind::Raw,
|
||||
pos,
|
||||
buf.len() as u64,
|
||||
)?;
|
||||
let read_len = read_range.len();
|
||||
buf[..read_len].copy_from_slice(&self.vfs_data[read_range]);
|
||||
Ok(read_len)
|
||||
|
@ -901,6 +948,7 @@ impl FileBackedVfs {
|
|||
fn get_read_range(
|
||||
&self,
|
||||
file: &VirtualFile,
|
||||
sub_data_kind: VfsFileSubDataKind,
|
||||
pos: u64,
|
||||
len: u64,
|
||||
) -> std::io::Result<Range<usize>> {
|
||||
|
@ -910,7 +958,11 @@ impl FileBackedVfs {
|
|||
"unexpected EOF",
|
||||
));
|
||||
}
|
||||
let file_offset = self.fs_root.start_file_offset + file.offset;
|
||||
let offset = match sub_data_kind {
|
||||
VfsFileSubDataKind::Raw => file.offset,
|
||||
VfsFileSubDataKind::ModuleGraph => file.module_graph_offset,
|
||||
};
|
||||
let file_offset = self.fs_root.start_file_offset + offset;
|
||||
let start = file_offset + pos;
|
||||
let end = file_offset + std::cmp::min(pos + len, file.len);
|
||||
Ok(start as usize..end as usize)
|
||||
|
@ -951,7 +1003,13 @@ mod test {
|
|||
#[track_caller]
|
||||
fn read_file(vfs: &FileBackedVfs, path: &Path) -> String {
|
||||
let file = vfs.file_entry(path).unwrap();
|
||||
String::from_utf8(vfs.read_file_all(file).unwrap().into_owned()).unwrap()
|
||||
String::from_utf8(
|
||||
vfs
|
||||
.read_file_all(file, VfsFileSubDataKind::Raw)
|
||||
.unwrap()
|
||||
.into_owned(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -964,23 +1022,40 @@ mod test {
|
|||
let src_path = src_path.to_path_buf();
|
||||
let mut builder = VfsBuilder::new(src_path.clone()).unwrap();
|
||||
builder
|
||||
.add_file_with_data_inner(&src_path.join("a.txt"), "data".into())
|
||||
.add_file_with_data_inner(
|
||||
&src_path.join("a.txt"),
|
||||
"data".into(),
|
||||
VfsFileSubDataKind::Raw,
|
||||
)
|
||||
.unwrap();
|
||||
builder
|
||||
.add_file_with_data_inner(&src_path.join("b.txt"), "data".into())
|
||||
.add_file_with_data_inner(
|
||||
&src_path.join("b.txt"),
|
||||
"data".into(),
|
||||
VfsFileSubDataKind::Raw,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(builder.files.len(), 1); // because duplicate data
|
||||
builder
|
||||
.add_file_with_data_inner(&src_path.join("c.txt"), "c".into())
|
||||
.add_file_with_data_inner(
|
||||
&src_path.join("c.txt"),
|
||||
"c".into(),
|
||||
VfsFileSubDataKind::Raw,
|
||||
)
|
||||
.unwrap();
|
||||
builder
|
||||
.add_file_with_data_inner(
|
||||
&src_path.join("sub_dir").join("d.txt"),
|
||||
"d".into(),
|
||||
VfsFileSubDataKind::Raw,
|
||||
)
|
||||
.unwrap();
|
||||
builder
|
||||
.add_file_with_data_inner(&src_path.join("e.txt"), "e".into())
|
||||
.add_file_with_data_inner(
|
||||
&src_path.join("e.txt"),
|
||||
"e".into(),
|
||||
VfsFileSubDataKind::Raw,
|
||||
)
|
||||
.unwrap();
|
||||
builder
|
||||
.add_symlink(
|
||||
|
@ -1151,6 +1226,7 @@ mod test {
|
|||
.add_file_with_data_inner(
|
||||
temp_path.join("a.txt").as_path(),
|
||||
"0123456789".to_string().into_bytes(),
|
||||
VfsFileSubDataKind::Raw,
|
||||
)
|
||||
.unwrap();
|
||||
let (dest_path, virtual_fs) = into_virtual_fs(builder, &temp_dir);
|
||||
|
|
|
@ -14,6 +14,7 @@ use deno_runtime::deno_node::NodeResolver;
|
|||
use deno_semver::package::PackageNv;
|
||||
use deno_task_shell::ExecutableCommand;
|
||||
use deno_task_shell::ExecuteResult;
|
||||
use deno_task_shell::KillSignal;
|
||||
use deno_task_shell::ShellCommand;
|
||||
use deno_task_shell::ShellCommandContext;
|
||||
use deno_task_shell::ShellPipeReader;
|
||||
|
@ -22,6 +23,7 @@ use lazy_regex::Lazy;
|
|||
use regex::Regex;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio::task::LocalSet;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::InnerCliNpmResolverRef;
|
||||
|
@ -45,9 +47,11 @@ impl TaskStdio {
|
|||
pub fn stdout() -> Self {
|
||||
Self(None, ShellPipeWriter::stdout())
|
||||
}
|
||||
|
||||
pub fn stderr() -> Self {
|
||||
Self(None, ShellPipeWriter::stderr())
|
||||
}
|
||||
|
||||
pub fn piped() -> Self {
|
||||
let (r, w) = deno_task_shell::pipe();
|
||||
Self(Some(r), w)
|
||||
|
@ -62,8 +66,8 @@ pub struct TaskIo {
|
|||
impl Default for TaskIo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
stderr: TaskStdio::stderr(),
|
||||
stdout: TaskStdio::stdout(),
|
||||
stderr: TaskStdio::stderr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -78,6 +82,7 @@ pub struct RunTaskOptions<'a> {
|
|||
pub custom_commands: HashMap<String, Rc<dyn ShellCommand>>,
|
||||
pub root_node_modules_dir: Option<&'a Path>,
|
||||
pub stdio: Option<TaskIo>,
|
||||
pub kill_signal: KillSignal,
|
||||
}
|
||||
|
||||
pub type TaskCustomCommands = HashMap<String, Rc<dyn ShellCommand>>;
|
||||
|
@ -96,8 +101,12 @@ pub async fn run_task(
|
|||
.with_context(|| format!("Error parsing script '{}'.", opts.task_name))?;
|
||||
let env_vars =
|
||||
prepare_env_vars(opts.env_vars, opts.init_cwd, opts.root_node_modules_dir);
|
||||
let state =
|
||||
deno_task_shell::ShellState::new(env_vars, opts.cwd, opts.custom_commands);
|
||||
let state = deno_task_shell::ShellState::new(
|
||||
env_vars,
|
||||
opts.cwd,
|
||||
opts.custom_commands,
|
||||
opts.kill_signal,
|
||||
);
|
||||
let stdio = opts.stdio.unwrap_or_default();
|
||||
let (
|
||||
TaskStdio(stdout_read, stdout_write),
|
||||
|
@ -537,6 +546,80 @@ fn resolve_managed_npm_commands(
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
/// Runs a deno task future forwarding any signals received
|
||||
/// to the process.
|
||||
///
|
||||
/// Signal listeners and ctrl+c listening will be setup.
|
||||
pub async fn run_future_forwarding_signals<TOutput>(
|
||||
kill_signal: KillSignal,
|
||||
future: impl std::future::Future<Output = TOutput>,
|
||||
) -> TOutput {
|
||||
fn spawn_future_with_cancellation(
|
||||
future: impl std::future::Future<Output = ()> + 'static,
|
||||
token: CancellationToken,
|
||||
) {
|
||||
deno_core::unsync::spawn(async move {
|
||||
tokio::select! {
|
||||
_ = future => {}
|
||||
_ = token.cancelled() => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let token = CancellationToken::new();
|
||||
let _token_drop_guard = token.clone().drop_guard();
|
||||
let _drop_guard = kill_signal.clone().drop_guard();
|
||||
|
||||
spawn_future_with_cancellation(
|
||||
listen_ctrl_c(kill_signal.clone()),
|
||||
token.clone(),
|
||||
);
|
||||
#[cfg(unix)]
|
||||
spawn_future_with_cancellation(
|
||||
listen_and_forward_all_signals(kill_signal),
|
||||
token,
|
||||
);
|
||||
|
||||
future.await
|
||||
}
|
||||
|
||||
async fn listen_ctrl_c(kill_signal: KillSignal) {
|
||||
while let Ok(()) = tokio::signal::ctrl_c().await {
|
||||
kill_signal.send(deno_task_shell::SignalKind::SIGINT)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
async fn listen_and_forward_all_signals(kill_signal: KillSignal) {
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_runtime::signal::SIGNAL_NUMS;
|
||||
|
||||
// listen and forward every signal we support
|
||||
let mut futures = Vec::with_capacity(SIGNAL_NUMS.len());
|
||||
for signo in SIGNAL_NUMS.iter().copied() {
|
||||
if signo == libc::SIGKILL || signo == libc::SIGSTOP {
|
||||
continue; // skip, can't listen to these
|
||||
}
|
||||
|
||||
let kill_signal = kill_signal.clone();
|
||||
futures.push(
|
||||
async move {
|
||||
let Ok(mut stream) = tokio::signal::unix::signal(
|
||||
tokio::signal::unix::SignalKind::from_raw(signo),
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
let signal_kind: deno_task_shell::SignalKind = signo.into();
|
||||
while let Some(()) = stream.recv().await {
|
||||
kill_signal.send(signal_kind);
|
||||
}
|
||||
}
|
||||
.boxed_local(),
|
||||
)
|
||||
}
|
||||
futures::future::join_all(futures).await;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
|
|
|
@ -209,10 +209,14 @@ pub async fn doc(
|
|||
Default::default()
|
||||
};
|
||||
|
||||
let mut main_entrypoint = None;
|
||||
|
||||
let rewrite_map =
|
||||
if let Some(config_file) = cli_options.start_dir.maybe_deno_json() {
|
||||
let config = config_file.to_exports_config()?;
|
||||
|
||||
main_entrypoint = config.get_resolved(".").ok().flatten();
|
||||
|
||||
let rewrite_map = config
|
||||
.clone()
|
||||
.into_map()
|
||||
|
@ -240,6 +244,7 @@ pub async fn doc(
|
|||
html_options,
|
||||
deno_ns,
|
||||
rewrite_map,
|
||||
main_entrypoint,
|
||||
)
|
||||
} else {
|
||||
let modules_len = doc_nodes_by_url.len();
|
||||
|
@ -383,6 +388,7 @@ fn generate_docs_directory(
|
|||
html_options: &DocHtmlFlag,
|
||||
deno_ns: std::collections::HashMap<Vec<String>, Option<Rc<ShortPath>>>,
|
||||
rewrite_map: Option<IndexMap<ModuleSpecifier, String>>,
|
||||
main_entrypoint: Option<ModuleSpecifier>,
|
||||
) -> Result<(), AnyError> {
|
||||
let cwd = std::env::current_dir().context("Failed to get CWD")?;
|
||||
let output_dir_resolved = cwd.join(&html_options.output);
|
||||
|
@ -415,7 +421,7 @@ fn generate_docs_directory(
|
|||
|
||||
let options = deno_doc::html::GenerateOptions {
|
||||
package_name: html_options.name.clone(),
|
||||
main_entrypoint: None,
|
||||
main_entrypoint,
|
||||
rewrite_map,
|
||||
href_resolver: Rc::new(DocResolver {
|
||||
deno_ns,
|
||||
|
@ -427,33 +433,7 @@ fn generate_docs_directory(
|
|||
symbol_redirect_map,
|
||||
default_symbol_map,
|
||||
markdown_renderer: deno_doc::html::comrak::create_renderer(
|
||||
None,
|
||||
Some(Box::new(|ammonia| {
|
||||
ammonia.add_allowed_classes(
|
||||
"code",
|
||||
&[
|
||||
"language-ts",
|
||||
"language-tsx",
|
||||
"language-typescript",
|
||||
"language-js",
|
||||
"language-jsx",
|
||||
"language-javascript",
|
||||
"language-bash",
|
||||
"language-shell",
|
||||
"language-md",
|
||||
"language-markdown",
|
||||
"language-rs",
|
||||
"language-rust",
|
||||
"language-html",
|
||||
"language-xml",
|
||||
"language-css",
|
||||
"language-json",
|
||||
"language-regex",
|
||||
"language-svg",
|
||||
],
|
||||
);
|
||||
})),
|
||||
None,
|
||||
None, None, None,
|
||||
),
|
||||
markdown_stripper: Rc::new(deno_doc::html::comrak::strip),
|
||||
head_inject: Some(Rc::new(|root| {
|
||||
|
|
|
@ -440,8 +440,10 @@ pub fn format_html(
|
|||
)
|
||||
}
|
||||
_ => {
|
||||
let mut typescript_config =
|
||||
get_resolved_typescript_config(fmt_options);
|
||||
let mut typescript_config_builder =
|
||||
get_typescript_config_builder(fmt_options);
|
||||
typescript_config_builder.file_indent_level(hints.indent_level);
|
||||
let mut typescript_config = typescript_config_builder.build();
|
||||
typescript_config.line_width = hints.print_width as u32;
|
||||
dprint_plugin_typescript::format_text(
|
||||
&path,
|
||||
|
@ -919,9 +921,9 @@ fn files_str(len: usize) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_resolved_typescript_config(
|
||||
fn get_typescript_config_builder(
|
||||
options: &FmtOptionsConfig,
|
||||
) -> dprint_plugin_typescript::configuration::Configuration {
|
||||
) -> dprint_plugin_typescript::configuration::ConfigurationBuilder {
|
||||
let mut builder =
|
||||
dprint_plugin_typescript::configuration::ConfigurationBuilder::new();
|
||||
builder.deno();
|
||||
|
@ -953,7 +955,13 @@ fn get_resolved_typescript_config(
|
|||
});
|
||||
}
|
||||
|
||||
builder.build()
|
||||
builder
|
||||
}
|
||||
|
||||
fn get_resolved_typescript_config(
|
||||
options: &FmtOptionsConfig,
|
||||
) -> dprint_plugin_typescript::configuration::Configuration {
|
||||
get_typescript_config_builder(options).build()
|
||||
}
|
||||
|
||||
fn get_resolved_markdown_config(
|
||||
|
@ -1075,6 +1083,7 @@ fn get_resolved_markup_fmt_config(
|
|||
};
|
||||
|
||||
let language_options = LanguageOptions {
|
||||
script_formatter: Some(markup_fmt::config::ScriptFormatter::Dprint),
|
||||
quotes: Quotes::Double,
|
||||
format_comments: false,
|
||||
script_indent: true,
|
||||
|
|
|
@ -49,19 +49,67 @@ pub async fn info(
|
|||
let module_graph_creator = factory.module_graph_creator().await?;
|
||||
let npm_resolver = factory.npm_resolver().await?;
|
||||
let maybe_lockfile = cli_options.maybe_lockfile();
|
||||
let resolver = factory.workspace_resolver().await?.clone();
|
||||
let npmrc = cli_options.npmrc();
|
||||
let resolver = factory.workspace_resolver().await?;
|
||||
let node_resolver = factory.node_resolver().await?;
|
||||
|
||||
let cwd_url =
|
||||
url::Url::from_directory_path(cli_options.initial_cwd()).unwrap();
|
||||
|
||||
let maybe_import_specifier = if let Some(import_map) =
|
||||
resolver.maybe_import_map()
|
||||
let maybe_import_specifier = if let Ok(resolved) =
|
||||
resolver.resolve(&specifier, &cwd_url)
|
||||
{
|
||||
if let Ok(imports_specifier) = import_map.resolve(&specifier, &cwd_url) {
|
||||
Some(imports_specifier)
|
||||
} else {
|
||||
None
|
||||
match resolved {
|
||||
deno_config::workspace::MappedResolution::Normal {
|
||||
specifier, ..
|
||||
}
|
||||
| deno_config::workspace::MappedResolution::ImportMap {
|
||||
specifier,
|
||||
..
|
||||
}
|
||||
| deno_config::workspace::MappedResolution::WorkspaceJsrPackage {
|
||||
specifier,
|
||||
..
|
||||
} => Some(specifier),
|
||||
deno_config::workspace::MappedResolution::WorkspaceNpmPackage {
|
||||
target_pkg_json,
|
||||
sub_path,
|
||||
..
|
||||
} => Some(node_resolver.resolve_package_subpath_from_deno_module(
|
||||
target_pkg_json.clone().dir_path(),
|
||||
sub_path.as_deref(),
|
||||
Some(&cwd_url),
|
||||
node_resolver::ResolutionMode::Import,
|
||||
node_resolver::NodeResolutionKind::Execution,
|
||||
)?),
|
||||
deno_config::workspace::MappedResolution::PackageJson {
|
||||
alias,
|
||||
sub_path,
|
||||
dep_result,
|
||||
..
|
||||
} => match dep_result.as_ref().map_err(|e| e.clone())? {
|
||||
deno_package_json::PackageJsonDepValue::Workspace(version_req) => {
|
||||
let pkg_folder = resolver
|
||||
.resolve_workspace_pkg_json_folder_for_pkg_json_dep(
|
||||
alias,
|
||||
version_req,
|
||||
)?;
|
||||
Some(node_resolver.resolve_package_subpath_from_deno_module(
|
||||
pkg_folder,
|
||||
sub_path.as_deref(),
|
||||
Some(&cwd_url),
|
||||
node_resolver::ResolutionMode::Import,
|
||||
node_resolver::NodeResolutionKind::Execution,
|
||||
)?)
|
||||
}
|
||||
deno_package_json::PackageJsonDepValue::Req(req) => {
|
||||
Some(ModuleSpecifier::parse(&format!(
|
||||
"npm:{}{}",
|
||||
req,
|
||||
sub_path.map(|s| format!("/{}", s)).unwrap_or_default()
|
||||
))?)
|
||||
}
|
||||
},
|
||||
}
|
||||
} else {
|
||||
None
|
||||
|
@ -235,22 +283,31 @@ fn add_npm_packages_to_json(
|
|||
.get_mut("dependencies")
|
||||
.and_then(|d| d.as_array_mut());
|
||||
if let Some(dependencies) = dependencies {
|
||||
for dep in dependencies.iter_mut() {
|
||||
if let serde_json::Value::Object(dep) = dep {
|
||||
let specifier = dep.get("specifier").and_then(|s| s.as_str());
|
||||
if let Some(specifier) = specifier {
|
||||
if let Ok(npm_ref) = NpmPackageReqReference::from_str(specifier) {
|
||||
if let Ok(pkg) =
|
||||
snapshot.resolve_pkg_from_pkg_req(npm_ref.req())
|
||||
{
|
||||
dep.insert(
|
||||
"npmPackage".to_string(),
|
||||
pkg.id.as_serialized().into(),
|
||||
);
|
||||
}
|
||||
for dep in dependencies.iter_mut().flat_map(|d| d.as_object_mut()) {
|
||||
if let Some(specifier) = dep.get("specifier").and_then(|s| s.as_str())
|
||||
{
|
||||
if let Ok(npm_ref) = NpmPackageReqReference::from_str(specifier) {
|
||||
if let Ok(pkg) = snapshot.resolve_pkg_from_pkg_req(npm_ref.req())
|
||||
{
|
||||
dep.insert(
|
||||
"npmPackage".to_string(),
|
||||
pkg.id.as_serialized().into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// don't show this in the output unless someone needs it
|
||||
if let Some(code) =
|
||||
dep.get_mut("code").and_then(|c| c.as_object_mut())
|
||||
{
|
||||
code.remove("resolutionMode");
|
||||
}
|
||||
if let Some(types) =
|
||||
dep.get_mut("types").and_then(|c| c.as_object_mut())
|
||||
{
|
||||
types.remove("resolutionMode");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use crate::args::RunFlags;
|
|||
use crate::colors;
|
||||
use color_print::cformat;
|
||||
use color_print::cstr;
|
||||
use deno_config::deno_json::NodeModulesDirMode;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json::json;
|
||||
|
@ -251,8 +252,46 @@ Deno.test(function addTest() {
|
|||
Ok(0)
|
||||
}
|
||||
|
||||
fn npm_name_to_create_package(name: &str) -> String {
|
||||
let mut s = "npm:".to_string();
|
||||
|
||||
let mut scoped = false;
|
||||
let mut create = false;
|
||||
|
||||
for (i, ch) in name.char_indices() {
|
||||
if i == 0 {
|
||||
if ch == '@' {
|
||||
scoped = true;
|
||||
} else {
|
||||
create = true;
|
||||
s.push_str("create-");
|
||||
}
|
||||
} else if scoped {
|
||||
if ch == '/' {
|
||||
scoped = false;
|
||||
create = true;
|
||||
s.push_str("/create-");
|
||||
continue;
|
||||
} else if ch == '@' && !create {
|
||||
scoped = false;
|
||||
create = true;
|
||||
s.push_str("/create@");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
s.push(ch);
|
||||
}
|
||||
|
||||
if !create {
|
||||
s.push_str("/create");
|
||||
}
|
||||
|
||||
s
|
||||
}
|
||||
|
||||
async fn init_npm(name: &str, args: Vec<String>) -> Result<i32, AnyError> {
|
||||
let script_name = format!("npm:create-{}", name);
|
||||
let script_name = npm_name_to_create_package(name);
|
||||
|
||||
fn print_manual_usage(script_name: &str, args: &[String]) -> i32 {
|
||||
log::info!("{}", cformat!("You can initialize project manually by running <u>deno run {} {}</> and applying desired permissions.", script_name, args.join(" ")));
|
||||
|
@ -288,6 +327,7 @@ async fn init_npm(name: &str, args: Vec<String>) -> Result<i32, AnyError> {
|
|||
},
|
||||
allow_scripts: PackagesAllowedScripts::All,
|
||||
argv: args,
|
||||
node_modules_dir: Some(NodeModulesDirMode::Auto),
|
||||
subcommand: DenoSubcommand::Run(RunFlags {
|
||||
script: script_name,
|
||||
..Default::default()
|
||||
|
@ -334,3 +374,37 @@ fn create_file(
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::tools::init::npm_name_to_create_package;
|
||||
|
||||
#[test]
|
||||
fn npm_name_to_create_package_test() {
|
||||
// See https://docs.npmjs.com/cli/v8/commands/npm-init#description
|
||||
assert_eq!(
|
||||
npm_name_to_create_package("foo"),
|
||||
"npm:create-foo".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
npm_name_to_create_package("foo@1.0.0"),
|
||||
"npm:create-foo@1.0.0".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
npm_name_to_create_package("@foo"),
|
||||
"npm:@foo/create".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
npm_name_to_create_package("@foo@1.0.0"),
|
||||
"npm:@foo/create@1.0.0".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
npm_name_to_create_package("@foo/bar"),
|
||||
"npm:@foo/create-bar".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
npm_name_to_create_package("@foo/bar@1.0.0"),
|
||||
"npm:@foo/create-bar@1.0.0".to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use std::sync::Arc;
|
|||
use deno_ast::SourceRange;
|
||||
use deno_config::workspace::WorkspaceResolver;
|
||||
use deno_core::anyhow::anyhow;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::source::ResolutionKind;
|
||||
use deno_graph::source::ResolveError;
|
||||
use deno_graph::Range;
|
||||
use deno_lint::diagnostic::LintDiagnosticDetails;
|
||||
|
@ -17,7 +17,7 @@ use deno_lint::diagnostic::LintFix;
|
|||
use deno_lint::diagnostic::LintFixChange;
|
||||
use deno_lint::rules::LintRule;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolution;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolutionMode;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolutionKind;
|
||||
use text_lines::LineAndColumnIndex;
|
||||
|
||||
use crate::graph_util::CliJsrUrlProvider;
|
||||
|
@ -101,16 +101,16 @@ impl LintRule for NoSloppyImportsRule {
|
|||
maybe_npm_resolver: None,
|
||||
});
|
||||
|
||||
for (range, sloppy_import) in resolver.captures.borrow_mut().drain() {
|
||||
for (referrer, sloppy_import) in resolver.captures.borrow_mut().drain() {
|
||||
let start_range =
|
||||
context.text_info().loc_to_source_pos(LineAndColumnIndex {
|
||||
line_index: range.start.line,
|
||||
column_index: range.start.character,
|
||||
line_index: referrer.range.start.line,
|
||||
column_index: referrer.range.start.character,
|
||||
});
|
||||
let end_range =
|
||||
context.text_info().loc_to_source_pos(LineAndColumnIndex {
|
||||
line_index: range.end.line,
|
||||
column_index: range.end.character,
|
||||
line_index: referrer.range.end.line,
|
||||
column_index: referrer.range.end.character,
|
||||
});
|
||||
let source_range = SourceRange::new(start_range, end_range);
|
||||
context.add_diagnostic_details(
|
||||
|
@ -183,7 +183,7 @@ impl<'a> deno_graph::source::Resolver for SloppyImportCaptureResolver<'a> {
|
|||
&self,
|
||||
specifier_text: &str,
|
||||
referrer_range: &Range,
|
||||
mode: ResolutionMode,
|
||||
resolution_kind: ResolutionKind,
|
||||
) -> Result<deno_ast::ModuleSpecifier, deno_graph::source::ResolveError> {
|
||||
let resolution = self
|
||||
.workspace_resolver
|
||||
|
@ -198,9 +198,9 @@ impl<'a> deno_graph::source::Resolver for SloppyImportCaptureResolver<'a> {
|
|||
specifier, ..
|
||||
} => match self.sloppy_imports_resolver.resolve(
|
||||
&specifier,
|
||||
match mode {
|
||||
ResolutionMode::Execution => SloppyImportsResolutionMode::Execution,
|
||||
ResolutionMode::Types => SloppyImportsResolutionMode::Types,
|
||||
match resolution_kind {
|
||||
ResolutionKind::Execution => SloppyImportsResolutionKind::Execution,
|
||||
ResolutionKind::Types => SloppyImportsResolutionKind::Types,
|
||||
},
|
||||
) {
|
||||
Some(res) => {
|
||||
|
|
|
@ -169,7 +169,7 @@ impl Diagnostic for PublishDiagnostic {
|
|||
..
|
||||
}) => DiagnosticLevel::Warning,
|
||||
FastCheck(_) => DiagnosticLevel::Error,
|
||||
SpecifierUnfurl(_) => DiagnosticLevel::Warning,
|
||||
SpecifierUnfurl(d) => d.level(),
|
||||
InvalidPath { .. } => DiagnosticLevel::Error,
|
||||
DuplicatePath { .. } => DiagnosticLevel::Error,
|
||||
UnsupportedFileType { .. } => DiagnosticLevel::Warning,
|
||||
|
@ -187,7 +187,7 @@ impl Diagnostic for PublishDiagnostic {
|
|||
use PublishDiagnostic::*;
|
||||
match &self {
|
||||
FastCheck(diagnostic) => diagnostic.code(),
|
||||
SpecifierUnfurl(diagnostic) => Cow::Borrowed(diagnostic.code()),
|
||||
SpecifierUnfurl(diagnostic) => diagnostic.code(),
|
||||
InvalidPath { .. } => Cow::Borrowed("invalid-path"),
|
||||
DuplicatePath { .. } => Cow::Borrowed("case-insensitive-duplicate-path"),
|
||||
UnsupportedFileType { .. } => Cow::Borrowed("unsupported-file-type"),
|
||||
|
@ -207,7 +207,7 @@ impl Diagnostic for PublishDiagnostic {
|
|||
use PublishDiagnostic::*;
|
||||
match &self {
|
||||
FastCheck(diagnostic) => diagnostic.message(),
|
||||
SpecifierUnfurl(diagnostic) => Cow::Borrowed(diagnostic.message()),
|
||||
SpecifierUnfurl(diagnostic) => diagnostic.message(),
|
||||
InvalidPath { message, .. } => Cow::Borrowed(message.as_str()),
|
||||
DuplicatePath { .. } => {
|
||||
Cow::Borrowed("package path is a case insensitive duplicate of another path in the package")
|
||||
|
@ -234,8 +234,8 @@ impl Diagnostic for PublishDiagnostic {
|
|||
specifier: Cow::Borrowed(&referrer.specifier),
|
||||
text_info: Cow::Borrowed(text_info),
|
||||
source_pos: DiagnosticSourcePos::LineAndCol {
|
||||
line: referrer.start.line,
|
||||
column: referrer.start.character,
|
||||
line: referrer.range.start.line,
|
||||
column: referrer.range.start.character,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -243,17 +243,7 @@ impl Diagnostic for PublishDiagnostic {
|
|||
use PublishDiagnostic::*;
|
||||
match &self {
|
||||
FastCheck(diagnostic) => diagnostic.location(),
|
||||
SpecifierUnfurl(diagnostic) => match diagnostic {
|
||||
SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport {
|
||||
specifier,
|
||||
text_info,
|
||||
range,
|
||||
} => DiagnosticLocation::ModulePosition {
|
||||
specifier: Cow::Borrowed(specifier),
|
||||
text_info: Cow::Borrowed(text_info),
|
||||
source_pos: DiagnosticSourcePos::SourcePos(range.start),
|
||||
},
|
||||
},
|
||||
SpecifierUnfurl(diagnostic) => diagnostic.location(),
|
||||
InvalidPath { path, .. } => {
|
||||
DiagnosticLocation::Path { path: path.clone() }
|
||||
}
|
||||
|
@ -300,7 +290,7 @@ impl Diagnostic for PublishDiagnostic {
|
|||
text_info: &'a SourceTextInfo,
|
||||
referrer: &'a deno_graph::Range,
|
||||
) -> Option<DiagnosticSnippet<'a>> {
|
||||
if referrer.start.line == 0 && referrer.start.character == 0 {
|
||||
if referrer.range.start.line == 0 && referrer.range.start.character == 0 {
|
||||
return None; // no range, probably a jsxImportSource import
|
||||
}
|
||||
|
||||
|
@ -310,12 +300,12 @@ impl Diagnostic for PublishDiagnostic {
|
|||
style: DiagnosticSnippetHighlightStyle::Error,
|
||||
range: DiagnosticSourceRange {
|
||||
start: DiagnosticSourcePos::LineAndCol {
|
||||
line: referrer.start.line,
|
||||
column: referrer.start.character,
|
||||
line: referrer.range.start.line,
|
||||
column: referrer.range.start.character,
|
||||
},
|
||||
end: DiagnosticSourcePos::LineAndCol {
|
||||
line: referrer.end.line,
|
||||
column: referrer.end.character,
|
||||
line: referrer.range.end.line,
|
||||
column: referrer.range.end.character,
|
||||
},
|
||||
},
|
||||
description: Some("the specifier".into()),
|
||||
|
@ -325,24 +315,8 @@ impl Diagnostic for PublishDiagnostic {
|
|||
|
||||
use PublishDiagnostic::*;
|
||||
match &self {
|
||||
FastCheck(diagnostic) => diagnostic.snippet(),
|
||||
SpecifierUnfurl(diagnostic) => match diagnostic {
|
||||
SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport {
|
||||
text_info,
|
||||
range,
|
||||
..
|
||||
} => Some(DiagnosticSnippet {
|
||||
source: Cow::Borrowed(text_info),
|
||||
highlights: vec![DiagnosticSnippetHighlight {
|
||||
style: DiagnosticSnippetHighlightStyle::Warning,
|
||||
range: DiagnosticSourceRange {
|
||||
start: DiagnosticSourcePos::SourcePos(range.start),
|
||||
end: DiagnosticSourcePos::SourcePos(range.end),
|
||||
},
|
||||
description: Some("the unanalyzable dynamic import".into()),
|
||||
}],
|
||||
}),
|
||||
},
|
||||
FastCheck(d) => d.snippet(),
|
||||
SpecifierUnfurl(d) => d.snippet(),
|
||||
InvalidPath { .. } => None,
|
||||
DuplicatePath { .. } => None,
|
||||
UnsupportedFileType { .. } => None,
|
||||
|
@ -380,7 +354,7 @@ impl Diagnostic for PublishDiagnostic {
|
|||
use PublishDiagnostic::*;
|
||||
match &self {
|
||||
FastCheck(diagnostic) => diagnostic.hint(),
|
||||
SpecifierUnfurl(_) => None,
|
||||
SpecifierUnfurl(d) => d.hint(),
|
||||
InvalidPath { .. } => Some(
|
||||
Cow::Borrowed("rename or remove the file, or add it to 'publish.exclude' in the config file"),
|
||||
),
|
||||
|
@ -436,9 +410,9 @@ impl Diagnostic for PublishDiagnostic {
|
|||
None => None,
|
||||
}
|
||||
}
|
||||
SyntaxError(diagnostic) => diagnostic.snippet_fixed(),
|
||||
SyntaxError(d) => d.snippet_fixed(),
|
||||
SpecifierUnfurl(d) => d.snippet_fixed(),
|
||||
FastCheck(_)
|
||||
| SpecifierUnfurl(_)
|
||||
| InvalidPath { .. }
|
||||
| DuplicatePath { .. }
|
||||
| UnsupportedFileType { .. }
|
||||
|
@ -453,16 +427,8 @@ impl Diagnostic for PublishDiagnostic {
|
|||
fn info(&self) -> Cow<'_, [Cow<'_, str>]> {
|
||||
use PublishDiagnostic::*;
|
||||
match &self {
|
||||
FastCheck(diagnostic) => {
|
||||
diagnostic.info()
|
||||
}
|
||||
SpecifierUnfurl(diagnostic) => match diagnostic {
|
||||
SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport { .. } => Cow::Borrowed(&[
|
||||
Cow::Borrowed("after publishing this package, imports from the local import map / package.json do not work"),
|
||||
Cow::Borrowed("dynamic imports that can not be analyzed at publish time will not be rewritten automatically"),
|
||||
Cow::Borrowed("make sure the dynamic import is resolvable at runtime without an import map / package.json")
|
||||
]),
|
||||
},
|
||||
FastCheck(d) => d.info(),
|
||||
SpecifierUnfurl(d) => d.info(),
|
||||
InvalidPath { .. } => Cow::Borrowed(&[
|
||||
Cow::Borrowed("to portably support all platforms, including windows, the allowed characters in package paths are limited"),
|
||||
]),
|
||||
|
@ -476,7 +442,7 @@ impl Diagnostic for PublishDiagnostic {
|
|||
InvalidExternalImport { imported, .. } => Cow::Owned(vec![
|
||||
Cow::Owned(format!("the import was resolved to '{}'", imported)),
|
||||
Cow::Borrowed("this specifier is not allowed to be imported on jsr"),
|
||||
Cow::Borrowed("jsr only supports importing `jsr:`, `npm:`, and `data:` specifiers"),
|
||||
Cow::Borrowed("jsr only supports importing `jsr:`, `npm:`, `data:`, `bun:`, and `node:` specifiers"),
|
||||
]),
|
||||
UnsupportedJsxTsx { .. } => Cow::Owned(vec![
|
||||
Cow::Borrowed("follow https://github.com/jsr-io/jsr/issues/24 for updates"),
|
||||
|
@ -503,10 +469,8 @@ impl Diagnostic for PublishDiagnostic {
|
|||
fn docs_url(&self) -> Option<Cow<'_, str>> {
|
||||
use PublishDiagnostic::*;
|
||||
match &self {
|
||||
FastCheck(diagnostic) => diagnostic.docs_url(),
|
||||
SpecifierUnfurl(diagnostic) => match diagnostic {
|
||||
SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport { .. } => None,
|
||||
},
|
||||
FastCheck(d) => d.docs_url(),
|
||||
SpecifierUnfurl(d) => d.docs_url(),
|
||||
InvalidPath { .. } => {
|
||||
Some(Cow::Borrowed("https://jsr.io/go/invalid-path"))
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ impl GraphDiagnosticsCollector {
|
|||
resolution: &ResolutionResolved| {
|
||||
if visited.insert(resolution.specifier.clone()) {
|
||||
match resolution.specifier.scheme() {
|
||||
"file" | "data" | "node" => {}
|
||||
"file" | "data" | "node" | "bun" => {}
|
||||
"jsr" => {
|
||||
skip_specifiers.insert(resolution.specifier.clone());
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ use base64::Engine;
|
|||
use deno_ast::ModuleSpecifier;
|
||||
use deno_config::deno_json::ConfigFile;
|
||||
use deno_config::workspace::JsrPackageConfig;
|
||||
use deno_config::workspace::PackageJsonDepResolution;
|
||||
use deno_config::workspace::Workspace;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::anyhow::Context;
|
||||
|
@ -44,8 +43,6 @@ use crate::cache::ParsedSourceCache;
|
|||
use crate::factory::CliFactory;
|
||||
use crate::graph_util::ModuleGraphCreator;
|
||||
use crate::http_util::HttpClient;
|
||||
use crate::resolver::CliSloppyImportsResolver;
|
||||
use crate::resolver::SloppyImportsCachedFs;
|
||||
use crate::tools::check::CheckOptions;
|
||||
use crate::tools::lint::collect_no_slow_type_diagnostics;
|
||||
use crate::tools::registry::diagnostics::PublishDiagnostic;
|
||||
|
@ -97,11 +94,10 @@ pub async fn publish(
|
|||
match cli_options.start_dir.maybe_deno_json() {
|
||||
Some(deno_json) => {
|
||||
debug_assert!(!deno_json.is_package());
|
||||
if deno_json.json.name.is_none() {
|
||||
bail!("Missing 'name' field in '{}'.", deno_json.specifier);
|
||||
}
|
||||
error_missing_exports_field(deno_json)?;
|
||||
bail!(
|
||||
"Missing 'name' or 'exports' field in '{}'.",
|
||||
deno_json.specifier
|
||||
);
|
||||
}
|
||||
None => {
|
||||
bail!(
|
||||
|
@ -124,19 +120,8 @@ pub async fn publish(
|
|||
}
|
||||
|
||||
let specifier_unfurler = Arc::new(SpecifierUnfurler::new(
|
||||
if cli_options.unstable_sloppy_imports() {
|
||||
Some(CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(
|
||||
cli_factory.fs().clone(),
|
||||
)))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
cli_options
|
||||
.create_workspace_resolver(
|
||||
cli_factory.file_fetcher()?,
|
||||
PackageJsonDepResolution::Enabled,
|
||||
)
|
||||
.await?,
|
||||
cli_factory.sloppy_imports_resolver()?.cloned(),
|
||||
cli_factory.workspace_resolver().await?.clone(),
|
||||
cli_options.unstable_bare_node_builtins(),
|
||||
));
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::factory::CliFactory;
|
||||
|
@ -37,6 +38,16 @@ pub async fn cache_top_level_deps(
|
|||
factory.file_fetcher()?.clone(),
|
||||
))
|
||||
};
|
||||
let mut graph_permit = factory
|
||||
.main_module_graph_container()
|
||||
.await?
|
||||
.acquire_update_permit()
|
||||
.await;
|
||||
let graph = graph_permit.graph_mut();
|
||||
if let Some(lockfile) = cli_options.maybe_lockfile() {
|
||||
let lockfile = lockfile.lock();
|
||||
crate::graph_util::fill_graph_from_lockfile(graph, &lockfile);
|
||||
}
|
||||
|
||||
let mut roots = Vec::new();
|
||||
|
||||
|
@ -67,13 +78,16 @@ pub async fn cache_top_level_deps(
|
|||
if !seen_reqs.insert(req.req().clone()) {
|
||||
continue;
|
||||
}
|
||||
let resolved_req = graph.packages.mappings().get(req.req());
|
||||
let jsr_resolver = jsr_resolver.clone();
|
||||
info_futures.push(async move {
|
||||
if let Some(nv) = jsr_resolver.req_to_nv(req.req()).await {
|
||||
if let Some(info) = jsr_resolver.package_version_info(&nv).await
|
||||
{
|
||||
return Some((specifier.clone(), info));
|
||||
}
|
||||
let nv = if let Some(req) = resolved_req {
|
||||
Cow::Borrowed(req)
|
||||
} else {
|
||||
Cow::Owned(jsr_resolver.req_to_nv(req.req()).await?)
|
||||
};
|
||||
if let Some(info) = jsr_resolver.package_version_info(&nv).await {
|
||||
return Some((specifier.clone(), info));
|
||||
}
|
||||
None
|
||||
});
|
||||
|
@ -106,12 +120,8 @@ pub async fn cache_top_level_deps(
|
|||
}
|
||||
}
|
||||
}
|
||||
let mut graph_permit = factory
|
||||
.main_module_graph_container()
|
||||
.await?
|
||||
.acquire_update_permit()
|
||||
.await;
|
||||
let graph = graph_permit.graph_mut();
|
||||
drop(info_futures);
|
||||
|
||||
factory
|
||||
.module_load_preparer()
|
||||
.await?
|
||||
|
|
|
@ -18,9 +18,7 @@ use deno_core::futures::stream::FuturesUnordered;
|
|||
use deno_core::futures::FutureExt;
|
||||
use deno_core::futures::StreamExt;
|
||||
use deno_core::serde_json;
|
||||
use deno_graph::FillFromLockfileOptions;
|
||||
use deno_package_json::PackageJsonDepValue;
|
||||
use deno_package_json::PackageJsonDepValueParseError;
|
||||
use deno_package_json::PackageJsonDepsMap;
|
||||
use deno_package_json::PackageJsonRc;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use deno_semver::jsr::JsrPackageReqReference;
|
||||
|
@ -32,7 +30,6 @@ use deno_semver::VersionReq;
|
|||
use import_map::ImportMap;
|
||||
use import_map::ImportMapWithDiagnostics;
|
||||
use import_map::SpecifierMapEntry;
|
||||
use indexmap::IndexMap;
|
||||
use tokio::sync::Semaphore;
|
||||
|
||||
use crate::args::CliLockfile;
|
||||
|
@ -269,94 +266,6 @@ enum PackageJsonDepKind {
|
|||
Dev,
|
||||
}
|
||||
|
||||
type PackageJsonDeps = IndexMap<
|
||||
String,
|
||||
Result<
|
||||
(PackageJsonDepKind, PackageJsonDepValue),
|
||||
PackageJsonDepValueParseError,
|
||||
>,
|
||||
>;
|
||||
|
||||
/// Resolve the package.json's dependencies.
|
||||
// TODO(nathanwhit): Remove once we update deno_package_json with dev deps split out
|
||||
fn resolve_local_package_json_deps(
|
||||
package_json: &PackageJsonRc,
|
||||
) -> PackageJsonDeps {
|
||||
/// Gets the name and raw version constraint for a registry info or
|
||||
/// package.json dependency entry taking into account npm package aliases.
|
||||
fn parse_dep_entry_name_and_raw_version<'a>(
|
||||
key: &'a str,
|
||||
value: &'a str,
|
||||
) -> (&'a str, &'a str) {
|
||||
if let Some(package_and_version) = value.strip_prefix("npm:") {
|
||||
if let Some((name, version)) = package_and_version.rsplit_once('@') {
|
||||
// if empty, then the name was scoped and there's no version
|
||||
if name.is_empty() {
|
||||
(package_and_version, "*")
|
||||
} else {
|
||||
(name, version)
|
||||
}
|
||||
} else {
|
||||
(package_and_version, "*")
|
||||
}
|
||||
} else {
|
||||
(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_entry(
|
||||
key: &str,
|
||||
value: &str,
|
||||
) -> Result<PackageJsonDepValue, PackageJsonDepValueParseError> {
|
||||
if let Some(workspace_key) = value.strip_prefix("workspace:") {
|
||||
let version_req = VersionReq::parse_from_npm(workspace_key)?;
|
||||
return Ok(PackageJsonDepValue::Workspace(version_req));
|
||||
}
|
||||
if value.starts_with("file:")
|
||||
|| value.starts_with("git:")
|
||||
|| value.starts_with("http:")
|
||||
|| value.starts_with("https:")
|
||||
{
|
||||
return Err(PackageJsonDepValueParseError::Unsupported {
|
||||
scheme: value.split(':').next().unwrap().to_string(),
|
||||
});
|
||||
}
|
||||
let (name, version_req) = parse_dep_entry_name_and_raw_version(key, value);
|
||||
let result = VersionReq::parse_from_npm(version_req);
|
||||
match result {
|
||||
Ok(version_req) => Ok(PackageJsonDepValue::Req(PackageReq {
|
||||
name: name.to_string(),
|
||||
version_req,
|
||||
})),
|
||||
Err(err) => Err(PackageJsonDepValueParseError::VersionReq(err)),
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_deps(
|
||||
deps: Option<&IndexMap<String, String>>,
|
||||
result: &mut PackageJsonDeps,
|
||||
kind: PackageJsonDepKind,
|
||||
) {
|
||||
if let Some(deps) = deps {
|
||||
for (key, value) in deps {
|
||||
result.entry(key.to_string()).or_insert_with(|| {
|
||||
parse_entry(key, value).map(|entry| (kind, entry))
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let deps = package_json.dependencies.as_ref();
|
||||
let dev_deps = package_json.dev_dependencies.as_ref();
|
||||
let mut result = IndexMap::new();
|
||||
|
||||
// favors the deps over dev_deps
|
||||
insert_deps(deps, &mut result, PackageJsonDepKind::Normal);
|
||||
insert_deps(dev_deps, &mut result, PackageJsonDepKind::Dev);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn add_deps_from_deno_json(
|
||||
deno_json: &Arc<ConfigFile>,
|
||||
mut filter: impl DepFilter,
|
||||
|
@ -406,40 +315,64 @@ fn add_deps_from_deno_json(
|
|||
|
||||
fn add_deps_from_package_json(
|
||||
package_json: &PackageJsonRc,
|
||||
mut filter: impl DepFilter,
|
||||
filter: impl DepFilter,
|
||||
deps: &mut Vec<Dep>,
|
||||
) {
|
||||
let package_json_deps = resolve_local_package_json_deps(package_json);
|
||||
for (k, v) in package_json_deps {
|
||||
let (package_dep_kind, v) = match v {
|
||||
Ok((k, v)) => (k, v),
|
||||
Err(e) => {
|
||||
log::warn!("bad package json dep value: {e}");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
match v {
|
||||
deno_package_json::PackageJsonDepValue::Req(req) => {
|
||||
let alias = k.as_str();
|
||||
let alias = (alias != req.name).then(|| alias.to_string());
|
||||
if !filter.should_include(alias.as_deref(), &req, DepKind::Npm) {
|
||||
let package_json_deps = package_json.resolve_local_package_json_deps();
|
||||
|
||||
fn iterate(
|
||||
package_json: &PackageJsonRc,
|
||||
mut filter: impl DepFilter,
|
||||
package_dep_kind: PackageJsonDepKind,
|
||||
package_json_deps: PackageJsonDepsMap,
|
||||
deps: &mut Vec<Dep>,
|
||||
) {
|
||||
for (k, v) in package_json_deps {
|
||||
let v = match v {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
log::warn!("bad package json dep value: {e}");
|
||||
continue;
|
||||
}
|
||||
let id = DepId(deps.len());
|
||||
deps.push(Dep {
|
||||
id,
|
||||
kind: DepKind::Npm,
|
||||
location: DepLocation::PackageJson(
|
||||
package_json.clone(),
|
||||
KeyPath::from_parts([package_dep_kind.into(), k.into()]),
|
||||
),
|
||||
req,
|
||||
alias,
|
||||
})
|
||||
};
|
||||
match v {
|
||||
deno_package_json::PackageJsonDepValue::Req(req) => {
|
||||
let alias = k.as_str();
|
||||
let alias = (alias != req.name).then(|| alias.to_string());
|
||||
if !filter.should_include(alias.as_deref(), &req, DepKind::Npm) {
|
||||
continue;
|
||||
}
|
||||
let id = DepId(deps.len());
|
||||
deps.push(Dep {
|
||||
id,
|
||||
kind: DepKind::Npm,
|
||||
location: DepLocation::PackageJson(
|
||||
package_json.clone(),
|
||||
KeyPath::from_parts([package_dep_kind.into(), k.into()]),
|
||||
),
|
||||
req,
|
||||
alias,
|
||||
})
|
||||
}
|
||||
deno_package_json::PackageJsonDepValue::Workspace(_) => continue,
|
||||
}
|
||||
deno_package_json::PackageJsonDepValue::Workspace(_) => continue,
|
||||
}
|
||||
}
|
||||
|
||||
iterate(
|
||||
package_json,
|
||||
filter,
|
||||
PackageJsonDepKind::Normal,
|
||||
package_json_deps.dependencies,
|
||||
deps,
|
||||
);
|
||||
iterate(
|
||||
package_json,
|
||||
filter,
|
||||
PackageJsonDepKind::Dev,
|
||||
package_json_deps.dev_dependencies,
|
||||
deps,
|
||||
);
|
||||
}
|
||||
|
||||
fn deps_from_workspace(
|
||||
|
@ -599,19 +532,8 @@ impl DepManager {
|
|||
// populate the information from the lockfile
|
||||
if let Some(lockfile) = &self.lockfile {
|
||||
let lockfile = lockfile.lock();
|
||||
graph.fill_from_lockfile(FillFromLockfileOptions {
|
||||
redirects: lockfile
|
||||
.content
|
||||
.redirects
|
||||
.iter()
|
||||
.map(|(from, to)| (from.as_str(), to.as_str())),
|
||||
package_specifiers: lockfile
|
||||
.content
|
||||
.packages
|
||||
.specifiers
|
||||
.iter()
|
||||
.map(|(dep, id)| (dep, id.as_str())),
|
||||
});
|
||||
|
||||
crate::graph_util::fill_graph_from_lockfile(graph, &lockfile);
|
||||
}
|
||||
|
||||
let npm_resolver = self.npm_resolver.as_managed().unwrap();
|
||||
|
|
|
@ -1,19 +1,35 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_ast::diagnostics::Diagnostic;
|
||||
use deno_ast::diagnostics::DiagnosticLevel;
|
||||
use deno_ast::diagnostics::DiagnosticLocation;
|
||||
use deno_ast::diagnostics::DiagnosticSnippet;
|
||||
use deno_ast::diagnostics::DiagnosticSnippetHighlight;
|
||||
use deno_ast::diagnostics::DiagnosticSnippetHighlightStyle;
|
||||
use deno_ast::diagnostics::DiagnosticSourcePos;
|
||||
use deno_ast::diagnostics::DiagnosticSourceRange;
|
||||
use deno_ast::ParsedSource;
|
||||
use deno_ast::SourceRange;
|
||||
use deno_ast::SourceTextInfo;
|
||||
use deno_ast::SourceTextProvider;
|
||||
use deno_config::workspace::MappedResolution;
|
||||
use deno_config::workspace::PackageJsonDepResolution;
|
||||
use deno_config::workspace::WorkspaceResolver;
|
||||
use deno_core::anyhow;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_graph::DependencyDescriptor;
|
||||
use deno_graph::DynamicTemplatePart;
|
||||
use deno_graph::ParserModuleAnalyzer;
|
||||
use deno_graph::TypeScriptReference;
|
||||
use deno_package_json::PackageJsonDepValue;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolutionMode;
|
||||
use deno_package_json::PackageJsonDepWorkspaceReq;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolutionKind;
|
||||
use deno_runtime::deno_node::is_builtin_node_module;
|
||||
use deno_semver::Version;
|
||||
use deno_semver::VersionReq;
|
||||
|
||||
use crate::resolver::CliSloppyImportsResolver;
|
||||
|
||||
|
@ -24,34 +40,163 @@ pub enum SpecifierUnfurlerDiagnostic {
|
|||
text_info: SourceTextInfo,
|
||||
range: SourceRange,
|
||||
},
|
||||
ResolvingNpmWorkspacePackage {
|
||||
specifier: ModuleSpecifier,
|
||||
package_name: String,
|
||||
text_info: SourceTextInfo,
|
||||
range: SourceRange,
|
||||
reason: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl SpecifierUnfurlerDiagnostic {
|
||||
pub fn code(&self) -> &'static str {
|
||||
impl Diagnostic for SpecifierUnfurlerDiagnostic {
|
||||
fn level(&self) -> DiagnosticLevel {
|
||||
match self {
|
||||
Self::UnanalyzableDynamicImport { .. } => "unanalyzable-dynamic-import",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn message(&self) -> &'static str {
|
||||
match self {
|
||||
Self::UnanalyzableDynamicImport { .. } => {
|
||||
"unable to analyze dynamic import"
|
||||
SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport { .. } => {
|
||||
DiagnosticLevel::Warning
|
||||
}
|
||||
SpecifierUnfurlerDiagnostic::ResolvingNpmWorkspacePackage { .. } => {
|
||||
DiagnosticLevel::Error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn code(&self) -> Cow<'_, str> {
|
||||
match self {
|
||||
Self::UnanalyzableDynamicImport { .. } => "unanalyzable-dynamic-import",
|
||||
Self::ResolvingNpmWorkspacePackage { .. } => "npm-workspace-package",
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
fn message(&self) -> Cow<'_, str> {
|
||||
match self {
|
||||
Self::UnanalyzableDynamicImport { .. } => {
|
||||
"unable to analyze dynamic import".into()
|
||||
}
|
||||
Self::ResolvingNpmWorkspacePackage {
|
||||
package_name,
|
||||
reason,
|
||||
..
|
||||
} => format!(
|
||||
"failed resolving npm workspace package '{}': {}",
|
||||
package_name, reason
|
||||
)
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn location(&self) -> deno_ast::diagnostics::DiagnosticLocation {
|
||||
match self {
|
||||
SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport {
|
||||
specifier,
|
||||
text_info,
|
||||
range,
|
||||
} => DiagnosticLocation::ModulePosition {
|
||||
specifier: Cow::Borrowed(specifier),
|
||||
text_info: Cow::Borrowed(text_info),
|
||||
source_pos: DiagnosticSourcePos::SourcePos(range.start),
|
||||
},
|
||||
SpecifierUnfurlerDiagnostic::ResolvingNpmWorkspacePackage {
|
||||
specifier,
|
||||
text_info,
|
||||
range,
|
||||
..
|
||||
} => DiagnosticLocation::ModulePosition {
|
||||
specifier: Cow::Borrowed(specifier),
|
||||
text_info: Cow::Borrowed(text_info),
|
||||
source_pos: DiagnosticSourcePos::SourcePos(range.start),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn snippet(&self) -> Option<deno_ast::diagnostics::DiagnosticSnippet<'_>> {
|
||||
match self {
|
||||
SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport {
|
||||
text_info,
|
||||
range,
|
||||
..
|
||||
} => Some(DiagnosticSnippet {
|
||||
source: Cow::Borrowed(text_info),
|
||||
highlights: vec![DiagnosticSnippetHighlight {
|
||||
style: DiagnosticSnippetHighlightStyle::Warning,
|
||||
range: DiagnosticSourceRange {
|
||||
start: DiagnosticSourcePos::SourcePos(range.start),
|
||||
end: DiagnosticSourcePos::SourcePos(range.end),
|
||||
},
|
||||
description: Some("the unanalyzable dynamic import".into()),
|
||||
}],
|
||||
}),
|
||||
SpecifierUnfurlerDiagnostic::ResolvingNpmWorkspacePackage {
|
||||
text_info,
|
||||
range,
|
||||
..
|
||||
} => Some(DiagnosticSnippet {
|
||||
source: Cow::Borrowed(text_info),
|
||||
highlights: vec![DiagnosticSnippetHighlight {
|
||||
style: DiagnosticSnippetHighlightStyle::Warning,
|
||||
range: DiagnosticSourceRange {
|
||||
start: DiagnosticSourcePos::SourcePos(range.start),
|
||||
end: DiagnosticSourcePos::SourcePos(range.end),
|
||||
},
|
||||
description: Some("the unresolved import".into()),
|
||||
}],
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn hint(&self) -> Option<Cow<'_, str>> {
|
||||
match self {
|
||||
SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport { .. } => {
|
||||
None
|
||||
}
|
||||
SpecifierUnfurlerDiagnostic::ResolvingNpmWorkspacePackage { .. } => Some(
|
||||
"make sure the npm workspace package is resolvable and has a version field in its package.json".into()
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn snippet_fixed(
|
||||
&self,
|
||||
) -> Option<deno_ast::diagnostics::DiagnosticSnippet<'_>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn info(&self) -> Cow<'_, [Cow<'_, str>]> {
|
||||
match self {
|
||||
SpecifierUnfurlerDiagnostic::UnanalyzableDynamicImport { .. } => Cow::Borrowed(&[
|
||||
Cow::Borrowed("after publishing this package, imports from the local import map / package.json do not work"),
|
||||
Cow::Borrowed("dynamic imports that can not be analyzed at publish time will not be rewritten automatically"),
|
||||
Cow::Borrowed("make sure the dynamic import is resolvable at runtime without an import map / package.json")
|
||||
]),
|
||||
SpecifierUnfurlerDiagnostic::ResolvingNpmWorkspacePackage { .. } => {
|
||||
Cow::Borrowed(&[])
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn docs_url(&self) -> Option<Cow<'_, str>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
enum UnfurlSpecifierError {
|
||||
Workspace {
|
||||
package_name: String,
|
||||
reason: String,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct SpecifierUnfurler {
|
||||
sloppy_imports_resolver: Option<CliSloppyImportsResolver>,
|
||||
workspace_resolver: WorkspaceResolver,
|
||||
sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
|
||||
workspace_resolver: Arc<WorkspaceResolver>,
|
||||
bare_node_builtins: bool,
|
||||
}
|
||||
|
||||
impl SpecifierUnfurler {
|
||||
pub fn new(
|
||||
sloppy_imports_resolver: Option<CliSloppyImportsResolver>,
|
||||
workspace_resolver: WorkspaceResolver,
|
||||
sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
|
||||
workspace_resolver: Arc<WorkspaceResolver>,
|
||||
bare_node_builtins: bool,
|
||||
) -> Self {
|
||||
debug_assert_eq!(
|
||||
|
@ -65,11 +210,45 @@ impl SpecifierUnfurler {
|
|||
}
|
||||
}
|
||||
|
||||
fn unfurl_specifier_reporting_diagnostic(
|
||||
&self,
|
||||
referrer: &ModuleSpecifier,
|
||||
specifier: &str,
|
||||
text_info: &SourceTextInfo,
|
||||
range: &deno_graph::PositionRange,
|
||||
diagnostic_reporter: &mut dyn FnMut(SpecifierUnfurlerDiagnostic),
|
||||
) -> Option<String> {
|
||||
match self.unfurl_specifier(referrer, specifier) {
|
||||
Ok(maybe_unfurled) => maybe_unfurled,
|
||||
Err(diagnostic) => match diagnostic {
|
||||
UnfurlSpecifierError::Workspace {
|
||||
package_name,
|
||||
reason,
|
||||
} => {
|
||||
let range = to_range(text_info, range);
|
||||
diagnostic_reporter(
|
||||
SpecifierUnfurlerDiagnostic::ResolvingNpmWorkspacePackage {
|
||||
specifier: referrer.clone(),
|
||||
package_name,
|
||||
text_info: text_info.clone(),
|
||||
range: SourceRange::new(
|
||||
text_info.start_pos() + range.start,
|
||||
text_info.start_pos() + range.end,
|
||||
),
|
||||
reason,
|
||||
},
|
||||
);
|
||||
None
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn unfurl_specifier(
|
||||
&self,
|
||||
referrer: &ModuleSpecifier,
|
||||
specifier: &str,
|
||||
) -> Option<String> {
|
||||
) -> Result<Option<String>, UnfurlSpecifierError> {
|
||||
let resolved = if let Ok(resolved) =
|
||||
self.workspace_resolver.resolve(specifier, referrer)
|
||||
{
|
||||
|
@ -120,8 +299,40 @@ impl SpecifierUnfurler {
|
|||
))
|
||||
.ok()
|
||||
}
|
||||
PackageJsonDepValue::Workspace(version_req) => {
|
||||
// todo(#24612): consider warning or error when this is also a jsr package?
|
||||
PackageJsonDepValue::Workspace(workspace_version_req) => {
|
||||
let version_req = match workspace_version_req {
|
||||
PackageJsonDepWorkspaceReq::VersionReq(version_req) => {
|
||||
Cow::Borrowed(version_req)
|
||||
}
|
||||
PackageJsonDepWorkspaceReq::Caret => {
|
||||
let version = self
|
||||
.find_workspace_npm_dep_version(alias)
|
||||
.map_err(|err| UnfurlSpecifierError::Workspace {
|
||||
package_name: alias.to_string(),
|
||||
reason: err.to_string(),
|
||||
})?;
|
||||
// version was validated, so ok to unwrap
|
||||
Cow::Owned(
|
||||
VersionReq::parse_from_npm(&format!("^{}", version))
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
PackageJsonDepWorkspaceReq::Tilde => {
|
||||
let version = self
|
||||
.find_workspace_npm_dep_version(alias)
|
||||
.map_err(|err| UnfurlSpecifierError::Workspace {
|
||||
package_name: alias.to_string(),
|
||||
reason: err.to_string(),
|
||||
})?;
|
||||
// version was validated, so ok to unwrap
|
||||
Cow::Owned(
|
||||
VersionReq::parse_from_npm(&format!("~{}", version))
|
||||
.unwrap(),
|
||||
)
|
||||
}
|
||||
};
|
||||
// todo(#24612): warn when this is also a jsr package telling
|
||||
// people to map the specifiers in the import map
|
||||
ModuleSpecifier::parse(&format!(
|
||||
"npm:{}@{}{}",
|
||||
alias,
|
||||
|
@ -151,10 +362,14 @@ impl SpecifierUnfurler {
|
|||
None if self.bare_node_builtins && is_builtin_node_module(specifier) => {
|
||||
format!("node:{specifier}").parse().unwrap()
|
||||
}
|
||||
None => ModuleSpecifier::options()
|
||||
None => match ModuleSpecifier::options()
|
||||
.base_url(Some(referrer))
|
||||
.parse(specifier)
|
||||
.ok()?,
|
||||
.ok()
|
||||
{
|
||||
Some(value) => value,
|
||||
None => return Ok(None),
|
||||
},
|
||||
};
|
||||
// TODO(lucacasonato): this requires integration in deno_graph first
|
||||
// let resolved = if let Ok(specifier) =
|
||||
|
@ -180,7 +395,7 @@ impl SpecifierUnfurler {
|
|||
let resolved =
|
||||
if let Some(sloppy_imports_resolver) = &self.sloppy_imports_resolver {
|
||||
sloppy_imports_resolver
|
||||
.resolve(&resolved, SloppyImportsResolutionMode::Execution)
|
||||
.resolve(&resolved, SloppyImportsResolutionKind::Execution)
|
||||
.map(|res| res.into_specifier())
|
||||
.unwrap_or(resolved)
|
||||
} else {
|
||||
|
@ -188,7 +403,7 @@ impl SpecifierUnfurler {
|
|||
};
|
||||
let relative_resolved = relative_url(&resolved, referrer);
|
||||
if relative_resolved == specifier {
|
||||
None // nothing to unfurl
|
||||
Ok(None) // nothing to unfurl
|
||||
} else {
|
||||
log::debug!(
|
||||
"Unfurled specifier: {} from {} -> {}",
|
||||
|
@ -196,7 +411,29 @@ impl SpecifierUnfurler {
|
|||
referrer,
|
||||
relative_resolved
|
||||
);
|
||||
Some(relative_resolved)
|
||||
Ok(Some(relative_resolved))
|
||||
}
|
||||
}
|
||||
|
||||
fn find_workspace_npm_dep_version(
|
||||
&self,
|
||||
pkg_name: &str,
|
||||
) -> Result<Version, anyhow::Error> {
|
||||
// todo(#24612): warn when this is also a jsr package telling
|
||||
// people to map the specifiers in the import map
|
||||
let pkg_json = self
|
||||
.workspace_resolver
|
||||
.package_jsons()
|
||||
.find(|pkg| pkg.name.as_deref() == Some(pkg_name))
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!("unable to find npm package in workspace")
|
||||
})?;
|
||||
if let Some(version) = &pkg_json.version {
|
||||
Ok(Version::parse_from_npm(version)?)
|
||||
} else {
|
||||
Err(anyhow::anyhow!(
|
||||
"missing version in package.json of npm package",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,6 +445,7 @@ impl SpecifierUnfurler {
|
|||
text_info: &SourceTextInfo,
|
||||
dep: &deno_graph::DynamicDependencyDescriptor,
|
||||
text_changes: &mut Vec<deno_ast::TextChange>,
|
||||
diagnostic_reporter: &mut dyn FnMut(SpecifierUnfurlerDiagnostic),
|
||||
) -> bool {
|
||||
match &dep.argument {
|
||||
deno_graph::DynamicArgument::String(specifier) => {
|
||||
|
@ -217,8 +455,14 @@ impl SpecifierUnfurler {
|
|||
let Some(relative_index) = maybe_relative_index else {
|
||||
return true; // always say it's analyzable for a string
|
||||
};
|
||||
let unfurled = self.unfurl_specifier(module_url, specifier);
|
||||
if let Some(unfurled) = unfurled {
|
||||
let maybe_unfurled = self.unfurl_specifier_reporting_diagnostic(
|
||||
module_url,
|
||||
specifier,
|
||||
text_info,
|
||||
&dep.argument_range,
|
||||
diagnostic_reporter,
|
||||
);
|
||||
if let Some(unfurled) = maybe_unfurled {
|
||||
let start = range.start + relative_index;
|
||||
text_changes.push(deno_ast::TextChange {
|
||||
range: start..start + specifier.len(),
|
||||
|
@ -238,7 +482,13 @@ impl SpecifierUnfurler {
|
|||
if !specifier.ends_with('/') {
|
||||
return false;
|
||||
}
|
||||
let unfurled = self.unfurl_specifier(module_url, specifier);
|
||||
let unfurled = self.unfurl_specifier_reporting_diagnostic(
|
||||
module_url,
|
||||
specifier,
|
||||
text_info,
|
||||
&dep.argument_range,
|
||||
diagnostic_reporter,
|
||||
);
|
||||
let Some(unfurled) = unfurled else {
|
||||
return true; // nothing to unfurl
|
||||
};
|
||||
|
@ -280,8 +530,15 @@ impl SpecifierUnfurler {
|
|||
let analyze_specifier =
|
||||
|specifier: &str,
|
||||
range: &deno_graph::PositionRange,
|
||||
text_changes: &mut Vec<deno_ast::TextChange>| {
|
||||
if let Some(unfurled) = self.unfurl_specifier(url, specifier) {
|
||||
text_changes: &mut Vec<deno_ast::TextChange>,
|
||||
diagnostic_reporter: &mut dyn FnMut(SpecifierUnfurlerDiagnostic)| {
|
||||
if let Some(unfurled) = self.unfurl_specifier_reporting_diagnostic(
|
||||
url,
|
||||
specifier,
|
||||
text_info,
|
||||
range,
|
||||
diagnostic_reporter,
|
||||
) {
|
||||
text_changes.push(deno_ast::TextChange {
|
||||
range: to_range(text_info, range),
|
||||
new_text: unfurled,
|
||||
|
@ -295,11 +552,17 @@ impl SpecifierUnfurler {
|
|||
&dep.specifier,
|
||||
&dep.specifier_range,
|
||||
&mut text_changes,
|
||||
diagnostic_reporter,
|
||||
);
|
||||
}
|
||||
DependencyDescriptor::Dynamic(dep) => {
|
||||
let success =
|
||||
self.try_unfurl_dynamic_dep(url, text_info, dep, &mut text_changes);
|
||||
let success = self.try_unfurl_dynamic_dep(
|
||||
url,
|
||||
text_info,
|
||||
dep,
|
||||
&mut text_changes,
|
||||
diagnostic_reporter,
|
||||
);
|
||||
|
||||
if !success {
|
||||
let start_pos = text_info.line_start(dep.argument_range.start.line)
|
||||
|
@ -319,20 +582,22 @@ impl SpecifierUnfurler {
|
|||
}
|
||||
for ts_ref in &module_info.ts_references {
|
||||
let specifier_with_range = match ts_ref {
|
||||
TypeScriptReference::Path(range) => range,
|
||||
TypeScriptReference::Types(range) => range,
|
||||
TypeScriptReference::Path(s) => s,
|
||||
TypeScriptReference::Types { specifier, .. } => specifier,
|
||||
};
|
||||
analyze_specifier(
|
||||
&specifier_with_range.text,
|
||||
&specifier_with_range.range,
|
||||
&mut text_changes,
|
||||
diagnostic_reporter,
|
||||
);
|
||||
}
|
||||
for specifier_with_range in &module_info.jsdoc_imports {
|
||||
for jsdoc in &module_info.jsdoc_imports {
|
||||
analyze_specifier(
|
||||
&specifier_with_range.text,
|
||||
&specifier_with_range.range,
|
||||
&jsdoc.specifier.text,
|
||||
&jsdoc.specifier.range,
|
||||
&mut text_changes,
|
||||
diagnostic_reporter,
|
||||
);
|
||||
}
|
||||
if let Some(specifier_with_range) = &module_info.jsx_import_source {
|
||||
|
@ -340,6 +605,7 @@ impl SpecifierUnfurler {
|
|||
&specifier_with_range.text,
|
||||
&specifier_with_range.range,
|
||||
&mut text_changes,
|
||||
diagnostic_reporter,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -458,10 +724,10 @@ mod tests {
|
|||
);
|
||||
let fs = Arc::new(RealFs);
|
||||
let unfurler = SpecifierUnfurler::new(
|
||||
Some(CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(
|
||||
fs,
|
||||
Some(Arc::new(CliSloppyImportsResolver::new(
|
||||
SloppyImportsCachedFs::new(fs),
|
||||
))),
|
||||
workspace_resolver,
|
||||
Arc::new(workspace_resolver),
|
||||
true,
|
||||
);
|
||||
|
||||
|
@ -547,4 +813,114 @@ const warn2 = await import(`${expr}`);
|
|||
assert_eq!(unfurled_source, expected_source);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unfurling_npm_dep_workspace_specifier() {
|
||||
let cwd = testdata_path().join("unfurl").to_path_buf();
|
||||
|
||||
let pkg_json_add = PackageJson::load_from_value(
|
||||
cwd.join("add/package.json"),
|
||||
json!({ "name": "add", "version": "0.1.0", }),
|
||||
);
|
||||
let pkg_json_subtract = PackageJson::load_from_value(
|
||||
cwd.join("subtract/package.json"),
|
||||
json!({ "name": "subtract", "version": "0.2.0", }),
|
||||
);
|
||||
let pkg_json_publishing = PackageJson::load_from_value(
|
||||
cwd.join("publish/package.json"),
|
||||
json!({
|
||||
"name": "@denotest/main",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"add": "workspace:~",
|
||||
"subtract": "workspace:^",
|
||||
"non-existent": "workspace:~",
|
||||
}
|
||||
}),
|
||||
);
|
||||
let root_pkg_json = PackageJson::load_from_value(
|
||||
cwd.join("package.json"),
|
||||
json!({ "workspaces": ["./publish", "./subtract", "./add"] }),
|
||||
);
|
||||
let workspace_resolver = WorkspaceResolver::new_raw(
|
||||
Arc::new(ModuleSpecifier::from_directory_path(&cwd).unwrap()),
|
||||
None,
|
||||
vec![ResolverWorkspaceJsrPackage {
|
||||
is_patch: false,
|
||||
base: ModuleSpecifier::from_directory_path(
|
||||
cwd.join("publish/jsr.json"),
|
||||
)
|
||||
.unwrap(),
|
||||
name: "@denotest/main".to_string(),
|
||||
version: Some(Version::parse_standard("1.0.0").unwrap()),
|
||||
exports: IndexMap::from([(".".to_string(), "mod.ts".to_string())]),
|
||||
}],
|
||||
vec![
|
||||
Arc::new(root_pkg_json),
|
||||
Arc::new(pkg_json_add),
|
||||
Arc::new(pkg_json_subtract),
|
||||
Arc::new(pkg_json_publishing),
|
||||
],
|
||||
deno_config::workspace::PackageJsonDepResolution::Enabled,
|
||||
);
|
||||
let fs = Arc::new(RealFs);
|
||||
let unfurler = SpecifierUnfurler::new(
|
||||
Some(Arc::new(CliSloppyImportsResolver::new(
|
||||
SloppyImportsCachedFs::new(fs),
|
||||
))),
|
||||
Arc::new(workspace_resolver),
|
||||
true,
|
||||
);
|
||||
|
||||
{
|
||||
let source_code = r#"import add from "add";
|
||||
import subtract from "subtract";
|
||||
|
||||
console.log(add, subtract);
|
||||
"#;
|
||||
let specifier =
|
||||
ModuleSpecifier::from_file_path(cwd.join("publish").join("mod.ts"))
|
||||
.unwrap();
|
||||
let source = parse_ast(&specifier, source_code);
|
||||
let mut d = Vec::new();
|
||||
let mut reporter = |diagnostic| d.push(diagnostic);
|
||||
let unfurled_source = unfurler.unfurl(&specifier, &source, &mut reporter);
|
||||
assert_eq!(d.len(), 0);
|
||||
// it will inline the version
|
||||
let expected_source = r#"import add from "npm:add@~0.1.0";
|
||||
import subtract from "npm:subtract@^0.2.0";
|
||||
|
||||
console.log(add, subtract);
|
||||
"#;
|
||||
assert_eq!(unfurled_source, expected_source);
|
||||
}
|
||||
|
||||
{
|
||||
let source_code = r#"import nonExistent from "non-existent";
|
||||
console.log(nonExistent);
|
||||
"#;
|
||||
let specifier =
|
||||
ModuleSpecifier::from_file_path(cwd.join("publish").join("other.ts"))
|
||||
.unwrap();
|
||||
let source = parse_ast(&specifier, source_code);
|
||||
let mut d = Vec::new();
|
||||
let mut reporter = |diagnostic| d.push(diagnostic);
|
||||
let unfurled_source = unfurler.unfurl(&specifier, &source, &mut reporter);
|
||||
assert_eq!(d.len(), 1);
|
||||
match &d[0] {
|
||||
SpecifierUnfurlerDiagnostic::ResolvingNpmWorkspacePackage {
|
||||
package_name,
|
||||
reason,
|
||||
..
|
||||
} => {
|
||||
assert_eq!(package_name, "non-existent");
|
||||
assert_eq!(reason, "unable to find npm package in workspace");
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
// won't make any changes, but the above will be a fatal error
|
||||
assert!(matches!(d[0].level(), DiagnosticLevel::Error));
|
||||
assert_eq!(unfurled_source, source_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,13 +43,13 @@ use deno_core::unsync::spawn;
|
|||
use deno_core::url::Url;
|
||||
use deno_core::LocalInspectorSession;
|
||||
use deno_core::PollEventLoopOptions;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::Position;
|
||||
use deno_graph::PositionRange;
|
||||
use deno_graph::SpecifierWithRange;
|
||||
use deno_runtime::worker::MainWorker;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use node_resolver::NodeResolutionKind;
|
||||
use node_resolver::ResolutionMode;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Match;
|
||||
use regex::Regex;
|
||||
|
@ -701,11 +701,6 @@ impl ReplSession {
|
|||
let mut collector = ImportCollector::new();
|
||||
program.visit_with(&mut collector);
|
||||
|
||||
let referrer_range = deno_graph::Range {
|
||||
specifier: self.referrer.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
};
|
||||
let resolved_imports = collector
|
||||
.imports
|
||||
.iter()
|
||||
|
@ -714,9 +709,10 @@ impl ReplSession {
|
|||
.resolver
|
||||
.resolve(
|
||||
i,
|
||||
&referrer_range,
|
||||
NodeModuleKind::Esm,
|
||||
ResolutionMode::Execution,
|
||||
&self.referrer,
|
||||
deno_graph::Position::zeroed(),
|
||||
ResolutionMode::Import,
|
||||
NodeResolutionKind::Execution,
|
||||
)
|
||||
.ok()
|
||||
.or_else(|| ModuleSpecifier::parse(i).ok())
|
||||
|
|
|
@ -8,6 +8,7 @@ use std::path::PathBuf;
|
|||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use console_static_text::ansi::strip_ansi_codes;
|
||||
use deno_config::workspace::FolderConfigs;
|
||||
use deno_config::workspace::TaskDefinition;
|
||||
use deno_config::workspace::TaskOrScript;
|
||||
|
@ -25,6 +26,7 @@ use deno_core::futures::StreamExt;
|
|||
use deno_core::url::Url;
|
||||
use deno_path_util::normalize_path;
|
||||
use deno_runtime::deno_node::NodeResolver;
|
||||
use deno_task_shell::KillSignal;
|
||||
use deno_task_shell::ShellCommand;
|
||||
use indexmap::IndexMap;
|
||||
use regex::Regex;
|
||||
|
@ -36,6 +38,7 @@ use crate::colors;
|
|||
use crate::factory::CliFactory;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::task_runner;
|
||||
use crate::task_runner::run_future_forwarding_signals;
|
||||
use crate::util::fs::canonicalize_path;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -75,43 +78,29 @@ pub async fn execute_script(
|
|||
let packages_task_configs: Vec<PackageTaskInfo> = if let Some(filter) =
|
||||
&task_flags.filter
|
||||
{
|
||||
let task_name = task_flags.task.as_ref().unwrap();
|
||||
|
||||
// Filter based on package name
|
||||
let package_regex = arg_to_regex(filter)?;
|
||||
let task_regex = arg_to_regex(task_name)?;
|
||||
let workspace = cli_options.workspace();
|
||||
|
||||
let Some(task_name) = &task_flags.task else {
|
||||
print_available_tasks_workspace(
|
||||
cli_options,
|
||||
&package_regex,
|
||||
filter,
|
||||
force_use_pkg_json,
|
||||
task_flags.recursive,
|
||||
)?;
|
||||
|
||||
return Ok(0);
|
||||
};
|
||||
|
||||
let task_regex = arg_to_regex(task_name)?;
|
||||
let mut packages_task_info: Vec<PackageTaskInfo> = vec![];
|
||||
|
||||
fn matches_package(
|
||||
config: &FolderConfigs,
|
||||
force_use_pkg_json: bool,
|
||||
regex: &Regex,
|
||||
) -> bool {
|
||||
if !force_use_pkg_json {
|
||||
if let Some(deno_json) = &config.deno_json {
|
||||
if let Some(name) = &deno_json.json.name {
|
||||
if regex.is_match(name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(package_json) = &config.pkg_json {
|
||||
if let Some(name) = &package_json.name {
|
||||
if regex.is_match(name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
let workspace = cli_options.workspace();
|
||||
for folder in workspace.config_folders() {
|
||||
if !matches_package(folder.1, force_use_pkg_json, &package_regex) {
|
||||
if !task_flags.recursive
|
||||
&& !matches_package(folder.1, force_use_pkg_json, &package_regex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -195,6 +184,7 @@ pub async fn execute_script(
|
|||
&mut std::io::stdout(),
|
||||
&cli_options.start_dir,
|
||||
&tasks_config,
|
||||
None,
|
||||
)?;
|
||||
return Ok(0);
|
||||
};
|
||||
|
@ -225,28 +215,36 @@ pub async fn execute_script(
|
|||
concurrency: no_of_concurrent_tasks.into(),
|
||||
};
|
||||
|
||||
if task_flags.eval {
|
||||
return task_runner
|
||||
.run_deno_task(
|
||||
&Url::from_directory_path(cli_options.initial_cwd()).unwrap(),
|
||||
"",
|
||||
&TaskDefinition {
|
||||
command: task_flags.task.as_ref().unwrap().to_string(),
|
||||
dependencies: vec![],
|
||||
description: None,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
for task_config in &packages_task_configs {
|
||||
let exit_code = task_runner.run_tasks(task_config).await?;
|
||||
if exit_code > 0 {
|
||||
return Ok(exit_code);
|
||||
let kill_signal = KillSignal::default();
|
||||
run_future_forwarding_signals(kill_signal.clone(), async {
|
||||
if task_flags.eval {
|
||||
return task_runner
|
||||
.run_deno_task(
|
||||
&Url::from_directory_path(cli_options.initial_cwd()).unwrap(),
|
||||
"",
|
||||
&TaskDefinition {
|
||||
command: task_flags.task.as_ref().unwrap().to_string(),
|
||||
dependencies: vec![],
|
||||
description: None,
|
||||
},
|
||||
kill_signal,
|
||||
cli_options.argv(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
for task_config in &packages_task_configs {
|
||||
let exit_code = task_runner
|
||||
.run_tasks(task_config, &kill_signal, cli_options.argv())
|
||||
.await?;
|
||||
if exit_code > 0 {
|
||||
return Ok(exit_code);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(0)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
struct RunSingleOptions<'a> {
|
||||
|
@ -254,6 +252,8 @@ struct RunSingleOptions<'a> {
|
|||
script: &'a str,
|
||||
cwd: &'a Path,
|
||||
custom_commands: HashMap<String, Rc<dyn ShellCommand>>,
|
||||
kill_signal: KillSignal,
|
||||
argv: &'a [String],
|
||||
}
|
||||
|
||||
struct TaskRunner<'a> {
|
||||
|
@ -269,9 +269,11 @@ impl<'a> TaskRunner<'a> {
|
|||
pub async fn run_tasks(
|
||||
&self,
|
||||
pkg_tasks_config: &PackageTaskInfo,
|
||||
kill_signal: &KillSignal,
|
||||
argv: &[String],
|
||||
) -> Result<i32, deno_core::anyhow::Error> {
|
||||
match sort_tasks_topo(pkg_tasks_config) {
|
||||
Ok(sorted) => self.run_tasks_in_parallel(sorted).await,
|
||||
Ok(sorted) => self.run_tasks_in_parallel(sorted, kill_signal, argv).await,
|
||||
Err(err) => match err {
|
||||
TaskError::NotFound(name) => {
|
||||
if self.task_flags.is_run {
|
||||
|
@ -300,12 +302,15 @@ impl<'a> TaskRunner<'a> {
|
|||
&mut std::io::stderr(),
|
||||
&self.cli_options.start_dir,
|
||||
tasks_config,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
async fn run_tasks_in_parallel(
|
||||
&self,
|
||||
tasks: Vec<ResolvedTask<'a>>,
|
||||
kill_signal: &KillSignal,
|
||||
args: &[String],
|
||||
) -> Result<i32, deno_core::anyhow::Error> {
|
||||
struct PendingTasksContext<'a> {
|
||||
completed: HashSet<usize>,
|
||||
|
@ -326,13 +331,22 @@ impl<'a> TaskRunner<'a> {
|
|||
fn get_next_task<'b>(
|
||||
&mut self,
|
||||
runner: &'b TaskRunner<'b>,
|
||||
kill_signal: &KillSignal,
|
||||
argv: &'a [String],
|
||||
) -> Option<
|
||||
LocalBoxFuture<'b, Result<(i32, &'a ResolvedTask<'a>), AnyError>>,
|
||||
>
|
||||
where
|
||||
'a: 'b,
|
||||
{
|
||||
for task in self.tasks.iter() {
|
||||
let mut tasks_iter = self.tasks.iter().peekable();
|
||||
while let Some(task) = tasks_iter.next() {
|
||||
let args = if tasks_iter.peek().is_none() {
|
||||
argv
|
||||
} else {
|
||||
&[]
|
||||
};
|
||||
|
||||
if self.completed.contains(&task.id)
|
||||
|| self.running.contains(&task.id)
|
||||
{
|
||||
|
@ -348,15 +362,30 @@ impl<'a> TaskRunner<'a> {
|
|||
}
|
||||
|
||||
self.running.insert(task.id);
|
||||
let kill_signal = kill_signal.clone();
|
||||
return Some(
|
||||
async move {
|
||||
match task.task_or_script {
|
||||
TaskOrScript::Task(_, def) => {
|
||||
runner.run_deno_task(task.folder_url, task.name, def).await
|
||||
runner
|
||||
.run_deno_task(
|
||||
task.folder_url,
|
||||
task.name,
|
||||
def,
|
||||
kill_signal,
|
||||
args,
|
||||
)
|
||||
.await
|
||||
}
|
||||
TaskOrScript::Script(scripts, _) => {
|
||||
runner
|
||||
.run_npm_script(task.folder_url, task.name, scripts)
|
||||
.run_npm_script(
|
||||
task.folder_url,
|
||||
task.name,
|
||||
scripts,
|
||||
kill_signal,
|
||||
args,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
@ -379,7 +408,7 @@ impl<'a> TaskRunner<'a> {
|
|||
|
||||
while context.has_remaining_tasks() {
|
||||
while queue.len() < self.concurrency {
|
||||
if let Some(task) = context.get_next_task(self) {
|
||||
if let Some(task) = context.get_next_task(self, kill_signal, args) {
|
||||
queue.push(task);
|
||||
} else {
|
||||
break;
|
||||
|
@ -408,6 +437,8 @@ impl<'a> TaskRunner<'a> {
|
|||
dir_url: &Url,
|
||||
task_name: &str,
|
||||
definition: &TaskDefinition,
|
||||
kill_signal: KillSignal,
|
||||
argv: &'a [String],
|
||||
) -> Result<i32, deno_core::anyhow::Error> {
|
||||
let cwd = match &self.task_flags.cwd {
|
||||
Some(path) => canonicalize_path(&PathBuf::from(path))
|
||||
|
@ -425,6 +456,8 @@ impl<'a> TaskRunner<'a> {
|
|||
script: &definition.command,
|
||||
cwd: &cwd,
|
||||
custom_commands,
|
||||
kill_signal,
|
||||
argv,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
@ -434,6 +467,8 @@ impl<'a> TaskRunner<'a> {
|
|||
dir_url: &Url,
|
||||
task_name: &str,
|
||||
scripts: &IndexMap<String, String>,
|
||||
kill_signal: KillSignal,
|
||||
argv: &[String],
|
||||
) -> Result<i32, deno_core::anyhow::Error> {
|
||||
// ensure the npm packages are installed if using a managed resolver
|
||||
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
|
||||
|
@ -465,6 +500,8 @@ impl<'a> TaskRunner<'a> {
|
|||
script,
|
||||
cwd: &cwd,
|
||||
custom_commands: custom_commands.clone(),
|
||||
kill_signal: kill_signal.clone(),
|
||||
argv,
|
||||
})
|
||||
.await?;
|
||||
if exit_code > 0 {
|
||||
|
@ -485,11 +522,13 @@ impl<'a> TaskRunner<'a> {
|
|||
script,
|
||||
cwd,
|
||||
custom_commands,
|
||||
kill_signal,
|
||||
argv,
|
||||
} = opts;
|
||||
|
||||
output_task(
|
||||
opts.task_name,
|
||||
&task_runner::get_script_with_args(script, self.cli_options.argv()),
|
||||
&task_runner::get_script_with_args(script, argv),
|
||||
);
|
||||
|
||||
Ok(
|
||||
|
@ -500,9 +539,10 @@ impl<'a> TaskRunner<'a> {
|
|||
env_vars: self.env_vars.clone(),
|
||||
custom_commands,
|
||||
init_cwd: self.cli_options.initial_cwd(),
|
||||
argv: self.cli_options.argv(),
|
||||
argv,
|
||||
root_node_modules_dir: self.npm_resolver.root_node_modules_path(),
|
||||
stdio: None,
|
||||
kill_signal,
|
||||
})
|
||||
.await?
|
||||
.exit_code,
|
||||
|
@ -623,6 +663,32 @@ fn sort_tasks_topo<'a>(
|
|||
Ok(sorted)
|
||||
}
|
||||
|
||||
fn matches_package(
|
||||
config: &FolderConfigs,
|
||||
force_use_pkg_json: bool,
|
||||
regex: &Regex,
|
||||
) -> bool {
|
||||
if !force_use_pkg_json {
|
||||
if let Some(deno_json) = &config.deno_json {
|
||||
if let Some(name) = &deno_json.json.name {
|
||||
if regex.is_match(name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(package_json) = &config.pkg_json {
|
||||
if let Some(name) = &package_json.name {
|
||||
if regex.is_match(name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn output_task(task_name: &str, script: &str) {
|
||||
log::info!(
|
||||
"{} {} {}",
|
||||
|
@ -632,12 +698,70 @@ fn output_task(task_name: &str, script: &str) {
|
|||
);
|
||||
}
|
||||
|
||||
fn print_available_tasks_workspace(
|
||||
cli_options: &Arc<CliOptions>,
|
||||
package_regex: &Regex,
|
||||
filter: &str,
|
||||
force_use_pkg_json: bool,
|
||||
recursive: bool,
|
||||
) -> Result<(), AnyError> {
|
||||
let workspace = cli_options.workspace();
|
||||
|
||||
let mut matched = false;
|
||||
for folder in workspace.config_folders() {
|
||||
if !recursive
|
||||
&& !matches_package(folder.1, force_use_pkg_json, package_regex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
matched = true;
|
||||
|
||||
let member_dir = workspace.resolve_member_dir(folder.0);
|
||||
let mut tasks_config = member_dir.to_tasks_config()?;
|
||||
|
||||
let mut pkg_name = folder
|
||||
.1
|
||||
.deno_json
|
||||
.as_ref()
|
||||
.and_then(|deno| deno.json.name.clone())
|
||||
.or(folder.1.pkg_json.as_ref().and_then(|pkg| pkg.name.clone()));
|
||||
|
||||
if force_use_pkg_json {
|
||||
tasks_config = tasks_config.with_only_pkg_json();
|
||||
pkg_name = folder.1.pkg_json.as_ref().and_then(|pkg| pkg.name.clone());
|
||||
}
|
||||
|
||||
print_available_tasks(
|
||||
&mut std::io::stdout(),
|
||||
&cli_options.start_dir,
|
||||
&tasks_config,
|
||||
pkg_name,
|
||||
)?;
|
||||
}
|
||||
|
||||
if !matched {
|
||||
log::warn!(
|
||||
"{}",
|
||||
colors::red(format!("No package name matched the filter '{}' in available 'deno.json' or 'package.json' files.", filter))
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_available_tasks(
|
||||
writer: &mut dyn std::io::Write,
|
||||
workspace_dir: &Arc<WorkspaceDirectory>,
|
||||
tasks_config: &WorkspaceTasksConfig,
|
||||
pkg_name: Option<String>,
|
||||
) -> Result<(), std::io::Error> {
|
||||
writeln!(writer, "{}", colors::green("Available tasks:"))?;
|
||||
let heading = if let Some(s) = pkg_name {
|
||||
format!("Available tasks ({}):", colors::cyan(s))
|
||||
} else {
|
||||
"Available tasks:".to_string()
|
||||
};
|
||||
|
||||
writeln!(writer, "{}", colors::green(heading))?;
|
||||
let is_cwd_root_dir = tasks_config.root.is_none();
|
||||
|
||||
if tasks_config.is_empty() {
|
||||
|
@ -721,22 +845,48 @@ fn print_available_tasks(
|
|||
)?;
|
||||
if let Some(description) = &desc.task.description {
|
||||
let slash_slash = colors::italic_gray("//");
|
||||
writeln!(
|
||||
writer,
|
||||
" {slash_slash} {}",
|
||||
colors::italic_gray(description)
|
||||
)?;
|
||||
for line in description.lines() {
|
||||
writeln!(
|
||||
writer,
|
||||
" {slash_slash} {}",
|
||||
colors::italic_gray(strip_ansi_codes_and_escape_control_chars(line))
|
||||
)?;
|
||||
}
|
||||
}
|
||||
writeln!(writer, " {}", desc.task.command)?;
|
||||
writeln!(
|
||||
writer,
|
||||
" {}",
|
||||
strip_ansi_codes_and_escape_control_chars(&desc.task.command)
|
||||
)?;
|
||||
if !desc.task.dependencies.is_empty() {
|
||||
let dependencies = desc
|
||||
.task
|
||||
.dependencies
|
||||
.into_iter()
|
||||
.map(|d| strip_ansi_codes_and_escape_control_chars(&d))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
writeln!(
|
||||
writer,
|
||||
" {} {}",
|
||||
colors::gray("depends on:"),
|
||||
colors::cyan(desc.task.dependencies.join(", "))
|
||||
colors::cyan(dependencies)
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn strip_ansi_codes_and_escape_control_chars(s: &str) -> String {
|
||||
strip_ansi_codes(s)
|
||||
.chars()
|
||||
.map(|c| match c {
|
||||
'\n' => "\\n".to_string(),
|
||||
'\r' => "\\r".to_string(),
|
||||
'\t' => "\\t".to_string(),
|
||||
c if c.is_control() => format!("\\x{:02x}", c as u8),
|
||||
c => c.to_string(),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
|
@ -681,14 +681,18 @@ delete Object.prototype.__proto__;
|
|||
getNewLine() {
|
||||
return "\n";
|
||||
},
|
||||
resolveTypeReferenceDirectives(
|
||||
typeDirectiveNames,
|
||||
resolveTypeReferenceDirectiveReferences(
|
||||
typeDirectiveReferences,
|
||||
containingFilePath,
|
||||
redirectedReference,
|
||||
options,
|
||||
containingFileMode,
|
||||
containingSourceFile,
|
||||
_reusedNames,
|
||||
) {
|
||||
return typeDirectiveNames.map((arg) => {
|
||||
const isCjs =
|
||||
containingSourceFile?.impliedNodeFormat === ts.ModuleKind.CommonJS;
|
||||
/** @type {Array<ts.ResolvedTypeReferenceDirectiveWithFailedLookupLocations>} */
|
||||
const result = typeDirectiveReferences.map((arg) => {
|
||||
/** @type {ts.FileReference} */
|
||||
const fileReference = typeof arg === "string"
|
||||
? {
|
||||
|
@ -701,16 +705,28 @@ delete Object.prototype.__proto__;
|
|||
/** @type {[string, ts.Extension] | undefined} */
|
||||
const resolved = ops.op_resolve(
|
||||
containingFilePath,
|
||||
containingFileMode === ts.ModuleKind.CommonJS,
|
||||
[fileReference.fileName],
|
||||
[
|
||||
[
|
||||
fileReference.resolutionMode == null
|
||||
? isCjs
|
||||
: fileReference.resolutionMode === ts.ModuleKind.CommonJS,
|
||||
fileReference.fileName,
|
||||
],
|
||||
],
|
||||
)?.[0];
|
||||
if (resolved) {
|
||||
return {
|
||||
primary: true,
|
||||
resolvedFileName: resolved[0],
|
||||
resolvedTypeReferenceDirective: {
|
||||
primary: true,
|
||||
resolvedFileName: resolved[0],
|
||||
// todo(dsherret): we should probably be setting this
|
||||
isExternalLibraryImport: undefined,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return undefined;
|
||||
return {
|
||||
resolvedTypeReferenceDirective: undefined,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return ts.resolveTypeReferenceDirective(
|
||||
|
@ -720,41 +736,56 @@ delete Object.prototype.__proto__;
|
|||
host,
|
||||
redirectedReference,
|
||||
undefined,
|
||||
containingFileMode ?? fileReference.resolutionMode,
|
||||
).resolvedTypeReferenceDirective;
|
||||
containingSourceFile?.impliedNodeFormat ??
|
||||
fileReference.resolutionMode,
|
||||
);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
resolveModuleNames(
|
||||
specifiers,
|
||||
resolveModuleNameLiterals(
|
||||
moduleLiterals,
|
||||
base,
|
||||
_reusedNames,
|
||||
_redirectedReference,
|
||||
_options,
|
||||
compilerOptions,
|
||||
containingSourceFile,
|
||||
_reusedNames,
|
||||
) {
|
||||
const specifiers = moduleLiterals.map((literal) => [
|
||||
ts.getModeForUsageLocation(
|
||||
containingSourceFile,
|
||||
literal,
|
||||
compilerOptions,
|
||||
) === ts.ModuleKind.CommonJS,
|
||||
literal.text,
|
||||
]);
|
||||
if (logDebug) {
|
||||
debug(`host.resolveModuleNames()`);
|
||||
debug(` base: ${base}`);
|
||||
debug(` specifiers: ${specifiers.join(", ")}`);
|
||||
debug(` specifiers: ${specifiers.map((s) => s[1]).join(", ")}`);
|
||||
}
|
||||
/** @type {Array<[string, ts.Extension] | undefined>} */
|
||||
const resolved = ops.op_resolve(
|
||||
base,
|
||||
containingSourceFile?.impliedNodeFormat === ts.ModuleKind.CommonJS,
|
||||
specifiers,
|
||||
);
|
||||
if (resolved) {
|
||||
/** @type {Array<ts.ResolvedModuleWithFailedLookupLocations>} */
|
||||
const result = resolved.map((item) => {
|
||||
if (item) {
|
||||
const [resolvedFileName, extension] = item;
|
||||
return {
|
||||
resolvedFileName,
|
||||
extension,
|
||||
isExternalLibraryImport: false,
|
||||
resolvedModule: {
|
||||
resolvedFileName,
|
||||
extension,
|
||||
// todo(dsherret): we should probably be setting this
|
||||
isExternalLibraryImport: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
return {
|
||||
resolvedModule: undefined,
|
||||
};
|
||||
});
|
||||
result.length = specifiers.length;
|
||||
return result;
|
||||
|
|
2
cli/tsc/dts/lib.dom.d.ts
vendored
2
cli/tsc/dts/lib.dom.d.ts
vendored
|
@ -18277,7 +18277,7 @@ declare var ReadableStream: {
|
|||
new(underlyingSource: UnderlyingByteSource, strategy?: { highWaterMark?: number }): ReadableStream<Uint8Array>;
|
||||
new<R = any>(underlyingSource: UnderlyingDefaultSource<R>, strategy?: QueuingStrategy<R>): ReadableStream<R>;
|
||||
new<R = any>(underlyingSource?: UnderlyingSource<R>, strategy?: QueuingStrategy<R>): ReadableStream<R>;
|
||||
from<R>(asyncIterable: AsyncIterable<R> | Iterable<R | PromiseLike<R>>): ReadableStream<R>;
|
||||
from<R>(asyncIterable: AsyncIterable<R> | Iterable<R | PromiseLike<R>> & object): ReadableStream<R>;
|
||||
};
|
||||
|
||||
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/ReadableStreamBYOBReader) */
|
||||
|
|
|
@ -41,8 +41,8 @@ use deno_semver::npm::NpmPackageReqReference;
|
|||
use node_resolver::errors::NodeJsErrorCode;
|
||||
use node_resolver::errors::NodeJsErrorCoded;
|
||||
use node_resolver::errors::PackageSubpathResolveError;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use node_resolver::NodeResolutionMode;
|
||||
use node_resolver::NodeResolutionKind;
|
||||
use node_resolver::ResolutionMode;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
|
@ -656,17 +656,21 @@ fn op_load_inner(
|
|||
}
|
||||
Module::Npm(_) | Module::Node(_) => None,
|
||||
Module::External(module) => {
|
||||
// means it's Deno code importing an npm module
|
||||
let specifier = node::resolve_specifier_into_node_modules(
|
||||
&module.specifier,
|
||||
&deno_fs::RealFs,
|
||||
);
|
||||
Some(Cow::Owned(load_from_node_modules(
|
||||
&specifier,
|
||||
state.maybe_npm.as_ref(),
|
||||
&mut media_type,
|
||||
&mut is_cjs,
|
||||
)?))
|
||||
if module.specifier.scheme() != "file" {
|
||||
None
|
||||
} else {
|
||||
// means it's Deno code importing an npm module
|
||||
let specifier = node::resolve_specifier_into_node_modules(
|
||||
&module.specifier,
|
||||
&deno_fs::RealFs,
|
||||
);
|
||||
Some(Cow::Owned(load_from_node_modules(
|
||||
&specifier,
|
||||
state.maybe_npm.as_ref(),
|
||||
&mut media_type,
|
||||
&mut is_cjs,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(npm) = state
|
||||
|
@ -703,10 +707,9 @@ pub struct ResolveArgs {
|
|||
/// The base specifier that the supplied specifier strings should be resolved
|
||||
/// relative to.
|
||||
pub base: String,
|
||||
/// If the base is cjs.
|
||||
pub is_base_cjs: bool,
|
||||
/// A list of specifiers that should be resolved.
|
||||
pub specifiers: Vec<String>,
|
||||
/// (is_cjs: bool, raw_specifier: String)
|
||||
pub specifiers: Vec<(bool, String)>,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
|
@ -714,17 +717,9 @@ pub struct ResolveArgs {
|
|||
fn op_resolve(
|
||||
state: &mut OpState,
|
||||
#[string] base: String,
|
||||
is_base_cjs: bool,
|
||||
#[serde] specifiers: Vec<String>,
|
||||
#[serde] specifiers: Vec<(bool, String)>,
|
||||
) -> Result<Vec<(String, &'static str)>, AnyError> {
|
||||
op_resolve_inner(
|
||||
state,
|
||||
ResolveArgs {
|
||||
base,
|
||||
is_base_cjs,
|
||||
specifiers,
|
||||
},
|
||||
)
|
||||
op_resolve_inner(state, ResolveArgs { base, specifiers })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -735,11 +730,6 @@ fn op_resolve_inner(
|
|||
let state = state.borrow_mut::<State>();
|
||||
let mut resolved: Vec<(String, &'static str)> =
|
||||
Vec::with_capacity(args.specifiers.len());
|
||||
let referrer_kind = if args.is_base_cjs {
|
||||
NodeModuleKind::Cjs
|
||||
} else {
|
||||
NodeModuleKind::Esm
|
||||
};
|
||||
let referrer = if let Some(remapped_specifier) =
|
||||
state.remapped_specifiers.get(&args.base)
|
||||
{
|
||||
|
@ -752,7 +742,7 @@ fn op_resolve_inner(
|
|||
)?
|
||||
};
|
||||
let referrer_module = state.graph.get(&referrer);
|
||||
for specifier in args.specifiers {
|
||||
for (is_cjs, specifier) in args.specifiers {
|
||||
if specifier.starts_with("node:") {
|
||||
resolved.push((
|
||||
MISSING_DEPENDENCY_SPECIFIER.to_string(),
|
||||
|
@ -771,13 +761,20 @@ fn op_resolve_inner(
|
|||
.and_then(|m| m.js())
|
||||
.and_then(|m| m.dependencies_prefer_fast_check().get(&specifier))
|
||||
.and_then(|d| d.maybe_type.ok().or_else(|| d.maybe_code.ok()));
|
||||
let resolution_mode = if is_cjs {
|
||||
ResolutionMode::Require
|
||||
} else {
|
||||
ResolutionMode::Import
|
||||
};
|
||||
|
||||
let maybe_result = match resolved_dep {
|
||||
Some(ResolutionResolved { specifier, .. }) => {
|
||||
resolve_graph_specifier_types(
|
||||
specifier,
|
||||
&referrer,
|
||||
referrer_kind,
|
||||
// we could get this from the resolved dep, but for now assume
|
||||
// the value resolved in TypeScript is better
|
||||
resolution_mode,
|
||||
state,
|
||||
)?
|
||||
}
|
||||
|
@ -785,7 +782,7 @@ fn op_resolve_inner(
|
|||
match resolve_non_graph_specifier_types(
|
||||
&specifier,
|
||||
&referrer,
|
||||
referrer_kind,
|
||||
resolution_mode,
|
||||
state,
|
||||
) {
|
||||
Ok(maybe_result) => maybe_result,
|
||||
|
@ -852,7 +849,7 @@ fn op_resolve_inner(
|
|||
fn resolve_graph_specifier_types(
|
||||
specifier: &ModuleSpecifier,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
resolution_mode: ResolutionMode,
|
||||
state: &State,
|
||||
) -> Result<Option<(ModuleSpecifier, MediaType)>, AnyError> {
|
||||
let graph = &state.graph;
|
||||
|
@ -908,8 +905,8 @@ fn resolve_graph_specifier_types(
|
|||
&package_folder,
|
||||
module.nv_reference.sub_path(),
|
||||
Some(referrer),
|
||||
referrer_kind,
|
||||
NodeResolutionMode::Types,
|
||||
resolution_mode,
|
||||
NodeResolutionKind::Types,
|
||||
);
|
||||
let maybe_url = match res_result {
|
||||
Ok(url) => Some(url),
|
||||
|
@ -949,7 +946,7 @@ enum ResolveNonGraphSpecifierTypesError {
|
|||
fn resolve_non_graph_specifier_types(
|
||||
raw_specifier: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
resolution_mode: ResolutionMode,
|
||||
state: &State,
|
||||
) -> Result<
|
||||
Option<(ModuleSpecifier, MediaType)>,
|
||||
|
@ -967,8 +964,8 @@ fn resolve_non_graph_specifier_types(
|
|||
.resolve(
|
||||
raw_specifier,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
NodeResolutionMode::Types,
|
||||
resolution_mode,
|
||||
NodeResolutionKind::Types,
|
||||
)
|
||||
.ok()
|
||||
.map(|res| res.into_url()),
|
||||
|
@ -976,7 +973,7 @@ fn resolve_non_graph_specifier_types(
|
|||
} else if let Ok(npm_req_ref) =
|
||||
NpmPackageReqReference::from_str(raw_specifier)
|
||||
{
|
||||
debug_assert_eq!(referrer_kind, NodeModuleKind::Esm);
|
||||
debug_assert_eq!(resolution_mode, ResolutionMode::Import);
|
||||
// todo(dsherret): add support for injecting this in the graph so
|
||||
// we don't need this special code here.
|
||||
// This could occur when resolving npm:@types/node when it is
|
||||
|
@ -988,8 +985,8 @@ fn resolve_non_graph_specifier_types(
|
|||
&package_folder,
|
||||
npm_req_ref.sub_path(),
|
||||
Some(referrer),
|
||||
referrer_kind,
|
||||
NodeResolutionMode::Types,
|
||||
resolution_mode,
|
||||
NodeResolutionKind::Types,
|
||||
);
|
||||
let maybe_url = match res_result {
|
||||
Ok(url) => Some(url),
|
||||
|
@ -1388,8 +1385,7 @@ mod tests {
|
|||
&mut state,
|
||||
ResolveArgs {
|
||||
base: "https://deno.land/x/a.ts".to_string(),
|
||||
is_base_cjs: false,
|
||||
specifiers: vec!["./b.ts".to_string()],
|
||||
specifiers: vec![(false, "./b.ts".to_string())],
|
||||
},
|
||||
)
|
||||
.expect("should have invoked op");
|
||||
|
@ -1408,8 +1404,7 @@ mod tests {
|
|||
&mut state,
|
||||
ResolveArgs {
|
||||
base: "https://deno.land/x/a.ts".to_string(),
|
||||
is_base_cjs: false,
|
||||
specifiers: vec!["./bad.ts".to_string()],
|
||||
specifiers: vec![(false, "./bad.ts".to_string())],
|
||||
},
|
||||
)
|
||||
.expect("should have not errored");
|
||||
|
|
|
@ -659,7 +659,7 @@ impl LaxSingleProcessFsFlag {
|
|||
//
|
||||
// This uses a blocking task because we use a single threaded
|
||||
// runtime and this is time sensitive so we don't want it to update
|
||||
// at the whims of of whatever is occurring on the runtime thread.
|
||||
// at the whims of whatever is occurring on the runtime thread.
|
||||
spawn_blocking({
|
||||
let token = token.clone();
|
||||
let last_updated_path = last_updated_path.clone();
|
||||
|
|
|
@ -29,7 +29,7 @@ impl log::Log for CliLogger {
|
|||
// thread's state
|
||||
DrawThread::hide();
|
||||
self.0.log(record);
|
||||
deno_runtime::ops::otel::handle_log(record);
|
||||
deno_telemetry::handle_log(record);
|
||||
DrawThread::show();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,19 +51,6 @@ pub fn get_extension(file_path: &Path) -> Option<String> {
|
|||
.map(|e| e.to_lowercase());
|
||||
}
|
||||
|
||||
pub fn get_atomic_dir_path(file_path: &Path) -> PathBuf {
|
||||
let rand = gen_rand_path_component();
|
||||
let new_file_name = format!(
|
||||
".{}_{}",
|
||||
file_path
|
||||
.file_name()
|
||||
.map(|f| f.to_string_lossy())
|
||||
.unwrap_or(Cow::Borrowed("")),
|
||||
rand
|
||||
);
|
||||
file_path.with_file_name(new_file_name)
|
||||
}
|
||||
|
||||
pub fn get_atomic_file_path(file_path: &Path) -> PathBuf {
|
||||
let rand = gen_rand_path_component();
|
||||
let extension = format!("{rand}.tmp");
|
||||
|
|
|
@ -3,11 +3,9 @@
|
|||
mod async_flag;
|
||||
mod sync_read_async_write_lock;
|
||||
mod task_queue;
|
||||
mod value_creator;
|
||||
|
||||
pub use async_flag::AsyncFlag;
|
||||
pub use deno_core::unsync::sync::AtomicFlag;
|
||||
pub use sync_read_async_write_lock::SyncReadAsyncWriteLock;
|
||||
pub use task_queue::TaskQueue;
|
||||
pub use task_queue::TaskQueuePermit;
|
||||
pub use value_creator::MultiRuntimeAsyncValueCreator;
|
||||
|
|
|
@ -1,213 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_core::futures::future::BoxFuture;
|
||||
use deno_core::futures::future::LocalBoxFuture;
|
||||
use deno_core::futures::future::Shared;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use tokio::task::JoinError;
|
||||
|
||||
type JoinResult<TResult> = Result<TResult, Arc<JoinError>>;
|
||||
type CreateFutureFn<TResult> =
|
||||
Box<dyn Fn() -> LocalBoxFuture<'static, TResult> + Send + Sync>;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct State<TResult> {
|
||||
retry_index: usize,
|
||||
future: Option<Shared<BoxFuture<'static, JoinResult<TResult>>>>,
|
||||
}
|
||||
|
||||
/// Attempts to create a shared value asynchronously on one tokio runtime while
|
||||
/// many runtimes are requesting the value.
|
||||
///
|
||||
/// This is only useful when the value needs to get created once across
|
||||
/// many runtimes.
|
||||
///
|
||||
/// This handles the case where the tokio runtime creating the value goes down
|
||||
/// while another one is waiting on the value.
|
||||
pub struct MultiRuntimeAsyncValueCreator<TResult: Send + Clone + 'static> {
|
||||
create_future: CreateFutureFn<TResult>,
|
||||
state: Mutex<State<TResult>>,
|
||||
}
|
||||
|
||||
impl<TResult: Send + Clone + 'static> std::fmt::Debug
|
||||
for MultiRuntimeAsyncValueCreator<TResult>
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MultiRuntimeAsyncValueCreator").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<TResult: Send + Clone + 'static> MultiRuntimeAsyncValueCreator<TResult> {
|
||||
pub fn new(create_future: CreateFutureFn<TResult>) -> Self {
|
||||
Self {
|
||||
state: Mutex::new(State {
|
||||
retry_index: 0,
|
||||
future: None,
|
||||
}),
|
||||
create_future,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get(&self) -> TResult {
|
||||
let (mut future, mut retry_index) = {
|
||||
let mut state = self.state.lock();
|
||||
let future = match &state.future {
|
||||
Some(future) => future.clone(),
|
||||
None => {
|
||||
let future = self.create_shared_future();
|
||||
state.future = Some(future.clone());
|
||||
future
|
||||
}
|
||||
};
|
||||
(future, state.retry_index)
|
||||
};
|
||||
|
||||
loop {
|
||||
let result = future.await;
|
||||
|
||||
match result {
|
||||
Ok(result) => return result,
|
||||
Err(join_error) => {
|
||||
if join_error.is_cancelled() {
|
||||
let mut state = self.state.lock();
|
||||
|
||||
if state.retry_index == retry_index {
|
||||
// we were the first one to retry, so create a new future
|
||||
// that we'll run from the current runtime
|
||||
state.retry_index += 1;
|
||||
state.future = Some(self.create_shared_future());
|
||||
}
|
||||
|
||||
retry_index = state.retry_index;
|
||||
future = state.future.as_ref().unwrap().clone();
|
||||
|
||||
// just in case we're stuck in a loop
|
||||
if retry_index > 1000 {
|
||||
panic!("Something went wrong.") // should never happen
|
||||
}
|
||||
} else {
|
||||
panic!("{}", join_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_shared_future(
|
||||
&self,
|
||||
) -> Shared<BoxFuture<'static, JoinResult<TResult>>> {
|
||||
let future = (self.create_future)();
|
||||
deno_core::unsync::spawn(future)
|
||||
.map(|result| result.map_err(Arc::new))
|
||||
.boxed()
|
||||
.shared()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use deno_core::unsync::spawn;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn single_runtime() {
|
||||
let value_creator = MultiRuntimeAsyncValueCreator::new(Box::new(|| {
|
||||
async { 1 }.boxed_local()
|
||||
}));
|
||||
let value = value_creator.get().await;
|
||||
assert_eq!(value, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_runtimes() {
|
||||
let value_creator =
|
||||
Arc::new(MultiRuntimeAsyncValueCreator::new(Box::new(|| {
|
||||
async {
|
||||
tokio::task::yield_now().await;
|
||||
1
|
||||
}
|
||||
.boxed_local()
|
||||
})));
|
||||
let handles = (0..3)
|
||||
.map(|_| {
|
||||
let value_creator = value_creator.clone();
|
||||
std::thread::spawn(|| {
|
||||
create_runtime().block_on(async move { value_creator.get().await })
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
for handle in handles {
|
||||
assert_eq!(handle.join().unwrap(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_runtimes_first_never_finishes() {
|
||||
let is_first_run = Arc::new(Mutex::new(true));
|
||||
let (tx, rx) = std::sync::mpsc::channel::<()>();
|
||||
let value_creator = Arc::new(MultiRuntimeAsyncValueCreator::new({
|
||||
let is_first_run = is_first_run.clone();
|
||||
Box::new(move || {
|
||||
let is_first_run = is_first_run.clone();
|
||||
let tx = tx.clone();
|
||||
async move {
|
||||
let is_first_run = {
|
||||
let mut is_first_run = is_first_run.lock();
|
||||
let initial_value = *is_first_run;
|
||||
*is_first_run = false;
|
||||
tx.send(()).unwrap();
|
||||
initial_value
|
||||
};
|
||||
if is_first_run {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(30_000)).await;
|
||||
panic!("TIMED OUT"); // should not happen
|
||||
} else {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
1
|
||||
}
|
||||
.boxed_local()
|
||||
})
|
||||
}));
|
||||
std::thread::spawn({
|
||||
let value_creator = value_creator.clone();
|
||||
let is_first_run = is_first_run.clone();
|
||||
move || {
|
||||
create_runtime().block_on(async {
|
||||
let value_creator = value_creator.clone();
|
||||
// spawn a task that will never complete
|
||||
spawn(async move { value_creator.get().await });
|
||||
// wait for the task to set is_first_run to false
|
||||
while *is_first_run.lock() {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
|
||||
}
|
||||
// now exit the runtime while the value_creator is still pending
|
||||
})
|
||||
}
|
||||
});
|
||||
let handle = {
|
||||
let value_creator = value_creator.clone();
|
||||
std::thread::spawn(|| {
|
||||
create_runtime().block_on(async move {
|
||||
let value_creator = value_creator.clone();
|
||||
rx.recv().unwrap();
|
||||
// even though the other runtime shutdown, this get() should
|
||||
// recover and still get the value
|
||||
value_creator.get().await
|
||||
})
|
||||
})
|
||||
};
|
||||
assert_eq!(handle.join().unwrap(), 1);
|
||||
}
|
||||
|
||||
fn create_runtime() -> tokio::runtime::Runtime {
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
|
@ -11,6 +11,15 @@ use deno_core::ModuleSourceCode;
|
|||
static SOURCE_MAP_PREFIX: &[u8] =
|
||||
b"//# sourceMappingURL=data:application/json;base64,";
|
||||
|
||||
#[inline(always)]
|
||||
pub fn from_utf8_lossy_cow(bytes: Cow<[u8]>) -> Cow<str> {
|
||||
match bytes {
|
||||
Cow::Borrowed(bytes) => String::from_utf8_lossy(bytes),
|
||||
Cow::Owned(bytes) => Cow::Owned(from_utf8_lossy_owned(bytes)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn from_utf8_lossy_owned(bytes: Vec<u8>) -> String {
|
||||
match String::from_utf8_lossy(&bytes) {
|
||||
Cow::Owned(code) => code,
|
||||
|
|
|
@ -30,7 +30,6 @@ use deno_runtime::deno_tls::RootCertStoreProvider;
|
|||
use deno_runtime::deno_web::BlobStore;
|
||||
use deno_runtime::fmt_errors::format_js_error;
|
||||
use deno_runtime::inspector_server::InspectorServer;
|
||||
use deno_runtime::ops::otel::OtelConfig;
|
||||
use deno_runtime::ops::process::NpmProcessStateProviderRc;
|
||||
use deno_runtime::ops::worker_host::CreateWebWorkerCb;
|
||||
use deno_runtime::web_worker::WebWorker;
|
||||
|
@ -43,9 +42,10 @@ use deno_runtime::BootstrapOptions;
|
|||
use deno_runtime::WorkerExecutionMode;
|
||||
use deno_runtime::WorkerLogLevel;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use deno_telemetry::OtelConfig;
|
||||
use deno_terminal::colors;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use node_resolver::NodeResolutionMode;
|
||||
use node_resolver::NodeResolutionKind;
|
||||
use node_resolver::ResolutionMode;
|
||||
use tokio::select;
|
||||
|
||||
use crate::args::CliLockfile;
|
||||
|
@ -698,8 +698,8 @@ impl CliMainWorkerFactory {
|
|||
package_folder,
|
||||
sub_path,
|
||||
/* referrer */ None,
|
||||
NodeModuleKind::Esm,
|
||||
NodeResolutionMode::Execution,
|
||||
ResolutionMode::Import,
|
||||
NodeResolutionKind::Execution,
|
||||
)?;
|
||||
if specifier
|
||||
.to_file_path()
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_broadcast_channel"
|
||||
version = "0.173.0"
|
||||
version = "0.174.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
2
ext/cache/Cargo.toml
vendored
2
ext/cache/Cargo.toml
vendored
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_cache"
|
||||
version = "0.111.0"
|
||||
version = "0.112.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_canvas"
|
||||
version = "0.48.0"
|
||||
version = "0.49.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_console"
|
||||
version = "0.179.0"
|
||||
version = "0.180.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_cron"
|
||||
version = "0.59.0"
|
||||
version = "0.60.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_crypto"
|
||||
version = "0.193.0"
|
||||
version = "0.194.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -286,8 +286,13 @@ function mixinBody(prototype, bodySymbol, mimeTypeSymbol) {
|
|||
*/
|
||||
get() {
|
||||
webidl.assertBranded(this, prototype);
|
||||
if (this[bodySymbol] !== null) {
|
||||
return this[bodySymbol].consumed();
|
||||
try {
|
||||
if (this[bodySymbol] !== null) {
|
||||
return this[bodySymbol].consumed();
|
||||
}
|
||||
} catch (_) {
|
||||
// Request is closed.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
import { core, primordials } from "ext:core/mod.js";
|
||||
import {
|
||||
op_fetch,
|
||||
op_fetch_promise_is_settled,
|
||||
op_fetch_send,
|
||||
op_wasm_streaming_feed,
|
||||
op_wasm_streaming_set_url,
|
||||
|
@ -28,7 +29,9 @@ const {
|
|||
PromisePrototypeThen,
|
||||
PromisePrototypeCatch,
|
||||
SafeArrayIterator,
|
||||
SafePromisePrototypeFinally,
|
||||
String,
|
||||
StringPrototypeEndsWith,
|
||||
StringPrototypeStartsWith,
|
||||
StringPrototypeToLowerCase,
|
||||
TypeError,
|
||||
|
@ -55,6 +58,17 @@ import {
|
|||
toInnerResponse,
|
||||
} from "ext:deno_fetch/23_response.js";
|
||||
import * as abortSignal from "ext:deno_web/03_abort_signal.js";
|
||||
import {
|
||||
endSpan,
|
||||
enterSpan,
|
||||
exitSpan,
|
||||
Span,
|
||||
TRACING_ENABLED,
|
||||
} from "ext:deno_telemetry/telemetry.ts";
|
||||
import {
|
||||
updateSpanFromRequest,
|
||||
updateSpanFromResponse,
|
||||
} from "ext:deno_telemetry/util.ts";
|
||||
|
||||
const REQUEST_BODY_HEADER_NAMES = [
|
||||
"content-encoding",
|
||||
|
@ -63,6 +77,12 @@ const REQUEST_BODY_HEADER_NAMES = [
|
|||
"content-type",
|
||||
];
|
||||
|
||||
const REDIRECT_SENSITIVE_HEADER_NAMES = [
|
||||
"authorization",
|
||||
"proxy-authorization",
|
||||
"cookie",
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {number} rid
|
||||
* @returns {Promise<{ status: number, statusText: string, headers: [string, string][], url: string, responseRid: number, error: [string, string]? }>}
|
||||
|
@ -250,12 +270,14 @@ function httpRedirectFetch(request, response, terminator) {
|
|||
if (locationHeaders.length === 0) {
|
||||
return response;
|
||||
}
|
||||
|
||||
const currentURL = new URL(request.currentUrl());
|
||||
const locationURL = new URL(
|
||||
locationHeaders[0][1],
|
||||
response.url() ?? undefined,
|
||||
);
|
||||
if (locationURL.hash === "") {
|
||||
locationURL.hash = request.currentUrl().hash;
|
||||
locationURL.hash = currentURL.hash;
|
||||
}
|
||||
if (locationURL.protocol !== "https:" && locationURL.protocol !== "http:") {
|
||||
return networkError("Can not redirect to a non HTTP(s) url");
|
||||
|
@ -294,6 +316,28 @@ function httpRedirectFetch(request, response, terminator) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Drop confidential headers when redirecting to a less secure protocol
|
||||
// or to a different domain that is not a superdomain
|
||||
if (
|
||||
locationURL.protocol !== currentURL.protocol &&
|
||||
locationURL.protocol !== "https:" ||
|
||||
locationURL.host !== currentURL.host &&
|
||||
!isSubdomain(locationURL.host, currentURL.host)
|
||||
) {
|
||||
for (let i = 0; i < request.headerList.length; i++) {
|
||||
if (
|
||||
ArrayPrototypeIncludes(
|
||||
REDIRECT_SENSITIVE_HEADER_NAMES,
|
||||
byteLowerCase(request.headerList[i][0]),
|
||||
)
|
||||
) {
|
||||
ArrayPrototypeSplice(request.headerList, i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (request.body !== null) {
|
||||
const res = extractBody(request.body.source);
|
||||
request.body = res.body;
|
||||
|
@ -307,93 +351,138 @@ function httpRedirectFetch(request, response, terminator) {
|
|||
* @param {RequestInit} init
|
||||
*/
|
||||
function fetch(input, init = { __proto__: null }) {
|
||||
// There is an async dispatch later that causes a stack trace disconnect.
|
||||
// We reconnect it by assigning the result of that dispatch to `opPromise`,
|
||||
// awaiting `opPromise` in an inner function also named `fetch()` and
|
||||
// returning the result from that.
|
||||
let opPromise = undefined;
|
||||
// 1.
|
||||
const result = new Promise((resolve, reject) => {
|
||||
const prefix = "Failed to execute 'fetch'";
|
||||
webidl.requiredArguments(arguments.length, 1, prefix);
|
||||
// 2.
|
||||
const requestObject = new Request(input, init);
|
||||
// 3.
|
||||
const request = toInnerRequest(requestObject);
|
||||
// 4.
|
||||
if (requestObject.signal.aborted) {
|
||||
reject(abortFetch(request, null, requestObject.signal.reason));
|
||||
return;
|
||||
let span;
|
||||
try {
|
||||
if (TRACING_ENABLED) {
|
||||
span = new Span("fetch", { kind: 2 });
|
||||
enterSpan(span);
|
||||
}
|
||||
|
||||
// 7.
|
||||
let responseObject = null;
|
||||
// 9.
|
||||
let locallyAborted = false;
|
||||
// 10.
|
||||
function onabort() {
|
||||
locallyAborted = true;
|
||||
reject(
|
||||
abortFetch(request, responseObject, requestObject.signal.reason),
|
||||
);
|
||||
}
|
||||
requestObject.signal[abortSignal.add](onabort);
|
||||
// There is an async dispatch later that causes a stack trace disconnect.
|
||||
// We reconnect it by assigning the result of that dispatch to `opPromise`,
|
||||
// awaiting `opPromise` in an inner function also named `fetch()` and
|
||||
// returning the result from that.
|
||||
let opPromise = undefined;
|
||||
// 1.
|
||||
const result = new Promise((resolve, reject) => {
|
||||
const prefix = "Failed to execute 'fetch'";
|
||||
webidl.requiredArguments(arguments.length, 1, prefix);
|
||||
// 2.
|
||||
const requestObject = new Request(input, init);
|
||||
|
||||
if (!requestObject.headers.has("Accept")) {
|
||||
ArrayPrototypePush(request.headerList, ["Accept", "*/*"]);
|
||||
}
|
||||
if (span) {
|
||||
updateSpanFromRequest(span, requestObject);
|
||||
}
|
||||
|
||||
if (!requestObject.headers.has("Accept-Language")) {
|
||||
ArrayPrototypePush(request.headerList, ["Accept-Language", "*"]);
|
||||
}
|
||||
// 3.
|
||||
const request = toInnerRequest(requestObject);
|
||||
// 4.
|
||||
if (requestObject.signal.aborted) {
|
||||
reject(abortFetch(request, null, requestObject.signal.reason));
|
||||
return;
|
||||
}
|
||||
// 7.
|
||||
let responseObject = null;
|
||||
// 9.
|
||||
let locallyAborted = false;
|
||||
// 10.
|
||||
function onabort() {
|
||||
locallyAborted = true;
|
||||
reject(
|
||||
abortFetch(request, responseObject, requestObject.signal.reason),
|
||||
);
|
||||
}
|
||||
requestObject.signal[abortSignal.add](onabort);
|
||||
|
||||
// 12.
|
||||
opPromise = PromisePrototypeCatch(
|
||||
PromisePrototypeThen(
|
||||
mainFetch(request, false, requestObject.signal),
|
||||
(response) => {
|
||||
// 12.1.
|
||||
if (locallyAborted) return;
|
||||
// 12.2.
|
||||
if (response.aborted) {
|
||||
reject(
|
||||
abortFetch(
|
||||
request,
|
||||
responseObject,
|
||||
requestObject.signal.reason,
|
||||
),
|
||||
);
|
||||
if (!requestObject.headers.has("Accept")) {
|
||||
ArrayPrototypePush(request.headerList, ["Accept", "*/*"]);
|
||||
}
|
||||
|
||||
if (!requestObject.headers.has("Accept-Language")) {
|
||||
ArrayPrototypePush(request.headerList, ["Accept-Language", "*"]);
|
||||
}
|
||||
|
||||
// 12.
|
||||
opPromise = PromisePrototypeCatch(
|
||||
PromisePrototypeThen(
|
||||
mainFetch(request, false, requestObject.signal),
|
||||
(response) => {
|
||||
// 12.1.
|
||||
if (locallyAborted) return;
|
||||
// 12.2.
|
||||
if (response.aborted) {
|
||||
reject(
|
||||
abortFetch(
|
||||
request,
|
||||
responseObject,
|
||||
requestObject.signal.reason,
|
||||
),
|
||||
);
|
||||
requestObject.signal[abortSignal.remove](onabort);
|
||||
return;
|
||||
}
|
||||
// 12.3.
|
||||
if (response.type === "error") {
|
||||
const err = new TypeError(
|
||||
"Fetch failed: " + (response.error ?? "unknown error"),
|
||||
);
|
||||
reject(err);
|
||||
requestObject.signal[abortSignal.remove](onabort);
|
||||
return;
|
||||
}
|
||||
responseObject = fromInnerResponse(response, "immutable");
|
||||
|
||||
if (span) {
|
||||
updateSpanFromResponse(span, responseObject);
|
||||
}
|
||||
|
||||
resolve(responseObject);
|
||||
requestObject.signal[abortSignal.remove](onabort);
|
||||
return;
|
||||
}
|
||||
// 12.3.
|
||||
if (response.type === "error") {
|
||||
const err = new TypeError(
|
||||
"Fetch failed: " + (response.error ?? "unknown error"),
|
||||
);
|
||||
reject(err);
|
||||
requestObject.signal[abortSignal.remove](onabort);
|
||||
return;
|
||||
}
|
||||
responseObject = fromInnerResponse(response, "immutable");
|
||||
resolve(responseObject);
|
||||
},
|
||||
),
|
||||
(err) => {
|
||||
reject(err);
|
||||
requestObject.signal[abortSignal.remove](onabort);
|
||||
},
|
||||
),
|
||||
(err) => {
|
||||
reject(err);
|
||||
requestObject.signal[abortSignal.remove](onabort);
|
||||
},
|
||||
);
|
||||
});
|
||||
if (opPromise) {
|
||||
PromisePrototypeCatch(result, () => {});
|
||||
return (async function fetch() {
|
||||
await opPromise;
|
||||
return result;
|
||||
})();
|
||||
);
|
||||
});
|
||||
|
||||
if (opPromise) {
|
||||
PromisePrototypeCatch(result, () => {});
|
||||
return (async function fetch() {
|
||||
try {
|
||||
await opPromise;
|
||||
return result;
|
||||
} finally {
|
||||
if (span) {
|
||||
endSpan(span);
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
// We need to end the span when the promise settles.
|
||||
// WPT has a test that aborted fetch is settled in the same tick.
|
||||
// This means we cannot wrap the promise if it is already settled.
|
||||
// But this is OK, because we can just immediately end the span
|
||||
// in that case.
|
||||
if (span) {
|
||||
// XXX: This should always be true, otherwise `opPromise` would be present.
|
||||
if (op_fetch_promise_is_settled(result)) {
|
||||
// It's already settled.
|
||||
endSpan(span);
|
||||
} else {
|
||||
// Not settled yet, we can return a new wrapper promise.
|
||||
return SafePromisePrototypeFinally(result, () => {
|
||||
endSpan(span);
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} finally {
|
||||
if (span) {
|
||||
exitSpan(span);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function abortFetch(request, responseObject, error) {
|
||||
|
@ -410,6 +499,19 @@ function abortFetch(request, responseObject, error) {
|
|||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given string is a subdomain of the given domain.
|
||||
*
|
||||
* @param {String} subdomain
|
||||
* @param {String} domain
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function isSubdomain(subdomain, domain) {
|
||||
const dot = subdomain.length - domain.length - 1;
|
||||
return dot > 0 && subdomain[dot] === "." &&
|
||||
StringPrototypeEndsWith(subdomain, domain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the Response argument to the WebAssembly streaming APIs, after
|
||||
* resolving if it was passed as a promise. This function should be registered
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_fetch"
|
||||
version = "0.203.0"
|
||||
version = "0.204.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -7,10 +7,7 @@ use std::task::Poll;
|
|||
use std::task::{self};
|
||||
use std::vec;
|
||||
|
||||
use hickory_resolver::error::ResolveError;
|
||||
use hickory_resolver::name_server::GenericConnector;
|
||||
use hickory_resolver::name_server::TokioRuntimeProvider;
|
||||
use hickory_resolver::AsyncResolver;
|
||||
use hickory_resolver::name_server::TokioConnectionProvider;
|
||||
use hyper_util::client::legacy::connect::dns::GaiResolver;
|
||||
use hyper_util::client::legacy::connect::dns::Name;
|
||||
use tokio::task::JoinHandle;
|
||||
|
@ -21,7 +18,7 @@ pub enum Resolver {
|
|||
/// A resolver using blocking `getaddrinfo` calls in a threadpool.
|
||||
Gai(GaiResolver),
|
||||
/// hickory-resolver's userspace resolver.
|
||||
Hickory(AsyncResolver<GenericConnector<TokioRuntimeProvider>>),
|
||||
Hickory(hickory_resolver::Resolver<TokioConnectionProvider>),
|
||||
}
|
||||
|
||||
impl Default for Resolver {
|
||||
|
@ -36,14 +33,14 @@ impl Resolver {
|
|||
}
|
||||
|
||||
/// Create a [`AsyncResolver`] from system conf.
|
||||
pub fn hickory() -> Result<Self, ResolveError> {
|
||||
pub fn hickory() -> Result<Self, hickory_resolver::ResolveError> {
|
||||
Ok(Self::Hickory(
|
||||
hickory_resolver::AsyncResolver::tokio_from_system_conf()?,
|
||||
hickory_resolver::Resolver::tokio_from_system_conf()?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn hickory_from_async_resolver(
|
||||
resolver: AsyncResolver<GenericConnector<TokioRuntimeProvider>>,
|
||||
pub fn hickory_from_resolver(
|
||||
resolver: hickory_resolver::Resolver<TokioConnectionProvider>,
|
||||
) -> Self {
|
||||
Self::Hickory(resolver)
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ use deno_core::futures::TryFutureExt;
|
|||
use deno_core::op2;
|
||||
use deno_core::url;
|
||||
use deno_core::url::Url;
|
||||
use deno_core::v8;
|
||||
use deno_core::AsyncRefCell;
|
||||
use deno_core::AsyncResult;
|
||||
use deno_core::BufView;
|
||||
|
@ -141,6 +142,7 @@ deno_core::extension!(deno_fetch,
|
|||
op_fetch_send,
|
||||
op_utf8_to_byte_string,
|
||||
op_fetch_custom_client<FP>,
|
||||
op_fetch_promise_is_settled,
|
||||
],
|
||||
esm = [
|
||||
"20_headers.js",
|
||||
|
@ -1206,3 +1208,8 @@ pub fn extract_authority(url: &mut Url) -> Option<(String, Option<String>)> {
|
|||
|
||||
None
|
||||
}
|
||||
|
||||
#[op2(fast)]
|
||||
fn op_fetch_promise_is_settled(promise: v8::Local<v8::Promise>) -> bool {
|
||||
promise.state() != v8::PromiseState::Pending
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ fn test_userspace_resolver() {
|
|||
// use `localhost` to ensure dns step happens.
|
||||
let addr = format!("localhost:{}", src_addr.port());
|
||||
|
||||
let hickory = hickory_resolver::AsyncResolver::tokio(
|
||||
let hickory = hickory_resolver::Resolver::tokio(
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
);
|
||||
|
@ -52,7 +52,7 @@ fn test_userspace_resolver() {
|
|||
addr.clone(),
|
||||
"https",
|
||||
http::Version::HTTP_2,
|
||||
dns::Resolver::hickory_from_async_resolver(hickory),
|
||||
dns::Resolver::hickory_from_resolver(hickory),
|
||||
)
|
||||
.await;
|
||||
assert_eq!(thread_counter.load(SeqCst), 0, "userspace resolver shouldn't spawn new threads.");
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_ffi"
|
||||
version = "0.166.0"
|
||||
version = "0.167.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -578,7 +578,7 @@ class FsFile {
|
|||
this.#rid = rid;
|
||||
if (!symbol || symbol !== SymbolFor("Deno.internal.FsFile")) {
|
||||
throw new TypeError(
|
||||
"`Deno.FsFile` cannot be constructed, use `Deno.open()` or `Deno.openSync()` instead.",
|
||||
"'Deno.FsFile' cannot be constructed, use 'Deno.open()' or 'Deno.openSync()' instead",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -713,11 +713,15 @@ function checkOpenOptions(options) {
|
|||
(val) => val === true,
|
||||
).length === 0
|
||||
) {
|
||||
throw new Error("OpenOptions requires at least one option to be true");
|
||||
throw new Error(
|
||||
"'options' requires at least one option to be true",
|
||||
);
|
||||
}
|
||||
|
||||
if (options.truncate && !options.write) {
|
||||
throw new Error("'truncate' option requires 'write' option");
|
||||
throw new Error(
|
||||
"'truncate' option requires 'write' to be true",
|
||||
);
|
||||
}
|
||||
|
||||
const createOrCreateNewWithoutWriteOrAppend =
|
||||
|
@ -726,7 +730,7 @@ function checkOpenOptions(options) {
|
|||
|
||||
if (createOrCreateNewWithoutWriteOrAppend) {
|
||||
throw new Error(
|
||||
"'create' or 'createNew' options require 'write' or 'append' option",
|
||||
"'create' or 'createNew' options require 'write' or 'append' to be true",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_fs"
|
||||
version = "0.89.0"
|
||||
version = "0.90.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// Allow using Arc for this module.
|
||||
#![allow(clippy::disallowed_types)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Error;
|
||||
|
@ -457,11 +458,11 @@ impl FileSystem for InMemoryFs {
|
|||
&self,
|
||||
path: &Path,
|
||||
_access_check: Option<AccessCheckCb>,
|
||||
) -> FsResult<Vec<u8>> {
|
||||
) -> FsResult<Cow<'static, [u8]>> {
|
||||
let entry = self.get_entry(path);
|
||||
match entry {
|
||||
Some(entry) => match &*entry {
|
||||
PathEntry::File(data) => Ok(data.clone()),
|
||||
PathEntry::File(data) => Ok(Cow::Owned(data.clone())),
|
||||
PathEntry::Dir => Err(FsError::Io(Error::new(
|
||||
ErrorKind::InvalidInput,
|
||||
"Is a directory",
|
||||
|
@ -474,7 +475,7 @@ impl FileSystem for InMemoryFs {
|
|||
&'a self,
|
||||
path: PathBuf,
|
||||
access_check: Option<AccessCheckCb<'a>>,
|
||||
) -> FsResult<Vec<u8>> {
|
||||
) -> FsResult<Cow<'static, [u8]>> {
|
||||
self.read_file_sync(&path, access_check)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use core::str;
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
@ -288,7 +289,7 @@ pub trait FileSystem: std::fmt::Debug + MaybeSend + MaybeSync {
|
|||
&self,
|
||||
path: &Path,
|
||||
access_check: Option<AccessCheckCb>,
|
||||
) -> FsResult<Vec<u8>> {
|
||||
) -> FsResult<Cow<'static, [u8]>> {
|
||||
let options = OpenOptions::read();
|
||||
let file = self.open_sync(path, options, access_check)?;
|
||||
let buf = file.read_all_sync()?;
|
||||
|
@ -298,7 +299,7 @@ pub trait FileSystem: std::fmt::Debug + MaybeSend + MaybeSync {
|
|||
&'a self,
|
||||
path: PathBuf,
|
||||
access_check: Option<AccessCheckCb<'a>>,
|
||||
) -> FsResult<Vec<u8>> {
|
||||
) -> FsResult<Cow<'static, [u8]>> {
|
||||
let options = OpenOptions::read();
|
||||
let file = self.open_async(path, options, access_check).await?;
|
||||
let buf = file.read_all_async().await?;
|
||||
|
@ -327,17 +328,25 @@ pub trait FileSystem: std::fmt::Debug + MaybeSend + MaybeSync {
|
|||
&self,
|
||||
path: &Path,
|
||||
access_check: Option<AccessCheckCb>,
|
||||
) -> FsResult<String> {
|
||||
) -> FsResult<Cow<'static, str>> {
|
||||
let buf = self.read_file_sync(path, access_check)?;
|
||||
Ok(string_from_utf8_lossy(buf))
|
||||
Ok(string_from_cow_utf8_lossy(buf))
|
||||
}
|
||||
async fn read_text_file_lossy_async<'a>(
|
||||
&'a self,
|
||||
path: PathBuf,
|
||||
access_check: Option<AccessCheckCb<'a>>,
|
||||
) -> FsResult<String> {
|
||||
) -> FsResult<Cow<'static, str>> {
|
||||
let buf = self.read_file_async(path, access_check).await?;
|
||||
Ok(string_from_utf8_lossy(buf))
|
||||
Ok(string_from_cow_utf8_lossy(buf))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn string_from_cow_utf8_lossy(buf: Cow<'static, [u8]>) -> Cow<'static, str> {
|
||||
match buf {
|
||||
Cow::Owned(buf) => Cow::Owned(string_from_utf8_lossy(buf)),
|
||||
Cow::Borrowed(buf) => String::from_utf8_lossy(buf),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ pub use crate::interface::OpenOptions;
|
|||
pub use crate::ops::FsOpsError;
|
||||
pub use crate::ops::FsOpsErrorKind;
|
||||
pub use crate::ops::OperationError;
|
||||
pub use crate::ops::V8MaybeStaticStr;
|
||||
pub use crate::std_fs::RealFs;
|
||||
pub use crate::sync::MaybeSend;
|
||||
pub use crate::sync::MaybeSync;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::error::Error;
|
||||
use std::fmt::Formatter;
|
||||
|
@ -18,12 +19,15 @@ use crate::FsPermissions;
|
|||
use crate::OpenOptions;
|
||||
use boxed_error::Boxed;
|
||||
use deno_core::op2;
|
||||
use deno_core::v8;
|
||||
use deno_core::CancelFuture;
|
||||
use deno_core::CancelHandle;
|
||||
use deno_core::FastString;
|
||||
use deno_core::JsBuffer;
|
||||
use deno_core::OpState;
|
||||
use deno_core::ResourceId;
|
||||
use deno_core::ToJsBuffer;
|
||||
use deno_core::ToV8;
|
||||
use deno_io::fs::FileResource;
|
||||
use deno_io::fs::FsError;
|
||||
use deno_io::fs::FsStat;
|
||||
|
@ -1333,7 +1337,8 @@ where
|
|||
.read_file_sync(&path, Some(&mut access_check))
|
||||
.map_err(|error| map_permission_error("readfile", error, &path))?;
|
||||
|
||||
Ok(buf.into())
|
||||
// todo(https://github.com/denoland/deno/issues/27107): do not clone here
|
||||
Ok(buf.into_owned().into_boxed_slice().into())
|
||||
}
|
||||
|
||||
#[op2(async, stack_trace)]
|
||||
|
@ -1375,15 +1380,61 @@ where
|
|||
.map_err(|error| map_permission_error("readfile", error, &path))?
|
||||
};
|
||||
|
||||
Ok(buf.into())
|
||||
// todo(https://github.com/denoland/deno/issues/27107): do not clone here
|
||||
Ok(buf.into_owned().into_boxed_slice().into())
|
||||
}
|
||||
|
||||
// todo(https://github.com/denoland/deno_core/pull/986): remove
|
||||
// when upgrading deno_core
|
||||
#[derive(Debug)]
|
||||
pub struct FastStringV8AllocationError;
|
||||
|
||||
impl std::error::Error for FastStringV8AllocationError {}
|
||||
|
||||
impl std::fmt::Display for FastStringV8AllocationError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"failed to allocate string; buffer exceeds maximum length"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Maintains a static reference to the string if possible.
|
||||
pub struct V8MaybeStaticStr(pub Cow<'static, str>);
|
||||
|
||||
impl<'s> ToV8<'s> for V8MaybeStaticStr {
|
||||
type Error = FastStringV8AllocationError;
|
||||
|
||||
#[inline]
|
||||
fn to_v8(
|
||||
self,
|
||||
scope: &mut v8::HandleScope<'s>,
|
||||
) -> Result<v8::Local<'s, v8::Value>, Self::Error> {
|
||||
// todo(https://github.com/denoland/deno_core/pull/986): remove this check
|
||||
// when upgrading deno_core
|
||||
const MAX_V8_STRING_LENGTH: usize = 536870888;
|
||||
if self.0.len() > MAX_V8_STRING_LENGTH {
|
||||
return Err(FastStringV8AllocationError);
|
||||
}
|
||||
|
||||
Ok(
|
||||
match self.0 {
|
||||
Cow::Borrowed(text) => FastString::from_static(text),
|
||||
Cow::Owned(value) => value.into(),
|
||||
}
|
||||
.v8_string(scope)
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[op2(stack_trace)]
|
||||
#[string]
|
||||
#[to_v8]
|
||||
pub fn op_fs_read_file_text_sync<P>(
|
||||
state: &mut OpState,
|
||||
#[string] path: String,
|
||||
) -> Result<String, FsOpsError>
|
||||
) -> Result<V8MaybeStaticStr, FsOpsError>
|
||||
where
|
||||
P: FsPermissions + 'static,
|
||||
{
|
||||
|
@ -1395,17 +1446,16 @@ where
|
|||
let str = fs
|
||||
.read_text_file_lossy_sync(&path, Some(&mut access_check))
|
||||
.map_err(|error| map_permission_error("readfile", error, &path))?;
|
||||
|
||||
Ok(str)
|
||||
Ok(V8MaybeStaticStr(str))
|
||||
}
|
||||
|
||||
#[op2(async, stack_trace)]
|
||||
#[string]
|
||||
#[to_v8]
|
||||
pub async fn op_fs_read_file_text_async<P>(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[string] path: String,
|
||||
#[smi] cancel_rid: Option<ResourceId>,
|
||||
) -> Result<String, FsOpsError>
|
||||
) -> Result<V8MaybeStaticStr, FsOpsError>
|
||||
where
|
||||
P: FsPermissions + 'static,
|
||||
{
|
||||
|
@ -1439,7 +1489,7 @@ where
|
|||
.map_err(|error| map_permission_error("readfile", error, &path))?
|
||||
};
|
||||
|
||||
Ok(str)
|
||||
Ok(V8MaybeStaticStr(str))
|
||||
}
|
||||
|
||||
fn to_seek_from(offset: i64, whence: i32) -> Result<SeekFrom, FsOpsError> {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#![allow(clippy::disallowed_methods)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::env::current_dir;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
|
@ -371,7 +372,7 @@ impl FileSystem for RealFs {
|
|||
&self,
|
||||
path: &Path,
|
||||
access_check: Option<AccessCheckCb>,
|
||||
) -> FsResult<Vec<u8>> {
|
||||
) -> FsResult<Cow<'static, [u8]>> {
|
||||
let mut file = open_with_access_check(
|
||||
OpenOptions {
|
||||
read: true,
|
||||
|
@ -382,13 +383,13 @@ impl FileSystem for RealFs {
|
|||
)?;
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf)?;
|
||||
Ok(buf)
|
||||
Ok(Cow::Owned(buf))
|
||||
}
|
||||
async fn read_file_async<'a>(
|
||||
&'a self,
|
||||
path: PathBuf,
|
||||
access_check: Option<AccessCheckCb<'a>>,
|
||||
) -> FsResult<Vec<u8>> {
|
||||
) -> FsResult<Cow<'static, [u8]>> {
|
||||
let mut file = open_with_access_check(
|
||||
OpenOptions {
|
||||
read: true,
|
||||
|
@ -400,7 +401,7 @@ impl FileSystem for RealFs {
|
|||
spawn_blocking(move || {
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf)?;
|
||||
Ok::<_, FsError>(buf)
|
||||
Ok::<_, FsError>(Cow::Owned(buf))
|
||||
})
|
||||
.await?
|
||||
.map_err(Into::into)
|
||||
|
|
|
@ -34,6 +34,7 @@ const {
|
|||
ObjectHasOwn,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
PromisePrototypeCatch,
|
||||
SafePromisePrototypeFinally,
|
||||
PromisePrototypeThen,
|
||||
StringPrototypeIncludes,
|
||||
Symbol,
|
||||
|
@ -88,6 +89,16 @@ import {
|
|||
} from "ext:deno_net/01_net.js";
|
||||
import { hasTlsKeyPairOptions, listenTls } from "ext:deno_net/02_tls.js";
|
||||
import { SymbolAsyncDispose } from "ext:deno_web/00_infra.js";
|
||||
import {
|
||||
endSpan,
|
||||
enterSpan,
|
||||
Span,
|
||||
TRACING_ENABLED,
|
||||
} from "ext:deno_telemetry/telemetry.ts";
|
||||
import {
|
||||
updateSpanFromRequest,
|
||||
updateSpanFromResponse,
|
||||
} from "ext:deno_telemetry/util.ts";
|
||||
|
||||
const _upgraded = Symbol("_upgraded");
|
||||
|
||||
|
@ -513,91 +524,126 @@ function fastSyncResponseOrStream(
|
|||
* This function returns a promise that will only reject in the case of abnormal exit.
|
||||
*/
|
||||
function mapToCallback(context, callback, onError) {
|
||||
return async function (req) {
|
||||
const asyncContext = getAsyncContext();
|
||||
setAsyncContext(context.asyncContext);
|
||||
|
||||
let mapped = async function (req, span) {
|
||||
// Get the response from the user-provided callback. If that fails, use onError. If that fails, return a fallback
|
||||
// 500 error.
|
||||
let innerRequest;
|
||||
let response;
|
||||
try {
|
||||
// Get the response from the user-provided callback. If that fails, use onError. If that fails, return a fallback
|
||||
// 500 error.
|
||||
let innerRequest;
|
||||
let response;
|
||||
try {
|
||||
innerRequest = new InnerRequest(req, context);
|
||||
const request = fromInnerRequest(innerRequest, "immutable");
|
||||
innerRequest.request = request;
|
||||
response = await callback(
|
||||
request,
|
||||
new ServeHandlerInfo(innerRequest),
|
||||
);
|
||||
innerRequest = new InnerRequest(req, context);
|
||||
const request = fromInnerRequest(innerRequest, "immutable");
|
||||
innerRequest.request = request;
|
||||
|
||||
// Throwing Error if the handler return value is not a Response class
|
||||
if (span) {
|
||||
updateSpanFromRequest(span, request);
|
||||
}
|
||||
|
||||
response = await callback(
|
||||
request,
|
||||
new ServeHandlerInfo(innerRequest),
|
||||
);
|
||||
|
||||
// Throwing Error if the handler return value is not a Response class
|
||||
if (!ObjectPrototypeIsPrototypeOf(ResponsePrototype, response)) {
|
||||
throw new TypeError(
|
||||
"Return value from serve handler must be a response or a promise resolving to a response",
|
||||
);
|
||||
}
|
||||
|
||||
if (response.type === "error") {
|
||||
throw new TypeError(
|
||||
"Return value from serve handler must not be an error response (like Response.error())",
|
||||
);
|
||||
}
|
||||
|
||||
if (response.bodyUsed) {
|
||||
throw new TypeError(
|
||||
"The body of the Response returned from the serve handler has already been consumed",
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
try {
|
||||
response = await onError(error);
|
||||
if (!ObjectPrototypeIsPrototypeOf(ResponsePrototype, response)) {
|
||||
throw new TypeError(
|
||||
"Return value from serve handler must be a response or a promise resolving to a response",
|
||||
);
|
||||
}
|
||||
|
||||
if (response.type === "error") {
|
||||
throw new TypeError(
|
||||
"Return value from serve handler must not be an error response (like Response.error())",
|
||||
);
|
||||
}
|
||||
|
||||
if (response.bodyUsed) {
|
||||
throw new TypeError(
|
||||
"The body of the Response returned from the serve handler has already been consumed",
|
||||
"Return value from onError handler must be a response or a promise resolving to a response",
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
try {
|
||||
response = await onError(error);
|
||||
if (!ObjectPrototypeIsPrototypeOf(ResponsePrototype, response)) {
|
||||
throw new TypeError(
|
||||
"Return value from onError handler must be a response or a promise resolving to a response",
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
// deno-lint-ignore no-console
|
||||
console.error("Exception in onError while handling exception", error);
|
||||
response = internalServerError();
|
||||
}
|
||||
// deno-lint-ignore no-console
|
||||
console.error("Exception in onError while handling exception", error);
|
||||
response = internalServerError();
|
||||
}
|
||||
const inner = toInnerResponse(response);
|
||||
if (innerRequest?.[_upgraded]) {
|
||||
// We're done here as the connection has been upgraded during the callback and no longer requires servicing.
|
||||
if (response !== UPGRADE_RESPONSE_SENTINEL) {
|
||||
// deno-lint-ignore no-console
|
||||
console.error("Upgrade response was not returned from callback");
|
||||
context.close();
|
||||
}
|
||||
innerRequest?.[_upgraded]();
|
||||
return;
|
||||
}
|
||||
|
||||
// Did everything shut down while we were waiting?
|
||||
if (context.closed) {
|
||||
// We're shutting down, so this status shouldn't make it back to the client but "Service Unavailable" seems appropriate
|
||||
innerRequest?.close();
|
||||
op_http_set_promise_complete(req, 503);
|
||||
return;
|
||||
}
|
||||
|
||||
const status = inner.status;
|
||||
const headers = inner.headerList;
|
||||
if (headers && headers.length > 0) {
|
||||
if (headers.length == 1) {
|
||||
op_http_set_response_header(req, headers[0][0], headers[0][1]);
|
||||
} else {
|
||||
op_http_set_response_headers(req, headers);
|
||||
}
|
||||
}
|
||||
|
||||
fastSyncResponseOrStream(req, inner.body, status, innerRequest);
|
||||
} finally {
|
||||
setAsyncContext(asyncContext);
|
||||
}
|
||||
|
||||
if (span) {
|
||||
updateSpanFromResponse(span, response);
|
||||
}
|
||||
|
||||
const inner = toInnerResponse(response);
|
||||
if (innerRequest?.[_upgraded]) {
|
||||
// We're done here as the connection has been upgraded during the callback and no longer requires servicing.
|
||||
if (response !== UPGRADE_RESPONSE_SENTINEL) {
|
||||
// deno-lint-ignore no-console
|
||||
console.error("Upgrade response was not returned from callback");
|
||||
context.close();
|
||||
}
|
||||
innerRequest?.[_upgraded]();
|
||||
return;
|
||||
}
|
||||
|
||||
// Did everything shut down while we were waiting?
|
||||
if (context.closed) {
|
||||
// We're shutting down, so this status shouldn't make it back to the client but "Service Unavailable" seems appropriate
|
||||
innerRequest?.close();
|
||||
op_http_set_promise_complete(req, 503);
|
||||
return;
|
||||
}
|
||||
|
||||
const status = inner.status;
|
||||
const headers = inner.headerList;
|
||||
if (headers && headers.length > 0) {
|
||||
if (headers.length == 1) {
|
||||
op_http_set_response_header(req, headers[0][0], headers[0][1]);
|
||||
} else {
|
||||
op_http_set_response_headers(req, headers);
|
||||
}
|
||||
}
|
||||
|
||||
fastSyncResponseOrStream(req, inner.body, status, innerRequest);
|
||||
};
|
||||
|
||||
if (TRACING_ENABLED) {
|
||||
const origMapped = mapped;
|
||||
mapped = function (req, _span) {
|
||||
const oldCtx = getAsyncContext();
|
||||
setAsyncContext(context.asyncContext);
|
||||
const span = new Span("deno.serve", { kind: 1 });
|
||||
try {
|
||||
enterSpan(span);
|
||||
return SafePromisePrototypeFinally(
|
||||
origMapped(req, span),
|
||||
() => endSpan(span),
|
||||
);
|
||||
} finally {
|
||||
// equiv to exitSpan.
|
||||
setAsyncContext(oldCtx);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
const origMapped = mapped;
|
||||
mapped = function (req, span) {
|
||||
const oldCtx = getAsyncContext();
|
||||
setAsyncContext(context.asyncContext);
|
||||
try {
|
||||
return origMapped(req, span);
|
||||
} finally {
|
||||
setAsyncContext(oldCtx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return mapped;
|
||||
}
|
||||
|
||||
type RawHandler = (
|
||||
|
@ -795,7 +841,7 @@ function serveHttpOn(context, addr, callback) {
|
|||
// Attempt to pull as many requests out of the queue as possible before awaiting. This API is
|
||||
// a synchronous, non-blocking API that returns u32::MAX if anything goes wrong.
|
||||
while ((req = op_http_try_wait(rid)) !== null) {
|
||||
PromisePrototypeCatch(callback(req), promiseErrorHandler);
|
||||
PromisePrototypeCatch(callback(req, undefined), promiseErrorHandler);
|
||||
}
|
||||
currentPromise = op_http_wait(rid);
|
||||
if (!ref) {
|
||||
|
@ -815,7 +861,7 @@ function serveHttpOn(context, addr, callback) {
|
|||
if (req === null) {
|
||||
break;
|
||||
}
|
||||
PromisePrototypeCatch(callback(req), promiseErrorHandler);
|
||||
PromisePrototypeCatch(callback(req, undefined), promiseErrorHandler);
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_http"
|
||||
version = "0.177.0"
|
||||
version = "0.178.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_io"
|
||||
version = "0.89.0"
|
||||
version = "0.90.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -407,7 +407,7 @@ pub fn bi_pipe_pair_raw(
|
|||
&s,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED,
|
||||
0,
|
||||
std::ptr::null_mut(),
|
||||
);
|
||||
if hd2 == INVALID_HANDLE_VALUE {
|
||||
return Err(io::Error::last_os_error());
|
||||
|
|
|
@ -215,8 +215,8 @@ pub trait File {
|
|||
fn write_all_sync(self: Rc<Self>, buf: &[u8]) -> FsResult<()>;
|
||||
async fn write_all(self: Rc<Self>, buf: BufView) -> FsResult<()>;
|
||||
|
||||
fn read_all_sync(self: Rc<Self>) -> FsResult<Vec<u8>>;
|
||||
async fn read_all_async(self: Rc<Self>) -> FsResult<Vec<u8>>;
|
||||
fn read_all_sync(self: Rc<Self>) -> FsResult<Cow<'static, [u8]>>;
|
||||
async fn read_all_async(self: Rc<Self>) -> FsResult<Cow<'static, [u8]>>;
|
||||
|
||||
fn chmod_sync(self: Rc<Self>, pathmode: u32) -> FsResult<()>;
|
||||
async fn chmod_async(self: Rc<Self>, mode: u32) -> FsResult<()>;
|
||||
|
|
|
@ -789,26 +789,26 @@ impl crate::fs::File for StdFileResourceInner {
|
|||
}
|
||||
}
|
||||
|
||||
fn read_all_sync(self: Rc<Self>) -> FsResult<Vec<u8>> {
|
||||
fn read_all_sync(self: Rc<Self>) -> FsResult<Cow<'static, [u8]>> {
|
||||
match self.kind {
|
||||
StdFileResourceKind::File | StdFileResourceKind::Stdin(_) => {
|
||||
let mut buf = Vec::new();
|
||||
self.with_sync(|file| Ok(file.read_to_end(&mut buf)?))?;
|
||||
Ok(buf)
|
||||
Ok(Cow::Owned(buf))
|
||||
}
|
||||
StdFileResourceKind::Stdout | StdFileResourceKind::Stderr => {
|
||||
Err(FsError::NotSupported)
|
||||
}
|
||||
}
|
||||
}
|
||||
async fn read_all_async(self: Rc<Self>) -> FsResult<Vec<u8>> {
|
||||
async fn read_all_async(self: Rc<Self>) -> FsResult<Cow<'static, [u8]>> {
|
||||
match self.kind {
|
||||
StdFileResourceKind::File | StdFileResourceKind::Stdin(_) => {
|
||||
self
|
||||
.with_inner_blocking_task(|file| {
|
||||
let mut buf = Vec::new();
|
||||
file.read_to_end(&mut buf)?;
|
||||
Ok(buf)
|
||||
Ok(Cow::Owned(buf))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_kv"
|
||||
version = "0.87.0"
|
||||
version = "0.88.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_napi"
|
||||
version = "0.110.0"
|
||||
version = "0.111.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