mirror of
https://github.com/denoland/deno.git
synced 2025-01-08 23:28:18 -05:00
Merge branch 'main' into Fix-UNC-Path-Permissions-Issue-on-Windows
This commit is contained in:
commit
b104d1085e
2678 changed files with 61369 additions and 29166 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
|
||||
|
|
|
@ -39,10 +39,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,7 +60,6 @@
|
|||
"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",
|
||||
|
@ -65,10 +68,10 @@
|
|||
"third_party"
|
||||
],
|
||||
"plugins": [
|
||||
"https://plugins.dprint.dev/typescript-0.91.7.wasm",
|
||||
"https://plugins.dprint.dev/typescript-0.93.0.wasm",
|
||||
"https://plugins.dprint.dev/json-0.19.3.wasm",
|
||||
"https://plugins.dprint.dev/markdown-0.17.8.wasm",
|
||||
"https://plugins.dprint.dev/toml-0.6.2.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.5.0.wasm"
|
||||
]
|
||||
|
|
11
.github/workflows/cargo_publish.yml
vendored
11
.github/workflows/cargo_publish.yml
vendored
|
@ -2,6 +2,11 @@ name: cargo_publish
|
|||
|
||||
on: workflow_dispatch
|
||||
|
||||
# Ensures only one publish is running at a time
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: cargo publish
|
||||
|
@ -32,12 +37,6 @@ jobs:
|
|||
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 }}
|
||||
|
|
53
.github/workflows/ci.generate.ts
vendored
53
.github/workflows/ci.generate.ts
vendored
|
@ -5,7 +5,7 @@ import { stringify } from "jsr:@std/yaml@^0.221/stringify";
|
|||
// Bump this number when you want to purge the cache.
|
||||
// Note: the tools/release/01_bump_crate_versions.ts script will update this version
|
||||
// automatically via regex, so ensure that this line maintains this format.
|
||||
const cacheVersion = 15;
|
||||
const cacheVersion = 21;
|
||||
|
||||
const ubuntuX86Runner = "ubuntu-22.04";
|
||||
const ubuntuX86XlRunner = "ubuntu-22.04-xl";
|
||||
|
@ -191,11 +191,6 @@ 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",
|
||||
|
@ -354,7 +349,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 +489,6 @@ const ci = {
|
|||
if: "matrix.job == 'bench' || matrix.job == 'test'",
|
||||
...installNodeStep,
|
||||
},
|
||||
installProtocStep,
|
||||
{
|
||||
if: [
|
||||
"matrix.profile == 'release' &&",
|
||||
|
@ -649,7 +643,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 +658,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",
|
||||
|
@ -758,8 +752,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"),
|
||||
},
|
||||
|
@ -784,8 +780,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"),
|
||||
},
|
||||
|
@ -800,7 +798,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"),
|
||||
},
|
||||
{
|
||||
|
@ -813,6 +813,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"),
|
||||
|
@ -826,7 +827,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)",
|
||||
|
@ -879,9 +880,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"),
|
||||
},
|
||||
|
@ -892,9 +893,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 \\",
|
||||
|
@ -958,8 +959,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"',
|
||||
|
@ -995,8 +995,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)",
|
||||
|
@ -1010,8 +1012,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",
|
||||
|
@ -1041,15 +1045,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"),
|
||||
|
@ -1068,6 +1082,7 @@ const ci = {
|
|||
"./target",
|
||||
"!./target/*/gn_out",
|
||||
"!./target/*/*.zip",
|
||||
"!./target/*/*.sha256sum",
|
||||
"!./target/*/*.tar.gz",
|
||||
].join("\n"),
|
||||
key: prCacheKeyPrefix + "${{ github.sha }}",
|
||||
|
|
59
.github/workflows/ci.yml
vendored
59
.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
|
||||
|
@ -199,12 +199,6 @@ jobs:
|
|||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install protoc
|
||||
uses: arduino/setup-protoc@v3
|
||||
with:
|
||||
version: '21.12'
|
||||
repo-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
if: '!(matrix.skip)'
|
||||
- if: |-
|
||||
!(matrix.skip) && (matrix.profile == 'release' &&
|
||||
matrix.job == 'test' &&
|
||||
|
@ -367,8 +361,8 @@ jobs:
|
|||
path: |-
|
||||
~/.cargo/registry/index
|
||||
~/.cargo/registry/cache
|
||||
key: '15-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
|
||||
restore-keys: '15-cargo-home-${{ matrix.os }}-${{ matrix.arch }}'
|
||||
key: '21-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
|
||||
restore-keys: '21-cargo-home-${{ matrix.os }}-${{ matrix.arch }}'
|
||||
if: '!(matrix.skip)'
|
||||
- name: Restore cache build output (PR)
|
||||
uses: actions/cache/restore@v4
|
||||
|
@ -381,7 +375,7 @@ jobs:
|
|||
!./target/*/*.zip
|
||||
!./target/*/*.tar.gz
|
||||
key: never_saved
|
||||
restore-keys: '15-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-'
|
||||
restore-keys: '21-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-'
|
||||
- name: Apply and update mtime cache
|
||||
if: '!(matrix.skip) && (!startsWith(github.ref, ''refs/tags/''))'
|
||||
uses: ./.github/mtime_cache
|
||||
|
@ -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,8 +683,9 @@ jobs:
|
|||
./target
|
||||
!./target/*/gn_out
|
||||
!./target/*/*.zip
|
||||
!./target/*/*.sha256sum
|
||||
!./target/*/*.tar.gz
|
||||
key: '15-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
|
||||
key: '21-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
|
||||
publish-canary:
|
||||
name: publish canary
|
||||
runs-on: ubuntu-22.04
|
||||
|
|
|
@ -1,15 +1,22 @@
|
|||
name: promote_to_rc
|
||||
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 the Release Candidate
|
||||
description: Commit to promote to release
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
promote-to-rc:
|
||||
name: Promote to Release Candidate
|
||||
promote-to-release:
|
||||
name: Promote to Release
|
||||
runs-on: macOS-latest
|
||||
if: github.repository == 'denoland/deno'
|
||||
steps:
|
||||
|
@ -42,14 +49,14 @@ jobs:
|
|||
./tools/install_prebuilt.js rcodesign
|
||||
echo $GITHUB_WORKSPACE/third_party/prebuilt/mac >> $GITHUB_PATH
|
||||
|
||||
- name: Promote to RC
|
||||
- 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_rc.ts ${{github.event.inputs.commitHash}}
|
||||
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-rc-latest.txt)/
|
||||
gsutil -h "Cache-Control: no-cache" cp release-rc-latest.txt gs://dl.deno.land/release-rc-latest.txt
|
||||
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/wpt_epoch.yml
vendored
4
.github/workflows/wpt_epoch.yml
vendored
|
@ -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
|
||||
|
||||
|
|
640
Cargo.lock
generated
640
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
171
Cargo.toml
171
Cargo.toml
|
@ -21,13 +21,14 @@ members = [
|
|||
"ext/napi",
|
||||
"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,17 +45,19 @@ license = "MIT"
|
|||
repository = "https://github.com/denoland/deno"
|
||||
|
||||
[workspace.dependencies]
|
||||
deno_ast = { version = "=0.42.0", features = ["transpiling"] }
|
||||
deno_core = { version = "0.307.0" }
|
||||
deno_ast = { version = "=0.42.2", features = ["transpiling"] }
|
||||
deno_core = { version = "0.314.2" }
|
||||
|
||||
deno_bench_util = { version = "0.162.0", path = "./bench_util" }
|
||||
deno_lockfile = "=0.23.0"
|
||||
deno_bench_util = { version = "0.167.0", path = "./bench_util" }
|
||||
deno_lockfile = "=0.23.1"
|
||||
deno_media_type = { version = "0.1.4", features = ["module_specifier"] }
|
||||
deno_permissions = { version = "0.28.0", path = "./runtime/permissions" }
|
||||
deno_runtime = { version = "0.177.0", path = "./runtime" }
|
||||
deno_semver = "=0.5.13"
|
||||
deno_npm = "=0.25.4"
|
||||
deno_path_util = "=0.2.1"
|
||||
deno_permissions = { version = "0.33.0", path = "./runtime/permissions" }
|
||||
deno_runtime = { version = "0.182.0", path = "./runtime" }
|
||||
deno_semver = "=0.5.14"
|
||||
deno_terminal = "0.2.0"
|
||||
napi_sym = { version = "0.98.0", path = "./cli/napi/sym" }
|
||||
napi_sym = { version = "0.103.0", path = "./cli/napi/sym" }
|
||||
test_util = { package = "test_server", path = "./tests/util/server" }
|
||||
|
||||
denokv_proto = "0.8.1"
|
||||
|
@ -63,29 +66,32 @@ denokv_remote = "0.8.1"
|
|||
denokv_sqlite = { default-features = false, version = "0.8.2" }
|
||||
|
||||
# exts
|
||||
deno_broadcast_channel = { version = "0.162.0", path = "./ext/broadcast_channel" }
|
||||
deno_cache = { version = "0.100.0", path = "./ext/cache" }
|
||||
deno_canvas = { version = "0.37.0", path = "./ext/canvas" }
|
||||
deno_console = { version = "0.168.0", path = "./ext/console" }
|
||||
deno_cron = { version = "0.48.0", path = "./ext/cron" }
|
||||
deno_crypto = { version = "0.182.0", path = "./ext/crypto" }
|
||||
deno_fetch = { version = "0.192.0", path = "./ext/fetch" }
|
||||
deno_ffi = { version = "0.155.0", path = "./ext/ffi" }
|
||||
deno_fs = { version = "0.78.0", path = "./ext/fs" }
|
||||
deno_http = { version = "0.166.0", path = "./ext/http" }
|
||||
deno_io = { version = "0.78.0", path = "./ext/io" }
|
||||
deno_kv = { version = "0.76.0", path = "./ext/kv" }
|
||||
deno_napi = { version = "0.99.0", path = "./ext/napi" }
|
||||
deno_net = { version = "0.160.0", path = "./ext/net" }
|
||||
deno_node = { version = "0.105.0", path = "./ext/node" }
|
||||
deno_tls = { version = "0.155.0", path = "./ext/tls" }
|
||||
deno_url = { version = "0.168.0", path = "./ext/url" }
|
||||
deno_web = { version = "0.199.0", path = "./ext/web" }
|
||||
deno_webgpu = { version = "0.135.0", path = "./ext/webgpu" }
|
||||
deno_webidl = { version = "0.168.0", path = "./ext/webidl" }
|
||||
deno_websocket = { version = "0.173.0", path = "./ext/websocket" }
|
||||
deno_webstorage = { version = "0.163.0", path = "./ext/webstorage" }
|
||||
node_resolver = { version = "0.7.0", path = "./ext/node_resolver" }
|
||||
deno_broadcast_channel = { version = "0.167.0", path = "./ext/broadcast_channel" }
|
||||
deno_cache = { version = "0.105.0", path = "./ext/cache" }
|
||||
deno_canvas = { version = "0.42.0", path = "./ext/canvas" }
|
||||
deno_console = { version = "0.173.0", path = "./ext/console" }
|
||||
deno_cron = { version = "0.53.0", path = "./ext/cron" }
|
||||
deno_crypto = { version = "0.187.0", path = "./ext/crypto" }
|
||||
deno_fetch = { version = "0.197.0", path = "./ext/fetch" }
|
||||
deno_ffi = { version = "0.160.0", path = "./ext/ffi" }
|
||||
deno_fs = { version = "0.83.0", path = "./ext/fs" }
|
||||
deno_http = { version = "0.171.0", path = "./ext/http" }
|
||||
deno_io = { version = "0.83.0", path = "./ext/io" }
|
||||
deno_kv = { version = "0.81.0", path = "./ext/kv" }
|
||||
deno_napi = { version = "0.104.0", path = "./ext/napi" }
|
||||
deno_net = { version = "0.165.0", path = "./ext/net" }
|
||||
deno_node = { version = "0.110.0", path = "./ext/node" }
|
||||
deno_tls = { version = "0.160.0", path = "./ext/tls" }
|
||||
deno_url = { version = "0.173.0", path = "./ext/url" }
|
||||
deno_web = { version = "0.204.0", path = "./ext/web" }
|
||||
deno_webgpu = { version = "0.140.0", path = "./ext/webgpu" }
|
||||
deno_webidl = { version = "0.173.0", path = "./ext/webidl" }
|
||||
deno_websocket = { version = "0.178.0", path = "./ext/websocket" }
|
||||
deno_webstorage = { version = "0.168.0", path = "./ext/webstorage" }
|
||||
|
||||
# resolvers
|
||||
deno_resolver = { version = "0.5.0", path = "./resolvers/deno" }
|
||||
node_resolver = { version = "0.12.0", path = "./resolvers/node" }
|
||||
|
||||
aes = "=0.8.3"
|
||||
anyhow = "1.0.57"
|
||||
|
@ -100,11 +106,13 @@ cbc = { version = "=0.1.2", features = ["alloc"] }
|
|||
# Note: Do not use the "clock" feature of chrono, as it links us to CoreFoundation on macOS.
|
||||
# Instead use util::time::utc_now()
|
||||
chrono = { version = "0.4", default-features = false, features = ["std", "serde"] }
|
||||
color-print = "0.3.5"
|
||||
console_static_text = "=0.8.1"
|
||||
dashmap = "5.5.3"
|
||||
data-encoding = "2.3.3"
|
||||
data-url = "=0.3.0"
|
||||
deno_cache_dir = "=0.11.1"
|
||||
deno_package_json = { version = "=0.1.1", default-features = false }
|
||||
deno_cache_dir = "=0.13.0"
|
||||
deno_package_json = { version = "0.1.2", default-features = false }
|
||||
dlopen2 = "0.6.1"
|
||||
ecb = "=0.1.2"
|
||||
elliptic-curve = { version = "0.13.4", features = ["alloc", "arithmetic", "ecdh", "std", "pem", "jwk"] }
|
||||
|
@ -132,7 +140,7 @@ ipnet = "2.3"
|
|||
jsonc-parser = { version = "=0.23.0", features = ["serde"] }
|
||||
lazy-regex = "3"
|
||||
libc = "0.2.126"
|
||||
libz-sys = { version = "1.1", default-features = false }
|
||||
libz-sys = { version = "1.1.20", default-features = false }
|
||||
log = "0.4.20"
|
||||
lsp-types = "=0.97.0" # used by tower-lsp and "proposed" feature is unstable in patch releases
|
||||
memmem = "0.1.1"
|
||||
|
@ -163,6 +171,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"
|
||||
|
@ -184,15 +193,17 @@ 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-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"] }
|
||||
uuid = { version = "1.3.0", features = ["v4"] }
|
||||
webpki-root-certs = "0.26.5"
|
||||
webpki-roots = "0.26"
|
||||
which = "4.2.5"
|
||||
yoke = { version = "0.7.4", features = ["derive"] }
|
||||
zeromq = { version = "=0.4.0", default-features = false, features = ["tcp-transport", "tokio-runtime"] }
|
||||
zstd = "=0.12.4"
|
||||
|
||||
|
@ -210,15 +221,14 @@ quote = "1"
|
|||
syn = { version = "2", features = ["full", "extra-traits"] }
|
||||
|
||||
# unix
|
||||
nix = "=0.26.2"
|
||||
nix = "=0.27.1"
|
||||
|
||||
# windows deps
|
||||
junction = "=0.2.0"
|
||||
winapi = "=0.3.9"
|
||||
windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_Media", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_WindowsProgramming", "Wdk", "Wdk_System", "Wdk_System_SystemInformation", "Win32_System_Pipes", "Wdk_Storage_FileSystem", "Win32_System_Registry"] }
|
||||
windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_Media", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_WindowsProgramming", "Wdk", "Wdk_System", "Wdk_System_SystemInformation", "Win32_Security", "Win32_System_Pipes", "Wdk_Storage_FileSystem", "Win32_System_Registry", "Win32_System_Kernel"] }
|
||||
winres = "=0.1.12"
|
||||
|
||||
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
incremental = true
|
||||
|
@ -236,13 +246,6 @@ inherits = "release"
|
|||
codegen-units = 128
|
||||
lto = "thin"
|
||||
|
||||
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
|
||||
[profile.bench]
|
||||
codegen-units = 1
|
||||
incremental = true
|
||||
lto = true
|
||||
opt-level = 'z' # Optimize for size
|
||||
|
||||
# Key generation is too slow on `debug`
|
||||
[profile.dev.package.num-bigint-dig]
|
||||
opt-level = 3
|
||||
|
@ -251,80 +254,6 @@ opt-level = 3
|
|||
[profile.dev.package.v8]
|
||||
opt-level = 1
|
||||
|
||||
# Optimize these packages for performance.
|
||||
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
|
||||
[profile.bench.package.async-compression]
|
||||
opt-level = 3
|
||||
[profile.bench.package.base64-simd]
|
||||
opt-level = 3
|
||||
[profile.bench.package.brotli]
|
||||
opt-level = 3
|
||||
[profile.bench.package.brotli-decompressor]
|
||||
opt-level = 3
|
||||
[profile.bench.package.bytes]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_bench_util]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_broadcast_channel]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_core]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_crypto]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_fetch]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_ffi]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_http]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_napi]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_net]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_node]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_runtime]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_tls]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_url]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_web]
|
||||
opt-level = 3
|
||||
[profile.bench.package.deno_websocket]
|
||||
opt-level = 3
|
||||
[profile.bench.package.fastwebsockets]
|
||||
opt-level = 3
|
||||
[profile.bench.package.flate2]
|
||||
opt-level = 3
|
||||
[profile.bench.package.futures-util]
|
||||
opt-level = 3
|
||||
[profile.bench.package.hyper]
|
||||
opt-level = 3
|
||||
[profile.bench.package.miniz_oxide]
|
||||
opt-level = 3
|
||||
[profile.bench.package.num-bigint-dig]
|
||||
opt-level = 3
|
||||
[profile.bench.package.rand]
|
||||
opt-level = 3
|
||||
[profile.bench.package.serde]
|
||||
opt-level = 3
|
||||
[profile.bench.package.serde_v8]
|
||||
opt-level = 3
|
||||
[profile.bench.package.test_napi]
|
||||
opt-level = 3
|
||||
[profile.bench.package.tokio]
|
||||
opt-level = 3
|
||||
[profile.bench.package.url]
|
||||
opt-level = 3
|
||||
[profile.bench.package.v8]
|
||||
opt-level = 3
|
||||
[profile.bench.package.zstd]
|
||||
opt-level = 3
|
||||
[profile.bench.package.zstd-sys]
|
||||
opt-level = 3
|
||||
|
||||
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
|
||||
[profile.release.package.async-compression]
|
||||
opt-level = 3
|
||||
[profile.release.package.base64-simd]
|
||||
|
@ -383,6 +312,8 @@ opt-level = 3
|
|||
opt-level = 3
|
||||
[profile.release.package.serde_v8]
|
||||
opt-level = 3
|
||||
[profile.release.package.libsui]
|
||||
opt-level = 3
|
||||
[profile.release.package.test_napi]
|
||||
opt-level = 3
|
||||
[profile.release.package.tokio]
|
||||
|
|
414
Releases.md
414
Releases.md
|
@ -6,6 +6,420 @@ https://github.com/denoland/deno/releases
|
|||
We also have one-line install commands at:
|
||||
https://github.com/denoland/deno_install
|
||||
|
||||
### 2.0.2 / 2024.10.17
|
||||
|
||||
- fix(cli): set napi object property properly (#26344)
|
||||
- fix(ext/node): add null check for kStreamBaseField (#26368)
|
||||
- fix(install): don't attempt to cache specifiers that point to directories
|
||||
(#26369)
|
||||
- fix(jupyter): fix panics for overslow subtraction (#26371)
|
||||
- fix(jupyter): update to the new logo (#26353)
|
||||
- fix(net): don't try to set nodelay on upgrade streams (#26342)
|
||||
- fix(node/fs): copyFile with `COPYFILE_EXCL` should not throw if the
|
||||
destination doesn't exist (#26360)
|
||||
- fix(node/http): normalize header names in `ServerResponse` (#26339)
|
||||
- fix(runtime): send ws ping frames from inspector server (#26352)
|
||||
- fix: don't warn on ignored signals on windows (#26332)
|
||||
|
||||
### 2.0.1 / 2024.10.16
|
||||
|
||||
- feat(lsp): "deno/didRefreshDenoConfigurationTree" notifications (#26215)
|
||||
- feat(unstable): `--unstable-detect-cjs` for respecting explicit
|
||||
`"type": "commonjs"` (#26149)
|
||||
- fix(add): create deno.json when running `deno add jsr:<pkg>` (#26275)
|
||||
- fix(add): exact version should not have range `^` specifier (#26302)
|
||||
- fix(child_process): map node `--no-warnings` flag to `--quiet` (#26288)
|
||||
- fix(cli): add prefix to install commands in help (#26318)
|
||||
- fix(cli): consolidate pkg parser for install & remove (#26298)
|
||||
- fix(cli): named export takes precedence over default export in doc testing
|
||||
(#26112)
|
||||
- fix(cli): improve deno info output for npm packages (#25906)
|
||||
- fix(console/ext/repl): support using parseFloat() (#25900)
|
||||
- fix(ext/console): apply coloring for console.table (#26280)
|
||||
- fix(ext/napi): pass user context to napi_threadsafe_fn finalizers (#26229)
|
||||
- fix(ext/node): allow writing to tty columns (#26201)
|
||||
- fix(ext/node): compute pem length (upper bound) for key exports (#26231)
|
||||
- fix(ext/node): fix dns.lookup result ordering (#26264)
|
||||
- fix(ext/node): handle http2 server ending stream (#26235)
|
||||
- fix(ext/node): implement TCP.setNoDelay (#26263)
|
||||
- fix(ext/node): timingSafeEqual account for AB byteOffset (#26292)
|
||||
- fix(ext/node): use primordials in `ext/node/polyfills/internal/buffer.mjs`
|
||||
(#24993)
|
||||
- fix(ext/webgpu): allow GL backend on Windows (#26206)
|
||||
- fix(install): duplicate dependencies in `package.json` (#26128)
|
||||
- fix(install): handle pkg with dep on self when pkg part of peer dep resolution
|
||||
(#26277)
|
||||
- fix(install): retry downloads of registry info / tarballs (#26278)
|
||||
- fix(install): support installing npm package with alias (#26246)
|
||||
- fix(jupyter): copy kernels icons to the kernel directory (#26084)
|
||||
- fix(jupyter): keep running event loop when waiting for messages (#26049)
|
||||
- fix(lsp): relative completions for bare import-mapped specifiers (#26137)
|
||||
- fix(node): make `process.stdout.isTTY` writable (#26130)
|
||||
- fix(node/util): export `styleText` from `node:util` (#26194)
|
||||
- fix(npm): support `--allow-scripts` on `deno run` (and `deno add`,
|
||||
`deno test`, etc) (#26075)
|
||||
- fix(repl): importing json files (#26053)
|
||||
- fix(repl): remove check flags (#26140)
|
||||
- fix(unstable/worker): ensure import permissions are passed (#26101)
|
||||
- fix: add hint for missing `document` global in terminal error (#26218)
|
||||
- fix: do not panic on wsl share file paths on windows (#26081)
|
||||
- fix: do not panic running remote cjs module (#26259)
|
||||
- fix: do not panic when using methods on classes and interfaces in deno doc
|
||||
html output (#26100)
|
||||
- fix: improve suggestions and hints when using CommonJS modules (#26287)
|
||||
- fix: node-api function call should use preamble (#26297)
|
||||
- fix: panic in `prepare_stack_trace_callback` when global interceptor throws
|
||||
(#26241)
|
||||
- fix: use syntect for deno doc html generation (#26322)
|
||||
- perf(http): avoid clone getting request method and url (#26250)
|
||||
- perf(http): cache webidl.converters lookups in ext/fetch/23_response.js
|
||||
(#26256)
|
||||
- perf(http): make heap allocation for path conditional (#26289)
|
||||
- perf: use fast calls for microtask ops (#26236)
|
||||
|
||||
### 2.0.0 / 2024.10.09
|
||||
|
||||
Read announcement blog post at: https://deno.com/blog/v2
|
||||
|
||||
- BREAKING: `DENO_FUTURE=1` by default, or welcome to Deno 2.0 (#25213)
|
||||
- BREAKING: disallow `new Deno.FsFile()` (#25478)
|
||||
- BREAKING: drop support for Deno.run.{clearEnv,gid,uid} (#25371)
|
||||
- BREAKING: improve types for `Deno.serve` (#25369)
|
||||
- BREAKING: improved error code accuracy (#25383)
|
||||
- BREAKING: make supported compilerOptions an allow list (#25432)
|
||||
- BREAKING: move `width` and `height` options to `UnsafeWindowSurface`
|
||||
constructor (#24200)
|
||||
- BREAKING: remove --allow-hrtime (#25367)
|
||||
- BREAKING: remove "emit" and "map" from deno info output (#25468)
|
||||
- BREAKING: remove `--allow-none` flag (#25337)
|
||||
- BREAKING: remove `--jobs` flag (#25336)
|
||||
- BREAKING: remove `--trace-ops` (#25344)
|
||||
- BREAKING: remove `--ts` flag (#25338)
|
||||
- BREAKING: remove `--unstable` flag (#25522)
|
||||
- BREAKING: remove `deno bundle` (#25339)
|
||||
- BREAKING: remove `deno vendor` (#25343)
|
||||
- BREAKING: remove `Deno.[Tls]Listener.prototype.rid` (#25556)
|
||||
- BREAKING: remove `Deno.{Conn,TlsConn,TcpConn,UnixConn}.prototype.rid` (#25446)
|
||||
- BREAKING: remove `Deno.{Reader,Writer}[Sync]` and `Deno.Closer` (#25524)
|
||||
- BREAKING: remove `Deno.Buffer` (#25441)
|
||||
- BREAKING: remove `Deno.close()` (#25347)
|
||||
- BREAKING: remove `Deno.ConnectTlsOptions.{certChain,certFile,privateKey}` and
|
||||
`Deno.ListenTlsOptions.certChain,certFile,keyFile}` (#25525)
|
||||
- BREAKING: remove `Deno.copy()` (#25345)
|
||||
- BREAKING: remove `Deno.customInspect` (#25348)
|
||||
- BREAKING: remove `Deno.fdatasync[Sync]()` (#25520)
|
||||
- BREAKING: remove `Deno.File` (#25447)
|
||||
- BREAKING: remove `Deno.flock[Sync]()` (#25350)
|
||||
- BREAKING: remove `Deno.FsFile.prototype.rid` (#25499)
|
||||
- BREAKING: remove `Deno.fstat[Sync]()` (#25351)
|
||||
- BREAKING: remove `Deno.FsWatcher.prototype.rid` (#25444)
|
||||
- BREAKING: remove `Deno.fsync[Sync]()` (#25448)
|
||||
- BREAKING: remove `Deno.ftruncate[Sync]()` (#25412)
|
||||
- BREAKING: remove `Deno.funlock[Sync]()` (#25442)
|
||||
- BREAKING: remove `Deno.futime[Sync]()` (#25252)
|
||||
- BREAKING: remove `Deno.iter[Sync]()` (#25346)
|
||||
- BREAKING: remove `Deno.read[Sync]()` (#25409)
|
||||
- BREAKING: remove `Deno.readAll[Sync]()` (#25386)
|
||||
- BREAKING: remove `Deno.seek[Sync]()` (#25449)
|
||||
- BREAKING: remove `Deno.Seeker[Sync]` (#25551)
|
||||
- BREAKING: remove `Deno.shutdown()` (#25253)
|
||||
- BREAKING: remove `Deno.write[Sync]()` (#25408)
|
||||
- BREAKING: remove `Deno.writeAll[Sync]()` (#25407)
|
||||
- BREAKING: remove deprecated `UnsafeFnPointer` constructor type with untyped
|
||||
`Deno.PointerObject` parameter (#25577)
|
||||
- BREAKING: remove deprecated files config (#25535)
|
||||
- BREAKING: Remove obsoleted Temporal APIs part 2 (#25505)
|
||||
- BREAKING: remove remaining web types for compatibility (#25334)
|
||||
- BREAKING: remove support for remote import maps in deno.json (#25836)
|
||||
- BREAKING: rename "deps" remote cache folder to "remote" (#25969)
|
||||
- BREAKING: soft-remove `Deno.isatty()` (#25410)
|
||||
- BREAKING: soft-remove `Deno.run()` (#25403)
|
||||
- BREAKING: soft-remove `Deno.serveHttp()` (#25451)
|
||||
- BREAKING: undeprecate `Deno.FsWatcher.prototype.return()` (#25623)
|
||||
- feat: add `--allow-import` flag (#25469)
|
||||
- feat: Add a hint on error about 'Relative import path ... not prefixed with
|
||||
...' (#25430)
|
||||
- feat: Add better error messages for unstable APIs (#25519)
|
||||
- feat: Add suggestion for packages using Node-API addons (#25975)
|
||||
- feat: Allow importing .cjs files (#25426)
|
||||
- feat: default to TS for file extension and support ext flag in more scenarios
|
||||
(#25472)
|
||||
- feat: deprecate import assertions (#25281)
|
||||
- feat: Don't warn about --allow-script when using esbuild (#25894)
|
||||
- feat: hide several --unstable-* flags (#25378)
|
||||
- feat: improve lockfile v4 to store normalized version constraints and be more
|
||||
terse (#25247)
|
||||
- feat: improve warnings for deprecations and lifecycle script for npm packages
|
||||
(#25694)
|
||||
- feat: include version number in all --json based outputs (#25335)
|
||||
- feat: lockfile v4 by default (#25165)
|
||||
- feat: make 'globalThis.location' a configurable property (#25812)
|
||||
- feat: print `Listening on` messages on stderr instead of stdout (#25491)
|
||||
- feat: remove `--lock-write` flag (#25214)
|
||||
- feat: require jsr prefix for `deno install` and `deno add` (#25698)
|
||||
- feat: require(esm) (#25501)
|
||||
- feat: Show hints when using `window` global (#25805)
|
||||
- feat: stabilize `Deno.createHttpClient()` (#25569)
|
||||
- feat: suggest `deno install --entrypoint` instead of `deno cache` (#25228)
|
||||
- feat: support DENO_LOG env var instead of RUST_LOG (#25356)
|
||||
- feat: TypeScript 5.6 and `npm:@types/node@22` (#25614)
|
||||
- feat: Update no-window lint rule (#25486)
|
||||
- feat: update warning message for --allow-run with no list (#25693)
|
||||
- feat: warn when using `--allow-run` with no allow list (#25215)
|
||||
- feat(add): Add npm packages to package.json if present (#25477)
|
||||
- feat(add): strip package subpath when adding a package (#25419)
|
||||
- feat(add/install): Flag to add dev dependency to package.json (#25495)
|
||||
- feat(byonm): support `deno run npm:<package>` when package is not in
|
||||
package.json (#25981)
|
||||
- feat(check): turn on noImplicitOverride (#25695)
|
||||
- feat(check): turn on useUnknownInCatchVariables (#25465)
|
||||
- feat(cli): evaluate code snippets in JSDoc and markdown (#25220)
|
||||
- feat(cli): give access to `process` global everywhere (#25291)
|
||||
- feat(cli): use NotCapable error for permission errors (#25431)
|
||||
- feat(config): Node modules option for 2.0 (#25299)
|
||||
- feat(ext/crypto): import and export p521 keys (#25789)
|
||||
- feat(ext/crypto): X448 support (#26043)
|
||||
- feat(ext/kv): configurable limit params (#25174)
|
||||
- feat(ext/node): add abort helpers, process & streams fix (#25262)
|
||||
- feat(ext/node): add rootCertificates to node:tls (#25707)
|
||||
- feat(ext/node): buffer.transcode() (#25972)
|
||||
- feat(ext/node): export 'promises' symbol from 'node:timers' (#25589)
|
||||
- feat(ext/node): export missing constants from 'zlib' module (#25584)
|
||||
- feat(ext/node): export missing symbols from domain, puncode, repl, tls
|
||||
(#25585)
|
||||
- feat(ext/node): export more symbols from streams and timers/promises (#25582)
|
||||
- feat(ext/node): expose ES modules for _ modules (#25588)
|
||||
- feat(flags): allow double commas to escape values in path based flags (#25453)
|
||||
- feat(flags): support user provided args in repl subcommand (#25605)
|
||||
- feat(fmt): better error on malfored HTML files (#25853)
|
||||
- feat(fmt): stabilize CSS, HTML and YAML formatters (#25753)
|
||||
- feat(fmt): support vto and njk extensions (#25831)
|
||||
- feat(fmt): upgrade markup_fmt (#25768)
|
||||
- feat(install): deno install with entrypoint (#25411)
|
||||
- feat(install): warn repeatedly about not-run lifecycle scripts on explicit
|
||||
installs (#25878)
|
||||
- feat(lint): add `no-process-global` lint rule (#25709)
|
||||
- feat(lsp): add a message when someone runs 'deno lsp' manually (#26051)
|
||||
- feat(lsp): auto-import types with 'import type' (#25662)
|
||||
- feat(lsp): html/css/yaml file formatting (#25353)
|
||||
- feat(lsp): quick fix for @deno-types="npm:@types/*" (#25954)
|
||||
- feat(lsp): turn on useUnknownInCatchVariables (#25474)
|
||||
- feat(lsp): unstable setting as list (#25552)
|
||||
- feat(permissions): `Deno.mainModule` doesn't require permissions (#25667)
|
||||
- feat(permissions): allow importing from cdn.jsdelivr.net by default (#26013)
|
||||
- feat(serve): Support second parameter in deno serve (#25606)
|
||||
- feat(tools/doc): display subitems in symbol overviews where applicable
|
||||
(#25885)
|
||||
- feat(uninstall): alias to 'deno remove' if -g flag missing (#25461)
|
||||
- feat(upgrade): better error message on failure (#25503)
|
||||
- feat(upgrade): print info links for Deno 2 RC releases (#25225)
|
||||
- feat(upgrade): support LTS release channel (#25123)
|
||||
- fix: add link to env var docs (#25557)
|
||||
- fix: add suggestion how to fix importing CJS module (#21764)
|
||||
- fix: add test ensuring als works across dynamic import (#25593)
|
||||
- fix: better error for Deno.UnsafeWindowSurface, correct HttpClient name,
|
||||
cleanup unused code (#25833)
|
||||
- fix: cjs resolution cases (#25739)
|
||||
- fix: consistent with deno_config and treat `"experimentalDecorators"` as
|
||||
deprecated (#25735)
|
||||
- fix: delete old Deno 1.x headers file when loading cache (#25283)
|
||||
- fix: do not panic running invalid file specifier (#25530)
|
||||
- fix: don't include extensionless files in file collection for lint & fmt by
|
||||
default (#25721)
|
||||
- fix: don't prompt when using `Deno.permissions.request` with `--no-prompt`
|
||||
(#25811)
|
||||
- fix: eagerly error for specifier with empty version constraint (#25944)
|
||||
- fix: enable `Win32_Security` feature in `windows-sys` (#26007)
|
||||
- fix: error on unsupported compiler options (#25714)
|
||||
- fix: error out if a valid flag is passed before a subcommand (#25830)
|
||||
- fix: fix jupyter display function type (#25326)
|
||||
- fix: Float16Array type (#25506)
|
||||
- fix: handle showing warnings while the progress bar is shown (#25187)
|
||||
- fix: Hide 'deno cache' from help output (#25960)
|
||||
- fix: invalid ipv6 hostname on `deno serve` (#25482)
|
||||
- fix: linux canonicalization checks (#24641)
|
||||
- fix: lock down allow-run permissions more (#25370)
|
||||
- fix: make some warnings more standard (#25324)
|
||||
- fix: no cmd prefix in help output go links (#25459)
|
||||
- fix: only enable byonm if workspace root has pkg json (#25379)
|
||||
- fix: panic when require(esm) (#25769)
|
||||
- fix: precompile preserve SVG camelCase attributes (#25945)
|
||||
- fix: reland async context (#25140)
|
||||
- fix: remove --allow-run warning when using deno without args or subcommand
|
||||
(#25684)
|
||||
- fix: remove entrypoint hack for Deno 2.0 (#25332)
|
||||
- fix: remove recently added deno.json node_modules aliasing (#25542)
|
||||
- fix: remove the typo in the help message (#25962)
|
||||
- fix: removed unstable-htttp from deno help (#25216)
|
||||
- fix: replace `npm install` hint with `deno install` hint (#25244)
|
||||
- fix: trim space around DENO_AUTH_TOKENS (#25147)
|
||||
- fix: update deno_doc (#25290)
|
||||
- fix: Update deno_npm to fix `deno install` with crossws (#25837)
|
||||
- fix: update hint for `deno add <package>` (#25455)
|
||||
- fix: update malva in deno to support astro css comments (#25553)
|
||||
- fix: update nodeModulesDir config JSON schema (#25653)
|
||||
- fix: update patchver to 0.2 (#25952)
|
||||
- fix: update sui to 0.4 (#25942)
|
||||
- fix: upgrade deno_ast 0.42 (#25313)
|
||||
- fix: upgrade deno_core to 0.307.0 (#25287)
|
||||
- fix(add/install): default to "latest" tag for npm packages in
|
||||
`deno add npm:pkg` (#25858)
|
||||
- fix(bench): Fix table column alignments and NO_COLOR=1 (#25190)
|
||||
- fix(BREAKING): make dns record types have consistent naming (#25357)
|
||||
- fix(byonm): resolve npm deps of jsr deps (#25399)
|
||||
- fix(check): ignore noImplicitOverrides in remote modules (#25854)
|
||||
- fix(check): move is cjs check from resolving to loading (#25597)
|
||||
- fix(check): properly surface dependency errors in types file of js file
|
||||
(#25860)
|
||||
- fix(cli): `deno task` exit with status 0 (#25637)
|
||||
- fix(cli): Default to auto with --node-modules-dir flag (#25772)
|
||||
- fix(cli): handle edge cases around `export`s in doc tests and default export
|
||||
(#25720)
|
||||
- fix(cli): Map error kind to `PermissionDenied` when symlinking fails due to
|
||||
permissions (#25398)
|
||||
- fix(cli): Only set allow net flag for deno serve if not already allowed all
|
||||
(#25743)
|
||||
- fix(cli): Warn on not-run lifecycle scripts with global cache (#25786)
|
||||
- fix(cli/tools): correct `deno init --serve` template behavior (#25318)
|
||||
- fix(compile): support 'deno compile' in RC and LTS releases (#25875)
|
||||
- fix(config): validate export names (#25436)
|
||||
- fix(coverage): ignore urls from doc testing (#25736)
|
||||
- fix(doc): surface graph errors as warnings (#25888)
|
||||
- fix(dts): stabilize `fetch` declaration for use with `Deno.HttpClient`
|
||||
(#25683)
|
||||
- fix(ext/console): more precision in console.time (#25723)
|
||||
- fix(ext/console): prevent duplicate error printing when the cause is assigned
|
||||
(#25327)
|
||||
- fix(ext/crypto): ensure EC public keys are exported uncompressed (#25766)
|
||||
- fix(ext/crypto): fix identity test for x25519 derive bits (#26011)
|
||||
- fix(ext/crypto): reject empty usages in SubtleCrypto#importKey (#25759)
|
||||
- fix(ext/crypto): support md4 digest algorithm (#25656)
|
||||
- fix(ext/crypto): throw DataError for invalid EC key import (#25181)
|
||||
- fix(ext/fetch): fix lowercase http_proxy classified as https (#25686)
|
||||
- fix(ext/fetch): percent decode userinfo when parsing proxies (#25229)
|
||||
- fix(ext/http): do not set localhost to hostname unnecessarily (#24777)
|
||||
- fix(ext/http): gracefully handle Response.error responses (#25712)
|
||||
- fix(ext/node): add `FileHandle#writeFile` (#25555)
|
||||
- fix(ext/node): add `vm.constants` (#25630)
|
||||
- fix(ext/node): Add missing `node:path` exports (#25567)
|
||||
- fix(ext/node): Add missing node:fs and node:constants exports (#25568)
|
||||
- fix(ext/node): add stubs for `node:trace_events` (#25628)
|
||||
- fix(ext/node): attach console stream properties (#25617)
|
||||
- fix(ext/node): avoid showing `UNKNOWN` error from TCP handle (#25550)
|
||||
- fix(ext/node): close upgraded socket when the underlying http connection is
|
||||
closed (#25387)
|
||||
- fix(ext/node): delay accept() call 2 ticks in net.Server#listen (#25481)
|
||||
- fix(ext/node): don't throw error for unsupported signal binding on windows
|
||||
(#25699)
|
||||
- fix(ext/node): emit `online` event after worker thread is initialized (#25243)
|
||||
- fix(ext/node): export `process.allowedNodeEnvironmentFlags` (#25629)
|
||||
- fix(ext/node): export JWK public key (#25239)
|
||||
- fix(ext/node): export request and response clases from `http2` module (#25592)
|
||||
- fix(ext/node): fix `Cipheriv#update(string, undefined)` (#25571)
|
||||
- fix(ext/node): fix Decipheriv when autoPadding disabled (#25598)
|
||||
- fix(ext/node): fix process.stdin.pause() (#25864)
|
||||
- fix(ext/node): Fix vm sandbox object panic (#24985)
|
||||
- fix(ext/node): http2session ready state (#25143)
|
||||
- fix(ext/node): Implement detached option in `child_process` (#25218)
|
||||
- fix(ext/node): import EC JWK keys (#25266)
|
||||
- fix(ext/node): import JWK octet key pairs (#25180)
|
||||
- fix(ext/node): import RSA JWK keys (#25267)
|
||||
- fix(ext/node): register `node:wasi` built-in (#25134)
|
||||
- fix(ext/node): remove unimplemented promiseHook stubs (#25979)
|
||||
- fix(ext/node): report freemem() on Linux in bytes (#25511)
|
||||
- fix(ext/node): Rewrite `node:v8` serialize/deserialize (#25439)
|
||||
- fix(ext/node): session close during stream setup (#25170)
|
||||
- fix(ext/node): Stream should be instance of EventEmitter (#25527)
|
||||
- fix(ext/node): stub `inspector/promises` (#25635)
|
||||
- fix(ext/node): stub `process.cpuUsage()` (#25462)
|
||||
- fix(ext/node): stub cpu_info() for OpenBSD (#25807)
|
||||
- fix(ext/node): support x509 certificates in `createPublicKey` (#25731)
|
||||
- fix(ext/node): throw when loading `cpu-features` module (#25257)
|
||||
- fix(ext/node): update aead-gcm-stream to 0.3 (#25261)
|
||||
- fix(ext/node): use primordials in `ext/node/polyfills/console.ts` (#25572)
|
||||
- fix(ext/node): use primordials in ext/node/polyfills/wasi.ts (#25608)
|
||||
- fix(ext/node): validate input lengths in `Cipheriv` and `Decipheriv` (#25570)
|
||||
- fix(ext/web): don't ignore capture in EventTarget.removeEventListener (#25788)
|
||||
- fix(ext/webgpu): allow to build on unsupported platforms (#25202)
|
||||
- fix(ext/webgpu): sync category comment (#25580)
|
||||
- fix(ext/webstorage): make `getOwnPropertyDescriptor` with symbol return
|
||||
`undefined` (#13348)
|
||||
- fix(flags): --allow-all should conflict with lower permissions (#25909)
|
||||
- fix(flags): don't treat empty run command as task subcommand (#25708)
|
||||
- fix(flags): move some content from docs.deno.com into help output (#25951)
|
||||
- fix(flags): properly error out for urls (#25770)
|
||||
- fix(flags): require global flag for permission flags in install subcommand
|
||||
(#25391)
|
||||
- fix(fmt): --check was broken for CSS, YAML and HTML (#25848)
|
||||
- fix(fmt): fix incorrect quotes in components (#25249)
|
||||
- fix(fmt): fix tabs in YAML (#25536)
|
||||
- fix(fmt/markdown): fix regression with multi-line footnotes and inline math
|
||||
(#25222)
|
||||
- fix(info): error instead of panic for npm specifiers when using byonm (#25947)
|
||||
- fix(info): move "version" field to top of json output (#25890)
|
||||
- fix(inspector): Fix panic when re-entering runtime ops (#25537)
|
||||
- fix(install): compare versions directly to decide whether to create a child
|
||||
node_modules dir for a workspace member (#26001)
|
||||
- fix(install): Make sure target node_modules exists when symlinking (#25494)
|
||||
- fix(install): recommend using `deno install -g` when using a single http url
|
||||
(#25388)
|
||||
- fix(install): store tags associated with package in node_modules dir (#26000)
|
||||
- fix(install): surface package.json dependency errors (#26023)
|
||||
- fix(install): Use relative symlinks in deno install (#25164)
|
||||
- fix(installl): make bin entries executable even if not put in
|
||||
`node_modules/.bin` (#25873)
|
||||
- fix(jupyter): allow unstable flags (#25483)
|
||||
- fix(lint): correctly handle old jsx in linter (#25902)
|
||||
- fix(lint): support linting jsr pkg without version field (#25230)
|
||||
- fix(lockfile): use loose deserialization for version constraints (#25660)
|
||||
- fix(lsp): encode url parts before parsing as uri (#25509)
|
||||
- fix(lsp): exclude missing import quick fixes with bad resolutions (#26025)
|
||||
- fix(lsp): panic on url_to_uri() (#25238)
|
||||
- fix(lsp): properly resolve jsxImportSource for caching (#25688)
|
||||
- fix(lsp): update diagnostics on npm install (#25352)
|
||||
- fix(napi): Don't run microtasks in napi_resolve_deferred (#25246)
|
||||
- fix(napi): Fix worker threads importing already-loaded NAPI addon (#25245)
|
||||
- fix(no-slow-types): better `override` handling (#25989)
|
||||
- fix(node): Don't error out if we fail to statically analyze CJS re-export
|
||||
(#25748)
|
||||
- fix(node): fix worker_threads issues blocking Angular support (#26024)
|
||||
- fix(node): implement libuv APIs needed to support `npm:sqlite3` (#25893)
|
||||
- fix(node): Include "node" condition during CJS re-export analysis (#25785)
|
||||
- fix(node): Pass NPM_PROCESS_STATE to subprocesses via temp file instead of env
|
||||
var (#25896)
|
||||
- fix(node/byonm): do not accidentally resolve bare node built-ins (#25543)
|
||||
- fix(node/cluster): improve stubs to make log4js work (#25146)
|
||||
- fix(npm): better error handling for remote npm deps (#25670)
|
||||
- fix(npm): root package has peer dependency on itself (#26022)
|
||||
- fix(permissions): disallow any `LD_` or `DYLD_` prefixed env var without full
|
||||
--allow-run permissions (#25271)
|
||||
- fix(permissions): disallow launching subprocess with LD_PRELOAD env var
|
||||
without full run permissions (#25221)
|
||||
- fix(publish): ensure provenance is spec compliant (#25200)
|
||||
- fix(regression): do not expose resolved path in Deno.Command permission denied
|
||||
error (#25434)
|
||||
- fix(runtime): don't error `child.output()` on consumed stream (#25657)
|
||||
- fix(runtime): use more null proto objects again (#25040)
|
||||
- fix(runtime/web_worker): populate `SnapshotOptions` for `WebWorker` when
|
||||
instantiated without snapshot (#25280)
|
||||
- fix(task): correct name for scoped npm package binaries (#25390)
|
||||
- fix(task): support tasks with colons in name in `deno run` (#25233)
|
||||
- fix(task): use current executable for deno even when not named deno (#26019)
|
||||
- fix(types): simplify mtls related types (#25658)
|
||||
- fix(upgrade): more informative information on invalid version (#25319)
|
||||
- fix(windows): Deno.Command - align binary resolution with linux and mac
|
||||
(#25429)
|
||||
- fix(workspace): handle when config has members when specified via --config
|
||||
(#25988)
|
||||
- perf: fast path for cached dyn imports (#25636)
|
||||
- perf: Use -O3 for sui in release builds (#26010)
|
||||
- perf(cache): single cache file for remote modules (#24983)
|
||||
- perf(cache): single cache file for typescript emit (#24994)
|
||||
- perf(ext/fetch): improve decompression throughput by upgrading `tower_http`
|
||||
(#25806)
|
||||
- perf(ext/node): reduce some allocations in require (#25197)
|
||||
- perf(ext/web): optimize performance.measure() (#25774)
|
||||
|
||||
### 1.46.3 / 2024.09.04
|
||||
|
||||
- feat(upgrade): print info links for Deno 2 RC releases (#25225)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno_bench_util"
|
||||
version = "0.162.0"
|
||||
version = "0.167.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "deno"
|
||||
version = "2.0.0-rc.0"
|
||||
version = "2.0.2"
|
||||
authors.workspace = true
|
||||
default-run = "deno"
|
||||
edition.workspace = true
|
||||
|
@ -38,6 +38,11 @@ path = "./bench/lsp_bench_standalone.rs"
|
|||
|
||||
[features]
|
||||
default = ["upgrade", "__vendored_zlib_ng"]
|
||||
# A feature that enables heap profiling with dhat on Linux.
|
||||
# 1. Compile with `cargo build --profile=release-with-debug --features=dhat-heap`
|
||||
# 2. Run the executable. It will output a dhat-heap.json file.
|
||||
# 3. Open the json file in https://nnethercote.github.io/dh_view/dh_view.html
|
||||
dhat-heap = ["dhat"]
|
||||
# A feature that enables the upgrade subcommand and the background check for
|
||||
# available updates (of deno binary). This is typically disabled for (Linux)
|
||||
# distribution packages.
|
||||
|
@ -65,26 +70,27 @@ 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.33.1", features = ["workspace", "sync"] }
|
||||
deno_config = { version = "=0.37.1", features = ["workspace", "sync"] }
|
||||
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
|
||||
deno_doc = { version = "0.148.0", features = ["html", "syntect"] }
|
||||
deno_graph = { version = "=0.82.0" }
|
||||
deno_lint = { version = "=0.64.0", features = ["docs"] }
|
||||
deno_doc = { version = "0.154.0", default-features = false, features = ["rust", "html", "syntect"] }
|
||||
deno_graph = { version = "=0.83.3" }
|
||||
deno_lint = { version = "=0.67.0", features = ["docs"] }
|
||||
deno_lockfile.workspace = true
|
||||
deno_npm = "=0.25.0"
|
||||
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.workspace = true
|
||||
deno_task_shell = "=0.17.0"
|
||||
deno_task_shell = "=0.18.1"
|
||||
deno_terminal.workspace = true
|
||||
eszip = "=0.78.0"
|
||||
libsui = "0.3.0"
|
||||
eszip = "=0.79.1"
|
||||
libsui = "0.4.0"
|
||||
napi_sym.workspace = true
|
||||
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
|
||||
|
@ -93,16 +99,17 @@ chrono = { workspace = true, features = ["now"] }
|
|||
clap = { version = "=4.5.16", features = ["env", "string", "wrap_help", "error-context"] }
|
||||
clap_complete = "=4.5.24"
|
||||
clap_complete_fig = "=4.5.2"
|
||||
color-print = "0.3.5"
|
||||
color-print.workspace = true
|
||||
console_static_text.workspace = true
|
||||
dashmap = "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.8"
|
||||
dprint-plugin-typescript = "=0.91.7"
|
||||
dprint-plugin-typescript = "=0.93.0"
|
||||
env_logger = "=0.10.0"
|
||||
fancy-regex = "=0.10.0"
|
||||
faster-hex.workspace = true
|
||||
|
@ -123,8 +130,8 @@ libc.workspace = true
|
|||
libz-sys.workspace = true
|
||||
log = { workspace = true, features = ["serde"] }
|
||||
lsp-types.workspace = true
|
||||
malva = "=0.9.0"
|
||||
markup_fmt = "=0.12.0"
|
||||
malva = "=0.10.1"
|
||||
markup_fmt = "=0.13.1"
|
||||
memmem.workspace = true
|
||||
monch.workspace = true
|
||||
notify.workspace = true
|
||||
|
@ -134,7 +141,7 @@ p256.workspace = true
|
|||
pathdiff = "0.2.1"
|
||||
percent-encoding.workspace = true
|
||||
phf.workspace = true
|
||||
pretty_yaml = "=0.4.0"
|
||||
pretty_yaml = "=0.5.0"
|
||||
quick-junit = "^0.3.5"
|
||||
rand = { workspace = true, features = ["small_rng"] }
|
||||
regex.workspace = true
|
||||
|
@ -161,6 +168,7 @@ typed-arena = "=2.0.2"
|
|||
uuid = { workspace = true, features = ["serde"] }
|
||||
walkdir = "=2.3.2"
|
||||
which.workspace = true
|
||||
yoke.workspace = true
|
||||
zeromq.workspace = true
|
||||
zip = { version = "2.1.6", default-features = false, features = ["deflate-flate2"] }
|
||||
zstd.workspace = true
|
||||
|
@ -168,12 +176,14 @@ zstd.workspace = true
|
|||
[target.'cfg(windows)'.dependencies]
|
||||
junction.workspace = true
|
||||
winapi = { workspace = true, features = ["knownfolders", "mswsock", "objbase", "shlobj", "tlhelp32", "winbase", "winerror", "winsock2"] }
|
||||
windows-sys.workspace = true
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
nix.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
deno_bench_util.workspace = true
|
||||
libuv-sys-lite = "=1.48.2"
|
||||
pretty_assertions.workspace = true
|
||||
test_util.workspace = true
|
||||
|
||||
|
|
1963
cli/args/flags.rs
1963
cli/args/flags.rs
File diff suppressed because it is too large
Load diff
|
@ -50,7 +50,7 @@ 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| {
|
||||
NetDescriptor::parse(&host_and_port).map_err(|e| {
|
||||
clap::Error::raw(clap::error::ErrorKind::InvalidValue, format!("{e:?}"))
|
||||
})?;
|
||||
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)?)
|
||||
|
|
|
@ -24,11 +24,20 @@ use crate::args::InstallKind;
|
|||
|
||||
use deno_lockfile::Lockfile;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CliLockfileReadFromPathOptions {
|
||||
pub file_path: PathBuf,
|
||||
pub frozen: bool,
|
||||
/// Causes the lockfile to only be read from, but not written to.
|
||||
pub skip_write: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CliLockfile {
|
||||
lockfile: Mutex<Lockfile>,
|
||||
pub filename: PathBuf,
|
||||
pub frozen: bool,
|
||||
frozen: bool,
|
||||
skip_write: bool,
|
||||
}
|
||||
|
||||
pub struct Guard<'a, T> {
|
||||
|
@ -50,15 +59,6 @@ impl<'a, T> std::ops::DerefMut for Guard<'a, T> {
|
|||
}
|
||||
|
||||
impl CliLockfile {
|
||||
pub fn new(lockfile: Lockfile, frozen: bool) -> Self {
|
||||
let filename = lockfile.filename.clone();
|
||||
Self {
|
||||
lockfile: Mutex::new(lockfile),
|
||||
filename,
|
||||
frozen,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the inner deno_lockfile::Lockfile.
|
||||
pub fn lock(&self) -> Guard<Lockfile> {
|
||||
Guard {
|
||||
|
@ -78,6 +78,10 @@ impl CliLockfile {
|
|||
}
|
||||
|
||||
pub fn write_if_changed(&self) -> Result<(), AnyError> {
|
||||
if self.skip_write {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.error_if_changed()?;
|
||||
let mut lockfile = self.lockfile.lock();
|
||||
let Some(bytes) = lockfile.resolve_write_bytes() else {
|
||||
|
@ -142,7 +146,7 @@ impl CliLockfile {
|
|||
return Ok(None);
|
||||
}
|
||||
|
||||
let filename = match flags.lock {
|
||||
let file_path = match flags.lock {
|
||||
Some(ref lock) => PathBuf::from(lock),
|
||||
None => match workspace.resolve_lockfile_path()? {
|
||||
Some(path) => path,
|
||||
|
@ -160,7 +164,11 @@ impl CliLockfile {
|
|||
.unwrap_or(false)
|
||||
});
|
||||
|
||||
let lockfile = Self::read_from_path(filename, frozen)?;
|
||||
let lockfile = Self::read_from_path(CliLockfileReadFromPathOptions {
|
||||
file_path,
|
||||
frozen,
|
||||
skip_write: flags.internal.lockfile_skip_write,
|
||||
})?;
|
||||
|
||||
// initialize the lockfile with the workspace's configuration
|
||||
let root_url = workspace.root_dir();
|
||||
|
@ -212,25 +220,29 @@ impl CliLockfile {
|
|||
}
|
||||
|
||||
pub fn read_from_path(
|
||||
file_path: PathBuf,
|
||||
frozen: bool,
|
||||
opts: CliLockfileReadFromPathOptions,
|
||||
) -> Result<CliLockfile, AnyError> {
|
||||
match std::fs::read_to_string(&file_path) {
|
||||
Ok(text) => Ok(CliLockfile::new(
|
||||
Lockfile::new(deno_lockfile::NewLockfileOptions {
|
||||
file_path,
|
||||
content: &text,
|
||||
overwrite: false,
|
||||
})?,
|
||||
frozen,
|
||||
)),
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(
|
||||
CliLockfile::new(Lockfile::new_empty(file_path, false), frozen),
|
||||
),
|
||||
Err(err) => Err(err).with_context(|| {
|
||||
format!("Failed reading lockfile '{}'", file_path.display())
|
||||
}),
|
||||
}
|
||||
let lockfile = match std::fs::read_to_string(&opts.file_path) {
|
||||
Ok(text) => Lockfile::new(deno_lockfile::NewLockfileOptions {
|
||||
file_path: opts.file_path,
|
||||
content: &text,
|
||||
overwrite: false,
|
||||
})?,
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
|
||||
Lockfile::new_empty(opts.file_path, false)
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(err).with_context(|| {
|
||||
format!("Failed reading lockfile '{}'", opts.file_path.display())
|
||||
});
|
||||
}
|
||||
};
|
||||
Ok(CliLockfile {
|
||||
filename: lockfile.filename.clone(),
|
||||
lockfile: Mutex::new(lockfile),
|
||||
frozen: opts.frozen,
|
||||
skip_write: opts.skip_write,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn error_if_changed(&self) -> Result<(), AnyError> {
|
||||
|
@ -246,7 +258,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 `deno cache --frozen=false`, `deno install --frozen=false`, 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(())
|
||||
|
|
251
cli/args/mod.rs
251
cli/args/mod.rs
|
@ -20,14 +20,13 @@ 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_semver::npm::NpmPackageReqReference;
|
||||
use import_map::resolve_import_map_value_from_specifier;
|
||||
|
||||
|
@ -45,6 +44,7 @@ pub use deno_config::glob::FilePatterns;
|
|||
pub use deno_json::check_warn_tsconfig;
|
||||
pub use flags::*;
|
||||
pub use lockfile::CliLockfile;
|
||||
pub use lockfile::CliLockfileReadFromPathOptions;
|
||||
pub use package_json::NpmInstallDepsProvider;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
|
@ -70,6 +70,8 @@ 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;
|
||||
|
@ -282,10 +284,7 @@ impl BenchOptions {
|
|||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
|
||||
pub struct UnstableFmtOptions {
|
||||
pub css: bool,
|
||||
pub html: bool,
|
||||
pub component: bool,
|
||||
pub yaml: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -318,10 +317,7 @@ impl FmtOptions {
|
|||
Self {
|
||||
options: resolve_fmt_options(fmt_flags, fmt_config.options),
|
||||
unstable: UnstableFmtOptions {
|
||||
css: unstable.css || fmt_flags.unstable_css,
|
||||
html: unstable.html || fmt_flags.unstable_html,
|
||||
component: unstable.component || fmt_flags.unstable_component,
|
||||
yaml: unstable.yaml || fmt_flags.unstable_yaml,
|
||||
},
|
||||
files: fmt_config.files,
|
||||
}
|
||||
|
@ -749,15 +745,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)
|
||||
});
|
||||
|
||||
|
@ -776,6 +790,7 @@ 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>>,
|
||||
|
@ -812,7 +827,7 @@ impl CliOptions {
|
|||
|
||||
let maybe_lockfile = maybe_lockfile.filter(|_| !force_global_cache);
|
||||
let deno_dir_provider =
|
||||
Arc::new(DenoDirProvider::new(flags.cache_path.clone()));
|
||||
Arc::new(DenoDirProvider::new(flags.internal.cache_path.clone()));
|
||||
let maybe_node_modules_folder = resolve_node_modules_folder(
|
||||
&initial_cwd,
|
||||
&flags,
|
||||
|
@ -830,6 +845,7 @@ impl CliOptions {
|
|||
npmrc,
|
||||
maybe_node_modules_folder,
|
||||
overrides: Default::default(),
|
||||
main_module_cell: std::sync::OnceLock::new(),
|
||||
start_dir,
|
||||
deno_dir_provider,
|
||||
})
|
||||
|
@ -1070,27 +1086,13 @@ impl CliOptions {
|
|||
None => None,
|
||||
}
|
||||
};
|
||||
Ok(
|
||||
self
|
||||
.workspace()
|
||||
.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> {
|
||||
|
@ -1124,40 +1126,39 @@ impl CliOptions {
|
|||
self.flags.env_file.as_ref()
|
||||
}
|
||||
|
||||
pub fn resolve_main_module(&self) -> Result<ModuleSpecifier, AnyError> {
|
||||
let main_module = 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", 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 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.")
|
||||
}
|
||||
};
|
||||
pub fn resolve_main_module(&self) -> Result<&ModuleSpecifier, AnyError> {
|
||||
self
|
||||
.main_module_cell
|
||||
.get_or_init(|| {
|
||||
let main_module = 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.ts", self.initial_cwd())?
|
||||
}
|
||||
DenoSubcommand::Repl(_) => {
|
||||
resolve_url_or_path("./$deno$repl.ts", self.initial_cwd())?
|
||||
}
|
||||
DenoSubcommand::Run(run_flags) => {
|
||||
if run_flags.is_stdin() {
|
||||
resolve_url_or_path("./$deno$stdin.ts", self.initial_cwd())?
|
||||
} 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)
|
||||
Ok(main_module)
|
||||
})
|
||||
.as_ref()
|
||||
.map_err(|err| deno_core::anyhow::anyhow!("{}", err))
|
||||
}
|
||||
|
||||
pub fn resolve_file_header_overrides(
|
||||
|
@ -1178,7 +1179,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 {
|
||||
|
@ -1300,10 +1301,7 @@ impl CliOptions {
|
|||
pub fn resolve_config_unstable_fmt_options(&self) -> UnstableFmtOptions {
|
||||
let workspace = self.workspace();
|
||||
UnstableFmtOptions {
|
||||
css: workspace.has_unstable("fmt-css"),
|
||||
html: workspace.has_unstable("fmt-html"),
|
||||
component: workspace.has_unstable("fmt-component"),
|
||||
yaml: workspace.has_unstable("fmt-yaml"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1344,11 +1342,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()),
|
||||
})
|
||||
}
|
||||
|
@ -1501,8 +1497,35 @@ 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)]),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or_default();
|
||||
self.flags.permissions.to_options(&cli_arg_urls)
|
||||
}
|
||||
|
||||
pub fn reload_flag(&self) -> bool {
|
||||
|
@ -1548,15 +1571,16 @@ 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 unstable_detect_cjs(&self) -> bool {
|
||||
self.flags.unstable_config.detect_cjs
|
||||
|| self.workspace().has_unstable("detect-cjs")
|
||||
}
|
||||
|
||||
fn byonm_enabled(&self) -> bool {
|
||||
// check if enabled via unstable
|
||||
self.node_modules_dir().ok().flatten() == Some(NodeModulesDirMode::Manual)
|
||||
|
@ -1600,37 +1624,18 @@ impl CliOptions {
|
|||
}
|
||||
});
|
||||
|
||||
// TODO(2.0): remove this code and enable these features in `99_main.js` by default.
|
||||
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.name)
|
||||
.collect();
|
||||
|
||||
let mut another_unstable_flags = Vec::from([
|
||||
"sloppy-imports",
|
||||
"byonm",
|
||||
"bare-node-builtins",
|
||||
"fmt-css",
|
||||
"fmt-html",
|
||||
"fmt-component",
|
||||
"fmt-yaml",
|
||||
]);
|
||||
// add more unstable flags to the same vector holding granular flags
|
||||
all_valid_unstable_flags.append(&mut another_unstable_flags);
|
||||
let all_valid_unstable_flags: Vec<&str> = crate::UNSTABLE_GRANULAR_FLAGS
|
||||
.iter()
|
||||
.map(|granular_flag| granular_flag.name)
|
||||
.chain([
|
||||
"sloppy-imports",
|
||||
"byonm",
|
||||
"bare-node-builtins",
|
||||
"fmt-component",
|
||||
"detect-cjs",
|
||||
])
|
||||
.collect();
|
||||
|
||||
// check and warn if the unstable flag of config file isn't supported, by
|
||||
// iterating through the vector holding the unstable flags
|
||||
|
@ -1693,14 +1698,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(_)
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,25 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_config::workspace::Workspace;
|
||||
use deno_core::serde_json;
|
||||
use deno_package_json::PackageJsonDepValue;
|
||||
use deno_package_json::PackageJsonDepValueParseError;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use deno_semver::package::PackageReq;
|
||||
|
||||
use crate::util::path::is_banned_path_char;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InstallNpmRemotePkg {
|
||||
pub alias: String,
|
||||
pub alias: Option<String>,
|
||||
pub base_dir: PathBuf,
|
||||
pub req: PackageReq,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InstallNpmWorkspacePkg {
|
||||
pub alias: String,
|
||||
pub alias: Option<String>,
|
||||
pub target_dir: PathBuf,
|
||||
}
|
||||
|
||||
|
@ -29,6 +27,7 @@ pub struct InstallNpmWorkspacePkg {
|
|||
pub struct NpmInstallDepsProvider {
|
||||
remote_pkgs: Vec<InstallNpmRemotePkg>,
|
||||
workspace_pkgs: Vec<InstallNpmWorkspacePkg>,
|
||||
pkg_json_dep_errors: Vec<PackageJsonDepValueParseError>,
|
||||
}
|
||||
|
||||
impl NpmInstallDepsProvider {
|
||||
|
@ -40,19 +39,17 @@ impl NpmInstallDepsProvider {
|
|||
// todo(dsherret): estimate capacity?
|
||||
let mut workspace_pkgs = Vec::new();
|
||||
let mut remote_pkgs = Vec::new();
|
||||
let mut pkg_json_dep_errors = Vec::new();
|
||||
let workspace_npm_pkgs = workspace.npm_packages();
|
||||
|
||||
for (_, folder) in workspace.config_folders() {
|
||||
let mut deno_json_aliases = HashSet::new();
|
||||
|
||||
// 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 {
|
||||
deno_json_aliases.reserve(obj.len());
|
||||
let mut pkg_pkgs = Vec::with_capacity(obj.len());
|
||||
for (alias, value) in obj {
|
||||
for (_alias, value) in obj {
|
||||
let serde_json::Value::String(specifier) = value else {
|
||||
continue;
|
||||
};
|
||||
|
@ -60,11 +57,6 @@ impl NpmInstallDepsProvider {
|
|||
else {
|
||||
continue;
|
||||
};
|
||||
// skip any aliases with banned characters
|
||||
if alias.chars().any(|c| c == '\\' || is_banned_path_char(c)) {
|
||||
continue;
|
||||
}
|
||||
deno_json_aliases.insert(alias.to_lowercase());
|
||||
let pkg_req = npm_req_ref.into_inner().req;
|
||||
let workspace_pkg = workspace_npm_pkgs
|
||||
.iter()
|
||||
|
@ -72,12 +64,12 @@ impl NpmInstallDepsProvider {
|
|||
|
||||
if let Some(pkg) = workspace_pkg {
|
||||
workspace_pkgs.push(InstallNpmWorkspacePkg {
|
||||
alias: alias.to_string(),
|
||||
alias: None,
|
||||
target_dir: pkg.pkg_json.dir_path().to_path_buf(),
|
||||
});
|
||||
} else {
|
||||
pkg_pkgs.push(InstallNpmRemotePkg {
|
||||
alias: alias.to_string(),
|
||||
alias: None,
|
||||
base_dir: deno_json.dir_path(),
|
||||
req: pkg_req,
|
||||
});
|
||||
|
@ -85,7 +77,7 @@ impl NpmInstallDepsProvider {
|
|||
}
|
||||
|
||||
// sort within each package (more like npm resolution)
|
||||
pkg_pkgs.sort_by(|a, b| a.alias.cmp(&b.alias));
|
||||
pkg_pkgs.sort_by(|a, b| a.req.cmp(&b.req));
|
||||
remote_pkgs.extend(pkg_pkgs);
|
||||
}
|
||||
}
|
||||
|
@ -94,14 +86,13 @@ impl NpmInstallDepsProvider {
|
|||
let deps = pkg_json.resolve_local_package_json_deps();
|
||||
let mut pkg_pkgs = Vec::with_capacity(deps.len());
|
||||
for (alias, dep) in deps {
|
||||
let Ok(dep) = dep else {
|
||||
continue;
|
||||
let dep = match dep {
|
||||
Ok(dep) => dep,
|
||||
Err(err) => {
|
||||
pkg_json_dep_errors.push(err);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if deno_json_aliases.contains(&alias.to_lowercase()) {
|
||||
// aliases in deno.json take precedence over package.json, so
|
||||
// since this can't be resolved don't bother installing it
|
||||
continue;
|
||||
}
|
||||
match dep {
|
||||
PackageJsonDepValue::Req(pkg_req) => {
|
||||
let workspace_pkg = workspace_npm_pkgs.iter().find(|pkg| {
|
||||
|
@ -112,12 +103,12 @@ impl NpmInstallDepsProvider {
|
|||
|
||||
if let Some(pkg) = workspace_pkg {
|
||||
workspace_pkgs.push(InstallNpmWorkspacePkg {
|
||||
alias,
|
||||
alias: Some(alias),
|
||||
target_dir: pkg.pkg_json.dir_path().to_path_buf(),
|
||||
});
|
||||
} else {
|
||||
pkg_pkgs.push(InstallNpmRemotePkg {
|
||||
alias,
|
||||
alias: Some(alias),
|
||||
base_dir: pkg_json.dir_path().to_path_buf(),
|
||||
req: pkg_req,
|
||||
});
|
||||
|
@ -128,7 +119,7 @@ impl NpmInstallDepsProvider {
|
|||
pkg.matches_name_and_version_req(&alias, &version_req)
|
||||
}) {
|
||||
workspace_pkgs.push(InstallNpmWorkspacePkg {
|
||||
alias,
|
||||
alias: Some(alias),
|
||||
target_dir: pkg.pkg_json.dir_path().to_path_buf(),
|
||||
});
|
||||
}
|
||||
|
@ -147,14 +138,19 @@ impl NpmInstallDepsProvider {
|
|||
Self {
|
||||
remote_pkgs,
|
||||
workspace_pkgs,
|
||||
pkg_json_dep_errors,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remote_pkgs(&self) -> &Vec<InstallNpmRemotePkg> {
|
||||
pub fn remote_pkgs(&self) -> &[InstallNpmRemotePkg] {
|
||||
&self.remote_pkgs
|
||||
}
|
||||
|
||||
pub fn workspace_pkgs(&self) -> &Vec<InstallNpmWorkspacePkg> {
|
||||
pub fn workspace_pkgs(&self) -> &[InstallNpmWorkspacePkg] {
|
||||
&self.workspace_pkgs
|
||||
}
|
||||
|
||||
pub fn pkg_json_dep_errors(&self) -> &[PackageJsonDepValueParseError] {
|
||||
&self.pkg_json_dep_errors
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ pub fn benchmark(
|
|||
}
|
||||
|
||||
let port = get_port();
|
||||
// deno run -A --unstable <path> <addr>
|
||||
// deno run -A --unstable-net <path> <addr>
|
||||
res.insert(
|
||||
file_stem.to_string(),
|
||||
run(
|
||||
|
@ -57,7 +57,7 @@ pub fn benchmark(
|
|||
deno_exe.as_str(),
|
||||
"run",
|
||||
"--allow-all",
|
||||
"--unstable",
|
||||
"--unstable-net",
|
||||
"--enable-testing-features-do-not-use",
|
||||
path,
|
||||
&server_addr(port),
|
||||
|
|
|
@ -150,7 +150,11 @@ fn bench_big_file_edits(deno_exe: &Path) -> Duration {
|
|||
.deno_exe(deno_exe)
|
||||
.build();
|
||||
client.initialize_default();
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
client.change_configuration(json!({ "deno": { "enable": true } }));
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
|
||||
client.write_notification(
|
||||
"textDocument/didOpen",
|
||||
|
@ -206,6 +210,8 @@ fn bench_code_lens(deno_exe: &Path) -> Duration {
|
|||
.deno_exe(deno_exe)
|
||||
.build();
|
||||
client.initialize_default();
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
client.change_configuration(json!({ "deno": {
|
||||
"enable": true,
|
||||
"codeLens": {
|
||||
|
@ -214,6 +220,8 @@ fn bench_code_lens(deno_exe: &Path) -> Duration {
|
|||
"test": true,
|
||||
},
|
||||
} }));
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
|
||||
client.write_notification(
|
||||
"textDocument/didOpen",
|
||||
|
@ -257,7 +265,11 @@ fn bench_find_replace(deno_exe: &Path) -> Duration {
|
|||
.deno_exe(deno_exe)
|
||||
.build();
|
||||
client.initialize_default();
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
client.change_configuration(json!({ "deno": { "enable": true } }));
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
|
||||
for i in 0..10 {
|
||||
client.write_notification(
|
||||
|
@ -341,7 +353,11 @@ fn bench_startup_shutdown(deno_exe: &Path) -> Duration {
|
|||
.deno_exe(deno_exe)
|
||||
.build();
|
||||
client.initialize_default();
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
client.change_configuration(json!({ "deno": { "enable": true } }));
|
||||
let (method, _): (String, Option<Value>) = client.read_notification();
|
||||
assert_eq!(method, "deno/didRefreshDenoConfigurationTree");
|
||||
|
||||
client.write_notification(
|
||||
"textDocument/didOpen",
|
||||
|
|
|
@ -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",
|
||||
|
|
12
cli/build.rs
12
cli/build.rs
|
@ -13,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;
|
||||
|
@ -25,7 +24,6 @@ mod ts {
|
|||
struct BuildInfoResponse {
|
||||
build_specifier: String,
|
||||
libs: Vec<String>,
|
||||
node_built_in_module_names: Vec<String>,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
|
@ -37,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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -243,6 +236,7 @@ mod ts {
|
|||
"esnext.decorators",
|
||||
"esnext.disposable",
|
||||
"esnext.intl",
|
||||
"esnext.iterator",
|
||||
"esnext.object",
|
||||
"esnext.promise",
|
||||
"esnext.regexp",
|
||||
|
@ -393,6 +387,8 @@ fn main() {
|
|||
"Missing symbols list! Generate using tools/napi/generate_symbols_lists.js",
|
||||
);
|
||||
|
||||
println!("cargo:rustc-rerun-if-changed={}", symbols_path.display());
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
println!(
|
||||
"cargo:rustc-link-arg-bin=deno=/DEF:{}",
|
||||
|
@ -446,7 +442,7 @@ 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");
|
||||
|
||||
|
|
13
cli/cache/code_cache.rs
vendored
13
cli/cache/code_cache.rs
vendored
|
@ -80,10 +80,6 @@ impl CodeCache {
|
|||
data,
|
||||
));
|
||||
}
|
||||
|
||||
pub fn remove_code_cache(&self, specifier: &str) {
|
||||
Self::ensure_ok(self.inner.remove_code_cache(specifier))
|
||||
}
|
||||
}
|
||||
|
||||
impl code_cache::CodeCache for CodeCache {
|
||||
|
@ -162,15 +158,6 @@ impl CodeCacheInner {
|
|||
self.conn.execute(sql, params)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_code_cache(&self, specifier: &str) -> Result<(), AnyError> {
|
||||
let sql = "
|
||||
DELETE FROM codecache
|
||||
WHERE specifier=$1;";
|
||||
let params = params![specifier];
|
||||
self.conn.execute(sql, params)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_code_cache_type(
|
||||
|
|
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.
|
||||
|
|
13
cli/cache/emit.rs
vendored
13
cli/cache/emit.rs
vendored
|
@ -82,19 +82,6 @@ impl EmitCache {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// 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)?),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_emit_filename(&self, specifier: &ModuleSpecifier) -> Option<PathBuf> {
|
||||
self
|
||||
.disk_cache
|
||||
|
|
223
cli/cache/mod.rs
vendored
223
cli/cache/mod.rs
vendored
|
@ -1,13 +1,21 @@
|
|||
// 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::resolver::CliNodeResolver;
|
||||
use crate::util::fs::atomic_write_file_with_retries;
|
||||
use crate::util::fs::atomic_write_file_with_retries_and_fs;
|
||||
use crate::util::fs::AtomicWriteFileFsAdapter;
|
||||
use crate::util::path::specifier_has_extension;
|
||||
use crate::util::text_encoding::arc_str_to_bytes;
|
||||
use crate::util::text_encoding::from_utf8_lossy_owned;
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_core::futures;
|
||||
|
@ -52,6 +60,7 @@ pub use fast_check::FastCheckCache;
|
|||
pub use incremental::IncrementalCache;
|
||||
pub use module_info::ModuleInfoCache;
|
||||
pub use node::NodeAnalysisCache;
|
||||
pub use parsed_source::EsmOrCjsChecker;
|
||||
pub use parsed_source::LazyGraphSourceParser;
|
||||
pub use parsed_source::ParsedSourceCache;
|
||||
|
||||
|
@ -74,8 +83,12 @@ impl deno_cache_dir::DenoCacheEnv for RealDenoCacheEnv {
|
|||
atomic_write_file_with_retries(path, bytes, CACHE_PERM)
|
||||
}
|
||||
|
||||
fn remove_file(&self, path: &Path) -> std::io::Result<()> {
|
||||
std::fs::remove_file(path)
|
||||
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>> {
|
||||
|
@ -97,43 +110,117 @@ 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,
|
||||
pub unstable_detect_cjs: bool,
|
||||
}
|
||||
|
||||
/// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides
|
||||
/// a concise interface to the DENO_DIR when building module graphs.
|
||||
pub struct FetchCacher {
|
||||
emit_cache: Arc<EmitCache>,
|
||||
pub file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
|
||||
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
permissions: PermissionsContainer,
|
||||
is_deno_publish: bool,
|
||||
unstable_detect_cjs: bool,
|
||||
cache_info_enabled: bool,
|
||||
}
|
||||
|
||||
impl FetchCacher {
|
||||
pub fn new(
|
||||
emit_cache: Arc<EmitCache>,
|
||||
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
permissions: PermissionsContainer,
|
||||
options: FetchCacherOptions,
|
||||
) -> Self {
|
||||
Self {
|
||||
emit_cache,
|
||||
file_fetcher,
|
||||
file_header_overrides,
|
||||
esm_or_cjs_checker,
|
||||
global_http_cache,
|
||||
node_resolver,
|
||||
npm_resolver,
|
||||
module_info_cache,
|
||||
permissions,
|
||||
file_header_overrides: options.file_header_overrides,
|
||||
permissions: options.permissions,
|
||||
is_deno_publish: options.is_deno_publish,
|
||||
unstable_detect_cjs: options.unstable_detect_cjs,
|
||||
cache_info_enabled: false,
|
||||
}
|
||||
}
|
||||
|
@ -144,15 +231,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 +258,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
|
||||
}
|
||||
|
@ -200,27 +271,90 @@ impl Loader for FetchCacher {
|
|||
) -> LoadFuture {
|
||||
use deno_graph::source::CacheSetting as LoaderCacheSetting;
|
||||
|
||||
if specifier.scheme() == "file"
|
||||
&& specifier.path().contains("/node_modules/")
|
||||
{
|
||||
// The specifier might be in a completely different symlinked tree than
|
||||
// what the node_modules url is in (ex. `/my-project-1/node_modules`
|
||||
// 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) {
|
||||
if specifier.scheme() == "file" {
|
||||
if specifier.path().contains("/node_modules/") {
|
||||
// The specifier might be in a completely different symlinked tree than
|
||||
// what the node_modules url is in (ex. `/my-project-1/node_modules`
|
||||
// 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) {
|
||||
return Box::pin(futures::future::ready(Ok(Some(
|
||||
LoadResponse::External { specifier },
|
||||
))));
|
||||
}
|
||||
}
|
||||
|
||||
// make local CJS modules external to the graph
|
||||
if specifier_has_extension(specifier, "cjs") {
|
||||
return Box::pin(futures::future::ready(Ok(Some(
|
||||
LoadResponse::External { specifier },
|
||||
LoadResponse::External {
|
||||
specifier: specifier.clone(),
|
||||
},
|
||||
))));
|
||||
}
|
||||
|
||||
if self.unstable_detect_cjs && specifier_has_extension(specifier, "js") {
|
||||
if let Ok(Some(pkg_json)) =
|
||||
self.node_resolver.get_closest_package_json(specifier)
|
||||
{
|
||||
if pkg_json.typ == "commonjs" {
|
||||
if let Ok(path) = specifier.to_file_path() {
|
||||
if let Ok(bytes) = std::fs::read(&path) {
|
||||
let text: Arc<str> = from_utf8_lossy_owned(bytes).into();
|
||||
let is_es_module = match self.esm_or_cjs_checker.is_esm(
|
||||
specifier,
|
||||
text.clone(),
|
||||
MediaType::JavaScript,
|
||||
) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
return Box::pin(futures::future::ready(Err(err.into())));
|
||||
}
|
||||
};
|
||||
if !is_es_module {
|
||||
self.node_resolver.mark_cjs_resolution(specifier.clone());
|
||||
return Box::pin(futures::future::ready(Ok(Some(
|
||||
LoadResponse::External {
|
||||
specifier: specifier.clone(),
|
||||
},
|
||||
))));
|
||||
} else {
|
||||
return Box::pin(futures::future::ready(Ok(Some(
|
||||
LoadResponse::Module {
|
||||
specifier: specifier.clone(),
|
||||
content: arc_str_to_bytes(text),
|
||||
maybe_headers: None,
|
||||
},
|
||||
))));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.is_deno_publish
|
||||
&& 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 +373,11 @@ 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_accept: None,
|
||||
maybe_cache_setting: maybe_cache_setting.as_ref(),
|
||||
},
|
||||
|
@ -293,6 +431,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 +439,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,
|
||||
);
|
||||
|
|
40
cli/cache/parsed_source.rs
vendored
40
cli/cache/parsed_source.rs
vendored
|
@ -5,6 +5,7 @@ use std::sync::Arc;
|
|||
|
||||
use deno_ast::MediaType;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_ast::ParseDiagnostic;
|
||||
use deno_ast::ParsedSource;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_graph::CapturingModuleParser;
|
||||
|
@ -149,3 +150,42 @@ impl deno_graph::ParsedSourceStore for ParsedSourceCache {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EsmOrCjsChecker {
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
}
|
||||
|
||||
impl EsmOrCjsChecker {
|
||||
pub fn new(parsed_source_cache: Arc<ParsedSourceCache>) -> Self {
|
||||
Self {
|
||||
parsed_source_cache,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_esm(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
source: Arc<str>,
|
||||
media_type: MediaType,
|
||||
) -> Result<bool, ParseDiagnostic> {
|
||||
// todo(dsherret): add a file cache here to avoid parsing with swc on each run
|
||||
let source = match self.parsed_source_cache.get_parsed_source(specifier) {
|
||||
Some(source) => source.clone(),
|
||||
None => {
|
||||
let source = deno_ast::parse_program(deno_ast::ParseParams {
|
||||
specifier: specifier.clone(),
|
||||
text: source,
|
||||
media_type,
|
||||
capture_tokens: true, // capture because it's used for cjs export analysis
|
||||
scope_analysis: false,
|
||||
maybe_syntax: None,
|
||||
})?;
|
||||
self
|
||||
.parsed_source_cache
|
||||
.set_parsed_source(specifier.clone(), source.clone());
|
||||
source
|
||||
}
|
||||
};
|
||||
Ok(source.is_module())
|
||||
}
|
||||
}
|
||||
|
|
118
cli/factory.rs
118
cli/factory.rs
|
@ -14,6 +14,7 @@ use crate::cache::CodeCache;
|
|||
use crate::cache::DenoDir;
|
||||
use crate::cache::DenoDirProvider;
|
||||
use crate::cache::EmitCache;
|
||||
use crate::cache::EsmOrCjsChecker;
|
||||
use crate::cache::GlobalHttpCache;
|
||||
use crate::cache::HttpCache;
|
||||
use crate::cache::LocalHttpCache;
|
||||
|
@ -32,17 +33,19 @@ use crate::module_loader::ModuleLoadPreparer;
|
|||
use crate::node::CliCjsCodeAnalyzer;
|
||||
use crate::node::CliNodeCodeTranslator;
|
||||
use crate::npm::create_cli_npm_resolver;
|
||||
use crate::npm::CliByonmNpmResolverCreateOptions;
|
||||
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::CliDenoResolverFs;
|
||||
use crate::resolver::CliGraphResolver;
|
||||
use crate::resolver::CliGraphResolverOptions;
|
||||
use crate::resolver::CliNodeResolver;
|
||||
use crate::resolver::CliSloppyImportsResolver;
|
||||
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;
|
||||
|
@ -65,10 +68,13 @@ use deno_core::FeatureChecker;
|
|||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
|
||||
use deno_runtime::deno_node::NodeResolver;
|
||||
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 once_cell::sync::OnceCell;
|
||||
|
@ -166,6 +172,7 @@ struct CliFactoryServices {
|
|||
http_client_provider: Deferred<Arc<HttpClientProvider>>,
|
||||
emit_cache: Deferred<Arc<EmitCache>>,
|
||||
emitter: Deferred<Arc<Emitter>>,
|
||||
esm_or_cjs_checker: Deferred<Arc<EsmOrCjsChecker>>,
|
||||
fs: Deferred<Arc<dyn deno_fs::FileSystem>>,
|
||||
main_graph_container: Deferred<Arc<MainModuleGraphContainer>>,
|
||||
maybe_inspector_server: Deferred<Option<Arc<InspectorServer>>>,
|
||||
|
@ -181,7 +188,9 @@ struct CliFactoryServices {
|
|||
node_code_translator: Deferred<Arc<CliNodeCodeTranslator>>,
|
||||
node_resolver: Deferred<Arc<NodeResolver>>,
|
||||
npm_resolver: Deferred<Arc<dyn CliNpmResolver>>,
|
||||
sloppy_imports_resolver: Deferred<Option<Arc<SloppyImportsResolver>>>,
|
||||
permission_desc_parser: Deferred<Arc<RuntimePermissionDescriptorParser>>,
|
||||
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>>,
|
||||
|
@ -291,10 +300,16 @@ impl CliFactory {
|
|||
.get_or_init(|| ProgressBar::new(ProgressBarStyle::TextOnly))
|
||||
}
|
||||
|
||||
pub fn esm_or_cjs_checker(&self) -> &Arc<EsmOrCjsChecker> {
|
||||
self.services.esm_or_cjs_checker.get_or_init(|| {
|
||||
Arc::new(EsmOrCjsChecker::new(self.parsed_source_cache().clone()))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn global_http_cache(&self) -> Result<&Arc<GlobalHttpCache>, AnyError> {
|
||||
self.services.global_http_cache.get_or_try_init(|| {
|
||||
Ok(Arc::new(GlobalHttpCache::new(
|
||||
self.deno_dir()?.deps_folder_path(),
|
||||
self.deno_dir()?.remote_folder_path(),
|
||||
crate::cache::RealDenoCacheEnv,
|
||||
)))
|
||||
})
|
||||
|
@ -355,8 +370,8 @@ impl CliFactory {
|
|||
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(_) | DenoSubcommand::Add(_) | DenoSubcommand::Remove(_)) {
|
||||
CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions {
|
||||
fs: fs.clone(),
|
||||
CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions {
|
||||
fs: CliDenoResolverFs(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
|
||||
|
@ -399,17 +414,16 @@ impl CliFactory {
|
|||
|
||||
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())
|
||||
}
|
||||
|
@ -568,8 +582,13 @@ 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.cli_node_resolver().await?.clone();
|
||||
let cjs_esm_analyzer = CliCjsCodeAnalyzer::new(
|
||||
node_analysis_cache,
|
||||
self.fs().clone(),
|
||||
node_resolver,
|
||||
Some(self.parsed_source_cache().clone()),
|
||||
);
|
||||
|
||||
Ok(Arc::new(NodeCodeTranslator::new(
|
||||
cjs_esm_analyzer,
|
||||
|
@ -609,16 +628,18 @@ impl CliFactory {
|
|||
Ok(Arc::new(ModuleGraphBuilder::new(
|
||||
cli_options.clone(),
|
||||
self.caches()?.clone(),
|
||||
self.esm_or_cjs_checker().clone(),
|
||||
self.fs().clone(),
|
||||
self.resolver().await?.clone(),
|
||||
self.cli_node_resolver().await?.clone(),
|
||||
self.npm_resolver().await?.clone(),
|
||||
self.module_info_cache()?.clone(),
|
||||
self.parsed_source_cache().clone(),
|
||||
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.root_permissions_container()?.clone(),
|
||||
)))
|
||||
})
|
||||
.await
|
||||
|
@ -652,6 +673,7 @@ impl CliFactory {
|
|||
Ok(Arc::new(MainModuleGraphContainer::new(
|
||||
self.cli_options()?.clone(),
|
||||
self.module_load_preparer().await?.clone(),
|
||||
self.root_permissions_container()?.clone(),
|
||||
)))
|
||||
})
|
||||
.await
|
||||
|
@ -709,16 +731,20 @@ impl CliFactory {
|
|||
.await
|
||||
}
|
||||
|
||||
pub fn permission_desc_parser(
|
||||
&self,
|
||||
) -> 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> {
|
||||
self.services.feature_checker.get_or_try_init(|| {
|
||||
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 granular_flag in crate::UNSTABLE_GRANULAR_FLAGS {
|
||||
if unstable_features.contains(&granular_flag.name.to_string()) {
|
||||
|
@ -744,6 +770,22 @@ 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> {
|
||||
|
@ -752,6 +794,7 @@ impl CliFactory {
|
|||
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 maybe_file_watcher_communicator = if cli_options.has_hmr() {
|
||||
Some(self.watcher_communicator.clone().unwrap())
|
||||
} else {
|
||||
|
@ -759,11 +802,18 @@ impl CliFactory {
|
|||
};
|
||||
|
||||
Ok(CliMainWorkerFactory::new(
|
||||
StorageKeyResolver::from_options(cli_options),
|
||||
cli_options.sub_command().clone(),
|
||||
npm_resolver.clone(),
|
||||
node_resolver.clone(),
|
||||
self.blob_store().clone(),
|
||||
self.cjs_resolutions().clone(),
|
||||
if cli_options.code_cache_enabled() {
|
||||
Some(self.code_cache()?.clone())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
self.feature_checker()?.clone(),
|
||||
self.fs().clone(),
|
||||
maybe_file_watcher_communicator,
|
||||
self.maybe_inspector_server()?.clone(),
|
||||
cli_options.maybe_lockfile().cloned(),
|
||||
Box::new(CliModuleLoaderFactory::new(
|
||||
cli_options,
|
||||
if cli_options.code_cache_enabled() {
|
||||
|
@ -775,6 +825,7 @@ impl CliFactory {
|
|||
self.main_module_graph_container().await?.clone(),
|
||||
self.module_load_preparer().await?.clone(),
|
||||
cli_node_resolver.clone(),
|
||||
cli_npm_resolver.clone(),
|
||||
NpmModuleLoader::new(
|
||||
self.cjs_resolutions().clone(),
|
||||
self.node_code_translator().await?.clone(),
|
||||
|
@ -784,17 +835,12 @@ impl CliFactory {
|
|||
self.parsed_source_cache().clone(),
|
||||
self.resolver().await?.clone(),
|
||||
)),
|
||||
node_resolver.clone(),
|
||||
npm_resolver.clone(),
|
||||
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(),
|
||||
if cli_options.code_cache_enabled() {
|
||||
Some(self.code_cache()?.clone())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
self.root_permissions_container()?.clone(),
|
||||
StorageKeyResolver::from_options(cli_options),
|
||||
cli_options.sub_command().clone(),
|
||||
self.create_cli_main_worker_options()?,
|
||||
))
|
||||
}
|
||||
|
@ -857,12 +903,12 @@ 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(),
|
||||
unstable_detect_cjs: cli_options.unstable_detect_cjs(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ 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 log::debug;
|
||||
|
@ -135,14 +136,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(),
|
||||
})
|
||||
}
|
||||
|
@ -161,9 +171,16 @@ fn get_validated_scheme(
|
|||
}
|
||||
}
|
||||
|
||||
#[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_accept: Option<&'a str>,
|
||||
pub maybe_cache_setting: Option<&'a CacheSetting>,
|
||||
}
|
||||
|
@ -316,7 +333,7 @@ impl FileFetcher {
|
|||
)
|
||||
})?;
|
||||
|
||||
let bytes = blob.read_all().await?;
|
||||
let bytes = blob.read_all().await;
|
||||
let headers =
|
||||
HashMap::from([("content-type".to_string(), blob.media_type.clone())]);
|
||||
|
||||
|
@ -515,11 +532,35 @@ impl FileFetcher {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub async fn fetch_bypass_permissions(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Result<File, AnyError> {
|
||||
self
|
||||
.fetch_inner(specifier, 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,
|
||||
FetchPermissionsOptionRef::StaticContainer(permissions),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn fetch_inner(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
permissions: FetchPermissionsOptionRef<'_>,
|
||||
) -> Result<File, AnyError> {
|
||||
self
|
||||
.fetch_with_options(FetchOptions {
|
||||
|
@ -583,7 +624,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" {
|
||||
|
@ -669,7 +726,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)),
|
||||
|
@ -684,9 +741,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)
|
||||
}
|
||||
|
@ -700,7 +755,7 @@ mod tests {
|
|||
.fetch_with_options_and_max_redirect(
|
||||
FetchOptions {
|
||||
specifier,
|
||||
permissions: &PermissionsContainer::allow_all(),
|
||||
permissions: FetchPermissionsOptionRef::AllowAll,
|
||||
maybe_accept: None,
|
||||
maybe_cache_setting: Some(&file_fetcher.cache_setting),
|
||||
},
|
||||
|
@ -796,9 +851,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);
|
||||
|
@ -809,9 +862,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!(
|
||||
|
@ -840,9 +891,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!(
|
||||
|
@ -862,9 +911,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!(
|
||||
|
@ -882,9 +929,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!(
|
||||
|
@ -908,9 +953,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!(
|
||||
|
@ -921,7 +964,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,
|
||||
|
@ -933,9 +976,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!(
|
||||
|
@ -949,7 +990,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();
|
||||
|
||||
|
@ -966,9 +1007,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();
|
||||
|
@ -1002,9 +1041,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 =
|
||||
|
@ -1041,9 +1078,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);
|
||||
|
@ -1082,9 +1117,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);
|
||||
|
@ -1123,7 +1156,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 =
|
||||
|
@ -1142,9 +1175,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
|
||||
|
@ -1182,7 +1213,7 @@ mod tests {
|
|||
None,
|
||||
);
|
||||
let result = file_fetcher
|
||||
.fetch(&redirected_specifier, &PermissionsContainer::allow_all())
|
||||
.fetch_bypass_permissions(&redirected_specifier)
|
||||
.await;
|
||||
assert!(result.is_ok());
|
||||
|
||||
|
@ -1223,7 +1254,7 @@ mod tests {
|
|||
.fetch_with_options_and_max_redirect(
|
||||
FetchOptions {
|
||||
specifier: &specifier,
|
||||
permissions: &PermissionsContainer::allow_all(),
|
||||
permissions: FetchPermissionsOptionRef::AllowAll,
|
||||
maybe_accept: None,
|
||||
maybe_cache_setting: Some(&file_fetcher.cache_setting),
|
||||
},
|
||||
|
@ -1236,7 +1267,7 @@ mod tests {
|
|||
.fetch_with_options_and_max_redirect(
|
||||
FetchOptions {
|
||||
specifier: &specifier,
|
||||
permissions: &PermissionsContainer::allow_all(),
|
||||
permissions: FetchPermissionsOptionRef::AllowAll,
|
||||
maybe_accept: None,
|
||||
maybe_cache_setting: Some(&file_fetcher.cache_setting),
|
||||
},
|
||||
|
@ -1264,9 +1295,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);
|
||||
|
@ -1295,7 +1324,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,
|
||||
|
@ -1310,9 +1339,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"));
|
||||
|
@ -1323,7 +1350,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,
|
||||
|
@ -1343,22 +1370,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());
|
||||
}
|
||||
|
||||
|
@ -1368,17 +1389,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");"#);
|
||||
|
@ -1392,18 +1409,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;
|
||||
|
@ -1419,18 +1432,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;
|
||||
|
|
|
@ -45,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(),
|
||||
|
@ -59,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();
|
||||
|
@ -75,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();
|
||||
|
@ -94,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(
|
||||
|
|
|
@ -6,6 +6,7 @@ use crate::args::CliLockfile;
|
|||
use crate::args::CliOptions;
|
||||
use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
|
||||
use crate::cache;
|
||||
use crate::cache::EsmOrCjsChecker;
|
||||
use crate::cache::GlobalHttpCache;
|
||||
use crate::cache::ModuleInfoCache;
|
||||
use crate::cache::ParsedSourceCache;
|
||||
|
@ -14,24 +15,26 @@ 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::CliNodeResolver;
|
||||
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::workspace::JsrPackageConfig;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_graph::source::LoaderChecksum;
|
||||
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::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::ModuleError;
|
||||
|
@ -39,12 +42,13 @@ 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::Version;
|
||||
use import_map::ImportMapError;
|
||||
use std::collections::HashSet;
|
||||
use std::error::Error;
|
||||
|
@ -52,14 +56,14 @@ 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
|
||||
|
@ -75,17 +79,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,
|
||||
},
|
||||
)
|
||||
|
@ -109,7 +150,7 @@ 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_deno_graph_error(error))
|
||||
}
|
||||
|
@ -132,56 +173,18 @@ 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_deno_graph_error(err.as_ref().deref()),
|
||||
));
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
@ -249,6 +252,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()?);
|
||||
|
@ -262,9 +278,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()
|
||||
|
@ -279,6 +298,7 @@ impl ModuleGraphCreator {
|
|||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(graph)
|
||||
}
|
||||
|
||||
|
@ -361,16 +381,18 @@ pub struct BuildFastCheckGraphOptions<'a> {
|
|||
pub struct ModuleGraphBuilder {
|
||||
options: Arc<CliOptions>,
|
||||
caches: Arc<cache::Caches>,
|
||||
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
|
||||
fs: Arc<dyn FileSystem>,
|
||||
resolver: Arc<CliGraphResolver>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
lockfile: Option<Arc<CliLockfile>>,
|
||||
maybe_file_watcher_reporter: Option<FileWatcherReporter>,
|
||||
emit_cache: Arc<cache::EmitCache>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
root_permissions_container: PermissionsContainer,
|
||||
}
|
||||
|
||||
impl ModuleGraphBuilder {
|
||||
|
@ -378,30 +400,34 @@ impl ModuleGraphBuilder {
|
|||
pub fn new(
|
||||
options: Arc<CliOptions>,
|
||||
caches: Arc<cache::Caches>,
|
||||
esm_or_cjs_checker: Arc<EsmOrCjsChecker>,
|
||||
fs: Arc<dyn FileSystem>,
|
||||
resolver: Arc<CliGraphResolver>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
module_info_cache: Arc<ModuleInfoCache>,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
lockfile: Option<Arc<CliLockfile>>,
|
||||
maybe_file_watcher_reporter: Option<FileWatcherReporter>,
|
||||
emit_cache: Arc<cache::EmitCache>,
|
||||
file_fetcher: Arc<FileFetcher>,
|
||||
global_http_cache: Arc<GlobalHttpCache>,
|
||||
root_permissions_container: PermissionsContainer,
|
||||
) -> Self {
|
||||
Self {
|
||||
options,
|
||||
caches,
|
||||
esm_or_cjs_checker,
|
||||
fs,
|
||||
resolver,
|
||||
node_resolver,
|
||||
npm_resolver,
|
||||
module_info_cache,
|
||||
parsed_source_cache,
|
||||
lockfile,
|
||||
maybe_file_watcher_reporter,
|
||||
emit_cache,
|
||||
file_fetcher,
|
||||
global_http_cache,
|
||||
root_permissions_container,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -556,33 +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 (req_dep, value) in &lockfile.content.packages.specifiers {
|
||||
match req_dep.kind {
|
||||
deno_semver::package::PackageKind::Jsr => {
|
||||
if let Ok(version) = Version::parse_standard(value) {
|
||||
graph.packages.add_nv(
|
||||
req_dep.req.clone(),
|
||||
PackageNv {
|
||||
name: req_dep.req.name.clone(),
|
||||
version,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
deno_semver::package::PackageKind::Npm => {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
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())),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -590,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;
|
||||
|
@ -673,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(
|
||||
|
@ -681,13 +699,21 @@ impl ModuleGraphBuilder {
|
|||
permissions: PermissionsContainer,
|
||||
) -> cache::FetchCacher {
|
||||
cache::FetchCacher::new(
|
||||
self.emit_cache.clone(),
|
||||
self.esm_or_cjs_checker.clone(),
|
||||
self.file_fetcher.clone(),
|
||||
self.options.resolve_file_header_overrides(),
|
||||
self.global_http_cache.clone(),
|
||||
self.node_resolver.clone(),
|
||||
self.npm_resolver.clone(),
|
||||
self.module_info_cache.clone(),
|
||||
permissions,
|
||||
cache::FetchCacherOptions {
|
||||
file_header_overrides: self.options.resolve_file_header_overrides(),
|
||||
permissions,
|
||||
is_deno_publish: matches!(
|
||||
self.options.sub_command(),
|
||||
crate::args::DenoSubcommand::Publish { .. }
|
||||
),
|
||||
unstable_detect_cjs: self.options.unstable_detect_cjs(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -711,10 +737,13 @@ impl ModuleGraphBuilder {
|
|||
&self.fs,
|
||||
roots,
|
||||
GraphValidOptions {
|
||||
is_vendoring: false,
|
||||
follow_type_only: self.options.type_check_mode().is_true(),
|
||||
kind: if self.options.type_check_mode().is_true() {
|
||||
GraphKind::All
|
||||
} else {
|
||||
GraphKind::CodeOnly
|
||||
},
|
||||
check_js: self.options.check_js(),
|
||||
exit_lockfile_errors: true,
|
||||
exit_integrity_errors: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -735,8 +764,8 @@ pub fn enhanced_resolution_error_message(error: &ResolutionError) -> String {
|
|||
} else {
|
||||
get_import_prefix_missing_error(error).map(|specifier| {
|
||||
format!(
|
||||
"If you want to use a JSR or npm package, try running `deno add {}`",
|
||||
specifier
|
||||
"If you want to use a JSR or npm package, try running `deno add jsr:{}` or `deno add npm:{}`",
|
||||
specifier, specifier
|
||||
)
|
||||
})
|
||||
};
|
||||
|
@ -755,8 +784,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",
|
||||
|
@ -768,7 +797,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,
|
||||
|
@ -881,24 +910,29 @@ 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
|
||||
{
|
||||
maybe_specifier = Some(specifier);
|
||||
} else if let ResolutionError::ResolverError { error, .. } = error {
|
||||
match error.as_ref() {
|
||||
ResolveError::Specifier(specifier_error) => {
|
||||
if let SpecifierError::ImportPrefixMissing { specifier, .. } =
|
||||
specifier_error
|
||||
{
|
||||
maybe_specifier = Some(specifier);
|
||||
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);
|
||||
ResolveError::Other(other_error) => {
|
||||
if let Some(SpecifierError::ImportPrefixMissing {
|
||||
specifier, ..
|
||||
}) = other_error.downcast_ref::<SpecifierError>()
|
||||
{
|
||||
maybe_specifier = Some(specifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -927,13 +961,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;
|
||||
|
|
|
@ -470,15 +470,23 @@ impl HttpClient {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn download_with_progress(
|
||||
pub async fn download_with_progress_and_retries(
|
||||
&self,
|
||||
url: Url,
|
||||
maybe_header: Option<(HeaderName, HeaderValue)>,
|
||||
progress_guard: &UpdateGuard,
|
||||
) -> Result<Option<Vec<u8>>, DownloadError> {
|
||||
self
|
||||
.download_inner(url, maybe_header, Some(progress_guard))
|
||||
.await
|
||||
crate::util::retry::retry(
|
||||
|| {
|
||||
self.download_inner(
|
||||
url.clone(),
|
||||
maybe_header.clone(),
|
||||
Some(progress_guard),
|
||||
)
|
||||
},
|
||||
|e| matches!(e, DownloadError::BadResponse(_) | DownloadError::Fetch(_)),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_redirected_url(
|
||||
|
|
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,6 +2,7 @@
|
|||
|
||||
use super::diagnostics::DenoDiagnostic;
|
||||
use super::diagnostics::DiagnosticSource;
|
||||
use super::documents::Document;
|
||||
use super::documents::Documents;
|
||||
use super::language_server;
|
||||
use super::resolver::LspResolver;
|
||||
|
@ -9,9 +10,10 @@ use super::tsc;
|
|||
use super::urls::url_to_uri;
|
||||
|
||||
use crate::args::jsr_url;
|
||||
use crate::lsp::search::PackageSearchApi;
|
||||
use crate::tools::lint::CliLinter;
|
||||
use deno_config::workspace::MappedResolution;
|
||||
use deno_lint::diagnostic::LintDiagnosticRange;
|
||||
use deno_runtime::fs_util::specifier_to_file_path;
|
||||
|
||||
use deno_ast::SourceRange;
|
||||
use deno_ast::SourceRangedForSpanned;
|
||||
|
@ -24,6 +26,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;
|
||||
|
@ -41,6 +44,7 @@ use std::cmp::Ordering;
|
|||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
use text_lines::LineAndColumnIndex;
|
||||
use tower_lsp::lsp_types as lsp;
|
||||
use tower_lsp::lsp_types::Position;
|
||||
use tower_lsp::lsp_types::Range;
|
||||
|
@ -225,6 +229,7 @@ pub struct TsResponseImportMapper<'a> {
|
|||
documents: &'a Documents,
|
||||
maybe_import_map: Option<&'a ImportMap>,
|
||||
resolver: &'a LspResolver,
|
||||
file_referrer: ModuleSpecifier,
|
||||
}
|
||||
|
||||
impl<'a> TsResponseImportMapper<'a> {
|
||||
|
@ -232,11 +237,13 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
documents: &'a Documents,
|
||||
maybe_import_map: Option<&'a ImportMap>,
|
||||
resolver: &'a LspResolver,
|
||||
file_referrer: &ModuleSpecifier,
|
||||
) -> Self {
|
||||
Self {
|
||||
documents,
|
||||
maybe_import_map,
|
||||
resolver,
|
||||
file_referrer: file_referrer.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -257,8 +264,6 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
let file_referrer = self.documents.get_file_referrer(referrer);
|
||||
|
||||
if let Some(jsr_path) = specifier.as_str().strip_prefix(jsr_url().as_str())
|
||||
{
|
||||
let mut segments = jsr_path.split('/');
|
||||
|
@ -273,7 +278,7 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
let export = self.resolver.jsr_lookup_export_for_path(
|
||||
&nv,
|
||||
&path,
|
||||
file_referrer.as_deref(),
|
||||
Some(&self.file_referrer),
|
||||
)?;
|
||||
let sub_path = (export != ".").then_some(export);
|
||||
let mut req = None;
|
||||
|
@ -299,7 +304,7 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
req = req.or_else(|| {
|
||||
self
|
||||
.resolver
|
||||
.jsr_lookup_req_for_nv(&nv, file_referrer.as_deref())
|
||||
.jsr_lookup_req_for_nv(&nv, Some(&self.file_referrer))
|
||||
});
|
||||
let spec_str = if let Some(req) = req {
|
||||
let req_ref = PackageReqReference { req, sub_path };
|
||||
|
@ -329,7 +334,7 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
|
||||
if let Some(npm_resolver) = self
|
||||
.resolver
|
||||
.maybe_managed_npm_resolver(file_referrer.as_deref())
|
||||
.maybe_managed_npm_resolver(Some(&self.file_referrer))
|
||||
{
|
||||
if npm_resolver.in_npm_package(specifier) {
|
||||
if let Ok(Some(pkg_id)) =
|
||||
|
@ -401,7 +406,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
|
||||
|
@ -465,6 +470,26 @@ impl<'a> TsResponseImportMapper<'a> {
|
|||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn is_valid_import(
|
||||
&self,
|
||||
specifier_text: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> bool {
|
||||
self
|
||||
.resolver
|
||||
.as_graph_resolver(Some(&self.file_referrer))
|
||||
.resolve(
|
||||
specifier_text,
|
||||
&deno_graph::Range {
|
||||
specifier: referrer.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
},
|
||||
deno_graph::source::ResolutionMode::Types,
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
fn try_reverse_map_package_json_exports(
|
||||
|
@ -577,7 +602,7 @@ fn fix_ts_import_action(
|
|||
referrer: &ModuleSpecifier,
|
||||
action: &tsc::CodeFixAction,
|
||||
import_mapper: &TsResponseImportMapper,
|
||||
) -> Result<tsc::CodeFixAction, AnyError> {
|
||||
) -> Result<Option<tsc::CodeFixAction>, AnyError> {
|
||||
if matches!(
|
||||
action.fix_name.as_str(),
|
||||
"import" | "fixMissingFunctionDeclaration"
|
||||
|
@ -620,19 +645,21 @@ fn fix_ts_import_action(
|
|||
})
|
||||
.collect();
|
||||
|
||||
return Ok(tsc::CodeFixAction {
|
||||
return Ok(Some(tsc::CodeFixAction {
|
||||
description,
|
||||
changes,
|
||||
commands: None,
|
||||
fix_name: action.fix_name.clone(),
|
||||
fix_id: None,
|
||||
fix_all_description: None,
|
||||
});
|
||||
}));
|
||||
} else if !import_mapper.is_valid_import(specifier, referrer) {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(action.clone())
|
||||
Ok(Some(action.clone()))
|
||||
}
|
||||
|
||||
/// Determines if two TypeScript diagnostic codes are effectively equivalent.
|
||||
|
@ -973,11 +1000,14 @@ impl CodeActionCollection {
|
|||
"The action returned from TypeScript is unsupported.",
|
||||
));
|
||||
}
|
||||
let action = fix_ts_import_action(
|
||||
let Some(action) = fix_ts_import_action(
|
||||
specifier,
|
||||
action,
|
||||
&language_server.get_ts_response_import_mapper(specifier),
|
||||
)?;
|
||||
)?
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
let edit = ts_changes_to_edit(&action.changes, language_server)?;
|
||||
let code_action = lsp::CodeAction {
|
||||
title: action.description.clone(),
|
||||
|
@ -1151,6 +1181,191 @@ 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,
|
||||
}),
|
||||
);
|
||||
|
||||
match parsed_source.program_ref() {
|
||||
deno_ast::swc::ast::Program::Module(module) => module
|
||||
.body
|
||||
.iter()
|
||||
.find(|i| i.range().contains(&specifier_range))
|
||||
.map(|i| text_info.line_and_column_index(i.range().start)),
|
||||
deno_ast::swc::ast::Program::Script(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
async fn deno_types_for_npm_action(
|
||||
document: &Document,
|
||||
range: &lsp::Range,
|
||||
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 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)
|
||||
{
|
||||
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, document.specifier(), 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,10 +7,10 @@ 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;
|
||||
|
@ -24,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),
|
||||
|
@ -82,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());
|
||||
})
|
||||
|
@ -94,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 {
|
||||
|
@ -165,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()
|
||||
|
@ -176,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,11 +147,11 @@ pub fn server_capabilities(
|
|||
moniker_provider: None,
|
||||
experimental: Some(json!({
|
||||
"denoConfigTasks": true,
|
||||
"testingApi":true,
|
||||
"testingApi": true,
|
||||
"didRefreshDenoConfigurationTreeNotifications": true,
|
||||
})),
|
||||
inlay_hint_provider: Some(OneOf::Left(true)),
|
||||
position_encoding: None,
|
||||
// TODO(nayeemrmn): Support pull-based diagnostics.
|
||||
diagnostic_provider: None,
|
||||
inline_value_provider: None,
|
||||
inline_completion_provider: None,
|
||||
|
|
|
@ -92,6 +92,19 @@ impl Client {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn send_did_refresh_deno_configuration_tree_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
|
||||
) {
|
||||
// do on a task in case the caller currently is in the lsp lock
|
||||
let client = self.0.clone();
|
||||
spawn(async move {
|
||||
client
|
||||
.send_did_refresh_deno_configuration_tree_notification(params)
|
||||
.await;
|
||||
});
|
||||
}
|
||||
|
||||
pub fn send_did_change_deno_configuration_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
|
||||
|
@ -169,6 +182,10 @@ trait ClientTrait: Send + Sync {
|
|||
params: lsp_custom::DiagnosticBatchNotificationParams,
|
||||
);
|
||||
async fn send_test_notification(&self, params: TestingNotification);
|
||||
async fn send_did_refresh_deno_configuration_tree_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
|
||||
);
|
||||
async fn send_did_change_deno_configuration_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
|
||||
|
@ -249,6 +266,18 @@ impl ClientTrait for TowerClient {
|
|||
}
|
||||
}
|
||||
|
||||
async fn send_did_refresh_deno_configuration_tree_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
|
||||
) {
|
||||
self
|
||||
.0
|
||||
.send_notification::<lsp_custom::DidRefreshDenoConfigurationTreeNotification>(
|
||||
params,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn send_did_change_deno_configuration_notification(
|
||||
&self,
|
||||
params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
|
||||
|
@ -366,6 +395,12 @@ impl ClientTrait for ReplClient {
|
|||
|
||||
async fn send_test_notification(&self, _params: TestingNotification) {}
|
||||
|
||||
async fn send_did_refresh_deno_configuration_tree_notification(
|
||||
&self,
|
||||
_params: lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams,
|
||||
) {
|
||||
}
|
||||
|
||||
async fn send_did_change_deno_configuration_notification(
|
||||
&self,
|
||||
_params: lsp_custom::DidChangeDenoConfigurationNotificationParams,
|
||||
|
|
|
@ -19,7 +19,6 @@ use crate::util::path::relative_specifier;
|
|||
use deno_graph::source::ResolutionMode;
|
||||
use deno_graph::Range;
|
||||
use deno_runtime::deno_node::SUPPORTED_BUILTIN_NODE_MODULES;
|
||||
use deno_runtime::fs_util::specifier_to_file_path;
|
||||
|
||||
use deno_ast::LineAndColumnIndex;
|
||||
use deno_ast::SourceTextInfo;
|
||||
|
@ -30,6 +29,7 @@ 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;
|
||||
|
@ -200,15 +200,11 @@ pub async fn get_import_completions(
|
|||
{
|
||||
// completions for import map specifiers
|
||||
Some(lsp::CompletionResponse::List(completion_list))
|
||||
} else if text.starts_with("./")
|
||||
|| text.starts_with("../")
|
||||
|| text.starts_with('/')
|
||||
} else if let Some(completion_list) =
|
||||
get_local_completions(specifier, &text, &range, resolver)
|
||||
{
|
||||
// completions for local relative modules
|
||||
Some(lsp::CompletionResponse::List(CompletionList {
|
||||
is_incomplete: false,
|
||||
items: get_local_completions(specifier, &text, &range, resolver)?,
|
||||
}))
|
||||
Some(lsp::CompletionResponse::List(completion_list))
|
||||
} else if !text.is_empty() {
|
||||
// completion of modules from a module registry or cache
|
||||
check_auto_config_registry(
|
||||
|
@ -363,15 +359,15 @@ fn get_local_completions(
|
|||
text: &str,
|
||||
range: &lsp::Range,
|
||||
resolver: &LspResolver,
|
||||
) -> Option<Vec<lsp::CompletionItem>> {
|
||||
) -> Option<CompletionList> {
|
||||
if base.scheme() != "file" {
|
||||
return None;
|
||||
}
|
||||
let parent = base.join(text).ok()?.join(".").ok()?;
|
||||
let parent = &text[..text.char_indices().rfind(|(_, c)| *c == '/')?.0 + 1];
|
||||
let resolved_parent = resolver
|
||||
.as_graph_resolver(Some(base))
|
||||
.resolve(
|
||||
parent.as_str(),
|
||||
parent,
|
||||
&Range {
|
||||
specifier: base.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
|
@ -380,63 +376,63 @@ fn get_local_completions(
|
|||
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 == *base {
|
||||
return None;
|
||||
}
|
||||
let full_text = format!("{parent}{label}");
|
||||
let text_edit = Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
|
||||
range: *range,
|
||||
new_text: full_text.clone(),
|
||||
}));
|
||||
let filter_text = Some(full_text);
|
||||
match de.file_type() {
|
||||
Ok(file_type) if file_type.is_dir() => Some(lsp::CompletionItem {
|
||||
label,
|
||||
kind: Some(lsp::CompletionItemKind::FOLDER),
|
||||
detail: Some("(local)".to_string()),
|
||||
filter_text,
|
||||
sort_text: Some("1".to_string()),
|
||||
text_edit,
|
||||
commit_characters: Some(
|
||||
IMPORT_COMMIT_CHARS.iter().map(|&c| c.into()).collect(),
|
||||
),
|
||||
..Default::default()
|
||||
}),
|
||||
Ok(file_type) if file_type.is_file() => {
|
||||
if is_importable_ext(&de.path()) {
|
||||
Some(lsp::CompletionItem {
|
||||
label,
|
||||
kind: Some(lsp::CompletionItemKind::FILE),
|
||||
detail: Some("(local)".to_string()),
|
||||
filter_text,
|
||||
sort_text: Some("1".to_string()),
|
||||
text_edit,
|
||||
commit_characters: Some(
|
||||
IMPORT_COMMIT_CHARS.iter().map(|&c| c.into()).collect(),
|
||||
),
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Some(CompletionList {
|
||||
is_incomplete: false,
|
||||
items,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -921,11 +917,11 @@ mod tests {
|
|||
},
|
||||
},
|
||||
&Default::default(),
|
||||
);
|
||||
assert!(actual.is_some());
|
||||
let actual = actual.unwrap();
|
||||
assert_eq!(actual.len(), 3);
|
||||
for item in actual {
|
||||
)
|
||||
.unwrap();
|
||||
assert!(!actual.is_incomplete);
|
||||
assert_eq!(actual.items.len(), 3);
|
||||
for item in actual.items {
|
||||
match item.text_edit {
|
||||
Some(lsp::CompletionTextEdit::Edit(text_edit)) => {
|
||||
assert!(["./b", "./f.mjs", "./g.json"]
|
||||
|
|
|
@ -36,29 +36,34 @@ 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_types::ClientCapabilities;
|
||||
use std::collections::BTreeMap;
|
||||
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::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 +75,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 {
|
||||
|
@ -538,7 +591,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 +621,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 +805,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 +814,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 +851,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) {
|
||||
|
@ -1080,11 +1133,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",
|
||||
|
@ -1132,7 +1185,7 @@ 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>,
|
||||
watched_files: HashMap<ModuleSpecifier, ConfigWatchedFileType>,
|
||||
}
|
||||
|
@ -1402,9 +1455,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(),
|
||||
|
@ -1460,17 +1514,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())
|
||||
|
@ -1494,50 +1547,32 @@ impl ConfigData {
|
|||
None
|
||||
}
|
||||
};
|
||||
let resolver = deno_core::unsync::spawn({
|
||||
let workspace = member_dir.workspace.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.resolver_jsr_pkgs().collect(),
|
||||
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{}",
|
||||
|
@ -1553,9 +1588,11 @@ impl ConfigData {
|
|||
.is_ok()
|
||||
|| member_dir.workspace.has_unstable("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(
|
||||
|
@ -1681,14 +1718,14 @@ impl ConfigTree {
|
|||
.unwrap_or_else(|| Arc::new(FmtConfig::new_with_base(PathBuf::from("/"))))
|
||||
}
|
||||
|
||||
/// Returns (scope_uri, type).
|
||||
/// Returns (scope_url, type).
|
||||
pub fn watched_file_type(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<(&ModuleSpecifier, ConfigWatchedFileType)> {
|
||||
for (scope_uri, data) in self.scopes.iter() {
|
||||
for (scope_url, data) in self.scopes.iter() {
|
||||
if let Some(typ) = data.watched_files.get(specifier) {
|
||||
return Some((scope_uri, *typ));
|
||||
return Some((scope_url, *typ));
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -1712,6 +1749,46 @@ impl ConfigTree {
|
|||
.any(|data| data.watched_files.contains_key(specifier))
|
||||
}
|
||||
|
||||
pub fn to_did_refresh_params(
|
||||
&self,
|
||||
) -> lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams {
|
||||
let data = self
|
||||
.scopes
|
||||
.values()
|
||||
.filter_map(|data| {
|
||||
let workspace_root_scope_uri =
|
||||
Some(data.member_dir.workspace.root_dir())
|
||||
.filter(|s| *s != data.member_dir.dir_url())
|
||||
.and_then(|s| url_to_uri(s).ok());
|
||||
Some(lsp_custom::DenoConfigurationData {
|
||||
scope_uri: url_to_uri(&data.scope).ok()?,
|
||||
deno_json: data.maybe_deno_json().and_then(|c| {
|
||||
if workspace_root_scope_uri.is_some()
|
||||
&& Some(&c.specifier)
|
||||
== data
|
||||
.member_dir
|
||||
.workspace
|
||||
.root_deno_json()
|
||||
.map(|c| &c.specifier)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Some(lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(&c.specifier).ok()?,
|
||||
})
|
||||
}),
|
||||
package_json: data.maybe_pkg_json().and_then(|p| {
|
||||
Some(lsp::TextDocumentIdentifier {
|
||||
uri: url_to_uri(&p.specifier()).ok()?,
|
||||
})
|
||||
}),
|
||||
workspace_root_scope_uri,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
lsp_custom::DidRefreshDenoConfigurationTreeNotificationParams { data }
|
||||
}
|
||||
|
||||
pub async fn refresh(
|
||||
&mut self,
|
||||
settings: &Settings,
|
||||
|
@ -1736,7 +1813,7 @@ impl ConfigTree {
|
|||
let config_file_path = (|| {
|
||||
let config_setting = ws_settings.config.as_ref()?;
|
||||
let config_uri = folder_uri.join(config_setting).ok()?;
|
||||
specifier_to_file_path(&config_uri).ok()
|
||||
url_to_file_path(&config_uri).ok()
|
||||
})();
|
||||
if config_file_path.is_some() || ws_settings.import_map.is_some() {
|
||||
scopes.insert(
|
||||
|
@ -1813,7 +1890,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,
|
||||
|
@ -1897,7 +1974,11 @@ fn resolve_lockfile_from_path(
|
|||
lockfile_path: PathBuf,
|
||||
frozen: bool,
|
||||
) -> Option<CliLockfile> {
|
||||
match CliLockfile::read_from_path(lockfile_path, frozen) {
|
||||
match CliLockfile::read_from_path(CliLockfileReadFromPathOptions {
|
||||
file_path: lockfile_path,
|
||||
frozen,
|
||||
skip_write: false,
|
||||
}) {
|
||||
Ok(value) => {
|
||||
if value.filename.exists() {
|
||||
if let Ok(specifier) = ModuleSpecifier::from_file_path(&value.filename)
|
||||
|
@ -2142,7 +2223,7 @@ mod tests {
|
|||
},
|
||||
tls_certificate: None,
|
||||
unsafely_ignore_certificate_errors: None,
|
||||
unstable: false,
|
||||
unstable: Default::default(),
|
||||
javascript: LanguageWorkspaceSettings {
|
||||
inlay_hints: InlayHintsSettings {
|
||||
parameter_names: InlayHintsParamNamesOptions {
|
||||
|
|
|
@ -12,14 +12,15 @@ use super::language_server::StateSnapshot;
|
|||
use super::performance::Performance;
|
||||
use super::tsc;
|
||||
use super::tsc::TsServer;
|
||||
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;
|
||||
|
@ -39,11 +40,12 @@ 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;
|
||||
|
@ -53,11 +55,9 @@ use deno_semver::package::PackageReq;
|
|||
use import_map::ImportMap;
|
||||
use import_map::ImportMapError;
|
||||
use log::error;
|
||||
use lsp_types::Uri;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
@ -738,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 = Uri::from_str(file_name).unwrap();
|
||||
let uri = uri_parse_unencoded(file_name).unwrap();
|
||||
Some(lsp::DiagnosticRelatedInformation {
|
||||
location: lsp::Location {
|
||||
uri,
|
||||
|
@ -1264,7 +1264,9 @@ impl DenoDiagnostic {
|
|||
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,
|
||||
|
@ -1515,17 +1517,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(
|
||||
|
|
|
@ -11,7 +11,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;
|
||||
|
@ -27,6 +26,7 @@ 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;
|
||||
|
@ -849,7 +849,7 @@ impl FileSystemDocuments {
|
|||
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()?;
|
||||
|
@ -1136,7 +1136,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);
|
||||
}
|
||||
|
@ -1325,7 +1325,7 @@ impl Documents {
|
|||
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;
|
||||
|
@ -1524,12 +1524,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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ 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;
|
||||
|
@ -311,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()
|
||||
})
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use base64::Engine;
|
||||
use deno_ast::MediaType;
|
||||
use deno_config::workspace::WorkspaceDirectory;
|
||||
use deno_config::workspace::WorkspaceDiscoverOptions;
|
||||
|
@ -16,6 +15,7 @@ use deno_core::url::Url;
|
|||
use deno_core::ModuleSpecifier;
|
||||
use deno_graph::GraphKind;
|
||||
use deno_graph::Resolution;
|
||||
use deno_path_util::url_to_file_path;
|
||||
use deno_runtime::deno_tls::rustls::RootCertStore;
|
||||
use deno_runtime::deno_tls::RootCertStoreProvider;
|
||||
use deno_semver::jsr::JsrPackageReqReference;
|
||||
|
@ -96,6 +96,7 @@ use crate::args::CaData;
|
|||
use crate::args::CacheSetting;
|
||||
use crate::args::CliOptions;
|
||||
use crate::args::Flags;
|
||||
use crate::args::InternalFlags;
|
||||
use crate::args::UnstableFmtOptions;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
|
@ -113,7 +114,6 @@ use crate::util::fs::remove_dir_all_if_exists;
|
|||
use crate::util::path::is_importable_ext;
|
||||
use crate::util::path::to_percent_decoded_str;
|
||||
use crate::util::sync::AsyncFlag;
|
||||
use deno_runtime::fs_util::specifier_to_file_path;
|
||||
|
||||
struct LspRootCertStoreProvider(RootCertStore);
|
||||
|
||||
|
@ -208,11 +208,11 @@ pub struct Inner {
|
|||
module_registry: ModuleRegistry,
|
||||
/// A lazily create "server" for handling test run requests.
|
||||
maybe_testing_server: Option<testing::TestServer>,
|
||||
npm_search_api: CliNpmSearchApi,
|
||||
pub npm_search_api: CliNpmSearchApi,
|
||||
project_version: usize,
|
||||
/// A collection of measurements which instrument that performance of the LSP.
|
||||
performance: Arc<Performance>,
|
||||
resolver: Arc<LspResolver>,
|
||||
pub resolver: Arc<LspResolver>,
|
||||
task_queue: LanguageServerTaskQueue,
|
||||
/// A memoized version of fixable diagnostic codes retrieved from TypeScript.
|
||||
ts_fixable_diagnostics: Vec<String>,
|
||||
|
@ -241,7 +241,7 @@ impl LanguageServer {
|
|||
}
|
||||
}
|
||||
|
||||
/// Similar to `deno cache` on the command line, where modules will be cached
|
||||
/// Similar to `deno install --entrypoint` on the command line, where modules will be cached
|
||||
/// in the Deno cache, including any of their dependencies.
|
||||
pub async fn cache(
|
||||
&self,
|
||||
|
@ -275,10 +275,9 @@ impl LanguageServer {
|
|||
factory.fs(),
|
||||
&roots,
|
||||
graph_util::GraphValidOptions {
|
||||
is_vendoring: false,
|
||||
follow_type_only: true,
|
||||
kind: GraphKind::All,
|
||||
check_js: false,
|
||||
exit_lockfile_errors: false,
|
||||
exit_integrity_errors: false,
|
||||
},
|
||||
)?;
|
||||
|
||||
|
@ -628,7 +627,7 @@ impl Inner {
|
|||
let maybe_root_path = self
|
||||
.config
|
||||
.root_uri()
|
||||
.and_then(|uri| specifier_to_file_path(uri).ok());
|
||||
.and_then(|uri| url_to_file_path(uri).ok());
|
||||
let root_cert_store = get_root_cert_store(
|
||||
maybe_root_path,
|
||||
workspace_settings.certificate_stores.clone(),
|
||||
|
@ -804,7 +803,7 @@ impl Inner {
|
|||
let mut roots = config
|
||||
.workspace_folders
|
||||
.iter()
|
||||
.filter_map(|p| specifier_to_file_path(&p.0).ok())
|
||||
.filter_map(|p| url_to_file_path(&p.0).ok())
|
||||
.collect::<Vec<_>>();
|
||||
roots.sort();
|
||||
let roots = roots
|
||||
|
@ -964,20 +963,36 @@ impl Inner {
|
|||
.tree
|
||||
.refresh(&self.config.settings, &self.workspace_files, &file_fetcher)
|
||||
.await;
|
||||
self
|
||||
.client
|
||||
.send_did_refresh_deno_configuration_tree_notification(
|
||||
self.config.tree.to_did_refresh_params(),
|
||||
);
|
||||
for config_file in self.config.tree.config_files() {
|
||||
(|| {
|
||||
let compiler_options = config_file.to_compiler_options().ok()?.options;
|
||||
let jsx_import_source = compiler_options.get("jsxImportSource")?;
|
||||
let jsx_import_source = jsx_import_source.as_str()?;
|
||||
let jsx_import_source = jsx_import_source.as_str()?.to_string();
|
||||
let referrer = config_file.specifier.clone();
|
||||
let specifier = Url::parse(&format!(
|
||||
"data:application/typescript;base64,{}",
|
||||
base64::engine::general_purpose::STANDARD
|
||||
.encode(format!("import '{jsx_import_source}/jsx-runtime';"))
|
||||
))
|
||||
.unwrap();
|
||||
let specifier = format!("{jsx_import_source}/jsx-runtime");
|
||||
self.task_queue.queue_task(Box::new(|ls: LanguageServer| {
|
||||
spawn(async move {
|
||||
let specifier = {
|
||||
let inner = ls.inner.read().await;
|
||||
let resolver = inner.resolver.as_graph_resolver(Some(&referrer));
|
||||
let Ok(specifier) = resolver.resolve(
|
||||
&specifier,
|
||||
&deno_graph::Range {
|
||||
specifier: referrer.clone(),
|
||||
start: deno_graph::Position::zeroed(),
|
||||
end: deno_graph::Position::zeroed(),
|
||||
},
|
||||
deno_graph::source::ResolutionMode::Types,
|
||||
) else {
|
||||
return;
|
||||
};
|
||||
specifier
|
||||
};
|
||||
if let Err(err) = ls.cache(vec![specifier], referrer, false).await {
|
||||
lsp_warn!("{:#}", err);
|
||||
}
|
||||
|
@ -1115,7 +1130,7 @@ impl Inner {
|
|||
{
|
||||
return;
|
||||
}
|
||||
match specifier_to_file_path(&specifier) {
|
||||
match url_to_file_path(&specifier) {
|
||||
Ok(path) if is_importable_ext(&path) => {}
|
||||
_ => return,
|
||||
}
|
||||
|
@ -1353,7 +1368,7 @@ impl Inner {
|
|||
{
|
||||
specifier = uri_to_url(¶ms.text_document.uri);
|
||||
}
|
||||
let file_path = specifier_to_file_path(&specifier).map_err(|err| {
|
||||
let file_path = url_to_file_path(&specifier).map_err(|err| {
|
||||
error!("{:#}", err);
|
||||
LspError::invalid_request()
|
||||
})?;
|
||||
|
@ -1375,18 +1390,9 @@ impl Inner {
|
|||
.data_for_specifier(&specifier)
|
||||
.map(|d| &d.member_dir.workspace);
|
||||
let unstable_options = UnstableFmtOptions {
|
||||
css: maybe_workspace
|
||||
.map(|w| w.has_unstable("fmt-css"))
|
||||
.unwrap_or(false),
|
||||
html: maybe_workspace
|
||||
.map(|w| w.has_unstable("fmt-html"))
|
||||
.unwrap_or(false),
|
||||
component: maybe_workspace
|
||||
.map(|w| w.has_unstable("fmt-component"))
|
||||
.unwrap_or(false),
|
||||
yaml: maybe_workspace
|
||||
.map(|w| w.has_unstable("fmt-yaml"))
|
||||
.unwrap_or(false),
|
||||
};
|
||||
let document = document.clone();
|
||||
move || {
|
||||
|
@ -1410,6 +1416,7 @@ impl Inner {
|
|||
document.content(),
|
||||
&fmt_options,
|
||||
&unstable_options,
|
||||
None,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
@ -1611,8 +1618,8 @@ impl Inner {
|
|||
None => false,
|
||||
})
|
||||
.collect();
|
||||
let mut code_actions = CodeActionCollection::default();
|
||||
if !fixable_diagnostics.is_empty() {
|
||||
let mut code_actions = CodeActionCollection::default();
|
||||
let file_diagnostics = self
|
||||
.diagnostics_server
|
||||
.get_ts_diagnostics(&specifier, asset_or_doc.document_lsp_version());
|
||||
|
@ -1720,9 +1727,14 @@ impl Inner {
|
|||
.add_cache_all_action(&specifier, no_cache_diagnostics.to_owned());
|
||||
}
|
||||
}
|
||||
code_actions.set_preferred_fixes();
|
||||
all_actions.extend(code_actions.get_response());
|
||||
}
|
||||
if let Some(document) = asset_or_doc.document() {
|
||||
code_actions
|
||||
.add_source_actions(document, ¶ms.range, self)
|
||||
.await;
|
||||
}
|
||||
code_actions.set_preferred_fixes();
|
||||
all_actions.extend(code_actions.get_response());
|
||||
|
||||
// Refactor
|
||||
let only = params
|
||||
|
@ -1911,6 +1923,7 @@ impl Inner {
|
|||
// as the import map is an implementation detail
|
||||
.and_then(|d| d.resolver.maybe_import_map()),
|
||||
self.resolver.as_ref(),
|
||||
file_referrer,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2507,7 +2520,7 @@ impl Inner {
|
|||
let maybe_root_path_owned = self
|
||||
.config
|
||||
.root_uri()
|
||||
.and_then(|uri| specifier_to_file_path(uri).ok());
|
||||
.and_then(|uri| url_to_file_path(uri).ok());
|
||||
let mut resolved_items = Vec::<CallHierarchyIncomingCall>::new();
|
||||
for item in incoming_calls.iter() {
|
||||
if let Some(resolved) = item.try_resolve_call_hierarchy_incoming_call(
|
||||
|
@ -2553,7 +2566,7 @@ impl Inner {
|
|||
let maybe_root_path_owned = self
|
||||
.config
|
||||
.root_uri()
|
||||
.and_then(|uri| specifier_to_file_path(uri).ok());
|
||||
.and_then(|uri| url_to_file_path(uri).ok());
|
||||
let mut resolved_items = Vec::<CallHierarchyOutgoingCall>::new();
|
||||
for item in outgoing_calls.iter() {
|
||||
if let Some(resolved) = item.try_resolve_call_hierarchy_outgoing_call(
|
||||
|
@ -2602,7 +2615,7 @@ impl Inner {
|
|||
let maybe_root_path_owned = self
|
||||
.config
|
||||
.root_uri()
|
||||
.and_then(|uri| specifier_to_file_path(uri).ok());
|
||||
.and_then(|uri| url_to_file_path(uri).ok());
|
||||
let mut resolved_items = Vec::<CallHierarchyItem>::new();
|
||||
match one_or_many {
|
||||
tsc::OneOrMany::One(item) => {
|
||||
|
@ -3599,7 +3612,10 @@ impl Inner {
|
|||
};
|
||||
let cli_options = CliOptions::new(
|
||||
Arc::new(Flags {
|
||||
cache_path: Some(self.cache.deno_dir().root.clone()),
|
||||
internal: InternalFlags {
|
||||
cache_path: Some(self.cache.deno_dir().root.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
ca_stores: workspace_settings.certificate_stores.clone(),
|
||||
ca_data: workspace_settings.tls_certificate.clone().map(CaData::File),
|
||||
unsafely_ignore_certificate_errors: workspace_settings
|
||||
|
@ -3612,6 +3628,11 @@ impl Inner {
|
|||
}),
|
||||
// bit of a hack to force the lsp to cache the @types/node package
|
||||
type_check_mode: crate::args::TypeCheckMode::Local,
|
||||
permissions: crate::args::PermissionFlags {
|
||||
// allow remote import permissions in the lsp for now
|
||||
allow_import: Some(vec![]),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}),
|
||||
initial_cwd,
|
||||
|
@ -3859,7 +3880,11 @@ impl Inner {
|
|||
|
||||
</details>
|
||||
"#,
|
||||
serde_json::to_string_pretty(&workspace_settings).unwrap(),
|
||||
serde_json::to_string_pretty(&workspace_settings)
|
||||
.inspect_err(|e| {
|
||||
dbg!(e);
|
||||
})
|
||||
.unwrap(),
|
||||
documents_specifiers.len(),
|
||||
documents_specifiers
|
||||
.into_iter()
|
||||
|
|
|
@ -46,6 +46,30 @@ pub struct DiagnosticBatchNotificationParams {
|
|||
pub messages_len: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DenoConfigurationData {
|
||||
pub scope_uri: lsp::Uri,
|
||||
pub workspace_root_scope_uri: Option<lsp::Uri>,
|
||||
pub deno_json: Option<lsp::TextDocumentIdentifier>,
|
||||
pub package_json: Option<lsp::TextDocumentIdentifier>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DidRefreshDenoConfigurationTreeNotificationParams {
|
||||
pub data: Vec<DenoConfigurationData>,
|
||||
}
|
||||
|
||||
pub enum DidRefreshDenoConfigurationTreeNotification {}
|
||||
|
||||
impl lsp::notification::Notification
|
||||
for DidRefreshDenoConfigurationTreeNotification
|
||||
{
|
||||
type Params = DidRefreshDenoConfigurationTreeNotificationParams;
|
||||
const METHOD: &'static str = "deno/didRefreshDenoConfigurationTree";
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, Hash, PartialEq, Copy, Clone, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum DenoConfigurationChangeType {
|
||||
|
@ -88,13 +112,15 @@ pub struct DidChangeDenoConfigurationNotificationParams {
|
|||
pub changes: Vec<DenoConfigurationChangeEvent>,
|
||||
}
|
||||
|
||||
// TODO(nayeemrmn): This is being replaced by
|
||||
// `DidRefreshDenoConfigurationTreeNotification` for Deno > v2.0.0. Remove it
|
||||
// soon.
|
||||
pub enum DidChangeDenoConfigurationNotification {}
|
||||
|
||||
impl lsp::notification::Notification
|
||||
for DidChangeDenoConfigurationNotification
|
||||
{
|
||||
type Params = DidChangeDenoConfigurationNotificationParams;
|
||||
|
||||
const METHOD: &'static str = "deno/didChangeDenoConfiguration";
|
||||
}
|
||||
|
||||
|
@ -102,7 +128,6 @@ pub enum DidUpgradeCheckNotification {}
|
|||
|
||||
impl lsp::notification::Notification for DidUpgradeCheckNotification {
|
||||
type Params = DidUpgradeCheckNotificationParams;
|
||||
|
||||
const METHOD: &'static str = "deno/didUpgradeCheck";
|
||||
}
|
||||
|
||||
|
@ -125,6 +150,5 @@ pub enum DiagnosticBatchNotification {}
|
|||
|
||||
impl lsp::notification::Notification for DiagnosticBatchNotification {
|
||||
type Params = DiagnosticBatchNotificationParams;
|
||||
|
||||
const METHOD: &'static str = "deno/internalTestDiagnosticBatch";
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ 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_semver::package::PackageNv;
|
||||
use deno_semver::Version;
|
||||
use serde::Deserialize;
|
||||
|
@ -55,7 +54,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()
|
||||
})
|
||||
|
|
|
@ -16,6 +16,7 @@ use crate::args::CacheSetting;
|
|||
use crate::cache::GlobalHttpCache;
|
||||
use crate::cache::HttpCache;
|
||||
use crate::file_fetcher::FetchOptions;
|
||||
use crate::file_fetcher::FetchPermissionsOptionRef;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
|
||||
|
@ -30,7 +31,6 @@ use deno_core::url::Position;
|
|||
use deno_core::url::Url;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_graph::Dependency;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use log::error;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::borrow::Cow;
|
||||
|
@ -481,7 +481,7 @@ impl ModuleRegistry {
|
|||
file_fetcher
|
||||
.fetch_with_options(FetchOptions {
|
||||
specifier: &specifier,
|
||||
permissions: &PermissionsContainer::allow_all(),
|
||||
permissions: FetchPermissionsOptionRef::AllowAll,
|
||||
maybe_accept: Some("application/vnd.deno.reg.v2+json, application/vnd.deno.reg.v1+json;q=0.9, application/json;q=0.8"),
|
||||
maybe_cache_setting: None,
|
||||
})
|
||||
|
@ -584,7 +584,7 @@ impl ModuleRegistry {
|
|||
let file = deno_core::unsync::spawn({
|
||||
async move {
|
||||
file_fetcher
|
||||
.fetch(&endpoint, &PermissionsContainer::allow_all())
|
||||
.fetch_bypass_permissions(&endpoint)
|
||||
.await
|
||||
.ok()?
|
||||
.into_text_decoded()
|
||||
|
@ -983,7 +983,7 @@ impl ModuleRegistry {
|
|||
// spawn due to the lsp's `Send` requirement
|
||||
let file = deno_core::unsync::spawn(async move {
|
||||
file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.fetch_bypass_permissions(&specifier)
|
||||
.await
|
||||
.ok()?
|
||||
.into_text_decoded()
|
||||
|
@ -1049,7 +1049,7 @@ impl ModuleRegistry {
|
|||
let specifier = specifier.clone();
|
||||
async move {
|
||||
file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.fetch_bypass_permissions(&specifier)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!(
|
||||
|
@ -1095,7 +1095,7 @@ impl ModuleRegistry {
|
|||
let specifier = specifier.clone();
|
||||
async move {
|
||||
file_fetcher
|
||||
.fetch(&specifier, &PermissionsContainer::allow_all())
|
||||
.fetch_bypass_permissions(&specifier)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!(
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::str::FromStr;
|
||||
|
||||
use deno_ast::LineAndColumnIndex;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
|
@ -42,6 +41,7 @@ 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)]
|
||||
|
@ -263,7 +263,8 @@ impl ReplLanguageServer {
|
|||
}
|
||||
|
||||
fn get_document_uri(&self) -> Uri {
|
||||
Uri::from_str(self.cwd_uri.join("$deno$repl.ts").unwrap().as_str()).unwrap()
|
||||
uri_parse_unencoded(self.cwd_uri.join("$deno$repl.ts").unwrap().as_str())
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -311,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,28 +1,5 @@
|
|||
// 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::NpmInstallDepsProvider;
|
||||
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::HttpCache;
|
||||
|
@ -33,10 +10,10 @@ use deno_graph::source::Resolver;
|
|||
use deno_graph::GraphImport;
|
||||
use deno_graph::ModuleSpecifier;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_path_util::url_to_file_path;
|
||||
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_semver::jsr::JsrPackageReqReference;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use deno_semver::package::PackageNv;
|
||||
|
@ -55,6 +32,30 @@ use std::sync::Arc;
|
|||
|
||||
use super::cache::LspCache;
|
||||
use super::jsr::JsrCacheResolver;
|
||||
use crate::args::create_default_npmrc;
|
||||
use crate::args::CacheSetting;
|
||||
use crate::args::CliLockfile;
|
||||
use crate::args::NpmInstallDepsProvider;
|
||||
use crate::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::CliNpmResolver;
|
||||
use crate::npm::CliNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||
use crate::npm::ManagedCliNpmResolver;
|
||||
use crate::resolver::CjsResolutionStore;
|
||||
use crate::resolver::CliDenoResolverFs;
|
||||
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;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct LspScopeResolver {
|
||||
|
@ -327,11 +328,11 @@ impl LspResolver {
|
|||
) -> 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(
|
||||
Some(NodeResolution::into_specifier_and_media_type(Some(
|
||||
node_resolver
|
||||
.resolve_req_reference(req_ref, referrer, NodeResolutionMode::Types)
|
||||
.ok(),
|
||||
))
|
||||
.ok()?,
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn in_node_modules(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
|
@ -372,6 +373,26 @@ impl LspResolver {
|
|||
Some(NodeResolution::into_specifier_and_media_type(Some(resolution)).1)
|
||||
}
|
||||
|
||||
pub fn is_bare_package_json_dep(
|
||||
&self,
|
||||
specifier_text: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> bool {
|
||||
let resolver = self.get_scope_resolver(Some(referrer));
|
||||
let Some(node_resolver) = resolver.node_resolver.as_ref() else {
|
||||
return false;
|
||||
};
|
||||
node_resolver
|
||||
.resolve_if_for_npm_pkg(
|
||||
specifier_text,
|
||||
referrer,
|
||||
NodeResolutionMode::Types,
|
||||
)
|
||||
.ok()
|
||||
.flatten()
|
||||
.is_some()
|
||||
}
|
||||
|
||||
pub fn get_closest_package_json(
|
||||
&self,
|
||||
referrer: &ModuleSpecifier,
|
||||
|
@ -439,11 +460,11 @@ async fn create_npm_resolver(
|
|||
) -> Option<Arc<dyn CliNpmResolver>> {
|
||||
let enable_byonm = config_data.map(|d| d.byonm).unwrap_or(false);
|
||||
let options = if enable_byonm {
|
||||
CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions {
|
||||
fs: Arc::new(deno_fs::RealFs),
|
||||
CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions {
|
||||
fs: CliDenoResolverFs(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)
|
||||
url_to_file_path(&config_data.scope)
|
||||
.ok()
|
||||
.map(|p| p.join("node_modules/"))
|
||||
})
|
||||
|
|
|
@ -12,6 +12,7 @@ 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;
|
||||
|
@ -30,13 +31,13 @@ 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 lsp_types::Uri;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
|
@ -219,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() {
|
||||
|
@ -275,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);
|
||||
|
@ -304,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,
|
||||
|
@ -452,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
|
||||
}
|
||||
|
@ -529,7 +535,7 @@ impl LspTestDescription {
|
|||
&self,
|
||||
tests: &IndexMap<usize, LspTestDescription>,
|
||||
) -> lsp_custom::TestIdentifier {
|
||||
let uri = Uri::from_str(&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() {
|
||||
|
|
|
@ -39,7 +39,6 @@ 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;
|
||||
|
@ -63,6 +62,7 @@ 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;
|
||||
|
@ -3191,7 +3191,7 @@ impl CallHierarchyItem {
|
|||
let use_file_name = self.is_source_file_item();
|
||||
let maybe_file_path = if uri.scheme().is_some_and(|s| s.as_str() == "file")
|
||||
{
|
||||
specifier_to_file_path(&uri_to_url(&uri)).ok()
|
||||
url_to_file_path(&uri_to_url(&uri)).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -3939,7 +3939,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(
|
||||
|
@ -5475,7 +5475,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"noEmit": true,
|
||||
"lib": [],
|
||||
}),
|
||||
|
@ -5521,7 +5520,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"jsx": "react",
|
||||
"lib": ["esnext", "dom", "deno.ns"],
|
||||
"noEmit": true,
|
||||
|
@ -5547,7 +5545,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -5578,7 +5575,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -5624,7 +5620,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -5655,7 +5650,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -5722,7 +5716,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -5800,7 +5793,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, cache) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -5942,7 +5934,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -6093,7 +6084,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -6164,7 +6154,7 @@ mod tests {
|
|||
let change = changes.text_changes.first().unwrap();
|
||||
assert_eq!(
|
||||
change.new_text,
|
||||
"import { someLongVariable } from './b.ts'\n"
|
||||
"import type { someLongVariable } from './b.ts'\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -6202,7 +6192,6 @@ mod tests {
|
|||
let (temp_dir, ts_server, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
@ -6273,7 +6262,6 @@ mod tests {
|
|||
let (temp_dir, _, snapshot, _) = setup(
|
||||
json!({
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"lib": ["deno.ns", "deno.window"],
|
||||
"noEmit": true,
|
||||
}),
|
||||
|
|
|
@ -55,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() {
|
||||
|
@ -122,8 +141,33 @@ impl LspUrlMapInner {
|
|||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
Ok(Uri::from_str(url.as_str()).inspect_err(|err| {
|
||||
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}")
|
||||
})?)
|
||||
}
|
||||
|
@ -189,7 +233,7 @@ impl LspUrlMap {
|
|||
} else {
|
||||
to_deno_uri(specifier)
|
||||
};
|
||||
let uri = Uri::from_str(&uri_str)?;
|
||||
let uri = uri_parse_unencoded(&uri_str)?;
|
||||
inner.put(specifier.clone(), uri.clone());
|
||||
uri
|
||||
};
|
||||
|
|
94
cli/main.rs
94
cli/main.rs
|
@ -37,6 +37,7 @@ use crate::util::v8::get_v8_flags_from_env;
|
|||
use crate::util::v8::init_v8_flags;
|
||||
|
||||
use args::TaskFlags;
|
||||
use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError;
|
||||
use deno_runtime::WorkerExecutionMode;
|
||||
pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS;
|
||||
|
||||
|
@ -50,14 +51,20 @@ use deno_runtime::fmt_errors::format_js_error;
|
|||
use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics;
|
||||
use deno_terminal::colors;
|
||||
use factory::CliFactory;
|
||||
use npm::ResolvePkgFolderFromDenoReqError;
|
||||
use standalone::MODULE_NOT_FOUND;
|
||||
use standalone::UNSUPPORTED_SCHEME;
|
||||
use std::env;
|
||||
use std::future::Future;
|
||||
use std::io::IsTerminal;
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
#[global_allocator]
|
||||
static ALLOC: dhat::Alloc = dhat::Alloc;
|
||||
|
||||
/// Ensures that all subcommands return an i32 exit code and an [`AnyError`] error type.
|
||||
trait SubcommandOutput {
|
||||
fn output(self) -> Result<i32, AnyError>;
|
||||
|
@ -120,12 +127,7 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
|||
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()
|
||||
|
@ -161,9 +163,21 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
|||
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(
|
||||
|
@ -186,6 +200,21 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
|
|||
match result {
|
||||
Ok(v) => Ok(v),
|
||||
Err(script_err) => {
|
||||
if let Some(ResolvePkgFolderFromDenoReqError::Byonm(ByonmResolvePkgFolderFromDenoReqError::UnmatchedReq(_))) = script_err.downcast_ref::<ResolvePkgFolderFromDenoReqError>() {
|
||||
if flags.node_modules_dir.is_none() {
|
||||
let mut flags = flags.deref().clone();
|
||||
let watch = match &flags.subcommand {
|
||||
DenoSubcommand::Run(run_flags) => run_flags.watch.clone(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
flags.node_modules_dir = Some(deno_config::deno_json::NodeModulesDirMode::None);
|
||||
// use the current lockfile, but don't write it out
|
||||
if flags.frozen_lockfile.is_none() {
|
||||
flags.internal.lockfile_skip_write = true;
|
||||
}
|
||||
return tools::run::run_script(WorkerExecutionMode::Run, Arc::new(flags), watch).await;
|
||||
}
|
||||
}
|
||||
let script_err_msg = script_err.to_string();
|
||||
if script_err_msg.starts_with(MODULE_NOT_FOUND) || script_err_msg.starts_with(UNSUPPORTED_SCHEME) {
|
||||
if run_flags.bare {
|
||||
|
@ -361,19 +390,10 @@ pub(crate) fn unstable_exit_cb(feature: &str, api_name: &str) {
|
|||
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
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
let profiler = dhat::Profiler::new_heap();
|
||||
|
||||
setup_panic_hook();
|
||||
|
||||
util::unix::raise_fd_limit();
|
||||
|
@ -394,7 +414,12 @@ pub fn main() {
|
|||
run_subcommand(Arc::new(flags)).await
|
||||
};
|
||||
|
||||
match create_and_run_current_thread_with_maybe_metrics(future) {
|
||||
let result = create_and_run_current_thread_with_maybe_metrics(future);
|
||||
|
||||
#[cfg(feature = "dhat-heap")]
|
||||
drop(profiler);
|
||||
|
||||
match result {
|
||||
Ok(exit_code) => std::process::exit(exit_code),
|
||||
Err(err) => exit_for_error(err),
|
||||
}
|
||||
|
@ -415,25 +440,14 @@ fn resolve_flags_and_init(
|
|||
Err(err) => exit_for_error(AnyError::from(err)),
|
||||
};
|
||||
|
||||
// TODO(bartlomieju): remove when `--unstable` flag is removed.
|
||||
// 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 {
|
||||
|
@ -450,7 +464,7 @@ fn resolve_flags_and_init(
|
|||
};
|
||||
|
||||
init_v8_flags(&default_v8_flags, &flags.v8_flags, get_v8_flags_from_env());
|
||||
// TODO(bartlomieju): remove last argument in Deno 2.
|
||||
// TODO(bartlomieju): remove last argument once Deploy no longer needs it
|
||||
deno_core::JsRuntime::init_platform(
|
||||
None, /* import assertions enabled */ false,
|
||||
);
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
|
@ -24,6 +23,7 @@ use crate::graph_container::ModuleGraphUpdatePermit;
|
|||
use crate::graph_util::CreateGraphOptions;
|
||||
use crate::graph_util::ModuleGraphBuilder;
|
||||
use crate::node;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::resolver::CliGraphResolver;
|
||||
use crate::resolver::CliNodeResolver;
|
||||
use crate::resolver::ModuleCodeStringSource;
|
||||
|
@ -44,7 +44,6 @@ use deno_core::error::generic_error;
|
|||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::future::FutureExt;
|
||||
use deno_core::futures::Future;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::resolve_url;
|
||||
use deno_core::ModuleCodeString;
|
||||
use deno_core::ModuleLoader;
|
||||
|
@ -107,11 +106,32 @@ impl ModuleLoadPreparer {
|
|||
is_dynamic: bool,
|
||||
lib: TsTypeLib,
|
||||
permissions: PermissionsContainer,
|
||||
ext_overwrite: Option<&String>,
|
||||
) -> Result<(), AnyError> {
|
||||
log::debug!("Preparing module load.");
|
||||
let _pb_clear_guard = self.progress_bar.clear_guard();
|
||||
|
||||
let mut cache = self.module_graph_builder.create_fetch_cacher(permissions);
|
||||
if let Some(ext) = ext_overwrite {
|
||||
let maybe_content_type = match ext.as_str() {
|
||||
"ts" => Some("text/typescript"),
|
||||
"tsx" => Some("text/tsx"),
|
||||
"js" => Some("text/javascript"),
|
||||
"jsx" => Some("text/jsx"),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(content_type) = maybe_content_type {
|
||||
for root in roots {
|
||||
cache.file_header_overrides.insert(
|
||||
root.clone(),
|
||||
std::collections::HashMap::from([(
|
||||
"content-type".to_string(),
|
||||
content_type.to_string(),
|
||||
)]),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
log::debug!("Building module graph.");
|
||||
let has_type_checked = !graph.roots.is_empty();
|
||||
|
||||
|
@ -184,6 +204,7 @@ struct SharedCliModuleLoaderState {
|
|||
main_module_graph_container: Arc<MainModuleGraphContainer>,
|
||||
module_load_preparer: Arc<ModuleLoadPreparer>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
npm_module_loader: NpmModuleLoader,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
resolver: Arc<CliGraphResolver>,
|
||||
|
@ -202,6 +223,7 @@ impl CliModuleLoaderFactory {
|
|||
main_module_graph_container: Arc<MainModuleGraphContainer>,
|
||||
module_load_preparer: Arc<ModuleLoadPreparer>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
npm_module_loader: NpmModuleLoader,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
resolver: Arc<CliGraphResolver>,
|
||||
|
@ -222,6 +244,7 @@ impl CliModuleLoaderFactory {
|
|||
main_module_graph_container,
|
||||
module_load_preparer,
|
||||
node_resolver,
|
||||
npm_resolver,
|
||||
npm_module_loader,
|
||||
parsed_source_cache,
|
||||
resolver,
|
||||
|
@ -233,18 +256,19 @@ impl CliModuleLoaderFactory {
|
|||
&self,
|
||||
graph_container: TGraphContainer,
|
||||
lib: TsTypeLib,
|
||||
root_permissions: PermissionsContainer,
|
||||
dynamic_permissions: PermissionsContainer,
|
||||
is_worker: bool,
|
||||
parent_permissions: PermissionsContainer,
|
||||
permissions: PermissionsContainer,
|
||||
) -> ModuleLoaderAndSourceMapGetter {
|
||||
let loader = Rc::new(CliModuleLoader(Rc::new(CliModuleLoaderInner {
|
||||
lib,
|
||||
root_permissions,
|
||||
dynamic_permissions,
|
||||
is_worker,
|
||||
parent_permissions,
|
||||
permissions,
|
||||
graph_container,
|
||||
emitter: self.shared.emitter.clone(),
|
||||
parsed_source_cache: self.shared.parsed_source_cache.clone(),
|
||||
shared: self.shared.clone(),
|
||||
prevent_v8_code_cache: Default::default(),
|
||||
})));
|
||||
ModuleLoaderAndSourceMapGetter {
|
||||
module_loader: loader,
|
||||
|
@ -256,20 +280,20 @@ impl ModuleLoaderFactory for CliModuleLoaderFactory {
|
|||
fn create_for_main(
|
||||
&self,
|
||||
root_permissions: PermissionsContainer,
|
||||
dynamic_permissions: PermissionsContainer,
|
||||
) -> ModuleLoaderAndSourceMapGetter {
|
||||
self.create_with_lib(
|
||||
(*self.shared.main_module_graph_container).clone(),
|
||||
self.shared.lib_window,
|
||||
/* is worker */ false,
|
||||
root_permissions.clone(),
|
||||
root_permissions,
|
||||
dynamic_permissions,
|
||||
)
|
||||
}
|
||||
|
||||
fn create_for_worker(
|
||||
&self,
|
||||
root_permissions: PermissionsContainer,
|
||||
dynamic_permissions: PermissionsContainer,
|
||||
parent_permissions: PermissionsContainer,
|
||||
permissions: PermissionsContainer,
|
||||
) -> ModuleLoaderAndSourceMapGetter {
|
||||
self.create_with_lib(
|
||||
// create a fresh module graph for the worker
|
||||
|
@ -277,29 +301,25 @@ impl ModuleLoaderFactory for CliModuleLoaderFactory {
|
|||
self.shared.graph_kind,
|
||||
))),
|
||||
self.shared.lib_worker,
|
||||
root_permissions,
|
||||
dynamic_permissions,
|
||||
/* is worker */ true,
|
||||
parent_permissions,
|
||||
permissions,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct CliModuleLoaderInner<TGraphContainer: ModuleGraphContainer> {
|
||||
lib: TsTypeLib,
|
||||
is_worker: bool,
|
||||
/// The initial set of permissions used to resolve the static imports in the
|
||||
/// worker. These are "allow all" for main worker, and parent thread
|
||||
/// permissions for Web Worker.
|
||||
root_permissions: PermissionsContainer,
|
||||
/// Permissions used to resolve dynamic imports, these get passed as
|
||||
/// "root permissions" for Web Worker.
|
||||
dynamic_permissions: PermissionsContainer,
|
||||
parent_permissions: PermissionsContainer,
|
||||
permissions: PermissionsContainer,
|
||||
shared: Arc<SharedCliModuleLoaderState>,
|
||||
emitter: Arc<Emitter>,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
graph_container: TGraphContainer,
|
||||
// NOTE(bartlomieju): this is temporary, for deprecated import assertions.
|
||||
// Should be removed in Deno 2.
|
||||
// Modules stored here should not be V8 code-cached.
|
||||
prevent_v8_code_cache: Arc<Mutex<HashSet<String>>>,
|
||||
}
|
||||
|
||||
impl<TGraphContainer: ModuleGraphContainer>
|
||||
|
@ -311,15 +331,23 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
requested_module_type: RequestedModuleType,
|
||||
) -> Result<ModuleSource, AnyError> {
|
||||
let code_source = if let Some(result) = self
|
||||
.shared
|
||||
.npm_module_loader
|
||||
.load_if_in_npm_package(specifier, maybe_referrer)
|
||||
.await
|
||||
{
|
||||
result?
|
||||
} else {
|
||||
self.load_prepared_module(specifier, maybe_referrer).await?
|
||||
let code_source = match self.load_prepared_module(specifier).await? {
|
||||
Some(code_source) => code_source,
|
||||
None => {
|
||||
if self.shared.npm_module_loader.if_in_npm_package(specifier) {
|
||||
self
|
||||
.shared
|
||||
.npm_module_loader
|
||||
.load(specifier, maybe_referrer)
|
||||
.await?
|
||||
} else {
|
||||
let mut msg = format!("Loading unprepared module: {specifier}");
|
||||
if let Some(referrer) = maybe_referrer {
|
||||
msg = format!("{}, imported from: {}", msg, referrer.as_str());
|
||||
}
|
||||
return Err(anyhow!(msg));
|
||||
}
|
||||
}
|
||||
};
|
||||
let code = if self.shared.is_inspecting {
|
||||
// we need the code with the source map in order for
|
||||
|
@ -462,7 +490,6 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
Some(Module::Npm(module)) => {
|
||||
let package_folder = self
|
||||
.shared
|
||||
.node_resolver
|
||||
.npm_resolver
|
||||
.as_managed()
|
||||
.unwrap() // byonm won't create a Module::Npm
|
||||
|
@ -495,17 +522,12 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
async fn load_prepared_module(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Result<ModuleCodeStringSource, AnyError> {
|
||||
) -> Result<Option<ModuleCodeStringSource>, AnyError> {
|
||||
// Note: keep this in sync with the sync version below
|
||||
let graph = self.graph_container.graph();
|
||||
match self.load_prepared_module_or_defer_emit(
|
||||
&graph,
|
||||
specifier,
|
||||
maybe_referrer,
|
||||
) {
|
||||
Ok(CodeOrDeferredEmit::Code(code_source)) => Ok(code_source),
|
||||
Ok(CodeOrDeferredEmit::DeferredEmit {
|
||||
match self.load_prepared_module_or_defer_emit(&graph, specifier)? {
|
||||
Some(CodeOrDeferredEmit::Code(code_source)) => Ok(Some(code_source)),
|
||||
Some(CodeOrDeferredEmit::DeferredEmit {
|
||||
specifier,
|
||||
media_type,
|
||||
source,
|
||||
|
@ -518,30 +540,25 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
// at this point, we no longer need the parsed source in memory, so free it
|
||||
self.parsed_source_cache.free(specifier);
|
||||
|
||||
Ok(ModuleCodeStringSource {
|
||||
Ok(Some(ModuleCodeStringSource {
|
||||
code: ModuleSourceCode::Bytes(transpile_result),
|
||||
found_url: specifier.clone(),
|
||||
media_type,
|
||||
})
|
||||
}))
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_prepared_module_sync(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Result<ModuleCodeStringSource, AnyError> {
|
||||
) -> Result<Option<ModuleCodeStringSource>, AnyError> {
|
||||
// Note: keep this in sync with the async version above
|
||||
let graph = self.graph_container.graph();
|
||||
match self.load_prepared_module_or_defer_emit(
|
||||
&graph,
|
||||
specifier,
|
||||
maybe_referrer,
|
||||
) {
|
||||
Ok(CodeOrDeferredEmit::Code(code_source)) => Ok(code_source),
|
||||
Ok(CodeOrDeferredEmit::DeferredEmit {
|
||||
match self.load_prepared_module_or_defer_emit(&graph, specifier)? {
|
||||
Some(CodeOrDeferredEmit::Code(code_source)) => Ok(Some(code_source)),
|
||||
Some(CodeOrDeferredEmit::DeferredEmit {
|
||||
specifier,
|
||||
media_type,
|
||||
source,
|
||||
|
@ -553,13 +570,13 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
// at this point, we no longer need the parsed source in memory, so free it
|
||||
self.parsed_source_cache.free(specifier);
|
||||
|
||||
Ok(ModuleCodeStringSource {
|
||||
Ok(Some(ModuleCodeStringSource {
|
||||
code: ModuleSourceCode::Bytes(transpile_result),
|
||||
found_url: specifier.clone(),
|
||||
media_type,
|
||||
})
|
||||
}))
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -567,8 +584,7 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
&self,
|
||||
graph: &'graph ModuleGraph,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Result<CodeOrDeferredEmit<'graph>, AnyError> {
|
||||
) -> Result<Option<CodeOrDeferredEmit<'graph>>, AnyError> {
|
||||
if specifier.scheme() == "node" {
|
||||
// Node built-in modules should be handled internally.
|
||||
unreachable!("Deno bug. {} was misconfigured internally.", specifier);
|
||||
|
@ -580,11 +596,11 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
media_type,
|
||||
specifier,
|
||||
..
|
||||
})) => Ok(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
|
||||
})) => Ok(Some(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
|
||||
code: ModuleSourceCode::String(source.clone().into()),
|
||||
found_url: specifier.clone(),
|
||||
media_type: *media_type,
|
||||
})),
|
||||
}))),
|
||||
Some(deno_graph::Module::Js(JsModule {
|
||||
source,
|
||||
media_type,
|
||||
|
@ -605,11 +621,11 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
| MediaType::Cts
|
||||
| MediaType::Jsx
|
||||
| MediaType::Tsx => {
|
||||
return Ok(CodeOrDeferredEmit::DeferredEmit {
|
||||
return Ok(Some(CodeOrDeferredEmit::DeferredEmit {
|
||||
specifier,
|
||||
media_type: *media_type,
|
||||
source,
|
||||
});
|
||||
}));
|
||||
}
|
||||
MediaType::TsBuildInfo | MediaType::Wasm | MediaType::SourceMap => {
|
||||
panic!("Unexpected media type {media_type} for {specifier}")
|
||||
|
@ -619,24 +635,18 @@ impl<TGraphContainer: ModuleGraphContainer>
|
|||
// at this point, we no longer need the parsed source in memory, so free it
|
||||
self.parsed_source_cache.free(specifier);
|
||||
|
||||
Ok(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
|
||||
Ok(Some(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
|
||||
code: ModuleSourceCode::String(code),
|
||||
found_url: specifier.clone(),
|
||||
media_type: *media_type,
|
||||
}))
|
||||
})))
|
||||
}
|
||||
Some(
|
||||
deno_graph::Module::External(_)
|
||||
| deno_graph::Module::Node(_)
|
||||
| deno_graph::Module::Npm(_),
|
||||
)
|
||||
| None => {
|
||||
let mut msg = format!("Loading unprepared module: {specifier}");
|
||||
if let Some(referrer) = maybe_referrer {
|
||||
msg = format!("{}, imported from: {}", msg, referrer.as_str());
|
||||
}
|
||||
Err(anyhow!(msg))
|
||||
}
|
||||
| None => Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -755,11 +765,12 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
|
|||
}
|
||||
}
|
||||
|
||||
let root_permissions = if is_dynamic {
|
||||
inner.dynamic_permissions.clone()
|
||||
let permissions = if is_dynamic {
|
||||
inner.permissions.clone()
|
||||
} else {
|
||||
inner.root_permissions.clone()
|
||||
inner.parent_permissions.clone()
|
||||
};
|
||||
let is_dynamic = is_dynamic || inner.is_worker; // consider workers as dynamic for permissions
|
||||
let lib = inner.lib;
|
||||
let mut update_permit = graph_container.acquire_update_permit().await;
|
||||
let graph = update_permit.graph_mut();
|
||||
|
@ -769,7 +780,8 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
|
|||
&[specifier],
|
||||
is_dynamic,
|
||||
lib,
|
||||
root_permissions,
|
||||
permissions,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
update_permit.commit();
|
||||
|
@ -785,14 +797,6 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
|
|||
code_cache: &[u8],
|
||||
) -> Pin<Box<dyn Future<Output = ()>>> {
|
||||
if let Some(cache) = self.0.shared.code_cache.as_ref() {
|
||||
if self
|
||||
.0
|
||||
.prevent_v8_code_cache
|
||||
.lock()
|
||||
.contains(specifier.as_str())
|
||||
{
|
||||
return std::future::ready(()).boxed_local();
|
||||
}
|
||||
// This log line is also used by tests.
|
||||
log::debug!(
|
||||
"Updating V8 code cache for ES module: {specifier}, [{source_hash:?}]"
|
||||
|
@ -807,19 +811,6 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
|
|||
std::future::ready(()).boxed_local()
|
||||
}
|
||||
|
||||
fn purge_and_prevent_code_cache(&self, specifier: &str) {
|
||||
if let Some(cache) = self.0.shared.code_cache.as_ref() {
|
||||
// This log line is also used by tests.
|
||||
log::debug!("Remove V8 code cache for ES module: {specifier}");
|
||||
cache.remove_code_cache(specifier);
|
||||
self
|
||||
.0
|
||||
.prevent_v8_code_cache
|
||||
.lock()
|
||||
.insert(specifier.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
fn get_source_map(&self, file_name: &str) -> Option<Vec<u8>> {
|
||||
let specifier = resolve_url(file_name).ok()?;
|
||||
match specifier.scheme() {
|
||||
|
@ -828,7 +819,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
|
|||
"wasm" | "file" | "http" | "https" | "data" | "blob" => (),
|
||||
_ => return None,
|
||||
}
|
||||
let source = self.0.load_prepared_module_sync(&specifier, None).ok()?;
|
||||
let source = self.0.load_prepared_module_sync(&specifier).ok()??;
|
||||
source_map_from_code(source.code.as_bytes())
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
{ "node_api_create_syntax_error"; "napi_make_callback"; "napi_has_named_property"; "napi_async_destroy"; "napi_coerce_to_object"; "napi_get_arraybuffer_info"; "napi_detach_arraybuffer"; "napi_get_undefined"; "napi_reference_unref"; "napi_fatal_error"; "napi_open_callback_scope"; "napi_close_callback_scope"; "napi_get_value_uint32"; "napi_create_function"; "napi_create_arraybuffer"; "napi_get_value_int64"; "napi_get_all_property_names"; "napi_resolve_deferred"; "napi_is_detached_arraybuffer"; "napi_create_string_utf8"; "napi_create_threadsafe_function"; "node_api_throw_syntax_error"; "napi_create_bigint_int64"; "napi_wrap"; "napi_set_property"; "napi_get_value_bigint_int64"; "napi_open_handle_scope"; "napi_create_error"; "napi_create_buffer"; "napi_cancel_async_work"; "napi_is_exception_pending"; "napi_acquire_threadsafe_function"; "napi_create_external"; "napi_get_threadsafe_function_context"; "napi_get_null"; "napi_create_string_utf16"; "node_api_create_external_string_utf16"; "napi_get_value_bigint_uint64"; "napi_module_register"; "napi_is_typedarray"; "napi_create_external_buffer"; "napi_get_new_target"; "napi_get_instance_data"; "napi_close_handle_scope"; "napi_get_value_string_utf16"; "napi_get_property_names"; "napi_is_arraybuffer"; "napi_get_cb_info"; "napi_define_properties"; "napi_add_env_cleanup_hook"; "node_api_get_module_file_name"; "napi_get_node_version"; "napi_create_int64"; "napi_create_double"; "napi_get_and_clear_last_exception"; "napi_create_reference"; "napi_get_typedarray_info"; "napi_call_threadsafe_function"; "napi_get_last_error_info"; "napi_create_array_with_length"; "napi_coerce_to_number"; "napi_get_global"; "napi_is_error"; "napi_set_instance_data"; "napi_create_typedarray"; "napi_throw_type_error"; "napi_has_property"; "napi_get_value_external"; "napi_create_range_error"; "napi_typeof"; "napi_ref_threadsafe_function"; "napi_create_bigint_uint64"; "napi_get_prototype"; "napi_adjust_external_memory"; "napi_release_threadsafe_function"; "napi_delete_async_work"; "napi_create_string_latin1"; "node_api_create_external_string_latin1"; "napi_is_array"; "napi_unref_threadsafe_function"; "napi_throw_error"; "napi_has_own_property"; "napi_get_reference_value"; "napi_remove_env_cleanup_hook"; "napi_get_value_string_utf8"; "napi_is_promise"; "napi_get_boolean"; "napi_run_script"; "napi_get_element"; "napi_get_named_property"; "napi_get_buffer_info"; "napi_get_value_bool"; "napi_reference_ref"; "napi_create_object"; "napi_create_promise"; "napi_create_int32"; "napi_escape_handle"; "napi_open_escapable_handle_scope"; "napi_throw"; "napi_get_value_double"; "napi_set_named_property"; "napi_call_function"; "napi_create_date"; "napi_object_freeze"; "napi_get_uv_event_loop"; "napi_get_value_string_latin1"; "napi_reject_deferred"; "napi_add_finalizer"; "napi_create_array"; "napi_delete_reference"; "napi_get_date_value"; "napi_create_dataview"; "napi_get_version"; "napi_define_class"; "napi_is_date"; "napi_remove_wrap"; "napi_delete_property"; "napi_instanceof"; "napi_create_buffer_copy"; "napi_delete_element"; "napi_object_seal"; "napi_queue_async_work"; "napi_get_value_bigint_words"; "napi_is_buffer"; "napi_get_array_length"; "napi_get_property"; "napi_new_instance"; "napi_set_element"; "napi_create_bigint_words"; "napi_strict_equals"; "napi_is_dataview"; "napi_close_escapable_handle_scope"; "napi_get_dataview_info"; "napi_get_value_int32"; "napi_unwrap"; "napi_throw_range_error"; "napi_coerce_to_bool"; "napi_create_uint32"; "napi_has_element"; "napi_create_external_arraybuffer"; "napi_create_symbol"; "node_api_symbol_for"; "napi_coerce_to_string"; "napi_create_type_error"; "napi_fatal_exception"; "napi_create_async_work"; "napi_async_init"; "node_api_create_property_key_utf16"; "napi_type_tag_object"; "napi_check_object_type_tag"; "node_api_post_finalizer"; "napi_add_async_cleanup_hook"; "napi_remove_async_cleanup_hook"; };
|
||||
{ "node_api_create_syntax_error"; "napi_make_callback"; "napi_has_named_property"; "napi_async_destroy"; "napi_coerce_to_object"; "napi_get_arraybuffer_info"; "napi_detach_arraybuffer"; "napi_get_undefined"; "napi_reference_unref"; "napi_fatal_error"; "napi_open_callback_scope"; "napi_close_callback_scope"; "napi_get_value_uint32"; "napi_create_function"; "napi_create_arraybuffer"; "napi_get_value_int64"; "napi_get_all_property_names"; "napi_resolve_deferred"; "napi_is_detached_arraybuffer"; "napi_create_string_utf8"; "napi_create_threadsafe_function"; "node_api_throw_syntax_error"; "napi_create_bigint_int64"; "napi_wrap"; "napi_set_property"; "napi_get_value_bigint_int64"; "napi_open_handle_scope"; "napi_create_error"; "napi_create_buffer"; "napi_cancel_async_work"; "napi_is_exception_pending"; "napi_acquire_threadsafe_function"; "napi_create_external"; "napi_get_threadsafe_function_context"; "napi_get_null"; "napi_create_string_utf16"; "node_api_create_external_string_utf16"; "napi_get_value_bigint_uint64"; "napi_module_register"; "napi_is_typedarray"; "napi_create_external_buffer"; "napi_get_new_target"; "napi_get_instance_data"; "napi_close_handle_scope"; "napi_get_value_string_utf16"; "napi_get_property_names"; "napi_is_arraybuffer"; "napi_get_cb_info"; "napi_define_properties"; "napi_add_env_cleanup_hook"; "node_api_get_module_file_name"; "napi_get_node_version"; "napi_create_int64"; "napi_create_double"; "napi_get_and_clear_last_exception"; "napi_create_reference"; "napi_get_typedarray_info"; "napi_call_threadsafe_function"; "napi_get_last_error_info"; "napi_create_array_with_length"; "napi_coerce_to_number"; "napi_get_global"; "napi_is_error"; "napi_set_instance_data"; "napi_create_typedarray"; "napi_throw_type_error"; "napi_has_property"; "napi_get_value_external"; "napi_create_range_error"; "napi_typeof"; "napi_ref_threadsafe_function"; "napi_create_bigint_uint64"; "napi_get_prototype"; "napi_adjust_external_memory"; "napi_release_threadsafe_function"; "napi_delete_async_work"; "napi_create_string_latin1"; "node_api_create_external_string_latin1"; "napi_is_array"; "napi_unref_threadsafe_function"; "napi_throw_error"; "napi_has_own_property"; "napi_get_reference_value"; "napi_remove_env_cleanup_hook"; "napi_get_value_string_utf8"; "napi_is_promise"; "napi_get_boolean"; "napi_run_script"; "napi_get_element"; "napi_get_named_property"; "napi_get_buffer_info"; "napi_get_value_bool"; "napi_reference_ref"; "napi_create_object"; "napi_create_promise"; "napi_create_int32"; "napi_escape_handle"; "napi_open_escapable_handle_scope"; "napi_throw"; "napi_get_value_double"; "napi_set_named_property"; "napi_call_function"; "napi_create_date"; "napi_object_freeze"; "napi_get_uv_event_loop"; "napi_get_value_string_latin1"; "napi_reject_deferred"; "napi_add_finalizer"; "napi_create_array"; "napi_delete_reference"; "napi_get_date_value"; "napi_create_dataview"; "napi_get_version"; "napi_define_class"; "napi_is_date"; "napi_remove_wrap"; "napi_delete_property"; "napi_instanceof"; "napi_create_buffer_copy"; "napi_delete_element"; "napi_object_seal"; "napi_queue_async_work"; "napi_get_value_bigint_words"; "napi_is_buffer"; "napi_get_array_length"; "napi_get_property"; "napi_new_instance"; "napi_set_element"; "napi_create_bigint_words"; "napi_strict_equals"; "napi_is_dataview"; "napi_close_escapable_handle_scope"; "napi_get_dataview_info"; "napi_get_value_int32"; "napi_unwrap"; "napi_throw_range_error"; "napi_coerce_to_bool"; "napi_create_uint32"; "napi_has_element"; "napi_create_external_arraybuffer"; "napi_create_symbol"; "node_api_symbol_for"; "napi_coerce_to_string"; "napi_create_type_error"; "napi_fatal_exception"; "napi_create_async_work"; "napi_async_init"; "node_api_create_property_key_utf16"; "napi_type_tag_object"; "napi_check_object_type_tag"; "node_api_post_finalizer"; "napi_add_async_cleanup_hook"; "napi_remove_async_cleanup_hook"; "uv_mutex_init"; "uv_mutex_lock"; "uv_mutex_unlock"; "uv_mutex_destroy"; "uv_async_init"; "uv_async_send"; "uv_close"; };
|
|
@ -150,4 +150,11 @@ _napi_type_tag_object
|
|||
_napi_check_object_type_tag
|
||||
_node_api_post_finalizer
|
||||
_napi_add_async_cleanup_hook
|
||||
_napi_remove_async_cleanup_hook
|
||||
_napi_remove_async_cleanup_hook
|
||||
_uv_mutex_init
|
||||
_uv_mutex_lock
|
||||
_uv_mutex_unlock
|
||||
_uv_mutex_destroy
|
||||
_uv_async_init
|
||||
_uv_async_send
|
||||
_uv_close
|
|
@ -152,4 +152,11 @@ EXPORTS
|
|||
napi_check_object_type_tag
|
||||
node_api_post_finalizer
|
||||
napi_add_async_cleanup_hook
|
||||
napi_remove_async_cleanup_hook
|
||||
napi_remove_async_cleanup_hook
|
||||
uv_mutex_init
|
||||
uv_mutex_lock
|
||||
uv_mutex_unlock
|
||||
uv_mutex_destroy
|
||||
uv_async_init
|
||||
uv_async_send
|
||||
uv_close
|
|
@ -264,6 +264,16 @@ fn napi_define_class<'s>(
|
|||
Err(status) => return status,
|
||||
};
|
||||
|
||||
let mut accessor_property = v8::PropertyAttribute::NONE;
|
||||
|
||||
if p.attributes & napi_enumerable == 0 {
|
||||
accessor_property = accessor_property | v8::PropertyAttribute::DONT_ENUM;
|
||||
}
|
||||
if p.attributes & napi_configurable == 0 {
|
||||
accessor_property =
|
||||
accessor_property | v8::PropertyAttribute::DONT_DELETE;
|
||||
}
|
||||
|
||||
if p.getter.is_some() || p.setter.is_some() {
|
||||
let getter = p.getter.map(|g| {
|
||||
create_function_template(&mut env.scope(), env_ptr, None, g, p.data)
|
||||
|
@ -271,8 +281,6 @@ fn napi_define_class<'s>(
|
|||
let setter = p.setter.map(|s| {
|
||||
create_function_template(&mut env.scope(), env_ptr, None, s, p.data)
|
||||
});
|
||||
|
||||
let mut accessor_property = v8::PropertyAttribute::NONE;
|
||||
if getter.is_some()
|
||||
&& setter.is_some()
|
||||
&& (p.attributes & napi_writable) == 0
|
||||
|
@ -280,15 +288,6 @@ fn napi_define_class<'s>(
|
|||
accessor_property =
|
||||
accessor_property | v8::PropertyAttribute::READ_ONLY;
|
||||
}
|
||||
if p.attributes & napi_enumerable == 0 {
|
||||
accessor_property =
|
||||
accessor_property | v8::PropertyAttribute::DONT_ENUM;
|
||||
}
|
||||
if p.attributes & napi_configurable == 0 {
|
||||
accessor_property =
|
||||
accessor_property | v8::PropertyAttribute::DONT_DELETE;
|
||||
}
|
||||
|
||||
let proto = tpl.prototype_template(&mut env.scope());
|
||||
proto.set_accessor_property(name, getter, setter, accessor_property);
|
||||
} else if let Some(method) = p.method {
|
||||
|
@ -300,10 +299,14 @@ fn napi_define_class<'s>(
|
|||
p.data,
|
||||
);
|
||||
let proto = tpl.prototype_template(&mut env.scope());
|
||||
proto.set(name, function.into());
|
||||
proto.set_with_attr(name, function.into(), accessor_property);
|
||||
} else {
|
||||
let proto = tpl.prototype_template(&mut env.scope());
|
||||
proto.set(name, p.value.unwrap().into());
|
||||
if (p.attributes & napi_writable) == 0 {
|
||||
accessor_property =
|
||||
accessor_property | v8::PropertyAttribute::READ_ONLY;
|
||||
}
|
||||
proto.set_with_attr(name, p.value.unwrap().into(), accessor_property);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1694,15 +1697,14 @@ fn napi_get_new_target(
|
|||
}
|
||||
|
||||
#[napi_sym]
|
||||
fn napi_call_function(
|
||||
env_ptr: *mut Env,
|
||||
recv: napi_value,
|
||||
func: napi_value,
|
||||
fn napi_call_function<'s>(
|
||||
env: &'s mut Env,
|
||||
recv: napi_value<'s>,
|
||||
func: napi_value<'s>,
|
||||
argc: usize,
|
||||
argv: *const napi_value,
|
||||
result: *mut napi_value,
|
||||
argv: *const napi_value<'s>,
|
||||
result: *mut napi_value<'s>,
|
||||
) -> napi_status {
|
||||
let env = check_env!(env_ptr);
|
||||
check_arg!(env, recv);
|
||||
let args = if argc > 0 {
|
||||
check_arg!(env, argv);
|
||||
|
@ -1716,11 +1718,11 @@ fn napi_call_function(
|
|||
let Some(func) =
|
||||
func.and_then(|f| v8::Local::<v8::Function>::try_from(f).ok())
|
||||
else {
|
||||
return napi_set_last_error(env, napi_function_expected);
|
||||
return napi_function_expected;
|
||||
};
|
||||
|
||||
let Some(v) = func.call(&mut env.scope(), recv.unwrap(), args) else {
|
||||
return napi_set_last_error(env_ptr, napi_generic_failure);
|
||||
return napi_generic_failure;
|
||||
};
|
||||
|
||||
if !result.is_null() {
|
||||
|
@ -1729,7 +1731,7 @@ fn napi_call_function(
|
|||
}
|
||||
}
|
||||
|
||||
return napi_clear_last_error(env_ptr);
|
||||
napi_ok
|
||||
}
|
||||
|
||||
#[napi_sym]
|
||||
|
|
|
@ -18,3 +18,4 @@
|
|||
pub mod js_native_api;
|
||||
pub mod node_api;
|
||||
pub mod util;
|
||||
pub mod uv;
|
||||
|
|
|
@ -547,11 +547,16 @@ fn napi_delete_async_work(env: *mut Env, work: napi_async_work) -> napi_status {
|
|||
}
|
||||
|
||||
#[napi_sym]
|
||||
fn napi_get_uv_event_loop(env: *mut Env, uv_loop: *mut *mut ()) -> napi_status {
|
||||
let env = check_env!(env);
|
||||
fn napi_get_uv_event_loop(
|
||||
env_ptr: *mut Env,
|
||||
uv_loop: *mut *mut (),
|
||||
) -> napi_status {
|
||||
let env = check_env!(env_ptr);
|
||||
check_arg!(env, uv_loop);
|
||||
// There is no uv_loop in Deno
|
||||
napi_set_last_error(env, napi_generic_failure)
|
||||
unsafe {
|
||||
*uv_loop = env_ptr.cast();
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[napi_sym]
|
||||
|
@ -687,7 +692,7 @@ impl Drop for TsFn {
|
|||
|
||||
if let Some(finalizer) = self.thread_finalize_cb {
|
||||
unsafe {
|
||||
(finalizer)(self.env as _, self.thread_finalize_data, ptr::null_mut());
|
||||
(finalizer)(self.env as _, self.thread_finalize_data, self.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[package]
|
||||
name = "napi_sym"
|
||||
version = "0.98.0"
|
||||
version = "0.103.0"
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
|
|
@ -152,6 +152,13 @@
|
|||
"napi_check_object_type_tag",
|
||||
"node_api_post_finalizer",
|
||||
"napi_add_async_cleanup_hook",
|
||||
"napi_remove_async_cleanup_hook"
|
||||
"napi_remove_async_cleanup_hook",
|
||||
"uv_mutex_init",
|
||||
"uv_mutex_lock",
|
||||
"uv_mutex_unlock",
|
||||
"uv_mutex_destroy",
|
||||
"uv_async_init",
|
||||
"uv_async_send",
|
||||
"uv_close"
|
||||
]
|
||||
}
|
||||
|
|
231
cli/napi/uv.rs
Normal file
231
cli/napi/uv.rs
Normal file
|
@ -0,0 +1,231 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_runtime::deno_napi::*;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr::addr_of_mut;
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
fn assert_ok(res: c_int) -> c_int {
|
||||
if res != 0 {
|
||||
eprintln!("bad result in uv polyfill: {res}");
|
||||
// don't panic because that might unwind into
|
||||
// c/c++
|
||||
std::process::abort();
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
use crate::napi::js_native_api::napi_create_string_utf8;
|
||||
use crate::napi::node_api::napi_create_async_work;
|
||||
use crate::napi::node_api::napi_delete_async_work;
|
||||
use crate::napi::node_api::napi_queue_async_work;
|
||||
use std::ffi::c_int;
|
||||
|
||||
const UV_MUTEX_SIZE: usize = {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
std::mem::size_of::<libc::pthread_mutex_t>()
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
std::mem::size_of::<windows_sys::Win32::System::Threading::CRITICAL_SECTION>(
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
struct uv_mutex_t {
|
||||
mutex: Mutex<()>,
|
||||
_padding: [MaybeUninit<usize>; const {
|
||||
(UV_MUTEX_SIZE - size_of::<Mutex<()>>()) / size_of::<usize>()
|
||||
}],
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn uv_mutex_init(lock: *mut uv_mutex_t) -> c_int {
|
||||
unsafe {
|
||||
addr_of_mut!((*lock).mutex).write(Mutex::new(()));
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn uv_mutex_lock(lock: *mut uv_mutex_t) {
|
||||
unsafe {
|
||||
let guard = (*lock).mutex.lock();
|
||||
// forget the guard so it doesn't unlock when it goes out of scope.
|
||||
// we're going to unlock it manually
|
||||
std::mem::forget(guard);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn uv_mutex_unlock(lock: *mut uv_mutex_t) {
|
||||
unsafe {
|
||||
(*lock).mutex.force_unlock();
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn uv_mutex_destroy(_lock: *mut uv_mutex_t) {
|
||||
// no cleanup required
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[allow(dead_code)]
|
||||
enum uv_handle_type {
|
||||
UV_UNKNOWN_HANDLE = 0,
|
||||
UV_ASYNC,
|
||||
UV_CHECK,
|
||||
UV_FS_EVENT,
|
||||
UV_FS_POLL,
|
||||
UV_HANDLE,
|
||||
UV_IDLE,
|
||||
UV_NAMED_PIPE,
|
||||
UV_POLL,
|
||||
UV_PREPARE,
|
||||
UV_PROCESS,
|
||||
UV_STREAM,
|
||||
UV_TCP,
|
||||
UV_TIMER,
|
||||
UV_TTY,
|
||||
UV_UDP,
|
||||
UV_SIGNAL,
|
||||
UV_FILE,
|
||||
UV_HANDLE_TYPE_MAX,
|
||||
}
|
||||
|
||||
const UV_HANDLE_SIZE: usize = 96;
|
||||
|
||||
#[repr(C)]
|
||||
struct uv_handle_t {
|
||||
// public members
|
||||
pub data: *mut c_void,
|
||||
pub r#loop: *mut uv_loop_t,
|
||||
pub r#type: uv_handle_type,
|
||||
|
||||
_padding: [MaybeUninit<usize>; const {
|
||||
(UV_HANDLE_SIZE
|
||||
- size_of::<*mut c_void>()
|
||||
- size_of::<*mut uv_loop_t>()
|
||||
- size_of::<uv_handle_type>())
|
||||
/ size_of::<usize>()
|
||||
}],
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
const UV_ASYNC_SIZE: usize = 128;
|
||||
|
||||
#[cfg(windows)]
|
||||
const UV_ASYNC_SIZE: usize = 224;
|
||||
|
||||
#[repr(C)]
|
||||
struct uv_async_t {
|
||||
// public members
|
||||
pub data: *mut c_void,
|
||||
pub r#loop: *mut uv_loop_t,
|
||||
pub r#type: uv_handle_type,
|
||||
// private
|
||||
async_cb: uv_async_cb,
|
||||
work: napi_async_work,
|
||||
_padding: [MaybeUninit<usize>; const {
|
||||
(UV_ASYNC_SIZE
|
||||
- size_of::<*mut c_void>()
|
||||
- size_of::<*mut uv_loop_t>()
|
||||
- size_of::<uv_handle_type>()
|
||||
- size_of::<uv_async_cb>()
|
||||
- size_of::<napi_async_work>())
|
||||
/ size_of::<usize>()
|
||||
}],
|
||||
}
|
||||
|
||||
type uv_loop_t = Env;
|
||||
type uv_async_cb = extern "C" fn(handle: *mut uv_async_t);
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn uv_async_init(
|
||||
r#loop: *mut uv_loop_t,
|
||||
// probably uninitialized
|
||||
r#async: *mut uv_async_t,
|
||||
async_cb: uv_async_cb,
|
||||
) -> c_int {
|
||||
unsafe {
|
||||
addr_of_mut!((*r#async).r#loop).write(r#loop);
|
||||
addr_of_mut!((*r#async).r#type).write(uv_handle_type::UV_ASYNC);
|
||||
addr_of_mut!((*r#async).async_cb).write(async_cb);
|
||||
|
||||
let mut resource_name: MaybeUninit<napi_value> = MaybeUninit::uninit();
|
||||
assert_ok(napi_create_string_utf8(
|
||||
r#loop,
|
||||
c"uv_async".as_ptr(),
|
||||
usize::MAX,
|
||||
resource_name.as_mut_ptr(),
|
||||
));
|
||||
let resource_name = resource_name.assume_init();
|
||||
|
||||
let res = napi_create_async_work(
|
||||
r#loop,
|
||||
None::<v8::Local<'static, v8::Value>>.into(),
|
||||
resource_name,
|
||||
Some(async_exec_wrap),
|
||||
None,
|
||||
r#async.cast(),
|
||||
addr_of_mut!((*r#async).work),
|
||||
);
|
||||
-res
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn uv_async_send(handle: *mut uv_async_t) -> c_int {
|
||||
unsafe { -napi_queue_async_work((*handle).r#loop, (*handle).work) }
|
||||
}
|
||||
|
||||
type uv_close_cb = unsafe extern "C" fn(*mut uv_handle_t);
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn uv_close(handle: *mut uv_handle_t, close: uv_close_cb) {
|
||||
unsafe {
|
||||
if handle.is_null() {
|
||||
close(handle);
|
||||
return;
|
||||
}
|
||||
if let uv_handle_type::UV_ASYNC = (*handle).r#type {
|
||||
let handle: *mut uv_async_t = handle.cast();
|
||||
napi_delete_async_work((*handle).r#loop, (*handle).work);
|
||||
}
|
||||
close(handle);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn async_exec_wrap(_env: napi_env, data: *mut c_void) {
|
||||
let data: *mut uv_async_t = data.cast();
|
||||
unsafe {
|
||||
((*data).async_cb)(data);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sizes() {
|
||||
assert_eq!(
|
||||
std::mem::size_of::<libuv_sys_lite::uv_mutex_t>(),
|
||||
UV_MUTEX_SIZE
|
||||
);
|
||||
assert_eq!(
|
||||
std::mem::size_of::<libuv_sys_lite::uv_handle_t>(),
|
||||
UV_HANDLE_SIZE
|
||||
);
|
||||
assert_eq!(
|
||||
std::mem::size_of::<libuv_sys_lite::uv_async_t>(),
|
||||
UV_ASYNC_SIZE
|
||||
);
|
||||
assert_eq!(std::mem::size_of::<uv_mutex_t>(), UV_MUTEX_SIZE);
|
||||
assert_eq!(std::mem::size_of::<uv_handle_t>(), UV_HANDLE_SIZE);
|
||||
assert_eq!(std::mem::size_of::<uv_async_t>(), UV_ASYNC_SIZE);
|
||||
}
|
||||
}
|
89
cli/node.rs
89
cli/node.rs
|
@ -5,6 +5,7 @@ use std::sync::Arc;
|
|||
use deno_ast::MediaType;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_graph::ParsedSourceStore;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
|
||||
use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis;
|
||||
|
@ -16,6 +17,8 @@ use serde::Serialize;
|
|||
|
||||
use crate::cache::CacheDBHash;
|
||||
use crate::cache::NodeAnalysisCache;
|
||||
use crate::cache::ParsedSourceCache;
|
||||
use crate::resolver::CliNodeResolver;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists;
|
||||
|
||||
pub type CliNodeCodeTranslator =
|
||||
|
@ -54,11 +57,23 @@ pub enum CliCjsAnalysis {
|
|||
pub struct CliCjsCodeAnalyzer {
|
||||
cache: NodeAnalysisCache,
|
||||
fs: deno_fs::FileSystemRc,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
parsed_source_cache: Option<Arc<ParsedSourceCache>>,
|
||||
}
|
||||
|
||||
impl CliCjsCodeAnalyzer {
|
||||
pub fn new(cache: NodeAnalysisCache, fs: deno_fs::FileSystemRc) -> Self {
|
||||
Self { cache, fs }
|
||||
pub fn new(
|
||||
cache: NodeAnalysisCache,
|
||||
fs: deno_fs::FileSystemRc,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
parsed_source_cache: Option<Arc<ParsedSourceCache>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
cache,
|
||||
fs,
|
||||
node_resolver,
|
||||
parsed_source_cache,
|
||||
}
|
||||
}
|
||||
|
||||
async fn inner_cjs_analysis(
|
||||
|
@ -73,7 +88,7 @@ impl CliCjsCodeAnalyzer {
|
|||
return Ok(analysis);
|
||||
}
|
||||
|
||||
let media_type = MediaType::from_specifier(specifier);
|
||||
let mut media_type = MediaType::from_specifier(specifier);
|
||||
if media_type == MediaType::Json {
|
||||
return Ok(CliCjsAnalysis::Cjs {
|
||||
exports: vec![],
|
||||
|
@ -81,24 +96,55 @@ impl CliCjsCodeAnalyzer {
|
|||
});
|
||||
}
|
||||
|
||||
if media_type == MediaType::JavaScript {
|
||||
if let Some(package_json) =
|
||||
self.node_resolver.get_closest_package_json(specifier)?
|
||||
{
|
||||
match package_json.typ.as_str() {
|
||||
"commonjs" => {
|
||||
media_type = MediaType::Cjs;
|
||||
}
|
||||
"module" => {
|
||||
media_type = MediaType::Mjs;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let maybe_parsed_source = self
|
||||
.parsed_source_cache
|
||||
.as_ref()
|
||||
.and_then(|c| c.remove_parsed_source(specifier));
|
||||
|
||||
let analysis = deno_core::unsync::spawn_blocking({
|
||||
let specifier = specifier.clone();
|
||||
let source: Arc<str> = source.into();
|
||||
move || -> Result<_, deno_ast::ParseDiagnostic> {
|
||||
let parsed_source = deno_ast::parse_program(deno_ast::ParseParams {
|
||||
specifier,
|
||||
text: source,
|
||||
media_type,
|
||||
capture_tokens: true,
|
||||
scope_analysis: false,
|
||||
maybe_syntax: None,
|
||||
})?;
|
||||
let parsed_source =
|
||||
maybe_parsed_source.map(Ok).unwrap_or_else(|| {
|
||||
deno_ast::parse_program(deno_ast::ParseParams {
|
||||
specifier,
|
||||
text: source,
|
||||
media_type,
|
||||
capture_tokens: true,
|
||||
scope_analysis: false,
|
||||
maybe_syntax: None,
|
||||
})
|
||||
})?;
|
||||
if parsed_source.is_script() {
|
||||
let analysis = parsed_source.analyze_cjs();
|
||||
Ok(CliCjsAnalysis::Cjs {
|
||||
exports: analysis.exports,
|
||||
reexports: analysis.reexports,
|
||||
})
|
||||
} else if media_type == MediaType::Cjs {
|
||||
// FIXME: `deno_ast` should internally handle MediaType::Cjs implying that
|
||||
// the result must never be Esm
|
||||
Ok(CliCjsAnalysis::Cjs {
|
||||
exports: vec![],
|
||||
reexports: vec![],
|
||||
})
|
||||
} else {
|
||||
Ok(CliCjsAnalysis::Esm)
|
||||
}
|
||||
|
@ -125,10 +171,23 @@ impl CjsCodeAnalyzer for CliCjsCodeAnalyzer {
|
|||
let source = match source {
|
||||
Some(source) => source,
|
||||
None => {
|
||||
self
|
||||
.fs
|
||||
.read_text_file_lossy_async(specifier.to_file_path().unwrap(), None)
|
||||
.await?
|
||||
if let Ok(path) = specifier.to_file_path() {
|
||||
if let Ok(source_from_file) =
|
||||
self.fs.read_text_file_lossy_async(path, None).await
|
||||
{
|
||||
source_from_file
|
||||
} else {
|
||||
return Ok(ExtNodeCjsAnalysis::Cjs(CjsAnalysisExports {
|
||||
exports: vec![],
|
||||
reexports: vec![],
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
return Ok(ExtNodeCjsAnalysis::Cjs(CjsAnalysisExports {
|
||||
exports: vec![],
|
||||
reexports: vec![],
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
let analysis = self.inner_cjs_analysis(specifier, &source).await?;
|
||||
|
|
367
cli/npm/byonm.rs
367
cli/npm/byonm.rs
|
@ -5,391 +5,99 @@ use std::path::Path;
|
|||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use deno_package_json::PackageJsonDepValue;
|
||||
use deno_runtime::deno_fs::FileSystem;
|
||||
use deno_runtime::deno_node::DenoPkgJsonFsAdapter;
|
||||
use deno_core::url::Url;
|
||||
use deno_resolver::npm::ByonmNpmResolver;
|
||||
use deno_resolver::npm::ByonmNpmResolverCreateOptions;
|
||||
use deno_runtime::deno_node::NodePermissions;
|
||||
use deno_runtime::deno_node::NodeRequireResolver;
|
||||
use deno_runtime::deno_node::NpmProcessStateProvider;
|
||||
use deno_runtime::deno_node::PackageJson;
|
||||
use deno_runtime::ops::process::NpmProcessStateProvider;
|
||||
use deno_semver::package::PackageReq;
|
||||
use deno_semver::Version;
|
||||
use node_resolver::errors::PackageFolderResolveError;
|
||||
use node_resolver::errors::PackageFolderResolveIoError;
|
||||
use node_resolver::errors::PackageJsonLoadError;
|
||||
use node_resolver::errors::PackageNotFoundError;
|
||||
use node_resolver::load_pkg_json;
|
||||
use node_resolver::NpmResolver;
|
||||
|
||||
use crate::args::NpmProcessState;
|
||||
use crate::args::NpmProcessStateKind;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
|
||||
use deno_runtime::fs_util::specifier_to_file_path;
|
||||
use crate::resolver::CliDenoResolverFs;
|
||||
|
||||
use super::managed::normalize_pkg_name_for_node_modules_deno_folder;
|
||||
use super::CliNpmResolver;
|
||||
use super::InnerCliNpmResolverRef;
|
||||
use super::ResolvePkgFolderFromDenoReqError;
|
||||
|
||||
pub struct CliNpmResolverByonmCreateOptions {
|
||||
pub fs: Arc<dyn FileSystem>,
|
||||
// todo(dsherret): investigate removing this
|
||||
pub root_node_modules_dir: Option<PathBuf>,
|
||||
}
|
||||
|
||||
pub fn create_byonm_npm_resolver(
|
||||
options: CliNpmResolverByonmCreateOptions,
|
||||
) -> Arc<dyn CliNpmResolver> {
|
||||
Arc::new(ByonmCliNpmResolver {
|
||||
fs: options.fs,
|
||||
root_node_modules_dir: options.root_node_modules_dir,
|
||||
})
|
||||
}
|
||||
pub type CliByonmNpmResolverCreateOptions =
|
||||
ByonmNpmResolverCreateOptions<CliDenoResolverFs>;
|
||||
pub type CliByonmNpmResolver = ByonmNpmResolver<CliDenoResolverFs>;
|
||||
|
||||
// todo(dsherret): the services hanging off `CliNpmResolver` doesn't seem ideal. We should probably decouple.
|
||||
#[derive(Debug)]
|
||||
pub struct ByonmCliNpmResolver {
|
||||
fs: Arc<dyn FileSystem>,
|
||||
root_node_modules_dir: Option<PathBuf>,
|
||||
}
|
||||
struct CliByonmWrapper(Arc<CliByonmNpmResolver>);
|
||||
|
||||
impl ByonmCliNpmResolver {
|
||||
fn load_pkg_json(
|
||||
&self,
|
||||
path: &Path,
|
||||
) -> Result<Option<Arc<PackageJson>>, PackageJsonLoadError> {
|
||||
load_pkg_json(&DenoPkgJsonFsAdapter(self.fs.as_ref()), path)
|
||||
}
|
||||
|
||||
/// Finds the ancestor package.json that contains the specified dependency.
|
||||
pub fn find_ancestor_package_json_with_dep(
|
||||
&self,
|
||||
dep_name: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> Option<Arc<PackageJson>> {
|
||||
let referrer_path = referrer.to_file_path().ok()?;
|
||||
let mut current_folder = referrer_path.parent()?;
|
||||
loop {
|
||||
let pkg_json_path = current_folder.join("package.json");
|
||||
if let Ok(Some(pkg_json)) = self.load_pkg_json(&pkg_json_path) {
|
||||
if let Some(deps) = &pkg_json.dependencies {
|
||||
if deps.contains_key(dep_name) {
|
||||
return Some(pkg_json);
|
||||
}
|
||||
}
|
||||
if let Some(deps) = &pkg_json.dev_dependencies {
|
||||
if deps.contains_key(dep_name) {
|
||||
return Some(pkg_json);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(parent) = current_folder.parent() {
|
||||
current_folder = parent;
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_pkg_json_and_alias_for_req(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> Result<Option<(Arc<PackageJson>, String)>, AnyError> {
|
||||
fn resolve_alias_from_pkg_json(
|
||||
req: &PackageReq,
|
||||
pkg_json: &PackageJson,
|
||||
) -> Option<String> {
|
||||
let deps = pkg_json.resolve_local_package_json_deps();
|
||||
for (key, value) in deps {
|
||||
if let Ok(value) = value {
|
||||
match value {
|
||||
PackageJsonDepValue::Req(dep_req) => {
|
||||
if dep_req.name == req.name
|
||||
&& dep_req.version_req.intersects(&req.version_req)
|
||||
{
|
||||
return Some(key);
|
||||
}
|
||||
}
|
||||
PackageJsonDepValue::Workspace(_workspace) => {
|
||||
if key == req.name && req.version_req.tag() == Some("workspace") {
|
||||
return Some(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// attempt to resolve the npm specifier from the referrer's package.json,
|
||||
if let Ok(file_path) = specifier_to_file_path(referrer) {
|
||||
let mut current_path = file_path.as_path();
|
||||
while let Some(dir_path) = current_path.parent() {
|
||||
let package_json_path = dir_path.join("package.json");
|
||||
if let Some(pkg_json) = self.load_pkg_json(&package_json_path)? {
|
||||
if let Some(alias) =
|
||||
resolve_alias_from_pkg_json(req, pkg_json.as_ref())
|
||||
{
|
||||
return Ok(Some((pkg_json, alias)));
|
||||
}
|
||||
}
|
||||
current_path = dir_path;
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, fall fallback to the project's package.json
|
||||
if let Some(root_node_modules_dir) = &self.root_node_modules_dir {
|
||||
let root_pkg_json_path =
|
||||
root_node_modules_dir.parent().unwrap().join("package.json");
|
||||
if let Some(pkg_json) = self.load_pkg_json(&root_pkg_json_path)? {
|
||||
if let Some(alias) = resolve_alias_from_pkg_json(req, pkg_json.as_ref())
|
||||
{
|
||||
return Ok(Some((pkg_json, alias)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn resolve_folder_in_root_node_modules(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
) -> Option<PathBuf> {
|
||||
// now check if node_modules/.deno/ matches this constraint
|
||||
let root_node_modules_dir = self.root_node_modules_dir.as_ref()?;
|
||||
let node_modules_deno_dir = root_node_modules_dir.join(".deno");
|
||||
let Ok(entries) = self.fs.read_dir_sync(&node_modules_deno_dir) else {
|
||||
return None;
|
||||
};
|
||||
let search_prefix = format!(
|
||||
"{}@",
|
||||
normalize_pkg_name_for_node_modules_deno_folder(&req.name)
|
||||
);
|
||||
let mut best_version = None;
|
||||
|
||||
// example entries:
|
||||
// - @denotest+add@1.0.0
|
||||
// - @denotest+add@1.0.0_1
|
||||
for entry in entries {
|
||||
if !entry.is_directory {
|
||||
continue;
|
||||
}
|
||||
let Some(version_and_copy_idx) = entry.name.strip_prefix(&search_prefix)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let version = version_and_copy_idx
|
||||
.rsplit_once('_')
|
||||
.map(|(v, _)| v)
|
||||
.unwrap_or(version_and_copy_idx);
|
||||
let Ok(version) = Version::parse_from_npm(version) else {
|
||||
continue;
|
||||
};
|
||||
if req.version_req.matches(&version) {
|
||||
if let Some((best_version_version, _)) = &best_version {
|
||||
if version > *best_version_version {
|
||||
best_version = Some((version, entry.name));
|
||||
}
|
||||
} else {
|
||||
best_version = Some((version, entry.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
best_version.map(|(_version, entry_name)| {
|
||||
join_package_name(
|
||||
&node_modules_deno_dir.join(entry_name).join("node_modules"),
|
||||
&req.name,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl NpmResolver for ByonmCliNpmResolver {
|
||||
fn resolve_package_folder_from_package(
|
||||
&self,
|
||||
name: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> Result<PathBuf, PackageFolderResolveError> {
|
||||
fn inner(
|
||||
fs: &dyn FileSystem,
|
||||
name: &str,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> Result<PathBuf, PackageFolderResolveError> {
|
||||
let maybe_referrer_file = specifier_to_file_path(referrer).ok();
|
||||
let maybe_start_folder =
|
||||
maybe_referrer_file.as_ref().and_then(|f| f.parent());
|
||||
if let Some(start_folder) = maybe_start_folder {
|
||||
for current_folder in start_folder.ancestors() {
|
||||
let node_modules_folder = if current_folder.ends_with("node_modules")
|
||||
{
|
||||
Cow::Borrowed(current_folder)
|
||||
} else {
|
||||
Cow::Owned(current_folder.join("node_modules"))
|
||||
};
|
||||
|
||||
let sub_dir = join_package_name(&node_modules_folder, name);
|
||||
if fs.is_dir_sync(&sub_dir) {
|
||||
return Ok(sub_dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(
|
||||
PackageNotFoundError {
|
||||
package_name: name.to_string(),
|
||||
referrer: referrer.clone(),
|
||||
referrer_extra: None,
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
let path = inner(&*self.fs, name, referrer)?;
|
||||
self.fs.realpath_sync(&path).map_err(|err| {
|
||||
PackageFolderResolveIoError {
|
||||
package_name: name.to_string(),
|
||||
referrer: referrer.clone(),
|
||||
source: err.into_io_error(),
|
||||
}
|
||||
.into()
|
||||
})
|
||||
}
|
||||
|
||||
fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
specifier.scheme() == "file"
|
||||
&& specifier
|
||||
.path()
|
||||
.to_ascii_lowercase()
|
||||
.contains("/node_modules/")
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeRequireResolver for ByonmCliNpmResolver {
|
||||
fn ensure_read_permission(
|
||||
impl NodeRequireResolver for CliByonmWrapper {
|
||||
fn ensure_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
path: &Path,
|
||||
) -> Result<(), AnyError> {
|
||||
path: &'a Path,
|
||||
) -> Result<Cow<'a, Path>, AnyError> {
|
||||
if !path
|
||||
.components()
|
||||
.any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules")
|
||||
{
|
||||
permissions.check_read(path)?;
|
||||
permissions.check_read_path(path)
|
||||
} else {
|
||||
Ok(Cow::Borrowed(path))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl NpmProcessStateProvider for ByonmCliNpmResolver {
|
||||
impl NpmProcessStateProvider for CliByonmWrapper {
|
||||
fn get_npm_process_state(&self) -> String {
|
||||
serde_json::to_string(&NpmProcessState {
|
||||
kind: NpmProcessStateKind::Byonm,
|
||||
local_node_modules_path: self
|
||||
.root_node_modules_dir
|
||||
.as_ref()
|
||||
.0
|
||||
.root_node_modules_dir()
|
||||
.map(|p| p.to_string_lossy().to_string()),
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl CliNpmResolver for ByonmCliNpmResolver {
|
||||
impl CliNpmResolver for CliByonmNpmResolver {
|
||||
fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver> {
|
||||
self
|
||||
}
|
||||
|
||||
fn into_require_resolver(self: Arc<Self>) -> Arc<dyn NodeRequireResolver> {
|
||||
self
|
||||
Arc::new(CliByonmWrapper(self))
|
||||
}
|
||||
|
||||
fn into_process_state_provider(
|
||||
self: Arc<Self>,
|
||||
) -> Arc<dyn NpmProcessStateProvider> {
|
||||
self
|
||||
Arc::new(CliByonmWrapper(self))
|
||||
}
|
||||
|
||||
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver> {
|
||||
Arc::new(Self {
|
||||
fs: self.fs.clone(),
|
||||
root_node_modules_dir: self.root_node_modules_dir.clone(),
|
||||
})
|
||||
Arc::new(self.clone())
|
||||
}
|
||||
|
||||
fn as_inner(&self) -> InnerCliNpmResolverRef {
|
||||
InnerCliNpmResolverRef::Byonm(self)
|
||||
}
|
||||
|
||||
fn root_node_modules_path(&self) -> Option<&PathBuf> {
|
||||
self.root_node_modules_dir.as_ref()
|
||||
fn root_node_modules_path(&self) -> Option<&Path> {
|
||||
self.root_node_modules_dir()
|
||||
}
|
||||
|
||||
fn resolve_pkg_folder_from_deno_module_req(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> Result<PathBuf, AnyError> {
|
||||
fn node_resolve_dir(
|
||||
fs: &dyn FileSystem,
|
||||
alias: &str,
|
||||
start_dir: &Path,
|
||||
) -> Result<Option<PathBuf>, AnyError> {
|
||||
for ancestor in start_dir.ancestors() {
|
||||
let node_modules_folder = ancestor.join("node_modules");
|
||||
let sub_dir = join_package_name(&node_modules_folder, alias);
|
||||
if fs.is_dir_sync(&sub_dir) {
|
||||
return Ok(Some(canonicalize_path_maybe_not_exists_with_fs(
|
||||
&sub_dir, fs,
|
||||
)?));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
// now attempt to resolve if it's found in any package.json
|
||||
let maybe_pkg_json_and_alias =
|
||||
self.resolve_pkg_json_and_alias_for_req(req, referrer)?;
|
||||
match maybe_pkg_json_and_alias {
|
||||
Some((pkg_json, alias)) => {
|
||||
// now try node resolution
|
||||
if let Some(resolved) =
|
||||
node_resolve_dir(self.fs.as_ref(), &alias, pkg_json.dir_path())?
|
||||
{
|
||||
return Ok(resolved);
|
||||
}
|
||||
|
||||
bail!(
|
||||
concat!(
|
||||
"Could not find \"{}\" in a node_modules folder. ",
|
||||
"Deno expects the node_modules/ directory to be up to date. ",
|
||||
"Did you forget to run `deno install`?"
|
||||
),
|
||||
alias,
|
||||
);
|
||||
}
|
||||
None => {
|
||||
// now check if node_modules/.deno/ matches this constraint
|
||||
if let Some(folder) = self.resolve_folder_in_root_node_modules(req) {
|
||||
return Ok(folder);
|
||||
}
|
||||
|
||||
bail!(
|
||||
concat!(
|
||||
"Could not find a matching package for 'npm:{}' in the node_modules ",
|
||||
"directory. Ensure you have all your JSR and npm dependencies listed ",
|
||||
"in your deno.json or package.json, then run `deno install`. Alternatively, ",
|
||||
r#"turn on auto-install by specifying `"nodeModulesDir": "auto"` in your "#,
|
||||
"deno.json file."
|
||||
),
|
||||
req,
|
||||
);
|
||||
}
|
||||
}
|
||||
referrer: &Url,
|
||||
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
|
||||
ByonmNpmResolver::resolve_pkg_folder_from_deno_module_req(
|
||||
self, req, referrer,
|
||||
)
|
||||
.map_err(ResolvePkgFolderFromDenoReqError::Byonm)
|
||||
}
|
||||
|
||||
fn check_state_hash(&self) -> Option<u64> {
|
||||
|
@ -398,12 +106,3 @@ impl CliNpmResolver for ByonmCliNpmResolver {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn join_package_name(path: &Path, package_name: &str) -> PathBuf {
|
||||
let mut path = path.to_path_buf();
|
||||
// ensure backslashes are used on windows
|
||||
for part in package_name.split('/') {
|
||||
path = path.join(part);
|
||||
}
|
||||
path
|
||||
}
|
||||
|
|
|
@ -1,295 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::url::Url;
|
||||
use deno_npm::NpmPackageCacheFolderId;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::Version;
|
||||
|
||||
use crate::util::fs::canonicalize_path;
|
||||
use crate::util::path::root_url_to_safe_local_dirname;
|
||||
|
||||
/// The global cache directory of npm packages.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NpmCacheDir {
|
||||
root_dir: PathBuf,
|
||||
// cached url representation of the root directory
|
||||
root_dir_url: Url,
|
||||
// A list of all registry that were discovered via `.npmrc` files
|
||||
// turned into a safe directory names.
|
||||
known_registries_dirnames: Vec<String>,
|
||||
}
|
||||
|
||||
impl NpmCacheDir {
|
||||
pub fn new(root_dir: PathBuf, known_registries_urls: Vec<Url>) -> Self {
|
||||
fn try_get_canonicalized_root_dir(
|
||||
root_dir: &Path,
|
||||
) -> Result<PathBuf, AnyError> {
|
||||
if !root_dir.exists() {
|
||||
std::fs::create_dir_all(root_dir)
|
||||
.with_context(|| format!("Error creating {}", root_dir.display()))?;
|
||||
}
|
||||
Ok(canonicalize_path(root_dir)?)
|
||||
}
|
||||
|
||||
// this may fail on readonly file systems, so just ignore if so
|
||||
let root_dir =
|
||||
try_get_canonicalized_root_dir(&root_dir).unwrap_or(root_dir);
|
||||
let root_dir_url = Url::from_directory_path(&root_dir).unwrap();
|
||||
|
||||
let known_registries_dirnames: Vec<_> = known_registries_urls
|
||||
.into_iter()
|
||||
.map(|url| {
|
||||
root_url_to_safe_local_dirname(&url)
|
||||
.to_string_lossy()
|
||||
.replace('\\', "/")
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
root_dir,
|
||||
root_dir_url,
|
||||
known_registries_dirnames,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root_dir(&self) -> &Path {
|
||||
&self.root_dir
|
||||
}
|
||||
|
||||
pub fn root_dir_url(&self) -> &Url {
|
||||
&self.root_dir_url
|
||||
}
|
||||
|
||||
pub fn package_folder_for_id(
|
||||
&self,
|
||||
folder_id: &NpmPackageCacheFolderId,
|
||||
registry_url: &Url,
|
||||
) -> PathBuf {
|
||||
if folder_id.copy_index == 0 {
|
||||
self.package_folder_for_nv(&folder_id.nv, registry_url)
|
||||
} else {
|
||||
self
|
||||
.package_name_folder(&folder_id.nv.name, registry_url)
|
||||
.join(format!("{}_{}", folder_id.nv.version, folder_id.copy_index))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn package_folder_for_nv(
|
||||
&self,
|
||||
package: &PackageNv,
|
||||
registry_url: &Url,
|
||||
) -> PathBuf {
|
||||
self
|
||||
.package_name_folder(&package.name, registry_url)
|
||||
.join(package.version.to_string())
|
||||
}
|
||||
|
||||
pub fn package_name_folder(&self, name: &str, registry_url: &Url) -> PathBuf {
|
||||
let mut dir = self.registry_folder(registry_url);
|
||||
if name.to_lowercase() != name {
|
||||
let encoded_name = mixed_case_package_name_encode(name);
|
||||
// Using the encoded directory may have a collision with an actual package name
|
||||
// so prefix it with an underscore since npm packages can't start with that
|
||||
dir.join(format!("_{encoded_name}"))
|
||||
} else {
|
||||
// ensure backslashes are used on windows
|
||||
for part in name.split('/') {
|
||||
dir = dir.join(part);
|
||||
}
|
||||
dir
|
||||
}
|
||||
}
|
||||
|
||||
fn registry_folder(&self, registry_url: &Url) -> PathBuf {
|
||||
self
|
||||
.root_dir
|
||||
.join(root_url_to_safe_local_dirname(registry_url))
|
||||
}
|
||||
|
||||
pub fn resolve_package_folder_id_from_specifier(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<NpmPackageCacheFolderId> {
|
||||
let mut maybe_relative_url = None;
|
||||
|
||||
// Iterate through known registries and try to get a match.
|
||||
for registry_dirname in &self.known_registries_dirnames {
|
||||
let registry_root_dir = self
|
||||
.root_dir_url
|
||||
.join(&format!("{}/", registry_dirname))
|
||||
// this not succeeding indicates a fatal issue, so unwrap
|
||||
.unwrap();
|
||||
|
||||
let Some(relative_url) = registry_root_dir.make_relative(specifier)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if relative_url.starts_with("../") {
|
||||
continue;
|
||||
}
|
||||
|
||||
maybe_relative_url = Some(relative_url);
|
||||
break;
|
||||
}
|
||||
|
||||
let mut relative_url = maybe_relative_url?;
|
||||
|
||||
// base32 decode the url if it starts with an underscore
|
||||
// * Ex. _{base32(package_name)}/
|
||||
if let Some(end_url) = relative_url.strip_prefix('_') {
|
||||
let mut parts = end_url
|
||||
.split('/')
|
||||
.map(ToOwned::to_owned)
|
||||
.collect::<Vec<_>>();
|
||||
match mixed_case_package_name_decode(&parts[0]) {
|
||||
Some(part) => {
|
||||
parts[0] = part;
|
||||
}
|
||||
None => return None,
|
||||
}
|
||||
relative_url = parts.join("/");
|
||||
}
|
||||
|
||||
// examples:
|
||||
// * chalk/5.0.1/
|
||||
// * @types/chalk/5.0.1/
|
||||
// * some-package/5.0.1_1/ -- where the `_1` (/_\d+/) is a copy of the folder for peer deps
|
||||
let is_scoped_package = relative_url.starts_with('@');
|
||||
let mut parts = relative_url
|
||||
.split('/')
|
||||
.enumerate()
|
||||
.take(if is_scoped_package { 3 } else { 2 })
|
||||
.map(|(_, part)| part)
|
||||
.collect::<Vec<_>>();
|
||||
if parts.len() < 2 {
|
||||
return None;
|
||||
}
|
||||
let version_part = parts.pop().unwrap();
|
||||
let name = parts.join("/");
|
||||
let (version, copy_index) =
|
||||
if let Some((version, copy_count)) = version_part.split_once('_') {
|
||||
(version, copy_count.parse::<u8>().ok()?)
|
||||
} else {
|
||||
(version_part, 0)
|
||||
};
|
||||
Some(NpmPackageCacheFolderId {
|
||||
nv: PackageNv {
|
||||
name,
|
||||
version: Version::parse_from_npm(version).ok()?,
|
||||
},
|
||||
copy_index,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_cache_location(&self) -> PathBuf {
|
||||
self.root_dir.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mixed_case_package_name_encode(name: &str) -> String {
|
||||
// use base32 encoding because it's reversible and the character set
|
||||
// only includes the characters within 0-9 and A-Z so it can be lower cased
|
||||
base32::encode(
|
||||
base32::Alphabet::Rfc4648Lower { padding: false },
|
||||
name.as_bytes(),
|
||||
)
|
||||
.to_lowercase()
|
||||
}
|
||||
|
||||
pub fn mixed_case_package_name_decode(name: &str) -> Option<String> {
|
||||
base32::decode(base32::Alphabet::Rfc4648Lower { padding: false }, name)
|
||||
.and_then(|b| String::from_utf8(b).ok())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use deno_core::url::Url;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::Version;
|
||||
|
||||
use super::NpmCacheDir;
|
||||
use crate::npm::cache_dir::NpmPackageCacheFolderId;
|
||||
|
||||
#[test]
|
||||
fn should_get_package_folder() {
|
||||
let deno_dir = crate::cache::DenoDir::new(None).unwrap();
|
||||
let root_dir = deno_dir.npm_folder_path();
|
||||
let registry_url = Url::parse("https://registry.npmjs.org/").unwrap();
|
||||
let cache = NpmCacheDir::new(root_dir.clone(), vec![registry_url.clone()]);
|
||||
|
||||
assert_eq!(
|
||||
cache.package_folder_for_id(
|
||||
&NpmPackageCacheFolderId {
|
||||
nv: PackageNv {
|
||||
name: "json".to_string(),
|
||||
version: Version::parse_from_npm("1.2.5").unwrap(),
|
||||
},
|
||||
copy_index: 0,
|
||||
},
|
||||
®istry_url,
|
||||
),
|
||||
root_dir
|
||||
.join("registry.npmjs.org")
|
||||
.join("json")
|
||||
.join("1.2.5"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cache.package_folder_for_id(
|
||||
&NpmPackageCacheFolderId {
|
||||
nv: PackageNv {
|
||||
name: "json".to_string(),
|
||||
version: Version::parse_from_npm("1.2.5").unwrap(),
|
||||
},
|
||||
copy_index: 1,
|
||||
},
|
||||
®istry_url,
|
||||
),
|
||||
root_dir
|
||||
.join("registry.npmjs.org")
|
||||
.join("json")
|
||||
.join("1.2.5_1"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cache.package_folder_for_id(
|
||||
&NpmPackageCacheFolderId {
|
||||
nv: PackageNv {
|
||||
name: "JSON".to_string(),
|
||||
version: Version::parse_from_npm("2.1.5").unwrap(),
|
||||
},
|
||||
copy_index: 0,
|
||||
},
|
||||
®istry_url,
|
||||
),
|
||||
root_dir
|
||||
.join("registry.npmjs.org")
|
||||
.join("_jjju6tq")
|
||||
.join("2.1.5"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
cache.package_folder_for_id(
|
||||
&NpmPackageCacheFolderId {
|
||||
nv: PackageNv {
|
||||
name: "@types/JSON".to_string(),
|
||||
version: Version::parse_from_npm("2.1.5").unwrap(),
|
||||
},
|
||||
copy_index: 0,
|
||||
},
|
||||
®istry_url,
|
||||
),
|
||||
root_dir
|
||||
.join("registry.npmjs.org")
|
||||
.join("_ib2hs4dfomxuuu2pjy")
|
||||
.join("2.1.5"),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -40,7 +40,7 @@ pub fn maybe_auth_header_for_npm_registry(
|
|||
header::AUTHORIZATION,
|
||||
header::HeaderValue::from_str(&format!(
|
||||
"Basic {}",
|
||||
BASE64_STANDARD.encode(&format!(
|
||||
BASE64_STANDARD.encode(format!(
|
||||
"{}:{}",
|
||||
username.unwrap(),
|
||||
password.unwrap()
|
||||
|
|
44
cli/npm/managed/cache/mod.rs
vendored
44
cli/npm/managed/cache/mod.rs
vendored
|
@ -8,6 +8,7 @@ use std::path::PathBuf;
|
|||
use std::sync::Arc;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_cache_dir::npm::NpmCacheDir;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
|
@ -18,10 +19,10 @@ use deno_npm::npm_rc::ResolvedNpmRc;
|
|||
use deno_npm::registry::NpmPackageInfo;
|
||||
use deno_npm::NpmPackageCacheFolderId;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::Version;
|
||||
|
||||
use crate::args::CacheSetting;
|
||||
use crate::cache::CACHE_PERM;
|
||||
use crate::npm::NpmCacheDir;
|
||||
use crate::util::fs::atomic_write_file_with_retries;
|
||||
use crate::util::fs::hard_link_dir_recursive;
|
||||
|
||||
|
@ -87,9 +88,12 @@ impl NpmCache {
|
|||
) -> Result<(), AnyError> {
|
||||
let registry_url = self.npmrc.get_registry_url(&folder_id.nv.name);
|
||||
assert_ne!(folder_id.copy_index, 0);
|
||||
let package_folder = self
|
||||
.cache_dir
|
||||
.package_folder_for_id(folder_id, registry_url);
|
||||
let package_folder = self.cache_dir.package_folder_for_id(
|
||||
&folder_id.nv.name,
|
||||
&folder_id.nv.version.to_string(),
|
||||
folder_id.copy_index,
|
||||
registry_url,
|
||||
);
|
||||
|
||||
if package_folder.exists()
|
||||
// if this file exists, then the package didn't successfully initialize
|
||||
|
@ -100,9 +104,12 @@ impl NpmCache {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let original_package_folder = self
|
||||
.cache_dir
|
||||
.package_folder_for_nv(&folder_id.nv, registry_url);
|
||||
let original_package_folder = self.cache_dir.package_folder_for_id(
|
||||
&folder_id.nv.name,
|
||||
&folder_id.nv.version.to_string(),
|
||||
0, // original copy index
|
||||
registry_url,
|
||||
);
|
||||
|
||||
// it seems Windows does an "AccessDenied" error when moving a
|
||||
// directory with hard links, so that's why this solution is done
|
||||
|
@ -114,7 +121,12 @@ impl NpmCache {
|
|||
|
||||
pub fn package_folder_for_id(&self, id: &NpmPackageCacheFolderId) -> PathBuf {
|
||||
let registry_url = self.npmrc.get_registry_url(&id.nv.name);
|
||||
self.cache_dir.package_folder_for_id(id, registry_url)
|
||||
self.cache_dir.package_folder_for_id(
|
||||
&id.nv.name,
|
||||
&id.nv.version.to_string(),
|
||||
id.copy_index,
|
||||
registry_url,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn package_folder_for_nv(&self, package: &PackageNv) -> PathBuf {
|
||||
|
@ -127,7 +139,12 @@ impl NpmCache {
|
|||
package: &PackageNv,
|
||||
registry_url: &Url,
|
||||
) -> PathBuf {
|
||||
self.cache_dir.package_folder_for_nv(package, registry_url)
|
||||
self.cache_dir.package_folder_for_id(
|
||||
&package.name,
|
||||
&package.version.to_string(),
|
||||
0, // original copy_index
|
||||
registry_url,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn package_name_folder(&self, name: &str) -> PathBuf {
|
||||
|
@ -146,6 +163,15 @@ impl NpmCache {
|
|||
self
|
||||
.cache_dir
|
||||
.resolve_package_folder_id_from_specifier(specifier)
|
||||
.and_then(|cache_id| {
|
||||
Some(NpmPackageCacheFolderId {
|
||||
nv: PackageNv {
|
||||
name: cache_id.name,
|
||||
version: Version::parse_from_npm(&cache_id.version).ok()?,
|
||||
},
|
||||
copy_index: cache_id.copy_index,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn load_package_info(
|
||||
|
|
26
cli/npm/managed/cache/registry_info.rs
vendored
26
cli/npm/managed/cache/registry_info.rs
vendored
|
@ -202,10 +202,13 @@ impl RegistryInfoDownloader {
|
|||
let guard = self.progress_bar.update(package_url.as_str());
|
||||
let name = name.to_string();
|
||||
async move {
|
||||
let maybe_bytes = downloader
|
||||
.http_client_provider
|
||||
.get_or_create()?
|
||||
.download_with_progress(package_url, maybe_auth_header, &guard)
|
||||
let client = downloader.http_client_provider.get_or_create()?;
|
||||
let maybe_bytes = client
|
||||
.download_with_progress_and_retries(
|
||||
package_url,
|
||||
maybe_auth_header,
|
||||
&guard,
|
||||
)
|
||||
.await?;
|
||||
match maybe_bytes {
|
||||
Some(bytes) => {
|
||||
|
@ -239,6 +242,14 @@ impl RegistryInfoDownloader {
|
|||
|
||||
fn get_package_url(&self, name: &str) -> Url {
|
||||
let registry_url = self.npmrc.get_registry_url(name);
|
||||
// The '/' character in scoped package names "@scope/name" must be
|
||||
// encoded for older third party registries. Newer registries and
|
||||
// npm itself support both ways
|
||||
// - encoded: https://registry.npmjs.org/@rollup%2fplugin-json
|
||||
// - non-ecoded: https://registry.npmjs.org/@rollup/plugin-json
|
||||
// To support as many third party registries as possible we'll
|
||||
// always encode the '/' character.
|
||||
|
||||
// list of all characters used in npm packages:
|
||||
// !, ', (, ), *, -, ., /, [0-9], @, [A-Za-z], _, ~
|
||||
const ASCII_SET: percent_encoding::AsciiSet =
|
||||
|
@ -250,11 +261,14 @@ impl RegistryInfoDownloader {
|
|||
.remove(b'*')
|
||||
.remove(b'-')
|
||||
.remove(b'.')
|
||||
.remove(b'/')
|
||||
.remove(b'@')
|
||||
.remove(b'_')
|
||||
.remove(b'~');
|
||||
let name = percent_encoding::utf8_percent_encode(name, &ASCII_SET);
|
||||
registry_url.join(&name.to_string()).unwrap()
|
||||
registry_url
|
||||
// Ensure that scoped package name percent encoding is lower cased
|
||||
// to match npm.
|
||||
.join(&name.to_string().replace("%2F", "%2f"))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
|
2
cli/npm/managed/cache/tarball.rs
vendored
2
cli/npm/managed/cache/tarball.rs
vendored
|
@ -172,7 +172,7 @@ impl TarballCache {
|
|||
let guard = tarball_cache.progress_bar.update(&dist.tarball);
|
||||
let result = tarball_cache.http_client_provider
|
||||
.get_or_create()?
|
||||
.download_with_progress(tarball_uri, maybe_auth_header, &guard)
|
||||
.download_with_progress_and_retries(tarball_uri, maybe_auth_header, &guard)
|
||||
.await;
|
||||
let maybe_bytes = match result {
|
||||
Ok(maybe_bytes) => maybe_bytes,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
@ -7,6 +8,7 @@ use std::sync::Arc;
|
|||
use cache::RegistryInfoDownloader;
|
||||
use cache::TarballCache;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_cache_dir::npm::NpmCacheDir;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
|
@ -19,10 +21,11 @@ use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
|
|||
use deno_npm::NpmPackageId;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_runtime::colors;
|
||||
use deno_runtime::deno_fs::FileSystem;
|
||||
use deno_runtime::deno_node::NodePermissions;
|
||||
use deno_runtime::deno_node::NodeRequireResolver;
|
||||
use deno_runtime::deno_node::NpmProcessStateProvider;
|
||||
use deno_runtime::ops::process::NpmProcessStateProvider;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use node_resolver::errors::PackageFolderResolveError;
|
||||
|
@ -35,6 +38,7 @@ use crate::args::LifecycleScriptsConfig;
|
|||
use crate::args::NpmInstallDepsProvider;
|
||||
use crate::args::NpmProcessState;
|
||||
use crate::args::NpmProcessStateKind;
|
||||
use crate::cache::DenoCacheEnvFsAdapter;
|
||||
use crate::cache::FastInsecureHasher;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
|
||||
|
@ -45,12 +49,11 @@ use self::cache::NpmCache;
|
|||
use self::registry::CliNpmRegistryApi;
|
||||
use self::resolution::NpmResolution;
|
||||
use self::resolvers::create_npm_fs_resolver;
|
||||
pub use self::resolvers::normalize_pkg_name_for_node_modules_deno_folder;
|
||||
use self::resolvers::NpmPackageFsResolver;
|
||||
|
||||
use super::CliNpmResolver;
|
||||
use super::InnerCliNpmResolverRef;
|
||||
use super::NpmCacheDir;
|
||||
use super::ResolvePkgFolderFromDenoReqError;
|
||||
|
||||
mod cache;
|
||||
mod registry;
|
||||
|
@ -188,6 +191,7 @@ fn create_inner(
|
|||
fn create_cache(options: &CliNpmResolverManagedCreateOptions) -> Arc<NpmCache> {
|
||||
Arc::new(NpmCache::new(
|
||||
NpmCacheDir::new(
|
||||
&DenoCacheEnvFsAdapter(options.fs.as_ref()),
|
||||
options.npm_global_cache_dir.clone(),
|
||||
options.npmrc.get_all_known_registries_urls(),
|
||||
),
|
||||
|
@ -427,6 +431,16 @@ impl ManagedCliNpmResolver {
|
|||
self.resolution.snapshot()
|
||||
}
|
||||
|
||||
pub fn top_package_req_for_name(&self, name: &str) -> Option<PackageReq> {
|
||||
let package_reqs = self.resolution.package_reqs();
|
||||
let mut entries = package_reqs
|
||||
.iter()
|
||||
.filter(|(_, nv)| nv.name == name)
|
||||
.collect::<Vec<_>>();
|
||||
entries.sort_by_key(|(_, nv)| &nv.version);
|
||||
Some(entries.last()?.0.clone())
|
||||
}
|
||||
|
||||
pub fn serialized_valid_snapshot_for_system(
|
||||
&self,
|
||||
system_info: &NpmSystemInfo,
|
||||
|
@ -466,6 +480,25 @@ impl ManagedCliNpmResolver {
|
|||
self.resolution.resolve_pkg_id_from_pkg_req(req)
|
||||
}
|
||||
|
||||
pub fn ensure_no_pkg_json_dep_errors(&self) -> Result<(), AnyError> {
|
||||
for err in self.npm_install_deps_provider.pkg_json_dep_errors() {
|
||||
match err {
|
||||
deno_package_json::PackageJsonDepValueParseError::VersionReq(_) => {
|
||||
return Err(
|
||||
AnyError::from(err.clone())
|
||||
.context("Failed to install from package.json"),
|
||||
);
|
||||
}
|
||||
deno_package_json::PackageJsonDepValueParseError::Unsupported {
|
||||
..
|
||||
} => {
|
||||
log::warn!("{} {} in package.json", colors::yellow("Warning"), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensures that the top level `package.json` dependencies are installed.
|
||||
/// This may set up the `node_modules` directory.
|
||||
///
|
||||
|
@ -477,6 +510,7 @@ impl ManagedCliNpmResolver {
|
|||
if !self.top_level_install_flag.raise() {
|
||||
return Ok(false); // already did this
|
||||
}
|
||||
|
||||
let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs();
|
||||
if pkg_json_remote_pkgs.is_empty() {
|
||||
return Ok(false);
|
||||
|
@ -560,11 +594,11 @@ impl NpmResolver for ManagedCliNpmResolver {
|
|||
}
|
||||
|
||||
impl NodeRequireResolver for ManagedCliNpmResolver {
|
||||
fn ensure_read_permission(
|
||||
fn ensure_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
path: &Path,
|
||||
) -> Result<(), AnyError> {
|
||||
path: &'a Path,
|
||||
) -> Result<Cow<'a, Path>, AnyError> {
|
||||
self.fs_resolver.ensure_read_permission(permissions, path)
|
||||
}
|
||||
}
|
||||
|
@ -573,7 +607,7 @@ impl NpmProcessStateProvider for ManagedCliNpmResolver {
|
|||
fn get_npm_process_state(&self) -> String {
|
||||
npm_process_state(
|
||||
self.resolution.serialized_valid_snapshot(),
|
||||
self.fs_resolver.node_modules_path().map(|p| p.as_path()),
|
||||
self.fs_resolver.node_modules_path(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -630,7 +664,7 @@ impl CliNpmResolver for ManagedCliNpmResolver {
|
|||
InnerCliNpmResolverRef::Managed(self)
|
||||
}
|
||||
|
||||
fn root_node_modules_path(&self) -> Option<&PathBuf> {
|
||||
fn root_node_modules_path(&self) -> Option<&Path> {
|
||||
self.fs_resolver.node_modules_path()
|
||||
}
|
||||
|
||||
|
@ -638,9 +672,13 @@ impl CliNpmResolver for ManagedCliNpmResolver {
|
|||
&self,
|
||||
req: &PackageReq,
|
||||
_referrer: &ModuleSpecifier,
|
||||
) -> Result<PathBuf, AnyError> {
|
||||
let pkg_id = self.resolve_pkg_id_from_pkg_req(req)?;
|
||||
self.resolve_pkg_folder_from_pkg_id(&pkg_id)
|
||||
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
|
||||
let pkg_id = self
|
||||
.resolve_pkg_id_from_pkg_req(req)
|
||||
.map_err(|err| ResolvePkgFolderFromDenoReqError::Managed(err.into()))?;
|
||||
self
|
||||
.resolve_pkg_folder_from_pkg_id(&pkg_id)
|
||||
.map_err(ResolvePkgFolderFromDenoReqError::Managed)
|
||||
}
|
||||
|
||||
fn check_state_hash(&self) -> Option<u64> {
|
||||
|
|
|
@ -318,7 +318,7 @@ fn get_npm_pending_resolver(
|
|||
// WARNING: When bumping this version, check if anything needs to be
|
||||
// updated in the `setNodeOnlyGlobalNames` call in 99_main_compiler.js
|
||||
types_node_version_req: Some(
|
||||
VersionReq::parse_from_npm("18.0.0 - 18.16.19").unwrap(),
|
||||
VersionReq::parse_from_npm("22.0.0 - 22.5.4").unwrap(),
|
||||
),
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
pub mod bin_entries;
|
||||
pub mod lifecycle_scripts;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::io::ErrorKind;
|
||||
use std::path::Path;
|
||||
|
@ -30,7 +34,7 @@ pub trait NpmPackageFsResolver: Send + Sync {
|
|||
fn root_dir_url(&self) -> &Url;
|
||||
|
||||
/// The local node_modules folder if it is applicable to the implementation.
|
||||
fn node_modules_path(&self) -> Option<&PathBuf>;
|
||||
fn node_modules_path(&self) -> Option<&Path>;
|
||||
|
||||
fn maybe_package_folder(&self, package_id: &NpmPackageId) -> Option<PathBuf>;
|
||||
|
||||
|
@ -59,11 +63,12 @@ pub trait NpmPackageFsResolver: Send + Sync {
|
|||
|
||||
async fn cache_packages(&self) -> Result<(), AnyError>;
|
||||
|
||||
fn ensure_read_permission(
|
||||
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
|
||||
fn ensure_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
path: &Path,
|
||||
) -> Result<(), AnyError>;
|
||||
path: &'a Path,
|
||||
) -> Result<Cow<'a, Path>, AnyError>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -82,11 +87,15 @@ impl RegistryReadPermissionChecker {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn ensure_registry_read_permission(
|
||||
pub fn ensure_registry_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
path: &Path,
|
||||
) -> Result<(), AnyError> {
|
||||
path: &'a Path,
|
||||
) -> Result<Cow<'a, Path>, AnyError> {
|
||||
if permissions.query_read_all() {
|
||||
return Ok(Cow::Borrowed(path)); // skip permissions checks below
|
||||
}
|
||||
|
||||
// allow reading if it's in the node_modules
|
||||
let is_path_in_node_modules = path.starts_with(&self.registry_path)
|
||||
&& path
|
||||
|
@ -115,25 +124,26 @@ impl RegistryReadPermissionChecker {
|
|||
},
|
||||
}
|
||||
};
|
||||
let Some(registry_path_canon) = canonicalize(&self.registry_path)? else {
|
||||
return Ok(()); // not exists, allow reading
|
||||
};
|
||||
let Some(path_canon) = canonicalize(path)? else {
|
||||
return Ok(()); // not exists, allow reading
|
||||
};
|
||||
|
||||
if path_canon.starts_with(registry_path_canon) {
|
||||
return Ok(());
|
||||
if let Some(registry_path_canon) = canonicalize(&self.registry_path)? {
|
||||
if let Some(path_canon) = canonicalize(path)? {
|
||||
if path_canon.starts_with(registry_path_canon) {
|
||||
return Ok(Cow::Owned(path_canon));
|
||||
}
|
||||
} else if path.starts_with(registry_path_canon)
|
||||
|| path.starts_with(&self.registry_path)
|
||||
{
|
||||
return Ok(Cow::Borrowed(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
permissions.check_read(path)
|
||||
permissions.check_read_path(path)
|
||||
}
|
||||
}
|
||||
|
||||
/// Caches all the packages in parallel.
|
||||
pub async fn cache_packages(
|
||||
packages: Vec<NpmResolutionPackage>,
|
||||
packages: &[NpmResolutionPackage],
|
||||
tarball_cache: &Arc<TarballCache>,
|
||||
) -> Result<(), AnyError> {
|
||||
let mut futures_unordered = futures::stream::FuturesUnordered::new();
|
||||
|
|
|
@ -12,12 +12,12 @@ use std::path::Path;
|
|||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(super) struct BinEntries {
|
||||
pub struct BinEntries<'a> {
|
||||
/// Packages that have colliding bin names
|
||||
collisions: HashSet<NpmPackageId>,
|
||||
seen_names: HashMap<String, NpmPackageId>,
|
||||
collisions: HashSet<&'a NpmPackageId>,
|
||||
seen_names: HashMap<&'a str, &'a NpmPackageId>,
|
||||
/// The bin entries
|
||||
entries: Vec<(NpmResolutionPackage, PathBuf)>,
|
||||
entries: Vec<(&'a NpmResolutionPackage, PathBuf)>,
|
||||
}
|
||||
|
||||
/// Returns the name of the default binary for the given package.
|
||||
|
@ -31,37 +31,32 @@ fn default_bin_name(package: &NpmResolutionPackage) -> &str {
|
|||
.map_or(package.id.nv.name.as_str(), |(_, name)| name)
|
||||
}
|
||||
|
||||
impl BinEntries {
|
||||
pub(super) fn new() -> Self {
|
||||
impl<'a> BinEntries<'a> {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Add a new bin entry (package with a bin field)
|
||||
pub(super) fn add(
|
||||
pub fn add(
|
||||
&mut self,
|
||||
package: NpmResolutionPackage,
|
||||
package: &'a NpmResolutionPackage,
|
||||
package_path: PathBuf,
|
||||
) {
|
||||
// check for a new collision, if we haven't already
|
||||
// found one
|
||||
match package.bin.as_ref().unwrap() {
|
||||
deno_npm::registry::NpmPackageVersionBinEntry::String(_) => {
|
||||
let bin_name = default_bin_name(&package);
|
||||
let bin_name = default_bin_name(package);
|
||||
|
||||
if let Some(other) = self
|
||||
.seen_names
|
||||
.insert(bin_name.to_string(), package.id.clone())
|
||||
{
|
||||
self.collisions.insert(package.id.clone());
|
||||
if let Some(other) = self.seen_names.insert(bin_name, &package.id) {
|
||||
self.collisions.insert(&package.id);
|
||||
self.collisions.insert(other);
|
||||
}
|
||||
}
|
||||
deno_npm::registry::NpmPackageVersionBinEntry::Map(entries) => {
|
||||
for name in entries.keys() {
|
||||
if let Some(other) =
|
||||
self.seen_names.insert(name.to_string(), package.id.clone())
|
||||
{
|
||||
self.collisions.insert(package.id.clone());
|
||||
if let Some(other) = self.seen_names.insert(name, &package.id) {
|
||||
self.collisions.insert(&package.id);
|
||||
self.collisions.insert(other);
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +69,11 @@ impl BinEntries {
|
|||
fn for_each_entry(
|
||||
&mut self,
|
||||
snapshot: &NpmResolutionSnapshot,
|
||||
mut f: impl FnMut(
|
||||
mut already_seen: impl FnMut(
|
||||
&Path,
|
||||
&str, // bin script
|
||||
) -> Result<(), AnyError>,
|
||||
mut new: impl FnMut(
|
||||
&NpmResolutionPackage,
|
||||
&Path,
|
||||
&str, // bin name
|
||||
|
@ -95,18 +94,20 @@ impl BinEntries {
|
|||
deno_npm::registry::NpmPackageVersionBinEntry::String(script) => {
|
||||
let name = default_bin_name(package);
|
||||
if !seen.insert(name) {
|
||||
already_seen(package_path, script)?;
|
||||
// we already set up a bin entry with this name
|
||||
continue;
|
||||
}
|
||||
f(package, package_path, name, script)?;
|
||||
new(package, package_path, name, script)?;
|
||||
}
|
||||
deno_npm::registry::NpmPackageVersionBinEntry::Map(entries) => {
|
||||
for (name, script) in entries {
|
||||
if !seen.insert(name) {
|
||||
already_seen(package_path, script)?;
|
||||
// we already set up a bin entry with this name
|
||||
continue;
|
||||
}
|
||||
f(package, package_path, name, script)?;
|
||||
new(package, package_path, name, script)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -117,23 +118,27 @@ impl BinEntries {
|
|||
}
|
||||
|
||||
/// Collect the bin entries into a vec of (name, script path)
|
||||
pub(super) fn into_bin_files(
|
||||
pub fn into_bin_files(
|
||||
mut self,
|
||||
snapshot: &NpmResolutionSnapshot,
|
||||
) -> Vec<(String, PathBuf)> {
|
||||
let mut bins = Vec::new();
|
||||
self
|
||||
.for_each_entry(snapshot, |_, package_path, name, script| {
|
||||
bins.push((name.to_string(), package_path.join(script)));
|
||||
Ok(())
|
||||
})
|
||||
.for_each_entry(
|
||||
snapshot,
|
||||
|_, _| Ok(()),
|
||||
|_, package_path, name, script| {
|
||||
bins.push((name.to_string(), package_path.join(script)));
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
bins
|
||||
}
|
||||
|
||||
/// Finish setting up the bin entries, writing the necessary files
|
||||
/// to disk.
|
||||
pub(super) fn finish(
|
||||
pub fn finish(
|
||||
mut self,
|
||||
snapshot: &NpmResolutionSnapshot,
|
||||
bin_node_modules_dir_path: &Path,
|
||||
|
@ -144,15 +149,26 @@ impl BinEntries {
|
|||
)?;
|
||||
}
|
||||
|
||||
self.for_each_entry(snapshot, |package, package_path, name, script| {
|
||||
set_up_bin_entry(
|
||||
package,
|
||||
name,
|
||||
script,
|
||||
package_path,
|
||||
bin_node_modules_dir_path,
|
||||
)
|
||||
})?;
|
||||
self.for_each_entry(
|
||||
snapshot,
|
||||
|_package_path, _script| {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
let path = _package_path.join(_script);
|
||||
make_executable_if_exists(&path)?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
|package, package_path, name, script| {
|
||||
set_up_bin_entry(
|
||||
package,
|
||||
name,
|
||||
script,
|
||||
package_path,
|
||||
bin_node_modules_dir_path,
|
||||
)
|
||||
},
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -162,8 +178,8 @@ impl BinEntries {
|
|||
// that has a bin entry, then sort them by depth
|
||||
fn sort_by_depth(
|
||||
snapshot: &NpmResolutionSnapshot,
|
||||
bin_entries: &mut [(NpmResolutionPackage, PathBuf)],
|
||||
collisions: &mut HashSet<NpmPackageId>,
|
||||
bin_entries: &mut [(&NpmResolutionPackage, PathBuf)],
|
||||
collisions: &mut HashSet<&NpmPackageId>,
|
||||
) {
|
||||
enum Entry<'a> {
|
||||
Pkg(&'a NpmPackageId),
|
||||
|
@ -217,7 +233,7 @@ fn sort_by_depth(
|
|||
});
|
||||
}
|
||||
|
||||
pub(super) fn set_up_bin_entry(
|
||||
pub fn set_up_bin_entry(
|
||||
package: &NpmResolutionPackage,
|
||||
bin_name: &str,
|
||||
#[allow(unused_variables)] bin_script: &str,
|
||||
|
@ -259,6 +275,32 @@ fn set_up_bin_shim(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
/// Make the file at `path` executable if it exists.
|
||||
/// Returns `true` if the file exists, `false` otherwise.
|
||||
fn make_executable_if_exists(path: &Path) -> Result<bool, AnyError> {
|
||||
use std::io;
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let mut perms = match std::fs::metadata(path) {
|
||||
Ok(metadata) => metadata.permissions(),
|
||||
Err(err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
return Ok(false);
|
||||
}
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
if perms.mode() & 0o111 == 0 {
|
||||
// if the original file is not executable, make it executable
|
||||
perms.set_mode(perms.mode() | 0o111);
|
||||
std::fs::set_permissions(path, perms).with_context(|| {
|
||||
format!("Setting permissions on '{}'", path.display())
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn symlink_bin_entry(
|
||||
_package: &NpmResolutionPackage,
|
||||
|
@ -272,32 +314,20 @@ fn symlink_bin_entry(
|
|||
let link = bin_node_modules_dir_path.join(bin_name);
|
||||
let original = package_path.join(bin_script);
|
||||
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let mut perms = match std::fs::metadata(&original) {
|
||||
Ok(metadata) => metadata.permissions(),
|
||||
Err(err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
log::warn!(
|
||||
"{} Trying to set up '{}' bin for \"{}\", but the entry point \"{}\" doesn't exist.",
|
||||
deno_terminal::colors::yellow("Warning"),
|
||||
bin_name,
|
||||
package_path.display(),
|
||||
original.display()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
return Err(err).with_context(|| {
|
||||
format!("Can't set up '{}' bin at {}", bin_name, original.display())
|
||||
});
|
||||
}
|
||||
};
|
||||
if perms.mode() & 0o111 == 0 {
|
||||
// if the original file is not executable, make it executable
|
||||
perms.set_mode(perms.mode() | 0o111);
|
||||
std::fs::set_permissions(&original, perms).with_context(|| {
|
||||
format!("Setting permissions on '{}'", original.display())
|
||||
})?;
|
||||
let found = make_executable_if_exists(&original).with_context(|| {
|
||||
format!("Can't set up '{}' bin at {}", bin_name, original.display())
|
||||
})?;
|
||||
if !found {
|
||||
log::warn!(
|
||||
"{} Trying to set up '{}' bin for \"{}\", but the entry point \"{}\" doesn't exist.",
|
||||
deno_terminal::colors::yellow("Warning"),
|
||||
bin_name,
|
||||
package_path.display(),
|
||||
original.display()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let original_relative =
|
||||
crate::util::path::relative_path(bin_node_modules_dir_path, &original)
|
||||
.unwrap_or(original);
|
368
cli/npm/managed/resolvers/common/lifecycle_scripts.rs
Normal file
368
cli/npm/managed/resolvers/common/lifecycle_scripts.rs
Normal file
|
@ -0,0 +1,368 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use super::bin_entries::BinEntries;
|
||||
use crate::args::LifecycleScriptsConfig;
|
||||
use crate::task_runner::TaskStdio;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_npm::resolution::NpmResolutionSnapshot;
|
||||
use deno_runtime::deno_io::FromRawIoHandle;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::Version;
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
|
||||
pub trait LifecycleScriptsStrategy {
|
||||
fn can_run_scripts(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn package_path(&self, package: &NpmResolutionPackage) -> PathBuf;
|
||||
|
||||
fn warn_on_scripts_not_run(
|
||||
&self,
|
||||
packages: &[(&NpmResolutionPackage, PathBuf)],
|
||||
) -> Result<(), AnyError>;
|
||||
|
||||
fn has_warned(&self, package: &NpmResolutionPackage) -> bool;
|
||||
|
||||
fn has_run(&self, package: &NpmResolutionPackage) -> bool;
|
||||
|
||||
fn did_run_scripts(
|
||||
&self,
|
||||
package: &NpmResolutionPackage,
|
||||
) -> Result<(), AnyError>;
|
||||
}
|
||||
|
||||
pub struct LifecycleScripts<'a> {
|
||||
packages_with_scripts: Vec<(&'a NpmResolutionPackage, PathBuf)>,
|
||||
packages_with_scripts_not_run: Vec<(&'a NpmResolutionPackage, PathBuf)>,
|
||||
|
||||
config: &'a LifecycleScriptsConfig,
|
||||
strategy: Box<dyn LifecycleScriptsStrategy + 'a>,
|
||||
}
|
||||
|
||||
impl<'a> LifecycleScripts<'a> {
|
||||
pub fn new<T: LifecycleScriptsStrategy + 'a>(
|
||||
config: &'a LifecycleScriptsConfig,
|
||||
strategy: T,
|
||||
) -> Self {
|
||||
Self {
|
||||
config,
|
||||
packages_with_scripts: Vec::new(),
|
||||
packages_with_scripts_not_run: Vec::new(),
|
||||
strategy: Box::new(strategy),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn has_lifecycle_scripts(
|
||||
package: &NpmResolutionPackage,
|
||||
package_path: &Path,
|
||||
) -> bool {
|
||||
if let Some(install) = package.scripts.get("install") {
|
||||
// default script
|
||||
if !is_broken_default_install_script(install, package_path) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
package.scripts.contains_key("preinstall")
|
||||
|| package.scripts.contains_key("postinstall")
|
||||
}
|
||||
|
||||
// npm defaults to running `node-gyp rebuild` if there is a `binding.gyp` file
|
||||
// but it always fails if the package excludes the `binding.gyp` file when they publish.
|
||||
// (for example, `fsevents` hits this)
|
||||
fn is_broken_default_install_script(script: &str, package_path: &Path) -> bool {
|
||||
script == "node-gyp rebuild" && !package_path.join("binding.gyp").exists()
|
||||
}
|
||||
|
||||
impl<'a> LifecycleScripts<'a> {
|
||||
fn can_run_scripts(&self, package_nv: &PackageNv) -> bool {
|
||||
if !self.strategy.can_run_scripts() {
|
||||
return false;
|
||||
}
|
||||
use crate::args::PackagesAllowedScripts;
|
||||
match &self.config.allowed {
|
||||
PackagesAllowedScripts::All => true,
|
||||
// TODO: make this more correct
|
||||
PackagesAllowedScripts::Some(allow_list) => allow_list.iter().any(|s| {
|
||||
let s = s.strip_prefix("npm:").unwrap_or(s);
|
||||
s == package_nv.name || s == package_nv.to_string()
|
||||
}),
|
||||
PackagesAllowedScripts::None => false,
|
||||
}
|
||||
}
|
||||
/// Register a package for running lifecycle scripts, if applicable.
|
||||
///
|
||||
/// `package_path` is the path containing the package's code (its root dir).
|
||||
/// `package_meta_path` is the path to serve as the base directory for lifecycle
|
||||
/// script-related metadata (e.g. to store whether the scripts have been run already)
|
||||
pub fn add(
|
||||
&mut self,
|
||||
package: &'a NpmResolutionPackage,
|
||||
package_path: Cow<Path>,
|
||||
) {
|
||||
if has_lifecycle_scripts(package, &package_path) {
|
||||
if self.can_run_scripts(&package.id.nv) {
|
||||
if !self.strategy.has_run(package) {
|
||||
self
|
||||
.packages_with_scripts
|
||||
.push((package, package_path.into_owned()));
|
||||
}
|
||||
} else if !self.strategy.has_run(package)
|
||||
&& (self.config.explicit_install || !self.strategy.has_warned(package))
|
||||
{
|
||||
// Skip adding `esbuild` as it is known that it can work properly without lifecycle script
|
||||
// being run, and it's also very popular - any project using Vite would raise warnings.
|
||||
{
|
||||
let nv = &package.id.nv;
|
||||
if nv.name == "esbuild"
|
||||
&& nv.version >= Version::parse_standard("0.18.0").unwrap()
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self
|
||||
.packages_with_scripts_not_run
|
||||
.push((package, package_path.into_owned()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn warn_not_run_scripts(&self) -> Result<(), AnyError> {
|
||||
if !self.packages_with_scripts_not_run.is_empty() {
|
||||
self
|
||||
.strategy
|
||||
.warn_on_scripts_not_run(&self.packages_with_scripts_not_run)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn finish(
|
||||
self,
|
||||
snapshot: &NpmResolutionSnapshot,
|
||||
packages: &[NpmResolutionPackage],
|
||||
root_node_modules_dir_path: Option<&Path>,
|
||||
progress_bar: &ProgressBar,
|
||||
) -> Result<(), AnyError> {
|
||||
self.warn_not_run_scripts()?;
|
||||
let get_package_path =
|
||||
|p: &NpmResolutionPackage| self.strategy.package_path(p);
|
||||
let mut failed_packages = Vec::new();
|
||||
if !self.packages_with_scripts.is_empty() {
|
||||
// get custom commands for each bin available in the node_modules dir (essentially
|
||||
// the scripts that are in `node_modules/.bin`)
|
||||
let base =
|
||||
resolve_baseline_custom_commands(snapshot, packages, get_package_path)?;
|
||||
let init_cwd = &self.config.initial_cwd;
|
||||
let process_state = crate::npm::managed::npm_process_state(
|
||||
snapshot.as_valid_serialized(),
|
||||
root_node_modules_dir_path,
|
||||
);
|
||||
|
||||
let mut env_vars = crate::task_runner::real_env_vars();
|
||||
// we want to pass the current state of npm resolution down to the deno subprocess
|
||||
// (that may be running as part of the script). we do this with an inherited temp file
|
||||
//
|
||||
// SAFETY: we are sharing a single temp file across all of the scripts. the file position
|
||||
// will be shared among these, which is okay since we run only one script at a time.
|
||||
// However, if we concurrently run scripts in the future we will
|
||||
// have to have multiple temp files.
|
||||
let temp_file_fd =
|
||||
deno_runtime::ops::process::npm_process_state_tempfile(
|
||||
process_state.as_bytes(),
|
||||
).context("failed to create npm process state tempfile for running lifecycle scripts")?;
|
||||
// SAFETY: fd/handle is valid
|
||||
let _temp_file =
|
||||
unsafe { std::fs::File::from_raw_io_handle(temp_file_fd) }; // make sure the file gets closed
|
||||
env_vars.insert(
|
||||
deno_runtime::ops::process::NPM_RESOLUTION_STATE_FD_ENV_VAR_NAME
|
||||
.to_string(),
|
||||
(temp_file_fd as usize).to_string(),
|
||||
);
|
||||
for (package, package_path) in self.packages_with_scripts {
|
||||
// add custom commands for binaries from the package's dependencies. this will take precedence over the
|
||||
// baseline commands, so if the package relies on a bin that conflicts with one higher in the dependency tree, the
|
||||
// correct bin will be used.
|
||||
let custom_commands = resolve_custom_commands_from_deps(
|
||||
base.clone(),
|
||||
package,
|
||||
snapshot,
|
||||
get_package_path,
|
||||
)?;
|
||||
for script_name in ["preinstall", "install", "postinstall"] {
|
||||
if let Some(script) = package.scripts.get(script_name) {
|
||||
if script_name == "install"
|
||||
&& is_broken_default_install_script(script, &package_path)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let _guard = progress_bar.update_with_prompt(
|
||||
crate::util::progress_bar::ProgressMessagePrompt::Initialize,
|
||||
&format!("{}: running '{script_name}' script", package.id.nv),
|
||||
);
|
||||
let crate::task_runner::TaskResult {
|
||||
exit_code,
|
||||
stderr,
|
||||
stdout,
|
||||
} = crate::task_runner::run_task(
|
||||
crate::task_runner::RunTaskOptions {
|
||||
task_name: script_name,
|
||||
script,
|
||||
cwd: &package_path,
|
||||
env_vars: env_vars.clone(),
|
||||
custom_commands: custom_commands.clone(),
|
||||
init_cwd,
|
||||
argv: &[],
|
||||
root_node_modules_dir: root_node_modules_dir_path,
|
||||
stdio: Some(crate::task_runner::TaskIo {
|
||||
stderr: TaskStdio::piped(),
|
||||
stdout: TaskStdio::piped(),
|
||||
}),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let stdout = stdout.unwrap();
|
||||
let stderr = stderr.unwrap();
|
||||
if exit_code != 0 {
|
||||
log::warn!(
|
||||
"error: script '{}' in '{}' failed with exit code {}{}{}",
|
||||
script_name,
|
||||
package.id.nv,
|
||||
exit_code,
|
||||
if !stdout.trim_ascii().is_empty() {
|
||||
format!(
|
||||
"\nstdout:\n{}\n",
|
||||
String::from_utf8_lossy(&stdout).trim()
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
if !stderr.trim_ascii().is_empty() {
|
||||
format!(
|
||||
"\nstderr:\n{}\n",
|
||||
String::from_utf8_lossy(&stderr).trim()
|
||||
)
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
);
|
||||
failed_packages.push(&package.id.nv);
|
||||
// assume if earlier script fails, later ones will fail too
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.strategy.did_run_scripts(package)?;
|
||||
}
|
||||
}
|
||||
if failed_packages.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(AnyError::msg(format!(
|
||||
"failed to run scripts for packages: {}",
|
||||
failed_packages
|
||||
.iter()
|
||||
.map(|p| p.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ")
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// take in all (non copy) packages from snapshot,
|
||||
// and resolve the set of available binaries to create
|
||||
// custom commands available to the task runner
|
||||
fn resolve_baseline_custom_commands(
|
||||
snapshot: &NpmResolutionSnapshot,
|
||||
packages: &[NpmResolutionPackage],
|
||||
get_package_path: impl Fn(&NpmResolutionPackage) -> PathBuf,
|
||||
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
|
||||
let mut custom_commands = crate::task_runner::TaskCustomCommands::new();
|
||||
custom_commands
|
||||
.insert("npx".to_string(), Rc::new(crate::task_runner::NpxCommand));
|
||||
|
||||
custom_commands
|
||||
.insert("npm".to_string(), Rc::new(crate::task_runner::NpmCommand));
|
||||
|
||||
custom_commands
|
||||
.insert("node".to_string(), Rc::new(crate::task_runner::NodeCommand));
|
||||
|
||||
custom_commands.insert(
|
||||
"node-gyp".to_string(),
|
||||
Rc::new(crate::task_runner::NodeGypCommand),
|
||||
);
|
||||
|
||||
// TODO: this recreates the bin entries which could be redoing some work, but the ones
|
||||
// we compute earlier in `sync_resolution_with_fs` may not be exhaustive (because we skip
|
||||
// doing it for packages that are set up already.
|
||||
// realistically, scripts won't be run very often so it probably isn't too big of an issue.
|
||||
resolve_custom_commands_from_packages(
|
||||
custom_commands,
|
||||
snapshot,
|
||||
packages,
|
||||
get_package_path,
|
||||
)
|
||||
}
|
||||
|
||||
// resolves the custom commands from an iterator of packages
|
||||
// and adds them to the existing custom commands.
|
||||
// note that this will overwrite any existing custom commands
|
||||
fn resolve_custom_commands_from_packages<
|
||||
'a,
|
||||
P: IntoIterator<Item = &'a NpmResolutionPackage>,
|
||||
>(
|
||||
mut commands: crate::task_runner::TaskCustomCommands,
|
||||
snapshot: &'a NpmResolutionSnapshot,
|
||||
packages: P,
|
||||
get_package_path: impl Fn(&'a NpmResolutionPackage) -> PathBuf,
|
||||
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
|
||||
let mut bin_entries = BinEntries::new();
|
||||
for package in packages {
|
||||
let package_path = get_package_path(package);
|
||||
|
||||
if package.bin.is_some() {
|
||||
bin_entries.add(package, package_path);
|
||||
}
|
||||
}
|
||||
let bins = bin_entries.into_bin_files(snapshot);
|
||||
for (bin_name, script_path) in bins {
|
||||
commands.insert(
|
||||
bin_name.clone(),
|
||||
Rc::new(crate::task_runner::NodeModulesFileRunCommand {
|
||||
command_name: bin_name,
|
||||
path: script_path,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(commands)
|
||||
}
|
||||
|
||||
// resolves the custom commands from the dependencies of a package
|
||||
// and adds them to the existing custom commands.
|
||||
// note that this will overwrite any existing custom commands.
|
||||
fn resolve_custom_commands_from_deps(
|
||||
baseline: crate::task_runner::TaskCustomCommands,
|
||||
package: &NpmResolutionPackage,
|
||||
snapshot: &NpmResolutionSnapshot,
|
||||
get_package_path: impl Fn(&NpmResolutionPackage) -> PathBuf,
|
||||
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
|
||||
resolve_custom_commands_from_packages(
|
||||
baseline,
|
||||
snapshot,
|
||||
package
|
||||
.dependencies
|
||||
.values()
|
||||
.map(|id| snapshot.package_from_id(id).unwrap()),
|
||||
get_package_path,
|
||||
)
|
||||
}
|
|
@ -2,16 +2,19 @@
|
|||
|
||||
//! Code for global npm cache resolution.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::colors;
|
||||
use async_trait::async_trait;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::url::Url;
|
||||
use deno_npm::NpmPackageCacheFolderId;
|
||||
use deno_npm::NpmPackageId;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_runtime::deno_fs::FileSystem;
|
||||
use deno_runtime::deno_node::NodePermissions;
|
||||
|
@ -19,10 +22,14 @@ use node_resolver::errors::PackageFolderResolveError;
|
|||
use node_resolver::errors::PackageNotFoundError;
|
||||
use node_resolver::errors::ReferrerNotFoundError;
|
||||
|
||||
use crate::args::LifecycleScriptsConfig;
|
||||
use crate::cache::FastInsecureHasher;
|
||||
|
||||
use super::super::cache::NpmCache;
|
||||
use super::super::cache::TarballCache;
|
||||
use super::super::resolution::NpmResolution;
|
||||
use super::common::cache_packages;
|
||||
use super::common::lifecycle_scripts::LifecycleScriptsStrategy;
|
||||
use super::common::NpmPackageFsResolver;
|
||||
use super::common::RegistryReadPermissionChecker;
|
||||
|
||||
|
@ -34,6 +41,7 @@ pub struct GlobalNpmPackageResolver {
|
|||
resolution: Arc<NpmResolution>,
|
||||
system_info: NpmSystemInfo,
|
||||
registry_read_permission_checker: RegistryReadPermissionChecker,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
}
|
||||
|
||||
impl GlobalNpmPackageResolver {
|
||||
|
@ -43,6 +51,7 @@ impl GlobalNpmPackageResolver {
|
|||
tarball_cache: Arc<TarballCache>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
system_info: NpmSystemInfo,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
registry_read_permission_checker: RegistryReadPermissionChecker::new(
|
||||
|
@ -53,6 +62,7 @@ impl GlobalNpmPackageResolver {
|
|||
tarball_cache,
|
||||
resolution,
|
||||
system_info,
|
||||
lifecycle_scripts,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +73,7 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver {
|
|||
self.cache.root_dir_url()
|
||||
}
|
||||
|
||||
fn node_modules_path(&self) -> Option<&PathBuf> {
|
||||
fn node_modules_path(&self) -> Option<&Path> {
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -149,8 +159,7 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver {
|
|||
let package_partitions = self
|
||||
.resolution
|
||||
.all_system_packages_partitioned(&self.system_info);
|
||||
|
||||
cache_packages(package_partitions.packages, &self.tarball_cache).await?;
|
||||
cache_packages(&package_partitions.packages, &self.tarball_cache).await?;
|
||||
|
||||
// create the copy package folders
|
||||
for copy in package_partitions.copy_packages {
|
||||
|
@ -159,16 +168,103 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver {
|
|||
.ensure_copy_package(©.get_package_cache_folder_id())?;
|
||||
}
|
||||
|
||||
let mut lifecycle_scripts =
|
||||
super::common::lifecycle_scripts::LifecycleScripts::new(
|
||||
&self.lifecycle_scripts,
|
||||
GlobalLifecycleScripts::new(self, &self.lifecycle_scripts.root_dir),
|
||||
);
|
||||
for package in &package_partitions.packages {
|
||||
let package_folder = self.cache.package_folder_for_nv(&package.id.nv);
|
||||
lifecycle_scripts.add(package, Cow::Borrowed(&package_folder));
|
||||
}
|
||||
|
||||
lifecycle_scripts.warn_not_run_scripts()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_read_permission(
|
||||
fn ensure_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
path: &Path,
|
||||
) -> Result<(), AnyError> {
|
||||
path: &'a Path,
|
||||
) -> Result<Cow<'a, Path>, AnyError> {
|
||||
self
|
||||
.registry_read_permission_checker
|
||||
.ensure_registry_read_permission(permissions, path)
|
||||
}
|
||||
}
|
||||
|
||||
struct GlobalLifecycleScripts<'a> {
|
||||
resolver: &'a GlobalNpmPackageResolver,
|
||||
path_hash: u64,
|
||||
}
|
||||
|
||||
impl<'a> GlobalLifecycleScripts<'a> {
|
||||
fn new(resolver: &'a GlobalNpmPackageResolver, root_dir: &Path) -> Self {
|
||||
let mut hasher = FastInsecureHasher::new_without_deno_version();
|
||||
hasher.write(root_dir.to_string_lossy().as_bytes());
|
||||
let path_hash = hasher.finish();
|
||||
Self {
|
||||
resolver,
|
||||
path_hash,
|
||||
}
|
||||
}
|
||||
|
||||
fn warned_scripts_file(&self, package: &NpmResolutionPackage) -> PathBuf {
|
||||
self
|
||||
.package_path(package)
|
||||
.join(format!(".scripts-warned-{}", self.path_hash))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> super::common::lifecycle_scripts::LifecycleScriptsStrategy
|
||||
for GlobalLifecycleScripts<'a>
|
||||
{
|
||||
fn can_run_scripts(&self) -> bool {
|
||||
false
|
||||
}
|
||||
fn package_path(&self, package: &NpmResolutionPackage) -> PathBuf {
|
||||
self.resolver.cache.package_folder_for_nv(&package.id.nv)
|
||||
}
|
||||
|
||||
fn warn_on_scripts_not_run(
|
||||
&self,
|
||||
packages: &[(&NpmResolutionPackage, PathBuf)],
|
||||
) -> std::result::Result<(), deno_core::anyhow::Error> {
|
||||
log::warn!("{} The following packages contained npm lifecycle scripts ({}) that were not executed:", colors::yellow("Warning"), colors::gray("preinstall/install/postinstall"));
|
||||
for (package, _) in packages {
|
||||
log::warn!("┠─ {}", colors::gray(format!("npm:{}", package.id.nv)));
|
||||
}
|
||||
log::warn!("┃");
|
||||
log::warn!(
|
||||
"┠─ {}",
|
||||
colors::italic("This may cause the packages to not work correctly.")
|
||||
);
|
||||
log::warn!("┠─ {}", colors::italic("Lifecycle scripts are only supported when using a `node_modules` directory."));
|
||||
log::warn!(
|
||||
"┠─ {}",
|
||||
colors::italic("Enable it in your deno config file:")
|
||||
);
|
||||
log::warn!("┖─ {}", colors::bold("\"nodeModulesDir\": \"auto\""));
|
||||
|
||||
for (package, _) in packages {
|
||||
std::fs::write(self.warned_scripts_file(package), "")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn did_run_scripts(
|
||||
&self,
|
||||
_package: &NpmResolutionPackage,
|
||||
) -> std::result::Result<(), deno_core::anyhow::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_warned(&self, package: &NpmResolutionPackage) -> bool {
|
||||
self.warned_scripts_file(package).exists()
|
||||
}
|
||||
|
||||
fn has_run(&self, _package: &NpmResolutionPackage) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,13 @@
|
|||
|
||||
//! Code for local node_modules resolution.
|
||||
|
||||
mod bin_entries;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
@ -17,20 +16,22 @@ use std::rc::Rc;
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::args::LifecycleScriptsConfig;
|
||||
use crate::args::PackagesAllowedScripts;
|
||||
use crate::colors;
|
||||
use async_trait::async_trait;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::anyhow;
|
||||
use deno_cache_dir::npm::mixed_case_package_name_decode;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::stream::FuturesUnordered;
|
||||
use deno_core::futures::StreamExt;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::url::Url;
|
||||
use deno_npm::resolution::NpmResolutionSnapshot;
|
||||
use deno_npm::NpmPackageCacheFolderId;
|
||||
use deno_npm::NpmPackageId;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_resolver::npm::normalize_pkg_name_for_node_modules_deno_folder;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_node::NodePermissions;
|
||||
use deno_semver::package::PackageNv;
|
||||
|
@ -43,8 +44,6 @@ use serde::Serialize;
|
|||
|
||||
use crate::args::NpmInstallDepsProvider;
|
||||
use crate::cache::CACHE_PERM;
|
||||
use crate::npm::cache_dir::mixed_case_package_name_decode;
|
||||
use crate::npm::cache_dir::mixed_case_package_name_encode;
|
||||
use crate::util::fs::atomic_write_file_with_retries;
|
||||
use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
|
||||
use crate::util::fs::clone_dir_recursive;
|
||||
|
@ -160,8 +159,8 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver {
|
|||
&self.root_node_modules_url
|
||||
}
|
||||
|
||||
fn node_modules_path(&self) -> Option<&PathBuf> {
|
||||
Some(&self.root_node_modules_path)
|
||||
fn node_modules_path(&self) -> Option<&Path> {
|
||||
Some(self.root_node_modules_path.as_ref())
|
||||
}
|
||||
|
||||
fn maybe_package_folder(&self, id: &NpmPackageId) -> Option<PathBuf> {
|
||||
|
@ -258,88 +257,21 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver {
|
|||
.await
|
||||
}
|
||||
|
||||
fn ensure_read_permission(
|
||||
fn ensure_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
path: &Path,
|
||||
) -> Result<(), AnyError> {
|
||||
path: &'a Path,
|
||||
) -> Result<Cow<'a, Path>, AnyError> {
|
||||
self
|
||||
.registry_read_permission_checker
|
||||
.ensure_registry_read_permission(permissions, path)
|
||||
}
|
||||
}
|
||||
|
||||
// take in all (non copy) packages from snapshot,
|
||||
// and resolve the set of available binaries to create
|
||||
// custom commands available to the task runner
|
||||
fn resolve_baseline_custom_commands(
|
||||
snapshot: &NpmResolutionSnapshot,
|
||||
packages: &[NpmResolutionPackage],
|
||||
local_registry_dir: &Path,
|
||||
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
|
||||
let mut custom_commands = crate::task_runner::TaskCustomCommands::new();
|
||||
custom_commands
|
||||
.insert("npx".to_string(), Rc::new(crate::task_runner::NpxCommand));
|
||||
|
||||
custom_commands
|
||||
.insert("npm".to_string(), Rc::new(crate::task_runner::NpmCommand));
|
||||
|
||||
custom_commands
|
||||
.insert("node".to_string(), Rc::new(crate::task_runner::NodeCommand));
|
||||
|
||||
custom_commands.insert(
|
||||
"node-gyp".to_string(),
|
||||
Rc::new(crate::task_runner::NodeGypCommand),
|
||||
);
|
||||
|
||||
// TODO: this recreates the bin entries which could be redoing some work, but the ones
|
||||
// we compute earlier in `sync_resolution_with_fs` may not be exhaustive (because we skip
|
||||
// doing it for packages that are set up already.
|
||||
// realistically, scripts won't be run very often so it probably isn't too big of an issue.
|
||||
resolve_custom_commands_from_packages(
|
||||
custom_commands,
|
||||
snapshot,
|
||||
packages,
|
||||
local_registry_dir,
|
||||
)
|
||||
}
|
||||
|
||||
// resolves the custom commands from an iterator of packages
|
||||
// and adds them to the existing custom commands.
|
||||
// note that this will overwrite any existing custom commands
|
||||
fn resolve_custom_commands_from_packages<
|
||||
'a,
|
||||
P: IntoIterator<Item = &'a NpmResolutionPackage>,
|
||||
>(
|
||||
mut commands: crate::task_runner::TaskCustomCommands,
|
||||
snapshot: &'a NpmResolutionSnapshot,
|
||||
packages: P,
|
||||
local_registry_dir: &Path,
|
||||
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
|
||||
let mut bin_entries = bin_entries::BinEntries::new();
|
||||
for package in packages {
|
||||
let package_path =
|
||||
local_node_modules_package_path(local_registry_dir, package);
|
||||
|
||||
if package.bin.is_some() {
|
||||
bin_entries.add(package.clone(), package_path);
|
||||
}
|
||||
}
|
||||
let bins = bin_entries.into_bin_files(snapshot);
|
||||
for (bin_name, script_path) in bins {
|
||||
commands.insert(
|
||||
bin_name.clone(),
|
||||
Rc::new(crate::task_runner::NodeModulesFileRunCommand {
|
||||
command_name: bin_name,
|
||||
path: script_path,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(commands)
|
||||
}
|
||||
|
||||
fn local_node_modules_package_path(
|
||||
/// `node_modules/.deno/<package>/node_modules/<package_name>`
|
||||
///
|
||||
/// Where the actual package is stored.
|
||||
fn local_node_modules_package_contents_path(
|
||||
local_registry_dir: &Path,
|
||||
package: &NpmResolutionPackage,
|
||||
) -> PathBuf {
|
||||
|
@ -351,62 +283,6 @@ fn local_node_modules_package_path(
|
|||
.join(&package.id.nv.name)
|
||||
}
|
||||
|
||||
// resolves the custom commands from the dependencies of a package
|
||||
// and adds them to the existing custom commands.
|
||||
// note that this will overwrite any existing custom commands.
|
||||
fn resolve_custom_commands_from_deps(
|
||||
baseline: crate::task_runner::TaskCustomCommands,
|
||||
package: &NpmResolutionPackage,
|
||||
snapshot: &NpmResolutionSnapshot,
|
||||
local_registry_dir: &Path,
|
||||
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
|
||||
resolve_custom_commands_from_packages(
|
||||
baseline,
|
||||
snapshot,
|
||||
package
|
||||
.dependencies
|
||||
.values()
|
||||
.map(|id| snapshot.package_from_id(id).unwrap()),
|
||||
local_registry_dir,
|
||||
)
|
||||
}
|
||||
|
||||
fn can_run_scripts(
|
||||
allow_scripts: &PackagesAllowedScripts,
|
||||
package_nv: &PackageNv,
|
||||
) -> bool {
|
||||
match allow_scripts {
|
||||
PackagesAllowedScripts::All => true,
|
||||
// TODO: make this more correct
|
||||
PackagesAllowedScripts::Some(allow_list) => allow_list.iter().any(|s| {
|
||||
let s = s.strip_prefix("npm:").unwrap_or(s);
|
||||
s == package_nv.name || s == package_nv.to_string()
|
||||
}),
|
||||
PackagesAllowedScripts::None => false,
|
||||
}
|
||||
}
|
||||
|
||||
// npm defaults to running `node-gyp rebuild` if there is a `binding.gyp` file
|
||||
// but it always fails if the package excludes the `binding.gyp` file when they publish.
|
||||
// (for example, `fsevents` hits this)
|
||||
fn is_broken_default_install_script(script: &str, package_path: &Path) -> bool {
|
||||
script == "node-gyp rebuild" && !package_path.join("binding.gyp").exists()
|
||||
}
|
||||
|
||||
fn has_lifecycle_scripts(
|
||||
package: &NpmResolutionPackage,
|
||||
package_path: &Path,
|
||||
) -> bool {
|
||||
if let Some(install) = package.scripts.get("install") {
|
||||
// default script
|
||||
if !is_broken_default_install_script(install, package_path) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
package.scripts.contains_key("preinstall")
|
||||
|| package.scripts.contains_key("postinstall")
|
||||
}
|
||||
|
||||
/// Creates a pnpm style folder structure.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn sync_resolution_with_fs(
|
||||
|
@ -457,9 +333,24 @@ async fn sync_resolution_with_fs(
|
|||
let mut cache_futures = FuturesUnordered::new();
|
||||
let mut newest_packages_by_name: HashMap<&String, &NpmResolutionPackage> =
|
||||
HashMap::with_capacity(package_partitions.packages.len());
|
||||
let bin_entries = Rc::new(RefCell::new(bin_entries::BinEntries::new()));
|
||||
let mut packages_with_scripts = Vec::with_capacity(2);
|
||||
let mut packages_with_scripts_not_run = Vec::new();
|
||||
let bin_entries =
|
||||
Rc::new(RefCell::new(super::common::bin_entries::BinEntries::new()));
|
||||
let mut lifecycle_scripts =
|
||||
super::common::lifecycle_scripts::LifecycleScripts::new(
|
||||
lifecycle_scripts,
|
||||
LocalLifecycleScripts {
|
||||
deno_local_registry_dir: &deno_local_registry_dir,
|
||||
},
|
||||
);
|
||||
let packages_with_deprecation_warnings = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
let mut package_tags: HashMap<&PackageNv, Vec<&str>> = HashMap::new();
|
||||
for (package_req, package_nv) in snapshot.package_reqs() {
|
||||
if let Some(tag) = package_req.version_req.tag() {
|
||||
package_tags.entry(package_nv).or_default().push(tag);
|
||||
}
|
||||
}
|
||||
|
||||
for package in &package_partitions.packages {
|
||||
if let Some(current_pkg) =
|
||||
newest_packages_by_name.get_mut(&package.id.nv.name)
|
||||
|
@ -474,11 +365,29 @@ async fn sync_resolution_with_fs(
|
|||
let package_folder_name =
|
||||
get_package_folder_id_folder_name(&package.get_package_cache_folder_id());
|
||||
let folder_path = deno_local_registry_dir.join(&package_folder_name);
|
||||
let tags = package_tags
|
||||
.get(&package.id.nv)
|
||||
.map(|tags| tags.join(","))
|
||||
.unwrap_or_default();
|
||||
enum PackageFolderState {
|
||||
UpToDate,
|
||||
Uninitialized,
|
||||
TagsOutdated,
|
||||
}
|
||||
let initialized_file = folder_path.join(".initialized");
|
||||
let package_state = std::fs::read_to_string(&initialized_file)
|
||||
.map(|s| {
|
||||
if s != tags {
|
||||
PackageFolderState::TagsOutdated
|
||||
} else {
|
||||
PackageFolderState::UpToDate
|
||||
}
|
||||
})
|
||||
.unwrap_or(PackageFolderState::Uninitialized);
|
||||
if !cache
|
||||
.cache_setting()
|
||||
.should_use_for_npm_package(&package.id.nv.name)
|
||||
|| !initialized_file.exists()
|
||||
|| matches!(package_state, PackageFolderState::Uninitialized)
|
||||
{
|
||||
// cache bust the dep from the dep setup cache so the symlinks
|
||||
// are forced to be recreated
|
||||
|
@ -486,6 +395,9 @@ async fn sync_resolution_with_fs(
|
|||
|
||||
let folder_path = folder_path.clone();
|
||||
let bin_entries_to_setup = bin_entries.clone();
|
||||
let packages_with_deprecation_warnings =
|
||||
packages_with_deprecation_warnings.clone();
|
||||
|
||||
cache_futures.push(async move {
|
||||
tarball_cache
|
||||
.ensure_package(&package.id.nv, &package.dist)
|
||||
|
@ -504,7 +416,7 @@ async fn sync_resolution_with_fs(
|
|||
move || {
|
||||
clone_dir_recursive(&cache_folder, &package_path)?;
|
||||
// write out a file that indicates this folder has been initialized
|
||||
fs::write(initialized_file, "")?;
|
||||
fs::write(initialized_file, tags)?;
|
||||
|
||||
Ok::<_, AnyError>(())
|
||||
}
|
||||
|
@ -512,44 +424,27 @@ async fn sync_resolution_with_fs(
|
|||
.await??;
|
||||
|
||||
if package.bin.is_some() {
|
||||
bin_entries_to_setup
|
||||
.borrow_mut()
|
||||
.add(package.clone(), package_path);
|
||||
bin_entries_to_setup.borrow_mut().add(package, package_path);
|
||||
}
|
||||
|
||||
if let Some(deprecated) = &package.deprecated {
|
||||
log::info!(
|
||||
"{} {:?} is deprecated: {}",
|
||||
crate::colors::yellow("Warning"),
|
||||
package.id,
|
||||
crate::colors::gray(deprecated),
|
||||
);
|
||||
packages_with_deprecation_warnings
|
||||
.lock()
|
||||
.push((package.id.clone(), deprecated.clone()));
|
||||
}
|
||||
|
||||
// finally stop showing the progress bar
|
||||
drop(pb_guard); // explicit for clarity
|
||||
Ok::<_, AnyError>(())
|
||||
});
|
||||
} else if matches!(package_state, PackageFolderState::TagsOutdated) {
|
||||
fs::write(initialized_file, tags)?;
|
||||
}
|
||||
|
||||
let sub_node_modules = folder_path.join("node_modules");
|
||||
let package_path =
|
||||
join_package_name(&sub_node_modules, &package.id.nv.name);
|
||||
if has_lifecycle_scripts(package, &package_path) {
|
||||
let scripts_run = folder_path.join(".scripts-run");
|
||||
let has_warned = folder_path.join(".scripts-warned");
|
||||
if can_run_scripts(&lifecycle_scripts.allowed, &package.id.nv) {
|
||||
if !scripts_run.exists() {
|
||||
packages_with_scripts.push((
|
||||
package.clone(),
|
||||
package_path,
|
||||
scripts_run,
|
||||
));
|
||||
}
|
||||
} else if !scripts_run.exists() && !has_warned.exists() {
|
||||
packages_with_scripts_not_run.push((has_warned, package.id.nv.clone()));
|
||||
}
|
||||
}
|
||||
lifecycle_scripts.add(package, package_path.into());
|
||||
}
|
||||
|
||||
while let Some(result) = cache_futures.next().await {
|
||||
|
@ -620,6 +515,9 @@ async fn sync_resolution_with_fs(
|
|||
|
||||
let mut found_names: HashMap<&String, &PackageNv> = HashMap::new();
|
||||
|
||||
// set of node_modules in workspace packages that we've already ensured exist
|
||||
let mut existing_child_node_modules_dirs: HashSet<PathBuf> = HashSet::new();
|
||||
|
||||
// 4. Create symlinks for package json dependencies
|
||||
{
|
||||
for remote in npm_install_deps_provider.remote_pkgs() {
|
||||
|
@ -638,17 +536,20 @@ async fn sync_resolution_with_fs(
|
|||
} else {
|
||||
continue; // skip, package not found
|
||||
};
|
||||
let alias_clashes = remote.req.name != remote.alias
|
||||
&& newest_packages_by_name.contains_key(&remote.alias);
|
||||
let Some(remote_alias) = &remote.alias else {
|
||||
continue;
|
||||
};
|
||||
let alias_clashes = remote.req.name != *remote_alias
|
||||
&& newest_packages_by_name.contains_key(remote_alias);
|
||||
let install_in_child = {
|
||||
// we'll install in the child if the alias is taken by another package, or
|
||||
// if there's already a package with the same name but different version
|
||||
// linked into the root
|
||||
match found_names.entry(&remote.alias) {
|
||||
match found_names.entry(remote_alias) {
|
||||
Entry::Occupied(nv) => {
|
||||
alias_clashes
|
||||
|| remote.req.name != nv.get().name // alias to a different package (in case of duplicate aliases)
|
||||
|| !remote.req.version_req.matches(&nv.get().version) // incompatible version
|
||||
// alias to a different package (in case of duplicate aliases)
|
||||
// or the version doesn't match the version in the root node_modules
|
||||
alias_clashes || &remote_pkg.id.nv != *nv.get()
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(&remote_pkg.id.nv);
|
||||
|
@ -667,8 +568,15 @@ async fn sync_resolution_with_fs(
|
|||
);
|
||||
if install_in_child {
|
||||
// symlink the dep into the package's child node_modules folder
|
||||
let dest_path =
|
||||
remote.base_dir.join("node_modules").join(&remote.alias);
|
||||
let dest_node_modules = remote.base_dir.join("node_modules");
|
||||
if !existing_child_node_modules_dirs.contains(&dest_node_modules) {
|
||||
fs::create_dir_all(&dest_node_modules).with_context(|| {
|
||||
format!("Creating '{}'", dest_node_modules.display())
|
||||
})?;
|
||||
existing_child_node_modules_dirs.insert(dest_node_modules.clone());
|
||||
}
|
||||
let mut dest_path = dest_node_modules;
|
||||
dest_path.push(remote_alias);
|
||||
|
||||
symlink_package_dir(&local_registry_package_path, &dest_path)?;
|
||||
} else {
|
||||
|
@ -678,7 +586,7 @@ async fn sync_resolution_with_fs(
|
|||
{
|
||||
symlink_package_dir(
|
||||
&local_registry_package_path,
|
||||
&join_package_name(root_node_modules_dir_path, &remote.alias),
|
||||
&join_package_name(root_node_modules_dir_path, remote_alias),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
@ -763,88 +671,51 @@ async fn sync_resolution_with_fs(
|
|||
// install correctly for a workspace (potentially in sub directories),
|
||||
// but this is good enough for a first pass
|
||||
for workspace in npm_install_deps_provider.workspace_pkgs() {
|
||||
let Some(workspace_alias) = &workspace.alias else {
|
||||
continue;
|
||||
};
|
||||
symlink_package_dir(
|
||||
&workspace.target_dir,
|
||||
&root_node_modules_dir_path.join(&workspace.alias),
|
||||
&root_node_modules_dir_path.join(workspace_alias),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
if !packages_with_scripts.is_empty() {
|
||||
// get custom commands for each bin available in the node_modules dir (essentially
|
||||
// the scripts that are in `node_modules/.bin`)
|
||||
let base = resolve_baseline_custom_commands(
|
||||
snapshot,
|
||||
&package_partitions.packages,
|
||||
&deno_local_registry_dir,
|
||||
)?;
|
||||
let init_cwd = lifecycle_scripts.initial_cwd.as_deref().unwrap();
|
||||
let process_state = crate::npm::managed::npm_process_state(
|
||||
snapshot.as_valid_serialized(),
|
||||
Some(root_node_modules_dir_path),
|
||||
);
|
||||
|
||||
let mut env_vars = crate::task_runner::real_env_vars();
|
||||
env_vars.insert(
|
||||
crate::args::NPM_RESOLUTION_STATE_ENV_VAR_NAME.to_string(),
|
||||
process_state,
|
||||
);
|
||||
for (package, package_path, scripts_run_path) in packages_with_scripts {
|
||||
// add custom commands for binaries from the package's dependencies. this will take precedence over the
|
||||
// baseline commands, so if the package relies on a bin that conflicts with one higher in the dependency tree, the
|
||||
// correct bin will be used.
|
||||
let custom_commands = resolve_custom_commands_from_deps(
|
||||
base.clone(),
|
||||
&package,
|
||||
snapshot,
|
||||
&deno_local_registry_dir,
|
||||
)?;
|
||||
for script_name in ["preinstall", "install", "postinstall"] {
|
||||
if let Some(script) = package.scripts.get(script_name) {
|
||||
if script_name == "install"
|
||||
&& is_broken_default_install_script(script, &package_path)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let exit_code =
|
||||
crate::task_runner::run_task(crate::task_runner::RunTaskOptions {
|
||||
task_name: script_name,
|
||||
script,
|
||||
cwd: &package_path,
|
||||
env_vars: env_vars.clone(),
|
||||
custom_commands: custom_commands.clone(),
|
||||
init_cwd,
|
||||
argv: &[],
|
||||
root_node_modules_dir: Some(root_node_modules_dir_path),
|
||||
})
|
||||
.await?;
|
||||
if exit_code != 0 {
|
||||
anyhow::bail!(
|
||||
"script '{}' in '{}' failed with exit code {}",
|
||||
script_name,
|
||||
package.id.nv,
|
||||
exit_code,
|
||||
);
|
||||
}
|
||||
{
|
||||
let packages_with_deprecation_warnings =
|
||||
packages_with_deprecation_warnings.lock();
|
||||
if !packages_with_deprecation_warnings.is_empty() {
|
||||
log::warn!(
|
||||
"{} The following packages are deprecated:",
|
||||
colors::yellow("Warning")
|
||||
);
|
||||
let len = packages_with_deprecation_warnings.len();
|
||||
for (idx, (package_id, msg)) in
|
||||
packages_with_deprecation_warnings.iter().enumerate()
|
||||
{
|
||||
if idx != len - 1 {
|
||||
log::warn!(
|
||||
"┠─ {}",
|
||||
colors::gray(format!("npm:{:?} ({})", package_id, msg))
|
||||
);
|
||||
} else {
|
||||
log::warn!(
|
||||
"┖─ {}",
|
||||
colors::gray(format!("npm:{:?} ({})", package_id, msg))
|
||||
);
|
||||
}
|
||||
}
|
||||
fs::write(scripts_run_path, "")?;
|
||||
}
|
||||
}
|
||||
|
||||
if !packages_with_scripts_not_run.is_empty() {
|
||||
let packages = packages_with_scripts_not_run
|
||||
.iter()
|
||||
.map(|(_, p)| format!("npm:{p}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
log::warn!("{} Packages contained npm lifecycle scripts (preinstall/install/postinstall) that were not executed.
|
||||
This may cause the packages to not work correctly. To run them, use the `--allow-scripts` flag with `deno cache` or `deno install`
|
||||
(e.g. `deno cache --allow-scripts=pkg1,pkg2 <entrypoint>` or `deno install --allow-scripts=pkg1,pkg2`):\n {packages}", crate::colors::yellow("Warning"));
|
||||
for (scripts_warned_path, _) in packages_with_scripts_not_run {
|
||||
let _ignore_err = fs::write(scripts_warned_path, "");
|
||||
}
|
||||
}
|
||||
lifecycle_scripts
|
||||
.finish(
|
||||
snapshot,
|
||||
&package_partitions.packages,
|
||||
Some(root_node_modules_dir_path),
|
||||
progress_bar,
|
||||
)
|
||||
.await?;
|
||||
|
||||
setup_cache.save();
|
||||
drop(single_process_lock);
|
||||
|
@ -853,6 +724,98 @@ async fn sync_resolution_with_fs(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// `node_modules/.deno/<package>/`
|
||||
fn local_node_modules_package_folder(
|
||||
local_registry_dir: &Path,
|
||||
package: &NpmResolutionPackage,
|
||||
) -> PathBuf {
|
||||
local_registry_dir.join(get_package_folder_id_folder_name(
|
||||
&package.get_package_cache_folder_id(),
|
||||
))
|
||||
}
|
||||
|
||||
struct LocalLifecycleScripts<'a> {
|
||||
deno_local_registry_dir: &'a Path,
|
||||
}
|
||||
|
||||
impl<'a> LocalLifecycleScripts<'a> {
|
||||
/// `node_modules/.deno/<package>/.scripts-run`
|
||||
fn ran_scripts_file(&self, package: &NpmResolutionPackage) -> PathBuf {
|
||||
local_node_modules_package_folder(self.deno_local_registry_dir, package)
|
||||
.join(".scripts-run")
|
||||
}
|
||||
|
||||
/// `node_modules/.deno/<package>/.scripts-warned`
|
||||
fn warned_scripts_file(&self, package: &NpmResolutionPackage) -> PathBuf {
|
||||
local_node_modules_package_folder(self.deno_local_registry_dir, package)
|
||||
.join(".scripts-warned")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> super::common::lifecycle_scripts::LifecycleScriptsStrategy
|
||||
for LocalLifecycleScripts<'a>
|
||||
{
|
||||
fn package_path(&self, package: &NpmResolutionPackage) -> PathBuf {
|
||||
local_node_modules_package_contents_path(
|
||||
self.deno_local_registry_dir,
|
||||
package,
|
||||
)
|
||||
}
|
||||
|
||||
fn did_run_scripts(
|
||||
&self,
|
||||
package: &NpmResolutionPackage,
|
||||
) -> std::result::Result<(), deno_core::anyhow::Error> {
|
||||
std::fs::write(self.ran_scripts_file(package), "")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn warn_on_scripts_not_run(
|
||||
&self,
|
||||
packages: &[(&NpmResolutionPackage, std::path::PathBuf)],
|
||||
) -> Result<(), AnyError> {
|
||||
if !packages.is_empty() {
|
||||
log::warn!("{} The following packages contained npm lifecycle scripts ({}) that were not executed:", colors::yellow("Warning"), colors::gray("preinstall/install/postinstall"));
|
||||
|
||||
for (package, _) in packages {
|
||||
log::warn!("┠─ {}", colors::gray(format!("npm:{}", package.id.nv)));
|
||||
}
|
||||
|
||||
log::warn!("┃");
|
||||
log::warn!(
|
||||
"┠─ {}",
|
||||
colors::italic("This may cause the packages to not work correctly.")
|
||||
);
|
||||
log::warn!("┖─ {}", colors::italic("To run lifecycle scripts, use the `--allow-scripts` flag with `deno install`:"));
|
||||
let packages_comma_separated = packages
|
||||
.iter()
|
||||
.map(|(p, _)| format!("npm:{}", p.id.nv))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
log::warn!(
|
||||
" {}",
|
||||
colors::bold(format!(
|
||||
"deno install --allow-scripts={}",
|
||||
packages_comma_separated
|
||||
))
|
||||
);
|
||||
|
||||
for (package, _) in packages {
|
||||
let _ignore_err = fs::write(self.warned_scripts_file(package), "");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn has_warned(&self, package: &NpmResolutionPackage) -> bool {
|
||||
self.warned_scripts_file(package).exists()
|
||||
}
|
||||
|
||||
fn has_run(&self, package: &NpmResolutionPackage) -> bool {
|
||||
self.ran_scripts_file(package).exists()
|
||||
}
|
||||
}
|
||||
|
||||
// Uses BTreeMap to preserve the ordering of the elements in memory, to ensure
|
||||
// the file generated from this datastructure is deterministic.
|
||||
// See: https://github.com/denoland/deno/issues/24479
|
||||
|
@ -987,20 +950,6 @@ impl SetupCache {
|
|||
}
|
||||
}
|
||||
|
||||
/// Normalizes a package name for use at `node_modules/.deno/<pkg-name>@<version>[_<copy_index>]`
|
||||
pub fn normalize_pkg_name_for_node_modules_deno_folder(name: &str) -> Cow<str> {
|
||||
let name = if name.to_lowercase() == name {
|
||||
Cow::Borrowed(name)
|
||||
} else {
|
||||
Cow::Owned(format!("_{}", mixed_case_package_name_encode(name)))
|
||||
};
|
||||
if name.starts_with('@') {
|
||||
name.replace('/', "+").into()
|
||||
} else {
|
||||
name
|
||||
}
|
||||
}
|
||||
|
||||
fn get_package_folder_id_folder_name(
|
||||
folder_id: &NpmPackageCacheFolderId,
|
||||
) -> String {
|
||||
|
|
|
@ -15,7 +15,6 @@ use crate::args::NpmInstallDepsProvider;
|
|||
use crate::util::progress_bar::ProgressBar;
|
||||
|
||||
pub use self::common::NpmPackageFsResolver;
|
||||
pub use self::local::normalize_pkg_name_for_node_modules_deno_folder;
|
||||
|
||||
use self::global::GlobalNpmPackageResolver;
|
||||
use self::local::LocalNpmPackageResolver;
|
||||
|
@ -54,6 +53,7 @@ pub fn create_npm_fs_resolver(
|
|||
tarball_cache,
|
||||
resolution,
|
||||
system_info,
|
||||
lifecycle_scripts,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
mod byonm;
|
||||
mod cache_dir;
|
||||
mod common;
|
||||
mod managed;
|
||||
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -13,26 +13,35 @@ use deno_ast::ModuleSpecifier;
|
|||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use deno_npm::registry::NpmPackageInfo;
|
||||
use deno_resolver::npm::ByonmNpmResolver;
|
||||
use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError;
|
||||
use deno_runtime::deno_node::NodeRequireResolver;
|
||||
use deno_runtime::deno_node::NpmProcessStateProvider;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use deno_runtime::ops::process::NpmProcessStateProvider;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
use node_resolver::NpmResolver;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::args::npm_registry_url;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
|
||||
pub use self::byonm::ByonmCliNpmResolver;
|
||||
pub use self::byonm::CliNpmResolverByonmCreateOptions;
|
||||
pub use self::cache_dir::NpmCacheDir;
|
||||
pub use self::byonm::CliByonmNpmResolver;
|
||||
pub use self::byonm::CliByonmNpmResolverCreateOptions;
|
||||
pub use self::managed::CliNpmResolverManagedCreateOptions;
|
||||
pub use self::managed::CliNpmResolverManagedSnapshotOption;
|
||||
pub use self::managed::ManagedCliNpmResolver;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum ResolvePkgFolderFromDenoReqError {
|
||||
#[error(transparent)]
|
||||
Managed(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
Byonm(#[from] ByonmResolvePkgFolderFromDenoReqError),
|
||||
}
|
||||
|
||||
pub enum CliNpmResolverCreateOptions {
|
||||
Managed(CliNpmResolverManagedCreateOptions),
|
||||
Byonm(CliNpmResolverByonmCreateOptions),
|
||||
Byonm(CliByonmNpmResolverCreateOptions),
|
||||
}
|
||||
|
||||
pub async fn create_cli_npm_resolver_for_lsp(
|
||||
|
@ -43,7 +52,7 @@ pub async fn create_cli_npm_resolver_for_lsp(
|
|||
Managed(options) => {
|
||||
managed::create_managed_npm_resolver_for_lsp(options).await
|
||||
}
|
||||
Byonm(options) => byonm::create_byonm_npm_resolver(options),
|
||||
Byonm(options) => Arc::new(ByonmNpmResolver::new(options)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,14 +62,14 @@ pub async fn create_cli_npm_resolver(
|
|||
use CliNpmResolverCreateOptions::*;
|
||||
match options {
|
||||
Managed(options) => managed::create_managed_npm_resolver(options).await,
|
||||
Byonm(options) => Ok(byonm::create_byonm_npm_resolver(options)),
|
||||
Byonm(options) => Ok(Arc::new(ByonmNpmResolver::new(options))),
|
||||
}
|
||||
}
|
||||
|
||||
pub enum InnerCliNpmResolverRef<'a> {
|
||||
Managed(&'a ManagedCliNpmResolver),
|
||||
#[allow(dead_code)]
|
||||
Byonm(&'a ByonmCliNpmResolver),
|
||||
Byonm(&'a CliByonmNpmResolver),
|
||||
}
|
||||
|
||||
pub trait CliNpmResolver: NpmResolver {
|
||||
|
@ -81,20 +90,20 @@ pub trait CliNpmResolver: NpmResolver {
|
|||
}
|
||||
}
|
||||
|
||||
fn as_byonm(&self) -> Option<&ByonmCliNpmResolver> {
|
||||
fn as_byonm(&self) -> Option<&CliByonmNpmResolver> {
|
||||
match self.as_inner() {
|
||||
InnerCliNpmResolverRef::Managed(_) => None,
|
||||
InnerCliNpmResolverRef::Byonm(inner) => Some(inner),
|
||||
}
|
||||
}
|
||||
|
||||
fn root_node_modules_path(&self) -> Option<&PathBuf>;
|
||||
fn root_node_modules_path(&self) -> Option<&Path>;
|
||||
|
||||
fn resolve_pkg_folder_from_deno_module_req(
|
||||
&self,
|
||||
req: &PackageReq,
|
||||
referrer: &ModuleSpecifier,
|
||||
) -> Result<PathBuf, AnyError>;
|
||||
) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError>;
|
||||
|
||||
/// Returns a hash returning the state of the npm resolver
|
||||
/// or `None` if the state currently can't be determined.
|
||||
|
@ -152,10 +161,7 @@ impl NpmFetchResolver {
|
|||
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(&info_url, &PermissionsContainer::allow_all())
|
||||
.await
|
||||
.ok()
|
||||
file_fetcher.fetch_bypass_permissions(&info_url).await.ok()
|
||||
})
|
||||
.await
|
||||
.ok()??;
|
||||
|
|
|
@ -11,7 +11,6 @@ use deno_core::op2;
|
|||
use deno_core::v8;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_core::OpState;
|
||||
use deno_runtime::deno_permissions::create_child_permissions;
|
||||
use deno_runtime::deno_permissions::ChildPermissionsArg;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
@ -60,11 +59,7 @@ pub fn op_pledge_test_permissions(
|
|||
) -> Result<Uuid, AnyError> {
|
||||
let token = Uuid::new_v4();
|
||||
let parent_permissions = state.borrow_mut::<PermissionsContainer>();
|
||||
let worker_permissions = {
|
||||
let mut parent_permissions = parent_permissions.0.lock();
|
||||
let perms = create_child_permissions(&mut parent_permissions, args)?;
|
||||
PermissionsContainer::new(perms)
|
||||
};
|
||||
let worker_permissions = parent_permissions.create_child_permissions(args)?;
|
||||
let parent_permissions = parent_permissions.clone();
|
||||
|
||||
if state.try_take::<PermissionsHolder>().is_some() {
|
||||
|
@ -74,7 +69,6 @@ pub fn op_pledge_test_permissions(
|
|||
state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions));
|
||||
|
||||
// NOTE: This call overrides current permission set for the worker
|
||||
state.put(worker_permissions.0.clone());
|
||||
state.put::<PermissionsContainer>(worker_permissions);
|
||||
|
||||
Ok(token)
|
||||
|
@ -91,7 +85,6 @@ pub fn op_restore_test_permissions(
|
|||
}
|
||||
|
||||
let permissions = permissions_holder.1;
|
||||
state.put(permissions.0.clone());
|
||||
state.put::<PermissionsContainer>(permissions);
|
||||
Ok(())
|
||||
} else {
|
||||
|
|
|
@ -16,7 +16,6 @@ use deno_core::op2;
|
|||
use deno_core::v8;
|
||||
use deno_core::ModuleSpecifier;
|
||||
use deno_core::OpState;
|
||||
use deno_runtime::deno_permissions::create_child_permissions;
|
||||
use deno_runtime::deno_permissions::ChildPermissionsArg;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
|
@ -55,11 +54,7 @@ pub fn op_pledge_test_permissions(
|
|||
) -> Result<Uuid, AnyError> {
|
||||
let token = Uuid::new_v4();
|
||||
let parent_permissions = state.borrow_mut::<PermissionsContainer>();
|
||||
let worker_permissions = {
|
||||
let mut parent_permissions = parent_permissions.0.lock();
|
||||
let perms = create_child_permissions(&mut parent_permissions, args)?;
|
||||
PermissionsContainer::new(perms)
|
||||
};
|
||||
let worker_permissions = parent_permissions.create_child_permissions(args)?;
|
||||
let parent_permissions = parent_permissions.clone();
|
||||
|
||||
if state.try_take::<PermissionsHolder>().is_some() {
|
||||
|
@ -68,7 +63,6 @@ pub fn op_pledge_test_permissions(
|
|||
state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions));
|
||||
|
||||
// NOTE: This call overrides current permission set for the worker
|
||||
state.put(worker_permissions.0.clone());
|
||||
state.put::<PermissionsContainer>(worker_permissions);
|
||||
|
||||
Ok(token)
|
||||
|
@ -85,7 +79,6 @@ pub fn op_restore_test_permissions(
|
|||
}
|
||||
|
||||
let permissions = permissions_holder.1;
|
||||
state.put(permissions.0.clone());
|
||||
state.put::<PermissionsContainer>(permissions);
|
||||
Ok(())
|
||||
} else {
|
||||
|
|
623
cli/resolver.rs
623
cli/resolver.rs
|
@ -22,12 +22,13 @@ use deno_graph::NpmLoadError;
|
|||
use deno_graph::NpmResolvePkgReqsResult;
|
||||
use deno_npm::resolution::NpmResolutionError;
|
||||
use deno_package_json::PackageJsonDepValue;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolutionMode;
|
||||
use deno_resolver::sloppy_imports::SloppyImportsResolver;
|
||||
use deno_runtime::colors;
|
||||
use deno_runtime::deno_fs;
|
||||
use deno_runtime::deno_fs::FileSystem;
|
||||
use deno_runtime::deno_node::is_builtin_node_module;
|
||||
use deno_runtime::deno_node::NodeResolver;
|
||||
use deno_runtime::fs_util::specifier_to_file_path;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
use deno_semver::package::PackageReq;
|
||||
use node_resolver::errors::ClosestPkgJsonError;
|
||||
|
@ -42,7 +43,6 @@ use node_resolver::NodeModuleKind;
|
|||
use node_resolver::NodeResolution;
|
||||
use node_resolver::NodeResolutionMode;
|
||||
use node_resolver::PackageJson;
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
@ -52,7 +52,9 @@ use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
|
|||
use crate::node::CliNodeCodeTranslator;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::InnerCliNpmResolverRef;
|
||||
use crate::util::path::specifier_has_extension;
|
||||
use crate::util::sync::AtomicFlag;
|
||||
use crate::util::text_encoding::from_utf8_lossy_owned;
|
||||
|
||||
pub struct ModuleCodeStringSource {
|
||||
pub code: ModuleSourceCode,
|
||||
|
@ -60,13 +62,52 @@ pub struct ModuleCodeStringSource {
|
|||
pub media_type: MediaType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CliDenoResolverFs(pub Arc<dyn FileSystem>);
|
||||
|
||||
impl deno_resolver::fs::DenoResolverFs for CliDenoResolverFs {
|
||||
fn read_to_string_lossy(&self, path: &Path) -> std::io::Result<String> {
|
||||
self
|
||||
.0
|
||||
.read_text_file_lossy_sync(path, None)
|
||||
.map_err(|e| e.into_io_error())
|
||||
}
|
||||
|
||||
fn realpath_sync(&self, path: &Path) -> std::io::Result<PathBuf> {
|
||||
self.0.realpath_sync(path).map_err(|e| e.into_io_error())
|
||||
}
|
||||
|
||||
fn is_dir_sync(&self, path: &Path) -> bool {
|
||||
self.0.is_dir_sync(path)
|
||||
}
|
||||
|
||||
fn read_dir_sync(
|
||||
&self,
|
||||
dir_path: &Path,
|
||||
) -> std::io::Result<Vec<deno_resolver::fs::DirEntry>> {
|
||||
self
|
||||
.0
|
||||
.read_dir_sync(dir_path)
|
||||
.map(|entries| {
|
||||
entries
|
||||
.into_iter()
|
||||
.map(|e| deno_resolver::fs::DirEntry {
|
||||
name: e.name,
|
||||
is_file: e.is_file,
|
||||
is_directory: e.is_directory,
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.map_err(|err| err.into_io_error())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CliNodeResolver {
|
||||
cjs_resolutions: Arc<CjsResolutionStore>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
node_resolver: Arc<NodeResolver>,
|
||||
// todo(dsherret): remove this pub(crate)
|
||||
pub(crate) npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
}
|
||||
|
||||
impl CliNodeResolver {
|
||||
|
@ -175,7 +216,7 @@ impl CliNodeResolver {
|
|||
referrer: &ModuleSpecifier,
|
||||
mode: NodeResolutionMode,
|
||||
) -> Result<NodeResolution, NodeResolveError> {
|
||||
let referrer_kind = if self.cjs_resolutions.contains(referrer) {
|
||||
let referrer_kind = if self.cjs_resolutions.is_known_cjs(referrer) {
|
||||
NodeModuleKind::Cjs
|
||||
} else {
|
||||
NodeModuleKind::Esm
|
||||
|
@ -270,9 +311,7 @@ impl CliNodeResolver {
|
|||
if self.in_npm_package(&specifier) {
|
||||
let resolution =
|
||||
self.node_resolver.url_to_node_resolution(specifier)?;
|
||||
if let NodeResolution::CommonJs(specifier) = &resolution {
|
||||
self.cjs_resolutions.insert(specifier.clone());
|
||||
}
|
||||
let resolution = self.handle_node_resolution(resolution);
|
||||
return Ok(Some(resolution.into_url()));
|
||||
}
|
||||
}
|
||||
|
@ -293,12 +332,17 @@ impl CliNodeResolver {
|
|||
) -> NodeResolution {
|
||||
if let NodeResolution::CommonJs(specifier) = &resolution {
|
||||
// remember that this was a common js resolution
|
||||
self.cjs_resolutions.insert(specifier.clone());
|
||||
self.mark_cjs_resolution(specifier.clone());
|
||||
}
|
||||
resolution
|
||||
}
|
||||
|
||||
pub fn mark_cjs_resolution(&self, specifier: ModuleSpecifier) {
|
||||
self.cjs_resolutions.insert(specifier);
|
||||
}
|
||||
}
|
||||
|
||||
// todo(dsherret): move to module_loader.rs
|
||||
#[derive(Clone)]
|
||||
pub struct NpmModuleLoader {
|
||||
cjs_resolutions: Arc<CjsResolutionStore>,
|
||||
|
@ -322,16 +366,9 @@ impl NpmModuleLoader {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn load_if_in_npm_package(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
maybe_referrer: Option<&ModuleSpecifier>,
|
||||
) -> Option<Result<ModuleCodeStringSource, AnyError>> {
|
||||
if self.node_resolver.in_npm_package(specifier) {
|
||||
Some(self.load(specifier, maybe_referrer).await)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn if_in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
self.node_resolver.in_npm_package(specifier)
|
||||
|| self.cjs_resolutions.is_known_cjs(specifier)
|
||||
}
|
||||
|
||||
pub async fn load(
|
||||
|
@ -376,14 +413,9 @@ impl NpmModuleLoader {
|
|||
}
|
||||
})?;
|
||||
|
||||
let code = if self.cjs_resolutions.contains(specifier) {
|
||||
let code = if self.cjs_resolutions.is_known_cjs(specifier) {
|
||||
// translate cjs to esm if it's cjs and inject node globals
|
||||
let code = match String::from_utf8_lossy(&code) {
|
||||
Cow::Owned(code) => code,
|
||||
// SAFETY: `String::from_utf8_lossy` guarantees that the result is valid
|
||||
// UTF-8 if `Cow::Borrowed` is returned.
|
||||
Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(code) },
|
||||
};
|
||||
let code = from_utf8_lossy_owned(code);
|
||||
ModuleSourceCode::String(
|
||||
self
|
||||
.node_code_translator
|
||||
|
@ -408,8 +440,12 @@ impl NpmModuleLoader {
|
|||
pub struct CjsResolutionStore(DashSet<ModuleSpecifier>);
|
||||
|
||||
impl CjsResolutionStore {
|
||||
pub fn contains(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
self.0.contains(specifier)
|
||||
pub fn is_known_cjs(&self, specifier: &ModuleSpecifier) -> bool {
|
||||
if specifier.scheme() != "file" {
|
||||
return false;
|
||||
}
|
||||
|
||||
specifier_has_extension(specifier, "cjs") || self.0.contains(specifier)
|
||||
}
|
||||
|
||||
pub fn insert(&self, specifier: ModuleSpecifier) {
|
||||
|
@ -417,13 +453,16 @@ impl CjsResolutionStore {
|
|||
}
|
||||
}
|
||||
|
||||
pub type CliSloppyImportsResolver =
|
||||
SloppyImportsResolver<SloppyImportsCachedFs>;
|
||||
|
||||
/// A resolver that takes care of resolution, taking into account loaded
|
||||
/// import map, JSX settings.
|
||||
#[derive(Debug)]
|
||||
pub struct CliGraphResolver {
|
||||
node_resolver: Option<Arc<CliNodeResolver>>,
|
||||
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
sloppy_imports_resolver: Option<Arc<SloppyImportsResolver>>,
|
||||
sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
|
||||
workspace_resolver: Arc<WorkspaceResolver>,
|
||||
maybe_default_jsx_import_source: Option<String>,
|
||||
maybe_default_jsx_import_source_types: Option<String>,
|
||||
|
@ -437,7 +476,7 @@ pub struct CliGraphResolver {
|
|||
pub struct CliGraphResolverOptions<'a> {
|
||||
pub node_resolver: Option<Arc<CliNodeResolver>>,
|
||||
pub npm_resolver: Option<Arc<dyn CliNpmResolver>>,
|
||||
pub sloppy_imports_resolver: Option<Arc<SloppyImportsResolver>>,
|
||||
pub sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
|
||||
pub workspace_resolver: Arc<WorkspaceResolver>,
|
||||
pub bare_node_builtins_enabled: bool,
|
||||
pub maybe_jsx_import_source_config: Option<JsxImportSourceConfig>,
|
||||
|
@ -561,7 +600,15 @@ impl Resolver for CliGraphResolver {
|
|||
if let Some(sloppy_imports_resolver) = &self.sloppy_imports_resolver {
|
||||
Ok(
|
||||
sloppy_imports_resolver
|
||||
.resolve(&specifier, mode)
|
||||
.resolve(
|
||||
&specifier,
|
||||
match mode {
|
||||
ResolutionMode::Execution => {
|
||||
SloppyImportsResolutionMode::Execution
|
||||
}
|
||||
ResolutionMode::Types => SloppyImportsResolutionMode::Types,
|
||||
},
|
||||
)
|
||||
.map(|s| s.into_specifier())
|
||||
.unwrap_or(specifier),
|
||||
)
|
||||
|
@ -703,7 +750,14 @@ impl Resolver for CliGraphResolver {
|
|||
.resolve_if_for_npm_pkg(raw_specifier, referrer, to_node_mode(mode))
|
||||
.map_err(ResolveError::Other)?;
|
||||
if let Some(res) = maybe_resolution {
|
||||
return Ok(res.into_url());
|
||||
match res {
|
||||
NodeResolution::Esm(url) | NodeResolution::CommonJs(url) => {
|
||||
return Ok(url)
|
||||
}
|
||||
NodeResolution::BuiltIn(_) => {
|
||||
// don't resolve bare specifiers for built-in modules via node resolution
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -836,96 +890,18 @@ impl<'a> deno_graph::source::NpmResolver for WorkerCliNpmGraphResolver<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SloppyImportsFsEntry {
|
||||
File,
|
||||
Dir,
|
||||
}
|
||||
|
||||
impl SloppyImportsFsEntry {
|
||||
pub fn from_fs_stat(
|
||||
stat: &deno_runtime::deno_io::fs::FsStat,
|
||||
) -> Option<SloppyImportsFsEntry> {
|
||||
if stat.is_file {
|
||||
Some(SloppyImportsFsEntry::File)
|
||||
} else if stat.is_directory {
|
||||
Some(SloppyImportsFsEntry::Dir)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum SloppyImportsResolution {
|
||||
/// Ex. `./file.js` to `./file.ts`
|
||||
JsToTs(ModuleSpecifier),
|
||||
/// Ex. `./file` to `./file.ts`
|
||||
NoExtension(ModuleSpecifier),
|
||||
/// Ex. `./dir` to `./dir/index.ts`
|
||||
Directory(ModuleSpecifier),
|
||||
}
|
||||
|
||||
impl SloppyImportsResolution {
|
||||
pub fn as_specifier(&self) -> &ModuleSpecifier {
|
||||
match self {
|
||||
Self::JsToTs(specifier) => specifier,
|
||||
Self::NoExtension(specifier) => specifier,
|
||||
Self::Directory(specifier) => specifier,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_specifier(self) -> ModuleSpecifier {
|
||||
match self {
|
||||
Self::JsToTs(specifier) => specifier,
|
||||
Self::NoExtension(specifier) => specifier,
|
||||
Self::Directory(specifier) => specifier,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_suggestion_message(&self) -> String {
|
||||
format!("Maybe {}", self.as_base_message())
|
||||
}
|
||||
|
||||
pub fn as_quick_fix_message(&self) -> String {
|
||||
let message = self.as_base_message();
|
||||
let mut chars = message.chars();
|
||||
format!(
|
||||
"{}{}.",
|
||||
chars.next().unwrap().to_uppercase(),
|
||||
chars.as_str()
|
||||
)
|
||||
}
|
||||
|
||||
fn as_base_message(&self) -> String {
|
||||
match self {
|
||||
SloppyImportsResolution::JsToTs(specifier) => {
|
||||
let media_type = MediaType::from_specifier(specifier);
|
||||
format!("change the extension to '{}'", media_type.as_ts_extension())
|
||||
}
|
||||
SloppyImportsResolution::NoExtension(specifier) => {
|
||||
let media_type = MediaType::from_specifier(specifier);
|
||||
format!("add a '{}' extension", media_type.as_ts_extension())
|
||||
}
|
||||
SloppyImportsResolution::Directory(specifier) => {
|
||||
let file_name = specifier
|
||||
.path()
|
||||
.rsplit_once('/')
|
||||
.map(|(_, file_name)| file_name)
|
||||
.unwrap_or(specifier.path());
|
||||
format!("specify path to '{}' file in directory instead", file_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SloppyImportsResolver {
|
||||
fs: Arc<dyn FileSystem>,
|
||||
cache: Option<DashMap<PathBuf, Option<SloppyImportsFsEntry>>>,
|
||||
pub struct SloppyImportsCachedFs {
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
cache: Option<
|
||||
DashMap<
|
||||
PathBuf,
|
||||
Option<deno_resolver::sloppy_imports::SloppyImportsFsEntry>,
|
||||
>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl SloppyImportsResolver {
|
||||
impl SloppyImportsCachedFs {
|
||||
pub fn new(fs: Arc<dyn FileSystem>) -> Self {
|
||||
Self {
|
||||
fs,
|
||||
|
@ -936,409 +912,34 @@ impl SloppyImportsResolver {
|
|||
pub fn new_without_stat_cache(fs: Arc<dyn FileSystem>) -> Self {
|
||||
Self { fs, cache: None }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve(
|
||||
impl deno_resolver::sloppy_imports::SloppyImportResolverFs
|
||||
for SloppyImportsCachedFs
|
||||
{
|
||||
fn stat_sync(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
mode: ResolutionMode,
|
||||
) -> Option<SloppyImportsResolution> {
|
||||
fn path_without_ext(
|
||||
path: &Path,
|
||||
media_type: MediaType,
|
||||
) -> Option<Cow<str>> {
|
||||
let old_path_str = path.to_string_lossy();
|
||||
match media_type {
|
||||
MediaType::Unknown => Some(old_path_str),
|
||||
_ => old_path_str
|
||||
.strip_suffix(media_type.as_ts_extension())
|
||||
.map(|s| Cow::Owned(s.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn media_types_to_paths(
|
||||
path_no_ext: &str,
|
||||
original_media_type: MediaType,
|
||||
probe_media_type_types: Vec<MediaType>,
|
||||
reason: SloppyImportsResolutionReason,
|
||||
) -> Vec<(PathBuf, SloppyImportsResolutionReason)> {
|
||||
probe_media_type_types
|
||||
.into_iter()
|
||||
.filter(|media_type| *media_type != original_media_type)
|
||||
.map(|media_type| {
|
||||
(
|
||||
PathBuf::from(format!(
|
||||
"{}{}",
|
||||
path_no_ext,
|
||||
media_type.as_ts_extension()
|
||||
)),
|
||||
reason,
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
if specifier.scheme() != "file" {
|
||||
return None;
|
||||
}
|
||||
|
||||
let path = specifier_to_file_path(specifier).ok()?;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum SloppyImportsResolutionReason {
|
||||
JsToTs,
|
||||
NoExtension,
|
||||
Directory,
|
||||
}
|
||||
|
||||
let probe_paths: Vec<(PathBuf, SloppyImportsResolutionReason)> =
|
||||
match self.stat_sync(&path) {
|
||||
Some(SloppyImportsFsEntry::File) => {
|
||||
if mode.is_types() {
|
||||
let media_type = MediaType::from_specifier(specifier);
|
||||
// attempt to resolve the .d.ts file before the .js file
|
||||
let probe_media_type_types = match media_type {
|
||||
MediaType::JavaScript => {
|
||||
vec![(MediaType::Dts), MediaType::JavaScript]
|
||||
}
|
||||
MediaType::Mjs => {
|
||||
vec![MediaType::Dmts, MediaType::Dts, MediaType::Mjs]
|
||||
}
|
||||
MediaType::Cjs => {
|
||||
vec![MediaType::Dcts, MediaType::Dts, MediaType::Cjs]
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
let path_no_ext = path_without_ext(&path, media_type)?;
|
||||
media_types_to_paths(
|
||||
&path_no_ext,
|
||||
media_type,
|
||||
probe_media_type_types,
|
||||
SloppyImportsResolutionReason::JsToTs,
|
||||
)
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
entry @ None | entry @ Some(SloppyImportsFsEntry::Dir) => {
|
||||
let media_type = MediaType::from_specifier(specifier);
|
||||
let probe_media_type_types = match media_type {
|
||||
MediaType::JavaScript => (
|
||||
if mode.is_types() {
|
||||
vec![MediaType::TypeScript, MediaType::Tsx, MediaType::Dts]
|
||||
} else {
|
||||
vec![MediaType::TypeScript, MediaType::Tsx]
|
||||
},
|
||||
SloppyImportsResolutionReason::JsToTs,
|
||||
),
|
||||
MediaType::Jsx => {
|
||||
(vec![MediaType::Tsx], SloppyImportsResolutionReason::JsToTs)
|
||||
}
|
||||
MediaType::Mjs => (
|
||||
if mode.is_types() {
|
||||
vec![MediaType::Mts, MediaType::Dmts, MediaType::Dts]
|
||||
} else {
|
||||
vec![MediaType::Mts]
|
||||
},
|
||||
SloppyImportsResolutionReason::JsToTs,
|
||||
),
|
||||
MediaType::Cjs => (
|
||||
if mode.is_types() {
|
||||
vec![MediaType::Cts, MediaType::Dcts, MediaType::Dts]
|
||||
} else {
|
||||
vec![MediaType::Cts]
|
||||
},
|
||||
SloppyImportsResolutionReason::JsToTs,
|
||||
),
|
||||
MediaType::TypeScript
|
||||
| MediaType::Mts
|
||||
| MediaType::Cts
|
||||
| MediaType::Dts
|
||||
| MediaType::Dmts
|
||||
| MediaType::Dcts
|
||||
| MediaType::Tsx
|
||||
| MediaType::Json
|
||||
| MediaType::Wasm
|
||||
| MediaType::TsBuildInfo
|
||||
| MediaType::SourceMap => {
|
||||
return None;
|
||||
}
|
||||
MediaType::Unknown => (
|
||||
if mode.is_types() {
|
||||
vec![
|
||||
MediaType::TypeScript,
|
||||
MediaType::Tsx,
|
||||
MediaType::Mts,
|
||||
MediaType::Dts,
|
||||
MediaType::Dmts,
|
||||
MediaType::Dcts,
|
||||
MediaType::JavaScript,
|
||||
MediaType::Jsx,
|
||||
MediaType::Mjs,
|
||||
]
|
||||
} else {
|
||||
vec![
|
||||
MediaType::TypeScript,
|
||||
MediaType::JavaScript,
|
||||
MediaType::Tsx,
|
||||
MediaType::Jsx,
|
||||
MediaType::Mts,
|
||||
MediaType::Mjs,
|
||||
]
|
||||
},
|
||||
SloppyImportsResolutionReason::NoExtension,
|
||||
),
|
||||
};
|
||||
let mut probe_paths = match path_without_ext(&path, media_type) {
|
||||
Some(path_no_ext) => media_types_to_paths(
|
||||
&path_no_ext,
|
||||
media_type,
|
||||
probe_media_type_types.0,
|
||||
probe_media_type_types.1,
|
||||
),
|
||||
None => vec![],
|
||||
};
|
||||
|
||||
if matches!(entry, Some(SloppyImportsFsEntry::Dir)) {
|
||||
// try to resolve at the index file
|
||||
if mode.is_types() {
|
||||
probe_paths.push((
|
||||
path.join("index.ts"),
|
||||
SloppyImportsResolutionReason::Directory,
|
||||
));
|
||||
|
||||
probe_paths.push((
|
||||
path.join("index.mts"),
|
||||
SloppyImportsResolutionReason::Directory,
|
||||
));
|
||||
probe_paths.push((
|
||||
path.join("index.d.ts"),
|
||||
SloppyImportsResolutionReason::Directory,
|
||||
));
|
||||
probe_paths.push((
|
||||
path.join("index.d.mts"),
|
||||
SloppyImportsResolutionReason::Directory,
|
||||
));
|
||||
probe_paths.push((
|
||||
path.join("index.js"),
|
||||
SloppyImportsResolutionReason::Directory,
|
||||
));
|
||||
probe_paths.push((
|
||||
path.join("index.mjs"),
|
||||
SloppyImportsResolutionReason::Directory,
|
||||
));
|
||||
probe_paths.push((
|
||||
path.join("index.tsx"),
|
||||
SloppyImportsResolutionReason::Directory,
|
||||
));
|
||||
probe_paths.push((
|
||||
path.join("index.jsx"),
|
||||
SloppyImportsResolutionReason::Directory,
|
||||
));
|
||||
} else {
|
||||
probe_paths.push((
|
||||
path.join("index.ts"),
|
||||
SloppyImportsResolutionReason::Directory,
|
||||
));
|
||||
probe_paths.push((
|
||||
path.join("index.mts"),
|
||||
SloppyImportsResolutionReason::Directory,
|
||||
));
|
||||
probe_paths.push((
|
||||
path.join("index.tsx"),
|
||||
SloppyImportsResolutionReason::Directory,
|
||||
));
|
||||
probe_paths.push((
|
||||
path.join("index.js"),
|
||||
SloppyImportsResolutionReason::Directory,
|
||||
));
|
||||
probe_paths.push((
|
||||
path.join("index.mjs"),
|
||||
SloppyImportsResolutionReason::Directory,
|
||||
));
|
||||
probe_paths.push((
|
||||
path.join("index.jsx"),
|
||||
SloppyImportsResolutionReason::Directory,
|
||||
));
|
||||
}
|
||||
}
|
||||
if probe_paths.is_empty() {
|
||||
return None;
|
||||
}
|
||||
probe_paths
|
||||
}
|
||||
};
|
||||
|
||||
for (probe_path, reason) in probe_paths {
|
||||
if self.stat_sync(&probe_path) == Some(SloppyImportsFsEntry::File) {
|
||||
if let Ok(specifier) = ModuleSpecifier::from_file_path(probe_path) {
|
||||
match reason {
|
||||
SloppyImportsResolutionReason::JsToTs => {
|
||||
return Some(SloppyImportsResolution::JsToTs(specifier));
|
||||
}
|
||||
SloppyImportsResolutionReason::NoExtension => {
|
||||
return Some(SloppyImportsResolution::NoExtension(specifier));
|
||||
}
|
||||
SloppyImportsResolutionReason::Directory => {
|
||||
return Some(SloppyImportsResolution::Directory(specifier));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn stat_sync(&self, path: &Path) -> Option<SloppyImportsFsEntry> {
|
||||
path: &Path,
|
||||
) -> Option<deno_resolver::sloppy_imports::SloppyImportsFsEntry> {
|
||||
if let Some(cache) = &self.cache {
|
||||
if let Some(entry) = cache.get(path) {
|
||||
return *entry;
|
||||
}
|
||||
}
|
||||
|
||||
let entry = self
|
||||
.fs
|
||||
.stat_sync(path)
|
||||
.ok()
|
||||
.and_then(|stat| SloppyImportsFsEntry::from_fs_stat(&stat));
|
||||
let entry = self.fs.stat_sync(path).ok().and_then(|stat| {
|
||||
if stat.is_file {
|
||||
Some(deno_resolver::sloppy_imports::SloppyImportsFsEntry::File)
|
||||
} else if stat.is_directory {
|
||||
Some(deno_resolver::sloppy_imports::SloppyImportsFsEntry::Dir)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(cache) = &self.cache {
|
||||
cache.insert(path.to_owned(), entry);
|
||||
}
|
||||
entry
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use test_util::TestContext;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_unstable_sloppy_imports() {
|
||||
fn resolve(specifier: &ModuleSpecifier) -> Option<SloppyImportsResolution> {
|
||||
resolve_with_mode(specifier, ResolutionMode::Execution)
|
||||
}
|
||||
|
||||
fn resolve_types(
|
||||
specifier: &ModuleSpecifier,
|
||||
) -> Option<SloppyImportsResolution> {
|
||||
resolve_with_mode(specifier, ResolutionMode::Types)
|
||||
}
|
||||
|
||||
fn resolve_with_mode(
|
||||
specifier: &ModuleSpecifier,
|
||||
mode: ResolutionMode,
|
||||
) -> Option<SloppyImportsResolution> {
|
||||
SloppyImportsResolver::new(Arc::new(deno_fs::RealFs))
|
||||
.resolve(specifier, mode)
|
||||
}
|
||||
|
||||
let context = TestContext::default();
|
||||
let temp_dir = context.temp_dir().path();
|
||||
|
||||
// scenarios like resolving ./example.js to ./example.ts
|
||||
for (ext_from, ext_to) in [("js", "ts"), ("js", "tsx"), ("mjs", "mts")] {
|
||||
let ts_file = temp_dir.join(format!("file.{}", ext_to));
|
||||
ts_file.write("");
|
||||
assert_eq!(resolve(&ts_file.url_file()), None);
|
||||
assert_eq!(
|
||||
resolve(
|
||||
&temp_dir
|
||||
.url_dir()
|
||||
.join(&format!("file.{}", ext_from))
|
||||
.unwrap()
|
||||
),
|
||||
Some(SloppyImportsResolution::JsToTs(ts_file.url_file())),
|
||||
);
|
||||
ts_file.remove_file();
|
||||
}
|
||||
|
||||
// no extension scenarios
|
||||
for ext in ["js", "ts", "js", "tsx", "jsx", "mjs", "mts"] {
|
||||
let file = temp_dir.join(format!("file.{}", ext));
|
||||
file.write("");
|
||||
assert_eq!(
|
||||
resolve(
|
||||
&temp_dir
|
||||
.url_dir()
|
||||
.join("file") // no ext
|
||||
.unwrap()
|
||||
),
|
||||
Some(SloppyImportsResolution::NoExtension(file.url_file()))
|
||||
);
|
||||
file.remove_file();
|
||||
}
|
||||
|
||||
// .ts and .js exists, .js specified (goes to specified)
|
||||
{
|
||||
let ts_file = temp_dir.join("file.ts");
|
||||
ts_file.write("");
|
||||
let js_file = temp_dir.join("file.js");
|
||||
js_file.write("");
|
||||
assert_eq!(resolve(&js_file.url_file()), None);
|
||||
}
|
||||
|
||||
// only js exists, .js specified
|
||||
{
|
||||
let js_only_file = temp_dir.join("js_only.js");
|
||||
js_only_file.write("");
|
||||
assert_eq!(resolve(&js_only_file.url_file()), None);
|
||||
assert_eq!(resolve_types(&js_only_file.url_file()), None);
|
||||
}
|
||||
|
||||
// resolving a directory to an index file
|
||||
{
|
||||
let routes_dir = temp_dir.join("routes");
|
||||
routes_dir.create_dir_all();
|
||||
let index_file = routes_dir.join("index.ts");
|
||||
index_file.write("");
|
||||
assert_eq!(
|
||||
resolve(&routes_dir.url_file()),
|
||||
Some(SloppyImportsResolution::Directory(index_file.url_file())),
|
||||
);
|
||||
}
|
||||
|
||||
// both a directory and a file with specifier is present
|
||||
{
|
||||
let api_dir = temp_dir.join("api");
|
||||
api_dir.create_dir_all();
|
||||
let bar_file = api_dir.join("bar.ts");
|
||||
bar_file.write("");
|
||||
let api_file = temp_dir.join("api.ts");
|
||||
api_file.write("");
|
||||
assert_eq!(
|
||||
resolve(&api_dir.url_file()),
|
||||
Some(SloppyImportsResolution::NoExtension(api_file.url_file())),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sloppy_import_resolution_suggestion_message() {
|
||||
// directory
|
||||
assert_eq!(
|
||||
SloppyImportsResolution::Directory(
|
||||
ModuleSpecifier::parse("file:///dir/index.js").unwrap()
|
||||
)
|
||||
.as_suggestion_message(),
|
||||
"Maybe specify path to 'index.js' file in directory instead"
|
||||
);
|
||||
// no ext
|
||||
assert_eq!(
|
||||
SloppyImportsResolution::NoExtension(
|
||||
ModuleSpecifier::parse("file:///dir/index.mjs").unwrap()
|
||||
)
|
||||
.as_suggestion_message(),
|
||||
"Maybe add a '.mjs' extension"
|
||||
);
|
||||
// js to ts
|
||||
assert_eq!(
|
||||
SloppyImportsResolution::JsToTs(
|
||||
ModuleSpecifier::parse("file:///dir/index.mts").unwrap()
|
||||
)
|
||||
.as_suggestion_message(),
|
||||
"Maybe change the extension to '.mts'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"$id": "https://deno.land/x/deno/cli/schemas/config-file.v1.json",
|
||||
"$schema": "http://json-schema.org/draft-07/schema",
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"description": "A JSON representation of a Deno configuration file.",
|
||||
"required": [],
|
||||
"title": "Deno configuration file Schema",
|
||||
|
@ -9,6 +9,7 @@
|
|||
"compilerOptions": {
|
||||
"type": "object",
|
||||
"description": "Instructs the TypeScript compiler how to compile .ts files.",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"allowJs": {
|
||||
"description": "Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files.",
|
||||
|
@ -34,18 +35,32 @@
|
|||
"default": false,
|
||||
"markdownDescription": "Enable error reporting in type-checked JavaScript files.\n\nSee more: https://www.typescriptlang.org/tsconfig#checkJs"
|
||||
},
|
||||
"exactOptionalPropertyTypes": {
|
||||
"description": "Differentiate between undefined and not present when type checking",
|
||||
"emitDecoratorMetadata": {
|
||||
"description": "Emit design-type metadata for decorated declarations in source files.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "Differentiate between undefined and not present when type checking\n\nSee more: https://www.typescriptlang.org/tsconfig#exactOptionalPropertyTypes"
|
||||
"deprecated": true,
|
||||
"markdownDescription": "Emit design-type metadata for decorated declarations in source files.\n\nSee more: https://www.typescriptlang.org/tsconfig/#emitDecoratorMetadata"
|
||||
},
|
||||
"exactOptionalPropertyTypes": {
|
||||
"description": "Interpret optional property types as written, rather than adding 'undefined'.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "Interpret optional property types as written, rather than adding 'undefined'.\n\nSee more: https://www.typescriptlang.org/tsconfig#exactOptionalPropertyTypes"
|
||||
},
|
||||
"experimentalDecorators": {
|
||||
"description": "Enable experimental support for legacy experimental decorators.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"deprecated": true,
|
||||
"markdownDescription": "Enable experimental support for legacy experimental decorators.\n\nSee more: https://www.typescriptlang.org/tsconfig#experimentalDecorators"
|
||||
},
|
||||
"isolatedDeclarations": {
|
||||
"description": "Require sufficient annotation on exports so other tools can trivially generate declaration files.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "Require sufficient annotation on exports so other tools can trivially generate declaration files.\n\nSee more: https://www.typescriptlang.org/tsconfig/#isolatedDeclarations"
|
||||
},
|
||||
"jsx": {
|
||||
"description": "Specify what JSX code is generated.",
|
||||
"default": "react",
|
||||
|
@ -90,12 +105,6 @@
|
|||
},
|
||||
"markdownDescription": "Specify list of elements that should be exempt from being precompiled when the jsx `precompile` transform is used."
|
||||
},
|
||||
"keyofStringsOnly": {
|
||||
"description": "Make keyof only return strings instead of string, numbers or symbols. Legacy option.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "Make keyof only return strings instead of string, numbers or symbols. Legacy option.\n\nSee more: https://www.typescriptlang.org/tsconfig#keyofStringsOnly"
|
||||
},
|
||||
"lib": {
|
||||
"description": "Specify a set of bundled library declaration files that describe the target runtime environment.",
|
||||
"type": "array",
|
||||
|
@ -127,7 +136,7 @@
|
|||
"noImplicitOverride": {
|
||||
"description": "Ensure overriding members in derived classes are marked with an override modifier.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"default": true,
|
||||
"markdownDescription": "Ensure overriding members in derived classes are marked with an override modifier.\n\nSee more: https://www.typescriptlang.org/tsconfig#noImplicitOverride"
|
||||
},
|
||||
"noImplicitReturns": {
|
||||
|
@ -142,23 +151,17 @@
|
|||
"default": true,
|
||||
"markdownDescription": "Enable error reporting when `this` is given the type `any`.\n\nSee more: https://www.typescriptlang.org/tsconfig#noImplicitThis"
|
||||
},
|
||||
"noImplicitUseStrict": {
|
||||
"description": "Disable adding 'use strict' directives in emitted JavaScript files.",
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "Disable adding 'use strict' directives in emitted JavaScript files.\n\nSee more: https://www.typescriptlang.org/tsconfig#noImplicitUseStrict"
|
||||
},
|
||||
"noPropertyAccessFromIndexSignature": {
|
||||
"description": "Enforces using indexed accessors for keys declared using an indexed type.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "Enforces using indexed accessors for keys declared using an indexed type.\n\nSee more: https://www.typescriptlang.org/tsconfig#noPropertyAccessFromIndexSignature"
|
||||
},
|
||||
"noStrictGenericChecks": {
|
||||
"description": "Disable strict checking of generic signatures in function types.",
|
||||
"noUncheckedIndexedAccess": {
|
||||
"description": "Add `undefined` to a type when accessed using an index.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "Disable strict checking of generic signatures in function types.\n\nSee more: https://www.typescriptlang.org/tsconfig#noStrictGenericChecks"
|
||||
"markdownDescription": "Add `undefined` to a type when accessed using an index.\n\nSee more: https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess"
|
||||
},
|
||||
"noUnusedLocals": {
|
||||
"description": "Enable error reporting when a local variables aren't read.",
|
||||
|
@ -172,12 +175,6 @@
|
|||
"default": false,
|
||||
"markdownDescription": "Raise an error when a function parameter isn't read\n\nSee more: https://www.typescriptlang.org/tsconfig#noUnusedParameters"
|
||||
},
|
||||
"noUncheckedIndexedAccess": {
|
||||
"description": "Add `undefined` to a type when accessed using an index.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "Add `undefined` to a type when accessed using an index.\n\nSee more: https://www.typescriptlang.org/tsconfig#noUncheckedIndexedAccess"
|
||||
},
|
||||
"strict": {
|
||||
"description": "Enable all strict type checking options.",
|
||||
"type": "boolean",
|
||||
|
@ -190,41 +187,49 @@
|
|||
"default": true,
|
||||
"markdownDescription": "Check that the arguments for `bind`, `call`, and `apply` methods match the original function.\n\nSee more: https://www.typescriptlang.org/tsconfig#strictBindCallApply"
|
||||
},
|
||||
"strictBuiltinIteratorReturn": {
|
||||
"description": "Built-in iterators are instantiated with a `TReturn` type of undefined instead of `any`.",
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "Built-in iterators are instantiated with a `TReturn` type of undefined instead of `any`.\n\nSee more: https://www.typescriptlang.org/tsconfig/#strictBuiltinIteratorReturn"
|
||||
},
|
||||
"strictFunctionTypes": {
|
||||
"description": "When assigning functions, check to ensure parameters and the return values are subtype-compatible.",
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "When assigning functions, check to ensure parameters and the return values are subtype-compatible.\n\nSee more: https://www.typescriptlang.org/tsconfig#strictFunctionTypes"
|
||||
},
|
||||
"strictPropertyInitialization": {
|
||||
"description": "Check for class properties that are declared but not set in the constructor.",
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "Check for class properties that are declared but not set in the constructor.\n\nSee more: https://www.typescriptlang.org/tsconfig#strictPropertyInitialization"
|
||||
},
|
||||
"strictNullChecks": {
|
||||
"description": "When type checking, take into account `null` and `undefined`.",
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"markdownDescription": "When type checking, take into account `null` and `undefined`.\n\nSee more: https://www.typescriptlang.org/tsconfig#strictNullChecks"
|
||||
},
|
||||
"suppressExcessPropertyErrors": {
|
||||
"description": "Disable reporting of excess property errors during the creation of object literals.",
|
||||
"strictPropertyInitialization": {
|
||||
"description": "Check for class properties that are declared but not set in the constructor.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "Disable reporting of excess property errors during the creation of object literals.\n\nSee more: https://www.typescriptlang.org/tsconfig#suppressExcessPropertyErrors"
|
||||
"default": true,
|
||||
"markdownDescription": "Check for class properties that are declared but not set in the constructor.\n\nSee more: https://www.typescriptlang.org/tsconfig#strictPropertyInitialization"
|
||||
},
|
||||
"suppressImplicitAnyIndexErrors": {
|
||||
"description": "Suppress `noImplicitAny` errors when indexing objects that lack index signatures.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "Suppress `noImplicitAny` errors when indexing objects that lack index signatures.\n\nSee more: https://www.typescriptlang.org/tsconfig#suppressImplicitAnyIndexErrors"
|
||||
"types": {
|
||||
"description": "Specify type package names to be included without being referenced in a source file.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"markdownDescription": "Specify type package names to be included without being referenced in a source file.\n\nSee more: https://www.typescriptlang.org/tsconfig/#types"
|
||||
},
|
||||
"useUnknownInCatchVariables": {
|
||||
"description": "Default catch clause variables as `unknown` instead of `any`.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"default": true,
|
||||
"markdownDescription": "Default catch clause variables as `unknown` instead of `any`.\n\nSee more: https://www.typescriptlang.org/tsconfig#useUnknownInCatchVariables"
|
||||
},
|
||||
"verbatimModuleSyntax": {
|
||||
"description": "Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"markdownDescription": "Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting.\n\nSee more: https://www.typescriptlang.org/tsconfig/#verbatimModuleSyntax"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -279,25 +284,6 @@
|
|||
"type": "string"
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"include": {
|
||||
"type": "array",
|
||||
"description": "List of files, directories or globs that will be linted.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"exclude": {
|
||||
"type": "array",
|
||||
"description": "List of files, directories or globs that will not be linted.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
@ -355,25 +341,6 @@
|
|||
"type": "string"
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"include": {
|
||||
"type": "array",
|
||||
"description": "List of files, directories or globs that will be formatted.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"exclude": {
|
||||
"type": "array",
|
||||
"description": "List of files, directories or globs that will not be formatted.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"useTabs": {
|
||||
"description": "Whether to use tabs (true) or spaces (false) for indentation.",
|
||||
"type": "boolean",
|
||||
|
@ -442,8 +409,18 @@
|
|||
}
|
||||
},
|
||||
"nodeModulesDir": {
|
||||
"description": "Enables or disables the use of a local node_modules folder for npm packages. Alternatively, use the `--node-modules-dir` flag or override the config via `--node-modules-dir=false`. Requires Deno 1.34 or later.",
|
||||
"type": "boolean"
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Sets the node_modules management mode for npm packages. Alternatively, use the `--node-modules-dir=<MODE>` flag. Requires Deno 2.0-rc.1 or later.",
|
||||
"default": "none",
|
||||
"enum": ["auto", "manual", "none"]
|
||||
},
|
||||
{
|
||||
"description": "Enables or disables the use of a local node_modules folder for npm packages. Alternatively, use the `--node-modules-dir` flag or override the config via `--node-modules-dir=false`. Requires Deno 1.34 or later.",
|
||||
"type": "boolean",
|
||||
"deprecated": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"vendor": {
|
||||
"description": "Enables or disables the use of a local vendor folder as a local cache for remote modules and node_modules folder for npm packages. Alternatively, use the `--vendor` flag or override the config via `--vendor=false`. Requires Deno 1.36.1 or later.",
|
||||
|
@ -477,25 +454,6 @@
|
|||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"include": {
|
||||
"type": "array",
|
||||
"description": "List of files, directories or globs that will be searched for tests.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"exclude": {
|
||||
"type": "array",
|
||||
"description": "List of files, directories or globs that will not be searched for tests.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -536,25 +494,6 @@
|
|||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"files": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"include": {
|
||||
"type": "array",
|
||||
"description": "List of files, directories or globs that will be searched for benchmarks.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"exclude": {
|
||||
"type": "array",
|
||||
"description": "List of files, directories or globs that will not be searched for benchmarks.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -589,6 +528,7 @@
|
|||
"bare-node-builtins",
|
||||
"byonm",
|
||||
"cron",
|
||||
"detect-cjs",
|
||||
"ffi",
|
||||
"fs",
|
||||
"http",
|
||||
|
|
|
@ -83,14 +83,6 @@
|
|||
"type": "string",
|
||||
"description": "The checksum of the local source file. This can be used to validate if the current on disk version matches the version described here."
|
||||
},
|
||||
"emit": {
|
||||
"type": "string",
|
||||
"description": "The path to an emitted version of the module, if the module requires transpilation to be loaded into the Deno runtime."
|
||||
},
|
||||
"map": {
|
||||
"type": "string",
|
||||
"description": "The path to an optionally emitted source map between the original and emitted version of the file."
|
||||
},
|
||||
"error": {
|
||||
"type": "string",
|
||||
"description": "If when resolving the module, Deno encountered an error and the module is unavailable, the text of that error will be indicated here."
|
||||
|
|
|
@ -427,13 +427,9 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
binary_name
|
||||
)
|
||||
}
|
||||
ReleaseChannel::Stable => {
|
||||
_ => {
|
||||
format!("release/v{}/{}", env!("CARGO_PKG_VERSION"), binary_name)
|
||||
}
|
||||
_ => bail!(
|
||||
"`deno compile` current doesn't support {} release channel",
|
||||
crate::version::DENO_VERSION_INFO.release_channel.name()
|
||||
),
|
||||
};
|
||||
|
||||
let download_directory = self.deno_dir.dl_folder_path();
|
||||
|
@ -472,7 +468,11 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
self
|
||||
.http_client_provider
|
||||
.get_or_create()?
|
||||
.download_with_progress(download_url.parse()?, None, &progress)
|
||||
.download_with_progress_and_retries(
|
||||
download_url.parse()?,
|
||||
None,
|
||||
&progress,
|
||||
)
|
||||
.await?
|
||||
};
|
||||
let bytes = match maybe_bytes {
|
||||
|
@ -624,8 +624,9 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
},
|
||||
node_modules,
|
||||
unstable_config: UnstableConfig {
|
||||
legacy_flag_enabled: cli_options.legacy_unstable_flag(),
|
||||
legacy_flag_enabled: false,
|
||||
bare_node_builtins: cli_options.unstable_bare_node_builtins(),
|
||||
detect_cjs: cli_options.unstable_detect_cjs(),
|
||||
sloppy_imports: cli_options.unstable_sloppy_imports(),
|
||||
features: cli_options.unstable_features(),
|
||||
},
|
||||
|
|
|
@ -102,7 +102,7 @@ impl FileSystem for DenoCompileFileSystem {
|
|||
&self,
|
||||
path: &Path,
|
||||
recursive: bool,
|
||||
mode: u32,
|
||||
mode: Option<u32>,
|
||||
) -> FsResult<()> {
|
||||
self.error_if_in_vfs(path)?;
|
||||
RealFs.mkdir_sync(path, recursive, mode)
|
||||
|
@ -111,7 +111,7 @@ impl FileSystem for DenoCompileFileSystem {
|
|||
&self,
|
||||
path: PathBuf,
|
||||
recursive: bool,
|
||||
mode: u32,
|
||||
mode: Option<u32>,
|
||||
) -> FsResult<()> {
|
||||
self.error_if_in_vfs(&path)?;
|
||||
RealFs.mkdir_async(path, recursive, mode).await
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#![allow(unused_imports)]
|
||||
|
||||
use deno_ast::MediaType;
|
||||
use deno_cache_dir::npm::NpmCacheDir;
|
||||
use deno_config::workspace::MappedResolution;
|
||||
use deno_config::workspace::MappedResolutionError;
|
||||
use deno_config::workspace::ResolverWorkspaceJsrPackage;
|
||||
|
@ -32,6 +33,8 @@ 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::permissions::RuntimePermissionDescriptorParser;
|
||||
use deno_runtime::WorkerExecutionMode;
|
||||
use deno_runtime::WorkerLogLevel;
|
||||
use deno_semver::npm::NpmPackageReqReference;
|
||||
|
@ -53,15 +56,16 @@ use crate::args::StorageKeyResolver;
|
|||
use crate::cache::Caches;
|
||||
use crate::cache::DenoDirProvider;
|
||||
use crate::cache::NodeAnalysisCache;
|
||||
use crate::cache::RealDenoCacheEnv;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
use crate::node::CliCjsCodeAnalyzer;
|
||||
use crate::npm::create_cli_npm_resolver;
|
||||
use crate::npm::CliNpmResolverByonmCreateOptions;
|
||||
use crate::npm::CliByonmNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||
use crate::npm::NpmCacheDir;
|
||||
use crate::resolver::CjsResolutionStore;
|
||||
use crate::resolver::CliDenoResolverFs;
|
||||
use crate::resolver::CliNodeResolver;
|
||||
use crate::resolver::NpmModuleLoader;
|
||||
use crate::util::progress_bar::ProgressBar;
|
||||
|
@ -128,8 +132,6 @@ struct SharedModuleLoaderState {
|
|||
#[derive(Clone)]
|
||||
struct EmbeddedModuleLoader {
|
||||
shared: Arc<SharedModuleLoaderState>,
|
||||
root_permissions: PermissionsContainer,
|
||||
dynamic_permissions: PermissionsContainer,
|
||||
}
|
||||
|
||||
pub const MODULE_NOT_FOUND: &str = "Module not found";
|
||||
|
@ -400,28 +402,23 @@ struct StandaloneModuleLoaderFactory {
|
|||
impl ModuleLoaderFactory for StandaloneModuleLoaderFactory {
|
||||
fn create_for_main(
|
||||
&self,
|
||||
root_permissions: PermissionsContainer,
|
||||
dynamic_permissions: PermissionsContainer,
|
||||
_root_permissions: PermissionsContainer,
|
||||
) -> ModuleLoaderAndSourceMapGetter {
|
||||
ModuleLoaderAndSourceMapGetter {
|
||||
module_loader: Rc::new(EmbeddedModuleLoader {
|
||||
shared: self.shared.clone(),
|
||||
root_permissions,
|
||||
dynamic_permissions,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_for_worker(
|
||||
&self,
|
||||
root_permissions: PermissionsContainer,
|
||||
dynamic_permissions: PermissionsContainer,
|
||||
_parent_permissions: PermissionsContainer,
|
||||
_permissions: PermissionsContainer,
|
||||
) -> ModuleLoaderAndSourceMapGetter {
|
||||
ModuleLoaderAndSourceMapGetter {
|
||||
module_loader: Rc::new(EmbeddedModuleLoader {
|
||||
shared: self.shared.clone(),
|
||||
root_permissions,
|
||||
dynamic_permissions,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -449,7 +446,6 @@ pub async fn run(
|
|||
let current_exe_path = std::env::current_exe().unwrap();
|
||||
let current_exe_name =
|
||||
current_exe_path.file_name().unwrap().to_string_lossy();
|
||||
let maybe_cwd = std::env::current_dir().ok();
|
||||
let deno_dir_provider = Arc::new(DenoDirProvider::new(None));
|
||||
let root_cert_store_provider = Arc::new(StandaloneRootCertStoreProvider {
|
||||
ca_stores: metadata.ca_stores,
|
||||
|
@ -470,6 +466,7 @@ pub async fn run(
|
|||
let main_module = root_dir_url.join(&metadata.entrypoint_key).unwrap();
|
||||
let root_node_modules_path = root_path.join("node_modules");
|
||||
let npm_cache_dir = NpmCacheDir::new(
|
||||
&RealDenoCacheEnv,
|
||||
root_node_modules_path.clone(),
|
||||
vec![npm_registry_url.clone()],
|
||||
);
|
||||
|
@ -534,8 +531,8 @@ pub async fn run(
|
|||
let fs = Arc::new(DenoCompileFileSystem::new(vfs))
|
||||
as Arc<dyn deno_fs::FileSystem>;
|
||||
let npm_resolver = create_cli_npm_resolver(
|
||||
CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions {
|
||||
fs: fs.clone(),
|
||||
CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions {
|
||||
fs: CliDenoResolverFs(fs.clone()),
|
||||
root_node_modules_dir,
|
||||
}),
|
||||
)
|
||||
|
@ -579,8 +576,18 @@ pub async fn run(
|
|||
let cjs_resolutions = Arc::new(CjsResolutionStore::default());
|
||||
let cache_db = Caches::new(deno_dir_provider.clone());
|
||||
let node_analysis_cache = NodeAnalysisCache::new(cache_db.node_analysis_db());
|
||||
let cjs_esm_code_analyzer =
|
||||
CliCjsCodeAnalyzer::new(node_analysis_cache, fs.clone());
|
||||
let cli_node_resolver = Arc::new(CliNodeResolver::new(
|
||||
cjs_resolutions.clone(),
|
||||
fs.clone(),
|
||||
node_resolver.clone(),
|
||||
npm_resolver.clone(),
|
||||
));
|
||||
let cjs_esm_code_analyzer = CliCjsCodeAnalyzer::new(
|
||||
node_analysis_cache,
|
||||
fs.clone(),
|
||||
cli_node_resolver.clone(),
|
||||
None,
|
||||
);
|
||||
let node_code_translator = Arc::new(NodeCodeTranslator::new(
|
||||
cjs_esm_code_analyzer,
|
||||
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
|
||||
|
@ -636,12 +643,6 @@ pub async fn run(
|
|||
metadata.workspace_resolver.pkg_json_resolution,
|
||||
)
|
||||
};
|
||||
let cli_node_resolver = Arc::new(CliNodeResolver::new(
|
||||
cjs_resolutions.clone(),
|
||||
fs.clone(),
|
||||
node_resolver.clone(),
|
||||
npm_resolver.clone(),
|
||||
));
|
||||
let module_loader_factory = StandaloneModuleLoaderFactory {
|
||||
shared: Arc::new(SharedModuleLoaderState {
|
||||
eszip: WorkspaceEszip {
|
||||
|
@ -651,7 +652,7 @@ pub async fn run(
|
|||
workspace_resolver,
|
||||
node_resolver: cli_node_resolver.clone(),
|
||||
npm_module_loader: Arc::new(NpmModuleLoader::new(
|
||||
cjs_resolutions,
|
||||
cjs_resolutions.clone(),
|
||||
node_code_translator,
|
||||
fs.clone(),
|
||||
cli_node_resolver,
|
||||
|
@ -661,7 +662,7 @@ pub async fn run(
|
|||
|
||||
let permissions = {
|
||||
let mut permissions =
|
||||
metadata.permissions.to_options(maybe_cwd.as_deref())?;
|
||||
metadata.permissions.to_options(/* cli_arg_urls */ &[]);
|
||||
// if running with an npm vfs, grant read access to it
|
||||
if let Some(vfs_root) = maybe_vfs_root {
|
||||
match &mut permissions.allow_read {
|
||||
|
@ -669,25 +670,24 @@ pub async fn run(
|
|||
// do nothing, already granted
|
||||
}
|
||||
Some(vec) => {
|
||||
vec.push(vfs_root);
|
||||
vec.push(vfs_root.to_string_lossy().to_string());
|
||||
}
|
||||
None => {
|
||||
permissions.allow_read = Some(vec![vfs_root]);
|
||||
permissions.allow_read =
|
||||
Some(vec![vfs_root.to_string_lossy().to_string()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PermissionsContainer::new(Permissions::from_options(&permissions)?)
|
||||
let desc_parser =
|
||||
Arc::new(RuntimePermissionDescriptorParser::new(fs.clone()));
|
||||
let permissions =
|
||||
Permissions::from_options(desc_parser.as_ref(), &permissions)?;
|
||||
PermissionsContainer::new(desc_parser, permissions)
|
||||
};
|
||||
let feature_checker = Arc::new({
|
||||
let mut checker = FeatureChecker::default();
|
||||
checker.set_exit_cb(Box::new(crate::unstable_exit_cb));
|
||||
// TODO(bartlomieju): enable, once we deprecate `--unstable` in favor
|
||||
// of granular --unstable-* flags.
|
||||
// feature_checker.set_warn_cb(Box::new(crate::unstable_warn_cb));
|
||||
if metadata.unstable_config.legacy_flag_enabled {
|
||||
checker.enable_legacy_unstable();
|
||||
}
|
||||
for feature in metadata.unstable_config.features {
|
||||
// `metadata` is valid for the whole lifetime of the program, so we
|
||||
// can leak the string here.
|
||||
|
@ -696,20 +696,22 @@ pub async fn run(
|
|||
checker
|
||||
});
|
||||
let worker_factory = CliMainWorkerFactory::new(
|
||||
StorageKeyResolver::empty(),
|
||||
crate::args::DenoSubcommand::Run(Default::default()),
|
||||
npm_resolver,
|
||||
node_resolver,
|
||||
Default::default(),
|
||||
Box::new(module_loader_factory),
|
||||
root_cert_store_provider,
|
||||
Arc::new(BlobStore::default()),
|
||||
cjs_resolutions,
|
||||
// Code cache is not supported for standalone binary yet.
|
||||
None,
|
||||
feature_checker,
|
||||
fs,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
feature_checker,
|
||||
// Code cache is not supported for standalone binary yet.
|
||||
None,
|
||||
Box::new(module_loader_factory),
|
||||
node_resolver,
|
||||
npm_resolver,
|
||||
root_cert_store_provider,
|
||||
permissions,
|
||||
StorageKeyResolver::empty(),
|
||||
crate::args::DenoSubcommand::Run(Default::default()),
|
||||
CliMainWorkerOptions {
|
||||
argv: metadata.argv,
|
||||
log_level: WorkerLogLevel::Info,
|
||||
|
@ -733,22 +735,22 @@ pub async fn run(
|
|||
seed: metadata.seed,
|
||||
unsafely_ignore_certificate_errors: metadata
|
||||
.unsafely_ignore_certificate_errors,
|
||||
unstable: metadata.unstable_config.legacy_flag_enabled,
|
||||
create_hmr_runner: None,
|
||||
create_coverage_collector: None,
|
||||
node_ipc: None,
|
||||
serve_port: None,
|
||||
serve_host: None,
|
||||
unstable_detect_cjs: metadata.unstable_config.detect_cjs,
|
||||
},
|
||||
);
|
||||
|
||||
// Initialize v8 once from the main thread.
|
||||
v8_set_flags(construct_v8_flags(&[], &metadata.v8_flags, vec![]));
|
||||
// TODO(bartlomieju): remove last argument in Deno 2.
|
||||
// TODO(bartlomieju): remove last argument once Deploy no longer needs it
|
||||
deno_core::JsRuntime::init_platform(None, true);
|
||||
|
||||
let mut worker = worker_factory
|
||||
.create_main_worker(WorkerExecutionMode::Run, main_module, permissions)
|
||||
.create_main_worker(WorkerExecutionMode::Run, main_module)
|
||||
.await?;
|
||||
|
||||
let exit_code = worker.run().await?;
|
||||
|
|
|
@ -16,8 +16,11 @@ use deno_task_shell::ExecutableCommand;
|
|||
use deno_task_shell::ExecuteResult;
|
||||
use deno_task_shell::ShellCommand;
|
||||
use deno_task_shell::ShellCommandContext;
|
||||
use deno_task_shell::ShellPipeReader;
|
||||
use deno_task_shell::ShellPipeWriter;
|
||||
use lazy_regex::Lazy;
|
||||
use regex::Regex;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio::task::LocalSet;
|
||||
|
||||
use crate::npm::CliNpmResolver;
|
||||
|
@ -36,6 +39,35 @@ pub fn get_script_with_args(script: &str, argv: &[String]) -> String {
|
|||
script.trim().to_owned()
|
||||
}
|
||||
|
||||
pub struct TaskStdio(Option<ShellPipeReader>, ShellPipeWriter);
|
||||
|
||||
impl TaskStdio {
|
||||
pub fn stdout() -> Self {
|
||||
Self(None, ShellPipeWriter::stdout())
|
||||
}
|
||||
pub fn stderr() -> Self {
|
||||
Self(None, ShellPipeWriter::stderr())
|
||||
}
|
||||
pub fn piped() -> Self {
|
||||
let (r, w) = deno_task_shell::pipe();
|
||||
Self(Some(r), w)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TaskIo {
|
||||
pub stdout: TaskStdio,
|
||||
pub stderr: TaskStdio,
|
||||
}
|
||||
|
||||
impl Default for TaskIo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
stderr: TaskStdio::stderr(),
|
||||
stdout: TaskStdio::stdout(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RunTaskOptions<'a> {
|
||||
pub task_name: &'a str,
|
||||
pub script: &'a str,
|
||||
|
@ -45,24 +77,69 @@ pub struct RunTaskOptions<'a> {
|
|||
pub argv: &'a [String],
|
||||
pub custom_commands: HashMap<String, Rc<dyn ShellCommand>>,
|
||||
pub root_node_modules_dir: Option<&'a Path>,
|
||||
pub stdio: Option<TaskIo>,
|
||||
}
|
||||
|
||||
pub type TaskCustomCommands = HashMap<String, Rc<dyn ShellCommand>>;
|
||||
|
||||
pub async fn run_task(opts: RunTaskOptions<'_>) -> Result<i32, AnyError> {
|
||||
pub struct TaskResult {
|
||||
pub exit_code: i32,
|
||||
pub stdout: Option<Vec<u8>>,
|
||||
pub stderr: Option<Vec<u8>>,
|
||||
}
|
||||
|
||||
pub async fn run_task(
|
||||
opts: RunTaskOptions<'_>,
|
||||
) -> Result<TaskResult, AnyError> {
|
||||
let script = get_script_with_args(opts.script, opts.argv);
|
||||
let seq_list = deno_task_shell::parser::parse(&script)
|
||||
.with_context(|| format!("Error parsing script '{}'.", opts.task_name))?;
|
||||
let env_vars =
|
||||
prepare_env_vars(opts.env_vars, opts.init_cwd, opts.root_node_modules_dir);
|
||||
let state =
|
||||
deno_task_shell::ShellState::new(env_vars, opts.cwd, opts.custom_commands);
|
||||
let stdio = opts.stdio.unwrap_or_default();
|
||||
let (
|
||||
TaskStdio(stdout_read, stdout_write),
|
||||
TaskStdio(stderr_read, stderr_write),
|
||||
) = (stdio.stdout, stdio.stderr);
|
||||
|
||||
fn read(reader: ShellPipeReader) -> JoinHandle<Result<Vec<u8>, AnyError>> {
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let mut buf = Vec::new();
|
||||
reader.pipe_to(&mut buf)?;
|
||||
Ok(buf)
|
||||
})
|
||||
}
|
||||
|
||||
let stdout = stdout_read.map(read);
|
||||
let stderr = stderr_read.map(read);
|
||||
|
||||
let local = LocalSet::new();
|
||||
let future = deno_task_shell::execute(
|
||||
seq_list,
|
||||
env_vars,
|
||||
opts.cwd,
|
||||
opts.custom_commands,
|
||||
);
|
||||
Ok(local.run_until(future).await)
|
||||
let future = async move {
|
||||
let exit_code = deno_task_shell::execute_with_pipes(
|
||||
seq_list,
|
||||
state,
|
||||
ShellPipeReader::stdin(),
|
||||
stdout_write,
|
||||
stderr_write,
|
||||
)
|
||||
.await;
|
||||
Ok::<_, AnyError>(TaskResult {
|
||||
exit_code,
|
||||
stdout: if let Some(stdout) = stdout {
|
||||
Some(stdout.await??)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
stderr: if let Some(stderr) = stderr {
|
||||
Some(stderr.await??)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
};
|
||||
local.run_until(future).await
|
||||
}
|
||||
|
||||
fn prepare_env_vars(
|
||||
|
|
|
@ -30,6 +30,7 @@ use deno_core::ModuleSpecifier;
|
|||
use deno_core::PollEventLoopOptions;
|
||||
use deno_runtime::deno_permissions::Permissions;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use deno_runtime::permissions::RuntimePermissionDescriptorParser;
|
||||
use deno_runtime::tokio_util::create_and_run_current_thread;
|
||||
use deno_runtime::WorkerExecutionMode;
|
||||
use indexmap::IndexMap;
|
||||
|
@ -144,14 +145,14 @@ fn create_reporter(
|
|||
/// Run a single specifier as an executable bench module.
|
||||
async fn bench_specifier(
|
||||
worker_factory: Arc<CliMainWorkerFactory>,
|
||||
permissions: Permissions,
|
||||
permissions_container: PermissionsContainer,
|
||||
specifier: ModuleSpecifier,
|
||||
sender: UnboundedSender<BenchEvent>,
|
||||
filter: TestFilter,
|
||||
) -> Result<(), AnyError> {
|
||||
match bench_specifier_inner(
|
||||
worker_factory,
|
||||
permissions,
|
||||
permissions_container,
|
||||
specifier.clone(),
|
||||
&sender,
|
||||
filter,
|
||||
|
@ -176,7 +177,7 @@ async fn bench_specifier(
|
|||
/// Run a single specifier as an executable bench module.
|
||||
async fn bench_specifier_inner(
|
||||
worker_factory: Arc<CliMainWorkerFactory>,
|
||||
permissions: Permissions,
|
||||
permissions_container: PermissionsContainer,
|
||||
specifier: ModuleSpecifier,
|
||||
sender: &UnboundedSender<BenchEvent>,
|
||||
filter: TestFilter,
|
||||
|
@ -185,7 +186,7 @@ async fn bench_specifier_inner(
|
|||
.create_custom_worker(
|
||||
WorkerExecutionMode::Bench,
|
||||
specifier.clone(),
|
||||
PermissionsContainer::new(permissions),
|
||||
permissions_container,
|
||||
vec![ops::bench::deno_bench::init_ops(sender.clone())],
|
||||
Default::default(),
|
||||
)
|
||||
|
@ -264,6 +265,7 @@ async fn bench_specifier_inner(
|
|||
async fn bench_specifiers(
|
||||
worker_factory: Arc<CliMainWorkerFactory>,
|
||||
permissions: &Permissions,
|
||||
permissions_desc_parser: &Arc<RuntimePermissionDescriptorParser>,
|
||||
specifiers: Vec<ModuleSpecifier>,
|
||||
options: BenchSpecifierOptions,
|
||||
) -> Result<(), AnyError> {
|
||||
|
@ -273,13 +275,16 @@ async fn bench_specifiers(
|
|||
|
||||
let join_handles = specifiers.into_iter().map(move |specifier| {
|
||||
let worker_factory = worker_factory.clone();
|
||||
let permissions = permissions.clone();
|
||||
let permissions_container = PermissionsContainer::new(
|
||||
permissions_desc_parser.clone(),
|
||||
permissions.clone(),
|
||||
);
|
||||
let sender = sender.clone();
|
||||
let options = option_for_handles.clone();
|
||||
spawn_blocking(move || {
|
||||
let future = bench_specifier(
|
||||
worker_factory,
|
||||
permissions,
|
||||
permissions_container,
|
||||
specifier,
|
||||
sender,
|
||||
options.filter,
|
||||
|
@ -410,8 +415,11 @@ pub async fn run_benchmarks(
|
|||
// Various bench 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 members_with_bench_options =
|
||||
cli_options.resolve_bench_options_for_members(&bench_flags)?;
|
||||
|
@ -434,7 +442,9 @@ pub async fn run_benchmarks(
|
|||
}
|
||||
|
||||
let main_graph_container = factory.main_module_graph_container().await?;
|
||||
main_graph_container.check_specifiers(&specifiers).await?;
|
||||
main_graph_container
|
||||
.check_specifiers(&specifiers, cli_options.ext_flag().as_ref())
|
||||
.await?;
|
||||
|
||||
if workspace_bench_options.no_run {
|
||||
return Ok(());
|
||||
|
@ -446,6 +456,7 @@ pub async fn run_benchmarks(
|
|||
bench_specifiers(
|
||||
worker_factory,
|
||||
&permissions,
|
||||
&permission_desc_parser,
|
||||
specifiers,
|
||||
BenchSpecifierOptions {
|
||||
filter: TestFilter::from_flag(&workspace_bench_options.filter),
|
||||
|
@ -519,8 +530,11 @@ pub async fn run_benchmarks_with_watch(
|
|||
// Various bench 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 graph = module_graph_creator
|
||||
.create_graph(graph_kind, collected_bench_modules.clone())
|
||||
|
@ -557,7 +571,7 @@ pub async fn run_benchmarks_with_watch(
|
|||
factory
|
||||
.main_module_graph_container()
|
||||
.await?
|
||||
.check_specifiers(&specifiers)
|
||||
.check_specifiers(&specifiers, cli_options.ext_flag().as_ref())
|
||||
.await?;
|
||||
|
||||
if workspace_bench_options.no_run {
|
||||
|
@ -568,6 +582,7 @@ pub async fn run_benchmarks_with_watch(
|
|||
bench_specifiers(
|
||||
worker_factory,
|
||||
&permissions,
|
||||
&permission_desc_parser,
|
||||
specifiers,
|
||||
BenchSpecifierOptions {
|
||||
filter: TestFilter::from_flag(&workspace_bench_options.filter),
|
||||
|
|
|
@ -18,8 +18,11 @@ pub trait BenchReporter {
|
|||
fn report_uncaught_error(&mut self, origin: &str, error: Box<JsError>);
|
||||
}
|
||||
|
||||
const JSON_SCHEMA_VERSION: u8 = 1;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct JsonReporterOutput {
|
||||
version: u8,
|
||||
runtime: String,
|
||||
cpu: String,
|
||||
benches: Vec<JsonReporterBench>,
|
||||
|
@ -28,6 +31,7 @@ struct JsonReporterOutput {
|
|||
impl Default for JsonReporterOutput {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
version: JSON_SCHEMA_VERSION,
|
||||
runtime: format!(
|
||||
"{} {}",
|
||||
version::DENO_VERSION_INFO.user_agent,
|
||||
|
|
|
@ -15,7 +15,9 @@ use once_cell::sync::Lazy;
|
|||
use regex::Regex;
|
||||
|
||||
use crate::args::check_warn_tsconfig;
|
||||
use crate::args::CheckFlags;
|
||||
use crate::args::CliOptions;
|
||||
use crate::args::Flags;
|
||||
use crate::args::TsConfig;
|
||||
use crate::args::TsConfigType;
|
||||
use crate::args::TsTypeLib;
|
||||
|
@ -24,13 +26,58 @@ use crate::cache::CacheDBHash;
|
|||
use crate::cache::Caches;
|
||||
use crate::cache::FastInsecureHasher;
|
||||
use crate::cache::TypeCheckCache;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::graph_util::BuildFastCheckGraphOptions;
|
||||
use crate::graph_util::ModuleGraphBuilder;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::tsc;
|
||||
use crate::tsc::Diagnostics;
|
||||
use crate::util::extract;
|
||||
use crate::util::path::to_percent_decoded_str;
|
||||
|
||||
pub async fn check(
|
||||
flags: Arc<Flags>,
|
||||
check_flags: CheckFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let factory = CliFactory::from_flags(flags);
|
||||
|
||||
let main_graph_container = factory.main_module_graph_container().await?;
|
||||
|
||||
let specifiers =
|
||||
main_graph_container.collect_specifiers(&check_flags.files)?;
|
||||
if specifiers.is_empty() {
|
||||
log::warn!("{} No matching files found.", colors::yellow("Warning"));
|
||||
}
|
||||
|
||||
let specifiers_for_typecheck = if check_flags.doc || check_flags.doc_only {
|
||||
let file_fetcher = factory.file_fetcher()?;
|
||||
let root_permissions = factory.root_permissions_container()?;
|
||||
|
||||
let mut specifiers_for_typecheck = if check_flags.doc {
|
||||
specifiers.clone()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
for s in specifiers {
|
||||
let file = file_fetcher.fetch(&s, root_permissions).await?;
|
||||
let snippet_files = extract::extract_snippet_files(file)?;
|
||||
for snippet_file in snippet_files {
|
||||
specifiers_for_typecheck.push(snippet_file.specifier.clone());
|
||||
file_fetcher.insert_memory_files(snippet_file);
|
||||
}
|
||||
}
|
||||
|
||||
specifiers_for_typecheck
|
||||
} else {
|
||||
specifiers
|
||||
};
|
||||
|
||||
main_graph_container
|
||||
.check_specifiers(&specifiers_for_typecheck, None)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Options for performing a check of a module graph. Note that the decision to
|
||||
/// emit or not is determined by the `ts_config` settings.
|
||||
pub struct CheckOptions {
|
||||
|
@ -84,7 +131,9 @@ impl TypeChecker {
|
|||
graph: ModuleGraph,
|
||||
options: CheckOptions,
|
||||
) -> Result<Arc<ModuleGraph>, AnyError> {
|
||||
let (graph, diagnostics) = self.check_diagnostics(graph, options).await?;
|
||||
let (graph, mut diagnostics) =
|
||||
self.check_diagnostics(graph, options).await?;
|
||||
diagnostics.emit_warnings();
|
||||
if diagnostics.is_empty() {
|
||||
Ok(graph)
|
||||
} else {
|
||||
|
|
|
@ -54,6 +54,16 @@ pub async fn compile(
|
|||
);
|
||||
}
|
||||
|
||||
if cli_options.unstable_detect_cjs() {
|
||||
log::warn!(
|
||||
concat!(
|
||||
"{} --unstable-detect-cjs is not properly supported in deno compile. ",
|
||||
"The compiled executable may encounter runtime errors.",
|
||||
),
|
||||
crate::colors::yellow("Warning"),
|
||||
);
|
||||
}
|
||||
|
||||
let output_path = resolve_compile_executable_output_path(
|
||||
http_client,
|
||||
&compile_flags,
|
||||
|
@ -135,7 +145,7 @@ pub async fn compile(
|
|||
file,
|
||||
eszip,
|
||||
root_dir_url,
|
||||
&module_specifier,
|
||||
module_specifier,
|
||||
&compile_flags,
|
||||
cli_options,
|
||||
)
|
||||
|
|
|
@ -452,6 +452,11 @@ fn filter_coverages(
|
|||
let exclude: Vec<Regex> =
|
||||
exclude.iter().map(|e| Regex::new(e).unwrap()).collect();
|
||||
|
||||
// Matches virtual file paths for doc testing
|
||||
// e.g. file:///path/to/mod.ts$23-29.ts
|
||||
let doc_test_re =
|
||||
Regex::new(r"\$\d+-\d+\.(js|mjs|cjs|jsx|ts|mts|cts|tsx)$").unwrap();
|
||||
|
||||
coverages
|
||||
.into_iter()
|
||||
.filter(|e| {
|
||||
|
@ -460,6 +465,7 @@ fn filter_coverages(
|
|||
|| e.url.ends_with("$deno$test.js")
|
||||
|| e.url.ends_with(".snap")
|
||||
|| is_supported_test_path(Path::new(e.url.as_str()))
|
||||
|| doc_test_re.is_match(e.url.as_str())
|
||||
|| Url::parse(&e.url)
|
||||
.ok()
|
||||
.map(|url| npm_resolver.in_npm_package(&url))
|
||||
|
|
|
@ -5,10 +5,11 @@ use crate::args::DocHtmlFlag;
|
|||
use crate::args::DocSourceFileFlag;
|
||||
use crate::args::Flags;
|
||||
use crate::colors;
|
||||
use crate::display::write_json_to_stdout;
|
||||
use crate::display::write_to_stdout_ignore_sigpipe;
|
||||
use crate::display;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::graph_util::graph_exit_lock_errors;
|
||||
use crate::graph_util::graph_exit_integrity_errors;
|
||||
use crate::graph_util::graph_walk_errors;
|
||||
use crate::graph_util::GraphWalkErrorsOptions;
|
||||
use crate::tsc::get_types_declaration_file_text;
|
||||
use crate::util::fs::collect_specifiers;
|
||||
use deno_ast::diagnostics::Diagnostic;
|
||||
|
@ -17,6 +18,7 @@ use deno_config::glob::PathOrPatternSet;
|
|||
use deno_core::anyhow::bail;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use deno_doc as doc;
|
||||
use deno_doc::html::UrlResolveKind;
|
||||
use deno_graph::source::NullFileSystem;
|
||||
|
@ -31,6 +33,8 @@ use std::collections::BTreeMap;
|
|||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
const JSON_SCHEMA_VERSION: u8 = 1;
|
||||
|
||||
async fn generate_doc_nodes_for_builtin_types(
|
||||
doc_flags: DocFlags,
|
||||
parser: &dyn ModuleParser,
|
||||
|
@ -105,7 +109,7 @@ pub async fn doc(
|
|||
}
|
||||
DocSourceFileFlag::Paths(ref source_files) => {
|
||||
let module_graph_creator = factory.module_graph_creator().await?;
|
||||
let maybe_lockfile = cli_options.maybe_lockfile();
|
||||
let fs = factory.fs();
|
||||
|
||||
let module_specifiers = collect_specifiers(
|
||||
FilePatterns {
|
||||
|
@ -125,8 +129,18 @@ pub async fn doc(
|
|||
.create_graph(GraphKind::TypesOnly, module_specifiers.clone())
|
||||
.await?;
|
||||
|
||||
if maybe_lockfile.is_some() {
|
||||
graph_exit_lock_errors(&graph);
|
||||
graph_exit_integrity_errors(&graph);
|
||||
let errors = graph_walk_errors(
|
||||
&graph,
|
||||
fs,
|
||||
&module_specifiers,
|
||||
GraphWalkErrorsOptions {
|
||||
check_js: false,
|
||||
kind: GraphKind::TypesOnly,
|
||||
},
|
||||
);
|
||||
for error in errors {
|
||||
log::warn!("{} {}", colors::yellow("Warning"), error);
|
||||
}
|
||||
|
||||
let doc_parser = doc::DocParser::new(
|
||||
|
@ -181,7 +195,7 @@ pub async fn doc(
|
|||
kind_with_drilldown:
|
||||
deno_doc::html::DocNodeKindWithDrilldown::Other(node.kind()),
|
||||
inner: Rc::new(node),
|
||||
drilldown_parent_kind: None,
|
||||
drilldown_name: None,
|
||||
parent: None,
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
|
@ -228,7 +242,11 @@ pub async fn doc(
|
|||
doc_nodes_by_url.into_values().flatten().collect::<Vec<_>>();
|
||||
|
||||
if doc_flags.json {
|
||||
write_json_to_stdout(&doc_nodes)
|
||||
let json_output = serde_json::json!({
|
||||
"version": JSON_SCHEMA_VERSION,
|
||||
"nodes": &doc_nodes
|
||||
});
|
||||
display::write_json_to_stdout(&json_output)
|
||||
} else if doc_flags.lint {
|
||||
// don't output docs if running with only the --lint flag
|
||||
log::info!(
|
||||
|
@ -244,7 +262,7 @@ pub async fn doc(
|
|||
}
|
||||
|
||||
struct DocResolver {
|
||||
deno_ns: std::collections::HashSet<Vec<String>>,
|
||||
deno_ns: std::collections::HashMap<Vec<String>, Option<Rc<ShortPath>>>,
|
||||
strip_trailing_html: bool,
|
||||
}
|
||||
|
||||
|
@ -268,7 +286,7 @@ impl deno_doc::html::HrefResolver for DocResolver {
|
|||
}
|
||||
|
||||
fn resolve_global_symbol(&self, symbol: &[String]) -> Option<String> {
|
||||
if self.deno_ns.contains(symbol) {
|
||||
if self.deno_ns.contains_key(symbol) {
|
||||
Some(format!(
|
||||
"https://deno.land/api@v{}?s={}",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
|
@ -437,7 +455,7 @@ impl deno_doc::html::HrefResolver for NodeDocResolver {
|
|||
fn generate_docs_directory(
|
||||
doc_nodes_by_url: IndexMap<ModuleSpecifier, Vec<doc::DocNode>>,
|
||||
html_options: &DocHtmlFlag,
|
||||
deno_ns: std::collections::HashSet<Vec<String>>,
|
||||
deno_ns: std::collections::HashMap<Vec<String>, Option<Rc<ShortPath>>>,
|
||||
rewrite_map: Option<IndexMap<ModuleSpecifier, String>>,
|
||||
) -> Result<(), AnyError> {
|
||||
let cwd = std::env::current_dir().context("Failed to get CWD")?;
|
||||
|
@ -495,7 +513,6 @@ fn generate_docs_directory(
|
|||
rewrite_map,
|
||||
href_resolver,
|
||||
usage_composer: None,
|
||||
composable_output: false,
|
||||
category_docs,
|
||||
disable_search: internal_env.is_some(),
|
||||
symbol_redirect_map,
|
||||
|
@ -553,7 +570,8 @@ fn print_docs_to_stdout(
|
|||
)
|
||||
};
|
||||
|
||||
write_to_stdout_ignore_sigpipe(details.as_bytes()).map_err(AnyError::from)
|
||||
display::write_to_stdout_ignore_sigpipe(details.as_bytes())
|
||||
.map_err(AnyError::from)
|
||||
}
|
||||
|
||||
fn check_diagnostics(diagnostics: &[DocDiagnostic]) -> Result<(), AnyError> {
|
||||
|
|
183
cli/tools/fmt.rs
183
cli/tools/fmt.rs
|
@ -33,6 +33,7 @@ use deno_core::error::AnyError;
|
|||
use deno_core::futures;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::unsync::spawn_blocking;
|
||||
use deno_core::url::Url;
|
||||
use log::debug;
|
||||
use log::info;
|
||||
use log::warn;
|
||||
|
@ -120,7 +121,13 @@ pub async fn format(
|
|||
};
|
||||
}
|
||||
|
||||
format_files(caches, &fmt_flags, paths_with_options_batches).await?;
|
||||
format_files(
|
||||
caches,
|
||||
cli_options,
|
||||
&fmt_flags,
|
||||
paths_with_options_batches,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
@ -133,7 +140,8 @@ pub async fn format(
|
|||
let caches = factory.caches()?;
|
||||
let paths_with_options_batches =
|
||||
resolve_paths_with_options_batches(cli_options, &fmt_flags)?;
|
||||
format_files(caches, &fmt_flags, paths_with_options_batches).await?;
|
||||
format_files(caches, cli_options, &fmt_flags, paths_with_options_batches)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -172,6 +180,7 @@ fn resolve_paths_with_options_batches(
|
|||
|
||||
async fn format_files(
|
||||
caches: &Arc<Caches>,
|
||||
cli_options: &Arc<CliOptions>,
|
||||
fmt_flags: &FmtFlags,
|
||||
paths_with_options_batches: Vec<PathsWithOptions>,
|
||||
) -> Result<(), AnyError> {
|
||||
|
@ -199,6 +208,7 @@ async fn format_files(
|
|||
fmt_options.options,
|
||||
fmt_options.unstable,
|
||||
incremental_cache.clone(),
|
||||
cli_options.ext_flag().clone(),
|
||||
)
|
||||
.await?;
|
||||
incremental_cache.wait_completion().await;
|
||||
|
@ -211,11 +221,14 @@ fn collect_fmt_files(
|
|||
cli_options: &CliOptions,
|
||||
files: FilePatterns,
|
||||
) -> Result<Vec<PathBuf>, AnyError> {
|
||||
FileCollector::new(|e| is_supported_ext_fmt(e.path))
|
||||
.ignore_git_folder()
|
||||
.ignore_node_modules()
|
||||
.set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned))
|
||||
.collect_file_patterns(&deno_config::fs::RealDenoConfigFs, files)
|
||||
FileCollector::new(|e| {
|
||||
is_supported_ext_fmt(e.path)
|
||||
|| (e.path.extension().is_none() && cli_options.ext_flag().is_some())
|
||||
})
|
||||
.ignore_git_folder()
|
||||
.ignore_node_modules()
|
||||
.set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned))
|
||||
.collect_file_patterns(&deno_config::fs::RealDenoConfigFs, files)
|
||||
}
|
||||
|
||||
/// Formats markdown (using <https://github.com/dprint/dprint-plugin-markdown>) and its code blocks
|
||||
|
@ -253,6 +266,8 @@ fn format_markdown(
|
|||
| "svelte"
|
||||
| "vue"
|
||||
| "astro"
|
||||
| "vto"
|
||||
| "njk"
|
||||
| "yml"
|
||||
| "yaml"
|
||||
) {
|
||||
|
@ -273,44 +288,24 @@ fn format_markdown(
|
|||
dprint_plugin_json::format_text(&fake_filename, text, &json_config)
|
||||
}
|
||||
"css" | "scss" | "sass" | "less" => {
|
||||
if unstable_options.css {
|
||||
format_css(&fake_filename, text, fmt_options)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
format_css(&fake_filename, text, fmt_options)
|
||||
}
|
||||
"html" => {
|
||||
if unstable_options.html {
|
||||
format_html(&fake_filename, text, fmt_options)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
"svelte" | "vue" | "astro" => {
|
||||
"html" => format_html(&fake_filename, text, fmt_options),
|
||||
"svelte" | "vue" | "astro" | "vto" | "njk" => {
|
||||
if unstable_options.component {
|
||||
format_html(&fake_filename, text, fmt_options)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
"yml" | "yaml" => {
|
||||
if unstable_options.yaml {
|
||||
pretty_yaml::format_text(
|
||||
text,
|
||||
&get_resolved_yaml_config(fmt_options),
|
||||
)
|
||||
.map(Some)
|
||||
.map_err(AnyError::from)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
"yml" | "yaml" => format_yaml(text, fmt_options),
|
||||
_ => {
|
||||
let mut codeblock_config =
|
||||
get_resolved_typescript_config(fmt_options);
|
||||
codeblock_config.line_width = line_width;
|
||||
dprint_plugin_typescript::format_text(
|
||||
&fake_filename,
|
||||
None,
|
||||
text.to_string(),
|
||||
&codeblock_config,
|
||||
)
|
||||
|
@ -340,13 +335,33 @@ pub fn format_css(
|
|||
file_text: &str,
|
||||
fmt_options: &FmtOptionsConfig,
|
||||
) -> Result<Option<String>, AnyError> {
|
||||
malva::format_text(
|
||||
let formatted_str = malva::format_text(
|
||||
file_text,
|
||||
malva::detect_syntax(file_path).unwrap_or(malva::Syntax::Css),
|
||||
&get_resolved_malva_config(fmt_options),
|
||||
)
|
||||
.map(Some)
|
||||
.map_err(AnyError::from)
|
||||
.map_err(AnyError::from)?;
|
||||
|
||||
Ok(if formatted_str == file_text {
|
||||
None
|
||||
} else {
|
||||
Some(formatted_str)
|
||||
})
|
||||
}
|
||||
|
||||
fn format_yaml(
|
||||
file_text: &str,
|
||||
fmt_options: &FmtOptionsConfig,
|
||||
) -> Result<Option<String>, AnyError> {
|
||||
let formatted_str =
|
||||
pretty_yaml::format_text(file_text, &get_resolved_yaml_config(fmt_options))
|
||||
.map_err(AnyError::from)?;
|
||||
|
||||
Ok(if formatted_str == file_text {
|
||||
None
|
||||
} else {
|
||||
Some(formatted_str)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_html(
|
||||
|
@ -354,7 +369,7 @@ pub fn format_html(
|
|||
file_text: &str,
|
||||
fmt_options: &FmtOptionsConfig,
|
||||
) -> Result<Option<String>, AnyError> {
|
||||
markup_fmt::format_text(
|
||||
let format_result = markup_fmt::format_text(
|
||||
file_text,
|
||||
markup_fmt::detect_language(file_path)
|
||||
.unwrap_or(markup_fmt::Language::Html),
|
||||
|
@ -405,6 +420,7 @@ pub fn format_html(
|
|||
typescript_config.line_width = hints.print_width as u32;
|
||||
dprint_plugin_typescript::format_text(
|
||||
&path,
|
||||
None,
|
||||
text.to_string(),
|
||||
&typescript_config,
|
||||
)
|
||||
|
@ -419,9 +435,30 @@ pub fn format_html(
|
|||
}
|
||||
},
|
||||
)
|
||||
.map(Some)
|
||||
.map_err(|error| match error {
|
||||
markup_fmt::FormatError::Syntax(error) => AnyError::from(error),
|
||||
markup_fmt::FormatError::Syntax(error) => {
|
||||
fn inner(
|
||||
error: &markup_fmt::SyntaxError,
|
||||
file_path: &Path,
|
||||
) -> Option<String> {
|
||||
let url = Url::from_file_path(file_path).ok()?;
|
||||
|
||||
let error_msg = format!(
|
||||
"Syntax error ({}) at {}:{}:{}\n",
|
||||
error.kind,
|
||||
url.as_str(),
|
||||
error.line,
|
||||
error.column
|
||||
);
|
||||
Some(error_msg)
|
||||
}
|
||||
|
||||
if let Some(error_msg) = inner(&error, file_path) {
|
||||
AnyError::from(generic_error(error_msg))
|
||||
} else {
|
||||
AnyError::from(error)
|
||||
}
|
||||
}
|
||||
markup_fmt::FormatError::External(errors) => {
|
||||
let last = errors.len() - 1;
|
||||
AnyError::msg(
|
||||
|
@ -438,6 +475,14 @@ pub fn format_html(
|
|||
.collect::<String>(),
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
let formatted_str = format_result?;
|
||||
|
||||
Ok(if formatted_str == file_text {
|
||||
None
|
||||
} else {
|
||||
Some(formatted_str)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -447,8 +492,11 @@ pub fn format_file(
|
|||
file_text: &str,
|
||||
fmt_options: &FmtOptionsConfig,
|
||||
unstable_options: &UnstableFmtOptions,
|
||||
ext: Option<String>,
|
||||
) -> Result<Option<String>, AnyError> {
|
||||
let ext = get_extension(file_path).unwrap_or_default();
|
||||
let ext = ext
|
||||
.or_else(|| get_extension(file_path))
|
||||
.unwrap_or("ts".to_string());
|
||||
|
||||
match ext.as_str() {
|
||||
"md" | "mkd" | "mkdn" | "mdwn" | "mdown" | "markdown" => {
|
||||
|
@ -456,48 +504,28 @@ pub fn format_file(
|
|||
}
|
||||
"json" | "jsonc" => format_json(file_path, file_text, fmt_options),
|
||||
"css" | "scss" | "sass" | "less" => {
|
||||
if unstable_options.css {
|
||||
format_css(file_path, file_text, fmt_options)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
format_css(file_path, file_text, fmt_options)
|
||||
}
|
||||
"html" => {
|
||||
if unstable_options.html {
|
||||
format_html(file_path, file_text, fmt_options)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
"svelte" | "vue" | "astro" => {
|
||||
"html" => format_html(file_path, file_text, fmt_options),
|
||||
"svelte" | "vue" | "astro" | "vto" | "njk" => {
|
||||
if unstable_options.component {
|
||||
format_html(file_path, file_text, fmt_options)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
"yml" | "yaml" => {
|
||||
if unstable_options.yaml {
|
||||
pretty_yaml::format_text(
|
||||
file_text,
|
||||
&get_resolved_yaml_config(fmt_options),
|
||||
)
|
||||
.map(Some)
|
||||
.map_err(AnyError::from)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
"yml" | "yaml" => format_yaml(file_text, fmt_options),
|
||||
"ipynb" => dprint_plugin_jupyter::format_text(
|
||||
file_text,
|
||||
|file_path: &Path, file_text: String| {
|
||||
format_file(file_path, &file_text, fmt_options, unstable_options)
|
||||
format_file(file_path, &file_text, fmt_options, unstable_options, None)
|
||||
},
|
||||
),
|
||||
_ => {
|
||||
let config = get_resolved_typescript_config(fmt_options);
|
||||
dprint_plugin_typescript::format_text(
|
||||
file_path,
|
||||
Some(&ext),
|
||||
file_text.to_string(),
|
||||
&config,
|
||||
)
|
||||
|
@ -523,6 +551,7 @@ trait Formatter {
|
|||
fmt_options: FmtOptionsConfig,
|
||||
unstable_options: UnstableFmtOptions,
|
||||
incremental_cache: Arc<IncrementalCache>,
|
||||
ext: Option<String>,
|
||||
) -> Result<(), AnyError>;
|
||||
|
||||
fn finish(&self) -> Result<(), AnyError>;
|
||||
|
@ -542,6 +571,7 @@ impl Formatter for CheckFormatter {
|
|||
fmt_options: FmtOptionsConfig,
|
||||
unstable_options: UnstableFmtOptions,
|
||||
incremental_cache: Arc<IncrementalCache>,
|
||||
ext: Option<String>,
|
||||
) -> Result<(), AnyError> {
|
||||
// prevent threads outputting at the same time
|
||||
let output_lock = Arc::new(Mutex::new(0));
|
||||
|
@ -563,6 +593,7 @@ impl Formatter for CheckFormatter {
|
|||
&file_text,
|
||||
&fmt_options,
|
||||
&unstable_options,
|
||||
ext.clone(),
|
||||
) {
|
||||
Ok(Some(formatted_text)) => {
|
||||
not_formatted_files_count.fetch_add(1, Ordering::Relaxed);
|
||||
|
@ -640,6 +671,7 @@ impl Formatter for RealFormatter {
|
|||
fmt_options: FmtOptionsConfig,
|
||||
unstable_options: UnstableFmtOptions,
|
||||
incremental_cache: Arc<IncrementalCache>,
|
||||
ext: Option<String>,
|
||||
) -> Result<(), AnyError> {
|
||||
let output_lock = Arc::new(Mutex::new(0)); // prevent threads outputting at the same time
|
||||
|
||||
|
@ -659,7 +691,13 @@ impl Formatter for RealFormatter {
|
|||
&file_path,
|
||||
&file_contents.text,
|
||||
|file_path, file_text| {
|
||||
format_file(file_path, file_text, &fmt_options, &unstable_options)
|
||||
format_file(
|
||||
file_path,
|
||||
file_text,
|
||||
&fmt_options,
|
||||
&unstable_options,
|
||||
ext.clone(),
|
||||
)
|
||||
},
|
||||
) {
|
||||
Ok(Some(formatted_text)) => {
|
||||
|
@ -785,6 +823,7 @@ fn format_stdin(
|
|||
&source,
|
||||
&fmt_options.options,
|
||||
&fmt_options.unstable,
|
||||
None,
|
||||
)?;
|
||||
if fmt_flags.check {
|
||||
#[allow(clippy::print_stdout)]
|
||||
|
@ -907,6 +946,7 @@ fn get_resolved_malva_config(
|
|||
};
|
||||
|
||||
let language_options = LanguageOptions {
|
||||
align_comments: true,
|
||||
hex_case: HexCase::Lower,
|
||||
hex_color_length: None,
|
||||
quotes: if let Some(true) = options.single_quote {
|
||||
|
@ -1011,7 +1051,6 @@ fn get_resolved_yaml_config(
|
|||
|
||||
let layout_options = LayoutOptions {
|
||||
print_width: options.line_width.unwrap_or(80) as usize,
|
||||
use_tabs: options.use_tabs.unwrap_or_default(),
|
||||
indent_width: options.indent_width.unwrap_or(2) as usize,
|
||||
line_break: LineBreak::Lf,
|
||||
};
|
||||
|
@ -1028,6 +1067,9 @@ fn get_resolved_yaml_config(
|
|||
brace_spacing: true,
|
||||
bracket_spacing: false,
|
||||
dash_spacing: DashSpacing::OneSpace,
|
||||
prefer_single_line: false,
|
||||
flow_sequence_prefer_single_line: None,
|
||||
flow_map_prefer_single_line: None,
|
||||
trim_trailing_whitespaces: true,
|
||||
trim_trailing_zero: false,
|
||||
ignore_comment_directive: "deno-fmt-ignore".into(),
|
||||
|
@ -1139,6 +1181,8 @@ fn is_supported_ext_fmt(path: &Path) -> bool {
|
|||
| "svelte"
|
||||
| "vue"
|
||||
| "astro"
|
||||
| "vto"
|
||||
| "njk"
|
||||
| "md"
|
||||
| "mkd"
|
||||
| "mkdn"
|
||||
|
@ -1197,6 +1241,10 @@ mod test {
|
|||
assert!(is_supported_ext_fmt(Path::new("foo.VUE")));
|
||||
assert!(is_supported_ext_fmt(Path::new("foo.astro")));
|
||||
assert!(is_supported_ext_fmt(Path::new("foo.AsTrO")));
|
||||
assert!(is_supported_ext_fmt(Path::new("foo.vto")));
|
||||
assert!(is_supported_ext_fmt(Path::new("foo.Vto")));
|
||||
assert!(is_supported_ext_fmt(Path::new("foo.njk")));
|
||||
assert!(is_supported_ext_fmt(Path::new("foo.NJk")));
|
||||
assert!(is_supported_ext_fmt(Path::new("foo.yml")));
|
||||
assert!(is_supported_ext_fmt(Path::new("foo.Yml")));
|
||||
assert!(is_supported_ext_fmt(Path::new("foo.yaml")));
|
||||
|
@ -1263,6 +1311,7 @@ mod test {
|
|||
..Default::default()
|
||||
},
|
||||
&UnstableFmtOptions::default(),
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
|
|
@ -11,13 +11,14 @@ use deno_core::anyhow::bail;
|
|||
use deno_core::error::AnyError;
|
||||
use deno_core::resolve_url_or_path;
|
||||
use deno_core::serde_json;
|
||||
use deno_core::serde_json::json;
|
||||
use deno_core::url;
|
||||
use deno_graph::Dependency;
|
||||
use deno_graph::GraphKind;
|
||||
use deno_graph::Module;
|
||||
use deno_graph::ModuleError;
|
||||
use deno_graph::ModuleGraph;
|
||||
use deno_graph::Resolution;
|
||||
use deno_npm::npm_rc::ResolvedNpmRc;
|
||||
use deno_npm::resolution::NpmResolutionSnapshot;
|
||||
use deno_npm::NpmPackageId;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
|
@ -30,11 +31,13 @@ use crate::args::Flags;
|
|||
use crate::args::InfoFlags;
|
||||
use crate::display;
|
||||
use crate::factory::CliFactory;
|
||||
use crate::graph_util::graph_exit_lock_errors;
|
||||
use crate::graph_util::graph_exit_integrity_errors;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::ManagedCliNpmResolver;
|
||||
use crate::util::checksum;
|
||||
|
||||
const JSON_SCHEMA_VERSION: u8 = 1;
|
||||
|
||||
pub async fn info(
|
||||
flags: Arc<Flags>,
|
||||
info_flags: InfoFlags,
|
||||
|
@ -46,20 +49,23 @@ pub async fn info(
|
|||
let module_graph_creator = factory.module_graph_creator().await?;
|
||||
let npm_resolver = factory.npm_resolver().await?;
|
||||
let maybe_lockfile = cli_options.maybe_lockfile();
|
||||
let npmrc = cli_options.npmrc();
|
||||
let resolver = factory.workspace_resolver().await?;
|
||||
|
||||
let maybe_import_specifier =
|
||||
if let Some(import_map) = resolver.maybe_import_map() {
|
||||
if let Ok(imports_specifier) =
|
||||
import_map.resolve(&specifier, import_map.base_url())
|
||||
{
|
||||
Some(imports_specifier)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
let cwd_url =
|
||||
url::Url::from_directory_path(cli_options.initial_cwd()).unwrap();
|
||||
|
||||
let maybe_import_specifier = if let Some(import_map) =
|
||||
resolver.maybe_import_map()
|
||||
{
|
||||
if let Ok(imports_specifier) = import_map.resolve(&specifier, &cwd_url) {
|
||||
Some(imports_specifier)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let specifier = match maybe_import_specifier {
|
||||
Some(specifier) => specifier,
|
||||
|
@ -74,13 +80,21 @@ pub async fn info(
|
|||
|
||||
// write out the lockfile if there is one
|
||||
if let Some(lockfile) = &maybe_lockfile {
|
||||
graph_exit_lock_errors(&graph);
|
||||
graph_exit_integrity_errors(&graph);
|
||||
lockfile.write_if_changed()?;
|
||||
}
|
||||
|
||||
if info_flags.json {
|
||||
let mut json_graph = json!(graph);
|
||||
add_npm_packages_to_json(&mut json_graph, npm_resolver.as_ref());
|
||||
let mut json_graph = serde_json::json!(graph);
|
||||
if let Some(output) = json_graph.as_object_mut() {
|
||||
output.shift_insert(
|
||||
0,
|
||||
"version".to_string(),
|
||||
JSON_SCHEMA_VERSION.into(),
|
||||
);
|
||||
}
|
||||
|
||||
add_npm_packages_to_json(&mut json_graph, npm_resolver.as_ref(), npmrc);
|
||||
display::write_json_to_stdout(&json_graph)?;
|
||||
} else {
|
||||
let mut output = String::new();
|
||||
|
@ -121,7 +135,8 @@ fn print_cache_info(
|
|||
let local_storage_dir = origin_dir.join("local_storage");
|
||||
|
||||
if json {
|
||||
let mut output = json!({
|
||||
let mut json_output = serde_json::json!({
|
||||
"version": JSON_SCHEMA_VERSION,
|
||||
"denoDir": deno_dir,
|
||||
"modulesCache": modules_cache,
|
||||
"npmCache": npm_cache,
|
||||
|
@ -131,10 +146,10 @@ fn print_cache_info(
|
|||
});
|
||||
|
||||
if location.is_some() {
|
||||
output["localStorage"] = serde_json::to_value(local_storage_dir)?;
|
||||
json_output["localStorage"] = serde_json::to_value(local_storage_dir)?;
|
||||
}
|
||||
|
||||
display::write_json_to_stdout(&output)
|
||||
display::write_json_to_stdout(&json_output)
|
||||
} else {
|
||||
println!("{} {}", colors::bold("DENO_DIR location:"), deno_dir);
|
||||
println!(
|
||||
|
@ -176,6 +191,7 @@ fn print_cache_info(
|
|||
fn add_npm_packages_to_json(
|
||||
json: &mut serde_json::Value,
|
||||
npm_resolver: &dyn CliNpmResolver,
|
||||
npmrc: &ResolvedNpmRc,
|
||||
) {
|
||||
let Some(npm_resolver) = npm_resolver.as_managed() else {
|
||||
return; // does not include byonm to deno info's output
|
||||
|
@ -186,45 +202,28 @@ fn add_npm_packages_to_json(
|
|||
let json = json.as_object_mut().unwrap();
|
||||
let modules = json.get_mut("modules").and_then(|m| m.as_array_mut());
|
||||
if let Some(modules) = modules {
|
||||
if modules.len() == 1
|
||||
&& modules[0].get("kind").and_then(|k| k.as_str()) == Some("npm")
|
||||
{
|
||||
// If there is only one module and it's "external", then that means
|
||||
// someone provided an npm specifier as a cli argument. In this case,
|
||||
// we want to show which npm package the cli argument resolved to.
|
||||
let module = &mut modules[0];
|
||||
let maybe_package = module
|
||||
.get("specifier")
|
||||
.and_then(|k| k.as_str())
|
||||
.and_then(|specifier| NpmPackageNvReference::from_str(specifier).ok())
|
||||
.and_then(|package_ref| {
|
||||
snapshot
|
||||
.resolve_package_from_deno_module(package_ref.nv())
|
||||
.ok()
|
||||
});
|
||||
if let Some(pkg) = maybe_package {
|
||||
if let Some(module) = module.as_object_mut() {
|
||||
module
|
||||
.insert("npmPackage".to_string(), pkg.id.as_serialized().into());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Filter out npm package references from the modules and instead
|
||||
// have them only listed as dependencies. This is done because various
|
||||
// npm specifiers modules in the graph are really just unresolved
|
||||
// references. So there could be listed multiple npm specifiers
|
||||
// that would resolve to a single npm package.
|
||||
for i in (0..modules.len()).rev() {
|
||||
if matches!(
|
||||
modules[i].get("kind").and_then(|k| k.as_str()),
|
||||
Some("npm") | Some("external")
|
||||
) {
|
||||
modules.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for module in modules.iter_mut() {
|
||||
if matches!(module.get("kind").and_then(|k| k.as_str()), Some("npm")) {
|
||||
// If there is only one module and it's "external", then that means
|
||||
// someone provided an npm specifier as a cli argument. In this case,
|
||||
// we want to show which npm package the cli argument resolved to.
|
||||
let maybe_package = module
|
||||
.get("specifier")
|
||||
.and_then(|k| k.as_str())
|
||||
.and_then(|specifier| NpmPackageNvReference::from_str(specifier).ok())
|
||||
.and_then(|package_ref| {
|
||||
snapshot
|
||||
.resolve_package_from_deno_module(package_ref.nv())
|
||||
.ok()
|
||||
});
|
||||
if let Some(pkg) = maybe_package {
|
||||
if let Some(module) = module.as_object_mut() {
|
||||
module
|
||||
.insert("npmPackage".to_string(), pkg.id.as_serialized().into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let dependencies = module
|
||||
.get_mut("dependencies")
|
||||
.and_then(|d| d.as_array_mut());
|
||||
|
@ -256,7 +255,7 @@ fn add_npm_packages_to_json(
|
|||
let mut json_packages = serde_json::Map::with_capacity(sorted_packages.len());
|
||||
for pkg in sorted_packages {
|
||||
let mut kv = serde_json::Map::new();
|
||||
kv.insert("name".to_string(), pkg.id.nv.name.to_string().into());
|
||||
kv.insert("name".to_string(), pkg.id.nv.name.clone().into());
|
||||
kv.insert("version".to_string(), pkg.id.nv.version.to_string().into());
|
||||
let mut deps = pkg.dependencies.values().collect::<Vec<_>>();
|
||||
deps.sort();
|
||||
|
@ -265,6 +264,8 @@ fn add_npm_packages_to_json(
|
|||
.map(|id| serde_json::Value::String(id.as_serialized()))
|
||||
.collect::<Vec<_>>();
|
||||
kv.insert("dependencies".to_string(), deps.into());
|
||||
let registry_url = npmrc.get_registry_url(&pkg.id.nv.name);
|
||||
kv.insert("registryUrl".to_string(), registry_url.to_string().into());
|
||||
|
||||
json_packages.insert(pkg.id.as_serialized(), kv.into());
|
||||
}
|
||||
|
@ -456,22 +457,6 @@ impl<'a> GraphDisplayContext<'a> {
|
|||
local.to_string_lossy()
|
||||
)?;
|
||||
}
|
||||
if let Some(emit) = &cache_info.emit {
|
||||
writeln!(
|
||||
writer,
|
||||
"{} {}",
|
||||
colors::bold("emit:"),
|
||||
emit.to_string_lossy()
|
||||
)?;
|
||||
}
|
||||
if let Some(map) = &cache_info.map {
|
||||
writeln!(
|
||||
writer,
|
||||
"{} {}",
|
||||
colors::bold("map:"),
|
||||
map.to_string_lossy()
|
||||
)?;
|
||||
}
|
||||
}
|
||||
if let Some(module) = root.js() {
|
||||
writeln!(writer, "{} {}", colors::bold("type:"), module.media_type)?;
|
||||
|
@ -655,8 +640,21 @@ impl<'a> GraphDisplayContext<'a> {
|
|||
ModuleError::InvalidTypeAssertion { .. } => {
|
||||
self.build_error_msg(specifier, "(invalid import attribute)")
|
||||
}
|
||||
ModuleError::LoadingErr(_, _, _) => {
|
||||
self.build_error_msg(specifier, "(loading error)")
|
||||
ModuleError::LoadingErr(_, _, err) => {
|
||||
use deno_graph::ModuleLoadError::*;
|
||||
let message = match err {
|
||||
HttpsChecksumIntegrity(_) => "(checksum integrity error)",
|
||||
Decode(_) => "(loading decode error)",
|
||||
Loader(err) => match deno_core::error::get_custom_error_class(err) {
|
||||
Some("NotCapable") => "(not capable, requires --allow-import)",
|
||||
_ => "(loading error)",
|
||||
},
|
||||
Jsr(_) => "(loading error)",
|
||||
NodeUnknownBuiltinModule(_) => "(unknown node built-in error)",
|
||||
Npm(_) => "(npm loading error)",
|
||||
TooManyRedirects => "(too many redirects error)",
|
||||
};
|
||||
self.build_error_msg(specifier, message.as_ref())
|
||||
}
|
||||
ModuleError::ParseErr(_, _) => {
|
||||
self.build_error_msg(specifier, "(parsing error)")
|
||||
|
|
|
@ -198,14 +198,15 @@ pub async fn infer_name_from_url(
|
|||
Some(stem.to_string())
|
||||
}
|
||||
|
||||
pub fn uninstall(uninstall_flags: UninstallFlags) -> Result<(), AnyError> {
|
||||
if !uninstall_flags.global {
|
||||
log::warn!("⚠️ `deno install` behavior will change in Deno 2. To preserve the current behavior use the `-g` or `--global` flag.");
|
||||
}
|
||||
|
||||
pub async fn uninstall(
|
||||
flags: Arc<Flags>,
|
||||
uninstall_flags: UninstallFlags,
|
||||
) -> Result<(), AnyError> {
|
||||
let uninstall_flags = match uninstall_flags.kind {
|
||||
UninstallKind::Global(flags) => flags,
|
||||
UninstallKind::Local => unreachable!(),
|
||||
UninstallKind::Local(remove_flags) => {
|
||||
return super::registry::remove(flags, remove_flags).await;
|
||||
}
|
||||
};
|
||||
|
||||
let cwd = std::env::current_dir().context("Unable to get CWD")?;
|
||||
|
@ -297,6 +298,10 @@ async fn install_local(
|
|||
}
|
||||
InstallFlagsLocal::TopLevel => {
|
||||
let factory = CliFactory::from_flags(flags);
|
||||
// surface any errors in the package.json
|
||||
if let Some(npm_resolver) = factory.npm_resolver().await?.as_managed() {
|
||||
npm_resolver.ensure_no_pkg_json_dep_errors()?;
|
||||
}
|
||||
crate::tools::registry::cache_top_level_deps(&factory, None).await?;
|
||||
|
||||
if let Some(lockfile) = factory.cli_options()?.maybe_lockfile() {
|
||||
|
@ -332,10 +337,6 @@ pub async fn install_command(
|
|||
) -> Result<(), AnyError> {
|
||||
match install_flags.kind {
|
||||
InstallKind::Global(global_flags) => {
|
||||
if !install_flags.global {
|
||||
log::warn!("⚠️ `deno install` behavior will change in Deno 2. To preserve the current behavior use the `-g` or `--global` flag.");
|
||||
}
|
||||
|
||||
install_global(flags, global_flags).await
|
||||
}
|
||||
InstallKind::Local(local_flags) => {
|
||||
|
@ -493,10 +494,6 @@ async fn resolve_shim_data(
|
|||
TypeCheckMode::Local => executable_args.push("--check".to_string()),
|
||||
}
|
||||
|
||||
if flags.unstable_config.legacy_flag_enabled {
|
||||
executable_args.push("--unstable".to_string());
|
||||
}
|
||||
|
||||
for feature in &flags.unstable_config.features {
|
||||
executable_args.push(format!("--unstable-{}", feature));
|
||||
}
|
||||
|
@ -825,13 +822,7 @@ mod tests {
|
|||
|
||||
create_install_shim(
|
||||
&HttpClientProvider::new(None, None),
|
||||
&Flags {
|
||||
unstable_config: UnstableConfig {
|
||||
legacy_flag_enabled: true,
|
||||
..Default::default()
|
||||
},
|
||||
..Flags::default()
|
||||
},
|
||||
&Flags::default(),
|
||||
InstallFlagsGlobal {
|
||||
module_url: "http://localhost:4545/echo_server.ts".to_string(),
|
||||
args: vec![],
|
||||
|
@ -853,12 +844,11 @@ mod tests {
|
|||
let content = fs::read_to_string(file_path).unwrap();
|
||||
if cfg!(windows) {
|
||||
assert!(content.contains(
|
||||
r#""run" "--unstable" "--no-config" "http://localhost:4545/echo_server.ts""#
|
||||
r#""run" "--no-config" "http://localhost:4545/echo_server.ts""#
|
||||
));
|
||||
} else {
|
||||
assert!(content.contains(
|
||||
r#"run --unstable --no-config 'http://localhost:4545/echo_server.ts'"#
|
||||
));
|
||||
assert!(content
|
||||
.contains(r#"run --no-config 'http://localhost:4545/echo_server.ts'"#));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -889,13 +879,7 @@ mod tests {
|
|||
async fn install_unstable_legacy() {
|
||||
let shim_data = resolve_shim_data(
|
||||
&HttpClientProvider::new(None, None),
|
||||
&Flags {
|
||||
unstable_config: UnstableConfig {
|
||||
legacy_flag_enabled: true,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
&Default::default(),
|
||||
&InstallFlagsGlobal {
|
||||
module_url: "http://localhost:4545/echo_server.ts".to_string(),
|
||||
args: vec![],
|
||||
|
@ -910,12 +894,7 @@ mod tests {
|
|||
assert_eq!(shim_data.name, "echo_server");
|
||||
assert_eq!(
|
||||
shim_data.args,
|
||||
vec![
|
||||
"run",
|
||||
"--unstable",
|
||||
"--no-config",
|
||||
"http://localhost:4545/echo_server.ts",
|
||||
]
|
||||
vec!["run", "--no-config", "http://localhost:4545/echo_server.ts",]
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1512,8 +1491,8 @@ mod tests {
|
|||
assert!(content.contains(&expected_string));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uninstall_basic() {
|
||||
#[tokio::test]
|
||||
async fn uninstall_basic() {
|
||||
let temp_dir = TempDir::new();
|
||||
let bin_dir = temp_dir.path().join("bin");
|
||||
std::fs::create_dir(&bin_dir).unwrap();
|
||||
|
@ -1540,13 +1519,16 @@ mod tests {
|
|||
File::create(file_path).unwrap();
|
||||
}
|
||||
|
||||
uninstall(UninstallFlags {
|
||||
kind: UninstallKind::Global(UninstallFlagsGlobal {
|
||||
name: "echo_test".to_string(),
|
||||
root: Some(temp_dir.path().to_string()),
|
||||
}),
|
||||
global: false,
|
||||
})
|
||||
uninstall(
|
||||
Default::default(),
|
||||
UninstallFlags {
|
||||
kind: UninstallKind::Global(UninstallFlagsGlobal {
|
||||
name: "echo_test".to_string(),
|
||||
root: Some(temp_dir.path().to_string()),
|
||||
}),
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(!file_path.exists());
|
||||
|
|
|
@ -58,9 +58,9 @@ pub fn install() -> Result<(), AnyError> {
|
|||
|
||||
let f = std::fs::File::create(kernel_json_path)?;
|
||||
serde_json::to_writer_pretty(f, &json_data)?;
|
||||
install_icon(&user_data_dir, "logo-32x32.png", DENO_ICON_32)?;
|
||||
install_icon(&user_data_dir, "logo-64x64.png", DENO_ICON_64)?;
|
||||
install_icon(&user_data_dir, "logo-svg.svg", DENO_ICON_SVG)?;
|
||||
install_icon(&kernel_dir, "logo-32x32.png", DENO_ICON_32)?;
|
||||
install_icon(&kernel_dir, "logo-64x64.png", DENO_ICON_64)?;
|
||||
install_icon(&kernel_dir, "logo-svg.svg", DENO_ICON_SVG)?;
|
||||
|
||||
log::info!("✅ Deno kernelspec installed successfully.");
|
||||
Ok(())
|
||||
|
|
|
@ -25,7 +25,6 @@ use deno_core::serde_json::json;
|
|||
use deno_core::url::Url;
|
||||
use deno_runtime::deno_io::Stdio;
|
||||
use deno_runtime::deno_io::StdioPipe;
|
||||
use deno_runtime::deno_permissions::Permissions;
|
||||
use deno_runtime::deno_permissions::PermissionsContainer;
|
||||
use deno_runtime::WorkerExecutionMode;
|
||||
use deno_terminal::colors;
|
||||
|
@ -65,7 +64,8 @@ pub async fn kernel(
|
|||
resolve_url_or_path("./$deno$jupyter.ts", cli_options.initial_cwd())
|
||||
.unwrap();
|
||||
// TODO(bartlomieju): should we run with all permissions?
|
||||
let permissions = PermissionsContainer::new(Permissions::allow_all());
|
||||
let permissions =
|
||||
PermissionsContainer::allow_all(factory.permission_desc_parser()?.clone());
|
||||
let npm_resolver = factory.npm_resolver().await?.clone();
|
||||
let resolver = factory.resolver().await?.clone();
|
||||
let worker_factory = factory.create_cli_main_worker_factory().await?;
|
||||
|
@ -357,56 +357,74 @@ pub struct JupyterReplSession {
|
|||
|
||||
impl JupyterReplSession {
|
||||
pub async fn start(&mut self) {
|
||||
let mut poll_worker = true;
|
||||
loop {
|
||||
let Some(msg) = self.rx.recv().await else {
|
||||
break;
|
||||
};
|
||||
let resp = match msg {
|
||||
JupyterReplRequest::LspCompletions {
|
||||
line_text,
|
||||
position,
|
||||
} => JupyterReplResponse::LspCompletions(
|
||||
self.lsp_completions(&line_text, position).await,
|
||||
),
|
||||
JupyterReplRequest::JsGetProperties { object_id } => {
|
||||
JupyterReplResponse::JsGetProperties(
|
||||
self.get_properties(object_id).await,
|
||||
)
|
||||
}
|
||||
JupyterReplRequest::JsEvaluate { expr } => {
|
||||
JupyterReplResponse::JsEvaluate(self.evaluate(expr).await)
|
||||
}
|
||||
JupyterReplRequest::JsGlobalLexicalScopeNames => {
|
||||
JupyterReplResponse::JsGlobalLexicalScopeNames(
|
||||
self.global_lexical_scope_names().await,
|
||||
)
|
||||
}
|
||||
JupyterReplRequest::JsEvaluateLineWithObjectWrapping { line } => {
|
||||
JupyterReplResponse::JsEvaluateLineWithObjectWrapping(
|
||||
self.evaluate_line_with_object_wrapping(&line).await,
|
||||
)
|
||||
}
|
||||
JupyterReplRequest::JsCallFunctionOnArgs {
|
||||
function_declaration,
|
||||
args,
|
||||
} => JupyterReplResponse::JsCallFunctionOnArgs(
|
||||
self
|
||||
.call_function_on_args(function_declaration, &args)
|
||||
.await,
|
||||
),
|
||||
JupyterReplRequest::JsCallFunctionOn { arg0, arg1 } => {
|
||||
JupyterReplResponse::JsCallFunctionOn(
|
||||
self.call_function_on(arg0, arg1).await,
|
||||
)
|
||||
}
|
||||
};
|
||||
tokio::select! {
|
||||
biased;
|
||||
|
||||
let Ok(()) = self.tx.send(resp) else {
|
||||
break;
|
||||
};
|
||||
maybe_message = self.rx.recv() => {
|
||||
let Some(msg) = maybe_message else {
|
||||
break;
|
||||
};
|
||||
if self.handle_message(msg).await.is_err() {
|
||||
break;
|
||||
}
|
||||
poll_worker = true;
|
||||
},
|
||||
_ = self.repl_session.run_event_loop(), if poll_worker => {
|
||||
poll_worker = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_message(
|
||||
&mut self,
|
||||
msg: JupyterReplRequest,
|
||||
) -> Result<(), AnyError> {
|
||||
let resp = match msg {
|
||||
JupyterReplRequest::LspCompletions {
|
||||
line_text,
|
||||
position,
|
||||
} => JupyterReplResponse::LspCompletions(
|
||||
self.lsp_completions(&line_text, position).await,
|
||||
),
|
||||
JupyterReplRequest::JsGetProperties { object_id } => {
|
||||
JupyterReplResponse::JsGetProperties(
|
||||
self.get_properties(object_id).await,
|
||||
)
|
||||
}
|
||||
JupyterReplRequest::JsEvaluate { expr } => {
|
||||
JupyterReplResponse::JsEvaluate(self.evaluate(expr).await)
|
||||
}
|
||||
JupyterReplRequest::JsGlobalLexicalScopeNames => {
|
||||
JupyterReplResponse::JsGlobalLexicalScopeNames(
|
||||
self.global_lexical_scope_names().await,
|
||||
)
|
||||
}
|
||||
JupyterReplRequest::JsEvaluateLineWithObjectWrapping { line } => {
|
||||
JupyterReplResponse::JsEvaluateLineWithObjectWrapping(
|
||||
self.evaluate_line_with_object_wrapping(&line).await,
|
||||
)
|
||||
}
|
||||
JupyterReplRequest::JsCallFunctionOnArgs {
|
||||
function_declaration,
|
||||
args,
|
||||
} => JupyterReplResponse::JsCallFunctionOnArgs(
|
||||
self
|
||||
.call_function_on_args(function_declaration, &args)
|
||||
.await,
|
||||
),
|
||||
JupyterReplRequest::JsCallFunctionOn { arg0, arg1 } => {
|
||||
JupyterReplResponse::JsCallFunctionOn(
|
||||
self.call_function_on(arg0, arg1).await,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
self.tx.send(resp).map_err(|e| e.into())
|
||||
}
|
||||
|
||||
pub async fn lsp_completions(
|
||||
&mut self,
|
||||
line_text: &str,
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1 KiB After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2.8 KiB |
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue