You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(vscode): stop forcing color when the project config disables it (#33)
* fix(vscode): stop forcing color when the project config disables it
The master hard-coded FORCE_COLOR=1 into the rstest worker env (as the
upstream extension does), so a project whose config sets
process.env.NO_COLOR = '1' at load time hit Node's "'NO_COLOR' env is
ignored" warning in every pool process.
Mirror the CLI's getForceColorEnv semantics instead (adaptation #9): the
master injects FORCE_COLOR=1 into the composed spawn env only when the
user set neither color standard (marking the injection), and the worker
retracts the marked injection right after config load — the CLI's own
decision point — when the config turned color off. A user-set FORCE_COLOR
beside a config-set NO_COLOR still warns, exactly as the bare CLI does.
Also apply the review cleanups from the same pass: versionOk now retires
a root's not-installed latch too (dropping the paired installed() calls),
versionMismatch gains the same re-raise dedupe as notInstalled, the
missing-package verdict rides on RslintResolutionError instead of a
partial code table, missingDependencyCauseOf moves to shared/ for the
lint/fmt config loaders (#30), stackCommandTitle loses its single-value
verb parameter and now also feeds the status-bar hover, the unused
NormalizedConfigResult.reason discriminant is dropped, the lint
core-selection failure wording is spelled once, and the worker spawn
reuses one resolved rstest path and one IPC send callback.
* fix(vscode): address review findings on quiet classification, latches, color marker
Three review findings, each verified before fixing:
- The missing-cwd spawn refusal threw a plain Error, so callers re-logged
the already-warned stale-project state as an error with a stack. The
guard now throws ReportedRstestResolutionError (which gained an optional
message), and the four catch sites above RstestApi share one
logUnlessReported helper next to the class instead of re-deciding.
- The re-raise dedupe in versionMismatch/notInstalled short-circuited
before the package-state restatement, so a crash latched between two
identical verdicts survived a retry that aborted before spawning. Both
observations now fold into one #observePackageState that restates
first and skips only the repaint.
- retractForceColorIfDisabled left RSTACK_FORCE_COLOR_INJECTED in the env
on the no-NO_COLOR path; the marker is now removed once the decision is
complete, so pool processes and user test code never observe it.
* fix(vscode): keep the spawn-race cwd classification quiet through pending RPCs
The delete-after-check handler logged the stale-project warning but closed
the worker with birpc's default error, so pending RPCs rejected with a bare
'[birpc] rpc is closed' and the callers' catches re-logged the failure.
birpc's $close(customError) rejects pending calls with the given error;
the cwd-gone branch now passes a ReportedRstestResolutionError carrying the
same message, so logUnlessReported stays quiet — the same contract the
pre-spawn guard already follows. The branch's race window (cwd deleted
between the guard and spawn, with no 'spawn' event timing guarantee) has no
deterministic unit test; the classification is covered by types and the
existing spawn-failure suite.
* fix(vscode): retract only the injected FORCE_COLOR value
A config that assigns both FORCE_COLOR and NO_COLOR at load time owns the
FORCE_COLOR value; the bare CLI, deciding after config load, leaves both
intact. The retraction now removes FORCE_COLOR only while it still holds
the injected '1', so pools keep a config-set force-color preference.
Copy file name to clipboardExpand all lines: packages/vscode/AGENTS.md
+3-2Lines changed: 3 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,10 +5,10 @@ One extension replacing the standalone `rstack.rslint` and `rstack.rstest` exten
5
5
## The copies are intentional
6
6
7
7
-`stacks/lint` and `stacks/test` are deliberate near-verbatim copies of the upstream extensions, kept close to upstream so changes can be synced by diffing. Do NOT deduplicate or refactor across the two stacks — the duplication is the point; consolidation is a later, explicit phase.
8
-
- The copies diverge from upstream in exactly eight ways (the "adaptations" below). When syncing upstream, preserve them. A ninth divergence is either a bug or must be added to this list.
8
+
- The copies diverge from upstream in exactly nine ways (the "adaptations" below). When syncing upstream, preserve them. A tenth divergence is either a bug or must be added to this list.
9
9
-**Tracked upstream state.**`stacks/lint` is synced to web-infra-dev/rslint `packages/vscode-extension` at **39536fd6** (#1617 — per-document core resolution, `CoreResolver` + `RuntimeManager`, `corePath`, PnP removed) and **892482e0** (#1630 — `configPath` on `rslint/configRefresh`). `CoreResolver.ts` / `RuntimeManager.ts` / `WorkspaceDocumentRouter.ts` / `Rslint.ts` are the files to diff when syncing further; record the new commits here when you do.
10
10
11
-
## The eight adaptations
11
+
## The nine adaptations
12
12
13
13
1.**Shell activation** — stacks never self-activate; `register()` returns fast and never blocks on starting a server/worker.
14
14
2.**Namespace** — everything user-visible is `rstack.*`. Legacy `rslint.*` / `rstest.*` settings and command ids are not read, aliased or migrated (breaking old settings and keybindings was an accepted cost).
@@ -18,6 +18,7 @@ One extension replacing the standalone `rstack.rslint` and `rstack.rstest` exten
18
18
6.**Node runtime selection** (lint, test, fmt) — the Node a project-loading child process runs on is a **User Node runtime** chosen by the extension against one uniform floor, never assumed from PATH; the recovery path is the user's own shell, and the dividing line is the **load bound** (terms in CONTEXT.md; the full rule and rationale in `docs/adr/0001-node-runtime-selection.md`). All three callers — the lint worker, the rstest worker and the `rs fmt --lsp` server — take the decision from the one shared module (`shared/nodeResolution.ts`) and share one escape hatch, the resource-scoped `rstack.nodeExecutable` (`shared/nodeExecutableSetting.ts`); each appends its own consequence to the shared preflight message.
19
19
7. **Lint worker and Rstack bridge** — the extension host is only Rslint's language client. One vscode-free, editor-shipped lint worker per **Lint runtime** (one Rslint core inside one workspace folder — CONTEXT.md) runs on the User Node runtime, owns the Go LSP plus all five reverse requests, and derives the binary/config/plugin pieces from one explicit `@rslint/core` directory. Upstream's `CoreResolver` loads that core in the extension host; ours only walks to the directory (`fs.stat` + `package.json` + semver) and hands the path to the worker, and its `CoreInstallation` therefore carries paths, not module factories; upstream's installation cache goes with the module loading it memoized (`clear()` is a no-op kept for the `RuntimeManager` contract). A bridged folder passes only rstack's published `dist/rslintConfig.js` shim; neither the extension nor the worker re-implements Rstack config semantics. Because protocol 2 locks `configPath` per process, the shim is part of the runtime key (`folder + core identity + shim`), which upstream — having no bridge — keys on the core alone. Why: `docs/adr/0003-lint-through-editor-worker.md`.
20
20
8.**Self-documenting Rslint diagnostics** — client-side providers parse Inline directives into per-rule hover, DocumentLink and underline-decoration affordances (the hover renders `Rslint(rule-id)`, the shape VS Code gives the published diagnostics), and the router enriches today's `[rule-id] message` diagnostics with a derived Rule docs link. No rule metadata or network lookup is bundled (ADR 0004). The hover provider yields whenever the owning language client's resolved capabilities advertise `hoverProvider`; an optional `Rslint.onClosed` hook identity-safely prunes the controller's capability mirror; the diagnostic synthesis is removed once upstream publishes `code` / `codeDescription` natively.
21
+
9.**Color env parity with the CLI** (test) — upstream hard-codes `FORCE_COLOR: '1'` into the worker's spawn env; ours mirrors the CLI's `getForceColorEnv` (rstest `packages/core/src/utils/logger.ts`) instead (`stacks/test/shared/colorEnv.ts`): the master injects `FORCE_COLOR=1` into the composed spawn env only when neither `FORCE_COLOR` nor `NO_COLOR` is already set (marking the injection with `RSTACK_FORCE_COLOR_INJECTED`), and the worker retracts the marked injection right after config load if the config set `NO_COLOR` — the CLI's own decision point. Otherwise a project whose config sets `process.env.NO_COLOR` (rstack-cli does) hits Node's "'NO_COLOR' env is ignored" warning in every pool process. A user-set `FORCE_COLOR` beside a config-set `NO_COLOR` still warns, exactly as the bare CLI does.
// Whether `specifier` itself is what could not be found. `MODULE_NOT_FOUND`
33
39
// alone is too broad: a package that is installed but whose entry file is gone
34
40
// (an interrupted install, or a workspace link that has not been built) throws
@@ -52,53 +58,3 @@ export function formatConfiguredCoreNotFoundMessage(
52
58
): string{
53
59
return`Cannot find "@rstest/core" at the configured "rstack.rstest.rstestPackagePath": ${configuredPackagePath}. Update the setting to point at an installed "@rstest/core" package.json.`;
54
60
}
55
-
56
-
// The one-line cause when a config evaluation failed on a package that is
57
-
// not installed, or `undefined` for a real error. Gated on the error's
58
-
// `code` — Node's own classification (`ERR_MODULE_NOT_FOUND` for ESM,
59
-
// `MODULE_NOT_FOUND` for CJS) — but the code alone is too broad: a typo'd
60
-
// relative import fails with the same codes, and installing dependencies
61
-
// cannot fix it, so only a bare specifier — a package name, read from the
62
-
// message since CJS carries no structured one — counts, and anything
63
-
// unrecognized fails towards the full error report. The check has to run in
64
-
// the worker, where the error is thrown: the IPC channel back to the
65
-
// extension host (`serialization: 'advanced'`) drops the `code`, so the
66
-
// verdict travels as data (`NormalizedConfigResult`). Only the first line
67
-
// comes back: the rest of a CJS message is the require stack, and the
68
-
// not-installed state is one warn line without one.
0 commit comments