1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-21 04:52:26 -05:00

Merge branch 'main' into update-imagebitmap

This commit is contained in:
Hajime-san 2024-10-24 16:17:35 +09:00
commit 529c3f0f3d
585 changed files with 12595 additions and 5830 deletions

View file

@ -1,9 +1,8 @@
FROM mcr.microsoft.com/vscode/devcontainers/rust:1-bullseye
# Install cmake and protobuf-compiler
# Install cmake
RUN apt-get update \
&& apt-get install -y cmake \
&& apt-get install -y protobuf-compiler \
&& rm -rf /var/lib/apt/lists/*
# Install Deno

View file

@ -69,7 +69,7 @@
],
"plugins": [
"https://plugins.dprint.dev/typescript-0.93.0.wasm",
"https://plugins.dprint.dev/json-0.19.3.wasm",
"https://plugins.dprint.dev/json-0.19.4.wasm",
"https://plugins.dprint.dev/markdown-0.17.8.wasm",
"https://plugins.dprint.dev/toml-0.6.3.wasm",
"https://plugins.dprint.dev/exec-0.5.0.json@8d9972eee71fa1590e04873540421f3eda7674d0f1aae3d7c788615e7b7413d0",

View file

@ -2,6 +2,11 @@ name: cargo_publish
on: workflow_dispatch
# Ensures only one publish is running at a time
concurrency:
group: ${{ github.workflow }}
cancel-in-progress: true
jobs:
build:
name: cargo publish
@ -28,16 +33,10 @@ jobs:
- uses: dsherret/rust-toolchain-file@v1
- name: Install deno
uses: denoland/setup-deno@v1
uses: denoland/setup-deno@v2
with:
deno-version: v1.x
- name: Install protoc
uses: arduino/setup-protoc@v3
with:
version: '21.12'
repo-token: '${{ secrets.GITHUB_TOKEN }}'
- name: Publish
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

View file

@ -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 = 15;
const cacheVersion = 21;
const ubuntuX86Runner = "ubuntu-22.04";
const ubuntuX86XlRunner = "ubuntu-22.04-xl";
@ -191,14 +191,9 @@ const installNodeStep = {
uses: "actions/setup-node@v4",
with: { "node-version": 18 },
};
const installProtocStep = {
name: "Install protoc",
uses: "arduino/setup-protoc@v3",
with: { "version": "21.12", "repo-token": "${{ secrets.GITHUB_TOKEN }}" },
};
const installDenoStep = {
name: "Install Deno",
uses: "denoland/setup-deno@v1",
uses: "denoland/setup-deno@v2",
with: { "deno-version": "v1.x" },
};
@ -494,7 +489,6 @@ const ci = {
if: "matrix.job == 'bench' || matrix.job == 'test'",
...installNodeStep,
},
installProtocStep,
{
if: [
"matrix.profile == 'release' &&",
@ -757,11 +751,11 @@ const ci = {
].join("\n"),
run: [
"cd target/release",
"shasum -a 256 deno > deno-${{ matrix.arch }}-unknown-linux-gnu.sha256sum",
"zip -r deno-${{ matrix.arch }}-unknown-linux-gnu.zip deno",
"shasum -a 256 deno-${{ matrix.arch }}-unknown-linux-gnu.zip > deno-${{ matrix.arch }}-unknown-linux-gnu.zip.sha256sum",
"strip denort",
"shasum -a 256 denort > denort-${{ matrix.arch }}-unknown-linux-gnu.sha256sum",
"zip -r denort-${{ matrix.arch }}-unknown-linux-gnu.zip denort",
"shasum -a 256 denort-${{ matrix.arch }}-unknown-linux-gnu.zip > denort-${{ matrix.arch }}-unknown-linux-gnu.zip.sha256sum",
"./deno types > lib.deno.d.ts",
].join("\n"),
},
@ -785,11 +779,11 @@ const ci = {
"--p12-file=<(echo $APPLE_CODESIGN_KEY | base64 -d) " +
"--entitlements-xml-file=cli/entitlements.plist",
"cd target/release",
"shasum -a 256 deno > deno-${{ matrix.arch }}-apple-darwin.sha256sum",
"zip -r deno-${{ matrix.arch }}-apple-darwin.zip deno",
"shasum -a 256 deno-${{ matrix.arch }}-apple-darwin.zip > deno-${{ matrix.arch }}-apple-darwin.zip.sha256sum",
"strip denort",
"shasum -a 256 denort > denort-${{ matrix.arch }}-apple-darwin.sha256sum",
"zip -r denort-${{ matrix.arch }}-apple-darwin.zip denort",
"shasum -a 256 denort-${{ matrix.arch }}-apple-darwin.zip > denort-${{ matrix.arch }}-apple-darwin.zip.sha256sum",
]
.join("\n"),
},
@ -803,10 +797,10 @@ const ci = {
].join("\n"),
shell: "pwsh",
run: [
"Get-FileHash target/release/deno.exe -Algorithm SHA256 | Format-List > target/release/deno-${{ matrix.arch }}-pc-windows-msvc.sha256sum",
"Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip",
"Get-FileHash target/release/denort.exe -Algorithm SHA256 | Format-List > target/release/denort-${{ matrix.arch }}-pc-windows-msvc.sha256sum",
"Get-FileHash target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip -Algorithm SHA256 | Format-List > target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip.sha256sum",
"Compress-Archive -CompressionLevel Optimal -Force -Path target/release/denort.exe -DestinationPath target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip",
"Get-FileHash target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip -Algorithm SHA256 | Format-List > target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip.sha256sum",
].join("\n"),
},
{
@ -1051,25 +1045,25 @@ const ci = {
with: {
files: [
"target/release/deno-x86_64-pc-windows-msvc.zip",
"target/release/deno-x86_64-pc-windows-msvc.sha256sum",
"target/release/deno-x86_64-pc-windows-msvc.zip.sha256sum",
"target/release/denort-x86_64-pc-windows-msvc.zip",
"target/release/denort-x86_64-pc-windows-msvc.sha256sum",
"target/release/denort-x86_64-pc-windows-msvc.zip.sha256sum",
"target/release/deno-x86_64-unknown-linux-gnu.zip",
"target/release/deno-x86_64-unknown-linux-gnu.sha256sum",
"target/release/deno-x86_64-unknown-linux-gnu.zip.sha256sum",
"target/release/denort-x86_64-unknown-linux-gnu.zip",
"target/release/denort-x86_64-unknown-linux-gnu.sha256sum",
"target/release/denort-x86_64-unknown-linux-gnu.zip.sha256sum",
"target/release/deno-x86_64-apple-darwin.zip",
"target/release/deno-x86_64-apple-darwin.sha256sum",
"target/release/deno-x86_64-apple-darwin.zip.sha256sum",
"target/release/denort-x86_64-apple-darwin.zip",
"target/release/denort-x86_64-apple-darwin.sha256sum",
"target/release/denort-x86_64-apple-darwin.zip.sha256sum",
"target/release/deno-aarch64-unknown-linux-gnu.zip",
"target/release/deno-aarch64-unknown-linux-gnu.sha256sum",
"target/release/deno-aarch64-unknown-linux-gnu.zip.sha256sum",
"target/release/denort-aarch64-unknown-linux-gnu.zip",
"target/release/denort-aarch64-unknown-linux-gnu.sha256sum",
"target/release/denort-aarch64-unknown-linux-gnu.zip.sha256sum",
"target/release/deno-aarch64-apple-darwin.zip",
"target/release/deno-aarch64-apple-darwin.sha256sum",
"target/release/deno-aarch64-apple-darwin.zip.sha256sum",
"target/release/denort-aarch64-apple-darwin.zip",
"target/release/denort-aarch64-apple-darwin.sha256sum",
"target/release/denort-aarch64-apple-darwin.zip.sha256sum",
"target/release/deno_src.tar.gz",
"target/release/lib.deno.d.ts",
].join("\n"),

View file

@ -178,7 +178,7 @@ jobs:
if: '!(matrix.skip)'
- if: '!(matrix.skip) && (matrix.job == ''lint'' || matrix.job == ''test'' || matrix.job == ''bench'')'
name: Install Deno
uses: denoland/setup-deno@v1
uses: denoland/setup-deno@v2
with:
deno-version: v1.x
- name: Install Python
@ -199,12 +199,6 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: 18
- name: Install protoc
uses: arduino/setup-protoc@v3
with:
version: '21.12'
repo-token: '${{ secrets.GITHUB_TOKEN }}'
if: '!(matrix.skip)'
- if: |-
!(matrix.skip) && (matrix.profile == 'release' &&
matrix.job == 'test' &&
@ -367,8 +361,8 @@ jobs:
path: |-
~/.cargo/registry/index
~/.cargo/registry/cache
key: '15-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
restore-keys: '15-cargo-home-${{ matrix.os }}-${{ matrix.arch }}'
key: '21-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
restore-keys: '21-cargo-home-${{ matrix.os }}-${{ matrix.arch }}'
if: '!(matrix.skip)'
- name: Restore cache build output (PR)
uses: actions/cache/restore@v4
@ -381,7 +375,7 @@ jobs:
!./target/*/*.zip
!./target/*/*.tar.gz
key: never_saved
restore-keys: '15-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-'
restore-keys: '21-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
@ -448,11 +442,11 @@ jobs:
github.repository == 'denoland/deno')
run: |-
cd target/release
shasum -a 256 deno > deno-${{ matrix.arch }}-unknown-linux-gnu.sha256sum
zip -r deno-${{ matrix.arch }}-unknown-linux-gnu.zip deno
shasum -a 256 deno-${{ matrix.arch }}-unknown-linux-gnu.zip > deno-${{ matrix.arch }}-unknown-linux-gnu.zip.sha256sum
strip denort
shasum -a 256 denort > denort-${{ matrix.arch }}-unknown-linux-gnu.sha256sum
zip -r denort-${{ matrix.arch }}-unknown-linux-gnu.zip denort
shasum -a 256 denort-${{ matrix.arch }}-unknown-linux-gnu.zip > denort-${{ matrix.arch }}-unknown-linux-gnu.zip.sha256sum
./deno types > lib.deno.d.ts
- name: Pre-release (mac)
if: |-
@ -467,11 +461,11 @@ jobs:
echo "Key is $(echo $APPLE_CODESIGN_KEY | base64 -d | wc -c) bytes"
rcodesign sign target/release/deno --code-signature-flags=runtime --p12-password="$APPLE_CODESIGN_PASSWORD" --p12-file=<(echo $APPLE_CODESIGN_KEY | base64 -d) --entitlements-xml-file=cli/entitlements.plist
cd target/release
shasum -a 256 deno > deno-${{ matrix.arch }}-apple-darwin.sha256sum
zip -r deno-${{ matrix.arch }}-apple-darwin.zip deno
shasum -a 256 deno-${{ matrix.arch }}-apple-darwin.zip > deno-${{ matrix.arch }}-apple-darwin.zip.sha256sum
strip denort
shasum -a 256 denort > denort-${{ matrix.arch }}-apple-darwin.sha256sum
zip -r denort-${{ matrix.arch }}-apple-darwin.zip denort
shasum -a 256 denort-${{ matrix.arch }}-apple-darwin.zip > denort-${{ matrix.arch }}-apple-darwin.zip.sha256sum
- name: Pre-release (windows)
if: |-
!(matrix.skip) && (matrix.os == 'windows' &&
@ -480,10 +474,10 @@ jobs:
github.repository == 'denoland/deno')
shell: pwsh
run: |-
Get-FileHash target/release/deno.exe -Algorithm SHA256 | Format-List > target/release/deno-${{ matrix.arch }}-pc-windows-msvc.sha256sum
Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip
Get-FileHash target/release/denort.exe -Algorithm SHA256 | Format-List > target/release/denort-${{ matrix.arch }}-pc-windows-msvc.sha256sum
Get-FileHash target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip -Algorithm SHA256 | Format-List > target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip.sha256sum
Compress-Archive -CompressionLevel Optimal -Force -Path target/release/denort.exe -DestinationPath target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip
Get-FileHash target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip -Algorithm SHA256 | Format-List > target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip.sha256sum
- name: Upload canary to dl.deno.land
if: |-
!(matrix.skip) && (matrix.job == 'test' &&
@ -658,25 +652,25 @@ jobs:
with:
files: |-
target/release/deno-x86_64-pc-windows-msvc.zip
target/release/deno-x86_64-pc-windows-msvc.sha256sum
target/release/deno-x86_64-pc-windows-msvc.zip.sha256sum
target/release/denort-x86_64-pc-windows-msvc.zip
target/release/denort-x86_64-pc-windows-msvc.sha256sum
target/release/denort-x86_64-pc-windows-msvc.zip.sha256sum
target/release/deno-x86_64-unknown-linux-gnu.zip
target/release/deno-x86_64-unknown-linux-gnu.sha256sum
target/release/deno-x86_64-unknown-linux-gnu.zip.sha256sum
target/release/denort-x86_64-unknown-linux-gnu.zip
target/release/denort-x86_64-unknown-linux-gnu.sha256sum
target/release/denort-x86_64-unknown-linux-gnu.zip.sha256sum
target/release/deno-x86_64-apple-darwin.zip
target/release/deno-x86_64-apple-darwin.sha256sum
target/release/deno-x86_64-apple-darwin.zip.sha256sum
target/release/denort-x86_64-apple-darwin.zip
target/release/denort-x86_64-apple-darwin.sha256sum
target/release/denort-x86_64-apple-darwin.zip.sha256sum
target/release/deno-aarch64-unknown-linux-gnu.zip
target/release/deno-aarch64-unknown-linux-gnu.sha256sum
target/release/deno-aarch64-unknown-linux-gnu.zip.sha256sum
target/release/denort-aarch64-unknown-linux-gnu.zip
target/release/denort-aarch64-unknown-linux-gnu.sha256sum
target/release/denort-aarch64-unknown-linux-gnu.zip.sha256sum
target/release/deno-aarch64-apple-darwin.zip
target/release/deno-aarch64-apple-darwin.sha256sum
target/release/deno-aarch64-apple-darwin.zip.sha256sum
target/release/denort-aarch64-apple-darwin.zip
target/release/denort-aarch64-apple-darwin.sha256sum
target/release/denort-aarch64-apple-darwin.zip.sha256sum
target/release/deno_src.tar.gz
target/release/lib.deno.d.ts
body_path: target/release/release-notes.md
@ -691,7 +685,7 @@ jobs:
!./target/*/*.zip
!./target/*/*.sha256sum
!./target/*/*.tar.gz
key: '15-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
key: '21-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
publish-canary:
name: publish canary
runs-on: ubuntu-22.04

View file

@ -40,7 +40,7 @@ jobs:
project_id: denoland
- name: Install deno
uses: denoland/setup-deno@v1
uses: denoland/setup-deno@v2
with:
deno-version: v1.x

View file

@ -34,7 +34,7 @@ jobs:
uses: actions/checkout@v4
- name: Install deno
uses: denoland/setup-deno@v1
uses: denoland/setup-deno@v2
with:
deno-version: v1.x

View file

@ -39,7 +39,7 @@ jobs:
- uses: dsherret/rust-toolchain-file@v1
- name: Install deno
uses: denoland/setup-deno@v1
uses: denoland/setup-deno@v2
with:
deno-version: v1.x

View file

@ -30,7 +30,7 @@ jobs:
persist-credentials: false
- name: Setup Deno
uses: denoland/setup-deno@v1
uses: denoland/setup-deno@v2
with:
deno-version: ${{ matrix.deno-version }}

