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: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ jobs:

- name: test-e2e-legacy
run: pnpm test-e2e --retry 2
env:
TEST_LEGACY: true

- uses: actions/upload-artifact@v4
if: always()
Expand Down
94 changes: 94 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

This is the **Vitest extension for Visual Studio Code** - a VSCode extension that provides test running, debugging, and coverage capabilities for Vitest tests. The extension integrates with VSCode's native TestController API to provide a unified testing experience.

## Architecture

The project uses a monorepo structure with multiple packages that work together:

- **packages/extension** - Main VSCode extension entry point and core logic
- **packages/shared** - Shared utilities and RPC communication between extension and workers
- **packages/worker** - New worker implementation for running Vitest processes
- **packages/worker-legacy** - Legacy worker implementation for older Vitest versions
- **samples/** - Sample projects for testing and demonstration

### Key Components

- **Extension Host** (packages/extension): Manages VSCode integration, test discovery, debugging, coverage
- **Worker Processes** (packages/worker*): Execute Vitest in isolated processes, handle test running and reporting
- **RPC Communication** (packages/shared): Bidirectional communication between extension and workers using birpc
- **API Abstraction**: Supports both child_process and terminal shell types for running Vitest

## Development Commands

### Building
```bash
pnpm build # Build for production (minified)
pnpm dev # Build in development mode with watch and sourcemap
pnpm vscode:prepublish # Prepare for publishing (runs build)
```

### Testing
```bash
pnpm test # Run unit tests (Mocha-based VSCode tests)
pnpm test:watch # Run unit tests in watch mode
pnpm test-e2e # Run end-to-end tests (Vitest-based)
```

### Code Quality
```bash
pnpm typecheck # TypeScript type checking
pnpm lint # Run ESLint
pnpm lint:fix # Run ESLint with auto-fix
```

### Packaging
```bash
pnpm package # Create .vsix package for distribution
```

## Package Manager

Uses **pnpm** with workspaces. The project requires [email protected] as specified in package.json.

## Build System

- **tsup** - Main build tool for bundling TypeScript
- Multiple entry points: extension, workers, setup files
- Supports both CJS and ESM output formats
- External dependencies like 'vscode' and 'vitest' are excluded from bundles

## Testing Infrastructure

- **Unit Tests**: Mocha-based tests in `test/unit/` using `@vscode/test-cli`
- **E2E Tests**: Vitest-based tests in `test/e2e/`
- **VSCode Test Runner**: Uses `.vscode-test.mjs` configuration
- **Samples**: Multiple sample projects for testing different scenarios

## Worker Architecture

The extension uses a multi-process architecture:
- Extension runs in VSCode extension host
- Worker processes execute Vitest in isolation
- Communication via RPC (birpc)
- Supports both legacy and modern Vitest versions
- Can spawn workers via child_process or terminal

## Key Configuration Files

- `tsup.config.ts` - Build configuration with multiple entry points
- `pnpm-workspace.yaml` - Workspace and catalog definitions
- `.vscode-test.mjs` - VSCode test runner configuration
- `tsconfig.base.json` - Base TypeScript configuration

## Development Notes

- Extension activates on workspaces containing Vitest config files
- Supports both standalone configs and Vitest workspace configurations
- Uses static AST analysis for test discovery (experimentalStaticAstCollect)
- Integrates with VSCode's native testing UI and debugging capabilities
- Supports coverage collection and display
12 changes: 8 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -315,21 +315,25 @@
},
"pnpm": {
"overrides": {
"@vitest/browser": "^4.0.0-beta.8",
"@vitest/coverage": "^4.0.0-beta.8",
"@vitest/runner": "^4.0.0-beta.8",
"vitest": "^4.0.0-beta.8",
"@vitest/browser": "^4.0.0-beta.12",
"@vitest/coverage": "^4.0.0-beta.12",
"@vitest/runner": "^4.0.0-beta.12",
"@vitest/utils": "^4.0.0-beta.12",
"vitest": "^4.0.0-beta.12",
"vitest-vscode-extension>@vitest/browser": "^3.2.0",
"vitest-vscode-extension>@vitest/coverage": "^3.2.0",
"vitest-vscode-extension>@vitest/runner": "^3.2.0",
"vitest-vscode-extension>@vitest/utils": "^3.2.0",
"vitest-vscode-extension>vitest": "^3.2.0",
"vitest-vscode-shared>@vitest/browser": "^3.2.0",
"vitest-vscode-shared>@vitest/coverage": "^3.2.0",
"vitest-vscode-shared>@vitest/runner": "^3.2.0",
"vitest-vscode-shared>@vitest/utils": "^3.2.0",
"vitest-vscode-shared>vitest": "^3.2.0",
"vitest-vscode-worker-legacy>@vitest/browser": "^3.2.0",
"vitest-vscode-worker-legacy>@vitest/coverage": "^3.2.0",
"vitest-vscode-worker-legacy>@vitest/runner": "^3.2.0",
"vitest-vscode-worker-legacy>@vitest/utils": "^3.2.0",
"vitest-vscode-worker-legacy>vitest": "^3.2.0"
}
},
Expand Down
1 change: 1 addition & 0 deletions packages/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "0.0.0",
"private": true,
"dependencies": {
"@vitest/browser": "workspace:*",
"vitest": "$vitest",
"vitest-vscode-shared": "workspace:*"
}
Expand Down
6 changes: 5 additions & 1 deletion packages/extension/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,11 @@ export class VitestFolderAPI {
}

waitForCoverageReport() {
return this.meta.rpc.waitForCoverageReport()
if (this.process.closed)
return
return this.meta.rpc.waitForCoverageReport().catch(() => {
// ignore if failed -- can only fail if rpc is closed
})
}

async invalidateIstanbulTestModules(modules: string[] | null) {
Expand Down
2 changes: 1 addition & 1 deletion packages/extension/src/api/child_process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export async function createVitestProcess(pkg: VitestPackage) {
vitest.on('exit', onExit)
vitest.on('error', onError)

waitForWsConnection(wss, pkg, false, 'child_process', false)
waitForWsConnection(wss, pkg, 'child_process', false)
.then((resolved) => {
resolved.handlers.onStdout = (callback: (data: string) => void) => {
stdoutCallbacks.add(callback)
Expand Down
2 changes: 1 addition & 1 deletion packages/extension/src/api/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export async function createVitestTerminalProcess(pkg: VitestPackage): Promise<R
wss.once('connection', () => {
clearTimeout(timeout)
})
waitForWsConnection(wss, pkg, false, 'terminal', !!shellIntegration).then(resolve, reject)
waitForWsConnection(wss, pkg, 'terminal', !!shellIntegration).then(resolve, reject)
})

log.info('[API]', `${formatPkg(pkg)} terminal process ${processId} created`)
Expand Down
14 changes: 8 additions & 6 deletions packages/extension/src/api/ws.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { WorkerEvent, WorkerRunnerOptions } from 'vitest-vscode-shared'
import type { WorkerEvent, WorkerRunnerDebugOptions, WorkerRunnerOptions } from 'vitest-vscode-shared'
import type { WebSocket, WebSocketServer } from 'ws'
import type { ResolvedMeta } from '../api'
import type { VitestPackage } from './pkg'
import { pathToFileURL } from 'node:url'
import { gte } from 'semver'
import { getConfig } from '../config'
import { finalCoverageFileName, setupFilePath } from '../constants'
import { browserSetupFilePath, finalCoverageFileName, setupFilePath } from '../constants'
import { log } from '../log'
import { createVitestRpc } from './rpc'

Expand All @@ -16,7 +16,6 @@ export type WsConnectionMetadata = Omit<ResolvedMeta, 'process'> & {
export function waitForWsConnection(
wss: WebSocketServer,
pkg: VitestPackage,
debug: boolean,
shellType: 'terminal' | 'child_process',
hasShellIntegration: boolean,
) {
Expand All @@ -25,7 +24,7 @@ export function waitForWsConnection(
onWsConnection(
ws,
pkg,
debug,
false,
shellType,
hasShellIntegration,
meta => resolve(meta),
Expand All @@ -52,7 +51,7 @@ export function waitForWsConnection(
export function onWsConnection(
ws: WebSocket,
pkg: VitestPackage,
debug: boolean,
debug: WorkerRunnerDebugOptions | boolean,
shellType: 'terminal' | 'child_process',
hasShellIntegration: boolean,
onStart: (meta: WsConnectionMetadata) => unknown,
Expand Down Expand Up @@ -132,7 +131,10 @@ export function onWsConnection(
pnpLoader: pnpLoader && gte(process.version, '18.19.0')
? pathToFileURL(pnpLoader).toString()
: undefined,
setupFilePath,
setupFilePaths: {
watcher: setupFilePath,
browserDebug: browserSetupFilePath,
},
finalCoverageFileName,
},
debug,
Expand Down
1 change: 1 addition & 0 deletions packages/extension/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const minimumNodeVersion = '18.0.0'
export const distDir = __dirname
export const workerPath = resolve(__dirname, 'worker.js')
export const setupFilePath = resolve(__dirname, 'setupFile.mjs')
export const browserSetupFilePath = resolve(__dirname, 'browserSetupFile.mjs')

export const configGlob = '**/*{vite,vitest}*.config*.{ts,js,mjs,cjs,cts,mts}'
export const workspaceGlob = '**/*vitest.{workspace,projects}*.{ts,js,mjs,cjs,cts,mts,json}'
Expand Down
Loading
Loading