Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions .github/renovate.json5
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
'schedule:weekly',
'helpers:pinGitHubActionDigests',
],
// E2E fixtures deliberately pin the published toolchain versions the suites
// run against; bumping them is a manual, E2E-verified decision.
ignorePaths: ['**/node_modules/**', 'packages/vscode/e2e/**'],
// E2E fixtures pin exact published versions; their Rstack dependencies are
// updated by the `rstack toolchain` group below.
ignorePaths: ['**/node_modules/**'],
packageRules: [
// Use chore as semantic commit type for commit messages
{
Expand All @@ -32,10 +32,11 @@
// waiver list staying in sync. Listed after `all-patch` so these land in
// their own PR instead of the weekly patch roll-up.
//
// This includes `@rslint/core`, `@rstest/core` and `rstack`, whose ranges
// sit alongside the runtime `SUPPORT_MATRIX` (shared/versionCheck.ts).
// That matrix is a floor, not a pin: raising a devDependency does not move
// it. Lowering the floor stays a deliberate, separate edit.
// This includes `@rslint/core`, `@rstest/core` and `rstack`, which appear as
// root/development ranges and exact fixture pins. The runtime
// `SUPPORT_MATRIX` (shared/versionCheck.ts) is a floor, not a pin: raising a
// dependency does not move it. Lowering the floor stays a deliberate,
// separate edit.
{
groupName: 'rstack toolchain',
groupSlug: 'rstack',
Expand Down
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ tests-dist/
# F5 playground's materialized Node (see .vscode/tasks.json "playground node")
packages/vscode/.playground/

# E2E fixtures install published npm versions on demand; only
# their manifests and configs are tracked.
# E2E fixtures pin exact published versions and install them fresh;
# their generated node_modules directories and lockfiles stay disposable.
packages/vscode/e2e/fixtures/*/node_modules/
packages/vscode/e2e/fixtures/*/pnpm-lock.yaml
packages/vscode/e2e/lint/fixtures/pnpm-lock.yaml
Expand Down
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Thanks for your interest in contributing to Rstack Editor!

## Setup

- Node.js: the version is pinned in [`.nvmrc`](./.nvmrc) (`nvm use` / `fnm use`). Node >= 22.12 is required.
- Node.js: the version is pinned in [`.nvmrc`](./.nvmrc) (`nvm use` / `fnm use`). Node `^22.18.0 || >=24.3.0` is required.
- pnpm: pinned via the `packageManager` field — run `corepack enable pnpm` once and the right version is used automatically.

```bash
Expand All @@ -19,9 +19,9 @@ pnpm install # also installs the git hooks (rs hooks)
| `pnpm lint` | Lint + type check (`rs lint --type-check`) |
| `pnpm fmt` | Format the repo (`rs fmt`) |
| `pnpm test:unit` | Unit tests |
| `pnpm test:e2e` | Full E2E chain (installs fixtures, launches a real VS Code) |
| `pnpm test:e2e` | Full E2E chain (fresh-installs exactly pinned fixtures, launches a real VS Code) |

To try the extension: press F5 in VS Code at the repo root — the playground launch config starts a watch build, lets you pick a fixture project, and opens an Extension Development Host on it. Run `pnpm --filter rstack test:e2e:fixtures` once beforehand to install the fixture dependencies.
To try the extension: press F5 in VS Code at the repo root — the playground launch config starts a watch build, lets you pick a fixture project, and opens an Extension Development Host on it. Run `pnpm --filter rstack test:e2e:fixtures` once beforehand to install the exactly pinned fixture dependencies.

## Submitting changes

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ The extension resolves the tools from your project's `node_modules` and checks t
| -------------- | --------- |
| `@rslint/core` | `>=0.8.0` |
| `@rstest/core` | `>=0.6.0` |
| `rstack` | `>=0.6.1` |
| `rstack` | `>=0.7.0` |

## Documentation

Expand Down
12 changes: 7 additions & 5 deletions docs/adr/0001-node-runtime-selection.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
# Node runtime selection

The Node.js a test worker runs on is a **User Node runtime** — chosen by the extension from the user's own environment, the PATH `node` first and then the `node` the user's interactive shell would give them — and it must satisfy a uniform floor, `^22.18.0 || >=23.6.0`. The **VS Code Node runtime** is never a candidate. When nothing satisfies the floor, the test stack reports a status and runs nothing.
The Node.js a test worker runs on is a **User Node runtime** — chosen by the extension from the user's own environment, the PATH `node` first and then the `node` the user's interactive shell would give them — and it must satisfy a uniform floor, `^22.18.0 || >=24.3.0`. The **VS Code Node runtime** is never a candidate. When nothing satisfies the floor, the test stack reports a status and runs nothing.

## Why the floor is `^22.18.0 || >=23.6.0`
## Why the floor is `^22.18.0 || >=24.3.0`

The strictest thing a worker does is load an `rstack.config.*`, which rstack's shipped shim loads through `@rstackjs/load-config` with `loader: 'native'`. That path rethrows with no jiti fallback (`rstack-cli` `packages/rstack/src/config.ts:174`, bundled verbatim into `rstack@0.4.0`'s `dist/687.js`), and `native` never consults `process.features.typescript` — it calls `import()` directly. So the worker needs Node's native TypeScript stripping, which was unflagged in 23.6.0 and backported to the LTS line in 22.18.0. That release history is why the floor is a disjunction rather than a single version: 23.0–23.5 compare above 22.18.0 yet predate the unflagging, and `^22.18.0 || >=23.6.0` names exactly the versions that strip by default.
The strictest thing a worker does is load an `rstack.config.*`, which rstack's shipped shim loads through `@rstackjs/load-config` with `loader: 'native'`. That path rethrows with no jiti fallback (`rstack-cli` `packages/rstack/src/config.ts:174`, bundled verbatim into `rstack@0.4.0`'s `dist/687.js`), and `native` never consults `process.features.typescript` — it calls `import()` directly. So the worker needs Node's native TypeScript stripping, which was unflagged in 23.6.0 and backported to the LTS line in 22.18.0; that is the type-stripping lower bound.

Native type stripping is the _only_ thing on the worker's path that needs more than the declared `engines` of the packages involved: `@rstest/core` 0.11.6 and `@rsbuild/core` declare `^20.19.0 || >=22.12.0`, `rstack` 0.4.0 declares `>=22.12.0`, and `Module.registerHooks` (used by rstack's `freshImport`, added in 22.15) has a three-level fallback.
The supported rstack line adds a narrower declared contract. Since rstack 0.7.0, `engines.node` is `^22.18.0 || >=24.3.0`: Node 23 is unsupported, and Node 24.0–24.2 still emits the experimental type-stripping warning that 24.3.0 removes ([rstackjs/rstack-cli#427](https://github.com/rstackjs/rstack-cli/pull/427)). The extension therefore intersects native type-stripping availability with rstack's declared engines rather than accepting a runtime the supported toolchain rejects. The range follows those engines as that contract changes.

Other packages on the worker path allow older runtimes: `@rstest/core` and `@rsbuild/core` declare `^20.19.0 || >=22.12.0`, and `Module.registerHooks` (used by rstack's `freshImport`, added in 22.15) has a three-level fallback.

## Considered options

**A per-project floor** — 22.18.0 only for projects driven by an `rstack.config.*`, 22.12.0 for a native `rstest.config.*`. Rejected: it buys back Node 20.19–22.17 at the cost of a second code path through every call site. Node 20 left support on 2026-04-30, so the users it genuinely serves are those on Node 22.12–22.17 — a supported LTS line, needing only a patch-level update within 22.x. That is a low-friction ask, and one uniform floor is a support contract the README and the status bar can each state in one sentence.
**A per-project floor** — the full rstack floor only for projects driven by an `rstack.config.*`, with looser floors where package contracts allow. Rejected: it buys back Node 20.19–22.17, Node 23 and Node 24.0–24.2 at the cost of a second code path through every call site. Node 20 is out of support; Node 23 is an unsupported odd-numbered line; and users on Node 22 or 24 need only a patch-level update within that line. That is a low-friction ask, and one uniform floor is a support contract the README and the status bar can each state in one sentence.

**A capability probe instead of a version check** — asking each candidate for `process.features.typescript` rather than comparing semver. Rejected: `process.features.typescript` is itself Stability 1.2 (release candidate) and its value set has moved (`"transform"` existed on 22.18–25.1, removed in 26.0.0). More decisively, `loader: 'native'` never reads it, so the probe would not be testing the condition that actually fails. A floor is a contract; contracts are declared, not sniffed.

Expand Down
2 changes: 1 addition & 1 deletion docs/adr/0002-fmt-lsp-on-user-node-runtime.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Formatting through the `rs fmt` language server

Document formatting is served by **`rs fmt --lsp`** — one language server per detected workspace folder, spawned with its cwd at the folder root, running on a **User Node runtime** that satisfies the floor ADR 0001 sets (`^22.18.0 || >=23.6.0`). It replaces a spawn-per-request `rs fmt --stdin-filepath` MVP that ran on the VS Code Node runtime and kept one pre-spawned process warm for the active editor. The floor for the project's `rstack` rises to `>=0.5.2`, the first release that ships `--lsp`; older releases surface as `version mismatch` and format nothing. The raise is **toolchain-wide by decision**, not fmt-scoped: `SUPPORT_MATRIX.rstack` is one entry and the Rstest bridge gates on the same one, so a project on `rstack` 0.3.5–0.5.1 also stops running tests until it upgrades. A per-stack floor was considered and rejected — "which rstack does the extension support?" should have one answer, and the mismatch status names the required version.
Document formatting is served by **`rs fmt --lsp`** — one language server per detected workspace folder, spawned with its cwd at the folder root, running on a **User Node runtime** that satisfies the floor ADR 0001 sets. It replaces a spawn-per-request `rs fmt --stdin-filepath` MVP that ran on the VS Code Node runtime and kept one pre-spawned process warm for the active editor. The floor for the project's `rstack` rises to `>=0.5.2`, the first release that ships `--lsp`; older releases surface as `version mismatch` and format nothing. The raise is **toolchain-wide by decision**, not fmt-scoped: `SUPPORT_MATRIX.rstack` is one entry and the Rstest bridge gates on the same one, so a project on `rstack` 0.3.5–0.5.1 also stops running tests until it upgrades. A per-stack floor was considered and rejected — "which rstack does the extension support?" should have one answer, and the mismatch status names the required version.

## Why a server rather than a process per request

Expand Down
Loading