1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

Merge branch 'main' into fix-doc-test-shebang-parse-error

This commit is contained in:
Bartek Iwańczuk 2024-10-26 22:10:44 +01:00 committed by GitHub
commit dc61a96e24
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
606 changed files with 12875 additions and 6783 deletions

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,7 +33,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

@ -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 = 18;
const cacheVersion = 22;
const ubuntuX86Runner = "ubuntu-22.04";
const ubuntuX86XlRunner = "ubuntu-22.04-xl";
@ -193,7 +193,7 @@ const installNodeStep = {
};
const installDenoStep = {
name: "Install Deno",
uses: "denoland/setup-deno@v1",
uses: "denoland/setup-deno@v2",
with: { "deno-version": "v1.x" },
};
@ -751,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"),
},
@ -779,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"),
},
@ -797,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"),
},
{
@ -1045,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
@ -361,8 +361,8 @@ jobs:
path: |-
~/.cargo/registry/index
~/.cargo/registry/cache
key: '18-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
restore-keys: '18-cargo-home-${{ matrix.os }}-${{ matrix.arch }}'
key: '22-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
restore-keys: '22-cargo-home-${{ matrix.os }}-${{ matrix.arch }}'
if: '!(matrix.skip)'
- name: Restore cache build output (PR)
uses: actions/cache/restore@v4
@ -375,7 +375,7 @@ jobs:
!./target/*/*.zip
!./target/*/*.tar.gz
key: never_saved
restore-keys: '18-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-'
restore-keys: '22-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
@ -442,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: |-
@ -461,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' &&
@ -474,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' &&
@ -652,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
@ -685,7 +685,7 @@ jobs:
!./target/*/*.zip
!./target/*/*.sha256sum
!./target/*/*.tar.gz
key: '18-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
key: '22-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 }}

433
Cargo.lock generated
View file

@ -769,7 +769,7 @@ dependencies = [
"http-body-util",
"hyper 1.4.1",
"hyper-util",
"nix 0.26.2",
"nix",
"once_cell",
"os_pipe",
"pretty_assertions",
@ -1154,7 +1154,7 @@ dependencies = [
[[package]]
name = "deno"
version = "2.0.0"
version = "2.0.3"
dependencies = [
"anstream",
"async-trait",
@ -1188,6 +1188,7 @@ dependencies = [
"deno_task_shell",
"deno_terminal 0.2.0",
"deno_tower_lsp",
"dhat",
"dissimilar",
"dotenvy",
"dprint-plugin-json",
@ -1195,7 +1196,6 @@ dependencies = [
"dprint-plugin-markdown",
"dprint-plugin-typescript",
"env_logger",
"eszip",
"fancy-regex",
"faster-hex",
"flate2",
@ -1213,7 +1213,6 @@ dependencies = [
"lazy-regex",
"libc",
"libsui",
"libuv-sys-lite",
"libz-sys",
"log",
"lsp-types",
@ -1221,8 +1220,7 @@ dependencies = [
"markup_fmt",
"memmem",
"monch",
"napi_sym",
"nix 0.26.2",
"nix",
"node_resolver",
"notify",
"once_cell",
@ -1262,9 +1260,7 @@ dependencies = [
"walkdir",
"which 4.4.2",
"winapi",
"windows-sys 0.52.0",
"winres",
"yoke",
"zeromq",
"zip",
"zstd",
@ -1327,7 +1323,7 @@ dependencies = [
[[package]]
name = "deno_bench_util"
version = "0.165.0"
version = "0.168.0"
dependencies = [
"bencher",
"deno_core",
@ -1336,23 +1332,25 @@ dependencies = [
[[package]]
name = "deno_broadcast_channel"
version = "0.165.0"
version = "0.168.0"
dependencies = [
"async-trait",
"deno_core",
"thiserror",
"tokio",
"uuid",
]
[[package]]
name = "deno_cache"
version = "0.103.0"
version = "0.106.0"
dependencies = [
"async-trait",
"deno_core",
"rusqlite",
"serde",
"sha2",
"thiserror",
"tokio",
]
@ -1378,19 +1376,20 @@ dependencies = [
[[package]]
name = "deno_canvas"
version = "0.40.0"
version = "0.43.0"
dependencies = [
"deno_core",
"deno_webgpu",
"image",
"serde",
"thiserror",
]
[[package]]
name = "deno_config"
version = "0.37.1"
version = "0.37.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cb7a1723676fba5964f8d7441d8b53748f9e74d6d4241be7de9730da021859a"
checksum = "5900bfb37538d83b19ba0b157cdc785770e38422ee4632411e3bd3d90ac0f537"
dependencies = [
"anyhow",
"deno_package_json",
@ -1412,16 +1411,16 @@ dependencies = [
[[package]]
name = "deno_console"
version = "0.171.0"
version = "0.174.0"
dependencies = [
"deno_core",
]
[[package]]
name = "deno_core"
version = "0.311.0"
version = "0.314.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e09bd55da542fa1fde753aff617c355b5d782e763ab2a19e4371a56d7844cac"
checksum = "83138917579676069b423c3eb9be3c1e579f60dc022d85f6ded4c792456255ff"
dependencies = [
"anyhow",
"bincode",
@ -1434,7 +1433,7 @@ dependencies = [
"deno_unsync",
"futures",
"libc",
"memoffset 0.9.1",
"memoffset",
"parking_lot",
"percent-encoding",
"pin-project",
@ -1457,19 +1456,20 @@ checksum = "a13951ea98c0a4c372f162d669193b4c9d991512de9f2381dd161027f34b26b1"
[[package]]
name = "deno_cron"
version = "0.51.0"
version = "0.54.0"
dependencies = [
"anyhow",
"async-trait",
"chrono",
"deno_core",
"saffron",
"thiserror",
"tokio",
]
[[package]]
name = "deno_crypto"
version = "0.185.0"
version = "0.188.0"
dependencies = [
"aes",
"aes-gcm",
@ -1498,15 +1498,17 @@ dependencies = [
"sha2",
"signature",
"spki",
"thiserror",
"tokio",
"uuid",
"x25519-dalek",
]
[[package]]
name = "deno_doc"
version = "0.152.0"
version = "0.154.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8d237256ad99d6064b271324485120028e843329fd0fa0e93175d5e98f17033"
checksum = "17e204e45b0d79750880114e37b34abe19ad0710d8435a8da8f23a528fe98de4"
dependencies = [
"anyhow",
"cfg-if",
@ -1523,23 +1525,13 @@ dependencies = [
"regex",
"serde",
"serde_json",
"syntect",
"termcolor",
"tree-sitter-bash",
"tree-sitter-css",
"tree-sitter-highlight",
"tree-sitter-html",
"tree-sitter-javascript",
"tree-sitter-json",
"tree-sitter-md",
"tree-sitter-regex",
"tree-sitter-rust",
"tree-sitter-typescript",
"tree-sitter-xml",
]
[[package]]
name = "deno_fetch"
version = "0.195.0"
version = "0.198.0"
dependencies = [
"base64 0.21.7",
"bytes",
@ -1560,6 +1552,7 @@ dependencies = [
"rustls-webpki",
"serde",
"serde_json",
"thiserror",
"tokio",
"tokio-rustls",
"tokio-socks",
@ -1571,7 +1564,7 @@ dependencies = [
[[package]]
name = "deno_ffi"
version = "0.158.0"
version = "0.161.0"
dependencies = [
"deno_core",
"deno_permissions",
@ -1580,15 +1573,18 @@ dependencies = [
"libffi",
"libffi-sys",
"log",
"num-bigint",
"serde",
"serde-value",
"serde_json",
"thiserror",
"tokio",
"winapi",
]
[[package]]
name = "deno_fs"
version = "0.81.0"
version = "0.84.0"
dependencies = [
"async-trait",
"base32",
@ -1599,19 +1595,20 @@ dependencies = [
"filetime",
"junction",
"libc",
"nix 0.26.2",
"nix",
"rand",
"rayon",
"serde",
"thiserror",
"winapi",
"windows-sys 0.52.0",
]
[[package]]
name = "deno_graph"
version = "0.83.3"
version = "0.83.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77163c46755676d8f793fc19e365537ba660a8db173cd1e02d21eb010c0b3cef"
checksum = "5bd20bc0780071989c622cbfd5d4fb2e4fd05a247ccd7f791f13c8d2c3792228"
dependencies = [
"anyhow",
"async-trait",
@ -1638,7 +1635,7 @@ dependencies = [
[[package]]
name = "deno_http"
version = "0.169.0"
version = "0.172.0"
dependencies = [
"async-compression",
"async-trait",
@ -1677,7 +1674,7 @@ dependencies = [
[[package]]
name = "deno_io"
version = "0.81.0"
version = "0.84.0"
dependencies = [
"async-trait",
"deno_core",
@ -1698,7 +1695,7 @@ dependencies = [
[[package]]
name = "deno_kv"
version = "0.79.0"
version = "0.82.0"
dependencies = [
"anyhow",
"async-trait",
@ -1723,6 +1720,7 @@ dependencies = [
"rand",
"rusqlite",
"serde",
"thiserror",
"url",
]
@ -1769,11 +1767,17 @@ dependencies = [
[[package]]
name = "deno_napi"
version = "0.102.0"
version = "0.105.0"
dependencies = [
"deno_core",
"deno_permissions",
"libc",
"libloading 0.7.4",
"libuv-sys-lite",
"log",
"napi_sym",
"thiserror",
"windows-sys 0.52.0",
]
[[package]]
@ -1791,7 +1795,7 @@ dependencies = [
[[package]]
name = "deno_net"
version = "0.163.0"
version = "0.166.0"
dependencies = [
"deno_core",
"deno_permissions",
@ -1800,6 +1804,7 @@ dependencies = [
"rustls-tokio-stream",
"serde",
"socket2",
"thiserror",
"tokio",
"trust-dns-proto",
"trust-dns-resolver",
@ -1807,7 +1812,7 @@ dependencies = [
[[package]]
name = "deno_node"
version = "0.108.0"
version = "0.111.0"
dependencies = [
"aead-gcm-stream",
"aes",
@ -1897,9 +1902,9 @@ dependencies = [
[[package]]
name = "deno_npm"
version = "0.25.3"
version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8050bcc2513046cbc0134ae1bc0f3b251a58b95012f3b81e0ea09a7f069c301b"
checksum = "e6b4dc4a9f1cff63d5638e7d93042f24f46300d1cc77b86f3caaa699a7ddccf7"
dependencies = [
"anyhow",
"async-trait",
@ -1916,9 +1921,9 @@ dependencies = [
[[package]]
name = "deno_ops"
version = "0.187.0"
version = "0.190.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e040fd4def8a67538fe38c9955fd970efc9f44284bd69d44f8992a456afd665d"
checksum = "26f46d4e4f52f26c882b74a9b58810ea75252b807cf0966166ec333077cdfd85"
dependencies = [
"proc-macro-rules",
"proc-macro2",
@ -1945,9 +1950,9 @@ dependencies = [
[[package]]
name = "deno_path_util"
version = "0.2.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4889646c1ce8437a6fde3acb057fd7e2d039e62c61f5063fc125ed1ede114dc6"
checksum = "ff25f6e08e7a0214bbacdd6f7195c7f1ebcd850c87a624e4ff06326b68b42d99"
dependencies = [
"percent-encoding",
"thiserror",
@ -1956,7 +1961,7 @@ dependencies = [
[[package]]
name = "deno_permissions"
version = "0.31.0"
version = "0.34.0"
dependencies = [
"deno_core",
"deno_path_util",
@ -1973,7 +1978,7 @@ dependencies = [
[[package]]
name = "deno_resolver"
version = "0.3.0"
version = "0.6.0"
dependencies = [
"anyhow",
"base32",
@ -1989,8 +1994,9 @@ dependencies = [
[[package]]
name = "deno_runtime"
version = "0.180.0"
version = "0.183.0"
dependencies = [
"color-print",
"deno_ast",
"deno_broadcast_channel",
"deno_cache",
@ -2030,7 +2036,7 @@ dependencies = [
"libc",
"log",
"netif",
"nix 0.26.2",
"nix",
"node_resolver",
"notify",
"ntapi",
@ -2038,11 +2044,13 @@ dependencies = [
"percent-encoding",
"regex",
"rustyline",
"same-file",
"serde",
"signal-hook",
"signal-hook-registry",
"tempfile",
"test_server",
"thiserror",
"tokio",
"tokio-metrics",
"twox-hash",
@ -2054,9 +2062,9 @@ dependencies = [
[[package]]
name = "deno_semver"
version = "0.5.14"
version = "0.5.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670fec7ef309384e23c2a90ac5d2d9d91a776d225306c75f5cdd28cf6cc8a59f"
checksum = "c957c6a57c38b7dde2315df0da0ec228911e56a74f185b108a488d0401841a67"
dependencies = [
"monch",
"once_cell",
@ -2104,7 +2112,7 @@ dependencies = [
[[package]]
name = "deno_tls"
version = "0.158.0"
version = "0.161.0"
dependencies = [
"deno_core",
"deno_native_certs",
@ -2113,6 +2121,7 @@ dependencies = [
"rustls-tokio-stream",
"rustls-webpki",
"serde",
"thiserror",
"tokio",
"webpki-roots",
]
@ -2152,18 +2161,19 @@ dependencies = [
[[package]]
name = "deno_url"
version = "0.171.0"
version = "0.174.0"
dependencies = [
"deno_bench_util",
"deno_console",
"deno_core",
"deno_webidl",
"thiserror",
"urlpattern",
]
[[package]]
name = "deno_web"
version = "0.202.0"
version = "0.205.0"
dependencies = [
"async-trait",
"base64-simd 0.8.0",
@ -2178,17 +2188,19 @@ dependencies = [
"flate2",
"futures",
"serde",
"thiserror",
"tokio",
"uuid",
]
[[package]]
name = "deno_webgpu"
version = "0.138.0"
version = "0.141.0"
dependencies = [
"deno_core",
"raw-window-handle",
"serde",
"thiserror",
"tokio",
"wgpu-core",
"wgpu-types",
@ -2196,7 +2208,7 @@ dependencies = [
[[package]]
name = "deno_webidl"
version = "0.171.0"
version = "0.174.0"
dependencies = [
"deno_bench_util",
"deno_core",
@ -2204,7 +2216,7 @@ dependencies = [
[[package]]
name = "deno_websocket"
version = "0.176.0"
version = "0.179.0"
dependencies = [
"bytes",
"deno_core",
@ -2220,16 +2232,18 @@ dependencies = [
"once_cell",
"rustls-tokio-stream",
"serde",
"thiserror",
"tokio",
]
[[package]]
name = "deno_webstorage"
version = "0.166.0"
version = "0.169.0"
dependencies = [
"deno_core",
"deno_web",
"rusqlite",
"thiserror",
]
[[package]]
@ -2415,6 +2429,22 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6e854126756c496b8c81dec88f9a706b15b875c5849d4097a3854476b9fdf94"
[[package]]
name = "dhat"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cd11d84628e233de0ce467de10b8633f4ddaecafadefc86e13b84b8739b827"
dependencies = [
"backtrace",
"lazy_static",
"mintex",
"parking_lot",
"rustc-hash 1.1.0",
"serde",
"serde_json",
"thousands",
]
[[package]]
name = "diff"
version = "0.1.13"
@ -2536,9 +2566,9 @@ dependencies = [
[[package]]
name = "dprint-plugin-json"
version = "0.19.3"
version = "0.19.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19f4a9f2f548b2098b8ec597d7bb40af133b6e9a3187c1d3c4caa101b8c93c3"
checksum = "57f91e594559b450b7c5d6a0ba9f3f9fe951c1ea371168f7c95973da3fdbd85a"
dependencies = [
"anyhow",
"dprint-core",
@ -2550,9 +2580,9 @@ dependencies = [
[[package]]
name = "dprint-plugin-jupyter"
version = "0.1.3"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c67b0e54b552a4775c221b44ed33be918c400bd8041d1f044f947fbb01025cc0"
checksum = "d0d20684e37b3824e2bc917cfcb14e2cdf88398eef507335d839cbd78172bfee"
dependencies = [
"anyhow",
"dprint-core",
@ -2864,29 +2894,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31ae425815400e5ed474178a7a22e275a9687086a12ca63ec793ff292d8fdae8"
[[package]]
name = "eszip"
version = "0.79.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eb55c89bdde75a3826a79d49c9d847623ae7fbdb2695b542982982da990d33e"
dependencies = [
"anyhow",
"async-trait",
"base64 0.21.7",
"deno_ast",
"deno_graph",
"deno_npm",
"deno_semver",
"futures",
"hashlink 0.8.4",
"indexmap",
"serde",
"serde_json",
"sha2",
"thiserror",
"url",
]
[[package]]
name = "fallible-iterator"
version = "0.3.0"
@ -3498,15 +3505,6 @@ dependencies = [
"allocator-api2",
]
[[package]]
name = "hashlink"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
dependencies = [
"hashbrown",
]
[[package]]
name = "hashlink"
version = "0.9.1"
@ -4007,9 +4005,9 @@ dependencies = [
[[package]]
name = "jsonc-parser"
version = "0.23.0"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7725c320caac8c21d8228c1d055af27a995d371f78cc763073d3e068323641b5"
checksum = "b558af6b49fd918e970471374e7a798b2c9bbcda624a210ffa3901ee5614bc8e"
dependencies = [
"serde_json",
]
@ -4300,9 +4298,9 @@ dependencies = [
[[package]]
name = "malva"
version = "0.10.1"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "484beda6e5d775ed06a8ec0fce79e51d39f49d834ed2a29da3f437079321804f"
checksum = "1c67b97ed99f56b86fa3c010843441f1fcdb71884bab96b8551bb3d1e7c6d529"
dependencies = [
"aho-corasick",
"itertools 0.13.0",
@ -4313,9 +4311,9 @@ dependencies = [
[[package]]
name = "markup_fmt"
version = "0.13.1"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dab5ae899659fbe5c8835b2c8ca8d3e357974a3e454138925b404004973361f"
checksum = "3f15d7b24ae4ea9b87279bc0696462a4fb6c2168847f2cc162a2da05fe1a0f61"
dependencies = [
"aho-corasick",
"css_dataset",
@ -4370,15 +4368,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a64a92489e2744ce060c349162be1c5f33c6969234104dbd99ddb5feb08b8c15"
[[package]]
name = "memoffset"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
[[package]]
name = "memoffset"
version = "0.9.1"
@ -4425,6 +4414,12 @@ dependencies = [
"simd-adler32",
]
[[package]]
name = "mintex"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bec4598fddb13cc7b528819e697852653252b760f1228b7642679bf2ff2cd07"
[[package]]
name = "mio"
version = "0.8.11"
@ -4488,7 +4483,7 @@ dependencies = [
[[package]]
name = "napi_sym"
version = "0.101.0"
version = "0.104.0"
dependencies = [
"quote",
"serde",
@ -4530,20 +4525,6 @@ dependencies = [
"smallvec",
]
[[package]]
name = "nix"
version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
dependencies = [
"bitflags 1.3.2",
"cfg-if",
"libc",
"memoffset 0.7.1",
"pin-utils",
"static_assertions",
]
[[package]]
name = "nix"
version = "0.27.1"
@ -4557,7 +4538,7 @@ dependencies = [
[[package]]
name = "node_resolver"
version = "0.10.0"
version = "0.13.0"
dependencies = [
"anyhow",
"async-trait",
@ -4733,6 +4714,28 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "onig"
version = "6.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f"
dependencies = [
"bitflags 1.3.2",
"libc",
"once_cell",
"onig_sys",
]
[[package]]
name = "onig_sys"
version = "69.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7"
dependencies = [
"cc",
"pkg-config",
]
[[package]]
name = "opaque-debug"
version = "0.3.1"
@ -5721,7 +5724,7 @@ checksum = "32a58fa8a7ccff2aec4f39cc45bf5f985cec7125ab271cf681c279fd00192b49"
dependencies = [
"countme",
"hashbrown",
"memoffset 0.9.1",
"memoffset",
"rustc-hash 1.1.0",
"text-size",
]
@ -5779,7 +5782,7 @@ dependencies = [
"bitflags 2.6.0",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink 0.9.1",
"hashlink",
"libsqlite3-sys",
"smallvec",
]
@ -5929,7 +5932,7 @@ dependencies = [
"libc",
"log",
"memchr",
"nix 0.27.1",
"nix",
"radix_trie",
"unicode-segmentation",
"unicode-width",
@ -6166,9 +6169,9 @@ dependencies = [
[[package]]
name = "serde_v8"
version = "0.220.0"
version = "0.223.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e7a65d91d79acc82aa229aeb084f4a39bda269069bc1520df40f679495388e4"
checksum = "9cf3d859dda87ee96423c01244f10af864fa6d6a9fcdc2b77e0595078ea0ea11"
dependencies = [
"num-bigint",
"serde",
@ -7021,6 +7024,26 @@ dependencies = [
"syn 2.0.72",
]
[[package]]
name = "syntect"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1"
dependencies = [
"bincode",
"bitflags 1.3.2",
"flate2",
"fnv",
"once_cell",
"onig",
"regex-syntax",
"serde",
"serde_derive",
"serde_json",
"thiserror",
"walkdir",
]
[[package]]
name = "tap"
version = "1.0.1"
@ -7112,7 +7135,7 @@ dependencies = [
"libc",
"lsp-types",
"monch",
"nix 0.26.2",
"nix",
"once_cell",
"os_pipe",
"parking_lot",
@ -7153,24 +7176,30 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.61"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.61"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.72",
]
[[package]]
name = "thousands"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
[[package]]
name = "thread_local"
version = "1.1.8"
@ -7452,128 +7481,6 @@ dependencies = [
"once_cell",
]
[[package]]
name = "tree-sitter"
version = "0.22.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df7cc499ceadd4dcdf7ec6d4cbc34ece92c3fa07821e287aedecd4416c516dca"
dependencies = [
"cc",
"regex",
]
[[package]]
name = "tree-sitter-bash"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5244703ad2e08a616d859a0557d7aa290adcd5e0990188a692e628ffe9dce40"
dependencies = [
"cc",
"tree-sitter",
]
[[package]]
name = "tree-sitter-css"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e08e324b1cf60fd3291774b49724c66de2ce8fcf4d358d0b4b82e37b41b1c9b"
dependencies = [
"cc",
"tree-sitter",
]
[[package]]
name = "tree-sitter-highlight"
version = "0.22.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaca0fe34fa96eec6aaa8e63308dbe1bafe65a6317487c287f93938959b21907"
dependencies = [
"lazy_static",
"regex",
"thiserror",
"tree-sitter",
]
[[package]]
name = "tree-sitter-html"
version = "0.20.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8766b5ad3721517f8259e6394aefda9c686aebf7a8c74ab8624f2c3b46902fd5"
dependencies = [
"cc",
"tree-sitter",
]
[[package]]
name = "tree-sitter-javascript"
version = "0.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8710a71bc6779e33811a8067bdda3ed08bed1733296ff915e44faf60f8c533d7"
dependencies = [
"cc",
"tree-sitter",
]
[[package]]
name = "tree-sitter-json"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b737dcb73c35d74b7d64a5f3dde158113c86a012bf3cee2bfdf2150d23b05db"
dependencies = [
"cc",
"tree-sitter",
]
[[package]]
name = "tree-sitter-md"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c3cfd068f2527250bbd8ff407431164e12b17863e7eafb76e311dd3f96965a"
dependencies = [
"cc",
"tree-sitter",
]
[[package]]
name = "tree-sitter-regex"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ff1286fe9651b2797484839ffa37aa76c8618d4ccb6836d7e31765dfd60c0d5"
dependencies = [
"cc",
"tree-sitter",
]
[[package]]
name = "tree-sitter-rust"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "277690f420bf90741dea984f3da038ace46c4fe6047cba57a66822226cde1c93"
dependencies = [
"cc",
"tree-sitter",
]
[[package]]
name = "tree-sitter-typescript"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecb35d98a688378e56c18c9c159824fd16f730ccbea19aacf4f206e5d5438ed9"
dependencies = [
"cc",
"tree-sitter",
]
[[package]]
name = "tree-sitter-xml"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65c3a1b08e9842143f84fde1a18ac40ee77ca80a80b14077e4ca67a3b4808b8b"
dependencies = [
"cc",
"tree-sitter",
]
[[package]]
name = "triomphe"
version = "0.1.13"

View file

@ -5,7 +5,6 @@ resolver = "2"
members = [
"bench_util",
"cli",
"cli/napi/sym",
"ext/broadcast_channel",
"ext/cache",
"ext/canvas",
@ -19,6 +18,7 @@ members = [
"ext/io",
"ext/kv",
"ext/napi",
"ext/napi/sym",
"ext/net",
"ext/node",
"ext/url",
@ -46,18 +46,18 @@ repository = "https://github.com/denoland/deno"
[workspace.dependencies]
deno_ast = { version = "=0.42.2", features = ["transpiling"] }
deno_core = { version = "0.311.0" }
deno_core = { version = "0.314.2" }
deno_bench_util = { version = "0.165.0", path = "./bench_util" }
deno_bench_util = { version = "0.168.0", path = "./bench_util" }
deno_lockfile = "=0.23.1"
deno_media_type = { version = "0.1.4", features = ["module_specifier"] }
deno_npm = "=0.25.3"
deno_path_util = "=0.2.0"
deno_permissions = { version = "0.31.0", path = "./runtime/permissions" }
deno_runtime = { version = "0.180.0", path = "./runtime" }
deno_semver = "=0.5.14"
deno_npm = "=0.25.4"
deno_path_util = "=0.2.1"
deno_permissions = { version = "0.34.0", path = "./runtime/permissions" }
deno_runtime = { version = "0.183.0", path = "./runtime" }
deno_semver = "=0.5.16"
deno_terminal = "0.2.0"
napi_sym = { version = "0.101.0", path = "./cli/napi/sym" }
napi_sym = { version = "0.104.0", path = "./ext/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.165.0", path = "./ext/broadcast_channel" }
deno_cache = { version = "0.103.0", path = "./ext/cache" }
deno_canvas = { version = "0.40.0", path = "./ext/canvas" }
deno_console = { version = "0.171.0", path = "./ext/console" }
deno_cron = { version = "0.51.0", path = "./ext/cron" }
deno_crypto = { version = "0.185.0", path = "./ext/crypto" }
deno_fetch = { version = "0.195.0", path = "./ext/fetch" }
deno_ffi = { version = "0.158.0", path = "./ext/ffi" }
deno_fs = { version = "0.81.0", path = "./ext/fs" }
deno_http = { version = "0.169.0", path = "./ext/http" }
deno_io = { version = "0.81.0", path = "./ext/io" }
deno_kv = { version = "0.79.0", path = "./ext/kv" }
deno_napi = { version = "0.102.0", path = "./ext/napi" }
deno_net = { version = "0.163.0", path = "./ext/net" }
deno_node = { version = "0.108.0", path = "./ext/node" }
deno_tls = { version = "0.158.0", path = "./ext/tls" }
deno_url = { version = "0.171.0", path = "./ext/url" }
deno_web = { version = "0.202.0", path = "./ext/web" }
deno_webgpu = { version = "0.138.0", path = "./ext/webgpu" }
deno_webidl = { version = "0.171.0", path = "./ext/webidl" }
deno_websocket = { version = "0.176.0", path = "./ext/websocket" }
deno_webstorage = { version = "0.166.0", path = "./ext/webstorage" }
deno_broadcast_channel = { version = "0.168.0", path = "./ext/broadcast_channel" }
deno_cache = { version = "0.106.0", path = "./ext/cache" }
deno_canvas = { version = "0.43.0", path = "./ext/canvas" }
deno_console = { version = "0.174.0", path = "./ext/console" }
deno_cron = { version = "0.54.0", path = "./ext/cron" }
deno_crypto = { version = "0.188.0", path = "./ext/crypto" }
deno_fetch = { version = "0.198.0", path = "./ext/fetch" }
deno_ffi = { version = "0.161.0", path = "./ext/ffi" }
deno_fs = { version = "0.84.0", path = "./ext/fs" }
deno_http = { version = "0.172.0", path = "./ext/http" }
deno_io = { version = "0.84.0", path = "./ext/io" }
deno_kv = { version = "0.82.0", path = "./ext/kv" }
deno_napi = { version = "0.105.0", path = "./ext/napi" }
deno_net = { version = "0.166.0", path = "./ext/net" }
deno_node = { version = "0.111.0", path = "./ext/node" }
deno_tls = { version = "0.161.0", path = "./ext/tls" }
deno_url = { version = "0.174.0", path = "./ext/url" }
deno_web = { version = "0.205.0", path = "./ext/web" }
deno_webgpu = { version = "0.141.0", path = "./ext/webgpu" }
deno_webidl = { version = "0.174.0", path = "./ext/webidl" }
deno_websocket = { version = "0.179.0", path = "./ext/websocket" }
deno_webstorage = { version = "0.169.0", path = "./ext/webstorage" }
# resolvers
deno_resolver = { version = "0.3.0", path = "./resolvers/deno" }
node_resolver = { version = "0.10.0", path = "./resolvers/node" }
deno_resolver = { version = "0.6.0", path = "./resolvers/deno" }
node_resolver = { version = "0.13.0", path = "./resolvers/node" }
aes = "=0.8.3"
anyhow = "1.0.57"
@ -106,6 +106,7 @@ 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"
@ -136,7 +137,7 @@ 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.2", features = ["serde"] }
lazy-regex = "3"
libc = "0.2.126"
libz-sys = { version = "1.1.20", default-features = false }
@ -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,7 +221,7 @@ quote = "1"
syn = { version = "2", features = ["full", "extra-traits"] }
# unix
nix = "=0.26.2"
nix = "=0.27.1"
# windows deps
junction = "=0.2.0"

View file

@ -6,6 +6,115 @@ https://github.com/denoland/deno/releases
We also have one-line install commands at:
https://github.com/denoland/deno_install
### 2.0.3 / 2024.10.25
- feat(lsp): interactive inlay hints (#26382)
- fix: support node-api in denort (#26389)
- fix(check): support `--frozen` on deno check (#26479)
- fix(cli): increase size of blocking task threadpool on windows (#26465)
- fix(config): schemas for lint rule and tag autocompletion (#26515)
- fix(ext/console): ignore casing for named colors in css parsing (#26466)
- fix(ext/ffi): return u64/i64 as bigints from nonblocking ffi calls (#26486)
- fix(ext/node): cancel pending ipc writes on channel close (#26504)
- fix(ext/node): map `ERROR_INVALID_NAME` to `ENOENT` on windows (#26475)
- fix(ext/node): only set our end of child process pipe to nonblocking mode
(#26495)
- fix(ext/node): properly map reparse point error in readlink (#26375)
- fix(ext/node): refactor http.ServerResponse into function class (#26210)
- fix(ext/node): stub HTTPParser internal binding (#26401)
- fix(ext/node): use primordials in `ext/node/polyfills/https.ts` (#26323)
- fix(fmt): --ext flag requires to pass files (#26525)
- fix(fmt): upgrade formatters (#26469)
- fix(help): missing package specifier (#26380)
- fix(info): resolve workspace member mappings (#26350)
- fix(install): better json editing (#26450)
- fix(install): cache all exports of JSR packages listed in `deno.json` (#26501)
- fix(install): cache type only module deps in `deno install` (#26497)
- fix(install): don't cache json exports of JSR packages (for now) (#26530)
- fix(install): update lockfile when using package.json (#26458)
- fix(lsp): import-map-remap quickfix for type imports (#26454)
- fix(node/util): support array formats in `styleText` (#26507)
- fix(node:tls): set TLSSocket.alpnProtocol for client connections (#26476)
- fix(npm): ensure scoped package name is encoded in URLs (#26390)
- fix(npm): support version ranges with && or comma (#26453)
- fix: `.npmrc` settings not being passed to install/add command (#26473)
- fix: add 'fmt-component' to unstable features in schema file (#26526)
- fix: share inotify fd across watchers (#26200)
- fix: unpin tokio version (#26457)
- perf(compile): pass module source data from binary directly to v8 (#26494)
- perf: avoid multiple calls to runMicrotask (#26378)
### 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

View file

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

View file

@ -2,7 +2,7 @@
[package]
name = "deno"
version = "2.0.0"
version = "2.0.3"
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.37.1", 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.152.0", features = ["html"] }
deno_graph = { version = "=0.83.3" }
deno_doc = { version = "0.154.0", default-features = false, features = ["rust", "html", "syntect"] }
deno_graph = { version = "=0.83.4" }
deno_lint = { version = "=0.67.0", features = ["docs"] }
deno_lockfile.workspace = true
deno_npm.workspace = true
@ -79,9 +84,7 @@ deno_runtime = { workspace = true, features = ["include_js_files_for_snapshottin
deno_semver.workspace = true
deno_task_shell = "=0.18.1"
deno_terminal.workspace = true
eszip = "=0.79.1"
libsui = "0.4.0"
napi_sym.workspace = true
node_resolver.workspace = true
anstream = "0.6.14"
@ -94,14 +97,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 +121,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 +166,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,14 +173,12 @@ 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,7 +575,8 @@ 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
}
@ -1177,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
@ -1342,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),
@ -1528,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(
@ -1630,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 {
@ -1658,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,
)
@ -1675,6 +1676,7 @@ You can add multiple dependencies at once:
.action(ArgAction::Append),
)
.arg(add_dev_arg())
.arg(allow_scripts_arg())
})
}
@ -1717,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")
@ -1854,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())
}
)
@ -1881,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")
@ -2202,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(
@ -2271,7 +2274,7 @@ Ignore formatting a file by adding an ignore comment at the top of the file:
"sass", "less", "html", "svelte", "vue", "astro", "yml", "yaml",
"ipynb",
])
.help_heading(FMT_HEADING),
.help_heading(FMT_HEADING).requires("files"),
)
.arg(
Arg::new("ignore")
@ -2468,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</>
@ -2501,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(
@ -2767,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")
@ -2787,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")
@ -2799,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))
@ -2855,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")
@ -2929,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")
@ -3642,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 {
@ -3654,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())
@ -4135,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")
@ -4159,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")
@ -4180,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(
@ -4235,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(
@ -4262,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
@ -4330,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;
@ -4352,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();
@ -4527,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();
@ -4620,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 {
@ -4846,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")
@ -4879,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");
@ -4920,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.
@ -5015,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
@ -5380,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)?;
@ -5391,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);
@ -5662,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");
@ -6742,6 +6802,32 @@ mod tests {
..Flags::default()
}
);
let r = flags_from_vec(svec!["deno", "fmt", "--ext", "html"]);
assert!(r.is_err());
let r = flags_from_vec(svec!["deno", "fmt", "--ext", "html", "./**"]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Fmt(FmtFlags {
check: false,
files: FileFlags {
include: vec!["./**".to_string()],
ignore: vec![],
},
use_tabs: None,
line_width: None,
indent_width: None,
single_quote: None,
prose_wrap: None,
no_semicolons: None,
unstable_component: false,
watch: Default::default(),
}),
ext: Some("html".to_string()),
..Flags::default()
}
);
}
#[test]
@ -7390,7 +7476,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 {
@ -7438,7 +7524,6 @@ mod tests {
allow_write: Some(vec![]),
..Default::default()
},
type_check_mode: TypeCheckMode::None,
..Flags::default()
}
);
@ -7460,7 +7545,6 @@ mod tests {
eval: None,
is_default_command: false,
}),
type_check_mode: TypeCheckMode::None,
..Flags::default()
}
);
@ -8862,8 +8946,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

@ -578,6 +578,7 @@ fn discover_npmrc(
let resolved = npmrc
.as_resolved(npm_registry_url())
.context("Failed to resolve .npmrc options")?;
log::debug!(".npmrc found at: '{}'", path.display());
Ok(Arc::new(resolved))
}
@ -963,6 +964,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(),
}
}
@ -1576,6 +1580,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)
@ -1620,21 +1629,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

View file

@ -1,167 +0,0 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::collections::HashMap;
use std::net::TcpStream;
use std::path::Path;
use std::process::Command;
use std::sync::atomic::AtomicU16;
use std::sync::atomic::Ordering;
use std::time::Duration;
use std::time::Instant;
use super::Result;
pub use test_util::parse_wrk_output;
pub use test_util::WrkOutput as HttpBenchmarkResult;
// Some of the benchmarks in this file have been renamed. In case the history
// somehow gets messed up:
// "node_http" was once called "node"
// "deno_tcp" was once called "deno"
// "deno_http" was once called "deno_net_http"
const DURATION: &str = "10s";
pub fn benchmark(
target_path: &Path,
) -> Result<HashMap<String, HttpBenchmarkResult>> {
let deno_exe = test_util::deno_exe_path();
let deno_exe = deno_exe.to_string();
let hyper_hello_exe = target_path.join("test_server");
let hyper_hello_exe = hyper_hello_exe.to_str().unwrap();
let mut res = HashMap::new();
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let http_dir = manifest_dir.join("bench").join("http");
for entry in std::fs::read_dir(&http_dir)? {
let entry = entry?;
let pathbuf = entry.path();
let path = pathbuf.to_str().unwrap();
if path.ends_with(".lua") {
continue;
}
let file_stem = pathbuf.file_stem().unwrap().to_str().unwrap();
let lua_script = http_dir.join(format!("{file_stem}.lua"));
let mut maybe_lua = None;
if lua_script.exists() {
maybe_lua = Some(lua_script.to_str().unwrap());
}
let port = get_port();
// deno run -A --unstable-net <path> <addr>
res.insert(
file_stem.to_string(),
run(
&[
deno_exe.as_str(),
"run",
"--allow-all",
"--unstable-net",
"--enable-testing-features-do-not-use",
path,
&server_addr(port),
],
port,
None,
None,
maybe_lua,
)?,
);
}
res.insert("hyper".to_string(), hyper_http(hyper_hello_exe)?);
Ok(res)
}
fn run(
server_cmd: &[&str],
port: u16,
env: Option<Vec<(String, String)>>,
origin_cmd: Option<&[&str]>,
lua_script: Option<&str>,
) -> Result<HttpBenchmarkResult> {
// Wait for port 4544 to become available.
// TODO Need to use SO_REUSEPORT with tokio::net::TcpListener.
std::thread::sleep(Duration::from_secs(5));
let mut origin = None;
if let Some(cmd) = origin_cmd {
let mut com = Command::new(cmd[0]);
com.args(&cmd[1..]);
if let Some(env) = env.clone() {
com.envs(env);
}
origin = Some(com.spawn()?);
};
println!("{}", server_cmd.join(" "));
let mut server = {
let mut com = Command::new(server_cmd[0]);
com.args(&server_cmd[1..]);
if let Some(env) = env {
com.envs(env);
}
com.spawn()?
};
// Wait for server to wake up.
let now = Instant::now();
let addr = format!("127.0.0.1:{port}");
while now.elapsed().as_secs() < 30 {
if TcpStream::connect(&addr).is_ok() {
break;
}
std::thread::sleep(Duration::from_millis(10));
}
TcpStream::connect(&addr).expect("Failed to connect to server in time");
println!("Server took {} ms to start", now.elapsed().as_millis());
let wrk = test_util::prebuilt_tool_path("wrk");
assert!(wrk.is_file());
let addr = format!("http://{addr}/");
let wrk = wrk.to_string();
let mut wrk_cmd = vec![wrk.as_str(), "-d", DURATION, "--latency", &addr];
if let Some(lua_script) = lua_script {
wrk_cmd.push("-s");
wrk_cmd.push(lua_script);
}
println!("{}", wrk_cmd.join(" "));
let output = test_util::run_collect(&wrk_cmd, None, None, None, true).0;
std::thread::sleep(Duration::from_secs(1)); // wait to capture failure. TODO racy.
println!("{output}");
assert!(
server.try_wait()?.map(|s| s.success()).unwrap_or(true),
"server ended with error"
);
server.kill()?;
if let Some(mut origin) = origin {
origin.kill()?;
}
Ok(parse_wrk_output(&output))
}
static NEXT_PORT: AtomicU16 = AtomicU16::new(4544);
pub(crate) fn get_port() -> u16 {
let p = NEXT_PORT.load(Ordering::SeqCst);
NEXT_PORT.store(p.wrapping_add(1), Ordering::SeqCst);
p
}
fn server_addr(port: u16) -> String {
format!("0.0.0.0:{port}")
}
fn hyper_http(exe: &str) -> Result<HttpBenchmarkResult> {
let port = get_port();
println!("http_benchmark testing RUST hyper");
run(&[exe, &port.to_string()], port, None, None, None)
}

View file

@ -1,10 +0,0 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { Hono } from "https://deno.land/x/hono@v2.0.9/mod.ts";
const addr = Deno.args[0] || "127.0.0.1:4500";
const [hostname, port] = addr.split(":");
const app = new Hono();
app.get("/", (c) => c.text("Hello, World!"));
Deno.serve({ port: Number(port), hostname }, app.fetch);

View file

@ -1,14 +0,0 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
const addr = Deno.args[0] || "127.0.0.1:4500";
const [hostname, port] = addr.split(":");
const { serve } = Deno;
const path = new URL("../testdata/128k.bin", import.meta.url).pathname;
function handler() {
const file = Deno.openSync(path);
return new Response(file.readable);
}
serve({ hostname, port: Number(port) }, handler);

View file

@ -1,5 +0,0 @@
wrk.headers["foo"] = "bar"
wrk.headers["User-Agent"] = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
wrk.headers["Viewport-Width"] = "1920"
wrk.headers["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
wrk.headers["Accept-Language"] = "en,la;q=0.9"

View file

@ -1,11 +0,0 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
const addr = Deno.args[0] ?? "127.0.0.1:4500";
const [hostname, port] = addr.split(":");
const { serve } = Deno;
function handler() {
return new Response("Hello World");
}
serve({ hostname, port: Number(port), reusePort: true }, handler);

View file

@ -1,5 +0,0 @@
wrk.method = "POST"
wrk.headers["Content-Type"] = "application/octet-stream"
file = io.open("./cli/bench/testdata/128k.bin", "rb")
wrk.body = file:read("*a")

View file

@ -1,3 +0,0 @@
wrk.method = "POST"
wrk.headers["Content-Type"] = "application/json"
wrk.body = '{"hello":"deno"}'

View file

@ -1,25 +0,0 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { renderToReadableStream } from "https://esm.run/react-dom/server";
import * as React from "https://esm.run/react";
const { serve } = Deno;
const addr = Deno.args[0] || "127.0.0.1:4500";
const [hostname, port] = addr.split(":");
const App = () => (
<html>
<body>
<h1>Hello World</h1>
</body>
</html>
);
const headers = {
headers: {
"Content-Type": "text/html",
},
};
serve({ hostname, port: Number(port) }, async () => {
return new Response(await renderToReadableStream(<App />), headers);
});

View file

@ -1,34 +0,0 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// Used for benchmarking Deno's networking.
// TODO(bartlomieju): Replace this with a real HTTP server once
// https://github.com/denoland/deno/issues/726 is completed.
// Note: this is a keep-alive server.
// deno-lint-ignore-file no-console
const addr = Deno.args[0] || "127.0.0.1:4500";
const [hostname, port] = addr.split(":");
const listener = Deno.listen({ hostname, port: Number(port) });
const response = new TextEncoder().encode(
"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n",
);
async function handle(conn: Deno.Conn): Promise<void> {
const buffer = new Uint8Array(1024);
try {
while (true) {
await conn.read(buffer);
await conn.write(response);
}
} catch (e) {
if (
!(e instanceof Deno.errors.BrokenPipe) &&
!(e instanceof Deno.errors.ConnectionReset)
) {
throw e;
}
}
conn.close();
}
console.log("Listening on", addr);
for await (const conn of listener) {
handle(conn);
}

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

@ -17,7 +17,6 @@ use std::process::Stdio;
use std::time::SystemTime;
use test_util::PathRef;
mod http;
mod lsp;
fn read_json(filename: &Path) -> Result<Value> {
@ -345,9 +344,11 @@ struct BenchResult {
binary_size: HashMap<String, i64>,
bundle_size: HashMap<String, i64>,
cargo_deps: usize,
// TODO(bartlomieju): remove
max_latency: HashMap<String, f64>,
max_memory: HashMap<String, i64>,
lsp_exec_time: HashMap<String, i64>,
// TODO(bartlomieju): remove
req_per_sec: HashMap<String, i64>,
syscall_count: HashMap<String, i64>,
thread_count: HashMap<String, i64>,
@ -362,7 +363,6 @@ async fn main() -> Result<()> {
"binary_size",
"cargo_deps",
"lsp",
"http",
"strace",
"mem_usage",
];
@ -427,21 +427,6 @@ async fn main() -> Result<()> {
new_data.lsp_exec_time = lsp_exec_times;
}
if benchmarks.contains(&"http") && cfg!(not(target_os = "windows")) {
let stats = http::benchmark(target_dir.as_path())?;
let req_per_sec = stats
.iter()
.map(|(name, result)| (name.clone(), result.requests as i64))
.collect();
new_data.req_per_sec = req_per_sec;
let max_latency = stats
.iter()
.map(|(name, result)| (name.clone(), result.latency))
.collect();
new_data.max_latency = max_latency;
}
if cfg!(target_os = "linux") && benchmarks.contains(&"strace") {
use std::io::Read;

View file

@ -365,6 +365,9 @@ fn main() {
return;
}
deno_napi::print_linker_flags("deno");
deno_napi::print_linker_flags("denort");
// Host snapshots won't work when cross compiling.
let target = env::var("TARGET").unwrap();
let host = env::var("HOST").unwrap();
@ -374,58 +377,6 @@ fn main() {
panic!("Cross compiling with snapshot is not supported.");
}
let symbols_file_name = match env::consts::OS {
"android" | "freebsd" | "openbsd" => {
"generated_symbol_exports_list_linux.def".to_string()
}
os => format!("generated_symbol_exports_list_{}.def", os),
};
let symbols_path = std::path::Path::new("napi")
.join(symbols_file_name)
.canonicalize()
.expect(
"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:{}",
symbols_path.display()
);
#[cfg(target_os = "macos")]
println!(
"cargo:rustc-link-arg-bin=deno=-Wl,-exported_symbols_list,{}",
symbols_path.display()
);
#[cfg(target_os = "linux")]
{
// If a custom compiler is set, the glibc version is not reliable.
// Here, we assume that if a custom compiler is used, that it will be modern enough to support a dynamic symbol list.
if env::var("CC").is_err()
&& glibc_version::get_version()
.map(|ver| ver.major <= 2 && ver.minor < 35)
.unwrap_or(false)
{
println!("cargo:warning=Compiling with all symbols exported, this will result in a larger binary. Please use glibc 2.35 or later for an optimised build.");
println!("cargo:rustc-link-arg-bin=deno=-rdynamic");
} else {
println!(
"cargo:rustc-link-arg-bin=deno=-Wl,--export-dynamic-symbol-list={}",
symbols_path.display()
);
}
}
#[cfg(target_os = "android")]
println!(
"cargo:rustc-link-arg-bin=deno=-Wl,--export-dynamic-symbol-list={}",
symbols_path.display()
);
// To debug snapshot issues uncomment:
// op_fetch_asset::trace_serializer();

25
cli/cache/emit.rs vendored
View file

@ -39,7 +39,7 @@ impl EmitCache {
&self,
specifier: &ModuleSpecifier,
expected_source_hash: u64,
) -> Option<Vec<u8>> {
) -> Option<String> {
let emit_filename = self.get_emit_filename(specifier)?;
let bytes = self.disk_cache.get(&emit_filename).ok()?;
self
@ -100,7 +100,7 @@ impl EmitFileSerializer {
&self,
mut bytes: Vec<u8>,
expected_source_hash: u64,
) -> Option<Vec<u8>> {
) -> Option<String> {
let last_newline_index = bytes.iter().rposition(|&b| b == b'\n')?;
let (content, last_line) = bytes.split_at(last_newline_index);
let hashes = last_line.strip_prefix(LAST_LINE_PREFIX.as_bytes())?;
@ -120,7 +120,7 @@ impl EmitFileSerializer {
// everything looks good, truncate and return it
bytes.truncate(content.len());
Some(bytes)
String::from_utf8(bytes).ok()
}
pub fn serialize(&self, code: &[u8], source_hash: u64) -> Vec<u8> {
@ -170,8 +170,6 @@ mod test {
},
emit_failed_flag: Default::default(),
};
let to_string =
|bytes: Vec<u8>| -> String { String::from_utf8(bytes).unwrap() };
let specifier1 =
ModuleSpecifier::from_file_path(temp_dir.path().join("file1.ts"))
@ -188,13 +186,10 @@ mod test {
assert_eq!(cache.get_emit_code(&specifier1, 5), None);
// providing the correct source hash
assert_eq!(
cache.get_emit_code(&specifier1, 10).map(to_string),
cache.get_emit_code(&specifier1, 10),
Some(emit_code1.clone()),
);
assert_eq!(
cache.get_emit_code(&specifier2, 2).map(to_string),
Some(emit_code2)
);
assert_eq!(cache.get_emit_code(&specifier2, 2), Some(emit_code2));
// try changing the cli version (should not load previous ones)
let cache = EmitCache {
@ -215,18 +210,12 @@ mod test {
},
emit_failed_flag: Default::default(),
};
assert_eq!(
cache.get_emit_code(&specifier1, 5).map(to_string),
Some(emit_code1)
);
assert_eq!(cache.get_emit_code(&specifier1, 5), Some(emit_code1));
// adding when already exists should not cause issue
let emit_code3 = "asdf".to_string();
cache.set_emit_code(&specifier1, 20, emit_code3.as_bytes());
assert_eq!(cache.get_emit_code(&specifier1, 5), None);
assert_eq!(
cache.get_emit_code(&specifier1, 20).map(to_string),
Some(emit_code3)
);
assert_eq!(cache.get_emit_code(&specifier1, 20), Some(emit_code3));
}
}

58
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
@ -325,6 +378,7 @@ impl Loader for FetchCacher {
} else {
FetchPermissionsOptionRef::DynamicContainer(&permissions)
},
maybe_auth: None,
maybe_accept: None,
maybe_cache_setting: maybe_cache_setting.as_ref(),
},

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

@ -13,7 +13,6 @@ use deno_core::error::AnyError;
use deno_core::futures::stream::FuturesUnordered;
use deno_core::futures::FutureExt;
use deno_core::futures::StreamExt;
use deno_core::ModuleCodeBytes;
use deno_core::ModuleSpecifier;
use deno_graph::MediaType;
use deno_graph::Module;
@ -60,6 +59,7 @@ impl Emitter {
continue;
};
// todo(https://github.com/denoland/deno_media_type/pull/12): use is_emittable()
let is_emittable = matches!(
module.media_type,
MediaType::TypeScript
@ -93,7 +93,7 @@ impl Emitter {
&self,
specifier: &ModuleSpecifier,
source: &str,
) -> Option<Vec<u8>> {
) -> Option<String> {
let source_hash = self.get_source_hash(source);
self.emit_cache.get_emit_code(specifier, source_hash)
}
@ -103,7 +103,7 @@ impl Emitter {
specifier: &ModuleSpecifier,
media_type: MediaType,
source: &Arc<str>,
) -> Result<ModuleCodeBytes, AnyError> {
) -> Result<String, AnyError> {
// Note: keep this in sync with the sync version below
let helper = EmitParsedSourceHelper(self);
match helper.pre_emit_parsed_source(specifier, source) {
@ -112,7 +112,7 @@ impl Emitter {
let parsed_source_cache = self.parsed_source_cache.clone();
let transpile_and_emit_options =
self.transpile_and_emit_options.clone();
let transpile_result = deno_core::unsync::spawn_blocking({
let transpiled_source = deno_core::unsync::spawn_blocking({
let specifier = specifier.clone();
let source = source.clone();
move || -> Result<_, AnyError> {
@ -128,11 +128,12 @@ impl Emitter {
})
.await
.unwrap()?;
Ok(helper.post_emit_parsed_source(
helper.post_emit_parsed_source(
specifier,
transpile_result,
&transpiled_source,
source_hash,
))
);
Ok(transpiled_source)
}
}
}
@ -142,13 +143,13 @@ impl Emitter {
specifier: &ModuleSpecifier,
media_type: MediaType,
source: &Arc<str>,
) -> Result<ModuleCodeBytes, AnyError> {
) -> Result<String, AnyError> {
// Note: keep this in sync with the async version above
let helper = EmitParsedSourceHelper(self);
match helper.pre_emit_parsed_source(specifier, source) {
PreEmitResult::Cached(emitted_text) => Ok(emitted_text),
PreEmitResult::NotCached { source_hash } => {
let transpile_result = EmitParsedSourceHelper::transpile(
let transpiled_source = EmitParsedSourceHelper::transpile(
&self.parsed_source_cache,
specifier,
source.clone(),
@ -156,11 +157,12 @@ impl Emitter {
&self.transpile_and_emit_options.0,
&self.transpile_and_emit_options.1,
)?;
Ok(helper.post_emit_parsed_source(
helper.post_emit_parsed_source(
specifier,
transpile_result,
&transpiled_source,
source_hash,
))
);
Ok(transpiled_source)
}
}
}
@ -226,7 +228,7 @@ impl Emitter {
}
enum PreEmitResult {
Cached(ModuleCodeBytes),
Cached(String),
NotCached { source_hash: u64 },
}
@ -244,7 +246,7 @@ impl<'a> EmitParsedSourceHelper<'a> {
if let Some(emit_code) =
self.0.emit_cache.get_emit_code(specifier, source_hash)
{
PreEmitResult::Cached(emit_code.into_boxed_slice().into())
PreEmitResult::Cached(emit_code)
} else {
PreEmitResult::NotCached { source_hash }
}
@ -257,21 +259,14 @@ impl<'a> EmitParsedSourceHelper<'a> {
media_type: MediaType,
transpile_options: &deno_ast::TranspileOptions,
emit_options: &deno_ast::EmitOptions,
) -> Result<TranspileResult, AnyError> {
) -> Result<String, AnyError> {
// nothing else needs the parsed source at this point, so remove from
// the cache in order to not transpile owned
let parsed_source = parsed_source_cache
.remove_or_parse_module(specifier, source, media_type)?;
ensure_no_import_assertion(&parsed_source)?;
Ok(parsed_source.transpile(transpile_options, emit_options)?)
}
pub fn post_emit_parsed_source(
&self,
specifier: &ModuleSpecifier,
transpile_result: TranspileResult,
source_hash: u64,
) -> ModuleCodeBytes {
let transpile_result =
parsed_source.transpile(transpile_options, emit_options)?;
let transpiled_source = match transpile_result {
TranspileResult::Owned(source) => source,
TranspileResult::Cloned(source) => {
@ -280,12 +275,21 @@ impl<'a> EmitParsedSourceHelper<'a> {
}
};
debug_assert!(transpiled_source.source_map.is_none());
let text = String::from_utf8(transpiled_source.source)?;
Ok(text)
}
pub fn post_emit_parsed_source(
&self,
specifier: &ModuleSpecifier,
transpiled_source: &str,
source_hash: u64,
) {
self.0.emit_cache.set_emit_code(
specifier,
source_hash,
&transpiled_source.source,
transpiled_source.as_bytes(),
);
transpiled_source.source.into_boxed_slice().into()
}
}

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(),
@ -751,6 +762,7 @@ impl CliFactory {
let cli_options = self.cli_options()?;
Ok(DenoCompileBinaryWriter::new(
self.deno_dir()?,
self.emitter()?,
self.file_fetcher()?,
self.http_client_provider(),
self.npm_resolver().await?.as_ref(),
@ -792,6 +804,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 +909,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

@ -24,6 +24,7 @@ use deno_graph::source::LoaderChecksum;
use deno_path_util::url_to_file_path;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_web::BlobStore;
use http::header;
use log::debug;
use std::borrow::Cow;
use std::collections::HashMap;
@ -181,6 +182,7 @@ pub enum FetchPermissionsOptionRef<'a> {
pub struct FetchOptions<'a> {
pub specifier: &'a ModuleSpecifier,
pub permissions: FetchPermissionsOptionRef<'a>,
pub maybe_auth: Option<(header::HeaderName, header::HeaderValue)>,
pub maybe_accept: Option<&'a str>,
pub maybe_cache_setting: Option<&'a CacheSetting>,
}
@ -333,7 +335,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())]);
@ -350,6 +352,7 @@ impl FileFetcher {
maybe_accept: Option<&str>,
cache_setting: &CacheSetting,
maybe_checksum: Option<&LoaderChecksum>,
maybe_auth: Option<(header::HeaderName, header::HeaderValue)>,
) -> Result<FileOrRedirect, AnyError> {
debug!(
"FileFetcher::fetch_remote_no_follow - specifier: {}",
@ -442,6 +445,7 @@ impl FileFetcher {
.as_ref()
.map(|(_, etag)| etag.clone()),
maybe_auth_token: maybe_auth_token.clone(),
maybe_auth: maybe_auth.clone(),
maybe_progress_guard: maybe_progress_guard.as_ref(),
})
.await?
@ -538,7 +542,18 @@ impl FileFetcher {
specifier: &ModuleSpecifier,
) -> Result<File, AnyError> {
self
.fetch_inner(specifier, FetchPermissionsOptionRef::AllowAll)
.fetch_inner(specifier, None, FetchPermissionsOptionRef::AllowAll)
.await
}
#[inline(always)]
pub async fn fetch_bypass_permissions_with_maybe_auth(
&self,
specifier: &ModuleSpecifier,
maybe_auth: Option<(header::HeaderName, header::HeaderValue)>,
) -> Result<File, AnyError> {
self
.fetch_inner(specifier, maybe_auth, FetchPermissionsOptionRef::AllowAll)
.await
}
@ -552,6 +567,7 @@ impl FileFetcher {
self
.fetch_inner(
specifier,
None,
FetchPermissionsOptionRef::StaticContainer(permissions),
)
.await
@ -560,12 +576,14 @@ impl FileFetcher {
async fn fetch_inner(
&self,
specifier: &ModuleSpecifier,
maybe_auth: Option<(header::HeaderName, header::HeaderValue)>,
permissions: FetchPermissionsOptionRef<'_>,
) -> Result<File, AnyError> {
self
.fetch_with_options(FetchOptions {
specifier,
permissions,
maybe_auth,
maybe_accept: None,
maybe_cache_setting: None,
})
@ -585,12 +603,14 @@ impl FileFetcher {
max_redirect: usize,
) -> Result<File, AnyError> {
let mut specifier = Cow::Borrowed(options.specifier);
let mut maybe_auth = options.maybe_auth.clone();
for _ in 0..=max_redirect {
match self
.fetch_no_follow_with_options(FetchNoFollowOptions {
fetch_options: FetchOptions {
specifier: &specifier,
permissions: options.permissions,
maybe_auth: maybe_auth.clone(),
maybe_accept: options.maybe_accept,
maybe_cache_setting: options.maybe_cache_setting,
},
@ -602,6 +622,10 @@ impl FileFetcher {
return Ok(file);
}
FileOrRedirect::Redirect(redirect_specifier) => {
// If we were redirected to another origin, don't send the auth header anymore.
if redirect_specifier.origin() != specifier.origin() {
maybe_auth = None;
}
specifier = Cow::Owned(redirect_specifier);
}
}
@ -666,6 +690,7 @@ impl FileFetcher {
options.maybe_accept,
options.maybe_cache_setting.unwrap_or(&self.cache_setting),
maybe_checksum,
options.maybe_auth,
)
.await
}
@ -756,6 +781,7 @@ mod tests {
FetchOptions {
specifier,
permissions: FetchPermissionsOptionRef::AllowAll,
maybe_auth: None,
maybe_accept: None,
maybe_cache_setting: Some(&file_fetcher.cache_setting),
},
@ -1255,6 +1281,7 @@ mod tests {
FetchOptions {
specifier: &specifier,
permissions: FetchPermissionsOptionRef::AllowAll,
maybe_auth: None,
maybe_accept: None,
maybe_cache_setting: Some(&file_fetcher.cache_setting),
},
@ -1268,6 +1295,7 @@ mod tests {
FetchOptions {
specifier: &specifier,
permissions: FetchPermissionsOptionRef::AllowAll,
maybe_auth: None,
maybe_accept: None,
maybe_cache_setting: Some(&file_fetcher.cache_setting),
},

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

@ -19,6 +19,7 @@ use deno_runtime::deno_fetch;
use deno_runtime::deno_fetch::create_http_client;
use deno_runtime::deno_fetch::CreateHttpClientOptions;
use deno_runtime::deno_tls::RootCertStoreProvider;
use http::header;
use http::header::HeaderName;
use http::header::HeaderValue;
use http::header::ACCEPT;
@ -204,6 +205,7 @@ pub struct FetchOnceArgs<'a> {
pub maybe_accept: Option<String>,
pub maybe_etag: Option<String>,
pub maybe_auth_token: Option<AuthToken>,
pub maybe_auth: Option<(header::HeaderName, header::HeaderValue)>,
pub maybe_progress_guard: Option<&'a UpdateGuard>,
}
@ -382,6 +384,8 @@ impl HttpClient {
request
.headers_mut()
.insert(AUTHORIZATION, authorization_val);
} else if let Some((header, value)) = args.maybe_auth {
request.headers_mut().insert(header, value);
}
if let Some(accept) = args.maybe_accept {
let accepts_val = HeaderValue::from_str(&accept)?;
@ -470,15 +474,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(
@ -784,6 +796,7 @@ mod test {
maybe_etag: None,
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;
if let Ok(FetchOnceResult::Code(body, headers)) = result {
@ -810,6 +823,7 @@ mod test {
maybe_etag: None,
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;
if let Ok(FetchOnceResult::Code(body, headers)) = result {
@ -837,6 +851,7 @@ mod test {
maybe_etag: None,
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;
if let Ok(FetchOnceResult::Code(body, headers)) = result {
@ -858,6 +873,7 @@ mod test {
maybe_etag: Some("33a64df551425fcc55e".to_string()),
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;
assert_eq!(res.unwrap(), FetchOnceResult::NotModified);
@ -877,6 +893,7 @@ mod test {
maybe_etag: None,
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;
if let Ok(FetchOnceResult::Code(body, headers)) = result {
@ -906,6 +923,7 @@ mod test {
maybe_etag: None,
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;
if let Ok(FetchOnceResult::Code(body, _)) = result {
@ -931,6 +949,7 @@ mod test {
maybe_etag: None,
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;
if let Ok(FetchOnceResult::Redirect(url, _)) = result {
@ -966,6 +985,7 @@ mod test {
maybe_etag: None,
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;
if let Ok(FetchOnceResult::Code(body, headers)) = result {
@ -1013,6 +1033,7 @@ mod test {
maybe_etag: None,
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;
@ -1075,6 +1096,7 @@ mod test {
maybe_etag: None,
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;
@ -1128,6 +1150,7 @@ mod test {
maybe_etag: None,
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;
if let Ok(FetchOnceResult::Code(body, headers)) = result {
@ -1169,6 +1192,7 @@ mod test {
maybe_etag: None,
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;
if let Ok(FetchOnceResult::Code(body, headers)) = result {
@ -1191,6 +1215,7 @@ mod test {
maybe_etag: Some("33a64df551425fcc55e".to_string()),
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;
assert_eq!(res.unwrap(), FetchOnceResult::NotModified);
@ -1225,6 +1250,7 @@ mod test {
maybe_etag: None,
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;
if let Ok(FetchOnceResult::Code(body, headers)) = result {
@ -1254,6 +1280,7 @@ mod test {
maybe_etag: None,
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;
assert!(result.is_err());
@ -1275,6 +1302,7 @@ mod test {
maybe_etag: None,
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;
@ -1298,6 +1326,7 @@ mod test {
maybe_etag: None,
maybe_auth_token: None,
maybe_progress_guard: None,
maybe_auth: None,
})
.await;

View file

@ -18,7 +18,6 @@ use deno_lint::diagnostic::LintDiagnosticRange;
use deno_ast::SourceRange;
use deno_ast::SourceRangedForSpanned;
use deno_ast::SourceTextInfo;
use deno_core::anyhow::anyhow;
use deno_core::error::custom_error;
use deno_core::error::AnyError;
use deno_core::serde::Deserialize;
@ -40,6 +39,7 @@ use import_map::ImportMap;
use node_resolver::NpmResolver;
use once_cell::sync::Lazy;
use regex::Regex;
use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::HashSet;
@ -598,68 +598,62 @@ pub fn fix_ts_import_changes(
/// Fix tsc import code actions so that the module specifier is correct for
/// resolution by Deno (includes the extension).
fn fix_ts_import_action(
fn fix_ts_import_action<'a>(
referrer: &ModuleSpecifier,
action: &tsc::CodeFixAction,
action: &'a tsc::CodeFixAction,
import_mapper: &TsResponseImportMapper,
) -> Result<Option<tsc::CodeFixAction>, AnyError> {
if matches!(
) -> Option<Cow<'a, tsc::CodeFixAction>> {
if !matches!(
action.fix_name.as_str(),
"import" | "fixMissingFunctionDeclaration"
) {
let change = action
return Some(Cow::Borrowed(action));
}
let specifier = (|| {
let text_change = action.changes.first()?.text_changes.first()?;
let captures = IMPORT_SPECIFIER_RE.captures(&text_change.new_text)?;
Some(captures.get(1)?.as_str())
})();
let Some(specifier) = specifier else {
return Some(Cow::Borrowed(action));
};
if let Some(new_specifier) =
import_mapper.check_unresolved_specifier(specifier, referrer)
{
let description = action.description.replace(specifier, &new_specifier);
let changes = action
.changes
.first()
.ok_or_else(|| anyhow!("Unexpected action changes."))?;
let text_change = change
.text_changes
.first()
.ok_or_else(|| anyhow!("Missing text change."))?;
if let Some(captures) = IMPORT_SPECIFIER_RE.captures(&text_change.new_text)
{
let specifier = captures
.get(1)
.ok_or_else(|| anyhow!("Missing capture."))?
.as_str();
if let Some(new_specifier) =
import_mapper.check_unresolved_specifier(specifier, referrer)
{
let description = action.description.replace(specifier, &new_specifier);
let changes = action
.changes
.iter()
.map(|c| {
let text_changes = c
.text_changes
.iter()
.map(|c| {
let text_changes = c
.text_changes
.iter()
.map(|tc| tsc::TextChange {
span: tc.span.clone(),
new_text: tc.new_text.replace(specifier, &new_specifier),
})
.collect();
tsc::FileTextChanges {
file_name: c.file_name.clone(),
text_changes,
is_new_file: c.is_new_file,
}
.map(|tc| tsc::TextChange {
span: tc.span.clone(),
new_text: tc.new_text.replace(specifier, &new_specifier),
})
.collect();
tsc::FileTextChanges {
file_name: c.file_name.clone(),
text_changes,
is_new_file: c.is_new_file,
}
})
.collect();
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);
}
}
Some(Cow::Owned(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) {
None
} else {
Some(Cow::Borrowed(action))
}
Ok(Some(action.clone()))
}
/// Determines if two TypeScript diagnostic codes are effectively equivalent.
@ -1004,8 +998,7 @@ impl CodeActionCollection {
specifier,
action,
&language_server.get_ts_response_import_mapper(specifier),
)?
else {
) else {
return Ok(());
};
let edit = ts_changes_to_edit(&action.changes, language_server)?;
@ -1027,7 +1020,7 @@ impl CodeActionCollection {
});
self
.actions
.push(CodeActionKind::Tsc(code_action, action.clone()));
.push(CodeActionKind::Tsc(code_action, action.as_ref().clone()));
if let Some(fix_id) = &action.fix_id {
if let Some(CodeActionKind::Tsc(existing_fix_all, existing_action)) =

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,6 +50,8 @@ 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;
@ -437,6 +439,8 @@ pub struct LanguagePreferences {
pub use_aliases_for_renames: bool,
#[serde(default)]
pub quote_style: QuoteStyle,
#[serde(default)]
pub prefer_type_only_auto_imports: bool,
}
impl Default for LanguagePreferences {
@ -447,6 +451,7 @@ impl Default for LanguagePreferences {
auto_import_file_exclude_patterns: vec![],
use_aliases_for_renames: true,
quote_style: Default::default(),
prefer_type_only_auto_imports: false,
}
}
}
@ -1716,14 +1721,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
@ -1747,6 +1752,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,
@ -2209,6 +2254,7 @@ mod tests {
auto_import_file_exclude_patterns: vec![],
use_aliases_for_renames: true,
quote_style: QuoteStyle::Auto,
prefer_type_only_auto_imports: false,
},
suggest: CompletionSettings {
complete_function_calls: false,
@ -2254,6 +2300,7 @@ mod tests {
auto_import_file_exclude_patterns: vec![],
use_aliases_for_renames: true,
quote_style: QuoteStyle::Auto,
prefer_type_only_auto_imports: false,
},
suggest: CompletionSettings {
complete_function_calls: false,

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

@ -963,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;
@ -3807,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

@ -4,6 +4,7 @@ use dashmap::DashMap;
use deno_core::anyhow::anyhow;
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_npm::npm_rc::NpmRc;
use deno_semver::package::PackageNv;
use deno_semver::Version;
use serde::Deserialize;
@ -25,7 +26,10 @@ pub struct CliNpmSearchApi {
impl CliNpmSearchApi {
pub fn new(file_fetcher: Arc<FileFetcher>) -> Self {
let resolver = NpmFetchResolver::new(file_fetcher.clone());
let resolver = NpmFetchResolver::new(
file_fetcher.clone(),
Arc::new(NpmRc::default().as_resolved(npm_registry_url()).unwrap()),
);
Self {
file_fetcher,
resolver,

View file

@ -482,6 +482,7 @@ impl ModuleRegistry {
.fetch_with_options(FetchOptions {
specifier: &specifier,
permissions: FetchPermissionsOptionRef::AllowAll,
maybe_auth: None,
maybe_accept: Some("application/vnd.deno.reg.v2+json, application/vnd.deno.reg.v1+json;q=0.9, application/json;q=0.8"),
maybe_cache_setting: None,
})

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,10 @@ 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>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prefer_type_only_auto_imports: Option<bool>,
}
impl UserPreferences {
@ -4909,6 +4971,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),
@ -5013,6 +5076,9 @@ impl UserPreferences {
} else {
Some(language_settings.preferences.quote_style)
},
prefer_type_only_auto_imports: Some(
language_settings.preferences.prefer_type_only_auto_imports,
),
..base_preferences
}
}
@ -6154,7 +6220,7 @@ mod tests {
let change = changes.text_changes.first().unwrap();
assert_eq!(
change.new_text,
"import type { someLongVariable } from './b.ts'\n"
"import { someLongVariable } from './b.ts'\n"
);
}

View file

@ -15,7 +15,6 @@ mod js;
mod jsr;
mod lsp;
mod module_loader;
mod napi;
mod node;
mod npm;
mod ops;
@ -47,8 +46,7 @@ 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;
@ -62,6 +60,10 @@ 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>;
@ -166,10 +168,10 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
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"));
}
@ -362,104 +364,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>()
{
@ -480,6 +390,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();
@ -500,7 +413,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

@ -88,11 +88,10 @@ fn main() {
let standalone = standalone::extract_standalone(Cow::Owned(args));
let future = async move {
match standalone {
Ok(Some(future)) => {
let (metadata, eszip) = future.await?;
util::logger::init(metadata.log_level);
load_env_vars(&metadata.env_vars_from_env_file);
let exit_code = standalone::run(eszip, metadata).await?;
Ok(Some(data)) => {
util::logger::init(data.metadata.log_level);
load_env_vars(&data.metadata.env_vars_from_env_file);
let exit_code = standalone::run(data).await?;
std::process::exit(exit_code);
}
Ok(None) => Ok(()),

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,26 @@ 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 {
code: ModuleSourceCode::Bytes(transpile_result),
Ok(Some(ModuleCodeStringSource {
// note: it's faster to provide a string if we know it's a string
code: ModuleSourceCode::String(transpile_result.into()),
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 +571,14 @@ 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 {
code: ModuleSourceCode::Bytes(transpile_result),
Ok(Some(ModuleCodeStringSource {
// note: it's faster to provide a string if we know it's a string
code: ModuleSourceCode::String(transpile_result.into()),
found_url: specifier.clone(),
media_type,
})
}))
}
Err(err) => Err(err),
None => Ok(None),
}
}
@ -586,8 +586,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 +598,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 +623,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 +637,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 +821,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,114 +0,0 @@
# napi
This directory contains source for Deno's Node-API implementation. It depends on
`napi_sym` and `deno_napi`.
Files are generally organized the same as in Node.js's implementation to ease in
ensuring compatibility.
## Adding a new function
Add the symbol name to
[`cli/napi_sym/symbol_exports.json`](../napi_sym/symbol_exports.json).
```diff
{
"symbols": [
...
"napi_get_undefined",
- "napi_get_null"
+ "napi_get_null",
+ "napi_get_boolean"
]
}
```
Determine where to place the implementation. `napi_get_boolean` is related to JS
values so we will place it in `js_native_api.rs`. If something is not clear,
just create a new file module.
See [`napi_sym`](../napi_sym/) for writing the implementation:
```rust
#[napi_sym::napi_sym]
pub fn napi_get_boolean(
env: *mut Env,
value: bool,
result: *mut napi_value,
) -> Result {
// ...
Ok(())
}
```
Update the generated symbol lists using the script:
```
deno run --allow-write tools/napi/generate_symbols_lists.js
```
Add a test in [`/tests/napi`](../../tests/napi/). You can also refer to Node.js
test suite for Node-API.
```js
// tests/napi/boolean_test.js
import { assertEquals, loadTestLibrary } from "./common.js";
const lib = loadTestLibrary();
Deno.test("napi get boolean", function () {
assertEquals(lib.test_get_boolean(true), true);
assertEquals(lib.test_get_boolean(false), false);
});
```
```rust
// tests/napi/src/boolean.rs
use napi_sys::Status::napi_ok;
use napi_sys::ValueType::napi_boolean;
use napi_sys::*;
extern "C" fn test_boolean(
env: napi_env,
info: napi_callback_info,
) -> napi_value {
let (args, argc, _) = crate::get_callback_info!(env, info, 1);
assert_eq!(argc, 1);
let mut ty = -1;
assert!(unsafe { napi_typeof(env, args[0], &mut ty) } == napi_ok);
assert_eq!(ty, napi_boolean);
// Use napi_get_boolean here...
value
}
pub fn init(env: napi_env, exports: napi_value) {
let properties = &[crate::new_property!(env, "test_boolean\0", test_boolean)];
unsafe {
napi_define_properties(env, exports, properties.len(), properties.as_ptr())
};
}
```
```diff
// tests/napi/src/lib.rs
+ mod boolean;
...
#[no_mangle]
unsafe extern "C" fn napi_register_module_v1(
env: napi_env,
exports: napi_value,
) -> napi_value {
...
+ boolean::init(env, exports);
exports
}
```
Run the test using `cargo test -p tests/napi`.

View file

@ -1,21 +0,0 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
#![allow(unused_mut)]
#![allow(non_camel_case_types)]
#![allow(clippy::undocumented_unsafe_blocks)]
//! Symbols to be exported are now defined in this JSON file.
//! The `#[napi_sym]` macro checks for missing entries and panics.
//!
//! `./tools/napi/generate_symbols_list.js` is used to generate the LINK `cli/exports.def` on Windows,
//! which is also checked into git.
//!
//! To add a new napi function:
//! 1. Place `#[napi_sym]` on top of your implementation.
//! 2. Add the function's identifier to this JSON list.
//! 3. Finally, run `tools/napi/generate_symbols_list.js` to update `cli/napi/generated_symbol_exports_list_*.def`.
pub mod js_native_api;
pub mod node_api;
pub mod util;
pub mod uv;

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

@ -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

@ -26,7 +26,7 @@ use crate::cache::CACHE_PERM;
use crate::util::fs::atomic_write_file_with_retries;
use crate::util::fs::hard_link_dir_recursive;
mod registry_info;
pub mod registry_info;
mod tarball;
mod tarball_extract;

View file

@ -84,7 +84,7 @@ impl RegistryInfoDownloader {
self.load_package_info_inner(name).await.with_context(|| {
format!(
"Error getting response at {} for package \"{}\"",
self.get_package_url(name),
get_package_url(&self.npmrc, name),
name
)
})
@ -190,7 +190,7 @@ impl RegistryInfoDownloader {
fn create_load_future(self: &Arc<Self>, name: &str) -> LoadFuture {
let downloader = self.clone();
let package_url = self.get_package_url(name);
let package_url = get_package_url(&self.npmrc, name);
let registry_config = self.npmrc.get_registry_config(name);
let maybe_auth_header =
match maybe_auth_header_for_npm_registry(registry_config) {
@ -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) => {
@ -236,25 +239,36 @@ impl RegistryInfoDownloader {
.map(|r| r.map_err(Arc::new))
.boxed_local()
}
fn get_package_url(&self, name: &str) -> Url {
let registry_url = self.npmrc.get_registry_url(name);
// list of all characters used in npm packages:
// !, ', (, ), *, -, ., /, [0-9], @, [A-Za-z], _, ~
const ASCII_SET: percent_encoding::AsciiSet =
percent_encoding::NON_ALPHANUMERIC
.remove(b'!')
.remove(b'\'')
.remove(b'(')
.remove(b')')
.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()
}
}
pub fn get_package_url(npmrc: &ResolvedNpmRc, name: &str) -> Url {
let registry_url = 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 =
percent_encoding::NON_ALPHANUMERIC
.remove(b'!')
.remove(b'\'')
.remove(b'(')
.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
// 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

@ -55,7 +55,7 @@ use super::CliNpmResolver;
use super::InnerCliNpmResolverRef;
use super::ResolvePkgFolderFromDenoReqError;
mod cache;
pub mod cache;
mod registry;
mod resolution;
mod resolvers;

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

@ -713,6 +713,7 @@ async fn sync_resolution_with_fs(
snapshot,
&package_partitions.packages,
Some(root_node_modules_dir_path),
progress_bar,
)
.await?;

View file

@ -8,10 +8,12 @@ use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use common::maybe_auth_header_for_npm_registry;
use dashmap::DashMap;
use deno_ast::ModuleSpecifier;
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::registry::NpmPackageInfo;
use deno_resolver::npm::ByonmNpmResolver;
use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError;
@ -19,10 +21,10 @@ use deno_runtime::deno_node::NodeRequireResolver;
use deno_runtime::ops::process::NpmProcessStateProvider;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
use managed::cache::registry_info::get_package_url;
use node_resolver::NpmResolver;
use thiserror::Error;
use crate::args::npm_registry_url;
use crate::file_fetcher::FileFetcher;
pub use self::byonm::CliByonmNpmResolver;
@ -115,14 +117,19 @@ pub struct NpmFetchResolver {
nv_by_req: DashMap<PackageReq, Option<PackageNv>>,
info_by_name: DashMap<String, Option<Arc<NpmPackageInfo>>>,
file_fetcher: Arc<FileFetcher>,
npmrc: Arc<ResolvedNpmRc>,
}
impl NpmFetchResolver {
pub fn new(file_fetcher: Arc<FileFetcher>) -> Self {
pub fn new(
file_fetcher: Arc<FileFetcher>,
npmrc: Arc<ResolvedNpmRc>,
) -> Self {
Self {
nv_by_req: Default::default(),
info_by_name: Default::default(),
file_fetcher,
npmrc,
}
}
@ -157,11 +164,21 @@ impl NpmFetchResolver {
return info.value().clone();
}
let fetch_package_info = || async {
let info_url = npm_registry_url().join(name).ok()?;
let info_url = get_package_url(&self.npmrc, name);
let file_fetcher = self.file_fetcher.clone();
let registry_config = self.npmrc.get_registry_config(name);
// TODO(bartlomieju): this should error out, not use `.ok()`.
let maybe_auth_header =
maybe_auth_header_for_npm_registry(registry_config).ok()?;
// spawn due to the lsp's `Send` requirement
let file = deno_core::unsync::spawn(async move {
file_fetcher.fetch_bypass_permissions(&info_url).await.ok()
file_fetcher
.fetch_bypass_permissions_with_maybe_auth(
&info_url,
maybe_auth_header,
)
.await
.ok()
})
.await
.ok()??;

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

@ -291,7 +291,7 @@
"type": "array",
"description": "List of tag names that will be run. Empty list disables all tags and will only use rules from `include`.",
"items": {
"type": "string"
"$ref": "https://raw.githubusercontent.com/denoland/deno_lint/main/schemas/tags.v1.json"
},
"minItems": 0,
"uniqueItems": true
@ -300,7 +300,7 @@
"type": "array",
"description": "List of rule names that will be excluded from configured tag sets. If the same rule is in `include` it will be run.",
"items": {
"type": "string"
"$ref": "https://raw.githubusercontent.com/denoland/deno_lint/main/schemas/rules.v1.json"
},
"minItems": 0,
"uniqueItems": true
@ -309,7 +309,7 @@
"type": "array",
"description": "List of rule names that will be run. Even if the same rule is in `exclude` it will be run.",
"items": {
"type": "string"
"$ref": "https://raw.githubusercontent.com/denoland/deno_lint/main/schemas/rules.v1.json"
},
"minItems": 0,
"uniqueItems": true
@ -528,8 +528,10 @@
"bare-node-builtins",
"byonm",
"cron",
"detect-cjs",
"ffi",
"fs",
"fmt-component",
"http",
"kv",
"net",

View file

@ -9,14 +9,18 @@ use std::ffi::OsString;
use std::fs;
use std::fs::File;
use std::future::Future;
use std::io::ErrorKind;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Write;
use std::ops::Range;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::sync::Arc;
use deno_ast::MediaType;
use deno_ast::ModuleSpecifier;
use deno_config::workspace::PackageJsonDepResolution;
use deno_config::workspace::ResolverWorkspaceJsrPackage;
@ -30,13 +34,22 @@ use deno_core::futures::AsyncReadExt;
use deno_core::futures::AsyncSeekExt;
use deno_core::serde_json;
use deno_core::url::Url;
use deno_graph::source::RealFileSystem;
use deno_graph::ModuleGraph;
use deno_npm::resolution::SerializedNpmResolutionSnapshot;
use deno_npm::resolution::SerializedNpmResolutionSnapshotPackage;
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmPackageId;
use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs;
use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_fs::RealFs;
use deno_runtime::deno_io::fs::FsError;
use deno_runtime::deno_node::PackageJson;
use deno_semver::npm::NpmVersionReqParseError;
use deno_semver::package::PackageReq;
use deno_semver::Version;
use deno_semver::VersionReqSpecifierParseError;
use eszip::EszipRelativeFileBaseUrl;
use indexmap::IndexMap;
use log::Level;
use serde::Deserialize;
@ -49,6 +62,7 @@ use crate::args::NpmInstallDepsProvider;
use crate::args::PermissionFlags;
use crate::args::UnstableConfig;
use crate::cache::DenoDir;
use crate::emit::Emitter;
use crate::file_fetcher::FileFetcher;
use crate::http_util::HttpClientProvider;
use crate::npm::CliNpmResolver;
@ -60,12 +74,63 @@ use crate::util::fs::canonicalize_path_maybe_not_exists;
use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle;
use super::file_system::DenoCompileFileSystem;
use super::serialization::deserialize_binary_data_section;
use super::serialization::serialize_binary_data_section;
use super::serialization::DenoCompileModuleData;
use super::serialization::DeserializedDataSection;
use super::serialization::RemoteModulesStore;
use super::serialization::RemoteModulesStoreBuilder;
use super::virtual_fs::FileBackedVfs;
use super::virtual_fs::VfsBuilder;
use super::virtual_fs::VfsRoot;
use super::virtual_fs::VirtualDirectory;
const MAGIC_TRAILER: &[u8; 8] = b"d3n0l4nd";
/// A URL that can be designated as the base for relative URLs.
///
/// After creation, this URL may be used to get the key for a
/// module in the binary.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StandaloneRelativeFileBaseUrl<'a>(&'a Url);
impl<'a> From<&'a Url> for StandaloneRelativeFileBaseUrl<'a> {
fn from(url: &'a Url) -> Self {
Self(url)
}
}
impl<'a> StandaloneRelativeFileBaseUrl<'a> {
pub fn new(url: &'a Url) -> Self {
debug_assert_eq!(url.scheme(), "file");
Self(url)
}
/// Gets the module map key of the provided specifier.
///
/// * Descendant file specifiers will be made relative to the base.
/// * Non-descendant file specifiers will stay as-is (absolute).
/// * Non-file specifiers will stay as-is.
pub fn specifier_key<'b>(&self, target: &'b Url) -> Cow<'b, str> {
if target.scheme() != "file" {
return Cow::Borrowed(target.as_str());
}
match self.0.make_relative(target) {
Some(relative) => {
if relative.starts_with("../") {
Cow::Borrowed(target.as_str())
} else {
Cow::Owned(relative)
}
}
None => Cow::Borrowed(target.as_str()),
}
}
pub fn inner(&self) -> &Url {
self.0
}
}
#[derive(Deserialize, Serialize)]
pub enum NodeModules {
@ -120,78 +185,23 @@ pub struct Metadata {
pub unstable_config: UnstableConfig,
}
pub fn load_npm_vfs(root_dir_path: PathBuf) -> Result<FileBackedVfs, AnyError> {
let data = libsui::find_section("d3n0l4nd").unwrap();
// We do the first part sync so it can complete quickly
let trailer: [u8; TRAILER_SIZE] = data[0..TRAILER_SIZE].try_into().unwrap();
let trailer = match Trailer::parse(&trailer)? {
None => panic!("Could not find trailer"),
Some(trailer) => trailer,
};
let data = &data[TRAILER_SIZE..];
let vfs_data =
&data[trailer.npm_vfs_pos as usize..trailer.npm_files_pos as usize];
let mut dir: VirtualDirectory = serde_json::from_slice(vfs_data)?;
// align the name of the directory with the root dir
dir.name = root_dir_path
.file_name()
.unwrap()
.to_string_lossy()
.to_string();
let fs_root = VfsRoot {
dir,
root_path: root_dir_path,
start_file_offset: trailer.npm_files_pos,
};
Ok(FileBackedVfs::new(data.to_vec(), fs_root))
}
fn write_binary_bytes(
mut file_writer: File,
original_bin: Vec<u8>,
metadata: &Metadata,
eszip: eszip::EszipV2,
npm_vfs: Option<&VirtualDirectory>,
npm_files: &Vec<Vec<u8>>,
npm_snapshot: Option<SerializedNpmResolutionSnapshot>,
remote_modules: &RemoteModulesStoreBuilder,
vfs: VfsBuilder,
compile_flags: &CompileFlags,
) -> Result<(), AnyError> {
let metadata = serde_json::to_string(metadata)?.as_bytes().to_vec();
let npm_vfs = serde_json::to_string(&npm_vfs)?.as_bytes().to_vec();
let eszip_archive = eszip.into_bytes();
let mut writer = Vec::new();
// write the trailer, which includes the positions
// of the data blocks in the file
writer.write_all(&{
let metadata_pos = eszip_archive.len() as u64;
let npm_vfs_pos = metadata_pos + (metadata.len() as u64);
let npm_files_pos = npm_vfs_pos + (npm_vfs.len() as u64);
Trailer {
eszip_pos: 0,
metadata_pos,
npm_vfs_pos,
npm_files_pos,
}
.as_bytes()
})?;
writer.write_all(&eszip_archive)?;
writer.write_all(&metadata)?;
writer.write_all(&npm_vfs)?;
for file in npm_files {
writer.write_all(file)?;
}
let data_section_bytes =
serialize_binary_data_section(metadata, npm_snapshot, remote_modules, vfs)?;
let target = compile_flags.resolve_target();
if target.contains("linux") {
libsui::Elf::new(&original_bin).append(
"d3n0l4nd",
&writer,
&data_section_bytes,
&mut file_writer,
)?;
} else if target.contains("windows") {
@ -201,11 +211,11 @@ fn write_binary_bytes(
pe = pe.set_icon(&icon)?;
}
pe.write_resource("d3n0l4nd", writer)?
pe.write_resource("d3n0l4nd", data_section_bytes)?
.build(&mut file_writer)?;
} else if target.contains("darwin") {
libsui::Macho::from(original_bin)?
.write_section("d3n0l4nd", writer)?
.write_section("d3n0l4nd", data_section_bytes)?
.build_and_sign(&mut file_writer)?;
}
Ok(())
@ -221,6 +231,63 @@ pub fn is_standalone_binary(exe_path: &Path) -> bool {
|| libsui::utils::is_macho(&data)
}
pub struct StandaloneData {
pub fs: Arc<dyn deno_fs::FileSystem>,
pub metadata: Metadata,
pub modules: StandaloneModules,
pub npm_snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
pub root_path: PathBuf,
pub vfs: Arc<FileBackedVfs>,
}
pub struct StandaloneModules {
remote_modules: RemoteModulesStore,
vfs: Arc<FileBackedVfs>,
}
impl StandaloneModules {
pub fn resolve_specifier<'a>(
&'a self,
specifier: &'a ModuleSpecifier,
) -> Result<Option<&'a ModuleSpecifier>, AnyError> {
if specifier.scheme() == "file" {
Ok(Some(specifier))
} else {
self.remote_modules.resolve_specifier(specifier)
}
}
pub fn read<'a>(
&'a self,
specifier: &'a ModuleSpecifier,
) -> Result<Option<DenoCompileModuleData<'a>>, AnyError> {
if specifier.scheme() == "file" {
let path = deno_path_util::url_to_file_path(specifier)?;
let bytes = match self.vfs.file_entry(&path) {
Ok(entry) => self.vfs.read_file_all(entry)?,
Err(err) if err.kind() == ErrorKind::NotFound => {
let bytes = match RealFs.read_file_sync(&path, None) {
Ok(bytes) => bytes,
Err(FsError::Io(err)) if err.kind() == ErrorKind::NotFound => {
return Ok(None)
}
Err(err) => return Err(err.into()),
};
Cow::Owned(bytes)
}
Err(err) => return Err(err.into()),
};
Ok(Some(DenoCompileModuleData {
media_type: MediaType::from_specifier(specifier),
specifier,
data: bytes,
}))
} else {
self.remote_modules.read(specifier)
}
}
}
/// This function will try to run this binary as a standalone binary
/// produced by `deno compile`. It determines if this is a standalone
/// binary by skipping over the trailer width at the end of the file,
@ -228,110 +295,66 @@ pub fn is_standalone_binary(exe_path: &Path) -> bool {
/// the bundle is executed. If not, this function exits with `Ok(None)`.
pub fn extract_standalone(
cli_args: Cow<Vec<OsString>>,
) -> Result<
Option<impl Future<Output = Result<(Metadata, eszip::EszipV2), AnyError>>>,
AnyError,
> {
) -> Result<Option<StandaloneData>, AnyError> {
let Some(data) = libsui::find_section("d3n0l4nd") else {
return Ok(None);
};
// We do the first part sync so it can complete quickly
let trailer = match Trailer::parse(&data[0..TRAILER_SIZE])? {
let DeserializedDataSection {
mut metadata,
npm_snapshot,
remote_modules,
mut vfs_dir,
vfs_files_data,
} = match deserialize_binary_data_section(data)? {
Some(data_section) => data_section,
None => return Ok(None),
Some(trailer) => trailer,
};
let root_path = {
let maybe_current_exe = std::env::current_exe().ok();
let current_exe_name = maybe_current_exe
.as_ref()
.and_then(|p| p.file_name())
.map(|p| p.to_string_lossy())
// should never happen
.unwrap_or_else(|| Cow::Borrowed("binary"));
std::env::temp_dir().join(format!("deno-compile-{}", current_exe_name))
};
let cli_args = cli_args.into_owned();
// If we have an eszip, read it out
Ok(Some(async move {
let bufreader =
deno_core::futures::io::BufReader::new(&data[TRAILER_SIZE..]);
metadata.argv.reserve(cli_args.len() - 1);
for arg in cli_args.into_iter().skip(1) {
metadata.argv.push(arg.into_string().unwrap());
}
let vfs = {
// align the name of the directory with the root dir
vfs_dir.name = root_path.file_name().unwrap().to_string_lossy().to_string();
let (eszip, loader) = eszip::EszipV2::parse(bufreader)
.await
.context("Failed to parse eszip header")?;
let bufreader = loader.await.context("Failed to parse eszip archive")?;
let mut metadata = String::new();
bufreader
.take(trailer.metadata_len())
.read_to_string(&mut metadata)
.await
.context("Failed to read metadata from the current executable")?;
let mut metadata: Metadata = serde_json::from_str(&metadata).unwrap();
metadata.argv.reserve(cli_args.len() - 1);
for arg in cli_args.into_iter().skip(1) {
metadata.argv.push(arg.into_string().unwrap());
}
Ok((metadata, eszip))
let fs_root = VfsRoot {
dir: vfs_dir,
root_path: root_path.clone(),
start_file_offset: 0,
};
Arc::new(FileBackedVfs::new(Cow::Borrowed(vfs_files_data), fs_root))
};
let fs: Arc<dyn deno_fs::FileSystem> =
Arc::new(DenoCompileFileSystem::new(vfs.clone()));
Ok(Some(StandaloneData {
fs,
metadata,
modules: StandaloneModules {
remote_modules,
vfs: vfs.clone(),
},
npm_snapshot,
root_path,
vfs,
}))
}
const TRAILER_SIZE: usize = std::mem::size_of::<Trailer>() + 8; // 8 bytes for the magic trailer string
struct Trailer {
eszip_pos: u64,
metadata_pos: u64,
npm_vfs_pos: u64,
npm_files_pos: u64,
}
impl Trailer {
pub fn parse(trailer: &[u8]) -> Result<Option<Trailer>, AnyError> {
let (magic_trailer, rest) = trailer.split_at(8);
if magic_trailer != MAGIC_TRAILER {
return Ok(None);
}
let (eszip_archive_pos, rest) = rest.split_at(8);
let (metadata_pos, rest) = rest.split_at(8);
let (npm_vfs_pos, npm_files_pos) = rest.split_at(8);
let eszip_archive_pos = u64_from_bytes(eszip_archive_pos)?;
let metadata_pos = u64_from_bytes(metadata_pos)?;
let npm_vfs_pos = u64_from_bytes(npm_vfs_pos)?;
let npm_files_pos = u64_from_bytes(npm_files_pos)?;
Ok(Some(Trailer {
eszip_pos: eszip_archive_pos,
metadata_pos,
npm_vfs_pos,
npm_files_pos,
}))
}
pub fn metadata_len(&self) -> u64 {
self.npm_vfs_pos - self.metadata_pos
}
pub fn npm_vfs_len(&self) -> u64 {
self.npm_files_pos - self.npm_vfs_pos
}
pub fn as_bytes(&self) -> Vec<u8> {
let mut trailer = MAGIC_TRAILER.to_vec();
trailer.write_all(&self.eszip_pos.to_be_bytes()).unwrap();
trailer.write_all(&self.metadata_pos.to_be_bytes()).unwrap();
trailer.write_all(&self.npm_vfs_pos.to_be_bytes()).unwrap();
trailer
.write_all(&self.npm_files_pos.to_be_bytes())
.unwrap();
trailer
}
}
fn u64_from_bytes(arr: &[u8]) -> Result<u64, AnyError> {
let fixed_arr: &[u8; 8] = arr
.try_into()
.context("Failed to convert the buffer into a fixed-size array")?;
Ok(u64::from_be_bytes(*fixed_arr))
}
pub struct DenoCompileBinaryWriter<'a> {
deno_dir: &'a DenoDir,
emitter: &'a Emitter,
file_fetcher: &'a FileFetcher,
http_client_provider: &'a HttpClientProvider,
npm_resolver: &'a dyn CliNpmResolver,
@ -343,6 +366,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
#[allow(clippy::too_many_arguments)]
pub fn new(
deno_dir: &'a DenoDir,
emitter: &'a Emitter,
file_fetcher: &'a FileFetcher,
http_client_provider: &'a HttpClientProvider,
npm_resolver: &'a dyn CliNpmResolver,
@ -351,6 +375,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
) -> Self {
Self {
deno_dir,
emitter,
file_fetcher,
http_client_provider,
npm_resolver,
@ -362,8 +387,8 @@ impl<'a> DenoCompileBinaryWriter<'a> {
pub async fn write_bin(
&self,
writer: File,
eszip: eszip::EszipV2,
root_dir_url: EszipRelativeFileBaseUrl<'_>,
graph: &ModuleGraph,
root_dir_url: StandaloneRelativeFileBaseUrl<'_>,
entrypoint: &ModuleSpecifier,
compile_flags: &CompileFlags,
cli_options: &CliOptions,
@ -390,15 +415,17 @@ impl<'a> DenoCompileBinaryWriter<'a> {
)
}
}
self.write_standalone_binary(
writer,
original_binary,
eszip,
root_dir_url,
entrypoint,
cli_options,
compile_flags,
)
self
.write_standalone_binary(
writer,
original_binary,
graph,
root_dir_url,
entrypoint,
cli_options,
compile_flags,
)
.await
}
async fn get_base_binary(
@ -468,7 +495,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 {
@ -489,12 +520,12 @@ impl<'a> DenoCompileBinaryWriter<'a> {
/// This functions creates a standalone deno binary by appending a bundle
/// and magic trailer to the currently executing binary.
#[allow(clippy::too_many_arguments)]
fn write_standalone_binary(
async fn write_standalone_binary(
&self,
writer: File,
original_bin: Vec<u8>,
mut eszip: eszip::EszipV2,
root_dir_url: EszipRelativeFileBaseUrl<'_>,
graph: &ModuleGraph,
root_dir_url: StandaloneRelativeFileBaseUrl<'_>,
entrypoint: &ModuleSpecifier,
cli_options: &CliOptions,
compile_flags: &CompileFlags,
@ -508,19 +539,17 @@ impl<'a> DenoCompileBinaryWriter<'a> {
None => None,
};
let root_path = root_dir_url.inner().to_file_path().unwrap();
let (npm_vfs, npm_files, node_modules) = match self.npm_resolver.as_inner()
let (maybe_npm_vfs, node_modules, npm_snapshot) = match self
.npm_resolver
.as_inner()
{
InnerCliNpmResolverRef::Managed(managed) => {
let snapshot =
managed.serialized_valid_snapshot_for_system(&self.npm_system_info);
if !snapshot.as_serialized().packages.is_empty() {
let (root_dir, files) = self
.build_vfs(&root_path, cli_options)?
.into_dir_and_files();
eszip.add_npm_snapshot(snapshot);
let npm_vfs_builder = self.build_npm_vfs(&root_path, cli_options)?;
(
Some(root_dir),
files,
Some(npm_vfs_builder),
Some(NodeModules::Managed {
node_modules_dir: self.npm_resolver.root_node_modules_path().map(
|path| {
@ -532,18 +561,16 @@ impl<'a> DenoCompileBinaryWriter<'a> {
},
),
}),
Some(snapshot),
)
} else {
(None, Vec::new(), None)
(None, None, None)
}
}
InnerCliNpmResolverRef::Byonm(resolver) => {
let (root_dir, files) = self
.build_vfs(&root_path, cli_options)?
.into_dir_and_files();
let npm_vfs_builder = self.build_npm_vfs(&root_path, cli_options)?;
(
Some(root_dir),
files,
Some(npm_vfs_builder),
Some(NodeModules::Byonm {
root_node_modules_dir: resolver.root_node_modules_path().map(
|node_modules_dir| {
@ -556,9 +583,67 @@ impl<'a> DenoCompileBinaryWriter<'a> {
},
),
}),
None,
)
}
};
let mut vfs = if let Some(npm_vfs) = maybe_npm_vfs {
npm_vfs
} else {
VfsBuilder::new(root_path.clone())?
};
let mut remote_modules_store = RemoteModulesStoreBuilder::default();
for module in graph.modules() {
if module.specifier().scheme() == "data" {
continue; // don't store data urls as an entry as they're in the code
}
let (maybe_source, media_type) = match module {
deno_graph::Module::Js(m) => {
// todo(https://github.com/denoland/deno_media_type/pull/12): use is_emittable()
let is_emittable = matches!(
m.media_type,
MediaType::TypeScript
| MediaType::Mts
| MediaType::Cts
| MediaType::Jsx
| MediaType::Tsx
);
let source = if is_emittable {
let source = self
.emitter
.emit_parsed_source(&m.specifier, m.media_type, &m.source)
.await?;
source.into_bytes()
} else {
m.source.as_bytes().to_vec()
};
(Some(source), m.media_type)
}
deno_graph::Module::Json(m) => {
(Some(m.source.as_bytes().to_vec()), m.media_type)
}
deno_graph::Module::Npm(_)
| deno_graph::Module::Node(_)
| deno_graph::Module::External(_) => (None, MediaType::Unknown),
};
if module.specifier().scheme() == "file" {
let file_path = deno_path_util::url_to_file_path(module.specifier())?;
vfs
.add_file_with_data(
&file_path,
match maybe_source {
Some(source) => source,
None => RealFs.read_file_sync(&file_path, None)?,
},
)
.with_context(|| {
format!("Failed adding '{}'", file_path.display())
})?;
} else if let Some(source) = maybe_source {
remote_modules_store.add(module.specifier(), media_type, source);
}
}
remote_modules_store.add_redirects(&graph.redirects);
let env_vars_from_env_file = match cli_options.env_file_name() {
Some(env_filename) => {
@ -622,6 +707,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(),
},
@ -631,14 +717,14 @@ impl<'a> DenoCompileBinaryWriter<'a> {
writer,
original_bin,
&metadata,
eszip,
npm_vfs.as_ref(),
&npm_files,
npm_snapshot.map(|s| s.into_serialized()),
&remote_modules_store,
vfs,
compile_flags,
)
}
fn build_vfs(
fn build_npm_vfs(
&self,
root_path: &Path,
cli_options: &CliOptions,
@ -659,8 +745,8 @@ impl<'a> DenoCompileBinaryWriter<'a> {
} else {
// DO NOT include the user's registry url as it may contain credentials,
// but also don't make this dependent on the registry url
let root_path = npm_resolver.global_cache_root_folder();
let mut builder = VfsBuilder::new(root_path)?;
let global_cache_root_path = npm_resolver.global_cache_root_folder();
let mut builder = VfsBuilder::new(global_cache_root_path)?;
let mut packages =
npm_resolver.all_system_packages(&self.npm_system_info);
packages.sort_by(|a, b| a.id.cmp(&b.id)); // determinism
@ -670,12 +756,12 @@ impl<'a> DenoCompileBinaryWriter<'a> {
builder.add_dir_recursive(&folder)?;
}
// Flatten all the registries folders into a single "node_modules/localhost" folder
// Flatten all the registries folders into a single ".deno_compile_node_modules/localhost" folder
// that will be used by denort when loading the npm cache. This avoids us exposing
// the user's private registry information and means we don't have to bother
// serializing all the different registry config into the binary.
builder.with_root_dir(|root_dir| {
root_dir.name = "node_modules".to_string();
root_dir.name = ".deno_compile_node_modules".to_string();
let mut new_entries = Vec::with_capacity(root_dir.entries.len());
let mut localhost_entries = IndexMap::new();
for entry in std::mem::take(&mut root_dir.entries) {
@ -710,6 +796,8 @@ impl<'a> DenoCompileBinaryWriter<'a> {
root_dir.entries = new_entries;
});
builder.set_new_root_path(root_path.to_path_buf())?;
Ok(builder)
}
}

View file

@ -22,8 +22,8 @@ use super::virtual_fs::FileBackedVfs;
pub struct DenoCompileFileSystem(Arc<FileBackedVfs>);
impl DenoCompileFileSystem {
pub fn new(vfs: FileBackedVfs) -> Self {
Self(Arc::new(vfs))
pub fn new(vfs: Arc<FileBackedVfs>) -> Self {
Self(vfs)
}
fn error_if_in_vfs(&self, path: &Path) -> FsResult<()> {

View file

@ -5,6 +5,8 @@
#![allow(dead_code)]
#![allow(unused_imports)]
use binary::StandaloneData;
use binary::StandaloneModules;
use deno_ast::MediaType;
use deno_cache_dir::npm::NpmCacheDir;
use deno_config::workspace::MappedResolution;
@ -38,7 +40,6 @@ use deno_runtime::permissions::RuntimePermissionDescriptorParser;
use deno_runtime::WorkerExecutionMode;
use deno_runtime::WorkerLogLevel;
use deno_semver::npm::NpmPackageReqReference;
use eszip::EszipRelativeFileBaseUrl;
use import_map::parse_from_json;
use node_resolver::analyze::NodeCodeTranslator;
use node_resolver::NodeResolutionMode;
@ -54,6 +55,7 @@ use crate::args::CacheSetting;
use crate::args::NpmInstallDepsProvider;
use crate::args::StorageKeyResolver;
use crate::cache::Caches;
use crate::cache::DenoCacheEnvFsAdapter;
use crate::cache::DenoDirProvider;
use crate::cache::NodeAnalysisCache;
use crate::cache::RealDenoCacheEnv;
@ -78,52 +80,18 @@ use crate::worker::ModuleLoaderFactory;
pub mod binary;
mod file_system;
mod serialization;
mod virtual_fs;
pub use binary::extract_standalone;
pub use binary::is_standalone_binary;
pub use binary::DenoCompileBinaryWriter;
use self::binary::load_npm_vfs;
use self::binary::Metadata;
use self::file_system::DenoCompileFileSystem;
struct WorkspaceEszipModule {
specifier: ModuleSpecifier,
inner: eszip::Module,
}
struct WorkspaceEszip {
eszip: eszip::EszipV2,
root_dir_url: Arc<ModuleSpecifier>,
}
impl WorkspaceEszip {
pub fn get_module(
&self,
specifier: &ModuleSpecifier,
) -> Option<WorkspaceEszipModule> {
if specifier.scheme() == "file" {
let specifier_key = EszipRelativeFileBaseUrl::new(&self.root_dir_url)
.specifier_key(specifier);
let module = self.eszip.get_module(&specifier_key)?;
let specifier = self.root_dir_url.join(&module.specifier).unwrap();
Some(WorkspaceEszipModule {
specifier,
inner: module,
})
} else {
let module = self.eszip.get_module(specifier.as_str())?;
Some(WorkspaceEszipModule {
specifier: ModuleSpecifier::parse(&module.specifier).unwrap(),
inner: module,
})
}
}
}
struct SharedModuleLoaderState {
eszip: WorkspaceEszip,
modules: StandaloneModules,
workspace_resolver: WorkspaceResolver,
node_resolver: Arc<CliNodeResolver>,
npm_module_loader: Arc<NpmModuleLoader>,
@ -249,8 +217,10 @@ impl ModuleLoader for EmbeddedModuleLoader {
}
if specifier.scheme() == "jsr" {
if let Some(module) = self.shared.eszip.get_module(&specifier) {
return Ok(module.specifier);
if let Some(specifier) =
self.shared.modules.resolve_specifier(&specifier)?
{
return Ok(specifier.clone());
}
}
@ -345,56 +315,30 @@ impl ModuleLoader for EmbeddedModuleLoader {
);
}
let Some(module) = self.shared.eszip.get_module(original_specifier) else {
return deno_core::ModuleLoadResponse::Sync(Err(type_error(format!(
"{MODULE_NOT_FOUND}: {}",
original_specifier
))));
};
let original_specifier = original_specifier.clone();
deno_core::ModuleLoadResponse::Async(
async move {
let code = module.inner.source().await.ok_or_else(|| {
type_error(format!("Module not found: {}", original_specifier))
})?;
let code = arc_u8_to_arc_str(code)
.map_err(|_| type_error("Module source is not utf-8"))?;
Ok(deno_core::ModuleSource::new_with_redirect(
match module.inner.kind {
eszip::ModuleKind::JavaScript => ModuleType::JavaScript,
eszip::ModuleKind::Json => ModuleType::Json,
eszip::ModuleKind::Jsonc => {
return Err(type_error("jsonc modules not supported"))
}
eszip::ModuleKind::OpaqueData => {
unreachable!();
}
},
ModuleSourceCode::String(code.into()),
&original_specifier,
&module.specifier,
None,
match self.shared.modules.read(original_specifier) {
Ok(Some(module)) => {
let (module_specifier, module_type, module_source) =
module.into_for_v8();
deno_core::ModuleLoadResponse::Sync(Ok(
deno_core::ModuleSource::new_with_redirect(
module_type,
module_source,
original_specifier,
module_specifier,
None,
),
))
}
.boxed_local(),
)
Ok(None) => deno_core::ModuleLoadResponse::Sync(Err(type_error(
format!("{MODULE_NOT_FOUND}: {}", original_specifier),
))),
Err(err) => deno_core::ModuleLoadResponse::Sync(Err(type_error(
format!("{:?}", err),
))),
}
}
}
fn arc_u8_to_arc_str(
arc_u8: Arc<[u8]>,
) -> Result<Arc<str>, std::str::Utf8Error> {
// Check that the string is valid UTF-8.
std::str::from_utf8(&arc_u8)?;
// SAFETY: the string is valid UTF-8, and the layout Arc<[u8]> is the same as
// Arc<str>. This is proven by the From<Arc<str>> impl for Arc<[u8]> from the
// standard library.
Ok(unsafe {
std::mem::transmute::<std::sync::Arc<[u8]>, std::sync::Arc<str>>(arc_u8)
})
}
struct StandaloneModuleLoaderFactory {
shared: Arc<SharedModuleLoaderState>,
}
@ -439,13 +383,15 @@ impl RootCertStoreProvider for StandaloneRootCertStoreProvider {
}
}
pub async fn run(
mut eszip: eszip::EszipV2,
metadata: Metadata,
) -> Result<i32, AnyError> {
let current_exe_path = std::env::current_exe().unwrap();
let current_exe_name =
current_exe_path.file_name().unwrap().to_string_lossy();
pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
let StandaloneData {
fs,
metadata,
modules,
npm_snapshot,
root_path,
vfs,
} = data;
let deno_dir_provider = Arc::new(DenoDirProvider::new(None));
let root_cert_store_provider = Arc::new(StandaloneRootCertStoreProvider {
ca_stores: metadata.ca_stores,
@ -459,112 +405,83 @@ pub async fn run(
));
// use a dummy npm registry url
let npm_registry_url = ModuleSpecifier::parse("https://localhost/").unwrap();
let root_path =
std::env::temp_dir().join(format!("deno-compile-{}", current_exe_name));
let root_dir_url =
Arc::new(ModuleSpecifier::from_directory_path(&root_path).unwrap());
let main_module = root_dir_url.join(&metadata.entrypoint_key).unwrap();
let root_node_modules_path = root_path.join("node_modules");
let npm_cache_dir = NpmCacheDir::new(
&RealDenoCacheEnv,
root_node_modules_path.clone(),
vec![npm_registry_url.clone()],
);
let npm_global_cache_dir = npm_cache_dir.get_cache_location();
let npm_global_cache_dir = root_path.join(".deno_compile_node_modules");
let cache_setting = CacheSetting::Only;
let (fs, npm_resolver, maybe_vfs_root) = match metadata.node_modules {
let npm_resolver = match metadata.node_modules {
Some(binary::NodeModules::Managed { node_modules_dir }) => {
// this will always have a snapshot
let snapshot = eszip.take_npm_snapshot().unwrap();
let vfs_root_dir_path = if node_modules_dir.is_some() {
root_path.clone()
} else {
npm_cache_dir.root_dir().to_owned()
};
let vfs = load_npm_vfs(vfs_root_dir_path.clone())
.context("Failed to load npm vfs.")?;
let snapshot = npm_snapshot.unwrap();
let maybe_node_modules_path = node_modules_dir
.map(|node_modules_dir| vfs_root_dir_path.join(node_modules_dir));
let fs = Arc::new(DenoCompileFileSystem::new(vfs))
as Arc<dyn deno_fs::FileSystem>;
let npm_resolver =
create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
CliNpmResolverManagedCreateOptions {
snapshot: CliNpmResolverManagedSnapshotOption::Specified(Some(
snapshot,
)),
maybe_lockfile: None,
fs: fs.clone(),
http_client_provider: http_client_provider.clone(),
npm_global_cache_dir,
cache_setting,
text_only_progress_bar: progress_bar,
maybe_node_modules_path,
npm_system_info: Default::default(),
npm_install_deps_provider: Arc::new(
// this is only used for installing packages, which isn't necessary with deno compile
NpmInstallDepsProvider::empty(),
),
// create an npmrc that uses the fake npm_registry_url to resolve packages
npmrc: Arc::new(ResolvedNpmRc {
default_config: deno_npm::npm_rc::RegistryConfigWithUrl {
registry_url: npm_registry_url.clone(),
config: Default::default(),
},
scopes: Default::default(),
registry_configs: Default::default(),
}),
lifecycle_scripts: Default::default(),
},
))
.await?;
(fs, npm_resolver, Some(vfs_root_dir_path))
.map(|node_modules_dir| root_path.join(node_modules_dir));
create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
CliNpmResolverManagedCreateOptions {
snapshot: CliNpmResolverManagedSnapshotOption::Specified(Some(
snapshot,
)),
maybe_lockfile: None,
fs: fs.clone(),
http_client_provider: http_client_provider.clone(),
npm_global_cache_dir,
cache_setting,
text_only_progress_bar: progress_bar,
maybe_node_modules_path,
npm_system_info: Default::default(),
npm_install_deps_provider: Arc::new(
// this is only used for installing packages, which isn't necessary with deno compile
NpmInstallDepsProvider::empty(),
),
// create an npmrc that uses the fake npm_registry_url to resolve packages
npmrc: Arc::new(ResolvedNpmRc {
default_config: deno_npm::npm_rc::RegistryConfigWithUrl {
registry_url: npm_registry_url.clone(),
config: Default::default(),
},
scopes: Default::default(),
registry_configs: Default::default(),
}),
lifecycle_scripts: Default::default(),
},
))
.await?
}
Some(binary::NodeModules::Byonm {
root_node_modules_dir,
}) => {
let vfs_root_dir_path = root_path.clone();
let vfs = load_npm_vfs(vfs_root_dir_path.clone())
.context("Failed to load vfs.")?;
let root_node_modules_dir =
root_node_modules_dir.map(|p| vfs.root().join(p));
let fs = Arc::new(DenoCompileFileSystem::new(vfs))
as Arc<dyn deno_fs::FileSystem>;
let npm_resolver = create_cli_npm_resolver(
CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions {
create_cli_npm_resolver(CliNpmResolverCreateOptions::Byonm(
CliByonmNpmResolverCreateOptions {
fs: CliDenoResolverFs(fs.clone()),
root_node_modules_dir,
}),
)
.await?;
(fs, npm_resolver, Some(vfs_root_dir_path))
},
))
.await?
}
None => {
let fs = Arc::new(deno_fs::RealFs) as Arc<dyn deno_fs::FileSystem>;
let npm_resolver =
create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
CliNpmResolverManagedCreateOptions {
snapshot: CliNpmResolverManagedSnapshotOption::Specified(None),
maybe_lockfile: None,
fs: fs.clone(),
http_client_provider: http_client_provider.clone(),
npm_global_cache_dir,
cache_setting,
text_only_progress_bar: progress_bar,
maybe_node_modules_path: None,
npm_system_info: Default::default(),
npm_install_deps_provider: Arc::new(
// this is only used for installing packages, which isn't necessary with deno compile
NpmInstallDepsProvider::empty(),
),
// Packages from different registries are already inlined in the ESZip,
// so no need to create actual `.npmrc` configuration.
npmrc: create_default_npmrc(),
lifecycle_scripts: Default::default(),
},
))
.await?;
(fs, npm_resolver, None)
create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
CliNpmResolverManagedCreateOptions {
snapshot: CliNpmResolverManagedSnapshotOption::Specified(None),
maybe_lockfile: None,
fs: fs.clone(),
http_client_provider: http_client_provider.clone(),
npm_global_cache_dir,
cache_setting,
text_only_progress_bar: progress_bar,
maybe_node_modules_path: None,
npm_system_info: Default::default(),
npm_install_deps_provider: Arc::new(
// this is only used for installing packages, which isn't necessary with deno compile
NpmInstallDepsProvider::empty(),
),
// Packages from different registries are already inlined in the binary,
// so no need to create actual `.npmrc` configuration.
npmrc: create_default_npmrc(),
lifecycle_scripts: Default::default(),
},
))
.await?
}
};
@ -586,6 +503,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,
@ -644,14 +562,11 @@ pub async fn run(
};
let module_loader_factory = StandaloneModuleLoaderFactory {
shared: Arc::new(SharedModuleLoaderState {
eszip: WorkspaceEszip {
eszip,
root_dir_url,
},
modules,
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,
@ -662,19 +577,17 @@ pub async fn run(
let permissions = {
let mut permissions =
metadata.permissions.to_options(/* cli_arg_urls */ &[]);
// if running with an npm vfs, grant read access to it
if let Some(vfs_root) = maybe_vfs_root {
match &mut permissions.allow_read {
Some(vec) if vec.is_empty() => {
// do nothing, already granted
}
Some(vec) => {
vec.push(vfs_root.to_string_lossy().to_string());
}
None => {
permissions.allow_read =
Some(vec![vfs_root.to_string_lossy().to_string()]);
}
// grant read access to the vfs
match &mut permissions.allow_read {
Some(vec) if vec.is_empty() => {
// do nothing, already granted
}
Some(vec) => {
vec.push(root_path.to_string_lossy().to_string());
}
None => {
permissions.allow_read =
Some(vec![root_path.to_string_lossy().to_string()]);
}
}
@ -696,6 +609,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 +652,7 @@ pub async fn run(
node_ipc: None,
serve_port: None,
serve_host: None,
unstable_detect_cjs: metadata.unstable_config.detect_cjs,
},
);

View file

@ -0,0 +1,642 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::io::Write;
use deno_ast::MediaType;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_core::url::Url;
use deno_core::FastString;
use deno_core::ModuleSourceCode;
use deno_core::ModuleType;
use deno_npm::resolution::SerializedNpmResolutionSnapshot;
use deno_npm::resolution::SerializedNpmResolutionSnapshotPackage;
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmPackageId;
use deno_semver::package::PackageReq;
use crate::standalone::virtual_fs::VirtualDirectory;
use super::binary::Metadata;
use super::virtual_fs::VfsBuilder;
const MAGIC_BYTES: &[u8; 8] = b"d3n0l4nd";
/// Binary format:
/// * d3n0l4nd
/// * <metadata_len><metadata>
/// * <npm_snapshot_len><npm_snapshot>
/// * <remote_modules_len><remote_modules>
/// * <vfs_headers_len><vfs_headers>
/// * <vfs_file_data_len><vfs_file_data>
/// * d3n0l4nd
pub fn serialize_binary_data_section(
metadata: &Metadata,
npm_snapshot: Option<SerializedNpmResolutionSnapshot>,
remote_modules: &RemoteModulesStoreBuilder,
vfs: VfsBuilder,
) -> Result<Vec<u8>, AnyError> {
fn write_bytes_with_len(bytes: &mut Vec<u8>, data: &[u8]) {
bytes.extend_from_slice(&(data.len() as u64).to_le_bytes());
bytes.extend_from_slice(data);
}
let mut bytes = Vec::new();
bytes.extend_from_slice(MAGIC_BYTES);
// 1. Metadata
{
let metadata = serde_json::to_string(metadata)?;
write_bytes_with_len(&mut bytes, metadata.as_bytes());
}
// 2. Npm snapshot
{
let npm_snapshot =
npm_snapshot.map(serialize_npm_snapshot).unwrap_or_default();
write_bytes_with_len(&mut bytes, &npm_snapshot);
}
// 3. Remote modules
{
let update_index = bytes.len();
bytes.extend_from_slice(&(0_u64).to_le_bytes());
let start_index = bytes.len();
remote_modules.write(&mut bytes)?;
let length = bytes.len() - start_index;
let length_bytes = (length as u64).to_le_bytes();
bytes[update_index..update_index + length_bytes.len()]
.copy_from_slice(&length_bytes);
}
// 4. VFS
{
let (vfs, vfs_files) = vfs.into_dir_and_files();
let vfs = serde_json::to_string(&vfs)?;
write_bytes_with_len(&mut bytes, vfs.as_bytes());
let vfs_bytes_len = vfs_files.iter().map(|f| f.len() as u64).sum::<u64>();
bytes.extend_from_slice(&vfs_bytes_len.to_le_bytes());
for file in &vfs_files {
bytes.extend_from_slice(file);
}
}
// write the magic bytes at the end so we can use it
// to make sure we've deserialized correctly
bytes.extend_from_slice(MAGIC_BYTES);
Ok(bytes)
}
pub struct DeserializedDataSection {
pub metadata: Metadata,
pub npm_snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
pub remote_modules: RemoteModulesStore,
pub vfs_dir: VirtualDirectory,
pub vfs_files_data: &'static [u8],
}
pub fn deserialize_binary_data_section(
data: &'static [u8],
) -> Result<Option<DeserializedDataSection>, AnyError> {
fn read_bytes_with_len(input: &[u8]) -> Result<(&[u8], &[u8]), AnyError> {
let (input, len) = read_u64(input)?;
let (input, data) = read_bytes(input, len as usize)?;
Ok((input, data))
}
fn read_magic_bytes(input: &[u8]) -> Result<(&[u8], bool), AnyError> {
if input.len() < MAGIC_BYTES.len() {
bail!("Unexpected end of data. Could not find magic bytes.");
}
let (magic_bytes, input) = input.split_at(MAGIC_BYTES.len());
if magic_bytes != MAGIC_BYTES {
return Ok((input, false));
}
Ok((input, true))
}
let (input, found) = read_magic_bytes(data)?;
if !found {
return Ok(None);
}
// 1. Metadata
let (input, data) = read_bytes_with_len(input).context("reading metadata")?;
let metadata: Metadata =
serde_json::from_slice(data).context("deserializing metadata")?;
// 2. Npm snapshot
let (input, data) =
read_bytes_with_len(input).context("reading npm snapshot")?;
let npm_snapshot = if data.is_empty() {
None
} else {
Some(deserialize_npm_snapshot(data).context("deserializing npm snapshot")?)
};
// 3. Remote modules
let (input, data) =
read_bytes_with_len(input).context("reading remote modules data")?;
let remote_modules =
RemoteModulesStore::build(data).context("deserializing remote modules")?;
// 4. VFS
let (input, data) = read_bytes_with_len(input).context("vfs")?;
let vfs_dir: VirtualDirectory =
serde_json::from_slice(data).context("deserializing vfs data")?;
let (input, vfs_files_data) =
read_bytes_with_len(input).context("reading vfs files data")?;
// finally ensure we read the magic bytes at the end
let (_input, found) = read_magic_bytes(input)?;
if !found {
bail!("Could not find magic bytes at the end of the data.");
}
Ok(Some(DeserializedDataSection {
metadata,
npm_snapshot,
remote_modules,
vfs_dir,
vfs_files_data,
}))
}
#[derive(Default)]
pub struct RemoteModulesStoreBuilder {
specifiers: Vec<(String, u64)>,
data: Vec<(MediaType, Vec<u8>)>,
data_byte_len: u64,
redirects: Vec<(String, String)>,
redirects_len: u64,
}
impl RemoteModulesStoreBuilder {
pub fn add(&mut self, specifier: &Url, media_type: MediaType, data: Vec<u8>) {
let specifier = specifier.to_string();
self.specifiers.push((specifier, self.data_byte_len));
self.data_byte_len += 1 + 8 + data.len() as u64; // media type (1 byte), data length (8 bytes), data
self.data.push((media_type, data));
}
pub fn add_redirects(&mut self, redirects: &BTreeMap<Url, Url>) {
self.redirects.reserve(redirects.len());
for (from, to) in redirects {
let from = from.to_string();
let to = to.to_string();
self.redirects_len += (4 + from.len() + 4 + to.len()) as u64;
self.redirects.push((from, to));
}
}
fn write(&self, writer: &mut dyn Write) -> Result<(), AnyError> {
writer.write_all(&(self.specifiers.len() as u32).to_le_bytes())?;
writer.write_all(&(self.redirects.len() as u32).to_le_bytes())?;
for (specifier, offset) in &self.specifiers {
writer.write_all(&(specifier.len() as u32).to_le_bytes())?;
writer.write_all(specifier.as_bytes())?;
writer.write_all(&offset.to_le_bytes())?;
}
for (from, to) in &self.redirects {
writer.write_all(&(from.len() as u32).to_le_bytes())?;
writer.write_all(from.as_bytes())?;
writer.write_all(&(to.len() as u32).to_le_bytes())?;
writer.write_all(to.as_bytes())?;
}
for (media_type, data) in &self.data {
writer.write_all(&[serialize_media_type(*media_type)])?;
writer.write_all(&(data.len() as u64).to_le_bytes())?;
writer.write_all(data)?;
}
Ok(())
}
}
pub struct DenoCompileModuleData<'a> {
pub specifier: &'a Url,
pub media_type: MediaType,
pub data: Cow<'static, [u8]>,
}
impl<'a> DenoCompileModuleData<'a> {
pub fn into_for_v8(self) -> (&'a Url, ModuleType, ModuleSourceCode) {
fn into_bytes(data: Cow<'static, [u8]>) -> ModuleSourceCode {
ModuleSourceCode::Bytes(match data {
Cow::Borrowed(d) => d.into(),
Cow::Owned(d) => d.into_boxed_slice().into(),
})
}
fn into_string_unsafe(data: Cow<'static, [u8]>) -> ModuleSourceCode {
// todo(https://github.com/denoland/deno_core/pull/943): store whether
// the string is ascii or not ahead of time so we can avoid the is_ascii()
// check in FastString::from_static
match data {
Cow::Borrowed(d) => ModuleSourceCode::String(
// SAFETY: we know this is a valid utf8 string
unsafe { FastString::from_static(std::str::from_utf8_unchecked(d)) },
),
Cow::Owned(d) => ModuleSourceCode::Bytes(d.into_boxed_slice().into()),
}
}
let (media_type, source) = match self.media_type {
MediaType::JavaScript
| MediaType::Jsx
| MediaType::Mjs
| MediaType::Cjs
| MediaType::TypeScript
| MediaType::Mts
| MediaType::Cts
| MediaType::Dts
| MediaType::Dmts
| MediaType::Dcts
| MediaType::Tsx => {
(ModuleType::JavaScript, into_string_unsafe(self.data))
}
MediaType::Json => (ModuleType::Json, into_string_unsafe(self.data)),
MediaType::Wasm => (ModuleType::Wasm, into_bytes(self.data)),
// just assume javascript if we made it here
MediaType::TsBuildInfo | MediaType::SourceMap | MediaType::Unknown => {
(ModuleType::JavaScript, into_bytes(self.data))
}
};
(self.specifier, media_type, source)
}
}
enum RemoteModulesStoreSpecifierValue {
Data(usize),
Redirect(Url),
}
pub struct RemoteModulesStore {
specifiers: HashMap<Url, RemoteModulesStoreSpecifierValue>,
files_data: &'static [u8],
}
impl RemoteModulesStore {
fn build(data: &'static [u8]) -> Result<Self, AnyError> {
fn read_specifier(input: &[u8]) -> Result<(&[u8], (Url, u64)), AnyError> {
let (input, specifier) = read_string_lossy(input)?;
let specifier = Url::parse(&specifier)?;
let (input, offset) = read_u64(input)?;
Ok((input, (specifier, offset)))
}
fn read_redirect(input: &[u8]) -> Result<(&[u8], (Url, Url)), AnyError> {
let (input, from) = read_string_lossy(input)?;
let from = Url::parse(&from)?;
let (input, to) = read_string_lossy(input)?;
let to = Url::parse(&to)?;
Ok((input, (from, to)))
}
fn read_headers(
input: &[u8],
) -> Result<(&[u8], HashMap<Url, RemoteModulesStoreSpecifierValue>), AnyError>
{
let (input, specifiers_len) = read_u32_as_usize(input)?;
let (mut input, redirects_len) = read_u32_as_usize(input)?;
let mut specifiers =
HashMap::with_capacity(specifiers_len + redirects_len);
for _ in 0..specifiers_len {
let (current_input, (specifier, offset)) =
read_specifier(input).context("reading specifier")?;
input = current_input;
specifiers.insert(
specifier,
RemoteModulesStoreSpecifierValue::Data(offset as usize),
);
}
for _ in 0..redirects_len {
let (current_input, (from, to)) = read_redirect(input)?;
input = current_input;
specifiers.insert(from, RemoteModulesStoreSpecifierValue::Redirect(to));
}
Ok((input, specifiers))
}
let (files_data, specifiers) = read_headers(data)?;
Ok(Self {
specifiers,
files_data,
})
}
pub fn resolve_specifier<'a>(
&'a self,
specifier: &'a Url,
) -> Result<Option<&'a Url>, AnyError> {
let mut count = 0;
let mut current = specifier;
loop {
if count > 10 {
bail!("Too many redirects resolving '{}'", specifier);
}
match self.specifiers.get(current) {
Some(RemoteModulesStoreSpecifierValue::Redirect(to)) => {
current = to;
count += 1;
}
Some(RemoteModulesStoreSpecifierValue::Data(_)) => {
return Ok(Some(current));
}
None => {
return Ok(None);
}
}
}
}
pub fn read<'a>(
&'a self,
specifier: &'a Url,
) -> Result<Option<DenoCompileModuleData<'a>>, AnyError> {
let mut count = 0;
let mut current = specifier;
loop {
if count > 10 {
bail!("Too many redirects resolving '{}'", specifier);
}
match self.specifiers.get(current) {
Some(RemoteModulesStoreSpecifierValue::Redirect(to)) => {
current = to;
count += 1;
}
Some(RemoteModulesStoreSpecifierValue::Data(offset)) => {
let input = &self.files_data[*offset..];
let (input, media_type_byte) = read_bytes(input, 1)?;
let media_type = deserialize_media_type(media_type_byte[0])?;
let (input, len) = read_u64(input)?;
let (_input, data) = read_bytes(input, len as usize)?;
return Ok(Some(DenoCompileModuleData {
specifier,
media_type,
data: Cow::Borrowed(data),
}));
}
None => {
return Ok(None);
}
}
}
}
}
fn serialize_npm_snapshot(
mut snapshot: SerializedNpmResolutionSnapshot,
) -> Vec<u8> {
fn append_string(bytes: &mut Vec<u8>, string: &str) {
let len = string.len() as u32;
bytes.extend_from_slice(&len.to_le_bytes());
bytes.extend_from_slice(string.as_bytes());
}
snapshot.packages.sort_by(|a, b| a.id.cmp(&b.id)); // determinism
let ids_to_stored_ids = snapshot
.packages
.iter()
.enumerate()
.map(|(i, pkg)| (&pkg.id, i as u32))
.collect::<HashMap<_, _>>();
let mut root_packages: Vec<_> = snapshot.root_packages.iter().collect();
root_packages.sort();
let mut bytes = Vec::new();
bytes.extend_from_slice(&(snapshot.packages.len() as u32).to_le_bytes());
for pkg in &snapshot.packages {
append_string(&mut bytes, &pkg.id.as_serialized());
}
bytes.extend_from_slice(&(root_packages.len() as u32).to_le_bytes());
for (req, id) in root_packages {
append_string(&mut bytes, &req.to_string());
let id = ids_to_stored_ids.get(&id).unwrap();
bytes.extend_from_slice(&id.to_le_bytes());
}
for pkg in &snapshot.packages {
let deps_len = pkg.dependencies.len() as u32;
bytes.extend_from_slice(&deps_len.to_le_bytes());
let mut deps: Vec<_> = pkg.dependencies.iter().collect();
deps.sort();
for (req, id) in deps {
append_string(&mut bytes, req);
let id = ids_to_stored_ids.get(&id).unwrap();
bytes.extend_from_slice(&id.to_le_bytes());
}
}
bytes
}
fn deserialize_npm_snapshot(
input: &[u8],
) -> Result<ValidSerializedNpmResolutionSnapshot, AnyError> {
fn parse_id(input: &[u8]) -> Result<(&[u8], NpmPackageId), AnyError> {
let (input, id) = read_string_lossy(input)?;
let id = NpmPackageId::from_serialized(&id)?;
Ok((input, id))
}
#[allow(clippy::needless_lifetimes)] // clippy bug
fn parse_root_package<'a>(
id_to_npm_id: &'a impl Fn(usize) -> Result<NpmPackageId, AnyError>,
) -> impl Fn(&[u8]) -> Result<(&[u8], (PackageReq, NpmPackageId)), AnyError> + 'a
{
|input| {
let (input, req) = read_string_lossy(input)?;
let req = PackageReq::from_str(&req)?;
let (input, id) = read_u32_as_usize(input)?;
Ok((input, (req, id_to_npm_id(id)?)))
}
}
#[allow(clippy::needless_lifetimes)] // clippy bug
fn parse_package_dep<'a>(
id_to_npm_id: &'a impl Fn(usize) -> Result<NpmPackageId, AnyError>,
) -> impl Fn(&[u8]) -> Result<(&[u8], (String, NpmPackageId)), AnyError> + 'a
{
|input| {
let (input, req) = read_string_lossy(input)?;
let (input, id) = read_u32_as_usize(input)?;
Ok((input, (req.into_owned(), id_to_npm_id(id)?)))
}
}
fn parse_package<'a>(
input: &'a [u8],
id: NpmPackageId,
id_to_npm_id: &impl Fn(usize) -> Result<NpmPackageId, AnyError>,
) -> Result<(&'a [u8], SerializedNpmResolutionSnapshotPackage), AnyError> {
let (input, deps_len) = read_u32_as_usize(input)?;
let (input, dependencies) =
parse_hashmap_n_times(input, deps_len, parse_package_dep(id_to_npm_id))?;
Ok((
input,
SerializedNpmResolutionSnapshotPackage {
id,
system: Default::default(),
dist: Default::default(),
dependencies,
optional_dependencies: Default::default(),
bin: None,
scripts: Default::default(),
deprecated: Default::default(),
},
))
}
let (input, packages_len) = read_u32_as_usize(input)?;
// get a hashmap of all the npm package ids to their serialized ids
let (input, data_ids_to_npm_ids) =
parse_vec_n_times(input, packages_len, parse_id)
.context("deserializing id")?;
let data_id_to_npm_id = |id: usize| {
data_ids_to_npm_ids
.get(id)
.cloned()
.ok_or_else(|| deno_core::anyhow::anyhow!("Invalid npm package id"))
};
let (input, root_packages_len) = read_u32_as_usize(input)?;
let (input, root_packages) = parse_hashmap_n_times(
input,
root_packages_len,
parse_root_package(&data_id_to_npm_id),
)
.context("deserializing root package")?;
let (input, packages) =
parse_vec_n_times_with_index(input, packages_len, |input, index| {
parse_package(input, data_id_to_npm_id(index)?, &data_id_to_npm_id)
})
.context("deserializing package")?;
if !input.is_empty() {
bail!("Unexpected data left over");
}
Ok(
SerializedNpmResolutionSnapshot {
packages,
root_packages,
}
// this is ok because we have already verified that all the
// identifiers found in the snapshot are valid via the
// npm package id -> npm package id mapping
.into_valid_unsafe(),
)
}
fn serialize_media_type(media_type: MediaType) -> u8 {
match media_type {
MediaType::JavaScript => 0,
MediaType::Jsx => 1,
MediaType::Mjs => 2,
MediaType::Cjs => 3,
MediaType::TypeScript => 4,
MediaType::Mts => 5,
MediaType::Cts => 6,
MediaType::Dts => 7,
MediaType::Dmts => 8,
MediaType::Dcts => 9,
MediaType::Tsx => 10,
MediaType::Json => 11,
MediaType::Wasm => 12,
MediaType::TsBuildInfo => 13,
MediaType::SourceMap => 14,
MediaType::Unknown => 15,
}
}
fn deserialize_media_type(value: u8) -> Result<MediaType, AnyError> {
match value {
0 => Ok(MediaType::JavaScript),
1 => Ok(MediaType::Jsx),
2 => Ok(MediaType::Mjs),
3 => Ok(MediaType::Cjs),
4 => Ok(MediaType::TypeScript),
5 => Ok(MediaType::Mts),
6 => Ok(MediaType::Cts),
7 => Ok(MediaType::Dts),
8 => Ok(MediaType::Dmts),
9 => Ok(MediaType::Dcts),
10 => Ok(MediaType::Tsx),
11 => Ok(MediaType::Json),
12 => Ok(MediaType::Wasm),
13 => Ok(MediaType::TsBuildInfo),
14 => Ok(MediaType::SourceMap),
15 => Ok(MediaType::Unknown),
_ => bail!("Unknown media type value: {}", value),
}
}
fn parse_hashmap_n_times<TKey: std::cmp::Eq + std::hash::Hash, TValue>(
mut input: &[u8],
times: usize,
parse: impl Fn(&[u8]) -> Result<(&[u8], (TKey, TValue)), AnyError>,
) -> Result<(&[u8], HashMap<TKey, TValue>), AnyError> {
let mut results = HashMap::with_capacity(times);
for _ in 0..times {
let result = parse(input);
let (new_input, (key, value)) = result?;
results.insert(key, value);
input = new_input;
}
Ok((input, results))
}
fn parse_vec_n_times<TResult>(
input: &[u8],
times: usize,
parse: impl Fn(&[u8]) -> Result<(&[u8], TResult), AnyError>,
) -> Result<(&[u8], Vec<TResult>), AnyError> {
parse_vec_n_times_with_index(input, times, |input, _index| parse(input))
}
fn parse_vec_n_times_with_index<TResult>(
mut input: &[u8],
times: usize,
parse: impl Fn(&[u8], usize) -> Result<(&[u8], TResult), AnyError>,
) -> Result<(&[u8], Vec<TResult>), AnyError> {
let mut results = Vec::with_capacity(times);
for i in 0..times {
let result = parse(input, i);
let (new_input, result) = result?;
results.push(result);
input = new_input;
}
Ok((input, results))
}
fn read_bytes(input: &[u8], len: usize) -> Result<(&[u8], &[u8]), AnyError> {
if input.len() < len {
bail!("Unexpected end of data.",);
}
let (len_bytes, input) = input.split_at(len);
Ok((input, len_bytes))
}
fn read_string_lossy(input: &[u8]) -> Result<(&[u8], Cow<str>), AnyError> {
let (input, str_len) = read_u32_as_usize(input)?;
let (input, data_bytes) = read_bytes(input, str_len)?;
Ok((input, String::from_utf8_lossy(data_bytes)))
}
fn read_u32_as_usize(input: &[u8]) -> Result<(&[u8], usize), AnyError> {
let (input, len_bytes) = read_bytes(input, 4)?;
let len = u32::from_le_bytes(len_bytes.try_into()?);
Ok((input, len as usize))
}
fn read_u64(input: &[u8]) -> Result<(&[u8], u64), AnyError> {
let (input, len_bytes) = read_bytes(input, 8)?;
let len = u64::from_le_bytes(len_bytes.try_into()?);
Ok((input, len))
}

