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

Merge branch 'main' into err-msg-ext-fs

This commit is contained in:
Bartek Iwańczuk 2024-11-15 09:53:36 +00:00 committed by GitHub
commit faaca5a806
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2907 changed files with 39495 additions and 16479 deletions

View file

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

View file

@ -65,13 +65,17 @@
"tests/wpt/runner/expectation.json", "tests/wpt/runner/expectation.json",
"tests/wpt/runner/manifest.json", "tests/wpt/runner/manifest.json",
"tests/wpt/suite", "tests/wpt/suite",
"third_party" "third_party",
"tests/specs/run/shebang_with_json_imports_tsc",
"tests/specs/run/shebang_with_json_imports_swc",
"tests/specs/run/ext_flag_takes_precedence_over_extension",
"tests/specs/run/error_syntax_empty_trailing_line/error_syntax_empty_trailing_line.mjs"
], ],
"plugins": [ "plugins": [
"https://plugins.dprint.dev/typescript-0.93.0.wasm", "https://plugins.dprint.dev/typescript-0.93.2.wasm",
"https://plugins.dprint.dev/json-0.19.3.wasm", "https://plugins.dprint.dev/json-0.19.4.wasm",
"https://plugins.dprint.dev/markdown-0.17.8.wasm", "https://plugins.dprint.dev/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/exec-0.5.0.json@8d9972eee71fa1590e04873540421f3eda7674d0f1aae3d7c788615e7b7413d0",
"https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.0.wasm" "https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.0.wasm"
] ]

View file

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

View file

@ -5,16 +5,16 @@ import { stringify } from "jsr:@std/yaml@^0.221/stringify";
// Bump this number when you want to purge the cache. // Bump this number when you want to purge the cache.
// Note: the tools/release/01_bump_crate_versions.ts script will update this version // 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. // automatically via regex, so ensure that this line maintains this format.
const cacheVersion = 15; const cacheVersion = 25;
const ubuntuX86Runner = "ubuntu-22.04"; const ubuntuX86Runner = "ubuntu-24.04";
const ubuntuX86XlRunner = "ubuntu-22.04-xl"; const ubuntuX86XlRunner = "ubuntu-24.04-xl";
const ubuntuARMRunner = "ubicloud-standard-16-arm"; const ubuntuARMRunner = "ubicloud-standard-16-arm";
const windowsX86Runner = "windows-2022"; const windowsX86Runner = "windows-2022";
const windowsX86XlRunner = "windows-2022-xl"; const windowsX86XlRunner = "windows-2022-xl";
const macosX86Runner = "macos-13"; const macosX86Runner = "macos-13";
const macosArmRunner = "macos-14"; const macosArmRunner = "macos-14";
const macosArmXlRunner = "macos-14-xlarge"; const selfHostedMacosArmRunner = "self-hosted";
const Runners = { const Runners = {
linuxX86: { linuxX86: {
@ -39,15 +39,10 @@ const Runners = {
runner: macosX86Runner, runner: macosX86Runner,
}, },
macosArm: { macosArm: {
os: "macos",
arch: "aarch64",
runner: macosArmRunner,
},
macosArmXl: {
os: "macos", os: "macos",
arch: "aarch64", arch: "aarch64",
runner: runner:
`\${{ github.repository == 'denoland/deno' && '${macosArmXlRunner}' || '${macosArmRunner}' }}`, `\${{ github.repository == 'denoland/deno' && startsWith(github.ref, 'refs/tags/') && '${selfHostedMacosArmRunner}' || '${macosArmRunner}' }}`,
}, },
windowsX86: { windowsX86: {
os: "windows", os: "windows",
@ -66,7 +61,7 @@ const prCacheKeyPrefix =
`${cacheVersion}-cargo-target-\${{ matrix.os }}-\${{ matrix.arch }}-\${{ matrix.profile }}-\${{ matrix.job }}-`; `${cacheVersion}-cargo-target-\${{ matrix.os }}-\${{ matrix.arch }}-\${{ matrix.profile }}-\${{ matrix.job }}-`;
// Note that you may need to add more version to the `apt-get remove` line below if you change this // Note that you may need to add more version to the `apt-get remove` line below if you change this
const llvmVersion = 18; const llvmVersion = 19;
const installPkgsCommand = const installPkgsCommand =
`sudo apt-get install --no-install-recommends clang-${llvmVersion} lld-${llvmVersion} clang-tools-${llvmVersion} clang-format-${llvmVersion} clang-tidy-${llvmVersion}`; `sudo apt-get install --no-install-recommends clang-${llvmVersion} lld-${llvmVersion} clang-tools-${llvmVersion} clang-format-${llvmVersion} clang-tidy-${llvmVersion}`;
const sysRootStep = { const sysRootStep = {
@ -78,7 +73,7 @@ export DEBIAN_FRONTEND=noninteractive
sudo apt-get -qq remove --purge -y man-db > /dev/null 2> /dev/null sudo apt-get -qq remove --purge -y man-db > /dev/null 2> /dev/null
# Remove older clang before we install # Remove older clang before we install
sudo apt-get -qq remove \ sudo apt-get -qq remove \
'clang-12*' 'clang-13*' 'clang-14*' 'clang-15*' 'clang-16*' 'llvm-12*' 'llvm-13*' 'llvm-14*' 'llvm-15*' 'llvm-16*' 'lld-12*' 'lld-13*' 'lld-14*' 'lld-15*' 'lld-16*' > /dev/null 2> /dev/null 'clang-12*' 'clang-13*' 'clang-14*' 'clang-15*' 'clang-16*' 'clang-17*' 'clang-18*' 'llvm-12*' 'llvm-13*' 'llvm-14*' 'llvm-15*' 'llvm-16*' 'lld-12*' 'lld-13*' 'lld-14*' 'lld-15*' 'lld-16*' 'lld-17*' 'lld-18*' > /dev/null 2> /dev/null
# Install clang-XXX, lld-XXX, and debootstrap. # Install clang-XXX, lld-XXX, and debootstrap.
echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-${llvmVersion} main" | echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-${llvmVersion} main" |
@ -93,7 +88,7 @@ ${installPkgsCommand} || echo 'Failed. Trying again.' && sudo apt-get clean && s
(yes '' | sudo update-alternatives --force --all) > /dev/null 2> /dev/null || true (yes '' | sudo update-alternatives --force --all) > /dev/null 2> /dev/null || true
echo "Decompressing sysroot..." echo "Decompressing sysroot..."
wget -q https://github.com/denoland/deno_sysroot_build/releases/download/sysroot-20240528/sysroot-\`uname -m\`.tar.xz -O /tmp/sysroot.tar.xz wget -q https://github.com/denoland/deno_sysroot_build/releases/download/sysroot-20241030/sysroot-\`uname -m\`.tar.xz -O /tmp/sysroot.tar.xz
cd / cd /
xzcat /tmp/sysroot.tar.xz | sudo tar -x xzcat /tmp/sysroot.tar.xz | sudo tar -x
sudo mount --rbind /dev /sysroot/dev sudo mount --rbind /dev /sysroot/dev
@ -198,14 +193,9 @@ const installNodeStep = {
uses: "actions/setup-node@v4", uses: "actions/setup-node@v4",
with: { "node-version": 18 }, 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 = { const installDenoStep = {
name: "Install Deno", name: "Install Deno",
uses: "denoland/setup-deno@v1", uses: "denoland/setup-deno@v2",
with: { "deno-version": "v1.x" }, with: { "deno-version": "v1.x" },
}; };
@ -385,7 +375,7 @@ const ci = {
job: "test", job: "test",
profile: "debug", profile: "debug",
}, { }, {
...Runners.macosArmXl, ...Runners.macosArm,
job: "test", job: "test",
profile: "release", profile: "release",
skip_pr: true, skip_pr: true,
@ -501,7 +491,6 @@ const ci = {
if: "matrix.job == 'bench' || matrix.job == 'test'", if: "matrix.job == 'bench' || matrix.job == 'test'",
...installNodeStep, ...installNodeStep,
}, },
installProtocStep,
{ {
if: [ if: [
"matrix.profile == 'release' &&", "matrix.profile == 'release' &&",
@ -765,8 +754,10 @@ const ci = {
run: [ run: [
"cd target/release", "cd target/release",
"zip -r deno-${{ matrix.arch }}-unknown-linux-gnu.zip deno", "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", "strip denort",
"zip -r denort-${{ matrix.arch }}-unknown-linux-gnu.zip 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", "./deno types > lib.deno.d.ts",
].join("\n"), ].join("\n"),
}, },
@ -791,8 +782,10 @@ const ci = {
"--entitlements-xml-file=cli/entitlements.plist", "--entitlements-xml-file=cli/entitlements.plist",
"cd target/release", "cd target/release",
"zip -r deno-${{ matrix.arch }}-apple-darwin.zip deno", "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", "strip denort",
"zip -r denort-${{ matrix.arch }}-apple-darwin.zip 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"), .join("\n"),
}, },
@ -807,7 +800,9 @@ const ci = {
shell: "pwsh", shell: "pwsh",
run: [ run: [
"Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip", "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", "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"), ].join("\n"),
}, },
{ {
@ -820,6 +815,7 @@ const ci = {
].join("\n"), ].join("\n"),
run: [ 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/*.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", "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', '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"), ].join("\n"),
@ -1001,8 +997,10 @@ const ci = {
"github.repository == 'denoland/deno' &&", "github.repository == 'denoland/deno' &&",
"startsWith(github.ref, 'refs/tags/')", "startsWith(github.ref, 'refs/tags/')",
].join("\n"), ].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/*.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)", name: "Upload release to dl.deno.land (windows)",
@ -1016,8 +1014,10 @@ const ci = {
env: { env: {
CLOUDSDK_PYTHON: "${{env.pythonLocation}}\\python.exe", 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/*.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", name: "Create release notes",
@ -1047,15 +1047,25 @@ const ci = {
with: { with: {
files: [ files: [
"target/release/deno-x86_64-pc-windows-msvc.zip", "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",
"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",
"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",
"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",
"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",
"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",
"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",
"target/release/denort-aarch64-unknown-linux-gnu.zip.sha256sum",
"target/release/deno-aarch64-apple-darwin.zip", "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",
"target/release/denort-aarch64-apple-darwin.zip.sha256sum",
"target/release/deno_src.tar.gz", "target/release/deno_src.tar.gz",
"target/release/lib.deno.d.ts", "target/release/lib.deno.d.ts",
].join("\n"), ].join("\n"),
@ -1074,6 +1084,7 @@ const ci = {
"./target", "./target",
"!./target/*/gn_out", "!./target/*/gn_out",
"!./target/*/*.zip", "!./target/*/*.zip",
"!./target/*/*.sha256sum",
"!./target/*/*.tar.gz", "!./target/*/*.tar.gz",
].join("\n"), ].join("\n"),
key: prCacheKeyPrefix + "${{ github.sha }}", key: prCacheKeyPrefix + "${{ github.sha }}",

View file

@ -62,18 +62,18 @@ jobs:
profile: debug profile: debug
- os: macos - os: macos
arch: x86_64 arch: x86_64
runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'')) && ''ubuntu-22.04'' || ''macos-13'' }}' runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'')) && ''ubuntu-24.04'' || ''macos-13'' }}'
job: test job: test
profile: release profile: release
skip: '${{ !contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'') }}' skip: '${{ !contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'') }}'
- os: macos - os: macos
arch: aarch64 arch: aarch64
runner: macos-14 runner: '${{ github.repository == ''denoland/deno'' && startsWith(github.ref, ''refs/tags/'') && ''self-hosted'' || ''macos-14'' }}'
job: test job: test
profile: debug profile: debug
- os: macos - os: macos
arch: aarch64 arch: aarch64
runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'')) && ''ubuntu-22.04'' || github.repository == ''denoland/deno'' && ''macos-14-xlarge'' || ''macos-14'' }}' runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'')) && ''ubuntu-24.04'' || github.repository == ''denoland/deno'' && startsWith(github.ref, ''refs/tags/'') && ''self-hosted'' || ''macos-14'' }}'
job: test job: test
profile: release profile: release
skip: '${{ !contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'') }}' skip: '${{ !contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'') }}'
@ -84,33 +84,33 @@ jobs:
profile: debug profile: debug
- os: windows - os: windows
arch: x86_64 arch: x86_64
runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'')) && ''ubuntu-22.04'' || github.repository == ''denoland/deno'' && ''windows-2022-xl'' || ''windows-2022'' }}' runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'')) && ''ubuntu-24.04'' || github.repository == ''denoland/deno'' && ''windows-2022-xl'' || ''windows-2022'' }}'
job: test job: test
profile: release profile: release
skip: '${{ !contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'') }}' skip: '${{ !contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'') }}'
- os: linux - os: linux
arch: x86_64 arch: x86_64
runner: '${{ github.repository == ''denoland/deno'' && ''ubuntu-22.04-xl'' || ''ubuntu-22.04'' }}' runner: '${{ github.repository == ''denoland/deno'' && ''ubuntu-24.04-xl'' || ''ubuntu-24.04'' }}'
job: test job: test
profile: release profile: release
use_sysroot: true use_sysroot: true
wpt: '${{ !startsWith(github.ref, ''refs/tags/'') }}' wpt: '${{ !startsWith(github.ref, ''refs/tags/'') }}'
- os: linux - os: linux
arch: x86_64 arch: x86_64
runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'' && !contains(github.event.pull_request.labels.*.name, ''ci-bench''))) && ''ubuntu-22.04'' || github.repository == ''denoland/deno'' && ''ubuntu-22.04-xl'' || ''ubuntu-22.04'' }}' runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'' && !contains(github.event.pull_request.labels.*.name, ''ci-bench''))) && ''ubuntu-24.04'' || github.repository == ''denoland/deno'' && ''ubuntu-24.04-xl'' || ''ubuntu-24.04'' }}'
job: bench job: bench
profile: release profile: release
use_sysroot: true use_sysroot: true
skip: '${{ !contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'' && !contains(github.event.pull_request.labels.*.name, ''ci-bench'')) }}' skip: '${{ !contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'' && !contains(github.event.pull_request.labels.*.name, ''ci-bench'')) }}'
- os: linux - os: linux
arch: x86_64 arch: x86_64
runner: ubuntu-22.04 runner: ubuntu-24.04
job: test job: test
profile: debug profile: debug
use_sysroot: true use_sysroot: true
- os: linux - os: linux
arch: x86_64 arch: x86_64
runner: ubuntu-22.04 runner: ubuntu-24.04
job: lint job: lint
profile: debug profile: debug
- os: linux - os: linux
@ -178,7 +178,7 @@ jobs:
if: '!(matrix.skip)' if: '!(matrix.skip)'
- if: '!(matrix.skip) && (matrix.job == ''lint'' || matrix.job == ''test'' || matrix.job == ''bench'')' - if: '!(matrix.skip) && (matrix.job == ''lint'' || matrix.job == ''test'' || matrix.job == ''bench'')'
name: Install Deno name: Install Deno
uses: denoland/setup-deno@v1 uses: denoland/setup-deno@v2
with: with:
deno-version: v1.x deno-version: v1.x
- name: Install Python - name: Install Python
@ -199,12 +199,6 @@ jobs:
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 18 node-version: 18
- name: Install protoc
uses: arduino/setup-protoc@v3
with:
version: '21.12'
repo-token: '${{ secrets.GITHUB_TOKEN }}'
if: '!(matrix.skip)'
- if: |- - if: |-
!(matrix.skip) && (matrix.profile == 'release' && !(matrix.skip) && (matrix.profile == 'release' &&
matrix.job == 'test' && matrix.job == 'test' &&
@ -258,22 +252,22 @@ jobs:
# to complete. # to complete.
sudo apt-get -qq remove --purge -y man-db > /dev/null 2> /dev/null sudo apt-get -qq remove --purge -y man-db > /dev/null 2> /dev/null
# Remove older clang before we install # Remove older clang before we install
sudo apt-get -qq remove 'clang-12*' 'clang-13*' 'clang-14*' 'clang-15*' 'clang-16*' 'llvm-12*' 'llvm-13*' 'llvm-14*' 'llvm-15*' 'llvm-16*' 'lld-12*' 'lld-13*' 'lld-14*' 'lld-15*' 'lld-16*' > /dev/null 2> /dev/null sudo apt-get -qq remove 'clang-12*' 'clang-13*' 'clang-14*' 'clang-15*' 'clang-16*' 'clang-17*' 'clang-18*' 'llvm-12*' 'llvm-13*' 'llvm-14*' 'llvm-15*' 'llvm-16*' 'lld-12*' 'lld-13*' 'lld-14*' 'lld-15*' 'lld-16*' 'lld-17*' 'lld-18*' > /dev/null 2> /dev/null
# Install clang-XXX, lld-XXX, and debootstrap. # Install clang-XXX, lld-XXX, and debootstrap.
echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main" | echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main" |
sudo dd of=/etc/apt/sources.list.d/llvm-toolchain-jammy-18.list sudo dd of=/etc/apt/sources.list.d/llvm-toolchain-jammy-19.list
curl https://apt.llvm.org/llvm-snapshot.gpg.key | curl https://apt.llvm.org/llvm-snapshot.gpg.key |
gpg --dearmor | gpg --dearmor |
sudo dd of=/etc/apt/trusted.gpg.d/llvm-snapshot.gpg sudo dd of=/etc/apt/trusted.gpg.d/llvm-snapshot.gpg
sudo apt-get update sudo apt-get update
# this was unreliable sometimes, so try again if it fails # this was unreliable sometimes, so try again if it fails
sudo apt-get install --no-install-recommends clang-18 lld-18 clang-tools-18 clang-format-18 clang-tidy-18 || echo 'Failed. Trying again.' && sudo apt-get clean && sudo apt-get update && sudo apt-get install --no-install-recommends clang-18 lld-18 clang-tools-18 clang-format-18 clang-tidy-18 sudo apt-get install --no-install-recommends clang-19 lld-19 clang-tools-19 clang-format-19 clang-tidy-19 || echo 'Failed. Trying again.' && sudo apt-get clean && sudo apt-get update && sudo apt-get install --no-install-recommends clang-19 lld-19 clang-tools-19 clang-format-19 clang-tidy-19
# Fix alternatives # Fix alternatives
(yes '' | sudo update-alternatives --force --all) > /dev/null 2> /dev/null || true (yes '' | sudo update-alternatives --force --all) > /dev/null 2> /dev/null || true
echo "Decompressing sysroot..." echo "Decompressing sysroot..."
wget -q https://github.com/denoland/deno_sysroot_build/releases/download/sysroot-20240528/sysroot-`uname -m`.tar.xz -O /tmp/sysroot.tar.xz wget -q https://github.com/denoland/deno_sysroot_build/releases/download/sysroot-20241030/sysroot-`uname -m`.tar.xz -O /tmp/sysroot.tar.xz
cd / cd /
xzcat /tmp/sysroot.tar.xz | sudo tar -x xzcat /tmp/sysroot.tar.xz | sudo tar -x
sudo mount --rbind /dev /sysroot/dev sudo mount --rbind /dev /sysroot/dev
@ -305,8 +299,8 @@ jobs:
CARGO_PROFILE_RELEASE_LTO=false CARGO_PROFILE_RELEASE_LTO=false
RUSTFLAGS<<__1 RUSTFLAGS<<__1
-C linker-plugin-lto=true -C linker-plugin-lto=true
-C linker=clang-18 -C linker=clang-19
-C link-arg=-fuse-ld=lld-18 -C link-arg=-fuse-ld=lld-19
-C link-arg=-ldl -C link-arg=-ldl
-C link-arg=-Wl,--allow-shlib-undefined -C link-arg=-Wl,--allow-shlib-undefined
-C link-arg=-Wl,--thinlto-cache-dir=$(pwd)/target/release/lto-cache -C link-arg=-Wl,--thinlto-cache-dir=$(pwd)/target/release/lto-cache
@ -316,8 +310,8 @@ jobs:
__1 __1
RUSTDOCFLAGS<<__1 RUSTDOCFLAGS<<__1
-C linker-plugin-lto=true -C linker-plugin-lto=true
-C linker=clang-18 -C linker=clang-19
-C link-arg=-fuse-ld=lld-18 -C link-arg=-fuse-ld=lld-19
-C link-arg=-ldl -C link-arg=-ldl
-C link-arg=-Wl,--allow-shlib-undefined -C link-arg=-Wl,--allow-shlib-undefined
-C link-arg=-Wl,--thinlto-cache-dir=$(pwd)/target/release/lto-cache -C link-arg=-Wl,--thinlto-cache-dir=$(pwd)/target/release/lto-cache
@ -325,7 +319,7 @@ jobs:
--cfg tokio_unstable --cfg tokio_unstable
$RUSTFLAGS $RUSTFLAGS
__1 __1
CC=/usr/bin/clang-18 CC=/usr/bin/clang-19
CFLAGS=-flto=thin $CFLAGS CFLAGS=-flto=thin $CFLAGS
" > $GITHUB_ENV " > $GITHUB_ENV
- name: Remove macOS cURL --ipv4 flag - name: Remove macOS cURL --ipv4 flag
@ -367,8 +361,8 @@ jobs:
path: |- path: |-
~/.cargo/registry/index ~/.cargo/registry/index
~/.cargo/registry/cache ~/.cargo/registry/cache
key: '15-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}' key: '25-cargo-home-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles(''Cargo.lock'') }}'
restore-keys: '15-cargo-home-${{ matrix.os }}-${{ matrix.arch }}' restore-keys: '25-cargo-home-${{ matrix.os }}-${{ matrix.arch }}'
if: '!(matrix.skip)' if: '!(matrix.skip)'
- name: Restore cache build output (PR) - name: Restore cache build output (PR)
uses: actions/cache/restore@v4 uses: actions/cache/restore@v4
@ -381,7 +375,7 @@ jobs:
!./target/*/*.zip !./target/*/*.zip
!./target/*/*.tar.gz !./target/*/*.tar.gz
key: never_saved key: never_saved
restore-keys: '15-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-' restore-keys: '25-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-'
- name: Apply and update mtime cache - name: Apply and update mtime cache
if: '!(matrix.skip) && (!startsWith(github.ref, ''refs/tags/''))' if: '!(matrix.skip) && (!startsWith(github.ref, ''refs/tags/''))'
uses: ./.github/mtime_cache uses: ./.github/mtime_cache
@ -449,8 +443,10 @@ jobs:
run: |- run: |-
cd target/release cd target/release
zip -r deno-${{ matrix.arch }}-unknown-linux-gnu.zip deno 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 strip denort
zip -r denort-${{ matrix.arch }}-unknown-linux-gnu.zip 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 ./deno types > lib.deno.d.ts
- name: Pre-release (mac) - name: Pre-release (mac)
if: |- 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 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 cd target/release
zip -r deno-${{ matrix.arch }}-apple-darwin.zip deno 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 strip denort
zip -r denort-${{ matrix.arch }}-apple-darwin.zip 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) - name: Pre-release (windows)
if: |- if: |-
!(matrix.skip) && (matrix.os == 'windows' && !(matrix.skip) && (matrix.os == 'windows' &&
@ -477,7 +475,9 @@ jobs:
shell: pwsh shell: pwsh
run: |- run: |-
Compress-Archive -CompressionLevel Optimal -Force -Path target/release/deno.exe -DestinationPath target/release/deno-${{ matrix.arch }}-pc-windows-msvc.zip 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 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 - name: Upload canary to dl.deno.land
if: |- if: |-
!(matrix.skip) && (matrix.job == 'test' && !(matrix.skip) && (matrix.job == 'test' &&
@ -486,6 +486,7 @@ jobs:
github.ref == 'refs/heads/main') github.ref == 'refs/heads/main')
run: |- 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/*.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 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 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 - name: Autobahn testsuite
@ -615,7 +616,9 @@ jobs:
matrix.profile == 'release' && matrix.profile == 'release' &&
github.repository == 'denoland/deno' && github.repository == 'denoland/deno' &&
startsWith(github.ref, 'refs/tags/')) 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) - name: Upload release to dl.deno.land (windows)
if: |- if: |-
!(matrix.skip) && (matrix.os == 'windows' && !(matrix.skip) && (matrix.os == 'windows' &&
@ -625,7 +628,9 @@ jobs:
startsWith(github.ref, 'refs/tags/')) startsWith(github.ref, 'refs/tags/'))
env: env:
CLOUDSDK_PYTHON: '${{env.pythonLocation}}\python.exe' 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 - name: Create release notes
if: |- if: |-
!(matrix.skip) && (matrix.job == 'test' && !(matrix.skip) && (matrix.job == 'test' &&
@ -647,15 +652,25 @@ jobs:
with: with:
files: |- files: |-
target/release/deno-x86_64-pc-windows-msvc.zip 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
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
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
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
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
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
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
target/release/denort-aarch64-unknown-linux-gnu.zip.sha256sum
target/release/deno-aarch64-apple-darwin.zip 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
target/release/denort-aarch64-apple-darwin.zip.sha256sum
target/release/deno_src.tar.gz target/release/deno_src.tar.gz
target/release/lib.deno.d.ts target/release/lib.deno.d.ts
body_path: target/release/release-notes.md body_path: target/release/release-notes.md
@ -668,11 +683,12 @@ jobs:
./target ./target
!./target/*/gn_out !./target/*/gn_out
!./target/*/*.zip !./target/*/*.zip
!./target/*/*.sha256sum
!./target/*/*.tar.gz !./target/*/*.tar.gz
key: '15-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}' key: '25-cargo-target-${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.profile }}-${{ matrix.job }}-${{ github.sha }}'
publish-canary: publish-canary:
name: publish canary name: publish canary
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
needs: needs:
- build - build
if: github.repository == 'denoland/deno' && github.ref == 'refs/heads/main' if: github.repository == 'denoland/deno' && github.ref == 'refs/heads/main'

View file

@ -7,7 +7,7 @@ on:
jobs: jobs:
update-dl-version: update-dl-version:
name: update dl.deno.land version name: update dl.deno.land version
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
if: github.repository == 'denoland/deno' if: github.repository == 'denoland/deno'
steps: steps:
- name: Authenticate with Google Cloud - name: Authenticate with Google Cloud

View file

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

View file

@ -16,7 +16,7 @@ on:
jobs: jobs:
build: build:
name: start release name: start release
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
timeout-minutes: 30 timeout-minutes: 30
env: env:
@ -34,7 +34,7 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install deno - name: Install deno
uses: denoland/setup-deno@v1 uses: denoland/setup-deno@v2
with: with:
deno-version: v1.x deno-version: v1.x

View file

@ -16,7 +16,7 @@ on:
jobs: jobs:
build: build:
name: version bump name: version bump
runs-on: ubuntu-22.04 runs-on: ubuntu-24.04
timeout-minutes: 90 timeout-minutes: 90
env: env:
@ -39,7 +39,7 @@ jobs:
- uses: dsherret/rust-toolchain-file@v1 - uses: dsherret/rust-toolchain-file@v1
- name: Install deno - name: Install deno
uses: denoland/setup-deno@v1 uses: denoland/setup-deno@v2
with: with:
deno-version: v1.x deno-version: v1.x

View file

@ -20,7 +20,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
deno-version: [v1.x, canary] deno-version: [v1.x, canary]
os: [ubuntu-22.04-xl] os: [ubuntu-24.04-xl]
steps: steps:
- name: Clone repository - name: Clone repository
@ -30,7 +30,7 @@ jobs:
persist-credentials: false persist-credentials: false
- name: Setup Deno - name: Setup Deno
uses: denoland/setup-deno@v1 uses: denoland/setup-deno@v2
with: with:
deno-version: ${{ matrix.deno-version }} deno-version: ${{ matrix.deno-version }}

1191
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,6 @@ resolver = "2"
members = [ members = [
"bench_util", "bench_util",
"cli", "cli",
"cli/napi/sym",
"ext/broadcast_channel", "ext/broadcast_channel",
"ext/cache", "ext/cache",
"ext/canvas", "ext/canvas",
@ -19,15 +18,17 @@ members = [
"ext/io", "ext/io",
"ext/kv", "ext/kv",
"ext/napi", "ext/napi",
"ext/napi/sym",
"ext/net", "ext/net",
"ext/node", "ext/node",
"ext/node_resolver",
"ext/url", "ext/url",
"ext/web", "ext/web",
"ext/webgpu", "ext/webgpu",
"ext/webidl", "ext/webidl",
"ext/websocket", "ext/websocket",
"ext/webstorage", "ext/webstorage",
"resolvers/deno",
"resolvers/node",
"runtime", "runtime",
"runtime/permissions", "runtime/permissions",
"tests", "tests",
@ -44,48 +45,54 @@ license = "MIT"
repository = "https://github.com/denoland/deno" repository = "https://github.com/denoland/deno"
[workspace.dependencies] [workspace.dependencies]
deno_ast = { version = "=0.42.0", features = ["transpiling"] } deno_ast = { version = "=0.43.3", features = ["transpiling"] }
deno_core = { version = "0.311.0" } deno_core = { version = "0.319.0" }
deno_bench_util = { version = "0.162.0", path = "./bench_util" } deno_bench_util = { version = "0.171.0", path = "./bench_util" }
deno_config = { version = "=0.38.2", features = ["workspace", "sync"] }
deno_lockfile = "=0.23.1" deno_lockfile = "=0.23.1"
deno_media_type = { version = "0.1.4", features = ["module_specifier"] } deno_media_type = { version = "0.2.0", features = ["module_specifier"] }
deno_permissions = { version = "0.28.0", path = "./runtime/permissions" } deno_npm = "=0.25.4"
deno_runtime = { version = "0.177.0", path = "./runtime" } deno_path_util = "=0.2.1"
deno_semver = "=0.5.13" deno_permissions = { version = "0.37.0", path = "./runtime/permissions" }
deno_runtime = { version = "0.186.0", path = "./runtime" }
deno_semver = "=0.5.16"
deno_terminal = "0.2.0" deno_terminal = "0.2.0"
napi_sym = { version = "0.98.0", path = "./cli/napi/sym" } napi_sym = { version = "0.107.0", path = "./ext/napi/sym" }
test_util = { package = "test_server", path = "./tests/util/server" } test_util = { package = "test_server", path = "./tests/util/server" }
denokv_proto = "0.8.1" denokv_proto = "0.8.4"
denokv_remote = "0.8.1" denokv_remote = "0.8.4"
# denokv_sqlite brings in bundled sqlite if we don't disable the default features # denokv_sqlite brings in bundled sqlite if we don't disable the default features
denokv_sqlite = { default-features = false, version = "0.8.2" } denokv_sqlite = { default-features = false, version = "0.8.4" }
# exts # exts
deno_broadcast_channel = { version = "0.162.0", path = "./ext/broadcast_channel" } deno_broadcast_channel = { version = "0.171.0", path = "./ext/broadcast_channel" }
deno_cache = { version = "0.100.0", path = "./ext/cache" } deno_cache = { version = "0.109.0", path = "./ext/cache" }
deno_canvas = { version = "0.37.0", path = "./ext/canvas" } deno_canvas = { version = "0.46.0", path = "./ext/canvas" }
deno_console = { version = "0.168.0", path = "./ext/console" } deno_console = { version = "0.177.0", path = "./ext/console" }
deno_cron = { version = "0.48.0", path = "./ext/cron" } deno_cron = { version = "0.57.0", path = "./ext/cron" }
deno_crypto = { version = "0.182.0", path = "./ext/crypto" } deno_crypto = { version = "0.191.0", path = "./ext/crypto" }
deno_fetch = { version = "0.192.0", path = "./ext/fetch" } deno_fetch = { version = "0.201.0", path = "./ext/fetch" }
deno_ffi = { version = "0.155.0", path = "./ext/ffi" } deno_ffi = { version = "0.164.0", path = "./ext/ffi" }
deno_fs = { version = "0.78.0", path = "./ext/fs" } deno_fs = { version = "0.87.0", path = "./ext/fs" }
deno_http = { version = "0.166.0", path = "./ext/http" } deno_http = { version = "0.175.0", path = "./ext/http" }
deno_io = { version = "0.78.0", path = "./ext/io" } deno_io = { version = "0.87.0", path = "./ext/io" }
deno_kv = { version = "0.76.0", path = "./ext/kv" } deno_kv = { version = "0.85.0", path = "./ext/kv" }
deno_napi = { version = "0.99.0", path = "./ext/napi" } deno_napi = { version = "0.108.0", path = "./ext/napi" }
deno_net = { version = "0.160.0", path = "./ext/net" } deno_net = { version = "0.169.0", path = "./ext/net" }
deno_node = { version = "0.105.0", path = "./ext/node" } deno_node = { version = "0.114.0", path = "./ext/node" }
deno_tls = { version = "0.155.0", path = "./ext/tls" } deno_tls = { version = "0.164.0", path = "./ext/tls" }
deno_url = { version = "0.168.0", path = "./ext/url" } deno_url = { version = "0.177.0", path = "./ext/url" }
deno_web = { version = "0.199.0", path = "./ext/web" } deno_web = { version = "0.208.0", path = "./ext/web" }
deno_webgpu = { version = "0.135.0", path = "./ext/webgpu" } deno_webgpu = { version = "0.144.0", path = "./ext/webgpu" }
deno_webidl = { version = "0.168.0", path = "./ext/webidl" } deno_webidl = { version = "0.177.0", path = "./ext/webidl" }
deno_websocket = { version = "0.173.0", path = "./ext/websocket" } deno_websocket = { version = "0.182.0", path = "./ext/websocket" }
deno_webstorage = { version = "0.163.0", path = "./ext/webstorage" } deno_webstorage = { version = "0.172.0", path = "./ext/webstorage" }
node_resolver = { version = "0.7.0", path = "./ext/node_resolver" }
# resolvers
deno_resolver = { version = "0.9.0", path = "./resolvers/deno" }
node_resolver = { version = "0.16.0", path = "./resolvers/node" }
aes = "=0.8.3" aes = "=0.8.3"
anyhow = "1.0.57" anyhow = "1.0.57"
@ -100,11 +107,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. # Note: Do not use the "clock" feature of chrono, as it links us to CoreFoundation on macOS.
# Instead use util::time::utc_now() # Instead use util::time::utc_now()
chrono = { version = "0.4", default-features = false, features = ["std", "serde"] } chrono = { version = "0.4", default-features = false, features = ["std", "serde"] }
color-print = "0.3.5"
console_static_text = "=0.8.1" console_static_text = "=0.8.1"
dashmap = "5.5.3"
data-encoding = "2.3.3" data-encoding = "2.3.3"
data-url = "=0.3.0" data-url = "=0.3.0"
deno_cache_dir = "=0.11.1" deno_cache_dir = "=0.13.2"
deno_package_json = { version = "=0.1.1", default-features = false } deno_package_json = { version = "0.1.2", default-features = false }
dlopen2 = "0.6.1" dlopen2 = "0.6.1"
ecb = "=0.1.2" ecb = "=0.1.2"
elliptic-curve = { version = "0.13.4", features = ["alloc", "arithmetic", "ecdh", "std", "pem", "jwk"] } elliptic-curve = { version = "0.13.4", features = ["alloc", "arithmetic", "ecdh", "std", "pem", "jwk"] }
@ -129,11 +138,11 @@ hyper-util = { version = "=0.1.7", features = ["tokio", "client", "client-legacy
hyper_v014 = { package = "hyper", version = "0.14.26", features = ["runtime", "http1"] } hyper_v014 = { package = "hyper", version = "0.14.26", features = ["runtime", "http1"] }
indexmap = { version = "2", features = ["serde"] } indexmap = { version = "2", features = ["serde"] }
ipnet = "2.3" ipnet = "2.3"
jsonc-parser = { version = "=0.23.0", features = ["serde"] } jsonc-parser = { version = "=0.26.2", features = ["serde"] }
lazy-regex = "3" lazy-regex = "3"
libc = "0.2.126" 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" log = { version = "0.4.20", features = ["kv"] }
lsp-types = "=0.97.0" # used by tower-lsp and "proposed" feature is unstable in patch releases lsp-types = "=0.97.0" # used by tower-lsp and "proposed" feature is unstable in patch releases
memmem = "0.1.1" memmem = "0.1.1"
monch = "=0.5.0" monch = "=0.5.0"
@ -149,8 +158,8 @@ percent-encoding = "2.3.0"
phf = { version = "0.11", features = ["macros"] } phf = { version = "0.11", features = ["macros"] }
pin-project = "1.0.11" # don't pin because they yank crates from cargo pin-project = "1.0.11" # don't pin because they yank crates from cargo
pretty_assertions = "=1.4.0" pretty_assertions = "=1.4.0"
prost = "0.11" prost = "0.13"
prost-build = "0.11" prost-build = "0.13"
rand = "=0.8.5" rand = "=0.8.5"
regex = "^1.7.0" regex = "^1.7.0"
reqwest = { version = "=0.12.5", default-features = false, features = ["rustls-tls", "stream", "gzip", "brotli", "socks", "json", "http2"] } # pinned because of https://github.com/seanmonstar/reqwest/pull/1955 reqwest = { version = "=0.12.5", default-features = false, features = ["rustls-tls", "stream", "gzip", "brotli", "socks", "json", "http2"] } # pinned because of https://github.com/seanmonstar/reqwest/pull/1955
@ -179,7 +188,7 @@ tar = "=0.4.40"
tempfile = "3.4.0" tempfile = "3.4.0"
termcolor = "1.1.3" termcolor = "1.1.3"
thiserror = "1.0.61" thiserror = "1.0.61"
tokio = { version = "=1.36.0", features = ["full"] } tokio = { version = "1.36.0", features = ["full"] }
tokio-metrics = { version = "0.3.0", features = ["rt"] } tokio-metrics = { version = "0.3.0", features = ["rt"] }
tokio-rustls = { version = "0.26.0", default-features = false, features = ["ring", "tls12"] } tokio-rustls = { version = "0.26.0", default-features = false, features = ["ring", "tls12"] }
tokio-socks = "0.5.1" tokio-socks = "0.5.1"
@ -196,9 +205,15 @@ webpki-root-certs = "0.26.5"
webpki-roots = "0.26" webpki-roots = "0.26"
which = "4.2.5" which = "4.2.5"
yoke = { version = "0.7.4", features = ["derive"] } yoke = { version = "0.7.4", features = ["derive"] }
zeromq = { version = "=0.4.0", default-features = false, features = ["tcp-transport", "tokio-runtime"] } zeromq = { version = "=0.4.1", default-features = false, features = ["tcp-transport", "tokio-runtime"] }
zstd = "=0.12.4" zstd = "=0.12.4"
opentelemetry = "0.27.0"
opentelemetry-http = "0.27.0"
opentelemetry-otlp = { version = "0.27.0", features = ["logs", "http-proto", "http-json"] }
opentelemetry-semantic-conventions = { version = "0.27.0", features = ["semconv_experimental"] }
opentelemetry_sdk = "0.27.0"
# crypto # crypto
hkdf = "0.12.3" hkdf = "0.12.3"
rsa = { version = "0.9.3", default-features = false, features = ["std", "pem", "hazmat"] } # hazmat needed for PrehashSigner in ext/node rsa = { version = "0.9.3", default-features = false, features = ["std", "pem", "hazmat"] } # hazmat needed for PrehashSigner in ext/node
@ -213,15 +228,14 @@ quote = "1"
syn = { version = "2", features = ["full", "extra-traits"] } syn = { version = "2", features = ["full", "extra-traits"] }
# unix # unix
nix = "=0.26.2" nix = "=0.27.1"
# windows deps # windows deps
junction = "=0.2.0" junction = "=0.2.0"
winapi = "=0.3.9" 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" winres = "=0.1.12"
# NB: the `bench` and `release` profiles must remain EXACTLY the same.
[profile.release] [profile.release]
codegen-units = 1 codegen-units = 1
incremental = true incremental = true
@ -239,13 +253,6 @@ inherits = "release"
codegen-units = 128 codegen-units = 128
lto = "thin" 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` # Key generation is too slow on `debug`
[profile.dev.package.num-bigint-dig] [profile.dev.package.num-bigint-dig]
opt-level = 3 opt-level = 3
@ -254,80 +261,6 @@ opt-level = 3
[profile.dev.package.v8] [profile.dev.package.v8]
opt-level = 1 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] [profile.release.package.async-compression]
opt-level = 3 opt-level = 3
[profile.release.package.base64-simd] [profile.release.package.base64-simd]
@ -386,6 +319,8 @@ opt-level = 3
opt-level = 3 opt-level = 3
[profile.release.package.serde_v8] [profile.release.package.serde_v8]
opt-level = 3 opt-level = 3
[profile.release.package.libsui]
opt-level = 3
[profile.release.package.test_napi] [profile.release.package.test_napi]
opt-level = 3 opt-level = 3
[profile.release.package.tokio] [profile.release.package.tokio]

View file

@ -46,6 +46,12 @@ brew install deno
choco install deno choco install deno
``` ```
[WinGet](https://winstall.app/apps/DenoLand.Deno) (Windows):
```powershell
winget install --id=DenoLand.Deno
```
### Build and install from source ### Build and install from source
Complete instructions for building Deno from source can be found in the manual Complete instructions for building Deno from source can be found in the manual

View file

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

View file

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

View file

@ -2,7 +2,7 @@
[package] [package]
name = "deno" name = "deno"
version = "2.0.0-rc.7" version = "2.0.6"
authors.workspace = true authors.workspace = true
default-run = "deno" default-run = "deno"
edition.workspace = true edition.workspace = true
@ -38,6 +38,11 @@ path = "./bench/lsp_bench_standalone.rs"
[features] [features]
default = ["upgrade", "__vendored_zlib_ng"] 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 # A feature that enables the upgrade subcommand and the background check for
# available updates (of deno binary). This is typically disabled for (Linux) # available updates (of deno binary). This is typically disabled for (Linux)
# distribution packages. # distribution packages.
@ -64,27 +69,26 @@ winres.workspace = true
[dependencies] [dependencies]
deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] } deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] }
deno_cache_dir = { workspace = true } deno_cache_dir.workspace = true
deno_config = { version = "=0.35.0", features = ["workspace", "sync"] } deno_config.workspace = true
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
deno_doc = { version = "0.150.0", features = ["html", "syntect"] } deno_doc = { version = "0.156.0", default-features = false, features = ["rust", "html", "syntect"] }
deno_graph = { version = "=0.82.3" } deno_graph = { version = "=0.84.1" }
deno_lint = { version = "=0.67.0", features = ["docs"] } deno_lint = { version = "=0.68.0", features = ["docs"] }
deno_lockfile.workspace = true deno_lockfile.workspace = true
deno_npm = "=0.25.2" deno_npm.workspace = true
deno_package_json.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_runtime = { workspace = true, features = ["include_js_files_for_snapshotting"] }
deno_semver.workspace = true deno_semver.workspace = true
deno_task_shell = "=0.17.0" deno_task_shell = "=0.18.1"
deno_terminal.workspace = true deno_terminal.workspace = true
eszip = "=0.78.0" libsui = "0.5.0"
libsui = "0.3.1"
napi_sym.workspace = true
node_resolver.workspace = true node_resolver.workspace = true
anstream = "0.6.14" anstream = "0.6.14"
async-trait.workspace = true async-trait.workspace = true
base32.workspace = true
base64.workspace = true base64.workspace = true
bincode = "=1.3.3" bincode = "=1.3.3"
bytes.workspace = true bytes.workspace = true
@ -93,16 +97,17 @@ chrono = { workspace = true, features = ["now"] }
clap = { version = "=4.5.16", features = ["env", "string", "wrap_help", "error-context"] } clap = { version = "=4.5.16", features = ["env", "string", "wrap_help", "error-context"] }
clap_complete = "=4.5.24" clap_complete = "=4.5.24"
clap_complete_fig = "=4.5.2" clap_complete_fig = "=4.5.2"
color-print = "0.3.5" color-print.workspace = true
console_static_text.workspace = true console_static_text.workspace = true
dashmap = "5.5.3" dashmap.workspace = true
data-encoding.workspace = true data-encoding.workspace = true
dhat = { version = "0.3.3", optional = true }
dissimilar = "=1.0.4" dissimilar = "=1.0.4"
dotenvy = "0.15.7" dotenvy = "0.15.7"
dprint-plugin-json = "=0.19.3" dprint-plugin-json = "=0.19.4"
dprint-plugin-jupyter = "=0.1.3" dprint-plugin-jupyter = "=0.1.5"
dprint-plugin-markdown = "=0.17.8" dprint-plugin-markdown = "=0.17.8"
dprint-plugin-typescript = "=0.93.0" dprint-plugin-typescript = "=0.93.2"
env_logger = "=0.10.0" env_logger = "=0.10.0"
fancy-regex = "=0.10.0" fancy-regex = "=0.10.0"
faster-hex.workspace = true faster-hex.workspace = true
@ -116,15 +121,15 @@ http-body-util.workspace = true
hyper-util.workspace = true hyper-util.workspace = true
import_map = { version = "=0.20.1", features = ["ext"] } import_map = { version = "=0.20.1", features = ["ext"] }
indexmap.workspace = true indexmap.workspace = true
jsonc-parser.workspace = true jsonc-parser = { workspace = true, features = ["cst", "serde"] }
jupyter_runtime = { package = "runtimelib", version = "=0.14.0" } jupyter_runtime = { package = "runtimelib", version = "=0.19.0", features = ["tokio-runtime"] }
lazy-regex.workspace = true lazy-regex.workspace = true
libc.workspace = true libc.workspace = true
libz-sys.workspace = true libz-sys.workspace = true
log = { workspace = true, features = ["serde"] } log = { workspace = true, features = ["serde"] }
lsp-types.workspace = true lsp-types.workspace = true
malva = "=0.10.1" malva = "=0.11.0"
markup_fmt = "=0.13.1" markup_fmt = "=0.15.0"
memmem.workspace = true memmem.workspace = true
monch.workspace = true monch.workspace = true
notify.workspace = true notify.workspace = true
@ -161,7 +166,6 @@ typed-arena = "=2.0.2"
uuid = { workspace = true, features = ["serde"] } uuid = { workspace = true, features = ["serde"] }
walkdir = "=2.3.2" walkdir = "=2.3.2"
which.workspace = true which.workspace = true
yoke.workspace = true
zeromq.workspace = true zeromq.workspace = true
zip = { version = "2.1.6", default-features = false, features = ["deflate-flate2"] } zip = { version = "2.1.6", default-features = false, features = ["deflate-flate2"] }
zstd.workspace = true zstd.workspace = true

View file

@ -29,13 +29,14 @@ use deno_config::glob::PathOrPatternSet;
use deno_core::anyhow::bail; use deno_core::anyhow::bail;
use deno_core::anyhow::Context; use deno_core::anyhow::Context;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::normalize_path;
use deno_core::resolve_url_or_path; use deno_core::resolve_url_or_path;
use deno_core::url::Url; use deno_core::url::Url;
use deno_graph::GraphKind; use deno_graph::GraphKind;
use deno_runtime::deno_permissions::parse_sys_kind; use deno_path_util::normalize_path;
use deno_path_util::url_to_file_path;
use deno_runtime::deno_permissions::PermissionsOptions; use deno_runtime::deno_permissions::PermissionsOptions;
use deno_runtime::fs_util::specifier_to_file_path; use deno_runtime::deno_permissions::SysDescriptor;
use deno_runtime::ops::otel::OtelConfig;
use log::debug; use log::debug;
use log::Level; use log::Level;
use serde::Deserialize; use serde::Deserialize;
@ -575,11 +576,20 @@ fn parse_packages_allowed_scripts(s: &str) -> Result<String, AnyError> {
pub struct UnstableConfig { pub struct UnstableConfig {
// TODO(bartlomieju): remove in Deno 2.5 // TODO(bartlomieju): remove in Deno 2.5
pub legacy_flag_enabled: bool, // --unstable pub legacy_flag_enabled: bool, // --unstable
pub bare_node_builtins: bool, // --unstable-bare-node-builts pub bare_node_builtins: bool,
pub sloppy_imports: bool, pub sloppy_imports: bool,
pub features: Vec<String>, // --unstabe-kv --unstable-cron pub features: Vec<String>, // --unstabe-kv --unstable-cron
} }
#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct InternalFlags {
/// Used when the language server is configured with an
/// explicit cache option.
pub cache_path: Option<PathBuf>,
/// Only reads to the lockfile instead of writing to it.
pub lockfile_skip_write: bool,
}
#[derive(Clone, Debug, Eq, PartialEq, Default)] #[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct Flags { pub struct Flags {
/// Vector of CLI arguments - these are user script arguments, all Deno /// Vector of CLI arguments - these are user script arguments, all Deno
@ -591,9 +601,6 @@ pub struct Flags {
pub ca_stores: Option<Vec<String>>, pub ca_stores: Option<Vec<String>>,
pub ca_data: Option<CaData>, pub ca_data: Option<CaData>,
pub cache_blocklist: Vec<String>, pub cache_blocklist: Vec<String>,
/// This is not exposed as an option in the CLI, it is used internally when
/// the language server is configured with an explicit cache option.
pub cache_path: Option<PathBuf>,
pub cached_only: bool, pub cached_only: bool,
pub type_check_mode: TypeCheckMode, pub type_check_mode: TypeCheckMode,
pub config_flag: ConfigFlag, pub config_flag: ConfigFlag,
@ -602,6 +609,8 @@ pub struct Flags {
pub enable_op_summary_metrics: bool, pub enable_op_summary_metrics: bool,
pub enable_testing_features: bool, pub enable_testing_features: bool,
pub ext: Option<String>, pub ext: Option<String>,
/// Flags that aren't exposed in the CLI, but are used internally.
pub internal: InternalFlags,
pub ignore: Vec<String>, pub ignore: Vec<String>,
pub import_map_path: Option<String>, pub import_map_path: Option<String>,
pub env_file: Option<String>, pub env_file: Option<String>,
@ -688,9 +697,10 @@ impl PermissionFlags {
} }
let builtin_allowed_import_hosts = [ let builtin_allowed_import_hosts = [
"jsr.io:443",
"deno.land:443", "deno.land:443",
"esm.sh:443", "esm.sh:443",
"jsr.io:443", "cdn.jsdelivr.net:443",
"raw.githubusercontent.com:443", "raw.githubusercontent.com:443",
"gist.githubusercontent.com:443", "gist.githubusercontent.com:443",
]; ];
@ -958,6 +968,24 @@ impl Flags {
args args
} }
pub fn otel_config(&self) -> Option<OtelConfig> {
if self
.unstable_config
.features
.contains(&String::from("otel"))
{
Some(OtelConfig {
runtime_name: Cow::Borrowed("deno"),
runtime_version: Cow::Borrowed(crate::version::DENO_VERSION_INFO.deno),
deterministic: std::env::var("DENO_UNSTABLE_OTEL_DETERMINISTIC")
.is_ok(),
..Default::default()
})
} else {
None
}
}
/// Extract the paths the config file should be discovered from. /// Extract the paths the config file should be discovered from.
/// ///
/// Returns `None` if the config file should not be auto-discovered. /// Returns `None` if the config file should not be auto-discovered.
@ -1002,7 +1030,7 @@ impl Flags {
if module_specifier.scheme() == "file" if module_specifier.scheme() == "file"
|| module_specifier.scheme() == "npm" || module_specifier.scheme() == "npm"
{ {
if let Ok(p) = specifier_to_file_path(&module_specifier) { if let Ok(p) = url_to_file_path(&module_specifier) {
Some(vec![p.parent().unwrap().to_path_buf()]) Some(vec![p.parent().unwrap().to_path_buf()])
} else { } else {
Some(vec![current_dir.to_path_buf()]) Some(vec![current_dir.to_path_buf()])
@ -1168,15 +1196,14 @@ static DENO_HELP: &str = cstr!(
<y>Dependency management:</> <y>Dependency management:</>
<g>add</> Add dependencies <g>add</> Add dependencies
<p(245)>deno add @std/assert | deno add npm:express</> <p(245)>deno add jsr:@std/assert | deno add npm:express</>
<g>install</> Install script as an executable <g>install</> Installs dependencies either in the local project or globally to a bin directory
<g>uninstall</> Uninstall a script previously installed with deno install <g>uninstall</> Uninstalls a dependency or an executable script in the installation root's bin directory
<g>remove</> Remove dependencies from the configuration file <g>remove</> Remove dependencies from the configuration file
<y>Tooling:</> <y>Tooling:</>
<g>bench</> Run benchmarks <g>bench</> Run benchmarks
<p(245)>deno bench bench.ts</> <p(245)>deno bench bench.ts</>
<g>cache</> Cache the dependencies
<g>check</> Type-check the dependencies <g>check</> Type-check the dependencies
<g>clean</> Remove the cache directory <g>clean</> Remove the cache directory
<g>compile</> Compile the script into a self contained executable <g>compile</> Compile the script into a self contained executable
@ -1258,28 +1285,7 @@ pub fn flags_from_vec(args: Vec<OsString>) -> clap::error::Result<Flags> {
.get_arguments() .get_arguments()
.any(|arg| arg.get_id().as_str() == "unstable") .any(|arg| arg.get_id().as_str() == "unstable")
{ {
subcommand = subcommand subcommand = enable_unstable(subcommand);
.mut_arg("unstable", |arg| {
let new_help = arg
.get_help()
.unwrap()
.to_string()
.split_once("\n")
.unwrap()
.0
.to_string();
arg.help_heading(UNSTABLE_HEADING).help(new_help)
})
.mut_args(|arg| {
// long_help here is being used as a metadata, see unstable args definition
if arg.get_help_heading() == Some(UNSTABLE_HEADING)
&& arg.get_long_help().is_some()
{
arg.hide(false)
} else {
arg
}
});
} }
help_parse(&mut flags, subcommand); help_parse(&mut flags, subcommand);
@ -1355,7 +1361,7 @@ pub fn flags_from_vec(args: Vec<OsString>) -> clap::error::Result<Flags> {
} }
match subcommand.as_str() { match subcommand.as_str() {
"add" => add_parse(&mut flags, &mut m), "add" => add_parse(&mut flags, &mut m)?,
"remove" => remove_parse(&mut flags, &mut m), "remove" => remove_parse(&mut flags, &mut m),
"bench" => bench_parse(&mut flags, &mut m)?, "bench" => bench_parse(&mut flags, &mut m)?,
"bundle" => bundle_parse(&mut flags, &mut m), "bundle" => bundle_parse(&mut flags, &mut m),
@ -1414,6 +1420,31 @@ pub fn flags_from_vec(args: Vec<OsString>) -> clap::error::Result<Flags> {
Ok(flags) Ok(flags)
} }
fn enable_unstable(command: Command) -> Command {
command
.mut_arg("unstable", |arg| {
let new_help = arg
.get_help()
.unwrap()
.to_string()
.split_once("\n")
.unwrap()
.0
.to_string();
arg.help_heading(UNSTABLE_HEADING).help(new_help)
})
.mut_args(|arg| {
// long_help here is being used as a metadata, see unstable args definition
if arg.get_help_heading() == Some(UNSTABLE_HEADING)
&& arg.get_long_help().is_some()
{
arg.hide(false)
} else {
arg
}
})
}
macro_rules! heading { macro_rules! heading {
($($name:ident = $title:expr),+; $total:literal) => { ($($name:ident = $title:expr),+; $total:literal) => {
$(const $name: &str = $title;)+ $(const $name: &str = $title;)+
@ -1516,7 +1547,7 @@ pub fn clap_root() -> Command {
); );
run_args(Command::new("deno"), true) run_args(Command::new("deno"), true)
.args(unstable_args(UnstableArgsConfig::ResolutionAndRuntime)) .with_unstable_args(UnstableArgsConfig::ResolutionAndRuntime)
.next_line_help(false) .next_line_help(false)
.bin_name("deno") .bin_name("deno")
.styles( .styles(
@ -1618,7 +1649,7 @@ fn command(
) -> Command { ) -> Command {
Command::new(name) Command::new(name)
.about(about) .about(about)
.args(unstable_args(unstable_args_config)) .with_unstable_args(unstable_args_config)
} }
fn help_subcommand(app: &Command) -> Command { fn help_subcommand(app: &Command) -> Command {
@ -1646,10 +1677,10 @@ fn add_subcommand() -> Command {
"add", "add",
cstr!( cstr!(
"Add dependencies to your configuration file. "Add dependencies to your configuration file.
<p(245)>deno add @std/path</> <p(245)>deno add jsr:@std/path</>
You can add multiple dependencies at once: You can add multiple dependencies at once:
<p(245)>deno add @std/path @std/assert</>" <p(245)>deno add jsr:@std/path jsr:@std/assert</>"
), ),
UnstableArgsConfig::None, UnstableArgsConfig::None,
) )
@ -1663,6 +1694,7 @@ You can add multiple dependencies at once:
.action(ArgAction::Append), .action(ArgAction::Append),
) )
.arg(add_dev_arg()) .arg(add_dev_arg())
.arg(allow_scripts_arg())
}) })
} }
@ -1705,7 +1737,7 @@ If you specify a directory instead of a file, the path is expanded to all contai
UnstableArgsConfig::ResolutionAndRuntime, UnstableArgsConfig::ResolutionAndRuntime,
) )
.defer(|cmd| { .defer(|cmd| {
runtime_args(cmd, true, false) runtime_args(cmd, true, false, true)
.arg(check_arg(true)) .arg(check_arg(true))
.arg( .arg(
Arg::new("json") Arg::new("json")
@ -1842,6 +1874,7 @@ Unless --reload is specified, this command will not re-download already cached d
.required_unless_present("help") .required_unless_present("help")
.value_hint(ValueHint::FilePath), .value_hint(ValueHint::FilePath),
) )
.arg(frozen_lockfile_arg())
.arg(allow_import_arg()) .arg(allow_import_arg())
} }
) )
@ -1852,11 +1885,15 @@ fn compile_subcommand() -> Command {
"compile", "compile",
cstr!("Compiles the given script into a self contained executable. cstr!("Compiles the given script into a self contained executable.
<p(245)>deno compile -A jsr:@std/http/file-server</> <p(245)>deno compile --allow-read --allow-net jsr:@std/http/file-server</>
<p(245)>deno compile --output file_server jsr:@std/http/file-server</> <p(245)>deno compile --output file_server jsr:@std/http/file-server</>
Any flags specified which affect runtime behavior will be applied to the resulting binary. Any flags specified which affect runtime behavior will be applied to the resulting binary.
This allows distribution of a Deno application to systems that do not have Deno installed.
Under the hood, it bundles a slimmed down version of the Deno runtime along with your
JavaScript or TypeScript code.
Cross-compiling to different target architectures is supported using the <c>--target</> flag. Cross-compiling to different target architectures is supported using the <c>--target</> flag.
On the first invocation with deno will download the proper binary and cache it in <c>$DENO_DIR</>. On the first invocation with deno will download the proper binary and cache it in <c>$DENO_DIR</>.
@ -1865,7 +1902,7 @@ On the first invocation with deno will download the proper binary and cache it i
UnstableArgsConfig::ResolutionAndRuntime, UnstableArgsConfig::ResolutionAndRuntime,
) )
.defer(|cmd| { .defer(|cmd| {
runtime_args(cmd, true, false) runtime_args(cmd, true, false, true)
.arg(check_arg(true)) .arg(check_arg(true))
.arg( .arg(
Arg::new("include") Arg::new("include")
@ -2186,7 +2223,7 @@ This command has implicit access to all permissions.
UnstableArgsConfig::ResolutionAndRuntime, UnstableArgsConfig::ResolutionAndRuntime,
) )
.defer(|cmd| { .defer(|cmd| {
runtime_args(cmd, false, true) runtime_args(cmd, false, true, true)
.arg(check_arg(false)) .arg(check_arg(false))
.arg(executable_ext_arg()) .arg(executable_ext_arg())
.arg( .arg(
@ -2223,6 +2260,9 @@ Supported file types which are behind corresponding unstable flags (see formatti
Format stdin and write to stdout: Format stdin and write to stdout:
<p(245)>cat file.ts | deno fmt -</> <p(245)>cat file.ts | deno fmt -</>
Check if the files are formatted:
<p(245)>deno fmt --check</>
Ignore formatting code by preceding it with an ignore comment: Ignore formatting code by preceding it with an ignore comment:
<p(245)>// deno-fmt-ignore</> <p(245)>// deno-fmt-ignore</>
@ -2252,7 +2292,7 @@ Ignore formatting a file by adding an ignore comment at the top of the file:
"sass", "less", "html", "svelte", "vue", "astro", "yml", "yaml", "sass", "less", "html", "svelte", "vue", "astro", "yml", "yaml",
"ipynb", "ipynb",
]) ])
.help_heading(FMT_HEADING), .help_heading(FMT_HEADING).requires("files"),
) )
.arg( .arg(
Arg::new("ignore") Arg::new("ignore")
@ -2373,7 +2413,7 @@ Ignore formatting a file by adding an ignore comment at the top of the file:
} }
fn init_subcommand() -> Command { fn init_subcommand() -> Command {
command("init", "Initialize a new project", UnstableArgsConfig::None).defer( command("init", "scaffolds a basic Deno project with a script, test, and configuration file", UnstableArgsConfig::None).defer(
|cmd| { |cmd| {
cmd cmd
.arg(Arg::new("dir").value_hint(ValueHint::DirPath)) .arg(Arg::new("dir").value_hint(ValueHint::DirPath))
@ -2418,7 +2458,7 @@ The following information is shown:
.arg( .arg(
location_arg() location_arg()
.conflicts_with("file") .conflicts_with("file")
.help("Show files used for origin bound APIs like the Web Storage API when running a script with '--location=<HREF>'") .help(cstr!("Show files used for origin bound APIs like the Web Storage API when running a script with <c>--location=<<HREF>></>"))
) )
.arg(no_check_arg().hide(true)) // TODO(lucacasonato): remove for 2.0 .arg(no_check_arg().hide(true)) // TODO(lucacasonato): remove for 2.0
.arg(no_config_arg()) .arg(no_config_arg())
@ -2449,7 +2489,7 @@ in the package cache. If no dependency is specified, installs all dependencies l
If the <p(245)>--entrypoint</> flag is passed, installs the dependencies of the specified entrypoint(s). If the <p(245)>--entrypoint</> flag is passed, installs the dependencies of the specified entrypoint(s).
<p(245)>deno install</> <p(245)>deno install</>
<p(245)>deno install @std/bytes</> <p(245)>deno install jsr:@std/bytes</>
<p(245)>deno install npm:chalk</> <p(245)>deno install npm:chalk</>
<p(245)>deno install --entrypoint entry1.ts entry2.ts</> <p(245)>deno install --entrypoint entry1.ts entry2.ts</>
@ -2460,7 +2500,7 @@ If the <bold>--global</> flag is set, installs a script as an executable in the
<p(245)>deno install --global --allow-net --allow-read jsr:@std/http/file-server</> <p(245)>deno install --global --allow-net --allow-read jsr:@std/http/file-server</>
<p(245)>deno install -g https://examples.deno.land/color-logging.ts</> <p(245)>deno install -g https://examples.deno.land/color-logging.ts</>
To change the executable name, use -n/--name: To change the executable name, use <c>-n</>/<c>--name</>:
<p(245)>deno install -g --allow-net --allow-read -n serve jsr:@std/http/file-server</> <p(245)>deno install -g --allow-net --allow-read -n serve jsr:@std/http/file-server</>
The executable name is inferred by default: The executable name is inferred by default:
@ -2482,7 +2522,7 @@ The installation root is determined, in order of precedence:
These must be added to the path manually if required."), UnstableArgsConfig::ResolutionAndRuntime) These must be added to the path manually if required."), UnstableArgsConfig::ResolutionAndRuntime)
.visible_alias("i") .visible_alias("i")
.defer(|cmd| { .defer(|cmd| {
permission_args(runtime_args(cmd, false, true), Some("global")) permission_args(runtime_args(cmd, false, true, false), Some("global"))
.arg(check_arg(true)) .arg(check_arg(true))
.arg(allow_scripts_arg()) .arg(allow_scripts_arg())
.arg( .arg(
@ -2742,9 +2782,19 @@ To ignore linting on an entire file, you can add an ignore comment at the top of
} }
fn repl_subcommand() -> Command { fn repl_subcommand() -> Command {
command("repl", "Read Eval Print Loop", UnstableArgsConfig::ResolutionAndRuntime) command("repl", cstr!(
.defer(|cmd| runtime_args(cmd, true, true) "Starts a read-eval-print-loop, which lets you interactively build up program state in the global context.
.arg(check_arg(false)) It is especially useful for quick prototyping and checking snippets of code.
TypeScript is supported, however it is not type-checked, only transpiled."
), UnstableArgsConfig::ResolutionAndRuntime)
.defer(|cmd| {
let cmd = compile_args_without_check_args(cmd);
let cmd = inspect_args(cmd);
let cmd = permission_args(cmd, None);
let cmd = runtime_misc_args(cmd);
cmd
.arg( .arg(
Arg::new("eval-file") Arg::new("eval-file")
.long("eval-file") .long("eval-file")
@ -2763,7 +2813,7 @@ fn repl_subcommand() -> Command {
.after_help(cstr!("<y>Environment variables:</> .after_help(cstr!("<y>Environment variables:</>
<g>DENO_REPL_HISTORY</> Set REPL history file path. History file is disabled when the value is empty. <g>DENO_REPL_HISTORY</> Set REPL history file path. History file is disabled when the value is empty.
<p(245)>[default: $DENO_DIR/deno_history.txt]</>")) <p(245)>[default: $DENO_DIR/deno_history.txt]</>"))
) })
.arg(env_file_arg()) .arg(env_file_arg())
.arg( .arg(
Arg::new("args") Arg::new("args")
@ -2775,7 +2825,7 @@ fn repl_subcommand() -> Command {
} }
fn run_args(command: Command, top_level: bool) -> Command { fn run_args(command: Command, top_level: bool) -> Command {
runtime_args(command, true, true) runtime_args(command, true, true, true)
.arg(check_arg(false)) .arg(check_arg(false))
.arg(watch_arg(true)) .arg(watch_arg(true))
.arg(hmr_arg(true)) .arg(hmr_arg(true))
@ -2825,19 +2875,17 @@ fn serve_subcommand() -> Command {
The serve command uses the default exports of the main module to determine which servers to start. The serve command uses the default exports of the main module to determine which servers to start.
See https://docs.deno.com/runtime/manual/tools/serve for more detailed information.
Start a server defined in server.ts: Start a server defined in server.ts:
<p(245)>deno serve server.ts</> <p(245)>deno serve server.ts</>
Start a server defined in server.ts, watching for changes and running on port 5050: Start a server defined in server.ts, watching for changes and running on port 5050:
<p(245)>deno serve --watch --port 5050 server.ts</> <p(245)>deno serve --watch --port 5050 server.ts</>
<y>Read more:</> <c>https://docs.deno.com/go/serve</>"), UnstableArgsConfig::ResolutionAndRuntime), true, true) <y>Read more:</> <c>https://docs.deno.com/go/serve</>"), UnstableArgsConfig::ResolutionAndRuntime), true, true, true)
.arg( .arg(
Arg::new("port") Arg::new("port")
.long("port") .long("port")
.help("The TCP port to serve on, defaulting to 8000. Pass 0 to pick a random free port") .help(cstr!("The TCP port to serve on. Pass 0 to pick a random free port <p(245)>[default: 8000]</>"))
.value_parser(value_parser!(u16)), .value_parser(value_parser!(u16)),
) )
.arg( .arg(
@ -2888,6 +2936,7 @@ List all available tasks:
.help("Specify the directory to run the task in") .help("Specify the directory to run the task in")
.value_hint(ValueHint::DirPath), .value_hint(ValueHint::DirPath),
) )
.arg(node_modules_dir_arg())
}) })
} }
@ -2906,7 +2955,7 @@ or <c>**/__tests__/**</>:
UnstableArgsConfig::ResolutionAndRuntime UnstableArgsConfig::ResolutionAndRuntime
) )
.defer(|cmd| .defer(|cmd|
runtime_args(cmd, true, true) runtime_args(cmd, true, true, true)
.arg(check_arg(true)) .arg(check_arg(true))
.arg( .arg(
Arg::new("ignore") Arg::new("ignore")
@ -3047,11 +3096,13 @@ fn parallel_arg(descr: &str) -> Arg {
fn types_subcommand() -> Command { fn types_subcommand() -> Command {
command( command(
"types", "types",
cstr!(
"Print runtime TypeScript declarations. "Print runtime TypeScript declarations.
<p(245)>deno types > lib.deno.d.ts</> <p(245)>deno types > lib.deno.d.ts</>
The declaration file could be saved and used for typing information.", The declaration file could be saved and used for typing information."
),
UnstableArgsConfig::None, UnstableArgsConfig::None,
) )
} }
@ -3161,7 +3212,7 @@ See the Deno 1.x to 2.x Migration Guide for migration instructions: https://docs
} }
fn publish_subcommand() -> Command { fn publish_subcommand() -> Command {
command("publish", "Publish the current working directory's package or workspace", UnstableArgsConfig::ResolutionOnly) command("publish", "Publish the current working directory's package or workspace to JSR", UnstableArgsConfig::ResolutionOnly)
.defer(|cmd| { .defer(|cmd| {
cmd cmd
.arg( .arg(
@ -3238,7 +3289,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
<g>-W, --allow-write[=<<PATH>...]</> Allow file system write access. Optionally specify allowed paths. <g>-W, --allow-write[=<<PATH>...]</> Allow file system write access. Optionally specify allowed paths.
<p(245)>--allow-write | --allow-write="/etc,/var/log.txt"</> <p(245)>--allow-write | --allow-write="/etc,/var/log.txt"</>
<g>-I, --allow-import[=<<IP_OR_HOSTNAME>...]</> Allow importing from remote hosts. Optionally specify allowed IP addresses and host names, with ports as necessary. <g>-I, --allow-import[=<<IP_OR_HOSTNAME>...]</> Allow importing from remote hosts. Optionally specify allowed IP addresses and host names, with ports as necessary.
Default value: <p(245)>deno.land:443,jsr.io:443,esm.sh:443,raw.githubusercontent.com:443,user.githubusercontent.com:443</> Default value: <p(245)>deno.land:443,jsr.io:443,esm.sh:443,cdn.jsdelivr.net:443,raw.githubusercontent.com:443,user.githubusercontent.com:443</>
<p(245)>--allow-import | --allow-import="example.com,github.com"</> <p(245)>--allow-import | --allow-import="example.com,github.com"</>
<g>-N, --allow-net[=<<IP_OR_HOSTNAME>...]</> Allow network access. Optionally specify allowed IP addresses and host names, with ports as necessary. <g>-N, --allow-net[=<<IP_OR_HOSTNAME>...]</> Allow network access. Optionally specify allowed IP addresses and host names, with ports as necessary.
<p(245)>--allow-net | --allow-net="localhost:8080,deno.land"</> <p(245)>--allow-net | --allow-net="localhost:8080,deno.land"</>
@ -3258,7 +3309,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
<p(245)>--deny-net | --deny-net="localhost:8080,deno.land"</> <p(245)>--deny-net | --deny-net="localhost:8080,deno.land"</>
<g> --deny-env[=<<VARIABLE_NAME>...]</> Deny access to environment variables. Optionally specify inacessible environment variables. <g> --deny-env[=<<VARIABLE_NAME>...]</> Deny access to environment variables. Optionally specify inacessible environment variables.
<p(245)>--deny-env | --deny-env="PORT,HOME,PATH"</> <p(245)>--deny-env | --deny-env="PORT,HOME,PATH"</>
<g>-S, --deny-sys[=<<API_NAME>...]</> Deny access to OS information. Optionally deny specific APIs by function name. <g> --deny-sys[=<<API_NAME>...]</> Deny access to OS information. Optionally deny specific APIs by function name.
<p(245)>--deny-sys | --deny-sys="systemMemoryInfo,osRelease"</> <p(245)>--deny-sys | --deny-sys="systemMemoryInfo,osRelease"</>
<g>--deny-run[=<<PROGRAM_NAME>...]</> Deny running subprocesses. Optionally specify denied runnable program names. <g>--deny-run[=<<PROGRAM_NAME>...]</> Deny running subprocesses. Optionally specify denied runnable program names.
<p(245)>--deny-run | --deny-run="whoami,ps"</> <p(245)>--deny-run | --deny-run="whoami,ps"</>
@ -3355,8 +3406,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
.value_name("IP_OR_HOSTNAME") .value_name("IP_OR_HOSTNAME")
.help("Allow network access. Optionally specify allowed IP addresses and host names, with ports as necessary") .help("Allow network access. Optionally specify allowed IP addresses and host names, with ports as necessary")
.value_parser(flags_net::validator) .value_parser(flags_net::validator)
.hide(true) .hide(true);
;
if let Some(requires) = requires { if let Some(requires) = requires {
arg = arg.requires(requires) arg = arg.requires(requires)
} }
@ -3448,7 +3498,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
.require_equals(true) .require_equals(true)
.value_name("API_NAME") .value_name("API_NAME")
.help("Allow access to OS information. Optionally allow specific APIs by function name") .help("Allow access to OS information. Optionally allow specific APIs by function name")
.value_parser(|key: &str| parse_sys_kind(key).map(ToString::to_string)) .value_parser(|key: &str| SysDescriptor::parse(key.to_string()).map(|s| s.into_string()))
.hide(true) .hide(true)
; ;
if let Some(requires) = requires { if let Some(requires) = requires {
@ -3466,7 +3516,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
.require_equals(true) .require_equals(true)
.value_name("API_NAME") .value_name("API_NAME")
.help("Deny access to OS information. Optionally deny specific APIs by function name") .help("Deny access to OS information. Optionally deny specific APIs by function name")
.value_parser(|key: &str| parse_sys_kind(key).map(ToString::to_string)) .value_parser(|key: &str| SysDescriptor::parse(key.to_string()).map(|s| s.into_string()))
.hide(true) .hide(true)
; ;
if let Some(requires) = requires { if let Some(requires) = requires {
@ -3550,8 +3600,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
.long("allow-hrtime") .long("allow-hrtime")
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.help("REMOVED in Deno 2.0") .help("REMOVED in Deno 2.0")
.hide(true) .hide(true);
;
if let Some(requires) = requires { if let Some(requires) = requires {
arg = arg.requires(requires) arg = arg.requires(requires)
} }
@ -3564,8 +3613,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
.long("deny-hrtime") .long("deny-hrtime")
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.help("REMOVED in Deno 2.0") .help("REMOVED in Deno 2.0")
.hide(true) .hide(true);
;
if let Some(requires) = requires { if let Some(requires) = requires {
arg = arg.requires(requires) arg = arg.requires(requires)
} }
@ -3619,6 +3667,7 @@ fn runtime_args(
app: Command, app: Command,
include_perms: bool, include_perms: bool,
include_inspector: bool, include_inspector: bool,
include_allow_scripts: bool,
) -> Command { ) -> Command {
let app = compile_args(app); let app = compile_args(app);
let app = if include_perms { let app = if include_perms {
@ -3631,6 +3680,15 @@ fn runtime_args(
} else { } else {
app app
}; };
let app = if include_allow_scripts {
app.arg(allow_scripts_arg())
} else {
app
};
runtime_misc_args(app)
}
fn runtime_misc_args(app: Command) -> Command {
app app
.arg(frozen_lockfile_arg()) .arg(frozen_lockfile_arg())
.arg(cached_only_arg()) .arg(cached_only_arg())
@ -3650,7 +3708,7 @@ fn allow_import_arg() -> Arg {
.require_equals(true) .require_equals(true)
.value_name("IP_OR_HOSTNAME") .value_name("IP_OR_HOSTNAME")
.help(cstr!( .help(cstr!(
"Allow importing from remote hosts. Optionally specify allowed IP addresses and host names, with ports as necessary. Default value: <p(245)>deno.land:443,jsr.io:443,esm.sh:443,raw.githubusercontent.com:443,user.githubusercontent.com:443</>" "Allow importing from remote hosts. Optionally specify allowed IP addresses and host names, with ports as necessary. Default value: <p(245)>deno.land:443,jsr.io:443,esm.sh:443,cdn.jsdelivr.net:443,raw.githubusercontent.com:443,user.githubusercontent.com:443</>"
)) ))
.value_parser(flags_net::validator) .value_parser(flags_net::validator)
} }
@ -3801,7 +3859,9 @@ fn location_arg() -> Arg {
url.set_password(None).unwrap(); url.set_password(None).unwrap();
Ok(url) Ok(url)
}) })
.help("Value of 'globalThis.location' used by some web APIs") .help(cstr!(
"Value of <p(245)>globalThis.location</> used by some web APIs"
))
.value_hint(ValueHint::Url) .value_hint(ValueHint::Url)
} }
@ -4110,23 +4170,29 @@ enum UnstableArgsConfig {
ResolutionAndRuntime, ResolutionAndRuntime,
} }
struct UnstableArgsIter { trait CommandExt {
idx: usize, fn with_unstable_args(self, cfg: UnstableArgsConfig) -> Self;
cfg: UnstableArgsConfig,
} }
impl Iterator for UnstableArgsIter { impl CommandExt for Command {
type Item = Arg; fn with_unstable_args(self, cfg: UnstableArgsConfig) -> Self {
let mut next_display_order = {
let mut value = 1000;
move || {
value += 1;
value
}
};
fn next(&mut self) -> Option<Self::Item> { let mut cmd = self.arg(
let arg = if self.idx == 0 {
Arg::new("unstable") Arg::new("unstable")
.long("unstable") .long("unstable")
.help(cstr!("Enable all unstable features and APIs. Instead of using this flag, consider enabling individual unstable features .help(cstr!("Enable all unstable features and APIs. Instead of using this flag, consider enabling individual unstable features
<p(245)>To view the list of individual unstable feature flags, run this command again with --help=unstable</>")) <p(245)>To view the list of individual unstable feature flags, run this command again with --help=unstable</>"))
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.hide(matches!(self.cfg, UnstableArgsConfig::None)) .hide(matches!(cfg, UnstableArgsConfig::None))
} else if self.idx == 1 { .display_order(next_display_order())
).arg(
Arg::new("unstable-bare-node-builtins") Arg::new("unstable-bare-node-builtins")
.long("unstable-bare-node-builtins") .long("unstable-bare-node-builtins")
.help("Enable unstable bare node builtins feature") .help("Enable unstable bare node builtins feature")
@ -4134,20 +4200,36 @@ impl Iterator for UnstableArgsIter {
.value_parser(FalseyValueParser::new()) .value_parser(FalseyValueParser::new())
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.hide(true) .hide(true)
.long_help(match self.cfg { .long_help(match cfg {
UnstableArgsConfig::None => None, UnstableArgsConfig::None => None,
UnstableArgsConfig::ResolutionOnly UnstableArgsConfig::ResolutionOnly
| UnstableArgsConfig::ResolutionAndRuntime => Some("true"), | UnstableArgsConfig::ResolutionAndRuntime => Some("true"),
}) })
.help_heading(UNSTABLE_HEADING) .help_heading(UNSTABLE_HEADING)
} else if self.idx == 2 { .display_order(next_display_order()),
).arg(
Arg::new("unstable-detect-cjs")
.long("unstable-detect-cjs")
.help("Reads the package.json type field in a project to treat .js files as .cjs")
.value_parser(FalseyValueParser::new())
.action(ArgAction::SetTrue)
.hide(true)
.long_help(match cfg {
UnstableArgsConfig::None => None,
UnstableArgsConfig::ResolutionOnly
| UnstableArgsConfig::ResolutionAndRuntime => Some("true"),
})
.help_heading(UNSTABLE_HEADING)
.display_order(next_display_order())
).arg(
Arg::new("unstable-byonm") Arg::new("unstable-byonm")
.long("unstable-byonm") .long("unstable-byonm")
.value_parser(FalseyValueParser::new()) .value_parser(FalseyValueParser::new())
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.hide(true) .hide(true)
.help_heading(UNSTABLE_HEADING) .help_heading(UNSTABLE_HEADING)
} else if self.idx == 3 { .display_order(next_display_order()),
).arg(
Arg::new("unstable-sloppy-imports") Arg::new("unstable-sloppy-imports")
.long("unstable-sloppy-imports") .long("unstable-sloppy-imports")
.help("Enable unstable resolving of specifiers by extension probing, .js to .ts, and directory probing") .help("Enable unstable resolving of specifiers by extension probing, .js to .ts, and directory probing")
@ -4155,13 +4237,16 @@ impl Iterator for UnstableArgsIter {
.value_parser(FalseyValueParser::new()) .value_parser(FalseyValueParser::new())
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.hide(true) .hide(true)
.long_help(match self.cfg { .long_help(match cfg {
UnstableArgsConfig::None => None, UnstableArgsConfig::None => None,
UnstableArgsConfig::ResolutionOnly | UnstableArgsConfig::ResolutionAndRuntime => Some("true") UnstableArgsConfig::ResolutionOnly | UnstableArgsConfig::ResolutionAndRuntime => Some("true")
}) })
.help_heading(UNSTABLE_HEADING) .help_heading(UNSTABLE_HEADING)
} else if self.idx > 3 { .display_order(next_display_order())
let granular_flag = crate::UNSTABLE_GRANULAR_FLAGS.get(self.idx - 4)?; );
for granular_flag in crate::UNSTABLE_GRANULAR_FLAGS.iter() {
cmd = cmd.arg(
Arg::new(format!("unstable-{}", granular_flag.name)) Arg::new(format!("unstable-{}", granular_flag.name))
.long(format!("unstable-{}", granular_flag.name)) .long(format!("unstable-{}", granular_flag.name))
.help(granular_flag.help_text) .help(granular_flag.help_text)
@ -4170,7 +4255,7 @@ impl Iterator for UnstableArgsIter {
.help_heading(UNSTABLE_HEADING) .help_heading(UNSTABLE_HEADING)
// we don't render long help, so using it here as a sort of metadata // we don't render long help, so using it here as a sort of metadata
.long_help(if granular_flag.show_in_help { .long_help(if granular_flag.show_in_help {
match self.cfg { match cfg {
UnstableArgsConfig::None | UnstableArgsConfig::ResolutionOnly => { UnstableArgsConfig::None | UnstableArgsConfig::ResolutionOnly => {
None None
} }
@ -4179,16 +4264,12 @@ impl Iterator for UnstableArgsIter {
} else { } else {
None None
}) })
} else { .display_order(next_display_order()),
return None; );
};
self.idx += 1;
Some(arg.display_order(self.idx + 1000))
} }
}
fn unstable_args(cfg: UnstableArgsConfig) -> impl IntoIterator<Item = Arg> { cmd
UnstableArgsIter { idx: 0, cfg } }
} }
fn allow_scripts_arg_parse( fn allow_scripts_arg_parse(
@ -4210,8 +4291,13 @@ fn allow_scripts_arg_parse(
Ok(()) Ok(())
} }
fn add_parse(flags: &mut Flags, matches: &mut ArgMatches) { fn add_parse(
flags: &mut Flags,
matches: &mut ArgMatches,
) -> clap::error::Result<()> {
allow_scripts_arg_parse(flags, matches)?;
flags.subcommand = DenoSubcommand::Add(add_parse_inner(matches, None)); flags.subcommand = DenoSubcommand::Add(add_parse_inner(matches, None));
Ok(())
} }
fn add_parse_inner( fn add_parse_inner(
@ -4237,7 +4323,7 @@ fn bench_parse(
) -> clap::error::Result<()> { ) -> clap::error::Result<()> {
flags.type_check_mode = TypeCheckMode::Local; flags.type_check_mode = TypeCheckMode::Local;
runtime_args_parse(flags, matches, true, false)?; runtime_args_parse(flags, matches, true, false, true)?;
ext_arg_parse(flags, matches); ext_arg_parse(flags, matches);
// NOTE: `deno bench` always uses `--no-prompt`, tests shouldn't ever do // NOTE: `deno bench` always uses `--no-prompt`, tests shouldn't ever do
@ -4305,6 +4391,7 @@ fn check_parse(
flags.type_check_mode = TypeCheckMode::Local; flags.type_check_mode = TypeCheckMode::Local;
compile_args_without_check_parse(flags, matches)?; compile_args_without_check_parse(flags, matches)?;
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime); unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime);
frozen_lockfile_arg_parse(flags, matches);
let files = matches.remove_many::<String>("file").unwrap().collect(); let files = matches.remove_many::<String>("file").unwrap().collect();
if matches.get_flag("all") || matches.get_flag("remote") { if matches.get_flag("all") || matches.get_flag("remote") {
flags.type_check_mode = TypeCheckMode::All; flags.type_check_mode = TypeCheckMode::All;
@ -4327,7 +4414,7 @@ fn compile_parse(
matches: &mut ArgMatches, matches: &mut ArgMatches,
) -> clap::error::Result<()> { ) -> clap::error::Result<()> {
flags.type_check_mode = TypeCheckMode::Local; flags.type_check_mode = TypeCheckMode::Local;
runtime_args_parse(flags, matches, true, false)?; runtime_args_parse(flags, matches, true, false, true)?;
let mut script = matches.remove_many::<String>("script_arg").unwrap(); let mut script = matches.remove_many::<String>("script_arg").unwrap();
let source_file = script.next().unwrap(); let source_file = script.next().unwrap();
@ -4502,7 +4589,7 @@ fn eval_parse(
flags: &mut Flags, flags: &mut Flags,
matches: &mut ArgMatches, matches: &mut ArgMatches,
) -> clap::error::Result<()> { ) -> clap::error::Result<()> {
runtime_args_parse(flags, matches, false, true)?; runtime_args_parse(flags, matches, false, true, false)?;
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime); unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime);
flags.allow_all(); flags.allow_all();
@ -4595,7 +4682,7 @@ fn install_parse(
flags: &mut Flags, flags: &mut Flags,
matches: &mut ArgMatches, matches: &mut ArgMatches,
) -> clap::error::Result<()> { ) -> clap::error::Result<()> {
runtime_args_parse(flags, matches, true, true)?; runtime_args_parse(flags, matches, true, true, false)?;
let global = matches.get_flag("global"); let global = matches.get_flag("global");
if global { if global {
@ -4655,7 +4742,7 @@ fn json_reference_parse(
app.build(); app.build();
fn serialize_command( fn serialize_command(
command: &mut Command, mut command: Command,
top_level: bool, top_level: bool,
) -> deno_core::serde_json::Value { ) -> deno_core::serde_json::Value {
let args = command let args = command
@ -4663,7 +4750,7 @@ fn json_reference_parse(
.filter(|arg| { .filter(|arg| {
!arg.is_hide_set() !arg.is_hide_set()
&& if top_level { && if top_level {
true arg.is_global_set()
} else { } else {
!arg.is_global_set() !arg.is_global_set()
} }
@ -4672,40 +4759,49 @@ fn json_reference_parse(
let name = arg.get_id().as_str(); let name = arg.get_id().as_str();
let short = arg.get_short(); let short = arg.get_short();
let long = arg.get_long(); let long = arg.get_long();
let aliases = arg.get_visible_aliases();
let required = arg.is_required_set(); let required = arg.is_required_set();
let help = arg.get_help().map(|help| help.to_string()); let help = arg.get_help().map(|help| help.ansi().to_string());
let help_heading = arg
.get_help_heading()
.map(|help_heading| help_heading.to_string());
let usage = arg.to_string(); let usage = arg.to_string();
json!({ json!({
"name": name, "name": name,
"short": short, "short": short,
"long": long, "long": long,
"aliases": aliases,
"required": required, "required": required,
"help": help, "help": help,
"help_heading": help_heading,
"usage": usage, "usage": usage,
}) })
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let name = command.get_name().to_string(); let name = command.get_name().to_string();
let about = command.get_about().map(|about| about.to_string()); let about = command.get_about().map(|about| about.ansi().to_string());
let visible_aliases = command let usage = command.render_usage().ansi().to_string();
.get_visible_aliases()
.map(|s| s.to_string())
.collect::<Vec<_>>();
let usage = command.render_usage().to_string();
let subcommands = command let subcommands = command
.get_subcommands_mut() .get_subcommands()
.map(|command| serialize_command(command, false)) .map(|command| {
serialize_command(
if command
.get_arguments()
.any(|arg| arg.get_id().as_str() == "unstable")
{
enable_unstable(command.clone())
} else {
command.clone()
},
false,
)
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
json!({ json!({
"name": name, "name": name,
"about": about, "about": about,
"visible_aliases": visible_aliases,
"args": args, "args": args,
"subcommands": subcommands, "subcommands": subcommands,
"usage": usage, "usage": usage,
@ -4713,7 +4809,7 @@ fn json_reference_parse(
} }
flags.subcommand = DenoSubcommand::JSONReference(JSONReferenceFlags { flags.subcommand = DenoSubcommand::JSONReference(JSONReferenceFlags {
json: serialize_command(&mut app, true), json: serialize_command(app, true),
}) })
} }
@ -4812,8 +4908,18 @@ fn repl_parse(
flags: &mut Flags, flags: &mut Flags,
matches: &mut ArgMatches, matches: &mut ArgMatches,
) -> clap::error::Result<()> { ) -> clap::error::Result<()> {
runtime_args_parse(flags, matches, true, true)?; unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime);
unsafely_ignore_certificate_errors_parse(flags, matches); compile_args_without_check_parse(flags, matches)?;
cached_only_arg_parse(flags, matches);
frozen_lockfile_arg_parse(flags, matches);
permission_args_parse(flags, matches)?;
inspect_arg_parse(flags, matches);
location_arg_parse(flags, matches);
v8_flags_arg_parse(flags, matches);
seed_arg_parse(flags, matches);
enable_testing_features_arg_parse(flags, matches);
env_file_arg_parse(flags, matches);
strace_ops_parse(flags, matches);
let eval_files = matches let eval_files = matches
.remove_many::<String>("eval-file") .remove_many::<String>("eval-file")
@ -4845,7 +4951,7 @@ fn run_parse(
mut app: Command, mut app: Command,
bare: bool, bare: bool,
) -> clap::error::Result<()> { ) -> clap::error::Result<()> {
runtime_args_parse(flags, matches, true, true)?; runtime_args_parse(flags, matches, true, true, true)?;
ext_arg_parse(flags, matches); ext_arg_parse(flags, matches);
flags.code_cache_enabled = !matches.get_flag("no-code-cache"); flags.code_cache_enabled = !matches.get_flag("no-code-cache");
@ -4886,7 +4992,7 @@ fn serve_parse(
let worker_count = parallel_arg_parse(matches).map(|v| v.get()); let worker_count = parallel_arg_parse(matches).map(|v| v.get());
runtime_args_parse(flags, matches, true, true)?; runtime_args_parse(flags, matches, true, true, true)?;
// If the user didn't pass --allow-net, add this port to the network // If the user didn't pass --allow-net, add this port to the network
// allowlist. If the host is 0.0.0.0, we add :{port} and allow the same network perms // allowlist. If the host is 0.0.0.0, we add :{port} and allow the same network perms
// as if it was passed to --allow-net directly. // as if it was passed to --allow-net directly.
@ -4941,6 +5047,7 @@ fn task_parse(flags: &mut Flags, matches: &mut ArgMatches) {
.unwrap_or(ConfigFlag::Discover); .unwrap_or(ConfigFlag::Discover);
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime); unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime);
node_modules_arg_parse(flags, matches);
let mut task_flags = TaskFlags { let mut task_flags = TaskFlags {
cwd: matches.remove_one::<String>("cwd"), cwd: matches.remove_one::<String>("cwd"),
@ -4980,7 +5087,7 @@ fn test_parse(
matches: &mut ArgMatches, matches: &mut ArgMatches,
) -> clap::error::Result<()> { ) -> clap::error::Result<()> {
flags.type_check_mode = TypeCheckMode::Local; flags.type_check_mode = TypeCheckMode::Local;
runtime_args_parse(flags, matches, true, true)?; runtime_args_parse(flags, matches, true, true, true)?;
ext_arg_parse(flags, matches); ext_arg_parse(flags, matches);
// NOTE: `deno test` always uses `--no-prompt`, tests shouldn't ever do // NOTE: `deno test` always uses `--no-prompt`, tests shouldn't ever do
@ -5345,6 +5452,7 @@ fn runtime_args_parse(
matches: &mut ArgMatches, matches: &mut ArgMatches,
include_perms: bool, include_perms: bool,
include_inspector: bool, include_inspector: bool,
include_allow_scripts: bool,
) -> clap::error::Result<()> { ) -> clap::error::Result<()> {
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime); unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionAndRuntime);
compile_args_parse(flags, matches)?; compile_args_parse(flags, matches)?;
@ -5356,6 +5464,9 @@ fn runtime_args_parse(
if include_inspector { if include_inspector {
inspect_arg_parse(flags, matches); inspect_arg_parse(flags, matches);
} }
if include_allow_scripts {
allow_scripts_arg_parse(flags, matches)?;
}
location_arg_parse(flags, matches); location_arg_parse(flags, matches);
v8_flags_arg_parse(flags, matches); v8_flags_arg_parse(flags, matches);
seed_arg_parse(flags, matches); seed_arg_parse(flags, matches);
@ -6707,6 +6818,32 @@ mod tests {
..Flags::default() ..Flags::default()
} }
); );
let r = flags_from_vec(svec!["deno", "fmt", "--ext", "html"]);
assert!(r.is_err());
let r = flags_from_vec(svec!["deno", "fmt", "--ext", "html", "./**"]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Fmt(FmtFlags {
check: false,
files: FileFlags {
include: vec!["./**".to_string()],
ignore: vec![],
},
use_tabs: None,
line_width: None,
indent_width: None,
single_quote: None,
prose_wrap: None,
no_semicolons: None,
unstable_component: false,
watch: Default::default(),
}),
ext: Some("html".to_string()),
..Flags::default()
}
);
} }
#[test] #[test]
@ -7355,7 +7492,7 @@ mod tests {
#[test] #[test]
fn repl_with_flags() { fn repl_with_flags() {
#[rustfmt::skip] #[rustfmt::skip]
let r = flags_from_vec(svec!["deno", "repl", "-A", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--unsafely-ignore-certificate-errors", "--env=.example.env"]); let r = flags_from_vec(svec!["deno", "repl", "-A", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--v8-flags=--help", "--seed", "1", "--inspect=127.0.0.1:9229", "--unsafely-ignore-certificate-errors", "--env=.example.env"]);
assert_eq!( assert_eq!(
r.unwrap(), r.unwrap(),
Flags { Flags {
@ -7403,7 +7540,6 @@ mod tests {
allow_write: Some(vec![]), allow_write: Some(vec![]),
..Default::default() ..Default::default()
}, },
type_check_mode: TypeCheckMode::None,
..Flags::default() ..Flags::default()
} }
); );
@ -7425,7 +7561,6 @@ mod tests {
eval: None, eval: None,
is_default_command: false, is_default_command: false,
}), }),
type_check_mode: TypeCheckMode::None,
..Flags::default() ..Flags::default()
} }
); );
@ -8827,8 +8962,12 @@ mod tests {
#[test] #[test]
fn test_no_colon_in_value_name() { fn test_no_colon_in_value_name() {
let app = let app = runtime_args(
runtime_args(Command::new("test_inspect_completion_value"), true, true); Command::new("test_inspect_completion_value"),
true,
true,
false,
);
let inspect_args = app let inspect_args = app
.get_arguments() .get_arguments()
.filter(|arg| arg.get_id() == "inspect") .filter(|arg| arg.get_id() == "inspect")

View file

@ -51,7 +51,7 @@ pub fn parse(paths: Vec<String>) -> clap::error::Result<Vec<String>> {
} }
} else { } else {
NetDescriptor::parse(&host_and_port).map_err(|e| { NetDescriptor::parse(&host_and_port).map_err(|e| {
clap::Error::raw(clap::error::ErrorKind::InvalidValue, format!("{e:?}")) clap::Error::raw(clap::error::ErrorKind::InvalidValue, e.to_string())
})?; })?;
out.push(host_and_port) out.push(host_and_port)
} }

View file

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

View file

@ -7,6 +7,7 @@ mod import_map;
mod lockfile; mod lockfile;
mod package_json; mod package_json;
use deno_ast::MediaType;
use deno_ast::SourceMapOption; use deno_ast::SourceMapOption;
use deno_config::deno_json::NodeModulesDirMode; use deno_config::deno_json::NodeModulesDirMode;
use deno_config::workspace::CreateResolverOptions; use deno_config::workspace::CreateResolverOptions;
@ -20,20 +21,20 @@ use deno_config::workspace::WorkspaceDiscoverOptions;
use deno_config::workspace::WorkspaceDiscoverStart; use deno_config::workspace::WorkspaceDiscoverStart;
use deno_config::workspace::WorkspaceLintConfig; use deno_config::workspace::WorkspaceLintConfig;
use deno_config::workspace::WorkspaceResolver; use deno_config::workspace::WorkspaceResolver;
use deno_core::normalize_path;
use deno_core::resolve_url_or_path; use deno_core::resolve_url_or_path;
use deno_graph::GraphKind; use deno_graph::GraphKind;
use deno_npm::npm_rc::NpmRc; use deno_npm::npm_rc::NpmRc;
use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmSystemInfo; use deno_npm::NpmSystemInfo;
use deno_path_util::normalize_path;
use deno_runtime::ops::otel::OtelConfig;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
use import_map::resolve_import_map_value_from_specifier; use import_map::resolve_import_map_value_from_specifier;
pub use deno_config::deno_json::BenchConfig; pub use deno_config::deno_json::BenchConfig;
pub use deno_config::deno_json::ConfigFile; pub use deno_config::deno_json::ConfigFile;
pub use deno_config::deno_json::FmtOptionsConfig; pub use deno_config::deno_json::FmtOptionsConfig;
pub use deno_config::deno_json::JsxImportSourceConfig;
pub use deno_config::deno_json::LintRulesConfig; pub use deno_config::deno_json::LintRulesConfig;
pub use deno_config::deno_json::ProseWrap; pub use deno_config::deno_json::ProseWrap;
pub use deno_config::deno_json::TsConfig; pub use deno_config::deno_json::TsConfig;
@ -44,7 +45,9 @@ pub use deno_config::glob::FilePatterns;
pub use deno_json::check_warn_tsconfig; pub use deno_json::check_warn_tsconfig;
pub use flags::*; pub use flags::*;
pub use lockfile::CliLockfile; pub use lockfile::CliLockfile;
pub use lockfile::CliLockfileReadFromPathOptions;
pub use package_json::NpmInstallDepsProvider; pub use package_json::NpmInstallDepsProvider;
pub use package_json::PackageJsonDepValueParseWithLocationError;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_core::anyhow::bail; use deno_core::anyhow::bail;
@ -199,6 +202,8 @@ pub fn ts_config_to_transpile_and_emit_options(
precompile_jsx_dynamic_props: None, precompile_jsx_dynamic_props: None,
transform_jsx, transform_jsx,
var_decl_imports: false, var_decl_imports: false,
// todo(dsherret): support verbatim_module_syntax here properly
verbatim_module_syntax: false,
}, },
deno_ast::EmitOptions { deno_ast::EmitOptions {
inline_sources: options.inline_sources, inline_sources: options.inline_sources,
@ -577,6 +582,7 @@ fn discover_npmrc(
let resolved = npmrc let resolved = npmrc
.as_resolved(npm_registry_url()) .as_resolved(npm_registry_url())
.context("Failed to resolve .npmrc options")?; .context("Failed to resolve .npmrc options")?;
log::debug!(".npmrc found at: '{}'", path.display());
Ok(Arc::new(resolved)) Ok(Arc::new(resolved))
} }
@ -817,18 +823,14 @@ impl CliOptions {
}; };
let msg = let msg =
format!("DANGER: TLS certificate validation is disabled {}", domains); format!("DANGER: TLS certificate validation is disabled {}", domains);
#[allow(clippy::print_stderr)]
{ {
// use eprintln instead of log::warn so this always gets shown log::error!("{}", colors::yellow(msg));
eprintln!("{}", colors::yellow(msg));
} }
} }
warn_insecure_allow_run_flags(&flags);
let maybe_lockfile = maybe_lockfile.filter(|_| !force_global_cache); let maybe_lockfile = maybe_lockfile.filter(|_| !force_global_cache);
let deno_dir_provider = 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( let maybe_node_modules_folder = resolve_node_modules_folder(
&initial_cwd, &initial_cwd,
&flags, &flags,
@ -964,6 +966,9 @@ impl CliOptions {
match self.sub_command() { match self.sub_command() {
DenoSubcommand::Cache(_) => GraphKind::All, DenoSubcommand::Cache(_) => GraphKind::All,
DenoSubcommand::Check(_) => GraphKind::TypesOnly, DenoSubcommand::Check(_) => GraphKind::TypesOnly,
DenoSubcommand::Install(InstallFlags {
kind: InstallKind::Local(_),
}) => GraphKind::All,
_ => self.type_check_mode().as_graph_kind(), _ => self.type_check_mode().as_graph_kind(),
} }
} }
@ -1123,6 +1128,10 @@ impl CliOptions {
} }
} }
pub fn otel_config(&self) -> Option<OtelConfig> {
self.flags.otel_config()
}
pub fn env_file_name(&self) -> Option<&String> { pub fn env_file_name(&self) -> Option<&String> {
self.flags.env_file.as_ref() self.flags.env_file.as_ref()
} }
@ -1131,25 +1140,34 @@ impl CliOptions {
self self
.main_module_cell .main_module_cell
.get_or_init(|| { .get_or_init(|| {
let main_module = match &self.flags.subcommand { Ok(match &self.flags.subcommand {
DenoSubcommand::Compile(compile_flags) => { DenoSubcommand::Compile(compile_flags) => {
resolve_url_or_path(&compile_flags.source_file, self.initial_cwd())? resolve_url_or_path(&compile_flags.source_file, self.initial_cwd())?
} }
DenoSubcommand::Eval(_) => { DenoSubcommand::Eval(_) => {
resolve_url_or_path("./$deno$eval.ts", self.initial_cwd())? resolve_url_or_path("./$deno$eval.mts", self.initial_cwd())?
} }
DenoSubcommand::Repl(_) => { DenoSubcommand::Repl(_) => {
resolve_url_or_path("./$deno$repl.ts", self.initial_cwd())? resolve_url_or_path("./$deno$repl.mts", self.initial_cwd())?
} }
DenoSubcommand::Run(run_flags) => { DenoSubcommand::Run(run_flags) => {
if run_flags.is_stdin() { if run_flags.is_stdin() {
resolve_url_or_path("./$deno$stdin.ts", self.initial_cwd())? resolve_url_or_path("./$deno$stdin.mts", self.initial_cwd())?
} else if NpmPackageReqReference::from_str(&run_flags.script)
.is_ok()
{
ModuleSpecifier::parse(&run_flags.script)?
} else { } else {
resolve_url_or_path(&run_flags.script, self.initial_cwd())? let url =
resolve_url_or_path(&run_flags.script, self.initial_cwd())?;
if self.is_node_main()
&& url.scheme() == "file"
&& MediaType::from_specifier(&url) == MediaType::Unknown
{
try_resolve_node_binary_main_entrypoint(
&run_flags.script,
self.initial_cwd(),
)?
.unwrap_or(url)
} else {
url
}
} }
} }
DenoSubcommand::Serve(run_flags) => { DenoSubcommand::Serve(run_flags) => {
@ -1158,9 +1176,7 @@ impl CliOptions {
_ => { _ => {
bail!("No main module.") bail!("No main module.")
} }
}; })
Ok(main_module)
}) })
.as_ref() .as_ref()
.map_err(|err| deno_core::anyhow::anyhow!("{}", err)) .map_err(|err| deno_core::anyhow::anyhow!("{}", err))
@ -1209,7 +1225,7 @@ impl CliOptions {
// This is triggered via a secret environment variable which is used // This is triggered via a secret environment variable which is used
// for functionality like child_process.fork. Users should NOT depend // for functionality like child_process.fork. Users should NOT depend
// on this functionality. // on this functionality.
pub fn is_npm_main(&self) -> bool { pub fn is_node_main(&self) -> bool {
NPM_PROCESS_STATE.is_some() NPM_PROCESS_STATE.is_some()
} }
@ -1453,6 +1469,12 @@ impl CliOptions {
watch: Some(WatchFlagsWithPaths { hmr, .. }), watch: Some(WatchFlagsWithPaths { hmr, .. }),
.. ..
}) = &self.flags.subcommand }) = &self.flags.subcommand
{
*hmr
} else if let DenoSubcommand::Serve(ServeFlags {
watch: Some(WatchFlagsWithPaths { hmr, .. }),
..
}) = &self.flags.subcommand
{ {
*hmr *hmr
} else { } else {
@ -1581,6 +1603,13 @@ impl CliOptions {
|| self.workspace().has_unstable("bare-node-builtins") || self.workspace().has_unstable("bare-node-builtins")
} }
pub fn detect_cjs(&self) -> bool {
// only enabled when there's a package.json in order to not have a
// perf penalty for non-npm Deno projects of searching for the closest
// package.json beside each module
self.workspace().package_jsons().next().is_some() || self.is_node_main()
}
fn byonm_enabled(&self) -> bool { fn byonm_enabled(&self) -> bool {
// check if enabled via unstable // check if enabled via unstable
self.node_modules_dir().ok().flatten() == Some(NodeModulesDirMode::Manual) self.node_modules_dir().ok().flatten() == Some(NodeModulesDirMode::Manual)
@ -1591,6 +1620,15 @@ impl CliOptions {
} }
pub fn use_byonm(&self) -> bool { pub fn use_byonm(&self) -> bool {
if matches!(
self.sub_command(),
DenoSubcommand::Install(_)
| DenoSubcommand::Add(_)
| DenoSubcommand::Remove(_)
) {
// For `deno install/add/remove` we want to force the managed resolver so it can set up `node_modules/` directory.
return false;
}
if self.node_modules_dir().ok().flatten().is_none() if self.node_modules_dir().ok().flatten().is_none()
&& self.maybe_node_modules_folder.is_some() && self.maybe_node_modules_folder.is_some()
&& self && self
@ -1625,21 +1663,16 @@ impl CliOptions {
}); });
if !from_config_file.is_empty() { if !from_config_file.is_empty() {
// collect unstable granular flags let all_valid_unstable_flags: Vec<&str> = crate::UNSTABLE_GRANULAR_FLAGS
let mut all_valid_unstable_flags: Vec<&str> =
crate::UNSTABLE_GRANULAR_FLAGS
.iter() .iter()
.map(|granular_flag| granular_flag.name) .map(|granular_flag| granular_flag.name)
.collect(); .chain([
let mut another_unstable_flags = Vec::from([
"sloppy-imports", "sloppy-imports",
"byonm", "byonm",
"bare-node-builtins", "bare-node-builtins",
"fmt-component", "fmt-component",
]); ])
// add more unstable flags to the same vector holding granular flags .collect();
all_valid_unstable_flags.append(&mut another_unstable_flags);
// check and warn if the unstable flag of config file isn't supported, by // check and warn if the unstable flag of config file isn't supported, by
// iterating through the vector holding the unstable flags // iterating through the vector holding the unstable flags
@ -1672,6 +1705,10 @@ impl CliOptions {
if let DenoSubcommand::Run(RunFlags { if let DenoSubcommand::Run(RunFlags {
watch: Some(WatchFlagsWithPaths { paths, .. }), watch: Some(WatchFlagsWithPaths { paths, .. }),
.. ..
})
| DenoSubcommand::Serve(ServeFlags {
watch: Some(WatchFlagsWithPaths { paths, .. }),
..
}) = &self.flags.subcommand }) = &self.flags.subcommand
{ {
full_paths.extend(paths.iter().map(|path| self.initial_cwd.join(path))); full_paths.extend(paths.iter().map(|path| self.initial_cwd.join(path)));
@ -1714,27 +1751,6 @@ impl CliOptions {
} }
} }
/// Warns for specific uses of `--allow-run`. This function is not
/// intended to catch every single possible insecure use of `--allow-run`,
/// but is just an attempt to discourage some common pitfalls.
fn warn_insecure_allow_run_flags(flags: &Flags) {
let permissions = &flags.permissions;
if permissions.allow_all {
return;
}
let Some(allow_run_list) = permissions.allow_run.as_ref() else {
return;
};
// discourage using --allow-run without an allow list
if allow_run_list.is_empty() {
log::warn!(
"{} --allow-run without an allow list is susceptible to exploits. Prefer specifying an allow list (https://docs.deno.com/runtime/fundamentals/security/#running-subprocesses)",
colors::yellow("Warning")
);
}
}
/// Resolves the path to use for a local node_modules folder. /// Resolves the path to use for a local node_modules folder.
fn resolve_node_modules_folder( fn resolve_node_modules_folder(
cwd: &Path, cwd: &Path,
@ -1792,6 +1808,36 @@ fn resolve_node_modules_folder(
Ok(Some(canonicalize_path_maybe_not_exists(&path)?)) Ok(Some(canonicalize_path_maybe_not_exists(&path)?))
} }
fn try_resolve_node_binary_main_entrypoint(
specifier: &str,
initial_cwd: &Path,
) -> Result<Option<Url>, AnyError> {
// node allows running files at paths without a `.js` extension
// or at directories with an index.js file
let path = deno_core::normalize_path(initial_cwd.join(specifier));
if path.is_dir() {
let index_file = path.join("index.js");
Ok(if index_file.is_file() {
Some(deno_path_util::url_from_file_path(&index_file)?)
} else {
None
})
} else {
let path = path.with_extension(
path
.extension()
.and_then(|s| s.to_str())
.map(|s| format!("{}.js", s))
.unwrap_or("js".to_string()),
);
if path.is_file() {
Ok(Some(deno_path_util::url_from_file_path(&path)?))
} else {
Ok(None)
}
}
}
fn resolve_import_map_specifier( fn resolve_import_map_specifier(
maybe_import_map_path: Option<&str>, maybe_import_map_path: Option<&str>,
maybe_config_file: Option<&ConfigFile>, maybe_config_file: Option<&ConfigFile>,

View file

@ -5,9 +5,12 @@ use std::sync::Arc;
use deno_config::workspace::Workspace; use deno_config::workspace::Workspace;
use deno_core::serde_json; use deno_core::serde_json;
use deno_core::url::Url;
use deno_package_json::PackageJsonDepValue; use deno_package_json::PackageJsonDepValue;
use deno_package_json::PackageJsonDepValueParseError;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use thiserror::Error;
#[derive(Debug)] #[derive(Debug)]
pub struct InstallNpmRemotePkg { pub struct InstallNpmRemotePkg {
@ -22,10 +25,20 @@ pub struct InstallNpmWorkspacePkg {
pub target_dir: PathBuf, pub target_dir: PathBuf,
} }
#[derive(Debug, Error, Clone)]
#[error("Failed to install '{}'\n at {}", alias, location)]
pub struct PackageJsonDepValueParseWithLocationError {
pub location: Url,
pub alias: String,
#[source]
pub source: PackageJsonDepValueParseError,
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct NpmInstallDepsProvider { pub struct NpmInstallDepsProvider {
remote_pkgs: Vec<InstallNpmRemotePkg>, remote_pkgs: Vec<InstallNpmRemotePkg>,
workspace_pkgs: Vec<InstallNpmWorkspacePkg>, workspace_pkgs: Vec<InstallNpmWorkspacePkg>,
pkg_json_dep_errors: Vec<PackageJsonDepValueParseWithLocationError>,
} }
impl NpmInstallDepsProvider { impl NpmInstallDepsProvider {
@ -37,6 +50,7 @@ impl NpmInstallDepsProvider {
// todo(dsherret): estimate capacity? // todo(dsherret): estimate capacity?
let mut workspace_pkgs = Vec::new(); let mut workspace_pkgs = Vec::new();
let mut remote_pkgs = Vec::new(); let mut remote_pkgs = Vec::new();
let mut pkg_json_dep_errors = Vec::new();
let workspace_npm_pkgs = workspace.npm_packages(); let workspace_npm_pkgs = workspace.npm_packages();
for (_, folder) in workspace.config_folders() { for (_, folder) in workspace.config_folders() {
@ -83,8 +97,18 @@ impl NpmInstallDepsProvider {
let deps = pkg_json.resolve_local_package_json_deps(); let deps = pkg_json.resolve_local_package_json_deps();
let mut pkg_pkgs = Vec::with_capacity(deps.len()); let mut pkg_pkgs = Vec::with_capacity(deps.len());
for (alias, dep) in deps { for (alias, dep) in deps {
let Ok(dep) = dep else { let dep = match dep {
Ok(dep) => dep,
Err(err) => {
pkg_json_dep_errors.push(
PackageJsonDepValueParseWithLocationError {
location: pkg_json.specifier(),
alias,
source: err,
},
);
continue; continue;
}
}; };
match dep { match dep {
PackageJsonDepValue::Req(pkg_req) => { PackageJsonDepValue::Req(pkg_req) => {
@ -131,14 +155,21 @@ impl NpmInstallDepsProvider {
Self { Self {
remote_pkgs, remote_pkgs,
workspace_pkgs, workspace_pkgs,
pkg_json_dep_errors,
} }
} }
pub fn remote_pkgs(&self) -> &Vec<InstallNpmRemotePkg> { pub fn remote_pkgs(&self) -> &[InstallNpmRemotePkg] {
&self.remote_pkgs &self.remote_pkgs
} }
pub fn workspace_pkgs(&self) -> &Vec<InstallNpmWorkspacePkg> { pub fn workspace_pkgs(&self) -> &[InstallNpmWorkspacePkg] {
&self.workspace_pkgs &self.workspace_pkgs
} }
pub fn pkg_json_dep_errors(
&self,
) -> &[PackageJsonDepValueParseWithLocationError] {
&self.pkg_json_dep_errors
}
} }

View file

@ -1,6 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// deno-lint-ignore-file no-console no-process-globals
// deno-lint-ignore-file no-console
let [total, count] = typeof Deno !== "undefined" let [total, count] = typeof Deno !== "undefined"
? Deno.args ? Deno.args

View file

@ -1,6 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// deno-lint-ignore-file no-console no-process-globals
// deno-lint-ignore-file no-console
let [total, count] = typeof Deno !== "undefined" let [total, count] = typeof Deno !== "undefined"
? Deno.args ? Deno.args

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// deno-lint-ignore-file no-console no-process-globals
// deno-lint-ignore-file no-console
const queueMicrotask = globalThis.queueMicrotask || process.nextTick; const queueMicrotask = globalThis.queueMicrotask || process.nextTick;
let [total, count] = typeof Deno !== "undefined" let [total, count] = typeof Deno !== "undefined"

View file

@ -1,6 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// deno-lint-ignore-file no-console no-process-globals
// deno-lint-ignore-file no-console
let [total, count] = typeof Deno !== "undefined" let [total, count] = typeof Deno !== "undefined"
? Deno.args ? Deno.args

View file

@ -1,6 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// deno-lint-ignore-file no-console no-process-globals
// deno-lint-ignore-file no-console
const queueMicrotask = globalThis.queueMicrotask || process.nextTick; const queueMicrotask = globalThis.queueMicrotask || process.nextTick;
let [total, count] = typeof Deno !== "undefined" let [total, count] = typeof Deno !== "undefined"

View file

@ -1,6 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// deno-lint-ignore-file no-console no-process-globals
// deno-lint-ignore-file no-console
const queueMicrotask = globalThis.queueMicrotask || process.nextTick; const queueMicrotask = globalThis.queueMicrotask || process.nextTick;
let [total, count] = typeof Deno !== "undefined" let [total, count] = typeof Deno !== "undefined"

View file

@ -1,6 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
// deno-lint-ignore-file no-console no-process-globals
// deno-lint-ignore-file no-console
const queueMicrotask = globalThis.queueMicrotask || process.nextTick; const queueMicrotask = globalThis.queueMicrotask || process.nextTick;
let [total, count] = typeof Deno !== "undefined" let [total, count] = typeof Deno !== "undefined"

View file

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

View file

@ -57,7 +57,7 @@ impl rusqlite::types::FromSql for CacheDBHash {
} }
/// What should the cache should do on failure? /// What should the cache should do on failure?
#[derive(Default)] #[derive(Debug, Default)]
pub enum CacheFailure { pub enum CacheFailure {
/// Return errors if failure mode otherwise unspecified. /// Return errors if failure mode otherwise unspecified.
#[default] #[default]
@ -69,6 +69,7 @@ pub enum CacheFailure {
} }
/// Configuration SQL and other parameters for a [`CacheDB`]. /// Configuration SQL and other parameters for a [`CacheDB`].
#[derive(Debug)]
pub struct CacheDBConfiguration { pub struct CacheDBConfiguration {
/// SQL to run for a new database. /// SQL to run for a new database.
pub table_initializer: &'static str, pub table_initializer: &'static str,
@ -98,6 +99,7 @@ impl CacheDBConfiguration {
} }
} }
#[derive(Debug)]
enum ConnectionState { enum ConnectionState {
Connected(Connection), Connected(Connection),
Blackhole, Blackhole,
@ -106,7 +108,7 @@ enum ConnectionState {
/// A cache database that eagerly initializes itself off-thread, preventing initialization operations /// A cache database that eagerly initializes itself off-thread, preventing initialization operations
/// from blocking the main thread. /// from blocking the main thread.
#[derive(Clone)] #[derive(Debug, Clone)]
pub struct CacheDB { pub struct CacheDB {
// TODO(mmastrac): We can probably simplify our thread-safe implementation here // TODO(mmastrac): We can probably simplify our thread-safe implementation here
conn: Arc<Mutex<OnceCell<ConnectionState>>>, conn: Arc<Mutex<OnceCell<ConnectionState>>>,

View file

@ -126,9 +126,9 @@ impl DenoDir {
self.root.join("registries") self.root.join("registries")
} }
/// Path to the dependencies cache folder. /// Path to the remote cache folder.
pub fn deps_folder_path(&self) -> PathBuf { pub fn remote_folder_path(&self) -> PathBuf {
self.root.join("deps") self.root.join("remote")
} }
/// Path to the origin data cache folder. /// Path to the origin data cache folder.

27
cli/cache/emit.rs vendored
View file

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

111
cli/cache/mod.rs vendored
View file

@ -8,9 +8,9 @@ use crate::file_fetcher::FetchOptions;
use crate::file_fetcher::FetchPermissionsOptionRef; use crate::file_fetcher::FetchPermissionsOptionRef;
use crate::file_fetcher::FileFetcher; use crate::file_fetcher::FileFetcher;
use crate::file_fetcher::FileOrRedirect; use crate::file_fetcher::FileOrRedirect;
use crate::npm::CliNpmResolver;
use crate::util::fs::atomic_write_file_with_retries; use crate::util::fs::atomic_write_file_with_retries;
use crate::util::path::specifier_has_extension; use crate::util::fs::atomic_write_file_with_retries_and_fs;
use crate::util::fs::AtomicWriteFileFsAdapter;
use deno_ast::MediaType; use deno_ast::MediaType;
use deno_core::futures; use deno_core::futures;
@ -20,7 +20,9 @@ use deno_graph::source::CacheInfo;
use deno_graph::source::LoadFuture; use deno_graph::source::LoadFuture;
use deno_graph::source::LoadResponse; use deno_graph::source::LoadResponse;
use deno_graph::source::Loader; use deno_graph::source::Loader;
use deno_runtime::deno_fs;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use node_resolver::InNpmPackageChecker;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
@ -77,8 +79,12 @@ impl deno_cache_dir::DenoCacheEnv for RealDenoCacheEnv {
atomic_write_file_with_retries(path, bytes, CACHE_PERM) atomic_write_file_with_retries(path, bytes, CACHE_PERM)
} }
fn remove_file(&self, path: &Path) -> std::io::Result<()> { fn canonicalize_path(&self, path: &Path) -> std::io::Result<PathBuf> {
std::fs::remove_file(path) 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>> { fn modified(&self, path: &Path) -> std::io::Result<Option<SystemTime>> {
@ -100,6 +106,66 @@ 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 GlobalHttpCache = deno_cache_dir::GlobalHttpCache<RealDenoCacheEnv>;
pub type LocalHttpCache = deno_cache_dir::LocalHttpCache<RealDenoCacheEnv>; pub type LocalHttpCache = deno_cache_dir::LocalHttpCache<RealDenoCacheEnv>;
pub type LocalLspHttpCache = pub type LocalLspHttpCache =
@ -116,28 +182,31 @@ pub struct FetchCacherOptions {
/// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides /// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides
/// a concise interface to the DENO_DIR when building module graphs. /// a concise interface to the DENO_DIR when building module graphs.
pub struct FetchCacher { pub struct FetchCacher {
file_fetcher: Arc<FileFetcher>,
pub file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>, pub file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
file_fetcher: Arc<FileFetcher>,
fs: Arc<dyn deno_fs::FileSystem>,
global_http_cache: Arc<GlobalHttpCache>, global_http_cache: Arc<GlobalHttpCache>,
npm_resolver: Arc<dyn CliNpmResolver>, in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
module_info_cache: Arc<ModuleInfoCache>, module_info_cache: Arc<ModuleInfoCache>,
permissions: PermissionsContainer, permissions: PermissionsContainer,
cache_info_enabled: bool,
is_deno_publish: bool, is_deno_publish: bool,
cache_info_enabled: bool,
} }
impl FetchCacher { impl FetchCacher {
pub fn new( pub fn new(
file_fetcher: Arc<FileFetcher>, file_fetcher: Arc<FileFetcher>,
fs: Arc<dyn deno_fs::FileSystem>,
global_http_cache: Arc<GlobalHttpCache>, global_http_cache: Arc<GlobalHttpCache>,
npm_resolver: Arc<dyn CliNpmResolver>, in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
module_info_cache: Arc<ModuleInfoCache>, module_info_cache: Arc<ModuleInfoCache>,
options: FetchCacherOptions, options: FetchCacherOptions,
) -> Self { ) -> Self {
Self { Self {
file_fetcher, file_fetcher,
fs,
global_http_cache, global_http_cache,
npm_resolver, in_npm_pkg_checker,
module_info_cache, module_info_cache,
file_header_overrides: options.file_header_overrides, file_header_overrides: options.file_header_overrides,
permissions: options.permissions, permissions: options.permissions,
@ -192,32 +261,25 @@ impl Loader for FetchCacher {
) -> LoadFuture { ) -> LoadFuture {
use deno_graph::source::CacheSetting as LoaderCacheSetting; use deno_graph::source::CacheSetting as LoaderCacheSetting;
if specifier.scheme() == "file" { if specifier.scheme() == "file"
if specifier.path().contains("/node_modules/") { && specifier.path().contains("/node_modules/")
{
// The specifier might be in a completely different symlinked tree than // The specifier might be in a completely different symlinked tree than
// what the node_modules url is in (ex. `/my-project-1/node_modules` // 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 // 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 // is in a node_modules dir to avoid needlessly canonicalizing, then now compare
// against the canonicalized specifier. // against the canonicalized specifier.
let specifier = let specifier = crate::node::resolve_specifier_into_node_modules(
crate::node::resolve_specifier_into_node_modules(specifier); specifier,
if self.npm_resolver.in_npm_package(&specifier) { self.fs.as_ref(),
);
if self.in_npm_pkg_checker.in_npm_package(&specifier) {
return Box::pin(futures::future::ready(Ok(Some( return Box::pin(futures::future::ready(Ok(Some(
LoadResponse::External { specifier }, 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: specifier.clone(),
},
))));
}
}
if self.is_deno_publish if self.is_deno_publish
&& matches!(specifier.scheme(), "http" | "https") && matches!(specifier.scheme(), "http" | "https")
&& !specifier.as_str().starts_with(jsr_url().as_str()) && !specifier.as_str().starts_with(jsr_url().as_str())
@ -259,6 +321,7 @@ impl Loader for FetchCacher {
} else { } else {
FetchPermissionsOptionRef::DynamicContainer(&permissions) FetchPermissionsOptionRef::DynamicContainer(&permissions)
}, },
maybe_auth: None,
maybe_accept: None, maybe_accept: None,
maybe_cache_setting: maybe_cache_setting.as_ref(), maybe_cache_setting: maybe_cache_setting.as_ref(),
}, },

View file

@ -44,18 +44,32 @@ pub static MODULE_INFO_CACHE_DB: CacheDBConfiguration = CacheDBConfiguration {
/// A cache of `deno_graph::ModuleInfo` objects. Using this leads to a considerable /// A cache of `deno_graph::ModuleInfo` objects. Using this leads to a considerable
/// performance improvement because when it exists we can skip parsing a module for /// performance improvement because when it exists we can skip parsing a module for
/// deno_graph. /// deno_graph.
#[derive(Debug)]
pub struct ModuleInfoCache { pub struct ModuleInfoCache {
conn: CacheDB, conn: CacheDB,
parsed_source_cache: Arc<ParsedSourceCache>,
} }
impl ModuleInfoCache { impl ModuleInfoCache {
#[cfg(test)] #[cfg(test)]
pub fn new_in_memory(version: &'static str) -> Self { pub fn new_in_memory(
Self::new(CacheDB::in_memory(&MODULE_INFO_CACHE_DB, version)) version: &'static str,
parsed_source_cache: Arc<ParsedSourceCache>,
) -> Self {
Self::new(
CacheDB::in_memory(&MODULE_INFO_CACHE_DB, version),
parsed_source_cache,
)
} }
pub fn new(conn: CacheDB) -> Self { pub fn new(
Self { conn } conn: CacheDB,
parsed_source_cache: Arc<ParsedSourceCache>,
) -> Self {
Self {
conn,
parsed_source_cache,
}
} }
/// Useful for testing: re-create this cache DB with a different current version. /// Useful for testing: re-create this cache DB with a different current version.
@ -63,6 +77,7 @@ impl ModuleInfoCache {
pub(crate) fn recreate_with_version(self, version: &'static str) -> Self { pub(crate) fn recreate_with_version(self, version: &'static str) -> Self {
Self { Self {
conn: self.conn.recreate_with_version(version), conn: self.conn.recreate_with_version(version),
parsed_source_cache: self.parsed_source_cache,
} }
} }
@ -113,13 +128,10 @@ impl ModuleInfoCache {
Ok(()) Ok(())
} }
pub fn as_module_analyzer<'a>( pub fn as_module_analyzer(&self) -> ModuleInfoCacheModuleAnalyzer {
&'a self,
parsed_source_cache: &'a Arc<ParsedSourceCache>,
) -> ModuleInfoCacheModuleAnalyzer<'a> {
ModuleInfoCacheModuleAnalyzer { ModuleInfoCacheModuleAnalyzer {
module_info_cache: self, module_info_cache: self,
parsed_source_cache, parsed_source_cache: &self.parsed_source_cache,
} }
} }
} }
@ -129,6 +141,84 @@ pub struct ModuleInfoCacheModuleAnalyzer<'a> {
parsed_source_cache: &'a Arc<ParsedSourceCache>, parsed_source_cache: &'a Arc<ParsedSourceCache>,
} }
impl<'a> ModuleInfoCacheModuleAnalyzer<'a> {
fn load_cached_module_info(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
source_hash: CacheDBHash,
) -> Option<ModuleInfo> {
match self.module_info_cache.get_module_info(
specifier,
media_type,
source_hash,
) {
Ok(Some(info)) => Some(info),
Ok(None) => None,
Err(err) => {
log::debug!(
"Error loading module cache info for {}. {:#}",
specifier,
err
);
None
}
}
}
fn save_module_info_to_cache(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
source_hash: CacheDBHash,
module_info: &ModuleInfo,
) {
if let Err(err) = self.module_info_cache.set_module_info(
specifier,
media_type,
source_hash,
module_info,
) {
log::debug!(
"Error saving module cache info for {}. {:#}",
specifier,
err
);
}
}
pub fn analyze_sync(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
source: &Arc<str>,
) -> Result<ModuleInfo, deno_ast::ParseDiagnostic> {
// attempt to load from the cache
let source_hash = CacheDBHash::from_source(source);
if let Some(info) =
self.load_cached_module_info(specifier, media_type, source_hash)
{
return Ok(info);
}
// otherwise, get the module info from the parsed source cache
let parser = self.parsed_source_cache.as_capturing_parser();
let analyzer = ParserModuleAnalyzer::new(&parser);
let module_info =
analyzer.analyze_sync(specifier, source.clone(), media_type)?;
// then attempt to cache it
self.save_module_info_to_cache(
specifier,
media_type,
source_hash,
&module_info,
);
Ok(module_info)
}
}
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl<'a> deno_graph::ModuleAnalyzer for ModuleInfoCacheModuleAnalyzer<'a> { impl<'a> deno_graph::ModuleAnalyzer for ModuleInfoCacheModuleAnalyzer<'a> {
async fn analyze( async fn analyze(
@ -139,20 +229,10 @@ impl<'a> deno_graph::ModuleAnalyzer for ModuleInfoCacheModuleAnalyzer<'a> {
) -> Result<ModuleInfo, deno_ast::ParseDiagnostic> { ) -> Result<ModuleInfo, deno_ast::ParseDiagnostic> {
// attempt to load from the cache // attempt to load from the cache
let source_hash = CacheDBHash::from_source(&source); let source_hash = CacheDBHash::from_source(&source);
match self.module_info_cache.get_module_info( if let Some(info) =
specifier, self.load_cached_module_info(specifier, media_type, source_hash)
media_type, {
source_hash, return Ok(info);
) {
Ok(Some(info)) => return Ok(info),
Ok(None) => {}
Err(err) => {
log::debug!(
"Error loading module cache info for {}. {:#}",
specifier,
err
);
}
} }
// otherwise, get the module info from the parsed source cache // otherwise, get the module info from the parsed source cache
@ -169,18 +249,12 @@ impl<'a> deno_graph::ModuleAnalyzer for ModuleInfoCacheModuleAnalyzer<'a> {
.unwrap()?; .unwrap()?;
// then attempt to cache it // then attempt to cache it
if let Err(err) = self.module_info_cache.set_module_info( self.save_module_info_to_cache(
specifier, specifier,
media_type, media_type,
source_hash, source_hash,
&module_info, &module_info,
) {
log::debug!(
"Error saving module cache info for {}. {:#}",
specifier,
err
); );
}
Ok(module_info) Ok(module_info)
} }
@ -202,7 +276,7 @@ fn serialize_media_type(media_type: MediaType) -> i64 {
Tsx => 11, Tsx => 11,
Json => 12, Json => 12,
Wasm => 13, Wasm => 13,
TsBuildInfo => 14, Css => 14,
SourceMap => 15, SourceMap => 15,
Unknown => 16, Unknown => 16,
} }
@ -217,7 +291,7 @@ mod test {
#[test] #[test]
pub fn module_info_cache_general_use() { pub fn module_info_cache_general_use() {
let cache = ModuleInfoCache::new_in_memory("1.0.0"); let cache = ModuleInfoCache::new_in_memory("1.0.0", Default::default());
let specifier1 = let specifier1 =
ModuleSpecifier::parse("https://localhost/mod.ts").unwrap(); ModuleSpecifier::parse("https://localhost/mod.ts").unwrap();
let specifier2 = let specifier2 =

View file

@ -7,9 +7,9 @@ use deno_ast::MediaType;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_ast::ParsedSource; use deno_ast::ParsedSource;
use deno_core::parking_lot::Mutex; use deno_core::parking_lot::Mutex;
use deno_graph::CapturingModuleParser; use deno_graph::CapturingEsParser;
use deno_graph::DefaultModuleParser; use deno_graph::DefaultEsParser;
use deno_graph::ModuleParser; use deno_graph::EsParser;
use deno_graph::ParseOptions; use deno_graph::ParseOptions;
use deno_graph::ParsedSourceStore; use deno_graph::ParsedSourceStore;
@ -46,7 +46,7 @@ impl<'a> LazyGraphSourceParser<'a> {
} }
} }
#[derive(Default)] #[derive(Debug, Default)]
pub struct ParsedSourceCache { pub struct ParsedSourceCache {
sources: Mutex<HashMap<ModuleSpecifier, ParsedSource>>, sources: Mutex<HashMap<ModuleSpecifier, ParsedSource>>,
} }
@ -57,12 +57,11 @@ impl ParsedSourceCache {
module: &deno_graph::JsModule, module: &deno_graph::JsModule,
) -> Result<ParsedSource, deno_ast::ParseDiagnostic> { ) -> Result<ParsedSource, deno_ast::ParseDiagnostic> {
let parser = self.as_capturing_parser(); let parser = self.as_capturing_parser();
// this will conditionally parse because it's using a CapturingModuleParser // this will conditionally parse because it's using a CapturingEsParser
parser.parse_module(ParseOptions { parser.parse_program(ParseOptions {
specifier: &module.specifier, specifier: &module.specifier,
source: module.source.clone(), source: module.source.clone(),
media_type: module.media_type, media_type: module.media_type,
// don't bother enabling because this method is currently only used for vendoring
scope_analysis: false, scope_analysis: false,
}) })
} }
@ -86,10 +85,9 @@ impl ParsedSourceCache {
specifier, specifier,
source, source,
media_type, media_type,
// don't bother enabling because this method is currently only used for emitting
scope_analysis: false, scope_analysis: false,
}; };
DefaultModuleParser.parse_module(options) DefaultEsParser.parse_program(options)
} }
/// Frees the parsed source from memory. /// Frees the parsed source from memory.
@ -99,8 +97,8 @@ impl ParsedSourceCache {
/// Creates a parser that will reuse a ParsedSource from the store /// Creates a parser that will reuse a ParsedSource from the store
/// if it exists, or else parse. /// if it exists, or else parse.
pub fn as_capturing_parser(&self) -> CapturingModuleParser { pub fn as_capturing_parser(&self) -> CapturingEsParser {
CapturingModuleParser::new(None, self) CapturingEsParser::new(None, self)
} }
} }

View file

@ -1,5 +1,6 @@
disallowed-methods = [ disallowed-methods = [
{ path = "reqwest::Client::new", reason = "create an HttpClient via an HttpClientProvider instead" }, { path = "reqwest::Client::new", reason = "create an HttpClient via an HttpClientProvider instead" },
{ path = "std::process::exit", reason = "use deno_runtime::exit instead" },
] ]
disallowed-types = [ disallowed-types = [
{ path = "reqwest::Client", reason = "use crate::http_util::HttpClient instead" }, { path = "reqwest::Client", reason = "use crate::http_util::HttpClient instead" },

View file

@ -3,24 +3,28 @@
use crate::cache::EmitCache; use crate::cache::EmitCache;
use crate::cache::FastInsecureHasher; use crate::cache::FastInsecureHasher;
use crate::cache::ParsedSourceCache; use crate::cache::ParsedSourceCache;
use crate::resolver::CjsTracker;
use deno_ast::ModuleKind;
use deno_ast::SourceMapOption; use deno_ast::SourceMapOption;
use deno_ast::SourceRange; use deno_ast::SourceRange;
use deno_ast::SourceRanged; use deno_ast::SourceRanged;
use deno_ast::SourceRangedForSpanned; use deno_ast::SourceRangedForSpanned;
use deno_ast::TranspileModuleOptions;
use deno_ast::TranspileResult; use deno_ast::TranspileResult;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::stream::FuturesUnordered;
use deno_core::futures::FutureExt; use deno_core::futures::FutureExt;
use deno_core::futures::StreamExt; use deno_core::futures::StreamExt;
use deno_core::ModuleCodeBytes;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_graph::MediaType; use deno_graph::MediaType;
use deno_graph::Module; use deno_graph::Module;
use deno_graph::ModuleGraph; use deno_graph::ModuleGraph;
use std::sync::Arc; use std::sync::Arc;
#[derive(Debug)]
pub struct Emitter { pub struct Emitter {
cjs_tracker: Arc<CjsTracker>,
emit_cache: Arc<EmitCache>, emit_cache: Arc<EmitCache>,
parsed_source_cache: Arc<ParsedSourceCache>, parsed_source_cache: Arc<ParsedSourceCache>,
transpile_and_emit_options: transpile_and_emit_options:
@ -31,6 +35,7 @@ pub struct Emitter {
impl Emitter { impl Emitter {
pub fn new( pub fn new(
cjs_tracker: Arc<CjsTracker>,
emit_cache: Arc<EmitCache>, emit_cache: Arc<EmitCache>,
parsed_source_cache: Arc<ParsedSourceCache>, parsed_source_cache: Arc<ParsedSourceCache>,
transpile_options: deno_ast::TranspileOptions, transpile_options: deno_ast::TranspileOptions,
@ -43,6 +48,7 @@ impl Emitter {
hasher.finish() hasher.finish()
}; };
Self { Self {
cjs_tracker,
emit_cache, emit_cache,
parsed_source_cache, parsed_source_cache,
transpile_and_emit_options: Arc::new((transpile_options, emit_options)), transpile_and_emit_options: Arc::new((transpile_options, emit_options)),
@ -60,20 +66,19 @@ impl Emitter {
continue; continue;
}; };
let is_emittable = matches!( if module.media_type.is_emittable() {
module.media_type,
MediaType::TypeScript
| MediaType::Mts
| MediaType::Cts
| MediaType::Jsx
| MediaType::Tsx
);
if is_emittable {
futures.push( futures.push(
self self
.emit_parsed_source( .emit_parsed_source(
&module.specifier, &module.specifier,
module.media_type, module.media_type,
ModuleKind::from_is_cjs(
self.cjs_tracker.is_cjs_with_known_is_script(
&module.specifier,
module.media_type,
module.is_script,
)?,
),
&module.source, &module.source,
) )
.boxed_local(), .boxed_local(),
@ -92,9 +97,10 @@ impl Emitter {
pub fn maybe_cached_emit( pub fn maybe_cached_emit(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
module_kind: deno_ast::ModuleKind,
source: &str, source: &str,
) -> Option<Vec<u8>> { ) -> Option<String> {
let source_hash = self.get_source_hash(source); let source_hash = self.get_source_hash(module_kind, source);
self.emit_cache.get_emit_code(specifier, source_hash) self.emit_cache.get_emit_code(specifier, source_hash)
} }
@ -102,25 +108,27 @@ impl Emitter {
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
media_type: MediaType, media_type: MediaType,
module_kind: deno_ast::ModuleKind,
source: &Arc<str>, source: &Arc<str>,
) -> Result<ModuleCodeBytes, AnyError> { ) -> Result<String, AnyError> {
// Note: keep this in sync with the sync version below // Note: keep this in sync with the sync version below
let helper = EmitParsedSourceHelper(self); let helper = EmitParsedSourceHelper(self);
match helper.pre_emit_parsed_source(specifier, source) { match helper.pre_emit_parsed_source(specifier, module_kind, source) {
PreEmitResult::Cached(emitted_text) => Ok(emitted_text), PreEmitResult::Cached(emitted_text) => Ok(emitted_text),
PreEmitResult::NotCached { source_hash } => { PreEmitResult::NotCached { source_hash } => {
let parsed_source_cache = self.parsed_source_cache.clone(); let parsed_source_cache = self.parsed_source_cache.clone();
let transpile_and_emit_options = let transpile_and_emit_options =
self.transpile_and_emit_options.clone(); self.transpile_and_emit_options.clone();
let transpile_result = deno_core::unsync::spawn_blocking({ let transpiled_source = deno_core::unsync::spawn_blocking({
let specifier = specifier.clone(); let specifier = specifier.clone();
let source = source.clone(); let source = source.clone();
move || -> Result<_, AnyError> { move || -> Result<_, AnyError> {
EmitParsedSourceHelper::transpile( EmitParsedSourceHelper::transpile(
&parsed_source_cache, &parsed_source_cache,
&specifier, &specifier,
source.clone(),
media_type, media_type,
module_kind,
source.clone(),
&transpile_and_emit_options.0, &transpile_and_emit_options.0,
&transpile_and_emit_options.1, &transpile_and_emit_options.1,
) )
@ -128,11 +136,12 @@ impl Emitter {
}) })
.await .await
.unwrap()?; .unwrap()?;
Ok(helper.post_emit_parsed_source( helper.post_emit_parsed_source(
specifier, specifier,
transpile_result, &transpiled_source,
source_hash, source_hash,
)) );
Ok(transpiled_source)
} }
} }
} }
@ -141,26 +150,29 @@ impl Emitter {
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
media_type: MediaType, media_type: MediaType,
module_kind: deno_ast::ModuleKind,
source: &Arc<str>, source: &Arc<str>,
) -> Result<ModuleCodeBytes, AnyError> { ) -> Result<String, AnyError> {
// Note: keep this in sync with the async version above // Note: keep this in sync with the async version above
let helper = EmitParsedSourceHelper(self); let helper = EmitParsedSourceHelper(self);
match helper.pre_emit_parsed_source(specifier, source) { match helper.pre_emit_parsed_source(specifier, module_kind, source) {
PreEmitResult::Cached(emitted_text) => Ok(emitted_text), PreEmitResult::Cached(emitted_text) => Ok(emitted_text),
PreEmitResult::NotCached { source_hash } => { PreEmitResult::NotCached { source_hash } => {
let transpile_result = EmitParsedSourceHelper::transpile( let transpiled_source = EmitParsedSourceHelper::transpile(
&self.parsed_source_cache, &self.parsed_source_cache,
specifier, specifier,
source.clone(),
media_type, media_type,
module_kind,
source.clone(),
&self.transpile_and_emit_options.0, &self.transpile_and_emit_options.0,
&self.transpile_and_emit_options.1, &self.transpile_and_emit_options.1,
)?; )?;
Ok(helper.post_emit_parsed_source( helper.post_emit_parsed_source(
specifier, specifier,
transpile_result, &transpiled_source,
source_hash, source_hash,
)) );
Ok(transpiled_source)
} }
} }
} }
@ -190,10 +202,20 @@ impl Emitter {
// this statement is probably wrong) // this statement is probably wrong)
let mut options = self.transpile_and_emit_options.1.clone(); let mut options = self.transpile_and_emit_options.1.clone();
options.source_map = SourceMapOption::None; options.source_map = SourceMapOption::None;
let is_cjs = self.cjs_tracker.is_cjs_with_known_is_script(
specifier,
media_type,
parsed_source.compute_is_script(),
)?;
let transpiled_source = parsed_source let transpiled_source = parsed_source
.transpile(&self.transpile_and_emit_options.0, &options)? .transpile(
.into_source() &self.transpile_and_emit_options.0,
.into_string()?; &deno_ast::TranspileModuleOptions {
module_kind: Some(ModuleKind::from_is_cjs(is_cjs)),
},
&options,
)?
.into_source();
Ok(transpiled_source.text) Ok(transpiled_source.text)
} }
MediaType::JavaScript MediaType::JavaScript
@ -204,7 +226,7 @@ impl Emitter {
| MediaType::Dcts | MediaType::Dcts
| MediaType::Json | MediaType::Json
| MediaType::Wasm | MediaType::Wasm
| MediaType::TsBuildInfo | MediaType::Css
| MediaType::SourceMap | MediaType::SourceMap
| MediaType::Unknown => { | MediaType::Unknown => {
// clear this specifier from the parsed source cache as it's now out of date // clear this specifier from the parsed source cache as it's now out of date
@ -217,16 +239,17 @@ impl Emitter {
/// A hashing function that takes the source code and uses the global emit /// A hashing function that takes the source code and uses the global emit
/// options then generates a string hash which can be stored to /// options then generates a string hash which can be stored to
/// determine if the cached emit is valid or not. /// determine if the cached emit is valid or not.
fn get_source_hash(&self, source_text: &str) -> u64 { fn get_source_hash(&self, module_kind: ModuleKind, source_text: &str) -> u64 {
FastInsecureHasher::new_without_deno_version() // stored in the transpile_and_emit_options_hash FastInsecureHasher::new_without_deno_version() // stored in the transpile_and_emit_options_hash
.write_str(source_text) .write_str(source_text)
.write_u64(self.transpile_and_emit_options_hash) .write_u64(self.transpile_and_emit_options_hash)
.write_hashable(module_kind)
.finish() .finish()
} }
} }
enum PreEmitResult { enum PreEmitResult {
Cached(ModuleCodeBytes), Cached(String),
NotCached { source_hash: u64 }, NotCached { source_hash: u64 },
} }
@ -237,14 +260,15 @@ impl<'a> EmitParsedSourceHelper<'a> {
pub fn pre_emit_parsed_source( pub fn pre_emit_parsed_source(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
module_kind: deno_ast::ModuleKind,
source: &Arc<str>, source: &Arc<str>,
) -> PreEmitResult { ) -> PreEmitResult {
let source_hash = self.0.get_source_hash(source); let source_hash = self.0.get_source_hash(module_kind, source);
if let Some(emit_code) = if let Some(emit_code) =
self.0.emit_cache.get_emit_code(specifier, source_hash) self.0.emit_cache.get_emit_code(specifier, source_hash)
{ {
PreEmitResult::Cached(emit_code.into_boxed_slice().into()) PreEmitResult::Cached(emit_code)
} else { } else {
PreEmitResult::NotCached { source_hash } PreEmitResult::NotCached { source_hash }
} }
@ -253,25 +277,24 @@ impl<'a> EmitParsedSourceHelper<'a> {
pub fn transpile( pub fn transpile(
parsed_source_cache: &ParsedSourceCache, parsed_source_cache: &ParsedSourceCache,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
source: Arc<str>,
media_type: MediaType, media_type: MediaType,
module_kind: deno_ast::ModuleKind,
source: Arc<str>,
transpile_options: &deno_ast::TranspileOptions, transpile_options: &deno_ast::TranspileOptions,
emit_options: &deno_ast::EmitOptions, emit_options: &deno_ast::EmitOptions,
) -> Result<TranspileResult, AnyError> { ) -> Result<String, AnyError> {
// nothing else needs the parsed source at this point, so remove from // nothing else needs the parsed source at this point, so remove from
// the cache in order to not transpile owned // the cache in order to not transpile owned
let parsed_source = parsed_source_cache let parsed_source = parsed_source_cache
.remove_or_parse_module(specifier, source, media_type)?; .remove_or_parse_module(specifier, source, media_type)?;
ensure_no_import_assertion(&parsed_source)?; ensure_no_import_assertion(&parsed_source)?;
Ok(parsed_source.transpile(transpile_options, emit_options)?) let transpile_result = parsed_source.transpile(
} transpile_options,
&TranspileModuleOptions {
pub fn post_emit_parsed_source( module_kind: Some(module_kind),
&self, },
specifier: &ModuleSpecifier, emit_options,
transpile_result: TranspileResult, )?;
source_hash: u64,
) -> ModuleCodeBytes {
let transpiled_source = match transpile_result { let transpiled_source = match transpile_result {
TranspileResult::Owned(source) => source, TranspileResult::Owned(source) => source,
TranspileResult::Cloned(source) => { TranspileResult::Cloned(source) => {
@ -280,12 +303,20 @@ impl<'a> EmitParsedSourceHelper<'a> {
} }
}; };
debug_assert!(transpiled_source.source_map.is_none()); debug_assert!(transpiled_source.source_map.is_none());
Ok(transpiled_source.text)
}
pub fn post_emit_parsed_source(
&self,
specifier: &ModuleSpecifier,
transpiled_source: &str,
source_hash: u64,
) {
self.0.emit_cache.set_emit_code( self.0.emit_cache.set_emit_code(
specifier, specifier,
source_hash, source_hash,
&transpiled_source.source, transpiled_source.as_bytes(),
); );
transpiled_source.source.into_boxed_slice().into()
} }
} }
@ -317,7 +348,7 @@ fn ensure_no_import_assertion(
deno_core::anyhow::anyhow!("{}", msg) deno_core::anyhow::anyhow!("{}", msg)
} }
let Some(module) = parsed_source.program_ref().as_module() else { let deno_ast::ProgramRef::Module(module) = parsed_source.program_ref() else {
return Ok(()); return Ok(());
}; };

View file

@ -88,6 +88,10 @@ fn get_resolution_error_class(err: &ResolutionError) -> &'static str {
} }
} }
fn get_try_from_int_error_class(_: &std::num::TryFromIntError) -> &'static str {
"TypeError"
}
pub fn get_error_class_name(e: &AnyError) -> &'static str { pub fn get_error_class_name(e: &AnyError) -> &'static str {
deno_runtime::errors::get_error_class_name(e) deno_runtime::errors::get_error_class_name(e)
.or_else(|| { .or_else(|| {
@ -106,5 +110,9 @@ pub fn get_error_class_name(e: &AnyError) -> &'static str {
e.downcast_ref::<ResolutionError>() e.downcast_ref::<ResolutionError>()
.map(get_resolution_error_class) .map(get_resolution_error_class)
}) })
.or_else(|| {
e.downcast_ref::<std::num::TryFromIntError>()
.map(get_try_from_int_error_class)
})
.unwrap_or("Error") .unwrap_or("Error")
} }

View file

@ -11,6 +11,7 @@ use crate::args::StorageKeyResolver;
use crate::args::TsConfigType; use crate::args::TsConfigType;
use crate::cache::Caches; use crate::cache::Caches;
use crate::cache::CodeCache; use crate::cache::CodeCache;
use crate::cache::DenoCacheEnvFsAdapter;
use crate::cache::DenoDir; use crate::cache::DenoDir;
use crate::cache::DenoDirProvider; use crate::cache::DenoDirProvider;
use crate::cache::EmitCache; use crate::cache::EmitCache;
@ -32,22 +33,30 @@ use crate::module_loader::ModuleLoadPreparer;
use crate::node::CliCjsCodeAnalyzer; use crate::node::CliCjsCodeAnalyzer;
use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeCodeTranslator;
use crate::npm::create_cli_npm_resolver; use crate::npm::create_cli_npm_resolver;
use crate::npm::create_in_npm_pkg_checker;
use crate::npm::CliByonmNpmResolverCreateOptions;
use crate::npm::CliManagedInNpmPkgCheckerCreateOptions;
use crate::npm::CliManagedNpmResolverCreateOptions;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::npm::CliNpmResolverByonmCreateOptions;
use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverCreateOptions;
use crate::npm::CliNpmResolverManagedCreateOptions;
use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CliNpmResolverManagedSnapshotOption;
use crate::resolver::CjsResolutionStore; use crate::npm::CreateInNpmPkgCheckerOptions;
use crate::resolver::CliGraphResolver; use crate::resolver::CjsTracker;
use crate::resolver::CliGraphResolverOptions; use crate::resolver::CliDenoResolver;
use crate::resolver::CliNodeResolver; use crate::resolver::CliDenoResolverFs;
use crate::resolver::CliNpmReqResolver;
use crate::resolver::CliResolver;
use crate::resolver::CliResolverOptions;
use crate::resolver::CliSloppyImportsResolver;
use crate::resolver::IsCjsResolverOptions;
use crate::resolver::NpmModuleLoader; use crate::resolver::NpmModuleLoader;
use crate::resolver::SloppyImportsResolver; use crate::resolver::SloppyImportsCachedFs;
use crate::standalone::DenoCompileBinaryWriter; use crate::standalone::DenoCompileBinaryWriter;
use crate::tools::check::TypeChecker; use crate::tools::check::TypeChecker;
use crate::tools::coverage::CoverageCollector; use crate::tools::coverage::CoverageCollector;
use crate::tools::lint::LintRuleProvider; use crate::tools::lint::LintRuleProvider;
use crate::tools::run::hmr::HmrRunner; use crate::tools::run::hmr::HmrRunner;
use crate::tsc::TypeCheckingCjsTracker;
use crate::util::file_watcher::WatcherCommunicator; use crate::util::file_watcher::WatcherCommunicator;
use crate::util::fs::canonicalize_path_maybe_not_exists; use crate::util::fs::canonicalize_path_maybe_not_exists;
use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBar;
@ -56,15 +65,20 @@ use crate::worker::CliMainWorkerFactory;
use crate::worker::CliMainWorkerOptions; use crate::worker::CliMainWorkerOptions;
use std::path::PathBuf; use std::path::PathBuf;
use deno_cache_dir::npm::NpmCacheDir;
use deno_config::workspace::PackageJsonDepResolution; use deno_config::workspace::PackageJsonDepResolution;
use deno_config::workspace::WorkspaceResolver; use deno_config::workspace::WorkspaceResolver;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::futures::FutureExt; use deno_core::futures::FutureExt;
use deno_core::FeatureChecker; use deno_core::FeatureChecker;
use deno_resolver::npm::NpmReqResolverOptions;
use deno_resolver::DenoResolverOptions;
use deno_resolver::NodeAndNpmReqResolver;
use deno_runtime::deno_fs; use deno_runtime::deno_fs;
use deno_runtime::deno_node::DenoFsNodeResolverEnv; use deno_runtime::deno_node::DenoFsNodeResolverEnv;
use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_node::PackageJsonResolver;
use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_tls::rustls::RootCertStore;
@ -74,6 +88,7 @@ use deno_runtime::inspector_server::InspectorServer;
use deno_runtime::permissions::RuntimePermissionDescriptorParser; use deno_runtime::permissions::RuntimePermissionDescriptorParser;
use log::warn; use log::warn;
use node_resolver::analyze::NodeCodeTranslator; use node_resolver::analyze::NodeCodeTranslator;
use node_resolver::InNpmPackageChecker;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use std::future::Future; use std::future::Future;
use std::sync::Arc; use std::sync::Arc;
@ -115,7 +130,7 @@ impl RootCertStoreProvider for CliRootCertStoreProvider {
} }
} }
struct Deferred<T>(once_cell::unsync::OnceCell<T>); pub struct Deferred<T>(once_cell::unsync::OnceCell<T>);
impl<T> Default for Deferred<T> { impl<T> Default for Deferred<T> {
fn default() -> Self { fn default() -> Self {
@ -161,38 +176,42 @@ impl<T> Deferred<T> {
#[derive(Default)] #[derive(Default)]
struct CliFactoryServices { struct CliFactoryServices {
cli_options: Deferred<Arc<CliOptions>>, blob_store: Deferred<Arc<BlobStore>>,
caches: Deferred<Arc<Caches>>, caches: Deferred<Arc<Caches>>,
cjs_tracker: Deferred<Arc<CjsTracker>>,
cli_options: Deferred<Arc<CliOptions>>,
code_cache: Deferred<Arc<CodeCache>>,
deno_resolver: Deferred<Arc<CliDenoResolver>>,
emit_cache: Deferred<Arc<EmitCache>>,
emitter: Deferred<Arc<Emitter>>,
feature_checker: Deferred<Arc<FeatureChecker>>,
file_fetcher: Deferred<Arc<FileFetcher>>, file_fetcher: Deferred<Arc<FileFetcher>>,
fs: Deferred<Arc<dyn deno_fs::FileSystem>>,
global_http_cache: Deferred<Arc<GlobalHttpCache>>, global_http_cache: Deferred<Arc<GlobalHttpCache>>,
http_cache: Deferred<Arc<dyn HttpCache>>, http_cache: Deferred<Arc<dyn HttpCache>>,
http_client_provider: Deferred<Arc<HttpClientProvider>>, http_client_provider: Deferred<Arc<HttpClientProvider>>,
emit_cache: Deferred<Arc<EmitCache>>, in_npm_pkg_checker: Deferred<Arc<dyn InNpmPackageChecker>>,
emitter: Deferred<Arc<Emitter>>,
fs: Deferred<Arc<dyn deno_fs::FileSystem>>,
main_graph_container: Deferred<Arc<MainModuleGraphContainer>>, main_graph_container: Deferred<Arc<MainModuleGraphContainer>>,
maybe_inspector_server: Deferred<Option<Arc<InspectorServer>>>,
root_cert_store_provider: Deferred<Arc<dyn RootCertStoreProvider>>,
blob_store: Deferred<Arc<BlobStore>>,
module_info_cache: Deferred<Arc<ModuleInfoCache>>,
parsed_source_cache: Deferred<Arc<ParsedSourceCache>>,
resolver: Deferred<Arc<CliGraphResolver>>,
maybe_file_watcher_reporter: Deferred<Option<FileWatcherReporter>>, maybe_file_watcher_reporter: Deferred<Option<FileWatcherReporter>>,
maybe_inspector_server: Deferred<Option<Arc<InspectorServer>>>,
module_graph_builder: Deferred<Arc<ModuleGraphBuilder>>, module_graph_builder: Deferred<Arc<ModuleGraphBuilder>>,
module_graph_creator: Deferred<Arc<ModuleGraphCreator>>, module_graph_creator: Deferred<Arc<ModuleGraphCreator>>,
module_info_cache: Deferred<Arc<ModuleInfoCache>>,
module_load_preparer: Deferred<Arc<ModuleLoadPreparer>>, module_load_preparer: Deferred<Arc<ModuleLoadPreparer>>,
node_code_translator: Deferred<Arc<CliNodeCodeTranslator>>, node_code_translator: Deferred<Arc<CliNodeCodeTranslator>>,
node_resolver: Deferred<Arc<NodeResolver>>, node_resolver: Deferred<Arc<NodeResolver>>,
npm_cache_dir: Deferred<Arc<NpmCacheDir>>,
npm_req_resolver: Deferred<Arc<CliNpmReqResolver>>,
npm_resolver: Deferred<Arc<dyn CliNpmResolver>>, npm_resolver: Deferred<Arc<dyn CliNpmResolver>>,
parsed_source_cache: Deferred<Arc<ParsedSourceCache>>,
permission_desc_parser: Deferred<Arc<RuntimePermissionDescriptorParser>>, permission_desc_parser: Deferred<Arc<RuntimePermissionDescriptorParser>>,
pkg_json_resolver: Deferred<Arc<PackageJsonResolver>>,
resolver: Deferred<Arc<CliResolver>>,
root_cert_store_provider: Deferred<Arc<dyn RootCertStoreProvider>>,
root_permissions_container: Deferred<PermissionsContainer>, root_permissions_container: Deferred<PermissionsContainer>,
sloppy_imports_resolver: Deferred<Option<Arc<SloppyImportsResolver>>>, sloppy_imports_resolver: Deferred<Option<Arc<CliSloppyImportsResolver>>>,
text_only_progress_bar: Deferred<ProgressBar>, text_only_progress_bar: Deferred<ProgressBar>,
type_checker: Deferred<Arc<TypeChecker>>, type_checker: Deferred<Arc<TypeChecker>>,
cjs_resolutions: Deferred<Arc<CjsResolutionStore>>,
cli_node_resolver: Deferred<Arc<CliNodeResolver>>,
feature_checker: Deferred<Arc<FeatureChecker>>,
code_cache: Deferred<Arc<CodeCache>>,
workspace_resolver: Deferred<Arc<WorkspaceResolver>>, workspace_resolver: Deferred<Arc<WorkspaceResolver>>,
} }
@ -299,7 +318,7 @@ impl CliFactory {
pub fn global_http_cache(&self) -> Result<&Arc<GlobalHttpCache>, AnyError> { pub fn global_http_cache(&self) -> Result<&Arc<GlobalHttpCache>, AnyError> {
self.services.global_http_cache.get_or_try_init(|| { self.services.global_http_cache.get_or_try_init(|| {
Ok(Arc::new(GlobalHttpCache::new( Ok(Arc::new(GlobalHttpCache::new(
self.deno_dir()?.deps_folder_path(), self.deno_dir()?.remote_folder_path(),
crate::cache::RealDenoCacheEnv, crate::cache::RealDenoCacheEnv,
))) )))
}) })
@ -349,32 +368,76 @@ impl CliFactory {
self.services.fs.get_or_init(|| Arc::new(deno_fs::RealFs)) self.services.fs.get_or_init(|| Arc::new(deno_fs::RealFs))
} }
pub fn in_npm_pkg_checker(
&self,
) -> Result<&Arc<dyn InNpmPackageChecker>, AnyError> {
self.services.in_npm_pkg_checker.get_or_try_init(|| {
let cli_options = self.cli_options()?;
let options = if cli_options.use_byonm() {
CreateInNpmPkgCheckerOptions::Byonm
} else {
CreateInNpmPkgCheckerOptions::Managed(
CliManagedInNpmPkgCheckerCreateOptions {
root_cache_dir_url: self.npm_cache_dir()?.root_dir_url(),
maybe_node_modules_path: cli_options
.node_modules_dir_path()
.map(|p| p.as_path()),
},
)
};
Ok(create_in_npm_pkg_checker(options))
})
}
pub fn npm_cache_dir(&self) -> Result<&Arc<NpmCacheDir>, AnyError> {
self.services.npm_cache_dir.get_or_try_init(|| {
let fs = self.fs();
let global_path = self.deno_dir()?.npm_folder_path();
let cli_options = self.cli_options()?;
Ok(Arc::new(NpmCacheDir::new(
&DenoCacheEnvFsAdapter(fs.as_ref()),
global_path,
cli_options.npmrc().get_all_known_registries_urls(),
)))
})
}
pub async fn npm_resolver( pub async fn npm_resolver(
&self, &self,
) -> Result<&Arc<dyn CliNpmResolver>, AnyError> { ) -> Result<&Arc<dyn CliNpmResolver>, AnyError> {
self self
.services .services
.npm_resolver .npm_resolver
.get_or_try_init_async(async { .get_or_try_init_async(
async {
let fs = self.fs(); let fs = self.fs();
let cli_options = self.cli_options()?; 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() {
create_cli_npm_resolver(if cli_options.use_byonm() && !matches!(cli_options.sub_command(), DenoSubcommand::Install(_) | DenoSubcommand::Add(_) | DenoSubcommand::Remove(_)) { CliNpmResolverCreateOptions::Byonm(
CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions { CliByonmNpmResolverCreateOptions {
fs: fs.clone(), fs: CliDenoResolverFs(fs.clone()),
root_node_modules_dir: Some(match cli_options.node_modules_dir_path() { pkg_json_resolver: self.pkg_json_resolver().clone(),
root_node_modules_dir: Some(
match cli_options.node_modules_dir_path() {
Some(node_modules_path) => node_modules_path.to_path_buf(), Some(node_modules_path) => node_modules_path.to_path_buf(),
// path needs to be canonicalized for node resolution // path needs to be canonicalized for node resolution
// (node_modules_dir_path above is already canonicalized) // (node_modules_dir_path above is already canonicalized)
None => canonicalize_path_maybe_not_exists(cli_options.initial_cwd())? None => canonicalize_path_maybe_not_exists(
cli_options.initial_cwd(),
)?
.join("node_modules"), .join("node_modules"),
}), },
}) ),
},
)
} else { } else {
CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions { CliNpmResolverCreateOptions::Managed(
CliManagedNpmResolverCreateOptions {
snapshot: match cli_options.resolve_npm_resolution_snapshot()? { snapshot: match cli_options.resolve_npm_resolution_snapshot()? {
Some(snapshot) => { Some(snapshot) => {
CliNpmResolverManagedSnapshotOption::Specified(Some(snapshot)) CliNpmResolverManagedSnapshotOption::Specified(Some(
snapshot,
))
} }
None => match cli_options.maybe_lockfile() { None => match cli_options.maybe_lockfile() {
Some(lockfile) => { Some(lockfile) => {
@ -382,39 +445,50 @@ impl CliFactory {
lockfile.clone(), lockfile.clone(),
) )
} }
None => CliNpmResolverManagedSnapshotOption::Specified(None), None => {
CliNpmResolverManagedSnapshotOption::Specified(None)
}
}, },
}, },
maybe_lockfile: cli_options.maybe_lockfile().cloned(), maybe_lockfile: cli_options.maybe_lockfile().cloned(),
fs: fs.clone(), fs: fs.clone(),
http_client_provider: self.http_client_provider().clone(), http_client_provider: self.http_client_provider().clone(),
npm_global_cache_dir: self.deno_dir()?.npm_folder_path(), npm_cache_dir: self.npm_cache_dir()?.clone(),
cache_setting: cli_options.cache_setting(), cache_setting: cli_options.cache_setting(),
text_only_progress_bar: self.text_only_progress_bar().clone(), text_only_progress_bar: self.text_only_progress_bar().clone(),
maybe_node_modules_path: cli_options.node_modules_dir_path().cloned(), maybe_node_modules_path: cli_options
npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::from_workspace(cli_options.workspace())), .node_modules_dir_path()
.cloned(),
npm_install_deps_provider: Arc::new(
NpmInstallDepsProvider::from_workspace(
cli_options.workspace(),
),
),
npm_system_info: cli_options.npm_system_info(), npm_system_info: cli_options.npm_system_info(),
npmrc: cli_options.npmrc().clone(), npmrc: cli_options.npmrc().clone(),
lifecycle_scripts: cli_options.lifecycle_scripts_config(), lifecycle_scripts: cli_options.lifecycle_scripts_config(),
},
)
}) })
}).await .await
}.boxed_local()) }
.boxed_local(),
)
.await .await
} }
pub fn sloppy_imports_resolver( pub fn sloppy_imports_resolver(
&self, &self,
) -> Result<Option<&Arc<SloppyImportsResolver>>, AnyError> { ) -> Result<Option<&Arc<CliSloppyImportsResolver>>, AnyError> {
self self
.services .services
.sloppy_imports_resolver .sloppy_imports_resolver
.get_or_try_init(|| { .get_or_try_init(|| {
Ok( Ok(self.cli_options()?.unstable_sloppy_imports().then(|| {
self Arc::new(CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(
.cli_options()? self.fs().clone(),
.unstable_sloppy_imports() )))
.then(|| Arc::new(SloppyImportsResolver::new(self.fs().clone()))), }))
)
}) })
.map(|maybe| maybe.as_ref()) .map(|maybe| maybe.as_ref())
} }
@ -454,28 +528,47 @@ impl CliFactory {
.await .await
} }
pub async fn resolver(&self) -> Result<&Arc<CliGraphResolver>, AnyError> { pub async fn deno_resolver(&self) -> Result<&Arc<CliDenoResolver>, AnyError> {
self
.services
.deno_resolver
.get_or_try_init_async(async {
let cli_options = self.cli_options()?;
Ok(Arc::new(CliDenoResolver::new(DenoResolverOptions {
in_npm_pkg_checker: self.in_npm_pkg_checker()?.clone(),
node_and_req_resolver: if cli_options.no_npm() {
None
} else {
Some(NodeAndNpmReqResolver {
node_resolver: self.node_resolver().await?.clone(),
npm_req_resolver: self.npm_req_resolver().await?.clone(),
})
},
sloppy_imports_resolver: self.sloppy_imports_resolver()?.cloned(),
workspace_resolver: self.workspace_resolver().await?.clone(),
is_byonm: cli_options.use_byonm(),
maybe_vendor_dir: cli_options.vendor_dir_path(),
})))
})
.await
}
pub async fn resolver(&self) -> Result<&Arc<CliResolver>, AnyError> {
self self
.services .services
.resolver .resolver
.get_or_try_init_async( .get_or_try_init_async(
async { async {
let cli_options = self.cli_options()?; let cli_options = self.cli_options()?;
Ok(Arc::new(CliGraphResolver::new(CliGraphResolverOptions { Ok(Arc::new(CliResolver::new(CliResolverOptions {
sloppy_imports_resolver: self.sloppy_imports_resolver()?.cloned(),
node_resolver: Some(self.cli_node_resolver().await?.clone()),
npm_resolver: if cli_options.no_npm() { npm_resolver: if cli_options.no_npm() {
None None
} else { } else {
Some(self.npm_resolver().await?.clone()) Some(self.npm_resolver().await?.clone())
}, },
workspace_resolver: self.workspace_resolver().await?.clone(),
bare_node_builtins_enabled: cli_options bare_node_builtins_enabled: cli_options
.unstable_bare_node_builtins(), .unstable_bare_node_builtins(),
maybe_jsx_import_source_config: cli_options deno_resolver: self.deno_resolver().await?.clone(),
.workspace()
.to_maybe_jsx_import_source_config()?,
maybe_vendor_dir: cli_options.vendor_dir_path(),
}))) })))
} }
.boxed_local(), .boxed_local(),
@ -504,6 +597,7 @@ impl CliFactory {
self.services.module_info_cache.get_or_try_init(|| { self.services.module_info_cache.get_or_try_init(|| {
Ok(Arc::new(ModuleInfoCache::new( Ok(Arc::new(ModuleInfoCache::new(
self.caches()?.dep_analysis_db(), self.caches()?.dep_analysis_db(),
self.parsed_source_cache().clone(),
))) )))
}) })
} }
@ -532,6 +626,7 @@ impl CliFactory {
ts_config_result.ts_config, ts_config_result.ts_config,
)?; )?;
Ok(Arc::new(Emitter::new( Ok(Arc::new(Emitter::new(
self.cjs_tracker()?.clone(),
self.emit_cache()?.clone(), self.emit_cache()?.clone(),
self.parsed_source_cache().clone(), self.parsed_source_cache().clone(),
transpile_options, transpile_options,
@ -555,7 +650,13 @@ impl CliFactory {
async { async {
Ok(Arc::new(NodeResolver::new( Ok(Arc::new(NodeResolver::new(
DenoFsNodeResolverEnv::new(self.fs().clone()), DenoFsNodeResolverEnv::new(self.fs().clone()),
self.npm_resolver().await?.clone().into_npm_resolver(), self.in_npm_pkg_checker()?.clone(),
self
.npm_resolver()
.await?
.clone()
.into_npm_pkg_folder_resolver(),
self.pkg_json_resolver().clone(),
))) )))
} }
.boxed_local(), .boxed_local(),
@ -573,23 +674,57 @@ impl CliFactory {
let caches = self.caches()?; let caches = self.caches()?;
let node_analysis_cache = let node_analysis_cache =
NodeAnalysisCache::new(caches.node_analysis_db()); NodeAnalysisCache::new(caches.node_analysis_db());
let node_resolver = self.cli_node_resolver().await?.clone(); let node_resolver = self.node_resolver().await?.clone();
let cjs_esm_analyzer = CliCjsCodeAnalyzer::new( let cjs_esm_analyzer = CliCjsCodeAnalyzer::new(
node_analysis_cache, node_analysis_cache,
self.cjs_tracker()?.clone(),
self.fs().clone(), self.fs().clone(),
node_resolver, Some(self.parsed_source_cache().clone()),
); );
Ok(Arc::new(NodeCodeTranslator::new( Ok(Arc::new(NodeCodeTranslator::new(
cjs_esm_analyzer, cjs_esm_analyzer,
DenoFsNodeResolverEnv::new(self.fs().clone()), DenoFsNodeResolverEnv::new(self.fs().clone()),
self.node_resolver().await?.clone(), self.in_npm_pkg_checker()?.clone(),
self.npm_resolver().await?.clone().into_npm_resolver(), node_resolver,
self
.npm_resolver()
.await?
.clone()
.into_npm_pkg_folder_resolver(),
self.pkg_json_resolver().clone(),
))) )))
}) })
.await .await
} }
pub async fn npm_req_resolver(
&self,
) -> Result<&Arc<CliNpmReqResolver>, AnyError> {
self
.services
.npm_req_resolver
.get_or_try_init_async(async {
let npm_resolver = self.npm_resolver().await?;
Ok(Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions {
byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(),
fs: CliDenoResolverFs(self.fs().clone()),
in_npm_pkg_checker: self.in_npm_pkg_checker()?.clone(),
node_resolver: self.node_resolver().await?.clone(),
npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(),
})))
})
.await
}
pub fn pkg_json_resolver(&self) -> &Arc<PackageJsonResolver> {
self.services.pkg_json_resolver.get_or_init(|| {
Arc::new(PackageJsonResolver::new(DenoFsNodeResolverEnv::new(
self.fs().clone(),
)))
})
}
pub async fn type_checker(&self) -> Result<&Arc<TypeChecker>, AnyError> { pub async fn type_checker(&self) -> Result<&Arc<TypeChecker>, AnyError> {
self self
.services .services
@ -598,6 +733,10 @@ impl CliFactory {
let cli_options = self.cli_options()?; let cli_options = self.cli_options()?;
Ok(Arc::new(TypeChecker::new( Ok(Arc::new(TypeChecker::new(
self.caches()?.clone(), self.caches()?.clone(),
Arc::new(TypeCheckingCjsTracker::new(
self.cjs_tracker()?.clone(),
self.module_info_cache()?.clone(),
)),
cli_options.clone(), cli_options.clone(),
self.module_graph_builder().await?.clone(), self.module_graph_builder().await?.clone(),
self.node_resolver().await?.clone(), self.node_resolver().await?.clone(),
@ -616,17 +755,19 @@ impl CliFactory {
.get_or_try_init_async(async { .get_or_try_init_async(async {
let cli_options = self.cli_options()?; let cli_options = self.cli_options()?;
Ok(Arc::new(ModuleGraphBuilder::new( Ok(Arc::new(ModuleGraphBuilder::new(
cli_options.clone(),
self.caches()?.clone(), self.caches()?.clone(),
self.cjs_tracker()?.clone(),
cli_options.clone(),
self.file_fetcher()?.clone(),
self.fs().clone(), self.fs().clone(),
self.resolver().await?.clone(), self.global_http_cache()?.clone(),
self.npm_resolver().await?.clone(), self.in_npm_pkg_checker()?.clone(),
self.module_info_cache()?.clone(),
self.parsed_source_cache().clone(),
cli_options.maybe_lockfile().cloned(), cli_options.maybe_lockfile().cloned(),
self.maybe_file_watcher_reporter().clone(), self.maybe_file_watcher_reporter().clone(),
self.file_fetcher()?.clone(), self.module_info_cache()?.clone(),
self.global_http_cache()?.clone(), self.npm_resolver().await?.clone(),
self.parsed_source_cache().clone(),
self.resolver().await?.clone(),
self.root_permissions_container()?.clone(), self.root_permissions_container()?.clone(),
))) )))
}) })
@ -698,25 +839,18 @@ impl CliFactory {
.await .await
} }
pub fn cjs_resolutions(&self) -> &Arc<CjsResolutionStore> { pub fn cjs_tracker(&self) -> Result<&Arc<CjsTracker>, AnyError> {
self.services.cjs_resolutions.get_or_init(Default::default) self.services.cjs_tracker.get_or_try_init(|| {
} let options = self.cli_options()?;
Ok(Arc::new(CjsTracker::new(
pub async fn cli_node_resolver( self.in_npm_pkg_checker()?.clone(),
&self, self.pkg_json_resolver().clone(),
) -> Result<&Arc<CliNodeResolver>, AnyError> { IsCjsResolverOptions {
self detect_cjs: options.detect_cjs(),
.services is_node_main: options.is_node_main(),
.cli_node_resolver },
.get_or_try_init_async(async {
Ok(Arc::new(CliNodeResolver::new(
self.cjs_resolutions().clone(),
self.fs().clone(),
self.node_resolver().await?.clone(),
self.npm_resolver().await?.clone(),
))) )))
}) })
.await
} }
pub fn permission_desc_parser( pub fn permission_desc_parser(
@ -749,7 +883,9 @@ impl CliFactory {
) -> Result<DenoCompileBinaryWriter, AnyError> { ) -> Result<DenoCompileBinaryWriter, AnyError> {
let cli_options = self.cli_options()?; let cli_options = self.cli_options()?;
Ok(DenoCompileBinaryWriter::new( Ok(DenoCompileBinaryWriter::new(
self.cjs_tracker()?,
self.deno_dir()?, self.deno_dir()?,
self.emitter()?,
self.file_fetcher()?, self.file_fetcher()?,
self.http_client_provider(), self.http_client_provider(),
self.npm_resolver().await?.as_ref(), self.npm_resolver().await?.as_ref(),
@ -778,15 +914,20 @@ impl CliFactory {
&self, &self,
) -> Result<CliMainWorkerFactory, AnyError> { ) -> Result<CliMainWorkerFactory, AnyError> {
let cli_options = self.cli_options()?; let cli_options = self.cli_options()?;
let fs = self.fs();
let node_resolver = self.node_resolver().await?; let node_resolver = self.node_resolver().await?;
let npm_resolver = self.npm_resolver().await?; let npm_resolver = self.npm_resolver().await?;
let fs = self.fs(); let cli_npm_resolver = self.npm_resolver().await?.clone();
let cli_node_resolver = self.cli_node_resolver().await?; let in_npm_pkg_checker = self.in_npm_pkg_checker()?;
let maybe_file_watcher_communicator = if cli_options.has_hmr() { let maybe_file_watcher_communicator = if cli_options.has_hmr() {
Some(self.watcher_communicator.clone().unwrap()) Some(self.watcher_communicator.clone().unwrap())
} else { } else {
None None
}; };
let node_code_translator = self.node_code_translator().await?;
let cjs_tracker = self.cjs_tracker()?.clone();
let pkg_json_resolver = self.pkg_json_resolver().clone();
let npm_req_resolver = self.npm_req_resolver().await?;
Ok(CliMainWorkerFactory::new( Ok(CliMainWorkerFactory::new(
self.blob_store().clone(), self.blob_store().clone(),
@ -796,38 +937,44 @@ impl CliFactory {
None None
}, },
self.feature_checker()?.clone(), self.feature_checker()?.clone(),
self.fs().clone(), fs.clone(),
maybe_file_watcher_communicator, maybe_file_watcher_communicator,
self.maybe_inspector_server()?.clone(), self.maybe_inspector_server()?.clone(),
cli_options.maybe_lockfile().cloned(), cli_options.maybe_lockfile().cloned(),
Box::new(CliModuleLoaderFactory::new( Box::new(CliModuleLoaderFactory::new(
cli_options, cli_options,
cjs_tracker,
if cli_options.code_cache_enabled() { if cli_options.code_cache_enabled() {
Some(self.code_cache()?.clone()) Some(self.code_cache()?.clone())
} else { } else {
None None
}, },
self.emitter()?.clone(), self.emitter()?.clone(),
fs.clone(),
in_npm_pkg_checker.clone(),
self.main_module_graph_container().await?.clone(), self.main_module_graph_container().await?.clone(),
self.module_load_preparer().await?.clone(), self.module_load_preparer().await?.clone(),
cli_node_resolver.clone(), node_code_translator.clone(),
node_resolver.clone(),
npm_req_resolver.clone(),
cli_npm_resolver.clone(),
NpmModuleLoader::new( NpmModuleLoader::new(
self.cjs_resolutions().clone(), self.cjs_tracker()?.clone(),
self.node_code_translator().await?.clone(),
fs.clone(), fs.clone(),
cli_node_resolver.clone(), node_code_translator.clone(),
), ),
self.parsed_source_cache().clone(), self.parsed_source_cache().clone(),
self.resolver().await?.clone(), self.resolver().await?.clone(),
)), )),
node_resolver.clone(), node_resolver.clone(),
npm_resolver.clone(), npm_resolver.clone(),
self.permission_desc_parser()?.clone(), pkg_json_resolver,
self.root_cert_store_provider().clone(), self.root_cert_store_provider().clone(),
self.root_permissions_container()?.clone(), self.root_permissions_container()?.clone(),
StorageKeyResolver::from_options(cli_options), StorageKeyResolver::from_options(cli_options),
cli_options.sub_command().clone(), cli_options.sub_command().clone(),
self.create_cli_main_worker_options()?, self.create_cli_main_worker_options()?,
self.cli_options()?.otel_config(),
)) ))
} }
@ -876,7 +1023,6 @@ impl CliFactory {
inspect_wait: cli_options.inspect_wait().is_some(), inspect_wait: cli_options.inspect_wait().is_some(),
strace_ops: cli_options.strace_ops().clone(), strace_ops: cli_options.strace_ops().clone(),
is_inspecting: cli_options.is_inspecting(), is_inspecting: cli_options.is_inspecting(),
is_npm_main: cli_options.is_npm_main(),
location: cli_options.location_flag().clone(), location: cli_options.location_flag().clone(),
// if the user ran a binary command, we'll need to set process.argv[0] // if the user ran a binary command, we'll need to set process.argv[0]
// to be the name of the binary command instead of deno // to be the name of the binary command instead of deno

View file

@ -21,9 +21,10 @@ use deno_core::url::Url;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_graph::source::LoaderChecksum; use deno_graph::source::LoaderChecksum;
use deno_path_util::url_to_file_path;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_web::BlobStore; use deno_runtime::deno_web::BlobStore;
use deno_runtime::fs_util::specifier_to_file_path; use http::header;
use log::debug; use log::debug;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
@ -136,7 +137,7 @@ impl MemoryFiles {
/// Fetch a source file from the local file system. /// Fetch a source file from the local file system.
fn fetch_local(specifier: &ModuleSpecifier) -> Result<File, AnyError> { fn fetch_local(specifier: &ModuleSpecifier) -> Result<File, AnyError> {
let local = specifier_to_file_path(specifier).map_err(|_| { let local = url_to_file_path(specifier).map_err(|_| {
uri_error(format!("Invalid file path.\n Specifier: {specifier}")) uri_error(format!("Invalid file path.\n Specifier: {specifier}"))
})?; })?;
// If it doesnt have a extension, we want to treat it as typescript by default // If it doesnt have a extension, we want to treat it as typescript by default
@ -181,6 +182,7 @@ pub enum FetchPermissionsOptionRef<'a> {
pub struct FetchOptions<'a> { pub struct FetchOptions<'a> {
pub specifier: &'a ModuleSpecifier, pub specifier: &'a ModuleSpecifier,
pub permissions: FetchPermissionsOptionRef<'a>, pub permissions: FetchPermissionsOptionRef<'a>,
pub maybe_auth: Option<(header::HeaderName, header::HeaderValue)>,
pub maybe_accept: Option<&'a str>, pub maybe_accept: Option<&'a str>,
pub maybe_cache_setting: Option<&'a CacheSetting>, pub maybe_cache_setting: Option<&'a CacheSetting>,
} }
@ -333,7 +335,7 @@ impl FileFetcher {
) )
})?; })?;
let bytes = blob.read_all().await?; let bytes = blob.read_all().await;
let headers = let headers =
HashMap::from([("content-type".to_string(), blob.media_type.clone())]); HashMap::from([("content-type".to_string(), blob.media_type.clone())]);
@ -350,6 +352,7 @@ impl FileFetcher {
maybe_accept: Option<&str>, maybe_accept: Option<&str>,
cache_setting: &CacheSetting, cache_setting: &CacheSetting,
maybe_checksum: Option<&LoaderChecksum>, maybe_checksum: Option<&LoaderChecksum>,
maybe_auth: Option<(header::HeaderName, header::HeaderValue)>,
) -> Result<FileOrRedirect, AnyError> { ) -> Result<FileOrRedirect, AnyError> {
debug!( debug!(
"FileFetcher::fetch_remote_no_follow - specifier: {}", "FileFetcher::fetch_remote_no_follow - specifier: {}",
@ -442,6 +445,7 @@ impl FileFetcher {
.as_ref() .as_ref()
.map(|(_, etag)| etag.clone()), .map(|(_, etag)| etag.clone()),
maybe_auth_token: maybe_auth_token.clone(), maybe_auth_token: maybe_auth_token.clone(),
maybe_auth: maybe_auth.clone(),
maybe_progress_guard: maybe_progress_guard.as_ref(), maybe_progress_guard: maybe_progress_guard.as_ref(),
}) })
.await? .await?
@ -538,7 +542,18 @@ impl FileFetcher {
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
) -> Result<File, AnyError> { ) -> Result<File, AnyError> {
self self
.fetch_inner(specifier, FetchPermissionsOptionRef::AllowAll) .fetch_inner(specifier, None, FetchPermissionsOptionRef::AllowAll)
.await
}
#[inline(always)]
pub async fn fetch_bypass_permissions_with_maybe_auth(
&self,
specifier: &ModuleSpecifier,
maybe_auth: Option<(header::HeaderName, header::HeaderValue)>,
) -> Result<File, AnyError> {
self
.fetch_inner(specifier, maybe_auth, FetchPermissionsOptionRef::AllowAll)
.await .await
} }
@ -552,6 +567,7 @@ impl FileFetcher {
self self
.fetch_inner( .fetch_inner(
specifier, specifier,
None,
FetchPermissionsOptionRef::StaticContainer(permissions), FetchPermissionsOptionRef::StaticContainer(permissions),
) )
.await .await
@ -560,12 +576,14 @@ impl FileFetcher {
async fn fetch_inner( async fn fetch_inner(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
maybe_auth: Option<(header::HeaderName, header::HeaderValue)>,
permissions: FetchPermissionsOptionRef<'_>, permissions: FetchPermissionsOptionRef<'_>,
) -> Result<File, AnyError> { ) -> Result<File, AnyError> {
self self
.fetch_with_options(FetchOptions { .fetch_with_options(FetchOptions {
specifier, specifier,
permissions, permissions,
maybe_auth,
maybe_accept: None, maybe_accept: None,
maybe_cache_setting: None, maybe_cache_setting: None,
}) })
@ -585,12 +603,14 @@ impl FileFetcher {
max_redirect: usize, max_redirect: usize,
) -> Result<File, AnyError> { ) -> Result<File, AnyError> {
let mut specifier = Cow::Borrowed(options.specifier); let mut specifier = Cow::Borrowed(options.specifier);
let mut maybe_auth = options.maybe_auth.clone();
for _ in 0..=max_redirect { for _ in 0..=max_redirect {
match self match self
.fetch_no_follow_with_options(FetchNoFollowOptions { .fetch_no_follow_with_options(FetchNoFollowOptions {
fetch_options: FetchOptions { fetch_options: FetchOptions {
specifier: &specifier, specifier: &specifier,
permissions: options.permissions, permissions: options.permissions,
maybe_auth: maybe_auth.clone(),
maybe_accept: options.maybe_accept, maybe_accept: options.maybe_accept,
maybe_cache_setting: options.maybe_cache_setting, maybe_cache_setting: options.maybe_cache_setting,
}, },
@ -602,6 +622,10 @@ impl FileFetcher {
return Ok(file); return Ok(file);
} }
FileOrRedirect::Redirect(redirect_specifier) => { FileOrRedirect::Redirect(redirect_specifier) => {
// If we were redirected to another origin, don't send the auth header anymore.
if redirect_specifier.origin() != specifier.origin() {
maybe_auth = None;
}
specifier = Cow::Owned(redirect_specifier); specifier = Cow::Owned(redirect_specifier);
} }
} }
@ -666,6 +690,7 @@ impl FileFetcher {
options.maybe_accept, options.maybe_accept,
options.maybe_cache_setting.unwrap_or(&self.cache_setting), options.maybe_cache_setting.unwrap_or(&self.cache_setting),
maybe_checksum, maybe_checksum,
options.maybe_auth,
) )
.await .await
} }
@ -726,7 +751,7 @@ mod tests {
maybe_temp_dir: Option<TempDir>, maybe_temp_dir: Option<TempDir>,
) -> (FileFetcher, TempDir, Arc<BlobStore>) { ) -> (FileFetcher, TempDir, Arc<BlobStore>) {
let temp_dir = maybe_temp_dir.unwrap_or_default(); 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 blob_store: Arc<BlobStore> = Default::default();
let file_fetcher = FileFetcher::new( let file_fetcher = FileFetcher::new(
Arc::new(GlobalHttpCache::new(location, RealDenoCacheEnv)), Arc::new(GlobalHttpCache::new(location, RealDenoCacheEnv)),
@ -756,6 +781,7 @@ mod tests {
FetchOptions { FetchOptions {
specifier, specifier,
permissions: FetchPermissionsOptionRef::AllowAll, permissions: FetchPermissionsOptionRef::AllowAll,
maybe_auth: None,
maybe_accept: None, maybe_accept: None,
maybe_cache_setting: Some(&file_fetcher.cache_setting), maybe_cache_setting: Some(&file_fetcher.cache_setting),
}, },
@ -964,7 +990,7 @@ mod tests {
// This creates a totally new instance, simulating another Deno process // This creates a totally new instance, simulating another Deno process
// invocation and indicates to "cache bust". // 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( let file_fetcher = FileFetcher::new(
Arc::new(GlobalHttpCache::new( Arc::new(GlobalHttpCache::new(
location, location,
@ -990,7 +1016,7 @@ mod tests {
async fn test_fetch_uses_cache() { async fn test_fetch_uses_cache() {
let _http_server_guard = test_util::http_server(); let _http_server_guard = test_util::http_server();
let temp_dir = TempDir::new(); 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 = let specifier =
resolve_url("http://localhost:4545/subdir/mismatch_ext.ts").unwrap(); resolve_url("http://localhost:4545/subdir/mismatch_ext.ts").unwrap();
@ -1156,7 +1182,7 @@ mod tests {
async fn test_fetch_uses_cache_with_redirects() { async fn test_fetch_uses_cache_with_redirects() {
let _http_server_guard = test_util::http_server(); let _http_server_guard = test_util::http_server();
let temp_dir = TempDir::new(); 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 = let specifier =
resolve_url("http://localhost:4548/subdir/mismatch_ext.ts").unwrap(); resolve_url("http://localhost:4548/subdir/mismatch_ext.ts").unwrap();
let redirected_specifier = let redirected_specifier =
@ -1255,6 +1281,7 @@ mod tests {
FetchOptions { FetchOptions {
specifier: &specifier, specifier: &specifier,
permissions: FetchPermissionsOptionRef::AllowAll, permissions: FetchPermissionsOptionRef::AllowAll,
maybe_auth: None,
maybe_accept: None, maybe_accept: None,
maybe_cache_setting: Some(&file_fetcher.cache_setting), maybe_cache_setting: Some(&file_fetcher.cache_setting),
}, },
@ -1268,6 +1295,7 @@ mod tests {
FetchOptions { FetchOptions {
specifier: &specifier, specifier: &specifier,
permissions: FetchPermissionsOptionRef::AllowAll, permissions: FetchPermissionsOptionRef::AllowAll,
maybe_auth: None,
maybe_accept: None, maybe_accept: None,
maybe_cache_setting: Some(&file_fetcher.cache_setting), maybe_cache_setting: Some(&file_fetcher.cache_setting),
}, },
@ -1324,7 +1352,7 @@ mod tests {
async fn test_fetch_no_remote() { async fn test_fetch_no_remote() {
let _http_server_guard = test_util::http_server(); let _http_server_guard = test_util::http_server();
let temp_dir = TempDir::new(); 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( let file_fetcher = FileFetcher::new(
Arc::new(GlobalHttpCache::new( Arc::new(GlobalHttpCache::new(
location, location,
@ -1350,7 +1378,7 @@ mod tests {
async fn test_fetch_cache_only() { async fn test_fetch_cache_only() {
let _http_server_guard = test_util::http_server(); let _http_server_guard = test_util::http_server();
let temp_dir = TempDir::new(); 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( let file_fetcher_01 = FileFetcher::new(
Arc::new(GlobalHttpCache::new(location.clone(), RealDenoCacheEnv)), Arc::new(GlobalHttpCache::new(location.clone(), RealDenoCacheEnv)),
CacheSetting::Only, CacheSetting::Only,

View file

@ -13,14 +13,19 @@ use crate::colors;
use crate::errors::get_error_class_name; use crate::errors::get_error_class_name;
use crate::file_fetcher::FileFetcher; use crate::file_fetcher::FileFetcher;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::resolver::CliGraphResolver; use crate::resolver::CjsTracker;
use crate::resolver::SloppyImportsResolver; use crate::resolver::CliResolver;
use crate::resolver::CliSloppyImportsResolver;
use crate::resolver::SloppyImportsCachedFs;
use crate::tools::check; use crate::tools::check;
use crate::tools::check::TypeChecker; use crate::tools::check::TypeChecker;
use crate::util::file_watcher::WatcherCommunicator; use crate::util::file_watcher::WatcherCommunicator;
use crate::util::fs::canonicalize_path; use crate::util::fs::canonicalize_path;
use deno_config::deno_json::JsxImportSourceConfig;
use deno_config::workspace::JsrPackageConfig; use deno_config::workspace::JsrPackageConfig;
use deno_core::anyhow::bail;
use deno_graph::source::LoaderChecksum; use deno_graph::source::LoaderChecksum;
use deno_graph::source::ResolutionMode;
use deno_graph::FillFromLockfileOptions; use deno_graph::FillFromLockfileOptions;
use deno_graph::JsrLoadError; use deno_graph::JsrLoadError;
use deno_graph::ModuleLoadError; use deno_graph::ModuleLoadError;
@ -31,7 +36,6 @@ use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex; use deno_core::parking_lot::Mutex;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_graph::source::Loader; use deno_graph::source::Loader;
use deno_graph::source::ResolutionMode;
use deno_graph::source::ResolveError; use deno_graph::source::ResolveError;
use deno_graph::GraphKind; use deno_graph::GraphKind;
use deno_graph::ModuleError; use deno_graph::ModuleError;
@ -39,13 +43,15 @@ use deno_graph::ModuleGraph;
use deno_graph::ModuleGraphError; use deno_graph::ModuleGraphError;
use deno_graph::ResolutionError; use deno_graph::ResolutionError;
use deno_graph::SpecifierError; 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_fs::FileSystem;
use deno_runtime::deno_node; use deno_runtime::deno_node;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::fs_util::specifier_to_file_path;
use deno_semver::jsr::JsrDepPackageReq; use deno_semver::jsr::JsrDepPackageReq;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use import_map::ImportMapError; use import_map::ImportMapError;
use node_resolver::InNpmPackageChecker;
use std::collections::HashSet; use std::collections::HashSet;
use std::error::Error; use std::error::Error;
use std::ops::Deref; use std::ops::Deref;
@ -182,7 +188,7 @@ pub fn graph_exit_integrity_errors(graph: &ModuleGraph) {
fn exit_for_integrity_error(err: &ModuleError) { fn exit_for_integrity_error(err: &ModuleError) {
if let Some(err_message) = enhanced_integrity_error_message(err) { if let Some(err_message) = enhanced_integrity_error_message(err) {
log::error!("{} {}", colors::red("error:"), err_message); log::error!("{} {}", colors::red("error:"), err_message);
std::process::exit(10); deno_runtime::exit(10);
} }
} }
@ -375,48 +381,54 @@ pub struct BuildFastCheckGraphOptions<'a> {
} }
pub struct ModuleGraphBuilder { pub struct ModuleGraphBuilder {
options: Arc<CliOptions>,
caches: Arc<cache::Caches>, caches: Arc<cache::Caches>,
cjs_tracker: Arc<CjsTracker>,
cli_options: Arc<CliOptions>,
file_fetcher: Arc<FileFetcher>,
fs: Arc<dyn FileSystem>, fs: Arc<dyn FileSystem>,
resolver: Arc<CliGraphResolver>, global_http_cache: Arc<GlobalHttpCache>,
npm_resolver: Arc<dyn CliNpmResolver>, in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
module_info_cache: Arc<ModuleInfoCache>,
parsed_source_cache: Arc<ParsedSourceCache>,
lockfile: Option<Arc<CliLockfile>>, lockfile: Option<Arc<CliLockfile>>,
maybe_file_watcher_reporter: Option<FileWatcherReporter>, maybe_file_watcher_reporter: Option<FileWatcherReporter>,
file_fetcher: Arc<FileFetcher>, module_info_cache: Arc<ModuleInfoCache>,
global_http_cache: Arc<GlobalHttpCache>, npm_resolver: Arc<dyn CliNpmResolver>,
parsed_source_cache: Arc<ParsedSourceCache>,
resolver: Arc<CliResolver>,
root_permissions_container: PermissionsContainer, root_permissions_container: PermissionsContainer,
} }
impl ModuleGraphBuilder { impl ModuleGraphBuilder {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
options: Arc<CliOptions>,
caches: Arc<cache::Caches>, caches: Arc<cache::Caches>,
cjs_tracker: Arc<CjsTracker>,
cli_options: Arc<CliOptions>,
file_fetcher: Arc<FileFetcher>,
fs: Arc<dyn FileSystem>, fs: Arc<dyn FileSystem>,
resolver: Arc<CliGraphResolver>, global_http_cache: Arc<GlobalHttpCache>,
npm_resolver: Arc<dyn CliNpmResolver>, in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
module_info_cache: Arc<ModuleInfoCache>,
parsed_source_cache: Arc<ParsedSourceCache>,
lockfile: Option<Arc<CliLockfile>>, lockfile: Option<Arc<CliLockfile>>,
maybe_file_watcher_reporter: Option<FileWatcherReporter>, maybe_file_watcher_reporter: Option<FileWatcherReporter>,
file_fetcher: Arc<FileFetcher>, module_info_cache: Arc<ModuleInfoCache>,
global_http_cache: Arc<GlobalHttpCache>, npm_resolver: Arc<dyn CliNpmResolver>,
parsed_source_cache: Arc<ParsedSourceCache>,
resolver: Arc<CliResolver>,
root_permissions_container: PermissionsContainer, root_permissions_container: PermissionsContainer,
) -> Self { ) -> Self {
Self { Self {
options,
caches, caches,
cjs_tracker,
cli_options,
file_fetcher,
fs, fs,
resolver, global_http_cache,
npm_resolver, in_npm_pkg_checker,
module_info_cache,
parsed_source_cache,
lockfile, lockfile,
maybe_file_watcher_reporter, maybe_file_watcher_reporter,
file_fetcher, module_info_cache,
global_http_cache, npm_resolver,
parsed_source_cache,
resolver,
root_permissions_container, root_permissions_container,
} }
} }
@ -502,19 +514,17 @@ impl ModuleGraphBuilder {
} }
let maybe_imports = if options.graph_kind.include_types() { let maybe_imports = if options.graph_kind.include_types() {
self.options.to_compiler_option_types()? self.cli_options.to_compiler_option_types()?
} else { } else {
Vec::new() Vec::new()
}; };
let analyzer = self let analyzer = self.module_info_cache.as_module_analyzer();
.module_info_cache
.as_module_analyzer(&self.parsed_source_cache);
let mut loader = match options.loader { let mut loader = match options.loader {
Some(loader) => MutLoaderRef::Borrowed(loader), Some(loader) => MutLoaderRef::Borrowed(loader),
None => MutLoaderRef::Owned(self.create_graph_loader()), None => MutLoaderRef::Owned(self.create_graph_loader()),
}; };
let cli_resolver = &self.resolver; let cli_resolver = &self.resolver;
let graph_resolver = cli_resolver.as_graph_resolver(); let graph_resolver = self.create_graph_resolver()?;
let graph_npm_resolver = cli_resolver.create_graph_npm_resolver(); let graph_npm_resolver = cli_resolver.create_graph_npm_resolver();
let maybe_file_watcher_reporter = self let maybe_file_watcher_reporter = self
.maybe_file_watcher_reporter .maybe_file_watcher_reporter
@ -539,7 +549,7 @@ impl ModuleGraphBuilder {
npm_resolver: Some(&graph_npm_resolver), npm_resolver: Some(&graph_npm_resolver),
module_analyzer: &analyzer, module_analyzer: &analyzer,
reporter: maybe_file_watcher_reporter, reporter: maybe_file_watcher_reporter,
resolver: Some(graph_resolver), resolver: Some(&graph_resolver),
locker: locker.as_mut().map(|l| l as _), locker: locker.as_mut().map(|l| l as _),
}, },
) )
@ -556,7 +566,7 @@ impl ModuleGraphBuilder {
// ensure an "npm install" is done if the user has explicitly // ensure an "npm install" is done if the user has explicitly
// opted into using a node_modules directory // opted into using a node_modules directory
if self if self
.options .cli_options
.node_modules_dir()? .node_modules_dir()?
.map(|m| m.uses_node_modules_dir()) .map(|m| m.uses_node_modules_dir())
.unwrap_or(false) .unwrap_or(false)
@ -592,6 +602,12 @@ impl ModuleGraphBuilder {
let initial_package_deps_len = graph.packages.package_deps_sum(); let initial_package_deps_len = graph.packages.package_deps_sum();
let initial_package_mappings_len = graph.packages.mappings().len(); 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; graph.build(roots, loader, options).await;
let has_redirects_changed = graph.redirects.len() != initial_redirects_len; let has_redirects_changed = graph.redirects.len() != initial_redirects_len;
@ -656,16 +672,16 @@ impl ModuleGraphBuilder {
}; };
let parser = self.parsed_source_cache.as_capturing_parser(); let parser = self.parsed_source_cache.as_capturing_parser();
let cli_resolver = &self.resolver; let cli_resolver = &self.resolver;
let graph_resolver = cli_resolver.as_graph_resolver(); let graph_resolver = self.create_graph_resolver()?;
let graph_npm_resolver = cli_resolver.create_graph_npm_resolver(); let graph_npm_resolver = cli_resolver.create_graph_npm_resolver();
graph.build_fast_check_type_graph( graph.build_fast_check_type_graph(
deno_graph::BuildFastCheckTypeGraphOptions { deno_graph::BuildFastCheckTypeGraphOptions {
jsr_url_provider: &CliJsrUrlProvider, es_parser: Some(&parser),
fast_check_cache: fast_check_cache.as_ref().map(|c| c as _), fast_check_cache: fast_check_cache.as_ref().map(|c| c as _),
fast_check_dts: false, fast_check_dts: false,
module_parser: Some(&parser), jsr_url_provider: &CliJsrUrlProvider,
resolver: Some(graph_resolver), resolver: Some(&graph_resolver),
npm_resolver: Some(&graph_npm_resolver), npm_resolver: Some(&graph_npm_resolver),
workspace_fast_check: options.workspace_fast_check, workspace_fast_check: options.workspace_fast_check,
}, },
@ -684,14 +700,15 @@ impl ModuleGraphBuilder {
) -> cache::FetchCacher { ) -> cache::FetchCacher {
cache::FetchCacher::new( cache::FetchCacher::new(
self.file_fetcher.clone(), self.file_fetcher.clone(),
self.fs.clone(),
self.global_http_cache.clone(), self.global_http_cache.clone(),
self.npm_resolver.clone(), self.in_npm_pkg_checker.clone(),
self.module_info_cache.clone(), self.module_info_cache.clone(),
cache::FetchCacherOptions { cache::FetchCacherOptions {
file_header_overrides: self.options.resolve_file_header_overrides(), file_header_overrides: self.cli_options.resolve_file_header_overrides(),
permissions, permissions,
is_deno_publish: matches!( is_deno_publish: matches!(
self.options.sub_command(), self.cli_options.sub_command(),
crate::args::DenoSubcommand::Publish { .. } crate::args::DenoSubcommand::Publish { .. }
), ),
}, },
@ -718,16 +735,28 @@ impl ModuleGraphBuilder {
&self.fs, &self.fs,
roots, roots,
GraphValidOptions { GraphValidOptions {
kind: if self.options.type_check_mode().is_true() { kind: if self.cli_options.type_check_mode().is_true() {
GraphKind::All GraphKind::All
} else { } else {
GraphKind::CodeOnly GraphKind::CodeOnly
}, },
check_js: self.options.check_js(), check_js: self.cli_options.check_js(),
exit_integrity_errors: true, exit_integrity_errors: true,
}, },
) )
} }
fn create_graph_resolver(&self) -> Result<CliGraphResolver, AnyError> {
let jsx_import_source_config = self
.cli_options
.workspace()
.to_maybe_jsx_import_source_config()?;
Ok(CliGraphResolver {
cjs_tracker: &self.cjs_tracker,
resolver: &self.resolver,
jsx_import_source_config,
})
}
} }
/// Adds more explanatory information to a resolution error. /// Adds more explanatory information to a resolution error.
@ -765,8 +794,8 @@ fn enhanced_sloppy_imports_error_message(
match error { match error {
ModuleError::LoadingErr(specifier, _, ModuleLoadError::Loader(_)) // ex. "Is a directory" error ModuleError::LoadingErr(specifier, _, ModuleLoadError::Loader(_)) // ex. "Is a directory" error
| ModuleError::Missing(specifier, _) => { | ModuleError::Missing(specifier, _) => {
let additional_message = SloppyImportsResolver::new(fs.clone()) let additional_message = CliSloppyImportsResolver::new(SloppyImportsCachedFs::new(fs.clone()))
.resolve(specifier, ResolutionMode::Execution)? .resolve(specifier, SloppyImportsResolutionMode::Execution)?
.as_suggestion_message(); .as_suggestion_message();
Some(format!( Some(format!(
"{} {} or run with --unstable-sloppy-imports", "{} {} or run with --unstable-sloppy-imports",
@ -948,7 +977,7 @@ pub fn has_graph_root_local_dependent_changed(
}, },
); );
while let Some((s, _)) = dependent_specifiers.next() { 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 let Ok(path) = canonicalize_path(&path) {
if canonicalized_changed_paths.contains(&path) { if canonicalized_changed_paths.contains(&path) {
return true; return true;
@ -990,7 +1019,11 @@ impl deno_graph::source::Reporter for FileWatcherReporter {
) { ) {
let mut file_paths = self.file_paths.lock(); let mut file_paths = self.file_paths.lock();
if specifier.scheme() == "file" { if specifier.scheme() == "file" {
file_paths.push(specifier.to_file_path().unwrap()); // Don't trust that the path is a valid path at this point:
// https://github.com/denoland/deno/issues/26209.
if let Ok(file_path) = specifier.to_file_path() {
file_paths.push(file_path);
}
} }
if modules_done == modules_total { if modules_done == modules_total {
@ -1128,6 +1161,53 @@ fn format_deno_graph_error(err: &dyn Error) -> String {
message message
} }
#[derive(Debug)]
struct CliGraphResolver<'a> {
cjs_tracker: &'a CjsTracker,
resolver: &'a CliResolver,
jsx_import_source_config: Option<JsxImportSourceConfig>,
}
impl<'a> deno_graph::source::Resolver for CliGraphResolver<'a> {
fn default_jsx_import_source(&self) -> Option<String> {
self
.jsx_import_source_config
.as_ref()
.and_then(|c| c.default_specifier.clone())
}
fn default_jsx_import_source_types(&self) -> Option<String> {
self
.jsx_import_source_config
.as_ref()
.and_then(|c| c.default_types_specifier.clone())
}
fn jsx_import_source_module(&self) -> &str {
self
.jsx_import_source_config
.as_ref()
.map(|c| c.module.as_str())
.unwrap_or(deno_graph::source::DEFAULT_JSX_IMPORT_SOURCE_MODULE)
}
fn resolve(
&self,
raw_specifier: &str,
referrer_range: &deno_graph::Range,
mode: ResolutionMode,
) -> Result<ModuleSpecifier, ResolveError> {
self.resolver.resolve(
raw_specifier,
referrer_range,
self
.cjs_tracker
.get_referrer_kind(&referrer_range.specifier),
mode,
)
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::sync::Arc; use std::sync::Arc;

View file

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

View file

@ -2,6 +2,7 @@
use super::diagnostics::DenoDiagnostic; use super::diagnostics::DenoDiagnostic;
use super::diagnostics::DiagnosticSource; use super::diagnostics::DiagnosticSource;
use super::documents::Document;
use super::documents::Documents; use super::documents::Documents;
use super::language_server; use super::language_server;
use super::resolver::LspResolver; use super::resolver::LspResolver;
@ -9,13 +10,17 @@ use super::tsc;
use super::urls::url_to_uri; use super::urls::url_to_uri;
use crate::args::jsr_url; use crate::args::jsr_url;
use crate::lsp::logging::lsp_warn;
use crate::lsp::search::PackageSearchApi;
use crate::tools::lint::CliLinter; use crate::tools::lint::CliLinter;
use crate::util::path::relative_specifier;
use deno_config::workspace::MappedResolution;
use deno_graph::source::ResolutionMode;
use deno_lint::diagnostic::LintDiagnosticRange; use deno_lint::diagnostic::LintDiagnosticRange;
use deno_ast::SourceRange; use deno_ast::SourceRange;
use deno_ast::SourceRangedForSpanned; use deno_ast::SourceRangedForSpanned;
use deno_ast::SourceTextInfo; use deno_ast::SourceTextInfo;
use deno_core::anyhow::anyhow;
use deno_core::error::custom_error; use deno_core::error::custom_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::serde::Deserialize; use deno_core::serde::Deserialize;
@ -23,8 +28,8 @@ use deno_core::serde::Serialize;
use deno_core::serde_json; use deno_core::serde_json;
use deno_core::serde_json::json; use deno_core::serde_json::json;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_path_util::url_to_file_path;
use deno_runtime::deno_node::PathClean; use deno_runtime::deno_node::PathClean;
use deno_runtime::fs_util::specifier_to_file_path;
use deno_semver::jsr::JsrPackageNvReference; use deno_semver::jsr::JsrPackageNvReference;
use deno_semver::jsr::JsrPackageReqReference; use deno_semver::jsr::JsrPackageReqReference;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
@ -34,13 +39,15 @@ use deno_semver::package::PackageReq;
use deno_semver::package::PackageReqReference; use deno_semver::package::PackageReqReference;
use deno_semver::Version; use deno_semver::Version;
use import_map::ImportMap; use import_map::ImportMap;
use node_resolver::NpmResolver; use node_resolver::NodeModuleKind;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use std::borrow::Cow;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::HashSet; use std::collections::HashSet;
use std::path::Path; use std::path::Path;
use text_lines::LineAndColumnIndex;
use tower_lsp::lsp_types as lsp; use tower_lsp::lsp_types as lsp;
use tower_lsp::lsp_types::Position; use tower_lsp::lsp_types::Position;
use tower_lsp::lsp_types::Range; use tower_lsp::lsp_types::Range;
@ -225,6 +232,8 @@ pub struct TsResponseImportMapper<'a> {
documents: &'a Documents, documents: &'a Documents,
maybe_import_map: Option<&'a ImportMap>, maybe_import_map: Option<&'a ImportMap>,
resolver: &'a LspResolver, resolver: &'a LspResolver,
tsc_specifier_map: &'a tsc::TscSpecifierMap,
file_referrer: ModuleSpecifier,
} }
impl<'a> TsResponseImportMapper<'a> { impl<'a> TsResponseImportMapper<'a> {
@ -232,11 +241,15 @@ impl<'a> TsResponseImportMapper<'a> {
documents: &'a Documents, documents: &'a Documents,
maybe_import_map: Option<&'a ImportMap>, maybe_import_map: Option<&'a ImportMap>,
resolver: &'a LspResolver, resolver: &'a LspResolver,
tsc_specifier_map: &'a tsc::TscSpecifierMap,
file_referrer: &ModuleSpecifier,
) -> Self { ) -> Self {
Self { Self {
documents, documents,
maybe_import_map, maybe_import_map,
resolver, resolver,
tsc_specifier_map,
file_referrer: file_referrer.clone(),
} }
} }
@ -257,8 +270,6 @@ impl<'a> TsResponseImportMapper<'a> {
} }
} }
let file_referrer = self.documents.get_file_referrer(referrer);
if let Some(jsr_path) = specifier.as_str().strip_prefix(jsr_url().as_str()) if let Some(jsr_path) = specifier.as_str().strip_prefix(jsr_url().as_str())
{ {
let mut segments = jsr_path.split('/'); let mut segments = jsr_path.split('/');
@ -273,7 +284,7 @@ impl<'a> TsResponseImportMapper<'a> {
let export = self.resolver.jsr_lookup_export_for_path( let export = self.resolver.jsr_lookup_export_for_path(
&nv, &nv,
&path, &path,
file_referrer.as_deref(), Some(&self.file_referrer),
)?; )?;
let sub_path = (export != ".").then_some(export); let sub_path = (export != ".").then_some(export);
let mut req = None; let mut req = None;
@ -299,7 +310,7 @@ impl<'a> TsResponseImportMapper<'a> {
req = req.or_else(|| { req = req.or_else(|| {
self self
.resolver .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 spec_str = if let Some(req) = req {
let req_ref = PackageReqReference { req, sub_path }; let req_ref = PackageReqReference { req, sub_path };
@ -329,9 +340,13 @@ impl<'a> TsResponseImportMapper<'a> {
if let Some(npm_resolver) = self if let Some(npm_resolver) = self
.resolver .resolver
.maybe_managed_npm_resolver(file_referrer.as_deref()) .maybe_managed_npm_resolver(Some(&self.file_referrer))
{ {
if npm_resolver.in_npm_package(specifier) { let in_npm_pkg = self
.resolver
.in_npm_pkg_checker(Some(&self.file_referrer))
.in_npm_package(specifier);
if in_npm_pkg {
if let Ok(Some(pkg_id)) = if let Ok(Some(pkg_id)) =
npm_resolver.resolve_pkg_id_from_specifier(specifier) npm_resolver.resolve_pkg_id_from_specifier(specifier)
{ {
@ -378,6 +393,11 @@ impl<'a> TsResponseImportMapper<'a> {
} }
} }
} }
} else if let Some(dep_name) = self
.resolver
.file_url_to_package_json_dep(specifier, Some(&self.file_referrer))
{
return Some(dep_name);
} }
// check if the import map has this specifier // check if the import map has this specifier
@ -401,7 +421,7 @@ impl<'a> TsResponseImportMapper<'a> {
.flatten()?; .flatten()?;
let root_folder = package_json.path.parent()?; 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()]; let mut search_paths = vec![specifier_path.clone()];
// TypeScript will provide a .js extension for quick fixes, so do // TypeScript will provide a .js extension for quick fixes, so do
// a search for the .d.ts file instead // a search for the .d.ts file instead
@ -447,24 +467,65 @@ impl<'a> TsResponseImportMapper<'a> {
&self, &self,
specifier: &str, specifier: &str,
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
referrer_kind: NodeModuleKind,
) -> Option<String> { ) -> Option<String> {
if let Ok(specifier) = referrer.join(specifier) { let specifier_stem = specifier.strip_suffix(".js").unwrap_or(specifier);
if let Some(specifier) = self.check_specifier(&specifier, referrer) { let specifiers = std::iter::once(Cow::Borrowed(specifier)).chain(
SUPPORTED_EXTENSIONS
.iter()
.map(|ext| Cow::Owned(format!("{specifier_stem}{ext}"))),
);
for specifier in specifiers {
if let Some(specifier) = self
.resolver
.as_cli_resolver(Some(&self.file_referrer))
.resolve(
&specifier,
&deno_graph::Range {
specifier: referrer.clone(),
start: deno_graph::Position::zeroed(),
end: deno_graph::Position::zeroed(),
},
referrer_kind,
ResolutionMode::Types,
)
.ok()
.and_then(|s| self.tsc_specifier_map.normalize(s.as_str()).ok())
.filter(|s| self.documents.exists(s, Some(&self.file_referrer)))
{
if let Some(specifier) = self
.check_specifier(&specifier, referrer)
.or_else(|| relative_specifier(referrer, &specifier))
.filter(|s| !s.contains("/node_modules/"))
{
return Some(specifier); return Some(specifier);
} }
} }
let specifier = specifier.strip_suffix(".js").unwrap_or(specifier);
for ext in SUPPORTED_EXTENSIONS {
let specifier_with_ext = format!("{specifier}{ext}");
if self
.documents
.contains_import(&specifier_with_ext, referrer)
{
return Some(specifier_with_ext);
}
} }
None None
} }
pub fn is_valid_import(
&self,
specifier_text: &str,
referrer: &ModuleSpecifier,
referrer_kind: NodeModuleKind,
) -> bool {
self
.resolver
.as_cli_resolver(Some(&self.file_referrer))
.resolve(
specifier_text,
&deno_graph::Range {
specifier: referrer.clone(),
start: deno_graph::Position::zeroed(),
end: deno_graph::Position::zeroed(),
},
referrer_kind,
deno_graph::source::ResolutionMode::Types,
)
.is_ok()
}
} }
fn try_reverse_map_package_json_exports( fn try_reverse_map_package_json_exports(
@ -529,9 +590,11 @@ fn try_reverse_map_package_json_exports(
/// like an import and rewrite the import specifier to include the extension /// like an import and rewrite the import specifier to include the extension
pub fn fix_ts_import_changes( pub fn fix_ts_import_changes(
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
referrer_kind: NodeModuleKind,
changes: &[tsc::FileTextChanges], changes: &[tsc::FileTextChanges],
import_mapper: &TsResponseImportMapper, language_server: &language_server::Inner,
) -> Result<Vec<tsc::FileTextChanges>, AnyError> { ) -> Result<Vec<tsc::FileTextChanges>, AnyError> {
let import_mapper = language_server.get_ts_response_import_mapper(referrer);
let mut r = Vec::new(); let mut r = Vec::new();
for change in changes { for change in changes {
let mut text_changes = Vec::new(); let mut text_changes = Vec::new();
@ -544,8 +607,8 @@ pub fn fix_ts_import_changes(
if let Some(captures) = IMPORT_SPECIFIER_RE.captures(line) { if let Some(captures) = IMPORT_SPECIFIER_RE.captures(line) {
let specifier = let specifier =
captures.iter().skip(1).find_map(|s| s).unwrap().as_str(); captures.iter().skip(1).find_map(|s| s).unwrap().as_str();
if let Some(new_specifier) = if let Some(new_specifier) = import_mapper
import_mapper.check_unresolved_specifier(specifier, referrer) .check_unresolved_specifier(specifier, referrer, referrer_kind)
{ {
line.replace(specifier, &new_specifier) line.replace(specifier, &new_specifier)
} else { } else {
@ -573,31 +636,29 @@ pub fn fix_ts_import_changes(
/// Fix tsc import code actions so that the module specifier is correct for /// Fix tsc import code actions so that the module specifier is correct for
/// resolution by Deno (includes the extension). /// resolution by Deno (includes the extension).
fn fix_ts_import_action( fn fix_ts_import_action<'a>(
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
action: &tsc::CodeFixAction, referrer_kind: NodeModuleKind,
import_mapper: &TsResponseImportMapper, action: &'a tsc::CodeFixAction,
) -> Result<tsc::CodeFixAction, AnyError> { language_server: &language_server::Inner,
if matches!( ) -> Option<Cow<'a, tsc::CodeFixAction>> {
if !matches!(
action.fix_name.as_str(), action.fix_name.as_str(),
"import" | "fixMissingFunctionDeclaration" "import" | "fixMissingFunctionDeclaration"
) { ) {
let change = action return Some(Cow::Borrowed(action));
.changes }
.first() let specifier = (|| {
.ok_or_else(|| anyhow!("Unexpected action changes."))?; let text_change = action.changes.first()?.text_changes.first()?;
let text_change = change let captures = IMPORT_SPECIFIER_RE.captures(&text_change.new_text)?;
.text_changes Some(captures.get(1)?.as_str())
.first() })();
.ok_or_else(|| anyhow!("Missing text change."))?; let Some(specifier) = specifier else {
if let Some(captures) = IMPORT_SPECIFIER_RE.captures(&text_change.new_text) return Some(Cow::Borrowed(action));
{ };
let specifier = captures let import_mapper = language_server.get_ts_response_import_mapper(referrer);
.get(1)
.ok_or_else(|| anyhow!("Missing capture."))?
.as_str();
if let Some(new_specifier) = if let Some(new_specifier) =
import_mapper.check_unresolved_specifier(specifier, referrer) import_mapper.check_unresolved_specifier(specifier, referrer, referrer_kind)
{ {
let description = action.description.replace(specifier, &new_specifier); let description = action.description.replace(specifier, &new_specifier);
let changes = action let changes = action
@ -620,19 +681,19 @@ fn fix_ts_import_action(
}) })
.collect(); .collect();
return Ok(tsc::CodeFixAction { Some(Cow::Owned(tsc::CodeFixAction {
description, description,
changes, changes,
commands: None, commands: None,
fix_name: action.fix_name.clone(), fix_name: action.fix_name.clone(),
fix_id: None, fix_id: None,
fix_all_description: None, fix_all_description: None,
}); }))
} else if !import_mapper.is_valid_import(specifier, referrer, referrer_kind) {
None
} else {
Some(Cow::Borrowed(action))
} }
}
}
Ok(action.clone())
} }
/// Determines if two TypeScript diagnostic codes are effectively equivalent. /// Determines if two TypeScript diagnostic codes are effectively equivalent.
@ -693,8 +754,14 @@ pub fn ts_changes_to_edit(
) -> Result<Option<lsp::WorkspaceEdit>, AnyError> { ) -> Result<Option<lsp::WorkspaceEdit>, AnyError> {
let mut text_document_edits = Vec::new(); let mut text_document_edits = Vec::new();
for change in changes { for change in changes {
let text_document_edit = change.to_text_document_edit(language_server)?; let edit = match change.to_text_document_edit(language_server) {
text_document_edits.push(text_document_edit); Ok(e) => e,
Err(err) => {
lsp_warn!("Couldn't covert text document edit: {:#}", err);
continue;
}
};
text_document_edits.push(edit);
} }
Ok(Some(lsp::WorkspaceEdit { Ok(Some(lsp::WorkspaceEdit {
changes: None, changes: None,
@ -703,7 +770,7 @@ pub fn ts_changes_to_edit(
})) }))
} }
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CodeActionData { pub struct CodeActionData {
pub specifier: ModuleSpecifier, pub specifier: ModuleSpecifier,
@ -956,6 +1023,7 @@ impl CodeActionCollection {
pub fn add_ts_fix_action( pub fn add_ts_fix_action(
&mut self, &mut self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
specifier_kind: NodeModuleKind,
action: &tsc::CodeFixAction, action: &tsc::CodeFixAction,
diagnostic: &lsp::Diagnostic, diagnostic: &lsp::Diagnostic,
language_server: &language_server::Inner, language_server: &language_server::Inner,
@ -973,11 +1041,11 @@ impl CodeActionCollection {
"The action returned from TypeScript is unsupported.", "The action returned from TypeScript is unsupported.",
)); ));
} }
let action = fix_ts_import_action( let Some(action) =
specifier, fix_ts_import_action(specifier, specifier_kind, action, language_server)
action, else {
&language_server.get_ts_response_import_mapper(specifier), return Ok(());
)?; };
let edit = ts_changes_to_edit(&action.changes, language_server)?; let edit = ts_changes_to_edit(&action.changes, language_server)?;
let code_action = lsp::CodeAction { let code_action = lsp::CodeAction {
title: action.description.clone(), title: action.description.clone(),
@ -997,7 +1065,7 @@ impl CodeActionCollection {
}); });
self self
.actions .actions
.push(CodeActionKind::Tsc(code_action, action.clone())); .push(CodeActionKind::Tsc(code_action, action.as_ref().clone()));
if let Some(fix_id) = &action.fix_id { if let Some(fix_id) = &action.fix_id {
if let Some(CodeActionKind::Tsc(existing_fix_all, existing_action)) = if let Some(CodeActionKind::Tsc(existing_fix_all, existing_action)) =
@ -1024,10 +1092,12 @@ impl CodeActionCollection {
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
diagnostic: &lsp::Diagnostic, diagnostic: &lsp::Diagnostic,
) { ) {
let data = Some(json!({ let data = action.fix_id.as_ref().map(|fix_id| {
"specifier": specifier, json!(CodeActionData {
"fixId": action.fix_id, specifier: specifier.clone(),
})); fix_id: fix_id.clone(),
})
});
let title = if let Some(description) = &action.fix_all_description { let title = if let Some(description) = &action.fix_all_description {
description.clone() description.clone()
} else { } else {
@ -1151,6 +1221,192 @@ impl CodeActionCollection {
..Default::default() ..Default::default()
})); }));
} }
pub async fn add_source_actions(
&mut self,
document: &Document,
range: &lsp::Range,
language_server: &language_server::Inner,
) {
fn import_start_from_specifier(
document: &Document,
import: &deno_graph::Import,
) -> Option<LineAndColumnIndex> {
// find the top level statement that contains the specifier
let parsed_source = document.maybe_parsed_source()?.as_ref().ok()?;
let text_info = parsed_source.text_info_lazy();
let specifier_range = SourceRange::new(
text_info.loc_to_source_pos(LineAndColumnIndex {
line_index: import.specifier_range.start.line,
column_index: import.specifier_range.start.character,
}),
text_info.loc_to_source_pos(LineAndColumnIndex {
line_index: import.specifier_range.end.line,
column_index: import.specifier_range.end.character,
}),
);
parsed_source
.program_ref()
.body()
.find(|i| i.range().contains(&specifier_range))
.map(|i| text_info.line_and_column_index(i.range().start))
}
async fn deno_types_for_npm_action(
document: &Document,
range: &lsp::Range,
language_server: &language_server::Inner,
) -> Option<lsp::CodeAction> {
let (dep_key, dependency, _) =
document.get_maybe_dependency(&range.end)?;
if dependency.maybe_deno_types_specifier.is_some() {
return None;
}
if dependency.maybe_code.maybe_specifier().is_none()
&& dependency.maybe_type.maybe_specifier().is_none()
{
// We're using byonm and the package is not cached.
return None;
}
let position = deno_graph::Position::new(
range.end.line as usize,
range.end.character as usize,
);
let import_start = dependency.imports.iter().find_map(|i| {
if json!(i.kind) != json!("es") && json!(i.kind) != json!("tsType") {
return None;
}
if !i.specifier_range.includes(&position) {
return None;
}
import_start_from_specifier(document, i)
})?;
let referrer = document.specifier();
let referrer_kind = language_server
.is_cjs_resolver
.get_doc_module_kind(document);
let file_referrer = document.file_referrer();
let config_data = language_server
.config
.tree
.data_for_specifier(file_referrer?)?;
let workspace_resolver = config_data.resolver.clone();
let npm_ref = if let Ok(resolution) =
workspace_resolver.resolve(&dep_key, document.specifier())
{
let specifier = match resolution {
MappedResolution::Normal { specifier, .. }
| MappedResolution::ImportMap { specifier, .. } => specifier,
_ => {
return None;
}
};
NpmPackageReqReference::from_specifier(&specifier).ok()?
} else {
// Only resolve bare package.json deps for byonm.
if !config_data.byonm {
return None;
}
if !language_server.resolver.is_bare_package_json_dep(
&dep_key,
referrer,
referrer_kind,
) {
return None;
}
NpmPackageReqReference::from_str(&format!("npm:{}", &dep_key)).ok()?
};
let package_name = &npm_ref.req().name;
if package_name.starts_with("@types/") {
return None;
}
let managed_npm_resolver = language_server
.resolver
.maybe_managed_npm_resolver(file_referrer);
if let Some(npm_resolver) = managed_npm_resolver {
if !npm_resolver.is_pkg_req_folder_cached(npm_ref.req()) {
return None;
}
}
if language_server
.resolver
.npm_to_file_url(&npm_ref, referrer, referrer_kind, file_referrer)
.is_some()
{
// The package import has types.
return None;
}
let types_package_name = format!("@types/{package_name}");
let types_package_version = language_server
.npm_search_api
.versions(&types_package_name)
.await
.ok()
.and_then(|versions| versions.first().cloned())?;
let types_specifier_text =
if let Some(npm_resolver) = managed_npm_resolver {
let mut specifier_text = if let Some(req) =
npm_resolver.top_package_req_for_name(&types_package_name)
{
format!("npm:{req}")
} else {
format!("npm:{}@^{}", &types_package_name, types_package_version)
};
let specifier = ModuleSpecifier::parse(&specifier_text).ok()?;
if let Some(file_referrer) = file_referrer {
if let Some(text) = language_server
.get_ts_response_import_mapper(file_referrer)
.check_specifier(&specifier, referrer)
{
specifier_text = text;
}
}
specifier_text
} else {
types_package_name.clone()
};
let uri = language_server
.url_map
.specifier_to_uri(referrer, file_referrer)
.ok()?;
let position = lsp::Position {
line: import_start.line_index as u32,
character: import_start.column_index as u32,
};
let new_text = format!(
"{}// @deno-types=\"{}\"\n",
if position.character == 0 { "" } else { "\n" },
&types_specifier_text
);
let text_edit = lsp::TextEdit {
range: lsp::Range {
start: position,
end: position,
},
new_text,
};
Some(lsp::CodeAction {
title: format!(
"Add @deno-types directive for \"{}\"",
&types_specifier_text
),
kind: Some(lsp::CodeActionKind::QUICKFIX),
diagnostics: None,
edit: Some(lsp::WorkspaceEdit {
changes: Some([(uri, vec![text_edit])].into_iter().collect()),
..Default::default()
}),
..Default::default()
})
}
if let Some(action) =
deno_types_for_npm_action(document, range, language_server).await
{
self.actions.push(CodeActionKind::Deno(action));
}
}
} }
/// Prepend the whitespace characters found at the start of line_content to content. /// Prepend the whitespace characters found at the start of line_content to content.

View file

@ -10,7 +10,7 @@ use crate::lsp::logging::lsp_warn;
use deno_core::url::Url; use deno_core::url::Url;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_runtime::fs_util::specifier_to_file_path; use deno_path_util::url_to_file_path;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
@ -24,7 +24,7 @@ pub fn calculate_fs_version(
) -> Option<String> { ) -> Option<String> {
match specifier.scheme() { match specifier.scheme() {
"npm" | "node" | "data" | "blob" => None, "npm" | "node" | "data" | "blob" => None,
"file" => specifier_to_file_path(specifier) "file" => url_to_file_path(specifier)
.ok() .ok()
.and_then(|path| calculate_fs_version_at_path(&path)), .and_then(|path| calculate_fs_version_at_path(&path)),
_ => calculate_fs_version_in_cache(cache, specifier, file_referrer), _ => calculate_fs_version_in_cache(cache, specifier, file_referrer),
@ -82,7 +82,7 @@ impl Default for LspCache {
impl LspCache { impl LspCache {
pub fn new(global_cache_url: Option<Url>) -> Self { pub fn new(global_cache_url: Option<Url>) -> Self {
let global_cache_path = global_cache_url.and_then(|s| { let global_cache_path = global_cache_url.and_then(|s| {
specifier_to_file_path(&s) url_to_file_path(&s)
.inspect(|p| { .inspect(|p| {
lsp_log!("Resolved global cache path: \"{}\"", p.to_string_lossy()); lsp_log!("Resolved global cache path: \"{}\"", p.to_string_lossy());
}) })
@ -94,7 +94,7 @@ impl LspCache {
let deno_dir = DenoDir::new(global_cache_path) let deno_dir = DenoDir::new(global_cache_path)
.expect("should be infallible with absolute custom root"); .expect("should be infallible with absolute custom root");
let global = Arc::new(GlobalHttpCache::new( let global = Arc::new(GlobalHttpCache::new(
deno_dir.deps_folder_path(), deno_dir.remote_folder_path(),
crate::cache::RealDenoCacheEnv, crate::cache::RealDenoCacheEnv,
)); ));
Self { Self {
@ -165,7 +165,7 @@ impl LspCache {
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
) -> Option<ModuleSpecifier> { ) -> Option<ModuleSpecifier> {
let path = specifier_to_file_path(specifier).ok()?; let path = url_to_file_path(specifier).ok()?;
let vendor = self let vendor = self
.vendors_by_scope .vendors_by_scope
.iter() .iter()
@ -176,7 +176,7 @@ impl LspCache {
} }
pub fn is_valid_file_referrer(&self, specifier: &ModuleSpecifier) -> bool { 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) { if !path.starts_with(&self.deno_dir().root) {
return true; return true;
} }

View file

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

View file

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

View file

@ -421,7 +421,7 @@ pub fn collect_test(
) -> Result<Vec<lsp::CodeLens>, AnyError> { ) -> Result<Vec<lsp::CodeLens>, AnyError> {
let mut collector = let mut collector =
DenoTestCollector::new(specifier.clone(), parsed_source.clone()); DenoTestCollector::new(specifier.clone(), parsed_source.clone());
parsed_source.module().visit_with(&mut collector); parsed_source.program().visit_with(&mut collector);
Ok(collector.take()) Ok(collector.take())
} }
@ -581,7 +581,7 @@ mod tests {
.unwrap(); .unwrap();
let mut collector = let mut collector =
DenoTestCollector::new(specifier, parsed_module.clone()); DenoTestCollector::new(specifier, parsed_module.clone());
parsed_module.module().visit_with(&mut collector); parsed_module.program().visit_with(&mut collector);
assert_eq!( assert_eq!(
collector.take(), collector.take(),
vec![ vec![

View file

@ -9,6 +9,7 @@ use super::jsr::CliJsrSearchApi;
use super::lsp_custom; use super::lsp_custom;
use super::npm::CliNpmSearchApi; use super::npm::CliNpmSearchApi;
use super::registries::ModuleRegistry; use super::registries::ModuleRegistry;
use super::resolver::LspIsCjsResolver;
use super::resolver::LspResolver; use super::resolver::LspResolver;
use super::search::PackageSearchApi; use super::search::PackageSearchApi;
use super::tsc; use super::tsc;
@ -29,12 +30,13 @@ use deno_core::serde::Serialize;
use deno_core::serde_json::json; use deno_core::serde_json::json;
use deno_core::url::Position; use deno_core::url::Position;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_runtime::fs_util::specifier_to_file_path; use deno_path_util::url_to_file_path;
use deno_semver::jsr::JsrPackageReqReference; use deno_semver::jsr::JsrPackageReqReference;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use import_map::ImportMap; use import_map::ImportMap;
use indexmap::IndexSet; use indexmap::IndexSet;
use lsp_types::CompletionList; use lsp_types::CompletionList;
use node_resolver::NodeModuleKind;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use tower_lsp::lsp_types as lsp; use tower_lsp::lsp_types as lsp;
@ -159,15 +161,17 @@ pub async fn get_import_completions(
jsr_search_api: &CliJsrSearchApi, jsr_search_api: &CliJsrSearchApi,
npm_search_api: &CliNpmSearchApi, npm_search_api: &CliNpmSearchApi,
documents: &Documents, documents: &Documents,
is_cjs_resolver: &LspIsCjsResolver,
resolver: &LspResolver, resolver: &LspResolver,
maybe_import_map: Option<&ImportMap>, maybe_import_map: Option<&ImportMap>,
) -> Option<lsp::CompletionResponse> { ) -> Option<lsp::CompletionResponse> {
let document = documents.get(specifier)?; let document = documents.get(specifier)?;
let specifier_kind = is_cjs_resolver.get_doc_module_kind(&document);
let file_referrer = document.file_referrer(); let file_referrer = document.file_referrer();
let (text, _, range) = document.get_maybe_dependency(position)?; let (text, _, range) = document.get_maybe_dependency(position)?;
let range = to_narrow_lsp_range(document.text_info(), &range); let range = to_narrow_lsp_range(document.text_info(), &range);
let resolved = resolver let resolved = resolver
.as_graph_resolver(file_referrer) .as_cli_resolver(file_referrer)
.resolve( .resolve(
&text, &text,
&Range { &Range {
@ -175,6 +179,7 @@ pub async fn get_import_completions(
start: deno_graph::Position::zeroed(), start: deno_graph::Position::zeroed(),
end: deno_graph::Position::zeroed(), end: deno_graph::Position::zeroed(),
}, },
specifier_kind,
ResolutionMode::Execution, ResolutionMode::Execution,
) )
.ok(); .ok();
@ -200,15 +205,11 @@ pub async fn get_import_completions(
{ {
// completions for import map specifiers // completions for import map specifiers
Some(lsp::CompletionResponse::List(completion_list)) Some(lsp::CompletionResponse::List(completion_list))
} else if text.starts_with("./") } else if let Some(completion_list) =
|| text.starts_with("../") get_local_completions(specifier, specifier_kind, &text, &range, resolver)
|| text.starts_with('/')
{ {
// completions for local relative modules // completions for local relative modules
Some(lsp::CompletionResponse::List(CompletionList { Some(lsp::CompletionResponse::List(completion_list))
is_incomplete: false,
items: get_local_completions(specifier, &text, &range, resolver)?,
}))
} else if !text.is_empty() { } else if !text.is_empty() {
// completion of modules from a module registry or cache // completion of modules from a module registry or cache
check_auto_config_registry( check_auto_config_registry(
@ -359,43 +360,42 @@ fn get_import_map_completions(
/// Return local completions that are relative to the base specifier. /// Return local completions that are relative to the base specifier.
fn get_local_completions( fn get_local_completions(
base: &ModuleSpecifier, referrer: &ModuleSpecifier,
referrer_kind: NodeModuleKind,
text: &str, text: &str,
range: &lsp::Range, range: &lsp::Range,
resolver: &LspResolver, resolver: &LspResolver,
) -> Option<Vec<lsp::CompletionItem>> { ) -> Option<CompletionList> {
if base.scheme() != "file" { if referrer.scheme() != "file" {
return None; 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 let resolved_parent = resolver
.as_graph_resolver(Some(base)) .as_cli_resolver(Some(referrer))
.resolve( .resolve(
parent.as_str(), parent,
&Range { &Range {
specifier: base.clone(), specifier: referrer.clone(),
start: deno_graph::Position::zeroed(), start: deno_graph::Position::zeroed(),
end: deno_graph::Position::zeroed(), end: deno_graph::Position::zeroed(),
}, },
referrer_kind,
ResolutionMode::Execution, ResolutionMode::Execution,
) )
.ok()?; .ok()?;
let resolved_parent_path = specifier_to_file_path(&resolved_parent).ok()?; let resolved_parent_path = url_to_file_path(&resolved_parent).ok()?;
let raw_parent =
&text[..text.char_indices().rfind(|(_, c)| *c == '/')?.0 + 1];
if resolved_parent_path.is_dir() { if resolved_parent_path.is_dir() {
let cwd = std::env::current_dir().ok()?; let cwd = std::env::current_dir().ok()?;
let items = std::fs::read_dir(resolved_parent_path).ok()?; let entries = std::fs::read_dir(resolved_parent_path).ok()?;
Some( let items = entries
items
.filter_map(|de| { .filter_map(|de| {
let de = de.ok()?; let de = de.ok()?;
let label = de.path().file_name()?.to_string_lossy().to_string(); let label = de.path().file_name()?.to_string_lossy().to_string();
let entry_specifier = resolve_path(de.path().to_str()?, &cwd).ok()?; let entry_specifier = resolve_path(de.path().to_str()?, &cwd).ok()?;
if entry_specifier == *base { if entry_specifier == *referrer {
return None; return None;
} }
let full_text = format!("{raw_parent}{label}"); let full_text = format!("{parent}{label}");
let text_edit = Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { let text_edit = Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
range: *range, range: *range,
new_text: full_text.clone(), new_text: full_text.clone(),
@ -435,8 +435,11 @@ fn get_local_completions(
_ => None, _ => None,
} }
}) })
.collect(), .collect();
) Some(CompletionList {
is_incomplete: false,
items,
})
} else { } else {
None None
} }
@ -909,6 +912,7 @@ mod tests {
ModuleSpecifier::from_file_path(file_c).expect("could not create"); ModuleSpecifier::from_file_path(file_c).expect("could not create");
let actual = get_local_completions( let actual = get_local_completions(
&specifier, &specifier,
NodeModuleKind::Esm,
"./", "./",
&lsp::Range { &lsp::Range {
start: lsp::Position { start: lsp::Position {
@ -921,11 +925,11 @@ mod tests {
}, },
}, },
&Default::default(), &Default::default(),
); )
assert!(actual.is_some()); .unwrap();
let actual = actual.unwrap(); assert!(!actual.is_incomplete);
assert_eq!(actual.len(), 3); assert_eq!(actual.items.len(), 3);
for item in actual { for item in actual.items {
match item.text_edit { match item.text_edit {
Some(lsp::CompletionTextEdit::Edit(text_edit)) => { Some(lsp::CompletionTextEdit::Edit(text_edit)) => {
assert!(["./b", "./f.mjs", "./g.json"] assert!(["./b", "./f.mjs", "./g.json"]

View file

@ -4,6 +4,7 @@ use deno_ast::MediaType;
use deno_config::deno_json::DenoJsonCache; use deno_config::deno_json::DenoJsonCache;
use deno_config::deno_json::FmtConfig; use deno_config::deno_json::FmtConfig;
use deno_config::deno_json::FmtOptionsConfig; use deno_config::deno_json::FmtOptionsConfig;
use deno_config::deno_json::JsxImportSourceConfig;
use deno_config::deno_json::LintConfig; use deno_config::deno_json::LintConfig;
use deno_config::deno_json::NodeModulesDirMode; use deno_config::deno_json::NodeModulesDirMode;
use deno_config::deno_json::TestConfig; use deno_config::deno_json::TestConfig;
@ -36,11 +37,12 @@ use deno_core::ModuleSpecifier;
use deno_lint::linter::LintConfig as DenoLintConfig; use deno_lint::linter::LintConfig as DenoLintConfig;
use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::npm_rc::ResolvedNpmRc;
use deno_package_json::PackageJsonCache; use deno_package_json::PackageJsonCache;
use deno_path_util::url_to_file_path;
use deno_runtime::deno_node::PackageJson; use deno_runtime::deno_node::PackageJson;
use deno_runtime::fs_util::specifier_to_file_path;
use indexmap::IndexSet; use indexmap::IndexSet;
use lsp_types::ClientCapabilities; use lsp_types::ClientCapabilities;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Deref; use std::ops::Deref;
use std::ops::DerefMut; use std::ops::DerefMut;
@ -50,16 +52,20 @@ use std::sync::Arc;
use tower_lsp::lsp_types as lsp; use tower_lsp::lsp_types as lsp;
use super::logging::lsp_log; 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::discover_npmrc_from_workspace;
use crate::args::has_flag_env_var; use crate::args::has_flag_env_var;
use crate::args::CliLockfile; use crate::args::CliLockfile;
use crate::args::CliLockfileReadFromPathOptions;
use crate::args::ConfigFile; use crate::args::ConfigFile;
use crate::args::LintFlags; use crate::args::LintFlags;
use crate::args::LintOptions; use crate::args::LintOptions;
use crate::cache::FastInsecureHasher; use crate::cache::FastInsecureHasher;
use crate::file_fetcher::FileFetcher; use crate::file_fetcher::FileFetcher;
use crate::lsp::logging::lsp_warn; 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::CliLinter;
use crate::tools::lint::CliLinterOptions; use crate::tools::lint::CliLinterOptions;
use crate::tools::lint::LintRuleProvider; use crate::tools::lint::LintRuleProvider;
@ -435,6 +441,8 @@ pub struct LanguagePreferences {
pub use_aliases_for_renames: bool, pub use_aliases_for_renames: bool,
#[serde(default)] #[serde(default)]
pub quote_style: QuoteStyle, pub quote_style: QuoteStyle,
#[serde(default)]
pub prefer_type_only_auto_imports: bool,
} }
impl Default for LanguagePreferences { impl Default for LanguagePreferences {
@ -445,6 +453,7 @@ impl Default for LanguagePreferences {
auto_import_file_exclude_patterns: vec![], auto_import_file_exclude_patterns: vec![],
use_aliases_for_renames: true, use_aliases_for_renames: true,
quote_style: Default::default(), quote_style: Default::default(),
prefer_type_only_auto_imports: false,
} }
} }
} }
@ -801,7 +810,7 @@ impl Settings {
/// Returns `None` if the value should be deferred to the presence of a /// Returns `None` if the value should be deferred to the presence of a
/// `deno.json` file. /// `deno.json` file.
pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> Option<bool> { 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. // Non-file URLs are not disabled by these settings.
return Some(true); return Some(true);
}; };
@ -810,7 +819,7 @@ impl Settings {
let mut disable_paths = vec![]; let mut disable_paths = vec![];
let mut enable_paths = None; let mut enable_paths = None;
if let Some(folder_uri) = folder_uri { 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 = settings
.disable_paths .disable_paths
.iter() .iter()
@ -847,12 +856,12 @@ impl Settings {
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
) -> (&WorkspaceSettings, Option<&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()); return (&self.unscoped, self.first_folder.as_ref());
}; };
for (folder_uri, settings) in self.by_workspace_folder.iter().rev() { for (folder_uri, settings) in self.by_workspace_folder.iter().rev() {
if let Some(settings) = settings { 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; continue;
}; };
if path.starts_with(folder_path) { if path.starts_with(folder_path) {
@ -977,7 +986,7 @@ impl Config {
| MediaType::Tsx => Some(&workspace_settings.typescript), | MediaType::Tsx => Some(&workspace_settings.typescript),
MediaType::Json MediaType::Json
| MediaType::Wasm | MediaType::Wasm
| MediaType::TsBuildInfo | MediaType::Css
| MediaType::SourceMap | MediaType::SourceMap
| MediaType::Unknown => None, | MediaType::Unknown => None,
} }
@ -1181,8 +1190,9 @@ pub struct ConfigData {
pub lockfile: Option<Arc<CliLockfile>>, pub lockfile: Option<Arc<CliLockfile>>,
pub npmrc: Option<Arc<ResolvedNpmRc>>, pub npmrc: Option<Arc<ResolvedNpmRc>>,
pub resolver: Arc<WorkspaceResolver>, pub resolver: Arc<WorkspaceResolver>,
pub sloppy_imports_resolver: Option<Arc<SloppyImportsResolver>>, pub sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
pub import_map_from_settings: Option<ModuleSpecifier>, pub import_map_from_settings: Option<ModuleSpecifier>,
pub unstable: BTreeSet<String>,
watched_files: HashMap<ModuleSpecifier, ConfigWatchedFileType>, watched_files: HashMap<ModuleSpecifier, ConfigWatchedFileType>,
} }
@ -1580,13 +1590,22 @@ impl ConfigData {
.join("\n") .join("\n")
); );
} }
let unstable = member_dir
.workspace
.unstable_features()
.iter()
.chain(settings.unstable.as_deref())
.cloned()
.collect::<BTreeSet<_>>();
let unstable_sloppy_imports = std::env::var("DENO_UNSTABLE_SLOPPY_IMPORTS") let unstable_sloppy_imports = std::env::var("DENO_UNSTABLE_SLOPPY_IMPORTS")
.is_ok() .is_ok()
|| member_dir.workspace.has_unstable("sloppy-imports"); || unstable.contains("sloppy-imports");
let sloppy_imports_resolver = unstable_sloppy_imports.then(|| { let sloppy_imports_resolver = unstable_sloppy_imports.then(|| {
Arc::new(SloppyImportsResolver::new_without_stat_cache(Arc::new( Arc::new(CliSloppyImportsResolver::new(
SloppyImportsCachedFs::new_without_stat_cache(Arc::new(
deno_runtime::deno_fs::RealFs, deno_runtime::deno_fs::RealFs,
))) )),
))
}); });
let resolver = Arc::new(resolver); let resolver = Arc::new(resolver);
let lint_rule_provider = LintRuleProvider::new( let lint_rule_provider = LintRuleProvider::new(
@ -1621,6 +1640,7 @@ impl ConfigData {
lockfile, lockfile,
npmrc, npmrc,
import_map_from_settings, import_map_from_settings,
unstable,
watched_files, watched_files,
} }
} }
@ -1635,6 +1655,17 @@ impl ConfigData {
self.member_dir.maybe_pkg_json() self.member_dir.maybe_pkg_json()
} }
pub fn maybe_jsx_import_source_config(
&self,
) -> Option<JsxImportSourceConfig> {
self
.member_dir
.workspace
.to_maybe_jsx_import_source_config()
.ok()
.flatten()
}
pub fn scope_contains_specifier(&self, specifier: &ModuleSpecifier) -> bool { pub fn scope_contains_specifier(&self, specifier: &ModuleSpecifier) -> bool {
specifier.as_str().starts_with(self.scope.as_str()) specifier.as_str().starts_with(self.scope.as_str())
|| self || self
@ -1712,14 +1743,14 @@ impl ConfigTree {
.unwrap_or_else(|| Arc::new(FmtConfig::new_with_base(PathBuf::from("/")))) .unwrap_or_else(|| Arc::new(FmtConfig::new_with_base(PathBuf::from("/"))))
} }
/// Returns (scope_uri, type). /// Returns (scope_url, type).
pub fn watched_file_type( pub fn watched_file_type(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
) -> Option<(&ModuleSpecifier, ConfigWatchedFileType)> { ) -> 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) { if let Some(typ) = data.watched_files.get(specifier) {
return Some((scope_uri, *typ)); return Some((scope_url, *typ));
} }
} }
None None
@ -1743,6 +1774,46 @@ impl ConfigTree {
.any(|data| data.watched_files.contains_key(specifier)) .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( pub async fn refresh(
&mut self, &mut self,
settings: &Settings, settings: &Settings,
@ -1767,7 +1838,7 @@ impl ConfigTree {
let config_file_path = (|| { let config_file_path = (|| {
let config_setting = ws_settings.config.as_ref()?; let config_setting = ws_settings.config.as_ref()?;
let config_uri = folder_uri.join(config_setting).ok()?; 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() { if config_file_path.is_some() || ws_settings.import_map.is_some() {
scopes.insert( scopes.insert(
@ -1844,7 +1915,7 @@ impl ConfigTree {
let scope = config_file.specifier.join(".").unwrap(); let scope = config_file.specifier.join(".").unwrap();
let json_text = serde_json::to_string(&config_file.json).unwrap(); let json_text = serde_json::to_string(&config_file.json).unwrap();
let test_fs = deno_runtime::deno_fs::InMemoryFs::default(); 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![( test_fs.setup_text_files(vec![(
config_path.to_string_lossy().to_string(), config_path.to_string_lossy().to_string(),
json_text, json_text,
@ -1928,7 +1999,11 @@ fn resolve_lockfile_from_path(
lockfile_path: PathBuf, lockfile_path: PathBuf,
frozen: bool, frozen: bool,
) -> Option<CliLockfile> { ) -> 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) => { Ok(value) => {
if value.filename.exists() { if value.filename.exists() {
if let Ok(specifier) = ModuleSpecifier::from_file_path(&value.filename) if let Ok(specifier) = ModuleSpecifier::from_file_path(&value.filename)
@ -2201,6 +2276,7 @@ mod tests {
auto_import_file_exclude_patterns: vec![], auto_import_file_exclude_patterns: vec![],
use_aliases_for_renames: true, use_aliases_for_renames: true,
quote_style: QuoteStyle::Auto, quote_style: QuoteStyle::Auto,
prefer_type_only_auto_imports: false,
}, },
suggest: CompletionSettings { suggest: CompletionSettings {
complete_function_calls: false, complete_function_calls: false,
@ -2246,6 +2322,7 @@ mod tests {
auto_import_file_exclude_patterns: vec![], auto_import_file_exclude_patterns: vec![],
use_aliases_for_renames: true, use_aliases_for_renames: true,
quote_style: QuoteStyle::Auto, quote_style: QuoteStyle::Auto,
prefer_type_only_auto_imports: false,
}, },
suggest: CompletionSettings { suggest: CompletionSettings {
complete_function_calls: false, complete_function_calls: false,

View file

@ -19,8 +19,8 @@ use super::urls::LspUrlMap;
use crate::graph_util; use crate::graph_util;
use crate::graph_util::enhanced_resolution_error_message; use crate::graph_util::enhanced_resolution_error_message;
use crate::lsp::lsp_custom::DiagnosticBatchNotificationParams; use crate::lsp::lsp_custom::DiagnosticBatchNotificationParams;
use crate::resolver::SloppyImportsResolution; use crate::resolver::CliSloppyImportsResolver;
use crate::resolver::SloppyImportsResolver; use crate::resolver::SloppyImportsCachedFs;
use crate::tools::lint::CliLinter; use crate::tools::lint::CliLinter;
use crate::tools::lint::CliLinterOptions; use crate::tools::lint::CliLinterOptions;
use crate::tools::lint::LintRuleProvider; use crate::tools::lint::LintRuleProvider;
@ -40,11 +40,12 @@ use deno_core::unsync::spawn_blocking;
use deno_core::unsync::JoinHandle; use deno_core::unsync::JoinHandle;
use deno_core::url::Url; use deno_core::url::Url;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_graph::source::ResolutionMode;
use deno_graph::source::ResolveError; use deno_graph::source::ResolveError;
use deno_graph::Resolution; use deno_graph::Resolution;
use deno_graph::ResolutionError; use deno_graph::ResolutionError;
use deno_graph::SpecifierError; 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_fs;
use deno_runtime::deno_node; use deno_runtime::deno_node;
use deno_runtime::tokio_util::create_basic_runtime; use deno_runtime::tokio_util::create_basic_runtime;
@ -1263,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::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::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) => { 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| { let data = maybe_sloppy_resolution.as_ref().map(|res| {
json!({ json!({
"specifier": specifier, "specifier": specifier,
@ -1496,7 +1499,11 @@ fn diagnose_dependency(
.data_for_specifier(referrer_doc.file_referrer().unwrap_or(referrer)) .data_for_specifier(referrer_doc.file_referrer().unwrap_or(referrer))
.and_then(|d| d.resolver.maybe_import_map()); .and_then(|d| d.resolver.maybe_import_map());
if let Some(import_map) = import_map { if let Some(import_map) = import_map {
if let Resolution::Ok(resolved) = &dependency.maybe_code { let resolved = dependency
.maybe_code
.ok()
.or_else(|| dependency.maybe_type.ok());
if let Some(resolved) = resolved {
if let Some(to) = import_map.lookup(&resolved.specifier, referrer) { if let Some(to) = import_map.lookup(&resolved.specifier, referrer) {
if dependency_key != to { if dependency_key != to {
diagnostics.push( diagnostics.push(
@ -1514,17 +1521,19 @@ fn diagnose_dependency(
let import_ranges: Vec<_> = dependency let import_ranges: Vec<_> = dependency
.imports .imports
.iter() .iter()
.map(|i| documents::to_lsp_range(&i.range)) .map(|i| documents::to_lsp_range(&i.specifier_range))
.collect(); .collect();
// TODO(nayeemrmn): This is a crude way of detecting `@deno-types` which has // TODO(nayeemrmn): This is a crude way of detecting `@deno-types` which has
// a different specifier and therefore needs a separate call to // a different specifier and therefore needs a separate call to
// `diagnose_resolution()`. It would be much cleaner if that were modelled as // `diagnose_resolution()`. It would be much cleaner if that were modelled as
// a separate dependency: https://github.com/denoland/deno_graph/issues/247. // a separate dependency: https://github.com/denoland/deno_graph/issues/247.
let is_types_deno_types = !dependency.maybe_type.is_none() let is_types_deno_types = !dependency.maybe_type.is_none()
&& !dependency && !dependency.imports.iter().any(|i| {
.imports dependency
.iter() .maybe_type
.any(|i| dependency.maybe_type.includes(&i.range.start).is_some()); .includes(&i.specifier_range.start)
.is_some()
});
diagnostics.extend( diagnostics.extend(
diagnose_resolution( diagnose_resolution(
@ -1698,6 +1707,7 @@ mod tests {
documents: Arc::new(documents), documents: Arc::new(documents),
assets: Default::default(), assets: Default::default(),
config: Arc::new(config), config: Arc::new(config),
is_cjs_resolver: Default::default(),
resolver, resolver,
}, },
) )

View file

@ -3,7 +3,9 @@
use super::cache::calculate_fs_version; use super::cache::calculate_fs_version;
use super::cache::LspCache; use super::cache::LspCache;
use super::config::Config; use super::config::Config;
use super::resolver::LspIsCjsResolver;
use super::resolver::LspResolver; use super::resolver::LspResolver;
use super::resolver::SingleReferrerGraphResolver;
use super::testing::TestCollector; use super::testing::TestCollector;
use super::testing::TestModule; use super::testing::TestModule;
use super::text::LineIndex; use super::text::LineIndex;
@ -26,13 +28,14 @@ use deno_core::parking_lot::Mutex;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_graph::source::ResolutionMode; use deno_graph::source::ResolutionMode;
use deno_graph::Resolution; use deno_graph::Resolution;
use deno_path_util::url_to_file_path;
use deno_runtime::deno_node; use deno_runtime::deno_node;
use deno_runtime::fs_util::specifier_to_file_path;
use deno_semver::jsr::JsrPackageReqReference; use deno_semver::jsr::JsrPackageReqReference;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use indexmap::IndexMap; use indexmap::IndexMap;
use indexmap::IndexSet; use indexmap::IndexSet;
use node_resolver::NodeModuleKind;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::BTreeSet; use std::collections::BTreeSet;
@ -272,7 +275,7 @@ fn get_maybe_test_module_fut(
parsed_source.specifier().clone(), parsed_source.specifier().clone(),
parsed_source.text_info_lazy().clone(), parsed_source.text_info_lazy().clone(),
); );
parsed_source.module().visit_with(&mut collector); parsed_source.program().visit_with(&mut collector);
Arc::new(collector.take()) Arc::new(collector.take())
}) })
.map(Result::ok) .map(Result::ok)
@ -293,6 +296,8 @@ pub struct Document {
/// Contains the last-known-good set of dependencies from parsing the module. /// Contains the last-known-good set of dependencies from parsing the module.
config: Arc<Config>, config: Arc<Config>,
dependencies: Arc<IndexMap<String, deno_graph::Dependency>>, dependencies: Arc<IndexMap<String, deno_graph::Dependency>>,
/// If this is maybe a CJS script and maybe not an ES module.
is_script: Option<bool>,
// TODO(nayeemrmn): This is unused, use it for scope attribution for remote // TODO(nayeemrmn): This is unused, use it for scope attribution for remote
// modules. // modules.
file_referrer: Option<ModuleSpecifier>, file_referrer: Option<ModuleSpecifier>,
@ -323,6 +328,7 @@ impl Document {
maybe_lsp_version: Option<i32>, maybe_lsp_version: Option<i32>,
maybe_language_id: Option<LanguageId>, maybe_language_id: Option<LanguageId>,
maybe_headers: Option<HashMap<String, String>>, maybe_headers: Option<HashMap<String, String>>,
is_cjs_resolver: &LspIsCjsResolver,
resolver: Arc<LspResolver>, resolver: Arc<LspResolver>,
config: Arc<Config>, config: Arc<Config>,
cache: &Arc<LspCache>, cache: &Arc<LspCache>,
@ -332,12 +338,8 @@ impl Document {
.filter(|s| cache.is_valid_file_referrer(s)) .filter(|s| cache.is_valid_file_referrer(s))
.cloned() .cloned()
.or(file_referrer); .or(file_referrer);
let media_type = resolve_media_type( let media_type =
&specifier, resolve_media_type(&specifier, maybe_headers.as_ref(), maybe_language_id);
maybe_headers.as_ref(),
maybe_language_id,
&resolver,
);
let (maybe_parsed_source, maybe_module) = let (maybe_parsed_source, maybe_module) =
if media_type_is_diagnosable(media_type) { if media_type_is_diagnosable(media_type) {
parse_and_analyze_module( parse_and_analyze_module(
@ -346,6 +348,7 @@ impl Document {
maybe_headers.as_ref(), maybe_headers.as_ref(),
media_type, media_type,
file_referrer.as_ref(), file_referrer.as_ref(),
is_cjs_resolver,
&resolver, &resolver,
) )
} else { } else {
@ -371,6 +374,7 @@ impl Document {
file_referrer.as_ref(), file_referrer.as_ref(),
), ),
file_referrer, file_referrer,
is_script: maybe_module.as_ref().map(|m| m.is_script),
maybe_types_dependency, maybe_types_dependency,
line_index, line_index,
maybe_language_id, maybe_language_id,
@ -392,6 +396,7 @@ impl Document {
fn with_new_config( fn with_new_config(
&self, &self,
is_cjs_resolver: &LspIsCjsResolver,
resolver: Arc<LspResolver>, resolver: Arc<LspResolver>,
config: Arc<Config>, config: Arc<Config>,
) -> Arc<Self> { ) -> Arc<Self> {
@ -399,11 +404,11 @@ impl Document {
&self.specifier, &self.specifier,
self.maybe_headers.as_ref(), self.maybe_headers.as_ref(),
self.maybe_language_id, self.maybe_language_id,
&resolver,
); );
let dependencies; let dependencies;
let maybe_types_dependency; let maybe_types_dependency;
let maybe_parsed_source; let maybe_parsed_source;
let is_script;
let maybe_test_module_fut; let maybe_test_module_fut;
if media_type != self.media_type { if media_type != self.media_type {
let parsed_source_result = let parsed_source_result =
@ -413,6 +418,7 @@ impl Document {
&parsed_source_result, &parsed_source_result,
self.maybe_headers.as_ref(), self.maybe_headers.as_ref(),
self.file_referrer.as_ref(), self.file_referrer.as_ref(),
is_cjs_resolver,
&resolver, &resolver,
) )
.ok(); .ok();
@ -420,6 +426,7 @@ impl Document {
.as_ref() .as_ref()
.map(|m| Arc::new(m.dependencies.clone())) .map(|m| Arc::new(m.dependencies.clone()))
.unwrap_or_default(); .unwrap_or_default();
is_script = maybe_module.as_ref().map(|m| m.is_script);
maybe_types_dependency = maybe_module maybe_types_dependency = maybe_module
.as_ref() .as_ref()
.and_then(|m| Some(Arc::new(m.maybe_types_dependency.clone()?))); .and_then(|m| Some(Arc::new(m.maybe_types_dependency.clone()?)));
@ -427,10 +434,19 @@ impl Document {
maybe_test_module_fut = maybe_test_module_fut =
get_maybe_test_module_fut(maybe_parsed_source.as_ref(), &config); get_maybe_test_module_fut(maybe_parsed_source.as_ref(), &config);
} else { } else {
let graph_resolver = let cli_resolver = resolver.as_cli_resolver(self.file_referrer.as_ref());
resolver.as_graph_resolver(self.file_referrer.as_ref());
let npm_resolver = let npm_resolver =
resolver.create_graph_npm_resolver(self.file_referrer.as_ref()); resolver.create_graph_npm_resolver(self.file_referrer.as_ref());
let config_data = resolver.as_config_data(self.file_referrer.as_ref());
let jsx_import_source_config =
config_data.and_then(|d| d.maybe_jsx_import_source_config());
let resolver = SingleReferrerGraphResolver {
valid_referrer: &self.specifier,
referrer_kind: is_cjs_resolver
.get_lsp_referrer_kind(&self.specifier, self.is_script),
cli_resolver,
jsx_import_source_config: jsx_import_source_config.as_ref(),
};
dependencies = Arc::new( dependencies = Arc::new(
self self
.dependencies .dependencies
@ -441,7 +457,7 @@ impl Document {
d.with_new_resolver( d.with_new_resolver(
s, s,
&CliJsrUrlProvider, &CliJsrUrlProvider,
Some(graph_resolver), Some(&resolver),
Some(&npm_resolver), Some(&npm_resolver),
), ),
) )
@ -451,10 +467,11 @@ impl Document {
maybe_types_dependency = self.maybe_types_dependency.as_ref().map(|d| { maybe_types_dependency = self.maybe_types_dependency.as_ref().map(|d| {
Arc::new(d.with_new_resolver( Arc::new(d.with_new_resolver(
&CliJsrUrlProvider, &CliJsrUrlProvider,
Some(graph_resolver), Some(&resolver),
Some(&npm_resolver), Some(&npm_resolver),
)) ))
}); });
is_script = self.is_script;
maybe_parsed_source = self.maybe_parsed_source().cloned(); maybe_parsed_source = self.maybe_parsed_source().cloned();
maybe_test_module_fut = self maybe_test_module_fut = self
.maybe_test_module_fut .maybe_test_module_fut
@ -466,6 +483,7 @@ impl Document {
// updated properties // updated properties
dependencies, dependencies,
file_referrer: self.file_referrer.clone(), file_referrer: self.file_referrer.clone(),
is_script,
maybe_types_dependency, maybe_types_dependency,
maybe_navigation_tree: Mutex::new(None), maybe_navigation_tree: Mutex::new(None),
// maintain - this should all be copies/clones // maintain - this should all be copies/clones
@ -490,6 +508,7 @@ impl Document {
fn with_change( fn with_change(
&self, &self,
is_cjs_resolver: &LspIsCjsResolver,
version: i32, version: i32,
changes: Vec<lsp::TextDocumentContentChangeEvent>, changes: Vec<lsp::TextDocumentContentChangeEvent>,
) -> Result<Arc<Self>, AnyError> { ) -> Result<Arc<Self>, AnyError> {
@ -523,6 +542,7 @@ impl Document {
self.maybe_headers.as_ref(), self.maybe_headers.as_ref(),
media_type, media_type,
self.file_referrer.as_ref(), self.file_referrer.as_ref(),
is_cjs_resolver,
self.resolver.as_ref(), self.resolver.as_ref(),
) )
} else { } else {
@ -546,6 +566,7 @@ impl Document {
get_maybe_test_module_fut(maybe_parsed_source.as_ref(), &self.config); get_maybe_test_module_fut(maybe_parsed_source.as_ref(), &self.config);
Ok(Arc::new(Self { Ok(Arc::new(Self {
config: self.config.clone(), config: self.config.clone(),
is_script: maybe_module.as_ref().map(|m| m.is_script),
specifier: self.specifier.clone(), specifier: self.specifier.clone(),
file_referrer: self.file_referrer.clone(), file_referrer: self.file_referrer.clone(),
maybe_fs_version: self.maybe_fs_version.clone(), maybe_fs_version: self.maybe_fs_version.clone(),
@ -580,6 +601,7 @@ impl Document {
), ),
maybe_language_id: self.maybe_language_id, maybe_language_id: self.maybe_language_id,
dependencies: self.dependencies.clone(), dependencies: self.dependencies.clone(),
is_script: self.is_script,
maybe_types_dependency: self.maybe_types_dependency.clone(), maybe_types_dependency: self.maybe_types_dependency.clone(),
text: self.text.clone(), text: self.text.clone(),
text_info_cell: once_cell::sync::OnceCell::new(), text_info_cell: once_cell::sync::OnceCell::new(),
@ -607,6 +629,7 @@ impl Document {
), ),
maybe_language_id: self.maybe_language_id, maybe_language_id: self.maybe_language_id,
dependencies: self.dependencies.clone(), dependencies: self.dependencies.clone(),
is_script: self.is_script,
maybe_types_dependency: self.maybe_types_dependency.clone(), maybe_types_dependency: self.maybe_types_dependency.clone(),
text: self.text.clone(), text: self.text.clone(),
text_info_cell: once_cell::sync::OnceCell::new(), text_info_cell: once_cell::sync::OnceCell::new(),
@ -655,6 +678,13 @@ impl Document {
}) })
} }
/// If this is maybe a CJS script and maybe not an ES module.
///
/// Use `LspIsCjsResolver` to determine for sure.
pub fn is_script(&self) -> Option<bool> {
self.is_script
}
pub fn line_index(&self) -> Arc<LineIndex> { pub fn line_index(&self) -> Arc<LineIndex> {
self.line_index.clone() self.line_index.clone()
} }
@ -764,14 +794,7 @@ fn resolve_media_type(
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
maybe_headers: Option<&HashMap<String, String>>, maybe_headers: Option<&HashMap<String, String>>,
maybe_language_id: Option<LanguageId>, maybe_language_id: Option<LanguageId>,
resolver: &LspResolver,
) -> MediaType { ) -> MediaType {
if resolver.in_node_modules(specifier) {
if let Some(media_type) = resolver.node_media_type(specifier) {
return media_type;
}
}
if let Some(language_id) = maybe_language_id { if let Some(language_id) = maybe_language_id {
return MediaType::from_specifier_and_content_type( return MediaType::from_specifier_and_content_type(
specifier, specifier,
@ -809,6 +832,7 @@ impl FileSystemDocuments {
pub fn get( pub fn get(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
is_cjs_resolver: &LspIsCjsResolver,
resolver: &Arc<LspResolver>, resolver: &Arc<LspResolver>,
config: &Arc<Config>, config: &Arc<Config>,
cache: &Arc<LspCache>, cache: &Arc<LspCache>,
@ -832,7 +856,14 @@ impl FileSystemDocuments {
}; };
if dirty { if dirty {
// attempt to update the file on the file system // attempt to update the file on the file system
self.refresh_document(specifier, resolver, config, cache, file_referrer) self.refresh_document(
specifier,
is_cjs_resolver,
resolver,
config,
cache,
file_referrer,
)
} else { } else {
old_doc old_doc
} }
@ -843,13 +874,14 @@ impl FileSystemDocuments {
fn refresh_document( fn refresh_document(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
is_cjs_resolver: &LspIsCjsResolver,
resolver: &Arc<LspResolver>, resolver: &Arc<LspResolver>,
config: &Arc<Config>, config: &Arc<Config>,
cache: &Arc<LspCache>, cache: &Arc<LspCache>,
file_referrer: Option<&ModuleSpecifier>, file_referrer: Option<&ModuleSpecifier>,
) -> Option<Arc<Document>> { ) -> Option<Arc<Document>> {
let doc = if specifier.scheme() == "file" { 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 bytes = fs::read(path).ok()?;
let content = let content =
deno_graph::source::decode_owned_source(specifier, bytes, None).ok()?; deno_graph::source::decode_owned_source(specifier, bytes, None).ok()?;
@ -859,6 +891,7 @@ impl FileSystemDocuments {
None, None,
None, None,
None, None,
is_cjs_resolver,
resolver.clone(), resolver.clone(),
config.clone(), config.clone(),
cache, cache,
@ -875,6 +908,7 @@ impl FileSystemDocuments {
None, None,
None, None,
None, None,
is_cjs_resolver,
resolver.clone(), resolver.clone(),
config.clone(), config.clone(),
cache, cache,
@ -902,6 +936,7 @@ impl FileSystemDocuments {
None, None,
None, None,
maybe_headers, maybe_headers,
is_cjs_resolver,
resolver.clone(), resolver.clone(),
config.clone(), config.clone(),
cache, cache,
@ -942,6 +977,11 @@ pub struct Documents {
/// The DENO_DIR that the documents looks for non-file based modules. /// The DENO_DIR that the documents looks for non-file based modules.
cache: Arc<LspCache>, cache: Arc<LspCache>,
config: Arc<Config>, config: Arc<Config>,
/// Resolver for detecting if a document is CJS or ESM.
is_cjs_resolver: Arc<LspIsCjsResolver>,
/// A resolver that takes into account currently loaded import map and JSX
/// settings.
resolver: Arc<LspResolver>,
/// A flag that indicates that stated data is potentially invalid and needs to /// A flag that indicates that stated data is potentially invalid and needs to
/// be recalculated before being considered valid. /// be recalculated before being considered valid.
dirty: bool, dirty: bool,
@ -949,9 +989,6 @@ pub struct Documents {
open_docs: HashMap<ModuleSpecifier, Arc<Document>>, open_docs: HashMap<ModuleSpecifier, Arc<Document>>,
/// Documents stored on the file system. /// Documents stored on the file system.
file_system_docs: Arc<FileSystemDocuments>, file_system_docs: Arc<FileSystemDocuments>,
/// A resolver that takes into account currently loaded import map and JSX
/// settings.
resolver: Arc<LspResolver>,
/// The npm package requirements found in npm specifiers. /// The npm package requirements found in npm specifiers.
npm_reqs_by_scope: npm_reqs_by_scope:
Arc<BTreeMap<Option<ModuleSpecifier>, BTreeSet<PackageReq>>>, Arc<BTreeMap<Option<ModuleSpecifier>, BTreeSet<PackageReq>>>,
@ -982,6 +1019,7 @@ impl Documents {
// the cache for remote modules here in order to get the // the cache for remote modules here in order to get the
// x-typescript-types? // x-typescript-types?
None, None,
&self.is_cjs_resolver,
self.resolver.clone(), self.resolver.clone(),
self.config.clone(), self.config.clone(),
&self.cache, &self.cache,
@ -1016,7 +1054,7 @@ impl Documents {
)) ))
})?; })?;
self.dirty = true; self.dirty = true;
let doc = doc.with_change(version, changes)?; let doc = doc.with_change(&self.is_cjs_resolver, version, changes)?;
self.open_docs.insert(doc.specifier().clone(), doc.clone()); self.open_docs.insert(doc.specifier().clone(), doc.clone());
Ok(doc) Ok(doc)
} }
@ -1071,34 +1109,6 @@ impl Documents {
self.cache.is_valid_file_referrer(specifier) self.cache.is_valid_file_referrer(specifier)
} }
/// Return `true` if the provided specifier can be resolved to a document,
/// otherwise `false`.
pub fn contains_import(
&self,
specifier: &str,
referrer: &ModuleSpecifier,
) -> bool {
let file_referrer = self.get_file_referrer(referrer);
let maybe_specifier = self
.resolver
.as_graph_resolver(file_referrer.as_deref())
.resolve(
specifier,
&deno_graph::Range {
specifier: referrer.clone(),
start: deno_graph::Position::zeroed(),
end: deno_graph::Position::zeroed(),
},
ResolutionMode::Types,
)
.ok();
if let Some(import_specifier) = maybe_specifier {
self.exists(&import_specifier, file_referrer.as_deref())
} else {
false
}
}
pub fn resolve_document_specifier( pub fn resolve_document_specifier(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
@ -1136,7 +1146,7 @@ impl Documents {
return true; return true;
} }
if specifier.scheme() == "file" { if specifier.scheme() == "file" {
return specifier_to_file_path(&specifier) return url_to_file_path(&specifier)
.map(|p| p.is_file()) .map(|p| p.is_file())
.unwrap_or(false); .unwrap_or(false);
} }
@ -1173,6 +1183,7 @@ impl Documents {
if let Some(old_doc) = old_doc { if let Some(old_doc) = old_doc {
self.file_system_docs.get( self.file_system_docs.get(
specifier, specifier,
&self.is_cjs_resolver,
&self.resolver, &self.resolver,
&self.config, &self.config,
&self.cache, &self.cache,
@ -1197,6 +1208,7 @@ impl Documents {
} else { } else {
self.file_system_docs.get( self.file_system_docs.get(
&specifier, &specifier,
&self.is_cjs_resolver,
&self.resolver, &self.resolver,
&self.config, &self.config,
&self.cache, &self.cache,
@ -1255,12 +1267,15 @@ impl Documents {
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
file_referrer: Option<&ModuleSpecifier>, file_referrer: Option<&ModuleSpecifier>,
) -> Vec<Option<(ModuleSpecifier, MediaType)>> { ) -> Vec<Option<(ModuleSpecifier, MediaType)>> {
let document = self.get(referrer); let referrer_doc = self.get(referrer);
let file_referrer = document let file_referrer = referrer_doc
.as_ref() .as_ref()
.and_then(|d| d.file_referrer()) .and_then(|d| d.file_referrer())
.or(file_referrer); .or(file_referrer);
let dependencies = document.as_ref().map(|d| d.dependencies()); let dependencies = referrer_doc.as_ref().map(|d| d.dependencies());
let referrer_kind = self
.is_cjs_resolver
.get_maybe_doc_module_kind(referrer, referrer_doc.as_deref());
let mut results = Vec::new(); let mut results = Vec::new();
for raw_specifier in raw_specifiers { for raw_specifier in raw_specifiers {
if raw_specifier.starts_with("asset:") { if raw_specifier.starts_with("asset:") {
@ -1277,31 +1292,35 @@ impl Documents {
results.push(self.resolve_dependency( results.push(self.resolve_dependency(
specifier, specifier,
referrer, referrer,
referrer_kind,
file_referrer, file_referrer,
)); ));
} else if let Some(specifier) = dep.maybe_code.maybe_specifier() { } else if let Some(specifier) = dep.maybe_code.maybe_specifier() {
results.push(self.resolve_dependency( results.push(self.resolve_dependency(
specifier, specifier,
referrer, referrer,
referrer_kind,
file_referrer, file_referrer,
)); ));
} else { } else {
results.push(None); results.push(None);
} }
} else if let Ok(specifier) = } else if let Ok(specifier) =
self.resolver.as_graph_resolver(file_referrer).resolve( self.resolver.as_cli_resolver(file_referrer).resolve(
raw_specifier, raw_specifier,
&deno_graph::Range { &deno_graph::Range {
specifier: referrer.clone(), specifier: referrer.clone(),
start: deno_graph::Position::zeroed(), start: deno_graph::Position::zeroed(),
end: deno_graph::Position::zeroed(), end: deno_graph::Position::zeroed(),
}, },
referrer_kind,
ResolutionMode::Types, ResolutionMode::Types,
) )
{ {
results.push(self.resolve_dependency( results.push(self.resolve_dependency(
&specifier, &specifier,
referrer, referrer,
referrer_kind,
file_referrer, file_referrer,
)); ));
} else { } else {
@ -1320,12 +1339,16 @@ impl Documents {
) { ) {
self.config = Arc::new(config.clone()); self.config = Arc::new(config.clone());
self.cache = Arc::new(cache.clone()); self.cache = Arc::new(cache.clone());
self.is_cjs_resolver = Arc::new(LspIsCjsResolver::new(cache));
self.resolver = resolver.clone(); self.resolver = resolver.clone();
node_resolver::PackageJsonThreadLocalCache::clear();
{ {
let fs_docs = &self.file_system_docs; let fs_docs = &self.file_system_docs;
// Clean up non-existent documents. // Clean up non-existent documents.
fs_docs.docs.retain(|specifier, _| { 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 // Remove non-file schemed docs (deps). They may not be dependencies
// anymore after updating resolvers. // anymore after updating resolvers.
return false; return false;
@ -1340,14 +1363,21 @@ impl Documents {
if !config.specifier_enabled(doc.specifier()) { if !config.specifier_enabled(doc.specifier()) {
continue; continue;
} }
*doc = doc.with_new_config(self.resolver.clone(), self.config.clone()); *doc = doc.with_new_config(
&self.is_cjs_resolver,
self.resolver.clone(),
self.config.clone(),
);
} }
for mut doc in self.file_system_docs.docs.iter_mut() { for mut doc in self.file_system_docs.docs.iter_mut() {
if !config.specifier_enabled(doc.specifier()) { if !config.specifier_enabled(doc.specifier()) {
continue; continue;
} }
*doc.value_mut() = *doc.value_mut() = doc.with_new_config(
doc.with_new_config(self.resolver.clone(), self.config.clone()); &self.is_cjs_resolver,
self.resolver.clone(),
self.config.clone(),
);
} }
self.open_docs = open_docs; self.open_docs = open_docs;
let mut preload_count = 0; let mut preload_count = 0;
@ -1364,6 +1394,7 @@ impl Documents {
{ {
fs_docs.refresh_document( fs_docs.refresh_document(
specifier, specifier,
&self.is_cjs_resolver,
&self.resolver, &self.resolver,
&self.config, &self.config,
&self.cache, &self.cache,
@ -1449,6 +1480,7 @@ impl Documents {
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
referrer_kind: NodeModuleKind,
file_referrer: Option<&ModuleSpecifier>, file_referrer: Option<&ModuleSpecifier>,
) -> Option<(ModuleSpecifier, MediaType)> { ) -> Option<(ModuleSpecifier, MediaType)> {
if let Some(module_name) = specifier.as_str().strip_prefix("node:") { if let Some(module_name) = specifier.as_str().strip_prefix("node:") {
@ -1462,10 +1494,12 @@ impl Documents {
let mut specifier = specifier.clone(); let mut specifier = specifier.clone();
let mut media_type = None; let mut media_type = None;
if let Ok(npm_ref) = NpmPackageReqReference::from_specifier(&specifier) { if let Ok(npm_ref) = NpmPackageReqReference::from_specifier(&specifier) {
let (s, mt) = let (s, mt) = self.resolver.npm_to_file_url(
self &npm_ref,
.resolver referrer,
.npm_to_file_url(&npm_ref, referrer, file_referrer)?; referrer_kind,
file_referrer,
)?;
specifier = s; specifier = s;
media_type = Some(mt); media_type = Some(mt);
} }
@ -1475,7 +1509,8 @@ impl Documents {
return Some((specifier, media_type)); return Some((specifier, media_type));
}; };
if let Some(types) = doc.maybe_types_dependency().maybe_specifier() { if let Some(types) = doc.maybe_types_dependency().maybe_specifier() {
self.resolve_dependency(types, &specifier, file_referrer) let specifier_kind = self.is_cjs_resolver.get_doc_module_kind(&doc);
self.resolve_dependency(types, &specifier, specifier_kind, file_referrer)
} else { } else {
Some((doc.specifier().clone(), doc.media_type())) Some((doc.specifier().clone(), doc.media_type()))
} }
@ -1543,6 +1578,7 @@ fn parse_and_analyze_module(
maybe_headers: Option<&HashMap<String, String>>, maybe_headers: Option<&HashMap<String, String>>,
media_type: MediaType, media_type: MediaType,
file_referrer: Option<&ModuleSpecifier>, file_referrer: Option<&ModuleSpecifier>,
is_cjs_resolver: &LspIsCjsResolver,
resolver: &LspResolver, resolver: &LspResolver,
) -> (Option<ParsedSourceResult>, Option<ModuleResult>) { ) -> (Option<ParsedSourceResult>, Option<ModuleResult>) {
let parsed_source_result = parse_source(specifier.clone(), text, media_type); let parsed_source_result = parse_source(specifier.clone(), text, media_type);
@ -1551,6 +1587,7 @@ fn parse_and_analyze_module(
&parsed_source_result, &parsed_source_result,
maybe_headers, maybe_headers,
file_referrer, file_referrer,
is_cjs_resolver,
resolver, resolver,
); );
(Some(parsed_source_result), Some(module_result)) (Some(parsed_source_result), Some(module_result))
@ -1561,7 +1598,7 @@ fn parse_source(
text: Arc<str>, text: Arc<str>,
media_type: MediaType, media_type: MediaType,
) -> ParsedSourceResult { ) -> ParsedSourceResult {
deno_ast::parse_module(deno_ast::ParseParams { deno_ast::parse_program(deno_ast::ParseParams {
specifier, specifier,
text, text,
media_type, media_type,
@ -1576,11 +1613,26 @@ fn analyze_module(
parsed_source_result: &ParsedSourceResult, parsed_source_result: &ParsedSourceResult,
maybe_headers: Option<&HashMap<String, String>>, maybe_headers: Option<&HashMap<String, String>>,
file_referrer: Option<&ModuleSpecifier>, file_referrer: Option<&ModuleSpecifier>,
is_cjs_resolver: &LspIsCjsResolver,
resolver: &LspResolver, resolver: &LspResolver,
) -> ModuleResult { ) -> ModuleResult {
match parsed_source_result { match parsed_source_result {
Ok(parsed_source) => { Ok(parsed_source) => {
let npm_resolver = resolver.create_graph_npm_resolver(file_referrer); let npm_resolver = resolver.create_graph_npm_resolver(file_referrer);
let cli_resolver = resolver.as_cli_resolver(file_referrer);
let config_data = resolver.as_config_data(file_referrer);
let valid_referrer = specifier.clone();
let jsx_import_source_config =
config_data.and_then(|d| d.maybe_jsx_import_source_config());
let resolver = SingleReferrerGraphResolver {
valid_referrer: &valid_referrer,
referrer_kind: is_cjs_resolver.get_lsp_referrer_kind(
&specifier,
Some(parsed_source.compute_is_script()),
),
cli_resolver,
jsx_import_source_config: jsx_import_source_config.as_ref(),
};
Ok(deno_graph::parse_module_from_ast( Ok(deno_graph::parse_module_from_ast(
deno_graph::ParseModuleFromAstOptions { deno_graph::ParseModuleFromAstOptions {
graph_kind: deno_graph::GraphKind::TypesOnly, graph_kind: deno_graph::GraphKind::TypesOnly,
@ -1591,7 +1643,7 @@ fn analyze_module(
// dynamic imports like import(`./dir/${something}`) in the LSP // dynamic imports like import(`./dir/${something}`) in the LSP
file_system: &deno_graph::source::NullFileSystem, file_system: &deno_graph::source::NullFileSystem,
jsr_url_provider: &CliJsrUrlProvider, jsr_url_provider: &CliJsrUrlProvider,
maybe_resolver: Some(resolver.as_graph_resolver(file_referrer)), maybe_resolver: Some(&resolver),
maybe_npm_resolver: Some(&npm_resolver), maybe_npm_resolver: Some(&npm_resolver),
}, },
)) ))

View file

@ -15,13 +15,14 @@ use deno_core::url::Url;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_graph::GraphKind; use deno_graph::GraphKind;
use deno_graph::Resolution; use deno_graph::Resolution;
use deno_path_util::url_to_file_path;
use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_tls::rustls::RootCertStore;
use deno_runtime::deno_tls::RootCertStoreProvider; use deno_runtime::deno_tls::RootCertStoreProvider;
use deno_runtime::fs_util::specifier_to_file_path;
use deno_semver::jsr::JsrPackageReqReference; use deno_semver::jsr::JsrPackageReqReference;
use indexmap::Equivalent; use indexmap::Equivalent;
use indexmap::IndexSet; use indexmap::IndexSet;
use log::error; use log::error;
use node_resolver::NodeModuleKind;
use serde::Deserialize; use serde::Deserialize;
use serde_json::from_value; use serde_json::from_value;
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -77,6 +78,7 @@ use super::parent_process_checker;
use super::performance::Performance; use super::performance::Performance;
use super::refactor; use super::refactor;
use super::registries::ModuleRegistry; use super::registries::ModuleRegistry;
use super::resolver::LspIsCjsResolver;
use super::resolver::LspResolver; use super::resolver::LspResolver;
use super::testing; use super::testing;
use super::text; use super::text;
@ -96,6 +98,7 @@ use crate::args::CaData;
use crate::args::CacheSetting; use crate::args::CacheSetting;
use crate::args::CliOptions; use crate::args::CliOptions;
use crate::args::Flags; use crate::args::Flags;
use crate::args::InternalFlags;
use crate::args::UnstableFmtOptions; use crate::args::UnstableFmtOptions;
use crate::factory::CliFactory; use crate::factory::CliFactory;
use crate::file_fetcher::FileFetcher; use crate::file_fetcher::FileFetcher;
@ -143,6 +146,7 @@ pub struct StateSnapshot {
pub project_version: usize, pub project_version: usize,
pub assets: AssetsSnapshot, pub assets: AssetsSnapshot,
pub config: Arc<Config>, pub config: Arc<Config>,
pub is_cjs_resolver: Arc<LspIsCjsResolver>,
pub documents: Arc<Documents>, pub documents: Arc<Documents>,
pub resolver: Arc<LspResolver>, pub resolver: Arc<LspResolver>,
} }
@ -202,16 +206,17 @@ pub struct Inner {
pub documents: Documents, pub documents: Documents,
http_client_provider: Arc<HttpClientProvider>, http_client_provider: Arc<HttpClientProvider>,
initial_cwd: PathBuf, initial_cwd: PathBuf,
pub is_cjs_resolver: Arc<LspIsCjsResolver>,
jsr_search_api: CliJsrSearchApi, jsr_search_api: CliJsrSearchApi,
/// Handles module registries, which allow discovery of modules /// Handles module registries, which allow discovery of modules
module_registry: ModuleRegistry, module_registry: ModuleRegistry,
/// A lazily create "server" for handling test run requests. /// A lazily create "server" for handling test run requests.
maybe_testing_server: Option<testing::TestServer>, maybe_testing_server: Option<testing::TestServer>,
npm_search_api: CliNpmSearchApi, pub npm_search_api: CliNpmSearchApi,
project_version: usize, project_version: usize,
/// A collection of measurements which instrument that performance of the LSP. /// A collection of measurements which instrument that performance of the LSP.
performance: Arc<Performance>, performance: Arc<Performance>,
resolver: Arc<LspResolver>, pub resolver: Arc<LspResolver>,
task_queue: LanguageServerTaskQueue, task_queue: LanguageServerTaskQueue,
/// A memoized version of fixable diagnostic codes retrieved from TypeScript. /// A memoized version of fixable diagnostic codes retrieved from TypeScript.
ts_fixable_diagnostics: Vec<String>, ts_fixable_diagnostics: Vec<String>,
@ -479,6 +484,7 @@ impl Inner {
let initial_cwd = std::env::current_dir().unwrap_or_else(|_| { let initial_cwd = std::env::current_dir().unwrap_or_else(|_| {
panic!("Could not resolve current working directory") panic!("Could not resolve current working directory")
}); });
let is_cjs_resolver = Arc::new(LspIsCjsResolver::new(&cache));
Self { Self {
assets, assets,
@ -490,6 +496,7 @@ impl Inner {
documents, documents,
http_client_provider, http_client_provider,
initial_cwd: initial_cwd.clone(), initial_cwd: initial_cwd.clone(),
is_cjs_resolver,
jsr_search_api, jsr_search_api,
project_version: 0, project_version: 0,
task_queue: Default::default(), task_queue: Default::default(),
@ -600,6 +607,7 @@ impl Inner {
project_version: self.project_version, project_version: self.project_version,
assets: self.assets.snapshot(), assets: self.assets.snapshot(),
config: Arc::new(self.config.clone()), config: Arc::new(self.config.clone()),
is_cjs_resolver: self.is_cjs_resolver.clone(),
documents: Arc::new(self.documents.clone()), documents: Arc::new(self.documents.clone()),
resolver: self.resolver.snapshot(), resolver: self.resolver.snapshot(),
}) })
@ -621,12 +629,13 @@ impl Inner {
} }
}); });
self.cache = LspCache::new(global_cache_url); self.cache = LspCache::new(global_cache_url);
self.is_cjs_resolver = Arc::new(LspIsCjsResolver::new(&self.cache));
let deno_dir = self.cache.deno_dir(); let deno_dir = self.cache.deno_dir();
let workspace_settings = self.config.workspace_settings(); let workspace_settings = self.config.workspace_settings();
let maybe_root_path = self let maybe_root_path = self
.config .config
.root_uri() .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( let root_cert_store = get_root_cert_store(
maybe_root_path, maybe_root_path,
workspace_settings.certificate_stores.clone(), workspace_settings.certificate_stores.clone(),
@ -802,7 +811,7 @@ impl Inner {
let mut roots = config let mut roots = config
.workspace_folders .workspace_folders
.iter() .iter()
.filter_map(|p| specifier_to_file_path(&p.0).ok()) .filter_map(|p| url_to_file_path(&p.0).ok())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
roots.sort(); roots.sort();
let roots = roots let roots = roots
@ -862,7 +871,10 @@ impl Inner {
// We ignore these directories by default because there is a // We ignore these directories by default because there is a
// high likelihood they aren't relevant. Someone can opt-into // high likelihood they aren't relevant. Someone can opt-into
// them by specifying one of them as an enabled path. // them by specifying one of them as an enabled path.
if matches!(dir_name.as_str(), "vendor" | "node_modules" | ".git") { if matches!(
dir_name.as_str(),
"vendor" | "coverage" | "node_modules" | ".git"
) {
continue; continue;
} }
// ignore cargo target directories for anyone using Deno with Rust // ignore cargo target directories for anyone using Deno with Rust
@ -903,7 +915,7 @@ impl Inner {
| MediaType::Tsx => {} | MediaType::Tsx => {}
MediaType::Wasm MediaType::Wasm
| MediaType::SourceMap | MediaType::SourceMap
| MediaType::TsBuildInfo | MediaType::Css
| MediaType::Unknown => { | MediaType::Unknown => {
if path.extension().and_then(|s| s.to_str()) != Some("jsonc") { if path.extension().and_then(|s| s.to_str()) != Some("jsonc") {
continue; continue;
@ -962,6 +974,11 @@ impl Inner {
.tree .tree
.refresh(&self.config.settings, &self.workspace_files, &file_fetcher) .refresh(&self.config.settings, &self.workspace_files, &file_fetcher)
.await; .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() { for config_file in self.config.tree.config_files() {
(|| { (|| {
let compiler_options = config_file.to_compiler_options().ok()?.options; let compiler_options = config_file.to_compiler_options().ok()?.options;
@ -973,7 +990,7 @@ impl Inner {
spawn(async move { spawn(async move {
let specifier = { let specifier = {
let inner = ls.inner.read().await; let inner = ls.inner.read().await;
let resolver = inner.resolver.as_graph_resolver(Some(&referrer)); let resolver = inner.resolver.as_cli_resolver(Some(&referrer));
let Ok(specifier) = resolver.resolve( let Ok(specifier) = resolver.resolve(
&specifier, &specifier,
&deno_graph::Range { &deno_graph::Range {
@ -981,6 +998,7 @@ impl Inner {
start: deno_graph::Position::zeroed(), start: deno_graph::Position::zeroed(),
end: deno_graph::Position::zeroed(), end: deno_graph::Position::zeroed(),
}, },
NodeModuleKind::Esm,
deno_graph::source::ResolutionMode::Types, deno_graph::source::ResolutionMode::Types,
) else { ) else {
return; return;
@ -1124,7 +1142,7 @@ impl Inner {
{ {
return; return;
} }
match specifier_to_file_path(&specifier) { match url_to_file_path(&specifier) {
Ok(path) if is_importable_ext(&path) => {} Ok(path) if is_importable_ext(&path) => {}
_ => return, _ => return,
} }
@ -1362,7 +1380,7 @@ impl Inner {
{ {
specifier = uri_to_url(&params.text_document.uri); specifier = uri_to_url(&params.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); error!("{:#}", err);
LspError::invalid_request() LspError::invalid_request()
})?; })?;
@ -1378,14 +1396,10 @@ impl Inner {
.clone(); .clone();
fmt_options.use_tabs = Some(!params.options.insert_spaces); fmt_options.use_tabs = Some(!params.options.insert_spaces);
fmt_options.indent_width = Some(params.options.tab_size as u8); fmt_options.indent_width = Some(params.options.tab_size as u8);
let maybe_workspace = self let config_data = self.config.tree.data_for_specifier(&specifier);
.config
.tree
.data_for_specifier(&specifier)
.map(|d| &d.member_dir.workspace);
let unstable_options = UnstableFmtOptions { let unstable_options = UnstableFmtOptions {
component: maybe_workspace component: config_data
.map(|w| w.has_unstable("fmt-component")) .map(|d| d.unstable.contains("fmt-component"))
.unwrap_or(false), .unwrap_or(false),
}; };
let document = document.clone(); let document = document.clone();
@ -1612,11 +1626,15 @@ impl Inner {
None => false, None => false,
}) })
.collect(); .collect();
if !fixable_diagnostics.is_empty() {
let mut code_actions = CodeActionCollection::default(); let mut code_actions = CodeActionCollection::default();
if !fixable_diagnostics.is_empty() {
let file_diagnostics = self let file_diagnostics = self
.diagnostics_server .diagnostics_server
.get_ts_diagnostics(&specifier, asset_or_doc.document_lsp_version()); .get_ts_diagnostics(&specifier, asset_or_doc.document_lsp_version());
let specifier_kind = asset_or_doc
.document()
.map(|d| self.is_cjs_resolver.get_doc_module_kind(d))
.unwrap_or(NodeModuleKind::Esm);
let mut includes_no_cache = false; let mut includes_no_cache = false;
for diagnostic in &fixable_diagnostics { for diagnostic in &fixable_diagnostics {
match diagnostic.source.as_deref() { match diagnostic.source.as_deref() {
@ -1655,7 +1673,13 @@ impl Inner {
.await; .await;
for action in actions { for action in actions {
code_actions code_actions
.add_ts_fix_action(&specifier, &action, diagnostic, self) .add_ts_fix_action(
&specifier,
specifier_kind,
&action,
diagnostic,
self,
)
.map_err(|err| { .map_err(|err| {
error!("Unable to convert fix: {:#}", err); error!("Unable to convert fix: {:#}", err);
LspError::internal_error() LspError::internal_error()
@ -1721,9 +1745,14 @@ impl Inner {
.add_cache_all_action(&specifier, no_cache_diagnostics.to_owned()); .add_cache_all_action(&specifier, no_cache_diagnostics.to_owned());
} }
} }
}
if let Some(document) = asset_or_doc.document() {
code_actions
.add_source_actions(document, &params.range, self)
.await;
}
code_actions.set_preferred_fixes(); code_actions.set_preferred_fixes();
all_actions.extend(code_actions.get_response()); all_actions.extend(code_actions.get_response());
}
// Refactor // Refactor
let only = params let only = params
@ -1796,10 +1825,9 @@ impl Inner {
error!("Unable to decode code action data: {:#}", err); error!("Unable to decode code action data: {:#}", err);
LspError::invalid_params("The CodeAction's data is invalid.") LspError::invalid_params("The CodeAction's data is invalid.")
})?; })?;
let scope = self let maybe_asset_or_doc =
.get_asset_or_document(&code_action_data.specifier) self.get_asset_or_document(&code_action_data.specifier).ok();
.ok() let scope = maybe_asset_or_doc.as_ref().and_then(|d| d.scope().cloned());
.and_then(|d| d.scope().cloned());
let combined_code_actions = self let combined_code_actions = self
.ts_server .ts_server
.get_combined_code_fix( .get_combined_code_fix(
@ -1826,8 +1854,13 @@ impl Inner {
let changes = if code_action_data.fix_id == "fixMissingImport" { let changes = if code_action_data.fix_id == "fixMissingImport" {
fix_ts_import_changes( fix_ts_import_changes(
&code_action_data.specifier, &code_action_data.specifier,
maybe_asset_or_doc
.as_ref()
.and_then(|d| d.document())
.map(|d| self.is_cjs_resolver.get_doc_module_kind(d))
.unwrap_or(NodeModuleKind::Esm),
&combined_code_actions.changes, &combined_code_actions.changes,
&self.get_ts_response_import_mapper(&code_action_data.specifier), self,
) )
.map_err(|err| { .map_err(|err| {
error!("Unable to remap changes: {:#}", err); error!("Unable to remap changes: {:#}", err);
@ -1879,8 +1912,12 @@ impl Inner {
if kind_suffix == ".rewrite.function.returnType" { if kind_suffix == ".rewrite.function.returnType" {
refactor_edit_info.edits = fix_ts_import_changes( refactor_edit_info.edits = fix_ts_import_changes(
&action_data.specifier, &action_data.specifier,
asset_or_doc
.document()
.map(|d| self.is_cjs_resolver.get_doc_module_kind(d))
.unwrap_or(NodeModuleKind::Esm),
&refactor_edit_info.edits, &refactor_edit_info.edits,
&self.get_ts_response_import_mapper(&action_data.specifier), self,
) )
.map_err(|err| { .map_err(|err| {
error!("Unable to remap changes: {:#}", err); error!("Unable to remap changes: {:#}", err);
@ -1911,7 +1948,9 @@ impl Inner {
// todo(dsherret): this should probably just take the resolver itself // todo(dsherret): this should probably just take the resolver itself
// as the import map is an implementation detail // as the import map is an implementation detail
.and_then(|d| d.resolver.maybe_import_map()), .and_then(|d| d.resolver.maybe_import_map()),
self.resolver.as_ref(), &self.resolver,
&self.ts_server.specifier_map,
file_referrer,
) )
} }
@ -2226,6 +2265,7 @@ impl Inner {
&self.jsr_search_api, &self.jsr_search_api,
&self.npm_search_api, &self.npm_search_api,
&self.documents, &self.documents,
&self.is_cjs_resolver,
self.resolver.as_ref(), self.resolver.as_ref(),
self self
.config .config
@ -2273,7 +2313,11 @@ impl Inner {
.into(), .into(),
scope.cloned(), scope.cloned(),
) )
.await; .await
.unwrap_or_else(|err| {
error!("Unable to get completion info from TypeScript: {:#}", err);
None
});
if let Some(completions) = maybe_completion_info { if let Some(completions) = maybe_completion_info {
response = Some( response = Some(
@ -2508,7 +2552,7 @@ impl Inner {
let maybe_root_path_owned = self let maybe_root_path_owned = self
.config .config
.root_uri() .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(); let mut resolved_items = Vec::<CallHierarchyIncomingCall>::new();
for item in incoming_calls.iter() { for item in incoming_calls.iter() {
if let Some(resolved) = item.try_resolve_call_hierarchy_incoming_call( if let Some(resolved) = item.try_resolve_call_hierarchy_incoming_call(
@ -2554,7 +2598,7 @@ impl Inner {
let maybe_root_path_owned = self let maybe_root_path_owned = self
.config .config
.root_uri() .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(); let mut resolved_items = Vec::<CallHierarchyOutgoingCall>::new();
for item in outgoing_calls.iter() { for item in outgoing_calls.iter() {
if let Some(resolved) = item.try_resolve_call_hierarchy_outgoing_call( if let Some(resolved) = item.try_resolve_call_hierarchy_outgoing_call(
@ -2603,7 +2647,7 @@ impl Inner {
let maybe_root_path_owned = self let maybe_root_path_owned = self
.config .config
.root_uri() .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(); let mut resolved_items = Vec::<CallHierarchyItem>::new();
match one_or_many { match one_or_many {
tsc::OneOrMany::One(item) => { tsc::OneOrMany::One(item) => {
@ -3600,7 +3644,10 @@ impl Inner {
}; };
let cli_options = CliOptions::new( let cli_options = CliOptions::new(
Arc::new(Flags { Arc::new(Flags {
internal: InternalFlags {
cache_path: Some(self.cache.deno_dir().root.clone()), cache_path: Some(self.cache.deno_dir().root.clone()),
..Default::default()
},
ca_stores: workspace_settings.certificate_stores.clone(), ca_stores: workspace_settings.certificate_stores.clone(),
ca_data: workspace_settings.tls_certificate.clone().map(CaData::File), ca_data: workspace_settings.tls_certificate.clone().map(CaData::File),
unsafely_ignore_certificate_errors: workspace_settings unsafely_ignore_certificate_errors: workspace_settings
@ -3797,7 +3844,7 @@ impl Inner {
let maybe_inlay_hints = maybe_inlay_hints.map(|hints| { let maybe_inlay_hints = maybe_inlay_hints.map(|hints| {
hints hints
.iter() .iter()
.map(|hint| hint.to_lsp(line_index.clone())) .map(|hint| hint.to_lsp(line_index.clone(), self))
.collect() .collect()
}); });
self.performance.measure(mark); self.performance.measure(mark);
@ -3933,7 +3980,9 @@ mod tests {
fn test_walk_workspace() { fn test_walk_workspace() {
let temp_dir = TempDir::new(); let temp_dir = TempDir::new();
temp_dir.create_dir_all("root1/vendor/"); temp_dir.create_dir_all("root1/vendor/");
temp_dir.create_dir_all("root1/coverage/");
temp_dir.write("root1/vendor/mod.ts", ""); // no, vendor temp_dir.write("root1/vendor/mod.ts", ""); // no, vendor
temp_dir.write("root1/coverage/mod.ts", ""); // no, coverage
temp_dir.create_dir_all("root1/node_modules/"); temp_dir.create_dir_all("root1/node_modules/");
temp_dir.write("root1/node_modules/mod.ts", ""); // no, node_modules temp_dir.write("root1/node_modules/mod.ts", ""); // no, node_modules

View file

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

View file

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

View file

@ -11,7 +11,7 @@ pub fn start(parent_process_id: u32) {
std::thread::sleep(Duration::from_secs(10)); std::thread::sleep(Duration::from_secs(10));
if !is_process_active(parent_process_id) { if !is_process_active(parent_process_id) {
std::process::exit(1); deno_runtime::exit(1);
} }
}); });
} }

View file

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

View file

@ -263,7 +263,7 @@ impl ReplLanguageServer {
} }
fn get_document_uri(&self) -> Uri { fn get_document_uri(&self) -> Uri {
uri_parse_unencoded(self.cwd_uri.join("$deno$repl.ts").unwrap().as_str()) uri_parse_unencoded(self.cwd_uri.join("$deno$repl.mts").unwrap().as_str())
.unwrap() .unwrap()
} }
} }

View file

@ -2,27 +2,35 @@
use dashmap::DashMap; use dashmap::DashMap;
use deno_ast::MediaType; use deno_ast::MediaType;
use deno_cache_dir::npm::NpmCacheDir;
use deno_cache_dir::HttpCache; use deno_cache_dir::HttpCache;
use deno_config::deno_json::JsxImportSourceConfig;
use deno_config::workspace::PackageJsonDepResolution; use deno_config::workspace::PackageJsonDepResolution;
use deno_config::workspace::WorkspaceResolver; use deno_config::workspace::WorkspaceResolver;
use deno_core::url::Url; use deno_core::url::Url;
use deno_graph::source::Resolver; use deno_graph::source::ResolutionMode;
use deno_graph::GraphImport; use deno_graph::GraphImport;
use deno_graph::ModuleSpecifier; use deno_graph::ModuleSpecifier;
use deno_graph::Range;
use deno_npm::NpmSystemInfo; use deno_npm::NpmSystemInfo;
use deno_path_util::url_from_directory_path;
use deno_path_util::url_to_file_path;
use deno_resolver::npm::NpmReqResolverOptions;
use deno_resolver::DenoResolverOptions;
use deno_resolver::NodeAndNpmReqResolver;
use deno_runtime::deno_fs; use deno_runtime::deno_fs;
use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_node::PackageJson; use deno_runtime::deno_node::PackageJson;
use deno_runtime::fs_util::specifier_to_file_path; use deno_runtime::deno_node::PackageJsonResolver;
use deno_semver::jsr::JsrPackageReqReference; use deno_semver::jsr::JsrPackageReqReference;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use indexmap::IndexMap; use indexmap::IndexMap;
use node_resolver::errors::ClosestPkgJsonError; use node_resolver::errors::ClosestPkgJsonError;
use node_resolver::NodeResolution; use node_resolver::InNpmPackageChecker;
use node_resolver::NodeModuleKind;
use node_resolver::NodeResolutionMode; use node_resolver::NodeResolutionMode;
use node_resolver::NpmResolver;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::BTreeSet; use std::collections::BTreeSet;
@ -31,51 +39,69 @@ use std::collections::HashSet;
use std::sync::Arc; use std::sync::Arc;
use super::cache::LspCache; use super::cache::LspCache;
use super::documents::Document;
use super::jsr::JsrCacheResolver; use super::jsr::JsrCacheResolver;
use crate::args::create_default_npmrc; use crate::args::create_default_npmrc;
use crate::args::CacheSetting; use crate::args::CacheSetting;
use crate::args::CliLockfile; use crate::args::CliLockfile;
use crate::args::NpmInstallDepsProvider; use crate::args::NpmInstallDepsProvider;
use crate::cache::DenoCacheEnvFsAdapter;
use crate::factory::Deferred;
use crate::graph_util::CliJsrUrlProvider; use crate::graph_util::CliJsrUrlProvider;
use crate::http_util::HttpClientProvider; use crate::http_util::HttpClientProvider;
use crate::lsp::config::Config; use crate::lsp::config::Config;
use crate::lsp::config::ConfigData; use crate::lsp::config::ConfigData;
use crate::lsp::logging::lsp_warn; use crate::lsp::logging::lsp_warn;
use crate::npm::create_cli_npm_resolver_for_lsp; use crate::npm::create_cli_npm_resolver_for_lsp;
use crate::npm::CliByonmNpmResolverCreateOptions;
use crate::npm::CliManagedInNpmPkgCheckerCreateOptions;
use crate::npm::CliManagedNpmResolverCreateOptions;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::npm::CliNpmResolverByonmCreateOptions;
use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverCreateOptions;
use crate::npm::CliNpmResolverManagedCreateOptions;
use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CliNpmResolverManagedSnapshotOption;
use crate::npm::CreateInNpmPkgCheckerOptions;
use crate::npm::ManagedCliNpmResolver; use crate::npm::ManagedCliNpmResolver;
use crate::resolver::CjsResolutionStore; use crate::resolver::CliDenoResolver;
use crate::resolver::CliGraphResolver; use crate::resolver::CliDenoResolverFs;
use crate::resolver::CliGraphResolverOptions; use crate::resolver::CliNpmReqResolver;
use crate::resolver::CliNodeResolver; use crate::resolver::CliResolver;
use crate::resolver::CliResolverOptions;
use crate::resolver::IsCjsResolver;
use crate::resolver::WorkerCliNpmGraphResolver; use crate::resolver::WorkerCliNpmGraphResolver;
use crate::tsc::into_specifier_and_media_type;
use crate::util::fs::canonicalize_path_maybe_not_exists;
use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle; use crate::util::progress_bar::ProgressBarStyle;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct LspScopeResolver { struct LspScopeResolver {
graph_resolver: Arc<CliGraphResolver>, resolver: Arc<CliResolver>,
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
jsr_resolver: Option<Arc<JsrCacheResolver>>, jsr_resolver: Option<Arc<JsrCacheResolver>>,
npm_resolver: Option<Arc<dyn CliNpmResolver>>, npm_resolver: Option<Arc<dyn CliNpmResolver>>,
node_resolver: Option<Arc<CliNodeResolver>>, node_resolver: Option<Arc<NodeResolver>>,
npm_pkg_req_resolver: Option<Arc<CliNpmReqResolver>>,
pkg_json_resolver: Arc<PackageJsonResolver>,
redirect_resolver: Option<Arc<RedirectResolver>>, redirect_resolver: Option<Arc<RedirectResolver>>,
graph_imports: Arc<IndexMap<ModuleSpecifier, GraphImport>>, graph_imports: Arc<IndexMap<ModuleSpecifier, GraphImport>>,
package_json_deps_by_resolution: Arc<IndexMap<ModuleSpecifier, String>>,
config_data: Option<Arc<ConfigData>>, config_data: Option<Arc<ConfigData>>,
} }
impl Default for LspScopeResolver { impl Default for LspScopeResolver {
fn default() -> Self { fn default() -> Self {
let factory = ResolverFactory::new(None);
Self { Self {
graph_resolver: create_graph_resolver(None, None, None), resolver: factory.cli_resolver().clone(),
in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(),
jsr_resolver: None, jsr_resolver: None,
npm_resolver: None, npm_resolver: None,
node_resolver: None, node_resolver: None,
npm_pkg_req_resolver: None,
pkg_json_resolver: factory.pkg_json_resolver().clone(),
redirect_resolver: None, redirect_resolver: None,
graph_imports: Default::default(), graph_imports: Default::default(),
package_json_deps_by_resolution: Default::default(),
config_data: None, config_data: None,
} }
} }
@ -87,22 +113,16 @@ impl LspScopeResolver {
cache: &LspCache, cache: &LspCache,
http_client_provider: Option<&Arc<HttpClientProvider>>, http_client_provider: Option<&Arc<HttpClientProvider>>,
) -> Self { ) -> Self {
let mut npm_resolver = None; let mut factory = ResolverFactory::new(config_data);
let mut node_resolver = None; if let Some(http_client_provider) = http_client_provider {
if let Some(http_client) = http_client_provider { factory.init_npm_resolver(http_client_provider, cache).await;
npm_resolver = create_npm_resolver(
config_data.map(|d| d.as_ref()),
cache,
http_client,
)
.await;
node_resolver = create_node_resolver(npm_resolver.as_ref());
} }
let graph_resolver = create_graph_resolver( let in_npm_pkg_checker = factory.in_npm_pkg_checker().clone();
config_data.map(|d| d.as_ref()), let npm_resolver = factory.npm_resolver().cloned();
npm_resolver.as_ref(), let node_resolver = factory.node_resolver().cloned();
node_resolver.as_ref(), let npm_pkg_req_resolver = factory.npm_pkg_req_resolver().cloned();
); let cli_resolver = factory.cli_resolver().clone();
let pkg_json_resolver = factory.pkg_json_resolver().clone();
let jsr_resolver = Some(Arc::new(JsrCacheResolver::new( let jsr_resolver = Some(Arc::new(JsrCacheResolver::new(
cache.for_specifier(config_data.map(|d| d.scope.as_ref())), cache.for_specifier(config_data.map(|d| d.scope.as_ref())),
config_data.map(|d| d.as_ref()), config_data.map(|d| d.as_ref()),
@ -111,7 +131,9 @@ impl LspScopeResolver {
cache.for_specifier(config_data.map(|d| d.scope.as_ref())), cache.for_specifier(config_data.map(|d| d.scope.as_ref())),
config_data.and_then(|d| d.lockfile.clone()), config_data.and_then(|d| d.lockfile.clone()),
))); )));
let npm_graph_resolver = graph_resolver.create_graph_npm_resolver(); let npm_graph_resolver = cli_resolver.create_graph_npm_resolver();
let maybe_jsx_import_source_config =
config_data.and_then(|d| d.maybe_jsx_import_source_config());
let graph_imports = config_data let graph_imports = config_data
.and_then(|d| d.member_dir.workspace.to_compiler_option_types().ok()) .and_then(|d| d.member_dir.workspace.to_compiler_option_types().ok())
.map(|imports| { .map(|imports| {
@ -119,11 +141,18 @@ impl LspScopeResolver {
imports imports
.into_iter() .into_iter()
.map(|(referrer, imports)| { .map(|(referrer, imports)| {
let resolver = SingleReferrerGraphResolver {
valid_referrer: &referrer,
referrer_kind: NodeModuleKind::Esm,
cli_resolver: &cli_resolver,
jsx_import_source_config: maybe_jsx_import_source_config
.as_ref(),
};
let graph_import = GraphImport::new( let graph_import = GraphImport::new(
&referrer, &referrer,
imports, imports,
&CliJsrUrlProvider, &CliJsrUrlProvider,
Some(graph_resolver.as_ref()), Some(&resolver),
Some(&npm_graph_resolver), Some(&npm_graph_resolver),
); );
(referrer, graph_import) (referrer, graph_import)
@ -132,33 +161,70 @@ impl LspScopeResolver {
) )
}) })
.unwrap_or_default(); .unwrap_or_default();
let package_json_deps_by_resolution = (|| {
let npm_pkg_req_resolver = npm_pkg_req_resolver.as_ref()?;
let package_json = config_data?.maybe_pkg_json()?;
let referrer = package_json.specifier();
let dependencies = package_json.dependencies.as_ref()?;
let result = dependencies
.iter()
.flat_map(|(name, _)| {
let req_ref =
NpmPackageReqReference::from_str(&format!("npm:{name}")).ok()?;
let specifier = into_specifier_and_media_type(Some(
npm_pkg_req_resolver
.resolve_req_reference(
&req_ref,
&referrer,
// todo(dsherret): this is wrong because it doesn't consider CJS referrers
NodeModuleKind::Esm,
NodeResolutionMode::Types,
)
.ok()?,
))
.0;
Some((specifier, name.clone()))
})
.collect();
Some(result)
})();
let package_json_deps_by_resolution =
Arc::new(package_json_deps_by_resolution.unwrap_or_default());
Self { Self {
graph_resolver, resolver: cli_resolver,
in_npm_pkg_checker,
jsr_resolver, jsr_resolver,
npm_pkg_req_resolver,
npm_resolver, npm_resolver,
node_resolver, node_resolver,
pkg_json_resolver,
redirect_resolver, redirect_resolver,
graph_imports, graph_imports,
package_json_deps_by_resolution,
config_data: config_data.cloned(), config_data: config_data.cloned(),
} }
} }
fn snapshot(&self) -> Arc<Self> { fn snapshot(&self) -> Arc<Self> {
let mut factory = ResolverFactory::new(self.config_data.as_ref());
let npm_resolver = let npm_resolver =
self.npm_resolver.as_ref().map(|r| r.clone_snapshotted()); self.npm_resolver.as_ref().map(|r| r.clone_snapshotted());
let node_resolver = create_node_resolver(npm_resolver.as_ref()); if let Some(npm_resolver) = &npm_resolver {
let graph_resolver = create_graph_resolver( factory.set_npm_resolver(npm_resolver.clone());
self.config_data.as_deref(), }
npm_resolver.as_ref(),
node_resolver.as_ref(),
);
Arc::new(Self { Arc::new(Self {
graph_resolver, resolver: factory.cli_resolver().clone(),
in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(),
jsr_resolver: self.jsr_resolver.clone(), jsr_resolver: self.jsr_resolver.clone(),
npm_resolver, npm_pkg_req_resolver: factory.npm_pkg_req_resolver().cloned(),
node_resolver, npm_resolver: factory.npm_resolver().cloned(),
node_resolver: factory.node_resolver().cloned(),
redirect_resolver: self.redirect_resolver.clone(), redirect_resolver: self.redirect_resolver.clone(),
pkg_json_resolver: factory.pkg_json_resolver().clone(),
graph_imports: self.graph_imports.clone(), graph_imports: self.graph_imports.clone(),
package_json_deps_by_resolution: self
.package_json_deps_by_resolution
.clone(),
config_data: self.config_data.clone(), config_data: self.config_data.clone(),
}) })
} }
@ -244,12 +310,12 @@ impl LspResolver {
} }
} }
pub fn as_graph_resolver( pub fn as_cli_resolver(
&self, &self,
file_referrer: Option<&ModuleSpecifier>, file_referrer: Option<&ModuleSpecifier>,
) -> &dyn Resolver { ) -> &CliResolver {
let resolver = self.get_scope_resolver(file_referrer); let resolver = self.get_scope_resolver(file_referrer);
resolver.graph_resolver.as_ref() resolver.resolver.as_ref()
} }
pub fn create_graph_npm_resolver( pub fn create_graph_npm_resolver(
@ -257,7 +323,23 @@ impl LspResolver {
file_referrer: Option<&ModuleSpecifier>, file_referrer: Option<&ModuleSpecifier>,
) -> WorkerCliNpmGraphResolver { ) -> WorkerCliNpmGraphResolver {
let resolver = self.get_scope_resolver(file_referrer); let resolver = self.get_scope_resolver(file_referrer);
resolver.graph_resolver.create_graph_npm_resolver() resolver.resolver.create_graph_npm_resolver()
}
pub fn as_config_data(
&self,
file_referrer: Option<&ModuleSpecifier>,
) -> Option<&Arc<ConfigData>> {
let resolver = self.get_scope_resolver(file_referrer);
resolver.config_data.as_ref()
}
pub fn in_npm_pkg_checker(
&self,
file_referrer: Option<&ModuleSpecifier>,
) -> &Arc<dyn InNpmPackageChecker> {
let resolver = self.get_scope_resolver(file_referrer);
&resolver.in_npm_pkg_checker
} }
pub fn maybe_managed_npm_resolver( pub fn maybe_managed_npm_resolver(
@ -323,15 +405,33 @@ impl LspResolver {
&self, &self,
req_ref: &NpmPackageReqReference, req_ref: &NpmPackageReqReference,
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
referrer_kind: NodeModuleKind,
file_referrer: Option<&ModuleSpecifier>, file_referrer: Option<&ModuleSpecifier>,
) -> Option<(ModuleSpecifier, MediaType)> { ) -> Option<(ModuleSpecifier, MediaType)> {
let resolver = self.get_scope_resolver(file_referrer); let resolver = self.get_scope_resolver(file_referrer);
let node_resolver = resolver.node_resolver.as_ref()?; let npm_pkg_req_resolver = resolver.npm_pkg_req_resolver.as_ref()?;
Some(NodeResolution::into_specifier_and_media_type( Some(into_specifier_and_media_type(Some(
node_resolver npm_pkg_req_resolver
.resolve_req_reference(req_ref, referrer, NodeResolutionMode::Types) .resolve_req_reference(
.ok(), req_ref,
)) referrer,
referrer_kind,
NodeResolutionMode::Types,
)
.ok()?,
)))
}
pub fn file_url_to_package_json_dep(
&self,
specifier: &ModuleSpecifier,
file_referrer: Option<&ModuleSpecifier>,
) -> Option<String> {
let resolver = self.get_scope_resolver(file_referrer);
resolver
.package_json_deps_by_resolution
.get(specifier)
.cloned()
} }
pub fn in_node_modules(&self, specifier: &ModuleSpecifier) -> bool { pub fn in_node_modules(&self, specifier: &ModuleSpecifier) -> bool {
@ -345,14 +445,10 @@ impl LspResolver {
.contains("/node_modules/") .contains("/node_modules/")
} }
let global_npm_resolver = self if let Some(node_resolver) =
.get_scope_resolver(Some(specifier)) &self.get_scope_resolver(Some(specifier)).node_resolver
.npm_resolver {
.as_ref() if node_resolver.in_npm_package(specifier) {
.and_then(|npm_resolver| npm_resolver.as_managed())
.filter(|r| r.root_node_modules_path().is_none());
if let Some(npm_resolver) = &global_npm_resolver {
if npm_resolver.in_npm_package(specifier) {
return true; return true;
} }
} }
@ -360,16 +456,27 @@ impl LspResolver {
has_node_modules_dir(specifier) has_node_modules_dir(specifier)
} }
pub fn node_media_type( pub fn is_bare_package_json_dep(
&self, &self,
specifier: &ModuleSpecifier, specifier_text: &str,
) -> Option<MediaType> { referrer: &ModuleSpecifier,
let resolver = self.get_scope_resolver(Some(specifier)); referrer_kind: NodeModuleKind,
let node_resolver = resolver.node_resolver.as_ref()?; ) -> bool {
let resolution = node_resolver let resolver = self.get_scope_resolver(Some(referrer));
.url_to_node_resolution(specifier.clone()) let Some(npm_pkg_req_resolver) = resolver.npm_pkg_req_resolver.as_ref()
.ok()?; else {
Some(NodeResolution::into_specifier_and_media_type(Some(resolution)).1) return false;
};
npm_pkg_req_resolver
.resolve_if_for_npm_pkg(
specifier_text,
referrer,
referrer_kind,
NodeResolutionMode::Types,
)
.ok()
.flatten()
.is_some()
} }
pub fn get_closest_package_json( pub fn get_closest_package_json(
@ -377,10 +484,9 @@ impl LspResolver {
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
) -> Result<Option<Arc<PackageJson>>, ClosestPkgJsonError> { ) -> Result<Option<Arc<PackageJson>>, ClosestPkgJsonError> {
let resolver = self.get_scope_resolver(Some(referrer)); let resolver = self.get_scope_resolver(Some(referrer));
let Some(node_resolver) = resolver.node_resolver.as_ref() else { resolver
return Ok(None); .pkg_json_resolver
}; .get_closest_package_json(referrer)
node_resolver.get_closest_package_json(referrer)
} }
pub fn resolve_redirects( pub fn resolve_redirects(
@ -432,27 +538,67 @@ impl LspResolver {
} }
} }
async fn create_npm_resolver( #[derive(Default)]
config_data: Option<&ConfigData>, struct ResolverFactoryServices {
cache: &LspCache, cli_resolver: Deferred<Arc<CliResolver>>,
in_npm_pkg_checker: Deferred<Arc<dyn InNpmPackageChecker>>,
node_resolver: Deferred<Option<Arc<NodeResolver>>>,
npm_pkg_req_resolver: Deferred<Option<Arc<CliNpmReqResolver>>>,
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
}
struct ResolverFactory<'a> {
config_data: Option<&'a Arc<ConfigData>>,
fs: Arc<dyn deno_fs::FileSystem>,
pkg_json_resolver: Arc<PackageJsonResolver>,
services: ResolverFactoryServices,
}
impl<'a> ResolverFactory<'a> {
pub fn new(config_data: Option<&'a Arc<ConfigData>>) -> Self {
let fs = Arc::new(deno_fs::RealFs);
let pkg_json_resolver = Arc::new(PackageJsonResolver::new(
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
));
Self {
config_data,
fs,
pkg_json_resolver,
services: Default::default(),
}
}
async fn init_npm_resolver(
&mut self,
http_client_provider: &Arc<HttpClientProvider>, http_client_provider: &Arc<HttpClientProvider>,
) -> Option<Arc<dyn CliNpmResolver>> { cache: &LspCache,
let enable_byonm = config_data.map(|d| d.byonm).unwrap_or(false); ) {
let enable_byonm = self.config_data.map(|d| d.byonm).unwrap_or(false);
let options = if enable_byonm { let options = if enable_byonm {
CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions { CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions {
fs: Arc::new(deno_fs::RealFs), fs: CliDenoResolverFs(Arc::new(deno_fs::RealFs)),
root_node_modules_dir: config_data.and_then(|config_data| { pkg_json_resolver: self.pkg_json_resolver.clone(),
root_node_modules_dir: self.config_data.and_then(|config_data| {
config_data.node_modules_dir.clone().or_else(|| { config_data.node_modules_dir.clone().or_else(|| {
specifier_to_file_path(&config_data.scope) url_to_file_path(&config_data.scope)
.ok() .ok()
.map(|p| p.join("node_modules/")) .map(|p| p.join("node_modules/"))
}) })
}), }),
}) })
} else { } else {
CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions { let npmrc = self
.config_data
.and_then(|d| d.npmrc.clone())
.unwrap_or_else(create_default_npmrc);
let npm_cache_dir = Arc::new(NpmCacheDir::new(
&DenoCacheEnvFsAdapter(self.fs.as_ref()),
cache.deno_dir().npm_folder_path(),
npmrc.get_all_known_registries_urls(),
));
CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions {
http_client_provider: http_client_provider.clone(), http_client_provider: http_client_provider.clone(),
snapshot: match config_data.and_then(|d| d.lockfile.as_ref()) { snapshot: match self.config_data.and_then(|d| d.lockfile.as_ref()) {
Some(lockfile) => { Some(lockfile) => {
CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( CliNpmResolverManagedSnapshotOption::ResolveFromLockfile(
lockfile.clone(), lockfile.clone(),
@ -464,62 +610,55 @@ async fn create_npm_resolver(
// updating it. Only the cache request should update the lockfile. // updating it. Only the cache request should update the lockfile.
maybe_lockfile: None, maybe_lockfile: None,
fs: Arc::new(deno_fs::RealFs), fs: Arc::new(deno_fs::RealFs),
npm_global_cache_dir: cache.deno_dir().npm_folder_path(), npm_cache_dir,
// Use an "only" cache setting in order to make the // Use an "only" cache setting in order to make the
// user do an explicit "cache" command and prevent // user do an explicit "cache" command and prevent
// the cache from being filled with lots of packages while // the cache from being filled with lots of packages while
// the user is typing. // the user is typing.
cache_setting: CacheSetting::Only, cache_setting: CacheSetting::Only,
text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly), text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly),
maybe_node_modules_path: config_data maybe_node_modules_path: self
.config_data
.and_then(|d| d.node_modules_dir.clone()), .and_then(|d| d.node_modules_dir.clone()),
// only used for top level install, so we can ignore this // only used for top level install, so we can ignore this
npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()), npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()),
npmrc: config_data npmrc,
.and_then(|d| d.npmrc.clone())
.unwrap_or_else(create_default_npmrc),
npm_system_info: NpmSystemInfo::default(), npm_system_info: NpmSystemInfo::default(),
lifecycle_scripts: Default::default(), lifecycle_scripts: Default::default(),
}) })
}; };
Some(create_cli_npm_resolver_for_lsp(options).await) self.set_npm_resolver(create_cli_npm_resolver_for_lsp(options).await);
} }
fn create_node_resolver( pub fn set_npm_resolver(&mut self, npm_resolver: Arc<dyn CliNpmResolver>) {
npm_resolver: Option<&Arc<dyn CliNpmResolver>>, self.services.npm_resolver = Some(npm_resolver);
) -> Option<Arc<CliNodeResolver>> { }
use once_cell::sync::Lazy;
// it's not ideal to share this across all scopes and to pub fn npm_resolver(&self) -> Option<&Arc<dyn CliNpmResolver>> {
// never clear it, but it's fine for the time being self.services.npm_resolver.as_ref()
static CJS_RESOLUTIONS: Lazy<Arc<CjsResolutionStore>> = }
Lazy::new(Default::default);
let npm_resolver = npm_resolver?; pub fn cli_resolver(&self) -> &Arc<CliResolver> {
let fs = Arc::new(deno_fs::RealFs); self.services.cli_resolver.get_or_init(|| {
let node_resolver_inner = Arc::new(NodeResolver::new( let npm_req_resolver = self.npm_pkg_req_resolver().cloned();
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), let deno_resolver = Arc::new(CliDenoResolver::new(DenoResolverOptions {
npm_resolver.clone().into_npm_resolver(), in_npm_pkg_checker: self.in_npm_pkg_checker().clone(),
)); node_and_req_resolver: match (self.node_resolver(), npm_req_resolver) {
Some(Arc::new(CliNodeResolver::new( (Some(node_resolver), Some(npm_req_resolver)) => {
CJS_RESOLUTIONS.clone(), Some(NodeAndNpmReqResolver {
fs, node_resolver: node_resolver.clone(),
node_resolver_inner, npm_req_resolver,
npm_resolver.clone(), })
))) }
} _ => None,
},
fn create_graph_resolver( sloppy_imports_resolver: self
config_data: Option<&ConfigData>, .config_data
npm_resolver: Option<&Arc<dyn CliNpmResolver>>, .and_then(|d| d.sloppy_imports_resolver.clone()),
node_resolver: Option<&Arc<CliNodeResolver>>, workspace_resolver: self
) -> Arc<CliGraphResolver> { .config_data
let workspace = config_data.map(|d| &d.member_dir.workspace); .map(|d| d.resolver.clone())
Arc::new(CliGraphResolver::new(CliGraphResolverOptions { .unwrap_or_else(|| {
node_resolver: node_resolver.cloned(),
npm_resolver: npm_resolver.cloned(),
workspace_resolver: config_data.map(|d| d.resolver.clone()).unwrap_or_else(
|| {
Arc::new(WorkspaceResolver::new_raw( Arc::new(WorkspaceResolver::new_raw(
// this is fine because this is only used before initialization // this is fine because this is only used before initialization
Arc::new(ModuleSpecifier::parse("file:///").unwrap()), Arc::new(ModuleSpecifier::parse("file:///").unwrap()),
@ -528,17 +667,77 @@ fn create_graph_resolver(
Vec::new(), Vec::new(),
PackageJsonDepResolution::Disabled, PackageJsonDepResolution::Disabled,
)) ))
},
),
maybe_jsx_import_source_config: workspace.and_then(|workspace| {
workspace.to_maybe_jsx_import_source_config().ok().flatten()
}), }),
maybe_vendor_dir: config_data.and_then(|d| d.vendor_dir.as_ref()), is_byonm: self.config_data.map(|d| d.byonm).unwrap_or(false),
bare_node_builtins_enabled: workspace maybe_vendor_dir: self.config_data.and_then(|d| d.vendor_dir.as_ref()),
.is_some_and(|workspace| workspace.has_unstable("bare-node-builtins")), }));
sloppy_imports_resolver: config_data Arc::new(CliResolver::new(CliResolverOptions {
.and_then(|d| d.sloppy_imports_resolver.clone()), deno_resolver,
npm_resolver: self.npm_resolver().cloned(),
bare_node_builtins_enabled: self
.config_data
.is_some_and(|d| d.unstable.contains("bare-node-builtins")),
})) }))
})
}
pub fn pkg_json_resolver(&self) -> &Arc<PackageJsonResolver> {
&self.pkg_json_resolver
}
pub fn in_npm_pkg_checker(&self) -> &Arc<dyn InNpmPackageChecker> {
self.services.in_npm_pkg_checker.get_or_init(|| {
crate::npm::create_in_npm_pkg_checker(
match self.services.npm_resolver.as_ref().map(|r| r.as_inner()) {
Some(crate::npm::InnerCliNpmResolverRef::Byonm(_)) | None => {
CreateInNpmPkgCheckerOptions::Byonm
}
Some(crate::npm::InnerCliNpmResolverRef::Managed(m)) => {
CreateInNpmPkgCheckerOptions::Managed(
CliManagedInNpmPkgCheckerCreateOptions {
root_cache_dir_url: m.global_cache_root_url(),
maybe_node_modules_path: m.maybe_node_modules_path(),
},
)
}
},
)
})
}
pub fn node_resolver(&self) -> Option<&Arc<NodeResolver>> {
self
.services
.node_resolver
.get_or_init(|| {
let npm_resolver = self.services.npm_resolver.as_ref()?;
Some(Arc::new(NodeResolver::new(
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(self.fs.clone()),
self.in_npm_pkg_checker().clone(),
npm_resolver.clone().into_npm_pkg_folder_resolver(),
self.pkg_json_resolver.clone(),
)))
})
.as_ref()
}
pub fn npm_pkg_req_resolver(&self) -> Option<&Arc<CliNpmReqResolver>> {
self
.services
.npm_pkg_req_resolver
.get_or_init(|| {
let node_resolver = self.node_resolver()?;
let npm_resolver = self.npm_resolver()?;
Some(Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions {
byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(),
fs: CliDenoResolverFs(self.fs.clone()),
in_npm_pkg_checker: self.in_npm_pkg_checker().clone(),
node_resolver: node_resolver.clone(),
npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(),
})))
})
.as_ref()
}
} }
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
@ -565,6 +764,141 @@ impl std::fmt::Debug for RedirectResolver {
} }
} }
#[derive(Debug)]
pub struct LspIsCjsResolver {
inner: IsCjsResolver,
}
impl Default for LspIsCjsResolver {
fn default() -> Self {
LspIsCjsResolver::new(&Default::default())
}
}
impl LspIsCjsResolver {
pub fn new(cache: &LspCache) -> Self {
#[derive(Debug)]
struct LspInNpmPackageChecker {
global_cache_dir: ModuleSpecifier,
}
impl LspInNpmPackageChecker {
pub fn new(cache: &LspCache) -> Self {
let npm_folder_path = cache.deno_dir().npm_folder_path();
Self {
global_cache_dir: url_from_directory_path(
&canonicalize_path_maybe_not_exists(&npm_folder_path)
.unwrap_or(npm_folder_path),
)
.unwrap_or_else(|_| {
ModuleSpecifier::parse("file:///invalid/").unwrap()
}),
}
}
}
impl InNpmPackageChecker for LspInNpmPackageChecker {
fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
if specifier.scheme() != "file" {
return false;
}
if specifier
.as_str()
.starts_with(self.global_cache_dir.as_str())
{
return true;
}
specifier.as_str().contains("/node_modules/")
}
}
let fs = Arc::new(deno_fs::RealFs);
let pkg_json_resolver = Arc::new(PackageJsonResolver::new(
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
));
LspIsCjsResolver {
inner: IsCjsResolver::new(
Arc::new(LspInNpmPackageChecker::new(cache)),
pkg_json_resolver,
crate::resolver::IsCjsResolverOptions {
detect_cjs: true,
is_node_main: false,
},
),
}
}
pub fn get_maybe_doc_module_kind(
&self,
specifier: &ModuleSpecifier,
maybe_document: Option<&Document>,
) -> NodeModuleKind {
self.get_lsp_referrer_kind(
specifier,
maybe_document.and_then(|d| d.is_script()),
)
}
pub fn get_doc_module_kind(&self, document: &Document) -> NodeModuleKind {
self.get_lsp_referrer_kind(document.specifier(), document.is_script())
}
pub fn get_lsp_referrer_kind(
&self,
specifier: &ModuleSpecifier,
is_script: Option<bool>,
) -> NodeModuleKind {
self.inner.get_lsp_referrer_kind(specifier, is_script)
}
}
#[derive(Debug)]
pub struct SingleReferrerGraphResolver<'a> {
pub valid_referrer: &'a ModuleSpecifier,
pub referrer_kind: NodeModuleKind,
pub cli_resolver: &'a CliResolver,
pub jsx_import_source_config: Option<&'a JsxImportSourceConfig>,
}
impl<'a> deno_graph::source::Resolver for SingleReferrerGraphResolver<'a> {
fn default_jsx_import_source(&self) -> Option<String> {
self
.jsx_import_source_config
.and_then(|c| c.default_specifier.clone())
}
fn default_jsx_import_source_types(&self) -> Option<String> {
self
.jsx_import_source_config
.and_then(|c| c.default_types_specifier.clone())
}
fn jsx_import_source_module(&self) -> &str {
self
.jsx_import_source_config
.map(|c| c.module.as_str())
.unwrap_or(deno_graph::source::DEFAULT_JSX_IMPORT_SOURCE_MODULE)
}
fn resolve(
&self,
specifier_text: &str,
referrer_range: &Range,
mode: ResolutionMode,
) -> Result<ModuleSpecifier, deno_graph::source::ResolveError> {
// this resolver assumes it will only be used with a single referrer
// with the provided referrer kind
debug_assert_eq!(referrer_range.specifier, *self.valid_referrer);
self.cli_resolver.resolve(
specifier_text,
referrer_range,
self.referrer_kind,
mode,
)
}
}
impl RedirectResolver { impl RedirectResolver {
fn new( fn new(
cache: Arc<dyn HttpCache>, cache: Arc<dyn HttpCache>,

View file

@ -650,7 +650,7 @@ pub mod tests {
.unwrap(); .unwrap();
let text_info = parsed_module.text_info_lazy().clone(); let text_info = parsed_module.text_info_lazy().clone();
let mut collector = TestCollector::new(specifier, text_info); let mut collector = TestCollector::new(specifier, text_info);
parsed_module.module().visit_with(&mut collector); parsed_module.program().visit_with(&mut collector);
collector.take() collector.take()
} }

View file

@ -34,6 +34,7 @@ use crate::util::path::relative_specifier;
use crate::util::path::to_percent_decoded_str; use crate::util::path::to_percent_decoded_str;
use crate::util::result::InfallibleResultExt; use crate::util::result::InfallibleResultExt;
use crate::util::v8::convert; use crate::util::v8::convert;
use crate::worker::create_isolate_create_params;
use deno_core::convert::Smi; use deno_core::convert::Smi;
use deno_core::convert::ToV8; use deno_core::convert::ToV8;
use deno_core::error::StdAnyError; use deno_core::error::StdAnyError;
@ -62,13 +63,14 @@ use deno_core::ModuleSpecifier;
use deno_core::OpState; use deno_core::OpState;
use deno_core::PollEventLoopOptions; use deno_core::PollEventLoopOptions;
use deno_core::RuntimeOptions; use deno_core::RuntimeOptions;
use deno_runtime::fs_util::specifier_to_file_path; use deno_path_util::url_to_file_path;
use deno_runtime::inspector_server::InspectorServer; use deno_runtime::inspector_server::InspectorServer;
use deno_runtime::tokio_util::create_basic_runtime; use deno_runtime::tokio_util::create_basic_runtime;
use indexmap::IndexMap; use indexmap::IndexMap;
use indexmap::IndexSet; use indexmap::IndexSet;
use lazy_regex::lazy_regex; use lazy_regex::lazy_regex;
use log::error; use log::error;
use node_resolver::NodeModuleKind;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Captures; use regex::Captures;
use regex::Regex; use regex::Regex;
@ -236,7 +238,7 @@ pub struct TsServer {
performance: Arc<Performance>, performance: Arc<Performance>,
sender: mpsc::UnboundedSender<Request>, sender: mpsc::UnboundedSender<Request>,
receiver: Mutex<Option<mpsc::UnboundedReceiver<Request>>>, receiver: Mutex<Option<mpsc::UnboundedReceiver<Request>>>,
specifier_map: Arc<TscSpecifierMap>, pub specifier_map: Arc<TscSpecifierMap>,
inspector_server: Mutex<Option<Arc<InspectorServer>>>, inspector_server: Mutex<Option<Arc<InspectorServer>>>,
pending_change: Mutex<Option<PendingChange>>, pending_change: Mutex<Option<PendingChange>>,
} }
@ -882,20 +884,22 @@ impl TsServer {
options: GetCompletionsAtPositionOptions, options: GetCompletionsAtPositionOptions,
format_code_settings: FormatCodeSettings, format_code_settings: FormatCodeSettings,
scope: Option<ModuleSpecifier>, scope: Option<ModuleSpecifier>,
) -> Option<CompletionInfo> { ) -> Result<Option<CompletionInfo>, AnyError> {
let req = TscRequest::GetCompletionsAtPosition(Box::new(( let req = TscRequest::GetCompletionsAtPosition(Box::new((
self.specifier_map.denormalize(&specifier), self.specifier_map.denormalize(&specifier),
position, position,
options, options,
format_code_settings, format_code_settings,
))); )));
match self.request(snapshot, req, scope).await { self
Ok(maybe_info) => maybe_info, .request::<Option<CompletionInfo>>(snapshot, req, scope)
Err(err) => { .await
log::error!("Unable to get completion info from TypeScript: {:#}", err); .map(|mut info| {
None if let Some(info) = &mut info {
} info.normalize(&self.specifier_map);
} }
info
})
} }
pub async fn get_completion_details( pub async fn get_completion_details(
@ -2182,6 +2186,50 @@ impl NavigateToItem {
} }
} }
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InlayHintDisplayPart {
pub text: String,
pub span: Option<TextSpan>,
pub file: Option<String>,
}
impl InlayHintDisplayPart {
pub fn to_lsp(
&self,
language_server: &language_server::Inner,
) -> lsp::InlayHintLabelPart {
let location = self.file.as_ref().map(|f| {
let specifier =
resolve_url(f).unwrap_or_else(|_| INVALID_SPECIFIER.clone());
let file_referrer =
language_server.documents.get_file_referrer(&specifier);
let uri = language_server
.url_map
.specifier_to_uri(&specifier, file_referrer.as_deref())
.unwrap_or_else(|_| INVALID_URI.clone());
let range = self
.span
.as_ref()
.and_then(|s| {
let asset_or_doc =
language_server.get_asset_or_document(&specifier).ok()?;
Some(s.to_range(asset_or_doc.line_index()))
})
.unwrap_or_else(|| {
lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0))
});
lsp::Location { uri, range }
});
lsp::InlayHintLabelPart {
value: self.text.clone(),
tooltip: None,
location,
command: None,
}
}
}
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
pub enum InlayHintKind { pub enum InlayHintKind {
Type, Type,
@ -2203,6 +2251,7 @@ impl InlayHintKind {
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct InlayHint { pub struct InlayHint {
pub text: String, pub text: String,
pub display_parts: Option<Vec<InlayHintDisplayPart>>,
pub position: u32, pub position: u32,
pub kind: InlayHintKind, pub kind: InlayHintKind,
pub whitespace_before: Option<bool>, pub whitespace_before: Option<bool>,
@ -2210,10 +2259,23 @@ pub struct InlayHint {
} }
impl InlayHint { impl InlayHint {
pub fn to_lsp(&self, line_index: Arc<LineIndex>) -> lsp::InlayHint { pub fn to_lsp(
&self,
line_index: Arc<LineIndex>,
language_server: &language_server::Inner,
) -> lsp::InlayHint {
lsp::InlayHint { lsp::InlayHint {
position: line_index.position_tsc(self.position.into()), position: line_index.position_tsc(self.position.into()),
label: lsp::InlayHintLabel::String(self.text.clone()), label: if let Some(display_parts) = &self.display_parts {
lsp::InlayHintLabel::LabelParts(
display_parts
.iter()
.map(|p| p.to_lsp(language_server))
.collect(),
)
} else {
lsp::InlayHintLabel::String(self.text.clone())
},
kind: self.kind.to_lsp(), kind: self.kind.to_lsp(),
padding_left: self.whitespace_before, padding_left: self.whitespace_before,
padding_right: self.whitespace_after, padding_right: self.whitespace_after,
@ -3191,7 +3253,7 @@ impl CallHierarchyItem {
let use_file_name = self.is_source_file_item(); let use_file_name = self.is_source_file_item();
let maybe_file_path = if uri.scheme().is_some_and(|s| s.as_str() == "file") 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 { } else {
None None
}; };
@ -3584,6 +3646,12 @@ pub struct CompletionInfo {
} }
impl CompletionInfo { impl CompletionInfo {
fn normalize(&mut self, specifier_map: &TscSpecifierMap) {
for entry in &mut self.entries {
entry.normalize(specifier_map);
}
}
pub fn as_completion_response( pub fn as_completion_response(
&self, &self,
line_index: Arc<LineIndex>, line_index: Arc<LineIndex>,
@ -3645,11 +3713,17 @@ pub struct CompletionItemData {
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
struct CompletionEntryDataImport { struct CompletionEntryDataAutoImport {
module_specifier: String, module_specifier: String,
file_name: String, file_name: String,
} }
#[derive(Debug)]
pub struct CompletionNormalizedAutoImportData {
raw: CompletionEntryDataAutoImport,
normalized: ModuleSpecifier,
}
#[derive(Debug, Default, Deserialize, Serialize)] #[derive(Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CompletionEntry { pub struct CompletionEntry {
@ -3682,9 +3756,28 @@ pub struct CompletionEntry {
is_import_statement_completion: Option<bool>, is_import_statement_completion: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
data: Option<Value>, data: Option<Value>,
/// This is not from tsc, we add it for convenience during normalization.
/// Represents `self.data.file_name`, but normalized.
#[serde(skip)]
auto_import_data: Option<CompletionNormalizedAutoImportData>,
} }
impl CompletionEntry { impl CompletionEntry {
fn normalize(&mut self, specifier_map: &TscSpecifierMap) {
let Some(data) = &self.data else {
return;
};
let Ok(raw) =
serde_json::from_value::<CompletionEntryDataAutoImport>(data.clone())
else {
return;
};
if let Ok(normalized) = specifier_map.normalize(&raw.file_name) {
self.auto_import_data =
Some(CompletionNormalizedAutoImportData { raw, normalized });
}
}
fn get_commit_characters( fn get_commit_characters(
&self, &self,
info: &CompletionInfo, info: &CompletionInfo,
@ -3833,27 +3926,26 @@ impl CompletionEntry {
if let Some(source) = &self.source { if let Some(source) = &self.source {
let mut display_source = source.clone(); let mut display_source = source.clone();
if let Some(data) = &self.data { if let Some(import_data) = &self.auto_import_data {
if let Ok(import_data) =
serde_json::from_value::<CompletionEntryDataImport>(data.clone())
{
if let Ok(import_specifier) = resolve_url(&import_data.file_name) {
if let Some(new_module_specifier) = language_server if let Some(new_module_specifier) = language_server
.get_ts_response_import_mapper(specifier) .get_ts_response_import_mapper(specifier)
.check_specifier(&import_specifier, specifier) .check_specifier(&import_data.normalized, specifier)
.or_else(|| relative_specifier(specifier, &import_specifier)) .or_else(|| relative_specifier(specifier, &import_data.normalized))
{ {
if new_module_specifier.contains("/node_modules/") {
return None;
}
display_source.clone_from(&new_module_specifier); display_source.clone_from(&new_module_specifier);
if new_module_specifier != import_data.module_specifier { if new_module_specifier != import_data.raw.module_specifier {
specifier_rewrite = specifier_rewrite = Some((
Some((import_data.module_specifier, new_module_specifier)); import_data.raw.module_specifier.clone(),
new_module_specifier,
));
} }
} else if source.starts_with(jsr_url().as_str()) { } else if source.starts_with(jsr_url().as_str()) {
return None; return None;
} }
} }
}
}
// We want relative or bare (import-mapped or otherwise) specifiers to // We want relative or bare (import-mapped or otherwise) specifiers to
// appear at the top. // appear at the top.
if resolve_url(&display_source).is_err() { if resolve_url(&display_source).is_err() {
@ -3939,7 +4031,7 @@ pub struct OutliningSpan {
kind: OutliningSpanKind, kind: OutliningSpanKind,
} }
const FOLD_END_PAIR_CHARACTERS: &[u8] = &[b'}', b']', b')', b'`']; const FOLD_END_PAIR_CHARACTERS: &[u8] = b"}])`";
impl OutliningSpan { impl OutliningSpan {
pub fn to_folding_range( pub fn to_folding_range(
@ -4154,6 +4246,13 @@ impl TscSpecifierMap {
return specifier.to_string(); return specifier.to_string();
} }
let mut specifier = original.to_string(); let mut specifier = original.to_string();
if specifier.contains("/node_modules/.deno/")
&& !specifier.contains("/node_modules/@types/node/")
{
// The ts server doesn't give completions from files in
// `node_modules/.deno/`. We work around it like this.
specifier = specifier.replace("/node_modules/", "/$node_modules/");
}
let media_type = MediaType::from_specifier(original); let media_type = MediaType::from_specifier(original);
// If the URL-inferred media type doesn't correspond to tsc's path-inferred // If the URL-inferred media type doesn't correspond to tsc's path-inferred
// media type, force it to be the same by appending an extension. // media type, force it to be the same by appending an extension.
@ -4271,7 +4370,7 @@ fn op_is_cancelled(state: &mut OpState) -> bool {
fn op_is_node_file(state: &mut OpState, #[string] path: String) -> bool { fn op_is_node_file(state: &mut OpState, #[string] path: String) -> bool {
let state = state.borrow::<State>(); let state = state.borrow::<State>();
let mark = state.performance.mark("tsc.op.op_is_node_file"); let mark = state.performance.mark("tsc.op.op_is_node_file");
let r = match ModuleSpecifier::parse(&path) { let r = match state.specifier_map.normalize(path) {
Ok(specifier) => state.state_snapshot.resolver.in_node_modules(&specifier), Ok(specifier) => state.state_snapshot.resolver.in_node_modules(&specifier),
Err(_) => false, Err(_) => false,
}; };
@ -4308,10 +4407,11 @@ fn op_load<'s>(
data: doc.text(), data: doc.text(),
script_kind: crate::tsc::as_ts_script_kind(doc.media_type()), script_kind: crate::tsc::as_ts_script_kind(doc.media_type()),
version: state.script_version(&specifier), version: state.script_version(&specifier),
is_cjs: matches!( is_cjs: doc
doc.media_type(), .document()
MediaType::Cjs | MediaType::Cts | MediaType::Dcts .map(|d| state.state_snapshot.is_cjs_resolver.get_doc_module_kind(d))
), .unwrap_or(NodeModuleKind::Esm)
== NodeModuleKind::Cjs,
}) })
}; };
@ -4540,7 +4640,10 @@ fn op_script_names(state: &mut OpState) -> ScriptNames {
for doc in &docs { for doc in &docs {
let specifier = doc.specifier(); let specifier = doc.specifier();
let is_open = doc.is_open(); let is_open = doc.is_open();
if is_open || specifier.scheme() == "file" { if is_open
|| (specifier.scheme() == "file"
&& !state.state_snapshot.resolver.in_node_modules(specifier))
{
let script_names = doc let script_names = doc
.scope() .scope()
.and_then(|s| result.by_scope.get_mut(s)) .and_then(|s| result.by_scope.get_mut(s))
@ -4551,6 +4654,10 @@ fn op_script_names(state: &mut OpState) -> ScriptNames {
let (types, _) = documents.resolve_dependency( let (types, _) = documents.resolve_dependency(
types, types,
specifier, specifier,
state
.state_snapshot
.is_cjs_resolver
.get_doc_module_kind(doc),
doc.file_referrer(), doc.file_referrer(),
)?; )?;
let types_doc = documents.get_or_load(&types, doc.file_referrer())?; let types_doc = documents.get_or_load(&types, doc.file_referrer())?;
@ -4654,6 +4761,7 @@ fn run_tsc_thread(
specifier_map, specifier_map,
request_rx, request_rx,
)], )],
create_params: create_isolate_create_params(),
startup_snapshot: Some(tsc::compiler_snapshot()), startup_snapshot: Some(tsc::compiler_snapshot()),
inspector: has_inspector_server, inspector: has_inspector_server,
..Default::default() ..Default::default()
@ -4892,6 +5000,10 @@ pub struct UserPreferences {
pub allow_rename_of_import_path: Option<bool>, pub allow_rename_of_import_path: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub auto_import_file_exclude_patterns: Option<Vec<String>>, pub auto_import_file_exclude_patterns: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub interactive_inlay_hints: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub prefer_type_only_auto_imports: Option<bool>,
} }
impl UserPreferences { impl UserPreferences {
@ -4909,6 +5021,7 @@ impl UserPreferences {
include_completions_with_snippet_text: Some( include_completions_with_snippet_text: Some(
config.snippet_support_capable(), config.snippet_support_capable(),
), ),
interactive_inlay_hints: Some(true),
provide_refactor_not_applicable_reason: Some(true), provide_refactor_not_applicable_reason: Some(true),
quote_preference: Some(fmt_config.into()), quote_preference: Some(fmt_config.into()),
use_label_details_in_completion_entries: Some(true), use_label_details_in_completion_entries: Some(true),
@ -5013,6 +5126,9 @@ impl UserPreferences {
} else { } else {
Some(language_settings.preferences.quote_style) Some(language_settings.preferences.quote_style)
}, },
prefer_type_only_auto_imports: Some(
language_settings.preferences.prefer_type_only_auto_imports,
),
..base_preferences ..base_preferences
} }
} }
@ -5425,6 +5541,7 @@ mod tests {
documents: Arc::new(documents), documents: Arc::new(documents),
assets: Default::default(), assets: Default::default(),
config: Arc::new(config), config: Arc::new(config),
is_cjs_resolver: Default::default(),
resolver, resolver,
}); });
let performance = Arc::new(Performance::default()); let performance = Arc::new(Performance::default());
@ -5958,6 +6075,7 @@ mod tests {
Some(temp_dir.url()), Some(temp_dir.url()),
) )
.await .await
.unwrap()
.unwrap(); .unwrap();
assert_eq!(info.entries.len(), 22); assert_eq!(info.entries.len(), 22);
let details = ts_server let details = ts_server
@ -6117,6 +6235,7 @@ mod tests {
Some(temp_dir.url()), Some(temp_dir.url()),
) )
.await .await
.unwrap()
.unwrap(); .unwrap();
let entry = info let entry = info
.entries .entries
@ -6154,7 +6273,7 @@ mod tests {
let change = changes.text_changes.first().unwrap(); let change = changes.text_changes.first().unwrap();
assert_eq!( assert_eq!(
change.new_text, change.new_text,
"import type { someLongVariable } from './b.ts'\n" "import { someLongVariable } from './b.ts'\n"
); );
} }

View file

@ -15,7 +15,6 @@ mod js;
mod jsr; mod jsr;
mod lsp; mod lsp;
mod module_loader; mod module_loader;
mod napi;
mod node; mod node;
mod npm; mod npm;
mod ops; mod ops;
@ -37,6 +36,8 @@ use crate::util::v8::get_v8_flags_from_env;
use crate::util::v8::init_v8_flags; use crate::util::v8::init_v8_flags;
use args::TaskFlags; use args::TaskFlags;
use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError;
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
use deno_runtime::WorkerExecutionMode; use deno_runtime::WorkerExecutionMode;
pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS; pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS;
@ -46,8 +47,7 @@ use deno_core::error::JsError;
use deno_core::futures::FutureExt; use deno_core::futures::FutureExt;
use deno_core::unsync::JoinHandle; use deno_core::unsync::JoinHandle;
use deno_npm::resolution::SnapshotFromLockfileError; use deno_npm::resolution::SnapshotFromLockfileError;
use deno_runtime::fmt_errors::format_js_error_with_suggestions; use deno_runtime::fmt_errors::format_js_error;
use deno_runtime::fmt_errors::FixSuggestion;
use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics; use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics;
use deno_terminal::colors; use deno_terminal::colors;
use factory::CliFactory; use factory::CliFactory;
@ -55,10 +55,15 @@ use standalone::MODULE_NOT_FOUND;
use standalone::UNSUPPORTED_SCHEME; use standalone::UNSUPPORTED_SCHEME;
use std::env; use std::env;
use std::future::Future; use std::future::Future;
use std::io::IsTerminal;
use std::ops::Deref; use std::ops::Deref;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; 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. /// Ensures that all subcommands return an i32 exit code and an [`AnyError`] error type.
trait SubcommandOutput { trait SubcommandOutput {
fn output(self) -> Result<i32, AnyError>; fn output(self) -> Result<i32, AnyError>;
@ -130,7 +135,7 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
tools::compile::compile(flags, compile_flags).await tools::compile::compile(flags, compile_flags).await
}), }),
DenoSubcommand::Coverage(coverage_flags) => spawn_subcommand(async { DenoSubcommand::Coverage(coverage_flags) => spawn_subcommand(async {
tools::coverage::cover_files(flags, coverage_flags).await tools::coverage::cover_files(flags, coverage_flags)
}), }),
DenoSubcommand::Fmt(fmt_flags) => { DenoSubcommand::Fmt(fmt_flags) => {
spawn_subcommand( spawn_subcommand(
@ -159,7 +164,19 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
DenoSubcommand::Uninstall(uninstall_flags) => spawn_subcommand(async { DenoSubcommand::Uninstall(uninstall_flags) => spawn_subcommand(async {
tools::installer::uninstall(flags, uninstall_flags).await tools::installer::uninstall(flags, uninstall_flags).await
}), }),
DenoSubcommand::Lsp => spawn_subcommand(async { lsp::start().await }), DenoSubcommand::Lsp => spawn_subcommand(async {
if std::io::stderr().is_terminal() {
log::warn!(
"{} command is intended to be run by text editors and IDEs and shouldn't be run manually.
Visit https://docs.deno.com/runtime/getting_started/setup_your_environment/ for instruction
how to setup your favorite text editor.
Press Ctrl+C to exit.
", colors::cyan("deno lsp"));
}
lsp::start().await
}),
DenoSubcommand::Lint(lint_flags) => spawn_subcommand(async { DenoSubcommand::Lint(lint_flags) => spawn_subcommand(async {
if lint_flags.rules { if lint_flags.rules {
tools::lint::print_rules_list( tools::lint::print_rules_list(
@ -182,6 +199,21 @@ async fn run_subcommand(flags: Arc<Flags>) -> Result<i32, AnyError> {
match result { match result {
Ok(v) => Ok(v), Ok(v) => Ok(v),
Err(script_err) => { 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(); 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 script_err_msg.starts_with(MODULE_NOT_FOUND) || script_err_msg.starts_with(UNSUPPORTED_SCHEME) {
if run_flags.bare { if run_flags.bare {
@ -318,84 +350,17 @@ fn setup_panic_hook() {
eprintln!("Args: {:?}", env::args().collect::<Vec<_>>()); eprintln!("Args: {:?}", env::args().collect::<Vec<_>>());
eprintln!(); eprintln!();
orig_hook(panic_info); orig_hook(panic_info);
std::process::exit(1); deno_runtime::exit(1);
})); }));
} }
#[allow(clippy::print_stderr)]
fn exit_with_message(message: &str, code: i32) -> ! { fn exit_with_message(message: &str, code: i32) -> ! {
eprintln!( log::error!(
"{}: {}", "{}: {}",
colors::red_bold("error"), colors::red_bold("error"),
message.trim_start_matches("error: ") message.trim_start_matches("error: ")
); );
std::process::exit(code); deno_runtime::exit(code);
}
fn get_suggestions_for_terminal_errors(e: &JsError) -> Vec<FixSuggestion> {
if let Some(msg) = &e.message {
if msg.contains("module is not defined")
|| msg.contains("exports is not defined")
{
return vec![
FixSuggestion::info(
"Deno does not support CommonJS modules without `.cjs` extension.",
),
FixSuggestion::hint(
"Rewrite this module to ESM or change the file extension to `.cjs`.",
),
];
} else if msg.contains("openKv is not a function") {
return vec![
FixSuggestion::info("Deno.openKv() is an unstable API."),
FixSuggestion::hint(
"Run again with `--unstable-kv` flag to enable this API.",
),
];
} else if msg.contains("cron is not a function") {
return vec![
FixSuggestion::info("Deno.cron() is an unstable API."),
FixSuggestion::hint(
"Run again with `--unstable-cron` flag to enable this API.",
),
];
} else if msg.contains("WebSocketStream is not defined") {
return vec![
FixSuggestion::info("new WebSocketStream() is an unstable API."),
FixSuggestion::hint(
"Run again with `--unstable-net` flag to enable this API.",
),
];
} else if msg.contains("Temporal is not defined") {
return vec![
FixSuggestion::info("Temporal is an unstable API."),
FixSuggestion::hint(
"Run again with `--unstable-temporal` flag to enable this API.",
),
];
} else if msg.contains("BroadcastChannel is not defined") {
return vec![
FixSuggestion::info("BroadcastChannel is an unstable API."),
FixSuggestion::hint(
"Run again with `--unstable-broadcast-channel` flag to enable this API.",
),
];
} else if msg.contains("window is not defined") {
return vec![
FixSuggestion::info("window global is not available in Deno 2."),
FixSuggestion::hint("Replace `window` with `globalThis`."),
];
} else if msg.contains("UnsafeWindowSurface is not a constructor") {
return vec![
FixSuggestion::info("Deno.UnsafeWindowSurface is an unstable API."),
FixSuggestion::hint(
"Run again with `--unstable-webgpu` flag to enable this API.",
),
];
}
}
vec![]
} }
fn exit_for_error(error: AnyError) -> ! { fn exit_for_error(error: AnyError) -> ! {
@ -403,8 +368,7 @@ fn exit_for_error(error: AnyError) -> ! {
let mut error_code = 1; let mut error_code = 1;
if let Some(e) = error.downcast_ref::<JsError>() { if let Some(e) = error.downcast_ref::<JsError>() {
let suggestions = get_suggestions_for_terminal_errors(e); error_string = format_js_error(e);
error_string = format_js_error_with_suggestions(e, suggestions);
} else if let Some(SnapshotFromLockfileError::IntegrityCheckFailed(e)) = } else if let Some(SnapshotFromLockfileError::IntegrityCheckFailed(e)) =
error.downcast_ref::<SnapshotFromLockfileError>() error.downcast_ref::<SnapshotFromLockfileError>()
{ {
@ -415,16 +379,18 @@ fn exit_for_error(error: AnyError) -> ! {
exit_with_message(&error_string, error_code); exit_with_message(&error_string, error_code);
} }
#[allow(clippy::print_stderr)]
pub(crate) fn unstable_exit_cb(feature: &str, api_name: &str) { pub(crate) fn unstable_exit_cb(feature: &str, api_name: &str) {
eprintln!( log::error!(
"Unstable API '{api_name}'. The `--unstable-{}` flag must be provided.", "Unstable API '{api_name}'. The `--unstable-{}` flag must be provided.",
feature feature
); );
std::process::exit(70); deno_runtime::exit(70);
} }
pub fn main() { pub fn main() {
#[cfg(feature = "dhat-heap")]
let profiler = dhat::Profiler::new_heap();
setup_panic_hook(); setup_panic_hook();
util::unix::raise_fd_limit(); util::unix::raise_fd_limit();
@ -445,8 +411,13 @@ pub fn main() {
run_subcommand(Arc::new(flags)).await 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);
Ok(exit_code) => std::process::exit(exit_code),
#[cfg(feature = "dhat-heap")]
drop(profiler);
match result {
Ok(exit_code) => deno_runtime::exit(exit_code),
Err(err) => exit_for_error(err), Err(err) => exit_for_error(err),
} }
} }
@ -460,12 +431,21 @@ fn resolve_flags_and_init(
if err.kind() == clap::error::ErrorKind::DisplayVersion => if err.kind() == clap::error::ErrorKind::DisplayVersion =>
{ {
// Ignore results to avoid BrokenPipe errors. // Ignore results to avoid BrokenPipe errors.
util::logger::init(None);
let _ = err.print(); let _ = err.print();
std::process::exit(0); deno_runtime::exit(0);
}
Err(err) => {
util::logger::init(None);
exit_for_error(AnyError::from(err))
} }
Err(err) => exit_for_error(AnyError::from(err)),
}; };
if let Some(otel_config) = flags.otel_config() {
deno_runtime::ops::otel::init(otel_config)?;
}
util::logger::init(flags.log_level);
// TODO(bartlomieju): remove in Deno v2.5 and hard error then. // TODO(bartlomieju): remove in Deno v2.5 and hard error then.
if flags.unstable_config.legacy_flag_enabled { if flags.unstable_config.legacy_flag_enabled {
log::warn!( log::warn!(
@ -494,7 +474,6 @@ fn resolve_flags_and_init(
deno_core::JsRuntime::init_platform( deno_core::JsRuntime::init_platform(
None, /* import assertions enabled */ false, None, /* import assertions enabled */ false,
); );
util::logger::init(flags.log_level);
Ok(flags) Ok(flags)
} }

View file

@ -40,23 +40,21 @@ use std::env::current_exe;
use crate::args::Flags; use crate::args::Flags;
#[allow(clippy::print_stderr)]
pub(crate) fn unstable_exit_cb(feature: &str, api_name: &str) { pub(crate) fn unstable_exit_cb(feature: &str, api_name: &str) {
eprintln!( log::error!(
"Unstable API '{api_name}'. The `--unstable-{}` flag must be provided.", "Unstable API '{api_name}'. The `--unstable-{}` flag must be provided.",
feature feature
); );
std::process::exit(70); deno_runtime::exit(70);
} }
#[allow(clippy::print_stderr)]
fn exit_with_message(message: &str, code: i32) -> ! { fn exit_with_message(message: &str, code: i32) -> ! {
eprintln!( log::error!(
"{}: {}", "{}: {}",
colors::red_bold("error"), colors::red_bold("error"),
message.trim_start_matches("error: ") message.trim_start_matches("error: ")
); );
std::process::exit(code); deno_runtime::exit(code);
} }
fn unwrap_or_exit<T>(result: Result<T, AnyError>) -> T { fn unwrap_or_exit<T>(result: Result<T, AnyError>) -> T {
@ -88,15 +86,20 @@ fn main() {
let standalone = standalone::extract_standalone(Cow::Owned(args)); let standalone = standalone::extract_standalone(Cow::Owned(args));
let future = async move { let future = async move {
match standalone { match standalone {
Ok(Some(future)) => { Ok(Some(data)) => {
let (metadata, eszip) = future.await?; if let Some(otel_config) = data.metadata.otel_config.clone() {
util::logger::init(metadata.log_level); deno_runtime::ops::otel::init(otel_config)?;
load_env_vars(&metadata.env_vars_from_env_file); }
let exit_code = standalone::run(eszip, metadata).await?; util::logger::init(data.metadata.log_level);
std::process::exit(exit_code); load_env_vars(&data.metadata.env_vars_from_env_file);
let exit_code = standalone::run(data).await?;
deno_runtime::exit(exit_code);
} }
Ok(None) => Ok(()), Ok(None) => Ok(()),
Err(err) => Err(err), Err(err) => {
util::logger::init(None);
Err(err)
}
} }
}; };

View file

@ -2,6 +2,7 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::RefCell; use std::cell::RefCell;
use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::pin::Pin; use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
@ -23,18 +24,23 @@ use crate::graph_container::ModuleGraphUpdatePermit;
use crate::graph_util::CreateGraphOptions; use crate::graph_util::CreateGraphOptions;
use crate::graph_util::ModuleGraphBuilder; use crate::graph_util::ModuleGraphBuilder;
use crate::node; use crate::node;
use crate::resolver::CliGraphResolver; use crate::node::CliNodeCodeTranslator;
use crate::resolver::CliNodeResolver; use crate::npm::CliNpmResolver;
use crate::resolver::CjsTracker;
use crate::resolver::CliNpmReqResolver;
use crate::resolver::CliResolver;
use crate::resolver::ModuleCodeStringSource; use crate::resolver::ModuleCodeStringSource;
use crate::resolver::NotSupportedKindInNpmError;
use crate::resolver::NpmModuleLoader; use crate::resolver::NpmModuleLoader;
use crate::tools::check; use crate::tools::check;
use crate::tools::check::TypeChecker; use crate::tools::check::TypeChecker;
use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBar;
use crate::util::text_encoding::code_without_source_map; use crate::util::text_encoding::code_without_source_map;
use crate::util::text_encoding::source_map_from_code; use crate::util::text_encoding::source_map_from_code;
use crate::worker::ModuleLoaderAndSourceMapGetter; use crate::worker::CreateModuleLoaderResult;
use crate::worker::ModuleLoaderFactory; use crate::worker::ModuleLoaderFactory;
use deno_ast::MediaType; use deno_ast::MediaType;
use deno_ast::ModuleKind;
use deno_core::anyhow::anyhow; use deno_core::anyhow::anyhow;
use deno_core::anyhow::bail; use deno_core::anyhow::bail;
use deno_core::anyhow::Context; use deno_core::anyhow::Context;
@ -54,7 +60,6 @@ use deno_core::RequestedModuleType;
use deno_core::ResolutionKind; use deno_core::ResolutionKind;
use deno_core::SourceCodeCacheInfo; use deno_core::SourceCodeCacheInfo;
use deno_graph::source::ResolutionMode; use deno_graph::source::ResolutionMode;
use deno_graph::source::Resolver;
use deno_graph::GraphKind; use deno_graph::GraphKind;
use deno_graph::JsModule; use deno_graph::JsModule;
use deno_graph::JsonModule; use deno_graph::JsonModule;
@ -62,9 +67,14 @@ use deno_graph::Module;
use deno_graph::ModuleGraph; use deno_graph::ModuleGraph;
use deno_graph::Resolution; use deno_graph::Resolution;
use deno_runtime::code_cache; use deno_runtime::code_cache;
use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::create_host_defined_options; use deno_runtime::deno_node::create_host_defined_options;
use deno_runtime::deno_node::NodeRequireLoader;
use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
use node_resolver::errors::ClosestPkgJsonError;
use node_resolver::InNpmPackageChecker;
use node_resolver::NodeResolutionMode; use node_resolver::NodeResolutionMode;
pub struct ModuleLoadPreparer { pub struct ModuleLoadPreparer {
@ -198,14 +208,20 @@ struct SharedCliModuleLoaderState {
initial_cwd: PathBuf, initial_cwd: PathBuf,
is_inspecting: bool, is_inspecting: bool,
is_repl: bool, is_repl: bool,
cjs_tracker: Arc<CjsTracker>,
code_cache: Option<Arc<CodeCache>>, code_cache: Option<Arc<CodeCache>>,
emitter: Arc<Emitter>, emitter: Arc<Emitter>,
fs: Arc<dyn FileSystem>,
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
main_module_graph_container: Arc<MainModuleGraphContainer>, main_module_graph_container: Arc<MainModuleGraphContainer>,
module_load_preparer: Arc<ModuleLoadPreparer>, module_load_preparer: Arc<ModuleLoadPreparer>,
node_resolver: Arc<CliNodeResolver>, node_code_translator: Arc<CliNodeCodeTranslator>,
node_resolver: Arc<NodeResolver>,
npm_req_resolver: Arc<CliNpmReqResolver>,
npm_resolver: Arc<dyn CliNpmResolver>,
npm_module_loader: NpmModuleLoader, npm_module_loader: NpmModuleLoader,
parsed_source_cache: Arc<ParsedSourceCache>, parsed_source_cache: Arc<ParsedSourceCache>,
resolver: Arc<CliGraphResolver>, resolver: Arc<CliResolver>,
} }
pub struct CliModuleLoaderFactory { pub struct CliModuleLoaderFactory {
@ -216,14 +232,20 @@ impl CliModuleLoaderFactory {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
options: &CliOptions, options: &CliOptions,
cjs_tracker: Arc<CjsTracker>,
code_cache: Option<Arc<CodeCache>>, code_cache: Option<Arc<CodeCache>>,
emitter: Arc<Emitter>, emitter: Arc<Emitter>,
fs: Arc<dyn FileSystem>,
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
main_module_graph_container: Arc<MainModuleGraphContainer>, main_module_graph_container: Arc<MainModuleGraphContainer>,
module_load_preparer: Arc<ModuleLoadPreparer>, module_load_preparer: Arc<ModuleLoadPreparer>,
node_resolver: Arc<CliNodeResolver>, node_code_translator: Arc<CliNodeCodeTranslator>,
node_resolver: Arc<NodeResolver>,
npm_req_resolver: Arc<CliNpmReqResolver>,
npm_resolver: Arc<dyn CliNpmResolver>,
npm_module_loader: NpmModuleLoader, npm_module_loader: NpmModuleLoader,
parsed_source_cache: Arc<ParsedSourceCache>, parsed_source_cache: Arc<ParsedSourceCache>,
resolver: Arc<CliGraphResolver>, resolver: Arc<CliResolver>,
) -> Self { ) -> Self {
Self { Self {
shared: Arc::new(SharedCliModuleLoaderState { shared: Arc::new(SharedCliModuleLoaderState {
@ -236,11 +258,17 @@ impl CliModuleLoaderFactory {
options.sub_command(), options.sub_command(),
DenoSubcommand::Repl(_) | DenoSubcommand::Jupyter(_) DenoSubcommand::Repl(_) | DenoSubcommand::Jupyter(_)
), ),
cjs_tracker,
code_cache, code_cache,
emitter, emitter,
fs,
in_npm_pkg_checker,
main_module_graph_container, main_module_graph_container,
module_load_preparer, module_load_preparer,
node_code_translator,
node_resolver, node_resolver,
npm_req_resolver,
npm_resolver,
npm_module_loader, npm_module_loader,
parsed_source_cache, parsed_source_cache,
resolver, resolver,
@ -255,19 +283,30 @@ impl CliModuleLoaderFactory {
is_worker: bool, is_worker: bool,
parent_permissions: PermissionsContainer, parent_permissions: PermissionsContainer,
permissions: PermissionsContainer, permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter { ) -> CreateModuleLoaderResult {
let loader = Rc::new(CliModuleLoader(Rc::new(CliModuleLoaderInner { let module_loader =
Rc::new(CliModuleLoader(Rc::new(CliModuleLoaderInner {
lib, lib,
is_worker, is_worker,
parent_permissions, parent_permissions,
permissions, permissions,
graph_container, graph_container: graph_container.clone(),
node_code_translator: self.shared.node_code_translator.clone(),
emitter: self.shared.emitter.clone(), emitter: self.shared.emitter.clone(),
parsed_source_cache: self.shared.parsed_source_cache.clone(), parsed_source_cache: self.shared.parsed_source_cache.clone(),
shared: self.shared.clone(), shared: self.shared.clone(),
}))); })));
ModuleLoaderAndSourceMapGetter { let node_require_loader = Rc::new(CliNodeRequireLoader {
module_loader: loader, cjs_tracker: self.shared.cjs_tracker.clone(),
emitter: self.shared.emitter.clone(),
fs: self.shared.fs.clone(),
graph_container,
in_npm_pkg_checker: self.shared.in_npm_pkg_checker.clone(),
npm_resolver: self.shared.npm_resolver.clone(),
});
CreateModuleLoaderResult {
module_loader,
node_require_loader,
} }
} }
} }
@ -276,7 +315,7 @@ impl ModuleLoaderFactory for CliModuleLoaderFactory {
fn create_for_main( fn create_for_main(
&self, &self,
root_permissions: PermissionsContainer, root_permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter { ) -> CreateModuleLoaderResult {
self.create_with_lib( self.create_with_lib(
(*self.shared.main_module_graph_container).clone(), (*self.shared.main_module_graph_container).clone(),
self.shared.lib_window, self.shared.lib_window,
@ -290,7 +329,7 @@ impl ModuleLoaderFactory for CliModuleLoaderFactory {
&self, &self,
parent_permissions: PermissionsContainer, parent_permissions: PermissionsContainer,
permissions: PermissionsContainer, permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter { ) -> CreateModuleLoaderResult {
self.create_with_lib( self.create_with_lib(
// create a fresh module graph for the worker // create a fresh module graph for the worker
WorkerModuleGraphContainer::new(Arc::new(ModuleGraph::new( WorkerModuleGraphContainer::new(Arc::new(ModuleGraph::new(
@ -314,6 +353,7 @@ struct CliModuleLoaderInner<TGraphContainer: ModuleGraphContainer> {
permissions: PermissionsContainer, permissions: PermissionsContainer,
shared: Arc<SharedCliModuleLoaderState>, shared: Arc<SharedCliModuleLoaderState>,
emitter: Arc<Emitter>, emitter: Arc<Emitter>,
node_code_translator: Arc<CliNodeCodeTranslator>,
parsed_source_cache: Arc<ParsedSourceCache>, parsed_source_cache: Arc<ParsedSourceCache>,
graph_container: TGraphContainer, graph_container: TGraphContainer,
} }
@ -327,16 +367,7 @@ impl<TGraphContainer: ModuleGraphContainer>
maybe_referrer: Option<&ModuleSpecifier>, maybe_referrer: Option<&ModuleSpecifier>,
requested_module_type: RequestedModuleType, requested_module_type: RequestedModuleType,
) -> Result<ModuleSource, AnyError> { ) -> Result<ModuleSource, AnyError> {
let code_source = if let Some(result) = self let code_source = self.load_code_source(specifier, maybe_referrer).await?;
.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 = if self.shared.is_inspecting { let code = if self.shared.is_inspecting {
// we need the code with the source map in order for // we need the code with the source map in order for
// it to work with --inspect or --inspect-brk // it to work with --inspect or --inspect-brk
@ -390,6 +421,29 @@ impl<TGraphContainer: ModuleGraphContainer>
)) ))
} }
async fn load_code_source(
&self,
specifier: &ModuleSpecifier,
maybe_referrer: Option<&ModuleSpecifier>,
) -> Result<ModuleCodeStringSource, AnyError> {
if let Some(code_source) = self.load_prepared_module(specifier).await? {
return Ok(code_source);
}
if self.shared.in_npm_pkg_checker.in_npm_package(specifier) {
return self
.shared
.npm_module_loader
.load(specifier, maybe_referrer)
.await;
}
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))
}
fn resolve_referrer( fn resolve_referrer(
&self, &self,
referrer: &str, referrer: &str,
@ -397,7 +451,7 @@ impl<TGraphContainer: ModuleGraphContainer>
let referrer = if referrer.is_empty() && self.shared.is_repl { let referrer = if referrer.is_empty() && self.shared.is_repl {
// FIXME(bartlomieju): this is a hacky way to provide compatibility with REPL // FIXME(bartlomieju): this is a hacky way to provide compatibility with REPL
// and `Deno.core.evalContext` API. Ideally we should always have a referrer filled // and `Deno.core.evalContext` API. Ideally we should always have a referrer filled
"./$deno$repl.ts" "./$deno$repl.mts"
} else { } else {
referrer referrer
}; };
@ -420,12 +474,17 @@ impl<TGraphContainer: ModuleGraphContainer>
raw_specifier: &str, raw_specifier: &str,
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
) -> Result<ModuleSpecifier, AnyError> { ) -> Result<ModuleSpecifier, AnyError> {
if self.shared.node_resolver.in_npm_package(referrer) { if self.shared.in_npm_pkg_checker.in_npm_package(referrer) {
return Ok( return Ok(
self self
.shared .shared
.node_resolver .node_resolver
.resolve(raw_specifier, referrer, NodeResolutionMode::Execution)? .resolve(
raw_specifier,
referrer,
self.shared.cjs_tracker.get_referrer_kind(referrer),
NodeResolutionMode::Execution,
)?
.into_url(), .into_url(),
); );
} }
@ -455,6 +514,7 @@ impl<TGraphContainer: ModuleGraphContainer>
start: deno_graph::Position::zeroed(), start: deno_graph::Position::zeroed(),
end: deno_graph::Position::zeroed(), end: deno_graph::Position::zeroed(),
}, },
self.shared.cjs_tracker.get_referrer_kind(referrer),
ResolutionMode::Execution, ResolutionMode::Execution,
)?), )?),
}; };
@ -464,13 +524,14 @@ impl<TGraphContainer: ModuleGraphContainer>
{ {
return self return self
.shared .shared
.node_resolver .npm_req_resolver
.resolve_req_reference( .resolve_req_reference(
&reference, &reference,
referrer, referrer,
self.shared.cjs_tracker.get_referrer_kind(referrer),
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
) )
.map(|res| res.into_url()); .map_err(AnyError::from);
} }
} }
@ -478,7 +539,6 @@ impl<TGraphContainer: ModuleGraphContainer>
Some(Module::Npm(module)) => { Some(Module::Npm(module)) => {
let package_folder = self let package_folder = self
.shared .shared
.node_resolver
.npm_resolver .npm_resolver
.as_managed() .as_managed()
.unwrap() // byonm won't create a Module::Npm .unwrap() // byonm won't create a Module::Npm
@ -486,22 +546,25 @@ impl<TGraphContainer: ModuleGraphContainer>
self self
.shared .shared
.node_resolver .node_resolver
.resolve_package_sub_path_from_deno_module( .resolve_package_subpath_from_deno_module(
&package_folder, &package_folder,
module.nv_reference.sub_path(), module.nv_reference.sub_path(),
Some(referrer), Some(referrer),
self.shared.cjs_tracker.get_referrer_kind(referrer),
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
) )
.with_context(|| { .with_context(|| {
format!("Could not resolve '{}'.", module.nv_reference) format!("Could not resolve '{}'.", module.nv_reference)
})? })?
.into_url()
} }
Some(Module::Node(module)) => module.specifier.clone(), Some(Module::Node(module)) => module.specifier.clone(),
Some(Module::Js(module)) => module.specifier.clone(), Some(Module::Js(module)) => module.specifier.clone(),
Some(Module::Json(module)) => module.specifier.clone(), Some(Module::Json(module)) => module.specifier.clone(),
Some(Module::External(module)) => { Some(Module::External(module)) => {
node::resolve_specifier_into_node_modules(&module.specifier) node::resolve_specifier_into_node_modules(
&module.specifier,
self.shared.fs.as_ref(),
)
} }
None => specifier.into_owned(), None => specifier.into_owned(),
}; };
@ -511,71 +574,82 @@ impl<TGraphContainer: ModuleGraphContainer>
async fn load_prepared_module( async fn load_prepared_module(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
maybe_referrer: Option<&ModuleSpecifier>, ) -> Result<Option<ModuleCodeStringSource>, AnyError> {
) -> Result<ModuleCodeStringSource, AnyError> {
// Note: keep this in sync with the sync version below // Note: keep this in sync with the sync version below
let graph = self.graph_container.graph(); let graph = self.graph_container.graph();
match self.load_prepared_module_or_defer_emit( match self.load_prepared_module_or_defer_emit(&graph, specifier)? {
&graph, Some(CodeOrDeferredEmit::Code(code_source)) => Ok(Some(code_source)),
specifier, Some(CodeOrDeferredEmit::DeferredEmit {
maybe_referrer,
) {
Ok(CodeOrDeferredEmit::Code(code_source)) => Ok(code_source),
Ok(CodeOrDeferredEmit::DeferredEmit {
specifier, specifier,
media_type, media_type,
source, source,
}) => { }) => {
let transpile_result = self let transpile_result = self
.emitter .emitter
.emit_parsed_source(specifier, media_type, source) .emit_parsed_source(specifier, media_type, ModuleKind::Esm, source)
.await?; .await?;
// at this point, we no longer need the parsed source in memory, so free it // at this point, we no longer need the parsed source in memory, so free it
self.parsed_source_cache.free(specifier); self.parsed_source_cache.free(specifier);
Ok(ModuleCodeStringSource { Ok(Some(ModuleCodeStringSource {
code: ModuleSourceCode::Bytes(transpile_result), // note: it's faster to provide a string if we know it's a string
code: ModuleSourceCode::String(transpile_result.into()),
found_url: specifier.clone(), found_url: specifier.clone(),
media_type, media_type,
}) }))
} }
Err(err) => Err(err), Some(CodeOrDeferredEmit::Cjs {
specifier,
media_type,
source,
}) => self
.load_maybe_cjs(specifier, media_type, source)
.await
.map(Some),
None => Ok(None),
} }
} }
fn load_prepared_module_sync( fn load_prepared_module_for_source_map_sync(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
maybe_referrer: Option<&ModuleSpecifier>, ) -> Result<Option<ModuleCodeStringSource>, AnyError> {
) -> Result<ModuleCodeStringSource, AnyError> {
// Note: keep this in sync with the async version above // Note: keep this in sync with the async version above
let graph = self.graph_container.graph(); let graph = self.graph_container.graph();
match self.load_prepared_module_or_defer_emit( match self.load_prepared_module_or_defer_emit(&graph, specifier)? {
&graph, Some(CodeOrDeferredEmit::Code(code_source)) => Ok(Some(code_source)),
specifier, Some(CodeOrDeferredEmit::DeferredEmit {
maybe_referrer,
) {
Ok(CodeOrDeferredEmit::Code(code_source)) => Ok(code_source),
Ok(CodeOrDeferredEmit::DeferredEmit {
specifier, specifier,
media_type, media_type,
source, source,
}) => { }) => {
let transpile_result = self let transpile_result = self.emitter.emit_parsed_source_sync(
.emitter specifier,
.emit_parsed_source_sync(specifier, media_type, source)?; media_type,
ModuleKind::Esm,
source,
)?;
// at this point, we no longer need the parsed source in memory, so free it // at this point, we no longer need the parsed source in memory, so free it
self.parsed_source_cache.free(specifier); self.parsed_source_cache.free(specifier);
Ok(ModuleCodeStringSource { Ok(Some(ModuleCodeStringSource {
code: ModuleSourceCode::Bytes(transpile_result), // note: it's faster to provide a string if we know it's a string
code: ModuleSourceCode::String(transpile_result.into()),
found_url: specifier.clone(), found_url: specifier.clone(),
media_type, media_type,
}) }))
} }
Err(err) => Err(err), Some(CodeOrDeferredEmit::Cjs { .. }) => {
self.parsed_source_cache.free(specifier);
// todo(dsherret): to make this work, we should probably just
// rely on the CJS export cache. At the moment this is hard because
// cjs export analysis is only async
Ok(None)
}
None => Ok(None),
} }
} }
@ -583,8 +657,7 @@ impl<TGraphContainer: ModuleGraphContainer>
&self, &self,
graph: &'graph ModuleGraph, graph: &'graph ModuleGraph,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
maybe_referrer: Option<&ModuleSpecifier>, ) -> Result<Option<CodeOrDeferredEmit<'graph>>, AnyError> {
) -> Result<CodeOrDeferredEmit<'graph>, AnyError> {
if specifier.scheme() == "node" { if specifier.scheme() == "node" {
// Node built-in modules should be handled internally. // Node built-in modules should be handled internally.
unreachable!("Deno bug. {} was misconfigured internally.", specifier); unreachable!("Deno bug. {} was misconfigured internally.", specifier);
@ -596,38 +669,55 @@ impl<TGraphContainer: ModuleGraphContainer>
media_type, media_type,
specifier, specifier,
.. ..
})) => Ok(CodeOrDeferredEmit::Code(ModuleCodeStringSource { })) => Ok(Some(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
code: ModuleSourceCode::String(source.clone().into()), code: ModuleSourceCode::String(source.clone().into()),
found_url: specifier.clone(), found_url: specifier.clone(),
media_type: *media_type, media_type: *media_type,
})), }))),
Some(deno_graph::Module::Js(JsModule { Some(deno_graph::Module::Js(JsModule {
source, source,
media_type, media_type,
specifier, specifier,
is_script,
.. ..
})) => { })) => {
if self.shared.cjs_tracker.is_cjs_with_known_is_script(
specifier,
*media_type,
*is_script,
)? {
return Ok(Some(CodeOrDeferredEmit::Cjs {
specifier,
media_type: *media_type,
source,
}));
}
let code: ModuleCodeString = match media_type { let code: ModuleCodeString = match media_type {
MediaType::JavaScript MediaType::JavaScript
| MediaType::Unknown | MediaType::Unknown
| MediaType::Cjs
| MediaType::Mjs | MediaType::Mjs
| MediaType::Json => source.clone().into(), | MediaType::Json => source.clone().into(),
MediaType::Dts | MediaType::Dcts | MediaType::Dmts => { MediaType::Dts | MediaType::Dcts | MediaType::Dmts => {
Default::default() Default::default()
} }
MediaType::TypeScript MediaType::Cjs | MediaType::Cts => {
| MediaType::Mts return Ok(Some(CodeOrDeferredEmit::Cjs {
| MediaType::Cts
| MediaType::Jsx
| MediaType::Tsx => {
return Ok(CodeOrDeferredEmit::DeferredEmit {
specifier, specifier,
media_type: *media_type, media_type: *media_type,
source, source,
}); }));
} }
MediaType::TsBuildInfo | MediaType::Wasm | MediaType::SourceMap => { MediaType::TypeScript
| MediaType::Mts
| MediaType::Jsx
| MediaType::Tsx => {
return Ok(Some(CodeOrDeferredEmit::DeferredEmit {
specifier,
media_type: *media_type,
source,
}));
}
MediaType::Css | MediaType::Wasm | MediaType::SourceMap => {
panic!("Unexpected media type {media_type} for {specifier}") panic!("Unexpected media type {media_type} for {specifier}")
} }
}; };
@ -635,25 +725,61 @@ impl<TGraphContainer: ModuleGraphContainer>
// at this point, we no longer need the parsed source in memory, so free it // at this point, we no longer need the parsed source in memory, so free it
self.parsed_source_cache.free(specifier); self.parsed_source_cache.free(specifier);
Ok(CodeOrDeferredEmit::Code(ModuleCodeStringSource { Ok(Some(CodeOrDeferredEmit::Code(ModuleCodeStringSource {
code: ModuleSourceCode::String(code), code: ModuleSourceCode::String(code),
found_url: specifier.clone(), found_url: specifier.clone(),
media_type: *media_type, media_type: *media_type,
})) })))
} }
Some( Some(
deno_graph::Module::External(_) deno_graph::Module::External(_)
| deno_graph::Module::Node(_) | deno_graph::Module::Node(_)
| deno_graph::Module::Npm(_), | deno_graph::Module::Npm(_),
) )
| None => { | None => Ok(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))
} }
} }
async fn load_maybe_cjs(
&self,
specifier: &ModuleSpecifier,
media_type: MediaType,
original_source: &Arc<str>,
) -> Result<ModuleCodeStringSource, AnyError> {
let js_source = if media_type.is_emittable() {
Cow::Owned(
self
.emitter
.emit_parsed_source(
specifier,
media_type,
ModuleKind::Cjs,
original_source,
)
.await?,
)
} else {
Cow::Borrowed(original_source.as_ref())
};
let text = self
.node_code_translator
.translate_cjs_to_esm(specifier, Some(js_source))
.await?;
// at this point, we no longer need the parsed source in memory, so free it
self.parsed_source_cache.free(specifier);
Ok(ModuleCodeStringSource {
code: match text {
// perf: if the text is borrowed, that means it didn't make any changes
// to the original source, so we can just provide that instead of cloning
// the borrowed text
Cow::Borrowed(_) => {
ModuleSourceCode::String(original_source.clone().into())
}
Cow::Owned(text) => ModuleSourceCode::String(text.into()),
},
found_url: specifier.clone(),
media_type,
})
} }
} }
@ -664,6 +790,11 @@ enum CodeOrDeferredEmit<'a> {
media_type: MediaType, media_type: MediaType,
source: &'a Arc<str>, source: &'a Arc<str>,
}, },
Cjs {
specifier: &'a ModuleSpecifier,
media_type: MediaType,
source: &'a Arc<str>,
},
} }
// todo(dsherret): this double Rc boxing is not ideal // todo(dsherret): this double Rc boxing is not ideal
@ -705,7 +836,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
name: &str, name: &str,
) -> Option<deno_core::v8::Local<'s, deno_core::v8::Data>> { ) -> Option<deno_core::v8::Local<'s, deno_core::v8::Data>> {
let name = deno_core::ModuleSpecifier::parse(name).ok()?; let name = deno_core::ModuleSpecifier::parse(name).ok()?;
if self.0.shared.node_resolver.in_npm_package(&name) { if self.0.shared.in_npm_pkg_checker.in_npm_package(&name) {
Some(create_host_defined_options(scope)) Some(create_host_defined_options(scope))
} else { } else {
None None
@ -742,7 +873,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
_maybe_referrer: Option<String>, _maybe_referrer: Option<String>,
is_dynamic: bool, is_dynamic: bool,
) -> Pin<Box<dyn Future<Output = Result<(), AnyError>>>> { ) -> Pin<Box<dyn Future<Output = Result<(), AnyError>>>> {
if self.0.shared.node_resolver.in_npm_package(specifier) { if self.0.shared.in_npm_pkg_checker.in_npm_package(specifier) {
return Box::pin(deno_core::futures::future::ready(Ok(()))); return Box::pin(deno_core::futures::future::ready(Ok(())));
} }
@ -825,7 +956,10 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
"wasm" | "file" | "http" | "https" | "data" | "blob" => (), "wasm" | "file" | "http" | "https" | "data" | "blob" => (),
_ => return None, _ => return None,
} }
let source = self.0.load_prepared_module_sync(&specifier, None).ok()?; let source = self
.0
.load_prepared_module_for_source_map_sync(&specifier)
.ok()??;
source_map_from_code(source.code.as_bytes()) source_map_from_code(source.code.as_bytes())
} }
@ -904,3 +1038,68 @@ impl ModuleGraphUpdatePermit for WorkerModuleGraphUpdatePermit {
drop(self.permit); // explicit drop for clarity drop(self.permit); // explicit drop for clarity
} }
} }
#[derive(Debug)]
struct CliNodeRequireLoader<TGraphContainer: ModuleGraphContainer> {
cjs_tracker: Arc<CjsTracker>,
emitter: Arc<Emitter>,
fs: Arc<dyn FileSystem>,
graph_container: TGraphContainer,
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
npm_resolver: Arc<dyn CliNpmResolver>,
}
impl<TGraphContainer: ModuleGraphContainer> NodeRequireLoader
for CliNodeRequireLoader<TGraphContainer>
{
fn ensure_read_permission<'a>(
&self,
permissions: &mut dyn deno_runtime::deno_node::NodePermissions,
path: &'a Path,
) -> Result<std::borrow::Cow<'a, Path>, AnyError> {
if let Ok(url) = deno_path_util::url_from_file_path(path) {
// allow reading if it's in the module graph
if self.graph_container.graph().get(&url).is_some() {
return Ok(std::borrow::Cow::Borrowed(path));
}
}
self.npm_resolver.ensure_read_permission(permissions, path)
}
fn load_text_file_lossy(&self, path: &Path) -> Result<String, AnyError> {
// todo(dsherret): use the preloaded module from the graph if available?
let media_type = MediaType::from_path(path);
let text = self.fs.read_text_file_lossy_sync(path, None)?;
if media_type.is_emittable() {
let specifier = deno_path_util::url_from_file_path(path)?;
if self.in_npm_pkg_checker.in_npm_package(&specifier) {
return Err(
NotSupportedKindInNpmError {
media_type,
specifier,
}
.into(),
);
}
self.emitter.emit_parsed_source_sync(
&specifier,
media_type,
// this is probably not super accurate due to require esm, but probably ok.
// If we find this causes a lot of churn in the emit cache then we should
// investigate how we can make this better
ModuleKind::Cjs,
&text.into(),
)
} else {
Ok(text)
}
}
fn is_maybe_cjs(
&self,
specifier: &ModuleSpecifier,
) -> Result<bool, ClosestPkgJsonError> {
let media_type = MediaType::from_specifier(specifier);
self.cjs_tracker.is_maybe_cjs(specifier, media_type)
}
}

View file

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

View file

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

View file

@ -1,10 +1,12 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::borrow::Cow;
use std::sync::Arc; use std::sync::Arc;
use deno_ast::MediaType; use deno_ast::MediaType;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_graph::ParsedSourceStore;
use deno_runtime::deno_fs; use deno_runtime::deno_fs;
use deno_runtime::deno_node::DenoFsNodeResolverEnv; use deno_runtime::deno_node::DenoFsNodeResolverEnv;
use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis; use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis;
@ -16,8 +18,8 @@ use serde::Serialize;
use crate::cache::CacheDBHash; use crate::cache::CacheDBHash;
use crate::cache::NodeAnalysisCache; use crate::cache::NodeAnalysisCache;
use crate::resolver::CliNodeResolver; use crate::cache::ParsedSourceCache;
use crate::util::fs::canonicalize_path_maybe_not_exists; use crate::resolver::CjsTracker;
pub type CliNodeCodeTranslator = pub type CliNodeCodeTranslator =
NodeCodeTranslator<CliCjsCodeAnalyzer, DenoFsNodeResolverEnv>; NodeCodeTranslator<CliCjsCodeAnalyzer, DenoFsNodeResolverEnv>;
@ -30,15 +32,11 @@ pub type CliNodeCodeTranslator =
/// because the node_modules folder might not exist at that time. /// because the node_modules folder might not exist at that time.
pub fn resolve_specifier_into_node_modules( pub fn resolve_specifier_into_node_modules(
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
fs: &dyn deno_fs::FileSystem,
) -> ModuleSpecifier { ) -> ModuleSpecifier {
specifier node_resolver::resolve_specifier_into_node_modules(specifier, &|path| {
.to_file_path() fs.realpath_sync(path).map_err(|err| err.into_io_error())
.ok() })
// this path might not exist at the time the graph is being created
// because the node_modules folder might not yet exist
.and_then(|path| canonicalize_path_maybe_not_exists(&path).ok())
.and_then(|path| ModuleSpecifier::from_file_path(path).ok())
.unwrap_or_else(|| specifier.clone())
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@ -54,20 +52,23 @@ pub enum CliCjsAnalysis {
pub struct CliCjsCodeAnalyzer { pub struct CliCjsCodeAnalyzer {
cache: NodeAnalysisCache, cache: NodeAnalysisCache,
cjs_tracker: Arc<CjsTracker>,
fs: deno_fs::FileSystemRc, fs: deno_fs::FileSystemRc,
node_resolver: Arc<CliNodeResolver>, parsed_source_cache: Option<Arc<ParsedSourceCache>>,
} }
impl CliCjsCodeAnalyzer { impl CliCjsCodeAnalyzer {
pub fn new( pub fn new(
cache: NodeAnalysisCache, cache: NodeAnalysisCache,
cjs_tracker: Arc<CjsTracker>,
fs: deno_fs::FileSystemRc, fs: deno_fs::FileSystemRc,
node_resolver: Arc<CliNodeResolver>, parsed_source_cache: Option<Arc<ParsedSourceCache>>,
) -> Self { ) -> Self {
Self { Self {
cache, cache,
cjs_tracker,
fs, fs,
node_resolver, parsed_source_cache,
} }
} }
@ -83,7 +84,7 @@ impl CliCjsCodeAnalyzer {
return Ok(analysis); return Ok(analysis);
} }
let mut media_type = MediaType::from_specifier(specifier); let media_type = MediaType::from_specifier(specifier);
if media_type == MediaType::Json { if media_type == MediaType::Json {
return Ok(CliCjsAnalysis::Cjs { return Ok(CliCjsAnalysis::Cjs {
exports: vec![], exports: vec![],
@ -91,54 +92,51 @@ impl CliCjsCodeAnalyzer {
}); });
} }
if media_type == MediaType::JavaScript { let cjs_tracker = self.cjs_tracker.clone();
if let Some(package_json) = let is_maybe_cjs = cjs_tracker.is_maybe_cjs(specifier, media_type)?;
self.node_resolver.get_closest_package_json(specifier)? let analysis = if is_maybe_cjs {
{ let maybe_parsed_source = self
match package_json.typ.as_str() { .parsed_source_cache
"commonjs" => { .as_ref()
media_type = MediaType::Cjs; .and_then(|c| c.remove_parsed_source(specifier));
}
"module" => {
media_type = MediaType::Mjs;
}
_ => {}
}
}
}
let analysis = deno_core::unsync::spawn_blocking({ deno_core::unsync::spawn_blocking({
let specifier = specifier.clone(); let specifier = specifier.clone();
let source: Arc<str> = source.into(); let source: Arc<str> = source.into();
move || -> Result<_, deno_ast::ParseDiagnostic> { move || -> Result<_, AnyError> {
let parsed_source = deno_ast::parse_program(deno_ast::ParseParams { let parsed_source =
maybe_parsed_source.map(Ok).unwrap_or_else(|| {
deno_ast::parse_program(deno_ast::ParseParams {
specifier, specifier,
text: source, text: source,
media_type, media_type,
capture_tokens: true, capture_tokens: true,
scope_analysis: false, scope_analysis: false,
maybe_syntax: None, maybe_syntax: None,
})
})?; })?;
if parsed_source.is_script() { let is_script = parsed_source.compute_is_script();
let is_cjs = cjs_tracker.is_cjs_with_known_is_script(
parsed_source.specifier(),
media_type,
is_script,
)?;
if is_cjs {
let analysis = parsed_source.analyze_cjs(); let analysis = parsed_source.analyze_cjs();
Ok(CliCjsAnalysis::Cjs { Ok(CliCjsAnalysis::Cjs {
exports: analysis.exports, exports: analysis.exports,
reexports: analysis.reexports, 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 { } else {
Ok(CliCjsAnalysis::Esm) Ok(CliCjsAnalysis::Esm)
} }
} }
}) })
.await .await
.unwrap()?; .unwrap()?
} else {
CliCjsAnalysis::Esm
};
self self
.cache .cache
@ -150,11 +148,11 @@ impl CliCjsCodeAnalyzer {
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl CjsCodeAnalyzer for CliCjsCodeAnalyzer { impl CjsCodeAnalyzer for CliCjsCodeAnalyzer {
async fn analyze_cjs( async fn analyze_cjs<'a>(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
source: Option<String>, source: Option<Cow<'a, str>>,
) -> Result<ExtNodeCjsAnalysis, AnyError> { ) -> Result<ExtNodeCjsAnalysis<'a>, AnyError> {
let source = match source { let source = match source {
Some(source) => source, Some(source) => source,
None => { None => {
@ -162,7 +160,7 @@ impl CjsCodeAnalyzer for CliCjsCodeAnalyzer {
if let Ok(source_from_file) = if let Ok(source_from_file) =
self.fs.read_text_file_lossy_async(path, None).await self.fs.read_text_file_lossy_async(path, None).await
{ {
source_from_file Cow::Owned(source_from_file)
} else { } else {
return Ok(ExtNodeCjsAnalysis::Cjs(CjsAnalysisExports { return Ok(ExtNodeCjsAnalysis::Cjs(CjsAnalysisExports {
exports: vec![], exports: vec![],

View file

@ -2,393 +2,92 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::path::Path; use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use deno_ast::ModuleSpecifier;
use deno_core::anyhow::bail;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::serde_json; use deno_core::serde_json;
use deno_package_json::PackageJsonDepValue; use deno_resolver::npm::ByonmNpmResolver;
use deno_runtime::deno_fs::FileSystem; use deno_resolver::npm::ByonmNpmResolverCreateOptions;
use deno_runtime::deno_node::DenoPkgJsonFsAdapter; use deno_resolver::npm::CliNpmReqResolver;
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NodeRequireResolver;
use deno_runtime::deno_node::PackageJson;
use deno_runtime::fs_util::specifier_to_file_path;
use deno_runtime::ops::process::NpmProcessStateProvider; use deno_runtime::ops::process::NpmProcessStateProvider;
use deno_semver::package::PackageReq; use node_resolver::NpmPackageFolderResolver;
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::NpmProcessState;
use crate::args::NpmProcessStateKind; use crate::args::NpmProcessStateKind;
use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs; use crate::resolver::CliDenoResolverFs;
use super::managed::normalize_pkg_name_for_node_modules_deno_folder;
use super::CliNpmResolver; use super::CliNpmResolver;
use super::InnerCliNpmResolverRef; use super::InnerCliNpmResolverRef;
pub struct CliNpmResolverByonmCreateOptions { pub type CliByonmNpmResolverCreateOptions =
pub fs: Arc<dyn FileSystem>, ByonmNpmResolverCreateOptions<CliDenoResolverFs, DenoFsNodeResolverEnv>;
// todo(dsherret): investigate removing this pub type CliByonmNpmResolver =
pub root_node_modules_dir: Option<PathBuf>, ByonmNpmResolver<CliDenoResolverFs, DenoFsNodeResolverEnv>;
}
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,
})
}
// todo(dsherret): the services hanging off `CliNpmResolver` doesn't seem ideal. We should probably decouple.
#[derive(Debug)] #[derive(Debug)]
pub struct ByonmCliNpmResolver { struct CliByonmWrapper(Arc<CliByonmNpmResolver>);
fs: Arc<dyn FileSystem>,
root_node_modules_dir: Option<PathBuf>,
}
impl ByonmCliNpmResolver { impl NpmProcessStateProvider for CliByonmWrapper {
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(
&self,
permissions: &mut dyn NodePermissions,
path: &Path,
) -> Result<(), AnyError> {
if !path
.components()
.any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules")
{
_ = permissions.check_read_path(path)?;
}
Ok(())
}
}
impl NpmProcessStateProvider for ByonmCliNpmResolver {
fn get_npm_process_state(&self) -> String { fn get_npm_process_state(&self) -> String {
serde_json::to_string(&NpmProcessState { serde_json::to_string(&NpmProcessState {
kind: NpmProcessStateKind::Byonm, kind: NpmProcessStateKind::Byonm,
local_node_modules_path: self local_node_modules_path: self
.root_node_modules_dir .0
.as_ref() .root_node_modules_dir()
.map(|p| p.to_string_lossy().to_string()), .map(|p| p.to_string_lossy().to_string()),
}) })
.unwrap() .unwrap()
} }
} }
impl CliNpmResolver for ByonmCliNpmResolver { impl CliNpmResolver for CliByonmNpmResolver {
fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver> { fn into_npm_pkg_folder_resolver(
self: Arc<Self>,
) -> Arc<dyn NpmPackageFolderResolver> {
self self
} }
fn into_require_resolver(self: Arc<Self>) -> Arc<dyn NodeRequireResolver> { fn into_npm_req_resolver(self: Arc<Self>) -> Arc<dyn CliNpmReqResolver> {
self self
} }
fn into_process_state_provider( fn into_process_state_provider(
self: Arc<Self>, self: Arc<Self>,
) -> Arc<dyn NpmProcessStateProvider> { ) -> Arc<dyn NpmProcessStateProvider> {
self Arc::new(CliByonmWrapper(self))
}
fn into_maybe_byonm(self: Arc<Self>) -> Option<Arc<CliByonmNpmResolver>> {
Some(self)
} }
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver> { fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver> {
Arc::new(Self { Arc::new(self.clone())
fs: self.fs.clone(),
root_node_modules_dir: self.root_node_modules_dir.clone(),
})
} }
fn as_inner(&self) -> InnerCliNpmResolverRef { fn as_inner(&self) -> InnerCliNpmResolverRef {
InnerCliNpmResolverRef::Byonm(self) InnerCliNpmResolverRef::Byonm(self)
} }
fn root_node_modules_path(&self) -> Option<&PathBuf> { fn root_node_modules_path(&self) -> Option<&Path> {
self.root_node_modules_dir.as_ref() self.root_node_modules_dir()
} }
fn resolve_pkg_folder_from_deno_module_req( fn ensure_read_permission<'a>(
&self, &self,
req: &PackageReq, permissions: &mut dyn NodePermissions,
referrer: &ModuleSpecifier, path: &'a Path,
) -> Result<PathBuf, AnyError> { ) -> Result<Cow<'a, Path>, AnyError> {
fn node_resolve_dir( if !path
fs: &dyn FileSystem, .components()
alias: &str, .any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules")
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); permissions.check_read_path(path).map_err(Into::into)
} } else {
Ok(Cow::Borrowed(path))
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,
);
}
} }
} }
@ -398,12 +97,3 @@ impl CliNpmResolver for ByonmCliNpmResolver {
None 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
}

View file

@ -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,
},
&registry_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,
},
&registry_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,
},
&registry_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,
},
&registry_url,
),
root_dir
.join("registry.npmjs.org")
.join("_ib2hs4dfomxuuu2pjy")
.join("2.1.5"),
);
}
}

View file

@ -3,6 +3,7 @@
use base64::prelude::BASE64_STANDARD; use base64::prelude::BASE64_STANDARD;
use base64::Engine; use base64::Engine;
use deno_core::anyhow::bail; use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_npm::npm_rc::RegistryConfig; use deno_npm::npm_rc::RegistryConfig;
use http::header; use http::header;
@ -36,17 +37,21 @@ pub fn maybe_auth_header_for_npm_registry(
} }
if username.is_some() && password.is_some() { if username.is_some() && password.is_some() {
return Ok(Some(( // The npm client does some double encoding when generating the
header::AUTHORIZATION, // bearer token value, see
header::HeaderValue::from_str(&format!( // https://github.com/npm/cli/blob/780afc50e3a345feb1871a28e33fa48235bc3bd5/workspaces/config/lib/index.js#L846-L851
"Basic {}", let pw_base64 = BASE64_STANDARD
BASE64_STANDARD.encode(&format!( .decode(password.unwrap())
.with_context(|| "The password in npmrc is an invalid base64 string")?;
let bearer = BASE64_STANDARD.encode(format!(
"{}:{}", "{}:{}",
username.unwrap(), username.unwrap(),
password.unwrap() String::from_utf8_lossy(&pw_base64)
)) ));
))
.unwrap(), return Ok(Some((
header::AUTHORIZATION,
header::HeaderValue::from_str(&format!("Basic {}", bearer)).unwrap(),
))); )));
} }

View file

@ -8,6 +8,7 @@ use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_cache_dir::npm::NpmCacheDir;
use deno_core::anyhow::bail; use deno_core::anyhow::bail;
use deno_core::anyhow::Context; use deno_core::anyhow::Context;
use deno_core::error::AnyError; use deno_core::error::AnyError;
@ -18,14 +19,14 @@ use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::registry::NpmPackageInfo; use deno_npm::registry::NpmPackageInfo;
use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageCacheFolderId;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use deno_semver::Version;
use crate::args::CacheSetting; use crate::args::CacheSetting;
use crate::cache::CACHE_PERM; use crate::cache::CACHE_PERM;
use crate::npm::NpmCacheDir;
use crate::util::fs::atomic_write_file_with_retries; use crate::util::fs::atomic_write_file_with_retries;
use crate::util::fs::hard_link_dir_recursive; use crate::util::fs::hard_link_dir_recursive;
mod registry_info; pub mod registry_info;
mod tarball; mod tarball;
mod tarball_extract; mod tarball_extract;
@ -35,7 +36,7 @@ pub use tarball::TarballCache;
/// Stores a single copy of npm packages in a cache. /// Stores a single copy of npm packages in a cache.
#[derive(Debug)] #[derive(Debug)]
pub struct NpmCache { pub struct NpmCache {
cache_dir: NpmCacheDir, cache_dir: Arc<NpmCacheDir>,
cache_setting: CacheSetting, cache_setting: CacheSetting,
npmrc: Arc<ResolvedNpmRc>, npmrc: Arc<ResolvedNpmRc>,
/// ensures a package is only downloaded once per run /// ensures a package is only downloaded once per run
@ -44,7 +45,7 @@ pub struct NpmCache {
impl NpmCache { impl NpmCache {
pub fn new( pub fn new(
cache_dir: NpmCacheDir, cache_dir: Arc<NpmCacheDir>,
cache_setting: CacheSetting, cache_setting: CacheSetting,
npmrc: Arc<ResolvedNpmRc>, npmrc: Arc<ResolvedNpmRc>,
) -> Self { ) -> Self {
@ -60,6 +61,10 @@ impl NpmCache {
&self.cache_setting &self.cache_setting
} }
pub fn root_dir_path(&self) -> &Path {
self.cache_dir.root_dir()
}
pub fn root_dir_url(&self) -> &Url { pub fn root_dir_url(&self) -> &Url {
self.cache_dir.root_dir_url() self.cache_dir.root_dir_url()
} }
@ -87,9 +92,12 @@ impl NpmCache {
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let registry_url = self.npmrc.get_registry_url(&folder_id.nv.name); let registry_url = self.npmrc.get_registry_url(&folder_id.nv.name);
assert_ne!(folder_id.copy_index, 0); assert_ne!(folder_id.copy_index, 0);
let package_folder = self let package_folder = self.cache_dir.package_folder_for_id(
.cache_dir &folder_id.nv.name,
.package_folder_for_id(folder_id, registry_url); &folder_id.nv.version.to_string(),
folder_id.copy_index,
registry_url,
);
if package_folder.exists() if package_folder.exists()
// if this file exists, then the package didn't successfully initialize // if this file exists, then the package didn't successfully initialize
@ -100,9 +108,12 @@ impl NpmCache {
return Ok(()); return Ok(());
} }
let original_package_folder = self let original_package_folder = self.cache_dir.package_folder_for_id(
.cache_dir &folder_id.nv.name,
.package_folder_for_nv(&folder_id.nv, registry_url); &folder_id.nv.version.to_string(),
0, // original copy index
registry_url,
);
// it seems Windows does an "AccessDenied" error when moving a // it seems Windows does an "AccessDenied" error when moving a
// directory with hard links, so that's why this solution is done // directory with hard links, so that's why this solution is done
@ -114,7 +125,12 @@ impl NpmCache {
pub fn package_folder_for_id(&self, id: &NpmPackageCacheFolderId) -> PathBuf { pub fn package_folder_for_id(&self, id: &NpmPackageCacheFolderId) -> PathBuf {
let registry_url = self.npmrc.get_registry_url(&id.nv.name); 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 { pub fn package_folder_for_nv(&self, package: &PackageNv) -> PathBuf {
@ -127,7 +143,12 @@ impl NpmCache {
package: &PackageNv, package: &PackageNv,
registry_url: &Url, registry_url: &Url,
) -> PathBuf { ) -> 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 { pub fn package_name_folder(&self, name: &str) -> PathBuf {
@ -135,10 +156,6 @@ impl NpmCache {
self.cache_dir.package_name_folder(name, registry_url) self.cache_dir.package_name_folder(name, registry_url)
} }
pub fn root_folder(&self) -> PathBuf {
self.cache_dir.root_dir().to_owned()
}
pub fn resolve_package_folder_id_from_specifier( pub fn resolve_package_folder_id_from_specifier(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
@ -146,6 +163,15 @@ impl NpmCache {
self self
.cache_dir .cache_dir
.resolve_package_folder_id_from_specifier(specifier) .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( pub fn load_package_info(

View file

@ -84,7 +84,7 @@ impl RegistryInfoDownloader {
self.load_package_info_inner(name).await.with_context(|| { self.load_package_info_inner(name).await.with_context(|| {
format!( format!(
"Error getting response at {} for package \"{}\"", "Error getting response at {} for package \"{}\"",
self.get_package_url(name), get_package_url(&self.npmrc, name),
name name
) )
}) })
@ -190,7 +190,7 @@ impl RegistryInfoDownloader {
fn create_load_future(self: &Arc<Self>, name: &str) -> LoadFuture { fn create_load_future(self: &Arc<Self>, name: &str) -> LoadFuture {
let downloader = self.clone(); let downloader = self.clone();
let package_url = self.get_package_url(name); let package_url = get_package_url(&self.npmrc, name);
let registry_config = self.npmrc.get_registry_config(name); let registry_config = self.npmrc.get_registry_config(name);
let maybe_auth_header = let maybe_auth_header =
match maybe_auth_header_for_npm_registry(registry_config) { match maybe_auth_header_for_npm_registry(registry_config) {
@ -202,10 +202,13 @@ impl RegistryInfoDownloader {
let guard = self.progress_bar.update(package_url.as_str()); let guard = self.progress_bar.update(package_url.as_str());
let name = name.to_string(); let name = name.to_string();
async move { async move {
let maybe_bytes = downloader let client = downloader.http_client_provider.get_or_create()?;
.http_client_provider let maybe_bytes = client
.get_or_create()? .download_with_progress_and_retries(
.download_with_progress(package_url, maybe_auth_header, &guard) package_url,
maybe_auth_header,
&guard,
)
.await?; .await?;
match maybe_bytes { match maybe_bytes {
Some(bytes) => { Some(bytes) => {
@ -236,9 +239,18 @@ impl RegistryInfoDownloader {
.map(|r| r.map_err(Arc::new)) .map(|r| r.map_err(Arc::new))
.boxed_local() .boxed_local()
} }
}
pub fn get_package_url(npmrc: &ResolvedNpmRc, name: &str) -> Url {
let registry_url = npmrc.get_registry_url(name);
// The '/' character in scoped package names "@scope/name" must be
// encoded for older third party registries. Newer registries and
// npm itself support both ways
// - encoded: https://registry.npmjs.org/@rollup%2fplugin-json
// - non-ecoded: https://registry.npmjs.org/@rollup/plugin-json
// To support as many third party registries as possible we'll
// always encode the '/' character.
fn get_package_url(&self, name: &str) -> Url {
let registry_url = self.npmrc.get_registry_url(name);
// list of all characters used in npm packages: // list of all characters used in npm packages:
// !, ', (, ), *, -, ., /, [0-9], @, [A-Za-z], _, ~ // !, ', (, ), *, -, ., /, [0-9], @, [A-Za-z], _, ~
const ASCII_SET: percent_encoding::AsciiSet = const ASCII_SET: percent_encoding::AsciiSet =
@ -250,11 +262,13 @@ impl RegistryInfoDownloader {
.remove(b'*') .remove(b'*')
.remove(b'-') .remove(b'-')
.remove(b'.') .remove(b'.')
.remove(b'/')
.remove(b'@') .remove(b'@')
.remove(b'_') .remove(b'_')
.remove(b'~'); .remove(b'~');
let name = percent_encoding::utf8_percent_encode(name, &ASCII_SET); let name = percent_encoding::utf8_percent_encode(name, &ASCII_SET);
registry_url.join(&name.to_string()).unwrap() registry_url
} // Ensure that scoped package name percent encoding is lower cased
// to match npm.
.join(&name.to_string().replace("%2F", "%2f"))
.unwrap()
} }

View file

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

View file

@ -1,5 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use std::borrow::Cow;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
@ -7,9 +8,11 @@ use std::sync::Arc;
use cache::RegistryInfoDownloader; use cache::RegistryInfoDownloader;
use cache::TarballCache; use cache::TarballCache;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_cache_dir::npm::NpmCacheDir;
use deno_core::anyhow::Context; use deno_core::anyhow::Context;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::serde_json; use deno_core::serde_json;
use deno_core::url::Url;
use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::registry::NpmPackageInfo; use deno_npm::registry::NpmPackageInfo;
use deno_npm::registry::NpmRegistryApi; use deno_npm::registry::NpmRegistryApi;
@ -19,15 +22,17 @@ use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmPackageId; use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage; use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo; use deno_npm::NpmSystemInfo;
use deno_resolver::npm::CliNpmReqResolver;
use deno_runtime::colors;
use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NodeRequireResolver;
use deno_runtime::ops::process::NpmProcessStateProvider; use deno_runtime::ops::process::NpmProcessStateProvider;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use node_resolver::errors::PackageFolderResolveError; use node_resolver::errors::PackageFolderResolveError;
use node_resolver::errors::PackageFolderResolveIoError; use node_resolver::errors::PackageFolderResolveIoError;
use node_resolver::NpmResolver; use node_resolver::InNpmPackageChecker;
use node_resolver::NpmPackageFolderResolver;
use resolution::AddPkgReqsResult; use resolution::AddPkgReqsResult;
use crate::args::CliLockfile; use crate::args::CliLockfile;
@ -35,6 +40,7 @@ use crate::args::LifecycleScriptsConfig;
use crate::args::NpmInstallDepsProvider; use crate::args::NpmInstallDepsProvider;
use crate::args::NpmProcessState; use crate::args::NpmProcessState;
use crate::args::NpmProcessStateKind; use crate::args::NpmProcessStateKind;
use crate::args::PackageJsonDepValueParseWithLocationError;
use crate::cache::FastInsecureHasher; use crate::cache::FastInsecureHasher;
use crate::http_util::HttpClientProvider; use crate::http_util::HttpClientProvider;
use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs; use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
@ -45,14 +51,13 @@ use self::cache::NpmCache;
use self::registry::CliNpmRegistryApi; use self::registry::CliNpmRegistryApi;
use self::resolution::NpmResolution; use self::resolution::NpmResolution;
use self::resolvers::create_npm_fs_resolver; use self::resolvers::create_npm_fs_resolver;
pub use self::resolvers::normalize_pkg_name_for_node_modules_deno_folder;
use self::resolvers::NpmPackageFsResolver; use self::resolvers::NpmPackageFsResolver;
use super::CliNpmResolver; use super::CliNpmResolver;
use super::InnerCliNpmResolverRef; use super::InnerCliNpmResolverRef;
use super::NpmCacheDir; use super::ResolvePkgFolderFromDenoReqError;
mod cache; pub mod cache;
mod registry; mod registry;
mod resolution; mod resolution;
mod resolvers; mod resolvers;
@ -62,12 +67,12 @@ pub enum CliNpmResolverManagedSnapshotOption {
Specified(Option<ValidSerializedNpmResolutionSnapshot>), Specified(Option<ValidSerializedNpmResolutionSnapshot>),
} }
pub struct CliNpmResolverManagedCreateOptions { pub struct CliManagedNpmResolverCreateOptions {
pub snapshot: CliNpmResolverManagedSnapshotOption, pub snapshot: CliNpmResolverManagedSnapshotOption,
pub maybe_lockfile: Option<Arc<CliLockfile>>, pub maybe_lockfile: Option<Arc<CliLockfile>>,
pub fs: Arc<dyn deno_runtime::deno_fs::FileSystem>, pub fs: Arc<dyn deno_runtime::deno_fs::FileSystem>,
pub http_client_provider: Arc<crate::http_util::HttpClientProvider>, pub http_client_provider: Arc<crate::http_util::HttpClientProvider>,
pub npm_global_cache_dir: PathBuf, pub npm_cache_dir: Arc<NpmCacheDir>,
pub cache_setting: crate::args::CacheSetting, pub cache_setting: crate::args::CacheSetting,
pub text_only_progress_bar: crate::util::progress_bar::ProgressBar, pub text_only_progress_bar: crate::util::progress_bar::ProgressBar,
pub maybe_node_modules_path: Option<PathBuf>, pub maybe_node_modules_path: Option<PathBuf>,
@ -78,7 +83,7 @@ pub struct CliNpmResolverManagedCreateOptions {
} }
pub async fn create_managed_npm_resolver_for_lsp( pub async fn create_managed_npm_resolver_for_lsp(
options: CliNpmResolverManagedCreateOptions, options: CliManagedNpmResolverCreateOptions,
) -> Arc<dyn CliNpmResolver> { ) -> Arc<dyn CliNpmResolver> {
let npm_cache = create_cache(&options); let npm_cache = create_cache(&options);
let npm_api = create_api(&options, npm_cache.clone()); let npm_api = create_api(&options, npm_cache.clone());
@ -111,7 +116,7 @@ pub async fn create_managed_npm_resolver_for_lsp(
} }
pub async fn create_managed_npm_resolver( pub async fn create_managed_npm_resolver(
options: CliNpmResolverManagedCreateOptions, options: CliManagedNpmResolverCreateOptions,
) -> Result<Arc<dyn CliNpmResolver>, AnyError> { ) -> Result<Arc<dyn CliNpmResolver>, AnyError> {
let npm_cache = create_cache(&options); let npm_cache = create_cache(&options);
let npm_api = create_api(&options, npm_cache.clone()); let npm_api = create_api(&options, npm_cache.clone());
@ -185,19 +190,16 @@ fn create_inner(
)) ))
} }
fn create_cache(options: &CliNpmResolverManagedCreateOptions) -> Arc<NpmCache> { fn create_cache(options: &CliManagedNpmResolverCreateOptions) -> Arc<NpmCache> {
Arc::new(NpmCache::new( Arc::new(NpmCache::new(
NpmCacheDir::new( options.npm_cache_dir.clone(),
options.npm_global_cache_dir.clone(),
options.npmrc.get_all_known_registries_urls(),
),
options.cache_setting.clone(), options.cache_setting.clone(),
options.npmrc.clone(), options.npmrc.clone(),
)) ))
} }
fn create_api( fn create_api(
options: &CliNpmResolverManagedCreateOptions, options: &CliManagedNpmResolverCreateOptions,
npm_cache: Arc<NpmCache>, npm_cache: Arc<NpmCache>,
) -> Arc<CliNpmRegistryApi> { ) -> Arc<CliNpmRegistryApi> {
Arc::new(CliNpmRegistryApi::new( Arc::new(CliNpmRegistryApi::new(
@ -254,6 +256,35 @@ async fn snapshot_from_lockfile(
Ok(snapshot) Ok(snapshot)
} }
#[derive(Debug)]
struct ManagedInNpmPackageChecker {
root_dir: Url,
}
impl InNpmPackageChecker for ManagedInNpmPackageChecker {
fn in_npm_package(&self, specifier: &Url) -> bool {
specifier.as_ref().starts_with(self.root_dir.as_str())
}
}
pub struct CliManagedInNpmPkgCheckerCreateOptions<'a> {
pub root_cache_dir_url: &'a Url,
pub maybe_node_modules_path: Option<&'a Path>,
}
pub fn create_managed_in_npm_pkg_checker(
options: CliManagedInNpmPkgCheckerCreateOptions,
) -> Arc<dyn InNpmPackageChecker> {
let root_dir = match options.maybe_node_modules_path {
Some(node_modules_folder) => {
deno_path_util::url_from_directory_path(node_modules_folder).unwrap()
}
None => options.root_cache_dir_url.clone(),
};
debug_assert!(root_dir.as_str().ends_with('/'));
Arc::new(ManagedInNpmPackageChecker { root_dir })
}
/// An npm resolver where the resolution is managed by Deno rather than /// An npm resolver where the resolution is managed by Deno rather than
/// the user bringing their own node_modules (BYONM) on the file system. /// the user bringing their own node_modules (BYONM) on the file system.
pub struct ManagedCliNpmResolver { pub struct ManagedCliNpmResolver {
@ -427,6 +458,16 @@ impl ManagedCliNpmResolver {
self.resolution.snapshot() 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( pub fn serialized_valid_snapshot_for_system(
&self, &self,
system_info: &NpmSystemInfo, system_info: &NpmSystemInfo,
@ -466,6 +507,30 @@ impl ManagedCliNpmResolver {
self.resolution.resolve_pkg_id_from_pkg_req(req) self.resolution.resolve_pkg_id_from_pkg_req(req)
} }
pub fn ensure_no_pkg_json_dep_errors(
&self,
) -> Result<(), Box<PackageJsonDepValueParseWithLocationError>> {
for err in self.npm_install_deps_provider.pkg_json_dep_errors() {
match &err.source {
deno_package_json::PackageJsonDepValueParseError::VersionReq(_) => {
return Err(Box::new(err.clone()));
}
deno_package_json::PackageJsonDepValueParseError::Unsupported {
..
} => {
// only warn for this one
log::warn!(
"{} {}\n at {}",
colors::yellow("Warning"),
err.source,
err.location,
)
}
}
}
Ok(())
}
/// Ensures that the top level `package.json` dependencies are installed. /// Ensures that the top level `package.json` dependencies are installed.
/// This may set up the `node_modules` directory. /// This may set up the `node_modules` directory.
/// ///
@ -477,6 +542,7 @@ impl ManagedCliNpmResolver {
if !self.top_level_install_flag.raise() { if !self.top_level_install_flag.raise() {
return Ok(false); // already did this return Ok(false); // already did this
} }
let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs(); let pkg_json_remote_pkgs = self.npm_install_deps_provider.remote_pkgs();
if pkg_json_remote_pkgs.is_empty() { if pkg_json_remote_pkgs.is_empty() {
return Ok(false); return Ok(false);
@ -515,8 +581,16 @@ impl ManagedCliNpmResolver {
.map_err(|err| err.into()) .map_err(|err| err.into())
} }
pub fn global_cache_root_folder(&self) -> PathBuf { pub fn maybe_node_modules_path(&self) -> Option<&Path> {
self.npm_cache.root_folder() self.fs_resolver.node_modules_path()
}
pub fn global_cache_root_path(&self) -> &Path {
self.npm_cache.root_dir_path()
}
pub fn global_cache_root_url(&self) -> &Url {
self.npm_cache.root_dir_url()
} }
} }
@ -532,7 +606,7 @@ fn npm_process_state(
.unwrap() .unwrap()
} }
impl NpmResolver for ManagedCliNpmResolver { impl NpmPackageFolderResolver for ManagedCliNpmResolver {
fn resolve_package_folder_from_package( fn resolve_package_folder_from_package(
&self, &self,
name: &str, name: &str,
@ -551,39 +625,40 @@ impl NpmResolver for ManagedCliNpmResolver {
log::debug!("Resolved {} from {} to {}", name, referrer, path.display()); log::debug!("Resolved {} from {} to {}", name, referrer, path.display());
Ok(path) Ok(path)
} }
fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
let root_dir_url = self.fs_resolver.root_dir_url();
debug_assert!(root_dir_url.as_str().ends_with('/'));
specifier.as_ref().starts_with(root_dir_url.as_str())
}
}
impl NodeRequireResolver for ManagedCliNpmResolver {
fn ensure_read_permission(
&self,
permissions: &mut dyn NodePermissions,
path: &Path,
) -> Result<(), AnyError> {
self.fs_resolver.ensure_read_permission(permissions, path)
}
} }
impl NpmProcessStateProvider for ManagedCliNpmResolver { impl NpmProcessStateProvider for ManagedCliNpmResolver {
fn get_npm_process_state(&self) -> String { fn get_npm_process_state(&self) -> String {
npm_process_state( npm_process_state(
self.resolution.serialized_valid_snapshot(), self.resolution.serialized_valid_snapshot(),
self.fs_resolver.node_modules_path().map(|p| p.as_path()), self.fs_resolver.node_modules_path(),
) )
} }
} }
impl CliNpmReqResolver for ManagedCliNpmResolver {
fn resolve_pkg_folder_from_deno_module_req(
&self,
req: &PackageReq,
_referrer: &ModuleSpecifier,
) -> 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)
}
}
impl CliNpmResolver for ManagedCliNpmResolver { impl CliNpmResolver for ManagedCliNpmResolver {
fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver> { fn into_npm_pkg_folder_resolver(
self: Arc<Self>,
) -> Arc<dyn NpmPackageFolderResolver> {
self self
} }
fn into_require_resolver(self: Arc<Self>) -> Arc<dyn NodeRequireResolver> { fn into_npm_req_resolver(self: Arc<Self>) -> Arc<dyn CliNpmReqResolver> {
self self
} }
@ -630,17 +705,16 @@ impl CliNpmResolver for ManagedCliNpmResolver {
InnerCliNpmResolverRef::Managed(self) 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() self.fs_resolver.node_modules_path()
} }
fn resolve_pkg_folder_from_deno_module_req( fn ensure_read_permission<'a>(
&self, &self,
req: &PackageReq, permissions: &mut dyn NodePermissions,
_referrer: &ModuleSpecifier, path: &'a Path,
) -> Result<PathBuf, AnyError> { ) -> Result<Cow<'a, Path>, AnyError> {
let pkg_id = self.resolve_pkg_id_from_pkg_req(req)?; self.fs_resolver.ensure_read_permission(permissions, path)
self.resolve_pkg_folder_from_pkg_id(&pkg_id)
} }
fn check_state_hash(&self) -> Option<u64> { fn check_state_hash(&self) -> Option<u64> {

View file

@ -3,6 +3,7 @@
pub mod bin_entries; pub mod bin_entries;
pub mod lifecycle_scripts; pub mod lifecycle_scripts;
use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
use std::io::ErrorKind; use std::io::ErrorKind;
use std::path::Path; use std::path::Path;
@ -16,7 +17,6 @@ use deno_core::anyhow::Context;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::futures; use deno_core::futures;
use deno_core::futures::StreamExt; use deno_core::futures::StreamExt;
use deno_core::url::Url;
use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageCacheFolderId;
use deno_npm::NpmPackageId; use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage; use deno_npm::NpmResolutionPackage;
@ -29,11 +29,8 @@ use crate::npm::managed::cache::TarballCache;
/// Part of the resolution that interacts with the file system. /// Part of the resolution that interacts with the file system.
#[async_trait(?Send)] #[async_trait(?Send)]
pub trait NpmPackageFsResolver: Send + Sync { pub trait NpmPackageFsResolver: Send + Sync {
/// Specifier for the root directory.
fn root_dir_url(&self) -> &Url;
/// The local node_modules folder if it is applicable to the implementation. /// 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>; fn maybe_package_folder(&self, package_id: &NpmPackageId) -> Option<PathBuf>;
@ -62,11 +59,12 @@ pub trait NpmPackageFsResolver: Send + Sync {
async fn cache_packages(&self) -> Result<(), AnyError>; 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, &self,
permissions: &mut dyn NodePermissions, permissions: &mut dyn NodePermissions,
path: &Path, path: &'a Path,
) -> Result<(), AnyError>; ) -> Result<Cow<'a, Path>, AnyError>;
} }
#[derive(Debug)] #[derive(Debug)]
@ -85,11 +83,15 @@ impl RegistryReadPermissionChecker {
} }
} }
pub fn ensure_registry_read_permission( pub fn ensure_registry_read_permission<'a>(
&self, &self,
permissions: &mut dyn NodePermissions, permissions: &mut dyn NodePermissions,
path: &Path, path: &'a Path,
) -> Result<(), AnyError> { ) -> 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 // allow reading if it's in the node_modules
let is_path_in_node_modules = path.starts_with(&self.registry_path) let is_path_in_node_modules = path.starts_with(&self.registry_path)
&& path && path
@ -118,20 +120,20 @@ impl RegistryReadPermissionChecker {
}, },
} }
}; };
let Some(registry_path_canon) = canonicalize(&self.registry_path)? else { if let Some(registry_path_canon) = canonicalize(&self.registry_path)? {
return Ok(()); // not exists, allow reading if let Some(path_canon) = canonicalize(path)? {
};
let Some(path_canon) = canonicalize(path)? else {
return Ok(()); // not exists, allow reading
};
if path_canon.starts_with(registry_path_canon) { if path_canon.starts_with(registry_path_canon) {
return Ok(()); return Ok(Cow::Owned(path_canon));
}
} else if path.starts_with(registry_path_canon)
|| path.starts_with(&self.registry_path)
{
return Ok(Cow::Borrowed(path));
}
} }
} }
_ = permissions.check_read_path(path)?; permissions.check_read_path(path).map_err(Into::into)
Ok(())
} }
} }

View file

@ -18,6 +18,7 @@ pub struct BinEntries<'a> {
seen_names: HashMap<&'a str, &'a NpmPackageId>, seen_names: HashMap<&'a str, &'a NpmPackageId>,
/// The bin entries /// The bin entries
entries: Vec<(&'a NpmResolutionPackage, PathBuf)>, entries: Vec<(&'a NpmResolutionPackage, PathBuf)>,
sorted: bool,
} }
/// Returns the name of the default binary for the given package. /// Returns the name of the default binary for the given package.
@ -31,6 +32,20 @@ fn default_bin_name(package: &NpmResolutionPackage) -> &str {
.map_or(package.id.nv.name.as_str(), |(_, name)| name) .map_or(package.id.nv.name.as_str(), |(_, name)| name)
} }
pub fn warn_missing_entrypoint(
bin_name: &str,
package_path: &Path,
entrypoint: &Path,
) {
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(),
entrypoint.display()
);
}
impl<'a> BinEntries<'a> { impl<'a> BinEntries<'a> {
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()
@ -42,6 +57,7 @@ impl<'a> BinEntries<'a> {
package: &'a NpmResolutionPackage, package: &'a NpmResolutionPackage,
package_path: PathBuf, package_path: PathBuf,
) { ) {
self.sorted = false;
// check for a new collision, if we haven't already // check for a new collision, if we haven't already
// found one // found one
match package.bin.as_ref().unwrap() { match package.bin.as_ref().unwrap() {
@ -79,16 +95,21 @@ impl<'a> BinEntries<'a> {
&str, // bin name &str, // bin name
&str, // bin script &str, // bin script
) -> Result<(), AnyError>, ) -> Result<(), AnyError>,
mut filter: impl FnMut(&NpmResolutionPackage) -> bool,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
if !self.collisions.is_empty() { if !self.collisions.is_empty() && !self.sorted {
// walking the dependency tree to find out the depth of each package // walking the dependency tree to find out the depth of each package
// is sort of expensive, so we only do it if there's a collision // is sort of expensive, so we only do it if there's a collision
sort_by_depth(snapshot, &mut self.entries, &mut self.collisions); sort_by_depth(snapshot, &mut self.entries, &mut self.collisions);
self.sorted = true;
} }
let mut seen = HashSet::new(); let mut seen = HashSet::new();
for (package, package_path) in &self.entries { for (package, package_path) in &self.entries {
if !filter(package) {
continue;
}
if let Some(bin_entries) = &package.bin { if let Some(bin_entries) = &package.bin {
match bin_entries { match bin_entries {
deno_npm::registry::NpmPackageVersionBinEntry::String(script) => { deno_npm::registry::NpmPackageVersionBinEntry::String(script) => {
@ -118,8 +139,8 @@ impl<'a> BinEntries<'a> {
} }
/// Collect the bin entries into a vec of (name, script path) /// Collect the bin entries into a vec of (name, script path)
pub fn into_bin_files( pub fn collect_bin_files(
mut self, &mut self,
snapshot: &NpmResolutionSnapshot, snapshot: &NpmResolutionSnapshot,
) -> Vec<(String, PathBuf)> { ) -> Vec<(String, PathBuf)> {
let mut bins = Vec::new(); let mut bins = Vec::new();
@ -131,17 +152,18 @@ impl<'a> BinEntries<'a> {
bins.push((name.to_string(), package_path.join(script))); bins.push((name.to_string(), package_path.join(script)));
Ok(()) Ok(())
}, },
|_| true,
) )
.unwrap(); .unwrap();
bins bins
} }
/// Finish setting up the bin entries, writing the necessary files fn set_up_entries_filtered(
/// to disk.
pub fn finish(
mut self, mut self,
snapshot: &NpmResolutionSnapshot, snapshot: &NpmResolutionSnapshot,
bin_node_modules_dir_path: &Path, bin_node_modules_dir_path: &Path,
filter: impl FnMut(&NpmResolutionPackage) -> bool,
mut handler: impl FnMut(&EntrySetupOutcome<'_>),
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
if !self.entries.is_empty() && !bin_node_modules_dir_path.exists() { if !self.entries.is_empty() && !bin_node_modules_dir_path.exists() {
std::fs::create_dir_all(bin_node_modules_dir_path).with_context( std::fs::create_dir_all(bin_node_modules_dir_path).with_context(
@ -160,18 +182,54 @@ impl<'a> BinEntries<'a> {
Ok(()) Ok(())
}, },
|package, package_path, name, script| { |package, package_path, name, script| {
set_up_bin_entry( let outcome = set_up_bin_entry(
package, package,
name, name,
script, script,
package_path, package_path,
bin_node_modules_dir_path, bin_node_modules_dir_path,
) )?;
handler(&outcome);
Ok(())
}, },
filter,
)?; )?;
Ok(()) Ok(())
} }
/// Finish setting up the bin entries, writing the necessary files
/// to disk.
pub fn finish(
self,
snapshot: &NpmResolutionSnapshot,
bin_node_modules_dir_path: &Path,
handler: impl FnMut(&EntrySetupOutcome<'_>),
) -> Result<(), AnyError> {
self.set_up_entries_filtered(
snapshot,
bin_node_modules_dir_path,
|_| true,
handler,
)
}
/// Finish setting up the bin entries, writing the necessary files
/// to disk.
pub fn finish_only(
self,
snapshot: &NpmResolutionSnapshot,
bin_node_modules_dir_path: &Path,
handler: impl FnMut(&EntrySetupOutcome<'_>),
only: &HashSet<&NpmPackageId>,
) -> Result<(), AnyError> {
self.set_up_entries_filtered(
snapshot,
bin_node_modules_dir_path,
|package| only.contains(&package.id),
handler,
)
}
} }
// walk the dependency tree to find out the depth of each package // walk the dependency tree to find out the depth of each package
@ -233,16 +291,17 @@ fn sort_by_depth(
}); });
} }
pub fn set_up_bin_entry( pub fn set_up_bin_entry<'a>(
package: &NpmResolutionPackage, package: &'a NpmResolutionPackage,
bin_name: &str, bin_name: &'a str,
#[allow(unused_variables)] bin_script: &str, #[allow(unused_variables)] bin_script: &str,
#[allow(unused_variables)] package_path: &Path, #[allow(unused_variables)] package_path: &'a Path,
bin_node_modules_dir_path: &Path, bin_node_modules_dir_path: &Path,
) -> Result<(), AnyError> { ) -> Result<EntrySetupOutcome<'a>, AnyError> {
#[cfg(windows)] #[cfg(windows)]
{ {
set_up_bin_shim(package, bin_name, bin_node_modules_dir_path)?; set_up_bin_shim(package, bin_name, bin_node_modules_dir_path)?;
Ok(EntrySetupOutcome::Success)
} }
#[cfg(unix)] #[cfg(unix)]
{ {
@ -252,9 +311,8 @@ pub fn set_up_bin_entry(
bin_script, bin_script,
package_path, package_path,
bin_node_modules_dir_path, bin_node_modules_dir_path,
)?; )
} }
Ok(())
} }
#[cfg(windows)] #[cfg(windows)]
@ -301,14 +359,39 @@ fn make_executable_if_exists(path: &Path) -> Result<bool, AnyError> {
Ok(true) Ok(true)
} }
pub enum EntrySetupOutcome<'a> {
#[cfg_attr(windows, allow(dead_code))]
MissingEntrypoint {
bin_name: &'a str,
package_path: &'a Path,
entrypoint: PathBuf,
package: &'a NpmResolutionPackage,
},
Success,
}
impl<'a> EntrySetupOutcome<'a> {
pub fn warn_if_failed(&self) {
match self {
EntrySetupOutcome::MissingEntrypoint {
bin_name,
package_path,
entrypoint,
..
} => warn_missing_entrypoint(bin_name, package_path, entrypoint),
EntrySetupOutcome::Success => {}
}
}
}
#[cfg(unix)] #[cfg(unix)]
fn symlink_bin_entry( fn symlink_bin_entry<'a>(
_package: &NpmResolutionPackage, package: &'a NpmResolutionPackage,
bin_name: &str, bin_name: &'a str,
bin_script: &str, bin_script: &str,
package_path: &Path, package_path: &'a Path,
bin_node_modules_dir_path: &Path, bin_node_modules_dir_path: &Path,
) -> Result<(), AnyError> { ) -> Result<EntrySetupOutcome<'a>, AnyError> {
use std::io; use std::io;
use std::os::unix::fs::symlink; use std::os::unix::fs::symlink;
let link = bin_node_modules_dir_path.join(bin_name); let link = bin_node_modules_dir_path.join(bin_name);
@ -318,14 +401,12 @@ fn symlink_bin_entry(
format!("Can't set up '{}' bin at {}", bin_name, original.display()) format!("Can't set up '{}' bin at {}", bin_name, original.display())
})?; })?;
if !found { if !found {
log::warn!( return Ok(EntrySetupOutcome::MissingEntrypoint {
"{} Trying to set up '{}' bin for \"{}\", but the entry point \"{}\" doesn't exist.",
deno_terminal::colors::yellow("Warning"),
bin_name, bin_name,
package_path.display(), package_path,
original.display() entrypoint: original,
); package,
return Ok(()); });
} }
let original_relative = let original_relative =
@ -348,7 +429,7 @@ fn symlink_bin_entry(
original_relative.display() original_relative.display()
) )
})?; })?;
return Ok(()); return Ok(EntrySetupOutcome::Success);
} }
return Err(err).with_context(|| { return Err(err).with_context(|| {
format!( format!(
@ -359,5 +440,5 @@ fn symlink_bin_entry(
}); });
} }
Ok(()) Ok(EntrySetupOutcome::Success)
} }

View file

@ -2,12 +2,15 @@
use super::bin_entries::BinEntries; use super::bin_entries::BinEntries;
use crate::args::LifecycleScriptsConfig; use crate::args::LifecycleScriptsConfig;
use crate::task_runner::TaskStdio;
use crate::util::progress_bar::ProgressBar;
use deno_core::anyhow::Context; use deno_core::anyhow::Context;
use deno_npm::resolution::NpmResolutionSnapshot; use deno_npm::resolution::NpmResolutionSnapshot;
use deno_runtime::deno_io::FromRawIoHandle; use deno_runtime::deno_io::FromRawIoHandle;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use deno_semver::Version; use deno_semver::Version;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashSet;
use std::rc::Rc; use std::rc::Rc;
use std::path::Path; use std::path::Path;
@ -59,7 +62,7 @@ impl<'a> LifecycleScripts<'a> {
} }
} }
fn has_lifecycle_scripts( pub fn has_lifecycle_scripts(
package: &NpmResolutionPackage, package: &NpmResolutionPackage,
package_path: &Path, package_path: &Path,
) -> bool { ) -> bool {
@ -81,7 +84,7 @@ fn is_broken_default_install_script(script: &str, package_path: &Path) -> bool {
} }
impl<'a> LifecycleScripts<'a> { impl<'a> LifecycleScripts<'a> {
fn can_run_scripts(&self, package_nv: &PackageNv) -> bool { pub fn can_run_scripts(&self, package_nv: &PackageNv) -> bool {
if !self.strategy.can_run_scripts() { if !self.strategy.can_run_scripts() {
return false; return false;
} }
@ -96,6 +99,9 @@ impl<'a> LifecycleScripts<'a> {
PackagesAllowedScripts::None => false, PackagesAllowedScripts::None => false,
} }
} }
pub fn has_run_scripts(&self, package: &NpmResolutionPackage) -> bool {
self.strategy.has_run(package)
}
/// Register a package for running lifecycle scripts, if applicable. /// Register a package for running lifecycle scripts, if applicable.
/// ///
/// `package_path` is the path containing the package's code (its root dir). /// `package_path` is the path containing the package's code (its root dir).
@ -108,12 +114,12 @@ impl<'a> LifecycleScripts<'a> {
) { ) {
if has_lifecycle_scripts(package, &package_path) { if has_lifecycle_scripts(package, &package_path) {
if self.can_run_scripts(&package.id.nv) { if self.can_run_scripts(&package.id.nv) {
if !self.strategy.has_run(package) { if !self.has_run_scripts(package) {
self self
.packages_with_scripts .packages_with_scripts
.push((package, package_path.into_owned())); .push((package, package_path.into_owned()));
} }
} else if !self.strategy.has_run(package) } else if !self.has_run_scripts(package)
&& (self.config.explicit_install || !self.strategy.has_warned(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 // Skip adding `esbuild` as it is known that it can work properly without lifecycle script
@ -147,21 +153,32 @@ impl<'a> LifecycleScripts<'a> {
self, self,
snapshot: &NpmResolutionSnapshot, snapshot: &NpmResolutionSnapshot,
packages: &[NpmResolutionPackage], packages: &[NpmResolutionPackage],
root_node_modules_dir_path: Option<&Path>, root_node_modules_dir_path: &Path,
progress_bar: &ProgressBar,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
self.warn_not_run_scripts()?; self.warn_not_run_scripts()?;
let get_package_path = let get_package_path =
|p: &NpmResolutionPackage| self.strategy.package_path(p); |p: &NpmResolutionPackage| self.strategy.package_path(p);
let mut failed_packages = Vec::new(); let mut failed_packages = Vec::new();
let mut bin_entries = BinEntries::new();
if !self.packages_with_scripts.is_empty() { if !self.packages_with_scripts.is_empty() {
let package_ids = self
.packages_with_scripts
.iter()
.map(|(p, _)| &p.id)
.collect::<HashSet<_>>();
// get custom commands for each bin available in the node_modules dir (essentially // get custom commands for each bin available in the node_modules dir (essentially
// the scripts that are in `node_modules/.bin`) // the scripts that are in `node_modules/.bin`)
let base = let base = resolve_baseline_custom_commands(
resolve_baseline_custom_commands(snapshot, packages, get_package_path)?; &mut bin_entries,
snapshot,
packages,
get_package_path,
)?;
let init_cwd = &self.config.initial_cwd; let init_cwd = &self.config.initial_cwd;
let process_state = crate::npm::managed::npm_process_state( let process_state = crate::npm::managed::npm_process_state(
snapshot.as_valid_serialized(), snapshot.as_valid_serialized(),
root_node_modules_dir_path, Some(root_node_modules_dir_path),
); );
let mut env_vars = crate::task_runner::real_env_vars(); let mut env_vars = crate::task_runner::real_env_vars();
@ -201,7 +218,15 @@ impl<'a> LifecycleScripts<'a> {
{ {
continue; continue;
} }
let exit_code = crate::task_runner::run_task( let _guard = progress_bar.update_with_prompt(
crate::util::progress_bar::ProgressMessagePrompt::Initialize,
&format!("{}: running '{script_name}' script", package.id.nv),
);
let crate::task_runner::TaskResult {
exit_code,
stderr,
stdout,
} = crate::task_runner::run_task(
crate::task_runner::RunTaskOptions { crate::task_runner::RunTaskOptions {
task_name: script_name, task_name: script_name,
script, script,
@ -210,16 +235,38 @@ impl<'a> LifecycleScripts<'a> {
custom_commands: custom_commands.clone(), custom_commands: custom_commands.clone(),
init_cwd, init_cwd,
argv: &[], argv: &[],
root_node_modules_dir: root_node_modules_dir_path, root_node_modules_dir: Some(root_node_modules_dir_path),
stdio: Some(crate::task_runner::TaskIo {
stderr: TaskStdio::piped(),
stdout: TaskStdio::piped(),
}),
}, },
) )
.await?; .await?;
let stdout = stdout.unwrap();
let stderr = stderr.unwrap();
if exit_code != 0 { if exit_code != 0 {
log::warn!( log::warn!(
"error: script '{}' in '{}' failed with exit code {}", "error: script '{}' in '{}' failed with exit code {}{}{}",
script_name, script_name,
package.id.nv, package.id.nv,
exit_code, 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); failed_packages.push(&package.id.nv);
// assume if earlier script fails, later ones will fail too // assume if earlier script fails, later ones will fail too
@ -229,6 +276,17 @@ impl<'a> LifecycleScripts<'a> {
} }
self.strategy.did_run_scripts(package)?; self.strategy.did_run_scripts(package)?;
} }
// re-set up bin entries for the packages which we've run scripts for.
// lifecycle scripts can create files that are linked to by bin entries,
// and the only reliable way to handle this is to re-link bin entries
// (this is what PNPM does as well)
bin_entries.finish_only(
snapshot,
&root_node_modules_dir_path.join(".bin"),
|outcome| outcome.warn_if_failed(),
&package_ids,
)?;
} }
if failed_packages.is_empty() { if failed_packages.is_empty() {
Ok(()) Ok(())
@ -248,9 +306,10 @@ impl<'a> LifecycleScripts<'a> {
// take in all (non copy) packages from snapshot, // take in all (non copy) packages from snapshot,
// and resolve the set of available binaries to create // and resolve the set of available binaries to create
// custom commands available to the task runner // custom commands available to the task runner
fn resolve_baseline_custom_commands( fn resolve_baseline_custom_commands<'a>(
snapshot: &NpmResolutionSnapshot, bin_entries: &mut BinEntries<'a>,
packages: &[NpmResolutionPackage], snapshot: &'a NpmResolutionSnapshot,
packages: &'a [NpmResolutionPackage],
get_package_path: impl Fn(&NpmResolutionPackage) -> PathBuf, get_package_path: impl Fn(&NpmResolutionPackage) -> PathBuf,
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> { ) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
let mut custom_commands = crate::task_runner::TaskCustomCommands::new(); let mut custom_commands = crate::task_runner::TaskCustomCommands::new();
@ -273,6 +332,7 @@ fn resolve_baseline_custom_commands(
// doing it for packages that are set up already. // 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. // realistically, scripts won't be run very often so it probably isn't too big of an issue.
resolve_custom_commands_from_packages( resolve_custom_commands_from_packages(
bin_entries,
custom_commands, custom_commands,
snapshot, snapshot,
packages, packages,
@ -287,12 +347,12 @@ fn resolve_custom_commands_from_packages<
'a, 'a,
P: IntoIterator<Item = &'a NpmResolutionPackage>, P: IntoIterator<Item = &'a NpmResolutionPackage>,
>( >(
bin_entries: &mut BinEntries<'a>,
mut commands: crate::task_runner::TaskCustomCommands, mut commands: crate::task_runner::TaskCustomCommands,
snapshot: &'a NpmResolutionSnapshot, snapshot: &'a NpmResolutionSnapshot,
packages: P, packages: P,
get_package_path: impl Fn(&'a NpmResolutionPackage) -> PathBuf, get_package_path: impl Fn(&'a NpmResolutionPackage) -> PathBuf,
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> { ) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
let mut bin_entries = BinEntries::new();
for package in packages { for package in packages {
let package_path = get_package_path(package); let package_path = get_package_path(package);
@ -300,7 +360,7 @@ fn resolve_custom_commands_from_packages<
bin_entries.add(package, package_path); bin_entries.add(package, package_path);
} }
} }
let bins = bin_entries.into_bin_files(snapshot); let bins: Vec<(String, PathBuf)> = bin_entries.collect_bin_files(snapshot);
for (bin_name, script_path) in bins { for (bin_name, script_path) in bins {
commands.insert( commands.insert(
bin_name.clone(), bin_name.clone(),
@ -323,7 +383,9 @@ fn resolve_custom_commands_from_deps(
snapshot: &NpmResolutionSnapshot, snapshot: &NpmResolutionSnapshot,
get_package_path: impl Fn(&NpmResolutionPackage) -> PathBuf, get_package_path: impl Fn(&NpmResolutionPackage) -> PathBuf,
) -> Result<crate::task_runner::TaskCustomCommands, AnyError> { ) -> Result<crate::task_runner::TaskCustomCommands, AnyError> {
let mut bin_entries = BinEntries::new();
resolve_custom_commands_from_packages( resolve_custom_commands_from_packages(
&mut bin_entries,
baseline, baseline,
snapshot, snapshot,
package package

View file

@ -11,7 +11,6 @@ use crate::colors;
use async_trait::async_trait; use async_trait::async_trait;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::url::Url;
use deno_npm::NpmPackageCacheFolderId; use deno_npm::NpmPackageCacheFolderId;
use deno_npm::NpmPackageId; use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage; use deno_npm::NpmResolutionPackage;
@ -56,7 +55,7 @@ impl GlobalNpmPackageResolver {
Self { Self {
registry_read_permission_checker: RegistryReadPermissionChecker::new( registry_read_permission_checker: RegistryReadPermissionChecker::new(
fs, fs,
cache.root_folder(), cache.root_dir_path().to_path_buf(),
), ),
cache, cache,
tarball_cache, tarball_cache,
@ -69,11 +68,7 @@ impl GlobalNpmPackageResolver {
#[async_trait(?Send)] #[async_trait(?Send)]
impl NpmPackageFsResolver for GlobalNpmPackageResolver { impl NpmPackageFsResolver for GlobalNpmPackageResolver {
fn root_dir_url(&self) -> &Url { fn node_modules_path(&self) -> Option<&Path> {
self.cache.root_dir_url()
}
fn node_modules_path(&self) -> Option<&PathBuf> {
None None
} }
@ -183,11 +178,11 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver {
Ok(()) Ok(())
} }
fn ensure_read_permission( fn ensure_read_permission<'a>(
&self, &self,
permissions: &mut dyn NodePermissions, permissions: &mut dyn NodePermissions,
path: &Path, path: &'a Path,
) -> Result<(), AnyError> { ) -> Result<Cow<'a, Path>, AnyError> {
self self
.registry_read_permission_checker .registry_read_permission_checker
.ensure_registry_read_permission(permissions, path) .ensure_registry_read_permission(permissions, path)

View file

@ -19,6 +19,7 @@ use crate::args::LifecycleScriptsConfig;
use crate::colors; use crate::colors;
use async_trait::async_trait; use async_trait::async_trait;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_cache_dir::npm::mixed_case_package_name_decode;
use deno_core::anyhow::Context; use deno_core::anyhow::Context;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::stream::FuturesUnordered;
@ -30,6 +31,7 @@ use deno_npm::NpmPackageCacheFolderId;
use deno_npm::NpmPackageId; use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage; use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo; 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_fs;
use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodePermissions;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
@ -42,8 +44,6 @@ use serde::Serialize;
use crate::args::NpmInstallDepsProvider; use crate::args::NpmInstallDepsProvider;
use crate::cache::CACHE_PERM; 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::atomic_write_file_with_retries;
use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs; use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
use crate::util::fs::clone_dir_recursive; use crate::util::fs::clone_dir_recursive;
@ -55,6 +55,7 @@ use crate::util::progress_bar::ProgressMessagePrompt;
use super::super::cache::NpmCache; use super::super::cache::NpmCache;
use super::super::cache::TarballCache; use super::super::cache::TarballCache;
use super::super::resolution::NpmResolution; use super::super::resolution::NpmResolution;
use super::common::bin_entries;
use super::common::NpmPackageFsResolver; use super::common::NpmPackageFsResolver;
use super::common::RegistryReadPermissionChecker; use super::common::RegistryReadPermissionChecker;
@ -155,12 +156,8 @@ impl LocalNpmPackageResolver {
#[async_trait(?Send)] #[async_trait(?Send)]
impl NpmPackageFsResolver for LocalNpmPackageResolver { impl NpmPackageFsResolver for LocalNpmPackageResolver {
fn root_dir_url(&self) -> &Url { fn node_modules_path(&self) -> Option<&Path> {
&self.root_node_modules_url Some(self.root_node_modules_path.as_ref())
}
fn node_modules_path(&self) -> Option<&PathBuf> {
Some(&self.root_node_modules_path)
} }
fn maybe_package_folder(&self, id: &NpmPackageId) -> Option<PathBuf> { fn maybe_package_folder(&self, id: &NpmPackageId) -> Option<PathBuf> {
@ -257,11 +254,11 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver {
.await .await
} }
fn ensure_read_permission( fn ensure_read_permission<'a>(
&self, &self,
permissions: &mut dyn NodePermissions, permissions: &mut dyn NodePermissions,
path: &Path, path: &'a Path,
) -> Result<(), AnyError> { ) -> Result<Cow<'a, Path>, AnyError> {
self self
.registry_read_permission_checker .registry_read_permission_checker
.ensure_registry_read_permission(permissions, path) .ensure_registry_read_permission(permissions, path)
@ -333,8 +330,7 @@ async fn sync_resolution_with_fs(
let mut cache_futures = FuturesUnordered::new(); let mut cache_futures = FuturesUnordered::new();
let mut newest_packages_by_name: HashMap<&String, &NpmResolutionPackage> = let mut newest_packages_by_name: HashMap<&String, &NpmResolutionPackage> =
HashMap::with_capacity(package_partitions.packages.len()); HashMap::with_capacity(package_partitions.packages.len());
let bin_entries = let bin_entries = Rc::new(RefCell::new(bin_entries::BinEntries::new()));
Rc::new(RefCell::new(super::common::bin_entries::BinEntries::new()));
let mut lifecycle_scripts = let mut lifecycle_scripts =
super::common::lifecycle_scripts::LifecycleScripts::new( super::common::lifecycle_scripts::LifecycleScripts::new(
lifecycle_scripts, lifecycle_scripts,
@ -343,6 +339,14 @@ async fn sync_resolution_with_fs(
}, },
); );
let packages_with_deprecation_warnings = Arc::new(Mutex::new(Vec::new())); 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 { for package in &package_partitions.packages {
if let Some(current_pkg) = if let Some(current_pkg) =
newest_packages_by_name.get_mut(&package.id.nv.name) newest_packages_by_name.get_mut(&package.id.nv.name)
@ -357,11 +361,29 @@ async fn sync_resolution_with_fs(
let package_folder_name = let package_folder_name =
get_package_folder_id_folder_name(&package.get_package_cache_folder_id()); get_package_folder_id_folder_name(&package.get_package_cache_folder_id());
let folder_path = deno_local_registry_dir.join(&package_folder_name); 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 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 if !cache
.cache_setting() .cache_setting()
.should_use_for_npm_package(&package.id.nv.name) .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 // cache bust the dep from the dep setup cache so the symlinks
// are forced to be recreated // are forced to be recreated
@ -371,6 +393,7 @@ async fn sync_resolution_with_fs(
let bin_entries_to_setup = bin_entries.clone(); let bin_entries_to_setup = bin_entries.clone();
let packages_with_deprecation_warnings = let packages_with_deprecation_warnings =
packages_with_deprecation_warnings.clone(); packages_with_deprecation_warnings.clone();
cache_futures.push(async move { cache_futures.push(async move {
tarball_cache tarball_cache
.ensure_package(&package.id.nv, &package.dist) .ensure_package(&package.id.nv, &package.dist)
@ -389,7 +412,7 @@ async fn sync_resolution_with_fs(
move || { move || {
clone_dir_recursive(&cache_folder, &package_path)?; clone_dir_recursive(&cache_folder, &package_path)?;
// write out a file that indicates this folder has been initialized // write out a file that indicates this folder has been initialized
fs::write(initialized_file, "")?; fs::write(initialized_file, tags)?;
Ok::<_, AnyError>(()) Ok::<_, AnyError>(())
} }
@ -410,6 +433,8 @@ async fn sync_resolution_with_fs(
drop(pb_guard); // explicit for clarity drop(pb_guard); // explicit for clarity
Ok::<_, AnyError>(()) Ok::<_, AnyError>(())
}); });
} else if matches!(package_state, PackageFolderState::TagsOutdated) {
fs::write(initialized_file, tags)?;
} }
let sub_node_modules = folder_path.join("node_modules"); let sub_node_modules = folder_path.join("node_modules");
@ -518,9 +543,9 @@ async fn sync_resolution_with_fs(
// linked into the root // linked into the root
match found_names.entry(remote_alias) { match found_names.entry(remote_alias) {
Entry::Occupied(nv) => { Entry::Occupied(nv) => {
alias_clashes // alias to a different package (in case of duplicate aliases)
|| remote.req.name != nv.get().name // alias to a different package (in case of duplicate aliases) // or the version doesn't match the version in the root node_modules
|| !remote.req.version_req.matches(&nv.get().version) // incompatible version alias_clashes || &remote_pkg.id.nv != *nv.get()
} }
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
entry.insert(&remote_pkg.id.nv); entry.insert(&remote_pkg.id.nv);
@ -633,7 +658,28 @@ async fn sync_resolution_with_fs(
// 7. Set up `node_modules/.bin` entries for packages that need it. // 7. Set up `node_modules/.bin` entries for packages that need it.
{ {
let bin_entries = std::mem::take(&mut *bin_entries.borrow_mut()); let bin_entries = std::mem::take(&mut *bin_entries.borrow_mut());
bin_entries.finish(snapshot, &bin_node_modules_dir_path)?; bin_entries.finish(
snapshot,
&bin_node_modules_dir_path,
|setup_outcome| {
match setup_outcome {
bin_entries::EntrySetupOutcome::MissingEntrypoint {
package,
package_path,
..
} if super::common::lifecycle_scripts::has_lifecycle_scripts(
package,
package_path,
) && lifecycle_scripts.can_run_scripts(&package.id.nv)
&& !lifecycle_scripts.has_run_scripts(package) =>
{
// ignore, it might get fixed when the lifecycle scripts run.
// if not, we'll warn then
}
outcome => outcome.warn_if_failed(),
}
},
)?;
} }
// 8. Create symlinks for the workspace packages // 8. Create symlinks for the workspace packages
@ -683,7 +729,8 @@ async fn sync_resolution_with_fs(
.finish( .finish(
snapshot, snapshot,
&package_partitions.packages, &package_partitions.packages,
Some(root_node_modules_dir_path), root_node_modules_dir_path,
progress_bar,
) )
.await?; .await?;
@ -920,20 +967,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( fn get_package_folder_id_folder_name(
folder_id: &NpmPackageCacheFolderId, folder_id: &NpmPackageCacheFolderId,
) -> String { ) -> String {
@ -1023,12 +1056,18 @@ fn junction_or_symlink_dir(
if symlink_err.kind() == std::io::ErrorKind::PermissionDenied => if symlink_err.kind() == std::io::ErrorKind::PermissionDenied =>
{ {
USE_JUNCTIONS.store(true, std::sync::atomic::Ordering::Relaxed); USE_JUNCTIONS.store(true, std::sync::atomic::Ordering::Relaxed);
junction::create(old_path, new_path).map_err(Into::into) junction::create(old_path, new_path)
.context("Failed creating junction in node_modules folder")
}
Err(symlink_err) => {
log::warn!(
"{} Unexpected error symlinking node_modules: {symlink_err}",
colors::yellow("Warning")
);
USE_JUNCTIONS.store(true, std::sync::atomic::Ordering::Relaxed);
junction::create(old_path, new_path)
.context("Failed creating junction in node_modules folder")
} }
Err(symlink_err) => Err(
AnyError::from(symlink_err)
.context("Failed creating symlink in node_modules folder"),
),
} }
} }

View file

@ -15,7 +15,6 @@ use crate::args::NpmInstallDepsProvider;
use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBar;
pub use self::common::NpmPackageFsResolver; pub use self::common::NpmPackageFsResolver;
pub use self::local::normalize_pkg_name_for_node_modules_deno_folder;
use self::global::GlobalNpmPackageResolver; use self::global::GlobalNpmPackageResolver;
use self::local::LocalNpmPackageResolver; use self::local::LocalNpmPackageResolver;

View file

@ -1,37 +1,44 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
mod byonm; mod byonm;
mod cache_dir;
mod common; mod common;
mod managed; mod managed;
use std::path::PathBuf; use std::borrow::Cow;
use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use common::maybe_auth_header_for_npm_registry;
use dashmap::DashMap; use dashmap::DashMap;
use deno_ast::ModuleSpecifier;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::serde_json; use deno_core::serde_json;
use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::registry::NpmPackageInfo; use deno_npm::registry::NpmPackageInfo;
use deno_runtime::deno_node::NodeRequireResolver; use deno_resolver::npm::ByonmInNpmPackageChecker;
use deno_resolver::npm::ByonmNpmResolver;
use deno_resolver::npm::CliNpmReqResolver;
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
use deno_runtime::deno_node::NodePermissions;
use deno_runtime::ops::process::NpmProcessStateProvider; use deno_runtime::ops::process::NpmProcessStateProvider;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq; use deno_semver::package::PackageReq;
use node_resolver::NpmResolver; use managed::cache::registry_info::get_package_url;
use managed::create_managed_in_npm_pkg_checker;
use node_resolver::InNpmPackageChecker;
use node_resolver::NpmPackageFolderResolver;
use crate::args::npm_registry_url;
use crate::file_fetcher::FileFetcher; use crate::file_fetcher::FileFetcher;
pub use self::byonm::ByonmCliNpmResolver; pub use self::byonm::CliByonmNpmResolver;
pub use self::byonm::CliNpmResolverByonmCreateOptions; pub use self::byonm::CliByonmNpmResolverCreateOptions;
pub use self::cache_dir::NpmCacheDir; pub use self::managed::CliManagedInNpmPkgCheckerCreateOptions;
pub use self::managed::CliNpmResolverManagedCreateOptions; pub use self::managed::CliManagedNpmResolverCreateOptions;
pub use self::managed::CliNpmResolverManagedSnapshotOption; pub use self::managed::CliNpmResolverManagedSnapshotOption;
pub use self::managed::ManagedCliNpmResolver; pub use self::managed::ManagedCliNpmResolver;
pub enum CliNpmResolverCreateOptions { pub enum CliNpmResolverCreateOptions {
Managed(CliNpmResolverManagedCreateOptions), Managed(CliManagedNpmResolverCreateOptions),
Byonm(CliNpmResolverByonmCreateOptions), Byonm(CliByonmNpmResolverCreateOptions),
} }
pub async fn create_cli_npm_resolver_for_lsp( pub async fn create_cli_npm_resolver_for_lsp(
@ -42,7 +49,7 @@ pub async fn create_cli_npm_resolver_for_lsp(
Managed(options) => { Managed(options) => {
managed::create_managed_npm_resolver_for_lsp(options).await managed::create_managed_npm_resolver_for_lsp(options).await
} }
Byonm(options) => byonm::create_byonm_npm_resolver(options), Byonm(options) => Arc::new(ByonmNpmResolver::new(options)),
} }
} }
@ -52,22 +59,43 @@ pub async fn create_cli_npm_resolver(
use CliNpmResolverCreateOptions::*; use CliNpmResolverCreateOptions::*;
match options { match options {
Managed(options) => managed::create_managed_npm_resolver(options).await, 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 CreateInNpmPkgCheckerOptions<'a> {
Managed(CliManagedInNpmPkgCheckerCreateOptions<'a>),
Byonm,
}
pub fn create_in_npm_pkg_checker(
options: CreateInNpmPkgCheckerOptions,
) -> Arc<dyn InNpmPackageChecker> {
match options {
CreateInNpmPkgCheckerOptions::Managed(options) => {
create_managed_in_npm_pkg_checker(options)
}
CreateInNpmPkgCheckerOptions::Byonm => Arc::new(ByonmInNpmPackageChecker),
} }
} }
pub enum InnerCliNpmResolverRef<'a> { pub enum InnerCliNpmResolverRef<'a> {
Managed(&'a ManagedCliNpmResolver), Managed(&'a ManagedCliNpmResolver),
#[allow(dead_code)] #[allow(dead_code)]
Byonm(&'a ByonmCliNpmResolver), Byonm(&'a CliByonmNpmResolver),
} }
pub trait CliNpmResolver: NpmResolver { pub trait CliNpmResolver: NpmPackageFolderResolver + CliNpmReqResolver {
fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver>; fn into_npm_pkg_folder_resolver(
fn into_require_resolver(self: Arc<Self>) -> Arc<dyn NodeRequireResolver>; self: Arc<Self>,
) -> Arc<dyn NpmPackageFolderResolver>;
fn into_npm_req_resolver(self: Arc<Self>) -> Arc<dyn CliNpmReqResolver>;
fn into_process_state_provider( fn into_process_state_provider(
self: Arc<Self>, self: Arc<Self>,
) -> Arc<dyn NpmProcessStateProvider>; ) -> Arc<dyn NpmProcessStateProvider>;
fn into_maybe_byonm(self: Arc<Self>) -> Option<Arc<CliByonmNpmResolver>> {
None
}
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver>; fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver>;
@ -80,20 +108,20 @@ pub trait CliNpmResolver: NpmResolver {
} }
} }
fn as_byonm(&self) -> Option<&ByonmCliNpmResolver> { fn as_byonm(&self) -> Option<&CliByonmNpmResolver> {
match self.as_inner() { match self.as_inner() {
InnerCliNpmResolverRef::Managed(_) => None, InnerCliNpmResolverRef::Managed(_) => None,
InnerCliNpmResolverRef::Byonm(inner) => Some(inner), 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( fn ensure_read_permission<'a>(
&self, &self,
req: &PackageReq, permissions: &mut dyn NodePermissions,
referrer: &ModuleSpecifier, path: &'a Path,
) -> Result<PathBuf, AnyError>; ) -> Result<Cow<'a, Path>, AnyError>;
/// Returns a hash returning the state of the npm resolver /// Returns a hash returning the state of the npm resolver
/// or `None` if the state currently can't be determined. /// or `None` if the state currently can't be determined.
@ -105,14 +133,19 @@ pub struct NpmFetchResolver {
nv_by_req: DashMap<PackageReq, Option<PackageNv>>, nv_by_req: DashMap<PackageReq, Option<PackageNv>>,
info_by_name: DashMap<String, Option<Arc<NpmPackageInfo>>>, info_by_name: DashMap<String, Option<Arc<NpmPackageInfo>>>,
file_fetcher: Arc<FileFetcher>, file_fetcher: Arc<FileFetcher>,
npmrc: Arc<ResolvedNpmRc>,
} }
impl NpmFetchResolver { impl NpmFetchResolver {
pub fn new(file_fetcher: Arc<FileFetcher>) -> Self { pub fn new(
file_fetcher: Arc<FileFetcher>,
npmrc: Arc<ResolvedNpmRc>,
) -> Self {
Self { Self {
nv_by_req: Default::default(), nv_by_req: Default::default(),
info_by_name: Default::default(), info_by_name: Default::default(),
file_fetcher, file_fetcher,
npmrc,
} }
} }
@ -147,11 +180,21 @@ impl NpmFetchResolver {
return info.value().clone(); return info.value().clone();
} }
let fetch_package_info = || async { let fetch_package_info = || async {
let info_url = npm_registry_url().join(name).ok()?; let info_url = get_package_url(&self.npmrc, name);
let file_fetcher = self.file_fetcher.clone(); let file_fetcher = self.file_fetcher.clone();
let registry_config = self.npmrc.get_registry_config(name);
// TODO(bartlomieju): this should error out, not use `.ok()`.
let maybe_auth_header =
maybe_auth_header_for_npm_registry(registry_config).ok()?;
// spawn due to the lsp's `Send` requirement // spawn due to the lsp's `Send` requirement
let file = deno_core::unsync::spawn(async move { let file = deno_core::unsync::spawn(async move {
file_fetcher.fetch_bypass_permissions(&info_url).await.ok() file_fetcher
.fetch_bypass_permissions_with_maybe_auth(
&info_url,
maybe_auth_header,
)
.await
.ok()
}) })
.await .await
.ok()??; .ok()??;
@ -162,3 +205,15 @@ impl NpmFetchResolver {
info info
} }
} }
pub const NPM_CONFIG_USER_AGENT_ENV_VAR: &str = "npm_config_user_agent";
pub fn get_npm_config_user_agent() -> String {
format!(
"deno/{} npm/? deno/{} {} {}",
env!("CARGO_PKG_VERSION"),
env!("CARGO_PKG_VERSION"),
std::env::consts::OS,
std::env::consts::ARCH
)
}

View file

@ -2,8 +2,6 @@
use std::sync::atomic::AtomicUsize; use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::time;
use deno_core::error::generic_error; use deno_core::error::generic_error;
use deno_core::error::type_error; use deno_core::error::type_error;
@ -12,10 +10,9 @@ use deno_core::op2;
use deno_core::v8; use deno_core::v8;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_core::OpState; use deno_core::OpState;
use deno_runtime::deno_permissions::create_child_permissions;
use deno_runtime::deno_permissions::ChildPermissionsArg; use deno_runtime::deno_permissions::ChildPermissionsArg;
use deno_runtime::deno_permissions::PermissionDescriptorParser;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_web::StartTime;
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use uuid::Uuid; use uuid::Uuid;
@ -59,21 +56,10 @@ struct PermissionsHolder(Uuid, PermissionsContainer);
pub fn op_pledge_test_permissions( pub fn op_pledge_test_permissions(
state: &mut OpState, state: &mut OpState,
#[serde] args: ChildPermissionsArg, #[serde] args: ChildPermissionsArg,
) -> Result<Uuid, AnyError> { ) -> Result<Uuid, deno_runtime::deno_permissions::ChildPermissionError> {
let token = Uuid::new_v4(); let token = Uuid::new_v4();
let permission_desc_parser = state
.borrow::<Arc<dyn PermissionDescriptorParser>>()
.clone();
let parent_permissions = state.borrow_mut::<PermissionsContainer>(); let parent_permissions = state.borrow_mut::<PermissionsContainer>();
let worker_permissions = { let worker_permissions = parent_permissions.create_child_permissions(args)?;
let mut parent_permissions = parent_permissions.inner.lock();
let perms = create_child_permissions(
permission_desc_parser.as_ref(),
&mut parent_permissions,
args,
)?;
PermissionsContainer::new(permission_desc_parser, perms)
};
let parent_permissions = parent_permissions.clone(); let parent_permissions = parent_permissions.clone();
if state.try_take::<PermissionsHolder>().is_some() { if state.try_take::<PermissionsHolder>().is_some() {
@ -83,7 +69,6 @@ pub fn op_pledge_test_permissions(
state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions)); state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions));
// NOTE: This call overrides current permission set for the worker // NOTE: This call overrides current permission set for the worker
state.put(worker_permissions.inner.clone());
state.put::<PermissionsContainer>(worker_permissions); state.put::<PermissionsContainer>(worker_permissions);
Ok(token) Ok(token)
@ -100,7 +85,6 @@ pub fn op_restore_test_permissions(
} }
let permissions = permissions_holder.1; let permissions = permissions_holder.1;
state.put(permissions.inner.clone());
state.put::<PermissionsContainer>(permissions); state.put::<PermissionsContainer>(permissions);
Ok(()) Ok(())
} else { } else {
@ -163,8 +147,8 @@ fn op_dispatch_bench_event(state: &mut OpState, #[serde] event: BenchEvent) {
#[op2(fast)] #[op2(fast)]
#[number] #[number]
fn op_bench_now(state: &mut OpState) -> Result<u64, AnyError> { fn op_bench_now(state: &mut OpState) -> Result<u64, std::num::TryFromIntError> {
let ns = state.borrow::<time::Instant>().elapsed().as_nanos(); let ns = state.borrow::<StartTime>().elapsed().as_nanos();
let ns_u64 = u64::try_from(ns)?; let ns_u64 = u64::try_from(ns)?;
Ok(ns_u64) Ok(ns_u64)
} }

View file

@ -46,7 +46,7 @@ pub fn op_jupyter_input(
state: &mut OpState, state: &mut OpState,
#[string] prompt: String, #[string] prompt: String,
is_password: bool, is_password: bool,
) -> Result<Option<String>, AnyError> { ) -> Option<String> {
let (last_execution_request, stdin_connection_proxy) = { let (last_execution_request, stdin_connection_proxy) = {
( (
state.borrow::<Arc<Mutex<Option<JupyterMessage>>>>().clone(), state.borrow::<Arc<Mutex<Option<JupyterMessage>>>>().clone(),
@ -58,11 +58,11 @@ pub fn op_jupyter_input(
if let Some(last_request) = maybe_last_request { if let Some(last_request) = maybe_last_request {
let JupyterMessageContent::ExecuteRequest(msg) = &last_request.content let JupyterMessageContent::ExecuteRequest(msg) = &last_request.content
else { else {
return Ok(None); return None;
}; };
if !msg.allow_stdin { if !msg.allow_stdin {
return Ok(None); return None;
} }
let content = InputRequest { let content = InputRequest {
@ -73,7 +73,7 @@ pub fn op_jupyter_input(
let msg = JupyterMessage::new(content, Some(&last_request)); let msg = JupyterMessage::new(content, Some(&last_request));
let Ok(()) = stdin_connection_proxy.lock().tx.send(msg) else { let Ok(()) = stdin_connection_proxy.lock().tx.send(msg) else {
return Ok(None); return None;
}; };
// Need to spawn a separate thread here, because `blocking_recv()` can't // Need to spawn a separate thread here, because `blocking_recv()` can't
@ -82,17 +82,25 @@ pub fn op_jupyter_input(
stdin_connection_proxy.lock().rx.blocking_recv() stdin_connection_proxy.lock().rx.blocking_recv()
}); });
let Ok(Some(response)) = join_handle.join() else { let Ok(Some(response)) = join_handle.join() else {
return Ok(None); return None;
}; };
let JupyterMessageContent::InputReply(msg) = response.content else { let JupyterMessageContent::InputReply(msg) = response.content else {
return Ok(None); return None;
}; };
return Ok(Some(msg.value)); return Some(msg.value);
} }
Ok(None) None
}
#[derive(Debug, thiserror::Error)]
pub enum JupyterBroadcastError {
#[error(transparent)]
SerdeJson(serde_json::Error),
#[error(transparent)]
ZeroMq(AnyError),
} }
#[op2(async)] #[op2(async)]
@ -102,7 +110,7 @@ pub async fn op_jupyter_broadcast(
#[serde] content: serde_json::Value, #[serde] content: serde_json::Value,
#[serde] metadata: serde_json::Value, #[serde] metadata: serde_json::Value,
#[serde] buffers: Vec<deno_core::JsBuffer>, #[serde] buffers: Vec<deno_core::JsBuffer>,
) -> Result<(), AnyError> { ) -> Result<(), JupyterBroadcastError> {
let (iopub_connection, last_execution_request) = { let (iopub_connection, last_execution_request) = {
let s = state.borrow(); let s = state.borrow();
@ -125,36 +133,35 @@ pub async fn op_jupyter_broadcast(
content, content,
err err
); );
err JupyterBroadcastError::SerdeJson(err)
})?; })?;
let jupyter_message = JupyterMessage::new(content, Some(&last_request)) let jupyter_message = JupyterMessage::new(content, Some(&last_request))
.with_metadata(metadata) .with_metadata(metadata)
.with_buffers(buffers.into_iter().map(|b| b.to_vec().into()).collect()); .with_buffers(buffers.into_iter().map(|b| b.to_vec().into()).collect());
iopub_connection.lock().send(jupyter_message).await?; iopub_connection
.lock()
.send(jupyter_message)
.await
.map_err(JupyterBroadcastError::ZeroMq)?;
} }
Ok(()) Ok(())
} }
#[op2(fast)] #[op2(fast)]
pub fn op_print( pub fn op_print(state: &mut OpState, #[string] msg: &str, is_err: bool) {
state: &mut OpState,
#[string] msg: &str,
is_err: bool,
) -> Result<(), AnyError> {
let sender = state.borrow_mut::<mpsc::UnboundedSender<StreamContent>>(); let sender = state.borrow_mut::<mpsc::UnboundedSender<StreamContent>>();
if is_err { if is_err {
if let Err(err) = sender.send(StreamContent::stderr(msg)) { if let Err(err) = sender.send(StreamContent::stderr(msg)) {
log::error!("Failed to send stderr message: {}", err); log::error!("Failed to send stderr message: {}", err);
} }
return Ok(()); return;
} }
if let Err(err) = sender.send(StreamContent::stdout(msg)) { if let Err(err) = sender.send(StreamContent::stdout(msg)) {
log::error!("Failed to send stdout message: {}", err); log::error!("Failed to send stdout message: {}", err);
} }
Ok(())
} }

View file

@ -16,13 +16,10 @@ use deno_core::op2;
use deno_core::v8; use deno_core::v8;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_core::OpState; use deno_core::OpState;
use deno_runtime::deno_permissions::create_child_permissions;
use deno_runtime::deno_permissions::ChildPermissionsArg; use deno_runtime::deno_permissions::ChildPermissionsArg;
use deno_runtime::deno_permissions::PermissionDescriptorParser;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use std::sync::atomic::AtomicUsize; use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
use std::sync::Arc;
use uuid::Uuid; use uuid::Uuid;
deno_core::extension!(deno_test, deno_core::extension!(deno_test,
@ -54,21 +51,10 @@ struct PermissionsHolder(Uuid, PermissionsContainer);
pub fn op_pledge_test_permissions( pub fn op_pledge_test_permissions(
state: &mut OpState, state: &mut OpState,
#[serde] args: ChildPermissionsArg, #[serde] args: ChildPermissionsArg,
) -> Result<Uuid, AnyError> { ) -> Result<Uuid, deno_runtime::deno_permissions::ChildPermissionError> {
let token = Uuid::new_v4(); let token = Uuid::new_v4();
let permission_desc_parser = state
.borrow::<Arc<dyn PermissionDescriptorParser>>()
.clone();
let parent_permissions = state.borrow_mut::<PermissionsContainer>(); let parent_permissions = state.borrow_mut::<PermissionsContainer>();
let worker_permissions = { let worker_permissions = parent_permissions.create_child_permissions(args)?;
let mut parent_permissions = parent_permissions.inner.lock();
let perms = create_child_permissions(
permission_desc_parser.as_ref(),
&mut parent_permissions,
args,
)?;
PermissionsContainer::new(permission_desc_parser, perms)
};
let parent_permissions = parent_permissions.clone(); let parent_permissions = parent_permissions.clone();
if state.try_take::<PermissionsHolder>().is_some() { if state.try_take::<PermissionsHolder>().is_some() {
@ -77,7 +63,6 @@ pub fn op_pledge_test_permissions(
state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions)); state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions));
// NOTE: This call overrides current permission set for the worker // NOTE: This call overrides current permission set for the worker
state.put(worker_permissions.inner.clone());
state.put::<PermissionsContainer>(worker_permissions); state.put::<PermissionsContainer>(worker_permissions);
Ok(token) Ok(token)
@ -94,7 +79,6 @@ pub fn op_restore_test_permissions(
} }
let permissions = permissions_holder.1; let permissions = permissions_holder.1;
state.put(permissions.inner.clone());
state.put::<PermissionsContainer>(permissions); state.put::<PermissionsContainer>(permissions);
Ok(()) Ok(())
} else { } else {
@ -166,7 +150,7 @@ fn op_register_test_step(
#[smi] parent_id: usize, #[smi] parent_id: usize,
#[smi] root_id: usize, #[smi] root_id: usize,
#[string] root_name: String, #[string] root_name: String,
) -> Result<usize, AnyError> { ) -> usize {
let id = NEXT_ID.fetch_add(1, Ordering::SeqCst); let id = NEXT_ID.fetch_add(1, Ordering::SeqCst);
let origin = state.borrow::<ModuleSpecifier>().to_string(); let origin = state.borrow::<ModuleSpecifier>().to_string();
let description = TestStepDescription { let description = TestStepDescription {
@ -185,7 +169,7 @@ fn op_register_test_step(
}; };
let sender = state.borrow_mut::<TestEventSender>(); let sender = state.borrow_mut::<TestEventSender>();
sender.send(TestEvent::StepRegister(description)).ok(); sender.send(TestEvent::StepRegister(description)).ok();
Ok(id) id
} }
#[op2(fast)] #[op2(fast)]

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

@ -22,8 +22,8 @@ use super::virtual_fs::FileBackedVfs;
pub struct DenoCompileFileSystem(Arc<FileBackedVfs>); pub struct DenoCompileFileSystem(Arc<FileBackedVfs>);
impl DenoCompileFileSystem { impl DenoCompileFileSystem {
pub fn new(vfs: FileBackedVfs) -> Self { pub fn new(vfs: Arc<FileBackedVfs>) -> Self {
Self(Arc::new(vfs)) Self(vfs)
} }
fn error_if_in_vfs(&self, path: &Path) -> FsResult<()> { fn error_if_in_vfs(&self, path: &Path) -> FsResult<()> {
@ -102,7 +102,7 @@ impl FileSystem for DenoCompileFileSystem {
&self, &self,
path: &Path, path: &Path,
recursive: bool, recursive: bool,
mode: u32, mode: Option<u32>,
) -> FsResult<()> { ) -> FsResult<()> {
self.error_if_in_vfs(path)?; self.error_if_in_vfs(path)?;
RealFs.mkdir_sync(path, recursive, mode) RealFs.mkdir_sync(path, recursive, mode)
@ -111,7 +111,7 @@ impl FileSystem for DenoCompileFileSystem {
&self, &self,
path: PathBuf, path: PathBuf,
recursive: bool, recursive: bool,
mode: u32, mode: Option<u32>,
) -> FsResult<()> { ) -> FsResult<()> {
self.error_if_in_vfs(&path)?; self.error_if_in_vfs(&path)?;
RealFs.mkdir_async(path, recursive, mode).await RealFs.mkdir_async(path, recursive, mode).await

View file

@ -5,7 +5,10 @@
#![allow(dead_code)] #![allow(dead_code)]
#![allow(unused_imports)] #![allow(unused_imports)]
use binary::StandaloneData;
use binary::StandaloneModules;
use deno_ast::MediaType; use deno_ast::MediaType;
use deno_cache_dir::npm::NpmCacheDir;
use deno_config::workspace::MappedResolution; use deno_config::workspace::MappedResolution;
use deno_config::workspace::MappedResolutionError; use deno_config::workspace::MappedResolutionError;
use deno_config::workspace::ResolverWorkspaceJsrPackage; use deno_config::workspace::ResolverWorkspaceJsrPackage;
@ -16,6 +19,7 @@ use deno_core::error::type_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::futures::FutureExt; use deno_core::futures::FutureExt;
use deno_core::v8_set_flags; use deno_core::v8_set_flags;
use deno_core::FastString;
use deno_core::FeatureChecker; use deno_core::FeatureChecker;
use deno_core::ModuleLoader; use deno_core::ModuleLoader;
use deno_core::ModuleSourceCode; use deno_core::ModuleSourceCode;
@ -25,9 +29,12 @@ use deno_core::RequestedModuleType;
use deno_core::ResolutionKind; use deno_core::ResolutionKind;
use deno_npm::npm_rc::ResolvedNpmRc; use deno_npm::npm_rc::ResolvedNpmRc;
use deno_package_json::PackageJsonDepValue; use deno_package_json::PackageJsonDepValue;
use deno_resolver::npm::NpmReqResolverOptions;
use deno_runtime::deno_fs; use deno_runtime::deno_fs;
use deno_runtime::deno_node::create_host_defined_options; use deno_runtime::deno_node::create_host_defined_options;
use deno_runtime::deno_node::NodeRequireLoader;
use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_node::PackageJsonResolver;
use deno_runtime::deno_permissions::Permissions; use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer; use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_tls::rustls::RootCertStore;
@ -37,10 +44,12 @@ use deno_runtime::permissions::RuntimePermissionDescriptorParser;
use deno_runtime::WorkerExecutionMode; use deno_runtime::WorkerExecutionMode;
use deno_runtime::WorkerLogLevel; use deno_runtime::WorkerLogLevel;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
use eszip::EszipRelativeFileBaseUrl;
use import_map::parse_from_json; use import_map::parse_from_json;
use node_resolver::analyze::NodeCodeTranslator; use node_resolver::analyze::NodeCodeTranslator;
use node_resolver::errors::ClosestPkgJsonError;
use node_resolver::NodeModuleKind;
use node_resolver::NodeResolutionMode; use node_resolver::NodeResolutionMode;
use serialization::DenoCompileModuleSource;
use std::borrow::Cow; use std::borrow::Cow;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
@ -53,78 +62,57 @@ use crate::args::CacheSetting;
use crate::args::NpmInstallDepsProvider; use crate::args::NpmInstallDepsProvider;
use crate::args::StorageKeyResolver; use crate::args::StorageKeyResolver;
use crate::cache::Caches; use crate::cache::Caches;
use crate::cache::DenoCacheEnvFsAdapter;
use crate::cache::DenoDirProvider; use crate::cache::DenoDirProvider;
use crate::cache::NodeAnalysisCache; use crate::cache::NodeAnalysisCache;
use crate::cache::RealDenoCacheEnv;
use crate::http_util::HttpClientProvider; use crate::http_util::HttpClientProvider;
use crate::node::CliCjsCodeAnalyzer; use crate::node::CliCjsCodeAnalyzer;
use crate::node::CliNodeCodeTranslator;
use crate::npm::create_cli_npm_resolver; use crate::npm::create_cli_npm_resolver;
use crate::npm::CliNpmResolverByonmCreateOptions; use crate::npm::create_in_npm_pkg_checker;
use crate::npm::CliByonmNpmResolverCreateOptions;
use crate::npm::CliManagedInNpmPkgCheckerCreateOptions;
use crate::npm::CliManagedNpmResolverCreateOptions;
use crate::npm::CliNpmResolver;
use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverCreateOptions;
use crate::npm::CliNpmResolverManagedCreateOptions;
use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CliNpmResolverManagedSnapshotOption;
use crate::npm::NpmCacheDir; use crate::npm::CreateInNpmPkgCheckerOptions;
use crate::resolver::CjsResolutionStore; use crate::resolver::CjsTracker;
use crate::resolver::CliNodeResolver; use crate::resolver::CliDenoResolverFs;
use crate::resolver::CliNpmReqResolver;
use crate::resolver::IsCjsResolverOptions;
use crate::resolver::NpmModuleLoader; use crate::resolver::NpmModuleLoader;
use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle; use crate::util::progress_bar::ProgressBarStyle;
use crate::util::v8::construct_v8_flags; use crate::util::v8::construct_v8_flags;
use crate::worker::CliMainWorkerFactory; use crate::worker::CliMainWorkerFactory;
use crate::worker::CliMainWorkerOptions; use crate::worker::CliMainWorkerOptions;
use crate::worker::ModuleLoaderAndSourceMapGetter; use crate::worker::CreateModuleLoaderResult;
use crate::worker::ModuleLoaderFactory; use crate::worker::ModuleLoaderFactory;
pub mod binary; pub mod binary;
mod file_system; mod file_system;
mod serialization;
mod virtual_fs; mod virtual_fs;
pub use binary::extract_standalone; pub use binary::extract_standalone;
pub use binary::is_standalone_binary; pub use binary::is_standalone_binary;
pub use binary::DenoCompileBinaryWriter; pub use binary::DenoCompileBinaryWriter;
use self::binary::load_npm_vfs;
use self::binary::Metadata; use self::binary::Metadata;
use self::file_system::DenoCompileFileSystem; use self::file_system::DenoCompileFileSystem;
struct WorkspaceEszipModule {
specifier: ModuleSpecifier,
inner: eszip::Module,
}
struct WorkspaceEszip {
eszip: eszip::EszipV2,
root_dir_url: Arc<ModuleSpecifier>,
}
impl WorkspaceEszip {
pub fn get_module(
&self,
specifier: &ModuleSpecifier,
) -> Option<WorkspaceEszipModule> {
if specifier.scheme() == "file" {
let specifier_key = EszipRelativeFileBaseUrl::new(&self.root_dir_url)
.specifier_key(specifier);
let module = self.eszip.get_module(&specifier_key)?;
let specifier = self.root_dir_url.join(&module.specifier).unwrap();
Some(WorkspaceEszipModule {
specifier,
inner: module,
})
} else {
let module = self.eszip.get_module(specifier.as_str())?;
Some(WorkspaceEszipModule {
specifier: ModuleSpecifier::parse(&module.specifier).unwrap(),
inner: module,
})
}
}
}
struct SharedModuleLoaderState { struct SharedModuleLoaderState {
eszip: WorkspaceEszip, cjs_tracker: Arc<CjsTracker>,
workspace_resolver: WorkspaceResolver, fs: Arc<dyn deno_fs::FileSystem>,
node_resolver: Arc<CliNodeResolver>, modules: StandaloneModules,
node_code_translator: Arc<CliNodeCodeTranslator>,
node_resolver: Arc<NodeResolver>,
npm_module_loader: Arc<NpmModuleLoader>, npm_module_loader: Arc<NpmModuleLoader>,
npm_req_resolver: Arc<CliNpmReqResolver>,
npm_resolver: Arc<dyn CliNpmResolver>,
workspace_resolver: WorkspaceResolver,
} }
#[derive(Clone)] #[derive(Clone)]
@ -132,6 +120,12 @@ struct EmbeddedModuleLoader {
shared: Arc<SharedModuleLoaderState>, shared: Arc<SharedModuleLoaderState>,
} }
impl std::fmt::Debug for EmbeddedModuleLoader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EmbeddedModuleLoader").finish()
}
}
pub const MODULE_NOT_FOUND: &str = "Module not found"; pub const MODULE_NOT_FOUND: &str = "Module not found";
pub const UNSUPPORTED_SCHEME: &str = "Unsupported scheme"; pub const UNSUPPORTED_SCHEME: &str = "Unsupported scheme";
@ -156,13 +150,27 @@ impl ModuleLoader for EmbeddedModuleLoader {
type_error(format!("Referrer uses invalid specifier: {}", err)) type_error(format!("Referrer uses invalid specifier: {}", err))
})? })?
}; };
let referrer_kind = if self
.shared
.cjs_tracker
.is_maybe_cjs(&referrer, MediaType::from_specifier(&referrer))?
{
NodeModuleKind::Cjs
} else {
NodeModuleKind::Esm
};
if self.shared.node_resolver.in_npm_package(&referrer) { if self.shared.node_resolver.in_npm_package(&referrer) {
return Ok( return Ok(
self self
.shared .shared
.node_resolver .node_resolver
.resolve(raw_specifier, &referrer, NodeResolutionMode::Execution)? .resolve(
raw_specifier,
&referrer,
referrer_kind,
NodeResolutionMode::Execution,
)?
.into_url(), .into_url(),
); );
} }
@ -184,13 +192,13 @@ impl ModuleLoader for EmbeddedModuleLoader {
self self
.shared .shared
.node_resolver .node_resolver
.resolve_package_sub_path_from_deno_module( .resolve_package_subpath_from_deno_module(
pkg_json.dir_path(), pkg_json.dir_path(),
sub_path.as_deref(), sub_path.as_deref(),
Some(&referrer), Some(&referrer),
referrer_kind,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
)? )?,
.into_url(),
), ),
Ok(MappedResolution::PackageJson { Ok(MappedResolution::PackageJson {
dep_result, dep_result,
@ -200,14 +208,15 @@ impl ModuleLoader for EmbeddedModuleLoader {
}) => match dep_result.as_ref().map_err(|e| AnyError::from(e.clone()))? { }) => match dep_result.as_ref().map_err(|e| AnyError::from(e.clone()))? {
PackageJsonDepValue::Req(req) => self PackageJsonDepValue::Req(req) => self
.shared .shared
.node_resolver .npm_req_resolver
.resolve_req_with_sub_path( .resolve_req_with_sub_path(
req, req,
sub_path.as_deref(), sub_path.as_deref(),
&referrer, &referrer,
referrer_kind,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
) )
.map(|res| res.into_url()), .map_err(AnyError::from),
PackageJsonDepValue::Workspace(version_req) => { PackageJsonDepValue::Workspace(version_req) => {
let pkg_folder = self let pkg_folder = self
.shared .shared
@ -220,13 +229,13 @@ impl ModuleLoader for EmbeddedModuleLoader {
self self
.shared .shared
.node_resolver .node_resolver
.resolve_package_sub_path_from_deno_module( .resolve_package_subpath_from_deno_module(
pkg_folder, pkg_folder,
sub_path.as_deref(), sub_path.as_deref(),
Some(&referrer), Some(&referrer),
referrer_kind,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
)? )?,
.into_url(),
) )
} }
}, },
@ -235,20 +244,19 @@ impl ModuleLoader for EmbeddedModuleLoader {
if let Ok(reference) = if let Ok(reference) =
NpmPackageReqReference::from_specifier(&specifier) NpmPackageReqReference::from_specifier(&specifier)
{ {
return self return Ok(self.shared.npm_req_resolver.resolve_req_reference(
.shared
.node_resolver
.resolve_req_reference(
&reference, &reference,
&referrer, &referrer,
referrer_kind,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
) )?);
.map(|res| res.into_url());
} }
if specifier.scheme() == "jsr" { if specifier.scheme() == "jsr" {
if let Some(module) = self.shared.eszip.get_module(&specifier) { if let Some(specifier) =
return Ok(module.specifier); self.shared.modules.resolve_specifier(&specifier)?
{
return Ok(specifier.clone());
} }
} }
@ -256,16 +264,17 @@ impl ModuleLoader for EmbeddedModuleLoader {
self self
.shared .shared
.node_resolver .node_resolver
.handle_if_in_node_modules(&specifier)? .handle_if_in_node_modules(&specifier)
.unwrap_or(specifier), .unwrap_or(specifier),
) )
} }
Err(err) Err(err)
if err.is_unmapped_bare_specifier() && referrer.scheme() == "file" => if err.is_unmapped_bare_specifier() && referrer.scheme() == "file" =>
{ {
let maybe_res = self.shared.node_resolver.resolve_if_for_npm_pkg( let maybe_res = self.shared.npm_req_resolver.resolve_if_for_npm_pkg(
raw_specifier, raw_specifier,
&referrer, &referrer,
referrer_kind,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
)?; )?;
if let Some(res) = maybe_res { if let Some(res) = maybe_res {
@ -343,82 +352,148 @@ impl ModuleLoader for EmbeddedModuleLoader {
); );
} }
let Some(module) = self.shared.eszip.get_module(original_specifier) else { match self.shared.modules.read(original_specifier) {
return deno_core::ModuleLoadResponse::Sync(Err(type_error(format!( Ok(Some(module)) => {
"{MODULE_NOT_FOUND}: {}", let media_type = module.media_type;
original_specifier let (module_specifier, module_type, module_source) =
)))); module.into_parts();
let is_maybe_cjs = match self
.shared
.cjs_tracker
.is_maybe_cjs(original_specifier, media_type)
{
Ok(is_maybe_cjs) => is_maybe_cjs,
Err(err) => {
return deno_core::ModuleLoadResponse::Sync(Err(type_error(
format!("{:?}", err),
)));
}
}; };
if is_maybe_cjs {
let original_specifier = original_specifier.clone(); let original_specifier = original_specifier.clone();
let module_specifier = module_specifier.clone();
let shared = self.shared.clone();
deno_core::ModuleLoadResponse::Async( deno_core::ModuleLoadResponse::Async(
async move { async move {
let code = module.inner.source().await.ok_or_else(|| { let source = match module_source {
type_error(format!("Module not found: {}", original_specifier)) DenoCompileModuleSource::String(string) => {
})?; Cow::Borrowed(string)
let code = arc_u8_to_arc_str(code) }
.map_err(|_| type_error("Module source is not utf-8"))?; DenoCompileModuleSource::Bytes(module_code_bytes) => {
match module_code_bytes {
Cow::Owned(bytes) => Cow::Owned(
crate::util::text_encoding::from_utf8_lossy_owned(bytes),
),
Cow::Borrowed(bytes) => String::from_utf8_lossy(bytes),
}
}
};
let source = shared
.node_code_translator
.translate_cjs_to_esm(&module_specifier, Some(source))
.await?;
let module_source = match source {
Cow::Owned(source) => ModuleSourceCode::String(source.into()),
Cow::Borrowed(source) => {
ModuleSourceCode::String(FastString::from_static(source))
}
};
Ok(deno_core::ModuleSource::new_with_redirect( Ok(deno_core::ModuleSource::new_with_redirect(
match module.inner.kind { module_type,
eszip::ModuleKind::JavaScript => ModuleType::JavaScript, module_source,
eszip::ModuleKind::Json => ModuleType::Json,
eszip::ModuleKind::Jsonc => {
return Err(type_error("jsonc modules not supported"))
}
eszip::ModuleKind::OpaqueData => {
unreachable!();
}
},
ModuleSourceCode::String(code.into()),
&original_specifier, &original_specifier,
&module.specifier, &module_specifier,
None, None,
)) ))
} }
.boxed_local(), .boxed_local(),
) )
} else {
let module_source = module_source.into_for_v8();
deno_core::ModuleLoadResponse::Sync(Ok(
deno_core::ModuleSource::new_with_redirect(
module_type,
module_source,
original_specifier,
module_specifier,
None,
),
))
}
}
Ok(None) => deno_core::ModuleLoadResponse::Sync(Err(type_error(
format!("{MODULE_NOT_FOUND}: {}", original_specifier),
))),
Err(err) => deno_core::ModuleLoadResponse::Sync(Err(type_error(
format!("{:?}", err),
))),
}
} }
} }
fn arc_u8_to_arc_str( impl NodeRequireLoader for EmbeddedModuleLoader {
arc_u8: Arc<[u8]>, fn ensure_read_permission<'a>(
) -> Result<Arc<str>, std::str::Utf8Error> { &self,
// Check that the string is valid UTF-8. permissions: &mut dyn deno_runtime::deno_node::NodePermissions,
std::str::from_utf8(&arc_u8)?; path: &'a std::path::Path,
// SAFETY: the string is valid UTF-8, and the layout Arc<[u8]> is the same as ) -> Result<Cow<'a, std::path::Path>, AnyError> {
// Arc<str>. This is proven by the From<Arc<str>> impl for Arc<[u8]> from the if self.shared.modules.has_file(path) {
// standard library. // allow reading if the file is in the snapshot
Ok(unsafe { return Ok(Cow::Borrowed(path));
std::mem::transmute::<std::sync::Arc<[u8]>, std::sync::Arc<str>>(arc_u8) }
})
self
.shared
.npm_resolver
.ensure_read_permission(permissions, path)
}
fn load_text_file_lossy(
&self,
path: &std::path::Path,
) -> Result<String, AnyError> {
Ok(self.shared.fs.read_text_file_lossy_sync(path, None)?)
}
fn is_maybe_cjs(
&self,
specifier: &ModuleSpecifier,
) -> Result<bool, ClosestPkgJsonError> {
let media_type = MediaType::from_specifier(specifier);
self.shared.cjs_tracker.is_maybe_cjs(specifier, media_type)
}
} }
struct StandaloneModuleLoaderFactory { struct StandaloneModuleLoaderFactory {
shared: Arc<SharedModuleLoaderState>, shared: Arc<SharedModuleLoaderState>,
} }
impl StandaloneModuleLoaderFactory {
pub fn create_result(&self) -> CreateModuleLoaderResult {
let loader = Rc::new(EmbeddedModuleLoader {
shared: self.shared.clone(),
});
CreateModuleLoaderResult {
module_loader: loader.clone(),
node_require_loader: loader,
}
}
}
impl ModuleLoaderFactory for StandaloneModuleLoaderFactory { impl ModuleLoaderFactory for StandaloneModuleLoaderFactory {
fn create_for_main( fn create_for_main(
&self, &self,
_root_permissions: PermissionsContainer, _root_permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter { ) -> CreateModuleLoaderResult {
ModuleLoaderAndSourceMapGetter { self.create_result()
module_loader: Rc::new(EmbeddedModuleLoader {
shared: self.shared.clone(),
}),
}
} }
fn create_for_worker( fn create_for_worker(
&self, &self,
_parent_permissions: PermissionsContainer, _parent_permissions: PermissionsContainer,
_permissions: PermissionsContainer, _permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter { ) -> CreateModuleLoaderResult {
ModuleLoaderAndSourceMapGetter { self.create_result()
module_loader: Rc::new(EmbeddedModuleLoader {
shared: self.shared.clone(),
}),
}
} }
} }
@ -437,13 +512,15 @@ impl RootCertStoreProvider for StandaloneRootCertStoreProvider {
} }
} }
pub async fn run( pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
mut eszip: eszip::EszipV2, let StandaloneData {
metadata: Metadata, fs,
) -> Result<i32, AnyError> { metadata,
let current_exe_path = std::env::current_exe().unwrap(); modules,
let current_exe_name = npm_snapshot,
current_exe_path.file_name().unwrap().to_string_lossy(); root_path,
vfs,
} = data;
let deno_dir_provider = Arc::new(DenoDirProvider::new(None)); let deno_dir_provider = Arc::new(DenoDirProvider::new(None));
let root_cert_store_provider = Arc::new(StandaloneRootCertStoreProvider { let root_cert_store_provider = Arc::new(StandaloneRootCertStoreProvider {
ca_stores: metadata.ca_stores, ca_stores: metadata.ca_stores,
@ -457,43 +534,50 @@ pub async fn run(
)); ));
// use a dummy npm registry url // use a dummy npm registry url
let npm_registry_url = ModuleSpecifier::parse("https://localhost/").unwrap(); let npm_registry_url = ModuleSpecifier::parse("https://localhost/").unwrap();
let root_path =
std::env::temp_dir().join(format!("deno-compile-{}", current_exe_name));
let root_dir_url = let root_dir_url =
Arc::new(ModuleSpecifier::from_directory_path(&root_path).unwrap()); Arc::new(ModuleSpecifier::from_directory_path(&root_path).unwrap());
let main_module = root_dir_url.join(&metadata.entrypoint_key).unwrap(); let main_module = root_dir_url.join(&metadata.entrypoint_key).unwrap();
let root_node_modules_path = root_path.join("node_modules"); let npm_global_cache_dir = root_path.join(".deno_compile_node_modules");
let npm_cache_dir = NpmCacheDir::new(
root_node_modules_path.clone(),
vec![npm_registry_url.clone()],
);
let npm_global_cache_dir = npm_cache_dir.get_cache_location();
let cache_setting = CacheSetting::Only; let cache_setting = CacheSetting::Only;
let (fs, npm_resolver, maybe_vfs_root) = match metadata.node_modules { let pkg_json_resolver = Arc::new(PackageJsonResolver::new(
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
));
let (in_npm_pkg_checker, npm_resolver) = match metadata.node_modules {
Some(binary::NodeModules::Managed { node_modules_dir }) => { Some(binary::NodeModules::Managed { node_modules_dir }) => {
// this will always have a snapshot // create an npmrc that uses the fake npm_registry_url to resolve packages
let snapshot = eszip.take_npm_snapshot().unwrap(); let npmrc = Arc::new(ResolvedNpmRc {
let vfs_root_dir_path = if node_modules_dir.is_some() { default_config: deno_npm::npm_rc::RegistryConfigWithUrl {
root_path.clone() registry_url: npm_registry_url.clone(),
} else { config: Default::default(),
npm_cache_dir.root_dir().to_owned() },
}; scopes: Default::default(),
let vfs = load_npm_vfs(vfs_root_dir_path.clone()) registry_configs: Default::default(),
.context("Failed to load npm vfs.")?; });
let npm_cache_dir = Arc::new(NpmCacheDir::new(
&DenoCacheEnvFsAdapter(fs.as_ref()),
npm_global_cache_dir,
npmrc.get_all_known_registries_urls(),
));
let snapshot = npm_snapshot.unwrap();
let maybe_node_modules_path = node_modules_dir let maybe_node_modules_path = node_modules_dir
.map(|node_modules_dir| vfs_root_dir_path.join(node_modules_dir)); .map(|node_modules_dir| root_path.join(node_modules_dir));
let fs = Arc::new(DenoCompileFileSystem::new(vfs)) let in_npm_pkg_checker =
as Arc<dyn deno_fs::FileSystem>; create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Managed(
CliManagedInNpmPkgCheckerCreateOptions {
root_cache_dir_url: npm_cache_dir.root_dir_url(),
maybe_node_modules_path: maybe_node_modules_path.as_deref(),
},
));
let npm_resolver = let npm_resolver =
create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed( create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
CliNpmResolverManagedCreateOptions { CliManagedNpmResolverCreateOptions {
snapshot: CliNpmResolverManagedSnapshotOption::Specified(Some( snapshot: CliNpmResolverManagedSnapshotOption::Specified(Some(
snapshot, snapshot,
)), )),
maybe_lockfile: None, maybe_lockfile: None,
fs: fs.clone(), fs: fs.clone(),
http_client_provider: http_client_provider.clone(), http_client_provider: http_client_provider.clone(),
npm_global_cache_dir, npm_cache_dir,
cache_setting, cache_setting,
text_only_progress_bar: progress_bar, text_only_progress_bar: progress_bar,
maybe_node_modules_path, maybe_node_modules_path,
@ -502,50 +586,54 @@ pub async fn run(
// this is only used for installing packages, which isn't necessary with deno compile // this is only used for installing packages, which isn't necessary with deno compile
NpmInstallDepsProvider::empty(), NpmInstallDepsProvider::empty(),
), ),
// create an npmrc that uses the fake npm_registry_url to resolve packages npmrc,
npmrc: Arc::new(ResolvedNpmRc {
default_config: deno_npm::npm_rc::RegistryConfigWithUrl {
registry_url: npm_registry_url.clone(),
config: Default::default(),
},
scopes: Default::default(),
registry_configs: Default::default(),
}),
lifecycle_scripts: Default::default(), lifecycle_scripts: Default::default(),
}, },
)) ))
.await?; .await?;
(fs, npm_resolver, Some(vfs_root_dir_path)) (in_npm_pkg_checker, npm_resolver)
} }
Some(binary::NodeModules::Byonm { Some(binary::NodeModules::Byonm {
root_node_modules_dir, root_node_modules_dir,
}) => { }) => {
let vfs_root_dir_path = root_path.clone();
let vfs = load_npm_vfs(vfs_root_dir_path.clone())
.context("Failed to load vfs.")?;
let root_node_modules_dir = let root_node_modules_dir =
root_node_modules_dir.map(|p| vfs.root().join(p)); root_node_modules_dir.map(|p| vfs.root().join(p));
let fs = Arc::new(DenoCompileFileSystem::new(vfs)) let in_npm_pkg_checker =
as Arc<dyn deno_fs::FileSystem>; create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Byonm);
let npm_resolver = create_cli_npm_resolver( let npm_resolver = create_cli_npm_resolver(
CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions { CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions {
fs: fs.clone(), fs: CliDenoResolverFs(fs.clone()),
pkg_json_resolver: pkg_json_resolver.clone(),
root_node_modules_dir, root_node_modules_dir,
}), }),
) )
.await?; .await?;
(fs, npm_resolver, Some(vfs_root_dir_path)) (in_npm_pkg_checker, npm_resolver)
} }
None => { None => {
let fs = Arc::new(deno_fs::RealFs) as Arc<dyn deno_fs::FileSystem>; // Packages from different registries are already inlined in the binary,
// so no need to create actual `.npmrc` configuration.
let npmrc = create_default_npmrc();
let npm_cache_dir = Arc::new(NpmCacheDir::new(
&DenoCacheEnvFsAdapter(fs.as_ref()),
npm_global_cache_dir,
npmrc.get_all_known_registries_urls(),
));
let in_npm_pkg_checker =
create_in_npm_pkg_checker(CreateInNpmPkgCheckerOptions::Managed(
CliManagedInNpmPkgCheckerCreateOptions {
root_cache_dir_url: npm_cache_dir.root_dir_url(),
maybe_node_modules_path: None,
},
));
let npm_resolver = let npm_resolver =
create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed( create_cli_npm_resolver(CliNpmResolverCreateOptions::Managed(
CliNpmResolverManagedCreateOptions { CliManagedNpmResolverCreateOptions {
snapshot: CliNpmResolverManagedSnapshotOption::Specified(None), snapshot: CliNpmResolverManagedSnapshotOption::Specified(None),
maybe_lockfile: None, maybe_lockfile: None,
fs: fs.clone(), fs: fs.clone(),
http_client_provider: http_client_provider.clone(), http_client_provider: http_client_provider.clone(),
npm_global_cache_dir, npm_cache_dir,
cache_setting, cache_setting,
text_only_progress_bar: progress_bar, text_only_progress_bar: progress_bar,
maybe_node_modules_path: None, maybe_node_modules_path: None,
@ -554,41 +642,53 @@ pub async fn run(
// this is only used for installing packages, which isn't necessary with deno compile // this is only used for installing packages, which isn't necessary with deno compile
NpmInstallDepsProvider::empty(), NpmInstallDepsProvider::empty(),
), ),
// Packages from different registries are already inlined in the ESZip,
// so no need to create actual `.npmrc` configuration.
npmrc: create_default_npmrc(), npmrc: create_default_npmrc(),
lifecycle_scripts: Default::default(), lifecycle_scripts: Default::default(),
}, },
)) ))
.await?; .await?;
(fs, npm_resolver, None) (in_npm_pkg_checker, npm_resolver)
} }
}; };
let has_node_modules_dir = npm_resolver.root_node_modules_path().is_some(); let has_node_modules_dir = npm_resolver.root_node_modules_path().is_some();
let node_resolver = Arc::new(NodeResolver::new( let node_resolver = Arc::new(NodeResolver::new(
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
npm_resolver.clone().into_npm_resolver(), in_npm_pkg_checker.clone(),
npm_resolver.clone().into_npm_pkg_folder_resolver(),
pkg_json_resolver.clone(),
));
let cjs_tracker = Arc::new(CjsTracker::new(
in_npm_pkg_checker.clone(),
pkg_json_resolver.clone(),
IsCjsResolverOptions {
detect_cjs: !metadata.workspace_resolver.package_jsons.is_empty(),
is_node_main: false,
},
)); ));
let cjs_resolutions = Arc::new(CjsResolutionStore::default());
let cache_db = Caches::new(deno_dir_provider.clone()); let cache_db = Caches::new(deno_dir_provider.clone());
let node_analysis_cache = NodeAnalysisCache::new(cache_db.node_analysis_db()); let node_analysis_cache = NodeAnalysisCache::new(cache_db.node_analysis_db());
let cli_node_resolver = Arc::new(CliNodeResolver::new( let npm_req_resolver =
cjs_resolutions.clone(), Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions {
fs.clone(), byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(),
node_resolver.clone(), fs: CliDenoResolverFs(fs.clone()),
npm_resolver.clone(), in_npm_pkg_checker: in_npm_pkg_checker.clone(),
)); node_resolver: node_resolver.clone(),
npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(),
}));
let cjs_esm_code_analyzer = CliCjsCodeAnalyzer::new( let cjs_esm_code_analyzer = CliCjsCodeAnalyzer::new(
node_analysis_cache, node_analysis_cache,
cjs_tracker.clone(),
fs.clone(), fs.clone(),
cli_node_resolver.clone(), None,
); );
let node_code_translator = Arc::new(NodeCodeTranslator::new( let node_code_translator = Arc::new(NodeCodeTranslator::new(
cjs_esm_code_analyzer, cjs_esm_code_analyzer,
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
in_npm_pkg_checker,
node_resolver.clone(), node_resolver.clone(),
npm_resolver.clone().into_npm_resolver(), npm_resolver.clone().into_npm_pkg_folder_resolver(),
pkg_json_resolver.clone(),
)); ));
let workspace_resolver = { let workspace_resolver = {
let import_map = match metadata.workspace_resolver.import_map { let import_map = match metadata.workspace_resolver.import_map {
@ -641,37 +741,36 @@ pub async fn run(
}; };
let module_loader_factory = StandaloneModuleLoaderFactory { let module_loader_factory = StandaloneModuleLoaderFactory {
shared: Arc::new(SharedModuleLoaderState { shared: Arc::new(SharedModuleLoaderState {
eszip: WorkspaceEszip { cjs_tracker: cjs_tracker.clone(),
eszip, fs: fs.clone(),
root_dir_url, modules,
}, node_code_translator: node_code_translator.clone(),
workspace_resolver, node_resolver: node_resolver.clone(),
node_resolver: cli_node_resolver.clone(),
npm_module_loader: Arc::new(NpmModuleLoader::new( npm_module_loader: Arc::new(NpmModuleLoader::new(
cjs_resolutions, cjs_tracker.clone(),
node_code_translator,
fs.clone(), fs.clone(),
cli_node_resolver, node_code_translator,
)), )),
npm_resolver: npm_resolver.clone(),
workspace_resolver,
npm_req_resolver,
}), }),
}; };
let permissions = { let permissions = {
let mut permissions = let mut permissions =
metadata.permissions.to_options(/* cli_arg_urls */ &[]); metadata.permissions.to_options(/* cli_arg_urls */ &[]);
// if running with an npm vfs, grant read access to it // grant read access to the vfs
if let Some(vfs_root) = maybe_vfs_root {
match &mut permissions.allow_read { match &mut permissions.allow_read {
Some(vec) if vec.is_empty() => { Some(vec) if vec.is_empty() => {
// do nothing, already granted // do nothing, already granted
} }
Some(vec) => { Some(vec) => {
vec.push(vfs_root.to_string_lossy().to_string()); vec.push(root_path.to_string_lossy().to_string());
} }
None => { None => {
permissions.allow_read = permissions.allow_read =
Some(vec![vfs_root.to_string_lossy().to_string()]); Some(vec![root_path.to_string_lossy().to_string()]);
}
} }
} }
@ -691,8 +790,6 @@ pub async fn run(
} }
checker checker
}); });
let permission_desc_parser =
Arc::new(RuntimePermissionDescriptorParser::new(fs.clone()));
let worker_factory = CliMainWorkerFactory::new( let worker_factory = CliMainWorkerFactory::new(
Arc::new(BlobStore::default()), Arc::new(BlobStore::default()),
// Code cache is not supported for standalone binary yet. // Code cache is not supported for standalone binary yet.
@ -705,7 +802,7 @@ pub async fn run(
Box::new(module_loader_factory), Box::new(module_loader_factory),
node_resolver, node_resolver,
npm_resolver, npm_resolver,
permission_desc_parser, pkg_json_resolver,
root_cert_store_provider, root_cert_store_provider,
permissions, permissions,
StorageKeyResolver::empty(), StorageKeyResolver::empty(),
@ -721,7 +818,6 @@ pub async fn run(
inspect_wait: false, inspect_wait: false,
strace_ops: None, strace_ops: None,
is_inspecting: false, is_inspecting: false,
is_npm_main: main_module.scheme() == "npm",
skip_op_registration: true, skip_op_registration: true,
location: metadata.location, location: metadata.location,
argv0: NpmPackageReqReference::from_specifier(&main_module) argv0: NpmPackageReqReference::from_specifier(&main_module)
@ -739,6 +835,7 @@ pub async fn run(
serve_port: None, serve_port: None,
serve_host: None, serve_host: None,
}, },
metadata.otel_config,
); );
// Initialize v8 once from the main thread. // Initialize v8 once from the main thread.

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