feat(js_runtime): add vite_js_runtime crate for JavaScript runtime management#508
feat(js_runtime): add vite_js_runtime crate for JavaScript runtime management#508
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
25c0076 to
1dcbb99
Compare
2db1648 to
d71ed84
Compare
6e4df5c to
b064bbf
Compare
d4885e8 to
8922738
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds a new vite_js_runtime crate for managing JavaScript runtime downloads and caching, specifically for Node.js with a design that allows future support for Bun and Deno. The implementation provides automatic version resolution from package.json's devEngines.runtime field, integrity verification via SHA256 checksums, and concurrent-safe caching.
Changes:
- Adds
vite_js_runtimecrate with provider-based architecture for runtime management - Implements Node.js download support with version resolution (exact versions and semver ranges)
- Includes comprehensive RFC document describing the design and architecture
Reviewed changes
Copilot reviewed 14 out of 15 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| rfcs/js-runtime.md | Comprehensive RFC documenting the design, architecture, and implementation details |
| crates/vite_js_runtime/src/runtime.rs | Main runtime download orchestration and project-based configuration support |
| crates/vite_js_runtime/src/providers/node.rs | Node.js-specific provider with version resolution and caching |
| crates/vite_js_runtime/src/download.rs | Generic download utilities with retry logic and hash verification |
| crates/vite_js_runtime/src/dev_engines.rs | Package.json parsing with formatting preservation |
| crates/vite_js_runtime/src/provider.rs | Provider trait definition for runtime abstraction |
| crates/vite_js_runtime/src/platform.rs | Platform detection for OS and architecture |
| crates/vite_js_runtime/src/lib.rs | Public API exports |
| crates/vite_js_runtime/src/error.rs | Error type definitions |
| crates/vite_js_runtime/Cargo.toml | Crate dependencies and configuration |
| Cargo.toml | Workspace configuration with new dependencies |
| Cargo.lock | Dependency lock file updates |
| CLAUDE.md | Documentation on path type usage |
| .typos.toml | Typo dictionary for "Jod" LTS codename |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
cursor review |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
…nagement
This crate provides functionality to download and cache JavaScript runtimes
like Node.js. Features include:
- Runtime spec parsing (e.g., "node@22.13.1")
- Automatic platform detection (Linux/macOS/Windows + x64/arm64)
- Download with retry logic and SHASUMS256.txt integrity verification
- Caching to ~/.cache/vite/js_runtime/{runtime}/{version}/{platform}/
- Atomic operations for concurrent-safe downloads
Also includes RFC document at rfcs/js-runtime.md describing the design.
The vite_js_runtime crate is a pure Rust library and does not need NAPI bindings for JavaScript CLI usage.
Remove #[ignore] from integration tests so they run with the regular test suite. The tests download a real Node.js version and verify cache behavior.
…ection
Implement file-based locking in move_to_cache() to prevent race conditions
when multiple processes/threads try to install the same runtime version
concurrently. This follows the same pattern used in vite_install's
package_manager.rs.
Changes:
- Add version-specific lock file ({version}.lock) acquisition before move
- Use tokio::task::spawn_blocking to wrap blocking File::lock() call
- Re-check target existence after acquiring lock (another process may complete)
- Add test_concurrent_downloads() to verify no corruption with 4 parallel tasks
Remove unused dependencies detected by cargo-shear: - serde from dependencies - httpmock from dev-dependencies - test-log from dev-dependencies Add cargo-shear ignore for vite_js_runtime workspace dependency (pending integration with vite_install).
Replace blanket #![allow(clippy::disallowed_types)] with targeted #[expect(clippy::disallowed_types)] on specific functions that require std::path::Path for external crate interop (tempfile, tar, zip). - Change return types to use vite_str::Str instead of String - Use vite_str::format! instead of std::format! - Add targeted expects only where std::path::Path is needed
…ePath Use project-standard path types instead of std::path::Path in helper functions, eliminating most #[expect(clippy::disallowed_types)] attributes. Changes: - Convert TempDir::path() to AbsolutePathBuf at the call site - Update download_file, verify_file_hash, extract_archive, extract_tar_gz, extract_zip, and move_to_cache to use &AbsolutePath - Keep copy_dir_recursive with std::path::Path due to recursive entry.path()
- Update copy_dir_recursive to use &AbsolutePath instead of &std::path::Path - Document vite_path usage patterns in CLAUDE.md
Remove unnecessary runtime spec parsing functionality: - Remove parse_runtime_spec function and related tests - Remove InvalidRuntimeSpec and UnsupportedRuntime error types - Update RFC to reflect simplified API
Add support for VITE_NODE_DIST_MIRROR environment variable to override the default Node.js distribution URL. This is useful for corporate environments or regions where nodejs.org might be slow or blocked. Example: VITE_NODE_DIST_MIRROR=https://npmmirror.com/mirrors/node
- Add JsRuntimeProvider trait for runtime-specific logic - Create providers/ module with NodeProvider implementation - Extract generic download utilities to download.rs - Simplify platform.rs by moving runtime-specific logic to providers - Update JsRuntime to store relative paths from provider - Enable zip crate on all platforms for future Bun/Deno support - Update RFC to document the new trait-based architecture This refactoring separates concerns between generic download logic and runtime-specific details, making it straightforward to add support for Bun and Deno in the future.
Just throw error if fs::rename fails instead of falling back to recursive copy. Also removes the unused copy_dir_recursive function.
If the install directory exists but the binary doesn't, it indicates an incomplete or corrupted installation. Now we detect this and remove the incomplete directory before downloading again. Added test_incomplete_installation_cleanup to verify this behavior.
The platform is redundant in the cache path since a runtime always runs
on the current OS. Simplifies the structure from
`{runtime}/{version}/{platform}/` to `{runtime}/{version}/`.
…e download Add a new function `download_runtime_for_project()` that reads `devEngines.runtime` from a project's package.json and downloads the appropriate Node.js version. Features: - Parse devEngines.runtime as single object or array of runtimes - Resolve semver ranges (^, ~, etc.) using node-semver crate - Fetch and cache version index from nodejs.org/dist/index.json (1-hour TTL) - Fall back to latest Node.js if no configuration found - Support custom mirrors via VITE_NODE_DIST_MIRROR env var New public API: - `download_runtime_for_project(project_path)` - project-based download - `NodeProvider::fetch_version_index()` - fetch cached version list - `NodeProvider::resolve_version(version_req)` - resolve semver range - `NodeProvider::resolve_latest_version()` - get latest version New error variants: - VersionIndexParseFailed, NoMatchingVersion, Json, SemverRange
…olution - Add `fetch_with_cache_headers()` for conditional requests with If-None-Match - Handle 304 Not Modified responses to refresh cache TTL without re-downloading - Parse Cache-Control max-age header for dynamic TTL - Fix `resolve_version()` to return highest matching version, not first - Fix `resolve_latest_version()` to return highest LTS version, not first entry - Extract logic into testable functions with comprehensive unit tests using mock data
After downloading a runtime without a specified version, write the resolved version to devEngines.runtime in package.json. This allows subsequent executions to skip version resolution. - Add update_runtime_version() to update package.json with resolved version - Detect and preserve original indentation (2/4 spaces or tabs) - Handle all runtime config formats (single object, array, missing) - Only write back when no version was specified (skip when version range exists) - Add serde_json preserve_order feature to maintain key order
Add documentation for the new feature that writes resolved runtime version back to package.json when no version was specified.
- Skip network resolution for exact versions (e.g., "20.18.0") - Check locally cached versions first for range versions (e.g., "^20.18.0") - Normalize 'v' prefix in exact versions to avoid double 'v' in URLs This reduces unnecessary network requests when: 1. An exact version is specified - use it directly without validation 2. A range is specified and a cached version satisfies it - use cached version
- Skip network resolution for exact versions (e.g., "20.18.0") - Check locally cached versions first for range versions (e.g., "^20.18.0") - Normalize 'v' prefix in exact versions to avoid double 'v' in URLs - Use semver max() instead of sort() for finding highest matching version This reduces unnecessary network requests when: 1. An exact version is specified - use it directly without validation 2. A range is specified and a cached version satisfies it - use cached version
…ntime name When devEngines.runtime is a single object with a different runtime name (e.g., "deno"), calling update_or_create_runtime with "node" now correctly converts to array format instead of overwriting the existing runtime's version. This prevents corruption of user configuration data where a deno runtime with version "^2.0.0" would incorrectly have its version replaced.
The move_to_cache function was acquiring a file lock but immediately dropping it after spawn_blocking completed. This caused the lock to be released before the actual rename operation, defeating the concurrent download protection. Now the lock_file handle is stored and kept alive until after the fs::rename call completes, ensuring the critical section is protected.
… error On Linux systems where /tmp is mounted as tmpfs and the cache directory is on a different filesystem, fs::rename fails with EXDEV (cross-device link) error. This is common on many Linux distributions. Use TempDir::new_in(&cache_dir) instead of TempDir::new() to ensure the temp directory and cache directory are on the same filesystem, allowing the atomic rename operation to succeed.
Add compile_error! for unsupported operating systems and CPU architectures to provide clear error messages during compilation instead of cryptic "missing return value" errors. Supported platforms: - OS: Linux, macOS, Windows - Arch: x86_64, aarch64
8922738 to
a6745a0
Compare
|
cursor review |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a6745a08d3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

This crate provides functionality to download and cache JavaScript runtimes
like Node.js. Features include:
Also includes RFC document at rfcs/js-runtime.md describing the design.
Note
Adds a new
vite_js_runtimecrate to download, verify, extract, and cache JavaScript runtimes (initially Node.js) with an extensible provider architecture.NodeProviderwith platform detection, SHASUMS256 verification, retrying downloads, ZIP/TAR.GZ extraction, and file-lock–based atomic installs into~/.cache/vite/js_runtime/{runtime}/{version}node-semver, ETag/TTL-cached version index, and uses cached versions when possibledevEngines.runtimefrompackage.json, resolve version, download, and optionally write back the resolved version while preserving JSON formattingrfcs/js-runtime.mdand minor config updates (typos, Cargo.lock)Written by Cursor Bugbot for commit 8922738. This will update automatically on new commits. Configure here.