mirror of
https://github.com/denoland/deno.git
synced 2025-01-21 04:52:26 -05:00
Merge branch 'main' into lint_plugins
This commit is contained in:
commit
53a680bded
851 changed files with 38097 additions and 5236 deletions
2
.github/workflows/cargo_publish.yml
vendored
2
.github/workflows/cargo_publish.yml
vendored
|
@ -35,7 +35,7 @@ jobs:
|
|||
- name: Install deno
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v1.x
|
||||
deno-version: v2.x
|
||||
|
||||
- name: Publish
|
||||
env:
|
||||
|
|
69
.github/workflows/ci.generate.ts
vendored
69
.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 = 28;
|
||||
const cacheVersion = 31;
|
||||
|
||||
const ubuntuX86Runner = "ubuntu-24.04";
|
||||
const ubuntuX86XlRunner = "ubuntu-24.04-xl";
|
||||
|
@ -59,6 +59,15 @@ const Runners = {
|
|||
|
||||
const prCacheKeyPrefix =
|
||||
`${cacheVersion}-cargo-target-\${{ matrix.os }}-\${{ matrix.arch }}-\${{ matrix.profile }}-\${{ matrix.job }}-`;
|
||||
const prCacheKey = `${prCacheKeyPrefix}\${{ github.sha }}`;
|
||||
const prCachePath = [
|
||||
// this must match for save and restore (https://github.com/actions/cache/issues/1444)
|
||||
"./target",
|
||||
"!./target/*/gn_out",
|
||||
"!./target/*/gn_root",
|
||||
"!./target/*/*.zip",
|
||||
"!./target/*/*.tar.gz",
|
||||
].join("\n");
|
||||
|
||||
// Note that you may need to add more version to the `apt-get remove` line below if you change this
|
||||
const llvmVersion = 19;
|
||||
|
@ -196,7 +205,7 @@ const installNodeStep = {
|
|||
const installDenoStep = {
|
||||
name: "Install Deno",
|
||||
uses: "denoland/setup-deno@v2",
|
||||
with: { "deno-version": "v1.x" },
|
||||
with: { "deno-version": "v2.x" },
|
||||
};
|
||||
|
||||
const authenticateWithGoogleCloud = {
|
||||
|
@ -475,6 +484,27 @@ const ci = {
|
|||
" -czvf target/release/deno_src.tar.gz -C .. deno",
|
||||
].join("\n"),
|
||||
},
|
||||
{
|
||||
name: "Cache Cargo home",
|
||||
uses: "actions/cache@v4",
|
||||
with: {
|
||||
// See https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci
|
||||
// Note that with the new sparse registry format, we no longer have to cache a `.git` dir
|
||||
path: [
|
||||
"~/.cargo/.crates.toml",
|
||||
"~/.cargo/.crates2.json",
|
||||
"~/.cargo/bin",
|
||||
"~/.cargo/registry/index",
|
||||
"~/.cargo/registry/cache",
|
||||
"~/.cargo/git/db",
|
||||
].join("\n"),
|
||||
key:
|
||||
`${cacheVersion}-cargo-home-\${{ matrix.os }}-\${{ matrix.arch }}-\${{ hashFiles('Cargo.lock') }}`,
|
||||
// We will try to restore from the closest cargo-home we can find
|
||||
"restore-keys":
|
||||
`${cacheVersion}-cargo-home-\${{ matrix.os }}-\${{ matrix.arch }}-`,
|
||||
},
|
||||
},
|
||||
installRustStep,
|
||||
{
|
||||
if:
|
||||
|
@ -598,23 +628,6 @@ const ci = {
|
|||
installBenchTools,
|
||||
].join("\n"),
|
||||
},
|
||||
{
|
||||
name: "Cache Cargo home",
|
||||
uses: "actions/cache@v4",
|
||||
with: {
|
||||
// See https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci
|
||||
// Note that with the new sparse registry format, we no longer have to cache a `.git` dir
|
||||
path: [
|
||||
"~/.cargo/registry/index",
|
||||
"~/.cargo/registry/cache",
|
||||
].join("\n"),
|
||||
key:
|
||||
`${cacheVersion}-cargo-home-\${{ matrix.os }}-\${{ matrix.arch }}-\${{ hashFiles('Cargo.lock') }}`,
|
||||
// We will try to restore from the closest cargo-home we can find
|
||||
"restore-keys":
|
||||
`${cacheVersion}-cargo-home-\${{ matrix.os }}-\${{ matrix.arch }}`,
|
||||
},
|
||||
},
|
||||
{
|
||||
// Restore cache from the latest 'main' branch build.
|
||||
name: "Restore cache build output (PR)",
|
||||
|
@ -622,13 +635,7 @@ const ci = {
|
|||
if:
|
||||
"github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/')",
|
||||
with: {
|
||||
path: [
|
||||
"./target",
|
||||
"!./target/*/gn_out",
|
||||
"!./target/*/gn_root",
|
||||
"!./target/*/*.zip",
|
||||
"!./target/*/*.tar.gz",
|
||||
].join("\n"),
|
||||
path: prCachePath,
|
||||
key: "never_saved",
|
||||
"restore-keys": prCacheKeyPrefix,
|
||||
},
|
||||
|
@ -1080,14 +1087,8 @@ const ci = {
|
|||
if:
|
||||
"(matrix.job == 'test' || matrix.job == 'lint') && github.ref == 'refs/heads/main'",
|
||||
with: {
|
||||
path: [
|
||||
"./target",
|
||||
"!./target/*/gn_out",
|
||||
"!./target/*/*.zip",
|
||||
"!./target/*/*.sha256sum",
|
||||
"!./target/*/*.tar.gz",
|
||||
].join("\n"),
|
||||
key: prCacheKeyPrefix + "${{ github.sha }}",
|
||||
path: prCachePath,
|
||||
key: prCacheKey,
|
||||
},
|
||||
},
|
||||
]),
|
||||
|
|
30
.github/workflows/ci.yml
vendored
30
.github/workflows/ci.yml
vendored
|
@ -174,13 +174,26 @@ jobs:
|
|||
mkdir -p target/release
|
||||
tar --exclude=".git*" --exclude=target --exclude=third_party/prebuilt \
|
||||
-czvf target/release/deno_src.tar.gz -C .. deno
|
||||
- name: Cache Cargo home
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |-
|
||||
~/.cargo/.crates.toml
|
||||
~/.cargo/.crates2.json
|
||||
~/.cargo/bin
|
||||
~/.cargo/registry/index
|
||||
~/.cargo/registry/cache
|
||||
~/.cargo/git/db
|
||||
key: '31-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
|
||||
restore-keys: '31-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-'
|
||||
if: '!(matrix.skip)'
|
||||
- uses: dsherret/rust-toolchain-file@v1
|
||||
if: '!(matrix.skip)'
|
||||
- if: '!(matrix.skip) && (matrix.job == ''lint'' || matrix.job == ''test'' || matrix.job == ''bench'')'
|
||||
name: Install Deno
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v1.x
|
||||
deno-version: v2.x
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
|
@ -355,15 +368,6 @@ jobs:
|
|||
- name: Install benchmark tools
|
||||
if: '!(matrix.skip) && (matrix.job == ''bench'')'
|
||||
run: ./tools/install_prebuilt.js wrk hyperfine
|
||||
- name: Cache Cargo home
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |-
|
||||
~/.cargo/registry/index
|
||||
~/.cargo/registry/cache
|
||||
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
|
||||
if: '!(matrix.skip) && (github.ref != ''refs/heads/main'' && !startsWith(github.ref, ''refs/tags/''))'
|
||||
|
@ -375,7 +379,7 @@ jobs:
|
|||
!./target/*/*.zip
|
||||
!./target/*/*.tar.gz
|
||||
key: never_saved
|
||||
restore-keys: '28-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-'
|
||||
restore-keys: '31-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
|
||||
|
@ -682,10 +686,10 @@ jobs:
|
|||
path: |-
|
||||
./target
|
||||
!./target/*/gn_out
|
||||
!./target/*/gn_root
|
||||
!./target/*/*.zip
|
||||
!./target/*/*.sha256sum
|
||||
!./target/*/*.tar.gz
|
||||
key: '28-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
|
||||
key: '31-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
|
||||
publish-canary:
|
||||
name: publish canary
|
||||
runs-on: ubuntu-24.04
|
||||
|
|
45
.github/workflows/npm_publish.yml
vendored
Normal file
45
.github/workflows/npm_publish.yml
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
name: npm_publish
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version'
|
||||
type: string
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: npm publish
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- name: Configure git
|
||||
run: |
|
||||
git config --global core.symlinks true
|
||||
git config --global fetch.parallel 32
|
||||
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install Deno
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v2.x
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: ./tools/release/npm/build.ts ${{ github.event.inputs.version }} --publish
|
2
.github/workflows/promote_to_release.yml
vendored
2
.github/workflows/promote_to_release.yml
vendored
|
@ -42,7 +42,7 @@ jobs:
|
|||
- name: Install deno
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v1.x
|
||||
deno-version: v2.x
|
||||
|
||||
- name: Install rust-codesign
|
||||
run: |-
|
||||
|
|
2
.github/workflows/start_release.yml
vendored
2
.github/workflows/start_release.yml
vendored
|
@ -36,7 +36,7 @@ jobs:
|
|||
- name: Install deno
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v1.x
|
||||
deno-version: v2.x
|
||||
|
||||
- name: Create Gist URL
|
||||
env:
|
||||
|
|
2
.github/workflows/version_bump.yml
vendored
2
.github/workflows/version_bump.yml
vendored
|
@ -41,7 +41,7 @@ jobs:
|
|||
- name: Install deno
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v1.x
|
||||
deno-version: v2.x
|
||||
|
||||
- name: Run version bump
|
||||
run: |
|
||||
|
|
427
Cargo.lock
generated
427
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
82
Cargo.toml
82
Cargo.toml
|
@ -48,19 +48,19 @@ repository = "https://github.com/denoland/deno"
|
|||
|
||||
[workspace.dependencies]
|
||||
deno_ast = { version = "=0.44.0", features = ["transpiling"] }
|
||||
deno_core = { version = "0.323.0" }
|
||||
deno_core = { version = "0.326.0" }
|
||||
|
||||
deno_bench_util = { version = "0.174.0", path = "./bench_util" }
|
||||
deno_bench_util = { version = "0.177.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.26.0"
|
||||
deno_path_util = "=0.2.1"
|
||||
deno_permissions = { version = "0.40.0", path = "./runtime/permissions" }
|
||||
deno_runtime = { version = "0.189.0", path = "./runtime" }
|
||||
deno_semver = "=0.6.0"
|
||||
deno_path_util = "=0.2.2"
|
||||
deno_permissions = { version = "0.43.0", path = "./runtime/permissions" }
|
||||
deno_runtime = { version = "0.192.0", path = "./runtime" }
|
||||
deno_semver = "=0.6.1"
|
||||
deno_terminal = "0.2.0"
|
||||
napi_sym = { version = "0.110.0", path = "./ext/napi/sym" }
|
||||
napi_sym = { version = "0.113.0", path = "./ext/napi/sym" }
|
||||
test_util = { package = "test_server", path = "./tests/util/server" }
|
||||
|
||||
denokv_proto = "0.8.4"
|
||||
|
@ -69,34 +69,34 @@ denokv_remote = "0.8.4"
|
|||
denokv_sqlite = { default-features = false, version = "0.8.4" }
|
||||
|
||||
# exts
|
||||
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" }
|
||||
deno_broadcast_channel = { version = "0.177.0", path = "./ext/broadcast_channel" }
|
||||
deno_cache = { version = "0.115.0", path = "./ext/cache" }
|
||||
deno_canvas = { version = "0.52.0", path = "./ext/canvas" }
|
||||
deno_console = { version = "0.183.0", path = "./ext/console" }
|
||||
deno_cron = { version = "0.63.0", path = "./ext/cron" }
|
||||
deno_crypto = { version = "0.197.0", path = "./ext/crypto" }
|
||||
deno_fetch = { version = "0.207.0", path = "./ext/fetch" }
|
||||
deno_ffi = { version = "0.170.0", path = "./ext/ffi" }
|
||||
deno_fs = { version = "0.93.0", path = "./ext/fs" }
|
||||
deno_http = { version = "0.181.0", path = "./ext/http" }
|
||||
deno_io = { version = "0.93.0", path = "./ext/io" }
|
||||
deno_kv = { version = "0.91.0", path = "./ext/kv" }
|
||||
deno_napi = { version = "0.114.0", path = "./ext/napi" }
|
||||
deno_net = { version = "0.175.0", path = "./ext/net" }
|
||||
deno_node = { version = "0.120.0", path = "./ext/node" }
|
||||
deno_telemetry = { version = "0.5.0", path = "./ext/telemetry" }
|
||||
deno_tls = { version = "0.170.0", path = "./ext/tls" }
|
||||
deno_url = { version = "0.183.0", path = "./ext/url" }
|
||||
deno_web = { version = "0.214.0", path = "./ext/web" }
|
||||
deno_webgpu = { version = "0.150.0", path = "./ext/webgpu" }
|
||||
deno_webidl = { version = "0.183.0", path = "./ext/webidl" }
|
||||
deno_websocket = { version = "0.188.0", path = "./ext/websocket" }
|
||||
deno_webstorage = { version = "0.178.0", path = "./ext/webstorage" }
|
||||
|
||||
# resolvers
|
||||
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" }
|
||||
deno_npm_cache = { version = "0.3.0", path = "./resolvers/npm_cache" }
|
||||
deno_resolver = { version = "0.15.0", path = "./resolvers/deno" }
|
||||
node_resolver = { version = "0.22.0", path = "./resolvers/node" }
|
||||
|
||||
aes = "=0.8.3"
|
||||
anyhow = "1.0.57"
|
||||
|
@ -104,10 +104,11 @@ async-trait = "0.1.73"
|
|||
base32 = "=0.5.1"
|
||||
base64 = "0.21.7"
|
||||
bencher = "0.1"
|
||||
boxed_error = "0.2.2"
|
||||
boxed_error = "0.2.3"
|
||||
brotli = "6.0.0"
|
||||
bytes = "1.4.0"
|
||||
cache_control = "=0.2.0"
|
||||
capacity_builder = "0.1.3"
|
||||
cbc = { version = "=0.1.2", features = ["alloc"] }
|
||||
# Note: Do not use the "clock" feature of chrono, as it links us to CoreFoundation on macOS.
|
||||
# Instead use util::time::utc_now()
|
||||
|
@ -116,8 +117,9 @@ color-print = "0.3.5"
|
|||
console_static_text = "=0.8.1"
|
||||
dashmap = "5.5.3"
|
||||
data-encoding = "2.3.3"
|
||||
data-url = "=0.3.0"
|
||||
deno_cache_dir = "=0.14.0"
|
||||
data-url = "=0.3.1"
|
||||
deno_cache_dir = "=0.15.0"
|
||||
deno_error = "=0.5.2"
|
||||
deno_package_json = { version = "0.2.1", default-features = false }
|
||||
deno_unsync = "0.4.2"
|
||||
dlopen2 = "0.6.1"
|
||||
|
@ -133,7 +135,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"
|
||||
|
@ -141,7 +143,7 @@ http_v02 = { package = "http", version = "0.2.9" }
|
|||
httparse = "1.8.0"
|
||||
hyper = { version = "1.4.1", features = ["full"] }
|
||||
hyper-rustls = { version = "0.27.2", default-features = false, features = ["http1", "http2", "tls12", "ring"] }
|
||||
hyper-util = { version = "=0.1.7", features = ["tokio", "client", "client-legacy", "server", "server-auto"] }
|
||||
hyper-util = { version = "0.1.10", features = ["tokio", "client", "client-legacy", "server", "server-auto"] }
|
||||
hyper_v014 = { package = "hyper", version = "0.14.26", features = ["runtime", "http1"] }
|
||||
indexmap = { version = "2", features = ["serde"] }
|
||||
ipnet = "2.3"
|
||||
|
@ -194,7 +196,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"] }
|
||||
|
@ -239,7 +241,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]
|
||||
|
|
40
Releases.md
40
Releases.md
|
@ -6,6 +6,46 @@ https://github.com/denoland/deno/releases
|
|||
We also have one-line install commands at:
|
||||
https://github.com/denoland/deno_install
|
||||
|
||||
### 2.1.4 / 2024.12.11
|
||||
|
||||
- feat(unstable): support caching npm dependencies only as they're needed
|
||||
(#27300)
|
||||
- fix(compile): correct read length for transpiled typescript files (#27301)
|
||||
- fix(ext/node): accept file descriptor in fs.readFile(Sync) (#27252)
|
||||
- fix(ext/node): handle Float16Array in node:v8 module (#27285)
|
||||
- fix(lint): do not error providing --allow-import (#27321)
|
||||
- fix(node): update list of builtin node modules, add missing export to
|
||||
_http_common (#27294)
|
||||
- fix(outdated): error when there are no config files (#27306)
|
||||
- fix(outdated): respect --quiet flag for hints (#27317)
|
||||
- fix(outdated): show a suggestion for updating (#27304)
|
||||
- fix(task): do not always kill child on ctrl+c on windows (#27269)
|
||||
- fix(unstable): don't unwrap optional state in otel (#27292)
|
||||
- fix: do not error when subpath has an @ symbol (#27290)
|
||||
- fix: do not panic when fetching invalid file url on Windows (#27259)
|
||||
- fix: replace the @deno-types with @ts-types (#27310)
|
||||
- perf(compile): improve FileBackedVfsFile (#27299)
|
||||
|
||||
### 2.1.3 / 2024.12.05
|
||||
|
||||
- feat(unstable): add metrics to otel (#27143)
|
||||
- fix(fmt): stable formatting of HTML files with JS (#27164)
|
||||
- fix(install): use locked version of jsr package when fetching exports (#27237)
|
||||
- fix(node/fs): support `recursive` option in readdir (#27179)
|
||||
- fix(node/worker_threads): data url not encoded properly with eval (#27184)
|
||||
- fix(outdated): allow `--latest` without `--update` (#27227)
|
||||
- fix(task): `--recursive` option not working (#27183)
|
||||
- fix(task): don't panic with filter on missing task argument (#27180)
|
||||
- fix(task): forward signals to spawned sub-processes on unix (#27141)
|
||||
- fix(task): kill descendants when killing task process on Windows (#27163)
|
||||
- fix(task): only pass args to root task (#27213)
|
||||
- fix(unstable): otel context with multiple keys (#27230)
|
||||
- fix(unstable/temporal): respect locale in `Duration.prototype.toLocaleString`
|
||||
(#27000)
|
||||
- fix: clear dep analysis when module loading is done (#27204)
|
||||
- fix: improve auto-imports for npm packages (#27224)
|
||||
- fix: support `workspace:^` and `workspace:~` version constraints (#27096)
|
||||
|
||||
### 2.1.2 / 2024.11.28
|
||||
|
||||
- feat(unstable): Instrument Deno.serve (#26964)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_bench_util"
|
||||
version = "0.174.0"
|
||||
version = "0.177.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno"
|
||||
version = "2.1.2"
|
||||
version = "2.1.4"
|
||||
authors.workspace = true
|
||||
default-run = "deno"
|
||||
edition.workspace = true
|
||||
|
@ -72,7 +72,8 @@ 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.161.2", features = ["rust", "comrak"] }
|
||||
deno_doc = { version = "=0.161.3", features = ["rust", "comrak"] }
|
||||
deno_error.workspace = true
|
||||
deno_graph = { version = "=0.86.3" }
|
||||
deno_lint = { version = "=0.68.2", features = ["docs"] }
|
||||
deno_lockfile.workspace = true
|
||||
|
@ -80,10 +81,10 @@ deno_npm.workspace = true
|
|||
deno_npm_cache.workspace = true
|
||||
deno_package_json.workspace = true
|
||||
deno_path_util.workspace = true
|
||||
deno_resolver.workspace = true
|
||||
deno_resolver = { workspace = true, features = ["sync"] }
|
||||
deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] }
|
||||
deno_semver.workspace = true
|
||||
deno_task_shell = "=0.20.1"
|
||||
deno_task_shell = "=0.20.2"
|
||||
deno_telemetry.workspace = true
|
||||
deno_terminal.workspace = true
|
||||
libsui = "0.5.0"
|
||||
|
@ -93,8 +94,10 @@ anstream = "0.6.14"
|
|||
async-trait.workspace = true
|
||||
base64.workspace = true
|
||||
bincode = "=1.3.3"
|
||||
boxed_error.workspace = true
|
||||
bytes.workspace = true
|
||||
cache_control.workspace = true
|
||||
capacity_builder.workspace = true
|
||||
chrono = { workspace = true, features = ["now"] }
|
||||
clap = { version = "=4.5.16", features = ["env", "string", "wrap_help", "error-context"] }
|
||||
clap_complete = "=4.5.24"
|
||||
|
|
|
@ -64,6 +64,15 @@ impl<'a> deno_config::fs::DenoConfigFs for DenoConfigFsAdapter<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn import_map_deps(
|
||||
import_map: &serde_json::Value,
|
||||
) -> HashSet<JsrDepPackageReq> {
|
||||
let values = imports_values(import_map.get("imports"))
|
||||
.into_iter()
|
||||
.chain(scope_values(import_map.get("scopes")));
|
||||
values_to_set(values)
|
||||
}
|
||||
|
||||
pub fn deno_json_deps(
|
||||
config: &deno_config::deno_json::ConfigFile,
|
||||
) -> HashSet<JsrDepPackageReq> {
|
||||
|
|
|
@ -37,6 +37,7 @@ use deno_path_util::url_to_file_path;
|
|||
use deno_runtime::deno_permissions::PermissionsOptions;
|
||||
use deno_runtime::deno_permissions::SysDescriptor;
|
||||
use deno_telemetry::OtelConfig;
|
||||
use deno_telemetry::OtelConsoleConfig;
|
||||
use log::debug;
|
||||
use log::Level;
|
||||
use serde::Deserialize;
|
||||
|
@ -245,7 +246,7 @@ pub struct InstallFlagsGlobal {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum InstallKind {
|
||||
pub enum InstallFlags {
|
||||
Local(InstallFlagsLocal),
|
||||
Global(InstallFlagsGlobal),
|
||||
}
|
||||
|
@ -257,11 +258,6 @@ pub enum InstallFlagsLocal {
|
|||
Entrypoints(Vec<String>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct InstallFlags {
|
||||
pub kind: InstallKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct JSONReferenceFlags {
|
||||
pub json: deno_core::serde_json::Value,
|
||||
|
@ -602,6 +598,7 @@ pub struct UnstableConfig {
|
|||
pub bare_node_builtins: bool,
|
||||
pub detect_cjs: bool,
|
||||
pub sloppy_imports: bool,
|
||||
pub npm_lazy_caching: bool,
|
||||
pub features: Vec<String>, // --unstabe-kv --unstable-cron
|
||||
}
|
||||
|
||||
|
@ -992,21 +989,41 @@ impl Flags {
|
|||
args
|
||||
}
|
||||
|
||||
pub fn otel_config(&self) -> Option<OtelConfig> {
|
||||
if self
|
||||
pub fn otel_config(&self) -> OtelConfig {
|
||||
let has_unstable_flag = self
|
||||
.unstable_config
|
||||
.features
|
||||
.contains(&String::from("otel"))
|
||||
{
|
||||
Some(OtelConfig {
|
||||
runtime_name: Cow::Borrowed("deno"),
|
||||
runtime_version: Cow::Borrowed(crate::version::DENO_VERSION_INFO.deno),
|
||||
deterministic: std::env::var("DENO_UNSTABLE_OTEL_DETERMINISTIC")
|
||||
.is_ok(),
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
None
|
||||
.contains(&String::from("otel"));
|
||||
|
||||
let otel_var = |name| match std::env::var(name) {
|
||||
Ok(s) if s.to_lowercase() == "true" => Some(true),
|
||||
Ok(s) if s.to_lowercase() == "false" => Some(false),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let disabled =
|
||||
!has_unstable_flag || otel_var("OTEL_SDK_DISABLED").unwrap_or(false);
|
||||
let default = !disabled && otel_var("OTEL_DENO").unwrap_or(false);
|
||||
|
||||
OtelConfig {
|
||||
tracing_enabled: !disabled
|
||||
&& otel_var("OTEL_DENO_TRACING").unwrap_or(default),
|
||||
console: match std::env::var("OTEL_DENO_CONSOLE").as_deref() {
|
||||
Ok(_) if disabled => OtelConsoleConfig::Ignore,
|
||||
Ok("ignore") => OtelConsoleConfig::Ignore,
|
||||
Ok("capture") => OtelConsoleConfig::Capture,
|
||||
Ok("replace") => OtelConsoleConfig::Replace,
|
||||
_ => {
|
||||
if default {
|
||||
OtelConsoleConfig::Capture
|
||||
} else {
|
||||
OtelConsoleConfig::Ignore
|
||||
}
|
||||
}
|
||||
},
|
||||
deterministic: std::env::var("DENO_UNSTABLE_OTEL_DETERMINISTIC")
|
||||
.as_deref()
|
||||
== Ok("1"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2666,10 +2683,10 @@ 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/*\"</>
|
||||
|
@ -2705,7 +2722,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(
|
||||
|
@ -2917,6 +2933,7 @@ To ignore linting on an entire file, you can add an ignore comment at the top of
|
|||
.arg(watch_arg(false))
|
||||
.arg(watch_exclude_arg())
|
||||
.arg(no_clear_screen_arg())
|
||||
.arg(allow_import_arg())
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -4419,6 +4436,16 @@ impl CommandExt for Command {
|
|||
})
|
||||
.help_heading(UNSTABLE_HEADING)
|
||||
.display_order(next_display_order())
|
||||
).arg(
|
||||
Arg::new("unstable-npm-lazy-caching")
|
||||
.long("unstable-npm-lazy-caching")
|
||||
.help("Enable unstable lazy caching of npm dependencies, downloading them only as needed (disabled: all npm packages in package.json are installed on startup; enabled: only npm packages that are actually referenced in an import are installed")
|
||||
.env("DENO_UNSTABLE_NPM_LAZY_CACHING")
|
||||
.value_parser(FalseyValueParser::new())
|
||||
.action(ArgAction::SetTrue)
|
||||
.hide(true)
|
||||
.help_heading(UNSTABLE_HEADING)
|
||||
.display_order(next_display_order()),
|
||||
);
|
||||
|
||||
for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS.iter() {
|
||||
|
@ -4932,15 +4959,14 @@ fn install_parse(
|
|||
let module_url = cmd_values.next().unwrap();
|
||||
let args = cmd_values.collect();
|
||||
|
||||
flags.subcommand = DenoSubcommand::Install(InstallFlags {
|
||||
kind: InstallKind::Global(InstallFlagsGlobal {
|
||||
flags.subcommand =
|
||||
DenoSubcommand::Install(InstallFlags::Global(InstallFlagsGlobal {
|
||||
name,
|
||||
module_url,
|
||||
args,
|
||||
root,
|
||||
force,
|
||||
}),
|
||||
});
|
||||
}));
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -4949,22 +4975,19 @@ fn install_parse(
|
|||
allow_scripts_arg_parse(flags, matches)?;
|
||||
if matches.get_flag("entrypoint") {
|
||||
let entrypoints = matches.remove_many::<String>("cmd").unwrap_or_default();
|
||||
flags.subcommand = DenoSubcommand::Install(InstallFlags {
|
||||
kind: InstallKind::Local(InstallFlagsLocal::Entrypoints(
|
||||
entrypoints.collect(),
|
||||
)),
|
||||
});
|
||||
flags.subcommand = DenoSubcommand::Install(InstallFlags::Local(
|
||||
InstallFlagsLocal::Entrypoints(entrypoints.collect()),
|
||||
));
|
||||
} else if let Some(add_files) = matches
|
||||
.remove_many("cmd")
|
||||
.map(|packages| add_parse_inner(matches, Some(packages)))
|
||||
{
|
||||
flags.subcommand = DenoSubcommand::Install(InstallFlags {
|
||||
kind: InstallKind::Local(InstallFlagsLocal::Add(add_files)),
|
||||
})
|
||||
flags.subcommand = DenoSubcommand::Install(InstallFlags::Local(
|
||||
InstallFlagsLocal::Add(add_files),
|
||||
))
|
||||
} else {
|
||||
flags.subcommand = DenoSubcommand::Install(InstallFlags {
|
||||
kind: InstallKind::Local(InstallFlagsLocal::TopLevel),
|
||||
});
|
||||
flags.subcommand =
|
||||
DenoSubcommand::Install(InstallFlags::Local(InstallFlagsLocal::TopLevel));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -5096,6 +5119,7 @@ fn lint_parse(
|
|||
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionOnly);
|
||||
ext_arg_parse(flags, matches);
|
||||
config_args_parse(flags, matches);
|
||||
allow_import_parse(flags, matches);
|
||||
|
||||
let files = match matches.remove_many::<String>("files") {
|
||||
Some(f) => f.collect(),
|
||||
|
@ -5295,8 +5319,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"),
|
||||
|
@ -6007,6 +6038,8 @@ fn unstable_args_parse(
|
|||
flags.unstable_config.detect_cjs = matches.get_flag("unstable-detect-cjs");
|
||||
flags.unstable_config.sloppy_imports =
|
||||
matches.get_flag("unstable-sloppy-imports");
|
||||
flags.unstable_config.npm_lazy_caching =
|
||||
matches.get_flag("unstable-npm-lazy-caching");
|
||||
|
||||
if matches!(cfg, UnstableArgsConfig::ResolutionAndRuntime) {
|
||||
for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS {
|
||||
|
@ -7153,6 +7186,7 @@ mod tests {
|
|||
let r = flags_from_vec(svec![
|
||||
"deno",
|
||||
"lint",
|
||||
"--allow-import",
|
||||
"--watch",
|
||||
"script_1.ts",
|
||||
"script_2.ts"
|
||||
|
@ -7175,6 +7209,10 @@ mod tests {
|
|||
watch: Some(Default::default()),
|
||||
maybe_plugins: None,
|
||||
}),
|
||||
permissions: PermissionFlags {
|
||||
allow_import: Some(vec![]),
|
||||
..Default::default()
|
||||
},
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
|
@ -8650,15 +8688,15 @@ mod tests {
|
|||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
subcommand: DenoSubcommand::Install(InstallFlags {
|
||||
kind: InstallKind::Global(InstallFlagsGlobal {
|
||||
subcommand: DenoSubcommand::Install(InstallFlags::Global(
|
||||
InstallFlagsGlobal {
|
||||
name: None,
|
||||
module_url: "jsr:@std/http/file-server".to_string(),
|
||||
args: vec![],
|
||||
root: None,
|
||||
force: false,
|
||||
}),
|
||||
}),
|
||||
}
|
||||
),),
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
|
@ -8672,15 +8710,15 @@ mod tests {
|
|||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
subcommand: DenoSubcommand::Install(InstallFlags {
|
||||
kind: InstallKind::Global(InstallFlagsGlobal {
|
||||
subcommand: DenoSubcommand::Install(InstallFlags::Global(
|
||||
InstallFlagsGlobal {
|
||||
name: None,
|
||||
module_url: "jsr:@std/http/file-server".to_string(),
|
||||
args: vec![],
|
||||
root: None,
|
||||
force: false,
|
||||
}),
|
||||
}),
|
||||
}
|
||||
),),
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
|
@ -8693,15 +8731,15 @@ mod tests {
|
|||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
subcommand: DenoSubcommand::Install(InstallFlags {
|
||||
kind: InstallKind::Global(InstallFlagsGlobal {
|
||||
subcommand: DenoSubcommand::Install(InstallFlags::Global(
|
||||
InstallFlagsGlobal {
|
||||
name: Some("file_server".to_string()),
|
||||
module_url: "jsr:@std/http/file-server".to_string(),
|
||||
args: svec!["foo", "bar"],
|
||||
root: Some("/foo".to_string()),
|
||||
force: true,
|
||||
}),
|
||||
}),
|
||||
}
|
||||
),),
|
||||
import_map_path: Some("import_map.json".to_string()),
|
||||
no_remote: true,
|
||||
config_flag: ConfigFlag::Path("tsconfig.json".to_owned()),
|
||||
|
@ -10596,7 +10634,7 @@ mod tests {
|
|||
cwd: None,
|
||||
task: Some("build".to_string()),
|
||||
is_run: false,
|
||||
recursive: true,
|
||||
recursive: false,
|
||||
filter: Some("*".to_string()),
|
||||
eval: false,
|
||||
}),
|
||||
|
@ -10613,7 +10651,7 @@ mod tests {
|
|||
task: Some("build".to_string()),
|
||||
is_run: false,
|
||||
recursive: true,
|
||||
filter: None,
|
||||
filter: Some("*".to_string()),
|
||||
eval: false,
|
||||
}),
|
||||
..Flags::default()
|
||||
|
@ -10629,7 +10667,7 @@ mod tests {
|
|||
task: Some("build".to_string()),
|
||||
is_run: false,
|
||||
recursive: true,
|
||||
filter: None,
|
||||
filter: Some("*".to_string()),
|
||||
eval: false,
|
||||
}),
|
||||
..Flags::default()
|
||||
|
@ -11255,9 +11293,9 @@ mod tests {
|
|||
..Flags::default()
|
||||
},
|
||||
"install" => Flags {
|
||||
subcommand: DenoSubcommand::Install(InstallFlags {
|
||||
kind: InstallKind::Local(InstallFlagsLocal::Add(flags)),
|
||||
}),
|
||||
subcommand: DenoSubcommand::Install(InstallFlags::Local(
|
||||
InstallFlagsLocal::Add(flags),
|
||||
)),
|
||||
..Flags::default()
|
||||
},
|
||||
_ => unreachable!(),
|
||||
|
@ -11744,6 +11782,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"];
|
||||
|
|
|
@ -4,21 +4,21 @@ use deno_core::error::AnyError;
|
|||
use deno_core::serde_json;
|
||||
use deno_core::url::Url;
|
||||
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::file_fetcher::TextDecodedFile;
|
||||
|
||||
pub async fn resolve_import_map_value_from_specifier(
|
||||
specifier: &Url,
|
||||
file_fetcher: &FileFetcher,
|
||||
file_fetcher: &CliFileFetcher,
|
||||
) -> Result<serde_json::Value, AnyError> {
|
||||
if specifier.scheme() == "data" {
|
||||
let data_url_text =
|
||||
deno_graph::source::RawDataUrl::parse(specifier)?.decode()?;
|
||||
Ok(serde_json::from_str(&data_url_text)?)
|
||||
} else {
|
||||
let file = file_fetcher
|
||||
.fetch_bypass_permissions(specifier)
|
||||
.await?
|
||||
.into_text_decoded()?;
|
||||
let file = TextDecodedFile::decode(
|
||||
file_fetcher.fetch_bypass_permissions(specifier).await?,
|
||||
)?;
|
||||
Ok(serde_json::from_str(&file.source)?)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,18 +9,19 @@ use deno_core::anyhow::Context;
|
|||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::parking_lot::MutexGuard;
|
||||
use deno_core::serde_json;
|
||||
use deno_lockfile::WorkspaceMemberConfig;
|
||||
use deno_package_json::PackageJsonDepValue;
|
||||
use deno_runtime::deno_node::PackageJson;
|
||||
use deno_semver::jsr::JsrDepPackageReq;
|
||||
|
||||
use crate::args::deno_json::import_map_deps;
|
||||
use crate::cache;
|
||||
use crate::util::fs::atomic_write_file_with_retries;
|
||||
use crate::Flags;
|
||||
|
||||
use crate::args::DenoSubcommand;
|
||||
use crate::args::InstallFlags;
|
||||
use crate::args::InstallKind;
|
||||
|
||||
use deno_lockfile::Lockfile;
|
||||
|
||||
|
@ -102,6 +103,7 @@ impl CliLockfile {
|
|||
pub fn discover(
|
||||
flags: &Flags,
|
||||
workspace: &Workspace,
|
||||
maybe_external_import_map: Option<&serde_json::Value>,
|
||||
) -> Result<Option<CliLockfile>, AnyError> {
|
||||
fn pkg_json_deps(
|
||||
maybe_pkg_json: Option<&PackageJson>,
|
||||
|
@ -136,10 +138,8 @@ impl CliLockfile {
|
|||
if flags.no_lock
|
||||
|| matches!(
|
||||
flags.subcommand,
|
||||
DenoSubcommand::Install(InstallFlags {
|
||||
kind: InstallKind::Global(..),
|
||||
..
|
||||
}) | DenoSubcommand::Uninstall(_)
|
||||
DenoSubcommand::Install(InstallFlags::Global(..))
|
||||
| DenoSubcommand::Uninstall(_)
|
||||
)
|
||||
{
|
||||
return Ok(None);
|
||||
|
@ -174,7 +174,11 @@ impl CliLockfile {
|
|||
let config = deno_lockfile::WorkspaceConfig {
|
||||
root: WorkspaceMemberConfig {
|
||||
package_json_deps: pkg_json_deps(root_folder.pkg_json.as_deref()),
|
||||
dependencies: deno_json_deps(root_folder.deno_json.as_deref()),
|
||||
dependencies: if let Some(map) = maybe_external_import_map {
|
||||
import_map_deps(map)
|
||||
} else {
|
||||
deno_json_deps(root_folder.deno_json.as_deref())
|
||||
},
|
||||
},
|
||||
members: workspace
|
||||
.config_folders()
|
||||
|
|
152
cli/args/mod.rs
152
cli/args/mod.rs
|
@ -9,6 +9,7 @@ mod package_json;
|
|||
|
||||
use deno_ast::MediaType;
|
||||
use deno_ast::SourceMapOption;
|
||||
use deno_cache_dir::file_fetcher::CacheSetting;
|
||||
use deno_config::deno_json::NodeModulesDirMode;
|
||||
use deno_config::workspace::CreateResolverOptions;
|
||||
use deno_config::workspace::FolderConfigs;
|
||||
|
@ -23,14 +24,15 @@ use deno_config::workspace::WorkspaceLintConfig;
|
|||
use deno_config::workspace::WorkspaceResolver;
|
||||
use deno_core::resolve_url_or_path;
|
||||
use deno_graph::GraphKind;
|
||||
use deno_lint::linter::LintConfig as DenoLintConfig;
|
||||
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_semver::npm::NpmPackageReqReference;
|
||||
use deno_telemetry::OtelConfig;
|
||||
use deno_telemetry::OtelRuntimeConfig;
|
||||
use import_map::resolve_import_map_value_from_specifier;
|
||||
|
||||
pub use deno_config::deno_json::BenchConfig;
|
||||
|
@ -84,7 +86,7 @@ use thiserror::Error;
|
|||
|
||||
use crate::cache;
|
||||
use crate::cache::DenoDirProvider;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists;
|
||||
use crate::version;
|
||||
|
||||
|
@ -216,52 +218,6 @@ pub fn ts_config_to_transpile_and_emit_options(
|
|||
))
|
||||
}
|
||||
|
||||
/// Indicates how cached source files should be handled.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum CacheSetting {
|
||||
/// Only the cached files should be used. Any files not in the cache will
|
||||
/// error. This is the equivalent of `--cached-only` in the CLI.
|
||||
Only,
|
||||
/// No cached source files should be used, and all files should be reloaded.
|
||||
/// This is the equivalent of `--reload` in the CLI.
|
||||
ReloadAll,
|
||||
/// Only some cached resources should be used. This is the equivalent of
|
||||
/// `--reload=jsr:@std/http/file-server` or
|
||||
/// `--reload=jsr:@std/http/file-server,jsr:@std/assert/assert-equals`.
|
||||
ReloadSome(Vec<String>),
|
||||
/// The usability of a cached value is determined by analyzing the cached
|
||||
/// headers and other metadata associated with a cached response, reloading
|
||||
/// any cached "non-fresh" cached responses.
|
||||
RespectHeaders,
|
||||
/// The cached source files should be used for local modules. This is the
|
||||
/// default behavior of the CLI.
|
||||
Use,
|
||||
}
|
||||
|
||||
impl CacheSetting {
|
||||
pub fn as_npm_cache_setting(&self) -> NpmCacheSetting {
|
||||
match self {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
CacheSetting::RespectHeaders => unreachable!(), // not supported
|
||||
CacheSetting::Use => NpmCacheSetting::Use,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WorkspaceBenchOptions {
|
||||
pub filter: Option<String>,
|
||||
pub json: bool,
|
||||
|
@ -816,6 +772,7 @@ pub struct CliOptions {
|
|||
maybe_node_modules_folder: Option<PathBuf>,
|
||||
npmrc: Arc<ResolvedNpmRc>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
maybe_external_import_map: Option<(PathBuf, serde_json::Value)>,
|
||||
overrides: CliOptionOverrides,
|
||||
pub start_dir: Arc<WorkspaceDirectory>,
|
||||
pub deno_dir_provider: Arc<DenoDirProvider>,
|
||||
|
@ -829,6 +786,7 @@ impl CliOptions {
|
|||
npmrc: Arc<ResolvedNpmRc>,
|
||||
start_dir: Arc<WorkspaceDirectory>,
|
||||
force_global_cache: bool,
|
||||
maybe_external_import_map: Option<(PathBuf, serde_json::Value)>,
|
||||
) -> Result<Self, AnyError> {
|
||||
if let Some(insecure_allowlist) =
|
||||
flags.unsafely_ignore_certificate_errors.as_ref()
|
||||
|
@ -866,6 +824,7 @@ impl CliOptions {
|
|||
maybe_node_modules_folder,
|
||||
overrides: Default::default(),
|
||||
main_module_cell: std::sync::OnceLock::new(),
|
||||
maybe_external_import_map,
|
||||
start_dir,
|
||||
deno_dir_provider,
|
||||
})
|
||||
|
@ -941,7 +900,33 @@ impl CliOptions {
|
|||
|
||||
let (npmrc, _) = discover_npmrc_from_workspace(&start_dir.workspace)?;
|
||||
|
||||
let maybe_lock_file = CliLockfile::discover(&flags, &start_dir.workspace)?;
|
||||
fn load_external_import_map(
|
||||
deno_json: &ConfigFile,
|
||||
) -> Result<Option<(PathBuf, serde_json::Value)>, AnyError> {
|
||||
if !deno_json.is_an_import_map() {
|
||||
if let Some(path) = deno_json.to_import_map_path()? {
|
||||
let contents = std::fs::read_to_string(&path).with_context(|| {
|
||||
format!("Unable to read import map at '{}'", path.display())
|
||||
})?;
|
||||
let map = serde_json::from_str(&contents)?;
|
||||
return Ok(Some((path, map)));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
let external_import_map =
|
||||
if let Some(deno_json) = start_dir.workspace.root_deno_json() {
|
||||
load_external_import_map(deno_json)?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let maybe_lock_file = CliLockfile::discover(
|
||||
&flags,
|
||||
&start_dir.workspace,
|
||||
external_import_map.as_ref().map(|(_, v)| v),
|
||||
)?;
|
||||
|
||||
log::debug!("Finished config loading.");
|
||||
|
||||
|
@ -952,6 +937,7 @@ impl CliOptions {
|
|||
npmrc,
|
||||
Arc::new(start_dir),
|
||||
false,
|
||||
external_import_map,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -979,9 +965,7 @@ impl CliOptions {
|
|||
match self.sub_command() {
|
||||
DenoSubcommand::Cache(_) => GraphKind::All,
|
||||
DenoSubcommand::Check(_) => GraphKind::TypesOnly,
|
||||
DenoSubcommand::Install(InstallFlags {
|
||||
kind: InstallKind::Local(_),
|
||||
}) => GraphKind::All,
|
||||
DenoSubcommand::Install(InstallFlags::Local(_)) => GraphKind::All,
|
||||
_ => self.type_check_mode().as_graph_kind(),
|
||||
}
|
||||
}
|
||||
|
@ -1071,10 +1055,10 @@ impl CliOptions {
|
|||
|
||||
pub async fn create_workspace_resolver(
|
||||
&self,
|
||||
file_fetcher: &FileFetcher,
|
||||
file_fetcher: &CliFileFetcher,
|
||||
pkg_json_dep_resolution: PackageJsonDepResolution,
|
||||
) -> Result<WorkspaceResolver, AnyError> {
|
||||
let overrode_no_import_map = self
|
||||
let overrode_no_import_map: bool = self
|
||||
.overrides
|
||||
.import_map_specifier
|
||||
.as_ref()
|
||||
|
@ -1102,7 +1086,19 @@ impl CliOptions {
|
|||
value,
|
||||
})
|
||||
}
|
||||
None => None,
|
||||
None => {
|
||||
if let Some((path, import_map)) =
|
||||
self.maybe_external_import_map.as_ref()
|
||||
{
|
||||
let path_url = deno_path_util::url_from_file_path(path)?;
|
||||
Some(deno_config::workspace::SpecifiedImportMap {
|
||||
base_url: path_url,
|
||||
value: import_map.clone(),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(self.workspace().create_resolver(
|
||||
|
@ -1141,7 +1137,7 @@ impl CliOptions {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn otel_config(&self) -> Option<OtelConfig> {
|
||||
pub fn otel_config(&self) -> OtelConfig {
|
||||
self.flags.otel_config()
|
||||
}
|
||||
|
||||
|
@ -1365,9 +1361,7 @@ impl CliOptions {
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn resolve_deno_lint_config(
|
||||
&self,
|
||||
) -> Result<deno_lint::linter::LintConfig, AnyError> {
|
||||
pub fn resolve_deno_lint_config(&self) -> Result<DenoLintConfig, AnyError> {
|
||||
let ts_config_result =
|
||||
self.resolve_ts_config_for_emit(TsConfigType::Emit)?;
|
||||
|
||||
|
@ -1376,7 +1370,7 @@ impl CliOptions {
|
|||
ts_config_result.ts_config,
|
||||
)?;
|
||||
|
||||
Ok(deno_lint::linter::LintConfig {
|
||||
Ok(DenoLintConfig {
|
||||
default_jsx_factory: (!transpile_options.jsx_automatic)
|
||||
.then(|| transpile_options.jsx_factory.clone()),
|
||||
default_jsx_fragment_factory: (!transpile_options.jsx_automatic)
|
||||
|
@ -1558,11 +1552,11 @@ impl CliOptions {
|
|||
DenoSubcommand::Check(check_flags) => {
|
||||
Some(files_to_urls(&check_flags.files))
|
||||
}
|
||||
DenoSubcommand::Install(InstallFlags {
|
||||
kind: InstallKind::Global(flags),
|
||||
}) => Url::parse(&flags.module_url)
|
||||
.ok()
|
||||
.map(|url| vec![Cow::Owned(url)]),
|
||||
DenoSubcommand::Install(InstallFlags::Global(flags)) => {
|
||||
Url::parse(&flags.module_url)
|
||||
.ok()
|
||||
.map(|url| vec![Cow::Owned(url)])
|
||||
}
|
||||
DenoSubcommand::Doc(DocFlags {
|
||||
source_files: DocSourceFileFlag::Paths(paths),
|
||||
..
|
||||
|
@ -1698,6 +1692,7 @@ impl CliOptions {
|
|||
"detect-cjs",
|
||||
"fmt-component",
|
||||
"fmt-sql",
|
||||
"lazy-npm-caching",
|
||||
])
|
||||
.collect();
|
||||
|
||||
|
@ -1776,6 +1771,19 @@ impl CliOptions {
|
|||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unstable_npm_lazy_caching(&self) -> bool {
|
||||
self.flags.unstable_config.npm_lazy_caching
|
||||
|| self.workspace().has_unstable("npm-lazy-caching")
|
||||
}
|
||||
|
||||
pub fn default_npm_caching_strategy(&self) -> NpmCachingStrategy {
|
||||
if self.flags.unstable_config.npm_lazy_caching {
|
||||
NpmCachingStrategy::Lazy
|
||||
} else {
|
||||
NpmCachingStrategy::Eager
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves the path to use for a local node_modules folder.
|
||||
|
@ -1990,6 +1998,20 @@ fn load_env_variables_from_env_file(filename: Option<&Vec<String>>) {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum NpmCachingStrategy {
|
||||
Eager,
|
||||
Lazy,
|
||||
Manual,
|
||||
}
|
||||
|
||||
pub(crate) fn otel_runtime_config() -> OtelRuntimeConfig {
|
||||
OtelRuntimeConfig {
|
||||
runtime_name: Cow::Borrowed("deno"),
|
||||
runtime_version: Cow::Borrowed(crate::version::DENO_VERSION_INFO.deno),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use pretty_assertions::assert_eq;
|
||||
|
|
|
@ -1,369 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use base64::prelude::BASE64_STANDARD;
|
||||
use base64::Engine;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use log::debug;
|
||||
use log::error;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::net::IpAddr;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::net::Ipv6Addr;
|
||||
use std::net::SocketAddr;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum AuthTokenData {
|
||||
Bearer(String),
|
||||
Basic { username: String, password: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AuthToken {
|
||||
host: AuthDomain,
|
||||
token: AuthTokenData,
|
||||
}
|
||||
|
||||
impl fmt::Display for AuthToken {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match &self.token {
|
||||
AuthTokenData::Bearer(token) => write!(f, "Bearer {token}"),
|
||||
AuthTokenData::Basic { username, password } => {
|
||||
let credentials = format!("{username}:{password}");
|
||||
write!(f, "Basic {}", BASE64_STANDARD.encode(credentials))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure which contains bearer tokens that can be used when sending
|
||||
/// requests to websites, intended to authorize access to private resources
|
||||
/// such as remote modules.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AuthTokens(Vec<AuthToken>);
|
||||
|
||||
/// An authorization domain, either an exact or suffix match.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum AuthDomain {
|
||||
Ip(IpAddr),
|
||||
IpPort(SocketAddr),
|
||||
/// Suffix match, no dot. May include a port.
|
||||
Suffix(Cow<'static, str>),
|
||||
}
|
||||
|
||||
impl<T: ToString> From<T> for AuthDomain {
|
||||
fn from(value: T) -> Self {
|
||||
let s = value.to_string().to_lowercase();
|
||||
if let Ok(ip) = SocketAddr::from_str(&s) {
|
||||
return AuthDomain::IpPort(ip);
|
||||
};
|
||||
if s.starts_with('[') && s.ends_with(']') {
|
||||
if let Ok(ip) = Ipv6Addr::from_str(&s[1..s.len() - 1]) {
|
||||
return AuthDomain::Ip(ip.into());
|
||||
}
|
||||
} else if let Ok(ip) = Ipv4Addr::from_str(&s) {
|
||||
return AuthDomain::Ip(ip.into());
|
||||
}
|
||||
if let Some(s) = s.strip_prefix('.') {
|
||||
AuthDomain::Suffix(Cow::Owned(s.to_owned()))
|
||||
} else {
|
||||
AuthDomain::Suffix(Cow::Owned(s))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthDomain {
|
||||
pub fn matches(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
let Some(host) = specifier.host_str() else {
|
||||
return false;
|
||||
};
|
||||
match *self {
|
||||
Self::Ip(ip) => {
|
||||
let AuthDomain::Ip(parsed) = AuthDomain::from(host) else {
|
||||
return false;
|
||||
};
|
||||
ip == parsed && specifier.port().is_none()
|
||||
}
|
||||
Self::IpPort(ip) => {
|
||||
let AuthDomain::Ip(parsed) = AuthDomain::from(host) else {
|
||||
return false;
|
||||
};
|
||||
ip.ip() == parsed && specifier.port() == Some(ip.port())
|
||||
}
|
||||
Self::Suffix(ref suffix) => {
|
||||
let hostname = if let Some(port) = specifier.port() {
|
||||
Cow::Owned(format!("{}:{}", host, port))
|
||||
} else {
|
||||
Cow::Borrowed(host)
|
||||
};
|
||||
|
||||
if suffix.len() == hostname.len() {
|
||||
return suffix == &hostname;
|
||||
}
|
||||
|
||||
// If it's a suffix match, ensure a dot
|
||||
if hostname.ends_with(suffix.as_ref())
|
||||
&& hostname.ends_with(&format!(".{suffix}"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AuthTokens {
|
||||
/// Create a new set of tokens based on the provided string. It is intended
|
||||
/// that the string be the value of an environment variable and the string is
|
||||
/// parsed for token values. The string is expected to be a semi-colon
|
||||
/// separated string, where each value is `{token}@{hostname}`.
|
||||
pub fn new(maybe_tokens_str: Option<String>) -> Self {
|
||||
let mut tokens = Vec::new();
|
||||
if let Some(tokens_str) = maybe_tokens_str {
|
||||
for token_str in tokens_str.trim().split(';') {
|
||||
if token_str.contains('@') {
|
||||
let mut iter = token_str.rsplitn(2, '@');
|
||||
let host = AuthDomain::from(iter.next().unwrap());
|
||||
let token = iter.next().unwrap();
|
||||
if token.contains(':') {
|
||||
let mut iter = token.rsplitn(2, ':');
|
||||
let password = iter.next().unwrap().to_owned();
|
||||
let username = iter.next().unwrap().to_owned();
|
||||
tokens.push(AuthToken {
|
||||
host,
|
||||
token: AuthTokenData::Basic { username, password },
|
||||
});
|
||||
} else {
|
||||
tokens.push(AuthToken {
|
||||
host,
|
||||
token: AuthTokenData::Bearer(token.to_string()),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
error!("Badly formed auth token discarded.");
|
||||
}
|
||||
}
|
||||
debug!("Parsed {} auth token(s).", tokens.len());
|
||||
}
|
||||
|
||||
Self(tokens)
|
||||
}
|
||||
|
||||
/// Attempt to match the provided specifier to the tokens in the set. The
|
||||
/// matching occurs from the right of the hostname plus port, irrespective of
|
||||
/// scheme. For example `https://www.deno.land:8080/` would match a token
|
||||
/// with a host value of `deno.land:8080` but not match `www.deno.land`. The
|
||||
/// matching is case insensitive.
|
||||
pub fn get(&self, specifier: &ModuleSpecifier) -> Option<AuthToken> {
|
||||
self.0.iter().find_map(|t| {
|
||||
if t.host.matches(specifier) {
|
||||
Some(t.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use deno_core::resolve_url;
|
||||
|
||||
#[test]
|
||||
fn test_auth_token() {
|
||||
let auth_tokens = AuthTokens::new(Some("abc123@deno.land".to_string()));
|
||||
let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap();
|
||||
assert_eq!(
|
||||
auth_tokens.get(&fixture).unwrap().to_string(),
|
||||
"Bearer abc123"
|
||||
);
|
||||
let fixture = resolve_url("https://www.deno.land/x/mod.ts").unwrap();
|
||||
assert_eq!(
|
||||
auth_tokens.get(&fixture).unwrap().to_string(),
|
||||
"Bearer abc123".to_string()
|
||||
);
|
||||
let fixture = resolve_url("http://127.0.0.1:8080/x/mod.ts").unwrap();
|
||||
assert_eq!(auth_tokens.get(&fixture), None);
|
||||
let fixture =
|
||||
resolve_url("https://deno.land.example.com/x/mod.ts").unwrap();
|
||||
assert_eq!(auth_tokens.get(&fixture), None);
|
||||
let fixture = resolve_url("https://deno.land:8080/x/mod.ts").unwrap();
|
||||
assert_eq!(auth_tokens.get(&fixture), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auth_tokens_multiple() {
|
||||
let auth_tokens =
|
||||
AuthTokens::new(Some("abc123@deno.land;def456@example.com".to_string()));
|
||||
let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap();
|
||||
assert_eq!(
|
||||
auth_tokens.get(&fixture).unwrap().to_string(),
|
||||
"Bearer abc123".to_string()
|
||||
);
|
||||
let fixture = resolve_url("http://example.com/a/file.ts").unwrap();
|
||||
assert_eq!(
|
||||
auth_tokens.get(&fixture).unwrap().to_string(),
|
||||
"Bearer def456".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auth_tokens_space() {
|
||||
let auth_tokens = AuthTokens::new(Some(
|
||||
" abc123@deno.land;def456@example.com\t".to_string(),
|
||||
));
|
||||
let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap();
|
||||
assert_eq!(
|
||||
auth_tokens.get(&fixture).unwrap().to_string(),
|
||||
"Bearer abc123".to_string()
|
||||
);
|
||||
let fixture = resolve_url("http://example.com/a/file.ts").unwrap();
|
||||
assert_eq!(
|
||||
auth_tokens.get(&fixture).unwrap().to_string(),
|
||||
"Bearer def456".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auth_tokens_newline() {
|
||||
let auth_tokens = AuthTokens::new(Some(
|
||||
"\nabc123@deno.land;def456@example.com\n".to_string(),
|
||||
));
|
||||
let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap();
|
||||
assert_eq!(
|
||||
auth_tokens.get(&fixture).unwrap().to_string(),
|
||||
"Bearer abc123".to_string()
|
||||
);
|
||||
let fixture = resolve_url("http://example.com/a/file.ts").unwrap();
|
||||
assert_eq!(
|
||||
auth_tokens.get(&fixture).unwrap().to_string(),
|
||||
"Bearer def456".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auth_tokens_port() {
|
||||
let auth_tokens =
|
||||
AuthTokens::new(Some("abc123@deno.land:8080".to_string()));
|
||||
let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap();
|
||||
assert_eq!(auth_tokens.get(&fixture), None);
|
||||
let fixture = resolve_url("http://deno.land:8080/x/mod.ts").unwrap();
|
||||
assert_eq!(
|
||||
auth_tokens.get(&fixture).unwrap().to_string(),
|
||||
"Bearer abc123".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auth_tokens_contain_at() {
|
||||
let auth_tokens = AuthTokens::new(Some("abc@123@deno.land".to_string()));
|
||||
let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap();
|
||||
assert_eq!(
|
||||
auth_tokens.get(&fixture).unwrap().to_string(),
|
||||
"Bearer abc@123".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auth_token_basic() {
|
||||
let auth_tokens = AuthTokens::new(Some("abc:123@deno.land".to_string()));
|
||||
let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap();
|
||||
assert_eq!(
|
||||
auth_tokens.get(&fixture).unwrap().to_string(),
|
||||
"Basic YWJjOjEyMw=="
|
||||
);
|
||||
let fixture = resolve_url("https://www.deno.land/x/mod.ts").unwrap();
|
||||
assert_eq!(
|
||||
auth_tokens.get(&fixture).unwrap().to_string(),
|
||||
"Basic YWJjOjEyMw==".to_string()
|
||||
);
|
||||
let fixture = resolve_url("http://127.0.0.1:8080/x/mod.ts").unwrap();
|
||||
assert_eq!(auth_tokens.get(&fixture), None);
|
||||
let fixture =
|
||||
resolve_url("https://deno.land.example.com/x/mod.ts").unwrap();
|
||||
assert_eq!(auth_tokens.get(&fixture), None);
|
||||
let fixture = resolve_url("https://deno.land:8080/x/mod.ts").unwrap();
|
||||
assert_eq!(auth_tokens.get(&fixture), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_ip() {
|
||||
let ip = AuthDomain::from("[2001:db8:a::123]");
|
||||
assert_eq!("Ip(2001:db8:a::123)", format!("{ip:?}"));
|
||||
let ip = AuthDomain::from("[2001:db8:a::123]:8080");
|
||||
assert_eq!("IpPort([2001:db8:a::123]:8080)", format!("{ip:?}"));
|
||||
let ip = AuthDomain::from("1.1.1.1");
|
||||
assert_eq!("Ip(1.1.1.1)", format!("{ip:?}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_case_insensitive() {
|
||||
let domain = AuthDomain::from("EXAMPLE.com");
|
||||
assert!(
|
||||
domain.matches(&ModuleSpecifier::parse("http://example.com").unwrap())
|
||||
);
|
||||
assert!(
|
||||
domain.matches(&ModuleSpecifier::parse("http://example.COM").unwrap())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_matches() {
|
||||
let candidates = [
|
||||
"example.com",
|
||||
"www.example.com",
|
||||
"1.1.1.1",
|
||||
"[2001:db8:a::123]",
|
||||
// These will never match
|
||||
"example.com.evil.com",
|
||||
"1.1.1.1.evil.com",
|
||||
"notexample.com",
|
||||
"www.notexample.com",
|
||||
];
|
||||
let domains = [
|
||||
("example.com", vec!["example.com", "www.example.com"]),
|
||||
(".example.com", vec!["example.com", "www.example.com"]),
|
||||
("www.example.com", vec!["www.example.com"]),
|
||||
("1.1.1.1", vec!["1.1.1.1"]),
|
||||
("[2001:db8:a::123]", vec!["[2001:db8:a::123]"]),
|
||||
];
|
||||
let url = |c: &str| ModuleSpecifier::parse(&format!("http://{c}")).unwrap();
|
||||
let url_port =
|
||||
|c: &str| ModuleSpecifier::parse(&format!("http://{c}:8080")).unwrap();
|
||||
|
||||
// Generate each candidate with and without a port
|
||||
let candidates = candidates
|
||||
.into_iter()
|
||||
.flat_map(|c| [url(c), url_port(c)])
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (domain, expected_domain) in domains {
|
||||
// Test without a port -- all candidates return without a port
|
||||
let auth_domain = AuthDomain::from(domain);
|
||||
let actual = candidates
|
||||
.iter()
|
||||
.filter(|c| auth_domain.matches(c))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
let expected = expected_domain.iter().map(|u| url(u)).collect::<Vec<_>>();
|
||||
assert_eq!(actual, expected);
|
||||
|
||||
// Test with a port, all candidates return with a port
|
||||
let auth_domain = AuthDomain::from(&format!("{domain}:8080"));
|
||||
let actual = candidates
|
||||
.iter()
|
||||
.filter(|c| auth_domain.matches(c))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
let expected = expected_domain
|
||||
.iter()
|
||||
.map(|u| url_port(u))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
||||
}
|
93
cli/cache/mod.rs
vendored
93
cli/cache/mod.rs
vendored
|
@ -1,18 +1,19 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::args::jsr_url;
|
||||
use crate::args::CacheSetting;
|
||||
use crate::errors::get_error_class_name;
|
||||
use crate::file_fetcher::CliFetchNoFollowErrorKind;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::file_fetcher::FetchNoFollowOptions;
|
||||
use crate::file_fetcher::FetchOptions;
|
||||
use crate::file_fetcher::FetchPermissionsOptionRef;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::FileOrRedirect;
|
||||
use crate::util::fs::atomic_write_file_with_retries;
|
||||
use crate::util::fs::atomic_write_file_with_retries_and_fs;
|
||||
use crate::util::fs::AtomicWriteFileFsAdapter;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_cache_dir::file_fetcher::CacheSetting;
|
||||
use deno_cache_dir::file_fetcher::FetchNoFollowErrorKind;
|
||||
use deno_cache_dir::file_fetcher::FileOrRedirect;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::ModuleSpecifier;
|
||||
|
@ -190,7 +191,7 @@ pub struct FetchCacherOptions {
|
|||
/// a concise interface to the DENO_DIR when building module graphs.
|
||||
pub struct FetchCacher {
|
||||
pub file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
file_fetcher: Arc<CliFileFetcher>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
|
@ -202,7 +203,7 @@ pub struct FetchCacher {
|
|||
|
||||
impl FetchCacher {
|
||||
pub fn new(
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
file_fetcher: Arc<CliFileFetcher>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
|
@ -320,18 +321,18 @@ impl Loader for FetchCacher {
|
|||
LoaderCacheSetting::Only => Some(CacheSetting::Only),
|
||||
};
|
||||
file_fetcher
|
||||
.fetch_no_follow_with_options(FetchNoFollowOptions {
|
||||
fetch_options: FetchOptions {
|
||||
specifier: &specifier,
|
||||
permissions: if is_statically_analyzable {
|
||||
FetchPermissionsOptionRef::StaticContainer(&permissions)
|
||||
} else {
|
||||
FetchPermissionsOptionRef::DynamicContainer(&permissions)
|
||||
},
|
||||
maybe_auth: None,
|
||||
maybe_accept: None,
|
||||
maybe_cache_setting: maybe_cache_setting.as_ref(),
|
||||
},
|
||||
.fetch_no_follow(
|
||||
&specifier,
|
||||
FetchPermissionsOptionRef::Restricted(&permissions,
|
||||
if is_statically_analyzable {
|
||||
deno_runtime::deno_permissions::CheckSpecifierKind::Static
|
||||
} else {
|
||||
deno_runtime::deno_permissions::CheckSpecifierKind::Dynamic
|
||||
}),
|
||||
FetchNoFollowOptions {
|
||||
maybe_auth: None,
|
||||
maybe_accept: None,
|
||||
maybe_cache_setting: maybe_cache_setting.as_ref(),
|
||||
maybe_checksum: options.maybe_checksum.as_ref(),
|
||||
})
|
||||
.await
|
||||
|
@ -348,7 +349,7 @@ impl Loader for FetchCacher {
|
|||
(None, None) => None,
|
||||
};
|
||||
Ok(Some(LoadResponse::Module {
|
||||
specifier: file.specifier,
|
||||
specifier: file.url,
|
||||
maybe_headers,
|
||||
content: file.source,
|
||||
}))
|
||||
|
@ -361,18 +362,46 @@ impl Loader for FetchCacher {
|
|||
}
|
||||
})
|
||||
.unwrap_or_else(|err| {
|
||||
if let Some(io_err) = err.downcast_ref::<std::io::Error>() {
|
||||
if io_err.kind() == std::io::ErrorKind::NotFound {
|
||||
return Ok(None);
|
||||
} else {
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
let error_class_name = get_error_class_name(&err);
|
||||
match error_class_name {
|
||||
"NotFound" => Ok(None),
|
||||
"NotCached" if options.cache_setting == LoaderCacheSetting::Only => Ok(None),
|
||||
_ => Err(err),
|
||||
let err = err.into_kind();
|
||||
match err {
|
||||
CliFetchNoFollowErrorKind::FetchNoFollow(err) => {
|
||||
let err = err.into_kind();
|
||||
match err {
|
||||
FetchNoFollowErrorKind::NotFound(_) => Ok(None),
|
||||
FetchNoFollowErrorKind::UrlToFilePath { .. } |
|
||||
FetchNoFollowErrorKind::ReadingBlobUrl { .. } |
|
||||
FetchNoFollowErrorKind::ReadingFile { .. } |
|
||||
FetchNoFollowErrorKind::FetchingRemote { .. } |
|
||||
FetchNoFollowErrorKind::ClientError { .. } |
|
||||
FetchNoFollowErrorKind::NoRemote { .. } |
|
||||
FetchNoFollowErrorKind::DataUrlDecode { .. } |
|
||||
FetchNoFollowErrorKind::RedirectResolution { .. } |
|
||||
FetchNoFollowErrorKind::CacheRead { .. } |
|
||||
FetchNoFollowErrorKind::CacheSave { .. } |
|
||||
FetchNoFollowErrorKind::UnsupportedScheme { .. } |
|
||||
FetchNoFollowErrorKind::RedirectHeaderParse { .. } |
|
||||
FetchNoFollowErrorKind::InvalidHeader { .. } => Err(AnyError::from(err)),
|
||||
FetchNoFollowErrorKind::NotCached { .. } => {
|
||||
if options.cache_setting == LoaderCacheSetting::Only {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(AnyError::from(err))
|
||||
}
|
||||
},
|
||||
FetchNoFollowErrorKind::ChecksumIntegrity(err) => {
|
||||
// convert to the equivalent deno_graph error so that it
|
||||
// enhances it if this is passed to deno_graph
|
||||
Err(
|
||||
deno_graph::source::ChecksumIntegrityError {
|
||||
actual: err.actual,
|
||||
expected: err.expected,
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
CliFetchNoFollowErrorKind::PermissionCheck(permission_check_error) => Err(AnyError::from(permission_check_error)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
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
|
||||
|
|
|
@ -22,7 +22,7 @@ use crate::cache::ModuleInfoCache;
|
|||
use crate::cache::NodeAnalysisCache;
|
||||
use crate::cache::ParsedSourceCache;
|
||||
use crate::emit::Emitter;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::graph_container::MainModuleGraphContainer;
|
||||
use crate::graph_util::FileWatcherReporter;
|
||||
use crate::graph_util::ModuleGraphBuilder;
|
||||
|
@ -185,7 +185,7 @@ struct CliFactoryServices {
|
|||
emit_cache: Deferred<Arc<EmitCache>>,
|
||||
emitter: Deferred<Arc<Emitter>>,
|
||||
feature_checker: Deferred<Arc<FeatureChecker>>,
|
||||
file_fetcher: Deferred<Arc<FileFetcher>>,
|
||||
file_fetcher: Deferred<Arc<CliFileFetcher>>,
|
||||
fs: Deferred<Arc<dyn deno_fs::FileSystem>>,
|
||||
global_http_cache: Deferred<Arc<GlobalHttpCache>>,
|
||||
http_cache: Deferred<Arc<dyn HttpCache>>,
|
||||
|
@ -350,16 +350,17 @@ impl CliFactory {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn file_fetcher(&self) -> Result<&Arc<FileFetcher>, AnyError> {
|
||||
pub fn file_fetcher(&self) -> Result<&Arc<CliFileFetcher>, AnyError> {
|
||||
self.services.file_fetcher.get_or_try_init(|| {
|
||||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(FileFetcher::new(
|
||||
Ok(Arc::new(CliFileFetcher::new(
|
||||
self.http_cache()?.clone(),
|
||||
cli_options.cache_setting(),
|
||||
!cli_options.no_remote(),
|
||||
self.http_client_provider().clone(),
|
||||
self.blob_store().clone(),
|
||||
Some(self.text_only_progress_bar().clone()),
|
||||
!cli_options.no_remote(),
|
||||
cli_options.cache_setting(),
|
||||
log::Level::Info,
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
@ -984,6 +985,7 @@ impl CliFactory {
|
|||
cli_options.sub_command().clone(),
|
||||
self.create_cli_main_worker_options()?,
|
||||
self.cli_options()?.otel_config(),
|
||||
self.cli_options()?.default_npm_caching_strategy(),
|
||||
))
|
||||
}
|
||||
|
||||
|
|
1388
cli/file_fetcher.rs
1388
cli/file_fetcher.rs
File diff suppressed because it is too large
Load diff
|
@ -4,6 +4,7 @@ use crate::args::config_to_deno_graph_workspace_member;
|
|||
use crate::args::jsr_url;
|
||||
use crate::args::CliLockfile;
|
||||
use crate::args::CliOptions;
|
||||
pub use crate::args::NpmCachingStrategy;
|
||||
use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
|
||||
use crate::cache;
|
||||
use crate::cache::FetchCacher;
|
||||
|
@ -12,7 +13,7 @@ use crate::cache::ModuleInfoCache;
|
|||
use crate::cache::ParsedSourceCache;
|
||||
use crate::colors;
|
||||
use crate::errors::get_error_class_name;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::resolver::CjsTracker;
|
||||
use crate::resolver::CliResolver;
|
||||
|
@ -109,6 +110,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,
|
||||
|
@ -199,6 +219,7 @@ pub struct CreateGraphOptions<'a> {
|
|||
pub is_dynamic: bool,
|
||||
/// Specify `None` to use the default CLI loader.
|
||||
pub loader: Option<&'a mut dyn Loader>,
|
||||
pub npm_caching: NpmCachingStrategy,
|
||||
}
|
||||
|
||||
pub struct ModuleGraphCreator {
|
||||
|
@ -227,10 +248,11 @@ impl ModuleGraphCreator {
|
|||
&self,
|
||||
graph_kind: GraphKind,
|
||||
roots: Vec<ModuleSpecifier>,
|
||||
npm_caching: NpmCachingStrategy,
|
||||
) -> Result<deno_graph::ModuleGraph, AnyError> {
|
||||
let mut cache = self.module_graph_builder.create_graph_loader();
|
||||
self
|
||||
.create_graph_with_loader(graph_kind, roots, &mut cache)
|
||||
.create_graph_with_loader(graph_kind, roots, &mut cache, npm_caching)
|
||||
.await
|
||||
}
|
||||
|
||||
|
@ -239,6 +261,7 @@ impl ModuleGraphCreator {
|
|||
graph_kind: GraphKind,
|
||||
roots: Vec<ModuleSpecifier>,
|
||||
loader: &mut dyn Loader,
|
||||
npm_caching: NpmCachingStrategy,
|
||||
) -> Result<ModuleGraph, AnyError> {
|
||||
self
|
||||
.create_graph_with_options(CreateGraphOptions {
|
||||
|
@ -246,6 +269,7 @@ impl ModuleGraphCreator {
|
|||
graph_kind,
|
||||
roots,
|
||||
loader: Some(loader),
|
||||
npm_caching,
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
@ -298,6 +322,7 @@ impl ModuleGraphCreator {
|
|||
graph_kind: deno_graph::GraphKind::All,
|
||||
roots,
|
||||
loader: Some(&mut publish_loader),
|
||||
npm_caching: self.options.default_npm_caching_strategy(),
|
||||
})
|
||||
.await?;
|
||||
self.graph_valid(&graph)?;
|
||||
|
@ -357,6 +382,7 @@ impl ModuleGraphCreator {
|
|||
graph_kind,
|
||||
roots,
|
||||
loader: None,
|
||||
npm_caching: self.options.default_npm_caching_strategy(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
|
@ -405,7 +431,7 @@ pub struct ModuleGraphBuilder {
|
|||
caches: Arc<cache::Caches>,
|
||||
cjs_tracker: Arc<CjsTracker>,
|
||||
cli_options: Arc<CliOptions>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
file_fetcher: Arc<CliFileFetcher>,
|
||||
fs: Arc<dyn FileSystem>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
|
@ -424,7 +450,7 @@ impl ModuleGraphBuilder {
|
|||
caches: Arc<cache::Caches>,
|
||||
cjs_tracker: Arc<CjsTracker>,
|
||||
cli_options: Arc<CliOptions>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
file_fetcher: Arc<CliFileFetcher>,
|
||||
fs: Arc<dyn FileSystem>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
|
@ -546,7 +572,8 @@ impl ModuleGraphBuilder {
|
|||
};
|
||||
let cli_resolver = &self.resolver;
|
||||
let graph_resolver = self.create_graph_resolver()?;
|
||||
let graph_npm_resolver = cli_resolver.create_graph_npm_resolver();
|
||||
let graph_npm_resolver =
|
||||
cli_resolver.create_graph_npm_resolver(options.npm_caching);
|
||||
let maybe_file_watcher_reporter = self
|
||||
.maybe_file_watcher_reporter
|
||||
.as_ref()
|
||||
|
@ -573,6 +600,7 @@ impl ModuleGraphBuilder {
|
|||
resolver: Some(&graph_resolver),
|
||||
locker: locker.as_mut().map(|l| l as _),
|
||||
},
|
||||
options.npm_caching,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
@ -583,6 +611,7 @@ impl ModuleGraphBuilder {
|
|||
roots: Vec<ModuleSpecifier>,
|
||||
loader: &'a mut dyn deno_graph::source::Loader,
|
||||
options: deno_graph::BuildOptions<'a>,
|
||||
npm_caching: NpmCachingStrategy,
|
||||
) -> Result<(), AnyError> {
|
||||
// ensure an "npm install" is done if the user has explicitly
|
||||
// opted into using a node_modules directory
|
||||
|
@ -593,7 +622,13 @@ impl ModuleGraphBuilder {
|
|||
.unwrap_or(false)
|
||||
{
|
||||
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
let already_done =
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
if !already_done && matches!(npm_caching, NpmCachingStrategy::Eager) {
|
||||
npm_resolver
|
||||
.cache_packages(crate::npm::PackageCaching::All)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -603,19 +638,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -694,7 +717,9 @@ impl ModuleGraphBuilder {
|
|||
let parser = self.parsed_source_cache.as_capturing_parser();
|
||||
let cli_resolver = &self.resolver;
|
||||
let graph_resolver = self.create_graph_resolver()?;
|
||||
let graph_npm_resolver = cli_resolver.create_graph_npm_resolver();
|
||||
let graph_npm_resolver = cli_resolver.create_graph_npm_resolver(
|
||||
self.cli_options.default_npm_caching_strategy(),
|
||||
);
|
||||
|
||||
graph.build_fast_check_type_graph(
|
||||
deno_graph::BuildFastCheckTypeGraphOptions {
|
||||
|
|
895
cli/http_util.rs
895
cli/http_util.rs
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::args::jsr_url;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use dashmap::DashMap;
|
||||
use deno_core::serde_json;
|
||||
use deno_graph::packages::JsrPackageInfo;
|
||||
|
@ -19,11 +19,11 @@ pub struct JsrFetchResolver {
|
|||
/// It can be large and we don't want to store it.
|
||||
info_by_nv: DashMap<PackageNv, Option<Arc<JsrPackageVersionInfo>>>,
|
||||
info_by_name: DashMap<String, Option<Arc<JsrPackageInfo>>>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
file_fetcher: Arc<CliFileFetcher>,
|
||||
}
|
||||
|
||||
impl JsrFetchResolver {
|
||||
pub fn new(file_fetcher: Arc<FileFetcher>) -> Self {
|
||||
pub fn new(file_fetcher: Arc<CliFileFetcher>) -> Self {
|
||||
Self {
|
||||
nv_by_req: Default::default(),
|
||||
info_by_nv: Default::default(),
|
||||
|
|
|
@ -270,6 +270,10 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
if specifier.scheme() == "node" {
|
||||
return Some(specifier.to_string());
|
||||
}
|
||||
|
||||
if let Some(jsr_path) = specifier.as_str().strip_prefix(jsr_url().as_str())
|
||||
{
|
||||
let mut segments = jsr_path.split('/');
|
||||
|
@ -353,7 +357,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 +377,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 +427,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()?;
|
||||
|
@ -1371,7 +1391,7 @@ impl CodeActionCollection {
|
|||
character: import_start.column_index as u32,
|
||||
};
|
||||
let new_text = format!(
|
||||
"{}// @deno-types=\"{}\"\n",
|
||||
"{}// @ts-types=\"{}\"\n",
|
||||
if position.character == 0 { "" } else { "\n" },
|
||||
&types_specifier_text
|
||||
);
|
||||
|
@ -1384,7 +1404,7 @@ impl CodeActionCollection {
|
|||
};
|
||||
Some(lsp::CodeAction {
|
||||
title: format!(
|
||||
"Add @deno-types directive for \"{}\"",
|
||||
"Add @ts-types directive for \"{}\"",
|
||||
&types_specifier_text
|
||||
),
|
||||
kind: Some(lsp::CodeActionKind::QUICKFIX),
|
||||
|
|
|
@ -743,13 +743,16 @@ fn get_node_completions(
|
|||
}
|
||||
let items = SUPPORTED_BUILTIN_NODE_MODULES
|
||||
.iter()
|
||||
.map(|name| {
|
||||
.filter_map(|name| {
|
||||
if name.starts_with('_') {
|
||||
return None;
|
||||
}
|
||||
let specifier = format!("node:{}", name);
|
||||
let text_edit = Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
|
||||
range: *range,
|
||||
new_text: specifier.clone(),
|
||||
}));
|
||||
lsp::CompletionItem {
|
||||
Some(lsp::CompletionItem {
|
||||
label: specifier,
|
||||
kind: Some(lsp::CompletionItemKind::FILE),
|
||||
detail: Some("(node)".to_string()),
|
||||
|
@ -758,7 +761,7 @@ fn get_node_completions(
|
|||
IMPORT_COMMIT_CHARS.iter().map(|&c| c.into()).collect(),
|
||||
),
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
Some(CompletionList {
|
||||
|
|
|
@ -63,7 +63,7 @@ use crate::args::ConfigFile;
|
|||
use crate::args::LintFlags;
|
||||
use crate::args::LintOptions;
|
||||
use crate::cache::FastInsecureHasher;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::lsp::logging::lsp_warn;
|
||||
use crate::resolver::CliSloppyImportsResolver;
|
||||
use crate::resolver::SloppyImportsCachedFs;
|
||||
|
@ -459,6 +459,19 @@ impl Default for LanguagePreferences {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SuggestionActionsSettings {
|
||||
#[serde(default = "is_true")]
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
impl Default for SuggestionActionsSettings {
|
||||
fn default() -> Self {
|
||||
SuggestionActionsSettings { enabled: true }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateImportsOnFileMoveOptions {
|
||||
|
@ -490,6 +503,8 @@ pub struct LanguageWorkspaceSettings {
|
|||
#[serde(default)]
|
||||
pub suggest: CompletionSettings,
|
||||
#[serde(default)]
|
||||
pub suggestion_actions: SuggestionActionsSettings,
|
||||
#[serde(default)]
|
||||
pub update_imports_on_file_move: UpdateImportsOnFileMoveOptions,
|
||||
}
|
||||
|
||||
|
@ -1203,7 +1218,7 @@ impl ConfigData {
|
|||
specified_config: Option<&Path>,
|
||||
scope: &ModuleSpecifier,
|
||||
settings: &Settings,
|
||||
file_fetcher: &Arc<FileFetcher>,
|
||||
file_fetcher: &Arc<CliFileFetcher>,
|
||||
// sync requirement is because the lsp requires sync
|
||||
cached_deno_config_fs: &(dyn DenoConfigFs + Sync),
|
||||
deno_json_cache: &(dyn DenoJsonCache + Sync),
|
||||
|
@ -1298,7 +1313,7 @@ impl ConfigData {
|
|||
member_dir: Arc<WorkspaceDirectory>,
|
||||
scope: Arc<ModuleSpecifier>,
|
||||
settings: &Settings,
|
||||
file_fetcher: Option<&Arc<FileFetcher>>,
|
||||
file_fetcher: Option<&Arc<CliFileFetcher>>,
|
||||
) -> Self {
|
||||
let (settings, workspace_folder) = settings.get_for_specifier(&scope);
|
||||
let mut watched_files = HashMap::with_capacity(10);
|
||||
|
@ -1820,7 +1835,7 @@ impl ConfigTree {
|
|||
&mut self,
|
||||
settings: &Settings,
|
||||
workspace_files: &IndexSet<ModuleSpecifier>,
|
||||
file_fetcher: &Arc<FileFetcher>,
|
||||
file_fetcher: &Arc<CliFileFetcher>,
|
||||
) {
|
||||
lsp_log!("Refreshing configuration tree...");
|
||||
// since we're resolving a workspace multiple times in different
|
||||
|
@ -2293,6 +2308,7 @@ mod tests {
|
|||
enabled: true,
|
||||
},
|
||||
},
|
||||
suggestion_actions: SuggestionActionsSettings { enabled: true },
|
||||
update_imports_on_file_move: UpdateImportsOnFileMoveOptions {
|
||||
enabled: UpdateImportsOnFileMoveEnabled::Prompt
|
||||
}
|
||||
|
@ -2339,6 +2355,7 @@ mod tests {
|
|||
enabled: true,
|
||||
},
|
||||
},
|
||||
suggestion_actions: SuggestionActionsSettings { enabled: true },
|
||||
update_imports_on_file_move: UpdateImportsOnFileMoveOptions {
|
||||
enabled: UpdateImportsOnFileMoveEnabled::Prompt
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ use crate::resolver::SloppyImportsCachedFs;
|
|||
use crate::tools::lint::CliLinter;
|
||||
use crate::tools::lint::CliLinterOptions;
|
||||
use crate::tools::lint::LintRuleProvider;
|
||||
use crate::tsc::DiagnosticCategory;
|
||||
use crate::util::path::to_percent_decoded_str;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
|
@ -44,6 +45,7 @@ use deno_graph::source::ResolveError;
|
|||
use deno_graph::Resolution;
|
||||
use deno_graph::ResolutionError;
|
||||
use deno_graph::SpecifierError;
|
||||
use deno_lint::linter::LintConfig as DenoLintConfig;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolution;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolutionKind;
|
||||
use deno_runtime::deno_fs;
|
||||
|
@ -833,7 +835,7 @@ fn generate_lint_diagnostics(
|
|||
lint_rule_provider.resolve_lint_rules(Default::default(), None)
|
||||
},
|
||||
fix: false,
|
||||
deno_lint_config: deno_lint::linter::LintConfig {
|
||||
deno_lint_config: DenoLintConfig {
|
||||
default_jsx_factory: None,
|
||||
default_jsx_fragment_factory: None,
|
||||
},
|
||||
|
@ -907,8 +909,22 @@ async fn generate_ts_diagnostics(
|
|||
} else {
|
||||
Default::default()
|
||||
};
|
||||
for (specifier_str, ts_json_diagnostics) in ts_diagnostics_map {
|
||||
for (specifier_str, mut ts_json_diagnostics) in ts_diagnostics_map {
|
||||
let specifier = resolve_url(&specifier_str)?;
|
||||
let suggestion_actions_settings = snapshot
|
||||
.config
|
||||
.language_settings_for_specifier(&specifier)
|
||||
.map(|s| s.suggestion_actions.clone())
|
||||
.unwrap_or_default();
|
||||
if !suggestion_actions_settings.enabled {
|
||||
ts_json_diagnostics.retain(|d| {
|
||||
d.category != DiagnosticCategory::Suggestion
|
||||
// Still show deprecated and unused diagnostics.
|
||||
// https://github.com/microsoft/vscode/blob/ce50bd4876af457f64d83cfd956bc916535285f4/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts#L113-L114
|
||||
|| d.reports_deprecated == Some(true)
|
||||
|| d.reports_unnecessary == Some(true)
|
||||
});
|
||||
}
|
||||
let version = snapshot
|
||||
.documents
|
||||
.get(&specifier)
|
||||
|
@ -1263,7 +1279,7 @@ 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))
|
||||
|
@ -1356,7 +1372,7 @@ fn diagnose_resolution(
|
|||
}
|
||||
// don't bother warning about sloppy import redirects from .js to .d.ts
|
||||
// because explaining how to fix this via a diagnostic involves using
|
||||
// @deno-types and that's a bit complicated to explain
|
||||
// @ts-types and that's a bit complicated to explain
|
||||
let is_sloppy_import_dts_redirect = doc_specifier.scheme() == "file"
|
||||
&& doc.media_type().is_declaration()
|
||||
&& !MediaType::from_specifier(specifier).is_declaration();
|
||||
|
@ -1524,7 +1540,7 @@ fn diagnose_dependency(
|
|||
.iter()
|
||||
.map(|i| documents::to_lsp_range(&i.specifier_range))
|
||||
.collect();
|
||||
// TODO(nayeemrmn): This is a crude way of detecting `@deno-types` which has
|
||||
// TODO(nayeemrmn): This is a crude way of detecting `@ts-types` which has
|
||||
// a different specifier and therefore needs a separate call to
|
||||
// `diagnose_resolution()`. It would be much cleaner if that were modelled as
|
||||
// a separate dependency: https://github.com/denoland/deno_graph/issues/247.
|
||||
|
@ -1541,7 +1557,7 @@ fn diagnose_dependency(
|
|||
snapshot,
|
||||
dependency_key,
|
||||
if dependency.maybe_code.is_none()
|
||||
// If not @deno-types, diagnose the types if the code errored because
|
||||
// If not @ts-types, diagnose the types if the code errored because
|
||||
// it's likely resolving into the node_modules folder, which might be
|
||||
// erroring correctly due to resolution only being for bundlers. Let this
|
||||
// fail at runtime if necessary, but don't bother erroring in the editor
|
||||
|
@ -1952,7 +1968,7 @@ let c: number = "a";
|
|||
&[(
|
||||
"a.ts",
|
||||
r#"
|
||||
// @deno-types="bad.d.ts"
|
||||
// @ts-types="bad.d.ts"
|
||||
import "bad.js";
|
||||
import "bad.js";
|
||||
"#,
|
||||
|
@ -2006,11 +2022,11 @@ let c: number = "a";
|
|||
"range": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"character": 23
|
||||
"character": 21
|
||||
},
|
||||
"end": {
|
||||
"line": 1,
|
||||
"character": 33
|
||||
"character": 31
|
||||
}
|
||||
},
|
||||
"severity": 1,
|
||||
|
|
|
@ -65,6 +65,12 @@ pub enum LanguageId {
|
|||
Html,
|
||||
Css,
|
||||
Yaml,
|
||||
Sql,
|
||||
Svelte,
|
||||
Vue,
|
||||
Astro,
|
||||
Vento,
|
||||
Nunjucks,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
|
@ -81,6 +87,12 @@ impl LanguageId {
|
|||
LanguageId::Html => Some("html"),
|
||||
LanguageId::Css => Some("css"),
|
||||
LanguageId::Yaml => Some("yaml"),
|
||||
LanguageId::Sql => Some("sql"),
|
||||
LanguageId::Svelte => Some("svelte"),
|
||||
LanguageId::Vue => Some("vue"),
|
||||
LanguageId::Astro => Some("astro"),
|
||||
LanguageId::Vento => Some("vto"),
|
||||
LanguageId::Nunjucks => Some("njk"),
|
||||
LanguageId::Unknown => None,
|
||||
}
|
||||
}
|
||||
|
@ -96,6 +108,12 @@ impl LanguageId {
|
|||
LanguageId::Html => Some("text/html"),
|
||||
LanguageId::Css => Some("text/css"),
|
||||
LanguageId::Yaml => Some("application/yaml"),
|
||||
LanguageId::Sql => None,
|
||||
LanguageId::Svelte => None,
|
||||
LanguageId::Vue => None,
|
||||
LanguageId::Astro => None,
|
||||
LanguageId::Vento => None,
|
||||
LanguageId::Nunjucks => None,
|
||||
LanguageId::Unknown => None,
|
||||
}
|
||||
}
|
||||
|
@ -123,6 +141,12 @@ impl FromStr for LanguageId {
|
|||
"html" => Ok(Self::Html),
|
||||
"css" => Ok(Self::Css),
|
||||
"yaml" => Ok(Self::Yaml),
|
||||
"sql" => Ok(Self::Sql),
|
||||
"svelte" => Ok(Self::Svelte),
|
||||
"vue" => Ok(Self::Vue),
|
||||
"astro" => Ok(Self::Astro),
|
||||
"vento" => Ok(Self::Vento),
|
||||
"nunjucks" => Ok(Self::Nunjucks),
|
||||
_ => Ok(Self::Unknown),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
use crate::args::jsr_api_url;
|
||||
use crate::args::jsr_url;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::file_fetcher::TextDecodedFile;
|
||||
use crate::jsr::partial_jsr_package_version_info_from_slice;
|
||||
use crate::jsr::JsrFetchResolver;
|
||||
use dashmap::DashMap;
|
||||
|
@ -267,7 +268,7 @@ fn read_cached_url(
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct CliJsrSearchApi {
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
file_fetcher: Arc<CliFileFetcher>,
|
||||
resolver: JsrFetchResolver,
|
||||
search_cache: DashMap<String, Arc<Vec<String>>>,
|
||||
versions_cache: DashMap<String, Arc<Vec<Version>>>,
|
||||
|
@ -275,7 +276,7 @@ pub struct CliJsrSearchApi {
|
|||
}
|
||||
|
||||
impl CliJsrSearchApi {
|
||||
pub fn new(file_fetcher: Arc<FileFetcher>) -> Self {
|
||||
pub fn new(file_fetcher: Arc<CliFileFetcher>) -> Self {
|
||||
let resolver = JsrFetchResolver::new(file_fetcher.clone());
|
||||
Self {
|
||||
file_fetcher,
|
||||
|
@ -309,10 +310,8 @@ impl PackageSearchApi for CliJsrSearchApi {
|
|||
let file_fetcher = self.file_fetcher.clone();
|
||||
// spawn due to the lsp's `Send` requirement
|
||||
let file = deno_core::unsync::spawn(async move {
|
||||
file_fetcher
|
||||
.fetch_bypass_permissions(&search_url)
|
||||
.await?
|
||||
.into_text_decoded()
|
||||
let file = file_fetcher.fetch_bypass_permissions(&search_url).await?;
|
||||
TextDecodedFile::decode(file)
|
||||
})
|
||||
.await??;
|
||||
let names = Arc::new(parse_jsr_search_response(&file.source)?);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_cache_dir::file_fetcher::CacheSetting;
|
||||
use deno_config::workspace::WorkspaceDirectory;
|
||||
use deno_config::workspace::WorkspaceDiscoverOptions;
|
||||
use deno_core::anyhow::anyhow;
|
||||
|
@ -95,13 +96,12 @@ use crate::args::create_default_npmrc;
|
|||
use crate::args::get_root_cert_store;
|
||||
use crate::args::has_flag_env_var;
|
||||
use crate::args::CaData;
|
||||
use crate::args::CacheSetting;
|
||||
use crate::args::CliOptions;
|
||||
use crate::args::Flags;
|
||||
use crate::args::InternalFlags;
|
||||
use crate::args::UnstableFmtOptions;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::graph_util;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
use crate::lsp::config::ConfigWatchedFileType;
|
||||
|
@ -270,7 +270,12 @@ impl LanguageServer {
|
|||
open_docs: &open_docs,
|
||||
};
|
||||
let graph = module_graph_creator
|
||||
.create_graph_with_loader(GraphKind::All, roots.clone(), &mut loader)
|
||||
.create_graph_with_loader(
|
||||
GraphKind::All,
|
||||
roots.clone(),
|
||||
&mut loader,
|
||||
graph_util::NpmCachingStrategy::Eager,
|
||||
)
|
||||
.await?;
|
||||
graph_util::graph_valid(
|
||||
&graph,
|
||||
|
@ -953,15 +958,15 @@ impl Inner {
|
|||
}
|
||||
|
||||
async fn refresh_config_tree(&mut self) {
|
||||
let mut file_fetcher = FileFetcher::new(
|
||||
let file_fetcher = CliFileFetcher::new(
|
||||
self.cache.global().clone(),
|
||||
CacheSetting::RespectHeaders,
|
||||
true,
|
||||
self.http_client_provider.clone(),
|
||||
Default::default(),
|
||||
None,
|
||||
true,
|
||||
CacheSetting::RespectHeaders,
|
||||
super::logging::lsp_log_level(),
|
||||
);
|
||||
file_fetcher.set_download_log_level(super::logging::lsp_log_level());
|
||||
let file_fetcher = Arc::new(file_fetcher);
|
||||
self
|
||||
.config
|
||||
|
@ -3671,6 +3676,7 @@ impl Inner {
|
|||
.unwrap_or_else(create_default_npmrc),
|
||||
workspace,
|
||||
force_global_cache,
|
||||
None,
|
||||
)?;
|
||||
|
||||
let open_docs = self.documents.documents(DocumentsFilter::OpenDiagnosable);
|
||||
|
|
|
@ -11,21 +11,22 @@ use serde::Deserialize;
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::args::npm_registry_url;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::file_fetcher::TextDecodedFile;
|
||||
use crate::npm::NpmFetchResolver;
|
||||
|
||||
use super::search::PackageSearchApi;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CliNpmSearchApi {
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
file_fetcher: Arc<CliFileFetcher>,
|
||||
resolver: NpmFetchResolver,
|
||||
search_cache: DashMap<String, Arc<Vec<String>>>,
|
||||
versions_cache: DashMap<String, Arc<Vec<Version>>>,
|
||||
}
|
||||
|
||||
impl CliNpmSearchApi {
|
||||
pub fn new(file_fetcher: Arc<FileFetcher>) -> Self {
|
||||
pub fn new(file_fetcher: Arc<CliFileFetcher>) -> Self {
|
||||
let resolver = NpmFetchResolver::new(
|
||||
file_fetcher.clone(),
|
||||
Arc::new(NpmRc::default().as_resolved(npm_registry_url()).unwrap()),
|
||||
|
@ -57,10 +58,8 @@ impl PackageSearchApi for CliNpmSearchApi {
|
|||
.append_pair("text", &format!("{} boost-exact:false", query));
|
||||
let file_fetcher = self.file_fetcher.clone();
|
||||
let file = deno_core::unsync::spawn(async move {
|
||||
file_fetcher
|
||||
.fetch_bypass_permissions(&search_url)
|
||||
.await?
|
||||
.into_text_decoded()
|
||||
let file = file_fetcher.fetch_bypass_permissions(&search_url).await?;
|
||||
TextDecodedFile::decode(file)
|
||||
})
|
||||
.await??;
|
||||
let names = Arc::new(parse_npm_search_response(&file.source)?);
|
||||
|
|
|
@ -12,14 +12,15 @@ use super::path_to_regex::StringOrNumber;
|
|||
use super::path_to_regex::StringOrVec;
|
||||
use super::path_to_regex::Token;
|
||||
|
||||
use crate::args::CacheSetting;
|
||||
use crate::cache::GlobalHttpCache;
|
||||
use crate::cache::HttpCache;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::file_fetcher::FetchOptions;
|
||||
use crate::file_fetcher::FetchPermissionsOptionRef;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::TextDecodedFile;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
|
||||
use deno_cache_dir::file_fetcher::CacheSetting;
|
||||
use deno_core::anyhow::anyhow;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde::Deserialize;
|
||||
|
@ -418,7 +419,7 @@ enum VariableItems {
|
|||
pub struct ModuleRegistry {
|
||||
origins: HashMap<String, Vec<RegistryConfiguration>>,
|
||||
pub location: PathBuf,
|
||||
pub file_fetcher: Arc<FileFetcher>,
|
||||
pub file_fetcher: Arc<CliFileFetcher>,
|
||||
http_cache: Arc<GlobalHttpCache>,
|
||||
}
|
||||
|
||||
|
@ -432,15 +433,15 @@ impl ModuleRegistry {
|
|||
location.clone(),
|
||||
crate::cache::RealDenoCacheEnv,
|
||||
));
|
||||
let mut file_fetcher = FileFetcher::new(
|
||||
let file_fetcher = CliFileFetcher::new(
|
||||
http_cache.clone(),
|
||||
CacheSetting::RespectHeaders,
|
||||
true,
|
||||
http_client_provider,
|
||||
Default::default(),
|
||||
None,
|
||||
true,
|
||||
CacheSetting::RespectHeaders,
|
||||
super::logging::lsp_log_level(),
|
||||
);
|
||||
file_fetcher.set_download_log_level(super::logging::lsp_log_level());
|
||||
|
||||
Self {
|
||||
origins: HashMap::new(),
|
||||
|
@ -479,13 +480,15 @@ impl ModuleRegistry {
|
|||
let specifier = specifier.clone();
|
||||
async move {
|
||||
file_fetcher
|
||||
.fetch_with_options(FetchOptions {
|
||||
specifier: &specifier,
|
||||
permissions: FetchPermissionsOptionRef::AllowAll,
|
||||
maybe_auth: None,
|
||||
maybe_accept: Some("application/vnd.deno.reg.v2+json, application/vnd.deno.reg.v1+json;q=0.9, application/json;q=0.8"),
|
||||
maybe_cache_setting: None,
|
||||
})
|
||||
.fetch_with_options(
|
||||
&specifier,
|
||||
FetchPermissionsOptionRef::AllowAll,
|
||||
FetchOptions {
|
||||
maybe_auth: None,
|
||||
maybe_accept: Some("application/vnd.deno.reg.v2+json, application/vnd.deno.reg.v1+json;q=0.9, application/json;q=0.8"),
|
||||
maybe_cache_setting: None,
|
||||
}
|
||||
)
|
||||
.await
|
||||
}
|
||||
}).await?;
|
||||
|
@ -500,7 +503,7 @@ impl ModuleRegistry {
|
|||
);
|
||||
self.http_cache.set(specifier, headers_map, &[])?;
|
||||
}
|
||||
let file = fetch_result?.into_text_decoded()?;
|
||||
let file = TextDecodedFile::decode(fetch_result?)?;
|
||||
let config: RegistryConfigurationJson = serde_json::from_str(&file.source)?;
|
||||
validate_config(&config)?;
|
||||
Ok(config.registries)
|
||||
|
@ -584,12 +587,11 @@ impl ModuleRegistry {
|
|||
// spawn due to the lsp's `Send` requirement
|
||||
let file = deno_core::unsync::spawn({
|
||||
async move {
|
||||
file_fetcher
|
||||
let file = file_fetcher
|
||||
.fetch_bypass_permissions(&endpoint)
|
||||
.await
|
||||
.ok()?
|
||||
.into_text_decoded()
|
||||
.ok()
|
||||
.ok()?;
|
||||
TextDecodedFile::decode(file).ok()
|
||||
}
|
||||
})
|
||||
.await
|
||||
|
@ -983,12 +985,11 @@ impl ModuleRegistry {
|
|||
let file_fetcher = self.file_fetcher.clone();
|
||||
// spawn due to the lsp's `Send` requirement
|
||||
let file = deno_core::unsync::spawn(async move {
|
||||
file_fetcher
|
||||
let file = file_fetcher
|
||||
.fetch_bypass_permissions(&specifier)
|
||||
.await
|
||||
.ok()?
|
||||
.into_text_decoded()
|
||||
.ok()
|
||||
.ok()?;
|
||||
TextDecodedFile::decode(file).ok()
|
||||
})
|
||||
.await
|
||||
.ok()??;
|
||||
|
@ -1049,7 +1050,7 @@ impl ModuleRegistry {
|
|||
let file_fetcher = self.file_fetcher.clone();
|
||||
let specifier = specifier.clone();
|
||||
async move {
|
||||
file_fetcher
|
||||
let file = file_fetcher
|
||||
.fetch_bypass_permissions(&specifier)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
|
@ -1058,9 +1059,8 @@ impl ModuleRegistry {
|
|||
specifier, err
|
||||
);
|
||||
})
|
||||
.ok()?
|
||||
.into_text_decoded()
|
||||
.ok()
|
||||
.ok()?;
|
||||
TextDecodedFile::decode(file).ok()
|
||||
}
|
||||
})
|
||||
.await
|
||||
|
@ -1095,7 +1095,7 @@ impl ModuleRegistry {
|
|||
let file_fetcher = self.file_fetcher.clone();
|
||||
let specifier = specifier.clone();
|
||||
async move {
|
||||
file_fetcher
|
||||
let file = file_fetcher
|
||||
.fetch_bypass_permissions(&specifier)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
|
@ -1104,9 +1104,8 @@ impl ModuleRegistry {
|
|||
specifier, err
|
||||
);
|
||||
})
|
||||
.ok()?
|
||||
.into_text_decoded()
|
||||
.ok()
|
||||
.ok()?;
|
||||
TextDecodedFile::decode(file).ok()
|
||||
}
|
||||
})
|
||||
.await
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use dashmap::DashMap;
|
||||
use deno_ast::MediaType;
|
||||
use deno_cache_dir::file_fetcher::CacheSetting;
|
||||
use deno_cache_dir::npm::NpmCacheDir;
|
||||
use deno_cache_dir::HttpCache;
|
||||
use deno_config::deno_json::JsxImportSourceConfig;
|
||||
|
@ -20,14 +21,12 @@ 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::NodeResolutionKind;
|
||||
use node_resolver::ResolutionMode;
|
||||
|
@ -41,7 +40,6 @@ use std::sync::Arc;
|
|||
use super::cache::LspCache;
|
||||
use super::jsr::JsrCacheResolver;
|
||||
use crate::args::create_default_npmrc;
|
||||
use crate::args::CacheSetting;
|
||||
use crate::args::CliLockfile;
|
||||
use crate::args::NpmInstallDepsProvider;
|
||||
use crate::cache::DenoCacheEnvFsAdapter;
|
||||
|
@ -135,7 +133,8 @@ impl LspScopeResolver {
|
|||
cache.for_specifier(config_data.map(|d| d.scope.as_ref())),
|
||||
config_data.and_then(|d| d.lockfile.clone()),
|
||||
)));
|
||||
let npm_graph_resolver = cli_resolver.create_graph_npm_resolver();
|
||||
let npm_graph_resolver = cli_resolver
|
||||
.create_graph_npm_resolver(crate::graph_util::NpmCachingStrategy::Eager);
|
||||
let maybe_jsx_import_source_config =
|
||||
config_data.and_then(|d| d.maybe_jsx_import_source_config());
|
||||
let graph_imports = config_data
|
||||
|
@ -345,7 +344,9 @@ impl LspResolver {
|
|||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> WorkerCliNpmGraphResolver {
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
resolver.resolver.create_graph_npm_resolver()
|
||||
resolver
|
||||
.resolver
|
||||
.create_graph_npm_resolver(crate::graph_util::NpmCachingStrategy::Eager)
|
||||
}
|
||||
|
||||
pub fn as_is_cjs_resolver(
|
||||
|
@ -380,6 +381,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,
|
||||
|
@ -522,16 +531,6 @@ impl LspResolver {
|
|||
.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,
|
||||
|
@ -945,9 +944,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((
|
||||
|
|
|
@ -64,6 +64,7 @@ use deno_core::OpState;
|
|||
use deno_core::PollEventLoopOptions;
|
||||
use deno_core::RuntimeOptions;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_runtime::deno_node::SUPPORTED_BUILTIN_NODE_MODULES;
|
||||
use deno_runtime::inspector_server::InspectorServer;
|
||||
use deno_runtime::tokio_util::create_basic_runtime;
|
||||
use indexmap::IndexMap;
|
||||
|
@ -3411,15 +3412,23 @@ fn parse_code_actions(
|
|||
additional_text_edits.extend(change.text_changes.iter().map(|tc| {
|
||||
let mut text_edit = tc.as_text_edit(asset_or_doc.line_index());
|
||||
if let Some(specifier_rewrite) = &data.specifier_rewrite {
|
||||
text_edit.new_text = text_edit.new_text.replace(
|
||||
&specifier_rewrite.old_specifier,
|
||||
&specifier_rewrite.new_specifier,
|
||||
);
|
||||
let specifier_index = text_edit
|
||||
.new_text
|
||||
.char_indices()
|
||||
.find_map(|(b, c)| (c == '\'' || c == '"').then_some(b));
|
||||
if let Some(i) = specifier_index {
|
||||
let mut specifier_part = text_edit.new_text.split_off(i);
|
||||
specifier_part = specifier_part.replace(
|
||||
&specifier_rewrite.old_specifier,
|
||||
&specifier_rewrite.new_specifier,
|
||||
);
|
||||
text_edit.new_text.push_str(&specifier_part);
|
||||
}
|
||||
if let Some(deno_types_specifier) =
|
||||
&specifier_rewrite.new_deno_types_specifier
|
||||
{
|
||||
text_edit.new_text = format!(
|
||||
"// @deno-types=\"{}\"\n{}",
|
||||
"// @ts-types=\"{}\"\n{}",
|
||||
deno_types_specifier, &text_edit.new_text
|
||||
);
|
||||
}
|
||||
|
@ -3587,17 +3596,22 @@ impl CompletionEntryDetails {
|
|||
&mut insert_replace_edit.new_text
|
||||
}
|
||||
};
|
||||
*new_text = new_text.replace(
|
||||
&specifier_rewrite.old_specifier,
|
||||
&specifier_rewrite.new_specifier,
|
||||
);
|
||||
let specifier_index = new_text
|
||||
.char_indices()
|
||||
.find_map(|(b, c)| (c == '\'' || c == '"').then_some(b));
|
||||
if let Some(i) = specifier_index {
|
||||
let mut specifier_part = new_text.split_off(i);
|
||||
specifier_part = specifier_part.replace(
|
||||
&specifier_rewrite.old_specifier,
|
||||
&specifier_rewrite.new_specifier,
|
||||
);
|
||||
new_text.push_str(&specifier_part);
|
||||
}
|
||||
if let Some(deno_types_specifier) =
|
||||
&specifier_rewrite.new_deno_types_specifier
|
||||
{
|
||||
*new_text = format!(
|
||||
"// @deno-types=\"{}\"\n{}",
|
||||
deno_types_specifier, new_text
|
||||
);
|
||||
*new_text =
|
||||
format!("// @ts-types=\"{}\"\n{}", deno_types_specifier, new_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3731,7 +3745,7 @@ pub struct CompletionItemData {
|
|||
#[serde(rename_all = "camelCase")]
|
||||
struct CompletionEntryDataAutoImport {
|
||||
module_specifier: String,
|
||||
file_name: String,
|
||||
file_name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -3788,9 +3802,20 @@ impl CompletionEntry {
|
|||
else {
|
||||
return;
|
||||
};
|
||||
if let Ok(normalized) = specifier_map.normalize(&raw.file_name) {
|
||||
self.auto_import_data =
|
||||
Some(CompletionNormalizedAutoImportData { raw, normalized });
|
||||
if let Some(file_name) = &raw.file_name {
|
||||
if let Ok(normalized) = specifier_map.normalize(file_name) {
|
||||
self.auto_import_data =
|
||||
Some(CompletionNormalizedAutoImportData { raw, normalized });
|
||||
}
|
||||
} else if SUPPORTED_BUILTIN_NODE_MODULES
|
||||
.contains(&raw.module_specifier.as_str())
|
||||
{
|
||||
if let Ok(normalized) =
|
||||
resolve_url(&format!("node:{}", &raw.module_specifier))
|
||||
{
|
||||
self.auto_import_data =
|
||||
Some(CompletionNormalizedAutoImportData { raw, normalized });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4498,6 +4523,7 @@ impl<'a> ToV8<'a> for TscRequestArray {
|
|||
|
||||
let method_name = deno_core::FastString::from_static(method_name)
|
||||
.v8_string(scope)
|
||||
.unwrap()
|
||||
.into();
|
||||
let args = args.unwrap_or_else(|| v8::Array::new(scope, 0).into());
|
||||
let scope_url = serde_v8::to_v8(scope, self.scope)
|
||||
|
@ -5517,7 +5543,6 @@ impl TscRequest {
|
|||
mod tests {
|
||||
use super::*;
|
||||
use crate::cache::HttpCache;
|
||||
use crate::http_util::HeadersMap;
|
||||
use crate::lsp::cache::LspCache;
|
||||
use crate::lsp::config::Config;
|
||||
use crate::lsp::config::WorkspaceSettings;
|
||||
|
@ -5747,6 +5772,7 @@ mod tests {
|
|||
"sourceLine": " import { A } from \".\";",
|
||||
"category": 2,
|
||||
"code": 6133,
|
||||
"reportsUnnecessary": true,
|
||||
}]
|
||||
})
|
||||
);
|
||||
|
@ -5829,6 +5855,7 @@ mod tests {
|
|||
"sourceLine": " import {",
|
||||
"category": 2,
|
||||
"code": 6192,
|
||||
"reportsUnnecessary": true,
|
||||
}, {
|
||||
"start": {
|
||||
"line": 8,
|
||||
|
@ -5952,7 +5979,7 @@ mod tests {
|
|||
.global()
|
||||
.set(
|
||||
&specifier_dep,
|
||||
HeadersMap::default(),
|
||||
Default::default(),
|
||||
b"export const b = \"b\";\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -5991,7 +6018,7 @@ mod tests {
|
|||
.global()
|
||||
.set(
|
||||
&specifier_dep,
|
||||
HeadersMap::default(),
|
||||
Default::default(),
|
||||
b"export const b = \"b\";\n\nexport const a = \"b\";\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
|
11
cli/main.rs
11
cli/main.rs
|
@ -1,7 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
mod args;
|
||||
mod auth_tokens;
|
||||
mod cache;
|
||||
mod cdp;
|
||||
mod emit;
|
||||
|
@ -437,20 +436,18 @@ fn resolve_flags_and_init(
|
|||
if err.kind() == clap::error::ErrorKind::DisplayVersion =>
|
||||
{
|
||||
// Ignore results to avoid BrokenPipe errors.
|
||||
util::logger::init(None);
|
||||
util::logger::init(None, None);
|
||||
let _ = err.print();
|
||||
deno_runtime::exit(0);
|
||||
}
|
||||
Err(err) => {
|
||||
util::logger::init(None);
|
||||
util::logger::init(None, None);
|
||||
exit_for_error(AnyError::from(err))
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(otel_config) = flags.otel_config() {
|
||||
deno_telemetry::init(otel_config)?;
|
||||
}
|
||||
util::logger::init(flags.log_level);
|
||||
deno_telemetry::init(crate::args::otel_runtime_config())?;
|
||||
util::logger::init(flags.log_level, Some(flags.otel_config()));
|
||||
|
||||
// TODO(bartlomieju): remove in Deno v2.5 and hard error then.
|
||||
if flags.unstable_config.legacy_flag_enabled {
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
mod standalone;
|
||||
|
||||
mod args;
|
||||
mod auth_tokens;
|
||||
mod cache;
|
||||
mod emit;
|
||||
mod errors;
|
||||
|
@ -87,17 +86,18 @@ fn main() {
|
|||
let future = async move {
|
||||
match standalone {
|
||||
Ok(Some(data)) => {
|
||||
if let Some(otel_config) = data.metadata.otel_config.clone() {
|
||||
deno_telemetry::init(otel_config)?;
|
||||
}
|
||||
util::logger::init(data.metadata.log_level);
|
||||
deno_telemetry::init(crate::args::otel_runtime_config())?;
|
||||
util::logger::init(
|
||||
data.metadata.log_level,
|
||||
Some(data.metadata.otel_config.clone()),
|
||||
);
|
||||
load_env_vars(&data.metadata.env_vars_from_env_file);
|
||||
let exit_code = standalone::run(data).await?;
|
||||
deno_runtime::exit(exit_code);
|
||||
}
|
||||
Ok(None) => Ok(()),
|
||||
Err(err) => {
|
||||
util::logger::init(None);
|
||||
util::logger::init(None, None);
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -153,6 +156,7 @@ impl ModuleLoadPreparer {
|
|||
graph_kind: graph.graph_kind(),
|
||||
roots: roots.to_vec(),
|
||||
loader: Some(&mut cache),
|
||||
npm_caching: self.options.default_npm_caching_strategy(),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
@ -222,6 +226,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 {
|
||||
|
@ -272,6 +312,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)),
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -867,6 +912,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(())));
|
||||
}
|
||||
|
@ -921,6 +967,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,
|
||||
|
@ -1103,3 +1157,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
|
|||
use deno_npm::NpmPackageId;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_npm_cache::NpmCacheSetting;
|
||||
use deno_resolver::npm::CliNpmReqResolver;
|
||||
use deno_runtime::colors;
|
||||
use deno_runtime::deno_fs::FileSystem;
|
||||
|
@ -44,7 +45,6 @@ use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
|
|||
use crate::util::progress_bar::ProgressBar;
|
||||
use crate::util::sync::AtomicFlag;
|
||||
|
||||
use self::registry::CliNpmRegistryApi;
|
||||
use self::resolution::NpmResolution;
|
||||
use self::resolvers::create_npm_fs_resolver;
|
||||
use self::resolvers::NpmPackageFsResolver;
|
||||
|
@ -57,7 +57,6 @@ use super::CliNpmTarballCache;
|
|||
use super::InnerCliNpmResolverRef;
|
||||
use super::ResolvePkgFolderFromDenoReqError;
|
||||
|
||||
mod registry;
|
||||
mod resolution;
|
||||
mod resolvers;
|
||||
|
||||
|
@ -72,7 +71,7 @@ pub struct CliManagedNpmResolverCreateOptions {
|
|||
pub fs: Arc<dyn deno_runtime::deno_fs::FileSystem>,
|
||||
pub http_client_provider: Arc<crate::http_util::HttpClientProvider>,
|
||||
pub npm_cache_dir: Arc<NpmCacheDir>,
|
||||
pub cache_setting: crate::args::CacheSetting,
|
||||
pub cache_setting: deno_cache_dir::file_fetcher::CacheSetting,
|
||||
pub text_only_progress_bar: crate::util::progress_bar::ProgressBar,
|
||||
pub maybe_node_modules_path: Option<PathBuf>,
|
||||
pub npm_system_info: NpmSystemInfo,
|
||||
|
@ -120,13 +119,13 @@ pub async fn create_managed_npm_resolver(
|
|||
) -> Result<Arc<dyn CliNpmResolver>, AnyError> {
|
||||
let npm_cache_env = create_cache_env(&options);
|
||||
let npm_cache = create_cache(npm_cache_env.clone(), &options);
|
||||
let npm_api = create_api(npm_cache.clone(), npm_cache_env.clone(), &options);
|
||||
let snapshot = resolve_snapshot(&npm_api, options.snapshot).await?;
|
||||
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.maybe_lockfile,
|
||||
npm_api,
|
||||
api,
|
||||
npm_cache,
|
||||
options.npmrc,
|
||||
options.npm_install_deps_provider,
|
||||
|
@ -143,7 +142,7 @@ fn create_inner(
|
|||
env: Arc<CliNpmCacheEnv>,
|
||||
fs: Arc<dyn deno_runtime::deno_fs::FileSystem>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
npm_api: Arc<CliNpmRegistryApi>,
|
||||
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
npm_cache: Arc<CliNpmCache>,
|
||||
npm_rc: Arc<ResolvedNpmRc>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
|
@ -154,7 +153,7 @@ 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(),
|
||||
));
|
||||
|
@ -178,7 +177,7 @@ fn create_inner(
|
|||
fs,
|
||||
fs_resolver,
|
||||
maybe_lockfile,
|
||||
npm_api,
|
||||
registry_info_provider,
|
||||
npm_cache,
|
||||
npm_install_deps_provider,
|
||||
resolution,
|
||||
|
@ -205,7 +204,7 @@ fn create_cache(
|
|||
) -> Arc<CliNpmCache> {
|
||||
Arc::new(CliNpmCache::new(
|
||||
options.npm_cache_dir.clone(),
|
||||
options.cache_setting.as_npm_cache_setting(),
|
||||
NpmCacheSetting::from_cache_setting(&options.cache_setting),
|
||||
env,
|
||||
options.npmrc.clone(),
|
||||
))
|
||||
|
@ -215,29 +214,29 @@ fn create_api(
|
|||
cache: Arc<CliNpmCache>,
|
||||
env: Arc<CliNpmCacheEnv>,
|
||||
options: &CliManagedNpmResolverCreateOptions,
|
||||
) -> Arc<CliNpmRegistryApi> {
|
||||
Arc::new(CliNpmRegistryApi::new(
|
||||
cache.clone(),
|
||||
Arc::new(CliNpmRegistryInfoProvider::new(
|
||||
cache,
|
||||
env,
|
||||
options.npmrc.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)
|
||||
|
@ -298,13 +297,19 @@ pub fn create_managed_in_npm_pkg_checker(
|
|||
Arc::new(ManagedInNpmPackageChecker { root_dir })
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum PackageCaching<'a> {
|
||||
Only(Cow<'a, [PackageReq]>),
|
||||
All,
|
||||
}
|
||||
|
||||
/// An npm resolver where the resolution is managed by Deno rather than
|
||||
/// the user bringing their own node_modules (BYONM) on the file system.
|
||||
pub struct ManagedCliNpmResolver {
|
||||
fs: Arc<dyn FileSystem>,
|
||||
fs_resolver: Arc<dyn NpmPackageFsResolver>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
npm_api: Arc<CliNpmRegistryApi>,
|
||||
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
npm_cache: Arc<CliNpmCache>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
|
@ -329,7 +334,7 @@ impl ManagedCliNpmResolver {
|
|||
fs: Arc<dyn FileSystem>,
|
||||
fs_resolver: Arc<dyn NpmPackageFsResolver>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
npm_api: Arc<CliNpmRegistryApi>,
|
||||
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
npm_cache: Arc<CliNpmCache>,
|
||||
npm_install_deps_provider: Arc<NpmInstallDepsProvider>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
|
@ -342,7 +347,7 @@ impl ManagedCliNpmResolver {
|
|||
fs,
|
||||
fs_resolver,
|
||||
maybe_lockfile,
|
||||
npm_api,
|
||||
registry_info_provider,
|
||||
npm_cache,
|
||||
npm_install_deps_provider,
|
||||
text_only_progress_bar,
|
||||
|
@ -422,19 +427,44 @@ impl ManagedCliNpmResolver {
|
|||
|
||||
/// Adds package requirements to the resolver and ensures everything is setup.
|
||||
/// This includes setting up the `node_modules` directory, if applicable.
|
||||
pub async fn add_package_reqs(
|
||||
pub async fn add_and_cache_package_reqs(
|
||||
&self,
|
||||
packages: &[PackageReq],
|
||||
) -> Result<(), AnyError> {
|
||||
self
|
||||
.add_package_reqs_raw(packages)
|
||||
.add_package_reqs_raw(
|
||||
packages,
|
||||
Some(PackageCaching::Only(packages.into())),
|
||||
)
|
||||
.await
|
||||
.dependencies_result
|
||||
}
|
||||
|
||||
pub async fn add_package_reqs_raw(
|
||||
pub async fn add_package_reqs_no_cache(
|
||||
&self,
|
||||
packages: &[PackageReq],
|
||||
) -> Result<(), AnyError> {
|
||||
self
|
||||
.add_package_reqs_raw(packages, None)
|
||||
.await
|
||||
.dependencies_result
|
||||
}
|
||||
|
||||
pub async fn add_package_reqs(
|
||||
&self,
|
||||
packages: &[PackageReq],
|
||||
caching: PackageCaching<'_>,
|
||||
) -> Result<(), AnyError> {
|
||||
self
|
||||
.add_package_reqs_raw(packages, Some(caching))
|
||||
.await
|
||||
.dependencies_result
|
||||
}
|
||||
|
||||
pub async fn add_package_reqs_raw<'a>(
|
||||
&self,
|
||||
packages: &[PackageReq],
|
||||
caching: Option<PackageCaching<'a>>,
|
||||
) -> AddPkgReqsResult {
|
||||
if packages.is_empty() {
|
||||
return AddPkgReqsResult {
|
||||
|
@ -451,7 +481,9 @@ impl ManagedCliNpmResolver {
|
|||
}
|
||||
}
|
||||
if result.dependencies_result.is_ok() {
|
||||
result.dependencies_result = self.cache_packages().await;
|
||||
if let Some(caching) = caching {
|
||||
result.dependencies_result = self.cache_packages(caching).await;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
|
@ -493,16 +525,20 @@ impl ManagedCliNpmResolver {
|
|||
pub async fn inject_synthetic_types_node_package(
|
||||
&self,
|
||||
) -> Result<(), AnyError> {
|
||||
let reqs = &[PackageReq::from_str("@types/node").unwrap()];
|
||||
// add and ensure this isn't added to the lockfile
|
||||
self
|
||||
.add_package_reqs(&[PackageReq::from_str("@types/node").unwrap()])
|
||||
.add_package_reqs(reqs, PackageCaching::Only(reqs.into()))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn cache_packages(&self) -> Result<(), AnyError> {
|
||||
self.fs_resolver.cache_packages().await
|
||||
pub async fn cache_packages(
|
||||
&self,
|
||||
caching: PackageCaching<'_>,
|
||||
) -> Result<(), AnyError> {
|
||||
self.fs_resolver.cache_packages(caching).await
|
||||
}
|
||||
|
||||
pub fn resolve_pkg_folder_from_deno_module(
|
||||
|
@ -547,18 +583,18 @@ impl ManagedCliNpmResolver {
|
|||
/// Ensures that the top level `package.json` dependencies are installed.
|
||||
/// This may set up the `node_modules` directory.
|
||||
///
|
||||
/// Returns `true` if any changes (such as caching packages) were made.
|
||||
/// If this returns `false`, `node_modules` has _not_ been set up.
|
||||
/// Returns `true` if the top level packages are already installed. A
|
||||
/// return value of `false` means that new packages were added to the NPM resolution.
|
||||
pub async fn ensure_top_level_package_json_install(
|
||||
&self,
|
||||
) -> Result<bool, AnyError> {
|
||||
if !self.top_level_install_flag.raise() {
|
||||
return Ok(false); // already did this
|
||||
return Ok(true); // already did this
|
||||
}
|
||||
|
||||
let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs();
|
||||
if pkg_json_remote_pkgs.is_empty() {
|
||||
return Ok(false);
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// check if something needs resolving before bothering to load all
|
||||
|
@ -572,14 +608,16 @@ impl ManagedCliNpmResolver {
|
|||
log::debug!(
|
||||
"All package.json deps resolvable. Skipping top level install."
|
||||
);
|
||||
return Ok(false); // everything is already resolvable
|
||||
return Ok(true); // everything is already resolvable
|
||||
}
|
||||
|
||||
let pkg_reqs = pkg_json_remote_pkgs
|
||||
.iter()
|
||||
.map(|pkg| pkg.req.clone())
|
||||
.collect::<Vec<_>>();
|
||||
self.add_package_reqs(&pkg_reqs).await.map(|_| true)
|
||||
self.add_package_reqs_no_cache(&pkg_reqs).await?;
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
pub async fn cache_package_info(
|
||||
|
@ -588,7 +626,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())
|
||||
|
@ -684,7 +722,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(),
|
||||
));
|
||||
|
@ -703,7 +741,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,201 +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 deno_npm_cache::NpmCacheSetting;
|
||||
|
||||
use crate::npm::CliNpmCache;
|
||||
use crate::npm::CliNpmRegistryInfoProvider;
|
||||
use crate::util::sync::AtomicFlag;
|
||||
|
||||
// todo(#27198): Remove this and move functionality down into
|
||||
// RegistryInfoProvider, which already does most of this.
|
||||
#[derive(Debug)]
|
||||
pub struct CliNpmRegistryApi(Option<Arc<CliNpmRegistryApiInner>>);
|
||||
|
||||
impl CliNpmRegistryApi {
|
||||
pub fn new(
|
||||
cache: Arc<CliNpmCache>,
|
||||
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
) -> Self {
|
||||
Self(Some(Arc::new(CliNpmRegistryApiInner {
|
||||
cache,
|
||||
force_reload_flag: Default::default(),
|
||||
mem_cache: Default::default(),
|
||||
previously_reloaded_packages: Default::default(),
|
||||
registry_info_provider,
|
||||
})))
|
||||
}
|
||||
|
||||
/// 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<CliNpmCache>,
|
||||
force_reload_flag: AtomicFlag,
|
||||
mem_cache: Mutex<HashMap<String, CacheItem>>,
|
||||
previously_reloaded_packages: Mutex<HashSet<String>>,
|
||||
registry_info_provider: Arc<CliNpmRegistryInfoProvider>,
|
||||
}
|
||||
|
||||
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_provider
|
||||
.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(),
|
||||
NpmCacheSetting::Only | NpmCacheSetting::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();
|
||||
}
|
||||
}
|
|
@ -27,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.
|
||||
///
|
||||
|
@ -47,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>>,
|
||||
}
|
||||
|
@ -63,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,
|
||||
}
|
||||
|
@ -91,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(),
|
||||
|
@ -119,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(),
|
||||
|| {
|
||||
|
@ -256,10 +255,14 @@ impl NpmResolution {
|
|||
.read()
|
||||
.as_valid_serialized_for_system(system_info)
|
||||
}
|
||||
|
||||
pub fn subset(&self, package_reqs: &[PackageReq]) -> NpmResolutionSnapshot {
|
||||
self.snapshot.read().subset(package_reqs)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -282,26 +285,28 @@ async fn add_package_reqs_to_snapshot(
|
|||
/* this string is used in tests */
|
||||
"Running npm resolution."
|
||||
);
|
||||
let npm_registry_api = registry_info_provider.as_npm_registry_api();
|
||||
let result = snapshot
|
||||
.add_pkg_reqs(api, get_add_pkg_reqs_options(package_reqs))
|
||||
.add_pkg_reqs(&npm_registry_api, get_add_pkg_reqs_options(package_reqs))
|
||||
.await;
|
||||
api.clear_memory_cache();
|
||||
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 = snapshot
|
||||
.add_pkg_reqs(api, get_add_pkg_reqs_options(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);
|
||||
|
|
|
@ -11,6 +11,7 @@ use std::path::PathBuf;
|
|||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use super::super::PackageCaching;
|
||||
use async_trait::async_trait;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::anyhow::Context;
|
||||
|
@ -57,7 +58,10 @@ pub trait NpmPackageFsResolver: Send + Sync {
|
|||
specifier: &ModuleSpecifier,
|
||||
) -> Result<Option<NpmPackageCacheFolderId>, AnyError>;
|
||||
|
||||
async fn cache_packages(&self) -> Result<(), AnyError>;
|
||||
async fn cache_packages<'a>(
|
||||
&self,
|
||||
caching: PackageCaching<'a>,
|
||||
) -> Result<(), AnyError>;
|
||||
|
||||
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
|
||||
fn ensure_read_permission<'a>(
|
||||
|
|
|
@ -8,6 +8,7 @@ use std::path::PathBuf;
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::colors;
|
||||
use crate::npm::managed::PackageCaching;
|
||||
use crate::npm::CliNpmCache;
|
||||
use crate::npm::CliNpmTarballCache;
|
||||
use async_trait::async_trait;
|
||||
|
@ -150,10 +151,19 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver {
|
|||
)
|
||||
}
|
||||
|
||||
async fn cache_packages(&self) -> Result<(), AnyError> {
|
||||
let package_partitions = self
|
||||
.resolution
|
||||
.all_system_packages_partitioned(&self.system_info);
|
||||
async fn cache_packages<'a>(
|
||||
&self,
|
||||
caching: PackageCaching<'a>,
|
||||
) -> Result<(), AnyError> {
|
||||
let package_partitions = match caching {
|
||||
PackageCaching::All => self
|
||||
.resolution
|
||||
.all_system_packages_partitioned(&self.system_info),
|
||||
PackageCaching::Only(reqs) => self
|
||||
.resolution
|
||||
.subset(&reqs)
|
||||
.all_system_packages_partitioned(&self.system_info),
|
||||
};
|
||||
cache_packages(&package_partitions.packages, &self.tarball_cache).await?;
|
||||
|
||||
// create the copy package folders
|
||||
|
|
|
@ -17,6 +17,7 @@ use std::sync::Arc;
|
|||
|
||||
use crate::args::LifecycleScriptsConfig;
|
||||
use crate::colors;
|
||||
use crate::npm::managed::PackageCaching;
|
||||
use crate::npm::CliNpmCache;
|
||||
use crate::npm::CliNpmTarballCache;
|
||||
use async_trait::async_trait;
|
||||
|
@ -236,13 +237,33 @@ 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> {
|
||||
async fn cache_packages<'a>(
|
||||
&self,
|
||||
caching: PackageCaching<'a>,
|
||||
) -> Result<(), AnyError> {
|
||||
let snapshot = match caching {
|
||||
PackageCaching::All => self.resolution.snapshot(),
|
||||
PackageCaching::Only(reqs) => self.resolution.subset(&reqs),
|
||||
};
|
||||
sync_resolution_with_fs(
|
||||
&self.resolution.snapshot(),
|
||||
&snapshot,
|
||||
&self.cache,
|
||||
&self.npm_install_deps_provider,
|
||||
&self.progress_bar,
|
||||
|
|
|
@ -28,7 +28,7 @@ use managed::create_managed_in_npm_pkg_checker;
|
|||
use node_resolver::InNpmPackageChecker;
|
||||
use node_resolver::NpmPackageFolderResolver;
|
||||
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
use crate::util::fs::atomic_write_file_with_retries_and_fs;
|
||||
use crate::util::fs::hard_link_dir_recursive;
|
||||
|
@ -41,6 +41,7 @@ pub use self::managed::CliManagedInNpmPkgCheckerCreateOptions;
|
|||
pub use self::managed::CliManagedNpmResolverCreateOptions;
|
||||
pub use self::managed::CliNpmResolverManagedSnapshotOption;
|
||||
pub use self::managed::ManagedCliNpmResolver;
|
||||
pub use self::managed::PackageCaching;
|
||||
|
||||
pub type CliNpmTarballCache = deno_npm_cache::TarballCache<CliNpmCacheEnv>;
|
||||
pub type CliNpmCache = deno_npm_cache::NpmCache<CliNpmCacheEnv>;
|
||||
|
@ -114,14 +115,14 @@ impl deno_npm_cache::NpmCacheEnv for CliNpmCacheEnv {
|
|||
.download_with_progress_and_retries(url, maybe_auth_header, &guard)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
use crate::http_util::DownloadError::*;
|
||||
let status_code = match &err {
|
||||
use crate::http_util::DownloadErrorKind::*;
|
||||
let status_code = match err.as_kind() {
|
||||
Fetch { .. }
|
||||
| UrlParse { .. }
|
||||
| HttpParse { .. }
|
||||
| Json { .. }
|
||||
| ToStr { .. }
|
||||
| NoRedirectHeader { .. }
|
||||
| RedirectHeaderParse { .. }
|
||||
| TooManyRedirects => None,
|
||||
BadResponse(bad_response_error) => {
|
||||
Some(bad_response_error.status_code)
|
||||
|
@ -231,13 +232,13 @@ pub trait CliNpmResolver: NpmPackageFolderResolver + CliNpmReqResolver {
|
|||
pub struct NpmFetchResolver {
|
||||
nv_by_req: DashMap<PackageReq, Option<PackageNv>>,
|
||||
info_by_name: DashMap<String, Option<Arc<NpmPackageInfo>>>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
file_fetcher: Arc<CliFileFetcher>,
|
||||
npmrc: Arc<ResolvedNpmRc>,
|
||||
}
|
||||
|
||||
impl NpmFetchResolver {
|
||||
pub fn new(
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
file_fetcher: Arc<CliFileFetcher>,
|
||||
npmrc: Arc<ResolvedNpmRc>,
|
||||
) -> Self {
|
||||
Self {
|
||||
|
|
|
@ -32,6 +32,7 @@ use std::path::PathBuf;
|
|||
use std::sync::Arc;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::args::NpmCachingStrategy;
|
||||
use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
|
||||
use crate::node::CliNodeCodeTranslator;
|
||||
use crate::npm::CliNpmResolver;
|
||||
|
@ -240,11 +241,15 @@ impl CliResolver {
|
|||
|
||||
// todo(dsherret): move this off CliResolver as CliResolver is acting
|
||||
// like a factory by doing this (it's beyond its responsibility)
|
||||
pub fn create_graph_npm_resolver(&self) -> WorkerCliNpmGraphResolver {
|
||||
pub fn create_graph_npm_resolver(
|
||||
&self,
|
||||
npm_caching: NpmCachingStrategy,
|
||||
) -> WorkerCliNpmGraphResolver {
|
||||
WorkerCliNpmGraphResolver {
|
||||
npm_resolver: self.npm_resolver.as_ref(),
|
||||
found_package_json_dep_flag: &self.found_package_json_dep_flag,
|
||||
bare_node_builtins_enabled: self.bare_node_builtins_enabled,
|
||||
npm_caching,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,6 +309,7 @@ pub struct WorkerCliNpmGraphResolver<'a> {
|
|||
npm_resolver: Option<&'a Arc<dyn CliNpmResolver>>,
|
||||
found_package_json_dep_flag: &'a AtomicFlag,
|
||||
bare_node_builtins_enabled: bool,
|
||||
npm_caching: NpmCachingStrategy,
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
|
@ -373,7 +379,20 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> {
|
|||
Ok(())
|
||||
};
|
||||
|
||||
let result = npm_resolver.add_package_reqs_raw(package_reqs).await;
|
||||
let result = npm_resolver
|
||||
.add_package_reqs_raw(
|
||||
package_reqs,
|
||||
match self.npm_caching {
|
||||
NpmCachingStrategy::Eager => {
|
||||
Some(crate::npm::PackageCaching::All)
|
||||
}
|
||||
NpmCachingStrategy::Lazy => {
|
||||
Some(crate::npm::PackageCaching::Only(package_reqs.into()))
|
||||
}
|
||||
NpmCachingStrategy::Manual => None,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
NpmResolvePkgReqsResult {
|
||||
results: result
|
||||
|
|
|
@ -291,7 +291,7 @@
|
|||
"type": "array",
|
||||
"description": "List of tag names that will be run. Empty list disables all tags and will only use rules from `include`.",
|
||||
"items": {
|
||||
"$ref": "https://raw.githubusercontent.com/denoland/deno_lint/main/schemas/tags.v1.json"
|
||||
"$ref": "lint-tags.v1.json"
|
||||
},
|
||||
"minItems": 0,
|
||||
"uniqueItems": true
|
||||
|
@ -300,7 +300,7 @@
|
|||
"type": "array",
|
||||
"description": "List of rule names that will be excluded from configured tag sets. If the same rule is in `include` it will be run.",
|
||||
"items": {
|
||||
"$ref": "https://raw.githubusercontent.com/denoland/deno_lint/main/schemas/rules.v1.json"
|
||||
"$ref": "lint-rules.v1.json"
|
||||
},
|
||||
"minItems": 0,
|
||||
"uniqueItems": true
|
||||
|
@ -309,7 +309,7 @@
|
|||
"type": "array",
|
||||
"description": "List of rule names that will be run. Even if the same rule is in `exclude` it will be run.",
|
||||
"items": {
|
||||
"$ref": "https://raw.githubusercontent.com/denoland/deno_lint/main/schemas/rules.v1.json"
|
||||
"$ref": "lint-rules.v1.json"
|
||||
},
|
||||
"minItems": 0,
|
||||
"uniqueItems": true
|
||||
|
|
112
cli/schemas/lint-rules.v1.json
Normal file
112
cli/schemas/lint-rules.v1.json
Normal file
|
@ -0,0 +1,112 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"enum": [
|
||||
"adjacent-overload-signatures",
|
||||
"ban-ts-comment",
|
||||
"ban-types",
|
||||
"ban-unknown-rule-code",
|
||||
"ban-untagged-ignore",
|
||||
"ban-untagged-todo",
|
||||
"ban-unused-ignore",
|
||||
"camelcase",
|
||||
"constructor-super",
|
||||
"default-param-last",
|
||||
"eqeqeq",
|
||||
"explicit-function-return-type",
|
||||
"explicit-module-boundary-types",
|
||||
"for-direction",
|
||||
"fresh-handler-export",
|
||||
"fresh-server-event-handlers",
|
||||
"getter-return",
|
||||
"guard-for-in",
|
||||
"no-array-constructor",
|
||||
"no-async-promise-executor",
|
||||
"no-await-in-loop",
|
||||
"no-await-in-sync-fn",
|
||||
"no-boolean-literal-for-arguments",
|
||||
"no-case-declarations",
|
||||
"no-class-assign",
|
||||
"no-compare-neg-zero",
|
||||
"no-cond-assign",
|
||||
"no-console",
|
||||
"no-const-assign",
|
||||
"no-constant-condition",
|
||||
"no-control-regex",
|
||||
"no-debugger",
|
||||
"no-delete-var",
|
||||
"no-deprecated-deno-api",
|
||||
"no-dupe-args",
|
||||
"no-dupe-class-members",
|
||||
"no-dupe-else-if",
|
||||
"no-dupe-keys",
|
||||
"no-duplicate-case",
|
||||
"no-empty",
|
||||
"no-empty-character-class",
|
||||
"no-empty-enum",
|
||||
"no-empty-interface",
|
||||
"no-empty-pattern",
|
||||
"no-eval",
|
||||
"no-ex-assign",
|
||||
"no-explicit-any",
|
||||
"no-external-import",
|
||||
"no-extra-boolean-cast",
|
||||
"no-extra-non-null-assertion",
|
||||
"no-fallthrough",
|
||||
"no-func-assign",
|
||||
"no-global-assign",
|
||||
"no-implicit-declare-namespace-export",
|
||||
"no-import-assertions",
|
||||
"no-import-assign",
|
||||
"no-inferrable-types",
|
||||
"no-inner-declarations",
|
||||
"no-invalid-regexp",
|
||||
"no-invalid-triple-slash-reference",
|
||||
"no-irregular-whitespace",
|
||||
"no-misused-new",
|
||||
"no-namespace",
|
||||
"no-new-symbol",
|
||||
"no-node-globals",
|
||||
"no-non-null-asserted-optional-chain",
|
||||
"no-non-null-assertion",
|
||||
"no-obj-calls",
|
||||
"no-octal",
|
||||
"no-process-globals",
|
||||
"no-prototype-builtins",
|
||||
"no-redeclare",
|
||||
"no-regex-spaces",
|
||||
"no-self-assign",
|
||||
"no-self-compare",
|
||||
"no-setter-return",
|
||||
"no-shadow-restricted-names",
|
||||
"no-sloppy-imports",
|
||||
"no-slow-types",
|
||||
"no-sparse-arrays",
|
||||
"no-sync-fn-in-async-fn",
|
||||
"no-this-alias",
|
||||
"no-this-before-super",
|
||||
"no-throw-literal",
|
||||
"no-top-level-await",
|
||||
"no-undef",
|
||||
"no-unreachable",
|
||||
"no-unsafe-finally",
|
||||
"no-unsafe-negation",
|
||||
"no-unused-labels",
|
||||
"no-unused-vars",
|
||||
"no-var",
|
||||
"no-window",
|
||||
"no-window-prefix",
|
||||
"no-with",
|
||||
"prefer-as-const",
|
||||
"prefer-ascii",
|
||||
"prefer-const",
|
||||
"prefer-namespace-keyword",
|
||||
"prefer-primordials",
|
||||
"require-await",
|
||||
"require-yield",
|
||||
"single-var-declarator",
|
||||
"triple-slash-reference",
|
||||
"use-isnan",
|
||||
"valid-typeof",
|
||||
"verbatim-module-syntax"
|
||||
]
|
||||
}
|
4
cli/schemas/lint-tags.v1.json
Normal file
4
cli/schemas/lint-tags.v1.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"enum": ["fresh", "jsr", "jsx", "react", "recommended"]
|
||||
}
|
|
@ -44,6 +44,9 @@ use deno_npm::resolution::SerializedNpmResolutionSnapshotPackage;
|
|||
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
|
||||
use deno_npm::NpmPackageId;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_path_util::url_from_directory_path;
|
||||
use deno_path_util::url_from_file_path;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_fs::FileSystem;
|
||||
use deno_runtime::deno_fs::RealFs;
|
||||
|
@ -68,7 +71,7 @@ use crate::args::UnstableConfig;
|
|||
use crate::cache::DenoDir;
|
||||
use crate::cache::FastInsecureHasher;
|
||||
use crate::emit::Emitter;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::InnerCliNpmResolverRef;
|
||||
|
@ -76,6 +79,7 @@ use crate::resolver::CjsTracker;
|
|||
use crate::shared::ReleaseChannel;
|
||||
use crate::standalone::virtual_fs::VfsEntry;
|
||||
use crate::util::archive;
|
||||
use crate::util::fs::canonicalize_path;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
use crate::util::progress_bar::ProgressBarStyle;
|
||||
|
@ -87,31 +91,29 @@ use super::serialization::DenoCompileModuleData;
|
|||
use super::serialization::DeserializedDataSection;
|
||||
use super::serialization::RemoteModulesStore;
|
||||
use super::serialization::RemoteModulesStoreBuilder;
|
||||
use super::virtual_fs::output_vfs;
|
||||
use super::virtual_fs::BuiltVfs;
|
||||
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;
|
||||
use super::virtual_fs::WindowsSystemRootablePath;
|
||||
|
||||
pub static DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME: &str =
|
||||
".deno_compile_node_modules";
|
||||
|
||||
/// A URL that can be designated as the base for relative URLs.
|
||||
///
|
||||
/// After creation, this URL may be used to get the key for a
|
||||
/// module in the binary.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct StandaloneRelativeFileBaseUrl<'a>(&'a Url);
|
||||
|
||||
impl<'a> From<&'a Url> for StandaloneRelativeFileBaseUrl<'a> {
|
||||
fn from(url: &'a Url) -> Self {
|
||||
Self(url)
|
||||
}
|
||||
pub enum StandaloneRelativeFileBaseUrl<'a> {
|
||||
WindowsSystemRoot,
|
||||
Path(&'a Url),
|
||||
}
|
||||
|
||||
impl<'a> StandaloneRelativeFileBaseUrl<'a> {
|
||||
pub fn new(url: &'a Url) -> Self {
|
||||
debug_assert_eq!(url.scheme(), "file");
|
||||
Self(url)
|
||||
}
|
||||
|
||||
/// Gets the module map key of the provided specifier.
|
||||
///
|
||||
/// * Descendant file specifiers will be made relative to the base.
|
||||
|
@ -121,22 +123,29 @@ impl<'a> StandaloneRelativeFileBaseUrl<'a> {
|
|||
if target.scheme() != "file" {
|
||||
return Cow::Borrowed(target.as_str());
|
||||
}
|
||||
let base = match self {
|
||||
Self::Path(base) => base,
|
||||
Self::WindowsSystemRoot => return Cow::Borrowed(target.path()),
|
||||
};
|
||||
|
||||
match self.0.make_relative(target) {
|
||||
match base.make_relative(target) {
|
||||
Some(relative) => {
|
||||
if relative.starts_with("../") {
|
||||
Cow::Borrowed(target.as_str())
|
||||
} else {
|
||||
Cow::Owned(relative)
|
||||
}
|
||||
// This is not a great scenario to have because it means that the
|
||||
// specifier is outside the vfs and could cause the binary to act
|
||||
// strangely. If you encounter this, the fix is to add more paths
|
||||
// to the vfs builder by calling `add_possible_min_root_dir`.
|
||||
debug_assert!(
|
||||
!relative.starts_with("../"),
|
||||
"{} -> {} ({})",
|
||||
base.as_str(),
|
||||
target.as_str(),
|
||||
relative,
|
||||
);
|
||||
Cow::Owned(relative)
|
||||
}
|
||||
None => Cow::Borrowed(target.as_str()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &Url {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
|
@ -191,7 +200,7 @@ pub struct Metadata {
|
|||
pub entrypoint_key: String,
|
||||
pub node_modules: Option<NodeModules>,
|
||||
pub unstable_config: UnstableConfig,
|
||||
pub otel_config: Option<OtelConfig>, // None means disabled.
|
||||
pub otel_config: OtelConfig,
|
||||
}
|
||||
|
||||
fn write_binary_bytes(
|
||||
|
@ -200,7 +209,7 @@ fn write_binary_bytes(
|
|||
metadata: &Metadata,
|
||||
npm_snapshot: Option<SerializedNpmResolutionSnapshot>,
|
||||
remote_modules: &RemoteModulesStoreBuilder,
|
||||
vfs: VfsBuilder,
|
||||
vfs: &BuiltVfs,
|
||||
compile_flags: &CompileFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let data_section_bytes =
|
||||
|
@ -367,12 +376,21 @@ pub fn extract_standalone(
|
|||
}))
|
||||
}
|
||||
|
||||
pub struct WriteBinOptions<'a> {
|
||||
pub writer: File,
|
||||
pub display_output_filename: &'a str,
|
||||
pub graph: &'a ModuleGraph,
|
||||
pub entrypoint: &'a ModuleSpecifier,
|
||||
pub include_files: &'a [ModuleSpecifier],
|
||||
pub compile_flags: &'a CompileFlags,
|
||||
}
|
||||
|
||||
pub struct DenoCompileBinaryWriter<'a> {
|
||||
cjs_tracker: &'a CjsTracker,
|
||||
cli_options: &'a CliOptions,
|
||||
deno_dir: &'a DenoDir,
|
||||
emitter: &'a Emitter,
|
||||
file_fetcher: &'a FileFetcher,
|
||||
file_fetcher: &'a CliFileFetcher,
|
||||
http_client_provider: &'a HttpClientProvider,
|
||||
npm_resolver: &'a dyn CliNpmResolver,
|
||||
workspace_resolver: &'a WorkspaceResolver,
|
||||
|
@ -386,7 +404,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
cli_options: &'a CliOptions,
|
||||
deno_dir: &'a DenoDir,
|
||||
emitter: &'a Emitter,
|
||||
file_fetcher: &'a FileFetcher,
|
||||
file_fetcher: &'a CliFileFetcher,
|
||||
http_client_provider: &'a HttpClientProvider,
|
||||
npm_resolver: &'a dyn CliNpmResolver,
|
||||
workspace_resolver: &'a WorkspaceResolver,
|
||||
|
@ -407,18 +425,14 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
|
||||
pub async fn write_bin(
|
||||
&self,
|
||||
writer: File,
|
||||
graph: &ModuleGraph,
|
||||
root_dir_url: StandaloneRelativeFileBaseUrl<'_>,
|
||||
entrypoint: &ModuleSpecifier,
|
||||
include_files: &[ModuleSpecifier],
|
||||
compile_flags: &CompileFlags,
|
||||
options: WriteBinOptions<'_>,
|
||||
) -> Result<(), AnyError> {
|
||||
// Select base binary based on target
|
||||
let mut original_binary = self.get_base_binary(compile_flags).await?;
|
||||
let mut original_binary =
|
||||
self.get_base_binary(options.compile_flags).await?;
|
||||
|
||||
if compile_flags.no_terminal {
|
||||
let target = compile_flags.resolve_target();
|
||||
if options.compile_flags.no_terminal {
|
||||
let target = options.compile_flags.resolve_target();
|
||||
if !target.contains("windows") {
|
||||
bail!(
|
||||
"The `--no-terminal` flag is only available when targeting Windows (current: {})",
|
||||
|
@ -428,8 +442,8 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
set_windows_binary_to_gui(&mut original_binary)
|
||||
.context("Setting windows binary to GUI.")?;
|
||||
}
|
||||
if compile_flags.icon.is_some() {
|
||||
let target = compile_flags.resolve_target();
|
||||
if options.compile_flags.icon.is_some() {
|
||||
let target = options.compile_flags.resolve_target();
|
||||
if !target.contains("windows") {
|
||||
bail!(
|
||||
"The `--icon` flag is only available when targeting Windows (current: {})",
|
||||
|
@ -437,17 +451,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
)
|
||||
}
|
||||
}
|
||||
self
|
||||
.write_standalone_binary(
|
||||
writer,
|
||||
original_binary,
|
||||
graph,
|
||||
root_dir_url,
|
||||
entrypoint,
|
||||
include_files,
|
||||
compile_flags,
|
||||
)
|
||||
.await
|
||||
self.write_standalone_binary(options, original_binary).await
|
||||
}
|
||||
|
||||
async fn get_base_binary(
|
||||
|
@ -552,14 +556,17 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
async fn write_standalone_binary(
|
||||
&self,
|
||||
writer: File,
|
||||
options: WriteBinOptions<'_>,
|
||||
original_bin: Vec<u8>,
|
||||
graph: &ModuleGraph,
|
||||
root_dir_url: StandaloneRelativeFileBaseUrl<'_>,
|
||||
entrypoint: &ModuleSpecifier,
|
||||
include_files: &[ModuleSpecifier],
|
||||
compile_flags: &CompileFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let WriteBinOptions {
|
||||
writer,
|
||||
display_output_filename,
|
||||
graph,
|
||||
entrypoint,
|
||||
include_files,
|
||||
compile_flags,
|
||||
} = options;
|
||||
let ca_data = match self.cli_options.ca_data() {
|
||||
Some(CaData::File(ca_file)) => Some(
|
||||
std::fs::read(ca_file).with_context(|| format!("Reading {ca_file}"))?,
|
||||
|
@ -567,74 +574,28 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
Some(CaData::Bytes(bytes)) => Some(bytes.clone()),
|
||||
None => None,
|
||||
};
|
||||
let root_path = root_dir_url.inner().to_file_path().unwrap();
|
||||
let (maybe_npm_vfs, node_modules, npm_snapshot) =
|
||||
match self.npm_resolver.as_inner() {
|
||||
InnerCliNpmResolverRef::Managed(managed) => {
|
||||
let snapshot =
|
||||
managed.serialized_valid_snapshot_for_system(&self.npm_system_info);
|
||||
if !snapshot.as_serialized().packages.is_empty() {
|
||||
let npm_vfs_builder = self
|
||||
.build_npm_vfs(&root_path)
|
||||
.context("Building npm vfs.")?;
|
||||
(
|
||||
Some(npm_vfs_builder),
|
||||
Some(NodeModules::Managed {
|
||||
node_modules_dir: self
|
||||
.npm_resolver
|
||||
.root_node_modules_path()
|
||||
.map(|path| {
|
||||
root_dir_url
|
||||
.specifier_key(
|
||||
&ModuleSpecifier::from_directory_path(path).unwrap(),
|
||||
)
|
||||
.into_owned()
|
||||
}),
|
||||
}),
|
||||
Some(snapshot),
|
||||
)
|
||||
} else {
|
||||
(None, None, None)
|
||||
}
|
||||
let mut vfs = VfsBuilder::new();
|
||||
let npm_snapshot = match self.npm_resolver.as_inner() {
|
||||
InnerCliNpmResolverRef::Managed(managed) => {
|
||||
let snapshot =
|
||||
managed.serialized_valid_snapshot_for_system(&self.npm_system_info);
|
||||
if !snapshot.as_serialized().packages.is_empty() {
|
||||
self.fill_npm_vfs(&mut vfs).context("Building npm vfs.")?;
|
||||
Some(snapshot)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
InnerCliNpmResolverRef::Byonm(resolver) => {
|
||||
let npm_vfs_builder = self.build_npm_vfs(&root_path)?;
|
||||
(
|
||||
Some(npm_vfs_builder),
|
||||
Some(NodeModules::Byonm {
|
||||
root_node_modules_dir: resolver.root_node_modules_path().map(
|
||||
|node_modules_dir| {
|
||||
root_dir_url
|
||||
.specifier_key(
|
||||
&ModuleSpecifier::from_directory_path(node_modules_dir)
|
||||
.unwrap(),
|
||||
)
|
||||
.into_owned()
|
||||
},
|
||||
),
|
||||
}),
|
||||
None,
|
||||
)
|
||||
}
|
||||
};
|
||||
let mut vfs = if let Some(npm_vfs) = maybe_npm_vfs {
|
||||
npm_vfs
|
||||
} else {
|
||||
VfsBuilder::new(root_path.clone())?
|
||||
}
|
||||
InnerCliNpmResolverRef::Byonm(_) => {
|
||||
self.fill_npm_vfs(&mut vfs)?;
|
||||
None
|
||||
}
|
||||
};
|
||||
for include_file in include_files {
|
||||
let path = deno_path_util::url_to_file_path(include_file)?;
|
||||
if path.is_dir() {
|
||||
// TODO(#26941): we should analyze if any of these are
|
||||
// modules in order to include their dependencies
|
||||
vfs
|
||||
.add_dir_recursive(&path)
|
||||
.with_context(|| format!("Including {}", path.display()))?;
|
||||
} else {
|
||||
vfs
|
||||
.add_file_at_path(&path)
|
||||
.with_context(|| format!("Including {}", path.display()))?;
|
||||
}
|
||||
vfs
|
||||
.add_file_at_path(&path)
|
||||
.with_context(|| format!("Including {}", path.display()))?;
|
||||
}
|
||||
let mut remote_modules_store = RemoteModulesStoreBuilder::default();
|
||||
let mut code_cache_key_hasher = if self.cli_options.code_cache_enabled() {
|
||||
|
@ -706,6 +667,62 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
}
|
||||
remote_modules_store.add_redirects(&graph.redirects);
|
||||
|
||||
if let Some(import_map) = self.workspace_resolver.maybe_import_map() {
|
||||
if let Ok(file_path) = url_to_file_path(import_map.base_url()) {
|
||||
if let Some(import_map_parent_dir) = file_path.parent() {
|
||||
// tell the vfs about the import map's parent directory in case it
|
||||
// falls outside what the root of where the VFS will be based
|
||||
vfs.add_possible_min_root_dir(import_map_parent_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(node_modules_dir) = self.npm_resolver.root_node_modules_path() {
|
||||
// ensure the vfs doesn't go below the node_modules directory's parent
|
||||
if let Some(parent) = node_modules_dir.parent() {
|
||||
vfs.add_possible_min_root_dir(parent);
|
||||
}
|
||||
}
|
||||
|
||||
let vfs = self.build_vfs_consolidating_global_npm_cache(vfs);
|
||||
let root_dir_url = match &vfs.root_path {
|
||||
WindowsSystemRootablePath::Path(dir) => {
|
||||
Some(url_from_directory_path(dir)?)
|
||||
}
|
||||
WindowsSystemRootablePath::WindowSystemRoot => None,
|
||||
};
|
||||
let root_dir_url = match &root_dir_url {
|
||||
Some(url) => StandaloneRelativeFileBaseUrl::Path(url),
|
||||
None => StandaloneRelativeFileBaseUrl::WindowsSystemRoot,
|
||||
};
|
||||
|
||||
let node_modules = match self.npm_resolver.as_inner() {
|
||||
InnerCliNpmResolverRef::Managed(_) => {
|
||||
npm_snapshot.as_ref().map(|_| NodeModules::Managed {
|
||||
node_modules_dir: self.npm_resolver.root_node_modules_path().map(
|
||||
|path| {
|
||||
root_dir_url
|
||||
.specifier_key(
|
||||
&ModuleSpecifier::from_directory_path(path).unwrap(),
|
||||
)
|
||||
.into_owned()
|
||||
},
|
||||
),
|
||||
})
|
||||
}
|
||||
InnerCliNpmResolverRef::Byonm(resolver) => Some(NodeModules::Byonm {
|
||||
root_node_modules_dir: resolver.root_node_modules_path().map(
|
||||
|node_modules_dir| {
|
||||
root_dir_url
|
||||
.specifier_key(
|
||||
&ModuleSpecifier::from_directory_path(node_modules_dir)
|
||||
.unwrap(),
|
||||
)
|
||||
.into_owned()
|
||||
},
|
||||
),
|
||||
}),
|
||||
};
|
||||
|
||||
let env_vars_from_env_file = match self.cli_options.env_file_name() {
|
||||
Some(env_filenames) => {
|
||||
let mut aggregated_env_vars = IndexMap::new();
|
||||
|
@ -720,6 +737,8 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
None => Default::default(),
|
||||
};
|
||||
|
||||
output_vfs(&vfs, display_output_filename);
|
||||
|
||||
let metadata = Metadata {
|
||||
argv: compile_flags.args.clone(),
|
||||
seed: self.cli_options.seed(),
|
||||
|
@ -779,6 +798,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
detect_cjs: self.cli_options.unstable_detect_cjs(),
|
||||
sloppy_imports: self.cli_options.unstable_sloppy_imports(),
|
||||
features: self.cli_options.unstable_features(),
|
||||
npm_lazy_caching: self.cli_options.unstable_npm_lazy_caching(),
|
||||
},
|
||||
otel_config: self.cli_options.otel_config(),
|
||||
};
|
||||
|
@ -789,13 +809,13 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
&metadata,
|
||||
npm_snapshot.map(|s| s.into_serialized()),
|
||||
&remote_modules_store,
|
||||
vfs,
|
||||
&vfs,
|
||||
compile_flags,
|
||||
)
|
||||
.context("Writing binary bytes")
|
||||
}
|
||||
|
||||
fn build_npm_vfs(&self, root_path: &Path) -> Result<VfsBuilder, AnyError> {
|
||||
fn fill_npm_vfs(&self, builder: &mut VfsBuilder) -> Result<(), AnyError> {
|
||||
fn maybe_warn_different_system(system_info: &NpmSystemInfo) {
|
||||
if system_info != &NpmSystemInfo::default() {
|
||||
log::warn!("{} The node_modules directory may be incompatible with the target system.", crate::colors::yellow("Warning"));
|
||||
|
@ -806,15 +826,10 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
InnerCliNpmResolverRef::Managed(npm_resolver) => {
|
||||
if let Some(node_modules_path) = npm_resolver.root_node_modules_path() {
|
||||
maybe_warn_different_system(&self.npm_system_info);
|
||||
let mut builder = VfsBuilder::new(root_path.to_path_buf())?;
|
||||
builder.add_dir_recursive(node_modules_path)?;
|
||||
Ok(builder)
|
||||
Ok(())
|
||||
} else {
|
||||
// DO NOT include the user's registry url as it may contain credentials,
|
||||
// but also don't make this dependent on the registry url
|
||||
let global_cache_root_path = npm_resolver.global_cache_root_path();
|
||||
let mut builder =
|
||||
VfsBuilder::new(global_cache_root_path.to_path_buf())?;
|
||||
// we'll flatten to remove any custom registries later
|
||||
let mut packages =
|
||||
npm_resolver.all_system_packages(&self.npm_system_info);
|
||||
packages.sort_by(|a, b| a.id.cmp(&b.id)); // determinism
|
||||
|
@ -823,55 +838,11 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
npm_resolver.resolve_pkg_folder_from_pkg_id(&package.id)?;
|
||||
builder.add_dir_recursive(&folder)?;
|
||||
}
|
||||
|
||||
// Flatten all the registries folders into a single ".deno_compile_node_modules/localhost" folder
|
||||
// that will be used by denort when loading the npm cache. This avoids us exposing
|
||||
// the user's private registry information and means we don't have to bother
|
||||
// serializing all the different registry config into the binary.
|
||||
builder.with_root_dir(|root_dir| {
|
||||
root_dir.name = ".deno_compile_node_modules".to_string();
|
||||
let mut new_entries = Vec::with_capacity(root_dir.entries.len());
|
||||
let mut localhost_entries = IndexMap::new();
|
||||
for entry in std::mem::take(&mut root_dir.entries) {
|
||||
match entry {
|
||||
VfsEntry::Dir(dir) => {
|
||||
for entry in dir.entries {
|
||||
log::debug!(
|
||||
"Flattening {} into node_modules",
|
||||
entry.name()
|
||||
);
|
||||
if let Some(existing) =
|
||||
localhost_entries.insert(entry.name().to_string(), entry)
|
||||
{
|
||||
panic!(
|
||||
"Unhandled scenario where a duplicate entry was found: {:?}",
|
||||
existing
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
VfsEntry::File(_) | VfsEntry::Symlink(_) => {
|
||||
new_entries.push(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
new_entries.push(VfsEntry::Dir(VirtualDirectory {
|
||||
name: "localhost".to_string(),
|
||||
entries: localhost_entries.into_iter().map(|(_, v)| v).collect(),
|
||||
}));
|
||||
// needs to be sorted by name
|
||||
new_entries.sort_by(|a, b| a.name().cmp(b.name()));
|
||||
root_dir.entries = new_entries;
|
||||
});
|
||||
|
||||
builder.set_new_root_path(root_path.to_path_buf())?;
|
||||
|
||||
Ok(builder)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
InnerCliNpmResolverRef::Byonm(_) => {
|
||||
maybe_warn_different_system(&self.npm_system_info);
|
||||
let mut builder = VfsBuilder::new(root_path.to_path_buf())?;
|
||||
for pkg_json in self.cli_options.workspace().package_jsons() {
|
||||
builder.add_file_at_path(&pkg_json.path)?;
|
||||
}
|
||||
|
@ -904,10 +875,102 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
Ok(builder)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_vfs_consolidating_global_npm_cache(
|
||||
&self,
|
||||
mut vfs: VfsBuilder,
|
||||
) -> BuiltVfs {
|
||||
match self.npm_resolver.as_inner() {
|
||||
InnerCliNpmResolverRef::Managed(npm_resolver) => {
|
||||
if npm_resolver.root_node_modules_path().is_some() {
|
||||
return vfs.build();
|
||||
}
|
||||
|
||||
let global_cache_root_path = npm_resolver.global_cache_root_path();
|
||||
|
||||
// Flatten all the registries folders into a single ".deno_compile_node_modules/localhost" folder
|
||||
// that will be used by denort when loading the npm cache. This avoids us exposing
|
||||
// the user's private registry information and means we don't have to bother
|
||||
// serializing all the different registry config into the binary.
|
||||
let Some(root_dir) = vfs.get_dir_mut(global_cache_root_path) else {
|
||||
return vfs.build();
|
||||
};
|
||||
|
||||
root_dir.name = DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME.to_string();
|
||||
let mut new_entries = Vec::with_capacity(root_dir.entries.len());
|
||||
let mut localhost_entries = IndexMap::new();
|
||||
for entry in std::mem::take(&mut root_dir.entries) {
|
||||
match entry {
|
||||
VfsEntry::Dir(dir) => {
|
||||
for entry in dir.entries {
|
||||
log::debug!("Flattening {} into node_modules", entry.name());
|
||||
if let Some(existing) =
|
||||
localhost_entries.insert(entry.name().to_string(), entry)
|
||||
{
|
||||
panic!(
|
||||
"Unhandled scenario where a duplicate entry was found: {:?}",
|
||||
existing
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
VfsEntry::File(_) | VfsEntry::Symlink(_) => {
|
||||
new_entries.push(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
new_entries.push(VfsEntry::Dir(VirtualDirectory {
|
||||
name: "localhost".to_string(),
|
||||
entries: localhost_entries.into_iter().map(|(_, v)| v).collect(),
|
||||
}));
|
||||
// needs to be sorted by name
|
||||
new_entries.sort_by(|a, b| a.name().cmp(b.name()));
|
||||
root_dir.entries = new_entries;
|
||||
|
||||
// it's better to not expose the user's cache directory, so take it out
|
||||
// of there
|
||||
let parent = global_cache_root_path.parent().unwrap();
|
||||
let parent_dir = vfs.get_dir_mut(parent).unwrap();
|
||||
let index = parent_dir
|
||||
.entries
|
||||
.iter()
|
||||
.position(|entry| {
|
||||
entry.name() == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME
|
||||
})
|
||||
.unwrap();
|
||||
let npm_global_cache_dir_entry = parent_dir.entries.remove(index);
|
||||
|
||||
// go up from the ancestors removing empty directories...
|
||||
// this is not as optimized as it could be
|
||||
let mut last_name =
|
||||
Cow::Borrowed(DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME);
|
||||
for ancestor in parent.ancestors() {
|
||||
let dir = vfs.get_dir_mut(ancestor).unwrap();
|
||||
if let Some(index) = dir
|
||||
.entries
|
||||
.iter()
|
||||
.position(|entry| entry.name() == last_name)
|
||||
{
|
||||
dir.entries.remove(index);
|
||||
}
|
||||
last_name = Cow::Owned(dir.name.clone());
|
||||
if !dir.entries.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// now build the vfs and add the global cache dir entry there
|
||||
let mut built_vfs = vfs.build();
|
||||
built_vfs.root.insert_entry(npm_global_cache_dir_entry);
|
||||
built_vfs
|
||||
}
|
||||
InnerCliNpmResolverRef::Byonm(_) => vfs.build(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_denort_path(deno_exe: PathBuf) -> Option<OsString> {
|
||||
|
|
|
@ -9,6 +9,7 @@ use binary::StandaloneData;
|
|||
use binary::StandaloneModules;
|
||||
use code_cache::DenoCompileCodeCache;
|
||||
use deno_ast::MediaType;
|
||||
use deno_cache_dir::file_fetcher::CacheSetting;
|
||||
use deno_cache_dir::npm::NpmCacheDir;
|
||||
use deno_config::workspace::MappedResolution;
|
||||
use deno_config::workspace::MappedResolutionError;
|
||||
|
@ -64,7 +65,6 @@ use crate::args::create_default_npmrc;
|
|||
use crate::args::get_root_cert_store;
|
||||
use crate::args::npm_pkg_req_ref_to_binary_command;
|
||||
use crate::args::CaData;
|
||||
use crate::args::CacheSetting;
|
||||
use crate::args::NpmInstallDepsProvider;
|
||||
use crate::args::StorageKeyResolver;
|
||||
use crate::cache::Caches;
|
||||
|
@ -924,6 +924,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
|||
serve_host: None,
|
||||
},
|
||||
metadata.otel_config,
|
||||
crate::args::NpmCachingStrategy::Lazy,
|
||||
);
|
||||
|
||||
// Initialize v8 once from the main thread.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::Cell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
|
@ -23,6 +24,7 @@ use deno_semver::package::PackageReq;
|
|||
use crate::standalone::virtual_fs::VirtualDirectory;
|
||||
|
||||
use super::binary::Metadata;
|
||||
use super::virtual_fs::BuiltVfs;
|
||||
use super::virtual_fs::VfsBuilder;
|
||||
|
||||
const MAGIC_BYTES: &[u8; 8] = b"d3n0l4nd";
|
||||
|
@ -39,53 +41,48 @@ pub fn serialize_binary_data_section(
|
|||
metadata: &Metadata,
|
||||
npm_snapshot: Option<SerializedNpmResolutionSnapshot>,
|
||||
remote_modules: &RemoteModulesStoreBuilder,
|
||||
vfs: VfsBuilder,
|
||||
vfs: &BuiltVfs,
|
||||
) -> Result<Vec<u8>, AnyError> {
|
||||
fn write_bytes_with_len(bytes: &mut Vec<u8>, data: &[u8]) {
|
||||
bytes.extend_from_slice(&(data.len() as u64).to_le_bytes());
|
||||
bytes.extend_from_slice(data);
|
||||
}
|
||||
let metadata = serde_json::to_string(metadata)?;
|
||||
let npm_snapshot =
|
||||
npm_snapshot.map(serialize_npm_snapshot).unwrap_or_default();
|
||||
let remote_modules_len = Cell::new(0_u64);
|
||||
let serialized_vfs = serde_json::to_string(&vfs.root)?;
|
||||
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(MAGIC_BYTES);
|
||||
|
||||
// 1. Metadata
|
||||
{
|
||||
let metadata = serde_json::to_string(metadata)?;
|
||||
write_bytes_with_len(&mut bytes, metadata.as_bytes());
|
||||
}
|
||||
// 2. Npm snapshot
|
||||
{
|
||||
let npm_snapshot =
|
||||
npm_snapshot.map(serialize_npm_snapshot).unwrap_or_default();
|
||||
write_bytes_with_len(&mut bytes, &npm_snapshot);
|
||||
}
|
||||
// 3. Remote modules
|
||||
{
|
||||
let update_index = bytes.len();
|
||||
bytes.extend_from_slice(&(0_u64).to_le_bytes());
|
||||
let start_index = bytes.len();
|
||||
remote_modules.write(&mut bytes)?;
|
||||
let length = bytes.len() - start_index;
|
||||
let length_bytes = (length as u64).to_le_bytes();
|
||||
bytes[update_index..update_index + length_bytes.len()]
|
||||
.copy_from_slice(&length_bytes);
|
||||
}
|
||||
// 4. VFS
|
||||
{
|
||||
let (vfs, vfs_files) = vfs.into_dir_and_files();
|
||||
let vfs = serde_json::to_string(&vfs)?;
|
||||
write_bytes_with_len(&mut bytes, vfs.as_bytes());
|
||||
let vfs_bytes_len = vfs_files.iter().map(|f| f.len() as u64).sum::<u64>();
|
||||
bytes.extend_from_slice(&vfs_bytes_len.to_le_bytes());
|
||||
for file in &vfs_files {
|
||||
bytes.extend_from_slice(file);
|
||||
let bytes = capacity_builder::BytesBuilder::build(|builder| {
|
||||
builder.append(MAGIC_BYTES);
|
||||
// 1. Metadata
|
||||
{
|
||||
builder.append_le(metadata.len() as u64);
|
||||
builder.append(&metadata);
|
||||
}
|
||||
// 2. Npm snapshot
|
||||
{
|
||||
builder.append_le(npm_snapshot.len() as u64);
|
||||
builder.append(&npm_snapshot);
|
||||
}
|
||||
// 3. Remote modules
|
||||
{
|
||||
builder.append_le(remote_modules_len.get()); // this will be properly initialized on the second pass
|
||||
let start_index = builder.len();
|
||||
remote_modules.write(builder);
|
||||
remote_modules_len.set((builder.len() - start_index) as u64);
|
||||
}
|
||||
// 4. VFS
|
||||
{
|
||||
builder.append_le(serialized_vfs.len() as u64);
|
||||
builder.append(&serialized_vfs);
|
||||
let vfs_bytes_len = vfs.files.iter().map(|f| f.len() as u64).sum::<u64>();
|
||||
builder.append_le(vfs_bytes_len);
|
||||
for file in &vfs.files {
|
||||
builder.append(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// write the magic bytes at the end so we can use it
|
||||
// to make sure we've deserialized correctly
|
||||
bytes.extend_from_slice(MAGIC_BYTES);
|
||||
// write the magic bytes at the end so we can use it
|
||||
// to make sure we've deserialized correctly
|
||||
builder.append(MAGIC_BYTES);
|
||||
})?;
|
||||
|
||||
Ok(bytes)
|
||||
}
|
||||
|
@ -191,26 +188,25 @@ impl RemoteModulesStoreBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
fn write(&self, writer: &mut dyn Write) -> Result<(), AnyError> {
|
||||
writer.write_all(&(self.specifiers.len() as u32).to_le_bytes())?;
|
||||
writer.write_all(&(self.redirects.len() as u32).to_le_bytes())?;
|
||||
fn write<'a>(&'a self, builder: &mut capacity_builder::BytesBuilder<'a>) {
|
||||
builder.append_le(self.specifiers.len() as u32);
|
||||
builder.append_le(self.redirects.len() as u32);
|
||||
for (specifier, offset) in &self.specifiers {
|
||||
writer.write_all(&(specifier.len() as u32).to_le_bytes())?;
|
||||
writer.write_all(specifier.as_bytes())?;
|
||||
writer.write_all(&offset.to_le_bytes())?;
|
||||
builder.append_le(specifier.len() as u32);
|
||||
builder.append(specifier.as_bytes());
|
||||
builder.append_le(*offset);
|
||||
}
|
||||
for (from, to) in &self.redirects {
|
||||
writer.write_all(&(from.len() as u32).to_le_bytes())?;
|
||||
writer.write_all(from.as_bytes())?;
|
||||
writer.write_all(&(to.len() as u32).to_le_bytes())?;
|
||||
writer.write_all(to.as_bytes())?;
|
||||
builder.append_le(from.len() as u32);
|
||||
builder.append(from);
|
||||
builder.append_le(to.len() as u32);
|
||||
builder.append(to);
|
||||
}
|
||||
for (media_type, data) in &self.data {
|
||||
writer.write_all(&[serialize_media_type(*media_type)])?;
|
||||
writer.write_all(&(data.len() as u64).to_le_bytes())?;
|
||||
writer.write_all(data)?;
|
||||
builder.append(serialize_media_type(*media_type));
|
||||
builder.append_le(data.len() as u64);
|
||||
builder.append(data);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -585,7 +585,13 @@ pub async fn run_future_forwarding_signals<TOutput>(
|
|||
|
||||
async fn listen_ctrl_c(kill_signal: KillSignal) {
|
||||
while let Ok(()) = tokio::signal::ctrl_c().await {
|
||||
kill_signal.send(deno_task_shell::SignalKind::SIGINT)
|
||||
// On windows, ctrl+c is sent to the process group, so the signal would
|
||||
// have already been sent to the child process. We still want to listen
|
||||
// for ctrl+c here to keep the process alive when receiving it, but no
|
||||
// need to forward the signal because it's already been sent.
|
||||
if !cfg!(windows) {
|
||||
kill_signal.send(deno_task_shell::SignalKind::SIGINT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -538,7 +538,11 @@ pub async fn run_benchmarks_with_watch(
|
|||
)?;
|
||||
|
||||
let graph = module_graph_creator
|
||||
.create_graph(graph_kind, collected_bench_modules.clone())
|
||||
.create_graph(
|
||||
graph_kind,
|
||||
collected_bench_modules.clone(),
|
||||
crate::graph_util::NpmCachingStrategy::Eager,
|
||||
)
|
||||
.await?;
|
||||
module_graph_creator.graph_valid(&graph)?;
|
||||
let bench_modules = &graph.roots;
|
||||
|
|
|
@ -64,7 +64,7 @@ pub async fn check(
|
|||
let file = file_fetcher.fetch(&s, root_permissions).await?;
|
||||
let snippet_files = extract::extract_snippet_files(file)?;
|
||||
for snippet_file in snippet_files {
|
||||
specifiers_for_typecheck.push(snippet_file.specifier.clone());
|
||||
specifiers_for_typecheck.push(snippet_file.url.clone());
|
||||
file_fetcher.insert_memory_files(snippet_file);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::args::CompileFlags;
|
|||
use crate::args::Flags;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
use crate::standalone::binary::StandaloneRelativeFileBaseUrl;
|
||||
use crate::standalone::binary::WriteBinOptions;
|
||||
use crate::standalone::is_standalone_binary;
|
||||
use deno_ast::MediaType;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
|
@ -15,8 +15,12 @@ use deno_core::error::generic_error;
|
|||
use deno_core::error::AnyError;
|
||||
use deno_core::resolve_url_or_path;
|
||||
use deno_graph::GraphKind;
|
||||
use deno_path_util::url_from_file_path;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_terminal::colors;
|
||||
use rand::Rng;
|
||||
use std::collections::HashSet;
|
||||
use std::collections::VecDeque;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
@ -69,7 +73,11 @@ pub async fn compile(
|
|||
// create a module graph with types information in it. We don't want to
|
||||
// store that in the binary so create a code only module graph from scratch.
|
||||
module_graph_creator
|
||||
.create_graph(GraphKind::CodeOnly, module_roots)
|
||||
.create_graph(
|
||||
GraphKind::CodeOnly,
|
||||
module_roots,
|
||||
crate::graph_util::NpmCachingStrategy::Eager,
|
||||
)
|
||||
.await?
|
||||
} else {
|
||||
graph
|
||||
|
@ -78,20 +86,6 @@ pub async fn compile(
|
|||
let ts_config_for_emit = cli_options
|
||||
.resolve_ts_config_for_emit(deno_config::deno_json::TsConfigType::Emit)?;
|
||||
check_warn_tsconfig(&ts_config_for_emit);
|
||||
let root_dir_url = resolve_root_dir_from_specifiers(
|
||||
cli_options.workspace().root_dir(),
|
||||
graph
|
||||
.specifiers()
|
||||
.map(|(s, _)| s)
|
||||
.chain(
|
||||
cli_options
|
||||
.node_modules_dir_path()
|
||||
.and_then(|p| ModuleSpecifier::from_directory_path(p).ok())
|
||||
.iter(),
|
||||
)
|
||||
.chain(include_files.iter()),
|
||||
);
|
||||
log::debug!("Binary root dir: {}", root_dir_url);
|
||||
log::info!(
|
||||
"{} {} to {}",
|
||||
colors::green("Compile"),
|
||||
|
@ -116,14 +110,17 @@ pub async fn compile(
|
|||
})?;
|
||||
|
||||
let write_result = binary_writer
|
||||
.write_bin(
|
||||
file,
|
||||
&graph,
|
||||
StandaloneRelativeFileBaseUrl::from(&root_dir_url),
|
||||
.write_bin(WriteBinOptions {
|
||||
writer: file,
|
||||
display_output_filename: &output_path
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_string_lossy(),
|
||||
graph: &graph,
|
||||
entrypoint,
|
||||
&include_files,
|
||||
&compile_flags,
|
||||
)
|
||||
include_files: &include_files,
|
||||
compile_flags: &compile_flags,
|
||||
})
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
|
@ -242,15 +239,58 @@ fn get_module_roots_and_include_files(
|
|||
}
|
||||
}
|
||||
|
||||
let mut module_roots = Vec::with_capacity(compile_flags.include.len() + 1);
|
||||
let mut include_files = Vec::with_capacity(compile_flags.include.len());
|
||||
fn analyze_path(
|
||||
url: &ModuleSpecifier,
|
||||
module_roots: &mut Vec<ModuleSpecifier>,
|
||||
include_files: &mut Vec<ModuleSpecifier>,
|
||||
searched_paths: &mut HashSet<PathBuf>,
|
||||
) -> Result<(), AnyError> {
|
||||
let Ok(path) = url_to_file_path(url) else {
|
||||
return Ok(());
|
||||
};
|
||||
let mut pending = VecDeque::from([path]);
|
||||
while let Some(path) = pending.pop_front() {
|
||||
if !searched_paths.insert(path.clone()) {
|
||||
continue;
|
||||
}
|
||||
if !path.is_dir() {
|
||||
let url = url_from_file_path(&path)?;
|
||||
include_files.push(url.clone());
|
||||
if is_module_graph_module(&url) {
|
||||
module_roots.push(url);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for entry in std::fs::read_dir(&path).with_context(|| {
|
||||
format!("Failed reading directory '{}'", path.display())
|
||||
})? {
|
||||
let entry = entry.with_context(|| {
|
||||
format!("Failed reading entry in directory '{}'", path.display())
|
||||
})?;
|
||||
pending.push_back(entry.path());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let mut searched_paths = HashSet::new();
|
||||
let mut module_roots = Vec::new();
|
||||
let mut include_files = Vec::new();
|
||||
module_roots.push(entrypoint.clone());
|
||||
for side_module in &compile_flags.include {
|
||||
let url = resolve_url_or_path(side_module, initial_cwd)?;
|
||||
if is_module_graph_module(&url) {
|
||||
module_roots.push(url);
|
||||
module_roots.push(url.clone());
|
||||
if url.scheme() == "file" {
|
||||
include_files.push(url);
|
||||
}
|
||||
} else {
|
||||
include_files.push(url);
|
||||
analyze_path(
|
||||
&url,
|
||||
&mut module_roots,
|
||||
&mut include_files,
|
||||
&mut searched_paths,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok((module_roots, include_files))
|
||||
|
@ -316,68 +356,6 @@ fn get_os_specific_filepath(
|
|||
}
|
||||
}
|
||||
|
||||
fn resolve_root_dir_from_specifiers<'a>(
|
||||
starting_dir: &ModuleSpecifier,
|
||||
specifiers: impl Iterator<Item = &'a ModuleSpecifier>,
|
||||
) -> ModuleSpecifier {
|
||||
fn select_common_root<'a>(a: &'a str, b: &'a str) -> &'a str {
|
||||
let min_length = a.len().min(b.len());
|
||||
|
||||
let mut last_slash = 0;
|
||||
for i in 0..min_length {
|
||||
if a.as_bytes()[i] == b.as_bytes()[i] && a.as_bytes()[i] == b'/' {
|
||||
last_slash = i;
|
||||
} else if a.as_bytes()[i] != b.as_bytes()[i] {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the common root path up to the last common slash.
|
||||
// This returns a slice of the original string 'a', up to and including the last matching '/'.
|
||||
let common = &a[..=last_slash];
|
||||
if cfg!(windows) && common == "file:///" {
|
||||
a
|
||||
} else {
|
||||
common
|
||||
}
|
||||
}
|
||||
|
||||
fn is_file_system_root(url: &str) -> bool {
|
||||
let Some(path) = url.strip_prefix("file:///") else {
|
||||
return false;
|
||||
};
|
||||
if cfg!(windows) {
|
||||
let Some((_drive, path)) = path.split_once('/') else {
|
||||
return true;
|
||||
};
|
||||
path.is_empty()
|
||||
} else {
|
||||
path.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
let mut found_dir = starting_dir.as_str();
|
||||
if !is_file_system_root(found_dir) {
|
||||
for specifier in specifiers {
|
||||
if specifier.scheme() == "file" {
|
||||
found_dir = select_common_root(found_dir, specifier.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
let found_dir = if is_file_system_root(found_dir) {
|
||||
found_dir
|
||||
} else {
|
||||
// include the parent dir name because it helps create some context
|
||||
found_dir
|
||||
.strip_suffix('/')
|
||||
.unwrap_or(found_dir)
|
||||
.rfind('/')
|
||||
.map(|i| &found_dir[..i + 1])
|
||||
.unwrap_or(found_dir)
|
||||
};
|
||||
ModuleSpecifier::parse(found_dir).unwrap()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
pub use super::*;
|
||||
|
@ -454,38 +432,4 @@ mod test {
|
|||
run_test("C:\\my-exe.0.1.2", Some("windows"), "C:\\my-exe.0.1.2.exe");
|
||||
run_test("my-exe-0.1.2", Some("linux"), "my-exe-0.1.2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolve_root_dir_from_specifiers() {
|
||||
fn resolve(start: &str, specifiers: &[&str]) -> String {
|
||||
let specifiers = specifiers
|
||||
.iter()
|
||||
.map(|s| ModuleSpecifier::parse(s).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
resolve_root_dir_from_specifiers(
|
||||
&ModuleSpecifier::parse(start).unwrap(),
|
||||
specifiers.iter(),
|
||||
)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
assert_eq!(resolve("file:///a/b/c", &["file:///a/b/c/d"]), "file:///a/");
|
||||
assert_eq!(
|
||||
resolve("file:///a/b/c/", &["file:///a/b/c/d"]),
|
||||
"file:///a/b/"
|
||||
);
|
||||
assert_eq!(
|
||||
resolve("file:///a/b/c/", &["file:///a/b/c/d", "file:///a/b/c/e"]),
|
||||
"file:///a/b/"
|
||||
);
|
||||
assert_eq!(resolve("file:///", &["file:///a/b/c/d"]), "file:///");
|
||||
if cfg!(windows) {
|
||||
assert_eq!(resolve("file:///c:/", &["file:///c:/test"]), "file:///c:/");
|
||||
// this will ignore the other one because it's on a separate drive
|
||||
assert_eq!(
|
||||
resolve("file:///c:/a/b/c/", &["file:///v:/a/b/c/d"]),
|
||||
"file:///c:/a/b/"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use crate::args::FileFlags;
|
|||
use crate::args::Flags;
|
||||
use crate::cdp;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::file_fetcher::TextDecodedFile;
|
||||
use crate::tools::fmt::format_json;
|
||||
use crate::tools::test::is_supported_test_path;
|
||||
use crate::util::text_encoding::source_map_from_code;
|
||||
|
@ -559,6 +560,12 @@ pub fn cover_files(
|
|||
},
|
||||
None => None,
|
||||
};
|
||||
let get_message = |specifier: &ModuleSpecifier| -> String {
|
||||
format!(
|
||||
"Failed to fetch \"{}\" from cache. Before generating coverage report, run `deno test --coverage` to ensure consistent state.",
|
||||
specifier,
|
||||
)
|
||||
};
|
||||
|
||||
for script_coverage in script_coverages {
|
||||
let module_specifier = deno_core::resolve_url_or_path(
|
||||
|
@ -566,21 +573,14 @@ pub fn cover_files(
|
|||
cli_options.initial_cwd(),
|
||||
)?;
|
||||
|
||||
let maybe_file = if module_specifier.scheme() == "file" {
|
||||
file_fetcher.get_source(&module_specifier)
|
||||
} else {
|
||||
file_fetcher
|
||||
.fetch_cached(&module_specifier, 10)
|
||||
.with_context(|| {
|
||||
format!("Failed to fetch \"{module_specifier}\" from cache.")
|
||||
})?
|
||||
let maybe_file_result = file_fetcher
|
||||
.get_cached_source_or_local(&module_specifier)
|
||||
.map_err(AnyError::from);
|
||||
let file = match maybe_file_result {
|
||||
Ok(Some(file)) => TextDecodedFile::decode(file)?,
|
||||
Ok(None) => return Err(anyhow!("{}", get_message(&module_specifier))),
|
||||
Err(err) => return Err(err).context(get_message(&module_specifier)),
|
||||
};
|
||||
let file = maybe_file.ok_or_else(|| {
|
||||
anyhow!("Failed to fetch \"{}\" from cache.
|
||||
Before generating coverage report, run `deno test --coverage` to ensure consistent state.",
|
||||
module_specifier
|
||||
)
|
||||
})?.into_text_decoded()?;
|
||||
|
||||
let original_source = file.source.clone();
|
||||
// Check if file was transpiled
|
||||
|
|
|
@ -131,7 +131,11 @@ pub async fn doc(
|
|||
|_| true,
|
||||
)?;
|
||||
let graph = module_graph_creator
|
||||
.create_graph(GraphKind::TypesOnly, module_specifiers.clone())
|
||||
.create_graph(
|
||||
GraphKind::TypesOnly,
|
||||
module_specifiers.clone(),
|
||||
crate::graph_util::NpmCachingStrategy::Eager,
|
||||
)
|
||||
.await?;
|
||||
|
||||
graph_exit_integrity_errors(&graph);
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::fmt::Write;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -35,6 +34,7 @@ use crate::graph_util::graph_exit_integrity_errors;
|
|||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::ManagedCliNpmResolver;
|
||||
use crate::util::checksum;
|
||||
use crate::util::display::DisplayTreeNode;
|
||||
|
||||
const JSON_SCHEMA_VERSION: u8 = 1;
|
||||
|
||||
|
@ -123,7 +123,12 @@ pub async fn info(
|
|||
let mut loader = module_graph_builder.create_graph_loader();
|
||||
loader.enable_loading_cache_info(); // for displaying the cache information
|
||||
let graph = module_graph_creator
|
||||
.create_graph_with_loader(GraphKind::All, vec![specifier], &mut loader)
|
||||
.create_graph_with_loader(
|
||||
GraphKind::All,
|
||||
vec![specifier],
|
||||
&mut loader,
|
||||
crate::graph_util::NpmCachingStrategy::Eager,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// write out the lockfile if there is one
|
||||
|
@ -337,76 +342,6 @@ fn add_npm_packages_to_json(
|
|||
json.insert("npmPackages".to_string(), json_packages.into());
|
||||
}
|
||||
|
||||
struct TreeNode {
|
||||
text: String,
|
||||
children: Vec<TreeNode>,
|
||||
}
|
||||
|
||||
impl TreeNode {
|
||||
pub fn from_text(text: String) -> Self {
|
||||
Self {
|
||||
text,
|
||||
children: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_tree_node<TWrite: Write>(
|
||||
tree_node: &TreeNode,
|
||||
writer: &mut TWrite,
|
||||
) -> fmt::Result {
|
||||
fn print_children<TWrite: Write>(
|
||||
writer: &mut TWrite,
|
||||
prefix: &str,
|
||||
children: &[TreeNode],
|
||||
) -> fmt::Result {
|
||||
const SIBLING_CONNECTOR: char = '├';
|
||||
const LAST_SIBLING_CONNECTOR: char = '└';
|
||||
const CHILD_DEPS_CONNECTOR: char = '┬';
|
||||
const CHILD_NO_DEPS_CONNECTOR: char = '─';
|
||||
const VERTICAL_CONNECTOR: char = '│';
|
||||
const EMPTY_CONNECTOR: char = ' ';
|
||||
|
||||
let child_len = children.len();
|
||||
for (index, child) in children.iter().enumerate() {
|
||||
let is_last = index + 1 == child_len;
|
||||
let sibling_connector = if is_last {
|
||||
LAST_SIBLING_CONNECTOR
|
||||
} else {
|
||||
SIBLING_CONNECTOR
|
||||
};
|
||||
let child_connector = if child.children.is_empty() {
|
||||
CHILD_NO_DEPS_CONNECTOR
|
||||
} else {
|
||||
CHILD_DEPS_CONNECTOR
|
||||
};
|
||||
writeln!(
|
||||
writer,
|
||||
"{} {}",
|
||||
colors::gray(format!("{prefix}{sibling_connector}─{child_connector}")),
|
||||
child.text
|
||||
)?;
|
||||
let child_prefix = format!(
|
||||
"{}{}{}",
|
||||
prefix,
|
||||
if is_last {
|
||||
EMPTY_CONNECTOR
|
||||
} else {
|
||||
VERTICAL_CONNECTOR
|
||||
},
|
||||
EMPTY_CONNECTOR
|
||||
);
|
||||
print_children(writer, &child_prefix, &child.children)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
writeln!(writer, "{}", tree_node.text)?;
|
||||
print_children(writer, "", &tree_node.children)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Precached information about npm packages that are used in deno info.
|
||||
#[derive(Default)]
|
||||
struct NpmInfo {
|
||||
|
@ -563,7 +498,7 @@ impl<'a> GraphDisplayContext<'a> {
|
|||
)?;
|
||||
writeln!(writer)?;
|
||||
let root_node = self.build_module_info(root, false);
|
||||
print_tree_node(&root_node, writer)?;
|
||||
root_node.print(writer)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
|
@ -579,7 +514,7 @@ impl<'a> GraphDisplayContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn build_dep_info(&mut self, dep: &Dependency) -> Vec<TreeNode> {
|
||||
fn build_dep_info(&mut self, dep: &Dependency) -> Vec<DisplayTreeNode> {
|
||||
let mut children = Vec::with_capacity(2);
|
||||
if !dep.maybe_code.is_none() {
|
||||
if let Some(child) = self.build_resolved_info(&dep.maybe_code, false) {
|
||||
|
@ -594,7 +529,11 @@ impl<'a> GraphDisplayContext<'a> {
|
|||
children
|
||||
}
|
||||
|
||||
fn build_module_info(&mut self, module: &Module, type_dep: bool) -> TreeNode {
|
||||
fn build_module_info(
|
||||
&mut self,
|
||||
module: &Module,
|
||||
type_dep: bool,
|
||||
) -> DisplayTreeNode {
|
||||
enum PackageOrSpecifier {
|
||||
Package(Box<NpmResolutionPackage>),
|
||||
Specifier(ModuleSpecifier),
|
||||
|
@ -640,7 +579,7 @@ impl<'a> GraphDisplayContext<'a> {
|
|||
format!("{} {}", header_text, maybe_size_to_text(maybe_size))
|
||||
};
|
||||
|
||||
let mut tree_node = TreeNode::from_text(header_text);
|
||||
let mut tree_node = DisplayTreeNode::from_text(header_text);
|
||||
|
||||
if !was_seen {
|
||||
match &package_or_specifier {
|
||||
|
@ -678,14 +617,14 @@ impl<'a> GraphDisplayContext<'a> {
|
|||
fn build_npm_deps(
|
||||
&mut self,
|
||||
package: &NpmResolutionPackage,
|
||||
) -> Vec<TreeNode> {
|
||||
) -> Vec<DisplayTreeNode> {
|
||||
let mut deps = package.dependencies.values().collect::<Vec<_>>();
|
||||
deps.sort();
|
||||
let mut children = Vec::with_capacity(deps.len());
|
||||
for dep_id in deps.into_iter() {
|
||||
let maybe_size = self.npm_info.package_sizes.get(dep_id).cloned();
|
||||
let size_str = maybe_size_to_text(maybe_size);
|
||||
let mut child = TreeNode::from_text(format!(
|
||||
let mut child = DisplayTreeNode::from_text(format!(
|
||||
"npm:/{} {}",
|
||||
dep_id.as_serialized(),
|
||||
size_str
|
||||
|
@ -710,7 +649,7 @@ impl<'a> GraphDisplayContext<'a> {
|
|||
&mut self,
|
||||
err: &ModuleError,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> TreeNode {
|
||||
) -> DisplayTreeNode {
|
||||
self.seen.insert(specifier.to_string());
|
||||
match err {
|
||||
ModuleError::InvalidTypeAssertion { .. } => {
|
||||
|
@ -753,8 +692,8 @@ impl<'a> GraphDisplayContext<'a> {
|
|||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
error_msg: &str,
|
||||
) -> TreeNode {
|
||||
TreeNode::from_text(format!(
|
||||
) -> DisplayTreeNode {
|
||||
DisplayTreeNode::from_text(format!(
|
||||
"{} {}",
|
||||
colors::red(specifier),
|
||||
colors::red_bold(error_msg)
|
||||
|
@ -765,7 +704,7 @@ impl<'a> GraphDisplayContext<'a> {
|
|||
&mut self,
|
||||
resolution: &Resolution,
|
||||
type_dep: bool,
|
||||
) -> Option<TreeNode> {
|
||||
) -> Option<DisplayTreeNode> {
|
||||
match resolution {
|
||||
Resolution::Ok(resolved) => {
|
||||
let specifier = &resolved.specifier;
|
||||
|
@ -773,14 +712,14 @@ impl<'a> GraphDisplayContext<'a> {
|
|||
Some(match self.graph.try_get(resolved_specifier) {
|
||||
Ok(Some(module)) => self.build_module_info(module, type_dep),
|
||||
Err(err) => self.build_error_info(err, resolved_specifier),
|
||||
Ok(None) => TreeNode::from_text(format!(
|
||||
Ok(None) => DisplayTreeNode::from_text(format!(
|
||||
"{} {}",
|
||||
colors::red(specifier),
|
||||
colors::red_bold("(missing)")
|
||||
)),
|
||||
})
|
||||
}
|
||||
Resolution::Err(err) => Some(TreeNode::from_text(format!(
|
||||
Resolution::Err(err) => Some(DisplayTreeNode::from_text(format!(
|
||||
"{} {}",
|
||||
colors::italic(err.to_string()),
|
||||
colors::red_bold("(resolve error)")
|
||||
|
|
|
@ -3,24 +3,23 @@
|
|||
use crate::args::resolve_no_prompt;
|
||||
use crate::args::AddFlags;
|
||||
use crate::args::CaData;
|
||||
use crate::args::CacheSetting;
|
||||
use crate::args::ConfigFlag;
|
||||
use crate::args::Flags;
|
||||
use crate::args::InstallFlags;
|
||||
use crate::args::InstallFlagsGlobal;
|
||||
use crate::args::InstallFlagsLocal;
|
||||
use crate::args::InstallKind;
|
||||
use crate::args::TypeCheckMode;
|
||||
use crate::args::UninstallFlags;
|
||||
use crate::args::UninstallKind;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::graph_container::ModuleGraphContainer;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
use crate::jsr::JsrFetchResolver;
|
||||
use crate::npm::NpmFetchResolver;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists;
|
||||
|
||||
use deno_cache_dir::file_fetcher::CacheSetting;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::generic_error;
|
||||
|
@ -339,11 +338,11 @@ pub async fn install_command(
|
|||
flags: Arc<Flags>,
|
||||
install_flags: InstallFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
match install_flags.kind {
|
||||
InstallKind::Global(global_flags) => {
|
||||
match install_flags {
|
||||
InstallFlags::Global(global_flags) => {
|
||||
install_global(flags, global_flags).await
|
||||
}
|
||||
InstallKind::Local(local_flags) => {
|
||||
InstallFlags::Local(local_flags) => {
|
||||
if let InstallFlagsLocal::Add(add_flags) = &local_flags {
|
||||
check_if_installs_a_single_package_globally(Some(add_flags))?;
|
||||
}
|
||||
|
@ -362,18 +361,18 @@ async fn install_global(
|
|||
let cli_options = factory.cli_options()?;
|
||||
let http_client = factory.http_client_provider();
|
||||
let deps_http_cache = factory.global_http_cache()?;
|
||||
let mut deps_file_fetcher = FileFetcher::new(
|
||||
let deps_file_fetcher = CliFileFetcher::new(
|
||||
deps_http_cache.clone(),
|
||||
CacheSetting::ReloadAll,
|
||||
true,
|
||||
http_client.clone(),
|
||||
Default::default(),
|
||||
None,
|
||||
true,
|
||||
CacheSetting::ReloadAll,
|
||||
log::Level::Trace,
|
||||
);
|
||||
|
||||
let npmrc = factory.cli_options().unwrap().npmrc();
|
||||
|
||||
deps_file_fetcher.set_download_log_level(log::Level::Trace);
|
||||
let deps_file_fetcher = Arc::new(deps_file_fetcher);
|
||||
let jsr_resolver = Arc::new(JsrFetchResolver::new(deps_file_fetcher.clone()));
|
||||
let npm_resolver = Arc::new(NpmFetchResolver::new(
|
||||
|
|
|
@ -21,7 +21,7 @@ use deno_core::unsync::future::LocalFutureExt;
|
|||
use deno_core::unsync::future::SharedLocal;
|
||||
use deno_graph::ModuleGraph;
|
||||
use deno_lint::diagnostic::LintDiagnostic;
|
||||
use deno_lint::linter::LintConfig;
|
||||
use deno_lint::linter::LintConfig as DenoLintConfig;
|
||||
use deno_runtime::tokio_util;
|
||||
use log::debug;
|
||||
use reporters::create_reporter;
|
||||
|
@ -31,7 +31,6 @@ use std::collections::HashSet;
|
|||
use std::fs;
|
||||
use std::io::stdin;
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
@ -49,6 +48,7 @@ use crate::graph_util::ModuleGraphCreator;
|
|||
use crate::tools::fmt::run_parallelized;
|
||||
use crate::util::display;
|
||||
use crate::util::file_watcher;
|
||||
use crate::util::file_watcher::WatcherCommunicator;
|
||||
use crate::util::fs::canonicalize_path;
|
||||
use crate::util::path::is_script_ext;
|
||||
use crate::util::sync::AtomicFlag;
|
||||
|
@ -75,139 +75,143 @@ pub async fn lint(
|
|||
flags: Arc<Flags>,
|
||||
lint_flags: LintFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
if let Some(watch_flags) = &lint_flags.watch {
|
||||
if lint_flags.watch.is_some() {
|
||||
if lint_flags.is_stdin() {
|
||||
return Err(generic_error(
|
||||
"Lint watch on standard input is not supported.",
|
||||
));
|
||||
}
|
||||
file_watcher::watch_func(
|
||||
flags,
|
||||
file_watcher::PrintConfig::new("Lint", !watch_flags.no_clear_screen),
|
||||
move |flags, watcher_communicator, changed_paths| {
|
||||
let lint_flags = lint_flags.clone();
|
||||
watcher_communicator.show_path_changed(changed_paths.clone());
|
||||
Ok(async move {
|
||||
let factory = CliFactory::from_flags(flags);
|
||||
let cli_options = factory.cli_options()?;
|
||||
let lint_config = cli_options.resolve_deno_lint_config()?;
|
||||
let mut paths_with_options_batches =
|
||||
resolve_paths_with_options_batches(cli_options, &lint_flags)?;
|
||||
for paths_with_options in &mut paths_with_options_batches {
|
||||
_ = watcher_communicator
|
||||
.watch_paths(paths_with_options.paths.clone());
|
||||
|
||||
let files = std::mem::take(&mut paths_with_options.paths);
|
||||
paths_with_options.paths = if let Some(paths) = &changed_paths {
|
||||
// lint all files on any changed (https://github.com/denoland/deno/issues/12446)
|
||||
files
|
||||
.iter()
|
||||
.any(|path| {
|
||||
canonicalize_path(path)
|
||||
.map(|p| paths.contains(&p))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.then_some(files)
|
||||
.unwrap_or_else(|| [].to_vec())
|
||||
} else {
|
||||
files
|
||||
};
|
||||
}
|
||||
return lint_with_watch(flags, lint_flags).await;
|
||||
}
|
||||
|
||||
let mut linter = WorkspaceLinter::new(
|
||||
factory.caches()?.clone(),
|
||||
factory.lint_rule_provider().await?,
|
||||
factory.module_graph_creator().await?.clone(),
|
||||
cli_options.start_dir.clone(),
|
||||
&cli_options.resolve_workspace_lint_options(&lint_flags)?,
|
||||
);
|
||||
for paths_with_options in paths_with_options_batches {
|
||||
linter
|
||||
.lint_files(
|
||||
cli_options,
|
||||
paths_with_options.options,
|
||||
lint_config.clone(),
|
||||
paths_with_options.dir,
|
||||
paths_with_options.paths,
|
||||
lint_flags.maybe_plugins.clone(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
linter.finish();
|
||||
|
||||
Ok(())
|
||||
})
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let factory = CliFactory::from_flags(flags);
|
||||
let cli_options = factory.cli_options()?;
|
||||
let lint_rule_provider = factory.lint_rule_provider().await?;
|
||||
let is_stdin = lint_flags.is_stdin();
|
||||
let deno_lint_config = cli_options.resolve_deno_lint_config()?;
|
||||
let workspace_lint_options =
|
||||
cli_options.resolve_workspace_lint_options(&lint_flags)?;
|
||||
let success = if is_stdin {
|
||||
lint_stdin(
|
||||
cli_options,
|
||||
lint_rule_provider,
|
||||
workspace_lint_options,
|
||||
lint_flags,
|
||||
deno_lint_config,
|
||||
)?
|
||||
} else {
|
||||
let factory = CliFactory::from_flags(flags);
|
||||
let cli_options = factory.cli_options()?;
|
||||
let is_stdin = lint_flags.is_stdin();
|
||||
let deno_lint_config = cli_options.resolve_deno_lint_config()?;
|
||||
let workspace_lint_options =
|
||||
cli_options.resolve_workspace_lint_options(&lint_flags)?;
|
||||
let success = if is_stdin {
|
||||
let start_dir = &cli_options.start_dir;
|
||||
let reporter_lock = Arc::new(Mutex::new(create_reporter(
|
||||
workspace_lint_options.reporter_kind,
|
||||
)));
|
||||
let lint_config = start_dir
|
||||
.to_lint_config(FilePatterns::new_with_base(start_dir.dir_path()))?;
|
||||
let lint_options = LintOptions::resolve(lint_config, &lint_flags);
|
||||
let lint_rules = factory
|
||||
.lint_rule_provider()
|
||||
.await?
|
||||
.resolve_lint_rules_err_empty(
|
||||
lint_options.rules,
|
||||
start_dir.maybe_deno_json().map(|c| c.as_ref()),
|
||||
)?;
|
||||
let mut file_path = cli_options.initial_cwd().join(STDIN_FILE_NAME);
|
||||
if let Some(ext) = cli_options.ext_flag() {
|
||||
file_path.set_extension(ext);
|
||||
}
|
||||
let r = lint_stdin(&file_path, lint_rules, deno_lint_config);
|
||||
let success = handle_lint_result(
|
||||
&file_path.to_string_lossy(),
|
||||
r,
|
||||
reporter_lock.clone(),
|
||||
);
|
||||
reporter_lock.lock().close(1);
|
||||
success
|
||||
} else {
|
||||
let mut linter = WorkspaceLinter::new(
|
||||
factory.caches()?.clone(),
|
||||
factory.lint_rule_provider().await?,
|
||||
factory.module_graph_creator().await?.clone(),
|
||||
cli_options.start_dir.clone(),
|
||||
&workspace_lint_options,
|
||||
);
|
||||
let paths_with_options_batches =
|
||||
resolve_paths_with_options_batches(cli_options, &lint_flags)?;
|
||||
|
||||
for paths_with_options in paths_with_options_batches {
|
||||
linter
|
||||
.lint_files(
|
||||
cli_options,
|
||||
paths_with_options.options,
|
||||
deno_lint_config.clone(),
|
||||
paths_with_options.dir,
|
||||
paths_with_options.paths,
|
||||
lint_flags.maybe_plugins.clone(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
linter.finish()
|
||||
};
|
||||
if !success {
|
||||
deno_runtime::exit(1);
|
||||
let mut linter = WorkspaceLinter::new(
|
||||
factory.caches()?.clone(),
|
||||
lint_rule_provider,
|
||||
factory.module_graph_creator().await?.clone(),
|
||||
cli_options.start_dir.clone(),
|
||||
&workspace_lint_options,
|
||||
);
|
||||
let paths_with_options_batches =
|
||||
resolve_paths_with_options_batches(cli_options, &lint_flags)?;
|
||||
for paths_with_options in paths_with_options_batches {
|
||||
linter
|
||||
.lint_files(
|
||||
cli_options,
|
||||
paths_with_options.options,
|
||||
deno_lint_config.clone(),
|
||||
paths_with_options.dir,
|
||||
paths_with_options.paths,
|
||||
// TODO(bartlomieju): clean this up
|
||||
lint_flags.maybe_plugins.clone(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
linter.finish()
|
||||
};
|
||||
if !success {
|
||||
deno_runtime::exit(1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn lint_with_watch_inner(
|
||||
flags: Arc<Flags>,
|
||||
lint_flags: LintFlags,
|
||||
watcher_communicator: Arc<WatcherCommunicator>,
|
||||
changed_paths: Option<Vec<PathBuf>>,
|
||||
) -> Result<(), AnyError> {
|
||||
let factory = CliFactory::from_flags(flags);
|
||||
let cli_options = factory.cli_options()?;
|
||||
let lint_config = cli_options.resolve_deno_lint_config()?;
|
||||
let mut paths_with_options_batches =
|
||||
resolve_paths_with_options_batches(cli_options, &lint_flags)?;
|
||||
for paths_with_options in &mut paths_with_options_batches {
|
||||
_ = watcher_communicator.watch_paths(paths_with_options.paths.clone());
|
||||
|
||||
let files = std::mem::take(&mut paths_with_options.paths);
|
||||
paths_with_options.paths = if let Some(paths) = &changed_paths {
|
||||
// lint all files on any changed (https://github.com/denoland/deno/issues/12446)
|
||||
files
|
||||
.iter()
|
||||
.any(|path| {
|
||||
canonicalize_path(path)
|
||||
.map(|p| paths.contains(&p))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.then_some(files)
|
||||
.unwrap_or_else(|| [].to_vec())
|
||||
} else {
|
||||
files
|
||||
};
|
||||
}
|
||||
|
||||
let mut linter = WorkspaceLinter::new(
|
||||
factory.caches()?.clone(),
|
||||
factory.lint_rule_provider().await?,
|
||||
factory.module_graph_creator().await?.clone(),
|
||||
cli_options.start_dir.clone(),
|
||||
&cli_options.resolve_workspace_lint_options(&lint_flags)?,
|
||||
);
|
||||
for paths_with_options in paths_with_options_batches {
|
||||
linter
|
||||
.lint_files(
|
||||
cli_options,
|
||||
paths_with_options.options,
|
||||
lint_config.clone(),
|
||||
paths_with_options.dir,
|
||||
paths_with_options.paths,
|
||||
// TODO(bartlomieju): clean this up
|
||||
lint_flags.maybe_plugins.clone(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
linter.finish();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn lint_with_watch(
|
||||
flags: Arc<Flags>,
|
||||
lint_flags: LintFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let watch_flags = lint_flags.watch.as_ref().unwrap();
|
||||
|
||||
file_watcher::watch_func(
|
||||
flags,
|
||||
file_watcher::PrintConfig::new("Lint", !watch_flags.no_clear_screen),
|
||||
move |flags, watcher_communicator, changed_paths| {
|
||||
let lint_flags = lint_flags.clone();
|
||||
watcher_communicator.show_path_changed(changed_paths.clone());
|
||||
Ok(lint_with_watch_inner(
|
||||
flags,
|
||||
lint_flags,
|
||||
watcher_communicator,
|
||||
changed_paths,
|
||||
))
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
struct PathsWithOptions {
|
||||
dir: WorkspaceDirectory,
|
||||
paths: Vec<PathBuf>,
|
||||
|
@ -278,7 +282,7 @@ impl WorkspaceLinter {
|
|||
&mut self,
|
||||
cli_options: &Arc<CliOptions>,
|
||||
lint_options: LintOptions,
|
||||
lint_config: LintConfig,
|
||||
lint_config: DenoLintConfig,
|
||||
member_dir: WorkspaceDirectory,
|
||||
paths: Vec<PathBuf>,
|
||||
maybe_plugins: Option<Vec<String>>,
|
||||
|
@ -305,6 +309,7 @@ impl WorkspaceLinter {
|
|||
} else {
|
||||
Some(lint_options.plugins.clone())
|
||||
};
|
||||
|
||||
let plugin_specifiers = if let Some(plugins) = maybe_plugins {
|
||||
let mut plugin_specifiers = Vec::with_capacity(plugins.len());
|
||||
let cwd = cli_options.initial_cwd();
|
||||
|
@ -338,143 +343,92 @@ impl WorkspaceLinter {
|
|||
maybe_plugin_runner: plugin_runner,
|
||||
}));
|
||||
|
||||
let has_error = self.has_error.clone();
|
||||
let reporter_lock = self.reporter_lock.clone();
|
||||
|
||||
let mut futures = Vec::with_capacity(2);
|
||||
if linter.has_package_rules() {
|
||||
if self.workspace_module_graph.is_none() {
|
||||
let module_graph_creator = self.module_graph_creator.clone();
|
||||
let packages = self.workspace_dir.jsr_packages_for_publish();
|
||||
self.workspace_module_graph = Some(
|
||||
async move {
|
||||
module_graph_creator
|
||||
.create_and_validate_publish_graph(&packages, true)
|
||||
.await
|
||||
.map(Rc::new)
|
||||
.map_err(Rc::new)
|
||||
}
|
||||
.boxed_local()
|
||||
.shared_local(),
|
||||
);
|
||||
}
|
||||
let workspace_module_graph_future =
|
||||
self.workspace_module_graph.as_ref().unwrap().clone();
|
||||
let publish_config = member_dir.maybe_package_config();
|
||||
if let Some(publish_config) = publish_config {
|
||||
let has_error = self.has_error.clone();
|
||||
let reporter_lock = self.reporter_lock.clone();
|
||||
let linter = linter.clone();
|
||||
let path_urls = paths
|
||||
.iter()
|
||||
.filter_map(|p| ModuleSpecifier::from_file_path(p).ok())
|
||||
.collect::<HashSet<_>>();
|
||||
futures.push(
|
||||
async move {
|
||||
let graph = workspace_module_graph_future
|
||||
.await
|
||||
.map_err(|err| anyhow!("{:#}", err))?;
|
||||
let export_urls =
|
||||
publish_config.config_file.resolve_export_value_urls()?;
|
||||
if !export_urls.iter().any(|url| path_urls.contains(url)) {
|
||||
return Ok(()); // entrypoint is not specified, so skip
|
||||
}
|
||||
let diagnostics = linter.lint_package(&graph, &export_urls);
|
||||
if !diagnostics.is_empty() {
|
||||
has_error.raise();
|
||||
let mut reporter = reporter_lock.lock();
|
||||
for diagnostic in &diagnostics {
|
||||
reporter.visit_diagnostic(diagnostic);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
.boxed_local(),
|
||||
);
|
||||
if let Some(fut) = self.run_package_rules(&linter, &member_dir, &paths) {
|
||||
futures.push(fut);
|
||||
}
|
||||
}
|
||||
|
||||
futures.push({
|
||||
let has_error = self.has_error.clone();
|
||||
let reporter_lock = self.reporter_lock.clone();
|
||||
let maybe_incremental_cache = maybe_incremental_cache.clone();
|
||||
let linter = linter.clone();
|
||||
let cli_options = cli_options.clone();
|
||||
async move {
|
||||
run_parallelized(paths, {
|
||||
move |file_path| {
|
||||
let file_text =
|
||||
deno_ast::strip_bom(fs::read_to_string(&file_path)?);
|
||||
let maybe_incremental_cache_ = maybe_incremental_cache.clone();
|
||||
let linter = linter.clone();
|
||||
let cli_options = cli_options.clone();
|
||||
let fut = async move {
|
||||
let operation = move |file_path: PathBuf| {
|
||||
let file_text = deno_ast::strip_bom(fs::read_to_string(&file_path)?);
|
||||
|
||||
// don't bother rechecking this file if it didn't have any diagnostics before
|
||||
// if let Some(incremental_cache) = &maybe_incremental_cache {
|
||||
// if incremental_cache.is_file_same(&file_path, &file_text) {
|
||||
// return Ok(());
|
||||
// }
|
||||
// }
|
||||
// don't bother rechecking this file if it didn't have any diagnostics before
|
||||
// if let Some(incremental_cache) = &maybe_incremental_cache_ {
|
||||
// if incremental_cache.is_file_same(&file_path, &file_text) {
|
||||
// return Ok(());
|
||||
// }
|
||||
// }
|
||||
|
||||
let r = linter.lint_file(
|
||||
&file_path,
|
||||
file_text,
|
||||
cli_options.ext_flag().as_deref(),
|
||||
);
|
||||
let r = match r {
|
||||
Ok((file_source, mut file_diagnostics)) => {
|
||||
let file_source_ = file_source.clone();
|
||||
let source_text_info = file_source.text_info_lazy().clone();
|
||||
let tmp_file_path = file_path.clone();
|
||||
let plugin_diagnostics = if linter.get_plugin_runner().is_some()
|
||||
{
|
||||
tokio_util::create_and_run_current_thread(
|
||||
async move {
|
||||
let plugin_runner = linter.get_plugin_runner().unwrap();
|
||||
#[allow(clippy::await_holding_lock)]
|
||||
let mut plugin_runner = plugin_runner.lock();
|
||||
let serialized_ast =
|
||||
plugins::serialize_ast(file_source_)?;
|
||||
plugins::run_rules_for_ast(
|
||||
&mut plugin_runner,
|
||||
&tmp_file_path,
|
||||
serialized_ast,
|
||||
source_text_info,
|
||||
)
|
||||
.await
|
||||
}
|
||||
.boxed_local(),
|
||||
)?
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
file_diagnostics.extend_from_slice(&plugin_diagnostics);
|
||||
if let Some(incremental_cache) = &maybe_incremental_cache {
|
||||
if file_diagnostics.is_empty() {
|
||||
// update the incremental cache if there were no diagnostics
|
||||
incremental_cache.update_file(
|
||||
&file_path,
|
||||
// ensure the returned text is used here as it may have been modified via --fix
|
||||
file_source.text(),
|
||||
)
|
||||
}
|
||||
let r = linter.lint_file(
|
||||
&file_path,
|
||||
file_text,
|
||||
cli_options.ext_flag().as_deref(),
|
||||
);
|
||||
let r = match r {
|
||||
Ok((file_source, mut file_diagnostics)) => {
|
||||
let file_source_ = file_source.clone();
|
||||
let source_text_info = file_source.text_info_lazy().clone();
|
||||
let tmp_file_path = file_path.clone();
|
||||
let plugin_diagnostics = if linter.get_plugin_runner().is_some() {
|
||||
tokio_util::create_and_run_current_thread(
|
||||
async move {
|
||||
let plugin_runner = linter.get_plugin_runner().unwrap();
|
||||
#[allow(clippy::await_holding_lock)]
|
||||
let mut plugin_runner = plugin_runner.lock();
|
||||
let serialized_ast = plugins::serialize_ast(file_source_)?;
|
||||
plugins::run_rules_for_ast(
|
||||
&mut plugin_runner,
|
||||
&tmp_file_path,
|
||||
serialized_ast,
|
||||
source_text_info,
|
||||
)
|
||||
.await
|
||||
}
|
||||
Ok((file_source, file_diagnostics))
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
.boxed_local(),
|
||||
)?
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
let success = handle_lint_result(
|
||||
&file_path.to_string_lossy(),
|
||||
r,
|
||||
reporter_lock.clone(),
|
||||
);
|
||||
if !success {
|
||||
has_error.raise();
|
||||
file_diagnostics.extend_from_slice(&plugin_diagnostics);
|
||||
if let Some(incremental_cache) = &maybe_incremental_cache_ {
|
||||
if file_diagnostics.is_empty() {
|
||||
// update the incremental cache if there were no diagnostics
|
||||
incremental_cache.update_file(
|
||||
&file_path,
|
||||
// ensure the returned text is used here as it may have been modified via --fix
|
||||
file_source.text(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok((file_source, file_diagnostics))
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
.boxed_local()
|
||||
});
|
||||
Err(err) => Err(err),
|
||||
};
|
||||
|
||||
let success = handle_lint_result(
|
||||
&file_path.to_string_lossy(),
|
||||
r,
|
||||
reporter_lock.clone(),
|
||||
);
|
||||
if !success {
|
||||
has_error.raise();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
run_parallelized(paths, operation).await
|
||||
}
|
||||
.boxed_local();
|
||||
futures.push(fut);
|
||||
|
||||
if lint_options.fix {
|
||||
// run sequentially when using `--fix` to lower the chances of weird
|
||||
|
@ -494,6 +448,63 @@ impl WorkspaceLinter {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn run_package_rules(
|
||||
&mut self,
|
||||
linter: &Arc<CliLinter>,
|
||||
member_dir: &WorkspaceDirectory,
|
||||
paths: &[PathBuf],
|
||||
) -> Option<LocalBoxFuture<Result<(), AnyError>>> {
|
||||
if self.workspace_module_graph.is_none() {
|
||||
let module_graph_creator = self.module_graph_creator.clone();
|
||||
let packages = self.workspace_dir.jsr_packages_for_publish();
|
||||
self.workspace_module_graph = Some(
|
||||
async move {
|
||||
module_graph_creator
|
||||
.create_and_validate_publish_graph(&packages, true)
|
||||
.await
|
||||
.map(Rc::new)
|
||||
.map_err(Rc::new)
|
||||
}
|
||||
.boxed_local()
|
||||
.shared_local(),
|
||||
);
|
||||
}
|
||||
|
||||
let workspace_module_graph_future =
|
||||
self.workspace_module_graph.as_ref().unwrap().clone();
|
||||
let maybe_publish_config = member_dir.maybe_package_config();
|
||||
let publish_config = maybe_publish_config?;
|
||||
|
||||
let has_error = self.has_error.clone();
|
||||
let reporter_lock = self.reporter_lock.clone();
|
||||
let linter = linter.clone();
|
||||
let path_urls = paths
|
||||
.iter()
|
||||
.filter_map(|p| ModuleSpecifier::from_file_path(p).ok())
|
||||
.collect::<HashSet<_>>();
|
||||
let fut = async move {
|
||||
let graph = workspace_module_graph_future
|
||||
.await
|
||||
.map_err(|err| anyhow!("{:#}", err))?;
|
||||
let export_urls =
|
||||
publish_config.config_file.resolve_export_value_urls()?;
|
||||
if !export_urls.iter().any(|url| path_urls.contains(url)) {
|
||||
return Ok(()); // entrypoint is not specified, so skip
|
||||
}
|
||||
let diagnostics = linter.lint_package(&graph, &export_urls);
|
||||
if !diagnostics.is_empty() {
|
||||
has_error.raise();
|
||||
let mut reporter = reporter_lock.lock();
|
||||
for diagnostic in &diagnostics {
|
||||
reporter.visit_diagnostic(diagnostic);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
.boxed_local();
|
||||
Some(fut)
|
||||
}
|
||||
|
||||
pub fn finish(self) -> bool {
|
||||
debug!("Found {} files", self.file_count);
|
||||
self.reporter_lock.lock().close(self.file_count);
|
||||
|
@ -569,10 +580,27 @@ pub fn print_rules_list(json: bool, maybe_rules_tags: Option<Vec<String>>) {
|
|||
/// Treats input as TypeScript.
|
||||
/// Compatible with `--json` flag.
|
||||
fn lint_stdin(
|
||||
file_path: &Path,
|
||||
configured_rules: ConfiguredRules,
|
||||
deno_lint_config: LintConfig,
|
||||
) -> Result<(ParsedSource, Vec<LintDiagnostic>), AnyError> {
|
||||
cli_options: &Arc<CliOptions>,
|
||||
lint_rule_provider: LintRuleProvider,
|
||||
workspace_lint_options: WorkspaceLintOptions,
|
||||
lint_flags: LintFlags,
|
||||
deno_lint_config: DenoLintConfig,
|
||||
) -> Result<bool, AnyError> {
|
||||
let start_dir = &cli_options.start_dir;
|
||||
let reporter_lock = Arc::new(Mutex::new(create_reporter(
|
||||
workspace_lint_options.reporter_kind,
|
||||
)));
|
||||
let lint_config = start_dir
|
||||
.to_lint_config(FilePatterns::new_with_base(start_dir.dir_path()))?;
|
||||
let lint_options = LintOptions::resolve(lint_config, &lint_flags);
|
||||
let configured_rules = lint_rule_provider.resolve_lint_rules_err_empty(
|
||||
lint_options.rules,
|
||||
start_dir.maybe_deno_json().map(|c| c.as_ref()),
|
||||
)?;
|
||||
let mut file_path = cli_options.initial_cwd().join(STDIN_FILE_NAME);
|
||||
if let Some(ext) = cli_options.ext_flag() {
|
||||
file_path.set_extension(ext);
|
||||
}
|
||||
let mut source_code = String::new();
|
||||
if stdin().read_to_string(&mut source_code).is_err() {
|
||||
return Err(generic_error("Failed to read from stdin"));
|
||||
|
@ -585,9 +613,14 @@ fn lint_stdin(
|
|||
maybe_plugin_runner: None,
|
||||
});
|
||||
|
||||
linter
|
||||
.lint_file(file_path, deno_ast::strip_bom(source_code), None)
|
||||
.map_err(AnyError::from)
|
||||
let r = linter
|
||||
.lint_file(&file_path, deno_ast::strip_bom(source_code), None)
|
||||
.map_err(AnyError::from);
|
||||
|
||||
let success =
|
||||
handle_lint_result(&file_path.to_string_lossy(), r, reporter_lock.clone());
|
||||
reporter_lock.lock().close(1);
|
||||
Ok(success)
|
||||
}
|
||||
|
||||
fn handle_lint_result(
|
||||
|
@ -632,3 +665,68 @@ struct LintError {
|
|||
file_path: String,
|
||||
message: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde::Deserialize;
|
||||
use test_util as util;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct RulesSchema {
|
||||
#[serde(rename = "$schema")]
|
||||
schema: String,
|
||||
|
||||
#[serde(rename = "enum")]
|
||||
rules: Vec<String>,
|
||||
}
|
||||
|
||||
fn get_all_rules() -> Vec<String> {
|
||||
let rule_provider = LintRuleProvider::new(None, None);
|
||||
let configured_rules =
|
||||
rule_provider.resolve_lint_rules(Default::default(), None);
|
||||
let mut all_rules = configured_rules
|
||||
.all_rule_codes
|
||||
.into_iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
all_rules.sort();
|
||||
|
||||
all_rules
|
||||
}
|
||||
|
||||
// TODO(bartlomieju): do the same for tags, once https://github.com/denoland/deno/pull/27162 lands
|
||||
#[test]
|
||||
fn all_lint_rules_are_listed_in_schema_file() {
|
||||
let all_rules = get_all_rules();
|
||||
|
||||
let rules_schema_path =
|
||||
util::root_path().join("cli/schemas/lint-rules.v1.json");
|
||||
let rules_schema_file =
|
||||
std::fs::read_to_string(&rules_schema_path).unwrap();
|
||||
|
||||
let schema: RulesSchema = serde_json::from_str(&rules_schema_file).unwrap();
|
||||
|
||||
const UPDATE_ENV_VAR_NAME: &str = "UPDATE_EXPECTED";
|
||||
|
||||
if std::env::var(UPDATE_ENV_VAR_NAME).ok().is_none() {
|
||||
assert_eq!(
|
||||
schema.rules, all_rules,
|
||||
"Lint rules schema file not up to date. Run again with {}=1 to update the expected output",
|
||||
UPDATE_ENV_VAR_NAME
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
std::fs::write(
|
||||
&rules_schema_path,
|
||||
serde_json::to_string_pretty(&RulesSchema {
|
||||
schema: schema.schema,
|
||||
rules: all_rules,
|
||||
})
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::path::Path;
|
|||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_cache_dir::file_fetcher::CacheSetting;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
|
@ -23,12 +24,11 @@ use jsonc_parser::cst::CstRootNode;
|
|||
use jsonc_parser::json;
|
||||
|
||||
use crate::args::AddFlags;
|
||||
use crate::args::CacheSetting;
|
||||
use crate::args::CliOptions;
|
||||
use crate::args::Flags;
|
||||
use crate::args::RemoveFlags;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::jsr::JsrFetchResolver;
|
||||
use crate::npm::NpmFetchResolver;
|
||||
|
||||
|
@ -411,18 +411,18 @@ pub async fn add(
|
|||
|
||||
let http_client = cli_factory.http_client_provider();
|
||||
let deps_http_cache = cli_factory.global_http_cache()?;
|
||||
let mut deps_file_fetcher = FileFetcher::new(
|
||||
let deps_file_fetcher = CliFileFetcher::new(
|
||||
deps_http_cache.clone(),
|
||||
CacheSetting::ReloadAll,
|
||||
true,
|
||||
http_client.clone(),
|
||||
Default::default(),
|
||||
None,
|
||||
true,
|
||||
CacheSetting::ReloadAll,
|
||||
log::Level::Trace,
|
||||
);
|
||||
|
||||
let npmrc = cli_factory.cli_options().unwrap().npmrc();
|
||||
|
||||
deps_file_fetcher.set_download_log_level(log::Level::Trace);
|
||||
let deps_file_fetcher = Arc::new(deps_file_fetcher);
|
||||
let jsr_resolver = Arc::new(JsrFetchResolver::new(deps_file_fetcher.clone()));
|
||||
let npm_resolver =
|
||||
|
@ -432,9 +432,8 @@ pub async fn add(
|
|||
let mut package_reqs = Vec::with_capacity(add_flags.packages.len());
|
||||
|
||||
for entry_text in add_flags.packages.iter() {
|
||||
let req = AddRmPackageReq::parse(entry_text).with_context(|| {
|
||||
format!("Failed to parse package required: {}", entry_text)
|
||||
})?;
|
||||
let req = AddRmPackageReq::parse(entry_text)
|
||||
.with_context(|| format!("Failed to parse package: {}", entry_text))?;
|
||||
|
||||
match req {
|
||||
Ok(add_req) => package_reqs.push(add_req),
|
||||
|
@ -805,9 +804,8 @@ pub async fn remove(
|
|||
let mut removed_packages = vec![];
|
||||
|
||||
for package in &remove_flags.packages {
|
||||
let req = AddRmPackageReq::parse(package).with_context(|| {
|
||||
format!("Failed to parse package required: {}", package)
|
||||
})?;
|
||||
let req = AddRmPackageReq::parse(package)
|
||||
.with_context(|| format!("Failed to parse package: {}", package))?;
|
||||
let mut parsed_pkg_name = None;
|
||||
for config in configs.iter_mut().flatten() {
|
||||
match &req {
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::factory::CliFactory;
|
||||
use crate::graph_container::ModuleGraphContainer;
|
||||
use crate::graph_container::ModuleGraphUpdatePermit;
|
||||
use crate::graph_util::CreateGraphOptions;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::stream::FuturesUnordered;
|
||||
use deno_core::futures::StreamExt;
|
||||
|
@ -17,18 +19,16 @@ pub async fn cache_top_level_deps(
|
|||
) -> Result<(), AnyError> {
|
||||
let npm_resolver = factory.npm_resolver().await?;
|
||||
let cli_options = factory.cli_options()?;
|
||||
let root_permissions = factory.root_permissions_container()?;
|
||||
if let Some(npm_resolver) = npm_resolver.as_managed() {
|
||||
if !npm_resolver.ensure_top_level_package_json_install().await? {
|
||||
if let Some(lockfile) = cli_options.maybe_lockfile() {
|
||||
lockfile.error_if_changed()?;
|
||||
}
|
||||
|
||||
npm_resolver.cache_packages().await?;
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
if let Some(lockfile) = cli_options.maybe_lockfile() {
|
||||
lockfile.error_if_changed()?;
|
||||
}
|
||||
}
|
||||
// cache as many entries in the import map as we can
|
||||
let resolver = factory.workspace_resolver().await?;
|
||||
|
||||
let mut maybe_graph_error = Ok(());
|
||||
if let Some(import_map) = resolver.maybe_import_map() {
|
||||
let jsr_resolver = if let Some(resolver) = jsr_resolver {
|
||||
resolver
|
||||
|
@ -37,6 +37,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 +77,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,25 +119,31 @@ 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();
|
||||
factory
|
||||
.module_load_preparer()
|
||||
.await?
|
||||
.prepare_module_load(
|
||||
drop(info_futures);
|
||||
|
||||
let graph_builder = factory.module_graph_builder().await?;
|
||||
graph_builder
|
||||
.build_graph_with_npm_resolution(
|
||||
graph,
|
||||
&roots,
|
||||
false,
|
||||
deno_config::deno_json::TsTypeLib::DenoWorker,
|
||||
root_permissions.clone(),
|
||||
None,
|
||||
CreateGraphOptions {
|
||||
loader: None,
|
||||
graph_kind: graph.graph_kind(),
|
||||
is_dynamic: false,
|
||||
roots: roots.clone(),
|
||||
npm_caching: crate::graph_util::NpmCachingStrategy::Manual,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
maybe_graph_error = graph_builder.graph_roots_valid(graph, &roots);
|
||||
}
|
||||
|
||||
if let Some(npm_resolver) = npm_resolver.as_managed() {
|
||||
npm_resolver
|
||||
.cache_packages(crate::npm::PackageCaching::All)
|
||||
.await?;
|
||||
}
|
||||
|
||||
maybe_graph_error?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
|
@ -11,6 +11,7 @@ use deno_config::deno_json::ConfigFileRc;
|
|||
use deno_config::workspace::Workspace;
|
||||
use deno_config::workspace::WorkspaceDirectory;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::future::try_join;
|
||||
use deno_core::futures::stream::FuturesOrdered;
|
||||
|
@ -18,7 +19,6 @@ 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::PackageJsonDepsMap;
|
||||
use deno_package_json::PackageJsonRc;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
|
@ -27,6 +27,7 @@ use deno_semver::npm::NpmPackageReqReference;
|
|||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use deno_semver::package::PackageReqReference;
|
||||
use deno_semver::Version;
|
||||
use deno_semver::VersionReq;
|
||||
use import_map::ImportMap;
|
||||
use import_map::ImportMapWithDiagnostics;
|
||||
|
@ -41,13 +42,14 @@ use crate::jsr::JsrFetchResolver;
|
|||
use crate::module_loader::ModuleLoadPreparer;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::NpmFetchResolver;
|
||||
use crate::util::sync::AtomicFlag;
|
||||
|
||||
use super::ConfigUpdater;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ImportMapKind {
|
||||
Inline,
|
||||
Outline,
|
||||
Outline(PathBuf),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -63,9 +65,12 @@ impl DepLocation {
|
|||
|
||||
pub fn file_path(&self) -> Cow<std::path::Path> {
|
||||
match self {
|
||||
DepLocation::DenoJson(arc, _, _) => {
|
||||
Cow::Owned(arc.specifier.to_file_path().unwrap())
|
||||
}
|
||||
DepLocation::DenoJson(arc, _, kind) => match kind {
|
||||
ImportMapKind::Inline => {
|
||||
Cow::Owned(arc.specifier.to_file_path().unwrap())
|
||||
}
|
||||
ImportMapKind::Outline(path) => Cow::Borrowed(path.as_path()),
|
||||
},
|
||||
DepLocation::PackageJson(arc, _) => Cow::Borrowed(arc.path.as_ref()),
|
||||
}
|
||||
}
|
||||
|
@ -239,22 +244,30 @@ fn to_import_map_value_from_imports(
|
|||
fn deno_json_import_map(
|
||||
deno_json: &ConfigFile,
|
||||
) -> Result<Option<(ImportMapWithDiagnostics, ImportMapKind)>, AnyError> {
|
||||
let (value, kind) =
|
||||
if deno_json.json.imports.is_some() || deno_json.json.scopes.is_some() {
|
||||
(
|
||||
to_import_map_value_from_imports(deno_json),
|
||||
ImportMapKind::Inline,
|
||||
)
|
||||
} else {
|
||||
match deno_json.to_import_map_path()? {
|
||||
Some(path) => {
|
||||
let text = std::fs::read_to_string(&path)?;
|
||||
let value = serde_json::from_str(&text)?;
|
||||
(value, ImportMapKind::Outline)
|
||||
}
|
||||
None => return Ok(None),
|
||||
let (value, kind) = if deno_json.json.imports.is_some()
|
||||
|| deno_json.json.scopes.is_some()
|
||||
{
|
||||
(
|
||||
to_import_map_value_from_imports(deno_json),
|
||||
ImportMapKind::Inline,
|
||||
)
|
||||
} else {
|
||||
match deno_json.to_import_map_path()? {
|
||||
Some(path) => {
|
||||
let err_context = || {
|
||||
format!(
|
||||
"loading import map at '{}' (from \"importMap\" field in '{}')",
|
||||
path.display(),
|
||||
deno_json.specifier
|
||||
)
|
||||
};
|
||||
let text = std::fs::read_to_string(&path).with_context(err_context)?;
|
||||
let value = serde_json::from_str(&text).with_context(err_context)?;
|
||||
(value, ImportMapKind::Outline(path))
|
||||
}
|
||||
};
|
||||
None => return Ok(None),
|
||||
}
|
||||
};
|
||||
|
||||
import_map::parse_from_value(deno_json.specifier.clone(), value)
|
||||
.map_err(Into::into)
|
||||
|
@ -304,7 +317,7 @@ fn add_deps_from_deno_json(
|
|||
location: DepLocation::DenoJson(
|
||||
deno_json.clone(),
|
||||
key_path,
|
||||
import_map_kind,
|
||||
import_map_kind.clone(),
|
||||
),
|
||||
kind,
|
||||
req,
|
||||
|
@ -435,7 +448,7 @@ pub struct DepManager {
|
|||
|
||||
pending_changes: Vec<Change>,
|
||||
|
||||
dependencies_resolved: AtomicBool,
|
||||
dependencies_resolved: AtomicFlag,
|
||||
module_load_preparer: Arc<ModuleLoadPreparer>,
|
||||
// TODO(nathanwhit): probably shouldn't be pub
|
||||
pub(crate) jsr_fetch_resolver: Arc<JsrFetchResolver>,
|
||||
|
@ -477,7 +490,7 @@ impl DepManager {
|
|||
resolved_versions: Vec::new(),
|
||||
latest_versions: Vec::new(),
|
||||
jsr_fetch_resolver,
|
||||
dependencies_resolved: AtomicBool::new(false),
|
||||
dependencies_resolved: AtomicFlag::lowered(),
|
||||
module_load_preparer,
|
||||
npm_fetch_resolver,
|
||||
npm_resolver,
|
||||
|
@ -518,10 +531,7 @@ impl DepManager {
|
|||
}
|
||||
|
||||
async fn run_dependency_resolution(&self) -> Result<(), AnyError> {
|
||||
if self
|
||||
.dependencies_resolved
|
||||
.load(std::sync::atomic::Ordering::Relaxed)
|
||||
{
|
||||
if self.dependencies_resolved.is_raised() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -533,19 +543,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();
|
||||
|
@ -555,9 +554,7 @@ impl DepManager {
|
|||
}
|
||||
DepKind::Jsr => graph.packages.mappings().contains_key(&dep.req),
|
||||
}) {
|
||||
self
|
||||
.dependencies_resolved
|
||||
.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
self.dependencies_resolved.raise();
|
||||
graph_permit.commit();
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -612,6 +609,7 @@ impl DepManager {
|
|||
)
|
||||
.await?;
|
||||
|
||||
self.dependencies_resolved.raise();
|
||||
graph_permit.commit();
|
||||
|
||||
Ok(())
|
||||
|
@ -654,10 +652,6 @@ impl DepManager {
|
|||
if self.latest_versions.len() == self.deps.len() {
|
||||
return Ok(self.latest_versions.clone());
|
||||
}
|
||||
let latest_tag_req = deno_semver::VersionReq::from_raw_text_and_inner(
|
||||
"latest".into(),
|
||||
deno_semver::RangeSetOrTag::Tag("latest".into()),
|
||||
);
|
||||
let mut latest_versions = Vec::with_capacity(self.deps.len());
|
||||
|
||||
let npm_sema = Semaphore::new(32);
|
||||
|
@ -669,14 +663,25 @@ impl DepManager {
|
|||
DepKind::Npm => futs.push_back(
|
||||
async {
|
||||
let semver_req = &dep.req;
|
||||
let latest_req = PackageReq {
|
||||
name: dep.req.name.clone(),
|
||||
version_req: latest_tag_req.clone(),
|
||||
};
|
||||
let _permit = npm_sema.acquire().await;
|
||||
let semver_compatible =
|
||||
self.npm_fetch_resolver.req_to_nv(semver_req).await;
|
||||
let latest = self.npm_fetch_resolver.req_to_nv(&latest_req).await;
|
||||
let info =
|
||||
self.npm_fetch_resolver.package_info(&semver_req.name).await;
|
||||
let latest = info
|
||||
.and_then(|info| {
|
||||
let latest_tag = info.dist_tags.get("latest")?;
|
||||
let lower_bound = &semver_compatible.as_ref()?.version;
|
||||
if latest_tag > lower_bound {
|
||||
Some(latest_tag.clone())
|
||||
} else {
|
||||
latest_version(Some(latest_tag), info.versions.keys())
|
||||
}
|
||||
})
|
||||
.map(|version| PackageNv {
|
||||
name: semver_req.name.clone(),
|
||||
version,
|
||||
});
|
||||
PackageLatestVersion {
|
||||
latest,
|
||||
semver_compatible,
|
||||
|
@ -687,14 +692,29 @@ impl DepManager {
|
|||
DepKind::Jsr => futs.push_back(
|
||||
async {
|
||||
let semver_req = &dep.req;
|
||||
let latest_req = PackageReq {
|
||||
name: dep.req.name.clone(),
|
||||
version_req: deno_semver::WILDCARD_VERSION_REQ.clone(),
|
||||
};
|
||||
let _permit = jsr_sema.acquire().await;
|
||||
let semver_compatible =
|
||||
self.jsr_fetch_resolver.req_to_nv(semver_req).await;
|
||||
let latest = self.jsr_fetch_resolver.req_to_nv(&latest_req).await;
|
||||
let info =
|
||||
self.jsr_fetch_resolver.package_info(&semver_req.name).await;
|
||||
let latest = info
|
||||
.and_then(|info| {
|
||||
let lower_bound = &semver_compatible.as_ref()?.version;
|
||||
latest_version(
|
||||
Some(lower_bound),
|
||||
info.versions.iter().filter_map(|(version, version_info)| {
|
||||
if !version_info.yanked {
|
||||
Some(version)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
)
|
||||
})
|
||||
.map(|version| PackageNv {
|
||||
name: semver_req.name.clone(),
|
||||
version,
|
||||
});
|
||||
PackageLatestVersion {
|
||||
latest,
|
||||
semver_compatible,
|
||||
|
@ -759,11 +779,7 @@ impl DepManager {
|
|||
let dep = &mut self.deps[dep_id.0];
|
||||
dep.req.version_req = version_req.clone();
|
||||
match &dep.location {
|
||||
DepLocation::DenoJson(arc, key_path, import_map_kind) => {
|
||||
if matches!(import_map_kind, ImportMapKind::Outline) {
|
||||
// not supported
|
||||
continue;
|
||||
}
|
||||
DepLocation::DenoJson(arc, key_path, _) => {
|
||||
let updater =
|
||||
get_or_create_updater(&mut config_updaters, &dep.location)?;
|
||||
|
||||
|
@ -896,3 +912,18 @@ fn parse_req_reference(
|
|||
DepKind::Jsr => JsrPackageReqReference::from_str(input)?.into_inner(),
|
||||
})
|
||||
}
|
||||
|
||||
fn latest_version<'a>(
|
||||
start: Option<&Version>,
|
||||
versions: impl IntoIterator<Item = &'a Version>,
|
||||
) -> Option<Version> {
|
||||
let mut best = start;
|
||||
for version in versions {
|
||||
match best {
|
||||
Some(best_version) if version > best_version => best = Some(version),
|
||||
None => best = Some(version),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
best.cloned()
|
||||
}
|
||||
|
|
|
@ -3,18 +3,19 @@
|
|||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_cache_dir::file_fetcher::CacheSetting;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use deno_semver::VersionReq;
|
||||
use deno_terminal::colors;
|
||||
|
||||
use crate::args::CacheSetting;
|
||||
use crate::args::CliOptions;
|
||||
use crate::args::Flags;
|
||||
use crate::args::OutdatedFlags;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::jsr::JsrFetchResolver;
|
||||
use crate::npm::NpmFetchResolver;
|
||||
use crate::tools::registry::pm::deps::DepKind;
|
||||
|
@ -100,6 +101,23 @@ fn print_outdated_table(packages: &[OutdatedPackage]) {
|
|||
println!("└{package_fill}┴{current_fill}┴{update_fill}┴{latest_fill}┘",);
|
||||
}
|
||||
|
||||
fn print_suggestion(compatible: bool) {
|
||||
log::info!("");
|
||||
let (cmd, txt) = if compatible {
|
||||
("", "compatible")
|
||||
} else {
|
||||
(" --latest", "available")
|
||||
};
|
||||
log::info!(
|
||||
"{}",
|
||||
color_print::cformat!(
|
||||
"<p(245)>Run</> <u>deno outdated --update{}</> <p(245)>to update to the latest {} versions,</>\n<p(245)>or</> <u>deno outdated --help</> <p(245)>for more information.</>",
|
||||
cmd,
|
||||
txt,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
fn print_outdated(
|
||||
deps: &mut DepManager,
|
||||
compatible: bool,
|
||||
|
@ -148,6 +166,7 @@ fn print_outdated(
|
|||
if !outdated.is_empty() {
|
||||
outdated.sort();
|
||||
print_outdated_table(&outdated);
|
||||
print_suggestion(compatible);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -162,15 +181,15 @@ pub async fn outdated(
|
|||
let workspace = cli_options.workspace();
|
||||
let http_client = factory.http_client_provider();
|
||||
let deps_http_cache = factory.global_http_cache()?;
|
||||
let mut file_fetcher = FileFetcher::new(
|
||||
let file_fetcher = CliFileFetcher::new(
|
||||
deps_http_cache.clone(),
|
||||
CacheSetting::RespectHeaders,
|
||||
true,
|
||||
http_client.clone(),
|
||||
Default::default(),
|
||||
None,
|
||||
true,
|
||||
CacheSetting::RespectHeaders,
|
||||
log::Level::Trace,
|
||||
);
|
||||
file_fetcher.set_download_log_level(log::Level::Trace);
|
||||
let file_fetcher = Arc::new(file_fetcher);
|
||||
let npm_fetch_resolver = Arc::new(NpmFetchResolver::new(
|
||||
file_fetcher.clone(),
|
||||
|
@ -179,6 +198,15 @@ pub async fn outdated(
|
|||
let jsr_fetch_resolver =
|
||||
Arc::new(JsrFetchResolver::new(file_fetcher.clone()));
|
||||
|
||||
if !cli_options.start_dir.has_deno_json()
|
||||
&& !cli_options.start_dir.has_pkg_json()
|
||||
{
|
||||
bail!(
|
||||
"No deno.json or package.json in \"{}\".",
|
||||
cli_options.initial_cwd().display(),
|
||||
);
|
||||
}
|
||||
|
||||
let args = dep_manager_args(
|
||||
&factory,
|
||||
cli_options,
|
||||
|
|
|
@ -11,7 +11,8 @@ use crate::args::ReplFlags;
|
|||
use crate::cdp;
|
||||
use crate::colors;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::file_fetcher::TextDecodedFile;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::StreamExt;
|
||||
use deno_core::serde_json;
|
||||
|
@ -143,7 +144,7 @@ async fn read_line_and_poll(
|
|||
|
||||
async fn read_eval_file(
|
||||
cli_options: &CliOptions,
|
||||
file_fetcher: &FileFetcher,
|
||||
file_fetcher: &CliFileFetcher,
|
||||
eval_file: &str,
|
||||
) -> Result<Arc<str>, AnyError> {
|
||||
let specifier =
|
||||
|
@ -151,7 +152,7 @@ async fn read_eval_file(
|
|||
|
||||
let file = file_fetcher.fetch_bypass_permissions(&specifier).await?;
|
||||
|
||||
Ok(file.into_text_decoded()?.source)
|
||||
Ok(TextDecodedFile::decode(file)?.source)
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stdout)]
|
||||
|
|
|
@ -727,7 +727,9 @@ impl ReplSession {
|
|||
let has_node_specifier =
|
||||
resolved_imports.iter().any(|url| url.scheme() == "node");
|
||||
if !npm_imports.is_empty() || has_node_specifier {
|
||||
npm_resolver.add_package_reqs(&npm_imports).await?;
|
||||
npm_resolver
|
||||
.add_and_cache_package_reqs(&npm_imports)
|
||||
.await?;
|
||||
|
||||
// prevent messages in the repl about @types/node not being cached
|
||||
if has_node_specifier {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use std::io::Read;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_cache_dir::file_fetcher::File;
|
||||
use deno_config::deno_json::NodeModulesDirMode;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_runtime::WorkerExecutionMode;
|
||||
|
@ -11,7 +12,6 @@ use crate::args::EvalFlags;
|
|||
use crate::args::Flags;
|
||||
use crate::args::WatchFlagsWithPaths;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::file_fetcher::File;
|
||||
use crate::util;
|
||||
use crate::util::file_watcher::WatcherRestartMode;
|
||||
|
||||
|
@ -97,7 +97,7 @@ pub async fn run_from_stdin(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
|||
// Save a fake file into file fetcher cache
|
||||
// to allow module access by TS compiler
|
||||
file_fetcher.insert_memory_files(File {
|
||||
specifier: main_module.clone(),
|
||||
url: main_module.clone(),
|
||||
maybe_headers: None,
|
||||
source: source.into(),
|
||||
});
|
||||
|
@ -184,7 +184,7 @@ pub async fn eval_command(
|
|||
// Save a fake file into file fetcher cache
|
||||
// to allow module access by TS compiler.
|
||||
file_fetcher.insert_memory_files(File {
|
||||
specifier: main_module.clone(),
|
||||
url: main_module.clone(),
|
||||
maybe_headers: None,
|
||||
source: source_code.into_bytes().into(),
|
||||
});
|
||||
|
@ -198,13 +198,23 @@ pub async fn eval_command(
|
|||
}
|
||||
|
||||
pub async fn maybe_npm_install(factory: &CliFactory) -> Result<(), AnyError> {
|
||||
let cli_options = factory.cli_options()?;
|
||||
// ensure an "npm install" is done if the user has explicitly
|
||||
// opted into using a managed node_modules directory
|
||||
if factory.cli_options()?.node_modules_dir()?
|
||||
== Some(NodeModulesDirMode::Auto)
|
||||
{
|
||||
if cli_options.node_modules_dir()? == Some(NodeModulesDirMode::Auto) {
|
||||
if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() {
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
let already_done =
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
if !already_done
|
||||
&& matches!(
|
||||
cli_options.default_npm_caching_strategy(),
|
||||
crate::graph_util::NpmCachingStrategy::Eager
|
||||
)
|
||||
{
|
||||
npm_resolver
|
||||
.cache_packages(crate::npm::PackageCaching::All)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -78,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_name_filter = arg_to_task_name_filter(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;
|
||||
}
|
||||
|
||||
|
@ -151,12 +137,20 @@ pub async fn execute_script(
|
|||
|
||||
// Match tasks in deno.json
|
||||
for name in tasks_config.task_names() {
|
||||
if task_regex.is_match(name) && !visited.contains(name) {
|
||||
let matches_filter = match &task_name_filter {
|
||||
TaskNameFilter::Exact(n) => *n == name,
|
||||
TaskNameFilter::Regex(re) => re.is_match(name),
|
||||
};
|
||||
if matches_filter && !visited.contains(name) {
|
||||
matched.insert(name.to_string());
|
||||
visit_task(&tasks_config, &mut visited, name);
|
||||
}
|
||||
}
|
||||
|
||||
if matched.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
packages_task_info.push(PackageTaskInfo {
|
||||
matched_tasks: matched
|
||||
.iter()
|
||||
|
@ -198,6 +192,7 @@ pub async fn execute_script(
|
|||
&mut std::io::stdout(),
|
||||
&cli_options.start_dir,
|
||||
&tasks_config,
|
||||
None,
|
||||
)?;
|
||||
return Ok(0);
|
||||
};
|
||||
|
@ -315,6 +310,7 @@ impl<'a> TaskRunner<'a> {
|
|||
&mut std::io::stderr(),
|
||||
&self.cli_options.start_dir,
|
||||
tasks_config,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -452,6 +448,13 @@ impl<'a> TaskRunner<'a> {
|
|||
kill_signal: KillSignal,
|
||||
argv: &'a [String],
|
||||
) -> Result<i32, deno_core::anyhow::Error> {
|
||||
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
npm_resolver
|
||||
.cache_packages(crate::npm::PackageCaching::All)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let cwd = match &self.task_flags.cwd {
|
||||
Some(path) => canonicalize_path(&PathBuf::from(path))
|
||||
.context("failed canonicalizing --cwd")?,
|
||||
|
@ -462,6 +465,7 @@ impl<'a> TaskRunner<'a> {
|
|||
self.npm_resolver,
|
||||
self.node_resolver,
|
||||
)?;
|
||||
|
||||
self
|
||||
.run_single(RunSingleOptions {
|
||||
task_name,
|
||||
|
@ -485,6 +489,9 @@ impl<'a> TaskRunner<'a> {
|
|||
// ensure the npm packages are installed if using a managed resolver
|
||||
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
npm_resolver
|
||||
.cache_packages(crate::npm::PackageCaching::All)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let cwd = match &self.task_flags.cwd {
|
||||
|
@ -504,6 +511,7 @@ impl<'a> TaskRunner<'a> {
|
|||
self.npm_resolver,
|
||||
self.node_resolver,
|
||||
)?;
|
||||
|
||||
for task_name in &task_names {
|
||||
if let Some(script) = scripts.get(task_name) {
|
||||
let exit_code = self
|
||||
|
@ -675,6 +683,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!(
|
||||
"{} {} {}",
|
||||
|
@ -684,12 +718,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() {
|
||||
|
@ -818,3 +910,41 @@ fn strip_ansi_codes_and_escape_control_chars(s: &str) -> String {
|
|||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn arg_to_task_name_filter(input: &str) -> Result<TaskNameFilter, AnyError> {
|
||||
if !input.contains("*") {
|
||||
return Ok(TaskNameFilter::Exact(input));
|
||||
}
|
||||
|
||||
let mut regex_str = regex::escape(input);
|
||||
regex_str = regex_str.replace("\\*", ".*");
|
||||
let re = Regex::new(®ex_str)?;
|
||||
Ok(TaskNameFilter::Regex(re))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum TaskNameFilter<'s> {
|
||||
Exact(&'s str),
|
||||
Regex(regex::Regex),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_arg_to_task_name_filter() {
|
||||
assert!(matches!(
|
||||
arg_to_task_name_filter("test").unwrap(),
|
||||
TaskNameFilter::Exact("test")
|
||||
));
|
||||
assert!(matches!(
|
||||
arg_to_task_name_filter("test-").unwrap(),
|
||||
TaskNameFilter::Exact("test-")
|
||||
));
|
||||
assert!(matches!(
|
||||
arg_to_task_name_filter("test*").unwrap(),
|
||||
TaskNameFilter::Regex(_)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,7 @@ use crate::args::TestReporterConfig;
|
|||
use crate::colors;
|
||||
use crate::display;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::file_fetcher::File;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::graph_util::has_graph_root_local_dependent_changed;
|
||||
use crate::ops;
|
||||
use crate::util::extract::extract_doc_tests;
|
||||
|
@ -21,6 +20,7 @@ use crate::worker::CliMainWorkerFactory;
|
|||
use crate::worker::CoverageCollector;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_cache_dir::file_fetcher::File;
|
||||
use deno_config::glob::FilePatterns;
|
||||
use deno_config::glob::WalkEntry;
|
||||
use deno_core::anyhow;
|
||||
|
@ -1514,7 +1514,7 @@ fn collect_specifiers_with_test_mode(
|
|||
/// as well.
|
||||
async fn fetch_specifiers_with_test_mode(
|
||||
cli_options: &CliOptions,
|
||||
file_fetcher: &FileFetcher,
|
||||
file_fetcher: &CliFileFetcher,
|
||||
member_patterns: impl Iterator<Item = FilePatterns>,
|
||||
doc: &bool,
|
||||
) -> Result<Vec<(ModuleSpecifier, TestMode)>, AnyError> {
|
||||
|
@ -1716,7 +1716,11 @@ pub async fn run_tests_with_watch(
|
|||
&cli_options.permissions_options(),
|
||||
)?;
|
||||
let graph = module_graph_creator
|
||||
.create_graph(graph_kind, test_modules)
|
||||
.create_graph(
|
||||
graph_kind,
|
||||
test_modules,
|
||||
crate::graph_util::NpmCachingStrategy::Eager,
|
||||
)
|
||||
.await?;
|
||||
module_graph_creator.graph_valid(&graph)?;
|
||||
let test_modules = &graph.roots;
|
||||
|
@ -1818,7 +1822,7 @@ pub async fn run_tests_with_watch(
|
|||
/// Extracts doc tests from files specified by the given specifiers.
|
||||
async fn get_doc_tests(
|
||||
specifiers_with_mode: &[(Url, TestMode)],
|
||||
file_fetcher: &FileFetcher,
|
||||
file_fetcher: &CliFileFetcher,
|
||||
) -> Result<Vec<File>, AnyError> {
|
||||
let specifiers_needing_extraction = specifiers_with_mode
|
||||
.iter()
|
||||
|
@ -1843,7 +1847,7 @@ fn get_target_specifiers(
|
|||
specifiers_with_mode
|
||||
.into_iter()
|
||||
.filter_map(|(s, mode)| mode.needs_test_run().then_some(s))
|
||||
.chain(doc_tests.iter().map(|d| d.specifier.clone()))
|
||||
.chain(doc_tests.iter().map(|d| d.url.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
|
|
@ -133,6 +133,12 @@ pub struct Diagnostic {
|
|||
pub file_name: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub related_information: Option<Vec<Diagnostic>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub reports_deprecated: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub reports_unnecessary: Option<bool>,
|
||||
#[serde(flatten)]
|
||||
pub other: deno_core::serde_json::Map<String, deno_core::serde_json::Value>,
|
||||
}
|
||||
|
||||
impl Diagnostic {
|
||||
|
|
|
@ -1444,6 +1444,9 @@ mod tests {
|
|||
source_line: None,
|
||||
file_name: None,
|
||||
related_information: None,
|
||||
reports_deprecated: None,
|
||||
reports_unnecessary: None,
|
||||
other: Default::default(),
|
||||
}]),
|
||||
stats: Stats(vec![("a".to_string(), 12)])
|
||||
})
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use deno_runtime::colors;
|
||||
use std::io::Write;
|
||||
|
||||
/// A function that converts a float to a string the represents a human
|
||||
|
@ -85,6 +86,78 @@ where
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub struct DisplayTreeNode {
|
||||
pub text: String,
|
||||
pub children: Vec<DisplayTreeNode>,
|
||||
}
|
||||
|
||||
impl DisplayTreeNode {
|
||||
pub fn from_text(text: String) -> Self {
|
||||
Self {
|
||||
text,
|
||||
children: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print<TWrite: std::fmt::Write>(
|
||||
&self,
|
||||
writer: &mut TWrite,
|
||||
) -> std::fmt::Result {
|
||||
fn print_children<TWrite: std::fmt::Write>(
|
||||
writer: &mut TWrite,
|
||||
prefix: &str,
|
||||
children: &[DisplayTreeNode],
|
||||
) -> std::fmt::Result {
|
||||
const SIBLING_CONNECTOR: char = '├';
|
||||
const LAST_SIBLING_CONNECTOR: char = '└';
|
||||
const CHILD_DEPS_CONNECTOR: char = '┬';
|
||||
const CHILD_NO_DEPS_CONNECTOR: char = '─';
|
||||
const VERTICAL_CONNECTOR: char = '│';
|
||||
const EMPTY_CONNECTOR: char = ' ';
|
||||
|
||||
let child_len = children.len();
|
||||
for (index, child) in children.iter().enumerate() {
|
||||
let is_last = index + 1 == child_len;
|
||||
let sibling_connector = if is_last {
|
||||
LAST_SIBLING_CONNECTOR
|
||||
} else {
|
||||
SIBLING_CONNECTOR
|
||||
};
|
||||
let child_connector = if child.children.is_empty() {
|
||||
CHILD_NO_DEPS_CONNECTOR
|
||||
} else {
|
||||
CHILD_DEPS_CONNECTOR
|
||||
};
|
||||
writeln!(
|
||||
writer,
|
||||
"{} {}",
|
||||
colors::gray(format!(
|
||||
"{prefix}{sibling_connector}─{child_connector}"
|
||||
)),
|
||||
child.text
|
||||
)?;
|
||||
let child_prefix = format!(
|
||||
"{}{}{}",
|
||||
prefix,
|
||||
if is_last {
|
||||
EMPTY_CONNECTOR
|
||||
} else {
|
||||
VERTICAL_CONNECTOR
|
||||
},
|
||||
EMPTY_CONNECTOR
|
||||
);
|
||||
print_children(writer, &child_prefix, &child.children)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
writeln!(writer, "{}", self.text)?;
|
||||
print_children(writer, "", &self.children)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
|
@ -13,6 +13,7 @@ use deno_ast::swc::visit::VisitMut;
|
|||
use deno_ast::swc::visit::VisitWith as _;
|
||||
use deno_ast::MediaType;
|
||||
use deno_ast::SourceRangedForSpanned as _;
|
||||
use deno_cache_dir::file_fetcher::File;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use regex::Regex;
|
||||
|
@ -20,7 +21,7 @@ use std::collections::BTreeSet;
|
|||
use std::fmt::Write as _;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::file_fetcher::File;
|
||||
use crate::file_fetcher::TextDecodedFile;
|
||||
use crate::util::path::mapped_specifier_for_tsc;
|
||||
|
||||
/// Extracts doc tests from a given file, transforms them into pseudo test
|
||||
|
@ -52,7 +53,7 @@ fn extract_inner(
|
|||
file: File,
|
||||
wrap_kind: WrapKind,
|
||||
) -> Result<Vec<File>, AnyError> {
|
||||
let file = file.into_text_decoded()?;
|
||||
let file = TextDecodedFile::decode(file)?;
|
||||
|
||||
let exports = match deno_ast::parse_program(deno_ast::ParseParams {
|
||||
specifier: file.specifier.clone(),
|
||||
|
@ -230,7 +231,7 @@ fn extract_files_from_regex_blocks(
|
|||
.unwrap_or(file_specifier);
|
||||
|
||||
Some(File {
|
||||
specifier: file_specifier,
|
||||
url: file_specifier,
|
||||
maybe_headers: None,
|
||||
source: file_source.into_bytes().into(),
|
||||
})
|
||||
|
@ -558,7 +559,7 @@ fn generate_pseudo_file(
|
|||
exports: &ExportCollector,
|
||||
wrap_kind: WrapKind,
|
||||
) -> Result<File, AnyError> {
|
||||
let file = file.into_text_decoded()?;
|
||||
let file = TextDecodedFile::decode(file)?;
|
||||
|
||||
let parsed = deno_ast::parse_program(deno_ast::ParseParams {
|
||||
specifier: file.specifier.clone(),
|
||||
|
@ -594,7 +595,7 @@ fn generate_pseudo_file(
|
|||
log::debug!("{}:\n{}", file.specifier, source);
|
||||
|
||||
Ok(File {
|
||||
specifier: file.specifier,
|
||||
url: file.specifier,
|
||||
maybe_headers: None,
|
||||
source: source.into_bytes().into(),
|
||||
})
|
||||
|
@ -1199,14 +1200,14 @@ Deno.test("file:///main.ts$3-7.ts", async ()=>{
|
|||
|
||||
for test in tests {
|
||||
let file = File {
|
||||
specifier: ModuleSpecifier::parse(test.input.specifier).unwrap(),
|
||||
url: ModuleSpecifier::parse(test.input.specifier).unwrap(),
|
||||
maybe_headers: None,
|
||||
source: test.input.source.as_bytes().into(),
|
||||
};
|
||||
let got_decoded = extract_doc_tests(file)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|f| f.into_text_decoded().unwrap())
|
||||
.map(|f| TextDecodedFile::decode(f).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let expected = test
|
||||
.expected
|
||||
|
@ -1435,14 +1436,14 @@ add('1', '2');
|
|||
|
||||
for test in tests {
|
||||
let file = File {
|
||||
specifier: ModuleSpecifier::parse(test.input.specifier).unwrap(),
|
||||
url: ModuleSpecifier::parse(test.input.specifier).unwrap(),
|
||||
maybe_headers: None,
|
||||
source: test.input.source.as_bytes().into(),
|
||||
};
|
||||
let got_decoded = extract_snippet_files(file)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|f| f.into_text_decoded().unwrap())
|
||||
.map(|f| TextDecodedFile::decode(f).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let expected = test
|
||||
.expected
|
||||
|
|
|
@ -1,24 +1,34 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use super::draw_thread::DrawThread;
|
||||
use deno_telemetry::OtelConfig;
|
||||
use deno_telemetry::OtelConsoleConfig;
|
||||
use std::io::Write;
|
||||
|
||||
use super::draw_thread::DrawThread;
|
||||
|
||||
struct CliLogger(env_logger::Logger);
|
||||
struct CliLogger {
|
||||
otel_console_config: OtelConsoleConfig,
|
||||
logger: env_logger::Logger,
|
||||
}
|
||||
|
||||
impl CliLogger {
|
||||
pub fn new(logger: env_logger::Logger) -> Self {
|
||||
Self(logger)
|
||||
pub fn new(
|
||||
logger: env_logger::Logger,
|
||||
otel_console_config: OtelConsoleConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
logger,
|
||||
otel_console_config,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn filter(&self) -> log::LevelFilter {
|
||||
self.0.filter()
|
||||
self.logger.filter()
|
||||
}
|
||||
}
|
||||
|
||||
impl log::Log for CliLogger {
|
||||
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
||||
self.0.enabled(metadata)
|
||||
self.logger.enabled(metadata)
|
||||
}
|
||||
|
||||
fn log(&self, record: &log::Record) {
|
||||
|
@ -28,18 +38,30 @@ impl log::Log for CliLogger {
|
|||
// could potentially block other threads that access the draw
|
||||
// thread's state
|
||||
DrawThread::hide();
|
||||
self.0.log(record);
|
||||
deno_telemetry::handle_log(record);
|
||||
|
||||
match self.otel_console_config {
|
||||
OtelConsoleConfig::Ignore => {
|
||||
self.logger.log(record);
|
||||
}
|
||||
OtelConsoleConfig::Capture => {
|
||||
self.logger.log(record);
|
||||
deno_telemetry::handle_log(record);
|
||||
}
|
||||
OtelConsoleConfig::Replace => {
|
||||
deno_telemetry::handle_log(record);
|
||||
}
|
||||
}
|
||||
|
||||
DrawThread::show();
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {
|
||||
self.0.flush();
|
||||
self.logger.flush();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(maybe_level: Option<log::Level>) {
|
||||
pub fn init(maybe_level: Option<log::Level>, otel_config: Option<OtelConfig>) {
|
||||
let log_level = maybe_level.unwrap_or(log::Level::Info);
|
||||
let logger = env_logger::Builder::from_env(
|
||||
env_logger::Env::new()
|
||||
|
@ -93,7 +115,12 @@ pub fn init(maybe_level: Option<log::Level>) {
|
|||
})
|
||||
.build();
|
||||
|
||||
let cli_logger = CliLogger::new(logger);
|
||||
let cli_logger = CliLogger::new(
|
||||
logger,
|
||||
otel_config
|
||||
.map(|c| c.console)
|
||||
.unwrap_or(OtelConsoleConfig::Ignore),
|
||||
);
|
||||
let max_level = cli_logger.filter();
|
||||
let r = log::set_boxed_logger(Box::new(cli_logger));
|
||||
if r.is_ok() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
|
@ -58,8 +59,8 @@ pub fn get_atomic_file_path(file_path: &Path) -> PathBuf {
|
|||
}
|
||||
|
||||
fn gen_rand_path_component() -> String {
|
||||
(0..4).fold(String::new(), |mut output, _| {
|
||||
output.push_str(&format!("{:02x}", rand::random::<u8>()));
|
||||
(0..4).fold(String::with_capacity(8), |mut output, _| {
|
||||
write!(&mut output, "{:02x}", rand::random::<u8>()).unwrap();
|
||||
output
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::fmt::Write;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
|
@ -81,12 +82,14 @@ impl ProgressBarRenderer for BarProgressBarRenderer {
|
|||
let elapsed_text = get_elapsed_text(data.duration);
|
||||
let mut text = String::new();
|
||||
if !display_entry.message.is_empty() {
|
||||
text.push_str(&format!(
|
||||
"{} {}{}\n",
|
||||
writeln!(
|
||||
&mut text,
|
||||
"{} {}{}",
|
||||
colors::green("Download"),
|
||||
display_entry.message,
|
||||
bytes_text,
|
||||
));
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
text.push_str(&elapsed_text);
|
||||
let max_width = (data.terminal_width as i32 - 5).clamp(10, 75) as usize;
|
||||
|
|
|
@ -50,6 +50,7 @@ use tokio::select;
|
|||
|
||||
use crate::args::CliLockfile;
|
||||
use crate::args::DenoSubcommand;
|
||||
use crate::args::NpmCachingStrategy;
|
||||
use crate::args::StorageKeyResolver;
|
||||
use crate::errors;
|
||||
use crate::npm::CliNpmResolver;
|
||||
|
@ -153,7 +154,8 @@ struct SharedWorkerState {
|
|||
storage_key_resolver: StorageKeyResolver,
|
||||
options: CliMainWorkerOptions,
|
||||
subcommand: DenoSubcommand,
|
||||
otel_config: Option<OtelConfig>, // `None` means OpenTelemetry is disabled.
|
||||
otel_config: OtelConfig,
|
||||
default_npm_caching_strategy: NpmCachingStrategy,
|
||||
}
|
||||
|
||||
impl SharedWorkerState {
|
||||
|
@ -424,7 +426,8 @@ impl CliMainWorkerFactory {
|
|||
storage_key_resolver: StorageKeyResolver,
|
||||
subcommand: DenoSubcommand,
|
||||
options: CliMainWorkerOptions,
|
||||
otel_config: Option<OtelConfig>,
|
||||
otel_config: OtelConfig,
|
||||
default_npm_caching_strategy: NpmCachingStrategy,
|
||||
) -> Self {
|
||||
Self {
|
||||
shared: Arc::new(SharedWorkerState {
|
||||
|
@ -448,6 +451,7 @@ impl CliMainWorkerFactory {
|
|||
options,
|
||||
subcommand,
|
||||
otel_config,
|
||||
default_npm_caching_strategy,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -487,8 +491,19 @@ impl CliMainWorkerFactory {
|
|||
NpmPackageReqReference::from_specifier(&main_module)
|
||||
{
|
||||
if let Some(npm_resolver) = shared.npm_resolver.as_managed() {
|
||||
let reqs = &[package_ref.req().clone()];
|
||||
npm_resolver
|
||||
.add_package_reqs(&[package_ref.req().clone()])
|
||||
.add_package_reqs(
|
||||
reqs,
|
||||
if matches!(
|
||||
shared.default_npm_caching_strategy,
|
||||
NpmCachingStrategy::Lazy
|
||||
) {
|
||||
crate::npm::PackageCaching::Only(reqs.into())
|
||||
} else {
|
||||
crate::npm::PackageCaching::All
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_broadcast_channel"
|
||||
version = "0.174.0"
|
||||
version = "0.177.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.112.0"
|
||||
version = "0.115.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_canvas"
|
||||
version = "0.49.0"
|
||||
version = "0.52.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_console"
|
||||
version = "0.180.0"
|
||||
version = "0.183.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_cron"
|
||||
version = "0.60.0"
|
||||
version = "0.63.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_crypto"
|
||||
version = "0.194.0"
|
||||
version = "0.197.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_fetch"
|
||||
version = "0.204.0"
|
||||
version = "0.207.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
@ -18,6 +18,7 @@ base64.workspace = true
|
|||
bytes.workspace = true
|
||||
data-url.workspace = true
|
||||
deno_core.workspace = true
|
||||
deno_path_util.workspace = true
|
||||
deno_permissions.workspace = true
|
||||
deno_tls.workspace = true
|
||||
dyn-clone = "1"
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -41,6 +41,8 @@ use deno_core::OpState;
|
|||
use deno_core::RcRef;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
use deno_path_util::url_from_file_path;
|
||||
use deno_path_util::PathToUrlError;
|
||||
use deno_permissions::PermissionCheckError;
|
||||
use deno_tls::rustls::RootCertStore;
|
||||
use deno_tls::Proxy;
|
||||
|
@ -172,6 +174,8 @@ pub enum FetchError {
|
|||
NetworkError,
|
||||
#[error("Fetching files only supports the GET method: received {0}")]
|
||||
FsNotGet(Method),
|
||||
#[error(transparent)]
|
||||
PathToUrl(#[from] PathToUrlError),
|
||||
#[error("Invalid URL {0}")]
|
||||
InvalidUrl(Url),
|
||||
#[error(transparent)]
|
||||
|
@ -202,9 +206,6 @@ pub enum FetchError {
|
|||
RequestBuilderHook(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
// Only used for node upgrade
|
||||
#[error(transparent)]
|
||||
Hyper(#[from] hyper::Error),
|
||||
}
|
||||
|
||||
pub type CancelableResponseFuture =
|
||||
|
@ -436,7 +437,7 @@ where
|
|||
let permissions = state.borrow_mut::<FP>();
|
||||
let path = permissions.check_read(&path, "fetch()")?;
|
||||
let url = match path {
|
||||
Cow::Owned(path) => Url::from_file_path(path).unwrap(),
|
||||
Cow::Owned(path) => url_from_file_path(&path)?,
|
||||
Cow::Borrowed(_) => url,
|
||||
};
|
||||
|
||||
|
|
|
@ -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.167.0"
|
||||
version = "0.170.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_fs"
|
||||
version = "0.90.0"
|
||||
version = "0.93.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -1411,19 +1411,13 @@ impl<'s> ToV8<'s> for V8MaybeStaticStr {
|
|||
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)
|
||||
.map_err(|_| FastStringV8AllocationError)?
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_http"
|
||||
version = "0.178.0"
|
||||
version = "0.181.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_io"
|
||||
version = "0.90.0"
|
||||
version = "0.93.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());
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_kv"
|
||||
version = "0.88.0"
|
||||
version = "0.91.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_napi"
|
||||
version = "0.111.0"
|
||||
version = "0.114.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "napi_sym"
|
||||
version = "0.110.0"
|
||||
version = "0.113.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_net"
|
||||
version = "0.172.0"
|
||||
version = "0.175.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
@ -17,7 +17,7 @@ path = "lib.rs"
|
|||
deno_core.workspace = true
|
||||
deno_permissions.workspace = true
|
||||
deno_tls.workspace = true
|
||||
hickory-proto = "0.24"
|
||||
hickory-proto = "0.25.0-alpha.4"
|
||||
hickory-resolver.workspace = true
|
||||
pin-project.workspace = true
|
||||
rustls-tokio-stream.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