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
2 changes: 1 addition & 1 deletion .github/renovate.json5
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
],
// 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/tests/**'],
ignorePaths: ['**/node_modules/**', 'packages/vscode/e2e/**'],
packageRules: [
// Use chore as semantic commit type for commit messages
{
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ jobs:
vscode-test-${{ runner.os }}-

# The E2E fixtures install published npm packages at test time
# (tests/e2e/setupFixtures.mjs, invoked by test:e2e), so this step needs
# (e2e/setupFixtures.mjs, invoked by test:e2e), so this step needs
# network access.
- name: E2E Test
run: pnpm run test:e2e
8 changes: 4 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,10 @@ tests-dist/

# E2E fixtures install published npm versions on demand; only
# their manifests and configs are tracked.
packages/vscode/tests/e2e/fixtures/*/node_modules/
packages/vscode/tests/e2e/fixtures/*/pnpm-lock.yaml
packages/vscode/tests/e2e/lint/fixtures/pnpm-lock.yaml
packages/vscode/tests/e2e/rstest/fixtures/*/pnpm-lock.yaml
packages/vscode/e2e/fixtures/*/node_modules/
Comment thread
fi3ework marked this conversation as resolved.
packages/vscode/e2e/fixtures/*/pnpm-lock.yaml
packages/vscode/e2e/lint/fixtures/pnpm-lock.yaml
packages/vscode/e2e/rstest/fixtures/*/pnpm-lock.yaml

# Build-time copy of the workspace root LICENSE (see rslib.config.mts)
packages/vscode/LICENSE
Expand Down
10 changes: 5 additions & 5 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,15 @@
"id": "playgroundTarget",
"type": "pickString",
"description": "Which fixture should the dev host open?",
"default": "tests/e2e/fixtures/e2e.code-workspace",
"default": "e2e/fixtures/e2e.code-workspace",
"options": [
{
"label": "all three fixtures (multi-root)",
"value": "tests/e2e/fixtures/e2e.code-workspace"
"value": "e2e/fixtures/e2e.code-workspace"
},
{ "label": "rslint fixture", "value": "tests/e2e/fixtures/rslint" },
{ "label": "rstest fixture", "value": "tests/e2e/fixtures/rstest" },
{ "label": "rstack fixture", "value": "tests/e2e/fixtures/rstack" }
{ "label": "rslint fixture", "value": "e2e/fixtures/rslint" },
{ "label": "rstest fixture", "value": "e2e/fixtures/rstest" },
{ "label": "rstack fixture", "value": "e2e/fixtures/rstack" }
]
}
]
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Rstack Editor provides unified editor support for [Rstack](https://rstack.rs), the fast, unified JavaScript toolchain for developers and agents. It integrates [Rslint](https://github.com/web-infra-dev/rslint), [Rstest](https://github.com/web-infra-dev/rstest) and [rstack-cli](https://github.com/rstackjs/rstack-cli) into a single extension, so one install covers the whole toolchain.

> [!IMPORTANT]
>
> **Work in progress.** Rstack Editor is pre-1.0 and under active development. Settings, command ids and behavior can change between releases, and not every config source is wired up yet — see the roadmap below for what works today. Bug reports and feedback are very welcome.

## Packages

| Name | Description |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import * as vscode from 'vscode';
import { isCodeActionCancellation } from './utils/codeActionRegistry';
import { runBeforeDeadline } from './utils/deadline';
import { EXTENSION_ID, workspaceMarkerFile } from './utils/extension';
import type { RstackExtensionExports } from '../../../src/types';
import type { RstackExtensionExports } from '../../src/types';

const startupTimeoutMs = 120_000;
const typescriptCodeActionProbeSource = `interface RslintCodeActionProbe {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* `dist/extension.js`.
* - There is no built-in binary: every fixture resolves
* `@rslint/core` — including its native Go binary — from the shared fixture
* install root (`tests/e2e/lint/fixtures/package.json`, published npm
* install root (`e2e/lint/fixtures/package.json`, published npm
* versions). The sandbox preserves that resolution the way
* upstream preserved its monorepo package boundary: the install root's
* `package.json` is copied next to the workspace copy and its `node_modules`
Expand Down Expand Up @@ -178,12 +178,9 @@ async function runIsolatedSuite(
}

async function main(): Promise<void> {
// `__dirname` is `<package>/tests-dist/tests/e2e/lint` (see tsconfig.e2e.json).
const extensionDevelopmentPath = path.resolve(__dirname, '../../../..');
const fixturesRoot = path.join(
extensionDevelopmentPath,
'tests/e2e/lint/fixtures',
);
// `__dirname` is `<package>/tests-dist/e2e/lint` (see tsconfig.e2e.json).
const extensionDevelopmentPath = path.resolve(__dirname, '../../..');
const fixturesRoot = path.join(extensionDevelopmentPath, 'e2e/lint/fixtures');
const fixture = (name: string): string => path.join(fixturesRoot, name);
const suiteDir = (name: string): string => path.resolve(__dirname, name);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import type {
PluginLintHost,
} from '@rslint/core/eslint-plugin';
import { CancellationTokenSource } from 'vscode';
import { PluginLintPool } from '../../../../src/stacks/lint/PluginLintPool';
import type { Logger } from '../../../../src/stacks/lint/logger';
import { PluginLintPool } from '../../../src/stacks/lint/PluginLintPool';
import type { Logger } from '../../../src/stacks/lint/logger';

function deferred<T>(): {
promise: Promise<T>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import {
isConfigSourceChangeDuringTransaction,
recoverConfigDiscoveryOnServerState,
retryConfigRefreshOnSourceChange,
} from '../../../../src/stacks/lint/Rslint';
import { LspConfigTransactionAdapter } from '../../../../src/stacks/lint/ConfigTransactionAdapter';
} from '../../../src/stacks/lint/Rslint';
import { LspConfigTransactionAdapter } from '../../../src/stacks/lint/ConfigTransactionAdapter';
import { State } from 'vscode-languageclient/node';
import {
RelativePattern,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
import * as assert from 'node:assert';
import { window } from 'vscode';
import { CloseAction, ErrorAction, State } from 'vscode-languageclient/node';
import { LanguageServerProcessOwner } from '../../../../src/stacks/lint/LanguageServerProcessOwner';
import { LanguageServerProcessOwner } from '../../../src/stacks/lint/LanguageServerProcessOwner';
import {
disposeLanguageClient,
ManagedLanguageClient,
shouldResetDocumentSessionOnServerState,
waitForPromiseSettlement,
} from '../../../../src/stacks/lint/Rslint';
} from '../../../src/stacks/lint/Rslint';

const FORCE_KILL_CHILD =
"process.on('SIGTERM', () => undefined); setTimeout(() => process.stdout.write('ready\\n'), 20); setInterval(() => undefined, 1_000)";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
type WorkspaceCoordinatorLogger,
type WorkspaceRootRouter,
type WorkspaceRuntime,
} from '../../../../src/stacks/lint/WorkspaceRslintCoordinator';
} from '../../../src/stacks/lint/WorkspaceRslintCoordinator';

type StartMode = 'ready' | 'fail' | 'pending' | 'factory-fail';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
import {
WorkspaceDocumentRouter,
type DocumentRoutingRuntime,
} from '../../../../src/stacks/lint/WorkspaceDocumentRouter';
} from '../../../src/stacks/lint/WorkspaceDocumentRouter';

class FakeRoutingRuntime implements DocumentRoutingRuntime {
readonly events: string[] = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* `getStackExports('rslint')` / `whenStackActive('rslint')` instead.
*/
import * as vscode from 'vscode';
import type { RstackExtensionExports } from '../../../../src/types';
import type { RstackExtensionExports } from '../../../src/types';

export const EXTENSION_ID = 'rstack.rstack';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* The `@vscode/test-electron` entry point for the ported Rstest suites
* (upstream `rstest/packages/vscode/tests/runTest.ts`, restated on this repo's
* harness patterns — see `tests/e2e/runTest.ts`).
* harness patterns — see `e2e/runTest.ts`).
*
* Unlike the shell/detection harness this one does **not** open a checked-in
* workspace file: `suite/workspace.test.ts` calls `updateWorkspaceFolders` to
Expand All @@ -19,12 +19,12 @@ import { runTests } from '@vscode/test-electron';
const FIXTURE_DIRS = ['workspace-1', 'workspace-2'] as const;

async function main() {
// `__dirname` is `<repo>/tests-dist/tests/e2e/rstest` (see tsconfig.e2e.json).
const extensionDevelopmentPath = path.resolve(__dirname, '../../../..');
// `__dirname` is `<repo>/tests-dist/e2e/rstest` (see tsconfig.e2e.json).
const extensionDevelopmentPath = path.resolve(__dirname, '../../..');
const extensionTestsPath = path.resolve(__dirname, './suite/index');
const fixturesRoot = path.join(
extensionDevelopmentPath,
'tests/e2e/rstest/fixtures',
'e2e/rstest/fixtures',
);

// The extension host loads `main` from `package.json`; an unbuilt repo would
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import assert from 'node:assert';
import path from 'node:path';
import vscode from 'vscode';
import type { RstackExtensionExports } from '../../../../src/types';
import type { RstackExtensionExports } from '../../../src/types';

/**
* The stack exports the suites consume — the shape
Expand All @@ -22,11 +22,11 @@ export interface RstestExports {
) => Promise<void>;
}

/** `<repo>/tests/e2e/rstest/fixtures` (— `__dirname` is under `tests-dist/`). */
/** `<repo>/e2e/rstest/fixtures` (— `__dirname` is under `tests-dist/`). */
export const FIXTURES_ROOT = path.resolve(
__dirname,
'../../../../..',
'tests/e2e/rstest/fixtures',
'../../../..',
'e2e/rstest/fixtures',
);

const EXTENSION_ID = 'rstack.rstack';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import { runTests } from '@vscode/test-electron';
const FIXTURE_NAMES = ['rslint', 'rstest', 'rstack'] as const;

async function main() {
// `__dirname` is `<repo>/tests-dist/tests/e2e` (see tsconfig.e2e.json).
const extensionDevelopmentPath = path.resolve(__dirname, '../../..');
// `__dirname` is `<repo>/tests-dist/e2e` (see tsconfig.e2e.json).
const extensionDevelopmentPath = path.resolve(__dirname, '../..');
const extensionTestsPath = path.resolve(__dirname, './suite/index');
const fixturesDir = path.join(extensionDevelopmentPath, 'tests/e2e/fixtures');
const fixturesDir = path.join(extensionDevelopmentPath, 'e2e/fixtures');
const workspaceFile = path.join(fixturesDir, 'e2e.code-workspace');

// The extension host loads `main` from `package.json`; an unbuilt repo would
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ const here = path.dirname(fileURLToPath(import.meta.url));
export const FIXTURES_DIR = path.join(here, 'fixtures');
/**
* Fixture name -> project directory. The `rstest-workspace-*` entries are the
* projects the ported Rstest suites (`tests/e2e/rstest/`) run against;
* projects the ported Rstest suites (`e2e/rstest/`) run against;
* workspace-2 is one install at its root serving both nested projects. The
* `lint` entry is the shared install root serving every ported Rslint suite
* workspace (`tests/e2e/lint/fixtures/*` — the workspaces themselves have no
* workspace (`e2e/lint/fixtures/*` — the workspaces themselves have no
* package.json; @rslint/core resolves via Node's walk-up from one install).
*/
export const FIXTURES = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import assert from 'node:assert/strict';
import path from 'node:path';
import * as vscode from 'vscode';
import { detectFolder } from '../../../src/detection';
import type { StackId } from '../../../src/types';
import { detectFolder } from '../../src/detection';
import type { StackId } from '../../src/types';
import { eventually } from './helpers';

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import assert from 'node:assert/strict';
import * as vscode from 'vscode';
import type { RstackExtensionExports } from '../../../src/types';
import type { RstackExtensionExports } from '../../src/types';
import { eventually } from './helpers';

const EXTENSION_ID = 'rstack.rstack';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import assert from 'node:assert/strict';
import * as vscode from 'vscode';
import type { RstackExtensionExports } from '../../../src/types';
import type { RstackExtensionExports } from '../../src/types';
import { eventually } from './helpers';

const EXTENSION_ID = 'rstack.rstack';
Expand Down
10 changes: 5 additions & 5 deletions packages/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@
"package:targets": "node scripts/packageTargets.mjs",
"test": "pnpm run test:unit && pnpm run test:e2e",
"test:e2e": "pnpm run test:e2e:fixtures && pnpm run test:e2e:smoke && pnpm run test:e2e:vscode && pnpm run test:e2e:rstest:run && pnpm run test:e2e:lint:run",
"test:e2e:fixtures": "node ./tests/e2e/setupFixtures.mjs",
"test:e2e:fixtures": "node ./e2e/setupFixtures.mjs",
"test:e2e:lint": "pnpm run test:e2e:fixtures lint && pnpm run test:e2e:lint:run",
"test:e2e:lint:run": "tsc -p tsconfig.e2e.json && node ./tests-dist/tests/e2e/lint/runTest.js",
"test:e2e:lint:run": "tsc -p tsconfig.e2e.json && node ./tests-dist/e2e/lint/runTest.js",
"test:e2e:rstest": "pnpm run test:e2e:fixtures rstest-workspace-1 rstest-workspace-2 && pnpm run test:e2e:rstest:run",
"test:e2e:rstest:run": "tsc -p tsconfig.e2e.json && node ./tests-dist/tests/e2e/rstest/runTest.js",
"test:e2e:smoke": "node ./tests/e2e/smoke/rslintPluginHost.mjs",
"test:e2e:vscode": "tsc -p tsconfig.e2e.json && node ./tests-dist/tests/e2e/runTest.js",
"test:e2e:rstest:run": "tsc -p tsconfig.e2e.json && node ./tests-dist/e2e/rstest/runTest.js",
"test:e2e:smoke": "node ./e2e/smoke/rslintPluginHost.mjs",
"test:e2e:vscode": "tsc -p tsconfig.e2e.json && node ./tests-dist/e2e/runTest.js",
"test:unit": "rstest",
"watch": "rslib build --watch",
"watch:local": "cross-env SOURCEMAP=true rslib build --watch"
Expand Down
4 changes: 2 additions & 2 deletions packages/vscode/rstest.config.mts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { defineConfig } from '@rstest/core';

export default defineConfig({
include: ['tests/unit/**/*.test.ts', 'src/**/*.test.ts'],
include: ['tests/**/*.test.ts'],
// The E2E suite runs in a VS Code extension host, not in Rstest.
exclude: ['**/tests/e2e/**'],
exclude: ['**/e2e/**'],
globals: true,
name: 'rstack-editor',
output: {
Expand Down
2 changes: 1 addition & 1 deletion packages/vscode/src/stacks/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class Rstest implements vscode.Disposable {

/**
* What upstream's `activate()` effectively exported (the `Rstest` instance):
* the E2E suites (`tests/e2e/rstest/`) consume `testController`, `runProfile`
* the E2E suites (`e2e/rstest/`) consume `testController`, `runProfile`
* and `startTestRun`. The shell republishes this object through the
* extension's public exports (`RstackExtensionExports.whenStackActive`).
* All three values are stable for the lifetime of one registration; a
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it, rs } from '@rstest/core';
import type vscode from 'vscode';
import type { DetectionSnapshot } from './types';
import type { DetectionSnapshot } from '../src/types';

// `detection.ts` imports the `vscode` namespace for the watcher/`findFiles`
// paths. `detectionWatchPatterns` is pure, but the module still has to load, so
Expand Down Expand Up @@ -47,7 +47,7 @@ import {
DETECTION_WATCH_NAMES,
DetectionService,
detectionWatchPatterns,
} from './detection';
} from '../src/detection';

/**
* VS Code's glob engine has no nested brace groups. `splitGlobAware`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ rs.mock('vscode', () => {
return { ...vscode, default: vscode };
});

rs.mock('./detection', () => {
rs.mock('../src/detection', () => {
const snapshot = () => ({
folders: [],
isDetected: (stack: string) => harness.detected.has(stack),
Expand All @@ -213,21 +213,21 @@ rs.mock('./detection', () => {
return { DetectionService };
});

rs.mock('./stacks/lint', () => ({
rs.mock('../src/stacks/lint', () => ({
createRslintController: () => harness.controller('rslint'),
}));
rs.mock('./stacks/test', () => ({
rs.mock('../src/stacks/test', () => ({
createRstestController: () => harness.controller('rstest'),
}));
rs.mock('./stacks/fmt', () => ({
rs.mock('../src/stacks/fmt', () => ({
createFmtController: () => harness.controller('fmt'),
}));
rs.mock('./migration', () => ({
rs.mock('../src/migration', () => ({
maybePromptForMigration: async () => undefined,
runSettingsMigration: async () => undefined,
}));

import { activate, deactivate } from './extension';
import { activate, deactivate } from '../src/extension';

const context = { subscriptions: [] } as unknown as vscode.ExtensionContext;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from '@rstest/core';
import { loadRstackConfig } from '../../src/shared/vendored/loadRstackConfig';
import { loadRstackConfig } from '../src/shared/vendored/loadRstackConfig';

// A stand-in for the project's own `rstack` install. It talks to the session
// storage exactly the way rstack's shipped `dist` chunk does — through
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
type LegacyReading,
layerLabel,
planMigration,
} from './migration';
} from '../src/migration';

const reading = (partial: Partial<LegacyReading> & { key: string }) =>
({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { afterEach, beforeEach, describe, expect, it } from '@rstest/core';
import { minimalEdit, pickConfigDir, runRsFmt, type RsFmtRun } from './run';
import {
minimalEdit,
pickConfigDir,
runRsFmt,
type RsFmtRun,
} from '../../../src/stacks/fmt/run';
import { createStubRoot, type StubRoot } from './stubProcess';

describe('pickConfigDir', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'node:path';
import { afterEach, beforeEach, describe, expect, it } from '@rstest/core';
import { FmtStandby, type StandbyKey } from './standby';
import { FmtStandby, type StandbyKey } from '../../../src/stacks/fmt/standby';
import { createStubRoot, type StubRoot } from './stubProcess';

/**
Expand Down
Loading
Loading