View file

@ -7,6 +7,7 @@ use std::fs::File;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::ops::Range;
use std::path::Path;
use std::path::PathBuf;
use std::rc::Rc;
@ -67,6 +68,26 @@ impl VfsBuilder {
})
}
pub fn set_new_root_path(
&mut self,
root_path: PathBuf,
) -> Result<(), AnyError> {
let root_path = canonicalize_path(&root_path)?;
self.root_path = root_path;
self.root_dir = VirtualDirectory {
name: self
.root_path
.file_stem()
.map(|s| s.to_string_lossy().into_owned())
.unwrap_or("root".to_string()),
entries: vec![VfsEntry::Dir(VirtualDirectory {
name: std::mem::take(&mut self.root_dir.name),
entries: std::mem::take(&mut self.root_dir.entries),
})],
};
Ok(())
}
pub fn with_root_dir<R>(
&mut self,
with_root: impl FnOnce(&mut VirtualDirectory) -> R,
@ -119,7 +140,7 @@ impl VfsBuilder {
// inline the symlink and make the target file
let file_bytes = std::fs::read(&target)
.with_context(|| format!("Reading {}", path.display()))?;
self.add_file(&path, file_bytes)?;
self.add_file_with_data_inner(&path, file_bytes)?;
} else {
log::warn!(
"{} Symlink target is outside '{}'. Excluding symlink at '{}' with target '{}'.",
@ -191,16 +212,32 @@ impl VfsBuilder {
self.add_file_at_path_not_symlink(&target_path)
}
pub fn add_file_at_path_not_symlink(
fn add_file_at_path_not_symlink(
&mut self,
path: &Path,
) -> Result<(), AnyError> {
let file_bytes = std::fs::read(path)
.with_context(|| format!("Reading {}", path.display()))?;
self.add_file(path, file_bytes)
self.add_file_with_data_inner(path, file_bytes)
}
fn add_file(&mut self, path: &Path, data: Vec<u8>) -> Result<(), AnyError> {
pub fn add_file_with_data(
&mut self,
path: &Path,
data: Vec<u8>,
) -> Result<(), AnyError> {
let target_path = canonicalize_path(path)?;
if target_path != path {
self.add_symlink(path, &target_path)?;
}
self.add_file_with_data_inner(&target_path, data)
}
fn add_file_with_data_inner(
&mut self,
path: &Path,
data: Vec<u8>,
) -> Result<(), AnyError> {
log::debug!("Adding file '{}'", path.display());
let checksum = util::checksum::gen(&[&data]);
let offset = if let Some(offset) = self.file_offsets.get(&checksum) {
@ -249,8 +286,15 @@ impl VfsBuilder {
path.display(),
target.display()
);
let dest = self.path_relative_root(target)?;
if dest == self.path_relative_root(path)? {
let relative_target = self.path_relative_root(target)?;
let relative_path = match self.path_relative_root(path) {
Ok(path) => path,
Err(StripRootError { .. }) => {
// ignore if the original path is outside the root directory
return Ok(());
}
};
if relative_target == relative_path {
// it's the same, ignore
return Ok(());
}
@ -263,7 +307,7 @@ impl VfsBuilder {
insert_index,
VfsEntry::Symlink(VirtualSymlink {
name: name.to_string(),
dest_parts: dest
dest_parts: relative_target
.components()
.map(|c| c.as_os_str().to_string_lossy().to_string())
.collect::<Vec<_>>(),
@ -751,14 +795,14 @@ impl deno_io::fs::File for FileBackedVfsFile {
#[derive(Debug)]
pub struct FileBackedVfs {
file: Mutex<Vec<u8>>,
vfs_data: Cow<'static, [u8]>,
fs_root: VfsRoot,
}
impl FileBackedVfs {
pub fn new(file: Vec<u8>, fs_root: VfsRoot) -> Self {
pub fn new(data: Cow<'static, [u8]>, fs_root: VfsRoot) -> Self {
Self {
file: Mutex::new(file),
vfs_data: data,
fs_root,
}
}
@ -827,10 +871,15 @@ impl FileBackedVfs {
Ok(path)
}
pub fn read_file_all(&self, file: &VirtualFile) -> std::io::Result<Vec<u8>> {
let mut buf = vec![0; file.len as usize];
self.read_file(file, 0, &mut buf)?;
Ok(buf)
pub fn read_file_all(
&self,
file: &VirtualFile,
) -> std::io::Result<Cow<'static, [u8]>> {
let read_range = self.get_read_range(file, 0, file.len)?;
match &self.vfs_data {
Cow::Borrowed(data) => Ok(Cow::Borrowed(&data[read_range])),
Cow::Owned(data) => Ok(Cow::Owned(data[read_range].to_vec())),
}
}
pub fn read_file(
@ -839,18 +888,27 @@ impl FileBackedVfs {
pos: u64,
buf: &mut [u8],
) -> std::io::Result<usize> {
let data = self.file.lock();
let read_range = self.get_read_range(file, pos, buf.len() as u64)?;
buf.copy_from_slice(&self.vfs_data[read_range]);
Ok(buf.len())
}
fn get_read_range(
&self,
file: &VirtualFile,
pos: u64,
len: u64,
) -> std::io::Result<Range<usize>> {
let data = &self.vfs_data;
let start = self.fs_root.start_file_offset + file.offset + pos;
let end = start + buf.len() as u64;
let end = start + len;
if end > data.len() as u64 {
return Err(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"unexpected EOF",
));
}
buf.copy_from_slice(&data[start as usize..end as usize]);
Ok(buf.len())
Ok(start as usize..end as usize)
}
pub fn dir_entry(&self, path: &Path) -> std::io::Result<&VirtualDirectory> {
@ -888,7 +946,7 @@ mod test {
#[track_caller]
fn read_file(vfs: &FileBackedVfs, path: &Path) -> String {
let file = vfs.file_entry(path).unwrap();
String::from_utf8(vfs.read_file_all(file).unwrap()).unwrap()
String::from_utf8(vfs.read_file_all(file).unwrap().into_owned()).unwrap()
}
#[test]
@ -901,20 +959,23 @@ mod test {
let src_path = src_path.to_path_buf();
let mut builder = VfsBuilder::new(src_path.clone()).unwrap();
builder
.add_file(&src_path.join("a.txt"), "data".into())
.add_file_with_data_inner(&src_path.join("a.txt"), "data".into())
.unwrap();
builder
.add_file(&src_path.join("b.txt"), "data".into())
.add_file_with_data_inner(&src_path.join("b.txt"), "data".into())
.unwrap();
assert_eq!(builder.files.len(), 1); // because duplicate data
builder
.add_file(&src_path.join("c.txt"), "c".into())
.add_file_with_data_inner(&src_path.join("c.txt"), "c".into())
.unwrap();
builder
.add_file(&src_path.join("sub_dir").join("d.txt"), "d".into())
.add_file_with_data_inner(
&src_path.join("sub_dir").join("d.txt"),
"d".into(),
)
.unwrap();
builder
.add_file(&src_path.join("e.txt"), "e".into())
.add_file_with_data_inner(&src_path.join("e.txt"), "e".into())
.unwrap();
builder
.add_symlink(
@ -1031,7 +1092,7 @@ mod test {
(
dest_path.to_path_buf(),
FileBackedVfs::new(
data,
Cow::Owned(data),
VfsRoot {
dir: root_dir,
root_path: dest_path.to_path_buf(),
@ -1082,7 +1143,7 @@ mod test {
let temp_path = temp_dir.path().canonicalize();
let mut builder = VfsBuilder::new(temp_path.to_path_buf()).unwrap();
builder
.add_file(
.add_file_with_data_inner(
temp_path.join("a.txt").as_path(),
"0123456789".to_string().into_bytes(),
)

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

@ -5,6 +5,7 @@ use crate::args::CompileFlags;
use crate::args::Flags;
use crate::factory::CliFactory;
use crate::http_util::HttpClientProvider;
use crate::standalone::binary::StandaloneRelativeFileBaseUrl;
use crate::standalone::is_standalone_binary;
use deno_ast::ModuleSpecifier;
use deno_core::anyhow::bail;
@ -14,7 +15,6 @@ use deno_core::error::AnyError;
use deno_core::resolve_url_or_path;
use deno_graph::GraphKind;
use deno_terminal::colors;
use eszip::EszipRelativeFileBaseUrl;
use rand::Rng;
use std::path::Path;
use std::path::PathBuf;
@ -29,7 +29,6 @@ pub async fn compile(
let factory = CliFactory::from_flags(flags);
let cli_options = factory.cli_options()?;
let module_graph_creator = factory.module_graph_creator().await?;
let parsed_source_cache = factory.parsed_source_cache();
let binary_writer = factory.create_compile_binary_writer().await?;
let http_client = factory.http_client_provider();
let module_specifier = cli_options.resolve_main_module()?;
@ -54,6 +53,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,
@ -70,7 +79,7 @@ pub async fn compile(
let graph = if cli_options.type_check_mode().is_true() {
// In this case, the previous graph creation did type checking, which will
// create a module graph with types information in it. We don't want to
// store that in the eszip so create a code only module graph from scratch.
// store that in the binary so create a code only module graph from scratch.
module_graph_creator
.create_graph(GraphKind::CodeOnly, module_roots)
.await?
@ -81,11 +90,6 @@ pub async fn compile(
let ts_config_for_emit = cli_options
.resolve_ts_config_for_emit(deno_config::deno_json::TsConfigType::Emit)?;
check_warn_tsconfig(&ts_config_for_emit);
let (transpile_options, emit_options) =
crate::args::ts_config_to_transpile_and_emit_options(
ts_config_for_emit.ts_config,
)?;
let parser = parsed_source_cache.as_capturing_parser();
let root_dir_url = resolve_root_dir_from_specifiers(
cli_options.workspace().root_dir(),
graph.specifiers().map(|(s, _)| s).chain(
@ -96,17 +100,6 @@ pub async fn compile(
),
);
log::debug!("Binary root dir: {}", root_dir_url);
let root_dir_url = EszipRelativeFileBaseUrl::new(&root_dir_url);
let eszip = eszip::EszipV2::from_graph(eszip::FromGraphOptions {
graph,
parser,
transpile_options,
emit_options,
// make all the modules relative to the root folder
relative_file_base: Some(root_dir_url),
npm_packages: None,
})?;
log::info!(
"{} {} to {}",
colors::green("Compile"),
@ -133,15 +126,18 @@ pub async fn compile(
let write_result = binary_writer
.write_bin(
file,
eszip,
root_dir_url,
&graph,
StandaloneRelativeFileBaseUrl::from(&root_dir_url),
module_specifier,
&compile_flags,
cli_options,
)
.await
.with_context(|| {
format!("Writing temporary file '{}'", temp_path.display())
format!(
"Writing deno compile executable to temporary file '{}'",
temp_path.display()
)
});
// set it as executable

View file

@ -571,7 +571,7 @@ pub async fn cover_files(
| MediaType::Cjs
| MediaType::Mjs
| MediaType::Json => None,
MediaType::Dts | MediaType::Dmts | MediaType::Dcts => Some(Vec::new()),
MediaType::Dts | MediaType::Dmts | MediaType::Dcts => Some(String::new()),
MediaType::TypeScript
| MediaType::Jsx
| MediaType::Mts
@ -593,8 +593,7 @@ pub async fn cover_files(
}
};
let runtime_code: String = match transpiled_code {
Some(code) => String::from_utf8(code)
.with_context(|| format!("Failed decoding {}", file.specifier))?,
Some(code) => code,
None => original_source.to_string(),
};

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

@ -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(())

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(),
);
}
}
@ -447,16 +367,20 @@ pub async fn add(
Default::default(),
None,
);
let npmrc = cli_factory.cli_options().unwrap().npmrc();
deps_file_fetcher.set_download_log_level(log::Level::Trace);
let deps_file_fetcher = Arc::new(deps_file_fetcher);
let jsr_resolver = Arc::new(JsrFetchResolver::new(deps_file_fetcher.clone()));
let npm_resolver = Arc::new(NpmFetchResolver::new(deps_file_fetcher));
let npm_resolver =
Arc::new(NpmFetchResolver::new(deps_file_fetcher, npmrc.clone()));
let mut selected_packages = Vec::with_capacity(add_flags.packages.len());
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,18 +468,11 @@ 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()?;
}
npm_install_after_modification(flags, Some(jsr_resolver)).await?;
@ -582,10 +499,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() {
@ -603,9 +520,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,
@ -614,7 +533,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 {
@ -623,11 +542,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,
@ -639,18 +562,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,
@ -705,9 +628,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 => {
@ -725,49 +648,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);
}
}
@ -778,7 +700,7 @@ pub async fn remove(
log::info!("Removed {}", crate::colors::green(package));
}
for config in configs.into_iter().flatten() {
config.commit().await?;
config.commit()?;
}
npm_install_after_modification(flags, None).await?;
@ -805,88 +727,11 @@ async fn npm_install_after_modification(
// npm install
cache_deps::cache_top_level_deps(&cli_factory, jsr_resolver).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(),
})
}
}
// 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!(),
}
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)]
@ -896,48 +741,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

@ -75,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());
}
}
@ -82,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

@ -579,6 +579,10 @@ pub async fn upgrade(
let output_exe_path =
full_path_output_flag.as_ref().unwrap_or(&current_exe_path);
#[cfg(windows)]
kill_running_deno_lsp_processes();
let output_result = if *output_exe_path == current_exe_path {
replace_exe(&new_exe_path, output_exe_path)
} else {
@ -913,7 +917,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)
@ -966,6 +970,34 @@ fn check_windows_access_denied_error(
})
}
#[cfg(windows)]
fn kill_running_deno_lsp_processes() {
// limit this to `deno lsp` invocations to avoid killing important programs someone might be running
let is_debug = log::log_enabled!(log::Level::Debug);
let get_pipe = || {
if is_debug {
std::process::Stdio::inherit()
} else {
std::process::Stdio::null()
}
};
let _ = Command::new("powershell.exe")
.args([
"-Command",
r#"Get-WmiObject Win32_Process | Where-Object {
$_.Name -eq 'deno.exe' -and
$_.CommandLine -match '^(?:\"[^\"]+\"|\S+)\s+lsp\b'
} | ForEach-Object {
if ($_.Terminate()) {
Write-Host 'Terminated:' $_.ProcessId
}
}"#,
])
.stdout(get_pipe())
.stderr(get_pipe())
.output();
}
fn set_exe_permissions(
current_exe_path: &Path,
output_exe_path: &Path,

View file

@ -516,7 +516,6 @@ delete Object.prototype.__proto__;
/** @typedef {{
* ls: ts.LanguageService & { [k:string]: any },
* compilerOptions: ts.CompilerOptions,
* forceEnabledVerbatimModuleSyntax: boolean,
* }} LanguageServiceEntry */
/** @type {{ unscoped: LanguageServiceEntry, byScope: Map<string, LanguageServiceEntry> }} */
const languageServiceEntries = {
@ -1026,7 +1025,7 @@ delete Object.prototype.__proto__;
: ts.sortAndDeduplicateDiagnostics(
checkFiles.map((s) => program.getSemanticDiagnostics(s)).flat(),
)),
].filter(filterMapDiagnostic.bind(null, false));
].filter(filterMapDiagnostic);
// emit the tsbuildinfo file
// @ts-ignore: emitBuildInfo is not exposed (https://github.com/microsoft/TypeScript/issues/49871)
@ -1041,28 +1040,11 @@ delete Object.prototype.__proto__;
debug("<<< exec stop");
}
/**
* @param {boolean} isLsp
* @param {ts.Diagnostic} diagnostic
*/
function filterMapDiagnostic(isLsp, diagnostic) {
/** @param {ts.Diagnostic} diagnostic */
function filterMapDiagnostic(diagnostic) {
if (IGNORED_DIAGNOSTICS.includes(diagnostic.code)) {
return false;
}
if (isLsp) {
// TS1484: `...` is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.
// We force-enable `verbatimModuleSyntax` in the LSP so the `type`
// modifier is used when auto-importing types. But we don't want this
// diagnostic unless it was explicitly enabled by the user.
if (diagnostic.code == 1484) {
const entry = (lastRequestScope
? languageServiceEntries.byScope.get(lastRequestScope)
: null) ?? languageServiceEntries.unscoped;
if (entry.forceEnabledVerbatimModuleSyntax) {
return false;
}
}
}
// make the diagnostic for using an `export =` in an es module a warning
if (diagnostic.code === 1203) {
diagnostic.category = ts.DiagnosticCategory.Warning;
@ -1159,12 +1141,10 @@ delete Object.prototype.__proto__;
"strict": true,
"target": "esnext",
"useDefineForClassFields": true,
"verbatimModuleSyntax": true,
"jsx": "react",
"jsxFactory": "React.createElement",
"jsxFragmentFactory": "React.Fragment",
}),
forceEnabledVerbatimModuleSyntax: true,
};
setLogDebug(enableDebugLogging, "TSLS");
debug("serverInit()");
@ -1230,17 +1210,8 @@ delete Object.prototype.__proto__;
const ls = oldEntry
? oldEntry.ls
: ts.createLanguageService(host, documentRegistry);
let forceEnabledVerbatimModuleSyntax = false;
if (!config["verbatimModuleSyntax"]) {
config["verbatimModuleSyntax"] = true;
forceEnabledVerbatimModuleSyntax = true;
}
const compilerOptions = lspTsConfigToCompilerOptions(config);
newByScope.set(scope, {
ls,
compilerOptions,
forceEnabledVerbatimModuleSyntax,
});
newByScope.set(scope, { ls, compilerOptions });
languageServiceEntries.byScope.delete(scope);
}
for (const oldEntry of languageServiceEntries.byScope.values()) {
@ -1305,7 +1276,7 @@ delete Object.prototype.__proto__;
...ls.getSemanticDiagnostics(specifier),
...ls.getSuggestionDiagnostics(specifier),
...ls.getSyntacticDiagnostics(specifier),
].filter(filterMapDiagnostic.bind(null, true)));
].filter(filterMapDiagnostic));
}
return respond(id, diagnosticMap);
} catch (e) {

View file

@ -5,6 +5,7 @@ use crate::args::TypeCheckMode;
use crate::cache::FastInsecureHasher;
use crate::node;
use crate::npm::CliNpmResolver;
use crate::npm::ResolvePkgFolderFromDenoReqError;
use crate::util::checksum;
use crate::util::path::mapped_specifier_for_tsc;
@ -35,6 +36,7 @@ use deno_runtime::deno_node::NodeResolver;
use deno_semver::npm::NpmPackageReqReference;
use node_resolver::errors::NodeJsErrorCode;
use node_resolver::errors::NodeJsErrorCoded;
use node_resolver::errors::ResolvePkgSubpathFromDenoModuleError;
use node_resolver::NodeModuleKind;
use node_resolver::NodeResolution;
use node_resolver::NodeResolutionMode;
@ -45,6 +47,7 @@ use std::fmt;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use thiserror::Error;
mod diagnostics;
@ -688,12 +691,30 @@ fn op_resolve_inner(
Some(ResolutionResolved { specifier, .. }) => {
resolve_graph_specifier_types(specifier, &referrer, state)?
}
_ => resolve_non_graph_specifier_types(
&specifier,
&referrer,
referrer_kind,
state,
)?,
_ => {
match resolve_non_graph_specifier_types(
&specifier,
&referrer,
referrer_kind,
state,
) {
Ok(maybe_result) => maybe_result,
Err(
err @ ResolveNonGraphSpecifierTypesError::ResolvePkgFolderFromDenoReq(
ResolvePkgFolderFromDenoReqError::Managed(_),
),
) => {
// it's most likely requesting the jsxImportSource, which isn't loaded
// into the graph when not using jsx, so just ignore this error
if specifier.ends_with("/jsx-runtime") {
None
} else {
return Err(err.into());
}
}
Err(err) => return Err(err.into()),
}
}
};
let result = match maybe_result {
Some((specifier, media_type)) => {
@ -818,12 +839,23 @@ fn resolve_graph_specifier_types(
}
}
#[derive(Debug, Error)]
enum ResolveNonGraphSpecifierTypesError {
#[error(transparent)]
ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError),
#[error(transparent)]
ResolvePkgSubpathFromDenoModule(#[from] ResolvePkgSubpathFromDenoModuleError),
}
fn resolve_non_graph_specifier_types(
raw_specifier: &str,
referrer: &ModuleSpecifier,
referrer_kind: NodeModuleKind,
state: &State,
) -> Result<Option<(ModuleSpecifier, MediaType)>, AnyError> {
) -> Result<
Option<(ModuleSpecifier, MediaType)>,
ResolveNonGraphSpecifierTypesError,
> {
let npm = match state.maybe_npm.as_ref() {
Some(npm) => npm,
None => return Ok(None), // we only support non-graph types for npm packages

View file

@ -255,7 +255,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,
@ -1138,6 +1142,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 {
@ -1327,6 +1355,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 {
@ -1582,6 +1632,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,28 @@ 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]) }
}
/// Converts an `Arc<u8>` to an `Arc<str>` if able.
#[allow(dead_code)]
pub fn arc_u8_to_arc_str(
arc_u8: Arc<[u8]>,
) -> Result<Arc<str>, std::str::Utf8Error> {
// Check that the string is valid UTF-8.
std::str::from_utf8(&arc_u8)?;
// SAFETY: the string is valid UTF-8, and the layout Arc<[u8]> is the same as
// Arc<str>. This is proven by the From<Arc<str>> impl for Arc<[u8]> from the
// standard library.
Ok(unsafe {
std::mem::transmute::<std::sync::Arc<[u8]>, std::sync::Arc<str>>(arc_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.165.0"
version = "0.168.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.103.0"
version = "0.106.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.40.0"
version = "0.43.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
@ -18,3 +18,4 @@ deno_core.workspace = true
deno_webgpu.workspace = true
image = { version = "0.24.7", default-features = false, features = ["png"] }
serde = { workspace = true, features = ["derive"] }
thiserror.workspace = true

View file

@ -1,7 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::ToJsBuffer;
use image::imageops::FilterType;
@ -13,6 +11,14 @@ use serde::Deserialize;
use serde::Serialize;
use std::path::PathBuf;
#[derive(Debug, thiserror::Error)]
pub enum CanvasError {
#[error("Color type '{0:?}' not supported")]
UnsupportedColorType(ColorType),
#[error(transparent)]
Image(#[from] image::ImageError),
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
enum ImageResizeQuality {
@ -43,7 +49,7 @@ struct ImageProcessArgs {
fn op_image_process(
#[buffer] buf: &[u8],
#[serde] args: ImageProcessArgs,
) -> Result<ToJsBuffer, AnyError> {
) -> ToJsBuffer {
let view =
RgbaImage::from_vec(args.width, args.height, buf.to_vec()).unwrap();
@ -105,7 +111,7 @@ fn op_image_process(
}
}
Ok(image_out.to_vec().into())
image_out.to_vec().into()
}
#[derive(Debug, Serialize)]
@ -117,17 +123,16 @@ struct DecodedPng {
#[op2]
#[serde]
fn op_image_decode_png(#[buffer] buf: &[u8]) -> Result<DecodedPng, AnyError> {
fn op_image_decode_png(
#[buffer] buf: &[u8],
) -> Result<DecodedPng, CanvasError> {
let png = image::codecs::png::PngDecoder::new(buf)?;
let (width, height) = png.dimensions();
// TODO(@crowlKats): maybe use DynamicImage https://docs.rs/image/0.24.7/image/enum.DynamicImage.html ?
if png.color_type() != ColorType::Rgba8 {
return Err(type_error(format!(
"Color type '{:?}' not supported",
png.color_type()
)));
return Err(CanvasError::UnsupportedColorType(png.color_type()));
}
// read_image will assert that the buffer is the correct size, so we need to fill it with zeros

View file

@ -84,6 +84,7 @@ const {
NumberIsInteger,
NumberIsNaN,
NumberParseInt,
NumberParseFloat,
NumberPrototypeToFixed,
NumberPrototypeToString,
NumberPrototypeValueOf,
@ -2652,6 +2653,7 @@ const HSL_PATTERN = new SafeRegExp(
);
function parseCssColor(colorString) {
colorString = StringPrototypeToLowerCase(colorString);
if (colorKeywords.has(colorString)) {
colorString = colorKeywords.get(colorString);
}
@ -3010,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.
@ -3257,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.171.0"
version = "0.174.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

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