mirror of
https://github.com/denoland/deno.git
synced 2025-02-01 12:16:11 -05:00
Merge
This commit is contained in:
commit
68507171b2
5948 changed files with 127128 additions and 61035 deletions
|
@ -1,9 +1,8 @@
|
|||
FROM mcr.microsoft.com/vscode/devcontainers/rust:1-bullseye
|
||||
|
||||
# Install cmake and protobuf-compiler
|
||||
# Install cmake
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y cmake \
|
||||
&& apt-get install -y protobuf-compiler \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Deno
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
"include": [
|
||||
"ban-untagged-todo",
|
||||
"camelcase",
|
||||
"no-console",
|
||||
"guard-for-in"
|
||||
],
|
||||
"exclude": [
|
||||
|
|
23
.dprint.json
23
.dprint.json
|
@ -31,6 +31,8 @@
|
|||
"cli/tsc/dts/lib.scripthost.d.ts",
|
||||
"cli/tsc/dts/lib.webworker*.d.ts",
|
||||
"cli/tsc/dts/typescript.d.ts",
|
||||
"cli/tools/doc/prism.css",
|
||||
"cli/tools/doc/prism.js",
|
||||
"ext/websocket/autobahn/reports",
|
||||
"gh-pages",
|
||||
"target",
|
||||
|
@ -39,10 +41,14 @@
|
|||
"tests/node_compat/runner/TODO.md",
|
||||
"tests/node_compat/test",
|
||||
"tests/registry/",
|
||||
"tests/specs/bench/default_ts",
|
||||
"tests/specs/fmt",
|
||||
"tests/specs/lint/bom",
|
||||
"tests/specs/lint/default_ts",
|
||||
"tests/specs/lint/syntax_error_reporting",
|
||||
"tests/specs/publish/no_check_surfaces_syntax_error",
|
||||
"tests/specs/run/default_ts",
|
||||
"tests/specs/test/default_ts",
|
||||
"tests/testdata/byte_order_mark.ts",
|
||||
"tests/testdata/encoding",
|
||||
"tests/testdata/file_extensions/ts_with_js_extension.js",
|
||||
|
@ -56,20 +62,23 @@
|
|||
"tests/testdata/run/byte_order_mark.ts",
|
||||
"tests/testdata/run/error_syntax_empty_trailing_line.mjs",
|
||||
"tests/testdata/run/inline_js_source_map*",
|
||||
"tests/testdata/test/glob/",
|
||||
"tests/testdata/test/markdown_windows.md",
|
||||
"tests/util/std",
|
||||
"tests/wpt/runner/expectation.json",
|
||||
"tests/wpt/runner/manifest.json",
|
||||
"tests/wpt/suite",
|
||||
"third_party"
|
||||
"third_party",
|
||||
"tests/specs/run/shebang_with_json_imports_tsc",
|
||||
"tests/specs/run/shebang_with_json_imports_swc",
|
||||
"tests/specs/run/ext_flag_takes_precedence_over_extension",
|
||||
"tests/specs/run/error_syntax_empty_trailing_line/error_syntax_empty_trailing_line.mjs"
|
||||
],
|
||||
"plugins": [
|
||||
"https://plugins.dprint.dev/typescript-0.91.4.wasm",
|
||||
"https://plugins.dprint.dev/json-0.19.3.wasm",
|
||||
"https://plugins.dprint.dev/markdown-0.17.1.wasm",
|
||||
"https://plugins.dprint.dev/toml-0.6.2.wasm",
|
||||
"https://plugins.dprint.dev/typescript-0.93.2.wasm",
|
||||
"https://plugins.dprint.dev/json-0.19.4.wasm",
|
||||
"https://plugins.dprint.dev/markdown-0.17.8.wasm",
|
||||
"https://plugins.dprint.dev/toml-0.6.3.wasm",
|
||||
"https://plugins.dprint.dev/exec-0.5.0.json@8d9972eee71fa1590e04873540421f3eda7674d0f1aae3d7c788615e7b7413d0",
|
||||
"https://plugins.dprint.dev/g-plane/pretty_yaml-v0.3.0.wasm"
|
||||
"https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.0.wasm"
|
||||
]
|
||||
}
|
||||
|
|
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
1
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -4,7 +4,6 @@ about: Report an issue found in the Deno CLI.
|
|||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Version: Deno x.x.x
|
||||
|
|
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -4,5 +4,4 @@ about: Suggest a feature for the Deno CLI.
|
|||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
|
15
.github/workflows/cargo_publish.yml
vendored
15
.github/workflows/cargo_publish.yml
vendored
|
@ -2,10 +2,15 @@ name: cargo_publish
|
|||
|
||||
on: workflow_dispatch
|
||||
|
||||
# Ensures only one publish is running at a time
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: cargo publish
|
||||
runs-on: ubuntu-20.04-xl
|
||||
runs-on: ubuntu-24.04-xl
|
||||
timeout-minutes: 90
|
||||
|
||||
env:
|
||||
|
@ -28,16 +33,10 @@ jobs:
|
|||
- uses: dsherret/rust-toolchain-file@v1
|
||||
|
||||
- name: Install deno
|
||||
uses: denoland/setup-deno@v1
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v1.x
|
||||
|
||||
- name: Install protoc
|
||||
uses: arduino/setup-protoc@v3
|
||||
with:
|
||||
version: '21.12'
|
||||
repo-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
|
||||
- name: Publish
|
||||
env:
|
||||
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
|
||||
|
|
69
.github/workflows/ci.generate.ts
vendored
69
.github/workflows/ci.generate.ts
vendored
|
@ -5,15 +5,16 @@ 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 = 9;
|
||||
const cacheVersion = 27;
|
||||
|
||||
const ubuntuX86Runner = "ubuntu-22.04";
|
||||
const ubuntuX86XlRunner = "ubuntu-22.04-xl";
|
||||
const ubuntuX86Runner = "ubuntu-24.04";
|
||||
const ubuntuX86XlRunner = "ubuntu-24.04-xl";
|
||||
const ubuntuARMRunner = "ubicloud-standard-16-arm";
|
||||
const windowsX86Runner = "windows-2022";
|
||||
const windowsX86XlRunner = "windows-2022-xl";
|
||||
const macosX86Runner = "macos-13";
|
||||
const macosArmRunner = "macos-14";
|
||||
const selfHostedMacosArmRunner = "self-hosted";
|
||||
|
||||
const Runners = {
|
||||
linuxX86: {
|
||||
|
@ -40,7 +41,8 @@ const Runners = {
|
|||
macosArm: {
|
||||
os: "macos",
|
||||
arch: "aarch64",
|
||||
runner: macosArmRunner,
|
||||
runner:
|
||||
`\${{ github.repository == 'denoland/deno' && startsWith(github.ref, 'refs/tags/') && '${selfHostedMacosArmRunner}' || '${macosArmRunner}' }}`,
|
||||
},
|
||||
windowsX86: {
|
||||
os: "windows",
|
||||
|
@ -59,7 +61,7 @@ const prCacheKeyPrefix =
|
|||
`${cacheVersion}-cargo-target-\${{ matrix.os }}-\${{ matrix.arch }}-\${{ matrix.profile }}-\${{ matrix.job }}-`;
|
||||
|
||||
// Note that you may need to add more version to the `apt-get remove` line below if you change this
|
||||
const llvmVersion = 18;
|
||||
const llvmVersion = 19;
|
||||
const installPkgsCommand =
|
||||
`sudo apt-get install --no-install-recommends clang-${llvmVersion} lld-${llvmVersion} clang-tools-${llvmVersion} clang-format-${llvmVersion} clang-tidy-${llvmVersion}`;
|
||||
const sysRootStep = {
|
||||
|
@ -71,7 +73,7 @@ export DEBIAN_FRONTEND=noninteractive
|
|||
sudo apt-get -qq remove --purge -y man-db > /dev/null 2> /dev/null
|
||||
# Remove older clang before we install
|
||||
sudo apt-get -qq remove \
|
||||
'clang-12*' 'clang-13*' 'clang-14*' 'clang-15*' 'clang-16*' 'llvm-12*' 'llvm-13*' 'llvm-14*' 'llvm-15*' 'llvm-16*' 'lld-12*' 'lld-13*' 'lld-14*' 'lld-15*' 'lld-16*' > /dev/null 2> /dev/null
|
||||
'clang-12*' 'clang-13*' 'clang-14*' 'clang-15*' 'clang-16*' 'clang-17*' 'clang-18*' 'llvm-12*' 'llvm-13*' 'llvm-14*' 'llvm-15*' 'llvm-16*' 'lld-12*' 'lld-13*' 'lld-14*' 'lld-15*' 'lld-16*' 'lld-17*' 'lld-18*' > /dev/null 2> /dev/null
|
||||
|
||||
# Install clang-XXX, lld-XXX, and debootstrap.
|
||||
echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-${llvmVersion} main" |
|
||||
|
@ -86,7 +88,7 @@ ${installPkgsCommand} || echo 'Failed. Trying again.' && sudo apt-get clean && s
|
|||
(yes '' | sudo update-alternatives --force --all) > /dev/null 2> /dev/null || true
|
||||
|
||||
echo "Decompressing sysroot..."
|
||||
wget -q https://github.com/denoland/deno_sysroot_build/releases/download/sysroot-20240528/sysroot-\`uname -m\`.tar.xz -O /tmp/sysroot.tar.xz
|
||||
wget -q https://github.com/denoland/deno_sysroot_build/releases/download/sysroot-20241030/sysroot-\`uname -m\`.tar.xz -O /tmp/sysroot.tar.xz
|
||||
cd /
|
||||
xzcat /tmp/sysroot.tar.xz | sudo tar -x
|
||||
sudo mount --rbind /dev /sysroot/dev
|
||||
|
@ -191,14 +193,9 @@ const installNodeStep = {
|
|||
uses: "actions/setup-node@v4",
|
||||
with: { "node-version": 18 },
|
||||
};
|
||||
const installProtocStep = {
|
||||
name: "Install protoc",
|
||||
uses: "arduino/setup-protoc@v3",
|
||||
with: { "version": "21.12", "repo-token": "${{ secrets.GITHUB_TOKEN }}" },
|
||||
};
|
||||
const installDenoStep = {
|
||||
name: "Install Deno",
|
||||
uses: "denoland/setup-deno@v1",
|
||||
uses: "denoland/setup-deno@v2",
|
||||
with: { "deno-version": "v1.x" },
|
||||
};
|
||||
|
||||
|
@ -354,7 +351,7 @@ const ci = {
|
|||
needs: ["pre_build"],
|
||||
if: "${{ needs.pre_build.outputs.skip_build != 'true' }}",
|
||||
"runs-on": "${{ matrix.runner }}",
|
||||
"timeout-minutes": 150,
|
||||
"timeout-minutes": 180,
|
||||
defaults: {
|
||||
run: {
|
||||
// GH actions does not fail fast by default on
|
||||
|
@ -494,7 +491,6 @@ const ci = {
|
|||
if: "matrix.job == 'bench' || matrix.job == 'test'",
|
||||
...installNodeStep,
|
||||
},
|
||||
installProtocStep,
|
||||
{
|
||||
if: [
|
||||
"matrix.profile == 'release' &&",
|
||||
|
@ -649,7 +645,7 @@ const ci = {
|
|||
name: "test_format.js",
|
||||
if: "matrix.job == 'lint' && matrix.os == 'linux'",
|
||||
run:
|
||||
"deno run --unstable --allow-write --allow-read --allow-run --allow-net ./tools/format.js --check",
|
||||
"deno run --allow-write --allow-read --allow-run --allow-net ./tools/format.js --check",
|
||||
},
|
||||
{
|
||||
name: "Lint PR title",
|
||||
|
@ -664,7 +660,7 @@ const ci = {
|
|||
name: "lint.js",
|
||||
if: "matrix.job == 'lint'",
|
||||
run:
|
||||
"deno run --unstable --allow-write --allow-read --allow-run --allow-net ./tools/lint.js",
|
||||
"deno run --allow-write --allow-read --allow-run --allow-net ./tools/lint.js",
|
||||
},
|
||||
{
|
||||
name: "jsdoc_checker.js",
|
||||
|
@ -768,8 +764,10 @@ const ci = {
|
|||
run: [
|
||||
"cd target/release",
|
||||
"zip -r deno-${{ matrix.arch }}-unknown-linux-gnu.zip deno",
|
||||
"shasum -a 256 deno-${{ matrix.arch }}-unknown-linux-gnu.zip > deno-${{ matrix.arch }}-unknown-linux-gnu.zip.sha256sum",
|
||||
"strip denort",
|
||||
"zip -r denort-${{ matrix.arch }}-unknown-linux-gnu.zip denort",
|
||||
"shasum -a 256 denort-${{ matrix.arch }}-unknown-linux-gnu.zip > denort-${{ matrix.arch }}-unknown-linux-gnu.zip.sha256sum",
|
||||
"./deno types > lib.deno.d.ts",
|
||||
].join("\n"),
|
||||
},
|
||||
|
@ -794,8 +792,10 @@ const ci = {
|
|||
"--entitlements-xml-file=cli/entitlements.plist",
|
||||
"cd target/release",
|
||||
"zip -r deno-${{ matrix.arch }}-apple-darwin.zip deno",
|
||||
"shasum -a 256 deno-${{ matrix.arch }}-apple-darwin.zip > deno-${{ matrix.arch }}-apple-darwin.zip.sha256sum",
|
||||
"strip denort",
|
||||
"zip -r denort-${{ matrix.arch }}-apple-darwin.zip denort",
|
||||
"shasum -a 256 denort-${{ matrix.arch }}-apple-darwin.zip > denort-${{ matrix.arch }}-apple-darwin.zip.sha256sum",
|
||||
]
|
||||
.join("\n"),
|
||||
},
|
||||
|
@ -810,7 +810,9 @@ const ci = {
|
|||
shell: "pwsh",
|
||||
run: [
|
||||
"Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip",
|
||||
"Get-FileHash target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip -Algorithm SHA256 | Format-List > target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip.sha256sum",
|
||||
"Compress-Archive -CompressionLevel Optimal -Force -Path target/release/denort.exe -DestinationPath target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip",
|
||||
"Get-FileHash target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip -Algorithm SHA256 | Format-List > target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip.sha256sum",
|
||||
].join("\n"),
|
||||
},
|
||||
{
|
||||
|
@ -823,6 +825,7 @@ const ci = {
|
|||
].join("\n"),
|
||||
run: [
|
||||
'gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.zip gs://dl.deno.land/canary/$(git rev-parse HEAD)/',
|
||||
'gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.sha256sum gs://dl.deno.land/canary/$(git rev-parse HEAD)/',
|
||||
"echo ${{ github.sha }} > canary-latest.txt",
|
||||
'gsutil -h "Cache-Control: no-cache" cp canary-latest.txt gs://dl.deno.land/canary-$(rustc -vV | sed -n "s|host: ||p")-latest.txt',
|
||||
].join("\n"),
|
||||
|
@ -836,7 +839,7 @@ const ci = {
|
|||
"!startsWith(github.ref, 'refs/tags/')",
|
||||
].join("\n"),
|
||||
run:
|
||||
"target/release/deno run -A --unstable --config tests/config/deno.json ext/websocket/autobahn/fuzzingclient.js",
|
||||
"target/release/deno run -A --config tests/config/deno.json ext/websocket/autobahn/fuzzingclient.js",
|
||||
},
|
||||
{
|
||||
name: "Test (full, debug)",
|
||||
|
@ -889,9 +892,9 @@ const ci = {
|
|||
DENO_BIN: "./target/debug/deno",
|
||||
},
|
||||
run: [
|
||||
"deno run -A --unstable --lock=tools/deno.lock.json --config tests/config/deno.json\\",
|
||||
"deno run -A --lock=tools/deno.lock.json --config tests/config/deno.json\\",
|
||||
" ./tests/wpt/wpt.ts setup",
|
||||
"deno run -A --unstable --lock=tools/deno.lock.json --config tests/config/deno.json\\",
|
||||
"deno run -A --lock=tools/deno.lock.json --config tests/config/deno.json\\",
|
||||
' ./tests/wpt/wpt.ts run --quiet --binary="$DENO_BIN"',
|
||||
].join("\n"),
|
||||
},
|
||||
|
@ -902,9 +905,9 @@ const ci = {
|
|||
DENO_BIN: "./target/release/deno",
|
||||
},
|
||||
run: [
|
||||
"deno run -A --unstable --lock=tools/deno.lock.json --config tests/config/deno.json\\",
|
||||
"deno run -A --lock=tools/deno.lock.json --config tests/config/deno.json\\",
|
||||
" ./tests/wpt/wpt.ts setup",
|
||||
"deno run -A --unstable --lock=tools/deno.lock.json --config tests/config/deno.json\\",
|
||||
"deno run -A --lock=tools/deno.lock.json --config tests/config/deno.json\\",
|
||||
" ./tests/wpt/wpt.ts run --quiet --release \\",
|
||||
' --binary="$DENO_BIN" \\',
|
||||
" --json=wpt.json \\",
|
||||
|
@ -968,8 +971,7 @@ const ci = {
|
|||
"git clone --depth 1 --branch gh-pages \\",
|
||||
" https://${DENOBOT_PAT}@github.com/denoland/benchmark_data.git \\",
|
||||
" gh-pages",
|
||||
"./target/release/deno run --allow-all --unstable \\",
|
||||
" ./tools/build_benchmark_jsons.js --release",
|
||||
"./target/release/deno run --allow-all ./tools/build_benchmark_jsons.js --release",
|
||||
"cd gh-pages",
|
||||
'git config user.email "propelml@gmail.com"',
|
||||
'git config user.name "denobot"',
|
||||
|
@ -1005,8 +1007,10 @@ const ci = {
|
|||
"github.repository == 'denoland/deno' &&",
|
||||
"startsWith(github.ref, 'refs/tags/')",
|
||||
].join("\n"),
|
||||
run:
|
||||
run: [
|
||||
'gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.zip gs://dl.deno.land/release/${GITHUB_REF#refs/*/}/',
|
||||
'gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.sha256sum gs://dl.deno.land/release/${GITHUB_REF#refs/*/}/',
|
||||
].join("\n"),
|
||||
},
|
||||
{
|
||||
name: "Upload release to dl.deno.land (windows)",
|
||||
|
@ -1020,8 +1024,10 @@ const ci = {
|
|||
env: {
|
||||
CLOUDSDK_PYTHON: "${{env.pythonLocation}}\\python.exe",
|
||||
},
|
||||
run:
|
||||
run: [
|
||||
'gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.zip gs://dl.deno.land/release/${GITHUB_REF#refs/*/}/',
|
||||
'gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.sha256sum gs://dl.deno.land/release/${GITHUB_REF#refs/*/}/',
|
||||
].join("\n"),
|
||||
},
|
||||
{
|
||||
name: "Create release notes",
|
||||
|
@ -1051,15 +1057,25 @@ const ci = {
|
|||
with: {
|
||||
files: [
|
||||
"target/release/deno-x86_64-pc-windows-msvc.zip",
|
||||
"target/release/deno-x86_64-pc-windows-msvc.zip.sha256sum",
|
||||
"target/release/denort-x86_64-pc-windows-msvc.zip",
|
||||
"target/release/denort-x86_64-pc-windows-msvc.zip.sha256sum",
|
||||
"target/release/deno-x86_64-unknown-linux-gnu.zip",
|
||||
"target/release/deno-x86_64-unknown-linux-gnu.zip.sha256sum",
|
||||
"target/release/denort-x86_64-unknown-linux-gnu.zip",
|
||||
"target/release/denort-x86_64-unknown-linux-gnu.zip.sha256sum",
|
||||
"target/release/deno-x86_64-apple-darwin.zip",
|
||||
"target/release/deno-x86_64-apple-darwin.zip.sha256sum",
|
||||
"target/release/denort-x86_64-apple-darwin.zip",
|
||||
"target/release/denort-x86_64-apple-darwin.zip.sha256sum",
|
||||
"target/release/deno-aarch64-unknown-linux-gnu.zip",
|
||||
"target/release/deno-aarch64-unknown-linux-gnu.zip.sha256sum",
|
||||
"target/release/denort-aarch64-unknown-linux-gnu.zip",
|
||||
"target/release/denort-aarch64-unknown-linux-gnu.zip.sha256sum",
|
||||
"target/release/deno-aarch64-apple-darwin.zip",
|
||||
"target/release/deno-aarch64-apple-darwin.zip.sha256sum",
|
||||
"target/release/denort-aarch64-apple-darwin.zip",
|
||||
"target/release/denort-aarch64-apple-darwin.zip.sha256sum",
|
||||
"target/release/deno_src.tar.gz",
|
||||
"target/release/lib.deno.d.ts",
|
||||
].join("\n"),
|
||||
|
@ -1078,6 +1094,7 @@ const ci = {
|
|||
"./target",
|
||||
"!./target/*/gn_out",
|
||||
"!./target/*/*.zip",
|
||||
"!./target/*/*.sha256sum",
|
||||
"!./target/*/*.tar.gz",
|
||||
].join("\n"),
|
||||
key: prCacheKeyPrefix + "${{ github.sha }}",
|
||||
|
|
99
.github/workflows/ci.yml
vendored
99
.github/workflows/ci.yml
vendored
|
@ -48,7 +48,7 @@ jobs:
|
|||
- pre_build
|
||||
if: '${{ needs.pre_build.outputs.skip_build != ''true'' }}'
|
||||
runs-on: '${{ matrix.runner }}'
|
||||
timeout-minutes: 150
|
||||
timeout-minutes: 180
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
@ -62,18 +62,18 @@ jobs:
|
|||
profile: debug
|
||||
- os: macos
|
||||
arch: x86_64
|
||||
runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'')) && ''ubuntu-22.04'' || ''macos-13'' }}'
|
||||
runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'')) && ''ubuntu-24.04'' || ''macos-13'' }}'
|
||||
job: test
|
||||
profile: release
|
||||
skip: '${{ !contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'') }}'
|
||||
- os: macos
|
||||
arch: aarch64
|
||||
runner: macos-14
|
||||
runner: '${{ github.repository == ''denoland/deno'' && startsWith(github.ref, ''refs/tags/'') && ''self-hosted'' || ''macos-14'' }}'
|
||||
job: test
|
||||
profile: debug
|
||||
- os: macos
|
||||
arch: aarch64
|
||||
runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'')) && ''ubuntu-22.04'' || ''macos-14'' }}'
|
||||
runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'')) && ''ubuntu-24.04'' || github.repository == ''denoland/deno'' && startsWith(github.ref, ''refs/tags/'') && ''self-hosted'' || ''macos-14'' }}'
|
||||
job: test
|
||||
profile: release
|
||||
skip: '${{ !contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'') }}'
|
||||
|
@ -84,33 +84,33 @@ jobs:
|
|||
profile: debug
|
||||
- os: windows
|
||||
arch: x86_64
|
||||
runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'')) && ''ubuntu-22.04'' || github.repository == ''denoland/deno'' && ''windows-2022-xl'' || ''windows-2022'' }}'
|
||||
runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'')) && ''ubuntu-24.04'' || github.repository == ''denoland/deno'' && ''windows-2022-xl'' || ''windows-2022'' }}'
|
||||
job: test
|
||||
profile: release
|
||||
skip: '${{ !contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'') }}'
|
||||
- os: linux
|
||||
arch: x86_64
|
||||
runner: '${{ github.repository == ''denoland/deno'' && ''ubuntu-22.04-xl'' || ''ubuntu-22.04'' }}'
|
||||
runner: '${{ github.repository == ''denoland/deno'' && ''ubuntu-24.04-xl'' || ''ubuntu-24.04'' }}'
|
||||
job: test
|
||||
profile: release
|
||||
use_sysroot: true
|
||||
wpt: '${{ !startsWith(github.ref, ''refs/tags/'') }}'
|
||||
- os: linux
|
||||
arch: x86_64
|
||||
runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'' && !contains(github.event.pull_request.labels.*.name, ''ci-bench''))) && ''ubuntu-22.04'' || github.repository == ''denoland/deno'' && ''ubuntu-22.04-xl'' || ''ubuntu-22.04'' }}'
|
||||
runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'' && !contains(github.event.pull_request.labels.*.name, ''ci-bench''))) && ''ubuntu-24.04'' || github.repository == ''denoland/deno'' && ''ubuntu-24.04-xl'' || ''ubuntu-24.04'' }}'
|
||||
job: bench
|
||||
profile: release
|
||||
use_sysroot: true
|
||||
skip: '${{ !contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'' && !contains(github.event.pull_request.labels.*.name, ''ci-bench'')) }}'
|
||||
- os: linux
|
||||
arch: x86_64
|
||||
runner: ubuntu-22.04
|
||||
runner: ubuntu-24.04
|
||||
job: test
|
||||
profile: debug
|
||||
use_sysroot: true
|
||||
- os: linux
|
||||
arch: x86_64
|
||||
runner: ubuntu-22.04
|
||||
runner: ubuntu-24.04
|
||||
job: lint
|
||||
profile: debug
|
||||
- os: linux
|
||||
|
@ -178,7 +178,7 @@ jobs:
|
|||
if: '!(matrix.skip)'
|
||||
- if: '!(matrix.skip) && (matrix.job == ''lint'' || matrix.job == ''test'' || matrix.job == ''bench'')'
|
||||
name: Install Deno
|
||||
uses: denoland/setup-deno@v1
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v1.x
|
||||
- name: Install Python
|
||||
|
@ -199,12 +199,6 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install protoc
|
||||
uses: arduino/setup-protoc@v3
|
||||
with:
|
||||
version: '21.12'
|
||||
repo-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
if: '!(matrix.skip)'
|
||||
- if: |-
|
||||
!(matrix.skip) && (matrix.profile == 'release' &&
|
||||
matrix.job == 'test' &&
|
||||
|
@ -258,22 +252,22 @@ jobs:
|
|||
# to complete.
|
||||
sudo apt-get -qq remove --purge -y man-db > /dev/null 2> /dev/null
|
||||
# Remove older clang before we install
|
||||
sudo apt-get -qq remove 'clang-12*' 'clang-13*' 'clang-14*' 'clang-15*' 'clang-16*' 'llvm-12*' 'llvm-13*' 'llvm-14*' 'llvm-15*' 'llvm-16*' 'lld-12*' 'lld-13*' 'lld-14*' 'lld-15*' 'lld-16*' > /dev/null 2> /dev/null
|
||||
sudo apt-get -qq remove 'clang-12*' 'clang-13*' 'clang-14*' 'clang-15*' 'clang-16*' 'clang-17*' 'clang-18*' 'llvm-12*' 'llvm-13*' 'llvm-14*' 'llvm-15*' 'llvm-16*' 'lld-12*' 'lld-13*' 'lld-14*' 'lld-15*' 'lld-16*' 'lld-17*' 'lld-18*' > /dev/null 2> /dev/null
|
||||
|
||||
# Install clang-XXX, lld-XXX, and debootstrap.
|
||||
echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main" |
|
||||
sudo dd of=/etc/apt/sources.list.d/llvm-toolchain-jammy-18.list
|
||||
echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main" |
|
||||
sudo dd of=/etc/apt/sources.list.d/llvm-toolchain-jammy-19.list
|
||||
curl https://apt.llvm.org/llvm-snapshot.gpg.key |
|
||||
gpg --dearmor |
|
||||
sudo dd of=/etc/apt/trusted.gpg.d/llvm-snapshot.gpg
|
||||
sudo apt-get update
|
||||
# this was unreliable sometimes, so try again if it fails
|
||||
sudo apt-get install --no-install-recommends clang-18 lld-18 clang-tools-18 clang-format-18 clang-tidy-18 || echo 'Failed. Trying again.' && sudo apt-get clean && sudo apt-get update && sudo apt-get install --no-install-recommends clang-18 lld-18 clang-tools-18 clang-format-18 clang-tidy-18
|
||||
sudo apt-get install --no-install-recommends clang-19 lld-19 clang-tools-19 clang-format-19 clang-tidy-19 || echo 'Failed. Trying again.' && sudo apt-get clean && sudo apt-get update && sudo apt-get install --no-install-recommends clang-19 lld-19 clang-tools-19 clang-format-19 clang-tidy-19
|
||||
# Fix alternatives
|
||||
(yes '' | sudo update-alternatives --force --all) > /dev/null 2> /dev/null || true
|
||||
|
||||
echo "Decompressing sysroot..."
|
||||
wget -q https://github.com/denoland/deno_sysroot_build/releases/download/sysroot-20240528/sysroot-`uname -m`.tar.xz -O /tmp/sysroot.tar.xz
|
||||
wget -q https://github.com/denoland/deno_sysroot_build/releases/download/sysroot-20241030/sysroot-`uname -m`.tar.xz -O /tmp/sysroot.tar.xz
|
||||
cd /
|
||||
xzcat /tmp/sysroot.tar.xz | sudo tar -x
|
||||
sudo mount --rbind /dev /sysroot/dev
|
||||
|
@ -305,8 +299,8 @@ jobs:
|
|||
CARGO_PROFILE_RELEASE_LTO=false
|
||||
RUSTFLAGS<<__1
|
||||
-C linker-plugin-lto=true
|
||||
-C linker=clang-18
|
||||
-C link-arg=-fuse-ld=lld-18
|
||||
-C linker=clang-19
|
||||
-C link-arg=-fuse-ld=lld-19
|
||||
-C link-arg=-ldl
|
||||
-C link-arg=-Wl,--allow-shlib-undefined
|
||||
-C link-arg=-Wl,--thinlto-cache-dir=$(pwd)/target/release/lto-cache
|
||||
|
@ -316,8 +310,8 @@ jobs:
|
|||
__1
|
||||
RUSTDOCFLAGS<<__1
|
||||
-C linker-plugin-lto=true
|
||||
-C linker=clang-18
|
||||
-C link-arg=-fuse-ld=lld-18
|
||||
-C linker=clang-19
|
||||
-C link-arg=-fuse-ld=lld-19
|
||||
-C link-arg=-ldl
|
||||
-C link-arg=-Wl,--allow-shlib-undefined
|
||||
-C link-arg=-Wl,--thinlto-cache-dir=$(pwd)/target/release/lto-cache
|
||||
|
@ -325,7 +319,7 @@ jobs:
|
|||
--cfg tokio_unstable
|
||||
$RUSTFLAGS
|
||||
__1
|
||||
CC=/usr/bin/clang-18
|
||||
CC=/usr/bin/clang-19
|
||||
CFLAGS=-flto=thin $CFLAGS
|
||||
" > $GITHUB_ENV
|
||||
- name: Remove macOS cURL --ipv4 flag
|
||||
|
@ -367,8 +361,8 @@ jobs:
|
|||
path: |-
|
||||
~/.cargo/registry/index
|
||||
~/.cargo/registry/cache
|
||||
key: '9-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
|
||||
restore-keys: '9-cargo-home-${{ matrix.os }}-${{ matrix.arch }}'
|
||||
key: '27-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
|
||||
restore-keys: '27-cargo-home-${{ matrix.os }}-${{ matrix.arch }}'
|
||||
if: '!(matrix.skip)'
|
||||
- name: Restore cache build output (PR)
|
||||
uses: actions/cache/restore@v4
|
||||
|
@ -381,7 +375,7 @@ jobs:
|
|||
!./target/*/*.zip
|
||||
!./target/*/*.tar.gz
|
||||
key: never_saved
|
||||
restore-keys: '9-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-'
|
||||
restore-keys: '27-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
|
||||
|
@ -389,7 +383,7 @@ jobs:
|
|||
cache-path: ./target
|
||||
- name: test_format.js
|
||||
if: '!(matrix.skip) && (matrix.job == ''lint'' && matrix.os == ''linux'')'
|
||||
run: deno run --unstable --allow-write --allow-read --allow-run --allow-net ./tools/format.js --check
|
||||
run: deno run --allow-write --allow-read --allow-run --allow-net ./tools/format.js --check
|
||||
- name: Lint PR title
|
||||
if: '!(matrix.skip) && (matrix.job == ''lint'' && github.event_name == ''pull_request'' && matrix.os == ''linux'')'
|
||||
env:
|
||||
|
@ -397,7 +391,7 @@ jobs:
|
|||
run: deno run ./tools/verify_pr_title.js "$PR_TITLE"
|
||||
- name: lint.js
|
||||
if: '!(matrix.skip) && (matrix.job == ''lint'')'
|
||||
run: deno run --unstable --allow-write --allow-read --allow-run --allow-net ./tools/lint.js
|
||||
run: deno run --allow-write --allow-read --allow-run --allow-net ./tools/lint.js
|
||||
- name: jsdoc_checker.js
|
||||
if: '!(matrix.skip) && (matrix.job == ''lint'')'
|
||||
run: deno run --allow-read --allow-env --allow-sys ./tools/jsdoc_checker.js
|
||||
|
@ -449,8 +443,10 @@ jobs:
|
|||
run: |-
|
||||
cd target/release
|
||||
zip -r deno-${{ matrix.arch }}-unknown-linux-gnu.zip deno
|
||||
shasum -a 256 deno-${{ matrix.arch }}-unknown-linux-gnu.zip > deno-${{ matrix.arch }}-unknown-linux-gnu.zip.sha256sum
|
||||
strip denort
|
||||
zip -r denort-${{ matrix.arch }}-unknown-linux-gnu.zip denort
|
||||
shasum -a 256 denort-${{ matrix.arch }}-unknown-linux-gnu.zip > denort-${{ matrix.arch }}-unknown-linux-gnu.zip.sha256sum
|
||||
./deno types > lib.deno.d.ts
|
||||
- name: Pre-release (mac)
|
||||
if: |-
|
||||
|
@ -466,8 +462,10 @@ jobs:
|
|||
rcodesign sign target/release/deno --code-signature-flags=runtime --p12-password="$APPLE_CODESIGN_PASSWORD" --p12-file=<(echo $APPLE_CODESIGN_KEY | base64 -d) --entitlements-xml-file=cli/entitlements.plist
|
||||
cd target/release
|
||||
zip -r deno-${{ matrix.arch }}-apple-darwin.zip deno
|
||||
shasum -a 256 deno-${{ matrix.arch }}-apple-darwin.zip > deno-${{ matrix.arch }}-apple-darwin.zip.sha256sum
|
||||
strip denort
|
||||
zip -r denort-${{ matrix.arch }}-apple-darwin.zip denort
|
||||
shasum -a 256 denort-${{ matrix.arch }}-apple-darwin.zip > denort-${{ matrix.arch }}-apple-darwin.zip.sha256sum
|
||||
- name: Pre-release (windows)
|
||||
if: |-
|
||||
!(matrix.skip) && (matrix.os == 'windows' &&
|
||||
|
@ -477,7 +475,9 @@ jobs:
|
|||
shell: pwsh
|
||||
run: |-
|
||||
Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip
|
||||
Get-FileHash target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip -Algorithm SHA256 | Format-List > target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip.sha256sum
|
||||
Compress-Archive -CompressionLevel Optimal -Force -Path target/release/denort.exe -DestinationPath target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip
|
||||
Get-FileHash target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip -Algorithm SHA256 | Format-List > target/release/denort-${{ matrix.arch }}-pc-windows-msvc.zip.sha256sum
|
||||
- name: Upload canary to dl.deno.land
|
||||
if: |-
|
||||
!(matrix.skip) && (matrix.job == 'test' &&
|
||||
|
@ -486,6 +486,7 @@ jobs:
|
|||
github.ref == 'refs/heads/main')
|
||||
run: |-
|
||||
gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.zip gs://dl.deno.land/canary/$(git rev-parse HEAD)/
|
||||
gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.sha256sum gs://dl.deno.land/canary/$(git rev-parse HEAD)/
|
||||
echo ${{ github.sha }} > canary-latest.txt
|
||||
gsutil -h "Cache-Control: no-cache" cp canary-latest.txt gs://dl.deno.land/canary-$(rustc -vV | sed -n "s|host: ||p")-latest.txt
|
||||
- name: Autobahn testsuite
|
||||
|
@ -494,7 +495,7 @@ jobs:
|
|||
matrix.job == 'test' &&
|
||||
matrix.profile == 'release' &&
|
||||
!startsWith(github.ref, 'refs/tags/'))
|
||||
run: target/release/deno run -A --unstable --config tests/config/deno.json ext/websocket/autobahn/fuzzingclient.js
|
||||
run: target/release/deno run -A --config tests/config/deno.json ext/websocket/autobahn/fuzzingclient.js
|
||||
- name: 'Test (full, debug)'
|
||||
if: |-
|
||||
!(matrix.skip) && (matrix.job == 'test' &&
|
||||
|
@ -531,18 +532,18 @@ jobs:
|
|||
env:
|
||||
DENO_BIN: ./target/debug/deno
|
||||
run: |-
|
||||
deno run -A --unstable --lock=tools/deno.lock.json --config tests/config/deno.json\
|
||||
deno run -A --lock=tools/deno.lock.json --config tests/config/deno.json\
|
||||
./tests/wpt/wpt.ts setup
|
||||
deno run -A --unstable --lock=tools/deno.lock.json --config tests/config/deno.json\
|
||||
deno run -A --lock=tools/deno.lock.json --config tests/config/deno.json\
|
||||
./tests/wpt/wpt.ts run --quiet --binary="$DENO_BIN"
|
||||
- name: Run web platform tests (release)
|
||||
if: '!(matrix.skip) && (matrix.wpt && matrix.profile == ''release'')'
|
||||
env:
|
||||
DENO_BIN: ./target/release/deno
|
||||
run: |-
|
||||
deno run -A --unstable --lock=tools/deno.lock.json --config tests/config/deno.json\
|
||||
deno run -A --lock=tools/deno.lock.json --config tests/config/deno.json\
|
||||
./tests/wpt/wpt.ts setup
|
||||
deno run -A --unstable --lock=tools/deno.lock.json --config tests/config/deno.json\
|
||||
deno run -A --lock=tools/deno.lock.json --config tests/config/deno.json\
|
||||
./tests/wpt/wpt.ts run --quiet --release \
|
||||
--binary="$DENO_BIN" \
|
||||
--json=wpt.json \
|
||||
|
@ -590,8 +591,7 @@ jobs:
|
|||
git clone --depth 1 --branch gh-pages \
|
||||
https://${DENOBOT_PAT}@github.com/denoland/benchmark_data.git \
|
||||
gh-pages
|
||||
./target/release/deno run --allow-all --unstable \
|
||||
./tools/build_benchmark_jsons.js --release
|
||||
./target/release/deno run --allow-all ./tools/build_benchmark_jsons.js --release
|
||||
cd gh-pages
|
||||
git config user.email "propelml@gmail.com"
|
||||
git config user.name "denobot"
|
||||
|
@ -616,7 +616,9 @@ jobs:
|
|||
matrix.profile == 'release' &&
|
||||
github.repository == 'denoland/deno' &&
|
||||
startsWith(github.ref, 'refs/tags/'))
|
||||
run: 'gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.zip gs://dl.deno.land/release/${GITHUB_REF#refs/*/}/'
|
||||
run: |-
|
||||
gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.zip gs://dl.deno.land/release/${GITHUB_REF#refs/*/}/
|
||||
gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.sha256sum gs://dl.deno.land/release/${GITHUB_REF#refs/*/}/
|
||||
- name: Upload release to dl.deno.land (windows)
|
||||
if: |-
|
||||
!(matrix.skip) && (matrix.os == 'windows' &&
|
||||
|
@ -626,7 +628,9 @@ jobs:
|
|||
startsWith(github.ref, 'refs/tags/'))
|
||||
env:
|
||||
CLOUDSDK_PYTHON: '${{env.pythonLocation}}\python.exe'
|
||||
run: 'gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.zip gs://dl.deno.land/release/${GITHUB_REF#refs/*/}/'
|
||||
run: |-
|
||||
gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.zip gs://dl.deno.land/release/${GITHUB_REF#refs/*/}/
|
||||
gsutil -h "Cache-Control: public, max-age=3600" cp ./target/release/*.sha256sum gs://dl.deno.land/release/${GITHUB_REF#refs/*/}/
|
||||
- name: Create release notes
|
||||
if: |-
|
||||
!(matrix.skip) && (matrix.job == 'test' &&
|
||||
|
@ -648,15 +652,25 @@ jobs:
|
|||
with:
|
||||
files: |-
|
||||
target/release/deno-x86_64-pc-windows-msvc.zip
|
||||
target/release/deno-x86_64-pc-windows-msvc.zip.sha256sum
|
||||
target/release/denort-x86_64-pc-windows-msvc.zip
|
||||
target/release/denort-x86_64-pc-windows-msvc.zip.sha256sum
|
||||
target/release/deno-x86_64-unknown-linux-gnu.zip
|
||||
target/release/deno-x86_64-unknown-linux-gnu.zip.sha256sum
|
||||
target/release/denort-x86_64-unknown-linux-gnu.zip
|
||||
target/release/denort-x86_64-unknown-linux-gnu.zip.sha256sum
|
||||
target/release/deno-x86_64-apple-darwin.zip
|
||||
target/release/deno-x86_64-apple-darwin.zip.sha256sum
|
||||
target/release/denort-x86_64-apple-darwin.zip
|
||||
target/release/denort-x86_64-apple-darwin.zip.sha256sum
|
||||
target/release/deno-aarch64-unknown-linux-gnu.zip
|
||||
target/release/deno-aarch64-unknown-linux-gnu.zip.sha256sum
|
||||
target/release/denort-aarch64-unknown-linux-gnu.zip
|
||||
target/release/denort-aarch64-unknown-linux-gnu.zip.sha256sum
|
||||
target/release/deno-aarch64-apple-darwin.zip
|
||||
target/release/deno-aarch64-apple-darwin.zip.sha256sum
|
||||
target/release/denort-aarch64-apple-darwin.zip
|
||||
target/release/denort-aarch64-apple-darwin.zip.sha256sum
|
||||
target/release/deno_src.tar.gz
|
||||
target/release/lib.deno.d.ts
|
||||
body_path: target/release/release-notes.md
|
||||
|
@ -669,11 +683,12 @@ jobs:
|
|||
./target
|
||||
!./target/*/gn_out
|
||||
!./target/*/*.zip
|
||||
!./target/*/*.sha256sum
|
||||
!./target/*/*.tar.gz
|
||||
key: '9-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
|
||||
key: '27-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
|
||||
publish-canary:
|
||||
name: publish canary
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- build
|
||||
if: github.repository == 'denoland/deno' && github.ref == 'refs/heads/main'
|
||||
|
|
2
.github/workflows/post_publish.yml
vendored
2
.github/workflows/post_publish.yml
vendored
|
@ -7,7 +7,7 @@ on:
|
|||
jobs:
|
||||
update-dl-version:
|
||||
name: update dl.deno.land version
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.repository == 'denoland/deno'
|
||||
steps:
|
||||
- name: Authenticate with Google Cloud
|
||||
|
|
62
.github/workflows/promote_to_release.yml
vendored
Normal file
62
.github/workflows/promote_to_release.yml
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
name: promote_to_release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
releaseKind:
|
||||
description: 'Kind of release'
|
||||
type: choice
|
||||
options:
|
||||
- rc
|
||||
- lts
|
||||
required: true
|
||||
commitHash:
|
||||
description: Commit to promote to release
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
promote-to-release:
|
||||
name: Promote to Release
|
||||
runs-on: macOS-latest
|
||||
if: github.repository == 'denoland/deno'
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.DENOBOT_PAT }}
|
||||
submodules: recursive
|
||||
|
||||
- name: Authenticate with Google Cloud
|
||||
uses: google-github-actions/auth@v1
|
||||
with:
|
||||
project_id: denoland
|
||||
credentials_json: ${{ secrets.GCP_SA_KEY }}
|
||||
export_environment_variables: true
|
||||
create_credentials_file: true
|
||||
|
||||
- name: Setup gcloud
|
||||
uses: google-github-actions/setup-gcloud@v1
|
||||
with:
|
||||
project_id: denoland
|
||||
|
||||
- name: Install deno
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v1.x
|
||||
|
||||
- name: Install rust-codesign
|
||||
run: |-
|
||||
./tools/install_prebuilt.js rcodesign
|
||||
echo $GITHUB_WORKSPACE/third_party/prebuilt/mac >> $GITHUB_PATH
|
||||
|
||||
- name: Promote to Release
|
||||
env:
|
||||
APPLE_CODESIGN_KEY: '${{ secrets.APPLE_CODESIGN_KEY }}'
|
||||
APPLE_CODESIGN_PASSWORD: '${{ secrets.APPLE_CODESIGN_PASSWORD }}'
|
||||
run: |
|
||||
deno run -A ./tools/release/promote_to_release.ts ${{github.event.inputs.releaseKind}} ${{github.event.inputs.commitHash}}
|
||||
|
||||
- name: Upload archives to dl.deno.land
|
||||
run: |
|
||||
gsutil -h "Cache-Control: public, max-age=3600" cp ./*.zip gs://dl.deno.land/release/$(cat release-${{github.event.inputs.releaseKind}}-latest.txt)/
|
||||
gsutil -h "Cache-Control: no-cache" cp release-${{github.event.inputs.releaseKind}}-latest.txt gs://dl.deno.land/release-${{github.event.inputs.releaseKind}}-latest.txt
|
4
.github/workflows/start_release.yml
vendored
4
.github/workflows/start_release.yml
vendored
|
@ -16,7 +16,7 @@ on:
|
|||
jobs:
|
||||
build:
|
||||
name: start release
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 30
|
||||
|
||||
env:
|
||||
|
@ -34,7 +34,7 @@ jobs:
|
|||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install deno
|
||||
uses: denoland/setup-deno@v1
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v1.x
|
||||
|
||||
|
|
4
.github/workflows/version_bump.yml
vendored
4
.github/workflows/version_bump.yml
vendored
|
@ -16,7 +16,7 @@ on:
|
|||
jobs:
|
||||
build:
|
||||
name: version bump
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 90
|
||||
|
||||
env:
|
||||
|
@ -39,7 +39,7 @@ jobs:
|
|||
- uses: dsherret/rust-toolchain-file@v1
|
||||
|
||||
- name: Install deno
|
||||
uses: denoland/setup-deno@v1
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v1.x
|
||||
|
||||
|
|
8
.github/workflows/wpt_epoch.yml
vendored
8
.github/workflows/wpt_epoch.yml
vendored
|
@ -20,7 +20,7 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
deno-version: [v1.x, canary]
|
||||
os: [ubuntu-22.04-xl]
|
||||
os: [ubuntu-24.04-xl]
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
|
@ -30,7 +30,7 @@ jobs:
|
|||
persist-credentials: false
|
||||
|
||||
- name: Setup Deno
|
||||
uses: denoland/setup-deno@v1
|
||||
uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: ${{ matrix.deno-version }}
|
||||
|
||||
|
@ -66,9 +66,9 @@ jobs:
|
|||
- name: Run web platform tests
|
||||
shell: bash
|
||||
run: |
|
||||
deno run --unstable -A --lock=tools/deno.lock.json --config=tests/config/deno.json \
|
||||
deno run -A --lock=tools/deno.lock.json --config=tests/config/deno.json \
|
||||
./tests/wpt/wpt.ts setup
|
||||
deno run --unstable -A --lock=tools/deno.lock.json --config=tests/config/deno.json \
|
||||
deno run -A --lock=tools/deno.lock.json --config=tests/config/deno.json \
|
||||
./tests/wpt/wpt.ts run \ \
|
||||
--binary=$(which deno) --quiet --release --no-ignore --json=wpt.json --wptreport=wptreport.json --exit-zero
|
||||
|
||||
|
|
2640
Cargo.lock
generated
2640
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
231
Cargo.toml
231
Cargo.toml
|
@ -5,7 +5,6 @@ resolver = "2"
|
|||
members = [
|
||||
"bench_util",
|
||||
"cli",
|
||||
"cli/napi/sym",
|
||||
"ext/broadcast_channel",
|
||||
"ext/cache",
|
||||
"ext/canvas",
|
||||
|
@ -19,15 +18,17 @@ members = [
|
|||
"ext/io",
|
||||
"ext/kv",
|
||||
"ext/napi",
|
||||
"ext/napi/sym",
|
||||
"ext/net",
|
||||
"ext/node",
|
||||
"ext/node_resolver",
|
||||
"ext/url",
|
||||
"ext/web",
|
||||
"ext/webgpu",
|
||||
"ext/webidl",
|
||||
"ext/websocket",
|
||||
"ext/webstorage",
|
||||
"resolvers/deno",
|
||||
"resolvers/node",
|
||||
"runtime",
|
||||
"runtime/permissions",
|
||||
"tests",
|
||||
|
@ -44,54 +45,62 @@ license = "MIT"
|
|||
repository = "https://github.com/denoland/deno"
|
||||
|
||||
[workspace.dependencies]
|
||||
deno_ast = { version = "=0.40.0", features = ["transpiling"] }
|
||||
deno_core = { version = "0.299.0" }
|
||||
deno_ast = { version = "=0.43.3", features = ["transpiling"] }
|
||||
deno_core = { version = "0.322.0" }
|
||||
|
||||
deno_bench_util = { version = "0.157.0", path = "./bench_util" }
|
||||
deno_lockfile = "0.20.0"
|
||||
deno_media_type = { version = "0.1.4", features = ["module_specifier"] }
|
||||
deno_permissions = { version = "0.23.0", path = "./runtime/permissions" }
|
||||
deno_runtime = { version = "0.172.0", path = "./runtime" }
|
||||
deno_bench_util = { version = "0.173.0", path = "./bench_util" }
|
||||
deno_config = { version = "=0.39.2", features = ["workspace", "sync"] }
|
||||
deno_lockfile = "=0.23.1"
|
||||
deno_media_type = { version = "0.2.0", features = ["module_specifier"] }
|
||||
deno_npm = "=0.25.4"
|
||||
deno_path_util = "=0.2.1"
|
||||
deno_permissions = { version = "0.39.0", path = "./runtime/permissions" }
|
||||
deno_runtime = { version = "0.188.0", path = "./runtime" }
|
||||
deno_semver = "=0.5.16"
|
||||
deno_terminal = "0.2.0"
|
||||
napi_sym = { version = "0.93.0", path = "./cli/napi/sym" }
|
||||
napi_sym = { version = "0.109.0", path = "./ext/napi/sym" }
|
||||
test_util = { package = "test_server", path = "./tests/util/server" }
|
||||
|
||||
denokv_proto = "0.8.1"
|
||||
denokv_remote = "0.8.1"
|
||||
denokv_proto = "0.8.4"
|
||||
denokv_remote = "0.8.4"
|
||||
# denokv_sqlite brings in bundled sqlite if we don't disable the default features
|
||||
denokv_sqlite = { default-features = false, version = "0.8.1" }
|
||||
denokv_sqlite = { default-features = false, version = "0.8.4" }
|
||||
|
||||
# exts
|
||||
deno_broadcast_channel = { version = "0.157.0", path = "./ext/broadcast_channel" }
|
||||
deno_cache = { version = "0.95.0", path = "./ext/cache" }
|
||||
deno_canvas = { version = "0.32.0", path = "./ext/canvas" }
|
||||
deno_console = { version = "0.163.0", path = "./ext/console" }
|
||||
deno_cron = { version = "0.43.0", path = "./ext/cron" }
|
||||
deno_crypto = { version = "0.177.0", path = "./ext/crypto" }
|
||||
deno_fetch = { version = "0.187.0", path = "./ext/fetch" }
|
||||
deno_ffi = { version = "0.150.0", path = "./ext/ffi" }
|
||||
deno_fs = { version = "0.73.0", path = "./ext/fs" }
|
||||
deno_http = { version = "0.161.0", path = "./ext/http" }
|
||||
deno_io = { version = "0.73.0", path = "./ext/io" }
|
||||
deno_kv = { version = "0.71.0", path = "./ext/kv" }
|
||||
deno_napi = { version = "0.94.0", path = "./ext/napi" }
|
||||
deno_net = { version = "0.155.0", path = "./ext/net" }
|
||||
deno_node = { version = "0.100.0", path = "./ext/node" }
|
||||
deno_tls = { version = "0.150.0", path = "./ext/tls" }
|
||||
deno_url = { version = "0.163.0", path = "./ext/url" }
|
||||
deno_web = { version = "0.194.0", path = "./ext/web" }
|
||||
deno_webgpu = { version = "0.130.0", path = "./ext/webgpu" }
|
||||
deno_webidl = { version = "0.163.0", path = "./ext/webidl" }
|
||||
deno_websocket = { version = "0.168.0", path = "./ext/websocket" }
|
||||
deno_webstorage = { version = "0.158.0", path = "./ext/webstorage" }
|
||||
node_resolver = { version = "0.2.0", path = "./ext/node_resolver" }
|
||||
deno_broadcast_channel = { version = "0.173.0", path = "./ext/broadcast_channel" }
|
||||
deno_cache = { version = "0.111.0", path = "./ext/cache" }
|
||||
deno_canvas = { version = "0.48.0", path = "./ext/canvas" }
|
||||
deno_console = { version = "0.179.0", path = "./ext/console" }
|
||||
deno_cron = { version = "0.59.0", path = "./ext/cron" }
|
||||
deno_crypto = { version = "0.193.0", path = "./ext/crypto" }
|
||||
deno_fetch = { version = "0.203.0", path = "./ext/fetch" }
|
||||
deno_ffi = { version = "0.166.0", path = "./ext/ffi" }
|
||||
deno_fs = { version = "0.89.0", path = "./ext/fs" }
|
||||
deno_http = { version = "0.177.0", path = "./ext/http" }
|
||||
deno_io = { version = "0.89.0", path = "./ext/io" }
|
||||
deno_kv = { version = "0.87.0", path = "./ext/kv" }
|
||||
deno_napi = { version = "0.110.0", path = "./ext/napi" }
|
||||
deno_net = { version = "0.171.0", path = "./ext/net" }
|
||||
deno_node = { version = "0.116.0", path = "./ext/node" }
|
||||
deno_tls = { version = "0.166.0", path = "./ext/tls" }
|
||||
deno_url = { version = "0.179.0", path = "./ext/url" }
|
||||
deno_web = { version = "0.210.0", path = "./ext/web" }
|
||||
deno_webgpu = { version = "0.146.0", path = "./ext/webgpu" }
|
||||
deno_webidl = { version = "0.179.0", path = "./ext/webidl" }
|
||||
deno_websocket = { version = "0.184.0", path = "./ext/websocket" }
|
||||
deno_webstorage = { version = "0.174.0", path = "./ext/webstorage" }
|
||||
|
||||
# resolvers
|
||||
deno_resolver = { version = "0.11.0", path = "./resolvers/deno" }
|
||||
node_resolver = { version = "0.18.0", path = "./resolvers/node" }
|
||||
|
||||
aes = "=0.8.3"
|
||||
anyhow = "1.0.57"
|
||||
async-trait = "0.1.73"
|
||||
base32 = "=0.4.0"
|
||||
base64 = "0.21.4"
|
||||
base32 = "=0.5.1"
|
||||
base64 = "0.21.7"
|
||||
bencher = "0.1"
|
||||
boxed_error = "0.2.2"
|
||||
brotli = "6.0.0"
|
||||
bytes = "1.4.0"
|
||||
cache_control = "=0.2.0"
|
||||
|
@ -99,23 +108,27 @@ cbc = { version = "=0.1.2", features = ["alloc"] }
|
|||
# Note: Do not use the "clock" feature of chrono, as it links us to CoreFoundation on macOS.
|
||||
# Instead use util::time::utc_now()
|
||||
chrono = { version = "0.4", default-features = false, features = ["std", "serde"] }
|
||||
color-print = "0.3.5"
|
||||
console_static_text = "=0.8.1"
|
||||
dashmap = "5.5.3"
|
||||
data-encoding = "2.3.3"
|
||||
data-url = "=0.3.0"
|
||||
deno_cache_dir = "=0.10.2"
|
||||
deno_package_json = { version = "=0.1.1", default-features = false }
|
||||
deno_cache_dir = "=0.13.2"
|
||||
deno_package_json = { version = "0.1.2", default-features = false }
|
||||
dlopen2 = "0.6.1"
|
||||
ecb = "=0.1.2"
|
||||
elliptic-curve = { version = "0.13.4", features = ["alloc", "arithmetic", "ecdh", "std", "pem"] }
|
||||
elliptic-curve = { version = "0.13.4", features = ["alloc", "arithmetic", "ecdh", "std", "pem", "jwk"] }
|
||||
encoding_rs = "=0.8.33"
|
||||
fast-socks5 = "0.9.6"
|
||||
faster-hex = "0.9"
|
||||
fastwebsockets = { version = "0.6", features = ["upgrade", "unstable-split"] }
|
||||
fastwebsockets = { version = "0.8", features = ["upgrade", "unstable-split"] }
|
||||
filetime = "0.2.16"
|
||||
flate2 = { version = "1.0.26", default-features = false }
|
||||
flate2 = { version = "1.0.30", default-features = false }
|
||||
fs3 = "0.5.0"
|
||||
futures = "0.3.21"
|
||||
glob = "0.3.1"
|
||||
h2 = "0.4.4"
|
||||
hickory-resolver = { version = "0.24", features = ["tokio-runtime", "serde-config"] }
|
||||
http = "1.0"
|
||||
http-body = "1.0"
|
||||
http-body-util = "0.1.2"
|
||||
|
@ -123,37 +136,37 @@ http_v02 = { package = "http", version = "0.2.9" }
|
|||
httparse = "1.8.0"
|
||||
hyper = { version = "1.4.1", features = ["full"] }
|
||||
hyper-rustls = { version = "0.27.2", default-features = false, features = ["http1", "http2", "tls12", "ring"] }
|
||||
hyper-util = { version = "=0.1.6", features = ["tokio", "client", "client-legacy", "server", "server-auto"] }
|
||||
hyper-util = { version = "=0.1.7", features = ["tokio", "client", "client-legacy", "server", "server-auto"] }
|
||||
hyper_v014 = { package = "hyper", version = "0.14.26", features = ["runtime", "http1"] }
|
||||
indexmap = { version = "2", features = ["serde"] }
|
||||
ipnet = "2.3"
|
||||
jsonc-parser = { version = "=0.23.0", features = ["serde"] }
|
||||
jsonc-parser = { version = "=0.26.2", features = ["serde"] }
|
||||
lazy-regex = "3"
|
||||
libc = "0.2.126"
|
||||
libz-sys = { version = "1.1", default-features = false }
|
||||
log = "0.4.20"
|
||||
lsp-types = "=0.94.1" # used by tower-lsp and "proposed" feature is unstable in patch releases
|
||||
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
|
||||
memmem = "0.1.1"
|
||||
monch = "=0.5.0"
|
||||
notify = { version = "=5.0.0", default-features = false, features = ["macos_kqueue"] }
|
||||
notify = { version = "=6.1.1", default-features = false, features = ["macos_kqueue"] }
|
||||
num-bigint = { version = "0.4", features = ["rand"] }
|
||||
once_cell = "1.17.1"
|
||||
os_pipe = { version = "=1.1.5", features = ["io_safety"] }
|
||||
p224 = { version = "0.13.0", features = ["ecdh"] }
|
||||
p256 = { version = "0.13.2", features = ["ecdh"] }
|
||||
p384 = { version = "0.13.0", features = ["ecdh"] }
|
||||
p256 = { version = "0.13.2", features = ["ecdh", "jwk"] }
|
||||
p384 = { version = "0.13.0", features = ["ecdh", "jwk"] }
|
||||
parking_lot = "0.12.0"
|
||||
percent-encoding = "2.3.0"
|
||||
phf = { version = "0.11", features = ["macros"] }
|
||||
pin-project = "1.0.11" # don't pin because they yank crates from cargo
|
||||
pretty_assertions = "=1.4.0"
|
||||
prost = "0.11"
|
||||
prost-build = "0.11"
|
||||
prost = "0.13"
|
||||
prost-build = "0.13"
|
||||
rand = "=0.8.5"
|
||||
regex = "^1.7.0"
|
||||
reqwest = { version = "=0.12.5", default-features = false, features = ["rustls-tls", "stream", "gzip", "brotli", "socks", "json", "http2"] } # pinned because of https://github.com/seanmonstar/reqwest/pull/1955
|
||||
ring = "^0.17.0"
|
||||
rusqlite = { version = "=0.29.0", features = ["unlock_notify", "bundled"] }
|
||||
rusqlite = { version = "0.32.0", features = ["unlock_notify", "bundled"] }
|
||||
rustls = { version = "0.23.11", default-features = false, features = ["logging", "std", "tls12", "ring"] }
|
||||
rustls-pemfile = "2"
|
||||
rustls-tokio-stream = "=0.3.0"
|
||||
|
@ -161,6 +174,7 @@ rustls-webpki = "0.102"
|
|||
rustyline = "=13.0.0"
|
||||
saffron = "=0.1.0"
|
||||
scopeguard = "1.2.0"
|
||||
sec1 = "0.7"
|
||||
serde = { version = "1.0.149", features = ["derive"] }
|
||||
serde_bytes = "0.11"
|
||||
serde_json = "1.0.85"
|
||||
|
@ -182,18 +196,25 @@ tokio-rustls = { version = "0.26.0", default-features = false, features = ["ring
|
|||
tokio-socks = "0.5.1"
|
||||
tokio-util = "0.7.4"
|
||||
tower = { version = "0.4.13", default-features = false, features = ["util"] }
|
||||
tower-http = { version = "0.5.2", features = ["decompression-br", "decompression-gzip"] }
|
||||
tower-lsp = { version = "=0.20.0", features = ["proposed"] }
|
||||
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"
|
||||
twox-hash = "=1.6.3"
|
||||
# Upgrading past 2.4.1 may cause WPT failures
|
||||
url = { version = "< 2.5.0", features = ["serde", "expose_internals"] }
|
||||
url = { version = "2.5", features = ["serde", "expose_internals"] }
|
||||
uuid = { version = "1.3.0", features = ["v4"] }
|
||||
webpki-root-certs = "0.26.5"
|
||||
webpki-roots = "0.26"
|
||||
which = "4.2.5"
|
||||
zeromq = { version = "=0.4.0", default-features = false, features = ["tcp-transport", "tokio-runtime"] }
|
||||
yoke = { version = "0.7.4", features = ["derive"] }
|
||||
zeromq = { version = "=0.4.1", default-features = false, features = ["tcp-transport", "tokio-runtime"] }
|
||||
zstd = "=0.12.4"
|
||||
|
||||
opentelemetry = "0.27.0"
|
||||
opentelemetry-http = "0.27.0"
|
||||
opentelemetry-otlp = { version = "0.27.0", features = ["logs", "http-proto", "http-json"] }
|
||||
opentelemetry-semantic-conventions = { version = "0.27.0", features = ["semconv_experimental"] }
|
||||
opentelemetry_sdk = "0.27.0"
|
||||
|
||||
# crypto
|
||||
hkdf = "0.12.3"
|
||||
rsa = { version = "0.9.3", default-features = false, features = ["std", "pem", "hazmat"] } # hazmat needed for PrehashSigner in ext/node
|
||||
|
@ -208,15 +229,14 @@ quote = "1"
|
|||
syn = { version = "2", features = ["full", "extra-traits"] }
|
||||
|
||||
# unix
|
||||
nix = "=0.26.2"
|
||||
nix = "=0.27.1"
|
||||
|
||||
# windows deps
|
||||
junction = "=0.2.0"
|
||||
winapi = "=0.3.9"
|
||||
windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_Media", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_WindowsProgramming", "Wdk", "Wdk_System", "Wdk_System_SystemInformation", "Win32_System_Pipes", "Wdk_Storage_FileSystem", "Win32_System_Registry"] }
|
||||
windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_Media", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_WindowsProgramming", "Wdk", "Wdk_System", "Wdk_System_SystemInformation", "Win32_Security", "Win32_System_Pipes", "Wdk_Storage_FileSystem", "Win32_System_Registry", "Win32_System_Kernel"] }
|
||||
winres = "=0.1.12"
|
||||
|
||||
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
incremental = true
|
||||
|
@ -228,12 +248,11 @@ opt-level = 'z' # Optimize for size
|
|||
inherits = "release"
|
||||
debug = true
|
||||
|
||||
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
|
||||
[profile.bench]
|
||||
codegen-units = 1
|
||||
incremental = true
|
||||
lto = true
|
||||
opt-level = 'z' # Optimize for size
|
||||
# Faster to compile than `release` but with similar performance.
|
||||
[profile.release-lite]
|
||||
inherits = "release"
|
||||
codegen-units = 128
|
||||
lto = "thin"
|
||||
|
||||
# Key generation is too slow on `debug`
|
||||
[profile.dev.package.num-bigint-dig]
|
||||
|
@ -243,80 +262,6 @@ opt-level = 3
|
|||
[profile.dev.package.v8]
|
||||
opt-level = 1
|
||||
|
||||
# Optimize these packages for performance.
|
||||
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
|
||||
[profile.bench.package.async-compression]
|
||||
opt-level = 3
|
||||
[profile.bench.package.base64-simd]
|
||||
opt-level = 3
|
||||
[profile.bench.package.brotli]
|
||||
opt-level = 3
|
||||
[profile.bench.package.brotli-decompressor]
|
||||
opt-level = 3
|
||||
[profile.bench.package.bytes]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_bench_util]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_broadcast_channel]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_core]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_crypto]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_fetch]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_ffi]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_http]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_napi]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_net]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_node]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_runtime]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_tls]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_url]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_web]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_websocket]
|
||||
opt-level = 3
|
||||
[profile.bench.package.fastwebsockets]
|
||||
opt-level = 3
|
||||
[profile.bench.package.flate2]
|
||||
opt-level = 3
|
||||
[profile.bench.package.futures-util]
|
||||
opt-level = 3
|
||||
[profile.bench.package.hyper]
|
||||
opt-level = 3
|
||||
[profile.bench.package.miniz_oxide]
|
||||
opt-level = 3
|
||||
[profile.bench.package.num-bigint-dig]
|
||||
opt-level = 3
|
||||
[profile.bench.package.rand]
|
||||
opt-level = 3
|
||||
[profile.bench.package.serde]
|
||||
opt-level = 3
|
||||
[profile.bench.package.serde_v8]
|
||||
opt-level = 3
|
||||
[profile.bench.package.test_napi]
|
||||
opt-level = 3
|
||||
[profile.bench.package.tokio]
|
||||
opt-level = 3
|
||||
[profile.bench.package.url]
|
||||
opt-level = 3
|
||||
[profile.bench.package.v8]
|
||||
opt-level = 3
|
||||
[profile.bench.package.zstd]
|
||||
opt-level = 3
|
||||
[profile.bench.package.zstd-sys]
|
||||
opt-level = 3
|
||||
|
||||
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
|
||||
[profile.release.package.async-compression]
|
||||
opt-level = 3
|
||||
[profile.release.package.base64-simd]
|
||||
|
@ -375,6 +320,8 @@ opt-level = 3
|
|||
opt-level = 3
|
||||
[profile.release.package.serde_v8]
|
||||
opt-level = 3
|
||||
[profile.release.package.libsui]
|
||||
opt-level = 3
|
||||
[profile.release.package.test_napi]
|
||||
opt-level = 3
|
||||
[profile.release.package.tokio]
|
||||
|
|
|
@ -46,6 +46,12 @@ brew install deno
|
|||
choco install deno
|
||||
```
|
||||
|
||||
[WinGet](https://winstall.app/apps/DenoLand.Deno) (Windows):
|
||||
|
||||
```powershell
|
||||
winget install --id=DenoLand.Deno
|
||||
```
|
||||
|
||||
### Build and install from source
|
||||
|
||||
Complete instructions for building Deno from source can be found in the manual
|
||||
|
|
954
Releases.md
954
Releases.md
|
@ -6,6 +6,960 @@ https://github.com/denoland/deno/releases
|
|||
We also have one-line install commands at:
|
||||
https://github.com/denoland/deno_install
|
||||
|
||||
### 2.1.1 / 2024.11.21
|
||||
|
||||
- docs(add): clarification to add command (#26968)
|
||||
- docs(doc): fix typo in doc subcommand help output (#26321)
|
||||
- fix(node): regression where ts files were sometimes resolved instead of js
|
||||
(#26971)
|
||||
- fix(task): ensure root config always looks up dependencies in root (#26959)
|
||||
- fix(watch): don't panic if there's no path provided (#26972)
|
||||
- fix: Buffer global in --unstable-node-globals (#26973)
|
||||
|
||||
### 2.1.0 / 2024.11.21
|
||||
|
||||
- feat(cli): add `--unstable-node-globals` flag (#26617)
|
||||
- feat(cli): support multiple env file argument (#26527)
|
||||
- feat(compile): ability to embed directory in executable (#26939)
|
||||
- feat(compile): ability to embed local data files (#26934)
|
||||
- feat(ext/fetch): Make fetch client parameters configurable (#26909)
|
||||
- feat(ext/fetch): allow embedders to use `hickory_dns_resolver` instead of
|
||||
default `GaiResolver` (#26740)
|
||||
- feat(ext/fs): add ctime to Deno.stats and use it in node compat layer (#24801)
|
||||
- feat(ext/http): Make http server parameters configurable (#26785)
|
||||
- feat(ext/node): perf_hooks.monitorEventLoopDelay() (#26905)
|
||||
- feat(fetch): accept async iterables for body (#26882)
|
||||
- feat(fmt): support SQL (#26750)
|
||||
- feat(info): show location for Web Cache (#26205)
|
||||
- feat(init): add --npm flag to initialize npm projects (#26896)
|
||||
- feat(jupyter): Add `Deno.jupyter.image` API (#26284)
|
||||
- feat(lint): Add checked files list to the JSON output(#26936)
|
||||
- feat(lsp): auto-imports with @deno-types directives (#26821)
|
||||
- feat(node): stabilize detecting if CJS via `"type": "commonjs"` in a
|
||||
package.json (#26439)
|
||||
- feat(permission): support suffix wildcards in `--allow-env` flag (#25255)
|
||||
- feat(publish): add `--set-version <version>` flag (#26141)
|
||||
- feat(runtime): remove public OTEL trace API (#26854)
|
||||
- feat(task): add --eval flag (#26943)
|
||||
- feat(task): dependencies (#26467)
|
||||
- feat(task): support object notation, remove support for JSDocs (#26886)
|
||||
- feat(task): workspace support with --filter and --recursive (#26949)
|
||||
- feat(watch): log which file changed on HMR or watch change (#25801)
|
||||
- feat: OpenTelemetry Tracing API and Exporting (#26710)
|
||||
- feat: Wasm module support (#26668)
|
||||
- feat: fmt and lint respect .gitignore file (#26897)
|
||||
- feat: permission stack traces in ops (#26938)
|
||||
- feat: subcommand to view and update outdated dependencies (#26942)
|
||||
- feat: upgrade V8 to 13.0 (#26851)
|
||||
- fix(cli): preserve comments in doc tests (#26828)
|
||||
- fix(cli): show prefix hint when installing a package globally (#26629)
|
||||
- fix(ext/cache): gracefully error when cache creation failed (#26895)
|
||||
- fix(ext/http): prefer brotli for `accept-encoding: gzip, deflate, br, zstd`
|
||||
(#26814)
|
||||
- fix(ext/node): New async setInterval function to improve the nodejs
|
||||
compatibility (#26703)
|
||||
- fix(ext/node): add autoSelectFamily option to net.createConnection (#26661)
|
||||
- fix(ext/node): handle `--allow-sys=inspector` (#26836)
|
||||
- fix(ext/node): increase tolerance for interval test (#26899)
|
||||
- fix(ext/node): process.getBuiltinModule (#26833)
|
||||
- fix(ext/node): use ERR_NOT_IMPLEMENTED for notImplemented (#26853)
|
||||
- fix(ext/node): zlib.crc32() (#26856)
|
||||
- fix(ext/webgpu): Create GPUQuerySet converter before usage (#26883)
|
||||
- fix(ext/websocket): initialize `error` attribute of WebSocket ErrorEvent
|
||||
(#26796)
|
||||
- fix(ext/webstorage): use error class for sqlite error case (#26806)
|
||||
- fix(fmt): error instead of panic on unstable format (#26859)
|
||||
- fix(fmt): formatting of .svelte files (#26948)
|
||||
- fix(install): percent encodings in interactive progress bar (#26600)
|
||||
- fix(install): re-setup bin entries after running lifecycle scripts (#26752)
|
||||
- fix(lockfile): track dependencies specified in TypeScript compiler options
|
||||
(#26551)
|
||||
- fix(lsp): ignore editor indent settings if deno.json is present (#26912)
|
||||
- fix(lsp): skip code action edits that can't be converted (#26831)
|
||||
- fix(node): handle resolving ".//<something>" in npm packages (#26920)
|
||||
- fix(node/crypto): support promisify on generateKeyPair (#26913)
|
||||
- fix(permissions): say to use --allow-run instead of --allow-all (#26842)
|
||||
- fix(publish): improve error message when missing exports (#26945)
|
||||
- fix: otel resiliency (#26857)
|
||||
- fix: update message for unsupported schemes with npm and jsr (#26884)
|
||||
- perf(compile): code cache (#26528)
|
||||
- perf(windows): delay load webgpu and some other dlls (#26917)
|
||||
- perf: use available system memory for v8 isolate memory limit (#26868)
|
||||
|
||||
### 2.0.6 / 2024.11.10
|
||||
|
||||
- feat(ext/http): abort event when request is cancelled (#26781)
|
||||
- feat(ext/http): abort signal when request is cancelled (#26761)
|
||||
- feat(lsp): auto-import completions from byonm dependencies (#26680)
|
||||
- fix(ext/cache): don't panic when creating cache (#26780)
|
||||
- fix(ext/node): better inspector support (#26471)
|
||||
- fix(fmt): don't use self-closing tags in HTML (#26754)
|
||||
- fix(install): cache jsr deps from all workspace config files (#26779)
|
||||
- fix(node:zlib): gzip & gzipSync should accept ArrayBuffer (#26762)
|
||||
- fix: performance.timeOrigin (#26787)
|
||||
|
||||
### 2.0.5 / 2024.11.05
|
||||
|
||||
- fix(add): better error message when adding package that only has pre-release
|
||||
versions (#26724)
|
||||
- fix(add): only add npm deps to package.json if it's at least as close as
|
||||
deno.json (#26683)
|
||||
- fix(cli): set `npm_config_user_agent` when running npm packages or tasks
|
||||
(#26639)
|
||||
- fix(coverage): exclude comment lines from coverage reports (#25939)
|
||||
- fix(ext/node): add `findSourceMap` to the default export of `node:module`
|
||||
(#26720)
|
||||
- fix(ext/node): convert errors from `fs.readFile/fs.readFileSync` to node
|
||||
format (#26632)
|
||||
- fix(ext/node): resolve exports even if parent module filename isn't present
|
||||
(#26553)
|
||||
- fix(ext/node): return `this` from `http.Server.ref/unref()` (#26647)
|
||||
- fix(fmt): do not panic for jsx ignore container followed by jsx text (#26723)
|
||||
- fix(fmt): fix several HTML and components issues (#26654)
|
||||
- fix(fmt): ignore file directive for YAML files (#26717)
|
||||
- fix(install): handle invalid function error, and fallback to junctions
|
||||
regardless of the error (#26730)
|
||||
- fix(lsp): include unstable features from editor settings (#26655)
|
||||
- fix(lsp): scope attribution for lazily loaded assets (#26699)
|
||||
- fix(node): Implement `os.userInfo` properly, add missing `toPrimitive`
|
||||
(#24702)
|
||||
- fix(serve): support serve hmr (#26078)
|
||||
- fix(types): missing `import` permission on `PermissionOptionsObject` (#26627)
|
||||
- fix(workspace): support wildcard packages (#26568)
|
||||
- fix: clamp smi in fast calls by default (#26506)
|
||||
- fix: improved support for cjs and cts modules (#26558)
|
||||
- fix: op_run_microtasks crash (#26718)
|
||||
- fix: panic_hook hangs without procfs (#26732)
|
||||
- fix: remove permission check in op_require_node_module_paths (#26645)
|
||||
- fix: surface package.json location on dep parse failure (#26665)
|
||||
- perf(lsp): don't walk coverage directory (#26715)
|
||||
|
||||
### 2.0.4 / 2024.10.29
|
||||
|
||||
- Revert "fix(ext/node): fix dns.lookup result ordering (#26264)" (#26621)
|
||||
- Revert "fix(ext/node): use primordials in `ext/node/polyfills/https.ts`
|
||||
(#26323)" (#26613)
|
||||
- feat(lsp): "typescript.preferences.preferTypeOnlyAutoImports" setting (#26546)
|
||||
- fix(check): expose more globals from @types/node (#26603)
|
||||
- fix(check): ignore resolving `jsxImportSource` when jsx is not used in graph
|
||||
(#26548)
|
||||
- fix(cli): Make --watcher CLEAR_SCREEN clear scrollback buffer as well as
|
||||
visible screen (#25997)
|
||||
- fix(compile): regression handling redirects (#26586)
|
||||
- fix(ext/napi): export dynamic symbols list for {Free,Open}BSD (#26605)
|
||||
- fix(ext/node): add path to `fs.stat` and `fs.statSync` error (#26037)
|
||||
- fix(ext/node): compatibility with {Free,Open}BSD (#26604)
|
||||
- fix(ext/node): use primordials in
|
||||
ext\node\polyfills\internal\crypto\_randomInt.ts (#26534)
|
||||
- fix(install): cache json exports of JSR packages (#26552)
|
||||
- fix(install): regression - do not panic when config file contains \r\n
|
||||
newlines (#26547)
|
||||
- fix(lsp): make missing import action fix infallible (#26539)
|
||||
- fix(npm): match npm bearer token generation (#26544)
|
||||
- fix(upgrade): stop running `deno lsp` processes on windows before attempting
|
||||
to replace executable (#26542)
|
||||
- fix(watch): don't panic on invalid file specifiers (#26577)
|
||||
- fix: do not panic when failing to write to http cache (#26591)
|
||||
- fix: provide hints in terminal errors for Node.js globals (#26610)
|
||||
- fix: report exceptions from nextTick (#26579)
|
||||
- fix: support watch flag to enable watching other files than the main module on
|
||||
serve subcommand (#26622)
|
||||
- perf: pass transpiled module to deno_core as known string (#26555)
|
||||
|
||||
### 2.0.3 / 2024.10.25
|
||||
|
||||
- feat(lsp): interactive inlay hints (#26382)
|
||||
- fix: support node-api in denort (#26389)
|
||||
- fix(check): support `--frozen` on deno check (#26479)
|
||||
- fix(cli): increase size of blocking task threadpool on windows (#26465)
|
||||
- fix(config): schemas for lint rule and tag autocompletion (#26515)
|
||||
- fix(ext/console): ignore casing for named colors in css parsing (#26466)
|
||||
- fix(ext/ffi): return u64/i64 as bigints from nonblocking ffi calls (#26486)
|
||||
- fix(ext/node): cancel pending ipc writes on channel close (#26504)
|
||||
- fix(ext/node): map `ERROR_INVALID_NAME` to `ENOENT` on windows (#26475)
|
||||
- fix(ext/node): only set our end of child process pipe to nonblocking mode
|
||||
(#26495)
|
||||
- fix(ext/node): properly map reparse point error in readlink (#26375)
|
||||
- fix(ext/node): refactor http.ServerResponse into function class (#26210)
|
||||
- fix(ext/node): stub HTTPParser internal binding (#26401)
|
||||
- fix(ext/node): use primordials in `ext/node/polyfills/https.ts` (#26323)
|
||||
- fix(fmt): --ext flag requires to pass files (#26525)
|
||||
- fix(fmt): upgrade formatters (#26469)
|
||||
- fix(help): missing package specifier (#26380)
|
||||
- fix(info): resolve workspace member mappings (#26350)
|
||||
- fix(install): better json editing (#26450)
|
||||
- fix(install): cache all exports of JSR packages listed in `deno.json` (#26501)
|
||||
- fix(install): cache type only module deps in `deno install` (#26497)
|
||||
- fix(install): don't cache json exports of JSR packages (for now) (#26530)
|
||||
- fix(install): update lockfile when using package.json (#26458)
|
||||
- fix(lsp): import-map-remap quickfix for type imports (#26454)
|
||||
- fix(node/util): support array formats in `styleText` (#26507)
|
||||
- fix(node:tls): set TLSSocket.alpnProtocol for client connections (#26476)
|
||||
- fix(npm): ensure scoped package name is encoded in URLs (#26390)
|
||||
- fix(npm): support version ranges with && or comma (#26453)
|
||||
- fix: `.npmrc` settings not being passed to install/add command (#26473)
|
||||
- fix: add 'fmt-component' to unstable features in schema file (#26526)
|
||||
- fix: share inotify fd across watchers (#26200)
|
||||
- fix: unpin tokio version (#26457)
|
||||
- perf(compile): pass module source data from binary directly to v8 (#26494)
|
||||
- perf: avoid multiple calls to runMicrotask (#26378)
|
||||
|
||||
### 2.0.2 / 2024.10.17
|
||||
|
||||
- fix(cli): set napi object property properly (#26344)
|
||||
- fix(ext/node): add null check for kStreamBaseField (#26368)
|
||||
- fix(install): don't attempt to cache specifiers that point to directories
|
||||
(#26369)
|
||||
- fix(jupyter): fix panics for overslow subtraction (#26371)
|
||||
- fix(jupyter): update to the new logo (#26353)
|
||||
- fix(net): don't try to set nodelay on upgrade streams (#26342)
|
||||
- fix(node/fs): copyFile with `COPYFILE_EXCL` should not throw if the
|
||||
destination doesn't exist (#26360)
|
||||
- fix(node/http): normalize header names in `ServerResponse` (#26339)
|
||||
- fix(runtime): send ws ping frames from inspector server (#26352)
|
||||
- fix: don't warn on ignored signals on windows (#26332)
|
||||
|
||||
### 2.0.1 / 2024.10.16
|
||||
|
||||
- feat(lsp): "deno/didRefreshDenoConfigurationTree" notifications (#26215)
|
||||
- feat(unstable): `--unstable-detect-cjs` for respecting explicit
|
||||
`"type": "commonjs"` (#26149)
|
||||
- fix(add): create deno.json when running `deno add jsr:<pkg>` (#26275)
|
||||
- fix(add): exact version should not have range `^` specifier (#26302)
|
||||
- fix(child_process): map node `--no-warnings` flag to `--quiet` (#26288)
|
||||
- fix(cli): add prefix to install commands in help (#26318)
|
||||
- fix(cli): consolidate pkg parser for install & remove (#26298)
|
||||
- fix(cli): named export takes precedence over default export in doc testing
|
||||
(#26112)
|
||||
- fix(cli): improve deno info output for npm packages (#25906)
|
||||
- fix(console/ext/repl): support using parseFloat() (#25900)
|
||||
- fix(ext/console): apply coloring for console.table (#26280)
|
||||
- fix(ext/napi): pass user context to napi_threadsafe_fn finalizers (#26229)
|
||||
- fix(ext/node): allow writing to tty columns (#26201)
|
||||
- fix(ext/node): compute pem length (upper bound) for key exports (#26231)
|
||||
- fix(ext/node): fix dns.lookup result ordering (#26264)
|
||||
- fix(ext/node): handle http2 server ending stream (#26235)
|
||||
- fix(ext/node): implement TCP.setNoDelay (#26263)
|
||||
- fix(ext/node): timingSafeEqual account for AB byteOffset (#26292)
|
||||
- fix(ext/node): use primordials in `ext/node/polyfills/internal/buffer.mjs`
|
||||
(#24993)
|
||||
- fix(ext/webgpu): allow GL backend on Windows (#26206)
|
||||
- fix(install): duplicate dependencies in `package.json` (#26128)
|
||||
- fix(install): handle pkg with dep on self when pkg part of peer dep resolution
|
||||
(#26277)
|
||||
- fix(install): retry downloads of registry info / tarballs (#26278)
|
||||
- fix(install): support installing npm package with alias (#26246)
|
||||
- fix(jupyter): copy kernels icons to the kernel directory (#26084)
|
||||
- fix(jupyter): keep running event loop when waiting for messages (#26049)
|
||||
- fix(lsp): relative completions for bare import-mapped specifiers (#26137)
|
||||
- fix(node): make `process.stdout.isTTY` writable (#26130)
|
||||
- fix(node/util): export `styleText` from `node:util` (#26194)
|
||||
- fix(npm): support `--allow-scripts` on `deno run` (and `deno add`,
|
||||
`deno test`, etc) (#26075)
|
||||
- fix(repl): importing json files (#26053)
|
||||
- fix(repl): remove check flags (#26140)
|
||||
- fix(unstable/worker): ensure import permissions are passed (#26101)
|
||||
- fix: add hint for missing `document` global in terminal error (#26218)
|
||||
- fix: do not panic on wsl share file paths on windows (#26081)
|
||||
- fix: do not panic running remote cjs module (#26259)
|
||||
- fix: do not panic when using methods on classes and interfaces in deno doc
|
||||
html output (#26100)
|
||||
- fix: improve suggestions and hints when using CommonJS modules (#26287)
|
||||
- fix: node-api function call should use preamble (#26297)
|
||||
- fix: panic in `prepare_stack_trace_callback` when global interceptor throws
|
||||
(#26241)
|
||||
- fix: use syntect for deno doc html generation (#26322)
|
||||
- perf(http): avoid clone getting request method and url (#26250)
|
||||
- perf(http): cache webidl.converters lookups in ext/fetch/23_response.js
|
||||
(#26256)
|
||||
- perf(http): make heap allocation for path conditional (#26289)
|
||||
- perf: use fast calls for microtask ops (#26236)
|
||||
|
||||
### 2.0.0 / 2024.10.09
|
||||
|
||||
Read announcement blog post at: https://deno.com/blog/v2
|
||||
|
||||
- BREAKING: `DENO_FUTURE=1` by default, or welcome to Deno 2.0 (#25213)
|
||||
- BREAKING: disallow `new Deno.FsFile()` (#25478)
|
||||
- BREAKING: drop support for Deno.run.{clearEnv,gid,uid} (#25371)
|
||||
- BREAKING: improve types for `Deno.serve` (#25369)
|
||||
- BREAKING: improved error code accuracy (#25383)
|
||||
- BREAKING: make supported compilerOptions an allow list (#25432)
|
||||
- BREAKING: move `width` and `height` options to `UnsafeWindowSurface`
|
||||
constructor (#24200)
|
||||
- BREAKING: remove --allow-hrtime (#25367)
|
||||
- BREAKING: remove "emit" and "map" from deno info output (#25468)
|
||||
- BREAKING: remove `--allow-none` flag (#25337)
|
||||
- BREAKING: remove `--jobs` flag (#25336)
|
||||
- BREAKING: remove `--trace-ops` (#25344)
|
||||
- BREAKING: remove `--ts` flag (#25338)
|
||||
- BREAKING: remove `--unstable` flag (#25522)
|
||||
- BREAKING: remove `deno bundle` (#25339)
|
||||
- BREAKING: remove `deno vendor` (#25343)
|
||||
- BREAKING: remove `Deno.[Tls]Listener.prototype.rid` (#25556)
|
||||
- BREAKING: remove `Deno.{Conn,TlsConn,TcpConn,UnixConn}.prototype.rid` (#25446)
|
||||
- BREAKING: remove `Deno.{Reader,Writer}[Sync]` and `Deno.Closer` (#25524)
|
||||
- BREAKING: remove `Deno.Buffer` (#25441)
|
||||
- BREAKING: remove `Deno.close()` (#25347)
|
||||
- BREAKING: remove `Deno.ConnectTlsOptions.{certChain,certFile,privateKey}` and
|
||||
`Deno.ListenTlsOptions.certChain,certFile,keyFile}` (#25525)
|
||||
- BREAKING: remove `Deno.copy()` (#25345)
|
||||
- BREAKING: remove `Deno.customInspect` (#25348)
|
||||
- BREAKING: remove `Deno.fdatasync[Sync]()` (#25520)
|
||||
- BREAKING: remove `Deno.File` (#25447)
|
||||
- BREAKING: remove `Deno.flock[Sync]()` (#25350)
|
||||
- BREAKING: remove `Deno.FsFile.prototype.rid` (#25499)
|
||||
- BREAKING: remove `Deno.fstat[Sync]()` (#25351)
|
||||
- BREAKING: remove `Deno.FsWatcher.prototype.rid` (#25444)
|
||||
- BREAKING: remove `Deno.fsync[Sync]()` (#25448)
|
||||
- BREAKING: remove `Deno.ftruncate[Sync]()` (#25412)
|
||||
- BREAKING: remove `Deno.funlock[Sync]()` (#25442)
|
||||
- BREAKING: remove `Deno.futime[Sync]()` (#25252)
|
||||
- BREAKING: remove `Deno.iter[Sync]()` (#25346)
|
||||
- BREAKING: remove `Deno.read[Sync]()` (#25409)
|
||||
- BREAKING: remove `Deno.readAll[Sync]()` (#25386)
|
||||
- BREAKING: remove `Deno.seek[Sync]()` (#25449)
|
||||
- BREAKING: remove `Deno.Seeker[Sync]` (#25551)
|
||||
- BREAKING: remove `Deno.shutdown()` (#25253)
|
||||
- BREAKING: remove `Deno.write[Sync]()` (#25408)
|
||||
- BREAKING: remove `Deno.writeAll[Sync]()` (#25407)
|
||||
- BREAKING: remove deprecated `UnsafeFnPointer` constructor type with untyped
|
||||
`Deno.PointerObject` parameter (#25577)
|
||||
- BREAKING: remove deprecated files config (#25535)
|
||||
- BREAKING: Remove obsoleted Temporal APIs part 2 (#25505)
|
||||
- BREAKING: remove remaining web types for compatibility (#25334)
|
||||
- BREAKING: remove support for remote import maps in deno.json (#25836)
|
||||
- BREAKING: rename "deps" remote cache folder to "remote" (#25969)
|
||||
- BREAKING: soft-remove `Deno.isatty()` (#25410)
|
||||
- BREAKING: soft-remove `Deno.run()` (#25403)
|
||||
- BREAKING: soft-remove `Deno.serveHttp()` (#25451)
|
||||
- BREAKING: undeprecate `Deno.FsWatcher.prototype.return()` (#25623)
|
||||
- feat: add `--allow-import` flag (#25469)
|
||||
- feat: Add a hint on error about 'Relative import path ... not prefixed with
|
||||
...' (#25430)
|
||||
- feat: Add better error messages for unstable APIs (#25519)
|
||||
- feat: Add suggestion for packages using Node-API addons (#25975)
|
||||
- feat: Allow importing .cjs files (#25426)
|
||||
- feat: default to TS for file extension and support ext flag in more scenarios
|
||||
(#25472)
|
||||
- feat: deprecate import assertions (#25281)
|
||||
- feat: Don't warn about --allow-script when using esbuild (#25894)
|
||||
- feat: hide several --unstable-* flags (#25378)
|
||||
- feat: improve lockfile v4 to store normalized version constraints and be more
|
||||
terse (#25247)
|
||||
- feat: improve warnings for deprecations and lifecycle script for npm packages
|
||||
(#25694)
|
||||
- feat: include version number in all --json based outputs (#25335)
|
||||
- feat: lockfile v4 by default (#25165)
|
||||
- feat: make 'globalThis.location' a configurable property (#25812)
|
||||
- feat: print `Listening on` messages on stderr instead of stdout (#25491)
|
||||
- feat: remove `--lock-write` flag (#25214)
|
||||
- feat: require jsr prefix for `deno install` and `deno add` (#25698)
|
||||
- feat: require(esm) (#25501)
|
||||
- feat: Show hints when using `window` global (#25805)
|
||||
- feat: stabilize `Deno.createHttpClient()` (#25569)
|
||||
- feat: suggest `deno install --entrypoint` instead of `deno cache` (#25228)
|
||||
- feat: support DENO_LOG env var instead of RUST_LOG (#25356)
|
||||
- feat: TypeScript 5.6 and `npm:@types/node@22` (#25614)
|
||||
- feat: Update no-window lint rule (#25486)
|
||||
- feat: update warning message for --allow-run with no list (#25693)
|
||||
- feat: warn when using `--allow-run` with no allow list (#25215)
|
||||
- feat(add): Add npm packages to package.json if present (#25477)
|
||||
- feat(add): strip package subpath when adding a package (#25419)
|
||||
- feat(add/install): Flag to add dev dependency to package.json (#25495)
|
||||
- feat(byonm): support `deno run npm:<package>` when package is not in
|
||||
package.json (#25981)
|
||||
- feat(check): turn on noImplicitOverride (#25695)
|
||||
- feat(check): turn on useUnknownInCatchVariables (#25465)
|
||||
- feat(cli): evaluate code snippets in JSDoc and markdown (#25220)
|
||||
- feat(cli): give access to `process` global everywhere (#25291)
|
||||
- feat(cli): use NotCapable error for permission errors (#25431)
|
||||
- feat(config): Node modules option for 2.0 (#25299)
|
||||
- feat(ext/crypto): import and export p521 keys (#25789)
|
||||
- feat(ext/crypto): X448 support (#26043)
|
||||
- feat(ext/kv): configurable limit params (#25174)
|
||||
- feat(ext/node): add abort helpers, process & streams fix (#25262)
|
||||
- feat(ext/node): add rootCertificates to node:tls (#25707)
|
||||
- feat(ext/node): buffer.transcode() (#25972)
|
||||
- feat(ext/node): export 'promises' symbol from 'node:timers' (#25589)
|
||||
- feat(ext/node): export missing constants from 'zlib' module (#25584)
|
||||
- feat(ext/node): export missing symbols from domain, puncode, repl, tls
|
||||
(#25585)
|
||||
- feat(ext/node): export more symbols from streams and timers/promises (#25582)
|
||||
- feat(ext/node): expose ES modules for _ modules (#25588)
|
||||
- feat(flags): allow double commas to escape values in path based flags (#25453)
|
||||
- feat(flags): support user provided args in repl subcommand (#25605)
|
||||
- feat(fmt): better error on malfored HTML files (#25853)
|
||||
- feat(fmt): stabilize CSS, HTML and YAML formatters (#25753)
|
||||
- feat(fmt): support vto and njk extensions (#25831)
|
||||
- feat(fmt): upgrade markup_fmt (#25768)
|
||||
- feat(install): deno install with entrypoint (#25411)
|
||||
- feat(install): warn repeatedly about not-run lifecycle scripts on explicit
|
||||
installs (#25878)
|
||||
- feat(lint): add `no-process-global` lint rule (#25709)
|
||||
- feat(lsp): add a message when someone runs 'deno lsp' manually (#26051)
|
||||
- feat(lsp): auto-import types with 'import type' (#25662)
|
||||
- feat(lsp): html/css/yaml file formatting (#25353)
|
||||
- feat(lsp): quick fix for @deno-types="npm:@types/*" (#25954)
|
||||
- feat(lsp): turn on useUnknownInCatchVariables (#25474)
|
||||
- feat(lsp): unstable setting as list (#25552)
|
||||
- feat(permissions): `Deno.mainModule` doesn't require permissions (#25667)
|
||||
- feat(permissions): allow importing from cdn.jsdelivr.net by default (#26013)
|
||||
- feat(serve): Support second parameter in deno serve (#25606)
|
||||
- feat(tools/doc): display subitems in symbol overviews where applicable
|
||||
(#25885)
|
||||
- feat(uninstall): alias to 'deno remove' if -g flag missing (#25461)
|
||||
- feat(upgrade): better error message on failure (#25503)
|
||||
- feat(upgrade): print info links for Deno 2 RC releases (#25225)
|
||||
- feat(upgrade): support LTS release channel (#25123)
|
||||
- fix: add link to env var docs (#25557)
|
||||
- fix: add suggestion how to fix importing CJS module (#21764)
|
||||
- fix: add test ensuring als works across dynamic import (#25593)
|
||||
- fix: better error for Deno.UnsafeWindowSurface, correct HttpClient name,
|
||||
cleanup unused code (#25833)
|
||||
- fix: cjs resolution cases (#25739)
|
||||
- fix: consistent with deno_config and treat `"experimentalDecorators"` as
|
||||
deprecated (#25735)
|
||||
- fix: delete old Deno 1.x headers file when loading cache (#25283)
|
||||
- fix: do not panic running invalid file specifier (#25530)
|
||||
- fix: don't include extensionless files in file collection for lint & fmt by
|
||||
default (#25721)
|
||||
- fix: don't prompt when using `Deno.permissions.request` with `--no-prompt`
|
||||
(#25811)
|
||||
- fix: eagerly error for specifier with empty version constraint (#25944)
|
||||
- fix: enable `Win32_Security` feature in `windows-sys` (#26007)
|
||||
- fix: error on unsupported compiler options (#25714)
|
||||
- fix: error out if a valid flag is passed before a subcommand (#25830)
|
||||
- fix: fix jupyter display function type (#25326)
|
||||
- fix: Float16Array type (#25506)
|
||||
- fix: handle showing warnings while the progress bar is shown (#25187)
|
||||
- fix: Hide 'deno cache' from help output (#25960)
|
||||
- fix: invalid ipv6 hostname on `deno serve` (#25482)
|
||||
- fix: linux canonicalization checks (#24641)
|
||||
- fix: lock down allow-run permissions more (#25370)
|
||||
- fix: make some warnings more standard (#25324)
|
||||
- fix: no cmd prefix in help output go links (#25459)
|
||||
- fix: only enable byonm if workspace root has pkg json (#25379)
|
||||
- fix: panic when require(esm) (#25769)
|
||||
- fix: precompile preserve SVG camelCase attributes (#25945)
|
||||
- fix: reland async context (#25140)
|
||||
- fix: remove --allow-run warning when using deno without args or subcommand
|
||||
(#25684)
|
||||
- fix: remove entrypoint hack for Deno 2.0 (#25332)
|
||||
- fix: remove recently added deno.json node_modules aliasing (#25542)
|
||||
- fix: remove the typo in the help message (#25962)
|
||||
- fix: removed unstable-htttp from deno help (#25216)
|
||||
- fix: replace `npm install` hint with `deno install` hint (#25244)
|
||||
- fix: trim space around DENO_AUTH_TOKENS (#25147)
|
||||
- fix: update deno_doc (#25290)
|
||||
- fix: Update deno_npm to fix `deno install` with crossws (#25837)
|
||||
- fix: update hint for `deno add <package>` (#25455)
|
||||
- fix: update malva in deno to support astro css comments (#25553)
|
||||
- fix: update nodeModulesDir config JSON schema (#25653)
|
||||
- fix: update patchver to 0.2 (#25952)
|
||||
- fix: update sui to 0.4 (#25942)
|
||||
- fix: upgrade deno_ast 0.42 (#25313)
|
||||
- fix: upgrade deno_core to 0.307.0 (#25287)
|
||||
- fix(add/install): default to "latest" tag for npm packages in
|
||||
`deno add npm:pkg` (#25858)
|
||||
- fix(bench): Fix table column alignments and NO_COLOR=1 (#25190)
|
||||
- fix(BREAKING): make dns record types have consistent naming (#25357)
|
||||
- fix(byonm): resolve npm deps of jsr deps (#25399)
|
||||
- fix(check): ignore noImplicitOverrides in remote modules (#25854)
|
||||
- fix(check): move is cjs check from resolving to loading (#25597)
|
||||
- fix(check): properly surface dependency errors in types file of js file
|
||||
(#25860)
|
||||
- fix(cli): `deno task` exit with status 0 (#25637)
|
||||
- fix(cli): Default to auto with --node-modules-dir flag (#25772)
|
||||
- fix(cli): handle edge cases around `export`s in doc tests and default export
|
||||
(#25720)
|
||||
- fix(cli): Map error kind to `PermissionDenied` when symlinking fails due to
|
||||
permissions (#25398)
|
||||
- fix(cli): Only set allow net flag for deno serve if not already allowed all
|
||||
(#25743)
|
||||
- fix(cli): Warn on not-run lifecycle scripts with global cache (#25786)
|
||||
- fix(cli/tools): correct `deno init --serve` template behavior (#25318)
|
||||
- fix(compile): support 'deno compile' in RC and LTS releases (#25875)
|
||||
- fix(config): validate export names (#25436)
|
||||
- fix(coverage): ignore urls from doc testing (#25736)
|
||||
- fix(doc): surface graph errors as warnings (#25888)
|
||||
- fix(dts): stabilize `fetch` declaration for use with `Deno.HttpClient`
|
||||
(#25683)
|
||||
- fix(ext/console): more precision in console.time (#25723)
|
||||
- fix(ext/console): prevent duplicate error printing when the cause is assigned
|
||||
(#25327)
|
||||
- fix(ext/crypto): ensure EC public keys are exported uncompressed (#25766)
|
||||
- fix(ext/crypto): fix identity test for x25519 derive bits (#26011)
|
||||
- fix(ext/crypto): reject empty usages in SubtleCrypto#importKey (#25759)
|
||||
- fix(ext/crypto): support md4 digest algorithm (#25656)
|
||||
- fix(ext/crypto): throw DataError for invalid EC key import (#25181)
|
||||
- fix(ext/fetch): fix lowercase http_proxy classified as https (#25686)
|
||||
- fix(ext/fetch): percent decode userinfo when parsing proxies (#25229)
|
||||
- fix(ext/http): do not set localhost to hostname unnecessarily (#24777)
|
||||
- fix(ext/http): gracefully handle Response.error responses (#25712)
|
||||
- fix(ext/node): add `FileHandle#writeFile` (#25555)
|
||||
- fix(ext/node): add `vm.constants` (#25630)
|
||||
- fix(ext/node): Add missing `node:path` exports (#25567)
|
||||
- fix(ext/node): Add missing node:fs and node:constants exports (#25568)
|
||||
- fix(ext/node): add stubs for `node:trace_events` (#25628)
|
||||
- fix(ext/node): attach console stream properties (#25617)
|
||||
- fix(ext/node): avoid showing `UNKNOWN` error from TCP handle (#25550)
|
||||
- fix(ext/node): close upgraded socket when the underlying http connection is
|
||||
closed (#25387)
|
||||
- fix(ext/node): delay accept() call 2 ticks in net.Server#listen (#25481)
|
||||
- fix(ext/node): don't throw error for unsupported signal binding on windows
|
||||
(#25699)
|
||||
- fix(ext/node): emit `online` event after worker thread is initialized (#25243)
|
||||
- fix(ext/node): export `process.allowedNodeEnvironmentFlags` (#25629)
|
||||
- fix(ext/node): export JWK public key (#25239)
|
||||
- fix(ext/node): export request and response clases from `http2` module (#25592)
|
||||
- fix(ext/node): fix `Cipheriv#update(string, undefined)` (#25571)
|
||||
- fix(ext/node): fix Decipheriv when autoPadding disabled (#25598)
|
||||
- fix(ext/node): fix process.stdin.pause() (#25864)
|
||||
- fix(ext/node): Fix vm sandbox object panic (#24985)
|
||||
- fix(ext/node): http2session ready state (#25143)
|
||||
- fix(ext/node): Implement detached option in `child_process` (#25218)
|
||||
- fix(ext/node): import EC JWK keys (#25266)
|
||||
- fix(ext/node): import JWK octet key pairs (#25180)
|
||||
- fix(ext/node): import RSA JWK keys (#25267)
|
||||
- fix(ext/node): register `node:wasi` built-in (#25134)
|
||||
- fix(ext/node): remove unimplemented promiseHook stubs (#25979)
|
||||
- fix(ext/node): report freemem() on Linux in bytes (#25511)
|
||||
- fix(ext/node): Rewrite `node:v8` serialize/deserialize (#25439)
|
||||
- fix(ext/node): session close during stream setup (#25170)
|
||||
- fix(ext/node): Stream should be instance of EventEmitter (#25527)
|
||||
- fix(ext/node): stub `inspector/promises` (#25635)
|
||||
- fix(ext/node): stub `process.cpuUsage()` (#25462)
|
||||
- fix(ext/node): stub cpu_info() for OpenBSD (#25807)
|
||||
- fix(ext/node): support x509 certificates in `createPublicKey` (#25731)
|
||||
- fix(ext/node): throw when loading `cpu-features` module (#25257)
|
||||
- fix(ext/node): update aead-gcm-stream to 0.3 (#25261)
|
||||
- fix(ext/node): use primordials in `ext/node/polyfills/console.ts` (#25572)
|
||||
- fix(ext/node): use primordials in ext/node/polyfills/wasi.ts (#25608)
|
||||
- fix(ext/node): validate input lengths in `Cipheriv` and `Decipheriv` (#25570)
|
||||
- fix(ext/web): don't ignore capture in EventTarget.removeEventListener (#25788)
|
||||
- fix(ext/webgpu): allow to build on unsupported platforms (#25202)
|
||||
- fix(ext/webgpu): sync category comment (#25580)
|
||||
- fix(ext/webstorage): make `getOwnPropertyDescriptor` with symbol return
|
||||
`undefined` (#13348)
|
||||
- fix(flags): --allow-all should conflict with lower permissions (#25909)
|
||||
- fix(flags): don't treat empty run command as task subcommand (#25708)
|
||||
- fix(flags): move some content from docs.deno.com into help output (#25951)
|
||||
- fix(flags): properly error out for urls (#25770)
|
||||
- fix(flags): require global flag for permission flags in install subcommand
|
||||
(#25391)
|
||||
- fix(fmt): --check was broken for CSS, YAML and HTML (#25848)
|
||||
- fix(fmt): fix incorrect quotes in components (#25249)
|
||||
- fix(fmt): fix tabs in YAML (#25536)
|
||||
- fix(fmt/markdown): fix regression with multi-line footnotes and inline math
|
||||
(#25222)
|
||||
- fix(info): error instead of panic for npm specifiers when using byonm (#25947)
|
||||
- fix(info): move "version" field to top of json output (#25890)
|
||||
- fix(inspector): Fix panic when re-entering runtime ops (#25537)
|
||||
- fix(install): compare versions directly to decide whether to create a child
|
||||
node_modules dir for a workspace member (#26001)
|
||||
- fix(install): Make sure target node_modules exists when symlinking (#25494)
|
||||
- fix(install): recommend using `deno install -g` when using a single http url
|
||||
(#25388)
|
||||
- fix(install): store tags associated with package in node_modules dir (#26000)
|
||||
- fix(install): surface package.json dependency errors (#26023)
|
||||
- fix(install): Use relative symlinks in deno install (#25164)
|
||||
- fix(installl): make bin entries executable even if not put in
|
||||
`node_modules/.bin` (#25873)
|
||||
- fix(jupyter): allow unstable flags (#25483)
|
||||
- fix(lint): correctly handle old jsx in linter (#25902)
|
||||
- fix(lint): support linting jsr pkg without version field (#25230)
|
||||
- fix(lockfile): use loose deserialization for version constraints (#25660)
|
||||
- fix(lsp): encode url parts before parsing as uri (#25509)
|
||||
- fix(lsp): exclude missing import quick fixes with bad resolutions (#26025)
|
||||
- fix(lsp): panic on url_to_uri() (#25238)
|
||||
- fix(lsp): properly resolve jsxImportSource for caching (#25688)
|
||||
- fix(lsp): update diagnostics on npm install (#25352)
|
||||
- fix(napi): Don't run microtasks in napi_resolve_deferred (#25246)
|
||||
- fix(napi): Fix worker threads importing already-loaded NAPI addon (#25245)
|
||||
- fix(no-slow-types): better `override` handling (#25989)
|
||||
- fix(node): Don't error out if we fail to statically analyze CJS re-export
|
||||
(#25748)
|
||||
- fix(node): fix worker_threads issues blocking Angular support (#26024)
|
||||
- fix(node): implement libuv APIs needed to support `npm:sqlite3` (#25893)
|
||||
- fix(node): Include "node" condition during CJS re-export analysis (#25785)
|
||||
- fix(node): Pass NPM_PROCESS_STATE to subprocesses via temp file instead of env
|
||||
var (#25896)
|
||||
- fix(node/byonm): do not accidentally resolve bare node built-ins (#25543)
|
||||
- fix(node/cluster): improve stubs to make log4js work (#25146)
|
||||
- fix(npm): better error handling for remote npm deps (#25670)
|
||||
- fix(npm): root package has peer dependency on itself (#26022)
|
||||
- fix(permissions): disallow any `LD_` or `DYLD_` prefixed env var without full
|
||||
--allow-run permissions (#25271)
|
||||
- fix(permissions): disallow launching subprocess with LD_PRELOAD env var
|
||||
without full run permissions (#25221)
|
||||
- fix(publish): ensure provenance is spec compliant (#25200)
|
||||
- fix(regression): do not expose resolved path in Deno.Command permission denied
|
||||
error (#25434)
|
||||
- fix(runtime): don't error `child.output()` on consumed stream (#25657)
|
||||
- fix(runtime): use more null proto objects again (#25040)
|
||||
- fix(runtime/web_worker): populate `SnapshotOptions` for `WebWorker` when
|
||||
instantiated without snapshot (#25280)
|
||||
- fix(task): correct name for scoped npm package binaries (#25390)
|
||||
- fix(task): support tasks with colons in name in `deno run` (#25233)
|
||||
- fix(task): use current executable for deno even when not named deno (#26019)
|
||||
- fix(types): simplify mtls related types (#25658)
|
||||
- fix(upgrade): more informative information on invalid version (#25319)
|
||||
- fix(windows): Deno.Command - align binary resolution with linux and mac
|
||||
(#25429)
|
||||
- fix(workspace): handle when config has members when specified via --config
|
||||
(#25988)
|
||||
- perf: fast path for cached dyn imports (#25636)
|
||||
- perf: Use -O3 for sui in release builds (#26010)
|
||||
- perf(cache): single cache file for remote modules (#24983)
|
||||
- perf(cache): single cache file for typescript emit (#24994)
|
||||
- perf(ext/fetch): improve decompression throughput by upgrading `tower_http`
|
||||
(#25806)
|
||||
- perf(ext/node): reduce some allocations in require (#25197)
|
||||
- perf(ext/web): optimize performance.measure() (#25774)
|
||||
|
||||
### 1.46.3 / 2024.09.04
|
||||
|
||||
- feat(upgrade): print info links for Deno 2 RC releases (#25225)
|
||||
- fix(cli): Map error kind to `PermissionDenied` when symlinking fails due to
|
||||
permissions (#25398)
|
||||
- fix(cli/tools): correct `deno init --serve` template behavior (#25318)
|
||||
- fix(ext/node): session close during stream setup (#25170)
|
||||
- fix(publish): ensure provenance is spec compliant (#25200)
|
||||
- fix(upgrade): more informative information on invalid version (#25319)
|
||||
- fix: fix jupyter display function type (#25326)
|
||||
|
||||
### 1.46.2 / 2024.08.29
|
||||
|
||||
- Revert "feat(fetch): accept async iterables for body" (#25207)
|
||||
- fix(bench): Fix table column alignments and NO_COLOR=1 (#25190)
|
||||
- fix(ext/crypto): throw DataError for invalid EC key import (#25181)
|
||||
- fix(ext/fetch): percent decode userinfo when parsing proxies (#25229)
|
||||
- fix(ext/node): emit `online` event after worker thread is initialized (#25243)
|
||||
- fix(ext/node): export JWK public key (#25239)
|
||||
- fix(ext/node): import EC JWK keys (#25266)
|
||||
- fix(ext/node): import JWK octet key pairs (#25180)
|
||||
- fix(ext/node): import RSA JWK keys (#25267)
|
||||
- fix(ext/node): throw when loading `cpu-features` module (#25257)
|
||||
- fix(ext/node): update aead-gcm-stream to 0.3 (#25261)
|
||||
- fix(ext/webgpu): allow to build on unsupported platforms (#25202)
|
||||
- fix(fmt): fix incorrect quotes in components (#25249)
|
||||
- fix(fmt/markdown): fix regression with multi-line footnotes and inline math
|
||||
(#25222)
|
||||
- fix(install): Use relative symlinks in deno install (#25164)
|
||||
- fix(lsp): panic on url_to_uri() (#25238)
|
||||
- fix(napi): Don't run microtasks in napi_resolve_deferred (#25246)
|
||||
- fix(napi): Fix worker threads importing already-loaded NAPI addon (#25245)
|
||||
- fix(node/cluster): improve stubs to make log4js work (#25146)
|
||||
- fix(runtime/web_worker): populate `SnapshotOptions` for `WebWorker` when
|
||||
instantiated without snapshot (#25280)
|
||||
- fix(task): support tasks with colons in name in `deno run` (#25233)
|
||||
- fix: handle showing warnings while the progress bar is shown (#25187)
|
||||
- fix: reland async context (#25140)
|
||||
- fix: removed unstable-htttp from deno help (#25216)
|
||||
- fix: replace `npm install` hint with `deno install` hint (#25244)
|
||||
- fix: update deno_doc (#25290)
|
||||
- fix: upgrade deno_core to 0.307.0 (#25287)
|
||||
- perf(ext/node): reduce some allocations in require (#25197)
|
||||
|
||||
### 1.46.1 / 2024.08.22
|
||||
|
||||
- fix(ext/node): http2session ready state (#25143)
|
||||
- fix(ext/node): register `node:wasi` built-in (#25134)
|
||||
- fix(urlpattern): fallback to empty string for undefined group values (#25151)
|
||||
- fix: trim space around DENO_AUTH_TOKENS (#25147)
|
||||
|
||||
### 1.46.0 / 2024.08.22
|
||||
|
||||
- BREAKING(temporal/unstable): Remove obsoleted Temporal APIs (#24836)
|
||||
- BREAKING(webgpu/unstable): Replace async .requestAdapterInfo() with sync .info
|
||||
(#24783)
|
||||
- feat: `deno compile --icon <ico>` (#25039)
|
||||
- feat: `deno init --serve` (#24897)
|
||||
- feat: `deno upgrade --rc` (#24905)
|
||||
- feat: Add Deno.ServeDefaultExport type (#24879)
|
||||
- feat: async context (#24402)
|
||||
- feat: better help output (#24958)
|
||||
- feat: codesign for deno compile binaries (#24604)
|
||||
- feat: deno clean (#24950)
|
||||
- feat: deno remove (#24952)
|
||||
- feat: deno run <task> (#24891)
|
||||
- feat: Deprecate "import assertions" with a warning (#24743)
|
||||
- feat: glob and directory support for `deno check` and `deno cache` cli arg
|
||||
paths (#25001)
|
||||
- feat: Print deprecation message for npm packages (#24992)
|
||||
- feat: refresh "Download" progress bar with a spinner (#24913)
|
||||
- feat: Rename --unstable-hmr to --watch-hmr (#24975)
|
||||
- feat: support short flags for permissions (#24883)
|
||||
- feat: treat bare deno command with run arguments as deno run (#24887)
|
||||
- feat: upgrade deno_core (#24886)
|
||||
- feat: upgrade deno_core (#25042)
|
||||
- feat: upgrade V8 to 12.8 (#24693)
|
||||
- feat: Upgrade V8 to 12.9 (#25138)
|
||||
- feat: vm rewrite (#24596)
|
||||
- feat(clean): add progress bar (#25026)
|
||||
- feat(cli): Add --env-file as alternative to --env (#24555)
|
||||
- feat(cli/tools): add a subcommand `--hide-stacktraces` for test (#24095)
|
||||
- feat(config): Support frozen lockfile config option in deno.json (#25100)
|
||||
- feat(config/jsr): add license field (#25056)
|
||||
- feat(coverage): add breadcrumbs to deno coverage `--html` report (#24860)
|
||||
- feat(ext/node): rewrite crypto keys (#24463)
|
||||
- feat(ext/node): support http2session.socket (#24786)
|
||||
- feat(fetch): accept async iterables for body (#24623)
|
||||
- feat(flags): improve help output and make `deno run` list tasks (#25108)
|
||||
- feat(fmt): support CSS, SCSS, Sass and Less (#24870)
|
||||
- feat(fmt): support HTML, Svelte, Vue, Astro and Angular (#25019)
|
||||
- feat(fmt): support YAML (#24717)
|
||||
- feat(FUTURE): terse lockfile (v4) (#25059)
|
||||
- feat(install): change 'Add ...' message (#24949)
|
||||
- feat(lint): Add lint for usage of node globals (with autofix) (#25048)
|
||||
- feat(lsp): node specifier completions (#24904)
|
||||
- feat(lsp): registry completions for import-mapped specifiers (#24792)
|
||||
- feat(node): support `username` and `_password` in `.npmrc` file (#24793)
|
||||
- feat(permissions): link to docs in permission prompt (#24948)
|
||||
- feat(publish): error on missing license file (#25011)
|
||||
- feat(publish): suggest importing `jsr:@std/` for `deno.land/std` urls (#25046)
|
||||
- feat(serve): Opt-in parallelism for `deno serve` (#24920)
|
||||
- feat(test): rename --allow-none to --permit-no-files (#24809)
|
||||
- feat(unstable): ability to use a local copy of jsr packages (#25068)
|
||||
- feat(unstable/fmt): move yaml formatting behind unstable flag (#24848)
|
||||
- feat(upgrade): refresh output (#24911)
|
||||
- feat(upgrade): support `deno upgrade 1.46.0` (#25096)
|
||||
- feat(urlpattern): add ignoreCase option & hasRegExpGroups property, and fix
|
||||
spec discrepancies (#24741)
|
||||
- feat(watch): add watch paths to test subcommand (#24771)
|
||||
- fix: `node:inspector` not being registered (#25007)
|
||||
- fix: `rename` watch event missing (#24893)
|
||||
- fix: actually add missing `node:readline/promises` module (#24772)
|
||||
- fix: adapt to new jupyter runtime API and include session IDs (#24762)
|
||||
- fix: add permission name when accessing a special file errors (#25085)
|
||||
- fix: adjust suggestion for lockfile regeneration (#25107)
|
||||
- fix: cache bust jsr meta file when version not found in dynamic branches
|
||||
(#24928)
|
||||
- fix: CFunctionInfo and CTypeInfo leaks (#24634)
|
||||
- fix: clean up flag help output (#24686)
|
||||
- fix: correct JSON config schema to show vendor option as stable (#25090)
|
||||
- fix: dd-trace http message compat (#25021)
|
||||
- fix: deserialize lockfile v3 straight (#25121)
|
||||
- fix: Don't panic if fail to handle JS stack frame (#25122)
|
||||
- fix: Don't panic if failed to add system certificate (#24823)
|
||||
- fix: Don't shell out to `unzip` in deno upgrade/compile (#24926)
|
||||
- fix: enable the reporting of parsing related problems when running deno lint
|
||||
(#24332)
|
||||
- fix: errors with CallSite methods (#24907)
|
||||
- fix: include already seen deps in lockfile dep tracking (#24556)
|
||||
- fix: log current version when using deno upgrade (#25079)
|
||||
- fix: make `deno add` output more deterministic (#25083)
|
||||
- fix: make vendor cache manifest more deterministic (#24658)
|
||||
- fix: missing `emitWarning` import (#24587)
|
||||
- fix: regressions around Error.prepareStackTrace (#24839)
|
||||
- fix: stub `node:module.register()` (#24965)
|
||||
- fix: support `npm:bindings` and `npm:callsites` packages (#24727)
|
||||
- fix: unblock fsevents native module (#24542)
|
||||
- fix: update deno_doc (#24972)
|
||||
- fix: update dry run success message (#24885)
|
||||
- fix: update lsp error message of 'relative import path' to 'use deno add' for
|
||||
npm/jsr packages (#24524)
|
||||
- fix: upgrade deno_core to 0.298.0 (#24709)
|
||||
- fix: warn about import assertions when using typescript (#25135)
|
||||
- fix(add): better error message providing scoped pkg missing leading `@` symbol
|
||||
(#24961)
|
||||
- fix(add): Better error message when missing npm specifier (#24970)
|
||||
- fix(add): error when config file contains importMap field (#25115)
|
||||
- fix(add): Handle packages without root exports (#25102)
|
||||
- fix(add): Support dist tags in deno add (#24960)
|
||||
- fix(cli): add NAPI support in standalone mode (#24642)
|
||||
- fix(cli): Create child node_modules for conflicting dependency versions,
|
||||
respect aliases in package.json (#24609)
|
||||
- fix(cli): Respect implied BYONM from DENO_FUTURE in `deno task` (#24652)
|
||||
- fix(cli): shorten examples in help text (#24374)
|
||||
- fix(cli): support --watch when running cjs npm packages (#25038)
|
||||
- fix(cli): Unhide publish subcommand help string (#24787)
|
||||
- fix(cli): update permission prompt message for compiled binaries (#24081)
|
||||
- fix(cli/init): broken link in deno init sample template (#24545)
|
||||
- fix(compile): adhoc codesign mach-o by default (#24824)
|
||||
- fix(compile): make output more deterministic (#25092)
|
||||
- fix(compile): support workspace members importing other members (#24909)
|
||||
- fix(compile/windows): handle cjs re-export of relative path with parent
|
||||
component (#24795)
|
||||
- fix(config): regression - should not discover npm workspace for nested
|
||||
deno.json not in workspace (#24559)
|
||||
- fix(cron): improve error message for invalid cron names (#24644)
|
||||
- fix(docs): fix some deno.land/manual broken urls (#24557)
|
||||
- fix(ext/console): Error Cause Not Inspect-Formatted when printed (#24526)
|
||||
- fix(ext/console): render properties of Intl.Locale (#24827)
|
||||
- fix(ext/crypto): respect offsets when writing into ab views in randomFillSync
|
||||
(#24816)
|
||||
- fix(ext/fetch): include TCP src/dst socket info in error messages (#24939)
|
||||
- fix(ext/fetch): include URL and error details on fetch failures (#24910)
|
||||
- fix(ext/fetch): respect authority from URL (#24705)
|
||||
- fix(ext/fetch): use correct ALPN to proxies (#24696)
|
||||
- fix(ext/fetch): use correct ALPN to socks5 proxies (#24817)
|
||||
- fix(ext/http): correctly consume response body in `Deno.serve` (#24811)
|
||||
- fix(ext/net): validate port in Deno.{connect,serve,listen} (#24399)
|
||||
- fix(ext/node): add `CipherIv.setAutoPadding()` (#24940)
|
||||
- fix(ext/node): add crypto.diffieHellman (#24938)
|
||||
- fix(ext/node): client closing streaming request shouldn't terminate http
|
||||
server (#24946)
|
||||
- fix(ext/node): createBrotliCompress params (#24984)
|
||||
- fix(ext/node): do not expose `self` global in node (#24637)
|
||||
- fix(ext/node): don't concat set-cookie in ServerResponse.appendHeader (#25000)
|
||||
- fix(ext/node): don't throw when calling PerformanceObserver.observe (#25036)
|
||||
- fix(ext/node): ed25519 signing and cipheriv autopadding fixes (#24957)
|
||||
- fix(ext/node): fix prismjs compatibiliy in Web Worker (#25062)
|
||||
- fix(ext/node): handle node child_process with --v8-options flag (#24804)
|
||||
- fix(ext/node): handle prefix mapping for IPv4-mapped IPv6 addresses (#24546)
|
||||
- fix(ext/node): http request uploads of subarray of buffer should work (#24603)
|
||||
- fix(ext/node): improve shelljs compat with managed npm execution (#24912)
|
||||
- fix(ext/node): node:zlib coerces quality 10 to 9.5 (#24850)
|
||||
- fix(ext/node): pass content-disposition header as string instead of bytes
|
||||
(#25128)
|
||||
- fix(ext/node): prevent panic in http2.connect with uppercase header names
|
||||
(#24780)
|
||||
- fix(ext/node): read correct CPU usage stats on Linux (#24732)
|
||||
- fix(ext/node): rewrite X509Certificate resource and add `publicKey()` (#24988)
|
||||
- fix(ext/node): stat.mode on windows (#24434)
|
||||
- fix(ext/node): support ieee-p1363 ECDSA signatures and pss salt len (#24981)
|
||||
- fix(ext/node): use pem private keys in createPublicKey (#24969)
|
||||
- fix(ext/node/net): emit `error` before `close` when connection is refused
|
||||
(#24656)
|
||||
- fix(ext/web): make CompressionResource garbage collectable (#24884)
|
||||
- fix(ext/web): make TextDecoderResource use cppgc (#24888)
|
||||
- fix(ext/webgpu): assign missing `constants` property of shader about
|
||||
`GPUDevice.createRenderPipeline[Async]` (#24803)
|
||||
- fix(ext/webgpu): don't crash while constructing GPUOutOfMemoryError (#24807)
|
||||
- fix(ext/webgpu): GPUDevice.createRenderPipelineAsync should return a Promise
|
||||
(#24349)
|
||||
- fix(ext/websocket): unhandled close rejection in WebsocketStream (#25125)
|
||||
- fix(fmt): handle using stmt in for of stmt (#24834)
|
||||
- fix(fmt): regression with pipe in code blocks in tables (#25098)
|
||||
- fix(fmt): upgrade to dprint-plugin-markdown 0.17.4 (#25075)
|
||||
- fix(fmt): was sometimes putting comments in front of commas in parameter lists
|
||||
(#24650)
|
||||
- fix(future): Emit `deno install` warning less often, suggest `deno install` in
|
||||
error message (#24706)
|
||||
- fix(http): Adjust hostname display for Windows when using 0.0.0.0 (#24698)
|
||||
- fix(init): use bare specifier for `jsr:@std/assert` (#24581)
|
||||
- fix(install): Properly handle dist tags when setting up node_modules (#24968)
|
||||
- fix(lint): support linting tsx/jsx from stdin (#24955)
|
||||
- fix(lsp): directly use file referrer when loading document (#24997)
|
||||
- fix(lsp): don't always use byonm resolver when DENO_FUTURE=1 (#24865)
|
||||
- fix(lsp): hang when caching failed (#24651)
|
||||
- fix(lsp): import map lookup for jsr subpath auto import (#25025)
|
||||
- fix(lsp): include scoped import map keys in completions (#25047)
|
||||
- fix(lsp): resolve jsx import source with types mode (#25064)
|
||||
- fix(lsp): rewrite import for 'infer return type' action (#24685)
|
||||
- fix(lsp): scope attribution for asset documents (#24663)
|
||||
- fix(lsp): support npm workspaces and fix some resolution issues (#24627)
|
||||
- fix(node): better detection for when to surface node resolution errors
|
||||
(#24653)
|
||||
- fix(node): cjs pkg dynamically importing esm-only pkg fails (#24730)
|
||||
- fix(node): Create additional pipes for child processes (#25016)
|
||||
- fix(node): Fix `--allow-scripts` with no `deno.json` (#24533)
|
||||
- fix(node): Fix node IPC serialization for objects with undefined values
|
||||
(#24894)
|
||||
- fix(node): revert invalid package target change (#24539)
|
||||
- fix(node): Rework node:child_process IPC (#24763)
|
||||
- fix(node): Run node compat tests listed in the `ignore` field (and fix the
|
||||
ones that fail) (#24631)
|
||||
- fix(node): support `tty.hasColors()` and `tty.getColorDepth()` (#24619)
|
||||
- fix(node): support wildcards in package.json imports (#24794)
|
||||
- fix(node/crypto): Assign publicKey and privateKey with let instead of const
|
||||
(#24943)
|
||||
- fix(node/fs): node:fs.read and write should accept typed arrays other than
|
||||
Uint8Array (#25030)
|
||||
- fix(node/fs): Use correct offset and length in node:fs.read and write (#25049)
|
||||
- fix(node/fs/promises): watch should be async iterable (#24805)
|
||||
- fix(node/http): wrong `req.url` value (#25081)
|
||||
- fix(node/inspector): Session constructor should not throw (#25041)
|
||||
- fix(node/timers/promises): add scheduler APIs (#24802)
|
||||
- fix(node/tty): fix `tty.WriteStream.hasColor` with different args (#25094)
|
||||
- fix(node/util): add missing `debug` alias of `debuglog` (#24944)
|
||||
- fix(node/worker_threads): support `port.once()` (#24725)
|
||||
- fix(npm): handle packages with only pre-released 0.0.0 versions (#24563)
|
||||
- fix(npm): use start directory deno.json as "root deno.json config" in npm
|
||||
workspace (#24538)
|
||||
- fix(npmrc): skip loading .npmrc in home dir on permission error (#24758)
|
||||
- fix(publish): show dirty files on dirty check failure (#24541)
|
||||
- fix(publish): surface syntax errors when using --no-check (#24620)
|
||||
- fix(publish): warn about missing license file (#24677)
|
||||
- fix(publish): workspace included license file had incorrect path (#24747)
|
||||
- fix(repl): Prevent panic on broken pipe (#21945)
|
||||
- fix(runtime/windows): fix calculation of console size (#23873)
|
||||
- fix(std/http2): release window capacity back to remote stream (#24576)
|
||||
- fix(tls): print a warning if a system certificate can't be loaded (#25023)
|
||||
- fix(types): Conform lib.deno_web.d.ts to lib.dom.d.ts and lib.webworker.d.ts
|
||||
(#24599)
|
||||
- fix(types): fix streams types (#24770)
|
||||
- fix(unstable): move sloppy-import warnings to lint rule (#24710)
|
||||
- fix(unstable): panic when running deno install with DENO_FUTURE=1 (#24866)
|
||||
- fix(unstable/compile): handle byonm import in sub dir (#24755)
|
||||
- fix(upgrade): better error message when check_exe fails (#25133)
|
||||
- fix(upgrade): correctly compute latest version based on current release
|
||||
channel (#25087)
|
||||
- fix(upgrade): do not error if config in cwd invalid (#24689)
|
||||
- fix(upgrade): fallback to Content-Length header for progress bar (#24923)
|
||||
- fix(upgrade): return no RC versions if fetching fails (#25013)
|
||||
- fix(upgrade): support RC release with --version flag (#25091)
|
||||
- fix(upgrade): use proper version display (#25029)
|
||||
- fix(urlpattern): correct typings for added APIs (#24881)
|
||||
- fix(webgpu): Fix `GPUAdapter#isFallbackAdapter` and `GPUAdapter#info`
|
||||
properties (#24914)
|
||||
- fix(workspace): do not resolve to self for npm pkg depending on matching req
|
||||
(#24591)
|
||||
- fix(workspace): support resolving bare specifiers to npm pkgs within a
|
||||
workspace (#24611)
|
||||
- fix(workspaces/publish): include the license file from the workspace root if
|
||||
not in pkg (#24714)
|
||||
- perf: skip saving to emit cache after first failure (#24896)
|
||||
- perf: update deno_ast to 0.41 (#24819)
|
||||
- perf: update deno_doc (#24700)
|
||||
- perf(ext/crypto): make randomUUID() 5x faster (#24510)
|
||||
- perf(ext/fetch): speed up `resp.clone()` (#24812)
|
||||
- perf(ext/http): Reduce size of `ResponseBytesInner` (#24840)
|
||||
- perf(ext/node): improve `Buffer` from string performance (#24567)
|
||||
- perf(ext/node): optimize fs.exists[Sync] (#24613)
|
||||
- perf(lsp): remove fallback config scopes for workspace folders (#24868)
|
||||
- refactor: `version` module exports a single const struct (#25014)
|
||||
- refactor: decouple node resolution from deno_core (#24724)
|
||||
- refactor: move importMap with imports/scopes diagnostic to deno_config
|
||||
(#24553)
|
||||
- refactor: remove version::is_canary(), use ReleaseChannel instead (#25053)
|
||||
- refactor: show release channel in `deno --version` (#25061)
|
||||
- refactor: update to deno_config 0.25 (#24645)
|
||||
- refactor: update to use deno_package_json (#24688)
|
||||
- refactor(ext/node): create separate ops for node:http module (#24788)
|
||||
- refactor(fetch): reimplement fetch with hyper instead of reqwest (#24237)
|
||||
- refactor(lint): move reporters to separate module (#24757)
|
||||
- refactor(node): internally add `.code()` to node resolution errors (#24610)
|
||||
- refactor(upgrade): cleanup pass (#24954)
|
||||
- refactor(upgrade): make fetching latest version async (#24919)
|
||||
- Reland "fix: CFunctionInfo and CTypeInfo leaks (#24634)" (#24692)
|
||||
- Reland "refactor(fetch): reimplement fetch with hyper instead of reqwest"
|
||||
(#24593)
|
||||
|
||||
### 1.45.5 / 2024.07.31
|
||||
|
||||
- fix(cli): Unhide publish subcommand help string (#24787)
|
||||
- fix(compile/windows): handle cjs re-export of relative path with parent
|
||||
component (#24795)
|
||||
- fix(ext/node): handle node child_process with --v8-options flag (#24804)
|
||||
- fix(ext/node): prevent panic in http2.connect with uppercase header names
|
||||
(#24780)
|
||||
- fix(ext/webgpu): don't crash while constructing GPUOutOfMemoryError (#24807)
|
||||
- fix(http): Adjust hostname display for Windows when using 0.0.0.0 (#24698)
|
||||
- fix(node): Rework node:child_process IPC (#24763)
|
||||
- fix(node): support wildcards in package.json imports (#24794)
|
||||
- fix(node/fs/promises): watch should be async iterable (#24805)
|
||||
- fix(node/timers/promises): add scheduler APIs (#24802)
|
||||
- fix(npmrc): skip loading .npmrc in home dir on permission error (#24758)
|
||||
- fix(types): fix streams types (#24770)
|
||||
- fix(unstable/compile): handle byonm import in sub dir (#24755)
|
||||
- fix: actually add missing `node:readline/promises` module (#24772)
|
||||
- fix: adapt to new jupyter runtime API and include session IDs (#24762)
|
||||
- perf(ext/fetch): speed up `resp.clone()` (#24812)
|
||||
- perf(ext/node): improve `Buffer` from string performance (#24567)
|
||||
|
||||
### 1.45.4 / 2024.07.26
|
||||
|
||||
- Reland "fix: CFunctionInfo and CTypeInfo leaks (#24634)" (#24692)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_bench_util"
|
||||
version = "0.157.0"
|
||||
version = "0.173.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno"
|
||||
version = "1.45.4"
|
||||
version = "2.1.1"
|
||||
authors.workspace = true
|
||||
default-run = "deno"
|
||||
edition.workspace = true
|
||||
|
@ -38,6 +38,11 @@ path = "./bench/lsp_bench_standalone.rs"
|
|||
|
||||
[features]
|
||||
default = ["upgrade", "__vendored_zlib_ng"]
|
||||
# A feature that enables heap profiling with dhat on Linux.
|
||||
# 1. Compile with `cargo build --profile=release-with-debug --features=dhat-heap`
|
||||
# 2. Run the executable. It will output a dhat-heap.json file.
|
||||
# 3. Open the json file in https://nnethercote.github.io/dh_view/dh_view.html
|
||||
dhat-heap = ["dhat"]
|
||||
# A feature that enables the upgrade subcommand and the background check for
|
||||
# available updates (of deno binary). This is typically disabled for (Linux)
|
||||
# distribution packages.
|
||||
|
@ -64,44 +69,45 @@ winres.workspace = true
|
|||
|
||||
[dependencies]
|
||||
deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] }
|
||||
deno_cache_dir = { workspace = true }
|
||||
deno_config = { version = "=0.26.1", features = ["workspace", "sync"] }
|
||||
deno_cache_dir.workspace = true
|
||||
deno_config.workspace = true
|
||||
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
|
||||
deno_doc = { version = "0.144.0", features = ["html", "syntect"] }
|
||||
deno_emit = "=0.43.1"
|
||||
deno_graph = { version = "=0.80.1", features = ["tokio_executor"] }
|
||||
deno_lint = { version = "=0.61.0", features = ["docs"] }
|
||||
deno_doc = { version = "0.160.0", features = ["rust", "comrak"] }
|
||||
deno_graph = { version = "=0.85.0" }
|
||||
deno_lint = { version = "=0.68.0", features = ["docs"] }
|
||||
deno_lockfile.workspace = true
|
||||
deno_npm = "=0.21.4"
|
||||
deno_npm.workspace = true
|
||||
deno_package_json.workspace = true
|
||||
deno_path_util.workspace = true
|
||||
deno_resolver.workspace = true
|
||||
deno_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] }
|
||||
deno_semver = "=0.5.7"
|
||||
deno_task_shell = "=0.17.0"
|
||||
deno_semver.workspace = true
|
||||
deno_task_shell = "=0.18.1"
|
||||
deno_terminal.workspace = true
|
||||
eszip = "=0.72.2"
|
||||
napi_sym.workspace = true
|
||||
libsui = "0.5.0"
|
||||
node_resolver.workspace = true
|
||||
|
||||
anstream = "0.6.14"
|
||||
async-trait.workspace = true
|
||||
base32.workspace = true
|
||||
base64.workspace = true
|
||||
bincode = "=1.3.3"
|
||||
bytes.workspace = true
|
||||
cache_control.workspace = true
|
||||
chrono = { workspace = true, features = ["now"] }
|
||||
clap = { version = "=4.4.17", features = ["env", "string"] }
|
||||
clap_complete = "=4.4.7"
|
||||
clap_complete_fig = "=4.4.2"
|
||||
color-print = "0.3.5"
|
||||
clap = { version = "=4.5.16", features = ["env", "string", "wrap_help", "error-context"] }
|
||||
clap_complete = "=4.5.24"
|
||||
clap_complete_fig = "=4.5.2"
|
||||
color-print.workspace = true
|
||||
console_static_text.workspace = true
|
||||
dashmap = "5.5.3"
|
||||
dashmap.workspace = true
|
||||
data-encoding.workspace = true
|
||||
dhat = { version = "0.3.3", optional = true }
|
||||
dissimilar = "=1.0.4"
|
||||
dotenvy = "0.15.7"
|
||||
dprint-plugin-json = "=0.19.3"
|
||||
dprint-plugin-jupyter = "=0.1.3"
|
||||
dprint-plugin-markdown = "=0.17.1"
|
||||
dprint-plugin-typescript = "=0.91.4"
|
||||
dprint-plugin-json = "=0.19.4"
|
||||
dprint-plugin-jupyter = "=0.1.5"
|
||||
dprint-plugin-markdown = "=0.17.8"
|
||||
dprint-plugin-typescript = "=0.93.2"
|
||||
env_logger = "=0.10.0"
|
||||
fancy-regex = "=0.10.0"
|
||||
faster-hex.workspace = true
|
||||
|
@ -113,15 +119,17 @@ http.workspace = true
|
|||
http-body.workspace = true
|
||||
http-body-util.workspace = true
|
||||
hyper-util.workspace = true
|
||||
import_map = { version = "=0.20.0", features = ["ext"] }
|
||||
import_map = { version = "=0.20.1", features = ["ext"] }
|
||||
indexmap.workspace = true
|
||||
jsonc-parser.workspace = true
|
||||
jupyter_runtime = { package = "runtimelib", version = "=0.14.0" }
|
||||
jsonc-parser = { workspace = true, features = ["cst", "serde"] }
|
||||
jupyter_runtime = { package = "runtimelib", version = "=0.19.0", features = ["tokio-runtime"] }
|
||||
lazy-regex.workspace = true
|
||||
libc.workspace = true
|
||||
libz-sys.workspace = true
|
||||
log = { workspace = true, features = ["serde"] }
|
||||
lsp-types.workspace = true
|
||||
malva = "=0.11.0"
|
||||
markup_fmt = "=0.16.0"
|
||||
memmem.workspace = true
|
||||
monch.workspace = true
|
||||
notify.workspace = true
|
||||
|
@ -131,6 +139,7 @@ p256.workspace = true
|
|||
pathdiff = "0.2.1"
|
||||
percent-encoding.workspace = true
|
||||
phf.workspace = true
|
||||
pretty_yaml = "=0.5.0"
|
||||
quick-junit = "^0.3.5"
|
||||
rand = { workspace = true, features = ["small_rng"] }
|
||||
regex.workspace = true
|
||||
|
@ -142,6 +151,9 @@ serde_repr.workspace = true
|
|||
sha2.workspace = true
|
||||
shell-escape = "=0.1.5"
|
||||
spki = { version = "0.7", features = ["pem"] }
|
||||
# NOTE(bartlomieju): using temporary fork for now, revert back to `sqlformat-rs` later
|
||||
sqlformat = { package = "deno_sqlformat", version = "0.3.2" }
|
||||
strsim = "0.11.1"
|
||||
tar.workspace = true
|
||||
tempfile.workspace = true
|
||||
text-size = "=1.1.0"
|
||||
|
@ -150,11 +162,14 @@ thiserror.workspace = true
|
|||
tokio.workspace = true
|
||||
tokio-util.workspace = true
|
||||
tower-lsp.workspace = true
|
||||
tracing = { version = "0.1", features = ["log", "default"] }
|
||||
twox-hash.workspace = true
|
||||
typed-arena = "=2.0.1"
|
||||
typed-arena = "=2.0.2"
|
||||
uuid = { workspace = true, features = ["serde"] }
|
||||
walkdir = "=2.3.2"
|
||||
which.workspace = true
|
||||
zeromq.workspace = true
|
||||
zip = { version = "2.1.6", default-features = false, features = ["deflate-flate2"] }
|
||||
zstd.workspace = true
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
|
@ -168,7 +183,6 @@ nix.workspace = true
|
|||
deno_bench_util.workspace = true
|
||||
pretty_assertions.workspace = true
|
||||
test_util.workspace = true
|
||||
walkdir = "=2.3.2"
|
||||
|
||||
[package.metadata.winres]
|
||||
# This section defines the metadata that appears in the deno.exe PE header.
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use deno_config::deno_json::TsConfigForEmit;
|
||||
use deno_core::serde_json;
|
||||
use deno_semver::jsr::JsrDepPackageReq;
|
||||
use deno_semver::jsr::JsrPackageReqReference;
|
||||
|
@ -69,7 +70,41 @@ pub fn deno_json_deps(
|
|||
let values = imports_values(config.json.imports.as_ref())
|
||||
.into_iter()
|
||||
.chain(scope_values(config.json.scopes.as_ref()));
|
||||
values_to_set(values)
|
||||
let mut set = values_to_set(values);
|
||||
|
||||
if let Some(serde_json::Value::Object(compiler_options)) =
|
||||
&config.json.compiler_options
|
||||
{
|
||||
// add jsxImportSource
|
||||
if let Some(serde_json::Value::String(value)) =
|
||||
compiler_options.get("jsxImportSource")
|
||||
{
|
||||
if let Some(dep_req) = value_to_dep_req(value) {
|
||||
set.insert(dep_req);
|
||||
}
|
||||
}
|
||||
// add jsxImportSourceTypes
|
||||
if let Some(serde_json::Value::String(value)) =
|
||||
compiler_options.get("jsxImportSourceTypes")
|
||||
{
|
||||
if let Some(dep_req) = value_to_dep_req(value) {
|
||||
set.insert(dep_req);
|
||||
}
|
||||
}
|
||||
// add the dependencies in the types array
|
||||
if let Some(serde_json::Value::Array(types)) = compiler_options.get("types")
|
||||
{
|
||||
for value in types {
|
||||
if let serde_json::Value::String(value) = value {
|
||||
if let Some(dep_req) = value_to_dep_req(value) {
|
||||
set.insert(dep_req);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
}
|
||||
|
||||
fn imports_values(value: Option<&serde_json::Value>) -> Vec<&String> {
|
||||
|
@ -97,11 +132,34 @@ fn values_to_set<'a>(
|
|||
) -> HashSet<JsrDepPackageReq> {
|
||||
let mut entries = HashSet::new();
|
||||
for value in values {
|
||||
if let Ok(req_ref) = JsrPackageReqReference::from_str(value) {
|
||||
entries.insert(JsrDepPackageReq::jsr(req_ref.into_inner().req));
|
||||
} else if let Ok(req_ref) = NpmPackageReqReference::from_str(value) {
|
||||
entries.insert(JsrDepPackageReq::npm(req_ref.into_inner().req));
|
||||
if let Some(dep_req) = value_to_dep_req(value) {
|
||||
entries.insert(dep_req);
|
||||
}
|
||||
}
|
||||
entries
|
||||
}
|
||||
|
||||
fn value_to_dep_req(value: &str) -> Option<JsrDepPackageReq> {
|
||||
if let Ok(req_ref) = JsrPackageReqReference::from_str(value) {
|
||||
Some(JsrDepPackageReq::jsr(req_ref.into_inner().req))
|
||||
} else if let Ok(req_ref) = NpmPackageReqReference::from_str(value) {
|
||||
Some(JsrDepPackageReq::npm(req_ref.into_inner().req))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_warn_tsconfig(ts_config: &TsConfigForEmit) {
|
||||
if let Some(ignored_options) = &ts_config.maybe_ignored_options {
|
||||
log::warn!("{}", ignored_options);
|
||||
}
|
||||
let serde_json::Value::Object(obj) = &ts_config.ts_config.0 else {
|
||||
return;
|
||||
};
|
||||
if obj.get("experimentalDecorators") == Some(&serde_json::Value::Bool(true)) {
|
||||
log::warn!(
|
||||
"{} experimentalDecorators compiler option is deprecated and may be removed at any time",
|
||||
deno_runtime::colors::yellow("Warning"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
7332
cli/args/flags.rs
7332
cli/args/flags.rs
File diff suppressed because it is too large
Load diff
|
@ -50,8 +50,8 @@ pub fn parse(paths: Vec<String>) -> clap::error::Result<Vec<String>> {
|
|||
out.push(format!("{}:{}", host, port.0));
|
||||
}
|
||||
} else {
|
||||
host_and_port.parse::<NetDescriptor>().map_err(|e| {
|
||||
clap::Error::raw(clap::error::ErrorKind::InvalidValue, format!("{e:?}"))
|
||||
NetDescriptor::parse(&host_and_port).map_err(|e| {
|
||||
clap::Error::raw(clap::error::ErrorKind::InvalidValue, e.to_string())
|
||||
})?;
|
||||
out.push(host_and_port)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::url::Url;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
|
||||
|
@ -17,7 +16,7 @@ pub async fn resolve_import_map_value_from_specifier(
|
|||
Ok(serde_json::from_str(&data_url_text)?)
|
||||
} else {
|
||||
let file = file_fetcher
|
||||
.fetch(specifier, &PermissionsContainer::allow_all())
|
||||
.fetch_bypass_permissions(specifier)
|
||||
.await?
|
||||
.into_text_decoded()?;
|
||||
Ok(serde_json::from_str(&file.source)?)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use deno_config::deno_json::ConfigFile;
|
||||
|
@ -12,6 +12,7 @@ use deno_core::parking_lot::MutexGuard;
|
|||
use deno_lockfile::WorkspaceMemberConfig;
|
||||
use deno_package_json::PackageJsonDepValue;
|
||||
use deno_runtime::deno_node::PackageJson;
|
||||
use deno_semver::jsr::JsrDepPackageReq;
|
||||
|
||||
use crate::cache;
|
||||
use crate::util::fs::atomic_write_file_with_retries;
|
||||
|
@ -23,11 +24,20 @@ use crate::args::InstallKind;
|
|||
|
||||
use deno_lockfile::Lockfile;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CliLockfileReadFromPathOptions {
|
||||
pub file_path: PathBuf,
|
||||
pub frozen: bool,
|
||||
/// Causes the lockfile to only be read from, but not written to.
|
||||
pub skip_write: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CliLockfile {
|
||||
lockfile: Mutex<Lockfile>,
|
||||
pub filename: PathBuf,
|
||||
pub frozen: bool,
|
||||
frozen: bool,
|
||||
skip_write: bool,
|
||||
}
|
||||
|
||||
pub struct Guard<'a, T> {
|
||||
|
@ -49,15 +59,6 @@ impl<'a, T> std::ops::DerefMut for Guard<'a, T> {
|
|||
}
|
||||
|
||||
impl CliLockfile {
|
||||
pub fn new(lockfile: Lockfile, frozen: bool) -> Self {
|
||||
let filename = lockfile.filename.clone();
|
||||
Self {
|
||||
lockfile: Mutex::new(lockfile),
|
||||
filename,
|
||||
frozen,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the inner deno_lockfile::Lockfile.
|
||||
pub fn lock(&self) -> Guard<Lockfile> {
|
||||
Guard {
|
||||
|
@ -77,6 +78,10 @@ impl CliLockfile {
|
|||
}
|
||||
|
||||
pub fn write_if_changed(&self) -> Result<(), AnyError> {
|
||||
if self.skip_write {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.error_if_changed()?;
|
||||
let mut lockfile = self.lockfile.lock();
|
||||
let Some(bytes) = lockfile.resolve_write_bytes() else {
|
||||
|
@ -98,7 +103,9 @@ impl CliLockfile {
|
|||
flags: &Flags,
|
||||
workspace: &Workspace,
|
||||
) -> Result<Option<CliLockfile>, AnyError> {
|
||||
fn pkg_json_deps(maybe_pkg_json: Option<&PackageJson>) -> BTreeSet<String> {
|
||||
fn pkg_json_deps(
|
||||
maybe_pkg_json: Option<&PackageJson>,
|
||||
) -> HashSet<JsrDepPackageReq> {
|
||||
let Some(pkg_json) = maybe_pkg_json else {
|
||||
return Default::default();
|
||||
};
|
||||
|
@ -107,23 +114,19 @@ impl CliLockfile {
|
|||
.values()
|
||||
.filter_map(|dep| dep.as_ref().ok())
|
||||
.filter_map(|dep| match dep {
|
||||
PackageJsonDepValue::Req(req) => Some(req),
|
||||
PackageJsonDepValue::Req(req) => {
|
||||
Some(JsrDepPackageReq::npm(req.clone()))
|
||||
}
|
||||
PackageJsonDepValue::Workspace(_) => None,
|
||||
})
|
||||
.map(|r| format!("npm:{}", r))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn deno_json_deps(
|
||||
maybe_deno_json: Option<&ConfigFile>,
|
||||
) -> BTreeSet<String> {
|
||||
) -> HashSet<JsrDepPackageReq> {
|
||||
maybe_deno_json
|
||||
.map(|c| {
|
||||
crate::args::deno_json::deno_json_deps(c)
|
||||
.into_iter()
|
||||
.map(|req| req.to_string())
|
||||
.collect()
|
||||
})
|
||||
.map(crate::args::deno_json::deno_json_deps)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
|
@ -139,7 +142,7 @@ impl CliLockfile {
|
|||
return Ok(None);
|
||||
}
|
||||
|
||||
let filename = match flags.lock {
|
||||
let file_path = match flags.lock {
|
||||
Some(ref lock) => PathBuf::from(lock),
|
||||
None => match workspace.resolve_lockfile_path()? {
|
||||
Some(path) => path,
|
||||
|
@ -147,22 +150,24 @@ impl CliLockfile {
|
|||
},
|
||||
};
|
||||
|
||||
let lockfile = if flags.lock_write {
|
||||
log::warn!(
|
||||
"{} \"--lock-write\" flag is deprecated and will be removed in Deno 2.",
|
||||
crate::colors::yellow("Warning")
|
||||
);
|
||||
CliLockfile::new(
|
||||
Lockfile::new_empty(filename, true),
|
||||
flags.frozen_lockfile,
|
||||
)
|
||||
} else {
|
||||
Self::read_from_path(filename, flags.frozen_lockfile)?
|
||||
};
|
||||
let root_folder = workspace.root_folder_configs();
|
||||
// CLI flag takes precedence over the config
|
||||
let frozen = flags.frozen_lockfile.unwrap_or_else(|| {
|
||||
root_folder
|
||||
.deno_json
|
||||
.as_ref()
|
||||
.and_then(|c| c.to_lock_config().ok().flatten().map(|c| c.frozen()))
|
||||
.unwrap_or(false)
|
||||
});
|
||||
|
||||
let lockfile = Self::read_from_path(CliLockfileReadFromPathOptions {
|
||||
file_path,
|
||||
frozen,
|
||||
skip_write: flags.internal.lockfile_skip_write,
|
||||
})?;
|
||||
|
||||
// initialize the lockfile with the workspace's configuration
|
||||
let root_url = workspace.root_dir();
|
||||
let root_folder = workspace.root_folder_configs();
|
||||
let config = deno_lockfile::WorkspaceConfig {
|
||||
root: WorkspaceMemberConfig {
|
||||
package_json_deps: pkg_json_deps(root_folder.pkg_json.as_deref()),
|
||||
|
@ -209,35 +214,39 @@ impl CliLockfile {
|
|||
|
||||
Ok(Some(lockfile))
|
||||
}
|
||||
|
||||
pub fn read_from_path(
|
||||
filename: PathBuf,
|
||||
frozen: bool,
|
||||
opts: CliLockfileReadFromPathOptions,
|
||||
) -> Result<CliLockfile, AnyError> {
|
||||
match std::fs::read_to_string(&filename) {
|
||||
Ok(text) => Ok(CliLockfile::new(
|
||||
Lockfile::with_lockfile_content(filename, &text, false)?,
|
||||
frozen,
|
||||
)),
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(
|
||||
CliLockfile::new(Lockfile::new_empty(filename, false), frozen),
|
||||
),
|
||||
Err(err) => Err(err).with_context(|| {
|
||||
format!("Failed reading lockfile '{}'", filename.display())
|
||||
}),
|
||||
}
|
||||
let lockfile = match std::fs::read_to_string(&opts.file_path) {
|
||||
Ok(text) => Lockfile::new(deno_lockfile::NewLockfileOptions {
|
||||
file_path: opts.file_path,
|
||||
content: &text,
|
||||
overwrite: false,
|
||||
})?,
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
Lockfile::new_empty(opts.file_path, false)
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(err).with_context(|| {
|
||||
format!("Failed reading lockfile '{}'", opts.file_path.display())
|
||||
});
|
||||
}
|
||||
};
|
||||
Ok(CliLockfile {
|
||||
filename: lockfile.filename.clone(),
|
||||
lockfile: Mutex::new(lockfile),
|
||||
frozen: opts.frozen,
|
||||
skip_write: opts.skip_write,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn error_if_changed(&self) -> Result<(), AnyError> {
|
||||
if !self.frozen {
|
||||
return Ok(());
|
||||
}
|
||||
let lockfile = self.lockfile.lock();
|
||||
if lockfile.has_content_changed {
|
||||
let suggested = if *super::DENO_FUTURE {
|
||||
"`deno cache --frozen=false`, `deno install --frozen=false`,"
|
||||
} else {
|
||||
"`deno cache --frozen=false`"
|
||||
};
|
||||
|
||||
let contents =
|
||||
std::fs::read_to_string(&lockfile.filename).unwrap_or_default();
|
||||
let new_contents = lockfile.as_json_string();
|
||||
|
@ -245,7 +254,7 @@ impl CliLockfile {
|
|||
// has an extra newline at the end
|
||||
let diff = diff.trim_end();
|
||||
Err(deno_core::anyhow::anyhow!(
|
||||
"The lockfile is out of date. Run {suggested} or rerun with `--frozen=false` to update it.\nchanges:\n{diff}"
|
||||
"The lockfile is out of date. Run `deno install --frozen=false`, or rerun with `--frozen=false` to update it.\nchanges:\n{diff}"
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
|
|
615
cli/args/mod.rs
615
cli/args/mod.rs
|
@ -7,8 +7,11 @@ mod import_map;
|
|||
mod lockfile;
|
||||
mod package_json;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_ast::SourceMapOption;
|
||||
use deno_config::deno_json::NodeModulesDirMode;
|
||||
use deno_config::workspace::CreateResolverOptions;
|
||||
use deno_config::workspace::FolderConfigs;
|
||||
use deno_config::workspace::PackageJsonDepResolution;
|
||||
use deno_config::workspace::VendorEnablement;
|
||||
use deno_config::workspace::Workspace;
|
||||
|
@ -18,21 +21,20 @@ use deno_config::workspace::WorkspaceDiscoverOptions;
|
|||
use deno_config::workspace::WorkspaceDiscoverStart;
|
||||
use deno_config::workspace::WorkspaceLintConfig;
|
||||
use deno_config::workspace::WorkspaceResolver;
|
||||
use deno_core::normalize_path;
|
||||
use deno_core::resolve_url_or_path;
|
||||
use deno_graph::GraphKind;
|
||||
use deno_npm::npm_rc::NpmRc;
|
||||
use deno_npm::npm_rc::ResolvedNpmRc;
|
||||
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use deno_path_util::normalize_path;
|
||||
use deno_runtime::ops::otel::OtelConfig;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use import_map::resolve_import_map_value_from_specifier;
|
||||
|
||||
pub use deno_config::deno_json::BenchConfig;
|
||||
pub use deno_config::deno_json::ConfigFile;
|
||||
pub use deno_config::deno_json::FmtOptionsConfig;
|
||||
pub use deno_config::deno_json::JsxImportSourceConfig;
|
||||
pub use deno_config::deno_json::LintRulesConfig;
|
||||
pub use deno_config::deno_json::ProseWrap;
|
||||
pub use deno_config::deno_json::TsConfig;
|
||||
|
@ -40,9 +42,12 @@ pub use deno_config::deno_json::TsConfigForEmit;
|
|||
pub use deno_config::deno_json::TsConfigType;
|
||||
pub use deno_config::deno_json::TsTypeLib;
|
||||
pub use deno_config::glob::FilePatterns;
|
||||
pub use deno_json::check_warn_tsconfig;
|
||||
pub use flags::*;
|
||||
pub use lockfile::CliLockfile;
|
||||
pub use package_json::PackageJsonInstallDepsProvider;
|
||||
pub use lockfile::CliLockfileReadFromPathOptions;
|
||||
pub use package_json::NpmInstallDepsProvider;
|
||||
pub use package_json::PackageJsonDepValueParseWithLocationError;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::anyhow::bail;
|
||||
|
@ -50,7 +55,6 @@ use deno_core::anyhow::Context;
|
|||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::url::Url;
|
||||
use deno_runtime::deno_node::PackageJson;
|
||||
use deno_runtime::deno_permissions::PermissionsOptions;
|
||||
use deno_runtime::deno_tls::deno_native_certs::load_native_certs;
|
||||
use deno_runtime::deno_tls::rustls;
|
||||
|
@ -63,10 +67,13 @@ use dotenvy::from_filename;
|
|||
use once_cell::sync::Lazy;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::io::BufReader;
|
||||
use std::io::Cursor;
|
||||
use std::io::Read;
|
||||
use std::io::Seek;
|
||||
use std::net::SocketAddr;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::path::Path;
|
||||
|
@ -75,6 +82,7 @@ use std::sync::Arc;
|
|||
use thiserror::Error;
|
||||
|
||||
use crate::cache;
|
||||
use crate::cache::DenoDirProvider;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists;
|
||||
use crate::version;
|
||||
|
@ -115,9 +123,6 @@ pub static DENO_DISABLE_PEDANTIC_NODE_WARNINGS: Lazy<bool> = Lazy::new(|| {
|
|||
.is_some()
|
||||
});
|
||||
|
||||
pub static DENO_FUTURE: Lazy<bool> =
|
||||
Lazy::new(|| std::env::var("DENO_FUTURE").ok().is_some());
|
||||
|
||||
pub fn jsr_url() -> &'static Url {
|
||||
static JSR_URL: Lazy<Url> = Lazy::new(|| {
|
||||
let env_var_name = "JSR_URL";
|
||||
|
@ -197,11 +202,14 @@ pub fn ts_config_to_transpile_and_emit_options(
|
|||
precompile_jsx_dynamic_props: None,
|
||||
transform_jsx,
|
||||
var_decl_imports: false,
|
||||
// todo(dsherret): support verbatim_module_syntax here properly
|
||||
verbatim_module_syntax: false,
|
||||
},
|
||||
deno_ast::EmitOptions {
|
||||
inline_sources: options.inline_sources,
|
||||
remove_comments: false,
|
||||
source_map,
|
||||
source_map_base: None,
|
||||
source_map_file: None,
|
||||
},
|
||||
))
|
||||
|
@ -278,9 +286,16 @@ impl BenchOptions {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
|
||||
pub struct UnstableFmtOptions {
|
||||
pub component: bool,
|
||||
pub sql: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FmtOptions {
|
||||
pub options: FmtOptionsConfig,
|
||||
pub unstable: UnstableFmtOptions,
|
||||
pub files: FilePatterns,
|
||||
}
|
||||
|
||||
|
@ -294,13 +309,22 @@ impl FmtOptions {
|
|||
pub fn new_with_base(base: PathBuf) -> Self {
|
||||
Self {
|
||||
options: FmtOptionsConfig::default(),
|
||||
unstable: Default::default(),
|
||||
files: FilePatterns::new_with_base(base),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve(fmt_config: FmtConfig, fmt_flags: &FmtFlags) -> Self {
|
||||
pub fn resolve(
|
||||
fmt_config: FmtConfig,
|
||||
unstable: UnstableFmtOptions,
|
||||
fmt_flags: &FmtFlags,
|
||||
) -> Self {
|
||||
Self {
|
||||
options: resolve_fmt_options(fmt_flags, fmt_config.options),
|
||||
unstable: UnstableFmtOptions {
|
||||
component: unstable.component || fmt_flags.unstable_component,
|
||||
sql: unstable.sql || fmt_flags.unstable_sql,
|
||||
},
|
||||
files: fmt_config.files,
|
||||
}
|
||||
}
|
||||
|
@ -348,19 +372,20 @@ pub struct WorkspaceTestOptions {
|
|||
pub doc: bool,
|
||||
pub no_run: bool,
|
||||
pub fail_fast: Option<NonZeroUsize>,
|
||||
pub allow_none: bool,
|
||||
pub permit_no_files: bool,
|
||||
pub filter: Option<String>,
|
||||
pub shuffle: Option<u64>,
|
||||
pub concurrent_jobs: NonZeroUsize,
|
||||
pub trace_leaks: bool,
|
||||
pub reporter: TestReporterConfig,
|
||||
pub junit_path: Option<String>,
|
||||
pub hide_stacktraces: bool,
|
||||
}
|
||||
|
||||
impl WorkspaceTestOptions {
|
||||
pub fn resolve(test_flags: &TestFlags) -> Self {
|
||||
Self {
|
||||
allow_none: test_flags.allow_none,
|
||||
permit_no_files: test_flags.permit_no_files,
|
||||
concurrent_jobs: test_flags
|
||||
.concurrent_jobs
|
||||
.unwrap_or_else(|| NonZeroUsize::new(1).unwrap()),
|
||||
|
@ -372,6 +397,7 @@ impl WorkspaceTestOptions {
|
|||
trace_leaks: test_flags.trace_leaks,
|
||||
reporter: test_flags.reporter,
|
||||
junit_path: test_flags.junit_path.clone(),
|
||||
hide_stacktraces: test_flags.hide_stacktraces,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -558,6 +584,7 @@ fn discover_npmrc(
|
|||
let resolved = npmrc
|
||||
.as_resolved(npm_registry_url())
|
||||
.context("Failed to resolve .npmrc options")?;
|
||||
log::debug!(".npmrc found at: '{}'", path.display());
|
||||
Ok(Arc::new(resolved))
|
||||
}
|
||||
|
||||
|
@ -656,9 +683,19 @@ pub fn get_root_cert_store(
|
|||
"system" => {
|
||||
let roots = load_native_certs().expect("could not load platform certs");
|
||||
for root in roots {
|
||||
root_cert_store
|
||||
.add(rustls::pki_types::CertificateDer::from(root.0))
|
||||
.expect("Failed to add platform cert to root cert store");
|
||||
if let Err(err) = root_cert_store
|
||||
.add(rustls::pki_types::CertificateDer::from(root.0.clone()))
|
||||
{
|
||||
log::error!(
|
||||
"{}",
|
||||
colors::yellow(&format!(
|
||||
"Unable to add system certificate to certificate store: {:?}",
|
||||
err
|
||||
))
|
||||
);
|
||||
let hex_encoded_root = faster_hex::hex_string(&root.0);
|
||||
log::error!("{}", colors::gray(&hex_encoded_root));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
@ -715,15 +752,33 @@ pub enum NpmProcessStateKind {
|
|||
Byonm,
|
||||
}
|
||||
|
||||
pub(crate) const NPM_RESOLUTION_STATE_ENV_VAR_NAME: &str =
|
||||
"DENO_DONT_USE_INTERNAL_NODE_COMPAT_STATE";
|
||||
|
||||
static NPM_PROCESS_STATE: Lazy<Option<NpmProcessState>> = Lazy::new(|| {
|
||||
let state = std::env::var(NPM_RESOLUTION_STATE_ENV_VAR_NAME).ok()?;
|
||||
let state: NpmProcessState = serde_json::from_str(&state).ok()?;
|
||||
// remove the environment variable so that sub processes
|
||||
// that are spawned do not also use this.
|
||||
std::env::remove_var(NPM_RESOLUTION_STATE_ENV_VAR_NAME);
|
||||
use deno_runtime::ops::process::NPM_RESOLUTION_STATE_FD_ENV_VAR_NAME;
|
||||
let fd = std::env::var(NPM_RESOLUTION_STATE_FD_ENV_VAR_NAME).ok()?;
|
||||
std::env::remove_var(NPM_RESOLUTION_STATE_FD_ENV_VAR_NAME);
|
||||
let fd = fd.parse::<usize>().ok()?;
|
||||
let mut file = {
|
||||
use deno_runtime::deno_io::FromRawIoHandle;
|
||||
unsafe { std::fs::File::from_raw_io_handle(fd as _) }
|
||||
};
|
||||
let mut buf = Vec::new();
|
||||
// seek to beginning. after the file is written the position will be inherited by this subprocess,
|
||||
// and also this file might have been read before
|
||||
file.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||
file
|
||||
.read_to_end(&mut buf)
|
||||
.inspect_err(|e| {
|
||||
log::error!("failed to read npm process state from fd {fd}: {e}");
|
||||
})
|
||||
.ok()?;
|
||||
let state: NpmProcessState = serde_json::from_slice(&buf)
|
||||
.inspect_err(|e| {
|
||||
log::error!(
|
||||
"failed to deserialize npm process state: {e} {}",
|
||||
String::from_utf8_lossy(&buf)
|
||||
)
|
||||
})
|
||||
.ok()?;
|
||||
Some(state)
|
||||
});
|
||||
|
||||
|
@ -742,13 +797,13 @@ pub struct CliOptions {
|
|||
// application need not concern itself with, so keep these private
|
||||
flags: Arc<Flags>,
|
||||
initial_cwd: PathBuf,
|
||||
main_module_cell: std::sync::OnceLock<Result<ModuleSpecifier, AnyError>>,
|
||||
maybe_node_modules_folder: Option<PathBuf>,
|
||||
npmrc: Arc<ResolvedNpmRc>,
|
||||
maybe_lockfile: Option<Arc<CliLockfile>>,
|
||||
overrides: CliOptionOverrides,
|
||||
pub start_dir: Arc<WorkspaceDirectory>,
|
||||
pub disable_deprecated_api_warning: bool,
|
||||
pub verbose_deprecated_api_warning: bool,
|
||||
pub deno_dir_provider: Arc<DenoDirProvider>,
|
||||
}
|
||||
|
||||
impl CliOptions {
|
||||
|
@ -770,32 +825,24 @@ impl CliOptions {
|
|||
};
|
||||
let msg =
|
||||
format!("DANGER: TLS certificate validation is disabled {}", domains);
|
||||
#[allow(clippy::print_stderr)]
|
||||
{
|
||||
// use eprintln instead of log::warn so this always gets shown
|
||||
eprintln!("{}", colors::yellow(msg));
|
||||
log::error!("{}", colors::yellow(msg));
|
||||
}
|
||||
}
|
||||
|
||||
let maybe_lockfile = maybe_lockfile.filter(|_| !force_global_cache);
|
||||
let root_folder = start_dir.workspace.root_folder_configs();
|
||||
let deno_dir_provider =
|
||||
Arc::new(DenoDirProvider::new(flags.internal.cache_path.clone()));
|
||||
let maybe_node_modules_folder = resolve_node_modules_folder(
|
||||
&initial_cwd,
|
||||
&flags,
|
||||
root_folder.deno_json.as_deref(),
|
||||
root_folder.pkg_json.as_deref(),
|
||||
&start_dir.workspace,
|
||||
&deno_dir_provider,
|
||||
)
|
||||
.with_context(|| "Resolving node_modules folder.")?;
|
||||
|
||||
load_env_variables_from_env_file(flags.env_file.as_ref());
|
||||
|
||||
let disable_deprecated_api_warning = flags.log_level
|
||||
== Some(log::Level::Error)
|
||||
|| std::env::var("DENO_NO_DEPRECATION_WARNINGS").ok().is_some();
|
||||
|
||||
let verbose_deprecated_api_warning =
|
||||
std::env::var("DENO_VERBOSE_WARNINGS").ok().is_some();
|
||||
|
||||
Ok(Self {
|
||||
flags,
|
||||
initial_cwd,
|
||||
|
@ -803,9 +850,9 @@ impl CliOptions {
|
|||
npmrc,
|
||||
maybe_node_modules_folder,
|
||||
overrides: Default::default(),
|
||||
main_module_cell: std::sync::OnceLock::new(),
|
||||
start_dir,
|
||||
disable_deprecated_api_warning,
|
||||
verbose_deprecated_api_warning,
|
||||
deno_dir_provider,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -823,12 +870,8 @@ impl CliOptions {
|
|||
} else {
|
||||
&[]
|
||||
};
|
||||
let config_parse_options = deno_config::deno_json::ConfigParseOptions {
|
||||
include_task_comments: matches!(
|
||||
flags.subcommand,
|
||||
DenoSubcommand::Task(..)
|
||||
),
|
||||
};
|
||||
let config_parse_options =
|
||||
deno_config::deno_json::ConfigParseOptions::default();
|
||||
let discover_pkg_json = flags.config_flag != ConfigFlag::Disabled
|
||||
&& !flags.no_npm
|
||||
&& !has_flag_env_var("DENO_NO_PACKAGE_JSON");
|
||||
|
@ -921,6 +964,9 @@ impl CliOptions {
|
|||
match self.sub_command() {
|
||||
DenoSubcommand::Cache(_) => GraphKind::All,
|
||||
DenoSubcommand::Check(_) => GraphKind::TypesOnly,
|
||||
DenoSubcommand::Install(InstallFlags {
|
||||
kind: InstallKind::Local(_),
|
||||
}) => GraphKind::All,
|
||||
_ => self.type_check_mode().as_graph_kind(),
|
||||
}
|
||||
}
|
||||
|
@ -1044,34 +1090,20 @@ impl CliOptions {
|
|||
None => None,
|
||||
}
|
||||
};
|
||||
Ok(
|
||||
self
|
||||
.start_dir
|
||||
.create_resolver(
|
||||
CreateResolverOptions {
|
||||
pkg_json_dep_resolution,
|
||||
specified_import_map: cli_arg_specified_import_map,
|
||||
},
|
||||
|specifier| {
|
||||
let specifier = specifier.clone();
|
||||
async move {
|
||||
let file = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await?
|
||||
.into_text_decoded()?;
|
||||
Ok(file.source.to_string())
|
||||
}
|
||||
},
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
Ok(self.workspace().create_resolver(
|
||||
CreateResolverOptions {
|
||||
pkg_json_dep_resolution,
|
||||
specified_import_map: cli_arg_specified_import_map,
|
||||
},
|
||||
|path| Ok(std::fs::read_to_string(path)?),
|
||||
)?)
|
||||
}
|
||||
|
||||
pub fn node_ipc_fd(&self) -> Option<i64> {
|
||||
let maybe_node_channel_fd = std::env::var("DENO_CHANNEL_FD").ok();
|
||||
let maybe_node_channel_fd = std::env::var("NODE_CHANNEL_FD").ok();
|
||||
if let Some(node_channel_fd) = maybe_node_channel_fd {
|
||||
// Remove so that child processes don't inherit this environment variable.
|
||||
std::env::remove_var("DENO_CHANNEL_FD");
|
||||
std::env::remove_var("NODE_CHANNEL_FD");
|
||||
node_channel_fd.parse::<i64>().ok()
|
||||
} else {
|
||||
None
|
||||
|
@ -1094,53 +1126,58 @@ impl CliOptions {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn env_file_name(&self) -> Option<&String> {
|
||||
pub fn otel_config(&self) -> Option<OtelConfig> {
|
||||
self.flags.otel_config()
|
||||
}
|
||||
|
||||
pub fn env_file_name(&self) -> Option<&Vec<String>> {
|
||||
self.flags.env_file.as_ref()
|
||||
}
|
||||
|
||||
pub fn enable_future_features(&self) -> bool {
|
||||
*DENO_FUTURE
|
||||
}
|
||||
|
||||
pub fn resolve_main_module(&self) -> Result<ModuleSpecifier, AnyError> {
|
||||
let main_module = match &self.flags.subcommand {
|
||||
DenoSubcommand::Bundle(bundle_flags) => {
|
||||
resolve_url_or_path(&bundle_flags.source_file, self.initial_cwd())?
|
||||
}
|
||||
DenoSubcommand::Compile(compile_flags) => {
|
||||
resolve_url_or_path(&compile_flags.source_file, self.initial_cwd())?
|
||||
}
|
||||
DenoSubcommand::Eval(_) => {
|
||||
resolve_url_or_path("./$deno$eval", self.initial_cwd())?
|
||||
}
|
||||
DenoSubcommand::Repl(_) => {
|
||||
resolve_url_or_path("./$deno$repl.ts", self.initial_cwd())?
|
||||
}
|
||||
DenoSubcommand::Run(run_flags) => {
|
||||
if run_flags.is_stdin() {
|
||||
std::env::current_dir()
|
||||
.context("Unable to get CWD")
|
||||
.and_then(|cwd| {
|
||||
resolve_url_or_path("./$deno$stdin.ts", &cwd)
|
||||
.map_err(AnyError::from)
|
||||
})?
|
||||
} else if run_flags.watch.is_some() {
|
||||
resolve_url_or_path(&run_flags.script, self.initial_cwd())?
|
||||
} else if NpmPackageReqReference::from_str(&run_flags.script).is_ok() {
|
||||
ModuleSpecifier::parse(&run_flags.script)?
|
||||
} else {
|
||||
resolve_url_or_path(&run_flags.script, self.initial_cwd())?
|
||||
}
|
||||
}
|
||||
DenoSubcommand::Serve(run_flags) => {
|
||||
resolve_url_or_path(&run_flags.script, self.initial_cwd())?
|
||||
}
|
||||
_ => {
|
||||
bail!("No main module.")
|
||||
}
|
||||
};
|
||||
|
||||
Ok(main_module)
|
||||
pub fn resolve_main_module(&self) -> Result<&ModuleSpecifier, AnyError> {
|
||||
self
|
||||
.main_module_cell
|
||||
.get_or_init(|| {
|
||||
Ok(match &self.flags.subcommand {
|
||||
DenoSubcommand::Compile(compile_flags) => {
|
||||
resolve_url_or_path(&compile_flags.source_file, self.initial_cwd())?
|
||||
}
|
||||
DenoSubcommand::Eval(_) => {
|
||||
resolve_url_or_path("./$deno$eval.mts", self.initial_cwd())?
|
||||
}
|
||||
DenoSubcommand::Repl(_) => {
|
||||
resolve_url_or_path("./$deno$repl.mts", self.initial_cwd())?
|
||||
}
|
||||
DenoSubcommand::Run(run_flags) => {
|
||||
if run_flags.is_stdin() {
|
||||
resolve_url_or_path("./$deno$stdin.mts", self.initial_cwd())?
|
||||
} else {
|
||||
let url =
|
||||
resolve_url_or_path(&run_flags.script, self.initial_cwd())?;
|
||||
if self.is_node_main()
|
||||
&& url.scheme() == "file"
|
||||
&& MediaType::from_specifier(&url) == MediaType::Unknown
|
||||
{
|
||||
try_resolve_node_binary_main_entrypoint(
|
||||
&run_flags.script,
|
||||
self.initial_cwd(),
|
||||
)?
|
||||
.unwrap_or(url)
|
||||
} else {
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
DenoSubcommand::Serve(run_flags) => {
|
||||
resolve_url_or_path(&run_flags.script, self.initial_cwd())?
|
||||
}
|
||||
_ => {
|
||||
bail!("No main module.")
|
||||
}
|
||||
})
|
||||
})
|
||||
.as_ref()
|
||||
.map_err(|err| deno_core::anyhow::anyhow!("{}", err))
|
||||
}
|
||||
|
||||
pub fn resolve_file_header_overrides(
|
||||
|
@ -1161,7 +1198,7 @@ impl CliOptions {
|
|||
(maybe_main_specifier, maybe_content_type)
|
||||
{
|
||||
HashMap::from([(
|
||||
main_specifier,
|
||||
main_specifier.clone(),
|
||||
HashMap::from([("content-type".to_string(), content_type.to_string())]),
|
||||
)])
|
||||
} else {
|
||||
|
@ -1186,15 +1223,10 @@ impl CliOptions {
|
|||
// This is triggered via a secret environment variable which is used
|
||||
// for functionality like child_process.fork. Users should NOT depend
|
||||
// on this functionality.
|
||||
pub fn is_npm_main(&self) -> bool {
|
||||
pub fn is_node_main(&self) -> bool {
|
||||
NPM_PROCESS_STATE.is_some()
|
||||
}
|
||||
|
||||
/// Overrides the import map specifier to use.
|
||||
pub fn set_import_map_specifier(&mut self, path: Option<ModuleSpecifier>) {
|
||||
self.overrides.import_map_specifier = Some(path);
|
||||
}
|
||||
|
||||
pub fn has_node_modules_dir(&self) -> bool {
|
||||
self.maybe_node_modules_folder.is_some()
|
||||
}
|
||||
|
@ -1203,25 +1235,13 @@ impl CliOptions {
|
|||
self.maybe_node_modules_folder.as_ref()
|
||||
}
|
||||
|
||||
pub fn with_node_modules_dir_path(&self, path: PathBuf) -> Self {
|
||||
Self {
|
||||
flags: self.flags.clone(),
|
||||
initial_cwd: self.initial_cwd.clone(),
|
||||
maybe_node_modules_folder: Some(path),
|
||||
npmrc: self.npmrc.clone(),
|
||||
maybe_lockfile: self.maybe_lockfile.clone(),
|
||||
start_dir: self.start_dir.clone(),
|
||||
overrides: self.overrides.clone(),
|
||||
disable_deprecated_api_warning: self.disable_deprecated_api_warning,
|
||||
verbose_deprecated_api_warning: self.verbose_deprecated_api_warning,
|
||||
pub fn node_modules_dir(
|
||||
&self,
|
||||
) -> Result<Option<NodeModulesDirMode>, AnyError> {
|
||||
if let Some(flag) = self.flags.node_modules_dir {
|
||||
return Ok(Some(flag));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node_modules_dir_enablement(&self) -> Option<bool> {
|
||||
self
|
||||
.flags
|
||||
.node_modules_dir
|
||||
.or_else(|| self.workspace().node_modules_dir())
|
||||
self.workspace().node_modules_dir().map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn vendor_dir_path(&self) -> Option<&PathBuf> {
|
||||
|
@ -1232,23 +1252,7 @@ impl CliOptions {
|
|||
&self,
|
||||
config_type: TsConfigType,
|
||||
) -> Result<TsConfigForEmit, AnyError> {
|
||||
let result = self.workspace().resolve_ts_config_for_emit(config_type);
|
||||
|
||||
match result {
|
||||
Ok(mut ts_config_for_emit) => {
|
||||
if matches!(self.flags.subcommand, DenoSubcommand::Bundle(..)) {
|
||||
// For backwards compatibility, force `experimentalDecorators` setting
|
||||
// to true.
|
||||
*ts_config_for_emit
|
||||
.ts_config
|
||||
.0
|
||||
.get_mut("experimentalDecorators")
|
||||
.unwrap() = serde_json::Value::Bool(true);
|
||||
}
|
||||
Ok(ts_config_for_emit)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
self.workspace().resolve_ts_config_for_emit(config_type)
|
||||
}
|
||||
|
||||
pub fn resolve_inspector_server(
|
||||
|
@ -1264,7 +1268,10 @@ impl CliOptions {
|
|||
return Ok(None);
|
||||
};
|
||||
|
||||
Ok(Some(InspectorServer::new(host, version::get_user_agent())?))
|
||||
Ok(Some(InspectorServer::new(
|
||||
host,
|
||||
version::DENO_VERSION_INFO.user_agent,
|
||||
)?))
|
||||
}
|
||||
|
||||
pub fn maybe_lockfile(&self) -> Option<&Arc<CliLockfile>> {
|
||||
|
@ -1301,14 +1308,23 @@ impl CliOptions {
|
|||
let member_configs = self
|
||||
.workspace()
|
||||
.resolve_fmt_config_for_members(&cli_arg_patterns)?;
|
||||
let unstable = self.resolve_config_unstable_fmt_options();
|
||||
let mut result = Vec::with_capacity(member_configs.len());
|
||||
for (ctx, config) in member_configs {
|
||||
let options = FmtOptions::resolve(config, fmt_flags);
|
||||
let options = FmtOptions::resolve(config, unstable.clone(), fmt_flags);
|
||||
result.push((ctx, options));
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn resolve_config_unstable_fmt_options(&self) -> UnstableFmtOptions {
|
||||
let workspace = self.workspace();
|
||||
UnstableFmtOptions {
|
||||
component: workspace.has_unstable("fmt-component"),
|
||||
sql: workspace.has_unstable("fmt-sql"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_workspace_lint_options(
|
||||
&self,
|
||||
lint_flags: &LintFlags,
|
||||
|
@ -1346,11 +1362,9 @@ impl CliOptions {
|
|||
)?;
|
||||
|
||||
Ok(deno_lint::linter::LintConfig {
|
||||
default_jsx_factory: transpile_options
|
||||
.jsx_automatic
|
||||
default_jsx_factory: (!transpile_options.jsx_automatic)
|
||||
.then(|| transpile_options.jsx_factory.clone()),
|
||||
default_jsx_fragment_factory: transpile_options
|
||||
.jsx_automatic
|
||||
default_jsx_fragment_factory: (!transpile_options.jsx_automatic)
|
||||
.then(|| transpile_options.jsx_fragment_factory.clone()),
|
||||
})
|
||||
}
|
||||
|
@ -1403,17 +1417,6 @@ impl CliOptions {
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn resolve_deno_graph_workspace_members(
|
||||
&self,
|
||||
) -> Result<Vec<deno_graph::WorkspaceMember>, AnyError> {
|
||||
self
|
||||
.workspace()
|
||||
.jsr_packages()
|
||||
.into_iter()
|
||||
.map(|pkg| config_to_deno_graph_workspace_member(&pkg.config_file))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
}
|
||||
|
||||
/// Vector of user script CLI arguments.
|
||||
pub fn argv(&self) -> &Vec<String> {
|
||||
&self.flags.argv
|
||||
|
@ -1465,6 +1468,12 @@ impl CliOptions {
|
|||
watch: Some(WatchFlagsWithPaths { hmr, .. }),
|
||||
..
|
||||
}) = &self.flags.subcommand
|
||||
{
|
||||
*hmr
|
||||
} else if let DenoSubcommand::Serve(ServeFlags {
|
||||
watch: Some(WatchFlagsWithPaths { hmr, .. }),
|
||||
..
|
||||
}) = &self.flags.subcommand
|
||||
{
|
||||
*hmr
|
||||
} else {
|
||||
|
@ -1502,10 +1511,6 @@ impl CliOptions {
|
|||
&self.flags.location
|
||||
}
|
||||
|
||||
pub fn maybe_custom_root(&self) -> &Option<PathBuf> {
|
||||
&self.flags.cache_path
|
||||
}
|
||||
|
||||
pub fn no_remote(&self) -> bool {
|
||||
self.flags.no_remote
|
||||
}
|
||||
|
@ -1518,8 +1523,39 @@ impl CliOptions {
|
|||
&self.flags.permissions
|
||||
}
|
||||
|
||||
pub fn permissions_options(&self) -> Result<PermissionsOptions, AnyError> {
|
||||
self.flags.permissions.to_options(Some(&self.initial_cwd))
|
||||
pub fn permissions_options(&self) -> PermissionsOptions {
|
||||
fn files_to_urls(files: &[String]) -> Vec<Cow<'_, Url>> {
|
||||
files
|
||||
.iter()
|
||||
.filter_map(|f| Url::parse(f).ok().map(Cow::Owned))
|
||||
.collect()
|
||||
}
|
||||
|
||||
// get a list of urls to imply for --allow-import
|
||||
let cli_arg_urls = self
|
||||
.resolve_main_module()
|
||||
.ok()
|
||||
.map(|url| vec![Cow::Borrowed(url)])
|
||||
.or_else(|| match &self.flags.subcommand {
|
||||
DenoSubcommand::Cache(cache_flags) => {
|
||||
Some(files_to_urls(&cache_flags.files))
|
||||
}
|
||||
DenoSubcommand::Check(check_flags) => {
|
||||
Some(files_to_urls(&check_flags.files))
|
||||
}
|
||||
DenoSubcommand::Install(InstallFlags {
|
||||
kind: InstallKind::Global(flags),
|
||||
}) => Url::parse(&flags.module_url)
|
||||
.ok()
|
||||
.map(|url| vec![Cow::Owned(url)]),
|
||||
DenoSubcommand::Doc(DocFlags {
|
||||
source_files: DocSourceFileFlag::Paths(paths),
|
||||
..
|
||||
}) => Some(files_to_urls(paths)),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or_default();
|
||||
self.flags.permissions.to_options(&cli_arg_urls)
|
||||
}
|
||||
|
||||
pub fn reload_flag(&self) -> bool {
|
||||
|
@ -1565,18 +1601,41 @@ impl CliOptions {
|
|||
&self.flags.unsafely_ignore_certificate_errors
|
||||
}
|
||||
|
||||
pub fn legacy_unstable_flag(&self) -> bool {
|
||||
self.flags.unstable_config.legacy_flag_enabled
|
||||
}
|
||||
|
||||
pub fn unstable_bare_node_builtins(&self) -> bool {
|
||||
self.flags.unstable_config.bare_node_builtins
|
||||
|| self.workspace().has_unstable("bare-node-builtins")
|
||||
}
|
||||
|
||||
pub fn detect_cjs(&self) -> bool {
|
||||
// only enabled when there's a package.json in order to not have a
|
||||
// perf penalty for non-npm Deno projects of searching for the closest
|
||||
// package.json beside each module
|
||||
self.workspace().package_jsons().next().is_some() || self.is_node_main()
|
||||
}
|
||||
|
||||
fn byonm_enabled(&self) -> bool {
|
||||
// check if enabled via unstable
|
||||
self.node_modules_dir().ok().flatten() == Some(NodeModulesDirMode::Manual)
|
||||
|| NPM_PROCESS_STATE
|
||||
.as_ref()
|
||||
.map(|s| matches!(s.kind, NpmProcessStateKind::Byonm))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn use_byonm(&self) -> bool {
|
||||
if self.enable_future_features()
|
||||
&& self.node_modules_dir_enablement().is_none()
|
||||
if matches!(
|
||||
self.sub_command(),
|
||||
DenoSubcommand::Install(_)
|
||||
| DenoSubcommand::Add(_)
|
||||
| DenoSubcommand::Remove(_)
|
||||
| DenoSubcommand::Init(_)
|
||||
| DenoSubcommand::Outdated(_)
|
||||
) {
|
||||
// For `deno install/add/remove/init` we want to force the managed resolver so it can set up `node_modules/` directory.
|
||||
return false;
|
||||
}
|
||||
if self.node_modules_dir().ok().flatten().is_none()
|
||||
&& self.maybe_node_modules_folder.is_some()
|
||||
&& self
|
||||
.workspace()
|
||||
.config_folders()
|
||||
|
@ -1586,13 +1645,7 @@ impl CliOptions {
|
|||
return true;
|
||||
}
|
||||
|
||||
// check if enabled via unstable
|
||||
self.flags.unstable_config.byonm
|
||||
|| NPM_PROCESS_STATE
|
||||
.as_ref()
|
||||
.map(|s| matches!(s.kind, NpmProcessStateKind::Byonm))
|
||||
.unwrap_or(false)
|
||||
|| self.workspace().has_unstable("byonm")
|
||||
self.byonm_enabled()
|
||||
}
|
||||
|
||||
pub fn unstable_sloppy_imports(&self) -> bool {
|
||||
|
@ -1614,31 +1667,18 @@ impl CliOptions {
|
|||
}
|
||||
});
|
||||
|
||||
if *DENO_FUTURE {
|
||||
let future_features = [
|
||||
deno_runtime::deno_ffi::UNSTABLE_FEATURE_NAME.to_string(),
|
||||
deno_runtime::deno_fs::UNSTABLE_FEATURE_NAME.to_string(),
|
||||
deno_runtime::deno_webgpu::UNSTABLE_FEATURE_NAME.to_string(),
|
||||
];
|
||||
future_features.iter().for_each(|future_feature| {
|
||||
if !from_config_file.contains(future_feature) {
|
||||
from_config_file.push(future_feature.to_string());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if !from_config_file.is_empty() {
|
||||
// collect unstable granular flags
|
||||
let mut all_valid_unstable_flags: Vec<&str> =
|
||||
crate::UNSTABLE_GRANULAR_FLAGS
|
||||
.iter()
|
||||
.map(|granular_flag| granular_flag.0)
|
||||
.collect();
|
||||
|
||||
let mut another_unstable_flags =
|
||||
Vec::from(["sloppy-imports", "byonm", "bare-node-builtins"]);
|
||||
// add more unstable flags to the same vector holding granular flags
|
||||
all_valid_unstable_flags.append(&mut another_unstable_flags);
|
||||
let all_valid_unstable_flags: Vec<&str> = crate::UNSTABLE_GRANULAR_FLAGS
|
||||
.iter()
|
||||
.map(|granular_flag| granular_flag.name)
|
||||
.chain([
|
||||
"sloppy-imports",
|
||||
"byonm",
|
||||
"bare-node-builtins",
|
||||
"fmt-component",
|
||||
"fmt-sql",
|
||||
])
|
||||
.collect();
|
||||
|
||||
// check and warn if the unstable flag of config file isn't supported, by
|
||||
// iterating through the vector holding the unstable flags
|
||||
|
@ -1671,6 +1711,10 @@ impl CliOptions {
|
|||
if let DenoSubcommand::Run(RunFlags {
|
||||
watch: Some(WatchFlagsWithPaths { paths, .. }),
|
||||
..
|
||||
})
|
||||
| DenoSubcommand::Serve(ServeFlags {
|
||||
watch: Some(WatchFlagsWithPaths { paths, .. }),
|
||||
..
|
||||
}) = &self.flags.subcommand
|
||||
{
|
||||
full_paths.extend(paths.iter().map(|path| self.initial_cwd.join(path)));
|
||||
|
@ -1701,14 +1745,14 @@ impl CliOptions {
|
|||
pub fn lifecycle_scripts_config(&self) -> LifecycleScriptsConfig {
|
||||
LifecycleScriptsConfig {
|
||||
allowed: self.flags.allow_scripts.clone(),
|
||||
initial_cwd: if matches!(
|
||||
self.flags.allow_scripts,
|
||||
PackagesAllowedScripts::None
|
||||
) {
|
||||
None
|
||||
} else {
|
||||
Some(self.initial_cwd.clone())
|
||||
},
|
||||
initial_cwd: self.initial_cwd.clone(),
|
||||
root_dir: self.workspace().root_dir_path(),
|
||||
explicit_install: matches!(
|
||||
self.sub_command(),
|
||||
DenoSubcommand::Install(_)
|
||||
| DenoSubcommand::Cache(_)
|
||||
| DenoSubcommand::Add(_)
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1717,34 +1761,89 @@ impl CliOptions {
|
|||
fn resolve_node_modules_folder(
|
||||
cwd: &Path,
|
||||
flags: &Flags,
|
||||
maybe_config_file: Option<&ConfigFile>,
|
||||
maybe_package_json: Option<&PackageJson>,
|
||||
workspace: &Workspace,
|
||||
deno_dir_provider: &Arc<DenoDirProvider>,
|
||||
) -> Result<Option<PathBuf>, AnyError> {
|
||||
let use_node_modules_dir = flags
|
||||
.node_modules_dir
|
||||
.or_else(|| maybe_config_file.and_then(|c| c.json.node_modules_dir))
|
||||
.or(flags.vendor)
|
||||
.or_else(|| maybe_config_file.and_then(|c| c.json.vendor));
|
||||
fn resolve_from_root(root_folder: &FolderConfigs, cwd: &Path) -> PathBuf {
|
||||
root_folder
|
||||
.deno_json
|
||||
.as_ref()
|
||||
.map(|c| Cow::Owned(c.dir_path()))
|
||||
.or_else(|| {
|
||||
root_folder
|
||||
.pkg_json
|
||||
.as_ref()
|
||||
.map(|c| Cow::Borrowed(c.dir_path()))
|
||||
})
|
||||
.unwrap_or(Cow::Borrowed(cwd))
|
||||
.join("node_modules")
|
||||
}
|
||||
|
||||
let root_folder = workspace.root_folder_configs();
|
||||
let use_node_modules_dir = if let Some(mode) = flags.node_modules_dir {
|
||||
Some(mode.uses_node_modules_dir())
|
||||
} else {
|
||||
workspace
|
||||
.node_modules_dir()?
|
||||
.map(|m| m.uses_node_modules_dir())
|
||||
.or(flags.vendor)
|
||||
.or_else(|| root_folder.deno_json.as_ref().and_then(|c| c.json.vendor))
|
||||
};
|
||||
let path = if use_node_modules_dir == Some(false) {
|
||||
return Ok(None);
|
||||
} else if let Some(state) = &*NPM_PROCESS_STATE {
|
||||
return Ok(state.local_node_modules_path.as_ref().map(PathBuf::from));
|
||||
} else if let Some(package_json_path) = maybe_package_json.map(|c| &c.path) {
|
||||
// always auto-discover the local_node_modules_folder when a package.json exists
|
||||
package_json_path.parent().unwrap().join("node_modules")
|
||||
} else if root_folder.pkg_json.is_some() {
|
||||
let node_modules_dir = resolve_from_root(root_folder, cwd);
|
||||
if let Ok(deno_dir) = deno_dir_provider.get_or_create() {
|
||||
// `deno_dir.root` can be symlink in macOS
|
||||
if let Ok(root) = canonicalize_path_maybe_not_exists(&deno_dir.root) {
|
||||
if node_modules_dir.starts_with(root) {
|
||||
// if the package.json is in deno_dir, then do not use node_modules
|
||||
// next to it as local node_modules dir
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
node_modules_dir
|
||||
} else if use_node_modules_dir.is_none() {
|
||||
return Ok(None);
|
||||
} else if let Some(config_path) = maybe_config_file
|
||||
.as_ref()
|
||||
.and_then(|c| c.specifier.to_file_path().ok())
|
||||
{
|
||||
config_path.parent().unwrap().join("node_modules")
|
||||
} else {
|
||||
cwd.join("node_modules")
|
||||
resolve_from_root(root_folder, cwd)
|
||||
};
|
||||
Ok(Some(canonicalize_path_maybe_not_exists(&path)?))
|
||||
}
|
||||
|
||||
fn try_resolve_node_binary_main_entrypoint(
|
||||
specifier: &str,
|
||||
initial_cwd: &Path,
|
||||
) -> Result<Option<Url>, AnyError> {
|
||||
// node allows running files at paths without a `.js` extension
|
||||
// or at directories with an index.js file
|
||||
let path = deno_core::normalize_path(initial_cwd.join(specifier));
|
||||
if path.is_dir() {
|
||||
let index_file = path.join("index.js");
|
||||
Ok(if index_file.is_file() {
|
||||
Some(deno_path_util::url_from_file_path(&index_file)?)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
} else {
|
||||
let path = path.with_extension(
|
||||
path
|
||||
.extension()
|
||||
.and_then(|s| s.to_str())
|
||||
.map(|s| format!("{}.js", s))
|
||||
.unwrap_or("js".to_string()),
|
||||
);
|
||||
if path.is_file() {
|
||||
Ok(Some(deno_path_util::url_from_file_path(&path)?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_import_map_specifier(
|
||||
maybe_import_map_path: Option<&str>,
|
||||
maybe_config_file: Option<&ConfigFile>,
|
||||
|
@ -1815,6 +1914,10 @@ pub fn resolve_no_prompt(flags: &PermissionFlags) -> bool {
|
|||
flags.no_prompt || has_flag_env_var("DENO_NO_PROMPT")
|
||||
}
|
||||
|
||||
pub fn has_trace_permissions_enabled() -> bool {
|
||||
has_flag_env_var("DENO_TRACE_PERMISSIONS")
|
||||
}
|
||||
|
||||
pub fn has_flag_env_var(name: &str) -> bool {
|
||||
let value = env::var(name);
|
||||
matches!(value.as_ref().map(|s| s.as_str()), Ok("1"))
|
||||
|
@ -1830,36 +1933,38 @@ pub fn npm_pkg_req_ref_to_binary_command(
|
|||
pub fn config_to_deno_graph_workspace_member(
|
||||
config: &ConfigFile,
|
||||
) -> Result<deno_graph::WorkspaceMember, AnyError> {
|
||||
let nv = deno_semver::package::PackageNv {
|
||||
name: match &config.json.name {
|
||||
Some(name) => name.clone(),
|
||||
None => bail!("Missing 'name' field in config file."),
|
||||
},
|
||||
version: match &config.json.version {
|
||||
Some(name) => deno_semver::Version::parse_standard(name)?,
|
||||
None => bail!("Missing 'version' field in config file."),
|
||||
},
|
||||
let name = match &config.json.name {
|
||||
Some(name) => name.clone(),
|
||||
None => bail!("Missing 'name' field in config file."),
|
||||
};
|
||||
let version = match &config.json.version {
|
||||
Some(name) => Some(deno_semver::Version::parse_standard(name)?),
|
||||
None => None,
|
||||
};
|
||||
Ok(deno_graph::WorkspaceMember {
|
||||
base: config.specifier.join("./").unwrap(),
|
||||
nv,
|
||||
name,
|
||||
version,
|
||||
exports: config.to_exports_config()?.into_map(),
|
||||
})
|
||||
}
|
||||
|
||||
fn load_env_variables_from_env_file(filename: Option<&String>) {
|
||||
let Some(env_file_name) = filename else {
|
||||
fn load_env_variables_from_env_file(filename: Option<&Vec<String>>) {
|
||||
let Some(env_file_names) = filename else {
|
||||
return;
|
||||
};
|
||||
match from_filename(env_file_name) {
|
||||
Ok(_) => (),
|
||||
Err(error) => {
|
||||
match error {
|
||||
|
||||
for env_file_name in env_file_names.iter().rev() {
|
||||
match from_filename(env_file_name) {
|
||||
Ok(_) => (),
|
||||
Err(error) => {
|
||||
match error {
|
||||
dotenvy::Error::LineParse(line, index)=> log::info!("{} Parsing failed within the specified environment file: {} at index: {} of the value: {}",colors::yellow("Warning"), env_file_name, index, line),
|
||||
dotenvy::Error::Io(_)=> log::info!("{} The `--env` flag was used, but the environment file specified '{}' was not found.",colors::yellow("Warning"),env_file_name),
|
||||
dotenvy::Error::Io(_)=> log::info!("{} The `--env-file` flag was used, but the environment file specified '{}' was not found.",colors::yellow("Warning"),env_file_name),
|
||||
dotenvy::Error::EnvVar(_)=> log::info!("{} One or more of the environment variables isn't present or not unicode within the specified environment file: {}",colors::yellow("Warning"),env_file_name),
|
||||
_ => log::info!("{} Unknown failure occurred with the specified environment file: {}", colors::yellow("Warning"), env_file_name),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,102 +4,172 @@ use std::path::PathBuf;
|
|||
use std::sync::Arc;
|
||||
|
||||
use deno_config::workspace::Workspace;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::url::Url;
|
||||
use deno_package_json::PackageJsonDepValue;
|
||||
use deno_package_json::PackageJsonDepValueParseError;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use deno_semver::package::PackageReq;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InstallNpmRemotePkg {
|
||||
pub alias: String,
|
||||
// todo(24419): use this when setting up the node_modules dir
|
||||
#[allow(dead_code)]
|
||||
pub alias: Option<String>,
|
||||
pub base_dir: PathBuf,
|
||||
pub req: PackageReq,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InstallNpmWorkspacePkg {
|
||||
pub alias: String,
|
||||
// todo(24419): use this when setting up the node_modules dir
|
||||
#[allow(dead_code)]
|
||||
pub base_dir: PathBuf,
|
||||
pub alias: Option<String>,
|
||||
pub target_dir: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct PackageJsonInstallDepsProvider {
|
||||
remote_pkgs: Vec<InstallNpmRemotePkg>,
|
||||
workspace_pkgs: Vec<InstallNpmWorkspacePkg>,
|
||||
#[derive(Debug, Error, Clone)]
|
||||
#[error("Failed to install '{}'\n at {}", alias, location)]
|
||||
pub struct PackageJsonDepValueParseWithLocationError {
|
||||
pub location: Url,
|
||||
pub alias: String,
|
||||
#[source]
|
||||
pub source: PackageJsonDepValueParseError,
|
||||
}
|
||||
|
||||
impl PackageJsonInstallDepsProvider {
|
||||
#[derive(Debug, Default)]
|
||||
pub struct NpmInstallDepsProvider {
|
||||
remote_pkgs: Vec<InstallNpmRemotePkg>,
|
||||
workspace_pkgs: Vec<InstallNpmWorkspacePkg>,
|
||||
pkg_json_dep_errors: Vec<PackageJsonDepValueParseWithLocationError>,
|
||||
}
|
||||
|
||||
impl NpmInstallDepsProvider {
|
||||
pub fn empty() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn from_workspace(workspace: &Arc<Workspace>) -> Self {
|
||||
// todo(dsherret): estimate capacity?
|
||||
let mut workspace_pkgs = Vec::new();
|
||||
let mut remote_pkgs = Vec::new();
|
||||
let mut pkg_json_dep_errors = Vec::new();
|
||||
let workspace_npm_pkgs = workspace.npm_packages();
|
||||
for pkg_json in workspace.package_jsons() {
|
||||
let deps = pkg_json.resolve_local_package_json_deps();
|
||||
let mut pkg_pkgs = Vec::with_capacity(deps.len());
|
||||
for (alias, dep) in deps {
|
||||
let Ok(dep) = dep else {
|
||||
continue;
|
||||
};
|
||||
match dep {
|
||||
PackageJsonDepValue::Req(pkg_req) => {
|
||||
let workspace_pkg = workspace_npm_pkgs.iter().find(|pkg| {
|
||||
pkg.matches_req(&pkg_req)
|
||||
// do not resolve to the current package
|
||||
&& pkg.pkg_json.path != pkg_json.path
|
||||
});
|
||||
|
||||
for (_, folder) in workspace.config_folders() {
|
||||
// deal with the deno.json first because it takes precedence during resolution
|
||||
if let Some(deno_json) = &folder.deno_json {
|
||||
// don't bother with externally referenced import maps as users
|
||||
// should inline their import map to get this behaviour
|
||||
if let Some(serde_json::Value::Object(obj)) = &deno_json.json.imports {
|
||||
let mut pkg_pkgs = Vec::with_capacity(obj.len());
|
||||
for (_alias, value) in obj {
|
||||
let serde_json::Value::String(specifier) = value else {
|
||||
continue;
|
||||
};
|
||||
let Ok(npm_req_ref) = NpmPackageReqReference::from_str(specifier)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let pkg_req = npm_req_ref.into_inner().req;
|
||||
let workspace_pkg = workspace_npm_pkgs
|
||||
.iter()
|
||||
.find(|pkg| pkg.matches_req(&pkg_req));
|
||||
|
||||
if let Some(pkg) = workspace_pkg {
|
||||
workspace_pkgs.push(InstallNpmWorkspacePkg {
|
||||
alias,
|
||||
base_dir: pkg_json.dir_path().to_path_buf(),
|
||||
alias: None,
|
||||
target_dir: pkg.pkg_json.dir_path().to_path_buf(),
|
||||
});
|
||||
} else {
|
||||
pkg_pkgs.push(InstallNpmRemotePkg {
|
||||
alias,
|
||||
base_dir: pkg_json.dir_path().to_path_buf(),
|
||||
alias: None,
|
||||
base_dir: deno_json.dir_path(),
|
||||
req: pkg_req,
|
||||
});
|
||||
}
|
||||
}
|
||||
PackageJsonDepValue::Workspace(version_req) => {
|
||||
if let Some(pkg) = workspace_npm_pkgs.iter().find(|pkg| {
|
||||
pkg.matches_name_and_version_req(&alias, &version_req)
|
||||
}) {
|
||||
workspace_pkgs.push(InstallNpmWorkspacePkg {
|
||||
alias,
|
||||
base_dir: pkg_json.dir_path().to_path_buf(),
|
||||
target_dir: pkg.pkg_json.dir_path().to_path_buf(),
|
||||
|
||||
// sort within each package (more like npm resolution)
|
||||
pkg_pkgs.sort_by(|a, b| a.req.cmp(&b.req));
|
||||
remote_pkgs.extend(pkg_pkgs);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(pkg_json) = &folder.pkg_json {
|
||||
let deps = pkg_json.resolve_local_package_json_deps();
|
||||
let mut pkg_pkgs = Vec::with_capacity(deps.len());
|
||||
for (alias, dep) in deps {
|
||||
let dep = match dep {
|
||||
Ok(dep) => dep,
|
||||
Err(err) => {
|
||||
pkg_json_dep_errors.push(
|
||||
PackageJsonDepValueParseWithLocationError {
|
||||
location: pkg_json.specifier(),
|
||||
alias,
|
||||
source: err,
|
||||
},
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
match dep {
|
||||
PackageJsonDepValue::Req(pkg_req) => {
|
||||
let workspace_pkg = workspace_npm_pkgs.iter().find(|pkg| {
|
||||
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),
|
||||
target_dir: pkg.pkg_json.dir_path().to_path_buf(),
|
||||
});
|
||||
} else {
|
||||
pkg_pkgs.push(InstallNpmRemotePkg {
|
||||
alias: Some(alias),
|
||||
base_dir: pkg_json.dir_path().to_path_buf(),
|
||||
req: pkg_req,
|
||||
});
|
||||
}
|
||||
}
|
||||
PackageJsonDepValue::Workspace(version_req) => {
|
||||
if let Some(pkg) = workspace_npm_pkgs.iter().find(|pkg| {
|
||||
pkg.matches_name_and_version_req(&alias, &version_req)
|
||||
}) {
|
||||
workspace_pkgs.push(InstallNpmWorkspacePkg {
|
||||
alias: Some(alias),
|
||||
target_dir: pkg.pkg_json.dir_path().to_path_buf(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// sort within each package
|
||||
pkg_pkgs.sort_by(|a, b| a.alias.cmp(&b.alias));
|
||||
|
||||
remote_pkgs.extend(pkg_pkgs);
|
||||
// sort within each package as npm does
|
||||
pkg_pkgs.sort_by(|a, b| a.alias.cmp(&b.alias));
|
||||
remote_pkgs.extend(pkg_pkgs);
|
||||
}
|
||||
}
|
||||
|
||||
remote_pkgs.shrink_to_fit();
|
||||
workspace_pkgs.shrink_to_fit();
|
||||
Self {
|
||||
remote_pkgs,
|
||||
workspace_pkgs,
|
||||
pkg_json_dep_errors,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remote_pkgs(&self) -> &Vec<InstallNpmRemotePkg> {
|
||||
pub fn remote_pkgs(&self) -> &[InstallNpmRemotePkg] {
|
||||
&self.remote_pkgs
|
||||
}
|
||||
|
||||
pub fn workspace_pkgs(&self) -> &Vec<InstallNpmWorkspacePkg> {
|
||||
pub fn workspace_pkgs(&self) -> &[InstallNpmWorkspacePkg] {
|
||||
&self.workspace_pkgs
|
||||
}
|
||||
|
||||
pub fn pkg_json_dep_errors(
|
||||
&self,
|
||||
) -> &[PackageJsonDepValueParseWithLocationError] {
|
||||
&self.pkg_json_dep_errors
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,19 +123,19 @@ impl AuthTokens {
|
|||
pub fn new(maybe_tokens_str: Option<String>) -> Self {
|
||||
let mut tokens = Vec::new();
|
||||
if let Some(tokens_str) = maybe_tokens_str {
|
||||
for token_str in tokens_str.split(';') {
|
||||
for token_str in tokens_str.trim().split(';') {
|
||||
if token_str.contains('@') {
|
||||
let pair: Vec<&str> = token_str.rsplitn(2, '@').collect();
|
||||
let token = pair[1];
|
||||
let host = AuthDomain::from(pair[0]);
|
||||
let mut iter = token_str.rsplitn(2, '@');
|
||||
let host = AuthDomain::from(iter.next().unwrap());
|
||||
let token = iter.next().unwrap();
|
||||
if token.contains(':') {
|
||||
let pair: Vec<&str> = token.rsplitn(2, ':').collect();
|
||||
let username = pair[1].to_string();
|
||||
let password = pair[0].to_string();
|
||||
let mut iter = token.rsplitn(2, ':');
|
||||
let password = iter.next().unwrap().to_owned();
|
||||
let username = iter.next().unwrap().to_owned();
|
||||
tokens.push(AuthToken {
|
||||
host,
|
||||
token: AuthTokenData::Basic { username, password },
|
||||
})
|
||||
});
|
||||
} else {
|
||||
tokens.push(AuthToken {
|
||||
host,
|
||||
|
@ -211,6 +211,40 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auth_tokens_space() {
|
||||
let auth_tokens = AuthTokens::new(Some(
|
||||
" abc123@deno.land;def456@example.com\t".to_string(),
|
||||
));
|
||||
let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap();
|
||||
assert_eq!(
|
||||
auth_tokens.get(&fixture).unwrap().to_string(),
|
||||
"Bearer abc123".to_string()
|
||||
);
|
||||
let fixture = resolve_url("http://example.com/a/file.ts").unwrap();
|
||||
assert_eq!(
|
||||
auth_tokens.get(&fixture).unwrap().to_string(),
|
||||
"Bearer def456".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auth_tokens_newline() {
|
||||
let auth_tokens = AuthTokens::new(Some(
|
||||
"\nabc123@deno.land;def456@example.com\n".to_string(),
|
||||
));
|
||||
let fixture = resolve_url("https://deno.land/x/mod.ts").unwrap();
|
||||
assert_eq!(
|
||||
auth_tokens.get(&fixture).unwrap().to_string(),
|
||||
"Bearer abc123".to_string()
|
||||
);
|
||||
let fixture = resolve_url("http://example.com/a/file.ts").unwrap();
|
||||
assert_eq!(
|
||||
auth_tokens.get(&fixture).unwrap().to_string(),
|
||||
"Bearer def456".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auth_tokens_port() {
|
||||
let auth_tokens =
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// deno-lint-ignore-file no-console
|
||||
|
||||
const count = 100000;
|
||||
for (let i = 0; i < count; i++) console.log("Hello World");
|
||||
|
|
|
@ -46,8 +46,7 @@ Deno.bench("b64_rt_short", { n: 1e6 }, () => {
|
|||
const buf = new Uint8Array(100);
|
||||
const file = Deno.openSync("/dev/zero");
|
||||
Deno.bench("read_zero", { n: 5e5 }, () => {
|
||||
// deno-lint-ignore no-deprecated-deno-api
|
||||
Deno.readSync(file.rid, buf);
|
||||
file.readSync(buf);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// deno-lint-ignore-file no-console no-process-globals
|
||||
|
||||
let [total, count] = typeof Deno !== "undefined"
|
||||
? Deno.args
|
||||
: [process.argv[2], process.argv[3]];
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
let total = 5;
|
||||
let current = "";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
/** @jsx h */
|
||||
import results from "./deno.json" assert { type: "json" };
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// deno-lint-ignore-file no-console no-process-globals
|
||||
|
||||
let [total, count] = typeof Deno !== "undefined"
|
||||
? Deno.args
|
||||
: [process.argv[2], process.argv[3]];
|
||||
|
|
|
@ -1,167 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::net::TcpStream;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::sync::atomic::AtomicU16;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
|
||||
use super::Result;
|
||||
|
||||
pub use test_util::parse_wrk_output;
|
||||
pub use test_util::WrkOutput as HttpBenchmarkResult;
|
||||
// Some of the benchmarks in this file have been renamed. In case the history
|
||||
// somehow gets messed up:
|
||||
// "node_http" was once called "node"
|
||||
// "deno_tcp" was once called "deno"
|
||||
// "deno_http" was once called "deno_net_http"
|
||||
|
||||
const DURATION: &str = "10s";
|
||||
|
||||
pub fn benchmark(
|
||||
target_path: &Path,
|
||||
) -> Result<HashMap<String, HttpBenchmarkResult>> {
|
||||
let deno_exe = test_util::deno_exe_path();
|
||||
let deno_exe = deno_exe.to_string();
|
||||
|
||||
let hyper_hello_exe = target_path.join("test_server");
|
||||
let hyper_hello_exe = hyper_hello_exe.to_str().unwrap();
|
||||
|
||||
let mut res = HashMap::new();
|
||||
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
let http_dir = manifest_dir.join("bench").join("http");
|
||||
for entry in std::fs::read_dir(&http_dir)? {
|
||||
let entry = entry?;
|
||||
let pathbuf = entry.path();
|
||||
let path = pathbuf.to_str().unwrap();
|
||||
if path.ends_with(".lua") {
|
||||
continue;
|
||||
}
|
||||
let file_stem = pathbuf.file_stem().unwrap().to_str().unwrap();
|
||||
|
||||
let lua_script = http_dir.join(format!("{file_stem}.lua"));
|
||||
let mut maybe_lua = None;
|
||||
if lua_script.exists() {
|
||||
maybe_lua = Some(lua_script.to_str().unwrap());
|
||||
}
|
||||
|
||||
let port = get_port();
|
||||
// deno run -A --unstable <path> <addr>
|
||||
res.insert(
|
||||
file_stem.to_string(),
|
||||
run(
|
||||
&[
|
||||
deno_exe.as_str(),
|
||||
"run",
|
||||
"--allow-all",
|
||||
"--unstable",
|
||||
"--enable-testing-features-do-not-use",
|
||||
path,
|
||||
&server_addr(port),
|
||||
],
|
||||
port,
|
||||
None,
|
||||
None,
|
||||
maybe_lua,
|
||||
)?,
|
||||
);
|
||||
}
|
||||
|
||||
res.insert("hyper".to_string(), hyper_http(hyper_hello_exe)?);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn run(
|
||||
server_cmd: &[&str],
|
||||
port: u16,
|
||||
env: Option<Vec<(String, String)>>,
|
||||
origin_cmd: Option<&[&str]>,
|
||||
lua_script: Option<&str>,
|
||||
) -> Result<HttpBenchmarkResult> {
|
||||
// Wait for port 4544 to become available.
|
||||
// TODO Need to use SO_REUSEPORT with tokio::net::TcpListener.
|
||||
std::thread::sleep(Duration::from_secs(5));
|
||||
|
||||
let mut origin = None;
|
||||
if let Some(cmd) = origin_cmd {
|
||||
let mut com = Command::new(cmd[0]);
|
||||
com.args(&cmd[1..]);
|
||||
if let Some(env) = env.clone() {
|
||||
com.envs(env);
|
||||
}
|
||||
origin = Some(com.spawn()?);
|
||||
};
|
||||
|
||||
println!("{}", server_cmd.join(" "));
|
||||
let mut server = {
|
||||
let mut com = Command::new(server_cmd[0]);
|
||||
com.args(&server_cmd[1..]);
|
||||
if let Some(env) = env {
|
||||
com.envs(env);
|
||||
}
|
||||
com.spawn()?
|
||||
};
|
||||
|
||||
// Wait for server to wake up.
|
||||
let now = Instant::now();
|
||||
let addr = format!("127.0.0.1:{port}");
|
||||
while now.elapsed().as_secs() < 30 {
|
||||
if TcpStream::connect(&addr).is_ok() {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
TcpStream::connect(&addr).expect("Failed to connect to server in time");
|
||||
println!("Server took {} ms to start", now.elapsed().as_millis());
|
||||
|
||||
let wrk = test_util::prebuilt_tool_path("wrk");
|
||||
assert!(wrk.is_file());
|
||||
|
||||
let addr = format!("http://{addr}/");
|
||||
let wrk = wrk.to_string();
|
||||
let mut wrk_cmd = vec![wrk.as_str(), "-d", DURATION, "--latency", &addr];
|
||||
|
||||
if let Some(lua_script) = lua_script {
|
||||
wrk_cmd.push("-s");
|
||||
wrk_cmd.push(lua_script);
|
||||
}
|
||||
|
||||
println!("{}", wrk_cmd.join(" "));
|
||||
let output = test_util::run_collect(&wrk_cmd, None, None, None, true).0;
|
||||
|
||||
std::thread::sleep(Duration::from_secs(1)); // wait to capture failure. TODO racy.
|
||||
|
||||
println!("{output}");
|
||||
assert!(
|
||||
server.try_wait()?.map(|s| s.success()).unwrap_or(true),
|
||||
"server ended with error"
|
||||
);
|
||||
|
||||
server.kill()?;
|
||||
if let Some(mut origin) = origin {
|
||||
origin.kill()?;
|
||||
}
|
||||
|
||||
Ok(parse_wrk_output(&output))
|
||||
}
|
||||
|
||||
static NEXT_PORT: AtomicU16 = AtomicU16::new(4544);
|
||||
pub(crate) fn get_port() -> u16 {
|
||||
let p = NEXT_PORT.load(Ordering::SeqCst);
|
||||
NEXT_PORT.store(p.wrapping_add(1), Ordering::SeqCst);
|
||||
p
|
||||
}
|
||||
|
||||
fn server_addr(port: u16) -> String {
|
||||
format!("0.0.0.0:{port}")
|
||||
}
|
||||
|
||||
fn hyper_http(exe: &str) -> Result<HttpBenchmarkResult> {
|
||||
let port = get_port();
|
||||
println!("http_benchmark testing RUST hyper");
|
||||
run(&[exe, &port.to_string()], port, None, None, None)
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
import { Hono } from "https://deno.land/x/hono@v2.0.9/mod.ts";
|
||||
|
||||
const addr = Deno.args[0] || "127.0.0.1:4500";
|
||||
const [hostname, port] = addr.split(":");
|
||||
|
||||
const app = new Hono();
|
||||
app.get("/", (c) => c.text("Hello, World!"));
|
||||
|
||||
Deno.serve({ port: Number(port), hostname }, app.fetch);
|
|
@ -1,14 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
const addr = Deno.args[0] || "127.0.0.1:4500";
|
||||
const [hostname, port] = addr.split(":");
|
||||
const { serve } = Deno;
|
||||
|
||||
const path = new URL("../testdata/128k.bin", import.meta.url).pathname;
|
||||
|
||||
function handler() {
|
||||
const file = Deno.openSync(path);
|
||||
return new Response(file.readable);
|
||||
}
|
||||
|
||||
serve({ hostname, port: Number(port) }, handler);
|
|
@ -1,5 +0,0 @@
|
|||
wrk.headers["foo"] = "bar"
|
||||
wrk.headers["User-Agent"] = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36"
|
||||
wrk.headers["Viewport-Width"] = "1920"
|
||||
wrk.headers["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
|
||||
wrk.headers["Accept-Language"] = "en,la;q=0.9"
|
|
@ -1,11 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
const addr = Deno.args[0] ?? "127.0.0.1:4500";
|
||||
const [hostname, port] = addr.split(":");
|
||||
const { serve } = Deno;
|
||||
|
||||
function handler() {
|
||||
return new Response("Hello World");
|
||||
}
|
||||
|
||||
serve({ hostname, port: Number(port), reusePort: true }, handler);
|
|
@ -1,5 +0,0 @@
|
|||
wrk.method = "POST"
|
||||
wrk.headers["Content-Type"] = "application/octet-stream"
|
||||
|
||||
file = io.open("./cli/bench/testdata/128k.bin", "rb")
|
||||
wrk.body = file:read("*a")
|
|
@ -1,3 +0,0 @@
|
|||
wrk.method = "POST"
|
||||
wrk.headers["Content-Type"] = "application/json"
|
||||
wrk.body = '{"hello":"deno"}'
|
|
@ -1,23 +0,0 @@
|
|||
import { renderToReadableStream } from "https://esm.run/react-dom/server";
|
||||
import * as React from "https://esm.run/react";
|
||||
const { serve } = Deno;
|
||||
const addr = Deno.args[0] || "127.0.0.1:4500";
|
||||
const [hostname, port] = addr.split(":");
|
||||
|
||||
const App = () => (
|
||||
<html>
|
||||
<body>
|
||||
<h1>Hello World</h1>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
const headers = {
|
||||
headers: {
|
||||
"Content-Type": "text/html",
|
||||
},
|
||||
};
|
||||
|
||||
serve({ hostname, port: Number(port) }, async () => {
|
||||
return new Response(await renderToReadableStream(<App />), headers);
|
||||
});
|
|
@ -1,33 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Used for benchmarking Deno's networking.
|
||||
// TODO(bartlomieju): Replace this with a real HTTP server once
|
||||
// https://github.com/denoland/deno/issues/726 is completed.
|
||||
// Note: this is a keep-alive server.
|
||||
const addr = Deno.args[0] || "127.0.0.1:4500";
|
||||
const [hostname, port] = addr.split(":");
|
||||
const listener = Deno.listen({ hostname, port: Number(port) });
|
||||
const response = new TextEncoder().encode(
|
||||
"HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nHello World\n",
|
||||
);
|
||||
async function handle(conn: Deno.Conn): Promise<void> {
|
||||
const buffer = new Uint8Array(1024);
|
||||
try {
|
||||
while (true) {
|
||||
await conn.read(buffer);
|
||||
await conn.write(response);
|
||||
}
|
||||
} catch (e) {
|
||||
if (
|
||||
!(e instanceof Deno.errors.BrokenPipe) &&
|
||||
!(e instanceof Deno.errors.ConnectionReset)
|
||||
) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
conn.close();
|
||||
}
|
||||
|
||||
console.log("Listening on", addr);
|
||||
for await (const conn of listener) {
|
||||
handle(conn);
|
||||
}
|
|
@ -4,9 +4,10 @@ use deno_core::serde::Deserialize;
|
|||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::url::Url;
|
||||
use lsp_types::Uri;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use test_util::lsp::LspClientBuilder;
|
||||
use test_util::PathRef;
|
||||
|
@ -91,7 +92,7 @@ fn bench_deco_apps_edits(deno_exe: &Path) -> Duration {
|
|||
.build();
|
||||
client.initialize(|c| {
|
||||
c.set_workspace_folders(vec![lsp_types::WorkspaceFolder {
|
||||
uri: Url::from_file_path(&apps).unwrap(),
|
||||
uri: apps.uri_dir(),
|
||||
name: "apps".to_string(),
|
||||
}]);
|
||||
c.set_deno_enable(true);
|
||||
|
@ -149,7 +150,11 @@ fn bench_big_file_edits(deno_exe: &Path) -> Duration {
|
|||
.deno_exe(deno_exe)
|
||||
.build();
|
||||
client.initialize_default();
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
client.change_configuration(json!({ "deno": { "enable": true } }));
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
|
||||
client.write_notification(
|
||||
"textDocument/didOpen",
|
||||
|
@ -205,6 +210,8 @@ fn bench_code_lens(deno_exe: &Path) -> Duration {
|
|||
.deno_exe(deno_exe)
|
||||
.build();
|
||||
client.initialize_default();
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
client.change_configuration(json!({ "deno": {
|
||||
"enable": true,
|
||||
"codeLens": {
|
||||
|
@ -213,6 +220,8 @@ fn bench_code_lens(deno_exe: &Path) -> Duration {
|
|||
"test": true,
|
||||
},
|
||||
} }));
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
|
||||
client.write_notification(
|
||||
"textDocument/didOpen",
|
||||
|
@ -256,7 +265,11 @@ fn bench_find_replace(deno_exe: &Path) -> Duration {
|
|||
.deno_exe(deno_exe)
|
||||
.build();
|
||||
client.initialize_default();
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
client.change_configuration(json!({ "deno": { "enable": true } }));
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
|
||||
for i in 0..10 {
|
||||
client.write_notification(
|
||||
|
@ -283,7 +296,7 @@ fn bench_find_replace(deno_exe: &Path) -> Duration {
|
|||
"textDocument/didChange",
|
||||
lsp::DidChangeTextDocumentParams {
|
||||
text_document: lsp::VersionedTextDocumentIdentifier {
|
||||
uri: Url::parse(&file_name).unwrap(),
|
||||
uri: Uri::from_str(&file_name).unwrap(),
|
||||
version: 2,
|
||||
},
|
||||
content_changes: vec![lsp::TextDocumentContentChangeEvent {
|
||||
|
@ -310,7 +323,7 @@ fn bench_find_replace(deno_exe: &Path) -> Duration {
|
|||
"textDocument/formatting",
|
||||
lsp::DocumentFormattingParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: Url::parse(&file_name).unwrap(),
|
||||
uri: Uri::from_str(&file_name).unwrap(),
|
||||
},
|
||||
options: lsp::FormattingOptions {
|
||||
tab_size: 2,
|
||||
|
@ -340,7 +353,11 @@ fn bench_startup_shutdown(deno_exe: &Path) -> Duration {
|
|||
.deno_exe(deno_exe)
|
||||
.build();
|
||||
client.initialize_default();
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
client.change_configuration(json!({ "deno": { "enable": true } }));
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
|
||||
client.write_notification(
|
||||
"textDocument/didOpen",
|
||||
|
|
|
@ -13,7 +13,11 @@ use test_util::lsp::LspClientBuilder;
|
|||
fn incremental_change_wait(bench: &mut Bencher) {
|
||||
let mut client = LspClientBuilder::new().use_diagnostic_sync(false).build();
|
||||
client.initialize_default();
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
client.change_configuration(json!({ "deno": { "enable": true } }));
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
|
||||
client.write_notification(
|
||||
"textDocument/didOpen",
|
||||
|
|
|
@ -17,7 +17,6 @@ use std::process::Stdio;
|
|||
use std::time::SystemTime;
|
||||
use test_util::PathRef;
|
||||
|
||||
mod http;
|
||||
mod lsp;
|
||||
|
||||
fn read_json(filename: &Path) -> Result<Value> {
|
||||
|
@ -143,29 +142,6 @@ const EXEC_TIME_BENCHMARKS: &[(&str, &[&str], Option<i32>)] = &[
|
|||
],
|
||||
None,
|
||||
),
|
||||
(
|
||||
"bundle",
|
||||
&[
|
||||
"bundle",
|
||||
"--unstable",
|
||||
"--config",
|
||||
"tests/config/deno.json",
|
||||
"tests/util/std/http/file_server_test.ts",
|
||||
],
|
||||
None,
|
||||
),
|
||||
(
|
||||
"bundle_no_check",
|
||||
&[
|
||||
"bundle",
|
||||
"--no-check",
|
||||
"--unstable",
|
||||
"--config",
|
||||
"tests/config/deno.json",
|
||||
"tests/util/std/http/file_server_test.ts",
|
||||
],
|
||||
None,
|
||||
),
|
||||
];
|
||||
|
||||
const RESULT_KEYS: &[&str] =
|
||||
|
@ -314,40 +290,6 @@ fn get_binary_sizes(target_dir: &Path) -> Result<HashMap<String, i64>> {
|
|||
Ok(sizes)
|
||||
}
|
||||
|
||||
const BUNDLES: &[(&str, &str)] = &[
|
||||
("file_server", "./tests/util/std/http/file_server.ts"),
|
||||
("welcome", "./tests/testdata/welcome.ts"),
|
||||
];
|
||||
fn bundle_benchmark(deno_exe: &Path) -> Result<HashMap<String, i64>> {
|
||||
let mut sizes = HashMap::<String, i64>::new();
|
||||
|
||||
for (name, url) in BUNDLES {
|
||||
let path = format!("{name}.bundle.js");
|
||||
test_util::run(
|
||||
&[
|
||||
deno_exe.to_str().unwrap(),
|
||||
"bundle",
|
||||
"--unstable",
|
||||
"--config",
|
||||
"tests/config/deno.json",
|
||||
url,
|
||||
&path,
|
||||
],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
|
||||
let file = PathBuf::from(path);
|
||||
assert!(file.is_file());
|
||||
sizes.insert(name.to_string(), file.metadata()?.len() as i64);
|
||||
let _ = fs::remove_file(file);
|
||||
}
|
||||
|
||||
Ok(sizes)
|
||||
}
|
||||
|
||||
fn run_max_mem_benchmark(deno_exe: &Path) -> Result<HashMap<String, i64>> {
|
||||
let mut results = HashMap::<String, i64>::new();
|
||||
|
||||
|
@ -402,9 +344,11 @@ struct BenchResult {
|
|||
binary_size: HashMap<String, i64>,
|
||||
bundle_size: HashMap<String, i64>,
|
||||
cargo_deps: usize,
|
||||
// TODO(bartlomieju): remove
|
||||
max_latency: HashMap<String, f64>,
|
||||
max_memory: HashMap<String, i64>,
|
||||
lsp_exec_time: HashMap<String, i64>,
|
||||
// TODO(bartlomieju): remove
|
||||
req_per_sec: HashMap<String, i64>,
|
||||
syscall_count: HashMap<String, i64>,
|
||||
thread_count: HashMap<String, i64>,
|
||||
|
@ -415,12 +359,10 @@ async fn main() -> Result<()> {
|
|||
let mut args = env::args();
|
||||
|
||||
let mut benchmarks = vec![
|
||||
"bundle",
|
||||
"exec_time",
|
||||
"binary_size",
|
||||
"cargo_deps",
|
||||
"lsp",
|
||||
"http",
|
||||
"strace",
|
||||
"mem_usage",
|
||||
];
|
||||
|
@ -465,11 +407,6 @@ async fn main() -> Result<()> {
|
|||
..Default::default()
|
||||
};
|
||||
|
||||
if benchmarks.contains(&"bundle") {
|
||||
let bundle_size = bundle_benchmark(&deno_exe)?;
|
||||
new_data.bundle_size = bundle_size;
|
||||
}
|
||||
|
||||
if benchmarks.contains(&"exec_time") {
|
||||
let exec_times = run_exec_time(&deno_exe, &target_dir)?;
|
||||
new_data.benchmark = exec_times;
|
||||
|
@ -490,21 +427,6 @@ async fn main() -> Result<()> {
|
|||
new_data.lsp_exec_time = lsp_exec_times;
|
||||
}
|
||||
|
||||
if benchmarks.contains(&"http") && cfg!(not(target_os = "windows")) {
|
||||
let stats = http::benchmark(target_dir.as_path())?;
|
||||
let req_per_sec = stats
|
||||
.iter()
|
||||
.map(|(name, result)| (name.clone(), result.requests as i64))
|
||||
.collect();
|
||||
new_data.req_per_sec = req_per_sec;
|
||||
let max_latency = stats
|
||||
.iter()
|
||||
.map(|(name, result)| (name.clone(), result.latency))
|
||||
.collect();
|
||||
|
||||
new_data.max_latency = max_latency;
|
||||
}
|
||||
|
||||
if cfg!(target_os = "linux") && benchmarks.contains(&"strace") {
|
||||
use std::io::Read;
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { bench, run } from "mitata";
|
||||
import { createRequire } from "module";
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// deno-lint-ignore-file no-console no-process-globals
|
||||
|
||||
const queueMicrotask = globalThis.queueMicrotask || process.nextTick;
|
||||
let [total, count] = typeof Deno !== "undefined"
|
||||
? Deno.args
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// deno-lint-ignore-file no-console no-process-globals
|
||||
|
||||
let [total, count] = typeof Deno !== "undefined"
|
||||
? Deno.args
|
||||
: [process.argv[2], process.argv[3]];
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// From https://github.com/just-js/benchmarks/tree/main/01-stdio
|
||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
@ -26,4 +26,4 @@ int main(int argc, char *argv[]) {
|
|||
exit(1);
|
||||
}
|
||||
fprintf(stdout, "size %lu reads %u blocksize %u\n", size, reads, blocksize);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
//
|
||||
// From https://github.com/just-js/benchmarks/tree/main/01-stdio
|
||||
|
||||
// deno-lint-ignore-file no-console
|
||||
|
||||
const blocksize = parseInt(Deno.args[0] || 65536);
|
||||
const buf = new Uint8Array(blocksize);
|
||||
let size = 0;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// deno-lint-ignore-file no-console no-process-globals
|
||||
|
||||
const queueMicrotask = globalThis.queueMicrotask || process.nextTick;
|
||||
let [total, count] = typeof Deno !== "undefined"
|
||||
? Deno.args
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// deno-lint-ignore-file no-console no-process-globals
|
||||
|
||||
const queueMicrotask = globalThis.queueMicrotask || process.nextTick;
|
||||
let [total, count] = typeof Deno !== "undefined"
|
||||
? Deno.args
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
// deno-lint-ignore-file no-console
|
||||
|
||||
// Note: when benchmarking across different Deno version, make sure to clear
|
||||
// the DENO_DIR cache.
|
||||
let [total, count] = typeof Deno !== "undefined" ? Deno.args : [];
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// deno-lint-ignore-file no-console no-process-globals
|
||||
|
||||
const queueMicrotask = globalThis.queueMicrotask || process.nextTick;
|
||||
let [total, count] = typeof Deno !== "undefined"
|
||||
? Deno.args
|
||||
|
|
95
cli/build.rs
95
cli/build.rs
|
@ -5,6 +5,7 @@ use std::path::PathBuf;
|
|||
|
||||
use deno_core::snapshot::*;
|
||||
use deno_runtime::*;
|
||||
mod shared;
|
||||
|
||||
mod ts {
|
||||
use super::*;
|
||||
|
@ -12,7 +13,6 @@ mod ts {
|
|||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_runtime::deno_node::SUPPORTED_BUILTIN_NODE_MODULES;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
|
@ -24,7 +24,6 @@ mod ts {
|
|||
struct BuildInfoResponse {
|
||||
build_specifier: String,
|
||||
libs: Vec<String>,
|
||||
node_built_in_module_names: Vec<String>,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
|
@ -36,14 +35,9 @@ mod ts {
|
|||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
let node_built_in_module_names = SUPPORTED_BUILTIN_NODE_MODULES
|
||||
.iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
BuildInfoResponse {
|
||||
build_specifier,
|
||||
libs: build_libs,
|
||||
node_built_in_module_names,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,6 +236,7 @@ mod ts {
|
|||
"esnext.decorators",
|
||||
"esnext.disposable",
|
||||
"esnext.intl",
|
||||
"esnext.iterator",
|
||||
"esnext.object",
|
||||
"esnext.promise",
|
||||
"esnext.regexp",
|
||||
|
@ -329,20 +324,9 @@ mod ts {
|
|||
fn create_cli_snapshot(snapshot_path: PathBuf) {
|
||||
use deno_runtime::ops::bootstrap::SnapshotOptions;
|
||||
|
||||
// NOTE(bartlomieju): keep in sync with `cli/version.rs`.
|
||||
// Ideally we could deduplicate that code.
|
||||
fn deno_version() -> String {
|
||||
if env::var("DENO_CANARY").is_ok() {
|
||||
format!("{}+{}", env!("CARGO_PKG_VERSION"), &git_commit_hash()[..7])
|
||||
} else {
|
||||
env!("CARGO_PKG_VERSION").to_string()
|
||||
}
|
||||
}
|
||||
|
||||
let snapshot_options = SnapshotOptions {
|
||||
deno_version: deno_version(),
|
||||
ts_version: ts::version(),
|
||||
v8_version: deno_core::v8_version(),
|
||||
v8_version: deno_core::v8::VERSION_STRING,
|
||||
target: std::env::var("TARGET").unwrap(),
|
||||
};
|
||||
|
||||
|
@ -381,6 +365,9 @@ fn main() {
|
|||
return;
|
||||
}
|
||||
|
||||
deno_napi::print_linker_flags("deno");
|
||||
deno_napi::print_linker_flags("denort");
|
||||
|
||||
// Host snapshots won't work when cross compiling.
|
||||
let target = env::var("TARGET").unwrap();
|
||||
let host = env::var("HOST").unwrap();
|
||||
|
@ -390,56 +377,6 @@ fn main() {
|
|||
panic!("Cross compiling with snapshot is not supported.");
|
||||
}
|
||||
|
||||
let symbols_file_name = match env::consts::OS {
|
||||
"android" | "freebsd" | "openbsd" => {
|
||||
"generated_symbol_exports_list_linux.def".to_string()
|
||||
}
|
||||
os => format!("generated_symbol_exports_list_{}.def", os),
|
||||
};
|
||||
let symbols_path = std::path::Path::new("napi")
|
||||
.join(symbols_file_name)
|
||||
.canonicalize()
|
||||
.expect(
|
||||
"Missing symbols list! Generate using tools/napi/generate_symbols_lists.js",
|
||||
);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
println!(
|
||||
"cargo:rustc-link-arg-bin=deno=/DEF:{}",
|
||||
symbols_path.display()
|
||||
);
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
println!(
|
||||
"cargo:rustc-link-arg-bin=deno=-Wl,-exported_symbols_list,{}",
|
||||
symbols_path.display()
|
||||
);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
// If a custom compiler is set, the glibc version is not reliable.
|
||||
// Here, we assume that if a custom compiler is used, that it will be modern enough to support a dynamic symbol list.
|
||||
if env::var("CC").is_err()
|
||||
&& glibc_version::get_version()
|
||||
.map(|ver| ver.major <= 2 && ver.minor < 35)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
println!("cargo:warning=Compiling with all symbols exported, this will result in a larger binary. Please use glibc 2.35 or later for an optimised build.");
|
||||
println!("cargo:rustc-link-arg-bin=deno=-rdynamic");
|
||||
} else {
|
||||
println!(
|
||||
"cargo:rustc-link-arg-bin=deno=-Wl,--export-dynamic-symbol-list={}",
|
||||
symbols_path.display()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
println!(
|
||||
"cargo:rustc-link-arg-bin=deno=-Wl,--export-dynamic-symbol-list={}",
|
||||
symbols_path.display()
|
||||
);
|
||||
|
||||
// To debug snapshot issues uncomment:
|
||||
// op_fetch_asset::trace_serializer();
|
||||
|
||||
|
@ -456,13 +393,31 @@ fn main() {
|
|||
);
|
||||
|
||||
let ts_version = ts::version();
|
||||
debug_assert_eq!(ts_version, "5.5.2"); // bump this assertion when it changes
|
||||
debug_assert_eq!(ts_version, "5.6.2"); // bump this assertion when it changes
|
||||
println!("cargo:rustc-env=TS_VERSION={}", ts_version);
|
||||
println!("cargo:rerun-if-env-changed=TS_VERSION");
|
||||
|
||||
println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap());
|
||||
println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap());
|
||||
|
||||
if cfg!(windows) {
|
||||
// these dls load slowly, so delay loading them
|
||||
let dlls = [
|
||||
// webgpu
|
||||
"d3dcompiler_47",
|
||||
"OPENGL32",
|
||||
// network related functions
|
||||
"iphlpapi",
|
||||
];
|
||||
for dll in dlls {
|
||||
println!("cargo:rustc-link-arg-bin=deno=/delayload:{dll}.dll");
|
||||
println!("cargo:rustc-link-arg-bin=denort=/delayload:{dll}.dll");
|
||||
}
|
||||
// enable delay loading
|
||||
println!("cargo:rustc-link-arg-bin=deno=delayimp.lib");
|
||||
println!("cargo:rustc-link-arg-bin=denort=delayimp.lib");
|
||||
}
|
||||
|
||||
let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
|
||||
let o = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
|
||||
|
|
8
cli/cache/cache_db.rs
vendored
8
cli/cache/cache_db.rs
vendored
|
@ -57,7 +57,7 @@ impl rusqlite::types::FromSql for CacheDBHash {
|
|||
}
|
||||
|
||||
/// What should the cache should do on failure?
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, Default)]
|
||||
pub enum CacheFailure {
|
||||
/// Return errors if failure mode otherwise unspecified.
|
||||
#[default]
|
||||
|
@ -69,6 +69,7 @@ pub enum CacheFailure {
|
|||
}
|
||||
|
||||
/// Configuration SQL and other parameters for a [`CacheDB`].
|
||||
#[derive(Debug)]
|
||||
pub struct CacheDBConfiguration {
|
||||
/// SQL to run for a new database.
|
||||
pub table_initializer: &'static str,
|
||||
|
@ -98,6 +99,7 @@ impl CacheDBConfiguration {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ConnectionState {
|
||||
Connected(Connection),
|
||||
Blackhole,
|
||||
|
@ -106,7 +108,7 @@ enum ConnectionState {
|
|||
|
||||
/// A cache database that eagerly initializes itself off-thread, preventing initialization operations
|
||||
/// from blocking the main thread.
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CacheDB {
|
||||
// TODO(mmastrac): We can probably simplify our thread-safe implementation here
|
||||
conn: Arc<Mutex<OnceCell<ConnectionState>>>,
|
||||
|
@ -470,7 +472,7 @@ mod tests {
|
|||
};
|
||||
|
||||
static TEST_DB_BLACKHOLE: CacheDBConfiguration = CacheDBConfiguration {
|
||||
table_initializer: "syntax error", // intentially cause an error
|
||||
table_initializer: "syntax error", // intentionally cause an error
|
||||
on_version_change: "",
|
||||
preheat_queries: &[],
|
||||
on_failure: CacheFailure::Blackhole,
|
||||
|
|
8
cli/cache/caches.rs
vendored
8
cli/cache/caches.rs
vendored
|
@ -48,9 +48,13 @@ impl Caches {
|
|||
cell
|
||||
.get_or_init(|| {
|
||||
if let Some(path) = path {
|
||||
CacheDB::from_path(config, path, crate::version::deno())
|
||||
CacheDB::from_path(
|
||||
config,
|
||||
path,
|
||||
crate::version::DENO_VERSION_INFO.deno,
|
||||
)
|
||||
} else {
|
||||
CacheDB::in_memory(config, crate::version::deno())
|
||||
CacheDB::in_memory(config, crate::version::DENO_VERSION_INFO.deno)
|
||||
}
|
||||
})
|
||||
.clone()
|
||||
|
|
10
cli/cache/code_cache.rs
vendored
10
cli/cache/code_cache.rs
vendored
|
@ -1,10 +1,14 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_runtime::code_cache;
|
||||
use deno_runtime::deno_webstorage::rusqlite::params;
|
||||
|
||||
use crate::worker::CliCodeCache;
|
||||
|
||||
use super::cache_db::CacheDB;
|
||||
use super::cache_db::CacheDBConfiguration;
|
||||
use super::cache_db::CacheDBHash;
|
||||
|
@ -82,6 +86,12 @@ impl CodeCache {
|
|||
}
|
||||
}
|
||||
|
||||
impl CliCodeCache for CodeCache {
|
||||
fn as_code_cache(self: Arc<Self>) -> Arc<dyn code_cache::CodeCache> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl code_cache::CodeCache for CodeCache {
|
||||
fn get_sync(
|
||||
&self,
|
||||
|
|
2
cli/cache/common.rs
vendored
2
cli/cache/common.rs
vendored
|
@ -12,7 +12,7 @@ impl FastInsecureHasher {
|
|||
|
||||
pub fn new_deno_versioned() -> Self {
|
||||
let mut hasher = Self::new_without_deno_version();
|
||||
hasher.write_str(crate::version::deno());
|
||||
hasher.write_str(crate::version::DENO_VERSION_INFO.deno);
|
||||
hasher
|
||||
}
|
||||
|
||||
|
|
6
cli/cache/deno_dir.rs
vendored
6
cli/cache/deno_dir.rs
vendored
|
@ -126,9 +126,9 @@ impl DenoDir {
|
|||
self.root.join("registries")
|
||||
}
|
||||
|
||||
/// Path to the dependencies cache folder.
|
||||
pub fn deps_folder_path(&self) -> PathBuf {
|
||||
self.root.join("deps")
|
||||
/// Path to the remote cache folder.
|
||||
pub fn remote_folder_path(&self) -> PathBuf {
|
||||
self.root.join("remote")
|
||||
}
|
||||
|
||||
/// Path to the origin data cache folder.
|
||||
|
|
201
cli/cache/emit.rs
vendored
201
cli/cache/emit.rs
vendored
|
@ -5,31 +5,26 @@ use std::path::PathBuf;
|
|||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::anyhow::anyhow;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use deno_core::unsync::sync::AtomicFlag;
|
||||
|
||||
use super::DiskCache;
|
||||
use super::FastInsecureHasher;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct EmitMetadata {
|
||||
pub source_hash: u64,
|
||||
pub emit_hash: u64,
|
||||
}
|
||||
|
||||
/// The cache that stores previously emitted files.
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug)]
|
||||
pub struct EmitCache {
|
||||
disk_cache: DiskCache,
|
||||
cli_version: &'static str,
|
||||
emit_failed_flag: AtomicFlag,
|
||||
file_serializer: EmitFileSerializer,
|
||||
}
|
||||
|
||||
impl EmitCache {
|
||||
pub fn new(disk_cache: DiskCache) -> Self {
|
||||
Self {
|
||||
disk_cache,
|
||||
cli_version: crate::version::deno(),
|
||||
emit_failed_flag: Default::default(),
|
||||
file_serializer: EmitFileSerializer {
|
||||
cli_version: crate::version::DENO_VERSION_INFO.deno,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,38 +40,12 @@ impl EmitCache {
|
|||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
expected_source_hash: u64,
|
||||
) -> Option<Vec<u8>> {
|
||||
let meta_filename = self.get_meta_filename(specifier)?;
|
||||
) -> Option<String> {
|
||||
let emit_filename = self.get_emit_filename(specifier)?;
|
||||
|
||||
// load and verify the meta data file is for this source and CLI version
|
||||
let bytes = self.disk_cache.get(&meta_filename).ok()?;
|
||||
let meta: EmitMetadata = serde_json::from_slice(&bytes).ok()?;
|
||||
if meta.source_hash != expected_source_hash {
|
||||
return None;
|
||||
}
|
||||
|
||||
// load and verify the emit is for the meta data
|
||||
let emit_bytes = self.disk_cache.get(&emit_filename).ok()?;
|
||||
if meta.emit_hash != compute_emit_hash(&emit_bytes, self.cli_version) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// everything looks good, return it
|
||||
Some(emit_bytes)
|
||||
}
|
||||
|
||||
/// Gets the filepath which stores the emit.
|
||||
pub fn get_emit_filepath(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<PathBuf> {
|
||||
Some(
|
||||
self
|
||||
.disk_cache
|
||||
.location
|
||||
.join(self.get_emit_filename(specifier)?),
|
||||
)
|
||||
let bytes = self.disk_cache.get(&emit_filename).ok()?;
|
||||
self
|
||||
.file_serializer
|
||||
.deserialize(bytes, expected_source_hash)
|
||||
}
|
||||
|
||||
/// Sets the emit code in the cache.
|
||||
|
@ -87,12 +56,10 @@ impl EmitCache {
|
|||
code: &[u8],
|
||||
) {
|
||||
if let Err(err) = self.set_emit_code_result(specifier, source_hash, code) {
|
||||
// should never error here, but if it ever does don't fail
|
||||
if cfg!(debug_assertions) {
|
||||
panic!("Error saving emit data ({specifier}): {err}");
|
||||
} else {
|
||||
log::debug!("Error saving emit data({}): {}", specifier, err);
|
||||
}
|
||||
// might error in cases such as a readonly file system
|
||||
log::debug!("Error saving emit data ({}): {}", specifier, err);
|
||||
// assume the cache can't be written to and disable caching to it
|
||||
self.emit_failed_flag.raise();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,34 +69,20 @@ impl EmitCache {
|
|||
source_hash: u64,
|
||||
code: &[u8],
|
||||
) -> Result<(), AnyError> {
|
||||
let meta_filename = self
|
||||
.get_meta_filename(specifier)
|
||||
.ok_or_else(|| anyhow!("Could not get meta filename."))?;
|
||||
if self.emit_failed_flag.is_raised() {
|
||||
log::debug!("Skipped emit cache save of {}", specifier);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let emit_filename = self
|
||||
.get_emit_filename(specifier)
|
||||
.ok_or_else(|| anyhow!("Could not get emit filename."))?;
|
||||
|
||||
// save the metadata
|
||||
let metadata = EmitMetadata {
|
||||
source_hash,
|
||||
emit_hash: compute_emit_hash(code, self.cli_version),
|
||||
};
|
||||
self
|
||||
.disk_cache
|
||||
.set(&meta_filename, &serde_json::to_vec(&metadata)?)?;
|
||||
|
||||
// save the emit source
|
||||
self.disk_cache.set(&emit_filename, code)?;
|
||||
let cache_data = self.file_serializer.serialize(code, source_hash);
|
||||
self.disk_cache.set(&emit_filename, &cache_data)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_meta_filename(&self, specifier: &ModuleSpecifier) -> Option<PathBuf> {
|
||||
self
|
||||
.disk_cache
|
||||
.get_cache_filename_with_extension(specifier, "meta")
|
||||
}
|
||||
|
||||
fn get_emit_filename(&self, specifier: &ModuleSpecifier) -> Option<PathBuf> {
|
||||
self
|
||||
.disk_cache
|
||||
|
@ -137,15 +90,69 @@ impl EmitCache {
|
|||
}
|
||||
}
|
||||
|
||||
fn compute_emit_hash(bytes: &[u8], cli_version: &str) -> u64 {
|
||||
// it's ok to use an insecure hash here because
|
||||
// if someone can change the emit source then they
|
||||
// can also change the version hash
|
||||
FastInsecureHasher::new_without_deno_version() // use cli_version param instead
|
||||
.write(bytes)
|
||||
// emit should not be re-used between cli versions
|
||||
.write_str(cli_version)
|
||||
.finish()
|
||||
const LAST_LINE_PREFIX: &str = "\n// denoCacheMetadata=";
|
||||
|
||||
#[derive(Debug)]
|
||||
struct EmitFileSerializer {
|
||||
cli_version: &'static str,
|
||||
}
|
||||
|
||||
impl EmitFileSerializer {
|
||||
pub fn deserialize(
|
||||
&self,
|
||||
mut bytes: Vec<u8>,
|
||||
expected_source_hash: u64,
|
||||
) -> Option<String> {
|
||||
let last_newline_index = bytes.iter().rposition(|&b| b == b'\n')?;
|
||||
let (content, last_line) = bytes.split_at(last_newline_index);
|
||||
let hashes = last_line.strip_prefix(LAST_LINE_PREFIX.as_bytes())?;
|
||||
let hashes = String::from_utf8_lossy(hashes);
|
||||
let (source_hash, emit_hash) = hashes.split_once(',')?;
|
||||
|
||||
// verify the meta data file is for this source and CLI version
|
||||
let source_hash = source_hash.parse::<u64>().ok()?;
|
||||
if source_hash != expected_source_hash {
|
||||
return None;
|
||||
}
|
||||
let emit_hash = emit_hash.parse::<u64>().ok()?;
|
||||
// prevent using an emit from a different cli version or emits that were tampered with
|
||||
if emit_hash != self.compute_emit_hash(content) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// everything looks good, truncate and return it
|
||||
bytes.truncate(content.len());
|
||||
String::from_utf8(bytes).ok()
|
||||
}
|
||||
|
||||
pub fn serialize(&self, code: &[u8], source_hash: u64) -> Vec<u8> {
|
||||
let source_hash = source_hash.to_string();
|
||||
let emit_hash = self.compute_emit_hash(code).to_string();
|
||||
let capacity = code.len()
|
||||
+ LAST_LINE_PREFIX.len()
|
||||
+ source_hash.len()
|
||||
+ 1
|
||||
+ emit_hash.len();
|
||||
let mut cache_data = Vec::with_capacity(capacity);
|
||||
cache_data.extend(code);
|
||||
cache_data.extend(LAST_LINE_PREFIX.as_bytes());
|
||||
cache_data.extend(source_hash.as_bytes());
|
||||
cache_data.push(b',');
|
||||
cache_data.extend(emit_hash.as_bytes());
|
||||
debug_assert_eq!(cache_data.len(), capacity);
|
||||
cache_data
|
||||
}
|
||||
|
||||
fn compute_emit_hash(&self, bytes: &[u8]) -> u64 {
|
||||
// it's ok to use an insecure hash here because
|
||||
// if someone can change the emit source then they
|
||||
// can also change the version hash
|
||||
crate::cache::FastInsecureHasher::new_without_deno_version() // use cli_version property instead
|
||||
.write(bytes)
|
||||
// emit should not be re-used between cli versions
|
||||
.write_str(self.cli_version)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -160,10 +167,11 @@ mod test {
|
|||
let disk_cache = DiskCache::new(temp_dir.path().as_path());
|
||||
let cache = EmitCache {
|
||||
disk_cache: disk_cache.clone(),
|
||||
cli_version: "1.0.0",
|
||||
file_serializer: EmitFileSerializer {
|
||||
cli_version: "1.0.0",
|
||||
},
|
||||
emit_failed_flag: Default::default(),
|
||||
};
|
||||
let to_string =
|
||||
|bytes: Vec<u8>| -> String { String::from_utf8(bytes).unwrap() };
|
||||
|
||||
let specifier1 =
|
||||
ModuleSpecifier::from_file_path(temp_dir.path().join("file1.ts"))
|
||||
|
@ -180,18 +188,18 @@ mod test {
|
|||
assert_eq!(cache.get_emit_code(&specifier1, 5), None);
|
||||
// providing the correct source hash
|
||||
assert_eq!(
|
||||
cache.get_emit_code(&specifier1, 10).map(to_string),
|
||||
cache.get_emit_code(&specifier1, 10),
|
||||
Some(emit_code1.clone()),
|
||||
);
|
||||
assert_eq!(
|
||||
cache.get_emit_code(&specifier2, 2).map(to_string),
|
||||
Some(emit_code2)
|
||||
);
|
||||
assert_eq!(cache.get_emit_code(&specifier2, 2), Some(emit_code2));
|
||||
|
||||
// try changing the cli version (should not load previous ones)
|
||||
let cache = EmitCache {
|
||||
disk_cache: disk_cache.clone(),
|
||||
cli_version: "2.0.0",
|
||||
file_serializer: EmitFileSerializer {
|
||||
cli_version: "2.0.0",
|
||||
},
|
||||
emit_failed_flag: Default::default(),
|
||||
};
|
||||
assert_eq!(cache.get_emit_code(&specifier1, 10), None);
|
||||
cache.set_emit_code(&specifier1, 5, emit_code1.as_bytes());
|
||||
|
@ -199,20 +207,17 @@ mod test {
|
|||
// recreating the cache should still load the data because the CLI version is the same
|
||||
let cache = EmitCache {
|
||||
disk_cache,
|
||||
cli_version: "2.0.0",
|
||||
file_serializer: EmitFileSerializer {
|
||||
cli_version: "2.0.0",
|
||||
},
|
||||
emit_failed_flag: Default::default(),
|
||||
};
|
||||
assert_eq!(
|
||||
cache.get_emit_code(&specifier1, 5).map(to_string),
|
||||
Some(emit_code1)
|
||||
);
|
||||
assert_eq!(cache.get_emit_code(&specifier1, 5), Some(emit_code1));
|
||||
|
||||
// adding when already exists should not cause issue
|
||||
let emit_code3 = "asdf".to_string();
|
||||
cache.set_emit_code(&specifier1, 20, emit_code3.as_bytes());
|
||||
assert_eq!(cache.get_emit_code(&specifier1, 5), None);
|
||||
assert_eq!(
|
||||
cache.get_emit_code(&specifier1, 20).map(to_string),
|
||||
Some(emit_code3)
|
||||
);
|
||||
assert_eq!(cache.get_emit_code(&specifier1, 20), Some(emit_code3));
|
||||
}
|
||||
}
|
||||
|
|
165
cli/cache/mod.rs
vendored
165
cli/cache/mod.rs
vendored
|
@ -1,13 +1,16 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::args::jsr_url;
|
||||
use crate::args::CacheSetting;
|
||||
use crate::errors::get_error_class_name;
|
||||
use crate::file_fetcher::FetchNoFollowOptions;
|
||||
use crate::file_fetcher::FetchOptions;
|
||||
use crate::file_fetcher::FetchPermissionsOptionRef;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::file_fetcher::FileOrRedirect;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::util::fs::atomic_write_file_with_retries;
|
||||
use crate::util::fs::atomic_write_file_with_retries_and_fs;
|
||||
use crate::util::fs::AtomicWriteFileFsAdapter;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::futures;
|
||||
|
@ -17,7 +20,9 @@ use deno_graph::source::CacheInfo;
|
|||
use deno_graph::source::LoadFuture;
|
||||
use deno_graph::source::LoadResponse;
|
||||
use deno_graph::source::Loader;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
@ -62,12 +67,8 @@ pub const CACHE_PERM: u32 = 0o644;
|
|||
pub struct RealDenoCacheEnv;
|
||||
|
||||
impl deno_cache_dir::DenoCacheEnv for RealDenoCacheEnv {
|
||||
fn read_file_bytes(&self, path: &Path) -> std::io::Result<Option<Vec<u8>>> {
|
||||
match std::fs::read(path) {
|
||||
Ok(s) => Ok(Some(s)),
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
fn read_file_bytes(&self, path: &Path) -> std::io::Result<Vec<u8>> {
|
||||
std::fs::read(path)
|
||||
}
|
||||
|
||||
fn atomic_write_file(
|
||||
|
@ -78,6 +79,14 @@ impl deno_cache_dir::DenoCacheEnv for RealDenoCacheEnv {
|
|||
atomic_write_file_with_retries(path, bytes, CACHE_PERM)
|
||||
}
|
||||
|
||||
fn canonicalize_path(&self, path: &Path) -> std::io::Result<PathBuf> {
|
||||
crate::util::fs::canonicalize_path(path)
|
||||
}
|
||||
|
||||
fn create_dir_all(&self, path: &Path) -> std::io::Result<()> {
|
||||
std::fs::create_dir_all(path)
|
||||
}
|
||||
|
||||
fn modified(&self, path: &Path) -> std::io::Result<Option<SystemTime>> {
|
||||
match std::fs::metadata(path) {
|
||||
Ok(metadata) => Ok(Some(
|
||||
|
@ -97,43 +106,111 @@ impl deno_cache_dir::DenoCacheEnv for RealDenoCacheEnv {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DenoCacheEnvFsAdapter<'a>(
|
||||
pub &'a dyn deno_runtime::deno_fs::FileSystem,
|
||||
);
|
||||
|
||||
impl<'a> deno_cache_dir::DenoCacheEnv for DenoCacheEnvFsAdapter<'a> {
|
||||
fn read_file_bytes(&self, path: &Path) -> std::io::Result<Vec<u8>> {
|
||||
self
|
||||
.0
|
||||
.read_file_sync(path, None)
|
||||
.map_err(|err| err.into_io_error())
|
||||
}
|
||||
|
||||
fn atomic_write_file(
|
||||
&self,
|
||||
path: &Path,
|
||||
bytes: &[u8],
|
||||
) -> std::io::Result<()> {
|
||||
atomic_write_file_with_retries_and_fs(
|
||||
&AtomicWriteFileFsAdapter {
|
||||
fs: self.0,
|
||||
write_mode: CACHE_PERM,
|
||||
},
|
||||
path,
|
||||
bytes,
|
||||
)
|
||||
}
|
||||
|
||||
fn canonicalize_path(&self, path: &Path) -> std::io::Result<PathBuf> {
|
||||
self.0.realpath_sync(path).map_err(|e| e.into_io_error())
|
||||
}
|
||||
|
||||
fn create_dir_all(&self, path: &Path) -> std::io::Result<()> {
|
||||
self
|
||||
.0
|
||||
.mkdir_sync(path, true, None)
|
||||
.map_err(|e| e.into_io_error())
|
||||
}
|
||||
|
||||
fn modified(&self, path: &Path) -> std::io::Result<Option<SystemTime>> {
|
||||
self
|
||||
.0
|
||||
.stat_sync(path)
|
||||
.map(|stat| {
|
||||
stat
|
||||
.mtime
|
||||
.map(|ts| SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(ts))
|
||||
})
|
||||
.map_err(|e| e.into_io_error())
|
||||
}
|
||||
|
||||
fn is_file(&self, path: &Path) -> bool {
|
||||
self.0.is_file_sync(path)
|
||||
}
|
||||
|
||||
fn time_now(&self) -> SystemTime {
|
||||
SystemTime::now()
|
||||
}
|
||||
}
|
||||
|
||||
pub type GlobalHttpCache = deno_cache_dir::GlobalHttpCache<RealDenoCacheEnv>;
|
||||
pub type LocalHttpCache = deno_cache_dir::LocalHttpCache<RealDenoCacheEnv>;
|
||||
pub type LocalLspHttpCache =
|
||||
deno_cache_dir::LocalLspHttpCache<RealDenoCacheEnv>;
|
||||
pub use deno_cache_dir::HttpCache;
|
||||
|
||||
pub struct FetchCacherOptions {
|
||||
pub file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
|
||||
pub permissions: PermissionsContainer,
|
||||
/// If we're publishing for `deno publish`.
|
||||
pub is_deno_publish: bool,
|
||||
}
|
||||
|
||||
/// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides
|
||||
/// a concise interface to the DENO_DIR when building module graphs.
|
||||
pub struct FetchCacher {
|
||||
emit_cache: EmitCache,
|
||||
pub file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
permissions: PermissionsContainer,
|
||||
is_deno_publish: bool,
|
||||
cache_info_enabled: bool,
|
||||
}
|
||||
|
||||
impl FetchCacher {
|
||||
pub fn new(
|
||||
emit_cache: EmitCache,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
permissions: PermissionsContainer,
|
||||
options: FetchCacherOptions,
|
||||
) -> Self {
|
||||
Self {
|
||||
emit_cache,
|
||||
file_fetcher,
|
||||
file_header_overrides,
|
||||
fs,
|
||||
global_http_cache,
|
||||
npm_resolver,
|
||||
in_npm_pkg_checker,
|
||||
module_info_cache,
|
||||
permissions,
|
||||
file_header_overrides: options.file_header_overrides,
|
||||
permissions: options.permissions,
|
||||
is_deno_publish: options.is_deno_publish,
|
||||
cache_info_enabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -144,15 +221,7 @@ impl FetchCacher {
|
|||
self.cache_info_enabled = true;
|
||||
}
|
||||
|
||||
// DEPRECATED: Where the file is stored and how it's stored should be an implementation
|
||||
// detail of the cache.
|
||||
//
|
||||
// todo(dsheret): remove once implementing
|
||||
// * https://github.com/denoland/deno/issues/17707
|
||||
// * https://github.com/denoland/deno/issues/17703
|
||||
#[deprecated(
|
||||
note = "There should not be a way to do this because the file may not be cached at a local path in the future."
|
||||
)]
|
||||
/// Only use this for `deno info`.
|
||||
fn get_local_path(&self, specifier: &ModuleSpecifier) -> Option<PathBuf> {
|
||||
// TODO(@kitsonk) fix when deno_graph does not query cache for synthetic
|
||||
// modules
|
||||
|
@ -179,15 +248,7 @@ impl Loader for FetchCacher {
|
|||
#[allow(deprecated)]
|
||||
let local = self.get_local_path(specifier)?;
|
||||
if local.is_file() {
|
||||
let emit = self
|
||||
.emit_cache
|
||||
.get_emit_filepath(specifier)
|
||||
.filter(|p| p.is_file());
|
||||
Some(CacheInfo {
|
||||
local: Some(local),
|
||||
emit,
|
||||
map: None,
|
||||
})
|
||||
Some(CacheInfo { local: Some(local) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -208,19 +269,35 @@ impl Loader for FetchCacher {
|
|||
// symlinked to `/my-project-2/node_modules`), so first we checked if the path
|
||||
// is in a node_modules dir to avoid needlessly canonicalizing, then now compare
|
||||
// against the canonicalized specifier.
|
||||
let specifier =
|
||||
crate::node::resolve_specifier_into_node_modules(specifier);
|
||||
if self.npm_resolver.in_npm_package(&specifier) {
|
||||
let specifier = crate::node::resolve_specifier_into_node_modules(
|
||||
specifier,
|
||||
self.fs.as_ref(),
|
||||
);
|
||||
if self.in_npm_pkg_checker.in_npm_package(&specifier) {
|
||||
return Box::pin(futures::future::ready(Ok(Some(
|
||||
LoadResponse::External { specifier },
|
||||
))));
|
||||
}
|
||||
}
|
||||
|
||||
if self.is_deno_publish
|
||||
&& matches!(specifier.scheme(), "http" | "https")
|
||||
&& !specifier.as_str().starts_with(jsr_url().as_str())
|
||||
{
|
||||
// mark non-JSR remote modules as external so we don't need --allow-import
|
||||
// permissions as these will error out later when publishing
|
||||
return Box::pin(futures::future::ready(Ok(Some(
|
||||
LoadResponse::External {
|
||||
specifier: specifier.clone(),
|
||||
},
|
||||
))));
|
||||
}
|
||||
|
||||
let file_fetcher = self.file_fetcher.clone();
|
||||
let file_header_overrides = self.file_header_overrides.clone();
|
||||
let permissions = self.permissions.clone();
|
||||
let specifier = specifier.clone();
|
||||
let is_statically_analyzable = !options.was_dynamic_root;
|
||||
|
||||
async move {
|
||||
let maybe_cache_setting = match options.cache_setting {
|
||||
|
@ -239,7 +316,12 @@ impl Loader for FetchCacher {
|
|||
.fetch_no_follow_with_options(FetchNoFollowOptions {
|
||||
fetch_options: FetchOptions {
|
||||
specifier: &specifier,
|
||||
permissions: &permissions,
|
||||
permissions: if is_statically_analyzable {
|
||||
FetchPermissionsOptionRef::StaticContainer(&permissions)
|
||||
} else {
|
||||
FetchPermissionsOptionRef::DynamicContainer(&permissions)
|
||||
},
|
||||
maybe_auth: None,
|
||||
maybe_accept: None,
|
||||
maybe_cache_setting: maybe_cache_setting.as_ref(),
|
||||
},
|
||||
|
@ -293,6 +375,7 @@ impl Loader for FetchCacher {
|
|||
fn cache_module_info(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
source: &Arc<[u8]>,
|
||||
module_info: &deno_graph::ModuleInfo,
|
||||
) {
|
||||
|
@ -300,7 +383,7 @@ impl Loader for FetchCacher {
|
|||
let source_hash = CacheDBHash::from_source(source);
|
||||
let result = self.module_info_cache.set_module_info(
|
||||
specifier,
|
||||
MediaType::from_specifier(specifier),
|
||||
media_type,
|
||||
source_hash,
|
||||
module_info,
|
||||
);
|
||||
|
|
140
cli/cache/module_info.rs
vendored
140
cli/cache/module_info.rs
vendored
|
@ -44,18 +44,32 @@ pub static MODULE_INFO_CACHE_DB: CacheDBConfiguration = CacheDBConfiguration {
|
|||
/// A cache of `deno_graph::ModuleInfo` objects. Using this leads to a considerable
|
||||
/// performance improvement because when it exists we can skip parsing a module for
|
||||
/// deno_graph.
|
||||
#[derive(Debug)]
|
||||
pub struct ModuleInfoCache {
|
||||
conn: CacheDB,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
}
|
||||
|
||||
impl ModuleInfoCache {
|
||||
#[cfg(test)]
|
||||
pub fn new_in_memory(version: &'static str) -> Self {
|
||||
Self::new(CacheDB::in_memory(&MODULE_INFO_CACHE_DB, version))
|
||||
pub fn new_in_memory(
|
||||
version: &'static str,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
CacheDB::in_memory(&MODULE_INFO_CACHE_DB, version),
|
||||
parsed_source_cache,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn new(conn: CacheDB) -> Self {
|
||||
Self { conn }
|
||||
pub fn new(
|
||||
conn: CacheDB,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
) -> Self {
|
||||
Self {
|
||||
conn,
|
||||
parsed_source_cache,
|
||||
}
|
||||
}
|
||||
|
||||
/// Useful for testing: re-create this cache DB with a different current version.
|
||||
|
@ -63,6 +77,7 @@ impl ModuleInfoCache {
|
|||
pub(crate) fn recreate_with_version(self, version: &'static str) -> Self {
|
||||
Self {
|
||||
conn: self.conn.recreate_with_version(version),
|
||||
parsed_source_cache: self.parsed_source_cache,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,13 +128,10 @@ impl ModuleInfoCache {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn as_module_analyzer<'a>(
|
||||
&'a self,
|
||||
parsed_source_cache: &'a Arc<ParsedSourceCache>,
|
||||
) -> ModuleInfoCacheModuleAnalyzer<'a> {
|
||||
pub fn as_module_analyzer(&self) -> ModuleInfoCacheModuleAnalyzer {
|
||||
ModuleInfoCacheModuleAnalyzer {
|
||||
module_info_cache: self,
|
||||
parsed_source_cache,
|
||||
parsed_source_cache: &self.parsed_source_cache,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,6 +141,84 @@ pub struct ModuleInfoCacheModuleAnalyzer<'a> {
|
|||
parsed_source_cache: &'a Arc<ParsedSourceCache>,
|
||||
}
|
||||
|
||||
impl<'a> ModuleInfoCacheModuleAnalyzer<'a> {
|
||||
fn load_cached_module_info(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
source_hash: CacheDBHash,
|
||||
) -> Option<ModuleInfo> {
|
||||
match self.module_info_cache.get_module_info(
|
||||
specifier,
|
||||
media_type,
|
||||
source_hash,
|
||||
) {
|
||||
Ok(Some(info)) => Some(info),
|
||||
Ok(None) => None,
|
||||
Err(err) => {
|
||||
log::debug!(
|
||||
"Error loading module cache info for {}. {:#}",
|
||||
specifier,
|
||||
err
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn save_module_info_to_cache(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
source_hash: CacheDBHash,
|
||||
module_info: &ModuleInfo,
|
||||
) {
|
||||
if let Err(err) = self.module_info_cache.set_module_info(
|
||||
specifier,
|
||||
media_type,
|
||||
source_hash,
|
||||
module_info,
|
||||
) {
|
||||
log::debug!(
|
||||
"Error saving module cache info for {}. {:#}",
|
||||
specifier,
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn analyze_sync(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
source: &Arc<str>,
|
||||
) -> Result<ModuleInfo, deno_ast::ParseDiagnostic> {
|
||||
// attempt to load from the cache
|
||||
let source_hash = CacheDBHash::from_source(source);
|
||||
if let Some(info) =
|
||||
self.load_cached_module_info(specifier, media_type, source_hash)
|
||||
{
|
||||
return Ok(info);
|
||||
}
|
||||
|
||||
// otherwise, get the module info from the parsed source cache
|
||||
let parser = self.parsed_source_cache.as_capturing_parser();
|
||||
let analyzer = ParserModuleAnalyzer::new(&parser);
|
||||
let module_info =
|
||||
analyzer.analyze_sync(specifier, source.clone(), media_type)?;
|
||||
|
||||
// then attempt to cache it
|
||||
self.save_module_info_to_cache(
|
||||
specifier,
|
||||
media_type,
|
||||
source_hash,
|
||||
&module_info,
|
||||
);
|
||||
|
||||
Ok(module_info)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl<'a> deno_graph::ModuleAnalyzer for ModuleInfoCacheModuleAnalyzer<'a> {
|
||||
async fn analyze(
|
||||
|
@ -139,20 +229,10 @@ impl<'a> deno_graph::ModuleAnalyzer for ModuleInfoCacheModuleAnalyzer<'a> {
|
|||
) -> Result<ModuleInfo, deno_ast::ParseDiagnostic> {
|
||||
// attempt to load from the cache
|
||||
let source_hash = CacheDBHash::from_source(&source);
|
||||
match self.module_info_cache.get_module_info(
|
||||
specifier,
|
||||
media_type,
|
||||
source_hash,
|
||||
) {
|
||||
Ok(Some(info)) => return Ok(info),
|
||||
Ok(None) => {}
|
||||
Err(err) => {
|
||||
log::debug!(
|
||||
"Error loading module cache info for {}. {:#}",
|
||||
specifier,
|
||||
err
|
||||
);
|
||||
}
|
||||
if let Some(info) =
|
||||
self.load_cached_module_info(specifier, media_type, source_hash)
|
||||
{
|
||||
return Ok(info);
|
||||
}
|
||||
|
||||
// otherwise, get the module info from the parsed source cache
|
||||
|
@ -169,18 +249,12 @@ impl<'a> deno_graph::ModuleAnalyzer for ModuleInfoCacheModuleAnalyzer<'a> {
|
|||
.unwrap()?;
|
||||
|
||||
// then attempt to cache it
|
||||
if let Err(err) = self.module_info_cache.set_module_info(
|
||||
self.save_module_info_to_cache(
|
||||
specifier,
|
||||
media_type,
|
||||
source_hash,
|
||||
&module_info,
|
||||
) {
|
||||
log::debug!(
|
||||
"Error saving module cache info for {}. {:#}",
|
||||
specifier,
|
||||
err
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Ok(module_info)
|
||||
}
|
||||
|
@ -202,7 +276,7 @@ fn serialize_media_type(media_type: MediaType) -> i64 {
|
|||
Tsx => 11,
|
||||
Json => 12,
|
||||
Wasm => 13,
|
||||
TsBuildInfo => 14,
|
||||
Css => 14,
|
||||
SourceMap => 15,
|
||||
Unknown => 16,
|
||||
}
|
||||
|
@ -217,7 +291,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
pub fn module_info_cache_general_use() {
|
||||
let cache = ModuleInfoCache::new_in_memory("1.0.0");
|
||||
let cache = ModuleInfoCache::new_in_memory("1.0.0", Default::default());
|
||||
let specifier1 =
|
||||
ModuleSpecifier::parse("https://localhost/mod.ts").unwrap();
|
||||
let specifier2 =
|
||||
|
|
20
cli/cache/parsed_source.rs
vendored
20
cli/cache/parsed_source.rs
vendored
|
@ -7,9 +7,9 @@ use deno_ast::MediaType;
|
|||
use deno_ast::ModuleSpecifier;
|
||||
use deno_ast::ParsedSource;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_graph::CapturingModuleParser;
|
||||
use deno_graph::DefaultModuleParser;
|
||||
use deno_graph::ModuleParser;
|
||||
use deno_graph::CapturingEsParser;
|
||||
use deno_graph::DefaultEsParser;
|
||||
use deno_graph::EsParser;
|
||||
use deno_graph::ParseOptions;
|
||||
use deno_graph::ParsedSourceStore;
|
||||
|
||||
|
@ -46,7 +46,7 @@ impl<'a> LazyGraphSourceParser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ParsedSourceCache {
|
||||
sources: Mutex<HashMap<ModuleSpecifier, ParsedSource>>,
|
||||
}
|
||||
|
@ -57,12 +57,11 @@ impl ParsedSourceCache {
|
|||
module: &deno_graph::JsModule,
|
||||
) -> Result<ParsedSource, deno_ast::ParseDiagnostic> {
|
||||
let parser = self.as_capturing_parser();
|
||||
// this will conditionally parse because it's using a CapturingModuleParser
|
||||
parser.parse_module(ParseOptions {
|
||||
// this will conditionally parse because it's using a CapturingEsParser
|
||||
parser.parse_program(ParseOptions {
|
||||
specifier: &module.specifier,
|
||||
source: module.source.clone(),
|
||||
media_type: module.media_type,
|
||||
// don't bother enabling because this method is currently only used for vendoring
|
||||
scope_analysis: false,
|
||||
})
|
||||
}
|
||||
|
@ -86,10 +85,9 @@ impl ParsedSourceCache {
|
|||
specifier,
|
||||
source,
|
||||
media_type,
|
||||
// don't bother enabling because this method is currently only used for emitting
|
||||
scope_analysis: false,
|
||||
};
|
||||
DefaultModuleParser.parse_module(options)
|
||||
DefaultEsParser.parse_program(options)
|
||||
}
|
||||
|
||||
/// Frees the parsed source from memory.
|
||||
|
@ -99,8 +97,8 @@ impl ParsedSourceCache {
|
|||
|
||||
/// Creates a parser that will reuse a ParsedSource from the store
|
||||
/// if it exists, or else parse.
|
||||
pub fn as_capturing_parser(&self) -> CapturingModuleParser {
|
||||
CapturingModuleParser::new(None, self)
|
||||
pub fn as_capturing_parser(&self) -> CapturingEsParser {
|
||||
CapturingEsParser::new(None, self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
disallowed-methods = [
|
||||
{ path = "reqwest::Client::new", reason = "create an HttpClient via an HttpClientProvider instead" },
|
||||
{ path = "std::process::exit", reason = "use deno_runtime::exit instead" },
|
||||
]
|
||||
disallowed-types = [
|
||||
{ path = "reqwest::Client", reason = "use crate::http_util::HttpClient instead" },
|
||||
]
|
||||
ignore-interior-mutability = [
|
||||
"lsp_types::Uri",
|
||||
]
|
||||
|
|
203
cli/emit.rs
203
cli/emit.rs
|
@ -3,22 +3,29 @@
|
|||
use crate::cache::EmitCache;
|
||||
use crate::cache::FastInsecureHasher;
|
||||
use crate::cache::ParsedSourceCache;
|
||||
use crate::resolver::CjsTracker;
|
||||
|
||||
use deno_ast::ModuleKind;
|
||||
use deno_ast::SourceMapOption;
|
||||
use deno_ast::SourceRange;
|
||||
use deno_ast::SourceRanged;
|
||||
use deno_ast::SourceRangedForSpanned;
|
||||
use deno_ast::TranspileModuleOptions;
|
||||
use deno_ast::TranspileResult;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::stream::FuturesUnordered;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::futures::StreamExt;
|
||||
use deno_core::ModuleCodeBytes;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_graph::MediaType;
|
||||
use deno_graph::Module;
|
||||
use deno_graph::ModuleGraph;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Emitter {
|
||||
emit_cache: EmitCache,
|
||||
cjs_tracker: Arc<CjsTracker>,
|
||||
emit_cache: Arc<EmitCache>,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
transpile_and_emit_options:
|
||||
Arc<(deno_ast::TranspileOptions, deno_ast::EmitOptions)>,
|
||||
|
@ -28,7 +35,8 @@ pub struct Emitter {
|
|||
|
||||
impl Emitter {
|
||||
pub fn new(
|
||||
emit_cache: EmitCache,
|
||||
cjs_tracker: Arc<CjsTracker>,
|
||||
emit_cache: Arc<EmitCache>,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
transpile_options: deno_ast::TranspileOptions,
|
||||
emit_options: deno_ast::EmitOptions,
|
||||
|
@ -40,6 +48,7 @@ impl Emitter {
|
|||
hasher.finish()
|
||||
};
|
||||
Self {
|
||||
cjs_tracker,
|
||||
emit_cache,
|
||||
parsed_source_cache,
|
||||
transpile_and_emit_options: Arc::new((transpile_options, emit_options)),
|
||||
|
@ -57,20 +66,19 @@ impl Emitter {
|
|||
continue;
|
||||
};
|
||||
|
||||
let is_emittable = matches!(
|
||||
module.media_type,
|
||||
MediaType::TypeScript
|
||||
| MediaType::Mts
|
||||
| MediaType::Cts
|
||||
| MediaType::Jsx
|
||||
| MediaType::Tsx
|
||||
);
|
||||
if is_emittable {
|
||||
if module.media_type.is_emittable() {
|
||||
futures.push(
|
||||
self
|
||||
.emit_parsed_source(
|
||||
&module.specifier,
|
||||
module.media_type,
|
||||
ModuleKind::from_is_cjs(
|
||||
self.cjs_tracker.is_cjs_with_known_is_script(
|
||||
&module.specifier,
|
||||
module.media_type,
|
||||
module.is_script,
|
||||
)?,
|
||||
),
|
||||
&module.source,
|
||||
)
|
||||
.boxed_local(),
|
||||
|
@ -89,9 +97,10 @@ impl Emitter {
|
|||
pub fn maybe_cached_emit(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
module_kind: deno_ast::ModuleKind,
|
||||
source: &str,
|
||||
) -> Option<Vec<u8>> {
|
||||
let source_hash = self.get_source_hash(source);
|
||||
) -> Option<String> {
|
||||
let source_hash = self.get_source_hash(module_kind, source);
|
||||
self.emit_cache.get_emit_code(specifier, source_hash)
|
||||
}
|
||||
|
||||
|
@ -99,25 +108,27 @@ impl Emitter {
|
|||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
module_kind: deno_ast::ModuleKind,
|
||||
source: &Arc<str>,
|
||||
) -> Result<ModuleCodeBytes, AnyError> {
|
||||
) -> Result<String, AnyError> {
|
||||
// Note: keep this in sync with the sync version below
|
||||
let helper = EmitParsedSourceHelper(self);
|
||||
match helper.pre_emit_parsed_source(specifier, source) {
|
||||
match helper.pre_emit_parsed_source(specifier, module_kind, source) {
|
||||
PreEmitResult::Cached(emitted_text) => Ok(emitted_text),
|
||||
PreEmitResult::NotCached { source_hash } => {
|
||||
let parsed_source_cache = self.parsed_source_cache.clone();
|
||||
let transpile_and_emit_options =
|
||||
self.transpile_and_emit_options.clone();
|
||||
let transpile_result = deno_core::unsync::spawn_blocking({
|
||||
let transpiled_source = deno_core::unsync::spawn_blocking({
|
||||
let specifier = specifier.clone();
|
||||
let source = source.clone();
|
||||
move || -> Result<_, AnyError> {
|
||||
EmitParsedSourceHelper::transpile(
|
||||
&parsed_source_cache,
|
||||
&specifier,
|
||||
source.clone(),
|
||||
media_type,
|
||||
module_kind,
|
||||
source.clone(),
|
||||
&transpile_and_emit_options.0,
|
||||
&transpile_and_emit_options.1,
|
||||
)
|
||||
|
@ -125,11 +136,12 @@ impl Emitter {
|
|||
})
|
||||
.await
|
||||
.unwrap()?;
|
||||
Ok(helper.post_emit_parsed_source(
|
||||
helper.post_emit_parsed_source(
|
||||
specifier,
|
||||
transpile_result,
|
||||
&transpiled_source,
|
||||
source_hash,
|
||||
))
|
||||
);
|
||||
Ok(transpiled_source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,26 +150,29 @@ impl Emitter {
|
|||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
module_kind: deno_ast::ModuleKind,
|
||||
source: &Arc<str>,
|
||||
) -> Result<ModuleCodeBytes, AnyError> {
|
||||
) -> Result<String, AnyError> {
|
||||
// Note: keep this in sync with the async version above
|
||||
let helper = EmitParsedSourceHelper(self);
|
||||
match helper.pre_emit_parsed_source(specifier, source) {
|
||||
match helper.pre_emit_parsed_source(specifier, module_kind, source) {
|
||||
PreEmitResult::Cached(emitted_text) => Ok(emitted_text),
|
||||
PreEmitResult::NotCached { source_hash } => {
|
||||
let transpile_result = EmitParsedSourceHelper::transpile(
|
||||
let transpiled_source = EmitParsedSourceHelper::transpile(
|
||||
&self.parsed_source_cache,
|
||||
specifier,
|
||||
source.clone(),
|
||||
media_type,
|
||||
module_kind,
|
||||
source.clone(),
|
||||
&self.transpile_and_emit_options.0,
|
||||
&self.transpile_and_emit_options.1,
|
||||
)?;
|
||||
Ok(helper.post_emit_parsed_source(
|
||||
helper.post_emit_parsed_source(
|
||||
specifier,
|
||||
transpile_result,
|
||||
&transpiled_source,
|
||||
source_hash,
|
||||
))
|
||||
);
|
||||
Ok(transpiled_source)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -187,10 +202,20 @@ impl Emitter {
|
|||
// this statement is probably wrong)
|
||||
let mut options = self.transpile_and_emit_options.1.clone();
|
||||
options.source_map = SourceMapOption::None;
|
||||
let is_cjs = self.cjs_tracker.is_cjs_with_known_is_script(
|
||||
specifier,
|
||||
media_type,
|
||||
parsed_source.compute_is_script(),
|
||||
)?;
|
||||
let transpiled_source = parsed_source
|
||||
.transpile(&self.transpile_and_emit_options.0, &options)?
|
||||
.into_source()
|
||||
.into_string()?;
|
||||
.transpile(
|
||||
&self.transpile_and_emit_options.0,
|
||||
&deno_ast::TranspileModuleOptions {
|
||||
module_kind: Some(ModuleKind::from_is_cjs(is_cjs)),
|
||||
},
|
||||
&options,
|
||||
)?
|
||||
.into_source();
|
||||
Ok(transpiled_source.text)
|
||||
}
|
||||
MediaType::JavaScript
|
||||
|
@ -201,7 +226,7 @@ impl Emitter {
|
|||
| MediaType::Dcts
|
||||
| MediaType::Json
|
||||
| MediaType::Wasm
|
||||
| MediaType::TsBuildInfo
|
||||
| MediaType::Css
|
||||
| MediaType::SourceMap
|
||||
| MediaType::Unknown => {
|
||||
// clear this specifier from the parsed source cache as it's now out of date
|
||||
|
@ -214,16 +239,17 @@ impl Emitter {
|
|||
/// A hashing function that takes the source code and uses the global emit
|
||||
/// options then generates a string hash which can be stored to
|
||||
/// determine if the cached emit is valid or not.
|
||||
fn get_source_hash(&self, source_text: &str) -> u64 {
|
||||
fn get_source_hash(&self, module_kind: ModuleKind, source_text: &str) -> u64 {
|
||||
FastInsecureHasher::new_without_deno_version() // stored in the transpile_and_emit_options_hash
|
||||
.write_str(source_text)
|
||||
.write_u64(self.transpile_and_emit_options_hash)
|
||||
.write_hashable(module_kind)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
enum PreEmitResult {
|
||||
Cached(ModuleCodeBytes),
|
||||
Cached(String),
|
||||
NotCached { source_hash: u64 },
|
||||
}
|
||||
|
||||
|
@ -234,14 +260,15 @@ impl<'a> EmitParsedSourceHelper<'a> {
|
|||
pub fn pre_emit_parsed_source(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
module_kind: deno_ast::ModuleKind,
|
||||
source: &Arc<str>,
|
||||
) -> PreEmitResult {
|
||||
let source_hash = self.0.get_source_hash(source);
|
||||
let source_hash = self.0.get_source_hash(module_kind, source);
|
||||
|
||||
if let Some(emit_code) =
|
||||
self.0.emit_cache.get_emit_code(specifier, source_hash)
|
||||
{
|
||||
PreEmitResult::Cached(emit_code.into_boxed_slice().into())
|
||||
PreEmitResult::Cached(emit_code)
|
||||
} else {
|
||||
PreEmitResult::NotCached { source_hash }
|
||||
}
|
||||
|
@ -250,24 +277,24 @@ impl<'a> EmitParsedSourceHelper<'a> {
|
|||
pub fn transpile(
|
||||
parsed_source_cache: &ParsedSourceCache,
|
||||
specifier: &ModuleSpecifier,
|
||||
source: Arc<str>,
|
||||
media_type: MediaType,
|
||||
module_kind: deno_ast::ModuleKind,
|
||||
source: Arc<str>,
|
||||
transpile_options: &deno_ast::TranspileOptions,
|
||||
emit_options: &deno_ast::EmitOptions,
|
||||
) -> Result<TranspileResult, AnyError> {
|
||||
) -> Result<String, AnyError> {
|
||||
// nothing else needs the parsed source at this point, so remove from
|
||||
// the cache in order to not transpile owned
|
||||
let parsed_source = parsed_source_cache
|
||||
.remove_or_parse_module(specifier, source, media_type)?;
|
||||
Ok(parsed_source.transpile(transpile_options, emit_options)?)
|
||||
}
|
||||
|
||||
pub fn post_emit_parsed_source(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
transpile_result: TranspileResult,
|
||||
source_hash: u64,
|
||||
) -> ModuleCodeBytes {
|
||||
ensure_no_import_assertion(&parsed_source)?;
|
||||
let transpile_result = parsed_source.transpile(
|
||||
transpile_options,
|
||||
&TranspileModuleOptions {
|
||||
module_kind: Some(module_kind),
|
||||
},
|
||||
emit_options,
|
||||
)?;
|
||||
let transpiled_source = match transpile_result {
|
||||
TranspileResult::Owned(source) => source,
|
||||
TranspileResult::Cloned(source) => {
|
||||
|
@ -276,11 +303,89 @@ impl<'a> EmitParsedSourceHelper<'a> {
|
|||
}
|
||||
};
|
||||
debug_assert!(transpiled_source.source_map.is_none());
|
||||
Ok(transpiled_source.text)
|
||||
}
|
||||
|
||||
pub fn post_emit_parsed_source(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
transpiled_source: &str,
|
||||
source_hash: u64,
|
||||
) {
|
||||
self.0.emit_cache.set_emit_code(
|
||||
specifier,
|
||||
source_hash,
|
||||
&transpiled_source.source,
|
||||
transpiled_source.as_bytes(),
|
||||
);
|
||||
transpiled_source.source.into_boxed_slice().into()
|
||||
}
|
||||
}
|
||||
|
||||
// todo(dsherret): this is a temporary measure until we have swc erroring for this
|
||||
fn ensure_no_import_assertion(
|
||||
parsed_source: &deno_ast::ParsedSource,
|
||||
) -> Result<(), AnyError> {
|
||||
fn has_import_assertion(text: &str) -> bool {
|
||||
// good enough
|
||||
text.contains(" assert ") && !text.contains(" with ")
|
||||
}
|
||||
|
||||
fn create_err(
|
||||
parsed_source: &deno_ast::ParsedSource,
|
||||
range: SourceRange,
|
||||
) -> AnyError {
|
||||
let text_info = parsed_source.text_info_lazy();
|
||||
let loc = text_info.line_and_column_display(range.start);
|
||||
let mut msg = "Import assertions are deprecated. Use `with` keyword, instead of 'assert' keyword.".to_string();
|
||||
msg.push_str("\n\n");
|
||||
msg.push_str(range.text_fast(text_info));
|
||||
msg.push_str("\n\n");
|
||||
msg.push_str(&format!(
|
||||
" at {}:{}:{}\n",
|
||||
parsed_source.specifier(),
|
||||
loc.line_number,
|
||||
loc.column_number,
|
||||
));
|
||||
deno_core::anyhow::anyhow!("{}", msg)
|
||||
}
|
||||
|
||||
let deno_ast::ProgramRef::Module(module) = parsed_source.program_ref() else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
for item in &module.body {
|
||||
match item {
|
||||
deno_ast::swc::ast::ModuleItem::ModuleDecl(decl) => match decl {
|
||||
deno_ast::swc::ast::ModuleDecl::Import(n) => {
|
||||
if n.with.is_some()
|
||||
&& has_import_assertion(n.text_fast(parsed_source.text_info_lazy()))
|
||||
{
|
||||
return Err(create_err(parsed_source, n.range()));
|
||||
}
|
||||
}
|
||||
deno_ast::swc::ast::ModuleDecl::ExportAll(n) => {
|
||||
if n.with.is_some()
|
||||
&& has_import_assertion(n.text_fast(parsed_source.text_info_lazy()))
|
||||
{
|
||||
return Err(create_err(parsed_source, n.range()));
|
||||
}
|
||||
}
|
||||
deno_ast::swc::ast::ModuleDecl::ExportNamed(n) => {
|
||||
if n.with.is_some()
|
||||
&& has_import_assertion(n.text_fast(parsed_source.text_info_lazy()))
|
||||
{
|
||||
return Err(create_err(parsed_source, n.range()));
|
||||
}
|
||||
}
|
||||
deno_ast::swc::ast::ModuleDecl::ExportDecl(_)
|
||||
| deno_ast::swc::ast::ModuleDecl::ExportDefaultDecl(_)
|
||||
| deno_ast::swc::ast::ModuleDecl::ExportDefaultExpr(_)
|
||||
| deno_ast::swc::ast::ModuleDecl::TsImportEquals(_)
|
||||
| deno_ast::swc::ast::ModuleDecl::TsExportAssignment(_)
|
||||
| deno_ast::swc::ast::ModuleDecl::TsNamespaceExport(_) => {}
|
||||
},
|
||||
deno_ast::swc::ast::ModuleItem::Stmt(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ use deno_graph::ModuleGraphError;
|
|||
use deno_graph::ModuleLoadError;
|
||||
use deno_graph::ResolutionError;
|
||||
use import_map::ImportMapError;
|
||||
use std::fmt::Write;
|
||||
|
||||
fn get_import_map_error_class(_: &ImportMapError) -> &'static str {
|
||||
"URIError"
|
||||
|
@ -30,7 +29,6 @@ fn get_diagnostic_class(_: &ParseDiagnostic) -> &'static str {
|
|||
fn get_module_graph_error_class(err: &ModuleGraphError) -> &'static str {
|
||||
use deno_graph::JsrLoadError;
|
||||
use deno_graph::NpmLoadError;
|
||||
use deno_graph::WorkspaceLoadError;
|
||||
|
||||
match err {
|
||||
ModuleGraphError::ResolutionError(err)
|
||||
|
@ -40,6 +38,7 @@ fn get_module_graph_error_class(err: &ModuleGraphError) -> &'static str {
|
|||
ModuleGraphError::ModuleError(err) => match err {
|
||||
ModuleError::InvalidTypeAssertion { .. } => "SyntaxError",
|
||||
ModuleError::ParseErr(_, diagnostic) => get_diagnostic_class(diagnostic),
|
||||
ModuleError::WasmParseErr(..) => "SyntaxError",
|
||||
ModuleError::UnsupportedMediaType { .. }
|
||||
| ModuleError::UnsupportedImportAttributeType { .. } => "TypeError",
|
||||
ModuleError::Missing(_, _) | ModuleError::MissingDynamic(_, _) => {
|
||||
|
@ -72,10 +71,6 @@ fn get_module_graph_error_class(err: &ModuleGraphError) -> &'static str {
|
|||
| JsrLoadError::PackageVersionNotFound(_)
|
||||
| JsrLoadError::UnknownExport { .. } => "NotFound",
|
||||
},
|
||||
ModuleLoadError::Workspace(err) => match err {
|
||||
WorkspaceLoadError::MemberInvalidExportPath { .. } => "TypeError",
|
||||
WorkspaceLoadError::MissingMemberExports { .. } => "NotFound",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -94,6 +89,10 @@ fn get_resolution_error_class(err: &ResolutionError) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
fn get_try_from_int_error_class(_: &std::num::TryFromIntError) -> &'static str {
|
||||
"TypeError"
|
||||
}
|
||||
|
||||
pub fn get_error_class_name(e: &AnyError) -> &'static str {
|
||||
deno_runtime::errors::get_error_class_name(e)
|
||||
.or_else(|| {
|
||||
|
@ -112,17 +111,9 @@ pub fn get_error_class_name(e: &AnyError) -> &'static str {
|
|||
e.downcast_ref::<ResolutionError>()
|
||||
.map(get_resolution_error_class)
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
if cfg!(debug) {
|
||||
log::warn!(
|
||||
"Error '{}' contains boxed error of unknown type:{}",
|
||||
e,
|
||||
e.chain().fold(String::new(), |mut output, e| {
|
||||
let _ = write!(output, "\n {e:?}");
|
||||
output
|
||||
})
|
||||
);
|
||||
}
|
||||
"Error"
|
||||
.or_else(|| {
|
||||
e.downcast_ref::<std::num::TryFromIntError>()
|
||||
.map(get_try_from_int_error_class)
|
||||
})
|
||||
.unwrap_or("Error")
|
||||
}
|
||||
|
|
498
cli/factory.rs
498
cli/factory.rs
|
@ -1,15 +1,17 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::args::check_warn_tsconfig;
|
||||
use crate::args::get_root_cert_store;
|
||||
use crate::args::CaData;
|
||||
use crate::args::CliOptions;
|
||||
use crate::args::DenoSubcommand;
|
||||
use crate::args::Flags;
|
||||
use crate::args::PackageJsonInstallDepsProvider;
|
||||
use crate::args::NpmInstallDepsProvider;
|
||||
use crate::args::StorageKeyResolver;
|
||||
use crate::args::TsConfigType;
|
||||
use crate::cache::Caches;
|
||||
use crate::cache::CodeCache;
|
||||
use crate::cache::DenoCacheEnvFsAdapter;
|
||||
use crate::cache::DenoDir;
|
||||
use crate::cache::DenoDirProvider;
|
||||
use crate::cache::EmitCache;
|
||||
|
@ -31,22 +33,30 @@ use crate::module_loader::ModuleLoadPreparer;
|
|||
use crate::node::CliCjsCodeAnalyzer;
|
||||
use crate::node::CliNodeCodeTranslator;
|
||||
use crate::npm::create_cli_npm_resolver;
|
||||
use crate::npm::create_in_npm_pkg_checker;
|
||||
use crate::npm::CliByonmNpmResolverCreateOptions;
|
||||
use crate::npm::CliManagedInNpmPkgCheckerCreateOptions;
|
||||
use crate::npm::CliManagedNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::CliNpmResolverByonmCreateOptions;
|
||||
use crate::npm::CliNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||
use crate::resolver::CjsResolutionStore;
|
||||
use crate::resolver::CliGraphResolver;
|
||||
use crate::resolver::CliGraphResolverOptions;
|
||||
use crate::resolver::CliNodeResolver;
|
||||
use crate::npm::CreateInNpmPkgCheckerOptions;
|
||||
use crate::resolver::CjsTracker;
|
||||
use crate::resolver::CliDenoResolver;
|
||||
use crate::resolver::CliDenoResolverFs;
|
||||
use crate::resolver::CliNpmReqResolver;
|
||||
use crate::resolver::CliResolver;
|
||||
use crate::resolver::CliResolverOptions;
|
||||
use crate::resolver::CliSloppyImportsResolver;
|
||||
use crate::resolver::IsCjsResolverOptions;
|
||||
use crate::resolver::NpmModuleLoader;
|
||||
use crate::resolver::SloppyImportsResolver;
|
||||
use crate::resolver::SloppyImportsCachedFs;
|
||||
use crate::standalone::DenoCompileBinaryWriter;
|
||||
use crate::tools::check::TypeChecker;
|
||||
use crate::tools::coverage::CoverageCollector;
|
||||
use crate::tools::lint::LintRuleProvider;
|
||||
use crate::tools::run::hmr::HmrRunner;
|
||||
use crate::tsc::TypeCheckingCjsTracker;
|
||||
use crate::util::file_watcher::WatcherCommunicator;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
|
@ -55,21 +65,30 @@ use crate::worker::CliMainWorkerFactory;
|
|||
use crate::worker::CliMainWorkerOptions;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use deno_cache_dir::npm::NpmCacheDir;
|
||||
use deno_config::workspace::PackageJsonDepResolution;
|
||||
use deno_config::workspace::WorkspaceResolver;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::FeatureChecker;
|
||||
|
||||
use deno_resolver::npm::NpmReqResolverOptions;
|
||||
use deno_resolver::DenoResolverOptions;
|
||||
use deno_resolver::NodeAndNpmReqResolver;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
|
||||
use deno_runtime::deno_node::NodeResolver;
|
||||
use deno_runtime::deno_node::PackageJsonResolver;
|
||||
use deno_runtime::deno_permissions::Permissions;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use deno_runtime::deno_tls::rustls::RootCertStore;
|
||||
use deno_runtime::deno_tls::RootCertStoreProvider;
|
||||
use deno_runtime::deno_web::BlobStore;
|
||||
use deno_runtime::inspector_server::InspectorServer;
|
||||
use deno_runtime::permissions::RuntimePermissionDescriptorParser;
|
||||
use log::warn;
|
||||
use node_resolver::analyze::NodeCodeTranslator;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::future::Future;
|
||||
use std::sync::Arc;
|
||||
|
@ -111,7 +130,7 @@ impl RootCertStoreProvider for CliRootCertStoreProvider {
|
|||
}
|
||||
}
|
||||
|
||||
struct Deferred<T>(once_cell::unsync::OnceCell<T>);
|
||||
pub struct Deferred<T>(once_cell::unsync::OnceCell<T>);
|
||||
|
||||
impl<T> Default for Deferred<T> {
|
||||
fn default() -> Self {
|
||||
|
@ -157,37 +176,42 @@ impl<T> Deferred<T> {
|
|||
|
||||
#[derive(Default)]
|
||||
struct CliFactoryServices {
|
||||
cli_options: Deferred<Arc<CliOptions>>,
|
||||
deno_dir_provider: Deferred<Arc<DenoDirProvider>>,
|
||||
blob_store: Deferred<Arc<BlobStore>>,
|
||||
caches: Deferred<Arc<Caches>>,
|
||||
cjs_tracker: Deferred<Arc<CjsTracker>>,
|
||||
cli_options: Deferred<Arc<CliOptions>>,
|
||||
code_cache: Deferred<Arc<CodeCache>>,
|
||||
deno_resolver: Deferred<Arc<CliDenoResolver>>,
|
||||
emit_cache: Deferred<Arc<EmitCache>>,
|
||||
emitter: Deferred<Arc<Emitter>>,
|
||||
feature_checker: Deferred<Arc<FeatureChecker>>,
|
||||
file_fetcher: Deferred<Arc<FileFetcher>>,
|
||||
fs: Deferred<Arc<dyn deno_fs::FileSystem>>,
|
||||
global_http_cache: Deferred<Arc<GlobalHttpCache>>,
|
||||
http_cache: Deferred<Arc<dyn HttpCache>>,
|
||||
http_client_provider: Deferred<Arc<HttpClientProvider>>,
|
||||
emit_cache: Deferred<EmitCache>,
|
||||
emitter: Deferred<Arc<Emitter>>,
|
||||
fs: Deferred<Arc<dyn deno_fs::FileSystem>>,
|
||||
in_npm_pkg_checker: Deferred<Arc<dyn InNpmPackageChecker>>,
|
||||
main_graph_container: Deferred<Arc<MainModuleGraphContainer>>,
|
||||
maybe_inspector_server: Deferred<Option<Arc<InspectorServer>>>,
|
||||
root_cert_store_provider: Deferred<Arc<dyn RootCertStoreProvider>>,
|
||||
blob_store: Deferred<Arc<BlobStore>>,
|
||||
module_info_cache: Deferred<Arc<ModuleInfoCache>>,
|
||||
parsed_source_cache: Deferred<Arc<ParsedSourceCache>>,
|
||||
resolver: Deferred<Arc<CliGraphResolver>>,
|
||||
maybe_file_watcher_reporter: Deferred<Option<FileWatcherReporter>>,
|
||||
maybe_inspector_server: Deferred<Option<Arc<InspectorServer>>>,
|
||||
module_graph_builder: Deferred<Arc<ModuleGraphBuilder>>,
|
||||
module_graph_creator: Deferred<Arc<ModuleGraphCreator>>,
|
||||
module_info_cache: Deferred<Arc<ModuleInfoCache>>,
|
||||
module_load_preparer: Deferred<Arc<ModuleLoadPreparer>>,
|
||||
node_code_translator: Deferred<Arc<CliNodeCodeTranslator>>,
|
||||
node_resolver: Deferred<Arc<NodeResolver>>,
|
||||
npm_cache_dir: Deferred<Arc<NpmCacheDir>>,
|
||||
npm_req_resolver: Deferred<Arc<CliNpmReqResolver>>,
|
||||
npm_resolver: Deferred<Arc<dyn CliNpmResolver>>,
|
||||
sloppy_imports_resolver: Deferred<Option<Arc<SloppyImportsResolver>>>,
|
||||
parsed_source_cache: Deferred<Arc<ParsedSourceCache>>,
|
||||
permission_desc_parser: Deferred<Arc<RuntimePermissionDescriptorParser>>,
|
||||
pkg_json_resolver: Deferred<Arc<PackageJsonResolver>>,
|
||||
resolver: Deferred<Arc<CliResolver>>,
|
||||
root_cert_store_provider: Deferred<Arc<dyn RootCertStoreProvider>>,
|
||||
root_permissions_container: Deferred<PermissionsContainer>,
|
||||
sloppy_imports_resolver: Deferred<Option<Arc<CliSloppyImportsResolver>>>,
|
||||
text_only_progress_bar: Deferred<ProgressBar>,
|
||||
type_checker: Deferred<Arc<TypeChecker>>,
|
||||
cjs_resolutions: Deferred<Arc<CjsResolutionStore>>,
|
||||
cli_node_resolver: Deferred<Arc<CliNodeResolver>>,
|
||||
feature_checker: Deferred<Arc<FeatureChecker>>,
|
||||
code_cache: Deferred<Arc<CodeCache>>,
|
||||
workspace_resolver: Deferred<Arc<WorkspaceResolver>>,
|
||||
}
|
||||
|
||||
|
@ -236,11 +260,7 @@ impl CliFactory {
|
|||
}
|
||||
|
||||
pub fn deno_dir_provider(&self) -> Result<&Arc<DenoDirProvider>, AnyError> {
|
||||
self.services.deno_dir_provider.get_or_try_init(|| {
|
||||
Ok(Arc::new(DenoDirProvider::new(
|
||||
self.cli_options()?.maybe_custom_root().clone(),
|
||||
)))
|
||||
})
|
||||
Ok(&self.cli_options()?.deno_dir_provider)
|
||||
}
|
||||
|
||||
pub fn deno_dir(&self) -> Result<&DenoDir, AnyError> {
|
||||
|
@ -298,7 +318,7 @@ impl CliFactory {
|
|||
pub fn global_http_cache(&self) -> Result<&Arc<GlobalHttpCache>, AnyError> {
|
||||
self.services.global_http_cache.get_or_try_init(|| {
|
||||
Ok(Arc::new(GlobalHttpCache::new(
|
||||
self.deno_dir()?.deps_folder_path(),
|
||||
self.deno_dir()?.remote_folder_path(),
|
||||
crate::cache::RealDenoCacheEnv,
|
||||
)))
|
||||
})
|
||||
|
@ -309,8 +329,11 @@ impl CliFactory {
|
|||
let global_cache = self.global_http_cache()?.clone();
|
||||
match self.cli_options()?.vendor_dir_path() {
|
||||
Some(local_path) => {
|
||||
let local_cache =
|
||||
LocalHttpCache::new(local_path.clone(), global_cache);
|
||||
let local_cache = LocalHttpCache::new(
|
||||
local_path.clone(),
|
||||
global_cache,
|
||||
deno_cache_dir::GlobalToLocalCopy::Allow,
|
||||
);
|
||||
Ok(Arc::new(local_cache))
|
||||
}
|
||||
None => Ok(global_cache),
|
||||
|
@ -345,74 +368,127 @@ impl CliFactory {
|
|||
self.services.fs.get_or_init(|| Arc::new(deno_fs::RealFs))
|
||||
}
|
||||
|
||||
pub fn in_npm_pkg_checker(
|
||||
&self,
|
||||
) -> Result<&Arc<dyn InNpmPackageChecker>, AnyError> {
|
||||
self.services.in_npm_pkg_checker.get_or_try_init(|| {
|
||||
let cli_options = self.cli_options()?;
|
||||
let options = if cli_options.use_byonm() {
|
||||
CreateInNpmPkgCheckerOptions::Byonm
|
||||
} else {
|
||||
CreateInNpmPkgCheckerOptions::Managed(
|
||||
CliManagedInNpmPkgCheckerCreateOptions {
|
||||
root_cache_dir_url: self.npm_cache_dir()?.root_dir_url(),
|
||||
maybe_node_modules_path: cli_options
|
||||
.node_modules_dir_path()
|
||||
.map(|p| p.as_path()),
|
||||
},
|
||||
)
|
||||
};
|
||||
Ok(create_in_npm_pkg_checker(options))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn npm_cache_dir(&self) -> Result<&Arc<NpmCacheDir>, AnyError> {
|
||||
self.services.npm_cache_dir.get_or_try_init(|| {
|
||||
let fs = self.fs();
|
||||
let global_path = self.deno_dir()?.npm_folder_path();
|
||||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(NpmCacheDir::new(
|
||||
&DenoCacheEnvFsAdapter(fs.as_ref()),
|
||||
global_path,
|
||||
cli_options.npmrc().get_all_known_registries_urls(),
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn npm_resolver(
|
||||
&self,
|
||||
) -> Result<&Arc<dyn CliNpmResolver>, AnyError> {
|
||||
self
|
||||
.services
|
||||
.npm_resolver
|
||||
.get_or_try_init_async(async {
|
||||
let fs = self.fs();
|
||||
let cli_options = self.cli_options()?;
|
||||
// For `deno install` we want to force the managed resolver so it can set up `node_modules/` directory.
|
||||
create_cli_npm_resolver(if cli_options.use_byonm() && !matches!(cli_options.sub_command(), DenoSubcommand::Install(_)) {
|
||||
CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions {
|
||||
fs: fs.clone(),
|
||||
root_node_modules_dir: Some(match cli_options.node_modules_dir_path() {
|
||||
Some(node_modules_path) => node_modules_path.to_path_buf(),
|
||||
// path needs to be canonicalized for node resolution
|
||||
// (node_modules_dir_path above is already canonicalized)
|
||||
None => canonicalize_path_maybe_not_exists(cli_options.initial_cwd())?
|
||||
.join("node_modules"),
|
||||
}),
|
||||
})
|
||||
} else {
|
||||
CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions {
|
||||
snapshot: match cli_options.resolve_npm_resolution_snapshot()? {
|
||||
Some(snapshot) => {
|
||||
CliNpmResolverManagedSnapshotOption::Specified(Some(snapshot))
|
||||
}
|
||||
None => match cli_options.maybe_lockfile() {
|
||||
Some(lockfile) => {
|
||||
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
|
||||
lockfile.clone(),
|
||||
)
|
||||
}
|
||||
None => CliNpmResolverManagedSnapshotOption::Specified(None),
|
||||
.get_or_try_init_async(
|
||||
async {
|
||||
let fs = self.fs();
|
||||
let cli_options = self.cli_options()?;
|
||||
create_cli_npm_resolver(if cli_options.use_byonm() {
|
||||
CliNpmResolverCreateOptions::Byonm(
|
||||
CliByonmNpmResolverCreateOptions {
|
||||
fs: CliDenoResolverFs(fs.clone()),
|
||||
pkg_json_resolver: self.pkg_json_resolver().clone(),
|
||||
root_node_modules_dir: Some(
|
||||
match cli_options.node_modules_dir_path() {
|
||||
Some(node_modules_path) => node_modules_path.to_path_buf(),
|
||||
// path needs to be canonicalized for node resolution
|
||||
// (node_modules_dir_path above is already canonicalized)
|
||||
None => canonicalize_path_maybe_not_exists(
|
||||
cli_options.initial_cwd(),
|
||||
)?
|
||||
.join("node_modules"),
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
maybe_lockfile: cli_options.maybe_lockfile().cloned(),
|
||||
fs: fs.clone(),
|
||||
http_client_provider: self.http_client_provider().clone(),
|
||||
npm_global_cache_dir: self.deno_dir()?.npm_folder_path(),
|
||||
cache_setting: cli_options.cache_setting(),
|
||||
text_only_progress_bar: self.text_only_progress_bar().clone(),
|
||||
maybe_node_modules_path: cli_options.node_modules_dir_path().cloned(),
|
||||
package_json_deps_provider: Arc::new(PackageJsonInstallDepsProvider::from_workspace(
|
||||
cli_options.workspace(),
|
||||
)),
|
||||
npm_system_info: cli_options.npm_system_info(),
|
||||
npmrc: cli_options.npmrc().clone(),
|
||||
lifecycle_scripts: cli_options.lifecycle_scripts_config(),
|
||||
)
|
||||
} else {
|
||||
CliNpmResolverCreateOptions::Managed(
|
||||
CliManagedNpmResolverCreateOptions {
|
||||
snapshot: match cli_options.resolve_npm_resolution_snapshot()? {
|
||||
Some(snapshot) => {
|
||||
CliNpmResolverManagedSnapshotOption::Specified(Some(
|
||||
snapshot,
|
||||
))
|
||||
}
|
||||
None => match cli_options.maybe_lockfile() {
|
||||
Some(lockfile) => {
|
||||
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
|
||||
lockfile.clone(),
|
||||
)
|
||||
}
|
||||
None => {
|
||||
CliNpmResolverManagedSnapshotOption::Specified(None)
|
||||
}
|
||||
},
|
||||
},
|
||||
maybe_lockfile: cli_options.maybe_lockfile().cloned(),
|
||||
fs: fs.clone(),
|
||||
http_client_provider: self.http_client_provider().clone(),
|
||||
npm_cache_dir: self.npm_cache_dir()?.clone(),
|
||||
cache_setting: cli_options.cache_setting(),
|
||||
text_only_progress_bar: self.text_only_progress_bar().clone(),
|
||||
maybe_node_modules_path: cli_options
|
||||
.node_modules_dir_path()
|
||||
.cloned(),
|
||||
npm_install_deps_provider: Arc::new(
|
||||
NpmInstallDepsProvider::from_workspace(
|
||||
cli_options.workspace(),
|
||||
),
|
||||
),
|
||||
npm_system_info: cli_options.npm_system_info(),
|
||||
npmrc: cli_options.npmrc().clone(),
|
||||
lifecycle_scripts: cli_options.lifecycle_scripts_config(),
|
||||
},
|
||||
)
|
||||
})
|
||||
}).await
|
||||
}.boxed_local())
|
||||
.await
|
||||
}
|
||||
.boxed_local(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn sloppy_imports_resolver(
|
||||
&self,
|
||||
) -> Result<Option<&Arc<SloppyImportsResolver>>, AnyError> {
|
||||
) -> Result<Option<&Arc<CliSloppyImportsResolver>>, AnyError> {
|
||||
self
|
||||
.services
|
||||
.sloppy_imports_resolver
|
||||
.get_or_try_init(|| {
|
||||
Ok(
|
||||
self
|
||||
.cli_options()?
|
||||
.unstable_sloppy_imports()
|
||||
.then(|| Arc::new(SloppyImportsResolver::new(self.fs().clone()))),
|
||||
)
|
||||
Ok(self.cli_options()?.unstable_sloppy_imports().then(|| {
|
||||
Arc::new(CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(
|
||||
self.fs().clone(),
|
||||
)))
|
||||
}))
|
||||
})
|
||||
.map(|maybe| maybe.as_ref())
|
||||
}
|
||||
|
@ -452,28 +528,47 @@ impl CliFactory {
|
|||
.await
|
||||
}
|
||||
|
||||
pub async fn resolver(&self) -> Result<&Arc<CliGraphResolver>, AnyError> {
|
||||
pub async fn deno_resolver(&self) -> Result<&Arc<CliDenoResolver>, AnyError> {
|
||||
self
|
||||
.services
|
||||
.deno_resolver
|
||||
.get_or_try_init_async(async {
|
||||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(CliDenoResolver::new(DenoResolverOptions {
|
||||
in_npm_pkg_checker: self.in_npm_pkg_checker()?.clone(),
|
||||
node_and_req_resolver: if cli_options.no_npm() {
|
||||
None
|
||||
} else {
|
||||
Some(NodeAndNpmReqResolver {
|
||||
node_resolver: self.node_resolver().await?.clone(),
|
||||
npm_req_resolver: self.npm_req_resolver().await?.clone(),
|
||||
})
|
||||
},
|
||||
sloppy_imports_resolver: self.sloppy_imports_resolver()?.cloned(),
|
||||
workspace_resolver: self.workspace_resolver().await?.clone(),
|
||||
is_byonm: cli_options.use_byonm(),
|
||||
maybe_vendor_dir: cli_options.vendor_dir_path(),
|
||||
})))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn resolver(&self) -> Result<&Arc<CliResolver>, AnyError> {
|
||||
self
|
||||
.services
|
||||
.resolver
|
||||
.get_or_try_init_async(
|
||||
async {
|
||||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(CliGraphResolver::new(CliGraphResolverOptions {
|
||||
sloppy_imports_resolver: self.sloppy_imports_resolver()?.cloned(),
|
||||
node_resolver: Some(self.cli_node_resolver().await?.clone()),
|
||||
Ok(Arc::new(CliResolver::new(CliResolverOptions {
|
||||
npm_resolver: if cli_options.no_npm() {
|
||||
None
|
||||
} else {
|
||||
Some(self.npm_resolver().await?.clone())
|
||||
},
|
||||
workspace_resolver: self.workspace_resolver().await?.clone(),
|
||||
bare_node_builtins_enabled: cli_options
|
||||
.unstable_bare_node_builtins(),
|
||||
maybe_jsx_import_source_config: cli_options
|
||||
.workspace()
|
||||
.to_maybe_jsx_import_source_config()?,
|
||||
maybe_vendor_dir: cli_options.vendor_dir_path(),
|
||||
deno_resolver: self.deno_resolver().await?.clone(),
|
||||
})))
|
||||
}
|
||||
.boxed_local(),
|
||||
|
@ -492,9 +587,9 @@ impl CliFactory {
|
|||
.get_or_init(|| maybe_file_watcher_reporter)
|
||||
}
|
||||
|
||||
pub fn emit_cache(&self) -> Result<&EmitCache, AnyError> {
|
||||
pub fn emit_cache(&self) -> Result<&Arc<EmitCache>, AnyError> {
|
||||
self.services.emit_cache.get_or_try_init(|| {
|
||||
Ok(EmitCache::new(self.deno_dir()?.gen_cache.clone()))
|
||||
Ok(Arc::new(EmitCache::new(self.deno_dir()?.gen_cache.clone())))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -502,6 +597,7 @@ impl CliFactory {
|
|||
self.services.module_info_cache.get_or_try_init(|| {
|
||||
Ok(Arc::new(ModuleInfoCache::new(
|
||||
self.caches()?.dep_analysis_db(),
|
||||
self.parsed_source_cache().clone(),
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
@ -524,14 +620,13 @@ impl CliFactory {
|
|||
let cli_options = self.cli_options()?;
|
||||
let ts_config_result =
|
||||
cli_options.resolve_ts_config_for_emit(TsConfigType::Emit)?;
|
||||
if let Some(ignored_options) = ts_config_result.maybe_ignored_options {
|
||||
warn!("{}", ignored_options);
|
||||
}
|
||||
check_warn_tsconfig(&ts_config_result);
|
||||
let (transpile_options, emit_options) =
|
||||
crate::args::ts_config_to_transpile_and_emit_options(
|
||||
ts_config_result.ts_config,
|
||||
)?;
|
||||
Ok(Arc::new(Emitter::new(
|
||||
self.cjs_tracker()?.clone(),
|
||||
self.emit_cache()?.clone(),
|
||||
self.parsed_source_cache().clone(),
|
||||
transpile_options,
|
||||
|
@ -555,7 +650,13 @@ impl CliFactory {
|
|||
async {
|
||||
Ok(Arc::new(NodeResolver::new(
|
||||
DenoFsNodeResolverEnv::new(self.fs().clone()),
|
||||
self.npm_resolver().await?.clone().into_npm_resolver(),
|
||||
self.in_npm_pkg_checker()?.clone(),
|
||||
self
|
||||
.npm_resolver()
|
||||
.await?
|
||||
.clone()
|
||||
.into_npm_pkg_folder_resolver(),
|
||||
self.pkg_json_resolver().clone(),
|
||||
)))
|
||||
}
|
||||
.boxed_local(),
|
||||
|
@ -573,19 +674,57 @@ impl CliFactory {
|
|||
let caches = self.caches()?;
|
||||
let node_analysis_cache =
|
||||
NodeAnalysisCache::new(caches.node_analysis_db());
|
||||
let cjs_esm_analyzer =
|
||||
CliCjsCodeAnalyzer::new(node_analysis_cache, self.fs().clone());
|
||||
let node_resolver = self.node_resolver().await?.clone();
|
||||
let cjs_esm_analyzer = CliCjsCodeAnalyzer::new(
|
||||
node_analysis_cache,
|
||||
self.cjs_tracker()?.clone(),
|
||||
self.fs().clone(),
|
||||
Some(self.parsed_source_cache().clone()),
|
||||
);
|
||||
|
||||
Ok(Arc::new(NodeCodeTranslator::new(
|
||||
cjs_esm_analyzer,
|
||||
DenoFsNodeResolverEnv::new(self.fs().clone()),
|
||||
self.node_resolver().await?.clone(),
|
||||
self.npm_resolver().await?.clone().into_npm_resolver(),
|
||||
self.in_npm_pkg_checker()?.clone(),
|
||||
node_resolver,
|
||||
self
|
||||
.npm_resolver()
|
||||
.await?
|
||||
.clone()
|
||||
.into_npm_pkg_folder_resolver(),
|
||||
self.pkg_json_resolver().clone(),
|
||||
)))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn npm_req_resolver(
|
||||
&self,
|
||||
) -> Result<&Arc<CliNpmReqResolver>, AnyError> {
|
||||
self
|
||||
.services
|
||||
.npm_req_resolver
|
||||
.get_or_try_init_async(async {
|
||||
let npm_resolver = self.npm_resolver().await?;
|
||||
Ok(Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions {
|
||||
byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(),
|
||||
fs: CliDenoResolverFs(self.fs().clone()),
|
||||
in_npm_pkg_checker: self.in_npm_pkg_checker()?.clone(),
|
||||
node_resolver: self.node_resolver().await?.clone(),
|
||||
npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(),
|
||||
})))
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub fn pkg_json_resolver(&self) -> &Arc<PackageJsonResolver> {
|
||||
self.services.pkg_json_resolver.get_or_init(|| {
|
||||
Arc::new(PackageJsonResolver::new(DenoFsNodeResolverEnv::new(
|
||||
self.fs().clone(),
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn type_checker(&self) -> Result<&Arc<TypeChecker>, AnyError> {
|
||||
self
|
||||
.services
|
||||
|
@ -594,6 +733,10 @@ impl CliFactory {
|
|||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(TypeChecker::new(
|
||||
self.caches()?.clone(),
|
||||
Arc::new(TypeCheckingCjsTracker::new(
|
||||
self.cjs_tracker()?.clone(),
|
||||
self.module_info_cache()?.clone(),
|
||||
)),
|
||||
cli_options.clone(),
|
||||
self.module_graph_builder().await?.clone(),
|
||||
self.node_resolver().await?.clone(),
|
||||
|
@ -612,18 +755,20 @@ impl CliFactory {
|
|||
.get_or_try_init_async(async {
|
||||
let cli_options = self.cli_options()?;
|
||||
Ok(Arc::new(ModuleGraphBuilder::new(
|
||||
cli_options.clone(),
|
||||
self.caches()?.clone(),
|
||||
self.cjs_tracker()?.clone(),
|
||||
cli_options.clone(),
|
||||
self.file_fetcher()?.clone(),
|
||||
self.fs().clone(),
|
||||
self.resolver().await?.clone(),
|
||||
self.npm_resolver().await?.clone(),
|
||||
self.module_info_cache()?.clone(),
|
||||
self.parsed_source_cache().clone(),
|
||||
self.global_http_cache()?.clone(),
|
||||
self.in_npm_pkg_checker()?.clone(),
|
||||
cli_options.maybe_lockfile().cloned(),
|
||||
self.maybe_file_watcher_reporter().clone(),
|
||||
self.emit_cache()?.clone(),
|
||||
self.file_fetcher()?.clone(),
|
||||
self.global_http_cache()?.clone(),
|
||||
self.module_info_cache()?.clone(),
|
||||
self.npm_resolver().await?.clone(),
|
||||
self.parsed_source_cache().clone(),
|
||||
self.resolver().await?.clone(),
|
||||
self.root_permissions_container()?.clone(),
|
||||
)))
|
||||
})
|
||||
.await
|
||||
|
@ -657,6 +802,7 @@ impl CliFactory {
|
|||
Ok(Arc::new(MainModuleGraphContainer::new(
|
||||
self.cli_options()?.clone(),
|
||||
self.module_load_preparer().await?.clone(),
|
||||
self.root_permissions_container()?.clone(),
|
||||
)))
|
||||
})
|
||||
.await
|
||||
|
@ -693,25 +839,27 @@ impl CliFactory {
|
|||
.await
|
||||
}
|
||||
|
||||
pub fn cjs_resolutions(&self) -> &Arc<CjsResolutionStore> {
|
||||
self.services.cjs_resolutions.get_or_init(Default::default)
|
||||
pub fn cjs_tracker(&self) -> Result<&Arc<CjsTracker>, AnyError> {
|
||||
self.services.cjs_tracker.get_or_try_init(|| {
|
||||
let options = self.cli_options()?;
|
||||
Ok(Arc::new(CjsTracker::new(
|
||||
self.in_npm_pkg_checker()?.clone(),
|
||||
self.pkg_json_resolver().clone(),
|
||||
IsCjsResolverOptions {
|
||||
detect_cjs: options.detect_cjs(),
|
||||
is_node_main: options.is_node_main(),
|
||||
},
|
||||
)))
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn cli_node_resolver(
|
||||
pub fn permission_desc_parser(
|
||||
&self,
|
||||
) -> Result<&Arc<CliNodeResolver>, AnyError> {
|
||||
self
|
||||
.services
|
||||
.cli_node_resolver
|
||||
.get_or_try_init_async(async {
|
||||
Ok(Arc::new(CliNodeResolver::new(
|
||||
self.cjs_resolutions().clone(),
|
||||
self.fs().clone(),
|
||||
self.node_resolver().await?.clone(),
|
||||
self.npm_resolver().await?.clone(),
|
||||
)))
|
||||
})
|
||||
.await
|
||||
) -> Result<&Arc<RuntimePermissionDescriptorParser>, AnyError> {
|
||||
self.services.permission_desc_parser.get_or_try_init(|| {
|
||||
let fs = self.fs().clone();
|
||||
Ok(Arc::new(RuntimePermissionDescriptorParser::new(fs)))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn feature_checker(&self) -> Result<&Arc<FeatureChecker>, AnyError> {
|
||||
|
@ -719,15 +867,10 @@ impl CliFactory {
|
|||
let cli_options = self.cli_options()?;
|
||||
let mut checker = FeatureChecker::default();
|
||||
checker.set_exit_cb(Box::new(crate::unstable_exit_cb));
|
||||
checker.set_warn_cb(Box::new(crate::unstable_warn_cb));
|
||||
if cli_options.legacy_unstable_flag() {
|
||||
checker.enable_legacy_unstable();
|
||||
checker.warn_on_legacy_unstable();
|
||||
}
|
||||
let unstable_features = cli_options.unstable_features();
|
||||
for (flag_name, _, _) in crate::UNSTABLE_GRANULAR_FLAGS {
|
||||
if unstable_features.contains(&flag_name.to_string()) {
|
||||
checker.enable_feature(flag_name);
|
||||
for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS {
|
||||
if unstable_features.contains(&granular_flag.name.to_string()) {
|
||||
checker.enable_feature(granular_flag.name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -740,7 +883,10 @@ impl CliFactory {
|
|||
) -> Result<DenoCompileBinaryWriter, AnyError> {
|
||||
let cli_options = self.cli_options()?;
|
||||
Ok(DenoCompileBinaryWriter::new(
|
||||
self.cjs_tracker()?,
|
||||
self.cli_options()?,
|
||||
self.deno_dir()?,
|
||||
self.emitter()?,
|
||||
self.file_fetcher()?,
|
||||
self.http_client_provider(),
|
||||
self.npm_resolver().await?.as_ref(),
|
||||
|
@ -749,66 +895,87 @@ impl CliFactory {
|
|||
))
|
||||
}
|
||||
|
||||
pub fn root_permissions_container(
|
||||
&self,
|
||||
) -> Result<&PermissionsContainer, AnyError> {
|
||||
self
|
||||
.services
|
||||
.root_permissions_container
|
||||
.get_or_try_init(|| {
|
||||
let desc_parser = self.permission_desc_parser()?.clone();
|
||||
let permissions = Permissions::from_options(
|
||||
desc_parser.as_ref(),
|
||||
&self.cli_options()?.permissions_options(),
|
||||
)?;
|
||||
Ok(PermissionsContainer::new(desc_parser, permissions))
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn create_cli_main_worker_factory(
|
||||
&self,
|
||||
) -> Result<CliMainWorkerFactory, AnyError> {
|
||||
let cli_options = self.cli_options()?;
|
||||
let fs = self.fs();
|
||||
let node_resolver = self.node_resolver().await?;
|
||||
let npm_resolver = self.npm_resolver().await?;
|
||||
let fs = self.fs();
|
||||
let cli_node_resolver = self.cli_node_resolver().await?;
|
||||
let cli_npm_resolver = self.npm_resolver().await?.clone();
|
||||
let in_npm_pkg_checker = self.in_npm_pkg_checker()?;
|
||||
let maybe_file_watcher_communicator = if cli_options.has_hmr() {
|
||||
Some(self.watcher_communicator.clone().unwrap())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let node_code_translator = self.node_code_translator().await?;
|
||||
let cjs_tracker = self.cjs_tracker()?.clone();
|
||||
let pkg_json_resolver = self.pkg_json_resolver().clone();
|
||||
let npm_req_resolver = self.npm_req_resolver().await?;
|
||||
|
||||
Ok(CliMainWorkerFactory::new(
|
||||
StorageKeyResolver::from_options(cli_options),
|
||||
cli_options.sub_command().clone(),
|
||||
npm_resolver.clone(),
|
||||
node_resolver.clone(),
|
||||
self.blob_store().clone(),
|
||||
if cli_options.code_cache_enabled() {
|
||||
Some(self.code_cache()?.clone())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
self.feature_checker()?.clone(),
|
||||
fs.clone(),
|
||||
maybe_file_watcher_communicator,
|
||||
self.maybe_inspector_server()?.clone(),
|
||||
cli_options.maybe_lockfile().cloned(),
|
||||
Box::new(CliModuleLoaderFactory::new(
|
||||
cli_options,
|
||||
cjs_tracker,
|
||||
if cli_options.code_cache_enabled() {
|
||||
Some(self.code_cache()?.clone())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
self.emitter()?.clone(),
|
||||
fs.clone(),
|
||||
in_npm_pkg_checker.clone(),
|
||||
self.main_module_graph_container().await?.clone(),
|
||||
self.module_load_preparer().await?.clone(),
|
||||
cli_node_resolver.clone(),
|
||||
node_code_translator.clone(),
|
||||
node_resolver.clone(),
|
||||
npm_req_resolver.clone(),
|
||||
cli_npm_resolver.clone(),
|
||||
NpmModuleLoader::new(
|
||||
self.cjs_resolutions().clone(),
|
||||
self.node_code_translator().await?.clone(),
|
||||
self.cjs_tracker()?.clone(),
|
||||
fs.clone(),
|
||||
cli_node_resolver.clone(),
|
||||
node_code_translator.clone(),
|
||||
),
|
||||
self.parsed_source_cache().clone(),
|
||||
self.resolver().await?.clone(),
|
||||
)),
|
||||
node_resolver.clone(),
|
||||
npm_resolver.clone(),
|
||||
pkg_json_resolver,
|
||||
self.root_cert_store_provider().clone(),
|
||||
self.fs().clone(),
|
||||
maybe_file_watcher_communicator,
|
||||
self.maybe_inspector_server()?.clone(),
|
||||
cli_options.maybe_lockfile().cloned(),
|
||||
self.feature_checker()?.clone(),
|
||||
self.root_permissions_container()?.clone(),
|
||||
StorageKeyResolver::from_options(cli_options),
|
||||
cli_options.sub_command().clone(),
|
||||
self.create_cli_main_worker_options()?,
|
||||
cli_options.node_ipc_fd(),
|
||||
cli_options.serve_port(),
|
||||
cli_options.serve_host(),
|
||||
cli_options.enable_future_features(),
|
||||
// TODO(bartlomieju): temporarily disabled
|
||||
// cli_options.disable_deprecated_api_warning,
|
||||
true,
|
||||
cli_options.verbose_deprecated_api_warning,
|
||||
if cli_options.code_cache_enabled() {
|
||||
Some(self.code_cache()?.clone())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
self.cli_options()?.otel_config(),
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -857,7 +1024,6 @@ impl CliFactory {
|
|||
inspect_wait: cli_options.inspect_wait().is_some(),
|
||||
strace_ops: cli_options.strace_ops().clone(),
|
||||
is_inspecting: cli_options.is_inspecting(),
|
||||
is_npm_main: cli_options.is_npm_main(),
|
||||
location: cli_options.location_flag().clone(),
|
||||
// if the user ran a binary command, we'll need to set process.argv[0]
|
||||
// to be the name of the binary command instead of deno
|
||||
|
@ -870,9 +1036,11 @@ impl CliFactory {
|
|||
unsafely_ignore_certificate_errors: cli_options
|
||||
.unsafely_ignore_certificate_errors()
|
||||
.clone(),
|
||||
unstable: cli_options.legacy_unstable_flag(),
|
||||
create_hmr_runner,
|
||||
create_coverage_collector,
|
||||
node_ipc: cli_options.node_ipc_fd(),
|
||||
serve_port: cli_options.serve_port(),
|
||||
serve_host: cli_options.serve_host(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ use crate::http_util::HttpClientProvider;
|
|||
use crate::util::progress_bar::ProgressBar;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::generic_error;
|
||||
|
@ -22,8 +21,10 @@ use deno_core::url::Url;
|
|||
use deno_core::ModuleSpecifier;
|
||||
use deno_graph::source::LoaderChecksum;
|
||||
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use deno_runtime::deno_web::BlobStore;
|
||||
use http::header;
|
||||
use log::debug;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
|
@ -52,6 +53,25 @@ pub enum FileOrRedirect {
|
|||
Redirect(ModuleSpecifier),
|
||||
}
|
||||
|
||||
impl FileOrRedirect {
|
||||
fn from_deno_cache_entry(
|
||||
specifier: &ModuleSpecifier,
|
||||
cache_entry: deno_cache_dir::CacheEntry,
|
||||
) -> Result<Self, AnyError> {
|
||||
if let Some(redirect_to) = cache_entry.metadata.headers.get("location") {
|
||||
let redirect =
|
||||
deno_core::resolve_import(redirect_to, specifier.as_str())?;
|
||||
Ok(FileOrRedirect::Redirect(redirect))
|
||||
} else {
|
||||
Ok(FileOrRedirect::File(File {
|
||||
specifier: specifier.clone(),
|
||||
maybe_headers: Some(cache_entry.metadata.headers),
|
||||
source: Arc::from(cache_entry.content),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure representing a source file.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct File {
|
||||
|
@ -117,14 +137,23 @@ impl MemoryFiles {
|
|||
|
||||
/// Fetch a source file from the local file system.
|
||||
fn fetch_local(specifier: &ModuleSpecifier) -> Result<File, AnyError> {
|
||||
let local = specifier.to_file_path().map_err(|_| {
|
||||
let local = url_to_file_path(specifier).map_err(|_| {
|
||||
uri_error(format!("Invalid file path.\n Specifier: {specifier}"))
|
||||
})?;
|
||||
// If it doesnt have a extension, we want to treat it as typescript by default
|
||||
let headers = if local.extension().is_none() {
|
||||
Some(HashMap::from([(
|
||||
"content-type".to_string(),
|
||||
"application/typescript".to_string(),
|
||||
)]))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let bytes = fs::read(local)?;
|
||||
|
||||
Ok(File {
|
||||
specifier: specifier.clone(),
|
||||
maybe_headers: None,
|
||||
maybe_headers: headers,
|
||||
source: bytes.into(),
|
||||
})
|
||||
}
|
||||
|
@ -135,17 +164,36 @@ fn get_validated_scheme(
|
|||
) -> Result<String, AnyError> {
|
||||
let scheme = specifier.scheme();
|
||||
if !SUPPORTED_SCHEMES.contains(&scheme) {
|
||||
// NOTE(bartlomieju): this message list additional `npm` and `jsr` schemes, but they should actually be handled
|
||||
// before `file_fetcher.rs` APIs are even hit.
|
||||
let mut all_supported_schemes = SUPPORTED_SCHEMES.to_vec();
|
||||
all_supported_schemes.extend_from_slice(&["npm", "jsr"]);
|
||||
all_supported_schemes.sort();
|
||||
let scheme_list = all_supported_schemes
|
||||
.iter()
|
||||
.map(|scheme| format!(" - \"{}\"", scheme))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
Err(generic_error(format!(
|
||||
"Unsupported scheme \"{scheme}\" for module \"{specifier}\". Supported schemes: {SUPPORTED_SCHEMES:#?}"
|
||||
"Unsupported scheme \"{scheme}\" for module \"{specifier}\". Supported schemes:\n{}",
|
||||
scheme_list
|
||||
)))
|
||||
} else {
|
||||
Ok(scheme.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum FetchPermissionsOptionRef<'a> {
|
||||
AllowAll,
|
||||
DynamicContainer(&'a PermissionsContainer),
|
||||
StaticContainer(&'a PermissionsContainer),
|
||||
}
|
||||
|
||||
pub struct FetchOptions<'a> {
|
||||
pub specifier: &'a ModuleSpecifier,
|
||||
pub permissions: &'a PermissionsContainer,
|
||||
pub permissions: FetchPermissionsOptionRef<'a>,
|
||||
pub maybe_auth: Option<(header::HeaderName, header::HeaderValue)>,
|
||||
pub maybe_accept: Option<&'a str>,
|
||||
pub maybe_cache_setting: Option<&'a CacheSetting>,
|
||||
}
|
||||
|
@ -238,45 +286,32 @@ impl FileFetcher {
|
|||
);
|
||||
|
||||
let cache_key = self.http_cache.cache_item_key(specifier)?; // compute this once
|
||||
let Some(headers) = self.http_cache.read_headers(&cache_key)? else {
|
||||
return Ok(None);
|
||||
};
|
||||
if let Some(redirect_to) = headers.get("location") {
|
||||
let redirect =
|
||||
deno_core::resolve_import(redirect_to, specifier.as_str())?;
|
||||
return Ok(Some(FileOrRedirect::Redirect(redirect)));
|
||||
}
|
||||
let result = self.http_cache.read_file_bytes(
|
||||
let result = self.http_cache.get(
|
||||
&cache_key,
|
||||
maybe_checksum
|
||||
.as_ref()
|
||||
.map(|c| deno_cache_dir::Checksum::new(c.as_str())),
|
||||
deno_cache_dir::GlobalToLocalCopy::Allow,
|
||||
);
|
||||
let bytes = match result {
|
||||
Ok(Some(bytes)) => bytes,
|
||||
Ok(None) => return Ok(None),
|
||||
match result {
|
||||
Ok(Some(cache_data)) => Ok(Some(FileOrRedirect::from_deno_cache_entry(
|
||||
specifier, cache_data,
|
||||
)?)),
|
||||
Ok(None) => Ok(None),
|
||||
Err(err) => match err {
|
||||
deno_cache_dir::CacheReadFileError::Io(err) => return Err(err.into()),
|
||||
deno_cache_dir::CacheReadFileError::Io(err) => Err(err.into()),
|
||||
deno_cache_dir::CacheReadFileError::ChecksumIntegrity(err) => {
|
||||
// convert to the equivalent deno_graph error so that it
|
||||
// enhances it if this is passed to deno_graph
|
||||
return Err(
|
||||
Err(
|
||||
deno_graph::source::ChecksumIntegrityError {
|
||||
actual: err.actual,
|
||||
expected: err.expected,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
)
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Ok(Some(FileOrRedirect::File(File {
|
||||
specifier: specifier.clone(),
|
||||
maybe_headers: Some(headers),
|
||||
source: Arc::from(bytes),
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a data URL into a file, resulting in an error if the URL is
|
||||
|
@ -311,7 +346,7 @@ impl FileFetcher {
|
|||
)
|
||||
})?;
|
||||
|
||||
let bytes = blob.read_all().await?;
|
||||
let bytes = blob.read_all().await;
|
||||
let headers =
|
||||
HashMap::from([("content-type".to_string(), blob.media_type.clone())]);
|
||||
|
||||
|
@ -328,6 +363,7 @@ impl FileFetcher {
|
|||
maybe_accept: Option<&str>,
|
||||
cache_setting: &CacheSetting,
|
||||
maybe_checksum: Option<&LoaderChecksum>,
|
||||
maybe_auth: Option<(header::HeaderName, header::HeaderValue)>,
|
||||
) -> Result<FileOrRedirect, AnyError> {
|
||||
debug!(
|
||||
"FileFetcher::fetch_remote_no_follow - specifier: {}",
|
||||
|
@ -363,12 +399,30 @@ impl FileFetcher {
|
|||
);
|
||||
}
|
||||
|
||||
let maybe_etag = self
|
||||
let maybe_etag_cache_entry = self
|
||||
.http_cache
|
||||
.cache_item_key(specifier)
|
||||
.ok()
|
||||
.and_then(|key| self.http_cache.read_headers(&key).ok().flatten())
|
||||
.and_then(|headers| headers.get("etag").cloned());
|
||||
.and_then(|key| {
|
||||
self
|
||||
.http_cache
|
||||
.get(
|
||||
&key,
|
||||
maybe_checksum
|
||||
.as_ref()
|
||||
.map(|c| deno_cache_dir::Checksum::new(c.as_str())),
|
||||
)
|
||||
.ok()
|
||||
.flatten()
|
||||
})
|
||||
.and_then(|cache_entry| {
|
||||
cache_entry
|
||||
.metadata
|
||||
.headers
|
||||
.get("etag")
|
||||
.cloned()
|
||||
.map(|etag| (cache_entry, etag))
|
||||
});
|
||||
let maybe_auth_token = self.auth_tokens.get(specifier);
|
||||
|
||||
async fn handle_request_or_server_error(
|
||||
|
@ -390,7 +444,6 @@ impl FileFetcher {
|
|||
}
|
||||
}
|
||||
|
||||
let mut maybe_etag = maybe_etag;
|
||||
let mut retried = false; // retry intermittent failures
|
||||
let result = loop {
|
||||
let result = match self
|
||||
|
@ -399,31 +452,18 @@ impl FileFetcher {
|
|||
.fetch_no_follow(FetchOnceArgs {
|
||||
url: specifier.clone(),
|
||||
maybe_accept: maybe_accept.map(ToOwned::to_owned),
|
||||
maybe_etag: maybe_etag.clone(),
|
||||
maybe_etag: maybe_etag_cache_entry
|
||||
.as_ref()
|
||||
.map(|(_, etag)| etag.clone()),
|
||||
maybe_auth_token: maybe_auth_token.clone(),
|
||||
maybe_auth: maybe_auth.clone(),
|
||||
maybe_progress_guard: maybe_progress_guard.as_ref(),
|
||||
})
|
||||
.await?
|
||||
{
|
||||
FetchOnceResult::NotModified => {
|
||||
let file_or_redirect =
|
||||
self.fetch_cached_no_follow(specifier, maybe_checksum)?;
|
||||
match file_or_redirect {
|
||||
Some(file_or_redirect) => Ok(file_or_redirect),
|
||||
None => {
|
||||
// Someone may have deleted the body from the cache since
|
||||
// it's currently stored in a separate file from the headers,
|
||||
// so delete the etag and try again
|
||||
if maybe_etag.is_some() {
|
||||
debug!("Cache body not found. Trying again without etag.");
|
||||
maybe_etag = None;
|
||||
continue;
|
||||
} else {
|
||||
// should never happen
|
||||
bail!("Your deno cache directory is in an unrecoverable state. Please delete it and try again.")
|
||||
}
|
||||
}
|
||||
}
|
||||
let (cache_entry, _) = maybe_etag_cache_entry.unwrap();
|
||||
FileOrRedirect::from_deno_cache_entry(specifier, cache_entry)
|
||||
}
|
||||
FetchOnceResult::Redirect(redirect_url, headers) => {
|
||||
self.http_cache.set(specifier, headers, &[])?;
|
||||
|
@ -507,16 +547,54 @@ impl FileFetcher {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub async fn fetch_bypass_permissions(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Result<File, AnyError> {
|
||||
self
|
||||
.fetch_inner(specifier, None, FetchPermissionsOptionRef::AllowAll)
|
||||
.await
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub async fn fetch_bypass_permissions_with_maybe_auth(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_auth: Option<(header::HeaderName, header::HeaderValue)>,
|
||||
) -> Result<File, AnyError> {
|
||||
self
|
||||
.fetch_inner(specifier, maybe_auth, FetchPermissionsOptionRef::AllowAll)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Fetch a source file and asynchronously return it.
|
||||
#[inline(always)]
|
||||
pub async fn fetch(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
permissions: &PermissionsContainer,
|
||||
) -> Result<File, AnyError> {
|
||||
self
|
||||
.fetch_inner(
|
||||
specifier,
|
||||
None,
|
||||
FetchPermissionsOptionRef::StaticContainer(permissions),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn fetch_inner(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_auth: Option<(header::HeaderName, header::HeaderValue)>,
|
||||
permissions: FetchPermissionsOptionRef<'_>,
|
||||
) -> Result<File, AnyError> {
|
||||
self
|
||||
.fetch_with_options(FetchOptions {
|
||||
specifier,
|
||||
permissions,
|
||||
maybe_auth,
|
||||
maybe_accept: None,
|
||||
maybe_cache_setting: None,
|
||||
})
|
||||
|
@ -536,12 +614,14 @@ impl FileFetcher {
|
|||
max_redirect: usize,
|
||||
) -> Result<File, AnyError> {
|
||||
let mut specifier = Cow::Borrowed(options.specifier);
|
||||
let mut maybe_auth = options.maybe_auth.clone();
|
||||
for _ in 0..=max_redirect {
|
||||
match self
|
||||
.fetch_no_follow_with_options(FetchNoFollowOptions {
|
||||
fetch_options: FetchOptions {
|
||||
specifier: &specifier,
|
||||
permissions: options.permissions,
|
||||
maybe_auth: maybe_auth.clone(),
|
||||
maybe_accept: options.maybe_accept,
|
||||
maybe_cache_setting: options.maybe_cache_setting,
|
||||
},
|
||||
|
@ -553,6 +633,10 @@ impl FileFetcher {
|
|||
return Ok(file);
|
||||
}
|
||||
FileOrRedirect::Redirect(redirect_specifier) => {
|
||||
// If we were redirected to another origin, don't send the auth header anymore.
|
||||
if redirect_specifier.origin() != specifier.origin() {
|
||||
maybe_auth = None;
|
||||
}
|
||||
specifier = Cow::Owned(redirect_specifier);
|
||||
}
|
||||
}
|
||||
|
@ -575,7 +659,23 @@ impl FileFetcher {
|
|||
specifier
|
||||
);
|
||||
let scheme = get_validated_scheme(specifier)?;
|
||||
options.permissions.check_specifier(specifier)?;
|
||||
match options.permissions {
|
||||
FetchPermissionsOptionRef::AllowAll => {
|
||||
// allow
|
||||
}
|
||||
FetchPermissionsOptionRef::StaticContainer(permissions) => {
|
||||
permissions.check_specifier(
|
||||
specifier,
|
||||
deno_runtime::deno_permissions::CheckSpecifierKind::Static,
|
||||
)?;
|
||||
}
|
||||
FetchPermissionsOptionRef::DynamicContainer(permissions) => {
|
||||
permissions.check_specifier(
|
||||
specifier,
|
||||
deno_runtime::deno_permissions::CheckSpecifierKind::Dynamic,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
if let Some(file) = self.memory_files.get(specifier) {
|
||||
Ok(FileOrRedirect::File(file))
|
||||
} else if scheme == "file" {
|
||||
|
@ -601,6 +701,7 @@ impl FileFetcher {
|
|||
options.maybe_accept,
|
||||
options.maybe_cache_setting.unwrap_or(&self.cache_setting),
|
||||
maybe_checksum,
|
||||
options.maybe_auth,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
@ -661,7 +762,7 @@ mod tests {
|
|||
maybe_temp_dir: Option<TempDir>,
|
||||
) -> (FileFetcher, TempDir, Arc<BlobStore>) {
|
||||
let temp_dir = maybe_temp_dir.unwrap_or_default();
|
||||
let location = temp_dir.path().join("deps").to_path_buf();
|
||||
let location = temp_dir.path().join("remote").to_path_buf();
|
||||
let blob_store: Arc<BlobStore> = Default::default();
|
||||
let file_fetcher = FileFetcher::new(
|
||||
Arc::new(GlobalHttpCache::new(location, RealDenoCacheEnv)),
|
||||
|
@ -676,9 +777,7 @@ mod tests {
|
|||
|
||||
async fn test_fetch(specifier: &ModuleSpecifier) -> (File, FileFetcher) {
|
||||
let (file_fetcher, _) = setup(CacheSetting::ReloadAll, None);
|
||||
let result = file_fetcher
|
||||
.fetch(specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(specifier).await;
|
||||
assert!(result.is_ok());
|
||||
(result.unwrap(), file_fetcher)
|
||||
}
|
||||
|
@ -692,7 +791,8 @@ mod tests {
|
|||
.fetch_with_options_and_max_redirect(
|
||||
FetchOptions {
|
||||
specifier,
|
||||
permissions: &PermissionsContainer::allow_all(),
|
||||
permissions: FetchPermissionsOptionRef::AllowAll,
|
||||
maybe_auth: None,
|
||||
maybe_accept: None,
|
||||
maybe_cache_setting: Some(&file_fetcher.cache_setting),
|
||||
},
|
||||
|
@ -788,9 +888,7 @@ mod tests {
|
|||
};
|
||||
file_fetcher.insert_memory_files(file.clone());
|
||||
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let result_file = result.unwrap();
|
||||
assert_eq!(result_file, file);
|
||||
|
@ -801,9 +899,7 @@ mod tests {
|
|||
let (file_fetcher, _) = setup(CacheSetting::Use, None);
|
||||
let specifier = resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap();
|
||||
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let file = result.unwrap().into_text_decoded().unwrap();
|
||||
assert_eq!(
|
||||
|
@ -832,9 +928,7 @@ mod tests {
|
|||
None,
|
||||
);
|
||||
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let file = result.unwrap().into_text_decoded().unwrap();
|
||||
assert_eq!(
|
||||
|
@ -854,9 +948,7 @@ mod tests {
|
|||
let specifier =
|
||||
ModuleSpecifier::parse("http://localhost:4545/subdir/mod2.ts").unwrap();
|
||||
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let file = result.unwrap().into_text_decoded().unwrap();
|
||||
assert_eq!(
|
||||
|
@ -874,9 +966,7 @@ mod tests {
|
|||
.set(&specifier, headers.clone(), file.source.as_bytes())
|
||||
.unwrap();
|
||||
|
||||
let result = file_fetcher_01
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher_01.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let file = result.unwrap().into_text_decoded().unwrap();
|
||||
assert_eq!(
|
||||
|
@ -900,9 +990,7 @@ mod tests {
|
|||
.set(&specifier, headers.clone(), file.source.as_bytes())
|
||||
.unwrap();
|
||||
|
||||
let result = file_fetcher_02
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher_02.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let file = result.unwrap().into_text_decoded().unwrap();
|
||||
assert_eq!(
|
||||
|
@ -913,7 +1001,7 @@ mod tests {
|
|||
|
||||
// This creates a totally new instance, simulating another Deno process
|
||||
// invocation and indicates to "cache bust".
|
||||
let location = temp_dir.path().join("deps").to_path_buf();
|
||||
let location = temp_dir.path().join("remote").to_path_buf();
|
||||
let file_fetcher = FileFetcher::new(
|
||||
Arc::new(GlobalHttpCache::new(
|
||||
location,
|
||||
|
@ -925,9 +1013,7 @@ mod tests {
|
|||
Default::default(),
|
||||
None,
|
||||
);
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let file = result.unwrap().into_text_decoded().unwrap();
|
||||
assert_eq!(
|
||||
|
@ -941,7 +1027,7 @@ mod tests {
|
|||
async fn test_fetch_uses_cache() {
|
||||
let _http_server_guard = test_util::http_server();
|
||||
let temp_dir = TempDir::new();
|
||||
let location = temp_dir.path().join("deps").to_path_buf();
|
||||
let location = temp_dir.path().join("remote").to_path_buf();
|
||||
let specifier =
|
||||
resolve_url("http://localhost:4545/subdir/mismatch_ext.ts").unwrap();
|
||||
|
||||
|
@ -958,9 +1044,7 @@ mod tests {
|
|||
None,
|
||||
);
|
||||
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let cache_key =
|
||||
file_fetcher.http_cache.cache_item_key(&specifier).unwrap();
|
||||
|
@ -994,9 +1078,7 @@ mod tests {
|
|||
Default::default(),
|
||||
None,
|
||||
);
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
|
||||
let cache_key =
|
||||
|
@ -1033,9 +1115,7 @@ mod tests {
|
|||
resolve_url("http://localhost:4545/subdir/redirects/redirect1.js")
|
||||
.unwrap();
|
||||
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let file = result.unwrap();
|
||||
assert_eq!(file.specifier, redirected_specifier);
|
||||
|
@ -1074,9 +1154,7 @@ mod tests {
|
|||
resolve_url("http://localhost:4545/subdir/redirects/redirect1.js")
|
||||
.unwrap();
|
||||
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let file = result.unwrap();
|
||||
assert_eq!(file.specifier, redirected_02_specifier);
|
||||
|
@ -1115,7 +1193,7 @@ mod tests {
|
|||
async fn test_fetch_uses_cache_with_redirects() {
|
||||
let _http_server_guard = test_util::http_server();
|
||||
let temp_dir = TempDir::new();
|
||||
let location = temp_dir.path().join("deps").to_path_buf();
|
||||
let location = temp_dir.path().join("remote").to_path_buf();
|
||||
let specifier =
|
||||
resolve_url("http://localhost:4548/subdir/mismatch_ext.ts").unwrap();
|
||||
let redirected_specifier =
|
||||
|
@ -1134,9 +1212,7 @@ mod tests {
|
|||
None,
|
||||
);
|
||||
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
|
||||
let cache_key = file_fetcher
|
||||
|
@ -1174,7 +1250,7 @@ mod tests {
|
|||
None,
|
||||
);
|
||||
let result = file_fetcher
|
||||
.fetch(&redirected_specifier, &PermissionsContainer::allow_all())
|
||||
.fetch_bypass_permissions(&redirected_specifier)
|
||||
.await;
|
||||
assert!(result.is_ok());
|
||||
|
||||
|
@ -1215,7 +1291,8 @@ mod tests {
|
|||
.fetch_with_options_and_max_redirect(
|
||||
FetchOptions {
|
||||
specifier: &specifier,
|
||||
permissions: &PermissionsContainer::allow_all(),
|
||||
permissions: FetchPermissionsOptionRef::AllowAll,
|
||||
maybe_auth: None,
|
||||
maybe_accept: None,
|
||||
maybe_cache_setting: Some(&file_fetcher.cache_setting),
|
||||
},
|
||||
|
@ -1228,7 +1305,8 @@ mod tests {
|
|||
.fetch_with_options_and_max_redirect(
|
||||
FetchOptions {
|
||||
specifier: &specifier,
|
||||
permissions: &PermissionsContainer::allow_all(),
|
||||
permissions: FetchPermissionsOptionRef::AllowAll,
|
||||
maybe_auth: None,
|
||||
maybe_accept: None,
|
||||
maybe_cache_setting: Some(&file_fetcher.cache_setting),
|
||||
},
|
||||
|
@ -1256,9 +1334,7 @@ mod tests {
|
|||
resolve_url("http://localhost:4550/subdir/redirects/redirect1.js")
|
||||
.unwrap();
|
||||
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let file = result.unwrap();
|
||||
assert_eq!(file.specifier, redirected_specifier);
|
||||
|
@ -1287,7 +1363,7 @@ mod tests {
|
|||
async fn test_fetch_no_remote() {
|
||||
let _http_server_guard = test_util::http_server();
|
||||
let temp_dir = TempDir::new();
|
||||
let location = temp_dir.path().join("deps").to_path_buf();
|
||||
let location = temp_dir.path().join("remote").to_path_buf();
|
||||
let file_fetcher = FileFetcher::new(
|
||||
Arc::new(GlobalHttpCache::new(
|
||||
location,
|
||||
|
@ -1302,9 +1378,7 @@ mod tests {
|
|||
let specifier =
|
||||
resolve_url("http://localhost:4545/run/002_hello.ts").unwrap();
|
||||
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_err());
|
||||
let err = result.unwrap_err();
|
||||
assert_eq!(get_custom_error_class(&err), Some("NoRemote"));
|
||||
|
@ -1315,7 +1389,7 @@ mod tests {
|
|||
async fn test_fetch_cache_only() {
|
||||
let _http_server_guard = test_util::http_server();
|
||||
let temp_dir = TempDir::new();
|
||||
let location = temp_dir.path().join("deps").to_path_buf();
|
||||
let location = temp_dir.path().join("remote").to_path_buf();
|
||||
let file_fetcher_01 = FileFetcher::new(
|
||||
Arc::new(GlobalHttpCache::new(location.clone(), RealDenoCacheEnv)),
|
||||
CacheSetting::Only,
|
||||
|
@ -1335,22 +1409,16 @@ mod tests {
|
|||
let specifier =
|
||||
resolve_url("http://localhost:4545/run/002_hello.ts").unwrap();
|
||||
|
||||
let result = file_fetcher_01
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher_01.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_err());
|
||||
let err = result.unwrap_err();
|
||||
assert_eq!(err.to_string(), "Specifier not found in cache: \"http://localhost:4545/run/002_hello.ts\", --cached-only is specified.");
|
||||
assert_eq!(get_custom_error_class(&err), Some("NotCached"));
|
||||
|
||||
let result = file_fetcher_02
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher_02.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
|
||||
let result = file_fetcher_01
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher_01.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
|
@ -1360,17 +1428,13 @@ mod tests {
|
|||
let fixture_path = temp_dir.path().join("mod.ts");
|
||||
let specifier = ModuleSpecifier::from_file_path(&fixture_path).unwrap();
|
||||
fs::write(fixture_path.clone(), r#"console.log("hello deno");"#).unwrap();
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let file = result.unwrap().into_text_decoded().unwrap();
|
||||
assert_eq!(&*file.source, r#"console.log("hello deno");"#);
|
||||
|
||||
fs::write(fixture_path, r#"console.log("goodbye deno");"#).unwrap();
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let file = result.unwrap().into_text_decoded().unwrap();
|
||||
assert_eq!(&*file.source, r#"console.log("goodbye deno");"#);
|
||||
|
@ -1384,18 +1448,14 @@ mod tests {
|
|||
setup(CacheSetting::RespectHeaders, Some(temp_dir.clone()));
|
||||
let specifier =
|
||||
ModuleSpecifier::parse("http://localhost:4545/dynamic").unwrap();
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let file = result.unwrap();
|
||||
let first = file.source;
|
||||
|
||||
let (file_fetcher, _) =
|
||||
setup(CacheSetting::RespectHeaders, Some(temp_dir.clone()));
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let file = result.unwrap();
|
||||
let second = file.source;
|
||||
|
@ -1411,18 +1471,14 @@ mod tests {
|
|||
setup(CacheSetting::RespectHeaders, Some(temp_dir.clone()));
|
||||
let specifier =
|
||||
ModuleSpecifier::parse("http://localhost:4545/dynamic_cache").unwrap();
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let file = result.unwrap();
|
||||
let first = file.source;
|
||||
|
||||
let (file_fetcher, _) =
|
||||
setup(CacheSetting::RespectHeaders, Some(temp_dir.clone()));
|
||||
let result = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await;
|
||||
let result = file_fetcher.fetch_bypass_permissions(&specifier).await;
|
||||
assert!(result.is_ok());
|
||||
let file = result.unwrap();
|
||||
let second = file.source;
|
||||
|
@ -1480,13 +1536,10 @@ mod tests {
|
|||
let cache_key = file_fetcher.http_cache.cache_item_key(url).unwrap();
|
||||
let bytes = file_fetcher
|
||||
.http_cache
|
||||
.read_file_bytes(
|
||||
&cache_key,
|
||||
None,
|
||||
deno_cache_dir::GlobalToLocalCopy::Allow,
|
||||
)
|
||||
.get(&cache_key, None)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
.unwrap()
|
||||
.content;
|
||||
String::from_utf8(bytes).unwrap()
|
||||
}
|
||||
|
||||
|
|
|
@ -3,15 +3,18 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_config::glob::FilePatterns;
|
||||
use deno_config::glob::PathOrPatternSet;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::RwLock;
|
||||
use deno_core::resolve_url_or_path;
|
||||
use deno_graph::ModuleGraph;
|
||||
use deno_runtime::colors;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
|
||||
use crate::args::CliOptions;
|
||||
use crate::module_loader::ModuleLoadPreparer;
|
||||
use crate::util::fs::collect_specifiers;
|
||||
use crate::util::path::is_script_ext;
|
||||
|
||||
pub trait ModuleGraphContainer: Clone + 'static {
|
||||
/// Acquires a permit to modify the module graph without other code
|
||||
|
@ -42,12 +45,14 @@ pub struct MainModuleGraphContainer {
|
|||
inner: Arc<RwLock<Arc<ModuleGraph>>>,
|
||||
cli_options: Arc<CliOptions>,
|
||||
module_load_preparer: Arc<ModuleLoadPreparer>,
|
||||
root_permissions: PermissionsContainer,
|
||||
}
|
||||
|
||||
impl MainModuleGraphContainer {
|
||||
pub fn new(
|
||||
cli_options: Arc<CliOptions>,
|
||||
module_load_preparer: Arc<ModuleLoadPreparer>,
|
||||
root_permissions: PermissionsContainer,
|
||||
) -> Self {
|
||||
Self {
|
||||
update_queue: Default::default(),
|
||||
|
@ -56,12 +61,14 @@ impl MainModuleGraphContainer {
|
|||
)))),
|
||||
cli_options,
|
||||
module_load_preparer,
|
||||
root_permissions,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn check_specifiers(
|
||||
&self,
|
||||
specifiers: &[ModuleSpecifier],
|
||||
ext_overwrite: Option<&String>,
|
||||
) -> Result<(), AnyError> {
|
||||
let mut graph_permit = self.acquire_update_permit().await;
|
||||
let graph = graph_permit.graph_mut();
|
||||
|
@ -72,7 +79,8 @@ impl MainModuleGraphContainer {
|
|||
specifiers,
|
||||
false,
|
||||
self.cli_options.ts_type_lib_window(),
|
||||
PermissionsContainer::allow_all(),
|
||||
self.root_permissions.clone(),
|
||||
ext_overwrite,
|
||||
)
|
||||
.await?;
|
||||
graph_permit.commit();
|
||||
|
@ -91,7 +99,7 @@ impl MainModuleGraphContainer {
|
|||
log::warn!("{} No matching files found.", colors::yellow("Warning"));
|
||||
}
|
||||
|
||||
self.check_specifiers(&specifiers).await
|
||||
self.check_specifiers(&specifiers, None).await
|
||||
}
|
||||
|
||||
pub fn collect_specifiers(
|
||||
|
@ -99,24 +107,20 @@ impl MainModuleGraphContainer {
|
|||
files: &[String],
|
||||
) -> Result<Vec<ModuleSpecifier>, AnyError> {
|
||||
let excludes = self.cli_options.workspace().resolve_config_excludes()?;
|
||||
Ok(
|
||||
files
|
||||
.iter()
|
||||
.filter_map(|file| {
|
||||
let file_url =
|
||||
resolve_url_or_path(file, self.cli_options.initial_cwd()).ok()?;
|
||||
if file_url.scheme() != "file" {
|
||||
return Some(file_url);
|
||||
}
|
||||
// ignore local files that match any of files listed in `exclude` option
|
||||
let file_path = file_url.to_file_path().ok()?;
|
||||
if excludes.matches_path(&file_path) {
|
||||
None
|
||||
} else {
|
||||
Some(file_url)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
let include_patterns =
|
||||
PathOrPatternSet::from_include_relative_path_or_patterns(
|
||||
self.cli_options.initial_cwd(),
|
||||
files,
|
||||
)?;
|
||||
let file_patterns = FilePatterns {
|
||||
base: self.cli_options.initial_cwd().to_path_buf(),
|
||||
include: Some(include_patterns),
|
||||
exclude: excludes,
|
||||
};
|
||||
collect_specifiers(
|
||||
file_patterns,
|
||||
self.cli_options.vendor_dir_path().map(ToOwned::to_owned),
|
||||
|e| is_script_ext(e.path),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,52 +13,59 @@ use crate::colors;
|
|||
use crate::errors::get_error_class_name;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::resolver::CliGraphResolver;
|
||||
use crate::resolver::SloppyImportsResolver;
|
||||
use crate::resolver::CjsTracker;
|
||||
use crate::resolver::CliResolver;
|
||||
use crate::resolver::CliSloppyImportsResolver;
|
||||
use crate::resolver::SloppyImportsCachedFs;
|
||||
use crate::tools::check;
|
||||
use crate::tools::check::TypeChecker;
|
||||
use crate::util::file_watcher::WatcherCommunicator;
|
||||
use crate::util::fs::canonicalize_path;
|
||||
use deno_config::deno_json::JsxImportSourceConfig;
|
||||
use deno_config::workspace::JsrPackageConfig;
|
||||
use deno_emit::LoaderChecksum;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_graph::source::LoaderChecksum;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::FillFromLockfileOptions;
|
||||
use deno_graph::JsrLoadError;
|
||||
use deno_graph::ModuleLoadError;
|
||||
use deno_graph::WorkspaceFastCheckOption;
|
||||
use deno_runtime::fs_util::specifier_to_file_path;
|
||||
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_graph::source::Loader;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::source::ResolveError;
|
||||
use deno_graph::GraphKind;
|
||||
use deno_graph::Module;
|
||||
use deno_graph::ModuleError;
|
||||
use deno_graph::ModuleGraph;
|
||||
use deno_graph::ModuleGraphError;
|
||||
use deno_graph::ResolutionError;
|
||||
use deno_graph::SpecifierError;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolutionMode;
|
||||
use deno_runtime::deno_fs::FileSystem;
|
||||
use deno_runtime::deno_node;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use deno_semver::jsr::JsrDepPackageReq;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use import_map::ImportMapError;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
use std::collections::HashSet;
|
||||
use std::error::Error;
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone)]
|
||||
pub struct GraphValidOptions {
|
||||
pub check_js: bool,
|
||||
pub follow_type_only: bool,
|
||||
pub is_vendoring: bool,
|
||||
/// Whether to exit the process for lockfile errors.
|
||||
/// Otherwise, surfaces lockfile errors as errors.
|
||||
pub exit_lockfile_errors: bool,
|
||||
pub kind: GraphKind,
|
||||
/// Whether to exit the process for integrity check errors such as
|
||||
/// lockfile checksum mismatches and JSR integrity failures.
|
||||
/// Otherwise, surfaces integrity errors as errors.
|
||||
pub exit_integrity_errors: bool,
|
||||
}
|
||||
|
||||
/// Check if `roots` and their deps are available. Returns `Ok(())` if
|
||||
|
@ -74,17 +81,54 @@ pub fn graph_valid(
|
|||
roots: &[ModuleSpecifier],
|
||||
options: GraphValidOptions,
|
||||
) -> Result<(), AnyError> {
|
||||
if options.exit_lockfile_errors {
|
||||
graph_exit_lock_errors(graph);
|
||||
if options.exit_integrity_errors {
|
||||
graph_exit_integrity_errors(graph);
|
||||
}
|
||||
|
||||
let mut errors = graph
|
||||
let mut errors = graph_walk_errors(
|
||||
graph,
|
||||
fs,
|
||||
roots,
|
||||
GraphWalkErrorsOptions {
|
||||
check_js: options.check_js,
|
||||
kind: options.kind,
|
||||
},
|
||||
);
|
||||
if let Some(error) = errors.next() {
|
||||
Err(error)
|
||||
} else {
|
||||
// finally surface the npm resolution result
|
||||
if let Err(err) = &graph.npm_dep_graph_result {
|
||||
return Err(custom_error(
|
||||
get_error_class_name(err),
|
||||
format_deno_graph_error(err.as_ref().deref()),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GraphWalkErrorsOptions {
|
||||
pub check_js: bool,
|
||||
pub kind: GraphKind,
|
||||
}
|
||||
|
||||
/// Walks the errors found in the module graph that should be surfaced to users
|
||||
/// and enhances them with CLI information.
|
||||
pub fn graph_walk_errors<'a>(
|
||||
graph: &'a ModuleGraph,
|
||||
fs: &'a Arc<dyn FileSystem>,
|
||||
roots: &'a [ModuleSpecifier],
|
||||
options: GraphWalkErrorsOptions,
|
||||
) -> impl Iterator<Item = AnyError> + 'a {
|
||||
graph
|
||||
.walk(
|
||||
roots.iter(),
|
||||
deno_graph::WalkOptions {
|
||||
check_js: options.check_js,
|
||||
follow_type_only: options.follow_type_only,
|
||||
follow_dynamic: options.is_vendoring,
|
||||
kind: options.kind,
|
||||
follow_dynamic: false,
|
||||
prefer_fast_check_graph: false,
|
||||
},
|
||||
)
|
||||
|
@ -108,9 +152,9 @@ pub fn graph_valid(
|
|||
)
|
||||
}
|
||||
ModuleGraphError::ModuleError(error) => {
|
||||
enhanced_lockfile_error_message(error)
|
||||
enhanced_integrity_error_message(error)
|
||||
.or_else(|| enhanced_sloppy_imports_error_message(fs, error))
|
||||
.unwrap_or_else(|| format!("{}", error))
|
||||
.unwrap_or_else(|| format_deno_graph_error(error))
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -131,55 +175,20 @@ pub fn graph_valid(
|
|||
return None;
|
||||
}
|
||||
|
||||
if options.is_vendoring {
|
||||
// warn about failing dynamic imports when vendoring, but don't fail completely
|
||||
if matches!(
|
||||
error,
|
||||
ModuleGraphError::ModuleError(ModuleError::MissingDynamic(_, _))
|
||||
) {
|
||||
log::warn!("Ignoring: {}", message);
|
||||
return None;
|
||||
}
|
||||
|
||||
// ignore invalid downgrades and invalid local imports when vendoring
|
||||
match &error {
|
||||
ModuleGraphError::ResolutionError(err)
|
||||
| ModuleGraphError::TypesResolutionError(err) => {
|
||||
if matches!(
|
||||
err,
|
||||
ResolutionError::InvalidDowngrade { .. }
|
||||
| ResolutionError::InvalidLocalImport { .. }
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
ModuleGraphError::ModuleError(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
Some(custom_error(get_error_class_name(&error.into()), message))
|
||||
});
|
||||
if let Some(error) = errors.next() {
|
||||
Err(error)
|
||||
} else {
|
||||
// finally surface the npm resolution result
|
||||
if let Err(err) = &graph.npm_dep_graph_result {
|
||||
return Err(custom_error(get_error_class_name(err), format!("{}", err)));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn graph_exit_lock_errors(graph: &ModuleGraph) {
|
||||
pub fn graph_exit_integrity_errors(graph: &ModuleGraph) {
|
||||
for error in graph.module_errors() {
|
||||
exit_for_lockfile_error(error);
|
||||
exit_for_integrity_error(error);
|
||||
}
|
||||
}
|
||||
|
||||
fn exit_for_lockfile_error(err: &ModuleError) {
|
||||
if let Some(err_message) = enhanced_lockfile_error_message(err) {
|
||||
fn exit_for_integrity_error(err: &ModuleError) {
|
||||
if let Some(err_message) = enhanced_integrity_error_message(err) {
|
||||
log::error!("{} {}", colors::red("error:"), err_message);
|
||||
std::process::exit(10);
|
||||
deno_runtime::exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,6 +254,19 @@ impl ModuleGraphCreator {
|
|||
package_configs: &[JsrPackageConfig],
|
||||
build_fast_check_graph: bool,
|
||||
) -> Result<ModuleGraph, AnyError> {
|
||||
fn graph_has_external_remote(graph: &ModuleGraph) -> bool {
|
||||
// Earlier on, we marked external non-JSR modules as external.
|
||||
// If the graph contains any of those, it would cause type checking
|
||||
// to crash, so since publishing is going to fail anyway, skip type
|
||||
// checking.
|
||||
graph.modules().any(|module| match module {
|
||||
deno_graph::Module::External(external_module) => {
|
||||
matches!(external_module.specifier.scheme(), "http" | "https")
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
let mut roots = Vec::new();
|
||||
for package_config in package_configs {
|
||||
roots.extend(package_config.config_file.resolve_export_value_urls()?);
|
||||
|
@ -258,9 +280,12 @@ impl ModuleGraphCreator {
|
|||
})
|
||||
.await?;
|
||||
self.graph_valid(&graph)?;
|
||||
if self.options.type_check_mode().is_true() {
|
||||
if self.options.type_check_mode().is_true()
|
||||
&& !graph_has_external_remote(&graph)
|
||||
{
|
||||
self.type_check_graph(graph.clone()).await?;
|
||||
}
|
||||
|
||||
if build_fast_check_graph {
|
||||
let fast_check_workspace_members = package_configs
|
||||
.iter()
|
||||
|
@ -275,6 +300,7 @@ impl ModuleGraphCreator {
|
|||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(graph)
|
||||
}
|
||||
|
||||
|
@ -355,49 +381,55 @@ pub struct BuildFastCheckGraphOptions<'a> {
|
|||
}
|
||||
|
||||
pub struct ModuleGraphBuilder {
|
||||
options: Arc<CliOptions>,
|
||||
caches: Arc<cache::Caches>,
|
||||
cjs_tracker: Arc<CjsTracker>,
|
||||
cli_options: Arc<CliOptions>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
fs: Arc<dyn FileSystem>,
|
||||
resolver: Arc<CliGraphResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
lockfile: Option<Arc<CliLockfile>>,
|
||||
maybe_file_watcher_reporter: Option<FileWatcherReporter>,
|
||||
emit_cache: cache::EmitCache,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
resolver: Arc<CliResolver>,
|
||||
root_permissions_container: PermissionsContainer,
|
||||
}
|
||||
|
||||
impl ModuleGraphBuilder {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
options: Arc<CliOptions>,
|
||||
caches: Arc<cache::Caches>,
|
||||
cjs_tracker: Arc<CjsTracker>,
|
||||
cli_options: Arc<CliOptions>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
fs: Arc<dyn FileSystem>,
|
||||
resolver: Arc<CliGraphResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
lockfile: Option<Arc<CliLockfile>>,
|
||||
maybe_file_watcher_reporter: Option<FileWatcherReporter>,
|
||||
emit_cache: cache::EmitCache,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
resolver: Arc<CliResolver>,
|
||||
root_permissions_container: PermissionsContainer,
|
||||
) -> Self {
|
||||
Self {
|
||||
options,
|
||||
caches,
|
||||
cjs_tracker,
|
||||
cli_options,
|
||||
file_fetcher,
|
||||
fs,
|
||||
resolver,
|
||||
npm_resolver,
|
||||
module_info_cache,
|
||||
parsed_source_cache,
|
||||
global_http_cache,
|
||||
in_npm_pkg_checker,
|
||||
lockfile,
|
||||
maybe_file_watcher_reporter,
|
||||
emit_cache,
|
||||
file_fetcher,
|
||||
global_http_cache,
|
||||
module_info_cache,
|
||||
npm_resolver,
|
||||
parsed_source_cache,
|
||||
resolver,
|
||||
root_permissions_container,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -463,7 +495,7 @@ impl ModuleGraphBuilder {
|
|||
.content
|
||||
.packages
|
||||
.jsr
|
||||
.get(&package_nv.to_string())
|
||||
.get(package_nv)
|
||||
.map(|s| LoaderChecksum::new(s.integrity.clone()))
|
||||
}
|
||||
|
||||
|
@ -477,31 +509,27 @@ impl ModuleGraphBuilder {
|
|||
self
|
||||
.0
|
||||
.lock()
|
||||
.insert_package(package_nv.to_string(), checksum.into_string());
|
||||
.insert_package(package_nv.clone(), checksum.into_string());
|
||||
}
|
||||
}
|
||||
|
||||
let maybe_imports = if options.graph_kind.include_types() {
|
||||
self.options.to_compiler_option_types()?
|
||||
self.cli_options.to_compiler_option_types()?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let analyzer = self
|
||||
.module_info_cache
|
||||
.as_module_analyzer(&self.parsed_source_cache);
|
||||
let analyzer = self.module_info_cache.as_module_analyzer();
|
||||
let mut loader = match options.loader {
|
||||
Some(loader) => MutLoaderRef::Borrowed(loader),
|
||||
None => MutLoaderRef::Owned(self.create_graph_loader()),
|
||||
};
|
||||
let cli_resolver = &self.resolver;
|
||||
let graph_resolver = cli_resolver.as_graph_resolver();
|
||||
let graph_resolver = self.create_graph_resolver()?;
|
||||
let graph_npm_resolver = cli_resolver.create_graph_npm_resolver();
|
||||
let maybe_file_watcher_reporter = self
|
||||
.maybe_file_watcher_reporter
|
||||
.as_ref()
|
||||
.map(|r| r.as_reporter());
|
||||
let workspace_members =
|
||||
self.options.resolve_deno_graph_workspace_members()?;
|
||||
let mut locker = self
|
||||
.lockfile
|
||||
.as_ref()
|
||||
|
@ -515,14 +543,13 @@ impl ModuleGraphBuilder {
|
|||
imports: maybe_imports,
|
||||
is_dynamic: options.is_dynamic,
|
||||
passthrough_jsr_specifiers: false,
|
||||
workspace_members: &workspace_members,
|
||||
executor: Default::default(),
|
||||
file_system: &DenoGraphFsAdapter(self.fs.as_ref()),
|
||||
jsr_url_provider: &CliJsrUrlProvider,
|
||||
npm_resolver: Some(&graph_npm_resolver),
|
||||
module_analyzer: &analyzer,
|
||||
reporter: maybe_file_watcher_reporter,
|
||||
resolver: Some(graph_resolver),
|
||||
resolver: Some(&graph_resolver),
|
||||
locker: locker.as_mut().map(|l| l as _),
|
||||
},
|
||||
)
|
||||
|
@ -538,7 +565,12 @@ impl ModuleGraphBuilder {
|
|||
) -> Result<(), AnyError> {
|
||||
// ensure an "npm install" is done if the user has explicitly
|
||||
// opted into using a node_modules directory
|
||||
if self.options.node_modules_dir_enablement() == Some(true) {
|
||||
if self
|
||||
.cli_options
|
||||
.node_modules_dir()?
|
||||
.map(|m| m.uses_node_modules_dir())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
if let Some(npm_resolver) = self.npm_resolver.as_managed() {
|
||||
npm_resolver.ensure_top_level_package_json_install().await?;
|
||||
}
|
||||
|
@ -550,28 +582,19 @@ impl ModuleGraphBuilder {
|
|||
// populate the information from the lockfile
|
||||
if let Some(lockfile) = &self.lockfile {
|
||||
let lockfile = lockfile.lock();
|
||||
for (from, to) in &lockfile.content.redirects {
|
||||
if let Ok(from) = ModuleSpecifier::parse(from) {
|
||||
if let Ok(to) = ModuleSpecifier::parse(to) {
|
||||
if !matches!(from.scheme(), "file" | "npm" | "jsr") {
|
||||
graph.redirects.insert(from, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (key, value) in &lockfile.content.packages.specifiers {
|
||||
if let Some(key) = key
|
||||
.strip_prefix("jsr:")
|
||||
.and_then(|key| PackageReq::from_str(key).ok())
|
||||
{
|
||||
if let Some(value) = value
|
||||
.strip_prefix("jsr:")
|
||||
.and_then(|value| PackageNv::from_str(value).ok())
|
||||
{
|
||||
graph.packages.add_nv(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
graph.fill_from_lockfile(FillFromLockfileOptions {
|
||||
redirects: lockfile
|
||||
.content
|
||||
.redirects
|
||||
.iter()
|
||||
.map(|(from, to)| (from.as_str(), to.as_str())),
|
||||
package_specifiers: lockfile
|
||||
.content
|
||||
.packages
|
||||
.specifiers
|
||||
.iter()
|
||||
.map(|(dep, id)| (dep, id.as_str())),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -579,6 +602,12 @@ impl ModuleGraphBuilder {
|
|||
let initial_package_deps_len = graph.packages.package_deps_sum();
|
||||
let initial_package_mappings_len = graph.packages.mappings().len();
|
||||
|
||||
if roots.iter().any(|r| r.scheme() == "npm")
|
||||
&& self.npm_resolver.as_byonm().is_some()
|
||||
{
|
||||
bail!("Resolving npm specifier entrypoints this way is currently not supported with \"nodeModules\": \"manual\". In the meantime, try with --node-modules-dir=auto instead");
|
||||
}
|
||||
|
||||
graph.build(roots, loader, options).await;
|
||||
|
||||
let has_redirects_changed = graph.redirects.len() != initial_redirects_len;
|
||||
|
@ -606,16 +635,15 @@ impl ModuleGraphBuilder {
|
|||
if has_jsr_package_mappings_changed {
|
||||
for (from, to) in graph.packages.mappings() {
|
||||
lockfile.insert_package_specifier(
|
||||
format!("jsr:{}", from),
|
||||
format!("jsr:{}", to),
|
||||
JsrDepPackageReq::jsr(from.clone()),
|
||||
to.version.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
// jsr packages
|
||||
if has_jsr_package_deps_changed {
|
||||
for (name, deps) in graph.packages.packages_with_deps() {
|
||||
lockfile
|
||||
.add_package_deps(&name.to_string(), deps.map(|s| s.to_string()));
|
||||
for (nv, deps) in graph.packages.packages_with_deps() {
|
||||
lockfile.add_package_deps(nv, deps.cloned());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -644,16 +672,16 @@ impl ModuleGraphBuilder {
|
|||
};
|
||||
let parser = self.parsed_source_cache.as_capturing_parser();
|
||||
let cli_resolver = &self.resolver;
|
||||
let graph_resolver = cli_resolver.as_graph_resolver();
|
||||
let graph_resolver = self.create_graph_resolver()?;
|
||||
let graph_npm_resolver = cli_resolver.create_graph_npm_resolver();
|
||||
|
||||
graph.build_fast_check_type_graph(
|
||||
deno_graph::BuildFastCheckTypeGraphOptions {
|
||||
jsr_url_provider: &CliJsrUrlProvider,
|
||||
es_parser: Some(&parser),
|
||||
fast_check_cache: fast_check_cache.as_ref().map(|c| c as _),
|
||||
fast_check_dts: false,
|
||||
module_parser: Some(&parser),
|
||||
resolver: Some(graph_resolver),
|
||||
jsr_url_provider: &CliJsrUrlProvider,
|
||||
resolver: Some(&graph_resolver),
|
||||
npm_resolver: Some(&graph_npm_resolver),
|
||||
workspace_fast_check: options.workspace_fast_check,
|
||||
},
|
||||
|
@ -663,7 +691,7 @@ impl ModuleGraphBuilder {
|
|||
|
||||
/// Creates the default loader used for creating a graph.
|
||||
pub fn create_graph_loader(&self) -> cache::FetchCacher {
|
||||
self.create_fetch_cacher(PermissionsContainer::allow_all())
|
||||
self.create_fetch_cacher(self.root_permissions_container.clone())
|
||||
}
|
||||
|
||||
pub fn create_fetch_cacher(
|
||||
|
@ -671,13 +699,19 @@ impl ModuleGraphBuilder {
|
|||
permissions: PermissionsContainer,
|
||||
) -> cache::FetchCacher {
|
||||
cache::FetchCacher::new(
|
||||
self.emit_cache.clone(),
|
||||
self.file_fetcher.clone(),
|
||||
self.options.resolve_file_header_overrides(),
|
||||
self.fs.clone(),
|
||||
self.global_http_cache.clone(),
|
||||
self.npm_resolver.clone(),
|
||||
self.in_npm_pkg_checker.clone(),
|
||||
self.module_info_cache.clone(),
|
||||
permissions,
|
||||
cache::FetchCacherOptions {
|
||||
file_header_overrides: self.cli_options.resolve_file_header_overrides(),
|
||||
permissions,
|
||||
is_deno_publish: matches!(
|
||||
self.cli_options.sub_command(),
|
||||
crate::args::DenoSubcommand::Publish { .. }
|
||||
),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -701,42 +735,53 @@ impl ModuleGraphBuilder {
|
|||
&self.fs,
|
||||
roots,
|
||||
GraphValidOptions {
|
||||
is_vendoring: false,
|
||||
follow_type_only: self.options.type_check_mode().is_true(),
|
||||
check_js: self.options.check_js(),
|
||||
exit_lockfile_errors: true,
|
||||
kind: if self.cli_options.type_check_mode().is_true() {
|
||||
GraphKind::All
|
||||
} else {
|
||||
GraphKind::CodeOnly
|
||||
},
|
||||
check_js: self.cli_options.check_js(),
|
||||
exit_integrity_errors: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn error_for_any_npm_specifier(
|
||||
graph: &ModuleGraph,
|
||||
) -> Result<(), AnyError> {
|
||||
for module in graph.modules() {
|
||||
match module {
|
||||
Module::Npm(module) => {
|
||||
bail!("npm specifiers have not yet been implemented for this subcommand (https://github.com/denoland/deno/issues/15960). Found: {}", module.specifier)
|
||||
}
|
||||
Module::Node(module) => {
|
||||
bail!("Node specifiers have not yet been implemented for this subcommand (https://github.com/denoland/deno/issues/15960). Found: node:{}", module.module_name)
|
||||
}
|
||||
Module::Js(_) | Module::Json(_) | Module::External(_) => {}
|
||||
}
|
||||
fn create_graph_resolver(&self) -> Result<CliGraphResolver, AnyError> {
|
||||
let jsx_import_source_config = self
|
||||
.cli_options
|
||||
.workspace()
|
||||
.to_maybe_jsx_import_source_config()?;
|
||||
Ok(CliGraphResolver {
|
||||
cjs_tracker: &self.cjs_tracker,
|
||||
resolver: &self.resolver,
|
||||
jsx_import_source_config,
|
||||
})
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds more explanatory information to a resolution error.
|
||||
pub fn enhanced_resolution_error_message(error: &ResolutionError) -> String {
|
||||
let mut message = format!("{error}");
|
||||
let mut message = format_deno_graph_error(error);
|
||||
|
||||
if let Some(specifier) = get_resolution_error_bare_node_specifier(error) {
|
||||
let maybe_hint = if let Some(specifier) =
|
||||
get_resolution_error_bare_node_specifier(error)
|
||||
{
|
||||
if !*DENO_DISABLE_PEDANTIC_NODE_WARNINGS {
|
||||
message.push_str(&format!(
|
||||
"\nIf you want to use a built-in Node module, add a \"node:\" prefix (ex. \"node:{specifier}\")."
|
||||
));
|
||||
Some(format!("If you want to use a built-in Node module, add a \"node:\" prefix (ex. \"node:{specifier}\")."))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
get_import_prefix_missing_error(error).map(|specifier| {
|
||||
format!(
|
||||
"If you want to use a JSR or npm package, try running `deno add jsr:{}` or `deno add npm:{}`",
|
||||
specifier, specifier
|
||||
)
|
||||
})
|
||||
};
|
||||
|
||||
if let Some(hint) = maybe_hint {
|
||||
message.push_str(&format!("\n {} {}", colors::cyan("hint:"), hint));
|
||||
}
|
||||
|
||||
message
|
||||
|
@ -749,8 +794,8 @@ fn enhanced_sloppy_imports_error_message(
|
|||
match error {
|
||||
ModuleError::LoadingErr(specifier, _, ModuleLoadError::Loader(_)) // ex. "Is a directory" error
|
||||
| ModuleError::Missing(specifier, _) => {
|
||||
let additional_message = SloppyImportsResolver::new(fs.clone())
|
||||
.resolve(specifier, ResolutionMode::Execution)?
|
||||
let additional_message = CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(fs.clone()))
|
||||
.resolve(specifier, SloppyImportsResolutionMode::Execution)?
|
||||
.as_suggestion_message();
|
||||
Some(format!(
|
||||
"{} {} or run with --unstable-sloppy-imports",
|
||||
|
@ -762,7 +807,7 @@ fn enhanced_sloppy_imports_error_message(
|
|||
}
|
||||
}
|
||||
|
||||
fn enhanced_lockfile_error_message(err: &ModuleError) -> Option<String> {
|
||||
fn enhanced_integrity_error_message(err: &ModuleError) -> Option<String> {
|
||||
match err {
|
||||
ModuleError::LoadingErr(
|
||||
specifier,
|
||||
|
@ -806,7 +851,7 @@ fn enhanced_lockfile_error_message(err: &ModuleError) -> Option<String> {
|
|||
"This could be caused by:\n",
|
||||
" * the lock file may be corrupt\n",
|
||||
" * the source itself may be corrupt\n\n",
|
||||
"Use the --lock-write flag to regenerate the lockfile or --reload to reload the source code from the server."
|
||||
"Investigate the lockfile; delete it to regenerate the lockfile or --reload to reload the source code from the server."
|
||||
),
|
||||
package_nv,
|
||||
checksum_err.actual,
|
||||
|
@ -827,7 +872,7 @@ fn enhanced_lockfile_error_message(err: &ModuleError) -> Option<String> {
|
|||
"This could be caused by:\n",
|
||||
" * the lock file may be corrupt\n",
|
||||
" * the source itself may be corrupt\n\n",
|
||||
"Use the --lock-write flag to regenerate the lockfile or --reload to reload the source code from the server."
|
||||
"Investigate the lockfile; delete it to regenerate the lockfile or --reload to reload the source code from the server."
|
||||
),
|
||||
specifier,
|
||||
checksum_err.actual,
|
||||
|
@ -871,6 +916,50 @@ fn get_resolution_error_bare_specifier(
|
|||
}
|
||||
}
|
||||
|
||||
fn get_import_prefix_missing_error(error: &ResolutionError) -> Option<&str> {
|
||||
let mut maybe_specifier = None;
|
||||
if let ResolutionError::InvalidSpecifier {
|
||||
error: SpecifierError::ImportPrefixMissing { specifier, .. },
|
||||
range,
|
||||
} = error
|
||||
{
|
||||
if range.specifier.scheme() == "file" {
|
||||
maybe_specifier = Some(specifier);
|
||||
}
|
||||
} else if let ResolutionError::ResolverError { error, range, .. } = error {
|
||||
if range.specifier.scheme() == "file" {
|
||||
match error.as_ref() {
|
||||
ResolveError::Specifier(specifier_error) => {
|
||||
if let SpecifierError::ImportPrefixMissing { specifier, .. } =
|
||||
specifier_error
|
||||
{
|
||||
maybe_specifier = Some(specifier);
|
||||
}
|
||||
}
|
||||
ResolveError::Other(other_error) => {
|
||||
if let Some(SpecifierError::ImportPrefixMissing {
|
||||
specifier, ..
|
||||
}) = other_error.downcast_ref::<SpecifierError>()
|
||||
{
|
||||
maybe_specifier = Some(specifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(bartlomieju): For now, return None if a specifier contains a dot or a space. This is because
|
||||
// suggesting to `deno add bad-module.ts` makes no sense and is worse than not providing
|
||||
// a suggestion at all. This should be improved further in the future
|
||||
if let Some(specifier) = maybe_specifier {
|
||||
if specifier.contains('.') || specifier.contains(' ') {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
maybe_specifier.map(|s| s.as_str())
|
||||
}
|
||||
|
||||
/// Gets if any of the specified root's "file:" dependents are in the
|
||||
/// provided changed set.
|
||||
pub fn has_graph_root_local_dependent_changed(
|
||||
|
@ -882,13 +971,13 @@ pub fn has_graph_root_local_dependent_changed(
|
|||
std::iter::once(root),
|
||||
deno_graph::WalkOptions {
|
||||
follow_dynamic: true,
|
||||
follow_type_only: true,
|
||||
kind: GraphKind::All,
|
||||
prefer_fast_check_graph: true,
|
||||
check_js: true,
|
||||
},
|
||||
);
|
||||
while let Some((s, _)) = dependent_specifiers.next() {
|
||||
if let Ok(path) = specifier_to_file_path(s) {
|
||||
if let Ok(path) = url_to_file_path(s) {
|
||||
if let Ok(path) = canonicalize_path(&path) {
|
||||
if canonicalized_changed_paths.contains(&path) {
|
||||
return true;
|
||||
|
@ -930,7 +1019,11 @@ impl deno_graph::source::Reporter for FileWatcherReporter {
|
|||
) {
|
||||
let mut file_paths = self.file_paths.lock();
|
||||
if specifier.scheme() == "file" {
|
||||
file_paths.push(specifier.to_file_path().unwrap());
|
||||
// Don't trust that the path is a valid path at this point:
|
||||
// https://github.com/denoland/deno/issues/26209.
|
||||
if let Ok(file_path) = specifier.to_file_path() {
|
||||
file_paths.push(file_path);
|
||||
}
|
||||
}
|
||||
|
||||
if modules_done == modules_total {
|
||||
|
@ -1025,6 +1118,96 @@ impl deno_graph::source::JsrUrlProvider for CliJsrUrlProvider {
|
|||
}
|
||||
}
|
||||
|
||||
// todo(dsherret): We should change ModuleError to use thiserror so that
|
||||
// we don't need to do this.
|
||||
fn format_deno_graph_error(err: &dyn Error) -> String {
|
||||
use std::fmt::Write;
|
||||
|
||||
let mut message = format!("{}", err);
|
||||
let mut maybe_source = err.source();
|
||||
|
||||
if maybe_source.is_some() {
|
||||
let mut past_message = message.clone();
|
||||
let mut count = 0;
|
||||
let mut display_count = 0;
|
||||
while let Some(source) = maybe_source {
|
||||
let current_message = format!("{}", source);
|
||||
maybe_source = source.source();
|
||||
|
||||
// sometimes an error might be repeated due to
|
||||
// being boxed multiple times in another AnyError
|
||||
if current_message != past_message {
|
||||
write!(message, "\n {}: ", display_count,).unwrap();
|
||||
for (i, line) in current_message.lines().enumerate() {
|
||||
if i > 0 {
|
||||
write!(message, "\n {}", line).unwrap();
|
||||
} else {
|
||||
write!(message, "{}", line).unwrap();
|
||||
}
|
||||
}
|
||||
display_count += 1;
|
||||
}
|
||||
|
||||
if count > 8 {
|
||||
write!(message, "\n {}: ...", count).unwrap();
|
||||
break;
|
||||
}
|
||||
|
||||
past_message = current_message;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
message
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CliGraphResolver<'a> {
|
||||
cjs_tracker: &'a CjsTracker,
|
||||
resolver: &'a CliResolver,
|
||||
jsx_import_source_config: Option<JsxImportSourceConfig>,
|
||||
}
|
||||
|
||||
impl<'a> deno_graph::source::Resolver for CliGraphResolver<'a> {
|
||||
fn default_jsx_import_source(&self) -> Option<String> {
|
||||
self
|
||||
.jsx_import_source_config
|
||||
.as_ref()
|
||||
.and_then(|c| c.default_specifier.clone())
|
||||
}
|
||||
|
||||
fn default_jsx_import_source_types(&self) -> Option<String> {
|
||||
self
|
||||
.jsx_import_source_config
|
||||
.as_ref()
|
||||
.and_then(|c| c.default_types_specifier.clone())
|
||||
}
|
||||
|
||||
fn jsx_import_source_module(&self) -> &str {
|
||||
self
|
||||
.jsx_import_source_config
|
||||
.as_ref()
|
||||
.map(|c| c.module.as_str())
|
||||
.unwrap_or(deno_graph::source::DEFAULT_JSX_IMPORT_SOURCE_MODULE)
|
||||
}
|
||||
|
||||
fn resolve(
|
||||
&self,
|
||||
raw_specifier: &str,
|
||||
referrer_range: &deno_graph::Range,
|
||||
mode: ResolutionMode,
|
||||
) -> Result<ModuleSpecifier, ResolveError> {
|
||||
self.resolver.resolve(
|
||||
raw_specifier,
|
||||
referrer_range,
|
||||
self
|
||||
.cjs_tracker
|
||||
.get_referrer_kind(&referrer_range.specifier),
|
||||
mode,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::sync::Arc;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use crate::auth_tokens::AuthToken;
|
||||
use crate::util::progress_bar::UpdateGuard;
|
||||
use crate::version::get_user_agent;
|
||||
use crate::version;
|
||||
|
||||
use cache_control::Cachability;
|
||||
use cache_control::CacheControl;
|
||||
|
@ -19,10 +19,12 @@ use deno_runtime::deno_fetch;
|
|||
use deno_runtime::deno_fetch::create_http_client;
|
||||
use deno_runtime::deno_fetch::CreateHttpClientOptions;
|
||||
use deno_runtime::deno_tls::RootCertStoreProvider;
|
||||
use http::header;
|
||||
use http::header::HeaderName;
|
||||
use http::header::HeaderValue;
|
||||
use http::header::ACCEPT;
|
||||
use http::header::AUTHORIZATION;
|
||||
use http::header::CONTENT_LENGTH;
|
||||
use http::header::IF_NONE_MATCH;
|
||||
use http::header::LOCATION;
|
||||
use http::StatusCode;
|
||||
|
@ -203,6 +205,7 @@ pub struct FetchOnceArgs<'a> {
|
|||
pub maybe_accept: Option<String>,
|
||||
pub maybe_etag: Option<String>,
|
||||
pub maybe_auth_token: Option<AuthToken>,
|
||||
pub maybe_auth: Option<(header::HeaderName, header::HeaderValue)>,
|
||||
pub maybe_progress_guard: Option<&'a UpdateGuard>,
|
||||
}
|
||||
|
||||
|
@ -247,7 +250,7 @@ impl HttpClientProvider {
|
|||
Entry::Occupied(entry) => Ok(HttpClient::new(entry.get().clone())),
|
||||
Entry::Vacant(entry) => {
|
||||
let client = create_http_client(
|
||||
get_user_agent(),
|
||||
version::DENO_VERSION_INFO.user_agent,
|
||||
CreateHttpClientOptions {
|
||||
root_cert_store: match &self.root_cert_store_provider {
|
||||
Some(provider) => Some(provider.get_or_try_init()?.clone()),
|
||||
|
@ -381,6 +384,8 @@ impl HttpClient {
|
|||
request
|
||||
.headers_mut()
|
||||
.insert(AUTHORIZATION, authorization_val);
|
||||
} else if let Some((header, value)) = args.maybe_auth {
|
||||
request.headers_mut().insert(header, value);
|
||||
}
|
||||
if let Some(accept) = args.maybe_accept {
|
||||
let accepts_val = HeaderValue::from_str(&accept)?;
|
||||
|
@ -389,10 +394,10 @@ impl HttpClient {
|
|||
let response = match self.client.clone().send(request).await {
|
||||
Ok(resp) => resp,
|
||||
Err(err) => {
|
||||
if is_error_connect(&err) {
|
||||
if err.is_connect_error() {
|
||||
return Ok(FetchOnceResult::RequestError(err.to_string()));
|
||||
}
|
||||
return Err(err);
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -469,15 +474,23 @@ impl HttpClient {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn download_with_progress(
|
||||
pub async fn download_with_progress_and_retries(
|
||||
&self,
|
||||
url: Url,
|
||||
maybe_header: Option<(HeaderName, HeaderValue)>,
|
||||
progress_guard: &UpdateGuard,
|
||||
) -> Result<Option<Vec<u8>>, DownloadError> {
|
||||
self
|
||||
.download_inner(url, maybe_header, Some(progress_guard))
|
||||
.await
|
||||
crate::util::retry::retry(
|
||||
|| {
|
||||
self.download_inner(
|
||||
url.clone(),
|
||||
maybe_header.clone(),
|
||||
Some(progress_guard),
|
||||
)
|
||||
},
|
||||
|e| matches!(e, DownloadError::BadResponse(_) | DownloadError::Fetch(_)),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_redirected_url(
|
||||
|
@ -530,7 +543,7 @@ impl HttpClient {
|
|||
.clone()
|
||||
.send(req)
|
||||
.await
|
||||
.map_err(DownloadError::Fetch)?;
|
||||
.map_err(|e| DownloadError::Fetch(e.into()))?;
|
||||
let status = response.status();
|
||||
if status.is_redirection() {
|
||||
for _ in 0..5 {
|
||||
|
@ -550,7 +563,7 @@ impl HttpClient {
|
|||
.clone()
|
||||
.send(req)
|
||||
.await
|
||||
.map_err(DownloadError::Fetch)?;
|
||||
.map_err(|e| DownloadError::Fetch(e.into()))?;
|
||||
let status = new_response.status();
|
||||
if status.is_redirection() {
|
||||
response = new_response;
|
||||
|
@ -566,20 +579,21 @@ impl HttpClient {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_error_connect(err: &AnyError) -> bool {
|
||||
err
|
||||
.downcast_ref::<hyper_util::client::legacy::Error>()
|
||||
.map(|err| err.is_connect())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
async fn get_response_body_with_progress(
|
||||
response: http::Response<deno_fetch::ResBody>,
|
||||
progress_guard: Option<&UpdateGuard>,
|
||||
) -> Result<Vec<u8>, AnyError> {
|
||||
use http_body::Body as _;
|
||||
if let Some(progress_guard) = progress_guard {
|
||||
if let Some(total_size) = response.body().size_hint().exact() {
|
||||
let mut total_size = response.body().size_hint().exact();
|
||||
if total_size.is_none() {
|
||||
total_size = response
|
||||
.headers()
|
||||
.get(CONTENT_LENGTH)
|
||||
.and_then(|val| val.to_str().ok())
|
||||
.and_then(|s| s.parse::<u64>().ok());
|
||||
}
|
||||
if let Some(total_size) = total_size {
|
||||
progress_guard.set_total_size(total_size);
|
||||
let mut current_size = 0;
|
||||
let mut data = Vec::with_capacity(total_size as usize);
|
||||
|
@ -676,7 +690,7 @@ impl RequestBuilder {
|
|||
pub async fn send(
|
||||
self,
|
||||
) -> Result<http::Response<deno_fetch::ResBody>, AnyError> {
|
||||
self.client.send(self.req).await
|
||||
self.client.send(self.req).await.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn build(self) -> http::Request<deno_fetch::ReqBody> {
|
||||
|
@ -782,6 +796,7 @@ mod test {
|
|||
maybe_etag: None,
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
if let Ok(FetchOnceResult::Code(body, headers)) = result {
|
||||
|
@ -808,6 +823,7 @@ mod test {
|
|||
maybe_etag: None,
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
if let Ok(FetchOnceResult::Code(body, headers)) = result {
|
||||
|
@ -835,6 +851,7 @@ mod test {
|
|||
maybe_etag: None,
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
if let Ok(FetchOnceResult::Code(body, headers)) = result {
|
||||
|
@ -856,6 +873,7 @@ mod test {
|
|||
maybe_etag: Some("33a64df551425fcc55e".to_string()),
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
assert_eq!(res.unwrap(), FetchOnceResult::NotModified);
|
||||
|
@ -875,6 +893,7 @@ mod test {
|
|||
maybe_etag: None,
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
if let Ok(FetchOnceResult::Code(body, headers)) = result {
|
||||
|
@ -904,6 +923,7 @@ mod test {
|
|||
maybe_etag: None,
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
if let Ok(FetchOnceResult::Code(body, _)) = result {
|
||||
|
@ -929,6 +949,7 @@ mod test {
|
|||
maybe_etag: None,
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
if let Ok(FetchOnceResult::Redirect(url, _)) = result {
|
||||
|
@ -946,7 +967,7 @@ mod test {
|
|||
|
||||
let client = HttpClient::new(
|
||||
create_http_client(
|
||||
version::get_user_agent(),
|
||||
version::DENO_VERSION_INFO.user_agent,
|
||||
CreateHttpClientOptions {
|
||||
ca_certs: vec![std::fs::read(
|
||||
test_util::testdata_path().join("tls/RootCA.pem"),
|
||||
|
@ -964,6 +985,7 @@ mod test {
|
|||
maybe_etag: None,
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
if let Ok(FetchOnceResult::Code(body, headers)) = result {
|
||||
|
@ -998,7 +1020,7 @@ mod test {
|
|||
|
||||
let client = HttpClient::new(
|
||||
create_http_client(
|
||||
version::get_user_agent(),
|
||||
version::DENO_VERSION_INFO.user_agent,
|
||||
CreateHttpClientOptions::default(),
|
||||
)
|
||||
.unwrap(),
|
||||
|
@ -1011,6 +1033,7 @@ mod test {
|
|||
maybe_etag: None,
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
|
||||
|
@ -1057,7 +1080,7 @@ mod test {
|
|||
|
||||
let client = HttpClient::new(
|
||||
create_http_client(
|
||||
version::get_user_agent(),
|
||||
version::DENO_VERSION_INFO.user_agent,
|
||||
CreateHttpClientOptions {
|
||||
root_cert_store: Some(root_cert_store),
|
||||
..Default::default()
|
||||
|
@ -1073,6 +1096,7 @@ mod test {
|
|||
maybe_etag: None,
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
|
||||
|
@ -1106,7 +1130,7 @@ mod test {
|
|||
.unwrap();
|
||||
let client = HttpClient::new(
|
||||
create_http_client(
|
||||
version::get_user_agent(),
|
||||
version::DENO_VERSION_INFO.user_agent,
|
||||
CreateHttpClientOptions {
|
||||
ca_certs: vec![std::fs::read(
|
||||
test_util::testdata_path()
|
||||
|
@ -1126,6 +1150,7 @@ mod test {
|
|||
maybe_etag: None,
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
if let Ok(FetchOnceResult::Code(body, headers)) = result {
|
||||
|
@ -1147,7 +1172,7 @@ mod test {
|
|||
let url = Url::parse("https://localhost:5545/etag_script.ts").unwrap();
|
||||
let client = HttpClient::new(
|
||||
create_http_client(
|
||||
version::get_user_agent(),
|
||||
version::DENO_VERSION_INFO.user_agent,
|
||||
CreateHttpClientOptions {
|
||||
ca_certs: vec![std::fs::read(
|
||||
test_util::testdata_path()
|
||||
|
@ -1167,6 +1192,7 @@ mod test {
|
|||
maybe_etag: None,
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
if let Ok(FetchOnceResult::Code(body, headers)) = result {
|
||||
|
@ -1189,6 +1215,7 @@ mod test {
|
|||
maybe_etag: Some("33a64df551425fcc55e".to_string()),
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
assert_eq!(res.unwrap(), FetchOnceResult::NotModified);
|
||||
|
@ -1203,7 +1230,7 @@ mod test {
|
|||
.unwrap();
|
||||
let client = HttpClient::new(
|
||||
create_http_client(
|
||||
version::get_user_agent(),
|
||||
version::DENO_VERSION_INFO.user_agent,
|
||||
CreateHttpClientOptions {
|
||||
ca_certs: vec![std::fs::read(
|
||||
test_util::testdata_path()
|
||||
|
@ -1223,6 +1250,7 @@ mod test {
|
|||
maybe_etag: None,
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
if let Ok(FetchOnceResult::Code(body, headers)) = result {
|
||||
|
@ -1252,6 +1280,7 @@ mod test {
|
|||
maybe_etag: None,
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
assert!(result.is_err());
|
||||
|
@ -1273,6 +1302,7 @@ mod test {
|
|||
maybe_etag: None,
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
|
||||
|
@ -1296,6 +1326,7 @@ mod test {
|
|||
maybe_etag: None,
|
||||
maybe_auth_token: None,
|
||||
maybe_progress_guard: None,
|
||||
maybe_auth: None,
|
||||
})
|
||||
.await;
|
||||
|
||||
|
|
|
@ -104,12 +104,12 @@ function bench(
|
|||
}
|
||||
if (optionsOrFn.fn != undefined) {
|
||||
throw new TypeError(
|
||||
"Unexpected 'fn' field in options, bench function is already provided as the third argument.",
|
||||
"Unexpected 'fn' field in options, bench function is already provided as the third argument",
|
||||
);
|
||||
}
|
||||
if (optionsOrFn.name != undefined) {
|
||||
throw new TypeError(
|
||||
"Unexpected 'name' field in options, bench name is already provided as the first argument.",
|
||||
"Unexpected 'name' field in options, bench name is already provided as the first argument",
|
||||
);
|
||||
}
|
||||
benchDesc = {
|
||||
|
@ -141,7 +141,7 @@ function bench(
|
|||
fn = optionsOrFn;
|
||||
if (nameOrFnOrOptions.fn != undefined) {
|
||||
throw new TypeError(
|
||||
"Unexpected 'fn' field in options, bench function is already provided as the second argument.",
|
||||
"Unexpected 'fn' field in options, bench function is already provided as the second argument",
|
||||
);
|
||||
}
|
||||
name = nameOrFnOrOptions.name ?? fn.name;
|
||||
|
@ -150,7 +150,7 @@ function bench(
|
|||
!nameOrFnOrOptions.fn || typeof nameOrFnOrOptions.fn !== "function"
|
||||
) {
|
||||
throw new TypeError(
|
||||
"Expected 'fn' field in the first argument to be a bench function.",
|
||||
"Expected 'fn' field in the first argument to be a bench function",
|
||||
);
|
||||
}
|
||||
fn = nameOrFnOrOptions.fn;
|
||||
|
@ -385,12 +385,12 @@ function createBenchContext(desc) {
|
|||
start() {
|
||||
if (currentBenchId !== desc.id) {
|
||||
throw new TypeError(
|
||||
"The benchmark which this context belongs to is not being executed.",
|
||||
"The benchmark which this context belongs to is not being executed",
|
||||
);
|
||||
}
|
||||
if (currentBenchUserExplicitStart != null) {
|
||||
throw new TypeError(
|
||||
"BenchContext::start() has already been invoked.",
|
||||
"BenchContext::start() has already been invoked",
|
||||
);
|
||||
}
|
||||
currentBenchUserExplicitStart = benchNow();
|
||||
|
@ -399,11 +399,11 @@ function createBenchContext(desc) {
|
|||
const end = benchNow();
|
||||
if (currentBenchId !== desc.id) {
|
||||
throw new TypeError(
|
||||
"The benchmark which this context belongs to is not being executed.",
|
||||
"The benchmark which this context belongs to is not being executed",
|
||||
);
|
||||
}
|
||||
if (currentBenchUserExplicitEnd != null) {
|
||||
throw new TypeError("BenchContext::end() has already been invoked.");
|
||||
throw new TypeError("BenchContext::end() has already been invoked");
|
||||
}
|
||||
currentBenchUserExplicitEnd = end;
|
||||
},
|
||||
|
|
|
@ -177,6 +177,52 @@ function isCanvasLike(obj) {
|
|||
return obj !== null && typeof obj === "object" && "toDataURL" in obj;
|
||||
}
|
||||
|
||||
function isJpg(obj) {
|
||||
// Check if obj is a Uint8Array
|
||||
if (!(obj instanceof Uint8Array)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// JPG files start with the magic bytes FF D8
|
||||
if (obj.length < 2 || obj[0] !== 0xFF || obj[1] !== 0xD8) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// JPG files end with the magic bytes FF D9
|
||||
if (
|
||||
obj.length < 2 || obj[obj.length - 2] !== 0xFF ||
|
||||
obj[obj.length - 1] !== 0xD9
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function isPng(obj) {
|
||||
// Check if obj is a Uint8Array
|
||||
if (!(obj instanceof Uint8Array)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// PNG files start with a specific 8-byte signature
|
||||
const pngSignature = [137, 80, 78, 71, 13, 10, 26, 10];
|
||||
|
||||
// Check if the array is at least as long as the signature
|
||||
if (obj.length < pngSignature.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check each byte of the signature
|
||||
for (let i = 0; i < pngSignature.length; i++) {
|
||||
if (obj[i] !== pngSignature[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Possible HTML and SVG Elements */
|
||||
function isSVGElementLike(obj) {
|
||||
return obj !== null && typeof obj === "object" && "outerHTML" in obj &&
|
||||
|
@ -233,6 +279,16 @@ async function format(obj) {
|
|||
if (isDataFrameLike(obj)) {
|
||||
return extractDataFrame(obj);
|
||||
}
|
||||
if (isJpg(obj)) {
|
||||
return {
|
||||
"image/jpeg": core.ops.op_base64_encode(obj),
|
||||
};
|
||||
}
|
||||
if (isPng(obj)) {
|
||||
return {
|
||||
"image/png": core.ops.op_base64_encode(obj),
|
||||
};
|
||||
}
|
||||
if (isSVGElementLike(obj)) {
|
||||
return {
|
||||
"image/svg+xml": obj.outerHTML,
|
||||
|
@ -314,6 +370,28 @@ const html = createTaggedTemplateDisplayable("text/html");
|
|||
*/
|
||||
const svg = createTaggedTemplateDisplayable("image/svg+xml");
|
||||
|
||||
function image(obj) {
|
||||
if (typeof obj === "string") {
|
||||
try {
|
||||
obj = Deno.readFileSync(obj);
|
||||
} catch {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
if (isJpg(obj)) {
|
||||
return makeDisplayable({ "image/jpeg": core.ops.op_base64_encode(obj) });
|
||||
}
|
||||
|
||||
if (isPng(obj)) {
|
||||
return makeDisplayable({ "image/png": core.ops.op_base64_encode(obj) });
|
||||
}
|
||||
|
||||
throw new TypeError(
|
||||
"Object is not a valid image or a path to an image. `Deno.jupyter.image` supports displaying JPG or PNG images.",
|
||||
);
|
||||
}
|
||||
|
||||
function isMediaBundle(obj) {
|
||||
if (obj == null || typeof obj !== "object" || Array.isArray(obj)) {
|
||||
return false;
|
||||
|
@ -465,6 +543,7 @@ function enableJupyter() {
|
|||
md,
|
||||
html,
|
||||
svg,
|
||||
image,
|
||||
$display,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ function assertExit(fn, isTest) {
|
|||
throw new Error(
|
||||
`${
|
||||
isTest ? "Test case" : "Bench"
|
||||
} finished with exit code set to ${exitCode}.`,
|
||||
} finished with exit code set to ${exitCode}`,
|
||||
);
|
||||
}
|
||||
if (innerResult) {
|
||||
|
@ -242,12 +242,12 @@ function testInner(
|
|||
}
|
||||
if (optionsOrFn.fn != undefined) {
|
||||
throw new TypeError(
|
||||
"Unexpected 'fn' field in options, test function is already provided as the third argument.",
|
||||
"Unexpected 'fn' field in options, test function is already provided as the third argument",
|
||||
);
|
||||
}
|
||||
if (optionsOrFn.name != undefined) {
|
||||
throw new TypeError(
|
||||
"Unexpected 'name' field in options, test name is already provided as the first argument.",
|
||||
"Unexpected 'name' field in options, test name is already provided as the first argument",
|
||||
);
|
||||
}
|
||||
testDesc = {
|
||||
|
@ -279,7 +279,7 @@ function testInner(
|
|||
fn = optionsOrFn;
|
||||
if (nameOrFnOrOptions.fn != undefined) {
|
||||
throw new TypeError(
|
||||
"Unexpected 'fn' field in options, test function is already provided as the second argument.",
|
||||
"Unexpected 'fn' field in options, test function is already provided as the second argument",
|
||||
);
|
||||
}
|
||||
name = nameOrFnOrOptions.name ?? fn.name;
|
||||
|
@ -288,7 +288,7 @@ function testInner(
|
|||
!nameOrFnOrOptions.fn || typeof nameOrFnOrOptions.fn !== "function"
|
||||
) {
|
||||
throw new TypeError(
|
||||
"Expected 'fn' field in the first argument to be a test function.",
|
||||
"Expected 'fn' field in the first argument to be a test function",
|
||||
);
|
||||
}
|
||||
fn = nameOrFnOrOptions.fn;
|
||||
|
@ -426,7 +426,7 @@ function createTestContext(desc) {
|
|||
let stepDesc;
|
||||
if (typeof nameOrFnOrOptions === "string") {
|
||||
if (typeof maybeFn !== "function") {
|
||||
throw new TypeError("Expected function for second argument.");
|
||||
throw new TypeError("Expected function for second argument");
|
||||
}
|
||||
stepDesc = {
|
||||
name: nameOrFnOrOptions,
|
||||
|
@ -434,7 +434,7 @@ function createTestContext(desc) {
|
|||
};
|
||||
} else if (typeof nameOrFnOrOptions === "function") {
|
||||
if (!nameOrFnOrOptions.name) {
|
||||
throw new TypeError("The step function must have a name.");
|
||||
throw new TypeError("The step function must have a name");
|
||||
}
|
||||
if (maybeFn != undefined) {
|
||||
throw new TypeError(
|
||||
|
@ -449,7 +449,7 @@ function createTestContext(desc) {
|
|||
stepDesc = nameOrFnOrOptions;
|
||||
} else {
|
||||
throw new TypeError(
|
||||
"Expected a test definition or name and function.",
|
||||
"Expected a test definition or name and function",
|
||||
);
|
||||
}
|
||||
stepDesc.ignore ??= false;
|
||||
|
|
11
cli/jsr.rs
11
cli/jsr.rs
|
@ -6,7 +6,6 @@ use dashmap::DashMap;
|
|||
use deno_core::serde_json;
|
||||
use deno_graph::packages::JsrPackageInfo;
|
||||
use deno_graph::packages::JsrPackageVersionInfo;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use std::sync::Arc;
|
||||
|
@ -68,10 +67,7 @@ impl JsrFetchResolver {
|
|||
let file_fetcher = self.file_fetcher.clone();
|
||||
// spawn due to the lsp's `Send` requirement
|
||||
let file = deno_core::unsync::spawn(async move {
|
||||
file_fetcher
|
||||
.fetch(&meta_url, &PermissionsContainer::allow_all())
|
||||
.await
|
||||
.ok()
|
||||
file_fetcher.fetch_bypass_permissions(&meta_url).await.ok()
|
||||
})
|
||||
.await
|
||||
.ok()??;
|
||||
|
@ -96,10 +92,7 @@ impl JsrFetchResolver {
|
|||
let file_fetcher = self.file_fetcher.clone();
|
||||
// spawn due to the lsp's `Send` requirement
|
||||
let file = deno_core::unsync::spawn(async move {
|
||||
file_fetcher
|
||||
.fetch(&meta_url, &PermissionsContainer::allow_all())
|
||||
.await
|
||||
.ok()
|
||||
file_fetcher.fetch_bypass_permissions(&meta_url).await.ok()
|
||||
})
|
||||
.await
|
||||
.ok()??;
|
||||
|
|
|
@ -2,20 +2,25 @@
|
|||
|
||||
use super::diagnostics::DenoDiagnostic;
|
||||
use super::diagnostics::DiagnosticSource;
|
||||
use super::documents::Document;
|
||||
use super::documents::Documents;
|
||||
use super::language_server;
|
||||
use super::resolver::LspResolver;
|
||||
use super::tsc;
|
||||
use super::urls::url_to_uri;
|
||||
|
||||
use crate::args::jsr_url;
|
||||
use crate::lsp::logging::lsp_warn;
|
||||
use crate::lsp::search::PackageSearchApi;
|
||||
use crate::tools::lint::CliLinter;
|
||||
use crate::util::path::relative_specifier;
|
||||
use deno_config::workspace::MappedResolution;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_lint::diagnostic::LintDiagnosticRange;
|
||||
use deno_runtime::fs_util::specifier_to_file_path;
|
||||
|
||||
use deno_ast::SourceRange;
|
||||
use deno_ast::SourceRangedForSpanned;
|
||||
use deno_ast::SourceTextInfo;
|
||||
use deno_core::anyhow::anyhow;
|
||||
use deno_core::error::custom_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde::Deserialize;
|
||||
|
@ -23,6 +28,7 @@ use deno_core::serde::Serialize;
|
|||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_runtime::deno_node::PathClean;
|
||||
use deno_semver::jsr::JsrPackageNvReference;
|
||||
use deno_semver::jsr::JsrPackageReqReference;
|
||||
|
@ -33,13 +39,15 @@ use deno_semver::package::PackageReq;
|
|||
use deno_semver::package::PackageReqReference;
|
||||
use deno_semver::Version;
|
||||
use import_map::ImportMap;
|
||||
use node_resolver::NpmResolver;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
use text_lines::LineAndColumnIndex;
|
||||
use tower_lsp::lsp_types as lsp;
|
||||
use tower_lsp::lsp_types::Position;
|
||||
use tower_lsp::lsp_types::Range;
|
||||
|
@ -224,6 +232,8 @@ pub struct TsResponseImportMapper<'a> {
|
|||
documents: &'a Documents,
|
||||
maybe_import_map: Option<&'a ImportMap>,
|
||||
resolver: &'a LspResolver,
|
||||
tsc_specifier_map: &'a tsc::TscSpecifierMap,
|
||||
file_referrer: ModuleSpecifier,
|
||||
}
|
||||
|
||||
impl<'a> TsResponseImportMapper<'a> {
|
||||
|
@ -231,11 +241,15 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
documents: &'a Documents,
|
||||
maybe_import_map: Option<&'a ImportMap>,
|
||||
resolver: &'a LspResolver,
|
||||
tsc_specifier_map: &'a tsc::TscSpecifierMap,
|
||||
file_referrer: &ModuleSpecifier,
|
||||
) -> Self {
|
||||
Self {
|
||||
documents,
|
||||
maybe_import_map,
|
||||
resolver,
|
||||
tsc_specifier_map,
|
||||
file_referrer: file_referrer.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,8 +270,6 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
let file_referrer = self.documents.get_file_referrer(referrer);
|
||||
|
||||
if let Some(jsr_path) = specifier.as_str().strip_prefix(jsr_url().as_str())
|
||||
{
|
||||
let mut segments = jsr_path.split('/');
|
||||
|
@ -272,7 +284,7 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
let export = self.resolver.jsr_lookup_export_for_path(
|
||||
&nv,
|
||||
&path,
|
||||
file_referrer.as_deref(),
|
||||
Some(&self.file_referrer),
|
||||
)?;
|
||||
let sub_path = (export != ".").then_some(export);
|
||||
let mut req = None;
|
||||
|
@ -298,7 +310,7 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
req = req.or_else(|| {
|
||||
self
|
||||
.resolver
|
||||
.jsr_lookup_req_for_nv(&nv, file_referrer.as_deref())
|
||||
.jsr_lookup_req_for_nv(&nv, Some(&self.file_referrer))
|
||||
});
|
||||
let spec_str = if let Some(req) = req {
|
||||
let req_ref = PackageReqReference { req, sub_path };
|
||||
|
@ -312,15 +324,29 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
if let Some(result) = import_map.lookup(&specifier, referrer) {
|
||||
return Some(result);
|
||||
}
|
||||
if let Some(req_ref_str) = specifier.as_str().strip_prefix("jsr:") {
|
||||
if !req_ref_str.starts_with('/') {
|
||||
let specifier_str = format!("jsr:/{req_ref_str}");
|
||||
if let Ok(specifier) = ModuleSpecifier::parse(&specifier_str) {
|
||||
if let Some(result) = import_map.lookup(&specifier, referrer) {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Some(spec_str);
|
||||
}
|
||||
|
||||
if let Some(npm_resolver) = self
|
||||
.resolver
|
||||
.maybe_managed_npm_resolver(file_referrer.as_deref())
|
||||
.maybe_managed_npm_resolver(Some(&self.file_referrer))
|
||||
{
|
||||
if npm_resolver.in_npm_package(specifier) {
|
||||
let in_npm_pkg = self
|
||||
.resolver
|
||||
.in_npm_pkg_checker(Some(&self.file_referrer))
|
||||
.in_npm_package(specifier);
|
||||
if in_npm_pkg {
|
||||
if let Ok(Some(pkg_id)) =
|
||||
npm_resolver.resolve_pkg_id_from_specifier(specifier)
|
||||
{
|
||||
|
@ -367,6 +393,11 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(dep_name) = self
|
||||
.resolver
|
||||
.file_url_to_package_json_dep(specifier, Some(&self.file_referrer))
|
||||
{
|
||||
return Some(dep_name);
|
||||
}
|
||||
|
||||
// check if the import map has this specifier
|
||||
|
@ -390,7 +421,7 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
.flatten()?;
|
||||
let root_folder = package_json.path.parent()?;
|
||||
|
||||
let specifier_path = specifier_to_file_path(specifier).ok()?;
|
||||
let specifier_path = url_to_file_path(specifier).ok()?;
|
||||
let mut search_paths = vec![specifier_path.clone()];
|
||||
// TypeScript will provide a .js extension for quick fixes, so do
|
||||
// a search for the .d.ts file instead
|
||||
|
@ -436,24 +467,65 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
&self,
|
||||
specifier: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
) -> Option<String> {
|
||||
if let Ok(specifier) = referrer.join(specifier) {
|
||||
if let Some(specifier) = self.check_specifier(&specifier, referrer) {
|
||||
return Some(specifier);
|
||||
}
|
||||
}
|
||||
let specifier = specifier.strip_suffix(".js").unwrap_or(specifier);
|
||||
for ext in SUPPORTED_EXTENSIONS {
|
||||
let specifier_with_ext = format!("{specifier}{ext}");
|
||||
if self
|
||||
.documents
|
||||
.contains_import(&specifier_with_ext, referrer)
|
||||
let specifier_stem = specifier.strip_suffix(".js").unwrap_or(specifier);
|
||||
let specifiers = std::iter::once(Cow::Borrowed(specifier)).chain(
|
||||
SUPPORTED_EXTENSIONS
|
||||
.iter()
|
||||
.map(|ext| Cow::Owned(format!("{specifier_stem}{ext}"))),
|
||||
);
|
||||
for specifier in specifiers {
|
||||
if let Some(specifier) = self
|
||||
.resolver
|
||||
.as_cli_resolver(Some(&self.file_referrer))
|
||||
.resolve(
|
||||
&specifier,
|
||||
&deno_graph::Range {
|
||||
specifier: referrer.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
},
|
||||
referrer_kind,
|
||||
ResolutionMode::Types,
|
||||
)
|
||||
.ok()
|
||||
.and_then(|s| self.tsc_specifier_map.normalize(s.as_str()).ok())
|
||||
.filter(|s| self.documents.exists(s, Some(&self.file_referrer)))
|
||||
{
|
||||
return Some(specifier_with_ext);
|
||||
if let Some(specifier) = self
|
||||
.check_specifier(&specifier, referrer)
|
||||
.or_else(|| relative_specifier(referrer, &specifier))
|
||||
.filter(|s| !s.contains("/node_modules/"))
|
||||
{
|
||||
return Some(specifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn is_valid_import(
|
||||
&self,
|
||||
specifier_text: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
) -> bool {
|
||||
self
|
||||
.resolver
|
||||
.as_cli_resolver(Some(&self.file_referrer))
|
||||
.resolve(
|
||||
specifier_text,
|
||||
&deno_graph::Range {
|
||||
specifier: referrer.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
},
|
||||
referrer_kind,
|
||||
deno_graph::source::ResolutionMode::Types,
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
fn try_reverse_map_package_json_exports(
|
||||
|
@ -518,9 +590,11 @@ fn try_reverse_map_package_json_exports(
|
|||
/// like an import and rewrite the import specifier to include the extension
|
||||
pub fn fix_ts_import_changes(
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
changes: &[tsc::FileTextChanges],
|
||||
import_mapper: &TsResponseImportMapper,
|
||||
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 mut text_changes = Vec::new();
|
||||
|
@ -533,8 +607,8 @@ pub fn fix_ts_import_changes(
|
|||
if let Some(captures) = IMPORT_SPECIFIER_RE.captures(line) {
|
||||
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)
|
||||
if let Some(new_specifier) = import_mapper
|
||||
.check_unresolved_specifier(specifier, referrer, referrer_kind)
|
||||
{
|
||||
line.replace(specifier, &new_specifier)
|
||||
} else {
|
||||
|
@ -562,66 +636,64 @@ pub fn fix_ts_import_changes(
|
|||
|
||||
/// Fix tsc import code actions so that the module specifier is correct for
|
||||
/// resolution by Deno (includes the extension).
|
||||
fn fix_ts_import_action(
|
||||
fn fix_ts_import_action<'a>(
|
||||
referrer: &ModuleSpecifier,
|
||||
action: &tsc::CodeFixAction,
|
||||
import_mapper: &TsResponseImportMapper,
|
||||
) -> Result<tsc::CodeFixAction, AnyError> {
|
||||
if matches!(
|
||||
referrer_kind: NodeModuleKind,
|
||||
action: &'a tsc::CodeFixAction,
|
||||
language_server: &language_server::Inner,
|
||||
) -> Option<Cow<'a, tsc::CodeFixAction>> {
|
||||
if !matches!(
|
||||
action.fix_name.as_str(),
|
||||
"import" | "fixMissingFunctionDeclaration"
|
||||
) {
|
||||
let change = action
|
||||
return Some(Cow::Borrowed(action));
|
||||
}
|
||||
let specifier = (|| {
|
||||
let text_change = action.changes.first()?.text_changes.first()?;
|
||||
let captures = IMPORT_SPECIFIER_RE.captures(&text_change.new_text)?;
|
||||
Some(captures.get(1)?.as_str())
|
||||
})();
|
||||
let Some(specifier) = specifier else {
|
||||
return Some(Cow::Borrowed(action));
|
||||
};
|
||||
let import_mapper = language_server.get_ts_response_import_mapper(referrer);
|
||||
if let Some(new_specifier) =
|
||||
import_mapper.check_unresolved_specifier(specifier, referrer, referrer_kind)
|
||||
{
|
||||
let description = action.description.replace(specifier, &new_specifier);
|
||||
let changes = action
|
||||
.changes
|
||||
.first()
|
||||
.ok_or_else(|| anyhow!("Unexpected action changes."))?;
|
||||
let text_change = change
|
||||
.text_changes
|
||||
.first()
|
||||
.ok_or_else(|| anyhow!("Missing text change."))?;
|
||||
if let Some(captures) = IMPORT_SPECIFIER_RE.captures(&text_change.new_text)
|
||||
{
|
||||
let specifier = captures
|
||||
.get(1)
|
||||
.ok_or_else(|| anyhow!("Missing capture."))?
|
||||
.as_str();
|
||||
if let Some(new_specifier) =
|
||||
import_mapper.check_unresolved_specifier(specifier, referrer)
|
||||
{
|
||||
let description = action.description.replace(specifier, &new_specifier);
|
||||
let changes = action
|
||||
.changes
|
||||
.iter()
|
||||
.map(|c| {
|
||||
let text_changes = c
|
||||
.text_changes
|
||||
.iter()
|
||||
.map(|c| {
|
||||
let text_changes = c
|
||||
.text_changes
|
||||
.iter()
|
||||
.map(|tc| tsc::TextChange {
|
||||
span: tc.span.clone(),
|
||||
new_text: tc.new_text.replace(specifier, &new_specifier),
|
||||
})
|
||||
.collect();
|
||||
tsc::FileTextChanges {
|
||||
file_name: c.file_name.clone(),
|
||||
text_changes,
|
||||
is_new_file: c.is_new_file,
|
||||
}
|
||||
.map(|tc| tsc::TextChange {
|
||||
span: tc.span.clone(),
|
||||
new_text: tc.new_text.replace(specifier, &new_specifier),
|
||||
})
|
||||
.collect();
|
||||
tsc::FileTextChanges {
|
||||
file_name: c.file_name.clone(),
|
||||
text_changes,
|
||||
is_new_file: c.is_new_file,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
return Ok(tsc::CodeFixAction {
|
||||
description,
|
||||
changes,
|
||||
commands: None,
|
||||
fix_name: action.fix_name.clone(),
|
||||
fix_id: None,
|
||||
fix_all_description: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
Some(Cow::Owned(tsc::CodeFixAction {
|
||||
description,
|
||||
changes,
|
||||
commands: None,
|
||||
fix_name: action.fix_name.clone(),
|
||||
fix_id: None,
|
||||
fix_all_description: None,
|
||||
}))
|
||||
} else if !import_mapper.is_valid_import(specifier, referrer, referrer_kind) {
|
||||
None
|
||||
} else {
|
||||
Some(Cow::Borrowed(action))
|
||||
}
|
||||
|
||||
Ok(action.clone())
|
||||
}
|
||||
|
||||
/// Determines if two TypeScript diagnostic codes are effectively equivalent.
|
||||
|
@ -682,8 +754,14 @@ pub fn ts_changes_to_edit(
|
|||
) -> Result<Option<lsp::WorkspaceEdit>, AnyError> {
|
||||
let mut text_document_edits = Vec::new();
|
||||
for change in changes {
|
||||
let text_document_edit = change.to_text_document_edit(language_server)?;
|
||||
text_document_edits.push(text_document_edit);
|
||||
let edit = match change.to_text_document_edit(language_server) {
|
||||
Ok(e) => e,
|
||||
Err(err) => {
|
||||
lsp_warn!("Couldn't covert text document edit: {:#}", err);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
text_document_edits.push(edit);
|
||||
}
|
||||
Ok(Some(lsp::WorkspaceEdit {
|
||||
changes: None,
|
||||
|
@ -692,7 +770,7 @@ pub fn ts_changes_to_edit(
|
|||
}))
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CodeActionData {
|
||||
pub specifier: ModuleSpecifier,
|
||||
|
@ -740,10 +818,11 @@ impl CodeActionCollection {
|
|||
.as_ref()
|
||||
.and_then(|d| serde_json::from_value::<Vec<DataQuickFix>>(d.clone()).ok())
|
||||
{
|
||||
let uri = url_to_uri(specifier)?;
|
||||
for quick_fix in data_quick_fixes {
|
||||
let mut changes = HashMap::new();
|
||||
changes.insert(
|
||||
specifier.clone(),
|
||||
uri.clone(),
|
||||
quick_fix
|
||||
.changes
|
||||
.into_iter()
|
||||
|
@ -785,6 +864,7 @@ impl CodeActionCollection {
|
|||
maybe_text_info: Option<&SourceTextInfo>,
|
||||
maybe_parsed_source: Option<&deno_ast::ParsedSource>,
|
||||
) -> Result<(), AnyError> {
|
||||
let uri = url_to_uri(specifier)?;
|
||||
let code = diagnostic
|
||||
.code
|
||||
.as_ref()
|
||||
|
@ -801,7 +881,7 @@ impl CodeActionCollection {
|
|||
|
||||
let mut changes = HashMap::new();
|
||||
changes.insert(
|
||||
specifier.clone(),
|
||||
uri.clone(),
|
||||
vec![lsp::TextEdit {
|
||||
new_text: prepend_whitespace(
|
||||
format!("// deno-lint-ignore {code}\n"),
|
||||
|
@ -882,7 +962,7 @@ impl CodeActionCollection {
|
|||
}
|
||||
|
||||
let mut changes = HashMap::new();
|
||||
changes.insert(specifier.clone(), vec![lsp::TextEdit { new_text, range }]);
|
||||
changes.insert(uri.clone(), vec![lsp::TextEdit { new_text, range }]);
|
||||
let ignore_file_action = lsp::CodeAction {
|
||||
title: format!("Disable {code} for the entire file"),
|
||||
kind: Some(lsp::CodeActionKind::QUICKFIX),
|
||||
|
@ -903,7 +983,7 @@ impl CodeActionCollection {
|
|||
|
||||
let mut changes = HashMap::new();
|
||||
changes.insert(
|
||||
specifier.clone(),
|
||||
uri,
|
||||
vec![lsp::TextEdit {
|
||||
new_text: "// deno-lint-ignore-file\n".to_string(),
|
||||
range: lsp::Range {
|
||||
|
@ -943,6 +1023,7 @@ impl CodeActionCollection {
|
|||
pub fn add_ts_fix_action(
|
||||
&mut self,
|
||||
specifier: &ModuleSpecifier,
|
||||
specifier_kind: NodeModuleKind,
|
||||
action: &tsc::CodeFixAction,
|
||||
diagnostic: &lsp::Diagnostic,
|
||||
language_server: &language_server::Inner,
|
||||
|
@ -960,11 +1041,11 @@ impl CodeActionCollection {
|
|||
"The action returned from TypeScript is unsupported.",
|
||||
));
|
||||
}
|
||||
let action = fix_ts_import_action(
|
||||
specifier,
|
||||
action,
|
||||
&language_server.get_ts_response_import_mapper(specifier),
|
||||
)?;
|
||||
let Some(action) =
|
||||
fix_ts_import_action(specifier, specifier_kind, action, language_server)
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
let edit = ts_changes_to_edit(&action.changes, language_server)?;
|
||||
let code_action = lsp::CodeAction {
|
||||
title: action.description.clone(),
|
||||
|
@ -984,7 +1065,7 @@ impl CodeActionCollection {
|
|||
});
|
||||
self
|
||||
.actions
|
||||
.push(CodeActionKind::Tsc(code_action, action.clone()));
|
||||
.push(CodeActionKind::Tsc(code_action, action.as_ref().clone()));
|
||||
|
||||
if let Some(fix_id) = &action.fix_id {
|
||||
if let Some(CodeActionKind::Tsc(existing_fix_all, existing_action)) =
|
||||
|
@ -1011,10 +1092,12 @@ impl CodeActionCollection {
|
|||
specifier: &ModuleSpecifier,
|
||||
diagnostic: &lsp::Diagnostic,
|
||||
) {
|
||||
let data = Some(json!({
|
||||
"specifier": specifier,
|
||||
"fixId": action.fix_id,
|
||||
}));
|
||||
let data = action.fix_id.as_ref().map(|fix_id| {
|
||||
json!(CodeActionData {
|
||||
specifier: specifier.clone(),
|
||||
fix_id: fix_id.clone(),
|
||||
})
|
||||
});
|
||||
let title = if let Some(description) = &action.fix_all_description {
|
||||
description.clone()
|
||||
} else {
|
||||
|
@ -1138,6 +1221,192 @@ impl CodeActionCollection {
|
|||
..Default::default()
|
||||
}));
|
||||
}
|
||||
|
||||
pub async fn add_source_actions(
|
||||
&mut self,
|
||||
document: &Document,
|
||||
range: &lsp::Range,
|
||||
language_server: &language_server::Inner,
|
||||
) {
|
||||
fn import_start_from_specifier(
|
||||
document: &Document,
|
||||
import: &deno_graph::Import,
|
||||
) -> Option<LineAndColumnIndex> {
|
||||
// find the top level statement that contains the specifier
|
||||
let parsed_source = document.maybe_parsed_source()?.as_ref().ok()?;
|
||||
let text_info = parsed_source.text_info_lazy();
|
||||
let specifier_range = SourceRange::new(
|
||||
text_info.loc_to_source_pos(LineAndColumnIndex {
|
||||
line_index: import.specifier_range.start.line,
|
||||
column_index: import.specifier_range.start.character,
|
||||
}),
|
||||
text_info.loc_to_source_pos(LineAndColumnIndex {
|
||||
line_index: import.specifier_range.end.line,
|
||||
column_index: import.specifier_range.end.character,
|
||||
}),
|
||||
);
|
||||
|
||||
parsed_source
|
||||
.program_ref()
|
||||
.body()
|
||||
.find(|i| i.range().contains(&specifier_range))
|
||||
.map(|i| text_info.line_and_column_index(i.range().start))
|
||||
}
|
||||
|
||||
async fn deno_types_for_npm_action(
|
||||
document: &Document,
|
||||
range: &lsp::Range,
|
||||
language_server: &language_server::Inner,
|
||||
) -> Option<lsp::CodeAction> {
|
||||
let (dep_key, dependency, _) =
|
||||
document.get_maybe_dependency(&range.end)?;
|
||||
if dependency.maybe_deno_types_specifier.is_some() {
|
||||
return None;
|
||||
}
|
||||
if dependency.maybe_code.maybe_specifier().is_none()
|
||||
&& dependency.maybe_type.maybe_specifier().is_none()
|
||||
{
|
||||
// We're using byonm and the package is not cached.
|
||||
return None;
|
||||
}
|
||||
let position = deno_graph::Position::new(
|
||||
range.end.line as usize,
|
||||
range.end.character as usize,
|
||||
);
|
||||
let import_start = dependency.imports.iter().find_map(|i| {
|
||||
if json!(i.kind) != json!("es") && json!(i.kind) != json!("tsType") {
|
||||
return None;
|
||||
}
|
||||
if !i.specifier_range.includes(&position) {
|
||||
return None;
|
||||
}
|
||||
|
||||
import_start_from_specifier(document, i)
|
||||
})?;
|
||||
let referrer = document.specifier();
|
||||
let referrer_kind = language_server
|
||||
.is_cjs_resolver
|
||||
.get_doc_module_kind(document);
|
||||
let file_referrer = document.file_referrer();
|
||||
let config_data = language_server
|
||||
.config
|
||||
.tree
|
||||
.data_for_specifier(file_referrer?)?;
|
||||
let workspace_resolver = config_data.resolver.clone();
|
||||
let npm_ref = if let Ok(resolution) =
|
||||
workspace_resolver.resolve(&dep_key, document.specifier())
|
||||
{
|
||||
let specifier = match resolution {
|
||||
MappedResolution::Normal { specifier, .. }
|
||||
| MappedResolution::ImportMap { specifier, .. } => specifier,
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
NpmPackageReqReference::from_specifier(&specifier).ok()?
|
||||
} else {
|
||||
// Only resolve bare package.json deps for byonm.
|
||||
if !config_data.byonm {
|
||||
return None;
|
||||
}
|
||||
if !language_server.resolver.is_bare_package_json_dep(
|
||||
&dep_key,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
NpmPackageReqReference::from_str(&format!("npm:{}", &dep_key)).ok()?
|
||||
};
|
||||
let package_name = &npm_ref.req().name;
|
||||
if package_name.starts_with("@types/") {
|
||||
return None;
|
||||
}
|
||||
let managed_npm_resolver = language_server
|
||||
.resolver
|
||||
.maybe_managed_npm_resolver(file_referrer);
|
||||
if let Some(npm_resolver) = managed_npm_resolver {
|
||||
if !npm_resolver.is_pkg_req_folder_cached(npm_ref.req()) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
if language_server
|
||||
.resolver
|
||||
.npm_to_file_url(&npm_ref, referrer, referrer_kind, file_referrer)
|
||||
.is_some()
|
||||
{
|
||||
// The package import has types.
|
||||
return None;
|
||||
}
|
||||
let types_package_name = format!("@types/{package_name}");
|
||||
let types_package_version = language_server
|
||||
.npm_search_api
|
||||
.versions(&types_package_name)
|
||||
.await
|
||||
.ok()
|
||||
.and_then(|versions| versions.first().cloned())?;
|
||||
let types_specifier_text =
|
||||
if let Some(npm_resolver) = managed_npm_resolver {
|
||||
let mut specifier_text = if let Some(req) =
|
||||
npm_resolver.top_package_req_for_name(&types_package_name)
|
||||
{
|
||||
format!("npm:{req}")
|
||||
} else {
|
||||
format!("npm:{}@^{}", &types_package_name, types_package_version)
|
||||
};
|
||||
let specifier = ModuleSpecifier::parse(&specifier_text).ok()?;
|
||||
if let Some(file_referrer) = file_referrer {
|
||||
if let Some(text) = language_server
|
||||
.get_ts_response_import_mapper(file_referrer)
|
||||
.check_specifier(&specifier, referrer)
|
||||
{
|
||||
specifier_text = text;
|
||||
}
|
||||
}
|
||||
specifier_text
|
||||
} else {
|
||||
types_package_name.clone()
|
||||
};
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.specifier_to_uri(referrer, file_referrer)
|
||||
.ok()?;
|
||||
let position = lsp::Position {
|
||||
line: import_start.line_index as u32,
|
||||
character: import_start.column_index as u32,
|
||||
};
|
||||
let new_text = format!(
|
||||
"{}// @deno-types=\"{}\"\n",
|
||||
if position.character == 0 { "" } else { "\n" },
|
||||
&types_specifier_text
|
||||
);
|
||||
let text_edit = lsp::TextEdit {
|
||||
range: lsp::Range {
|
||||
start: position,
|
||||
end: position,
|
||||
},
|
||||
new_text,
|
||||
};
|
||||
Some(lsp::CodeAction {
|
||||
title: format!(
|
||||
"Add @deno-types directive for \"{}\"",
|
||||
&types_specifier_text
|
||||
),
|
||||
kind: Some(lsp::CodeActionKind::QUICKFIX),
|
||||
diagnostics: None,
|
||||
edit: Some(lsp::WorkspaceEdit {
|
||||
changes: Some([(uri, vec![text_edit])].into_iter().collect()),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
if let Some(action) =
|
||||
deno_types_for_npm_action(document, range, language_server).await
|
||||
{
|
||||
self.actions.push(CodeActionKind::Deno(action));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Prepend the whitespace characters found at the start of line_content to content.
|
||||
|
|
|
@ -7,26 +7,16 @@ use crate::cache::LocalLspHttpCache;
|
|||
use crate::lsp::config::Config;
|
||||
use crate::lsp::logging::lsp_log;
|
||||
use crate::lsp::logging::lsp_warn;
|
||||
use deno_runtime::fs_util::specifier_to_file_path;
|
||||
|
||||
use deno_core::url::Url;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::time::SystemTime;
|
||||
|
||||
/// In the LSP, we disallow the cache from automatically copying from
|
||||
/// the global cache to the local cache for technical reasons.
|
||||
///
|
||||
/// 1. We need to verify the checksums from the lockfile are correct when
|
||||
/// moving from the global to the local cache.
|
||||
/// 2. We need to verify the checksums for JSR https specifiers match what
|
||||
/// is found in the package's manifest.
|
||||
pub const LSP_DISALLOW_GLOBAL_TO_LOCAL_COPY: deno_cache_dir::GlobalToLocalCopy =
|
||||
deno_cache_dir::GlobalToLocalCopy::Disallow;
|
||||
|
||||
pub fn calculate_fs_version(
|
||||
cache: &LspCache,
|
||||
specifier: &ModuleSpecifier,
|
||||
|
@ -34,7 +24,7 @@ pub fn calculate_fs_version(
|
|||
) -> Option<String> {
|
||||
match specifier.scheme() {
|
||||
"npm" | "node" | "data" | "blob" => None,
|
||||
"file" => specifier_to_file_path(specifier)
|
||||
"file" => url_to_file_path(specifier)
|
||||
.ok()
|
||||
.and_then(|path| calculate_fs_version_at_path(&path)),
|
||||
_ => calculate_fs_version_in_cache(cache, specifier, file_referrer),
|
||||
|
@ -92,7 +82,7 @@ impl Default for LspCache {
|
|||
impl LspCache {
|
||||
pub fn new(global_cache_url: Option<Url>) -> Self {
|
||||
let global_cache_path = global_cache_url.and_then(|s| {
|
||||
specifier_to_file_path(&s)
|
||||
url_to_file_path(&s)
|
||||
.inspect(|p| {
|
||||
lsp_log!("Resolved global cache path: \"{}\"", p.to_string_lossy());
|
||||
})
|
||||
|
@ -104,7 +94,7 @@ impl LspCache {
|
|||
let deno_dir = DenoDir::new(global_cache_path)
|
||||
.expect("should be infallible with absolute custom root");
|
||||
let global = Arc::new(GlobalHttpCache::new(
|
||||
deno_dir.deps_folder_path(),
|
||||
deno_dir.remote_folder_path(),
|
||||
crate::cache::RealDenoCacheEnv,
|
||||
));
|
||||
Self {
|
||||
|
@ -175,7 +165,7 @@ impl LspCache {
|
|||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<ModuleSpecifier> {
|
||||
let path = specifier_to_file_path(specifier).ok()?;
|
||||
let path = url_to_file_path(specifier).ok()?;
|
||||
let vendor = self
|
||||
.vendors_by_scope
|
||||
.iter()
|
||||
|
@ -186,7 +176,7 @@ impl LspCache {
|
|||
}
|
||||
|
||||
pub fn is_valid_file_referrer(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
if let Ok(path) = specifier_to_file_path(specifier) {
|
||||
if let Ok(path) = url_to_file_path(specifier) {
|
||||
if !path.starts_with(&self.deno_dir().root) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -147,12 +147,14 @@ pub fn server_capabilities(
|
|||
moniker_provider: None,
|
||||
experimental: Some(json!({
|
||||
"denoConfigTasks": true,
|
||||
"testingApi":true,
|
||||
"testingApi": true,
|
||||
"didRefreshDenoConfigurationTreeNotifications": true,
|
||||
})),
|
||||
inlay_hint_provider: Some(OneOf::Left(true)),
|
||||
position_encoding: None,
|
||||
// TODO(nayeemrmn): Support pull-based diagnostics.
|
||||
diagnostic_provider: None,
|
||||
inline_value_provider: None,
|
||||
inline_completion_provider: None,
|
||||
notebook_document_sync: None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use deno_core::anyhow::bail;
|
|||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::unsync::spawn;
|
||||
use lsp_types::Uri;
|
||||
use tower_lsp::lsp_types as lsp;
|
||||
use tower_lsp::lsp_types::ConfigurationItem;
|
||||
|
||||
|
@ -17,7 +18,6 @@ use super::config::WorkspaceSettings;
|
|||
use super::config::SETTINGS_SECTION;
|
||||
use super::lsp_custom;
|
||||
use super::testing::lsp_custom as testing_lsp_custom;
|
||||
use super::urls::LspClientUrl;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum TestingNotification {
|
||||
|
@ -52,14 +52,11 @@ impl Client {
|
|||
|
||||
pub async fn publish_diagnostics(
|
||||
&self,
|
||||
uri: LspClientUrl,
|
||||
uri: Uri,
|
||||
diags: Vec<lsp::Diagnostic>,
|
||||
version: Option<i32>,
|
||||
) {
|
||||
self
|
||||
.0
|
||||
.publish_diagnostics(uri.into_url(), diags, version)
|
||||
.await;
|
||||
self.0.publish_diagnostics(uri, diags, version).await;
|
||||
}
|
||||
|
||||
pub fn send_registry_state_notification(
|
||||
|
@ -95,6 +92,19 @@ impl Client {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn send_did_refresh_deno_configuration_tree_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
|
||||
) {
|
||||
// do on a task in case the caller currently is in the lsp lock
|
||||
let client = self.0.clone();
|
||||
spawn(async move {
|
||||
client
|
||||
.send_did_refresh_deno_configuration_tree_notification(params)
|
||||
.await;
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_did_change_deno_configuration_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
|
||||
|
@ -149,7 +159,7 @@ impl OutsideLockClient {
|
|||
|
||||
pub async fn workspace_configuration(
|
||||
&self,
|
||||
scopes: Vec<Option<lsp::Url>>,
|
||||
scopes: Vec<Option<lsp::Uri>>,
|
||||
) -> Result<Vec<WorkspaceSettings>, AnyError> {
|
||||
self.0.workspace_configuration(scopes).await
|
||||
}
|
||||
|
@ -159,7 +169,7 @@ impl OutsideLockClient {
|
|||
trait ClientTrait: Send + Sync {
|
||||
async fn publish_diagnostics(
|
||||
&self,
|
||||
uri: lsp::Url,
|
||||
uri: lsp::Uri,
|
||||
diagnostics: Vec<lsp::Diagnostic>,
|
||||
version: Option<i32>,
|
||||
);
|
||||
|
@ -172,6 +182,10 @@ trait ClientTrait: Send + Sync {
|
|||
params: lsp_custom::DiagnosticBatchNotificationParams,
|
||||
);
|
||||
async fn send_test_notification(&self, params: TestingNotification);
|
||||
async fn send_did_refresh_deno_configuration_tree_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
|
||||
);
|
||||
async fn send_did_change_deno_configuration_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
|
||||
|
@ -182,7 +196,7 @@ trait ClientTrait: Send + Sync {
|
|||
);
|
||||
async fn workspace_configuration(
|
||||
&self,
|
||||
scopes: Vec<Option<lsp::Url>>,
|
||||
scopes: Vec<Option<lsp::Uri>>,
|
||||
) -> Result<Vec<WorkspaceSettings>, AnyError>;
|
||||
async fn show_message(&self, message_type: lsp::MessageType, text: String);
|
||||
async fn register_capability(
|
||||
|
@ -198,7 +212,7 @@ struct TowerClient(tower_lsp::Client);
|
|||
impl ClientTrait for TowerClient {
|
||||
async fn publish_diagnostics(
|
||||
&self,
|
||||
uri: lsp::Url,
|
||||
uri: lsp::Uri,
|
||||
diagnostics: Vec<lsp::Diagnostic>,
|
||||
version: Option<i32>,
|
||||
) {
|
||||
|
@ -252,6 +266,18 @@ impl ClientTrait for TowerClient {
|
|||
}
|
||||
}
|
||||
|
||||
async fn send_did_refresh_deno_configuration_tree_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
|
||||
) {
|
||||
self
|
||||
.0
|
||||
.send_notification::<lsp_custom::DidRefreshDenoConfigurationTreeNotification>(
|
||||
params,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn send_did_change_deno_configuration_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
|
||||
|
@ -276,7 +302,7 @@ impl ClientTrait for TowerClient {
|
|||
|
||||
async fn workspace_configuration(
|
||||
&self,
|
||||
scopes: Vec<Option<lsp::Url>>,
|
||||
scopes: Vec<Option<lsp::Uri>>,
|
||||
) -> Result<Vec<WorkspaceSettings>, AnyError> {
|
||||
let config_response = self
|
||||
.0
|
||||
|
@ -349,7 +375,7 @@ struct ReplClient;
|
|||
impl ClientTrait for ReplClient {
|
||||
async fn publish_diagnostics(
|
||||
&self,
|
||||
_uri: lsp::Url,
|
||||
_uri: lsp::Uri,
|
||||
_diagnostics: Vec<lsp::Diagnostic>,
|
||||
_version: Option<i32>,
|
||||
) {
|
||||
|
@ -369,6 +395,12 @@ impl ClientTrait for ReplClient {
|
|||
|
||||
async fn send_test_notification(&self, _params: TestingNotification) {}
|
||||
|
||||
async fn send_did_refresh_deno_configuration_tree_notification(
|
||||
&self,
|
||||
_params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
|
||||
) {
|
||||
}
|
||||
|
||||
async fn send_did_change_deno_configuration_notification(
|
||||
&self,
|
||||
_params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
|
||||
|
@ -383,7 +415,7 @@ impl ClientTrait for ReplClient {
|
|||
|
||||
async fn workspace_configuration(
|
||||
&self,
|
||||
scopes: Vec<Option<lsp::Url>>,
|
||||
scopes: Vec<Option<lsp::Uri>>,
|
||||
) -> Result<Vec<WorkspaceSettings>, AnyError> {
|
||||
Ok(vec![get_repl_workspace_settings(); scopes.len()])
|
||||
}
|
||||
|
|
|
@ -107,7 +107,7 @@ impl DenoTestCollector {
|
|||
for prop in &obj_lit.props {
|
||||
if let ast::PropOrSpread::Prop(prop) = prop {
|
||||
if let ast::Prop::KeyValue(key_value_prop) = prop.as_ref() {
|
||||
if let ast::PropName::Ident(ast::Ident { sym, .. }) =
|
||||
if let ast::PropName::Ident(ast::IdentName { sym, .. }) =
|
||||
&key_value_prop.key
|
||||
{
|
||||
if sym == "name" {
|
||||
|
@ -421,7 +421,7 @@ pub fn collect_test(
|
|||
) -> Result<Vec<lsp::CodeLens>, AnyError> {
|
||||
let mut collector =
|
||||
DenoTestCollector::new(specifier.clone(), parsed_source.clone());
|
||||
parsed_source.module().visit_with(&mut collector);
|
||||
parsed_source.program().visit_with(&mut collector);
|
||||
Ok(collector.take())
|
||||
}
|
||||
|
||||
|
@ -581,7 +581,7 @@ mod tests {
|
|||
.unwrap();
|
||||
let mut collector =
|
||||
DenoTestCollector::new(specifier, parsed_module.clone());
|
||||
parsed_module.module().visit_with(&mut collector);
|
||||
parsed_module.program().visit_with(&mut collector);
|
||||
assert_eq!(
|
||||
collector.take(),
|
||||
vec![
|
||||
|
|
|
@ -9,6 +9,7 @@ use super::jsr::CliJsrSearchApi;
|
|||
use super::lsp_custom;
|
||||
use super::npm::CliNpmSearchApi;
|
||||
use super::registries::ModuleRegistry;
|
||||
use super::resolver::LspIsCjsResolver;
|
||||
use super::resolver::LspResolver;
|
||||
use super::search::PackageSearchApi;
|
||||
use super::tsc;
|
||||
|
@ -18,7 +19,7 @@ use crate::util::path::is_importable_ext;
|
|||
use crate::util::path::relative_specifier;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::Range;
|
||||
use deno_runtime::fs_util::specifier_to_file_path;
|
||||
use deno_runtime::deno_node::SUPPORTED_BUILTIN_NODE_MODULES;
|
||||
|
||||
use deno_ast::LineAndColumnIndex;
|
||||
use deno_ast::SourceTextInfo;
|
||||
|
@ -29,11 +30,13 @@ use deno_core::serde::Serialize;
|
|||
use deno_core::serde_json::json;
|
||||
use deno_core::url::Position;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_semver::jsr::JsrPackageReqReference;
|
||||
use deno_semver::package::PackageNv;
|
||||
use import_map::ImportMap;
|
||||
use indexmap::IndexSet;
|
||||
use lsp_types::CompletionList;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use tower_lsp::lsp_types as lsp;
|
||||
|
@ -158,15 +161,17 @@ pub async fn get_import_completions(
|
|||
jsr_search_api: &CliJsrSearchApi,
|
||||
npm_search_api: &CliNpmSearchApi,
|
||||
documents: &Documents,
|
||||
is_cjs_resolver: &LspIsCjsResolver,
|
||||
resolver: &LspResolver,
|
||||
maybe_import_map: Option<&ImportMap>,
|
||||
) -> Option<lsp::CompletionResponse> {
|
||||
let document = documents.get(specifier)?;
|
||||
let specifier_kind = is_cjs_resolver.get_doc_module_kind(&document);
|
||||
let file_referrer = document.file_referrer();
|
||||
let (text, _, range) = document.get_maybe_dependency(position)?;
|
||||
let range = to_narrow_lsp_range(document.text_info(), &range);
|
||||
let resolved = resolver
|
||||
.as_graph_resolver(file_referrer)
|
||||
.as_cli_resolver(file_referrer)
|
||||
.resolve(
|
||||
&text,
|
||||
&Range {
|
||||
|
@ -174,6 +179,7 @@ pub async fn get_import_completions(
|
|||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
},
|
||||
specifier_kind,
|
||||
ResolutionMode::Execution,
|
||||
)
|
||||
.ok();
|
||||
|
@ -192,20 +198,18 @@ pub async fn get_import_completions(
|
|||
get_npm_completions(specifier, &text, &range, npm_search_api).await
|
||||
{
|
||||
Some(lsp::CompletionResponse::List(completion_list))
|
||||
} else if let Some(completion_list) = get_node_completions(&text, &range) {
|
||||
Some(lsp::CompletionResponse::List(completion_list))
|
||||
} else if let Some(completion_list) =
|
||||
get_import_map_completions(specifier, &text, &range, maybe_import_map)
|
||||
{
|
||||
// completions for import map specifiers
|
||||
Some(lsp::CompletionResponse::List(completion_list))
|
||||
} else if text.starts_with("./")
|
||||
|| text.starts_with("../")
|
||||
|| text.starts_with('/')
|
||||
} else if let Some(completion_list) =
|
||||
get_local_completions(specifier, specifier_kind, &text, &range, resolver)
|
||||
{
|
||||
// completions for local relative modules
|
||||
Some(lsp::CompletionResponse::List(CompletionList {
|
||||
is_incomplete: false,
|
||||
items: get_local_completions(specifier, &text, &range, resolver)?,
|
||||
}))
|
||||
Some(lsp::CompletionResponse::List(completion_list))
|
||||
} else if !text.is_empty() {
|
||||
// completion of modules from a module registry or cache
|
||||
check_auto_config_registry(
|
||||
|
@ -215,16 +219,13 @@ pub async fn get_import_completions(
|
|||
module_registries,
|
||||
)
|
||||
.await;
|
||||
let offset = if position.character > range.start.character {
|
||||
(position.character - range.start.character) as usize
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let maybe_list = module_registries
|
||||
.get_completions(&text, offset, &range, |s| {
|
||||
.get_completions(&text, &range, resolved.as_ref(), |s| {
|
||||
documents.exists(s, file_referrer)
|
||||
})
|
||||
.await;
|
||||
let maybe_list = maybe_list
|
||||
.or_else(|| module_registries.get_origin_completions(&text, &range));
|
||||
let list = maybe_list.unwrap_or_else(|| CompletionList {
|
||||
items: get_workspace_completions(specifier, &text, &range, documents),
|
||||
is_incomplete: false,
|
||||
|
@ -249,7 +250,7 @@ pub async fn get_import_completions(
|
|||
.collect();
|
||||
let mut is_incomplete = false;
|
||||
if let Some(import_map) = maybe_import_map {
|
||||
items.extend(get_base_import_map_completions(import_map));
|
||||
items.extend(get_base_import_map_completions(import_map, specifier));
|
||||
}
|
||||
if let Some(origin_items) =
|
||||
module_registries.get_origin_completions(&text, &range)
|
||||
|
@ -268,20 +269,20 @@ pub async fn get_import_completions(
|
|||
/// map as completion items.
|
||||
fn get_base_import_map_completions(
|
||||
import_map: &ImportMap,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> Vec<lsp::CompletionItem> {
|
||||
import_map
|
||||
.imports()
|
||||
.keys()
|
||||
.map(|key| {
|
||||
.entries_for_referrer(referrer)
|
||||
.map(|entry| {
|
||||
// for some strange reason, keys that start with `/` get stored in the
|
||||
// import map as `file:///`, and so when we pull the keys out, we need to
|
||||
// change the behavior
|
||||
let mut label = if key.starts_with("file://") {
|
||||
FILE_PROTO_RE.replace(key, "").to_string()
|
||||
let mut label = if entry.key.starts_with("file://") {
|
||||
FILE_PROTO_RE.replace(entry.key, "").to_string()
|
||||
} else {
|
||||
key.to_string()
|
||||
entry.key.to_string()
|
||||
};
|
||||
let kind = if key.ends_with('/') {
|
||||
let kind = if entry.key.ends_with('/') {
|
||||
label.pop();
|
||||
Some(lsp::CompletionItemKind::FOLDER)
|
||||
} else {
|
||||
|
@ -359,84 +360,86 @@ fn get_import_map_completions(
|
|||
|
||||
/// Return local completions that are relative to the base specifier.
|
||||
fn get_local_completions(
|
||||
base: &ModuleSpecifier,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
text: &str,
|
||||
range: &lsp::Range,
|
||||
resolver: &LspResolver,
|
||||
) -> Option<Vec<lsp::CompletionItem>> {
|
||||
if base.scheme() != "file" {
|
||||
) -> Option<CompletionList> {
|
||||
if referrer.scheme() != "file" {
|
||||
return None;
|
||||
}
|
||||
let parent = base.join(text).ok()?.join(".").ok()?;
|
||||
let parent = &text[..text.char_indices().rfind(|(_, c)| *c == '/')?.0 + 1];
|
||||
let resolved_parent = resolver
|
||||
.as_graph_resolver(Some(base))
|
||||
.as_cli_resolver(Some(referrer))
|
||||
.resolve(
|
||||
parent.as_str(),
|
||||
parent,
|
||||
&Range {
|
||||
specifier: base.clone(),
|
||||
specifier: referrer.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
},
|
||||
referrer_kind,
|
||||
ResolutionMode::Execution,
|
||||
)
|
||||
.ok()?;
|
||||
let resolved_parent_path = specifier_to_file_path(&resolved_parent).ok()?;
|
||||
let raw_parent =
|
||||
&text[..text.char_indices().rfind(|(_, c)| *c == '/')?.0 + 1];
|
||||
let resolved_parent_path = url_to_file_path(&resolved_parent).ok()?;
|
||||
if resolved_parent_path.is_dir() {
|
||||
let cwd = std::env::current_dir().ok()?;
|
||||
let items = std::fs::read_dir(resolved_parent_path).ok()?;
|
||||
Some(
|
||||
items
|
||||
.filter_map(|de| {
|
||||
let de = de.ok()?;
|
||||
let label = de.path().file_name()?.to_string_lossy().to_string();
|
||||
let entry_specifier = resolve_path(de.path().to_str()?, &cwd).ok()?;
|
||||
if entry_specifier == *base {
|
||||
return None;
|
||||
}
|
||||
let full_text = format!("{raw_parent}{label}");
|
||||
let text_edit = Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
|
||||
range: *range,
|
||||
new_text: full_text.clone(),
|
||||
}));
|
||||
let filter_text = Some(full_text);
|
||||
match de.file_type() {
|
||||
Ok(file_type) if file_type.is_dir() => Some(lsp::CompletionItem {
|
||||
label,
|
||||
kind: Some(lsp::CompletionItemKind::FOLDER),
|
||||
detail: Some("(local)".to_string()),
|
||||
filter_text,
|
||||
sort_text: Some("1".to_string()),
|
||||
text_edit,
|
||||
commit_characters: Some(
|
||||
IMPORT_COMMIT_CHARS.iter().map(|&c| c.into()).collect(),
|
||||
),
|
||||
..Default::default()
|
||||
}),
|
||||
Ok(file_type) if file_type.is_file() => {
|
||||
if is_importable_ext(&de.path()) {
|
||||
Some(lsp::CompletionItem {
|
||||
label,
|
||||
kind: Some(lsp::CompletionItemKind::FILE),
|
||||
detail: Some("(local)".to_string()),
|
||||
filter_text,
|
||||
sort_text: Some("1".to_string()),
|
||||
text_edit,
|
||||
commit_characters: Some(
|
||||
IMPORT_COMMIT_CHARS.iter().map(|&c| c.into()).collect(),
|
||||
),
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
let entries = std::fs::read_dir(resolved_parent_path).ok()?;
|
||||
let items = entries
|
||||
.filter_map(|de| {
|
||||
let de = de.ok()?;
|
||||
let label = de.path().file_name()?.to_string_lossy().to_string();
|
||||
let entry_specifier = resolve_path(de.path().to_str()?, &cwd).ok()?;
|
||||
if entry_specifier == *referrer {
|
||||
return None;
|
||||
}
|
||||
let full_text = format!("{parent}{label}");
|
||||
let text_edit = Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
|
||||
range: *range,
|
||||
new_text: full_text.clone(),
|
||||
}));
|
||||
let filter_text = Some(full_text);
|
||||
match de.file_type() {
|
||||
Ok(file_type) if file_type.is_dir() => Some(lsp::CompletionItem {
|
||||
label,
|
||||
kind: Some(lsp::CompletionItemKind::FOLDER),
|
||||
detail: Some("(local)".to_string()),
|
||||
filter_text,
|
||||
sort_text: Some("1".to_string()),
|
||||
text_edit,
|
||||
commit_characters: Some(
|
||||
IMPORT_COMMIT_CHARS.iter().map(|&c| c.into()).collect(),
|
||||
),
|
||||
..Default::default()
|
||||
}),
|
||||
Ok(file_type) if file_type.is_file() => {
|
||||
if is_importable_ext(&de.path()) {
|
||||
Some(lsp::CompletionItem {
|
||||
label,
|
||||
kind: Some(lsp::CompletionItemKind::FILE),
|
||||
detail: Some("(local)".to_string()),
|
||||
filter_text,
|
||||
sort_text: Some("1".to_string()),
|
||||
text_edit,
|
||||
commit_characters: Some(
|
||||
IMPORT_COMMIT_CHARS.iter().map(|&c| c.into()).collect(),
|
||||
),
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Some(CompletionList {
|
||||
is_incomplete: false,
|
||||
items,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -735,6 +738,40 @@ async fn get_npm_completions(
|
|||
})
|
||||
}
|
||||
|
||||
/// Get completions for `node:` specifiers.
|
||||
fn get_node_completions(
|
||||
specifier: &str,
|
||||
range: &lsp::Range,
|
||||
) -> Option<CompletionList> {
|
||||
if !specifier.starts_with("node:") {
|
||||
return None;
|
||||
}
|
||||
let items = SUPPORTED_BUILTIN_NODE_MODULES
|
||||
.iter()
|
||||
.map(|name| {
|
||||
let specifier = format!("node:{}", name);
|
||||
let text_edit = Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
|
||||
range: *range,
|
||||
new_text: specifier.clone(),
|
||||
}));
|
||||
lsp::CompletionItem {
|
||||
label: specifier,
|
||||
kind: Some(lsp::CompletionItemKind::FILE),
|
||||
detail: Some("(node)".to_string()),
|
||||
text_edit,
|
||||
commit_characters: Some(
|
||||
IMPORT_COMMIT_CHARS.iter().map(|&c| c.into()).collect(),
|
||||
),
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Some(CompletionList {
|
||||
is_incomplete: false,
|
||||
items,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get workspace completions that include modules in the Deno cache which match
|
||||
/// the current specifier string.
|
||||
fn get_workspace_completions(
|
||||
|
@ -804,7 +841,7 @@ mod tests {
|
|||
fs_sources: &[(&str, &str)],
|
||||
) -> Documents {
|
||||
let temp_dir = TempDir::new();
|
||||
let cache = LspCache::new(Some(temp_dir.uri().join(".deno_dir").unwrap()));
|
||||
let cache = LspCache::new(Some(temp_dir.url().join(".deno_dir").unwrap()));
|
||||
let mut documents = Documents::default();
|
||||
documents.update_config(
|
||||
&Default::default(),
|
||||
|
@ -824,8 +861,8 @@ mod tests {
|
|||
.global()
|
||||
.set(&specifier, HashMap::default(), source.as_bytes())
|
||||
.expect("could not cache file");
|
||||
let document =
|
||||
documents.get_or_load(&specifier, &temp_dir.uri().join("$").unwrap());
|
||||
let document = documents
|
||||
.get_or_load(&specifier, Some(&temp_dir.url().join("$").unwrap()));
|
||||
assert!(document.is_some(), "source could not be setup");
|
||||
}
|
||||
documents
|
||||
|
@ -875,6 +912,7 @@ mod tests {
|
|||
ModuleSpecifier::from_file_path(file_c).expect("could not create");
|
||||
let actual = get_local_completions(
|
||||
&specifier,
|
||||
NodeModuleKind::Esm,
|
||||
"./",
|
||||
&lsp::Range {
|
||||
start: lsp::Position {
|
||||
|
@ -887,11 +925,11 @@ mod tests {
|
|||
},
|
||||
},
|
||||
&Default::default(),
|
||||
);
|
||||
assert!(actual.is_some());
|
||||
let actual = actual.unwrap();
|
||||
assert_eq!(actual.len(), 3);
|
||||
for item in actual {
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!actual.is_incomplete);
|
||||
assert_eq!(actual.items.len(), 3);
|
||||
for item in actual.items {
|
||||
match item.text_edit {
|
||||
Some(lsp::CompletionTextEdit::Edit(text_edit)) => {
|
||||
assert!(["./b", "./f.mjs", "./g.json"]
|
||||
|
|
|
@ -4,7 +4,9 @@ use deno_ast::MediaType;
|
|||
use deno_config::deno_json::DenoJsonCache;
|
||||
use deno_config::deno_json::FmtConfig;
|
||||
use deno_config::deno_json::FmtOptionsConfig;
|
||||
use deno_config::deno_json::JsxImportSourceConfig;
|
||||
use deno_config::deno_json::LintConfig;
|
||||
use deno_config::deno_json::NodeModulesDirMode;
|
||||
use deno_config::deno_json::TestConfig;
|
||||
use deno_config::deno_json::TsConfig;
|
||||
use deno_config::fs::DenoConfigFs;
|
||||
|
@ -30,35 +32,40 @@ use deno_core::serde::Serialize;
|
|||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::serde_json::Value;
|
||||
use deno_core::url::Url;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_lint::linter::LintConfig as DenoLintConfig;
|
||||
use deno_npm::npm_rc::ResolvedNpmRc;
|
||||
use deno_package_json::PackageJsonCache;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_runtime::deno_node::PackageJson;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use deno_runtime::fs_util::specifier_to_file_path;
|
||||
use indexmap::IndexSet;
|
||||
use lsp::Url;
|
||||
use lsp_types::ClientCapabilities;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
use std::ops::DerefMut;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tower_lsp::lsp_types as lsp;
|
||||
|
||||
use super::logging::lsp_log;
|
||||
use super::lsp_custom;
|
||||
use super::urls::url_to_uri;
|
||||
use crate::args::discover_npmrc_from_workspace;
|
||||
use crate::args::has_flag_env_var;
|
||||
use crate::args::CliLockfile;
|
||||
use crate::args::CliLockfileReadFromPathOptions;
|
||||
use crate::args::ConfigFile;
|
||||
use crate::args::LintFlags;
|
||||
use crate::args::LintOptions;
|
||||
use crate::args::DENO_FUTURE;
|
||||
use crate::cache::FastInsecureHasher;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::lsp::logging::lsp_warn;
|
||||
use crate::resolver::SloppyImportsResolver;
|
||||
use crate::resolver::CliSloppyImportsResolver;
|
||||
use crate::resolver::SloppyImportsCachedFs;
|
||||
use crate::tools::lint::CliLinter;
|
||||
use crate::tools::lint::CliLinterOptions;
|
||||
use crate::tools::lint::LintRuleProvider;
|
||||
|
@ -70,6 +77,54 @@ fn is_true() -> bool {
|
|||
true
|
||||
}
|
||||
|
||||
/// Wrapper that defaults if it fails to deserialize. Good for individual
|
||||
/// settings.
|
||||
#[derive(Debug, Default, Clone, Eq, PartialEq)]
|
||||
pub struct SafeValue<T> {
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl<'de, T: Default + for<'de2> Deserialize<'de2>> Deserialize<'de>
|
||||
for SafeValue<T>
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
Ok(Self {
|
||||
inner: Deserialize::deserialize(deserializer).unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Serialize> Serialize for SafeValue<T> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
self.inner.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for SafeValue<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for SafeValue<T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SafeValue<T> {
|
||||
pub fn as_deref(&self) -> &T {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CodeLensSettings {
|
||||
|
@ -386,6 +441,8 @@ pub struct LanguagePreferences {
|
|||
pub use_aliases_for_renames: bool,
|
||||
#[serde(default)]
|
||||
pub quote_style: QuoteStyle,
|
||||
#[serde(default)]
|
||||
pub prefer_type_only_auto_imports: bool,
|
||||
}
|
||||
|
||||
impl Default for LanguagePreferences {
|
||||
|
@ -396,6 +453,7 @@ impl Default for LanguagePreferences {
|
|||
auto_import_file_exclude_patterns: vec![],
|
||||
use_aliases_for_renames: true,
|
||||
quote_style: Default::default(),
|
||||
prefer_type_only_auto_imports: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -538,7 +596,7 @@ pub struct WorkspaceSettings {
|
|||
pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
|
||||
|
||||
#[serde(default)]
|
||||
pub unstable: bool,
|
||||
pub unstable: SafeValue<Vec<String>>,
|
||||
|
||||
#[serde(default)]
|
||||
pub javascript: LanguageWorkspaceSettings,
|
||||
|
@ -568,7 +626,7 @@ impl Default for WorkspaceSettings {
|
|||
testing: Default::default(),
|
||||
tls_certificate: None,
|
||||
unsafely_ignore_certificate_errors: None,
|
||||
unstable: false,
|
||||
unstable: Default::default(),
|
||||
javascript: Default::default(),
|
||||
typescript: Default::default(),
|
||||
}
|
||||
|
@ -752,7 +810,7 @@ impl Settings {
|
|||
/// Returns `None` if the value should be deferred to the presence of a
|
||||
/// `deno.json` file.
|
||||
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> Option<bool> {
|
||||
let Ok(path) = specifier_to_file_path(specifier) else {
|
||||
let Ok(path) = url_to_file_path(specifier) else {
|
||||
// Non-file URLs are not disabled by these settings.
|
||||
return Some(true);
|
||||
};
|
||||
|
@ -761,7 +819,7 @@ impl Settings {
|
|||
let mut disable_paths = vec![];
|
||||
let mut enable_paths = None;
|
||||
if let Some(folder_uri) = folder_uri {
|
||||
if let Ok(folder_path) = specifier_to_file_path(folder_uri) {
|
||||
if let Ok(folder_path) = url_to_file_path(folder_uri) {
|
||||
disable_paths = settings
|
||||
.disable_paths
|
||||
.iter()
|
||||
|
@ -798,12 +856,12 @@ impl Settings {
|
|||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> (&WorkspaceSettings, Option<&ModuleSpecifier>) {
|
||||
let Ok(path) = specifier_to_file_path(specifier) else {
|
||||
let Ok(path) = url_to_file_path(specifier) else {
|
||||
return (&self.unscoped, self.first_folder.as_ref());
|
||||
};
|
||||
for (folder_uri, settings) in self.by_workspace_folder.iter().rev() {
|
||||
if let Some(settings) = settings {
|
||||
let Ok(folder_path) = specifier_to_file_path(folder_uri) else {
|
||||
let Ok(folder_path) = url_to_file_path(folder_uri) else {
|
||||
continue;
|
||||
};
|
||||
if path.starts_with(folder_path) {
|
||||
|
@ -844,14 +902,17 @@ pub struct Config {
|
|||
|
||||
impl Config {
|
||||
#[cfg(test)]
|
||||
pub fn new_with_roots(root_uris: impl IntoIterator<Item = Url>) -> Self {
|
||||
pub fn new_with_roots(root_urls: impl IntoIterator<Item = Url>) -> Self {
|
||||
use super::urls::url_to_uri;
|
||||
|
||||
let mut config = Self::default();
|
||||
let mut folders = vec![];
|
||||
for root_uri in root_uris {
|
||||
let name = root_uri.path_segments().and_then(|s| s.last());
|
||||
for root_url in root_urls {
|
||||
let root_uri = url_to_uri(&root_url).unwrap();
|
||||
let name = root_url.path_segments().and_then(|s| s.last());
|
||||
let name = name.unwrap_or_default().to_string();
|
||||
folders.push((
|
||||
root_uri.clone(),
|
||||
root_url,
|
||||
lsp::WorkspaceFolder {
|
||||
uri: root_uri,
|
||||
name,
|
||||
|
@ -925,7 +986,7 @@ impl Config {
|
|||
| MediaType::Tsx => Some(&workspace_settings.typescript),
|
||||
MediaType::Json
|
||||
| MediaType::Wasm
|
||||
| MediaType::TsBuildInfo
|
||||
| MediaType::Css
|
||||
| MediaType::SourceMap
|
||||
| MediaType::Unknown => None,
|
||||
}
|
||||
|
@ -1077,11 +1138,11 @@ impl Default for LspTsConfig {
|
|||
"module": "esnext",
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"noImplicitOverride": true,
|
||||
"resolveJsonModule": true,
|
||||
"strict": true,
|
||||
"target": "esnext",
|
||||
"useDefineForClassFields": true,
|
||||
"useUnknownInCatchVariables": false,
|
||||
"jsx": "react",
|
||||
"jsxFactory": "React.createElement",
|
||||
"jsxFragmentFactory": "React.Fragment",
|
||||
|
@ -1115,6 +1176,7 @@ pub enum ConfigWatchedFileType {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct ConfigData {
|
||||
pub scope: Arc<ModuleSpecifier>,
|
||||
pub canonicalized_scope: Option<Arc<ModuleSpecifier>>,
|
||||
pub member_dir: Arc<WorkspaceDirectory>,
|
||||
pub fmt_config: Arc<FmtConfig>,
|
||||
pub lint_config: Arc<LintConfig>,
|
||||
|
@ -1128,8 +1190,9 @@ pub struct ConfigData {
|
|||
pub lockfile: Option<Arc<CliLockfile>>,
|
||||
pub npmrc: Option<Arc<ResolvedNpmRc>>,
|
||||
pub resolver: Arc<WorkspaceResolver>,
|
||||
pub sloppy_imports_resolver: Option<Arc<SloppyImportsResolver>>,
|
||||
pub sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
|
||||
pub import_map_from_settings: Option<ModuleSpecifier>,
|
||||
pub unstable: BTreeSet<String>,
|
||||
watched_files: HashMap<ModuleSpecifier, ConfigWatchedFileType>,
|
||||
}
|
||||
|
||||
|
@ -1253,6 +1316,16 @@ impl ConfigData {
|
|||
watched_files.entry(specifier).or_insert(file_type);
|
||||
};
|
||||
|
||||
let canonicalized_scope = (|| {
|
||||
let path = scope.to_file_path().ok()?;
|
||||
let path = canonicalize_path_maybe_not_exists(&path).ok()?;
|
||||
let specifier = ModuleSpecifier::from_directory_path(path).ok()?;
|
||||
if specifier == *scope {
|
||||
return None;
|
||||
}
|
||||
Some(Arc::new(specifier))
|
||||
})();
|
||||
|
||||
if let Some(deno_json) = member_dir.maybe_deno_json() {
|
||||
lsp_log!(
|
||||
" Resolved Deno configuration file: \"{}\"",
|
||||
|
@ -1373,11 +1446,12 @@ impl ConfigData {
|
|||
}
|
||||
}
|
||||
|
||||
let byonm = std::env::var("DENO_UNSTABLE_BYONM").is_ok()
|
||||
|| member_dir.workspace.has_unstable("byonm")
|
||||
|| (*DENO_FUTURE
|
||||
&& member_dir.workspace.package_jsons().next().is_some()
|
||||
&& member_dir.workspace.node_modules_dir().is_none());
|
||||
let node_modules_dir =
|
||||
member_dir.workspace.node_modules_dir().unwrap_or_default();
|
||||
let byonm = match node_modules_dir {
|
||||
Some(mode) => mode == NodeModulesDirMode::Manual,
|
||||
None => member_dir.workspace.root_pkg_json().is_some(),
|
||||
};
|
||||
if byonm {
|
||||
lsp_log!(" Enabled 'bring your own node_modules'.");
|
||||
}
|
||||
|
@ -1387,9 +1461,10 @@ impl ConfigData {
|
|||
// Mark the import map as a watched file
|
||||
if let Some(import_map_specifier) = member_dir
|
||||
.workspace
|
||||
.to_import_map_specifier()
|
||||
.to_import_map_path()
|
||||
.ok()
|
||||
.flatten()
|
||||
.and_then(|path| Url::from_file_path(path).ok())
|
||||
{
|
||||
add_watched_file(
|
||||
import_map_specifier.clone(),
|
||||
|
@ -1445,17 +1520,16 @@ impl ConfigData {
|
|||
ConfigWatchedFileType::ImportMap,
|
||||
);
|
||||
// spawn due to the lsp's `Send` requirement
|
||||
let fetch_result = deno_core::unsync::spawn({
|
||||
let file_fetcher = file_fetcher.cloned().unwrap();
|
||||
let import_map_url = import_map_url.clone();
|
||||
async move {
|
||||
file_fetcher
|
||||
.fetch(&import_map_url, &PermissionsContainer::allow_all())
|
||||
.await
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let fetch_result =
|
||||
deno_core::unsync::spawn({
|
||||
let file_fetcher = file_fetcher.cloned().unwrap();
|
||||
let import_map_url = import_map_url.clone();
|
||||
async move {
|
||||
file_fetcher.fetch_bypass_permissions(&import_map_url).await
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let value_result = fetch_result.and_then(|f| {
|
||||
serde_json::from_slice::<Value>(&f.source).map_err(|e| e.into())
|
||||
|
@ -1479,49 +1553,32 @@ impl ConfigData {
|
|||
None
|
||||
}
|
||||
};
|
||||
let resolver = deno_core::unsync::spawn({
|
||||
let workspace = member_dir.clone();
|
||||
let file_fetcher = file_fetcher.cloned();
|
||||
async move {
|
||||
workspace
|
||||
.create_resolver(
|
||||
CreateResolverOptions {
|
||||
pkg_json_dep_resolution,
|
||||
specified_import_map,
|
||||
},
|
||||
move |specifier| {
|
||||
let specifier = specifier.clone();
|
||||
let file_fetcher = file_fetcher.clone().unwrap();
|
||||
async move {
|
||||
let file = file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.await?
|
||||
.into_text_decoded()?;
|
||||
Ok(file.source.to_string())
|
||||
}
|
||||
},
|
||||
)
|
||||
.await
|
||||
.inspect_err(|err| {
|
||||
lsp_warn!(
|
||||
" Failed to load resolver: {}",
|
||||
err // will contain the specifier
|
||||
);
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap_or_else(|| {
|
||||
// create a dummy resolver
|
||||
WorkspaceResolver::new_raw(
|
||||
scope.clone(),
|
||||
None,
|
||||
member_dir.workspace.package_jsons().cloned().collect(),
|
||||
pkg_json_dep_resolution,
|
||||
let resolver = member_dir
|
||||
.workspace
|
||||
.create_resolver(
|
||||
CreateResolverOptions {
|
||||
pkg_json_dep_resolution,
|
||||
specified_import_map,
|
||||
},
|
||||
|path| Ok(std::fs::read_to_string(path)?),
|
||||
)
|
||||
});
|
||||
.inspect_err(|err| {
|
||||
lsp_warn!(
|
||||
" Failed to load resolver: {}",
|
||||
err // will contain the specifier
|
||||
);
|
||||
})
|
||||
.ok()
|
||||
.unwrap_or_else(|| {
|
||||
// create a dummy resolver
|
||||
WorkspaceResolver::new_raw(
|
||||
scope.clone(),
|
||||
None,
|
||||
member_dir.workspace.resolver_jsr_pkgs().collect(),
|
||||
member_dir.workspace.package_jsons().cloned().collect(),
|
||||
pkg_json_dep_resolution,
|
||||
)
|
||||
});
|
||||
if !resolver.diagnostics().is_empty() {
|
||||
lsp_warn!(
|
||||
" Import map diagnostics:\n{}",
|
||||
|
@ -1533,13 +1590,22 @@ impl ConfigData {
|
|||
.join("\n")
|
||||
);
|
||||
}
|
||||
let unstable = member_dir
|
||||
.workspace
|
||||
.unstable_features()
|
||||
.iter()
|
||||
.chain(settings.unstable.as_deref())
|
||||
.cloned()
|
||||
.collect::<BTreeSet<_>>();
|
||||
let unstable_sloppy_imports = std::env::var("DENO_UNSTABLE_SLOPPY_IMPORTS")
|
||||
.is_ok()
|
||||
|| member_dir.workspace.has_unstable("sloppy-imports");
|
||||
|| unstable.contains("sloppy-imports");
|
||||
let sloppy_imports_resolver = unstable_sloppy_imports.then(|| {
|
||||
Arc::new(SloppyImportsResolver::new_without_stat_cache(Arc::new(
|
||||
deno_runtime::deno_fs::RealFs,
|
||||
)))
|
||||
Arc::new(CliSloppyImportsResolver::new(
|
||||
SloppyImportsCachedFs::new_without_stat_cache(Arc::new(
|
||||
deno_runtime::deno_fs::RealFs,
|
||||
)),
|
||||
))
|
||||
});
|
||||
let resolver = Arc::new(resolver);
|
||||
let lint_rule_provider = LintRuleProvider::new(
|
||||
|
@ -1558,6 +1624,7 @@ impl ConfigData {
|
|||
|
||||
ConfigData {
|
||||
scope,
|
||||
canonicalized_scope,
|
||||
member_dir,
|
||||
resolver,
|
||||
sloppy_imports_resolver,
|
||||
|
@ -1573,6 +1640,7 @@ impl ConfigData {
|
|||
lockfile,
|
||||
npmrc,
|
||||
import_map_from_settings,
|
||||
unstable,
|
||||
watched_files,
|
||||
}
|
||||
}
|
||||
|
@ -1586,6 +1654,26 @@ impl ConfigData {
|
|||
pub fn maybe_pkg_json(&self) -> Option<&Arc<deno_package_json::PackageJson>> {
|
||||
self.member_dir.maybe_pkg_json()
|
||||
}
|
||||
|
||||
pub fn maybe_jsx_import_source_config(
|
||||
&self,
|
||||
) -> Option<JsxImportSourceConfig> {
|
||||
self
|
||||
.member_dir
|
||||
.workspace
|
||||
.to_maybe_jsx_import_source_config()
|
||||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
pub fn scope_contains_specifier(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
specifier.as_str().starts_with(self.scope.as_str())
|
||||
|| self
|
||||
.canonicalized_scope
|
||||
.as_ref()
|
||||
.map(|s| specifier.as_str().starts_with(s.as_str()))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
|
@ -1600,8 +1688,9 @@ impl ConfigTree {
|
|||
) -> Option<&ModuleSpecifier> {
|
||||
self
|
||||
.scopes
|
||||
.keys()
|
||||
.rfind(|s| specifier.as_str().starts_with(s.as_str()))
|
||||
.iter()
|
||||
.rfind(|(_, d)| d.scope_contains_specifier(specifier))
|
||||
.map(|(s, _)| s)
|
||||
}
|
||||
|
||||
pub fn data_for_specifier(
|
||||
|
@ -1654,23 +1743,28 @@ impl ConfigTree {
|
|||
.unwrap_or_else(|| Arc::new(FmtConfig::new_with_base(PathBuf::from("/"))))
|
||||
}
|
||||
|
||||
/// Returns (scope_uri, type).
|
||||
/// Returns (scope_url, type).
|
||||
pub fn watched_file_type(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<(&ModuleSpecifier, ConfigWatchedFileType)> {
|
||||
for (scope_uri, data) in self.scopes.iter() {
|
||||
for (scope_url, data) in self.scopes.iter() {
|
||||
if let Some(typ) = data.watched_files.get(specifier) {
|
||||
return Some((scope_uri, *typ));
|
||||
return Some((scope_url, *typ));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn is_watched_file(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
if specifier.path().ends_with("/deno.json")
|
||||
|| specifier.path().ends_with("/deno.jsonc")
|
||||
|| specifier.path().ends_with("/package.json")
|
||||
let path = specifier.path();
|
||||
if path.ends_with("/deno.json")
|
||||
|| path.ends_with("/deno.jsonc")
|
||||
|| path.ends_with("/package.json")
|
||||
|| path.ends_with("/node_modules/.package-lock.json")
|
||||
|| path.ends_with("/node_modules/.yarn-integrity.json")
|
||||
|| path.ends_with("/node_modules/.modules.yaml")
|
||||
|| path.ends_with("/node_modules/.deno/.setup-cache.bin")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -1680,6 +1774,46 @@ impl ConfigTree {
|
|||
.any(|data| data.watched_files.contains_key(specifier))
|
||||
}
|
||||
|
||||
pub fn to_did_refresh_params(
|
||||
&self,
|
||||
) -> lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams {
|
||||
let data = self
|
||||
.scopes
|
||||
.values()
|
||||
.filter_map(|data| {
|
||||
let workspace_root_scope_uri =
|
||||
Some(data.member_dir.workspace.root_dir())
|
||||
.filter(|s| *s != data.member_dir.dir_url())
|
||||
.and_then(|s| url_to_uri(s).ok());
|
||||
Some(lsp_custom::DenoConfigurationData {
|
||||
scope_uri: url_to_uri(&data.scope).ok()?,
|
||||
deno_json: data.maybe_deno_json().and_then(|c| {
|
||||
if workspace_root_scope_uri.is_some()
|
||||
&& Some(&c.specifier)
|
||||
== data
|
||||
.member_dir
|
||||
.workspace
|
||||
.root_deno_json()
|
||||
.map(|c| &c.specifier)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Some(lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(&c.specifier).ok()?,
|
||||
})
|
||||
}),
|
||||
package_json: data.maybe_pkg_json().and_then(|p| {
|
||||
Some(lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(&p.specifier()).ok()?,
|
||||
})
|
||||
}),
|
||||
workspace_root_scope_uri,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams { data }
|
||||
}
|
||||
|
||||
pub async fn refresh(
|
||||
&mut self,
|
||||
settings: &Settings,
|
||||
|
@ -1701,27 +1835,28 @@ impl ConfigTree {
|
|||
ws_settings = ws_settings.or(Some(&settings.unscoped));
|
||||
}
|
||||
if let Some(ws_settings) = ws_settings {
|
||||
if let Some(config_path) = &ws_settings.config {
|
||||
if let Ok(config_uri) = folder_uri.join(config_path) {
|
||||
if let Ok(config_file_path) = config_uri.to_file_path() {
|
||||
scopes.insert(
|
||||
folder_uri.clone(),
|
||||
Arc::new(
|
||||
ConfigData::load(
|
||||
Some(&config_file_path),
|
||||
folder_uri,
|
||||
settings,
|
||||
file_fetcher,
|
||||
&cached_fs,
|
||||
&deno_json_cache,
|
||||
&pkg_json_cache,
|
||||
&workspace_cache,
|
||||
)
|
||||
.await,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
let config_file_path = (|| {
|
||||
let config_setting = ws_settings.config.as_ref()?;
|
||||
let config_uri = folder_uri.join(config_setting).ok()?;
|
||||
url_to_file_path(&config_uri).ok()
|
||||
})();
|
||||
if config_file_path.is_some() || ws_settings.import_map.is_some() {
|
||||
scopes.insert(
|
||||
folder_uri.clone(),
|
||||
Arc::new(
|
||||
ConfigData::load(
|
||||
config_file_path.as_deref(),
|
||||
folder_uri,
|
||||
settings,
|
||||
file_fetcher,
|
||||
&cached_fs,
|
||||
&deno_json_cache,
|
||||
&pkg_json_cache,
|
||||
&workspace_cache,
|
||||
)
|
||||
.await,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1772,29 +1907,6 @@ impl ConfigTree {
|
|||
}
|
||||
}
|
||||
|
||||
for folder_uri in settings.by_workspace_folder.keys() {
|
||||
if !scopes
|
||||
.keys()
|
||||
.any(|s| folder_uri.as_str().starts_with(s.as_str()))
|
||||
{
|
||||
scopes.insert(
|
||||
folder_uri.clone(),
|
||||
Arc::new(
|
||||
ConfigData::load(
|
||||
None,
|
||||
folder_uri,
|
||||
settings,
|
||||
file_fetcher,
|
||||
&cached_fs,
|
||||
&deno_json_cache,
|
||||
&pkg_json_cache,
|
||||
&workspace_cache,
|
||||
)
|
||||
.await,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
self.scopes = Arc::new(scopes);
|
||||
}
|
||||
|
||||
|
@ -1803,7 +1915,7 @@ impl ConfigTree {
|
|||
let scope = config_file.specifier.join(".").unwrap();
|
||||
let json_text = serde_json::to_string(&config_file.json).unwrap();
|
||||
let test_fs = deno_runtime::deno_fs::InMemoryFs::default();
|
||||
let config_path = specifier_to_file_path(&config_file.specifier).unwrap();
|
||||
let config_path = url_to_file_path(&config_file.specifier).unwrap();
|
||||
test_fs.setup_text_files(vec![(
|
||||
config_path.to_string_lossy().to_string(),
|
||||
json_text,
|
||||
|
@ -1845,7 +1957,12 @@ fn resolve_lockfile_from_workspace(
|
|||
return None;
|
||||
}
|
||||
};
|
||||
resolve_lockfile_from_path(lockfile_path)
|
||||
let frozen = workspace
|
||||
.workspace
|
||||
.root_deno_json()
|
||||
.and_then(|c| c.to_lock_config().ok().flatten().map(|c| c.frozen()))
|
||||
.unwrap_or(false);
|
||||
resolve_lockfile_from_path(lockfile_path, frozen)
|
||||
}
|
||||
|
||||
fn resolve_node_modules_dir(
|
||||
|
@ -1856,13 +1973,17 @@ fn resolve_node_modules_dir(
|
|||
// `nodeModulesDir: true` setting in the deno.json file. This is to
|
||||
// reduce the chance of modifying someone's node_modules directory
|
||||
// without them having asked us to do so.
|
||||
let explicitly_disabled = workspace.node_modules_dir() == Some(false);
|
||||
let node_modules_mode = workspace.node_modules_dir().ok().flatten();
|
||||
let explicitly_disabled = node_modules_mode == Some(NodeModulesDirMode::None);
|
||||
if explicitly_disabled {
|
||||
return None;
|
||||
}
|
||||
let enabled = byonm
|
||||
|| workspace.node_modules_dir() == Some(true)
|
||||
|| node_modules_mode
|
||||
.map(|m| m.uses_node_modules_dir())
|
||||
.unwrap_or(false)
|
||||
|| workspace.vendor_dir_path().is_some();
|
||||
|
||||
if !enabled {
|
||||
return None;
|
||||
}
|
||||
|
@ -1874,8 +1995,15 @@ fn resolve_node_modules_dir(
|
|||
canonicalize_path_maybe_not_exists(&node_modules_dir).ok()
|
||||
}
|
||||
|
||||
fn resolve_lockfile_from_path(lockfile_path: PathBuf) -> Option<CliLockfile> {
|
||||
match CliLockfile::read_from_path(lockfile_path, false) {
|
||||
fn resolve_lockfile_from_path(
|
||||
lockfile_path: PathBuf,
|
||||
frozen: bool,
|
||||
) -> Option<CliLockfile> {
|
||||
match CliLockfile::read_from_path(CliLockfileReadFromPathOptions {
|
||||
file_path: lockfile_path,
|
||||
frozen,
|
||||
skip_write: false,
|
||||
}) {
|
||||
Ok(value) => {
|
||||
if value.filename.exists() {
|
||||
if let Ok(specifier) = ModuleSpecifier::from_file_path(&value.filename)
|
||||
|
@ -2120,7 +2248,7 @@ mod tests {
|
|||
},
|
||||
tls_certificate: None,
|
||||
unsafely_ignore_certificate_errors: None,
|
||||
unstable: false,
|
||||
unstable: Default::default(),
|
||||
javascript: LanguageWorkspaceSettings {
|
||||
inlay_hints: InlayHintsSettings {
|
||||
parameter_names: InlayHintsParamNamesOptions {
|
||||
|
@ -2148,6 +2276,7 @@ mod tests {
|
|||
auto_import_file_exclude_patterns: vec![],
|
||||
use_aliases_for_renames: true,
|
||||
quote_style: QuoteStyle::Auto,
|
||||
prefer_type_only_auto_imports: false,
|
||||
},
|
||||
suggest: CompletionSettings {
|
||||
complete_function_calls: false,
|
||||
|
@ -2193,6 +2322,7 @@ mod tests {
|
|||
auto_import_file_exclude_patterns: vec![],
|
||||
use_aliases_for_renames: true,
|
||||
quote_style: QuoteStyle::Auto,
|
||||
prefer_type_only_auto_imports: false,
|
||||
},
|
||||
suggest: CompletionSettings {
|
||||
complete_function_calls: false,
|
||||
|
|
|
@ -12,14 +12,15 @@ use super::language_server::StateSnapshot;
|
|||
use super::performance::Performance;
|
||||
use super::tsc;
|
||||
use super::tsc::TsServer;
|
||||
use super::urls::LspClientUrl;
|
||||
use super::urls::uri_parse_unencoded;
|
||||
use super::urls::url_to_uri;
|
||||
use super::urls::LspUrlMap;
|
||||
|
||||
use crate::graph_util;
|
||||
use crate::graph_util::enhanced_resolution_error_message;
|
||||
use crate::lsp::lsp_custom::DiagnosticBatchNotificationParams;
|
||||
use crate::resolver::SloppyImportsResolution;
|
||||
use crate::resolver::SloppyImportsResolver;
|
||||
use crate::resolver::CliSloppyImportsResolver;
|
||||
use crate::resolver::SloppyImportsCachedFs;
|
||||
use crate::tools::lint::CliLinter;
|
||||
use crate::tools::lint::CliLinterOptions;
|
||||
use crate::tools::lint::LintRuleProvider;
|
||||
|
@ -37,12 +38,14 @@ use deno_core::serde_json::json;
|
|||
use deno_core::unsync::spawn;
|
||||
use deno_core::unsync::spawn_blocking;
|
||||
use deno_core::unsync::JoinHandle;
|
||||
use deno_core::url::Url;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::source::ResolveError;
|
||||
use deno_graph::Resolution;
|
||||
use deno_graph::ResolutionError;
|
||||
use deno_graph::SpecifierError;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolution;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolutionMode;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node;
|
||||
use deno_runtime::tokio_util::create_basic_runtime;
|
||||
|
@ -160,15 +163,14 @@ impl DiagnosticsPublisher {
|
|||
.state
|
||||
.update(&record.specifier, version, &all_specifier_diagnostics);
|
||||
let file_referrer = documents.get_file_referrer(&record.specifier);
|
||||
let Ok(uri) =
|
||||
url_map.specifier_to_uri(&record.specifier, file_referrer.as_deref())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
self
|
||||
.client
|
||||
.publish_diagnostics(
|
||||
url_map
|
||||
.normalize_specifier(&record.specifier, file_referrer.as_deref())
|
||||
.unwrap_or(LspClientUrl::new(record.specifier)),
|
||||
all_specifier_diagnostics,
|
||||
version,
|
||||
)
|
||||
.publish_diagnostics(uri, all_specifier_diagnostics, version)
|
||||
.await;
|
||||
messages_sent += 1;
|
||||
}
|
||||
|
@ -191,15 +193,14 @@ impl DiagnosticsPublisher {
|
|||
// clear out any diagnostics for this specifier
|
||||
self.state.update(specifier, removed_value.version, &[]);
|
||||
let file_referrer = documents.get_file_referrer(specifier);
|
||||
let Ok(uri) =
|
||||
url_map.specifier_to_uri(specifier, file_referrer.as_deref())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
self
|
||||
.client
|
||||
.publish_diagnostics(
|
||||
url_map
|
||||
.normalize_specifier(specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| LspClientUrl::new(specifier.clone())),
|
||||
Vec::new(),
|
||||
removed_value.version,
|
||||
)
|
||||
.publish_diagnostics(uri, Vec::new(), removed_value.version)
|
||||
.await;
|
||||
messages_sent += 1;
|
||||
}
|
||||
|
@ -337,9 +338,9 @@ impl DiagnosticsState {
|
|||
if diagnostic.code
|
||||
== Some(lsp::NumberOrString::String("no-cache".to_string()))
|
||||
|| diagnostic.code
|
||||
== Some(lsp::NumberOrString::String("no-cache-jsr".to_string()))
|
||||
== Some(lsp::NumberOrString::String("not-installed-jsr".to_string()))
|
||||
|| diagnostic.code
|
||||
== Some(lsp::NumberOrString::String("no-cache-npm".to_string()))
|
||||
== Some(lsp::NumberOrString::String("not-installed-npm".to_string()))
|
||||
{
|
||||
no_cache_diagnostics.push(diagnostic.clone());
|
||||
}
|
||||
|
@ -737,7 +738,7 @@ fn to_lsp_related_information(
|
|||
if let (Some(file_name), Some(start), Some(end)) =
|
||||
(&ri.file_name, &ri.start, &ri.end)
|
||||
{
|
||||
let uri = lsp::Url::parse(file_name).unwrap();
|
||||
let uri = uri_parse_unencoded(file_name).unwrap();
|
||||
Some(lsp::DiagnosticRelatedInformation {
|
||||
location: lsp::Location {
|
||||
uri,
|
||||
|
@ -991,9 +992,9 @@ pub enum DenoDiagnostic {
|
|||
/// A remote module was not found in the cache.
|
||||
NoCache(ModuleSpecifier),
|
||||
/// A remote jsr package reference was not found in the cache.
|
||||
NoCacheJsr(PackageReq, ModuleSpecifier),
|
||||
NotInstalledJsr(PackageReq, ModuleSpecifier),
|
||||
/// A remote npm package reference was not found in the cache.
|
||||
NoCacheNpm(PackageReq, ModuleSpecifier),
|
||||
NotInstalledNpm(PackageReq, ModuleSpecifier),
|
||||
/// A local module was not found on the local file system.
|
||||
NoLocal(ModuleSpecifier),
|
||||
/// The specifier resolved to a remote specifier that was redirected to
|
||||
|
@ -1018,8 +1019,8 @@ impl DenoDiagnostic {
|
|||
Self::InvalidAttributeType(_) => "invalid-attribute-type",
|
||||
Self::NoAttributeType => "no-attribute-type",
|
||||
Self::NoCache(_) => "no-cache",
|
||||
Self::NoCacheJsr(_, _) => "no-cache-jsr",
|
||||
Self::NoCacheNpm(_, _) => "no-cache-npm",
|
||||
Self::NotInstalledJsr(_, _) => "not-installed-jsr",
|
||||
Self::NotInstalledNpm(_, _) => "not-installed-npm",
|
||||
Self::NoLocal(_) => "no-local",
|
||||
Self::Redirect { .. } => "redirect",
|
||||
Self::ResolutionError(err) => {
|
||||
|
@ -1070,7 +1071,7 @@ impl DenoDiagnostic {
|
|||
diagnostics: Some(vec![diagnostic.clone()]),
|
||||
edit: Some(lsp::WorkspaceEdit {
|
||||
changes: Some(HashMap::from([(
|
||||
specifier.clone(),
|
||||
url_to_uri(specifier)?,
|
||||
vec![lsp::TextEdit {
|
||||
new_text: format!("\"{to}\""),
|
||||
range: diagnostic.range,
|
||||
|
@ -1087,7 +1088,7 @@ impl DenoDiagnostic {
|
|||
diagnostics: Some(vec![diagnostic.clone()]),
|
||||
edit: Some(lsp::WorkspaceEdit {
|
||||
changes: Some(HashMap::from([(
|
||||
specifier.clone(),
|
||||
url_to_uri(specifier)?,
|
||||
vec![lsp::TextEdit {
|
||||
new_text: " with { type: \"json\" }".to_string(),
|
||||
range: lsp::Range {
|
||||
|
@ -1100,17 +1101,22 @@ impl DenoDiagnostic {
|
|||
}),
|
||||
..Default::default()
|
||||
},
|
||||
"no-cache" | "no-cache-jsr" | "no-cache-npm" => {
|
||||
"no-cache" | "not-installed-jsr" | "not-installed-npm" => {
|
||||
let data = diagnostic
|
||||
.data
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("Diagnostic is missing data"))?;
|
||||
let data: DiagnosticDataSpecifier = serde_json::from_value(data)?;
|
||||
let title = if matches!(
|
||||
code.as_str(),
|
||||
"not-installed-jsr" | "not-installed-npm"
|
||||
) {
|
||||
format!("Install \"{}\" and its dependencies.", data.specifier)
|
||||
} else {
|
||||
format!("Cache \"{}\" and its dependencies.", data.specifier)
|
||||
};
|
||||
lsp::CodeAction {
|
||||
title: format!(
|
||||
"Cache \"{}\" and its dependencies.",
|
||||
data.specifier
|
||||
),
|
||||
title,
|
||||
kind: Some(lsp::CodeActionKind::QUICKFIX),
|
||||
diagnostics: Some(vec![diagnostic.clone()]),
|
||||
command: Some(lsp::Command {
|
||||
|
@ -1133,7 +1139,7 @@ impl DenoDiagnostic {
|
|||
diagnostics: Some(vec![diagnostic.clone()]),
|
||||
edit: Some(lsp::WorkspaceEdit {
|
||||
changes: Some(HashMap::from([(
|
||||
specifier.clone(),
|
||||
url_to_uri(specifier)?,
|
||||
vec![lsp::TextEdit {
|
||||
new_text: format!(
|
||||
"\"{}\"",
|
||||
|
@ -1159,7 +1165,7 @@ impl DenoDiagnostic {
|
|||
diagnostics: Some(vec![diagnostic.clone()]),
|
||||
edit: Some(lsp::WorkspaceEdit {
|
||||
changes: Some(HashMap::from([(
|
||||
specifier.clone(),
|
||||
url_to_uri(specifier)?,
|
||||
vec![lsp::TextEdit {
|
||||
new_text: format!(
|
||||
"\"{}\"",
|
||||
|
@ -1185,7 +1191,7 @@ impl DenoDiagnostic {
|
|||
diagnostics: Some(vec![diagnostic.clone()]),
|
||||
edit: Some(lsp::WorkspaceEdit {
|
||||
changes: Some(HashMap::from([(
|
||||
specifier.clone(),
|
||||
url_to_uri(specifier)?,
|
||||
vec![lsp::TextEdit {
|
||||
new_text: format!("\"node:{}\"", data.specifier),
|
||||
range: diagnostic.range,
|
||||
|
@ -1216,8 +1222,8 @@ impl DenoDiagnostic {
|
|||
match code.as_str() {
|
||||
"import-map-remap"
|
||||
| "no-cache"
|
||||
| "no-cache-jsr"
|
||||
| "no-cache-npm"
|
||||
| "not-installed-jsr"
|
||||
| "not-installed-npm"
|
||||
| "no-attribute-type"
|
||||
| "redirect"
|
||||
| "import-node-prefix-missing" => true,
|
||||
|
@ -1255,10 +1261,12 @@ impl DenoDiagnostic {
|
|||
Self::InvalidAttributeType(assert_type) => (lsp::DiagnosticSeverity::ERROR, format!("The module is a JSON module and expected an attribute type of \"json\". Instead got \"{assert_type}\"."), None),
|
||||
Self::NoAttributeType => (lsp::DiagnosticSeverity::ERROR, "The module is a JSON module and not being imported with an import attribute. Consider adding `with { type: \"json\" }` to the import statement.".to_string(), None),
|
||||
Self::NoCache(specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Uncached or missing remote URL: {specifier}"), Some(json!({ "specifier": specifier }))),
|
||||
Self::NoCacheJsr(pkg_req, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Uncached or missing jsr package: {}", pkg_req), Some(json!({ "specifier": specifier }))),
|
||||
Self::NoCacheNpm(pkg_req, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("Uncached or missing npm package: {}", pkg_req), Some(json!({ "specifier": specifier }))),
|
||||
Self::NotInstalledJsr(pkg_req, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("JSR package \"{pkg_req}\" is not installed or doesn't exist."), Some(json!({ "specifier": specifier }))),
|
||||
Self::NotInstalledNpm(pkg_req, specifier) => (lsp::DiagnosticSeverity::ERROR, format!("NPM package \"{pkg_req}\" is not installed or doesn't exist."), Some(json!({ "specifier": specifier }))),
|
||||
Self::NoLocal(specifier) => {
|
||||
let maybe_sloppy_resolution = SloppyImportsResolver::new(Arc::new(deno_fs::RealFs)).resolve(specifier, ResolutionMode::Execution);
|
||||
let maybe_sloppy_resolution = CliSloppyImportsResolver::new(
|
||||
SloppyImportsCachedFs::new(Arc::new(deno_fs::RealFs))
|
||||
).resolve(specifier, SloppyImportsResolutionMode::Execution);
|
||||
let data = maybe_sloppy_resolution.as_ref().map(|res| {
|
||||
json!({
|
||||
"specifier": specifier,
|
||||
|
@ -1303,10 +1311,7 @@ impl DenoDiagnostic {
|
|||
}
|
||||
}
|
||||
|
||||
fn specifier_text_for_redirected(
|
||||
redirect: &lsp::Url,
|
||||
referrer: &lsp::Url,
|
||||
) -> String {
|
||||
fn specifier_text_for_redirected(redirect: &Url, referrer: &Url) -> String {
|
||||
if redirect.scheme() == "file" && referrer.scheme() == "file" {
|
||||
// use a relative specifier when it's going to a file url
|
||||
relative_specifier(redirect, referrer)
|
||||
|
@ -1315,7 +1320,7 @@ fn specifier_text_for_redirected(
|
|||
}
|
||||
}
|
||||
|
||||
fn relative_specifier(specifier: &lsp::Url, referrer: &lsp::Url) -> String {
|
||||
fn relative_specifier(specifier: &Url, referrer: &Url) -> String {
|
||||
match referrer.make_relative(specifier) {
|
||||
Some(relative) => {
|
||||
if relative.starts_with('.') {
|
||||
|
@ -1367,21 +1372,20 @@ fn diagnose_resolution(
|
|||
let mut diagnostics = vec![];
|
||||
match resolution {
|
||||
Resolution::Ok(resolved) => {
|
||||
let file_referrer = referrer_doc.file_referrer();
|
||||
let specifier = &resolved.specifier;
|
||||
let managed_npm_resolver = snapshot
|
||||
.resolver
|
||||
.maybe_managed_npm_resolver(referrer_doc.file_referrer());
|
||||
let managed_npm_resolver =
|
||||
snapshot.resolver.maybe_managed_npm_resolver(file_referrer);
|
||||
for (_, headers) in snapshot
|
||||
.resolver
|
||||
.redirect_chain_headers(specifier, referrer_doc.file_referrer())
|
||||
.redirect_chain_headers(specifier, file_referrer)
|
||||
{
|
||||
if let Some(message) = headers.get("x-deno-warning") {
|
||||
diagnostics.push(DenoDiagnostic::DenoWarn(message.clone()));
|
||||
}
|
||||
}
|
||||
if let Some(doc) = snapshot
|
||||
.documents
|
||||
.get_or_load(specifier, referrer_doc.specifier())
|
||||
if let Some(doc) =
|
||||
snapshot.documents.get_or_load(specifier, file_referrer)
|
||||
{
|
||||
if let Some(headers) = doc.maybe_headers() {
|
||||
if let Some(message) = headers.get("x-deno-warning") {
|
||||
|
@ -1411,7 +1415,8 @@ fn diagnose_resolution(
|
|||
JsrPackageReqReference::from_specifier(specifier)
|
||||
{
|
||||
let req = pkg_ref.into_inner().req;
|
||||
diagnostics.push(DenoDiagnostic::NoCacheJsr(req, specifier.clone()));
|
||||
diagnostics
|
||||
.push(DenoDiagnostic::NotInstalledJsr(req, specifier.clone()));
|
||||
} else if let Ok(pkg_ref) =
|
||||
NpmPackageReqReference::from_specifier(specifier)
|
||||
{
|
||||
|
@ -1420,7 +1425,7 @@ fn diagnose_resolution(
|
|||
let req = pkg_ref.into_inner().req;
|
||||
if !npm_resolver.is_pkg_req_folder_cached(&req) {
|
||||
diagnostics
|
||||
.push(DenoDiagnostic::NoCacheNpm(req, specifier.clone()));
|
||||
.push(DenoDiagnostic::NotInstalledNpm(req, specifier.clone()));
|
||||
}
|
||||
}
|
||||
} else if let Some(module_name) = specifier.as_str().strip_prefix("node:")
|
||||
|
@ -1446,7 +1451,7 @@ fn diagnose_resolution(
|
|||
// check that a @types/node package exists in the resolver
|
||||
let types_node_req = PackageReq::from_str("@types/node").unwrap();
|
||||
if !npm_resolver.is_pkg_req_folder_cached(&types_node_req) {
|
||||
diagnostics.push(DenoDiagnostic::NoCacheNpm(
|
||||
diagnostics.push(DenoDiagnostic::NotInstalledNpm(
|
||||
types_node_req,
|
||||
ModuleSpecifier::parse("npm:@types/node").unwrap(),
|
||||
));
|
||||
|
@ -1494,7 +1499,11 @@ fn diagnose_dependency(
|
|||
.data_for_specifier(referrer_doc.file_referrer().unwrap_or(referrer))
|
||||
.and_then(|d| d.resolver.maybe_import_map());
|
||||
if let Some(import_map) = import_map {
|
||||
if let Resolution::Ok(resolved) = &dependency.maybe_code {
|
||||
let resolved = dependency
|
||||
.maybe_code
|
||||
.ok()
|
||||
.or_else(|| dependency.maybe_type.ok());
|
||||
if let Some(resolved) = resolved {
|
||||
if let Some(to) = import_map.lookup(&resolved.specifier, referrer) {
|
||||
if dependency_key != to {
|
||||
diagnostics.push(
|
||||
|
@ -1512,17 +1521,19 @@ fn diagnose_dependency(
|
|||
let import_ranges: Vec<_> = dependency
|
||||
.imports
|
||||
.iter()
|
||||
.map(|i| documents::to_lsp_range(&i.range))
|
||||
.map(|i| documents::to_lsp_range(&i.specifier_range))
|
||||
.collect();
|
||||
// TODO(nayeemrmn): This is a crude way of detecting `@deno-types` which has
|
||||
// a different specifier and therefore needs a separate call to
|
||||
// `diagnose_resolution()`. It would be much cleaner if that were modelled as
|
||||
// a separate dependency: https://github.com/denoland/deno_graph/issues/247.
|
||||
let is_types_deno_types = !dependency.maybe_type.is_none()
|
||||
&& !dependency
|
||||
.imports
|
||||
.iter()
|
||||
.any(|i| dependency.maybe_type.includes(&i.range.start).is_some());
|
||||
&& !dependency.imports.iter().any(|i| {
|
||||
dependency
|
||||
.maybe_type
|
||||
.includes(&i.specifier_range.start)
|
||||
.is_some()
|
||||
});
|
||||
|
||||
diagnostics.extend(
|
||||
diagnose_resolution(
|
||||
|
@ -1532,7 +1543,7 @@ fn diagnose_dependency(
|
|||
// If not @deno-types, diagnose the types if the code errored because
|
||||
// it's likely resolving into the node_modules folder, which might be
|
||||
// erroring correctly due to resolution only being for bundlers. Let this
|
||||
// fail at runtime if necesarry, but don't bother erroring in the editor
|
||||
// fail at runtime if necessary, but don't bother erroring in the editor
|
||||
|| !is_types_deno_types && matches!(dependency.maybe_type, Resolution::Ok(_))
|
||||
&& matches!(dependency.maybe_code, Resolution::Err(_))
|
||||
{
|
||||
|
@ -1635,7 +1646,8 @@ mod tests {
|
|||
use test_util::TempDir;
|
||||
|
||||
fn mock_config() -> Config {
|
||||
let root_uri = resolve_url("file:///").unwrap();
|
||||
let root_url = resolve_url("file:///").unwrap();
|
||||
let root_uri = url_to_uri(&root_url).unwrap();
|
||||
Config {
|
||||
settings: Arc::new(Settings {
|
||||
unscoped: Arc::new(WorkspaceSettings {
|
||||
|
@ -1646,7 +1658,7 @@ mod tests {
|
|||
..Default::default()
|
||||
}),
|
||||
workspace_folders: Arc::new(vec![(
|
||||
root_uri.clone(),
|
||||
root_url,
|
||||
lsp::WorkspaceFolder {
|
||||
uri: root_uri,
|
||||
name: "".to_string(),
|
||||
|
@ -1661,7 +1673,7 @@ mod tests {
|
|||
maybe_import_map: Option<(&str, &str)>,
|
||||
) -> (TempDir, StateSnapshot) {
|
||||
let temp_dir = TempDir::new();
|
||||
let root_uri = temp_dir.uri();
|
||||
let root_uri = temp_dir.url();
|
||||
let cache = LspCache::new(Some(root_uri.join(".deno_dir").unwrap()));
|
||||
let mut config = Config::new_with_roots([root_uri.clone()]);
|
||||
if let Some((relative_path, json_string)) = maybe_import_map {
|
||||
|
@ -1695,6 +1707,7 @@ mod tests {
|
|||
documents: Arc::new(documents),
|
||||
assets: Default::default(),
|
||||
config: Arc::new(config),
|
||||
is_cjs_resolver: Default::default(),
|
||||
resolver,
|
||||
},
|
||||
)
|
||||
|
@ -1828,7 +1841,7 @@ let c: number = "a";
|
|||
assert_eq!(actual.len(), 2);
|
||||
for record in actual {
|
||||
let relative_specifier =
|
||||
temp_dir.uri().make_relative(&record.specifier).unwrap();
|
||||
temp_dir.url().make_relative(&record.specifier).unwrap();
|
||||
match relative_specifier.as_str() {
|
||||
"std/assert/mod.ts" => {
|
||||
assert_eq!(json!(record.versioned.diagnostics), json!([]))
|
||||
|
@ -2047,7 +2060,7 @@ let c: number = "a";
|
|||
"source": "deno",
|
||||
"message": format!(
|
||||
"Unable to load a local module: {}🦕.ts\nPlease check the file path.",
|
||||
temp_dir.uri(),
|
||||
temp_dir.url(),
|
||||
),
|
||||
}
|
||||
])
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
use super::cache::calculate_fs_version;
|
||||
use super::cache::LspCache;
|
||||
use super::cache::LSP_DISALLOW_GLOBAL_TO_LOCAL_COPY;
|
||||
use super::config::Config;
|
||||
use super::resolver::LspIsCjsResolver;
|
||||
use super::resolver::LspResolver;
|
||||
use super::resolver::ScopeDepInfo;
|
||||
use super::resolver::SingleReferrerGraphResolver;
|
||||
use super::testing::TestCollector;
|
||||
use super::testing::TestModule;
|
||||
use super::text::LineIndex;
|
||||
|
@ -12,7 +14,6 @@ use super::tsc;
|
|||
use super::tsc::AssetDocument;
|
||||
|
||||
use crate::graph_util::CliJsrUrlProvider;
|
||||
use deno_runtime::fs_util::specifier_to_file_path;
|
||||
|
||||
use dashmap::DashMap;
|
||||
use deno_ast::swc::visit::VisitWith;
|
||||
|
@ -28,15 +29,16 @@ use deno_core::parking_lot::Mutex;
|
|||
use deno_core::ModuleSpecifier;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::Resolution;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_runtime::deno_node;
|
||||
use deno_semver::jsr::JsrPackageReqReference;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use deno_semver::package::PackageReq;
|
||||
use indexmap::IndexMap;
|
||||
use indexmap::IndexSet;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::fs;
|
||||
|
@ -61,6 +63,9 @@ pub enum LanguageId {
|
|||
Json,
|
||||
JsonC,
|
||||
Markdown,
|
||||
Html,
|
||||
Css,
|
||||
Yaml,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
|
@ -74,6 +79,9 @@ impl LanguageId {
|
|||
LanguageId::Json => Some("json"),
|
||||
LanguageId::JsonC => Some("jsonc"),
|
||||
LanguageId::Markdown => Some("md"),
|
||||
LanguageId::Html => Some("html"),
|
||||
LanguageId::Css => Some("css"),
|
||||
LanguageId::Yaml => Some("yaml"),
|
||||
LanguageId::Unknown => None,
|
||||
}
|
||||
}
|
||||
|
@ -86,6 +94,9 @@ impl LanguageId {
|
|||
LanguageId::Tsx => Some("text/tsx"),
|
||||
LanguageId::Json | LanguageId::JsonC => Some("application/json"),
|
||||
LanguageId::Markdown => Some("text/markdown"),
|
||||
LanguageId::Html => Some("text/html"),
|
||||
LanguageId::Css => Some("text/css"),
|
||||
LanguageId::Yaml => Some("application/yaml"),
|
||||
LanguageId::Unknown => None,
|
||||
}
|
||||
}
|
||||
|
@ -110,6 +121,9 @@ impl FromStr for LanguageId {
|
|||
"json" => Ok(Self::Json),
|
||||
"jsonc" => Ok(Self::JsonC),
|
||||
"markdown" => Ok(Self::Markdown),
|
||||
"html" => Ok(Self::Html),
|
||||
"css" => Ok(Self::Css),
|
||||
"yaml" => Ok(Self::Yaml),
|
||||
_ => Ok(Self::Unknown),
|
||||
}
|
||||
}
|
||||
|
@ -261,7 +275,7 @@ fn get_maybe_test_module_fut(
|
|||
parsed_source.specifier().clone(),
|
||||
parsed_source.text_info_lazy().clone(),
|
||||
);
|
||||
parsed_source.module().visit_with(&mut collector);
|
||||
parsed_source.program().visit_with(&mut collector);
|
||||
Arc::new(collector.take())
|
||||
})
|
||||
.map(Result::ok)
|
||||
|
@ -282,6 +296,8 @@ pub struct Document {
|
|||
/// Contains the last-known-good set of dependencies from parsing the module.
|
||||
config: Arc<Config>,
|
||||
dependencies: Arc<IndexMap<String, deno_graph::Dependency>>,
|
||||
/// If this is maybe a CJS script and maybe not an ES module.
|
||||
is_script: Option<bool>,
|
||||
// TODO(nayeemrmn): This is unused, use it for scope attribution for remote
|
||||
// modules.
|
||||
file_referrer: Option<ModuleSpecifier>,
|
||||
|
@ -312,6 +328,7 @@ impl Document {
|
|||
maybe_lsp_version: Option<i32>,
|
||||
maybe_language_id: Option<LanguageId>,
|
||||
maybe_headers: Option<HashMap<String, String>>,
|
||||
is_cjs_resolver: &LspIsCjsResolver,
|
||||
resolver: Arc<LspResolver>,
|
||||
config: Arc<Config>,
|
||||
cache: &Arc<LspCache>,
|
||||
|
@ -321,12 +338,8 @@ impl Document {
|
|||
.filter(|s| cache.is_valid_file_referrer(s))
|
||||
.cloned()
|
||||
.or(file_referrer);
|
||||
let media_type = resolve_media_type(
|
||||
&specifier,
|
||||
maybe_headers.as_ref(),
|
||||
maybe_language_id,
|
||||
&resolver,
|
||||
);
|
||||
let media_type =
|
||||
resolve_media_type(&specifier, maybe_headers.as_ref(), maybe_language_id);
|
||||
let (maybe_parsed_source, maybe_module) =
|
||||
if media_type_is_diagnosable(media_type) {
|
||||
parse_and_analyze_module(
|
||||
|
@ -335,6 +348,7 @@ impl Document {
|
|||
maybe_headers.as_ref(),
|
||||
media_type,
|
||||
file_referrer.as_ref(),
|
||||
is_cjs_resolver,
|
||||
&resolver,
|
||||
)
|
||||
} else {
|
||||
|
@ -360,6 +374,7 @@ impl Document {
|
|||
file_referrer.as_ref(),
|
||||
),
|
||||
file_referrer,
|
||||
is_script: maybe_module.as_ref().map(|m| m.is_script),
|
||||
maybe_types_dependency,
|
||||
line_index,
|
||||
maybe_language_id,
|
||||
|
@ -381,6 +396,7 @@ impl Document {
|
|||
|
||||
fn with_new_config(
|
||||
&self,
|
||||
is_cjs_resolver: &LspIsCjsResolver,
|
||||
resolver: Arc<LspResolver>,
|
||||
config: Arc<Config>,
|
||||
) -> Arc<Self> {
|
||||
|
@ -388,11 +404,11 @@ impl Document {
|
|||
&self.specifier,
|
||||
self.maybe_headers.as_ref(),
|
||||
self.maybe_language_id,
|
||||
&resolver,
|
||||
);
|
||||
let dependencies;
|
||||
let maybe_types_dependency;
|
||||
let maybe_parsed_source;
|
||||
let is_script;
|
||||
let maybe_test_module_fut;
|
||||
if media_type != self.media_type {
|
||||
let parsed_source_result =
|
||||
|
@ -402,6 +418,7 @@ impl Document {
|
|||
&parsed_source_result,
|
||||
self.maybe_headers.as_ref(),
|
||||
self.file_referrer.as_ref(),
|
||||
is_cjs_resolver,
|
||||
&resolver,
|
||||
)
|
||||
.ok();
|
||||
|
@ -409,6 +426,7 @@ impl Document {
|
|||
.as_ref()
|
||||
.map(|m| Arc::new(m.dependencies.clone()))
|
||||
.unwrap_or_default();
|
||||
is_script = maybe_module.as_ref().map(|m| m.is_script);
|
||||
maybe_types_dependency = maybe_module
|
||||
.as_ref()
|
||||
.and_then(|m| Some(Arc::new(m.maybe_types_dependency.clone()?)));
|
||||
|
@ -416,10 +434,19 @@ impl Document {
|
|||
maybe_test_module_fut =
|
||||
get_maybe_test_module_fut(maybe_parsed_source.as_ref(), &config);
|
||||
} else {
|
||||
let graph_resolver =
|
||||
resolver.as_graph_resolver(self.file_referrer.as_ref());
|
||||
let cli_resolver = resolver.as_cli_resolver(self.file_referrer.as_ref());
|
||||
let npm_resolver =
|
||||
resolver.create_graph_npm_resolver(self.file_referrer.as_ref());
|
||||
let config_data = resolver.as_config_data(self.file_referrer.as_ref());
|
||||
let jsx_import_source_config =
|
||||
config_data.and_then(|d| d.maybe_jsx_import_source_config());
|
||||
let resolver = SingleReferrerGraphResolver {
|
||||
valid_referrer: &self.specifier,
|
||||
referrer_kind: is_cjs_resolver
|
||||
.get_lsp_referrer_kind(&self.specifier, self.is_script),
|
||||
cli_resolver,
|
||||
jsx_import_source_config: jsx_import_source_config.as_ref(),
|
||||
};
|
||||
dependencies = Arc::new(
|
||||
self
|
||||
.dependencies
|
||||
|
@ -430,7 +457,7 @@ impl Document {
|
|||
d.with_new_resolver(
|
||||
s,
|
||||
&CliJsrUrlProvider,
|
||||
Some(graph_resolver),
|
||||
Some(&resolver),
|
||||
Some(&npm_resolver),
|
||||
),
|
||||
)
|
||||
|
@ -440,10 +467,11 @@ impl Document {
|
|||
maybe_types_dependency = self.maybe_types_dependency.as_ref().map(|d| {
|
||||
Arc::new(d.with_new_resolver(
|
||||
&CliJsrUrlProvider,
|
||||
Some(graph_resolver),
|
||||
Some(&resolver),
|
||||
Some(&npm_resolver),
|
||||
))
|
||||
});
|
||||
is_script = self.is_script;
|
||||
maybe_parsed_source = self.maybe_parsed_source().cloned();
|
||||
maybe_test_module_fut = self
|
||||
.maybe_test_module_fut
|
||||
|
@ -455,6 +483,7 @@ impl Document {
|
|||
// updated properties
|
||||
dependencies,
|
||||
file_referrer: self.file_referrer.clone(),
|
||||
is_script,
|
||||
maybe_types_dependency,
|
||||
maybe_navigation_tree: Mutex::new(None),
|
||||
// maintain - this should all be copies/clones
|
||||
|
@ -479,6 +508,7 @@ impl Document {
|
|||
|
||||
fn with_change(
|
||||
&self,
|
||||
is_cjs_resolver: &LspIsCjsResolver,
|
||||
version: i32,
|
||||
changes: Vec<lsp::TextDocumentContentChangeEvent>,
|
||||
) -> Result<Arc<Self>, AnyError> {
|
||||
|
@ -512,6 +542,7 @@ impl Document {
|
|||
self.maybe_headers.as_ref(),
|
||||
media_type,
|
||||
self.file_referrer.as_ref(),
|
||||
is_cjs_resolver,
|
||||
self.resolver.as_ref(),
|
||||
)
|
||||
} else {
|
||||
|
@ -535,6 +566,7 @@ impl Document {
|
|||
get_maybe_test_module_fut(maybe_parsed_source.as_ref(), &self.config);
|
||||
Ok(Arc::new(Self {
|
||||
config: self.config.clone(),
|
||||
is_script: maybe_module.as_ref().map(|m| m.is_script),
|
||||
specifier: self.specifier.clone(),
|
||||
file_referrer: self.file_referrer.clone(),
|
||||
maybe_fs_version: self.maybe_fs_version.clone(),
|
||||
|
@ -569,6 +601,7 @@ impl Document {
|
|||
),
|
||||
maybe_language_id: self.maybe_language_id,
|
||||
dependencies: self.dependencies.clone(),
|
||||
is_script: self.is_script,
|
||||
maybe_types_dependency: self.maybe_types_dependency.clone(),
|
||||
text: self.text.clone(),
|
||||
text_info_cell: once_cell::sync::OnceCell::new(),
|
||||
|
@ -596,6 +629,7 @@ impl Document {
|
|||
),
|
||||
maybe_language_id: self.maybe_language_id,
|
||||
dependencies: self.dependencies.clone(),
|
||||
is_script: self.is_script,
|
||||
maybe_types_dependency: self.maybe_types_dependency.clone(),
|
||||
text: self.text.clone(),
|
||||
text_info_cell: once_cell::sync::OnceCell::new(),
|
||||
|
@ -644,6 +678,13 @@ impl Document {
|
|||
})
|
||||
}
|
||||
|
||||
/// If this is maybe a CJS script and maybe not an ES module.
|
||||
///
|
||||
/// Use `LspIsCjsResolver` to determine for sure.
|
||||
pub fn is_script(&self) -> Option<bool> {
|
||||
self.is_script
|
||||
}
|
||||
|
||||
pub fn line_index(&self) -> Arc<LineIndex> {
|
||||
self.line_index.clone()
|
||||
}
|
||||
|
@ -753,14 +794,7 @@ fn resolve_media_type(
|
|||
specifier: &ModuleSpecifier,
|
||||
maybe_headers: Option<&HashMap<String, String>>,
|
||||
maybe_language_id: Option<LanguageId>,
|
||||
resolver: &LspResolver,
|
||||
) -> MediaType {
|
||||
if resolver.in_node_modules(specifier) {
|
||||
if let Some(media_type) = resolver.node_media_type(specifier) {
|
||||
return media_type;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(language_id) = maybe_language_id {
|
||||
return MediaType::from_specifier_and_content_type(
|
||||
specifier,
|
||||
|
@ -798,6 +832,7 @@ impl FileSystemDocuments {
|
|||
pub fn get(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
is_cjs_resolver: &LspIsCjsResolver,
|
||||
resolver: &Arc<LspResolver>,
|
||||
config: &Arc<Config>,
|
||||
cache: &Arc<LspCache>,
|
||||
|
@ -821,7 +856,14 @@ impl FileSystemDocuments {
|
|||
};
|
||||
if dirty {
|
||||
// attempt to update the file on the file system
|
||||
self.refresh_document(specifier, resolver, config, cache, file_referrer)
|
||||
self.refresh_document(
|
||||
specifier,
|
||||
is_cjs_resolver,
|
||||
resolver,
|
||||
config,
|
||||
cache,
|
||||
file_referrer,
|
||||
)
|
||||
} else {
|
||||
old_doc
|
||||
}
|
||||
|
@ -832,22 +874,29 @@ impl FileSystemDocuments {
|
|||
fn refresh_document(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
is_cjs_resolver: &LspIsCjsResolver,
|
||||
resolver: &Arc<LspResolver>,
|
||||
config: &Arc<Config>,
|
||||
cache: &Arc<LspCache>,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<Arc<Document>> {
|
||||
let doc = if specifier.scheme() == "file" {
|
||||
let path = specifier_to_file_path(specifier).ok()?;
|
||||
let path = url_to_file_path(specifier).ok()?;
|
||||
let bytes = fs::read(path).ok()?;
|
||||
let content =
|
||||
deno_graph::source::decode_owned_source(specifier, bytes, None).ok()?;
|
||||
let content = bytes_to_content(
|
||||
specifier,
|
||||
MediaType::from_specifier(specifier),
|
||||
bytes,
|
||||
None,
|
||||
)
|
||||
.ok()?;
|
||||
Document::new(
|
||||
specifier.clone(),
|
||||
content.into(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
is_cjs_resolver,
|
||||
resolver.clone(),
|
||||
config.clone(),
|
||||
cache,
|
||||
|
@ -864,6 +913,7 @@ impl FileSystemDocuments {
|
|||
None,
|
||||
None,
|
||||
None,
|
||||
is_cjs_resolver,
|
||||
resolver.clone(),
|
||||
config.clone(),
|
||||
cache,
|
||||
|
@ -872,28 +922,31 @@ impl FileSystemDocuments {
|
|||
} else {
|
||||
let http_cache = cache.for_specifier(file_referrer);
|
||||
let cache_key = http_cache.cache_item_key(specifier).ok()?;
|
||||
let bytes = http_cache
|
||||
.read_file_bytes(&cache_key, None, LSP_DISALLOW_GLOBAL_TO_LOCAL_COPY)
|
||||
.ok()??;
|
||||
let specifier_headers = http_cache.read_headers(&cache_key).ok()??;
|
||||
let cached_file = http_cache.get(&cache_key, None).ok()??;
|
||||
let (_, maybe_charset) =
|
||||
deno_graph::source::resolve_media_type_and_charset_from_headers(
|
||||
specifier,
|
||||
Some(&specifier_headers),
|
||||
Some(&cached_file.metadata.headers),
|
||||
);
|
||||
let content = deno_graph::source::decode_owned_source(
|
||||
let media_type = resolve_media_type(
|
||||
specifier,
|
||||
bytes,
|
||||
Some(&cached_file.metadata.headers),
|
||||
None,
|
||||
);
|
||||
let content = bytes_to_content(
|
||||
specifier,
|
||||
media_type,
|
||||
cached_file.content,
|
||||
maybe_charset,
|
||||
)
|
||||
.ok()?;
|
||||
let maybe_headers = Some(specifier_headers);
|
||||
Document::new(
|
||||
specifier.clone(),
|
||||
content.into(),
|
||||
None,
|
||||
None,
|
||||
maybe_headers,
|
||||
Some(cached_file.metadata.headers),
|
||||
is_cjs_resolver,
|
||||
resolver.clone(),
|
||||
config.clone(),
|
||||
cache,
|
||||
|
@ -934,6 +987,11 @@ pub struct Documents {
|
|||
/// The DENO_DIR that the documents looks for non-file based modules.
|
||||
cache: Arc<LspCache>,
|
||||
config: Arc<Config>,
|
||||
/// Resolver for detecting if a document is CJS or ESM.
|
||||
is_cjs_resolver: Arc<LspIsCjsResolver>,
|
||||
/// A resolver that takes into account currently loaded import map and JSX
|
||||
/// settings.
|
||||
resolver: Arc<LspResolver>,
|
||||
/// A flag that indicates that stated data is potentially invalid and needs to
|
||||
/// be recalculated before being considered valid.
|
||||
dirty: bool,
|
||||
|
@ -941,15 +999,7 @@ pub struct Documents {
|
|||
open_docs: HashMap<ModuleSpecifier, Arc<Document>>,
|
||||
/// Documents stored on the file system.
|
||||
file_system_docs: Arc<FileSystemDocuments>,
|
||||
/// A resolver that takes into account currently loaded import map and JSX
|
||||
/// settings.
|
||||
resolver: Arc<LspResolver>,
|
||||
/// The npm package requirements found in npm specifiers.
|
||||
npm_reqs_by_scope:
|
||||
Arc<BTreeMap<Option<ModuleSpecifier>, BTreeSet<PackageReq>>>,
|
||||
/// Config scopes that contain a node: specifier such that a @types/node
|
||||
/// package should be injected.
|
||||
scopes_with_node_specifier: Arc<HashSet<Option<ModuleSpecifier>>>,
|
||||
dep_info_by_scope: Arc<BTreeMap<Option<ModuleSpecifier>, Arc<ScopeDepInfo>>>,
|
||||
}
|
||||
|
||||
impl Documents {
|
||||
|
@ -974,6 +1024,7 @@ impl Documents {
|
|||
// the cache for remote modules here in order to get the
|
||||
// x-typescript-types?
|
||||
None,
|
||||
&self.is_cjs_resolver,
|
||||
self.resolver.clone(),
|
||||
self.config.clone(),
|
||||
&self.cache,
|
||||
|
@ -1008,7 +1059,7 @@ impl Documents {
|
|||
))
|
||||
})?;
|
||||
self.dirty = true;
|
||||
let doc = doc.with_change(version, changes)?;
|
||||
let doc = doc.with_change(&self.is_cjs_resolver, version, changes)?;
|
||||
self.open_docs.insert(doc.specifier().clone(), doc.clone());
|
||||
Ok(doc)
|
||||
}
|
||||
|
@ -1063,34 +1114,6 @@ impl Documents {
|
|||
self.cache.is_valid_file_referrer(specifier)
|
||||
}
|
||||
|
||||
/// Return `true` if the provided specifier can be resolved to a document,
|
||||
/// otherwise `false`.
|
||||
pub fn contains_import(
|
||||
&self,
|
||||
specifier: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> bool {
|
||||
let file_referrer = self.get_file_referrer(referrer);
|
||||
let maybe_specifier = self
|
||||
.resolver
|
||||
.as_graph_resolver(file_referrer.as_deref())
|
||||
.resolve(
|
||||
specifier,
|
||||
&deno_graph::Range {
|
||||
specifier: referrer.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
},
|
||||
ResolutionMode::Types,
|
||||
)
|
||||
.ok();
|
||||
if let Some(import_specifier) = maybe_specifier {
|
||||
self.exists(&import_specifier, file_referrer.as_deref())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_document_specifier(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
|
@ -1128,7 +1151,7 @@ impl Documents {
|
|||
return true;
|
||||
}
|
||||
if specifier.scheme() == "file" {
|
||||
return specifier_to_file_path(&specifier)
|
||||
return url_to_file_path(&specifier)
|
||||
.map(|p| p.is_file())
|
||||
.unwrap_or(false);
|
||||
}
|
||||
|
@ -1139,17 +1162,20 @@ impl Documents {
|
|||
false
|
||||
}
|
||||
|
||||
pub fn npm_reqs_by_scope(
|
||||
pub fn dep_info_by_scope(
|
||||
&mut self,
|
||||
) -> Arc<BTreeMap<Option<ModuleSpecifier>, BTreeSet<PackageReq>>> {
|
||||
self.calculate_npm_reqs_if_dirty();
|
||||
self.npm_reqs_by_scope.clone()
|
||||
) -> Arc<BTreeMap<Option<ModuleSpecifier>, Arc<ScopeDepInfo>>> {
|
||||
self.calculate_dep_info_if_dirty();
|
||||
self.dep_info_by_scope.clone()
|
||||
}
|
||||
|
||||
pub fn scopes_with_node_specifier(
|
||||
&self,
|
||||
) -> &Arc<HashSet<Option<ModuleSpecifier>>> {
|
||||
&self.scopes_with_node_specifier
|
||||
pub fn scopes_with_node_specifier(&self) -> HashSet<Option<ModuleSpecifier>> {
|
||||
self
|
||||
.dep_info_by_scope
|
||||
.iter()
|
||||
.filter(|(_, i)| i.has_node_specifier)
|
||||
.map(|(s, _)| s.clone())
|
||||
.collect::<HashSet<_>>()
|
||||
}
|
||||
|
||||
/// Return a document for the specifier.
|
||||
|
@ -1165,6 +1191,7 @@ impl Documents {
|
|||
if let Some(old_doc) = old_doc {
|
||||
self.file_system_docs.get(
|
||||
specifier,
|
||||
&self.is_cjs_resolver,
|
||||
&self.resolver,
|
||||
&self.config,
|
||||
&self.cache,
|
||||
|
@ -1180,20 +1207,20 @@ impl Documents {
|
|||
pub fn get_or_load(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
referrer: &ModuleSpecifier,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<Arc<Document>> {
|
||||
let file_referrer = self.get_file_referrer(referrer);
|
||||
let specifier =
|
||||
self.resolve_document_specifier(specifier, file_referrer.as_deref())?;
|
||||
self.resolve_document_specifier(specifier, file_referrer)?;
|
||||
if let Some(document) = self.open_docs.get(&specifier) {
|
||||
Some(document.clone())
|
||||
} else {
|
||||
self.file_system_docs.get(
|
||||
&specifier,
|
||||
&self.is_cjs_resolver,
|
||||
&self.resolver,
|
||||
&self.config,
|
||||
&self.cache,
|
||||
file_referrer.as_deref(),
|
||||
file_referrer,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1244,57 +1271,64 @@ impl Documents {
|
|||
/// tsc when type checking.
|
||||
pub fn resolve(
|
||||
&self,
|
||||
specifiers: &[String],
|
||||
raw_specifiers: &[String],
|
||||
referrer: &ModuleSpecifier,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Vec<Option<(ModuleSpecifier, MediaType)>> {
|
||||
let document = self.get(referrer);
|
||||
let file_referrer = document
|
||||
let referrer_doc = self.get(referrer);
|
||||
let file_referrer = referrer_doc
|
||||
.as_ref()
|
||||
.and_then(|d| d.file_referrer())
|
||||
.or(file_referrer);
|
||||
let dependencies = document.as_ref().map(|d| d.dependencies());
|
||||
let dependencies = referrer_doc.as_ref().map(|d| d.dependencies());
|
||||
let referrer_kind = self
|
||||
.is_cjs_resolver
|
||||
.get_maybe_doc_module_kind(referrer, referrer_doc.as_deref());
|
||||
let mut results = Vec::new();
|
||||
for specifier in specifiers {
|
||||
if specifier.starts_with("asset:") {
|
||||
if let Ok(specifier) = ModuleSpecifier::parse(specifier) {
|
||||
for raw_specifier in raw_specifiers {
|
||||
if raw_specifier.starts_with("asset:") {
|
||||
if let Ok(specifier) = ModuleSpecifier::parse(raw_specifier) {
|
||||
let media_type = MediaType::from_specifier(&specifier);
|
||||
results.push(Some((specifier, media_type)));
|
||||
} else {
|
||||
results.push(None);
|
||||
}
|
||||
} else if let Some(dep) =
|
||||
dependencies.as_ref().and_then(|d| d.get(specifier))
|
||||
dependencies.as_ref().and_then(|d| d.get(raw_specifier))
|
||||
{
|
||||
if let Some(specifier) = dep.maybe_type.maybe_specifier() {
|
||||
results.push(self.resolve_dependency(
|
||||
specifier,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
file_referrer,
|
||||
));
|
||||
} else if let Some(specifier) = dep.maybe_code.maybe_specifier() {
|
||||
results.push(self.resolve_dependency(
|
||||
specifier,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
file_referrer,
|
||||
));
|
||||
} else {
|
||||
results.push(None);
|
||||
}
|
||||
} else if let Ok(specifier) =
|
||||
self.resolver.as_graph_resolver(file_referrer).resolve(
|
||||
specifier,
|
||||
self.resolver.as_cli_resolver(file_referrer).resolve(
|
||||
raw_specifier,
|
||||
&deno_graph::Range {
|
||||
specifier: referrer.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
},
|
||||
referrer_kind,
|
||||
ResolutionMode::Types,
|
||||
)
|
||||
{
|
||||
results.push(self.resolve_dependency(
|
||||
&specifier,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
file_referrer,
|
||||
));
|
||||
} else {
|
||||
|
@ -1313,12 +1347,16 @@ impl Documents {
|
|||
) {
|
||||
self.config = Arc::new(config.clone());
|
||||
self.cache = Arc::new(cache.clone());
|
||||
self.is_cjs_resolver = Arc::new(LspIsCjsResolver::new(cache));
|
||||
self.resolver = resolver.clone();
|
||||
|
||||
node_resolver::PackageJsonThreadLocalCache::clear();
|
||||
|
||||
{
|
||||
let fs_docs = &self.file_system_docs;
|
||||
// Clean up non-existent documents.
|
||||
fs_docs.docs.retain(|specifier, _| {
|
||||
let Ok(path) = specifier_to_file_path(specifier) else {
|
||||
let Ok(path) = url_to_file_path(specifier) else {
|
||||
// Remove non-file schemed docs (deps). They may not be dependencies
|
||||
// anymore after updating resolvers.
|
||||
return false;
|
||||
|
@ -1333,14 +1371,21 @@ impl Documents {
|
|||
if !config.specifier_enabled(doc.specifier()) {
|
||||
continue;
|
||||
}
|
||||
*doc = doc.with_new_config(self.resolver.clone(), self.config.clone());
|
||||
*doc = doc.with_new_config(
|
||||
&self.is_cjs_resolver,
|
||||
self.resolver.clone(),
|
||||
self.config.clone(),
|
||||
);
|
||||
}
|
||||
for mut doc in self.file_system_docs.docs.iter_mut() {
|
||||
if !config.specifier_enabled(doc.specifier()) {
|
||||
continue;
|
||||
}
|
||||
*doc.value_mut() =
|
||||
doc.with_new_config(self.resolver.clone(), self.config.clone());
|
||||
*doc.value_mut() = doc.with_new_config(
|
||||
&self.is_cjs_resolver,
|
||||
self.resolver.clone(),
|
||||
self.config.clone(),
|
||||
);
|
||||
}
|
||||
self.open_docs = open_docs;
|
||||
let mut preload_count = 0;
|
||||
|
@ -1357,6 +1402,7 @@ impl Documents {
|
|||
{
|
||||
fs_docs.refresh_document(
|
||||
specifier,
|
||||
&self.is_cjs_resolver,
|
||||
&self.resolver,
|
||||
&self.config,
|
||||
&self.cache,
|
||||
|
@ -1372,34 +1418,46 @@ impl Documents {
|
|||
/// Iterate through the documents, building a map where the key is a unique
|
||||
/// document and the value is a set of specifiers that depend on that
|
||||
/// document.
|
||||
fn calculate_npm_reqs_if_dirty(&mut self) {
|
||||
let mut npm_reqs_by_scope: BTreeMap<_, BTreeSet<_>> = Default::default();
|
||||
let mut scopes_with_specifier = HashSet::new();
|
||||
fn calculate_dep_info_if_dirty(&mut self) {
|
||||
let mut dep_info_by_scope: BTreeMap<_, ScopeDepInfo> = Default::default();
|
||||
let is_fs_docs_dirty = self.file_system_docs.set_dirty(false);
|
||||
if !is_fs_docs_dirty && !self.dirty {
|
||||
return;
|
||||
}
|
||||
let mut visit_doc = |doc: &Arc<Document>| {
|
||||
let scope = doc.scope();
|
||||
let reqs = npm_reqs_by_scope.entry(scope.cloned()).or_default();
|
||||
let dep_info = dep_info_by_scope.entry(scope.cloned()).or_default();
|
||||
for dependency in doc.dependencies().values() {
|
||||
if let Some(dep) = dependency.get_code() {
|
||||
let code_specifier = dependency.get_code();
|
||||
let type_specifier = dependency.get_type();
|
||||
if let Some(dep) = code_specifier {
|
||||
if dep.scheme() == "node" {
|
||||
scopes_with_specifier.insert(scope.cloned());
|
||||
dep_info.has_node_specifier = true;
|
||||
}
|
||||
if let Ok(reference) = NpmPackageReqReference::from_specifier(dep) {
|
||||
reqs.insert(reference.into_inner().req);
|
||||
dep_info.npm_reqs.insert(reference.into_inner().req);
|
||||
}
|
||||
}
|
||||
if let Some(dep) = dependency.get_type() {
|
||||
if let Some(dep) = type_specifier {
|
||||
if let Ok(reference) = NpmPackageReqReference::from_specifier(dep) {
|
||||
reqs.insert(reference.into_inner().req);
|
||||
dep_info.npm_reqs.insert(reference.into_inner().req);
|
||||
}
|
||||
}
|
||||
if dependency.maybe_deno_types_specifier.is_some() {
|
||||
if let (Some(code_specifier), Some(type_specifier)) =
|
||||
(code_specifier, type_specifier)
|
||||
{
|
||||
if MediaType::from_specifier(type_specifier).is_declaration() {
|
||||
dep_info
|
||||
.deno_types_to_code_resolutions
|
||||
.insert(type_specifier.clone(), code_specifier.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(dep) = doc.maybe_types_dependency().maybe_specifier() {
|
||||
if let Ok(reference) = NpmPackageReqReference::from_specifier(dep) {
|
||||
reqs.insert(reference.into_inner().req);
|
||||
dep_info.npm_reqs.insert(reference.into_inner().req);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1410,16 +1468,49 @@ impl Documents {
|
|||
visit_doc(doc);
|
||||
}
|
||||
|
||||
// fill the reqs from the lockfile
|
||||
for (scope, config_data) in self.config.tree.data_by_scope().as_ref() {
|
||||
let dep_info = dep_info_by_scope.entry(Some(scope.clone())).or_default();
|
||||
(|| {
|
||||
let config_file = config_data.maybe_deno_json()?;
|
||||
let jsx_config =
|
||||
config_file.to_maybe_jsx_import_source_config().ok()??;
|
||||
let type_specifier = jsx_config.default_types_specifier.as_ref()?;
|
||||
let code_specifier = jsx_config.default_specifier.as_ref()?;
|
||||
let cli_resolver = self.resolver.as_cli_resolver(Some(scope));
|
||||
let range = deno_graph::Range {
|
||||
specifier: jsx_config.base_url.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
};
|
||||
let type_specifier = cli_resolver
|
||||
.resolve(
|
||||
type_specifier,
|
||||
&range,
|
||||
// todo(dsherret): this is wrong because it doesn't consider CJS referrers
|
||||
deno_package_json::NodeModuleKind::Esm,
|
||||
ResolutionMode::Types,
|
||||
)
|
||||
.ok()?;
|
||||
let code_specifier = cli_resolver
|
||||
.resolve(
|
||||
code_specifier,
|
||||
&range,
|
||||
// todo(dsherret): this is wrong because it doesn't consider CJS referrers
|
||||
deno_package_json::NodeModuleKind::Esm,
|
||||
ResolutionMode::Execution,
|
||||
)
|
||||
.ok()?;
|
||||
dep_info
|
||||
.deno_types_to_code_resolutions
|
||||
.insert(type_specifier, code_specifier);
|
||||
Some(())
|
||||
})();
|
||||
// fill the reqs from the lockfile
|
||||
if let Some(lockfile) = config_data.lockfile.as_ref() {
|
||||
let reqs = npm_reqs_by_scope.entry(Some(scope.clone())).or_default();
|
||||
let lockfile = lockfile.lock();
|
||||
for key in lockfile.content.packages.specifiers.keys() {
|
||||
if let Some(key) = key.strip_prefix("npm:") {
|
||||
if let Ok(req) = PackageReq::from_str(key) {
|
||||
reqs.insert(req);
|
||||
}
|
||||
for dep_req in lockfile.content.packages.specifiers.keys() {
|
||||
if dep_req.kind == deno_semver::package::PackageKind::Npm {
|
||||
dep_info.npm_reqs.insert(dep_req.req.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1428,15 +1519,22 @@ impl Documents {
|
|||
// Ensure a @types/node package exists when any module uses a node: specifier.
|
||||
// Unlike on the command line, here we just add @types/node to the npm package
|
||||
// requirements since this won't end up in the lockfile.
|
||||
for scope in &scopes_with_specifier {
|
||||
let reqs = npm_reqs_by_scope.entry(scope.clone()).or_default();
|
||||
if !reqs.iter().any(|r| r.name == "@types/node") {
|
||||
reqs.insert(PackageReq::from_str("@types/node").unwrap());
|
||||
for dep_info in dep_info_by_scope.values_mut() {
|
||||
if dep_info.has_node_specifier
|
||||
&& !dep_info.npm_reqs.iter().any(|r| r.name == "@types/node")
|
||||
{
|
||||
dep_info
|
||||
.npm_reqs
|
||||
.insert(PackageReq::from_str("@types/node").unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
self.npm_reqs_by_scope = Arc::new(npm_reqs_by_scope);
|
||||
self.scopes_with_node_specifier = Arc::new(scopes_with_specifier);
|
||||
self.dep_info_by_scope = Arc::new(
|
||||
dep_info_by_scope
|
||||
.into_iter()
|
||||
.map(|(s, i)| (s, Arc::new(i)))
|
||||
.collect(),
|
||||
);
|
||||
self.dirty = false;
|
||||
}
|
||||
|
||||
|
@ -1444,6 +1542,7 @@ impl Documents {
|
|||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<(ModuleSpecifier, MediaType)> {
|
||||
if let Some(module_name) = specifier.as_str().strip_prefix("node:") {
|
||||
|
@ -1457,20 +1556,23 @@ impl Documents {
|
|||
let mut specifier = specifier.clone();
|
||||
let mut media_type = None;
|
||||
if let Ok(npm_ref) = NpmPackageReqReference::from_specifier(&specifier) {
|
||||
let (s, mt) =
|
||||
self
|
||||
.resolver
|
||||
.npm_to_file_url(&npm_ref, referrer, file_referrer)?;
|
||||
let (s, mt) = self.resolver.npm_to_file_url(
|
||||
&npm_ref,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
file_referrer,
|
||||
)?;
|
||||
specifier = s;
|
||||
media_type = Some(mt);
|
||||
}
|
||||
let Some(doc) = self.get_or_load(&specifier, referrer) else {
|
||||
let Some(doc) = self.get_or_load(&specifier, file_referrer) else {
|
||||
let media_type =
|
||||
media_type.unwrap_or_else(|| MediaType::from_specifier(&specifier));
|
||||
return Some((specifier, media_type));
|
||||
};
|
||||
if let Some(types) = doc.maybe_types_dependency().maybe_specifier() {
|
||||
self.resolve_dependency(types, &specifier, file_referrer)
|
||||
let specifier_kind = self.is_cjs_resolver.get_doc_module_kind(&doc);
|
||||
self.resolve_dependency(types, &specifier, specifier_kind, file_referrer)
|
||||
} else {
|
||||
Some((doc.specifier().clone(), doc.media_type()))
|
||||
}
|
||||
|
@ -1519,12 +1621,16 @@ impl<'a> deno_graph::source::Loader for OpenDocumentsGraphLoader<'a> {
|
|||
fn cache_module_info(
|
||||
&self,
|
||||
specifier: &deno_ast::ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
source: &Arc<[u8]>,
|
||||
module_info: &deno_graph::ModuleInfo,
|
||||
) {
|
||||
self
|
||||
.inner_loader
|
||||
.cache_module_info(specifier, source, module_info)
|
||||
self.inner_loader.cache_module_info(
|
||||
specifier,
|
||||
media_type,
|
||||
source,
|
||||
module_info,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1534,6 +1640,7 @@ fn parse_and_analyze_module(
|
|||
maybe_headers: Option<&HashMap<String, String>>,
|
||||
media_type: MediaType,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
is_cjs_resolver: &LspIsCjsResolver,
|
||||
resolver: &LspResolver,
|
||||
) -> (Option<ParsedSourceResult>, Option<ModuleResult>) {
|
||||
let parsed_source_result = parse_source(specifier.clone(), text, media_type);
|
||||
|
@ -1542,6 +1649,7 @@ fn parse_and_analyze_module(
|
|||
&parsed_source_result,
|
||||
maybe_headers,
|
||||
file_referrer,
|
||||
is_cjs_resolver,
|
||||
resolver,
|
||||
);
|
||||
(Some(parsed_source_result), Some(module_result))
|
||||
|
@ -1552,7 +1660,7 @@ fn parse_source(
|
|||
text: Arc<str>,
|
||||
media_type: MediaType,
|
||||
) -> ParsedSourceResult {
|
||||
deno_ast::parse_module(deno_ast::ParseParams {
|
||||
deno_ast::parse_program(deno_ast::ParseParams {
|
||||
specifier,
|
||||
text,
|
||||
media_type,
|
||||
|
@ -1567,11 +1675,26 @@ fn analyze_module(
|
|||
parsed_source_result: &ParsedSourceResult,
|
||||
maybe_headers: Option<&HashMap<String, String>>,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
is_cjs_resolver: &LspIsCjsResolver,
|
||||
resolver: &LspResolver,
|
||||
) -> ModuleResult {
|
||||
match parsed_source_result {
|
||||
Ok(parsed_source) => {
|
||||
let npm_resolver = resolver.create_graph_npm_resolver(file_referrer);
|
||||
let cli_resolver = resolver.as_cli_resolver(file_referrer);
|
||||
let config_data = resolver.as_config_data(file_referrer);
|
||||
let valid_referrer = specifier.clone();
|
||||
let jsx_import_source_config =
|
||||
config_data.and_then(|d| d.maybe_jsx_import_source_config());
|
||||
let resolver = SingleReferrerGraphResolver {
|
||||
valid_referrer: &valid_referrer,
|
||||
referrer_kind: is_cjs_resolver.get_lsp_referrer_kind(
|
||||
&specifier,
|
||||
Some(parsed_source.compute_is_script()),
|
||||
),
|
||||
cli_resolver,
|
||||
jsx_import_source_config: jsx_import_source_config.as_ref(),
|
||||
};
|
||||
Ok(deno_graph::parse_module_from_ast(
|
||||
deno_graph::ParseModuleFromAstOptions {
|
||||
graph_kind: deno_graph::GraphKind::TypesOnly,
|
||||
|
@ -1582,7 +1705,7 @@ fn analyze_module(
|
|||
// dynamic imports like import(`./dir/${something}`) in the LSP
|
||||
file_system: &deno_graph::source::NullFileSystem,
|
||||
jsr_url_provider: &CliJsrUrlProvider,
|
||||
maybe_resolver: Some(resolver.as_graph_resolver(file_referrer)),
|
||||
maybe_resolver: Some(&resolver),
|
||||
maybe_npm_resolver: Some(&npm_resolver),
|
||||
},
|
||||
))
|
||||
|
@ -1593,6 +1716,24 @@ fn analyze_module(
|
|||
}
|
||||
}
|
||||
|
||||
fn bytes_to_content(
|
||||
specifier: &ModuleSpecifier,
|
||||
media_type: MediaType,
|
||||
bytes: Vec<u8>,
|
||||
maybe_charset: Option<&str>,
|
||||
) -> Result<String, AnyError> {
|
||||
if media_type == MediaType::Wasm {
|
||||
// we use the dts representation for Wasm modules
|
||||
Ok(deno_graph::source::wasm::wasm_module_to_dts(&bytes)?)
|
||||
} else {
|
||||
Ok(deno_graph::source::decode_owned_source(
|
||||
specifier,
|
||||
bytes,
|
||||
maybe_charset,
|
||||
)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -1608,7 +1749,7 @@ mod tests {
|
|||
async fn setup() -> (Documents, LspCache, TempDir) {
|
||||
let temp_dir = TempDir::new();
|
||||
temp_dir.create_dir_all(".deno_dir");
|
||||
let cache = LspCache::new(Some(temp_dir.uri().join(".deno_dir").unwrap()));
|
||||
let cache = LspCache::new(Some(temp_dir.url().join(".deno_dir").unwrap()));
|
||||
let config = Config::default();
|
||||
let resolver =
|
||||
Arc::new(LspResolver::from_config(&config, &cache, None).await);
|
||||
|
@ -1691,7 +1832,7 @@ console.log(b, "hello deno");
|
|||
// but we'll guard against it anyway
|
||||
let (mut documents, _, temp_dir) = setup().await;
|
||||
let file_path = temp_dir.path().join("file.ts");
|
||||
let file_specifier = temp_dir.uri().join("file.ts").unwrap();
|
||||
let file_specifier = temp_dir.url().join("file.ts").unwrap();
|
||||
file_path.write("");
|
||||
|
||||
// open the document
|
||||
|
@ -1719,18 +1860,18 @@ console.log(b, "hello deno");
|
|||
let (mut documents, cache, temp_dir) = setup().await;
|
||||
|
||||
let file1_path = temp_dir.path().join("file1.ts");
|
||||
let file1_specifier = temp_dir.uri().join("file1.ts").unwrap();
|
||||
let file1_specifier = temp_dir.url().join("file1.ts").unwrap();
|
||||
fs::write(&file1_path, "").unwrap();
|
||||
|
||||
let file2_path = temp_dir.path().join("file2.ts");
|
||||
let file2_specifier = temp_dir.uri().join("file2.ts").unwrap();
|
||||
let file2_specifier = temp_dir.url().join("file2.ts").unwrap();
|
||||
fs::write(&file2_path, "").unwrap();
|
||||
|
||||
let file3_path = temp_dir.path().join("file3.ts");
|
||||
let file3_specifier = temp_dir.uri().join("file3.ts").unwrap();
|
||||
let file3_specifier = temp_dir.url().join("file3.ts").unwrap();
|
||||
fs::write(&file3_path, "").unwrap();
|
||||
|
||||
let mut config = Config::new_with_roots([temp_dir.uri()]);
|
||||
let mut config = Config::new_with_roots([temp_dir.url()]);
|
||||
let workspace_settings =
|
||||
serde_json::from_str(r#"{ "enable": true }"#).unwrap();
|
||||
config.set_workspace_settings(workspace_settings, vec![]);
|
||||
|
|
|
@ -14,13 +14,11 @@ use deno_graph::packages::JsrPackageInfo;
|
|||
use deno_graph::packages::JsrPackageInfoVersion;
|
||||
use deno_graph::packages::JsrPackageVersionInfo;
|
||||
use deno_graph::ModuleSpecifier;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use deno_semver::jsr::JsrPackageReqReference;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use deno_semver::Version;
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -93,20 +91,23 @@ impl JsrCacheResolver {
|
|||
}
|
||||
}
|
||||
if let Some(lockfile) = config_data.and_then(|d| d.lockfile.as_ref()) {
|
||||
for (req_url, nv_url) in &lockfile.lock().content.packages.specifiers {
|
||||
let Some(req) = req_url.strip_prefix("jsr:") else {
|
||||
for (dep_req, version) in &lockfile.lock().content.packages.specifiers {
|
||||
let req = match dep_req.kind {
|
||||
deno_semver::package::PackageKind::Jsr => &dep_req.req,
|
||||
deno_semver::package::PackageKind::Npm => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let Ok(version) = Version::parse_standard(version) else {
|
||||
continue;
|
||||
};
|
||||
let Some(nv) = nv_url.strip_prefix("jsr:") else {
|
||||
continue;
|
||||
};
|
||||
let Ok(req) = PackageReq::from_str(req) else {
|
||||
continue;
|
||||
};
|
||||
let Ok(nv) = PackageNv::from_str(nv) else {
|
||||
continue;
|
||||
};
|
||||
nv_by_req.insert(req, Some(nv));
|
||||
nv_by_req.insert(
|
||||
req.clone(),
|
||||
Some(PackageNv {
|
||||
name: req.name.clone(),
|
||||
version,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
Self {
|
||||
|
@ -157,7 +158,7 @@ impl JsrCacheResolver {
|
|||
let maybe_nv = self.req_to_nv(&req);
|
||||
let nv = maybe_nv.as_ref()?;
|
||||
let info = self.package_version_info(nv)?;
|
||||
let path = info.export(&normalize_export_name(req_ref.sub_path()))?;
|
||||
let path = info.export(&req_ref.export_name())?;
|
||||
if let Some(workspace_scope) = self.workspace_scope_by_name.get(&nv.name) {
|
||||
workspace_scope.join(path).ok()
|
||||
} else {
|
||||
|
@ -259,36 +260,9 @@ fn read_cached_url(
|
|||
cache: &Arc<dyn HttpCache>,
|
||||
) -> Option<Vec<u8>> {
|
||||
cache
|
||||
.read_file_bytes(
|
||||
&cache.cache_item_key(url).ok()?,
|
||||
None,
|
||||
deno_cache_dir::GlobalToLocalCopy::Disallow,
|
||||
)
|
||||
.get(&cache.cache_item_key(url).ok()?, None)
|
||||
.ok()?
|
||||
}
|
||||
|
||||
// TODO(nayeemrmn): This is duplicated from a private function in deno_graph
|
||||
// 0.65.1. Make it public or cleanup otherwise.
|
||||
fn normalize_export_name(sub_path: Option<&str>) -> Cow<str> {
|
||||
let Some(sub_path) = sub_path else {
|
||||
return Cow::Borrowed(".");
|
||||
};
|
||||
if sub_path.is_empty() || matches!(sub_path, "/" | ".") {
|
||||
Cow::Borrowed(".")
|
||||
} else {
|
||||
let sub_path = if sub_path.starts_with('/') {
|
||||
Cow::Owned(format!(".{}", sub_path))
|
||||
} else if !sub_path.starts_with("./") {
|
||||
Cow::Owned(format!("./{}", sub_path))
|
||||
} else {
|
||||
Cow::Borrowed(sub_path)
|
||||
};
|
||||
if let Some(prefix) = sub_path.strip_suffix('/') {
|
||||
Cow::Owned(prefix.to_string())
|
||||
} else {
|
||||
sub_path
|
||||
}
|
||||
}
|
||||
.map(|f| f.content)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -336,7 +310,7 @@ impl PackageSearchApi for CliJsrSearchApi {
|
|||
// spawn due to the lsp's `Send` requirement
|
||||
let file = deno_core::unsync::spawn(async move {
|
||||
file_fetcher
|
||||
.fetch(&search_url, &PermissionsContainer::allow_all())
|
||||
.fetch_bypass_permissions(&search_url)
|
||||
.await?
|
||||
.into_text_decoded()
|
||||
})
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -17,7 +17,7 @@ pub struct TaskDefinition {
|
|||
// TODO(nayeemrmn): Rename this to `command` in vscode_deno.
|
||||
#[serde(rename = "detail")]
|
||||
pub command: String,
|
||||
pub source_uri: lsp::Url,
|
||||
pub source_uri: lsp::Uri,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
|
@ -46,6 +46,30 @@ pub struct DiagnosticBatchNotificationParams {
|
|||
pub messages_len: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DenoConfigurationData {
|
||||
pub scope_uri: lsp::Uri,
|
||||
pub workspace_root_scope_uri: Option<lsp::Uri>,
|
||||
pub deno_json: Option<lsp::TextDocumentIdentifier>,
|
||||
pub package_json: Option<lsp::TextDocumentIdentifier>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DidRefreshDenoConfigurationTreeNotificationParams {
|
||||
pub data: Vec<DenoConfigurationData>,
|
||||
}
|
||||
|
||||
pub enum DidRefreshDenoConfigurationTreeNotification {}
|
||||
|
||||
impl lsp::notification::Notification
|
||||
for DidRefreshDenoConfigurationTreeNotification
|
||||
{
|
||||
type Params = DidRefreshDenoConfigurationTreeNotificationParams;
|
||||
const METHOD: &'static str = "deno/didRefreshDenoConfigurationTree";
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, Hash, PartialEq, Copy, Clone, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum DenoConfigurationChangeType {
|
||||
|
@ -75,8 +99,8 @@ pub enum DenoConfigurationType {
|
|||
#[derive(Debug, Eq, Hash, PartialEq, Clone, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DenoConfigurationChangeEvent {
|
||||
pub scope_uri: lsp::Url,
|
||||
pub file_uri: lsp::Url,
|
||||
pub scope_uri: lsp::Uri,
|
||||
pub file_uri: lsp::Uri,
|
||||
#[serde(rename = "type")]
|
||||
pub typ: DenoConfigurationChangeType,
|
||||
pub configuration_type: DenoConfigurationType,
|
||||
|
@ -88,13 +112,15 @@ pub struct DidChangeDenoConfigurationNotificationParams {
|
|||
pub changes: Vec<DenoConfigurationChangeEvent>,
|
||||
}
|
||||
|
||||
// TODO(nayeemrmn): This is being replaced by
|
||||
// `DidRefreshDenoConfigurationTreeNotification` for Deno > v2.0.0. Remove it
|
||||
// soon.
|
||||
pub enum DidChangeDenoConfigurationNotification {}
|
||||
|
||||
impl lsp::notification::Notification
|
||||
for DidChangeDenoConfigurationNotification
|
||||
{
|
||||
type Params = DidChangeDenoConfigurationNotificationParams;
|
||||
|
||||
const METHOD: &'static str = "deno/didChangeDenoConfiguration";
|
||||
}
|
||||
|
||||
|
@ -102,7 +128,6 @@ pub enum DidUpgradeCheckNotification {}
|
|||
|
||||
impl lsp::notification::Notification for DidUpgradeCheckNotification {
|
||||
type Params = DidUpgradeCheckNotificationParams;
|
||||
|
||||
const METHOD: &'static str = "deno/didUpgradeCheck";
|
||||
}
|
||||
|
||||
|
@ -125,6 +150,5 @@ pub enum DiagnosticBatchNotification {}
|
|||
|
||||
impl lsp::notification::Notification for DiagnosticBatchNotification {
|
||||
type Params = DiagnosticBatchNotificationParams;
|
||||
|
||||
const METHOD: &'static str = "deno/internalTestDiagnosticBatch";
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use dashmap::DashMap;
|
|||
use deno_core::anyhow::anyhow;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use deno_npm::npm_rc::NpmRc;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::Version;
|
||||
use serde::Deserialize;
|
||||
|
@ -26,7 +26,10 @@ pub struct CliNpmSearchApi {
|
|||
|
||||
impl CliNpmSearchApi {
|
||||
pub fn new(file_fetcher: Arc<FileFetcher>) -> Self {
|
||||
let resolver = NpmFetchResolver::new(file_fetcher.clone());
|
||||
let resolver = NpmFetchResolver::new(
|
||||
file_fetcher.clone(),
|
||||
Arc::new(NpmRc::default().as_resolved(npm_registry_url()).unwrap()),
|
||||
);
|
||||
Self {
|
||||
file_fetcher,
|
||||
resolver,
|
||||
|
@ -55,7 +58,7 @@ impl PackageSearchApi for CliNpmSearchApi {
|
|||
let file_fetcher = self.file_fetcher.clone();
|
||||
let file = deno_core::unsync::spawn(async move {
|
||||
file_fetcher
|
||||
.fetch(&search_url, &PermissionsContainer::allow_all())
|
||||
.fetch_bypass_permissions(&search_url)
|
||||
.await?
|
||||
.into_text_decoded()
|
||||
})
|
||||
|
|
|
@ -11,7 +11,7 @@ pub fn start(parent_process_id: u32) {
|
|||
std::thread::sleep(Duration::from_secs(10));
|
||||
|
||||
if !is_process_active(parent_process_id) {
|
||||
std::process::exit(1);
|
||||
deno_runtime::exit(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,6 +8,7 @@ use deno_ast::SourceTextInfo;
|
|||
use deno_core::anyhow::anyhow;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use lsp_types::Uri;
|
||||
use tower_lsp::lsp_types::ClientCapabilities;
|
||||
use tower_lsp::lsp_types::ClientInfo;
|
||||
use tower_lsp::lsp_types::CompletionContext;
|
||||
|
@ -40,6 +41,8 @@ use super::config::LanguageWorkspaceSettings;
|
|||
use super::config::ObjectLiteralMethodSnippets;
|
||||
use super::config::TestingSettings;
|
||||
use super::config::WorkspaceSettings;
|
||||
use super::urls::uri_parse_unencoded;
|
||||
use super::urls::url_to_uri;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ReplCompletionItem {
|
||||
|
@ -73,7 +76,7 @@ impl ReplLanguageServer {
|
|||
.initialize(InitializeParams {
|
||||
process_id: None,
|
||||
root_path: None,
|
||||
root_uri: Some(cwd_uri.clone()),
|
||||
root_uri: Some(url_to_uri(&cwd_uri).unwrap()),
|
||||
initialization_options: Some(
|
||||
serde_json::to_value(get_repl_workspace_settings()).unwrap(),
|
||||
),
|
||||
|
@ -84,6 +87,7 @@ impl ReplLanguageServer {
|
|||
general: None,
|
||||
experimental: None,
|
||||
offset_encoding: None,
|
||||
notebook_document: None,
|
||||
},
|
||||
trace: None,
|
||||
workspace_folders: None,
|
||||
|
@ -92,6 +96,7 @@ impl ReplLanguageServer {
|
|||
version: None,
|
||||
}),
|
||||
locale: None,
|
||||
work_done_progress_params: Default::default(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
|
@ -133,7 +138,7 @@ impl ReplLanguageServer {
|
|||
.completion(CompletionParams {
|
||||
text_document_position: TextDocumentPositionParams {
|
||||
text_document: TextDocumentIdentifier {
|
||||
uri: self.get_document_specifier(),
|
||||
uri: self.get_document_uri(),
|
||||
},
|
||||
position: Position {
|
||||
line: line_and_column.line_index as u32,
|
||||
|
@ -208,7 +213,7 @@ impl ReplLanguageServer {
|
|||
.language_server
|
||||
.did_change(DidChangeTextDocumentParams {
|
||||
text_document: VersionedTextDocumentIdentifier {
|
||||
uri: self.get_document_specifier(),
|
||||
uri: self.get_document_uri(),
|
||||
version: self.document_version,
|
||||
},
|
||||
content_changes: vec![TextDocumentContentChangeEvent {
|
||||
|
@ -233,7 +238,7 @@ impl ReplLanguageServer {
|
|||
.language_server
|
||||
.did_close(DidCloseTextDocumentParams {
|
||||
text_document: TextDocumentIdentifier {
|
||||
uri: self.get_document_specifier(),
|
||||
uri: self.get_document_uri(),
|
||||
},
|
||||
})
|
||||
.await;
|
||||
|
@ -248,7 +253,7 @@ impl ReplLanguageServer {
|
|||
.language_server
|
||||
.did_open(DidOpenTextDocumentParams {
|
||||
text_document: TextDocumentItem {
|
||||
uri: self.get_document_specifier(),
|
||||
uri: self.get_document_uri(),
|
||||
language_id: "typescript".to_string(),
|
||||
version: self.document_version,
|
||||
text: format!("{}{}", self.document_text, self.pending_text),
|
||||
|
@ -257,8 +262,9 @@ impl ReplLanguageServer {
|
|||
.await;
|
||||
}
|
||||
|
||||
fn get_document_specifier(&self) -> ModuleSpecifier {
|
||||
self.cwd_uri.join("$deno$repl.ts").unwrap()
|
||||
fn get_document_uri(&self) -> Uri {
|
||||
uri_parse_unencoded(self.cwd_uri.join("$deno$repl.mts").unwrap().as_str())
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -306,7 +312,7 @@ pub fn get_repl_workspace_settings() -> WorkspaceSettings {
|
|||
document_preload_limit: 0, // don't pre-load any modules as it's expensive and not useful for the repl
|
||||
tls_certificate: None,
|
||||
unsafely_ignore_certificate_errors: None,
|
||||
unstable: false,
|
||||
unstable: Default::default(),
|
||||
suggest: DenoCompletionSettings {
|
||||
imports: ImportCompletionSettings {
|
||||
auto_discover: false,
|
||||
|
|
|
@ -1,52 +1,37 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::args::create_default_npmrc;
|
||||
use crate::args::CacheSetting;
|
||||
use crate::args::CliLockfile;
|
||||
use crate::args::PackageJsonInstallDepsProvider;
|
||||
use crate::args::DENO_FUTURE;
|
||||
use crate::graph_util::CliJsrUrlProvider;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
use crate::lsp::config::Config;
|
||||
use crate::lsp::config::ConfigData;
|
||||
use crate::lsp::logging::lsp_warn;
|
||||
use crate::npm::create_cli_npm_resolver_for_lsp;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::CliNpmResolverByonmCreateOptions;
|
||||
use crate::npm::CliNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||
use crate::npm::ManagedCliNpmResolver;
|
||||
use crate::resolver::CjsResolutionStore;
|
||||
use crate::resolver::CliGraphResolver;
|
||||
use crate::resolver::CliGraphResolverOptions;
|
||||
use crate::resolver::CliNodeResolver;
|
||||
use crate::resolver::WorkerCliNpmGraphResolver;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
use crate::util::progress_bar::ProgressBarStyle;
|
||||
use dashmap::DashMap;
|
||||
use deno_ast::MediaType;
|
||||
use deno_cache_dir::npm::NpmCacheDir;
|
||||
use deno_cache_dir::HttpCache;
|
||||
use deno_config::deno_json::JsxImportSourceConfig;
|
||||
use deno_config::workspace::PackageJsonDepResolution;
|
||||
use deno_config::workspace::WorkspaceResolver;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::url::Url;
|
||||
use deno_graph::source::Resolver;
|
||||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::GraphImport;
|
||||
use deno_graph::ModuleSpecifier;
|
||||
use deno_graph::Range;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_path_util::url_from_directory_path;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_resolver::npm::NpmReqResolverOptions;
|
||||
use deno_resolver::DenoResolverOptions;
|
||||
use deno_resolver::NodeAndNpmReqResolver;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node::NodeResolver;
|
||||
use deno_runtime::deno_node::PackageJson;
|
||||
use deno_runtime::fs_util::specifier_to_file_path;
|
||||
use deno_runtime::deno_node::PackageJsonResolver;
|
||||
use deno_semver::jsr::JsrPackageReqReference;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use indexmap::IndexMap;
|
||||
use node_resolver::errors::ClosestPkgJsonError;
|
||||
use node_resolver::NodeResolution;
|
||||
use node_resolver::InNpmPackageChecker;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use node_resolver::NodeResolutionMode;
|
||||
use node_resolver::NpmResolver;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
|
@ -55,28 +40,71 @@ use std::collections::HashSet;
|
|||
use std::sync::Arc;
|
||||
|
||||
use super::cache::LspCache;
|
||||
use super::documents::Document;
|
||||
use super::jsr::JsrCacheResolver;
|
||||
use crate::args::create_default_npmrc;
|
||||
use crate::args::CacheSetting;
|
||||
use crate::args::CliLockfile;
|
||||
use crate::args::NpmInstallDepsProvider;
|
||||
use crate::cache::DenoCacheEnvFsAdapter;
|
||||
use crate::factory::Deferred;
|
||||
use crate::graph_util::CliJsrUrlProvider;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
use crate::lsp::config::Config;
|
||||
use crate::lsp::config::ConfigData;
|
||||
use crate::lsp::logging::lsp_warn;
|
||||
use crate::npm::create_cli_npm_resolver_for_lsp;
|
||||
use crate::npm::CliByonmNpmResolverCreateOptions;
|
||||
use crate::npm::CliManagedInNpmPkgCheckerCreateOptions;
|
||||
use crate::npm::CliManagedNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::CliNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||
use crate::npm::CreateInNpmPkgCheckerOptions;
|
||||
use crate::npm::ManagedCliNpmResolver;
|
||||
use crate::resolver::CliDenoResolver;
|
||||
use crate::resolver::CliDenoResolverFs;
|
||||
use crate::resolver::CliNpmReqResolver;
|
||||
use crate::resolver::CliResolver;
|
||||
use crate::resolver::CliResolverOptions;
|
||||
use crate::resolver::IsCjsResolver;
|
||||
use crate::resolver::WorkerCliNpmGraphResolver;
|
||||
use crate::tsc::into_specifier_and_media_type;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
use crate::util::progress_bar::ProgressBarStyle;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct LspScopeResolver {
|
||||
graph_resolver: Arc<CliGraphResolver>,
|
||||
resolver: Arc<CliResolver>,
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
jsr_resolver: Option<Arc<JsrCacheResolver>>,
|
||||
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
node_resolver: Option<Arc<CliNodeResolver>>,
|
||||
node_resolver: Option<Arc<NodeResolver>>,
|
||||
npm_pkg_req_resolver: Option<Arc<CliNpmReqResolver>>,
|
||||
pkg_json_resolver: Arc<PackageJsonResolver>,
|
||||
redirect_resolver: Option<Arc<RedirectResolver>>,
|
||||
graph_imports: Arc<IndexMap<ModuleSpecifier, GraphImport>>,
|
||||
dep_info: Arc<Mutex<Arc<ScopeDepInfo>>>,
|
||||
package_json_deps_by_resolution: Arc<IndexMap<ModuleSpecifier, String>>,
|
||||
config_data: Option<Arc<ConfigData>>,
|
||||
}
|
||||
|
||||
impl Default for LspScopeResolver {
|
||||
fn default() -> Self {
|
||||
let factory = ResolverFactory::new(None);
|
||||
Self {
|
||||
graph_resolver: create_graph_resolver(None, None, None),
|
||||
resolver: factory.cli_resolver().clone(),
|
||||
in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(),
|
||||
jsr_resolver: None,
|
||||
npm_resolver: None,
|
||||
node_resolver: None,
|
||||
npm_pkg_req_resolver: None,
|
||||
pkg_json_resolver: factory.pkg_json_resolver().clone(),
|
||||
redirect_resolver: None,
|
||||
graph_imports: Default::default(),
|
||||
dep_info: Default::default(),
|
||||
package_json_deps_by_resolution: Default::default(),
|
||||
config_data: None,
|
||||
}
|
||||
}
|
||||
|
@ -88,22 +116,16 @@ impl LspScopeResolver {
|
|||
cache: &LspCache,
|
||||
http_client_provider: Option<&Arc<HttpClientProvider>>,
|
||||
) -> Self {
|
||||
let mut npm_resolver = None;
|
||||
let mut node_resolver = None;
|
||||
if let Some(http_client) = http_client_provider {
|
||||
npm_resolver = create_npm_resolver(
|
||||
config_data.map(|d| d.as_ref()),
|
||||
cache,
|
||||
http_client,
|
||||
)
|
||||
.await;
|
||||
node_resolver = create_node_resolver(npm_resolver.as_ref());
|
||||
let mut factory = ResolverFactory::new(config_data);
|
||||
if let Some(http_client_provider) = http_client_provider {
|
||||
factory.init_npm_resolver(http_client_provider, cache).await;
|
||||
}
|
||||
let graph_resolver = create_graph_resolver(
|
||||
config_data.map(|d| d.as_ref()),
|
||||
npm_resolver.as_ref(),
|
||||
node_resolver.as_ref(),
|
||||
);
|
||||
let in_npm_pkg_checker = factory.in_npm_pkg_checker().clone();
|
||||
let npm_resolver = factory.npm_resolver().cloned();
|
||||
let node_resolver = factory.node_resolver().cloned();
|
||||
let npm_pkg_req_resolver = factory.npm_pkg_req_resolver().cloned();
|
||||
let cli_resolver = factory.cli_resolver().clone();
|
||||
let pkg_json_resolver = factory.pkg_json_resolver().clone();
|
||||
let jsr_resolver = Some(Arc::new(JsrCacheResolver::new(
|
||||
cache.for_specifier(config_data.map(|d| d.scope.as_ref())),
|
||||
config_data.map(|d| d.as_ref()),
|
||||
|
@ -112,7 +134,9 @@ impl LspScopeResolver {
|
|||
cache.for_specifier(config_data.map(|d| d.scope.as_ref())),
|
||||
config_data.and_then(|d| d.lockfile.clone()),
|
||||
)));
|
||||
let npm_graph_resolver = graph_resolver.create_graph_npm_resolver();
|
||||
let npm_graph_resolver = cli_resolver.create_graph_npm_resolver();
|
||||
let maybe_jsx_import_source_config =
|
||||
config_data.and_then(|d| d.maybe_jsx_import_source_config());
|
||||
let graph_imports = config_data
|
||||
.and_then(|d| d.member_dir.workspace.to_compiler_option_types().ok())
|
||||
.map(|imports| {
|
||||
|
@ -120,11 +144,18 @@ impl LspScopeResolver {
|
|||
imports
|
||||
.into_iter()
|
||||
.map(|(referrer, imports)| {
|
||||
let resolver = SingleReferrerGraphResolver {
|
||||
valid_referrer: &referrer,
|
||||
referrer_kind: NodeModuleKind::Esm,
|
||||
cli_resolver: &cli_resolver,
|
||||
jsx_import_source_config: maybe_jsx_import_source_config
|
||||
.as_ref(),
|
||||
};
|
||||
let graph_import = GraphImport::new(
|
||||
&referrer,
|
||||
imports,
|
||||
&CliJsrUrlProvider,
|
||||
Some(graph_resolver.as_ref()),
|
||||
Some(&resolver),
|
||||
Some(&npm_graph_resolver),
|
||||
);
|
||||
(referrer, graph_import)
|
||||
|
@ -133,33 +164,81 @@ impl LspScopeResolver {
|
|||
)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let package_json_deps_by_resolution = (|| {
|
||||
let npm_pkg_req_resolver = npm_pkg_req_resolver.as_ref()?;
|
||||
let package_json = config_data?.maybe_pkg_json()?;
|
||||
let referrer = package_json.specifier();
|
||||
let dependencies = package_json.dependencies.as_ref()?;
|
||||
let result = dependencies
|
||||
.iter()
|
||||
.flat_map(|(name, _)| {
|
||||
let req_ref =
|
||||
NpmPackageReqReference::from_str(&format!("npm:{name}")).ok()?;
|
||||
let specifier = into_specifier_and_media_type(Some(
|
||||
npm_pkg_req_resolver
|
||||
.resolve_req_reference(
|
||||
&req_ref,
|
||||
&referrer,
|
||||
// todo(dsherret): this is wrong because it doesn't consider CJS referrers
|
||||
NodeModuleKind::Esm,
|
||||
NodeResolutionMode::Types,
|
||||
)
|
||||
.or_else(|_| {
|
||||
npm_pkg_req_resolver.resolve_req_reference(
|
||||
&req_ref,
|
||||
&referrer,
|
||||
// todo(dsherret): this is wrong because it doesn't consider CJS referrers
|
||||
NodeModuleKind::Esm,
|
||||
NodeResolutionMode::Execution,
|
||||
)
|
||||
})
|
||||
.ok()?,
|
||||
))
|
||||
.0;
|
||||
Some((specifier, name.clone()))
|
||||
})
|
||||
.collect();
|
||||
Some(result)
|
||||
})();
|
||||
let package_json_deps_by_resolution =
|
||||
Arc::new(package_json_deps_by_resolution.unwrap_or_default());
|
||||
Self {
|
||||
graph_resolver,
|
||||
resolver: cli_resolver,
|
||||
in_npm_pkg_checker,
|
||||
jsr_resolver,
|
||||
npm_pkg_req_resolver,
|
||||
npm_resolver,
|
||||
node_resolver,
|
||||
pkg_json_resolver,
|
||||
redirect_resolver,
|
||||
graph_imports,
|
||||
dep_info: Default::default(),
|
||||
package_json_deps_by_resolution,
|
||||
config_data: config_data.cloned(),
|
||||
}
|
||||
}
|
||||
|
||||
fn snapshot(&self) -> Arc<Self> {
|
||||
let mut factory = ResolverFactory::new(self.config_data.as_ref());
|
||||
let npm_resolver =
|
||||
self.npm_resolver.as_ref().map(|r| r.clone_snapshotted());
|
||||
let node_resolver = create_node_resolver(npm_resolver.as_ref());
|
||||
let graph_resolver = create_graph_resolver(
|
||||
self.config_data.as_deref(),
|
||||
npm_resolver.as_ref(),
|
||||
node_resolver.as_ref(),
|
||||
);
|
||||
if let Some(npm_resolver) = &npm_resolver {
|
||||
factory.set_npm_resolver(npm_resolver.clone());
|
||||
}
|
||||
Arc::new(Self {
|
||||
graph_resolver,
|
||||
resolver: factory.cli_resolver().clone(),
|
||||
in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(),
|
||||
jsr_resolver: self.jsr_resolver.clone(),
|
||||
npm_resolver,
|
||||
node_resolver,
|
||||
npm_pkg_req_resolver: factory.npm_pkg_req_resolver().cloned(),
|
||||
npm_resolver: factory.npm_resolver().cloned(),
|
||||
node_resolver: factory.node_resolver().cloned(),
|
||||
redirect_resolver: self.redirect_resolver.clone(),
|
||||
pkg_json_resolver: factory.pkg_json_resolver().clone(),
|
||||
graph_imports: self.graph_imports.clone(),
|
||||
dep_info: self.dep_info.clone(),
|
||||
package_json_deps_by_resolution: self
|
||||
.package_json_deps_by_resolution
|
||||
.clone(),
|
||||
config_data: self.config_data.clone(),
|
||||
})
|
||||
}
|
||||
|
@ -223,19 +302,24 @@ impl LspResolver {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn set_npm_reqs(
|
||||
pub async fn set_dep_info_by_scope(
|
||||
&self,
|
||||
reqs: &BTreeMap<Option<ModuleSpecifier>, BTreeSet<PackageReq>>,
|
||||
dep_info_by_scope: &Arc<
|
||||
BTreeMap<Option<ModuleSpecifier>, Arc<ScopeDepInfo>>,
|
||||
>,
|
||||
) {
|
||||
for (scope, resolver) in [(None, &self.unscoped)]
|
||||
.into_iter()
|
||||
.chain(self.by_scope.iter().map(|(s, r)| (Some(s), r)))
|
||||
{
|
||||
let dep_info = dep_info_by_scope.get(&scope.cloned());
|
||||
if let Some(dep_info) = dep_info {
|
||||
*resolver.dep_info.lock() = dep_info.clone();
|
||||
}
|
||||
if let Some(npm_resolver) = resolver.npm_resolver.as_ref() {
|
||||
if let Some(npm_resolver) = npm_resolver.as_managed() {
|
||||
let reqs = reqs
|
||||
.get(&scope.cloned())
|
||||
.map(|reqs| reqs.iter().cloned().collect::<Vec<_>>())
|
||||
let reqs = dep_info
|
||||
.map(|i| i.npm_reqs.iter().cloned().collect::<Vec<_>>())
|
||||
.unwrap_or_default();
|
||||
if let Err(err) = npm_resolver.set_package_reqs(&reqs).await {
|
||||
lsp_warn!("Could not set npm package requirements: {:#}", err);
|
||||
|
@ -245,12 +329,12 @@ impl LspResolver {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn as_graph_resolver(
|
||||
pub fn as_cli_resolver(
|
||||
&self,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> &dyn Resolver {
|
||||
) -> &CliResolver {
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
resolver.graph_resolver.as_ref()
|
||||
resolver.resolver.as_ref()
|
||||
}
|
||||
|
||||
pub fn create_graph_npm_resolver(
|
||||
|
@ -258,7 +342,23 @@ impl LspResolver {
|
|||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> WorkerCliNpmGraphResolver {
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
resolver.graph_resolver.create_graph_npm_resolver()
|
||||
resolver.resolver.create_graph_npm_resolver()
|
||||
}
|
||||
|
||||
pub fn as_config_data(
|
||||
&self,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<&Arc<ConfigData>> {
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
resolver.config_data.as_ref()
|
||||
}
|
||||
|
||||
pub fn in_npm_pkg_checker(
|
||||
&self,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> &Arc<dyn InNpmPackageChecker> {
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
&resolver.in_npm_pkg_checker
|
||||
}
|
||||
|
||||
pub fn maybe_managed_npm_resolver(
|
||||
|
@ -324,15 +424,46 @@ impl LspResolver {
|
|||
&self,
|
||||
req_ref: &NpmPackageReqReference,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<(ModuleSpecifier, MediaType)> {
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
let node_resolver = resolver.node_resolver.as_ref()?;
|
||||
Some(NodeResolution::into_specifier_and_media_type(
|
||||
node_resolver
|
||||
.resolve_req_reference(req_ref, referrer, NodeResolutionMode::Types)
|
||||
.ok(),
|
||||
))
|
||||
let npm_pkg_req_resolver = resolver.npm_pkg_req_resolver.as_ref()?;
|
||||
Some(into_specifier_and_media_type(Some(
|
||||
npm_pkg_req_resolver
|
||||
.resolve_req_reference(
|
||||
req_ref,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
NodeResolutionMode::Types,
|
||||
)
|
||||
.ok()?,
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn file_url_to_package_json_dep(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<String> {
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
resolver
|
||||
.package_json_deps_by_resolution
|
||||
.get(specifier)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub fn deno_types_to_code_resolution(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<ModuleSpecifier> {
|
||||
let resolver = self.get_scope_resolver(file_referrer);
|
||||
let dep_info = resolver.dep_info.lock().clone();
|
||||
dep_info
|
||||
.deno_types_to_code_resolutions
|
||||
.get(specifier)
|
||||
.cloned()
|
||||
}
|
||||
|
||||
pub fn in_node_modules(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
|
@ -346,14 +477,10 @@ impl LspResolver {
|
|||
.contains("/node_modules/")
|
||||
}
|
||||
|
||||
let global_npm_resolver = self
|
||||
.get_scope_resolver(Some(specifier))
|
||||
.npm_resolver
|
||||
.as_ref()
|
||||
.and_then(|npm_resolver| npm_resolver.as_managed())
|
||||
.filter(|r| r.root_node_modules_path().is_none());
|
||||
if let Some(npm_resolver) = &global_npm_resolver {
|
||||
if npm_resolver.in_npm_package(specifier) {
|
||||
if let Some(node_resolver) =
|
||||
&self.get_scope_resolver(Some(specifier)).node_resolver
|
||||
{
|
||||
if node_resolver.in_npm_package(specifier) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -361,16 +488,27 @@ impl LspResolver {
|
|||
has_node_modules_dir(specifier)
|
||||
}
|
||||
|
||||
pub fn node_media_type(
|
||||
pub fn is_bare_package_json_dep(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<MediaType> {
|
||||
let resolver = self.get_scope_resolver(Some(specifier));
|
||||
let node_resolver = resolver.node_resolver.as_ref()?;
|
||||
let resolution = node_resolver
|
||||
.url_to_node_resolution(specifier.clone())
|
||||
.ok()?;
|
||||
Some(NodeResolution::into_specifier_and_media_type(Some(resolution)).1)
|
||||
specifier_text: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
referrer_kind: NodeModuleKind,
|
||||
) -> bool {
|
||||
let resolver = self.get_scope_resolver(Some(referrer));
|
||||
let Some(npm_pkg_req_resolver) = resolver.npm_pkg_req_resolver.as_ref()
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
npm_pkg_req_resolver
|
||||
.resolve_if_for_npm_pkg(
|
||||
specifier_text,
|
||||
referrer,
|
||||
referrer_kind,
|
||||
NodeResolutionMode::Types,
|
||||
)
|
||||
.ok()
|
||||
.flatten()
|
||||
.is_some()
|
||||
}
|
||||
|
||||
pub fn get_closest_package_json(
|
||||
|
@ -378,10 +516,9 @@ impl LspResolver {
|
|||
referrer: &ModuleSpecifier,
|
||||
) -> Result<Option<Arc<PackageJson>>, ClosestPkgJsonError> {
|
||||
let resolver = self.get_scope_resolver(Some(referrer));
|
||||
let Some(node_resolver) = resolver.node_resolver.as_ref() else {
|
||||
return Ok(None);
|
||||
};
|
||||
node_resolver.get_closest_package_json(referrer)
|
||||
resolver
|
||||
.pkg_json_resolver
|
||||
.get_closest_package_json(referrer)
|
||||
}
|
||||
|
||||
pub fn resolve_redirects(
|
||||
|
@ -421,121 +558,225 @@ impl LspResolver {
|
|||
};
|
||||
self
|
||||
.by_scope
|
||||
.iter()
|
||||
.rfind(|(s, _)| file_referrer.as_str().starts_with(s.as_str()))
|
||||
.map(|(_, r)| r.as_ref())
|
||||
.values()
|
||||
.rfind(|r| {
|
||||
r.config_data
|
||||
.as_ref()
|
||||
.map(|d| d.scope_contains_specifier(file_referrer))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.map(|r| r.as_ref())
|
||||
.unwrap_or(self.unscoped.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_npm_resolver(
|
||||
config_data: Option<&ConfigData>,
|
||||
cache: &LspCache,
|
||||
http_client_provider: &Arc<HttpClientProvider>,
|
||||
) -> Option<Arc<dyn CliNpmResolver>> {
|
||||
let enable_byonm = config_data.map(|d| d.byonm).unwrap_or(*DENO_FUTURE);
|
||||
let options = if enable_byonm {
|
||||
CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions {
|
||||
fs: Arc::new(deno_fs::RealFs),
|
||||
root_node_modules_dir: config_data.and_then(|config_data| {
|
||||
config_data.node_modules_dir.clone().or_else(|| {
|
||||
specifier_to_file_path(&config_data.scope)
|
||||
.ok()
|
||||
.map(|p| p.join("node_modules/"))
|
||||
})
|
||||
}),
|
||||
})
|
||||
} else {
|
||||
CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions {
|
||||
http_client_provider: http_client_provider.clone(),
|
||||
snapshot: match config_data.and_then(|d| d.lockfile.as_ref()) {
|
||||
Some(lockfile) => {
|
||||
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
|
||||
lockfile.clone(),
|
||||
)
|
||||
}
|
||||
None => CliNpmResolverManagedSnapshotOption::Specified(None),
|
||||
},
|
||||
// Don't provide the lockfile. We don't want these resolvers
|
||||
// updating it. Only the cache request should update the lockfile.
|
||||
maybe_lockfile: None,
|
||||
fs: Arc::new(deno_fs::RealFs),
|
||||
npm_global_cache_dir: cache.deno_dir().npm_folder_path(),
|
||||
// Use an "only" cache setting in order to make the
|
||||
// user do an explicit "cache" command and prevent
|
||||
// the cache from being filled with lots of packages while
|
||||
// the user is typing.
|
||||
cache_setting: CacheSetting::Only,
|
||||
text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly),
|
||||
maybe_node_modules_path: config_data
|
||||
.and_then(|d| d.node_modules_dir.clone()),
|
||||
// only used for top level install, so we can ignore this
|
||||
package_json_deps_provider: Arc::new(
|
||||
PackageJsonInstallDepsProvider::empty(),
|
||||
),
|
||||
npmrc: config_data
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct ScopeDepInfo {
|
||||
pub deno_types_to_code_resolutions: HashMap<ModuleSpecifier, ModuleSpecifier>,
|
||||
pub npm_reqs: BTreeSet<PackageReq>,
|
||||
pub has_node_specifier: bool,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ResolverFactoryServices {
|
||||
cli_resolver: Deferred<Arc<CliResolver>>,
|
||||
in_npm_pkg_checker: Deferred<Arc<dyn InNpmPackageChecker>>,
|
||||
node_resolver: Deferred<Option<Arc<NodeResolver>>>,
|
||||
npm_pkg_req_resolver: Deferred<Option<Arc<CliNpmReqResolver>>>,
|
||||
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
}
|
||||
|
||||
struct ResolverFactory<'a> {
|
||||
config_data: Option<&'a Arc<ConfigData>>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
pkg_json_resolver: Arc<PackageJsonResolver>,
|
||||
services: ResolverFactoryServices,
|
||||
}
|
||||
|
||||
impl<'a> ResolverFactory<'a> {
|
||||
pub fn new(config_data: Option<&'a Arc<ConfigData>>) -> Self {
|
||||
let fs = Arc::new(deno_fs::RealFs);
|
||||
let pkg_json_resolver = Arc::new(PackageJsonResolver::new(
|
||||
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
|
||||
));
|
||||
Self {
|
||||
config_data,
|
||||
fs,
|
||||
pkg_json_resolver,
|
||||
services: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn init_npm_resolver(
|
||||
&mut self,
|
||||
http_client_provider: &Arc<HttpClientProvider>,
|
||||
cache: &LspCache,
|
||||
) {
|
||||
let enable_byonm = self.config_data.map(|d| d.byonm).unwrap_or(false);
|
||||
let options = if enable_byonm {
|
||||
CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions {
|
||||
fs: CliDenoResolverFs(Arc::new(deno_fs::RealFs)),
|
||||
pkg_json_resolver: self.pkg_json_resolver.clone(),
|
||||
root_node_modules_dir: self.config_data.and_then(|config_data| {
|
||||
config_data.node_modules_dir.clone().or_else(|| {
|
||||
url_to_file_path(&config_data.scope)
|
||||
.ok()
|
||||
.map(|p| p.join("node_modules/"))
|
||||
})
|
||||
}),
|
||||
})
|
||||
} else {
|
||||
let npmrc = self
|
||||
.config_data
|
||||
.and_then(|d| d.npmrc.clone())
|
||||
.unwrap_or_else(create_default_npmrc),
|
||||
npm_system_info: NpmSystemInfo::default(),
|
||||
lifecycle_scripts: Default::default(),
|
||||
.unwrap_or_else(create_default_npmrc);
|
||||
let npm_cache_dir = Arc::new(NpmCacheDir::new(
|
||||
&DenoCacheEnvFsAdapter(self.fs.as_ref()),
|
||||
cache.deno_dir().npm_folder_path(),
|
||||
npmrc.get_all_known_registries_urls(),
|
||||
));
|
||||
CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions {
|
||||
http_client_provider: http_client_provider.clone(),
|
||||
snapshot: match self.config_data.and_then(|d| d.lockfile.as_ref()) {
|
||||
Some(lockfile) => {
|
||||
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
|
||||
lockfile.clone(),
|
||||
)
|
||||
}
|
||||
None => CliNpmResolverManagedSnapshotOption::Specified(None),
|
||||
},
|
||||
// Don't provide the lockfile. We don't want these resolvers
|
||||
// updating it. Only the cache request should update the lockfile.
|
||||
maybe_lockfile: None,
|
||||
fs: Arc::new(deno_fs::RealFs),
|
||||
npm_cache_dir,
|
||||
// Use an "only" cache setting in order to make the
|
||||
// user do an explicit "cache" command and prevent
|
||||
// the cache from being filled with lots of packages while
|
||||
// the user is typing.
|
||||
cache_setting: CacheSetting::Only,
|
||||
text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly),
|
||||
maybe_node_modules_path: self
|
||||
.config_data
|
||||
.and_then(|d| d.node_modules_dir.clone()),
|
||||
// only used for top level install, so we can ignore this
|
||||
npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()),
|
||||
npmrc,
|
||||
npm_system_info: NpmSystemInfo::default(),
|
||||
lifecycle_scripts: Default::default(),
|
||||
})
|
||||
};
|
||||
self.set_npm_resolver(create_cli_npm_resolver_for_lsp(options).await);
|
||||
}
|
||||
|
||||
pub fn set_npm_resolver(&mut self, npm_resolver: Arc<dyn CliNpmResolver>) {
|
||||
self.services.npm_resolver = Some(npm_resolver);
|
||||
}
|
||||
|
||||
pub fn npm_resolver(&self) -> Option<&Arc<dyn CliNpmResolver>> {
|
||||
self.services.npm_resolver.as_ref()
|
||||
}
|
||||
|
||||
pub fn cli_resolver(&self) -> &Arc<CliResolver> {
|
||||
self.services.cli_resolver.get_or_init(|| {
|
||||
let npm_req_resolver = self.npm_pkg_req_resolver().cloned();
|
||||
let deno_resolver = Arc::new(CliDenoResolver::new(DenoResolverOptions {
|
||||
in_npm_pkg_checker: self.in_npm_pkg_checker().clone(),
|
||||
node_and_req_resolver: match (self.node_resolver(), npm_req_resolver) {
|
||||
(Some(node_resolver), Some(npm_req_resolver)) => {
|
||||
Some(NodeAndNpmReqResolver {
|
||||
node_resolver: node_resolver.clone(),
|
||||
npm_req_resolver,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
sloppy_imports_resolver: self
|
||||
.config_data
|
||||
.and_then(|d| d.sloppy_imports_resolver.clone()),
|
||||
workspace_resolver: self
|
||||
.config_data
|
||||
.map(|d| d.resolver.clone())
|
||||
.unwrap_or_else(|| {
|
||||
Arc::new(WorkspaceResolver::new_raw(
|
||||
// this is fine because this is only used before initialization
|
||||
Arc::new(ModuleSpecifier::parse("file:///").unwrap()),
|
||||
None,
|
||||
Vec::new(),
|
||||
Vec::new(),
|
||||
PackageJsonDepResolution::Disabled,
|
||||
))
|
||||
}),
|
||||
is_byonm: self.config_data.map(|d| d.byonm).unwrap_or(false),
|
||||
maybe_vendor_dir: self.config_data.and_then(|d| d.vendor_dir.as_ref()),
|
||||
}));
|
||||
Arc::new(CliResolver::new(CliResolverOptions {
|
||||
deno_resolver,
|
||||
npm_resolver: self.npm_resolver().cloned(),
|
||||
bare_node_builtins_enabled: self
|
||||
.config_data
|
||||
.is_some_and(|d| d.unstable.contains("bare-node-builtins")),
|
||||
}))
|
||||
})
|
||||
};
|
||||
Some(create_cli_npm_resolver_for_lsp(options).await)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_node_resolver(
|
||||
npm_resolver: Option<&Arc<dyn CliNpmResolver>>,
|
||||
) -> Option<Arc<CliNodeResolver>> {
|
||||
use once_cell::sync::Lazy;
|
||||
pub fn pkg_json_resolver(&self) -> &Arc<PackageJsonResolver> {
|
||||
&self.pkg_json_resolver
|
||||
}
|
||||
|
||||
// it's not ideal to share this across all scopes and to
|
||||
// never clear it, but it's fine for the time being
|
||||
static CJS_RESOLUTIONS: Lazy<Arc<CjsResolutionStore>> =
|
||||
Lazy::new(Default::default);
|
||||
pub fn in_npm_pkg_checker(&self) -> &Arc<dyn InNpmPackageChecker> {
|
||||
self.services.in_npm_pkg_checker.get_or_init(|| {
|
||||
crate::npm::create_in_npm_pkg_checker(
|
||||
match self.services.npm_resolver.as_ref().map(|r| r.as_inner()) {
|
||||
Some(crate::npm::InnerCliNpmResolverRef::Byonm(_)) | None => {
|
||||
CreateInNpmPkgCheckerOptions::Byonm
|
||||
}
|
||||
Some(crate::npm::InnerCliNpmResolverRef::Managed(m)) => {
|
||||
CreateInNpmPkgCheckerOptions::Managed(
|
||||
CliManagedInNpmPkgCheckerCreateOptions {
|
||||
root_cache_dir_url: m.global_cache_root_url(),
|
||||
maybe_node_modules_path: m.maybe_node_modules_path(),
|
||||
},
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
let npm_resolver = npm_resolver?;
|
||||
let fs = Arc::new(deno_fs::RealFs);
|
||||
let node_resolver_inner = Arc::new(NodeResolver::new(
|
||||
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
|
||||
npm_resolver.clone().into_npm_resolver(),
|
||||
));
|
||||
Some(Arc::new(CliNodeResolver::new(
|
||||
CJS_RESOLUTIONS.clone(),
|
||||
fs,
|
||||
node_resolver_inner,
|
||||
npm_resolver.clone(),
|
||||
)))
|
||||
}
|
||||
pub fn node_resolver(&self) -> Option<&Arc<NodeResolver>> {
|
||||
self
|
||||
.services
|
||||
.node_resolver
|
||||
.get_or_init(|| {
|
||||
let npm_resolver = self.services.npm_resolver.as_ref()?;
|
||||
Some(Arc::new(NodeResolver::new(
|
||||
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(self.fs.clone()),
|
||||
self.in_npm_pkg_checker().clone(),
|
||||
npm_resolver.clone().into_npm_pkg_folder_resolver(),
|
||||
self.pkg_json_resolver.clone(),
|
||||
)))
|
||||
})
|
||||
.as_ref()
|
||||
}
|
||||
|
||||
fn create_graph_resolver(
|
||||
config_data: Option<&ConfigData>,
|
||||
npm_resolver: Option<&Arc<dyn CliNpmResolver>>,
|
||||
node_resolver: Option<&Arc<CliNodeResolver>>,
|
||||
) -> Arc<CliGraphResolver> {
|
||||
let workspace = config_data.map(|d| &d.member_dir.workspace);
|
||||
Arc::new(CliGraphResolver::new(CliGraphResolverOptions {
|
||||
node_resolver: node_resolver.cloned(),
|
||||
npm_resolver: npm_resolver.cloned(),
|
||||
workspace_resolver: config_data.map(|d| d.resolver.clone()).unwrap_or_else(
|
||||
|| {
|
||||
Arc::new(WorkspaceResolver::new_raw(
|
||||
// this is fine because this is only used before initialization
|
||||
Arc::new(ModuleSpecifier::parse("file:///").unwrap()),
|
||||
None,
|
||||
Vec::new(),
|
||||
PackageJsonDepResolution::Disabled,
|
||||
))
|
||||
},
|
||||
),
|
||||
maybe_jsx_import_source_config: workspace.and_then(|workspace| {
|
||||
workspace.to_maybe_jsx_import_source_config().ok().flatten()
|
||||
}),
|
||||
maybe_vendor_dir: config_data.and_then(|d| d.vendor_dir.as_ref()),
|
||||
bare_node_builtins_enabled: workspace
|
||||
.is_some_and(|workspace| workspace.has_unstable("bare-node-builtins")),
|
||||
sloppy_imports_resolver: config_data
|
||||
.and_then(|d| d.sloppy_imports_resolver.clone()),
|
||||
}))
|
||||
pub fn npm_pkg_req_resolver(&self) -> Option<&Arc<CliNpmReqResolver>> {
|
||||
self
|
||||
.services
|
||||
.npm_pkg_req_resolver
|
||||
.get_or_init(|| {
|
||||
let node_resolver = self.node_resolver()?;
|
||||
let npm_resolver = self.npm_resolver()?;
|
||||
Some(Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions {
|
||||
byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(),
|
||||
fs: CliDenoResolverFs(self.fs.clone()),
|
||||
in_npm_pkg_checker: self.in_npm_pkg_checker().clone(),
|
||||
node_resolver: node_resolver.clone(),
|
||||
npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(),
|
||||
})))
|
||||
})
|
||||
.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
|
@ -562,6 +803,141 @@ impl std::fmt::Debug for RedirectResolver {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LspIsCjsResolver {
|
||||
inner: IsCjsResolver,
|
||||
}
|
||||
|
||||
impl Default for LspIsCjsResolver {
|
||||
fn default() -> Self {
|
||||
LspIsCjsResolver::new(&Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl LspIsCjsResolver {
|
||||
pub fn new(cache: &LspCache) -> Self {
|
||||
#[derive(Debug)]
|
||||
struct LspInNpmPackageChecker {
|
||||
global_cache_dir: ModuleSpecifier,
|
||||
}
|
||||
|
||||
impl LspInNpmPackageChecker {
|
||||
pub fn new(cache: &LspCache) -> Self {
|
||||
let npm_folder_path = cache.deno_dir().npm_folder_path();
|
||||
Self {
|
||||
global_cache_dir: url_from_directory_path(
|
||||
&canonicalize_path_maybe_not_exists(&npm_folder_path)
|
||||
.unwrap_or(npm_folder_path),
|
||||
)
|
||||
.unwrap_or_else(|_| {
|
||||
ModuleSpecifier::parse("file:///invalid/").unwrap()
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InNpmPackageChecker for LspInNpmPackageChecker {
|
||||
fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
if specifier.scheme() != "file" {
|
||||
return false;
|
||||
}
|
||||
if specifier
|
||||
.as_str()
|
||||
.starts_with(self.global_cache_dir.as_str())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
specifier.as_str().contains("/node_modules/")
|
||||
}
|
||||
}
|
||||
|
||||
let fs = Arc::new(deno_fs::RealFs);
|
||||
let pkg_json_resolver = Arc::new(PackageJsonResolver::new(
|
||||
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
|
||||
));
|
||||
|
||||
LspIsCjsResolver {
|
||||
inner: IsCjsResolver::new(
|
||||
Arc::new(LspInNpmPackageChecker::new(cache)),
|
||||
pkg_json_resolver,
|
||||
crate::resolver::IsCjsResolverOptions {
|
||||
detect_cjs: true,
|
||||
is_node_main: false,
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_maybe_doc_module_kind(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_document: Option<&Document>,
|
||||
) -> NodeModuleKind {
|
||||
self.get_lsp_referrer_kind(
|
||||
specifier,
|
||||
maybe_document.and_then(|d| d.is_script()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_doc_module_kind(&self, document: &Document) -> NodeModuleKind {
|
||||
self.get_lsp_referrer_kind(document.specifier(), document.is_script())
|
||||
}
|
||||
|
||||
pub fn get_lsp_referrer_kind(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
is_script: Option<bool>,
|
||||
) -> NodeModuleKind {
|
||||
self.inner.get_lsp_referrer_kind(specifier, is_script)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SingleReferrerGraphResolver<'a> {
|
||||
pub valid_referrer: &'a ModuleSpecifier,
|
||||
pub referrer_kind: NodeModuleKind,
|
||||
pub cli_resolver: &'a CliResolver,
|
||||
pub jsx_import_source_config: Option<&'a JsxImportSourceConfig>,
|
||||
}
|
||||
|
||||
impl<'a> deno_graph::source::Resolver for SingleReferrerGraphResolver<'a> {
|
||||
fn default_jsx_import_source(&self) -> Option<String> {
|
||||
self
|
||||
.jsx_import_source_config
|
||||
.and_then(|c| c.default_specifier.clone())
|
||||
}
|
||||
|
||||
fn default_jsx_import_source_types(&self) -> Option<String> {
|
||||
self
|
||||
.jsx_import_source_config
|
||||
.and_then(|c| c.default_types_specifier.clone())
|
||||
}
|
||||
|
||||
fn jsx_import_source_module(&self) -> &str {
|
||||
self
|
||||
.jsx_import_source_config
|
||||
.map(|c| c.module.as_str())
|
||||
.unwrap_or(deno_graph::source::DEFAULT_JSX_IMPORT_SOURCE_MODULE)
|
||||
}
|
||||
|
||||
fn resolve(
|
||||
&self,
|
||||
specifier_text: &str,
|
||||
referrer_range: &Range,
|
||||
mode: ResolutionMode,
|
||||
) -> Result<ModuleSpecifier, deno_graph::source::ResolveError> {
|
||||
// this resolver assumes it will only be used with a single referrer
|
||||
// with the provided referrer kind
|
||||
debug_assert_eq!(referrer_range.specifier, *self.valid_referrer);
|
||||
self.cli_resolver.resolve(
|
||||
specifier_text,
|
||||
referrer_range,
|
||||
self.referrer_kind,
|
||||
mode,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl RedirectResolver {
|
||||
fn new(
|
||||
cache: Arc<dyn HttpCache>,
|
||||
|
|
|
@ -147,7 +147,7 @@ fn visit_call_expr(
|
|||
let ast::Prop::KeyValue(key_value_prop) = prop.as_ref() else {
|
||||
continue;
|
||||
};
|
||||
let ast::PropName::Ident(ast::Ident { sym, .. }) =
|
||||
let ast::PropName::Ident(ast::IdentName { sym, .. }) =
|
||||
&key_value_prop.key
|
||||
else {
|
||||
continue;
|
||||
|
@ -187,7 +187,7 @@ fn visit_call_expr(
|
|||
};
|
||||
match prop.as_ref() {
|
||||
ast::Prop::KeyValue(key_value_prop) => {
|
||||
let ast::PropName::Ident(ast::Ident { sym, .. }) =
|
||||
let ast::PropName::Ident(ast::IdentName { sym, .. }) =
|
||||
&key_value_prop.key
|
||||
else {
|
||||
continue;
|
||||
|
@ -206,7 +206,7 @@ fn visit_call_expr(
|
|||
}
|
||||
}
|
||||
ast::Prop::Method(method_prop) => {
|
||||
let ast::PropName::Ident(ast::Ident { sym, .. }) =
|
||||
let ast::PropName::Ident(ast::IdentName { sym, .. }) =
|
||||
&method_prop.key
|
||||
else {
|
||||
continue;
|
||||
|
@ -472,7 +472,7 @@ impl Visit for TestCollector {
|
|||
collector: &mut TestCollector,
|
||||
node: &ast::CallExpr,
|
||||
range: &deno_ast::SourceRange,
|
||||
ns_prop_ident: &ast::Ident,
|
||||
ns_prop_ident: &ast::IdentName,
|
||||
member_expr: &ast::MemberExpr,
|
||||
) {
|
||||
if ns_prop_ident.sym == "test" {
|
||||
|
@ -650,7 +650,7 @@ pub mod tests {
|
|||
.unwrap();
|
||||
let text_info = parsed_module.text_info_lazy().clone();
|
||||
let mut collector = TestCollector::new(specifier, text_info);
|
||||
parsed_module.module().visit_with(&mut collector);
|
||||
parsed_module.program().visit_with(&mut collector);
|
||||
collector.take()
|
||||
}
|
||||
|
||||
|
|
|
@ -5,10 +5,12 @@ use super::lsp_custom::TestData;
|
|||
|
||||
use crate::lsp::client::TestingNotification;
|
||||
use crate::lsp::logging::lsp_warn;
|
||||
use crate::lsp::urls::url_to_uri;
|
||||
use crate::tools::test::TestDescription;
|
||||
use crate::tools::test::TestStepDescription;
|
||||
use crate::util::checksum;
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use lsp::Range;
|
||||
use std::collections::HashMap;
|
||||
|
@ -143,21 +145,23 @@ impl TestModule {
|
|||
pub fn as_replace_notification(
|
||||
&self,
|
||||
maybe_root_uri: Option<&ModuleSpecifier>,
|
||||
) -> TestingNotification {
|
||||
) -> Result<TestingNotification, AnyError> {
|
||||
let label = self.label(maybe_root_uri);
|
||||
TestingNotification::Module(lsp_custom::TestModuleNotificationParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: self.specifier.clone(),
|
||||
Ok(TestingNotification::Module(
|
||||
lsp_custom::TestModuleNotificationParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(&self.specifier)?,
|
||||
},
|
||||
kind: lsp_custom::TestModuleNotificationKind::Replace,
|
||||
label,
|
||||
tests: self
|
||||
.defs
|
||||
.iter()
|
||||
.filter(|(_, def)| def.parent_id.is_none())
|
||||
.map(|(id, _)| self.get_test_data(id))
|
||||
.collect(),
|
||||
},
|
||||
kind: lsp_custom::TestModuleNotificationKind::Replace,
|
||||
label,
|
||||
tests: self
|
||||
.defs
|
||||
.iter()
|
||||
.filter(|(_, def)| def.parent_id.is_none())
|
||||
.map(|(id, _)| self.get_test_data(id))
|
||||
.collect(),
|
||||
})
|
||||
))
|
||||
}
|
||||
|
||||
pub fn label(&self, maybe_root_uri: Option<&ModuleSpecifier>) -> String {
|
||||
|
|
|
@ -12,9 +12,13 @@ use crate::lsp::client::Client;
|
|||
use crate::lsp::client::TestingNotification;
|
||||
use crate::lsp::config;
|
||||
use crate::lsp::logging::lsp_log;
|
||||
use crate::lsp::urls::uri_parse_unencoded;
|
||||
use crate::lsp::urls::uri_to_url;
|
||||
use crate::lsp::urls::url_to_uri;
|
||||
use crate::tools::test;
|
||||
use crate::tools::test::create_test_event_channel;
|
||||
use crate::tools::test::FailFastTracker;
|
||||
use crate::tools::test::TestFailureFormatOptions;
|
||||
|
||||
use deno_core::anyhow::anyhow;
|
||||
use deno_core::error::AnyError;
|
||||
|
@ -27,8 +31,10 @@ use deno_core::unsync::spawn;
|
|||
use deno_core::unsync::spawn_blocking;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_runtime::deno_permissions::Permissions;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use deno_runtime::tokio_util::create_and_run_current_thread;
|
||||
use indexmap::IndexMap;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::num::NonZeroUsize;
|
||||
|
@ -52,12 +58,12 @@ fn as_queue_and_filters(
|
|||
|
||||
if let Some(include) = ¶ms.include {
|
||||
for item in include {
|
||||
if let Some((test_definitions, _)) = tests.get(&item.text_document.uri) {
|
||||
queue.insert(item.text_document.uri.clone());
|
||||
let url = uri_to_url(&item.text_document.uri);
|
||||
if let Some((test_definitions, _)) = tests.get(&url) {
|
||||
queue.insert(url.clone());
|
||||
if let Some(id) = &item.id {
|
||||
if let Some(test) = test_definitions.get(id) {
|
||||
let filter =
|
||||
filters.entry(item.text_document.uri.clone()).or_default();
|
||||
let filter = filters.entry(url).or_default();
|
||||
if let Some(include) = filter.include.as_mut() {
|
||||
include.insert(test.id.clone(), test.clone());
|
||||
} else {
|
||||
|
@ -74,19 +80,19 @@ fn as_queue_and_filters(
|
|||
}
|
||||
|
||||
for item in ¶ms.exclude {
|
||||
if let Some((test_definitions, _)) = tests.get(&item.text_document.uri) {
|
||||
let url = uri_to_url(&item.text_document.uri);
|
||||
if let Some((test_definitions, _)) = tests.get(&url) {
|
||||
if let Some(id) = &item.id {
|
||||
// there is no way to exclude a test step
|
||||
if item.step_id.is_none() {
|
||||
if let Some(test) = test_definitions.get(id) {
|
||||
let filter =
|
||||
filters.entry(item.text_document.uri.clone()).or_default();
|
||||
let filter = filters.entry(url.clone()).or_default();
|
||||
filter.exclude.insert(test.id.clone(), test.clone());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// the entire test module is excluded
|
||||
queue.remove(&item.text_document.uri);
|
||||
queue.remove(&url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +187,7 @@ impl TestRun {
|
|||
self
|
||||
.queue
|
||||
.iter()
|
||||
.map(|s| {
|
||||
.filter_map(|s| {
|
||||
let ids = if let Some((test_module, _)) = tests.get(s) {
|
||||
if let Some(filter) = self.filters.get(s) {
|
||||
filter.as_ids(test_module)
|
||||
|
@ -191,10 +197,12 @@ impl TestRun {
|
|||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
lsp_custom::EnqueuedTestModule {
|
||||
text_document: lsp::TextDocumentIdentifier { uri: s.clone() },
|
||||
Some(lsp_custom::EnqueuedTestModule {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(s).ok()?,
|
||||
},
|
||||
ids,
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
@ -212,26 +220,23 @@ impl TestRun {
|
|||
) -> Result<(), AnyError> {
|
||||
let args = self.get_args();
|
||||
lsp_log!("Executing test run with arguments: {}", args.join(" "));
|
||||
let flags =
|
||||
Arc::new(flags_from_vec(args.into_iter().map(From::from).collect())?);
|
||||
let flags = Arc::new(flags_from_vec(
|
||||
args.into_iter().map(|s| From::from(s.as_ref())).collect(),
|
||||
)?);
|
||||
let factory = CliFactory::from_flags(flags);
|
||||
let cli_options = factory.cli_options()?;
|
||||
// Various test files should not share the same permissions in terms of
|
||||
// `PermissionsContainer` - otherwise granting/revoking permissions in one
|
||||
// file would have impact on other files, which is undesirable.
|
||||
let permissions =
|
||||
Permissions::from_options(&cli_options.permissions_options()?)?;
|
||||
let permission_desc_parser = factory.permission_desc_parser()?.clone();
|
||||
let permissions = Permissions::from_options(
|
||||
permission_desc_parser.as_ref(),
|
||||
&cli_options.permissions_options(),
|
||||
)?;
|
||||
let main_graph_container = factory.main_module_graph_container().await?;
|
||||
test::check_specifiers(
|
||||
factory.file_fetcher()?,
|
||||
main_graph_container,
|
||||
self
|
||||
.queue
|
||||
.iter()
|
||||
.map(|s| (s.clone(), test::TestMode::Executable))
|
||||
.collect(),
|
||||
)
|
||||
.await?;
|
||||
main_graph_container
|
||||
.check_specifiers(&self.queue.iter().cloned().collect::<Vec<_>>(), None)
|
||||
.await?;
|
||||
|
||||
let (concurrent_jobs, fail_fast) =
|
||||
if let DenoSubcommand::Test(test_flags) = cli_options.sub_command() {
|
||||
|
@ -268,7 +273,10 @@ impl TestRun {
|
|||
let join_handles = queue.into_iter().map(move |specifier| {
|
||||
let specifier = specifier.clone();
|
||||
let worker_factory = worker_factory.clone();
|
||||
let permissions = permissions.clone();
|
||||
let permissions_container = PermissionsContainer::new(
|
||||
permission_desc_parser.clone(),
|
||||
permissions.clone(),
|
||||
);
|
||||
let worker_sender = test_event_sender_factory.worker();
|
||||
let fail_fast_tracker = fail_fast_tracker.clone();
|
||||
let lsp_filter = self.filters.get(&specifier);
|
||||
|
@ -297,7 +305,7 @@ impl TestRun {
|
|||
// channel.
|
||||
create_and_run_current_thread(test::test_specifier(
|
||||
worker_factory,
|
||||
permissions,
|
||||
permissions_container,
|
||||
specifier,
|
||||
worker_sender,
|
||||
fail_fast_tracker,
|
||||
|
@ -445,37 +453,42 @@ impl TestRun {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_args(&self) -> Vec<&str> {
|
||||
let mut args = vec!["deno", "test"];
|
||||
fn get_args(&self) -> Vec<Cow<str>> {
|
||||
let mut args = vec![Cow::Borrowed("deno"), Cow::Borrowed("test")];
|
||||
args.extend(
|
||||
self
|
||||
.workspace_settings
|
||||
.testing
|
||||
.args
|
||||
.iter()
|
||||
.map(|s| s.as_str()),
|
||||
.map(|s| Cow::Borrowed(s.as_str())),
|
||||
);
|
||||
args.push("--trace-leaks");
|
||||
if self.workspace_settings.unstable && !args.contains(&"--unstable") {
|
||||
args.push("--unstable");
|
||||
args.push(Cow::Borrowed("--trace-leaks"));
|
||||
for unstable_feature in self.workspace_settings.unstable.as_deref() {
|
||||
let flag = format!("--unstable-{unstable_feature}");
|
||||
if !args.contains(&Cow::Borrowed(&flag)) {
|
||||
args.push(Cow::Owned(flag));
|
||||
}
|
||||
}
|
||||
if let Some(config) = &self.workspace_settings.config {
|
||||
if !args.contains(&"--config") && !args.contains(&"-c") {
|
||||
args.push("--config");
|
||||
args.push(config.as_str());
|
||||
if !args.contains(&Cow::Borrowed("--config"))
|
||||
&& !args.contains(&Cow::Borrowed("-c"))
|
||||
{
|
||||
args.push(Cow::Borrowed("--config"));
|
||||
args.push(Cow::Borrowed(config.as_str()));
|
||||
}
|
||||
}
|
||||
if let Some(import_map) = &self.workspace_settings.import_map {
|
||||
if !args.contains(&"--import-map") {
|
||||
args.push("--import-map");
|
||||
args.push(import_map.as_str());
|
||||
if !args.contains(&Cow::Borrowed("--import-map")) {
|
||||
args.push(Cow::Borrowed("--import-map"));
|
||||
args.push(Cow::Borrowed(import_map.as_str()));
|
||||
}
|
||||
}
|
||||
if self.kind == lsp_custom::TestRunKind::Debug
|
||||
&& !args.contains(&"--inspect")
|
||||
&& !args.contains(&"--inspect-brk")
|
||||
&& !args.contains(&Cow::Borrowed("--inspect"))
|
||||
&& !args.contains(&Cow::Borrowed("--inspect-brk"))
|
||||
{
|
||||
args.push("--inspect");
|
||||
args.push(Cow::Borrowed("--inspect"));
|
||||
}
|
||||
args
|
||||
}
|
||||
|
@ -522,7 +535,7 @@ impl LspTestDescription {
|
|||
&self,
|
||||
tests: &IndexMap<usize, LspTestDescription>,
|
||||
) -> lsp_custom::TestIdentifier {
|
||||
let uri = ModuleSpecifier::parse(&self.location().file_name).unwrap();
|
||||
let uri = uri_parse_unencoded(&self.location().file_name).unwrap();
|
||||
let static_id = self.static_id();
|
||||
let mut root_desc = self;
|
||||
while let Some(parent_id) = root_desc.parent_id() {
|
||||
|
@ -586,6 +599,9 @@ impl LspTestReporter {
|
|||
let (test_module, _) = files
|
||||
.entry(specifier.clone())
|
||||
.or_insert_with(|| (TestModule::new(specifier), "1".to_string()));
|
||||
let Ok(uri) = url_to_uri(&test_module.specifier) else {
|
||||
return;
|
||||
};
|
||||
let (static_id, is_new) = test_module.register_dynamic(desc);
|
||||
self.tests.insert(
|
||||
desc.id,
|
||||
|
@ -596,9 +612,7 @@ impl LspTestReporter {
|
|||
.client
|
||||
.send_test_notification(TestingNotification::Module(
|
||||
lsp_custom::TestModuleNotificationParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: test_module.specifier.clone(),
|
||||
},
|
||||
text_document: lsp::TextDocumentIdentifier { uri },
|
||||
kind: lsp_custom::TestModuleNotificationKind::Insert,
|
||||
label: test_module.label(self.maybe_root_uri.as_ref()),
|
||||
tests: vec![test_module.get_test_data(&static_id)],
|
||||
|
@ -655,7 +669,10 @@ impl LspTestReporter {
|
|||
let desc = self.tests.get(&desc.id).unwrap();
|
||||
self.progress(lsp_custom::TestRunProgressMessage::Failed {
|
||||
test: desc.as_test_identifier(&self.tests),
|
||||
messages: as_test_messages(failure.to_string(), false),
|
||||
messages: as_test_messages(
|
||||
failure.format(&TestFailureFormatOptions::default()),
|
||||
false,
|
||||
),
|
||||
duration: Some(elapsed as u32),
|
||||
})
|
||||
}
|
||||
|
@ -675,7 +692,7 @@ impl LspTestReporter {
|
|||
let err_string = format!(
|
||||
"Uncaught error from {}: {}\nThis error was not caught from a test and caused the test runner to fail on the referenced module.\nIt most likely originated from a dangling promise, event/timeout handler or top-level code.",
|
||||
origin,
|
||||
test::fmt::format_test_error(js_error)
|
||||
test::fmt::format_test_error(js_error, &TestFailureFormatOptions::default())
|
||||
);
|
||||
let messages = as_test_messages(err_string, false);
|
||||
for desc in self.tests.values().filter(|d| d.origin() == origin) {
|
||||
|
@ -693,6 +710,9 @@ impl LspTestReporter {
|
|||
let (test_module, _) = files
|
||||
.entry(specifier.clone())
|
||||
.or_insert_with(|| (TestModule::new(specifier), "1".to_string()));
|
||||
let Ok(uri) = url_to_uri(&test_module.specifier) else {
|
||||
return;
|
||||
};
|
||||
let (static_id, is_new) = test_module.register_step_dynamic(
|
||||
desc,
|
||||
self.tests.get(&desc.parent_id).unwrap().static_id(),
|
||||
|
@ -706,9 +726,7 @@ impl LspTestReporter {
|
|||
.client
|
||||
.send_test_notification(TestingNotification::Module(
|
||||
lsp_custom::TestModuleNotificationParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: test_module.specifier.clone(),
|
||||
},
|
||||
text_document: lsp::TextDocumentIdentifier { uri },
|
||||
kind: lsp_custom::TestModuleNotificationKind::Insert,
|
||||
label: test_module.label(self.maybe_root_uri.as_ref()),
|
||||
tests: vec![test_module.get_test_data(&static_id)],
|
||||
|
@ -751,7 +769,10 @@ impl LspTestReporter {
|
|||
test::TestStepResult::Failed(failure) => {
|
||||
self.progress(lsp_custom::TestRunProgressMessage::Failed {
|
||||
test: desc.as_test_identifier(&self.tests),
|
||||
messages: as_test_messages(failure.to_string(), false),
|
||||
messages: as_test_messages(
|
||||
failure.format(&TestFailureFormatOptions::default()),
|
||||
false,
|
||||
),
|
||||
duration: Some(elapsed as u32),
|
||||
})
|
||||
}
|
||||
|
@ -789,14 +810,14 @@ mod tests {
|
|||
include: Some(vec![
|
||||
lsp_custom::TestIdentifier {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: specifier.clone(),
|
||||
uri: url_to_uri(&specifier).unwrap(),
|
||||
},
|
||||
id: None,
|
||||
step_id: None,
|
||||
},
|
||||
lsp_custom::TestIdentifier {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: non_test_specifier.clone(),
|
||||
uri: url_to_uri(&non_test_specifier).unwrap(),
|
||||
},
|
||||
id: None,
|
||||
step_id: None,
|
||||
|
@ -804,7 +825,7 @@ mod tests {
|
|||
]),
|
||||
exclude: vec![lsp_custom::TestIdentifier {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: specifier.clone(),
|
||||
uri: url_to_uri(&specifier).unwrap(),
|
||||
},
|
||||
id: Some(
|
||||
"69d9fe87f64f5b66cb8b631d4fd2064e8224b8715a049be54276c42189ff8f9f"
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::lsp::config;
|
|||
use crate::lsp::documents::DocumentsFilter;
|
||||
use crate::lsp::language_server::StateSnapshot;
|
||||
use crate::lsp::performance::Performance;
|
||||
use crate::lsp::urls::url_to_uri;
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
|
@ -26,12 +27,16 @@ use tower_lsp::jsonrpc::Error as LspError;
|
|||
use tower_lsp::jsonrpc::Result as LspResult;
|
||||
use tower_lsp::lsp_types as lsp;
|
||||
|
||||
fn as_delete_notification(uri: ModuleSpecifier) -> TestingNotification {
|
||||
TestingNotification::DeleteModule(
|
||||
fn as_delete_notification(
|
||||
url: &ModuleSpecifier,
|
||||
) -> Result<TestingNotification, AnyError> {
|
||||
Ok(TestingNotification::DeleteModule(
|
||||
lsp_custom::TestModuleDeleteNotificationParams {
|
||||
text_document: lsp::TextDocumentIdentifier { uri },
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(url)?,
|
||||
},
|
||||
},
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
pub type TestServerTests =
|
||||
|
@ -123,20 +128,24 @@ impl TestServer {
|
|||
.map(|tm| tm.as_ref().clone())
|
||||
.unwrap_or_else(|| TestModule::new(specifier.clone()));
|
||||
if !test_module.is_empty() {
|
||||
client.send_test_notification(
|
||||
test_module.as_replace_notification(mru.as_ref()),
|
||||
);
|
||||
if let Ok(params) =
|
||||
test_module.as_replace_notification(mru.as_ref())
|
||||
{
|
||||
client.send_test_notification(params);
|
||||
}
|
||||
} else if !was_empty {
|
||||
client.send_test_notification(as_delete_notification(
|
||||
specifier.clone(),
|
||||
));
|
||||
if let Ok(params) = as_delete_notification(specifier) {
|
||||
client.send_test_notification(params);
|
||||
}
|
||||
}
|
||||
tests
|
||||
.insert(specifier.clone(), (test_module, script_version));
|
||||
}
|
||||
}
|
||||
for key in keys {
|
||||
client.send_test_notification(as_delete_notification(key));
|
||||
for key in &keys {
|
||||
if let Ok(params) = as_delete_notification(key) {
|
||||
client.send_test_notification(params);
|
||||
}
|
||||
}
|
||||
performance.measure(mark);
|
||||
}
|
||||
|
|
403
cli/lsp/tsc.rs
403
cli/lsp/tsc.rs
|
@ -19,8 +19,10 @@ use super::refactor::EXTRACT_TYPE;
|
|||
use super::semantic_tokens;
|
||||
use super::semantic_tokens::SemanticTokensBuilder;
|
||||
use super::text::LineIndex;
|
||||
use super::urls::LspClientUrl;
|
||||
use super::urls::uri_to_url;
|
||||
use super::urls::url_to_uri;
|
||||
use super::urls::INVALID_SPECIFIER;
|
||||
use super::urls::INVALID_URI;
|
||||
|
||||
use crate::args::jsr_url;
|
||||
use crate::args::FmtOptionsConfig;
|
||||
|
@ -32,12 +34,12 @@ use crate::util::path::relative_specifier;
|
|||
use crate::util::path::to_percent_decoded_str;
|
||||
use crate::util::result::InfallibleResultExt;
|
||||
use crate::util::v8::convert;
|
||||
use crate::worker::create_isolate_create_params;
|
||||
use deno_core::convert::Smi;
|
||||
use deno_core::convert::ToV8;
|
||||
use deno_core::error::StdAnyError;
|
||||
use deno_core::futures::stream::FuturesOrdered;
|
||||
use deno_core::futures::StreamExt;
|
||||
use deno_runtime::fs_util::specifier_to_file_path;
|
||||
|
||||
use dashmap::DashMap;
|
||||
use deno_ast::MediaType;
|
||||
|
@ -61,12 +63,14 @@ use deno_core::ModuleSpecifier;
|
|||
use deno_core::OpState;
|
||||
use deno_core::PollEventLoopOptions;
|
||||
use deno_core::RuntimeOptions;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_runtime::inspector_server::InspectorServer;
|
||||
use deno_runtime::tokio_util::create_basic_runtime;
|
||||
use indexmap::IndexMap;
|
||||
use indexmap::IndexSet;
|
||||
use lazy_regex::lazy_regex;
|
||||
use log::error;
|
||||
use node_resolver::NodeModuleKind;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Captures;
|
||||
use regex::Regex;
|
||||
|
@ -215,6 +219,8 @@ pub enum SemicolonPreference {
|
|||
Remove,
|
||||
}
|
||||
|
||||
// Allow due to false positive https://github.com/rust-lang/rust-clippy/issues/13170
|
||||
#[allow(clippy::needless_borrows_for_generic_args)]
|
||||
fn normalize_diagnostic(
|
||||
diagnostic: &mut crate::tsc::Diagnostic,
|
||||
specifier_map: &TscSpecifierMap,
|
||||
|
@ -232,7 +238,7 @@ pub struct TsServer {
|
|||
performance: Arc<Performance>,
|
||||
sender: mpsc::UnboundedSender<Request>,
|
||||
receiver: Mutex<Option<mpsc::UnboundedReceiver<Request>>>,
|
||||
specifier_map: Arc<TscSpecifierMap>,
|
||||
pub specifier_map: Arc<TscSpecifierMap>,
|
||||
inspector_server: Mutex<Option<Arc<InspectorServer>>>,
|
||||
pending_change: Mutex<Option<PendingChange>>,
|
||||
}
|
||||
|
@ -878,20 +884,22 @@ impl TsServer {
|
|||
options: GetCompletionsAtPositionOptions,
|
||||
format_code_settings: FormatCodeSettings,
|
||||
scope: Option<ModuleSpecifier>,
|
||||
) -> Option<CompletionInfo> {
|
||||
) -> Result<Option<CompletionInfo>, AnyError> {
|
||||
let req = TscRequest::GetCompletionsAtPosition(Box::new((
|
||||
self.specifier_map.denormalize(&specifier),
|
||||
position,
|
||||
options,
|
||||
format_code_settings,
|
||||
)));
|
||||
match self.request(snapshot, req, scope).await {
|
||||
Ok(maybe_info) => maybe_info,
|
||||
Err(err) => {
|
||||
log::error!("Unable to get completion info from TypeScript: {:#}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
self
|
||||
.request::<Option<CompletionInfo>>(snapshot, req, scope)
|
||||
.await
|
||||
.map(|mut info| {
|
||||
if let Some(info) = &mut info {
|
||||
info.normalize(&self.specifier_map);
|
||||
}
|
||||
info
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_completion_details(
|
||||
|
@ -2041,12 +2049,10 @@ impl DocumentSpan {
|
|||
let target_asset_or_doc =
|
||||
language_server.get_maybe_asset_or_document(&target_specifier)?;
|
||||
let target_line_index = target_asset_or_doc.line_index();
|
||||
let file_referrer = language_server
|
||||
.documents
|
||||
.get_file_referrer(&target_specifier);
|
||||
let file_referrer = target_asset_or_doc.file_referrer();
|
||||
let target_uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&target_specifier, file_referrer.as_deref())
|
||||
.specifier_to_uri(&target_specifier, file_referrer)
|
||||
.ok()?;
|
||||
let (target_range, target_selection_range) =
|
||||
if let Some(context_span) = &self.context_span {
|
||||
|
@ -2071,7 +2077,7 @@ impl DocumentSpan {
|
|||
};
|
||||
let link = lsp::LocationLink {
|
||||
origin_selection_range,
|
||||
target_uri: target_uri.into_url(),
|
||||
target_uri,
|
||||
target_range,
|
||||
target_selection_range,
|
||||
};
|
||||
|
@ -2090,12 +2096,12 @@ impl DocumentSpan {
|
|||
language_server.get_maybe_asset_or_document(&specifier)?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
let range = self.text_span.to_range(line_index);
|
||||
let file_referrer = language_server.documents.get_file_referrer(&specifier);
|
||||
let mut target = language_server
|
||||
let file_referrer = asset_or_doc.file_referrer();
|
||||
let target_uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&specifier, file_referrer.as_deref())
|
||||
.ok()?
|
||||
.into_url();
|
||||
.specifier_to_uri(&specifier, file_referrer)
|
||||
.ok()?;
|
||||
let mut target = uri_to_url(&target_uri);
|
||||
target.set_fragment(Some(&format!(
|
||||
"L{},{}",
|
||||
range.start.line + 1,
|
||||
|
@ -2151,16 +2157,13 @@ impl NavigateToItem {
|
|||
let asset_or_doc =
|
||||
language_server.get_asset_or_document(&specifier).ok()?;
|
||||
let line_index = asset_or_doc.line_index();
|
||||
let file_referrer = language_server.documents.get_file_referrer(&specifier);
|
||||
let file_referrer = asset_or_doc.file_referrer();
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&specifier, file_referrer.as_deref())
|
||||
.specifier_to_uri(&specifier, file_referrer)
|
||||
.ok()?;
|
||||
let range = self.text_span.to_range(line_index);
|
||||
let location = lsp::Location {
|
||||
uri: uri.into_url(),
|
||||
range,
|
||||
};
|
||||
let location = lsp::Location { uri, range };
|
||||
|
||||
let mut tags: Option<Vec<lsp::SymbolTag>> = None;
|
||||
let kind_modifiers = parse_kind_modifier(&self.kind_modifiers);
|
||||
|
@ -2183,6 +2186,50 @@ impl NavigateToItem {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InlayHintDisplayPart {
|
||||
pub text: String,
|
||||
pub span: Option<TextSpan>,
|
||||
pub file: Option<String>,
|
||||
}
|
||||
|
||||
impl InlayHintDisplayPart {
|
||||
pub fn to_lsp(
|
||||
&self,
|
||||
language_server: &language_server::Inner,
|
||||
) -> lsp::InlayHintLabelPart {
|
||||
let location = self.file.as_ref().map(|f| {
|
||||
let specifier =
|
||||
resolve_url(f).unwrap_or_else(|_| INVALID_SPECIFIER.clone());
|
||||
let file_referrer =
|
||||
language_server.documents.get_file_referrer(&specifier);
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.specifier_to_uri(&specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| INVALID_URI.clone());
|
||||
let range = self
|
||||
.span
|
||||
.as_ref()
|
||||
.and_then(|s| {
|
||||
let asset_or_doc =
|
||||
language_server.get_asset_or_document(&specifier).ok()?;
|
||||
Some(s.to_range(asset_or_doc.line_index()))
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0))
|
||||
});
|
||||
lsp::Location { uri, range }
|
||||
});
|
||||
lsp::InlayHintLabelPart {
|
||||
value: self.text.clone(),
|
||||
tooltip: None,
|
||||
location,
|
||||
command: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub enum InlayHintKind {
|
||||
Type,
|
||||
|
@ -2204,6 +2251,7 @@ impl InlayHintKind {
|
|||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InlayHint {
|
||||
pub text: String,
|
||||
pub display_parts: Option<Vec<InlayHintDisplayPart>>,
|
||||
pub position: u32,
|
||||
pub kind: InlayHintKind,
|
||||
pub whitespace_before: Option<bool>,
|
||||
|
@ -2211,10 +2259,23 @@ pub struct InlayHint {
|
|||
}
|
||||
|
||||
impl InlayHint {
|
||||
pub fn to_lsp(&self, line_index: Arc<LineIndex>) -> lsp::InlayHint {
|
||||
pub fn to_lsp(
|
||||
&self,
|
||||
line_index: Arc<LineIndex>,
|
||||
language_server: &language_server::Inner,
|
||||
) -> lsp::InlayHint {
|
||||
lsp::InlayHint {
|
||||
position: line_index.position_tsc(self.position.into()),
|
||||
label: lsp::InlayHintLabel::String(self.text.clone()),
|
||||
label: if let Some(display_parts) = &self.display_parts {
|
||||
lsp::InlayHintLabel::LabelParts(
|
||||
display_parts
|
||||
.iter()
|
||||
.map(|p| p.to_lsp(language_server))
|
||||
.collect(),
|
||||
)
|
||||
} else {
|
||||
lsp::InlayHintLabel::String(self.text.clone())
|
||||
},
|
||||
kind: self.kind.to_lsp(),
|
||||
padding_left: self.whitespace_before,
|
||||
padding_right: self.whitespace_after,
|
||||
|
@ -2413,12 +2474,10 @@ impl ImplementationLocation {
|
|||
let file_referrer = language_server.documents.get_file_referrer(&specifier);
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| {
|
||||
LspClientUrl::new(ModuleSpecifier::parse("deno://invalid").unwrap())
|
||||
});
|
||||
.specifier_to_uri(&specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| INVALID_URI.clone());
|
||||
lsp::Location {
|
||||
uri: uri.into_url(),
|
||||
uri,
|
||||
range: self.document_span.text_span.to_range(line_index),
|
||||
}
|
||||
}
|
||||
|
@ -2474,7 +2533,7 @@ impl RenameLocations {
|
|||
language_server.documents.get_file_referrer(&specifier);
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&specifier, file_referrer.as_deref())?;
|
||||
.specifier_to_uri(&specifier, file_referrer.as_deref())?;
|
||||
let asset_or_doc = language_server.get_asset_or_document(&specifier)?;
|
||||
|
||||
// ensure TextDocumentEdit for `location.file_name`.
|
||||
|
@ -2483,7 +2542,7 @@ impl RenameLocations {
|
|||
uri.clone(),
|
||||
lsp::TextDocumentEdit {
|
||||
text_document: lsp::OptionalVersionedTextDocumentIdentifier {
|
||||
uri: uri.as_url().clone(),
|
||||
uri: uri.clone(),
|
||||
version: asset_or_doc.document_lsp_version(),
|
||||
},
|
||||
edits:
|
||||
|
@ -2685,7 +2744,7 @@ impl FileTextChanges {
|
|||
.collect();
|
||||
Ok(lsp::TextDocumentEdit {
|
||||
text_document: lsp::OptionalVersionedTextDocumentIdentifier {
|
||||
uri: specifier,
|
||||
uri: url_to_uri(&specifier)?,
|
||||
version: asset_or_doc.document_lsp_version(),
|
||||
},
|
||||
edits,
|
||||
|
@ -2712,7 +2771,7 @@ impl FileTextChanges {
|
|||
if self.is_new_file.unwrap_or(false) {
|
||||
ops.push(lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Create(
|
||||
lsp::CreateFile {
|
||||
uri: specifier.clone(),
|
||||
uri: url_to_uri(&specifier)?,
|
||||
options: Some(lsp::CreateFileOptions {
|
||||
ignore_if_exists: Some(true),
|
||||
overwrite: None,
|
||||
|
@ -2729,7 +2788,7 @@ impl FileTextChanges {
|
|||
.collect();
|
||||
ops.push(lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit {
|
||||
text_document: lsp::OptionalVersionedTextDocumentIdentifier {
|
||||
uri: specifier,
|
||||
uri: url_to_uri(&specifier)?,
|
||||
version: maybe_asset_or_document.and_then(|d| d.document_lsp_version()),
|
||||
},
|
||||
edits,
|
||||
|
@ -3127,10 +3186,10 @@ impl ReferenceEntry {
|
|||
let file_referrer = language_server.documents.get_file_referrer(&specifier);
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| LspClientUrl::new(INVALID_SPECIFIER.clone()));
|
||||
.specifier_to_uri(&specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| INVALID_URI.clone());
|
||||
lsp::Location {
|
||||
uri: uri.into_url(),
|
||||
uri,
|
||||
range: self.document_span.text_span.to_range(line_index),
|
||||
}
|
||||
}
|
||||
|
@ -3188,12 +3247,13 @@ impl CallHierarchyItem {
|
|||
.get_file_referrer(&target_specifier);
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.normalize_specifier(&target_specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| LspClientUrl::new(INVALID_SPECIFIER.clone()));
|
||||
.specifier_to_uri(&target_specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| INVALID_URI.clone());
|
||||
|
||||
let use_file_name = self.is_source_file_item();
|
||||
let maybe_file_path = if uri.as_url().scheme() == "file" {
|
||||
specifier_to_file_path(uri.as_url()).ok()
|
||||
let maybe_file_path = if uri.scheme().is_some_and(|s| s.as_str() == "file")
|
||||
{
|
||||
url_to_file_path(&uri_to_url(&uri)).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -3237,7 +3297,7 @@ impl CallHierarchyItem {
|
|||
lsp::CallHierarchyItem {
|
||||
name,
|
||||
tags,
|
||||
uri: uri.into_url(),
|
||||
uri,
|
||||
detail: Some(detail),
|
||||
kind: self.kind.clone().into(),
|
||||
range: self.span.to_range(line_index.clone()),
|
||||
|
@ -3357,9 +3417,18 @@ fn parse_code_actions(
|
|||
additional_text_edits.extend(change.text_changes.iter().map(|tc| {
|
||||
let mut text_edit = tc.as_text_edit(asset_or_doc.line_index());
|
||||
if let Some(specifier_rewrite) = &data.specifier_rewrite {
|
||||
text_edit.new_text = text_edit
|
||||
.new_text
|
||||
.replace(&specifier_rewrite.0, &specifier_rewrite.1);
|
||||
text_edit.new_text = text_edit.new_text.replace(
|
||||
&specifier_rewrite.old_specifier,
|
||||
&specifier_rewrite.new_specifier,
|
||||
);
|
||||
if let Some(deno_types_specifier) =
|
||||
&specifier_rewrite.new_deno_types_specifier
|
||||
{
|
||||
text_edit.new_text = format!(
|
||||
"// @deno-types=\"{}\"\n{}",
|
||||
deno_types_specifier, &text_edit.new_text
|
||||
);
|
||||
}
|
||||
}
|
||||
text_edit
|
||||
}));
|
||||
|
@ -3518,17 +3587,23 @@ impl CompletionEntryDetails {
|
|||
let mut text_edit = original_item.text_edit.clone();
|
||||
if let Some(specifier_rewrite) = &data.specifier_rewrite {
|
||||
if let Some(text_edit) = &mut text_edit {
|
||||
match text_edit {
|
||||
lsp::CompletionTextEdit::Edit(text_edit) => {
|
||||
text_edit.new_text = text_edit
|
||||
.new_text
|
||||
.replace(&specifier_rewrite.0, &specifier_rewrite.1);
|
||||
}
|
||||
let new_text = match text_edit {
|
||||
lsp::CompletionTextEdit::Edit(text_edit) => &mut text_edit.new_text,
|
||||
lsp::CompletionTextEdit::InsertAndReplace(insert_replace_edit) => {
|
||||
insert_replace_edit.new_text = insert_replace_edit
|
||||
.new_text
|
||||
.replace(&specifier_rewrite.0, &specifier_rewrite.1);
|
||||
&mut insert_replace_edit.new_text
|
||||
}
|
||||
};
|
||||
*new_text = new_text.replace(
|
||||
&specifier_rewrite.old_specifier,
|
||||
&specifier_rewrite.new_specifier,
|
||||
);
|
||||
if let Some(deno_types_specifier) =
|
||||
&specifier_rewrite.new_deno_types_specifier
|
||||
{
|
||||
*new_text = format!(
|
||||
"// @deno-types=\"{}\"\n{}",
|
||||
deno_types_specifier, new_text
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3586,6 +3661,12 @@ pub struct CompletionInfo {
|
|||
}
|
||||
|
||||
impl CompletionInfo {
|
||||
fn normalize(&mut self, specifier_map: &TscSpecifierMap) {
|
||||
for entry in &mut self.entries {
|
||||
entry.normalize(specifier_map);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_completion_response(
|
||||
&self,
|
||||
line_index: Arc<LineIndex>,
|
||||
|
@ -3627,6 +3708,13 @@ impl CompletionInfo {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct CompletionSpecifierRewrite {
|
||||
old_specifier: String,
|
||||
new_specifier: String,
|
||||
new_deno_types_specifier: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CompletionItemData {
|
||||
|
@ -3639,7 +3727,7 @@ pub struct CompletionItemData {
|
|||
/// be rewritten by replacing the first string with the second. Intended for
|
||||
/// auto-import specifiers to be reverse-import-mapped.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub specifier_rewrite: Option<(String, String)>,
|
||||
pub specifier_rewrite: Option<CompletionSpecifierRewrite>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub data: Option<Value>,
|
||||
pub use_code_snippet: bool,
|
||||
|
@ -3647,11 +3735,17 @@ pub struct CompletionItemData {
|
|||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct CompletionEntryDataImport {
|
||||
struct CompletionEntryDataAutoImport {
|
||||
module_specifier: String,
|
||||
file_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CompletionNormalizedAutoImportData {
|
||||
raw: CompletionEntryDataAutoImport,
|
||||
normalized: ModuleSpecifier,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CompletionEntry {
|
||||
|
@ -3684,9 +3778,28 @@ pub struct CompletionEntry {
|
|||
is_import_statement_completion: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
data: Option<Value>,
|
||||
/// This is not from tsc, we add it for convenience during normalization.
|
||||
/// Represents `self.data.file_name`, but normalized.
|
||||
#[serde(skip)]
|
||||
auto_import_data: Option<CompletionNormalizedAutoImportData>,
|
||||
}
|
||||
|
||||
impl CompletionEntry {
|
||||
fn normalize(&mut self, specifier_map: &TscSpecifierMap) {
|
||||
let Some(data) = &self.data else {
|
||||
return;
|
||||
};
|
||||
let Ok(raw) =
|
||||
serde_json::from_value::<CompletionEntryDataAutoImport>(data.clone())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if let Ok(normalized) = specifier_map.normalize(&raw.file_name) {
|
||||
self.auto_import_data =
|
||||
Some(CompletionNormalizedAutoImportData { raw, normalized });
|
||||
}
|
||||
}
|
||||
|
||||
fn get_commit_characters(
|
||||
&self,
|
||||
info: &CompletionInfo,
|
||||
|
@ -3835,25 +3948,44 @@ impl CompletionEntry {
|
|||
|
||||
if let Some(source) = &self.source {
|
||||
let mut display_source = source.clone();
|
||||
if let Some(data) = &self.data {
|
||||
if let Ok(import_data) =
|
||||
serde_json::from_value::<CompletionEntryDataImport>(data.clone())
|
||||
if let Some(import_data) = &self.auto_import_data {
|
||||
let import_mapper =
|
||||
language_server.get_ts_response_import_mapper(specifier);
|
||||
if let Some(mut new_specifier) = import_mapper
|
||||
.check_specifier(&import_data.normalized, specifier)
|
||||
.or_else(|| relative_specifier(specifier, &import_data.normalized))
|
||||
{
|
||||
if let Ok(import_specifier) = resolve_url(&import_data.file_name) {
|
||||
if let Some(new_module_specifier) = language_server
|
||||
.get_ts_response_import_mapper(specifier)
|
||||
.check_specifier(&import_specifier, specifier)
|
||||
.or_else(|| relative_specifier(specifier, &import_specifier))
|
||||
{
|
||||
display_source.clone_from(&new_module_specifier);
|
||||
if new_module_specifier != import_data.module_specifier {
|
||||
specifier_rewrite =
|
||||
Some((import_data.module_specifier, new_module_specifier));
|
||||
}
|
||||
} else if source.starts_with(jsr_url().as_str()) {
|
||||
return None;
|
||||
}
|
||||
if new_specifier.contains("/node_modules/") {
|
||||
return None;
|
||||
}
|
||||
let mut new_deno_types_specifier = None;
|
||||
if let Some(code_specifier) = language_server
|
||||
.resolver
|
||||
.deno_types_to_code_resolution(
|
||||
&import_data.normalized,
|
||||
Some(specifier),
|
||||
)
|
||||
.and_then(|s| {
|
||||
import_mapper
|
||||
.check_specifier(&s, specifier)
|
||||
.or_else(|| relative_specifier(specifier, &s))
|
||||
})
|
||||
{
|
||||
new_deno_types_specifier =
|
||||
Some(std::mem::replace(&mut new_specifier, code_specifier));
|
||||
}
|
||||
display_source.clone_from(&new_specifier);
|
||||
if new_specifier != import_data.raw.module_specifier
|
||||
|| new_deno_types_specifier.is_some()
|
||||
{
|
||||
specifier_rewrite = Some(CompletionSpecifierRewrite {
|
||||
old_specifier: import_data.raw.module_specifier.clone(),
|
||||
new_specifier,
|
||||
new_deno_types_specifier,
|
||||
});
|
||||
}
|
||||
} else if source.starts_with(jsr_url().as_str()) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
// We want relative or bare (import-mapped or otherwise) specifiers to
|
||||
|
@ -3941,7 +4073,7 @@ pub struct OutliningSpan {
|
|||
kind: OutliningSpanKind,
|
||||
}
|
||||
|
||||
const FOLD_END_PAIR_CHARACTERS: &[u8] = &[b'}', b']', b')', b'`'];
|
||||
const FOLD_END_PAIR_CHARACTERS: &[u8] = b"}])`";
|
||||
|
||||
impl OutliningSpan {
|
||||
pub fn to_folding_range(
|
||||
|
@ -4156,6 +4288,11 @@ impl TscSpecifierMap {
|
|||
return specifier.to_string();
|
||||
}
|
||||
let mut specifier = original.to_string();
|
||||
if !specifier.contains("/node_modules/@types/node/") {
|
||||
// The ts server doesn't give completions from files in
|
||||
// `node_modules/.deno/`. We work around it like this.
|
||||
specifier = specifier.replace("/node_modules/", "/$node_modules/");
|
||||
}
|
||||
let media_type = MediaType::from_specifier(original);
|
||||
// If the URL-inferred media type doesn't correspond to tsc's path-inferred
|
||||
// media type, force it to be the same by appending an extension.
|
||||
|
@ -4230,14 +4367,10 @@ impl State {
|
|||
}
|
||||
|
||||
fn get_document(&self, specifier: &ModuleSpecifier) -> Option<Arc<Document>> {
|
||||
if let Some(scope) = &self.last_scope {
|
||||
self.state_snapshot.documents.get_or_load(specifier, scope)
|
||||
} else {
|
||||
self
|
||||
.state_snapshot
|
||||
.documents
|
||||
.get_or_load(specifier, &ModuleSpecifier::parse("file:///").unwrap())
|
||||
}
|
||||
self
|
||||
.state_snapshot
|
||||
.documents
|
||||
.get_or_load(specifier, self.last_scope.as_ref())
|
||||
}
|
||||
|
||||
fn get_asset_or_document(
|
||||
|
@ -4277,7 +4410,7 @@ fn op_is_cancelled(state: &mut OpState) -> bool {
|
|||
fn op_is_node_file(state: &mut OpState, #[string] path: String) -> bool {
|
||||
let state = state.borrow::<State>();
|
||||
let mark = state.performance.mark("tsc.op.op_is_node_file");
|
||||
let r = match ModuleSpecifier::parse(&path) {
|
||||
let r = match state.specifier_map.normalize(path) {
|
||||
Ok(specifier) => state.state_snapshot.resolver.in_node_modules(&specifier),
|
||||
Err(_) => false,
|
||||
};
|
||||
|
@ -4314,15 +4447,14 @@ fn op_load<'s>(
|
|||
data: doc.text(),
|
||||
script_kind: crate::tsc::as_ts_script_kind(doc.media_type()),
|
||||
version: state.script_version(&specifier),
|
||||
is_cjs: matches!(
|
||||
doc.media_type(),
|
||||
MediaType::Cjs | MediaType::Cts | MediaType::Dcts
|
||||
),
|
||||
is_cjs: doc
|
||||
.document()
|
||||
.map(|d| state.state_snapshot.is_cjs_resolver.get_doc_module_kind(d))
|
||||
.unwrap_or(NodeModuleKind::Esm)
|
||||
== NodeModuleKind::Cjs,
|
||||
})
|
||||
};
|
||||
|
||||
let serialized = serde_v8::to_v8(scope, maybe_load_response)?;
|
||||
|
||||
state.performance.measure(mark);
|
||||
Ok(serialized)
|
||||
}
|
||||
|
@ -4546,7 +4678,10 @@ fn op_script_names(state: &mut OpState) -> ScriptNames {
|
|||
for doc in &docs {
|
||||
let specifier = doc.specifier();
|
||||
let is_open = doc.is_open();
|
||||
if is_open || specifier.scheme() == "file" {
|
||||
if is_open
|
||||
|| (specifier.scheme() == "file"
|
||||
&& !state.state_snapshot.resolver.in_node_modules(specifier))
|
||||
{
|
||||
let script_names = doc
|
||||
.scope()
|
||||
.and_then(|s| result.by_scope.get_mut(s))
|
||||
|
@ -4557,9 +4692,13 @@ fn op_script_names(state: &mut OpState) -> ScriptNames {
|
|||
let (types, _) = documents.resolve_dependency(
|
||||
types,
|
||||
specifier,
|
||||
state
|
||||
.state_snapshot
|
||||
.is_cjs_resolver
|
||||
.get_doc_module_kind(doc),
|
||||
doc.file_referrer(),
|
||||
)?;
|
||||
let types_doc = documents.get_or_load(&types, specifier)?;
|
||||
let types_doc = documents.get_or_load(&types, doc.file_referrer())?;
|
||||
Some(types_doc.specifier().clone())
|
||||
})();
|
||||
// If there is a types dep, use that as the root instead. But if the doc
|
||||
|
@ -4660,6 +4799,7 @@ fn run_tsc_thread(
|
|||
specifier_map,
|
||||
request_rx,
|
||||
)],
|
||||
create_params: create_isolate_create_params(),
|
||||
startup_snapshot: Some(tsc::compiler_snapshot()),
|
||||
inspector: has_inspector_server,
|
||||
..Default::default()
|
||||
|
@ -4898,6 +5038,10 @@ pub struct UserPreferences {
|
|||
pub allow_rename_of_import_path: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub auto_import_file_exclude_patterns: Option<Vec<String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub interactive_inlay_hints: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub prefer_type_only_auto_imports: Option<bool>,
|
||||
}
|
||||
|
||||
impl UserPreferences {
|
||||
|
@ -4915,6 +5059,7 @@ impl UserPreferences {
|
|||
include_completions_with_snippet_text: Some(
|
||||
config.snippet_support_capable(),
|
||||
),
|
||||
interactive_inlay_hints: Some(true),
|
||||
provide_refactor_not_applicable_reason: Some(true),
|
||||
quote_preference: Some(fmt_config.into()),
|
||||
use_label_details_in_completion_entries: Some(true),
|
||||
|
@ -5019,6 +5164,9 @@ impl UserPreferences {
|
|||
} else {
|
||||
Some(language_settings.preferences.quote_style)
|
||||
},
|
||||
prefer_type_only_auto_imports: Some(
|
||||
language_settings.preferences.prefer_type_only_auto_imports,
|
||||
),
|
||||
..base_preferences
|
||||
}
|
||||
}
|
||||
|
@ -5402,7 +5550,7 @@ mod tests {
|
|||
sources: &[(&str, &str, i32, LanguageId)],
|
||||
) -> (TempDir, TsServer, Arc<StateSnapshot>, LspCache) {
|
||||
let temp_dir = TempDir::new();
|
||||
let cache = LspCache::new(Some(temp_dir.uri().join(".deno_dir").unwrap()));
|
||||
let cache = LspCache::new(Some(temp_dir.url().join(".deno_dir").unwrap()));
|
||||
let mut config = Config::default();
|
||||
config
|
||||
.tree
|
||||
|
@ -5412,7 +5560,7 @@ mod tests {
|
|||
"compilerOptions": ts_config,
|
||||
})
|
||||
.to_string(),
|
||||
temp_dir.uri().join("deno.json").unwrap(),
|
||||
temp_dir.url().join("deno.json").unwrap(),
|
||||
&Default::default(),
|
||||
)
|
||||
.unwrap(),
|
||||
|
@ -5423,7 +5571,7 @@ mod tests {
|
|||
let mut documents = Documents::default();
|
||||
documents.update_config(&config, &resolver, &cache, &Default::default());
|
||||
for (relative_specifier, source, version, language_id) in sources {
|
||||
let specifier = temp_dir.uri().join(relative_specifier).unwrap();
|
||||
let specifier = temp_dir.url().join(relative_specifier).unwrap();
|
||||
documents.open(specifier, *version, *language_id, (*source).into(), None);
|
||||
}
|
||||
let snapshot = Arc::new(StateSnapshot {
|
||||
|
@ -5431,6 +5579,7 @@ mod tests {
|
|||
documents: Arc::new(documents),
|
||||
assets: Default::default(),
|
||||
config: Arc::new(config),
|
||||
is_cjs_resolver: Default::default(),
|
||||
resolver,
|
||||
});
|
||||
let performance = Arc::new(Performance::default());
|
||||
|
@ -5456,7 +5605,7 @@ mod tests {
|
|||
let (_tx, rx) = mpsc::unbounded_channel();
|
||||
let state =
|
||||
State::new(state_snapshot, Default::default(), Default::default(), rx);
|
||||
let mut op_state = OpState::new(None);
|
||||
let mut op_state = OpState::new(None, None);
|
||||
op_state.put(state);
|
||||
op_state
|
||||
}
|
||||
|
@ -5481,7 +5630,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"noEmit": true,
|
||||
"lib": [],
|
||||
}),
|
||||
|
@ -5493,7 +5641,7 @@ mod tests {
|
|||
)],
|
||||
)
|
||||
.await;
|
||||
let specifier = temp_dir.uri().join("a.ts").unwrap();
|
||||
let specifier = temp_dir.url().join("a.ts").unwrap();
|
||||
let diagnostics = ts_server
|
||||
.get_diagnostics(snapshot, vec![specifier.clone()], Default::default())
|
||||
.await
|
||||
|
@ -5527,7 +5675,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"jsx": "react",
|
||||
"lib": ["esnext", "dom", "deno.ns"],
|
||||
"noEmit": true,
|
||||
|
@ -5540,7 +5687,7 @@ mod tests {
|
|||
)],
|
||||
)
|
||||
.await;
|
||||
let specifier = temp_dir.uri().join("a.ts").unwrap();
|
||||
let specifier = temp_dir.url().join("a.ts").unwrap();
|
||||
let diagnostics = ts_server
|
||||
.get_diagnostics(snapshot, vec![specifier.clone()], Default::default())
|
||||
.await
|
||||
|
@ -5553,7 +5700,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -5571,7 +5717,7 @@ mod tests {
|
|||
)],
|
||||
)
|
||||
.await;
|
||||
let specifier = temp_dir.uri().join("a.ts").unwrap();
|
||||
let specifier = temp_dir.url().join("a.ts").unwrap();
|
||||
let diagnostics = ts_server
|
||||
.get_diagnostics(snapshot, vec![specifier.clone()], Default::default())
|
||||
.await
|
||||
|
@ -5584,7 +5730,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -5598,7 +5743,7 @@ mod tests {
|
|||
)],
|
||||
)
|
||||
.await;
|
||||
let specifier = temp_dir.uri().join("a.ts").unwrap();
|
||||
let specifier = temp_dir.url().join("a.ts").unwrap();
|
||||
let diagnostics = ts_server
|
||||
.get_diagnostics(snapshot, vec![specifier.clone()], Default::default())
|
||||
.await
|
||||
|
@ -5630,7 +5775,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -5648,7 +5792,7 @@ mod tests {
|
|||
)],
|
||||
)
|
||||
.await;
|
||||
let specifier = temp_dir.uri().join("a.ts").unwrap();
|
||||
let specifier = temp_dir.url().join("a.ts").unwrap();
|
||||
let diagnostics = ts_server
|
||||
.get_diagnostics(snapshot, vec![specifier.clone()], Default::default())
|
||||
.await
|
||||
|
@ -5661,7 +5805,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -5682,7 +5825,7 @@ mod tests {
|
|||
)],
|
||||
)
|
||||
.await;
|
||||
let specifier = temp_dir.uri().join("a.ts").unwrap();
|
||||
let specifier = temp_dir.url().join("a.ts").unwrap();
|
||||
let diagnostics = ts_server
|
||||
.get_diagnostics(snapshot, vec![specifier.clone()], Default::default())
|
||||
.await
|
||||
|
@ -5728,7 +5871,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -5740,7 +5882,7 @@ mod tests {
|
|||
)],
|
||||
)
|
||||
.await;
|
||||
let specifier = temp_dir.uri().join("a.ts").unwrap();
|
||||
let specifier = temp_dir.url().join("a.ts").unwrap();
|
||||
let diagnostics = ts_server
|
||||
.get_diagnostics(snapshot, vec![specifier.clone()], Default::default())
|
||||
.await
|
||||
|
@ -5806,7 +5948,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, cache) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -5833,7 +5974,7 @@ mod tests {
|
|||
b"export const b = \"b\";\n",
|
||||
)
|
||||
.unwrap();
|
||||
let specifier = temp_dir.uri().join("a.ts").unwrap();
|
||||
let specifier = temp_dir.url().join("a.ts").unwrap();
|
||||
let diagnostics = ts_server
|
||||
.get_diagnostics(
|
||||
snapshot.clone(),
|
||||
|
@ -5883,7 +6024,7 @@ mod tests {
|
|||
[(&specifier_dep, ChangeKind::Opened)],
|
||||
None,
|
||||
);
|
||||
let specifier = temp_dir.uri().join("a.ts").unwrap();
|
||||
let specifier = temp_dir.url().join("a.ts").unwrap();
|
||||
let diagnostics = ts_server
|
||||
.get_diagnostics(
|
||||
snapshot.clone(),
|
||||
|
@ -5948,14 +6089,13 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
&[("a.ts", fixture, 1, LanguageId::TypeScript)],
|
||||
)
|
||||
.await;
|
||||
let specifier = temp_dir.uri().join("a.ts").unwrap();
|
||||
let specifier = temp_dir.url().join("a.ts").unwrap();
|
||||
let info = ts_server
|
||||
.get_completions(
|
||||
snapshot.clone(),
|
||||
|
@ -5970,9 +6110,10 @@ mod tests {
|
|||
trigger_kind: None,
|
||||
},
|
||||
Default::default(),
|
||||
Some(temp_dir.uri()),
|
||||
Some(temp_dir.url()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(info.entries.len(), 22);
|
||||
let details = ts_server
|
||||
|
@ -5987,7 +6128,7 @@ mod tests {
|
|||
preferences: None,
|
||||
data: None,
|
||||
},
|
||||
Some(temp_dir.uri()),
|
||||
Some(temp_dir.url()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
|
@ -6099,7 +6240,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -6109,7 +6249,7 @@ mod tests {
|
|||
],
|
||||
)
|
||||
.await;
|
||||
let specifier = temp_dir.uri().join("a.ts").unwrap();
|
||||
let specifier = temp_dir.url().join("a.ts").unwrap();
|
||||
let fmt_options_config = FmtOptionsConfig {
|
||||
semi_colons: Some(false),
|
||||
single_quote: Some(true),
|
||||
|
@ -6130,9 +6270,10 @@ mod tests {
|
|||
..Default::default()
|
||||
},
|
||||
FormatCodeSettings::from(&fmt_options_config),
|
||||
Some(temp_dir.uri()),
|
||||
Some(temp_dir.url()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let entry = info
|
||||
.entries
|
||||
|
@ -6156,7 +6297,7 @@ mod tests {
|
|||
}),
|
||||
data: entry.data.clone(),
|
||||
},
|
||||
Some(temp_dir.uri()),
|
||||
Some(temp_dir.url()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
|
@ -6208,7 +6349,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -6221,8 +6361,8 @@ mod tests {
|
|||
let changes = ts_server
|
||||
.get_edits_for_file_rename(
|
||||
snapshot,
|
||||
temp_dir.uri().join("b.ts").unwrap(),
|
||||
temp_dir.uri().join("🦕.ts").unwrap(),
|
||||
temp_dir.url().join("b.ts").unwrap(),
|
||||
temp_dir.url().join("🦕.ts").unwrap(),
|
||||
FormatCodeSettings::default(),
|
||||
UserPreferences::default(),
|
||||
)
|
||||
|
@ -6231,7 +6371,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
changes,
|
||||
vec![FileTextChanges {
|
||||
file_name: temp_dir.uri().join("a.ts").unwrap().to_string(),
|
||||
file_name: temp_dir.url().join("a.ts").unwrap().to_string(),
|
||||
text_changes: vec![TextChange {
|
||||
span: TextSpan {
|
||||
start: 8,
|
||||
|
@ -6279,7 +6419,6 @@ mod tests {
|
|||
let (temp_dir, _, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -6290,7 +6429,7 @@ mod tests {
|
|||
let resolved = op_resolve_inner(
|
||||
&mut state,
|
||||
ResolveArgs {
|
||||
base: temp_dir.uri().join("a.ts").unwrap().to_string(),
|
||||
base: temp_dir.url().join("a.ts").unwrap().to_string(),
|
||||
is_base_cjs: false,
|
||||
specifiers: vec!["./b.ts".to_string()],
|
||||
},
|
||||
|
@ -6299,7 +6438,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
resolved,
|
||||
vec![Some((
|
||||
temp_dir.uri().join("b.ts").unwrap().to_string(),
|
||||
temp_dir.url().join("b.ts").unwrap().to_string(),
|
||||
MediaType::TypeScript.as_ts_extension().to_string()
|
||||
))]
|
||||
);
|
||||
|
|
229
cli/lsp/urls.rs
229
cli/lsp/urls.rs
|
@ -6,17 +6,25 @@ use deno_core::parking_lot::Mutex;
|
|||
use deno_core::url::Position;
|
||||
use deno_core::url::Url;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use lsp_types::Uri;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::cache::LspCache;
|
||||
use super::logging::lsp_warn;
|
||||
|
||||
/// Used in situations where a default URL needs to be used where otherwise a
|
||||
/// panic is undesired.
|
||||
pub static INVALID_SPECIFIER: Lazy<ModuleSpecifier> =
|
||||
Lazy::new(|| ModuleSpecifier::parse("deno://invalid").unwrap());
|
||||
|
||||
/// Used in situations where a default URL needs to be used where otherwise a
|
||||
/// panic is undesired.
|
||||
pub static INVALID_URI: Lazy<Uri> =
|
||||
Lazy::new(|| Uri::from_str("deno://invalid").unwrap());
|
||||
|
||||
/// Matches the `encodeURIComponent()` encoding from JavaScript, which matches
|
||||
/// the component percent encoding set.
|
||||
///
|
||||
|
@ -47,6 +55,25 @@ const COMPONENT: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS
|
|||
.add(b'+')
|
||||
.add(b',');
|
||||
|
||||
/// Characters that may be left unencoded in a `Url` path but not valid in a
|
||||
/// `Uri` path.
|
||||
const URL_TO_URI_PATH: &percent_encoding::AsciiSet =
|
||||
&percent_encoding::CONTROLS
|
||||
.add(b'[')
|
||||
.add(b']')
|
||||
.add(b'^')
|
||||
.add(b'|');
|
||||
|
||||
/// Characters that may be left unencoded in a `Url` query but not valid in a
|
||||
/// `Uri` query.
|
||||
const URL_TO_URI_QUERY: &percent_encoding::AsciiSet =
|
||||
&URL_TO_URI_PATH.add(b'\\').add(b'`').add(b'{').add(b'}');
|
||||
|
||||
/// Characters that may be left unencoded in a `Url` fragment but not valid in
|
||||
/// a `Uri` fragment.
|
||||
const URL_TO_URI_FRAGMENT: &percent_encoding::AsciiSet =
|
||||
&URL_TO_URI_PATH.add(b'#').add(b'\\').add(b'{').add(b'}');
|
||||
|
||||
fn hash_data_specifier(specifier: &ModuleSpecifier) -> String {
|
||||
let mut file_name_str = specifier.path().to_string();
|
||||
if let Some(query) = specifier.query() {
|
||||
|
@ -56,7 +83,7 @@ fn hash_data_specifier(specifier: &ModuleSpecifier) -> String {
|
|||
crate::util::checksum::gen(&[file_name_str.as_bytes()])
|
||||
}
|
||||
|
||||
fn to_deno_url(specifier: &Url) -> String {
|
||||
fn to_deno_uri(specifier: &Url) -> String {
|
||||
let mut string = String::with_capacity(specifier.as_str().len() + 6);
|
||||
string.push_str("deno:/");
|
||||
string.push_str(specifier.scheme());
|
||||
|
@ -93,58 +120,62 @@ fn from_deno_url(url: &Url) -> Option<Url> {
|
|||
Url::parse(&string).ok()
|
||||
}
|
||||
|
||||
/// This exists to make it a little bit harder to accidentally use a `Url`
|
||||
/// in the wrong place where a client url should be used.
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
|
||||
pub struct LspClientUrl(Url);
|
||||
|
||||
impl LspClientUrl {
|
||||
pub fn new(url: Url) -> Self {
|
||||
Self(url)
|
||||
}
|
||||
|
||||
pub fn as_url(&self) -> &Url {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn into_url(self) -> Url {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.0.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for LspClientUrl {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct LspUrlMapInner {
|
||||
specifier_to_url: HashMap<ModuleSpecifier, LspClientUrl>,
|
||||
url_to_specifier: HashMap<Url, ModuleSpecifier>,
|
||||
specifier_to_uri: HashMap<ModuleSpecifier, Uri>,
|
||||
uri_to_specifier: HashMap<Uri, ModuleSpecifier>,
|
||||
}
|
||||
|
||||
impl LspUrlMapInner {
|
||||
fn put(&mut self, specifier: ModuleSpecifier, url: LspClientUrl) {
|
||||
self
|
||||
.url_to_specifier
|
||||
.insert(url.as_url().clone(), specifier.clone());
|
||||
self.specifier_to_url.insert(specifier, url);
|
||||
fn put(&mut self, specifier: ModuleSpecifier, uri: Uri) {
|
||||
self.uri_to_specifier.insert(uri.clone(), specifier.clone());
|
||||
self.specifier_to_uri.insert(specifier, uri);
|
||||
}
|
||||
|
||||
fn get_url(&self, specifier: &ModuleSpecifier) -> Option<&LspClientUrl> {
|
||||
self.specifier_to_url.get(specifier)
|
||||
fn get_uri(&self, specifier: &ModuleSpecifier) -> Option<&Uri> {
|
||||
self.specifier_to_uri.get(specifier)
|
||||
}
|
||||
|
||||
fn get_specifier(&self, url: &Url) -> Option<&ModuleSpecifier> {
|
||||
self.url_to_specifier.get(url)
|
||||
fn get_specifier(&self, uri: &Uri) -> Option<&ModuleSpecifier> {
|
||||
self.uri_to_specifier.get(uri)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn uri_parse_unencoded(s: &str) -> Result<Uri, AnyError> {
|
||||
url_to_uri(&Url::parse(s)?)
|
||||
}
|
||||
|
||||
pub fn url_to_uri(url: &Url) -> Result<Uri, AnyError> {
|
||||
let components = deno_core::url::quirks::internal_components(url);
|
||||
let mut input = String::with_capacity(url.as_str().len());
|
||||
input.push_str(&url.as_str()[..components.path_start as usize]);
|
||||
input.push_str(
|
||||
&percent_encoding::utf8_percent_encode(url.path(), URL_TO_URI_PATH)
|
||||
.to_string(),
|
||||
);
|
||||
if let Some(query) = url.query() {
|
||||
input.push('?');
|
||||
input.push_str(
|
||||
&percent_encoding::utf8_percent_encode(query, URL_TO_URI_QUERY)
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
if let Some(fragment) = url.fragment() {
|
||||
input.push('#');
|
||||
input.push_str(
|
||||
&percent_encoding::utf8_percent_encode(fragment, URL_TO_URI_FRAGMENT)
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
Ok(Uri::from_str(&input).inspect_err(|err| {
|
||||
lsp_warn!("Could not convert URL \"{url}\" to URI: {err}")
|
||||
})?)
|
||||
}
|
||||
|
||||
pub fn uri_to_url(uri: &Uri) -> Url {
|
||||
Url::parse(uri.as_str()).unwrap()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum LspUrlKind {
|
||||
File,
|
||||
|
@ -167,24 +198,24 @@ impl LspUrlMap {
|
|||
|
||||
/// Normalize a specifier that is used internally within Deno (or tsc) to a
|
||||
/// URL that can be handled as a "virtual" document by an LSP client.
|
||||
pub fn normalize_specifier(
|
||||
pub fn specifier_to_uri(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
file_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Result<LspClientUrl, AnyError> {
|
||||
) -> Result<Uri, AnyError> {
|
||||
if let Some(file_url) =
|
||||
self.cache.vendored_specifier(specifier, file_referrer)
|
||||
{
|
||||
return Ok(LspClientUrl(file_url));
|
||||
return url_to_uri(&file_url);
|
||||
}
|
||||
let mut inner = self.inner.lock();
|
||||
if let Some(url) = inner.get_url(specifier).cloned() {
|
||||
Ok(url)
|
||||
if let Some(uri) = inner.get_uri(specifier).cloned() {
|
||||
Ok(uri)
|
||||
} else {
|
||||
let url = if specifier.scheme() == "file" {
|
||||
LspClientUrl(specifier.clone())
|
||||
let uri = if specifier.scheme() == "file" {
|
||||
url_to_uri(specifier)?
|
||||
} else {
|
||||
let specifier_str = if specifier.scheme() == "asset" {
|
||||
let uri_str = if specifier.scheme() == "asset" {
|
||||
format!("deno:/asset{}", specifier.path())
|
||||
} else if specifier.scheme() == "data" {
|
||||
let data_url = deno_graph::source::RawDataUrl::parse(specifier)?;
|
||||
|
@ -200,13 +231,13 @@ impl LspUrlMap {
|
|||
extension
|
||||
)
|
||||
} else {
|
||||
to_deno_url(specifier)
|
||||
to_deno_uri(specifier)
|
||||
};
|
||||
let url = LspClientUrl(Url::parse(&specifier_str)?);
|
||||
inner.put(specifier.clone(), url.clone());
|
||||
url
|
||||
let uri = uri_parse_unencoded(&uri_str)?;
|
||||
inner.put(specifier.clone(), uri.clone());
|
||||
uri
|
||||
};
|
||||
Ok(url)
|
||||
Ok(uri)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,12 +249,17 @@ impl LspUrlMap {
|
|||
/// Note: Sometimes the url provided by the client may not have a trailing slash,
|
||||
/// so we need to force it to in the mapping and nee to explicitly state whether
|
||||
/// this is a file or directory url.
|
||||
pub fn normalize_url(&self, url: &Url, kind: LspUrlKind) -> ModuleSpecifier {
|
||||
if let Some(remote_url) = self.cache.unvendored_specifier(url) {
|
||||
pub fn uri_to_specifier(
|
||||
&self,
|
||||
uri: &Uri,
|
||||
kind: LspUrlKind,
|
||||
) -> ModuleSpecifier {
|
||||
let url = uri_to_url(uri);
|
||||
if let Some(remote_url) = self.cache.unvendored_specifier(&url) {
|
||||
return remote_url;
|
||||
}
|
||||
let mut inner = self.inner.lock();
|
||||
if let Some(specifier) = inner.get_specifier(url).cloned() {
|
||||
if let Some(specifier) = inner.get_specifier(uri).cloned() {
|
||||
return specifier;
|
||||
}
|
||||
let mut specifier = None;
|
||||
|
@ -234,13 +270,13 @@ impl LspUrlMap {
|
|||
LspUrlKind::File => Url::from_file_path(path).unwrap(),
|
||||
});
|
||||
}
|
||||
} else if let Some(s) = file_like_to_file_specifier(url) {
|
||||
} else if let Some(s) = file_like_to_file_specifier(&url) {
|
||||
specifier = Some(s);
|
||||
} else if let Some(s) = from_deno_url(url) {
|
||||
} else if let Some(s) = from_deno_url(&url) {
|
||||
specifier = Some(s);
|
||||
}
|
||||
let specifier = specifier.unwrap_or_else(|| url.clone());
|
||||
inner.put(specifier.clone(), LspClientUrl(url.clone()));
|
||||
inner.put(specifier.clone(), uri.clone());
|
||||
specifier
|
||||
}
|
||||
}
|
||||
|
@ -288,15 +324,14 @@ mod tests {
|
|||
fn test_lsp_url_map() {
|
||||
let map = LspUrlMap::default();
|
||||
let fixture = resolve_url("https://deno.land/x/pkg@1.0.0/mod.ts").unwrap();
|
||||
let actual_url = map
|
||||
.normalize_specifier(&fixture, None)
|
||||
let actual_uri = map
|
||||
.specifier_to_uri(&fixture, None)
|
||||
.expect("could not handle specifier");
|
||||
let expected_url =
|
||||
Url::parse("deno:/https/deno.land/x/pkg%401.0.0/mod.ts").unwrap();
|
||||
assert_eq!(actual_url.as_url(), &expected_url);
|
||||
|
||||
let actual_specifier =
|
||||
map.normalize_url(actual_url.as_url(), LspUrlKind::File);
|
||||
assert_eq!(
|
||||
actual_uri.as_str(),
|
||||
"deno:/https/deno.land/x/pkg%401.0.0/mod.ts"
|
||||
);
|
||||
let actual_specifier = map.uri_to_specifier(&actual_uri, LspUrlKind::File);
|
||||
assert_eq!(actual_specifier, fixture);
|
||||
}
|
||||
|
||||
|
@ -304,18 +339,14 @@ mod tests {
|
|||
fn test_lsp_url_reverse() {
|
||||
let map = LspUrlMap::default();
|
||||
let fixture =
|
||||
resolve_url("deno:/https/deno.land/x/pkg%401.0.0/mod.ts").unwrap();
|
||||
let actual_specifier = map.normalize_url(&fixture, LspUrlKind::File);
|
||||
Uri::from_str("deno:/https/deno.land/x/pkg%401.0.0/mod.ts").unwrap();
|
||||
let actual_specifier = map.uri_to_specifier(&fixture, LspUrlKind::File);
|
||||
let expected_specifier =
|
||||
Url::parse("https://deno.land/x/pkg@1.0.0/mod.ts").unwrap();
|
||||
assert_eq!(&actual_specifier, &expected_specifier);
|
||||
|
||||
let actual_url = map
|
||||
.normalize_specifier(&actual_specifier, None)
|
||||
.unwrap()
|
||||
.as_url()
|
||||
.clone();
|
||||
assert_eq!(actual_url, fixture);
|
||||
let actual_uri = map.specifier_to_uri(&actual_specifier, None).unwrap();
|
||||
assert_eq!(actual_uri, fixture);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -323,14 +354,11 @@ mod tests {
|
|||
// Test fix for #9741 - not properly encoding certain URLs
|
||||
let map = LspUrlMap::default();
|
||||
let fixture = resolve_url("https://cdn.skypack.dev/-/postcss@v8.2.9-E4SktPp9c0AtxrJHp8iV/dist=es2020,mode=types/lib/postcss.d.ts").unwrap();
|
||||
let actual_url = map
|
||||
.normalize_specifier(&fixture, None)
|
||||
let actual_uri = map
|
||||
.specifier_to_uri(&fixture, None)
|
||||
.expect("could not handle specifier");
|
||||
let expected_url = Url::parse("deno:/https/cdn.skypack.dev/-/postcss%40v8.2.9-E4SktPp9c0AtxrJHp8iV/dist%3Des2020%2Cmode%3Dtypes/lib/postcss.d.ts").unwrap();
|
||||
assert_eq!(actual_url.as_url(), &expected_url);
|
||||
|
||||
let actual_specifier =
|
||||
map.normalize_url(actual_url.as_url(), LspUrlKind::File);
|
||||
assert_eq!(actual_uri.as_str(), "deno:/https/cdn.skypack.dev/-/postcss%40v8.2.9-E4SktPp9c0AtxrJHp8iV/dist%3Des2020%2Cmode%3Dtypes/lib/postcss.d.ts");
|
||||
let actual_specifier = map.uri_to_specifier(&actual_uri, LspUrlKind::File);
|
||||
assert_eq!(actual_specifier, fixture);
|
||||
}
|
||||
|
||||
|
@ -338,14 +366,13 @@ mod tests {
|
|||
fn test_lsp_url_map_data() {
|
||||
let map = LspUrlMap::default();
|
||||
let fixture = resolve_url("data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=").unwrap();
|
||||
let actual_url = map
|
||||
.normalize_specifier(&fixture, None)
|
||||
let actual_uri = map
|
||||
.specifier_to_uri(&fixture, None)
|
||||
.expect("could not handle specifier");
|
||||
let expected_url = Url::parse("deno:/c21c7fc382b2b0553dc0864aa81a3acacfb7b3d1285ab5ae76da6abec213fb37/data_url.ts").unwrap();
|
||||
assert_eq!(actual_url.as_url(), &expected_url);
|
||||
assert_eq!(&uri_to_url(&actual_uri), &expected_url);
|
||||
|
||||
let actual_specifier =
|
||||
map.normalize_url(actual_url.as_url(), LspUrlKind::File);
|
||||
let actual_specifier = map.uri_to_specifier(&actual_uri, LspUrlKind::File);
|
||||
assert_eq!(actual_specifier, fixture);
|
||||
}
|
||||
|
||||
|
@ -353,15 +380,11 @@ mod tests {
|
|||
fn test_lsp_url_map_host_with_port() {
|
||||
let map = LspUrlMap::default();
|
||||
let fixture = resolve_url("http://localhost:8000/mod.ts").unwrap();
|
||||
let actual_url = map
|
||||
.normalize_specifier(&fixture, None)
|
||||
let actual_uri = map
|
||||
.specifier_to_uri(&fixture, None)
|
||||
.expect("could not handle specifier");
|
||||
let expected_url =
|
||||
Url::parse("deno:/http/localhost%3A8000/mod.ts").unwrap();
|
||||
assert_eq!(actual_url.as_url(), &expected_url);
|
||||
|
||||
let actual_specifier =
|
||||
map.normalize_url(actual_url.as_url(), LspUrlKind::File);
|
||||
assert_eq!(actual_uri.as_str(), "deno:/http/localhost%3A8000/mod.ts");
|
||||
let actual_specifier = map.uri_to_specifier(&actual_uri, LspUrlKind::File);
|
||||
assert_eq!(actual_specifier, fixture);
|
||||
}
|
||||
|
||||
|
@ -369,11 +392,11 @@ mod tests {
|
|||
#[test]
|
||||
fn test_normalize_windows_path() {
|
||||
let map = LspUrlMap::default();
|
||||
let fixture = resolve_url(
|
||||
let fixture = Uri::from_str(
|
||||
"file:///c%3A/Users/deno/Desktop/file%20with%20spaces%20in%20name.txt",
|
||||
)
|
||||
.unwrap();
|
||||
let actual = map.normalize_url(&fixture, LspUrlKind::File);
|
||||
let actual = map.uri_to_specifier(&fixture, LspUrlKind::File);
|
||||
let expected =
|
||||
Url::parse("file:///C:/Users/deno/Desktop/file with spaces in name.txt")
|
||||
.unwrap();
|
||||
|
@ -384,11 +407,11 @@ mod tests {
|
|||
#[test]
|
||||
fn test_normalize_percent_encoded_path() {
|
||||
let map = LspUrlMap::default();
|
||||
let fixture = resolve_url(
|
||||
let fixture = Uri::from_str(
|
||||
"file:///Users/deno/Desktop/file%20with%20spaces%20in%20name.txt",
|
||||
)
|
||||
.unwrap();
|
||||
let actual = map.normalize_url(&fixture, LspUrlKind::File);
|
||||
let actual = map.uri_to_specifier(&fixture, LspUrlKind::File);
|
||||
let expected =
|
||||
Url::parse("file:///Users/deno/Desktop/file with spaces in name.txt")
|
||||
.unwrap();
|
||||
|
@ -398,9 +421,9 @@ mod tests {
|
|||
#[test]
|
||||
fn test_normalize_deno_status() {
|
||||
let map = LspUrlMap::default();
|
||||
let fixture = resolve_url("deno:/status.md").unwrap();
|
||||
let actual = map.normalize_url(&fixture, LspUrlKind::File);
|
||||
assert_eq!(actual, fixture);
|
||||
let fixture = Uri::from_str("deno:/status.md").unwrap();
|
||||
let actual = map.uri_to_specifier(&fixture, LspUrlKind::File);
|
||||
assert_eq!(actual.as_str(), fixture.as_str());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
265
cli/main.rs
265
cli/main.rs
|
@ -15,11 +15,11 @@ mod js;
|
|||
mod jsr;
|
||||
mod lsp;
|
||||
mod module_loader;
|
||||
mod napi;
|
||||
mod node;
|
||||
mod npm;
|
||||
mod ops;
|
||||
mod resolver;
|
||||
mod shared;
|
||||
mod standalone;
|
||||
mod task_runner;
|
||||
mod tools;
|
||||
|
@ -31,12 +31,13 @@ mod worker;
|
|||
use crate::args::flags_from_vec;
|
||||
use crate::args::DenoSubcommand;
|
||||
use crate::args::Flags;
|
||||
use crate::args::DENO_FUTURE;
|
||||
use crate::graph_container::ModuleGraphContainer;
|
||||
use crate::util::display;
|
||||
use crate::util::v8::get_v8_flags_from_env;
|
||||
use crate::util::v8::init_v8_flags;
|
||||
|
||||
use args::TaskFlags;
|
||||
use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError;
|
||||
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
|
||||
use deno_runtime::WorkerExecutionMode;
|
||||
pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS;
|
||||
|
||||
|
@ -50,11 +51,19 @@ use deno_runtime::fmt_errors::format_js_error;
|
|||
use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics;
|
||||
use deno_terminal::colors;
|
||||
use factory::CliFactory;
|
||||
use standalone::MODULE_NOT_FOUND;
|
||||
use standalone::UNSUPPORTED_SCHEME;
|
||||
use std::env;
|
||||
use std::future::Future;
|
||||
use std::io::IsTerminal;
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
#[global_allocator]
|
||||
static ALLOC: dhat::Alloc = dhat::Alloc;
|
||||
|
||||
/// Ensures that all subcommands return an i32 exit code and an [`AnyError`] error type.
|
||||
trait SubcommandOutput {
|
||||
fn output(self) -> Result<i32, AnyError>;
|
||||
|
@ -94,7 +103,10 @@ fn spawn_subcommand<F: Future<Output = T> + 'static, T: SubcommandOutput>(
|
|||
async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
||||
let handle = match flags.subcommand.clone() {
|
||||
DenoSubcommand::Add(add_flags) => spawn_subcommand(async {
|
||||
tools::registry::add(flags, add_flags).await
|
||||
tools::registry::add(flags, add_flags, tools::registry::AddCommandName::Add).await
|
||||
}),
|
||||
DenoSubcommand::Remove(remove_flags) => spawn_subcommand(async {
|
||||
tools::registry::remove(flags, remove_flags).await
|
||||
}),
|
||||
DenoSubcommand::Bench(bench_flags) => spawn_subcommand(async {
|
||||
if bench_flags.watch.is_some() {
|
||||
|
@ -103,9 +115,7 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
|||
tools::bench::run_benchmarks(flags, bench_flags).await
|
||||
}
|
||||
}),
|
||||
DenoSubcommand::Bundle(bundle_flags) => spawn_subcommand(async {
|
||||
tools::bundle::bundle(flags, bundle_flags).await
|
||||
}),
|
||||
DenoSubcommand::Bundle => exit_with_message("⚠️ `deno bundle` was removed in Deno 2.\n\nSee the Deno 1.x to 2.x Migration Guide for migration instructions: https://docs.deno.com/runtime/manual/advanced/migrate_deprecations", 1),
|
||||
DenoSubcommand::Doc(doc_flags) => {
|
||||
spawn_subcommand(async { tools::doc::doc(flags, doc_flags).await })
|
||||
}
|
||||
|
@ -113,28 +123,19 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
|||
tools::run::eval_command(flags, eval_flags).await
|
||||
}),
|
||||
DenoSubcommand::Cache(cache_flags) => spawn_subcommand(async move {
|
||||
let factory = CliFactory::from_flags(flags);
|
||||
let emitter = factory.emitter()?;
|
||||
let main_graph_container =
|
||||
factory.main_module_graph_container().await?;
|
||||
main_graph_container
|
||||
.load_and_type_check_files(&cache_flags.files)
|
||||
.await?;
|
||||
emitter.cache_module_emits(&main_graph_container.graph()).await
|
||||
tools::installer::install_from_entrypoints(flags, &cache_flags.files).await
|
||||
}),
|
||||
DenoSubcommand::Check(check_flags) => spawn_subcommand(async move {
|
||||
let factory = CliFactory::from_flags(flags);
|
||||
let main_graph_container =
|
||||
factory.main_module_graph_container().await?;
|
||||
main_graph_container
|
||||
.load_and_type_check_files(&check_flags.files)
|
||||
.await
|
||||
tools::check::check(flags, check_flags).await
|
||||
}),
|
||||
DenoSubcommand::Clean => spawn_subcommand(async move {
|
||||
tools::clean::clean()
|
||||
}),
|
||||
DenoSubcommand::Compile(compile_flags) => spawn_subcommand(async {
|
||||
tools::compile::compile(flags, compile_flags).await
|
||||
}),
|
||||
DenoSubcommand::Coverage(coverage_flags) => spawn_subcommand(async {
|
||||
tools::coverage::cover_files(flags, coverage_flags).await
|
||||
tools::coverage::cover_files(flags, coverage_flags)
|
||||
}),
|
||||
DenoSubcommand::Fmt(fmt_flags) => {
|
||||
spawn_subcommand(
|
||||
|
@ -143,9 +144,7 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
|||
}
|
||||
DenoSubcommand::Init(init_flags) => {
|
||||
spawn_subcommand(async {
|
||||
// make compiler happy since init_project is sync
|
||||
tokio::task::yield_now().await;
|
||||
tools::init::init_project(init_flags)
|
||||
tools::init::init_project(init_flags).await
|
||||
})
|
||||
}
|
||||
DenoSubcommand::Info(info_flags) => {
|
||||
|
@ -154,13 +153,28 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
|||
DenoSubcommand::Install(install_flags) => spawn_subcommand(async {
|
||||
tools::installer::install_command(flags, install_flags).await
|
||||
}),
|
||||
DenoSubcommand::JSONReference(json_reference) => spawn_subcommand(async move {
|
||||
display::write_to_stdout_ignore_sigpipe(&deno_core::serde_json::to_vec_pretty(&json_reference.json).unwrap())
|
||||
}),
|
||||
DenoSubcommand::Jupyter(jupyter_flags) => spawn_subcommand(async {
|
||||
tools::jupyter::kernel(flags, jupyter_flags).await
|
||||
}),
|
||||
DenoSubcommand::Uninstall(uninstall_flags) => spawn_subcommand(async {
|
||||
tools::installer::uninstall(uninstall_flags)
|
||||
tools::installer::uninstall(flags, uninstall_flags).await
|
||||
}),
|
||||
DenoSubcommand::Lsp => spawn_subcommand(async {
|
||||
if std::io::stderr().is_terminal() {
|
||||
log::warn!(
|
||||
"{} command is intended to be run by text editors and IDEs and shouldn't be run manually.
|
||||
|
||||
Visit https://docs.deno.com/runtime/getting_started/setup_your_environment/ for instruction
|
||||
how to setup your favorite text editor.
|
||||
|
||||
Press Ctrl+C to exit.
|
||||
", colors::cyan("deno lsp"));
|
||||
}
|
||||
lsp::start().await
|
||||
}),
|
||||
DenoSubcommand::Lsp => spawn_subcommand(async { lsp::start().await }),
|
||||
DenoSubcommand::Lint(lint_flags) => spawn_subcommand(async {
|
||||
if lint_flags.rules {
|
||||
tools::lint::print_rules_list(
|
||||
|
@ -172,18 +186,84 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
|||
tools::lint::lint(flags, lint_flags).await
|
||||
}
|
||||
}),
|
||||
DenoSubcommand::Outdated(update_flags) => {
|
||||
spawn_subcommand(async move {
|
||||
tools::registry::outdated(flags, update_flags).await
|
||||
})
|
||||
}
|
||||
DenoSubcommand::Repl(repl_flags) => {
|
||||
spawn_subcommand(async move { tools::repl::run(flags, repl_flags).await })
|
||||
}
|
||||
DenoSubcommand::Run(run_flags) => spawn_subcommand(async move {
|
||||
if run_flags.is_stdin() {
|
||||
tools::run::run_from_stdin(flags).await
|
||||
tools::run::run_from_stdin(flags.clone()).await
|
||||
} else {
|
||||
tools::run::run_script(WorkerExecutionMode::Run, flags, run_flags.watch).await
|
||||
let result = tools::run::run_script(WorkerExecutionMode::Run, flags.clone(), run_flags.watch).await;
|
||||
match result {
|
||||
Ok(v) => Ok(v),
|
||||
Err(script_err) => {
|
||||
if let Some(ResolvePkgFolderFromDenoReqError::Byonm(ByonmResolvePkgFolderFromDenoReqError::UnmatchedReq(_))) = script_err.downcast_ref::<ResolvePkgFolderFromDenoReqError>() {
|
||||
if flags.node_modules_dir.is_none() {
|
||||
let mut flags = flags.deref().clone();
|
||||
let watch = match &flags.subcommand {
|
||||
DenoSubcommand::Run(run_flags) => run_flags.watch.clone(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
flags.node_modules_dir = Some(deno_config::deno_json::NodeModulesDirMode::None);
|
||||
// use the current lockfile, but don't write it out
|
||||
if flags.frozen_lockfile.is_none() {
|
||||
flags.internal.lockfile_skip_write = true;
|
||||
}
|
||||
return tools::run::run_script(WorkerExecutionMode::Run, Arc::new(flags), watch).await;
|
||||
}
|
||||
}
|
||||
let script_err_msg = script_err.to_string();
|
||||
if script_err_msg.starts_with(MODULE_NOT_FOUND) || script_err_msg.starts_with(UNSUPPORTED_SCHEME) {
|
||||
if run_flags.bare {
|
||||
let mut cmd = args::clap_root();
|
||||
cmd.build();
|
||||
let command_names = cmd.get_subcommands().map(|command| command.get_name()).collect::<Vec<_>>();
|
||||
let suggestions = args::did_you_mean(&run_flags.script, command_names);
|
||||
if !suggestions.is_empty() {
|
||||
let mut error = clap::error::Error::<clap::error::DefaultFormatter>::new(clap::error::ErrorKind::InvalidSubcommand).with_cmd(&cmd);
|
||||
error.insert(
|
||||
clap::error::ContextKind::SuggestedSubcommand,
|
||||
clap::error::ContextValue::Strings(suggestions),
|
||||
);
|
||||
|
||||
Err(error.into())
|
||||
} else {
|
||||
Err(script_err)
|
||||
}
|
||||
} else {
|
||||
let mut new_flags = flags.deref().clone();
|
||||
let task_flags = TaskFlags {
|
||||
cwd: None,
|
||||
task: Some(run_flags.script.clone()),
|
||||
is_run: true,
|
||||
recursive: false,
|
||||
filter: None,
|
||||
eval: false,
|
||||
};
|
||||
new_flags.subcommand = DenoSubcommand::Task(task_flags.clone());
|
||||
let result = tools::task::execute_script(Arc::new(new_flags), task_flags.clone()).await;
|
||||
match result {
|
||||
Ok(v) => Ok(v),
|
||||
Err(_) => {
|
||||
// Return script error for backwards compatibility.
|
||||
Err(script_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Err(script_err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
DenoSubcommand::Serve(serve_flags) => spawn_subcommand(async move {
|
||||
tools::run::run_script(WorkerExecutionMode::Serve, flags, serve_flags.watch).await
|
||||
tools::serve::serve(flags, serve_flags).await
|
||||
}),
|
||||
DenoSubcommand::Task(task_flags) => spawn_subcommand(async {
|
||||
tools::task::execute_script(flags, task_flags).await
|
||||
|
@ -229,12 +309,27 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
|||
"This deno was built without the \"upgrade\" feature. Please upgrade using the installation method originally used to install Deno.",
|
||||
1,
|
||||
),
|
||||
DenoSubcommand::Vendor(vendor_flags) => spawn_subcommand(async {
|
||||
tools::vendor::vendor(flags, vendor_flags).await
|
||||
}),
|
||||
DenoSubcommand::Vendor => exit_with_message("⚠️ `deno vendor` was removed in Deno 2.\n\nSee the Deno 1.x to 2.x Migration Guide for migration instructions: https://docs.deno.com/runtime/manual/advanced/migrate_deprecations", 1),
|
||||
DenoSubcommand::Publish(publish_flags) => spawn_subcommand(async {
|
||||
tools::registry::publish(flags, publish_flags).await
|
||||
}),
|
||||
DenoSubcommand::Help(help_flags) => spawn_subcommand(async move {
|
||||
use std::io::Write;
|
||||
|
||||
let mut stream = anstream::AutoStream::new(std::io::stdout(), if colors::use_color() {
|
||||
anstream::ColorChoice::Auto
|
||||
} else {
|
||||
anstream::ColorChoice::Never
|
||||
});
|
||||
|
||||
match stream.write_all(help_flags.help.ansi().to_string().as_bytes()) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(e) => match e.kind() {
|
||||
std::io::ErrorKind::BrokenPipe => Ok(()),
|
||||
_ => Err(e),
|
||||
},
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
handle.await?
|
||||
|
@ -257,22 +352,21 @@ fn setup_panic_hook() {
|
|||
eprintln!("var set and include the backtrace in your report.");
|
||||
eprintln!();
|
||||
eprintln!("Platform: {} {}", env::consts::OS, env::consts::ARCH);
|
||||
eprintln!("Version: {}", version::deno());
|
||||
eprintln!("Version: {}", version::DENO_VERSION_INFO.deno);
|
||||
eprintln!("Args: {:?}", env::args().collect::<Vec<_>>());
|
||||
eprintln!();
|
||||
orig_hook(panic_info);
|
||||
std::process::exit(1);
|
||||
deno_runtime::exit(1);
|
||||
}));
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
fn exit_with_message(message: &str, code: i32) -> ! {
|
||||
eprintln!(
|
||||
log::error!(
|
||||
"{}: {}",
|
||||
colors::red_bold("error"),
|
||||
message.trim_start_matches("error: ")
|
||||
);
|
||||
std::process::exit(code);
|
||||
deno_runtime::exit(code);
|
||||
}
|
||||
|
||||
fn exit_for_error(error: AnyError) -> ! {
|
||||
|
@ -291,28 +385,18 @@ fn exit_for_error(error: AnyError) -> ! {
|
|||
exit_with_message(&error_string, error_code);
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
pub(crate) fn unstable_exit_cb(feature: &str, api_name: &str) {
|
||||
eprintln!(
|
||||
log::error!(
|
||||
"Unstable API '{api_name}'. The `--unstable-{}` flag must be provided.",
|
||||
feature
|
||||
);
|
||||
std::process::exit(70);
|
||||
}
|
||||
|
||||
// TODO(bartlomieju): remove when `--unstable` flag is removed.
|
||||
#[allow(clippy::print_stderr)]
|
||||
pub(crate) fn unstable_warn_cb(feature: &str, api_name: &str) {
|
||||
eprintln!(
|
||||
"⚠️ {}",
|
||||
colors::yellow(format!(
|
||||
"The `{}` API was used with `--unstable` flag. The `--unstable` flag is deprecated and will be removed in Deno 2.0. Use granular `--unstable-{}` instead.\nLearn more at: https://docs.deno.com/runtime/manual/tools/unstable_flags",
|
||||
api_name, feature
|
||||
))
|
||||
);
|
||||
deno_runtime::exit(70);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
let profiler = dhat::Profiler::new_heap();
|
||||
|
||||
setup_panic_hook();
|
||||
|
||||
util::unix::raise_fd_limit();
|
||||
|
@ -333,8 +417,13 @@ pub fn main() {
|
|||
run_subcommand(Arc::new(flags)).await
|
||||
};
|
||||
|
||||
match create_and_run_current_thread_with_maybe_metrics(future) {
|
||||
Ok(exit_code) => std::process::exit(exit_code),
|
||||
let result = create_and_run_current_thread_with_maybe_metrics(future);
|
||||
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
drop(profiler);
|
||||
|
||||
match result {
|
||||
Ok(exit_code) => deno_runtime::exit(exit_code),
|
||||
Err(err) => exit_for_error(err),
|
||||
}
|
||||
}
|
||||
|
@ -345,35 +434,32 @@ fn resolve_flags_and_init(
|
|||
let flags = match flags_from_vec(args) {
|
||||
Ok(flags) => flags,
|
||||
Err(err @ clap::Error { .. })
|
||||
if err.kind() == clap::error::ErrorKind::DisplayHelp
|
||||
|| err.kind() == clap::error::ErrorKind::DisplayVersion =>
|
||||
if err.kind() == clap::error::ErrorKind::DisplayVersion =>
|
||||
{
|
||||
// Ignore results to avoid BrokenPipe errors.
|
||||
util::logger::init(None);
|
||||
let _ = err.print();
|
||||
std::process::exit(0);
|
||||
deno_runtime::exit(0);
|
||||
}
|
||||
Err(err) => {
|
||||
util::logger::init(None);
|
||||
exit_for_error(AnyError::from(err))
|
||||
}
|
||||
Err(err) => exit_for_error(AnyError::from(err)),
|
||||
};
|
||||
|
||||
// TODO(bartlomieju): remove when `--unstable` flag is removed.
|
||||
if let Some(otel_config) = flags.otel_config() {
|
||||
deno_runtime::ops::otel::init(otel_config)?;
|
||||
}
|
||||
util::logger::init(flags.log_level);
|
||||
|
||||
// TODO(bartlomieju): remove in Deno v2.5 and hard error then.
|
||||
if flags.unstable_config.legacy_flag_enabled {
|
||||
#[allow(clippy::print_stderr)]
|
||||
if matches!(flags.subcommand, DenoSubcommand::Check(_)) {
|
||||
// can't use log crate because that's not setup yet
|
||||
eprintln!(
|
||||
"⚠️ {}",
|
||||
colors::yellow(
|
||||
"The `--unstable` flag is not needed for `deno check` anymore."
|
||||
)
|
||||
);
|
||||
} else {
|
||||
eprintln!(
|
||||
"⚠️ {}",
|
||||
colors::yellow(
|
||||
"The `--unstable` flag is deprecated and will be removed in Deno 2.0. Use granular `--unstable-*` flags instead.\nLearn more at: https://docs.deno.com/runtime/manual/tools/unstable_flags"
|
||||
)
|
||||
);
|
||||
}
|
||||
log::warn!(
|
||||
"⚠️ {}",
|
||||
colors::yellow(
|
||||
"The `--unstable` flag has been removed in Deno 2.0. Use granular `--unstable-*` flags instead.\nLearn more at: https://docs.deno.com/runtime/manual/tools/unstable_flags"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
let default_v8_flags = match flags.subcommand {
|
||||
|
@ -381,26 +467,19 @@ fn resolve_flags_and_init(
|
|||
// https://github.com/microsoft/vscode/blob/48d4ba271686e8072fc6674137415bc80d936bc7/extensions/typescript-language-features/src/configuration/configuration.ts#L213-L214
|
||||
DenoSubcommand::Lsp => vec!["--max-old-space-size=3072".to_string()],
|
||||
_ => {
|
||||
if *DENO_FUTURE {
|
||||
// deno_ast removes TypeScript `assert` keywords, so this flag only affects JavaScript
|
||||
// TODO(petamoriken): Need to check TypeScript `assert` keywords in deno_ast
|
||||
vec!["--no-harmony-import-assertions".to_string()]
|
||||
} else {
|
||||
vec![
|
||||
// If we're still in v1.X version we want to support import assertions.
|
||||
// V8 12.6 unshipped the support by default, so force it by passing a
|
||||
// flag.
|
||||
"--harmony-import-assertions".to_string(),
|
||||
// Verify with DENO_FUTURE for now.
|
||||
"--no-maglev".to_string(),
|
||||
]
|
||||
}
|
||||
// TODO(bartlomieju): I think this can be removed as it's handled by `deno_core`
|
||||
// and its settings.
|
||||
// deno_ast removes TypeScript `assert` keywords, so this flag only affects JavaScript
|
||||
// TODO(petamoriken): Need to check TypeScript `assert` keywords in deno_ast
|
||||
vec!["--no-harmony-import-assertions".to_string()]
|
||||
}
|
||||
};
|
||||
|
||||
init_v8_flags(&default_v8_flags, &flags.v8_flags, get_v8_flags_from_env());
|
||||
deno_core::JsRuntime::init_platform(None);
|
||||
util::logger::init(flags.log_level);
|
||||
// TODO(bartlomieju): remove last argument once Deploy no longer needs it
|
||||
deno_core::JsRuntime::init_platform(
|
||||
None, /* import assertions enabled */ false,
|
||||
);
|
||||
|
||||
Ok(flags)
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue