Skip to content

feat: support devEngines for Node.js runtime and package manager selection#1760

Draft
fengmk2 wants to merge 2 commits into
mainfrom
feat/dev-engines
Draft

feat: support devEngines for Node.js runtime and package manager selection#1760
fengmk2 wants to merge 2 commits into
mainfrom
feat/dev-engines

Conversation

@fengmk2
Copy link
Copy Markdown
Member

@fengmk2 fengmk2 commented Jun 4, 2026

Implements rfcs/dev-engines.md (included in this PR), with a compatibility-first rule: existing .node-version and packageManager sources keep winning, devEngines becomes the default for new projects, and conflicts are surfaced by vp env doctor instead of silently resolved.

Changes

  • Detect devEngines.packageManager between the packageManager field and lockfiles; version ranges resolve against downloaded versions first, then the registry, and are never frozen into an exact pin
  • Auto-pin (lockfile detection, vp migrate) writes devEngines.packageManager instead of the packageManager field
  • devEngines.runtime now ranks above engines.node for Node.js version resolution
  • vp env pin / unpin target devEngines.runtime when no .node-version exists, with a --target override; an existing engines.node is never modified
  • vp env doctor gains a devEngines section with semver-aware conflict checks

Validation

just check, just test, crate-scoped cargo clippy --deny warnings, vp check, and pnpm test:unit all pass. Snap diffs reviewed: the packageManager to devEngines.packageManager swap plus deduplicated invalid-engines warnings.

Follow-ups

  • Templates: add devEngines.runtime alongside the kept engines.node
  • vp migrate: .nvmrc / Volta pins to devEngines.runtime with alias-to-semver conversion

Closes #864


Note

Medium Risk
Changes core Node and package-manager resolution plus automatic package.json mutations on pin and lockfile detection; broad test coverage limits regressions but wrong resolution would affect every project command.

Overview
Implements the devEngines RFC so devEngines.runtime and devEngines.packageManager drive development tooling alongside existing pins, without breaking .node-version or the top-level packageManager field when they already exist.

Node.js: Resolution now prefers devEngines.runtime over engines.node (still below .node-version). vp env pin / unpin follow a compatibility-first write target (existing .node-version wins; otherwise devEngines.runtime; --target overrides). Pinning can optionally sync devEngines.runtime when it conflicts with a new .node-version, and engines.node is never modified.

Package managers: Detection inserts devEngines.packageManager after packageManager and before lockfiles; semver ranges resolve from cache then the registry and are not frozen into an exact packageManager pin. Lockfile/default auto-pin writes devEngines.packageManager instead of packageManager. Mismatches between packageManager and devEngines emit warnings (future hard error).

Shared infrastructure: Lenient devEngines parsing (OnFail, array/single shapes), edit_json_object for indentation-preserving package.json edits, and a new UnsupportedDevEnginesPackageManager error.

Diagnostics & docs: vp env doctor adds a semver-aware devEngines section; user guides and CLI snap tests reflect the new auto-pin shape.

Reviewed by Cursor Bugbot for commit c13a831. Configure here.

@fengmk2 fengmk2 self-assigned this Jun 4, 2026
@netlify
Copy link
Copy Markdown

netlify Bot commented Jun 4, 2026

Deploy Preview for viteplus-preview canceled.

Name Link
🔨 Latest commit c13a831
🔍 Latest deploy log https://app.netlify.com/projects/viteplus-preview/deploys/6a2148585da1a30008d78824

…ction

Implement rfcs/dev-engines.md with a compatibility-first rule: existing
.node-version and packageManager sources keep winning for writes, while
devEngines.runtime and devEngines.packageManager become the recommended
default for new projects. Conflicts are surfaced by vp env doctor
(semver-aware), never silently resolved.

- Parse devEngines per the OpenJS spec (single/array shapes, optional
  version, onFail with positional defaults, lenient on malformed entries)
- Detect devEngines.packageManager between the packageManager field and
  lockfiles; resolve version ranges (cached-first, then registry); never
  freeze a range into an exact pin
- Auto-pin lockfile-detected package managers into devEngines.packageManager
  instead of the packageManager field (vp migrate included)
- Move devEngines.runtime above engines.node in Node.js version resolution
- vp env pin/unpin write devEngines.runtime when no .node-version exists,
  with a --target override; existing engines.node is never modified
- vp env doctor gains a devEngines section with semver-aware conflict checks

Closes #864
@fengmk2 fengmk2 force-pushed the feat/dev-engines branch from 94c0be0 to f67b97f Compare June 4, 2026 08:02
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 4, 2026

✅ Staging deployment successful!

Preview: https://viteplus-staging.void.app/
Commit: f67b97f

Mirror the test matrix from npm/npm-install-checks test/check-dev-engines.js,
mapped to Vite+ semantics: npm validates the current environment and throws
on malformed input, while Vite+ provisions the environment and reads
devEngines leniently (rfcs/dev-engines.md), so npm's throw cases pin down our
skip/treat-as-default behavior for the same inputs.

- vite_shared: empty arrays, non-object devEngines/fields, non-string
  name/version/onFail values, extra entry properties, unrecognized sub-fields
- vite_install: empty-array fallthrough, all-unsupported array onFail
  handling, first-supported-entry selection; extract
  dev_engines_package_manager_conflict_message() for direct unit tests
- vite_js_runtime: runtime array form, no-node-entry and name-only
  fallthrough to engines.node
- vite_global_cli: extract collect_dev_engines_findings() and cover all
  doctor findings (conflicts, spec violations, notes, all-satisfied)
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Jun 4, 2026

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit c13a831. Configure here.

});
let updated = vite_shared::edit_json_object(&content, |obj| {
if let Some(dev_engines) = obj.get_mut("devEngines").and_then(|v| v.as_object_mut()) {
dev_engines.insert("packageManager".into(), entry);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-pin wipes PM array

Medium Severity

When auto-pin runs after lockfile or default detection, set_dev_engines_package_manager_field assigns a single packageManager object via insert, which replaces an existing devEngines.packageManager array and drops alternate entries the spec allows.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c13a831. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support devEngines field

1 participant