diff --git a/docs/adr/0003-lint-through-editor-worker.md b/docs/adr/0003-lint-through-editor-worker.md
index 27a6033..6698d27 100644
--- a/docs/adr/0003-lint-through-editor-worker.md
+++ b/docs/adr/0003-lint-through-editor-worker.md
@@ -24,7 +24,7 @@ Rslint's language server is two halves: the Go process (`rslint --lsp`) lints na
- **Lint gains a Node floor it never had.** A native folder that lints on VS Code's Node today reports `version mismatch` and starts nothing when no User Node runtime clears the ADR 0001 floor. Accepted deliberately: one worker, one path, one floor (ADR 0001 already rejected per-project floors), and this is the debt that ADR named.
- **Resolution follows one chain, mirroring `rs lint`.** For a bridged folder: `rstack` from the folder root → `@rslint/core` from rstack's directory (the transitive copy `rs lint` itself imports; a pnpm project declaring only `rstack` cannot resolve `@rslint/core` from its root) → the Go binary through that core's `resolveRslintBinary()`. For a native folder the chain starts at `@rslint/core` from the **document's own directory** (the #1617 sync; before it, from the folder root). The extension walks the chain as far as the **core directory** — `fs.stat`, `package.json` reads and semver comparisons, no project code loaded, so inside the load bound and legitimately on the VS Code Node runtime, exactly as fmt resolves the `rs` bin — and gates there; the worker receives `--core
[--config ]` and takes the last hop itself, calling that core's `resolveRslintBinary()` on the User Node runtime, since it is a JS export of the project's package. Floors follow the evidence in `SUPPORT_MATRIX`: `@rslint/core >= 0.8.0` is the first release with explicit protocol-2 config selection, while `rstack >= 0.7.0` is the first release whose shim supplies `basePath` and itself pins `@rslint/core` 0.9.0; one answer to "which rstack does the extension support" is worth more than keeping older releases running.
-- **One override, and it names a core, not a binary.** `rstack.rslint.binPath` / `customBinPath` are removed in favour of `rstack.rslint.corePath` — the setting upstream introduced in rslint #1617: a path to an `@rslint/core` package directory, resource-scoped, from which the binary, config host, protocol version and plugin host all derive. In a bridged folder it overrides the rstack → `@rslint/core` hop only; the shim stays rstack's. A binary chosen independently of its core cannot be supported: the two must speak the same protocol. The rest of #1617 — per-document core resolution, one runtime per physical installation — has since been synced (issue #13): a **Lint runtime** is now one Rslint core inside one workspace folder, resolved per open document and refcounted by it, so a folder runs as many workers as its files have distinct cores (a bridged folder always exactly one, rstack's) and none at all while nothing is open. The worker never noticed: it still takes explicit `--core` / `--config` paths, which is precisely why that change did not touch it.
+- **One override, and it names a core, not a binary.** `rstack.rslint.binPath` / `customBinPath` are removed in favour of `rstack.rslint.corePath` — the setting upstream introduced in rslint #1617: a path to an `@rslint/core` package directory (or its `package.json`), resource-scoped, from which the binary, config host, protocol version and plugin host all derive. In a bridged folder it overrides the rstack → `@rslint/core` hop only; the shim stays rstack's. A binary chosen independently of its core cannot be supported: the two must speak the same protocol. The rest of #1617 — per-document core resolution, one runtime per physical installation — has since been synced (issue #13): a **Lint runtime** is now one Rslint core inside one workspace folder, resolved per open document and refcounted by it, so a folder runs as many workers as its files have distinct cores (a bridged folder always exactly one, rstack's) and none at all while nothing is open. The worker never noticed: it still takes explicit `--core` / `--config` paths, which is precisely why that change did not touch it.
- **Ownership is per folder, native wins.** One server holds one config choice for its lifetime (the supported config protocols lock `configPath` per process), and explicit and automatic modes cannot mix, so a folder is bridged only when no `rslint.config.*` exists anywhere in it and a `rstack.config.*` sits at its root; a subdirectory `rstack.config.*` lights nothing (`rs lint` in a terminal reads its cwd only — the same reason ADR 0002 rejected deepest-config-wins for fmt). Detection lights a bridged folder on the file's presence and never reads it: a `rstack.config.*` without `define.lint()` runs an empty config, as `rs lint` does.
- **Config changes refresh, mode changes restart.** Rslint has a live refresh (`rslint/configRefresh` with the same `configPath`), unlike `rs fmt --lsp`, so the extension keeps its watcher-driven refresh — extended, for a bridged folder, with the root `rstack.config.*` — and the worker re-stamps `protocolVersion` and its `configPath` on every refresh (the extension does not know either). Only a native ↔ bridged flip, or a dependency change the refresh cannot absorb, restarts the server. This is the "diverge only when the tool forces it" rule: rslint can refresh, fmt cannot.
- **Failure states mirror fmt.** No `rstack`, no `@rslint/core`, or a config importing an absent package → `disabled`; `rstack` or the chained `@rslint/core` below floor, or no Node clearing the floor → `version mismatch`; worker or Go dying → `crashed`. Config-import failures are classified where the worker still has the loader's structured error and carried to the editor as data; the live Go server remains available for a later refresh.
diff --git a/packages/vscode/AGENTS.md b/packages/vscode/AGENTS.md
index 3ac38af..405a3b4 100644
--- a/packages/vscode/AGENTS.md
+++ b/packages/vscode/AGENTS.md
@@ -12,7 +12,7 @@ One extension replacing the standalone `rstack.rslint` and `rstack.rstest` exten
## The eleven adaptations
1. **Shell activation** — stacks never self-activate; `register()` returns fast and never blocks on starting a server/worker.
-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).
+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). Both `rstack.rslint.corePath` and `rstack.rstest.corePath` accept a package directory or its `package.json`, resolving relative paths from the workspace folder; only Rstest expands `${workspaceFolder}`. Preserve these unified override names without legacy aliases when syncing.
3. **Resolve-from-project** — no tool binaries or tool packages in the VSIX; everything resolves from the user's project so the editor runs the CLI's exact versions. Version floors surface as a status, never a crash. All cooperating lint pieces (binary, config loader, plugin host) must come from one resolution root. Enforced by lint: `@typescript-eslint/no-restricted-imports` in the root `rstack.config.ts` rejects any non-type import of `@rslint/core`, `@rstest/core`, `rstack` or `jiti` under `src/` — types only at compile time, runtime modules through explicit project paths.
4. **Status aggregation** — stacks own no UI chrome; they report to the shell's single status bar item, which always exists. Upstream's plugin-host failure toast becomes a status verdict (adaptation 7). Rslint LSP tracing shares the **Rstack: Rslint** Output channel to preserve the four-channel cap; see `stacks/lint/index.ts`. In CI the test stack's `MasterLogger` also mirrors every entry to stderr (`RSTACK_E2E_MIRROR_LOGS=1`, set by `e2e/rstest/runTest.ts`) — the output channel is unreadable there; rationale in `stacks/test/logger.ts`.
5. **Worker-cwd decoupling** (test) — a project's cwd is explicit, not derived from the config file path; for native configs behavior stays byte-identical to upstream.
diff --git a/packages/vscode/README.md b/packages/vscode/README.md
index 181c3e7..c7357da 100644
--- a/packages/vscode/README.md
+++ b/packages/vscode/README.md
@@ -78,13 +78,13 @@ All settings live under the unified `rstack.*` namespace. There are no `rslint.*
| --- | --- | --- |
| `rstack.nodeExecutable` | — | Node binary used for the processes that load your project: the lint worker, test worker and `rs fmt` language server. Empty means the extension picks one (`PATH` first, then the `node` your interactive shell resolves). |
| `rstack.rslint.enable` | `true` | Enable/disable the Rslint integration. |
-| `rstack.rslint.corePath` | — | Path to an `@rslint/core` package directory; relative paths resolve from the workspace folder. |
+| `rstack.rslint.corePath` | — | Path to an `@rslint/core` package directory or its `package.json`; relative paths resolve from the workspace folder. Use only to override automatic resolution. |
| `rstack.rslint.trace.server` | `off` | LSP trace level (`off` / `messages` / `verbose`). |
| `rstack.rstest.enable` | `true` | Enable/disable the Rstest integration. |
| `rstack.rstest.configFileGlobPattern` | `["**/rstest.config.{mjs,ts,js,cjs,mts,cts}"]` | Glob patterns used to discover config files. |
| `rstack.rstest.testCaseCollectMethod` | `ast` | `ast` (fast) or `runtime` (supports dynamic test generation). |
| `rstack.rstest.applyDiagnostic` | `true` | Show diagnostics in the editor and Problems panel for failures. |
-| `rstack.rstest.rstestPackagePath` | — | Explicit `@rstest/core` `package.json`, last-resort override. |
+| `rstack.rstest.corePath` | — | Path to an `@rstest/core` package directory or its `package.json`; relative paths resolve from the workspace folder and `${workspaceFolder}` is supported. Use only to override automatic resolution, for both workers and Run in Terminal. |
| `rstack.rstest.nodeExecArgs` | `[]` | Extra Node args for the test worker. |
| `rstack.rstest.nodeEnv` | `null` | Extra env for the test worker. |
| `rstack.rstest.debugNodeEnv` | `null` | Extra env when debugging tests. |
@@ -101,7 +101,7 @@ Formatting runs one `rs fmt` language server per workspace folder, which loads `
## Coming from the standalone extensions
-Disable or uninstall the retired `rstack.rslint` / `rstack.rstest` extensions so only one copy of each tool runs. Settings and keybindings are not carried over from them: re-enter your settings under the `rstack.*` keys listed above and re-bind any keybinding to the new `rstack.*` command ids. Legacy `rslint.binPath` / `rslint.customBinPath` have no equivalent — use `rstack.rslint.corePath` to point at an `@rslint/core` package directory if you still need an override.
+Disable or uninstall the retired `rstack.rslint` / `rstack.rstest` extensions so only one copy of each tool runs. Settings and keybindings are not carried over from them: re-enter your settings under the `rstack.*` keys listed above and re-bind any keybinding to the new `rstack.*` command ids. Legacy `rslint.binPath` / `rslint.customBinPath` have no equivalent — use `rstack.rslint.corePath` to point at an `@rslint/core` package directory or its `package.json` if you still need an override.
## Community
diff --git a/packages/vscode/e2e/lint/suite-jsconfig/core-resolver.test.ts b/packages/vscode/e2e/lint/suite-jsconfig/core-resolver.test.ts
index 21cb30c..44c7649 100644
--- a/packages/vscode/e2e/lint/suite-jsconfig/core-resolver.test.ts
+++ b/packages/vscode/e2e/lint/suite-jsconfig/core-resolver.test.ts
@@ -257,17 +257,22 @@ suite('local core resolver', () => {
);
const document = await createSource('src/index.ts');
- const resolved = await new CoreResolver().resolve(
- document,
- temporaryWorkspaceFolder(),
- { mode: 'native', corePath: 'vendor/rslint-core' },
- );
+ for (const corePath of [
+ 'vendor/rslint-core',
+ 'vendor/rslint-core/package.json',
+ ]) {
+ const resolved = await new CoreResolver().resolve(
+ document,
+ temporaryWorkspaceFolder(),
+ { mode: 'native', corePath },
+ );
- await assertSamePhysicalPath(
- resolved.installation.packageDirectory,
- packageDirectory,
- );
- assert.strictEqual(resolved.installation.version, '3.0.0');
+ await assertSamePhysicalPath(
+ resolved.installation.packageDirectory,
+ packageDirectory,
+ );
+ assert.strictEqual(resolved.installation.version, '3.0.0');
+ }
});
test('rejects a core package below the supported version floor', async () => {
diff --git a/packages/vscode/package.json b/packages/vscode/package.json
index 12661a3..a5a995c 100644
--- a/packages/vscode/package.json
+++ b/packages/vscode/package.json
@@ -148,7 +148,7 @@
"type": "string",
"default": "",
"scope": "resource",
- "markdownDescription": "Path to an `@rslint/core` package directory. Relative paths are resolved from the workspace folder. When empty, a native folder uses the nearest installation above each file and a bridged folder (root `rstack.config.*`) uses the one its `rstack` depends on."
+ "markdownDescription": "Path to an `@rslint/core` installation, either its package directory or its `package.json` file. Relative paths are resolved from the workspace folder. Use this override only when automatic resolution does not select the installation you need. When empty, a native folder uses the nearest installation above each file and a bridged folder (root `rstack.config.*`) uses the one its `rstack` depends on."
},
"rstack.rslint.trace.server": {
"order": 2,
@@ -175,11 +175,11 @@
"scope": "window",
"markdownDescription": "Enable the Rstest test explorer for detected workspace folders. This is a window-level kill switch; which folders run Rstest is decided by detection."
},
- "rstack.rstest.rstestPackagePath": {
+ "rstack.rstest.corePath": {
"order": 1,
"type": "string",
"scope": "resource",
- "markdownDescription": "The path to a `package.json` file of an Rstest installation (usually inside `node_modules`) in case the extension cannot find it. It will be used to resolve Rstest API paths. This should be used as a last resort fix. Supports the `${workspaceFolder}` placeholder."
+ "markdownDescription": "Path to an `@rstest/core` installation, either its package directory or its `package.json` file. Relative paths are resolved from the workspace folder. Supports the `${workspaceFolder}` placeholder. Use this override only when automatic resolution does not select the installation you need. It applies to both worker and Run in Terminal resolution."
},
"rstack.rstest.configFileGlobPattern": {
"order": 2,
@@ -450,7 +450,6 @@
"vscode": "^1.97.0"
},
"icon": "icon.png",
- "preview": true,
"capabilities": {
"virtualWorkspaces": {
"supported": "limited",
diff --git a/packages/vscode/src/stacks/lint/resolution.ts b/packages/vscode/src/stacks/lint/resolution.ts
index e5860a1..f774e63 100644
--- a/packages/vscode/src/stacks/lint/resolution.ts
+++ b/packages/vscode/src/stacks/lint/resolution.ts
@@ -84,8 +84,11 @@ function resolveConfiguredCore(
folderRoot: string,
configuredPath: string,
): PackageLocation {
- const directory = path.resolve(folderRoot, configuredPath);
- const packageJsonPath = path.join(directory, 'package.json');
+ const resolvedPath = path.resolve(folderRoot, configuredPath);
+ const packageJsonPath = configuredPath.endsWith('package.json')
+ ? resolvedPath
+ : path.join(resolvedPath, 'package.json');
+ const directory = path.dirname(packageJsonPath);
try {
if (!fs.statSync(packageJsonPath).isFile()) throw new Error('not a file');
} catch (error) {
diff --git a/packages/vscode/src/stacks/test/config.ts b/packages/vscode/src/stacks/test/config.ts
index d1b0428..979ff42 100644
--- a/packages/vscode/src/stacks/test/config.ts
+++ b/packages/vscode/src/stacks/test/config.ts
@@ -23,9 +23,9 @@ export const CONFIG_SECTION = 'rstack.rstest';
// Centralized configuration types for the extension.
// Add new keys here to extend configuration in a type-safe way.
const configSchema = object({
- // The path to a package.json file of a Rstest executable.
+ // The path to an @rstest/core package directory or its package.json.
// Used as a last resort if the extension cannot auto-detect @rstest/core.
- rstestPackagePath: fallback(optional(string()), undefined),
+ corePath: fallback(optional(string()), undefined),
nodeExecArgs: fallback(array(string()), []),
nodeEnv: fallback(optional(record(string(), string())), undefined),
debugNodeEnv: fallback(optional(record(string(), string())), undefined),
diff --git a/packages/vscode/src/stacks/test/coreResolution.ts b/packages/vscode/src/stacks/test/coreResolution.ts
index 2530981..974674a 100644
--- a/packages/vscode/src/stacks/test/coreResolution.ts
+++ b/packages/vscode/src/stacks/test/coreResolution.ts
@@ -9,7 +9,7 @@
* embeds a multi-line require stack and says nothing about what to do. An
* uninstalled core is the normal state of a freshly cloned repository and is
* resolved for every config file without the user asking, so it is only
- * logged; a `rstestPackagePath` that does not resolve is a setting the user
+ * logged; a `corePath` that does not resolve is a setting the user
* has to fix, so it is notified.
*/
@@ -56,5 +56,5 @@ export function isModuleNotFoundError(
export function formatConfiguredCoreNotFoundMessage(
configuredPackagePath: string,
): string {
- return `Cannot find "@rstest/core" at the configured "rstack.rstest.rstestPackagePath": ${configuredPackagePath}. Update the setting to point at an installed "@rstest/core" package.json.`;
+ return `Cannot find "@rstest/core" at the configured "rstack.rstest.corePath": ${configuredPackagePath}. Update the setting to point at an installed "@rstest/core" package directory or package.json.`;
}
diff --git a/packages/vscode/src/stacks/test/master.ts b/packages/vscode/src/stacks/test/master.ts
index 388253e..c9b60e2 100644
--- a/packages/vscode/src/stacks/test/master.ts
+++ b/packages/vscode/src/stacks/test/master.ts
@@ -57,7 +57,7 @@ const CORE_NOT_INSTALLED_STATUS = formatNotInstalledStatus(
'rstest',
'@rstest/core',
);
-const CORE_NOT_INSTALLED_CONSEQUENCE = `install the project dependencies, or set "${CONFIG_SECTION}.rstestPackagePath" to an installed @rstest/core package.json`;
+const CORE_NOT_INSTALLED_CONSEQUENCE = `install the project dependencies, or set "${CONFIG_SECTION}.corePath" to an installed @rstest/core package directory or package.json`;
type WorkerRpc = BirpcReturn;
type RstestPaths = Pick;
@@ -155,7 +155,7 @@ export const warmWorkerNodePreflight = (
}
};
-// The specifier used when `rstestPackagePath` is unset.
+// The specifier used when `corePath` is unset.
const CORE_PACKAGE_JSON = '@rstest/core/package.json';
export class RstestApi {
@@ -189,7 +189,7 @@ export class RstestApi {
/**
* Where the default `@rstest/core` (and CLI bin) walk-up starts. Chosen
* by `Project` — see `ProjectSource.rstestResolutionDir`; an explicit
- * `rstestPackagePath` bypasses it.
+ * `corePath` bypasses it.
*/
private rstestResolutionDir: string,
) {}
@@ -314,24 +314,19 @@ export class RstestApi {
}
}
- // The validated absolute path to the package.json a `rstestPackagePath`
- // setting points at, or `undefined` when the setting is unset and the bare
+ // The absolute path to the package.json selected by a `corePath`
+ // setting, or `undefined` when the setting is unset and the bare
// `CORE_PACKAGE_JSON` specifier applies. Shared by the worker resolution and
// the terminal CLI resolution, which both also report the configured path.
private resolveConfiguredPackageJson(): string | undefined {
// TODO: support Yarn PnP
- let configuredPackagePath = getConfigValue(
- 'rstestPackagePath',
- this.workspace,
- );
+ let configuredPackagePath = getConfigValue('corePath', this.workspace);
if (!configuredPackagePath) {
return undefined;
}
configuredPackagePath = this.expandWorkspaceFolder(configuredPackagePath);
if (!configuredPackagePath.endsWith('package.json')) {
- throw new Error(
- `"${CONFIG_SECTION}.rstestPackagePath" must point to a package.json file, instead got: ${configuredPackagePath}`,
- );
+ configuredPackagePath = path.join(configuredPackagePath, 'package.json');
}
return path.isAbsolute(configuredPackagePath)
? configuredPackagePath
@@ -343,7 +338,7 @@ export class RstestApi {
// whose dependencies are not installed yet, so it is written to the output
// channel and never raised as a notification. This is the only place that
// policy lives, and it deliberately does not cover `configuredPackagePath`:
- // a `rstestPackagePath` that does not resolve is a setting the user got
+ // a `corePath` that does not resolve is a setting the user got
// wrong, so it is rethrown for the caller to report like any other failure.
private resolveFromCwd(
specifier: string,
@@ -400,7 +395,7 @@ export class RstestApi {
let corePackageJsonPath: string;
let nodeExport: string | undefined;
if (configured) {
- logger.debug('Using configured rstestPackagePath:', configured);
+ logger.debug('Using configured corePath:', configured);
// An explicit pin, resolved exactly as before — the setting names one
// fixed path, so cache staleness is moot.
// `dirname` turns the package.json specifier into its package entry.
@@ -493,7 +488,7 @@ export class RstestApi {
}
// Resolve the rstest CLI executable (its package `bin`) for the terminal run
- // mode, honoring a configured `rstestPackagePath` the same way as the worker
+ // mode, honoring a configured `corePath` the same way as the worker
// resolution above.
private resolveRstestBin(): string | undefined {
const configured = this.resolveConfiguredPackageJson();
@@ -747,7 +742,7 @@ export class RstestApi {
// runtime the user must fix: detection watches the config file and will
// drop or re-add the project. Refusing here — before package resolution,
// which would misread the deleted directory as "@rstest/core is not
- // installed" (or notify for a `rstestPackagePath` inside it) — spares
+ // installed" (or notify for a `corePath` inside it) — spares
// the caller a worker whose every call rejects with an opaque
// "[birpc] rpc is closed", and spares the user Node's misreading of a
// missing cwd as "spawn node ENOENT".
diff --git a/packages/vscode/tests/extension.test.ts b/packages/vscode/tests/extension.test.ts
index e5f56be..2342c32 100644
--- a/packages/vscode/tests/extension.test.ts
+++ b/packages/vscode/tests/extension.test.ts
@@ -529,7 +529,7 @@ describe('dependency recovery polling', () => {
});
describe('the extension manifest', () => {
- it('scopes language-client tracing to the window', () => {
+ it('scopes core overrides to resources and language-client tracing to the window', () => {
const manifest = require('../package.json') as {
contributes: {
configuration: Array<{
@@ -547,6 +547,8 @@ describe('the extension manifest', () => {
// the running client never reads.
expect(settings['rstack.rslint.trace.server']?.scope).toBe('window');
expect(settings['rstack.fmt.trace.server']?.scope).toBe('window');
+ expect(settings['rstack.rslint.corePath']?.scope).toBe('resource');
+ expect(settings['rstack.rstest.corePath']?.scope).toBe('resource');
});
});
diff --git a/packages/vscode/tests/stacks/lint/resolution.test.ts b/packages/vscode/tests/stacks/lint/resolution.test.ts
index 216e293..8a651ca 100644
--- a/packages/vscode/tests/stacks/lint/resolution.test.ts
+++ b/packages/vscode/tests/stacks/lint/resolution.test.ts
@@ -42,24 +42,42 @@ describe('resolveRslint', () => {
});
});
- it('uses corePath for the core hop in both modes', () => {
+ it.each(['custom-core', 'custom-core/package.json'])(
+ 'uses a relative corePath for the core hop in both modes (%s)',
+ (corePath) => {
+ const root = temporaryDirectory();
+ const rstackDir = installPackage(root, 'rstack', '0.6.1');
+ installShim(rstackDir);
+ const customCore = writePackage(
+ path.join(root, 'custom-core'),
+ '@rslint/core',
+ '0.8.1',
+ );
+ for (const mode of ['native', 'bridged'] as const) {
+ const resolution = resolveRslint({
+ folderRoot: root,
+ mode,
+ corePath,
+ });
+ expect(resolution.coreDir).toBe(customCore);
+ expect(resolution.coreVersion).toBe('0.8.1');
+ }
+ },
+ );
+
+ it('reports an invalid configured path', () => {
+ const corePath = 'missing-core';
const root = temporaryDirectory();
- const rstackDir = installPackage(root, 'rstack', '0.6.1');
- installShim(rstackDir);
- const customCore = writePackage(
- path.join(root, 'custom-core'),
- '@rslint/core',
- '0.8.1',
+ expect(() =>
+ resolveRslint({ folderRoot: root, mode: 'native', corePath }),
+ ).toThrow(
+ `Could not access @rslint/core at ${path.join(root, 'missing-core')}`,
);
-
- for (const mode of ['native', 'bridged'] as const) {
- const resolution = resolveRslint({
- folderRoot: root,
- mode,
- corePath: './custom-core',
- });
- expect(resolution.coreDir).toBe(customCore);
- expect(resolution.coreVersion).toBe('0.8.1');
+ try {
+ resolveRslint({ folderRoot: root, mode: 'native', corePath });
+ } catch (error) {
+ expect(error).toBeInstanceOf(RslintResolutionError);
+ expect(error).toMatchObject({ code: 'invalid-package' });
}
});
diff --git a/packages/vscode/tests/stacks/test/master.test.ts b/packages/vscode/tests/stacks/test/master.test.ts
index 4dd0a36..12fd199 100644
--- a/packages/vscode/tests/stacks/test/master.test.ts
+++ b/packages/vscode/tests/stacks/test/master.test.ts
@@ -283,29 +283,34 @@ describe('RstestApi package-resolution anchor', () => {
});
});
- it('lets rstestPackagePath override the bridge anchor', () => {
- writeCoreInstall(storeEntry);
- const configured = writeCoreInstall(path.join(root, 'configured'));
- settings.rstestPackagePath = path.join(
- configured.packageDir,
- 'package.json',
- );
-
- expect(resolveRstestPaths(createApi(cwd, rstackDir))).toEqual({
- paths: {
- apiPath: path.join(configured.packageDir, 'api.js'),
- rstestPath: configured.entry,
- },
- bin: configured.bin,
- });
- });
+ it.each(['directory', 'relative', 'workspaceFolder'])(
+ 'lets corePath override the bridge anchor (%s)',
+ (form) => {
+ writeCoreInstall(storeEntry);
+ const configured = writeCoreInstall(path.join(root, 'configured'));
+ settings.corePath =
+ form === 'relative'
+ ? path.relative(cwd, configured.packageDir)
+ : form === 'workspaceFolder'
+ ? '${workspaceFolder}/' + path.relative(cwd, configured.packageDir)
+ : configured.packageDir;
+
+ expect(resolveRstestPaths(createApi(cwd, rstackDir))).toEqual({
+ paths: {
+ apiPath: path.join(configured.packageDir, 'api.js'),
+ rstestPath: configured.entry,
+ },
+ bin: configured.bin,
+ });
+ },
+ );
it('resolves the configured core API outside node_modules by self-reference', () => {
const installed = writeCoreInstall(root);
const packageDir = path.join(root, 'vendor', 'rstest-core');
fs.mkdirSync(path.dirname(packageDir), { recursive: true });
fs.renameSync(installed.packageDir, packageDir);
- settings.rstestPackagePath = path.join(packageDir, 'package.json');
+ settings.corePath = path.join(packageDir, 'package.json');
expect(resolveRstestPaths(createApi(cwd))).toEqual({
paths: {
@@ -391,7 +396,7 @@ describe('RstestApi with a missing @rstest/core', () => {
const logged = loggedWarnings.join('\n');
expect(logged).toContain('@rstest/core is not installed');
expect(logged).toContain(`searched from ${noCoreDir}`);
- expect(logged).toContain('rstestPackagePath');
+ expect(logged).toContain('corePath');
expect(logged).not.toContain('Require stack');
expect(reported).toEqual([
{
@@ -455,21 +460,22 @@ describe('RstestApi with an unusable @rstest/core', () => {
});
});
-// A configured `rstestPackagePath` that does not resolve is not the
+// A configured `corePath` that does not resolve is not the
// "dependencies are not installed yet" state — the user picked that path and
// has to fix it, so silence would strand them.
-describe('RstestApi with an unresolvable rstestPackagePath', () => {
+describe('RstestApi with an unresolvable corePath', () => {
const configured = `${noCoreDir}/vendor/core/package.json`;
beforeEach(() => {
shownMessages.length = 0;
- settings.rstestPackagePath = configured;
+ settings.corePath = configured;
});
- it('should notify while discovering projects', async () => {
+ it('should notify while discovering projects with an invalid directory', async () => {
+ settings.corePath = path.dirname(configured);
await expect(createApi().getNormalizedConfig()).rejects.toThrow();
expect(shownMessages).toHaveLength(1);
- expect(shownMessages[0]).toContain('rstack.rstest.rstestPackagePath');
+ expect(shownMessages[0]).toContain('rstack.rstest.corePath');
expect(shownMessages[0]).toContain(configured);
});
@@ -486,13 +492,10 @@ describe('RstestApi with an unresolvable rstestPackagePath', () => {
expect(resolve).toThrow();
expect(shownMessages).toHaveLength(1);
- settings.rstestPackagePath = path.join(
- installed.packageDir,
- 'package.json',
- );
+ settings.corePath = path.join(installed.packageDir, 'package.json');
expect(resolve().rstestPath).toBe(installed.entry);
- settings.rstestPackagePath = configured;
+ settings.corePath = configured;
expect(resolve).toThrow();
expect(shownMessages).toHaveLength(2);
} finally {
@@ -506,7 +509,7 @@ describe('RstestApi with an unresolvable rstestPackagePath', () => {
);
const installed = writeCoreInstall(root);
const metadata = path.join(installed.packageDir, 'package.json');
- settings.rstestPackagePath = metadata;
+ settings.corePath = metadata;
loggedErrors.length = 0;
const original = nodeRequire.resolve;
let broken = true;
@@ -539,7 +542,7 @@ describe('RstestApi with an unresolvable rstestPackagePath', () => {
it('should notify for a terminal run', () => {
createApi().runInTerminal({});
expect(shownMessages).toHaveLength(1);
- expect(shownMessages[0]).toContain('rstack.rstest.rstestPackagePath');
+ expect(shownMessages[0]).toContain('rstack.rstest.corePath');
expect(createdTerminals).toEqual([]);
});
});