492
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -45,19 +45,19 @@ license = "MIT"
repository = "https://github.com/denoland/deno"
[workspace.dependencies]
deno_ast = { version = "=0.42.1", features = ["transpiling"] }
deno_core = { version = "0.311.0" }
deno_ast = { version = "=0.42.2", features = ["transpiling"] }
deno_core = { version = "0.314.2" }
deno_bench_util = { version = "0.162.0", path = "./bench_util" }
deno_bench_util = { version = "0.167.0", path = "./bench_util" }
deno_lockfile = "=0.23.1"
deno_media_type = { version = "0.1.4", features = ["module_specifier"] }
deno_npm = "=0.25.2"
deno_path_util = "=0.2.0"
deno_permissions = { version = "0.28.0", path = "./runtime/permissions" }
deno_runtime = { version = "0.177.0", path = "./runtime" }
deno_semver = "=0.5.14"
deno_npm = "=0.25.4"
deno_path_util = "=0.2.1"
deno_permissions = { version = "0.33.0", path = "./runtime/permissions" }
deno_runtime = { version = "0.182.0", path = "./runtime" }
deno_semver = "=0.5.16"
deno_terminal = "0.2.0"
napi_sym = { version = "0.98.0", path = "./cli/napi/sym" }
napi_sym = { version = "0.103.0", path = "./cli/napi/sym" }
test_util = { package = "test_server", path = "./tests/util/server" }
denokv_proto = "0.8.1"
@ -66,32 +66,32 @@ denokv_remote = "0.8.1"
denokv_sqlite = { default-features = false, version = "0.8.2" }
# exts
deno_broadcast_channel = { version = "0.162.0", path = "./ext/broadcast_channel" }
deno_cache = { version = "0.100.0", path = "./ext/cache" }
deno_canvas = { version = "0.37.0", path = "./ext/canvas" }
deno_console = { version = "0.168.0", path = "./ext/console" }
deno_cron = { version = "0.48.0", path = "./ext/cron" }
deno_crypto = { version = "0.182.0", path = "./ext/crypto" }
deno_fetch = { version = "0.192.0", path = "./ext/fetch" }
deno_ffi = { version = "0.155.0", path = "./ext/ffi" }
deno_fs = { version = "0.78.0", path = "./ext/fs" }
deno_http = { version = "0.166.0", path = "./ext/http" }
deno_io = { version = "0.78.0", path = "./ext/io" }
deno_kv = { version = "0.76.0", path = "./ext/kv" }
deno_napi = { version = "0.99.0", path = "./ext/napi" }
deno_net = { version = "0.160.0", path = "./ext/net" }
deno_node = { version = "0.105.0", path = "./ext/node" }
deno_tls = { version = "0.155.0", path = "./ext/tls" }
deno_url = { version = "0.168.0", path = "./ext/url" }
deno_web = { version = "0.199.0", path = "./ext/web" }
deno_webgpu = { version = "0.135.0", path = "./ext/webgpu" }
deno_webidl = { version = "0.168.0", path = "./ext/webidl" }
deno_websocket = { version = "0.173.0", path = "./ext/websocket" }
deno_webstorage = { version = "0.163.0", path = "./ext/webstorage" }
deno_broadcast_channel = { version = "0.167.0", path = "./ext/broadcast_channel" }
deno_cache = { version = "0.105.0", path = "./ext/cache" }
deno_canvas = { version = "0.42.0", path = "./ext/canvas" }
deno_console = { version = "0.173.0", path = "./ext/console" }
deno_cron = { version = "0.53.0", path = "./ext/cron" }
deno_crypto = { version = "0.187.0", path = "./ext/crypto" }
deno_fetch = { version = "0.197.0", path = "./ext/fetch" }
deno_ffi = { version = "0.160.0", path = "./ext/ffi" }
deno_fs = { version = "0.83.0", path = "./ext/fs" }
deno_http = { version = "0.171.0", path = "./ext/http" }
deno_io = { version = "0.83.0", path = "./ext/io" }
deno_kv = { version = "0.81.0", path = "./ext/kv" }
deno_napi = { version = "0.104.0", path = "./ext/napi" }
deno_net = { version = "0.165.0", path = "./ext/net" }
deno_node = { version = "0.110.0", path = "./ext/node" }
deno_tls = { version = "0.160.0", path = "./ext/tls" }
deno_url = { version = "0.173.0", path = "./ext/url" }
deno_web = { version = "0.204.0", path = "./ext/web" }
deno_webgpu = { version = "0.140.0", path = "./ext/webgpu" }
deno_webidl = { version = "0.173.0", path = "./ext/webidl" }
deno_websocket = { version = "0.178.0", path = "./ext/websocket" }
deno_webstorage = { version = "0.168.0", path = "./ext/webstorage" }
# resolvers
deno_resolver = { version = "0.0.1", path = "./resolvers/deno" }
node_resolver = { version = "0.7.0", path = "./resolvers/node" }
deno_resolver = { version = "0.5.0", path = "./resolvers/deno" }
node_resolver = { version = "0.12.0", path = "./resolvers/node" }
aes = "=0.8.3"
anyhow = "1.0.57"
@ -106,12 +106,13 @@ 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()
chrono = { version = "0.4", default-features = false, features = ["std", "serde"] }
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.13.0"
deno_package_json = { version = "0.1.1", default-features = false }
deno_package_json = { version = "0.1.2", default-features = false }
dlopen2 = "0.6.1"
ecb = "=0.1.2"
elliptic-curve = { version = "0.13.4", features = ["alloc", "arithmetic", "ecdh", "std", "pem", "jwk"] }
@ -136,10 +137,10 @@ hyper-util = { version = "=0.1.7", features = ["tokio", "client", "client-legacy
hyper_v014 = { package = "hyper", version = "0.14.26", features = ["runtime", "http1"] }
indexmap = { version = "2", features = ["serde"] }
ipnet = "2.3"
jsonc-parser = { version = "=0.23.0", features = ["serde"] }
jsonc-parser = { version = "=0.26.1", features = ["serde"] }
lazy-regex = "3"
libc = "0.2.126"
libz-sys = { version = "1.1", default-features = false }
libz-sys = { version = "1.1.20", default-features = false }
log = "0.4.20"
lsp-types = "=0.97.0" # used by tower-lsp and "proposed" feature is unstable in patch releases
memmem = "0.1.1"
@ -186,7 +187,7 @@ tar = "=0.4.40"
tempfile = "3.4.0"
termcolor = "1.1.3"
thiserror = "1.0.61"
tokio = { version = "=1.36.0", features = ["full"] }
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"] }
tokio-socks = "0.5.1"
@ -220,15 +221,14 @@ quote = "1"
syn = { version = "2", features = ["full", "extra-traits"] }
# unix
nix = "=0.26.2"
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_System_Pipes", "Wdk_Storage_FileSystem", "Win32_System_Registry"] }
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"] }
winres = "=0.1.12"
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
[profile.release]
codegen-units = 1
incremental = true
@ -246,13 +246,6 @@ inherits = "release"
codegen-units = 128
lto = "thin"
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
[profile.bench]
codegen-units = 1
incremental = true
lto = true
opt-level = 'z' # Optimize for size
# Key generation is too slow on `debug`
[profile.dev.package.num-bigint-dig]
opt-level = 3
@ -261,80 +254,6 @@ opt-level = 3
[profile.dev.package.v8]
opt-level = 1
# Optimize these packages for performance.
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
[profile.bench.package.async-compression]
opt-level = 3
[profile.bench.package.base64-simd]
opt-level = 3
[profile.bench.package.brotli]
opt-level = 3
[profile.bench.package.brotli-decompressor]
opt-level = 3
[profile.bench.package.bytes]
opt-level = 3
[profile.bench.package.deno_bench_util]
opt-level = 3
[profile.bench.package.deno_broadcast_channel]
opt-level = 3
[profile.bench.package.deno_core]
opt-level = 3
[profile.bench.package.deno_crypto]
opt-level = 3
[profile.bench.package.deno_fetch]
opt-level = 3
[profile.bench.package.deno_ffi]
opt-level = 3
[profile.bench.package.deno_http]
opt-level = 3
[profile.bench.package.deno_napi]
opt-level = 3
[profile.bench.package.deno_net]
opt-level = 3
[profile.bench.package.deno_node]
opt-level = 3
[profile.bench.package.deno_runtime]
opt-level = 3
[profile.bench.package.deno_tls]
opt-level = 3
[profile.bench.package.deno_url]
opt-level = 3
[profile.bench.package.deno_web]
opt-level = 3
[profile.bench.package.deno_websocket]
opt-level = 3
[profile.bench.package.fastwebsockets]
opt-level = 3
[profile.bench.package.flate2]
opt-level = 3
[profile.bench.package.futures-util]
opt-level = 3
[profile.bench.package.hyper]
opt-level = 3
[profile.bench.package.miniz_oxide]
opt-level = 3
[profile.bench.package.num-bigint-dig]
opt-level = 3
[profile.bench.package.rand]
opt-level = 3
[profile.bench.package.serde]
opt-level = 3
[profile.bench.package.serde_v8]
opt-level = 3
[profile.bench.package.test_napi]
opt-level = 3
[profile.bench.package.tokio]
opt-level = 3
[profile.bench.package.url]
opt-level = 3
[profile.bench.package.v8]
opt-level = 3
[profile.bench.package.zstd]
opt-level = 3
[profile.bench.package.zstd-sys]
opt-level = 3
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
[profile.release.package.async-compression]
opt-level = 3
[profile.release.package.base64-simd]
@ -393,6 +312,8 @@ opt-level = 3
opt-level = 3
[profile.release.package.serde_v8]
opt-level = 3
[profile.release.package.libsui]
opt-level = 3
[profile.release.package.test_napi]
opt-level = 3
[profile.release.package.tokio]

View file

@ -6,6 +6,420 @@ https://github.com/denoland/deno/releases
We also have one-line install commands at:
https://github.com/denoland/deno_install
### 2.0.2 / 2024.10.17
- fix(cli): set napi object property properly (#26344)
- fix(ext/node): add null check for kStreamBaseField (#26368)
- fix(install): don't attempt to cache specifiers that point to directories
(#26369)
- fix(jupyter): fix panics for overslow subtraction (#26371)
- fix(jupyter): update to the new logo (#26353)
- fix(net): don't try to set nodelay on upgrade streams (#26342)
- fix(node/fs): copyFile with `COPYFILE_EXCL` should not throw if the
destination doesn't exist (#26360)
- fix(node/http): normalize header names in `ServerResponse` (#26339)
- fix(runtime): send ws ping frames from inspector server (#26352)
- fix: don't warn on ignored signals on windows (#26332)
### 2.0.1 / 2024.10.16
- feat(lsp): "deno/didRefreshDenoConfigurationTree" notifications (#26215)
- feat(unstable): `--unstable-detect-cjs` for respecting explicit
`"type": "commonjs"` (#26149)
- fix(add): create deno.json when running `deno add jsr:<pkg>` (#26275)
- fix(add): exact version should not have range `^` specifier (#26302)
- fix(child_process): map node `--no-warnings` flag to `--quiet` (#26288)
- fix(cli): add prefix to install commands in help (#26318)
- fix(cli): consolidate pkg parser for install & remove (#26298)
- fix(cli): named export takes precedence over default export in doc testing
(#26112)
- fix(cli): improve deno info output for npm packages (#25906)
- fix(console/ext/repl): support using parseFloat() (#25900)
- fix(ext/console): apply coloring for console.table (#26280)
- fix(ext/napi): pass user context to napi_threadsafe_fn finalizers (#26229)
- fix(ext/node): allow writing to tty columns (#26201)
- fix(ext/node): compute pem length (upper bound) for key exports (#26231)
- fix(ext/node): fix dns.lookup result ordering (#26264)
- fix(ext/node): handle http2 server ending stream (#26235)
- fix(ext/node): implement TCP.setNoDelay (#26263)
- fix(ext/node): timingSafeEqual account for AB byteOffset (#26292)
- fix(ext/node): use primordials in `ext/node/polyfills/internal/buffer.mjs`
(#24993)
- fix(ext/webgpu): allow GL backend on Windows (#26206)
- fix(install): duplicate dependencies in `package.json` (#26128)
- fix(install): handle pkg with dep on self when pkg part of peer dep resolution
(#26277)
- fix(install): retry downloads of registry info / tarballs (#26278)
- fix(install): support installing npm package with alias (#26246)
- fix(jupyter): copy kernels icons to the kernel directory (#26084)
- fix(jupyter): keep running event loop when waiting for messages (#26049)
- fix(lsp): relative completions for bare import-mapped specifiers (#26137)
- fix(node): make `process.stdout.isTTY` writable (#26130)
- fix(node/util): export `styleText` from `node:util` (#26194)
- fix(npm): support `--allow-scripts` on `deno run` (and `deno add`,
`deno test`, etc) (#26075)
- fix(repl): importing json files (#26053)
- fix(repl): remove check flags (#26140)
- fix(unstable/worker): ensure import permissions are passed (#26101)
- fix: add hint for missing `document` global in terminal error (#26218)
- fix: do not panic on wsl share file paths on windows (#26081)
- fix: do not panic running remote cjs module (#26259)
- fix: do not panic when using methods on classes and interfaces in deno doc
html output (#26100)
- fix: improve suggestions and hints when using CommonJS modules (#26287)
- fix: node-api function call should use preamble (#26297)
- fix: panic in `prepare_stack_trace_callback` when global interceptor throws
(#26241)
- fix: use syntect for deno doc html generation (#26322)
- perf(http): avoid clone getting request method and url (#26250)
- perf(http): cache webidl.converters lookups in ext/fetch/23_response.js
(#26256)
- perf(http): make heap allocation for path conditional (#26289)
- perf: use fast calls for microtask ops (#26236)
### 2.0.0 / 2024.10.09
Read announcement blog post at: https://deno.com/blog/v2
- BREAKING: `DENO_FUTURE=1` by default, or welcome to Deno 2.0 (#25213)
- BREAKING: disallow `new Deno.FsFile()` (#25478)
- BREAKING: drop support for Deno.run.{clearEnv,gid,uid} (#25371)
- BREAKING: improve types for `Deno.serve` (#25369)
- BREAKING: improved error code accuracy (#25383)
- BREAKING: make supported compilerOptions an allow list (#25432)
- BREAKING: move `width` and `height` options to `UnsafeWindowSurface`
constructor (#24200)
- BREAKING: remove --allow-hrtime (#25367)
- BREAKING: remove "emit" and "map" from deno info output (#25468)
- BREAKING: remove `--allow-none` flag (#25337)
- BREAKING: remove `--jobs` flag (#25336)
- BREAKING: remove `--trace-ops` (#25344)
- BREAKING: remove `--ts` flag (#25338)
- BREAKING: remove `--unstable` flag (#25522)
- BREAKING: remove `deno bundle` (#25339)
- BREAKING: remove `deno vendor` (#25343)
- BREAKING: remove `Deno.[Tls]Listener.prototype.rid` (#25556)
- BREAKING: remove `Deno.{Conn,TlsConn,TcpConn,UnixConn}.prototype.rid` (#25446)
- BREAKING: remove `Deno.{Reader,Writer}[Sync]` and `Deno.Closer` (#25524)
- BREAKING: remove `Deno.Buffer` (#25441)
- BREAKING: remove `Deno.close()` (#25347)
- BREAKING: remove `Deno.ConnectTlsOptions.{certChain,certFile,privateKey}` and
`Deno.ListenTlsOptions.certChain,certFile,keyFile}` (#25525)
- BREAKING: remove `Deno.copy()` (#25345)
- BREAKING: remove `Deno.customInspect` (#25348)
- BREAKING: remove `Deno.fdatasync[Sync]()` (#25520)
- BREAKING: remove `Deno.File` (#25447)
- BREAKING: remove `Deno.flock[Sync]()` (#25350)
- BREAKING: remove `Deno.FsFile.prototype.rid` (#25499)
- BREAKING: remove `Deno.fstat[Sync]()` (#25351)
- BREAKING: remove `Deno.FsWatcher.prototype.rid` (#25444)
- BREAKING: remove `Deno.fsync[Sync]()` (#25448)
- BREAKING: remove `Deno.ftruncate[Sync]()` (#25412)
- BREAKING: remove `Deno.funlock[Sync]()` (#25442)
- BREAKING: remove `Deno.futime[Sync]()` (#25252)
- BREAKING: remove `Deno.iter[Sync]()` (#25346)
- BREAKING: remove `Deno.read[Sync]()` (#25409)
- BREAKING: remove `Deno.readAll[Sync]()` (#25386)
- BREAKING: remove `Deno.seek[Sync]()` (#25449)
- BREAKING: remove `Deno.Seeker[Sync]` (#25551)
- BREAKING: remove `Deno.shutdown()` (#25253)
- BREAKING: remove `Deno.write[Sync]()` (#25408)
- BREAKING: remove `Deno.writeAll[Sync]()` (#25407)
- BREAKING: remove deprecated `UnsafeFnPointer` constructor type with untyped
`Deno.PointerObject` parameter (#25577)
- BREAKING: remove deprecated files config (#25535)
- BREAKING: Remove obsoleted Temporal APIs part 2 (#25505)
- BREAKING: remove remaining web types for compatibility (#25334)
- BREAKING: remove support for remote import maps in deno.json (#25836)
- BREAKING: rename "deps" remote cache folder to "remote" (#25969)
- BREAKING: soft-remove `Deno.isatty()` (#25410)
- BREAKING: soft-remove `Deno.run()` (#25403)
- BREAKING: soft-remove `Deno.serveHttp()` (#25451)
- BREAKING: undeprecate `Deno.FsWatcher.prototype.return()` (#25623)
- feat: add `--allow-import` flag (#25469)
- feat: Add a hint on error about 'Relative import path ... not prefixed with
...' (#25430)
- feat: Add better error messages for unstable APIs (#25519)
- feat: Add suggestion for packages using Node-API addons (#25975)
- feat: Allow importing .cjs files (#25426)
- feat: default to TS for file extension and support ext flag in more scenarios
(#25472)
- feat: deprecate import assertions (#25281)
- feat: Don't warn about --allow-script when using esbuild (#25894)
- feat: hide several --unstable-* flags (#25378)
- feat: improve lockfile v4 to store normalized version constraints and be more
terse (#25247)
- feat: improve warnings for deprecations and lifecycle script for npm packages
(#25694)
- feat: include version number in all --json based outputs (#25335)
- feat: lockfile v4 by default (#25165)
- feat: make 'globalThis.location' a configurable property (#25812)
- feat: print `Listening on` messages on stderr instead of stdout (#25491)
- feat: remove `--lock-write` flag (#25214)
- feat: require jsr prefix for `deno install` and `deno add` (#25698)
- feat: require(esm) (#25501)
- feat: Show hints when using `window` global (#25805)
- feat: stabilize `Deno.createHttpClient()` (#25569)
- feat: suggest `deno install --entrypoint` instead of `deno cache` (#25228)
- feat: support DENO_LOG env var instead of RUST_LOG (#25356)
- feat: TypeScript 5.6 and `npm:@types/node@22` (#25614)
- feat: Update no-window lint rule (#25486)
- feat: update warning message for --allow-run with no list (#25693)
- feat: warn when using `--allow-run` with no allow list (#25215)
- feat(add): Add npm packages to package.json if present (#25477)
- feat(add): strip package subpath when adding a package (#25419)
- feat(add/install): Flag to add dev dependency to package.json (#25495)
- feat(byonm): support `deno run npm:<package>` when package is not in
package.json (#25981)
- feat(check): turn on noImplicitOverride (#25695)
- feat(check): turn on useUnknownInCatchVariables (#25465)
- feat(cli): evaluate code snippets in JSDoc and markdown (#25220)
- feat(cli): give access to `process` global everywhere (#25291)
- feat(cli): use NotCapable error for permission errors (#25431)
- feat(config): Node modules option for 2.0 (#25299)
- feat(ext/crypto): import and export p521 keys (#25789)
- feat(ext/crypto): X448 support (#26043)
- feat(ext/kv): configurable limit params (#25174)
- feat(ext/node): add abort helpers, process & streams fix (#25262)
- feat(ext/node): add rootCertificates to node:tls (#25707)
- feat(ext/node): buffer.transcode() (#25972)
- feat(ext/node): export 'promises' symbol from 'node:timers' (#25589)
- feat(ext/node): export missing constants from 'zlib' module (#25584)
- feat(ext/node): export missing symbols from domain, puncode, repl, tls
(#25585)
- feat(ext/node): export more symbols from streams and timers/promises (#25582)
- feat(ext/node): expose ES modules for _ modules (#25588)
- feat(flags): allow double commas to escape values in path based flags (#25453)
- feat(flags): support user provided args in repl subcommand (#25605)
- feat(fmt): better error on malfored HTML files (#25853)
- feat(fmt): stabilize CSS, HTML and YAML formatters (#25753)
- feat(fmt): support vto and njk extensions (#25831)
- feat(fmt): upgrade markup_fmt (#25768)
- feat(install): deno install with entrypoint (#25411)
- feat(install): warn repeatedly about not-run lifecycle scripts on explicit
installs (#25878)
- feat(lint): add `no-process-global` lint rule (#25709)
- feat(lsp): add a message when someone runs 'deno lsp' manually (#26051)
- feat(lsp): auto-import types with 'import type' (#25662)
- feat(lsp): html/css/yaml file formatting (#25353)
- feat(lsp): quick fix for @deno-types="npm:@types/*" (#25954)
- feat(lsp): turn on useUnknownInCatchVariables (#25474)
- feat(lsp): unstable setting as list (#25552)
- feat(permissions): `Deno.mainModule` doesn't require permissions (#25667)
- feat(permissions): allow importing from cdn.jsdelivr.net by default (#26013)
- feat(serve): Support second parameter in deno serve (#25606)
- feat(tools/doc): display subitems in symbol overviews where applicable
(#25885)
- feat(uninstall): alias to 'deno remove' if -g flag missing (#25461)
- feat(upgrade): better error message on failure (#25503)
- feat(upgrade): print info links for Deno 2 RC releases (#25225)
- feat(upgrade): support LTS release channel (#25123)
- fix: add link to env var docs (#25557)
- fix: add suggestion how to fix importing CJS module (#21764)
- fix: add test ensuring als works across dynamic import (#25593)
- fix: better error for Deno.UnsafeWindowSurface, correct HttpClient name,
cleanup unused code (#25833)
- fix: cjs resolution cases (#25739)
- fix: consistent with deno_config and treat `"experimentalDecorators"` as
deprecated (#25735)
- fix: delete old Deno 1.x headers file when loading cache (#25283)
- fix: do not panic running invalid file specifier (#25530)
- fix: don't include extensionless files in file collection for lint & fmt by
default (#25721)
- fix: don't prompt when using `Deno.permissions.request` with `--no-prompt`
(#25811)
- fix: eagerly error for specifier with empty version constraint (#25944)
- fix: enable `Win32_Security` feature in `windows-sys` (#26007)
- fix: error on unsupported compiler options (#25714)
- fix: error out if a valid flag is passed before a subcommand (#25830)
- fix: fix jupyter display function type (#25326)
- fix: Float16Array type (#25506)
- fix: handle showing warnings while the progress bar is shown (#25187)
- fix: Hide 'deno cache' from help output (#25960)
- fix: invalid ipv6 hostname on `deno serve` (#25482)
- fix: linux canonicalization checks (#24641)
- fix: lock down allow-run permissions more (#25370)
- fix: make some warnings more standard (#25324)
- fix: no cmd prefix in help output go links (#25459)
- fix: only enable byonm if workspace root has pkg json (#25379)
- fix: panic when require(esm) (#25769)
- fix: precompile preserve SVG camelCase attributes (#25945)
- fix: reland async context (#25140)
- fix: remove --allow-run warning when using deno without args or subcommand
(#25684)
- fix: remove entrypoint hack for Deno 2.0 (#25332)
- fix: remove recently added deno.json node_modules aliasing (#25542)
- fix: remove the typo in the help message (#25962)
- fix: removed unstable-htttp from deno help (#25216)
- fix: replace `npm install` hint with `deno install` hint (#25244)
- fix: trim space around DENO_AUTH_TOKENS (#25147)
- fix: update deno_doc (#25290)
- fix: Update deno_npm to fix `deno install` with crossws (#25837)
- fix: update hint for `deno add <package>` (#25455)
- fix: update malva in deno to support astro css comments (#25553)
- fix: update nodeModulesDir config JSON schema (#25653)
- fix: update patchver to 0.2 (#25952)
- fix: update sui to 0.4 (#25942)
- fix: upgrade deno_ast 0.42 (#25313)
- fix: upgrade deno_core to 0.307.0 (#25287)
- fix(add/install): default to "latest" tag for npm packages in
`deno add npm:pkg` (#25858)
- fix(bench): Fix table column alignments and NO_COLOR=1 (#25190)
- fix(BREAKING): make dns record types have consistent naming (#25357)
- fix(byonm): resolve npm deps of jsr deps (#25399)
- fix(check): ignore noImplicitOverrides in remote modules (#25854)
- fix(check): move is cjs check from resolving to loading (#25597)
- fix(check): properly surface dependency errors in types file of js file
(#25860)
- fix(cli): `deno task` exit with status 0 (#25637)
- fix(cli): Default to auto with --node-modules-dir flag (#25772)
- fix(cli): handle edge cases around `export`s in doc tests and default export
(#25720)
- fix(cli): Map error kind to `PermissionDenied` when symlinking fails due to
permissions (#25398)
- fix(cli): Only set allow net flag for deno serve if not already allowed all
(#25743)
- fix(cli): Warn on not-run lifecycle scripts with global cache (#25786)
- fix(cli/tools): correct `deno init --serve` template behavior (#25318)
- fix(compile): support 'deno compile' in RC and LTS releases (#25875)
- fix(config): validate export names (#25436)
- fix(coverage): ignore urls from doc testing (#25736)
- fix(doc): surface graph errors as warnings (#25888)
- fix(dts): stabilize `fetch` declaration for use with `Deno.HttpClient`
(#25683)
- fix(ext/console): more precision in console.time (#25723)
- fix(ext/console): prevent duplicate error printing when the cause is assigned
(#25327)
- fix(ext/crypto): ensure EC public keys are exported uncompressed (#25766)
- fix(ext/crypto): fix identity test for x25519 derive bits (#26011)
- fix(ext/crypto): reject empty usages in SubtleCrypto#importKey (#25759)
- fix(ext/crypto): support md4 digest algorithm (#25656)
- fix(ext/crypto): throw DataError for invalid EC key import (#25181)
- fix(ext/fetch): fix lowercase http_proxy classified as https (#25686)
- fix(ext/fetch): percent decode userinfo when parsing proxies (#25229)
- fix(ext/http): do not set localhost to hostname unnecessarily (#24777)
- fix(ext/http): gracefully handle Response.error responses (#25712)
- fix(ext/node): add `FileHandle#writeFile` (#25555)
- fix(ext/node): add `vm.constants` (#25630)
- fix(ext/node): Add missing `node:path` exports (#25567)
- fix(ext/node): Add missing node:fs and node:constants exports (#25568)
- fix(ext/node): add stubs for `node:trace_events` (#25628)
- fix(ext/node): attach console stream properties (#25617)
- fix(ext/node): avoid showing `UNKNOWN` error from TCP handle (#25550)
- fix(ext/node): close upgraded socket when the underlying http connection is
closed (#25387)
- fix(ext/node): delay accept() call 2 ticks in net.Server#listen (#25481)
- fix(ext/node): don't throw error for unsupported signal binding on windows
(#25699)
- fix(ext/node): emit `online` event after worker thread is initialized (#25243)
- fix(ext/node): export `process.allowedNodeEnvironmentFlags` (#25629)
- fix(ext/node): export JWK public key (#25239)
- fix(ext/node): export request and response clases from `http2` module (#25592)
- fix(ext/node): fix `Cipheriv#update(string, undefined)` (#25571)
- fix(ext/node): fix Decipheriv when autoPadding disabled (#25598)
- fix(ext/node): fix process.stdin.pause() (#25864)
- fix(ext/node): Fix vm sandbox object panic (#24985)
- fix(ext/node): http2session ready state (#25143)
- fix(ext/node): Implement detached option in `child_process` (#25218)
- fix(ext/node): import EC JWK keys (#25266)
- fix(ext/node): import JWK octet key pairs (#25180)
- fix(ext/node): import RSA JWK keys (#25267)
- fix(ext/node): register `node:wasi` built-in (#25134)
- fix(ext/node): remove unimplemented promiseHook stubs (#25979)
- fix(ext/node): report freemem() on Linux in bytes (#25511)
- fix(ext/node): Rewrite `node:v8` serialize/deserialize (#25439)
- fix(ext/node): session close during stream setup (#25170)
- fix(ext/node): Stream should be instance of EventEmitter (#25527)
- fix(ext/node): stub `inspector/promises` (#25635)
- fix(ext/node): stub `process.cpuUsage()` (#25462)
- fix(ext/node): stub cpu_info() for OpenBSD (#25807)
- fix(ext/node): support x509 certificates in `createPublicKey` (#25731)
- fix(ext/node): throw when loading `cpu-features` module (#25257)
- fix(ext/node): update aead-gcm-stream to 0.3 (#25261)
- fix(ext/node): use primordials in `ext/node/polyfills/console.ts` (#25572)
- fix(ext/node): use primordials in ext/node/polyfills/wasi.ts (#25608)
- fix(ext/node): validate input lengths in `Cipheriv` and `Decipheriv` (#25570)
- fix(ext/web): don't ignore capture in EventTarget.removeEventListener (#25788)
- fix(ext/webgpu): allow to build on unsupported platforms (#25202)
- fix(ext/webgpu): sync category comment (#25580)
- fix(ext/webstorage): make `getOwnPropertyDescriptor` with symbol return
`undefined` (#13348)
- fix(flags): --allow-all should conflict with lower permissions (#25909)
- fix(flags): don't treat empty run command as task subcommand (#25708)
- fix(flags): move some content from docs.deno.com into help output (#25951)
- fix(flags): properly error out for urls (#25770)
- fix(flags): require global flag for permission flags in install subcommand
(#25391)
- fix(fmt): --check was broken for CSS, YAML and HTML (#25848)
- fix(fmt): fix incorrect quotes in components (#25249)
- fix(fmt): fix tabs in YAML (#25536)
- fix(fmt/markdown): fix regression with multi-line footnotes and inline math
(#25222)
- fix(info): error instead of panic for npm specifiers when using byonm (#25947)
- fix(info): move "version" field to top of json output (#25890)
- fix(inspector): Fix panic when re-entering runtime ops (#25537)
- fix(install): compare versions directly to decide whether to create a child
node_modules dir for a workspace member (#26001)
- fix(install): Make sure target node_modules exists when symlinking (#25494)
- fix(install): recommend using `deno install -g` when using a single http url
(#25388)
- fix(install): store tags associated with package in node_modules dir (#26000)
- fix(install): surface package.json dependency errors (#26023)
- fix(install): Use relative symlinks in deno install (#25164)
- fix(installl): make bin entries executable even if not put in
`node_modules/.bin` (#25873)
- fix(jupyter): allow unstable flags (#25483)
- fix(lint): correctly handle old jsx in linter (#25902)
- fix(lint): support linting jsr pkg without version field (#25230)
- fix(lockfile): use loose deserialization for version constraints (#25660)
- fix(lsp): encode url parts before parsing as uri (#25509)
- fix(lsp): exclude missing import quick fixes with bad resolutions (#26025)
- fix(lsp): panic on url_to_uri() (#25238)
- fix(lsp): properly resolve jsxImportSource for caching (#25688)
- fix(lsp): update diagnostics on npm install (#25352)
- fix(napi): Don't run microtasks in napi_resolve_deferred (#25246)
- fix(napi): Fix worker threads importing already-loaded NAPI addon (#25245)
- fix(no-slow-types): better `override` handling (#25989)
- fix(node): Don't error out if we fail to statically analyze CJS re-export
(#25748)
- fix(node): fix worker_threads issues blocking Angular support (#26024)
- fix(node): implement libuv APIs needed to support `npm:sqlite3` (#25893)
- fix(node): Include "node" condition during CJS re-export analysis (#25785)
- fix(node): Pass NPM_PROCESS_STATE to subprocesses via temp file instead of env
var (#25896)
- fix(node/byonm): do not accidentally resolve bare node built-ins (#25543)
- fix(node/cluster): improve stubs to make log4js work (#25146)
- fix(npm): better error handling for remote npm deps (#25670)
- fix(npm): root package has peer dependency on itself (#26022)
- fix(permissions): disallow any `LD_` or `DYLD_` prefixed env var without full
--allow-run permissions (#25271)
- fix(permissions): disallow launching subprocess with LD_PRELOAD env var
without full run permissions (#25221)
- fix(publish): ensure provenance is spec compliant (#25200)
- fix(regression): do not expose resolved path in Deno.Command permission denied
error (#25434)
- fix(runtime): don't error `child.output()` on consumed stream (#25657)
- fix(runtime): use more null proto objects again (#25040)
- fix(runtime/web_worker): populate `SnapshotOptions` for `WebWorker` when
instantiated without snapshot (#25280)
- fix(task): correct name for scoped npm package binaries (#25390)
- fix(task): support tasks with colons in name in `deno run` (#25233)
- fix(task): use current executable for deno even when not named deno (#26019)
- fix(types): simplify mtls related types (#25658)
- fix(upgrade): more informative information on invalid version (#25319)
- fix(windows): Deno.Command - align binary resolution with linux and mac
(#25429)
- fix(workspace): handle when config has members when specified via --config
(#25988)
- perf: fast path for cached dyn imports (#25636)
- perf: Use -O3 for sui in release builds (#26010)
- perf(cache): single cache file for remote modules (#24983)
- perf(cache): single cache file for typescript emit (#24994)
- perf(ext/fetch): improve decompression throughput by upgrading `tower_http`
(#25806)
- perf(ext/node): reduce some allocations in require (#25197)
- perf(ext/web): optimize performance.measure() (#25774)
### 1.46.3 / 2024.09.04
- feat(upgrade): print info links for Deno 2 RC releases (#25225)

View file

@ -2,7 +2,7 @@
[package]
name = "deno_bench_util"
version = "0.162.0"
version = "0.167.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno"
version = "2.0.0-rc.9"
version = "2.0.2"
authors.workspace = true
default-run = "deno"
edition.workspace = true
@ -38,6 +38,11 @@ path = "./bench/lsp_bench_standalone.rs"
[features]
default = ["upgrade", "__vendored_zlib_ng"]
# A feature that enables heap profiling with dhat on Linux.
# 1. Compile with `cargo build --profile=release-with-debug --features=dhat-heap`
# 2. Run the executable. It will output a dhat-heap.json file.
# 3. Open the json file in https://nnethercote.github.io/dh_view/dh_view.html
dhat-heap = ["dhat"]
# A feature that enables the upgrade subcommand and the background check for
# available updates (of deno binary). This is typically disabled for (Linux)
# distribution packages.
@ -65,10 +70,10 @@ winres.workspace = true
[dependencies]
deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] }
deno_cache_dir = { workspace = true }
deno_config = { version = "=0.35.0", features = ["workspace", "sync"] }
deno_config = { version = "=0.37.2", features = ["workspace", "sync"] }
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
deno_doc = { version = "0.150.1", features = ["html", "syntect"] }
deno_graph = { version = "=0.83.0" }
deno_doc = { version = "0.154.0", default-features = false, features = ["rust", "html", "syntect"] }
deno_graph = { version = "=0.83.3" }
deno_lint = { version = "=0.67.0", features = ["docs"] }
deno_lockfile.workspace = true
deno_npm.workspace = true
@ -77,7 +82,7 @@ deno_path_util.workspace = true
deno_resolver.workspace = true
deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] }
deno_semver.workspace = true
deno_task_shell = "=0.17.0"
deno_task_shell = "=0.18.1"
deno_terminal.workspace = true
eszip = "=0.79.1"
libsui = "0.4.0"
@ -94,14 +99,15 @@ chrono = { workspace = true, features = ["now"] }
clap = { version = "=4.5.16", features = ["env", "string", "wrap_help", "error-context"] }
clap_complete = "=4.5.24"
clap_complete_fig = "=4.5.2"
color-print = "0.3.5"
color-print.workspace = true
console_static_text.workspace = true
dashmap.workspace = true
data-encoding.workspace = true
dhat = { version = "0.3.3", optional = true }
dissimilar = "=1.0.4"
dotenvy = "0.15.7"
dprint-plugin-json = "=0.19.3"
dprint-plugin-jupyter = "=0.1.3"
dprint-plugin-json = "=0.19.4"
dprint-plugin-jupyter = "=0.1.5"
dprint-plugin-markdown = "=0.17.8"
dprint-plugin-typescript = "=0.93.0"
env_logger = "=0.10.0"
@ -117,15 +123,15 @@ http-body-util.workspace = true
hyper-util.workspace = true
import_map = { version = "=0.20.1", features = ["ext"] }
indexmap.workspace = true
jsonc-parser.workspace = true
jsonc-parser = { workspace = true, features = ["cst", "serde"] }
jupyter_runtime = { package = "runtimelib", version = "=0.14.0" }
lazy-regex.workspace = true
libc.workspace = true
libz-sys.workspace = true
log = { workspace = true, features = ["serde"] }
lsp-types.workspace = true
malva = "=0.10.1"
markup_fmt = "=0.13.1"
malva = "=0.11.0"
markup_fmt = "=0.14.0"
memmem.workspace = true
monch.workspace = true
notify.workspace = true
@ -162,7 +168,6 @@ typed-arena = "=2.0.2"
uuid = { workspace = true, features = ["serde"] }
walkdir = "=2.3.2"
which.workspace = true
yoke.workspace = true
zeromq.workspace = true
zip = { version = "2.1.6", default-features = false, features = ["deflate-flate2"] }
zstd.workspace = true
@ -170,12 +175,14 @@ zstd.workspace = true
[target.'cfg(windows)'.dependencies]
junction.workspace = true
winapi = { workspace = true, features = ["knownfolders", "mswsock", "objbase", "shlobj", "tlhelp32", "winbase", "winerror", "winsock2"] }
windows-sys.workspace = true
[target.'cfg(unix)'.dependencies]
nix.workspace = true
[dev-dependencies]
deno_bench_util.workspace = true
libuv-sys-lite = "=1.48.2"
pretty_assertions.workspace = true
test_util.workspace = true

View file

@ -575,11 +575,21 @@ fn parse_packages_allowed_scripts(s: &str) -> Result<String, AnyError> {
pub struct UnstableConfig {
// TODO(bartlomieju): remove in Deno 2.5
pub legacy_flag_enabled: bool, // --unstable
pub bare_node_builtins: bool, // --unstable-bare-node-builts
pub bare_node_builtins: bool,
pub detect_cjs: bool,
pub sloppy_imports: bool,
pub features: Vec<String>, // --unstabe-kv --unstable-cron
}
#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct InternalFlags {
/// Used when the language server is configured with an
/// explicit cache option.
pub cache_path: Option<PathBuf>,
/// Only reads to the lockfile instead of writing to it.
pub lockfile_skip_write: bool,
}
#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct Flags {
/// Vector of CLI arguments - these are user script arguments, all Deno
@ -591,9 +601,6 @@ pub struct Flags {
pub ca_stores: Option<Vec<String>>,
pub ca_data: Option<CaData>,
pub cache_blocklist: Vec<String>,
/// This is not exposed as an option in the CLI, it is used internally when
/// the language server is configured with an explicit cache option.
pub cache_path: Option<PathBuf>,
pub cached_only: bool,
pub type_check_mode: TypeCheckMode,
pub config_flag: ConfigFlag,
@ -602,6 +609,8 @@ pub struct Flags {
pub enable_op_summary_metrics: bool,
pub enable_testing_features: bool,
pub ext: Option<String>,
/// Flags that aren't exposed in the CLI, but are used internally.
pub internal: InternalFlags,
pub ignore: Vec<String>,
pub import_map_path: Option<String>,
pub env_file: Option<String>,
@ -688,9 +697,10 @@ impl PermissionFlags {
}
let builtin_allowed_import_hosts = [
"jsr.io:443",
"deno.land:443",
"esm.sh:443",
"jsr.io:443",
"cdn.jsdelivr.net:443",
"raw.githubusercontent.com:443",
"gist.githubusercontent.com:443",
];
@ -1168,7 +1178,7 @@ static DENO_HELP: &str = cstr!(
<y>Dependency management:</>
<g>add</> Add dependencies
<p(245)>deno add @std/assert | deno add npm:express</>
<p(245)>deno add jsr:@std/assert | deno add npm:express</>
<g>install</> Install script as an executable
<g>uninstall</> Uninstall a script previously installed with deno install
<g>remove</> Remove dependencies from the configuration file
@ -1333,7 +1343,7 @@ pub fn flags_from_vec(args: Vec<OsString>) -> clap::error::Result<Flags> {
}
match subcommand.as_str() {
"add" => add_parse(&mut flags, &mut m),
"add" => add_parse(&mut flags, &mut m)?,
"remove" => remove_parse(&mut flags, &mut m),
"bench" => bench_parse(&mut flags, &mut m)?,
"bundle" => bundle_parse(&mut flags, &mut m),
@ -1519,7 +1529,7 @@ pub fn clap_root() -> Command {
);
run_args(Command::new("deno"), true)
.args(unstable_args(UnstableArgsConfig::ResolutionAndRuntime))
.with_unstable_args(UnstableArgsConfig::ResolutionAndRuntime)
.next_line_help(false)
.bin_name("deno")
.styles(
@ -1621,7 +1631,7 @@ fn command(
) -> Command {
Command::new(name)
.about(about)
.args(unstable_args(unstable_args_config))
.with_unstable_args(unstable_args_config)
}
fn help_subcommand(app: &Command) -> Command {
@ -1649,10 +1659,10 @@ fn add_subcommand() -> Command {
"add",
cstr!(
"Add dependencies to your configuration file.
<p(245)>deno add @std/path</>
<p(245)>deno add jsr:@std/path</>
You can add multiple dependencies at once:
<p(245)>deno add @std/path @std/assert</>"
<p(245)>deno add jsr:@std/path jsr:@std/assert</>"
),
UnstableArgsConfig::None,
)
@ -1666,6 +1676,7 @@ You can add multiple dependencies at once:
.action(ArgAction::Append),
)
.arg(add_dev_arg())
.arg(allow_scripts_arg())
})
}
@ -1708,7 +1719,7 @@ If you specify a directory instead of a file, the path is expanded to all contai
UnstableArgsConfig::ResolutionAndRuntime,
)
.defer(|cmd| {
runtime_args(cmd, true, false)
runtime_args(cmd, true, false, true)
.arg(check_arg(true))
.arg(
Arg::new("json")
@ -1845,6 +1856,7 @@ Unless --reload is specified, this command will not re-download already cached d
.required_unless_present("help")
.value_hint(ValueHint::FilePath),
)
.arg(frozen_lockfile_arg())
.arg(allow_import_arg())
}
)
@ -1872,7 +1884,7 @@ On the first invocation with deno will download the proper binary and cache it i
UnstableArgsConfig::ResolutionAndRuntime,
)
.defer(|cmd| {
runtime_args(cmd, true, false)
runtime_args(cmd, true, false, true)
.arg(check_arg(true))
.arg(
Arg::new("include")
@ -2193,7 +2205,7 @@ This command has implicit access to all permissions.
UnstableArgsConfig::ResolutionAndRuntime,
)
.defer(|cmd| {
runtime_args(cmd, false, true)
runtime_args(cmd, false, true, true)
.arg(check_arg(false))
.arg(executable_ext_arg())
.arg(
@ -2459,7 +2471,7 @@ in the package cache. If no dependency is specified, installs all dependencies l
If the <p(245)>--entrypoint</> flag is passed, installs the dependencies of the specified entrypoint(s).
<p(245)>deno install</>
<p(245)>deno install @std/bytes</>
<p(245)>deno install jsr:@std/bytes</>
<p(245)>deno install npm:chalk</>
<p(245)>deno install --entrypoint entry1.ts entry2.ts</>
@ -2492,7 +2504,7 @@ The installation root is determined, in order of precedence:
These must be added to the path manually if required."), UnstableArgsConfig::ResolutionAndRuntime)
.visible_alias("i")
.defer(|cmd| {
permission_args(runtime_args(cmd, false, true), Some("global"))
permission_args(runtime_args(cmd, false, true, false), Some("global"))
.arg(check_arg(true))
.arg(allow_scripts_arg())
.arg(
@ -2758,8 +2770,13 @@ It is especially useful for quick prototyping and checking snippets of code.
TypeScript is supported, however it is not type-checked, only transpiled."
), UnstableArgsConfig::ResolutionAndRuntime)
.defer(|cmd| runtime_args(cmd, true, true)
.arg(check_arg(false))
.defer(|cmd| {
let cmd = compile_args_without_check_args(cmd);
let cmd = inspect_args(cmd);
let cmd = permission_args(cmd, None);
let cmd = runtime_misc_args(cmd);
cmd
.arg(
Arg::new("eval-file")
.long("eval-file")
@ -2778,7 +2795,7 @@ TypeScript is supported, however it is not type-checked, only transpiled."
.after_help(cstr!("<y>Environment variables:</>
<g>DENO_REPL_HISTORY</> Set REPL history file path. History file is disabled when the value is empty.
<p(245)>[default: $DENO_DIR/deno_history.txt]</>"))
)
})
.arg(env_file_arg())
.arg(
Arg::new("args")
@ -2790,7 +2807,7 @@ TypeScript is supported, however it is not type-checked, only transpiled."
}
fn run_args(command: Command, top_level: bool) -> Command {
runtime_args(command, true, true)
runtime_args(command, true, true, true)
.arg(check_arg(false))
.arg(watch_arg(true))
.arg(hmr_arg(true))
@ -2846,7 +2863,7 @@ Start a server defined in server.ts:
Start a server defined in server.ts, watching for changes and running on port 5050:
<p(245)>deno serve --watch --port 5050 server.ts</>
<y>Read more:</> <c>https://docs.deno.com/go/serve</>"), UnstableArgsConfig::ResolutionAndRuntime), true, true)
<y>Read more:</> <c>https://docs.deno.com/go/serve</>"), UnstableArgsConfig::ResolutionAndRuntime), true, true, true)
.arg(
Arg::new("port")
.long("port")
@ -2901,6 +2918,7 @@ List all available tasks:
.help("Specify the directory to run the task in")
.value_hint(ValueHint::DirPath),
)
.arg(node_modules_dir_arg())
})
}
@ -2919,7 +2937,7 @@ or <c>**/__tests__/**</>:
UnstableArgsConfig::ResolutionAndRuntime
)
.defer(|cmd|
runtime_args(cmd, true, true)
runtime_args(cmd, true, true, true)
.arg(check_arg(true))
.arg(
Arg::new("ignore")
@ -3253,7 +3271,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
<g>-W, --allow-write[=<<PATH>...]</> Allow file system write access. Optionally specify allowed paths.
<p(245)>--allow-write | --allow-write="/etc,/var/log.txt"</>
<g>-I, --allow-import[=<<IP_OR_HOSTNAME>...]</> Allow importing from remote hosts. Optionally specify allowed IP addresses and host names, with ports as necessary.
Default value: <p(245)>deno.land:443,jsr.io:443,esm.sh:443,raw.githubusercontent.com:443,user.githubusercontent.com:443</>
Default value: <p(245)>deno.land:443,jsr.io:443,esm.sh:443,cdn.jsdelivr.net:443,raw.githubusercontent.com:443,user.githubusercontent.com:443</>
<p(245)>--allow-import | --allow-import="example.com,github.com"</>
<g>-N, --allow-net[=<<IP_OR_HOSTNAME>...]</> Allow network access. Optionally specify allowed IP addresses and host names, with ports as necessary.
<p(245)>--allow-net | --allow-net="localhost:8080,deno.land"</>
@ -3632,6 +3650,7 @@ fn runtime_args(
app: Command,
include_perms: bool,
include_inspector: bool,
include_allow_scripts: bool,
) -> Command {
let app = compile_args(app);
let app = if include_perms {
@ -3644,6 +3663,15 @@ fn runtime_args(
} else {
app
};
let app = if include_allow_scripts {
app.arg(allow_scripts_arg())
} else {
app
};
runtime_misc_args(app)
}
fn runtime_misc_args(app: Command) -> Command {
app
.arg(frozen_lockfile_arg())
.arg(cached_only_arg())
@ -3663,7 +3691,7 @@ fn allow_import_arg() -> Arg {
.require_equals(true)
.value_name("IP_OR_HOSTNAME")
.help(cstr!(
"Allow importing from remote hosts. Optionally specify allowed IP addresses and host names, with ports as necessary. Default value: <p(245)>deno.land:443,jsr.io:443,esm.sh:443,raw.githubusercontent.com:443,user.githubusercontent.com:443</>"
"Allow importing from remote hosts. Optionally specify allowed IP addresses and host names, with ports as necessary. Default value: <p(245)>deno.land:443,jsr.io:443,esm.sh:443,cdn.jsdelivr.net:443,raw.githubusercontent.com:443,user.githubusercontent.com:443</>"
))
.value_parser(flags_net::validator)
}
@ -4125,23 +4153,29 @@ enum UnstableArgsConfig {
ResolutionAndRuntime,
}
struct UnstableArgsIter {
idx: usize,
cfg: UnstableArgsConfig,
trait CommandExt {
fn with_unstable_args(self, cfg: UnstableArgsConfig) -> Self;
}
impl Iterator for UnstableArgsIter {
type Item = Arg;
impl CommandExt for Command {
fn with_unstable_args(self, cfg: UnstableArgsConfig) -> Self {
let mut next_display_order = {
let mut value = 1000;
move || {
value += 1;
value
}
};
fn next(&mut self) -> Option<Self::Item> {
let arg = if self.idx == 0 {
let mut cmd = self.arg(
Arg::new("unstable")
.long("unstable")
.help(cstr!("Enable all unstable features and APIs. Instead of using this flag, consider enabling individual unstable features
.long("unstable")
.help(cstr!("Enable all unstable features and APIs. Instead of using this flag, consider enabling individual unstable features
<p(245)>To view the list of individual unstable feature flags, run this command again with --help=unstable</>"))
.action(ArgAction::SetTrue)
.hide(matches!(self.cfg, UnstableArgsConfig::None))
} else if self.idx == 1 {
.action(ArgAction::SetTrue)
.hide(matches!(cfg, UnstableArgsConfig::None))
.display_order(next_display_order())
).arg(
Arg::new("unstable-bare-node-builtins")
.long("unstable-bare-node-builtins")
.help("Enable unstable bare node builtins feature")
@ -4149,20 +4183,36 @@ impl Iterator for UnstableArgsIter {
.value_parser(FalseyValueParser::new())
.action(ArgAction::SetTrue)
.hide(true)
.long_help(match self.cfg {
.long_help(match cfg {
UnstableArgsConfig::None => None,
UnstableArgsConfig::ResolutionOnly
| UnstableArgsConfig::ResolutionAndRuntime => Some("true"),
})
.help_heading(UNSTABLE_HEADING)
} else if self.idx == 2 {
.display_order(next_display_order()),
).arg(
Arg::new("unstable-detect-cjs")
.long("unstable-detect-cjs")
.help("Reads the package.json type field in a project to treat .js files as .cjs")
.value_parser(FalseyValueParser::new())
.action(ArgAction::SetTrue)
.hide(true)
.long_help(match cfg {
UnstableArgsConfig::None => None,
UnstableArgsConfig::ResolutionOnly
| UnstableArgsConfig::ResolutionAndRuntime => Some("true"),
})
.help_heading(UNSTABLE_HEADING)
.display_order(next_display_order())
).arg(
Arg::new("unstable-byonm")
.long("unstable-byonm")
.value_parser(FalseyValueParser::new())
.action(ArgAction::SetTrue)
.hide(true)
.help_heading(UNSTABLE_HEADING)
} else if self.idx == 3 {
.display_order(next_display_order()),
).arg(
Arg::new("unstable-sloppy-imports")
.long("unstable-sloppy-imports")
.help("Enable unstable resolving of specifiers by extension probing, .js to .ts, and directory probing")
@ -4170,40 +4220,39 @@ impl Iterator for UnstableArgsIter {
.value_parser(FalseyValueParser::new())
.action(ArgAction::SetTrue)
.hide(true)
.long_help(match self.cfg {
.long_help(match cfg {
UnstableArgsConfig::None => None,
UnstableArgsConfig::ResolutionOnly | UnstableArgsConfig::ResolutionAndRuntime => Some("true")
})
.help_heading(UNSTABLE_HEADING)
} else if self.idx > 3 {
let granular_flag = crate::UNSTABLE_GRANULAR_FLAGS.get(self.idx - 4)?;
Arg::new(format!("unstable-{}", granular_flag.name))
.long(format!("unstable-{}", granular_flag.name))
.help(granular_flag.help_text)
.action(ArgAction::SetTrue)
.hide(true)
.help_heading(UNSTABLE_HEADING)
// we don't render long help, so using it here as a sort of metadata
.long_help(if granular_flag.show_in_help {
match self.cfg {
UnstableArgsConfig::None | UnstableArgsConfig::ResolutionOnly => {
None
}
UnstableArgsConfig::ResolutionAndRuntime => Some("true"),
}
} else {
None
})
} else {
return None;
};
self.idx += 1;
Some(arg.display_order(self.idx + 1000))
}
}
.display_order(next_display_order())
);
fn unstable_args(cfg: UnstableArgsConfig) -> impl IntoIterator<Item = Arg> {
UnstableArgsIter { idx: 0, cfg }
for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS.iter() {
cmd = cmd.arg(
Arg::new(format!("unstable-{}", granular_flag.name))
.long(format!("unstable-{}", granular_flag.name))
.help(granular_flag.help_text)
.action(ArgAction::SetTrue)
.hide(true)
.help_heading(UNSTABLE_HEADING)
// we don't render long help, so using it here as a sort of metadata
.long_help(if granular_flag.show_in_help {
match cfg {
UnstableArgsConfig::None | UnstableArgsConfig::ResolutionOnly => {
None
}
UnstableArgsConfig::ResolutionAndRuntime => Some("true"),
}
} else {
None
})
.display_order(next_display_order()),
);
}
cmd
}
}
fn allow_scripts_arg_parse(
@ -4225,8 +4274,13 @@ fn allow_scripts_arg_parse(
Ok(())
}
fn add_parse(flags: &mut Flags, matches: &mut ArgMatches) {
fn add_parse(
flags: &mut Flags,
matches: &mut ArgMatches,
) -> clap::error::Result<()> {
allow_scripts_arg_parse(flags, matches)?;
flags.subcommand = DenoSubcommand::Add(add_parse_inner(matches, None));
Ok(())
}
fn add_parse_inner(
@ -4252,7 +4306,7 @@ fn bench_parse(
) -> clap::error::Result<()> {
flags.type_check_mode = TypeCheckMode::Local;
runtime_args_parse(flags, matches, true, false)?;
runtime_args_parse(flags, matches, true, false, true)?;
ext_arg_parse(flags, matches);
// NOTE: `deno bench` always uses `--no-prompt`, tests shouldn't ever do
@ -4320,6 +4374,7 @@ fn check_parse(
flags.type_check_mode = TypeCheckMode::Local;
compile_args_without_check_parse(flags, matches)?;
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime);
frozen_lockfile_arg_parse(flags, matches);
let files = matches.remove_many::<String>("file").unwrap().collect();
if matches.get_flag("all") || matches.get_flag("remote") {
flags.type_check_mode = TypeCheckMode::All;
@ -4342,7 +4397,7 @@ fn compile_parse(
matches: &mut ArgMatches,
) -> clap::error::Result<()> {
flags.type_check_mode = TypeCheckMode::Local;
runtime_args_parse(flags, matches, true, false)?;
runtime_args_parse(flags, matches, true, false, true)?;
let mut script = matches.remove_many::<String>("script_arg").unwrap();
let source_file = script.next().unwrap();
@ -4517,7 +4572,7 @@ fn eval_parse(
flags: &mut Flags,
matches: &mut ArgMatches,
) -> clap::error::Result<()> {
runtime_args_parse(flags, matches, false, true)?;
runtime_args_parse(flags, matches, false, true, false)?;
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime);
flags.allow_all();
@ -4610,7 +4665,7 @@ fn install_parse(
flags: &mut Flags,
matches: &mut ArgMatches,
) -> clap::error::Result<()> {
runtime_args_parse(flags, matches, true, true)?;
runtime_args_parse(flags, matches, true, true, false)?;
let global = matches.get_flag("global");
if global {
@ -4836,8 +4891,18 @@ fn repl_parse(
flags: &mut Flags,
matches: &mut ArgMatches,
) -> clap::error::Result<()> {
runtime_args_parse(flags, matches, true, true)?;
unsafely_ignore_certificate_errors_parse(flags, matches);
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime);
compile_args_without_check_parse(flags, matches)?;
cached_only_arg_parse(flags, matches);
frozen_lockfile_arg_parse(flags, matches);
permission_args_parse(flags, matches)?;
inspect_arg_parse(flags, matches);
location_arg_parse(flags, matches);
v8_flags_arg_parse(flags, matches);
seed_arg_parse(flags, matches);
enable_testing_features_arg_parse(flags, matches);
env_file_arg_parse(flags, matches);
strace_ops_parse(flags, matches);
let eval_files = matches
.remove_many::<String>("eval-file")
@ -4869,7 +4934,7 @@ fn run_parse(
mut app: Command,
bare: bool,
) -> clap::error::Result<()> {
runtime_args_parse(flags, matches, true, true)?;
runtime_args_parse(flags, matches, true, true, true)?;
ext_arg_parse(flags, matches);
flags.code_cache_enabled = !matches.get_flag("no-code-cache");
@ -4910,7 +4975,7 @@ fn serve_parse(
let worker_count = parallel_arg_parse(matches).map(|v| v.get());
runtime_args_parse(flags, matches, true, true)?;
runtime_args_parse(flags, matches, true, true, true)?;
// If the user didn't pass --allow-net, add this port to the network
// allowlist. If the host is 0.0.0.0, we add :{port} and allow the same network perms
// as if it was passed to --allow-net directly.
@ -4965,6 +5030,7 @@ fn task_parse(flags: &mut Flags, matches: &mut ArgMatches) {
.unwrap_or(ConfigFlag::Discover);
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime);
node_modules_arg_parse(flags, matches);
let mut task_flags = TaskFlags {
cwd: matches.remove_one::<String>("cwd"),
@ -5004,7 +5070,7 @@ fn test_parse(
matches: &mut ArgMatches,
) -> clap::error::Result<()> {
flags.type_check_mode = TypeCheckMode::Local;
runtime_args_parse(flags, matches, true, true)?;
runtime_args_parse(flags, matches, true, true, true)?;
ext_arg_parse(flags, matches);
// NOTE: `deno test` always uses `--no-prompt`, tests shouldn't ever do
@ -5369,6 +5435,7 @@ fn runtime_args_parse(
matches: &mut ArgMatches,
include_perms: bool,
include_inspector: bool,
include_allow_scripts: bool,
) -> clap::error::Result<()> {
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime);
compile_args_parse(flags, matches)?;
@ -5380,6 +5447,9 @@ fn runtime_args_parse(
if include_inspector {
inspect_arg_parse(flags, matches);
}
if include_allow_scripts {
allow_scripts_arg_parse(flags, matches)?;
}
location_arg_parse(flags, matches);
v8_flags_arg_parse(flags, matches);
seed_arg_parse(flags, matches);
@ -5651,6 +5721,7 @@ fn unstable_args_parse(
flags.unstable_config.bare_node_builtins =
matches.get_flag("unstable-bare-node-builtins");
flags.unstable_config.detect_cjs = matches.get_flag("unstable-detect-cjs");
flags.unstable_config.sloppy_imports =
matches.get_flag("unstable-sloppy-imports");
@ -7379,7 +7450,7 @@ mod tests {
#[test]
fn repl_with_flags() {
#[rustfmt::skip]
let r = flags_from_vec(svec!["deno", "repl", "-A", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--unsafely-ignore-certificate-errors", "--env=.example.env"]);
let r = flags_from_vec(svec!["deno", "repl", "-A", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--unsafely-ignore-certificate-errors", "--env=.example.env"]);
assert_eq!(
r.unwrap(),
Flags {
@ -7427,7 +7498,6 @@ mod tests {
allow_write: Some(vec![]),
..Default::default()
},
type_check_mode: TypeCheckMode::None,
..Flags::default()
}
);
@ -7449,7 +7519,6 @@ mod tests {
eval: None,
is_default_command: false,
}),
type_check_mode: TypeCheckMode::None,
..Flags::default()
}
);
@ -8851,8 +8920,12 @@ mod tests {
#[test]
fn test_no_colon_in_value_name() {
let app =
runtime_args(Command::new("test_inspect_completion_value"), true, true);
let app = runtime_args(
Command::new("test_inspect_completion_value"),
true,
true,
false,
);
let inspect_args = app
.get_arguments()
.filter(|arg| arg.get_id() == "inspect")

View file

@ -24,11 +24,20 @@ use crate::args::InstallKind;
use deno_lockfile::Lockfile;
#[derive(Debug)]
pub struct CliLockfileReadFromPathOptions {
pub file_path: PathBuf,
pub frozen: bool,
/// Causes the lockfile to only be read from, but not written to.
pub skip_write: bool,
}
#[derive(Debug)]
pub struct CliLockfile {
lockfile: Mutex<Lockfile>,
pub filename: PathBuf,
pub frozen: bool,
frozen: bool,
skip_write: bool,
}
pub struct Guard<'a, T> {
@ -50,15 +59,6 @@ impl<'a, T> std::ops::DerefMut for Guard<'a, T> {
}
impl CliLockfile {
pub fn new(lockfile: Lockfile, frozen: bool) -> Self {
let filename = lockfile.filename.clone();
Self {
lockfile: Mutex::new(lockfile),
filename,
frozen,
}
}
/// Get the inner deno_lockfile::Lockfile.
pub fn lock(&self) -> Guard<Lockfile> {
Guard {
@ -78,6 +78,10 @@ impl CliLockfile {
}
pub fn write_if_changed(&self) -> Result<(), AnyError> {
if self.skip_write {
return Ok(());
}
self.error_if_changed()?;
let mut lockfile = self.lockfile.lock();
let Some(bytes) = lockfile.resolve_write_bytes() else {
@ -142,7 +146,7 @@ impl CliLockfile {
return Ok(None);
}
let filename = match flags.lock {
let file_path = match flags.lock {
Some(ref lock) => PathBuf::from(lock),
None => match workspace.resolve_lockfile_path()? {
Some(path) => path,
@ -160,7 +164,11 @@ impl CliLockfile {
.unwrap_or(false)
});
let lockfile = Self::read_from_path(filename, frozen)?;
let lockfile = Self::read_from_path(CliLockfileReadFromPathOptions {
file_path,
frozen,
skip_write: flags.internal.lockfile_skip_write,
})?;
// initialize the lockfile with the workspace's configuration
let root_url = workspace.root_dir();
@ -212,25 +220,29 @@ impl CliLockfile {
}
pub fn read_from_path(
file_path: PathBuf,
frozen: bool,
opts: CliLockfileReadFromPathOptions,
) -> Result<CliLockfile, AnyError> {
match std::fs::read_to_string(&file_path) {
Ok(text) => Ok(CliLockfile::new(
Lockfile::new(deno_lockfile::NewLockfileOptions {
file_path,
content: &text,
overwrite: false,
})?,
frozen,
)),
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(
CliLockfile::new(Lockfile::new_empty(file_path, false), frozen),
),
Err(err) => Err(err).with_context(|| {
format!("Failed reading lockfile '{}'", file_path.display())
}),
}
let lockfile = match std::fs::read_to_string(&opts.file_path) {
Ok(text) => Lockfile::new(deno_lockfile::NewLockfileOptions {
file_path: opts.file_path,
content: &text,
overwrite: false,
})?,
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
Lockfile::new_empty(opts.file_path, false)
}
Err(err) => {
return Err(err).with_context(|| {
format!("Failed reading lockfile '{}'", opts.file_path.display())
});
}
};
Ok(CliLockfile {
filename: lockfile.filename.clone(),
lockfile: Mutex::new(lockfile),
frozen: opts.frozen,
skip_write: opts.skip_write,
})
}
pub fn error_if_changed(&self) -> Result<(), AnyError> {

View file

@ -44,6 +44,7 @@ pub use deno_config::glob::FilePatterns;
pub use deno_json::check_warn_tsconfig;
pub use flags::*;
pub use lockfile::CliLockfile;
pub use lockfile::CliLockfileReadFromPathOptions;
pub use package_json::NpmInstallDepsProvider;
use deno_ast::ModuleSpecifier;
@ -824,11 +825,9 @@ impl CliOptions {
}
}
warn_insecure_allow_run_flags(&flags);
let maybe_lockfile = maybe_lockfile.filter(|_| !force_global_cache);
let deno_dir_provider =
Arc::new(DenoDirProvider::new(flags.cache_path.clone()));
Arc::new(DenoDirProvider::new(flags.internal.cache_path.clone()));
let maybe_node_modules_folder = resolve_node_modules_folder(
&initial_cwd,
&flags,
@ -964,6 +963,9 @@ impl CliOptions {
match self.sub_command() {
DenoSubcommand::Cache(_) => GraphKind::All,
DenoSubcommand::Check(_) => GraphKind::TypesOnly,
DenoSubcommand::Install(InstallFlags {
kind: InstallKind::Local(_),
}) => GraphKind::All,
_ => self.type_check_mode().as_graph_kind(),
}
}
@ -1577,6 +1579,11 @@ impl CliOptions {
|| self.workspace().has_unstable("bare-node-builtins")
}
pub fn unstable_detect_cjs(&self) -> bool {
self.flags.unstable_config.detect_cjs
|| self.workspace().has_unstable("detect-cjs")
}
fn byonm_enabled(&self) -> bool {
// check if enabled via unstable
self.node_modules_dir().ok().flatten() == Some(NodeModulesDirMode::Manual)
@ -1621,21 +1628,17 @@ impl CliOptions {
});
if !from_config_file.is_empty() {
// collect unstable granular flags
let mut all_valid_unstable_flags: Vec<&str> =
crate::UNSTABLE_GRANULAR_FLAGS
.iter()
.map(|granular_flag| granular_flag.name)
.collect();
let mut another_unstable_flags = Vec::from([
"sloppy-imports",
"byonm",
"bare-node-builtins",
"fmt-component",
]);
// add more unstable flags to the same vector holding granular flags
all_valid_unstable_flags.append(&mut another_unstable_flags);
let all_valid_unstable_flags: Vec<&str> = crate::UNSTABLE_GRANULAR_FLAGS
.iter()
.map(|granular_flag| granular_flag.name)
.chain([
"sloppy-imports",
"byonm",
"bare-node-builtins",
"fmt-component",
"detect-cjs",
])
.collect();
// check and warn if the unstable flag of config file isn't supported, by
// iterating through the vector holding the unstable flags
@ -1710,27 +1713,6 @@ impl CliOptions {
}
}
/// Warns for specific uses of `--allow-run`. This function is not
/// intended to catch every single possible insecure use of `--allow-run`,
/// but is just an attempt to discourage some common pitfalls.
fn warn_insecure_allow_run_flags(flags: &Flags) {
let permissions = &flags.permissions;
if permissions.allow_all {
return;
}
let Some(allow_run_list) = permissions.allow_run.as_ref() else {
return;
};
// discourage using --allow-run without an allow list
if allow_run_list.is_empty() {
log::warn!(
"{} --allow-run without an allow list is susceptible to exploits. Prefer specifying an allow list (https://docs.deno.com/runtime/fundamentals/security/#running-subprocesses)",
colors::yellow("Warning")
);
}
}
/// Resolves the path to use for a local node_modules folder.
fn resolve_node_modules_folder(
cwd: &Path,

View file

@ -6,6 +6,7 @@ use std::sync::Arc;
use deno_config::workspace::Workspace;
use deno_core::serde_json;
use deno_package_json::PackageJsonDepValue;
use deno_package_json::PackageJsonDepValueParseError;
use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageReq;
@ -26,6 +27,7 @@ pub struct InstallNpmWorkspacePkg {
pub struct NpmInstallDepsProvider {
remote_pkgs: Vec<InstallNpmRemotePkg>,
workspace_pkgs: Vec<InstallNpmWorkspacePkg>,
pkg_json_dep_errors: Vec<PackageJsonDepValueParseError>,
}
impl NpmInstallDepsProvider {
@ -37,6 +39,7 @@ impl NpmInstallDepsProvider {
// todo(dsherret): estimate capacity?
let mut workspace_pkgs = Vec::new();
let mut remote_pkgs = Vec::new();
let mut pkg_json_dep_errors = Vec::new();
let workspace_npm_pkgs = workspace.npm_packages();
for (_, folder) in workspace.config_folders() {
@ -83,8 +86,12 @@ impl NpmInstallDepsProvider {
let deps = pkg_json.resolve_local_package_json_deps();
let mut pkg_pkgs = Vec::with_capacity(deps.len());
for (alias, dep) in deps {
let Ok(dep) = dep else {
continue;
let dep = match dep {
Ok(dep) => dep,
Err(err) => {
pkg_json_dep_errors.push(err);
continue;
}
};
match dep {
PackageJsonDepValue::Req(pkg_req) => {
@ -131,14 +138,19 @@ impl NpmInstallDepsProvider {
Self {
remote_pkgs,
workspace_pkgs,
pkg_json_dep_errors,
}
}
pub fn remote_pkgs(&self) -> &Vec<InstallNpmRemotePkg> {
pub fn remote_pkgs(&self) -> &[InstallNpmRemotePkg] {
&self.remote_pkgs
}
pub fn workspace_pkgs(&self) -> &Vec<InstallNpmWorkspacePkg> {
pub fn workspace_pkgs(&self) -> &[InstallNpmWorkspacePkg] {
&self.workspace_pkgs
}
pub fn pkg_json_dep_errors(&self) -> &[PackageJsonDepValueParseError] {
&self.pkg_json_dep_errors
}
}

View file

@ -150,7 +150,11 @@ fn bench_big_file_edits(deno_exe: &Path) -> Duration {
.deno_exe(deno_exe)
.build();
client.initialize_default();
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.change_configuration(json!({ "deno": { "enable": true } }));
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.write_notification(
"textDocument/didOpen",
@ -206,6 +210,8 @@ fn bench_code_lens(deno_exe: &Path) -> Duration {
.deno_exe(deno_exe)
.build();
client.initialize_default();
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.change_configuration(json!({ "deno": {
"enable": true,
"codeLens": {
@ -214,6 +220,8 @@ fn bench_code_lens(deno_exe: &Path) -> Duration {
"test": true,
},
} }));
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.write_notification(
"textDocument/didOpen",
@ -257,7 +265,11 @@ fn bench_find_replace(deno_exe: &Path) -> Duration {
.deno_exe(deno_exe)
.build();
client.initialize_default();
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.change_configuration(json!({ "deno": { "enable": true } }));
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
for i in 0..10 {
client.write_notification(
@ -341,7 +353,11 @@ fn bench_startup_shutdown(deno_exe: &Path) -> Duration {
.deno_exe(deno_exe)
.build();
client.initialize_default();
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.change_configuration(json!({ "deno": { "enable": true } }));
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.write_notification(
"textDocument/didOpen",

View file

@ -13,7 +13,11 @@ use test_util::lsp::LspClientBuilder;
fn incremental_change_wait(bench: &mut Bencher) {
let mut client = LspClientBuilder::new().use_diagnostic_sync(false).build();
client.initialize_default();
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.change_configuration(json!({ "deno": { "enable": true } }));
let (method, _): (String, Option<Value>) = client.read_notification();
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
client.write_notification(
"textDocument/didOpen",

View file

@ -387,6 +387,8 @@ fn main() {
"Missing symbols list! Generate using tools/napi/generate_symbols_lists.js",
);
println!("cargo:rustc-rerun-if-changed={}", symbols_path.display());
#[cfg(target_os = "windows")]
println!(
"cargo:rustc-link-arg-bin=deno=/DEF:{}",

57
cli/cache/mod.rs vendored
View file

@ -9,10 +9,13 @@ use crate::file_fetcher::FetchPermissionsOptionRef;
use crate::file_fetcher::FileFetcher;
use crate::file_fetcher::FileOrRedirect;
use crate::npm::CliNpmResolver;
use crate::resolver::CliNodeResolver;
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 crate::util::path::specifier_has_extension;
use crate::util::text_encoding::arc_str_to_bytes;
use crate::util::text_encoding::from_utf8_lossy_owned;
use deno_ast::MediaType;
use deno_core::futures;
@ -57,6 +60,7 @@ pub use fast_check::FastCheckCache;
pub use incremental::IncrementalCache;
pub use module_info::ModuleInfoCache;
pub use node::NodeAnalysisCache;
pub use parsed_source::EsmOrCjsChecker;
pub use parsed_source::LazyGraphSourceParser;
pub use parsed_source::ParsedSourceCache;
@ -177,37 +181,46 @@ pub struct FetchCacherOptions {
pub permissions: PermissionsContainer,
/// If we're publishing for `deno publish`.
pub is_deno_publish: bool,
pub unstable_detect_cjs: bool,
}
/// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides
/// a concise interface to the DENO_DIR when building module graphs.
pub struct FetchCacher {
file_fetcher: Arc<FileFetcher>,
pub file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
file_fetcher: Arc<FileFetcher>,
global_http_cache: Arc<GlobalHttpCache>,
node_resolver: Arc<CliNodeResolver>,
npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>,
permissions: PermissionsContainer,
cache_info_enabled: bool,
is_deno_publish: bool,
unstable_detect_cjs: bool,
cache_info_enabled: bool,
}
impl FetchCacher {
pub fn new(
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
file_fetcher: Arc<FileFetcher>,
global_http_cache: Arc<GlobalHttpCache>,
node_resolver: Arc<CliNodeResolver>,
npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>,
options: FetchCacherOptions,
) -> Self {
Self {
file_fetcher,
esm_or_cjs_checker,
global_http_cache,
node_resolver,
npm_resolver,
module_info_cache,
file_header_overrides: options.file_header_overrides,
permissions: options.permissions,
is_deno_publish: options.is_deno_publish,
unstable_detect_cjs: options.unstable_detect_cjs,
cache_info_enabled: false,
}
}
@ -282,6 +295,46 @@ impl Loader for FetchCacher {
},
))));
}
if self.unstable_detect_cjs && specifier_has_extension(specifier, "js") {
if let Ok(Some(pkg_json)) =
self.node_resolver.get_closest_package_json(specifier)
{
if pkg_json.typ == "commonjs" {
if let Ok(path) = specifier.to_file_path() {
if let Ok(bytes) = std::fs::read(&path) {
let text: Arc<str> = from_utf8_lossy_owned(bytes).into();
let is_es_module = match self.esm_or_cjs_checker.is_esm(
specifier,
text.clone(),
MediaType::JavaScript,
) {
Ok(value) => value,
Err(err) => {
return Box::pin(futures::future::ready(Err(err.into())));
}
};
if !is_es_module {
self.node_resolver.mark_cjs_resolution(specifier.clone());
return Box::pin(futures::future::ready(Ok(Some(
LoadResponse::External {
specifier: specifier.clone(),
},
))));
} else {
return Box::pin(futures::future::ready(Ok(Some(
LoadResponse::Module {
specifier: specifier.clone(),
content: arc_str_to_bytes(text),
maybe_headers: None,
},
))));
}
}
}
}
}
}
}
if self.is_deno_publish

View file

@ -5,6 +5,7 @@ use std::sync::Arc;
use deno_ast::MediaType;
use deno_ast::ModuleSpecifier;
use deno_ast::ParseDiagnostic;
use deno_ast::ParsedSource;
use deno_core::parking_lot::Mutex;
use deno_graph::CapturingModuleParser;
@ -149,3 +150,42 @@ impl deno_graph::ParsedSourceStore for ParsedSourceCache {
}
}
}
pub struct EsmOrCjsChecker {
parsed_source_cache: Arc<ParsedSourceCache>,
}
impl EsmOrCjsChecker {
pub fn new(parsed_source_cache: Arc<ParsedSourceCache>) -> Self {
Self {
parsed_source_cache,
}
}
pub fn is_esm(
&self,
specifier: &ModuleSpecifier,
source: Arc<str>,
media_type: MediaType,
) -> Result<bool, ParseDiagnostic> {
// todo(dsherret): add a file cache here to avoid parsing with swc on each run
let source = match self.parsed_source_cache.get_parsed_source(specifier) {
Some(source) => source.clone(),
None => {
let source = deno_ast::parse_program(deno_ast::ParseParams {
specifier: specifier.clone(),
text: source,
media_type,
capture_tokens: true, // capture because it's used for cjs export analysis
scope_analysis: false,
maybe_syntax: None,
})?;
self
.parsed_source_cache
.set_parsed_source(specifier.clone(), source.clone());
source
}
};
Ok(source.is_module())
}
}

View file

@ -14,6 +14,7 @@ use crate::cache::CodeCache;
use crate::cache::DenoDir;
use crate::cache::DenoDirProvider;
use crate::cache::EmitCache;
use crate::cache::EsmOrCjsChecker;
use crate::cache::GlobalHttpCache;
use crate::cache::HttpCache;
use crate::cache::LocalHttpCache;
@ -171,6 +172,7 @@ struct CliFactoryServices {
http_client_provider: Deferred<Arc<HttpClientProvider>>,
emit_cache: Deferred<Arc<EmitCache>>,
emitter: Deferred<Arc<Emitter>>,
esm_or_cjs_checker: Deferred<Arc<EsmOrCjsChecker>>,
fs: Deferred<Arc<dyn deno_fs::FileSystem>>,
main_graph_container: Deferred<Arc<MainModuleGraphContainer>>,
maybe_inspector_server: Deferred<Option<Arc<InspectorServer>>>,
@ -298,6 +300,12 @@ impl CliFactory {
.get_or_init(|| ProgressBar::new(ProgressBarStyle::TextOnly))
}
pub fn esm_or_cjs_checker(&self) -> &Arc<EsmOrCjsChecker> {
self.services.esm_or_cjs_checker.get_or_init(|| {
Arc::new(EsmOrCjsChecker::new(self.parsed_source_cache().clone()))
})
}
pub fn global_http_cache(&self) -> Result<&Arc<GlobalHttpCache>, AnyError> {
self.services.global_http_cache.get_or_try_init(|| {
Ok(Arc::new(GlobalHttpCache::new(
@ -579,6 +587,7 @@ impl CliFactory {
node_analysis_cache,
self.fs().clone(),
node_resolver,
Some(self.parsed_source_cache().clone()),
);
Ok(Arc::new(NodeCodeTranslator::new(
@ -619,8 +628,10 @@ impl CliFactory {
Ok(Arc::new(ModuleGraphBuilder::new(
cli_options.clone(),
self.caches()?.clone(),
self.esm_or_cjs_checker().clone(),
self.fs().clone(),
self.resolver().await?.clone(),
self.cli_node_resolver().await?.clone(),
self.npm_resolver().await?.clone(),
self.module_info_cache()?.clone(),
self.parsed_source_cache().clone(),
@ -792,6 +803,7 @@ impl CliFactory {
Ok(CliMainWorkerFactory::new(
self.blob_store().clone(),
self.cjs_resolutions().clone(),
if cli_options.code_cache_enabled() {
Some(self.code_cache()?.clone())
} else {
@ -896,6 +908,7 @@ impl CliFactory {
node_ipc: cli_options.node_ipc_fd(),
serve_port: cli_options.serve_port(),
serve_host: cli_options.serve_host(),
unstable_detect_cjs: cli_options.unstable_detect_cjs(),
})
}
}

View file

@ -333,7 +333,7 @@ impl FileFetcher {
)
})?;
let bytes = blob.read_all().await?;
let bytes = blob.read_all().await;
let headers =
HashMap::from([("content-type".to_string(), blob.media_type.clone())]);

View file

@ -6,6 +6,7 @@ use crate::args::CliLockfile;
use crate::args::CliOptions;
use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
use crate::cache;
use crate::cache::EsmOrCjsChecker;
use crate::cache::GlobalHttpCache;
use crate::cache::ModuleInfoCache;
use crate::cache::ParsedSourceCache;
@ -14,6 +15,7 @@ use crate::errors::get_error_class_name;
use crate::file_fetcher::FileFetcher;
use crate::npm::CliNpmResolver;
use crate::resolver::CliGraphResolver;
use crate::resolver::CliNodeResolver;
use crate::resolver::CliSloppyImportsResolver;
use crate::resolver::SloppyImportsCachedFs;
use crate::tools::check;
@ -379,8 +381,10 @@ pub struct BuildFastCheckGraphOptions<'a> {
pub struct ModuleGraphBuilder {
options: Arc<CliOptions>,
caches: Arc<cache::Caches>,
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
fs: Arc<dyn FileSystem>,
resolver: Arc<CliGraphResolver>,
node_resolver: Arc<CliNodeResolver>,
npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>,
parsed_source_cache: Arc<ParsedSourceCache>,
@ -396,8 +400,10 @@ impl ModuleGraphBuilder {
pub fn new(
options: Arc<CliOptions>,
caches: Arc<cache::Caches>,
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
fs: Arc<dyn FileSystem>,
resolver: Arc<CliGraphResolver>,
node_resolver: Arc<CliNodeResolver>,
npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>,
parsed_source_cache: Arc<ParsedSourceCache>,
@ -410,8 +416,10 @@ impl ModuleGraphBuilder {
Self {
options,
caches,
esm_or_cjs_checker,
fs,
resolver,
node_resolver,
npm_resolver,
module_info_cache,
parsed_source_cache,
@ -691,8 +699,10 @@ impl ModuleGraphBuilder {
permissions: PermissionsContainer,
) -> cache::FetchCacher {
cache::FetchCacher::new(
self.esm_or_cjs_checker.clone(),
self.file_fetcher.clone(),
self.global_http_cache.clone(),
self.node_resolver.clone(),
self.npm_resolver.clone(),
self.module_info_cache.clone(),
cache::FetchCacherOptions {
@ -702,6 +712,7 @@ impl ModuleGraphBuilder {
self.options.sub_command(),
crate::args::DenoSubcommand::Publish { .. }
),
unstable_detect_cjs: self.options.unstable_detect_cjs(),
},
)
}

View file

@ -470,15 +470,23 @@ impl HttpClient {
}
}
pub async fn download_with_progress(
pub async fn download_with_progress_and_retries(
&self,
url: Url,
maybe_header: Option<(HeaderName, HeaderValue)>,
progress_guard: &UpdateGuard,
) -> Result<Option<Vec<u8>>, DownloadError> {
self
.download_inner(url, maybe_header, Some(progress_guard))
.await
crate::util::retry::retry(
|| {
self.download_inner(
url.clone(),
maybe_header.clone(),
Some(progress_guard),
)
},
|e| matches!(e, DownloadError::BadResponse(_) | DownloadError::Fetch(_)),
)
.await
}
pub async fn get_redirected_url(

View file

@ -44,6 +44,7 @@ use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::HashSet;
use std::path::Path;
use text_lines::LineAndColumnIndex;
use tower_lsp::lsp_types as lsp;
use tower_lsp::lsp_types::Position;
use tower_lsp::lsp_types::Range;
@ -228,6 +229,7 @@ pub struct TsResponseImportMapper<'a> {
documents: &'a Documents,
maybe_import_map: Option<&'a ImportMap>,
resolver: &'a LspResolver,
file_referrer: ModuleSpecifier,
}
impl<'a> TsResponseImportMapper<'a> {
@ -235,11 +237,13 @@ impl<'a> TsResponseImportMapper<'a> {
documents: &'a Documents,
maybe_import_map: Option<&'a ImportMap>,
resolver: &'a LspResolver,
file_referrer: &ModuleSpecifier,
) -> Self {
Self {
documents,
maybe_import_map,
resolver,
file_referrer: file_referrer.clone(),
}
}
@ -260,8 +264,6 @@ impl<'a> TsResponseImportMapper<'a> {
}
}
let file_referrer = self.documents.get_file_referrer(referrer);
if let Some(jsr_path) = specifier.as_str().strip_prefix(jsr_url().as_str())
{
let mut segments = jsr_path.split('/');
@ -276,7 +278,7 @@ impl<'a> TsResponseImportMapper<'a> {
let export = self.resolver.jsr_lookup_export_for_path(
&nv,
&path,
file_referrer.as_deref(),
Some(&self.file_referrer),
)?;
let sub_path = (export != ".").then_some(export);
let mut req = None;
@ -302,7 +304,7 @@ impl<'a> TsResponseImportMapper<'a> {
req = req.or_else(|| {
self
.resolver
.jsr_lookup_req_for_nv(&nv, file_referrer.as_deref())
.jsr_lookup_req_for_nv(&nv, Some(&self.file_referrer))
});
let spec_str = if let Some(req) = req {
let req_ref = PackageReqReference { req, sub_path };
@ -332,7 +334,7 @@ impl<'a> TsResponseImportMapper<'a> {
if let Some(npm_resolver) = self
.resolver
.maybe_managed_npm_resolver(file_referrer.as_deref())
.maybe_managed_npm_resolver(Some(&self.file_referrer))
{
if npm_resolver.in_npm_package(specifier) {
if let Ok(Some(pkg_id)) =
@ -468,6 +470,26 @@ impl<'a> TsResponseImportMapper<'a> {
}
None
}
pub fn is_valid_import(
&self,
specifier_text: &str,
referrer: &ModuleSpecifier,
) -> bool {
self
.resolver
.as_graph_resolver(Some(&self.file_referrer))
.resolve(
specifier_text,
&deno_graph::Range {
specifier: referrer.clone(),
start: deno_graph::Position::zeroed(),
end: deno_graph::Position::zeroed(),
},
deno_graph::source::ResolutionMode::Types,
)
.is_ok()
}
}
fn try_reverse_map_package_json_exports(
@ -580,7 +602,7 @@ fn fix_ts_import_action(
referrer: &ModuleSpecifier,
action: &tsc::CodeFixAction,
import_mapper: &TsResponseImportMapper,
) -> Result<tsc::CodeFixAction, AnyError> {
) -> Result<Option<tsc::CodeFixAction>, AnyError> {
if matches!(
action.fix_name.as_str(),
"import" | "fixMissingFunctionDeclaration"
@ -623,19 +645,21 @@ fn fix_ts_import_action(
})
.collect();
return Ok(tsc::CodeFixAction {
return Ok(Some(tsc::CodeFixAction {
description,
changes,
commands: None,
fix_name: action.fix_name.clone(),
fix_id: None,
fix_all_description: None,
});
}));
} else if !import_mapper.is_valid_import(specifier, referrer) {
return Ok(None);
}
}
}
Ok(action.clone())
Ok(Some(action.clone()))
}
/// Determines if two TypeScript diagnostic codes are effectively equivalent.
@ -976,11 +1000,14 @@ impl CodeActionCollection {
"The action returned from TypeScript is unsupported.",
));
}
let action = fix_ts_import_action(
let Some(action) = fix_ts_import_action(
specifier,
action,
&language_server.get_ts_response_import_mapper(specifier),
)?;
)?
else {
return Ok(());
};
let edit = ts_changes_to_edit(&action.changes, language_server)?;
let code_action = lsp::CodeAction {
title: action.description.clone(),
@ -1161,6 +1188,34 @@ impl CodeActionCollection {
range: &lsp::Range,
language_server: &language_server::Inner,
) {
fn import_start_from_specifier(
document: &Document,
import: &deno_graph::Import,
) -> Option<LineAndColumnIndex> {
// find the top level statement that contains the specifier
let parsed_source = document.maybe_parsed_source()?.as_ref().ok()?;
let text_info = parsed_source.text_info_lazy();
let specifier_range = SourceRange::new(
text_info.loc_to_source_pos(LineAndColumnIndex {
line_index: import.specifier_range.start.line,
column_index: import.specifier_range.start.character,
}),
text_info.loc_to_source_pos(LineAndColumnIndex {
line_index: import.specifier_range.end.line,
column_index: import.specifier_range.end.character,
}),
);
match parsed_source.program_ref() {
deno_ast::swc::ast::Program::Module(module) => module
.body
.iter()
.find(|i| i.range().contains(&specifier_range))
.map(|i| text_info.line_and_column_index(i.range().start)),
deno_ast::swc::ast::Program::Script(_) => None,
}
}
async fn deno_types_for_npm_action(
document: &Document,
range: &lsp::Range,
@ -1181,14 +1236,15 @@ impl CodeActionCollection {
range.end.line as usize,
range.end.character as usize,
);
let import_range = dependency.imports.iter().find_map(|i| {
let import_start = dependency.imports.iter().find_map(|i| {
if json!(i.kind) != json!("es") && json!(i.kind) != json!("tsType") {
return None;
}
if !i.specifier_range.includes(&position) {
return None;
}
i.full_range.as_ref()
import_start_from_specifier(document, i)
})?;
let referrer = document.specifier();
let file_referrer = document.file_referrer();
@ -1275,8 +1331,8 @@ impl CodeActionCollection {
.specifier_to_uri(referrer, file_referrer)
.ok()?;
let position = lsp::Position {
line: import_range.start.line as u32,
character: import_range.start.character as u32,
line: import_start.line_index as u32,
character: import_start.column_index as u32,
};
let new_text = format!(
"{}// @deno-types=\"{}\"\n",

View file

@ -147,11 +147,11 @@ pub fn server_capabilities(
moniker_provider: None,
experimental: Some(json!({
"denoConfigTasks": true,
"testingApi":true,
"testingApi": true,
"didRefreshDenoConfigurationTreeNotifications": true,
})),
inlay_hint_provider: Some(OneOf::Left(true)),
position_encoding: None,
// TODO(nayeemrmn): Support pull-based diagnostics.
diagnostic_provider: None,
inline_value_provider: None,
inline_completion_provider: None,

View file

@ -92,6 +92,19 @@ impl Client {
});
}
pub fn send_did_refresh_deno_configuration_tree_notification(
&self,
params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
) {
// do on a task in case the caller currently is in the lsp lock
let client = self.0.clone();
spawn(async move {
client
.send_did_refresh_deno_configuration_tree_notification(params)
.await;
});
}
pub fn send_did_change_deno_configuration_notification(
&self,
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
@ -169,6 +182,10 @@ trait ClientTrait: Send + Sync {
params: lsp_custom::DiagnosticBatchNotificationParams,
);
async fn send_test_notification(&self, params: TestingNotification);
async fn send_did_refresh_deno_configuration_tree_notification(
&self,
params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
);
async fn send_did_change_deno_configuration_notification(
&self,
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
@ -249,6 +266,18 @@ impl ClientTrait for TowerClient {
}
}
async fn send_did_refresh_deno_configuration_tree_notification(
&self,
params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
) {
self
.0
.send_notification::<lsp_custom::DidRefreshDenoConfigurationTreeNotification>(
params,
)
.await
}
async fn send_did_change_deno_configuration_notification(
&self,
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
@ -366,6 +395,12 @@ impl ClientTrait for ReplClient {
async fn send_test_notification(&self, _params: TestingNotification) {}
async fn send_did_refresh_deno_configuration_tree_notification(
&self,
_params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
) {
}
async fn send_did_change_deno_configuration_notification(
&self,
_params: lsp_custom::DidChangeDenoConfigurationNotificationParams,

View file

@ -200,15 +200,11 @@ pub async fn get_import_completions(
{
// completions for import map specifiers
Some(lsp::CompletionResponse::List(completion_list))
} else if text.starts_with("./")
|| text.starts_with("../")
|| text.starts_with('/')
} else if let Some(completion_list) =
get_local_completions(specifier, &text, &range, resolver)
{
// completions for local relative modules
Some(lsp::CompletionResponse::List(CompletionList {
is_incomplete: false,
items: get_local_completions(specifier, &text, &range, resolver)?,
}))
Some(lsp::CompletionResponse::List(completion_list))
} else if !text.is_empty() {
// completion of modules from a module registry or cache
check_auto_config_registry(
@ -363,15 +359,15 @@ fn get_local_completions(
text: &str,
range: &lsp::Range,
resolver: &LspResolver,
) -> Option<Vec<lsp::CompletionItem>> {
) -> Option<CompletionList> {
if base.scheme() != "file" {
return None;
}
let parent = base.join(text).ok()?.join(".").ok()?;
let parent = &text[..text.char_indices().rfind(|(_, c)| *c == '/')?.0 + 1];
let resolved_parent = resolver
.as_graph_resolver(Some(base))
.resolve(
parent.as_str(),
parent,
&Range {
specifier: base.clone(),
start: deno_graph::Position::zeroed(),
@ -381,62 +377,62 @@ fn get_local_completions(
)
.ok()?;
let resolved_parent_path = url_to_file_path(&resolved_parent).ok()?;
let raw_parent =
&text[..text.char_indices().rfind(|(_, c)| *c == '/')?.0 + 1];
if resolved_parent_path.is_dir() {
let cwd = std::env::current_dir().ok()?;
let items = std::fs::read_dir(resolved_parent_path).ok()?;
Some(
items
.filter_map(|de| {
let de = de.ok()?;
let label = de.path().file_name()?.to_string_lossy().to_string();
let entry_specifier = resolve_path(de.path().to_str()?, &cwd).ok()?;
if entry_specifier == *base {
return None;
}
let full_text = format!("{raw_parent}{label}");
let text_edit = Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
range: *range,
new_text: full_text.clone(),
}));
let filter_text = Some(full_text);
match de.file_type() {
Ok(file_type) if file_type.is_dir() => Some(lsp::CompletionItem {
label,
kind: Some(lsp::CompletionItemKind::FOLDER),
detail: Some("(local)".to_string()),
filter_text,
sort_text: Some("1".to_string()),
text_edit,
commit_characters: Some(
IMPORT_COMMIT_CHARS.iter().map(|&c| c.into()).collect(),
),
..Default::default()
}),
Ok(file_type) if file_type.is_file() => {
if is_importable_ext(&de.path()) {
Some(lsp::CompletionItem {
label,
kind: Some(lsp::CompletionItemKind::FILE),
detail: Some("(local)".to_string()),
filter_text,
sort_text: Some("1".to_string()),
text_edit,
commit_characters: Some(
IMPORT_COMMIT_CHARS.iter().map(|&c| c.into()).collect(),
),
..Default::default()
})
} else {
None
}
let entries = std::fs::read_dir(resolved_parent_path).ok()?;
let items = entries
.filter_map(|de| {
let de = de.ok()?;
let label = de.path().file_name()?.to_string_lossy().to_string();
let entry_specifier = resolve_path(de.path().to_str()?, &cwd).ok()?;
if entry_specifier == *base {
return None;
}
let full_text = format!("{parent}{label}");
let text_edit = Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
range: *range,
new_text: full_text.clone(),
}));
let filter_text = Some(full_text);
match de.file_type() {
Ok(file_type) if file_type.is_dir() => Some(lsp::CompletionItem {
label,
kind: Some(lsp::CompletionItemKind::FOLDER),
detail: Some("(local)".to_string()),
filter_text,
sort_text: Some("1".to_string()),
text_edit,
commit_characters: Some(
IMPORT_COMMIT_CHARS.iter().map(|&c| c.into()).collect(),
),
..Default::default()
}),
Ok(file_type) if file_type.is_file() => {
if is_importable_ext(&de.path()) {
Some(lsp::CompletionItem {
label,
kind: Some(lsp::CompletionItemKind::FILE),
detail: Some("(local)".to_string()),
filter_text,
sort_text: Some("1".to_string()),
text_edit,
commit_characters: Some(
IMPORT_COMMIT_CHARS.iter().map(|&c| c.into()).collect(),
),
..Default::default()
})
} else {
None
}
_ => None,
}
})
.collect(),
)
_ => None,
}
})
.collect();
Some(CompletionList {
is_incomplete: false,
items,
})
} else {
None
}
@ -921,11 +917,11 @@ mod tests {
},
},
&Default::default(),
);
assert!(actual.is_some());
let actual = actual.unwrap();
assert_eq!(actual.len(), 3);
for item in actual {
)
.unwrap();
assert!(!actual.is_incomplete);
assert_eq!(actual.items.len(), 3);
for item in actual.items {
match item.text_edit {
Some(lsp::CompletionTextEdit::Edit(text_edit)) => {
assert!(["./b", "./f.mjs", "./g.json"]

View file

@ -50,9 +50,12 @@ use std::sync::Arc;
use tower_lsp::lsp_types as lsp;
use super::logging::lsp_log;
use super::lsp_custom;
use super::urls::url_to_uri;
use crate::args::discover_npmrc_from_workspace;
use crate::args::has_flag_env_var;
use crate::args::CliLockfile;
use crate::args::CliLockfileReadFromPathOptions;
use crate::args::ConfigFile;
use crate::args::LintFlags;
use crate::args::LintOptions;
@ -1715,14 +1718,14 @@ impl ConfigTree {
.unwrap_or_else(|| Arc::new(FmtConfig::new_with_base(PathBuf::from("/"))))
}
/// Returns (scope_uri, type).
/// Returns (scope_url, type).
pub fn watched_file_type(
&self,
specifier: &ModuleSpecifier,
) -> Option<(&ModuleSpecifier, ConfigWatchedFileType)> {
for (scope_uri, data) in self.scopes.iter() {
for (scope_url, data) in self.scopes.iter() {
if let Some(typ) = data.watched_files.get(specifier) {
return Some((scope_uri, *typ));
return Some((scope_url, *typ));
}
}
None
@ -1746,6 +1749,46 @@ impl ConfigTree {
.any(|data| data.watched_files.contains_key(specifier))
}
pub fn to_did_refresh_params(
&self,
) -> lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams {
let data = self
.scopes
.values()
.filter_map(|data| {
let workspace_root_scope_uri =
Some(data.member_dir.workspace.root_dir())
.filter(|s| *s != data.member_dir.dir_url())
.and_then(|s| url_to_uri(s).ok());
Some(lsp_custom::DenoConfigurationData {
scope_uri: url_to_uri(&data.scope).ok()?,
deno_json: data.maybe_deno_json().and_then(|c| {
if workspace_root_scope_uri.is_some()
&& Some(&c.specifier)
== data
.member_dir
.workspace
.root_deno_json()
.map(|c| &c.specifier)
{
return None;
}
Some(lsp::TextDocumentIdentifier {
uri: url_to_uri(&c.specifier).ok()?,
})
}),
package_json: data.maybe_pkg_json().and_then(|p| {
Some(lsp::TextDocumentIdentifier {
uri: url_to_uri(&p.specifier()).ok()?,
})
}),
workspace_root_scope_uri,
})
})
.collect();
lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams { data }
}
pub async fn refresh(
&mut self,
settings: &Settings,
@ -1931,7 +1974,11 @@ fn resolve_lockfile_from_path(
lockfile_path: PathBuf,
frozen: bool,
) -> Option<CliLockfile> {
match CliLockfile::read_from_path(lockfile_path, frozen) {
match CliLockfile::read_from_path(CliLockfileReadFromPathOptions {
file_path: lockfile_path,
frozen,
skip_write: false,
}) {
Ok(value) => {
if value.filename.exists() {
if let Ok(specifier) = ModuleSpecifier::from_file_path(&value.filename)

View file

@ -1499,7 +1499,11 @@ fn diagnose_dependency(
.data_for_specifier(referrer_doc.file_referrer().unwrap_or(referrer))
.and_then(|d| d.resolver.maybe_import_map());
if let Some(import_map) = import_map {
if let Resolution::Ok(resolved) = &dependency.maybe_code {
let resolved = dependency
.maybe_code
.ok()
.or_else(|| dependency.maybe_type.ok());
if let Some(resolved) = resolved {
if let Some(to) = import_map.lookup(&resolved.specifier, referrer) {
if dependency_key != to {
diagnostics.push(

View file

@ -96,6 +96,7 @@ 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;
@ -962,6 +963,11 @@ impl Inner {
.tree
.refresh(&self.config.settings, &self.workspace_files, &file_fetcher)
.await;
self
.client
.send_did_refresh_deno_configuration_tree_notification(
self.config.tree.to_did_refresh_params(),
);
for config_file in self.config.tree.config_files() {
(|| {
let compiler_options = config_file.to_compiler_options().ok()?.options;
@ -1917,6 +1923,7 @@ impl Inner {
// as the import map is an implementation detail
.and_then(|d| d.resolver.maybe_import_map()),
self.resolver.as_ref(),
file_referrer,
)
}
@ -3605,7 +3612,10 @@ impl Inner {
};
let cli_options = CliOptions::new(
Arc::new(Flags {
cache_path: Some(self.cache.deno_dir().root.clone()),
internal: InternalFlags {
cache_path: Some(self.cache.deno_dir().root.clone()),
..Default::default()
},
ca_stores: workspace_settings.certificate_stores.clone(),
ca_data: workspace_settings.tls_certificate.clone().map(CaData::File),
unsafely_ignore_certificate_errors: workspace_settings
@ -3802,7 +3812,7 @@ impl Inner {
let maybe_inlay_hints = maybe_inlay_hints.map(|hints| {
hints
.iter()
.map(|hint| hint.to_lsp(line_index.clone()))
.map(|hint| hint.to_lsp(line_index.clone(), self))
.collect()
});
self.performance.measure(mark);

View file

@ -46,6 +46,30 @@ pub struct DiagnosticBatchNotificationParams {
pub messages_len: usize,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DenoConfigurationData {
pub scope_uri: lsp::Uri,
pub workspace_root_scope_uri: Option<lsp::Uri>,
pub deno_json: Option<lsp::TextDocumentIdentifier>,
pub package_json: Option<lsp::TextDocumentIdentifier>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DidRefreshDenoConfigurationTreeNotificationParams {
pub data: Vec<DenoConfigurationData>,
}
pub enum DidRefreshDenoConfigurationTreeNotification {}
impl lsp::notification::Notification
for DidRefreshDenoConfigurationTreeNotification
{
type Params = DidRefreshDenoConfigurationTreeNotificationParams;
const METHOD: &'static str = "deno/didRefreshDenoConfigurationTree";
}
#[derive(Debug, Eq, Hash, PartialEq, Copy, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum DenoConfigurationChangeType {
@ -88,13 +112,15 @@ pub struct DidChangeDenoConfigurationNotificationParams {
pub changes: Vec<DenoConfigurationChangeEvent>,
}
// TODO(nayeemrmn): This is being replaced by
// `DidRefreshDenoConfigurationTreeNotification` for Deno > v2.0.0. Remove it
// soon.
pub enum DidChangeDenoConfigurationNotification {}
impl lsp::notification::Notification
for DidChangeDenoConfigurationNotification
{
type Params = DidChangeDenoConfigurationNotificationParams;
const METHOD: &'static str = "deno/didChangeDenoConfiguration";
}
@ -102,7 +128,6 @@ pub enum DidUpgradeCheckNotification {}
impl lsp::notification::Notification for DidUpgradeCheckNotification {
type Params = DidUpgradeCheckNotificationParams;
const METHOD: &'static str = "deno/didUpgradeCheck";
}
@ -125,6 +150,5 @@ pub enum DiagnosticBatchNotification {}
impl lsp::notification::Notification for DiagnosticBatchNotification {
type Params = DiagnosticBatchNotificationParams;
const METHOD: &'static str = "deno/internalTestDiagnosticBatch";
}

View file

@ -2182,6 +2182,50 @@ impl NavigateToItem {
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InlayHintDisplayPart {
pub text: String,
pub span: Option<TextSpan>,
pub file: Option<String>,
}
impl InlayHintDisplayPart {
pub fn to_lsp(
&self,
language_server: &language_server::Inner,
) -> lsp::InlayHintLabelPart {
let location = self.file.as_ref().map(|f| {
let specifier =
resolve_url(f).unwrap_or_else(|_| INVALID_SPECIFIER.clone());
let file_referrer =
language_server.documents.get_file_referrer(&specifier);
let uri = language_server
.url_map
.specifier_to_uri(&specifier, file_referrer.as_deref())
.unwrap_or_else(|_| INVALID_URI.clone());
let range = self
.span
.as_ref()
.and_then(|s| {
let asset_or_doc =
language_server.get_asset_or_document(&specifier).ok()?;
Some(s.to_range(asset_or_doc.line_index()))
})
.unwrap_or_else(|| {
lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0))
});
lsp::Location { uri, range }
});
lsp::InlayHintLabelPart {
value: self.text.clone(),
tooltip: None,
location,
command: None,
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub enum InlayHintKind {
Type,
@ -2203,6 +2247,7 @@ impl InlayHintKind {
#[serde(rename_all = "camelCase")]
pub struct InlayHint {
pub text: String,
pub display_parts: Option<Vec<InlayHintDisplayPart>>,
pub position: u32,
pub kind: InlayHintKind,
pub whitespace_before: Option<bool>,
@ -2210,10 +2255,23 @@ pub struct InlayHint {
}
impl InlayHint {
pub fn to_lsp(&self, line_index: Arc<LineIndex>) -> lsp::InlayHint {
pub fn to_lsp(
&self,
line_index: Arc<LineIndex>,
language_server: &language_server::Inner,
) -> lsp::InlayHint {
lsp::InlayHint {
position: line_index.position_tsc(self.position.into()),
label: lsp::InlayHintLabel::String(self.text.clone()),
label: if let Some(display_parts) = &self.display_parts {
lsp::InlayHintLabel::LabelParts(
display_parts
.iter()
.map(|p| p.to_lsp(language_server))
.collect(),
)
} else {
lsp::InlayHintLabel::String(self.text.clone())
},
kind: self.kind.to_lsp(),
padding_left: self.whitespace_before,
padding_right: self.whitespace_after,
@ -3939,7 +3997,7 @@ pub struct OutliningSpan {
kind: OutliningSpanKind,
}
const FOLD_END_PAIR_CHARACTERS: &[u8] = &[b'}', b']', b')', b'`'];
const FOLD_END_PAIR_CHARACTERS: &[u8] = b"}])`";
impl OutliningSpan {
pub fn to_folding_range(
@ -4892,6 +4950,8 @@ pub struct UserPreferences {
pub allow_rename_of_import_path: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auto_import_file_exclude_patterns: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub interactive_inlay_hints: Option<bool>,
}
impl UserPreferences {
@ -4909,6 +4969,7 @@ impl UserPreferences {
include_completions_with_snippet_text: Some(
config.snippet_support_capable(),
),
interactive_inlay_hints: Some(true),
provide_refactor_not_applicable_reason: Some(true),
quote_preference: Some(fmt_config.into()),
use_label_details_in_completion_entries: Some(true),

View file

@ -37,6 +37,7 @@ use crate::util::v8::get_v8_flags_from_env;
use crate::util::v8::init_v8_flags;
use args::TaskFlags;
use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError;
use deno_runtime::WorkerExecutionMode;
pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS;
@ -46,19 +47,24 @@ use deno_core::error::JsError;
use deno_core::futures::FutureExt;
use deno_core::unsync::JoinHandle;
use deno_npm::resolution::SnapshotFromLockfileError;
use deno_runtime::fmt_errors::format_js_error_with_suggestions;
use deno_runtime::fmt_errors::FixSuggestion;
use deno_runtime::fmt_errors::format_js_error;
use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics;
use deno_terminal::colors;
use factory::CliFactory;
use npm::ResolvePkgFolderFromDenoReqError;
use standalone::MODULE_NOT_FOUND;
use standalone::UNSUPPORTED_SCHEME;
use std::env;
use std::future::Future;
use std::io::IsTerminal;
use std::ops::Deref;
use std::path::PathBuf;
use std::sync::Arc;
#[cfg(feature = "dhat-heap")]
#[global_allocator]
static ALLOC: dhat::Alloc = dhat::Alloc;
/// Ensures that all subcommands return an i32 exit code and an [`AnyError`] error type.
trait SubcommandOutput {
fn output(self) -> Result<i32, AnyError>;
@ -159,7 +165,19 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
DenoSubcommand::Uninstall(uninstall_flags) => spawn_subcommand(async {
tools::installer::uninstall(flags, uninstall_flags).await
}),
DenoSubcommand::Lsp => spawn_subcommand(async { lsp::start().await }),
DenoSubcommand::Lsp => spawn_subcommand(async {
if std::io::stderr().is_terminal() {
log::warn!(
"{} command is intended to be run by text editors and IDEs and shouldn't be run manually.
Visit https://docs.deno.com/runtime/getting_started/setup_your_environment/ for instruction
how to setup your favorite text editor.
Press Ctrl+C to exit.
", colors::cyan("deno lsp"));
}
lsp::start().await
}),
DenoSubcommand::Lint(lint_flags) => spawn_subcommand(async {
if lint_flags.rules {
tools::lint::print_rules_list(
@ -182,6 +200,21 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
match result {
Ok(v) => Ok(v),
Err(script_err) => {
if let Some(ResolvePkgFolderFromDenoReqError::Byonm(ByonmResolvePkgFolderFromDenoReqError::UnmatchedReq(_))) = script_err.downcast_ref::<ResolvePkgFolderFromDenoReqError>() {
if flags.node_modules_dir.is_none() {
let mut flags = flags.deref().clone();
let watch = match &flags.subcommand {
DenoSubcommand::Run(run_flags) => run_flags.watch.clone(),
_ => unreachable!(),
};
flags.node_modules_dir = Some(deno_config::deno_json::NodeModulesDirMode::None);
// use the current lockfile, but don't write it out
if flags.frozen_lockfile.is_none() {
flags.internal.lockfile_skip_write = true;
}
return tools::run::run_script(WorkerExecutionMode::Run, Arc::new(flags), watch).await;
}
}
let script_err_msg = script_err.to_string();
if script_err_msg.starts_with(MODULE_NOT_FOUND) || script_err_msg.starts_with(UNSUPPORTED_SCHEME) {
if run_flags.bare {
@ -332,104 +365,12 @@ fn exit_with_message(message: &str, code: i32) -> ! {
std::process::exit(code);
}
fn get_suggestions_for_terminal_errors(e: &JsError) -> Vec<FixSuggestion> {
if let Some(msg) = &e.message {
if msg.contains("module is not defined")
|| msg.contains("exports is not defined")
{
return vec![
FixSuggestion::info(
"Deno does not support CommonJS modules without `.cjs` extension.",
),
FixSuggestion::hint(
"Rewrite this module to ESM or change the file extension to `.cjs`.",
),
];
} else if msg.contains("openKv is not a function") {
return vec![
FixSuggestion::info("Deno.openKv() is an unstable API."),
FixSuggestion::hint(
"Run again with `--unstable-kv` flag to enable this API.",
),
];
} else if msg.contains("cron is not a function") {
return vec![
FixSuggestion::info("Deno.cron() is an unstable API."),
FixSuggestion::hint(
"Run again with `--unstable-cron` flag to enable this API.",
),
];
} else if msg.contains("WebSocketStream is not defined") {
return vec![
FixSuggestion::info("new WebSocketStream() is an unstable API."),
FixSuggestion::hint(
"Run again with `--unstable-net` flag to enable this API.",
),
];
} else if msg.contains("Temporal is not defined") {
return vec![
FixSuggestion::info("Temporal is an unstable API."),
FixSuggestion::hint(
"Run again with `--unstable-temporal` flag to enable this API.",
),
];
} else if msg.contains("BroadcastChannel is not defined") {
return vec![
FixSuggestion::info("BroadcastChannel is an unstable API."),
FixSuggestion::hint(
"Run again with `--unstable-broadcast-channel` flag to enable this API.",
),
];
} else if msg.contains("window is not defined") {
return vec![
FixSuggestion::info("window global is not available in Deno 2."),
FixSuggestion::hint("Replace `window` with `globalThis`."),
];
} else if msg.contains("UnsafeWindowSurface is not a constructor") {
return vec![
FixSuggestion::info("Deno.UnsafeWindowSurface is an unstable API."),
FixSuggestion::hint(
"Run again with `--unstable-webgpu` flag to enable this API.",
),
];
// Try to capture errors like:
// ```
// Uncaught Error: Cannot find module '../build/Release/canvas.node'
// Require stack:
// - /.../deno/npm/registry.npmjs.org/canvas/2.11.2/lib/bindings.js
// - /.../.cache/deno/npm/registry.npmjs.org/canvas/2.11.2/lib/canvas.js
// ```
} else if msg.contains("Cannot find module")
&& msg.contains("Require stack")
&& msg.contains(".node'")
{
return vec![
FixSuggestion::info_multiline(
&[
"Trying to execute an npm package using Node-API addons,",
"these packages require local `node_modules` directory to be present."
]
),
FixSuggestion::hint_multiline(
&[
"Add `\"nodeModulesDir\": \"auto\" option to `deno.json`, and then run",
"`deno install --allow-scripts=npm:<package> --entrypoint <script>` to setup `node_modules` directory."
]
)
];
}
}
vec![]
}
fn exit_for_error(error: AnyError) -> ! {
let mut error_string = format!("{error:?}");
let mut error_code = 1;
if let Some(e) = error.downcast_ref::<JsError>() {
let suggestions = get_suggestions_for_terminal_errors(e);
error_string = format_js_error_with_suggestions(e, suggestions);
error_string = format_js_error(e);
} else if let Some(SnapshotFromLockfileError::IntegrityCheckFailed(e)) =
error.downcast_ref::<SnapshotFromLockfileError>()
{
@ -450,6 +391,9 @@ pub(crate) fn unstable_exit_cb(feature: &str, api_name: &str) {
}
pub fn main() {
#[cfg(feature = "dhat-heap")]
let profiler = dhat::Profiler::new_heap();
setup_panic_hook();
util::unix::raise_fd_limit();
@ -470,7 +414,12 @@ pub fn main() {
run_subcommand(Arc::new(flags)).await
};
match create_and_run_current_thread_with_maybe_metrics(future) {
let result = create_and_run_current_thread_with_maybe_metrics(future);
#[cfg(feature = "dhat-heap")]
drop(profiler);
match result {
Ok(exit_code) => std::process::exit(exit_code),
Err(err) => exit_for_error(err),
}

View file

@ -331,15 +331,23 @@ impl<TGraphContainer: ModuleGraphContainer>
maybe_referrer: Option<&ModuleSpecifier>,
requested_module_type: RequestedModuleType,
) -> Result<ModuleSource, AnyError> {
let code_source = if let Some(result) = self
.shared
.npm_module_loader
.load_if_in_npm_package(specifier, maybe_referrer)
.await
{
result?
} else {
self.load_prepared_module(specifier, maybe_referrer).await?
let code_source = match self.load_prepared_module(specifier).await? {
Some(code_source) => code_source,
None => {
if self.shared.npm_module_loader.if_in_npm_package(specifier) {
self
.shared
.npm_module_loader
.load(specifier, maybe_referrer)
.await?
} else {
let mut msg = format!("Loading unprepared module: {specifier}");
if let Some(referrer) = maybe_referrer {
msg = format!("{}, imported from: {}", msg, referrer.as_str());
}
return Err(anyhow!(msg));
}
}
};
let code = if self.shared.is_inspecting {
// we need the code with the source map in order for
@ -514,17 +522,12 @@ impl<TGraphContainer: ModuleGraphContainer>
async fn load_prepared_module(
&self,
specifier: &ModuleSpecifier,
maybe_referrer: Option<&ModuleSpecifier>,
) -> Result<ModuleCodeStringSource, AnyError> {
) -> Result<Option<ModuleCodeStringSource>, AnyError> {
// Note: keep this in sync with the sync version below
let graph = self.graph_container.graph();
match self.load_prepared_module_or_defer_emit(
&graph,
specifier,
maybe_referrer,
) {
Ok(CodeOrDeferredEmit::Code(code_source)) => Ok(code_source),
Ok(CodeOrDeferredEmit::DeferredEmit {
match self.load_prepared_module_or_defer_emit(&graph, specifier)? {
Some(CodeOrDeferredEmit::Code(code_source)) => Ok(Some(code_source)),
Some(CodeOrDeferredEmit::DeferredEmit {
specifier,
media_type,
source,
@ -537,30 +540,25 @@ impl<TGraphContainer: ModuleGraphContainer>
// at this point, we no longer need the parsed source in memory, so free it
self.parsed_source_cache.free(specifier);
Ok(ModuleCodeStringSource {
Ok(Some(ModuleCodeStringSource {
code: ModuleSourceCode::Bytes(transpile_result),
found_url: specifier.clone(),
media_type,
})
}))
}
Err(err) => Err(err),
None => Ok(None),
}
}
fn load_prepared_module_sync(
&self,
specifier: &ModuleSpecifier,
maybe_referrer: Option<&ModuleSpecifier>,
) -> Result<ModuleCodeStringSource, AnyError> {
) -> Result<Option<ModuleCodeStringSource>, AnyError> {
// Note: keep this in sync with the async version above
let graph = self.graph_container.graph();
match self.load_prepared_module_or_defer_emit(
&graph,
specifier,
maybe_referrer,
) {
Ok(CodeOrDeferredEmit::Code(code_source)) => Ok(code_source),
Ok(CodeOrDeferredEmit::DeferredEmit {
match self.load_prepared_module_or_defer_emit(&graph, specifier)? {
Some(CodeOrDeferredEmit::Code(code_source)) => Ok(Some(code_source)),
Some(CodeOrDeferredEmit::DeferredEmit {
specifier,
media_type,
source,
@ -572,13 +570,13 @@ impl<TGraphContainer: ModuleGraphContainer>
// at this point, we no longer need the parsed source in memory, so free it
self.parsed_source_cache.free(specifier);
Ok(ModuleCodeStringSource {
Ok(Some(ModuleCodeStringSource {
code: ModuleSourceCode::Bytes(transpile_result),
found_url: specifier.clone(),
media_type,
})
}))
}
Err(err) => Err(err),
None => Ok(None),
}
}
@ -586,8 +584,7 @@ impl<TGraphContainer: ModuleGraphContainer>
&self,
graph: &'graph ModuleGraph,
specifier: &ModuleSpecifier,
maybe_referrer: Option<&ModuleSpecifier>,
) -> Result<CodeOrDeferredEmit<'graph>, AnyError> {
) -> Result<Option<CodeOrDeferredEmit<'graph>>, AnyError> {
if specifier.scheme() == "node" {
// Node built-in modules should be handled internally.
unreachable!("Deno bug. {} was misconfigured internally.", specifier);
@ -599,11 +596,11 @@ impl<TGraphContainer: ModuleGraphContainer>
media_type,
specifier,
..
})) => Ok(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
})) => Ok(Some(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
code: ModuleSourceCode::String(source.clone().into()),
found_url: specifier.clone(),
media_type: *media_type,
})),
}))),
Some(deno_graph::Module::Js(JsModule {
source,
media_type,
@ -624,11 +621,11 @@ impl<TGraphContainer: ModuleGraphContainer>
| MediaType::Cts
| MediaType::Jsx
| MediaType::Tsx => {
return Ok(CodeOrDeferredEmit::DeferredEmit {
return Ok(Some(CodeOrDeferredEmit::DeferredEmit {
specifier,
media_type: *media_type,
source,
});
}));
}
MediaType::TsBuildInfo | MediaType::Wasm | MediaType::SourceMap => {
panic!("Unexpected media type {media_type} for {specifier}")
@ -638,24 +635,18 @@ impl<TGraphContainer: ModuleGraphContainer>
// at this point, we no longer need the parsed source in memory, so free it
self.parsed_source_cache.free(specifier);
Ok(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
Ok(Some(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
code: ModuleSourceCode::String(code),
found_url: specifier.clone(),
media_type: *media_type,
}))
})))
}
Some(
deno_graph::Module::External(_)
| deno_graph::Module::Node(_)
| deno_graph::Module::Npm(_),
)
| None => {
let mut msg = format!("Loading unprepared module: {specifier}");
if let Some(referrer) = maybe_referrer {
msg = format!("{}, imported from: {}", msg, referrer.as_str());
}
Err(anyhow!(msg))
}
| None => Ok(None),
}
}
}
@ -828,7 +819,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
"wasm" | "file" | "http" | "https" | "data" | "blob" => (),
_ => return None,
}
let source = self.0.load_prepared_module_sync(&specifier, None).ok()?;
let source = self.0.load_prepared_module_sync(&specifier).ok()??;
source_map_from_code(source.code.as_bytes())
}

View file

@ -1 +1 @@
{ "node_api_create_syntax_error"; "napi_make_callback"; "napi_has_named_property"; "napi_async_destroy"; "napi_coerce_to_object"; "napi_get_arraybuffer_info"; "napi_detach_arraybuffer"; "napi_get_undefined"; "napi_reference_unref"; "napi_fatal_error"; "napi_open_callback_scope"; "napi_close_callback_scope"; "napi_get_value_uint32"; "napi_create_function"; "napi_create_arraybuffer"; "napi_get_value_int64"; "napi_get_all_property_names"; "napi_resolve_deferred"; "napi_is_detached_arraybuffer"; "napi_create_string_utf8"; "napi_create_threadsafe_function"; "node_api_throw_syntax_error"; "napi_create_bigint_int64"; "napi_wrap"; "napi_set_property"; "napi_get_value_bigint_int64"; "napi_open_handle_scope"; "napi_create_error"; "napi_create_buffer"; "napi_cancel_async_work"; "napi_is_exception_pending"; "napi_acquire_threadsafe_function"; "napi_create_external"; "napi_get_threadsafe_function_context"; "napi_get_null"; "napi_create_string_utf16"; "node_api_create_external_string_utf16"; "napi_get_value_bigint_uint64"; "napi_module_register"; "napi_is_typedarray"; "napi_create_external_buffer"; "napi_get_new_target"; "napi_get_instance_data"; "napi_close_handle_scope"; "napi_get_value_string_utf16"; "napi_get_property_names"; "napi_is_arraybuffer"; "napi_get_cb_info"; "napi_define_properties"; "napi_add_env_cleanup_hook"; "node_api_get_module_file_name"; "napi_get_node_version"; "napi_create_int64"; "napi_create_double"; "napi_get_and_clear_last_exception"; "napi_create_reference"; "napi_get_typedarray_info"; "napi_call_threadsafe_function"; "napi_get_last_error_info"; "napi_create_array_with_length"; "napi_coerce_to_number"; "napi_get_global"; "napi_is_error"; "napi_set_instance_data"; "napi_create_typedarray"; "napi_throw_type_error"; "napi_has_property"; "napi_get_value_external"; "napi_create_range_error"; "napi_typeof"; "napi_ref_threadsafe_function"; "napi_create_bigint_uint64"; "napi_get_prototype"; "napi_adjust_external_memory"; "napi_release_threadsafe_function"; "napi_delete_async_work"; "napi_create_string_latin1"; "node_api_create_external_string_latin1"; "napi_is_array"; "napi_unref_threadsafe_function"; "napi_throw_error"; "napi_has_own_property"; "napi_get_reference_value"; "napi_remove_env_cleanup_hook"; "napi_get_value_string_utf8"; "napi_is_promise"; "napi_get_boolean"; "napi_run_script"; "napi_get_element"; "napi_get_named_property"; "napi_get_buffer_info"; "napi_get_value_bool"; "napi_reference_ref"; "napi_create_object"; "napi_create_promise"; "napi_create_int32"; "napi_escape_handle"; "napi_open_escapable_handle_scope"; "napi_throw"; "napi_get_value_double"; "napi_set_named_property"; "napi_call_function"; "napi_create_date"; "napi_object_freeze"; "napi_get_uv_event_loop"; "napi_get_value_string_latin1"; "napi_reject_deferred"; "napi_add_finalizer"; "napi_create_array"; "napi_delete_reference"; "napi_get_date_value"; "napi_create_dataview"; "napi_get_version"; "napi_define_class"; "napi_is_date"; "napi_remove_wrap"; "napi_delete_property"; "napi_instanceof"; "napi_create_buffer_copy"; "napi_delete_element"; "napi_object_seal"; "napi_queue_async_work"; "napi_get_value_bigint_words"; "napi_is_buffer"; "napi_get_array_length"; "napi_get_property"; "napi_new_instance"; "napi_set_element"; "napi_create_bigint_words"; "napi_strict_equals"; "napi_is_dataview"; "napi_close_escapable_handle_scope"; "napi_get_dataview_info"; "napi_get_value_int32"; "napi_unwrap"; "napi_throw_range_error"; "napi_coerce_to_bool"; "napi_create_uint32"; "napi_has_element"; "napi_create_external_arraybuffer"; "napi_create_symbol"; "node_api_symbol_for"; "napi_coerce_to_string"; "napi_create_type_error"; "napi_fatal_exception"; "napi_create_async_work"; "napi_async_init"; "node_api_create_property_key_utf16"; "napi_type_tag_object"; "napi_check_object_type_tag"; "node_api_post_finalizer"; "napi_add_async_cleanup_hook"; "napi_remove_async_cleanup_hook"; };
{ "node_api_create_syntax_error"; "napi_make_callback"; "napi_has_named_property"; "napi_async_destroy"; "napi_coerce_to_object"; "napi_get_arraybuffer_info"; "napi_detach_arraybuffer"; "napi_get_undefined"; "napi_reference_unref"; "napi_fatal_error"; "napi_open_callback_scope"; "napi_close_callback_scope"; "napi_get_value_uint32"; "napi_create_function"; "napi_create_arraybuffer"; "napi_get_value_int64"; "napi_get_all_property_names"; "napi_resolve_deferred"; "napi_is_detached_arraybuffer"; "napi_create_string_utf8"; "napi_create_threadsafe_function"; "node_api_throw_syntax_error"; "napi_create_bigint_int64"; "napi_wrap"; "napi_set_property"; "napi_get_value_bigint_int64"; "napi_open_handle_scope"; "napi_create_error"; "napi_create_buffer"; "napi_cancel_async_work"; "napi_is_exception_pending"; "napi_acquire_threadsafe_function"; "napi_create_external"; "napi_get_threadsafe_function_context"; "napi_get_null"; "napi_create_string_utf16"; "node_api_create_external_string_utf16"; "napi_get_value_bigint_uint64"; "napi_module_register"; "napi_is_typedarray"; "napi_create_external_buffer"; "napi_get_new_target"; "napi_get_instance_data"; "napi_close_handle_scope"; "napi_get_value_string_utf16"; "napi_get_property_names"; "napi_is_arraybuffer"; "napi_get_cb_info"; "napi_define_properties"; "napi_add_env_cleanup_hook"; "node_api_get_module_file_name"; "napi_get_node_version"; "napi_create_int64"; "napi_create_double"; "napi_get_and_clear_last_exception"; "napi_create_reference"; "napi_get_typedarray_info"; "napi_call_threadsafe_function"; "napi_get_last_error_info"; "napi_create_array_with_length"; "napi_coerce_to_number"; "napi_get_global"; "napi_is_error"; "napi_set_instance_data"; "napi_create_typedarray"; "napi_throw_type_error"; "napi_has_property"; "napi_get_value_external"; "napi_create_range_error"; "napi_typeof"; "napi_ref_threadsafe_function"; "napi_create_bigint_uint64"; "napi_get_prototype"; "napi_adjust_external_memory"; "napi_release_threadsafe_function"; "napi_delete_async_work"; "napi_create_string_latin1"; "node_api_create_external_string_latin1"; "napi_is_array"; "napi_unref_threadsafe_function"; "napi_throw_error"; "napi_has_own_property"; "napi_get_reference_value"; "napi_remove_env_cleanup_hook"; "napi_get_value_string_utf8"; "napi_is_promise"; "napi_get_boolean"; "napi_run_script"; "napi_get_element"; "napi_get_named_property"; "napi_get_buffer_info"; "napi_get_value_bool"; "napi_reference_ref"; "napi_create_object"; "napi_create_promise"; "napi_create_int32"; "napi_escape_handle"; "napi_open_escapable_handle_scope"; "napi_throw"; "napi_get_value_double"; "napi_set_named_property"; "napi_call_function"; "napi_create_date"; "napi_object_freeze"; "napi_get_uv_event_loop"; "napi_get_value_string_latin1"; "napi_reject_deferred"; "napi_add_finalizer"; "napi_create_array"; "napi_delete_reference"; "napi_get_date_value"; "napi_create_dataview"; "napi_get_version"; "napi_define_class"; "napi_is_date"; "napi_remove_wrap"; "napi_delete_property"; "napi_instanceof"; "napi_create_buffer_copy"; "napi_delete_element"; "napi_object_seal"; "napi_queue_async_work"; "napi_get_value_bigint_words"; "napi_is_buffer"; "napi_get_array_length"; "napi_get_property"; "napi_new_instance"; "napi_set_element"; "napi_create_bigint_words"; "napi_strict_equals"; "napi_is_dataview"; "napi_close_escapable_handle_scope"; "napi_get_dataview_info"; "napi_get_value_int32"; "napi_unwrap"; "napi_throw_range_error"; "napi_coerce_to_bool"; "napi_create_uint32"; "napi_has_element"; "napi_create_external_arraybuffer"; "napi_create_symbol"; "node_api_symbol_for"; "napi_coerce_to_string"; "napi_create_type_error"; "napi_fatal_exception"; "napi_create_async_work"; "napi_async_init"; "node_api_create_property_key_utf16"; "napi_type_tag_object"; "napi_check_object_type_tag"; "node_api_post_finalizer"; "napi_add_async_cleanup_hook"; "napi_remove_async_cleanup_hook"; "uv_mutex_init"; "uv_mutex_lock"; "uv_mutex_unlock"; "uv_mutex_destroy"; "uv_async_init"; "uv_async_send"; "uv_close"; };

View file

@ -150,4 +150,11 @@ _napi_type_tag_object
_napi_check_object_type_tag
_node_api_post_finalizer
_napi_add_async_cleanup_hook
_napi_remove_async_cleanup_hook
_napi_remove_async_cleanup_hook
_uv_mutex_init
_uv_mutex_lock
_uv_mutex_unlock
_uv_mutex_destroy
_uv_async_init
_uv_async_send
_uv_close

View file

@ -152,4 +152,11 @@ EXPORTS
napi_check_object_type_tag
node_api_post_finalizer
napi_add_async_cleanup_hook
napi_remove_async_cleanup_hook
napi_remove_async_cleanup_hook
uv_mutex_init
uv_mutex_lock
uv_mutex_unlock
uv_mutex_destroy
uv_async_init
uv_async_send
uv_close

View file

@ -264,6 +264,16 @@ fn napi_define_class<'s>(
Err(status) => return status,
};
let mut accessor_property = v8::PropertyAttribute::NONE;
if p.attributes & napi_enumerable == 0 {
accessor_property = accessor_property | v8::PropertyAttribute::DONT_ENUM;
}
if p.attributes & napi_configurable == 0 {
accessor_property =
accessor_property | v8::PropertyAttribute::DONT_DELETE;
}
if p.getter.is_some() || p.setter.is_some() {
let getter = p.getter.map(|g| {
create_function_template(&mut env.scope(), env_ptr, None, g, p.data)
@ -271,8 +281,6 @@ fn napi_define_class<'s>(
let setter = p.setter.map(|s| {
create_function_template(&mut env.scope(), env_ptr, None, s, p.data)
});
let mut accessor_property = v8::PropertyAttribute::NONE;
if getter.is_some()
&& setter.is_some()
&& (p.attributes & napi_writable) == 0
@ -280,15 +288,6 @@ fn napi_define_class<'s>(
accessor_property =
accessor_property | v8::PropertyAttribute::READ_ONLY;
}
if p.attributes & napi_enumerable == 0 {
accessor_property =
accessor_property | v8::PropertyAttribute::DONT_ENUM;
}
if p.attributes & napi_configurable == 0 {
accessor_property =
accessor_property | v8::PropertyAttribute::DONT_DELETE;
}
let proto = tpl.prototype_template(&mut env.scope());
proto.set_accessor_property(name, getter, setter, accessor_property);
} else if let Some(method) = p.method {
@ -300,10 +299,14 @@ fn napi_define_class<'s>(
p.data,
);
let proto = tpl.prototype_template(&mut env.scope());
proto.set(name, function.into());
proto.set_with_attr(name, function.into(), accessor_property);
} else {
let proto = tpl.prototype_template(&mut env.scope());
proto.set(name, p.value.unwrap().into());
if (p.attributes & napi_writable) == 0 {
accessor_property =
accessor_property | v8::PropertyAttribute::READ_ONLY;
}
proto.set_with_attr(name, p.value.unwrap().into(), accessor_property);
}
}
@ -1694,15 +1697,14 @@ fn napi_get_new_target(
}
#[napi_sym]
fn napi_call_function(
env_ptr: *mut Env,
recv: napi_value,
func: napi_value,
fn napi_call_function<'s>(
env: &'s mut Env,
recv: napi_value<'s>,
func: napi_value<'s>,
argc: usize,
argv: *const napi_value,
result: *mut napi_value,
argv: *const napi_value<'s>,
result: *mut napi_value<'s>,
) -> napi_status {
let env = check_env!(env_ptr);
check_arg!(env, recv);
let args = if argc > 0 {
check_arg!(env, argv);
@ -1716,11 +1718,11 @@ fn napi_call_function(
let Some(func) =
func.and_then(|f| v8::Local::<v8::Function>::try_from(f).ok())
else {
return napi_set_last_error(env, napi_function_expected);
return napi_function_expected;
};
let Some(v) = func.call(&mut env.scope(), recv.unwrap(), args) else {
return napi_set_last_error(env_ptr, napi_generic_failure);
return napi_generic_failure;
};
if !result.is_null() {
@ -1729,7 +1731,7 @@ fn napi_call_function(
}
}
return napi_clear_last_error(env_ptr);
napi_ok
}
#[napi_sym]

View file

@ -18,3 +18,4 @@
pub mod js_native_api;
pub mod node_api;
pub mod util;
pub mod uv;

View file

@ -547,11 +547,16 @@ fn napi_delete_async_work(env: *mut Env, work: napi_async_work) -> napi_status {
}
#[napi_sym]
fn napi_get_uv_event_loop(env: *mut Env, uv_loop: *mut *mut ()) -> napi_status {
let env = check_env!(env);
fn napi_get_uv_event_loop(
env_ptr: *mut Env,
uv_loop: *mut *mut (),
) -> napi_status {
let env = check_env!(env_ptr);
check_arg!(env, uv_loop);
// There is no uv_loop in Deno
napi_set_last_error(env, napi_generic_failure)
unsafe {
*uv_loop = env_ptr.cast();
}
0
}
#[napi_sym]
@ -687,7 +692,7 @@ impl Drop for TsFn {
if let Some(finalizer) = self.thread_finalize_cb {
unsafe {
(finalizer)(self.env as _, self.thread_finalize_data, ptr::null_mut());
(finalizer)(self.env as _, self.thread_finalize_data, self.context);
}
}
}

View file

@ -2,7 +2,7 @@
[package]
name = "napi_sym"
version = "0.98.0"
version = "0.103.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -152,6 +152,13 @@
"napi_check_object_type_tag",
"node_api_post_finalizer",
"napi_add_async_cleanup_hook",
"napi_remove_async_cleanup_hook"
"napi_remove_async_cleanup_hook",
"uv_mutex_init",
"uv_mutex_lock",
"uv_mutex_unlock",
"uv_mutex_destroy",
"uv_async_init",
"uv_async_send",
"uv_close"
]
}

231
cli/napi/uv.rs Normal file
View file

@ -0,0 +1,231 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use deno_core::parking_lot::Mutex;
use deno_runtime::deno_napi::*;
use std::mem::MaybeUninit;
use std::ptr::addr_of_mut;
#[allow(clippy::print_stderr)]
fn assert_ok(res: c_int) -> c_int {
if res != 0 {
eprintln!("bad result in uv polyfill: {res}");
// don't panic because that might unwind into
// c/c++
std::process::abort();
}
res
}
use crate::napi::js_native_api::napi_create_string_utf8;
use crate::napi::node_api::napi_create_async_work;
use crate::napi::node_api::napi_delete_async_work;
use crate::napi::node_api::napi_queue_async_work;
use std::ffi::c_int;
const UV_MUTEX_SIZE: usize = {
#[cfg(unix)]
{
std::mem::size_of::<libc::pthread_mutex_t>()
}
#[cfg(windows)]
{
std::mem::size_of::<windows_sys::Win32::System::Threading::CRITICAL_SECTION>(
)
}
};
#[repr(C)]
struct uv_mutex_t {
mutex: Mutex<()>,
_padding: [MaybeUninit<usize>; const {
(UV_MUTEX_SIZE - size_of::<Mutex<()>>()) / size_of::<usize>()
}],
}
#[no_mangle]
unsafe extern "C" fn uv_mutex_init(lock: *mut uv_mutex_t) -> c_int {
unsafe {
addr_of_mut!((*lock).mutex).write(Mutex::new(()));
0
}
}
#[no_mangle]
unsafe extern "C" fn uv_mutex_lock(lock: *mut uv_mutex_t) {
unsafe {
let guard = (*lock).mutex.lock();
// forget the guard so it doesn't unlock when it goes out of scope.
// we're going to unlock it manually
std::mem::forget(guard);
}
}
#[no_mangle]
unsafe extern "C" fn uv_mutex_unlock(lock: *mut uv_mutex_t) {
unsafe {
(*lock).mutex.force_unlock();
}
}
#[no_mangle]
unsafe extern "C" fn uv_mutex_destroy(_lock: *mut uv_mutex_t) {
// no cleanup required
}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
#[allow(dead_code)]
enum uv_handle_type {
UV_UNKNOWN_HANDLE = 0,
UV_ASYNC,
UV_CHECK,
UV_FS_EVENT,
UV_FS_POLL,
UV_HANDLE,
UV_IDLE,
UV_NAMED_PIPE,
UV_POLL,
UV_PREPARE,
UV_PROCESS,
UV_STREAM,
UV_TCP,
UV_TIMER,
UV_TTY,
UV_UDP,
UV_SIGNAL,
UV_FILE,
UV_HANDLE_TYPE_MAX,
}
const UV_HANDLE_SIZE: usize = 96;
#[repr(C)]
struct uv_handle_t {
// public members
pub data: *mut c_void,
pub r#loop: *mut uv_loop_t,
pub r#type: uv_handle_type,
_padding: [MaybeUninit<usize>; const {
(UV_HANDLE_SIZE
- size_of::<*mut c_void>()
- size_of::<*mut uv_loop_t>()
- size_of::<uv_handle_type>())
/ size_of::<usize>()
}],
}
#[cfg(unix)]
const UV_ASYNC_SIZE: usize = 128;
#[cfg(windows)]
const UV_ASYNC_SIZE: usize = 224;
#[repr(C)]
struct uv_async_t {
// public members
pub data: *mut c_void,
pub r#loop: *mut uv_loop_t,
pub r#type: uv_handle_type,
// private
async_cb: uv_async_cb,
work: napi_async_work,
_padding: [MaybeUninit<usize>; const {
(UV_ASYNC_SIZE
- size_of::<*mut c_void>()
- size_of::<*mut uv_loop_t>()
- size_of::<uv_handle_type>()
- size_of::<uv_async_cb>()
- size_of::<napi_async_work>())
/ size_of::<usize>()
}],
}
type uv_loop_t = Env;
type uv_async_cb = extern "C" fn(handle: *mut uv_async_t);
#[no_mangle]
unsafe extern "C" fn uv_async_init(
r#loop: *mut uv_loop_t,
// probably uninitialized
r#async: *mut uv_async_t,
async_cb: uv_async_cb,
) -> c_int {
unsafe {
addr_of_mut!((*r#async).r#loop).write(r#loop);
addr_of_mut!((*r#async).r#type).write(uv_handle_type::UV_ASYNC);
addr_of_mut!((*r#async).async_cb).write(async_cb);
let mut resource_name: MaybeUninit<napi_value> = MaybeUninit::uninit();
assert_ok(napi_create_string_utf8(
r#loop,
c"uv_async".as_ptr(),
usize::MAX,
resource_name.as_mut_ptr(),
));
let resource_name = resource_name.assume_init();
let res = napi_create_async_work(
r#loop,
None::<v8::Local<'static, v8::Value>>.into(),
resource_name,
Some(async_exec_wrap),
None,
r#async.cast(),
addr_of_mut!((*r#async).work),
);
-res
}
}
#[no_mangle]
unsafe extern "C" fn uv_async_send(handle: *mut uv_async_t) -> c_int {
unsafe { -napi_queue_async_work((*handle).r#loop, (*handle).work) }
}
type uv_close_cb = unsafe extern "C" fn(*mut uv_handle_t);
#[no_mangle]
unsafe extern "C" fn uv_close(handle: *mut uv_handle_t, close: uv_close_cb) {
unsafe {
if handle.is_null() {
close(handle);
return;
}
if let uv_handle_type::UV_ASYNC = (*handle).r#type {
let handle: *mut uv_async_t = handle.cast();
napi_delete_async_work((*handle).r#loop, (*handle).work);
}
close(handle);
}
}
unsafe extern "C" fn async_exec_wrap(_env: napi_env, data: *mut c_void) {
let data: *mut uv_async_t = data.cast();
unsafe {
((*data).async_cb)(data);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sizes() {
assert_eq!(
std::mem::size_of::<libuv_sys_lite::uv_mutex_t>(),
UV_MUTEX_SIZE
);
assert_eq!(
std::mem::size_of::<libuv_sys_lite::uv_handle_t>(),
UV_HANDLE_SIZE
);
assert_eq!(
std::mem::size_of::<libuv_sys_lite::uv_async_t>(),
UV_ASYNC_SIZE
);
assert_eq!(std::mem::size_of::<uv_mutex_t>(), UV_MUTEX_SIZE);
assert_eq!(std::mem::size_of::<uv_handle_t>(), UV_HANDLE_SIZE);
assert_eq!(std::mem::size_of::<uv_async_t>(), UV_ASYNC_SIZE);
}
}

View file

@ -5,6 +5,7 @@ use std::sync::Arc;
use deno_ast::MediaType;
use deno_ast::ModuleSpecifier;
use deno_core::error::AnyError;
use deno_graph::ParsedSourceStore;
use deno_runtime::deno_fs;
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis;
@ -16,6 +17,7 @@ use serde::Serialize;
use crate::cache::CacheDBHash;
use crate::cache::NodeAnalysisCache;
use crate::cache::ParsedSourceCache;
use crate::resolver::CliNodeResolver;
use crate::util::fs::canonicalize_path_maybe_not_exists;
@ -56,6 +58,7 @@ pub struct CliCjsCodeAnalyzer {
cache: NodeAnalysisCache,
fs: deno_fs::FileSystemRc,
node_resolver: Arc<CliNodeResolver>,
parsed_source_cache: Option<Arc<ParsedSourceCache>>,
}
impl CliCjsCodeAnalyzer {
@ -63,11 +66,13 @@ impl CliCjsCodeAnalyzer {
cache: NodeAnalysisCache,
fs: deno_fs::FileSystemRc,
node_resolver: Arc<CliNodeResolver>,
parsed_source_cache: Option<Arc<ParsedSourceCache>>,
) -> Self {
Self {
cache,
fs,
node_resolver,
parsed_source_cache,
}
}
@ -107,18 +112,26 @@ impl CliCjsCodeAnalyzer {
}
}
let maybe_parsed_source = self
.parsed_source_cache
.as_ref()
.and_then(|c| c.remove_parsed_source(specifier));
let analysis = deno_core::unsync::spawn_blocking({
let specifier = specifier.clone();
let source: Arc<str> = source.into();
move || -> Result<_, deno_ast::ParseDiagnostic> {
let parsed_source = deno_ast::parse_program(deno_ast::ParseParams {
specifier,
text: source,
media_type,
capture_tokens: true,
scope_analysis: false,
maybe_syntax: None,
})?;
let parsed_source =
maybe_parsed_source.map(Ok).unwrap_or_else(|| {
deno_ast::parse_program(deno_ast::ParseParams {
specifier,
text: source,
media_type,
capture_tokens: true,
scope_analysis: false,
maybe_syntax: None,
})
})?;
if parsed_source.is_script() {
let analysis = parsed_source.analyze_cjs();
Ok(CliCjsAnalysis::Cjs {

View file

@ -1,5 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::borrow::Cow;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
@ -21,6 +22,7 @@ use crate::resolver::CliDenoResolverFs;
use super::CliNpmResolver;
use super::InnerCliNpmResolverRef;
use super::ResolvePkgFolderFromDenoReqError;
pub type CliByonmNpmResolverCreateOptions =
ByonmNpmResolverCreateOptions<CliDenoResolverFs>;
@ -31,18 +33,19 @@ pub type CliByonmNpmResolver = ByonmNpmResolver<CliDenoResolverFs>;
struct CliByonmWrapper(Arc<CliByonmNpmResolver>);
impl NodeRequireResolver for CliByonmWrapper {
fn ensure_read_permission(
fn ensure_read_permission<'a>(
&self,
permissions: &mut dyn NodePermissions,
path: &Path,
) -> Result<(), AnyError> {
path: &'a Path,
) -> Result<Cow<'a, Path>, AnyError> {
if !path
.components()
.any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules")
{
_ = permissions.check_read_path(path)?;
permissions.check_read_path(path)
} else {
Ok(Cow::Borrowed(path))
}
Ok(())
}
}
@ -90,10 +93,11 @@ impl CliNpmResolver for CliByonmNpmResolver {
&self,
req: &PackageReq,
referrer: &Url,
) -> Result<PathBuf, AnyError> {
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
ByonmNpmResolver::resolve_pkg_folder_from_deno_module_req(
self, req, referrer,
)
.map_err(ResolvePkgFolderFromDenoReqError::Byonm)
}
fn check_state_hash(&self) -> Option<u64> {

View file

@ -40,7 +40,7 @@ pub fn maybe_auth_header_for_npm_registry(
header::AUTHORIZATION,
header::HeaderValue::from_str(&format!(
"Basic {}",
BASE64_STANDARD.encode(&format!(
BASE64_STANDARD.encode(format!(
"{}:{}",
username.unwrap(),
password.unwrap()

View file

@ -202,10 +202,13 @@ impl RegistryInfoDownloader {
let guard = self.progress_bar.update(package_url.as_str());
let name = name.to_string();
async move {
let maybe_bytes = downloader
.http_client_provider
.get_or_create()?
.download_with_progress(package_url, maybe_auth_header, &guard)
let client = downloader.http_client_provider.get_or_create()?;
let maybe_bytes = client
.download_with_progress_and_retries(
package_url,
maybe_auth_header,
&guard,
)
.await?;
match maybe_bytes {
Some(bytes) => {
@ -239,6 +242,14 @@ impl RegistryInfoDownloader {
fn get_package_url(&self, name: &str) -> Url {
let registry_url = self.npmrc.get_registry_url(name);
// The '/' character in scoped package names "@scope/name" must be
// encoded for older third party registries. Newer registries and
// npm itself support both ways
// - encoded: https://registry.npmjs.org/@rollup%2fplugin-json
// - non-ecoded: https://registry.npmjs.org/@rollup/plugin-json
// To support as many third party registries as possible we'll
// always encode the '/' character.
// list of all characters used in npm packages:
// !, ', (, ), *, -, ., /, [0-9], @, [A-Za-z], _, ~
const ASCII_SET: percent_encoding::AsciiSet =
@ -250,11 +261,14 @@ impl RegistryInfoDownloader {
.remove(b'*')
.remove(b'-')
.remove(b'.')
.remove(b'/')
.remove(b'@')
.remove(b'_')
.remove(b'~');
let name = percent_encoding::utf8_percent_encode(name, &ASCII_SET);
registry_url.join(&name.to_string()).unwrap()
registry_url
// Ensure that scoped package name percent encoding is lower cased
// to match npm.
.join(&name.to_string().replace("%2F", "%2f"))
.unwrap()
}
}

View file

@ -172,7 +172,7 @@ impl TarballCache {
let guard = tarball_cache.progress_bar.update(&dist.tarball);
let result = tarball_cache.http_client_provider
.get_or_create()?
.download_with_progress(tarball_uri, maybe_auth_header, &guard)
.download_with_progress_and_retries(tarball_uri, maybe_auth_header, &guard)
.await;
let maybe_bytes = match result {
Ok(maybe_bytes) => maybe_bytes,

View file

@ -1,5 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::borrow::Cow;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
@ -20,6 +21,7 @@ use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo;
use deno_runtime::colors;
use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NodeRequireResolver;
@ -51,6 +53,7 @@ use self::resolvers::NpmPackageFsResolver;
use super::CliNpmResolver;
use super::InnerCliNpmResolverRef;
use super::ResolvePkgFolderFromDenoReqError;
mod cache;
mod registry;
@ -477,6 +480,25 @@ impl ManagedCliNpmResolver {
self.resolution.resolve_pkg_id_from_pkg_req(req)
}
pub fn ensure_no_pkg_json_dep_errors(&self) -> Result<(), AnyError> {
for err in self.npm_install_deps_provider.pkg_json_dep_errors() {
match err {
deno_package_json::PackageJsonDepValueParseError::VersionReq(_) => {
return Err(
AnyError::from(err.clone())
.context("Failed to install from package.json"),
);
}
deno_package_json::PackageJsonDepValueParseError::Unsupported {
..
} => {
log::warn!("{} {} in package.json", colors::yellow("Warning"), err)
}
}
}
Ok(())
}
/// Ensures that the top level `package.json` dependencies are installed.
/// This may set up the `node_modules` directory.
///
@ -488,6 +510,7 @@ impl ManagedCliNpmResolver {
if !self.top_level_install_flag.raise() {
return Ok(false); // 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);
@ -571,11 +594,11 @@ impl NpmResolver for ManagedCliNpmResolver {
}
impl NodeRequireResolver for ManagedCliNpmResolver {
fn ensure_read_permission(
fn ensure_read_permission<'a>(
&self,
permissions: &mut dyn NodePermissions,
path: &Path,
) -> Result<(), AnyError> {
path: &'a Path,
) -> Result<Cow<'a, Path>, AnyError> {
self.fs_resolver.ensure_read_permission(permissions, path)
}
}
@ -649,9 +672,13 @@ impl CliNpmResolver for ManagedCliNpmResolver {
&self,
req: &PackageReq,
_referrer: &ModuleSpecifier,
) -> Result<PathBuf, AnyError> {
let pkg_id = self.resolve_pkg_id_from_pkg_req(req)?;
self.resolve_pkg_folder_from_pkg_id(&pkg_id)
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
let pkg_id = self
.resolve_pkg_id_from_pkg_req(req)
.map_err(|err| ResolvePkgFolderFromDenoReqError::Managed(err.into()))?;
self
.resolve_pkg_folder_from_pkg_id(&pkg_id)
.map_err(ResolvePkgFolderFromDenoReqError::Managed)
}
fn check_state_hash(&self) -> Option<u64> {

View file

@ -3,6 +3,7 @@
pub mod bin_entries;
pub mod lifecycle_scripts;
use std::borrow::Cow;
use std::collections::HashMap;
use std::io::ErrorKind;
use std::path::Path;
@ -62,11 +63,12 @@ pub trait NpmPackageFsResolver: Send + Sync {
async fn cache_packages(&self) -> Result<(), AnyError>;
fn ensure_read_permission(
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn ensure_read_permission<'a>(
&self,
permissions: &mut dyn NodePermissions,
path: &Path,
) -> Result<(), AnyError>;
path: &'a Path,
) -> Result<Cow<'a, Path>, AnyError>;
}
#[derive(Debug)]
@ -85,11 +87,15 @@ impl RegistryReadPermissionChecker {
}
}
pub fn ensure_registry_read_permission(
pub fn ensure_registry_read_permission<'a>(
&self,
permissions: &mut dyn NodePermissions,
path: &Path,
) -> Result<(), AnyError> {
path: &'a Path,
) -> Result<Cow<'a, Path>, AnyError> {
if permissions.query_read_all() {
return Ok(Cow::Borrowed(path)); // skip permissions checks below
}
// allow reading if it's in the node_modules
let is_path_in_node_modules = path.starts_with(&self.registry_path)
&& path
@ -118,20 +124,20 @@ impl RegistryReadPermissionChecker {
},
}
};
let Some(registry_path_canon) = canonicalize(&self.registry_path)? else {
return Ok(()); // not exists, allow reading
};
let Some(path_canon) = canonicalize(path)? else {
return Ok(()); // not exists, allow reading
};
if path_canon.starts_with(registry_path_canon) {
return Ok(());
if let Some(registry_path_canon) = canonicalize(&self.registry_path)? {
if let Some(path_canon) = canonicalize(path)? {
if path_canon.starts_with(registry_path_canon) {
return Ok(Cow::Owned(path_canon));
}
} else if path.starts_with(registry_path_canon)
|| path.starts_with(&self.registry_path)
{
return Ok(Cow::Borrowed(path));
}
}
}
_ = permissions.check_read_path(path)?;
Ok(())
permissions.check_read_path(path)
}
}

View file

@ -2,6 +2,8 @@
use super::bin_entries::BinEntries;
use crate::args::LifecycleScriptsConfig;
use crate::task_runner::TaskStdio;
use crate::util::progress_bar::ProgressBar;
use deno_core::anyhow::Context;
use deno_npm::resolution::NpmResolutionSnapshot;
use deno_runtime::deno_io::FromRawIoHandle;
@ -148,6 +150,7 @@ impl<'a> LifecycleScripts<'a> {
snapshot: &NpmResolutionSnapshot,
packages: &[NpmResolutionPackage],
root_node_modules_dir_path: Option<&Path>,
progress_bar: &ProgressBar,
) -> Result<(), AnyError> {
self.warn_not_run_scripts()?;
let get_package_path =
@ -201,7 +204,15 @@ impl<'a> LifecycleScripts<'a> {
{
continue;
}
let exit_code = crate::task_runner::run_task(
let _guard = progress_bar.update_with_prompt(
crate::util::progress_bar::ProgressMessagePrompt::Initialize,
&format!("{}: running '{script_name}' script", package.id.nv),
);
let crate::task_runner::TaskResult {
exit_code,
stderr,
stdout,
} = crate::task_runner::run_task(
crate::task_runner::RunTaskOptions {
task_name: script_name,
script,
@ -211,15 +222,37 @@ impl<'a> LifecycleScripts<'a> {
init_cwd,
argv: &[],
root_node_modules_dir: root_node_modules_dir_path,
stdio: Some(crate::task_runner::TaskIo {
stderr: TaskStdio::piped(),
stdout: TaskStdio::piped(),
}),
},
)
.await?;
let stdout = stdout.unwrap();
let stderr = stderr.unwrap();
if exit_code != 0 {
log::warn!(
"error: script '{}' in '{}' failed with exit code {}",
"error: script '{}' in '{}' failed with exit code {}{}{}",
script_name,
package.id.nv,
exit_code,
if !stdout.trim_ascii().is_empty() {
format!(
"\nstdout:\n{}\n",
String::from_utf8_lossy(&stdout).trim()
)
} else {
String::new()
},
if !stderr.trim_ascii().is_empty() {
format!(
"\nstderr:\n{}\n",
String::from_utf8_lossy(&stderr).trim()
)
} else {
String::new()
},
);
failed_packages.push(&package.id.nv);
// assume if earlier script fails, later ones will fail too

View file

@ -183,11 +183,11 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver {
Ok(())
}
fn ensure_read_permission(
fn ensure_read_permission<'a>(
&self,
permissions: &mut dyn NodePermissions,
path: &Path,
) -> Result<(), AnyError> {
path: &'a Path,
) -> Result<Cow<'a, Path>, AnyError> {
self
.registry_read_permission_checker
.ensure_registry_read_permission(permissions, path)

View file

@ -257,11 +257,11 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver {
.await
}
fn ensure_read_permission(
fn ensure_read_permission<'a>(
&self,
permissions: &mut dyn NodePermissions,
path: &Path,
) -> Result<(), AnyError> {
path: &'a Path,
) -> Result<Cow<'a, Path>, AnyError> {
self
.registry_read_permission_checker
.ensure_registry_read_permission(permissions, path)
@ -343,6 +343,14 @@ async fn sync_resolution_with_fs(
},
);
let packages_with_deprecation_warnings = Arc::new(Mutex::new(Vec::new()));
let mut package_tags: HashMap<&PackageNv, Vec<&str>> = HashMap::new();
for (package_req, package_nv) in snapshot.package_reqs() {
if let Some(tag) = package_req.version_req.tag() {
package_tags.entry(package_nv).or_default().push(tag);
}
}
for package in &package_partitions.packages {
if let Some(current_pkg) =
newest_packages_by_name.get_mut(&package.id.nv.name)
@ -357,11 +365,29 @@ async fn sync_resolution_with_fs(
let package_folder_name =
get_package_folder_id_folder_name(&package.get_package_cache_folder_id());
let folder_path = deno_local_registry_dir.join(&package_folder_name);
let tags = package_tags
.get(&package.id.nv)
.map(|tags| tags.join(","))
.unwrap_or_default();
enum PackageFolderState {
UpToDate,
Uninitialized,
TagsOutdated,
}
let initialized_file = folder_path.join(".initialized");
let package_state = std::fs::read_to_string(&initialized_file)
.map(|s| {
if s != tags {
PackageFolderState::TagsOutdated
} else {
PackageFolderState::UpToDate
}
})
.unwrap_or(PackageFolderState::Uninitialized);
if !cache
.cache_setting()
.should_use_for_npm_package(&package.id.nv.name)
|| !initialized_file.exists()
|| matches!(package_state, PackageFolderState::Uninitialized)
{
// cache bust the dep from the dep setup cache so the symlinks
// are forced to be recreated
@ -371,6 +397,7 @@ async fn sync_resolution_with_fs(
let bin_entries_to_setup = bin_entries.clone();
let packages_with_deprecation_warnings =
packages_with_deprecation_warnings.clone();
cache_futures.push(async move {
tarball_cache
.ensure_package(&package.id.nv, &package.dist)
@ -389,7 +416,7 @@ async fn sync_resolution_with_fs(
move || {
clone_dir_recursive(&cache_folder, &package_path)?;
// write out a file that indicates this folder has been initialized
fs::write(initialized_file, "")?;
fs::write(initialized_file, tags)?;
Ok::<_, AnyError>(())
}
@ -410,6 +437,8 @@ async fn sync_resolution_with_fs(
drop(pb_guard); // explicit for clarity
Ok::<_, AnyError>(())
});
} else if matches!(package_state, PackageFolderState::TagsOutdated) {
fs::write(initialized_file, tags)?;
}
let sub_node_modules = folder_path.join("node_modules");
@ -518,9 +547,9 @@ async fn sync_resolution_with_fs(
// linked into the root
match found_names.entry(remote_alias) {
Entry::Occupied(nv) => {
alias_clashes
|| remote.req.name != nv.get().name // alias to a different package (in case of duplicate aliases)
|| !remote.req.version_req.matches(&nv.get().version) // incompatible version
// alias to a different package (in case of duplicate aliases)
// or the version doesn't match the version in the root node_modules
alias_clashes || &remote_pkg.id.nv != *nv.get()
}
Entry::Vacant(entry) => {
entry.insert(&remote_pkg.id.nv);
@ -684,6 +713,7 @@ async fn sync_resolution_with_fs(
snapshot,
&package_partitions.packages,
Some(root_node_modules_dir_path),
progress_bar,
)
.await?;

View file

@ -14,11 +14,13 @@ use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_npm::registry::NpmPackageInfo;
use deno_resolver::npm::ByonmNpmResolver;
use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError;
use deno_runtime::deno_node::NodeRequireResolver;
use deno_runtime::ops::process::NpmProcessStateProvider;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
use node_resolver::NpmResolver;
use thiserror::Error;
use crate::args::npm_registry_url;
use crate::file_fetcher::FileFetcher;
@ -29,6 +31,14 @@ pub use self::managed::CliNpmResolverManagedCreateOptions;
pub use self::managed::CliNpmResolverManagedSnapshotOption;
pub use self::managed::ManagedCliNpmResolver;
#[derive(Debug, Error)]
pub enum ResolvePkgFolderFromDenoReqError {
#[error(transparent)]
Managed(deno_core::error::AnyError),
#[error(transparent)]
Byonm(#[from] ByonmResolvePkgFolderFromDenoReqError),
}
pub enum CliNpmResolverCreateOptions {
Managed(CliNpmResolverManagedCreateOptions),
Byonm(CliByonmNpmResolverCreateOptions),
@ -93,7 +103,7 @@ pub trait CliNpmResolver: NpmResolver {
&self,
req: &PackageReq,
referrer: &ModuleSpecifier,
) -> Result<PathBuf, AnyError>;
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError>;
/// Returns a hash returning the state of the npm resolver
/// or `None` if the state currently can't be determined.

View file

@ -43,7 +43,6 @@ use node_resolver::NodeModuleKind;
use node_resolver::NodeResolution;
use node_resolver::NodeResolutionMode;
use node_resolver::PackageJson;
use std::borrow::Cow;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
@ -53,7 +52,9 @@ use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
use crate::node::CliNodeCodeTranslator;
use crate::npm::CliNpmResolver;
use crate::npm::InnerCliNpmResolverRef;
use crate::util::path::specifier_has_extension;
use crate::util::sync::AtomicFlag;
use crate::util::text_encoding::from_utf8_lossy_owned;
pub struct ModuleCodeStringSource {
pub code: ModuleSourceCode,
@ -215,7 +216,7 @@ impl CliNodeResolver {
referrer: &ModuleSpecifier,
mode: NodeResolutionMode,
) -> Result<NodeResolution, NodeResolveError> {
let referrer_kind = if self.cjs_resolutions.contains(referrer) {
let referrer_kind = if self.cjs_resolutions.is_known_cjs(referrer) {
NodeModuleKind::Cjs
} else {
NodeModuleKind::Esm
@ -310,9 +311,7 @@ impl CliNodeResolver {
if self.in_npm_package(&specifier) {
let resolution =
self.node_resolver.url_to_node_resolution(specifier)?;
if let NodeResolution::CommonJs(specifier) = &resolution {
self.cjs_resolutions.insert(specifier.clone());
}
let resolution = self.handle_node_resolution(resolution);
return Ok(Some(resolution.into_url()));
}
}
@ -333,12 +332,17 @@ impl CliNodeResolver {
) -> NodeResolution {
if let NodeResolution::CommonJs(specifier) = &resolution {
// remember that this was a common js resolution
self.cjs_resolutions.insert(specifier.clone());
self.mark_cjs_resolution(specifier.clone());
}
resolution
}
pub fn mark_cjs_resolution(&self, specifier: ModuleSpecifier) {
self.cjs_resolutions.insert(specifier);
}
}
// todo(dsherret): move to module_loader.rs
#[derive(Clone)]
pub struct NpmModuleLoader {
cjs_resolutions: Arc<CjsResolutionStore>,
@ -362,18 +366,9 @@ impl NpmModuleLoader {
}
}
pub async fn load_if_in_npm_package(
&self,
specifier: &ModuleSpecifier,
maybe_referrer: Option<&ModuleSpecifier>,
) -> Option<Result<ModuleCodeStringSource, AnyError>> {
if self.node_resolver.in_npm_package(specifier)
|| (specifier.scheme() == "file" && specifier.path().ends_with(".cjs"))
{
Some(self.load(specifier, maybe_referrer).await)
} else {
None
}
pub fn if_in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
self.node_resolver.in_npm_package(specifier)
|| self.cjs_resolutions.is_known_cjs(specifier)
}
pub async fn load(
@ -418,16 +413,9 @@ impl NpmModuleLoader {
}
})?;
let code = if self.cjs_resolutions.contains(specifier)
|| (specifier.scheme() == "file" && specifier.path().ends_with(".cjs"))
{
let code = if self.cjs_resolutions.is_known_cjs(specifier) {
// translate cjs to esm if it's cjs and inject node globals
let code = match String::from_utf8_lossy(&code) {
Cow::Owned(code) => code,
// SAFETY: `String::from_utf8_lossy` guarantees that the result is valid
// UTF-8 if `Cow::Borrowed` is returned.
Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(code) },
};
let code = from_utf8_lossy_owned(code);
ModuleSourceCode::String(
self
.node_code_translator
@ -452,8 +440,12 @@ impl NpmModuleLoader {
pub struct CjsResolutionStore(DashSet<ModuleSpecifier>);
impl CjsResolutionStore {
pub fn contains(&self, specifier: &ModuleSpecifier) -> bool {
self.0.contains(specifier)
pub fn is_known_cjs(&self, specifier: &ModuleSpecifier) -> bool {
if specifier.scheme() != "file" {
return false;
}
specifier_has_extension(specifier, "cjs") || self.0.contains(specifier)
}
pub fn insert(&self, specifier: ModuleSpecifier) {

View file

@ -528,6 +528,7 @@
"bare-node-builtins",
"byonm",
"cron",
"detect-cjs",
"ffi",
"fs",
"http",

View file

@ -468,7 +468,11 @@ impl<'a> DenoCompileBinaryWriter<'a> {
self
.http_client_provider
.get_or_create()?
.download_with_progress(download_url.parse()?, None, &progress)
.download_with_progress_and_retries(
download_url.parse()?,
None,
&progress,
)
.await?
};
let bytes = match maybe_bytes {
@ -622,6 +626,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
unstable_config: UnstableConfig {
legacy_flag_enabled: false,
bare_node_builtins: cli_options.unstable_bare_node_builtins(),
detect_cjs: cli_options.unstable_detect_cjs(),
sloppy_imports: cli_options.unstable_sloppy_imports(),
features: cli_options.unstable_features(),
},

View file

@ -586,6 +586,7 @@ pub async fn run(
node_analysis_cache,
fs.clone(),
cli_node_resolver.clone(),
None,
);
let node_code_translator = Arc::new(NodeCodeTranslator::new(
cjs_esm_code_analyzer,
@ -651,7 +652,7 @@ pub async fn run(
workspace_resolver,
node_resolver: cli_node_resolver.clone(),
npm_module_loader: Arc::new(NpmModuleLoader::new(
cjs_resolutions,
cjs_resolutions.clone(),
node_code_translator,
fs.clone(),
cli_node_resolver,
@ -696,6 +697,7 @@ pub async fn run(
});
let worker_factory = CliMainWorkerFactory::new(
Arc::new(BlobStore::default()),
cjs_resolutions,
// Code cache is not supported for standalone binary yet.
None,
feature_checker,
@ -738,6 +740,7 @@ pub async fn run(
node_ipc: None,
serve_port: None,
serve_host: None,
unstable_detect_cjs: metadata.unstable_config.detect_cjs,
},
);

View file

@ -16,8 +16,11 @@ use deno_task_shell::ExecutableCommand;
use deno_task_shell::ExecuteResult;
use deno_task_shell::ShellCommand;
use deno_task_shell::ShellCommandContext;
use deno_task_shell::ShellPipeReader;
use deno_task_shell::ShellPipeWriter;
use lazy_regex::Lazy;
use regex::Regex;
use tokio::task::JoinHandle;
use tokio::task::LocalSet;
use crate::npm::CliNpmResolver;
@ -36,6 +39,35 @@ pub fn get_script_with_args(script: &str, argv: &[String]) -> String {
script.trim().to_owned()
}
pub struct TaskStdio(Option<ShellPipeReader>, ShellPipeWriter);
impl TaskStdio {
pub fn stdout() -> Self {
Self(None, ShellPipeWriter::stdout())
}
pub fn stderr() -> Self {
Self(None, ShellPipeWriter::stderr())
}
pub fn piped() -> Self {
let (r, w) = deno_task_shell::pipe();
Self(Some(r), w)
}
}
pub struct TaskIo {
pub stdout: TaskStdio,
pub stderr: TaskStdio,
}
impl Default for TaskIo {
fn default() -> Self {
Self {
stderr: TaskStdio::stderr(),
stdout: TaskStdio::stdout(),
}
}
}
pub struct RunTaskOptions<'a> {
pub task_name: &'a str,
pub script: &'a str,
@ -45,24 +77,69 @@ pub struct RunTaskOptions<'a> {
pub argv: &'a [String],
pub custom_commands: HashMap<String, Rc<dyn ShellCommand>>,
pub root_node_modules_dir: Option<&'a Path>,
pub stdio: Option<TaskIo>,
}
pub type TaskCustomCommands = HashMap<String, Rc<dyn ShellCommand>>;
pub async fn run_task(opts: RunTaskOptions<'_>) -> Result<i32, AnyError> {
pub struct TaskResult {
pub exit_code: i32,
pub stdout: Option<Vec<u8>>,
pub stderr: Option<Vec<u8>>,
}
pub async fn run_task(
opts: RunTaskOptions<'_>,
) -> Result<TaskResult, AnyError> {
let script = get_script_with_args(opts.script, opts.argv);
let seq_list = deno_task_shell::parser::parse(&script)
.with_context(|| format!("Error parsing script '{}'.", opts.task_name))?;
let env_vars =
prepare_env_vars(opts.env_vars, opts.init_cwd, opts.root_node_modules_dir);
let state =
deno_task_shell::ShellState::new(env_vars, opts.cwd, opts.custom_commands);
let stdio = opts.stdio.unwrap_or_default();
let (
TaskStdio(stdout_read, stdout_write),
TaskStdio(stderr_read, stderr_write),
) = (stdio.stdout, stdio.stderr);
fn read(reader: ShellPipeReader) -> JoinHandle<Result<Vec<u8>, AnyError>> {
tokio::task::spawn_blocking(move || {
let mut buf = Vec::new();
reader.pipe_to(&mut buf)?;
Ok(buf)
})
}
let stdout = stdout_read.map(read);
let stderr = stderr_read.map(read);
let local = LocalSet::new();
let future = deno_task_shell::execute(
seq_list,
env_vars,
opts.cwd,
opts.custom_commands,
);
Ok(local.run_until(future).await)
let future = async move {
let exit_code = deno_task_shell::execute_with_pipes(
seq_list,
state,
ShellPipeReader::stdin(),
stdout_write,
stderr_write,
)
.await;
Ok::<_, AnyError>(TaskResult {
exit_code,
stdout: if let Some(stdout) = stdout {
Some(stdout.await??)
} else {
None
},
stderr: if let Some(stderr) = stderr {
Some(stderr.await??)
} else {
None
},
})
};
local.run_until(future).await
}
fn prepare_env_vars(

View file

@ -54,6 +54,16 @@ pub async fn compile(
);
}
if cli_options.unstable_detect_cjs() {
log::warn!(
concat!(
"{} --unstable-detect-cjs is not properly supported in deno compile. ",
"The compiled executable may encounter runtime errors.",
),
crate::colors::yellow("Warning"),
);
}
let output_path = resolve_compile_executable_output_path(
http_client,
&compile_flags,

View file

@ -195,7 +195,7 @@ pub async fn doc(
kind_with_drilldown:
deno_doc::html::DocNodeKindWithDrilldown::Other(node.kind()),
inner: Rc::new(node),
drilldown_parent_kind: None,
drilldown_name: None,
parent: None,
})
.collect::<Vec<_>>(),
@ -262,7 +262,7 @@ pub async fn doc(
}
struct DocResolver {
deno_ns: std::collections::HashSet<Vec<String>>,
deno_ns: std::collections::HashMap<Vec<String>, Option<Rc<ShortPath>>>,
strip_trailing_html: bool,
}
@ -286,7 +286,7 @@ impl deno_doc::html::HrefResolver for DocResolver {
}
fn resolve_global_symbol(&self, symbol: &[String]) -> Option<String> {
if self.deno_ns.contains(symbol) {
if self.deno_ns.contains_key(symbol) {
Some(format!(
"https://deno.land/api@v{}?s={}",
env!("CARGO_PKG_VERSION"),
@ -455,7 +455,7 @@ impl deno_doc::html::HrefResolver for NodeDocResolver {
fn generate_docs_directory(
doc_nodes_by_url: IndexMap<ModuleSpecifier, Vec<doc::DocNode>>,
html_options: &DocHtmlFlag,
deno_ns: std::collections::HashSet<Vec<String>>,
deno_ns: std::collections::HashMap<Vec<String>, Option<Rc<ShortPath>>>,
rewrite_map: Option<IndexMap<ModuleSpecifier, String>>,
) -> Result<(), AnyError> {
let cwd = std::env::current_dir().context("Failed to get CWD")?;
@ -513,7 +513,6 @@ fn generate_docs_directory(
rewrite_map,
href_resolver,
usage_composer: None,
composable_output: false,
category_docs,
disable_search: internal_env.is_some(),
symbol_redirect_map,

View file

@ -978,6 +978,7 @@ fn get_resolved_malva_config(
single_line_top_level_declarations: false,
selector_override_comment_directive: "deno-fmt-selector-override".into(),
ignore_comment_directive: "deno-fmt-ignore".into(),
ignore_file_comment_directive: "deno-fmt-ignore-file".into(),
};
FormatOptions {
@ -1036,6 +1037,7 @@ fn get_resolved_markup_fmt_config(
svelte_directive_shorthand: Some(true),
astro_attr_shorthand: Some(true),
ignore_comment_directive: "deno-fmt-ignore".into(),
ignore_file_comment_directive: "deno-fmt-ignore-file".into(),
};
FormatOptions {

View file

@ -11,12 +11,14 @@ use deno_core::anyhow::bail;
use deno_core::error::AnyError;
use deno_core::resolve_url_or_path;
use deno_core::serde_json;
use deno_core::url;
use deno_graph::Dependency;
use deno_graph::GraphKind;
use deno_graph::Module;
use deno_graph::ModuleError;
use deno_graph::ModuleGraph;
use deno_graph::Resolution;
use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::resolution::NpmResolutionSnapshot;
use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage;
@ -47,20 +49,23 @@ pub async fn info(
let module_graph_creator = factory.module_graph_creator().await?;
let npm_resolver = factory.npm_resolver().await?;
let maybe_lockfile = cli_options.maybe_lockfile();
let npmrc = cli_options.npmrc();
let resolver = factory.workspace_resolver().await?;
let maybe_import_specifier =
if let Some(import_map) = resolver.maybe_import_map() {
if let Ok(imports_specifier) =
import_map.resolve(&specifier, import_map.base_url())
{
Some(imports_specifier)
} else {
None
}
let cwd_url =
url::Url::from_directory_path(cli_options.initial_cwd()).unwrap();
let maybe_import_specifier = if let Some(import_map) =
resolver.maybe_import_map()
{
if let Ok(imports_specifier) = import_map.resolve(&specifier, &cwd_url) {
Some(imports_specifier)
} else {
None
};
}
} else {
None
};
let specifier = match maybe_import_specifier {
Some(specifier) => specifier,
@ -88,7 +93,8 @@ pub async fn info(
JSON_SCHEMA_VERSION.into(),
);
}
add_npm_packages_to_json(&mut json_graph, npm_resolver.as_ref());
add_npm_packages_to_json(&mut json_graph, npm_resolver.as_ref(), npmrc);
display::write_json_to_stdout(&json_graph)?;
} else {
let mut output = String::new();
@ -185,6 +191,7 @@ fn print_cache_info(
fn add_npm_packages_to_json(
json: &mut serde_json::Value,
npm_resolver: &dyn CliNpmResolver,
npmrc: &ResolvedNpmRc,
) {
let Some(npm_resolver) = npm_resolver.as_managed() else {
return; // does not include byonm to deno info's output
@ -195,45 +202,28 @@ fn add_npm_packages_to_json(
let json = json.as_object_mut().unwrap();
let modules = json.get_mut("modules").and_then(|m| m.as_array_mut());
if let Some(modules) = modules {
if modules.len() == 1
&& modules[0].get("kind").and_then(|k| k.as_str()) == Some("npm")
{
// If there is only one module and it's "external", then that means
// someone provided an npm specifier as a cli argument. In this case,
// we want to show which npm package the cli argument resolved to.
let module = &mut modules[0];
let maybe_package = module
.get("specifier")
.and_then(|k| k.as_str())
.and_then(|specifier| NpmPackageNvReference::from_str(specifier).ok())
.and_then(|package_ref| {
snapshot
.resolve_package_from_deno_module(package_ref.nv())
.ok()
});
if let Some(pkg) = maybe_package {
if let Some(module) = module.as_object_mut() {
module
.insert("npmPackage".to_string(), pkg.id.as_serialized().into());
}
}
} else {
// Filter out npm package references from the modules and instead
// have them only listed as dependencies. This is done because various
// npm specifiers modules in the graph are really just unresolved
// references. So there could be listed multiple npm specifiers
// that would resolve to a single npm package.
for i in (0..modules.len()).rev() {
if matches!(
modules[i].get("kind").and_then(|k| k.as_str()),
Some("npm") | Some("external")
) {
modules.remove(i);
}
}
}
for module in modules.iter_mut() {
if matches!(module.get("kind").and_then(|k| k.as_str()), Some("npm")) {
// If there is only one module and it's "external", then that means
// someone provided an npm specifier as a cli argument. In this case,
// we want to show which npm package the cli argument resolved to.
let maybe_package = module
.get("specifier")
.and_then(|k| k.as_str())
.and_then(|specifier| NpmPackageNvReference::from_str(specifier).ok())
.and_then(|package_ref| {
snapshot
.resolve_package_from_deno_module(package_ref.nv())
.ok()
});
if let Some(pkg) = maybe_package {
if let Some(module) = module.as_object_mut() {
module
.insert("npmPackage".to_string(), pkg.id.as_serialized().into());
}
}
}
let dependencies = module
.get_mut("dependencies")
.and_then(|d| d.as_array_mut());
@ -265,7 +255,7 @@ fn add_npm_packages_to_json(
let mut json_packages = serde_json::Map::with_capacity(sorted_packages.len());
for pkg in sorted_packages {
let mut kv = serde_json::Map::new();
kv.insert("name".to_string(), pkg.id.nv.name.to_string().into());
kv.insert("name".to_string(), pkg.id.nv.name.clone().into());
kv.insert("version".to_string(), pkg.id.nv.version.to_string().into());
let mut deps = pkg.dependencies.values().collect::<Vec<_>>();
deps.sort();
@ -274,6 +264,8 @@ fn add_npm_packages_to_json(
.map(|id| serde_json::Value::String(id.as_serialized()))
.collect::<Vec<_>>();
kv.insert("dependencies".to_string(), deps.into());
let registry_url = npmrc.get_registry_url(&pkg.id.nv.name);
kv.insert("registryUrl".to_string(), registry_url.to_string().into());
json_packages.insert(pkg.id.as_serialized(), kv.into());
}

View file

@ -298,6 +298,10 @@ async fn install_local(
}
InstallFlagsLocal::TopLevel => {
let factory = CliFactory::from_flags(flags);
// surface any errors in the package.json
if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() {
npm_resolver.ensure_no_pkg_json_dep_errors()?;
}
crate::tools::registry::cache_top_level_deps(&factory, None).await?;
if let Some(lockfile) = factory.cli_options()?.maybe_lockfile() {

View file

@ -58,9 +58,9 @@ pub fn install() -> Result<(), AnyError> {
let f = std::fs::File::create(kernel_json_path)?;
serde_json::to_writer_pretty(f, &json_data)?;
install_icon(&user_data_dir, "logo-32x32.png", DENO_ICON_32)?;
install_icon(&user_data_dir, "logo-64x64.png", DENO_ICON_64)?;
install_icon(&user_data_dir, "logo-svg.svg", DENO_ICON_SVG)?;
install_icon(&kernel_dir, "logo-32x32.png", DENO_ICON_32)?;
install_icon(&kernel_dir, "logo-64x64.png", DENO_ICON_64)?;
install_icon(&kernel_dir, "logo-svg.svg", DENO_ICON_SVG)?;
log::info!("✅ Deno kernelspec installed successfully.");
Ok(())

View file

@ -357,56 +357,74 @@ pub struct JupyterReplSession {
impl JupyterReplSession {
pub async fn start(&mut self) {
let mut poll_worker = true;
loop {
let Some(msg) = self.rx.recv().await else {
break;
};
let resp = match msg {
JupyterReplRequest::LspCompletions {
line_text,
position,
} => JupyterReplResponse::LspCompletions(
self.lsp_completions(&line_text, position).await,
),
JupyterReplRequest::JsGetProperties { object_id } => {
JupyterReplResponse::JsGetProperties(
self.get_properties(object_id).await,
)
}
JupyterReplRequest::JsEvaluate { expr } => {
JupyterReplResponse::JsEvaluate(self.evaluate(expr).await)
}
JupyterReplRequest::JsGlobalLexicalScopeNames => {
JupyterReplResponse::JsGlobalLexicalScopeNames(
self.global_lexical_scope_names().await,
)
}
JupyterReplRequest::JsEvaluateLineWithObjectWrapping { line } => {
JupyterReplResponse::JsEvaluateLineWithObjectWrapping(
self.evaluate_line_with_object_wrapping(&line).await,
)
}
JupyterReplRequest::JsCallFunctionOnArgs {
function_declaration,
args,
} => JupyterReplResponse::JsCallFunctionOnArgs(
self
.call_function_on_args(function_declaration, &args)
.await,
),
JupyterReplRequest::JsCallFunctionOn { arg0, arg1 } => {
JupyterReplResponse::JsCallFunctionOn(
self.call_function_on(arg0, arg1).await,
)
}
};
tokio::select! {
biased;
let Ok(()) = self.tx.send(resp) else {
break;
};
maybe_message = self.rx.recv() => {
let Some(msg) = maybe_message else {
break;
};
if self.handle_message(msg).await.is_err() {
break;
}
poll_worker = true;
},
_ = self.repl_session.run_event_loop(), if poll_worker => {
poll_worker = false;
}
}
}
}
async fn handle_message(
&mut self,
msg: JupyterReplRequest,
) -> Result<(), AnyError> {
let resp = match msg {
JupyterReplRequest::LspCompletions {
line_text,
position,
} => JupyterReplResponse::LspCompletions(
self.lsp_completions(&line_text, position).await,
),
JupyterReplRequest::JsGetProperties { object_id } => {
JupyterReplResponse::JsGetProperties(
self.get_properties(object_id).await,
)
}
JupyterReplRequest::JsEvaluate { expr } => {
JupyterReplResponse::JsEvaluate(self.evaluate(expr).await)
}
JupyterReplRequest::JsGlobalLexicalScopeNames => {
JupyterReplResponse::JsGlobalLexicalScopeNames(
self.global_lexical_scope_names().await,
)
}
JupyterReplRequest::JsEvaluateLineWithObjectWrapping { line } => {
JupyterReplResponse::JsEvaluateLineWithObjectWrapping(
self.evaluate_line_with_object_wrapping(&line).await,
)
}
JupyterReplRequest::JsCallFunctionOnArgs {
function_declaration,
args,
} => JupyterReplResponse::JsCallFunctionOnArgs(
self
.call_function_on_args(function_declaration, &args)
.await,
),
JupyterReplRequest::JsCallFunctionOn { arg0, arg1 } => {
JupyterReplResponse::JsCallFunctionOn(
self.call_function_on(arg0, arg1).await,
)
}
};
self.tx.send(resp).map_err(|e| e.into())
}
pub async fn lsp_completions(
&mut self,
line_text: &str,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View file

@ -1 +1,17 @@
<svg viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg"><g clip-path="url(#clip0_29_599)"><path d="M15 0C23.2843 0 30 6.71572 30 15C30 23.2843 23.2843 30 15 30C6.71572 30 0 23.2843 0 15C0 6.71572 6.71572 0 15 0Z" fill="currentColor"></path><path d="M14.6635 22.3394C14.2788 22.2357 13.8831 22.4584 13.7705 22.8381L13.7655 22.8558L12.7694 26.5472L12.7649 26.565C12.6711 26.9498 12.9011 27.3414 13.2858 27.4451C13.6704 27.549 14.0661 27.3263 14.1787 26.9465L14.1837 26.9289L15.1797 23.2375L15.1843 23.2196C15.1911 23.1919 15.1962 23.164 15.1997 23.1362L15.2026 23.1084L15.179 22.9888L15.1445 22.8166L15.1227 22.7091C15.076 22.619 15.0111 22.5396 14.932 22.4759C14.853 22.4123 14.7615 22.3658 14.6635 22.3394ZM7.7224 18.5379C7.70424 18.5741 7.68883 18.6123 7.67658 18.6522L7.66967 18.6763L6.67358 22.3677L6.669 22.3856C6.57525 22.7704 6.80524 23.1619 7.1899 23.2657C7.57451 23.3695 7.97026 23.1469 8.08287 22.7671L8.08779 22.7494L8.99096 19.4023C8.51793 19.1518 8.09336 18.8628 7.7224 18.5379ZM5.34707 14.2929C4.9624 14.1891 4.56666 14.4117 4.4541 14.7915L4.44912 14.8092L3.45303 18.5006L3.44846 18.5184C3.35471 18.9032 3.58469 19.2947 3.96936 19.3985C4.35397 19.5023 4.74971 19.2797 4.86232 18.8999L4.86725 18.8822L5.86334 15.1908L5.86791 15.173C5.96166 14.7882 5.73174 14.3967 5.34707 14.2929ZM27.682 13.4546C27.2973 13.3508 26.9015 13.5734 26.789 13.9532L26.784 13.9709L25.7879 17.6623L25.7833 17.6801C25.6896 18.0649 25.9196 18.4564 26.3042 18.5602C26.6889 18.664 27.0846 18.4414 27.1972 18.0616L27.2021 18.0439L28.1982 14.3525L28.2028 14.3347C28.2965 13.9499 28.0666 13.5584 27.682 13.4546ZM3.17781 8.52527C2.34361 10.0444 1.81243 11.7112 1.61377 13.4329C1.7088 13.5412 1.83381 13.619 1.97301 13.6563C2.35768 13.7602 2.75342 13.5375 2.86598 13.1577L2.87096 13.1401L3.86705 9.44865L3.87162 9.43084C3.96537 9.04599 3.73539 8.65447 3.35072 8.5507C3.2943 8.53547 3.23623 8.52694 3.17781 8.52527ZM25.159 8.5507C24.7744 8.44687 24.3786 8.66953 24.266 9.04933L24.2611 9.06697L23.265 12.7584L23.2604 12.7762C23.1667 13.161 23.3966 13.5526 23.7813 13.6563C24.1659 13.7602 24.5617 13.5375 24.6743 13.1577L24.6792 13.1401L25.6753 9.44865L25.6799 9.43084C25.7736 9.04599 25.5436 8.65447 25.159 8.5507Z" fill="white"></path><path d="M7.51285 5.04065C7.12824 4.93682 6.73249 5.15948 6.61988 5.53929L6.61495 5.55692L5.61886 9.24833L5.61429 9.26614C5.52054 9.65098 5.75052 10.0425 6.13519 10.1463C6.5198 10.2501 6.91554 10.0274 7.02816 9.64764L7.03308 9.63001L8.02917 5.9386L8.03374 5.92079C8.12749 5.53595 7.89751 5.14442 7.51285 5.04065ZM20.3116 5.73845C19.9269 5.63462 19.5312 5.85727 19.4186 6.23708L19.4136 6.25471L18.7443 8.73499C19.1779 8.94915 19.5917 9.20126 19.9809 9.48839L20.0453 9.53643L20.8279 6.63639L20.8324 6.61858C20.9262 6.23374 20.6963 5.84221 20.3116 5.73845ZM13.7968 1.57642C13.3296 1.61771 12.8647 1.68338 12.4043 1.77317L12.3066 1.79263L11.3782 5.23419L11.3736 5.252C11.2799 5.63684 11.5099 6.02837 11.8945 6.13214C12.2792 6.23596 12.6749 6.01331 12.7875 5.6335L12.7924 5.61587L13.7885 1.92446L13.7931 1.90665C13.8196 1.79831 13.8209 1.68533 13.7968 1.57642ZM22.9626 4.1263L22.7669 4.85169L22.7623 4.86944C22.6686 5.25429 22.8986 5.64581 23.2832 5.74958C23.6678 5.85341 24.0636 5.63075 24.1762 5.25095L24.1811 5.23331L24.2025 5.15462C23.8362 4.81205 23.4511 4.49009 23.0491 4.19022L22.9626 4.1263ZM17.1672 1.69677L16.8139 3.00593L16.8094 3.02374C16.7156 3.40858 16.9456 3.80011 17.3303 3.90388C17.7149 4.0077 18.1106 3.78505 18.2233 3.40524L18.2282 3.38761L18.6 2.00966C18.1624 1.88867 17.719 1.79001 17.2714 1.71405L17.1672 1.69677Z" fill="white"></path><path d="M9.69085 24.6253C9.80341 24.2455 10.1992 24.0229 10.5838 24.1266C10.9685 24.2303 11.1984 24.6219 11.1047 25.0068L11.1001 25.0246L10.3872 27.6664L10.2876 27.6297C9.85836 27.4694 9.43765 27.2873 9.0271 27.0839L9.68587 24.6429L9.69085 24.6253Z" fill="white"></path><path d="M14.4141 8.49082C10.0522 8.49082 6.65918 11.2368 6.65918 14.6517C6.65918 17.8769 9.78123 19.9362 14.6211 19.8331C15.0327 19.8243 15.1517 20.1008 15.2856 20.4734C15.4196 20.846 15.7796 22.8097 16.0665 24.3117C16.3233 25.656 16.5842 27.0052 16.7834 28.3596C19.9439 27.9418 22.8663 26.3807 25.0076 24.0261L22.7237 15.5088C22.1544 13.4518 21.489 11.5564 19.7283 10.1794C18.3118 9.07166 16.5122 8.49082 14.4141 8.49082Z" fill="white"></path><path d="M15.3516 10.957C15.8694 10.957 16.2891 11.3767 16.2891 11.8945C16.2891 12.4123 15.8694 12.832 15.3516 12.832C14.8338 12.832 14.4141 12.4123 14.4141 11.8945C14.4141 11.3767 14.8338 10.957 15.3516 10.957Z" fill="currentColor"></path></g><defs><clipPath id="clip0_29_599"><rect width="30" height="30" fill="white"></rect></clipPath></defs></svg>
<svg width="100%" height="100%" viewBox="0 0 441 441" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/"
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1.02631,-2.08167e-17,2.08167e-17,1.02631,-0.525826,-0.525138)">
<path
d="M37.965,296.635C26.441,271.766 20.009,244.065 20.009,214.873C20.009,207.318 20.439,199.863 21.278,192.531C22.129,185.123 23.39,177.852 25.036,170.742C34.286,130.852 55.801,95.64 85.384,69.301C110.233,47.207 140.674,31.444 174.043,24.299C187.212,21.486 200.872,20.006 214.875,20.006C219.783,20.011 224.727,20.2 229.701,20.579C253.285,22.38 275.571,28.317 295.904,37.625C312.305,45.143 327.486,54.87 341.064,66.426C375.17,95.48 398.957,135.953 406.867,181.369C408.757,192.255 409.742,203.45 409.742,214.873C409.738,219.789 409.548,224.74 409.168,229.721C407.731,248.545 403.659,266.542 397.34,283.379C388.521,306.83 375.308,328.136 358.706,346.294C337.113,368.342 309.673,378.152 286.755,377.744C270.09,377.447 253.784,370.816 242.516,361.114C226.42,347.253 219.918,331.409 217.69,313.729C217.136,309.334 217.461,297.358 219.748,289.066C221.453,282.885 225.777,270.948 232.1,265.727C224.703,262.541 215.183,255.604 212.182,252.274C211.445,251.455 211.54,250.174 212.2,249.292C212.861,248.41 214.02,248.062 215.057,248.435C221.416,250.618 229.161,252.771 237.327,254.137C248.067,255.932 261.424,258.194 274.955,258.859C307.946,260.479 342.407,245.67 353.103,216.207C363.798,186.744 359.649,157.602 321.279,140.121C282.909,122.64 265.185,101.856 234.183,89.32C213.934,81.131 191.396,85.992 168.257,98.78C105.931,133.223 50.092,242.048 75.833,342.873C76.201,344.252 75.58,345.705 74.328,346.392C73.156,347.036 71.713,346.852 70.741,345.962C63.25,337.731 56.454,328.857 50.445,319.433C45.796,312.139 41.623,304.524 37.965,296.635Z" />
</g>
<g transform="matrix(0.0920293,0.00428099,-0.00428099,0.0920293,-28.1272,-500.301)">
<path
d="M3053.7,5296.9C4371.65,5296.9 5441.66,6366.91 5441.66,7684.86C5441.66,9002.81 4371.65,10072.8 3053.7,10072.8C1735.75,10072.8 665.74,9002.81 665.74,7684.86C665.74,6366.91 1735.75,5296.9 3053.7,5296.9ZM3745.03,8143.22C3594.12,8142.82 3444.31,8124.57 3323.87,8110.15C3232.29,8099.18 3144.99,8079.23 3073.1,8058.23C3061.36,8054.62 3048.65,8059.09 3041.75,8069.24C3034.86,8079.4 3034.46,8093.71 3043.09,8102.44C3078.21,8137.94 3187.74,8210.21 3271.7,8241.83C3204.04,8303.2 3162.1,8438.28 3146.33,8507.94C3125.17,8601.4 3127.75,8734.83 3136.19,8783.45C3170.14,8979.04 3250.69,9151.99 3436.99,9297.9C3567.4,9400.03 3752.28,9465.38 3937.88,9460.06C4194.01,9452.71 4495.48,9328.51 4724.65,9070.17C5023.25,8710.58 5208.52,8252.45 5223.47,7749.5C5259.08,6551.9 4315.7,5550.69 3118.1,5515.08C1920.51,5479.47 919.301,6422.86 883.689,7620.45C865.246,8240.66 1109.37,8808.21 1515.43,9216.2C1526.73,9227.39 1544.21,9229.43 1557.78,9221.14C1571.35,9212.85 1577.51,9196.36 1572.7,9181.2C1234.07,8072.55 1799.11,6832.64 2474.84,6417.1C2725.71,6262.82 2973.99,6197.06 3203.56,6277.7C3555.04,6401.15 3763.03,6623.26 4199.06,6797.93C4635.09,6972.59 4696.35,7294.74 4592.58,7628.14C4488.81,7961.54 4113,8144.17 3745.03,8143.22ZM2917.17,6442.51C2777.75,6459.97 2693.93,6637.44 2687.08,6749.42C2680.18,6861.39 2744.03,7042.7 2926.19,7030.63C3139.52,7016.49 3195.89,6830.7 3164.24,6654.94C3140.48,6522.94 3033.73,6427.9 2917.17,6442.51Z"
style="fill:white;" />
</g>
<g transform="matrix(7.12289,0.543899,-0.543899,7.12289,-4867.49,-1040.55)">
<path
d="M721.316,105.751C722.813,105.518 724.225,106.703 724.582,108.395C725.058,110.649 724.402,113.065 721.658,113.329C719.314,113.555 718.422,111.242 718.468,109.796C718.513,108.35 719.525,106.03 721.316,105.751Z" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -329,7 +329,12 @@ impl JupyterServer {
})
.collect();
(candidates, cursor_pos - prop_name.len())
if prop_name.len() > cursor_pos {
// TODO(bartlomieju): most likely not correct, but better than panicking because of sub with overflow
(candidates, cursor_pos)
} else {
(candidates, cursor_pos - prop_name.len())
}
} else {
// combine results of declarations and globalThis properties
let mut candidates = get_expression_property_names(
@ -349,7 +354,12 @@ impl JupyterServer {
candidates.sort();
candidates.dedup(); // make sure to sort first
(candidates, cursor_pos - expr.len())
if expr.len() > cursor_pos {
// TODO(bartlomieju): most likely not correct, but better than panicking because of sub with overflow
(candidates, cursor_pos)
} else {
(candidates, cursor_pos - expr.len())
}
};
connection

View file

@ -1,32 +1,22 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
mod cache_deps;
pub use cache_deps::cache_top_level_deps;
use deno_semver::jsr::JsrPackageReqReference;
use deno_semver::npm::NpmPackageReqReference;
use deno_semver::VersionReq;
use std::borrow::Cow;
use std::path::PathBuf;
use std::sync::Arc;
use deno_ast::TextChange;
use deno_config::deno_json::FmtOptionsConfig;
use deno_core::anyhow::anyhow;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::futures::FutureExt;
use deno_core::futures::StreamExt;
use deno_core::serde_json;
use deno_core::ModuleSpecifier;
use deno_runtime::deno_node;
use deno_path_util::url_to_file_path;
use deno_semver::jsr::JsrPackageReqReference;
use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageReq;
use indexmap::IndexMap;
use jsonc_parser::ast::ObjectProp;
use jsonc_parser::ast::Value;
use yoke::Yoke;
use deno_semver::VersionReq;
use jsonc_parser::cst::CstObject;
use jsonc_parser::cst::CstObjectProp;
use jsonc_parser::cst::CstRootNode;
use jsonc_parser::json;
use crate::args::AddFlags;
use crate::args::CacheSetting;
@ -38,234 +28,181 @@ use crate::file_fetcher::FileFetcher;
use crate::jsr::JsrFetchResolver;
use crate::npm::NpmFetchResolver;
enum DenoConfigFormat {
Json,
Jsonc,
mod cache_deps;
pub use cache_deps::cache_top_level_deps;
#[derive(Debug, Copy, Clone)]
enum ConfigKind {
DenoJson,
PackageJson,
}
impl DenoConfigFormat {
fn from_specifier(spec: &ModuleSpecifier) -> Result<Self, AnyError> {
let file_name = spec
.path_segments()
.ok_or_else(|| anyhow!("Empty path in deno config specifier: {spec}"))?
.last()
.unwrap();
match file_name {
"deno.json" => Ok(Self::Json),
"deno.jsonc" => Ok(Self::Jsonc),
_ => bail!("Unsupported deno config file: {file_name}"),
}
}
}
struct DenoConfig {
config: Arc<deno_config::deno_json::ConfigFile>,
format: DenoConfigFormat,
imports: IndexMap<String, String>,
}
fn deno_json_imports(
config: &deno_config::deno_json::ConfigFile,
) -> Result<IndexMap<String, String>, AnyError> {
Ok(
config
.json
.imports
.clone()
.map(|imports| {
serde_json::from_value(imports)
.map_err(|err| anyhow!("Malformed \"imports\" configuration: {err}"))
})
.transpose()?
.unwrap_or_default(),
)
}
impl DenoConfig {
fn from_options(options: &CliOptions) -> Result<Option<Self>, AnyError> {
let start_dir = &options.start_dir;
if let Some(config) = start_dir.maybe_deno_json() {
Ok(Some(Self {
imports: deno_json_imports(config)?,
config: config.clone(),
format: DenoConfigFormat::from_specifier(&config.specifier)?,
}))
} else {
Ok(None)
}
}
fn add(&mut self, selected: SelectedPackage) {
self.imports.insert(
selected.import_name,
format!("{}@{}", selected.package_name, selected.version_req),
);
}
fn remove(&mut self, package: &str) -> bool {
self.imports.shift_remove(package).is_some()
}
fn take_import_fields(
&mut self,
) -> Vec<(&'static str, IndexMap<String, String>)> {
vec![("imports", std::mem::take(&mut self.imports))]
}
}
impl NpmConfig {
fn from_options(options: &CliOptions) -> Result<Option<Self>, AnyError> {
let start_dir = &options.start_dir;
if let Some(pkg_json) = start_dir.maybe_pkg_json() {
Ok(Some(Self {
dependencies: pkg_json.dependencies.clone().unwrap_or_default(),
dev_dependencies: pkg_json.dev_dependencies.clone().unwrap_or_default(),
config: pkg_json.clone(),
fmt_options: None,
}))
} else {
Ok(None)
}
}
fn add(&mut self, selected: SelectedPackage, dev: bool) {
let (name, version) = package_json_dependency_entry(selected);
if dev {
self.dev_dependencies.insert(name, version);
} else {
self.dependencies.insert(name, version);
}
}
fn remove(&mut self, package: &str) -> bool {
let in_deps = self.dependencies.shift_remove(package).is_some();
let in_dev_deps = self.dev_dependencies.shift_remove(package).is_some();
in_deps || in_dev_deps
}
fn take_import_fields(
&mut self,
) -> Vec<(&'static str, IndexMap<String, String>)> {
vec![
("dependencies", std::mem::take(&mut self.dependencies)),
(
"devDependencies",
std::mem::take(&mut self.dev_dependencies),
),
]
}
}
struct NpmConfig {
config: Arc<deno_node::PackageJson>,
fmt_options: Option<FmtOptionsConfig>,
dependencies: IndexMap<String, String>,
dev_dependencies: IndexMap<String, String>,
}
enum DenoOrPackageJson {
Deno(DenoConfig),
Npm(NpmConfig),
}
impl From<DenoConfig> for DenoOrPackageJson {
fn from(config: DenoConfig) -> Self {
Self::Deno(config)
}
}
impl From<NpmConfig> for DenoOrPackageJson {
fn from(config: NpmConfig) -> Self {
Self::Npm(config)
}
}
/// Wrapper around `jsonc_parser::ast::Object` that can be stored in a `Yoke`
#[derive(yoke::Yokeable)]
struct JsoncObjectView<'a>(jsonc_parser::ast::Object<'a>);
struct ConfigUpdater {
config: DenoOrPackageJson,
// the `Yoke` is so we can carry the parsed object (which borrows from
// the source) along with the source itself
ast: Yoke<JsoncObjectView<'static>, String>,
kind: ConfigKind,
cst: CstRootNode,
root_object: CstObject,
path: PathBuf,
modified: bool,
}
impl ConfigUpdater {
fn obj(&self) -> &jsonc_parser::ast::Object<'_> {
&self.ast.get().0
}
fn contents(&self) -> &str {
self.ast.backing_cart()
}
async fn maybe_new(
config: Option<impl Into<DenoOrPackageJson>>,
) -> Result<Option<Self>, AnyError> {
if let Some(config) = config {
Ok(Some(Self::new(config.into()).await?))
} else {
Ok(None)
}
}
async fn new(config: DenoOrPackageJson) -> Result<Self, AnyError> {
let specifier = config.specifier();
if specifier.scheme() != "file" {
bail!("Can't update a remote configuration file");
}
let config_file_path = specifier.to_file_path().map_err(|_| {
anyhow!("Specifier {specifier:?} is an invalid file path")
})?;
let config_file_contents = {
let contents = tokio::fs::read_to_string(&config_file_path)
.await
.with_context(|| {
format!("Reading config file at: {}", config_file_path.display())
})?;
if contents.trim().is_empty() {
"{}\n".into()
} else {
contents
}
};
let ast = Yoke::try_attach_to_cart(config_file_contents, |contents| {
let ast = jsonc_parser::parse_to_ast(
contents,
&Default::default(),
&Default::default(),
)
fn new(
kind: ConfigKind,
config_file_path: PathBuf,
) -> Result<Self, AnyError> {
let config_file_contents = std::fs::read_to_string(&config_file_path)
.with_context(|| {
format!("Failed to parse config file at {}", specifier)
format!("Reading config file '{}'", config_file_path.display())
})?;
let obj = match ast.value {
Some(Value::Object(obj)) => obj,
_ => bail!(
"Failed to update config file at {}, expected an object",
specifier
),
};
Ok(JsoncObjectView(obj))
})?;
let cst = CstRootNode::parse(&config_file_contents, &Default::default())
.with_context(|| {
format!("Parsing config file '{}'", config_file_path.display())
})?;
let root_object = cst.object_value_or_set();
Ok(Self {
config,
ast,
kind,
cst,
root_object,
path: config_file_path,
modified: false,
})
}
fn display_path(&self) -> String {
deno_path_util::url_from_file_path(&self.path)
.map(|u| u.to_string())
.unwrap_or_else(|_| self.path.display().to_string())
}
fn obj(&self) -> &CstObject {
&self.root_object
}
fn contents(&self) -> String {
self.cst.to_string()
}
fn add(&mut self, selected: SelectedPackage, dev: bool) {
match &mut self.config {
DenoOrPackageJson::Deno(deno) => deno.add(selected),
DenoOrPackageJson::Npm(npm) => npm.add(selected, dev),
fn insert_index(object: &CstObject, searching_name: &str) -> usize {
object
.properties()
.into_iter()
.take_while(|prop| {
let prop_name =
prop.name().and_then(|name| name.decoded_value().ok());
match prop_name {
Some(current_name) => {
searching_name.cmp(&current_name) == std::cmp::Ordering::Greater
}
None => true,
}
})
.count()
}
match self.kind {
ConfigKind::DenoJson => {
let imports = self.root_object.object_value_or_set("imports");
let value =
format!("{}@{}", selected.package_name, selected.version_req);
if let Some(prop) = imports.get(&selected.import_name) {
prop.set_value(json!(value));
} else {
let index = insert_index(&imports, &selected.import_name);
imports.insert(index, &selected.import_name, json!(value));
}
}
ConfigKind::PackageJson => {
let deps_prop = self.root_object.get("dependencies");
let dev_deps_prop = self.root_object.get("devDependencies");
let dependencies = if dev {
self
.root_object
.object_value("devDependencies")
.unwrap_or_else(|| {
let index = deps_prop
.as_ref()
.map(|p| p.property_index() + 1)
.unwrap_or_else(|| self.root_object.properties().len());
self
.root_object
.insert(index, "devDependencies", json!({}))
.object_value_or_set()
})
} else {
self
.root_object
.object_value("dependencies")
.unwrap_or_else(|| {
let index = dev_deps_prop
.as_ref()
.map(|p| p.property_index())
.unwrap_or_else(|| self.root_object.properties().len());
self
.root_object
.insert(index, "dependencies", json!({}))
.object_value_or_set()
})
};
let other_dependencies = if dev {
deps_prop.and_then(|p| p.value().and_then(|v| v.as_object()))
} else {
dev_deps_prop.and_then(|p| p.value().and_then(|v| v.as_object()))
};
let (alias, value) = package_json_dependency_entry(selected);
if let Some(other) = other_dependencies {
if let Some(prop) = other.get(&alias) {
remove_prop_and_maybe_parent_prop(prop);
}
}
if let Some(prop) = dependencies.get(&alias) {
prop.set_value(json!(value));
} else {
let index = insert_index(&dependencies, &alias);
dependencies.insert(index, &alias, json!(value));
}
}
}
self.modified = true;
}
fn remove(&mut self, package: &str) -> bool {
let removed = match &mut self.config {
DenoOrPackageJson::Deno(deno) => deno.remove(package),
DenoOrPackageJson::Npm(npm) => npm.remove(package),
let removed = match self.kind {
ConfigKind::DenoJson => {
if let Some(prop) = self
.root_object
.object_value("imports")
.and_then(|i| i.get(package))
{
remove_prop_and_maybe_parent_prop(prop);
true
} else {
false
}
}
ConfigKind::PackageJson => {
let deps = [
self
.root_object
.object_value("dependencies")
.and_then(|deps| deps.get(package)),
self
.root_object
.object_value("devDependencies")
.and_then(|deps| deps.get(package)),
];
let removed = deps.iter().any(|d| d.is_some());
for dep in deps.into_iter().flatten() {
remove_prop_and_maybe_parent_prop(dep);
}
removed
}
};
if removed {
self.modified = true;
@ -273,76 +210,28 @@ impl ConfigUpdater {
removed
}
async fn commit(mut self) -> Result<(), AnyError> {
fn commit(&self) -> Result<(), AnyError> {
if !self.modified {
return Ok(());
}
let import_fields = self.config.take_import_fields();
let fmt_config_options = self.config.fmt_options();
let new_text = update_config_file_content(
self.obj(),
self.contents(),
fmt_config_options,
import_fields.into_iter().map(|(k, v)| {
(
k,
if v.is_empty() {
None
} else {
Some(generate_imports(v.into_iter().collect()))
},
)
}),
self.config.file_name(),
);
tokio::fs::write(&self.path, new_text).await?;
let new_text = self.contents();
std::fs::write(&self.path, new_text).with_context(|| {
format!("failed writing to '{}'", self.path.display())
})?;
Ok(())
}
}
impl DenoOrPackageJson {
fn specifier(&self) -> Cow<ModuleSpecifier> {
match self {
Self::Deno(d, ..) => Cow::Borrowed(&d.config.specifier),
Self::Npm(n, ..) => Cow::Owned(n.config.specifier()),
}
}
fn fmt_options(&self) -> FmtOptionsConfig {
match self {
DenoOrPackageJson::Deno(deno, ..) => deno
.config
.to_fmt_config()
.ok()
.map(|f| f.options)
.unwrap_or_default(),
DenoOrPackageJson::Npm(config) => {
config.fmt_options.clone().unwrap_or_default()
}
}
}
fn take_import_fields(
&mut self,
) -> Vec<(&'static str, IndexMap<String, String>)> {
match self {
Self::Deno(d) => d.take_import_fields(),
Self::Npm(n) => n.take_import_fields(),
}
}
fn file_name(&self) -> &'static str {
match self {
DenoOrPackageJson::Deno(config) => match config.format {
DenoConfigFormat::Json => "deno.json",
DenoConfigFormat::Jsonc => "deno.jsonc",
},
DenoOrPackageJson::Npm(..) => "package.json",
}
fn remove_prop_and_maybe_parent_prop(prop: CstObjectProp) {
let parent = prop.parent().unwrap().as_object().unwrap();
prop.remove();
if parent.properties().is_empty() {
let parent_property = parent.parent().unwrap();
let root_object = parent_property.parent().unwrap().as_object().unwrap();
// remove the property
parent_property.remove();
root_object.ensure_multiline();
}
}
@ -361,7 +250,14 @@ fn package_json_dependency_entry(
selected: SelectedPackage,
) -> (String, String) {
if let Some(npm_package) = selected.package_name.strip_prefix("npm:") {
(npm_package.into(), selected.version_req)
if selected.import_name == npm_package {
(npm_package.into(), selected.version_req)
} else {
(
selected.import_name,
format!("npm:{}@{}", npm_package, selected.version_req),
)
}
} else if let Some(jsr_package) = selected.package_name.strip_prefix("jsr:") {
let jsr_package = jsr_package.strip_prefix('@').unwrap_or(jsr_package);
let scope_replaced = jsr_package.replace('/', "__");
@ -391,21 +287,45 @@ impl std::fmt::Display for AddCommandName {
fn load_configs(
flags: &Arc<Flags>,
) -> Result<(CliFactory, Option<NpmConfig>, Option<DenoConfig>), AnyError> {
has_jsr_specifiers: impl FnOnce() -> bool,
) -> Result<(CliFactory, Option<ConfigUpdater>, Option<ConfigUpdater>), AnyError>
{
let cli_factory = CliFactory::from_flags(flags.clone());
let options = cli_factory.cli_options()?;
let npm_config = NpmConfig::from_options(options)?;
let (cli_factory, deno_config) = match DenoConfig::from_options(options)? {
let start_dir = &options.start_dir;
let npm_config = match start_dir.maybe_pkg_json() {
Some(pkg_json) => Some(ConfigUpdater::new(
ConfigKind::PackageJson,
pkg_json.path.clone(),
)?),
None => None,
};
let deno_config = match start_dir.maybe_deno_json() {
Some(deno_json) => Some(ConfigUpdater::new(
ConfigKind::DenoJson,
url_to_file_path(&deno_json.specifier)?,
)?),
None => None,
};
let (cli_factory, deno_config) = match deno_config {
Some(config) => (cli_factory, Some(config)),
None if npm_config.is_some() => (cli_factory, None),
None => {
None if npm_config.is_some() && !has_jsr_specifiers() => {
(cli_factory, None)
}
_ => {
let factory = create_deno_json(flags, options)?;
let options = factory.cli_options()?.clone();
let deno_json = options
.start_dir
.maybe_deno_json()
.expect("Just created deno.json");
(
factory,
Some(
DenoConfig::from_options(&options)?.expect("Just created deno.json"),
),
Some(ConfigUpdater::new(
ConfigKind::DenoJson,
url_to_file_path(&deno_json.specifier)?,
)?),
)
}
};
@ -418,13 +338,13 @@ pub async fn add(
add_flags: AddFlags,
cmd_name: AddCommandName,
) -> Result<(), AnyError> {
let (cli_factory, npm_config, deno_config) = load_configs(&flags)?;
let mut npm_config = ConfigUpdater::maybe_new(npm_config).await?;
let mut deno_config = ConfigUpdater::maybe_new(deno_config).await?;
let (cli_factory, mut npm_config, mut deno_config) =
load_configs(&flags, || {
add_flags.packages.iter().any(|s| s.starts_with("jsr:"))
})?;
if let Some(deno) = &deno_config {
let specifier = deno.config.specifier();
if deno.obj().get_string("importMap").is_some() {
if deno.obj().get("importMap").is_some() {
bail!(
concat!(
"`deno {}` is not supported when configuration file contains an \"importMap\" field. ",
@ -432,7 +352,7 @@ pub async fn add(
" at {}",
),
cmd_name,
specifier
deno.display_path(),
);
}
}
@ -456,7 +376,7 @@ 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 = AddPackageReq::parse(entry_text).with_context(|| {
let req = AddRmPackageReq::parse(entry_text).with_context(|| {
format!("Failed to parse package required: {}", entry_text)
})?;
@ -544,26 +464,14 @@ pub async fn add(
}
}
let mut commit_futures = vec![];
if let Some(npm) = npm_config {
commit_futures.push(npm.commit());
npm.commit()?;
}
if let Some(deno) = deno_config {
commit_futures.push(deno.commit());
}
let commit_futures =
deno_core::futures::future::join_all(commit_futures).await;
for result in commit_futures {
result.context("Failed to update configuration file")?;
deno.commit()?;
}
// clear the previously cached package.json from memory before reloading it
node_resolver::PackageJsonThreadLocalCache::clear();
// make a new CliFactory to pick up the updated config file
let cli_factory = CliFactory::from_flags(flags);
// cache deps
cache_deps::cache_top_level_deps(&cli_factory, Some(jsr_resolver)).await?;
npm_install_after_modification(flags, Some(jsr_resolver)).await?;
Ok(())
}
@ -587,10 +495,10 @@ enum PackageAndVersion {
async fn find_package_and_select_version_for_req(
jsr_resolver: Arc<JsrFetchResolver>,
npm_resolver: Arc<NpmFetchResolver>,
add_package_req: AddPackageReq,
add_package_req: AddRmPackageReq,
) -> Result<PackageAndVersion, AnyError> {
match add_package_req.value {
AddPackageReqValue::Jsr(req) => {
AddRmPackageReqValue::Jsr(req) => {
let jsr_prefixed_name = format!("jsr:{}", &req.name);
let Some(nv) = jsr_resolver.req_to_nv(&req).await else {
if npm_resolver.req_to_nv(&req).await.is_some() {
@ -608,9 +516,11 @@ async fn find_package_and_select_version_for_req(
});
};
let range_symbol = if req.version_req.version_text().starts_with('~') {
'~'
"~"
} else if req.version_req.version_text() == nv.version.to_string() {
""
} else {
'^'
"^"
};
Ok(PackageAndVersion::Selected(SelectedPackage {
import_name: add_package_req.alias,
@ -619,7 +529,7 @@ async fn find_package_and_select_version_for_req(
selected_version: nv.version.to_string(),
}))
}
AddPackageReqValue::Npm(req) => {
AddRmPackageReqValue::Npm(req) => {
let npm_prefixed_name = format!("npm:{}", &req.name);
let Some(nv) = npm_resolver.req_to_nv(&req).await else {
return Ok(PackageAndVersion::NotFound {
@ -628,11 +538,15 @@ async fn find_package_and_select_version_for_req(
package_req: req,
});
};
let range_symbol = if req.version_req.version_text().starts_with('~') {
'~'
"~"
} else if req.version_req.version_text() == nv.version.to_string() {
""
} else {
'^'
"^"
};
Ok(PackageAndVersion::Selected(SelectedPackage {
import_name: add_package_req.alias,
package_name: npm_prefixed_name,
@ -644,18 +558,18 @@ async fn find_package_and_select_version_for_req(
}
#[derive(Debug, PartialEq, Eq)]
enum AddPackageReqValue {
enum AddRmPackageReqValue {
Jsr(PackageReq),
Npm(PackageReq),
}
#[derive(Debug, PartialEq, Eq)]
struct AddPackageReq {
struct AddRmPackageReq {
alias: String,
value: AddPackageReqValue,
value: AddRmPackageReqValue,
}
impl AddPackageReq {
impl AddRmPackageReq {
pub fn parse(entry_text: &str) -> Result<Result<Self, PackageReq>, AnyError> {
enum Prefix {
Jsr,
@ -710,9 +624,9 @@ impl AddPackageReq {
let req_ref =
JsrPackageReqReference::from_str(&format!("jsr:{}", entry_text))?;
let package_req = req_ref.into_inner().req;
Ok(Ok(AddPackageReq {
Ok(Ok(AddRmPackageReq {
alias: maybe_alias.unwrap_or_else(|| package_req.name.to_string()),
value: AddPackageReqValue::Jsr(package_req),
value: AddRmPackageReqValue::Jsr(package_req),
}))
}
Prefix::Npm => {
@ -730,49 +644,48 @@ impl AddPackageReq {
deno_semver::RangeSetOrTag::Tag("latest".into()),
);
}
Ok(Ok(AddPackageReq {
Ok(Ok(AddRmPackageReq {
alias: maybe_alias.unwrap_or_else(|| package_req.name.to_string()),
value: AddPackageReqValue::Npm(package_req),
value: AddRmPackageReqValue::Npm(package_req),
}))
}
}
}
}
fn generate_imports(mut packages_to_version: Vec<(String, String)>) -> String {
packages_to_version.sort_by(|(k1, _), (k2, _)| k1.cmp(k2));
let mut contents = vec![];
let len = packages_to_version.len();
for (index, (package, version)) in packages_to_version.iter().enumerate() {
// TODO(bartlomieju): fix it, once we start support specifying version on the cli
contents.push(format!("\"{}\": \"{}\"", package, version));
if index != len - 1 {
contents.push(",".to_string());
}
}
contents.join("\n")
}
pub async fn remove(
flags: Arc<Flags>,
remove_flags: RemoveFlags,
) -> Result<(), AnyError> {
let (_, npm_config, deno_config) = load_configs(&flags)?;
let (_, npm_config, deno_config) = load_configs(&flags, || false)?;
let mut configs = [
ConfigUpdater::maybe_new(npm_config).await?,
ConfigUpdater::maybe_new(deno_config).await?,
];
let mut configs = [npm_config, deno_config];
let mut removed_packages = vec![];
for package in &remove_flags.packages {
let mut removed = false;
let req = AddRmPackageReq::parse(package).with_context(|| {
format!("Failed to parse package required: {}", package)
})?;
let mut parsed_pkg_name = None;
for config in configs.iter_mut().flatten() {
removed |= config.remove(package);
match &req {
Ok(rm_pkg) => {
if config.remove(&rm_pkg.alias) && parsed_pkg_name.is_none() {
parsed_pkg_name = Some(rm_pkg.alias.clone());
}
}
Err(pkg) => {
// An alias or a package name without registry/version
// constraints. Try to remove the package anyway.
if config.remove(&pkg.name) && parsed_pkg_name.is_none() {
parsed_pkg_name = Some(pkg.name.clone());
}
}
}
}
if removed {
removed_packages.push(package.clone());
if let Some(pkg) = parsed_pkg_name {
removed_packages.push(pkg);
}
}
@ -783,97 +696,38 @@ pub async fn remove(
log::info!("Removed {}", crate::colors::green(package));
}
for config in configs.into_iter().flatten() {
config.commit().await?;
config.commit()?;
}
// Update deno.lock
node_resolver::PackageJsonThreadLocalCache::clear();
let cli_factory = CliFactory::from_flags(flags);
cache_deps::cache_top_level_deps(&cli_factory, None).await?;
npm_install_after_modification(flags, None).await?;
}
Ok(())
}
fn update_config_file_content<
I: IntoIterator<Item = (&'static str, Option<String>)>,
>(
obj: &jsonc_parser::ast::Object,
config_file_contents: &str,
fmt_options: FmtOptionsConfig,
entries: I,
file_name: &str,
) -> String {
let mut text_changes = vec![];
for (key, value) in entries {
match obj.properties.iter().enumerate().find_map(|(idx, k)| {
if k.name.as_str() == key {
Some((idx, k))
} else {
None
}
}) {
Some((
idx,
ObjectProp {
value: Value::Object(lit),
range,
..
},
)) => {
if let Some(value) = value {
text_changes.push(TextChange {
range: (lit.range.start + 1)..(lit.range.end - 1),
new_text: value,
})
} else {
text_changes.push(TextChange {
// remove field entirely, making sure to
// remove the comma if it's not the last field
range: range.start..(if idx == obj.properties.len() - 1 {
range.end
} else {
obj.properties[idx + 1].range.start
}),
new_text: "".to_string(),
})
}
}
async fn npm_install_after_modification(
flags: Arc<Flags>,
// explicitly provided to prevent redownloading
jsr_resolver: Option<Arc<crate::jsr::JsrFetchResolver>>,
) -> Result<(), AnyError> {
// clear the previously cached package.json from memory before reloading it
node_resolver::PackageJsonThreadLocalCache::clear();
// need to add field
None => {
if let Some(value) = value {
let insert_position = obj.range.end - 1;
text_changes.push(TextChange {
range: insert_position..insert_position,
// NOTE(bartlomieju): adding `\n` here to force the formatter to always
// produce a config file that is multiline, like so:
// ```
// {
// "imports": {
// "<package_name>": "<registry>:<package_name>@<semver>"
// }
// }
new_text: format!("\"{key}\": {{\n {value} }}"),
})
}
}
// we verified the shape of `imports`/`dependencies` above
Some(_) => unreachable!(),
}
// make a new CliFactory to pick up the updated config file
let cli_factory = CliFactory::from_flags(flags);
// surface any errors in the package.json
let npm_resolver = cli_factory.npm_resolver().await?;
if let Some(npm_resolver) = npm_resolver.as_managed() {
npm_resolver.ensure_no_pkg_json_dep_errors()?;
}
// npm install
cache_deps::cache_top_level_deps(&cli_factory, jsr_resolver).await?;
if let Some(lockfile) = cli_factory.cli_options()?.maybe_lockfile() {
lockfile.write_if_changed()?;
}
let new_text =
deno_ast::apply_text_changes(config_file_contents, text_changes);
crate::tools::fmt::format_json(
&PathBuf::from(file_name),
&new_text,
&fmt_options,
)
.ok()
.map(|formatted_text| formatted_text.unwrap_or_else(|| new_text.clone()))
.unwrap_or(new_text)
Ok(())
}
#[cfg(test)]
@ -883,48 +737,52 @@ mod test {
#[test]
fn test_parse_add_package_req() {
assert_eq!(
AddPackageReq::parse("jsr:foo").unwrap().unwrap(),
AddPackageReq {
AddRmPackageReq::parse("jsr:foo").unwrap().unwrap(),
AddRmPackageReq {
alias: "foo".to_string(),
value: AddPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap())
value: AddRmPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap())
}
);
assert_eq!(
AddPackageReq::parse("alias@jsr:foo").unwrap().unwrap(),
AddPackageReq {
AddRmPackageReq::parse("alias@jsr:foo").unwrap().unwrap(),
AddRmPackageReq {
alias: "alias".to_string(),
value: AddPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap())
value: AddRmPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap())
}
);
assert_eq!(
AddPackageReq::parse("@alias/pkg@npm:foo").unwrap().unwrap(),
AddPackageReq {
AddRmPackageReq::parse("@alias/pkg@npm:foo")
.unwrap()
.unwrap(),
AddRmPackageReq {
alias: "@alias/pkg".to_string(),
value: AddPackageReqValue::Npm(
value: AddRmPackageReqValue::Npm(
PackageReq::from_str("foo@latest").unwrap()
)
}
);
assert_eq!(
AddPackageReq::parse("@alias/pkg@jsr:foo").unwrap().unwrap(),
AddPackageReq {
AddRmPackageReq::parse("@alias/pkg@jsr:foo")
.unwrap()
.unwrap(),
AddRmPackageReq {
alias: "@alias/pkg".to_string(),
value: AddPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap())
value: AddRmPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap())
}
);
assert_eq!(
AddPackageReq::parse("alias@jsr:foo@^1.5.0")
AddRmPackageReq::parse("alias@jsr:foo@^1.5.0")
.unwrap()
.unwrap(),
AddPackageReq {
AddRmPackageReq {
alias: "alias".to_string(),
value: AddPackageReqValue::Jsr(
value: AddRmPackageReqValue::Jsr(
PackageReq::from_str("foo@^1.5.0").unwrap()
)
}
);
assert_eq!(
AddPackageReq::parse("@scope/pkg@tag")
AddRmPackageReq::parse("@scope/pkg@tag")
.unwrap()
.unwrap_err()
.to_string(),

View file

@ -11,6 +11,7 @@ use deno_core::futures::StreamExt;
use deno_semver::package::PackageReq;
pub async fn cache_top_level_deps(
// todo(dsherret): don't pass the factory into this function. Instead use ctor deps
factory: &CliFactory,
jsr_resolver: Option<Arc<crate::jsr::JsrFetchResolver>>,
) -> Result<(), AnyError> {
@ -74,6 +75,13 @@ pub async fn cache_top_level_deps(
if entry.key.ends_with('/') && specifier.as_str().ends_with('/') {
continue;
}
if specifier.scheme() == "file" {
if let Ok(path) = specifier.to_file_path() {
if !path.is_file() {
continue;
}
}
}
roots.push(specifier.clone());
}
}
@ -81,10 +89,6 @@ pub async fn cache_top_level_deps(
while let Some(info_future) = info_futures.next().await {
if let Some((specifier, info)) = info_future {
if info.export(".").is_some() {
roots.push(specifier.clone());
continue;
}
let exports = info.exports();
for (k, _) in exports {
if let Ok(spec) = specifier.join(k) {

View file

@ -182,17 +182,21 @@ async fn run_task(opts: RunTaskOptions<'_>) -> Result<i32, AnyError> {
&task_runner::get_script_with_args(script, cli_options.argv()),
);
task_runner::run_task(task_runner::RunTaskOptions {
task_name,
script,
cwd,
env_vars,
custom_commands,
init_cwd: opts.cli_options.initial_cwd(),
argv: cli_options.argv(),
root_node_modules_dir: npm_resolver.root_node_modules_path(),
})
.await
Ok(
task_runner::run_task(task_runner::RunTaskOptions {
task_name,
script,
cwd,
env_vars,
custom_commands,
init_cwd: opts.cli_options.initial_cwd(),
argv: cli_options.argv(),
root_node_modules_dir: npm_resolver.root_node_modules_path(),
stdio: None,
})
.await?
.exit_code,
)
}
fn output_task(task_name: &str, script: &str) {

View file

@ -913,7 +913,7 @@ async fn download_package(
// text above which will stay alive after the progress bars are complete
let progress = progress_bar.update("");
let maybe_bytes = client
.download_with_progress(download_url.clone(), None, &progress)
.download_with_progress_and_retries(download_url.clone(), None, &progress)
.await
.with_context(|| format!("Failed downloading {download_url}. The version you requested may not have been built for the current architecture."))?;
Ok(maybe_bytes)

View file

@ -254,7 +254,11 @@ impl ExportCollector {
let mut import_specifiers = vec![];
if let Some(default_export) = &self.default_export {
if !symbols_to_exclude.contains(default_export) {
// If the default export conflicts with a named export, a named one
// takes precedence.
if !symbols_to_exclude.contains(default_export)
&& !self.named_exports.contains(default_export)
{
import_specifiers.push(ast::ImportSpecifier::Default(
ast::ImportDefaultSpecifier {
span: DUMMY_SP,
@ -1137,6 +1141,30 @@ Deno.test("file:///README.md$6-12.js", async ()=>{
media_type: MediaType::JavaScript,
}],
},
// https://github.com/denoland/deno/issues/26009
Test {
input: Input {
source: r#"
/**
* ```ts
* console.log(Foo)
* ```
*/
export class Foo {}
export default Foo
"#,
specifier: "file:///main.ts",
},
expected: vec![Expected {
source: r#"import { Foo } from "file:///main.ts";
Deno.test("file:///main.ts$3-6.ts", async ()=>{
console.log(Foo);
});
"#,
specifier: "file:///main.ts$3-6.ts",
media_type: MediaType::TypeScript,
}],
},
];
for test in tests {
@ -1326,6 +1354,28 @@ assertEquals(add(1, 2), 3);
media_type: MediaType::JavaScript,
}],
},
// https://github.com/denoland/deno/issues/26009
Test {
input: Input {
source: r#"
/**
* ```ts
* console.log(Foo)
* ```
*/
export class Foo {}
export default Foo
"#,
specifier: "file:///main.ts",
},
expected: vec![Expected {
source: r#"import { Foo } from "file:///main.ts";
console.log(Foo);
"#,
specifier: "file:///main.ts$3-6.ts",
media_type: MediaType::TypeScript,
}],
},
];
for test in tests {
@ -1581,6 +1631,16 @@ declare global {
named_expected: atom_set!(),
default_expected: None,
},
// The identifier `Foo` conflicts, but `ExportCollector` doesn't do
// anything about it. It is handled by `to_import_specifiers` method.
Test {
input: r#"
export class Foo {}
export default Foo
"#,
named_expected: atom_set!("Foo"),
default_expected: Some("Foo".into()),
},
];
for test in tests {

View file

@ -160,11 +160,11 @@ fn atomic_write_file(
data: &[u8],
) -> std::io::Result<()> {
fs.write_file(temp_file_path, data)?;
fs.rename_file(temp_file_path, file_path).map_err(|err| {
// clean up the created temp file on error
let _ = fs.remove_file(temp_file_path);
err
})
fs.rename_file(temp_file_path, file_path)
.inspect_err(|_err| {
// clean up the created temp file on error
let _ = fs.remove_file(temp_file_path);
})
}
let temp_file_path = get_atomic_file_path(file_path);
@ -277,7 +277,7 @@ pub fn write_file_2<T: AsRef<[u8]>>(
/// Similar to `std::fs::canonicalize()` but strips UNC prefixes on Windows.
pub fn canonicalize_path(path: &Path) -> Result<PathBuf, Error> {
Ok(deno_core::strip_unc_prefix(path.canonicalize()?))
Ok(deno_path_util::strip_unc_prefix(path.canonicalize()?))
}
/// Canonicalizes a path which might be non-existent by going up the

View file

@ -14,6 +14,7 @@ pub mod logger;
pub mod path;
pub mod progress_bar;
pub mod result;
pub mod retry;
pub mod sync;
pub mod text_encoding;
pub mod unix;

41
cli/util/retry.rs Normal file
View file

@ -0,0 +1,41 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::future::Future;
use std::time::Duration;
pub fn retry<
F: FnMut() -> Fut,
T,
E,
Fut: Future<Output = Result<T, E>>,
ShouldRetry: FnMut(&E) -> bool,
>(
mut f: F,
mut should_retry: ShouldRetry,
) -> impl Future<Output = Result<T, E>> {
const WAITS: [Duration; 3] = [
Duration::from_millis(100),
Duration::from_millis(250),
Duration::from_millis(500),
];
let mut waits = WAITS.into_iter();
async move {
let mut first_result = None;
loop {
let result = f().await;
match result {
Ok(r) => return Ok(r),
Err(e) if !should_retry(&e) => return Err(e),
_ => {}
}
if first_result.is_none() {
first_result = Some(result);
}
let Some(wait) = waits.next() else {
return first_result.unwrap();
};
tokio::time::sleep(wait).await;
}
}
}

View file

@ -1,6 +1,8 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::borrow::Cow;
use std::ops::Range;
use std::sync::Arc;
use base64::prelude::BASE64_STANDARD;
use base64::Engine;
@ -9,6 +11,15 @@ use deno_core::ModuleSourceCode;
static SOURCE_MAP_PREFIX: &[u8] =
b"//# sourceMappingURL=data:application/json;base64,";
pub fn from_utf8_lossy_owned(bytes: Vec<u8>) -> String {
match String::from_utf8_lossy(&bytes) {
Cow::Owned(code) => code,
// SAFETY: `String::from_utf8_lossy` guarantees that the result is valid
// UTF-8 if `Cow::Borrowed` is returned.
Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(bytes) },
}
}
pub fn source_map_from_code(code: &[u8]) -> Option<Vec<u8>> {
let range = find_source_map_range(code)?;
let source_map_range = &code[range];
@ -85,6 +96,13 @@ fn find_source_map_range(code: &[u8]) -> Option<Range<usize>> {
}
}
/// Converts an `Arc<str>` to an `Arc<[u8]>`.
pub fn arc_str_to_bytes(arc_str: Arc<str>) -> Arc<[u8]> {
let raw = Arc::into_raw(arc_str);
// SAFETY: This is safe because they have the same memory layout.
unsafe { Arc::from_raw(raw as *const [u8]) }
}
#[cfg(test)]
mod tests {
use std::sync::Arc;

View file

@ -51,9 +51,11 @@ use crate::args::DenoSubcommand;
use crate::args::StorageKeyResolver;
use crate::errors;
use crate::npm::CliNpmResolver;
use crate::resolver::CjsResolutionStore;
use crate::util::checksum;
use crate::util::file_watcher::WatcherCommunicator;
use crate::util::file_watcher::WatcherRestartMode;
use crate::util::path::specifier_has_extension;
use crate::version;
pub struct ModuleLoaderAndSourceMapGetter {
@ -120,11 +122,13 @@ pub struct CliMainWorkerOptions {
pub node_ipc: Option<i64>,
pub serve_port: Option<u16>,
pub serve_host: Option<String>,
pub unstable_detect_cjs: bool,
}
struct SharedWorkerState {
blob_store: Arc<BlobStore>,
broadcast_channel: InMemoryBroadcastChannel,
cjs_resolution_store: Arc<CjsResolutionStore>,
code_cache: Option<Arc<dyn code_cache::CodeCache>>,
compiled_wasm_module_store: CompiledWasmModuleStore,
feature_checker: Arc<FeatureChecker>,
@ -422,6 +426,7 @@ impl CliMainWorkerFactory {
#[allow(clippy::too_many_arguments)]
pub fn new(
blob_store: Arc<BlobStore>,
cjs_resolution_store: Arc<CjsResolutionStore>,
code_cache: Option<Arc<dyn code_cache::CodeCache>>,
feature_checker: Arc<FeatureChecker>,
fs: Arc<dyn deno_fs::FileSystem>,
@ -441,6 +446,7 @@ impl CliMainWorkerFactory {
shared: Arc::new(SharedWorkerState {
blob_store,
broadcast_channel: Default::default(),
cjs_resolution_store,
code_cache,
compiled_wasm_module_store: Default::default(),
feature_checker,
@ -486,6 +492,9 @@ impl CliMainWorkerFactory {
stdio: deno_runtime::deno_io::Stdio,
) -> Result<CliMainWorker, AnyError> {
let shared = &self.shared;
let ModuleLoaderAndSourceMapGetter { module_loader } = shared
.module_loader_factory
.create_for_main(permissions.clone());
let (main_module, is_main_cjs) = if let Ok(package_ref) =
NpmPackageReqReference::from_specifier(&main_module)
{
@ -526,13 +535,29 @@ impl CliMainWorkerFactory {
let is_main_cjs = matches!(node_resolution, NodeResolution::CommonJs(_));
(node_resolution.into_url(), is_main_cjs)
} else {
let is_cjs = main_module.path().ends_with(".cjs");
let is_maybe_cjs_js_ext = self.shared.options.unstable_detect_cjs
&& specifier_has_extension(&main_module, "js")
&& self
.shared
.node_resolver
.get_closest_package_json(&main_module)
.ok()
.flatten()
.map(|pkg_json| pkg_json.typ == "commonjs")
.unwrap_or(false);
let is_cjs = if is_maybe_cjs_js_ext {
// fill the cjs resolution store by preparing the module load
module_loader
.prepare_load(&main_module, None, false)
.await?;
self.shared.cjs_resolution_store.is_known_cjs(&main_module)
} else {
main_module.scheme() == "file"
&& specifier_has_extension(&main_module, "cjs")
};
(main_module, is_cjs)
};
let ModuleLoaderAndSourceMapGetter { module_loader } = shared
.module_loader_factory
.create_for_main(permissions.clone());
let maybe_inspector_server = shared.maybe_inspector_server.clone();
let create_web_worker_cb =

View file

@ -2,7 +2,7 @@
[package]
name = "deno_broadcast_channel"
version = "0.162.0"
version = "0.167.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
@ -16,5 +16,6 @@ path = "lib.rs"
[dependencies]
async-trait.workspace = true
deno_core.workspace = true
thiserror.workspace = true
tokio.workspace = true
uuid.workspace = true

View file

@ -3,13 +3,13 @@
use std::sync::Arc;
use async_trait::async_trait;
use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
use tokio::sync::broadcast;
use tokio::sync::mpsc;
use uuid::Uuid;
use crate::BroadcastChannel;
use crate::BroadcastChannelError;
#[derive(Clone)]
pub struct InMemoryBroadcastChannel(Arc<Mutex<broadcast::Sender<Message>>>);
@ -41,7 +41,7 @@ impl Default for InMemoryBroadcastChannel {
impl BroadcastChannel for InMemoryBroadcastChannel {
type Resource = InMemoryBroadcastChannelResource;
fn subscribe(&self) -> Result<Self::Resource, AnyError> {
fn subscribe(&self) -> Result<Self::Resource, BroadcastChannelError> {
let (cancel_tx, cancel_rx) = mpsc::unbounded_channel();
let broadcast_rx = self.0.lock().subscribe();
let rx = tokio::sync::Mutex::new((broadcast_rx, cancel_rx));
@ -53,7 +53,10 @@ impl BroadcastChannel for InMemoryBroadcastChannel {
})
}
fn unsubscribe(&self, resource: &Self::Resource) -> Result<(), AnyError> {
fn unsubscribe(
&self,
resource: &Self::Resource,
) -> Result<(), BroadcastChannelError> {
Ok(resource.cancel_tx.send(())?)
}
@ -62,7 +65,7 @@ impl BroadcastChannel for InMemoryBroadcastChannel {
resource: &Self::Resource,
name: String,
data: Vec<u8>,
) -> Result<(), AnyError> {
) -> Result<(), BroadcastChannelError> {
let name = Arc::new(name);
let data = Arc::new(data);
let uuid = resource.uuid;
@ -73,7 +76,7 @@ impl BroadcastChannel for InMemoryBroadcastChannel {
async fn recv(
&self,
resource: &Self::Resource,
) -> Result<Option<crate::Message>, AnyError> {
) -> Result<Option<crate::Message>, BroadcastChannelError> {
let mut g = resource.rx.lock().await;
let (broadcast_rx, cancel_rx) = &mut *g;
loop {

View file

@ -10,34 +10,69 @@ use std::path::PathBuf;
use std::rc::Rc;
use async_trait::async_trait;
use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::JsBuffer;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
use tokio::sync::broadcast::error::SendError as BroadcastSendError;
use tokio::sync::mpsc::error::SendError as MpscSendError;
pub const UNSTABLE_FEATURE_NAME: &str = "broadcast-channel";
#[derive(Debug, thiserror::Error)]
pub enum BroadcastChannelError {
#[error(transparent)]
Resource(deno_core::error::AnyError),
#[error(transparent)]
MPSCSendError(MpscSendError<Box<dyn std::fmt::Debug + Send + Sync>>),
#[error(transparent)]
BroadcastSendError(
BroadcastSendError<Box<dyn std::fmt::Debug + Send + Sync>>,
),
#[error(transparent)]
Other(deno_core::error::AnyError),
}
impl<T: std::fmt::Debug + Send + Sync + 'static> From<MpscSendError<T>>
for BroadcastChannelError
{
fn from(value: MpscSendError<T>) -> Self {
BroadcastChannelError::MPSCSendError(MpscSendError(Box::new(value.0)))
}
}
impl<T: std::fmt::Debug + Send + Sync + 'static> From<BroadcastSendError<T>>
for BroadcastChannelError
{
fn from(value: BroadcastSendError<T>) -> Self {
BroadcastChannelError::BroadcastSendError(BroadcastSendError(Box::new(
value.0,
)))
}
}
#[async_trait]
pub trait BroadcastChannel: Clone {
type Resource: Resource;
fn subscribe(&self) -> Result<Self::Resource, AnyError>;
fn subscribe(&self) -> Result<Self::Resource, BroadcastChannelError>;
fn unsubscribe(&self, resource: &Self::Resource) -> Result<(), AnyError>;
fn unsubscribe(
&self,
resource: &Self::Resource,
) -> Result<(), BroadcastChannelError>;
async fn send(
&self,
resource: &Self::Resource,
name: String,
data: Vec<u8>,
) -> Result<(), AnyError>;
) -> Result<(), BroadcastChannelError>;
async fn recv(
&self,
resource: &Self::Resource,
) -> Result<Option<Message>, AnyError>;
) -> Result<Option<Message>, BroadcastChannelError>;
}
pub type Message = (String, Vec<u8>);
@ -46,7 +81,7 @@ pub type Message = (String, Vec<u8>);
#[smi]
pub fn op_broadcast_subscribe<BC>(
state: &mut OpState,
) -> Result<ResourceId, AnyError>
) -> Result<ResourceId, BroadcastChannelError>
where
BC: BroadcastChannel + 'static,
{
@ -62,11 +97,14 @@ where
pub fn op_broadcast_unsubscribe<BC>(
state: &mut OpState,
#[smi] rid: ResourceId,
) -> Result<(), AnyError>
) -> Result<(), BroadcastChannelError>
where
BC: BroadcastChannel + 'static,
{
let resource = state.resource_table.get::<BC::Resource>(rid)?;
let resource = state
.resource_table
.get::<BC::Resource>(rid)
.map_err(BroadcastChannelError::Resource)?;
let bc = state.borrow::<BC>();
bc.unsubscribe(&resource)
}
@ -77,11 +115,15 @@ pub async fn op_broadcast_send<BC>(
#[smi] rid: ResourceId,
#[string] name: String,
#[buffer] buf: JsBuffer,
) -> Result<(), AnyError>
) -> Result<(), BroadcastChannelError>
where
BC: BroadcastChannel + 'static,
{
let resource = state.borrow().resource_table.get::<BC::Resource>(rid)?;
let resource = state
.borrow()
.resource_table
.get::<BC::Resource>(rid)
.map_err(BroadcastChannelError::Resource)?;
let bc = state.borrow().borrow::<BC>().clone();
bc.send(&resource, name, buf.to_vec()).await
}
@ -91,11 +133,15 @@ where
pub async fn op_broadcast_recv<BC>(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
) -> Result<Option<Message>, AnyError>
) -> Result<Option<Message>, BroadcastChannelError>
where
BC: BroadcastChannel + 'static,
{
let resource = state.borrow().resource_table.get::<BC::Resource>(rid)?;
let resource = state
.borrow()
.resource_table
.get::<BC::Resource>(rid)
.map_err(BroadcastChannelError::Resource)?;
let bc = state.borrow().borrow::<BC>().clone();
bc.recv(&resource).await
}

View file

@ -2,7 +2,7 @@
[package]
name = "deno_cache"
version = "0.100.0"
version = "0.105.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
@ -19,4 +19,5 @@ deno_core.workspace = true
rusqlite.workspace = true
serde.workspace = true
sha2.workspace = true
thiserror.workspace = true
tokio.workspace = true

60
ext/cache/lib.rs vendored
View file

@ -7,7 +7,6 @@ use std::sync::Arc;
use async_trait::async_trait;
use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::serde::Deserialize;
use deno_core::serde::Serialize;
@ -19,6 +18,20 @@ use deno_core::ResourceId;
mod sqlite;
pub use sqlite::SqliteBackedCache;
#[derive(Debug, thiserror::Error)]
pub enum CacheError {
#[error(transparent)]
Sqlite(#[from] rusqlite::Error),
#[error(transparent)]
JoinError(#[from] tokio::task::JoinError),
#[error(transparent)]
Resource(deno_core::error::AnyError),
#[error(transparent)]
Other(deno_core::error::AnyError),
#[error("{0}")]
Io(#[from] std::io::Error),
}
#[derive(Clone)]
pub struct CreateCache<C: Cache + 'static>(pub Arc<dyn Fn() -> C>);
@ -92,26 +105,31 @@ pub struct CacheDeleteRequest {
pub trait Cache: Clone + 'static {
type CacheMatchResourceType: Resource;
async fn storage_open(&self, cache_name: String) -> Result<i64, AnyError>;
async fn storage_has(&self, cache_name: String) -> Result<bool, AnyError>;
async fn storage_delete(&self, cache_name: String) -> Result<bool, AnyError>;
async fn storage_open(&self, cache_name: String) -> Result<i64, CacheError>;
async fn storage_has(&self, cache_name: String) -> Result<bool, CacheError>;
async fn storage_delete(
&self,
cache_name: String,
) -> Result<bool, CacheError>;
/// Put a resource into the cache.
async fn put(
&self,
request_response: CachePutRequest,
resource: Option<Rc<dyn Resource>>,
) -> Result<(), AnyError>;
) -> Result<(), CacheError>;
async fn r#match(
&self,
request: CacheMatchRequest,
) -> Result<
Option<(CacheMatchResponseMeta, Option<Self::CacheMatchResourceType>)>,
AnyError,
CacheError,
>;
async fn delete(&self, request: CacheDeleteRequest)
-> Result<bool, AnyError>;
async fn delete(
&self,
request: CacheDeleteRequest,
) -> Result<bool, CacheError>;
}
#[op2(async)]
@ -119,7 +137,7 @@ pub trait Cache: Clone + 'static {
pub async fn op_cache_storage_open<CA>(
state: Rc<RefCell<OpState>>,
#[string] cache_name: String,
) -> Result<i64, AnyError>
) -> Result<i64, CacheError>
where
CA: Cache,
{
@ -131,7 +149,7 @@ where
pub async fn op_cache_storage_has<CA>(
state: Rc<RefCell<OpState>>,
#[string] cache_name: String,
) -> Result<bool, AnyError>
) -> Result<bool, CacheError>
where
CA: Cache,
{
@ -143,7 +161,7 @@ where
pub async fn op_cache_storage_delete<CA>(
state: Rc<RefCell<OpState>>,
#[string] cache_name: String,
) -> Result<bool, AnyError>
) -> Result<bool, CacheError>
where
CA: Cache,
{
@ -155,13 +173,19 @@ where
pub async fn op_cache_put<CA>(
state: Rc<RefCell<OpState>>,
#[serde] request_response: CachePutRequest,
) -> Result<(), AnyError>
) -> Result<(), CacheError>
where
CA: Cache,
{
let cache = get_cache::<CA>(&state)?;
let resource = match request_response.response_rid {
Some(rid) => Some(state.borrow_mut().resource_table.take_any(rid)?),
Some(rid) => Some(
state
.borrow_mut()
.resource_table
.take_any(rid)
.map_err(CacheError::Resource)?,
),
None => None,
};
cache.put(request_response, resource).await
@ -172,7 +196,7 @@ where
pub async fn op_cache_match<CA>(
state: Rc<RefCell<OpState>>,
#[serde] request: CacheMatchRequest,
) -> Result<Option<CacheMatchResponse>, AnyError>
) -> Result<Option<CacheMatchResponse>, CacheError>
where
CA: Cache,
{
@ -191,7 +215,7 @@ where
pub async fn op_cache_delete<CA>(
state: Rc<RefCell<OpState>>,
#[serde] request: CacheDeleteRequest,
) -> Result<bool, AnyError>
) -> Result<bool, CacheError>
where
CA: Cache,
{
@ -199,7 +223,7 @@ where
cache.delete(request).await
}
pub fn get_cache<CA>(state: &Rc<RefCell<OpState>>) -> Result<CA, AnyError>
pub fn get_cache<CA>(state: &Rc<RefCell<OpState>>) -> Result<CA, CacheError>
where
CA: Cache,
{
@ -211,7 +235,9 @@ where
state.put(cache);
Ok(state.borrow::<CA>().clone())
} else {
Err(type_error("CacheStorage is not available in this context"))
Err(CacheError::Other(type_error(
"CacheStorage is not available in this context",
)))
}
}

50
ext/cache/sqlite.rs vendored
View file

@ -30,6 +30,7 @@ use crate::serialize_headers;
use crate::vary_header_matches;
use crate::Cache;
use crate::CacheDeleteRequest;
use crate::CacheError;
use crate::CacheMatchRequest;
use crate::CacheMatchResponseMeta;
use crate::CachePutRequest;
@ -102,7 +103,7 @@ impl Cache for SqliteBackedCache {
/// Open a cache storage. Internally, this creates a row in the
/// sqlite db if the cache doesn't exist and returns the internal id
/// of the cache.
async fn storage_open(&self, cache_name: String) -> Result<i64, AnyError> {
async fn storage_open(&self, cache_name: String) -> Result<i64, CacheError> {
let db = self.connection.clone();
let cache_storage_dir = self.cache_storage_dir.clone();
spawn_blocking(move || {
@ -121,14 +122,14 @@ impl Cache for SqliteBackedCache {
)?;
let responses_dir = get_responses_dir(cache_storage_dir, cache_id);
std::fs::create_dir_all(responses_dir)?;
Ok::<i64, AnyError>(cache_id)
Ok::<i64, CacheError>(cache_id)
})
.await?
}
/// Check if a cache with the provided name exists.
/// Note: this doesn't check the disk, it only checks the sqlite db.
async fn storage_has(&self, cache_name: String) -> Result<bool, AnyError> {
async fn storage_has(&self, cache_name: String) -> Result<bool, CacheError> {
let db = self.connection.clone();
spawn_blocking(move || {
let db = db.lock();
@ -140,13 +141,16 @@ impl Cache for SqliteBackedCache {
Ok(count > 0)
},
)?;
Ok::<bool, AnyError>(cache_exists)
Ok::<bool, CacheError>(cache_exists)
})
.await?
}
/// Delete a cache storage. Internally, this deletes the row in the sqlite db.
async fn storage_delete(&self, cache_name: String) -> Result<bool, AnyError> {
async fn storage_delete(
&self,
cache_name: String,
) -> Result<bool, CacheError> {
let db = self.connection.clone();
let cache_storage_dir = self.cache_storage_dir.clone();
spawn_blocking(move || {
@ -167,7 +171,7 @@ impl Cache for SqliteBackedCache {
std::fs::remove_dir_all(cache_dir)?;
}
}
Ok::<bool, AnyError>(maybe_cache_id.is_some())
Ok::<bool, CacheError>(maybe_cache_id.is_some())
})
.await?
}
@ -176,10 +180,12 @@ impl Cache for SqliteBackedCache {
&self,
request_response: CachePutRequest,
resource: Option<Rc<dyn Resource>>,
) -> Result<(), AnyError> {
) -> Result<(), CacheError> {
let db = self.connection.clone();
let cache_storage_dir = self.cache_storage_dir.clone();
let now = SystemTime::now().duration_since(UNIX_EPOCH)?;
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime is before unix epoch");
if let Some(resource) = resource {
let body_key = hash(&format!(
@ -193,7 +199,11 @@ impl Cache for SqliteBackedCache {
let mut file = tokio::fs::File::create(response_path).await?;
let mut buf = BufMutView::new(64 * 1024);
loop {
let (size, buf2) = resource.clone().read_byob(buf).await?;
let (size, buf2) = resource
.clone()
.read_byob(buf)
.await
.map_err(CacheError::Other)?;
if size == 0 {
break;
}
@ -224,7 +234,7 @@ impl Cache for SqliteBackedCache {
request: CacheMatchRequest,
) -> Result<
Option<(CacheMatchResponseMeta, Option<CacheResponseResource>)>,
AnyError,
CacheError,
> {
let db = self.connection.clone();
let cache_storage_dir = self.cache_storage_dir.clone();
@ -290,19 +300,17 @@ impl Cache for SqliteBackedCache {
}
Err(err) => return Err(err.into()),
};
return Ok(Some((cache_meta, Some(CacheResponseResource::new(file)))));
Ok(Some((cache_meta, Some(CacheResponseResource::new(file)))))
}
Some((cache_meta, None)) => {
return Ok(Some((cache_meta, None)));
}
None => return Ok(None),
Some((cache_meta, None)) => Ok(Some((cache_meta, None))),
None => Ok(None),
}
}
async fn delete(
&self,
request: CacheDeleteRequest,
) -> Result<bool, AnyError> {
) -> Result<bool, CacheError> {
let db = self.connection.clone();
spawn_blocking(move || {
// TODO(@satyarohith): remove the response body from disk if one exists
@ -311,17 +319,17 @@ impl Cache for SqliteBackedCache {
"DELETE FROM request_response_list WHERE cache_id = ?1 AND request_url = ?2",
(request.cache_id, &request.request_url),
)?;
Ok::<bool, AnyError>(rows_effected > 0)
Ok::<bool, CacheError>(rows_effected > 0)
})
.await?
}
}
async fn insert_cache_asset(
db: Arc<Mutex<rusqlite::Connection>>,
db: Arc<Mutex<Connection>>,
put: CachePutRequest,
response_body_key: Option<String>,
) -> Result<Option<String>, deno_core::anyhow::Error> {
) -> Result<Option<String>, CacheError> {
spawn_blocking(move || {
let maybe_response_body = {
let db = db.lock();
@ -339,7 +347,7 @@ async fn insert_cache_asset(
response_body_key,
put.response_status,
put.response_status_text,
SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(),
SystemTime::now().duration_since(UNIX_EPOCH).expect("SystemTime is before unix epoch").as_secs(),
),
|row| {
let response_body_key: Option<String> = row.get(0)?;
@ -347,7 +355,7 @@ async fn insert_cache_asset(
},
)?
};
Ok::<Option<String>, AnyError>(maybe_response_body)
Ok::<Option<String>, CacheError>(maybe_response_body)
}).await?
}

View file

@ -2,7 +2,7 @@
[package]
name = "deno_canvas"
version = "0.37.0"
version = "0.42.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
@ -27,3 +27,4 @@ image = { version = "0.25.4", default-features = false, features = ["png", "jpeg
lcms2 = { version = "6.1.0", features = ["static"] }
num-traits = { version = "0.2.19" }
serde = { workspace = true, features = ["derive"] }
thiserror.workspace = true

View file

@ -6,8 +6,17 @@ pub mod error;
mod image_decoder;
mod image_ops;
mod op_create_image_bitmap;
use image::ColorType;
use op_create_image_bitmap::op_create_image_bitmap;
#[derive(Debug, thiserror::Error)]
pub enum CanvasError {
#[error("Color type '{0:?}' not supported")]
UnsupportedColorType(ColorType),
#[error(transparent)]
Image(#[from] image::ImageError),
}
deno_core::extension!(
deno_canvas,
deps = [deno_webidl, deno_web, deno_webgpu],

View file

@ -84,6 +84,7 @@ const {
NumberIsInteger,
NumberIsNaN,
NumberParseInt,
NumberParseFloat,
NumberPrototypeToFixed,
NumberPrototypeToString,
NumberPrototypeValueOf,
@ -1301,7 +1302,9 @@ function getKeys(value, showHidden) {
ArrayPrototypePushApply(keys, ArrayPrototypeFilter(symbols, filter));
}
}
keys = ArrayPrototypeFilter(keys, (key) => key !== "cause");
if (ObjectPrototypeIsPrototypeOf(ErrorPrototype, value)) {
keys = ArrayPrototypeFilter(keys, (key) => key !== "cause");
}
return keys;
}
@ -2650,6 +2653,7 @@ const HSL_PATTERN = new SafeRegExp(
);
function parseCssColor(colorString) {
colorString = StringPrototypeToLowerCase(colorString);
if (colorKeywords.has(colorString)) {
colorString = colorKeywords.get(colorString);
}
@ -3008,20 +3012,18 @@ function inspectArgs(args, inspectOptions = { __proto__: null }) {
} else if (ArrayPrototypeIncludes(["d", "i"], char)) {
// Format as an integer.
const value = args[a++];
if (typeof value == "bigint") {
formattedArg = `${value}n`;
} else if (typeof value == "number") {
formattedArg = `${NumberParseInt(String(value))}`;
} else {
if (typeof value === "symbol") {
formattedArg = "NaN";
} else {
formattedArg = `${NumberParseInt(value)}`;
}
} else if (char == "f") {
// Format as a floating point value.
const value = args[a++];
if (typeof value == "number") {
formattedArg = `${value}`;
} else {
if (typeof value === "symbol") {
formattedArg = "NaN";
} else {
formattedArg = `${NumberParseFloat(value)}`;
}
} else if (ArrayPrototypeIncludes(["O", "o"], char)) {
// Format as an object.
@ -3255,7 +3257,7 @@ class Console {
const stringifyValue = (value) =>
inspectValueWithQuotes(value, {
...getDefaultInspectOptions(),
...getConsoleInspectOptions(noColorStdout()),
depth: 1,
compact: true,
});

View file

@ -2,7 +2,7 @@
[package]
name = "deno_console"
version = "0.168.0"
version = "0.173.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_cron"
version = "0.48.0"
version = "0.53.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
@ -19,4 +19,5 @@ async-trait.workspace = true
chrono = { workspace = true, features = ["now"] }
deno_core.workspace = true
saffron.workspace = true
thiserror.workspace = true
tokio.workspace = true

View file

@ -1,17 +1,17 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use crate::CronError;
use async_trait::async_trait;
use deno_core::error::AnyError;
pub trait CronHandler {
type EH: CronHandle + 'static;
fn create(&self, spec: CronSpec) -> Result<Self::EH, AnyError>;
fn create(&self, spec: CronSpec) -> Result<Self::EH, CronError>;
}
#[async_trait(?Send)]
pub trait CronHandle {
async fn next(&self, prev_success: bool) -> Result<bool, AnyError>;
async fn next(&self, prev_success: bool) -> Result<bool, CronError>;
fn close(&self);
}

View file

@ -7,16 +7,13 @@ use std::borrow::Cow;
use std::cell::RefCell;
use std::rc::Rc;
pub use crate::interface::*;
use deno_core::error::get_custom_error_class;
use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::OpState;
use deno_core::Resource;
use deno_core::ResourceId;
pub use crate::interface::*;
pub const UNSTABLE_FEATURE_NAME: &str = "cron";
deno_core::extension!(deno_cron,
@ -49,6 +46,28 @@ impl<EH: CronHandle + 'static> Resource for CronResource<EH> {
}
}
#[derive(Debug, thiserror::Error)]
pub enum CronError {
#[error(transparent)]
Resource(deno_core::error::AnyError),
#[error("Cron name cannot exceed 64 characters: current length {0}")]
NameExceeded(usize),
#[error("Invalid cron name: only alphanumeric characters, whitespace, hyphens, and underscores are allowed")]
NameInvalid,
#[error("Cron with this name already exists")]
AlreadyExists,
#[error("Too many crons")]
TooManyCrons,
#[error("Invalid cron schedule")]
InvalidCron,
#[error("Invalid backoff schedule")]
InvalidBackoff,
#[error(transparent)]
AcquireError(#[from] tokio::sync::AcquireError),
#[error(transparent)]
Other(deno_core::error::AnyError),
}
#[op2]
#[smi]
fn op_cron_create<C>(
@ -56,7 +75,7 @@ fn op_cron_create<C>(
#[string] name: String,
#[string] cron_schedule: String,
#[serde] backoff_schedule: Option<Vec<u32>>,
) -> Result<ResourceId, AnyError>
) -> Result<ResourceId, CronError>
where
C: CronHandler + 'static,
{
@ -90,7 +109,7 @@ async fn op_cron_next<C>(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
prev_success: bool,
) -> Result<bool, AnyError>
) -> Result<bool, CronError>
where
C: CronHandler + 'static,
{
@ -102,7 +121,7 @@ where
if get_custom_error_class(&err) == Some("BadResource") {
return Ok(false);
} else {
return Err(err);
return Err(CronError::Resource(err));
}
}
};
@ -112,17 +131,14 @@ where
cron_handler.next(prev_success).await
}
fn validate_cron_name(name: &str) -> Result<(), AnyError> {
fn validate_cron_name(name: &str) -> Result<(), CronError> {
if name.len() > 64 {
return Err(type_error(format!(
"Cron name cannot exceed 64 characters: current length {}",
name.len()
)));
return Err(CronError::NameExceeded(name.len()));
}
if !name.chars().all(|c| {
c.is_ascii_whitespace() || c.is_ascii_alphanumeric() || c == '_' || c == '-'
}) {
return Err(type_error("Invalid cron name: only alphanumeric characters, whitespace, hyphens, and underscores are allowed"));
return Err(CronError::NameInvalid);
}
Ok(())
}

View file

@ -10,8 +10,6 @@ use std::rc::Weak;
use std::sync::Arc;
use async_trait::async_trait;
use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::futures;
use deno_core::futures::FutureExt;
use deno_core::unsync::spawn;
@ -21,6 +19,7 @@ use tokio::sync::mpsc::WeakSender;
use tokio::sync::OwnedSemaphorePermit;
use tokio::sync::Semaphore;
use crate::CronError;
use crate::CronHandle;
use crate::CronHandler;
use crate::CronSpec;
@ -81,7 +80,7 @@ impl LocalCronHandler {
async fn cron_loop(
runtime_state: Rc<RefCell<RuntimeState>>,
mut cron_schedule_rx: mpsc::Receiver<(String, bool)>,
) -> Result<(), AnyError> {
) -> Result<(), CronError> {
loop {
let earliest_deadline = runtime_state
.borrow()
@ -154,7 +153,7 @@ impl LocalCronHandler {
impl RuntimeState {
fn get_ready_crons(
&mut self,
) -> Result<Vec<(String, WeakSender<()>)>, AnyError> {
) -> Result<Vec<(String, WeakSender<()>)>, CronError> {
let now = chrono::Utc::now().timestamp_millis() as u64;
let ready = {
@ -191,7 +190,7 @@ impl RuntimeState {
impl CronHandler for LocalCronHandler {
type EH = CronExecutionHandle;
fn create(&self, spec: CronSpec) -> Result<Self::EH, AnyError> {
fn create(&self, spec: CronSpec) -> Result<Self::EH, CronError> {
// Ensure that the cron loop is started.
self.cron_loop_join_handle.get_or_init(|| {
let (cron_schedule_tx, cron_schedule_rx) =
@ -208,17 +207,17 @@ impl CronHandler for LocalCronHandler {
let mut runtime_state = self.runtime_state.borrow_mut();
if runtime_state.crons.len() > MAX_CRONS {
return Err(type_error("Too many crons"));
return Err(CronError::TooManyCrons);
}
if runtime_state.crons.contains_key(&spec.name) {
return Err(type_error("Cron with this name already exists"));
return Err(CronError::AlreadyExists);
}
// Validate schedule expression.
spec
.cron_schedule
.parse::<saffron::Cron>()
.map_err(|_| type_error("Invalid cron schedule"))?;
.map_err(|_| CronError::InvalidCron)?;
// Validate backoff_schedule.
if let Some(backoff_schedule) = &spec.backoff_schedule {
@ -263,7 +262,7 @@ struct Inner {
#[async_trait(?Send)]
impl CronHandle for CronExecutionHandle {
async fn next(&self, prev_success: bool) -> Result<bool, AnyError> {
async fn next(&self, prev_success: bool) -> Result<bool, CronError> {
self.inner.borrow_mut().permit.take();
if self
@ -300,7 +299,7 @@ impl CronHandle for CronExecutionHandle {
}
}
fn compute_next_deadline(cron_expression: &str) -> Result<u64, AnyError> {
fn compute_next_deadline(cron_expression: &str) -> Result<u64, CronError> {
let now = chrono::Utc::now();
if let Ok(test_schedule) = env::var("DENO_CRON_TEST_SCHEDULE_OFFSET") {
@ -311,19 +310,21 @@ fn compute_next_deadline(cron_expression: &str) -> Result<u64, AnyError> {
let cron = cron_expression
.parse::<saffron::Cron>()
.map_err(|_| anyhow::anyhow!("invalid cron expression"))?;
.map_err(|_| CronError::InvalidCron)?;
let Some(next_deadline) = cron.next_after(now) else {
return Err(anyhow::anyhow!("invalid cron expression"));
return Err(CronError::InvalidCron);
};
Ok(next_deadline.timestamp_millis() as u64)
}
fn validate_backoff_schedule(backoff_schedule: &[u32]) -> Result<(), AnyError> {
fn validate_backoff_schedule(
backoff_schedule: &[u32],
) -> Result<(), CronError> {
if backoff_schedule.len() > MAX_BACKOFF_COUNT {
return Err(type_error("Invalid backoff schedule"));
return Err(CronError::InvalidBackoff);
}
if backoff_schedule.iter().any(|s| *s > MAX_BACKOFF_MS) {
return Err(type_error("Invalid backoff schedule"));
return Err(CronError::InvalidBackoff);
}
Ok(())
}

View file

@ -18,21 +18,27 @@ import {
op_crypto_decrypt,
op_crypto_derive_bits,
op_crypto_derive_bits_x25519,
op_crypto_derive_bits_x448,
op_crypto_encrypt,
op_crypto_export_key,
op_crypto_export_pkcs8_ed25519,
op_crypto_export_pkcs8_x25519,
op_crypto_export_pkcs8_x448,
op_crypto_export_spki_ed25519,
op_crypto_export_spki_x25519,
op_crypto_export_spki_x448,
op_crypto_generate_ed25519_keypair,
op_crypto_generate_key,
op_crypto_generate_x25519_keypair,
op_crypto_generate_x448_keypair,
op_crypto_get_random_values,
op_crypto_import_key,
op_crypto_import_pkcs8_ed25519,
op_crypto_import_pkcs8_x25519,
op_crypto_import_pkcs8_x448,
op_crypto_import_spki_ed25519,
op_crypto_import_spki_x25519,
op_crypto_import_spki_x448,
op_crypto_jwk_x_ed25519,
op_crypto_random_uuid,
op_crypto_sign_ed25519,
@ -134,6 +140,7 @@ const supportedAlgorithms = {
"AES-KW": "AesKeyGenParams",
"HMAC": "HmacKeyGenParams",
"X25519": null,
"X448": null,
"Ed25519": null,
},
"sign": {
@ -165,12 +172,14 @@ const supportedAlgorithms = {
"AES-KW": null,
"Ed25519": null,
"X25519": null,
"X448": null,
},
"deriveBits": {
"HKDF": "HkdfParams",
"PBKDF2": "Pbkdf2Params",
"ECDH": "EcdhKeyDeriveParams",
"X25519": "EcdhKeyDeriveParams",
"X448": "EcdhKeyDeriveParams",
},
"encrypt": {
"RSA-OAEP": "RsaOaepParams",
@ -1037,6 +1046,10 @@ class SubtleCrypto {
result = exportKeyEd25519(format, key, innerKey);
break;
}
case "X448": {
result = exportKeyX448(format, key, innerKey);
break;
}
case "X25519": {
result = exportKeyX25519(format, key, innerKey);
break;
@ -1954,6 +1967,48 @@ async function generateKey(normalizedAlgorithm, extractable, usages) {
return generateKeyAES(normalizedAlgorithm, extractable, usages);
}
case "X448": {
if (
ArrayPrototypeFind(
usages,
(u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u),
) !== undefined
) {
throw new DOMException("Invalid key usage", "SyntaxError");
}
const privateKeyData = new Uint8Array(56);
const publicKeyData = new Uint8Array(56);
op_crypto_generate_x448_keypair(privateKeyData, publicKeyData);
const handle = {};
WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData);
const publicHandle = {};
WeakMapPrototypeSet(KEY_STORE, publicHandle, publicKeyData);
const algorithm = {
name: algorithmName,
};
const publicKey = constructKey(
"public",
true,
usageIntersection(usages, []),
algorithm,
publicHandle,
);
const privateKey = constructKey(
"private",
extractable,
usageIntersection(usages, ["deriveKey", "deriveBits"]),
algorithm,
handle,
);
return { publicKey, privateKey };
}
case "X25519": {
if (
ArrayPrototypeFind(
@ -2100,6 +2155,211 @@ async function generateKey(normalizedAlgorithm, extractable, usages) {
}
}
function importKeyX448(
format,
keyData,
extractable,
keyUsages,
) {
switch (format) {
case "raw": {
// 1.
if (keyUsages.length > 0) {
throw new DOMException("Invalid key usage", "SyntaxError");
}
const handle = {};
WeakMapPrototypeSet(KEY_STORE, handle, keyData);
// 2-3.
const algorithm = {
name: "X448",
};
// 4-6.
return constructKey(
"public",
extractable,
[],
algorithm,
handle,
);
}
case "spki": {
// 1.
if (keyUsages.length > 0) {
throw new DOMException("Invalid key usage", "SyntaxError");
}
const publicKeyData = new Uint8Array(56);
if (!op_crypto_import_spki_x448(keyData, publicKeyData)) {
throw new DOMException("Invalid key data", "DataError");
}
const handle = {};
WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData);
const algorithm = {
name: "X448",
};
return constructKey(
"public",
extractable,
[],
algorithm,
handle,
);
}
case "pkcs8": {
// 1.
if (
ArrayPrototypeFind(
keyUsages,
(u) => !ArrayPrototypeIncludes(["deriveKey", "deriveBits"], u),
) !== undefined
) {
throw new DOMException("Invalid key usage", "SyntaxError");
}
const privateKeyData = new Uint8Array(32);
if (!op_crypto_import_pkcs8_x448(keyData, privateKeyData)) {
throw new DOMException("Invalid key data", "DataError");
}
const handle = {};
WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData);
const algorithm = {
name: "X448",
};
return constructKey(
"private",
extractable,
usageIntersection(keyUsages, recognisedUsages),
algorithm,
handle,
);
}
case "jwk": {
// 1.
const jwk = keyData;
// 2.
if (jwk.d !== undefined) {
if (
ArrayPrototypeFind(
keyUsages,
(u) =>
!ArrayPrototypeIncludes(
["deriveKey", "deriveBits"],
u,
),
) !== undefined
) {
throw new DOMException("Invalid key usage", "SyntaxError");
}
}
// 3.
if (jwk.d === undefined && keyUsages.length > 0) {
throw new DOMException("Invalid key usage", "SyntaxError");
}
// 4.
if (jwk.kty !== "OKP") {
throw new DOMException("Invalid key type", "DataError");
}
// 5.
if (jwk.crv !== "X448") {
throw new DOMException("Invalid curve", "DataError");
}
// 6.
if (keyUsages.length > 0 && jwk.use !== undefined) {
if (jwk.use !== "enc") {
throw new DOMException("Invalid key use", "DataError");
}
}
// 7.
if (jwk.key_ops !== undefined) {
if (
ArrayPrototypeFind(
jwk.key_ops,
(u) => !ArrayPrototypeIncludes(recognisedUsages, u),
) !== undefined
) {
throw new DOMException(
"'key_ops' property of JsonWebKey is invalid",
"DataError",
);
}
if (
!ArrayPrototypeEvery(
jwk.key_ops,
(u) => ArrayPrototypeIncludes(keyUsages, u),
)
) {
throw new DOMException(
"'key_ops' property of JsonWebKey is invalid",
"DataError",
);
}
}
// 8.
if (jwk.ext !== undefined && jwk.ext === false && extractable) {
throw new DOMException("Invalid key extractability", "DataError");
}
// 9.
if (jwk.d !== undefined) {
// https://www.rfc-editor.org/rfc/rfc8037#section-2
const privateKeyData = op_crypto_base64url_decode(jwk.d);
const handle = {};
WeakMapPrototypeSet(KEY_STORE, handle, privateKeyData);
const algorithm = {
name: "X448",
};
return constructKey(
"private",
extractable,
usageIntersection(keyUsages, ["deriveKey", "deriveBits"]),
algorithm,
handle,
);
} else {
// https://www.rfc-editor.org/rfc/rfc8037#section-2
const publicKeyData = op_crypto_base64url_decode(jwk.x);
const handle = {};
WeakMapPrototypeSet(KEY_STORE, handle, publicKeyData);
const algorithm = {
name: "X448",
};
return constructKey(
"public",
extractable,
[],
algorithm,
handle,
);
}
}
default:
throw new DOMException("Not implemented", "NotSupportedError");
}
}
function importKeyEd25519(
format,
keyData,
@ -3358,6 +3618,14 @@ async function importKeyInner(
["wrapKey", "unwrapKey"],
);
}
case "X448": {
return importKeyX448(
format,
keyData,
extractable,
keyUsages,
);
}
case "X25519": {
return importKeyX25519(
format,
@ -4162,6 +4430,66 @@ function exportKeyEd25519(format, key, innerKey) {
}
}
function exportKeyX448(format, key, innerKey) {
switch (format) {
case "raw": {
// 1.
if (key[_type] !== "public") {
throw new DOMException(
"Key is not a public key",
"InvalidAccessError",
);
}
// 2-3.
return TypedArrayPrototypeGetBuffer(innerKey);
}
case "spki": {
// 1.
if (key[_type] !== "public") {
throw new DOMException(
"Key is not a public key",
"InvalidAccessError",
);
}
const spkiDer = op_crypto_export_spki_x448(innerKey);
return TypedArrayPrototypeGetBuffer(spkiDer);
}
case "pkcs8": {
// 1.
if (key[_type] !== "private") {
throw new DOMException(
"Key is not a private key",
"InvalidAccessError",
);
}
const pkcs8Der = op_crypto_export_pkcs8_x448(
new Uint8Array([0x04, 0x22, ...new SafeArrayIterator(innerKey)]),
);
pkcs8Der[15] = 0x20;
return TypedArrayPrototypeGetBuffer(pkcs8Der);
}
case "jwk": {
if (key[_type] === "private") {
throw new DOMException("Not implemented", "NotSupportedError");
}
const x = op_crypto_base64url_encode(innerKey);
const jwk = {
kty: "OKP",
crv: "X448",
x,
"key_ops": key.usages,
ext: key[_extractable],
};
return jwk;
}
default:
throw new DOMException("Not implemented", "NotSupportedError");
}
}
function exportKeyX25519(format, key, innerKey) {
switch (format) {
case "raw": {
@ -4519,6 +4847,55 @@ async function deriveBits(normalizedAlgorithm, baseKey, length) {
return TypedArrayPrototypeGetBuffer(buf);
}
case "X448": {
// 1.
if (baseKey[_type] !== "private") {
throw new DOMException("Invalid key type", "InvalidAccessError");
}
// 2.
const publicKey = normalizedAlgorithm.public;
// 3.
if (publicKey[_type] !== "public") {
throw new DOMException("Invalid key type", "InvalidAccessError");
}
// 4.
if (publicKey[_algorithm].name !== baseKey[_algorithm].name) {
throw new DOMException(
"Algorithm mismatch",
"InvalidAccessError",
);
}
// 5.
const kHandle = baseKey[_handle];
const k = WeakMapPrototypeGet(KEY_STORE, kHandle);
const uHandle = publicKey[_handle];
const u = WeakMapPrototypeGet(KEY_STORE, uHandle);
const secret = new Uint8Array(56);
const isIdentity = op_crypto_derive_bits_x448(k, u, secret);
// 6.
if (isIdentity) {
throw new DOMException("Invalid key", "OperationError");
}
// 7.
if (length === null) {
return TypedArrayPrototypeGetBuffer(secret);
} else if (
TypedArrayPrototypeGetByteLength(secret) * 8 < length
) {
throw new DOMException("Invalid length", "OperationError");
} else {
return ArrayBufferPrototypeSlice(
TypedArrayPrototypeGetBuffer(secret),
0,
MathCeil(length / 8),
);
}
}
case "X25519": {
// 1.
if (baseKey[_type] !== "private") {

View file

@ -2,7 +2,7 @@
[package]
name = "deno_crypto"
version = "0.182.0"
version = "0.187.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
@ -24,6 +24,7 @@ ctr = "0.9.1"
curve25519-dalek = "4.1.3"
deno_core.workspace = true
deno_web.workspace = true
ed448-goldilocks = { version = "0.8.3", features = ["zeroize"] }
elliptic-curve = { version = "0.13.1", features = ["std", "pem"] }
num-traits = "0.2.14"
once_cell.workspace = true
@ -40,5 +41,7 @@ sha1.workspace = true
sha2.workspace = true
signature.workspace = true
spki.workspace = true
thiserror.workspace = true
tokio.workspace = true
uuid.workspace = true
x25519-dalek = "2.0.0"

Some files were not shown because too many files have changed in this diff Show more