0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-03 17:34:47 -05:00

Merge branch 'main' into lint_plugins

This commit is contained in:
Bartek Iwańczuk 2024-12-21 02:28:39 +01:00
commit d3e41a770a
No known key found for this signature in database
GPG key ID: 0C6BCDDC3B3AD750
133 changed files with 4218 additions and 970 deletions

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 = 31;
const cacheVersion = 32;
const ubuntuX86Runner = "ubuntu-24.04";
const ubuntuX86XlRunner = "ubuntu-24.04-xl";

View file

@ -184,8 +184,8 @@ jobs:
~/.cargo/registry/index
~/.cargo/registry/cache
~/.cargo/git/db
key: '31-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
restore-keys: '31-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-'
key: '32-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
restore-keys: '32-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-'
if: '!(matrix.skip)'
- uses: dsherret/rust-toolchain-file@v1
if: '!(matrix.skip)'
@ -379,7 +379,7 @@ jobs:
!./target/*/*.zip
!./target/*/*.tar.gz
key: never_saved
restore-keys: '31-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-'
restore-keys: '32-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
@ -689,7 +689,7 @@ jobs:
!./target/*/gn_root
!./target/*/*.zip
!./target/*/*.tar.gz
key: '31-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
key: '32-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
publish-canary:
name: publish canary
runs-on: ubuntu-24.04

236
Cargo.lock generated
View file

@ -380,7 +380,7 @@ dependencies = [
"rustversion",
"serde",
"sync_wrapper",
"tower",
"tower 0.4.13",
"tower-layer",
"tower-service",
]
@ -677,6 +677,28 @@ dependencies = [
"itoa",
]
[[package]]
name = "capacity_builder"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f2d24a6dcf0cd402a21b65d35340f3a49ff3475dc5fdac91d22d2733e6641c6"
dependencies = [
"capacity_builder_macros",
"ecow",
"hipstr",
"itoa",
]
[[package]]
name = "capacity_builder_macros"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5"
dependencies = [
"quote",
"syn 2.0.87",
]
[[package]]
name = "caseless"
version = "0.2.1"
@ -728,6 +750,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chrono"
version = "0.4.37"
@ -1224,7 +1252,7 @@ dependencies = [
"boxed_error",
"bytes",
"cache_control",
"capacity_builder",
"capacity_builder 0.5.0",
"chrono",
"clap",
"clap_complete",
@ -1391,7 +1419,7 @@ dependencies = [
[[package]]
name = "deno_bench_util"
version = "0.177.0"
version = "0.178.0"
dependencies = [
"bencher",
"deno_core",
@ -1400,7 +1428,7 @@ dependencies = [
[[package]]
name = "deno_broadcast_channel"
version = "0.177.0"
version = "0.178.0"
dependencies = [
"async-trait",
"deno_core",
@ -1411,7 +1439,7 @@ dependencies = [
[[package]]
name = "deno_cache"
version = "0.115.0"
version = "0.116.0"
dependencies = [
"async-trait",
"deno_core",
@ -1452,7 +1480,7 @@ dependencies = [
[[package]]
name = "deno_canvas"
version = "0.52.0"
version = "0.53.0"
dependencies = [
"deno_core",
"deno_webgpu",
@ -1463,8 +1491,14 @@ dependencies = [
[[package]]
name = "deno_config"
<<<<<<< HEAD
version = "0.39.3"
source = "git+https://github.com/denoland/deno_config.git?branch=deno_lint_rules#13f6ff94ce36896b29c2a84386e2173bb18a036d"
=======
version = "0.41.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8afa3beb6b9e0604cfe0380d30f88c5b758d44e228d5a5fc42ae637ccfb7d089"
>>>>>>> main
dependencies = [
"anyhow",
"deno_package_json",
@ -1486,16 +1520,16 @@ dependencies = [
[[package]]
name = "deno_console"
version = "0.183.0"
version = "0.184.0"
dependencies = [
"deno_core",
]
[[package]]
name = "deno_core"
version = "0.326.0"
version = "0.327.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed157162dc5320a2b46ffeeaec24788339df0f2437cfaea78a8d82696715ad7f"
checksum = "eaf8dff204b9c2415deb47b9f30d4d38b0925d0d88f1f9074e8e76f59e6d7ded"
dependencies = [
"anyhow",
"az",
@ -1503,7 +1537,7 @@ dependencies = [
"bit-set",
"bit-vec",
"bytes",
"capacity_builder",
"capacity_builder 0.1.3",
"cooked-waker",
"deno_core_icudata",
"deno_ops",
@ -1535,7 +1569,7 @@ checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695"
[[package]]
name = "deno_cron"
version = "0.63.0"
version = "0.64.0"
dependencies = [
"anyhow",
"async-trait",
@ -1548,7 +1582,7 @@ dependencies = [
[[package]]
name = "deno_crypto"
version = "0.197.0"
version = "0.198.0"
dependencies = [
"aes",
"aes-gcm",
@ -1638,7 +1672,7 @@ dependencies = [
[[package]]
name = "deno_fetch"
version = "0.207.0"
version = "0.208.0"
dependencies = [
"base64 0.21.7",
"bytes",
@ -1650,6 +1684,7 @@ dependencies = [
"dyn-clone",
"error_reporter",
"fast-socks5",
"h2 0.4.4",
"hickory-resolver",
"http 1.1.0",
"http-body-util",
@ -1666,14 +1701,14 @@ dependencies = [
"tokio-rustls",
"tokio-socks",
"tokio-util",
"tower",
"tower 0.5.2",
"tower-http",
"tower-service",
]
[[package]]
name = "deno_ffi"
version = "0.170.0"
version = "0.171.0"
dependencies = [
"deno_core",
"deno_permissions",
@ -1693,7 +1728,7 @@ dependencies = [
[[package]]
name = "deno_fs"
version = "0.93.0"
version = "0.94.0"
dependencies = [
"async-trait",
"base32",
@ -1716,12 +1751,13 @@ dependencies = [
[[package]]
name = "deno_graph"
version = "0.86.3"
version = "0.86.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc78ed0b4bbcb4197300f0d6e7d1edc2d2c5019cdb9dedba7ff229158441885b"
checksum = "f669d96d63841d9ba10f86b161d898678ce05bc1e3c9ee1c1f7449a68eed2b64"
dependencies = [
"anyhow",
"async-trait",
"capacity_builder 0.5.0",
"data-url",
"deno_ast",
"deno_semver",
@ -1746,7 +1782,7 @@ dependencies = [
[[package]]
name = "deno_http"
version = "0.181.0"
version = "0.182.0"
dependencies = [
"async-compression",
"async-trait",
@ -1785,7 +1821,7 @@ dependencies = [
[[package]]
name = "deno_io"
version = "0.93.0"
version = "0.94.0"
dependencies = [
"async-trait",
"deno_core",
@ -1806,7 +1842,7 @@ dependencies = [
[[package]]
name = "deno_kv"
version = "0.91.0"
version = "0.92.0"
dependencies = [
"anyhow",
"async-trait",
@ -1856,9 +1892,9 @@ dependencies = [
[[package]]
name = "deno_lockfile"
version = "0.23.2"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "559c19feb00af0c34f0bd4a20e56e12463fafd5c5069d6005f3ce33008027eea"
checksum = "632e835a53ed667d62fdd766c5780fe8361c831d3e3fbf1a760a0b7896657587"
dependencies = [
"deno_semver",
"serde",
@ -1879,7 +1915,7 @@ dependencies = [
[[package]]
name = "deno_napi"
version = "0.114.0"
version = "0.115.0"
dependencies = [
"deno_core",
"deno_permissions",
@ -1907,7 +1943,7 @@ dependencies = [
[[package]]
name = "deno_net"
version = "0.175.0"
version = "0.176.0"
dependencies = [
"deno_core",
"deno_permissions",
@ -1915,6 +1951,7 @@ dependencies = [
"hickory-proto",
"hickory-resolver",
"pin-project",
"quinn",
"rustls-tokio-stream",
"serde",
"socket2",
@ -1924,7 +1961,7 @@ dependencies = [
[[package]]
name = "deno_node"
version = "0.120.0"
version = "0.122.0"
dependencies = [
"aead-gcm-stream",
"aes",
@ -2016,12 +2053,13 @@ dependencies = [
[[package]]
name = "deno_npm"
version = "0.26.0"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f125a5dba7839c46394a0a9c835da9fe60f5f412587ab4956a76492a1cc6a8"
checksum = "5f818ad5dc4c206b50b5cfa6f10b4b94b127e15c8342c152768eba40c225ca23"
dependencies = [
"anyhow",
"async-trait",
"capacity_builder 0.5.0",
"deno_error",
"deno_lockfile",
"deno_semver",
"futures",
@ -2043,6 +2081,7 @@ dependencies = [
"boxed_error",
"deno_cache_dir",
"deno_core",
"deno_error",
"deno_npm",
"deno_semver",
"deno_unsync",
@ -2064,9 +2103,9 @@ dependencies = [
[[package]]
name = "deno_ops"
version = "0.202.0"
version = "0.203.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dd8ac1af251e292388e516dd339b9a3b982a6d1e7f8644c08e34671ca39003c"
checksum = "b146ca74cac431843486ade58e2accc16c11315fb2c6934590a52a73c56b7ec3"
dependencies = [
"proc-macro-rules",
"proc-macro2",
@ -2080,10 +2119,11 @@ dependencies = [
[[package]]
name = "deno_package_json"
version = "0.2.1"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80b0a3d81c592624a1ae15332a04b4dc2b7c163ef1dfc7c60171f736d1babdf5"
checksum = "81d72db99fdebfc371d7be16972c18a47daa7a29cb5fbb3900ab2114b1f42d96"
dependencies = [
"boxed_error",
"deno_error",
"deno_path_util",
"deno_semver",
@ -2110,7 +2150,7 @@ dependencies = [
name = "deno_permissions"
version = "0.43.0"
dependencies = [
"capacity_builder",
"capacity_builder 0.5.0",
"deno_core",
"deno_path_util",
"deno_terminal 0.2.0",
@ -2215,11 +2255,14 @@ dependencies = [
[[package]]
name = "deno_semver"
version = "0.6.1"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d1259270d66a5e6d29bb75c9289656541874f79ae9ff6c9f1c790846d5c07ba"
checksum = "4775271f9b5602482698f76d24ea9ed8ba27af7f587a7e9a876916300c542435"
dependencies = [
"capacity_builder 0.5.0",
"deno_error",
"ecow",
"hipstr",
"monch",
"once_cell",
"serde",
@ -2247,7 +2290,7 @@ dependencies = [
[[package]]
name = "deno_telemetry"
version = "0.5.0"
version = "0.6.0"
dependencies = [
"async-trait",
"deno_core",
@ -2288,7 +2331,7 @@ dependencies = [
[[package]]
name = "deno_tls"
version = "0.170.0"
version = "0.171.0"
dependencies = [
"deno_core",
"deno_native_certs",
@ -2321,7 +2364,7 @@ dependencies = [
"serde_json",
"tokio",
"tokio-util",
"tower",
"tower 0.4.13",
"tracing",
]
@ -2338,7 +2381,7 @@ dependencies = [
[[package]]
name = "deno_url"
version = "0.183.0"
version = "0.184.0"
dependencies = [
"deno_bench_util",
"deno_console",
@ -2350,7 +2393,7 @@ dependencies = [
[[package]]
name = "deno_web"
version = "0.214.0"
version = "0.215.0"
dependencies = [
"async-trait",
"base64-simd 0.8.0",
@ -2372,7 +2415,7 @@ dependencies = [
[[package]]
name = "deno_webgpu"
version = "0.150.0"
version = "0.151.0"
dependencies = [
"deno_core",
"raw-window-handle",
@ -2385,7 +2428,7 @@ dependencies = [
[[package]]
name = "deno_webidl"
version = "0.183.0"
version = "0.184.0"
dependencies = [
"deno_bench_util",
"deno_core",
@ -2393,7 +2436,7 @@ dependencies = [
[[package]]
name = "deno_websocket"
version = "0.188.0"
version = "0.189.0"
dependencies = [
"bytes",
"deno_core",
@ -2415,7 +2458,7 @@ dependencies = [
[[package]]
name = "deno_webstorage"
version = "0.178.0"
version = "0.179.0"
dependencies = [
"deno_core",
"deno_web",
@ -2886,6 +2929,15 @@ dependencies = [
"spki",
]
[[package]]
name = "ecow"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42fc0a93992b20c58b99e59d61eaf1635a25bfbe49e4275c34ba0aee98119ba"
dependencies = [
"serde",
]
[[package]]
name = "ed25519"
version = "2.2.3"
@ -3822,6 +3874,17 @@ dependencies = [
"tracing",
]
[[package]]
name = "hipstr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97971ffc85d4c98de12e2608e992a43f5294ebb625fdb045b27c731b64c4c6d6"
dependencies = [
"serde",
"serde_bytes",
"sptr",
]
[[package]]
name = "hkdf"
version = "0.12.4"
@ -4022,9 +4085,9 @@ dependencies = [
[[package]]
name = "hyper-timeout"
version = "0.5.1"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793"
checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
dependencies = [
"hyper 1.4.1",
"hyper-util",
@ -4551,9 +4614,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.153"
version = "0.2.168"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d"
[[package]]
name = "libffi"
@ -4929,7 +4992,7 @@ dependencies = [
[[package]]
name = "napi_sym"
version = "0.113.0"
version = "0.114.0"
dependencies = [
"quote",
"serde",
@ -5905,49 +5968,54 @@ dependencies = [
[[package]]
name = "quinn"
version = "0.11.2"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad"
checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef"
dependencies = [
"bytes",
"pin-project-lite",
"quinn-proto",
"quinn-udp",
"rustc-hash 1.1.0",
"rustc-hash 2.0.0",
"rustls",
"thiserror 1.0.64",
"socket2",
"thiserror 2.0.3",
"tokio",
"tracing",
]
[[package]]
name = "quinn-proto"
version = "0.11.8"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6"
checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d"
dependencies = [
"bytes",
"getrandom",
"rand",
"ring",
"rustc-hash 2.0.0",
"rustls",
"rustls-pki-types",
"slab",
"thiserror 1.0.64",
"thiserror 2.0.3",
"tinyvec",
"tracing",
"web-time",
]
[[package]]
name = "quinn-udp"
version = "0.5.2"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9096629c45860fc7fb143e125eb826b5e721e10be3263160c7d60ca832cf8c46"
checksum = "52cd4b1eff68bf27940dd39811292c49e007f4d0b4c357358dc9b0197be6b527"
dependencies = [
"cfg_aliases 0.2.1",
"libc",
"once_cell",
"socket2",
"tracing",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -6424,6 +6492,9 @@ name = "rustls-pki-types"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
dependencies = [
"web-time",
]
[[package]]
name = "rustls-tokio-stream"
@ -6707,9 +6778,9 @@ dependencies = [
[[package]]
name = "serde_v8"
version = "0.235.0"
version = "0.236.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07afd8b67b4a442ecc2823038473ac0e9e5682de93c213323b60661afdd7eb4"
checksum = "e23b3abce64010612f88f4ff689a959736f99eb3dc0dbf1c7903434b8bd8cda5"
dependencies = [
"num-bigint",
"serde",
@ -6985,6 +7056,12 @@ dependencies = [
"der",
]
[[package]]
name = "sptr"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a"
[[package]]
name = "sqlformat"
version = "0.3.2"
@ -7975,7 +8052,7 @@ dependencies = [
"socket2",
"tokio",
"tokio-stream",
"tower",
"tower 0.4.13",
"tower-layer",
"tower-service",
"tracing",
@ -8001,6 +8078,21 @@ dependencies = [
"tracing",
]
[[package]]
name = "tower"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
dependencies = [
"futures-core",
"futures-util",
"pin-project-lite",
"sync_wrapper",
"tokio",
"tower-layer",
"tower-service",
]
[[package]]
name = "tower-http"
version = "0.6.1"
@ -8029,9 +8121,9 @@ checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
[[package]]
name = "tower-service"
version = "0.3.2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
[[package]]
name = "tracing"
@ -8505,6 +8597,16 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "web-time"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webpki-root-certs"
version = "0.26.6"
@ -8532,7 +8634,7 @@ dependencies = [
"arrayvec",
"bit-vec",
"bitflags 2.6.0",
"cfg_aliases",
"cfg_aliases 0.1.1",
"codespan-reporting",
"document-features",
"indexmap 2.3.0",
@ -8564,7 +8666,7 @@ dependencies = [
"bit-set",
"bitflags 2.6.0",
"block",
"cfg_aliases",
"cfg_aliases 0.1.1",
"core-graphics-types",
"d3d12",
"glow",

View file

@ -48,19 +48,19 @@ repository = "https://github.com/denoland/deno"
[workspace.dependencies]
deno_ast = { version = "=0.44.0", features = ["transpiling"] }
deno_core = { version = "0.326.0" }
deno_core = { version = "0.327.0" }
deno_bench_util = { version = "0.177.0", path = "./bench_util" }
deno_config = { version = "=0.39.3", features = ["workspace", "sync"] }
deno_lockfile = "=0.23.2"
deno_bench_util = { version = "0.178.0", path = "./bench_util" }
deno_config = { version = "=0.41.0", features = ["workspace", "sync"] }
deno_lockfile = "=0.24.0"
deno_media_type = { version = "0.2.0", features = ["module_specifier"] }
deno_npm = "=0.26.0"
deno_npm = "=0.27.0"
deno_path_util = "=0.2.2"
deno_permissions = { version = "0.43.0", path = "./runtime/permissions" }
deno_runtime = { version = "0.192.0", path = "./runtime" }
deno_semver = "=0.6.1"
deno_semver = "=0.7.1"
deno_terminal = "0.2.0"
napi_sym = { version = "0.113.0", path = "./ext/napi/sym" }
napi_sym = { version = "0.114.0", path = "./ext/napi/sym" }
test_util = { package = "test_server", path = "./tests/util/server" }
denokv_proto = "0.8.4"
@ -69,29 +69,29 @@ denokv_remote = "0.8.4"
denokv_sqlite = { default-features = false, version = "0.8.4" }
# exts
deno_broadcast_channel = { version = "0.177.0", path = "./ext/broadcast_channel" }
deno_cache = { version = "0.115.0", path = "./ext/cache" }
deno_canvas = { version = "0.52.0", path = "./ext/canvas" }
deno_console = { version = "0.183.0", path = "./ext/console" }
deno_cron = { version = "0.63.0", path = "./ext/cron" }
deno_crypto = { version = "0.197.0", path = "./ext/crypto" }
deno_fetch = { version = "0.207.0", path = "./ext/fetch" }
deno_ffi = { version = "0.170.0", path = "./ext/ffi" }
deno_fs = { version = "0.93.0", path = "./ext/fs" }
deno_http = { version = "0.181.0", path = "./ext/http" }
deno_io = { version = "0.93.0", path = "./ext/io" }
deno_kv = { version = "0.91.0", path = "./ext/kv" }
deno_napi = { version = "0.114.0", path = "./ext/napi" }
deno_net = { version = "0.175.0", path = "./ext/net" }
deno_node = { version = "0.120.0", path = "./ext/node" }
deno_telemetry = { version = "0.5.0", path = "./ext/telemetry" }
deno_tls = { version = "0.170.0", path = "./ext/tls" }
deno_url = { version = "0.183.0", path = "./ext/url" }
deno_web = { version = "0.214.0", path = "./ext/web" }
deno_webgpu = { version = "0.150.0", path = "./ext/webgpu" }
deno_webidl = { version = "0.183.0", path = "./ext/webidl" }
deno_websocket = { version = "0.188.0", path = "./ext/websocket" }
deno_webstorage = { version = "0.178.0", path = "./ext/webstorage" }
deno_broadcast_channel = { version = "0.178.0", path = "./ext/broadcast_channel" }
deno_cache = { version = "0.116.0", path = "./ext/cache" }
deno_canvas = { version = "0.53.0", path = "./ext/canvas" }
deno_console = { version = "0.184.0", path = "./ext/console" }
deno_cron = { version = "0.64.0", path = "./ext/cron" }
deno_crypto = { version = "0.198.0", path = "./ext/crypto" }
deno_fetch = { version = "0.208.0", path = "./ext/fetch" }
deno_ffi = { version = "0.171.0", path = "./ext/ffi" }
deno_fs = { version = "0.94.0", path = "./ext/fs" }
deno_http = { version = "0.182.0", path = "./ext/http" }
deno_io = { version = "0.94.0", path = "./ext/io" }
deno_kv = { version = "0.92.0", path = "./ext/kv" }
deno_napi = { version = "0.115.0", path = "./ext/napi" }
deno_net = { version = "0.176.0", path = "./ext/net" }
deno_node = { version = "0.122.0", path = "./ext/node" }
deno_telemetry = { version = "0.6.0", path = "./ext/telemetry" }
deno_tls = { version = "0.171.0", path = "./ext/tls" }
deno_url = { version = "0.184.0", path = "./ext/url" }
deno_web = { version = "0.215.0", path = "./ext/web" }
deno_webgpu = { version = "0.151.0", path = "./ext/webgpu" }
deno_webidl = { version = "0.184.0", path = "./ext/webidl" }
deno_websocket = { version = "0.189.0", path = "./ext/websocket" }
deno_webstorage = { version = "0.179.0", path = "./ext/webstorage" }
# resolvers
deno_npm_cache = { version = "0.3.0", path = "./resolvers/npm_cache" }
@ -108,7 +108,7 @@ boxed_error = "0.2.3"
brotli = "6.0.0"
bytes = "1.4.0"
cache_control = "=0.2.0"
capacity_builder = "0.1.3"
capacity_builder = "0.5.0"
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()
@ -120,7 +120,7 @@ data-encoding = "2.3.3"
data-url = "=0.3.1"
deno_cache_dir = "=0.15.0"
deno_error = "=0.5.2"
deno_package_json = { version = "0.2.1", default-features = false }
deno_package_json = { version = "0.3.0", default-features = false }
deno_unsync = "0.4.2"
dlopen2 = "0.6.1"
ecb = "=0.1.2"
@ -149,7 +149,7 @@ indexmap = { version = "2", features = ["serde"] }
ipnet = "2.3"
jsonc-parser = { version = "=0.26.2", features = ["serde"] }
lazy-regex = "3"
libc = "0.2.126"
libc = "0.2.168"
libz-sys = { version = "1.1.20", default-features = false }
log = { version = "0.4.20", features = ["kv"] }
lsp-types = "=0.97.0" # used by tower-lsp and "proposed" feature is unstable in patch releases
@ -202,7 +202,7 @@ 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"
tokio-util = "0.7.4"
tower = { version = "0.4.13", default-features = false, features = ["util"] }
tower = { version = "0.5.2", default-features = false, features = ["retry", "util"] }
tower-http = { version = "0.6.1", features = ["decompression-br", "decompression-gzip"] }
tower-lsp = { package = "deno_tower_lsp", version = "0.1.0", features = ["proposed"] }
tower-service = "0.3.2"

View file

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

View file

@ -74,7 +74,7 @@ deno_config.workspace = true
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
deno_doc = { version = "=0.161.3", features = ["rust", "comrak"] }
deno_error.workspace = true
deno_graph = { version = "=0.86.3" }
deno_graph = { version = "=0.86.5" }
deno_lint = { version = "=0.68.2", features = ["docs"] }
deno_lockfile.workspace = true
deno_npm.workspace = true

View file

@ -31,6 +31,7 @@ use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmSystemInfo;
use deno_path_util::normalize_path;
use deno_semver::npm::NpmPackageReqReference;
use deno_semver::StackString;
use deno_telemetry::OtelConfig;
use deno_telemetry::OtelRuntimeConfig;
use import_map::resolve_import_map_value_from_specifier;
@ -1001,24 +1002,24 @@ impl CliOptions {
// https://nodejs.org/api/process.html
match target.as_str() {
"aarch64-apple-darwin" => NpmSystemInfo {
os: "darwin".to_string(),
cpu: "arm64".to_string(),
os: "darwin".into(),
cpu: "arm64".into(),
},
"aarch64-unknown-linux-gnu" => NpmSystemInfo {
os: "linux".to_string(),
cpu: "arm64".to_string(),
os: "linux".into(),
cpu: "arm64".into(),
},
"x86_64-apple-darwin" => NpmSystemInfo {
os: "darwin".to_string(),
cpu: "x64".to_string(),
os: "darwin".into(),
cpu: "x64".into(),
},
"x86_64-unknown-linux-gnu" => NpmSystemInfo {
os: "linux".to_string(),
cpu: "x64".to_string(),
os: "linux".into(),
cpu: "x64".into(),
},
"x86_64-pc-windows-msvc" => NpmSystemInfo {
os: "win32".to_string(),
cpu: "x64".to_string(),
os: "win32".into(),
cpu: "x64".into(),
},
value => {
log::warn!(
@ -1372,9 +1373,9 @@ impl CliOptions {
Ok(DenoLintConfig {
default_jsx_factory: (!transpile_options.jsx_automatic)
.then(|| transpile_options.jsx_factory.clone()),
.then_some(transpile_options.jsx_factory),
default_jsx_fragment_factory: (!transpile_options.jsx_automatic)
.then(|| transpile_options.jsx_fragment_factory.clone()),
.then_some(transpile_options.jsx_fragment_factory),
})
}
@ -1955,15 +1956,17 @@ pub fn has_flag_env_var(name: &str) -> bool {
pub fn npm_pkg_req_ref_to_binary_command(
req_ref: &NpmPackageReqReference,
) -> String {
let binary_name = req_ref.sub_path().unwrap_or(req_ref.req().name.as_str());
binary_name.to_string()
req_ref
.sub_path()
.map(|s| s.to_string())
.unwrap_or_else(|| req_ref.req().name.to_string())
}
pub fn config_to_deno_graph_workspace_member(
config: &ConfigFile,
) -> Result<deno_graph::WorkspaceMember, AnyError> {
let name = match &config.json.name {
Some(name) => name.clone(),
let name: StackString = match &config.json.name {
Some(name) => name.as_str().into(),
None => bail!("Missing 'name' field in config file."),
};
let version = match &config.json.version {

View file

@ -11,19 +11,20 @@ use deno_package_json::PackageJsonDepValueParseError;
use deno_package_json::PackageJsonDepWorkspaceReq;
use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageReq;
use deno_semver::StackString;
use deno_semver::VersionReq;
use thiserror::Error;
#[derive(Debug)]
pub struct InstallNpmRemotePkg {
pub alias: Option<String>,
pub alias: Option<StackString>,
pub base_dir: PathBuf,
pub req: PackageReq,
}
#[derive(Debug)]
pub struct InstallNpmWorkspacePkg {
pub alias: Option<String>,
pub alias: Option<StackString>,
pub target_dir: PathBuf,
}
@ -31,7 +32,7 @@ pub struct InstallNpmWorkspacePkg {
#[error("Failed to install '{}'\n at {}", alias, location)]
pub struct PackageJsonDepValueParseWithLocationError {
pub location: Url,
pub alias: String,
pub alias: StackString,
#[source]
pub source: PackageJsonDepValueParseError,
}
@ -100,10 +101,8 @@ impl NpmInstallDepsProvider {
let mut pkg_pkgs = Vec::with_capacity(
deps.dependencies.len() + deps.dev_dependencies.len(),
);
for (alias, dep) in deps
.dependencies
.into_iter()
.chain(deps.dev_dependencies.into_iter())
for (alias, dep) in
deps.dependencies.iter().chain(deps.dev_dependencies.iter())
{
let dep = match dep {
Ok(dep) => dep,
@ -111,8 +110,8 @@ impl NpmInstallDepsProvider {
pkg_json_dep_errors.push(
PackageJsonDepValueParseWithLocationError {
location: pkg_json.specifier(),
alias,
source: err,
alias: alias.clone(),
source: err.clone(),
},
);
continue;
@ -121,28 +120,28 @@ impl NpmInstallDepsProvider {
match dep {
PackageJsonDepValue::Req(pkg_req) => {
let workspace_pkg = workspace_npm_pkgs.iter().find(|pkg| {
pkg.matches_req(&pkg_req)
pkg.matches_req(pkg_req)
// do not resolve to the current package
&& pkg.pkg_json.path != pkg_json.path
});
if let Some(pkg) = workspace_pkg {
workspace_pkgs.push(InstallNpmWorkspacePkg {
alias: Some(alias),
alias: Some(alias.clone()),
target_dir: pkg.pkg_json.dir_path().to_path_buf(),
});
} else {
pkg_pkgs.push(InstallNpmRemotePkg {
alias: Some(alias),
alias: Some(alias.clone()),
base_dir: pkg_json.dir_path().to_path_buf(),
req: pkg_req,
req: pkg_req.clone(),
});
}
}
PackageJsonDepValue::Workspace(workspace_version_req) => {
let version_req = match workspace_version_req {
PackageJsonDepWorkspaceReq::VersionReq(version_req) => {
version_req
version_req.clone()
}
PackageJsonDepWorkspaceReq::Tilde
| PackageJsonDepWorkspaceReq::Caret => {
@ -150,10 +149,10 @@ impl NpmInstallDepsProvider {
}
};
if let Some(pkg) = workspace_npm_pkgs.iter().find(|pkg| {
pkg.matches_name_and_version_req(&alias, &version_req)
pkg.matches_name_and_version_req(alias, &version_req)
}) {
workspace_pkgs.push(InstallNpmWorkspacePkg {
alias: Some(alias),
alias: Some(alias.clone()),
target_dir: pkg.pkg_json.dir_path().to_path_buf(),
});
}

View file

@ -5,6 +5,7 @@ use crate::cache::FastInsecureHasher;
use crate::cache::ParsedSourceCache;
use crate::resolver::CjsTracker;
use deno_ast::EmittedSourceText;
use deno_ast::ModuleKind;
use deno_ast::SourceMapOption;
use deno_ast::SourceRange;
@ -132,6 +133,7 @@ impl Emitter {
&transpile_and_emit_options.0,
&transpile_and_emit_options.1,
)
.map(|r| r.text)
}
})
.await
@ -166,7 +168,8 @@ impl Emitter {
source.clone(),
&self.transpile_and_emit_options.0,
&self.transpile_and_emit_options.1,
)?;
)?
.text;
helper.post_emit_parsed_source(
specifier,
&transpiled_source,
@ -177,6 +180,31 @@ impl Emitter {
}
}
pub fn emit_parsed_source_for_deno_compile(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
module_kind: deno_ast::ModuleKind,
source: &Arc<str>,
) -> Result<(String, String), AnyError> {
let mut emit_options = self.transpile_and_emit_options.1.clone();
emit_options.inline_sources = false;
emit_options.source_map = SourceMapOption::Separate;
// strip off the path to have more deterministic builds as we don't care
// about the source name because we manually provide the source map to v8
emit_options.source_map_base = Some(deno_path_util::url_parent(specifier));
let source = EmitParsedSourceHelper::transpile(
&self.parsed_source_cache,
specifier,
media_type,
module_kind,
source.clone(),
&self.transpile_and_emit_options.0,
&emit_options,
)?;
Ok((source.text, source.source_map.unwrap()))
}
/// Expects a file URL, panics otherwise.
pub async fn load_and_emit_for_hmr(
&self,
@ -282,7 +310,7 @@ impl<'a> EmitParsedSourceHelper<'a> {
source: Arc<str>,
transpile_options: &deno_ast::TranspileOptions,
emit_options: &deno_ast::EmitOptions,
) -> Result<String, AnyError> {
) -> Result<EmittedSourceText, 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
@ -302,8 +330,7 @@ impl<'a> EmitParsedSourceHelper<'a> {
source
}
};
debug_assert!(transpiled_source.source_map.is_none());
Ok(transpiled_source.text)
Ok(transpiled_source)
}
pub fn post_emit_parsed_source(

View file

@ -52,6 +52,7 @@ use deno_runtime::deno_node;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::jsr::JsrDepPackageReq;
use deno_semver::package::PackageNv;
use deno_semver::SmallStackString;
use import_map::ImportMapError;
use node_resolver::InNpmPackageChecker;
use std::collections::HashSet;
@ -680,7 +681,7 @@ impl ModuleGraphBuilder {
for (from, to) in graph.packages.mappings() {
lockfile.insert_package_specifier(
JsrDepPackageReq::jsr(from.clone()),
to.version.to_string(),
to.version.to_custom_string::<SmallStackString>(),
);
}
}

View file

@ -145,9 +145,7 @@ impl HttpClient {
}
pub fn get(&self, url: Url) -> Result<RequestBuilder, http::Error> {
let body = http_body_util::Empty::new()
.map_err(|never| match never {})
.boxed();
let body = deno_fetch::ReqBody::empty();
let mut req = http::Request::new(body);
*req.uri_mut() = url.as_str().parse()?;
Ok(RequestBuilder {
@ -179,9 +177,7 @@ impl HttpClient {
S: serde::Serialize,
{
let json = deno_core::serde_json::to_vec(ser)?;
let body = http_body_util::Full::new(json.into())
.map_err(|never| match never {})
.boxed();
let body = deno_fetch::ReqBody::full(json.into());
let builder = self.post(url, body)?;
Ok(builder.header(
http::header::CONTENT_TYPE,
@ -194,9 +190,7 @@ impl HttpClient {
url: &Url,
headers: HeaderMap,
) -> Result<http::Response<ResBody>, SendError> {
let body = http_body_util::Empty::new()
.map_err(|never| match never {})
.boxed();
let body = deno_fetch::ReqBody::empty();
let mut request = http::Request::new(body);
*request.uri_mut() = http::Uri::try_from(url.as_str())?;
*request.headers_mut() = headers;

View file

@ -2,7 +2,7 @@
// @ts-check
import { core } from "ext:core/mod.js";
import { core, internals } from "ext:core/mod.js";
import {
compileSelector,
parseSelector,
@ -13,31 +13,48 @@ const {
op_lint_get_rule,
op_lint_get_source,
op_lint_report,
op_lint_create_serialized_ast,
} = core.ops;
// Keep in sync with Rust
// These types are expected to be present on every node. Note that this
// isn't set in stone. We could revise this at a future point.
const AST_PROP_TYPE = 0;
const AST_PROP_PARENT = 1;
const AST_PROP_RANGE = 2;
// Keep in sync with Rust
// Each node property is tagged with this enum to denote
// what kind of value it holds.
/** @enum {number} */
const PropFlags = {
/** This is an offset to another node */
Ref: 0,
/** This is an array of offsets to other nodes (like children of a BlockStatement) */
RefArr: 1,
/**
* This is a string id. The actual string needs to be looked up in
* the string table that was included in the message.
*/
String: 2,
/** This value is either 0 = false, or 1 = true */
Bool: 3,
/** No value, it's null */
Null: 4,
/** No value, it's undefined */
Undefined: 5,
};
/** @typedef {import("./40_lint_types.d.ts").AstContext} AstContext */
/** @typedef {import("./40_lint_types.d.ts").VisitorFn} VisitorFn */
/** @typedef {import("./40_lint_types.d.ts").MatcherFn} MatcherFn */
/** @typedef {import("./40_lint_types.d.ts").TransformFn} TransformerFn */
/** @typedef {import("./40_lint_types.d.ts").CompiledVisitor} CompiledVisitor */
/** @typedef {import("./40_lint_types.d.ts").LintState} LintState */
/** @typedef {import("./40_lint_types.d.ts").MatchContext} MatchContext */
/** @typedef {import("./40_lint_types.d.ts").RuleContext} RuleContext */
/** @typedef {import("./40_lint_types.d.ts").NodeFacade} NodeFacade */
/** @typedef {import("./40_lint_types.d.ts").LintPlugin} LintPlugin */
/** @typedef {import("./40_lint_types.d.ts").LintReportData} LintReportData */
/** @typedef {import("./40_lint_types.d.ts").TestReportData} TestReportData */
/** @type {LintState} */
const state = {
@ -45,7 +62,11 @@ const state = {
installedPlugins: new Set(),
};
/** @implements {Deno.LintRuleContext} */
/**
* Every rule gets their own instance of this class. This is the main
* API lint rules interact with.
* @implements {RuleContext}
*/
export class Context {
id;
@ -91,7 +112,7 @@ export class Context {
}
/**
* @param {Deno.LintPlugin} plugin
* @param {LintPlugin} plugin
*/
export function installPlugin(plugin) {
if (typeof plugin !== "object") {
@ -127,6 +148,8 @@ function getNode(ctx, offset) {
}
/**
* Find the offset of a specific property of a specific node. This will
* be used later a lot more for selectors.
* @param {Uint8Array} buf
* @param {number} search
* @param {number} offset
@ -164,117 +187,76 @@ function findPropOffset(buf, offset, search) {
return -1;
}
const INTERNAL_CTX = Symbol("ctx");
const INTERNAL_OFFSET = Symbol("offset");
// This class is a facade for all materialized nodes. Instead of creating a
// unique class per AST node, we have one class with getters for every
// possible node property. This allows us to lazily materialize child node
// only when they are needed.
class Node {
[INTERNAL_CTX];
[INTERNAL_OFFSET];
/**
* @param {AstContext} ctx
* @param {number} offset
*/
constructor(ctx, offset) {
this[INTERNAL_CTX] = ctx;
this[INTERNAL_OFFSET] = offset;
}
/**
* Logging a class with only getters prints just the class name. This
* makes debugging difficult because you don't see any of the properties.
* For that reason we'll intercept inspection and serialize the node to
* a plain JSON structure which can be logged and allows users to see all
* properties and their values.
*
* This is only expected to be used during development of a rule.
* @param {*} _
* @param {Deno.InspectOptions} options
* @returns {string}
*/
[Symbol.for("Deno.customInspect")](_, options) {
const json = toJsValue(this[INTERNAL_CTX], this[INTERNAL_OFFSET]);
return Deno.inspect(json, options);
}
[Symbol.for("Deno.lint.toJsValue")]() {
return toJsValue(this[INTERNAL_CTX], this[INTERNAL_OFFSET]);
}
}
/** @type {Set<number>} */
const appliedGetters = new Set();
/**
* Add getters for all potential properties found in the message.
* @param {AstContext} ctx
* @param {number} offset
* @param {number} search
* @returns {*}
*/
function readValue(ctx, offset, search) {
const { buf } = ctx;
const type = buf[offset];
function setNodeGetters(ctx) {
if (appliedGetters.size === ctx.strByProp.length) return;
if (search === AST_PROP_TYPE) {
return getString(ctx.strTable, ctx.strByType[type]);
} else if (search === AST_PROP_RANGE) {
const start = readU32(buf, offset + 1 + 4);
const end = readU32(buf, offset + 1 + 4 + 4);
return [start, end];
} else if (search === AST_PROP_PARENT) {
const pos = readU32(buf, offset + 1);
return getNode(ctx, pos);
for (let i = 0; i < ctx.strByProp.length; i++) {
const id = ctx.strByProp[i];
if (id === 0 || appliedGetters.has(i)) continue;
appliedGetters.add(i);
const name = getString(ctx.strTable, id);
Object.defineProperty(Node.prototype, name, {
get() {
return readValue(this[INTERNAL_CTX], this[INTERNAL_OFFSET], i);
},
});
}
offset = findPropOffset(ctx.buf, offset, search);
if (offset === -1) return undefined;
const kind = buf[offset + 1];
if (kind === PropFlags.Ref) {
const value = readU32(buf, offset + 2);
return getNode(ctx, value);
} else if (kind === PropFlags.RefArr) {
const len = readU32(buf, offset);
offset += 4;
const nodes = new Array(len);
for (let i = 0; i < len; i++) {
nodes[i] = getNode(ctx, readU32(buf, offset));
offset += 4;
}
return nodes;
} else if (kind === PropFlags.Bool) {
return buf[offset] === 1;
} else if (kind === PropFlags.String) {
const v = readU32(buf, offset);
return getString(ctx.strTable, v);
} else if (kind === PropFlags.Null) {
return null;
} else if (kind === PropFlags.Undefined) {
return undefined;
}
throw new Error(`Unknown prop kind: ${kind}`);
}
/**
* @param {AstContext["buf"]} buf
* @param {number} child
* @returns {null | [number, number]}
*/
function findChildOffset(buf, child) {
let offset = readU32(buf, child + 1);
// type + parentId + SpanLo + SpanHi
offset += 1 + 4 + 4 + 4;
const propCount = buf[offset++];
for (let i = 0; i < propCount; i++) {
const _prop = buf[offset++];
const kind = buf[offset++];
switch (kind) {
case PropFlags.Ref: {
const start = offset;
const value = readU32(buf, offset);
offset += 4;
if (value === child) {
return [start, -1];
}
break;
}
case PropFlags.RefArr: {
const start = offset;
const len = readU32(buf, offset);
offset += 4;
for (let j = 0; j < len; j++) {
const value = readU32(buf, offset);
offset += 4;
if (value === child) {
return [start, j];
}
}
break;
}
case PropFlags.String:
offset += 4;
break;
case PropFlags.Bool:
offset++;
break;
case PropFlags.Null:
case PropFlags.Undefined:
break;
}
}
return null;
}
/**
* Serialize a node recursively to plain JSON
* @param {AstContext} ctx
* @param {number} offset
* @returns {*}
@ -329,6 +311,7 @@ function toJsValue(ctx, offset) {
return node;
}
<<<<<<< HEAD
const INTERNAL_CTX = Symbol("ctx");
const INTERNAL_OFFSET = Symbol("offset");
@ -378,11 +361,69 @@ function setNodeGetters(ctx) {
},
});
}
}
/**
* Read a specific property from a node
* @param {AstContext} ctx
* @param {number} offset
* @param {number} search
* @returns {*}
*/
function readValue(ctx, offset, search) {
const { buf } = ctx;
const type = buf[offset];
if (search === AST_PROP_TYPE) {
return getString(ctx.strTable, ctx.strByType[type]);
} else if (search === AST_PROP_RANGE) {
const start = readU32(buf, offset + 1 + 4);
const end = readU32(buf, offset + 1 + 4 + 4);
return [start, end];
} else if (search === AST_PROP_PARENT) {
const pos = readU32(buf, offset + 1);
return getNode(ctx, pos);
}
offset = findPropOffset(ctx.buf, offset, search);
if (offset === -1) return undefined;
const kind = buf[offset + 1];
if (kind === PropFlags.Ref) {
const value = readU32(buf, offset + 2);
return getNode(ctx, value);
} else if (kind === PropFlags.RefArr) {
const len = readU32(buf, offset);
offset += 4;
const nodes = new Array(len);
for (let i = 0; i < len; i++) {
nodes[i] = getNode(ctx, readU32(buf, offset));
offset += 4;
}
return nodes;
} else if (kind === PropFlags.Bool) {
return buf[offset] === 1;
} else if (kind === PropFlags.String) {
const v = readU32(buf, offset);
return getString(ctx.strTable, v);
} else if (kind === PropFlags.Null) {
return null;
} else if (kind === PropFlags.Undefined) {
return undefined;
}
throw new Error(`Unknown prop kind: ${kind}`);
>>>>>>> main
}
const DECODER = new TextDecoder();
/**
* TODO: Check if it's faster to use the `ArrayView` API instead.
* @param {Uint8Array} buf
* @param {number} i
* @returns {number}
@ -393,6 +434,7 @@ function readU32(buf, i) {
}
/**
* Get a string by id and error if it wasn't found
* @param {AstContext["strTable"]} strTable
* @param {number} id
* @returns {string}
@ -646,18 +688,21 @@ function createAstContext(buf) {
/** @type {Map<number, string>} */
const strTable = new Map();
// console.log(JSON.stringify(buf, null, 2));
// The buffer has a few offsets at the end which allows us to easily
// jump to the relevant sections of the message.
const typeMapOffset = readU32(buf, buf.length - 16);
const propMapOffset = readU32(buf, buf.length - 12);
const strTableOffset = readU32(buf, buf.length - 8);
const rootId = readU32(buf, buf.length - 4);
// console.log({ strTableOffset, rootId });
// Offset of the topmost node in the AST Tree.
const rootOffset = readU32(buf, buf.length - 4);
let offset = strTableOffset;
const stringCount = readU32(buf, offset);
offset += 4;
// TODO(@marvinhagemeister): We could lazily decode the strings on an as needed basis.
// Not sure if this matters much in practice though.
let id = 0;
for (let i = 0; i < stringCount; i++) {
const len = readU32(buf, offset);
@ -670,8 +715,6 @@ function createAstContext(buf) {
id++;
}
// console.log({ stringCount, strTable, rootId });
if (strTable.size !== stringCount) {
throw new Error(
`Could not deserialize string table. Expected ${stringCount} items, but got ${strTable.size}`,
@ -688,7 +731,6 @@ function createAstContext(buf) {
const v = readU32(buf, offset);
offset += 4;
// console.log("type: ", i, v, strTable.get(v));
strByType[i] = v;
typeByStr.set(strTable.get(v), i);
}
@ -711,7 +753,7 @@ function createAstContext(buf) {
const ctx = {
buf,
strTable,
rootId,
rootOffset,
nodes: new Map(),
strTableOffset,
strByProp,
@ -723,6 +765,7 @@ function createAstContext(buf) {
setNodeGetters(ctx);
// DEV ONLY: Enable this to inspect the buffer message
// _dump(ctx);
return ctx;
@ -734,22 +777,21 @@ function createAstContext(buf) {
const NOOP = (_node) => {};
/**
* Kick off the actual linting process of JS plugins.
* @param {string} fileName
* @param {Uint8Array} serializedAst
*/
export function runPluginsForFile(fileName, serializedAst) {
const ctx = createAstContext(serializedAst);
// console.log(JSON.stringify(ctx, null, 2));
/** @type {Map<string, { enter: VisitorFn, exit: VisitorFn}>} */
const bySelector = new Map();
const destroyFns = [];
// console.log(state);
// Instantiate and merge visitors. This allows us to only traverse
// the AST once instead of per plugin.
// the AST once instead of per plugin. When ever we enter or exit a
// node we'll call all visitors that match.
for (let i = 0; i < state.plugins.length; i++) {
const plugin = state.plugins[i];
@ -759,12 +801,13 @@ export function runPluginsForFile(fileName, serializedAst) {
const ctx = new Context(id, fileName);
const visitor = rule.create(ctx);
// console.log({ visitor });
// deno-lint-ignore guard-for-in
for (let key in visitor) {
const fn = visitor[key];
if (fn === undefined) continue;
// Support enter and exit callbacks on a visitor.
// Exit callbacks are marked by having `:exit` at the end.
let isExit = false;
if (key.endsWith(":exit")) {
isExit = true;
@ -848,7 +891,7 @@ export function runPluginsForFile(fileName, serializedAst) {
// Traverse ast with all visitors at the same time to avoid traversing
// multiple times.
try {
traverse(ctx, visitors, ctx.rootId);
traverse(ctx, visitors, ctx.rootOffset);
} finally {
ctx.nodes.clear();
@ -865,10 +908,9 @@ export function runPluginsForFile(fileName, serializedAst) {
* @param {number} offset
*/
function traverse(ctx, visitors, offset) {
// console.log("traversing offset", offset);
// Empty id
// The 0 offset is used to denote an empty/placeholder node
if (offset === 0) return;
const { buf } = ctx;
/** @type {VisitorFn[] | null} */
@ -893,6 +935,8 @@ function traverse(ctx, visitors, offset) {
}
}
// Search for node references in the properties of the current node. All
// other properties can be ignored.
try {
// type + parentId + SpanLo + SpanHi
offset += 1 + 4 + 4 + 4;
@ -936,27 +980,33 @@ function traverse(ctx, visitors, offset) {
}
/**
* This is useful debugging helper to display the buffer's contents.
* @param {AstContext} ctx
*/
function _dump(ctx) {
const { buf, strTableOffset, strTable, strByType, strByProp } = ctx;
// @ts-ignore dump fn
// deno-lint-ignore no-console
console.log(strTable);
for (let i = 0; i < strByType.length; i++) {
const v = strByType[i];
// @ts-ignore dump fn
// deno-lint-ignore no-console
if (v > 0) console.log(" > type:", i, getString(ctx.strTable, v), v);
}
// @ts-ignore dump fn
// deno-lint-ignore no-console
console.log();
for (let i = 0; i < strByProp.length; i++) {
const v = strByProp[i];
// @ts-ignore dump fn
// deno-lint-ignore no-console
if (v > 0) console.log(" > prop:", i, getString(ctx.strTable, v), v);
}
// @ts-ignore dump fn
// deno-lint-ignore no-console
console.log();
let offset = 0;
@ -965,12 +1015,14 @@ function _dump(ctx) {
const type = buf[offset];
const name = getString(ctx.strTable, ctx.strByType[type]);
// @ts-ignore dump fn
// deno-lint-ignore no-console
console.log(`${name}, offset: ${offset}, type: ${type}`);
offset += 1;
const parent = readU32(buf, offset);
offset += 4;
// @ts-ignore dump fn
// deno-lint-ignore no-console
console.log(` parent: ${parent}`);
const start = readU32(buf, offset);
@ -978,10 +1030,12 @@ function _dump(ctx) {
const end = readU32(buf, offset);
offset += 4;
// @ts-ignore dump fn
// deno-lint-ignore no-console
console.log(` range: ${start} -> ${end}`);
const count = buf[offset++];
// @ts-ignore dump fn
// deno-lint-ignore no-console
console.log(` prop count: ${count}`);
for (let i = 0; i < count; i++) {
@ -1001,38 +1055,68 @@ function _dump(ctx) {
const v = readU32(buf, offset);
offset += 4;
// @ts-ignore dump fn
// deno-lint-ignore no-console
console.log(` ${name}: ${v} (${kindName}, ${prop})`);
} else if (kind === PropFlags.RefArr) {
const len = readU32(buf, offset);
offset += 4;
// @ts-ignore dump fn
// deno-lint-ignore no-console
console.log(` ${name}: Array(${len}) (${kindName}, ${prop})`);
for (let j = 0; j < len; j++) {
const v = readU32(buf, offset);
offset += 4;
// @ts-ignore dump fn
// deno-lint-ignore no-console
console.log(` - ${v} (${prop})`);
}
} else if (kind === PropFlags.Bool) {
const v = buf[offset];
offset += 1;
// @ts-ignore dump fn
// deno-lint-ignore no-console
console.log(` ${name}: ${v} (${kindName}, ${prop})`);
} else if (kind === PropFlags.String) {
const v = readU32(buf, offset);
offset += 4;
// @ts-ignore dump fn
// deno-lint-ignore no-console
console.log(
` ${name}: ${getString(ctx.strTable, v)} (${kindName}, ${prop})`,
);
} else if (kind === PropFlags.Null) {
// @ts-ignore dump fn
// deno-lint-ignore no-console
console.log(` ${name}: null (${kindName}, ${prop})`);
} else if (kind === PropFlags.Undefined) {
// @ts-ignore dump fn
// deno-lint-ignore no-console
console.log(` ${name}: undefined (${kindName}, ${prop})`);
}
}
}
}
// TODO(bartlomieju): this is temporary, until we get plugins plumbed through
// the CLI linter
/**
* @param {LintPlugin} plugin
* @param {string} fileName
* @param {string} sourceText
*/
function runLintPlugin(plugin, fileName, sourceText) {
installPlugin(plugin);
const serializedAst = op_lint_create_serialized_ast(fileName, sourceText);
try {
runPluginsForFile(fileName, serializedAst);
} finally {
// During testing we don't want to keep plugins around
state.installedPlugins.clear();
}
}
// TODO(bartlomieju): this is temporary, until we get plugins plumbed through
// the CLI linter
internals.runLintPlugin = runLintPlugin;

View file

@ -1,15 +1,16 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
export interface NodeFacade {
readonly type: string;
readonly range: [number, number];
type: string;
range: [number, number];
[key: string]: unknown;
}
export interface AstContext {
buf: Uint8Array;
strTable: Map<number, string>;
strTableOffset: number;
rootId: number;
rootOffset: number;
nodes: Map<number, NodeFacade>;
strByType: number[];
strByProp: number[];

View file

@ -36,6 +36,8 @@ use deno_semver::package::PackageNv;
use deno_semver::package::PackageNvReference;
use deno_semver::package::PackageReq;
use deno_semver::package::PackageReqReference;
use deno_semver::SmallStackString;
use deno_semver::StackString;
use deno_semver::Version;
use import_map::ImportMap;
use node_resolver::NodeResolutionKind;
@ -278,9 +280,16 @@ impl<'a> TsResponseImportMapper<'a> {
{
let mut segments = jsr_path.split('/');
let name = if jsr_path.starts_with('@') {
format!("{}/{}", segments.next()?, segments.next()?)
let scope = segments.next()?;
let name = segments.next()?;
capacity_builder::StringBuilder::<StackString>::build(|builder| {
builder.append(scope);
builder.append("/");
builder.append(name);
})
.unwrap()
} else {
segments.next()?.to_string()
StackString::from(segments.next()?)
};
let version = Version::parse_standard(segments.next()?).ok()?;
let nv = PackageNv { name, version };
@ -290,7 +299,9 @@ impl<'a> TsResponseImportMapper<'a> {
&path,
Some(&self.file_referrer),
)?;
let sub_path = (export != ".").then_some(export);
let sub_path = (export != ".")
.then_some(export)
.map(SmallStackString::from_string);
let mut req = None;
req = req.or_else(|| {
let import_map = self.maybe_import_map?;
@ -603,18 +614,24 @@ fn try_reverse_map_package_json_exports(
/// For a set of tsc changes, can them for any that contain something that looks
/// like an import and rewrite the import specifier to include the extension
pub fn fix_ts_import_changes(
referrer: &ModuleSpecifier,
resolution_mode: ResolutionMode,
changes: &[tsc::FileTextChanges],
language_server: &language_server::Inner,
) -> Result<Vec<tsc::FileTextChanges>, AnyError> {
let import_mapper = language_server.get_ts_response_import_mapper(referrer);
let mut r = Vec::new();
for change in changes {
let Ok(referrer) = ModuleSpecifier::parse(&change.file_name) else {
continue;
};
let referrer_doc = language_server.get_asset_or_document(&referrer).ok();
let resolution_mode = referrer_doc
.as_ref()
.map(|d| d.resolution_mode())
.unwrap_or(ResolutionMode::Import);
let import_mapper =
language_server.get_ts_response_import_mapper(&referrer);
let mut text_changes = Vec::new();
for text_change in &change.text_changes {
let lines = text_change.new_text.split('\n');
let new_lines: Vec<String> = lines
.map(|line| {
// This assumes that there's only one import per line.
@ -622,7 +639,7 @@ pub fn fix_ts_import_changes(
let specifier =
captures.iter().skip(1).find_map(|s| s).unwrap().as_str();
if let Some(new_specifier) = import_mapper
.check_unresolved_specifier(specifier, referrer, resolution_mode)
.check_unresolved_specifier(specifier, &referrer, resolution_mode)
{
line.replace(specifier, &new_specifier)
} else {

View file

@ -251,6 +251,13 @@ impl AssetOrDocument {
pub fn document_lsp_version(&self) -> Option<i32> {
self.document().and_then(|d| d.maybe_lsp_version())
}
pub fn resolution_mode(&self) -> ResolutionMode {
match self {
AssetOrDocument::Asset(_) => ResolutionMode::Import,
AssetOrDocument::Document(d) => d.resolution_mode(),
}
}
}
type ModuleResult = Result<deno_graph::JsModule, deno_graph::ModuleGraphError>;

View file

@ -18,6 +18,7 @@ use deno_graph::ModuleSpecifier;
use deno_semver::jsr::JsrPackageReqReference;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
use deno_semver::StackString;
use deno_semver::Version;
use serde::Deserialize;
use std::collections::HashMap;
@ -33,8 +34,8 @@ pub struct JsrCacheResolver {
/// The `module_graph` fields of the version infos should be forcibly absent.
/// It can be large and we don't want to store it.
info_by_nv: DashMap<PackageNv, Option<Arc<JsrPackageVersionInfo>>>,
info_by_name: DashMap<String, Option<Arc<JsrPackageInfo>>>,
workspace_scope_by_name: HashMap<String, ModuleSpecifier>,
info_by_name: DashMap<StackString, Option<Arc<JsrPackageInfo>>>,
workspace_scope_by_name: HashMap<StackString, ModuleSpecifier>,
cache: Arc<dyn HttpCache>,
}
@ -59,7 +60,7 @@ impl JsrCacheResolver {
continue;
};
let nv = PackageNv {
name: jsr_pkg_config.name.clone(),
name: jsr_pkg_config.name.as_str().into(),
version: version.clone(),
};
info_by_name.insert(
@ -125,8 +126,8 @@ impl JsrCacheResolver {
return nv.value().clone();
}
let maybe_get_nv = || {
let name = req.name.clone();
let package_info = self.package_info(&name)?;
let name = &req.name;
let package_info = self.package_info(name)?;
// Find the first matching version of the package which is cached.
let mut versions = package_info.versions.keys().collect::<Vec<_>>();
versions.sort();
@ -144,7 +145,10 @@ impl JsrCacheResolver {
self.package_version_info(&nv).is_some()
})
.cloned()?;
Some(PackageNv { name, version })
Some(PackageNv {
name: name.clone(),
version,
})
};
let nv = maybe_get_nv();
self.nv_by_req.insert(req.clone(), nv.clone());
@ -216,7 +220,10 @@ impl JsrCacheResolver {
None
}
pub fn package_info(&self, name: &str) -> Option<Arc<JsrPackageInfo>> {
pub fn package_info(
&self,
name: &StackString,
) -> Option<Arc<JsrPackageInfo>> {
if let Some(info) = self.info_by_name.get(name) {
return info.value().clone();
}
@ -226,7 +233,7 @@ impl JsrCacheResolver {
serde_json::from_slice::<JsrPackageInfo>(&meta_bytes).ok()
};
let info = read_cached_package_info().map(Arc::new);
self.info_by_name.insert(name.to_string(), info.clone());
self.info_by_name.insert(name.clone(), info.clone());
info
}

View file

@ -1855,20 +1855,12 @@ impl Inner {
}
let changes = if code_action_data.fix_id == "fixMissingImport" {
fix_ts_import_changes(
&code_action_data.specifier,
maybe_asset_or_doc
.as_ref()
.and_then(|d| d.document())
.map(|d| d.resolution_mode())
.unwrap_or(ResolutionMode::Import),
&combined_code_actions.changes,
self,
)
.map_err(|err| {
error!("Unable to remap changes: {:#}", err);
LspError::internal_error()
})?
fix_ts_import_changes(&combined_code_actions.changes, self).map_err(
|err| {
error!("Unable to remap changes: {:#}", err);
LspError::internal_error()
},
)?
} else {
combined_code_actions.changes
};
@ -1912,20 +1904,16 @@ impl Inner {
asset_or_doc.scope().cloned(),
)
.await?;
if kind_suffix == ".rewrite.function.returnType" {
refactor_edit_info.edits = fix_ts_import_changes(
&action_data.specifier,
asset_or_doc
.document()
.map(|d| d.resolution_mode())
.unwrap_or(ResolutionMode::Import),
&refactor_edit_info.edits,
self,
)
.map_err(|err| {
error!("Unable to remap changes: {:#}", err);
LspError::internal_error()
})?
if kind_suffix == ".rewrite.function.returnType"
|| kind_suffix == ".move.newFile"
{
refactor_edit_info.edits =
fix_ts_import_changes(&refactor_edit_info.edits, self).map_err(
|err| {
error!("Unable to remap changes: {:#}", err);
LspError::internal_error()
},
)?
}
code_action.edit = refactor_edit_info.to_workspace_edit(self)?;
code_action
@ -3793,7 +3781,7 @@ impl Inner {
for (name, command) in scripts {
result.push(TaskDefinition {
name: name.clone(),
command: command.clone(),
command: Some(command.clone()),
source_uri: url_to_uri(&package_json.specifier())
.map_err(|_| LspError::internal_error())?,
});

View file

@ -14,7 +14,7 @@ pub const LATEST_DIAGNOSTIC_BATCH_INDEX: &str =
#[serde(rename_all = "camelCase")]
pub struct TaskDefinition {
pub name: String,
pub command: String,
pub command: Option<String>,
pub source_uri: lsp::Uri,
}

View file

@ -67,7 +67,9 @@ pub mod tests {
&self,
nv: &PackageNv,
) -> Result<Arc<Vec<String>>, AnyError> {
let Some(exports_by_version) = self.package_versions.get(&nv.name) else {
let Some(exports_by_version) =
self.package_versions.get(nv.name.as_str())
else {
return Err(anyhow!("Package not found."));
};
let Some(exports) = exports_by_version.get(&nv.version) else {

View file

@ -996,7 +996,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
std::future::ready(()).boxed_local()
}
fn get_source_map(&self, file_name: &str) -> Option<Vec<u8>> {
fn get_source_map(&self, file_name: &str) -> Option<Cow<[u8]>> {
let specifier = resolve_url(file_name).ok()?;
match specifier.scheme() {
// we should only be looking for emits for schemes that denote external
@ -1008,7 +1008,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
.0
.load_prepared_module_for_source_map_sync(&specifier)
.ok()??;
source_map_from_code(source.code.as_bytes())
source_map_from_code(source.code.as_bytes()).map(Cow::Owned)
}
fn get_source_mapped_source_line(

View file

@ -560,11 +560,11 @@ impl ManagedCliNpmResolver {
&self,
) -> Result<(), Box<PackageJsonDepValueParseWithLocationError>> {
for err in self.npm_install_deps_provider.pkg_json_dep_errors() {
match &err.source {
deno_package_json::PackageJsonDepValueParseError::VersionReq(_) => {
match err.source.as_kind() {
deno_package_json::PackageJsonDepValueParseErrorKind::VersionReq(_) => {
return Err(Box::new(err.clone()));
}
deno_package_json::PackageJsonDepValueParseError::Unsupported {
deno_package_json::PackageJsonDepValueParseErrorKind::Unsupported {
..
} => {
// only warn for this one

View file

@ -4,6 +4,7 @@ use std::collections::HashMap;
use std::collections::HashSet;
use std::sync::Arc;
use capacity_builder::StringBuilder;
use deno_core::error::AnyError;
use deno_lockfile::NpmPackageDependencyLockfileInfo;
use deno_lockfile::NpmPackageLockfileInfo;
@ -24,6 +25,7 @@ use deno_npm::NpmSystemInfo;
use deno_semver::jsr::JsrDepPackageReq;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
use deno_semver::SmallStackString;
use deno_semver::VersionReq;
use crate::args::CliLockfile;
@ -336,7 +338,13 @@ fn populate_lockfile_from_snapshot(
let id = &snapshot.resolve_package_from_deno_module(nv).unwrap().id;
lockfile.insert_package_specifier(
JsrDepPackageReq::npm(package_req.clone()),
format!("{}{}", id.nv.version, id.peer_deps_serialized()),
{
StringBuilder::<SmallStackString>::build(|builder| {
builder.append(&id.nv.version);
builder.append(&id.peer_dependencies);
})
.unwrap()
},
);
}
for package in snapshot.all_packages_for_every_system() {

View file

@ -28,8 +28,10 @@ fn default_bin_name(package: &NpmResolutionPackage) -> &str {
.id
.nv
.name
.as_str()
.rsplit_once('/')
.map_or(package.id.nv.name.as_str(), |(_, name)| name)
.map(|(_, name)| name)
.unwrap_or(package.id.nv.name.as_str())
}
pub fn warn_missing_entrypoint(

View file

@ -38,6 +38,7 @@ use deno_resolver::npm::normalize_pkg_name_for_node_modules_deno_folder;
use deno_runtime::deno_fs;
use deno_runtime::deno_node::NodePermissions;
use deno_semver::package::PackageNv;
use deno_semver::StackString;
use node_resolver::errors::PackageFolderResolveError;
use node_resolver::errors::PackageFolderResolveIoError;
use node_resolver::errors::PackageNotFoundError;
@ -355,8 +356,10 @@ async fn sync_resolution_with_fs(
let package_partitions =
snapshot.all_system_packages_partitioned(system_info);
let mut cache_futures = FuturesUnordered::new();
let mut newest_packages_by_name: HashMap<&String, &NpmResolutionPackage> =
HashMap::with_capacity(package_partitions.packages.len());
let mut newest_packages_by_name: HashMap<
&StackString,
&NpmResolutionPackage,
> = HashMap::with_capacity(package_partitions.packages.len());
let bin_entries = Rc::new(RefCell::new(bin_entries::BinEntries::new()));
let mut lifecycle_scripts =
super::common::lifecycle_scripts::LifecycleScripts::new(
@ -536,7 +539,7 @@ async fn sync_resolution_with_fs(
}
}
let mut found_names: HashMap<&String, &PackageNv> = HashMap::new();
let mut found_names: HashMap<&StackString, &PackageNv> = HashMap::new();
// set of node_modules in workspace packages that we've already ensured exist
let mut existing_child_node_modules_dirs: HashSet<PathBuf> = HashSet::new();
@ -1012,10 +1015,10 @@ fn get_package_folder_id_from_folder_name(
) -> Option<NpmPackageCacheFolderId> {
let folder_name = folder_name.replace('+', "/");
let (name, ending) = folder_name.rsplit_once('@')?;
let name = if let Some(encoded_name) = name.strip_prefix('_') {
mixed_case_package_name_decode(encoded_name)?
let name: StackString = if let Some(encoded_name) = name.strip_prefix('_') {
StackString::from_string(mixed_case_package_name_decode(encoded_name)?)
} else {
name.to_string()
name.into()
};
let (raw_version, copy_index) = match ending.split_once('_') {
Some((raw_version, copy_index)) => {

34
cli/ops/lint.rs Normal file
View file

@ -0,0 +1,34 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use deno_ast::MediaType;
use deno_ast::ModuleSpecifier;
use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::op2;
use crate::tools::lint;
deno_core::extension!(deno_lint, ops = [op_lint_create_serialized_ast,],);
#[op2]
#[buffer]
fn op_lint_create_serialized_ast(
#[string] file_name: &str,
#[string] source: String,
) -> Result<Vec<u8>, AnyError> {
let file_text = deno_ast::strip_bom(source);
let path = std::env::current_dir()?.join(file_name);
let specifier = ModuleSpecifier::from_file_path(&path).map_err(|_| {
generic_error(format!("Failed to parse path as URL: {}", path.display()))
})?;
let media_type = MediaType::from_specifier(&specifier);
let parsed_source = deno_ast::parse_program(deno_ast::ParseParams {
specifier,
text: file_text.into(),
media_type,
capture_tokens: false,
scope_analysis: false,
maybe_syntax: None,
})?;
Ok(lint::serialize_ast_to_buffer(&parsed_source))
}

View file

@ -2,4 +2,5 @@
pub mod bench;
pub mod jupyter;
pub mod lint;
pub mod testing;

View file

@ -446,7 +446,6 @@
},
"command": {
"type": "string",
"required": true,
"description": "The task to execute"
},
"dependencies": {

View file

@ -91,6 +91,7 @@ use super::serialization::DenoCompileModuleData;
use super::serialization::DeserializedDataSection;
use super::serialization::RemoteModulesStore;
use super::serialization::RemoteModulesStoreBuilder;
use super::serialization::SourceMapStore;
use super::virtual_fs::output_vfs;
use super::virtual_fs::BuiltVfs;
use super::virtual_fs::FileBackedVfs;
@ -98,6 +99,7 @@ use super::virtual_fs::VfsBuilder;
use super::virtual_fs::VfsFileSubDataKind;
use super::virtual_fs::VfsRoot;
use super::virtual_fs::VirtualDirectory;
use super::virtual_fs::VirtualDirectoryEntries;
use super::virtual_fs::WindowsSystemRootablePath;
pub static DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME: &str =
@ -203,18 +205,25 @@ pub struct Metadata {
pub otel_config: OtelConfig,
}
#[allow(clippy::too_many_arguments)]
fn write_binary_bytes(
mut file_writer: File,
original_bin: Vec<u8>,
metadata: &Metadata,
npm_snapshot: Option<SerializedNpmResolutionSnapshot>,
remote_modules: &RemoteModulesStoreBuilder,
source_map_store: &SourceMapStore,
vfs: &BuiltVfs,
compile_flags: &CompileFlags,
) -> Result<(), AnyError> {
let data_section_bytes =
serialize_binary_data_section(metadata, npm_snapshot, remote_modules, vfs)
.context("Serializing binary data section.")?;
let data_section_bytes = serialize_binary_data_section(
metadata,
npm_snapshot,
remote_modules,
source_map_store,
vfs,
)
.context("Serializing binary data section.")?;
let target = compile_flags.resolve_target();
if target.contains("linux") {
@ -256,6 +265,7 @@ pub struct StandaloneData {
pub modules: StandaloneModules,
pub npm_snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
pub root_path: PathBuf,
pub source_maps: SourceMapStore,
pub vfs: Arc<FileBackedVfs>,
}
@ -283,13 +293,12 @@ impl StandaloneModules {
pub fn read<'a>(
&'a self,
specifier: &'a ModuleSpecifier,
kind: VfsFileSubDataKind,
) -> 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, VfsFileSubDataKind::ModuleGraph)?,
Ok(entry) => self.vfs.read_file_all(entry, kind)?,
Err(err) if err.kind() == ErrorKind::NotFound => {
match RealFs.read_file_sync(&path, None) {
Ok(bytes) => bytes,
@ -307,7 +316,18 @@ impl StandaloneModules {
data: bytes,
}))
} else {
self.remote_modules.read(specifier)
self.remote_modules.read(specifier).map(|maybe_entry| {
maybe_entry.map(|entry| DenoCompileModuleData {
media_type: entry.media_type,
specifier: entry.specifier,
data: match kind {
VfsFileSubDataKind::Raw => entry.data,
VfsFileSubDataKind::ModuleGraph => {
entry.transpiled_data.unwrap_or(entry.data)
}
},
})
})
}
}
}
@ -328,7 +348,8 @@ pub fn extract_standalone(
mut metadata,
npm_snapshot,
remote_modules,
mut vfs_dir,
source_maps,
vfs_root_entries,
vfs_files_data,
} = match deserialize_binary_data_section(data)? {
Some(data_section) => data_section,
@ -351,11 +372,12 @@ pub fn extract_standalone(
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 fs_root = VfsRoot {
dir: vfs_dir,
dir: VirtualDirectory {
// align the name of the directory with the root dir
name: root_path.file_name().unwrap().to_string_lossy().to_string(),
entries: vfs_root_entries,
},
root_path: root_path.clone(),
start_file_offset: 0,
};
@ -372,6 +394,7 @@ pub fn extract_standalone(
},
npm_snapshot,
root_path,
source_maps,
vfs,
}))
}
@ -451,7 +474,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
)
}
}
self.write_standalone_binary(options, original_binary).await
self.write_standalone_binary(options, original_binary)
}
async fn get_base_binary(
@ -554,7 +577,7 @@ 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)]
async fn write_standalone_binary(
fn write_standalone_binary(
&self,
options: WriteBinOptions<'_>,
original_bin: Vec<u8>,
@ -598,71 +621,81 @@ impl<'a> DenoCompileBinaryWriter<'a> {
.with_context(|| format!("Including {}", path.display()))?;
}
let mut remote_modules_store = RemoteModulesStoreBuilder::default();
let mut code_cache_key_hasher = if self.cli_options.code_cache_enabled() {
Some(FastInsecureHasher::new_deno_versioned())
} else {
None
};
let mut source_maps = Vec::with_capacity(graph.specifiers_count());
// todo(dsherret): transpile in parallel
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
}
if let Some(hasher) = &mut code_cache_key_hasher {
if let Some(source) = module.source() {
hasher.write(module.specifier().as_str().as_bytes());
hasher.write(source.as_bytes());
}
}
let (maybe_source, media_type) = match module {
let (maybe_original_source, maybe_transpiled, media_type) = match module {
deno_graph::Module::Js(m) => {
let source = if m.media_type.is_emittable() {
let original_bytes = m.source.as_bytes().to_vec();
let maybe_transpiled = if m.media_type.is_emittable() {
let is_cjs = self.cjs_tracker.is_cjs_with_known_is_script(
&m.specifier,
m.media_type,
m.is_script,
)?;
let module_kind = ModuleKind::from_is_cjs(is_cjs);
let source = self
.emitter
.emit_parsed_source(
let (source, source_map) =
self.emitter.emit_parsed_source_for_deno_compile(
&m.specifier,
m.media_type,
module_kind,
&m.source,
)
.await?;
source.into_bytes()
)?;
if source != m.source.as_ref() {
source_maps.push((&m.specifier, source_map));
Some(source.into_bytes())
} else {
None
}
} else {
m.source.as_bytes().to_vec()
None
};
(Some(source), m.media_type)
(Some(original_bytes), maybe_transpiled, m.media_type)
}
deno_graph::Module::Json(m) => {
(Some(m.source.as_bytes().to_vec()), m.media_type)
(Some(m.source.as_bytes().to_vec()), None, m.media_type)
}
deno_graph::Module::Wasm(m) => {
(Some(m.source.to_vec()), MediaType::Wasm)
(Some(m.source.to_vec()), None, MediaType::Wasm)
}
deno_graph::Module::Npm(_)
| deno_graph::Module::Node(_)
| deno_graph::Module::External(_) => (None, MediaType::Unknown),
| deno_graph::Module::External(_) => (None, 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)?.into_owned(),
},
VfsFileSubDataKind::ModuleGraph,
)
.with_context(|| {
format!("Failed adding '{}'", file_path.display())
})?;
} else if let Some(source) = maybe_source {
remote_modules_store.add(module.specifier(), media_type, source);
if let Some(original_source) = maybe_original_source {
if module.specifier().scheme() == "file" {
let file_path = deno_path_util::url_to_file_path(module.specifier())?;
vfs
.add_file_with_data(
&file_path,
original_source,
VfsFileSubDataKind::Raw,
)
.with_context(|| {
format!("Failed adding '{}'", file_path.display())
})?;
if let Some(transpiled_source) = maybe_transpiled {
vfs
.add_file_with_data(
&file_path,
transpiled_source,
VfsFileSubDataKind::ModuleGraph,
)
.with_context(|| {
format!("Failed adding '{}'", file_path.display())
})?;
}
} else {
remote_modules_store.add(
module.specifier(),
media_type,
original_source,
maybe_transpiled,
);
}
}
}
remote_modules_store.add_redirects(&graph.redirects);
@ -695,6 +728,28 @@ impl<'a> DenoCompileBinaryWriter<'a> {
None => StandaloneRelativeFileBaseUrl::WindowsSystemRoot,
};
let code_cache_key = if self.cli_options.code_cache_enabled() {
let mut hasher = FastInsecureHasher::new_deno_versioned();
for module in graph.modules() {
if let Some(source) = module.source() {
hasher
.write(root_dir_url.specifier_key(module.specifier()).as_bytes());
hasher.write(source.as_bytes());
}
}
Some(hasher.finish())
} else {
None
};
let mut source_map_store = SourceMapStore::with_capacity(source_maps.len());
for (specifier, source_map) in source_maps {
source_map_store.add(
Cow::Owned(root_dir_url.specifier_key(specifier).into_owned()),
Cow::Owned(source_map.into_bytes()),
);
}
let node_modules = match self.npm_resolver.as_inner() {
InnerCliNpmResolverRef::Managed(_) => {
npm_snapshot.as_ref().map(|_| NodeModules::Managed {
@ -742,7 +797,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
let metadata = Metadata {
argv: compile_flags.args.clone(),
seed: self.cli_options.seed(),
code_cache_key: code_cache_key_hasher.map(|h| h.finish()),
code_cache_key,
location: self.cli_options.location_flag().clone(),
permissions: self.cli_options.permission_flags().clone(),
v8_flags: self.cli_options.v8_flags().clone(),
@ -809,6 +864,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
&metadata,
npm_snapshot.map(|s| s.into_serialized()),
&remote_modules_store,
&source_map_store,
&vfs,
compile_flags,
)
@ -903,10 +959,10 @@ impl<'a> DenoCompileBinaryWriter<'a> {
root_dir.name = DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME.to_string();
let mut new_entries = Vec::with_capacity(root_dir.entries.len());
let mut localhost_entries = IndexMap::new();
for entry in std::mem::take(&mut root_dir.entries) {
for entry in root_dir.entries.take_inner() {
match entry {
VfsEntry::Dir(dir) => {
for entry in dir.entries {
VfsEntry::Dir(mut dir) => {
for entry in dir.entries.take_inner() {
log::debug!("Flattening {} into node_modules", entry.name());
if let Some(existing) =
localhost_entries.insert(entry.name().to_string(), entry)
@ -925,11 +981,11 @@ impl<'a> DenoCompileBinaryWriter<'a> {
}
new_entries.push(VfsEntry::Dir(VirtualDirectory {
name: "localhost".to_string(),
entries: localhost_entries.into_iter().map(|(_, v)| v).collect(),
entries: VirtualDirectoryEntries::new(
localhost_entries.into_iter().map(|(_, v)| v).collect(),
),
}));
// needs to be sorted by name
new_entries.sort_by(|a, b| a.name().cmp(b.name()));
root_dir.entries = new_entries;
root_dir.entries = VirtualDirectoryEntries::new(new_entries);
// it's better to not expose the user's cache directory, so take it out
// of there
@ -937,10 +993,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
let parent_dir = vfs.get_dir_mut(parent).unwrap();
let index = parent_dir
.entries
.iter()
.position(|entry| {
entry.name() == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME
})
.binary_search(DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME)
.unwrap();
let npm_global_cache_dir_entry = parent_dir.entries.remove(index);
@ -950,11 +1003,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
Cow::Borrowed(DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME);
for ancestor in parent.ancestors() {
let dir = vfs.get_dir_mut(ancestor).unwrap();
if let Some(index) = dir
.entries
.iter()
.position(|entry| entry.name() == last_name)
{
if let Ok(index) = dir.entries.binary_search(&last_name) {
dir.entries.remove(index);
}
last_name = Cow::Owned(dir.name.clone());
@ -965,7 +1014,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
// now build the vfs and add the global cache dir entry there
let mut built_vfs = vfs.build();
built_vfs.root.insert_entry(npm_global_cache_dir_entry);
built_vfs.entries.insert(npm_global_cache_dir_entry);
built_vfs
}
InnerCliNpmResolverRef::Byonm(_) => vfs.build(),

View file

@ -55,6 +55,7 @@ use node_resolver::errors::ClosestPkgJsonError;
use node_resolver::NodeResolutionKind;
use node_resolver::ResolutionMode;
use serialization::DenoCompileModuleSource;
use serialization::SourceMapStore;
use std::borrow::Cow;
use std::rc::Rc;
use std::sync::Arc;
@ -122,6 +123,7 @@ struct SharedModuleLoaderState {
npm_module_loader: Arc<NpmModuleLoader>,
npm_req_resolver: Arc<CliNpmReqResolver>,
npm_resolver: Arc<dyn CliNpmResolver>,
source_maps: SourceMapStore,
vfs: Arc<FileBackedVfs>,
workspace_resolver: WorkspaceResolver,
}
@ -396,7 +398,11 @@ impl ModuleLoader for EmbeddedModuleLoader {
);
}
match self.shared.modules.read(original_specifier) {
match self
.shared
.modules
.read(original_specifier, VfsFileSubDataKind::ModuleGraph)
{
Ok(Some(module)) => {
let media_type = module.media_type;
let (module_specifier, module_type, module_source) =
@ -495,6 +501,45 @@ impl ModuleLoader for EmbeddedModuleLoader {
}
std::future::ready(()).boxed_local()
}
fn get_source_map(&self, file_name: &str) -> Option<Cow<[u8]>> {
if file_name.starts_with("file:///") {
let url =
deno_path_util::url_from_directory_path(self.shared.vfs.root()).ok()?;
let file_url = ModuleSpecifier::parse(file_name).ok()?;
let relative_path = url.make_relative(&file_url)?;
self.shared.source_maps.get(&relative_path)
} else {
self.shared.source_maps.get(file_name)
}
.map(Cow::Borrowed)
}
fn get_source_mapped_source_line(
&self,
file_name: &str,
line_number: usize,
) -> Option<String> {
let specifier = ModuleSpecifier::parse(file_name).ok()?;
let data = self
.shared
.modules
.read(&specifier, VfsFileSubDataKind::Raw)
.ok()??;
let source = String::from_utf8_lossy(&data.data);
// Do NOT use .lines(): it skips the terminating empty line.
// (due to internally using_terminator() instead of .split())
let lines: Vec<&str> = source.split('\n').collect();
if line_number >= lines.len() {
Some(format!(
"{} Couldn't format source line: Line {} is out of bounds (source may have changed at runtime)",
crate::colors::yellow("Warning"), line_number + 1,
))
} else {
Some(lines[line_number].to_string())
}
}
}
impl NodeRequireLoader for EmbeddedModuleLoader {
@ -590,6 +635,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
modules,
npm_snapshot,
root_path,
source_maps,
vfs,
} = data;
let deno_dir_provider = Arc::new(DenoDirProvider::new(None));
@ -841,6 +887,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
)),
npm_resolver: npm_resolver.clone(),
npm_req_resolver,
source_maps,
vfs,
workspace_resolver,
}),

View file

@ -6,6 +6,8 @@ use std::collections::BTreeMap;
use std::collections::HashMap;
use std::io::Write;
use capacity_builder::BytesAppendable;
use deno_ast::swc::common::source_map;
use deno_ast::MediaType;
use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
@ -20,12 +22,15 @@ use deno_npm::resolution::SerializedNpmResolutionSnapshotPackage;
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmPackageId;
use deno_semver::package::PackageReq;
use deno_semver::StackString;
use indexmap::IndexMap;
use crate::standalone::virtual_fs::VirtualDirectory;
use super::binary::Metadata;
use super::virtual_fs::BuiltVfs;
use super::virtual_fs::VfsBuilder;
use super::virtual_fs::VirtualDirectoryEntries;
const MAGIC_BYTES: &[u8; 8] = b"d3n0l4nd";
@ -33,21 +38,22 @@ const MAGIC_BYTES: &[u8; 8] = b"d3n0l4nd";
/// * d3n0l4nd
/// * <metadata_len><metadata>
/// * <npm_snapshot_len><npm_snapshot>
/// * <remote_modules_len><remote_modules>
/// * <remote_modules>
/// * <vfs_headers_len><vfs_headers>
/// * <vfs_file_data_len><vfs_file_data>
/// * <source_map_data>
/// * d3n0l4nd
pub fn serialize_binary_data_section(
metadata: &Metadata,
npm_snapshot: Option<SerializedNpmResolutionSnapshot>,
remote_modules: &RemoteModulesStoreBuilder,
source_map_store: &SourceMapStore,
vfs: &BuiltVfs,
) -> Result<Vec<u8>, AnyError> {
let metadata = serde_json::to_string(metadata)?;
let npm_snapshot =
npm_snapshot.map(serialize_npm_snapshot).unwrap_or_default();
let remote_modules_len = Cell::new(0_u64);
let serialized_vfs = serde_json::to_string(&vfs.root)?;
let serialized_vfs = serde_json::to_string(&vfs.entries)?;
let bytes = capacity_builder::BytesBuilder::build(|builder| {
builder.append(MAGIC_BYTES);
@ -63,10 +69,7 @@ pub fn serialize_binary_data_section(
}
// 3. Remote modules
{
builder.append_le(remote_modules_len.get()); // this will be properly initialized on the second pass
let start_index = builder.len();
remote_modules.write(builder);
remote_modules_len.set((builder.len() - start_index) as u64);
}
// 4. VFS
{
@ -78,6 +81,16 @@ pub fn serialize_binary_data_section(
builder.append(file);
}
}
// 5. Source maps
{
builder.append_le(source_map_store.data.len() as u32);
for (specifier, source_map) in &source_map_store.data {
builder.append_le(specifier.len() as u32);
builder.append(specifier);
builder.append_le(source_map.len() as u32);
builder.append(source_map.as_ref());
}
}
// write the magic bytes at the end so we can use it
// to make sure we've deserialized correctly
@ -91,19 +104,14 @@ pub struct DeserializedDataSection {
pub metadata: Metadata,
pub npm_snapshot: Option<ValidSerializedNpmResolutionSnapshot>,
pub remote_modules: RemoteModulesStore,
pub vfs_dir: VirtualDirectory,
pub source_maps: SourceMapStore,
pub vfs_root_entries: VirtualDirectoryEntries,
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.");
@ -115,34 +123,51 @@ pub fn deserialize_binary_data_section(
Ok((input, true))
}
#[allow(clippy::type_complexity)]
fn read_source_map_entry(
input: &[u8],
) -> Result<(&[u8], (Cow<str>, &[u8])), AnyError> {
let (input, specifier) = read_string_lossy(input)?;
let (input, source_map) = read_bytes_with_u32_len(input)?;
Ok((input, (specifier, source_map)))
}
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 (input, data) =
read_bytes_with_u64_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")?;
read_bytes_with_u64_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")?;
let (input, remote_modules) =
RemoteModulesStore::build(input).context("deserializing remote modules")?;
// 4. VFS
let (input, data) = read_bytes_with_len(input).context("vfs")?;
let vfs_dir: VirtualDirectory =
let (input, data) = read_bytes_with_u64_len(input).context("vfs")?;
let vfs_root_entries: VirtualDirectoryEntries =
serde_json::from_slice(data).context("deserializing vfs data")?;
let (input, vfs_files_data) =
read_bytes_with_len(input).context("reading vfs files data")?;
read_bytes_with_u64_len(input).context("reading vfs files data")?;
// 5. Source maps
let (mut input, source_map_data_len) = read_u32_as_usize(input)?;
let mut source_maps = SourceMapStore::with_capacity(source_map_data_len);
for _ in 0..source_map_data_len {
let (current_input, (specifier, source_map)) =
read_source_map_entry(input)?;
input = current_input;
source_maps.add(specifier, Cow::Borrowed(source_map));
}
// finally ensure we read the magic bytes at the end
let (_input, found) = read_magic_bytes(input)?;
@ -154,7 +179,8 @@ pub fn deserialize_binary_data_section(
metadata,
npm_snapshot,
remote_modules,
vfs_dir,
source_maps,
vfs_root_entries,
vfs_files_data,
}))
}
@ -162,19 +188,31 @@ pub fn deserialize_binary_data_section(
#[derive(Default)]
pub struct RemoteModulesStoreBuilder {
specifiers: Vec<(String, u64)>,
data: Vec<(MediaType, Vec<u8>)>,
data: Vec<(MediaType, Vec<u8>, Option<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>) {
pub fn add(
&mut self,
specifier: &Url,
media_type: MediaType,
data: Vec<u8>,
maybe_transpiled: Option<Vec<u8>>,
) {
log::debug!("Adding '{}' ({})", specifier, media_type);
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));
let maybe_transpiled_len = match &maybe_transpiled {
// data length (4 bytes), data
Some(data) => 4 + data.len() as u64,
None => 0,
};
// media type (1 byte), data length (4 bytes), data, has transpiled (1 byte), transpiled length
self.data_byte_len += 1 + 4 + data.len() as u64 + 1 + maybe_transpiled_len;
self.data.push((media_type, data, maybe_transpiled));
}
pub fn add_redirects(&mut self, redirects: &BTreeMap<Url, Url>) {
@ -188,12 +226,15 @@ impl RemoteModulesStoreBuilder {
}
}
fn write<'a>(&'a self, builder: &mut capacity_builder::BytesBuilder<'a>) {
fn write<'a, TBytes: capacity_builder::BytesType>(
&'a self,
builder: &mut capacity_builder::BytesBuilder<'a, TBytes>,
) {
builder.append_le(self.specifiers.len() as u32);
builder.append_le(self.redirects.len() as u32);
for (specifier, offset) in &self.specifiers {
builder.append_le(specifier.len() as u32);
builder.append(specifier.as_bytes());
builder.append(specifier);
builder.append_le(*offset);
}
for (from, to) in &self.redirects {
@ -202,10 +243,32 @@ impl RemoteModulesStoreBuilder {
builder.append_le(to.len() as u32);
builder.append(to);
}
for (media_type, data) in &self.data {
builder.append_le(
self
.data
.iter()
.map(|(_, data, maybe_transpiled)| {
1 + 4
+ (data.len() as u64)
+ 1
+ match maybe_transpiled {
Some(transpiled) => 4 + (transpiled.len() as u64),
None => 0,
}
})
.sum::<u64>(),
);
for (media_type, data, maybe_transpiled) in &self.data {
builder.append(serialize_media_type(*media_type));
builder.append_le(data.len() as u64);
builder.append_le(data.len() as u32);
builder.append(data);
if let Some(transpiled) = maybe_transpiled {
builder.append(1);
builder.append_le(transpiled.len() as u32);
builder.append(transpiled);
} else {
builder.append(0);
}
}
}
}
@ -234,6 +297,30 @@ impl DenoCompileModuleSource {
}
}
pub struct SourceMapStore {
data: IndexMap<Cow<'static, str>, Cow<'static, [u8]>>,
}
impl SourceMapStore {
pub fn with_capacity(capacity: usize) -> Self {
Self {
data: IndexMap::with_capacity(capacity),
}
}
pub fn add(
&mut self,
specifier: Cow<'static, str>,
source_map: Cow<'static, [u8]>,
) {
self.data.insert(specifier, source_map);
}
pub fn get(&self, specifier: &str) -> Option<&[u8]> {
self.data.get(specifier).map(|v| v.as_ref())
}
}
pub struct DenoCompileModuleData<'a> {
pub specifier: &'a Url,
pub media_type: MediaType,
@ -280,6 +367,13 @@ impl<'a> DenoCompileModuleData<'a> {
}
}
pub struct RemoteModuleEntry<'a> {
pub specifier: &'a Url,
pub media_type: MediaType,
pub data: Cow<'static, [u8]>,
pub transpiled_data: Option<Cow<'static, [u8]>>,
}
enum RemoteModulesStoreSpecifierValue {
Data(usize),
Redirect(Url),
@ -291,7 +385,7 @@ pub struct RemoteModulesStore {
}
impl RemoteModulesStore {
fn build(data: &'static [u8]) -> Result<Self, AnyError> {
fn build(input: &'static [u8]) -> Result<(&'static [u8], Self), AnyError> {
fn read_specifier(input: &[u8]) -> Result<(&[u8], (Url, u64)), AnyError> {
let (input, specifier) = read_string_lossy(input)?;
let specifier = Url::parse(&specifier)?;
@ -334,12 +428,16 @@ impl RemoteModulesStore {
Ok((input, specifiers))
}
let (files_data, specifiers) = read_headers(data)?;
let (input, specifiers) = read_headers(input)?;
let (input, files_data) = read_bytes_with_u64_len(input)?;
Ok(Self {
specifiers,
files_data,
})
Ok((
input,
Self {
specifiers,
files_data,
},
))
}
pub fn resolve_specifier<'a>(
@ -370,7 +468,7 @@ impl RemoteModulesStore {
pub fn read<'a>(
&'a self,
original_specifier: &'a Url,
) -> Result<Option<DenoCompileModuleData<'a>>, AnyError> {
) -> Result<Option<RemoteModuleEntry<'a>>, AnyError> {
let mut count = 0;
let mut specifier = original_specifier;
loop {
@ -386,12 +484,25 @@ impl RemoteModulesStore {
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 {
let (input, data) = read_bytes_with_u32_len(input)?;
check_has_len(input, 1)?;
let (input, has_transpiled) = (&input[1..], input[0]);
let (_, transpiled_data) = match has_transpiled {
0 => (input, None),
1 => {
let (input, data) = read_bytes_with_u32_len(input)?;
(input, Some(data))
}
value => bail!(
"Invalid transpiled data flag: {}. Compiled data is corrupt.",
value
),
};
return Ok(Some(RemoteModuleEntry {
specifier,
media_type,
data: Cow::Borrowed(data),
transpiled_data: transpiled_data.map(Cow::Borrowed),
}));
}
None => {
@ -475,12 +586,13 @@ fn deserialize_npm_snapshot(
#[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
) -> impl Fn(&[u8]) -> Result<(&[u8], (StackString, 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)?)))
let req = StackString::from_cow(req);
Ok((input, (req, id_to_npm_id(id)?)))
}
}
@ -630,17 +742,34 @@ fn parse_vec_n_times_with_index<TResult>(
Ok((input, results))
}
fn read_bytes_with_u64_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_bytes_with_u32_len(input: &[u8]) -> Result<(&[u8], &[u8]), AnyError> {
let (input, len) = read_u32_as_usize(input)?;
let (input, data) = read_bytes(input, len)?;
Ok((input, data))
}
fn read_bytes(input: &[u8], len: usize) -> Result<(&[u8], &[u8]), AnyError> {
if input.len() < len {
bail!("Unexpected end of data.",);
}
check_has_len(input, len)?;
let (len_bytes, input) = input.split_at(len);
Ok((input, len_bytes))
}
#[inline(always)]
fn check_has_len(input: &[u8], len: usize) -> Result<(), AnyError> {
if input.len() < len {
bail!("Unexpected end of data.");
}
Ok(())
}
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)?;
let (input, data_bytes) = read_bytes_with_u32_len(input)?;
Ok((input, String::from_utf8_lossy(data_bytes)))
}

View file

@ -67,7 +67,7 @@ impl WindowsSystemRootablePath {
#[derive(Debug)]
pub struct BuiltVfs {
pub root_path: WindowsSystemRootablePath,
pub root: VirtualDirectory,
pub entries: VirtualDirectoryEntries,
pub files: Vec<Vec<u8>>,
}
@ -95,7 +95,7 @@ impl VfsBuilder {
Self {
executable_root: VirtualDirectory {
name: "/".to_string(),
entries: Vec::new(),
entries: Default::default(),
},
files: Vec::new(),
current_offset: 0,
@ -208,23 +208,20 @@ impl VfsBuilder {
continue;
}
let name = component.as_os_str().to_string_lossy();
let index = match current_dir
.entries
.binary_search_by(|e| e.name().cmp(&name))
{
let index = match current_dir.entries.binary_search(&name) {
Ok(index) => index,
Err(insert_index) => {
current_dir.entries.insert(
current_dir.entries.0.insert(
insert_index,
VfsEntry::Dir(VirtualDirectory {
name: name.to_string(),
entries: Vec::new(),
entries: Default::default(),
}),
);
insert_index
}
};
match &mut current_dir.entries[index] {
match &mut current_dir.entries.0[index] {
VfsEntry::Dir(dir) => {
current_dir = dir;
}
@ -248,14 +245,8 @@ impl VfsBuilder {
continue;
}
let name = component.as_os_str().to_string_lossy();
let index = match current_dir
.entries
.binary_search_by(|e| e.name().cmp(&name))
{
Ok(index) => index,
Err(_) => return None,
};
match &mut current_dir.entries[index] {
let entry = current_dir.entries.get_mut_by_name(&name)?;
match entry {
VfsEntry::Dir(dir) => {
current_dir = dir;
}
@ -320,9 +311,9 @@ impl VfsBuilder {
offset,
len: data.len() as u64,
};
match dir.entries.binary_search_by(|e| e.name().cmp(&name)) {
match dir.entries.binary_search(&name) {
Ok(index) => {
let entry = &mut dir.entries[index];
let entry = &mut dir.entries.0[index];
match entry {
VfsEntry::File(virtual_file) => match sub_data_kind {
VfsFileSubDataKind::Raw => {
@ -336,7 +327,7 @@ impl VfsBuilder {
}
}
Err(insert_index) => {
dir.entries.insert(
dir.entries.0.insert(
insert_index,
VfsEntry::File(VirtualFile {
name: name.to_string(),
@ -384,10 +375,10 @@ impl VfsBuilder {
let target = normalize_path(path.parent().unwrap().join(&target));
let dir = self.add_dir_raw(path.parent().unwrap());
let name = path.file_name().unwrap().to_string_lossy();
match dir.entries.binary_search_by(|e| e.name().cmp(&name)) {
match dir.entries.binary_search(&name) {
Ok(_) => {} // previously inserted
Err(insert_index) => {
dir.entries.insert(
dir.entries.0.insert(
insert_index,
VfsEntry::Symlink(VirtualSymlink {
name: name.to_string(),
@ -426,7 +417,7 @@ impl VfsBuilder {
dir: &mut VirtualDirectory,
parts: &[String],
) {
for entry in &mut dir.entries {
for entry in &mut dir.entries.0 {
match entry {
VfsEntry::Dir(dir) => {
strip_prefix_from_symlinks(dir, parts);
@ -454,13 +445,13 @@ impl VfsBuilder {
if self.min_root_dir.as_ref() == Some(&current_path) {
break;
}
match &current_dir.entries[0] {
match &current_dir.entries.0[0] {
VfsEntry::Dir(dir) => {
if dir.name == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME {
// special directory we want to maintain
break;
}
match current_dir.entries.remove(0) {
match current_dir.entries.0.remove(0) {
VfsEntry::Dir(dir) => {
current_path =
WindowsSystemRootablePath::Path(current_path.join(&dir.name));
@ -480,7 +471,7 @@ impl VfsBuilder {
}
BuiltVfs {
root_path: current_path,
root: current_dir,
entries: current_dir.entries,
files: self.files,
}
}
@ -506,7 +497,7 @@ pub fn output_vfs(vfs: &BuiltVfs, executable_name: &str) {
return; // no need to compute if won't output
}
if vfs.root.entries.is_empty() {
if vfs.entries.is_empty() {
return; // nothing to output
}
@ -696,7 +687,7 @@ fn vfs_as_display_tree(
fn dir_size(dir: &VirtualDirectory, seen_offsets: &mut HashSet<u64>) -> Size {
let mut size = Size::default();
for entry in &dir.entries {
for entry in dir.entries.iter() {
match entry {
VfsEntry::Dir(virtual_directory) => {
size = size + dir_size(virtual_directory, seen_offsets);
@ -760,15 +751,10 @@ fn vfs_as_display_tree(
fn include_all_entries<'a>(
dir_path: &WindowsSystemRootablePath,
vfs_dir: &'a VirtualDirectory,
entries: &'a VirtualDirectoryEntries,
seen_offsets: &mut HashSet<u64>,
) -> Vec<DirEntryOutput<'a>> {
if vfs_dir.name == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME {
return show_global_node_modules_dir(vfs_dir, seen_offsets);
}
vfs_dir
.entries
entries
.iter()
.map(|entry| DirEntryOutput {
name: Cow::Borrowed(entry.name()),
@ -826,10 +812,12 @@ fn vfs_as_display_tree(
} else {
EntryOutput::Subset(children)
}
} else if vfs_dir.name == DENO_COMPILE_GLOBAL_NODE_MODULES_DIR_NAME {
EntryOutput::Subset(show_global_node_modules_dir(vfs_dir, seen_offsets))
} else {
EntryOutput::Subset(include_all_entries(
&WindowsSystemRootablePath::Path(dir),
vfs_dir,
&vfs_dir.entries,
seen_offsets,
))
}
@ -839,7 +827,7 @@ fn vfs_as_display_tree(
// user might not have context about what's being shown
let mut seen_offsets = HashSet::with_capacity(vfs.files.len());
let mut child_entries =
include_all_entries(&vfs.root_path, &vfs.root, &mut seen_offsets);
include_all_entries(&vfs.root_path, &vfs.entries, &mut seen_offsets);
for child_entry in &mut child_entries {
child_entry.collapse_leaf_nodes();
}
@ -961,27 +949,70 @@ impl VfsEntry {
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct VirtualDirectoryEntries(Vec<VfsEntry>);
impl VirtualDirectoryEntries {
pub fn new(mut entries: Vec<VfsEntry>) -> Self {
// needs to be sorted by name
entries.sort_by(|a, b| a.name().cmp(b.name()));
Self(entries)
}
pub fn take_inner(&mut self) -> Vec<VfsEntry> {
std::mem::take(&mut self.0)
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn get_by_name(&self, name: &str) -> Option<&VfsEntry> {
self.binary_search(name).ok().map(|index| &self.0[index])
}
pub fn get_mut_by_name(&mut self, name: &str) -> Option<&mut VfsEntry> {
self
.binary_search(name)
.ok()
.map(|index| &mut self.0[index])
}
pub fn binary_search(&self, name: &str) -> Result<usize, usize> {
self.0.binary_search_by(|e| e.name().cmp(name))
}
pub fn insert(&mut self, entry: VfsEntry) {
match self.binary_search(entry.name()) {
Ok(index) => {
self.0[index] = entry;
}
Err(insert_index) => {
self.0.insert(insert_index, entry);
}
}
}
pub fn remove(&mut self, index: usize) -> VfsEntry {
self.0.remove(index)
}
pub fn iter(&self) -> std::slice::Iter<'_, VfsEntry> {
self.0.iter()
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct VirtualDirectory {
#[serde(rename = "n")]
pub name: String,
// should be sorted by name
#[serde(rename = "e")]
pub entries: Vec<VfsEntry>,
}
impl VirtualDirectory {
pub fn insert_entry(&mut self, entry: VfsEntry) {
let name = entry.name();
match self.entries.binary_search_by(|e| e.name().cmp(name)) {
Ok(index) => {
self.entries[index] = entry;
}
Err(insert_index) => {
self.entries.insert(insert_index, entry);
}
}
}
pub entries: VirtualDirectoryEntries,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
@ -1136,20 +1167,13 @@ impl VfsRoot {
}
};
let component = component.to_string_lossy();
match current_dir
current_entry = current_dir
.entries
.binary_search_by(|e| e.name().cmp(&component))
{
Ok(index) => {
current_entry = current_dir.entries[index].as_ref();
}
Err(_) => {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"path not found",
));
}
}
.get_by_name(&component)
.ok_or_else(|| {
std::io::Error::new(std::io::ErrorKind::NotFound, "path not found")
})?
.as_ref();
}
Ok((final_path, current_entry))
@ -1706,7 +1730,10 @@ mod test {
FileBackedVfs::new(
Cow::Owned(data),
VfsRoot {
dir: vfs.root,
dir: VirtualDirectory {
name: "".to_string(),
entries: vfs.entries,
},
root_path: dest_path.to_path_buf(),
start_file_offset: 0,
},

View file

@ -198,7 +198,7 @@ pub struct CoverageReport {
fn generate_coverage_report(
script_coverage: &cdp::ScriptCoverage,
script_source: String,
maybe_source_map: &Option<Vec<u8>>,
maybe_source_map: Option<&[u8]>,
output: &Option<PathBuf>,
) -> CoverageReport {
let maybe_source_map = maybe_source_map
@ -625,7 +625,7 @@ pub fn cover_files(
let coverage_report = generate_coverage_report(
&script_coverage,
runtime_code.as_str().to_owned(),
&source_map,
source_map.as_deref(),
&out_mode,
);

View file

@ -343,14 +343,14 @@ impl deno_doc::html::HrefResolver for DocResolver {
let name = &res.req().name;
Some((
format!("https://www.npmjs.com/package/{name}"),
name.to_owned(),
name.to_string(),
))
}
"jsr" => {
let res =
deno_semver::jsr::JsrPackageReqReference::from_str(module).ok()?;
let name = &res.req().name;
Some((format!("https://jsr.io/{name}"), name.to_owned()))
Some((format!("https://jsr.io/{name}"), name.to_string()))
}
_ => None,
}

View file

@ -278,8 +278,10 @@ fn add_npm_packages_to_json(
});
if let Some(pkg) = maybe_package {
if let Some(module) = module.as_object_mut() {
module
.insert("npmPackage".to_string(), pkg.id.as_serialized().into());
module.insert(
"npmPackage".to_string(),
pkg.id.as_serialized().into_string().into(),
);
}
}
}
@ -296,7 +298,7 @@ fn add_npm_packages_to_json(
{
dep.insert(
"npmPackage".to_string(),
pkg.id.as_serialized().into(),
pkg.id.as_serialized().into_string().into(),
);
}
}
@ -324,19 +326,19 @@ 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.clone().into());
kv.insert("name".to_string(), pkg.id.nv.name.to_string().into());
kv.insert("version".to_string(), pkg.id.nv.version.to_string().into());
let mut deps = pkg.dependencies.values().collect::<Vec<_>>();
deps.sort();
let deps = deps
.into_iter()
.map(|id| serde_json::Value::String(id.as_serialized()))
.map(|id| serde_json::Value::String(id.as_serialized().into_string()))
.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());
json_packages.insert(pkg.id.as_serialized().into_string(), kv.into());
}
json.insert("npmPackages".to_string(), json_packages.into());
@ -549,7 +551,7 @@ impl<'a> GraphDisplayContext<'a> {
None => Specifier(module.specifier().clone()),
};
let was_seen = !self.seen.insert(match &package_or_specifier {
Package(package) => package.id.as_serialized(),
Package(package) => package.id.as_serialized().into_string(),
Specifier(specifier) => specifier.to_string(),
});
let header_text = if was_seen {
@ -631,7 +633,8 @@ impl<'a> GraphDisplayContext<'a> {
));
if let Some(package) = self.npm_info.packages.get(dep_id) {
if !package.dependencies.is_empty() {
let was_seen = !self.seen.insert(package.id.as_serialized());
let was_seen =
!self.seen.insert(package.id.as_serialized().into_string());
if was_seen {
child.text = format!("{} {}", child.text, colors::gray("*"));
} else {

View file

@ -161,11 +161,11 @@ pub async fn infer_name_from_url(
let npm_ref = npm_ref.into_inner();
if let Some(sub_path) = npm_ref.sub_path {
if !sub_path.contains('/') {
return Some(sub_path);
return Some(sub_path.to_string());
}
}
if !npm_ref.req.name.contains('/') {
return Some(npm_ref.req.name);
return Some(npm_ref.req.name.into_string());
}
return None;
}

View file

@ -1,8 +1,14 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::fmt::Display;
use deno_ast::swc::common::{Span, DUMMY_SP};
use deno_ast::swc::common::Span;
use deno_ast::swc::common::DUMMY_SP;
use indexmap::IndexMap;
/// Each property has this flag to mark what kind of value it holds-
/// Plain objects and arrays are not supported yet, but could be easily
/// added if needed.
#[derive(Debug, PartialEq)]
pub enum PropFlags {
Ref,
@ -40,6 +46,7 @@ const MASK_U32_2: u32 = 0b00000000_11111111_00000000_00000000;
const MASK_U32_3: u32 = 0b00000000_00000000_11111111_00000000;
const MASK_U32_4: u32 = 0b00000000_00000000_00000000_11111111;
// TODO: There is probably a native Rust function to do this.
pub fn append_u32(result: &mut Vec<u8>, value: u32) {
let v1: u8 = ((value & MASK_U32_1) >> 24) as u8;
let v2: u8 = ((value & MASK_U32_2) >> 16) as u8;
@ -107,8 +114,6 @@ impl StringTable {
result.append(&mut bytes.to_vec());
}
// eprintln!("Serialized string table: {:#?}", result);
result
}
}
@ -180,6 +185,13 @@ pub struct SerializeCtx {
prop_map: Vec<usize>,
}
/// This is the internal context used to allocate and fill the buffer. The point
/// is to be able to write absolute offsets directly in place.
///
/// The typical workflow is to reserve all necessary space for the currrent
/// node with placeholders for the offsets of the child nodes. Once child
/// nodes have been traversed, we know their offsets and can replace the
/// placeholder values with the actual ones.
impl SerializeCtx {
pub fn new(kind_len: u8, prop_len: u8) -> Self {
let kind_size = kind_len as usize;
@ -204,6 +216,7 @@ impl SerializeCtx {
let parent_str = ctx.str_table.insert("parent");
let range_str = ctx.str_table.insert("range");
// These values are expected to be in this order on the JS side
ctx.prop_map[0] = type_str;
ctx.prop_map[1] = parent_str;
ctx.prop_map[2] = range_str;
@ -211,6 +224,7 @@ impl SerializeCtx {
ctx
}
/// Allocate a node's header
fn field_header<P>(&mut self, prop: P, prop_flags: PropFlags) -> usize
where
P: Into<u8> + Display + Clone,
@ -233,6 +247,7 @@ impl SerializeCtx {
offset
}
/// Allocate a property pointing to another node.
fn field<P>(&mut self, prop: P, prop_flags: PropFlags) -> usize
where
P: Into<u8> + Display + Clone,
@ -253,21 +268,29 @@ impl SerializeCtx {
) -> NodeRef {
let offset = self.buf.len();
// Node type fits in a u8
self.buf.push(kind);
// Offset to the parent node. Will be 0 if none exists
append_usize(&mut self.buf, parent.0);
// Span
// Span, the start and end location of this node
append_u32(&mut self.buf, span.lo.0);
append_u32(&mut self.buf, span.hi.0);
// No node has more than <10 properties
debug_assert!(prop_count < 10);
self.buf.push(prop_count as u8);
NodeRef(offset)
}
/// Begin writing a node
/// Allocate the node header. It's always the same for every node.
/// <type u8>
/// <parent offset u32>
/// <span lo u32>
/// <span high u32>
/// <property count u8> (There is no node with more than 10 properties)
pub fn header<N>(
&mut self,
kind: N,
@ -290,6 +313,8 @@ impl SerializeCtx {
self.append_node(n, parent, span, prop_count)
}
/// Allocate a reference property that will hold the offset of
/// another node.
pub fn ref_field<P>(&mut self, prop: P) -> usize
where
P: Into<u8> + Display + Clone,
@ -297,6 +322,8 @@ impl SerializeCtx {
self.field(prop, PropFlags::Ref)
}
/// Allocate a property that is a vec of node offsets pointing to other
/// nodes.
pub fn ref_vec_field<P>(&mut self, prop: P, len: usize) -> usize
where
P: Into<u8> + Display + Clone,
@ -310,6 +337,8 @@ impl SerializeCtx {
offset
}
// Allocate a property representing a string. Strings are deduplicated
// in the message and the property will only contain the string id.
pub fn str_field<P>(&mut self, prop: P) -> usize
where
P: Into<u8> + Display + Clone,
@ -317,6 +346,7 @@ impl SerializeCtx {
self.field(prop, PropFlags::String)
}
/// Allocate a bool field
pub fn bool_field<P>(&mut self, prop: P) -> usize
where
P: Into<u8> + Display + Clone,
@ -326,6 +356,7 @@ impl SerializeCtx {
offset
}
/// Allocate an undefined field
pub fn undefined_field<P>(&mut self, prop: P) -> usize
where
P: Into<u8> + Display + Clone,
@ -333,6 +364,7 @@ impl SerializeCtx {
self.field_header(prop, PropFlags::Undefined)
}
/// Allocate an undefined field
#[allow(dead_code)]
pub fn null_field<P>(&mut self, prop: P) -> usize
where
@ -341,6 +373,8 @@ impl SerializeCtx {
self.field_header(prop, PropFlags::Null)
}
/// Replace the placeholder of a reference field with the actual offset
/// to the node we want to point to.
pub fn write_ref(&mut self, field_offset: usize, value: NodeRef) {
#[cfg(debug_assertions)]
{
@ -353,6 +387,7 @@ impl SerializeCtx {
write_usize(&mut self.buf, value.0, field_offset + 2);
}
/// Helper for writing optional node offsets
pub fn write_maybe_ref(
&mut self,
field_offset: usize,
@ -370,6 +405,8 @@ impl SerializeCtx {
write_usize(&mut self.buf, ref_value.0, field_offset + 2);
}
/// Write a vec of node offsets into the property. The necessary space
/// has been reserved earlier.
pub fn write_refs(&mut self, field_offset: usize, value: Vec<NodeRef>) {
#[cfg(debug_assertions)]
{
@ -389,6 +426,8 @@ impl SerializeCtx {
}
}
/// Store the string in our string table and save the id of the string
/// in the current field.
pub fn write_str(&mut self, field_offset: usize, value: &str) {
#[cfg(debug_assertions)]
{
@ -402,6 +441,7 @@ impl SerializeCtx {
write_usize(&mut self.buf, id, field_offset + 2);
}
/// Write a bool to a field.
pub fn write_bool(&mut self, field_offset: usize, value: bool) {
#[cfg(debug_assertions)]
{
@ -414,30 +454,58 @@ impl SerializeCtx {
self.buf[field_offset + 2] = if value { 1 } else { 0 };
}
/// Serialize all information we have into a buffer that can be sent to JS.
/// It has the following structure:
///
/// <...ast>
/// <string table>
/// <node kind map> <- node kind id maps to string id
/// <node prop map> <- node property id maps to string id
/// <offset kind map>
/// <offset prop map>
/// <offset str table>
pub fn serialize(&mut self) -> Vec<u8> {
let mut buf: Vec<u8> = vec![];
// Append serialized AST
// The buffer starts with the serialized AST first, because that
// contains absolute offsets. By butting this at the start of the
// message we don't have to waste time updating any offsets.
buf.append(&mut self.buf);
// Next follows the string table. We'll keep track of the offset
// in the message of where the string table begins
let offset_str_table = buf.len();
// Serialize string table
// eprintln!("STRING {:#?}", self.str_table);
buf.append(&mut self.str_table.serialize());
// Next, serialize the mappings of kind -> string of encountered
// nodes in the AST. We use this additional lookup table to compress
// the message so that we can save space by using a u8 . All nodes of
// JS, TS and JSX together are <200
let offset_kind_map = buf.len();
// Write the total number of entries in the kind -> str mapping table
// TODO: make this a u8
append_usize(&mut buf, self.kind_map.len());
for v in &self.kind_map {
append_usize(&mut buf, *v);
}
// Store offset to prop -> string map. It's the same as with node kind
// as the total number of properties is <120 which allows us to store it
// as u8.
let offset_prop_map = buf.len();
// Write the total number of entries in the kind -> str mapping table
append_usize(&mut buf, self.prop_map.len());
for v in &self.prop_map {
append_usize(&mut buf, *v);
}
// Putting offsets of relevant parts of the buffer at the end. This
// allows us to hop to the relevant part by merely looking at the last
// for values in the message. Each value represents an offset into the
// buffer.
append_usize(&mut buf, offset_kind_map);
append_usize(&mut buf, offset_prop_map);
append_usize(&mut buf, offset_str_table);

View file

@ -0,0 +1,13 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use deno_ast::ParsedSource;
use swc::serialize_swc_to_buffer;
mod buffer;
mod swc;
mod ts_estree;
pub fn serialize_ast_to_buffer(parsed_source: &ParsedSource) -> Vec<u8> {
// TODO: We could support multiple languages here
serialize_swc_to_buffer(parsed_source)
}

View file

@ -1,35 +1,94 @@
use deno_ast::{
swc::{
ast::{
AssignTarget, AssignTargetPat, BlockStmtOrExpr, Callee, ClassMember,
Decl, ExportSpecifier, Expr, ExprOrSpread, FnExpr, ForHead, Function,
Ident, IdentName, JSXAttrName, JSXAttrOrSpread, JSXAttrValue, JSXElement,
JSXElementChild, JSXElementName, JSXEmptyExpr, JSXExpr, JSXExprContainer,
JSXFragment, JSXMemberExpr, JSXNamespacedName, JSXObject,
JSXOpeningElement, Lit, MemberExpr, MemberProp, ModuleDecl,
ModuleExportName, ModuleItem, ObjectPatProp, OptChainBase, Param,
ParamOrTsParamProp, Pat, PrivateName, Program, Prop, PropName,
PropOrSpread, SimpleAssignTarget, Stmt, SuperProp, Tpl, TsEntityName,
TsEnumMemberId, TsFnOrConstructorType, TsFnParam, TsIndexSignature,
TsLit, TsLitType, TsThisTypeOrIdent, TsType, TsTypeAnn, TsTypeElement,
TsTypeParam, TsTypeParamDecl, TsTypeParamInstantiation, TsTypeQueryExpr,
TsUnionOrIntersectionType, VarDeclOrExpr,
},
common::{Span, Spanned, SyntaxContext},
},
view::{
Accessibility, AssignOp, BinaryOp, TruePlusMinus, TsKeywordTypeKind,
TsTypeOperatorOp, UnaryOp, UpdateOp, VarDeclKind,
},
ParsedSource,
};
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use super::{
ast_buf::{AstBufSerializer, BoolPos, NodePos, NodeRef, StrPos},
ts_estree::{AstNode, AstProp, TsEsTreeBuilder},
};
use deno_ast::swc::ast::AssignTarget;
use deno_ast::swc::ast::AssignTargetPat;
use deno_ast::swc::ast::BlockStmtOrExpr;
use deno_ast::swc::ast::Callee;
use deno_ast::swc::ast::ClassMember;
use deno_ast::swc::ast::Decl;
use deno_ast::swc::ast::ExportSpecifier;
use deno_ast::swc::ast::Expr;
use deno_ast::swc::ast::ExprOrSpread;
use deno_ast::swc::ast::FnExpr;
use deno_ast::swc::ast::ForHead;
use deno_ast::swc::ast::Function;
use deno_ast::swc::ast::Ident;
use deno_ast::swc::ast::IdentName;
use deno_ast::swc::ast::JSXAttrName;
use deno_ast::swc::ast::JSXAttrOrSpread;
use deno_ast::swc::ast::JSXAttrValue;
use deno_ast::swc::ast::JSXElement;
use deno_ast::swc::ast::JSXElementChild;
use deno_ast::swc::ast::JSXElementName;
use deno_ast::swc::ast::JSXEmptyExpr;
use deno_ast::swc::ast::JSXExpr;
use deno_ast::swc::ast::JSXExprContainer;
use deno_ast::swc::ast::JSXFragment;
use deno_ast::swc::ast::JSXMemberExpr;
use deno_ast::swc::ast::JSXNamespacedName;
use deno_ast::swc::ast::JSXObject;
use deno_ast::swc::ast::JSXOpeningElement;
use deno_ast::swc::ast::Lit;
use deno_ast::swc::ast::MemberExpr;
use deno_ast::swc::ast::MemberProp;
use deno_ast::swc::ast::ModuleDecl;
use deno_ast::swc::ast::ModuleExportName;
use deno_ast::swc::ast::ModuleItem;
use deno_ast::swc::ast::ObjectPatProp;
use deno_ast::swc::ast::OptChainBase;
use deno_ast::swc::ast::Param;
use deno_ast::swc::ast::ParamOrTsParamProp;
use deno_ast::swc::ast::Pat;
use deno_ast::swc::ast::PrivateName;
use deno_ast::swc::ast::Program;
use deno_ast::swc::ast::Prop;
use deno_ast::swc::ast::PropName;
use deno_ast::swc::ast::PropOrSpread;
use deno_ast::swc::ast::SimpleAssignTarget;
use deno_ast::swc::ast::Stmt;
use deno_ast::swc::ast::SuperProp;
use deno_ast::swc::ast::Tpl;
use deno_ast::swc::ast::TsEntityName;
use deno_ast::swc::ast::TsEnumMemberId;
use deno_ast::swc::ast::TsFnOrConstructorType;
use deno_ast::swc::ast::TsFnParam;
use deno_ast::swc::ast::TsIndexSignature;
use deno_ast::swc::ast::TsLit;
use deno_ast::swc::ast::TsLitType;
use deno_ast::swc::ast::TsThisTypeOrIdent;
use deno_ast::swc::ast::TsType;
use deno_ast::swc::ast::TsTypeAnn;
use deno_ast::swc::ast::TsTypeElement;
use deno_ast::swc::ast::TsTypeParam;
use deno_ast::swc::ast::TsTypeParamDecl;
use deno_ast::swc::ast::TsTypeParamInstantiation;
use deno_ast::swc::ast::TsTypeQueryExpr;
use deno_ast::swc::ast::TsUnionOrIntersectionType;
use deno_ast::swc::ast::VarDeclOrExpr;
use deno_ast::swc::common::Span;
use deno_ast::swc::common::Spanned;
use deno_ast::swc::common::SyntaxContext;
use deno_ast::view::Accessibility;
use deno_ast::view::AssignOp;
use deno_ast::view::BinaryOp;
use deno_ast::view::TruePlusMinus;
use deno_ast::view::TsKeywordTypeKind;
use deno_ast::view::TsTypeOperatorOp;
use deno_ast::view::UnaryOp;
use deno_ast::view::UpdateOp;
use deno_ast::view::VarDeclKind;
use deno_ast::ParsedSource;
pub fn serialize_ast_bin(parsed_source: &ParsedSource) -> Vec<u8> {
use super::buffer::AstBufSerializer;
use super::buffer::BoolPos;
use super::buffer::NodePos;
use super::buffer::NodeRef;
use super::buffer::StrPos;
use super::ts_estree::AstNode;
use super::ts_estree::AstProp;
use super::ts_estree::TsEsTreeBuilder;
pub fn serialize_swc_to_buffer(parsed_source: &ParsedSource) -> Vec<u8> {
let mut ctx = TsEsTreeBuilder::new();
let program = &parsed_source.program();
@ -37,8 +96,6 @@ pub fn serialize_ast_bin(parsed_source: &ParsedSource) -> Vec<u8> {
let pos = ctx.header(AstNode::Program, NodeRef(0), &program.span(), 2);
let source_type_pos = ctx.str_field(AstProp::SourceType);
// eprintln!("SWC {:#?}", program);
match program.as_ref() {
Program::Module(module) => {
let body_pos = ctx.ref_vec_field(AstProp::Body, module.body.len());
@ -1286,8 +1343,6 @@ fn serialize_class_member(
};
// FIXME flags
// let mut flags = FlagValue::new();
// flags.set(Flag::ClassConstructor);
let key = serialize_prop_name(ctx, &constructor.key, member_id);
let body = constructor

View file

@ -1,17 +1,26 @@
use std::fmt::{self, Debug, Display};
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::fmt;
use std::fmt::Debug;
use std::fmt::Display;
use deno_ast::swc::common::Span;
use super::ast_buf::{
AstBufSerializer, BoolPos, FieldArrPos, FieldPos, NodeRef, NullPos,
SerializeCtx, StrPos, UndefPos,
};
use super::buffer::AstBufSerializer;
use super::buffer::BoolPos;
use super::buffer::FieldArrPos;
use super::buffer::FieldPos;
use super::buffer::NodeRef;
use super::buffer::NullPos;
use super::buffer::SerializeCtx;
use super::buffer::StrPos;
use super::buffer::UndefPos;
// Keep in sync with JS
#[derive(Debug, Clone, PartialEq)]
pub enum AstNode {
// First node must always be the empty/invalid node
Invalid,
//
// Typically the
Program,
// Module declarations
@ -88,6 +97,7 @@ pub enum AstNode {
UpdateExpression,
YieldExpression,
// TODO: TSEsTree uses a single literal node
// Literals
StringLiteral,
Bool,
@ -96,7 +106,6 @@ pub enum AstNode {
BigIntLiteral,
RegExpLiteral,
// Custom
EmptyExpr,
SpreadElement,
Property,
@ -191,12 +200,14 @@ impl From<AstNode> for u8 {
#[derive(Debug, Clone)]
pub enum AstProp {
// Base, these three must be in sync with JS
// Base, these three must be in sync with JS. The
// order here for these 3 fields is important.
Type,
Parent,
Range,
// Node
// Starting from here the order doesn't matter.
// Following are all possible AST node properties.
Abstract,
Accessibility,
Alternate,
@ -301,6 +312,8 @@ pub enum AstProp {
Value, // Last value is used for max value
}
// TODO: Feels like there should be an easier way to iterater over an
// enum in Rust and lowercase the first letter.
impl Display for AstProp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
@ -425,9 +438,12 @@ pub struct TsEsTreeBuilder {
ctx: SerializeCtx,
}
// TODO: Add a builder API to make it easier to convert from different source
// ast formats.
impl TsEsTreeBuilder {
pub fn new() -> Self {
// Max values
// TODO: Maybe there is a rust macro to grab the last enum value?
let kind_count: u8 = AstNode::TSEnumBody.into();
let prop_count: u8 = AstProp::Value.into();
Self {

View file

@ -53,14 +53,14 @@ use crate::util::fs::canonicalize_path;
use crate::util::path::is_script_ext;
use crate::util::sync::AtomicFlag;
mod ast_buf;
mod ast_buffer;
mod linter;
mod plugins;
mod reporters;
mod rules;
mod swc;
mod ts_estree;
// TODO(bartlomieju): remove once we wire plugins through the CLI linter
pub use ast_buffer::serialize_ast_to_buffer;
pub use linter::CliLinter;
pub use linter::CliLinterOptions;
pub use rules::collect_no_slow_type_diagnostics;

View file

@ -26,6 +26,7 @@ use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::serde_json::Value;
use deno_core::url::Url;
use deno_runtime::deno_fetch;
use deno_terminal::colors;
use http_body_util::BodyExt;
use serde::Deserialize;
@ -911,9 +912,7 @@ async fn publish_package(
package.config
);
let body = http_body_util::Full::new(package.tarball.bytes.clone())
.map_err(|never| match never {})
.boxed();
let body = deno_fetch::ReqBody::full(package.tarball.bytes.clone());
let response = http_client
.post(url.parse()?, body)?
.header(

View file

@ -15,6 +15,7 @@ use deno_semver::jsr::JsrPackageReqReference;
use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
use deno_semver::StackString;
use deno_semver::Version;
use deno_semver::VersionReq;
use deps::KeyPath;
@ -283,7 +284,7 @@ fn package_json_dependency_entry(
(npm_package.into(), selected.version_req)
} else {
(
selected.import_name,
selected.import_name.into_string(),
format!("npm:{}@{}", npm_package, selected.version_req),
)
}
@ -292,7 +293,7 @@ fn package_json_dependency_entry(
let scope_replaced = jsr_package.replace('/', "__");
let version_req =
format!("npm:@jsr/{scope_replaced}@{}", selected.version_req);
(selected.import_name, version_req)
(selected.import_name.into_string(), version_req)
} else {
(selected.package_name, selected.version_req)
}
@ -549,10 +550,10 @@ pub async fn add(
}
struct SelectedPackage {
import_name: String,
import_name: StackString,
package_name: String,
version_req: String,
selected_version: String,
selected_version: StackString,
}
enum NotFoundHelp {
@ -683,7 +684,7 @@ async fn find_package_and_select_version_for_req(
import_name: add_package_req.alias,
package_name: prefixed_name,
version_req: format!("{}{}", range_symbol, &nv.version),
selected_version: nv.version.to_string(),
selected_version: nv.version.to_custom_string::<StackString>(),
}))
}
@ -705,7 +706,7 @@ enum AddRmPackageReqValue {
#[derive(Debug, PartialEq, Eq)]
pub struct AddRmPackageReq {
alias: String,
alias: StackString,
value: AddRmPackageReqValue,
}
@ -753,7 +754,11 @@ impl AddRmPackageReq {
return Ok(Err(PackageReq::from_str(entry_text)?));
}
(maybe_prefix.unwrap(), Some(alias.to_string()), entry_text)
(
maybe_prefix.unwrap(),
Some(StackString::from(alias)),
entry_text,
)
}
None => return Ok(Err(PackageReq::from_str(entry_text)?)),
},
@ -765,7 +770,7 @@ impl AddRmPackageReq {
JsrPackageReqReference::from_str(&format!("jsr:{}", entry_text))?;
let package_req = req_ref.into_inner().req;
Ok(Ok(AddRmPackageReq {
alias: maybe_alias.unwrap_or_else(|| package_req.name.to_string()),
alias: maybe_alias.unwrap_or_else(|| package_req.name.clone()),
value: AddRmPackageReqValue::Jsr(package_req),
}))
}
@ -785,7 +790,7 @@ impl AddRmPackageReq {
);
}
Ok(Ok(AddRmPackageReq {
alias: maybe_alias.unwrap_or_else(|| package_req.name.to_string()),
alias: maybe_alias.unwrap_or_else(|| package_req.name.clone()),
value: AddRmPackageReqValue::Npm(package_req),
}))
}
@ -878,14 +883,14 @@ mod test {
assert_eq!(
AddRmPackageReq::parse("jsr:foo").unwrap().unwrap(),
AddRmPackageReq {
alias: "foo".to_string(),
alias: "foo".into(),
value: AddRmPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap())
}
);
assert_eq!(
AddRmPackageReq::parse("alias@jsr:foo").unwrap().unwrap(),
AddRmPackageReq {
alias: "alias".to_string(),
alias: "alias".into(),
value: AddRmPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap())
}
);
@ -894,7 +899,7 @@ mod test {
.unwrap()
.unwrap(),
AddRmPackageReq {
alias: "@alias/pkg".to_string(),
alias: "@alias/pkg".into(),
value: AddRmPackageReqValue::Npm(
PackageReq::from_str("foo@latest").unwrap()
)
@ -905,7 +910,7 @@ mod test {
.unwrap()
.unwrap(),
AddRmPackageReq {
alias: "@alias/pkg".to_string(),
alias: "@alias/pkg".into(),
value: AddRmPackageReqValue::Jsr(PackageReq::from_str("foo").unwrap())
}
);
@ -914,7 +919,7 @@ mod test {
.unwrap()
.unwrap(),
AddRmPackageReq {
alias: "alias".to_string(),
alias: "alias".into(),
value: AddRmPackageReqValue::Jsr(
PackageReq::from_str("foo@^1.5.0").unwrap()
)

View file

@ -27,6 +27,7 @@ use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
use deno_semver::package::PackageReqReference;
use deno_semver::StackString;
use deno_semver::Version;
use deno_semver::VersionReq;
use import_map::ImportMap;
@ -139,13 +140,7 @@ pub enum KeyPart {
Scopes,
Dependencies,
DevDependencies,
String(String),
}
impl From<String> for KeyPart {
fn from(value: String) -> Self {
KeyPart::String(value)
}
String(StackString),
}
impl From<PackageJsonDepKind> for KeyPart {
@ -164,7 +159,7 @@ impl KeyPart {
KeyPart::Scopes => "scopes",
KeyPart::Dependencies => "dependencies",
KeyPart::DevDependencies => "devDependencies",
KeyPart::String(s) => s,
KeyPart::String(s) => s.as_str(),
}
}
}
@ -217,12 +212,12 @@ fn import_map_entries(
.chain(import_map.scopes().flat_map(|scope| {
let path = KeyPath::from_parts([
KeyPart::Scopes,
scope.raw_key.to_string().into(),
KeyPart::String(scope.raw_key.into()),
]);
scope.imports.entries().map(move |entry| {
let mut full_path = path.clone();
full_path.push(KeyPart::String(entry.raw_key.to_string()));
full_path.push(KeyPart::String(entry.raw_key.into()));
(full_path, entry)
})
}))
@ -338,7 +333,7 @@ fn add_deps_from_package_json(
package_json: &PackageJsonRc,
mut filter: impl DepFilter,
package_dep_kind: PackageJsonDepKind,
package_json_deps: PackageJsonDepsMap,
package_json_deps: &PackageJsonDepsMap,
deps: &mut Vec<Dep>,
) {
for (k, v) in package_json_deps {
@ -353,7 +348,7 @@ fn add_deps_from_package_json(
deno_package_json::PackageJsonDepValue::Req(req) => {
let alias = k.as_str();
let alias = (alias != req.name).then(|| alias.to_string());
if !filter.should_include(alias.as_deref(), &req, DepKind::Npm) {
if !filter.should_include(alias.as_deref(), req, DepKind::Npm) {
continue;
}
let id = DepId(deps.len());
@ -362,9 +357,12 @@ fn add_deps_from_package_json(
kind: DepKind::Npm,
location: DepLocation::PackageJson(
package_json.clone(),
KeyPath::from_parts([package_dep_kind.into(), k.into()]),
KeyPath::from_parts([
package_dep_kind.into(),
KeyPart::String(k.clone()),
]),
),
req,
req: req.clone(),
alias,
})
}
@ -377,14 +375,14 @@ fn add_deps_from_package_json(
package_json,
filter,
PackageJsonDepKind::Normal,
package_json_deps.dependencies,
&package_json_deps.dependencies,
deps,
);
iterate(
package_json,
filter,
PackageJsonDepKind::Dev,
package_json_deps.dev_dependencies,
&package_json_deps.dev_dependencies,
deps,
);
}

View file

@ -8,6 +8,7 @@ use deno_core::anyhow::bail;
use deno_core::error::AnyError;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
use deno_semver::StackString;
use deno_semver::VersionReq;
use deno_terminal::colors;
@ -31,7 +32,7 @@ struct OutdatedPackage {
latest: String,
semver_compatible: String,
current: String,
name: String,
name: StackString,
}
#[allow(clippy::print_stdout)]

View file

@ -231,7 +231,7 @@ pub async fn execute_script(
&Url::from_directory_path(cli_options.initial_cwd()).unwrap(),
"",
&TaskDefinition {
command: task_flags.task.as_ref().unwrap().to_string(),
command: Some(task_flags.task.as_ref().unwrap().to_string()),
dependencies: vec![],
description: None,
},
@ -448,6 +448,16 @@ impl<'a> TaskRunner<'a> {
kill_signal: KillSignal,
argv: &'a [String],
) -> Result<i32, deno_core::anyhow::Error> {
let Some(command) = &definition.command else {
log::info!(
"{} {} {}",
colors::green("Task"),
colors::cyan(task_name),
colors::gray("(no command)")
);
return Ok(0);
};
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
npm_resolver.ensure_top_level_package_json_install().await?;
npm_resolver
@ -469,7 +479,7 @@ impl<'a> TaskRunner<'a> {
self
.run_single(RunSingleOptions {
task_name,
script: &definition.command,
script: command,
cwd: &cwd,
custom_commands,
kill_signal,
@ -837,7 +847,7 @@ fn print_available_tasks(
is_deno: false,
name: name.to_string(),
task: deno_config::deno_json::TaskDefinition {
command: script.to_string(),
command: Some(script.to_string()),
dependencies: vec![],
description: None,
},
@ -873,11 +883,13 @@ fn print_available_tasks(
)?;
}
}
writeln!(
writer,
" {}",
strip_ansi_codes_and_escape_control_chars(&desc.task.command)
)?;
if let Some(command) = &desc.task.command {
writeln!(
writer,
" {}",
strip_ansi_codes_and_escape_control_chars(command)
)?;
};
if !desc.task.dependencies.is_empty() {
let dependencies = desc
.task

View file

@ -616,7 +616,10 @@ async fn configure_main_worker(
WorkerExecutionMode::Test,
specifier.clone(),
permissions_container,
vec![ops::testing::deno_test::init_ops(worker_sender.sender)],
vec![
ops::testing::deno_test::init_ops(worker_sender.sender),
ops::lint::deno_lint::init_ops(),
],
Stdio {
stdin: StdioPipe::inherit(),
stdout: StdioPipe::file(worker_sender.stdout),

View file

@ -21,6 +21,7 @@ use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::unsync::spawn;
use deno_core::url::Url;
use deno_semver::SmallStackString;
use deno_semver::Version;
use once_cell::sync::Lazy;
use std::borrow::Cow;
@ -255,7 +256,7 @@ async fn print_release_notes(
let is_deno_2_rc = new_semver.major == 2
&& new_semver.minor == 0
&& new_semver.patch == 0
&& new_semver.pre.first() == Some(&"rc".to_string());
&& new_semver.pre.first().map(|s| s.as_str()) == Some("rc");
if is_deno_2_rc || is_switching_from_deno1_to_deno2 {
log::info!(
@ -674,7 +675,7 @@ impl RequestedVersion {
);
};
if semver.pre.contains(&"rc".to_string()) {
if semver.pre.contains(&SmallStackString::from_static("rc")) {
(ReleaseChannel::Rc, passed_version)
} else {
(ReleaseChannel::Stable, passed_version)

View file

@ -41,6 +41,13 @@ delete Object.prototype.__proto__;
"listen",
"listenDatagram",
"openKv",
"connectQuic",
"listenQuic",
"QuicBidirectionalStream",
"QuicConn",
"QuicListener",
"QuicReceiveStream",
"QuicSendStream",
]);
const unstableMsgSuggestion =
"If not, try changing the 'lib' compiler option to include 'deno.unstable' " +

View file

@ -140,23 +140,23 @@ mod tests {
#[test]
fn test_source_map_from_code() {
let to_string =
|bytes: Vec<u8>| -> String { String::from_utf8(bytes).unwrap() };
|bytes: Vec<u8>| -> String { String::from_utf8(bytes.to_vec()).unwrap() };
assert_eq!(
source_map_from_code(
b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=",
b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc="
).map(to_string),
Some("testingtesting".to_string())
);
assert_eq!(
source_map_from_code(
b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=\n \n",
b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=\n \n"
).map(to_string),
Some("testingtesting".to_string())
);
assert_eq!(
source_map_from_code(
b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=\n test\n",
),
b"test\n//# sourceMappingURL=data:application/json;base64,dGVzdGluZ3Rlc3Rpbmc=\n test\n"
).map(to_string),
None
);
assert_eq!(
@ -164,7 +164,7 @@ mod tests {
b"\"use strict\";
throw new Error(\"Hello world!\");
//# sourceMappingURL=data:application/json;base64,{",
//# sourceMappingURL=data:application/json;base64,{"
),
None
);

View file

@ -612,6 +612,7 @@ impl CliMainWorkerFactory {
serve_port: shared.options.serve_port,
serve_host: shared.options.serve_host.clone(),
otel_config: shared.otel_config.clone(),
close_on_idle: true,
},
extensions: custom_extensions,
startup_snapshot: crate::js::deno_isolate_init(),
@ -655,7 +656,8 @@ impl CliMainWorkerFactory {
"40_test_common.js",
"40_test.js",
"40_bench.js",
"40_jupyter.js"
"40_jupyter.js",
"40_lint.js"
);
}
@ -812,6 +814,7 @@ fn create_web_worker_callback(
serve_port: shared.options.serve_port,
serve_host: shared.options.serve_host.clone(),
otel_config: shared.otel_config.clone(),
close_on_idle: args.close_on_idle,
},
extensions: vec![],
startup_snapshot: crate::js::deno_isolate_init(),

View file

@ -2,7 +2,7 @@
[package]
name = "deno_broadcast_channel"
version = "0.177.0"
version = "0.178.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_cache"
version = "0.115.0"
version = "0.116.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_canvas"
version = "0.52.0"
version = "0.53.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

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

View file

@ -2,7 +2,7 @@
[package]
name = "deno_cron"
version = "0.63.0"
version = "0.64.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_crypto"
version = "0.197.0"
version = "0.198.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_fetch"
version = "0.207.0"
version = "0.208.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
@ -23,6 +23,7 @@ deno_permissions.workspace = true
deno_tls.workspace = true
dyn-clone = "1"
error_reporter = "1"
h2.workspace = true
hickory-resolver.workspace = true
http.workspace = true
http-body-util.workspace = true

View file

@ -10,6 +10,7 @@ use std::borrow::Cow;
use std::cell::RefCell;
use std::cmp::min;
use std::convert::From;
use std::future;
use std::path::Path;
use std::path::PathBuf;
use std::pin::Pin;
@ -66,6 +67,7 @@ use http::header::USER_AGENT;
use http::Extensions;
use http::Method;
use http::Uri;
use http_body_util::combinators::BoxBody;
use http_body_util::BodyExt;
use hyper::body::Frame;
use hyper_util::client::legacy::connect::HttpConnector;
@ -75,6 +77,7 @@ use hyper_util::rt::TokioExecutor;
use hyper_util::rt::TokioTimer;
use serde::Deserialize;
use serde::Serialize;
use tower::retry;
use tower::ServiceExt;
use tower_http::decompression::Decompression;
@ -476,9 +479,7 @@ where
// If a body is passed, we use it, and don't return a body for streaming.
con_len = Some(data.len() as u64);
http_body_util::Full::new(data.to_vec().into())
.map_err(|never| match never {})
.boxed()
ReqBody::full(data.to_vec().into())
}
(_, Some(resource)) => {
let resource = state
@ -491,7 +492,7 @@ where
}
_ => {}
}
ReqBody::new(ResourceToBodyAdapter::new(resource))
ReqBody::streaming(ResourceToBodyAdapter::new(resource))
}
(None, None) => unreachable!(),
}
@ -501,9 +502,7 @@ where
if matches!(method, Method::POST | Method::PUT) {
con_len = Some(0);
}
http_body_util::Empty::new()
.map_err(|never| match never {})
.boxed()
ReqBody::empty()
};
let mut request = http::Request::new(body);
@ -1066,7 +1065,8 @@ pub fn create_http_client(
}
let pooled_client = builder.build(connector);
let decompress = Decompression::new(pooled_client).gzip(true).br(true);
let retry_client = retry::Retry::new(FetchRetry, pooled_client);
let decompress = Decompression::new(retry_client).gzip(true).br(true);
Ok(Client {
inner: decompress,
@ -1083,7 +1083,12 @@ pub fn op_utf8_to_byte_string(#[string] input: String) -> ByteString {
#[derive(Clone, Debug)]
pub struct Client {
inner: Decompression<hyper_util::client::legacy::Client<Connector, ReqBody>>,
inner: Decompression<
retry::Retry<
FetchRetry,
hyper_util::client::legacy::Client<Connector, ReqBody>,
>,
>,
// Used to check whether to include a proxy-authorization header
proxies: Arc<proxy::Proxies>,
user_agent: HeaderValue,
@ -1174,10 +1179,70 @@ impl Client {
}
}
pub type ReqBody =
http_body_util::combinators::BoxBody<Bytes, deno_core::error::AnyError>;
pub type ResBody =
http_body_util::combinators::BoxBody<Bytes, deno_core::error::AnyError>;
// This is a custom enum to allow the retry policy to clone the variants that could be retried.
pub enum ReqBody {
Full(http_body_util::Full<Bytes>),
Empty(http_body_util::Empty<Bytes>),
Streaming(BoxBody<Bytes, deno_core::error::AnyError>),
}
pub type ResBody = BoxBody<Bytes, deno_core::error::AnyError>;
impl ReqBody {
pub fn full(bytes: Bytes) -> Self {
ReqBody::Full(http_body_util::Full::new(bytes))
}
pub fn empty() -> Self {
ReqBody::Empty(http_body_util::Empty::new())
}
pub fn streaming<B>(body: B) -> Self
where
B: hyper::body::Body<Data = Bytes, Error = deno_core::error::AnyError>
+ Send
+ Sync
+ 'static,
{
ReqBody::Streaming(BoxBody::new(body))
}
}
impl hyper::body::Body for ReqBody {
type Data = Bytes;
type Error = deno_core::error::AnyError;
fn poll_frame(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
match &mut *self {
ReqBody::Full(ref mut b) => {
Pin::new(b).poll_frame(cx).map_err(|never| match never {})
}
ReqBody::Empty(ref mut b) => {
Pin::new(b).poll_frame(cx).map_err(|never| match never {})
}
ReqBody::Streaming(ref mut b) => Pin::new(b).poll_frame(cx),
}
}
fn is_end_stream(&self) -> bool {
match self {
ReqBody::Full(ref b) => b.is_end_stream(),
ReqBody::Empty(ref b) => b.is_end_stream(),
ReqBody::Streaming(ref b) => b.is_end_stream(),
}
}
fn size_hint(&self) -> hyper::body::SizeHint {
match self {
ReqBody::Full(ref b) => b.size_hint(),
ReqBody::Empty(ref b) => b.size_hint(),
ReqBody::Streaming(ref b) => b.size_hint(),
}
}
}
/// Copied from https://github.com/seanmonstar/reqwest/blob/b9d62a0323d96f11672a61a17bf8849baec00275/src/async_impl/request.rs#L572
/// Check the request URL for a "username:password" type authority, and if
@ -1214,3 +1279,102 @@ pub fn extract_authority(url: &mut Url) -> Option<(String, Option<String>)> {
fn op_fetch_promise_is_settled(promise: v8::Local<v8::Promise>) -> bool {
promise.state() != v8::PromiseState::Pending
}
/// Deno.fetch's retry policy.
#[derive(Clone, Debug)]
struct FetchRetry;
/// Marker extension that a request has been retried once.
#[derive(Clone, Debug)]
struct Retried;
impl<ResBody, E>
retry::Policy<http::Request<ReqBody>, http::Response<ResBody>, E>
for FetchRetry
where
E: std::error::Error + 'static,
{
/// Don't delay retries.
type Future = future::Ready<()>;
fn retry(
&mut self,
req: &mut http::Request<ReqBody>,
result: &mut Result<http::Response<ResBody>, E>,
) -> Option<Self::Future> {
if req.extensions().get::<Retried>().is_some() {
// only retry once
return None;
}
match result {
Ok(..) => {
// never retry a Response
None
}
Err(err) => {
if is_error_retryable(&*err) {
req.extensions_mut().insert(Retried);
Some(future::ready(()))
} else {
None
}
}
}
}
fn clone_request(
&mut self,
req: &http::Request<ReqBody>,
) -> Option<http::Request<ReqBody>> {
let body = match req.body() {
ReqBody::Full(b) => ReqBody::Full(b.clone()),
ReqBody::Empty(b) => ReqBody::Empty(*b),
ReqBody::Streaming(..) => return None,
};
let mut clone = http::Request::new(body);
*clone.method_mut() = req.method().clone();
*clone.uri_mut() = req.uri().clone();
*clone.headers_mut() = req.headers().clone();
*clone.extensions_mut() = req.extensions().clone();
Some(clone)
}
}
fn is_error_retryable(err: &(dyn std::error::Error + 'static)) -> bool {
// Note: hyper doesn't promise it will always be this h2 version. Keep up to date.
if let Some(err) = find_source::<h2::Error>(err) {
// They sent us a graceful shutdown, try with a new connection!
if err.is_go_away()
&& err.is_remote()
&& err.reason() == Some(h2::Reason::NO_ERROR)
{
return true;
}
// REFUSED_STREAM was sent from the server, which is safe to retry.
// https://www.rfc-editor.org/rfc/rfc9113.html#section-8.7-3.2
if err.is_reset()
&& err.is_remote()
&& err.reason() == Some(h2::Reason::REFUSED_STREAM)
{
return true;
}
}
false
}
fn find_source<'a, E: std::error::Error + 'static>(
err: &'a (dyn std::error::Error + 'static),
) -> Option<&'a E> {
let mut err = Some(err);
while let Some(src) = err {
if let Some(found) = src.downcast_ref::<E>() {
return Some(found);
}
err = src.source();
}
None
}

View file

@ -133,11 +133,7 @@ async fn rust_test_client_with_resolver(
let req = http::Request::builder()
.uri(format!("https://{}/foo", src_addr))
.body(
http_body_util::Empty::new()
.map_err(|err| match err {})
.boxed(),
)
.body(crate::ReqBody::empty())
.unwrap();
let resp = client.send(req).await.unwrap();
assert_eq!(resp.status(), http::StatusCode::OK);

View file

@ -2,7 +2,7 @@
[package]
name = "deno_ffi"
version = "0.170.0"
version = "0.171.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_fs"
version = "0.93.0"
version = "0.94.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_http"
version = "0.181.0"
version = "0.182.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_io"
version = "0.93.0"
version = "0.94.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_kv"
version = "0.91.0"
version = "0.92.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -122,9 +122,7 @@ impl RemoteTransport for FetchClient {
headers: http::HeaderMap,
body: Bytes,
) -> Result<(Url, http::StatusCode, Self::Response), anyhow::Error> {
let body = http_body_util::Full::new(body)
.map_err(|never| match never {})
.boxed();
let body = deno_fetch::ReqBody::full(body);
let mut req = http::Request::new(body);
*req.method_mut() = http::Method::POST;
*req.uri_mut() = url.as_str().parse()?;

View file

@ -2,7 +2,7 @@
[package]
name = "deno_napi"
version = "0.114.0"
version = "0.115.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

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

367
ext/net/03_quic.js Normal file
View file

@ -0,0 +1,367 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { core, primordials } from "ext:core/mod.js";
import {
op_quic_accept,
op_quic_accept_bi,
op_quic_accept_incoming,
op_quic_accept_uni,
op_quic_close_connection,
op_quic_close_endpoint,
op_quic_connect,
op_quic_connection_closed,
op_quic_connection_get_protocol,
op_quic_connection_get_remote_addr,
op_quic_endpoint_get_addr,
op_quic_get_send_stream_priority,
op_quic_incoming_accept,
op_quic_incoming_ignore,
op_quic_incoming_local_ip,
op_quic_incoming_refuse,
op_quic_incoming_remote_addr,
op_quic_incoming_remote_addr_validated,
op_quic_listen,
op_quic_max_datagram_size,
op_quic_open_bi,
op_quic_open_uni,
op_quic_read_datagram,
op_quic_send_datagram,
op_quic_set_send_stream_priority,
} from "ext:core/ops";
import {
getWritableStreamResourceBacking,
ReadableStream,
readableStreamForRid,
WritableStream,
writableStreamForRid,
} from "ext:deno_web/06_streams.js";
import { loadTlsKeyPair } from "ext:deno_net/02_tls.js";
const {
BadResourcePrototype,
} = core;
const {
Uint8Array,
TypedArrayPrototypeSubarray,
SymbolAsyncIterator,
SafePromisePrototypeFinally,
ObjectPrototypeIsPrototypeOf,
} = primordials;
class QuicSendStream extends WritableStream {
get sendOrder() {
return op_quic_get_send_stream_priority(
getWritableStreamResourceBacking(this).rid,
);
}
set sendOrder(p) {
op_quic_set_send_stream_priority(
getWritableStreamResourceBacking(this).rid,
p,
);
}
}
class QuicReceiveStream extends ReadableStream {}
function readableStream(rid, closed) {
// stream can be indirectly closed by closing connection.
SafePromisePrototypeFinally(closed, () => {
core.tryClose(rid);
});
return readableStreamForRid(rid, true, QuicReceiveStream);
}
function writableStream(rid, closed) {
// stream can be indirectly closed by closing connection.
SafePromisePrototypeFinally(closed, () => {
core.tryClose(rid);
});
return writableStreamForRid(rid, true, QuicSendStream);
}
class QuicBidirectionalStream {
#readable;
#writable;
constructor(txRid, rxRid, closed) {
this.#readable = readableStream(rxRid, closed);
this.#writable = writableStream(txRid, closed);
}
get readable() {
return this.#readable;
}
get writable() {
return this.#writable;
}
}
async function* bidiStream(conn, closed) {
try {
while (true) {
const r = await op_quic_accept_bi(conn);
yield new QuicBidirectionalStream(r[0], r[1], closed);
}
} catch (error) {
if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) {
return;
}
throw error;
}
}
async function* uniStream(conn, closed) {
try {
while (true) {
const uniRid = await op_quic_accept_uni(conn);
yield readableStream(uniRid, closed);
}
} catch (error) {
if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) {
return;
}
throw error;
}
}
class QuicConn {
#resource;
#bidiStream = null;
#uniStream = null;
#closed;
constructor(resource) {
this.#resource = resource;
this.#closed = op_quic_connection_closed(this.#resource);
core.unrefOpPromise(this.#closed);
}
get protocol() {
return op_quic_connection_get_protocol(this.#resource);
}
get remoteAddr() {
return op_quic_connection_get_remote_addr(this.#resource);
}
async createBidirectionalStream(
{ sendOrder, waitUntilAvailable } = { __proto__: null },
) {
const { 0: txRid, 1: rxRid } = await op_quic_open_bi(
this.#resource,
waitUntilAvailable ?? false,
);
if (sendOrder !== null && sendOrder !== undefined) {
op_quic_set_send_stream_priority(txRid, sendOrder);
}
return new QuicBidirectionalStream(txRid, rxRid, this.#closed);
}
async createUnidirectionalStream(
{ sendOrder, waitUntilAvailable } = { __proto__: null },
) {
const rid = await op_quic_open_uni(
this.#resource,
waitUntilAvailable ?? false,
);
if (sendOrder !== null && sendOrder !== undefined) {
op_quic_set_send_stream_priority(rid, sendOrder);
}
return writableStream(rid, this.#closed);
}
get incomingBidirectionalStreams() {
if (this.#bidiStream === null) {
this.#bidiStream = ReadableStream.from(
bidiStream(this.#resource, this.#closed),
);
}
return this.#bidiStream;
}
get incomingUnidirectionalStreams() {
if (this.#uniStream === null) {
this.#uniStream = ReadableStream.from(
uniStream(this.#resource, this.#closed),
);
}
return this.#uniStream;
}
get maxDatagramSize() {
return op_quic_max_datagram_size(this.#resource);
}
async readDatagram(p) {
const view = p || new Uint8Array(this.maxDatagramSize);
const nread = await op_quic_read_datagram(this.#resource, view);
return TypedArrayPrototypeSubarray(view, 0, nread);
}
async sendDatagram(data) {
await op_quic_send_datagram(this.#resource, data);
}
get closed() {
core.refOpPromise(this.#closed);
return this.#closed;
}
close({ closeCode, reason }) {
op_quic_close_connection(this.#resource, closeCode, reason);
}
}
class QuicIncoming {
#incoming;
constructor(incoming) {
this.#incoming = incoming;
}
get localIp() {
return op_quic_incoming_local_ip(this.#incoming);
}
get remoteAddr() {
return op_quic_incoming_remote_addr(this.#incoming);
}
get remoteAddressValidated() {
return op_quic_incoming_remote_addr_validated(this.#incoming);
}
async accept() {
const conn = await op_quic_incoming_accept(this.#incoming);
return new QuicConn(conn);
}
refuse() {
op_quic_incoming_refuse(this.#incoming);
}
ignore() {
op_quic_incoming_ignore(this.#incoming);
}
}
class QuicListener {
#endpoint;
constructor(endpoint) {
this.#endpoint = endpoint;
}
get addr() {
return op_quic_endpoint_get_addr(this.#endpoint);
}
async accept() {
const conn = await op_quic_accept(this.#endpoint);
return new QuicConn(conn);
}
async incoming() {
const incoming = await op_quic_accept_incoming(this.#endpoint);
return new QuicIncoming(incoming);
}
async next() {
let conn;
try {
conn = await this.accept();
} catch (error) {
if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) {
return { value: undefined, done: true };
}
throw error;
}
return { value: conn, done: false };
}
[SymbolAsyncIterator]() {
return this;
}
close({ closeCode, reason }) {
op_quic_close_endpoint(this.#endpoint, closeCode, reason);
}
}
async function listenQuic(
{
hostname,
port,
cert,
key,
alpnProtocols,
keepAliveInterval,
maxIdleTimeout,
maxConcurrentBidirectionalStreams,
maxConcurrentUnidirectionalStreams,
},
) {
hostname = hostname || "0.0.0.0";
const keyPair = loadTlsKeyPair("Deno.listenQuic", { cert, key });
const endpoint = await op_quic_listen(
{ hostname, port },
{ alpnProtocols },
{
keepAliveInterval,
maxIdleTimeout,
maxConcurrentBidirectionalStreams,
maxConcurrentUnidirectionalStreams,
},
keyPair,
);
return new QuicListener(endpoint);
}
async function connectQuic(
{
hostname,
port,
serverName,
caCerts,
cert,
key,
alpnProtocols,
keepAliveInterval,
maxIdleTimeout,
maxConcurrentBidirectionalStreams,
maxConcurrentUnidirectionalStreams,
congestionControl,
},
) {
const keyPair = loadTlsKeyPair("Deno.connectQuic", { cert, key });
const conn = await op_quic_connect(
{ hostname, port },
{
caCerts,
alpnProtocols,
serverName,
},
{
keepAliveInterval,
maxIdleTimeout,
maxConcurrentBidirectionalStreams,
maxConcurrentUnidirectionalStreams,
congestionControl,
},
keyPair,
);
return new QuicConn(conn);
}
export {
connectQuic,
listenQuic,
QuicBidirectionalStream,
QuicConn,
QuicIncoming,
QuicListener,
QuicReceiveStream,
QuicSendStream,
};

View file

@ -2,7 +2,7 @@
[package]
name = "deno_net"
version = "0.175.0"
version = "0.176.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
@ -20,6 +20,7 @@ deno_tls.workspace = true
hickory-proto = "0.25.0-alpha.4"
hickory-resolver.workspace = true
pin-project.workspace = true
quinn = { version = "0.11.6", default-features = false, features = ["runtime-tokio", "rustls", "ring"] }
rustls-tokio-stream.workspace = true
serde.workspace = true
socket2.workspace = true

View file

@ -450,5 +450,293 @@ declare namespace Deno {
options?: StartTlsOptions,
): Promise<TlsConn>;
/**
* **UNSTABLE**: New API, yet to be vetted.
* @experimental
* @category Network
*/
export interface QuicTransportOptions {
/** Period of inactivity before sending a keep-alive packet. Keep-alive
* packets prevent an inactive but otherwise healthy connection from timing
* out. Only one side of any given connection needs keep-alive enabled for
* the connection to be preserved.
* @default {undefined}
*/
keepAliveInterval?: number;
/** Maximum duration of inactivity to accept before timing out the
* connection. The true idle timeout is the minimum of this and the peers
* own max idle timeout.
* @default {undefined}
*/
maxIdleTimeout?: number;
/** Maximum number of incoming bidirectional streams that may be open
* concurrently.
* @default {100}
*/
maxConcurrentBidirectionalStreams?: number;
/** Maximum number of incoming unidirectional streams that may be open
* concurrently.
* @default {100}
*/
maxConcurrentUnidirectionalStreams?: number;
}
/**
* **UNSTABLE**: New API, yet to be vetted.
* @experimental
* @category Network
*/
export interface ListenQuicOptions extends QuicTransportOptions {
/** The port to connect to. */
port: number;
/**
* A literal IP address or host name that can be resolved to an IP address.
* @default {"0.0.0.0"}
*/
hostname?: string;
/** Server private key in PEM format */
key: string;
/** Cert chain in PEM format */
cert: string;
/** Application-Layer Protocol Negotiation (ALPN) protocols to announce to
* the client. QUIC requires the use of ALPN.
*/
alpnProtocols: string[];
}
/**
* **UNSTABLE**: New API, yet to be vetted.
* Listen announces on the local transport address over QUIC.
*
* ```ts
* const lstnr = await Deno.listenQuic({ port: 443, cert: "...", key: "...", alpnProtocols: ["h3"] });
* ```
*
* Requires `allow-net` permission.
*
* @experimental
* @tags allow-net
* @category Network
*/
export function listenQuic(options: ListenQuicOptions): Promise<QuicListener>;
/**
* **UNSTABLE**: New API, yet to be vetted.
* @experimental
* @category Network
*/
export interface ConnectQuicOptions extends QuicTransportOptions {
/** The port to connect to. */
port: number;
/** A literal IP address or host name that can be resolved to an IP address. */
hostname: string;
/** The name used for validating the certificate provided by the server. If
* not provided, defaults to `hostname`. */
serverName?: string | undefined;
/** Application-Layer Protocol Negotiation (ALPN) protocols supported by
* the client. QUIC requires the use of ALPN.
*/
alpnProtocols: string[];
/** A list of root certificates that will be used in addition to the
* default root certificates to verify the peer's certificate.
*
* Must be in PEM format. */
caCerts?: string[];
/**
* The congestion control algorithm used when sending data over this connection.
*/
congestionControl?: "throughput" | "low-latency";
}
/**
* **UNSTABLE**: New API, yet to be vetted.
* Establishes a secure connection over QUIC using a hostname and port. The
* cert file is optional and if not included Mozilla's root certificates will
* be used. See also https://github.com/ctz/webpki-roots for specifics.
*
* ```ts
* const caCert = await Deno.readTextFile("./certs/my_custom_root_CA.pem");
* const conn1 = await Deno.connectQuic({ hostname: "example.com", port: 443, alpnProtocols: ["h3"] });
* const conn2 = await Deno.connectQuic({ caCerts: [caCert], hostname: "example.com", port: 443, alpnProtocols: ["h3"] });
* ```
*
* Requires `allow-net` permission.
*
* @experimental
* @tags allow-net
* @category Network
*/
export function connectQuic(options: ConnectQuicOptions): Promise<QuicConn>;
/**
* **UNSTABLE**: New API, yet to be vetted.
* @experimental
* @category Network
*/
export interface QuicCloseInfo {
/** A number representing the error code for the error. */
closeCode: number;
/** A string representing the reason for closing the connection. */
reason: string;
}
/**
* **UNSTABLE**: New API, yet to be vetted.
* An incoming connection for which the server has not yet begun its part of the handshake.
*
* @experimental
* @category Network
*/
export interface QuicIncoming {
/**
* The local IP address which was used when the peer established the connection.
*/
readonly localIp: string;
/**
* The peers UDP address.
*/
readonly remoteAddr: NetAddr;
/**
* Whether the socket address that is initiating this connection has proven that they can receive traffic.
*/
readonly remoteAddressValidated: boolean;
/**
* Accept this incoming connection.
*/
accept(): Promise<QuicConn>;
/**
* Refuse this incoming connection.
*/
refuse(): void;
/**
* Ignore this incoming connection attempt, not sending any packet in response.
*/
ignore(): void;
}
/**
* **UNSTABLE**: New API, yet to be vetted.
* Specialized listener that accepts QUIC connections.
*
* @experimental
* @category Network
*/
export interface QuicListener extends AsyncIterable<QuicConn> {
/** Return the address of the `QuicListener`. */
readonly addr: NetAddr;
/** Waits for and resolves to the next connection to the `QuicListener`. */
accept(): Promise<QuicConn>;
/** Waits for and resolves to the next incoming request to the `QuicListener`. */
incoming(): Promise<QuicIncoming>;
/** Close closes the listener. Any pending accept promises will be rejected
* with errors. */
close(info: QuicCloseInfo): void;
[Symbol.asyncIterator](): AsyncIterableIterator<QuicConn>;
}
/**
* **UNSTABLE**: New API, yet to be vetted.
*
* @experimental
* @category Network
*/
export interface QuicSendStreamOptions {
/** Indicates the send priority of this stream relative to other streams for
* which the value has been set.
* @default {undefined}
*/
sendOrder?: number;
/** Wait until there is sufficient flow credit to create the stream.
* @default {false}
*/
waitUntilAvailable?: boolean;
}
/**
* **UNSTABLE**: New API, yet to be vetted.
*
* @experimental
* @category Network
*/
export interface QuicConn {
/** Close closes the listener. Any pending accept promises will be rejected
* with errors. */
close(info: QuicCloseInfo): void;
/** Opens and returns a bidirectional stream. */
createBidirectionalStream(
options?: QuicSendStreamOptions,
): Promise<QuicBidirectionalStream>;
/** Opens and returns a unidirectional stream. */
createUnidirectionalStream(
options?: QuicSendStreamOptions,
): Promise<QuicSendStream>;
/** Send a datagram. The provided data cannot be larger than
* `maxDatagramSize`. */
sendDatagram(data: Uint8Array): Promise<void>;
/** Receive a datagram. If no buffer is provider, one will be allocated.
* The size of the provided buffer should be at least `maxDatagramSize`. */
readDatagram(buffer?: Uint8Array): Promise<Uint8Array>;
/** Return the remote address for the connection. Clients may change
* addresses at will, for example when switching to a cellular internet
* connection.
*/
readonly remoteAddr: NetAddr;
/** The negotiated ALPN protocol, if provided. */
readonly protocol: string | undefined;
/** Returns a promise that resolves when the connection is closed. */
readonly closed: Promise<QuicCloseInfo>;
/** A stream of bidirectional streams opened by the peer. */
readonly incomingBidirectionalStreams: ReadableStream<
QuicBidirectionalStream
>;
/** A stream of unidirectional streams opened by the peer. */
readonly incomingUnidirectionalStreams: ReadableStream<QuicReceiveStream>;
/** Returns the datagram stream for sending and receiving datagrams. */
readonly maxDatagramSize: number;
}
/**
* **UNSTABLE**: New API, yet to be vetted.
*
* @experimental
* @category Network
*/
export interface QuicBidirectionalStream {
/** Returns a QuicReceiveStream instance that can be used to read incoming data. */
readonly readable: QuicReceiveStream;
/** Returns a QuicSendStream instance that can be used to write outgoing data. */
readonly writable: QuicSendStream;
}
/**
* **UNSTABLE**: New API, yet to be vetted.
*
* @experimental
* @category Network
*/
export interface QuicSendStream extends WritableStream<Uint8Array> {
/** Indicates the send priority of this stream relative to other streams for
* which the value has been set. */
sendOrder: number;
}
/**
* **UNSTABLE**: New API, yet to be vetted.
*
* @experimental
* @category Network
*/
export interface QuicReceiveStream extends ReadableStream<Uint8Array> {}
export {}; // only export exports
}

View file

@ -5,6 +5,7 @@ pub mod ops;
pub mod ops_tls;
#[cfg(unix)]
pub mod ops_unix;
mod quic;
pub mod raw;
pub mod resolve_addr;
pub mod tcp;
@ -158,8 +159,34 @@ deno_core::extension!(deno_net,
ops_unix::op_node_unstable_net_listen_unixpacket<P>,
ops_unix::op_net_recv_unixpacket,
ops_unix::op_net_send_unixpacket<P>,
quic::op_quic_accept,
quic::op_quic_accept_bi,
quic::op_quic_accept_incoming,
quic::op_quic_accept_uni,
quic::op_quic_close_connection,
quic::op_quic_close_endpoint,
quic::op_quic_connection_closed,
quic::op_quic_connection_get_protocol,
quic::op_quic_connection_get_remote_addr,
quic::op_quic_connect<P>,
quic::op_quic_endpoint_get_addr,
quic::op_quic_get_send_stream_priority,
quic::op_quic_incoming_accept,
quic::op_quic_incoming_refuse,
quic::op_quic_incoming_ignore,
quic::op_quic_incoming_local_ip,
quic::op_quic_incoming_remote_addr,
quic::op_quic_incoming_remote_addr_validated,
quic::op_quic_listen<P>,
quic::op_quic_max_datagram_size,
quic::op_quic_open_bi,
quic::op_quic_open_uni,
quic::op_quic_read_datagram,
quic::op_quic_send_datagram,
quic::op_quic_set_send_stream_priority,
],
esm = [ "01_net.js", "02_tls.js" ],
esm = [ "01_net.js", "02_tls.js", "03_quic.js" ],
options = {
root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>,
unsafely_ignore_certificate_errors: Option<Vec<String>>,

660
ext/net/quic.rs Normal file
View file

@ -0,0 +1,660 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use crate::resolve_addr::resolve_addr;
use crate::DefaultTlsOptions;
use crate::NetPermissions;
use crate::UnsafelyIgnoreCertificateErrors;
use deno_core::error::bad_resource;
use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::futures::task::noop_waker_ref;
use deno_core::op2;
use deno_core::AsyncRefCell;
use deno_core::AsyncResult;
use deno_core::BufView;
use deno_core::GarbageCollected;
use deno_core::JsBuffer;
use deno_core::OpState;
use deno_core::RcRef;
use deno_core::Resource;
use deno_core::ResourceId;
use deno_core::WriteOutcome;
use deno_tls::create_client_config;
use deno_tls::SocketUse;
use deno_tls::TlsKeys;
use deno_tls::TlsKeysHolder;
use quinn::crypto::rustls::QuicClientConfig;
use quinn::crypto::rustls::QuicServerConfig;
use serde::Deserialize;
use serde::Serialize;
use std::borrow::Cow;
use std::cell::RefCell;
use std::future::Future;
use std::net::IpAddr;
use std::net::Ipv4Addr;
use std::net::Ipv6Addr;
use std::net::SocketAddrV4;
use std::net::SocketAddrV6;
use std::pin::pin;
use std::rc::Rc;
use std::sync::Arc;
use std::task::Context;
use std::task::Poll;
use std::time::Duration;
#[derive(Debug, Deserialize, Serialize)]
struct Addr {
hostname: String,
port: u16,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct ListenArgs {
alpn_protocols: Option<Vec<String>>,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct TransportConfig {
keep_alive_interval: Option<u64>,
max_idle_timeout: Option<u64>,
max_concurrent_bidirectional_streams: Option<u32>,
max_concurrent_unidirectional_streams: Option<u32>,
preferred_address_v4: Option<SocketAddrV4>,
preferred_address_v6: Option<SocketAddrV6>,
congestion_control: Option<String>,
}
impl TryInto<quinn::TransportConfig> for TransportConfig {
type Error = AnyError;
fn try_into(self) -> Result<quinn::TransportConfig, AnyError> {
let mut cfg = quinn::TransportConfig::default();
if let Some(interval) = self.keep_alive_interval {
cfg.keep_alive_interval(Some(Duration::from_millis(interval)));
}
if let Some(timeout) = self.max_idle_timeout {
cfg.max_idle_timeout(Some(Duration::from_millis(timeout).try_into()?));
}
if let Some(max) = self.max_concurrent_bidirectional_streams {
cfg.max_concurrent_bidi_streams(max.into());
}
if let Some(max) = self.max_concurrent_unidirectional_streams {
cfg.max_concurrent_uni_streams(max.into());
}
if let Some(v) = self.congestion_control {
let controller: Option<
Arc<dyn quinn::congestion::ControllerFactory + Send + Sync + 'static>,
> = match v.as_str() {
"low-latency" => {
Some(Arc::new(quinn::congestion::BbrConfig::default()))
}
"throughput" => {
Some(Arc::new(quinn::congestion::CubicConfig::default()))
}
_ => None,
};
if let Some(controller) = controller {
cfg.congestion_controller_factory(controller);
}
}
Ok(cfg)
}
}
struct EndpointResource(quinn::Endpoint, Arc<QuicServerConfig>);
impl GarbageCollected for EndpointResource {}
#[op2(async)]
#[cppgc]
pub(crate) async fn op_quic_listen<NP>(
state: Rc<RefCell<OpState>>,
#[serde] addr: Addr,
#[serde] args: ListenArgs,
#[serde] transport_config: TransportConfig,
#[cppgc] keys: &TlsKeysHolder,
) -> Result<EndpointResource, AnyError>
where
NP: NetPermissions + 'static,
{
state
.borrow_mut()
.borrow_mut::<NP>()
.check_net(&(&addr.hostname, Some(addr.port)), "Deno.listenQuic()")?;
let addr = resolve_addr(&addr.hostname, addr.port)
.await?
.next()
.ok_or_else(|| generic_error("No resolved address found"))?;
let TlsKeys::Static(deno_tls::TlsKey(cert, key)) = keys.take() else {
unreachable!()
};
let mut crypto =
quinn::rustls::ServerConfig::builder_with_protocol_versions(&[
&quinn::rustls::version::TLS13,
])
.with_no_client_auth()
.with_single_cert(cert.clone(), key.clone_key())?;
if let Some(alpn_protocols) = args.alpn_protocols {
crypto.alpn_protocols = alpn_protocols
.into_iter()
.map(|alpn| alpn.into_bytes())
.collect();
}
let server_config = Arc::new(QuicServerConfig::try_from(crypto)?);
let mut config = quinn::ServerConfig::with_crypto(server_config.clone());
config.preferred_address_v4(transport_config.preferred_address_v4);
config.preferred_address_v6(transport_config.preferred_address_v6);
config.transport_config(Arc::new(transport_config.try_into()?));
let endpoint = quinn::Endpoint::server(config, addr)?;
Ok(EndpointResource(endpoint, server_config))
}
#[op2]
#[serde]
pub(crate) fn op_quic_endpoint_get_addr(
#[cppgc] endpoint: &EndpointResource,
) -> Result<Addr, AnyError> {
let addr = endpoint.0.local_addr()?;
let addr = Addr {
hostname: format!("{}", addr.ip()),
port: addr.port(),
};
Ok(addr)
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct CloseInfo {
close_code: u64,
reason: String,
}
#[op2(fast)]
pub(crate) fn op_quic_close_endpoint(
#[cppgc] endpoint: &EndpointResource,
#[bigint] close_code: u64,
#[string] reason: String,
) -> Result<(), AnyError> {
endpoint
.0
.close(quinn::VarInt::from_u64(close_code)?, reason.as_bytes());
Ok(())
}
struct ConnectionResource(quinn::Connection);
impl GarbageCollected for ConnectionResource {}
#[op2(async)]
#[cppgc]
pub(crate) async fn op_quic_accept(
#[cppgc] endpoint: &EndpointResource,
) -> Result<ConnectionResource, AnyError> {
match endpoint.0.accept().await {
Some(incoming) => {
let conn = incoming.accept()?.await?;
Ok(ConnectionResource(conn))
}
None => Err(bad_resource("QuicListener is closed")),
}
}
struct IncomingResource(
RefCell<Option<quinn::Incoming>>,
Arc<QuicServerConfig>,
);
impl GarbageCollected for IncomingResource {}
#[op2(async)]
#[cppgc]
pub(crate) async fn op_quic_accept_incoming(
#[cppgc] endpoint: &EndpointResource,
) -> Result<IncomingResource, AnyError> {
match endpoint.0.accept().await {
Some(incoming) => Ok(IncomingResource(
RefCell::new(Some(incoming)),
endpoint.1.clone(),
)),
None => Err(bad_resource("QuicListener is closed")),
}
}
#[op2]
#[string]
pub(crate) fn op_quic_incoming_local_ip(
#[cppgc] incoming_resource: &IncomingResource,
) -> Result<Option<String>, AnyError> {
let Some(incoming) = incoming_resource.0.borrow_mut().take() else {
return Err(bad_resource("QuicIncoming already used"));
};
Ok(incoming.local_ip().map(|ip| ip.to_string()))
}
#[op2]
#[serde]
pub(crate) fn op_quic_incoming_remote_addr(
#[cppgc] incoming_resource: &IncomingResource,
) -> Result<Addr, AnyError> {
let Some(incoming) = incoming_resource.0.borrow_mut().take() else {
return Err(bad_resource("QuicIncoming already used"));
};
let addr = incoming.remote_address();
Ok(Addr {
hostname: format!("{}", addr.ip()),
port: addr.port(),
})
}
#[op2(fast)]
pub(crate) fn op_quic_incoming_remote_addr_validated(
#[cppgc] incoming_resource: &IncomingResource,
) -> Result<bool, AnyError> {
let Some(incoming) = incoming_resource.0.borrow_mut().take() else {
return Err(bad_resource("QuicIncoming already used"));
};
Ok(incoming.remote_address_validated())
}
#[op2(async)]
#[cppgc]
pub(crate) async fn op_quic_incoming_accept(
#[cppgc] incoming_resource: &IncomingResource,
#[serde] transport_config: Option<TransportConfig>,
) -> Result<ConnectionResource, AnyError> {
let Some(incoming) = incoming_resource.0.borrow_mut().take() else {
return Err(bad_resource("QuicIncoming already used"));
};
let conn = match transport_config {
Some(transport_config) => {
let mut config =
quinn::ServerConfig::with_crypto(incoming_resource.1.clone());
config.preferred_address_v4(transport_config.preferred_address_v4);
config.preferred_address_v6(transport_config.preferred_address_v6);
config.transport_config(Arc::new(transport_config.try_into()?));
incoming.accept_with(Arc::new(config))?.await?
}
None => incoming.accept()?.await?,
};
Ok(ConnectionResource(conn))
}
#[op2]
#[serde]
pub(crate) fn op_quic_incoming_refuse(
#[cppgc] incoming: &IncomingResource,
) -> Result<(), AnyError> {
let Some(incoming) = incoming.0.borrow_mut().take() else {
return Err(bad_resource("QuicIncoming already used"));
};
incoming.refuse();
Ok(())
}
#[op2]
#[serde]
pub(crate) fn op_quic_incoming_ignore(
#[cppgc] incoming: &IncomingResource,
) -> Result<(), AnyError> {
let Some(incoming) = incoming.0.borrow_mut().take() else {
return Err(bad_resource("QuicIncoming already used"));
};
incoming.ignore();
Ok(())
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct ConnectArgs {
ca_certs: Option<Vec<String>>,
alpn_protocols: Option<Vec<String>>,
server_name: Option<String>,
}
#[op2(async)]
#[cppgc]
pub(crate) async fn op_quic_connect<NP>(
state: Rc<RefCell<OpState>>,
#[serde] addr: Addr,
#[serde] args: ConnectArgs,
#[serde] transport_config: TransportConfig,
#[cppgc] key_pair: &TlsKeysHolder,
) -> Result<ConnectionResource, AnyError>
where
NP: NetPermissions + 'static,
{
state
.borrow_mut()
.borrow_mut::<NP>()
.check_net(&(&addr.hostname, Some(addr.port)), "Deno.connectQuic()")?;
let sock_addr = resolve_addr(&addr.hostname, addr.port)
.await?
.next()
.ok_or_else(|| generic_error("No resolved address found"))?;
let root_cert_store = state
.borrow()
.borrow::<DefaultTlsOptions>()
.root_cert_store()?;
let unsafely_ignore_certificate_errors = state
.borrow()
.try_borrow::<UnsafelyIgnoreCertificateErrors>()
.and_then(|it| it.0.clone());
let ca_certs = args
.ca_certs
.unwrap_or_default()
.into_iter()
.map(|s| s.into_bytes())
.collect::<Vec<_>>();
let mut tls_config = create_client_config(
root_cert_store,
ca_certs,
unsafely_ignore_certificate_errors,
key_pair.take(),
SocketUse::GeneralSsl,
)?;
if let Some(alpn_protocols) = args.alpn_protocols {
tls_config.alpn_protocols =
alpn_protocols.into_iter().map(|s| s.into_bytes()).collect();
}
let client_config = QuicClientConfig::try_from(tls_config)?;
let mut client_config = quinn::ClientConfig::new(Arc::new(client_config));
client_config.transport_config(Arc::new(transport_config.try_into()?));
let local_addr = match sock_addr.ip() {
IpAddr::V4(_) => IpAddr::from(Ipv4Addr::new(0, 0, 0, 0)),
IpAddr::V6(_) => IpAddr::from(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)),
};
let conn = quinn::Endpoint::client((local_addr, 0).into())?
.connect_with(
client_config,
sock_addr,
&args.server_name.unwrap_or(addr.hostname),
)?
.await?;
Ok(ConnectionResource(conn))
}
#[op2]
#[string]
pub(crate) fn op_quic_connection_get_protocol(
#[cppgc] connection: &ConnectionResource,
) -> Option<String> {
connection
.0
.handshake_data()
.and_then(|h| h.downcast::<quinn::crypto::rustls::HandshakeData>().ok())
.and_then(|h| h.protocol)
.map(|p| String::from_utf8_lossy(&p).into_owned())
}
#[op2]
#[serde]
pub(crate) fn op_quic_connection_get_remote_addr(
#[cppgc] connection: &ConnectionResource,
) -> Result<Addr, AnyError> {
let addr = connection.0.remote_address();
Ok(Addr {
hostname: format!("{}", addr.ip()),
port: addr.port(),
})
}
#[op2(fast)]
pub(crate) fn op_quic_close_connection(
#[cppgc] connection: &ConnectionResource,
#[bigint] close_code: u64,
#[string] reason: String,
) -> Result<(), AnyError> {
connection
.0
.close(quinn::VarInt::from_u64(close_code)?, reason.as_bytes());
Ok(())
}
#[op2(async)]
#[serde]
pub(crate) async fn op_quic_connection_closed(
#[cppgc] connection: &ConnectionResource,
) -> Result<CloseInfo, AnyError> {
let e = connection.0.closed().await;
match e {
quinn::ConnectionError::LocallyClosed => Ok(CloseInfo {
close_code: 0,
reason: "".into(),
}),
quinn::ConnectionError::ApplicationClosed(i) => Ok(CloseInfo {
close_code: i.error_code.into(),
reason: String::from_utf8_lossy(&i.reason).into_owned(),
}),
e => Err(e.into()),
}
}
struct SendStreamResource(AsyncRefCell<quinn::SendStream>);
impl SendStreamResource {
fn new(stream: quinn::SendStream) -> Self {
Self(AsyncRefCell::new(stream))
}
}
impl Resource for SendStreamResource {
fn name(&self) -> Cow<str> {
"quicSendStream".into()
}
fn write(self: Rc<Self>, view: BufView) -> AsyncResult<WriteOutcome> {
Box::pin(async move {
let mut r = RcRef::map(self, |r| &r.0).borrow_mut().await;
let nwritten = r.write(&view).await?;
Ok(WriteOutcome::Partial { nwritten, view })
})
}
}
struct RecvStreamResource(AsyncRefCell<quinn::RecvStream>);
impl RecvStreamResource {
fn new(stream: quinn::RecvStream) -> Self {
Self(AsyncRefCell::new(stream))
}
}
impl Resource for RecvStreamResource {
fn name(&self) -> Cow<str> {
"quicReceiveStream".into()
}
fn read(self: Rc<Self>, limit: usize) -> AsyncResult<BufView> {
Box::pin(async move {
let mut r = RcRef::map(self, |r| &r.0).borrow_mut().await;
let mut data = vec![0; limit];
let nread = r.read(&mut data).await?.unwrap_or(0);
data.truncate(nread);
Ok(BufView::from(data))
})
}
}
#[op2(async)]
#[serde]
pub(crate) async fn op_quic_accept_bi(
#[cppgc] connection: &ConnectionResource,
state: Rc<RefCell<OpState>>,
) -> Result<(ResourceId, ResourceId), AnyError> {
match connection.0.accept_bi().await {
Ok((tx, rx)) => {
let mut state = state.borrow_mut();
let tx_rid = state.resource_table.add(SendStreamResource::new(tx));
let rx_rid = state.resource_table.add(RecvStreamResource::new(rx));
Ok((tx_rid, rx_rid))
}
Err(e) => match e {
quinn::ConnectionError::LocallyClosed
| quinn::ConnectionError::ApplicationClosed(..) => {
Err(bad_resource("QuicConn is closed"))
}
_ => Err(e.into()),
},
}
}
#[op2(async)]
#[serde]
pub(crate) async fn op_quic_open_bi(
#[cppgc] connection: &ConnectionResource,
state: Rc<RefCell<OpState>>,
wait_for_available: bool,
) -> Result<(ResourceId, ResourceId), AnyError> {
let (tx, rx) = if wait_for_available {
connection.0.open_bi().await?
} else {
let waker = noop_waker_ref();
let mut cx = Context::from_waker(waker);
match pin!(connection.0.open_bi()).poll(&mut cx) {
Poll::Ready(r) => r?,
Poll::Pending => {
return Err(generic_error("Connection has reached the maximum number of outgoing concurrent bidirectional streams"));
}
}
};
let mut state = state.borrow_mut();
let tx_rid = state.resource_table.add(SendStreamResource::new(tx));
let rx_rid = state.resource_table.add(RecvStreamResource::new(rx));
Ok((tx_rid, rx_rid))
}
#[op2(async)]
#[serde]
pub(crate) async fn op_quic_accept_uni(
#[cppgc] connection: &ConnectionResource,
state: Rc<RefCell<OpState>>,
) -> Result<ResourceId, AnyError> {
match connection.0.accept_uni().await {
Ok(rx) => {
let rid = state
.borrow_mut()
.resource_table
.add(RecvStreamResource::new(rx));
Ok(rid)
}
Err(e) => match e {
quinn::ConnectionError::LocallyClosed
| quinn::ConnectionError::ApplicationClosed(..) => {
Err(bad_resource("QuicConn is closed"))
}
_ => Err(e.into()),
},
}
}
#[op2(async)]
#[serde]
pub(crate) async fn op_quic_open_uni(
#[cppgc] connection: &ConnectionResource,
state: Rc<RefCell<OpState>>,
wait_for_available: bool,
) -> Result<ResourceId, AnyError> {
let tx = if wait_for_available {
connection.0.open_uni().await?
} else {
let waker = noop_waker_ref();
let mut cx = Context::from_waker(waker);
match pin!(connection.0.open_uni()).poll(&mut cx) {
Poll::Ready(r) => r?,
Poll::Pending => {
return Err(generic_error("Connection has reached the maximum number of outgoing concurrent unidirectional streams"));
}
}
};
let rid = state
.borrow_mut()
.resource_table
.add(SendStreamResource::new(tx));
Ok(rid)
}
#[op2(async)]
pub(crate) async fn op_quic_send_datagram(
#[cppgc] connection: &ConnectionResource,
#[buffer] buf: JsBuffer,
) -> Result<(), AnyError> {
connection.0.send_datagram_wait(buf.to_vec().into()).await?;
Ok(())
}
#[op2(async)]
pub(crate) async fn op_quic_read_datagram(
#[cppgc] connection: &ConnectionResource,
#[buffer] mut buf: JsBuffer,
) -> Result<u32, AnyError> {
let data = connection.0.read_datagram().await?;
buf[0..data.len()].copy_from_slice(&data);
Ok(data.len() as _)
}
#[op2(fast)]
pub(crate) fn op_quic_max_datagram_size(
#[cppgc] connection: &ConnectionResource,
) -> Result<u32, AnyError> {
Ok(connection.0.max_datagram_size().unwrap_or(0) as _)
}
#[op2(fast)]
pub(crate) fn op_quic_get_send_stream_priority(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
) -> Result<i32, AnyError> {
let resource = state
.borrow()
.resource_table
.get::<SendStreamResource>(rid)?;
let r = RcRef::map(resource, |r| &r.0).try_borrow();
match r {
Some(s) => Ok(s.priority()?),
None => Err(generic_error("Unable to get priority")),
}
}
#[op2(fast)]
pub(crate) fn op_quic_set_send_stream_priority(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
priority: i32,
) -> Result<(), AnyError> {
let resource = state
.borrow()
.resource_table
.get::<SendStreamResource>(rid)?;
let r = RcRef::map(resource, |r| &r.0).try_borrow();
match r {
Some(s) => {
s.set_priority(priority)?;
Ok(())
}
None => Err(generic_error("Unable to set priority")),
}
}

View file

@ -2,7 +2,7 @@
[package]
name = "deno_node"
version = "0.120.0"
version = "0.122.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -73,12 +73,17 @@ pub fn cpu_info() -> Option<Vec<CpuInfo>> {
cpu_speed = 2_400_000_000;
}
extern "C" {
fn mach_host_self() -> std::ffi::c_uint;
static mut mach_task_self_: std::ffi::c_uint;
}
let mut num_cpus: libc::natural_t = 0;
let mut info: *mut libc::processor_cpu_load_info_data_t =
std::ptr::null_mut();
let mut msg_type: libc::mach_msg_type_number_t = 0;
if libc::host_processor_info(
libc::mach_host_self(),
mach_host_self(),
libc::PROCESSOR_CPU_LOAD_INFO,
&mut num_cpus,
&mut info as *mut _ as *mut libc::processor_info_array_t,
@ -111,7 +116,7 @@ pub fn cpu_info() -> Option<Vec<CpuInfo>> {
}
libc::vm_deallocate(
libc::mach_task_self(),
mach_task_self_,
info.as_ptr() as libc::vm_address_t,
msg_type as _,
);

View file

@ -67,9 +67,9 @@ generate_builtin_node_module_lists! {
"process",
"punycode",
"querystring",
"repl",
"readline",
"readline/promises",
"repl",
"stream",
"stream/consumers",
"stream/promises",
@ -90,3 +90,10 @@ generate_builtin_node_module_lists! {
"worker_threads",
"zlib",
}
#[test]
fn test_builtins_are_sorted() {
let mut builtins_list = SUPPORTED_BUILTIN_NODE_MODULES.to_vec();
builtins_list.sort();
assert_eq!(SUPPORTED_BUILTIN_NODE_MODULES, builtins_list);
}

View file

@ -30,50 +30,58 @@ export function access(
mode = getValidMode(mode, "access");
const cb = makeCallback(callback);
Deno.lstat(path).then((info) => {
if (info.mode === null) {
// If the file mode is unavailable, we pretend it has
// the permission
cb(null);
return;
}
const m = +mode || 0;
let fileMode = +info.mode || 0;
if (Deno.build.os !== "windows" && info.uid === Deno.uid()) {
// If the user is the owner of the file, then use the owner bits of
// the file permission
fileMode >>= 6;
}
// TODO(kt3k): Also check the case when the user belong to the group
// of the file
if ((m & fileMode) === m) {
// all required flags exist
cb(null);
} else {
// some required flags don't
// deno-lint-ignore no-explicit-any
const e: any = new Error(`EACCES: permission denied, access '${path}'`);
e.path = path;
e.syscall = "access";
e.errno = codeMap.get("EACCES");
e.code = "EACCES";
cb(e);
}
}, (err) => {
if (err instanceof Deno.errors.NotFound) {
// deno-lint-ignore no-explicit-any
const e: any = new Error(
`ENOENT: no such file or directory, access '${path}'`,
);
e.path = path;
e.syscall = "access";
e.errno = codeMap.get("ENOENT");
e.code = "ENOENT";
cb(e);
} else {
cb(err);
}
});
Deno.lstat(path).then(
(info) => {
if (info.mode === null) {
// If the file mode is unavailable, we pretend it has
// the permission
cb(null);
return;
}
let m = +mode || 0;
let fileMode = +info.mode || 0;
if (Deno.build.os === "windows") {
m &= ~fs.X_OK; // Ignore the X_OK bit on Windows
} else if (info.uid === Deno.uid()) {
// If the user is the owner of the file, then use the owner bits of
// the file permission
fileMode >>= 6;
}
// TODO(kt3k): Also check the case when the user belong to the group
// of the file
if ((m & fileMode) === m) {
// all required flags exist
cb(null);
} else {
// some required flags don't
// deno-lint-ignore no-explicit-any
const e: any = new Error(`EACCES: permission denied, access '${path}'`);
e.path = path;
e.syscall = "access";
e.errno = codeMap.get("EACCES");
e.code = "EACCES";
cb(e);
}
},
(err) => {
if (err instanceof Deno.errors.NotFound) {
// deno-lint-ignore no-explicit-any
const e: any = new Error(
`ENOENT: no such file or directory, access '${path}'`,
);
e.path = path;
e.syscall = "access";
e.errno = codeMap.get("ENOENT");
e.code = "ENOENT";
cb(e);
} else {
cb(err);
}
},
);
}
export const accessPromise = promisify(access) as (
@ -91,9 +99,11 @@ export function accessSync(path: string | Buffer | URL, mode?: number) {
// the permission
return;
}
const m = +mode! || 0;
let m = +mode! || 0;
let fileMode = +info.mode! || 0;
if (Deno.build.os !== "windows" && info.uid === Deno.uid()) {
if (Deno.build.os === "windows") {
m &= ~fs.X_OK; // Ignore the X_OK bit on Windows
} else if (info.uid === Deno.uid()) {
// If the user is the owner of the file, then use the owner bits of
// the file permission
fileMode >>= 6;

View file

@ -16,16 +16,24 @@ export function ftruncate(
: undefined;
const callback: CallbackWithError = typeof lenOrCallback === "function"
? lenOrCallback
: maybeCallback as CallbackWithError;
: (maybeCallback as CallbackWithError);
if (!callback) throw new Error("No callback function supplied");
new FsFile(fd, Symbol.for("Deno.internal.FsFile")).truncate(len).then(
() => callback(null),
callback,
);
new FsFile(fd, Symbol.for("Deno.internal.FsFile"))
.truncate(len)
.then(() => callback(null), callback);
}
export function ftruncateSync(fd: number, len?: number) {
new FsFile(fd, Symbol.for("Deno.internal.FsFile")).truncateSync(len);
}
export function ftruncatePromise(fd: number, len?: number): Promise<void> {
return new Promise((resolve, reject) => {
ftruncate(fd, len, (err) => {
if (err) reject(err);
else resolve();
});
});
}

View file

@ -13,6 +13,7 @@ import {
ReadOptions,
TextOptionsArgument,
} from "ext:deno_node/_fs/_fs_common.ts";
import { ftruncatePromise } from "ext:deno_node/_fs/_fs_ftruncate.ts";
import { core } from "ext:core/mod.js";
interface WriteResult {
@ -73,6 +74,10 @@ export class FileHandle extends EventEmitter {
}
}
truncate(len?: number): Promise<void> {
return fsCall(ftruncatePromise, this, len);
}
readFile(
opt?: TextOptionsArgument | BinaryOptionsArgument | FileOptionsArgument,
): Promise<string | Buffer> {
@ -85,11 +90,7 @@ export class FileHandle extends EventEmitter {
length: number,
position: number,
): Promise<WriteResult>;
write(
str: string,
position: number,
encoding: string,
): Promise<WriteResult>;
write(str: string, position: number, encoding: string): Promise<WriteResult>;
write(
bufferOrStr: Uint8Array | string,
offsetOrPosition: number,
@ -120,16 +121,10 @@ export class FileHandle extends EventEmitter {
const encoding = lengthOrEncoding;
return new Promise((resolve, reject) => {
write(
this.fd,
str,
position,
encoding,
(err, bytesWritten, buffer) => {
if (err) reject(err);
else resolve({ buffer, bytesWritten });
},
);
write(this.fd, str, position, encoding, (err, bytesWritten, buffer) => {
if (err) reject(err);
else resolve({ buffer, bytesWritten });
});
});
}
}

View file

@ -21,7 +21,7 @@ import {
nodeWorkerThreadCloseCb,
refMessagePort,
serializeJsMessageData,
unrefPollForMessages,
unrefParentPort,
} from "ext:deno_web/13_message_port.js";
import * as webidl from "ext:deno_webidl/00_webidl.js";
import { notImplemented } from "ext:deno_node/_utils.ts";
@ -451,10 +451,10 @@ internals.__initWorkerThreads = (
parentPort.emit("close");
});
parentPort.unref = () => {
parentPort[unrefPollForMessages] = true;
parentPort[unrefParentPort] = true;
};
parentPort.ref = () => {
parentPort[unrefPollForMessages] = false;
parentPort[unrefParentPort] = false;
};
if (isWorkerThread) {

View file

@ -2,7 +2,7 @@
[package]
name = "deno_telemetry"
version = "0.5.0"
version = "0.6.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_tls"
version = "0.170.0"
version = "0.171.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_url"
version = "0.183.0"
version = "0.184.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -908,8 +908,8 @@ const _original = Symbol("[[original]]");
* @param {boolean=} autoClose If the resource should be auto-closed when the stream closes. Defaults to true.
* @returns {ReadableStream<Uint8Array>}
*/
function readableStreamForRid(rid, autoClose = true) {
const stream = new ReadableStream(_brand);
function readableStreamForRid(rid, autoClose = true, Super) {
const stream = new (Super ?? ReadableStream)(_brand);
stream[_resourceBacking] = { rid, autoClose };
const tryClose = () => {
@ -1130,8 +1130,8 @@ async function readableStreamCollectIntoUint8Array(stream) {
* @param {boolean=} autoClose If the resource should be auto-closed when the stream closes. Defaults to true.
* @returns {ReadableStream<Uint8Array>}
*/
function writableStreamForRid(rid, autoClose = true) {
const stream = new WritableStream(_brand);
function writableStreamForRid(rid, autoClose = true, Super) {
const stream = new (Super ?? WritableStream)(_brand);
stream[_resourceBacking] = { rid, autoClose };
const tryClose = () => {

View file

@ -102,8 +102,8 @@ const nodeWorkerThreadCloseCb = Symbol("nodeWorkerThreadCloseCb");
const nodeWorkerThreadCloseCbInvoked = Symbol("nodeWorkerThreadCloseCbInvoked");
export const refMessagePort = Symbol("refMessagePort");
/** It is used by 99_main.js and worker_threads to
* unref/ref on the global pollForMessages promise. */
export const unrefPollForMessages = Symbol("unrefPollForMessages");
* unref/ref on the global message event handler count. */
export const unrefParentPort = Symbol("unrefParentPort");
/**
* @param {number} id

View file

@ -2,7 +2,7 @@
[package]
name = "deno_web"
version = "0.214.0"
version = "0.215.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_webgpu"
version = "0.150.0"
version = "0.151.0"
authors = ["the Deno authors"]
edition.workspace = true
license = "MIT"

View file

@ -2,7 +2,7 @@
[package]
name = "deno_webidl"
version = "0.183.0"
version = "0.184.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_websocket"
version = "0.188.0"
version = "0.189.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -2,7 +2,7 @@
[package]
name = "deno_webstorage"
version = "0.178.0"
version = "0.179.0"
authors.workspace = true
edition.workspace = true
license.workspace = true

View file

@ -9,6 +9,7 @@ use deno_package_json::PackageJsonDepValue;
use deno_package_json::PackageJsonRc;
use deno_path_util::url_to_file_path;
use deno_semver::package::PackageReq;
use deno_semver::StackString;
use deno_semver::Version;
use node_resolver::env::NodeResolverEnv;
use node_resolver::errors::PackageFolderResolveError;
@ -30,7 +31,7 @@ use super::ResolvePkgFolderFromDenoReqError;
#[derive(Debug, Error)]
pub enum ByonmResolvePkgFolderFromDenoReqError {
#[error("Could not find \"{}\" in a node_modules folder. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?", .0)]
MissingAlias(String),
MissingAlias(StackString),
#[error(transparent)]
PackageJson(#[from] PackageJsonLoadError),
#[error("Could not find a matching package for 'npm:{}' in the node_modules directory. Ensure you have all your JSR and npm dependencies listed in your deno.json or package.json, then run `deno install`. Alternatively, turn on auto-install by specifying `\"nodeModulesDir\": \"auto\"` in your deno.json file.", .0)]
@ -177,16 +178,14 @@ impl<Fs: DenoResolverFs, TEnv: NodeResolverEnv> ByonmNpmResolver<Fs, TEnv> {
&self,
req: &PackageReq,
referrer: &Url,
) -> Result<Option<(PackageJsonRc, String)>, PackageJsonLoadError> {
) -> Result<Option<(PackageJsonRc, StackString)>, PackageJsonLoadError> {
fn resolve_alias_from_pkg_json(
req: &PackageReq,
pkg_json: &PackageJson,
) -> Option<String> {
) -> Option<StackString> {
let deps = pkg_json.resolve_local_package_json_deps();
for (key, value) in deps
.dependencies
.into_iter()
.chain(deps.dev_dependencies.into_iter())
for (key, value) in
deps.dependencies.iter().chain(deps.dev_dependencies.iter())
{
if let Ok(value) = value {
match value {
@ -194,12 +193,14 @@ impl<Fs: DenoResolverFs, TEnv: NodeResolverEnv> ByonmNpmResolver<Fs, TEnv> {
if dep_req.name == req.name
&& dep_req.version_req.intersects(&req.version_req)
{
return Some(key);
return Some(key.clone());
}
}
PackageJsonDepValue::Workspace(_workspace) => {
if key == req.name && req.version_req.tag() == Some("workspace") {
return Some(key);
if key.as_str() == req.name
&& req.version_req.tag() == Some("workspace")
{
return Some(key.clone());
}
}
}
@ -246,7 +247,7 @@ impl<Fs: DenoResolverFs, TEnv: NodeResolverEnv> ByonmNpmResolver<Fs, TEnv> {
if let Ok(Some(dep_pkg_json)) =
self.load_pkg_json(&pkg_folder.join("package.json"))
{
if dep_pkg_json.name.as_ref() == Some(&req.name) {
if dep_pkg_json.name.as_deref() == Some(req.name.as_str()) {
let matches_req = dep_pkg_json
.version
.as_ref()

View file

@ -320,7 +320,6 @@ impl NodeJsErrorCoded for PackageJsonLoadError {
impl NodeJsErrorCoded for ClosestPkgJsonError {
fn code(&self) -> NodeJsErrorCode {
match self.as_kind() {
ClosestPkgJsonErrorKind::CanonicalizingDir(e) => e.code(),
ClosestPkgJsonErrorKind::Load(e) => e.code(),
}
}
@ -331,26 +330,10 @@ pub struct ClosestPkgJsonError(pub Box<ClosestPkgJsonErrorKind>);
#[derive(Debug, Error)]
pub enum ClosestPkgJsonErrorKind {
#[error(transparent)]
CanonicalizingDir(#[from] CanonicalizingPkgJsonDirError),
#[error(transparent)]
Load(#[from] PackageJsonLoadError),
}
#[derive(Debug, Error)]
#[error("[{}] Failed canonicalizing package.json directory '{}'.", self.code(), dir_path.display())]
pub struct CanonicalizingPkgJsonDirError {
pub dir_path: PathBuf,
#[source]
pub source: std::io::Error,
}
impl NodeJsErrorCoded for CanonicalizingPkgJsonDirError {
fn code(&self) -> NodeJsErrorCode {
NodeJsErrorCode::ERR_MODULE_NOT_FOUND
}
}
// todo(https://github.com/denoland/deno_core/issues/810): make this a TypeError
#[derive(Debug, Error)]
#[error(

View file

@ -2,7 +2,6 @@
use deno_package_json::PackageJson;
use deno_package_json::PackageJsonRc;
use deno_path_util::strip_unc_prefix;
use std::cell::RefCell;
use std::collections::HashMap;
use std::io::ErrorKind;
@ -11,7 +10,6 @@ use std::path::PathBuf;
use url::Url;
use crate::env::NodeResolverEnv;
use crate::errors::CanonicalizingPkgJsonDirError;
use crate::errors::ClosestPkgJsonError;
use crate::errors::PackageJsonLoadError;
@ -67,37 +65,8 @@ impl<TEnv: NodeResolverEnv> PackageJsonResolver<TEnv> {
&self,
file_path: &Path,
) -> Result<Option<PackageJsonRc>, ClosestPkgJsonError> {
// we use this for deno compile using byonm because the script paths
// won't be in virtual file system, but the package.json paths will be
fn canonicalize_first_ancestor_exists<TEnv: NodeResolverEnv>(
dir_path: &Path,
env: &TEnv,
) -> Result<Option<PathBuf>, std::io::Error> {
for ancestor in dir_path.ancestors() {
match env.realpath_sync(ancestor) {
Ok(dir_path) => return Ok(Some(dir_path)),
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
// keep searching
}
Err(err) => return Err(err),
}
}
Ok(None)
}
let parent_dir = file_path.parent().unwrap();
let Some(start_dir) = canonicalize_first_ancestor_exists(
parent_dir, &self.env,
)
.map_err(|source| CanonicalizingPkgJsonDirError {
dir_path: parent_dir.to_path_buf(),
source,
})?
else {
return Ok(None);
};
let start_dir = strip_unc_prefix(start_dir);
for current_dir in start_dir.ancestors() {
for current_dir in parent_dir.ancestors() {
let package_json_path = current_dir.join("package.json");
if let Some(pkg_json) = self.load_package_json(&package_json_path)? {
return Ok(Some(pkg_json));

View file

@ -318,6 +318,8 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
resolution_mode: ResolutionMode,
resolution_kind: NodeResolutionKind,
) -> Result<Url, PackageSubpathResolveError> {
// todo(dsherret): don't allocate a string here (maybe use an
// enum that says the subpath is not prefixed with a ./)
let package_subpath = package_subpath
.map(|s| format!("./{s}"))
.unwrap_or_else(|| ".".to_string());

View file

@ -23,6 +23,7 @@ async-trait.workspace = true
base64.workspace = true
boxed_error.workspace = true
deno_cache_dir.workspace = true
deno_error.workspace = true
deno_npm.workspace = true
deno_semver.workspace = true
deno_unsync = { workspace = true, features = ["tokio"] }

View file

@ -15,6 +15,7 @@ use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::registry::NpmPackageInfo;
use deno_npm::NpmPackageCacheFolderId;
use deno_semver::package::PackageNv;
use deno_semver::StackString;
use deno_semver::Version;
use http::HeaderName;
use http::HeaderValue;
@ -260,7 +261,7 @@ impl<TEnv: NpmCacheEnv> NpmCache<TEnv> {
.and_then(|cache_id| {
Some(NpmPackageCacheFolderId {
nv: PackageNv {
name: cache_id.name,
name: StackString::from_string(cache_id.name),
version: Version::parse_from_npm(&cache_id.version).ok()?,
},
copy_index: cache_id.copy_index,

View file

@ -18,6 +18,7 @@ use deno_unsync::sync::MultiRuntimeAsyncValueCreator;
use futures::future::LocalBoxFuture;
use futures::FutureExt;
use parking_lot::Mutex;
use thiserror::Error;
use url::Url;
use crate::remote::maybe_auth_header_for_npm_registry;
@ -28,6 +29,31 @@ use crate::NpmCacheSetting;
type LoadResult = Result<FutureResult, Arc<AnyError>>;
type LoadFuture = LocalBoxFuture<'static, LoadResult>;
#[derive(Debug, Error)]
#[error(transparent)]
pub struct AnyhowJsError(pub AnyError);
impl deno_error::JsErrorClass for AnyhowJsError {
fn get_class(&self) -> &'static str {
"generic"
}
fn get_message(&self) -> std::borrow::Cow<'static, str> {
self.0.to_string().into()
}
fn get_additional_properties(
&self,
) -> Option<
Vec<(
std::borrow::Cow<'static, str>,
std::borrow::Cow<'static, str>,
)>,
> {
None
}
}
#[derive(Debug, Clone)]
enum FutureResult {
PackageNotExists,
@ -157,9 +183,9 @@ impl<TEnv: NpmCacheEnv> RegistryInfoProvider<TEnv> {
Ok(None) => Err(NpmRegistryPackageInfoLoadError::PackageNotExists {
package_name: name.to_string(),
}),
Err(err) => {
Err(NpmRegistryPackageInfoLoadError::LoadError(Arc::new(err)))
}
Err(err) => Err(NpmRegistryPackageInfoLoadError::LoadError(Arc::new(
AnyhowJsError(err),
))),
}
}

View file

@ -236,7 +236,7 @@ mod test {
#[test]
pub fn test_verify_tarball() {
let package = PackageNv {
name: "package".to_string(),
name: "package".into(),
version: Version::parse_from_npm("1.0.0").unwrap(),
};
let actual_checksum =

View file

@ -422,6 +422,20 @@ fn get_suggestions_for_terminal_errors(e: &JsError) -> Vec<FixSuggestion> {
"Run again with `--unstable-webgpu` flag to enable this API.",
),
];
} else if msg.contains("listenQuic is not a function") {
return vec![
FixSuggestion::info("listenQuic is an unstable API."),
FixSuggestion::hint(
"Run again with `--unstable-net` flag to enable this API.",
),
];
} else if msg.contains("connectQuic is not a function") {
return vec![
FixSuggestion::info("connectQuic is an unstable API."),
FixSuggestion::hint(
"Run again with `--unstable-net` flag to enable this API.",
),
];
// Try to capture errors like:
// ```
// Uncaught Error: Cannot find module '../build/Release/canvas.node'

View file

@ -13,6 +13,7 @@ import * as console from "ext:deno_console/01_console.js";
import * as ffi from "ext:deno_ffi/00_ffi.js";
import * as net from "ext:deno_net/01_net.js";
import * as tls from "ext:deno_net/02_tls.js";
import * as quic from "ext:deno_net/03_quic.js";
import * as serve from "ext:deno_http/00_serve.ts";
import * as http from "ext:deno_http/01_http.js";
import * as websocket from "ext:deno_http/02_websocket.ts";
@ -174,6 +175,15 @@ denoNsUnstableById[unstableIds.net] = {
op_net_listen_udp,
op_net_listen_unixpacket,
),
connectQuic: quic.connectQuic,
listenQuic: quic.listenQuic,
QuicBidirectionalStream: quic.QuicBidirectionalStream,
QuicConn: quic.QuicConn,
QuicListener: quic.QuicListener,
QuicReceiveStream: quic.QuicReceiveStream,
QuicSendStream: quic.QuicSendStream,
QuicIncoming: quic.QuicIncoming,
};
// denoNsUnstableById[unstableIds.unsafeProto] = { __proto__: null }

View file

@ -173,12 +173,14 @@ function postMessage(message, transferOrOptions = { __proto__: null }) {
let isClosing = false;
let globalDispatchEvent;
let closeOnIdle;
function hasMessageEventListener() {
// the function name is kind of a misnomer, but we want to behave
// as if we have message event listeners if a node message port is explicitly
// refed (and the inverse as well)
return event.listenerCount(globalThis, "message") > 0 ||
return (event.listenerCount(globalThis, "message") > 0 &&
!globalThis[messagePort.unrefParentPort]) ||
messagePort.refedMessagePortsCount > 0;
}
@ -191,7 +193,10 @@ async function pollForMessages() {
}
while (!isClosing) {
const recvMessage = op_worker_recv_message();
if (globalThis[messagePort.unrefPollForMessages] === true) {
// In a Node.js worker, unref() the op promise to prevent it from
// keeping the event loop alive. This avoids the need to explicitly
// call self.close() or worker.terminate().
if (closeOnIdle) {
core.unrefOpPromise(recvMessage);
}
const data = await recvMessage;
@ -524,10 +529,11 @@ const NOT_IMPORTED_OPS = [
// Used in jupyter API
"op_base64_encode",
// Used in lint API
// Used in the lint API
"op_lint_get_rule",
"op_lint_report",
"op_lint_get_source",
"op_lint_create_serialized_ast",
// Related to `Deno.test()` API
"op_test_event_step_result_failed",
@ -923,6 +929,7 @@ function bootstrapWorkerRuntime(
6: argv0,
7: nodeDebug,
13: otelConfig,
14: closeOnIdle_,
} = runtimeOptions;
performance.setTimeOrigin();
@ -975,6 +982,7 @@ function bootstrapWorkerRuntime(
globalThis.pollForMessages = pollForMessages;
globalThis.hasMessageEventListener = hasMessageEventListener;
closeOnIdle = closeOnIdle_;
for (let i = 0; i <= unstableFeatures.length; i++) {
const id = unstableFeatures[i];

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