diff --git a/package.json b/package.json index 1828edd..01ea1c0 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,8 @@ "activationEvents": [ "workspaceContains:**/*{vite,vitest}*.config*.{ts,js,mjs,cjs,cts,mts}", "workspaceContains:**/*vitest.{workspace,projects}*.{ts,js,mjs,cjs,cts,mts,json}", - "workspaceContains:node_modules/.bin/vitest" + "workspaceContains:node_modules/.bin/vitest", + "workspaceContains:node_modules/.bin/vp" ], "contributes": { "languages": [ diff --git a/packages/extension/src/api/pkg.ts b/packages/extension/src/api/pkg.ts index a5604c2..c4fea8a 100644 --- a/packages/extension/src/api/pkg.ts +++ b/packages/extension/src/api/pkg.ts @@ -100,6 +100,9 @@ function resolveVitestConfig(showWarning: boolean, configOrWorkspaceFile: vscode } function validateVitestPkg(showWarning: boolean, pkgJsonPath: string, pkg: any) { + if (pkg.name === 'vite-plus') { + return true + } if (pkg.name !== 'vitest') { vscode.window.showErrorMessage( `Package was resolved to "${pkg.name}" instead of "vitest". If you are using "vitest.vitestPackagePath", make sure it points to a "vitest" package.`, diff --git a/packages/extension/src/api/resolve.ts b/packages/extension/src/api/resolve.ts index 4feeff8..049928f 100644 --- a/packages/extension/src/api/resolve.ts +++ b/packages/extension/src/api/resolve.ts @@ -22,13 +22,26 @@ export function resolveVitestPackage(cwd: string, folder: vscode.WorkspaceFolder vitestPackageJsonPath, } } + const vitePlus = resolveVitePlusPackagePath(cwd) + if (vitePlus) { + return { + vitestNodePath: resolveViePlusVitestNodePath(vitePlus), + vitestPackageJsonPath: vitePlus, + } + } - const pnp = resolveVitestPnpPackagePath(folder?.uri.fsPath || cwd) + const pnpCwd = folder?.uri.fsPath || cwd + const pnp = resolvePnp(pnpCwd) if (!pnp) return null + const vitestNodePath + = resolvePnpPackagePath(pnp.pnpApi, 'vitest/node', pnpCwd) + || resolvePnpPackagePath(pnp.pnpApi, 'vite-plus/test/node', pnpCwd) + if (!vitestNodePath) + return null return { - vitestNodePath: pnp.vitestNodePath, - vitestPackageJsonPath: 'vitest/package.json', + vitestNodePath, + vitestPackageJsonPath: '', // we don't read pkg.json for pnp pnp: { loaderPath: pnp.pnpLoader, pnpPath: pnp.pnpPath, @@ -53,20 +66,33 @@ export function resolveVitestPackagePath(cwd: string, folder: vscode.WorkspaceFo } } -export function resolveVitestPnpPackagePath(cwd: string) { +export function resolveVitePlusPackagePath(cwd: string) { + try { + const result = require.resolve('vite-plus/package.json', { + paths: [cwd], + }) + delete require.cache['vite-plus/package.json'] + delete require.cache[result] + return result + } + catch { + return null + } +} + +export function resolvePnp(cwd: string) { try { const pnpPath = findUpSync(['.pnp.js', '.pnp.cjs'], { cwd }) if (pnpPath == null) { return null } const pnpApi = _require(pnpPath) - const vitestNodePath = pnpApi.resolveRequest('vitest/node', cwd) return { pnpLoader: require.resolve('./.pnp.loader.mjs', { paths: [dirname(pnpPath)], }), pnpPath, - vitestNodePath, + pnpApi, } } catch { @@ -74,6 +100,20 @@ export function resolveVitestPnpPackagePath(cwd: string) { } } +export function resolvePnpPackagePath(pnpApi: any, pkg: 'vitest/node' | 'vite-plus/test/node', cwd: string): string | null { + try { + const vitestNodePath = pnpApi.resolveRequest(pkg, cwd) + return vitestNodePath + } + catch { + return null + } +} + +export function resolveViePlusVitestNodePath(vitePlusPkgPath: string) { + return resolve(dirname(vitePlusPkgPath), './dist/test/node.js') +} + export function resolveVitestNodePath(vitestPkgPath: string) { return resolve(dirname(vitestPkgPath), './dist/node.js') } -- 2.51.2 From 813cf69a6237c1ab4134bdc45fb75025edaf7d0c Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Thu, 26 Feb 2026 14:47:49 +0100 Subject: [PATCH 02/64] chore: release v1.44.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 01ea1c0..b44686b 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "name": "explorer", "displayName": "Vitest", "type": "commonjs", - "version": "1.44.0", + "version": "1.44.1", "packageManager": "pnpm@10.11.1", "description": "A Vite-native testing framework. It's fast!", "author": "Vitest Team", -- 2.51.2 From 41f095a2ca7d7d635aa96b8f56389744be08e8b5 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Thu, 5 Mar 2026 09:54:21 +0100 Subject: [PATCH 03/64] fix: disable validation for vite-plus --- packages/extension/src/api/pkg.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/api/pkg.ts b/packages/extension/src/api/pkg.ts index c4fea8a..4283c57 100644 --- a/packages/extension/src/api/pkg.ts +++ b/packages/extension/src/api/pkg.ts @@ -100,7 +100,7 @@ function resolveVitestConfig(showWarning: boolean, configOrWorkspaceFile: vscode } function validateVitestPkg(showWarning: boolean, pkgJsonPath: string, pkg: any) { - if (pkg.name === 'vite-plus') { + if (pkg.name === 'vite-plus' || pkg.name === '@voidzero-dev/vite-plus-test') { return true } if (pkg.name !== 'vitest') { -- 2.51.2 From 988b8fc222416395879ac7d55f53f5c1ea3e5f32 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Thu, 5 Mar 2026 09:54:57 +0100 Subject: [PATCH 04/64] chore: release v1.44.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b44686b..0581842 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "name": "explorer", "displayName": "Vitest", "type": "commonjs", - "version": "1.44.1", + "version": "1.44.2", "packageManager": "pnpm@10.11.1", "description": "A Vite-native testing framework. It's fast!", "author": "Vitest Team", -- 2.51.2 From 52fad9149a7623258b5b6059ab1422a841fa7ddb Mon Sep 17 00:00:00 2001 From: Vladimir Date: Mon, 9 Mar 2026 19:09:16 +0100 Subject: [PATCH 05/64] fix!: do not keep persistent process by default (#743) --- .github/workflows/ci.yml | 4 +- .vscode/launch.json | 2 + package.json | 7 +- packages/extension/src/api.ts | 414 ++---------- packages/extension/src/apiProcess.ts | 359 +++++++++++ packages/extension/src/config.ts | 2 +- packages/extension/src/constants.ts | 1 + packages/extension/src/coverage.ts | 14 +- packages/extension/src/debug.ts | 39 +- packages/extension/src/extension.ts | 313 +++++---- packages/extension/src/log.ts | 6 +- packages/extension/src/runQueue.ts | 236 +++++++ packages/extension/src/runner.ts | 597 ++++++++---------- packages/extension/src/schemaProvider.ts | 2 +- .../src/{api => spawn}/child_process.ts | 49 +- packages/extension/src/{api => spawn}/pkg.ts | 0 .../extension/src/{api => spawn}/resolve.ts | 0 packages/extension/src/{api => spawn}/rpc.ts | 0 .../extension/src/{api => spawn}/terminal.ts | 101 +-- .../extension/src/{api => spawn}/types.ts | 3 - packages/extension/src/{api => spawn}/ws.ts | 28 +- packages/extension/src/state.ts | 23 + packages/extension/src/testTree.ts | 27 +- packages/extension/src/testTreeData.ts | 6 +- packages/extension/src/utils.ts | 2 +- packages/extension/src/watcher.ts | 41 +- .../extension/src/worker/browserSetupFile.ts | 2 +- .../src/worker/browserSetupFileLegacy.ts | 7 + packages/extension/src/worker/index.ts | 4 + packages/shared/src/index.ts | 17 +- packages/shared/src/utils.ts | 39 +- packages/worker-legacy/src/coverage.ts | 109 ---- packages/worker-legacy/src/index.ts | 50 +- packages/worker-legacy/src/reporter.ts | 26 +- packages/worker-legacy/src/watcher.ts | 52 +- packages/worker-legacy/src/worker.ts | 64 +- packages/worker/src/coverage.ts | 40 -- packages/worker/src/index.ts | 27 +- packages/worker/src/reporter.ts | 12 +- packages/worker/src/runner.ts | 9 +- packages/worker/src/worker.ts | 51 +- pnpm-lock.yaml | 194 +++--- samples/basic-v4/package.json | 1 + samples/basic-v4/test/console.test.ts | 7 +- samples/basic-v4/vitest.config.ts | 3 + samples/browser/.vscode/settings.json | 1 + test/e2e/runner.test.ts | 4 +- test/e2e/utils/helper.ts | 6 +- test/e2e/utils/tester.ts | 23 +- test/unit/pkg.test.ts | 2 +- tsdown.config.mjs | 7 +- 51 files changed, 1580 insertions(+), 1453 deletions(-) create mode 100644 packages/extension/src/apiProcess.ts create mode 100644 packages/extension/src/runQueue.ts rename packages/extension/src/{api => spawn}/child_process.ts (75%) rename packages/extension/src/{api => spawn}/pkg.ts (100%) rename packages/extension/src/{api => spawn}/resolve.ts (100%) rename packages/extension/src/{api => spawn}/rpc.ts (100%) rename packages/extension/src/{api => spawn}/terminal.ts (61%) rename packages/extension/src/{api => spawn}/types.ts (55%) rename packages/extension/src/{api => spawn}/ws.ts (79%) create mode 100644 packages/extension/src/state.ts create mode 100644 packages/extension/src/worker/browserSetupFileLegacy.ts delete mode 100644 packages/worker-legacy/src/coverage.ts delete mode 100644 packages/worker/src/coverage.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba783fb..5a0173d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,7 +56,7 @@ jobs: if: always() with: name: 'test-results-${{ matrix.os }}' - path: 'test-results/${{ matrix.os }}' + path: test-results test-legacy: runs-on: ${{ matrix.os }} @@ -92,4 +92,4 @@ jobs: if: always() with: name: 'test-results-legacy-${{ matrix.os }}' - path: 'test-results/${{ matrix.os }}' + path: test-results diff --git a/.vscode/launch.json b/.vscode/launch.json index 2fb9934..f32d520 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -20,6 +20,7 @@ "type": "extensionHost", "request": "launch", "args": [ + "--disable-extensions", "--extensionDevelopmentPath=${workspaceFolder}", "${workspaceFolder}/samples/basic-v4" ], @@ -30,6 +31,7 @@ "type": "extensionHost", "request": "launch", "args": [ + "--disable-extensions", "--extensionDevelopmentPath=${workspaceFolder}", "${workspaceFolder}/samples/browser" ], diff --git a/package.json b/package.json index 0581842..1768305 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "icon": "img/icon.png", "pricing": "Free", "engines": { - "vscode": "^1.77.0" + "vscode": "^1.88.0" }, "activationEvents": [ "workspaceContains:**/*{vite,vitest}*.config*.{ts,js,mjs,cjs,cts,mts}", @@ -98,6 +98,11 @@ "title": "Copy Test Errors", "command": "vitest.copyTestItemErrors", "category": "Vitest" + }, + { + "title": "Toggle Configs", + "command": "vitest.toggleConfigs", + "category": "Vitest" } ], "menus": { diff --git a/packages/extension/src/api.ts b/packages/extension/src/api.ts index 1067b4f..38da74e 100644 --- a/packages/extension/src/api.ts +++ b/packages/extension/src/api.ts @@ -1,55 +1,20 @@ -import type { ExtensionTestSpecification, ModuleDefinitionDurationsDiagnostic, SerializedProject } from 'vitest-vscode-shared' -import type { VitestPackage } from './api/pkg' -import type { ExtensionWorkerEvents, VitestExtensionRPC } from './api/rpc' -import type { ExtensionWorkerProcess } from './api/types' -import type { TestFileMetadata } from './testTreeData' -import { readFileSync } from 'node:fs' +import type { ExtensionTestFileSpecification, ExtensionTestSpecification, ModuleDefinitionDurationsDiagnostic } from 'vitest-vscode-shared' +import type * as vscode from 'vscode' +import type { VitestPackage } from './spawn/pkg' import { dirname, isAbsolute } from 'node:path' import { normalize, relative } from 'pathe' -import pm from 'picomatch' -import { createQueuedHandler } from 'vitest-vscode-shared' -import * as vscode from 'vscode' -import { createVitestProcess } from './api/child_process' -import { createVitestTerminalProcess } from './api/terminal' -import { getConfig } from './config' +import { VitestProcessAPI, VitestProjectConfig, withProcess } from './apiProcess' import { log } from './log' import { showVitestError } from './utils' export class VitestAPI { - private disposing = false - private _disposes: (() => void)[] = [] - constructor( - private readonly api: VitestFolderAPI[], - ) { - this.processes.forEach((process) => { - const warn = (error: any) => { - if (!this.disposing) - showVitestError('Vitest process failed', error) - } - const dispose = process.onError(warn) - this._disposes.push(dispose) - }) - } - - onUnexpectedExit(callback: (code: number | null) => void) { - this.processes.forEach((process) => { - const onExit = (code: number | null) => { - if (!this.disposing) - callback(code) - } - const dispose = process.onExit(onExit) - this._disposes.push(dispose) - }) - } - - forEach(callback: (api: VitestFolderAPI, index: number) => T) { - return this.api.forEach(callback) - } + public readonly processes: VitestProcessAPI[], + ) {} async getSourceModuleDiagnostic(moduleId: string) { const allDiagnostic = await Promise.all( - this.folderAPIs.map(api => api.getSourceModuleDiagnostic(moduleId)), + this.processes.map(api => api.getSourceModuleDiagnostic(moduleId)), ) const modules = allDiagnostic[0]?.modules || [] const untrackedModules = allDiagnostic[0]?.untrackedModules || [] @@ -88,7 +53,7 @@ export class VitestAPI { getModuleEnvironments(moduleId: string) { return Promise.all( - this.api.map(async (api) => { + this.processes.map(async (api) => { return { api, projects: await api.getModuleEnvironments(moduleId), @@ -97,243 +62,19 @@ export class VitestAPI { ) } - get folderAPIs() { - return this.api - } - async dispose() { - this.disposing = true - try { - this._disposes.forEach(dispose => dispose()) - await Promise.all(this.api.map(api => api.dispose())) - } - finally { - this.disposing = false - } - } - - private get processes() { - return this.api.map(api => api.process) + await Promise.all(this.processes.map(api => api.dispose())) } } -export class VitestFolderAPI { - readonly id: string - readonly tag: vscode.TestTag - readonly workspaceFolder: vscode.WorkspaceFolder - - private handlers: ResolvedMeta['handlers'] - - public createDate = Date.now() - - constructor( - private pkg: VitestPackage, - private meta: ResolvedMeta, - ) { - const normalizedId = normalize(pkg.id) - this.id = normalizedId - this.workspaceFolder = pkg.folder - this.handlers = meta.handlers - this.tag = new vscode.TestTag(pkg.prefix) - } - - get processId() { - return this.process.id - } - - get prefix() { - return this.pkg.prefix - } - - get process() { - return this.meta.process - } - - get configs() { - return this.meta.projects.map(p => p.config).filter(n => n != null) - } - - get workspaceSource() { - return this.meta.workspaceSource - } - - get version() { - return this.pkg.version - } - - get package() { - return this.pkg - } - - getTransformedModule(project: string, environment: string, moduleId: string) { - return this.meta.rpc.getTransformedModule(project, environment, moduleId) - } - - getSourceModuleDiagnostic(moduleId: string) { - return this.meta.rpc.getSourceModuleDiagnostic(moduleId) - } - - async getModuleEnvironments(moduleId: string) { - return this.meta.rpc.getModuleEnvironments(moduleId) - } - - async runFiles(specs?: ExtensionTestSpecification[] | string[], testNamePatern?: string) { - await this.meta.rpc.runTests(normalizeSpecs(specs), testNamePatern) - } - - async updateSnapshots(specs?: ExtensionTestSpecification[] | string[], testNamePatern?: string) { - await this.meta.rpc.updateSnapshots(normalizeSpecs(specs), testNamePatern) - } - - getFiles() { - return this.meta.rpc.getFiles() - } - - getPotentialTestFileMetadata(file: string): TestFileMetadata[] { - const metadata: TestFileMetadata[] = [] - let fileContent: string - for (const project of this.meta.projects) { - if (this.matchesTestGlob(project, file, () => (fileContent ??= readFileSync(file, 'utf-8')))) { - metadata.push({ - pool: project.pool, - project: project.name, - browser: project.browser, - }) - } - } - return metadata - } - - matchesTestGlob(project: SerializedProject, moduleId: string, source: () => string) { - const relativeId = relative(project.dir || project.root, moduleId) - if (pm.isMatch(relativeId, project.exclude)) { - return false - } - if (pm.isMatch(relativeId, project.include)) { - return true - } - if ( - project.includeSource?.length - && pm.isMatch(relativeId, project.includeSource) - ) { - const code = source() - if (code.includes('import.meta.vitest')) { - return true - } - } - return false - } - - onFileCreated = createQueuedHandler(async (files: string[]) => { - if (this.process.closed) { - return - } - return this.meta.rpc.onFilesCreated(files).catch((err) => { - log.error('[API]', 'Failed to notify Vitest about file creation', err) - }) - }) - - onFileChanged = createQueuedHandler(async (files: string[]) => { - if (this.process.closed) { - return - } - return this.meta.rpc.onFilesChanged(files).catch((err) => { - log.error('[API]', 'Failed to notify Vitest about file change', err) - }) - }) - - async collectTests(projectName: string, testFile: string) { - return this._collectTests(`${projectName}\0${normalize(testFile)}`) - } - - private _collectTests = createQueuedHandler(async (testsQueue: string[]) => { - if (this.process.closed) { - return - } - const tests = Array.from(testsQueue).map((spec) => { - const [projectName, filepath] = spec.split('\0', 2) - return [projectName, filepath] as [string, string] - }) - const root = this.workspaceFolder.uri.fsPath - log.info('[API]', `Collecting tests: ${tests.map(t => `${relative(root, t[1])}${t[0] ? ` [${t[0]}]` : ''}`).join(', ')}`) - return this.meta.rpc.collectTests(tests) - }) - - async dispose() { - this.handlers.clearListeners() - delete require.cache[this.meta.pkg.vitestPackageJsonPath] - delete require.cache[this.meta.pkg.vitestNodePath] - if (!this.meta.process.closed) { - try { - await this.meta.rpc.close() - log.info('[API]', `Vitest process ${this.processId} closed successfully`) - } - catch (err) { - log.error('[API]', 'Failed to close Vitest RPC', err) - } - await this.meta.process.close().catch((err) => { - log.error('[API]', 'Failed to close Vitest process', err) - }) - } - } - - async cancelRun() { - if (this.process.closed) - return - await this.meta.rpc.cancelRun() - } - - 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) { - await this.meta.rpc.invalidateIstanbulTestModules(modules) - } - - async enableCoverage() { - await this.meta.rpc.enableCoverage() - } - - async disableCoverage() { - await this.meta.rpc.disableCoverage() - } - - async watchTests(files?: ExtensionTestSpecification[] | string[], testNamePattern?: string) { - await this.meta.rpc.watchTests(normalizeSpecs(files), testNamePattern) - } - - async unwatchTests() { - await this.meta.rpc.unwatchTests() - } - - onConsoleLog = this.createHandler('onConsoleLog') - onTaskUpdate = this.createHandler('onTaskUpdate') - onTestRunEnd = this.createHandler('onTestRunEnd') - onTestRunStart = this.createHandler('onTestRunStart') - onCollected = this.createHandler('onCollected') - - clearListeners(name?: Exclude) { - if (name) - this.handlers.removeListener(name, this.handlers[name]) - - this.handlers.clearListeners() - } - - private createHandler>(name: K) { - return (callback: ExtensionWorkerEvents[K]) => { - this.handlers[name](callback as any) - } - } -} - -export async function resolveVitestAPI(workspaceConfigs: VitestPackage[], configs: VitestPackage[]) { +export async function resolveVitestAPI( + workspaceConfigs: VitestPackage[], + configs: VitestPackage[], + cancelToken: vscode.CancellationToken | undefined, + onResolved?: (result: DiscoveryResult) => void, +) { const usedConfigs = new Set() - const workspacePromises = workspaceConfigs.map(pkg => createVitestFolderAPI(usedConfigs, pkg)) + const workspacePromises = workspaceConfigs.map(pkg => createVitestProcessAPI(usedConfigs, pkg)) if (workspacePromises.length) { log.info('[API]', `Resolving workspace configs: ${workspaceConfigs.map(p => relative(p.folder.uri.fsPath, p.id)).join(', ')}`) @@ -341,13 +82,14 @@ export async function resolveVitestAPI(workspaceConfigs: VitestPackage[], config const resolvedApisPromises = await Promise.allSettled(workspacePromises) const errors: unknown[] = [] - const apis: VitestFolderAPI[] = [] - for (const api of resolvedApisPromises) { - if (api.status === 'fulfilled') { - apis.push(api.value) + const apis: VitestProcessAPI[] = [] + for (const result of resolvedApisPromises) { + if (result.status === 'fulfilled') { + apis.push(result.value.api) + onResolved?.(result.value) } else { - errors.push(api.reason) + errors.push(result.reason) } } @@ -359,13 +101,9 @@ export async function resolveVitestAPI(workspaceConfigs: VitestPackage[], config return depthA - depthB }) - const maximumConfigs = getConfig().maximumConfigs ?? 5 - const workspaceRoots: string[] = apis - .map(api => api.workspaceSource ? dirname(api.workspaceSource) : null) - .filter(api => api != null) - - let configsResolved = 0 + .map(r => r.workspaceSource ? dirname(r.workspaceSource) : null) + .filter(r => r != null) if (configsToResolve.length) { log.info('[API]', `Resolving configs: ${configsToResolve.map(p => relative(dirname(p.cwd), p.id)).join(', ')}`) @@ -385,23 +123,21 @@ export async function resolveVitestAPI(workspaceConfigs: VitestPackage[], config continue } - configsResolved++ - - if (configsResolved > maximumConfigs) { - warnPerformanceConfigLimit(configsToResolve) - break - } - try { - const api = await createVitestFolderAPI(usedConfigs, pkg) - apis.push(api) - if (api.workspaceSource) { - workspaceRoots.push(dirname(api.workspaceSource)) + const result = await createVitestProcessAPI(usedConfigs, pkg) + apis.push(result.api) + onResolved?.(result) + if (result.api.workspaceSource) { + workspaceRoots.push(dirname(result.api.workspaceSource)) } } catch (err: unknown) { errors.push(err) } + + if (cancelToken?.isCancellationRequested) { + break + } } if (!apis.length) { @@ -425,82 +161,26 @@ function isCoveredByWorkspace(workspacesRoots: string[], currentConfig: string): }) } -function warnPerformanceConfigLimit(configsToResolve: VitestPackage[]) { - const maximumConfigs = getConfig().maximumConfigs ?? 5 - const warningMessage = [ - 'Vitest found multiple projects.', - `The extension will use only the first ${maximumConfigs} due to performance concerns.`, - 'Consider using a projects configuration to group your configs or increase', - 'the limit via "vitest.maximumConfigs" option.', - ].join(' ') - - const folders = Array.from(new Set(configsToResolve.map(c => c.folder))) - // remove all but the first 5 - const discardedConfigs = configsToResolve.splice(maximumConfigs) - - if (folders.every(f => getConfig(f).disableWorkspaceWarning !== true)) { - vscode.window.showWarningMessage( - warningMessage, - 'Documentation', - 'Disable notification', - ).then((result) => { - if (result === 'Documentation') { - vscode.commands.executeCommand( - 'vscode.open', - // /workspace redirects to /projects on the new version - vscode.Uri.parse('https://vitest.dev/guide/workspace'), - ) - } +interface DiscoveryResult { + api: VitestProcessAPI + files: ExtensionTestFileSpecification[] +} - if (result === 'Disable notification') { - folders.forEach((folder) => { - const rootConfig = vscode.workspace.getConfiguration('vitest', folder) - rootConfig.update('disableWorkspaceWarning', true) - }) +async function createVitestProcessAPI(usedConfigs: Set, pkg: VitestPackage): Promise { + return withProcess(pkg, async (meta) => { + meta.projects.forEach((project) => { + if (project.config) { + usedConfigs.add(project.config) } }) - } - else { - log.info(warningMessage) - log.info(`Discarded config files: ${discardedConfigs.map(x => x.workspaceFile || x.configFile).join(', ')}`) - } -} - -async function createVitestFolderAPI(usedConfigs: Set, pkg: VitestPackage) { - const config = getConfig(pkg.folder) - if (config.cliArguments && !pkg.arguments) { - pkg.arguments = `vitest ${config.cliArguments}` - } - const vitest = config.shellType === 'terminal' - ? await createVitestTerminalProcess(pkg) - : await createVitestProcess(pkg) - vitest.projects.forEach((project) => { - if (project.config) { - usedConfigs.add(project.config) - } + const files = await meta.rpc.getFiles() + const config = new VitestProjectConfig(pkg, meta.projects, meta.workspaceSource) + const api = new VitestProcessAPI(config) + return { api, files } }) - return new VitestFolderAPI(pkg, vitest) -} - -export interface ResolvedMeta { - rpc: VitestExtensionRPC - process: ExtensionWorkerProcess - workspaceSource: string | false - pkg: VitestPackage - projects: SerializedProject[] - handlers: { - onProcessLog: (listener: ExtensionWorkerEvents['onProcessLog']) => void - onConsoleLog: (listener: ExtensionWorkerEvents['onConsoleLog']) => void - onTaskUpdate: (listener: ExtensionWorkerEvents['onTaskUpdate']) => void - onTestRunEnd: (listener: ExtensionWorkerEvents['onTestRunEnd']) => void - onTestRunStart: (listener: ExtensionWorkerEvents['onTestRunStart']) => void - onCollected: (listener: ExtensionWorkerEvents['onCollected']) => void - clearListeners: () => void - removeListener: (name: string, listener: any) => void - } } -function normalizeSpecs(specs?: string[] | ExtensionTestSpecification[]) { +export function normalizeSpecs(specs?: string[] | ExtensionTestSpecification[]) { if (!specs) { return specs } diff --git a/packages/extension/src/apiProcess.ts b/packages/extension/src/apiProcess.ts new file mode 100644 index 0000000..ed63c59 --- /dev/null +++ b/packages/extension/src/apiProcess.ts @@ -0,0 +1,359 @@ +import type { SerializedProject } from 'vitest-vscode-shared' +import type { VitestPackage } from './spawn/pkg' +import type { ExtensionWorkerEvents, VitestExtensionRPC } from './spawn/rpc' +import type { ExtensionWorkerProcess } from './spawn/types' +import type { ProcessSpawnOptions } from './spawn/ws' +import type { TestFileMetadata } from './testTreeData' +import { readFileSync } from 'node:fs' +import { normalize, relative } from 'pathe' +import pm from 'picomatch' +import { createQueuedHandler } from 'vitest-vscode-shared' +import * as vscode from 'vscode' +import { getConfig } from './config' +import { log } from './log' +import { createVitestProcess } from './spawn/child_process' +import { createVitestTerminalProcess } from './spawn/terminal' + +export class VitestProjectConfig { + readonly id: string + readonly tag: vscode.TestTag + readonly workspaceFolder: vscode.WorkspaceFolder + + constructor( + readonly pkg: VitestPackage, + readonly projects: SerializedProject[], + readonly workspaceSource: string | false, + ) { + this.id = normalize(pkg.id) + this.workspaceFolder = pkg.folder + this.tag = new vscode.TestTag(pkg.prefix) + } + + get prefix() { + return this.pkg.prefix + } + + get configs() { + return this.projects.map(p => p.config).filter(n => n != null) + } + + get version() { + return this.pkg.version + } + + get package() { + return this.pkg + } + + getPotentialTestFileMetadata(file: string): TestFileMetadata[] { + const metadata: TestFileMetadata[] = [] + let fileContent: string + for (const project of this.projects) { + if (this.matchesTestGlob(project, file, () => (fileContent ??= readFileSync(file, 'utf-8')))) { + metadata.push({ + pool: project.pool, + project: project.name, + browser: project.browser, + }) + } + } + return metadata + } + + matchesTestGlob(project: SerializedProject, moduleId: string, source: () => string) { + const relativeId = relative(project.dir || project.root, moduleId) + if (pm.isMatch(relativeId, project.exclude)) { + return false + } + if (pm.isMatch(relativeId, project.include)) { + return true + } + if ( + project.includeSource?.length + && pm.isMatch(relativeId, project.includeSource) + ) { + const code = source() + if (code.includes('import.meta.vitest')) { + return true + } + } + return false + } +} + +export class VitestProcessAPI { + readonly config: VitestProjectConfig + + // Listeners for collection results (registered by testTree) + private collectionListeners: ExtensionWorkerEvents['onCollected'][] = [] + + // Currently active process (for cancellation, continuous run) + private currentMeta: ResolvedMeta | undefined + private _spawningPersistentProcess = false + private _pendingFileChanges: string[] = [] + + constructor(config: VitestProjectConfig) { + this.config = config + } + + /** + * Create a VitestFolderAPI for debug sessions where the process is + * already spawned by the debug launcher. spawnForRun() will return + * a handle wrapping the existing process (without closing it). + */ + static forDebug(pkg: VitestPackage, meta: ResolvedMeta): VitestProcessAPI { + const config = new VitestProjectConfig(pkg, meta.projects, meta.workspaceSource) + const api = new VitestProcessAPI(config) + api.currentMeta = meta + return api + } + + // --- Delegated from config (local, no process needed) --- + + get id() { + return this.config.id + } + + get tag() { + return this.config.tag + } + + get workspaceFolder() { + return this.config.workspaceFolder + } + + get prefix() { + return this.config.prefix + } + + get configs() { + return this.config.configs + } + + get workspaceSource() { + return this.config.workspaceSource + } + + get package() { + return this.config.package + } + + getPersistentProcessMeta() { + return this.currentMeta + } + + get isSpawningPersistentProcess() { + return this._spawningPersistentProcess + } + + getPotentialTestFileMetadata(file: string): TestFileMetadata[] { + return this.config.getPotentialTestFileMetadata(file) + } + + matchesTestGlob(project: SerializedProject, moduleId: string, source: () => string) { + return this.config.matchesTestGlob(project, moduleId, source) + } + + // --- Collection (on-demand, batched ~300ms) --- + + onCollected(callback: ExtensionWorkerEvents['onCollected']) { + this.collectionListeners.push(callback) + } + + collectTests(projectName: string, testFile: string) { + return this._collectTests(`${projectName}\0${normalize(testFile)}`) + } + + private _collectTests = createQueuedHandler(async (testsQueue: string[]) => { + const tests = testsQueue.map((spec) => { + const [projectName, filepath] = spec.split('\0', 2) + return [projectName, filepath] as [string, string] + }) + const root = this.workspaceFolder.uri.fsPath + log.info('[API]', `Collecting tests: ${tests.map(t => `${relative(root, t[1])}${t[0] ? ` [${t[0]}]` : ''}`).join(', ')}`) + const projects = [...new Set(tests.map(([projectName]) => projectName))] + try { + // TODO make sure errors are reported during collection (throw error in the config, for example) + await withProcess(this.config.pkg, async (meta) => { + meta.handlers.onCollected((file, collecting) => { + for (const listener of this.collectionListeners) { + listener(file, collecting) + } + }) + await meta.rpc.collectTests(tests) + }, { projects }) + } + catch (err) { + log.error('[API]', 'Collection failed:', err) + } + }, 300) + + // --- Running (on-demand, spawned per run) --- + + /** + * Spawn a process for running tests. The caller (TestRunner) manages + * event wiring and lifecycle. Returns a RunHandle. + */ + async spawnForRun(options?: ProcessSpawnOptions): Promise { + // For debug sessions, the process is already spawned — return a non-closing handle + if (this.currentMeta && !this.currentMeta.process.closed) { + const meta = this.currentMeta + return { + rpc: meta.rpc, + process: meta.process, + handlers: meta.handlers, + async dispose() { + // Debug process lifecycle is managed by the debug session, not by us + }, + } + } + this._spawningPersistentProcess = true + const meta = await spawnVitestProcess(this.config.pkg, options).finally(() => { + this._spawningPersistentProcess = false + }) + this.currentMeta = meta + if (this._pendingFileChanges.length) { + const pending = this._pendingFileChanges + this._pendingFileChanges = [] + meta.rpc.onFilesChanged(pending).catch((err) => { + log.error('[API]', 'Failed to notify Vitest about pending file changes', err) + }) + } + return { + rpc: meta.rpc, + process: meta.process, + handlers: meta.handlers, + dispose: async () => { + this.currentMeta = undefined + await meta.dispose().catch((err) => { + log.error('[API]', 'Failed to close Vitest process', err) + }) + }, + } + } + + async cancelRun() { + if (!this.currentMeta || this.currentMeta.process.closed) + return + await this.currentMeta.rpc.cancelRun() + } + + // --- Module diagnostics (from run process, before closing) --- + + async getSourceModuleDiagnostic(moduleId: string) { + if (!this.currentMeta || this.currentMeta.process.closed) + return { modules: [], untrackedModules: [] } + return this.currentMeta.rpc.getSourceModuleDiagnostic(normalize(moduleId)) + } + + async getModuleEnvironments(moduleId: string) { + if (!this.currentMeta || this.currentMeta.process.closed) + return [] + return this.currentMeta.rpc.getModuleEnvironments(normalize(moduleId)) + } + + async getTransformedModule(project: string, environment: string, moduleId: string) { + if (!this.currentMeta || this.currentMeta.process.closed) { + return null + } + return this.currentMeta.rpc.getTransformedModule(project, environment, normalize(moduleId)) + } + + onFileChanged = createQueuedHandler(async (files: string[]) => { + if (this._spawningPersistentProcess) { + this._pendingFileChanges.push(...files) + return + } + if (!this.currentMeta || this.currentMeta.process.closed) { + return + } + return this.currentMeta.rpc.onFilesChanged(files.map(f => normalize(f))).catch((err) => { + log.error('[API]', 'Failed to notify Vitest about file change', err) + }) + }) + + // --- Cleanup --- + + async dispose() { + delete require.cache[this.config.pkg.vitestPackageJsonPath] + delete require.cache[this.config.pkg.vitestNodePath] + if (this.currentMeta && !this.currentMeta.process.closed) { + await this.currentMeta.dispose().catch((err) => { + log.error('[API]', 'Failed to close Vitest process', err) + }) + } + this.currentMeta = undefined + this.collectionListeners = [] + } +} + +export interface RunHandle { + rpc: VitestExtensionRPC + process: ExtensionWorkerProcess + handlers: ResolvedMeta['handlers'] + dispose: () => Promise +} + +export interface RunHandlers { + onCollected: ExtensionWorkerEvents['onCollected'] + onTaskUpdate: ExtensionWorkerEvents['onTaskUpdate'] + onTestRunStart: ExtensionWorkerEvents['onTestRunStart'] + onTestRunEnd: ExtensionWorkerEvents['onTestRunEnd'] + onConsoleLog: ExtensionWorkerEvents['onConsoleLog'] +} + +export interface ResolvedMeta { + rpc: VitestExtensionRPC + process: ExtensionWorkerProcess + workspaceSource: string | false + pkg: VitestPackage + projects: SerializedProject[] + handlers: { + onProcessLog: (listener: ExtensionWorkerEvents['onProcessLog']) => void + onConsoleLog: (listener: ExtensionWorkerEvents['onConsoleLog']) => void + onTaskUpdate: (listener: ExtensionWorkerEvents['onTaskUpdate']) => void + onTestRunEnd: (listener: ExtensionWorkerEvents['onTestRunEnd']) => void + onTestRunStart: (listener: ExtensionWorkerEvents['onTestRunStart']) => void + onCollected: (listener: ExtensionWorkerEvents['onCollected']) => void + clearListeners: () => void + removeListener: (name: string, listener: any) => void + } + /** + * Closes vitest process, will force exit with timeout, stops the WS server. + */ + dispose: () => Promise +} + +export function spawnVitestProcess(pkg: VitestPackage, options?: ProcessSpawnOptions): Promise { + const config = getConfig(pkg.folder) + if (config.cliArguments && !pkg.arguments) { + pkg.arguments = `vitest ${config.cliArguments}` + } + const projects = options?.projects?.join(', ') + if (projects) { + log.verbose?.('[API]', `Filtering projects: ${projects}`) + } + return config.shellType === 'terminal' + ? createVitestTerminalProcess(pkg, options) + : createVitestProcess(pkg, options) +} + +export async function withProcess( + pkg: VitestPackage, + fn: (meta: ResolvedMeta) => Promise, + options?: ProcessSpawnOptions, +): Promise { + log.verbose?.('[API]', 'Spawning on-demand process...') + const start = performance.now() + const meta = await spawnVitestProcess(pkg, options) + try { + return await fn(meta) + } + finally { + await meta.dispose().catch((err) => { + log.error('[API]', 'Failed to close Vitest process', err) + }) + const duration = Math.round(performance.now() - start) + log.verbose?.('[API]', `On-demand process finished in ${duration}ms`) + } +} diff --git a/packages/extension/src/config.ts b/packages/extension/src/config.ts index 4274ba7..76fbde1 100644 --- a/packages/extension/src/config.ts +++ b/packages/extension/src/config.ts @@ -92,7 +92,7 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) { configSearchPatternInclude, configSearchPatternExclude, ignoreWorkspace, - maximumConfigs: get('maximumConfigs', 5), + // maximumConfigs: get('maximumConfigs', 5), nodeExecutable: resolveConfigPath(nodeExecutable), disableWorkspaceWarning: get('disableWorkspaceWarning', false), debuggerPort: get('debuggerPort') || undefined, diff --git a/packages/extension/src/constants.ts b/packages/extension/src/constants.ts index aaaec52..3de8f62 100644 --- a/packages/extension/src/constants.ts +++ b/packages/extension/src/constants.ts @@ -6,6 +6,7 @@ export const minimumNodeVersion = '18.0.0' export const distDir = __dirname export const workerPath = resolve(__dirname, 'worker.js') +export const browserSetupFilePathLegacy = resolve(__dirname, 'browserSetupFileLegacy.mjs') export const browserSetupFilePath = resolve(__dirname, 'browserSetupFile.mjs') export const configGlob = '**/*{vite,vitest}*.config*.{ts,js,mjs,cjs,cts,mts}' diff --git a/packages/extension/src/coverage.ts b/packages/extension/src/coverage.ts index 3161131..b25de72 100644 --- a/packages/extension/src/coverage.ts +++ b/packages/extension/src/coverage.ts @@ -1,15 +1,3 @@ -import { readFileSync } from 'node:fs' -import { IstanbulCoverageContext, IstanbulMissingCoverageError } from 'istanbul-to-vscode' -import { join } from 'pathe' -import { finalCoverageFileName } from './constants' +import { IstanbulCoverageContext } from 'istanbul-to-vscode' export const coverageContext = new IstanbulCoverageContext() - -export function readCoverageReport(reportsDirectory: string) { - try { - return JSON.parse(readFileSync(join(reportsDirectory, finalCoverageFileName), 'utf8')) - } - catch (err: any) { - throw new IstanbulMissingCoverageError(reportsDirectory, err) - } -} diff --git a/packages/extension/src/debug.ts b/packages/extension/src/debug.ts index 9d39aac..3661677 100644 --- a/packages/extension/src/debug.ts +++ b/packages/extension/src/debug.ts @@ -1,9 +1,9 @@ -import type { VitestPackage } from './api/pkg' -import type { ExtensionWorkerProcess } from './api/types' -import type { WsConnectionMetadata } from './api/ws' +import type { WebSocket } from 'ws' import type { ExtensionDiagnostic } from './diagnostic' import type { ImportsBreakdownProvider } from './importsBreakdownProvider' import type { InlineConsoleLogManager } from './inlineConsoleLog' +import type { VitestPackage } from './spawn/pkg' +import type { ExtensionWorkerProcess } from './spawn/types' import type { TestTree } from './testTree' import crypto from 'node:crypto' import { createServer } from 'node:http' @@ -11,12 +11,12 @@ import { pathToFileURL } from 'node:url' import getPort from 'get-port' import * as vscode from 'vscode' import { WebSocketServer } from 'ws' -import { VitestFolderAPI } from './api' -import { onWsConnection } from './api/ws' +import { VitestProcessAPI } from './apiProcess' import { getConfig } from './config' import { workerPath } from './constants' import { log } from './log' import { TestRunner } from './runner' +import { onWsConnection } from './spawn/ws' import { getTestData, TestCase, TestFile, TestFolder, TestSuite } from './testTreeData' import { findNode } from './utils' @@ -96,6 +96,7 @@ export async function debugTests( TEST: 'true', VITEST: 'true', NODE_ENV: env.NODE_ENV ?? process.env.NODE_ENV ?? 'test', + FORCE_COLOR: '1', }, } @@ -150,13 +151,15 @@ export async function debugTests( }) try { - const api = new VitestFolderAPI(pkg, { + const api = VitestProcessAPI.forDebug(pkg, { ...metadata, process: new ExtensionDebugProcess( - metadata, + metadata.ws, ), }) + const handle = await api.spawnForRun() const runner = new TestRunner( + handle, controller, tree, api, @@ -167,7 +170,7 @@ export async function debugTests( disposables.push(api, runner) token.onCancellationRequested(async () => { - await metadata.rpc.close() + await metadata.dispose() }) if (browserDebug) { @@ -223,7 +226,7 @@ export async function debugTests( ) } - await runner.runTests(request, token) + await runner.runTests(request) deferredPromise.resolve() } @@ -244,6 +247,7 @@ export async function debugTests( deferredPromise.reject(err) }, + { sendLog: true }, ), ) @@ -301,33 +305,20 @@ async function getRuntimeOptions(pkg: VitestPackage) { } class ExtensionDebugProcess implements ExtensionWorkerProcess { - public id: number = Math.random() public closed = false private _onDidExit = new vscode.EventEmitter() - constructor(private metadata: WsConnectionMetadata) { + constructor(ws: WebSocket) { // if websocket connection stopped working, close the debug session // otherwise it might hang indefinitely - metadata.ws.on('close', () => { + ws.on('close', () => { this.closed = true this._onDidExit.fire() this._onDidExit.dispose() }) } - async close() { - if (this.metadata.rpc.$closed) { - return - } - await this.metadata.rpc.close() - } - - onError() { - // do nothing - return () => {} - } - onExit(listener: (code: number | null) => void) { const { dispose } = this._onDidExit.event(() => { listener(null) diff --git a/packages/extension/src/extension.ts b/packages/extension/src/extension.ts index 237dc87..a171491 100644 --- a/packages/extension/src/extension.ts +++ b/packages/extension/src/extension.ts @@ -1,10 +1,9 @@ import type { VitestAPI } from './api' -import { normalize } from 'pathe' +import type { VitestProcessAPI } from './apiProcess' +import { normalize, relative } from 'pathe' import * as vscode from 'vscode' import { version } from '../../../package.json' import { resolveVitestAPI } from './api' -import { resolveVitestPackages } from './api/pkg' -import { ExtensionTerminalProcess } from './api/terminal' import { copyErrorOutput, copyTestItemErrors } from './commands/copyErrors' import { getConfig, testControllerId } from './config' import { configGlob, workspaceGlob } from './constants' @@ -14,8 +13,11 @@ import { ExtensionDiagnostic } from './diagnostic' import { ImportsBreakdownProvider } from './importsBreakdownProvider' import { InlineConsoleLogManager } from './inlineConsoleLog' import { log } from './log' -import { TestRunner } from './runner' -import { SchemaProvider } from './schemaProvider' +import { RunQueue } from './runQueue' +import { TransformSchemaProvider } from './schemaProvider' +import { resolveVitestPackages } from './spawn/pkg' +import { ExtensionTerminalProcess } from './spawn/terminal' +import { ExtensionState } from './state' import { TagsManager } from './tagsManager' import { TestTree } from './testTree' import { getTestData, TestFile } from './testTreeData' @@ -23,7 +25,7 @@ import { debounce, showVitestError } from './utils' import './polyfills' export async function activate(context: vscode.ExtensionContext) { - const extension = new VitestExtension() + const extension = new VitestExtension(context) context.subscriptions.push(extension) await extension.activate() } @@ -40,21 +42,23 @@ class VitestExtension { private tagsManager: TagsManager private api: VitestAPI | undefined - private runners: TestRunner[] = [] + private runQueues = new Set() + private state: ExtensionState private disposables: vscode.Disposable[] = [] private diagnostic: ExtensionDiagnostic | undefined private debugManager: DebugManager - private schemaProvider: SchemaProvider + private schemaProvider: TransformSchemaProvider private importsBreakdownProvider: ImportsBreakdownProvider private inlineConsoleLog: InlineConsoleLogManager /** @internal */ _debugDisposable: vscode.Disposable | undefined - constructor() { + constructor(context: vscode.ExtensionContext) { log.info(`[v${version}] Vitest extension is activated because Vitest is installed or there is a Vite/Vitest config file in the workspace.`) + this.state = new ExtensionState(context) this.testController = vscode.tests.createTestController(testControllerId, 'Vitest') this.testController.refreshHandler = cancelToken => this.defineTestProfiles(true, cancelToken).catch((err) => { showVitestError('Failed to refresh Vitest', err) @@ -62,9 +66,9 @@ class VitestExtension { this.testController.resolveHandler = item => this.resolveTestFile(item) this.loadingTestItem = this.testController.createTestItem('_resolving', 'Resolving Vitest...') this.loadingTestItem.sortText = '.0' // show it first - this.schemaProvider = new SchemaProvider( + this.schemaProvider = new TransformSchemaProvider( async (apiId, project, environment, file) => { - const api = this.api?.folderAPIs.find(a => a.id === apiId) + const api = this.api?.processes.find(a => a.id === apiId) return api?.getTransformedModule(project, environment, file) ?? null }, ) @@ -100,8 +104,8 @@ class VitestExtension { this.importsBreakdownProvider.clear() this.inlineConsoleLog.clear() this.testTree.reset([]) - this.runners.forEach(runner => runner.dispose()) - this.runners = [] + this.runQueues.forEach(q => q.dispose()) + this.runQueues.clear() const { workspaces, configs } = await resolveVitestPackages(showWarning) @@ -130,36 +134,25 @@ class VitestExtension { return } - this.api = await resolveVitestAPI(workspaces, configs) + for (const [_, profile] of previousRunProfiles) { + profile.dispose() + } - this.api.onUnexpectedExit((code) => { - if (code) { - showVitestError('Vitest process exited unexpectedly') - this.testTree.reset([]) - this.testController.items.delete(this.loadingTestItem.id) - this.api?.dispose() - this.api = undefined - } - else { - log.info('[API] Reloading API due to unexpected empty exit code.') - this.api?.dispose() - this.api = undefined - this.defineTestProfiles(false).catch((err) => { - log.error('[API]', 'Failed to refresh Vitest', err) - }) + this.api = await resolveVitestAPI(workspaces, configs, cancelToken, ({ api: vitest, files }) => { + if (this.state.hasDisabledConfigs()) { + if (this.state.isConfigDisabled(vitest.id)) { + return + } } - }) - for (const api of this.api.folderAPIs) { - const files = await api.getFiles() - await this.testTree.watchTestFilesInWorkspace( - api, - files, - ) - } + this.testTree.watchTestFilesInWorkspace(vitest, files) + this.setupProcessAPI(vitest) - this.testController.items.forEach((item) => { - item.busy = false + this.testController.items.forEach((item) => { + if (item.children.size) { + item.busy = false + } + }) }) } catch (err) { @@ -171,96 +164,6 @@ class VitestExtension { this.testController.items.delete(this.loadingTestItem.id) } - this.api.forEach((api) => { - const runner = new TestRunner( - this.testController, - this.testTree, - api, - this.diagnostic, - this.importsBreakdownProvider, - this.inlineConsoleLog, - ) - this.runners.push(runner) - - const prefix = api.prefix - let runProfile = previousRunProfiles.get(`${api.id}:run`) - if (!runProfile) { - runProfile = this.testController.createRunProfile( - prefix, - vscode.TestRunProfileKind.Run, - () => { - log.error('Run handler is not defined') - }, - false, - undefined, - true, - ) - } - runProfile.tag = api.tag - runProfile.runHandler = (request, token) => runner.runTests(request, token) - this.runProfiles.set(`${api.id}:run`, runProfile) - let debugProfile = previousRunProfiles.get(`${api.id}:debug`) - if (!debugProfile) { - debugProfile = this.testController.createRunProfile( - prefix, - vscode.TestRunProfileKind.Debug, - () => { - log.error('Run handler is not defined') - }, - false, - undefined, - false, // continues debugging is not supported - ) - } - debugProfile.tag = api.tag - debugProfile.runHandler = async (request, token) => { - await this.registerDebugOptions() - - await debugTests( - this.testController, - this.testTree, - api.package, - this.diagnostic, - this.importsBreakdownProvider, - this.inlineConsoleLog, - - request, - token, - this.debugManager, - ).catch((error) => { - vscode.window.showErrorMessage(error.message) - }) - } - this.runProfiles.set(`${api.id}:debug`, debugProfile) - - // coverage is supported since VS Code 1.88 - // @ts-expect-error check for 1.88 - if (vscode.TestRunProfileKind.Coverage && 'FileCoverage' in vscode) { - let coverageProfile = previousRunProfiles.get(`${api.id}:coverage`) - if (!coverageProfile) { - coverageProfile = this.testController.createRunProfile( - prefix, - vscode.TestRunProfileKind.Coverage, - () => { - log.error('Run handler is not defined') - }, - false, - undefined, - true, - ) - } - coverageProfile.tag = api.tag - coverageProfile.runHandler = (request, token) => runner.runCoverage(request, token) - coverageProfile.loadDetailedCoverage = coverageContext.loadDetailedCoverage - this.runProfiles.set(`${api.id}:coverage`, coverageProfile) - } - }) - - for (const [id, profile] of previousRunProfiles) { - if (!this.runProfiles.has(id)) - profile.dispose() - } - // collect tests inside a test file vscode.window.visibleTextEditors.forEach(async (editor) => { const testItems = this.testTree.getFileTestItems(editor.document.uri.fsPath) @@ -277,11 +180,113 @@ class VitestExtension { }) } + private setupProcessAPI(vitest: VitestProcessAPI) { + // Register collection listener so test tree gets notified when tests are collected + vitest.onCollected((file) => { + this.testTree.collectFile(vitest, file) + }) + + const prefix = vitest.prefix + + let runProfile = this.runProfiles.get(`${vitest.id}:run`) + if (!runProfile) { + runProfile = this.testController.createRunProfile( + prefix, + vscode.TestRunProfileKind.Run, + () => { + log.error('Run handler is not defined') + }, + true, + undefined, + true, + ) + } + + const runQueue = new RunQueue( + this.testController, + runProfile, + this.testTree, + vitest, + this.diagnostic, + this.importsBreakdownProvider, + this.inlineConsoleLog, + ) + this.runQueues.add(runQueue) + + runProfile.tag = vitest.tag + runProfile.runHandler = (request, token) => runQueue.enqueue(request, token, false) + this.runProfiles.set(`${vitest.id}:run`, runProfile) + + let debugProfile = this.runProfiles.get(`${vitest.id}:debug`) + if (!debugProfile) { + debugProfile = this.testController.createRunProfile( + prefix, + vscode.TestRunProfileKind.Debug, + () => { + log.error('Run handler is not defined') + }, + true, + undefined, + false, // continues debugging is not supported + ) + } + debugProfile.tag = vitest.tag + debugProfile.runHandler = async (request, token) => { + await this.registerDebugOptions() + + await debugTests( + this.testController, + this.testTree, + vitest.package, + this.diagnostic, + this.importsBreakdownProvider, + this.inlineConsoleLog, + + request, + token, + this.debugManager, + ).catch((error) => { + vscode.window.showErrorMessage(error.message) + }) + } + this.runProfiles.set(`${vitest.id}:debug`, debugProfile) + + let coverageProfile = this.runProfiles.get(`${vitest.id}:coverage`) + if (!coverageProfile) { + coverageProfile = this.testController.createRunProfile( + prefix, + vscode.TestRunProfileKind.Coverage, + () => { + log.error('Run handler is not defined') + }, + true, + undefined, + false, // continues run with coverage is not supported because we want to keep a single running process per API + ) + } + + const coverageQueue = new RunQueue( + this.testController, + coverageProfile, + this.testTree, + vitest, + this.diagnostic, + this.importsBreakdownProvider, + this.inlineConsoleLog, + ) + this.runQueues.add(coverageQueue) + + coverageProfile.tag = vitest.tag + coverageProfile.runHandler = (request, token) => coverageQueue.enqueue(request, token, true) + coverageProfile.loadDetailedCoverage = coverageContext.loadDetailedCoverage + this.runProfiles.set(`${vitest.id}:coverage`, coverageProfile) + } + private async resolveTestFile(item?: vscode.TestItem) { if (!item) return try { - await this.testTree.discoverFileTests(item) + await this.testTree.discoverTestsInFile(item) } catch (err) { showVitestError('There was an error during test discovery', err) @@ -337,22 +342,22 @@ class VitestExtension { } }), vscode.commands.registerCommand('vitest.showShellTerminal', async () => { - const apis = this.api?.folderAPIs - .filter(api => api.process instanceof ExtensionTerminalProcess) + const apis = this.api?.processes + .filter(api => api.getPersistentProcessMeta()?.process instanceof ExtensionTerminalProcess) if (!apis?.length) { - vscode.window.showInformationMessage('No shell terminals found. Did you change `vitest.shellType` to `terminal` in the configuration?') + vscode.window.showInformationMessage('No shell terminals found. Did you change `vitest.shellType` to `terminal` in the configuration? Do you have any continuous runs active?') return } if (apis.length === 1) { log.info('Showing the only available shell terminal'); - (apis[0].process as ExtensionTerminalProcess).show() + (apis[0].getPersistentProcessMeta()?.process as ExtensionTerminalProcess).show() return } const pick = await vscode.window.showQuickPick( apis.map((api) => { return { label: api.prefix, - process: api.process as ExtensionTerminalProcess, + process: api.getPersistentProcessMeta()?.process as ExtensionTerminalProcess, } }), ) @@ -427,6 +432,34 @@ class VitestExtension { }), vscode.commands.registerCommand('vitest.copyTestItemErrors', testItem => copyTestItemErrors(this.testController, testItem)), vscode.commands.registerCommand('vitest.copyErrorOutput', copyErrorOutput), + vscode.commands.registerCommand('vitest.toggleConfigs', async () => { + if (!this.api) { + return + } + + const items: (vscode.QuickPickItem & { key: string })[] = [] + for (const api of this.api.processes) { + items.push({ + label: relative(api.workspaceFolder.uri.fsPath, api.id), + picked: !this.state.isConfigDisabled(api.id), + key: api.id, + }) + } + + const result = await vscode.window.showQuickPick(items, { + canPickMany: true, + title: 'Toggle Vitest Configs', + }) + + if (!result) { + return + } + + const enabledKeys = new Set(result.map(i => i.key)) + await this.state.setDisabledConfigs(new Set(items.filter(i => !enabledKeys.has(i.key)).map(i => i.key))) + + await this.defineTestProfiles(false) + }), ] // if the config changes, re-define all test profiles @@ -448,7 +481,7 @@ class VitestExtension { } // otherwise ignore changes to unrelated configs const filePath = normalize(uri.fsPath) - for (const api of this.api.folderAPIs) { + for (const api of this.api.processes) { if ( api.package.workspaceFile === filePath || api.configs.includes(filePath) @@ -516,7 +549,11 @@ class VitestExtension { this.runProfiles.clear() this.disposables.forEach(d => d.dispose()) this.disposables = [] - this.runners.forEach(runner => runner.dispose()) - this.runners = [] + this.runQueues.forEach(q => q.dispose()) + this.runQueues.clear() } } + +// TODO: add to readme recommended process: +// - press continuous run +// - start editing tests diff --git a/packages/extension/src/log.ts b/packages/extension/src/log.ts index 7ed01d5..93bacae 100644 --- a/packages/extension/src/log.ts +++ b/packages/extension/src/log.ts @@ -103,7 +103,9 @@ function appendFile(log: string) { } export function createErrorLogger(prefix: string) { - return (...args: any[]) => { - log.error(prefix, ...args) + return (error?: Error) => { + if (error) { + log.error(prefix, error) + } } } diff --git a/packages/extension/src/runQueue.ts b/packages/extension/src/runQueue.ts new file mode 100644 index 0000000..a372374 --- /dev/null +++ b/packages/extension/src/runQueue.ts @@ -0,0 +1,236 @@ +import type * as vscode from 'vscode' +import type { RunHandle } from './apiProcess' +import type { ExtensionDiagnostic } from './diagnostic' +import type { ImportsBreakdownProvider } from './importsBreakdownProvider' +import type { InlineConsoleLogManager } from './inlineConsoleLog' +import type { TestTree } from './testTree' +import { VitestProcessAPI } from './apiProcess' +import { log } from './log' +import { ContinuousTestRunner, TestRunner } from './runner' +import { getTestData, TestFile, TestFolder } from './testTreeData' +import { showVitestError } from './utils' + +/** + * Per-process run queue. Ensures only one test run is active at a time per process API. + * Creates a fresh TestRunner + process for each run, like the debug flow does. + */ +export class RunQueue { + private currentRun: Promise | undefined + private pendingQueue: { + runTests: () => Promise + resolveWithoutRunning: () => void + }[] = [] + + private disposed = false + + private continuousHandle: ContinuousHandle | undefined + private continuousPromise: Promise | undefined + private continuousRequests = new Set() + + constructor( + private readonly controller: vscode.TestController, + private readonly testRunProfile: vscode.TestRunProfile, + private readonly tree: TestTree, + private readonly api: VitestProcessAPI, + private readonly diagnostic: ExtensionDiagnostic | undefined, + private readonly importsBreakdown: ImportsBreakdownProvider, + private readonly inlineConsoleLog: InlineConsoleLogManager, + ) {} + + async enqueue(request: vscode.TestRunRequest, token: vscode.CancellationToken, coverage: boolean) { + if (request.continuous) + return this.startContinuousRun(request, token, coverage) + + if (!this.currentRun) { + return this.executeRun(request, token, coverage) + } + + log.verbose?.('Queueing a new test run to execute when the current one is finished.') + return new Promise((resolve) => { + this.pendingQueue.push({ + runTests: () => this.executeRun(request, token, coverage), + resolveWithoutRunning: resolve, + }) + }) + } + + private async executeRun(request: vscode.TestRunRequest, token: vscode.CancellationToken, coverage: boolean) { + this.currentRun = (async () => { + // Each "run" click creates a new process to run tests + // We don't reuse the established process because it's harder to track + const api = new VitestProcessAPI(this.api.config) + // TODO: pass down profile instead of creating a new one, same for coverage/runner - or just disable coverage continuous? + const handle = await api.spawnForRun({ + coverage, + // performance optimization to avoid creating unused projects + projects: getProjectsFromRequest(request), + }) + const runner = this.createRunner(handle, api) + try { + await runner.runTests(request) + } + finally { + runner.dispose() + await handle.dispose() + } + })() + + try { + await this.currentRun + } + catch (err: any) { + if (!err.message?.startsWith('[birpc] rpc is closed')) { + showVitestError('Failed to run tests', err) + } + } + finally { + this.currentRun = undefined + this.drainQueue() + } + } + + private drainQueue() { + if (this.disposed) { + this.pendingQueue.forEach(p => p.resolveWithoutRunning()) + this.pendingQueue.length = 0 + return + } + const next = this.pendingQueue.shift() + if (next) { + log.verbose?.(`Running next tests in the queue`) + next.runTests().then(next.resolveWithoutRunning, next.resolveWithoutRunning) + } + } + + private continuousTimer: NodeJS.Timeout | undefined + + private async startContinuousRun(request: vscode.TestRunRequest, token: vscode.CancellationToken, coverage: boolean) { + this.continuousRequests.add(request) + + token.onCancellationRequested(() => { + this.continuousRequests.delete(request) + log.verbose?.('Continuous request was cancelled') + + if (this.continuousRequests.size) { + clearTimeout(this.continuousTimer) + const handle = this.continuousHandle + handle?.runner.syncWatcher().catch((error) => { + log.error('Failed to update the watcher state', error) + }) + return + } + + if (this.continuousTimer) { + return + } + + this.continuousTimer = setTimeout(() => { + if (!this.continuousRequests.size && this.continuousHandle) { + log.verbose?.('Stopping the continuous process because there are no more requests.') + this.continuousHandle.dispose() + } + this.continuousTimer = undefined + }, 1000) + }) + + const handle = await this.spawnForContinuesRun(coverage) + + // it's possible that request was cancelled before we spawn the process + if (this.continuousRequests.size) { + await handle.runner.syncWatcher() + } + else { + log.verbose?.('Closing the continues process because requests were cancelled.') + await handle.dispose() + } + } + + private async spawnForContinuesRun(coverage: boolean) { + if (this.continuousHandle) { + return this.continuousHandle + } + if (this.continuousPromise) { + return await this.continuousPromise + } + + this.continuousPromise = (async () => { + const handle = await this.api.spawnForRun({ coverage }) + const runner = this.createContinuousRunner(handle) + + const offExit = handle.process.onExit(() => { + // Unexpected exit, make sure we cleanup the state + if (this.continuousHandle) { + showVitestError('The process exited unexpectedly') + runner.dispose() + this.continuousHandle = undefined + } + }) + + this.continuousHandle = { + runner, + dispose: async () => { + offExit() + this.continuousHandle = undefined + runner.dispose() + await handle.dispose() + }, + } + return this.continuousHandle + })().finally(() => (this.continuousPromise = undefined)) + + return this.continuousPromise + } + + private createRunner(handle: RunHandle, api?: VitestProcessAPI) { + return new TestRunner( + handle, + this.controller, + this.tree, + api || this.api, + this.diagnostic, + this.importsBreakdown, + this.inlineConsoleLog, + ) + } + + private createContinuousRunner(handle: RunHandle) { + return new ContinuousTestRunner( + handle, + this.controller, + this.tree, + this.api, + this.diagnostic, + this.importsBreakdown, + this.inlineConsoleLog, + this.testRunProfile, + this.continuousRequests, + ) + } + + dispose() { + this.disposed = true + this.pendingQueue.forEach(p => p.resolveWithoutRunning()) + this.pendingQueue.length = 0 + this.api.cancelRun() + } +} + +interface ContinuousHandle { + runner: ContinuousTestRunner + dispose: () => Promise +} + +function getProjectsFromRequest(request: vscode.TestRunRequest): string[] | undefined { + const include = request.include + if (!include?.length) + return undefined + const projects = new Set() + for (const test of include) { + const data = getTestData(test) + if (data instanceof TestFolder) + return undefined + const project = data instanceof TestFile ? data.project : data.file.project + projects.add(project) + } + return [...projects] +} diff --git a/packages/extension/src/runner.ts b/packages/extension/src/runner.ts index c239d79..daed2b3 100644 --- a/packages/extension/src/runner.ts +++ b/packages/extension/src/runner.ts @@ -1,83 +1,61 @@ import type { ParsedStack, RunnerTaskResult, TestError } from 'vitest' import type { ExtensionTestSpecification } from 'vitest-vscode-shared' -import type { VitestFolderAPI } from './api' +import type { RunHandle, VitestProcessAPI } from './apiProcess' import type { ExtensionDiagnostic } from './diagnostic' import type { ImportsBreakdownProvider } from './importsBreakdownProvider' import type { InlineConsoleLogManager } from './inlineConsoleLog' import type { TestTree } from './testTree' import crypto from 'node:crypto' -import { rm } from 'node:fs/promises' import path from 'node:path' +import { stripVTControlCharacters } from 'node:util' import { getTasks } from '@vitest/runner/utils' import { basename, normalize, relative } from 'pathe' import { normalizeDriveLetter } from 'vitest-vscode-shared' import * as vscode from 'vscode' import { getConfig } from './config' -import { coverageContext, readCoverageReport } from './coverage' +import { coverageContext } from './coverage' import { log } from './log' import { getTestData, TestCase, TestFile, TestFolder } from './testTreeData' import { getErrorMessage, showVitestError } from './utils' export class TestRunner extends vscode.Disposable { - private continuousRequests = new Set() - private nonContinuousRequest: vscode.TestRunRequest | undefined + protected testRun: vscode.TestRun | undefined + // The request tied to the testRun + protected testRunRequest: vscode.TestRunRequest | undefined - private _onRequestsExhausted = new vscode.EventEmitter() - - private testRun: vscode.TestRun | undefined - private testRunDefer: PromiseWithResolvers | undefined - private testRunRequest: vscode.TestRunRequest | undefined - - private disposables: vscode.Disposable[] = [] - - private cancelled = false + protected disposables: vscode.Disposable[] = [] constructor( - private readonly controller: vscode.TestController, - private readonly tree: TestTree, - private readonly api: VitestFolderAPI, - private readonly diagnostic: ExtensionDiagnostic | undefined, - private readonly importsBreakdown: ImportsBreakdownProvider, - private readonly inlineConsoleLog: InlineConsoleLogManager, + protected readonly handle: RunHandle, + protected readonly controller: vscode.TestController, + protected readonly tree: TestTree, + protected readonly api: VitestProcessAPI, + protected readonly diagnostic: ExtensionDiagnostic | undefined, + protected readonly importsBreakdown: ImportsBreakdownProvider, + protected readonly inlineConsoleLog: InlineConsoleLogManager, ) { super(() => { log.verbose?.('Disposing test runner') - api.clearListeners() this.endTestRun() - this.nonContinuousRequest = undefined - this.continuousRequests.clear() - this.api.cancelRun() - this._onRequestsExhausted.dispose() this.disposables.forEach(d => d.dispose()) this.disposables = [] + log.offWorkerLog(this.onWorkerLog) }) - log.onWorkerLog((message) => { - if (this.testRun) { - this.testRun.appendOutput(formatTestOutput(message)) - } - }) + log.onWorkerLog(this.onWorkerLog) - api.onTestRunStart((files, collecting) => { - if (!files.length) { + handle.handlers.onTestRunStart((files) => { + if (!files.length) return - } - if (collecting) { - log.verbose?.('Not starting the runner because tests are being collected for', ...files.map(f => this.relative(f))) - } - else { - files.forEach((file) => { - const uri = vscode.Uri.file(file) - this.diagnostic?.deleteDiagnostic(uri) - }) - this.inlineConsoleLog.clear() - log.verbose?.('Starting a test run because', ...files.map(f => this.relative(f)), 'triggered a watch rerun event') - this.startTestRun(files) - } + files.forEach((file) => { + const uri = vscode.Uri.file(file) + this.diagnostic?.deleteDiagnostic(uri) + }) + this.inlineConsoleLog.clear() }) - api.onTaskUpdate((packs) => { + handle.handlers.onTaskUpdate((packs) => { packs.forEach(([testId, result]) => { const test = this.tree.getTestItemByTaskId(testId) if (!test) { @@ -90,12 +68,11 @@ export class TestRunner extends vscode.Disposable { log.verbose?.(`There is no test run for "${test.label}"`) return } - this.markResult(testRun, test, result) }) }) - api.onCollected((file, collecting) => { + handle.handlers.onCollected((file, collecting) => { this.tree.collectFile(this.api, file) if (collecting) return @@ -109,9 +86,8 @@ export class TestRunner extends vscode.Disposable { return } const testRun = this.testRun - if (!testRun) { + if (!testRun) return - } if (task.mode === 'skip' || task.mode === 'todo') { const include = this.testRunRequest?.include @@ -124,7 +100,7 @@ export class TestRunner extends vscode.Disposable { } } else if (!task.result && task.type !== 'suite') { - log.verbose?.(`Enqueuing "${test.label}" because it was just collected`) + log.verbose?.(`Enqueuing "${test.label}"`) testRun.enqueued(test) } else { @@ -133,26 +109,20 @@ export class TestRunner extends vscode.Disposable { }) }) - api.onTestRunEnd(async (files, unhandledError, collecting) => { + handle.handlers.onTestRunEnd(async (files, unhandledError, collecting, coverage) => { const testRun = this.testRun if (!testRun) { - log.verbose?.('No test run to finish for', files.map(f => this.relative(f.filepath)).join(', ')) - if (!files.length) { - log.verbose?.('No files to finish') - } - if (unhandledError) { + if (unhandledError) log.error(unhandledError) - } + this.endTestRun() return } - try { - if (!collecting) - await this.reportCoverage() - } - catch (err: any) { - showVitestError(`Failed to report coverage. ${err.message}`, err) + if (coverage) { + await this.reportCoverage(coverage).catch((err) => { + showVitestError(`Failed to report coverage. ${err.message}`, err) + }) } if (unhandledError) @@ -162,142 +132,73 @@ export class TestRunner extends vscode.Disposable { this.endTestRun() }) - api.onConsoleLog((cosoleLog) => { - inlineConsoleLog.addConsoleLog(cosoleLog) + handle.handlers.onConsoleLog((consoleLog) => { + this.inlineConsoleLog.addConsoleLog(consoleLog) }) } - protected endTestRun() { - log.verbose?.('Ending test run', this.testRun ? this.testRun.name || '' : '') - this.testRun?.end() - this.testRunDefer?.resolve() - this.testRun = undefined - this.testRunDefer = undefined - this.testRunRequest = undefined - } - - private async watchContinuousTests(request: vscode.TestRunRequest, token: vscode.CancellationToken) { - this.continuousRequests.add(request) - - this.disposables.push( - token.onCancellationRequested(() => { - log.verbose?.('Continuous test run for', labelTestItems(request.include), 'was cancelled') - - this.continuousRequests.delete(request) - if (!this.continuousRequests.size) { - log.verbose?.('Stopped watching test files') - this._onRequestsExhausted.fire() - this.api.unwatchTests() - this.endTestRun() - } - }), - ) - - if (!request.include?.length) { - log.info('[RUNNER]', 'Watching all test files') - await this.api.watchTests() + private onWorkerLog = (message: string) => { + if (this.testRun) { + this.testRun.appendOutput(formatTestOutput(message)) } - else { - const include = [...this.continuousRequests].map(r => r.include || []).flat() - const files = getTestFiles(include) - const testNamePatern = formatTestPattern(include) - log.info( - '[RUNNER]', - 'Watching test files:', - files.map(f => this.relative(f)).join(', '), - testNamePatern ? `with pattern ${testNamePatern}` : '', - ) - await this.api.watchTests(files, testNamePatern) + else if (message) { + // So we don't lose the log. Ideally, we should start runner sooner + log.verbose?.('[WORKER]', stripVTControlCharacters(message)) } } - public async runCoverage(request: vscode.TestRunRequest, token: vscode.CancellationToken) { - try { - await this.api.enableCoverage() - } - catch (err: any) { - showVitestError(`Failed to enable coverage. ${err.message}`, err) - return + protected endTestRun() { + if (this.testRun) { + log.verbose?.('Ending test run', this.testRun.name || '') + this.testRun?.end() + this.testRun = undefined } + this.testRunRequest = undefined + } - const { dispose } = this._onRequestsExhausted.event(() => { - if (!this.continuousRequests.size && !this.nonContinuousRequest) { - log.verbose?.('Coverage was disabled due to all requests being exhausted') - this.api.disableCoverage() - dispose() - } - }) - - this.disposables.push( - token.onCancellationRequested(() => { - log.verbose?.('Coverage for', labelTestItems(request.include), 'was manually stopped') - this.api.disableCoverage() - }), - ) + private triggerCancel(request?: vscode.TestRunRequest) { + const timeout = getConfig(this.api.workspaceFolder).forceCancelTimeout + const timeoutId = setTimeout(() => { + this.api.cancelRun() + log.error(`Triggering a force cancel timeout (${timeout}ms).`) + }, timeout) - const modules = !request.include - ? null - : getTestFiles(request.include).map((f) => { - if (typeof f === 'string') { - return f - } - return f[1] - }) + this.api.cancelRun().then(() => { + clearTimeout(timeoutId) + this.endTestRun() + }) - await this.api.invalidateIstanbulTestModules(modules) - await this.runTests(request, token) + log.verbose?.('Test run was cancelled manually for', labelTestItems(request?.include)) } - public async runTests(request: vscode.TestRunRequest, token: vscode.CancellationToken) { - // if request is continuous, we just mark it and wait for the changes to files - // users can also click on "run" button to trigger the run - if (request.continuous) - return await this.watchContinuousTests(request, token) + public async runTests(request: vscode.TestRunRequest) { + const tests = request.include || [] + const files = getTestFiles(tests) - try { - await this.scheduleTestItems(request, token) - } - catch (err: any) { - // the rpc can be closed during the test run by clicking on reload - if (!err.message.startsWith('[birpc] rpc is closed')) { - log.error('Failed to run tests', err) + const testFiles = files.filter(f => !(typeof f === 'string' ? f : f[1]).endsWith('/')) + const testRunName = testFiles.length === 1 + ? this.relative(testFiles[0]) + : undefined + const run = this.testRun = this.createCancellableTestRun(request, testRunName) + this.testRunRequest = request + + const testItems = request.include || this.controller.items + function enqueue(test: vscode.TestItem) { + const testData = getTestData(test) + // we only change the state of test cases to keep the correct test count + if (testData instanceof TestCase && !testData.dynamic) { + log.verbose?.(`Enqueuing "${test.label}"`) + run.enqueued(test) } - this.endTestRun() + test.children.forEach(enqueue) } - } - - protected scheduleTestRunsQueue: { - runTests: () => Promise - resolveWithoutRunning: () => void - }[] = [] - - private async runTestItems(request: vscode.TestRunRequest, token: vscode.CancellationToken) { - this.cancelled = false - this.nonContinuousRequest = request - - this.disposables.push( - token.onCancellationRequested(() => { - if (request === this.nonContinuousRequest) { - this.cancelled = true - const timeout = setTimeout(() => { - this.api.cancelRun() // cancel the second time for good - }, getConfig(this.api.workspaceFolder).forceCancelTimeout) - this.api.cancelRun().then(() => { - clearTimeout(timeout) - this.nonContinuousRequest = undefined - this.endTestRun() - }) - log.verbose?.('Test run was cancelled manually for', labelTestItems(request.include)) - } - }), - ) + testItems.forEach(test => enqueue(test)) const runTests = (files?: ExtensionTestSpecification[] | string[], testNamePatern?: string) => 'updateSnapshots' in request - ? this.api.updateSnapshots(files, testNamePatern) - : this.api.runFiles(files, testNamePatern) + ? this.handle.rpc.updateSnapshots(files, testNamePatern) + : this.handle.rpc.runTests(files, testNamePatern) - const tests = request.include || [] if (!tests.length) { const root = this.api.workspaceFolder.uri.fsPath log.info(`Running all tests in ${basename(root)}`) @@ -305,36 +206,12 @@ export class TestRunner extends vscode.Disposable { } else { const testNamePatern = formatTestPattern(tests) - const files = getTestFiles(tests) if (testNamePatern) log.info(`Running ${files.length} file(s) with name pattern: ${testNamePatern}`) else log.info(`Running ${files.length} file(s):`, files.map(f => this.relative(f))) await runTests(files, testNamePatern) } - - if (request === this.nonContinuousRequest) { - this.nonContinuousRequest = undefined - this._onRequestsExhausted.fire() - } - } - - protected async scheduleTestItems(request: vscode.TestRunRequest, token: vscode.CancellationToken) { - if (!this.testRunDefer) { - await this.runTestItems(request, token) - } - else { - log.verbose?.('Queueing a new test run to execute when the current one is finished.') - return new Promise((resolve, reject) => { - this.scheduleTestRunsQueue.push({ - runTests: () => { - log.verbose?.('Scheduled test run is starting now.') - return this.runTestItems(request, token).then(resolve, reject) - }, - resolveWithoutRunning: resolve, - }) - }) - } } private isTestIncluded(test: vscode.TestItem, include: readonly vscode.TestItem[] | vscode.TestItemCollection) { @@ -348,159 +225,42 @@ export class TestRunner extends vscode.Disposable { return false } - private isFileIncluded(file: string, include: readonly vscode.TestItem[] | vscode.TestItemCollection) { - for (const _item of include) { - const item = 'id' in _item ? _item : _item[1] - const data = getTestData(item) - if (data instanceof TestFile) { - if (data.filepath === file) - return true - } - else if (data instanceof TestFolder) { - if (this.isFileIncluded(file, item.children)) - return true - } - else { - if (data.file.filepath === file) - return true - } - } - return false - } - - private getTestFilesInFolder(path: string) { - const folder = this.tree.getOrCreateFolderTestItem(this.api, path) - const items = this.tree.getFolderFiles(folder) - return Array.from( - new Set(items.map(item => (getTestData(item) as TestFile).filepath)), - ) - } - - private createContinuousRequest() { - if (!this.continuousRequests.size) - return null - const include = [] - let primaryRequest: vscode.TestRunRequest | null = null - for (const request of this.continuousRequests) { - if (!primaryRequest) - primaryRequest = request - include.push(...request.include || []) - } - return new vscode.TestRunRequest( - include.length ? include : undefined, - undefined, - primaryRequest?.profile, - true, - ) - } - - private async startTestRun(files: string[], primaryRequest?: vscode.TestRunRequest) { - const request = primaryRequest || this.nonContinuousRequest || this.createContinuousRequest() - - if (!files.length) { - log.verbose?.('Started an empty test run. This should not happen...') - return - } - - if (!request) { - log.verbose?.('No test run request found for', ...files.map(f => this.relative(f))) - return - } - - if (this.testRun) { - log.verbose?.('Waiting for the previous test run to finish') - await this.testRunDefer?.promise - } - - const name = files.length > 1 - ? undefined - : this.relative(files[0]) - + protected createCancellableTestRun(request: vscode.TestRunRequest, name?: string) { const run = this.testRun = this.controller.createTestRun(request, name) - this.testRunRequest = request - this.testRunDefer = Promise.withResolvers() - // run the next test when this one finished, or cancell or test runs if they were cancelled - this.testRunDefer.promise = this.testRunDefer.promise.finally(() => { - run.end() - if (this.cancelled) { - log.verbose?.('Not starting a new test run because the previous one was cancelled manually.') - this.scheduleTestRunsQueue.forEach(item => item.resolveWithoutRunning()) - this.scheduleTestRunsQueue.length = 0 - this.cancelled = false - } - else { - log.verbose?.(`Test run promise is finished, the queue is ${this.scheduleTestRunsQueue.length}`) - this.scheduleTestRunsQueue.shift()?.runTests() - } - }) - for (const file of files) { - if (file[file.length - 1] === '/') { - const files = this.getTestFilesInFolder(file) - this.startTestRun(files, request) - continue - } - - // during test collection, we don't have test runs - if (request.include && !this.isFileIncluded(file, request.include)) - continue + run.token.onCancellationRequested(() => { + this.triggerCancel(this.testRunRequest) + }) - const testItems = request.include || this.tree.getFileTestItems(file) - function enqueue(test: vscode.TestItem) { - const testData = getTestData(test) - // we only change the state of test cases to keep the correct test count - if (testData instanceof TestCase && !testData.dynamic) { - log.verbose?.(`Enqueuing "${test.label}"`) - run.enqueued(test) - } - if (testData instanceof TestFile) { - // ignore tests in another files, this is relevant for continuous runs - if (!files.includes(testData.filepath)) { - return - } - } - test.children.forEach(enqueue) - } - testItems.forEach(test => enqueue(test)) - } + return run } - public async reportCoverage() { - if (!('FileCoverage' in vscode)) - return - - const reportsDirectory = await this.api.waitForCoverageReport() + public async reportCoverage(coverage: any) { const testRun = this.testRun - if (!reportsDirectory || !testRun) + if (!testRun) return - const coverage = readCoverageReport(reportsDirectory) // TODO: quick patch, coverage shouldn't report negative columns function ensureLoc(loc: any) { - if (!loc) { + if (!loc) return - } - if (loc.start?.column && loc.start.column < 0) { + if (loc.start?.column && loc.start.column < 0) loc.start.column = 0 - } - if (loc.end?.column && loc.end.column < 0) { + if (loc.end?.column && loc.end.column < 0) loc.end.column = 0 - } } for (const file in coverage) { - for (const key in coverage[file].branchMap) { - const branch = coverage[file].branchMap[key] + coverage[file] = coverage[file].data + + const fileCoverage = coverage[file] + for (const key in fileCoverage.branchMap) { + const branch = fileCoverage.branchMap[key] ensureLoc(branch.loc) branch.locations?.forEach((loc: any) => ensureLoc(loc)) } } - await coverageContext.applyJson(testRun, coverage) - rm(reportsDirectory, { recursive: true, force: true }).then(() => { - log.info('Removed coverage reports', reportsDirectory) - }).catch(() => { - log.error('Failed to remove coverage reports', reportsDirectory) - }) + await coverageContext.applyJson(testRun, coverage) } private markTestCase( @@ -519,9 +279,8 @@ export class TestRunner extends vscode.Disposable { log.verbose?.(`Test failed, but no errors found for "${test.label}"`) return } - if (test.uri) { + if (test.uri) this.diagnostic?.addDiagnostic(test.uri, errors) - } log.verbose?.(`Marking "${test.label}" as failed with ${errors.length} errors`) testRun.failed(test, errors, result.duration) break @@ -585,11 +344,153 @@ export class TestRunner extends vscode.Disposable { this.markTestCase(testRun, test, result) } - private relative(file: string | ExtensionTestSpecification) { + protected relative(file: string | ExtensionTestSpecification) { return relative(this.api.workspaceFolder.uri.fsPath, typeof file === 'string' ? file : file[1]) } } +export class ContinuousTestRunner extends TestRunner { + constructor( + handle: RunHandle, + controller: vscode.TestController, + tree: TestTree, + api: VitestProcessAPI, + diagnostic: ExtensionDiagnostic | undefined, + importsBreakdown: ImportsBreakdownProvider, + inlineConsoleLog: InlineConsoleLogManager, + private readonly testRunProfile: vscode.TestRunProfile, + private readonly continuousRequests: Set, + ) { + super(handle, controller, tree, api, diagnostic, importsBreakdown, inlineConsoleLog) + handle.handlers.onTestRunStart((files) => { + this.startTestRun(files) + log.verbose?.('Starting a test run because', ...files.map(f => this.relative(f)), 'triggered a watch rerun event') + }) + } + + public async syncWatcher() { + if (!this.continuousRequests.size) { + return + } + + const include = [...this.continuousRequests].map(r => r.include || []).flat() + + if (!include.length) { + await this.handle.rpc.watchTests() + log.info('[RUNNER]', 'Watching all test files') + } + else { + const files = getTestFiles(include) + const testNamePatern = formatTestPattern(include) + await this.handle.rpc.watchTests(files, testNamePatern) + log.info( + '[RUNNER]', + 'Watching test files:', + files.map(f => this.relative(f)).join(', '), + testNamePatern ? `with pattern ${testNamePatern}` : '', + ) + } + } + + private async startTestRun(files: string[], request = this.createContinuousRequest()) { + if (this.testRun) { + return + } + + if (!files.length) { + log.verbose?.('Started an empty test run. This should not happen...') + return + } + + if (!request) { + log.verbose?.('No test run request found for', ...files.map(f => this.relative(f))) + return + } + + const name = files.length > 1 + ? undefined + : this.relative(files[0]) + + this.testRunRequest = request + const run = this.createCancellableTestRun(request, name) + + for (const file of files) { + if (file[file.length - 1] === '/') { + const files = this.getTestFilesInFolder(file) + this.startTestRun(files, request) + continue + } + + // during test collection, we don't have test runs + if (request.include && !this.isFileIncluded(file, request.include)) + continue + + const testItems = request.include || this.tree.getFileTestItems(file) + function enqueue(test: vscode.TestItem) { + const testData = getTestData(test) + // we only change the state of test cases to keep the correct test count + if (testData instanceof TestCase && !testData.dynamic && files.includes(testData.file.filepath)) { + log.verbose?.(`Enqueuing "${test.label}"`) + run.enqueued(test) + } + if (testData instanceof TestFile) { + // ignore tests in another files, this is relevant for continuous runs + if (!files.includes(testData.filepath)) { + return + } + } + test.children.forEach(enqueue) + } + testItems.forEach(test => enqueue(test)) + } + } + + private isFileIncluded(file: string, include: readonly vscode.TestItem[] | vscode.TestItemCollection) { + for (const _item of include) { + const item = 'id' in _item ? _item : _item[1] + const data = getTestData(item) + if (data instanceof TestFile) { + if (data.filepath === file) + return true + } + else if (data instanceof TestFolder) { + if (this.isFileIncluded(file, item.children)) + return true + } + else { + if (data.file.filepath === file) + return true + } + } + return false + } + + private getTestFilesInFolder(path: string) { + const folder = this.tree.getOrCreateFolderTestItem(this.api, path) + const items = this.tree.getFolderFiles(folder) + return Array.from( + new Set(items.map(item => (getTestData(item) as TestFile).filepath)), + ) + } + + // It is important to create new requests every time the file is changed, + // Otherwise it becomes stale. + private createContinuousRequest() { + if (!this.continuousRequests.size) + return undefined + const include = [] + for (const request of this.continuousRequests) { + include.push(...request.include || []) + } + return new vscode.TestRunRequest( + include.length ? include : undefined, + undefined, + this.testRunProfile, + true, + ) + } +} + function setTestErrors(test: vscode.TestItem, errors: TestError[] | undefined) { const data = getTestData(test) if (data instanceof TestCase) { @@ -690,29 +591,29 @@ function getTestFiles(tests: readonly vscode.TestItem[]): string[] | ExtensionTe const testSpecs: ExtensionTestSpecification[] = [] const testFiles = new Set() for (const test of tests) { - const fsPath = test.uri!.fsPath + const fsPath = normalize(test.uri!.fsPath) const data = getTestData(test) // just to type guard, actually not possible to have - if (data instanceof TestFolder) { + if (data instanceof TestFolder) continue - } const project = data instanceof TestFile ? data.project : data.file.project const key = `${project}\0${fsPath}` - if (testFiles.has(key)) { + if (testFiles.has(key)) continue - } testFiles.add(key) testSpecs.push([project, fsPath]) } return testSpecs } -function formatTestPattern(tests: readonly vscode.TestItem[]) { - const patterns: string[] = [] +function formatTestPattern(tests: readonly vscode.TestItem[], patterns: string[] = []) { for (const test of tests) { const data = getTestData(test)! - if (!('getTestNamePattern' in data)) + // file or a folder, try to include every test in there + if (!('getTestNamePattern' in data)) { + formatTestPattern([...test.children].map(t => t[1]), patterns) continue + } patterns.push(data.getTestNamePattern()) } if (!patterns.length) diff --git a/packages/extension/src/schemaProvider.ts b/packages/extension/src/schemaProvider.ts index 2948f0c..e151f2b 100644 --- a/packages/extension/src/schemaProvider.ts +++ b/packages/extension/src/schemaProvider.ts @@ -1,6 +1,6 @@ import * as vscode from 'vscode' -export class SchemaProvider implements vscode.TextDocumentContentProvider, vscode.Disposable { +export class TransformSchemaProvider implements vscode.TextDocumentContentProvider, vscode.Disposable { private disposables: vscode.Disposable[] = [] private _onDidChangeEvents = new vscode.EventEmitter() diff --git a/packages/extension/src/api/child_process.ts b/packages/extension/src/spawn/child_process.ts similarity index 75% rename from packages/extension/src/api/child_process.ts rename to packages/extension/src/spawn/child_process.ts index 5cc2a8c..2f31910 100644 --- a/packages/extension/src/api/child_process.ts +++ b/packages/extension/src/spawn/child_process.ts @@ -1,9 +1,10 @@ import type { ChildProcessWithoutNullStreams } from 'node:child_process' import type { Server } from 'node:http' import type WebSocket from 'ws' -import type { ResolvedMeta } from '../api' +import type { ResolvedMeta } from '../apiProcess' import type { VitestPackage } from './pkg' import type { ExtensionWorkerProcess } from './types' +import type { ProcessSpawnOptions } from './ws' import { spawn } from 'node:child_process' import { createServer } from 'node:http' import { pathToFileURL } from 'node:url' @@ -15,7 +16,7 @@ import { createErrorLogger, log } from '../log' import { findNode, formatPkg, showVitestError } from '../utils' import { waitForWsConnection } from './ws' -export async function createVitestProcess(pkg: VitestPackage) { +export async function createVitestProcess(pkg: VitestPackage, options?: ProcessSpawnOptions) { const pnpLoader = pkg.loader const pnp = pkg.pnp if (pnpLoader && !pnp) @@ -83,11 +84,12 @@ export async function createVitestProcess(pkg: VitestPackage) { vitest.on('exit', onExit) vitest.on('error', onError) - waitForWsConnection(wss, pkg, 'child_process') - .then((resolved) => { + waitForWsConnection(wss, pkg, 'child_process', options) + .then((meta) => { + const process = new ExtensionChildProcess(vitest, server, meta.ws) resolve({ - ...resolved, - process: new ExtensionChildProcess(vitest, server, resolved.ws), + ...meta, + process, }) }, reject) .finally(() => { @@ -98,22 +100,13 @@ export async function createVitestProcess(pkg: VitestPackage) { } class ExtensionChildProcess implements ExtensionWorkerProcess { - public id: number - private stopped: Promise - constructor( private child: ChildProcessWithoutNullStreams, server: Server, ws: WebSocket, ) { - // the execution process cannot be created without a pid - this.id = child.pid! - this.stopped = new Promise((resolve, reject) => { - child.on('exit', () => { - server.close(createErrorLogger('Failed to close server')) - resolve() - }) - child.on('error', reject) + child.on('exit', () => { + server.close(createErrorLogger('Failed to close server')) }) // stop the process if websocket connection was somehow closed ws.on('close', () => { @@ -124,27 +117,7 @@ class ExtensionChildProcess implements ExtensionWorkerProcess { } get closed(): boolean { - return this.child.killed - } - - close() { - this.child.kill() - return new Promise((resolve, reject) => { - const timer = setTimeout(() => { - reject(new Error('The extension child process did not exit in time.')) - }, 5_000) - this.stopped - .finally(() => clearTimeout(timer)) - .then(resolve, reject) - }) - } - - onError(listener: (error: Error) => void, options?: { once?: boolean }) { - const method = options?.once ? 'once' : 'on' - this.child[method]('error', listener) - return () => { - this.child.off('error', listener) - } + return this.child.exitCode != null } onExit(listener: (code: number | null) => void, options?: { once?: boolean }) { diff --git a/packages/extension/src/api/pkg.ts b/packages/extension/src/spawn/pkg.ts similarity index 100% rename from packages/extension/src/api/pkg.ts rename to packages/extension/src/spawn/pkg.ts diff --git a/packages/extension/src/api/resolve.ts b/packages/extension/src/spawn/resolve.ts similarity index 100% rename from packages/extension/src/api/resolve.ts rename to packages/extension/src/spawn/resolve.ts diff --git a/packages/extension/src/api/rpc.ts b/packages/extension/src/spawn/rpc.ts similarity index 100% rename from packages/extension/src/api/rpc.ts rename to packages/extension/src/spawn/rpc.ts diff --git a/packages/extension/src/api/terminal.ts b/packages/extension/src/spawn/terminal.ts similarity index 61% rename from packages/extension/src/api/terminal.ts rename to packages/extension/src/spawn/terminal.ts index c1223d7..54cad69 100644 --- a/packages/extension/src/api/terminal.ts +++ b/packages/extension/src/spawn/terminal.ts @@ -1,9 +1,9 @@ import type { Server } from 'node:http' import type { WebSocket } from 'ws' -import type { ResolvedMeta } from '../api' +import type { ResolvedMeta } from '../apiProcess' import type { VitestPackage } from './pkg' import type { ExtensionWorkerProcess } from './types' -import type { WsConnectionMetadata } from './ws' +import type { ProcessSpawnOptions, WsConnectionMetadata } from './ws' import { createServer } from 'node:http' import { pathToFileURL } from 'node:url' import getPort from 'get-port' @@ -15,7 +15,7 @@ import { createErrorLogger, log } from '../log' import { formatPkg } from '../utils' import { waitForWsConnection } from './ws' -export async function createVitestTerminalProcess(pkg: VitestPackage): Promise { +export async function createVitestTerminalProcess(pkg: VitestPackage, options?: ProcessSpawnOptions): Promise { const pnpLoader = pkg.loader const pnp = pkg.pnp if (pnpLoader && !pnp) @@ -47,21 +47,6 @@ export async function createVitestTerminalProcess(pkg: VitestPackage): Promise((resolve) => { - const disposable = vscode.window.onDidChangeTerminalShellIntegration((e) => { - const timeout = setTimeout(() => { - disposable.dispose() - resolve(undefined) - }, 3_000) - - if (e.terminal === terminal) { - disposable.dispose() - clearTimeout(timeout) - resolve(e.shellIntegration) - } - }) - }) - const processId = await terminal.processId if (terminal.exitStatus && terminal.exitStatus.code != null) { throw new Error(`Terminal was ${getExitReason(terminal.exitStatus.reason)} with code ${terminal.exitStatus.code}`) @@ -76,46 +61,17 @@ export async function createVitestTerminalProcess(pkg: VitestPackage): Promise { - if (e.execution !== execution) { - return - } - - // TODO: terminal output is received _after_ the test run - // This is probably easier to fix when rewriting - log.info('[TERMINAL] Reporting the shell output.') - for await (const line of e.execution.read()) { - log.worker('info', line) - } - onWriteShell.dispose() - }) - - const onEndShell = vscode.window.onDidEndTerminalShellExecution((e) => { - if (e.execution === execution) { - log.info('[TERMINAL] The shell execution was finished.') - onWriteShell.dispose() - onEndShell.dispose() - } - }) - execution = shellIntegration.executeCommand(command) - } - else { - log.info('[TERMINAL] Shell integration is not initiated, fallback to `terminal.sendText`.') - terminal.sendText(command, true) - } + terminal.sendText(command, true) const meta = await new Promise((resolve, reject) => { const timeout = setTimeout(() => { terminal.show(false) - reject(new Error(`The extension could not connect to the terminal in 5 seconds. See the "vitest" terminal output for more details.`)) - }, 5_000) + reject(new Error(`The extension could not connect to the terminal in 30 seconds. See the "vitest" terminal output for more details.`)) + }, 30_000) wss.once('connection', () => { clearTimeout(timeout) }) - waitForWsConnection(wss, pkg, 'terminal').then(resolve, reject) + waitForWsConnection(wss, pkg, 'terminal', { ...options, sendLog: true }).then(resolve, reject) }) meta.handlers.onProcessLog((type, message) => { @@ -124,7 +80,6 @@ export async function createVitestTerminalProcess(pkg: VitestPackage): Promise() - private stopped: Promise - constructor( - public readonly id: number, private readonly terminal: vscode.Terminal, server: Server, ws: WebSocket, ) { - this.stopped = new Promise((resolve) => { - const disposer = vscode.window.onDidCloseTerminal(async (e) => { - if (e === terminal) { - const exitCode = e.exitStatus?.code - this._onDidExit.fire(exitCode ?? null) - this._onDidExit.dispose() - server.close(createErrorLogger('Failed to close server')) - disposer.dispose() - resolve() - } - }) + const disposer = vscode.window.onDidCloseTerminal(async (e) => { + if (e === terminal) { + const exitCode = e.exitStatus?.code + this._onDidExit.fire(exitCode ?? null) + this._onDidExit.dispose() + server.close(createErrorLogger('Failed to close server')) + disposer.dispose() + } }) ws.on('close', () => { this.close() @@ -191,29 +141,14 @@ export class ExtensionTerminalProcess implements ExtensionWorkerProcess { return this.terminal.exitStatus !== undefined } - close() { + private close() { if (this.closed) { - return Promise.resolve() + return } // send ctrl+c to sigint any running processs (vscode/#108289) this.terminal.sendText('\x03') // and then destroy it on the next event loop tick setTimeout(() => this.terminal.dispose(), 1) - return new Promise((resolve, reject) => { - const timer = setTimeout(() => { - reject(new Error('The extension terminal process did not exit in time.')) - }, 5_000) - this.stopped - .finally(() => clearTimeout(timer)) - .then(resolve, reject) - }) - } - - onError() { - // do nothing - return () => { - // do nothing - } } onExit(listener: (code: number | null) => void) { diff --git a/packages/extension/src/api/types.ts b/packages/extension/src/spawn/types.ts similarity index 55% rename from packages/extension/src/api/types.ts rename to packages/extension/src/spawn/types.ts index f56aba7..00b5882 100644 --- a/packages/extension/src/api/types.ts +++ b/packages/extension/src/spawn/types.ts @@ -1,7 +1,4 @@ export interface ExtensionWorkerProcess { - id: number closed: boolean - close: () => Promise - onError: (listener: (error: Error) => void) => () => void onExit: (listener: (code: number | null) => void) => () => void } diff --git a/packages/extension/src/api/ws.ts b/packages/extension/src/spawn/ws.ts similarity index 79% rename from packages/extension/src/api/ws.ts rename to packages/extension/src/spawn/ws.ts index 7fd83a6..c2cee79 100644 --- a/packages/extension/src/api/ws.ts +++ b/packages/extension/src/spawn/ws.ts @@ -1,12 +1,12 @@ import type { WorkerEvent, WorkerRunnerDebugOptions, WorkerRunnerOptions } from 'vitest-vscode-shared' import type { WebSocket, WebSocketServer } from 'ws' -import type { ResolvedMeta } from '../api' +import type { ResolvedMeta } from '../apiProcess' import type { VitestPackage } from './pkg' import { pathToFileURL } from 'node:url' import { gte } from 'semver' import vscode from 'vscode' import { getConfig } from '../config' -import { browserSetupFilePath, finalCoverageFileName } from '../constants' +import { browserSetupFilePath, browserSetupFilePathLegacy, finalCoverageFileName } from '../constants' import { log } from '../log' import { createVitestRpc } from './rpc' @@ -14,10 +14,17 @@ export type WsConnectionMetadata = Omit & { ws: WebSocket } +export interface ProcessSpawnOptions { + coverage?: boolean + sendLog?: boolean + projects?: string[] +} + export function waitForWsConnection( wss: WebSocketServer, pkg: VitestPackage, shellType: 'terminal' | 'child_process', + options?: ProcessSpawnOptions, ) { return new Promise((resolve, reject) => { wss.once('connection', (ws) => { @@ -28,6 +35,7 @@ export function waitForWsConnection( shellType, meta => resolve(meta), err => reject(err), + options, ) wss.off('error', onUnexpectedError) @@ -54,6 +62,7 @@ export function onWsConnection( shellType: 'terminal' | 'child_process', onStart: (meta: WsConnectionMetadata) => unknown, onFail: (err: Error) => unknown, + options?: ProcessSpawnOptions, ) { function onMessage(_message: any) { const message = JSON.parse(_message.toString()) as WorkerEvent @@ -84,6 +93,17 @@ export function onWsConnection( projects: message.projects, ws, pkg, + async dispose() { + if (!api.$closed) { + // Closing the process will also automatically close the WS server + // This is done in the server itself to catch unexpected close events too + await api.exit().catch((error) => { + if (!error.message.startsWith('[birpc] rpc is closed')) { + log.error('Failed to close the process', error) + } + }) + } + }, }) } @@ -132,10 +152,14 @@ export function onWsConnection( : undefined, setupFilePaths: { browserDebug: browserSetupFilePath, + browserDebugLegacy: browserSetupFilePathLegacy, }, finalCoverageFileName, + projectFilter: options?.projects, }, debug, + coverage: options?.coverage, + sendLog: options?.sendLog, } ws.send(JSON.stringify(runnerOptions)) diff --git a/packages/extension/src/state.ts b/packages/extension/src/state.ts new file mode 100644 index 0000000..dc348d7 --- /dev/null +++ b/packages/extension/src/state.ts @@ -0,0 +1,23 @@ +import type * as vscode from 'vscode' + +const DISABLED_CONFIGS_KEY = 'vitest.disabledConfigs' + +export class ExtensionState { + private state: vscode.Memento + + constructor(context: vscode.ExtensionContext) { + this.state = context.workspaceState + } + + isConfigDisabled(id: string): boolean { + return this.state.get(DISABLED_CONFIGS_KEY, []).includes(id) + } + + hasDisabledConfigs(): boolean { + return this.state.get(DISABLED_CONFIGS_KEY, []).length > 0 + } + + setDisabledConfigs(ids: Set): Thenable { + return this.state.update(DISABLED_CONFIGS_KEY, [...ids]) + } +} diff --git a/packages/extension/src/testTree.ts b/packages/extension/src/testTree.ts index 850de49..ace97ad 100644 --- a/packages/extension/src/testTree.ts +++ b/packages/extension/src/testTree.ts @@ -1,7 +1,7 @@ import type { RunnerTask, RunnerTestFile } from 'vitest' import type { ExtensionTestFileSpecification } from 'vitest-vscode-shared' -import type { VitestFolderAPI } from './api' -import type { SchemaProvider } from './schemaProvider' +import type { VitestProcessAPI } from './apiProcess' +import type { TransformSchemaProvider } from './schemaProvider' import type { TagsManager } from './tagsManager' import type { TestFileMetadata } from './testTreeData' import { realpathSync } from 'node:fs' @@ -25,6 +25,7 @@ export class TestTree extends vscode.Disposable { // file test items have the project name in their id, so we need a separate map // to store all of them private testItemsByFile = new Map() + // this is used by the "when" clause in commands private testFiles = new Set() private watcher: ExtensionWatcher @@ -33,7 +34,7 @@ export class TestTree extends vscode.Disposable { private readonly controller: vscode.TestController, private readonly loaderItem: vscode.TestItem, private readonly tagsManager: TagsManager, - schemaProvider: SchemaProvider, + transformSchemaProvider: TransformSchemaProvider, ) { super(() => { this.folderItems.clear() @@ -42,7 +43,7 @@ export class TestTree extends vscode.Disposable { this.testItemsByFile.clear() this.watcher.dispose() }) - this.watcher = new ExtensionWatcher(this, schemaProvider) + this.watcher = new ExtensionWatcher(this, transformSchemaProvider) } public getFileTestItems(fsPath: string) { @@ -77,7 +78,7 @@ export class TestTree extends vscode.Disposable { } } - async discoverAllTestFiles(api: VitestFolderAPI, files: ExtensionTestFileSpecification[]) { + discoverAllTestFiles(api: VitestProcessAPI, files: ExtensionTestFileSpecification[]) { const folderItem = this.folderItems.get(normalize(api.workspaceFolder.uri.fsPath)) if (folderItem) folderItem.busy = false @@ -145,7 +146,7 @@ export class TestTree extends vscode.Disposable { return folderItem } - getOrCreateFileTestItem(api: VitestFolderAPI, metadata: TestFileMetadata, file: string) { + getOrCreateFileTestItem(api: VitestProcessAPI, metadata: TestFileMetadata, file: string) { const project = metadata.project const normalizedFile = normalize(file) const fileId = `${normalizedFile}${project}` @@ -188,7 +189,7 @@ export class TestTree extends vscode.Disposable { return testFileItem } - getOrCreateFolderTestItem(api: VitestFolderAPI, normalizedFolder: string) { + getOrCreateFolderTestItem(api: VitestProcessAPI, normalizedFolder: string) { const cached = this.folderItems.get(normalizedFolder) if (cached) { if (!cached.tags.includes(api.tag)) @@ -223,8 +224,8 @@ export class TestTree extends vscode.Disposable { return folderItem } - async watchTestFilesInWorkspace(api: VitestFolderAPI, testFiles: ExtensionTestFileSpecification[]) { - await this.discoverAllTestFiles(api, testFiles) + watchTestFilesInWorkspace(api: VitestProcessAPI, testFiles: ExtensionTestFileSpecification[]) { + this.discoverAllTestFiles(api, testFiles) this.watcher.watchTestFilesInWorkspace(api) } @@ -255,7 +256,7 @@ export class TestTree extends vscode.Disposable { return getAPIFromTestItem(testItem) } - async discoverFileTests(testItem: vscode.TestItem) { + async discoverTestsInFile(testItem: vscode.TestItem) { const data = getTestData(testItem) if (!(data instanceof TestFile)) return @@ -304,7 +305,7 @@ export class TestTree extends vscode.Disposable { return files } - collectFile(api: VitestFolderAPI, file: RunnerTestFile) { + collectFile(api: VitestProcessAPI, file: RunnerTestFile) { const normalizedFile = normalize(file.filepath) const fileId = `${normalizedFile}${file.projectName || ''}` const fileTestItem = this.fileItems.get(fileId) @@ -472,7 +473,7 @@ function isTest(task: RunnerTask) { return true } -function getAPIFromFolder(folder: vscode.TestItem): VitestFolderAPI | null { +function getAPIFromFolder(folder: vscode.TestItem): VitestProcessAPI | null { const data = getTestData(folder) if (data instanceof TestFile) return data.api @@ -486,7 +487,7 @@ function getAPIFromFolder(folder: vscode.TestItem): VitestFolderAPI | null { return null } -function getAPIFromTestItem(testItem: vscode.TestItem): VitestFolderAPI | null { +function getAPIFromTestItem(testItem: vscode.TestItem): VitestProcessAPI | null { const data = getTestData(testItem) // API is stored in test files - if this is a folder, try to find a file inside, // otherwise go up until we find a file, this should never be a folder diff --git a/packages/extension/src/testTreeData.ts b/packages/extension/src/testTreeData.ts index bb1bf8b..d5f7013 100644 --- a/packages/extension/src/testTreeData.ts +++ b/packages/extension/src/testTreeData.ts @@ -1,6 +1,6 @@ import type { TestError } from 'vitest' import type * as vscode from 'vscode' -import type { VitestFolderAPI } from './api' +import type { VitestProcessAPI } from './apiProcess' export type TestData = TestFolder | TestFile | TestCase | TestSuite @@ -66,7 +66,7 @@ export class TestFile extends BaseTestData { item: vscode.TestItem, parent: vscode.TestItem, public readonly filepath: string, - public readonly api: VitestFolderAPI, + public readonly api: VitestProcessAPI, public readonly metadata: TestFileMetadata, ) { super(item, parent) @@ -77,7 +77,7 @@ export class TestFile extends BaseTestData { item: vscode.TestItem, parent: vscode.TestItem, filepath: string, - api: VitestFolderAPI, + api: VitestProcessAPI, metadata: TestFileMetadata, ) { return addTestData(item, new TestFile(item, parent, filepath, api, metadata)) diff --git a/packages/extension/src/utils.ts b/packages/extension/src/utils.ts index 7063ad8..d96fc01 100644 --- a/packages/extension/src/utils.ts +++ b/packages/extension/src/utils.ts @@ -1,5 +1,5 @@ import type { TestError } from 'vitest' -import type { VitestPackage } from './api/pkg' +import type { VitestPackage } from './spawn/pkg' import { spawn } from 'node:child_process' import fs from 'node:fs' import { inspect, stripVTControlCharacters } from 'node:util' diff --git a/packages/extension/src/watcher.ts b/packages/extension/src/watcher.ts index 2acc452..779b9d9 100644 --- a/packages/extension/src/watcher.ts +++ b/packages/extension/src/watcher.ts @@ -1,5 +1,5 @@ -import type { VitestFolderAPI } from './api' -import type { SchemaProvider } from './schemaProvider' +import type { VitestProcessAPI } from './apiProcess' +import type { TransformSchemaProvider } from './schemaProvider' import type { TestTree } from './testTree' import { relative } from 'node:path' import { normalize } from 'pathe' @@ -9,11 +9,11 @@ import { log } from './log' export class ExtensionWatcher extends vscode.Disposable { private watcherByFolder = new Map() - private apisByFolder = new WeakMap() + private apisByFolder = new WeakMap() constructor( private readonly testTree: TestTree, - private readonly schemaProvider: SchemaProvider, + private readonly transformSchemaProvider: TransformSchemaProvider, ) { super(() => { this.reset() @@ -24,9 +24,10 @@ export class ExtensionWatcher extends vscode.Disposable { reset() { this.watcherByFolder.forEach(x => x.dispose()) this.watcherByFolder.clear() + this.apisByFolder = new WeakMap() } - watchTestFilesInWorkspace(api: VitestFolderAPI) { + watchTestFilesInWorkspace(api: VitestProcessAPI) { const folder = api.workspaceFolder const apis = this.apisByFolder.get(folder) ?? [] if (!apis.includes(api)) { @@ -43,21 +44,34 @@ export class ExtensionWatcher extends vscode.Disposable { const watcher = vscode.workspace.createFileSystemWatcher(pattern) this.watcherByFolder.set(folder, watcher) - watcher.onDidDelete((uri) => { + watcher.onDidDelete(async (uri) => { + const path = normalize(uri.fsPath) + if (await this.shouldIgnoreFile(api, path, uri)) { + return + } log.verbose?.('[VSCODE] File deleted:', this.relative(api, uri)) this.testTree.removeFile(normalize(uri.fsPath)) - this.schemaProvider.emitChange(uri) + this.transformSchemaProvider.emitChange(uri) }) watcher.onDidChange(async (uri) => { - this.schemaProvider.emitChange(uri) const path = normalize(uri.fsPath) if (await this.shouldIgnoreFile(api, path, uri)) { return } + this.transformSchemaProvider.emitChange(uri) log.verbose?.('[VSCODE] File changed:', this.relative(api, uri)) const apis = this.apisByFolder.get(folder) || [] apis.forEach(api => api.onFileChanged(path)) + apis.forEach((api) => { + if (api.getPersistentProcessMeta() || api.isSpawningPersistentProcess) { + return + } + const metadata = api.getPotentialTestFileMetadata(path) + metadata.forEach((meta) => { + api.collectTests(meta.project, path) + }) + }) }) watcher.onDidCreate(async (uri) => { @@ -71,23 +85,26 @@ export class ExtensionWatcher extends vscode.Disposable { const metadata = api.getPotentialTestFileMetadata(path) metadata.forEach((meta) => { this.testTree.getOrCreateFileTestItem(api, meta, path) + if (!api.getPersistentProcessMeta() && !api.isSpawningPersistentProcess) { + api.collectTests(meta.project, path) + } }) - api.onFileCreated(path) }) }) } - private relative(api: VitestFolderAPI, uri: vscode.Uri) { + private relative(api: VitestProcessAPI, uri: vscode.Uri) { return relative(api.workspaceFolder.uri.fsPath, uri.fsPath) } - private async shouldIgnoreFile(api: VitestFolderAPI, path: string, uri: vscode.Uri) { + private async shouldIgnoreFile(api: VitestProcessAPI, path: string, uri: vscode.Uri) { if ( path.includes('/node_modules/') + || path.includes('\\node_modules\\') || path.includes('/.git/') + || path.includes('\\.git\\') || path.endsWith('.git') ) { - log.verbose?.('[VSCODE] Ignoring file:', this.relative(api, uri)) return true } try { diff --git a/packages/extension/src/worker/browserSetupFile.ts b/packages/extension/src/worker/browserSetupFile.ts index db904fb..61c3f3a 100644 --- a/packages/extension/src/worker/browserSetupFile.ts +++ b/packages/extension/src/worker/browserSetupFile.ts @@ -1,4 +1,4 @@ -import { commands, server } from '@vitest/browser/context' +import { commands, server } from 'vitest/browser' if (server.config.inspector.enabled) { // @ts-expect-error __vscode_waitForDebugger is not defined diff --git a/packages/extension/src/worker/browserSetupFileLegacy.ts b/packages/extension/src/worker/browserSetupFileLegacy.ts new file mode 100644 index 0000000..db904fb --- /dev/null +++ b/packages/extension/src/worker/browserSetupFileLegacy.ts @@ -0,0 +1,7 @@ +import { commands, server } from '@vitest/browser/context' + +if (server.config.inspector.enabled) { + // @ts-expect-error __vscode_waitForDebugger is not defined + // eslint-disable-next-line antfu/no-top-level-await + await commands.__vscode_waitForDebugger() +} diff --git a/packages/extension/src/worker/index.ts b/packages/extension/src/worker/index.ts index eda037c..51f79bf 100644 --- a/packages/extension/src/worker/index.ts +++ b/packages/extension/src/worker/index.ts @@ -11,6 +11,8 @@ const emitter = new WorkerWSEventEmitter( new WebSocket(process.env.VITEST_WS_ADDRESS!), ) +process.title = 'vitest-vscode' + if (process.platform === 'win32') { const cwd = process.cwd() const correctCwd = cwd.slice(0, 1).toUpperCase() + cwd.slice(1) @@ -62,6 +64,8 @@ emitter.on('message', async function onMessage(message: any) { worker.initRpc(rpc) reporter.initRpc(rpc) emitter.ready(projects, workspaceSource, isLegacy) + + await worker.vitest.report('onInit', worker.vitest) } catch (err: any) { emitter.error(err) diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 430f50e..dddaa87 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -60,15 +60,10 @@ export interface ExtensionWorkerTransport { runTests: (filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string) => Promise updateSnapshots: (filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string) => Promise - watchTests: (filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string) => void - unwatchTests: () => void + watchTests: (filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string) => Promise getSourceModuleDiagnostic: (moduleId: string) => Promise - invalidateIstanbulTestModules: (modules: string[] | null) => Promise - enableCoverage: () => void - disableCoverage: () => void - waitForCoverageReport: () => Promise - close: () => void + exit: () => void onFilesCreated: (files: string[]) => void onFilesChanged: (files: string[]) => void @@ -83,9 +78,9 @@ export interface ExtensionWorkerTransport { export interface ExtensionWorkerEvents { onConsoleLog: (log: ExtensionUserConsoleLog) => void onTaskUpdate: (task: RunnerTaskResultPack[]) => void - onTestRunEnd: (files: RunnerTestFile[], unhandledError: string, collecting?: boolean) => void + onTestRunEnd: (files: RunnerTestFile[], unhandledError: string, collecting?: boolean, coverage?: unknown) => void onCollected: (file: RunnerTestFile, collecting?: boolean) => void - onTestRunStart: (files: string[], collecting?: boolean) => void + onTestRunStart: (files: string[]) => void onProcessLog: (type: 'stdout' | 'stderr', log: string) => void } @@ -144,8 +139,10 @@ export interface WorkerInitMetadata { pnpLoader?: string setupFilePaths: { browserDebug: string + browserDebugLegacy: string } finalCoverageFileName: string + projectFilter?: string[] } export interface WorkerRunnerDebugOptions { @@ -157,7 +154,9 @@ export interface WorkerRunnerDebugOptions { export interface WorkerRunnerOptions { type: 'init' meta: WorkerInitMetadata + sendLog?: boolean debug?: WorkerRunnerDebugOptions | boolean + coverage?: boolean } export interface SerializedProject { diff --git a/packages/shared/src/utils.ts b/packages/shared/src/utils.ts index 0c655f7..d06573a 100644 --- a/packages/shared/src/utils.ts +++ b/packages/shared/src/utils.ts @@ -82,24 +82,39 @@ export function normalizeDriveLetter(path: string) { return path[0].toUpperCase() + path.slice(1) } -export function createQueuedHandler(resolver: (value: T[]) => Promise) { +export function createQueuedHandler(resolver: (value: T[]) => Promise, timeout = 50) { const cached = new Set() let promise: Promise | null = null - let timer: NodeJS.Timeout | null = null - return (value: T) => { + let timer: ReturnType | null = null + let pendingResolvers: Array<() => void> = [] + + function flush() { + if (promise) + return + const values = Array.from(cached) + cached.clear() + const resolvers = pendingResolvers + pendingResolvers = [] + promise = resolver(values).finally(() => { + promise = null + resolvers.forEach(fn => fn()) + // If more items were queued while resolving, flush them + if (cached.size) + flush() + }) + } + + return (value: T): Promise => { cached.add(value) if (timer) { clearTimeout(timer) } timer = setTimeout(() => { - if (promise) { - return - } - const values = Array.from(cached) - cached.clear() - promise = resolver(values).finally(() => { - promise = null - }) - }, 50) + timer = null + flush() + }, timeout) + return new Promise((resolve) => { + pendingResolvers.push(resolve) + }) } } diff --git a/packages/worker-legacy/src/coverage.ts b/packages/worker-legacy/src/coverage.ts deleted file mode 100644 index 8a6be75..0000000 --- a/packages/worker-legacy/src/coverage.ts +++ /dev/null @@ -1,109 +0,0 @@ -import type { CoverageProvider, ResolvedCoverageOptions } from 'vitest/node' -import type { ExtensionWorker } from './worker' -import { randomUUID } from 'node:crypto' -import { existsSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'pathe' - -export class ExtensionCoverageManager { - private _enabled = false - private _provider: CoverageProvider | null | undefined = undefined - - private _config: ResolvedCoverageOptions - - constructor( - private worker: ExtensionWorker, - private finalCoverageFileName: string, - ) { - this._config = worker.vitest.config.coverage - const projects = new Set([...worker.vitest.projects, worker.getRootTestProject()]) - projects.forEach((project) => { - Object.defineProperty(project.config, 'coverage', { - get: () => { - return this.config - }, - set: (coverage: ResolvedCoverageOptions) => { - this._config = coverage - }, - }) - }) - - Object.defineProperty(worker.vitest, 'coverageProvider', { - get: () => { - if (this.enabled) - return this._provider - - return null - }, - set: (provider: CoverageProvider | null) => { - this._provider = provider - }, - }) - } - - public get config(): ResolvedCoverageOptions { - return { - ...this._config, - enabled: this.enabled, - } - } - - public get enabled() { - return this._enabled && !this.worker.collecting - } - - public get resolved() { - return !!this._provider - } - - public async enable() { - const vitest = this.worker.vitest - this._enabled = true - - const jsonReporter = this._config.reporter.find(([name]) => name === 'json') - this._config.reporter = [ - ['json', { - ...jsonReporter?.[1], - file: this.finalCoverageFileName, - }], - ] - this._config.reportOnFailure = true - this._config.reportsDirectory = join(tmpdir(), `vitest-coverage-${randomUUID()}`) - - this.worker.vitest.logger.log('Running coverage with configuration:', this.config) - - if (!this._provider) { - // @ts-expect-error private method - await vitest.initCoverageProvider() - await this.coverageProvider?.clean(this._config.clean) - } - else { - await this._provider.clean(this._config.clean) - } - } - - private get coverageProvider() { - return (this.worker.vitest as any).coverageProvider as CoverageProvider | null | undefined - } - - public disable() { - this._enabled = false - } - - async waitForReport() { - if (!this.enabled) - return null - const ctx = this.worker.vitest - const coverage = ctx.config.coverage - if (!coverage.enabled || !this.coverageProvider) - return null - ctx.logger.error(`Waiting for the coverage report to generate: ${coverage.reportsDirectory}`) - await (ctx as any).runningPromise - if (existsSync(coverage.reportsDirectory)) { - ctx.logger.error(`Coverage reports retrieved: ${coverage.reportsDirectory}`) - return coverage.reportsDirectory - } - ctx.logger.error(`Coverage reports directory not found: ${coverage.reportsDirectory}`) - return null - } -} diff --git a/packages/worker-legacy/src/index.ts b/packages/worker-legacy/src/index.ts index babdd77..a139b7f 100644 --- a/packages/worker-legacy/src/index.ts +++ b/packages/worker-legacy/src/index.ts @@ -1,6 +1,11 @@ import type { SerializedProject, WorkerRunnerOptions, WorkerWSEventEmitter } from 'vitest-vscode-shared' import type { UserConfig } from 'vitest/node' +import { Console } from 'node:console' +import { randomUUID } from 'node:crypto' +import { tmpdir } from 'node:os' +import { Writable } from 'node:stream' import { toArray } from '@vitest/utils/helpers' +import { join } from 'pathe' import { VSCodeReporter } from './reporter' import { ExtensionWorker } from './worker' @@ -13,11 +18,33 @@ export async function initVitest( const reporter = new VSCodeReporter({ setupFilePaths: [ typeof data.debug === 'object' && data.debug.browser - ? meta.setupFilePaths.browserDebug + ? meta.setupFilePaths.browserDebugLegacy : null, ].filter(v => v != null), }) + let stdout: Writable | undefined + let stderr: Writable | undefined + + if (data.sendLog) { + stdout = new Writable({ + write(chunk, __, callback) { + const log = chunk.toString() + reporter.sendTerminalLog('stdout', log) + callback() + }, + }) + + stderr = new Writable({ + write(chunk, __, callback) { + const log = chunk.toString() + reporter.sendTerminalLog('stderr', log) + callback() + }, + }) + globalThis.console = new Console(stdout, stderr) + } + const pnpExecArgv = meta.pnpApi && meta.pnpLoader ? [ '--require', @@ -44,6 +71,7 @@ export async function initVitest( ...(meta.workspaceFile ? { workspace: meta.workspaceFile } : {}), ...args, ...options, + project: meta.projectFilter ?? args.project, watch: true, api: false, // @ts-expect-error private property @@ -95,6 +123,18 @@ export async function initVitest( } testReporters.push(reporter as any) test.reporters = testReporters + return { + test: { + coverage: { + enabled: !!data.coverage, + reportOnFailure: true, + reportsDirectory: join(tmpdir(), `vitest-coverage-${randomUUID()}`), + reporter: [ + ['json', { file: meta.finalCoverageFileName }], + ], + }, + }, + } }, configResolved(config) { // stub a server so Vite doesn't start a websocket connection, @@ -112,7 +152,7 @@ export async function initVitest( const options = context.project.config.browser if (options?.enabled && typeof data.debug === 'object') { - context.project.config.setupFiles.push(meta.setupFilePaths.browserDebug) + context.project.config.setupFiles.push(meta.setupFilePaths.browserDebugLegacy) context.vitest.config.inspector = { enabled: true, port: data.debug.port, @@ -125,8 +165,11 @@ export async function initVitest( }, ], }, + { + stderr, + stdout, + }, ) - await (vitest as any).report('onInit', vitest) const projects: SerializedProject[] = vitest.projects.map((project) => { const config = project.config @@ -165,7 +208,6 @@ export async function initVitest( vitest, !!data.debug, emitter, - data.meta.finalCoverageFileName, ) }, } diff --git a/packages/worker-legacy/src/reporter.ts b/packages/worker-legacy/src/reporter.ts index 67b9988..2f6e659 100644 --- a/packages/worker-legacy/src/reporter.ts +++ b/packages/worker-legacy/src/reporter.ts @@ -123,6 +123,19 @@ export class VSCodeReporter implements Reporter { this.rpc.onConsoleLog(extendedLog) } + private logPromises = new Set>() + sendTerminalLog(type: 'stderr' | 'stdout', message: string) { + if (!this.rpc) { + return + } + + const promise = this.rpc.onProcessLog(type, message).catch(() => {}).finally(() => { + this.logPromises.delete(promise) + }) + + this.logPromises.add(promise) + } + parseStackTrace(obj: ErrorWithDiff, taskId: string | undefined) { const project = taskId ? this.vitest.getProjectByTaskId(taskId) @@ -169,7 +182,7 @@ export class VSCodeReporter implements Reporter { this.rpc.onTaskUpdate(packs) } - async onFinished(files?: RunnerTestFile[], errors: unknown[] = this.vitest.state.getUnhandledErrors()) { + async onFinished(files?: RunnerTestFile[], errors: unknown[] = this.vitest.state.getUnhandledErrors(), coverage?: unknown) { const collecting = this.collecting let output = '' @@ -191,8 +204,15 @@ export class VSCodeReporter implements Reporter { this.vitest.logger.errorStream = errorStream this.vitest.logger.outputStream = outputStream } + + // Make sure we rendered everything before ending the test run + // If test run is no active, the log will be lost + if (this.logPromises.size) { + await Promise.all([...this.logPromises]) + } + nextTick(() => { - this.rpc.onTestRunEnd(files || [], output, collecting) + this.rpc.onTestRunEnd(files || [], output, collecting, coverage) }) } @@ -201,7 +221,7 @@ export class VSCodeReporter implements Reporter { } onWatcherRerun(files: string[]) { - this.rpc.onTestRunStart(files, this.collecting) + this.rpc.onTestRunStart(files) } toJSON() { diff --git a/packages/worker-legacy/src/watcher.ts b/packages/worker-legacy/src/watcher.ts index 6901415..c92d2bd 100644 --- a/packages/worker-legacy/src/watcher.ts +++ b/packages/worker-legacy/src/watcher.ts @@ -9,7 +9,7 @@ export class ExtensionWorkerWatcher { private enabled = false - constructor(worker: ExtensionWorker) { + constructor(private worker: ExtensionWorker) { // eslint-disable-next-line ts/no-this-alias const state = this const vitest = worker.vitest @@ -31,23 +31,7 @@ export class ExtensionWorkerWatcher { return await originalScheduleRerun.call(this, []) } - const tests = Array.from(this.changedTests) - const specs = tests.flatMap(file => this.getProjectsByTestFile(file)) - const astSpecs: [project: WorkspaceProject, file: string][] = [] - - for (const [project, file] of specs) { - astSpecs.push([project, file]) - } - - worker.setGlobalTestNamePattern(ExtensionWorker.COLLECT_NAME_PATTERN) - vitest.logger.log('Collecting tests due to file changes:', ...files.map(f => relative(vitest.config.root, f))) - - if (astSpecs.length) { - vitest.logger.log('Collecting using AST explorer...') - await worker.astCollect(astSpecs) - this.changedTests.clear() - return await originalScheduleRerun.call(this, []) - } + await state.collectTests(files, Array.from(this.changedTests)) return await originalScheduleRerun.call(this, []) } @@ -62,6 +46,8 @@ export class ExtensionWorkerWatcher { return await originalScheduleRerun.call(this, files) } + const changedFiles = Array.from(this.changedTests) + if (!isTestFileTrigger) { // if souce code is changed and related tests are not continious, remove them from changedTests const currentChanged = Array.from(this.changedTests) @@ -71,6 +57,10 @@ export class ExtensionWorkerWatcher { this.changedTests.add(file) } } + // the other test file was edited, ignore it + else if (!state.isTestFileWatched(triggerFile)) { + this.changedTests.clear() + } if (this.changedTests.size) { vitest.logger.log( @@ -79,11 +69,33 @@ export class ExtensionWorkerWatcher { namePattern ? `with pattern ${namePattern}` : '', ) } + else { + await state.collectTests(files, changedFiles) + } return await originalScheduleRerun.call(this, files) } } + private async collectTests(trigger: string[], tests: string[]) { + const vitest = this.worker.vitest + const specs = tests.flatMap(file => vitest.getProjectsByTestFile(file)) + const astSpecs: [project: WorkspaceProject, file: string][] = [] + + for (const [project, file] of specs) { + astSpecs.push([project, file]) + } + + this.worker.setGlobalTestNamePattern(ExtensionWorker.COLLECT_NAME_PATTERN) + vitest.logger.log('Collecting tests due to file changes:', ...trigger.map(f => relative(vitest.config.root, f))) + + if (astSpecs.length) { + vitest.logger.log('Collecting using AST explorer...') + await this.worker.astCollect(astSpecs) + vitest.changedTests.clear() + } + } + private isTestFileWatched(testFile: string) { if (!this.files?.length) return false @@ -110,8 +122,4 @@ export class ExtensionWorkerWatcher { this.files = [] this.testNamePattern = undefined } - - stopTracking() { - this.enabled = false - } } diff --git a/packages/worker-legacy/src/worker.ts b/packages/worker-legacy/src/worker.ts index 4447c1a..1e3650c 100644 --- a/packages/worker-legacy/src/worker.ts +++ b/packages/worker-legacy/src/worker.ts @@ -17,14 +17,12 @@ import mm from 'micromatch' import { relative } from 'pathe' import { assert, limitConcurrency } from '../../shared/src/utils' import { astCollectTests, createFailedFileTask } from './collect' -import { ExtensionCoverageManager } from './coverage' import { ExtensionWorkerWatcher } from './watcher' type ArgumentsType = T extends (...args: infer U) => any ? U : never export class ExtensionWorker implements ExtensionWorkerTransport { private readonly watcher: ExtensionWorkerWatcher - private readonly coverage: ExtensionCoverageManager public static emitter = new EventEmitter() @@ -33,11 +31,9 @@ export class ExtensionWorker implements ExtensionWorkerTransport { constructor( public readonly vitest: VitestCore, private readonly debug = false, - private emitter: WorkerWSEventEmitter, - finalCoverageFileName: string, + private ws: WorkerWSEventEmitter, ) { this.watcher = new ExtensionWorkerWatcher(this) - this.coverage = new ExtensionCoverageManager(this, finalCoverageFileName) } public get collecting() { @@ -144,8 +140,7 @@ export class ExtensionWorker implements ExtensionWorkerTransport { // debugger never runs in watch mode if (this.debug) { - await this.vitest.close() - this.emitter.close() + await this.exit() } } @@ -206,8 +201,7 @@ export class ExtensionWorker implements ExtensionWorkerTransport { this.setTestNamePattern(testNamePattern) // populate cache so it can find test files - if (this.debug) - await this.globTestSpecifications(specs.map(f => f[1])) + await this.globTestSpecifications(specs.map(f => f[1])) await this.rerunTests(specs, runAllFiles) } @@ -336,60 +330,18 @@ export class ExtensionWorker implements ExtensionWorkerTransport { return false } - unwatchTests() { - return this.watcher.stopTracking() - } + async watchTests(files?: ExtensionTestSpecification[] | string[] | undefined, testNamePatern?: string) { + await this.globTestSpecifications(files?.map(f => typeof f === 'string' ? f : f[1])) - watchTests(files?: ExtensionTestSpecification[] | string[] | undefined, testNamePatern?: string) { if (files) this.watcher.trackTests(files.map(f => typeof f === 'string' ? f : f[1]), testNamePatern) else this.watcher.trackEveryFile() } - // we need to invalidate the modules because Vitest caches the code injected by istanbul - async invalidateIstanbulTestModules(modules: string[] | null) { - if (!this.coverage.enabled || this.coverage.config.provider !== 'istanbul') { - return - } - if (!modules) { - this.vitest.server.moduleGraph.invalidateAll() - return - } - modules.forEach((moduleId) => { - const mod = this.vitest.server.moduleGraph.getModuleById(moduleId) - if (mod) { - this.invalidateTree(mod) - } - }) - } - - disableCoverage() { - return this.coverage.disable() - } - - async enableCoverage() { - try { - return await this.coverage.enable() - } - catch (error) { - this.disableCoverage() - throw error - } - } - - waitForCoverageReport() { - return this.coverage.waitForReport() - } - - dispose() { - this.coverage.disable() - this.watcher.stopTracking() - return this.vitest.close() - } - - close() { - return this.dispose() + async exit() { + await this.vitest.exit() + this.ws.close() } report(name: T, ...args: ArgumentsType) { diff --git a/packages/worker/src/coverage.ts b/packages/worker/src/coverage.ts deleted file mode 100644 index 060e0c2..0000000 --- a/packages/worker/src/coverage.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { Vitest } from 'vitest/node' -import { existsSync } from 'node:fs' - -const verbose = process.env.VITEST_VSCODE_LOG === 'verbose' - ? (...args: any[]) => { - // eslint-disable-next-line no-console - console.info(...args) - } - : undefined - -export class ExtensionCoverageManager { - private _enabled = false - - constructor(private vitest: Vitest) {} - - async enableCoverage() { - await this.vitest.enableCoverage() - this._enabled = true - } - - disableCoverage() { - this._enabled = false - this.vitest.disableCoverage() - } - - async waitForReport() { - if (!this._enabled) - return null - const vitest = this.vitest - const coverage = vitest.config.coverage - verbose?.(`Waiting for the coverage report to generate: ${coverage.reportsDirectory}`) - await vitest.waitForTestRunEnd() - if (existsSync(coverage.reportsDirectory)) { - verbose?.(`Coverage reports retrieved: ${coverage.reportsDirectory}`) - return coverage.reportsDirectory - } - verbose?.(`Coverage reports directory not found: ${coverage.reportsDirectory}`) - return null - } -} diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts index bc5df58..0579c9d 100644 --- a/packages/worker/src/index.ts +++ b/packages/worker/src/index.ts @@ -1,9 +1,6 @@ import type { SerializedProject, WorkerRunnerOptions, WorkerWSEventEmitter } from 'vitest-vscode-shared' -import type { CoverageIstanbulOptions, TestUserConfig } from 'vitest/node' +import type { TestUserConfig } from 'vitest/node' import { Console } from 'node:console' -import { randomUUID } from 'node:crypto' -import { tmpdir } from 'node:os' -import { join } from 'node:path' import { Writable } from 'node:stream' import { toArray } from '@vitest/utils/helpers' import { VSCodeReporter } from './reporter' @@ -20,12 +17,11 @@ export async function initVitest( let stdout: Writable | undefined let stderr: Writable | undefined - if (data.debug) { + if (data.sendLog) { stdout = new Writable({ write(chunk, __, callback) { const log = chunk.toString() reporter.sendTerminalLog('stdout', log) - // process.stdout.write(log) callback() }, }) @@ -34,7 +30,6 @@ export async function initVitest( write(chunk, __, callback) { const log = chunk.toString() reporter.sendTerminalLog('stderr', log) - // process.stderr.write(log) callback() }, }) @@ -58,6 +53,7 @@ export async function initVitest( config: meta.configFile, ...args, ...options, + project: meta.projectFilter ?? args.project, watch: true, api: false, // @ts-expect-error private property @@ -89,17 +85,6 @@ export async function initVitest( config(userConfig) { userConfig.test ??= {} - const testConfig = userConfig.test - const coverageOptions = (testConfig.coverage ??= {}) as CoverageIstanbulOptions - const coverageReporters = coverageOptions.reporter && Array.isArray(coverageOptions.reporter) - ? coverageOptions.reporter - : [coverageOptions.reporter] - const jsonReporter = coverageReporters.find(r => r && r[0] === 'json') - const jsonReporterOptions = typeof jsonReporter?.[1] === 'object' ? jsonReporter[1] : {} - coverageOptions.reporter = [ - ['json', { ...jsonReporterOptions, file: meta.finalCoverageFileName }], - ] - const testReporters = toArray(userConfig.test.reporters) if (!testReporters.length) { testReporters.push(['default', { isTTY: false }]) @@ -111,8 +96,11 @@ export async function initVitest( test: { printConsoleTrace: true, coverage: { + enabled: !!data.coverage, reportOnFailure: true, - reportsDirectory: join(tmpdir(), `vitest-coverage-${randomUUID()}`), + reporter: [ + ['json', { file: meta.finalCoverageFileName }], + ], }, }, } @@ -142,7 +130,6 @@ export async function initVitest( stdout, }, ) - await (vitest as any).report('onInit', vitest) const projects: SerializedProject[] = vitest.projects.map((project) => { const config = project.config diff --git a/packages/worker/src/reporter.ts b/packages/worker/src/reporter.ts index 0768772..1373ca5 100644 --- a/packages/worker/src/reporter.ts +++ b/packages/worker/src/reporter.ts @@ -23,6 +23,7 @@ export class VSCodeReporter implements Reporter { private execArgv: string[] = [] private debuggerAttached: boolean | undefined = undefined + private coverageData: Record | undefined = undefined constructor(meta: WorkerInitMetadata, debug: WorkerRunnerOptions['debug']) { this.setupFilePaths = meta.setupFilePaths @@ -136,10 +137,14 @@ export class VSCodeReporter implements Reporter { onTestRunStart(specifications: ReadonlyArray) { const files = specifications.map(spec => spec.moduleId) - this.rpc.onTestRunStart(Array.from(new Set(files)), false) + this.rpc.onTestRunStart(Array.from(new Set(files))) this.vitest.state.filesMap.clear() } + onCoverage(coverage: unknown) { + this.coverageData = (coverage as any).toJSON() + } + async onTestRunEnd(testModules: ReadonlyArray) { const files = testModules.map(m => getEntityJSONTask(m)) @@ -149,9 +154,12 @@ export class VSCodeReporter implements Reporter { await Promise.all([...this.logPromises]) } + const coverage = this.coverageData + this.coverageData = undefined + // as any because Vitest types are different between v3 and v4, // and shared packages uses the lowest Vitest version - this.rpc.onTestRunEnd(files as any, '', false) + this.rpc.onTestRunEnd(files as any, '', false, coverage) } onTestModuleCollected(testModule: TestModule) { diff --git a/packages/worker/src/runner.ts b/packages/worker/src/runner.ts index 0d9af09..3249619 100644 --- a/packages/worker/src/runner.ts +++ b/packages/worker/src/runner.ts @@ -54,12 +54,19 @@ export class ExtensionWorkerRunner { return this.vitest.cancelCurrentRun('keyboard-input') } - async runTests(filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string): Promise { + async runTests( + filesOrDirectories?: ExtensionTestSpecification[] | string[], + testNamePattern?: string, + ): Promise { const currentTestNamePattern = this.getGlobalTestNamePattern() if (testNamePattern) { this.vitest.setGlobalTestNamePattern(testNamePattern) } + if (this.vitest.config.coverage.enabled) { + await this.vitest.enableCoverage() + } + if (!filesOrDirectories || this.isOnlyDirectories(filesOrDirectories)) { const specifications = await this.vitest.getRelevantTestSpecifications(filesOrDirectories) await this.vitest.rerunTestSpecifications(specifications, true) diff --git a/packages/worker/src/worker.ts b/packages/worker/src/worker.ts index 792c637..6cd4822 100644 --- a/packages/worker/src/worker.ts +++ b/packages/worker/src/worker.ts @@ -8,13 +8,11 @@ import type { } from 'vitest-vscode-shared' import type { Vitest as VitestCore } from 'vitest/node' import EventEmitter from 'node:events' -import { ExtensionCoverageManager } from './coverage' import { ExtensionWorkerRunner } from './runner' import { ExtensionWorkerWatcher } from './watcher' export class ExtensionWorker implements ExtensionWorkerTransport { private readonly watcher: ExtensionWorkerWatcher - private readonly coverage: ExtensionCoverageManager private readonly runner: ExtensionWorkerRunner static emitter = new EventEmitter() @@ -22,11 +20,10 @@ export class ExtensionWorker implements ExtensionWorkerTransport { constructor( public readonly vitest: VitestCore, debug = false, - ws: WorkerWSEventEmitter, + private ws: WorkerWSEventEmitter, ) { this.runner = new ExtensionWorkerRunner(vitest, debug, ws) this.watcher = new ExtensionWorkerWatcher(vitest, this.runner) - this.coverage = new ExtensionCoverageManager(vitest) } async getFiles(): Promise { @@ -49,10 +46,21 @@ export class ExtensionWorker implements ExtensionWorkerTransport { return this.runner.updateSnapshots(filesOrDirectories, testNamePattern) } - watchTests(filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string): void { + async watchTests(filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string): Promise { + // Reset previous tracking state so re-clicking continuous run + // picks up the new files/pattern instead of appending to old ones + this.watcher.stopTracking() + if (testNamePattern) { this.vitest.setGlobalTestNamePattern(testNamePattern) } + else { + this.vitest.resetGlobalTestNamePattern() + } + + // Ensure test files are globbed so vitest's watcher can recognize them + // on file change (fresh process hasn't run globTestSpecifications yet) + await this.vitest.globTestSpecifications() if (!filesOrDirectories) { this.watcher.trackEveryFile() @@ -66,28 +74,6 @@ export class ExtensionWorker implements ExtensionWorkerTransport { this.watcher.stopTracking() } - async invalidateIstanbulTestModules(): Promise { - // do nothing, because Vitest 4 supports this out of the box - } - - async enableCoverage(): Promise { - try { - await this.coverage.enableCoverage() - } - catch (error) { - this.disableCoverage() - throw error - } - } - - disableCoverage(): void { - this.coverage.disableCoverage() - } - - waitForCoverageReport(): Promise { - return this.coverage.waitForReport() - } - onFilesChanged(files: string[]): void { files.forEach(file => this.vitest.watcher.onFileChange(file)) } @@ -96,14 +82,9 @@ export class ExtensionWorker implements ExtensionWorkerTransport { files.forEach(file => this.vitest.watcher.onFileCreate(file)) } - dispose() { - this.coverage.disableCoverage() - this.watcher.stopTracking() - return this.vitest.close() - } - - close() { - return this.dispose() + async exit() { + await this.vitest.exit() + this.ws.close() } initRpc(rpc: VitestWorkerRPC) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f1f30f5..d122a9f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -164,7 +164,7 @@ importers: version: 5.9.3 vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) which: specifier: ^4.0.0 version: 4.0.0 @@ -188,7 +188,7 @@ importers: version: 4.0.3 vitest: specifier: catalog:v3 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) vitest-vscode-shared: specifier: workspace:* version: link:../shared @@ -200,7 +200,7 @@ importers: version: 2.4.0 vitest: specifier: catalog:v3 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) packages/worker: devDependencies: @@ -209,7 +209,7 @@ importers: version: 4.1.0-beta.3 vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) vitest-vscode-shared: specifier: workspace:* version: link:../shared @@ -224,7 +224,7 @@ importers: version: 3.2.4 vitest: specifier: catalog:v3 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) vitest-vscode-shared: specifier: workspace:* version: link:../shared @@ -243,7 +243,7 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) samples/basic: dependencies: @@ -259,13 +259,16 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:v3 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) samples/basic-v4: devDependencies: '@vitest/browser': specifier: catalog:latest version: 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) + '@vitest/coverage-istanbul': + specifier: ^4.0.18 + version: 4.0.18(vitest@4.1.0-beta.3) '@vitest/coverage-v8': specifier: catalog:latest version: 4.1.0-beta.3(@vitest/browser@4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3))(vitest@4.1.0-beta.3) @@ -274,7 +277,7 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) samples/browser: dependencies: @@ -299,19 +302,19 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) samples/continuous: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) samples/e2e: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) samples/imba: devDependencies: @@ -347,7 +350,7 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) samples/monorepo-vitest-workspace: devDependencies: @@ -359,7 +362,7 @@ importers: version: 15.11.7 vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) samples/monorepo-vitest-workspace/packages/react: dependencies: @@ -381,7 +384,7 @@ importers: version: 2.55.0 jsdom: specifier: latest - version: 28.0.0 + version: 28.1.0 react-test-renderer: specifier: 17.0.2 version: 17.0.2(react@17.0.2) @@ -406,7 +409,7 @@ importers: version: 2.55.0 jsdom: specifier: latest - version: 28.0.0 + version: 28.1.0 react-test-renderer: specifier: 17.0.2 version: 17.0.2(react@17.0.2) @@ -415,19 +418,19 @@ importers: dependencies: vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) samples/no-config: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) samples/readme: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) samples/vue: dependencies: @@ -452,7 +455,7 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@14.7.1)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@14.7.1)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) packages: @@ -539,11 +542,12 @@ packages: '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} - '@asamuzakjp/css-color@4.1.2': - resolution: {integrity: sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==} + '@asamuzakjp/css-color@5.0.1': + resolution: {integrity: sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - '@asamuzakjp/dom-selector@6.7.8': - resolution: {integrity: sha512-stisC1nULNc9oH5lakAj8MH88ZxeGxzyWNDfbdCxvJSJIvDsHNZqYvscGTgy/ysgXWLJPt6K/4t0/GjvtKcFJQ==} + '@asamuzakjp/dom-selector@6.8.1': + resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==} '@asamuzakjp/nwsapi@2.3.9': resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} @@ -735,6 +739,10 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} + '@bramus/specificity@2.4.2': + resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} + hasBin: true + '@clack/core@0.5.0': resolution: {integrity: sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==} @@ -745,8 +753,8 @@ packages: resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} engines: {node: '>=18'} - '@csstools/color-helpers@6.0.1': - resolution: {integrity: sha512-NmXRccUJMk2AWA5A7e5a//3bCIMyOu2hAtdRYrhPPHjDxINuCwX1w6rnIZ4xjLcp0ayv6h8Pc3X0eJUGiAAXHQ==} + '@csstools/color-helpers@6.0.2': + resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} engines: {node: '>=20.19.0'} '@csstools/css-calc@2.1.4': @@ -756,8 +764,8 @@ packages: '@csstools/css-parser-algorithms': ^3.0.5 '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-calc@3.0.0': - resolution: {integrity: sha512-q4d82GTl8BIlh/dTnVsWmxnbWJeb3kiU8eUH71UxlxnS+WIaALmtzTL8gR15PkYOexMQYVk0CO4qIG93C1IvPA==} + '@csstools/css-calc@3.1.1': + resolution: {integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -770,8 +778,8 @@ packages: '@csstools/css-parser-algorithms': ^3.0.5 '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-color-parser@4.0.1': - resolution: {integrity: sha512-vYwO15eRBEkeF6xjAno/KQ61HacNhfQuuU/eGwH67DplL0zD5ZixUa563phQvUelA07yDczIXdtmYojCphKJcw==} + '@csstools/css-color-parser@4.0.2': + resolution: {integrity: sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -789,8 +797,8 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.0.26': - resolution: {integrity: sha512-6boXK0KkzT5u5xOgF6TKB+CLq9SOpEGmkZw0g5n9/7yg85wab3UzSxB8TxhLJ31L4SGJ6BCFRw/iftTha1CJXA==} + '@csstools/css-syntax-patches-for-csstree@1.0.29': + resolution: {integrity: sha512-jx9GjkkP5YHuTmko2eWAvpPnb0mB4mGRr2U7XwVNwevm8nlpobZEVk+GNmiYMk2VuA75v+plfXWyroWKmICZXg==} '@csstools/css-tokenizer@3.0.4': resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} @@ -1801,6 +1809,11 @@ packages: peerDependencies: vitest: 4.1.0-beta.3 + '@vitest/coverage-istanbul@4.0.18': + resolution: {integrity: sha512-0OhjP30owEDihYTZGWuq20rNtV1RjjJs1Mv4MaZIKcFBmiLUXX7HJLX4fU7wE+Mrc3lQxI2HKq6WrSXi5FGuCQ==} + peerDependencies: + vitest: 4.0.18 + '@vitest/coverage-v8@3.2.4': resolution: {integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==} peerDependencies: @@ -2418,8 +2431,8 @@ packages: resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} engines: {node: '>=18'} - cssstyle@5.3.7: - resolution: {integrity: sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==} + cssstyle@6.2.0: + resolution: {integrity: sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==} engines: {node: '>=20'} csstype@3.2.3: @@ -3592,6 +3605,10 @@ packages: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + istanbul-lib-report@3.0.1: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} @@ -3669,8 +3686,8 @@ packages: canvas: optional: true - jsdom@28.0.0: - resolution: {integrity: sha512-KDYJgZ6T2TKdU8yBfYueq5EPG/EylMsBvCaenWMJb2OXmjgczzwveRCoJ+Hgj1lXPDyasvrgneSn4GBuR1hYyA==} + jsdom@28.1.0: + resolution: {integrity: sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 @@ -3830,8 +3847,8 @@ packages: resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} engines: {node: 20 || >=22} - lru-cache@11.2.5: - resolution: {integrity: sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==} + lru-cache@11.2.6: + resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -5114,8 +5131,8 @@ packages: resolution: {integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==} engines: {node: '>=20.18.1'} - undici@7.20.0: - resolution: {integrity: sha512-MJZrkjyd7DeC+uPZh+5/YaMDxFiiEEaDgbUSVMXayofAkDWF1088CDo+2RPg7B1BuS1qf1vgNE7xqwPxE0DuSQ==} + undici@7.22.0: + resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==} engines: {node: '>=20.18.1'} unicorn-magic@0.1.0: @@ -5619,21 +5636,21 @@ snapshots: '@csstools/css-tokenizer': 3.0.4 lru-cache: 10.4.3 - '@asamuzakjp/css-color@4.1.2': + '@asamuzakjp/css-color@5.0.1': dependencies: - '@csstools/css-calc': 3.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-color-parser': 4.0.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - lru-cache: 11.2.5 + lru-cache: 11.2.6 - '@asamuzakjp/dom-selector@6.7.8': + '@asamuzakjp/dom-selector@6.8.1': dependencies: '@asamuzakjp/nwsapi': 2.3.9 bidi-js: 1.0.3 css-tree: 3.1.0 is-potential-custom-element-name: 1.0.1 - lru-cache: 11.2.5 + lru-cache: 11.2.6 '@asamuzakjp/nwsapi@2.3.9': {} @@ -5893,6 +5910,10 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} + '@bramus/specificity@2.4.2': + dependencies: + css-tree: 3.1.0 + '@clack/core@0.5.0': dependencies: picocolors: 1.1.1 @@ -5906,14 +5927,14 @@ snapshots: '@csstools/color-helpers@5.1.0': {} - '@csstools/color-helpers@6.0.1': {} + '@csstools/color-helpers@6.0.2': {} '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': dependencies: '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-calc@3.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-calc@3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -5925,10 +5946,10 @@ snapshots: '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-color-parser@4.0.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-color-parser@4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: - '@csstools/color-helpers': 6.0.1 - '@csstools/css-calc': 3.0.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/color-helpers': 6.0.2 + '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -5940,7 +5961,7 @@ snapshots: dependencies: '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.0.26': {} + '@csstools/css-syntax-patches-for-csstree@1.0.29': {} '@csstools/css-tokenizer@3.0.4': {} @@ -6802,7 +6823,7 @@ snapshots: '@vitest/mocker': 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) playwright: 1.57.0 tinyrainbow: 3.0.3 - vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - bufferutil - msw @@ -6818,7 +6839,7 @@ snapshots: magic-string: 0.30.21 sirv: 3.0.2 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) ws: 8.19.0 optionalDependencies: playwright: 1.57.0 @@ -6837,7 +6858,7 @@ snapshots: pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.0.3 - vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) ws: 8.19.0 transitivePeerDependencies: - bufferutil @@ -6845,6 +6866,22 @@ snapshots: - utf-8-validate - vite + '@vitest/coverage-istanbul@4.0.18(vitest@4.1.0-beta.3)': + dependencies: + '@istanbuljs/schema': 0.1.3 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.1 + obug: 2.1.1 + tinyrainbow: 3.0.3 + vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + transitivePeerDependencies: + - supports-color + '@vitest/coverage-v8@3.2.4(@vitest/browser@3.2.4)(vitest@3.2.4)': dependencies: '@ampproject/remapping': 2.3.0 @@ -6860,7 +6897,7 @@ snapshots: std-env: 3.10.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) optionalDependencies: '@vitest/browser': 3.2.4(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) transitivePeerDependencies: @@ -6878,7 +6915,7 @@ snapshots: obug: 2.1.1 std-env: 3.10.0 tinyrainbow: 3.0.3 - vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) optionalDependencies: '@vitest/browser': 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) @@ -6889,7 +6926,7 @@ snapshots: eslint: 9.39.1(jiti@2.6.1) optionalDependencies: typescript: 5.9.3 - vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color @@ -7619,12 +7656,12 @@ snapshots: '@asamuzakjp/css-color': 3.2.0 rrweb-cssom: 0.8.0 - cssstyle@5.3.7: + cssstyle@6.2.0: dependencies: - '@asamuzakjp/css-color': 4.1.2 - '@csstools/css-syntax-patches-for-csstree': 1.0.26 + '@asamuzakjp/css-color': 5.0.1 + '@csstools/css-syntax-patches-for-csstree': 1.0.29 css-tree: 3.1.0 - lru-cache: 11.2.5 + lru-cache: 11.2.6 csstype@3.2.3: {} @@ -8879,6 +8916,16 @@ snapshots: istanbul-lib-coverage@3.2.2: {} + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 7.7.3 + transitivePeerDependencies: + - supports-color + istanbul-lib-report@3.0.1: dependencies: istanbul-lib-coverage: 3.2.2 @@ -8979,12 +9026,13 @@ snapshots: - supports-color - utf-8-validate - jsdom@28.0.0: + jsdom@28.1.0: dependencies: '@acemir/cssom': 0.9.31 - '@asamuzakjp/dom-selector': 6.7.8 + '@asamuzakjp/dom-selector': 6.8.1 + '@bramus/specificity': 2.4.2 '@exodus/bytes': 1.11.0 - cssstyle: 5.3.7 + cssstyle: 6.2.0 data-urls: 7.0.0 decimal.js: 10.6.0 html-encoding-sniffer: 6.0.0 @@ -8995,7 +9043,7 @@ snapshots: saxes: 6.0.0 symbol-tree: 3.2.4 tough-cookie: 6.0.0 - undici: 7.20.0 + undici: 7.22.0 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 @@ -9154,7 +9202,7 @@ snapshots: lru-cache@11.2.4: {} - lru-cache@11.2.5: {} + lru-cache@11.2.6: {} lru-cache@5.1.1: dependencies: @@ -10700,7 +10748,7 @@ snapshots: undici@7.16.0: {} - undici@7.20.0: {} + undici@7.22.0: {} unicorn-magic@0.1.0: {} @@ -10825,7 +10873,7 @@ snapshots: source-map-js: 1.2.1 vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@24.1.3)(tsx@4.21.0)(yaml@2.8.2) - vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 @@ -10855,7 +10903,7 @@ snapshots: '@types/node': 24.10.1 '@vitest/browser': 3.2.4(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) happy-dom: 15.11.7 - jsdom: 28.0.0 + jsdom: 28.1.0 transitivePeerDependencies: - jiti - less @@ -10870,7 +10918,7 @@ snapshots: - tsx - yaml - vitest@4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@14.7.1)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2): + vitest@4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@14.7.1)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.1.0-beta.3 '@vitest/mocker': 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) @@ -10896,7 +10944,7 @@ snapshots: '@types/node': 24.10.1 '@vitest/browser-playwright': 4.1.0-beta.3(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) happy-dom: 14.7.1 - jsdom: 28.0.0 + jsdom: 28.1.0 transitivePeerDependencies: - jiti - less @@ -10950,7 +10998,7 @@ snapshots: - tsx - yaml - vitest@4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.0.0)(tsx@4.21.0)(yaml@2.8.2): + vitest@4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@vitest/expect': 4.1.0-beta.3 '@vitest/mocker': 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) @@ -10976,7 +11024,7 @@ snapshots: '@types/node': 24.10.1 '@vitest/browser-playwright': 4.1.0-beta.3(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) happy-dom: 15.11.7 - jsdom: 28.0.0 + jsdom: 28.1.0 transitivePeerDependencies: - jiti - less diff --git a/samples/basic-v4/package.json b/samples/basic-v4/package.json index 2e2a52c..8a9c5ed 100644 --- a/samples/basic-v4/package.json +++ b/samples/basic-v4/package.json @@ -10,6 +10,7 @@ }, "devDependencies": { "@vitest/browser": "catalog:latest", + "@vitest/coverage-istanbul": "^4.0.18", "@vitest/coverage-v8": "catalog:latest", "vite": "catalog:latest", "vitest": "catalog:latest" diff --git a/samples/basic-v4/test/console.test.ts b/samples/basic-v4/test/console.test.ts index 4efe8f6..6cfae08 100644 --- a/samples/basic-v4/test/console.test.ts +++ b/samples/basic-v4/test/console.test.ts @@ -4,15 +4,16 @@ const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) describe('console', () => { it('basic', () => { - console.log([ + const variables = [ 'string', { hello: 'world' }, - 1234, + 1235, /regex/g, true, false, null, - ]) + ] + console.log(variables) }) it('async', async () => { diff --git a/samples/basic-v4/vitest.config.ts b/samples/basic-v4/vitest.config.ts index df287a4..40c3fe9 100644 --- a/samples/basic-v4/vitest.config.ts +++ b/samples/basic-v4/vitest.config.ts @@ -11,5 +11,8 @@ export default defineConfig({ test: { include: ['src/should_included_test.ts', 'test/**/*.test.ts'], exclude: ['test/ignored.test.ts'], + coverage: { + provider: 'istanbul' + } }, }) diff --git a/samples/browser/.vscode/settings.json b/samples/browser/.vscode/settings.json index 53d6bf6..4f3303c 100644 --- a/samples/browser/.vscode/settings.json +++ b/samples/browser/.vscode/settings.json @@ -2,6 +2,7 @@ "vitest.nodeEnv": { "TEST_CUSTOM_ENV": "hello" }, + "vitest.shellType": "child_process", "[typescript]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" } diff --git a/test/e2e/runner.test.ts b/test/e2e/runner.test.ts index 8febc55..c8187e8 100644 --- a/test/e2e/runner.test.ts +++ b/test/e2e/runner.test.ts @@ -98,7 +98,9 @@ test('custom imba language', async ({ launch }) => { await tester.runAllTests() - await expect(tester.tree.getResultsLocator()).toHaveText('3/4') + await expect(tester.tree.getResultsLocator()).toHaveText('3/4', { + timeout: 10_000, + }) await expect(tester.tree.getFileItem('basic.test.imba')).toHaveState('passed') await expect(tester.tree.getFileItem('utils.imba')).toHaveState('passed') await expect(tester.tree.getFileItem('counter.imba')).toHaveState('failed') diff --git a/test/e2e/utils/helper.ts b/test/e2e/utils/helper.ts index f424594..751ee87 100644 --- a/test/e2e/utils/helper.ts +++ b/test/e2e/utils/helper.ts @@ -30,7 +30,7 @@ const defaultConfig = process.env as { export const test = baseTest.extend<{ launch: LaunchFixture; taskName: string; logPath: string }>({ taskName: async ({ task }, use) => use(`${task.name}-${task.id}`), - logPath: async ({ taskName }, use) => use(resolve(`./test-results/tests-logs-${taskName}.txt`)), + logPath: async ({ taskName }, use) => use(resolve(`./test-results/${process.env.OS_NAME + '/' || ''}tests-logs-${taskName}.txt`)), launch: async ({ taskName, logPath }, use) => { const teardowns: (() => Promise)[] = [] @@ -68,14 +68,14 @@ export const test = baseTest.extend<{ launch: LaunchFixture; taskName: string; l const teardown = async () => { if (trace) { - await page.context().tracing.stop({ path: `test-results/${taskName}/basic.zip` }) + await page.context().tracing.stop({ path: `test-results/${process.env.OS_NAME + '/' || ''}${taskName}/basic.zip` }) } await app.close() await fs.promises.rm(tempDir, { recursive: true, force: true }) } teardowns.push(teardown) - const tester = new VSCodeTester(page) + const tester = new VSCodeTester(page, logPath) async function step(name: string, fn: (context: Context) => Promise | void) { await page.reload() diff --git a/test/e2e/utils/tester.ts b/test/e2e/utils/tester.ts index a2b85a9..c92115f 100644 --- a/test/e2e/utils/tester.ts +++ b/test/e2e/utils/tester.ts @@ -1,7 +1,7 @@ import { basename } from 'node:path' -import fs from 'node:fs' +import fs, { readFileSync } from 'node:fs' import type { Locator, Page } from '@playwright/test' -import { afterEach } from 'vitest' +import { afterEach, vi } from 'vitest' export class VSCodeTester { public tree: TesterTree @@ -9,8 +9,9 @@ export class VSCodeTester { constructor( private page: Page, + private logPath: string, ) { - this.tree = new TesterTree(page) + this.tree = new TesterTree(page, logPath) this.errors = new TesterErrorOutput(page) } @@ -33,6 +34,7 @@ export class VSCodeTester { class TesterTree { constructor( private page: Page, + private logPath: string, ) {} getResultsLocator() { @@ -42,7 +44,13 @@ class TesterTree { getFileItem(file: string, project?: string) { const name = basename(file) const label = project ? `${name} [${project}]` : name - return new TesterTestItem(name, this.page.locator(`[aria-label*="${label} ("]`), this.page, project) + return new TesterTestItem( + name, + this.page.locator(`[aria-label*="${label} ("]`), + this.page, + project, + this.logPath + ) } async expand(path: string) { @@ -92,6 +100,7 @@ export class TesterTestItem { public locator: Locator, public page: Page, public project: string | undefined, + private logPath: string, ) {} async run() { @@ -112,6 +121,12 @@ export class TesterTestItem { async toggleContinuousRun() { await this.locator.hover() await this.locator.getByLabel(/Turn (on|off) Continuous Run/).click() + await vi.waitUntil(() => { + const log = readFileSync(this.logPath, 'utf-8') + return log.includes('Watching test files') || log.includes('Watching all test files') + }, { + timeout: 5_000, + }) } async navigate() { diff --git a/test/unit/pkg.test.ts b/test/unit/pkg.test.ts index dcc1679..617e790 100644 --- a/test/unit/pkg.test.ts +++ b/test/unit/pkg.test.ts @@ -1,5 +1,5 @@ import { expect } from 'chai' -import { findFirstUniqueFolderNames } from '../../packages/extension/src/api/pkg' +import { findFirstUniqueFolderNames } from '../../packages/extension/src/spawn/pkg' it('correctly makes prefixes unique', () => { expect(findFirstUniqueFolderNames([ diff --git a/tsdown.config.mjs b/tsdown.config.mjs index 84d6715..0a7d890 100644 --- a/tsdown.config.mjs +++ b/tsdown.config.mjs @@ -20,8 +20,11 @@ export default defineConfig([ }, }, { - entry: ['./packages/extension/src/worker/browserSetupFile.ts'], - external: ['vitest', '@vitest/browser/context'], + entry: [ + './packages/extension/src/worker/browserSetupFile.ts', + './packages/extension/src/worker/browserSetupFileLegacy.ts', + ], + external: ['vitest', '@vitest/browser/context', 'vitest/browser'], fixedExtension: false, inlineOnly: false, platform: 'node', -- 2.51.2 From 6cfd8c1744c48afa00ac332a2b1ea5c9ecc753d4 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Mon, 9 Mar 2026 19:09:29 +0100 Subject: [PATCH 06/64] chore: release v1.45.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1768305..f3e777f 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "name": "explorer", "displayName": "Vitest", "type": "commonjs", - "version": "1.44.2", + "version": "1.45.0", "packageManager": "pnpm@10.11.1", "description": "A Vite-native testing framework. It's fast!", "author": "Vitest Team", -- 2.51.2 From c2ef7b5b4af1d7a56f14be0f8b696f721da8776f Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 10 Mar 2026 14:08:47 +0100 Subject: [PATCH 07/64] feat: support deno runtime (#744) --- README.md | 7 +- package.json | 6 ++ packages/extension/src/config.ts | 2 + packages/extension/src/debug.ts | 68 +++++++++++-------- packages/extension/src/extension.ts | 11 +-- packages/extension/src/spawn/child_process.ts | 18 +++-- packages/extension/src/spawn/pkg.ts | 23 +++++++ packages/extension/src/spawn/terminal.ts | 5 +- packages/extension/src/utils.ts | 30 +++++--- packages/extension/src/watcher.ts | 3 +- packages/shared/src/index.ts | 4 +- packages/worker-legacy/src/index.ts | 20 +++--- packages/worker-legacy/src/reporter.ts | 4 +- packages/worker-legacy/src/worker.ts | 4 +- packages/worker/src/index.ts | 12 +++- packages/worker/src/reporter.ts | 54 ++++++++------- packages/worker/src/worker.ts | 4 +- 17 files changed, 174 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index 660f0c8..0a32ba2 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,9 @@ ## Features - **Run**, **debug**, and **watch** Vitest tests in Visual Studio Code. -- **Coverage** support (requires VS Code >= 1.88) -- An `@open` tag can be used when filtering tests, to only show the tests open in the editor. +- **Coverage** support - **Inline console.log display**: Console logs appear inline in the editor next to the code that produced them +- **Imports duration**: Displays the execution time for each import during continuous test runs. ## Requirements @@ -78,6 +78,7 @@ These options are resolved relative to the [workspace file](https://code.visuals - `vitest.ignoreWorkspace`: Ignores the workspace resolution step. The extension will only look for `vitest.config` files. - `vitest.configSearchPatternInclude`: [Glob pattern](https://code.visualstudio.com/docs/editor/glob-patterns) that should be used when this extension looks for config files. Note that this is applied to _config_ files, not test files inside configs. Default: `**/*{vite,vitest}*.config*.{ts,js,mjs,cjs,cts,mts}`. - `vitest.configSearchPatternExclude`: [Glob pattern](https://code.visualstudio.com/docs/editor/glob-patterns) that should be ignored when this extension looks for config files. Note that this is applied to _config_ files, not test files inside configs. Default: `{**/node_modules/**,**/vendor/**,**/.*/**,*.d.ts}`. If the extension cannot find Vitest, please open an issue. +- `vitest.runtime`: The default runtime to run tests in. Supported: `auto` (default) `node` and `deno`. If auto, the extension will looks for a `deno.enabled` config flag or a `deno.json` file in the root folder. - `vitest.shellType`: The method the extension uses to spawn a long-running Vitest process. This is particularly useful if you are using a custom shell script to set up the environment. When using the `terminal` shell type, the websocket connection will be established. Can either be `terminal` or `child_process`. Default: `child_process`. - `vitest.nodeExecutable`: The path to the Node.js executable. If not assigned, tries to find Node.js path via a PATH variable or a `which` command. This is applied only when `vitest.shellType` is `child_process` (the default). - `vitest.nodeExecArgs`: The arguments to pass to the Node.js executable. This is applied only when `vitest.shellType` is `child_process` (the default). @@ -119,7 +120,7 @@ You can also type the same command in the quick picker while the file is open. ### Import Breakdown -If you use Vitest 4.0.15 or higher, the extension will show how long it took to load the module on the same line where the import is defined. This number includes transform time and evaluation time, including static imports. +If you use Vitest 4.0.15 or higher, during continuous runs the extension will show how long it took to load the module on the same line where the import is defined. This number includes transform time and evaluation time, including static imports. If you hover over it, you can get a more detailed diagnostic. diff --git a/package.json b/package.json index f3e777f..0172812 100644 --- a/package.json +++ b/package.json @@ -329,6 +329,12 @@ "type": "number", "default": 1000, "scope": "resource" + }, + "vitest.runtime": { + "description": "Which runtime to use by default.", + "type": "string", + "default": "auto", + "enum": ["auto", "node", "deno"] } } } diff --git a/packages/extension/src/config.ts b/packages/extension/src/config.ts index 76fbde1..e302b4b 100644 --- a/packages/extension/src/config.ts +++ b/packages/extension/src/config.ts @@ -72,6 +72,7 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) { const ignoreWorkspace = get('ignoreWorkspace', false) ?? false const showInlineConsoleLog = get('showInlineConsoleLog', true) ?? true const forceCancelTimeout = get('forceCancelTimeout', 1000) ?? 1000 + const runtime = get<'node' | 'deno' | 'auto'>('runtime', 'auto') ?? 'auto' return { env: get>('nodeEnv', null), @@ -79,6 +80,7 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) { debugExclude: get('debugExclude'), debugOutFiles, filesWatcherInclude, + runtime, forceCancelTimeout, terminalShellArgs, terminalShellPath, diff --git a/packages/extension/src/debug.ts b/packages/extension/src/debug.ts index 3661677..a1f7a07 100644 --- a/packages/extension/src/debug.ts +++ b/packages/extension/src/debug.ts @@ -18,10 +18,11 @@ import { log } from './log' import { TestRunner } from './runner' import { onWsConnection } from './spawn/ws' import { getTestData, TestCase, TestFile, TestFolder, TestSuite } from './testTreeData' -import { findNode } from './utils' +import { findRuntimeExecutable } from './utils' const DebugSessionName = 'Vitest' const BrowserDebugSessionName = 'Vitest_Browser' +const AttachSessionName = 'Vitest (Test)' export async function debugTests( controller: vscode.TestController, @@ -48,6 +49,10 @@ export async function debugTests( const debugEnv = config.debugEnv || {} const logLevel = config.logLevel + if (pkg.runtime === 'deno') { + runtimeArgs.push('-A') + } + log.info('[DEBUG]', 'Starting debugging session', runtimeExecutable, ...(runtimeArgs || [])) const debugId = crypto.randomUUID() @@ -131,19 +136,21 @@ export async function debugTests( const disposables: vscode.Disposable[] = [] + const attachDebug = browserDebug || pkg.runtime === 'deno' + ? { + browser: browserDebug?.browser, + // wdio support this only since Vitest 4.beta-13 + port: config.debuggerPort ?? 9229, + host: browserDebug ? 'localhost' : '127.0.0.1', + } + : undefined + wss.on( 'connection', ws => onWsConnection( ws, pkg, - browserDebug - ? { - browser: browserDebug.browser, - // wdio support this only since Vitest 4.beta-13 - port: config.debuggerPort ?? 9229, - host: 'localhost', - } - : true, + attachDebug ?? true, config.shellType, async (metadata) => { metadata.handlers.onProcessLog((type, message) => { @@ -173,25 +180,30 @@ export async function debugTests( await metadata.dispose() }) - if (browserDebug) { - const browserAttachConfig: vscode.DebugConfiguration = { - __name: BrowserDebugSessionName, + if (attachDebug) { + const attachConfig: vscode.DebugConfiguration = { + __name: browserDebug + ? BrowserDebugSessionName + : AttachSessionName, __parentId: debugId, + type: browserDebug + ? (browserDebug.browser === 'edge' ? 'msedge' : 'chrome') + : 'node', request: 'attach', - name: `Debug Tests (${browserDebug.browser})`, - address: 'localhost', - port: config.debuggerPort ?? 9229, + name: `Debug Tests (${attachDebug.browser || 'test'})`, + address: attachDebug.host, + port: attachDebug.port, ...( config.debugOutFiles?.length ? { outFiles: config.debugOutFiles } : {} ), - webRoot: browserDebug.webRoot, + webRoot: browserDebug?.webRoot, smartStep: true, skipFiles, cwd: pkg.cwd, - type: browserDebug.browser === 'edge' ? 'msedge' : 'chrome', } + log.info('[DEBUG] Attaching to', attachConfig) let parentSession: vscode.DebugSession | undefined for (const session of debugManager.sessions.values()) { if (session.configuration.__vitestId === debugId) { @@ -200,7 +212,7 @@ export async function debugTests( } vscode.debug.startDebugging( pkg.folder, - browserAttachConfig, + attachConfig, { parentSession, // this is required for the "restart" button to work @@ -210,18 +222,18 @@ export async function debugTests( }, ).then( (fullfilled) => { - log.info('[DEBUG] Browser debugger started') - metadata.rpc.onBrowserDebug(fullfilled).catch(() => {}) + log.info('[DEBUG] Debug session started') + metadata.rpc.onDebugAttached(fullfilled).catch(() => {}) if (fullfilled) { - log.info('[DEBUG] Browser debugger attached') + log.info('[DEBUG] Debug session attached') } else { - log.error('[DEBUG] Browser debugger failed to attach') + log.error('[DEBUG] Debugger failed to attach') } }, (error) => { - metadata.rpc.onBrowserDebug(false).catch(() => {}) - log.error('[DEBUG] Browser debugger failed to launch', error.message) + metadata.rpc.onDebugAttached(false).catch(() => {}) + log.error('[DEBUG] Attach session failed to launch', error.message) }, ) } @@ -292,15 +304,15 @@ async function getRuntimeOptions(pkg: VitestPackage) { ] : runtimeArgs if (config.shellType === 'child_process') { - const executable = await findNode(pkg.cwd) + const executable = await findRuntimeExecutable(pkg.runtime, pkg.cwd) return { runtimeExecutable: executable, - runtimeArgs: execArgv, + runtimeArgs: [...execArgv], } } return { - runtimeExecutable: 'node', - runtimeArgs: execArgv, + runtimeExecutable: config.runtime, + runtimeArgs: [...execArgv], } } diff --git a/packages/extension/src/extension.ts b/packages/extension/src/extension.ts index a171491..3c90bde 100644 --- a/packages/extension/src/extension.ts +++ b/packages/extension/src/extension.ts @@ -21,7 +21,7 @@ import { ExtensionState } from './state' import { TagsManager } from './tagsManager' import { TestTree } from './testTree' import { getTestData, TestFile } from './testTreeData' -import { debounce, showVitestError } from './utils' +import { clearCachedRuntime, debounce, showVitestError } from './utils' import './polyfills' export async function activate(context: vscode.ExtensionContext) { @@ -312,11 +312,16 @@ class VitestExtension { 'vitest.terminalShellPath', 'vitest.filesWatcherInclude', 'vitest.cliArguments', + 'vitest.runtime', + 'deno.enabled', ] this.disposables = [ vscode.workspace.onDidChangeConfiguration((event) => { const configName = reloadConfigNames.find(x => event.affectsConfiguration(x)) + if (event.affectsConfiguration('vitest.runtime') || event.affectsConfiguration('deno.enabled')) { + clearCachedRuntime() + } if (configName) { this.defineTestProfiles(false).catch((error) => { log.error('[API]', `Failed to reload Vitest after "${configName}" has changed`, error) @@ -553,7 +558,3 @@ class VitestExtension { this.runQueues.clear() } } - -// TODO: add to readme recommended process: -// - press continuous run -// - start editing tests diff --git a/packages/extension/src/spawn/child_process.ts b/packages/extension/src/spawn/child_process.ts index 2f31910..1bc0d69 100644 --- a/packages/extension/src/spawn/child_process.ts +++ b/packages/extension/src/spawn/child_process.ts @@ -13,7 +13,7 @@ import { WebSocketServer } from 'ws' import { getConfig } from '../config' import { workerPath } from '../constants' import { createErrorLogger, log } from '../log' -import { findNode, formatPkg, showVitestError } from '../utils' +import { findRuntimeExecutable, formatPkg, showVitestError } from '../utils' import { waitForWsConnection } from './ws' export async function createVitestProcess(pkg: VitestPackage, options?: ProcessSpawnOptions) { @@ -22,7 +22,8 @@ export async function createVitestProcess(pkg: VitestPackage, options?: ProcessS if (pnpLoader && !pnp) throw new Error('pnp file is required if loader option is used') const env = getConfig().env || {} - const runtimeArgs = getConfig(pkg.folder).nodeExecArgs || [] + const folderConfig = getConfig(pkg.folder) + const runtimeArgs = folderConfig.nodeExecArgs || [] const execArgv = pnpLoader && pnp ? [ '--require', @@ -33,15 +34,20 @@ export async function createVitestProcess(pkg: VitestPackage, options?: ProcessS ] : runtimeArgs const arvString = execArgv.join(' ') - const executable = await findNode(pkg.cwd) - const script = `${executable} ${arvString ? `${arvString} ` : ''}${workerPath}`.trim() + const executable = await findRuntimeExecutable(pkg.runtime, pkg.cwd) + let executablePath = workerPath + if (folderConfig.runtime === 'deno') { + execArgv.push('-A') + executablePath = pathToFileURL(workerPath).toString() + } + const script = `${executable} ${arvString ? `${arvString} ` : ''}${executablePath}`.trim() log.info('[API]', `Running ${formatPkg(pkg)} with "${script}"`) - const logLevel = getConfig(pkg.folder).logLevel + const logLevel = folderConfig.logLevel const port = await getPort() const server = createServer().listen(port).unref() const wss = new WebSocketServer({ server }) const wsAddress = `ws://localhost:${port}` - const vitest = spawn(executable, [...execArgv, workerPath], { + const vitest = spawn(executable, [...execArgv, executablePath], { env: { ...process.env, ...env, diff --git a/packages/extension/src/spawn/pkg.ts b/packages/extension/src/spawn/pkg.ts index 4283c57..2de76c1 100644 --- a/packages/extension/src/spawn/pkg.ts +++ b/packages/extension/src/spawn/pkg.ts @@ -26,6 +26,7 @@ export interface VitestPackage { workspaceFile?: string loader?: string pnp?: string + runtime: 'deno' | 'node' } function isVitestInPackageJson(root: string) { @@ -69,6 +70,7 @@ function resolveVitestConfig(showWarning: boolean, configOrWorkspaceFile: vscode const id = normalize(configOrWorkspaceFile.fsPath) const prefix = `${basename(dirname(id))}:${basename(id)}` + const runtime = guessRuntime(cwd, folder) if (vitest.pnp) { return { @@ -81,6 +83,7 @@ function resolveVitestConfig(showWarning: boolean, configOrWorkspaceFile: vscode version: 'pnp', loader: vitest.pnp.loaderPath, pnp: vitest.pnp.pnpPath, + runtime, } } @@ -96,6 +99,7 @@ function resolveVitestConfig(showWarning: boolean, configOrWorkspaceFile: vscode vitestPackageJsonPath: vitest.vitestPackageJsonPath, vitestNodePath: vitest.vitestNodePath, version: pkg.version, + runtime, } } @@ -156,6 +160,7 @@ function resolveVitestWorkspacePackages(showWarning: boolean) { } const id = normalize(folder.uri.fsPath) const prefix = `${basename(cwd)}:${basename(id)}` + const runtime = guessRuntime(cwd, folder) meta.push({ folder, id, @@ -164,6 +169,7 @@ function resolveVitestWorkspacePackages(showWarning: boolean) { vitestPackageJsonPath: vitest.vitestPackageJsonPath, vitestNodePath: vitest.vitestNodePath, version: pkg.version, + runtime, }) }) return { @@ -214,6 +220,7 @@ export async function resolveVitestPackagesViaPackageJson(showWarning: boolean): const id = `${normalize(pkgPath.fsPath)}/${scriptName}` const prefix = `${basename(cwd)}/package.json:${scriptName}` + const runtime = guessRuntime(cwd, folder) meta.push({ folder, id, @@ -223,6 +230,7 @@ export async function resolveVitestPackagesViaPackageJson(showWarning: boolean): vitestPackageJsonPath: vitest.vitestPackageJsonPath, vitestNodePath: vitest.vitestNodePath, version: pkg.version, + runtime, }) }) @@ -332,6 +340,21 @@ async function resolveVitestConfigs(showWarning: boolean) { } } +function guessRuntime(cwd: string, folder: vscode.WorkspaceFolder): 'deno' | 'node' { + const vitestConfig = getConfig(folder) + if (vitestConfig.runtime !== 'auto') { + return vitestConfig.runtime + } + const denoConfig = vscode.workspace.getConfiguration('deno', folder) + if (denoConfig.get('enabled')) { + return 'deno' + } + if (existsSync(resolve(cwd, 'deno.json'))) { + return 'deno' + } + return 'node' +} + export function findFirstUniqueFolderNames(paths: string[]) { const folders: string[] = [] const mapCount: Record = {} diff --git a/packages/extension/src/spawn/terminal.ts b/packages/extension/src/spawn/terminal.ts index 54cad69..e571187 100644 --- a/packages/extension/src/spawn/terminal.ts +++ b/packages/extension/src/spawn/terminal.ts @@ -52,10 +52,13 @@ export async function createVitestTerminalProcess(pkg: VitestPackage, options?: throw new Error(`Terminal was ${getExitReason(terminal.exitStatus.reason)} with code ${terminal.exitStatus.code}`) } - let command = 'node' + let command = pkg.runtime if (pnpLoader && pnp) { command += ` --require ${pnp} --experimental-loader ${pathToFileURL(pnpLoader).toString()}` } + if (pkg.runtime === 'deno') { + command += ' -A' + } command += ` ${workerPath};` log.info('[TERMINAL]', `Initiated ws connection via ${wsAddress}`) diff --git a/packages/extension/src/utils.ts b/packages/extension/src/utils.ts index d96fc01..4a06f73 100644 --- a/packages/extension/src/utils.ts +++ b/packages/extension/src/utils.ts @@ -71,45 +71,53 @@ export function waitUntilExists(file: string, timeoutMs = 5000) { }) } -let pathToNodeJS: string | undefined +const pathToRuntime: { + deno?: string + node?: string +} = {} + +export function clearCachedRuntime() { + pathToRuntime.deno = undefined + pathToRuntime.node = undefined +} // based on https://github.com/microsoft/playwright-vscode/blob/main/src/utils.ts#L144 -export async function findNode(cwd: string): Promise { +export async function findRuntimeExecutable(runtime: 'node' | 'deno', cwd: string): Promise { if (getConfig().nodeExecutable) // if empty string, keep as undefined - pathToNodeJS = getConfig().nodeExecutable || undefined + pathToRuntime[runtime] = getConfig().nodeExecutable || undefined - if (pathToNodeJS) - return pathToNodeJS + if (pathToRuntime[runtime]) + return pathToRuntime[runtime] // Stage 1: Try to find Node.js via process.env.PATH - let node: string | null = await which('node', { nothrow: true }) + let node: string | null = await which(runtime, { nothrow: true }) // Stage 2: When extension host boots, it does not have the right env set, so we might need to wait. for (let i = 0; i < 5 && !node; ++i) { await new Promise(f => setTimeout(f, 200)) - node = await which('node', { nothrow: true }) + node = await which(runtime, { nothrow: true }) } // Stage 3: If we still haven't found Node.js, try to find it via a subprocess. // This evaluates shell rc/profile files and makes nvm work. - node ??= await findNodeViaShell(cwd) + node ??= await findRuntimeViaShell(runtime, cwd) if (!node) { const msg = `Unable to find 'node' executable.\nMake sure to have Node.js installed and available in your PATH.\nCurrent PATH: '${process.env.PATH}'.` log.error(msg) throw new Error(msg) } - pathToNodeJS = node + pathToRuntime[runtime] = node return node } -async function findNodeViaShell(cwd: string): Promise { +async function findRuntimeViaShell(runtime: 'node' | 'deno', cwd: string): Promise { if (process.platform === 'win32') return null return new Promise((resolve) => { const startToken = '___START_SHELL__' const endToken = '___END_SHELL__' try { - const childProcess = spawn(`${vscode.env.shell} -i -c 'if [[ $(type node 2>/dev/null) == *function* ]]; then node --version; fi; echo ${startToken} && which node && echo ${endToken}'`, { + const childProcess = spawn(`${vscode.env.shell} -i -c 'if [[ $(type ${runtime} 2>/dev/null) == *function* ]]; then ${runtime} --version; fi; echo ${startToken} && which ${runtime} && echo ${endToken}'`, { stdio: 'pipe', shell: true, cwd, diff --git a/packages/extension/src/watcher.ts b/packages/extension/src/watcher.ts index 779b9d9..713cb41 100644 --- a/packages/extension/src/watcher.ts +++ b/packages/extension/src/watcher.ts @@ -120,8 +120,7 @@ export class ExtensionWatcher extends vscode.Disposable { } return false } - catch (err: unknown) { - log.verbose?.('[VSCODE] Error checking file stats:', this.relative(api, uri), err as string) + catch { return true } } diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index dddaa87..2068781 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -72,7 +72,7 @@ export interface ExtensionWorkerTransport { getModuleEnvironments: (moduleId: string) => ExtensionEnvironment[] getTransformedModule: (project: string, environment: string, moduleId: string) => string | null - onBrowserDebug: (fulfilled: boolean) => void + onDebugAttached: (fulfilled: boolean) => void } export interface ExtensionWorkerEvents { @@ -146,7 +146,7 @@ export interface WorkerInitMetadata { } export interface WorkerRunnerDebugOptions { - browser: string + browser?: string port: number host: string } diff --git a/packages/worker-legacy/src/index.ts b/packages/worker-legacy/src/index.ts index a139b7f..2118508 100644 --- a/packages/worker-legacy/src/index.ts +++ b/packages/worker-legacy/src/index.ts @@ -95,6 +95,15 @@ export async function initVitest( } : {}, } + if (typeof data.debug === 'object') { + const inspect = `${data.debug.host}:${data.debug.port}` + if (data.debug.browser) { + cliOptions.inspect = inspect + } + else { + cliOptions.inspectBrk = inspect + } + } const vitest = await vitestModule.createVitest( 'test', cliOptions, @@ -150,16 +159,9 @@ export async function initVitest( // Enable printConsoleTrace for inline console log display context.project.config.printConsoleTrace = true - const options = context.project.config.browser - if (options?.enabled && typeof data.debug === 'object') { + const browser = context.project.config.browser + if (browser?.enabled && typeof data.debug === 'object') { context.project.config.setupFiles.push(meta.setupFilePaths.browserDebugLegacy) - context.vitest.config.inspector = { - enabled: true, - port: data.debug.port, - host: data.debug.host, - waitForDebugger: false, - } - context.project.config.inspector = context.vitest.config.inspector } }, }, diff --git a/packages/worker-legacy/src/reporter.ts b/packages/worker-legacy/src/reporter.ts index 2f6e659..2d2f6d6 100644 --- a/packages/worker-legacy/src/reporter.ts +++ b/packages/worker-legacy/src/reporter.ts @@ -77,7 +77,7 @@ export class VSCodeReporter implements Reporter { }) const __vscode_waitForDebugger: BrowserCommand<[]> = () => { return new Promise((resolve, reject) => { - ExtensionWorker.emitter.on('onBrowserDebug', (fullfilled) => { + ExtensionWorker.emitter.on('onDebugAttached', (fullfilled) => { if (fullfilled) { resolve() } @@ -120,7 +120,7 @@ export class VSCodeReporter implements Reporter { // If parsing fails, continue without parsed location } } - this.rpc.onConsoleLog(extendedLog) + return this.rpc.onConsoleLog(extendedLog) } private logPromises = new Set>() diff --git a/packages/worker-legacy/src/worker.ts b/packages/worker-legacy/src/worker.ts index 1e3650c..95185ce 100644 --- a/packages/worker-legacy/src/worker.ts +++ b/packages/worker-legacy/src/worker.ts @@ -352,8 +352,8 @@ export class ExtensionWorker implements ExtensionWorkerTransport { // ignore } - onBrowserDebug(fulfilled: boolean) { - ExtensionWorker.emitter.emit('onBrowserDebug', fulfilled) + onDebugAttached(fulfilled: boolean) { + ExtensionWorker.emitter.emit('onDebugAttached', fulfilled) } // TODO:(?) -- if environments are supported diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts index 0579c9d..5e51992 100644 --- a/packages/worker/src/index.ts +++ b/packages/worker/src/index.ts @@ -60,9 +60,6 @@ export async function initVitest( reporter: undefined, ui: false, includeTaskLocation: true, - inspect: typeof data.debug === 'object' - ? `${data.debug.host}:${data.debug.port}` - : undefined, experimental: { importDurations: { limit: Infinity, @@ -71,6 +68,15 @@ export async function initVitest( }, }, } + if (typeof data.debug === 'object') { + const inspect = `${data.debug.host}:${data.debug.port}` + if (data.debug.browser) { + cliOptions.inspect = inspect + } + else { + cliOptions.inspectBrk = inspect + } + } const vitest = await vitestModule.createVitest( 'test', cliOptions, diff --git a/packages/worker/src/reporter.ts b/packages/worker/src/reporter.ts index 1373ca5..fe0ea7d 100644 --- a/packages/worker/src/reporter.ts +++ b/packages/worker/src/reporter.ts @@ -40,7 +40,7 @@ export class VSCodeReporter implements Reporter { onInit(vitest: VitestCore) { this.vitest = vitest - this.configureBrowserDebugging(vitest) + this.configureAttachDebugging(vitest) vitest.projects.forEach((project) => { this.ensureSetupFileIsAllowed(project.vite.config) @@ -57,27 +57,7 @@ export class VSCodeReporter implements Reporter { this.ensureSetupFileIsAllowed(config) const __vscode_waitForDebugger: BrowserCommand<[]> = () => { - return new Promise((resolve, reject) => { - if (this.debuggerAttached !== undefined) { - if (this.debuggerAttached) { - resolve() - return - } - else if (this.debuggerAttached === false) { - reject(new Error(`Browser Debugger failed to connect.`)) - return - } - } - - ExtensionWorker.emitter.on('onBrowserDebug', (fullfilled) => { - if (fullfilled) { - resolve() - } - else { - reject(new Error(`Browser Debugger failed to connect.`)) - } - }) - }) + return this.createAttachPromise() } // TODO: move this command init to configureVitest when Vitest 4 is out // @ts-expect-error private "parent" property @@ -112,7 +92,7 @@ export class VSCodeReporter implements Reporter { // If parsing fails, continue without parsed location } } - this.rpc.onConsoleLog(extendedLog) + return this.rpc.onConsoleLog(extendedLog) } onTaskUpdate(packs: RunnerTaskResultPack[]) { @@ -179,8 +159,32 @@ export class VSCodeReporter implements Reporter { config.execArgv.push(...this.execArgv) } - configureBrowserDebugging(vitest: VitestCore) { - ExtensionWorker.emitter.on('onBrowserDebug', (fullfilled) => { + private createAttachPromise() { + return new Promise((resolve, reject) => { + if (this.debuggerAttached !== undefined) { + if (this.debuggerAttached) { + resolve() + return + } + else if (this.debuggerAttached === false) { + reject(new Error(`Browser Debugger failed to connect.`)) + return + } + } + + ExtensionWorker.emitter.on('onDebugAttached', (fullfilled) => { + if (fullfilled) { + resolve() + } + else { + reject(new Error(`Browser Debugger failed to connect.`)) + } + }) + }) + } + + configureAttachDebugging(vitest: VitestCore) { + ExtensionWorker.emitter.on('onDebugAttached', (fullfilled) => { this.debuggerAttached = fullfilled }) diff --git a/packages/worker/src/worker.ts b/packages/worker/src/worker.ts index 6cd4822..61e5d62 100644 --- a/packages/worker/src/worker.ts +++ b/packages/worker/src/worker.ts @@ -133,7 +133,7 @@ export class ExtensionWorker implements ExtensionWorkerTransport { return await this.vitest.experimental_getSourceModuleDiagnostic(moduleId) } - onBrowserDebug(fulfilled: boolean) { - ExtensionWorker.emitter.emit('onBrowserDebug', fulfilled) + onDebugAttached(fulfilled: boolean) { + ExtensionWorker.emitter.emit('onDebugAttached', fulfilled) } } -- 2.51.2 From caee48696c1eb8d826eebd6a8dfa7da14274757c Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 10 Mar 2026 14:13:33 +0100 Subject: [PATCH 08/64] chore: release v1.46.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0172812..3043c43 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "name": "explorer", "displayName": "Vitest", "type": "commonjs", - "version": "1.45.0", + "version": "1.46.0", "packageManager": "pnpm@10.11.1", "description": "A Vite-native testing framework. It's fast!", "author": "Vitest Team", -- 2.51.2 From 90a4f3ff8cca9ccb6daf07d8af4617af6f8b2e5b Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 10 Mar 2026 15:41:51 +0100 Subject: [PATCH 09/64] feat: add an option to enable watch on startup (#745) --- README.md | 1 + package.json | 18 +++++++++++ packages/extension/src/config.ts | 2 ++ packages/extension/src/extension.ts | 48 +++++++++++++++++++++++------ packages/extension/src/runQueue.ts | 26 ++++++++++++++++ 5 files changed, 85 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 0a32ba2..486ea5a 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ These options are resolved relative to the [workspace file](https://code.visuals - `vitest.debuggerAddress`: TCP/IP address of process to be debugged. Default: localhost - `vitest.cliArguments`: Additional arguments to pass to the Vitest CLI. Note that some arguments will be ignored: `watch`, `reporter`, `api`, and `ui`. Example: `--mode=staging` - `vitest.showImportsDuration`: Show how long it took to import and transform the modules. When hovering, the extension provides more diagnostics. +- `vitest.watchOnStartup`: Keep Vitest server running in the background at all times automatically on startup, rerunning tests when files change (default: `false`). This is the same as enabling continuous run. > 💡 The `vitest.nodeExecutable` and `vitest.nodeExecArgs` settings are used as `execPath` and `execArgv` when spawning a new `child_process`, and as `runtimeExecutable` and `runtimeArgs` when [debugging a test](https://github.com/microsoft/vscode-js-debug/blob/main/OPTIONS.md). > The `vitest.terminalShellPath` and `vitest.terminalShellArgs` settings are used as `shellPath` and `shellArgs` when creating a new [terminal](https://code.visualstudio.com/api/references/vscode-api#Terminal) diff --git a/package.json b/package.json index 3043c43..a6cf2cf 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,11 @@ "command": "vitest.updateSnapshot", "category": "Vitest" }, + { + "title": "Toggle Continuous Run", + "command": "vitest.toggleContinuousRun", + "category": "Vitest" + }, { "title": "Show Output Channel", "command": "vitest.openOutput", @@ -134,6 +139,10 @@ } ], "commandPalette": [ + { + "command": "vitest.toggleContinuousRun", + "when": "false" + }, { "command": "vitest.updateSnapshot", "when": "false" @@ -161,6 +170,10 @@ { "command": "vitest.copyTestItemErrors", "when": "controllerId == 'vitest'" + }, + { + "command": "vitest.toggleContinuousRun", + "when": "controllerId == 'vitest'" } ] }, @@ -335,6 +348,11 @@ "type": "string", "default": "auto", "enum": ["auto", "node", "deno"] + }, + "vitest.watchOnStartup": { + "description": "Watch every test file after the extension is loaded. This is the same as enabling continuous run.", + "type": "boolean", + "default": false } } } diff --git a/packages/extension/src/config.ts b/packages/extension/src/config.ts index e302b4b..482d8ba 100644 --- a/packages/extension/src/config.ts +++ b/packages/extension/src/config.ts @@ -73,6 +73,7 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) { const showInlineConsoleLog = get('showInlineConsoleLog', true) ?? true const forceCancelTimeout = get('forceCancelTimeout', 1000) ?? 1000 const runtime = get<'node' | 'deno' | 'auto'>('runtime', 'auto') ?? 'auto' + const watchOnStartup = get('watchOnStartup', false) ?? false return { env: get>('nodeEnv', null), @@ -82,6 +83,7 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) { filesWatcherInclude, runtime, forceCancelTimeout, + watchOnStartup, terminalShellArgs, terminalShellPath, shellType, diff --git a/packages/extension/src/extension.ts b/packages/extension/src/extension.ts index 3c90bde..6aeb6eb 100644 --- a/packages/extension/src/extension.ts +++ b/packages/extension/src/extension.ts @@ -42,7 +42,7 @@ class VitestExtension { private tagsManager: TagsManager private api: VitestAPI | undefined - private runQueues = new Set() + private runQueues = new Map() private state: ExtensionState private disposables: vscode.Disposable[] = [] @@ -139,10 +139,8 @@ class VitestExtension { } this.api = await resolveVitestAPI(workspaces, configs, cancelToken, ({ api: vitest, files }) => { - if (this.state.hasDisabledConfigs()) { - if (this.state.isConfigDisabled(vitest.id)) { - return - } + if (this.state.hasDisabledConfigs() && this.state.isConfigDisabled(vitest.id)) { + return } this.testTree.watchTestFilesInWorkspace(vitest, files) @@ -164,6 +162,16 @@ class VitestExtension { this.testController.items.delete(this.loadingTestItem.id) } + this.api.processes.forEach((process) => { + const config = getConfig(process.workspaceFolder) + if (config.watchOnStartup) { + const profile = this.runProfiles.get(`${process.id}:run`) + if (profile) { + vscode.commands.executeCommand('testing.startContinuousRun', profile) + } + } + }) + // collect tests inside a test file vscode.window.visibleTextEditors.forEach(async (editor) => { const testItems = this.testTree.getFileTestItems(editor.document.uri.fsPath) @@ -211,11 +219,12 @@ class VitestExtension { this.importsBreakdownProvider, this.inlineConsoleLog, ) - this.runQueues.add(runQueue) + const runQueueId = `${vitest.id}:run` + this.runQueues.set(runQueueId, runQueue) runProfile.tag = vitest.tag runProfile.runHandler = (request, token) => runQueue.enqueue(request, token, false) - this.runProfiles.set(`${vitest.id}:run`, runProfile) + this.runProfiles.set(runQueueId, runProfile) let debugProfile = this.runProfiles.get(`${vitest.id}:debug`) if (!debugProfile) { @@ -274,12 +283,13 @@ class VitestExtension { this.importsBreakdownProvider, this.inlineConsoleLog, ) - this.runQueues.add(coverageQueue) + const coverageQueueId = `${vitest.id}:coverage` + this.runQueues.set(coverageQueueId, coverageQueue) coverageProfile.tag = vitest.tag coverageProfile.runHandler = (request, token) => coverageQueue.enqueue(request, token, true) coverageProfile.loadDetailedCoverage = coverageContext.loadDetailedCoverage - this.runProfiles.set(`${vitest.id}:coverage`, coverageProfile) + this.runProfiles.set(coverageQueueId, coverageProfile) } private async resolveTestFile(item?: vscode.TestItem) { @@ -334,6 +344,24 @@ class VitestExtension { vscode.commands.registerCommand('vitest.openOutput', () => { log.openOuput() }), + vscode.commands.registerCommand('vitest.toggleContinuousRun', async (testItem?: vscode.TestItem) => { + if (!testItem) { + return + } + this.api?.processes.forEach((process) => { + const processId = `${process.id}:run` + const runProfile = this.runProfiles.get(processId) + const queue = this.runQueues.get(processId) + if (runProfile && testItem.tags.includes(runProfile.tag!) && queue) { + if (queue.isContinuousTestItem(testItem)) { + vscode.commands.executeCommand('vscode.stopContinuousTestRun', [testItem]) + } + else { + vscode.commands.executeCommand('vscode.startContinuousTestRun', runProfile, [testItem]) + } + } + }) + }), vscode.commands.registerCommand('vitest.revealInTestExplorer', async (uri: vscode.Uri | undefined) => { if (uri === undefined) { uri = vscode.window.activeTextEditor?.document.uri @@ -550,7 +578,7 @@ class VitestExtension { this.schemaProvider.dispose() this.importsBreakdownProvider.dispose() this.inlineConsoleLog.dispose() - this.runProfiles.forEach(profile => profile.dispose()) + this.runProfiles.forEach(p => p.dispose()) this.runProfiles.clear() this.disposables.forEach(d => d.dispose()) this.disposables = [] diff --git a/packages/extension/src/runQueue.ts b/packages/extension/src/runQueue.ts index a372374..20e0447 100644 --- a/packages/extension/src/runQueue.ts +++ b/packages/extension/src/runQueue.ts @@ -37,6 +37,20 @@ export class RunQueue { private readonly inlineConsoleLog: InlineConsoleLogManager, ) {} + public isContinuousTestItem(testItem: vscode.TestItem): boolean { + for (const req of this.continuousRequests) { + if (!req.include) { + return true + } + for (const item of req.include) { + if (includesTestItem(item, testItem)) { + return true + } + } + } + return false + } + async enqueue(request: vscode.TestRunRequest, token: vscode.CancellationToken, coverage: boolean) { if (request.continuous) return this.startContinuousRun(request, token, coverage) @@ -220,6 +234,18 @@ interface ContinuousHandle { dispose: () => Promise } +function includesTestItem(item: vscode.TestItem, testItem: vscode.TestItem): boolean { + if (item === testItem) { + return true + } + for (const [, child] of item.children) { + if (includesTestItem(child, testItem)) { + return true + } + } + return false +} + function getProjectsFromRequest(request: vscode.TestRunRequest): string[] | undefined { const include = request.include if (!include?.length) -- 2.51.2 From 371ea35d27e13b4e2dde73c92038b82176b95111 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 10 Mar 2026 15:59:46 +0100 Subject: [PATCH 10/64] chore: update renovate config (#747) --- .github/renovate.json5 | 20 +- eslint.config.mjs | 2 + package.json | 74 +- packages/extension/src/debug.ts | 2 +- packages/extension/src/extension.ts | 2 +- packages/extension/src/runner.ts | 26 +- packages/extension/src/testTree.ts | 6 +- packages/extension/src/utils.ts | 2 +- packages/extension/src/worker/index.ts | 2 +- packages/shared/package.json | 2 +- packages/shared/src/utils.ts | 2 +- packages/worker-legacy/src/watcher.ts | 10 +- packages/worker/src/reporter.ts | 2 +- packages/worker/src/watcher.ts | 2 +- packages/worker/src/worker.ts | 2 +- pnpm-lock.yaml | 1572 ++++++++++++++---------- pnpm-workspace.yaml | 44 +- samples/basic-v4/package.json | 2 +- 18 files changed, 1042 insertions(+), 732 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 5be40c6..5b432df 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -4,20 +4,22 @@ "labels": ["dependencies"], "rangeStrategy": "bump", "packageRules": [ + { + "groupName": "Eslint packages", + "matchPackageNames": ["/eslint/"] + }, { "depTypeList": ["peerDependencies"], "enabled": false }, { - "matchPaths": [ - "samples/**" - ], - "matchUpdateTypes": [ - "minor", - "patch" - ], - "groupName": "all non-major examples dependencies", - "groupSlug": "all-minor-patch-examples" + "matchDepTypes": ["action"], + "matchPackageNames": ["!actions/{/,}**", "!github/{/,}**"], + "pinDigests": true + }, + { + "matchFileNames": ["samples/**"], + "enabled": false } ] } diff --git a/eslint.config.mjs b/eslint.config.mjs index 455246e..17ee58c 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -23,6 +23,8 @@ export default antfu( 'antfu/indent-binary-ops': 'off', 'unused-imports/no-unused-imports': 'error', 'curly': 'off', + 'e18e/prefer-static-regex': 'off', + 'pnpm/yaml-no-duplicate-catalog-item': 'off', 'style/member-delimiter-style': [ 'error', { diff --git a/package.json b/package.json index a6cf2cf..69ff2a4 100644 --- a/package.json +++ b/package.json @@ -374,45 +374,45 @@ "lint:fix": "eslint --cache --fix ." }, "devDependencies": { - "@antfu/eslint-config": "^4.14.1", - "@playwright/test": "^1.42.1", - "@types/chai": "^5.2.2", - "@types/micromatch": "^4.0.6", - "@types/mocha": "^10.0.6", - "@types/node": "^24.0.0", - "@types/prompts": "^2.4.9", - "@types/semver": "^7.3.9", + "@antfu/eslint-config": "catalog:", + "@playwright/test": "catalog:", + "@types/chai": "catalog:", + "@types/micromatch": "catalog:", + "@types/mocha": "catalog:", + "@types/node": "catalog:", + "@types/prompts": "catalog:", + "@types/semver": "catalog:", "@types/vscode": "^1.77.0", - "@types/which": "^3.0.3", - "@types/ws": "^8.5.10", - "@vscode/test-cli": "^0.0.6", - "@vscode/test-electron": "^2.3.9", - "@vscode/vsce": "^3.1.0", - "@vue/reactivity": "^3.2.33", - "acorn": "^8.12.0", - "acorn-walk": "^8.3.3", - "birpc": "^2.4.0", - "bumpp": "^10.1.1", - "chai": "^5.1.0", - "changelogithub": "^13.15.0", - "eslint": "^9.7.0", - "execa": "^8.0.1", - "find-up": "^7.0.0", - "get-port": "^6.1.2", - "istanbul-to-vscode": "^2.1.0", - "micromatch": "^4.0.5", - "mighty-promise": "^0.0.8", - "mocha": "^10.3.0", - "pathe": "^1.1.2", - "picocolors": "^1.0.0", - "prompts": "^2.4.2", - "semver": "^7.3.5", - "tsdown": "^0.20.3", - "tsx": "^4.7.1", - "typescript": "^5.6.2", + "@types/which": "catalog:", + "@types/ws": "catalog:", + "@vscode/test-cli": "catalog:", + "@vscode/test-electron": "catalog:", + "@vscode/vsce": "catalog:", + "@vue/reactivity": "catalog:", + "acorn": "catalog:", + "acorn-walk": "catalog:", + "birpc": "catalog:", + "bumpp": "catalog:", + "chai": "catalog:", + "changelogithub": "catalog:", + "eslint": "catalog:", + "execa": "catalog:", + "find-up": "catalog:", + "get-port": "catalog:", + "istanbul-to-vscode": "catalog:", + "micromatch": "catalog:", + "mighty-promise": "catalog:", + "mocha": "catalog:", + "pathe": "catalog:", + "picocolors": "catalog:", + "prompts": "catalog:", + "semver": "catalog:", + "tsdown": "catalog:", + "tsx": "catalog:", + "typescript": "catalog:", "vitest": "catalog:latest", - "which": "^4.0.0", - "ws": "^8.16.0" + "which": "catalog:", + "ws": "catalog:" }, "lint-staged": { "*.{js,ts,tsx,vue,md}": [ diff --git a/packages/extension/src/debug.ts b/packages/extension/src/debug.ts index a1f7a07..62568f5 100644 --- a/packages/extension/src/debug.ts +++ b/packages/extension/src/debug.ts @@ -107,7 +107,7 @@ export async function debugTests( if (debugManager.sessions.size) { await Promise.all( - [...debugManager.sessions].map(session => vscode.debug.stopDebugging(session)), + Array.from(debugManager.sessions, session => vscode.debug.stopDebugging(session)), ).catch((error) => { log.error('[DEBUG] Failed to stop debugging sessions', error) }) diff --git a/packages/extension/src/extension.ts b/packages/extension/src/extension.ts index 6aeb6eb..a2e5d09 100644 --- a/packages/extension/src/extension.ts +++ b/packages/extension/src/extension.ts @@ -122,7 +122,7 @@ class VitestExtension { } const folders = new Set([...workspaces, ...configs].map(x => x.folder)) - this.testTree.reset(Array.from(folders)) + this.testTree.reset([...folders]) const previousRunProfiles = this.runProfiles this.runProfiles = new Map() diff --git a/packages/extension/src/runner.ts b/packages/extension/src/runner.ts index daed2b3..6b6e1cb 100644 --- a/packages/extension/src/runner.ts +++ b/packages/extension/src/runner.ts @@ -373,7 +373,7 @@ export class ContinuousTestRunner extends TestRunner { return } - const include = [...this.continuousRequests].map(r => r.include || []).flat() + const include = Array.from(this.continuousRequests, r => r.include || []).flat() if (!include.length) { await this.handle.rpc.watchTests() @@ -415,7 +415,7 @@ export class ContinuousTestRunner extends TestRunner { const run = this.createCancellableTestRun(request, name) for (const file of files) { - if (file[file.length - 1] === '/') { + if (file.at(-1) === '/') { const files = this.getTestFilesInFolder(file) this.startTestRun(files, request) continue @@ -468,9 +468,7 @@ export class ContinuousTestRunner extends TestRunner { private getTestFilesInFolder(path: string) { const folder = this.tree.getOrCreateFolderTestItem(this.api, path) const items = this.tree.getFolderFiles(folder) - return Array.from( - new Set(items.map(item => (getTestData(item) as TestFile).filepath)), - ) + return [...new Set(items.map(item => (getTestData(item) as TestFile).filepath))] } // It is important to create new requests every time the file is changed, @@ -578,15 +576,13 @@ function getTestFiles(tests: readonly vscode.TestItem[]): string[] | ExtensionTe // if there is a folder, we can't limit the tests to a specific project const hasFolder = tests.some(test => getTestData(test) instanceof TestFolder) if (hasFolder) { - return Array.from( - new Set(tests.map((test) => { - const data = getTestData(test) - const fsPath = normalize(test.uri!.fsPath) - if (data instanceof TestFolder) - return `${fsPath}/` - return fsPath - }).filter(Boolean) as string[]), - ) + return [...new Set(tests.map((test) => { + const data = getTestData(test) + const fsPath = normalize(test.uri!.fsPath) + if (data instanceof TestFolder) + return `${fsPath}/` + return fsPath + }).filter(Boolean) as string[])] } const testSpecs: ExtensionTestSpecification[] = [] const testFiles = new Set() @@ -611,7 +607,7 @@ function formatTestPattern(tests: readonly vscode.TestItem[], patterns: string[] const data = getTestData(test)! // file or a folder, try to include every test in there if (!('getTestNamePattern' in data)) { - formatTestPattern([...test.children].map(t => t[1]), patterns) + formatTestPattern(Array.from(test.children, t => t[1]), patterns) continue } patterns.push(data.getTestNamePattern()) diff --git a/packages/extension/src/testTree.ts b/packages/extension/src/testTree.ts index ace97ad..5924e9b 100644 --- a/packages/extension/src/testTree.ts +++ b/packages/extension/src/testTree.ts @@ -51,7 +51,7 @@ export class TestTree extends vscode.Disposable { } public getAllFileItems() { - return Array.from(this.fileItems.values()) + return [...this.fileItems.values()] } public reset(workspaceFolders: vscode.WorkspaceFolder[]) { @@ -183,7 +183,7 @@ export class TestTree extends vscode.Disposable { vscode.commands.executeCommand( 'setContext', 'vitest.testFiles', - Array.from(this.testFiles), + [...this.testFiles], ) return testFileItem @@ -360,7 +360,7 @@ export class TestTree extends vscode.Disposable { task.name, parent.uri, ) - testItem.tags = Array.from(new Set([...parent.tags, tag])) + testItem.tags = [...new Set([...parent.tags, tag])] testItem.error = undefined testItem.label = task.name const location = task.location diff --git a/packages/extension/src/utils.ts b/packages/extension/src/utils.ts index 4a06f73..78a41b9 100644 --- a/packages/extension/src/utils.ts +++ b/packages/extension/src/utils.ts @@ -40,7 +40,7 @@ export function debounce void>(cb: T, wait = 20) { const callable = (...args: any) => { if (h) clearTimeout(h) - h = setTimeout(() => cb(...args), wait) + h = setTimeout(cb, wait, ...args) } return (callable) } diff --git a/packages/extension/src/worker/index.ts b/packages/extension/src/worker/index.ts index 51f79bf..3aaf2cc 100644 --- a/packages/extension/src/worker/index.ts +++ b/packages/extension/src/worker/index.ts @@ -32,7 +32,7 @@ emitter.on('message', async function onMessage(message: any) { try { const vitestModule = await import( - pathToFileURL(normalizeDriveLetter(data.meta.vitestNodePath)).toString() + pathToFileURL(normalizeDriveLetter(data.meta.vitestNodePath)).toString(), ) as typeof import('vitest/node') const isLegacy = !vitestModule.version || (Number(vitestModule.version[0]) < 4) diff --git a/packages/shared/package.json b/packages/shared/package.json index 016241d..b879aaa 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -7,7 +7,7 @@ ".": "./src/index.ts" }, "devDependencies": { - "birpc": "2.4.0", + "birpc": "catalog:", "vitest": "catalog:v3" } } diff --git a/packages/shared/src/utils.ts b/packages/shared/src/utils.ts index d06573a..116fed1 100644 --- a/packages/shared/src/utils.ts +++ b/packages/shared/src/utils.ts @@ -91,7 +91,7 @@ export function createQueuedHandler(resolver: (value: T[]) => Promise, function flush() { if (promise) return - const values = Array.from(cached) + const values = [...cached] cached.clear() const resolvers = pendingResolvers pendingResolvers = [] diff --git a/packages/worker-legacy/src/watcher.ts b/packages/worker-legacy/src/watcher.ts index c92d2bd..19dfd0d 100644 --- a/packages/worker-legacy/src/watcher.ts +++ b/packages/worker-legacy/src/watcher.ts @@ -31,7 +31,7 @@ export class ExtensionWorkerWatcher { return await originalScheduleRerun.call(this, []) } - await state.collectTests(files, Array.from(this.changedTests)) + await state.collectTests(files, [...this.changedTests]) return await originalScheduleRerun.call(this, []) } @@ -46,11 +46,11 @@ export class ExtensionWorkerWatcher { return await originalScheduleRerun.call(this, files) } - const changedFiles = Array.from(this.changedTests) + const changedFiles = [...this.changedTests] if (!isTestFileTrigger) { // if souce code is changed and related tests are not continious, remove them from changedTests - const currentChanged = Array.from(this.changedTests) + const currentChanged = [...this.changedTests] this.changedTests.clear() for (const file of currentChanged) { if (state.isTestFileWatched(file)) @@ -65,7 +65,7 @@ export class ExtensionWorkerWatcher { if (this.changedTests.size) { vitest.logger.log( 'Rerunning tests due to file changes:', - ...[...this.changedTests].map(f => relative(vitest.config.root, f)), + ...Array.from(this.changedTests, f => relative(vitest.config.root, f)), namePattern ? `with pattern ${namePattern}` : '', ) } @@ -103,7 +103,7 @@ export class ExtensionWorkerWatcher { return this.files.some((file) => { if (file === testFile) return true - if (file[file.length - 1] === '/') + if (file.at(-1) === '/') return testFile.startsWith(file) return false }) diff --git a/packages/worker/src/reporter.ts b/packages/worker/src/reporter.ts index fe0ea7d..2b29e69 100644 --- a/packages/worker/src/reporter.ts +++ b/packages/worker/src/reporter.ts @@ -117,7 +117,7 @@ export class VSCodeReporter implements Reporter { onTestRunStart(specifications: ReadonlyArray) { const files = specifications.map(spec => spec.moduleId) - this.rpc.onTestRunStart(Array.from(new Set(files))) + this.rpc.onTestRunStart([...new Set(files)]) this.vitest.state.filesMap.clear() } diff --git a/packages/worker/src/watcher.ts b/packages/worker/src/watcher.ts index 5dafd56..5c19e7b 100644 --- a/packages/worker/src/watcher.ts +++ b/packages/worker/src/watcher.ts @@ -80,7 +80,7 @@ export class ExtensionWorkerWatcher { return files.some((file) => { if (file === testFile) return true - if (file[file.length - 1] === '/') + if (file.at(-1) === '/') return testFile.startsWith(file) return false }) diff --git a/packages/worker/src/worker.ts b/packages/worker/src/worker.ts index 61e5d62..fa32a1c 100644 --- a/packages/worker/src/worker.ts +++ b/packages/worker/src/worker.ts @@ -103,7 +103,7 @@ export class ExtensionWorker implements ExtensionWorkerTransport { } return { name: project.name, - environments: Array.from(environments).map(([name, { timestamp }]) => ({ + environments: Array.from(environments, ([name, { timestamp }]) => ({ name, transformTimestamp: timestamp, })), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d122a9f..7dea54f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,118 @@ settings: excludeLinksFromLockfile: false catalogs: + default: + '@antfu/eslint-config': + specifier: ^7.7.0 + version: 7.7.0 + '@playwright/test': + specifier: ^1.42.1 + version: 1.57.0 + '@types/chai': + specifier: ^5.2.2 + version: 5.2.3 + '@types/micromatch': + specifier: ^4.0.6 + version: 4.0.10 + '@types/mocha': + specifier: ^10.0.6 + version: 10.0.10 + '@types/node': + specifier: ^24.0.0 + version: 24.10.1 + '@types/prompts': + specifier: ^2.4.9 + version: 2.4.9 + '@types/semver': + specifier: ^7.3.9 + version: 7.7.1 + '@types/which': + specifier: ^3.0.3 + version: 3.0.4 + '@types/ws': + specifier: ^8.5.10 + version: 8.18.1 + '@vscode/test-cli': + specifier: ^0.0.6 + version: 0.0.6 + '@vscode/test-electron': + specifier: ^2.3.9 + version: 2.5.2 + '@vscode/vsce': + specifier: ^3.1.0 + version: 3.7.1 + '@vue/reactivity': + specifier: ^3.2.33 + version: 3.5.25 + acorn: + specifier: ^8.12.0 + version: 8.16.0 + acorn-walk: + specifier: ^8.3.3 + version: 8.3.4 + birpc: + specifier: 2.4.0 + version: 2.4.0 + bumpp: + specifier: ^10.1.1 + version: 10.3.2 + chai: + specifier: ^5.1.0 + version: 5.3.3 + changelogithub: + specifier: ^13.15.0 + version: 13.16.1 + eslint: + specifier: ^10.0.3 + version: 10.0.3 + execa: + specifier: ^8.0.1 + version: 8.0.1 + find-up: + specifier: ^7.0.0 + version: 7.0.0 + get-port: + specifier: ^6.1.2 + version: 6.1.2 + istanbul-to-vscode: + specifier: ^2.1.0 + version: 2.1.1 + micromatch: + specifier: ^4.0.5 + version: 4.0.8 + mighty-promise: + specifier: ^0.0.8 + version: 0.0.8 + mocha: + specifier: ^10.3.0 + version: 10.8.2 + pathe: + specifier: ^1.1.2 + version: 1.1.2 + picocolors: + specifier: ^1.0.0 + version: 1.1.1 + prompts: + specifier: ^2.4.2 + version: 2.4.2 + semver: + specifier: ^7.3.5 + version: 7.7.4 + tsdown: + specifier: ^0.20.3 + version: 0.20.3 + tsx: + specifier: ^4.7.1 + version: 4.21.0 + typescript: + specifier: ^5.6.2 + version: 5.9.3 + which: + specifier: ^4.0.0 + version: 4.0.0 + ws: + specifier: ^8.16.0 + version: 8.19.0 latest: '@types/picomatch': specifier: ^4.0.2 @@ -15,6 +127,9 @@ catalogs: '@vitest/browser-playwright': specifier: ^4.1.0-beta.3 version: 4.1.0-beta.3 + '@vitest/coverage-istanbul': + specifier: ^4.1.0-beta.3 + version: 4.1.0-beta.6 '@vitest/coverage-v8': specifier: ^4.1.0-beta.3 version: 4.1.0-beta.3 @@ -55,122 +170,122 @@ importers: .: devDependencies: '@antfu/eslint-config': - specifier: ^4.14.1 - version: 4.19.0(@vue/compiler-sfc@3.5.25)(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)(vitest@4.1.0-beta.3) + specifier: 'catalog:' + version: 7.7.0(@typescript-eslint/rule-tester@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(@typescript-eslint/typescript-estree@8.57.0(typescript@5.9.3))(@typescript-eslint/utils@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(@vue/compiler-sfc@3.5.25)(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)(vitest@4.1.0-beta.3) '@playwright/test': - specifier: ^1.42.1 + specifier: 'catalog:' version: 1.57.0 '@types/chai': - specifier: ^5.2.2 + specifier: 'catalog:' version: 5.2.3 '@types/micromatch': - specifier: ^4.0.6 + specifier: 'catalog:' version: 4.0.10 '@types/mocha': - specifier: ^10.0.6 + specifier: 'catalog:' version: 10.0.10 '@types/node': - specifier: ^24.0.0 + specifier: 'catalog:' version: 24.10.1 '@types/prompts': - specifier: ^2.4.9 + specifier: 'catalog:' version: 2.4.9 '@types/semver': - specifier: ^7.3.9 + specifier: 'catalog:' version: 7.7.1 '@types/vscode': specifier: ^1.77.0 version: 1.106.1 '@types/which': - specifier: ^3.0.3 + specifier: 'catalog:' version: 3.0.4 '@types/ws': - specifier: ^8.5.10 + specifier: 'catalog:' version: 8.18.1 '@vscode/test-cli': - specifier: ^0.0.6 + specifier: 'catalog:' version: 0.0.6 '@vscode/test-electron': - specifier: ^2.3.9 + specifier: 'catalog:' version: 2.5.2 '@vscode/vsce': - specifier: ^3.1.0 + specifier: 'catalog:' version: 3.7.1 '@vue/reactivity': - specifier: ^3.2.33 + specifier: 'catalog:' version: 3.5.25 acorn: - specifier: ^8.12.0 - version: 8.15.0 + specifier: 'catalog:' + version: 8.16.0 acorn-walk: - specifier: ^8.3.3 + specifier: 'catalog:' version: 8.3.4 birpc: - specifier: ^2.4.0 + specifier: 'catalog:' version: 2.4.0 bumpp: - specifier: ^10.1.1 + specifier: 'catalog:' version: 10.3.2(magicast@0.3.5) chai: - specifier: ^5.1.0 + specifier: 'catalog:' version: 5.3.3 changelogithub: - specifier: ^13.15.0 + specifier: 'catalog:' version: 13.16.1(magicast@0.3.5) eslint: - specifier: ^9.7.0 - version: 9.39.1(jiti@2.6.1) + specifier: 'catalog:' + version: 10.0.3(jiti@2.6.1) execa: - specifier: ^8.0.1 + specifier: 'catalog:' version: 8.0.1 find-up: - specifier: ^7.0.0 + specifier: 'catalog:' version: 7.0.0 get-port: - specifier: ^6.1.2 + specifier: 'catalog:' version: 6.1.2 istanbul-to-vscode: - specifier: ^2.1.0 + specifier: 'catalog:' version: 2.1.1 micromatch: - specifier: ^4.0.5 + specifier: 'catalog:' version: 4.0.8 mighty-promise: - specifier: ^0.0.8 + specifier: 'catalog:' version: 0.0.8 mocha: - specifier: ^10.3.0 + specifier: 'catalog:' version: 10.8.2 pathe: - specifier: ^1.1.2 + specifier: 'catalog:' version: 1.1.2 picocolors: - specifier: ^1.0.0 + specifier: 'catalog:' version: 1.1.1 prompts: - specifier: ^2.4.2 + specifier: 'catalog:' version: 2.4.2 semver: - specifier: ^7.3.5 - version: 7.7.3 + specifier: 'catalog:' + version: 7.7.4 tsdown: - specifier: ^0.20.3 - version: 0.20.3(synckit@0.11.11)(typescript@5.9.3) + specifier: 'catalog:' + version: 0.20.3(synckit@0.11.12)(typescript@5.9.3) tsx: - specifier: ^4.7.1 + specifier: 'catalog:' version: 4.21.0 typescript: - specifier: ^5.6.2 + specifier: 'catalog:' version: 5.9.3 vitest: specifier: catalog:latest version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) which: - specifier: ^4.0.0 + specifier: 'catalog:' version: 4.0.0 ws: - specifier: ^8.16.0 - version: 8.18.3 + specifier: 'catalog:' + version: 8.19.0 packages/extension: dependencies: @@ -196,7 +311,7 @@ importers: packages/shared: devDependencies: birpc: - specifier: 2.4.0 + specifier: 'catalog:' version: 2.4.0 vitest: specifier: catalog:v3 @@ -267,8 +382,8 @@ importers: specifier: catalog:latest version: 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) '@vitest/coverage-istanbul': - specifier: ^4.0.18 - version: 4.0.18(vitest@4.1.0-beta.3) + specifier: catalog:latest + version: 4.1.0-beta.6(vitest@4.1.0-beta.3) '@vitest/coverage-v8': specifier: catalog:latest version: 4.1.0-beta.3(@vitest/browser@4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3))(vitest@4.1.0-beta.3) @@ -481,20 +596,24 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@antfu/eslint-config@4.19.0': - resolution: {integrity: sha512-IQlML0cc7qNA1Uk55raMRZjOmh26rkX3bi2MFYjhO+VOtTQt8Mz2ngxBlIwpTgZFgfuYjle6JPuOuALnEZHDFw==} + '@antfu/eslint-config@7.7.0': + resolution: {integrity: sha512-lkxb84o8z4v1+me51XlrHHF6zvOZfvTu6Y11t6h6v17JSMl9yoNHwC0Sqp/NfMTHie/LGgjyXOupXpQCXxfs1Q==} hasBin: true peerDependencies: - '@eslint-react/eslint-plugin': ^1.38.4 - '@next/eslint-plugin-next': ^15.4.0-canary.115 + '@angular-eslint/eslint-plugin': ^21.1.0 + '@angular-eslint/eslint-plugin-template': ^21.1.0 + '@angular-eslint/template-parser': ^21.1.0 + '@eslint-react/eslint-plugin': ^2.11.0 + '@next/eslint-plugin-next': '>=15.0.0' '@prettier/plugin-xml': ^3.4.1 '@unocss/eslint-plugin': '>=0.50.0' astro-eslint-parser: ^1.0.2 - eslint: ^9.10.0 + eslint: ^9.10.0 || ^10.0.0 eslint-plugin-astro: ^1.2.0 eslint-plugin-format: '>=0.1.0' - eslint-plugin-react-hooks: ^5.2.0 - eslint-plugin-react-refresh: ^0.4.19 + eslint-plugin-jsx-a11y: '>=6.10.2' + eslint-plugin-react-hooks: ^7.0.0 + eslint-plugin-react-refresh: ^0.5.0 eslint-plugin-solid: ^0.14.3 eslint-plugin-svelte: '>=2.35.1' eslint-plugin-vuejs-accessibility: ^2.4.1 @@ -502,6 +621,12 @@ packages: prettier-plugin-slidev: ^1.0.5 svelte-eslint-parser: '>=0.37.0' peerDependenciesMeta: + '@angular-eslint/eslint-plugin': + optional: true + '@angular-eslint/eslint-plugin-template': + optional: true + '@angular-eslint/template-parser': + optional: true '@eslint-react/eslint-plugin': optional: true '@next/eslint-plugin-next': @@ -516,6 +641,8 @@ packages: optional: true eslint-plugin-format: optional: true + eslint-plugin-jsx-a11y: + optional: true eslint-plugin-react-hooks: optional: true eslint-plugin-react-refresh: @@ -606,18 +733,34 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + '@babel/compat-data@7.28.5': resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + '@babel/core@7.28.5': resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} engines: {node: '>=6.9.0'} + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + '@babel/generator@7.28.5': resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + '@babel/generator@8.0.0-rc.1': resolution: {integrity: sha512-3ypWOOiC4AYHKr8vYRVtWtWmyvcoItHtVqF8paFax+ydpmUdPsJpLBkBBs5ItmhdrwC3a0ZSqqFAdzls4ODP3w==} engines: {node: ^20.19.0 || >=22.12.0} @@ -630,6 +773,10 @@ packages: resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + '@babel/helper-globals@7.28.0': resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} @@ -638,12 +785,22 @@ packages: resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + '@babel/helper-module-transforms@7.28.3': resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-plugin-utils@7.27.1': resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} @@ -672,11 +829,20 @@ packages: resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} + '@babel/helpers@7.28.6': + resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + engines: {node: '>=6.9.0'} + '@babel/parser@7.28.5': resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.29.0': + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/parser@8.0.0-rc.1': resolution: {integrity: sha512-6HyyU5l1yK/7h9Ki52i5h6mDAx4qJdiLQO4FdCyJNoB/gy3T3GGJdhQzzbZgvgZCugYBvwtQiWRt94QKedHnkA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -720,14 +886,26 @@ packages: resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + '@babel/traverse@7.28.5': resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + '@babel/types@7.28.5': resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + '@babel/types@8.0.0-rc.1': resolution: {integrity: sha512-ubmJ6TShyaD69VE9DQrlXcdkvJbmwWPB8qYj0H2kaJi29O7vJT9ajSdBd2W8CG34pwL9pYA74fi7RHC1qbLoVQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -743,11 +921,11 @@ packages: resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true - '@clack/core@0.5.0': - resolution: {integrity: sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==} + '@clack/core@1.1.0': + resolution: {integrity: sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA==} - '@clack/prompts@0.11.0': - resolution: {integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==} + '@clack/prompts@1.1.0': + resolution: {integrity: sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g==} '@csstools/color-helpers@5.1.0': resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} @@ -808,6 +986,17 @@ packages: resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} engines: {node: '>=20.19.0'} + '@e18e/eslint-plugin@0.2.0': + resolution: {integrity: sha512-mXgODVwhuDjTJ+UT+XSvmMmCidtGKfrV5nMIv1UtpWex2pYLsIM3RSpT8HWIMAebS9qANbXPKlSX4BE7ZvuCgA==} + peerDependencies: + eslint: ^9.0.0 || ^10.0.0 + oxlint: ^1.41.0 + peerDependenciesMeta: + eslint: + optional: true + oxlint: + optional: true + '@emnapi/core@1.8.1': resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} @@ -817,13 +1006,13 @@ packages: '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} - '@es-joy/jsdoccomment@0.50.2': - resolution: {integrity: sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==} - engines: {node: '>=18'} + '@es-joy/jsdoccomment@0.84.0': + resolution: {integrity: sha512-0xew1CxOam0gV5OMjh2KjFQZsKL2bByX1+q4j3E73MpYIdyUxcZb/xQct9ccUb+ve5KGUYbCUxyPnYB7RbuP+w==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@es-joy/jsdoccomment@0.52.0': - resolution: {integrity: sha512-BXuN7BII+8AyNtn57euU2Yxo9yA/KUDNzrpXyi3pfqKmBhhysR6ZWOebFh3vyPoqA3/j1SOvGgucElMGwlXing==} - engines: {node: '>=20.11.0'} + '@es-joy/resolve.exports@1.2.0': + resolution: {integrity: sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==} + engines: {node: '>=10'} '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} @@ -1149,14 +1338,14 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-plugin-eslint-comments@4.5.0': - resolution: {integrity: sha512-MAhuTKlr4y/CE3WYX26raZjy+I/kS2PLKSzvfmDCGrBLTFHOYwqROZdr4XwPgXwX3K9rjzMr4pSmUWGnzsUyMg==} + '@eslint-community/eslint-plugin-eslint-comments@4.7.1': + resolution: {integrity: sha512-Ql2nJFwA8wUGpILYGOQaT1glPsmvEwE0d+a+l7AALLzQvInqdbXJdx7aSu0DpUX9dB1wMVBMhm99/++S3MdEtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 - '@eslint-community/eslint-utils@4.9.0': - resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -1165,55 +1354,47 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/compat@1.4.1': - resolution: {integrity: sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/compat@2.0.3': + resolution: {integrity: sha512-SjIJhGigp8hmd1YGIBwh7Ovri7Kisl42GYFjrOyHhtfYGGoLW6teYi/5p8W50KSsawUPpuLOSmsq1bD0NGQLBw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} peerDependencies: - eslint: ^8.40 || 9 + eslint: ^8.40 || 9 || 10 peerDependenciesMeta: eslint: optional: true - '@eslint/config-array@0.21.1': - resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/config-helpers@0.4.2': - resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/config-array@0.23.3': + resolution: {integrity: sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/core@0.15.2': - resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/config-helpers@0.5.3': + resolution: {integrity: sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/core@0.17.0': resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.3.3': - resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/js@9.39.1': - resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@1.1.1': + resolution: {integrity: sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/markdown@7.5.1': resolution: {integrity: sha512-R8uZemG9dKTbru/DQRPblbJyXpObwKzo8rv1KYGGuPUPtjM4LXBYM9q5CIZAComzZupws3tWbDwam5AFpPLyJQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.7': - resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/plugin-kit@0.3.5': - resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/object-schema@3.0.3': + resolution: {integrity: sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/plugin-kit@0.4.1': resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/plugin-kit@0.6.1': + resolution: {integrity: sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + '@exodus/bytes@1.11.0': resolution: {integrity: sha512-wO3vd8nsEHdumsXrjGO/v4p6irbg7hy9kvIeR6i2AwylZSk4HJdWgL0FNaVquW1+AweJcdvU1IEpuIWk/WaPnA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -1243,14 +1424,6 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@isaacs/balanced-match@4.0.1': - resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} - engines: {node: 20 || >=22} - - '@isaacs/brace-expansion@5.0.0': - resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} - engines: {node: 20 || >=22} - '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -1293,6 +1466,10 @@ packages: '@one-ini/wasm@0.1.1': resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + '@ota-meshi/ast-token-store@0.3.0': + resolution: {integrity: sha512-XRO0zi2NIUKq2lUk3T1ecFSld1fMWRKE6naRFGkgkdeosx7IslyUKNv5Dcb5PJTja9tHJoFu0v/7yEpAkrkrTg==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + '@oxc-project/types@0.112.0': resolution: {integrity: sha512-m6RebKHIRsax2iCwVpYW2ErQwa4ywHJrE4sCK3/8JK8ZZAWOKXaRJFl/uP51gaVyyXlaS4+chU1nSCdzYf6QqQ==} @@ -1560,6 +1737,10 @@ packages: resolution: {integrity: sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==} engines: {node: '>=20.0.0'} + '@sindresorhus/base62@1.0.0': + resolution: {integrity: sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==} + engines: {node: '>=18'} + '@sindresorhus/merge-streams@2.3.0': resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} @@ -1571,11 +1752,11 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@stylistic/eslint-plugin@5.6.1': - resolution: {integrity: sha512-JCs+MqoXfXrRPGbGmho/zGS/jMcn3ieKl/A8YImqib76C8kjgZwq5uUFzc30lJkMvcchuRn6/v8IApLxli3Jyw==} + '@stylistic/eslint-plugin@5.10.0': + resolution: {integrity: sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: '>=9.0.0' + eslint: ^9.0.0 || ^10.0.0 '@testing-library/dom@10.4.1': resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} @@ -1631,6 +1812,9 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/esrecurse@4.3.1': + resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -1709,63 +1893,69 @@ packages: '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@typescript-eslint/eslint-plugin@8.48.0': - resolution: {integrity: sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ==} + '@typescript-eslint/eslint-plugin@8.57.0': + resolution: {integrity: sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.48.0 - eslint: ^8.57.0 || ^9.0.0 + '@typescript-eslint/parser': ^8.57.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.48.0': - resolution: {integrity: sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==} + '@typescript-eslint/parser@8.57.0': + resolution: {integrity: sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.48.0': - resolution: {integrity: sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==} + '@typescript-eslint/project-service@8.57.0': + resolution: {integrity: sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.48.0': - resolution: {integrity: sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==} + '@typescript-eslint/rule-tester@8.57.0': + resolution: {integrity: sha512-qs4OapXmAIj3so85/20lQG1WrBSSvDE/3b42Orl3lpZkaOlNXtbfKzL+9EPaY5wSEgdlhKEpympAMFHPG9i72Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + + '@typescript-eslint/scope-manager@8.57.0': + resolution: {integrity: sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.48.0': - resolution: {integrity: sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==} + '@typescript-eslint/tsconfig-utils@8.57.0': + resolution: {integrity: sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.48.0': - resolution: {integrity: sha512-zbeVaVqeXhhab6QNEKfK96Xyc7UQuoFWERhEnj3mLVnUWrQnv15cJNseUni7f3g557gm0e46LZ6IJ4NJVOgOpw==} + '@typescript-eslint/type-utils@8.57.0': + resolution: {integrity: sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.48.0': - resolution: {integrity: sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==} + '@typescript-eslint/types@8.57.0': + resolution: {integrity: sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.48.0': - resolution: {integrity: sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==} + '@typescript-eslint/typescript-estree@8.57.0': + resolution: {integrity: sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.48.0': - resolution: {integrity: sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ==} + '@typescript-eslint/utils@8.57.0': + resolution: {integrity: sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.48.0': - resolution: {integrity: sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==} + '@typescript-eslint/visitor-keys@8.57.0': + resolution: {integrity: sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typespec/ts-http-runtime@0.3.2': @@ -1809,10 +1999,10 @@ packages: peerDependencies: vitest: 4.1.0-beta.3 - '@vitest/coverage-istanbul@4.0.18': - resolution: {integrity: sha512-0OhjP30owEDihYTZGWuq20rNtV1RjjJs1Mv4MaZIKcFBmiLUXX7HJLX4fU7wE+Mrc3lQxI2HKq6WrSXi5FGuCQ==} + '@vitest/coverage-istanbul@4.1.0-beta.6': + resolution: {integrity: sha512-HYfxxux7y/U9Qo3pi0n2AwjTVIIexHoOtGOSeT3jhRea6CxrAUHZsxzZQDZnnqx85yNO9gSH0t3ConfdODuFQQ==} peerDependencies: - vitest: 4.0.18 + vitest: 4.1.0-beta.6 '@vitest/coverage-v8@3.2.4': resolution: {integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==} @@ -1832,8 +2022,8 @@ packages: '@vitest/browser': optional: true - '@vitest/eslint-plugin@1.5.1': - resolution: {integrity: sha512-t49CNERe/YadnLn90NTTKJLKzs99xBkXElcoUTLodG6j1G0Q7jy3mXqqiHd3N5aryG2KkgOg4UAoGwgwSrZqKQ==} + '@vitest/eslint-plugin@1.6.10': + resolution: {integrity: sha512-/cOf+mTu4HBJIYHTETo8/OFCSZv3T2p+KfGnouzKfjK063cWLZp0TzvK7EU5B3eFG7ypUNtw6l+jK+SA+p1g8g==} engines: {node: '>=18'} peerDependencies: eslint: '>=8.57.0' @@ -2009,8 +2199,8 @@ packages: resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} engines: {node: '>=0.4.0'} - acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} engines: {node: '>=0.4.0'} hasBin: true @@ -2018,8 +2208,8 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} @@ -2118,6 +2308,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -2160,6 +2354,10 @@ packages: brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@5.0.4: + resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} + engines: {node: 18 || 20 || >=22} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -2222,6 +2420,10 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -2234,10 +2436,6 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - camelcase@6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} @@ -2363,8 +2561,8 @@ packages: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} - comment-parser@1.4.1: - resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} + comment-parser@1.4.5: + resolution: {integrity: sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw==} engines: {node: '>= 12.0.0'} concat-map@0.0.1: @@ -2529,9 +2727,9 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - diff-sequences@27.5.1: - resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} diff@5.2.0: resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} @@ -2817,19 +3015,13 @@ packages: peerDependencies: eslint: '>=6.0.0' - eslint-compat-utils@0.6.5: - resolution: {integrity: sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ==} - engines: {node: '>=12'} + eslint-config-flat-gitignore@2.2.1: + resolution: {integrity: sha512-wA5EqN0era7/7Gt5Botlsfin/UNY0etJSEeBgbUlFLFrBi47rAN//+39fI7fpYcl8RENutlFtvp/zRa/M/pZNg==} peerDependencies: - eslint: '>=6.0.0' + eslint: ^9.5.0 || ^10.0.0 - eslint-config-flat-gitignore@2.1.0: - resolution: {integrity: sha512-cJzNJ7L+psWp5mXM7jBX+fjHtBvvh06RBlcweMhKD8jWqQw0G78hOW5tpVALGHGFPsBV+ot2H+pdDGJy6CV8pA==} - peerDependencies: - eslint: ^9.5.0 - - eslint-flat-config-utils@2.1.4: - resolution: {integrity: sha512-bEnmU5gqzS+4O+id9vrbP43vByjF+8KOs+QuuV4OlqAuXmnRW2zfI/Rza1fQvdihQ5h4DUo0NqFAiViD4mSrzQ==} + eslint-flat-config-utils@3.0.2: + resolution: {integrity: sha512-mPvevWSDQFwgABvyCurwIu6ZdKxGI5NW22/BGDwA1T49NO6bXuxbV9VfJK/tkQoNyPogT6Yu1d57iM0jnZVWmg==} eslint-json-compat-utils@0.2.1: resolution: {integrity: sha512-YzEodbDyW8DX8bImKhAcCeu/L31Dd/70Bidx2Qex9OFUtgzXLqtfWL4Hr5fM/aCCB8QUZLuJur0S9k6UfgFkfg==} @@ -2847,46 +3039,50 @@ packages: peerDependencies: eslint: '*' - eslint-plugin-antfu@3.1.1: - resolution: {integrity: sha512-7Q+NhwLfHJFvopI2HBZbSxWXngTwBLKxW1AGXLr2lEGxcEIK/AsDs8pn8fvIizl5aZjBbVbVK5ujmMpBe4Tvdg==} + eslint-plugin-antfu@3.2.2: + resolution: {integrity: sha512-Qzixht2Dmd/pMbb5EnKqw2V8TiWHbotPlsORO8a+IzCLFwE0RxK8a9k4DCTFPzBwyxJzH+0m2Mn8IUGeGQkyUw==} peerDependencies: eslint: '*' - eslint-plugin-command@3.3.1: - resolution: {integrity: sha512-fBVTXQ2y48TVLT0+4A6PFINp7GcdIailHAXbvPBixE7x+YpYnNQhFZxTdvnb+aWk+COgNebQKen/7m4dmgyWAw==} + eslint-plugin-command@3.5.2: + resolution: {integrity: sha512-PA59QAkQDwvcCMEt5lYLJLI3zDGVKJeC4id/pcRY2XdRYhSGW7iyYT1VC1N3bmpuvu6Qb/9QptiS3GJMjeGTJg==} peerDependencies: + '@typescript-eslint/rule-tester': '*' + '@typescript-eslint/typescript-estree': '*' + '@typescript-eslint/utils': '*' eslint: '*' + eslint-plugin-depend@1.5.0: + resolution: {integrity: sha512-i3UeLYmclf1Icp35+6W7CR4Bp2PIpDgBuf/mpmXK5UeLkZlvYJ21VuQKKHHAIBKRTPivPGX/gZl5JGno1o9Y0A==} + peerDependencies: + eslint: '>=8.40.0' + eslint-plugin-es-x@7.8.0: resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: eslint: '>=8' - eslint-plugin-import-lite@0.3.0: - resolution: {integrity: sha512-dkNBAL6jcoCsXZsQ/Tt2yXmMDoNt5NaBh/U7yvccjiK8cai6Ay+MK77bMykmqQA2bTF6lngaLCDij6MTO3KkvA==} + eslint-plugin-import-lite@0.5.2: + resolution: {integrity: sha512-XvfdWOC5dSLEI9krIPRlNmKSI2ViIE9pVylzfV9fCq0ZpDaNeUk6o0wZv0OzN83QdadgXp1NsY0qjLINxwYCsw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: '>=9.0.0' - typescript: '>=4.5' - peerDependenciesMeta: - typescript: - optional: true - eslint-plugin-jsdoc@51.4.1: - resolution: {integrity: sha512-y4CA9OkachG8v5nAtrwvcvjIbdcKgSyS6U//IfQr4FZFFyeBFwZFf/tfSsMr46mWDJgidZjBTqoCRlXywfFBMg==} - engines: {node: '>=20.11.0'} + eslint-plugin-jsdoc@62.7.1: + resolution: {integrity: sha512-4Zvx99Q7d1uggYBUX/AIjvoyqXhluGbbKrRmG8SQTLprPFg6fa293tVJH1o1GQwNe3lUydd8ZHzn37OaSncgSQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} peerDependencies: - eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 - eslint-plugin-jsonc@2.21.0: - resolution: {integrity: sha512-HttlxdNG5ly3YjP1cFMP62R4qKLxJURfBZo2gnMY+yQojZxkLyOpY1H1KRTKBmvQeSG9pIpSGEhDjE17vvYosg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-plugin-jsonc@3.1.1: + resolution: {integrity: sha512-7TSQO8ZyvOuXWb0sYke3KUSh0DJA4/QviKfuzD3/Cy3XDjtrIrTWQbjb7j/Yy2l/DgwuM+lCS2c/jqJifv5jhg==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} peerDependencies: - eslint: '>=6.0.0' + eslint: '>=9.38.0' - eslint-plugin-n@17.23.1: - resolution: {integrity: sha512-68PealUpYoHOBh332JLLD9Sj7OQUDkFpmcfqt8R9sySfFSeuGJjMTJQvCRRB96zO3A/PELRLkPrzsHmzEFQQ5A==} + eslint-plugin-n@17.24.0: + resolution: {integrity: sha512-/gC7/KAYmfNnPNOb3eu8vw+TdVnV0zhdQwexsw6FLXbhzroVj20vRn2qL8lDWDGnAQ2J8DhdfvXxX9EoxvERvw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: '>=8.23.0' @@ -2895,51 +3091,51 @@ packages: resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==} engines: {node: '>=5.0.0'} - eslint-plugin-perfectionist@4.15.1: - resolution: {integrity: sha512-MHF0cBoOG0XyBf7G0EAFCuJJu4I18wy0zAoT1OHfx2o6EOx1EFTIzr2HGeuZa1kDcusoX0xJ9V7oZmaeFd773Q==} - engines: {node: ^18.0.0 || >=20.0.0} + eslint-plugin-perfectionist@5.6.0: + resolution: {integrity: sha512-pxrLrfRp5wl1Vol1fAEa/G5yTXxefTPJjz07qC7a8iWFXcOZNuWBItMQ2OtTzfQIvMq6bMyYcrzc3Wz++na55Q==} + engines: {node: ^20.0.0 || >=22.0.0} peerDependencies: - eslint: '>=8.45.0' + eslint: ^8.45.0 || ^9.0.0 || ^10.0.0 - eslint-plugin-pnpm@1.3.0: - resolution: {integrity: sha512-Lkdnj3afoeUIkDUu8X74z60nrzjQ2U55EbOeI+qz7H1He4IO4gmUKT2KQIl0It52iMHJeuyLDWWNgjr6UIK8nw==} + eslint-plugin-pnpm@1.6.0: + resolution: {integrity: sha512-dxmt9r3zvPaft6IugS4i0k16xag3fTbOvm/road5uV9Y8qUCQT0xzheSh3gMlYAlC6vXRpfArBDsTZ7H7JKCbg==} peerDependencies: - eslint: ^9.0.0 + eslint: ^9.0.0 || ^10.0.0 - eslint-plugin-regexp@2.10.0: - resolution: {integrity: sha512-ovzQT8ESVn5oOe5a7gIDPD5v9bCSjIFJu57sVPDqgPRXicQzOnYfFN21WoQBQF18vrhT5o7UMKFwJQVVjyJ0ng==} - engines: {node: ^18 || >=20} + eslint-plugin-regexp@3.1.0: + resolution: {integrity: sha512-qGXIC3DIKZHcK1H9A9+Byz9gmndY6TTSRkSMTZpNXdyCw2ObSehRgccJv35n9AdUakEjQp5VFNLas6BMXizCZg==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} peerDependencies: - eslint: '>=8.44.0' + eslint: '>=9.38.0' - eslint-plugin-toml@0.12.0: - resolution: {integrity: sha512-+/wVObA9DVhwZB1nG83D2OAQRrcQZXy+drqUnFJKymqnmbnbfg/UPmEMCKrJNcEboUGxUjYrJlgy+/Y930mURQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-plugin-toml@1.3.1: + resolution: {integrity: sha512-1l00fBP03HIt9IPV7ZxBi7x0y0NMdEZmakL1jBD6N/FoKBvfKxPw5S8XkmzBecOnFBTn5Z8sNJtL5vdf9cpRMQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} peerDependencies: - eslint: '>=6.0.0' + eslint: '>=9.38.0' - eslint-plugin-unicorn@60.0.0: - resolution: {integrity: sha512-QUzTefvP8stfSXsqKQ+vBQSEsXIlAiCduS/V1Em+FKgL9c21U/IIm20/e3MFy1jyCf14tHAhqC1sX8OTy6VUCg==} + eslint-plugin-unicorn@63.0.0: + resolution: {integrity: sha512-Iqecl9118uQEXYh7adylgEmGfkn5es3/mlQTLLkd4pXkIk9CTGrAbeUux+YljSa2ohXCBmQQ0+Ej1kZaFgcfkA==} engines: {node: ^20.10.0 || >=21.0.0} peerDependencies: - eslint: '>=9.29.0' + eslint: '>=9.38.0' - eslint-plugin-unused-imports@4.3.0: - resolution: {integrity: sha512-ZFBmXMGBYfHttdRtOG9nFFpmUvMtbHSjsKrS20vdWdbfiVYsO3yA2SGYy9i9XmZJDfMGBflZGBCm70SEnFQtOA==} + eslint-plugin-unused-imports@4.4.1: + resolution: {integrity: sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==} peerDependencies: '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0 - eslint: ^9.0.0 || ^8.0.0 + eslint: ^10.0.0 || ^9.0.0 || ^8.0.0 peerDependenciesMeta: '@typescript-eslint/eslint-plugin': optional: true - eslint-plugin-vue@10.6.2: - resolution: {integrity: sha512-nA5yUs/B1KmKzvC42fyD0+l9Yd+LtEpVhWRbXuDj0e+ZURcTtyRbMDWUeJmTAh2wC6jC83raS63anNM2YT3NPw==} + eslint-plugin-vue@10.8.0: + resolution: {integrity: sha512-f1J/tcbnrpgC8suPN5AtdJ5MQjuXbSU9pGRSSYAuF3SHoiYCOdEX6O22pLaRyLHXvDcOe+O5ENgc1owQ587agA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@stylistic/eslint-plugin': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 '@typescript-eslint/parser': ^7.0.0 || ^8.0.0 - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 vue-eslint-parser: ^10.0.0 peerDependenciesMeta: '@stylistic/eslint-plugin': @@ -2947,11 +3143,11 @@ packages: '@typescript-eslint/parser': optional: true - eslint-plugin-yml@1.19.0: - resolution: {integrity: sha512-S+4GbcCWksFKAvFJtf0vpdiCkZZvDJCV4Zsi9ahmYkYOYcf+LRqqzvzkb/ST7vTYV6sFwXOvawzYyL/jFT2nQA==} - engines: {node: ^14.17.0 || >=16.0.0} + eslint-plugin-yml@3.3.1: + resolution: {integrity: sha512-isntsZchaTqDMNNkD+CakrgA/pdUoJ45USWBKpuqfAW1MCuw731xX/vrXfoJFZU3tTFr24nCbDYmDfT2+g4QtQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} peerDependencies: - eslint: '>=6.0.0' + eslint: '>=9.38.0' eslint-processor-vue-blocks@2.0.0: resolution: {integrity: sha512-u4W0CJwGoWY3bjXAuFpc/b6eK3NQEI8MoeW7ritKj3G3z/WtHrKjkqf+wk8mPEy5rlMGS+k6AZYOw2XBoN/02Q==} @@ -2959,9 +3155,9 @@ packages: '@vue/compiler-sfc': ^3.3.0 eslint: '>=9.0.0' - eslint-scope@8.4.0: - resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-scope@9.1.2: + resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} @@ -2971,9 +3167,13 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.39.1: - resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@10.0.3: + resolution: {integrity: sha512-COV33RzXZkqhG9P2rZCFl9ZmJ7WL+gQSCRzE7RhkbclbQPtLAWReL7ysA0Sh4c8Im2U9ynybdR56PV0XcKvqaQ==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: jiti: '*' @@ -2985,17 +3185,17 @@ packages: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + espree@11.2.0: + resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} hasBin: true - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -3202,9 +3402,6 @@ packages: resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} engines: {node: '>=18'} - get-tsconfig@4.13.0: - resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} - get-tsconfig@4.13.6: resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} @@ -3250,10 +3447,6 @@ packages: engines: {node: '>=12'} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} - engines: {node: '>=18'} - globals@15.15.0: resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} engines: {node: '>=18'} @@ -3262,6 +3455,10 @@ packages: resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} engines: {node: '>=18'} + globals@17.4.0: + resolution: {integrity: sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==} + engines: {node: '>=18'} + globby@14.1.0: resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} engines: {node: '>=18'} @@ -3276,9 +3473,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - happy-dom@14.7.1: resolution: {integrity: sha512-v60Q0evZ4clvMcrAh5/F8EdxDdfHdFrtffz/CNe10jKD+nFweZVxM91tW+UyY2L4AtpgIaXdZ7TQmiO1pfcwbg==} engines: {node: '>=16.0.0'} @@ -3336,6 +3530,9 @@ packages: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -3409,10 +3606,6 @@ packages: immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - import-without-cache@0.2.5: resolution: {integrity: sha512-B6Lc2s6yApwnD2/pMzFh/d5AVjdsDXjgkeJ766FmFuJELIGHNycKRj+l3A39yZPM4CchqNCB4RITEAYB1KUM6A==} engines: {node: '>=20.19.0'} @@ -3605,10 +3798,6 @@ packages: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} - istanbul-lib-instrument@6.0.3: - resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} - engines: {node: '>=10'} - istanbul-lib-report@3.0.1: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} @@ -3669,13 +3858,9 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - jsdoc-type-pratt-parser@4.1.0: - resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==} - engines: {node: '>=12.0.0'} - - jsdoc-type-pratt-parser@4.8.0: - resolution: {integrity: sha512-iZ8Bdb84lWRuGHamRXFyML07r21pcwBrLkHEuHgEY5UbCouBwv7ECknDRKzsQIXMiqpPymqtIf8TC/shYKB5rw==} - engines: {node: '>=12.0.0'} + jsdoc-type-pratt-parser@7.1.1: + resolution: {integrity: sha512-/2uqY7x6bsrpi3i9LVU6J89352C0rpMk0as8trXxCtvd4kPk1ke/Eyif6wqfSLvoNJqcDG9Vk4UsXgygzCt2xA==} + engines: {node: '>=20.0.0'} jsdom@24.1.3: resolution: {integrity: sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ==} @@ -3695,11 +3880,6 @@ packages: canvas: optional: true - jsesc@3.0.2: - resolution: {integrity: sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==} - engines: {node: '>=6'} - hasBin: true - jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -3722,9 +3902,9 @@ packages: engines: {node: '>=6'} hasBin: true - jsonc-eslint-parser@2.4.1: - resolution: {integrity: sha512-uuPNLJkKN8NXAlZlQ6kmUF9qO+T6Kyd7oV4+/7yy8Jz6+MZNyhPq8EdLpdfnPVzUC8qSf1b4j1azKaGnFsjmsw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + jsonc-eslint-parser@3.1.0: + resolution: {integrity: sha512-75EA7EWZExL/j+MDKQrRbdzcRI2HOkRlmUw8fZJc1ioqFEOvBsq7Rt+A6yCxOt9w/TYNpkt52gC6nm/g5tFIng==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} @@ -3843,10 +4023,6 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.2.4: - resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} - engines: {node: 20 || >=22} - lru-cache@11.2.6: resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} engines: {node: 20 || >=22} @@ -3875,6 +4051,9 @@ packages: magicast@0.5.1: resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==} + magicast@0.5.2: + resolution: {integrity: sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -4066,9 +4245,9 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} - minimatch@10.1.1: - resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} - engines: {node: 20 || >=22} + minimatch@10.2.4: + resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} + engines: {node: 18 || 20 || >=22} minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} @@ -4120,6 +4299,9 @@ packages: engines: {node: '>= 14.0.0'} hasBin: true + module-replacements@2.11.0: + resolution: {integrity: sha512-j5sNQm3VCpQQ7nTqGeOZtoJtV3uKERgCBm9QRhmGRiXiqkf7iRFOkfxdJRZWLkqYY8PNf4cDQF/WfXUYLENrRA==} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -4220,6 +4402,9 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + object-deep-merge@2.0.0: + resolution: {integrity: sha512-3DC3UMpeffLTHiuXSy/UG4NOIYTLlY9u3V82+djSCLYClWobZiS4ivYzpIUWrRY/nfsJ8cWsKyG3QfyLePmhvg==} + object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -4304,10 +4489,6 @@ packages: pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - parse-cache-control@1.0.1: resolution: {integrity: sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==} @@ -4440,8 +4621,8 @@ packages: resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} engines: {node: '>=14.19.0'} - pnpm-workspace-yaml@1.3.0: - resolution: {integrity: sha512-Krb5q8Totd5mVuLx7we+EFHq/AfxA75nbfTm25Q1pIf606+RlaKUG+PXH8SDihfe5b5k4H09gE+sL47L1t5lbw==} + pnpm-workspace-yaml@1.6.0: + resolution: {integrity: sha512-uUy4dK3E11sp7nK+hnT7uAWfkBMe00KaUw8OG3NuNlYQoTk4sc9pcdIy1+XIP85v9Tvr02mK3JPaNNrP0QyRaw==} possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} @@ -4458,6 +4639,7 @@ packages: prebuild-install@7.1.3: resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} engines: {node: '>=10'} + deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. hasBin: true prelude-ls@1.2.1: @@ -4592,8 +4774,8 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} - regjsparser@0.12.0: - resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} + regjsparser@0.13.0: + resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} hasBin: true require-directory@2.1.1: @@ -4607,9 +4789,9 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} + reserved-identifiers@1.2.0: + resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==} + engines: {node: '>=18'} resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -4712,8 +4894,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.3: - resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} engines: {node: '>=10'} hasBin: true @@ -4916,8 +5098,8 @@ packages: sync-rpc@1.3.6: resolution: {integrity: sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==} - synckit@0.11.11: - resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} + synckit@0.11.12: + resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} engines: {node: ^14.18.0 || >=16.0.0} table@6.9.0: @@ -4938,6 +5120,7 @@ packages: tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me terminal-link@4.0.0: resolution: {integrity: sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA==} @@ -5007,9 +5190,13 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - toml-eslint-parser@0.10.0: - resolution: {integrity: sha512-khrZo4buq4qVmsGzS5yQjKe/WsFvV8fGfOjDQN0q4iy9FjRfPWRgTFrU8u1R2iu/SfWLhY9WnCi4Jhdrcbtg+g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + to-valid-identifier@1.0.0: + resolution: {integrity: sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==} + engines: {node: '>=20'} + + toml-eslint-parser@1.0.3: + resolution: {integrity: sha512-A5F0cM6+mDleacLIEUkmfpkBbnHJFV1d2rprHU2MXNk7mlxHq2zGojA+SRvQD1RoMo9gqjZPWEaKG4v1BQ48lw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} @@ -5038,8 +5225,8 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - ts-api-utils@2.1.0: - resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + ts-api-utils@2.4.0: + resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' @@ -5127,10 +5314,6 @@ packages: resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==} engines: {node: '>=14.0'} - undici@7.16.0: - resolution: {integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==} - engines: {node: '>=20.18.1'} - undici@7.22.0: resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==} engines: {node: '>=20.18.1'} @@ -5333,11 +5516,11 @@ packages: vue-component-type-helpers@2.2.12: resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==} - vue-eslint-parser@10.2.0: - resolution: {integrity: sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==} + vue-eslint-parser@10.4.0: + resolution: {integrity: sha512-Vxi9pJdbN3ZnVGLODVtZ7y4Y2kzAAE2Cm0CZ3ZDRvydVYxZ6VrnBhLikBsRS+dpwj4Jv4UCv21PTEwF5rQ9WXg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 vue@3.5.25: resolution: {integrity: sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==} @@ -5495,9 +5678,9 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - yaml-eslint-parser@1.3.1: - resolution: {integrity: sha512-MdSgP9YA9QjtAO2+lt4O7V2bnH22LPnfeVLiQqjY3cOyn8dy/Ief8otjIe6SPPTK03nM7O3Yl0LTfWuF7l+9yw==} - engines: {node: ^14.17.0 || >=16.0.0} + yaml-eslint-parser@2.0.0: + resolution: {integrity: sha512-h0uDm97wvT2bokfwwTmY6kJ1hp6YDFL0nRHwNKz8s/VD1FH/vvZjAKoMUE+un0eaYBSG7/c6h+lJTP+31tjgTw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} yaml@2.8.2: resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} @@ -5572,48 +5755,52 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@antfu/eslint-config@4.19.0(@vue/compiler-sfc@3.5.25)(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)(vitest@4.1.0-beta.3)': + '@antfu/eslint-config@7.7.0(@typescript-eslint/rule-tester@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(@typescript-eslint/typescript-estree@8.57.0(typescript@5.9.3))(@typescript-eslint/utils@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(@vue/compiler-sfc@3.5.25)(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)(vitest@4.1.0-beta.3)': dependencies: '@antfu/install-pkg': 1.1.0 - '@clack/prompts': 0.11.0 - '@eslint-community/eslint-plugin-eslint-comments': 4.5.0(eslint@9.39.1(jiti@2.6.1)) + '@clack/prompts': 1.1.0 + '@e18e/eslint-plugin': 0.2.0(eslint@10.0.3(jiti@2.6.1)) + '@eslint-community/eslint-plugin-eslint-comments': 4.7.1(eslint@10.0.3(jiti@2.6.1)) '@eslint/markdown': 7.5.1 - '@stylistic/eslint-plugin': 5.6.1(eslint@9.39.1(jiti@2.6.1)) - '@typescript-eslint/eslint-plugin': 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@vitest/eslint-plugin': 1.5.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)(vitest@4.1.0-beta.3) + '@stylistic/eslint-plugin': 5.10.0(eslint@10.0.3(jiti@2.6.1)) + '@typescript-eslint/eslint-plugin': 8.57.0(@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) + '@vitest/eslint-plugin': 1.6.10(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)(vitest@4.1.0-beta.3) ansis: 4.2.0 - cac: 6.7.14 - eslint: 9.39.1(jiti@2.6.1) - eslint-config-flat-gitignore: 2.1.0(eslint@9.39.1(jiti@2.6.1)) - eslint-flat-config-utils: 2.1.4 - eslint-merge-processors: 2.0.0(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-antfu: 3.1.1(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-command: 3.3.1(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-import-lite: 0.3.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - eslint-plugin-jsdoc: 51.4.1(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-jsonc: 2.21.0(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-n: 17.23.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + cac: 7.0.0 + eslint: 10.0.3(jiti@2.6.1) + eslint-config-flat-gitignore: 2.2.1(eslint@10.0.3(jiti@2.6.1)) + eslint-flat-config-utils: 3.0.2 + eslint-merge-processors: 2.0.0(eslint@10.0.3(jiti@2.6.1)) + eslint-plugin-antfu: 3.2.2(eslint@10.0.3(jiti@2.6.1)) + eslint-plugin-command: 3.5.2(@typescript-eslint/rule-tester@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(@typescript-eslint/typescript-estree@8.57.0(typescript@5.9.3))(@typescript-eslint/utils@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1)) + eslint-plugin-import-lite: 0.5.2(eslint@10.0.3(jiti@2.6.1)) + eslint-plugin-jsdoc: 62.7.1(eslint@10.0.3(jiti@2.6.1)) + eslint-plugin-jsonc: 3.1.1(eslint@10.0.3(jiti@2.6.1)) + eslint-plugin-n: 17.24.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) eslint-plugin-no-only-tests: 3.3.0 - eslint-plugin-perfectionist: 4.15.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - eslint-plugin-pnpm: 1.3.0(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-regexp: 2.10.0(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-toml: 0.12.0(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-unicorn: 60.0.0(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-unused-imports: 4.3.0(@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)) - eslint-plugin-vue: 10.6.2(@stylistic/eslint-plugin@5.6.1(eslint@9.39.1(jiti@2.6.1)))(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@2.6.1))) - eslint-plugin-yml: 1.19.0(eslint@9.39.1(jiti@2.6.1)) - eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.25)(eslint@9.39.1(jiti@2.6.1)) - globals: 16.5.0 - jsonc-eslint-parser: 2.4.1 + eslint-plugin-perfectionist: 5.6.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) + eslint-plugin-pnpm: 1.6.0(eslint@10.0.3(jiti@2.6.1)) + eslint-plugin-regexp: 3.1.0(eslint@10.0.3(jiti@2.6.1)) + eslint-plugin-toml: 1.3.1(eslint@10.0.3(jiti@2.6.1)) + eslint-plugin-unicorn: 63.0.0(eslint@10.0.3(jiti@2.6.1)) + eslint-plugin-unused-imports: 4.4.1(@typescript-eslint/eslint-plugin@8.57.0(@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1)) + eslint-plugin-vue: 10.8.0(@stylistic/eslint-plugin@5.10.0(eslint@10.0.3(jiti@2.6.1)))(@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@10.0.3(jiti@2.6.1))) + eslint-plugin-yml: 3.3.1(eslint@10.0.3(jiti@2.6.1)) + eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.25)(eslint@10.0.3(jiti@2.6.1)) + globals: 17.4.0 local-pkg: 1.1.2 parse-gitignore: 2.0.0 - toml-eslint-parser: 0.10.0 - vue-eslint-parser: 10.2.0(eslint@9.39.1(jiti@2.6.1)) - yaml-eslint-parser: 1.3.1 + toml-eslint-parser: 1.0.3 + vue-eslint-parser: 10.4.0(eslint@10.0.3(jiti@2.6.1)) + yaml-eslint-parser: 2.0.0 transitivePeerDependencies: - '@eslint/json' + - '@typescript-eslint/rule-tester' + - '@typescript-eslint/typescript-estree' + - '@typescript-eslint/utils' - '@vue/compiler-sfc' + - oxlint - supports-color - typescript - vitest @@ -5749,8 +5936,16 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/compat-data@7.28.5': {} + '@babel/compat-data@7.29.0': {} + '@babel/core@7.28.5': dependencies: '@babel/code-frame': 7.27.1 @@ -5771,6 +5966,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.28.6 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3(supports-color@8.1.1) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/generator@7.28.5': dependencies: '@babel/parser': 7.28.5 @@ -5779,6 +5994,14 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + '@babel/generator@8.0.0-rc.1': dependencies: '@babel/parser': 8.0.0-rc.1 @@ -5800,6 +6023,14 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.0 + lru-cache: 5.1.1 + semver: 6.3.1 + '@babel/helper-globals@7.28.0': {} '@babel/helper-module-imports@7.27.1': @@ -5809,6 +6040,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 @@ -5818,6 +6056,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + '@babel/helper-plugin-utils@7.27.1': {} '@babel/helper-string-parser@7.27.1': {} @@ -5835,10 +6082,19 @@ snapshots: '@babel/template': 7.27.2 '@babel/types': 7.28.5 + '@babel/helpers@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + '@babel/parser@7.28.5': dependencies: '@babel/types': 7.28.5 + '@babel/parser@7.29.0': + dependencies: + '@babel/types': 7.29.0 + '@babel/parser@8.0.0-rc.1': dependencies: '@babel/types': 8.0.0-rc.1 @@ -5884,6 +6140,12 @@ snapshots: '@babel/parser': 7.28.5 '@babel/types': 7.28.5 + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@babel/traverse@7.28.5': dependencies: '@babel/code-frame': 7.27.1 @@ -5896,11 +6158,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@8.0.0-rc.1': dependencies: '@babel/helper-string-parser': 8.0.0-rc.1 @@ -5914,15 +6193,13 @@ snapshots: dependencies: css-tree: 3.1.0 - '@clack/core@0.5.0': + '@clack/core@1.1.0': dependencies: - picocolors: 1.1.1 sisteransi: 1.0.5 - '@clack/prompts@0.11.0': + '@clack/prompts@1.1.0': dependencies: - '@clack/core': 0.5.0 - picocolors: 1.1.1 + '@clack/core': 1.1.0 sisteransi: 1.0.5 '@csstools/color-helpers@5.1.0': {} @@ -5967,6 +6244,12 @@ snapshots: '@csstools/css-tokenizer@4.0.0': {} + '@e18e/eslint-plugin@0.2.0(eslint@10.0.3(jiti@2.6.1))': + dependencies: + eslint-plugin-depend: 1.5.0(eslint@10.0.3(jiti@2.6.1)) + optionalDependencies: + eslint: 10.0.3(jiti@2.6.1) + '@emnapi/core@1.8.1': dependencies: '@emnapi/wasi-threads': 1.1.0 @@ -5983,21 +6266,15 @@ snapshots: tslib: 2.8.1 optional: true - '@es-joy/jsdoccomment@0.50.2': + '@es-joy/jsdoccomment@0.84.0': dependencies: '@types/estree': 1.0.8 - '@typescript-eslint/types': 8.48.0 - comment-parser: 1.4.1 - esquery: 1.6.0 - jsdoc-type-pratt-parser: 4.1.0 + '@typescript-eslint/types': 8.57.0 + comment-parser: 1.4.5 + esquery: 1.7.0 + jsdoc-type-pratt-parser: 7.1.1 - '@es-joy/jsdoccomment@0.52.0': - dependencies: - '@types/estree': 1.0.8 - '@typescript-eslint/types': 8.48.0 - comment-parser: 1.4.1 - esquery: 1.6.0 - jsdoc-type-pratt-parser: 4.1.0 + '@es-joy/resolve.exports@1.2.0': {} '@esbuild/aix-ppc64@0.25.12': optional: true @@ -6161,60 +6438,44 @@ snapshots: '@esbuild/win32-x64@0.27.0': optional: true - '@eslint-community/eslint-plugin-eslint-comments@4.5.0(eslint@9.39.1(jiti@2.6.1))': + '@eslint-community/eslint-plugin-eslint-comments@4.7.1(eslint@10.0.3(jiti@2.6.1))': dependencies: escape-string-regexp: 4.0.0 - eslint: 9.39.1(jiti@2.6.1) - ignore: 5.3.2 + eslint: 10.0.3(jiti@2.6.1) + ignore: 7.0.5 - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@2.6.1))': + '@eslint-community/eslint-utils@4.9.1(eslint@10.0.3(jiti@2.6.1))': dependencies: - eslint: 9.39.1(jiti@2.6.1) + eslint: 10.0.3(jiti@2.6.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/compat@1.4.1(eslint@9.39.1(jiti@2.6.1))': + '@eslint/compat@2.0.3(eslint@10.0.3(jiti@2.6.1))': dependencies: - '@eslint/core': 0.17.0 + '@eslint/core': 1.1.1 optionalDependencies: - eslint: 9.39.1(jiti@2.6.1) + eslint: 10.0.3(jiti@2.6.1) - '@eslint/config-array@0.21.1': + '@eslint/config-array@0.23.3': dependencies: - '@eslint/object-schema': 2.1.7 + '@eslint/object-schema': 3.0.3 debug: 4.4.3(supports-color@8.1.1) - minimatch: 3.1.2 + minimatch: 10.2.4 transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.4.2': - dependencies: - '@eslint/core': 0.17.0 - - '@eslint/core@0.15.2': + '@eslint/config-helpers@0.5.3': dependencies: - '@types/json-schema': 7.0.15 + '@eslint/core': 1.1.1 '@eslint/core@0.17.0': dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.3': + '@eslint/core@1.1.1': dependencies: - ajv: 6.12.6 - debug: 4.4.3(supports-color@8.1.1) - espree: 10.4.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/js@9.39.1': {} + '@types/json-schema': 7.0.15 '@eslint/markdown@7.5.1': dependencies: @@ -6230,16 +6491,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/object-schema@2.1.7': {} + '@eslint/object-schema@3.0.3': {} - '@eslint/plugin-kit@0.3.5': + '@eslint/plugin-kit@0.4.1': dependencies: - '@eslint/core': 0.15.2 + '@eslint/core': 0.17.0 levn: 0.4.1 - '@eslint/plugin-kit@0.4.1': + '@eslint/plugin-kit@0.6.1': dependencies: - '@eslint/core': 0.17.0 + '@eslint/core': 1.1.1 levn: 0.4.1 '@exodus/bytes@1.11.0': {} @@ -6257,12 +6518,6 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@isaacs/balanced-match@4.0.1': {} - - '@isaacs/brace-expansion@5.0.0': - dependencies: - '@isaacs/balanced-match': 4.0.1 - '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -6314,6 +6569,8 @@ snapshots: '@one-ini/wasm@0.1.1': {} + '@ota-meshi/ast-token-store@0.3.0': {} + '@oxc-project/types@0.112.0': {} '@pkgjs/parseargs@0.11.0': @@ -6523,17 +6780,19 @@ snapshots: '@secretlint/types@10.2.2': {} + '@sindresorhus/base62@1.0.0': {} + '@sindresorhus/merge-streams@2.3.0': {} '@sindresorhus/merge-streams@4.0.0': {} '@standard-schema/spec@1.1.0': {} - '@stylistic/eslint-plugin@5.6.1(eslint@9.39.1(jiti@2.6.1))': + '@stylistic/eslint-plugin@5.10.0(eslint@10.0.3(jiti@2.6.1))': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) - '@typescript-eslint/types': 8.48.0 - eslint: 9.39.1(jiti@2.6.1) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) + '@typescript-eslint/types': 8.57.0 + eslint: 10.0.3(jiti@2.6.1) eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 @@ -6627,6 +6886,8 @@ snapshots: '@types/deep-eql@4.0.2': {} + '@types/esrecurse@4.3.1': {} + '@types/estree@1.0.8': {} '@types/form-data@0.0.33': @@ -6698,97 +6959,110 @@ snapshots: dependencies: '@types/node': 24.10.1 - '@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.57.0(@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.48.0 - '@typescript-eslint/type-utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.48.0 - eslint: 9.39.1(jiti@2.6.1) - graphemer: 1.4.0 + '@typescript-eslint/parser': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.57.0 + '@typescript-eslint/type-utils': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.57.0 + eslint: 10.0.3(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.9.3) + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.48.0 - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.48.0 + '@typescript-eslint/scope-manager': 8.57.0 + '@typescript-eslint/types': 8.57.0 + '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.57.0 debug: 4.4.3(supports-color@8.1.1) - eslint: 9.39.1(jiti@2.6.1) + eslint: 10.0.3(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.48.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.57.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3) - '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@5.9.3) + '@typescript-eslint/types': 8.57.0 debug: 4.4.3(supports-color@8.1.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.48.0': + '@typescript-eslint/rule-tester@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/visitor-keys': 8.48.0 + '@typescript-eslint/parser': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) + ajv: 6.14.0 + eslint: 10.0.3(jiti@2.6.1) + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/scope-manager@8.57.0': + dependencies: + '@typescript-eslint/types': 8.57.0 + '@typescript-eslint/visitor-keys': 8.57.0 - '@typescript-eslint/tsconfig-utils@8.48.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.57.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.57.0 + '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3(supports-color@8.1.1) - eslint: 9.39.1(jiti@2.6.1) - ts-api-utils: 2.1.0(typescript@5.9.3) + eslint: 10.0.3(jiti@2.6.1) + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.48.0': {} + '@typescript-eslint/types@8.57.0': {} - '@typescript-eslint/typescript-estree@8.48.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.57.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.48.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3) - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/visitor-keys': 8.48.0 + '@typescript-eslint/project-service': 8.57.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@5.9.3) + '@typescript-eslint/types': 8.57.0 + '@typescript-eslint/visitor-keys': 8.57.0 debug: 4.4.3(supports-color@8.1.1) - minimatch: 9.0.5 - semver: 7.7.3 + minimatch: 10.2.4 + semver: 7.7.4 tinyglobby: 0.2.15 - ts-api-utils: 2.1.0(typescript@5.9.3) + ts-api-utils: 2.4.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.48.0 - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) - eslint: 9.39.1(jiti@2.6.1) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.57.0 + '@typescript-eslint/types': 8.57.0 + '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3) + eslint: 10.0.3(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.48.0': + '@typescript-eslint/visitor-keys@8.57.0': dependencies: - '@typescript-eslint/types': 8.48.0 - eslint-visitor-keys: 4.2.1 + '@typescript-eslint/types': 8.57.0 + eslint-visitor-keys: 5.0.1 '@typespec/ts-http-runtime@0.3.2': dependencies: @@ -6866,16 +7140,16 @@ snapshots: - utf-8-validate - vite - '@vitest/coverage-istanbul@4.0.18(vitest@4.1.0-beta.3)': + '@vitest/coverage-istanbul@4.1.0-beta.6(vitest@4.1.0-beta.3)': dependencies: + '@babel/core': 7.29.0 '@istanbuljs/schema': 0.1.3 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.3 istanbul-lib-report: 3.0.1 istanbul-reports: 3.2.0 - magicast: 0.5.1 + magicast: 0.5.2 obug: 2.1.1 tinyrainbow: 3.0.3 vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) @@ -6919,11 +7193,11 @@ snapshots: optionalDependencies: '@vitest/browser': 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) - '@vitest/eslint-plugin@1.5.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)(vitest@4.1.0-beta.3)': + '@vitest/eslint-plugin@1.6.10(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)(vitest@4.1.0-beta.3)': dependencies: - '@typescript-eslint/scope-manager': 8.48.0 - '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.39.1(jiti@2.6.1) + '@typescript-eslint/scope-manager': 8.57.0 + '@typescript-eslint/utils': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) + eslint: 10.0.3(jiti@2.6.1) optionalDependencies: typescript: 5.9.3 vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) @@ -7029,7 +7303,7 @@ snapshots: https-proxy-agent: 7.0.6 jszip: 3.10.1 ora: 8.2.0 - semver: 7.7.3 + semver: 7.7.4 transitivePeerDependencies: - supports-color @@ -7096,7 +7370,7 @@ snapshots: parse-semver: 1.1.1 read: 1.0.7 secretlint: 10.2.2 - semver: 7.7.3 + semver: 7.7.4 tmp: 0.2.5 typed-rest-client: 1.8.11 url-join: 4.0.1 @@ -7169,19 +7443,19 @@ snapshots: abbrev@2.0.0: {} - acorn-jsx@5.3.2(acorn@8.15.0): + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: - acorn: 8.15.0 + acorn: 8.16.0 acorn-walk@8.3.4: dependencies: - acorn: 8.15.0 + acorn: 8.16.0 - acorn@8.15.0: {} + acorn@8.16.0: {} agent-base@7.1.4: {} - ajv@6.12.6: + ajv@6.14.0: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 @@ -7276,6 +7550,8 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + base64-js@1.5.1: optional: true @@ -7317,6 +7593,10 @@ snapshots: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.4: + dependencies: + balanced-match: 4.0.4 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -7354,7 +7634,7 @@ snapshots: escalade: 3.2.0 jsonc-parser: 3.3.1 package-manager-detector: 1.6.0 - semver: 7.7.3 + semver: 7.7.4 tinyexec: 1.0.2 tinyglobby: 0.2.15 yaml: 2.8.2 @@ -7415,6 +7695,8 @@ snapshots: cac@6.7.14: {} + cac@7.0.0: {} + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -7432,8 +7714,6 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 - callsites@3.1.0: {} - camelcase@6.3.0: {} caniuse-lite@1.0.30001757: {} @@ -7474,7 +7754,7 @@ snapshots: pathe: 1.1.2 pkg-types: 1.3.1 scule: 1.3.0 - semver: 7.7.3 + semver: 7.7.4 std-env: 3.10.0 yaml: 2.8.2 transitivePeerDependencies: @@ -7489,7 +7769,7 @@ snapshots: convert-gitmoji: 0.1.5 execa: 9.6.1 ofetch: 1.5.1 - semver: 7.7.3 + semver: 7.7.4 tinyglobby: 0.2.15 transitivePeerDependencies: - magicast @@ -7518,7 +7798,7 @@ snapshots: parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 parse5-parser-stream: 7.1.2 - undici: 7.16.0 + undici: 7.22.0 whatwg-mimetype: 4.0.0 chokidar@3.6.0: @@ -7590,7 +7870,7 @@ snapshots: commander@12.1.0: {} - comment-parser@1.4.1: {} + comment-parser@1.4.5: {} concat-map@0.0.1: {} @@ -7762,7 +8042,7 @@ snapshots: dependencies: dequal: 2.0.3 - diff-sequences@27.5.1: {} + diff-sequences@29.6.3: {} diff@5.2.0: {} @@ -7815,7 +8095,7 @@ snapshots: '@one-ini/wasm': 0.1.1 commander: 10.0.1 minimatch: 9.0.1 - semver: 7.7.3 + semver: 7.7.4 electron-to-chromium@1.5.262: {} @@ -8032,159 +8312,164 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-compat-utils@0.5.1(eslint@9.39.1(jiti@2.6.1)): + eslint-compat-utils@0.5.1(eslint@10.0.3(jiti@2.6.1)): dependencies: - eslint: 9.39.1(jiti@2.6.1) - semver: 7.7.3 + eslint: 10.0.3(jiti@2.6.1) + semver: 7.7.4 - eslint-compat-utils@0.6.5(eslint@9.39.1(jiti@2.6.1)): + eslint-config-flat-gitignore@2.2.1(eslint@10.0.3(jiti@2.6.1)): dependencies: - eslint: 9.39.1(jiti@2.6.1) - semver: 7.7.3 + '@eslint/compat': 2.0.3(eslint@10.0.3(jiti@2.6.1)) + eslint: 10.0.3(jiti@2.6.1) - eslint-config-flat-gitignore@2.1.0(eslint@9.39.1(jiti@2.6.1)): + eslint-flat-config-utils@3.0.2: dependencies: - '@eslint/compat': 1.4.1(eslint@9.39.1(jiti@2.6.1)) - eslint: 9.39.1(jiti@2.6.1) + '@eslint/config-helpers': 0.5.3 + pathe: 2.0.3 - eslint-flat-config-utils@2.1.4: + eslint-json-compat-utils@0.2.1(eslint@10.0.3(jiti@2.6.1))(jsonc-eslint-parser@3.1.0): dependencies: - pathe: 2.0.3 + eslint: 10.0.3(jiti@2.6.1) + esquery: 1.7.0 + jsonc-eslint-parser: 3.1.0 - eslint-json-compat-utils@0.2.1(eslint@9.39.1(jiti@2.6.1))(jsonc-eslint-parser@2.4.1): + eslint-merge-processors@2.0.0(eslint@10.0.3(jiti@2.6.1)): dependencies: - eslint: 9.39.1(jiti@2.6.1) - esquery: 1.6.0 - jsonc-eslint-parser: 2.4.1 + eslint: 10.0.3(jiti@2.6.1) - eslint-merge-processors@2.0.0(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-antfu@3.2.2(eslint@10.0.3(jiti@2.6.1)): dependencies: - eslint: 9.39.1(jiti@2.6.1) + eslint: 10.0.3(jiti@2.6.1) - eslint-plugin-antfu@3.1.1(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-command@3.5.2(@typescript-eslint/rule-tester@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(@typescript-eslint/typescript-estree@8.57.0(typescript@5.9.3))(@typescript-eslint/utils@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1)): dependencies: - eslint: 9.39.1(jiti@2.6.1) + '@es-joy/jsdoccomment': 0.84.0 + '@typescript-eslint/rule-tester': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) + eslint: 10.0.3(jiti@2.6.1) - eslint-plugin-command@3.3.1(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-depend@1.5.0(eslint@10.0.3(jiti@2.6.1)): dependencies: - '@es-joy/jsdoccomment': 0.50.2 - eslint: 9.39.1(jiti@2.6.1) + empathic: 2.0.0 + eslint: 10.0.3(jiti@2.6.1) + module-replacements: 2.11.0 + semver: 7.7.4 - eslint-plugin-es-x@7.8.0(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-es-x@7.8.0(eslint@10.0.3(jiti@2.6.1)): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.2 - eslint: 9.39.1(jiti@2.6.1) - eslint-compat-utils: 0.5.1(eslint@9.39.1(jiti@2.6.1)) + eslint: 10.0.3(jiti@2.6.1) + eslint-compat-utils: 0.5.1(eslint@10.0.3(jiti@2.6.1)) - eslint-plugin-import-lite@0.3.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): + eslint-plugin-import-lite@0.5.2(eslint@10.0.3(jiti@2.6.1)): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) - '@typescript-eslint/types': 8.48.0 - eslint: 9.39.1(jiti@2.6.1) - optionalDependencies: - typescript: 5.9.3 + eslint: 10.0.3(jiti@2.6.1) - eslint-plugin-jsdoc@51.4.1(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-jsdoc@62.7.1(eslint@10.0.3(jiti@2.6.1)): dependencies: - '@es-joy/jsdoccomment': 0.52.0 + '@es-joy/jsdoccomment': 0.84.0 + '@es-joy/resolve.exports': 1.2.0 are-docs-informative: 0.0.2 - comment-parser: 1.4.1 + comment-parser: 1.4.5 debug: 4.4.3(supports-color@8.1.1) escape-string-regexp: 4.0.0 - eslint: 9.39.1(jiti@2.6.1) - espree: 10.4.0 - esquery: 1.6.0 + eslint: 10.0.3(jiti@2.6.1) + espree: 11.2.0 + esquery: 1.7.0 + html-entities: 2.6.0 + object-deep-merge: 2.0.0 parse-imports-exports: 0.2.4 - semver: 7.7.3 + semver: 7.7.4 spdx-expression-parse: 4.0.0 + to-valid-identifier: 1.0.0 transitivePeerDependencies: - supports-color - eslint-plugin-jsonc@2.21.0(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-jsonc@3.1.1(eslint@10.0.3(jiti@2.6.1)): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) - diff-sequences: 27.5.1 - eslint: 9.39.1(jiti@2.6.1) - eslint-compat-utils: 0.6.5(eslint@9.39.1(jiti@2.6.1)) - eslint-json-compat-utils: 0.2.1(eslint@9.39.1(jiti@2.6.1))(jsonc-eslint-parser@2.4.1) - espree: 10.4.0 - graphemer: 1.4.0 - jsonc-eslint-parser: 2.4.1 + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) + '@eslint/core': 1.1.1 + '@eslint/plugin-kit': 0.6.1 + '@ota-meshi/ast-token-store': 0.3.0 + diff-sequences: 29.6.3 + eslint: 10.0.3(jiti@2.6.1) + eslint-json-compat-utils: 0.2.1(eslint@10.0.3(jiti@2.6.1))(jsonc-eslint-parser@3.1.0) + jsonc-eslint-parser: 3.1.0 natural-compare: 1.4.0 - synckit: 0.11.11 + synckit: 0.11.12 transitivePeerDependencies: - '@eslint/json' - eslint-plugin-n@17.23.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): + eslint-plugin-n@17.24.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) enhanced-resolve: 5.18.3 - eslint: 9.39.1(jiti@2.6.1) - eslint-plugin-es-x: 7.8.0(eslint@9.39.1(jiti@2.6.1)) - get-tsconfig: 4.13.0 + eslint: 10.0.3(jiti@2.6.1) + eslint-plugin-es-x: 7.8.0(eslint@10.0.3(jiti@2.6.1)) + get-tsconfig: 4.13.6 globals: 15.15.0 globrex: 0.1.2 ignore: 5.3.2 - semver: 7.7.3 + semver: 7.7.4 ts-declaration-location: 1.0.7(typescript@5.9.3) transitivePeerDependencies: - typescript eslint-plugin-no-only-tests@3.3.0: {} - eslint-plugin-perfectionist@4.15.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): + eslint-plugin-perfectionist@5.6.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@typescript-eslint/types': 8.48.0 - '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) - eslint: 9.39.1(jiti@2.6.1) + '@typescript-eslint/utils': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) + eslint: 10.0.3(jiti@2.6.1) natural-orderby: 5.0.0 transitivePeerDependencies: - supports-color - typescript - eslint-plugin-pnpm@1.3.0(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-pnpm@1.6.0(eslint@10.0.3(jiti@2.6.1)): dependencies: empathic: 2.0.0 - eslint: 9.39.1(jiti@2.6.1) - jsonc-eslint-parser: 2.4.1 + eslint: 10.0.3(jiti@2.6.1) + jsonc-eslint-parser: 3.1.0 pathe: 2.0.3 - pnpm-workspace-yaml: 1.3.0 + pnpm-workspace-yaml: 1.6.0 tinyglobby: 0.2.15 - yaml-eslint-parser: 1.3.1 + yaml: 2.8.2 + yaml-eslint-parser: 2.0.0 - eslint-plugin-regexp@2.10.0(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-regexp@3.1.0(eslint@10.0.3(jiti@2.6.1)): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.2 - comment-parser: 1.4.1 - eslint: 9.39.1(jiti@2.6.1) - jsdoc-type-pratt-parser: 4.8.0 + comment-parser: 1.4.5 + eslint: 10.0.3(jiti@2.6.1) + jsdoc-type-pratt-parser: 7.1.1 refa: 0.12.1 regexp-ast-analysis: 0.7.1 scslre: 0.3.0 - eslint-plugin-toml@0.12.0(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-toml@1.3.1(eslint@10.0.3(jiti@2.6.1)): dependencies: + '@eslint/core': 1.1.1 + '@eslint/plugin-kit': 0.6.1 + '@ota-meshi/ast-token-store': 0.3.0 debug: 4.4.3(supports-color@8.1.1) - eslint: 9.39.1(jiti@2.6.1) - eslint-compat-utils: 0.6.5(eslint@9.39.1(jiti@2.6.1)) - lodash: 4.17.21 - toml-eslint-parser: 0.10.0 + eslint: 10.0.3(jiti@2.6.1) + toml-eslint-parser: 1.0.3 transitivePeerDependencies: - supports-color - eslint-plugin-unicorn@60.0.0(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-unicorn@63.0.0(eslint@10.0.3(jiti@2.6.1)): dependencies: '@babel/helper-validator-identifier': 7.28.5 - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) - '@eslint/plugin-kit': 0.3.5 + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) change-case: 5.4.4 ci-info: 4.3.1 clean-regexp: 1.0.0 core-js-compat: 3.47.0 - eslint: 9.39.1(jiti@2.6.1) - esquery: 1.6.0 + eslint: 10.0.3(jiti@2.6.1) find-up-simple: 1.0.1 globals: 16.5.0 indent-string: 5.0.0 @@ -8192,49 +8477,53 @@ snapshots: jsesc: 3.1.0 pluralize: 8.0.0 regexp-tree: 0.1.27 - regjsparser: 0.12.0 - semver: 7.7.3 + regjsparser: 0.13.0 + semver: 7.7.4 strip-indent: 4.1.1 - eslint-plugin-unused-imports@4.3.0(@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.57.0(@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1)): dependencies: - eslint: 9.39.1(jiti@2.6.1) + eslint: 10.0.3(jiti@2.6.1) optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.57.0(@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - eslint-plugin-vue@10.6.2(@stylistic/eslint-plugin@5.6.1(eslint@9.39.1(jiti@2.6.1)))(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@2.6.1))): + eslint-plugin-vue@10.8.0(@stylistic/eslint-plugin@5.10.0(eslint@10.0.3(jiti@2.6.1)))(@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@10.0.3(jiti@2.6.1))): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) - eslint: 9.39.1(jiti@2.6.1) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) + eslint: 10.0.3(jiti@2.6.1) natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 7.1.1 - semver: 7.7.3 - vue-eslint-parser: 10.2.0(eslint@9.39.1(jiti@2.6.1)) + semver: 7.7.4 + vue-eslint-parser: 10.4.0(eslint@10.0.3(jiti@2.6.1)) xml-name-validator: 4.0.0 optionalDependencies: - '@stylistic/eslint-plugin': 5.6.1(eslint@9.39.1(jiti@2.6.1)) - '@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@stylistic/eslint-plugin': 5.10.0(eslint@10.0.3(jiti@2.6.1)) + '@typescript-eslint/parser': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - eslint-plugin-yml@1.19.0(eslint@9.39.1(jiti@2.6.1)): + eslint-plugin-yml@3.3.1(eslint@10.0.3(jiti@2.6.1)): dependencies: + '@eslint/core': 1.1.1 + '@eslint/plugin-kit': 0.6.1 + '@ota-meshi/ast-token-store': 0.3.0 debug: 4.4.3(supports-color@8.1.1) - diff-sequences: 27.5.1 - escape-string-regexp: 4.0.0 - eslint: 9.39.1(jiti@2.6.1) - eslint-compat-utils: 0.6.5(eslint@9.39.1(jiti@2.6.1)) + diff-sequences: 29.6.3 + escape-string-regexp: 5.0.0 + eslint: 10.0.3(jiti@2.6.1) natural-compare: 1.4.0 - yaml-eslint-parser: 1.3.1 + yaml-eslint-parser: 2.0.0 transitivePeerDependencies: - supports-color - eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.25)(eslint@9.39.1(jiti@2.6.1)): + eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.25)(eslint@10.0.3(jiti@2.6.1)): dependencies: '@vue/compiler-sfc': 3.5.25 - eslint: 9.39.1(jiti@2.6.1) + eslint: 10.0.3(jiti@2.6.1) - eslint-scope@8.4.0: + eslint-scope@9.1.2: dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.8 esrecurse: 4.3.0 estraverse: 5.3.0 @@ -8242,29 +8531,28 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.39.1(jiti@2.6.1): + eslint-visitor-keys@5.0.1: {} + + eslint@10.0.3(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.21.1 - '@eslint/config-helpers': 0.4.2 - '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.3 - '@eslint/js': 9.39.1 - '@eslint/plugin-kit': 0.4.1 + '@eslint/config-array': 0.23.3 + '@eslint/config-helpers': 0.5.3 + '@eslint/core': 1.1.1 + '@eslint/plugin-kit': 0.6.1 '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 - ajv: 6.12.6 - chalk: 4.1.2 + ajv: 6.14.0 cross-spawn: 7.0.6 debug: 4.4.3(supports-color@8.1.1) escape-string-regexp: 4.0.0 - eslint-scope: 8.4.0 - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 - esquery: 1.6.0 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 8.0.0 @@ -8274,8 +8562,7 @@ snapshots: imurmurhash: 0.1.4 is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 + minimatch: 10.2.4 natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: @@ -8285,19 +8572,19 @@ snapshots: espree@10.4.0: dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) eslint-visitor-keys: 4.2.1 - espree@9.6.1: + espree@11.2.0: dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - eslint-visitor-keys: 3.4.3 + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 5.0.1 esprima@4.0.1: {} - esquery@1.6.0: + esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -8519,10 +8806,6 @@ snapshots: '@sec-ant/readable-stream': 0.4.1 is-stream: 4.0.1 - get-tsconfig@4.13.0: - dependencies: - resolve-pkg-maps: 1.0.0 - get-tsconfig@4.13.6: dependencies: resolve-pkg-maps: 1.0.0 @@ -8572,7 +8855,7 @@ snapshots: dependencies: foreground-child: 3.3.1 jackspeak: 4.1.1 - minimatch: 10.1.1 + minimatch: 10.2.4 minipass: 7.1.2 package-json-from-dist: 1.0.1 path-scurry: 2.0.1 @@ -8594,12 +8877,12 @@ snapshots: minimatch: 5.1.6 once: 1.4.0 - globals@14.0.0: {} - globals@15.15.0: {} globals@16.5.0: {} + globals@17.4.0: {} + globby@14.1.0: dependencies: '@sindresorhus/merge-streams': 2.3.0 @@ -8615,8 +8898,6 @@ snapshots: graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - happy-dom@14.7.1: dependencies: entities: 4.5.0 @@ -8681,6 +8962,8 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' + html-entities@2.6.0: {} + html-escaper@2.0.2: {} htmlparser2@10.0.0: @@ -8759,11 +9042,6 @@ snapshots: immediate@3.0.6: {} - import-fresh@3.3.1: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - import-without-cache@0.2.5: {} imurmurhash@0.1.4: {} @@ -8916,16 +9194,6 @@ snapshots: istanbul-lib-coverage@3.2.2: {} - istanbul-lib-instrument@6.0.3: - dependencies: - '@babel/core': 7.28.5 - '@babel/parser': 7.28.5 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 7.7.3 - transitivePeerDependencies: - - supports-color - istanbul-lib-report@3.0.1: dependencies: istanbul-lib-coverage: 3.2.2 @@ -8994,9 +9262,7 @@ snapshots: dependencies: argparse: 2.0.1 - jsdoc-type-pratt-parser@4.1.0: {} - - jsdoc-type-pratt-parser@4.8.0: {} + jsdoc-type-pratt-parser@7.1.1: {} jsdom@24.1.3: dependencies: @@ -9053,8 +9319,6 @@ snapshots: - '@noble/hashes' - supports-color - jsesc@3.0.2: {} - jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -9067,12 +9331,11 @@ snapshots: json5@2.2.3: {} - jsonc-eslint-parser@2.4.1: + jsonc-eslint-parser@3.1.0: dependencies: - acorn: 8.15.0 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - semver: 7.7.3 + acorn: 8.16.0 + eslint-visitor-keys: 5.0.1 + semver: 7.7.4 jsonc-parser@3.3.1: {} @@ -9093,7 +9356,7 @@ snapshots: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 7.7.3 + semver: 7.7.4 jszip@3.10.1: dependencies: @@ -9200,8 +9463,6 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.2.4: {} - lru-cache@11.2.6: {} lru-cache@5.1.1: @@ -9234,9 +9495,15 @@ snapshots: '@babel/types': 7.28.5 source-map-js: 1.2.1 + magicast@0.5.2: + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + source-map-js: 1.2.1 + make-dir@4.0.0: dependencies: - semver: 7.7.3 + semver: 7.7.4 markdown-it@14.1.0: dependencies: @@ -9596,9 +9863,9 @@ snapshots: min-indent@1.0.1: {} - minimatch@10.1.1: + minimatch@10.2.4: dependencies: - '@isaacs/brace-expansion': 5.0.0 + brace-expansion: 5.0.4 minimatch@3.1.2: dependencies: @@ -9639,7 +9906,7 @@ snapshots: mlly@1.8.0: dependencies: - acorn: 8.15.0 + acorn: 8.16.0 pathe: 2.0.3 pkg-types: 1.3.1 ufo: 1.6.1 @@ -9667,6 +9934,8 @@ snapshots: yargs-parser: 20.2.9 yargs-unparser: 2.0.0 + module-replacements@2.11.0: {} + mri@1.2.0: {} mrmime@2.0.1: {} @@ -9686,7 +9955,7 @@ snapshots: node-abi@3.85.0: dependencies: - semver: 7.7.3 + semver: 7.7.4 optional: true node-addon-api@4.3.0: @@ -9712,7 +9981,7 @@ snapshots: normalize-package-data@6.0.2: dependencies: hosted-git-info: 7.0.2 - semver: 7.7.3 + semver: 7.7.4 validate-npm-package-license: 3.0.4 normalize-path@3.0.0: {} @@ -9755,6 +10024,8 @@ snapshots: object-assign@4.1.1: {} + object-deep-merge@2.0.0: {} + object-inspect@1.13.4: {} object-is@1.1.6: @@ -9853,10 +10124,6 @@ snapshots: pako@1.0.11: {} - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - parse-cache-control@1.0.1: {} parse-gitignore@2.0.0: {} @@ -9915,7 +10182,7 @@ snapshots: path-scurry@2.0.1: dependencies: - lru-cache: 11.2.4 + lru-cache: 11.2.6 minipass: 7.1.2 path-type@6.0.0: {} @@ -9968,7 +10235,7 @@ snapshots: pngjs@7.0.0: {} - pnpm-workspace-yaml@1.3.0: + pnpm-workspace-yaml@1.6.0: dependencies: yaml: 2.8.2 @@ -10161,9 +10428,9 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 - regjsparser@0.12.0: + regjsparser@0.13.0: dependencies: - jsesc: 3.0.2 + jsesc: 3.1.0 require-directory@2.1.1: {} @@ -10171,7 +10438,7 @@ snapshots: requires-port@1.0.0: {} - resolve-from@4.0.0: {} + reserved-identifiers@1.2.0: {} resolve-pkg-maps@1.0.0: {} @@ -10309,7 +10576,7 @@ snapshots: semver@6.3.1: {} - semver@7.7.3: {} + semver@7.7.4: {} serialize-javascript@6.0.2: dependencies: @@ -10524,7 +10791,7 @@ snapshots: dependencies: get-port: 3.2.0 - synckit@0.11.11: + synckit@0.11.12: dependencies: '@pkgr/core': 0.2.9 @@ -10632,9 +10899,14 @@ snapshots: dependencies: is-number: 7.0.0 - toml-eslint-parser@0.10.0: + to-valid-identifier@1.0.0: dependencies: - eslint-visitor-keys: 3.4.3 + '@sindresorhus/base62': 1.0.0 + reserved-identifiers: 1.2.0 + + toml-eslint-parser@1.0.3: + dependencies: + eslint-visitor-keys: 5.0.1 totalist@3.0.1: {} @@ -10661,7 +10933,7 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@2.1.0(typescript@5.9.3): + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -10670,7 +10942,7 @@ snapshots: picomatch: 4.0.3 typescript: 5.9.3 - tsdown@0.20.3(synckit@0.11.11)(typescript@5.9.3): + tsdown@0.20.3(synckit@0.11.12)(typescript@5.9.3): dependencies: ansis: 4.2.0 cac: 6.7.14 @@ -10682,12 +10954,12 @@ snapshots: picomatch: 4.0.3 rolldown: 1.0.0-rc.3 rolldown-plugin-dts: 0.22.1(rolldown@1.0.0-rc.3)(typescript@5.9.3) - semver: 7.7.3 + semver: 7.7.4 tinyexec: 1.0.2 tinyglobby: 0.2.15 tree-kill: 1.2.2 unconfig-core: 7.4.2 - unrun: 0.2.27(synckit@0.11.11) + unrun: 0.2.27(synckit@0.11.12) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -10702,7 +10974,7 @@ snapshots: tsx@4.21.0: dependencies: esbuild: 0.27.0 - get-tsconfig: 4.13.0 + get-tsconfig: 4.13.6 optionalDependencies: fsevents: 2.3.3 @@ -10746,8 +11018,6 @@ snapshots: dependencies: '@fastify/busboy': 2.1.1 - undici@7.16.0: {} - undici@7.22.0: {} unicorn-magic@0.1.0: {} @@ -10777,11 +11047,11 @@ snapshots: universalify@2.0.1: {} - unrun@0.2.27(synckit@0.11.11): + unrun@0.2.27(synckit@0.11.12): dependencies: rolldown: 1.0.0-rc.3 optionalDependencies: - synckit: 0.11.11 + synckit: 0.11.12 update-browserslist-db@1.1.4(browserslist@4.28.0): dependencies: @@ -11040,15 +11310,15 @@ snapshots: vue-component-type-helpers@2.2.12: {} - vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@2.6.1)): + vue-eslint-parser@10.4.0(eslint@10.0.3(jiti@2.6.1)): dependencies: debug: 4.4.3(supports-color@8.1.1) - eslint: 9.39.1(jiti@2.6.1) - eslint-scope: 8.4.0 - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 - esquery: 1.6.0 - semver: 7.7.3 + eslint: 10.0.3(jiti@2.6.1) + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 + semver: 7.7.4 transitivePeerDependencies: - supports-color @@ -11187,9 +11457,9 @@ snapshots: yallist@4.0.0: {} - yaml-eslint-parser@1.3.1: + yaml-eslint-parser@2.0.0: dependencies: - eslint-visitor-keys: 3.4.3 + eslint-visitor-keys: 5.0.1 yaml: 2.8.2 yaml@2.8.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3b62a56..fa7a5c8 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,9 +1,51 @@ +shellEmulator: true + +trustPolicy: no-downgrade + packages: - ./ - ./packages/* - samples/* - samples/monorepo-vitest-workspace/packages/* +catalog: + '@antfu/eslint-config': ^7.7.0 + '@playwright/test': ^1.42.1 + '@types/chai': ^5.2.2 + '@types/micromatch': ^4.0.6 + '@types/mocha': ^10.0.6 + '@types/node': ^24.0.0 + '@types/prompts': ^2.4.9 + '@types/semver': ^7.3.9 + '@types/which': ^3.0.3 + '@types/ws': ^8.5.10 + '@vscode/test-cli': ^0.0.6 + '@vscode/test-electron': ^2.3.9 + '@vscode/vsce': ^3.1.0 + '@vue/reactivity': ^3.2.33 + acorn: ^8.12.0 + acorn-walk: ^8.3.3 + birpc: 2.4.0 + bumpp: ^10.1.1 + chai: ^5.1.0 + changelogithub: ^13.15.0 + eslint: ^10.0.3 + execa: ^8.0.1 + find-up: ^7.0.0 + get-port: ^6.1.2 + istanbul-to-vscode: ^2.1.0 + micromatch: ^4.0.5 + mighty-promise: ^0.0.8 + mocha: ^10.3.0 + pathe: ^1.1.2 + picocolors: ^1.0.0 + prompts: ^2.4.2 + semver: ^7.3.5 + tsdown: ^0.20.3 + tsx: ^4.7.1 + typescript: ^5.6.2 + which: ^4.0.0 + ws: ^8.16.0 catalogs: latest: '@types/picomatch': ^4.0.2 @@ -11,7 +53,6 @@ catalogs: '@vitest/browser-playwright': ^4.1.0-beta.3 '@vitest/coverage-istanbul': ^4.1.0-beta.3 '@vitest/coverage-v8': ^4.1.0-beta.3 - '@vitest/runner': ^4.1.0-beta.3 '@vitest/utils': ^4.1.0-beta.3 picomatch: ^4.0.3 vite: ^7.2.6 @@ -19,7 +60,6 @@ catalogs: v3: '@vitest/browser': ^3.2.4 - '@vitest/coverage-istanbul': ^3.2.4 '@vitest/coverage-v8': ^3.2.4 '@vitest/runner': ^3.2.4 '@vitest/utils': ^3.2.4 diff --git a/samples/basic-v4/package.json b/samples/basic-v4/package.json index 8a9c5ed..32bdc04 100644 --- a/samples/basic-v4/package.json +++ b/samples/basic-v4/package.json @@ -10,7 +10,7 @@ }, "devDependencies": { "@vitest/browser": "catalog:latest", - "@vitest/coverage-istanbul": "^4.0.18", + "@vitest/coverage-istanbul": "catalog:latest", "@vitest/coverage-v8": "catalog:latest", "vite": "catalog:latest", "vitest": "catalog:latest" -- 2.51.2 From e5cc3f275e5722a5d08a925a442950f349593fe4 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 10 Mar 2026 16:48:33 +0100 Subject: [PATCH 11/64] refactor: switch to oxc (#748) --- .github/renovate.json5 | 30 +- .github/workflows/ci.yml | 2 +- .oxfmtrc.json | 6 + .vscode/launch.json | 15 +- .vscode/settings.json | 18 +- CHANGELOG.md | 187 +- CLAUDE.md | 7 +- README.md | 16 +- debug-shims.d.ts | 4 +- eslint.config.mjs | 21 +- package.json | 174 +- packages/extension/src/api.ts | 79 +- packages/extension/src/apiProcess.ts | 55 +- packages/extension/src/commands/copyErrors.ts | 39 +- packages/extension/src/config.ts | 35 +- packages/extension/src/debug.ts | 164 +- packages/extension/src/diagnostic.ts | 2 +- packages/extension/src/extension.ts | 389 +-- .../extension/src/importsBreakdownProvider.ts | 5 +- packages/extension/src/inlineConsoleLog.ts | 14 +- packages/extension/src/log.ts | 27 +- packages/extension/src/runQueue.ts | 43 +- packages/extension/src/runner.ts | 239 +- packages/extension/src/schemaProvider.ts | 20 +- packages/extension/src/spawn/child_process.ts | 22 +- packages/extension/src/spawn/pkg.ts | 125 +- packages/extension/src/spawn/resolve.ts | 47 +- packages/extension/src/spawn/rpc.ts | 35 +- packages/extension/src/spawn/terminal.ts | 24 +- packages/extension/src/spawn/ws.ts | 36 +- packages/extension/src/testTree.ts | 164 +- packages/extension/src/testTreeData.ts | 33 +- packages/extension/src/utils.ts | 63 +- packages/extension/src/watcher.ts | 21 +- packages/extension/src/worker/index.ts | 36 +- packages/shared/package.json | 2 +- packages/shared/src/index.ts | 40 +- packages/shared/src/pkgManager.ts | 38 +- packages/shared/src/rpc.ts | 8 +- packages/shared/src/utils.ts | 45 +- packages/worker-legacy/src/collect.ts | 101 +- packages/worker-legacy/src/index.ts | 76 +- packages/worker-legacy/src/reporter.ts | 48 +- packages/worker-legacy/src/watcher.ts | 26 +- packages/worker-legacy/src/worker.ts | 111 +- packages/worker/package.json | 2 +- packages/worker/src/index.ts | 28 +- packages/worker/src/reporter.ts | 29 +- packages/worker/src/runner.ts | 30 +- packages/worker/src/watcher.ts | 17 +- packages/worker/src/worker.ts | 38 +- packages/worker/tsconfig.json | 10 +- pnpm-lock.yaml | 2366 ++--------------- pnpm-workspace.yaml | 2 - samples/ast-collector/package.json | 4 +- samples/ast-collector/src/add.ts | 5 +- samples/ast-collector/test/each.test.ts | 11 +- samples/basic-v4/.skip/vitest.config.ts | 2 +- samples/basic-v4/package.json | 2 +- samples/basic-v4/src/add.ts | 5 +- samples/basic-v4/test/add.test.ts | 11 +- samples/basic-v4/test/bug.test.ts | 6 +- samples/basic-v4/test/console.test.ts | 12 +- .../basic-v4/test/deep/deeper/deep.test.ts | 2 +- samples/basic-v4/test/duplicated.test.ts | 14 +- samples/basic-v4/test/each.test.ts | 62 +- samples/basic-v4/test/env.test.ts | 23 +- samples/basic-v4/test/snapshot.test.ts | 2 +- samples/basic-v4/test/throw.test.ts | 6 +- samples/basic-v4/test/using.test.ts | 8 +- samples/basic-v4/vite.config.ts | 2 +- samples/basic-v4/vitest.config.ts | 4 +- samples/basic/.skip/vitest.config.ts | 2 +- samples/basic/package.json | 2 +- samples/basic/src/add.ts | 5 +- samples/basic/test/add.test.ts | 11 +- samples/basic/test/bug.test.ts | 6 +- samples/basic/test/console.test.ts | 12 +- samples/basic/test/deep/deeper/deep.test.ts | 2 +- samples/basic/test/duplicated.test.ts | 14 +- samples/basic/test/each.test.ts | 62 +- samples/basic/test/env.test.ts | 14 +- samples/basic/test/snapshot.test.ts | 2 +- samples/basic/test/throw.test.ts | 6 +- samples/basic/test/using.test.ts | 8 +- samples/basic/vite.config.ts | 2 +- samples/browser/.skip/vitest.config.ts | 2 +- samples/browser/package.json | 4 +- samples/browser/src/add.ts | 2 +- samples/browser/test/add.test.ts | 11 +- samples/browser/test/console.test.ts | 12 +- samples/browser/test/deep/deeper/deep.test.ts | 2 +- samples/browser/test/duplicated.test.ts | 14 +- samples/browser/test/each.test.ts | 62 +- samples/browser/test/env.test.ts | 14 +- samples/browser/test/snapshot.test.ts | 2 +- samples/browser/test/using.test.ts | 8 +- samples/browser/vitest.config.ts | 13 +- samples/continuous/package.json | 4 +- .../continuous/test/imports-divide.test.ts | 1 - samples/e2e/package.json | 4 +- samples/e2e/vite.config.ts | 2 +- samples/imba/README.md | 3 +- samples/imba/index.html | 2 +- samples/imba/src/app.css | 138 +- samples/imba/src/main.js | 2 +- samples/imba/tsconfig.json | 10 +- samples/imba/vite.config.js | 32 +- samples/in-source/package.json | 2 +- samples/in-source/tsconfig.json | 6 +- .../monorepo-vitest-workspace/package.json | 2 +- .../packages/react copy/test/basic.test.tsx | 4 +- .../packages/react copy/vitest.config.ts | 4 +- .../packages/react/test/basic.test.tsx | 4 +- .../packages/react/vitest.config.ts | 4 +- .../test/vitest.config.ts | 2 +- .../vitest.config.ts | 4 +- .../sample.code-workspace | 16 +- .../multiple-configs/app1/vitest.config.js | 2 +- .../multiple-configs/app2/vitest.config.js | 2 +- samples/multiple-configs/package.json | 2 +- samples/no-config/package.json | 2 +- samples/readme/package.json | 6 +- samples/readme/test/example.test.ts | 2 +- samples/vue/components/AsyncWrapper.vue | 4 +- samples/vue/components/Hello.vue | 4 +- samples/vue/test/async.test.ts | 2 +- samples/vue/vitest.config.ts | 8 +- scripts/ecosystem-ci.mts | 3 +- scripts/lower-vitest-version.js | 2 +- scripts/release.mts | 18 +- test/e2e/runner.test.ts | 22 +- test/e2e/utils/assertions.ts | 14 +- test/e2e/utils/downloadSetup.ts | 3 +- test/e2e/utils/helper.ts | 24 +- test/e2e/utils/tester.ts | 47 +- test/e2e/vitest.config.ts | 8 +- test/unit/TestData.test.ts | 42 +- test/unit/config.test.ts | 4 +- test/unit/fixtures/discover/00_simple.ts | 4 +- test/unit/pkg.test.ts | 19 +- tsdown.config.mjs | 4 +- 142 files changed, 2113 insertions(+), 4070 deletions(-) create mode 100644 .oxfmtrc.json diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 5b432df..183dc8d 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -1,25 +1,21 @@ { - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": ["config:base", "schedule:weekly", "group:allNonMajor"], - "labels": ["dependencies"], - "rangeStrategy": "bump", - "packageRules": [ + $schema: 'https://docs.renovatebot.com/renovate-schema.json', + extends: ['config:base', 'schedule:weekly', 'group:allNonMajor'], + labels: ['dependencies'], + rangeStrategy: 'bump', + packageRules: [ { - "groupName": "Eslint packages", - "matchPackageNames": ["/eslint/"] + depTypeList: ['peerDependencies'], + enabled: false, }, { - "depTypeList": ["peerDependencies"], - "enabled": false + matchDepTypes: ['action'], + matchPackageNames: ['!actions/{/,}**', '!github/{/,}**'], + pinDigests: true, }, { - "matchDepTypes": ["action"], - "matchPackageNames": ["!actions/{/,}**", "!github/{/,}**"], - "pinDigests": true + matchFileNames: ['samples/**'], + enabled: false, }, - { - "matchFileNames": ["samples/**"], - "enabled": false - } - ] + ], } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a0173d..c9b3b80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: - run: pnpm install --filter="vitest-vscode-*" --frozen-lockfile - run: pnpm build - run: pnpm typecheck - - run: pnpm lint + - run: pnpm fmt test: runs-on: ${{ matrix.os }} diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 0000000..9da944d --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,6 @@ +{ + "$schema": "./node_modules/oxfmt/configuration_schema.json", + "ignorePatterns": [], + "semi": false, + "singleQuote": true +} diff --git a/.vscode/launch.json b/.vscode/launch.json index f32d520..16d6565 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,10 +9,7 @@ "name": "Run Extension Basic Sample", "type": "extensionHost", "request": "launch", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}", - "${workspaceFolder}/samples/basic" - ], + "args": ["--extensionDevelopmentPath=${workspaceFolder}", "${workspaceFolder}/samples/basic"], "outFiles": ["${workspaceFolder}/dist/**/*.js"] }, { @@ -51,10 +48,7 @@ "name": "Run Extension Imba Sample", "type": "extensionHost", "request": "launch", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}", - "${workspaceFolder}/samples/imba" - ], + "args": ["--extensionDevelopmentPath=${workspaceFolder}", "${workspaceFolder}/samples/imba"], "outFiles": ["${workspaceFolder}/dist/**/*.js"] }, { @@ -151,10 +145,7 @@ "name": "Run Extension Vue Sample", "type": "extensionHost", "request": "launch", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}", - "${workspaceFolder}/samples/vue" - ], + "args": ["--extensionDevelopmentPath=${workspaceFolder}", "${workspaceFolder}/samples/vue"], "outFiles": ["${workspaceFolder}/dist/**/*.js"] }, { diff --git a/.vscode/settings.json b/.vscode/settings.json index b3ba467..57c075c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,22 +1,17 @@ // Place your settings in this file to overwrite default and user settings. { - // Enable the ESlint flat config support - "eslint.experimental.useFlatConfig": true, - - // Turn off tsc task auto detection since we have the necessary tasks as npm scripts - "typescript.tsc.autoDetect": "off", // Disable the default formatter, use eslint instead "prettier.enable": false, - "editor.formatOnSave": false, + "editor.formatOnSave": true, + + "editor.defaultFormatter": "oxc.oxc-vscode", // Auto fix "editor.codeActionsOnSave": { - "source.fixAll.eslint": "explicit", + "source.fixAll.oxc": "always", "source.organizeImports": "never" }, - "testing.openTesting": "neverOpen", - // Enable eslint for all supported languages "eslint.validate": [ "javascript", @@ -30,5 +25,8 @@ "jsonc", "yaml" ], - "testing.automaticallyOpenTestResults": "neverOpen" + "testing.automaticallyOpenTestResults": "neverOpen", + "[typescript]": { + "editor.defaultFormatter": "oxc.oxc-vscode" + } } diff --git a/CHANGELOG.md b/CHANGELOG.md index eb72df0..a8f87ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,81 +4,71 @@ All notable changes to this project will be documented in this file. See [standa ### [0.2.44](https://github.com/vitest-dev/vscode/compare/v0.2.43...v0.2.44) (2024-02-18) - ### Bug Fixes -* exclude/include configs and missing workspacePath ([#192](https://github.com/vitest-dev/vscode/issues/192)) ([bee4f6e](https://github.com/vitest-dev/vscode/commit/bee4f6e9bce9a9761a627f89379d2aed20735d11)) +- exclude/include configs and missing workspacePath ([#192](https://github.com/vitest-dev/vscode/issues/192)) ([bee4f6e](https://github.com/vitest-dev/vscode/commit/bee4f6e9bce9a9761a627f89379d2aed20735d11)) ### [0.2.43](https://github.com/vitest-dev/vscode/compare/v0.2.42...v0.2.43) (2023-10-18) ### [0.2.42](https://github.com/vitest-dev/vscode/compare/v0.2.41...v0.2.42) (2023-07-01) - ### Bug Fixes -* Revert back to drive path capitalization on windows ([#161](https://github.com/vitest-dev/vscode/issues/161)) ([1afa62e](https://github.com/vitest-dev/vscode/commit/1afa62eaf234b6885dba7d0cf84e39400b2ce0a4)) +- Revert back to drive path capitalization on windows ([#161](https://github.com/vitest-dev/vscode/issues/161)) ([1afa62e](https://github.com/vitest-dev/vscode/commit/1afa62eaf234b6885dba7d0cf84e39400b2ce0a4)) ### [0.2.41](https://github.com/vitest-dev/vscode/compare/v0.2.40...v0.2.41) (2023-05-18) - ### Bug Fixes -* spawn err on mac ([74d3ab2](https://github.com/vitest-dev/vscode/commit/74d3ab2359a1ed21584ab2d8bdfb439ae9ea7d86)) +- spawn err on mac ([74d3ab2](https://github.com/vitest-dev/vscode/commit/74d3ab2359a1ed21584ab2d8bdfb439ae9ea7d86)) ### [0.2.40](https://github.com/vitest-dev/vscode/compare/v0.2.39...v0.2.40) (2023-05-18) - ### Bug Fixes -* spawn() requires a command name, not a full path ([#139](https://github.com/vitest-dev/vscode/issues/139)) ([fe7955e](https://github.com/vitest-dev/vscode/commit/fe7955e81dafb799b240c2db0e22bf828db2b36e)) +- spawn() requires a command name, not a full path ([#139](https://github.com/vitest-dev/vscode/issues/139)) ([fe7955e](https://github.com/vitest-dev/vscode/commit/fe7955e81dafb799b240c2db0e22bf828db2b36e)) ### [0.2.39](https://github.com/vitest-dev/vscode/compare/v0.2.38...v0.2.39) (2023-02-08) - ### Bug Fixes -* [#107](https://github.com/vitest-dev/vscode/issues/107) speed up activation events ([f4842b5](https://github.com/vitest-dev/vscode/commit/f4842b55de9b41e5e3e390b4408b19140a1b1942)) +- [#107](https://github.com/vitest-dev/vscode/issues/107) speed up activation events ([f4842b5](https://github.com/vitest-dev/vscode/commit/f4842b55de9b41e5e3e390b4408b19140a1b1942)) ### [0.2.38](https://github.com/vitest-dev/vscode/compare/v0.2.37...v0.2.38) (2023-02-08) - ### Features -* make skipFiles of launch config configurable via debugExclude setting ([1c07064](https://github.com/vitest-dev/vscode/commit/1c070648671e4c1c426dc564a6a110ee437e159a)) +- make skipFiles of launch config configurable via debugExclude setting ([1c07064](https://github.com/vitest-dev/vscode/commit/1c070648671e4c1c426dc564a6a110ee437e159a)) ### [0.2.37](https://github.com/vitest-dev/vscode/compare/v0.2.36...v0.2.37) (2023-01-20) ### [0.2.36](https://github.com/vitest-dev/vscode/compare/v0.2.35...v0.2.36) (2023-01-03) - ### Bug Fixes -* read package.json to check if a folder is a Vitest env ([df13127](https://github.com/vitest-dev/vscode/commit/df1312719887af6a89630a0e6bbd58a0da0629d7)) -* run single test on Windows not working properly ([c037333](https://github.com/vitest-dev/vscode/commit/c03733336b36dec6cde3b6da85c4be5da7f8284d)) -* tests still say Running after test run finishes ([ef2d017](https://github.com/vitest-dev/vscode/commit/ef2d01794a3fcba293126c3c5a42a1ec239b8b0c)) -* unable to cancel in-progress test runs ([7ca7f4a](https://github.com/vitest-dev/vscode/commit/7ca7f4a9f38a6cf3df2707d4bc5a1763f5e528e2)) +- read package.json to check if a folder is a Vitest env ([df13127](https://github.com/vitest-dev/vscode/commit/df1312719887af6a89630a0e6bbd58a0da0629d7)) +- run single test on Windows not working properly ([c037333](https://github.com/vitest-dev/vscode/commit/c03733336b36dec6cde3b6da85c4be5da7f8284d)) +- tests still say Running after test run finishes ([ef2d017](https://github.com/vitest-dev/vscode/commit/ef2d01794a3fcba293126c3c5a42a1ec239b8b0c)) +- unable to cancel in-progress test runs ([7ca7f4a](https://github.com/vitest-dev/vscode/commit/7ca7f4a9f38a6cf3df2707d4bc5a1763f5e528e2)) ### [0.2.35](https://github.com/vitest-dev/vscode/compare/v0.2.34...v0.2.35) (2022-12-29) - ### Bug Fixes -* map error to correct position when no sourcePos ([9d47d7d](https://github.com/vitest-dev/vscode/commit/9d47d7de20f021c933ba4b32dd05ffdf0a961875)) +- map error to correct position when no sourcePos ([9d47d7d](https://github.com/vitest-dev/vscode/commit/9d47d7de20f021c933ba4b32dd05ffdf0a961875)) ### [0.2.34](https://github.com/vitest-dev/vscode/compare/v0.2.33...v0.2.34) (2022-11-19) - ### Bug Fixes -* don't raise version warning if vitest is not installed yet ([744032c](https://github.com/vitest-dev/vscode/commit/744032c46cffec24aad8d3817063f07329284c62)) -* only collect test result once ([f330530](https://github.com/vitest-dev/vscode/commit/f330530b0f895f5834da08f5fbebe7f287a82497)) +- don't raise version warning if vitest is not installed yet ([744032c](https://github.com/vitest-dev/vscode/commit/744032c46cffec24aad8d3817063f07329284c62)) +- only collect test result once ([f330530](https://github.com/vitest-dev/vscode/commit/f330530b0f895f5834da08f5fbebe7f287a82497)) ### [0.2.33](https://github.com/vitest-dev/vscode/compare/v0.2.32...v0.2.33) (2022-11-19) - ### Bug Fixes -* windows drive letter match issue ([5c58ede](https://github.com/vitest-dev/vscode/commit/5c58edeb3580f9c378c6a38de76ae8fd6062c07c)) +- windows drive letter match issue ([5c58ede](https://github.com/vitest-dev/vscode/commit/5c58edeb3580f9c378c6a38de76ae8fd6062c07c)) ### [0.2.32](https://github.com/vitest-dev/vscode/compare/v0.2.31...v0.2.32) (2022-10-13) @@ -88,273 +78,238 @@ All notable changes to this project will be documented in this file. See [standa ### Bug Fixes -* Allow overwriting of the host parameter to solve the [issue 55](https://github.com/vitest-dev/vscode/issues/55). +- Allow overwriting of the host parameter to solve the [issue 55](https://github.com/vitest-dev/vscode/issues/55). ### [0.2.29](https://github.com/vitest-dev/vscode/compare/v0.2.28...v0.2.29) (2022-08-30) - ### Bug Fixes -* add more error message in when test not found ([dfdb154](https://github.com/vitest-dev/vscode/commit/dfdb1549dc99768cddb4bec798c1d7f5574bd752)) -* enrich error context around test not found ([443ed52](https://github.com/vitest-dev/vscode/commit/443ed52a263142258e68a358254e4543a2a82ce1)) +- add more error message in when test not found ([dfdb154](https://github.com/vitest-dev/vscode/commit/dfdb1549dc99768cddb4bec798c1d7f5574bd752)) +- enrich error context around test not found ([443ed52](https://github.com/vitest-dev/vscode/commit/443ed52a263142258e68a358254e4543a2a82ce1)) ### [0.2.28](https://github.com/vitest-dev/vscode/compare/v0.2.27...v0.2.28) (2022-08-26) ### [0.2.27](https://github.com/vitest-dev/vscode/compare/v0.2.26...v0.2.27) (2022-08-06) - ### Bug Fixes -* vitest env detect error [#61](https://github.com/vitest-dev/vscode/issues/61) ([9edf4c1](https://github.com/vitest-dev/vscode/commit/9edf4c1d6de56daafc44bebfad8fb7e47de98a02)) +- vitest env detect error [#61](https://github.com/vitest-dev/vscode/issues/61) ([9edf4c1](https://github.com/vitest-dev/vscode/commit/9edf4c1d6de56daafc44bebfad8fb7e47de98a02)) ### [0.2.26](https://github.com/vitest-dev/vscode/compare/v0.2.25...v0.2.26) (2022-08-06) - ### Bug Fixes -* conservatively display window error ([1312f2e](https://github.com/vitest-dev/vscode/commit/1312f2e98063b086c7bfcb4fc4bafed15dde3c4b)) +- conservatively display window error ([1312f2e](https://github.com/vitest-dev/vscode/commit/1312f2e98063b086c7bfcb4fc4bafed15dde3c4b)) ### [0.2.25](https://github.com/vitest-dev/vscode/compare/v0.2.24...v0.2.25) (2022-08-06) - ### Bug Fixes -* run partial test suite on windows ([#64](https://github.com/vitest-dev/vscode/issues/64)) ([a6019a4](https://github.com/vitest-dev/vscode/commit/a6019a45d45abb0411f3e6a67eaf5c5f00dead2e)) -* use source-mapped line when showing result. ([9db4fec](https://github.com/vitest-dev/vscode/commit/9db4fec1ca2336ccc53f0a4601e241268c5887e5)) +- run partial test suite on windows ([#64](https://github.com/vitest-dev/vscode/issues/64)) ([a6019a4](https://github.com/vitest-dev/vscode/commit/a6019a45d45abb0411f3e6a67eaf5c5f00dead2e)) +- use source-mapped line when showing result. ([9db4fec](https://github.com/vitest-dev/vscode/commit/9db4fec1ca2336ccc53f0a4601e241268c5887e5)) ### [0.2.24](https://github.com/vitest-dev/vscode/compare/v0.2.23...v0.2.24) (2022-08-06) ### [0.2.23](https://github.com/vitest-dev/vscode/compare/v0.2.22...v0.2.23) (2022-07-24) - ### Bug Fixes -* should try activate if contains package.json ([6d17b9f](https://github.com/vitest-dev/vscode/commit/6d17b9fa50330d56d159d29d35958c20d44219c6)) +- should try activate if contains package.json ([6d17b9f](https://github.com/vitest-dev/vscode/commit/6d17b9fa50330d56d159d29d35958c20d44219c6)) ### [0.2.22](https://github.com/vitest-dev/vscode/compare/v0.2.21...v0.2.22) (2022-07-23) - ### Features -* **runhandler:** add disabledWorkspaceFolders configuration ([67ba85c](https://github.com/vitest-dev/vscode/commit/67ba85c6772cb7bc2c340732a1d2ddb58a02bec3)), closes [#13](https://github.com/vitest-dev/vscode/issues/13) - +- **runhandler:** add disabledWorkspaceFolders configuration ([67ba85c](https://github.com/vitest-dev/vscode/commit/67ba85c6772cb7bc2c340732a1d2ddb58a02bec3)), closes [#13](https://github.com/vitest-dev/vscode/issues/13) ### Bug Fixes -* filter disabled workspace upfront ([a4fc22b](https://github.com/vitest-dev/vscode/commit/a4fc22b2b48f9d26cc25bfea1f9931a1e954df2d)) -* hidden test in side panel ([f89ce2f](https://github.com/vitest-dev/vscode/commit/f89ce2f34d66af3c2f677dc140256c75dbc771e4)) -* vitest.exclude not working [#60](https://github.com/vitest-dev/vscode/issues/60) ([9cd3218](https://github.com/vitest-dev/vscode/commit/9cd3218a0b430fd4d4a2deb565e6ac343f24470d)) +- filter disabled workspace upfront ([a4fc22b](https://github.com/vitest-dev/vscode/commit/a4fc22b2b48f9d26cc25bfea1f9931a1e954df2d)) +- hidden test in side panel ([f89ce2f](https://github.com/vitest-dev/vscode/commit/f89ce2f34d66af3c2f677dc140256c75dbc771e4)) +- vitest.exclude not working [#60](https://github.com/vitest-dev/vscode/issues/60) ([9cd3218](https://github.com/vitest-dev/vscode/commit/9cd3218a0b430fd4d4a2deb565e6ac343f24470d)) ### [0.2.21](https://github.com/vitest-dev/vscode/compare/v0.2.20...v0.2.21) (2022-07-23) - ### Bug Fixes -* no uppercase path allowed for test file ([f688bb5](https://github.com/vitest-dev/vscode/commit/f688bb50dc953c3c29ed36ed184eb006e8d41e00)) +- no uppercase path allowed for test file ([f688bb5](https://github.com/vitest-dev/vscode/commit/f688bb50dc953c3c29ed36ed184eb006e8d41e00)) ### [0.2.20](https://github.com/vitest-dev/vscode/compare/v0.2.19...v0.2.20) (2022-07-03) - ### Bug Fixes -* replace enqueued with started on test start ([d287772](https://github.com/vitest-dev/vscode/commit/d28777200477ca85624cc24c933f7307d203af5f)) +- replace enqueued with started on test start ([d287772](https://github.com/vitest-dev/vscode/commit/d28777200477ca85624cc24c933f7307d203af5f)) ### [0.2.19](https://github.com/vitest-dev/vscode/compare/v0.2.18...v0.2.19) (2022-06-20) - ### Bug Fixes -* don't stop debugging if restarted ([ae29785](https://github.com/vitest-dev/vscode/commit/ae29785251fbf7b8c3f9187eeddd19624495ab11)) +- don't stop debugging if restarted ([ae29785](https://github.com/vitest-dev/vscode/commit/ae29785251fbf7b8c3f9187eeddd19624495ab11)) ### [0.2.18](https://github.com/vitest-dev/vscode/compare/v0.2.17...v0.2.18) (2022-06-19) - ### Features -* use --api on debug mode ([77fd7b3](https://github.com/vitest-dev/vscode/commit/77fd7b3b7273a47d369f90babc816b351cd69561)) +- use --api on debug mode ([77fd7b3](https://github.com/vitest-dev/vscode/commit/77fd7b3b7273a47d369f90babc816b351cd69561)) ### [0.2.17](https://github.com/vitest-dev/vscode/compare/v0.2.16...v0.2.17) (2022-06-19) - ### Bug Fixes -* invoke onCollect when ws connected ([40bdd0a](https://github.com/vitest-dev/vscode/commit/40bdd0a8acecec682c9214cb71d965b716bca3ff)) +- invoke onCollect when ws connected ([40bdd0a](https://github.com/vitest-dev/vscode/commit/40bdd0a8acecec682c9214cb71d965b716bca3ff)) ### [0.2.16](https://github.com/vitest-dev/vscode/compare/v0.2.15...v0.2.16) (2022-06-19) - ### Features -* show enqueue status, show test result faster ([ff2ad74](https://github.com/vitest-dev/vscode/commit/ff2ad744cd96dc064102559d1c64348054cf968e)) - +- show enqueue status, show test result faster ([ff2ad74](https://github.com/vitest-dev/vscode/commit/ff2ad744cd96dc064102559d1c64348054cf968e)) ### Bug Fixes -* update required minimal vitest version ([3f8c817](https://github.com/vitest-dev/vscode/commit/3f8c817fbd28be8ddbfeadcb8c9109e81b937d1a)) +- update required minimal vitest version ([3f8c817](https://github.com/vitest-dev/vscode/commit/3f8c817fbd28be8ddbfeadcb8c9109e81b937d1a)) ### [0.2.15](https://github.com/vitest-dev/vscode/compare/v0.2.14...v0.2.15) (2022-06-19) - ### Bug Fixes -* windows path issue & improve error message ([8e2a3db](https://github.com/vitest-dev/vscode/commit/8e2a3db9963ae0999d5abf1e449dd42bd2a8710b)) +- windows path issue & improve error message ([8e2a3db](https://github.com/vitest-dev/vscode/commit/8e2a3db9963ae0999d5abf1e449dd42bd2a8710b)) ### [0.2.14](https://github.com/vitest-dev/vscode/compare/v0.2.13...v0.2.14) (2022-06-18) - ### Features -* use --api to get result from test run ([8e31504](https://github.com/vitest-dev/vscode/commit/8e31504db788db4ef5052dec49a1811e1ba86bf3)) - +- use --api to get result from test run ([8e31504](https://github.com/vitest-dev/vscode/commit/8e31504db788db4ef5052dec49a1811e1ba86bf3)) ### Bug Fixes -* trivial error ([7cb5a87](https://github.com/vitest-dev/vscode/commit/7cb5a8743137517b730e46b2069b11e6b1242924)) +- trivial error ([7cb5a87](https://github.com/vitest-dev/vscode/commit/7cb5a8743137517b730e46b2069b11e6b1242924)) ### [0.2.13](https://github.com/vitest-dev/vscode/compare/v0.2.12...v0.2.13) (2022-06-18) - ### Features -* add multi-root workspace run/debug support ([27951ad](https://github.com/vitest-dev/vscode/commit/27951ad148b29996d6a7b3e7e005bd235b86206f)) - +- add multi-root workspace run/debug support ([27951ad](https://github.com/vitest-dev/vscode/commit/27951ad148b29996d6a7b3e7e005bd235b86206f)) ### Bug Fixes -* add log & fix potential error cause [#44](https://github.com/vitest-dev/vscode/issues/44) ([080b5b8](https://github.com/vitest-dev/vscode/commit/080b5b86c3d7e3137dbe26034a72ec6e06a69d90)) -* consider workspace folder with no package.json not vitest env ([be4d4c3](https://github.com/vitest-dev/vscode/commit/be4d4c34beba029058bb869459f642fb8d94acc1)) -* loop over workspace folders for debugging ([690a880](https://github.com/vitest-dev/vscode/commit/690a880c4060009663029f874915121d1add8762)) -* loop to filter w/ async ([a63778f](https://github.com/vitest-dev/vscode/commit/a63778fdc246d00635fe11be6f3e1d53d2a89a2c)) -* sequentially debug separate workspace folders ([2239de7](https://github.com/vitest-dev/vscode/commit/2239de74f4db6466af2cd54239a5bf696858e877)) -* use specific workspace folder's vitest exe ([da560fc](https://github.com/vitest-dev/vscode/commit/da560fcedead5012fe752770bd37ed4d6c0688be)) +- add log & fix potential error cause [#44](https://github.com/vitest-dev/vscode/issues/44) ([080b5b8](https://github.com/vitest-dev/vscode/commit/080b5b86c3d7e3137dbe26034a72ec6e06a69d90)) +- consider workspace folder with no package.json not vitest env ([be4d4c3](https://github.com/vitest-dev/vscode/commit/be4d4c34beba029058bb869459f642fb8d94acc1)) +- loop over workspace folders for debugging ([690a880](https://github.com/vitest-dev/vscode/commit/690a880c4060009663029f874915121d1add8762)) +- loop to filter w/ async ([a63778f](https://github.com/vitest-dev/vscode/commit/a63778fdc246d00635fe11be6f3e1d53d2a89a2c)) +- sequentially debug separate workspace folders ([2239de7](https://github.com/vitest-dev/vscode/commit/2239de74f4db6466af2cd54239a5bf696858e877)) +- use specific workspace folder's vitest exe ([da560fc](https://github.com/vitest-dev/vscode/commit/da560fcedead5012fe752770bd37ed4d6c0688be)) ### [0.2.12](https://github.com/vitest-dev/vscode/compare/v0.2.11...v0.2.12) (2022-05-31) - ### Bug Fixes -* udpate test error message ([4b89bb3](https://github.com/vitest-dev/vscode/commit/4b89bb359f2466c66991ab7a3065be3284a4f606)) +- udpate test error message ([4b89bb3](https://github.com/vitest-dev/vscode/commit/4b89bb359f2466c66991ab7a3065be3284a4f606)) ### [0.2.11](https://github.com/vitest-dev/vscode/compare/v0.2.10...v0.2.11) (2022-05-23) - ### Features -* show diff in watch mode ([3e5a213](https://github.com/vitest-dev/vscode/commit/3e5a213fab6b827616380e13df11745b5a554472)) -* show error on the line it failed in watch mode [#37](https://github.com/vitest-dev/vscode/issues/37) ([793e2fc](https://github.com/vitest-dev/vscode/commit/793e2fc71b13ffd85e993e44628cffda4acb3895)) +- show diff in watch mode ([3e5a213](https://github.com/vitest-dev/vscode/commit/3e5a213fab6b827616380e13df11745b5a554472)) +- show error on the line it failed in watch mode [#37](https://github.com/vitest-dev/vscode/issues/37) ([793e2fc](https://github.com/vitest-dev/vscode/commit/793e2fc71b13ffd85e993e44628cffda4acb3895)) ### [0.2.10](https://github.com/vitest-dev/vscode/compare/v0.2.9...v0.2.10) (2022-05-20) - ### Bug Fixes -* runIf().concurrent [#36](https://github.com/vitest-dev/vscode/issues/36) ([e4c0dfe](https://github.com/vitest-dev/vscode/commit/e4c0dfef30fe0fcbea298fce4a87897248ae972b)) +- runIf().concurrent [#36](https://github.com/vitest-dev/vscode/issues/36) ([e4c0dfe](https://github.com/vitest-dev/vscode/commit/e4c0dfef30fe0fcbea298fce4a87897248ae972b)) ### [0.2.9](https://github.com/vitest-dev/vscode/compare/v0.2.8...v0.2.9) (2022-05-18) ### [0.2.8](https://github.com/vitest-dev/vscode/compare/v0.2.7...v0.2.8) (2022-05-18) - ### Bug Fixes -* use custom path when getVitestVersion ([3ebb2f0](https://github.com/vitest-dev/vscode/commit/3ebb2f099b9575fb9f5670321eb594e003103a4d)) +- use custom path when getVitestVersion ([3ebb2f0](https://github.com/vitest-dev/vscode/commit/3ebb2f099b9575fb9f5670321eb594e003103a4d)) ### [0.2.7](https://github.com/vitest-dev/vscode/compare/v0.2.6...v0.2.7) (2022-05-18) - ### Bug Fixes -* tests running state in watch mode ([4b36b31](https://github.com/vitest-dev/vscode/commit/4b36b3135036a07aee7f8a211b8988015a41b8f2)) +- tests running state in watch mode ([4b36b31](https://github.com/vitest-dev/vscode/commit/4b36b3135036a07aee7f8a211b8988015a41b8f2)) ### [0.2.6](https://github.com/vitest-dev/vscode/compare/v0.2.5...v0.2.6) (2022-05-18) - ### Bug Fixes -* potential extension activation error ([e5cdae5](https://github.com/vitest-dev/vscode/commit/e5cdae59f1dd3d1f8dddd71e53a0e165275ae9fc)) +- potential extension activation error ([e5cdae5](https://github.com/vitest-dev/vscode/commit/e5cdae59f1dd3d1f8dddd71e53a0e165275ae9fc)) ### [0.2.5](https://github.com/vitest-dev/vscode/compare/v0.2.4...v0.2.5) (2022-05-18) - ### Bug Fixes -* fix potential failed issue ([edba084](https://github.com/vitest-dev/vscode/commit/edba0841b75afb82d2b21e72f9225a9b842bc6e6)) +- fix potential failed issue ([edba084](https://github.com/vitest-dev/vscode/commit/edba0841b75afb82d2b21e72f9225a9b842bc6e6)) ### [0.2.4](https://github.com/vitest-dev/vscode/compare/v0.2.3...v0.2.4) (2022-05-17) - ### Bug Fixes -* add duration to failed tests ([923ea3f](https://github.com/vitest-dev/vscode/commit/923ea3f7df8cd074b774d4466075ac4ad770a09b)) -* dispose state after turning off watch mode ([a89e61b](https://github.com/vitest-dev/vscode/commit/a89e61b149a96907a0f2f96c295c0a5624e2216f)) -* use current available port for watch mode ([761b9d4](https://github.com/vitest-dev/vscode/commit/761b9d42972e9d503ba995029e28dad2296d77dd)) +- add duration to failed tests ([923ea3f](https://github.com/vitest-dev/vscode/commit/923ea3f7df8cd074b774d4466075ac4ad770a09b)) +- dispose state after turning off watch mode ([a89e61b](https://github.com/vitest-dev/vscode/commit/a89e61b149a96907a0f2f96c295c0a5624e2216f)) +- use current available port for watch mode ([761b9d4](https://github.com/vitest-dev/vscode/commit/761b9d42972e9d503ba995029e28dad2296d77dd)) ### [0.2.3](https://github.com/vitest-dev/vscode/compare/v0.2.2...v0.2.3) (2022-05-17) - ### Bug Fixes -* filter color char from testing output ([730ae97](https://github.com/vitest-dev/vscode/commit/730ae971a3b8c3e09bc01edfd7822ee5b968a718)), closes [#34](https://github.com/vitest-dev/vscode/issues/34) -* turn off auto error peek in watch mode ([0447fee](https://github.com/vitest-dev/vscode/commit/0447fee541e9eea4c84b44185df5aaf312556e12)) +- filter color char from testing output ([730ae97](https://github.com/vitest-dev/vscode/commit/730ae971a3b8c3e09bc01edfd7822ee5b968a718)), closes [#34](https://github.com/vitest-dev/vscode/issues/34) +- turn off auto error peek in watch mode ([0447fee](https://github.com/vitest-dev/vscode/commit/0447fee541e9eea4c84b44185df5aaf312556e12)) ### [0.2.2](https://github.com/vitest-dev/vscode/compare/v0.2.1...v0.2.2) (2022-05-16) ### [0.2.1](https://github.com/vitest-dev/vscode/compare/v0.2.0...v0.2.1) (2022-05-16) - ### Features -* add toggle watch mode to command palette ([7b17988](https://github.com/vitest-dev/vscode/commit/7b179886a873782bb437f5588b6343e5c06b5cab)) +- add toggle watch mode to command palette ([7b17988](https://github.com/vitest-dev/vscode/commit/7b179886a873782bb437f5588b6343e5c06b5cab)) ## [0.2.0](https://github.com/vitest-dev/vscode/compare/v0.1.27...v0.2.0) (2022-05-16) - ### Features -* add fuzzy match for tests ([ce91117](https://github.com/vitest-dev/vscode/commit/ce911178d1d567de3a32f0818f59e00bab2be3dc)) -* add watch mode run profile ([4ce6163](https://github.com/vitest-dev/vscode/commit/4ce6163dbb06909eaf2ddb4868a40694e74b1624)) -* add watcher ([6a28047](https://github.com/vitest-dev/vscode/commit/6a2804737f30744ec122dd572ef2a2fdc0781843)) -* introducing status bar item ([2ad2440](https://github.com/vitest-dev/vscode/commit/2ad2440ce9f20678d94984ed182c7b28233c1b21)) -* watcher works. now ([87071ee](https://github.com/vitest-dev/vscode/commit/87071ee518b2c8416642f56cca96eacca4ebfe3a)) - +- add fuzzy match for tests ([ce91117](https://github.com/vitest-dev/vscode/commit/ce911178d1d567de3a32f0818f59e00bab2be3dc)) +- add watch mode run profile ([4ce6163](https://github.com/vitest-dev/vscode/commit/4ce6163dbb06909eaf2ddb4868a40694e74b1624)) +- add watcher ([6a28047](https://github.com/vitest-dev/vscode/commit/6a2804737f30744ec122dd572ef2a2fdc0781843)) +- introducing status bar item ([2ad2440](https://github.com/vitest-dev/vscode/commit/2ad2440ce9f20678d94984ed182c7b28233c1b21)) +- watcher works. now ([87071ee](https://github.com/vitest-dev/vscode/commit/87071ee518b2c8416642f56cca96eacca4ebfe3a)) ### Bug Fixes -* fix spawn cmd ([296f2da](https://github.com/vitest-dev/vscode/commit/296f2da1fa61985f05b0fef612e5c15622e1b65a)) -* fix spawn cmd ([25f1dc8](https://github.com/vitest-dev/vscode/commit/25f1dc81ccf7293b691bc3970193e0d37d73fcd5)) -* make test run more visible ([3e7d37d](https://github.com/vitest-dev/vscode/commit/3e7d37da29dca8e7c04a9d52766402d133fbf032)) -* remove debounce to avoid watch mode error ([b7fbd85](https://github.com/vitest-dev/vscode/commit/b7fbd85000f1982b43081bd90b433db530035829)) +- fix spawn cmd ([296f2da](https://github.com/vitest-dev/vscode/commit/296f2da1fa61985f05b0fef612e5c15622e1b65a)) +- fix spawn cmd ([25f1dc8](https://github.com/vitest-dev/vscode/commit/25f1dc81ccf7293b691bc3970193e0d37d73fcd5)) +- make test run more visible ([3e7d37d](https://github.com/vitest-dev/vscode/commit/3e7d37da29dca8e7c04a9d52766402d133fbf032)) +- remove debounce to avoid watch mode error ([b7fbd85](https://github.com/vitest-dev/vscode/commit/b7fbd85000f1982b43081bd90b433db530035829)) ### [0.1.27](https://github.com/vitest-dev/vscode/compare/v0.1.26...v0.1.27) (2022-05-08) - ### Bug Fixes -* priority on win ([93a6112](https://github.com/vitest-dev/vscode/commit/93a611242e37298d0e7b911b804062ae7f89b6e8)) -* use .bin/vitest by default ([852cb41](https://github.com/vitest-dev/vscode/commit/852cb41c4ae465734a5dbf14ce2aef0fc7600238)) +- priority on win ([93a6112](https://github.com/vitest-dev/vscode/commit/93a611242e37298d0e7b911b804062ae7f89b6e8)) +- use .bin/vitest by default ([852cb41](https://github.com/vitest-dev/vscode/commit/852cb41c4ae465734a5dbf14ce2aef0fc7600238)) ### [0.1.26](https://github.com/vitest-dev/vscode/compare/v0.1.25...v0.1.26) (2022-05-08) ### [0.1.25](https://github.com/vitest-dev/vscode/compare/v0.1.24...v0.1.25) (2022-05-08) - ### Bug Fixes -* replace spawn with fork in some cases ([b0e36f9](https://github.com/vitest-dev/vscode/commit/b0e36f9895fffeabce3dd841bf111753b13b57f9)) +- replace spawn with fork in some cases ([b0e36f9](https://github.com/vitest-dev/vscode/commit/b0e36f9895fffeabce3dd841bf111753b13b57f9)) ### [0.1.24](https://github.com/vitest-dev/vscode/compare/v0.1.23...v0.1.24) (2022-05-06) - ### Bug Fixes -* get vitest version on linux ([5735ceb](https://github.com/vitest-dev/vscode/commit/5735ceb4a25e899cda55ccc639491fdde14c6eae)) +- get vitest version on linux ([5735ceb](https://github.com/vitest-dev/vscode/commit/5735ceb4a25e899cda55ccc639491fdde14c6eae)) ### [0.1.23](https://github.com/vitest-dev/vscode/compare/v0.1.22...v0.1.23) (2022-05-01) - ### Bug Fixes -* use decorator-legacy on ts file ([c04d4b9](https://github.com/vitest-dev/vscode/commit/c04d4b98c0e23bae765d9f447f43f076cb04406a)) +- use decorator-legacy on ts file ([c04d4b9](https://github.com/vitest-dev/vscode/commit/c04d4b98c0e23bae765d9f447f43f076cb04406a)) ### [0.1.22](https://github.com/vitest-dev/vscode/compare/v0.1.21...v0.1.22) (2022-04-29) diff --git a/CLAUDE.md b/CLAUDE.md index b4df49f..ddbccaf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,13 +19,14 @@ The project uses a monorepo structure with multiple packages that work together: ### 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 +- **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 @@ -33,6 +34,7 @@ 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 @@ -40,6 +42,7 @@ pnpm test-e2e # Run end-to-end tests (Vitest-based) ``` ### Code Quality + ```bash pnpm typecheck # TypeScript type checking pnpm lint # Run ESLint @@ -47,6 +50,7 @@ pnpm lint:fix # Run ESLint with auto-fix ``` ### Packaging + ```bash pnpm package # Create .vsix package for distribution ``` @@ -72,6 +76,7 @@ Uses **pnpm** with workspaces. The project requires pnpm@10.11.1 as specified in ## 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) diff --git a/README.md b/README.md index 486ea5a..2e6f0a0 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Vitest uses vscode's `TestController` API to provide a unified testing experienc ### In the Testing View -![Testing view](./img/vitest-extension.png "Testing view") +![Testing view](./img/vitest-extension.png 'Testing view') You can access the extension from the Testing view in the Visual Studio Code sidebar. @@ -53,7 +53,7 @@ Icons next to each test indicate their status—passed (checkmark), failed (cros ### In the Test File -![Vitest test file](./img/vitest-test-file.png "Vitest test file") +![Vitest test file](./img/vitest-test-file.png 'Vitest test file') When viewing a test file, you'll notice test icons in the gutter next to each test case: @@ -113,11 +113,11 @@ These options are resolved relative to the [workspace file](https://code.visuals You can reveal the current test file in the test explorer view by selecting the "Reveal in Test Explorer" option (the last option on the screenshot) in the file context: -![Reveal test in explorer](./img/reveal-in-explorer.png "Reveal test in explorer") +![Reveal test in explorer](./img/reveal-in-explorer.png 'Reveal test in explorer') You can also type the same command in the quick picker while the file is open. -![Reveal test in explorer](./img/reveal-in-picker.png "Reveal test in explorer") +![Reveal test in explorer](./img/reveal-in-picker.png 'Reveal test in explorer') ### Import Breakdown @@ -125,7 +125,7 @@ If you use Vitest 4.0.15 or higher, during continuous runs the extension will sh If you hover over it, you can get a more detailed diagnostic. -![Import breakdown example](./img/import-breakdown.png "Import breakdown example") +![Import breakdown example](./img/import-breakdown.png 'Import breakdown example') You can disable this feature by turning off `vitest.showImportsDuration`. @@ -141,11 +141,11 @@ By default, the extension doens't rerun tests when files change. Click on the "eye" icon next to the test, file or a directory to enable "continuous run" for a related item. Whenever that test, file or any file in the directory changes, Vitest will rerun that test. Note that Vitest will also rerun tests if an imported module of the file is changed. -![Turn on continuous run button for a test](./img/eye-item-icon.png "Turn on continuous run button for a test") +![Turn on continuous run button for a test](./img/eye-item-icon.png 'Turn on continuous run button for a test') To enabled continuous run globally, click on the "eye" icon in the "Test Explorer" row. -![Start continuous run button](./img/eye-global-icon.png "Start continuous run button") +![Start continuous run button](./img/eye-global-icon.png 'Start continuous run button') ### How to hide Test Results view when running tests @@ -154,7 +154,7 @@ You can change the behaviour of testing view by modifying `testing.automatically - `neverOpen` will never open the testing view - `openOnTestStart` (default) opens the test results view when test starts running - `openOnTestFailure` opens the test results view if at least one of test fails -- `openExplorerOnTestStart` will open the test tree view when tests starts +- `openExplorerOnTestStart` will open the test tree view when tests starts This is a vscode's built-in option and will control every plugin. diff --git a/debug-shims.d.ts b/debug-shims.d.ts index 13be086..9bf7c16 100644 --- a/debug-shims.d.ts +++ b/debug-shims.d.ts @@ -15,7 +15,9 @@ declare module '@vscode/js-debug' { * try to modify options in a additive way. For example prefer appending * to rather than reading and overwriting `options.env.PATH`. */ - provideTerminalOptions(options: vscode.TerminalOptions): vscode.ProviderResult + provideTerminalOptions( + options: vscode.TerminalOptions, + ): vscode.ProviderResult } /** diff --git a/eslint.config.mjs b/eslint.config.mjs index 17ee58c..658423b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -5,14 +5,7 @@ export default antfu( // Disable tests rules because we need to test with various setup test: false, // This replaces the old `.gitignore` - ignores: [ - '**/coverage', - '**/*.snap', - '**/bench.json', - '**/fixtures', - '**/samples', - 'test', - ], + ignores: ['**/coverage', '**/*.snap', '**/bench.json', '**/fixtures', '**/samples', 'test'], }, { rules: { @@ -22,7 +15,7 @@ export default antfu( 'no-empty-pattern': 'off', 'antfu/indent-binary-ops': 'off', 'unused-imports/no-unused-imports': 'error', - 'curly': 'off', + curly: 'off', 'e18e/prefer-static-regex': 'off', 'pnpm/yaml-no-duplicate-catalog-item': 'off', 'style/member-delimiter-style': [ @@ -67,9 +60,7 @@ export default antfu( }, }, { - files: [ - `docs/${GLOB_SRC}`, - ], + files: [`docs/${GLOB_SRC}`], rules: { 'style/max-statements-per-line': 'off', 'import/newline-after-import': 'off', @@ -78,11 +69,7 @@ export default antfu( }, }, { - files: [ - `docs/${GLOB_SRC}`, - `packages/web-worker/${GLOB_SRC}`, - `test/web-worker/${GLOB_SRC}`, - ], + files: [`docs/${GLOB_SRC}`, `packages/web-worker/${GLOB_SRC}`, `test/web-worker/${GLOB_SRC}`], rules: { 'no-restricted-globals': 'off', }, diff --git a/package.json b/package.json index 69ff2a4..6262c5d 100644 --- a/package.json +++ b/package.json @@ -1,44 +1,88 @@ { - "publisher": "vitest", "name": "explorer", "displayName": "Vitest", - "type": "commonjs", "version": "1.46.0", - "packageManager": "pnpm@10.11.1", "description": "A Vite-native testing framework. It's fast!", - "author": "Vitest Team", + "categories": [ + "Testing" + ], + "keywords": [ + "javascript", + "test", + "typescript", + "vitest" + ], + "bugs": { + "url": "https://github.com/vitest-dev/vscode/issues" + }, "license": "MIT", + "author": "Vitest Team", "repository": { "type": "git", "url": "https://github.com/vitest-dev/vscode.git" }, - "bugs": { - "url": "https://github.com/vitest-dev/vscode/issues" - }, "sponsor": { "url": "https://opencollective.com/vitest" }, - "keywords": [ - "vitest", - "test", - "typescript", - "javascript" - ], - "categories": [ - "Testing" - ], + "publisher": "vitest", + "type": "commonjs", "main": "./dist/extension.js", - "icon": "img/icon.png", - "pricing": "Free", - "engines": { - "vscode": "^1.88.0" + "scripts": { + "vscode:prepublish": "pnpm build", + "release": "tsx ./scripts/release.mts && git update-ref refs/heads/release refs/heads/main && git push origin release", + "build": "tsdown --minify --clean", + "package": "vsce package --no-dependencies", + "dev": "EXTENSION_NODE_ENV=dev tsdown --watch --sourcemap", + "test": "vscode-test", + "test:watch": "vscode-test --watch-files src/**/*.ts --watch-files test/**/*.test.ts", + "test-e2e": "vitest --root test/e2e", + "test-e2e:legacy": "TEST_LEGACY=true vitest --root test/e2e", + "ecosystem-ci:build": "pnpm build", + "ecosystem-ci:test": "tsx ./scripts/ecosystem-ci.mts", + "typecheck": "tsc -b ./ --noEmit", + "fmt": "oxfmt --check", + "fmt:fix": "oxfmt" + }, + "devDependencies": { + "@playwright/test": "catalog:", + "@types/chai": "catalog:", + "@types/micromatch": "catalog:", + "@types/mocha": "catalog:", + "@types/node": "catalog:", + "@types/prompts": "catalog:", + "@types/semver": "catalog:", + "@types/vscode": "^1.77.0", + "@types/which": "catalog:", + "@types/ws": "catalog:", + "@vscode/test-cli": "catalog:", + "@vscode/test-electron": "catalog:", + "@vscode/vsce": "catalog:", + "@vue/reactivity": "catalog:", + "acorn": "catalog:", + "acorn-walk": "catalog:", + "birpc": "catalog:", + "bumpp": "catalog:", + "chai": "catalog:", + "changelogithub": "catalog:", + "execa": "catalog:", + "find-up": "catalog:", + "get-port": "catalog:", + "istanbul-to-vscode": "catalog:", + "micromatch": "catalog:", + "mighty-promise": "catalog:", + "mocha": "catalog:", + "oxfmt": "^0.37.0", + "pathe": "catalog:", + "picocolors": "catalog:", + "prompts": "catalog:", + "semver": "catalog:", + "tsdown": "catalog:", + "tsx": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:latest", + "which": "catalog:", + "ws": "catalog:" }, - "activationEvents": [ - "workspaceContains:**/*{vite,vitest}*.config*.{ts,js,mjs,cjs,cts,mts}", - "workspaceContains:**/*vitest.{workspace,projects}*.{ts,js,mjs,cjs,cts,mts,json}", - "workspaceContains:node_modules/.bin/vitest", - "workspaceContains:node_modules/.bin/vp" - ], "contributes": { "languages": [ { @@ -347,7 +391,11 @@ "description": "Which runtime to use by default.", "type": "string", "default": "auto", - "enum": ["auto", "node", "deno"] + "enum": [ + "auto", + "node", + "deno" + ] }, "vitest.watchOnStartup": { "description": "Watch every test file after the extension is loaded. This is the same as enabling continuous run.", @@ -357,66 +405,16 @@ } } }, - "scripts": { - "vscode:prepublish": "pnpm build", - "release": "tsx ./scripts/release.mts && git update-ref refs/heads/release refs/heads/main && git push origin release", - "build": "tsdown --minify --clean", - "package": "vsce package --no-dependencies", - "dev": "EXTENSION_NODE_ENV=dev tsdown --watch --sourcemap", - "test": "vscode-test", - "test:watch": "vscode-test --watch-files src/**/*.ts --watch-files test/**/*.test.ts", - "test-e2e": "vitest --root test/e2e", - "test-e2e:legacy": "TEST_LEGACY=true vitest --root test/e2e", - "ecosystem-ci:build": "pnpm build", - "ecosystem-ci:test": "tsx ./scripts/ecosystem-ci.mts", - "typecheck": "tsc -b ./ --noEmit", - "lint": "eslint --cache .", - "lint:fix": "eslint --cache --fix ." - }, - "devDependencies": { - "@antfu/eslint-config": "catalog:", - "@playwright/test": "catalog:", - "@types/chai": "catalog:", - "@types/micromatch": "catalog:", - "@types/mocha": "catalog:", - "@types/node": "catalog:", - "@types/prompts": "catalog:", - "@types/semver": "catalog:", - "@types/vscode": "^1.77.0", - "@types/which": "catalog:", - "@types/ws": "catalog:", - "@vscode/test-cli": "catalog:", - "@vscode/test-electron": "catalog:", - "@vscode/vsce": "catalog:", - "@vue/reactivity": "catalog:", - "acorn": "catalog:", - "acorn-walk": "catalog:", - "birpc": "catalog:", - "bumpp": "catalog:", - "chai": "catalog:", - "changelogithub": "catalog:", - "eslint": "catalog:", - "execa": "catalog:", - "find-up": "catalog:", - "get-port": "catalog:", - "istanbul-to-vscode": "catalog:", - "micromatch": "catalog:", - "mighty-promise": "catalog:", - "mocha": "catalog:", - "pathe": "catalog:", - "picocolors": "catalog:", - "prompts": "catalog:", - "semver": "catalog:", - "tsdown": "catalog:", - "tsx": "catalog:", - "typescript": "catalog:", - "vitest": "catalog:latest", - "which": "catalog:", - "ws": "catalog:" + "activationEvents": [ + "workspaceContains:**/*vitest.{workspace,projects}*.{ts,js,mjs,cjs,cts,mts,json}", + "workspaceContains:**/*{vite,vitest}*.config*.{ts,js,mjs,cjs,cts,mts}", + "workspaceContains:node_modules/.bin/vitest", + "workspaceContains:node_modules/.bin/vp" + ], + "icon": "img/icon.png", + "engines": { + "vscode": "^1.88.0" }, - "lint-staged": { - "*.{js,ts,tsx,vue,md}": [ - "eslint --fix" - ] - } + "packageManager": "pnpm@10.11.1", + "pricing": "Free" } diff --git a/packages/extension/src/api.ts b/packages/extension/src/api.ts index 38da74e..60ce8cd 100644 --- a/packages/extension/src/api.ts +++ b/packages/extension/src/api.ts @@ -1,4 +1,8 @@ -import type { ExtensionTestFileSpecification, ExtensionTestSpecification, ModuleDefinitionDurationsDiagnostic } from 'vitest-vscode-shared' +import type { + ExtensionTestFileSpecification, + ExtensionTestSpecification, + ModuleDefinitionDurationsDiagnostic, +} from 'vitest-vscode-shared' import type * as vscode from 'vscode' import type { VitestPackage } from './spawn/pkg' import { dirname, isAbsolute } from 'node:path' @@ -8,18 +12,19 @@ import { log } from './log' import { showVitestError } from './utils' export class VitestAPI { - constructor( - public readonly processes: VitestProcessAPI[], - ) {} + constructor(public readonly processes: VitestProcessAPI[]) {} async getSourceModuleDiagnostic(moduleId: string) { const allDiagnostic = await Promise.all( - this.processes.map(api => api.getSourceModuleDiagnostic(moduleId)), + this.processes.map((api) => api.getSourceModuleDiagnostic(moduleId)), ) const modules = allDiagnostic[0]?.modules || [] const untrackedModules = allDiagnostic[0]?.untrackedModules || [] - type TimeDiagnostic = Pick + type TimeDiagnostic = Pick< + ModuleDefinitionDurationsDiagnostic, + 'selfTime' | 'totalTime' | 'transformTime' | 'resolvedId' + > const aggregateModules = (aggregatedModule: TimeDiagnostic, currentMod: TimeDiagnostic) => { if (aggregatedModule.resolvedId === currentMod.resolvedId) { aggregatedModule.selfTime += currentMod.selfTime @@ -63,7 +68,7 @@ export class VitestAPI { } async dispose() { - await Promise.all(this.processes.map(api => api.dispose())) + await Promise.all(this.processes.map((api) => api.dispose())) } } @@ -74,10 +79,13 @@ export async function resolveVitestAPI( onResolved?: (result: DiscoveryResult) => void, ) { const usedConfigs = new Set() - const workspacePromises = workspaceConfigs.map(pkg => createVitestProcessAPI(usedConfigs, pkg)) + const workspacePromises = workspaceConfigs.map((pkg) => createVitestProcessAPI(usedConfigs, pkg)) if (workspacePromises.length) { - log.info('[API]', `Resolving workspace configs: ${workspaceConfigs.map(p => relative(p.folder.uri.fsPath, p.id)).join(', ')}`) + log.info( + '[API]', + `Resolving workspace configs: ${workspaceConfigs.map((p) => relative(p.folder.uri.fsPath, p.id)).join(', ')}`, + ) } const resolvedApisPromises = await Promise.allSettled(workspacePromises) @@ -87,39 +95,49 @@ export async function resolveVitestAPI( if (result.status === 'fulfilled') { apis.push(result.value.api) onResolved?.(result.value) - } - else { + } else { errors.push(result.reason) } } - const configsToResolve = configs.filter((pkg) => { - return !pkg.configFile || pkg.workspaceFile || !usedConfigs.has(pkg.configFile) - }).sort((a, b) => { - const depthA = a.id.split('/').length - const depthB = b.id.split('/').length - return depthA - depthB - }) + const configsToResolve = configs + .filter((pkg) => { + return !pkg.configFile || pkg.workspaceFile || !usedConfigs.has(pkg.configFile) + }) + .sort((a, b) => { + const depthA = a.id.split('/').length + const depthB = b.id.split('/').length + return depthA - depthB + }) const workspaceRoots: string[] = apis - .map(r => r.workspaceSource ? dirname(r.workspaceSource) : null) - .filter(r => r != null) + .map((r) => (r.workspaceSource ? dirname(r.workspaceSource) : null)) + .filter((r) => r != null) if (configsToResolve.length) { - log.info('[API]', `Resolving configs: ${configsToResolve.map(p => relative(dirname(p.cwd), p.id)).join(', ')}`) + log.info( + '[API]', + `Resolving configs: ${configsToResolve.map((p) => relative(dirname(p.cwd), p.id)).join(', ')}`, + ) } // one by one because it's possible some of them have "workspace:" -- the configs are already sorted by priority for (const pkg of configsToResolve) { // if the config is used by the workspace, ignore the config if (pkg.configFile && usedConfigs.has(pkg.configFile)) { - log.info('[API]', `Ignoring config ${relative(dirname(pkg.cwd), pkg.id)} because it's already used by the workspace`) + log.info( + '[API]', + `Ignoring config ${relative(dirname(pkg.cwd), pkg.id)} because it's already used by the workspace`, + ) continue } // if the config is defined in the directory that is covered by the workspace, ignore the config if (pkg.configFile && isCoveredByWorkspace(workspaceRoots, pkg.configFile)) { - log.info('[API]', `Ignoring config ${relative(dirname(pkg.cwd), pkg.id)} because there is a workspace config in the parent folder`) + log.info( + '[API]', + `Ignoring config ${relative(dirname(pkg.cwd), pkg.id)} because there is a workspace config in the parent folder`, + ) continue } @@ -130,8 +148,7 @@ export async function resolveVitestAPI( if (result.api.workspaceSource) { workspaceRoots.push(dirname(result.api.workspaceSource)) } - } - catch (err: unknown) { + } catch (err: unknown) { errors.push(err) } @@ -142,12 +159,11 @@ export async function resolveVitestAPI( if (!apis.length) { log.error('There were errors during config load.') - errors.forEach(e => log.error(e)) + errors.forEach((e) => log.error(e)) throw new Error('The extension could not load any config.') - } - else if (errors.length) { + } else if (errors.length) { log.error('There were errors during config load.') - errors.forEach(e => log.error(e)) + errors.forEach((e) => log.error(e)) showVitestError('The extension could not load some configs') } @@ -166,7 +182,10 @@ interface DiscoveryResult { files: ExtensionTestFileSpecification[] } -async function createVitestProcessAPI(usedConfigs: Set, pkg: VitestPackage): Promise { +async function createVitestProcessAPI( + usedConfigs: Set, + pkg: VitestPackage, +): Promise { return withProcess(pkg, async (meta) => { meta.projects.forEach((project) => { if (project.config) { diff --git a/packages/extension/src/apiProcess.ts b/packages/extension/src/apiProcess.ts index ed63c59..c70521f 100644 --- a/packages/extension/src/apiProcess.ts +++ b/packages/extension/src/apiProcess.ts @@ -34,7 +34,7 @@ export class VitestProjectConfig { } get configs() { - return this.projects.map(p => p.config).filter(n => n != null) + return this.projects.map((p) => p.config).filter((n) => n != null) } get version() { @@ -49,7 +49,9 @@ export class VitestProjectConfig { const metadata: TestFileMetadata[] = [] let fileContent: string for (const project of this.projects) { - if (this.matchesTestGlob(project, file, () => (fileContent ??= readFileSync(file, 'utf-8')))) { + if ( + this.matchesTestGlob(project, file, () => (fileContent ??= readFileSync(file, 'utf-8'))) + ) { metadata.push({ pool: project.pool, project: project.name, @@ -68,10 +70,7 @@ export class VitestProjectConfig { if (pm.isMatch(relativeId, project.include)) { return true } - if ( - project.includeSource?.length - && pm.isMatch(relativeId, project.includeSource) - ) { + if (project.includeSource?.length && pm.isMatch(relativeId, project.includeSource)) { const code = source() if (code.includes('import.meta.vitest')) { return true @@ -170,20 +169,26 @@ export class VitestProcessAPI { return [projectName, filepath] as [string, string] }) const root = this.workspaceFolder.uri.fsPath - log.info('[API]', `Collecting tests: ${tests.map(t => `${relative(root, t[1])}${t[0] ? ` [${t[0]}]` : ''}`).join(', ')}`) + log.info( + '[API]', + `Collecting tests: ${tests.map((t) => `${relative(root, t[1])}${t[0] ? ` [${t[0]}]` : ''}`).join(', ')}`, + ) const projects = [...new Set(tests.map(([projectName]) => projectName))] try { // TODO make sure errors are reported during collection (throw error in the config, for example) - await withProcess(this.config.pkg, async (meta) => { - meta.handlers.onCollected((file, collecting) => { - for (const listener of this.collectionListeners) { - listener(file, collecting) - } - }) - await meta.rpc.collectTests(tests) - }, { projects }) - } - catch (err) { + await withProcess( + this.config.pkg, + async (meta) => { + meta.handlers.onCollected((file, collecting) => { + for (const listener of this.collectionListeners) { + listener(file, collecting) + } + }) + await meta.rpc.collectTests(tests) + }, + { projects }, + ) + } catch (err) { log.error('[API]', 'Collection failed:', err) } }, 300) @@ -233,8 +238,7 @@ export class VitestProcessAPI { } async cancelRun() { - if (!this.currentMeta || this.currentMeta.process.closed) - return + if (!this.currentMeta || this.currentMeta.process.closed) return await this.currentMeta.rpc.cancelRun() } @@ -247,8 +251,7 @@ export class VitestProcessAPI { } async getModuleEnvironments(moduleId: string) { - if (!this.currentMeta || this.currentMeta.process.closed) - return [] + if (!this.currentMeta || this.currentMeta.process.closed) return [] return this.currentMeta.rpc.getModuleEnvironments(normalize(moduleId)) } @@ -267,7 +270,7 @@ export class VitestProcessAPI { if (!this.currentMeta || this.currentMeta.process.closed) { return } - return this.currentMeta.rpc.onFilesChanged(files.map(f => normalize(f))).catch((err) => { + return this.currentMeta.rpc.onFilesChanged(files.map((f) => normalize(f))).catch((err) => { log.error('[API]', 'Failed to notify Vitest about file change', err) }) }) @@ -324,7 +327,10 @@ export interface ResolvedMeta { dispose: () => Promise } -export function spawnVitestProcess(pkg: VitestPackage, options?: ProcessSpawnOptions): Promise { +export function spawnVitestProcess( + pkg: VitestPackage, + options?: ProcessSpawnOptions, +): Promise { const config = getConfig(pkg.folder) if (config.cliArguments && !pkg.arguments) { pkg.arguments = `vitest ${config.cliArguments}` @@ -348,8 +354,7 @@ export async function withProcess( const meta = await spawnVitestProcess(pkg, options) try { return await fn(meta) - } - finally { + } finally { await meta.dispose().catch((err) => { log.error('[API]', 'Failed to close Vitest process', err) }) diff --git a/packages/extension/src/commands/copyErrors.ts b/packages/extension/src/commands/copyErrors.ts index a914f19..14f8844 100644 --- a/packages/extension/src/commands/copyErrors.ts +++ b/packages/extension/src/commands/copyErrors.ts @@ -3,7 +3,10 @@ import * as vscode from 'vscode' import { getTestData, TestCase } from '../testTreeData' import { createTestLabel, getErrorMessage, showVitestError } from '../utils' -export async function copyTestItemErrors(testController: vscode.TestController, testItem: vscode.TestItem | undefined) { +export async function copyTestItemErrors( + testController: vscode.TestController, + testItem: vscode.TestItem | undefined, +) { const errors: string[] = [] const data = testItem && getTestData(testItem) @@ -22,16 +25,14 @@ export async function copyTestItemErrors(testController: vscode.TestController, if (message != null) { errors.push(message) } - } - else if (item.children.size) { - item.children.forEach(item => walk(item)) + } else if (item.children.size) { + item.children.forEach((item) => walk(item)) } } if (testItem) { - testItem.children.forEach(item => walk(item)) - } - else { - testController.items.forEach(item => walk(item)) + testItem.children.forEach((item) => walk(item)) + } else { + testController.items.forEach((item) => walk(item)) } if (errors.length) { await vscode.env.clipboard.writeText(errors.join(`\n${'='.repeat(50)}\n\n`)) @@ -39,14 +40,16 @@ export async function copyTestItemErrors(testController: vscode.TestController, } function createTestItemErrors(item: vscode.TestItem, test: TestCase) { - const errors = test.errors?.map(error => createTestErrorMessage(getErrorMessage(error), error)) + const errors = test.errors?.map((error) => createTestErrorMessage(getErrorMessage(error), error)) if (errors?.length) { const errorLabel = createTestItemLabel(item) return errorLabel + errors.join(`\n${'='.repeat(50)}\n\n`) } } -export async function copyErrorOutput(arg1: { test: vscode.TestItem; message: vscode.TestMessage } | undefined) { +export async function copyErrorOutput( + arg1: { test: vscode.TestItem; message: vscode.TestMessage } | undefined, +) { if (!arg1) { return } @@ -58,7 +61,7 @@ export async function copyErrorOutput(arg1: { test: vscode.TestItem; message: vs return } - const error = data.errors?.find(e => e.__vscode_id === message.contextValue) + const error = data.errors?.find((e) => e.__vscode_id === message.contextValue) if (!error) { showVitestError('Cannot copy the error output. Please, open an issue with reproduction') return @@ -72,27 +75,19 @@ export async function copyErrorOutput(arg1: { test: vscode.TestItem; message: vs function createTestItemLabel(test: vscode.TestItem) { const parts: string[] = [] - parts.push( - `Test: ${createTestLabel(test)}`, - `File: ${test.uri}`, - '', - '', - ) + parts.push(`Test: ${createTestLabel(test)}`, `File: ${test.uri}`, '', '') return parts.join('\n') } function createTestErrorMessage(message: string, error: TestError) { const parts: string[] = [] - parts.push( - message, - ) + parts.push(message) for (const frame of error.stacks || []) { const location = `${frame.file}:${frame.line}:${frame.column}` if (frame.method) { parts.push(` at ${frame.method} (${location})`) - } - else { + } else { parts.push(` at ${location}`) } } diff --git a/packages/extension/src/config.ts b/packages/extension/src/config.ts index 482d8ba..95d6da8 100644 --- a/packages/extension/src/config.ts +++ b/packages/extension/src/config.ts @@ -26,12 +26,8 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) { const folderConfig = vscode.workspace.getConfiguration('vitest', workspaceFolder) const rootConfig = vscode.workspace.getConfiguration('vitest') - const get = (key: string, defaultValue?: T) => getConfigValue( - rootConfig, - folderConfig, - key, - defaultValue, - ) + const get = (key: string, defaultValue?: T) => + getConfigValue(rootConfig, folderConfig, key, defaultValue) const nodeExecutable = get('nodeExecutable') const workspaceConfig = get('workspaceConfig') @@ -42,19 +38,18 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) { '{**/node_modules/**,**/vendor/**,**/.*/**,**/*.d.ts}', )! - const configSearchPatternInclude = get( - 'configSearchPatternInclude', - configGlob, - ) || configGlob + const configSearchPatternInclude = + get('configSearchPatternInclude', configGlob) || configGlob const vitestPackagePath = get('vitestPackagePath') - const resolvedVitestPackagePath = workspaceFolder && vitestPackagePath - ? resolve( - workspaceFolder.uri.fsPath, - // eslint-disable-next-line no-template-curly-in-string - vitestPackagePath.replace('${workspaceFolder}', workspaceFolder.uri.fsPath), - ) - : vitestPackagePath + const resolvedVitestPackagePath = + workspaceFolder && vitestPackagePath + ? resolve( + workspaceFolder.uri.fsPath, + // eslint-disable-next-line no-template-curly-in-string + vitestPackagePath.replace('${workspaceFolder}', workspaceFolder.uri.fsPath), + ) + : vitestPackagePath const logLevel = get('logLevel', 'info') @@ -108,8 +103,7 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) { } export function resolveConfigPath(path: string | undefined) { - if (!path || isAbsolute(path)) - return path + if (!path || isAbsolute(path)) return path if (path.startsWith('~/')) { return resolve(homedir(), path.slice(2)) } @@ -119,8 +113,7 @@ export function resolveConfigPath(path: string | undefined) { return resolve(dirname(vscode.workspace.workspaceFile.fsPath), path) const workspaceFolders = vscode.workspace.workspaceFolders // if there is no workspace file, then it's probably a single folder workspace - if (workspaceFolders?.length === 1) - return resolve(workspaceFolders[0].uri.fsPath, path) + if (workspaceFolders?.length === 1) return resolve(workspaceFolders[0].uri.fsPath, path) // if there are still several folders, then we can't reliably resolve the path return path } diff --git a/packages/extension/src/debug.ts b/packages/extension/src/debug.ts index 62568f5..c72aea7 100644 --- a/packages/extension/src/debug.ts +++ b/packages/extension/src/debug.ts @@ -72,11 +72,7 @@ export async function debugTests( name: 'Debug Tests', autoAttachChildProcesses: true, skipFiles, - ...( - config.debugOutFiles?.length - ? { outFiles: config.debugOutFiles } - : {} - ), + ...(config.debugOutFiles?.length ? { outFiles: config.debugOutFiles } : {}), smartStep: true, ...(config.shellType === 'terminal' ? { @@ -86,8 +82,7 @@ export async function debugTests( program: workerPath, runtimeArgs, runtimeExecutable, - } - ), + }), cwd: pkg.cwd, env: { ...process.env, @@ -107,23 +102,20 @@ export async function debugTests( if (debugManager.sessions.size) { await Promise.all( - Array.from(debugManager.sessions, session => vscode.debug.stopDebugging(session)), + Array.from(debugManager.sessions, (session) => vscode.debug.stopDebugging(session)), ).catch((error) => { log.error('[DEBUG] Failed to stop debugging sessions', error) }) } - vscode.debug.startDebugging( - pkg.folder, - debugConfig, - { suppressDebugView: true }, - ).then( + vscode.debug.startDebugging(pkg.folder, debugConfig, { suppressDebugView: true }).then( (fulfilled) => { if (fulfilled) { log.info('[DEBUG] Debugging started') - } - else { - deferredPromise.reject(new Error('Failed to start debugging. See output for more information.')) + } else { + deferredPromise.reject( + new Error('Failed to start debugging. See output for more information.'), + ) log.error('[DEBUG] Debugging failed') } }, @@ -136,18 +128,18 @@ export async function debugTests( const disposables: vscode.Disposable[] = [] - const attachDebug = browserDebug || pkg.runtime === 'deno' - ? { - browser: browserDebug?.browser, - // wdio support this only since Vitest 4.beta-13 - port: config.debuggerPort ?? 9229, - host: browserDebug ? 'localhost' : '127.0.0.1', - } - : undefined + const attachDebug = + browserDebug || pkg.runtime === 'deno' + ? { + browser: browserDebug?.browser, + // wdio support this only since Vitest 4.beta-13 + port: config.debuggerPort ?? 9229, + host: browserDebug ? 'localhost' : '127.0.0.1', + } + : undefined - wss.on( - 'connection', - ws => onWsConnection( + wss.on('connection', (ws) => + onWsConnection( ws, pkg, attachDebug ?? true, @@ -160,9 +152,7 @@ export async function debugTests( try { const api = VitestProcessAPI.forDebug(pkg, { ...metadata, - process: new ExtensionDebugProcess( - metadata.ws, - ), + process: new ExtensionDebugProcess(metadata.ws), }) const handle = await api.spawnForRun() const runner = new TestRunner( @@ -182,22 +172,14 @@ export async function debugTests( if (attachDebug) { const attachConfig: vscode.DebugConfiguration = { - __name: browserDebug - ? BrowserDebugSessionName - : AttachSessionName, + __name: browserDebug ? BrowserDebugSessionName : AttachSessionName, __parentId: debugId, - type: browserDebug - ? (browserDebug.browser === 'edge' ? 'msedge' : 'chrome') - : 'node', + type: browserDebug ? (browserDebug.browser === 'edge' ? 'msedge' : 'chrome') : 'node', request: 'attach', name: `Debug Tests (${attachDebug.browser || 'test'})`, address: attachDebug.host, port: attachDebug.port, - ...( - config.debugOutFiles?.length - ? { outFiles: config.debugOutFiles } - : {} - ), + ...(config.debugOutFiles?.length ? { outFiles: config.debugOutFiles } : {}), webRoot: browserDebug?.webRoot, smartStep: true, skipFiles, @@ -210,39 +192,35 @@ export async function debugTests( parentSession = session } } - vscode.debug.startDebugging( - pkg.folder, - attachConfig, - { + vscode.debug + .startDebugging(pkg.folder, attachConfig, { parentSession, // this is required for the "restart" button to work // TODO: but it still doesn't work lifecycleManagedByParent: true, compact: true, - }, - ).then( - (fullfilled) => { - log.info('[DEBUG] Debug session started') - metadata.rpc.onDebugAttached(fullfilled).catch(() => {}) - if (fullfilled) { - log.info('[DEBUG] Debug session attached') - } - else { - log.error('[DEBUG] Debugger failed to attach') - } - }, - (error) => { - metadata.rpc.onDebugAttached(false).catch(() => {}) - log.error('[DEBUG] Attach session failed to launch', error.message) - }, - ) + }) + .then( + (fullfilled) => { + log.info('[DEBUG] Debug session started') + metadata.rpc.onDebugAttached(fullfilled).catch(() => {}) + if (fullfilled) { + log.info('[DEBUG] Debug session attached') + } else { + log.error('[DEBUG] Debugger failed to attach') + } + }, + (error) => { + metadata.rpc.onDebugAttached(false).catch(() => {}) + log.error('[DEBUG] Attach session failed to launch', error.message) + }, + ) } await runner.runTests(request) deferredPromise.resolve() - } - catch (err: any) { + } catch (err: any) { if (err.message.startsWith('[birpc] rpc is closed')) { deferredPromise.resolve() return @@ -268,18 +246,17 @@ export async function debugTests( // dispose all test runners if ( - session.configuration.__name !== BrowserDebugSessionName - && parent - && parent.configuration.__name === DebugSessionName + session.configuration.__name !== BrowserDebugSessionName && + parent && + parent.configuration.__name === DebugSessionName ) { - disposables.reverse().forEach(d => d.dispose()) + disposables.reverse().forEach((d) => d.dispose()) disposables.length = 0 } }) const onDidTerminate = vscode.debug.onDidTerminateDebugSession((session) => { - if (session.configuration.__name !== DebugSessionName) - return + if (session.configuration.__name !== DebugSessionName) return server.close() onDidTerminate.dispose() onDidWorkerTerminate.dispose() @@ -294,15 +271,16 @@ async function getRuntimeOptions(pkg: VitestPackage) { const runtimeArgs = config.nodeExecArgs || [] const pnpLoader = pkg.loader const pnp = pkg.pnp - const execArgv = pnpLoader && pnp - ? [ - '--require', - pnp, - '--experimental-loader', - pathToFileURL(pnpLoader).toString(), - ...runtimeArgs, - ] - : runtimeArgs + const execArgv = + pnpLoader && pnp + ? [ + '--require', + pnp, + '--experimental-loader', + pathToFileURL(pnpLoader).toString(), + ...runtimeArgs, + ] + : runtimeArgs if (config.shellType === 'child_process') { const executable = await findRuntimeExecutable(pkg.runtime, pkg.cwd) return { @@ -382,23 +360,27 @@ function getBrowserDebugInfo(controller: vscode.TestController, request: vscode. `VSCode can only debug tests running in the "chromium" browser. ${testItem.label} runs in ${options.name} instead.`, ) } - if (options.provider === 'webdriverio' && options.name !== 'chrome' && options.name !== 'edge') { + if ( + options.provider === 'webdriverio' && + options.name !== 'chrome' && + options.name !== 'edge' + ) { throw new Error( `VSCode can only debug tests running in the "chrome" or "edge" browser. ${testItem.label} runs in ${options.name} instead.`, ) } if (options.provider === 'preview') { - throw new Error(`Cannot debug tests running in the "preview" provider. Choose either "playwright" or "webdriverio" to be able to debug tests.`) + throw new Error( + `Cannot debug tests running in the "preview" provider. Choose either "playwright" or "webdriverio" to be able to debug tests.`, + ) } provider = options.provider browser = options.name webRootsFound.add(options.webRoot) - } - else if (data instanceof TestFolder) { + } else if (data instanceof TestFolder) { testItem.children.forEach(traverse) - } - else if (data instanceof TestCase || data instanceof TestSuite) { + } else if (data instanceof TestCase || data instanceof TestSuite) { if (testItem.parent) { traverse(testItem.parent) } @@ -407,17 +389,17 @@ function getBrowserDebugInfo(controller: vscode.TestController, request: vscode. if (request.include) { request.include.forEach(traverse) - } - else { + } else { controller.items.forEach(traverse) } let webRoot: string | undefined if (webRootsFound.size === 1) { - [webRoot] = webRootsFound // Grab the first (and only) value - } - else if (webRootsFound.size > 1) { - log.info('[DEBUG] Multiple webRoots found for browser debugging. Breakpoints in source code may not work as expected. Try debugging again by selecting specific tests or test files to debug.') + ;[webRoot] = webRootsFound // Grab the first (and only) value + } else if (webRootsFound.size > 1) { + log.info( + '[DEBUG] Multiple webRoots found for browser debugging. Breakpoints in source code may not work as expected. Try debugging again by selecting specific tests or test files to debug.', + ) } return provider && browser ? { provider, browser, webRoot } : null diff --git a/packages/extension/src/diagnostic.ts b/packages/extension/src/diagnostic.ts index 1386850..7c67954 100644 --- a/packages/extension/src/diagnostic.ts +++ b/packages/extension/src/diagnostic.ts @@ -4,7 +4,7 @@ export class ExtensionDiagnostic { private diagnostic = vscode.languages.createDiagnosticCollection('Vitest') addDiagnostic(testFile: vscode.Uri, errors: vscode.TestMessage[]) { - const diagnostics: vscode.Diagnostic[] = [...this.diagnostic.get(testFile) || []] + const diagnostics: vscode.Diagnostic[] = [...(this.diagnostic.get(testFile) || [])] errors.forEach((error) => { const range = error.location?.range if (!range) { diff --git a/packages/extension/src/extension.ts b/packages/extension/src/extension.ts index a2e5d09..c40ceaf 100644 --- a/packages/extension/src/extension.ts +++ b/packages/extension/src/extension.ts @@ -56,22 +56,23 @@ class VitestExtension { _debugDisposable: vscode.Disposable | undefined constructor(context: vscode.ExtensionContext) { - log.info(`[v${version}] Vitest extension is activated because Vitest is installed or there is a Vite/Vitest config file in the workspace.`) + log.info( + `[v${version}] Vitest extension is activated because Vitest is installed or there is a Vite/Vitest config file in the workspace.`, + ) this.state = new ExtensionState(context) this.testController = vscode.tests.createTestController(testControllerId, 'Vitest') - this.testController.refreshHandler = cancelToken => this.defineTestProfiles(true, cancelToken).catch((err) => { - showVitestError('Failed to refresh Vitest', err) - }) - this.testController.resolveHandler = item => this.resolveTestFile(item) + this.testController.refreshHandler = (cancelToken) => + this.defineTestProfiles(true, cancelToken).catch((err) => { + showVitestError('Failed to refresh Vitest', err) + }) + this.testController.resolveHandler = (item) => this.resolveTestFile(item) this.loadingTestItem = this.testController.createTestItem('_resolving', 'Resolving Vitest...') this.loadingTestItem.sortText = '.0' // show it first - this.schemaProvider = new TransformSchemaProvider( - async (apiId, project, environment, file) => { - const api = this.api?.processes.find(a => a.id === apiId) - return api?.getTransformedModule(project, environment, file) ?? null - }, - ) + this.schemaProvider = new TransformSchemaProvider(async (apiId, project, environment, file) => { + const api = this.api?.processes.find((a) => a.id === apiId) + return api?.getTransformedModule(project, environment, file) ?? null + }) this.tagsManager = new TagsManager() this.testTree = new TestTree( this.testController, @@ -81,10 +82,11 @@ class VitestExtension { ) this.debugManager = new DebugManager() this.importsBreakdownProvider = new ImportsBreakdownProvider( - async (moduleId: string) => this.api?.getSourceModuleDiagnostic(moduleId) || { - modules: [], - untrackedModules: [], - }, + async (moduleId: string) => + this.api?.getSourceModuleDiagnostic(moduleId) || { + modules: [], + untrackedModules: [], + }, ) this.inlineConsoleLog = new InlineConsoleLogManager(this.testTree) } @@ -93,7 +95,8 @@ class VitestExtension { private async defineTestProfiles(showWarning: boolean, cancelToken?: vscode.CancellationToken) { if (!this._defineTestProfilePromise) { - this._defineTestProfilePromise = (() => this._defineTestProfiles(showWarning, cancelToken))().finally(() => { + this._defineTestProfilePromise = (() => + this._defineTestProfiles(showWarning, cancelToken))().finally(() => { this._defineTestProfilePromise = undefined }) } @@ -104,7 +107,7 @@ class VitestExtension { this.importsBreakdownProvider.clear() this.inlineConsoleLog.clear() this.testTree.reset([]) - this.runQueues.forEach(q => q.dispose()) + this.runQueues.forEach((q) => q.dispose()) this.runQueues.clear() const { workspaces, configs } = await resolveVitestPackages(showWarning) @@ -121,7 +124,7 @@ class VitestExtension { return } - const folders = new Set([...workspaces, ...configs].map(x => x.folder)) + const folders = new Set([...workspaces, ...configs].map((x) => x.folder)) this.testTree.reset([...folders]) const previousRunProfiles = this.runProfiles @@ -138,27 +141,30 @@ class VitestExtension { profile.dispose() } - this.api = await resolveVitestAPI(workspaces, configs, cancelToken, ({ api: vitest, files }) => { - if (this.state.hasDisabledConfigs() && this.state.isConfigDisabled(vitest.id)) { - return - } + this.api = await resolveVitestAPI( + workspaces, + configs, + cancelToken, + ({ api: vitest, files }) => { + if (this.state.hasDisabledConfigs() && this.state.isConfigDisabled(vitest.id)) { + return + } - this.testTree.watchTestFilesInWorkspace(vitest, files) - this.setupProcessAPI(vitest) + this.testTree.watchTestFilesInWorkspace(vitest, files) + this.setupProcessAPI(vitest) - this.testController.items.forEach((item) => { - if (item.children.size) { - item.busy = false - } - }) - }) - } - catch (err) { + this.testController.items.forEach((item) => { + if (item.children.size) { + item.busy = false + } + }) + }, + ) + } catch (err) { this.testTree.reset([]) showVitestError('Failed to start Vitest', err) return - } - finally { + } finally { this.testController.items.delete(this.loadingTestItem.id) } @@ -293,20 +299,16 @@ class VitestExtension { } private async resolveTestFile(item?: vscode.TestItem) { - if (!item) - return + if (!item) return try { await this.testTree.discoverTestsInFile(item) - } - catch (err) { + } catch (err) { showVitestError('There was an error during test discovery', err) } } async activate() { - this.diagnostic = getConfig().applyDiagnostic - ? new ExtensionDiagnostic() - : undefined + this.diagnostic = getConfig().applyDiagnostic ? new ExtensionDiagnostic() : undefined this.loadingTestItem.busy = true this.testController.items.replace([this.loadingTestItem]) @@ -328,8 +330,11 @@ class VitestExtension { this.disposables = [ vscode.workspace.onDidChangeConfiguration((event) => { - const configName = reloadConfigNames.find(x => event.affectsConfiguration(x)) - if (event.affectsConfiguration('vitest.runtime') || event.affectsConfiguration('deno.enabled')) { + const configName = reloadConfigNames.find((x) => event.affectsConfiguration(x)) + if ( + event.affectsConfiguration('vitest.runtime') || + event.affectsConfiguration('deno.enabled') + ) { clearCachedRuntime() } if (configName) { @@ -338,52 +343,64 @@ class VitestExtension { }) } }), - vscode.workspace.onDidChangeWorkspaceFolders(() => this.defineTestProfiles(false).catch((error) => { - log.error('[API]', `Failed to reload Vitest after workspaces changed`, error) - })), + vscode.workspace.onDidChangeWorkspaceFolders(() => + this.defineTestProfiles(false).catch((error) => { + log.error('[API]', `Failed to reload Vitest after workspaces changed`, error) + }), + ), vscode.commands.registerCommand('vitest.openOutput', () => { log.openOuput() }), - vscode.commands.registerCommand('vitest.toggleContinuousRun', async (testItem?: vscode.TestItem) => { - if (!testItem) { - return - } - this.api?.processes.forEach((process) => { - const processId = `${process.id}:run` - const runProfile = this.runProfiles.get(processId) - const queue = this.runQueues.get(processId) - if (runProfile && testItem.tags.includes(runProfile.tag!) && queue) { - if (queue.isContinuousTestItem(testItem)) { - vscode.commands.executeCommand('vscode.stopContinuousTestRun', [testItem]) - } - else { - vscode.commands.executeCommand('vscode.startContinuousTestRun', runProfile, [testItem]) + vscode.commands.registerCommand( + 'vitest.toggleContinuousRun', + async (testItem?: vscode.TestItem) => { + if (!testItem) { + return + } + this.api?.processes.forEach((process) => { + const processId = `${process.id}:run` + const runProfile = this.runProfiles.get(processId) + const queue = this.runQueues.get(processId) + if (runProfile && testItem.tags.includes(runProfile.tag!) && queue) { + if (queue.isContinuousTestItem(testItem)) { + vscode.commands.executeCommand('vscode.stopContinuousTestRun', [testItem]) + } else { + vscode.commands.executeCommand('vscode.startContinuousTestRun', runProfile, [ + testItem, + ]) + } } + }) + }, + ), + vscode.commands.registerCommand( + 'vitest.revealInTestExplorer', + async (uri: vscode.Uri | undefined) => { + if (uri === undefined) { + uri = vscode.window.activeTextEditor?.document.uri } - }) - }), - vscode.commands.registerCommand('vitest.revealInTestExplorer', async (uri: vscode.Uri | undefined) => { - if (uri === undefined) { - uri = vscode.window.activeTextEditor?.document.uri - } - if (!(uri instanceof vscode.Uri)) { - return - } - const testItems = this.testTree.getFileTestItems(uri.fsPath) - if (testItems[0]) { - vscode.commands.executeCommand('vscode.revealTestInExplorer', testItems[0]) - } - }), + if (!(uri instanceof vscode.Uri)) { + return + } + const testItems = this.testTree.getFileTestItems(uri.fsPath) + if (testItems[0]) { + vscode.commands.executeCommand('vscode.revealTestInExplorer', testItems[0]) + } + }, + ), vscode.commands.registerCommand('vitest.showShellTerminal', async () => { - const apis = this.api?.processes - .filter(api => api.getPersistentProcessMeta()?.process instanceof ExtensionTerminalProcess) + const apis = this.api?.processes.filter( + (api) => api.getPersistentProcessMeta()?.process instanceof ExtensionTerminalProcess, + ) if (!apis?.length) { - vscode.window.showInformationMessage('No shell terminals found. Did you change `vitest.shellType` to `terminal` in the configuration? Do you have any continuous runs active?') + vscode.window.showInformationMessage( + 'No shell terminals found. Did you change `vitest.shellType` to `terminal` in the configuration? Do you have any continuous runs active?', + ) return } if (apis.length === 1) { - log.info('Showing the only available shell terminal'); - (apis[0].getPersistentProcessMeta()?.process as ExtensionTerminalProcess).show() + log.info('Showing the only available shell terminal') + ;(apis[0].getPersistentProcessMeta()?.process as ExtensionTerminalProcess).show() return } const pick = await vscode.window.showQuickPick( @@ -399,71 +416,80 @@ class VitestExtension { pick.process.show() } }), - vscode.commands.registerCommand('vitest.updateSnapshot', async (testItem: vscode.TestItem | undefined) => { - if (!testItem) - return - const api = this.testTree.getAPIFromTestItem(testItem) - if (!api) - return - const profile = this.runProfiles.get(`${api.id}:run`) - if (!profile) - return - const request = new vscode.TestRunRequest( - [testItem], - undefined, - profile, - false, - ) - Object.assign(request, { updateSnapshots: true }) - const tokenSource = new vscode.CancellationTokenSource() - await profile.runHandler(request, tokenSource.token) - }), - vscode.commands.registerCommand('vitest.openTransformedModule', async (uri: vscode.Uri | undefined) => { - const currentUri = uri || vscode.window.activeTextEditor?.document.uri - if (!this.api || !currentUri || currentUri.scheme === 'vitest-transform') { - return - } - const environments = await this.api.getModuleEnvironments(currentUri.fsPath) - const options = environments.map(({ api, projects }) => { - return projects.map((project) => { - return project.environments.map((environment) => { - let label = '' - if (environments.length > 1) { - label += `${api.prefix}: ` - } - if (project.name) { - label += `[${project.name}] ` - } - label += environment - return { - label, - uriParts: [api.id, project.name, environment.name, environment.transformTimestamp], - } + vscode.commands.registerCommand( + 'vitest.updateSnapshot', + async (testItem: vscode.TestItem | undefined) => { + if (!testItem) return + const api = this.testTree.getAPIFromTestItem(testItem) + if (!api) return + const profile = this.runProfiles.get(`${api.id}:run`) + if (!profile) return + const request = new vscode.TestRunRequest([testItem], undefined, profile, false) + Object.assign(request, { updateSnapshots: true }) + const tokenSource = new vscode.CancellationTokenSource() + await profile.runHandler(request, tokenSource.token) + }, + ), + vscode.commands.registerCommand( + 'vitest.openTransformedModule', + async (uri: vscode.Uri | undefined) => { + const currentUri = uri || vscode.window.activeTextEditor?.document.uri + if (!this.api || !currentUri || currentUri.scheme === 'vitest-transform') { + return + } + const environments = await this.api.getModuleEnvironments(currentUri.fsPath) + const options = environments + .map(({ api, projects }) => { + return projects.map((project) => { + return project.environments.map((environment) => { + let label = '' + if (environments.length > 1) { + label += `${api.prefix}: ` + } + if (project.name) { + label += `[${project.name}] ` + } + label += environment + return { + label, + uriParts: [ + api.id, + project.name, + environment.name, + environment.transformTimestamp, + ], + } + }) + }) }) - }) - }).flat(2) - if (options.length === 0) { - vscode.window.showWarningMessage('All module graphs are empty, nothing to show.') - return - } - const pick = options.length === 1 ? options[0] : await vscode.window.showQuickPick(options) - if (!pick) { - return - } - try { - const [apiId, projectName, environment, t] = pick.uriParts - const uri = vscode.Uri.parse( - `vitest-transform://${currentUri.fsPath}.js?apiId=${apiId}&project=${projectName}&environment=${environment}&t=${t}`, - ) - const doc = await vscode.workspace.openTextDocument(uri) - await vscode.window.showTextDocument(doc, { preview: false }) - } - catch (err) { - log.error(err) - vscode.window.showErrorMessage(`Vitest: The file was not processed by Vite yet. Try running the tests first${options.length > 1 ? ' or select a different environment' : ''}.`) - } - }), - vscode.commands.registerCommand('vitest.copyTestItemErrors', testItem => copyTestItemErrors(this.testController, testItem)), + .flat(2) + if (options.length === 0) { + vscode.window.showWarningMessage('All module graphs are empty, nothing to show.') + return + } + const pick = + options.length === 1 ? options[0] : await vscode.window.showQuickPick(options) + if (!pick) { + return + } + try { + const [apiId, projectName, environment, t] = pick.uriParts + const uri = vscode.Uri.parse( + `vitest-transform://${currentUri.fsPath}.js?apiId=${apiId}&project=${projectName}&environment=${environment}&t=${t}`, + ) + const doc = await vscode.workspace.openTextDocument(uri) + await vscode.window.showTextDocument(doc, { preview: false }) + } catch (err) { + log.error(err) + vscode.window.showErrorMessage( + `Vitest: The file was not processed by Vite yet. Try running the tests first${options.length > 1 ? ' or select a different environment' : ''}.`, + ) + } + }, + ), + vscode.commands.registerCommand('vitest.copyTestItemErrors', (testItem) => + copyTestItemErrors(this.testController, testItem), + ), vscode.commands.registerCommand('vitest.copyErrorOutput', copyErrorOutput), vscode.commands.registerCommand('vitest.toggleConfigs', async () => { if (!this.api) { @@ -488,8 +514,10 @@ class VitestExtension { return } - const enabledKeys = new Set(result.map(i => i.key)) - await this.state.setDisabledConfigs(new Set(items.filter(i => !enabledKeys.has(i.key)).map(i => i.key))) + const enabledKeys = new Set(result.map((i) => i.key)) + await this.state.setDisabledConfigs( + new Set(items.filter((i) => !enabledKeys.has(i.key)).map((i) => i.key)), + ) await this.defineTestProfiles(false) }), @@ -502,39 +530,44 @@ class VitestExtension { ] this.disposables.push(...configWatchers) - const redefineTestProfiles = debounce((uri: vscode.Uri, event: 'create' | 'delete' | 'change') => { - if (!this.api || uri.fsPath.includes('node_modules') || uri.fsPath.includes('.timestamp-')) - return - // if new config is created, always check if it should be respected - if (event === 'create') { - this.defineTestProfiles(false).catch((err) => { - log.error('Failed to define test profiles after a new config file was created', err) - }) - return - } - // otherwise ignore changes to unrelated configs - const filePath = normalize(uri.fsPath) - for (const api of this.api.processes) { - if ( - api.package.workspaceFile === filePath - || api.configs.includes(filePath) - ) { + const redefineTestProfiles = debounce( + (uri: vscode.Uri, event: 'create' | 'delete' | 'change') => { + if (!this.api || uri.fsPath.includes('node_modules') || uri.fsPath.includes('.timestamp-')) + return + // if new config is created, always check if it should be respected + if (event === 'create') { this.defineTestProfiles(false).catch((err) => { - log.error('Failed to define test profiles after a new config file was updated', err) + log.error('Failed to define test profiles after a new config file was created', err) }) return } - } - }, 300) + // otherwise ignore changes to unrelated configs + const filePath = normalize(uri.fsPath) + for (const api of this.api.processes) { + if (api.package.workspaceFile === filePath || api.configs.includes(filePath)) { + this.defineTestProfiles(false).catch((err) => { + log.error('Failed to define test profiles after a new config file was updated', err) + }) + return + } + } + }, + 300, + ) - configWatchers.forEach(watcher => watcher.onDidChange(uri => redefineTestProfiles(uri, 'change'))) - configWatchers.forEach(watcher => watcher.onDidCreate(uri => redefineTestProfiles(uri, 'create'))) - configWatchers.forEach(watcher => watcher.onDidDelete(uri => redefineTestProfiles(uri, 'delete'))) + configWatchers.forEach((watcher) => + watcher.onDidChange((uri) => redefineTestProfiles(uri, 'change')), + ) + configWatchers.forEach((watcher) => + watcher.onDidCreate((uri) => redefineTestProfiles(uri, 'create')), + ) + configWatchers.forEach((watcher) => + watcher.onDidDelete((uri) => redefineTestProfiles(uri, 'delete')), + ) try { await this.defineTestProfiles(true) - } - catch (err) { + } catch (err) { showVitestError('There was an error during Vitest startup', err) } } @@ -548,7 +581,9 @@ class VitestExtension { return } try { - const jsDebugExt = vscode.extensions.getExtension('ms-vscode.js-debug-nightly') || vscode.extensions.getExtension('ms-vscode.js-debug') + const jsDebugExt = + vscode.extensions.getExtension('ms-vscode.js-debug-nightly') || + vscode.extensions.getExtension('ms-vscode.js-debug') await jsDebugExt?.activate() const jsDebug: import('@vscode/js-debug').IExports = jsDebugExt?.exports @@ -561,12 +596,10 @@ class VitestExtension { }, }) this.disposables.push(this._debugDisposable) - } - else { + } else { log.error('Failed to connect to the debug extension. Debugger will open a terminal window.') } - } - catch (err) { + } catch (err) { log.error('Cannot create debug options provider.', err) } } @@ -578,11 +611,11 @@ class VitestExtension { this.schemaProvider.dispose() this.importsBreakdownProvider.dispose() this.inlineConsoleLog.dispose() - this.runProfiles.forEach(p => p.dispose()) + this.runProfiles.forEach((p) => p.dispose()) this.runProfiles.clear() - this.disposables.forEach(d => d.dispose()) + this.disposables.forEach((d) => d.dispose()) this.disposables = [] - this.runQueues.forEach(q => q.dispose()) + this.runQueues.forEach((q) => q.dispose()) this.runQueues.clear() } } diff --git a/packages/extension/src/importsBreakdownProvider.ts b/packages/extension/src/importsBreakdownProvider.ts index 57081fd..651be92 100644 --- a/packages/extension/src/importsBreakdownProvider.ts +++ b/packages/extension/src/importsBreakdownProvider.ts @@ -95,8 +95,7 @@ export class ImportsBreakdownProvider { let color: string | undefined if (overallTime >= 500) { color = 'rgb(248 113 113 / 0.8)' - } - else if (overallTime >= 100) { + } else if (overallTime >= 100) { color = 'rgb(251 146 60 / 0.8)' } @@ -136,7 +135,7 @@ export class ImportsBreakdownProvider { dispose() { this.decorationType.dispose() - this.disposables.forEach(d => d.dispose()) + this.disposables.forEach((d) => d.dispose()) } } diff --git a/packages/extension/src/inlineConsoleLog.ts b/packages/extension/src/inlineConsoleLog.ts index 0415455..8eb7fef 100644 --- a/packages/extension/src/inlineConsoleLog.ts +++ b/packages/extension/src/inlineConsoleLog.ts @@ -19,7 +19,7 @@ export class InlineConsoleLogManager extends vscode.Disposable { constructor(private readonly testTree: TestTree) { super(() => { this.decorationType.dispose() - this.disposables.forEach(d => d.dispose()) + this.disposables.forEach((d) => d.dispose()) this.disposables = [] }) @@ -94,7 +94,7 @@ export class InlineConsoleLogManager extends vscode.Disposable { clear(): void { this.consoleLogsByFile.clear() // Update all visible editors - vscode.window.visibleTextEditors.forEach(editor => this.updateDecorations(editor)) + vscode.window.visibleTextEditors.forEach((editor) => this.updateDecorations(editor)) } clearFile(file: string): void { @@ -130,16 +130,18 @@ export class InlineConsoleLogManager extends vscode.Disposable { return } - const noAnsi = entries.map(e => stripVTControlCharacters(e.content)) + const noAnsi = entries.map((e) => stripVTControlCharacters(e.content)) // Combine multiple console logs on the same line - const content = noAnsi.map(e => this.formatContent(e)).join(' ') + const content = noAnsi.map((e) => this.formatContent(e)).join(' ') const hoverMessage = entries.map((e, index) => { const md = new vscode.MarkdownString() if (e.testItem) { md.supportHtml = true const line = (e.testItem.range?.start.line ?? 0) + 1 - md.appendMarkdown(`[${createTestLabel(e.testItem)}](${e.testItem.uri?.with({ fragment: `L${line}` })})`) + md.appendMarkdown( + `[${createTestLabel(e.testItem)}](${e.testItem.uri?.with({ fragment: `L${line}` })})`, + ) md.appendText('\n') } return md.appendText(noAnsi[index]) @@ -176,6 +178,6 @@ export class InlineConsoleLogManager extends vscode.Disposable { private refresh(): void { // Update all visible editors - vscode.window.visibleTextEditors.forEach(editor => this.updateDecorations(editor)) + vscode.window.visibleTextEditors.forEach((editor) => this.updateDecorations(editor)) } } diff --git a/packages/extension/src/log.ts b/packages/extension/src/log.ts index 93bacae..14e27c1 100644 --- a/packages/extension/src/log.ts +++ b/packages/extension/src/log.ts @@ -8,7 +8,7 @@ import { getConfig } from './config' const logFile = process.env.VITEST_VSCODE_E2E_LOG_FILE! const channel = window.createOutputChannel('Vitest') -const callbacks: Set<((message: string) => void)> = new Set() +const callbacks: Set<(message: string) => void> = new Set() function logToCallbacks(message: string) { for (const callback of callbacks) { @@ -68,19 +68,20 @@ export const log = { } channel.appendLine(message) }, - verbose: getConfig().logLevel === 'verbose' || process.env.VITEST_VSCODE_LOG === 'verbose' - ? (...args: string[]) => { - const time = new Date().toLocaleTimeString() - if (process.env.EXTENSION_NODE_ENV === 'dev') { - console.log(`[${time}]`, ...args) + verbose: + getConfig().logLevel === 'verbose' || process.env.VITEST_VSCODE_LOG === 'verbose' + ? (...args: string[]) => { + const time = new Date().toLocaleTimeString() + if (process.env.EXTENSION_NODE_ENV === 'dev') { + console.log(`[${time}]`, ...args) + } + const message = `[${time}] ${args.map(inspectValue).join(' ')}` + if (logFile) { + appendFile(message) + } + channel.appendLine(message) } - const message = `[${time}] ${args.map(inspectValue).join(' ')}` - if (logFile) { - appendFile(message) - } - channel.appendLine(message) - } - : undefined, + : undefined, workspaceInfo: (folder: string, ...args: any[]) => { log.info(`[Workspace ${folder}]`, ...args) }, diff --git a/packages/extension/src/runQueue.ts b/packages/extension/src/runQueue.ts index 20e0447..8ef39a1 100644 --- a/packages/extension/src/runQueue.ts +++ b/packages/extension/src/runQueue.ts @@ -51,9 +51,12 @@ export class RunQueue { return false } - async enqueue(request: vscode.TestRunRequest, token: vscode.CancellationToken, coverage: boolean) { - if (request.continuous) - return this.startContinuousRun(request, token, coverage) + async enqueue( + request: vscode.TestRunRequest, + token: vscode.CancellationToken, + coverage: boolean, + ) { + if (request.continuous) return this.startContinuousRun(request, token, coverage) if (!this.currentRun) { return this.executeRun(request, token, coverage) @@ -68,7 +71,11 @@ export class RunQueue { }) } - private async executeRun(request: vscode.TestRunRequest, token: vscode.CancellationToken, coverage: boolean) { + private async executeRun( + request: vscode.TestRunRequest, + token: vscode.CancellationToken, + coverage: boolean, + ) { this.currentRun = (async () => { // Each "run" click creates a new process to run tests // We don't reuse the established process because it's harder to track @@ -82,8 +89,7 @@ export class RunQueue { const runner = this.createRunner(handle, api) try { await runner.runTests(request) - } - finally { + } finally { runner.dispose() await handle.dispose() } @@ -91,13 +97,11 @@ export class RunQueue { try { await this.currentRun - } - catch (err: any) { + } catch (err: any) { if (!err.message?.startsWith('[birpc] rpc is closed')) { showVitestError('Failed to run tests', err) } - } - finally { + } finally { this.currentRun = undefined this.drainQueue() } @@ -105,7 +109,7 @@ export class RunQueue { private drainQueue() { if (this.disposed) { - this.pendingQueue.forEach(p => p.resolveWithoutRunning()) + this.pendingQueue.forEach((p) => p.resolveWithoutRunning()) this.pendingQueue.length = 0 return } @@ -118,7 +122,11 @@ export class RunQueue { private continuousTimer: NodeJS.Timeout | undefined - private async startContinuousRun(request: vscode.TestRunRequest, token: vscode.CancellationToken, coverage: boolean) { + private async startContinuousRun( + request: vscode.TestRunRequest, + token: vscode.CancellationToken, + coverage: boolean, + ) { this.continuousRequests.add(request) token.onCancellationRequested(() => { @@ -152,8 +160,7 @@ export class RunQueue { // it's possible that request was cancelled before we spawn the process if (this.continuousRequests.size) { await handle.runner.syncWatcher() - } - else { + } else { log.verbose?.('Closing the continues process because requests were cancelled.') await handle.dispose() } @@ -223,7 +230,7 @@ export class RunQueue { dispose() { this.disposed = true - this.pendingQueue.forEach(p => p.resolveWithoutRunning()) + this.pendingQueue.forEach((p) => p.resolveWithoutRunning()) this.pendingQueue.length = 0 this.api.cancelRun() } @@ -248,13 +255,11 @@ function includesTestItem(item: vscode.TestItem, testItem: vscode.TestItem): boo function getProjectsFromRequest(request: vscode.TestRunRequest): string[] | undefined { const include = request.include - if (!include?.length) - return undefined + if (!include?.length) return undefined const projects = new Set() for (const test of include) { const data = getTestData(test) - if (data instanceof TestFolder) - return undefined + if (data instanceof TestFolder) return undefined const project = data instanceof TestFile ? data.project : data.file.project projects.add(project) } diff --git a/packages/extension/src/runner.ts b/packages/extension/src/runner.ts index 6b6e1cb..3e673dd 100644 --- a/packages/extension/src/runner.ts +++ b/packages/extension/src/runner.ts @@ -37,7 +37,7 @@ export class TestRunner extends vscode.Disposable { super(() => { log.verbose?.('Disposing test runner') this.endTestRun() - this.disposables.forEach(d => d.dispose()) + this.disposables.forEach((d) => d.dispose()) this.disposables = [] log.offWorkerLog(this.onWorkerLog) }) @@ -45,8 +45,7 @@ export class TestRunner extends vscode.Disposable { log.onWorkerLog(this.onWorkerLog) handle.handlers.onTestRunStart((files) => { - if (!files.length) - return + if (!files.length) return files.forEach((file) => { const uri = vscode.Uri.file(file) @@ -74,8 +73,7 @@ export class TestRunner extends vscode.Disposable { handle.handlers.onCollected((file, collecting) => { this.tree.collectFile(this.api, file) - if (collecting) - return + if (collecting) return this.importsBreakdown.refreshCurrentDecorations() @@ -86,24 +84,20 @@ export class TestRunner extends vscode.Disposable { return } const testRun = this.testRun - if (!testRun) - return + if (!testRun) return if (task.mode === 'skip' || task.mode === 'todo') { const include = this.testRunRequest?.include if (this.testRunRequest && (!include || this.isTestIncluded(test, include))) { log.verbose?.(`Marking "${test.label}" as skipped`) testRun.skipped(test) - } - else { + } else { log.verbose?.(`Ignore "${test.label}" during collection`) } - } - else if (!task.result && task.type !== 'suite') { + } else if (!task.result && task.type !== 'suite') { log.verbose?.(`Enqueuing "${test.label}"`) testRun.enqueued(test) - } - else { + } else { this.markResult(testRun, test, task.result) } }) @@ -113,8 +107,7 @@ export class TestRunner extends vscode.Disposable { const testRun = this.testRun if (!testRun) { - if (unhandledError) - log.error(unhandledError) + if (unhandledError) log.error(unhandledError) this.endTestRun() return } @@ -125,11 +118,9 @@ export class TestRunner extends vscode.Disposable { }) } - if (unhandledError) - testRun.appendOutput(formatTestOutput(unhandledError)) + if (unhandledError) testRun.appendOutput(formatTestOutput(unhandledError)) - if (!collecting) - this.endTestRun() + if (!collecting) this.endTestRun() }) handle.handlers.onConsoleLog((consoleLog) => { @@ -140,8 +131,7 @@ export class TestRunner extends vscode.Disposable { private onWorkerLog = (message: string) => { if (this.testRun) { this.testRun.appendOutput(formatTestOutput(message)) - } - else if (message) { + } else if (message) { // So we don't lose the log. Ideally, we should start runner sooner log.verbose?.('[WORKER]', stripVTControlCharacters(message)) } @@ -175,11 +165,9 @@ export class TestRunner extends vscode.Disposable { const tests = request.include || [] const files = getTestFiles(tests) - const testFiles = files.filter(f => !(typeof f === 'string' ? f : f[1]).endsWith('/')) - const testRunName = testFiles.length === 1 - ? this.relative(testFiles[0]) - : undefined - const run = this.testRun = this.createCancellableTestRun(request, testRunName) + const testFiles = files.filter((f) => !(typeof f === 'string' ? f : f[1]).endsWith('/')) + const testRunName = testFiles.length === 1 ? this.relative(testFiles[0]) : undefined + const run = (this.testRun = this.createCancellableTestRun(request, testRunName)) this.testRunRequest = request const testItems = request.include || this.controller.items @@ -192,7 +180,7 @@ export class TestRunner extends vscode.Disposable { } test.children.forEach(enqueue) } - testItems.forEach(test => enqueue(test)) + testItems.forEach((test) => enqueue(test)) const runTests = (files?: ExtensionTestSpecification[] | string[], testNamePatern?: string) => 'updateSnapshots' in request @@ -203,30 +191,33 @@ export class TestRunner extends vscode.Disposable { const root = this.api.workspaceFolder.uri.fsPath log.info(`Running all tests in ${basename(root)}`) await runTests() - } - else { + } else { const testNamePatern = formatTestPattern(tests) if (testNamePatern) log.info(`Running ${files.length} file(s) with name pattern: ${testNamePatern}`) else - log.info(`Running ${files.length} file(s):`, files.map(f => this.relative(f))) + log.info( + `Running ${files.length} file(s):`, + files.map((f) => this.relative(f)), + ) await runTests(files, testNamePatern) } } - private isTestIncluded(test: vscode.TestItem, include: readonly vscode.TestItem[] | vscode.TestItemCollection) { + private isTestIncluded( + test: vscode.TestItem, + include: readonly vscode.TestItem[] | vscode.TestItemCollection, + ) { for (const _item of include) { const item = 'id' in _item ? _item : _item[1] - if (item === test) - return true - if (this.isTestIncluded(test, item.children)) - return true + if (item === test) return true + if (this.isTestIncluded(test, item.children)) return true } return false } protected createCancellableTestRun(request: vscode.TestRunRequest, name?: string) { - const run = this.testRun = this.controller.createTestRun(request, name) + const run = (this.testRun = this.controller.createTestRun(request, name)) run.token.onCancellationRequested(() => { this.triggerCancel(this.testRunRequest) @@ -237,17 +228,13 @@ export class TestRunner extends vscode.Disposable { public async reportCoverage(coverage: any) { const testRun = this.testRun - if (!testRun) - return + if (!testRun) return // TODO: quick patch, coverage shouldn't report negative columns function ensureLoc(loc: any) { - if (!loc) - return - if (loc.start?.column && loc.start.column < 0) - loc.start.column = 0 - if (loc.end?.column && loc.end.column < 0) - loc.end.column = 0 + if (!loc) return + if (loc.start?.column && loc.start.column < 0) loc.start.column = 0 + if (loc.end?.column && loc.end.column < 0) loc.end.column = 0 } for (const file in coverage) { coverage[file] = coverage[file].data @@ -263,24 +250,18 @@ export class TestRunner extends vscode.Disposable { await coverageContext.applyJson(testRun, coverage) } - private markTestCase( - testRun: vscode.TestRun, - test: vscode.TestItem, - result: RunnerTaskResult, - ) { + private markTestCase(testRun: vscode.TestRun, test: vscode.TestItem, result: RunnerTaskResult) { setTestErrors(test, result.errors as TestError[]) switch (result.state) { case 'fail': { - const errors = result.errors?.map(err => - testMessageForTestError(test, err as TestError), - ) || [] + const errors = + result.errors?.map((err) => testMessageForTestError(test, err as TestError)) || [] if (!errors.length) { log.verbose?.(`Test failed, but no errors found for "${test.label}"`) return } - if (test.uri) - this.diagnostic?.addDiagnostic(test.uri, errors) + if (test.uri) this.diagnostic?.addDiagnostic(test.uri, errors) log.verbose?.(`Marking "${test.label}" as failed with ${errors.length} errors`) testRun.failed(test, errors, result.duration) break @@ -316,9 +297,7 @@ export class TestRunner extends vscode.Disposable { } // errors in a suite are stored only if it happens during discovery - const errors = result.errors?.map(err => - err.stack || err.message, - ) + const errors = result.errors?.map((err) => err.stack || err.message) if (!errors?.length) { log.verbose?.(`No errors found for "${test.label}"`) return @@ -364,7 +343,11 @@ export class ContinuousTestRunner extends TestRunner { super(handle, controller, tree, api, diagnostic, importsBreakdown, inlineConsoleLog) handle.handlers.onTestRunStart((files) => { this.startTestRun(files) - log.verbose?.('Starting a test run because', ...files.map(f => this.relative(f)), 'triggered a watch rerun event') + log.verbose?.( + 'Starting a test run because', + ...files.map((f) => this.relative(f)), + 'triggered a watch rerun event', + ) }) } @@ -373,20 +356,19 @@ export class ContinuousTestRunner extends TestRunner { return } - const include = Array.from(this.continuousRequests, r => r.include || []).flat() + const include = Array.from(this.continuousRequests, (r) => r.include || []).flat() if (!include.length) { await this.handle.rpc.watchTests() log.info('[RUNNER]', 'Watching all test files') - } - else { + } else { const files = getTestFiles(include) const testNamePatern = formatTestPattern(include) await this.handle.rpc.watchTests(files, testNamePatern) log.info( '[RUNNER]', 'Watching test files:', - files.map(f => this.relative(f)).join(', '), + files.map((f) => this.relative(f)).join(', '), testNamePatern ? `with pattern ${testNamePatern}` : '', ) } @@ -403,13 +385,11 @@ export class ContinuousTestRunner extends TestRunner { } if (!request) { - log.verbose?.('No test run request found for', ...files.map(f => this.relative(f))) + log.verbose?.('No test run request found for', ...files.map((f) => this.relative(f))) return } - const name = files.length > 1 - ? undefined - : this.relative(files[0]) + const name = files.length > 1 ? undefined : this.relative(files[0]) this.testRunRequest = request const run = this.createCancellableTestRun(request, name) @@ -422,14 +402,17 @@ export class ContinuousTestRunner extends TestRunner { } // during test collection, we don't have test runs - if (request.include && !this.isFileIncluded(file, request.include)) - continue + if (request.include && !this.isFileIncluded(file, request.include)) continue const testItems = request.include || this.tree.getFileTestItems(file) function enqueue(test: vscode.TestItem) { const testData = getTestData(test) // we only change the state of test cases to keep the correct test count - if (testData instanceof TestCase && !testData.dynamic && files.includes(testData.file.filepath)) { + if ( + testData instanceof TestCase && + !testData.dynamic && + files.includes(testData.file.filepath) + ) { log.verbose?.(`Enqueuing "${test.label}"`) run.enqueued(test) } @@ -441,25 +424,23 @@ export class ContinuousTestRunner extends TestRunner { } test.children.forEach(enqueue) } - testItems.forEach(test => enqueue(test)) + testItems.forEach((test) => enqueue(test)) } } - private isFileIncluded(file: string, include: readonly vscode.TestItem[] | vscode.TestItemCollection) { + private isFileIncluded( + file: string, + include: readonly vscode.TestItem[] | vscode.TestItemCollection, + ) { for (const _item of include) { const item = 'id' in _item ? _item : _item[1] const data = getTestData(item) if (data instanceof TestFile) { - if (data.filepath === file) - return true - } - else if (data instanceof TestFolder) { - if (this.isFileIncluded(file, item.children)) - return true - } - else { - if (data.file.filepath === file) - return true + if (data.filepath === file) return true + } else if (data instanceof TestFolder) { + if (this.isFileIncluded(file, item.children)) return true + } else { + if (data.file.filepath === file) return true } } return false @@ -468,17 +449,16 @@ export class ContinuousTestRunner extends TestRunner { private getTestFilesInFolder(path: string) { const folder = this.tree.getOrCreateFolderTestItem(this.api, path) const items = this.tree.getFolderFiles(folder) - return [...new Set(items.map(item => (getTestData(item) as TestFile).filepath))] + return [...new Set(items.map((item) => (getTestData(item) as TestFile).filepath))] } // It is important to create new requests every time the file is changed, // Otherwise it becomes stale. private createContinuousRequest() { - if (!this.continuousRequests.size) - return undefined + if (!this.continuousRequests.size) return undefined const include = [] for (const request of this.continuousRequests) { - include.push(...request.include || []) + include.push(...(request.include || [])) } return new vscode.TestRunRequest( include.length ? include : undefined, @@ -496,15 +476,21 @@ function setTestErrors(test: vscode.TestItem, errors: TestError[] | undefined) { } } -function testMessageForTestError(testItem: vscode.TestItem, error: TestError | undefined): vscode.TestMessage { - if (!error) - return new vscode.TestMessage('Unknown error') +function testMessageForTestError( + testItem: vscode.TestItem, + error: TestError | undefined, +): vscode.TestMessage { + if (!error) return new vscode.TestMessage('Unknown error') let testMessage - if (error.actual != null && error.expected != null && error.actual !== 'undefined' && error.expected !== 'undefined') + if ( + error.actual != null && + error.expected != null && + error.actual !== 'undefined' && + error.expected !== 'undefined' + ) testMessage = vscode.TestMessage.diff(getErrorMessage(error), error.expected, error.actual) - else - testMessage = new vscode.TestMessage(getErrorMessage(error)) + else testMessage = new vscode.TestMessage(getErrorMessage(error)) setMessageStackFramesFromErrorStacks(testMessage, error.stacks) @@ -525,7 +511,11 @@ export interface DebuggerLocation { column: number } -function getSourceFilepathAndLocationFromStack(stack: ParsedStack): { sourceFilepath?: string; line: number; column: number } { +function getSourceFilepathAndLocationFromStack(stack: ParsedStack): { + sourceFilepath?: string + line: number + column: number +} { return { sourceFilepath: stack.file.replace(/\//g, path.sep), line: stack.line, @@ -533,9 +523,11 @@ function getSourceFilepathAndLocationFromStack(stack: ParsedStack): { sourceFile } } -function parseLocationFromStacks(testItem: vscode.TestItem, stacks: ParsedStack[]): DebuggerLocation | undefined { - if (stacks.length === 0) - return undefined +function parseLocationFromStacks( + testItem: vscode.TestItem, + stacks: ParsedStack[], +): DebuggerLocation | undefined { + if (stacks.length === 0) return undefined const targetFilepath = testItem.uri!.fsPath for (const stack of stacks) { @@ -554,19 +546,24 @@ function parseLocationFromStacks(testItem: vscode.TestItem, stacks: ParsedStack[ log.verbose?.('Could not find a valid stack for', testItem.label, JSON.stringify(stacks, null, 2)) } -function setMessageStackFramesFromErrorStacks(testMessage: vscode.TestMessage, stacks: ParsedStack[] | undefined) { +function setMessageStackFramesFromErrorStacks( + testMessage: vscode.TestMessage, + stacks: ParsedStack[] | undefined, +) { // Error stack frames are available only in ^1.93 - if (!('TestMessageStackFrame' in vscode)) - return - if (!stacks || stacks.length === 0) - return + if (!('TestMessageStackFrame' in vscode)) return + if (!stacks || stacks.length === 0) return const TestMessageStackFrame = vscode.TestMessageStackFrame const frames = stacks.map((stack) => { const { sourceFilepath, line, column } = getSourceFilepathAndLocationFromStack(stack) const sourceUri = sourceFilepath ? vscode.Uri.file(sourceFilepath) : undefined - return new TestMessageStackFrame(stack.method, sourceUri, new vscode.Position(line - 1, column - 1)) + return new TestMessageStackFrame( + stack.method, + sourceUri, + new vscode.Position(line - 1, column - 1), + ) }) testMessage.stackTrace = frames @@ -574,15 +571,20 @@ function setMessageStackFramesFromErrorStacks(testMessage: vscode.TestMessage, s function getTestFiles(tests: readonly vscode.TestItem[]): string[] | ExtensionTestSpecification[] { // if there is a folder, we can't limit the tests to a specific project - const hasFolder = tests.some(test => getTestData(test) instanceof TestFolder) + const hasFolder = tests.some((test) => getTestData(test) instanceof TestFolder) if (hasFolder) { - return [...new Set(tests.map((test) => { - const data = getTestData(test) - const fsPath = normalize(test.uri!.fsPath) - if (data instanceof TestFolder) - return `${fsPath}/` - return fsPath - }).filter(Boolean) as string[])] + return [ + ...new Set( + tests + .map((test) => { + const data = getTestData(test) + const fsPath = normalize(test.uri!.fsPath) + if (data instanceof TestFolder) return `${fsPath}/` + return fsPath + }) + .filter(Boolean) as string[], + ), + ] } const testSpecs: ExtensionTestSpecification[] = [] const testFiles = new Set() @@ -590,12 +592,10 @@ function getTestFiles(tests: readonly vscode.TestItem[]): string[] | ExtensionTe const fsPath = normalize(test.uri!.fsPath) const data = getTestData(test) // just to type guard, actually not possible to have - if (data instanceof TestFolder) - continue + if (data instanceof TestFolder) continue const project = data instanceof TestFile ? data.project : data.file.project const key = `${project}\0${fsPath}` - if (testFiles.has(key)) - continue + if (testFiles.has(key)) continue testFiles.add(key) testSpecs.push([project, fsPath]) } @@ -607,13 +607,15 @@ function formatTestPattern(tests: readonly vscode.TestItem[], patterns: string[] const data = getTestData(test)! // file or a folder, try to include every test in there if (!('getTestNamePattern' in data)) { - formatTestPattern(Array.from(test.children, t => t[1]), patterns) + formatTestPattern( + Array.from(test.children, (t) => t[1]), + patterns, + ) continue } patterns.push(data.getTestNamePattern()) } - if (!patterns.length) - return undefined + if (!patterns.length) return undefined return patterns.join('|') } @@ -622,7 +624,6 @@ function formatTestOutput(output: string) { } function labelTestItems(items: readonly vscode.TestItem[] | undefined) { - if (!items) - return '' - return items.map(p => `"${p.label}"`).join(', ') + if (!items) return '' + return items.map((p) => `"${p.label}"`).join(', ') } diff --git a/packages/extension/src/schemaProvider.ts b/packages/extension/src/schemaProvider.ts index e151f2b..6fe2776 100644 --- a/packages/extension/src/schemaProvider.ts +++ b/packages/extension/src/schemaProvider.ts @@ -1,14 +1,23 @@ import * as vscode from 'vscode' -export class TransformSchemaProvider implements vscode.TextDocumentContentProvider, vscode.Disposable { +export class TransformSchemaProvider + implements vscode.TextDocumentContentProvider, vscode.Disposable +{ private disposables: vscode.Disposable[] = [] private _onDidChangeEvents = new vscode.EventEmitter() constructor( - private getTransformedModule: (apiId: string, project: string, environment: string, file: string) => Promise, + private getTransformedModule: ( + apiId: string, + project: string, + environment: string, + file: string, + ) => Promise, ) { - this.disposables.push(vscode.workspace.registerTextDocumentContentProvider('vitest-transform', this)) + this.disposables.push( + vscode.workspace.registerTextDocumentContentProvider('vitest-transform', this), + ) this.disposables.push(this._onDidChangeEvents) } @@ -38,8 +47,7 @@ export class TransformSchemaProvider implements vscode.TextDocumentContentProvid let _cachedUris = this._cachedFsPaths.get(fsPath) if (!_cachedUris) { _cachedUris = new Set() - } - else { + } else { // remove older files from the same environment _cachedUris.forEach((uri) => { const query = uri.query.replace(/&t=\d+/, '') @@ -61,6 +69,6 @@ export class TransformSchemaProvider implements vscode.TextDocumentContentProvid dispose() { this._cachedFsPaths.clear() - this.disposables.forEach(d => d.dispose()) + this.disposables.forEach((d) => d.dispose()) } } diff --git a/packages/extension/src/spawn/child_process.ts b/packages/extension/src/spawn/child_process.ts index 1bc0d69..feaf9c9 100644 --- a/packages/extension/src/spawn/child_process.ts +++ b/packages/extension/src/spawn/child_process.ts @@ -19,20 +19,20 @@ import { waitForWsConnection } from './ws' export async function createVitestProcess(pkg: VitestPackage, options?: ProcessSpawnOptions) { const pnpLoader = pkg.loader const pnp = pkg.pnp - if (pnpLoader && !pnp) - throw new Error('pnp file is required if loader option is used') + if (pnpLoader && !pnp) throw new Error('pnp file is required if loader option is used') const env = getConfig().env || {} const folderConfig = getConfig(pkg.folder) const runtimeArgs = folderConfig.nodeExecArgs || [] - const execArgv = pnpLoader && pnp - ? [ - '--require', - pnp, - '--experimental-loader', - pathToFileURL(pnpLoader).toString(), - ...runtimeArgs, - ] - : runtimeArgs + const execArgv = + pnpLoader && pnp + ? [ + '--require', + pnp, + '--experimental-loader', + pathToFileURL(pnpLoader).toString(), + ...runtimeArgs, + ] + : runtimeArgs const arvString = execArgv.join(' ') const executable = await findRuntimeExecutable(pkg.runtime, pkg.cwd) let executablePath = workerPath diff --git a/packages/extension/src/spawn/pkg.ts b/packages/extension/src/spawn/pkg.ts index 2de76c1..a7985fc 100644 --- a/packages/extension/src/spawn/pkg.ts +++ b/packages/extension/src/spawn/pkg.ts @@ -38,7 +38,10 @@ function isVitestInPackageJson(root: string) { return false } -function resolveVitestConfig(showWarning: boolean, configOrWorkspaceFile: vscode.Uri): VitestPackage | null { +function resolveVitestConfig( + showWarning: boolean, + configOrWorkspaceFile: vscode.Uri, +): VitestPackage | null { const folder = vscode.workspace.getWorkspaceFolder(configOrWorkspaceFile)! if (!folder) throw new Error(`Workspace folder not found for ${configOrWorkspaceFile}. Does the file exist?`) @@ -56,10 +59,13 @@ function resolveVitestConfig(showWarning: boolean, configOrWorkspaceFile: vscode `Please run \`${getSuggestedInstallCommand(cwd)}\` to install Vitest. `, ] if (isVitestConfig) { - message.push('You are seeing this message because the extension found a Vitest config in this folder.') - } - else if (isInPkgJson) { - message.push('You are seeing this message because the extension found a "vitest" dependency in the `package.json` file.') + message.push( + 'You are seeing this message because the extension found a Vitest config in this folder.', + ) + } else if (isInPkgJson) { + message.push( + 'You are seeing this message because the extension found a "vitest" dependency in the `package.json` file.', + ) } vscode.window.showWarningMessage(message.join('')) } @@ -88,8 +94,7 @@ function resolveVitestConfig(showWarning: boolean, configOrWorkspaceFile: vscode } const pkg = readPkgJson(vitest.vitestPackageJsonPath) - if (!pkg || !validateVitestPkg(showWarning, vitest.vitestPackageJsonPath, pkg)) - return null + if (!pkg || !validateVitestPkg(showWarning, vitest.vitestPackageJsonPath, pkg)) return null return { folder, @@ -116,17 +121,21 @@ function validateVitestPkg(showWarning: boolean, pkgJsonPath: string, pkg: any) } if (!gte(pkg.version, minimumVersion)) { const warning = `Vitest v${pkg.version} is not supported. Vitest v${minimumVersion} or newer is required.` - if (showWarning) - vscode.window.showWarningMessage(warning) + if (showWarning) vscode.window.showWarningMessage(warning) else - log.error('[API]', `Vitest v${pkg.version} from ${pkgJsonPath} is not supported. Vitest v${minimumVersion} or newer is required.`) + log.error( + '[API]', + `Vitest v${pkg.version} from ${pkgJsonPath} is not supported. Vitest v${minimumVersion} or newer is required.`, + ) delete require.cache[pkgJsonPath] return false } return true } -export async function resolveVitestPackages(showWarning: boolean): Promise<{ configs: VitestPackage[]; workspaces: VitestPackage[] }> { +export async function resolveVitestPackages( + showWarning: boolean, +): Promise<{ configs: VitestPackage[]; workspaces: VitestPackage[] }> { // TODO: update "warned" logic const [workspaceConfigs, configs] = await Promise.all([ resolveVitestWorkspaceConfigs(), @@ -150,8 +159,7 @@ function resolveVitestWorkspacePackages(showWarning: boolean) { vscode.workspace.workspaceFolders?.forEach((folder) => { const cwd = normalize(folder.uri.fsPath) const vitest = resolveVitestPackage(cwd, folder) - if (!vitest) - return + if (!vitest) return const pkg = readPkgJson(vitest.vitestPackageJsonPath) if (!pkg || !validateVitestPkg(showWarning, vitest.vitestPackageJsonPath, pkg)) { @@ -178,7 +186,9 @@ function resolveVitestWorkspacePackages(showWarning: boolean) { } } -export async function resolveVitestPackagesViaPackageJson(showWarning: boolean): Promise<{ meta: VitestPackage[]; warned: boolean }> { +export async function resolveVitestPackagesViaPackageJson( + showWarning: boolean, +): Promise<{ meta: VitestPackage[]; warned: boolean }> { const config = getConfig() const packages = await vscode.workspace.findFiles( @@ -189,20 +199,20 @@ export async function resolveVitestPackagesViaPackageJson(showWarning: boolean): let warned = false const meta: VitestPackage[] = [] packages.forEach((pkgPath) => { - const scripts = Object.entries(readPkgJson(pkgPath.fsPath)?.scripts || {}).filter(([, script]) => { - return typeof script === 'string' && script.startsWith('vitest ') - }) as [string, string][] + const scripts = Object.entries(readPkgJson(pkgPath.fsPath)?.scripts || {}).filter( + ([, script]) => { + return typeof script === 'string' && script.startsWith('vitest ') + }, + ) as [string, string][] - if (!scripts.length) - return + if (!scripts.length) return const folder = vscode.workspace.getWorkspaceFolder(pkgPath)! const cwd = dirname(pkgPath.fsPath) const vitest = resolveVitestPackage(cwd, folder) // skip if Vitest is not installed - if (!vitest) - return + if (!vitest) return const pkg = readPkgJson(vitest.vitestPackageJsonPath) if (!pkg || !validateVitestPkg(showWarning, vitest.vitestPackageJsonPath, pkg)) { @@ -213,8 +223,7 @@ export async function resolveVitestPackagesViaPackageJson(showWarning: boolean): // take only the fist script to not pollute the list const scriptOption = scripts[0] - if (!scriptOption) - return + if (!scriptOption) return const [scriptName, script] = scriptOption @@ -249,8 +258,7 @@ async function resolveVitestWorkspaceConfigs() { return { meta: [], warned: false } } - if (userWorkspace) - log.info('[API] Using user workspace config:', userWorkspace) + if (userWorkspace) log.info('[API] Using user workspace config:', userWorkspace) const vitestWorkspaces = userWorkspace ? [vscode.Uri.file(userWorkspace)] @@ -258,21 +266,28 @@ async function resolveVitestWorkspaceConfigs() { if (vitestWorkspaces.length) { // if there is a workspace config, use it as root - const meta = resolvePackagUniquePrefixes(vitestWorkspaces.map((config) => { - const vitest = resolveVitestConfig(/* don't show warnings for workspaces because they have limited support */ false, config) - if (!vitest) { - return null - } - // Version 4 doesn't support workspace files - if (gte(vitest.version, '4.0.0')) { - return null - } - return { - ...vitest, - configFile: rootConfig, - workspaceFile: vitest.id, - } - }).filter(nonNullable)) + const meta = resolvePackagUniquePrefixes( + vitestWorkspaces + .map((config) => { + const vitest = resolveVitestConfig( + /* don't show warnings for workspaces because they have limited support */ false, + config, + ) + if (!vitest) { + return null + } + // Version 4 doesn't support workspace files + if (gte(vitest.version, '4.0.0')) { + return null + } + return { + ...vitest, + configFile: rootConfig, + workspaceFile: vitest.id, + } + }) + .filter(nonNullable), + ) return { meta, @@ -291,8 +306,7 @@ async function resolveVitestConfigs(showWarning: boolean) { let warned = false - if (rootConfig) - log.info('[API] Using user root config:', rootConfig) + if (rootConfig) log.info('[API] Using user root config:', rootConfig) const configs = rootConfig ? [vscode.Uri.file(rootConfig)] @@ -303,8 +317,7 @@ async function resolveVitestConfigs(showWarning: boolean) { const configsByFolder = configs.reduce>((acc, config) => { const dir = dirname(config.fsPath) - if (!acc[dir]) - acc[dir] = [] + if (!acc[dir]) acc[dir] = [] acc[dir].push(config) return acc }, {}) @@ -314,11 +327,12 @@ async function resolveVitestConfigs(showWarning: boolean) { for (const [_, configFiles] of Object.entries(configsByFolder)) { // vitest config always overrides vite config - if there is a Vitest config, we assume vite was overriden, // but it's possible to have several Vitest configs (vitest.e2e. vitest.unit, etc.) - const hasViteAndVitestConfig = configFiles.some(file => basename(file.fsPath).includes('vite.')) - && configFiles.some(file => basename(file.fsPath).includes('vitest.')) + const hasViteAndVitestConfig = + configFiles.some((file) => basename(file.fsPath).includes('vite.')) && + configFiles.some((file) => basename(file.fsPath).includes('vitest.')) // remove all vite configs from a folder if there is at least one Vitest config const filteredConfigFiles = hasViteAndVitestConfig - ? configFiles.filter(file => !basename(file.fsPath).includes('vite.')) + ? configFiles.filter((file) => !basename(file.fsPath).includes('vite.')) : configFiles filteredConfigFiles.forEach((config) => { const vitest = resolveVitestConfig(showWarning, config) @@ -327,8 +341,7 @@ async function resolveVitestConfigs(showWarning: boolean) { ...vitest, configFile: vitest.id, }) - } - else { + } else { warned = true } }) @@ -358,12 +371,11 @@ function guessRuntime(cwd: string, folder: vscode.WorkspaceFolder): 'deno' | 'no export function findFirstUniqueFolderNames(paths: string[]) { const folders: string[] = [] const mapCount: Record = {} - const segments = paths.map(p => p.split('/').reverse().slice(2)) + const segments = paths.map((p) => p.split('/').reverse().slice(2)) paths.forEach((_, index) => { segments[index].forEach((str) => { - if (!str) - return + if (!str) return mapCount[str] = (mapCount[str] || 0) + 1 }) }) @@ -392,16 +404,14 @@ function resolvePackagUniquePrefixes(packages: VitestPackage[]) { const projects: Record = {} for (const pkg of packages) { const { prefix, id } = pkg - if (!prefixes[prefix]) - prefixes[prefix] = [] + if (!prefixes[prefix]) prefixes[prefix] = [] prefixes[prefix].push(id) projects[id] = pkg } for (const prefix in prefixes) { const paths = prefixes[prefix] - if (paths.length === 1) - continue + if (paths.length === 1) continue const folders = findFirstUniqueFolderNames(paths) paths.forEach((path, index) => { @@ -422,8 +432,7 @@ function readPkgJson(path: string): null | { } { try { return JSON.parse(readFileSync(path, 'utf-8')) - } - catch { + } catch { return null } } diff --git a/packages/extension/src/spawn/resolve.ts b/packages/extension/src/spawn/resolve.ts index 049928f..8c3166a 100644 --- a/packages/extension/src/spawn/resolve.ts +++ b/packages/extension/src/spawn/resolve.ts @@ -14,7 +14,10 @@ export interface VitestResolution { } } -export function resolveVitestPackage(cwd: string, folder: vscode.WorkspaceFolder | undefined): VitestResolution | null { +export function resolveVitestPackage( + cwd: string, + folder: vscode.WorkspaceFolder | undefined, +): VitestResolution | null { const vitestPackageJsonPath = !process.versions.pnp && resolveVitestPackagePath(cwd, folder) if (vitestPackageJsonPath) { return { @@ -32,13 +35,11 @@ export function resolveVitestPackage(cwd: string, folder: vscode.WorkspaceFolder const pnpCwd = folder?.uri.fsPath || cwd const pnp = resolvePnp(pnpCwd) - if (!pnp) - return null - const vitestNodePath - = resolvePnpPackagePath(pnp.pnpApi, 'vitest/node', pnpCwd) - || resolvePnpPackagePath(pnp.pnpApi, 'vite-plus/test/node', pnpCwd) - if (!vitestNodePath) - return null + if (!pnp) return null + const vitestNodePath = + resolvePnpPackagePath(pnp.pnpApi, 'vitest/node', pnpCwd) || + resolvePnpPackagePath(pnp.pnpApi, 'vite-plus/test/node', pnpCwd) + if (!vitestNodePath) return null return { vitestNodePath, vitestPackageJsonPath: '', // we don't read pkg.json for pnp @@ -52,16 +53,19 @@ export function resolveVitestPackage(cwd: string, folder: vscode.WorkspaceFolder export function resolveVitestPackagePath(cwd: string, folder: vscode.WorkspaceFolder | undefined) { const customPackagePath = getConfig(folder).vitestPackagePath if (customPackagePath && !customPackagePath.endsWith('package.json')) - throw new Error(`"vitest.vitestPackagePath" must point to a package.json file, instead got: ${customPackagePath}`) + throw new Error( + `"vitest.vitestPackagePath" must point to a package.json file, instead got: ${customPackagePath}`, + ) try { - const result = customPackagePath || require.resolve('vitest/package.json', { - paths: [cwd], - }) + const result = + customPackagePath || + require.resolve('vitest/package.json', { + paths: [cwd], + }) delete require.cache['vitest/package.json'] delete require.cache[result] return result - } - catch { + } catch { return null } } @@ -74,8 +78,7 @@ export function resolveVitePlusPackagePath(cwd: string) { delete require.cache['vite-plus/package.json'] delete require.cache[result] return result - } - catch { + } catch { return null } } @@ -94,18 +97,20 @@ export function resolvePnp(cwd: string) { pnpPath, pnpApi, } - } - catch { + } catch { return null } } -export function resolvePnpPackagePath(pnpApi: any, pkg: 'vitest/node' | 'vite-plus/test/node', cwd: string): string | null { +export function resolvePnpPackagePath( + pnpApi: any, + pkg: 'vitest/node' | 'vite-plus/test/node', + cwd: string, +): string | null { try { const vitestNodePath = pnpApi.resolveRequest(pkg, cwd) return vitestNodePath - } - catch { + } catch { return null } } diff --git a/packages/extension/src/spawn/rpc.ts b/packages/extension/src/spawn/rpc.ts index 5b923da..147e265 100644 --- a/packages/extension/src/spawn/rpc.ts +++ b/packages/extension/src/spawn/rpc.ts @@ -14,12 +14,11 @@ function createHandler any>() { return { handlers, register: (listener: any) => handlers.push(listener), - trigger: (...data: any) => handlers.forEach(handler => handler(...data)), - clear: () => handlers.length = 0, + trigger: (...data: any) => handlers.forEach((handler) => handler(...data)), + clear: () => (handlers.length = 0), remove: (listener: T) => { const index = handlers.indexOf(listener) - if (index !== -1) - handlers.splice(index, 1) + if (index !== -1) handlers.splice(index, 1) }, } } @@ -56,8 +55,7 @@ export function createRpcOptions() { handlers[name as 'onCollected']?.remove(listener) }, clearListeners() { - for (const name in handlers) - handlers[name as 'onCollected']?.clear() + for (const name in handlers) handlers[name as 'onCollected']?.clear() }, }, } @@ -69,21 +67,18 @@ export function createVitestRpc(options: { }) { const { events, handlers } = createRpcOptions() - const api = createBirpc( - events, - { - timeout: -1, - bind: 'functions', - on(listener) { - options.on(listener) - }, - post(message) { - options.send(message) - }, - serialize: v8.serialize, - deserialize: v => v8.deserialize(Buffer.from(v) as any), + const api = createBirpc(events, { + timeout: -1, + bind: 'functions', + on(listener) { + options.on(listener) + }, + post(message) { + options.send(message) }, - ) + serialize: v8.serialize, + deserialize: (v) => v8.deserialize(Buffer.from(v) as any), + }) return { api, diff --git a/packages/extension/src/spawn/terminal.ts b/packages/extension/src/spawn/terminal.ts index e571187..41bba9e 100644 --- a/packages/extension/src/spawn/terminal.ts +++ b/packages/extension/src/spawn/terminal.ts @@ -15,11 +15,13 @@ import { createErrorLogger, log } from '../log' import { formatPkg } from '../utils' import { waitForWsConnection } from './ws' -export async function createVitestTerminalProcess(pkg: VitestPackage, options?: ProcessSpawnOptions): Promise { +export async function createVitestTerminalProcess( + pkg: VitestPackage, + options?: ProcessSpawnOptions, +): Promise { const pnpLoader = pkg.loader const pnp = pkg.pnp - if (pnpLoader && !pnp) - throw new Error('pnp file is required if loader option is used') + if (pnpLoader && !pnp) throw new Error('pnp file is required if loader option is used') const port = await getPort() const server = createServer().listen(port).unref() const wss = new WebSocketServer({ server }) @@ -49,7 +51,9 @@ export async function createVitestTerminalProcess(pkg: VitestPackage, options?: const processId = await terminal.processId if (terminal.exitStatus && terminal.exitStatus.code != null) { - throw new Error(`Terminal was ${getExitReason(terminal.exitStatus.reason)} with code ${terminal.exitStatus.code}`) + throw new Error( + `Terminal was ${getExitReason(terminal.exitStatus.reason)} with code ${terminal.exitStatus.code}`, + ) } let command = pkg.runtime @@ -69,7 +73,11 @@ export async function createVitestTerminalProcess(pkg: VitestPackage, options?: const meta = await new Promise((resolve, reject) => { const timeout = setTimeout(() => { terminal.show(false) - reject(new Error(`The extension could not connect to the terminal in 30 seconds. See the "vitest" terminal output for more details.`)) + reject( + new Error( + `The extension could not connect to the terminal in 30 seconds. See the "vitest" terminal output for more details.`, + ), + ) }, 30_000) wss.once('connection', () => { clearTimeout(timeout) @@ -82,11 +90,7 @@ export async function createVitestTerminalProcess(pkg: VitestPackage, options?: }) log.info('[API]', `${formatPkg(pkg)} terminal process ${processId} created`) - const vitestProcess = new ExtensionTerminalProcess( - terminal, - server, - meta.ws, - ) + const vitestProcess = new ExtensionTerminalProcess(terminal, server, meta.ws) return { rpc: meta.rpc, handlers: meta.handlers, diff --git a/packages/extension/src/spawn/ws.ts b/packages/extension/src/spawn/ws.ts index c2cee79..1d0379e 100644 --- a/packages/extension/src/spawn/ws.ts +++ b/packages/extension/src/spawn/ws.ts @@ -1,4 +1,8 @@ -import type { WorkerEvent, WorkerRunnerDebugOptions, WorkerRunnerOptions } from 'vitest-vscode-shared' +import type { + WorkerEvent, + WorkerRunnerDebugOptions, + WorkerRunnerOptions, +} from 'vitest-vscode-shared' import type { WebSocket, WebSocketServer } from 'ws' import type { ResolvedMeta } from '../apiProcess' import type { VitestPackage } from './pkg' @@ -6,7 +10,11 @@ import { pathToFileURL } from 'node:url' import { gte } from 'semver' import vscode from 'vscode' import { getConfig } from '../config' -import { browserSetupFilePath, browserSetupFilePathLegacy, finalCoverageFileName } from '../constants' +import { + browserSetupFilePath, + browserSetupFilePathLegacy, + finalCoverageFileName, +} from '../constants' import { log } from '../log' import { createVitestRpc } from './rpc' @@ -33,8 +41,8 @@ export function waitForWsConnection( pkg, false, shellType, - meta => resolve(meta), - err => reject(err), + (meta) => resolve(meta), + (err) => reject(err), options, ) @@ -67,24 +75,19 @@ export function onWsConnection( function onMessage(_message: any) { const message = JSON.parse(_message.toString()) as WorkerEvent - if (message.type === 'debug') - log.worker('info', ...message.args) + if (message.type === 'debug') log.worker('info', ...message.args) if (message.type === 'ready') { const { api, handlers } = createVitestRpc({ - on: listener => ws.on('message', listener), - send: message => ws.send(message), + on: (listener) => ws.on('message', listener), + send: (message) => ws.send(message), }) ws.once('close', () => { log.verbose?.('[API]', 'Vitest WebSocket connection closed, cannot call RPC anymore.') api.$close() }) if (!message.legacy) { - vscode.commands.executeCommand( - 'setContext', - 'vitest.environmentsSupported', - true, - ) + vscode.commands.executeCommand('setContext', 'vitest.environmentsSupported', true) } onStart({ rpc: api, @@ -147,9 +150,10 @@ export function onWsConnection( workspaceFile: pkg.workspaceFile, id: pkg.id, pnpApi: pnp, - pnpLoader: pnpLoader && gte(process.version, '18.19.0') - ? pathToFileURL(pnpLoader).toString() - : undefined, + pnpLoader: + pnpLoader && gte(process.version, '18.19.0') + ? pathToFileURL(pnpLoader).toString() + : undefined, setupFilePaths: { browserDebug: browserSetupFilePath, browserDebugLegacy: browserSetupFilePathLegacy, diff --git a/packages/extension/src/testTree.ts b/packages/extension/src/testTree.ts index 5924e9b..bbb65e3 100644 --- a/packages/extension/src/testTree.ts +++ b/packages/extension/src/testTree.ts @@ -66,8 +66,7 @@ export class TestTree extends vscode.Disposable { if (workspaceFolders.length === 1) { const rootItem = this.getOrCreateInlineFolderItem(workspaceFolders[0].uri) rootItem.children.replace([this.loaderItem]) - } - else { + } else { const folderItems = workspaceFolders.map((x) => { const item = this.getOrCreateWorkspaceFolderItem(x.uri) item.children.replace([]) @@ -80,11 +79,9 @@ export class TestTree extends vscode.Disposable { discoverAllTestFiles(api: VitestProcessAPI, files: ExtensionTestFileSpecification[]) { const folderItem = this.folderItems.get(normalize(api.workspaceFolder.uri.fsPath)) - if (folderItem) - folderItem.busy = false + if (folderItem) folderItem.busy = false - for (const [file, metadata] of files) - this.getOrCreateFileTestItem(api, metadata, file) + for (const [file, metadata] of files) this.getOrCreateFileTestItem(api, metadata, file) return files } @@ -105,8 +102,7 @@ export class TestTree extends vscode.Disposable { const symlinkUri = this.getSymlinkFolder(folderUri) const id = normalize(symlinkUri.fsPath) const cached = this.folderItems.get(id) - if (cached) - return cached + if (cached) return cached const item: vscode.TestItem = { id: symlinkUri.toString(), children: this.controller.items, @@ -133,8 +129,7 @@ export class TestTree extends vscode.Disposable { const symlinkUri = this.getSymlinkFolder(folderUri) const folderId = normalize(symlinkUri.fsPath) const cached = this.folderItems.get(folderId) - if (cached) - return cached + if (cached) return cached const folderItem = this._createFolderItem(symlinkUri) this.folderItems.set(folderId, folderItem) @@ -151,40 +146,25 @@ export class TestTree extends vscode.Disposable { const normalizedFile = normalize(file) const fileId = `${normalizedFile}${project}` const cached = this.fileItems.get(fileId) - if (cached) - return cached + if (cached) return cached const fileUri = vscode.Uri.file(resolve(file)) const parentItem = this.getOrCreateFolderTestItem(api, dirname(file)) const label = `${basename(file)}${project ? ` [${project}]` : ''}` - const testFileItem = this.controller.createTestItem( - fileId, - label, - fileUri, - ) + const testFileItem = this.controller.createTestItem(fileId, label, fileUri) // "description" looks nicer in the test explorer, // but it's not displayed in the gutter icon // testFileItem.description = project testFileItem.tags = [api.tag] testFileItem.canResolveChildren = true - TestFile.register( - testFileItem, - parentItem, - normalizedFile, - api, - metadata, - ) + TestFile.register(testFileItem, parentItem, normalizedFile, api, metadata) parentItem.children.add(testFileItem) this.fileItems.set(fileId, testFileItem) const cachedItems = this.testItemsByFile.get(normalizedFile) || [] cachedItems.push(testFileItem) this.testItemsByFile.set(normalizedFile, cachedItems) this.testFiles.add(fileUri.fsPath) - vscode.commands.executeCommand( - 'setContext', - 'vitest.testFiles', - [...this.testFiles], - ) + vscode.commands.executeCommand('setContext', 'vitest.testFiles', [...this.testFiles]) return testFileItem } @@ -192,8 +172,7 @@ export class TestTree extends vscode.Disposable { getOrCreateFolderTestItem(api: VitestProcessAPI, normalizedFolder: string) { const cached = this.folderItems.get(normalizedFolder) if (cached) { - if (!cached.tags.includes(api.tag)) - cached.tags = [...cached.tags, api.tag] + if (!cached.tags.includes(api.tag)) cached.tags = [...cached.tags, api.tag] return cached } @@ -231,12 +210,11 @@ export class TestTree extends vscode.Disposable { public removeFile(filepath: string) { const items = this.testItemsByFile.get(normalize(filepath)) - items?.forEach(item => this.recursiveDelete(item)) + items?.forEach((item) => this.recursiveDelete(item)) } private recursiveDelete(item: vscode.TestItem) { - if (!item.parent) - return + if (!item.parent) return item.parent.children.delete(item.id) this.flatTestItems.delete(item.id) const data = getTestData(item) @@ -245,11 +223,9 @@ export class TestTree extends vscode.Disposable { this.testItemsByFile.delete(data.filepath) this.fileItems.delete(item.id) } - if (data instanceof TestFolder) - this.folderItems.delete(item.id) + if (data instanceof TestFolder) this.folderItems.delete(item.id) - if (!item.parent.children.size) - this.recursiveDelete(item.parent) + if (!item.parent.children.size) this.recursiveDelete(item.parent) } public getAPIFromTestItem(testItem: vscode.TestItem) { @@ -258,8 +234,7 @@ export class TestTree extends vscode.Disposable { async discoverTestsInFile(testItem: vscode.TestItem) { const data = getTestData(testItem) - if (!(data instanceof TestFile)) - return + if (!(data instanceof TestFile)) return const api = data.api if (!api) { log.error(`Cannot find collector for ${testItem.uri?.fsPath}`) @@ -269,23 +244,20 @@ export class TestTree extends vscode.Disposable { try { await api.collectTests(data.project, testItem.uri!.fsPath) return testItem - } - finally { + } finally { testItem.busy = false } } public getTestItemByTaskId(taskId: string): vscode.TestItem | undefined { const testItem = this.flatTestItems.get(taskId) - if (!testItem) - return undefined + if (!testItem) return undefined return testItem || undefined } public getTestItemByTask(task: RunnerTask): vscode.TestItem | null { const cachedItem = this.flatTestItems.get(task.id) - if (cachedItem) - return cachedItem + if (cachedItem) return cachedItem if ('filepath' in task && task.filepath) { const testItem = this.fileItems.get(`${task.filepath}${task.projectName || ''}`) return testItem || null @@ -297,10 +269,8 @@ export class TestTree extends vscode.Disposable { const files: vscode.TestItem[] = [] for (const [_, item] of folder.children) { const data = getTestData(item) - if (data instanceof TestFile) - files.push(item) - else if (data instanceof TestFolder) - files.push(...this.getFolderFiles(item)) + if (data instanceof TestFile) files.push(item) + else if (data instanceof TestFolder) files.push(...this.getFolderFiles(item)) } return files } @@ -310,7 +280,9 @@ export class TestTree extends vscode.Disposable { const fileId = `${normalizedFile}${file.projectName || ''}` const fileTestItem = this.fileItems.get(fileId) if (!fileTestItem) { - log.error(`Cannot find a file test item for ${file.filepath} in "${file.projectName || 'core'}" project.`) + log.error( + `Cannot find a file test item for ${file.filepath} in "${file.projectName || 'core'}" project.`, + ) return } fileTestItem.error = undefined @@ -318,11 +290,10 @@ export class TestTree extends vscode.Disposable { const data = getTestData(fileTestItem) as TestFile this.collectTasks(api.tag, data, file.tasks, fileTestItem) if (file.result?.errors) { - const error = file.result.errors.map(error => error.stack || error.message).join('\n') + const error = file.result.errors.map((error) => error.stack || error.message).join('\n') fileTestItem.error = error log.error(`Error in ${file.filepath}`, error) - } - else if (!file.tasks.length) { + } else if (!file.tasks.length) { fileTestItem.error = `No tests found in ${file.filepath}` } fileTestItem.canResolveChildren = false @@ -338,8 +309,14 @@ export class TestTree extends vscode.Disposable { } } = {} - collectTasks(tag: vscode.TestTag, fileData: TestFile, tasks: RunnerTask[], parent: vscode.TestItem) { - const fileCachedTests = this.cacheDynamic[fileData.filepath] || (this.cacheDynamic[fileData.filepath] = {}) + collectTasks( + tag: vscode.TestTag, + fileData: TestFile, + tasks: RunnerTask[], + parent: vscode.TestItem, + ) { + const fileCachedTests = + this.cacheDynamic[fileData.filepath] || (this.cacheDynamic[fileData.filepath] = {}) const ids = new Set() for (const task of tasks) { @@ -355,11 +332,9 @@ export class TestTree extends vscode.Disposable { } } - const testItem = this.flatTestItems.get(task.id) || this.controller.createTestItem( - task.id, - task.name, - parent.uri, - ) + const testItem = + this.flatTestItems.get(task.id) || + this.controller.createTestItem(task.id, task.name, parent.uri) testItem.tags = [...new Set([...parent.tags, tag])] testItem.error = undefined testItem.label = task.name @@ -367,37 +342,39 @@ export class TestTree extends vscode.Disposable { if (location) { const position = new vscode.Position(location.line - 1, location.column) testItem.range = new vscode.Range(position, position) - } - else { + } else { log.error(`Cannot find location for "${testItem.label}". Using "id" to sort instead.`) testItem.sortText = task.id } // dynamic exists only during AST collection // see src/worker/collect.ts:172 const isDynamic = (task as any).dynamic - if (task.type === 'suite') - TestSuite.register(testItem, parent, fileData, isDynamic) - else if (isTest(task)) - TestCase.register(testItem, parent, fileData, isDynamic) + if (task.type === 'suite') TestSuite.register(testItem, parent, fileData, isDynamic) + else if (isTest(task)) TestCase.register(testItem, parent, fileData, isDynamic) if (isDynamic) { testItem.description = 'pattern' - const dynamicTestRegExp = (getTestData(testItem) as TestCase | TestSuite).getTestNamePattern() - - const cachedDynamicTest = fileCachedTests[dynamicTestRegExp] || (fileCachedTests[dynamicTestRegExp] = { - id: task.id, - type: isTest(task) ? 'test' : task.type, - children: new Set(), - }) + const dynamicTestRegExp = ( + getTestData(testItem) as TestCase | TestSuite + ).getTestNamePattern() + + const cachedDynamicTest = + fileCachedTests[dynamicTestRegExp] || + (fileCachedTests[dynamicTestRegExp] = { + id: task.id, + type: isTest(task) ? 'test' : task.type, + children: new Set(), + }) cachedDynamicTest.children.forEach((fileId) => { // don't remove tests that were collected during runtime ids.add(fileId) }) - } - else if (task.each) { + } else if (task.each) { const fullName = getTaskFullName(task) // order in the opposite order so we only match one item with the longest name - const orderedTests = Object.entries(fileCachedTests).sort(([a1], [a2]) => a2.localeCompare(a1)) + const orderedTests = Object.entries(fileCachedTests).sort(([a1], [a2]) => + a2.localeCompare(a1), + ) for (const [testRegexp, cachedDynamicTask] of orderedTests) { if (new RegExp(testRegexp).test(fullName)) { const testId = cachedDynamicTask.id @@ -410,11 +387,9 @@ export class TestTree extends vscode.Disposable { ids.add(childId) if (dynamicTestItem) { // we are creating a separate one because we can't use the same one in multiple places - const suiteCopyChild = this.flatTestItems.get(childId) || this.controller.createTestItem( - childId, - dynamicTestItem.label, - dynamicTestItem.uri, - ) + const suiteCopyChild = + this.flatTestItems.get(childId) || + this.controller.createTestItem(childId, dynamicTestItem.label, dynamicTestItem.uri) this.flatTestItems.set(childId, suiteCopyChild) suiteCopyChild.tags = dynamicTestItem.tags suiteCopyChild.canResolveChildren = dynamicTestItem.canResolveChildren @@ -425,8 +400,7 @@ export class TestTree extends vscode.Disposable { if (task.type === 'suite') { TestSuite.register(suiteCopyChild, parent, fileData, true) - } - else { + } else { TestCase.register(suiteCopyChild, parent, fileData, true) } @@ -444,7 +418,7 @@ export class TestTree extends vscode.Disposable { // errors during collection are not test failures, they need to be // displayed as errors in the tree if (task.result?.errors) { - const error = task.result.errors.map(error => error.stack).join('\n') + const error = task.result.errors.map((error) => error.stack).join('\n') testItem.error = error } @@ -453,15 +427,14 @@ export class TestTree extends vscode.Disposable { } // only tests have tags, and 'tasks' in task narrows it down else if ('tags' in task) { - const tags = (task.tags as string[]).map(tag => this.tagsManager.getTestTag(tag)) + const tags = (task.tags as string[]).map((tag) => this.tagsManager.getTestTag(tag)) testItem.tags = [...testItem.tags, ...tags] } } // remove tasks that are no longer present parent.children.forEach((child) => { - if (!ids.has(child.id)) - parent.children.delete(child.id) + if (!ids.has(child.id)) parent.children.delete(child.id) }) } } @@ -475,14 +448,11 @@ function isTest(task: RunnerTask) { function getAPIFromFolder(folder: vscode.TestItem): VitestProcessAPI | null { const data = getTestData(folder) - if (data instanceof TestFile) - return data.api - if (!(data instanceof TestFolder)) - return null + if (data instanceof TestFile) return data.api + if (!(data instanceof TestFolder)) return null for (const [, child] of folder.children) { const api = getAPIFromTestItem(child) - if (api) - return api + if (api) return api } return null } @@ -491,11 +461,9 @@ function getAPIFromTestItem(testItem: vscode.TestItem): VitestProcessAPI | null const data = getTestData(testItem) // API is stored in test files - if this is a folder, try to find a file inside, // otherwise go up until we find a file, this should never be a folder - if (data instanceof TestFolder) - return getAPIFromFolder(testItem) + if (data instanceof TestFolder) return getAPIFromFolder(testItem) - if (data instanceof TestFile) - return data.api + if (data instanceof TestFile) return data.api return data.file.api } diff --git a/packages/extension/src/testTreeData.ts b/packages/extension/src/testTreeData.ts index d5f7013..50c9a64 100644 --- a/packages/extension/src/testTreeData.ts +++ b/packages/extension/src/testTreeData.ts @@ -9,7 +9,9 @@ const WEAKMAP_TEST_DATA = new WeakMap() export function getTestData(item: vscode.TestItem): TestData { const data = WEAKMAP_TEST_DATA.get(item) if (!data) - throw new Error(`Test data not found for "${item.label}". This is a bug in Vitest extension. Please report it to https://github.com/vitest-dev/vscode`) + throw new Error( + `Test data not found for "${item.label}". This is a bug in Vitest extension. Please report it to https://github.com/vitest-dev/vscode`, + ) return data } @@ -23,10 +25,7 @@ class BaseTestData { public readonly parent: TestData | undefined public readonly id: string - constructor( - item: vscode.TestItem, - parent?: vscode.TestItem, - ) { + constructor(item: vscode.TestItem, parent?: vscode.TestItem) { this.label = item.label this.id = item.id this.parent = parent ? WEAKMAP_TEST_DATA.get(parent) : undefined @@ -36,10 +35,7 @@ class BaseTestData { export class TestFolder extends BaseTestData { public readonly type = 'folder' - private constructor( - item: vscode.TestItem, - parent?: vscode.TestItem, - ) { + private constructor(item: vscode.TestItem, parent?: vscode.TestItem) { super(item, parent) } @@ -99,8 +95,7 @@ class TaskName { let iter = this.data.parent while (iter) { // if we reached test file, then stop - if (iter instanceof TestFile || iter instanceof TestFolder) - break + if (iter instanceof TestFile || iter instanceof TestFolder) break patterns.push(escapeTestName(iter.label, iter.name.dynamic)) iter = iter.parent } @@ -125,7 +120,12 @@ export class TestCase extends BaseTestData { this.name = new TaskName(this, dynamic) } - public static register(item: vscode.TestItem, parent: vscode.TestItem, file: TestFile, dynamic: boolean) { + public static register( + item: vscode.TestItem, + parent: vscode.TestItem, + file: TestFile, + dynamic: boolean, + ) { return addTestData(item, new TestCase(item, parent, file, dynamic)) } @@ -152,7 +152,12 @@ export class TestSuite extends BaseTestData { this.name = new TaskName(this, dynamic) } - public static register(item: vscode.TestItem, parent: vscode.TestItem, file: TestFile, dynamic: boolean) { + public static register( + item: vscode.TestItem, + parent: vscode.TestItem, + file: TestFile, + dynamic: boolean, + ) { return addTestData(item, new TestSuite(item, parent, file, dynamic)) } @@ -185,6 +190,6 @@ function escapeTestName(label: string, dynamic: boolean) { let pattern = label.replace(/\$[a-z_.]+/gi, '%s') pattern = escapeRegex(pattern) // Replace percent placeholders with their respective regex - pattern = pattern.replace(/%[i#dfsjo%]/g, m => kReplacers.get(m) || m) + pattern = pattern.replace(/%[i#dfsjo%]/g, (m) => kReplacers.get(m) || m) return pattern } diff --git a/packages/extension/src/utils.ts b/packages/extension/src/utils.ts index 78a41b9..4407d4b 100644 --- a/packages/extension/src/utils.ts +++ b/packages/extension/src/utils.ts @@ -17,16 +17,13 @@ export function formatPkg(pkg: VitestPackage) { } function _showVitestError(message: string, error?: any) { - if (error) - log.error(error) - - vscode.window.showErrorMessage( - `${message}. Check the output for more details.`, - 'See error', - ).then((result) => { - if (result === 'See error') - vscode.commands.executeCommand('vitest.openOutput') - }) + if (error) log.error(error) + + vscode.window + .showErrorMessage(`${message}. Check the output for more details.`, 'See error') + .then((result) => { + if (result === 'See error') vscode.commands.executeCommand('vitest.openOutput') + }) } export const showVitestError = debounce(_showVitestError, 100) @@ -38,8 +35,7 @@ export function pluralize(count: number, singular: string) { export function debounce void>(cb: T, wait = 20) { let h: NodeJS.Timeout | undefined const callable = (...args: any) => { - if (h) - clearTimeout(h) + if (h) clearTimeout(h) h = setTimeout(cb, wait, ...args) } return (callable) @@ -51,8 +47,7 @@ const urlAlphabet = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwy export function nanoid(size = 21) { let id = '' let i = size - while (i--) - id += urlAlphabet[(Math.random() * 64) | 0] + while (i--) id += urlAlphabet[(Math.random() * 64) | 0] return id } @@ -82,19 +77,21 @@ export function clearCachedRuntime() { } // based on https://github.com/microsoft/playwright-vscode/blob/main/src/utils.ts#L144 -export async function findRuntimeExecutable(runtime: 'node' | 'deno', cwd: string): Promise { +export async function findRuntimeExecutable( + runtime: 'node' | 'deno', + cwd: string, +): Promise { if (getConfig().nodeExecutable) // if empty string, keep as undefined pathToRuntime[runtime] = getConfig().nodeExecutable || undefined - if (pathToRuntime[runtime]) - return pathToRuntime[runtime] + if (pathToRuntime[runtime]) return pathToRuntime[runtime] // Stage 1: Try to find Node.js via process.env.PATH let node: string | null = await which(runtime, { nothrow: true }) // Stage 2: When extension host boots, it does not have the right env set, so we might need to wait. for (let i = 0; i < 5 && !node; ++i) { - await new Promise(f => setTimeout(f, 200)) + await new Promise((f) => setTimeout(f, 200)) node = await which(runtime, { nothrow: true }) } // Stage 3: If we still haven't found Node.js, try to find it via a subprocess. @@ -111,31 +108,30 @@ export async function findRuntimeExecutable(runtime: 'node' | 'deno', cwd: strin } async function findRuntimeViaShell(runtime: 'node' | 'deno', cwd: string): Promise { - if (process.platform === 'win32') - return null + if (process.platform === 'win32') return null return new Promise((resolve) => { const startToken = '___START_SHELL__' const endToken = '___END_SHELL__' try { - const childProcess = spawn(`${vscode.env.shell} -i -c 'if [[ $(type ${runtime} 2>/dev/null) == *function* ]]; then ${runtime} --version; fi; echo ${startToken} && which ${runtime} && echo ${endToken}'`, { - stdio: 'pipe', - shell: true, - cwd, - }) + const childProcess = spawn( + `${vscode.env.shell} -i -c 'if [[ $(type ${runtime} 2>/dev/null) == *function* ]]; then ${runtime} --version; fi; echo ${startToken} && which ${runtime} && echo ${endToken}'`, + { + stdio: 'pipe', + shell: true, + cwd, + }, + ) let output = '' - childProcess.stdout.on('data', data => output += data.toString()) + childProcess.stdout.on('data', (data) => (output += data.toString())) childProcess.on('error', () => resolve(null)) childProcess.on('exit', (exitCode) => { - if (exitCode !== 0) - return resolve(null) + if (exitCode !== 0) return resolve(null) const start = output.indexOf(startToken) const end = output.indexOf(endToken) - if (start === -1 || end === -1) - return resolve(null) + if (start === -1 || end === -1) return resolve(null) return resolve(output.substring(start + startToken.length, end).trim()) }) - } - catch (e) { + } catch (e) { log.error('[SPAWN]', vscode.env.shell, e) resolve(null) } @@ -150,8 +146,7 @@ export function getErrorMessage(error: TestError) { message += stripVTControlCharacters(error.message ?? '') if (typeof error.frame === 'string') { message += `\n${error.frame}` - } - else { + } else { const errorProperties = getErrorProperties(error) if (Object.keys(errorProperties).length) { const errorsInspect = inspect(errorProperties, { diff --git a/packages/extension/src/watcher.ts b/packages/extension/src/watcher.ts index 713cb41..a1b9328 100644 --- a/packages/extension/src/watcher.ts +++ b/packages/extension/src/watcher.ts @@ -22,7 +22,7 @@ export class ExtensionWatcher extends vscode.Disposable { } reset() { - this.watcherByFolder.forEach(x => x.dispose()) + this.watcherByFolder.forEach((x) => x.dispose()) this.watcherByFolder.clear() this.apisByFolder = new WeakMap() } @@ -62,7 +62,7 @@ export class ExtensionWatcher extends vscode.Disposable { this.transformSchemaProvider.emitChange(uri) log.verbose?.('[VSCODE] File changed:', this.relative(api, uri)) const apis = this.apisByFolder.get(folder) || [] - apis.forEach(api => api.onFileChanged(path)) + apis.forEach((api) => api.onFileChanged(path)) apis.forEach((api) => { if (api.getPersistentProcessMeta() || api.isSpawningPersistentProcess) { return @@ -99,11 +99,11 @@ export class ExtensionWatcher extends vscode.Disposable { private async shouldIgnoreFile(api: VitestProcessAPI, path: string, uri: vscode.Uri) { if ( - path.includes('/node_modules/') - || path.includes('\\node_modules\\') - || path.includes('/.git/') - || path.includes('\\.git\\') - || path.endsWith('.git') + path.includes('/node_modules/') || + path.includes('\\node_modules\\') || + path.includes('/.git/') || + path.includes('\\.git\\') || + path.endsWith('.git') ) { return true } @@ -111,16 +111,15 @@ export class ExtensionWatcher extends vscode.Disposable { const stats = await vscode.workspace.fs.stat(uri) if ( // if not a file - stats.type !== vscode.FileType.File + stats.type !== vscode.FileType.File && // if not a symlinked file - && stats.type !== (vscode.FileType.File | vscode.FileType.SymbolicLink) + stats.type !== (vscode.FileType.File | vscode.FileType.SymbolicLink) ) { log.verbose?.('[VSCODE]', this.relative(api, uri), 'is not a file. Ignoring.') return true } return false - } - catch { + } catch { return true } } diff --git a/packages/extension/src/worker/index.ts b/packages/extension/src/worker/index.ts index 3aaf2cc..cbf3056 100644 --- a/packages/extension/src/worker/index.ts +++ b/packages/extension/src/worker/index.ts @@ -7,9 +7,7 @@ import { WebSocket } from 'ws' // this is the file that will be executed with "node " -const emitter = new WorkerWSEventEmitter( - new WebSocket(process.env.VITEST_WS_ADDRESS!), -) +const emitter = new WorkerWSEventEmitter(new WebSocket(process.env.VITEST_WS_ADDRESS!)) process.title = 'vitest-vscode' @@ -31,11 +29,11 @@ emitter.on('message', async function onMessage(message: any) { const data = message as WorkerRunnerOptions try { - const vitestModule = await import( - pathToFileURL(normalizeDriveLetter(data.meta.vitestNodePath)).toString(), - ) as typeof import('vitest/node') + const vitestModule = (await import( + pathToFileURL(normalizeDriveLetter(data.meta.vitestNodePath)).toString() + )) as typeof import('vitest/node') - const isLegacy = !vitestModule.version || (Number(vitestModule.version[0]) < 4) + const isLegacy = !vitestModule.version || Number(vitestModule.version[0]) < 4 const workerName = isLegacy ? './workerLegacy.js' : './workerNew.js' const workerPath = pathToFileURL(join(__dirname, workerName)) const initModule = await import(workerPath.toString()) @@ -48,26 +46,22 @@ emitter.on('message', async function onMessage(message: any) { const worker = createWorker() - const rpc = createWorkerRPC( - worker, - { - on(listener) { - emitter.on('message', listener) - }, - post(message) { - emitter.send(message) - }, - serialize: v8.serialize, - deserialize: v => v8.deserialize(Buffer.from(v) as any), + const rpc = createWorkerRPC(worker, { + on(listener) { + emitter.on('message', listener) }, - ) + post(message) { + emitter.send(message) + }, + serialize: v8.serialize, + deserialize: (v) => v8.deserialize(Buffer.from(v) as any), + }) worker.initRpc(rpc) reporter.initRpc(rpc) emitter.ready(projects, workspaceSource, isLegacy) await worker.vitest.report('onInit', worker.vitest) - } - catch (err: any) { + } catch (err: any) { emitter.error(err) } } diff --git a/packages/shared/package.json b/packages/shared/package.json index b879aaa..10d9989 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,8 +1,8 @@ { "name": "vitest-vscode-shared", - "type": "module", "version": "0.0.0", "private": true, + "type": "module", "exports": { ".": "./src/index.ts" }, diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 2068781..137abd5 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -1,9 +1,5 @@ import type { BirpcReturn } from 'birpc' -import type { - RunnerTaskResultPack, - RunnerTestFile, - UserConsoleLog, -} from 'vitest' +import type { RunnerTaskResultPack, RunnerTestFile, UserConsoleLog } from 'vitest' export { WorkerWSEventEmitter } from './emitter' export { createWorkerRPC } from './rpc' @@ -15,10 +11,7 @@ export { normalizeDriveLetter, } from './utils' -export type ExtensionTestSpecification = [ - project: string, - file: string, -] +export type ExtensionTestSpecification = [project: string, file: string] export interface ExtensionTestFileMetadata { project: string @@ -30,10 +23,7 @@ export interface ExtensionTestFileMetadata { } } -export type ExtensionTestFileSpecification = [ - file: string, - ExtensionTestFileMetadata, -] +export type ExtensionTestFileSpecification = [file: string, ExtensionTestFileMetadata] export interface ExtensionUserConsoleLog extends UserConsoleLog { // Parsed location from stack trace for inline display @@ -57,10 +47,19 @@ export interface ExtensionWorkerTransport { collectTests: (testFile: ExtensionTestSpecification[]) => Promise cancelRun: () => Promise // accepts files with the project or folders (project doesn't matter for them) - runTests: (filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string) => Promise - updateSnapshots: (filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string) => Promise - - watchTests: (filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string) => Promise + runTests: ( + filesOrDirectories?: ExtensionTestSpecification[] | string[], + testNamePattern?: string, + ) => Promise + updateSnapshots: ( + filesOrDirectories?: ExtensionTestSpecification[] | string[], + testNamePattern?: string, + ) => Promise + + watchTests: ( + filesOrDirectories?: ExtensionTestSpecification[] | string[], + testNamePattern?: string, + ) => Promise getSourceModuleDiagnostic: (moduleId: string) => Promise exit: () => void @@ -78,7 +77,12 @@ export interface ExtensionWorkerTransport { export interface ExtensionWorkerEvents { onConsoleLog: (log: ExtensionUserConsoleLog) => void onTaskUpdate: (task: RunnerTaskResultPack[]) => void - onTestRunEnd: (files: RunnerTestFile[], unhandledError: string, collecting?: boolean, coverage?: unknown) => void + onTestRunEnd: ( + files: RunnerTestFile[], + unhandledError: string, + collecting?: boolean, + coverage?: unknown, + ) => void onCollected: (file: RunnerTestFile, collecting?: boolean) => void onTestRunStart: (files: string[]) => void diff --git a/packages/shared/src/pkgManager.ts b/packages/shared/src/pkgManager.ts index b2a867b..fe99226 100644 --- a/packages/shared/src/pkgManager.ts +++ b/packages/shared/src/pkgManager.ts @@ -1,15 +1,7 @@ import { readFileSync, statSync } from 'node:fs' import path from 'node:path' -const AGENTS = [ - 'npm', - 'yarn', - 'yarn@berry', - 'pnpm', - 'pnpm@6', - 'bun', - 'deno', -] +const AGENTS = ['npm', 'yarn', 'yarn@berry', 'pnpm', 'pnpm@6', 'bun', 'deno'] const LOCKS = { 'bun.lock': 'bun', @@ -26,8 +18,7 @@ function pathExists(path: string, type: 'file' | 'directory') { try { const stat = statSync(path) return type === 'file' ? stat.isFile() : stat.isDirectory() - } - catch { + } catch { return false } } @@ -42,10 +33,8 @@ export function detectPackageManager(cwd: string) { if (pathExists(path.join(directory, lock), 'file')) { const name = LOCKS[lock as 'bun.lock'] const result = parsePackageJson(path.join(directory, 'package.json')) - if (result) - return result - else - return { name, agent: name } + if (result) return result + else return { name, agent: name } } } break @@ -53,8 +42,7 @@ export function detectPackageManager(cwd: string) { case 'packageManager-field': case 'devEngines-field': { const result = parsePackageJson(path.join(directory, 'package.json')) - if (result) - return result + if (result) return result break } } @@ -64,8 +52,7 @@ export function detectPackageManager(cwd: string) { } function parsePackageJson(filepath: string) { - if (!filepath || !pathExists(filepath, 'file')) - return null + if (!filepath || !pathExists(filepath, 'file')) return null return handlePackageManager(filepath) } @@ -92,22 +79,17 @@ function handlePackageManager(filepath: string) { agent = 'yarn@berry' version = 'berry' return { name, agent, version } - } - else if (name === 'pnpm' && ver && Number.parseInt(ver) < 7) { + } else if (name === 'pnpm' && ver && Number.parseInt(ver) < 7) { agent = 'pnpm@6' return { name, agent, version } - } - else if (AGENTS.includes(name)) { + } else if (AGENTS.includes(name)) { agent = name return { name, agent, version } - } - else { + } else { return null } } - } - catch { - } + } catch {} return null } diff --git a/packages/shared/src/rpc.ts b/packages/shared/src/rpc.ts index 930b877..7f7b6dc 100644 --- a/packages/shared/src/rpc.ts +++ b/packages/shared/src/rpc.ts @@ -6,13 +6,7 @@ export function createWorkerRPC(vitest: ExtensionWorkerTransport, channel: Chann const rpc = createBirpc(vitest, { timeout: -1, bind: 'functions', - eventNames: [ - 'onConsoleLog', - 'onTaskUpdate', - 'onCollected', - 'onTestRunStart', - 'onTestRunEnd', - ], + eventNames: ['onConsoleLog', 'onTaskUpdate', 'onCollected', 'onTestRunStart', 'onTestRunEnd'], ...channel, }) return rpc diff --git a/packages/shared/src/utils.ts b/packages/shared/src/utils.ts index 116fed1..e8444f9 100644 --- a/packages/shared/src/utils.ts +++ b/packages/shared/src/utils.ts @@ -6,7 +6,12 @@ type QueueNode = [value: T, next?: QueueNode] /** * Return a function for running multiple async operations with limited concurrency. */ -export function limitConcurrency(concurrency = Number.POSITIVE_INFINITY): (func: (...args: Args) => PromiseLike | T, ...args: Args) => Promise { +export function limitConcurrency( + concurrency = Number.POSITIVE_INFINITY, +): ( + func: (...args: Args) => PromiseLike | T, + ...args: Args +) => Promise { // The number of currently active + pending tasks. let count = 0 @@ -41,20 +46,20 @@ export function limitConcurrency(concurrency = Number.POSITIVE_INFINITY): { - // Running func here ensures that even a non-thenable result or an - // immediately thrown error gets wrapped into a Promise. - return func(...args) - }).finally(finish) + }) + .then(() => { + // Running func here ensures that even a non-thenable result or an + // immediately thrown error gets wrapped into a Promise. + return func(...args) + }) + .finally(finish) } } @@ -67,9 +72,12 @@ export function assert(condition: unknown, message: string | (() => string)): as export function getSuggestedInstallCommand(cwd: string) { const pkgManager = detectPackageManager(cwd) switch (pkgManager?.name) { - case 'bun': return 'bun install --dev vitest' - case 'yarn': return 'yarn add -D vitest' - case 'pnpm': return 'pnpm add -D vitest' + case 'bun': + return 'bun install --dev vitest' + case 'yarn': + return 'yarn add -D vitest' + case 'pnpm': + return 'pnpm add -D vitest' case 'npm': default: return 'npm i --save-dev vitest' @@ -77,8 +85,7 @@ export function getSuggestedInstallCommand(cwd: string) { } export function normalizeDriveLetter(path: string) { - if (process.platform !== 'win32') - return path + if (process.platform !== 'win32') return path return path[0].toUpperCase() + path.slice(1) } @@ -89,18 +96,16 @@ export function createQueuedHandler(resolver: (value: T[]) => Promise, let pendingResolvers: Array<() => void> = [] function flush() { - if (promise) - return + if (promise) return const values = [...cached] cached.clear() const resolvers = pendingResolvers pendingResolvers = [] promise = resolver(values).finally(() => { promise = null - resolvers.forEach(fn => fn()) + resolvers.forEach((fn) => fn()) // If more items were queued while resolving, flush them - if (cached.size) - flush() + if (cached.size) flush() }) } diff --git a/packages/worker-legacy/src/collect.ts b/packages/worker-legacy/src/collect.ts index 074b195..449b7ef 100644 --- a/packages/worker-legacy/src/collect.ts +++ b/packages/worker-legacy/src/collect.ts @@ -1,11 +1,7 @@ import type { SourceMap } from 'node:module' import type { RunnerTestCase, RunnerTestFile, RunnerTestSuite, TaskBase, TestError } from 'vitest' import type { Vite, WorkspaceProject } from 'vitest/node' -import { - calculateSuiteHash, - generateHash, - someTasksAreOnly, -} from '@vitest/runner/utils' +import { calculateSuiteHash, generateHash, someTasksAreOnly } from '@vitest/runner/utils' import { originalPositionFor, TraceMap } from '@vitest/utils/source-map' import { parse } from 'acorn' import { ancestor as walkAst } from 'acorn-walk' @@ -46,19 +42,21 @@ export interface FileInformation { definitions: LocalCallDefinition[] } -const debug = process.env.VITEST_VSCODE_LOG !== 'info' - ? (...args: any[]) => { - // eslint-disable-next-line no-console - console.info(...args) - } - : undefined +const debug = + process.env.VITEST_VSCODE_LOG !== 'info' + ? (...args: any[]) => { + // eslint-disable-next-line no-console + console.info(...args) + } + : undefined -const verbose = process.env.VITEST_VSCODE_LOG === 'verbose' - ? (...args: any[]) => { - // eslint-disable-next-line no-console - console.info(...args) - } - : undefined +const verbose = + process.env.VITEST_VSCODE_LOG === 'verbose' + ? (...args: any[]) => { + // eslint-disable-next-line no-console + console.info(...args) + } + : undefined function isTestFunctionName(name: string) { return name === 'it' || name === 'test' || name.startsWith('test') || name.endsWith('Test') @@ -77,13 +75,8 @@ export function astParseFile(filepath: string, code: string) { }) if (verbose) { - verbose( - 'Collecting', - filepath, - code, - ) - } - else { + verbose('Collecting', filepath, code) + } else { debug?.('Collecting', filepath) } const definitions: LocalCallDefinition[] = [] @@ -101,18 +94,16 @@ export function astParseFile(filepath: string, code: string) { return getName(callee.tag) } if (callee.type === 'MemberExpression') { - if ( - callee.object?.type === 'Identifier' - && isVitestFunctionName(callee.object.name) - ) { + if (callee.object?.type === 'Identifier' && isVitestFunctionName(callee.object.name)) { return callee.object?.name } if ( // direct call as `__vite_ssr_exports_0__.test()` - callee.object?.name?.startsWith('__vite_ssr_') + callee.object?.name?.startsWith('__vite_ssr_') || // call as `__vite_ssr_exports_0__.Vitest.test`, // this is a special case for using Vitest namespaces popular in Effect - || (callee.object?.object?.name?.startsWith('__vite_ssr_') && callee.object?.property?.name === 'Vitest') + (callee.object?.object?.name?.startsWith('__vite_ssr_') && + callee.object?.property?.name === 'Vitest') ) { return getName(callee.property) } @@ -151,13 +142,12 @@ export function astParseFile(filepath: string, code: string) { const end = node.end // .each or (0, __vite_ssr_exports_0__.test)() if ( - callee.type === 'CallExpression' - || callee.type === 'SequenceExpression' - || callee.type === 'TaggedTemplateExpression' + callee.type === 'CallExpression' || + callee.type === 'SequenceExpression' || + callee.type === 'TaggedTemplateExpression' ) { start = callee.end - } - else { + } else { start = node.start } @@ -171,8 +161,7 @@ export function astParseFile(filepath: string, code: string) { let message: string if (messageNode?.type === 'Literal' || messageNode?.type === 'TemplateLiteral') { message = code.slice(messageNode.start + 1, messageNode.end - 1) - } - else { + } else { message = code.slice(messageNode.start, messageNode.end) } @@ -193,7 +182,10 @@ export function astParseFile(filepath: string, code: string) { mode = 'skip' } - const parentCalleeName = typeof callee?.callee === 'object' && callee?.callee.type === 'MemberExpression' && callee?.callee.property?.name + const parentCalleeName = + typeof callee?.callee === 'object' && + callee?.callee.type === 'MemberExpression' && + callee?.callee.property?.name let isDynamicEach = parentCalleeName === 'each' || parentCalleeName === 'for' if (!isDynamicEach && callee.type === 'TaggedTemplateExpression') { const property = callee.tag?.property?.name @@ -328,8 +320,7 @@ export function createFileTask( `${originalLocation.line}:${originalLocation.column}`, ) location = originalLocation - } - else { + } else { debug?.( 'Cannot find original location for', definition.type, @@ -337,8 +328,7 @@ export function createFileTask( `${processedLocation.column}:${processedLocation.line}`, ) } - } - else { + } else { debug?.( 'Cannot find original location for', definition.type, @@ -387,13 +377,7 @@ export function createFileTask( }) calculateSuiteHash(file) const hasOnly = someTasksAreOnly(file) - interpretTaskModes( - file, - options.testNamePattern, - hasOnly, - false, - options.allowOnly, - ) + interpretTaskModes(file, options.testNamePattern, hasOnly, false, options.allowOnly) markDynamicTests(file.tasks) if (!file.tasks.length) { file.result = { @@ -416,7 +400,7 @@ export async function astCollectTests( const request = await transformSSR(project, filepath) const testFilepath = relative(project.config.root, filepath) if (!request) { - debug?.('Cannot parse', testFilepath, '(vite didn\'t return anything)') + debug?.('Cannot parse', testFilepath, "(vite didn't return anything)") return createFailedFileTask( project, filepath, @@ -450,8 +434,7 @@ function createIndexMap(source: string) { if (char === '\n' || char === '\r\n') { line++ column = 0 - } - else { + } else { column++ } } @@ -480,11 +463,9 @@ function interpretTaskModes( checkAllowOnly(t, allowOnly) t.mode = 'run' } - } - else if (t.mode === 'run' && !includeTask) { + } else if (t.mode === 'run' && !includeTask) { t.mode = 'skip' - } - else if (t.mode === 'only') { + } else if (t.mode === 'only') { checkAllowOnly(t, allowOnly) t.mode = 'run' } @@ -493,12 +474,10 @@ function interpretTaskModes( if (namePattern && !getTaskFullName(t).match(namePattern)) { t.mode = 'skip' } - } - else if (t.type === 'suite') { + } else if (t.type === 'suite') { if (t.mode === 'skip') { skipAllTasks(t) - } - else { + } else { interpretTaskModes(t, namePattern, onlyMode, includeTask, allowOnly) } } @@ -506,7 +485,7 @@ function interpretTaskModes( // if all subtasks are skipped, mark as skip if (suite.mode === 'run') { - if (suite.tasks.length && suite.tasks.every(i => i.mode !== 'run')) { + if (suite.tasks.length && suite.tasks.every((i) => i.mode !== 'run')) { suite.mode = 'skip' } } diff --git a/packages/worker-legacy/src/index.ts b/packages/worker-legacy/src/index.ts index 2118508..5c2e8b6 100644 --- a/packages/worker-legacy/src/index.ts +++ b/packages/worker-legacy/src/index.ts @@ -1,4 +1,8 @@ -import type { SerializedProject, WorkerRunnerOptions, WorkerWSEventEmitter } from 'vitest-vscode-shared' +import type { + SerializedProject, + WorkerRunnerOptions, + WorkerWSEventEmitter, +} from 'vitest-vscode-shared' import type { UserConfig } from 'vitest/node' import { Console } from 'node:console' import { randomUUID } from 'node:crypto' @@ -20,7 +24,7 @@ export async function initVitest( typeof data.debug === 'object' && data.debug.browser ? meta.setupFilePaths.browserDebugLegacy : null, - ].filter(v => v != null), + ].filter((v) => v != null), }) let stdout: Writable | undefined @@ -45,18 +49,14 @@ export async function initVitest( globalThis.console = new Console(stdout, stderr) } - const pnpExecArgv = meta.pnpApi && meta.pnpLoader - ? [ - '--require', - meta.pnpApi, - '--experimental-loader', - meta.pnpLoader, - ] - : undefined + const pnpExecArgv = + meta.pnpApi && meta.pnpLoader + ? ['--require', meta.pnpApi, '--experimental-loader', meta.pnpLoader] + : undefined const args = meta.arguments ? vitestModule.parseCLI(meta.arguments, { - allowUnknownOptions: false, - }).options + allowUnknownOptions: false, + }).options : {} const options = data.debug ? { @@ -78,29 +78,29 @@ export async function initVitest( reporter: undefined, ui: false, includeTaskLocation: true, - poolOptions: meta.pnpApi && meta.pnpLoader - ? { - threads: { - execArgv: pnpExecArgv, - }, - forks: { - execArgv: pnpExecArgv, - }, - vmForks: { - execArgv: pnpExecArgv, - }, - vmThreads: { - execArgv: pnpExecArgv, - }, - } - : {}, + poolOptions: + meta.pnpApi && meta.pnpLoader + ? { + threads: { + execArgv: pnpExecArgv, + }, + forks: { + execArgv: pnpExecArgv, + }, + vmForks: { + execArgv: pnpExecArgv, + }, + vmThreads: { + execArgv: pnpExecArgv, + }, + } + : {}, } if (typeof data.debug === 'object') { const inspect = `${data.debug.host}:${data.debug.port}` if (data.debug.browser) { cliOptions.inspect = inspect - } - else { + } else { cliOptions.inspectBrk = inspect } } @@ -138,9 +138,7 @@ export async function initVitest( enabled: !!data.coverage, reportOnFailure: true, reportsDirectory: join(tmpdir(), `vitest-coverage-${randomUUID()}`), - reporter: [ - ['json', { file: meta.finalCoverageFileName }], - ], + reporter: [['json', { file: meta.finalCoverageFileName }]], }, }, } @@ -196,9 +194,9 @@ export async function initVitest( const workspaceSource: string | false = meta.workspaceFile ? meta.workspaceFile - : (vitest.config.workspace != null || vitest.config.projects != null) - ? vitest.server.config.configFile || false - : false + : vitest.config.workspace != null || vitest.config.projects != null + ? vitest.server.config.configFile || false + : false return { vitest, reporter, @@ -206,11 +204,7 @@ export async function initVitest( projects, meta, createWorker() { - return new ExtensionWorker( - vitest, - !!data.debug, - emitter, - ) + return new ExtensionWorker(vitest, !!data.debug, emitter) }, } } diff --git a/packages/worker-legacy/src/reporter.ts b/packages/worker-legacy/src/reporter.ts index 2d2f6d6..9c7f5c4 100644 --- a/packages/worker-legacy/src/reporter.ts +++ b/packages/worker-legacy/src/reporter.ts @@ -23,23 +23,21 @@ export class VSCodeReporter implements Reporter { } private get collecting(): boolean { - return (this.vitest as any).configOverride.testNamePattern?.toString() === `/${ExtensionWorker.COLLECT_NAME_PATTERN}/` + return ( + (this.vitest as any).configOverride.testNamePattern?.toString() === + `/${ExtensionWorker.COLLECT_NAME_PATTERN}/` + ) } onInit(vitest: VitestCore) { this.vitest = vitest const server = vitest.server.config.server this.setupFilePaths.forEach((setupFile) => { - if (!server.fs.allow.includes(setupFile)) - server.fs.allow.push(setupFile) + if (!server.fs.allow.includes(setupFile)) server.fs.allow.push(setupFile) vitest.projects.forEach((project) => { - project.config.setupFiles = [ - ...project.config.setupFiles || [], - setupFile, - ] + project.config.setupFiles = [...(project.config.setupFiles || []), setupFile] const server = project.server.config.server - if (!server.fs.allow.includes(setupFile)) - server.fs.allow.push(setupFile) + if (!server.fs.allow.includes(setupFile)) server.fs.allow.push(setupFile) // @ts-expect-error internal, Vitest 3 if (project._initBrowserProvider) { this.overrideInitBrowserProvider(project, '_initBrowserProvider') @@ -53,8 +51,7 @@ export class VSCodeReporter implements Reporter { return } const config = 'vite' in browser ? browser.vite.config.server : browser.config.server - if (!config.fs.allow.includes(setupFile)) - config.fs.allow.push(setupFile) + if (!config.fs.allow.includes(setupFile)) config.fs.allow.push(setupFile) }) }) } @@ -80,8 +77,7 @@ export class VSCodeReporter implements Reporter { ExtensionWorker.emitter.on('onDebugAttached', (fullfilled) => { if (fullfilled) { resolve() - } - else { + } else { reject(new Error(`Browser Debugger failed to connect.`)) } }) @@ -115,8 +111,7 @@ export class VSCodeReporter implements Reporter { } } } - } - catch { + } catch { // If parsing fails, continue without parsed location } } @@ -129,9 +124,12 @@ export class VSCodeReporter implements Reporter { return } - const promise = this.rpc.onProcessLog(type, message).catch(() => {}).finally(() => { - this.logPromises.delete(promise) - }) + const promise = this.rpc + .onProcessLog(type, message) + .catch(() => {}) + .finally(() => { + this.logPromises.delete(promise) + }) this.logPromises.add(promise) } @@ -144,7 +142,7 @@ export class VSCodeReporter implements Reporter { // the new version uses browser.parseErrorStacktrace if ('getBrowserSourceMapModuleById' in project) { return parseErrorStacktrace(obj as Error, { - getSourceMap: file => (project as any).getBrowserSourceMapModuleById(file), + getSourceMap: (file) => (project as any).getBrowserSourceMapModuleById(file), }) } @@ -182,7 +180,11 @@ export class VSCodeReporter implements Reporter { this.rpc.onTaskUpdate(packs) } - async onFinished(files?: RunnerTestFile[], errors: unknown[] = this.vitest.state.getUnhandledErrors(), coverage?: unknown) { + async onFinished( + files?: RunnerTestFile[], + errors: unknown[] = this.vitest.state.getUnhandledErrors(), + coverage?: unknown, + ) { const collecting = this.collecting let output = '' @@ -217,7 +219,7 @@ export class VSCodeReporter implements Reporter { } onCollected(files?: RunnerTestFile[]) { - files?.forEach(file => this.rpc.onCollected(file, this.collecting)) + files?.forEach((file) => this.rpc.onCollected(file, this.collecting)) } onWatcherRerun(files: string[]) { @@ -230,7 +232,5 @@ export class VSCodeReporter implements Reporter { } function isPrimitive(value: unknown) { - return ( - value === null || (typeof value !== 'function' && typeof value !== 'object') - ) + return value === null || (typeof value !== 'function' && typeof value !== 'object') } diff --git a/packages/worker-legacy/src/watcher.ts b/packages/worker-legacy/src/watcher.ts index 19dfd0d..696b6d0 100644 --- a/packages/worker-legacy/src/watcher.ts +++ b/packages/worker-legacy/src/watcher.ts @@ -40,7 +40,7 @@ export class ExtensionWorkerWatcher { if (state.watchEveryFile) { vitest.logger.log( 'Rerunning all tests due to file changes:', - ...files.map(f => relative(vitest.config.root, f)), + ...files.map((f) => relative(vitest.config.root, f)), namePattern ? `with pattern ${namePattern}` : '', ) return await originalScheduleRerun.call(this, files) @@ -53,8 +53,7 @@ export class ExtensionWorkerWatcher { const currentChanged = [...this.changedTests] this.changedTests.clear() for (const file of currentChanged) { - if (state.isTestFileWatched(file)) - this.changedTests.add(file) + if (state.isTestFileWatched(file)) this.changedTests.add(file) } } // the other test file was edited, ignore it @@ -65,11 +64,10 @@ export class ExtensionWorkerWatcher { if (this.changedTests.size) { vitest.logger.log( 'Rerunning tests due to file changes:', - ...Array.from(this.changedTests, f => relative(vitest.config.root, f)), + ...Array.from(this.changedTests, (f) => relative(vitest.config.root, f)), namePattern ? `with pattern ${namePattern}` : '', ) - } - else { + } else { await state.collectTests(files, changedFiles) } @@ -79,7 +77,7 @@ export class ExtensionWorkerWatcher { private async collectTests(trigger: string[], tests: string[]) { const vitest = this.worker.vitest - const specs = tests.flatMap(file => vitest.getProjectsByTestFile(file)) + const specs = tests.flatMap((file) => vitest.getProjectsByTestFile(file)) const astSpecs: [project: WorkspaceProject, file: string][] = [] for (const [project, file] of specs) { @@ -87,7 +85,10 @@ export class ExtensionWorkerWatcher { } this.worker.setGlobalTestNamePattern(ExtensionWorker.COLLECT_NAME_PATTERN) - vitest.logger.log('Collecting tests due to file changes:', ...trigger.map(f => relative(vitest.config.root, f))) + vitest.logger.log( + 'Collecting tests due to file changes:', + ...trigger.map((f) => relative(vitest.config.root, f)), + ) if (astSpecs.length) { vitest.logger.log('Collecting using AST explorer...') @@ -97,14 +98,11 @@ export class ExtensionWorkerWatcher { } private isTestFileWatched(testFile: string) { - if (!this.files?.length) - return false + if (!this.files?.length) return false return this.files.some((file) => { - if (file === testFile) - return true - if (file.at(-1) === '/') - return testFile.startsWith(file) + if (file === testFile) return true + if (file.at(-1) === '/') return testFile.startsWith(file) return false }) } diff --git a/packages/worker-legacy/src/worker.ts b/packages/worker-legacy/src/worker.ts index 95185ce..3c3724d 100644 --- a/packages/worker-legacy/src/worker.ts +++ b/packages/worker-legacy/src/worker.ts @@ -37,7 +37,10 @@ export class ExtensionWorker implements ExtensionWorkerTransport { } public get collecting() { - return this.configOverride.testNamePattern?.toString() === `/${ExtensionWorker.COLLECT_NAME_PATTERN}/` + return ( + this.configOverride.testNamePattern?.toString() === + `/${ExtensionWorker.COLLECT_NAME_PATTERN}/` + ) } private get configOverride(): Partial { @@ -47,14 +50,11 @@ export class ExtensionWorker implements ExtensionWorkerTransport { public setGlobalTestNamePattern(pattern?: string | RegExp): void { if (pattern == null || pattern === '') { this.configOverride.testNamePattern = undefined - } - else if ('setGlobalTestNamePattern' in this.vitest) { + } else if ('setGlobalTestNamePattern' in this.vitest) { return this.vitest.setGlobalTestNamePattern(pattern) - } - else { - this.configOverride.testNamePattern = typeof pattern === 'string' - ? new RegExp(pattern) - : pattern + } else { + this.configOverride.testNamePattern = + typeof pattern === 'string' ? new RegExp(pattern) : pattern } } @@ -74,7 +74,7 @@ export class ExtensionWorker implements ExtensionWorkerTransport { const specifications: [project: WorkspaceProject, filepath: string][] = [] for (const [projectName, filepath] of files) { - const project = this.vitest.projects.find(project => project.getName() === projectName) + const project = this.vitest.projects.find((project) => project.getName() === projectName) assert(project, `Project ${projectName} not found for file ${filepath}`) specifications.push([project, filepath]) } @@ -91,16 +91,23 @@ export class ExtensionWorker implements ExtensionWorkerTransport { const runConcurrently = limitConcurrency(5) - const promises = specs.map(([project, filename]) => runConcurrently( - () => astCollectTests(project, filename).catch(err => createFailedFileTask(project, filename, err)), - )) + const promises = specs.map(([project, filename]) => + runConcurrently(() => + astCollectTests(project, filename).catch((err) => + createFailedFileTask(project, filename, err), + ), + ), + ) const files = await Promise.all(promises) this.configOverride.testNamePattern = new RegExp(ExtensionWorker.COLLECT_NAME_PATTERN) await this.report('onCollected', files) this.setTestNamePattern(undefined) } - public async updateSnapshots(files?: ExtensionTestSpecification[] | string[] | undefined, testNamePattern?: string | undefined) { + public async updateSnapshots( + files?: ExtensionTestSpecification[] | string[] | undefined, + testNamePattern?: string | undefined, + ) { this.configOverride.snapshotOptions = { updateSnapshot: 'all', // environment is resolved inside a worker thread @@ -108,29 +115,30 @@ export class ExtensionWorker implements ExtensionWorkerTransport { } try { return await this.runTests(files, testNamePattern) - } - finally { + } finally { delete this.configOverride.snapshotOptions } } - async resolveTestSpecs(specs: string[] | ExtensionTestSpecification[] | undefined): Promise { + async resolveTestSpecs( + specs: string[] | ExtensionTestSpecification[] | undefined, + ): Promise { if (!specs || typeof specs[0] === 'string') { const files = await this.globTestSpecifications(specs as string[] | undefined) return files.map((spec) => { const project = spec[0] const file = spec[1] - return [ - project.getName(), - file, - ] + return [project.getName(), file] }) } - return (specs as ExtensionTestSpecification[] || []) + return (specs as ExtensionTestSpecification[]) || [] } - public async runTests(specsOrPaths: ExtensionTestSpecification[] | string[] | undefined, testNamePattern?: string) { + public async runTests( + specsOrPaths: ExtensionTestSpecification[] | string[] | undefined, + testNamePattern?: string, + ) { // @ts-expect-error private method in Vitest <=2.1.5 await this.vitest.initBrowserProviders?.() @@ -152,7 +160,7 @@ export class ExtensionWorker implements ExtensionWorkerTransport { // reset cached test files list this.vitest.projects.forEach((project) => { // testFilesList is private - (project as any).testFilesList = null + ;(project as any).testFilesList = null }) const files = await this.globTestSpecifications() return files.map((spec) => { @@ -195,13 +203,17 @@ export class ExtensionWorker implements ExtensionWorkerTransport { }) } - private async runTestFiles(specs: ExtensionTestSpecification[], testNamePattern?: string | undefined, runAllFiles = false) { + private async runTestFiles( + specs: ExtensionTestSpecification[], + testNamePattern?: string | undefined, + runAllFiles = false, + ) { await (this.vitest as any).runningPromise this.setTestNamePattern(testNamePattern) // populate cache so it can find test files - await this.globTestSpecifications(specs.map(f => f[1])) + await this.globTestSpecifications(specs.map((f) => f[1])) await this.rerunTests(specs, runAllFiles) } @@ -211,18 +223,18 @@ export class ExtensionWorker implements ExtensionWorkerTransport { } private async rerunTests(specs: ExtensionTestSpecification[], runAllFiles = false) { - const paths = specs.map(spec => spec[1]) + const paths = specs.map((spec) => spec[1]) const specsToRun = specs.flatMap((spec) => { const file = typeof spec === 'string' ? spec : spec[1] const fileSpecs = this.vitest.getModuleSpecifications ? this.vitest.getModuleSpecifications(file) - // supported by the older version - : this.vitest.getProjectsByTestFile(file) + : // supported by the older version + this.vitest.getProjectsByTestFile(file) if (!fileSpecs.length) { return [] } - return fileSpecs.filter(s => s[0].getName() === spec[0]) + return fileSpecs.filter((s) => s[0].getName() === spec[0]) }) await Promise.all([ this.report('onWatcherRerun', paths), @@ -256,10 +268,10 @@ export class ExtensionWorker implements ExtensionWorkerTransport { private updateLastChanged(filepath: string) { this.vitest.projects.forEach(({ server, browser }) => { const serverMods = server.moduleGraph.getModulesByFile(filepath) - serverMods?.forEach(mod => server.moduleGraph.invalidateModule(mod)) + serverMods?.forEach((mod) => server.moduleGraph.invalidateModule(mod)) if (browser) { const browserMods = browser.vite.moduleGraph.getModulesByFile(filepath) - browserMods?.forEach(mod => browser.vite.moduleGraph.invalidateModule(mod)) + browserMods?.forEach((mod) => browser.vite.moduleGraph.invalidateModule(mod)) } }) } @@ -273,8 +285,7 @@ export class ExtensionWorker implements ExtensionWorkerTransport { this.scheduleRerun(needRerun) } } - } - catch (err) { + } catch (err) { this.vitest.logger.error('Error during analyzing changed files', err) } } @@ -288,11 +299,9 @@ export class ExtensionWorker implements ExtensionWorkerTransport { let content: string | null = null const projects = [] for (const project of this.vitest.projects) { - if (this.isTestFile( - project, - file, - () => content ?? (content = readFileSync(file, 'utf-8')), - )) { + if ( + this.isTestFile(project, file, () => content ?? (content = readFileSync(file, 'utf-8'))) + ) { testFiles.push(file) ;(project as any).testFilesList?.push(file) this.vitest.changedTests.add(file) @@ -301,13 +310,12 @@ export class ExtensionWorker implements ExtensionWorkerTransport { } // to support Vitest 1.4.0 if (projects.length && (this.vitest as any).projectsTestFiles) { - (this.vitest as any).projectsTestFiles.set(file, new Set(projects)) + ;(this.vitest as any).projectsTestFiles.set(file, new Set(projects)) } } - testFiles.forEach(file => this.scheduleRerun([file])) - } - catch (err) { + testFiles.forEach((file) => this.scheduleRerun([file])) + } catch (err) { this.vitest.logger.error('Error during analyzing created files', err) } } @@ -321,8 +329,8 @@ export class ExtensionWorker implements ExtensionWorkerTransport { return true } if ( - project.config.includeSource?.length - && mm.isMatch(relativeId, project.config.includeSource) + project.config.includeSource?.length && + mm.isMatch(relativeId, project.config.includeSource) ) { const source = getContent() return source.includes('import.meta.vitest') @@ -330,13 +338,18 @@ export class ExtensionWorker implements ExtensionWorkerTransport { return false } - async watchTests(files?: ExtensionTestSpecification[] | string[] | undefined, testNamePatern?: string) { - await this.globTestSpecifications(files?.map(f => typeof f === 'string' ? f : f[1])) + async watchTests( + files?: ExtensionTestSpecification[] | string[] | undefined, + testNamePatern?: string, + ) { + await this.globTestSpecifications(files?.map((f) => (typeof f === 'string' ? f : f[1]))) if (files) - this.watcher.trackTests(files.map(f => typeof f === 'string' ? f : f[1]), testNamePatern) - else - this.watcher.trackEveryFile() + this.watcher.trackTests( + files.map((f) => (typeof f === 'string' ? f : f[1])), + testNamePatern, + ) + else this.watcher.trackEveryFile() } async exit() { diff --git a/packages/worker/package.json b/packages/worker/package.json index dfeb65a..0a1918f 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "vitest-vscode-worker", - "type": "module", "private": true, + "type": "module", "exports": { ".": "./src/index.ts" }, diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts index 5e51992..d312e13 100644 --- a/packages/worker/src/index.ts +++ b/packages/worker/src/index.ts @@ -1,4 +1,8 @@ -import type { SerializedProject, WorkerRunnerOptions, WorkerWSEventEmitter } from 'vitest-vscode-shared' +import type { + SerializedProject, + WorkerRunnerOptions, + WorkerWSEventEmitter, +} from 'vitest-vscode-shared' import type { TestUserConfig } from 'vitest/node' import { Console } from 'node:console' import { Writable } from 'node:stream' @@ -38,8 +42,8 @@ export async function initVitest( const args = meta.arguments ? vitestModule.parseCLI(meta.arguments, { - allowUnknownOptions: false, - }).options + allowUnknownOptions: false, + }).options : {} const options = data.debug ? { @@ -72,8 +76,7 @@ export async function initVitest( const inspect = `${data.debug.host}:${data.debug.port}` if (data.debug.browser) { cliOptions.inspect = inspect - } - else { + } else { cliOptions.inspectBrk = inspect } } @@ -104,9 +107,7 @@ export async function initVitest( coverage: { enabled: !!data.coverage, reportOnFailure: true, - reporter: [ - ['json', { file: meta.finalCoverageFileName }], - ], + reporter: [['json', { file: meta.finalCoverageFileName }]], }, }, } @@ -158,9 +159,8 @@ export async function initVitest( } }) - const workspaceSource: string | false = (vitest.config.projects != null) - ? vitest.vite.config.configFile || false - : false + const workspaceSource: string | false = + vitest.config.projects != null ? vitest.vite.config.configFile || false : false return { vitest, reporter, @@ -168,11 +168,7 @@ export async function initVitest( projects, meta, createWorker() { - return new ExtensionWorker( - vitest, - !!data.debug, - emitter, - ) + return new ExtensionWorker(vitest, !!data.debug, emitter) }, } } diff --git a/packages/worker/src/reporter.ts b/packages/worker/src/reporter.ts index 2b29e69..f447b34 100644 --- a/packages/worker/src/reporter.ts +++ b/packages/worker/src/reporter.ts @@ -29,12 +29,7 @@ export class VSCodeReporter implements Reporter { this.setupFilePaths = meta.setupFilePaths this.debug = debug if (meta.pnpApi && meta.pnpLoader) { - this.execArgv.push( - '--require', - meta.pnpApi, - '--experimental-loader', - meta.pnpLoader, - ) + this.execArgv.push('--require', meta.pnpApi, '--experimental-loader', meta.pnpLoader) } } @@ -87,8 +82,7 @@ export class VSCodeReporter implements Reporter { } } } - } - catch { + } catch { // If parsing fails, continue without parsed location } } @@ -116,7 +110,7 @@ export class VSCodeReporter implements Reporter { } onTestRunStart(specifications: ReadonlyArray) { - const files = specifications.map(spec => spec.moduleId) + const files = specifications.map((spec) => spec.moduleId) this.rpc.onTestRunStart([...new Set(files)]) this.vitest.state.filesMap.clear() } @@ -126,7 +120,7 @@ export class VSCodeReporter implements Reporter { } async onTestRunEnd(testModules: ReadonlyArray) { - const files = testModules.map(m => getEntityJSONTask(m)) + const files = testModules.map((m) => getEntityJSONTask(m)) // Make sure we rendered everything before ending the test run // If test run is no active, the log will be lost @@ -165,8 +159,7 @@ export class VSCodeReporter implements Reporter { if (this.debuggerAttached) { resolve() return - } - else if (this.debuggerAttached === false) { + } else if (this.debuggerAttached === false) { reject(new Error(`Browser Debugger failed to connect.`)) return } @@ -175,8 +168,7 @@ export class VSCodeReporter implements Reporter { ExtensionWorker.emitter.on('onDebugAttached', (fullfilled) => { if (fullfilled) { resolve() - } - else { + } else { reject(new Error(`Browser Debugger failed to connect.`)) } }) @@ -207,9 +199,12 @@ export class VSCodeReporter implements Reporter { return } - const promise = this.rpc.onProcessLog(type, message).catch(() => {}).finally(() => { - this.logPromises.delete(promise) - }) + const promise = this.rpc + .onProcessLog(type, message) + .catch(() => {}) + .finally(() => { + this.logPromises.delete(promise) + }) this.logPromises.add(promise) } diff --git a/packages/worker/src/runner.ts b/packages/worker/src/runner.ts index 3249619..35c5e39 100644 --- a/packages/worker/src/runner.ts +++ b/packages/worker/src/runner.ts @@ -1,4 +1,9 @@ -import type { ExtensionTestFileSpecification, ExtensionTestSpecification, VitestWorkerRPC, WorkerWSEventEmitter } from 'vitest-vscode-shared' +import type { + ExtensionTestFileSpecification, + ExtensionTestSpecification, + VitestWorkerRPC, + WorkerWSEventEmitter, +} from 'vitest-vscode-shared' import type { TestSpecification, Vitest as VitestCore } from 'vitest/node' export class ExtensionWorkerRunner { @@ -39,7 +44,7 @@ export class ExtensionWorkerRunner { public async collectSpecifications(specifications: TestSpecification[]): Promise { const testModules = await this.vitest.experimental_parseSpecifications(specifications) - const promises = testModules.map(module => + const promises = testModules.map((module) => // TODO: fix "as any" this.rpc.onCollected((module as any).task, true), ) @@ -70,8 +75,7 @@ export class ExtensionWorkerRunner { if (!filesOrDirectories || this.isOnlyDirectories(filesOrDirectories)) { const specifications = await this.vitest.getRelevantTestSpecifications(filesOrDirectories) await this.vitest.rerunTestSpecifications(specifications, true) - } - else { + } else { const specifications = await this.resolveTestSpecifications(filesOrDirectories) await this.vitest.rerunTestSpecifications(specifications, false) } @@ -84,8 +88,7 @@ export class ExtensionWorkerRunner { if (currentTestNamePattern) { this.vitest.setGlobalTestNamePattern(currentTestNamePattern) - } - else { + } else { this.vitest.resetGlobalTestNamePattern() } } @@ -98,7 +101,9 @@ export class ExtensionWorkerRunner { return this.vitest.config.testNamePattern } - async resolveTestSpecifications(files: ExtensionTestSpecification[]): Promise { + async resolveTestSpecifications( + files: ExtensionTestSpecification[], + ): Promise { const specifications: TestSpecification[] = [] files.forEach((file) => { const [projectName, filepath] = file @@ -108,17 +113,18 @@ export class ExtensionWorkerRunner { return specifications } - async updateSnapshots(filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string): Promise { + async updateSnapshots( + filesOrDirectories?: ExtensionTestSpecification[] | string[], + testNamePattern?: string, + ): Promise { const currentTestNamePattern = this.getGlobalTestNamePattern() this.vitest.enableSnapshotUpdate() try { return await this.runTests(filesOrDirectories, testNamePattern) - } - finally { + } finally { if (currentTestNamePattern) { this.vitest.setGlobalTestNamePattern(currentTestNamePattern) - } - else { + } else { this.vitest.resetSnapshotUpdate() } } diff --git a/packages/worker/src/watcher.ts b/packages/worker/src/watcher.ts index 5c19e7b..6a4e6b1 100644 --- a/packages/worker/src/watcher.ts +++ b/packages/worker/src/watcher.ts @@ -9,7 +9,10 @@ export class ExtensionWorkerWatcher { private trackedTestItems: Record = {} private trackedDirectories: string[] = [] - constructor(vitest: Vitest, private runner: ExtensionWorkerRunner) { + constructor( + vitest: Vitest, + private runner: ExtensionWorkerRunner, + ) { vitest.onFilterWatchedSpecification((specification) => { const shouldRun = this.shouldRunSpecification(specification) if (shouldRun) { @@ -50,8 +53,7 @@ export class ExtensionWorkerWatcher { this.enabled = true if (typeof filesOrDirectories[0] === 'string') { this.trackedDirectories = filesOrDirectories as string[] - } - else { + } else { for (const [project, file] of filesOrDirectories) { if (!this.trackedTestItems[project]) { this.trackedTestItems[project] = [] @@ -74,14 +76,11 @@ export class ExtensionWorkerWatcher { } private isTestFileWatched(testFile: string, files: string[]) { - if (!files?.length) - return false + if (!files?.length) return false return files.some((file) => { - if (file === testFile) - return true - if (file.at(-1) === '/') - return testFile.startsWith(file) + if (file === testFile) return true + if (file.at(-1) === '/') return testFile.startsWith(file) return false }) } diff --git a/packages/worker/src/worker.ts b/packages/worker/src/worker.ts index fa32a1c..6d1924b 100644 --- a/packages/worker/src/worker.ts +++ b/packages/worker/src/worker.ts @@ -38,23 +38,31 @@ export class ExtensionWorker implements ExtensionWorkerTransport { return this.runner.cancelRun() } - async runTests(filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string): Promise { + async runTests( + filesOrDirectories?: ExtensionTestSpecification[] | string[], + testNamePattern?: string, + ): Promise { await this.runner.runTests(filesOrDirectories, testNamePattern) } - async updateSnapshots(filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string): Promise { + async updateSnapshots( + filesOrDirectories?: ExtensionTestSpecification[] | string[], + testNamePattern?: string, + ): Promise { return this.runner.updateSnapshots(filesOrDirectories, testNamePattern) } - async watchTests(filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string): Promise { + async watchTests( + filesOrDirectories?: ExtensionTestSpecification[] | string[], + testNamePattern?: string, + ): Promise { // Reset previous tracking state so re-clicking continuous run // picks up the new files/pattern instead of appending to old ones this.watcher.stopTracking() if (testNamePattern) { this.vitest.setGlobalTestNamePattern(testNamePattern) - } - else { + } else { this.vitest.resetGlobalTestNamePattern() } @@ -64,8 +72,7 @@ export class ExtensionWorker implements ExtensionWorkerTransport { if (!filesOrDirectories) { this.watcher.trackEveryFile() - } - else { + } else { this.watcher.trackTestItems(filesOrDirectories) } } @@ -75,11 +82,11 @@ export class ExtensionWorker implements ExtensionWorkerTransport { } onFilesChanged(files: string[]): void { - files.forEach(file => this.vitest.watcher.onFileChange(file)) + files.forEach((file) => this.vitest.watcher.onFileChange(file)) } onFilesCreated(files: string[]): void { - files.forEach(file => this.vitest.watcher.onFileCreate(file)) + files.forEach((file) => this.vitest.watcher.onFileCreate(file)) } async exit() { @@ -96,8 +103,8 @@ export class ExtensionWorker implements ExtensionWorkerTransport { const environments = new Map() for (const name in project.vite.environments) { const environment = project.vite.environments[name] - const nodes = [...environment.moduleGraph.getModulesByFile(moduleId) || []] - if (nodes.some(n => n.transformResult)) { + const nodes = [...(environment.moduleGraph.getModulesByFile(moduleId) || [])] + if (nodes.some((n) => n.transformResult)) { environments.set(name, { timestamp: nodes[0].lastInvalidationTimestamp }) } } @@ -112,10 +119,11 @@ export class ExtensionWorker implements ExtensionWorkerTransport { } getTransformedModule(projectName: string, environmentName: string, moduleId: string) { - const project = this.vitest.projects.find(p => p.name === projectName) - const environment = environmentName === '__browser__' - ? project?.browser?.vite?.environments.client - : project?.vite.environments[environmentName] + const project = this.vitest.projects.find((p) => p.name === projectName) + const environment = + environmentName === '__browser__' + ? project?.browser?.vite?.environments.client + : project?.vite.environments[environmentName] const files = environment?.moduleGraph.getModulesByFile(moduleId) if (!files || !files.size) { return null diff --git a/packages/worker/tsconfig.json b/packages/worker/tsconfig.json index c9bb7b1..bced9f9 100644 --- a/packages/worker/tsconfig.json +++ b/packages/worker/tsconfig.json @@ -7,12 +7,6 @@ "noEmit": true, "outDir": "dist" }, - "include": [ - "**/*.ts", - "**/*.tsx" - ], - "exclude": [ - "node_modules", - "dist" - ] + "include": ["**/*.ts", "**/*.tsx"], + "exclude": ["node_modules", "dist"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7dea54f..0357b01 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,9 +6,6 @@ settings: catalogs: default: - '@antfu/eslint-config': - specifier: ^7.7.0 - version: 7.7.0 '@playwright/test': specifier: ^1.42.1 version: 1.57.0 @@ -66,9 +63,6 @@ catalogs: changelogithub: specifier: ^13.15.0 version: 13.16.1 - eslint: - specifier: ^10.0.3 - version: 10.0.3 execa: specifier: ^8.0.1 version: 8.0.1 @@ -169,9 +163,6 @@ importers: .: devDependencies: - '@antfu/eslint-config': - specifier: 'catalog:' - version: 7.7.0(@typescript-eslint/rule-tester@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(@typescript-eslint/typescript-estree@8.57.0(typescript@5.9.3))(@typescript-eslint/utils@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(@vue/compiler-sfc@3.5.25)(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)(vitest@4.1.0-beta.3) '@playwright/test': specifier: 'catalog:' version: 1.57.0 @@ -232,9 +223,6 @@ importers: changelogithub: specifier: 'catalog:' version: 13.16.1(magicast@0.3.5) - eslint: - specifier: 'catalog:' - version: 10.0.3(jiti@2.6.1) execa: specifier: 'catalog:' version: 8.0.1 @@ -256,6 +244,9 @@ importers: mocha: specifier: 'catalog:' version: 10.8.2 + oxfmt: + specifier: ^0.37.0 + version: 0.37.0 pathe: specifier: 'catalog:' version: 1.1.2 @@ -596,76 +587,9 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@antfu/eslint-config@7.7.0': - resolution: {integrity: sha512-lkxb84o8z4v1+me51XlrHHF6zvOZfvTu6Y11t6h6v17JSMl9yoNHwC0Sqp/NfMTHie/LGgjyXOupXpQCXxfs1Q==} - hasBin: true - peerDependencies: - '@angular-eslint/eslint-plugin': ^21.1.0 - '@angular-eslint/eslint-plugin-template': ^21.1.0 - '@angular-eslint/template-parser': ^21.1.0 - '@eslint-react/eslint-plugin': ^2.11.0 - '@next/eslint-plugin-next': '>=15.0.0' - '@prettier/plugin-xml': ^3.4.1 - '@unocss/eslint-plugin': '>=0.50.0' - astro-eslint-parser: ^1.0.2 - eslint: ^9.10.0 || ^10.0.0 - eslint-plugin-astro: ^1.2.0 - eslint-plugin-format: '>=0.1.0' - eslint-plugin-jsx-a11y: '>=6.10.2' - eslint-plugin-react-hooks: ^7.0.0 - eslint-plugin-react-refresh: ^0.5.0 - eslint-plugin-solid: ^0.14.3 - eslint-plugin-svelte: '>=2.35.1' - eslint-plugin-vuejs-accessibility: ^2.4.1 - prettier-plugin-astro: ^0.14.0 - prettier-plugin-slidev: ^1.0.5 - svelte-eslint-parser: '>=0.37.0' - peerDependenciesMeta: - '@angular-eslint/eslint-plugin': - optional: true - '@angular-eslint/eslint-plugin-template': - optional: true - '@angular-eslint/template-parser': - optional: true - '@eslint-react/eslint-plugin': - optional: true - '@next/eslint-plugin-next': - optional: true - '@prettier/plugin-xml': - optional: true - '@unocss/eslint-plugin': - optional: true - astro-eslint-parser: - optional: true - eslint-plugin-astro: - optional: true - eslint-plugin-format: - optional: true - eslint-plugin-jsx-a11y: - optional: true - eslint-plugin-react-hooks: - optional: true - eslint-plugin-react-refresh: - optional: true - eslint-plugin-solid: - optional: true - eslint-plugin-svelte: - optional: true - eslint-plugin-vuejs-accessibility: - optional: true - prettier-plugin-astro: - optional: true - prettier-plugin-slidev: - optional: true - svelte-eslint-parser: - optional: true - '@antfu/install-pkg@0.1.1': resolution: {integrity: sha512-LyB/8+bSfa0DFGC06zpCEfs89/XoWZwws5ygEa5D+Xsm3OfI+aXQ86VgVG7Acyef+rSZ5HE7J8rrxzrQeM3PjQ==} - '@antfu/install-pkg@1.1.0': - resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} @@ -921,12 +845,6 @@ packages: resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true - '@clack/core@1.1.0': - resolution: {integrity: sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA==} - - '@clack/prompts@1.1.0': - resolution: {integrity: sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g==} - '@csstools/color-helpers@5.1.0': resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} engines: {node: '>=18'} @@ -986,17 +904,6 @@ packages: resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} engines: {node: '>=20.19.0'} - '@e18e/eslint-plugin@0.2.0': - resolution: {integrity: sha512-mXgODVwhuDjTJ+UT+XSvmMmCidtGKfrV5nMIv1UtpWex2pYLsIM3RSpT8HWIMAebS9qANbXPKlSX4BE7ZvuCgA==} - peerDependencies: - eslint: ^9.0.0 || ^10.0.0 - oxlint: ^1.41.0 - peerDependenciesMeta: - eslint: - optional: true - oxlint: - optional: true - '@emnapi/core@1.8.1': resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} @@ -1006,14 +913,6 @@ packages: '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} - '@es-joy/jsdoccomment@0.84.0': - resolution: {integrity: sha512-0xew1CxOam0gV5OMjh2KjFQZsKL2bByX1+q4j3E73MpYIdyUxcZb/xQct9ccUb+ve5KGUYbCUxyPnYB7RbuP+w==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - - '@es-joy/resolve.exports@1.2.0': - resolution: {integrity: sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==} - engines: {node: '>=10'} - '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} @@ -1338,63 +1237,6 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-plugin-eslint-comments@4.7.1': - resolution: {integrity: sha512-Ql2nJFwA8wUGpILYGOQaT1glPsmvEwE0d+a+l7AALLzQvInqdbXJdx7aSu0DpUX9dB1wMVBMhm99/++S3MdEtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 - - '@eslint-community/eslint-utils@4.9.1': - resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - - '@eslint-community/regexpp@4.12.2': - resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - '@eslint/compat@2.0.3': - resolution: {integrity: sha512-SjIJhGigp8hmd1YGIBwh7Ovri7Kisl42GYFjrOyHhtfYGGoLW6teYi/5p8W50KSsawUPpuLOSmsq1bD0NGQLBw==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - peerDependencies: - eslint: ^8.40 || 9 || 10 - peerDependenciesMeta: - eslint: - optional: true - - '@eslint/config-array@0.23.3': - resolution: {integrity: sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - - '@eslint/config-helpers@0.5.3': - resolution: {integrity: sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - - '@eslint/core@0.17.0': - resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/core@1.1.1': - resolution: {integrity: sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - - '@eslint/markdown@7.5.1': - resolution: {integrity: sha512-R8uZemG9dKTbru/DQRPblbJyXpObwKzo8rv1KYGGuPUPtjM4LXBYM9q5CIZAComzZupws3tWbDwam5AFpPLyJQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/object-schema@3.0.3': - resolution: {integrity: sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - - '@eslint/plugin-kit@0.4.1': - resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/plugin-kit@0.6.1': - resolution: {integrity: sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@exodus/bytes@1.11.0': resolution: {integrity: sha512-wO3vd8nsEHdumsXrjGO/v4p6irbg7hy9kvIeR6i2AwylZSk4HJdWgL0FNaVquW1+AweJcdvU1IEpuIWk/WaPnA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -1408,22 +1250,6 @@ packages: resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} - engines: {node: '>=18.18.0'} - - '@humanfs/node@0.16.7': - resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} - engines: {node: '>=18.18.0'} - - '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - - '@humanwhocodes/retry@0.4.3': - resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} - engines: {node: '>=18.18'} - '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -1466,13 +1292,123 @@ packages: '@one-ini/wasm@0.1.1': resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} - '@ota-meshi/ast-token-store@0.3.0': - resolution: {integrity: sha512-XRO0zi2NIUKq2lUk3T1ecFSld1fMWRKE6naRFGkgkdeosx7IslyUKNv5Dcb5PJTja9tHJoFu0v/7yEpAkrkrTg==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@oxc-project/types@0.112.0': resolution: {integrity: sha512-m6RebKHIRsax2iCwVpYW2ErQwa4ywHJrE4sCK3/8JK8ZZAWOKXaRJFl/uP51gaVyyXlaS4+chU1nSCdzYf6QqQ==} + '@oxfmt/binding-android-arm-eabi@0.37.0': + resolution: {integrity: sha512-2AW4VHG6mePEb1r4l6nBOVz1MwevNa0obayXd5Xce+gtP+cL/FCaoVK7JtpqCj4cEVxbLU4jijBUIWK41X2GGg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.37.0': + resolution: {integrity: sha512-fW/oGfK337wYb/qfoeqKrcv3tMv7DlsKVmHca0DZrWHLMUYftpYD9z7TYOD5VQ1Lg8D/iTzQiTneT2CAMThPxg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.37.0': + resolution: {integrity: sha512-8sfuzKA8Ic43ZCC1ZMwk12rNVao9nn7K6crTvtLQy+yQVbXE1xxR4P1YTxqaLEOGJNq+sB2xyrfJywKVF9VODw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.37.0': + resolution: {integrity: sha512-X67bSfIDL1ufBY5OLxK3oG5Gj8Jvp7f2yEDVSduvolV+a0k6KJ1ZDFqG9wyTfancKVb7aZ5lTs63pAOxZYrj4A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.37.0': + resolution: {integrity: sha512-ULQ6098xUjZoZbT38qHj3Bgwq1BbglgnLOpB01Dsi79n94Dd4V0dPD4TlnSCdX33Rr/DBje4S2IpzgnAs8kknw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.37.0': + resolution: {integrity: sha512-GsNuj91bKV8jHdRBtnCxe7vpX06IADFbyOwkScmDaoroRooBOK9NeStctE0/wE4DT6QY7qfF0YzUTGB2e5tjzQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.37.0': + resolution: {integrity: sha512-13ywNNp291Tc1nUaISUS3u2Y2O26zERJoVy1xK2uO+/1oon3EAHxMrXd0bQjopT+Ia3rTPwO6iFxW1DZratehA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.37.0': + resolution: {integrity: sha512-JAYqsm6sTfZZbUp1CQfWZ+prXg9qBRSs5bO7bgLdD9SiqsDHn2+EfJXESL6uLqT/UO5FYvE16wivup0EOHit5w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxfmt/binding-linux-arm64-musl@0.37.0': + resolution: {integrity: sha512-EZj3TurW1iLbq+7tBr++wsxwFyD+pvjMrTNRuSynDrs8J7w46cu/ZIzU/lFw7OG1/tDRDZ9nrKXxwbvIKXo2zA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@oxfmt/binding-linux-ppc64-gnu@0.37.0': + resolution: {integrity: sha512-ELXrDe1xRj+f7VpzJO2j54izMbi+Hov+kdqusXO3T1BwVEbA5sWgZrVMqkwEsj4k6Lw/obJK1SLUeNulR1D//g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@oxfmt/binding-linux-riscv64-gnu@0.37.0': + resolution: {integrity: sha512-79gMZgLD62dGmo5Xl4gaMc6NHRFj3GuxPrchHBlW54tcRSXTtb3gLh/J6Bl8nbbzSFRQGR7dkNQ8yYadXt6txQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxfmt/binding-linux-riscv64-musl@0.37.0': + resolution: {integrity: sha512-QFdi9OhyWxnh975jeG490atcINXZwZb7epyNASPaT4wcodOTuDitrDgSPT8CFl8BcGOFTGZ6c3P/s8Afeg1Ngg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + + '@oxfmt/binding-linux-s390x-gnu@0.37.0': + resolution: {integrity: sha512-qweAj7+pLFQXfe3UU7EZiOmo+/2SWjzVZjyyTDcrZAT0E92zEKJBvYpHinUAOqipfo2Xlp8GIfq0FSb5Tmqd8g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@oxfmt/binding-linux-x64-gnu@0.37.0': + resolution: {integrity: sha512-Lqc/0vS20qzZLw1ThpWn1hQgRqj4rM+E7PuBzrqp+wLH5lYFqieAiontGpl2pMPvJ0QrmQYav9mslHlAB5kOSQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxfmt/binding-linux-x64-musl@0.37.0': + resolution: {integrity: sha512-TnJm22+1cEcpYXzbcXS5Z9+9c+R0ronFdx5bG4OTdOL/wSpQQKzc2izgAXJ03QkP3tq7aAPhlhhxasvH3xgoUA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@oxfmt/binding-openharmony-arm64@0.37.0': + resolution: {integrity: sha512-YLq27qMur3hPUponvV3Zr0oHxowox71j3+nc+/oCc1O+M0zFafhd6AoAoCiRrSYRW+asWhz3/UMPh0bYpimcMw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxfmt/binding-win32-arm64-msvc@0.37.0': + resolution: {integrity: sha512-0lYOsiYSODNh5RE9VqsydSUY7yMz8l+C4O2i3zpdZWEDNR6Tk949sMbakwUbtE5hViHnAq1cubr197DzKW+d6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxfmt/binding-win32-ia32-msvc@0.37.0': + resolution: {integrity: sha512-KHQF8DsMTE6nqQ5uBU0sx8sQsyBK/PzJdJV65+28lJGOJO59jCS5WlGcKnGtq14a2B3Xr6LoJGrSFi19xsBs/A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxfmt/binding-win32-x64-msvc@0.37.0': + resolution: {integrity: sha512-tDVVCHOPbIJ+sQE1z2DdWk82ewhmgcbXlYv4xUCnkY75vM7R3VkVgO2KqgEolMRXwI5RrsAbk+ZoP9/LKdzKVg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -1737,10 +1673,6 @@ packages: resolution: {integrity: sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==} engines: {node: '>=20.0.0'} - '@sindresorhus/base62@1.0.0': - resolution: {integrity: sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==} - engines: {node: '>=18'} - '@sindresorhus/merge-streams@2.3.0': resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} @@ -1752,12 +1684,6 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@stylistic/eslint-plugin@5.10.0': - resolution: {integrity: sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^9.0.0 || ^10.0.0 - '@testing-library/dom@10.4.1': resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} engines: {node: '>=18'} @@ -1812,9 +1738,6 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/esrecurse@4.3.1': - resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} - '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -1827,12 +1750,6 @@ packages: '@types/jsesc@2.5.1': resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - - '@types/mdast@4.0.4': - resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - '@types/micromatch@4.0.10': resolution: {integrity: sha512-5jOhFDElqr4DKTrTEbnW8DZ4Hz5LRUEmyrGpCMrD/NphYv3nUnaF08xmSLx1rGGnyEs/kFnhiw6dCgcDqMr5PQ==} @@ -1881,9 +1798,6 @@ packages: '@types/semver@7.7.1': resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} - '@types/unist@3.0.3': - resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@types/vscode@1.106.1': resolution: {integrity: sha512-R/HV8u2h8CAddSbX8cjpdd7B8/GnE4UjgjpuGuHcbp1xV6yh4OeqU4L1pKjlwujCrSFS0MOpwJAIs/NexMB1fQ==} @@ -1893,71 +1807,6 @@ packages: '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@typescript-eslint/eslint-plugin@8.57.0': - resolution: {integrity: sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.57.0 - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/parser@8.57.0': - resolution: {integrity: sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/project-service@8.57.0': - resolution: {integrity: sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/rule-tester@8.57.0': - resolution: {integrity: sha512-qs4OapXmAIj3so85/20lQG1WrBSSvDE/3b42Orl3lpZkaOlNXtbfKzL+9EPaY5wSEgdlhKEpympAMFHPG9i72Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - - '@typescript-eslint/scope-manager@8.57.0': - resolution: {integrity: sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/tsconfig-utils@8.57.0': - resolution: {integrity: sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/type-utils@8.57.0': - resolution: {integrity: sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/types@8.57.0': - resolution: {integrity: sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@8.57.0': - resolution: {integrity: sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/utils@8.57.0': - resolution: {integrity: sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/visitor-keys@8.57.0': - resolution: {integrity: sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typespec/ts-http-runtime@0.3.2': resolution: {integrity: sha512-IlqQ/Gv22xUC1r/WQm4StLkYQmaaTsXAhUVsNE0+xiyf0yRFiH5++q78U3bw6bLKDCTmh0uqKB9eG9+Bt75Dkg==} engines: {node: '>=20.0.0'} @@ -2022,19 +1871,6 @@ packages: '@vitest/browser': optional: true - '@vitest/eslint-plugin@1.6.10': - resolution: {integrity: sha512-/cOf+mTu4HBJIYHTETo8/OFCSZv3T2p+KfGnouzKfjK063cWLZp0TzvK7EU5B3eFG7ypUNtw6l+jK+SA+p1g8g==} - engines: {node: '>=18'} - peerDependencies: - eslint: '>=8.57.0' - typescript: '>=5.0.0' - vitest: '*' - peerDependenciesMeta: - typescript: - optional: true - vitest: - optional: true - '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} @@ -2190,11 +2026,6 @@ packages: resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn-walk@8.3.4: resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} engines: {node: '>=0.4.0'} @@ -2208,9 +2039,6 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} - ajv@6.14.0: - resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} - ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} @@ -2250,10 +2078,6 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - are-docs-informative@0.0.2: - resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} - engines: {node: '>=14'} - argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -2382,10 +2206,6 @@ packages: buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - builtin-modules@5.0.0: - resolution: {integrity: sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==} - engines: {node: '>=18.20'} - bumpp@10.3.2: resolution: {integrity: sha512-yUUkVx5zpTywLNX97MlrqtpanI7eMMwFwLntWR2EBVDw3/Pm3aRIzCoDEGHATLIiHK9PuJC7xWI4XNWqXItSPg==} engines: {node: '>=18'} @@ -2420,10 +2240,6 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - cac@7.0.0: - resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} - engines: {node: '>=20.19.0'} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -2446,9 +2262,6 @@ packages: caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} - ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chai@5.3.3: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} @@ -2465,9 +2278,6 @@ packages: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - change-case@5.4.4: - resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} - changelogen@0.5.7: resolution: {integrity: sha512-cTZXBcJMl3pudE40WENOakXkcVtrbBpbkmSkM20NdRiUqa4+VYRdXdEsgQ0BNQ6JBE2YymTNWtPKVF7UCTN5+g==} hasBin: true @@ -2477,9 +2287,6 @@ packages: engines: {node: '>=12.0.0'} hasBin: true - character-entities@2.0.2: - resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} - check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} @@ -2506,17 +2313,9 @@ packages: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} - ci-info@4.3.1: - resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} - engines: {node: '>=8'} - citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} - clean-regexp@1.0.0: - resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} - engines: {node: '>=4'} - cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} @@ -2561,10 +2360,6 @@ packages: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} - comment-parser@1.4.5: - resolution: {integrity: sha512-aRDkn3uyIlCFfk5NUA+VdwMmMsh8JGhc4hapfV4yxymHGQ3BVskMQfoXGpCo5IoBuQ9tS5iiVKhCpTcB4pW4qw==} - engines: {node: '>= 12.0.0'} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -2591,9 +2386,6 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - core-js-compat@3.47.0: - resolution: {integrity: sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==} - core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -2620,11 +2412,6 @@ packages: css.escape@1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} - cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - cssstyle@4.6.0: resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} engines: {node: '>=18'} @@ -2660,9 +2447,6 @@ packages: decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} - decode-named-character-reference@1.2.0: - resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} - decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -2679,9 +2463,6 @@ packages: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -2724,13 +2505,6 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - - diff-sequences@29.6.3: - resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - diff@5.2.0: resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} engines: {node: '>=0.3.1'} @@ -2997,272 +2771,59 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - escape-string-regexp@5.0.0: - resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} - engines: {node: '>=12'} - - eslint-compat-utils@0.5.1: - resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} - engines: {node: '>=12'} - peerDependencies: - eslint: '>=6.0.0' - - eslint-config-flat-gitignore@2.2.1: - resolution: {integrity: sha512-wA5EqN0era7/7Gt5Botlsfin/UNY0etJSEeBgbUlFLFrBi47rAN//+39fI7fpYcl8RENutlFtvp/zRa/M/pZNg==} - peerDependencies: - eslint: ^9.5.0 || ^10.0.0 + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true - eslint-flat-config-utils@3.0.2: - resolution: {integrity: sha512-mPvevWSDQFwgABvyCurwIu6ZdKxGI5NW22/BGDwA1T49NO6bXuxbV9VfJK/tkQoNyPogT6Yu1d57iM0jnZVWmg==} + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - eslint-json-compat-utils@0.2.1: - resolution: {integrity: sha512-YzEodbDyW8DX8bImKhAcCeu/L31Dd/70Bidx2Qex9OFUtgzXLqtfWL4Hr5fM/aCCB8QUZLuJur0S9k6UfgFkfg==} - engines: {node: '>=12'} - peerDependencies: - '@eslint/json': '*' - eslint: '*' - jsonc-eslint-parser: ^2.4.0 - peerDependenciesMeta: - '@eslint/json': - optional: true + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - eslint-merge-processors@2.0.0: - resolution: {integrity: sha512-sUuhSf3IrJdGooquEUB5TNpGNpBoQccbnaLHsb1XkBLUPPqCNivCpY05ZcpCOiV9uHwO2yxXEWVczVclzMxYlA==} - peerDependencies: - eslint: '*' + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} - eslint-plugin-antfu@3.2.2: - resolution: {integrity: sha512-Qzixht2Dmd/pMbb5EnKqw2V8TiWHbotPlsORO8a+IzCLFwE0RxK8a9k4DCTFPzBwyxJzH+0m2Mn8IUGeGQkyUw==} - peerDependencies: - eslint: '*' + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} - eslint-plugin-command@3.5.2: - resolution: {integrity: sha512-PA59QAkQDwvcCMEt5lYLJLI3zDGVKJeC4id/pcRY2XdRYhSGW7iyYT1VC1N3bmpuvu6Qb/9QptiS3GJMjeGTJg==} - peerDependencies: - '@typescript-eslint/rule-tester': '*' - '@typescript-eslint/typescript-estree': '*' - '@typescript-eslint/utils': '*' - eslint: '*' + execa@9.6.1: + resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} + engines: {node: ^18.19.0 || >=20.5.0} - eslint-plugin-depend@1.5.0: - resolution: {integrity: sha512-i3UeLYmclf1Icp35+6W7CR4Bp2PIpDgBuf/mpmXK5UeLkZlvYJ21VuQKKHHAIBKRTPivPGX/gZl5JGno1o9Y0A==} - peerDependencies: - eslint: '>=8.40.0' + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} - eslint-plugin-es-x@7.8.0: - resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - eslint: '>=8' + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} - eslint-plugin-import-lite@0.5.2: - resolution: {integrity: sha512-XvfdWOC5dSLEI9krIPRlNmKSI2ViIE9pVylzfV9fCq0ZpDaNeUk6o0wZv0OzN83QdadgXp1NsY0qjLINxwYCsw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: '>=9.0.0' + exsolve@1.0.8: + resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} - eslint-plugin-jsdoc@62.7.1: - resolution: {integrity: sha512-4Zvx99Q7d1uggYBUX/AIjvoyqXhluGbbKrRmG8SQTLprPFg6fa293tVJH1o1GQwNe3lUydd8ZHzn37OaSncgSQ==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - eslint-plugin-jsonc@3.1.1: - resolution: {integrity: sha512-7TSQO8ZyvOuXWb0sYke3KUSh0DJA4/QviKfuzD3/Cy3XDjtrIrTWQbjb7j/Yy2l/DgwuM+lCS2c/jqJifv5jhg==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - peerDependencies: - eslint: '>=9.38.0' + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} - eslint-plugin-n@17.24.0: - resolution: {integrity: sha512-/gC7/KAYmfNnPNOb3eu8vw+TdVnV0zhdQwexsw6FLXbhzroVj20vRn2qL8lDWDGnAQ2J8DhdfvXxX9EoxvERvw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: '>=8.23.0' + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - eslint-plugin-no-only-tests@3.3.0: - resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==} - engines: {node: '>=5.0.0'} + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - eslint-plugin-perfectionist@5.6.0: - resolution: {integrity: sha512-pxrLrfRp5wl1Vol1fAEa/G5yTXxefTPJjz07qC7a8iWFXcOZNuWBItMQ2OtTzfQIvMq6bMyYcrzc3Wz++na55Q==} - engines: {node: ^20.0.0 || >=22.0.0} - peerDependencies: - eslint: ^8.45.0 || ^9.0.0 || ^10.0.0 - - eslint-plugin-pnpm@1.6.0: - resolution: {integrity: sha512-dxmt9r3zvPaft6IugS4i0k16xag3fTbOvm/road5uV9Y8qUCQT0xzheSh3gMlYAlC6vXRpfArBDsTZ7H7JKCbg==} - peerDependencies: - eslint: ^9.0.0 || ^10.0.0 - - eslint-plugin-regexp@3.1.0: - resolution: {integrity: sha512-qGXIC3DIKZHcK1H9A9+Byz9gmndY6TTSRkSMTZpNXdyCw2ObSehRgccJv35n9AdUakEjQp5VFNLas6BMXizCZg==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - peerDependencies: - eslint: '>=9.38.0' - - eslint-plugin-toml@1.3.1: - resolution: {integrity: sha512-1l00fBP03HIt9IPV7ZxBi7x0y0NMdEZmakL1jBD6N/FoKBvfKxPw5S8XkmzBecOnFBTn5Z8sNJtL5vdf9cpRMQ==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - peerDependencies: - eslint: '>=9.38.0' - - eslint-plugin-unicorn@63.0.0: - resolution: {integrity: sha512-Iqecl9118uQEXYh7adylgEmGfkn5es3/mlQTLLkd4pXkIk9CTGrAbeUux+YljSa2ohXCBmQQ0+Ej1kZaFgcfkA==} - engines: {node: ^20.10.0 || >=21.0.0} - peerDependencies: - eslint: '>=9.38.0' - - eslint-plugin-unused-imports@4.4.1: - resolution: {integrity: sha512-oZGYUz1X3sRMGUB+0cZyK2VcvRX5lm/vB56PgNNcU+7ficUCKm66oZWKUubXWnOuPjQ8PvmXtCViXBMONPe7tQ==} - peerDependencies: - '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0 - eslint: ^10.0.0 || ^9.0.0 || ^8.0.0 - peerDependenciesMeta: - '@typescript-eslint/eslint-plugin': - optional: true - - eslint-plugin-vue@10.8.0: - resolution: {integrity: sha512-f1J/tcbnrpgC8suPN5AtdJ5MQjuXbSU9pGRSSYAuF3SHoiYCOdEX6O22pLaRyLHXvDcOe+O5ENgc1owQ587agA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@stylistic/eslint-plugin': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 - '@typescript-eslint/parser': ^7.0.0 || ^8.0.0 - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - vue-eslint-parser: ^10.0.0 - peerDependenciesMeta: - '@stylistic/eslint-plugin': - optional: true - '@typescript-eslint/parser': - optional: true - - eslint-plugin-yml@3.3.1: - resolution: {integrity: sha512-isntsZchaTqDMNNkD+CakrgA/pdUoJ45USWBKpuqfAW1MCuw731xX/vrXfoJFZU3tTFr24nCbDYmDfT2+g4QtQ==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} - peerDependencies: - eslint: '>=9.38.0' - - eslint-processor-vue-blocks@2.0.0: - resolution: {integrity: sha512-u4W0CJwGoWY3bjXAuFpc/b6eK3NQEI8MoeW7ritKj3G3z/WtHrKjkqf+wk8mPEy5rlMGS+k6AZYOw2XBoN/02Q==} - peerDependencies: - '@vue/compiler-sfc': ^3.3.0 - eslint: '>=9.0.0' - - eslint-scope@9.1.2: - resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - - eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-visitor-keys@4.2.1: - resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint-visitor-keys@5.0.1: - resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - - eslint@10.0.3: - resolution: {integrity: sha512-COV33RzXZkqhG9P2rZCFl9ZmJ7WL+gQSCRzE7RhkbclbQPtLAWReL7ysA0Sh4c8Im2U9ynybdR56PV0XcKvqaQ==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - hasBin: true - peerDependencies: - jiti: '*' - peerDependenciesMeta: - jiti: - optional: true - - espree@10.4.0: - resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - espree@11.2.0: - resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - esquery@1.7.0: - resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} - engines: {node: '>=0.10'} - - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - - estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - - estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - - execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} - - execa@9.6.1: - resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} - engines: {node: ^18.19.0 || >=20.5.0} - - expand-template@2.0.3: - resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} - engines: {node: '>=6'} - - expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} - engines: {node: '>=12.0.0'} - - exsolve@1.0.8: - resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - - fault@2.0.1: - resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} - - fd-slicer@1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fd-slicer@1.1.0: + resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -3277,18 +2838,10 @@ packages: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} - file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} - engines: {node: '>=16.0.0'} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - find-up-simple@1.0.1: - resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} - engines: {node: '>=18'} - find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -3297,17 +2850,10 @@ packages: resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} engines: {node: '>=18'} - flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} - engines: {node: '>=16'} - flat@5.0.2: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true - flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -3324,10 +2870,6 @@ packages: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} - format@0.2.2: - resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} - engines: {node: '>=0.4.x'} - fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} @@ -3416,17 +2958,10 @@ packages: github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} - github-slugger@2.0.0: - resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} - glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} - glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - glob@10.5.0: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me @@ -3447,25 +2982,10 @@ packages: engines: {node: '>=12'} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - globals@15.15.0: - resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} - engines: {node: '>=18'} - - globals@16.5.0: - resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} - engines: {node: '>=18'} - - globals@17.4.0: - resolution: {integrity: sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==} - engines: {node: '>=18'} - globby@14.1.0: resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} engines: {node: '>=18'} - globrex@0.1.2: - resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -3530,9 +3050,6 @@ packages: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - html-entities@2.6.0: - resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} - html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -3573,10 +3090,6 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - ignore@7.0.5: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} @@ -3610,18 +3123,10 @@ packages: resolution: {integrity: sha512-B6Lc2s6yApwnD2/pMzFh/d5AVjdsDXjgkeJ766FmFuJELIGHNycKRj+l3A39yZPM4CchqNCB4RITEAYB1KUM6A==} engines: {node: '>=20.19.0'} - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} - indent-string@5.0.0: - resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} - engines: {node: '>=12'} - index-to-position@1.2.0: resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} engines: {node: '>=18'} @@ -3660,10 +3165,6 @@ packages: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} - is-builtin-module@5.0.0: - resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==} - engines: {node: '>=18.20'} - is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} @@ -3858,10 +3359,6 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - jsdoc-type-pratt-parser@7.1.1: - resolution: {integrity: sha512-/2uqY7x6bsrpi3i9LVU6J89352C0rpMk0as8trXxCtvd4kPk1ke/Eyif6wqfSLvoNJqcDG9Vk4UsXgygzCt2xA==} - engines: {node: '>=20.0.0'} - jsdom@24.1.3: resolution: {integrity: sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ==} engines: {node: '>=18'} @@ -3885,27 +3382,14 @@ packages: engines: {node: '>=6'} hasBin: true - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true - jsonc-eslint-parser@3.1.0: - resolution: {integrity: sha512-75EA7EWZExL/j+MDKQrRbdzcRI2HOkRlmUw8fZJc1ioqFEOvBsq7Rt+A6yCxOt9w/TYNpkt52gC6nm/g5tFIng==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} @@ -3928,9 +3412,6 @@ packages: keytar@7.9.0: resolution: {integrity: sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==} - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -3943,10 +3424,6 @@ packages: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - lie@3.3.0: resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} @@ -3957,10 +3434,6 @@ packages: resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} engines: {node: '>=14'} - local-pkg@1.1.2: - resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} - engines: {node: '>=14'} - locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -3987,9 +3460,6 @@ packages: lodash.isstring@4.0.1: resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash.mergewith@4.6.2: resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} @@ -4010,9 +3480,6 @@ packages: resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} engines: {node: '>=18'} - longest-streak@3.1.0: - resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -4062,49 +3529,10 @@ packages: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true - markdown-table@3.0.4: - resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - mdast-util-find-and-replace@3.0.2: - resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} - - mdast-util-from-markdown@2.0.2: - resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} - - mdast-util-frontmatter@2.0.1: - resolution: {integrity: sha512-LRqI9+wdgC25P0URIJY9vwocIzCcksduHQ9OF2joxQoyTNVduwLAFUzjoopuRJbJAReaKrNQKAZKL3uCMugWJA==} - - mdast-util-gfm-autolink-literal@2.0.1: - resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} - - mdast-util-gfm-footnote@2.1.0: - resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} - - mdast-util-gfm-strikethrough@2.0.0: - resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} - - mdast-util-gfm-table@2.0.0: - resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} - - mdast-util-gfm-task-list-item@2.0.0: - resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} - - mdast-util-gfm@3.1.0: - resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} - - mdast-util-phrasing@4.1.0: - resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} - - mdast-util-to-markdown@2.1.2: - resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} - - mdast-util-to-string@4.0.0: - resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - mdn-data@2.12.2: resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} @@ -4118,93 +3546,6 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - micromark-core-commonmark@2.0.3: - resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} - - micromark-extension-frontmatter@2.0.0: - resolution: {integrity: sha512-C4AkuM3dA58cgZha7zVnuVxBhDsbttIMiytjgsM2XbHAB2faRVaHRle40558FBN+DJcrLNCoqG5mlrpdU4cRtg==} - - micromark-extension-gfm-autolink-literal@2.1.0: - resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} - - micromark-extension-gfm-footnote@2.1.0: - resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} - - micromark-extension-gfm-strikethrough@2.1.0: - resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} - - micromark-extension-gfm-table@2.1.1: - resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} - - micromark-extension-gfm-tagfilter@2.0.0: - resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} - - micromark-extension-gfm-task-list-item@2.1.0: - resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} - - micromark-extension-gfm@3.0.0: - resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} - - micromark-factory-destination@2.0.1: - resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} - - micromark-factory-label@2.0.1: - resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} - - micromark-factory-space@2.0.1: - resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} - - micromark-factory-title@2.0.1: - resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} - - micromark-factory-whitespace@2.0.1: - resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} - - micromark-util-character@2.1.1: - resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} - - micromark-util-chunked@2.0.1: - resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} - - micromark-util-classify-character@2.0.1: - resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} - - micromark-util-combine-extensions@2.0.1: - resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} - - micromark-util-decode-numeric-character-reference@2.0.2: - resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} - - micromark-util-decode-string@2.0.1: - resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} - - micromark-util-encode@2.0.1: - resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} - - micromark-util-html-tag-name@2.0.1: - resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} - - micromark-util-normalize-identifier@2.0.1: - resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} - - micromark-util-resolve-all@2.0.1: - resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} - - micromark-util-sanitize-uri@2.0.1: - resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - - micromark-util-subtokenize@2.1.0: - resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} - - micromark-util-symbol@2.0.1: - resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} - - micromark-util-types@2.0.2: - resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} - - micromark@4.0.2: - resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} - micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -4299,9 +3640,6 @@ packages: engines: {node: '>= 14.0.0'} hasBin: true - module-replacements@2.11.0: - resolution: {integrity: sha512-j5sNQm3VCpQQ7nTqGeOZtoJtV3uKERgCBm9QRhmGRiXiqkf7iRFOkfxdJRZWLkqYY8PNf4cDQF/WfXUYLENrRA==} - mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -4324,13 +3662,6 @@ packages: napi-build-utils@2.0.0: resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - - natural-orderby@5.0.0: - resolution: {integrity: sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==} - engines: {node: '>=18'} - node-abi@3.85.0: resolution: {integrity: sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==} engines: {node: '>=10'} @@ -4402,9 +3733,6 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-deep-merge@2.0.0: - resolution: {integrity: sha512-3DC3UMpeffLTHiuXSy/UG4NOIYTLlY9u3V82+djSCLYClWobZiS4ivYzpIUWrRY/nfsJ8cWsKyG3QfyLePmhvg==} - object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -4452,14 +3780,15 @@ packages: resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} engines: {node: '>=18'} - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - ora@8.2.0: resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} engines: {node: '>=18'} + oxfmt@0.37.0: + resolution: {integrity: sha512-Kd47gakZAU/i9KkXv3F0EDRoMvSso9O5966kflf9zYto0oZ0NN+Fh5vKKrLwp2Mkt0efYBk5LjCAS0BNC0y0eQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -4492,13 +3821,6 @@ packages: parse-cache-control@1.0.1: resolution: {integrity: sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==} - parse-gitignore@2.0.0: - resolution: {integrity: sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==} - engines: {node: '>=14'} - - parse-imports-exports@0.2.4: - resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==} - parse-json@8.3.0: resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} engines: {node: '>=18'} @@ -4510,9 +3832,6 @@ packages: parse-semver@1.1.1: resolution: {integrity: sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==} - parse-statements@1.0.11: - resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==} - parse5-htmlparser2-tree-adapter@7.1.0: resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} @@ -4621,17 +3940,10 @@ packages: resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} engines: {node: '>=14.19.0'} - pnpm-workspace-yaml@1.6.0: - resolution: {integrity: sha512-uUy4dK3E11sp7nK+hnT7uAWfkBMe00KaUw8OG3NuNlYQoTk4sc9pcdIy1+XIP85v9Tvr02mK3JPaNNrP0QyRaw==} - possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} - postcss-selector-parser@7.1.1: - resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} - engines: {node: '>=4'} - postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} @@ -4642,10 +3954,6 @@ packages: deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. hasBin: true - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - pretty-format@27.5.1: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} @@ -4685,9 +3993,6 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} - quansync@0.2.11: - resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} - quansync@1.0.0: resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} @@ -4758,26 +4063,10 @@ packages: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} - refa@0.12.1: - resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - regexp-ast-analysis@0.7.1: - resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - regexp-tree@0.1.27: - resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} - hasBin: true - regexp.prototype.flags@1.5.4: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} - regjsparser@0.13.0: - resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} - hasBin: true - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -4789,10 +4078,6 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - reserved-identifiers@1.2.0: - resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==} - engines: {node: '>=18'} - resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -4874,10 +4159,6 @@ packages: scheduler@0.20.2: resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==} - scslre@0.3.0: - resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} - engines: {node: ^14.0.0 || >=16.0.0} - scule@1.3.0: resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} @@ -4985,9 +4266,6 @@ packages: spdx-expression-parse@3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - spdx-expression-parse@4.0.0: - resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} - spdx-license-ids@3.0.22: resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} @@ -5050,10 +4328,6 @@ packages: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} - strip-indent@4.1.1: - resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} - engines: {node: '>=12'} - strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} @@ -5163,6 +4437,10 @@ packages: resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} engines: {node: ^18.0.0 || >=20.0.0} + tinypool@2.1.0: + resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} + engines: {node: ^20.0.0 || >=22.0.0} + tinyrainbow@2.0.0: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} @@ -5190,14 +4468,6 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - to-valid-identifier@1.0.0: - resolution: {integrity: sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==} - engines: {node: '>=20'} - - toml-eslint-parser@1.0.3: - resolution: {integrity: sha512-A5F0cM6+mDleacLIEUkmfpkBbnHJFV1d2rprHU2MXNk7mlxHq2zGojA+SRvQD1RoMo9gqjZPWEaKG4v1BQ48lw==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} @@ -5225,17 +4495,6 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - ts-api-utils@2.4.0: - resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} - engines: {node: '>=18.12'} - peerDependencies: - typescript: '>=4.8.4' - - ts-declaration-location@1.0.7: - resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==} - peerDependencies: - typescript: '>=4.0.0' - tsdown@0.20.3: resolution: {integrity: sha512-qWOUXSbe4jN8JZEgrkc/uhJpC8VN2QpNu3eZkBWwNuTEjc/Ik1kcc54ycfcQ5QPRHeu9OQXaLfCI3o7pEJgB2w==} engines: {node: '>=20.19.0'} @@ -5276,10 +4535,6 @@ packages: resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - type-fest@4.41.0: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} @@ -5326,18 +4581,6 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} - unist-util-is@6.0.1: - resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} - - unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - - unist-util-visit-parents@6.0.2: - resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} - - unist-util-visit@5.0.0: - resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} - universalify@0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} @@ -5362,9 +4605,6 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - url-join@4.0.1: resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} @@ -5516,12 +4756,6 @@ packages: vue-component-type-helpers@2.2.12: resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==} - vue-eslint-parser@10.4.0: - resolution: {integrity: sha512-Vxi9pJdbN3ZnVGLODVtZ7y4Y2kzAAE2Cm0CZ3ZDRvydVYxZ6VrnBhLikBsRS+dpwj4Jv4UCv21PTEwF5rQ9WXg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - vue@3.5.25: resolution: {integrity: sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==} peerDependencies: @@ -5548,10 +4782,12 @@ packages: whatwg-encoding@2.0.0: resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} engines: {node: '>=12'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation whatwg-mimetype@3.0.0: resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} @@ -5603,10 +4839,6 @@ packages: engines: {node: '>=8'} hasBin: true - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - workerpool@6.5.1: resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} @@ -5649,10 +4881,6 @@ packages: resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} engines: {node: '>=18'} - xml-name-validator@4.0.0: - resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} - engines: {node: '>=12'} - xml-name-validator@5.0.0: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} @@ -5678,10 +4906,6 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - yaml-eslint-parser@2.0.0: - resolution: {integrity: sha512-h0uDm97wvT2bokfwwTmY6kJ1hp6YDFL0nRHwNKz8s/VD1FH/vvZjAKoMUE+un0eaYBSG7/c6h+lJTP+31tjgTw==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - yaml@2.8.2: resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} engines: {node: '>= 14.6'} @@ -5725,9 +4949,6 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} - zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - snapshots: '@acemir/cssom@0.9.31': {} @@ -5755,66 +4976,11 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@antfu/eslint-config@7.7.0(@typescript-eslint/rule-tester@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(@typescript-eslint/typescript-estree@8.57.0(typescript@5.9.3))(@typescript-eslint/utils@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(@vue/compiler-sfc@3.5.25)(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)(vitest@4.1.0-beta.3)': - dependencies: - '@antfu/install-pkg': 1.1.0 - '@clack/prompts': 1.1.0 - '@e18e/eslint-plugin': 0.2.0(eslint@10.0.3(jiti@2.6.1)) - '@eslint-community/eslint-plugin-eslint-comments': 4.7.1(eslint@10.0.3(jiti@2.6.1)) - '@eslint/markdown': 7.5.1 - '@stylistic/eslint-plugin': 5.10.0(eslint@10.0.3(jiti@2.6.1)) - '@typescript-eslint/eslint-plugin': 8.57.0(@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - '@vitest/eslint-plugin': 1.6.10(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)(vitest@4.1.0-beta.3) - ansis: 4.2.0 - cac: 7.0.0 - eslint: 10.0.3(jiti@2.6.1) - eslint-config-flat-gitignore: 2.2.1(eslint@10.0.3(jiti@2.6.1)) - eslint-flat-config-utils: 3.0.2 - eslint-merge-processors: 2.0.0(eslint@10.0.3(jiti@2.6.1)) - eslint-plugin-antfu: 3.2.2(eslint@10.0.3(jiti@2.6.1)) - eslint-plugin-command: 3.5.2(@typescript-eslint/rule-tester@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(@typescript-eslint/typescript-estree@8.57.0(typescript@5.9.3))(@typescript-eslint/utils@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1)) - eslint-plugin-import-lite: 0.5.2(eslint@10.0.3(jiti@2.6.1)) - eslint-plugin-jsdoc: 62.7.1(eslint@10.0.3(jiti@2.6.1)) - eslint-plugin-jsonc: 3.1.1(eslint@10.0.3(jiti@2.6.1)) - eslint-plugin-n: 17.24.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - eslint-plugin-no-only-tests: 3.3.0 - eslint-plugin-perfectionist: 5.6.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - eslint-plugin-pnpm: 1.6.0(eslint@10.0.3(jiti@2.6.1)) - eslint-plugin-regexp: 3.1.0(eslint@10.0.3(jiti@2.6.1)) - eslint-plugin-toml: 1.3.1(eslint@10.0.3(jiti@2.6.1)) - eslint-plugin-unicorn: 63.0.0(eslint@10.0.3(jiti@2.6.1)) - eslint-plugin-unused-imports: 4.4.1(@typescript-eslint/eslint-plugin@8.57.0(@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1)) - eslint-plugin-vue: 10.8.0(@stylistic/eslint-plugin@5.10.0(eslint@10.0.3(jiti@2.6.1)))(@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@10.0.3(jiti@2.6.1))) - eslint-plugin-yml: 3.3.1(eslint@10.0.3(jiti@2.6.1)) - eslint-processor-vue-blocks: 2.0.0(@vue/compiler-sfc@3.5.25)(eslint@10.0.3(jiti@2.6.1)) - globals: 17.4.0 - local-pkg: 1.1.2 - parse-gitignore: 2.0.0 - toml-eslint-parser: 1.0.3 - vue-eslint-parser: 10.4.0(eslint@10.0.3(jiti@2.6.1)) - yaml-eslint-parser: 2.0.0 - transitivePeerDependencies: - - '@eslint/json' - - '@typescript-eslint/rule-tester' - - '@typescript-eslint/typescript-estree' - - '@typescript-eslint/utils' - - '@vue/compiler-sfc' - - oxlint - - supports-color - - typescript - - vitest - '@antfu/install-pkg@0.1.1': dependencies: execa: 5.1.1 find-up: 5.0.0 - '@antfu/install-pkg@1.1.0': - dependencies: - package-manager-detector: 1.6.0 - tinyexec: 1.0.2 - '@asamuzakjp/css-color@3.2.0': dependencies: '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) @@ -6193,15 +5359,6 @@ snapshots: dependencies: css-tree: 3.1.0 - '@clack/core@1.1.0': - dependencies: - sisteransi: 1.0.5 - - '@clack/prompts@1.1.0': - dependencies: - '@clack/core': 1.1.0 - sisteransi: 1.0.5 - '@csstools/color-helpers@5.1.0': {} '@csstools/color-helpers@6.0.2': {} @@ -6244,12 +5401,6 @@ snapshots: '@csstools/css-tokenizer@4.0.0': {} - '@e18e/eslint-plugin@0.2.0(eslint@10.0.3(jiti@2.6.1))': - dependencies: - eslint-plugin-depend: 1.5.0(eslint@10.0.3(jiti@2.6.1)) - optionalDependencies: - eslint: 10.0.3(jiti@2.6.1) - '@emnapi/core@1.8.1': dependencies: '@emnapi/wasi-threads': 1.1.0 @@ -6266,16 +5417,6 @@ snapshots: tslib: 2.8.1 optional: true - '@es-joy/jsdoccomment@0.84.0': - dependencies: - '@types/estree': 1.0.8 - '@typescript-eslint/types': 8.57.0 - comment-parser: 1.4.5 - esquery: 1.7.0 - jsdoc-type-pratt-parser: 7.1.1 - - '@es-joy/resolve.exports@1.2.0': {} - '@esbuild/aix-ppc64@0.25.12': optional: true @@ -6438,87 +5579,11 @@ snapshots: '@esbuild/win32-x64@0.27.0': optional: true - '@eslint-community/eslint-plugin-eslint-comments@4.7.1(eslint@10.0.3(jiti@2.6.1))': - dependencies: - escape-string-regexp: 4.0.0 - eslint: 10.0.3(jiti@2.6.1) - ignore: 7.0.5 - - '@eslint-community/eslint-utils@4.9.1(eslint@10.0.3(jiti@2.6.1))': - dependencies: - eslint: 10.0.3(jiti@2.6.1) - eslint-visitor-keys: 3.4.3 + '@exodus/bytes@1.11.0': {} - '@eslint-community/regexpp@4.12.2': {} + '@fastify/busboy@2.1.1': {} - '@eslint/compat@2.0.3(eslint@10.0.3(jiti@2.6.1))': - dependencies: - '@eslint/core': 1.1.1 - optionalDependencies: - eslint: 10.0.3(jiti@2.6.1) - - '@eslint/config-array@0.23.3': - dependencies: - '@eslint/object-schema': 3.0.3 - debug: 4.4.3(supports-color@8.1.1) - minimatch: 10.2.4 - transitivePeerDependencies: - - supports-color - - '@eslint/config-helpers@0.5.3': - dependencies: - '@eslint/core': 1.1.1 - - '@eslint/core@0.17.0': - dependencies: - '@types/json-schema': 7.0.15 - - '@eslint/core@1.1.1': - dependencies: - '@types/json-schema': 7.0.15 - - '@eslint/markdown@7.5.1': - dependencies: - '@eslint/core': 0.17.0 - '@eslint/plugin-kit': 0.4.1 - github-slugger: 2.0.0 - mdast-util-from-markdown: 2.0.2 - mdast-util-frontmatter: 2.0.1 - mdast-util-gfm: 3.1.0 - micromark-extension-frontmatter: 2.0.0 - micromark-extension-gfm: 3.0.0 - micromark-util-normalize-identifier: 2.0.1 - transitivePeerDependencies: - - supports-color - - '@eslint/object-schema@3.0.3': {} - - '@eslint/plugin-kit@0.4.1': - dependencies: - '@eslint/core': 0.17.0 - levn: 0.4.1 - - '@eslint/plugin-kit@0.6.1': - dependencies: - '@eslint/core': 1.1.1 - levn: 0.4.1 - - '@exodus/bytes@1.11.0': {} - - '@fastify/busboy@2.1.1': {} - - '@humanfs/core@0.19.1': {} - - '@humanfs/node@0.16.7': - dependencies: - '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.4.3 - - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/retry@0.4.3': {} - - '@isaacs/cliui@8.0.2': + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 @@ -6569,14 +5634,70 @@ snapshots: '@one-ini/wasm@0.1.1': {} - '@ota-meshi/ast-token-store@0.3.0': {} - '@oxc-project/types@0.112.0': {} + '@oxfmt/binding-android-arm-eabi@0.37.0': + optional: true + + '@oxfmt/binding-android-arm64@0.37.0': + optional: true + + '@oxfmt/binding-darwin-arm64@0.37.0': + optional: true + + '@oxfmt/binding-darwin-x64@0.37.0': + optional: true + + '@oxfmt/binding-freebsd-x64@0.37.0': + optional: true + + '@oxfmt/binding-linux-arm-gnueabihf@0.37.0': + optional: true + + '@oxfmt/binding-linux-arm-musleabihf@0.37.0': + optional: true + + '@oxfmt/binding-linux-arm64-gnu@0.37.0': + optional: true + + '@oxfmt/binding-linux-arm64-musl@0.37.0': + optional: true + + '@oxfmt/binding-linux-ppc64-gnu@0.37.0': + optional: true + + '@oxfmt/binding-linux-riscv64-gnu@0.37.0': + optional: true + + '@oxfmt/binding-linux-riscv64-musl@0.37.0': + optional: true + + '@oxfmt/binding-linux-s390x-gnu@0.37.0': + optional: true + + '@oxfmt/binding-linux-x64-gnu@0.37.0': + optional: true + + '@oxfmt/binding-linux-x64-musl@0.37.0': + optional: true + + '@oxfmt/binding-openharmony-arm64@0.37.0': + optional: true + + '@oxfmt/binding-win32-arm64-msvc@0.37.0': + optional: true + + '@oxfmt/binding-win32-ia32-msvc@0.37.0': + optional: true + + '@oxfmt/binding-win32-x64-msvc@0.37.0': + optional: true + '@pkgjs/parseargs@0.11.0': optional: true - '@pkgr/core@0.2.9': {} + '@pkgr/core@0.2.9': + optional: true '@playwright/test@1.57.0': dependencies: @@ -6780,24 +5901,12 @@ snapshots: '@secretlint/types@10.2.2': {} - '@sindresorhus/base62@1.0.0': {} - '@sindresorhus/merge-streams@2.3.0': {} '@sindresorhus/merge-streams@4.0.0': {} '@standard-schema/spec@1.1.0': {} - '@stylistic/eslint-plugin@5.10.0(eslint@10.0.3(jiti@2.6.1))': - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) - '@typescript-eslint/types': 8.57.0 - eslint: 10.0.3(jiti@2.6.1) - eslint-visitor-keys: 4.2.1 - espree: 10.4.0 - estraverse: 5.3.0 - picomatch: 4.0.3 - '@testing-library/dom@10.4.1': dependencies: '@babel/code-frame': 7.27.1 @@ -6883,11 +5992,10 @@ snapshots: '@types/debug@4.1.12': dependencies: '@types/ms': 2.1.0 + optional: true '@types/deep-eql@4.0.2': {} - '@types/esrecurse@4.3.1': {} - '@types/estree@1.0.8': {} '@types/form-data@0.0.33': @@ -6898,19 +6006,14 @@ snapshots: '@types/jsesc@2.5.1': {} - '@types/json-schema@7.0.15': {} - - '@types/mdast@4.0.4': - dependencies: - '@types/unist': 3.0.3 - '@types/micromatch@4.0.10': dependencies: '@types/braces': 3.0.5 '@types/mocha@10.0.10': {} - '@types/ms@2.1.0': {} + '@types/ms@2.1.0': + optional: true '@types/node@10.17.60': {} @@ -6949,8 +6052,6 @@ snapshots: '@types/semver@7.7.1': {} - '@types/unist@3.0.3': {} - '@types/vscode@1.106.1': {} '@types/which@3.0.4': {} @@ -6959,111 +6060,6 @@ snapshots: dependencies: '@types/node': 24.10.1 - '@typescript-eslint/eslint-plugin@8.57.0(@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.57.0 - '@typescript-eslint/type-utils': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.57.0 - eslint: 10.0.3(jiti@2.6.1) - ignore: 7.0.5 - natural-compare: 1.4.0 - ts-api-utils: 2.4.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.57.0 - '@typescript-eslint/types': 8.57.0 - '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.57.0 - debug: 4.4.3(supports-color@8.1.1) - eslint: 10.0.3(jiti@2.6.1) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/project-service@8.57.0(typescript@5.9.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@5.9.3) - '@typescript-eslint/types': 8.57.0 - debug: 4.4.3(supports-color@8.1.1) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/rule-tester@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/parser': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - ajv: 6.14.0 - eslint: 10.0.3(jiti@2.6.1) - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - semver: 7.7.4 - transitivePeerDependencies: - - supports-color - - typescript - - '@typescript-eslint/scope-manager@8.57.0': - dependencies: - '@typescript-eslint/types': 8.57.0 - '@typescript-eslint/visitor-keys': 8.57.0 - - '@typescript-eslint/tsconfig-utils@8.57.0(typescript@5.9.3)': - dependencies: - typescript: 5.9.3 - - '@typescript-eslint/type-utils@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/types': 8.57.0 - '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - debug: 4.4.3(supports-color@8.1.1) - eslint: 10.0.3(jiti@2.6.1) - ts-api-utils: 2.4.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@8.57.0': {} - - '@typescript-eslint/typescript-estree@8.57.0(typescript@5.9.3)': - dependencies: - '@typescript-eslint/project-service': 8.57.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.57.0(typescript@5.9.3) - '@typescript-eslint/types': 8.57.0 - '@typescript-eslint/visitor-keys': 8.57.0 - debug: 4.4.3(supports-color@8.1.1) - minimatch: 10.2.4 - semver: 7.7.4 - tinyglobby: 0.2.15 - ts-api-utils: 2.4.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.57.0 - '@typescript-eslint/types': 8.57.0 - '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3) - eslint: 10.0.3(jiti@2.6.1) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/visitor-keys@8.57.0': - dependencies: - '@typescript-eslint/types': 8.57.0 - eslint-visitor-keys: 5.0.1 - '@typespec/ts-http-runtime@0.3.2': dependencies: http-proxy-agent: 7.0.2 @@ -7193,17 +6189,6 @@ snapshots: optionalDependencies: '@vitest/browser': 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) - '@vitest/eslint-plugin@1.6.10(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3)(vitest@4.1.0-beta.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.57.0 - '@typescript-eslint/utils': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - eslint: 10.0.3(jiti@2.6.1) - optionalDependencies: - typescript: 5.9.3 - vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) - transitivePeerDependencies: - - supports-color - '@vitest/expect@3.2.4': dependencies: '@types/chai': 5.2.3 @@ -7443,10 +6428,6 @@ snapshots: abbrev@2.0.0: {} - acorn-jsx@5.3.2(acorn@8.16.0): - dependencies: - acorn: 8.16.0 - acorn-walk@8.3.4: dependencies: acorn: 8.16.0 @@ -7455,13 +6436,6 @@ snapshots: agent-base@7.1.4: {} - ajv@6.14.0: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 @@ -7494,8 +6468,6 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 - are-docs-informative@0.0.2: {} - argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -7623,8 +6595,6 @@ snapshots: ieee754: 1.2.1 optional: true - builtin-modules@5.0.0: {} - bumpp@10.3.2(magicast@0.3.5): dependencies: ansis: 4.2.0 @@ -7695,8 +6665,6 @@ snapshots: cac@6.7.14: {} - cac@7.0.0: {} - call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -7720,8 +6688,6 @@ snapshots: caseless@0.12.0: {} - ccount@2.0.1: {} - chai@5.3.3: dependencies: assertion-error: 2.0.1 @@ -7739,8 +6705,6 @@ snapshots: chalk@5.6.2: {} - change-case@5.4.4: {} - changelogen@0.5.7(magicast@0.3.5): dependencies: c12: 1.11.2(magicast@0.3.5) @@ -7774,8 +6738,6 @@ snapshots: transitivePeerDependencies: - magicast - character-entities@2.0.2: {} - check-error@2.1.1: {} cheerio-select@2.1.0: @@ -7822,16 +6784,10 @@ snapshots: chownr@2.0.0: {} - ci-info@4.3.1: {} - citty@0.1.6: dependencies: consola: 3.4.2 - clean-regexp@1.0.0: - dependencies: - escape-string-regexp: 1.0.5 - cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 @@ -7870,8 +6826,6 @@ snapshots: commander@12.1.0: {} - comment-parser@1.4.5: {} - concat-map@0.0.1: {} concat-stream@1.6.2: @@ -7896,10 +6850,6 @@ snapshots: convert-source-map@2.0.0: {} - core-js-compat@3.47.0: - dependencies: - browserslist: 4.28.0 - core-util-is@1.0.3: {} cross-env@7.0.3: @@ -7929,8 +6879,6 @@ snapshots: css.escape@1.5.1: {} - cssesc@3.0.0: {} - cssstyle@4.6.0: dependencies: '@asamuzakjp/css-color': 3.2.0 @@ -7967,10 +6915,6 @@ snapshots: decimal.js@10.6.0: {} - decode-named-character-reference@1.2.0: - dependencies: - character-entities: 2.0.2 - decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 @@ -8002,8 +6946,6 @@ snapshots: deep-extend@0.6.0: optional: true - deep-is@0.1.4: {} - deepmerge@4.3.1: {} default-browser-id@5.0.1: {} @@ -8038,12 +6980,6 @@ snapshots: detect-libc@2.1.2: optional: true - devlop@1.1.0: - dependencies: - dequal: 2.0.3 - - diff-sequences@29.6.3: {} - diff@5.2.0: {} dom-accessibility-api@0.5.16: {} @@ -8306,302 +7242,16 @@ snapshots: escalade@3.2.0: {} - escape-string-regexp@1.0.5: {} - escape-string-regexp@4.0.0: {} - escape-string-regexp@5.0.0: {} - - eslint-compat-utils@0.5.1(eslint@10.0.3(jiti@2.6.1)): - dependencies: - eslint: 10.0.3(jiti@2.6.1) - semver: 7.7.4 - - eslint-config-flat-gitignore@2.2.1(eslint@10.0.3(jiti@2.6.1)): - dependencies: - '@eslint/compat': 2.0.3(eslint@10.0.3(jiti@2.6.1)) - eslint: 10.0.3(jiti@2.6.1) - - eslint-flat-config-utils@3.0.2: - dependencies: - '@eslint/config-helpers': 0.5.3 - pathe: 2.0.3 - - eslint-json-compat-utils@0.2.1(eslint@10.0.3(jiti@2.6.1))(jsonc-eslint-parser@3.1.0): - dependencies: - eslint: 10.0.3(jiti@2.6.1) - esquery: 1.7.0 - jsonc-eslint-parser: 3.1.0 - - eslint-merge-processors@2.0.0(eslint@10.0.3(jiti@2.6.1)): - dependencies: - eslint: 10.0.3(jiti@2.6.1) - - eslint-plugin-antfu@3.2.2(eslint@10.0.3(jiti@2.6.1)): - dependencies: - eslint: 10.0.3(jiti@2.6.1) - - eslint-plugin-command@3.5.2(@typescript-eslint/rule-tester@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(@typescript-eslint/typescript-estree@8.57.0(typescript@5.9.3))(@typescript-eslint/utils@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1)): - dependencies: - '@es-joy/jsdoccomment': 0.84.0 - '@typescript-eslint/rule-tester': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.57.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - eslint: 10.0.3(jiti@2.6.1) - - eslint-plugin-depend@1.5.0(eslint@10.0.3(jiti@2.6.1)): - dependencies: - empathic: 2.0.0 - eslint: 10.0.3(jiti@2.6.1) - module-replacements: 2.11.0 - semver: 7.7.4 - - eslint-plugin-es-x@7.8.0(eslint@10.0.3(jiti@2.6.1)): - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) - '@eslint-community/regexpp': 4.12.2 - eslint: 10.0.3(jiti@2.6.1) - eslint-compat-utils: 0.5.1(eslint@10.0.3(jiti@2.6.1)) - - eslint-plugin-import-lite@0.5.2(eslint@10.0.3(jiti@2.6.1)): - dependencies: - eslint: 10.0.3(jiti@2.6.1) - - eslint-plugin-jsdoc@62.7.1(eslint@10.0.3(jiti@2.6.1)): - dependencies: - '@es-joy/jsdoccomment': 0.84.0 - '@es-joy/resolve.exports': 1.2.0 - are-docs-informative: 0.0.2 - comment-parser: 1.4.5 - debug: 4.4.3(supports-color@8.1.1) - escape-string-regexp: 4.0.0 - eslint: 10.0.3(jiti@2.6.1) - espree: 11.2.0 - esquery: 1.7.0 - html-entities: 2.6.0 - object-deep-merge: 2.0.0 - parse-imports-exports: 0.2.4 - semver: 7.7.4 - spdx-expression-parse: 4.0.0 - to-valid-identifier: 1.0.0 - transitivePeerDependencies: - - supports-color - - eslint-plugin-jsonc@3.1.1(eslint@10.0.3(jiti@2.6.1)): - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) - '@eslint/core': 1.1.1 - '@eslint/plugin-kit': 0.6.1 - '@ota-meshi/ast-token-store': 0.3.0 - diff-sequences: 29.6.3 - eslint: 10.0.3(jiti@2.6.1) - eslint-json-compat-utils: 0.2.1(eslint@10.0.3(jiti@2.6.1))(jsonc-eslint-parser@3.1.0) - jsonc-eslint-parser: 3.1.0 - natural-compare: 1.4.0 - synckit: 0.11.12 - transitivePeerDependencies: - - '@eslint/json' - - eslint-plugin-n@17.24.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3): - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) - enhanced-resolve: 5.18.3 - eslint: 10.0.3(jiti@2.6.1) - eslint-plugin-es-x: 7.8.0(eslint@10.0.3(jiti@2.6.1)) - get-tsconfig: 4.13.6 - globals: 15.15.0 - globrex: 0.1.2 - ignore: 5.3.2 - semver: 7.7.4 - ts-declaration-location: 1.0.7(typescript@5.9.3) - transitivePeerDependencies: - - typescript - - eslint-plugin-no-only-tests@3.3.0: {} - - eslint-plugin-perfectionist@5.6.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3): - dependencies: - '@typescript-eslint/utils': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - eslint: 10.0.3(jiti@2.6.1) - natural-orderby: 5.0.0 - transitivePeerDependencies: - - supports-color - - typescript - - eslint-plugin-pnpm@1.6.0(eslint@10.0.3(jiti@2.6.1)): - dependencies: - empathic: 2.0.0 - eslint: 10.0.3(jiti@2.6.1) - jsonc-eslint-parser: 3.1.0 - pathe: 2.0.3 - pnpm-workspace-yaml: 1.6.0 - tinyglobby: 0.2.15 - yaml: 2.8.2 - yaml-eslint-parser: 2.0.0 - - eslint-plugin-regexp@3.1.0(eslint@10.0.3(jiti@2.6.1)): - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) - '@eslint-community/regexpp': 4.12.2 - comment-parser: 1.4.5 - eslint: 10.0.3(jiti@2.6.1) - jsdoc-type-pratt-parser: 7.1.1 - refa: 0.12.1 - regexp-ast-analysis: 0.7.1 - scslre: 0.3.0 - - eslint-plugin-toml@1.3.1(eslint@10.0.3(jiti@2.6.1)): - dependencies: - '@eslint/core': 1.1.1 - '@eslint/plugin-kit': 0.6.1 - '@ota-meshi/ast-token-store': 0.3.0 - debug: 4.4.3(supports-color@8.1.1) - eslint: 10.0.3(jiti@2.6.1) - toml-eslint-parser: 1.0.3 - transitivePeerDependencies: - - supports-color - - eslint-plugin-unicorn@63.0.0(eslint@10.0.3(jiti@2.6.1)): - dependencies: - '@babel/helper-validator-identifier': 7.28.5 - '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) - change-case: 5.4.4 - ci-info: 4.3.1 - clean-regexp: 1.0.0 - core-js-compat: 3.47.0 - eslint: 10.0.3(jiti@2.6.1) - find-up-simple: 1.0.1 - globals: 16.5.0 - indent-string: 5.0.0 - is-builtin-module: 5.0.0 - jsesc: 3.1.0 - pluralize: 8.0.0 - regexp-tree: 0.1.27 - regjsparser: 0.13.0 - semver: 7.7.4 - strip-indent: 4.1.1 - - eslint-plugin-unused-imports@4.4.1(@typescript-eslint/eslint-plugin@8.57.0(@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1)): - dependencies: - eslint: 10.0.3(jiti@2.6.1) - optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.57.0(@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - - eslint-plugin-vue@10.8.0(@stylistic/eslint-plugin@5.10.0(eslint@10.0.3(jiti@2.6.1)))(@typescript-eslint/parser@8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.3(jiti@2.6.1))(vue-eslint-parser@10.4.0(eslint@10.0.3(jiti@2.6.1))): - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) - eslint: 10.0.3(jiti@2.6.1) - natural-compare: 1.4.0 - nth-check: 2.1.1 - postcss-selector-parser: 7.1.1 - semver: 7.7.4 - vue-eslint-parser: 10.4.0(eslint@10.0.3(jiti@2.6.1)) - xml-name-validator: 4.0.0 - optionalDependencies: - '@stylistic/eslint-plugin': 5.10.0(eslint@10.0.3(jiti@2.6.1)) - '@typescript-eslint/parser': 8.57.0(eslint@10.0.3(jiti@2.6.1))(typescript@5.9.3) - - eslint-plugin-yml@3.3.1(eslint@10.0.3(jiti@2.6.1)): - dependencies: - '@eslint/core': 1.1.1 - '@eslint/plugin-kit': 0.6.1 - '@ota-meshi/ast-token-store': 0.3.0 - debug: 4.4.3(supports-color@8.1.1) - diff-sequences: 29.6.3 - escape-string-regexp: 5.0.0 - eslint: 10.0.3(jiti@2.6.1) - natural-compare: 1.4.0 - yaml-eslint-parser: 2.0.0 - transitivePeerDependencies: - - supports-color - - eslint-processor-vue-blocks@2.0.0(@vue/compiler-sfc@3.5.25)(eslint@10.0.3(jiti@2.6.1)): - dependencies: - '@vue/compiler-sfc': 3.5.25 - eslint: 10.0.3(jiti@2.6.1) - - eslint-scope@9.1.2: - dependencies: - '@types/esrecurse': 4.3.1 - '@types/estree': 1.0.8 - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} - - eslint-visitor-keys@4.2.1: {} - - eslint-visitor-keys@5.0.1: {} - - eslint@10.0.3(jiti@2.6.1): - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3(jiti@2.6.1)) - '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.23.3 - '@eslint/config-helpers': 0.5.3 - '@eslint/core': 1.1.1 - '@eslint/plugin-kit': 0.6.1 - '@humanfs/node': 0.16.7 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 - ajv: 6.14.0 - cross-spawn: 7.0.6 - debug: 4.4.3(supports-color@8.1.1) - escape-string-regexp: 4.0.0 - eslint-scope: 9.1.2 - eslint-visitor-keys: 5.0.1 - espree: 11.2.0 - esquery: 1.7.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - minimatch: 10.2.4 - natural-compare: 1.4.0 - optionator: 0.9.4 - optionalDependencies: - jiti: 2.6.1 - transitivePeerDependencies: - - supports-color - - espree@10.4.0: - dependencies: - acorn: 8.16.0 - acorn-jsx: 5.3.2(acorn@8.16.0) - eslint-visitor-keys: 4.2.1 - - espree@11.2.0: - dependencies: - acorn: 8.16.0 - acorn-jsx: 5.3.2(acorn@8.16.0) - eslint-visitor-keys: 5.0.1 - esprima@4.0.1: {} - esquery@1.7.0: - dependencies: - estraverse: 5.3.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@5.3.0: {} - estree-walker@2.0.2: {} estree-walker@3.0.3: dependencies: '@types/estree': 1.0.8 - esutils@2.0.3: {} - execa@5.1.1: dependencies: cross-spawn: 7.0.6 @@ -8658,20 +7308,12 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} - - fast-levenshtein@2.0.6: {} - fast-uri@3.1.0: {} fastq@1.19.1: dependencies: reusify: 1.1.0 - fault@2.0.1: - dependencies: - format: 0.2.2 - fd-slicer@1.1.0: dependencies: pend: 1.2.0 @@ -8684,16 +7326,10 @@ snapshots: dependencies: is-unicode-supported: 2.1.0 - file-entry-cache@8.0.0: - dependencies: - flat-cache: 4.0.1 - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - find-up-simple@1.0.1: {} - find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -8705,15 +7341,8 @@ snapshots: path-exists: 5.0.0 unicorn-magic: 0.1.0 - flat-cache@4.0.1: - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 - flat@5.0.2: {} - flatted@3.3.3: {} - for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -8740,8 +7369,6 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 - format@0.2.2: {} - fs-constants@1.0.0: optional: true @@ -8832,16 +7459,10 @@ snapshots: github-from-package@0.0.0: optional: true - github-slugger@2.0.0: {} - glob-parent@5.1.2: dependencies: is-glob: 4.0.3 - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - glob@10.5.0: dependencies: foreground-child: 3.3.1 @@ -8877,12 +7498,6 @@ snapshots: minimatch: 5.1.6 once: 1.4.0 - globals@15.15.0: {} - - globals@16.5.0: {} - - globals@17.4.0: {} - globby@14.1.0: dependencies: '@sindresorhus/merge-streams': 2.3.0 @@ -8892,8 +7507,6 @@ snapshots: slash: 5.1.0 unicorn-magic: 0.3.0 - globrex@0.1.2: {} - gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -8962,8 +7575,6 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' - html-entities@2.6.0: {} - html-escaper@2.0.2: {} htmlparser2@10.0.0: @@ -9011,8 +7622,6 @@ snapshots: ieee754@1.2.1: optional: true - ignore@5.3.2: {} - ignore@7.0.5: {} imba@2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.9.1)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3): @@ -9044,12 +7653,8 @@ snapshots: import-without-cache@0.2.5: {} - imurmurhash@0.1.4: {} - indent-string@4.0.0: {} - indent-string@5.0.0: {} - index-to-position@1.2.0: {} inflight@1.0.6: @@ -9091,10 +7696,6 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 - is-builtin-module@5.0.0: - dependencies: - builtin-modules: 5.0.0 - is-callable@1.2.7: {} is-core-module@2.16.1: @@ -9262,8 +7863,6 @@ snapshots: dependencies: argparse: 2.0.1 - jsdoc-type-pratt-parser@7.1.1: {} - jsdom@24.1.3: dependencies: cssstyle: 4.6.0 @@ -9321,22 +7920,10 @@ snapshots: jsesc@3.1.0: {} - json-buffer@3.0.1: {} - - json-schema-traverse@0.4.1: {} - json-schema-traverse@1.0.0: {} - json-stable-stringify-without-jsonify@1.0.1: {} - json5@2.2.3: {} - jsonc-eslint-parser@3.1.0: - dependencies: - acorn: 8.16.0 - eslint-visitor-keys: 5.0.1 - semver: 7.7.4 - jsonc-parser@3.3.1: {} jsonfile@6.2.0: @@ -9382,21 +7969,12 @@ snapshots: prebuild-install: 7.1.3 optional: true - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - kleur@3.0.3: {} kleur@4.1.5: {} leven@3.1.0: {} - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - lie@3.3.0: dependencies: immediate: 3.0.6 @@ -9407,12 +7985,6 @@ snapshots: local-pkg@0.4.3: {} - local-pkg@1.1.2: - dependencies: - mlly: 1.8.0 - pkg-types: 2.3.0 - quansync: 0.2.11 - locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -9433,8 +8005,6 @@ snapshots: lodash.isstring@4.0.1: {} - lodash.merge@4.6.2: {} - lodash.mergewith@4.6.2: {} lodash.once@4.1.1: {} @@ -9453,8 +8023,6 @@ snapshots: chalk: 5.6.2 is-unicode-supported: 1.3.0 - longest-streak@3.1.0: {} - loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -9514,123 +8082,8 @@ snapshots: punycode.js: 2.3.1 uc.micro: 2.1.0 - markdown-table@3.0.4: {} - math-intrinsics@1.1.0: {} - mdast-util-find-and-replace@3.0.2: - dependencies: - '@types/mdast': 4.0.4 - escape-string-regexp: 5.0.0 - unist-util-is: 6.0.1 - unist-util-visit-parents: 6.0.2 - - mdast-util-from-markdown@2.0.2: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - decode-named-character-reference: 1.2.0 - devlop: 1.1.0 - mdast-util-to-string: 4.0.0 - micromark: 4.0.2 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-decode-string: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - unist-util-stringify-position: 4.0.0 - transitivePeerDependencies: - - supports-color - - mdast-util-frontmatter@2.0.1: - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - escape-string-regexp: 5.0.0 - mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.2 - micromark-extension-frontmatter: 2.0.0 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-autolink-literal@2.0.1: - dependencies: - '@types/mdast': 4.0.4 - ccount: 2.0.1 - devlop: 1.1.0 - mdast-util-find-and-replace: 3.0.2 - micromark-util-character: 2.1.1 - - mdast-util-gfm-footnote@2.1.0: - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.2 - micromark-util-normalize-identifier: 2.0.1 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-strikethrough@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-table@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - markdown-table: 3.0.4 - mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-task-list-item@2.0.0: - dependencies: - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.2 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm@3.1.0: - dependencies: - mdast-util-from-markdown: 2.0.2 - mdast-util-gfm-autolink-literal: 2.0.1 - mdast-util-gfm-footnote: 2.1.0 - mdast-util-gfm-strikethrough: 2.0.0 - mdast-util-gfm-table: 2.0.0 - mdast-util-gfm-task-list-item: 2.0.0 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-phrasing@4.1.0: - dependencies: - '@types/mdast': 4.0.4 - unist-util-is: 6.0.1 - - mdast-util-to-markdown@2.1.2: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - longest-streak: 3.1.0 - mdast-util-phrasing: 4.1.0 - mdast-util-to-string: 4.0.0 - micromark-util-classify-character: 2.0.1 - micromark-util-decode-string: 2.0.1 - unist-util-visit: 5.0.0 - zwitch: 2.0.4 - - mdast-util-to-string@4.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdn-data@2.12.2: {} mdurl@2.0.0: {} @@ -9639,204 +8092,6 @@ snapshots: merge2@1.4.1: {} - micromark-core-commonmark@2.0.3: - dependencies: - decode-named-character-reference: 1.2.0 - devlop: 1.1.0 - micromark-factory-destination: 2.0.1 - micromark-factory-label: 2.0.1 - micromark-factory-space: 2.0.1 - micromark-factory-title: 2.0.1 - micromark-factory-whitespace: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-chunked: 2.0.1 - micromark-util-classify-character: 2.0.1 - micromark-util-html-tag-name: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-subtokenize: 2.1.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-frontmatter@2.0.0: - dependencies: - fault: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-autolink-literal@2.1.0: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-sanitize-uri: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-footnote@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-core-commonmark: 2.0.3 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-sanitize-uri: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-strikethrough@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.1 - micromark-util-classify-character: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-table@2.1.1: - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm-tagfilter@2.0.0: - dependencies: - micromark-util-types: 2.0.2 - - micromark-extension-gfm-task-list-item@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-gfm@3.0.0: - dependencies: - micromark-extension-gfm-autolink-literal: 2.1.0 - micromark-extension-gfm-footnote: 2.1.0 - micromark-extension-gfm-strikethrough: 2.1.0 - micromark-extension-gfm-table: 2.1.1 - micromark-extension-gfm-tagfilter: 2.0.0 - micromark-extension-gfm-task-list-item: 2.1.0 - micromark-util-combine-extensions: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-destination@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-label@2.0.1: - dependencies: - devlop: 1.1.0 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-space@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-types: 2.0.2 - - micromark-factory-title@2.0.1: - dependencies: - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-whitespace@2.0.1: - dependencies: - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-character@2.1.1: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-chunked@2.0.1: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-classify-character@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-combine-extensions@2.0.1: - dependencies: - micromark-util-chunked: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-decode-numeric-character-reference@2.0.2: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-decode-string@2.0.1: - dependencies: - decode-named-character-reference: 1.2.0 - micromark-util-character: 2.1.1 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-symbol: 2.0.1 - - micromark-util-encode@2.0.1: {} - - micromark-util-html-tag-name@2.0.1: {} - - micromark-util-normalize-identifier@2.0.1: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-resolve-all@2.0.1: - dependencies: - micromark-util-types: 2.0.2 - - micromark-util-sanitize-uri@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-encode: 2.0.1 - micromark-util-symbol: 2.0.1 - - micromark-util-subtokenize@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-symbol@2.0.1: {} - - micromark-util-types@2.0.2: {} - - micromark@4.0.2: - dependencies: - '@types/debug': 4.1.12 - debug: 4.4.3(supports-color@8.1.1) - decode-named-character-reference: 1.2.0 - devlop: 1.1.0 - micromark-core-commonmark: 2.0.3 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-chunked: 2.0.1 - micromark-util-combine-extensions: 2.0.1 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-encode: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-sanitize-uri: 2.0.1 - micromark-util-subtokenize: 2.1.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - transitivePeerDependencies: - - supports-color - micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -9934,8 +8189,6 @@ snapshots: yargs-parser: 20.2.9 yargs-unparser: 2.0.0 - module-replacements@2.11.0: {} - mri@1.2.0: {} mrmime@2.0.1: {} @@ -9949,10 +8202,6 @@ snapshots: napi-build-utils@2.0.0: optional: true - natural-compare@1.4.0: {} - - natural-orderby@5.0.0: {} - node-abi@3.85.0: dependencies: semver: 7.7.4 @@ -10024,8 +8273,6 @@ snapshots: object-assign@4.1.1: {} - object-deep-merge@2.0.0: {} - object-inspect@1.13.4: {} object-is@1.1.6: @@ -10079,15 +8326,6 @@ snapshots: is-inside-container: 1.0.0 wsl-utils: 0.1.0 - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - ora@8.2.0: dependencies: chalk: 5.6.2 @@ -10100,6 +8338,30 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.1.2 + oxfmt@0.37.0: + dependencies: + tinypool: 2.1.0 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.37.0 + '@oxfmt/binding-android-arm64': 0.37.0 + '@oxfmt/binding-darwin-arm64': 0.37.0 + '@oxfmt/binding-darwin-x64': 0.37.0 + '@oxfmt/binding-freebsd-x64': 0.37.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.37.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.37.0 + '@oxfmt/binding-linux-arm64-gnu': 0.37.0 + '@oxfmt/binding-linux-arm64-musl': 0.37.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.37.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.37.0 + '@oxfmt/binding-linux-riscv64-musl': 0.37.0 + '@oxfmt/binding-linux-s390x-gnu': 0.37.0 + '@oxfmt/binding-linux-x64-gnu': 0.37.0 + '@oxfmt/binding-linux-x64-musl': 0.37.0 + '@oxfmt/binding-openharmony-arm64': 0.37.0 + '@oxfmt/binding-win32-arm64-msvc': 0.37.0 + '@oxfmt/binding-win32-ia32-msvc': 0.37.0 + '@oxfmt/binding-win32-x64-msvc': 0.37.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -10126,12 +8388,6 @@ snapshots: parse-cache-control@1.0.1: {} - parse-gitignore@2.0.0: {} - - parse-imports-exports@0.2.4: - dependencies: - parse-statements: 1.0.11 - parse-json@8.3.0: dependencies: '@babel/code-frame': 7.27.1 @@ -10144,8 +8400,6 @@ snapshots: dependencies: semver: 5.7.2 - parse-statements@1.0.11: {} - parse5-htmlparser2-tree-adapter@7.1.0: dependencies: domhandler: 5.0.3 @@ -10235,17 +8489,8 @@ snapshots: pngjs@7.0.0: {} - pnpm-workspace-yaml@1.6.0: - dependencies: - yaml: 2.8.2 - possible-typed-array-names@1.1.0: {} - postcss-selector-parser@7.1.1: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - postcss@8.5.6: dependencies: nanoid: 3.3.11 @@ -10268,8 +8513,6 @@ snapshots: tunnel-agent: 0.6.0 optional: true - prelude-ls@1.2.1: {} - pretty-format@27.5.1: dependencies: ansi-regex: 5.0.1 @@ -10311,8 +8554,6 @@ snapshots: dependencies: side-channel: 1.1.0 - quansync@0.2.11: {} - quansync@1.0.0: {} querystringify@2.2.0: {} @@ -10408,17 +8649,6 @@ snapshots: indent-string: 4.0.0 strip-indent: 3.0.0 - refa@0.12.1: - dependencies: - '@eslint-community/regexpp': 4.12.2 - - regexp-ast-analysis@0.7.1: - dependencies: - '@eslint-community/regexpp': 4.12.2 - refa: 0.12.1 - - regexp-tree@0.1.27: {} - regexp.prototype.flags@1.5.4: dependencies: call-bind: 1.0.8 @@ -10428,18 +8658,12 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 - regjsparser@0.13.0: - dependencies: - jsesc: 3.1.0 - require-directory@2.1.1: {} require-from-string@2.0.2: {} requires-port@1.0.0: {} - reserved-identifiers@1.2.0: {} - resolve-pkg-maps@1.0.0: {} resolve@1.22.11: @@ -10552,12 +8776,6 @@ snapshots: loose-envify: 1.4.0 object-assign: 4.1.1 - scslre@0.3.0: - dependencies: - '@eslint-community/regexpp': 4.12.2 - refa: 0.12.1 - regexp-ast-analysis: 0.7.1 - scule@1.3.0: {} secretlint@10.2.2: @@ -10682,11 +8900,6 @@ snapshots: spdx-exceptions: 2.5.0 spdx-license-ids: 3.0.22 - spdx-expression-parse@4.0.0: - dependencies: - spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.22 - spdx-license-ids@3.0.22: {} sprintf-js@1.0.3: {} @@ -10747,8 +8960,6 @@ snapshots: dependencies: min-indent: 1.0.1 - strip-indent@4.1.1: {} - strip-json-comments@2.0.1: optional: true @@ -10794,6 +9005,7 @@ snapshots: synckit@0.11.12: dependencies: '@pkgr/core': 0.2.9 + optional: true table@6.9.0: dependencies: @@ -10881,6 +9093,8 @@ snapshots: tinypool@1.1.1: {} + tinypool@2.1.0: {} + tinyrainbow@2.0.0: {} tinyrainbow@3.0.3: {} @@ -10899,15 +9113,6 @@ snapshots: dependencies: is-number: 7.0.0 - to-valid-identifier@1.0.0: - dependencies: - '@sindresorhus/base62': 1.0.0 - reserved-identifiers: 1.2.0 - - toml-eslint-parser@1.0.3: - dependencies: - eslint-visitor-keys: 5.0.1 - totalist@3.0.1: {} tough-cookie@4.1.4: @@ -10933,15 +9138,6 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@2.4.0(typescript@5.9.3): - dependencies: - typescript: 5.9.3 - - ts-declaration-location@1.0.7(typescript@5.9.3): - dependencies: - picomatch: 4.0.3 - typescript: 5.9.3 - tsdown@0.20.3(synckit@0.11.12)(typescript@5.9.3): dependencies: ansis: 4.2.0 @@ -10985,10 +9181,6 @@ snapshots: tunnel@0.0.6: {} - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - type-fest@4.41.0: {} typed-rest-client@1.8.11: @@ -11024,25 +9216,6 @@ snapshots: unicorn-magic@0.3.0: {} - unist-util-is@6.0.1: - dependencies: - '@types/unist': 3.0.3 - - unist-util-stringify-position@4.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-visit-parents@6.0.2: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - - unist-util-visit@5.0.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - unist-util-visit-parents: 6.0.2 - universalify@0.2.0: {} universalify@2.0.1: {} @@ -11059,10 +9232,6 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - url-join@4.0.1: {} url-parse@1.5.10: @@ -11310,18 +9479,6 @@ snapshots: vue-component-type-helpers@2.2.12: {} - vue-eslint-parser@10.4.0(eslint@10.0.3(jiti@2.6.1)): - dependencies: - debug: 4.4.3(supports-color@8.1.1) - eslint: 10.0.3(jiti@2.6.1) - eslint-scope: 9.1.2 - eslint-visitor-keys: 5.0.1 - espree: 11.2.0 - esquery: 1.7.0 - semver: 7.7.4 - transitivePeerDependencies: - - supports-color - vue@3.5.25(typescript@5.9.3): dependencies: '@vue/compiler-dom': 3.5.25 @@ -11412,8 +9569,6 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 - word-wrap@1.2.5: {} - workerpool@6.5.1: {} wrap-ansi@7.0.0: @@ -11438,8 +9593,6 @@ snapshots: dependencies: is-wsl: 3.1.0 - xml-name-validator@4.0.0: {} - xml-name-validator@5.0.0: {} xml2js@0.5.0: @@ -11457,11 +9610,6 @@ snapshots: yallist@4.0.0: {} - yaml-eslint-parser@2.0.0: - dependencies: - eslint-visitor-keys: 5.0.1 - yaml: 2.8.2 - yaml@2.8.2: {} yargs-parser@20.2.9: {} @@ -11509,5 +9657,3 @@ snapshots: yocto-queue@1.2.2: {} yoctocolors@2.1.2: {} - - zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index fa7a5c8..53b76af 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -9,7 +9,6 @@ packages: - samples/monorepo-vitest-workspace/packages/* catalog: - '@antfu/eslint-config': ^7.7.0 '@playwright/test': ^1.42.1 '@types/chai': ^5.2.2 '@types/micromatch': ^4.0.6 @@ -29,7 +28,6 @@ catalog: bumpp: ^10.1.1 chai: ^5.1.0 changelogithub: ^13.15.0 - eslint: ^10.0.3 execa: ^8.0.1 find-up: ^7.0.0 get-port: ^6.1.2 diff --git a/samples/ast-collector/package.json b/samples/ast-collector/package.json index 7fc014e..e453ea4 100644 --- a/samples/ast-collector/package.json +++ b/samples/ast-collector/package.json @@ -1,10 +1,10 @@ { "name": "ast-collector", - "private": true, "version": "1.0.0", + "private": true, "description": "", - "author": "", "license": "ISC", + "author": "", "scripts": { "test": "vitest run" }, diff --git a/samples/ast-collector/src/add.ts b/samples/ast-collector/src/add.ts index a0e1915..8742d7c 100644 --- a/samples/ast-collector/src/add.ts +++ b/samples/ast-collector/src/add.ts @@ -1,10 +1,9 @@ - export function add(a: number, b: number) { return a + b } export function sum(from: number, to: number) { - return (from + to) * (to - from + 1) / 2 + return ((from + to) * (to - from + 1)) / 2 } export function addError(from: number, to: number) { @@ -13,5 +12,5 @@ export function addError(from: number, to: number) { } function doSomething() { - throw new Error('Something went wrong'); + throw new Error('Something went wrong') } diff --git a/samples/ast-collector/test/each.test.ts b/samples/ast-collector/test/each.test.ts index d51037e..75e7283 100644 --- a/samples/ast-collector/test/each.test.ts +++ b/samples/ast-collector/test/each.test.ts @@ -1,15 +1,16 @@ -import { describe, expect, it, test, } from 'vitest' +import { describe, expect, it, test } from 'vitest' describe('testing', (a) => { it.each([ - [1, 1], [2, 2] + [1, 1], + [2, 2], ])(`all pass: %i => %i`, (a, b) => { expect(a).toBe(b) }) test.each` - a | b | expected - ${1} | ${1} | ${2} - ${'a'} | ${'b'} | ${'ab'} + a | b | expected + ${1} | ${1} | ${2} + ${'a'} | ${'b'} | ${'ab'} `('table1: returns $expected when $a is added $b', ({ a, b, expected }) => { expect(a + b).toBe(expected) }) diff --git a/samples/basic-v4/.skip/vitest.config.ts b/samples/basic-v4/.skip/vitest.config.ts index 9148068..a3fcad2 100644 --- a/samples/basic-v4/.skip/vitest.config.ts +++ b/samples/basic-v4/.skip/vitest.config.ts @@ -1 +1 @@ -throw new Error('do not import') \ No newline at end of file +throw new Error('do not import') diff --git a/samples/basic-v4/package.json b/samples/basic-v4/package.json index 32bdc04..e915a4d 100644 --- a/samples/basic-v4/package.json +++ b/samples/basic-v4/package.json @@ -3,8 +3,8 @@ "version": "1.0.0", "private": true, "description": "", - "author": "", "license": "ISC", + "author": "", "scripts": { "test": "vitest run" }, diff --git a/samples/basic-v4/src/add.ts b/samples/basic-v4/src/add.ts index a0e1915..8742d7c 100644 --- a/samples/basic-v4/src/add.ts +++ b/samples/basic-v4/src/add.ts @@ -1,10 +1,9 @@ - export function add(a: number, b: number) { return a + b } export function sum(from: number, to: number) { - return (from + to) * (to - from + 1) / 2 + return ((from + to) * (to - from + 1)) / 2 } export function addError(from: number, to: number) { @@ -13,5 +12,5 @@ export function addError(from: number, to: number) { } function doSomething() { - throw new Error('Something went wrong'); + throw new Error('Something went wrong') } diff --git a/samples/basic-v4/test/add.test.ts b/samples/basic-v4/test/add.test.ts index 2517c27..fca361a 100644 --- a/samples/basic-v4/test/add.test.ts +++ b/samples/basic-v4/test/add.test.ts @@ -16,21 +16,20 @@ describe('addition', () => { it.todo('todo') it('async task', async () => { - await new Promise(resolve => setTimeout(resolve, 100)) + await new Promise((resolve) => setTimeout(resolve, 100)) }) it('async task 0.5s', async () => { - await new Promise(resolve => setTimeout(resolve, 500)) + await new Promise((resolve) => setTimeout(resolve, 500)) }) it('async task 1s', async () => { - await new Promise(resolve => setTimeout(resolve, 1000)) + await new Promise((resolve) => setTimeout(resolve, 1000)) }) it('long task', () => { let sum = 0 - for (let i = 0; i < 2e8; i++) - sum += i + for (let i = 0; i < 2e8; i++) sum += i expect(sum).toBeGreaterThan(1) }) @@ -46,7 +45,7 @@ describe('testing', () => { expect(5 * 5).toBe(25) }) - it("mul fail", () => { + it('mul fail', () => { expect(5 * 5).toBe(25) }) }) diff --git a/samples/basic-v4/test/bug.test.ts b/samples/basic-v4/test/bug.test.ts index aba9a46..142621b 100644 --- a/samples/basic-v4/test/bug.test.ts +++ b/samples/basic-v4/test/bug.test.ts @@ -1,4 +1,4 @@ -import { expect, test } from 'vitest'; +import { expect, test } from 'vitest' test('fail', () => { - expect(1).toEqual(2); -}) \ No newline at end of file + expect(1).toEqual(2) +}) diff --git a/samples/basic-v4/test/console.test.ts b/samples/basic-v4/test/console.test.ts index 6cfae08..44d62fc 100644 --- a/samples/basic-v4/test/console.test.ts +++ b/samples/basic-v4/test/console.test.ts @@ -1,18 +1,10 @@ import { describe, it } from 'vitest' -const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) describe('console', () => { it('basic', () => { - const variables = [ - 'string', - { hello: 'world' }, - 1235, - /regex/g, - true, - false, - null, - ] + const variables = ['string', { hello: 'world' }, 1235, /regex/g, true, false, null] console.log(variables) }) diff --git a/samples/basic-v4/test/deep/deeper/deep.test.ts b/samples/basic-v4/test/deep/deeper/deep.test.ts index 64456cd..1cc98f7 100644 --- a/samples/basic-v4/test/deep/deeper/deep.test.ts +++ b/samples/basic-v4/test/deep/deeper/deep.test.ts @@ -2,4 +2,4 @@ import { expect, it } from 'vitest' it('test', () => { expect(1).toBe(1) -}) \ No newline at end of file +}) diff --git a/samples/basic-v4/test/duplicated.test.ts b/samples/basic-v4/test/duplicated.test.ts index 8f839ba..8065b4c 100644 --- a/samples/basic-v4/test/duplicated.test.ts +++ b/samples/basic-v4/test/duplicated.test.ts @@ -1,8 +1,8 @@ -import { describe, test } from "vitest"; +import { describe, test } from 'vitest' -describe("testing", () => { - test("number 1", () => { }) -}); -describe("testing", () => { - test("number 2", () => { }) -}); +describe('testing', () => { + test('number 1', () => {}) +}) +describe('testing', () => { + test('number 2', () => {}) +}) diff --git a/samples/basic-v4/test/each.test.ts b/samples/basic-v4/test/each.test.ts index f7cff91..7842a28 100644 --- a/samples/basic-v4/test/each.test.ts +++ b/samples/basic-v4/test/each.test.ts @@ -1,66 +1,78 @@ -import { describe, expect, it, test, } from 'vitest' +import { describe, expect, it, test } from 'vitest' describe('testing', (a) => { it.each([ - [1, 1], [2, 2], [3, 3] + [1, 1], + [2, 2], + [3, 3], ])(`all pass: %i => %i`, (a, b) => { expect(a).toBe(b) }) it.each([ - [1, 1], [2, 1], [3, 1] + [1, 1], + [2, 1], + [3, 1], ])(`first pass: %i => %i`, (a, b) => { expect(a).toBe(b) }) it.each([ - [1, 1], [2, 2], [3, 1] + [1, 1], + [2, 2], + [3, 1], ])(`last pass: %i => %i`, (a, b) => { expect(a).toBe(b) }) it.each([ - [1, 1], [2, 2], [3, 1] + [1, 1], + [2, 2], + [3, 1], ])(`first fail: %i => %i`, (a, b) => { expect(a).toBe(b) }) it.each([ - [1, 1], [2, 2], [3, 1] + [1, 1], + [2, 2], + [3, 1], ])(`last fail: %i => %i`, (a, b) => { expect(a).toBe(b) }) it.each([ - [1, 0], [2, 0], [3, 0] + [1, 0], + [2, 0], + [3, 0], ])(`all fail: %i => %i`, (a, b) => { expect(a).toBe(b) }) - it.each([ - 1, 2, 3 - ])('run %i', (a) => { + it.each([1, 2, 3])('run %i', (a) => { expect(a).toBe(a) }) it.each([ - [1, 1], [2, 4], [3, 9] - ])('run mul %i', (a,b) => { + [1, 1], + [2, 4], + [3, 9], + ])('run mul %i', (a, b) => { expect(a * a).toBe(b) }) test.each([ - ["test1", 1], - ["test2", 2], - ["test3", 3], + ['test1', 1], + ['test2', 2], + ['test3', 3], ])(`%s => %i`, (a, b) => { expect(a.at(-1)).toBe(`${b}`) }) test.each` - a | b | expected - ${1} | ${1} | ${2} - ${'a'} | ${'b'} | ${'ab'} - ${[]} | ${'b'} | ${'b'} - ${{}} | ${'b'} | ${'[object Object]b'} - ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} + a | b | expected + ${1} | ${1} | ${2} + ${'a'} | ${'b'} | ${'ab'} + ${[]} | ${'b'} | ${'b'} + ${{}} | ${'b'} | ${'[object Object]b'} + ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} `('table1: returns $expected when $a is added $b', ({ a, b, expected }) => { expect(a + b).toBe(expected) }) test.each` - a | b | expected - ${{v: 1}} | ${{v: 1}} | ${2} + a | b | expected + ${{ v: 1 }} | ${{ v: 1 }} | ${2} `('table2: returns $expected when $a.v is added $b.v', ({ a, b, expected }) => { expect(a.v + b.v).toBe(expected) }) @@ -74,10 +86,10 @@ describe('testing', (a) => { // 'Test result not fourd' error occurs as both .each patterns are matched // TODO: Fix this -describe("over matched test patterns", () => { +describe('over matched test patterns', () => { test.each(['1', '2'])('run %s', (a) => { expect(a).toBe(String(a)) - }) + }) test.each(['1', '2'])('run for %s', (a) => { expect(a).toBe(String(a)) }) diff --git a/samples/basic-v4/test/env.test.ts b/samples/basic-v4/test/env.test.ts index 64f168f..d69c737 100644 --- a/samples/basic-v4/test/env.test.ts +++ b/samples/basic-v4/test/env.test.ts @@ -1,16 +1,15 @@ -import { expect, test } from "vitest"; +import { expect, test } from 'vitest' test('process.env', () => { - expect(process.env.TEST).toBe('true'); - expect(process.env.VITEST).toBe('true'); - expect(process.env.NODE_ENV).toBe('test'); - expect(process.env.VITEST_VSCODE).toBe('true'); + expect(process.env.TEST).toBe('true') + expect(process.env.VITEST).toBe('true') + expect(process.env.NODE_ENV).toBe('test') + expect(process.env.VITEST_VSCODE).toBe('true') - if(process.env.TEST_CUSTOM_ENV_2 === undefined) { - expect(process.env.TEST_CUSTOM_ENV).toBe('hello'); + if (process.env.TEST_CUSTOM_ENV_2 === undefined) { + expect(process.env.TEST_CUSTOM_ENV).toBe('hello') + } else { + expect(process.env.TEST_CUSTOM_ENV).toBe('hello new') + expect(process.env.TEST_CUSTOM_ENV_2).toBe('world') } - else { - expect(process.env.TEST_CUSTOM_ENV).toBe('hello new'); - expect(process.env.TEST_CUSTOM_ENV_2).toBe('world'); - } -}); +}) diff --git a/samples/basic-v4/test/snapshot.test.ts b/samples/basic-v4/test/snapshot.test.ts index 2944c55..c27cfbf 100644 --- a/samples/basic-v4/test/snapshot.test.ts +++ b/samples/basic-v4/test/snapshot.test.ts @@ -5,7 +5,7 @@ describe('snapshots', () => { expect('bc').toMatchSnapshot() }) it('async', async () => { - await new Promise(resolve => setTimeout(resolve, 200)) + await new Promise((resolve) => setTimeout(resolve, 200)) expect('bc').toMatchSnapshot() }) }) diff --git a/samples/basic-v4/test/throw.test.ts b/samples/basic-v4/test/throw.test.ts index 73f8e54..33c26fa 100644 --- a/samples/basic-v4/test/throw.test.ts +++ b/samples/basic-v4/test/throw.test.ts @@ -1,9 +1,9 @@ -import { describe, expect, it } from 'vitest'; -import { addError } from '../src/add'; +import { describe, expect, it } from 'vitest' +import { addError } from '../src/add' describe('throw error', () => { it('passes expecting an error to be thrown', () => { - expect(()=>addError(1, 1)).toThrow() + expect(() => addError(1, 1)).toThrow() }) it('fails with error thrown', () => { diff --git a/samples/basic-v4/test/using.test.ts b/samples/basic-v4/test/using.test.ts index d7e13a3..f96949c 100644 --- a/samples/basic-v4/test/using.test.ts +++ b/samples/basic-v4/test/using.test.ts @@ -1,7 +1,7 @@ -import { describe, expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest' -(Symbol as any).dispose ??= Symbol('Symbol.dispose'); -(Symbol as any).asyncDispose ??= Symbol('Symbol.asyncDispose') +;(Symbol as any).dispose ??= Symbol('Symbol.dispose') +;(Symbol as any).asyncDispose ??= Symbol('Symbol.asyncDispose') describe('using keyword', () => { it('dispose', () => { @@ -37,7 +37,7 @@ class SomeAsyncDisposableResource implements AsyncDisposable { public isDisposed = false async [Symbol.asyncDispose](): Promise { - await new Promise(resolve => setTimeout(resolve, 0)) + await new Promise((resolve) => setTimeout(resolve, 0)) this.isDisposed = true } } diff --git a/samples/basic-v4/vite.config.ts b/samples/basic-v4/vite.config.ts index c55daa9..ed5c4d4 100644 --- a/samples/basic-v4/vite.config.ts +++ b/samples/basic-v4/vite.config.ts @@ -1,3 +1,3 @@ export default function () { throw new Error('This file should not be executed') -} \ No newline at end of file +} diff --git a/samples/basic-v4/vitest.config.ts b/samples/basic-v4/vitest.config.ts index 40c3fe9..2bae845 100644 --- a/samples/basic-v4/vitest.config.ts +++ b/samples/basic-v4/vitest.config.ts @@ -12,7 +12,7 @@ export default defineConfig({ include: ['src/should_included_test.ts', 'test/**/*.test.ts'], exclude: ['test/ignored.test.ts'], coverage: { - provider: 'istanbul' - } + provider: 'istanbul', + }, }, }) diff --git a/samples/basic/.skip/vitest.config.ts b/samples/basic/.skip/vitest.config.ts index 9148068..a3fcad2 100644 --- a/samples/basic/.skip/vitest.config.ts +++ b/samples/basic/.skip/vitest.config.ts @@ -1 +1 @@ -throw new Error('do not import') \ No newline at end of file +throw new Error('do not import') diff --git a/samples/basic/package.json b/samples/basic/package.json index 1972fdf..157695a 100644 --- a/samples/basic/package.json +++ b/samples/basic/package.json @@ -3,8 +3,8 @@ "version": "1.0.0", "private": true, "description": "", - "author": "", "license": "ISC", + "author": "", "scripts": { "test": "vitest run" }, diff --git a/samples/basic/src/add.ts b/samples/basic/src/add.ts index a0e1915..8742d7c 100644 --- a/samples/basic/src/add.ts +++ b/samples/basic/src/add.ts @@ -1,10 +1,9 @@ - export function add(a: number, b: number) { return a + b } export function sum(from: number, to: number) { - return (from + to) * (to - from + 1) / 2 + return ((from + to) * (to - from + 1)) / 2 } export function addError(from: number, to: number) { @@ -13,5 +12,5 @@ export function addError(from: number, to: number) { } function doSomething() { - throw new Error('Something went wrong'); + throw new Error('Something went wrong') } diff --git a/samples/basic/test/add.test.ts b/samples/basic/test/add.test.ts index 2517c27..fca361a 100644 --- a/samples/basic/test/add.test.ts +++ b/samples/basic/test/add.test.ts @@ -16,21 +16,20 @@ describe('addition', () => { it.todo('todo') it('async task', async () => { - await new Promise(resolve => setTimeout(resolve, 100)) + await new Promise((resolve) => setTimeout(resolve, 100)) }) it('async task 0.5s', async () => { - await new Promise(resolve => setTimeout(resolve, 500)) + await new Promise((resolve) => setTimeout(resolve, 500)) }) it('async task 1s', async () => { - await new Promise(resolve => setTimeout(resolve, 1000)) + await new Promise((resolve) => setTimeout(resolve, 1000)) }) it('long task', () => { let sum = 0 - for (let i = 0; i < 2e8; i++) - sum += i + for (let i = 0; i < 2e8; i++) sum += i expect(sum).toBeGreaterThan(1) }) @@ -46,7 +45,7 @@ describe('testing', () => { expect(5 * 5).toBe(25) }) - it("mul fail", () => { + it('mul fail', () => { expect(5 * 5).toBe(25) }) }) diff --git a/samples/basic/test/bug.test.ts b/samples/basic/test/bug.test.ts index aba9a46..142621b 100644 --- a/samples/basic/test/bug.test.ts +++ b/samples/basic/test/bug.test.ts @@ -1,4 +1,4 @@ -import { expect, test } from 'vitest'; +import { expect, test } from 'vitest' test('fail', () => { - expect(1).toEqual(2); -}) \ No newline at end of file + expect(1).toEqual(2) +}) diff --git a/samples/basic/test/console.test.ts b/samples/basic/test/console.test.ts index 4efe8f6..ee68914 100644 --- a/samples/basic/test/console.test.ts +++ b/samples/basic/test/console.test.ts @@ -1,18 +1,10 @@ import { describe, it } from 'vitest' -const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) describe('console', () => { it('basic', () => { - console.log([ - 'string', - { hello: 'world' }, - 1234, - /regex/g, - true, - false, - null, - ]) + console.log(['string', { hello: 'world' }, 1234, /regex/g, true, false, null]) }) it('async', async () => { diff --git a/samples/basic/test/deep/deeper/deep.test.ts b/samples/basic/test/deep/deeper/deep.test.ts index 64456cd..1cc98f7 100644 --- a/samples/basic/test/deep/deeper/deep.test.ts +++ b/samples/basic/test/deep/deeper/deep.test.ts @@ -2,4 +2,4 @@ import { expect, it } from 'vitest' it('test', () => { expect(1).toBe(1) -}) \ No newline at end of file +}) diff --git a/samples/basic/test/duplicated.test.ts b/samples/basic/test/duplicated.test.ts index 8f839ba..8065b4c 100644 --- a/samples/basic/test/duplicated.test.ts +++ b/samples/basic/test/duplicated.test.ts @@ -1,8 +1,8 @@ -import { describe, test } from "vitest"; +import { describe, test } from 'vitest' -describe("testing", () => { - test("number 1", () => { }) -}); -describe("testing", () => { - test("number 2", () => { }) -}); +describe('testing', () => { + test('number 1', () => {}) +}) +describe('testing', () => { + test('number 2', () => {}) +}) diff --git a/samples/basic/test/each.test.ts b/samples/basic/test/each.test.ts index f7cff91..7842a28 100644 --- a/samples/basic/test/each.test.ts +++ b/samples/basic/test/each.test.ts @@ -1,66 +1,78 @@ -import { describe, expect, it, test, } from 'vitest' +import { describe, expect, it, test } from 'vitest' describe('testing', (a) => { it.each([ - [1, 1], [2, 2], [3, 3] + [1, 1], + [2, 2], + [3, 3], ])(`all pass: %i => %i`, (a, b) => { expect(a).toBe(b) }) it.each([ - [1, 1], [2, 1], [3, 1] + [1, 1], + [2, 1], + [3, 1], ])(`first pass: %i => %i`, (a, b) => { expect(a).toBe(b) }) it.each([ - [1, 1], [2, 2], [3, 1] + [1, 1], + [2, 2], + [3, 1], ])(`last pass: %i => %i`, (a, b) => { expect(a).toBe(b) }) it.each([ - [1, 1], [2, 2], [3, 1] + [1, 1], + [2, 2], + [3, 1], ])(`first fail: %i => %i`, (a, b) => { expect(a).toBe(b) }) it.each([ - [1, 1], [2, 2], [3, 1] + [1, 1], + [2, 2], + [3, 1], ])(`last fail: %i => %i`, (a, b) => { expect(a).toBe(b) }) it.each([ - [1, 0], [2, 0], [3, 0] + [1, 0], + [2, 0], + [3, 0], ])(`all fail: %i => %i`, (a, b) => { expect(a).toBe(b) }) - it.each([ - 1, 2, 3 - ])('run %i', (a) => { + it.each([1, 2, 3])('run %i', (a) => { expect(a).toBe(a) }) it.each([ - [1, 1], [2, 4], [3, 9] - ])('run mul %i', (a,b) => { + [1, 1], + [2, 4], + [3, 9], + ])('run mul %i', (a, b) => { expect(a * a).toBe(b) }) test.each([ - ["test1", 1], - ["test2", 2], - ["test3", 3], + ['test1', 1], + ['test2', 2], + ['test3', 3], ])(`%s => %i`, (a, b) => { expect(a.at(-1)).toBe(`${b}`) }) test.each` - a | b | expected - ${1} | ${1} | ${2} - ${'a'} | ${'b'} | ${'ab'} - ${[]} | ${'b'} | ${'b'} - ${{}} | ${'b'} | ${'[object Object]b'} - ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} + a | b | expected + ${1} | ${1} | ${2} + ${'a'} | ${'b'} | ${'ab'} + ${[]} | ${'b'} | ${'b'} + ${{}} | ${'b'} | ${'[object Object]b'} + ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} `('table1: returns $expected when $a is added $b', ({ a, b, expected }) => { expect(a + b).toBe(expected) }) test.each` - a | b | expected - ${{v: 1}} | ${{v: 1}} | ${2} + a | b | expected + ${{ v: 1 }} | ${{ v: 1 }} | ${2} `('table2: returns $expected when $a.v is added $b.v', ({ a, b, expected }) => { expect(a.v + b.v).toBe(expected) }) @@ -74,10 +86,10 @@ describe('testing', (a) => { // 'Test result not fourd' error occurs as both .each patterns are matched // TODO: Fix this -describe("over matched test patterns", () => { +describe('over matched test patterns', () => { test.each(['1', '2'])('run %s', (a) => { expect(a).toBe(String(a)) - }) + }) test.each(['1', '2'])('run for %s', (a) => { expect(a).toBe(String(a)) }) diff --git a/samples/basic/test/env.test.ts b/samples/basic/test/env.test.ts index 07b8880..e528c97 100644 --- a/samples/basic/test/env.test.ts +++ b/samples/basic/test/env.test.ts @@ -1,9 +1,9 @@ -import { test, expect } from "vitest"; +import { test, expect } from 'vitest' test('process.env', () => { - expect(process.env.TEST).toBe('true'); - expect(process.env.VITEST).toBe('true'); - expect(process.env.NODE_ENV).toBe('test'); - expect(process.env.VITEST_VSCODE).toBe('true'); - expect(process.env.TEST_CUSTOM_ENV).toBe('hello'); -}); + expect(process.env.TEST).toBe('true') + expect(process.env.VITEST).toBe('true') + expect(process.env.NODE_ENV).toBe('test') + expect(process.env.VITEST_VSCODE).toBe('true') + expect(process.env.TEST_CUSTOM_ENV).toBe('hello') +}) diff --git a/samples/basic/test/snapshot.test.ts b/samples/basic/test/snapshot.test.ts index 2944c55..c27cfbf 100644 --- a/samples/basic/test/snapshot.test.ts +++ b/samples/basic/test/snapshot.test.ts @@ -5,7 +5,7 @@ describe('snapshots', () => { expect('bc').toMatchSnapshot() }) it('async', async () => { - await new Promise(resolve => setTimeout(resolve, 200)) + await new Promise((resolve) => setTimeout(resolve, 200)) expect('bc').toMatchSnapshot() }) }) diff --git a/samples/basic/test/throw.test.ts b/samples/basic/test/throw.test.ts index 73f8e54..33c26fa 100644 --- a/samples/basic/test/throw.test.ts +++ b/samples/basic/test/throw.test.ts @@ -1,9 +1,9 @@ -import { describe, expect, it } from 'vitest'; -import { addError } from '../src/add'; +import { describe, expect, it } from 'vitest' +import { addError } from '../src/add' describe('throw error', () => { it('passes expecting an error to be thrown', () => { - expect(()=>addError(1, 1)).toThrow() + expect(() => addError(1, 1)).toThrow() }) it('fails with error thrown', () => { diff --git a/samples/basic/test/using.test.ts b/samples/basic/test/using.test.ts index d7e13a3..f96949c 100644 --- a/samples/basic/test/using.test.ts +++ b/samples/basic/test/using.test.ts @@ -1,7 +1,7 @@ -import { describe, expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest' -(Symbol as any).dispose ??= Symbol('Symbol.dispose'); -(Symbol as any).asyncDispose ??= Symbol('Symbol.asyncDispose') +;(Symbol as any).dispose ??= Symbol('Symbol.dispose') +;(Symbol as any).asyncDispose ??= Symbol('Symbol.asyncDispose') describe('using keyword', () => { it('dispose', () => { @@ -37,7 +37,7 @@ class SomeAsyncDisposableResource implements AsyncDisposable { public isDisposed = false async [Symbol.asyncDispose](): Promise { - await new Promise(resolve => setTimeout(resolve, 0)) + await new Promise((resolve) => setTimeout(resolve, 0)) this.isDisposed = true } } diff --git a/samples/basic/vite.config.ts b/samples/basic/vite.config.ts index c55daa9..ed5c4d4 100644 --- a/samples/basic/vite.config.ts +++ b/samples/basic/vite.config.ts @@ -1,3 +1,3 @@ export default function () { throw new Error('This file should not be executed') -} \ No newline at end of file +} diff --git a/samples/browser/.skip/vitest.config.ts b/samples/browser/.skip/vitest.config.ts index 9148068..a3fcad2 100644 --- a/samples/browser/.skip/vitest.config.ts +++ b/samples/browser/.skip/vitest.config.ts @@ -1 +1 @@ -throw new Error('do not import') \ No newline at end of file +throw new Error('do not import') diff --git a/samples/browser/package.json b/samples/browser/package.json index fcfba43..8b3f3be 100644 --- a/samples/browser/package.json +++ b/samples/browser/package.json @@ -2,8 +2,8 @@ "name": "basic", "version": "1.0.0", "description": "", - "author": "", "license": "ISC", + "author": "", "scripts": { "test": "vitest run" }, @@ -11,8 +11,8 @@ "birpc": "^0.2.2" }, "devDependencies": { - "@vitest/browser-playwright": "catalog:latest", "@vitest/browser": "catalog:latest", + "@vitest/browser-playwright": "catalog:latest", "@vitest/coverage-v8": "catalog:latest", "playwright": "^1.47.0", "vite": "catalog:latest", diff --git a/samples/browser/src/add.ts b/samples/browser/src/add.ts index b8848d7..cd75b76 100644 --- a/samples/browser/src/add.ts +++ b/samples/browser/src/add.ts @@ -3,5 +3,5 @@ export function add(a: number, b: number) { } export function sum(from: number, to: number) { - return (from + to) * (to - from + 1) / 2 + return ((from + to) * (to - from + 1)) / 2 } diff --git a/samples/browser/test/add.test.ts b/samples/browser/test/add.test.ts index 2517c27..fca361a 100644 --- a/samples/browser/test/add.test.ts +++ b/samples/browser/test/add.test.ts @@ -16,21 +16,20 @@ describe('addition', () => { it.todo('todo') it('async task', async () => { - await new Promise(resolve => setTimeout(resolve, 100)) + await new Promise((resolve) => setTimeout(resolve, 100)) }) it('async task 0.5s', async () => { - await new Promise(resolve => setTimeout(resolve, 500)) + await new Promise((resolve) => setTimeout(resolve, 500)) }) it('async task 1s', async () => { - await new Promise(resolve => setTimeout(resolve, 1000)) + await new Promise((resolve) => setTimeout(resolve, 1000)) }) it('long task', () => { let sum = 0 - for (let i = 0; i < 2e8; i++) - sum += i + for (let i = 0; i < 2e8; i++) sum += i expect(sum).toBeGreaterThan(1) }) @@ -46,7 +45,7 @@ describe('testing', () => { expect(5 * 5).toBe(25) }) - it("mul fail", () => { + it('mul fail', () => { expect(5 * 5).toBe(25) }) }) diff --git a/samples/browser/test/console.test.ts b/samples/browser/test/console.test.ts index 4efe8f6..ee68914 100644 --- a/samples/browser/test/console.test.ts +++ b/samples/browser/test/console.test.ts @@ -1,18 +1,10 @@ import { describe, it } from 'vitest' -const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) +const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)) describe('console', () => { it('basic', () => { - console.log([ - 'string', - { hello: 'world' }, - 1234, - /regex/g, - true, - false, - null, - ]) + console.log(['string', { hello: 'world' }, 1234, /regex/g, true, false, null]) }) it('async', async () => { diff --git a/samples/browser/test/deep/deeper/deep.test.ts b/samples/browser/test/deep/deeper/deep.test.ts index 64456cd..1cc98f7 100644 --- a/samples/browser/test/deep/deeper/deep.test.ts +++ b/samples/browser/test/deep/deeper/deep.test.ts @@ -2,4 +2,4 @@ import { expect, it } from 'vitest' it('test', () => { expect(1).toBe(1) -}) \ No newline at end of file +}) diff --git a/samples/browser/test/duplicated.test.ts b/samples/browser/test/duplicated.test.ts index 8f839ba..8065b4c 100644 --- a/samples/browser/test/duplicated.test.ts +++ b/samples/browser/test/duplicated.test.ts @@ -1,8 +1,8 @@ -import { describe, test } from "vitest"; +import { describe, test } from 'vitest' -describe("testing", () => { - test("number 1", () => { }) -}); -describe("testing", () => { - test("number 2", () => { }) -}); +describe('testing', () => { + test('number 1', () => {}) +}) +describe('testing', () => { + test('number 2', () => {}) +}) diff --git a/samples/browser/test/each.test.ts b/samples/browser/test/each.test.ts index f7cff91..7842a28 100644 --- a/samples/browser/test/each.test.ts +++ b/samples/browser/test/each.test.ts @@ -1,66 +1,78 @@ -import { describe, expect, it, test, } from 'vitest' +import { describe, expect, it, test } from 'vitest' describe('testing', (a) => { it.each([ - [1, 1], [2, 2], [3, 3] + [1, 1], + [2, 2], + [3, 3], ])(`all pass: %i => %i`, (a, b) => { expect(a).toBe(b) }) it.each([ - [1, 1], [2, 1], [3, 1] + [1, 1], + [2, 1], + [3, 1], ])(`first pass: %i => %i`, (a, b) => { expect(a).toBe(b) }) it.each([ - [1, 1], [2, 2], [3, 1] + [1, 1], + [2, 2], + [3, 1], ])(`last pass: %i => %i`, (a, b) => { expect(a).toBe(b) }) it.each([ - [1, 1], [2, 2], [3, 1] + [1, 1], + [2, 2], + [3, 1], ])(`first fail: %i => %i`, (a, b) => { expect(a).toBe(b) }) it.each([ - [1, 1], [2, 2], [3, 1] + [1, 1], + [2, 2], + [3, 1], ])(`last fail: %i => %i`, (a, b) => { expect(a).toBe(b) }) it.each([ - [1, 0], [2, 0], [3, 0] + [1, 0], + [2, 0], + [3, 0], ])(`all fail: %i => %i`, (a, b) => { expect(a).toBe(b) }) - it.each([ - 1, 2, 3 - ])('run %i', (a) => { + it.each([1, 2, 3])('run %i', (a) => { expect(a).toBe(a) }) it.each([ - [1, 1], [2, 4], [3, 9] - ])('run mul %i', (a,b) => { + [1, 1], + [2, 4], + [3, 9], + ])('run mul %i', (a, b) => { expect(a * a).toBe(b) }) test.each([ - ["test1", 1], - ["test2", 2], - ["test3", 3], + ['test1', 1], + ['test2', 2], + ['test3', 3], ])(`%s => %i`, (a, b) => { expect(a.at(-1)).toBe(`${b}`) }) test.each` - a | b | expected - ${1} | ${1} | ${2} - ${'a'} | ${'b'} | ${'ab'} - ${[]} | ${'b'} | ${'b'} - ${{}} | ${'b'} | ${'[object Object]b'} - ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} + a | b | expected + ${1} | ${1} | ${2} + ${'a'} | ${'b'} | ${'ab'} + ${[]} | ${'b'} | ${'b'} + ${{}} | ${'b'} | ${'[object Object]b'} + ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} `('table1: returns $expected when $a is added $b', ({ a, b, expected }) => { expect(a + b).toBe(expected) }) test.each` - a | b | expected - ${{v: 1}} | ${{v: 1}} | ${2} + a | b | expected + ${{ v: 1 }} | ${{ v: 1 }} | ${2} `('table2: returns $expected when $a.v is added $b.v', ({ a, b, expected }) => { expect(a.v + b.v).toBe(expected) }) @@ -74,10 +86,10 @@ describe('testing', (a) => { // 'Test result not fourd' error occurs as both .each patterns are matched // TODO: Fix this -describe("over matched test patterns", () => { +describe('over matched test patterns', () => { test.each(['1', '2'])('run %s', (a) => { expect(a).toBe(String(a)) - }) + }) test.each(['1', '2'])('run for %s', (a) => { expect(a).toBe(String(a)) }) diff --git a/samples/browser/test/env.test.ts b/samples/browser/test/env.test.ts index 07b8880..e528c97 100644 --- a/samples/browser/test/env.test.ts +++ b/samples/browser/test/env.test.ts @@ -1,9 +1,9 @@ -import { test, expect } from "vitest"; +import { test, expect } from 'vitest' test('process.env', () => { - expect(process.env.TEST).toBe('true'); - expect(process.env.VITEST).toBe('true'); - expect(process.env.NODE_ENV).toBe('test'); - expect(process.env.VITEST_VSCODE).toBe('true'); - expect(process.env.TEST_CUSTOM_ENV).toBe('hello'); -}); + expect(process.env.TEST).toBe('true') + expect(process.env.VITEST).toBe('true') + expect(process.env.NODE_ENV).toBe('test') + expect(process.env.VITEST_VSCODE).toBe('true') + expect(process.env.TEST_CUSTOM_ENV).toBe('hello') +}) diff --git a/samples/browser/test/snapshot.test.ts b/samples/browser/test/snapshot.test.ts index 2944c55..c27cfbf 100644 --- a/samples/browser/test/snapshot.test.ts +++ b/samples/browser/test/snapshot.test.ts @@ -5,7 +5,7 @@ describe('snapshots', () => { expect('bc').toMatchSnapshot() }) it('async', async () => { - await new Promise(resolve => setTimeout(resolve, 200)) + await new Promise((resolve) => setTimeout(resolve, 200)) expect('bc').toMatchSnapshot() }) }) diff --git a/samples/browser/test/using.test.ts b/samples/browser/test/using.test.ts index d7e13a3..f96949c 100644 --- a/samples/browser/test/using.test.ts +++ b/samples/browser/test/using.test.ts @@ -1,7 +1,7 @@ -import { describe, expect, it } from 'vitest'; +import { describe, expect, it } from 'vitest' -(Symbol as any).dispose ??= Symbol('Symbol.dispose'); -(Symbol as any).asyncDispose ??= Symbol('Symbol.asyncDispose') +;(Symbol as any).dispose ??= Symbol('Symbol.dispose') +;(Symbol as any).asyncDispose ??= Symbol('Symbol.asyncDispose') describe('using keyword', () => { it('dispose', () => { @@ -37,7 +37,7 @@ class SomeAsyncDisposableResource implements AsyncDisposable { public isDisposed = false async [Symbol.asyncDispose](): Promise { - await new Promise(resolve => setTimeout(resolve, 0)) + await new Promise((resolve) => setTimeout(resolve, 0)) this.isDisposed = true } } diff --git a/samples/browser/vitest.config.ts b/samples/browser/vitest.config.ts index 2f39627..373e561 100644 --- a/samples/browser/vitest.config.ts +++ b/samples/browser/vitest.config.ts @@ -3,9 +3,10 @@ import { defineConfig } from 'vitest/config' export default defineConfig(async () => { - const provider: any = process.env.TEST_LEGACY !== 'true' - ? (await import('@vitest/browser-playwright')).playwright() - : 'playwright' + const provider: any = + process.env.TEST_LEGACY !== 'true' + ? (await import('@vitest/browser-playwright')).playwright() + : 'playwright' return { esbuild: { target: 'es2020', @@ -17,10 +18,8 @@ export default defineConfig(async () => { enabled: true, headless: true, provider, - instances: [ - { browser: 'chromium' as const }, - ], - } + instances: [{ browser: 'chromium' as const }], + }, }, } }) diff --git a/samples/continuous/package.json b/samples/continuous/package.json index 031dd64..16f372a 100644 --- a/samples/continuous/package.json +++ b/samples/continuous/package.json @@ -2,9 +2,9 @@ "name": "continuous", "version": "1.0.0", "description": "", - "type": "module", - "author": "", "license": "ISC", + "author": "", + "type": "module", "main": "index.js", "scripts": { "test": "vitest run" diff --git a/samples/continuous/test/imports-divide.test.ts b/samples/continuous/test/imports-divide.test.ts index 3330371..d5a813f 100644 --- a/samples/continuous/test/imports-divide.test.ts +++ b/samples/continuous/test/imports-divide.test.ts @@ -5,4 +5,3 @@ import { expect } from 'vitest' test('divide', () => { expect(divide(6, 3)).toBe(2) }) - diff --git a/samples/e2e/package.json b/samples/e2e/package.json index 813421c..0290464 100644 --- a/samples/e2e/package.json +++ b/samples/e2e/package.json @@ -1,8 +1,8 @@ { "name": "@vitest/vscode-sample-e2e", - "type": "module", - "private": true, "version": "1.0.0", + "private": true, + "type": "module", "scripts": { "test": "vitest" }, diff --git a/samples/e2e/vite.config.ts b/samples/e2e/vite.config.ts index c55daa9..ed5c4d4 100644 --- a/samples/e2e/vite.config.ts +++ b/samples/e2e/vite.config.ts @@ -1,3 +1,3 @@ export default function () { throw new Error('This file should not be executed') -} \ No newline at end of file +} diff --git a/samples/imba/README.md b/samples/imba/README.md index 8bfa2d6..6b40560 100644 --- a/samples/imba/README.md +++ b/samples/imba/README.md @@ -71,5 +71,6 @@ Run and watch the tests. Run and watch the tests - and open the [Vitest UI](https://vitest.dev/guide/ui.html) ## Notes + - This app doesn't have a server. If you need a full stack web application with server logic you can use [imba base template](https://github.com/imba/imba-base-template) or check out [Vite's backend integration guide](https://vitejs.dev/guide/backend-integration.html) -- There is a temporary `src/main.js` file that is still necessary for Vite to work correctly. You don't have to do anything with this file. And this will probably be fixed in a future version of Vite. \ No newline at end of file +- There is a temporary `src/main.js` file that is still necessary for Vite to work correctly. You don't have to do anything with this file. And this will probably be fixed in a future version of Vite. diff --git a/samples/imba/index.html b/samples/imba/index.html index 529af0c..a772454 100644 --- a/samples/imba/index.html +++ b/samples/imba/index.html @@ -1,4 +1,4 @@ - + diff --git a/samples/imba/src/app.css b/samples/imba/src/app.css index 1abfcc1..bcc7233 100644 --- a/samples/imba/src/app.css +++ b/samples/imba/src/app.css @@ -1,81 +1,81 @@ :root { - font-family: Inter, Avenir, Helvetica, Arial, sans-serif; - font-size: 16px; - line-height: 24px; - font-weight: 400; + font-family: Inter, Avenir, Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 24px; + font-weight: 400; - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; - } + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} - a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; - } - a:hover { - color: #535bf2; - } +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} - body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; - } +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} - h1 { - font-size: 3.2em; - line-height: 1.1; - } +h1 { + font-size: 3.2em; + line-height: 1.1; +} - .card { - padding: 2em; - } +.card { + padding: 2em; +} - #app { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; - } +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} - button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; - } - button:hover { - border-color: #646cff; +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; } - button:focus, - button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; + a:hover { + color: #747bff; } - - @media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } + button { + background-color: #f9f9f9; } +} diff --git a/samples/imba/src/main.js b/samples/imba/src/main.js index e6824ab..136e25d 100644 --- a/samples/imba/src/main.js +++ b/samples/imba/src/main.js @@ -1 +1 @@ -import "./main.imba" \ No newline at end of file +import './main.imba' diff --git a/samples/imba/tsconfig.json b/samples/imba/tsconfig.json index 89504dd..deb5d39 100644 --- a/samples/imba/tsconfig.json +++ b/samples/imba/tsconfig.json @@ -1,7 +1,5 @@ { - "compilerOptions": { - "types": [ - "vitest/importMeta" - ] - } - } \ No newline at end of file + "compilerOptions": { + "types": ["vitest/importMeta"] + } +} diff --git a/samples/imba/vite.config.js b/samples/imba/vite.config.js index 1435b06..c8fcff5 100644 --- a/samples/imba/vite.config.js +++ b/samples/imba/vite.config.js @@ -1,20 +1,18 @@ -import imba from 'imba/plugin'; -import { defineConfig } from 'vite'; +import imba from 'imba/plugin' +import { defineConfig } from 'vite' import GithubActionsReporter from 'vitest-github-actions-reporter-temp' export default defineConfig({ - plugins: [imba()], - define: { - 'import.meta.vitest': 'undefined', - }, - test: { - globals: true, - include: ["**/*.{test,spec}.{imba,js,mjs,cjs,ts,mts,cts,jsx,tsx}"], - includeSource: ['src/**/*.{imba,js,mjs,cjs,ts,mts,cts,jsx,tsx}'], - environment: "jsdom", - setupFiles: ["./test/setup.imba"], - reporters: process.env.GITHUB_ACTIONS - ? new GithubActionsReporter() - : 'default' - }, -}); + plugins: [imba()], + define: { + 'import.meta.vitest': 'undefined', + }, + test: { + globals: true, + include: ['**/*.{test,spec}.{imba,js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + includeSource: ['src/**/*.{imba,js,mjs,cjs,ts,mts,cts,jsx,tsx}'], + environment: 'jsdom', + setupFiles: ['./test/setup.imba'], + reporters: process.env.GITHUB_ACTIONS ? new GithubActionsReporter() : 'default', + }, +}) diff --git a/samples/in-source/package.json b/samples/in-source/package.json index eb85478..2a53e81 100644 --- a/samples/in-source/package.json +++ b/samples/in-source/package.json @@ -2,8 +2,8 @@ "name": "in-source", "version": "1.0.0", "description": "", - "author": "", "license": "ISC", + "author": "", "main": "index.js", "scripts": { "test": "vitest run" diff --git a/samples/in-source/tsconfig.json b/samples/in-source/tsconfig.json index 5076fb8..993c4c7 100644 --- a/samples/in-source/tsconfig.json +++ b/samples/in-source/tsconfig.json @@ -2,8 +2,6 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "types": [ - "vitest/importMeta" - ] + "types": ["vitest/importMeta"] } -} \ No newline at end of file +} diff --git a/samples/monorepo-vitest-workspace/package.json b/samples/monorepo-vitest-workspace/package.json index 183cdf3..114f64f 100644 --- a/samples/monorepo-vitest-workspace/package.json +++ b/samples/monorepo-vitest-workspace/package.json @@ -3,8 +3,8 @@ "version": "1.0.1", "private": true, "description": "", - "author": "", "license": "ISC", + "author": "", "scripts": { "test": "vitest" }, diff --git a/samples/monorepo-vitest-workspace/packages/react copy/test/basic.test.tsx b/samples/monorepo-vitest-workspace/packages/react copy/test/basic.test.tsx index 7ee94aa..b9e68a9 100644 --- a/samples/monorepo-vitest-workspace/packages/react copy/test/basic.test.tsx +++ b/samples/monorepo-vitest-workspace/packages/react copy/test/basic.test.tsx @@ -11,9 +11,7 @@ function toJson(component: renderer.ReactTestRenderer) { } test('Link changes the class when hovered', () => { - const component = renderer.create( - Anthony Fu, - ) + const component = renderer.create(Anthony Fu) let tree = toJson(component) expect(tree).toMatchSnapshot() diff --git a/samples/monorepo-vitest-workspace/packages/react copy/vitest.config.ts b/samples/monorepo-vitest-workspace/packages/react copy/vitest.config.ts index 5c4ecbc..73c4c11 100644 --- a/samples/monorepo-vitest-workspace/packages/react copy/vitest.config.ts +++ b/samples/monorepo-vitest-workspace/packages/react copy/vitest.config.ts @@ -1,5 +1,3 @@ import { defineProject } from 'vitest/config' -export default defineProject({ - -}) +export default defineProject({}) diff --git a/samples/monorepo-vitest-workspace/packages/react/test/basic.test.tsx b/samples/monorepo-vitest-workspace/packages/react/test/basic.test.tsx index 7ee94aa..b9e68a9 100644 --- a/samples/monorepo-vitest-workspace/packages/react/test/basic.test.tsx +++ b/samples/monorepo-vitest-workspace/packages/react/test/basic.test.tsx @@ -11,9 +11,7 @@ function toJson(component: renderer.ReactTestRenderer) { } test('Link changes the class when hovered', () => { - const component = renderer.create( - Anthony Fu, - ) + const component = renderer.create(Anthony Fu) let tree = toJson(component) expect(tree).toMatchSnapshot() diff --git a/samples/monorepo-vitest-workspace/packages/react/vitest.config.ts b/samples/monorepo-vitest-workspace/packages/react/vitest.config.ts index 5c4ecbc..73c4c11 100644 --- a/samples/monorepo-vitest-workspace/packages/react/vitest.config.ts +++ b/samples/monorepo-vitest-workspace/packages/react/vitest.config.ts @@ -1,5 +1,3 @@ import { defineProject } from 'vitest/config' -export default defineProject({ - -}) +export default defineProject({}) diff --git a/samples/monorepo-vitest-workspace/test/vitest.config.ts b/samples/monorepo-vitest-workspace/test/vitest.config.ts index 9e5305e..6249052 100644 --- a/samples/monorepo-vitest-workspace/test/vitest.config.ts +++ b/samples/monorepo-vitest-workspace/test/vitest.config.ts @@ -1 +1 @@ -throw new Error('should not be called') \ No newline at end of file +throw new Error('should not be called') diff --git a/samples/monorepo-vitest-workspace/vitest.config.ts b/samples/monorepo-vitest-workspace/vitest.config.ts index 0edfb1f..302d096 100644 --- a/samples/monorepo-vitest-workspace/vitest.config.ts +++ b/samples/monorepo-vitest-workspace/vitest.config.ts @@ -9,6 +9,6 @@ export default defineConfig({ environment: 'happy-dom', }, }, - ] - } + ], + }, }) diff --git a/samples/multi-root-workspace/sample.code-workspace b/samples/multi-root-workspace/sample.code-workspace index 387736d..36dac20 100644 --- a/samples/multi-root-workspace/sample.code-workspace +++ b/samples/multi-root-workspace/sample.code-workspace @@ -2,26 +2,26 @@ "folders": [ { "name": "rust", - "path": "./rust-project" + "path": "./rust-project", }, { "name": "basic", - "path": "../basic" + "path": "../basic", }, { "name": "react", - "path": "../monorepo/packages/react" + "path": "../monorepo/packages/react", }, { "name": "no-root-config", - "path": "../monorepo-no-root" + "path": "../monorepo-no-root", }, { "name": "react-no-root", - "path": "../monorepo-no-root/packages/react" - } + "path": "../monorepo-no-root/packages/react", + }, ], "settings": { - "vitest.disabledWorkspaceFolders": ["no-root-config"] - } + "vitest.disabledWorkspaceFolders": ["no-root-config"], + }, } diff --git a/samples/multiple-configs/app1/vitest.config.js b/samples/multiple-configs/app1/vitest.config.js index 56004c9..b1c6ea4 100644 --- a/samples/multiple-configs/app1/vitest.config.js +++ b/samples/multiple-configs/app1/vitest.config.js @@ -1 +1 @@ -export default {} \ No newline at end of file +export default {} diff --git a/samples/multiple-configs/app2/vitest.config.js b/samples/multiple-configs/app2/vitest.config.js index 56004c9..b1c6ea4 100644 --- a/samples/multiple-configs/app2/vitest.config.js +++ b/samples/multiple-configs/app2/vitest.config.js @@ -1 +1 @@ -export default {} \ No newline at end of file +export default {} diff --git a/samples/multiple-configs/package.json b/samples/multiple-configs/package.json index c051479..0d9ef14 100644 --- a/samples/multiple-configs/package.json +++ b/samples/multiple-configs/package.json @@ -4,4 +4,4 @@ "dependencies": { "vitest": "catalog:latest" } -} \ No newline at end of file +} diff --git a/samples/no-config/package.json b/samples/no-config/package.json index 2349376..ca91b88 100644 --- a/samples/no-config/package.json +++ b/samples/no-config/package.json @@ -2,8 +2,8 @@ "name": "no-package", "version": "1.0.0", "description": "", - "author": "", "license": "ISC", + "author": "", "main": "index.js", "scripts": { "test": "vitest run --pool=forks" diff --git a/samples/readme/package.json b/samples/readme/package.json index 87f44b3..683229d 100644 --- a/samples/readme/package.json +++ b/samples/readme/package.json @@ -2,13 +2,13 @@ "name": "readme", "version": "1.0.0", "description": "", + "keywords": [], + "license": "ISC", + "author": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, - "keywords": [], - "author": "", - "license": "ISC", "devDependencies": { "vitest": "catalog:latest" } diff --git a/samples/readme/test/example.test.ts b/samples/readme/test/example.test.ts index 7f0eb7c..fa1554c 100644 --- a/samples/readme/test/example.test.ts +++ b/samples/readme/test/example.test.ts @@ -17,4 +17,4 @@ describe('test suite', () => { // test('not run', () => { // expect(add(4, 4)).toBe(8) // }) -}) \ No newline at end of file +}) diff --git a/samples/vue/components/AsyncWrapper.vue b/samples/vue/components/AsyncWrapper.vue index e75b49d..1171f0e 100644 --- a/samples/vue/components/AsyncWrapper.vue +++ b/samples/vue/components/AsyncWrapper.vue @@ -7,8 +7,6 @@ defineProps<{ promise: Promise }>() diff --git a/samples/vue/components/Hello.vue b/samples/vue/components/Hello.vue index f242ec0..505f33b 100644 --- a/samples/vue/components/Hello.vue +++ b/samples/vue/components/Hello.vue @@ -11,7 +11,5 @@ defineExpose(props) diff --git a/samples/vue/test/async.test.ts b/samples/vue/test/async.test.ts index 69d68ba..38c0dd6 100644 --- a/samples/vue/test/async.test.ts +++ b/samples/vue/test/async.test.ts @@ -7,7 +7,7 @@ test('async component with suspense', async () => { let resolve: Function // eslint-disable-next-line promise/param-names - const promise = new Promise(_resolve => resolve = _resolve) + const promise = new Promise((_resolve) => (resolve = _resolve)) const wrapper = mount(AsyncWrapper, { props: { promise, diff --git a/samples/vue/vitest.config.ts b/samples/vue/vitest.config.ts index ed8dec6..660aa90 100644 --- a/samples/vue/vitest.config.ts +++ b/samples/vue/vitest.config.ts @@ -4,9 +4,7 @@ import { defineConfig } from 'vite' import Vue from '@vitejs/plugin-vue' export default defineConfig({ - plugins: [ - Vue(), - ], + plugins: [Vue()], test: { globals: true, environment: 'happy-dom', @@ -15,7 +13,7 @@ export default defineConfig({ reporter: ['text', 'json'], enabled: true, exclude: [], - include: ['components/**'] - } + include: ['components/**'], + }, }, }) diff --git a/scripts/ecosystem-ci.mts b/scripts/ecosystem-ci.mts index c6a4ce9..6c83bea 100644 --- a/scripts/ecosystem-ci.mts +++ b/scripts/ecosystem-ci.mts @@ -13,8 +13,7 @@ async function main() { if (process.env.CI === 'true' && process.platform === 'linux') { await $`xvfb-run pnpm test` await $`xvfb-run pnpm test-e2e --retry 2` - } - else { + } else { await $`pnpm test` await $`pnpm test-e2e` } diff --git a/scripts/lower-vitest-version.js b/scripts/lower-vitest-version.js index 21e16b2..c5f13d7 100644 --- a/scripts/lower-vitest-version.js +++ b/scripts/lower-vitest-version.js @@ -5,7 +5,7 @@ pkg.pnpm = { overrides: { '@vitest/browser': '^3.2.4', '@vitest/coverage': '^3.2.4', - 'vitest': '^3.2.4', + vitest: '^3.2.4', }, } writeFileSync('./package.json', `${JSON.stringify(pkg, null, 2)}\n`, 'utf-8') diff --git a/scripts/release.mts b/scripts/release.mts index 2b792d8..b31b964 100644 --- a/scripts/release.mts +++ b/scripts/release.mts @@ -29,15 +29,23 @@ const result = await prompts([ choices: [ { value: major, title: `${'major'.padStart(PADDING, ' ')} ${c.bold(major)}` }, { value: minor, title: `${'minor'.padStart(PADDING, ' ')} ${c.bold(minor)}` }, - { value: preminor, title: `${'pre-minor'.padStart(PADDING, ' ')} ${c.bold(preminor)} (odd number)` }, - { value: patch, title: `${(isCurrentlyPreRelease ? 'pre-patch' : 'patch').padStart(PADDING, ' ')} ${c.bold(patch)}` }, - { value: currentVersion, title: `${'as-is'.padStart(PADDING, ' ')} ${c.bold(currentVersion)}` }, + { + value: preminor, + title: `${'pre-minor'.padStart(PADDING, ' ')} ${c.bold(preminor)} (odd number)`, + }, + { + value: patch, + title: `${(isCurrentlyPreRelease ? 'pre-patch' : 'patch').padStart(PADDING, ' ')} ${c.bold(patch)}`, + }, + { + value: currentVersion, + title: `${'as-is'.padStart(PADDING, ' ')} ${c.bold(currentVersion)}`, + }, ], }, ]) -if (!result.release) - process.exit(0) +if (!result.release) process.exit(0) await versionBump({ release: result.release, diff --git a/test/e2e/runner.test.ts b/test/e2e/runner.test.ts index c8187e8..8f4f09f 100644 --- a/test/e2e/runner.test.ts +++ b/test/e2e/runner.test.ts @@ -118,7 +118,7 @@ test('browser mode correctly collects tests', async ({ launch }) => { console: 'waiting', }) - editFile('samples/browser/test/console.test.ts', content => `/arakara---\n${content}`) + editFile('samples/browser/test/console.test.ts', (content) => `/arakara---\n${content}`) await expect(consoleTest).toHaveError('Error: Unterminated regular expression') }) @@ -142,8 +142,12 @@ test('watcher updates the file if there are several config files', async ({ laun math: 'waiting', }) - editFile('samples/multiple-configs/app1/test-app1.test.ts', content => content.replace('math', 'math-123')) - editFile('samples/multiple-configs/app2/test-app2.test.ts', content => content.replace('math', 'math-987')) + editFile('samples/multiple-configs/app1/test-app1.test.ts', (content) => + content.replace('math', 'math-123'), + ) + editFile('samples/multiple-configs/app2/test-app2.test.ts', (content) => + content.replace('math', 'math-987'), + ) await expect(app1Test).toHaveTests({ 'math-123': 'waiting', @@ -167,7 +171,7 @@ test('ast collector keeps the pattern on rerun', async ({ launch }) => { const item = tester.tree.getFileItem('each.test.ts') await expect(item).toHaveTests({ - 'testing': { + testing: { // all pass: %i => %i 'pattern|3': 'waiting', // table1: returns $expected when $a is added $b @@ -195,7 +199,7 @@ test('ast collector keeps the pattern on rerun', async ({ launch }) => { // table1: returns $expected when $a is added $b 'pattern|6': 'waiting', 'table1: returns 2 when 1 is added 1': 'passed', - 'table1: returns \'ab\' when \'a\' is added \'b\'': 'passed', + "table1: returns 'ab' when 'a' is added 'b'": 'passed', }, // testing %s 'pattern|9': 'waiting', @@ -229,13 +233,13 @@ describe('continuous testing', () => { await item.toggleContinuousRun() - editFile('samples/continuous/test/imports-divide.test.ts', content => `${content}\n`) + editFile('samples/continuous/test/imports-divide.test.ts', (content) => `${content}\n`) await expect(item).toHaveTests({ divide: 'passed', }) - editFile('samples/continuous/src/calculator.ts', content => content.replace('a / b', '1000')) + editFile('samples/continuous/src/calculator.ts', (content) => content.replace('a / b', '1000')) await expect(item).toHaveTests({ divide: 'failed', @@ -244,8 +248,6 @@ describe('continuous testing', () => { const errors = await tester.errors.getInlineErrors() - expect(errors).toEqual([ - '1000 != 2', - ]) + expect(errors).toEqual(['1000 != 2']) }) }) diff --git a/test/e2e/utils/assertions.ts b/test/e2e/utils/assertions.ts index b5e926d..028bb94 100644 --- a/test/e2e/utils/assertions.ts +++ b/test/e2e/utils/assertions.ts @@ -26,10 +26,11 @@ expect.extend({ return { pass, - message: () => `${this.utils.matcherHint('toHaveState', title, state, { isNot: this.isNot })}\n\n` - + `Locator: ${item.locator}\n` - + `Expected: ${this.isNot ? 'not ' : ''}to have state: ${this.utils.printExpected(state)}\n` - + `Received: ${this.utils.printReceived(title)}\n`, + message: () => + `${this.utils.matcherHint('toHaveState', title, state, { isNot: this.isNot })}\n\n` + + `Locator: ${item.locator}\n` + + `Expected: ${this.isNot ? 'not ' : ''}to have state: ${this.utils.printExpected(state)}\n` + + `Received: ${this.utils.printReceived(title)}\n`, name: 'toHaveState', } }, @@ -55,7 +56,7 @@ expect.extend({ if (index) { locator += `[data-index="${index}"]` } - await expect(page.locator(locator)).toBeAttached() + await expect(page.locator(locator)).toBeAttached({ timeout: 10_000 }) } const counter = { index: currentIndex } @@ -67,8 +68,7 @@ expect.extend({ const item = tests[test] if (typeof item === 'string') { await assert(test, level, item, counter.index) - } - else { + } else { const [name, index = counter.index] = test.split('|') let locator = `[aria-label*="${name}"][aria-level="${level}"]` if (index) { diff --git a/test/e2e/utils/downloadSetup.ts b/test/e2e/utils/downloadSetup.ts index b50d006..e5c810c 100644 --- a/test/e2e/utils/downloadSetup.ts +++ b/test/e2e/utils/downloadSetup.ts @@ -4,8 +4,7 @@ import type { GlobalSetupContext } from 'vitest/node' export default async function downloadVscode({ provide }: GlobalSetupContext) { if (process.env.VSCODE_E2E_DOWNLOAD_PATH) provide('executablePath', process.env.VSCODE_E2E_DOWNLOAD_PATH) - else - provide('executablePath', await download()) + else provide('executablePath', await download()) } declare module 'vitest' { diff --git a/test/e2e/utils/helper.ts b/test/e2e/utils/helper.ts index 751ee87..1d5b5bf 100644 --- a/test/e2e/utils/helper.ts +++ b/test/e2e/utils/helper.ts @@ -18,9 +18,11 @@ type LaunchFixture = (options: { extensionPath?: string workspacePath?: string trace?: 'on' | 'off' -}) => Promise void | Promise) => Promise -}> +}) => Promise< + Context & { + step: (name: string, fn: (context: Context) => void | Promise) => Promise + } +> const defaultConfig = process.env as { VSCODE_E2E_EXTENSION_PATH?: string @@ -30,7 +32,8 @@ const defaultConfig = process.env as { export const test = baseTest.extend<{ launch: LaunchFixture; taskName: string; logPath: string }>({ taskName: async ({ task }, use) => use(`${task.name}-${task.id}`), - logPath: async ({ taskName }, use) => use(resolve(`./test-results/${process.env.OS_NAME + '/' || ''}tests-logs-${taskName}.txt`)), + logPath: async ({ taskName }, use) => + use(resolve(`./test-results/${process.env.OS_NAME + '/' || ''}tests-logs-${taskName}.txt`)), launch: async ({ taskName, logPath }, use) => { const teardowns: (() => Promise)[] = [] @@ -63,12 +66,13 @@ export const test = baseTest.extend<{ launch: LaunchFixture; taskName: string; l }) const page = await app.firstWindow() - if (trace) - await page.context().tracing.start({ screenshots: true, snapshots: true }) + if (trace) await page.context().tracing.start({ screenshots: true, snapshots: true }) const teardown = async () => { if (trace) { - await page.context().tracing.stop({ path: `test-results/${process.env.OS_NAME + '/' || ''}${taskName}/basic.zip` }) + await page.context().tracing.stop({ + path: `test-results/${process.env.OS_NAME + '/' || ''}${taskName}/basic.zip`, + }) } await app.close() await fs.promises.rm(tempDir, { recursive: true, force: true }) @@ -81,8 +85,7 @@ export const test = baseTest.extend<{ launch: LaunchFixture; taskName: string; l await page.reload() try { await fn({ page, tester }) - } - catch (err) { + } catch (err) { throw new Error(`Error during step "${name}"`, { cause: err }) } } @@ -92,7 +95,6 @@ export const test = baseTest.extend<{ launch: LaunchFixture; taskName: string; l return { page, tester, step } }) - for (const teardown of teardowns) - await teardown() + for (const teardown of teardowns) await teardown() }, }) diff --git a/test/e2e/utils/tester.ts b/test/e2e/utils/tester.ts index c92115f..31b315b 100644 --- a/test/e2e/utils/tester.ts +++ b/test/e2e/utils/tester.ts @@ -18,8 +18,7 @@ export class VSCodeTester { async openTestTab() { const tabLocator = this.page.getByRole('tab', { name: 'Testing' }) const attribute = await tabLocator.getAttribute('aria-selected') - if (attribute !== 'true') - await tabLocator.locator('a').click() + if (attribute !== 'true') await tabLocator.locator('a').click() } async runAllTests() { @@ -49,7 +48,7 @@ class TesterTree { this.page.locator(`[aria-label*="${label} ("]`), this.page, project, - this.logPath + this.logPath, ) } @@ -63,34 +62,31 @@ class TesterTree { // test already run .or(this.page.locator(`[aria-label="${segment}"][aria-level="${i + 1}"]`)) const state = await locator.getAttribute('aria-expanded') - if (state === 'true') - continue + if (state === 'true') continue await locator.click({ force: true }) } } } class TesterErrorOutput { - constructor( - private page: Page, - ) {} + constructor(private page: Page) {} async getInlineErrors() { const locator = this.page.locator('.test-error-content-widget') const text = await locator.allInnerTexts() - return text.map(t => t.trim().replace(/\s/g, ' ')) + return text.map((t) => t.trim().replace(/\s/g, ' ')) } async getInlineExpectedOutput() { - return await this.page.locator( - '.test-output-peek .editor.original .view-lines[role="presentation"]', - ).textContent() + return await this.page + .locator('.test-output-peek .editor.original .view-lines[role="presentation"]') + .textContent() } async getInlineActualOutput() { - return await this.page.locator( - '.test-output-peek .editor.modified .view-lines[role="presentation"]', - ).textContent() + return await this.page + .locator('.test-output-peek .editor.modified .view-lines[role="presentation"]') + .textContent() } } @@ -121,12 +117,15 @@ export class TesterTestItem { async toggleContinuousRun() { await this.locator.hover() await this.locator.getByLabel(/Turn (on|off) Continuous Run/).click() - await vi.waitUntil(() => { - const log = readFileSync(this.logPath, 'utf-8') - return log.includes('Watching test files') || log.includes('Watching all test files') - }, { - timeout: 5_000, - }) + await vi.waitUntil( + () => { + const log = readFileSync(this.logPath, 'utf-8') + return log.includes('Watching test files') || log.includes('Watching all test files') + }, + { + timeout: 5_000, + }, + ) } async navigate() { @@ -145,8 +144,7 @@ afterEach(() => { fs.writeFileSync(file, content, 'utf-8') }) createdFiles.forEach((file) => { - if (fs.existsSync(file)) - fs.unlinkSync(file) + if (fs.existsSync(file)) fs.unlinkSync(file) }) originalFiles.clear() createdFiles.clear() @@ -154,7 +152,6 @@ afterEach(() => { export function editFile(file: string, callback: (content: string) => string) { const content = fs.readFileSync(file, 'utf-8') - if (!originalFiles.has(file)) - originalFiles.set(file, content) + if (!originalFiles.has(file)) originalFiles.set(file, content) fs.writeFileSync(file, callback(content), 'utf-8') } diff --git a/test/e2e/vitest.config.ts b/test/e2e/vitest.config.ts index 71cff0e..9205365 100644 --- a/test/e2e/vitest.config.ts +++ b/test/e2e/vitest.config.ts @@ -9,12 +9,8 @@ export default defineConfig({ VSCODE_E2E_EXTENSION_PATH: './', VSCODE_E2E_TRACE: 'on', }, - setupFiles: [ - './utils/assertions.ts', - ], - globalSetup: [ - './utils/downloadSetup.ts', - ], + setupFiles: ['./utils/assertions.ts'], + globalSetup: ['./utils/downloadSetup.ts'], retry: process.env.CI ? 2 : 0, }, }) diff --git a/test/unit/TestData.test.ts b/test/unit/TestData.test.ts index f217bec..cf40040 100644 --- a/test/unit/TestData.test.ts +++ b/test/unit/TestData.test.ts @@ -1,7 +1,13 @@ import * as path from 'node:path' import * as vscode from 'vscode' import { expect } from 'chai' -import { TestCase, TestFile, TestFolder, TestSuite, getTestData } from '../../packages/extension/src/testTreeData' +import { + TestCase, + TestFile, + TestFolder, + TestSuite, + getTestData, +} from '../../packages/extension/src/testTreeData' describe('TestData', () => { const ctrl = vscode.tests.createTestController('mocha', 'Vitest') @@ -15,11 +21,7 @@ describe('TestData', () => { uri, ) TestFolder.register(folderItem) - const testItem = ctrl.createTestItem( - filepath, - path.basename(filepath), - uri, - ) + const testItem = ctrl.createTestItem(filepath, path.basename(filepath), uri) ctrl.items.add(testItem) const file = TestFile.register( testItem, @@ -28,30 +30,14 @@ describe('TestData', () => { null as any, // not used yet { project: '', pool: 'trheads' }, ) - const suiteItem = ctrl.createTestItem( - `${filepath}_1`, - 'describe', - uri, - ) + const suiteItem = ctrl.createTestItem(`${filepath}_1`, 'describe', uri) testItem.children.add(suiteItem) - const testItem1 = ctrl.createTestItem( - `${filepath}_1_1`, - 'test', - uri, - ) + const testItem1 = ctrl.createTestItem(`${filepath}_1_1`, 'test', uri) - const testItem2 = ctrl.createTestItem( - `${filepath}_1_2`, - 'test 1', - uri, - ) + const testItem2 = ctrl.createTestItem(`${filepath}_1_2`, 'test 1', uri) - const testItem3 = ctrl.createTestItem( - `${filepath}_1_3`, - 'test 2', - uri, - ) + const testItem3 = ctrl.createTestItem(`${filepath}_1_3`, 'test 2', uri) suiteItem.children.add(testItem1) suiteItem.children.add(testItem2) @@ -73,7 +59,9 @@ describe('TestData', () => { }) it('throws an error if data was not set', () => { - expect(() => getTestData({ label: 'invalid test' } as any)).to.throw(/Test data not found for "invalid test"/) + expect(() => getTestData({ label: 'invalid test' } as any)).to.throw( + /Test data not found for "invalid test"/, + ) }) }) }) diff --git a/test/unit/config.test.ts b/test/unit/config.test.ts index 5aa6f55..639ce2c 100644 --- a/test/unit/config.test.ts +++ b/test/unit/config.test.ts @@ -4,7 +4,5 @@ import { expect } from 'chai' import { resolveConfigPath } from '../../packages/extension/src/config' it('correctly resolves ~', () => { - expect(resolveConfigPath('~/test')).to.equal( - resolve(homedir(), 'test'), - ) + expect(resolveConfigPath('~/test')).to.equal(resolve(homedir(), 'test')) }) diff --git a/test/unit/fixtures/discover/00_simple.ts b/test/unit/fixtures/discover/00_simple.ts index e752b9c..e961085 100644 --- a/test/unit/fixtures/discover/00_simple.ts +++ b/test/unit/fixtures/discover/00_simple.ts @@ -4,7 +4,7 @@ describe('describe', () => { it('test', () => { expect(1).toBe(1) }) - it.each([1, 2, 3])("test %i", (a) => { - expect(a).toBe(a); + it.each([1, 2, 3])('test %i', (a) => { + expect(a).toBe(a) }) }) diff --git a/test/unit/pkg.test.ts b/test/unit/pkg.test.ts index 617e790..6382583 100644 --- a/test/unit/pkg.test.ts +++ b/test/unit/pkg.test.ts @@ -2,15 +2,12 @@ import { expect } from 'chai' import { findFirstUniqueFolderNames } from '../../packages/extension/src/spawn/pkg' it('correctly makes prefixes unique', () => { - expect(findFirstUniqueFolderNames([ - '/User/usr/vitest/packages/pkg1/react/vitest.config.ts', - '/User/usr/vitest/packages/pkg2/react/vitest.config.ts', - '/User/usr/vitest/packages/pkg2/some-new-field/react/vitest.config.ts', - '/User/usr/vitest/react/vitest.config.ts', - ])).to.eql([ - 'pkg1', - 'pkg2', - 'some-new-field', - 'vitest', - ]) + expect( + findFirstUniqueFolderNames([ + '/User/usr/vitest/packages/pkg1/react/vitest.config.ts', + '/User/usr/vitest/packages/pkg2/react/vitest.config.ts', + '/User/usr/vitest/packages/pkg2/some-new-field/react/vitest.config.ts', + '/User/usr/vitest/react/vitest.config.ts', + ]), + ).to.eql(['pkg1', 'pkg2', 'some-new-field', 'vitest']) }) diff --git a/tsdown.config.mjs b/tsdown.config.mjs index 0a7d890..aa011f3 100644 --- a/tsdown.config.mjs +++ b/tsdown.config.mjs @@ -16,7 +16,9 @@ export default defineConfig([ inlineOnly: false, platform: 'node', define: { - 'process.env.EXTENSION_NODE_ENV': JSON.stringify(process.env.EXTENSION_NODE_ENV || 'production'), + 'process.env.EXTENSION_NODE_ENV': JSON.stringify( + process.env.EXTENSION_NODE_ENV || 'production', + ), }, }, { -- 2.51.2 From a3abb96dced0057c3a28104c6e01d527a5499a71 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 10 Mar 2026 16:58:32 +0100 Subject: [PATCH 12/64] feat: add "run related tests" command (#749) --- README.md | 4 ++++ package.json | 10 ++++++++++ packages/extension/src/extension.ts | 31 ++++++++++++++++++++++++++++- packages/extension/src/runQueue.ts | 11 ++++------ packages/extension/src/spawn/ws.ts | 2 ++ packages/shared/src/index.ts | 1 + packages/worker/src/index.ts | 1 + 7 files changed, 52 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2e6f0a0..cc76863 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,10 @@ You can also type the same command in the quick picker while the file is open. ![Reveal test in explorer](./img/reveal-in-picker.png 'Reveal test in explorer') +### Run Related Tests + +You can run all tests that import the current file by using the "Run Related Tests" command. Its triggers are visible in the same places as "Reveal in Test Explorer". + ### Import Breakdown If you use Vitest 4.0.15 or higher, during continuous runs the extension will show how long it took to load the module on the same line where the import is defined. This number includes transform time and evaluation time, including static imports. diff --git a/package.json b/package.json index 6262c5d..0e5603c 100644 --- a/package.json +++ b/package.json @@ -132,6 +132,11 @@ "command": "vitest.revealInTestExplorer", "category": "Vitest" }, + { + "title": "Run Related Tests", + "command": "vitest.runRelatedTests", + "category": "Vitest" + }, { "title": "Open Transformed Module", "command": "vitest.openTransformedModule", @@ -180,6 +185,11 @@ "command": "vitest.openTransformedModule", "when": "vitest.environmentsSupported", "group": "vitest" + }, + { + "command": "vitest.runRelatedTests", + "when": "vitest.testFiles && !(resourcePath in vitest.testFiles)", + "group": "vitest" } ], "commandPalette": [ diff --git a/packages/extension/src/extension.ts b/packages/extension/src/extension.ts index c40ceaf..b2ddc5e 100644 --- a/packages/extension/src/extension.ts +++ b/packages/extension/src/extension.ts @@ -1,6 +1,6 @@ import type { VitestAPI } from './api' import type { VitestProcessAPI } from './apiProcess' -import { normalize, relative } from 'pathe' +import { basename, normalize, relative } from 'pathe' import * as vscode from 'vscode' import { version } from '../../../package.json' import { resolveVitestAPI } from './api' @@ -351,6 +351,35 @@ class VitestExtension { vscode.commands.registerCommand('vitest.openOutput', () => { log.openOuput() }), + vscode.commands.registerCommand('vitest.runRelatedTests', async (uri?: vscode.Uri) => { + const currentUri = uri || vscode.window.activeTextEditor?.document.uri + if (!currentUri) { + return + } + const fsPath = normalize(currentUri.fsPath) + if (this.testTree.getFileTestItems(fsPath).length) { + vscode.window.showWarningMessage( + `"${basename(fsPath)}" is a test file. Pick a source file to run related tests`, + ) + return + } + const promises = this.api?.processes.map(async (process) => { + const runProfile = this.runProfiles.get(`${process.id}:run`) + if (!runProfile) { + return + } + + const request = new vscode.TestRunRequest(undefined, undefined, runProfile, false, false) + const tokenSource = new vscode.CancellationTokenSource() + Object.assign(request, { related: fsPath }) + log.info( + '[COMMAND] Running tests that import', + relative(process.workspaceFolder.uri.fsPath, fsPath), + ) + await runProfile.runHandler(request, tokenSource.token) + }) + await Promise.all(promises || []) + }), vscode.commands.registerCommand( 'vitest.toggleContinuousRun', async (testItem?: vscode.TestItem) => { diff --git a/packages/extension/src/runQueue.ts b/packages/extension/src/runQueue.ts index 8ef39a1..5e1ef66 100644 --- a/packages/extension/src/runQueue.ts +++ b/packages/extension/src/runQueue.ts @@ -59,23 +59,19 @@ export class RunQueue { if (request.continuous) return this.startContinuousRun(request, token, coverage) if (!this.currentRun) { - return this.executeRun(request, token, coverage) + return this.executeRun(request, coverage) } log.verbose?.('Queueing a new test run to execute when the current one is finished.') return new Promise((resolve) => { this.pendingQueue.push({ - runTests: () => this.executeRun(request, token, coverage), + runTests: () => this.executeRun(request, coverage), resolveWithoutRunning: resolve, }) }) } - private async executeRun( - request: vscode.TestRunRequest, - token: vscode.CancellationToken, - coverage: boolean, - ) { + private async executeRun(request: vscode.TestRunRequest, coverage: boolean) { this.currentRun = (async () => { // Each "run" click creates a new process to run tests // We don't reuse the established process because it's harder to track @@ -85,6 +81,7 @@ export class RunQueue { coverage, // performance optimization to avoid creating unused projects projects: getProjectsFromRequest(request), + related: 'related' in request ? (request.related as string) : undefined, }) const runner = this.createRunner(handle, api) try { diff --git a/packages/extension/src/spawn/ws.ts b/packages/extension/src/spawn/ws.ts index 1d0379e..35b6229 100644 --- a/packages/extension/src/spawn/ws.ts +++ b/packages/extension/src/spawn/ws.ts @@ -26,6 +26,7 @@ export interface ProcessSpawnOptions { coverage?: boolean sendLog?: boolean projects?: string[] + related?: string } export function waitForWsConnection( @@ -160,6 +161,7 @@ export function onWsConnection( }, finalCoverageFileName, projectFilter: options?.projects, + related: options?.related, }, debug, coverage: options?.coverage, diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 137abd5..d23e061 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -147,6 +147,7 @@ export interface WorkerInitMetadata { } finalCoverageFileName: string projectFilter?: string[] + related?: string } export interface WorkerRunnerDebugOptions { diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts index d312e13..661fdf3 100644 --- a/packages/worker/src/index.ts +++ b/packages/worker/src/index.ts @@ -64,6 +64,7 @@ export async function initVitest( reporter: undefined, ui: false, includeTaskLocation: true, + related: meta.related ? [meta.related] : undefined, experimental: { importDurations: { limit: Infinity, -- 2.51.2 From 2c76430fc719c80786ed096f0fff36a6339aed45 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 10 Mar 2026 17:43:16 +0100 Subject: [PATCH 13/64] fix(watcher): renaming/deleting/creating folders correctly updates the test tree --- packages/extension/src/apiProcess.ts | 5 ++ packages/extension/src/testTree.ts | 7 ++ packages/extension/src/watcher.ts | 102 +++++++++++++++++++++------ 3 files changed, 92 insertions(+), 22 deletions(-) diff --git a/packages/extension/src/apiProcess.ts b/packages/extension/src/apiProcess.ts index c70521f..65461e4 100644 --- a/packages/extension/src/apiProcess.ts +++ b/packages/extension/src/apiProcess.ts @@ -33,6 +33,10 @@ export class VitestProjectConfig { return this.pkg.prefix } + get cwd() { + return this.pkg.cwd + } + get configs() { return this.projects.map((p) => p.config).filter((n) => n != null) } @@ -62,6 +66,7 @@ export class VitestProjectConfig { return metadata } + // TODO: if dir is set, this doesn't seem to work properly matchesTestGlob(project: SerializedProject, moduleId: string, source: () => string) { const relativeId = relative(project.dir || project.root, moduleId) if (pm.isMatch(relativeId, project.exclude)) { diff --git a/packages/extension/src/testTree.ts b/packages/extension/src/testTree.ts index bbb65e3..535ac09 100644 --- a/packages/extension/src/testTree.ts +++ b/packages/extension/src/testTree.ts @@ -213,6 +213,13 @@ export class TestTree extends vscode.Disposable { items?.forEach((item) => this.recursiveDelete(item)) } + public removeFolder(folderPath: string) { + const folderItem = this.folderItems.get(normalize(folderPath)) + if (folderItem) { + this.recursiveDelete(folderItem) + } + } + private recursiveDelete(item: vscode.TestItem) { if (!item.parent) return item.parent.children.delete(item.id) diff --git a/packages/extension/src/watcher.ts b/packages/extension/src/watcher.ts index a1b9328..28896c6 100644 --- a/packages/extension/src/watcher.ts +++ b/packages/extension/src/watcher.ts @@ -2,7 +2,7 @@ import type { VitestProcessAPI } from './apiProcess' import type { TransformSchemaProvider } from './schemaProvider' import type { TestTree } from './testTree' import { relative } from 'node:path' -import { normalize } from 'pathe' +import { normalize, resolve } from 'pathe' import * as vscode from 'vscode' import { getConfig } from './config' import { log } from './log' @@ -46,19 +46,26 @@ export class ExtensionWatcher extends vscode.Disposable { watcher.onDidDelete(async (uri) => { const path = normalize(uri.fsPath) - if (await this.shouldIgnoreFile(api, path, uri)) { + if (this.isCommondIgnore(path)) { return } - log.verbose?.('[VSCODE] File deleted:', this.relative(api, uri)) - this.testTree.removeFile(normalize(uri.fsPath)) + + log.verbose?.('[VSCODE] Item deleted:', this.relative(api, uri)) + this.transformSchemaProvider.emitChange(uri) + + // We don't know if it is a file or a folder + this.testTree.removeFile(path) + this.testTree.removeFolder(path) }) watcher.onDidChange(async (uri) => { const path = normalize(uri.fsPath) - if (await this.shouldIgnoreFile(api, path, uri)) { + const type = await this.getFsType(api, path, uri) + if (type !== 'file') { return } + this.transformSchemaProvider.emitChange(uri) log.verbose?.('[VSCODE] File changed:', this.relative(api, uri)) const apis = this.apisByFolder.get(folder) || [] @@ -76,18 +83,38 @@ export class ExtensionWatcher extends vscode.Disposable { watcher.onDidCreate(async (uri) => { const path = normalize(uri.fsPath) - if (await this.shouldIgnoreFile(api, path, uri)) { + const type = await this.getFsType(api, path, uri) + + if (!type) { return } - log.verbose?.('[VSCODE] File created:', this.relative(api, uri)) + + log.verbose?.('[VSCODE]', 'New', type, 'created:', this.relative(api, uri)) + const apis = this.apisByFolder.get(folder) || [] - apis.forEach((api) => { - const metadata = api.getPotentialTestFileMetadata(path) - metadata.forEach((meta) => { - this.testTree.getOrCreateFileTestItem(api, meta, path) - if (!api.getPersistentProcessMeta() && !api.isSpawningPersistentProcess) { - api.collectTests(meta.project, path) - } + const roots = apis.flatMap((api) => + // TODO: resolve should be done on the worker side + api.config.projects.map((p) => normalize(resolve(api.config.cwd, p.dir || p.root))), + ) + const files = type === 'file' ? [path] : await this.readFilesRecursively(uri, roots) + const openedFiles = vscode.workspace.textDocuments.map((d) => normalize(d.uri.fsPath)) + + files.forEach((file) => { + apis.forEach((api) => { + const metadata = api.getPotentialTestFileMetadata(file) + metadata.forEach((meta) => { + this.testTree.getOrCreateFileTestItem(api, meta, file) + + // If file is open and not a continuous run, + // Collect its tests immidetly, otherwise ignore + if ( + openedFiles.includes(file) && + !api.getPersistentProcessMeta() && + !api.isSpawningPersistentProcess + ) { + api.collectTests(meta.project, file) + } + }) }) }) }) @@ -97,15 +124,47 @@ export class ExtensionWatcher extends vscode.Disposable { return relative(api.workspaceFolder.uri.fsPath, uri.fsPath) } - private async shouldIgnoreFile(api: VitestProcessAPI, path: string, uri: vscode.Uri) { - if ( + private isCommondIgnore(path: string) { + return ( path.includes('/node_modules/') || path.includes('\\node_modules\\') || path.includes('/.git/') || path.includes('\\.git\\') || path.endsWith('.git') - ) { - return true + ) + } + + private async readFilesRecursively(uri: vscode.Uri, roots: string[]): Promise { + const dirPath = normalize(uri.fsPath) + // skip if this directory is not inside any project root and no root is inside it + if (!roots.some((root) => dirPath.startsWith(root) || root.startsWith(dirPath))) { + return [] + } + const entries = await vscode.workspace.fs.readDirectory(uri) + const files: string[] = [] + for (const [name, type] of entries) { + const childUri = vscode.Uri.joinPath(uri, name) + if ( + type === vscode.FileType.Directory || + type === (vscode.FileType.Directory | vscode.FileType.SymbolicLink) + ) { + if (this.isCommondIgnore(normalize(childUri.fsPath))) { + continue + } + files.push(...(await this.readFilesRecursively(childUri, roots))) + } else if ( + type === vscode.FileType.File || + type === (vscode.FileType.File | vscode.FileType.SymbolicLink) + ) { + files.push(normalize(childUri.fsPath)) + } + } + return files + } + + private async getFsType(api: VitestProcessAPI, path: string, uri: vscode.Uri) { + if (this.isCommondIgnore(path)) { + return null } try { const stats = await vscode.workspace.fs.stat(uri) @@ -115,12 +174,11 @@ export class ExtensionWatcher extends vscode.Disposable { // if not a symlinked file stats.type !== (vscode.FileType.File | vscode.FileType.SymbolicLink) ) { - log.verbose?.('[VSCODE]', this.relative(api, uri), 'is not a file. Ignoring.') - return true + return 'folder' } - return false + return 'file' } catch { - return true + return null } } } -- 2.51.2 From 72100ee5c1121458fc9ff695b9aff23812c38e33 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 10 Mar 2026 17:51:59 +0100 Subject: [PATCH 14/64] perf: flush watched files --- packages/extension/src/watcher.ts | 163 ++++++++++++++++++++---------- 1 file changed, 111 insertions(+), 52 deletions(-) diff --git a/packages/extension/src/watcher.ts b/packages/extension/src/watcher.ts index 28896c6..1a81785 100644 --- a/packages/extension/src/watcher.ts +++ b/packages/extension/src/watcher.ts @@ -7,9 +7,12 @@ import * as vscode from 'vscode' import { getConfig } from './config' import { log } from './log' +const DEBOUNCE_DELAY = 300 + export class ExtensionWatcher extends vscode.Disposable { private watcherByFolder = new Map() private apisByFolder = new WeakMap() + private debounceTimers = new Map>() constructor( private readonly testTree: TestTree, @@ -25,6 +28,8 @@ export class ExtensionWatcher extends vscode.Disposable { this.watcherByFolder.forEach((x) => x.dispose()) this.watcherByFolder.clear() this.apisByFolder = new WeakMap() + this.debounceTimers.forEach((timer) => clearTimeout(timer)) + this.debounceTimers.clear() } watchTestFilesInWorkspace(api: VitestProcessAPI) { @@ -44,82 +49,136 @@ export class ExtensionWatcher extends vscode.Disposable { const watcher = vscode.workspace.createFileSystemWatcher(pattern) this.watcherByFolder.set(folder, watcher) - watcher.onDidDelete(async (uri) => { + const deleteQueue = new Map() + const changeQueue = new Map() + const createQueue = new Map() + + watcher.onDidDelete((uri) => { const path = normalize(uri.fsPath) if (this.isCommondIgnore(path)) { return } - - log.verbose?.('[VSCODE] Item deleted:', this.relative(api, uri)) - - this.transformSchemaProvider.emitChange(uri) - - // We don't know if it is a file or a folder - this.testTree.removeFile(path) - this.testTree.removeFolder(path) + deleteQueue.set(path, uri) + this.scheduleFlush(`delete:${folder.name}`, () => { + const batch = new Map(deleteQueue) + deleteQueue.clear() + log.verbose?.(`[VSCODE] Flushing ${batch.size} deleted items`) + for (const [path, uri] of batch) { + this.transformSchemaProvider.emitChange(uri) + // We don't know if it is a file or a folder + this.testTree.removeFile(path) + this.testTree.removeFolder(path) + } + }) }) - watcher.onDidChange(async (uri) => { + watcher.onDidChange((uri) => { const path = normalize(uri.fsPath) - const type = await this.getFsType(api, path, uri) - if (type !== 'file') { + if (this.isCommondIgnore(path)) { return } + changeQueue.set(path, uri) + this.scheduleFlush(`change:${folder.name}`, async () => { + const batch = new Map(changeQueue) + changeQueue.clear() + log.verbose?.(`[VSCODE] Flushing ${batch.size} changed items`) + const apis = this.apisByFolder.get(folder) || [] + for (const [path, uri] of batch) { + const type = await this.getFsType(api, path, uri) + if (type !== 'file') { + continue + } + this.transformSchemaProvider.emitChange(uri) + + apis.forEach((api) => { + api.onFileChanged(path) + const fileItems = this.testTree.getFileTestItems(path) + + // Ignore changed to never opened files + if (fileItems.every((item) => item.children.size === 0 && !item.error)) { + return + } + + if (api.getPersistentProcessMeta() || api.isSpawningPersistentProcess) { + return + } - this.transformSchemaProvider.emitChange(uri) - log.verbose?.('[VSCODE] File changed:', this.relative(api, uri)) - const apis = this.apisByFolder.get(folder) || [] - apis.forEach((api) => api.onFileChanged(path)) - apis.forEach((api) => { - if (api.getPersistentProcessMeta() || api.isSpawningPersistentProcess) { - return + const metadata = api.getPotentialTestFileMetadata(path) + metadata.forEach((meta) => { + api.collectTests(meta.project, path) + }) + }) } - const metadata = api.getPotentialTestFileMetadata(path) - metadata.forEach((meta) => { - api.collectTests(meta.project, path) - }) }) }) - watcher.onDidCreate(async (uri) => { + watcher.onDidCreate((uri) => { const path = normalize(uri.fsPath) - const type = await this.getFsType(api, path, uri) - - if (!type) { + if (this.isCommondIgnore(path)) { return } + createQueue.set(path, uri) + this.scheduleFlush(`create:${folder.name}`, async () => { + const batch = new Map(createQueue) + createQueue.clear() + log.verbose?.(`[VSCODE] Flushing ${batch.size} created items`) + + const apis = this.apisByFolder.get(folder) || [] + const roots = apis.flatMap((api) => + // TODO: resolve should be done on the worker side + api.config.projects.map((p) => normalize(resolve(api.config.cwd, p.dir || p.root))), + ) + const openedFiles = vscode.workspace.textDocuments.map((d) => normalize(d.uri.fsPath)) + + const allFiles: string[] = [] + for (const [path, uri] of batch) { + const type = await this.getFsType(api, path, uri) + if (!type) { + continue + } + if (type === 'file') { + allFiles.push(path) + } else { + allFiles.push(...(await this.readFilesRecursively(uri, roots))) + } + } - log.verbose?.('[VSCODE]', 'New', type, 'created:', this.relative(api, uri)) - - const apis = this.apisByFolder.get(folder) || [] - const roots = apis.flatMap((api) => - // TODO: resolve should be done on the worker side - api.config.projects.map((p) => normalize(resolve(api.config.cwd, p.dir || p.root))), - ) - const files = type === 'file' ? [path] : await this.readFilesRecursively(uri, roots) - const openedFiles = vscode.workspace.textDocuments.map((d) => normalize(d.uri.fsPath)) - - files.forEach((file) => { - apis.forEach((api) => { - const metadata = api.getPotentialTestFileMetadata(file) - metadata.forEach((meta) => { - this.testTree.getOrCreateFileTestItem(api, meta, file) - - // If file is open and not a continuous run, - // Collect its tests immidetly, otherwise ignore - if ( - openedFiles.includes(file) && - !api.getPersistentProcessMeta() && - !api.isSpawningPersistentProcess - ) { - api.collectTests(meta.project, file) - } + allFiles.forEach((file) => { + apis.forEach((api) => { + const metadata = api.getPotentialTestFileMetadata(file) + metadata.forEach((meta) => { + this.testTree.getOrCreateFileTestItem(api, meta, file) + + // If file is open and not a continuous run, + // Collect its tests immidetly, otherwise ignore + if ( + openedFiles.includes(file) && + !api.getPersistentProcessMeta() && + !api.isSpawningPersistentProcess + ) { + api.collectTests(meta.project, file) + } + }) }) }) }) }) } + private scheduleFlush(key: string, flush: () => void) { + const existing = this.debounceTimers.get(key) + if (existing) { + clearTimeout(existing) + } + this.debounceTimers.set( + key, + setTimeout(() => { + this.debounceTimers.delete(key) + flush() + }, DEBOUNCE_DELAY), + ) + } + private relative(api: VitestProcessAPI, uri: vscode.Uri) { return relative(api.workspaceFolder.uri.fsPath, uri.fsPath) } -- 2.51.2 From ed29e0821e756f7e7814612fdea953e48ef80faf Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 10 Mar 2026 17:55:04 +0100 Subject: [PATCH 15/64] chore: release v1.48.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0e5603c..a2e50c7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "explorer", "displayName": "Vitest", - "version": "1.46.0", + "version": "1.48.0", "description": "A Vite-native testing framework. It's fast!", "categories": [ "Testing" -- 2.51.2 From 64bb4f933267c8b22c3e297283f08c4beee61908 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 10 Mar 2026 20:40:09 +0100 Subject: [PATCH 16/64] perf: batch watcher events, fix order (#750) --- packages/extension/src/extension.ts | 8 +- packages/extension/src/schemaProvider.ts | 1 + packages/extension/src/spawn/ws.ts | 8 +- packages/extension/src/testTree.ts | 17 +++ packages/extension/src/watcher.ts | 174 ++++++++++++++--------- test/e2e/runner.test.ts | 27 +++- test/e2e/utils/tester.ts | 10 ++ 7 files changed, 171 insertions(+), 74 deletions(-) diff --git a/packages/extension/src/extension.ts b/packages/extension/src/extension.ts index b2ddc5e..0d87f22 100644 --- a/packages/extension/src/extension.ts +++ b/packages/extension/src/extension.ts @@ -331,12 +331,6 @@ class VitestExtension { this.disposables = [ vscode.workspace.onDidChangeConfiguration((event) => { const configName = reloadConfigNames.find((x) => event.affectsConfiguration(x)) - if ( - event.affectsConfiguration('vitest.runtime') || - event.affectsConfiguration('deno.enabled') - ) { - clearCachedRuntime() - } if (configName) { this.defineTestProfiles(false).catch((error) => { log.error('[API]', `Failed to reload Vitest after "${configName}" has changed`, error) @@ -511,7 +505,7 @@ class VitestExtension { } catch (err) { log.error(err) vscode.window.showErrorMessage( - `Vitest: The file was not processed by Vite yet. Try running the tests first${options.length > 1 ? ' or select a different environment' : ''}.`, + `Vitest: The file was not processed by Vite yet. Try starting the continuous run first${options.length > 1 ? ' or select a different environment' : ''}.`, ) } }, diff --git a/packages/extension/src/schemaProvider.ts b/packages/extension/src/schemaProvider.ts index 6fe2776..e1a4fe8 100644 --- a/packages/extension/src/schemaProvider.ts +++ b/packages/extension/src/schemaProvider.ts @@ -21,6 +21,7 @@ export class TransformSchemaProvider this.disposables.push(this._onDidChangeEvents) } + // This is called by vscode to clear the internal cache public onDidChange = this._onDidChangeEvents.event public emitChange(uri: vscode.Uri) { diff --git a/packages/extension/src/spawn/ws.ts b/packages/extension/src/spawn/ws.ts index 35b6229..94e60bd 100644 --- a/packages/extension/src/spawn/ws.ts +++ b/packages/extension/src/spawn/ws.ts @@ -17,6 +17,7 @@ import { } from '../constants' import { log } from '../log' import { createVitestRpc } from './rpc' +import { resolve } from 'pathe' export type WsConnectionMetadata = Omit & { ws: WebSocket @@ -94,7 +95,12 @@ export function onWsConnection( rpc: api, workspaceSource: message.workspaceSource, handlers, - projects: message.projects, + projects: message.projects.map((p) => { + if (p.dir) { + p.dir = resolve(pkg.cwd, p.dir) + } + return p + }), ws, pkg, async dispose() { diff --git a/packages/extension/src/testTree.ts b/packages/extension/src/testTree.ts index 535ac09..7121bfe 100644 --- a/packages/extension/src/testTree.ts +++ b/packages/extension/src/testTree.ts @@ -222,6 +222,10 @@ export class TestTree extends vscode.Disposable { private recursiveDelete(item: vscode.TestItem) { if (!item.parent) return + + // Clean up children first so no stale entries remain in Maps + this.cleanupChildren(item) + item.parent.children.delete(item.id) this.flatTestItems.delete(item.id) const data = getTestData(item) @@ -235,6 +239,19 @@ export class TestTree extends vscode.Disposable { if (!item.parent.children.size) this.recursiveDelete(item.parent) } + private cleanupChildren(item: vscode.TestItem) { + item.children.forEach((child) => { + this.cleanupChildren(child) + this.flatTestItems.delete(child.id) + const data = getTestData(child) + if (data instanceof TestFile) { + this.testItemsByFile.delete(data.filepath) + this.fileItems.delete(child.id) + } + if (data instanceof TestFolder) this.folderItems.delete(child.id) + }) + } + public getAPIFromTestItem(testItem: vscode.TestItem) { return getAPIFromTestItem(testItem) } diff --git a/packages/extension/src/watcher.ts b/packages/extension/src/watcher.ts index 1a81785..32bf034 100644 --- a/packages/extension/src/watcher.ts +++ b/packages/extension/src/watcher.ts @@ -53,23 +53,97 @@ export class ExtensionWatcher extends vscode.Disposable { const changeQueue = new Map() const createQueue = new Map() + const scheduleCreateDeleteFlush = () => { + this.scheduleFlush(`create-delete:${folder.index}`, async () => { + const deleteBatch = new Map(deleteQueue) + const createBatch = new Map(createQueue) + deleteQueue.clear() + createQueue.clear() + + // Process creates first so new items exist in the tree + // before deletes remove old ones (prevents parent folders + // from being orphaned during renames) + if (createBatch.size) { + log.verbose?.(`[VSCODE] Flushing ${createBatch.size} created items`) + + const apis = this.apisByFolder.get(folder) || [] + const roots = apis.flatMap((api) => + api.config.projects.map((p) => normalize(p.dir || p.root)), + ) + const openedFiles = vscode.workspace.textDocuments.map((d) => normalize(d.uri.fsPath)) + + const entries = [...createBatch.entries()] + const types = await Promise.allSettled( + entries.map(([path, uri]) => this.getFsType(path, uri)), + ) + + const files: string[] = [] + const folders: [string, vscode.Uri][] = [] + for (let i = 0; i < entries.length; i++) { + const result = types[i] + if (result.status !== 'fulfilled' || !result.value) { + continue + } + if (result.value === 'file') { + files.push(entries[i][0]) + } else { + folders.push(entries[i]) + } + } + + const folderFiles = await Promise.allSettled( + folders.map(([, uri]) => this.readFilesRecursively(uri, roots)), + ) + + const seen = new Set(files) + for (const result of folderFiles) { + if (result.status !== 'fulfilled') { + continue + } + for (const file of result.value) { + seen.add(file) + } + } + + seen.forEach((file) => { + apis.forEach((api) => { + const metadata = api.getPotentialTestFileMetadata(file) + metadata.forEach((meta) => { + this.testTree.getOrCreateFileTestItem(api, meta, file) + + // If file is open and not a continuous run, + // Collect its tests immidetly, otherwise ignore + if ( + openedFiles.includes(file) && + !api.getPersistentProcessMeta() && + !api.isSpawningPersistentProcess + ) { + api.collectTests(meta.project, file) + } + }) + }) + }) + } + + if (deleteBatch.size) { + log.verbose?.(`[VSCODE] Flushing ${deleteBatch.size} deleted items`) + for (const [path, uri] of deleteBatch) { + this.transformSchemaProvider.emitChange(uri) + // We don't know if it was a file or a folder + this.testTree.removeFile(path) + this.testTree.removeFolder(path) + } + } + }) + } + watcher.onDidDelete((uri) => { const path = normalize(uri.fsPath) if (this.isCommondIgnore(path)) { return } deleteQueue.set(path, uri) - this.scheduleFlush(`delete:${folder.name}`, () => { - const batch = new Map(deleteQueue) - deleteQueue.clear() - log.verbose?.(`[VSCODE] Flushing ${batch.size} deleted items`) - for (const [path, uri] of batch) { - this.transformSchemaProvider.emitChange(uri) - // We don't know if it is a file or a folder - this.testTree.removeFile(path) - this.testTree.removeFolder(path) - } - }) + scheduleCreateDeleteFlush() }) watcher.onDidChange((uri) => { @@ -78,16 +152,23 @@ export class ExtensionWatcher extends vscode.Disposable { return } changeQueue.set(path, uri) - this.scheduleFlush(`change:${folder.name}`, async () => { + this.scheduleFlush(`change:${folder.index}`, async () => { const batch = new Map(changeQueue) changeQueue.clear() log.verbose?.(`[VSCODE] Flushing ${batch.size} changed items`) const apis = this.apisByFolder.get(folder) || [] - for (const [path, uri] of batch) { - const type = await this.getFsType(api, path, uri) - if (type !== 'file') { + + const entries = [...batch.entries()] + const types = await Promise.allSettled( + entries.map(([path, uri]) => this.getFsType(path, uri)), + ) + + for (let i = 0; i < entries.length; i++) { + const result = types[i] + if (result.status !== 'fulfilled' || result.value !== 'file') { continue } + const [path, uri] = entries[i] this.transformSchemaProvider.emitChange(uri) apis.forEach((api) => { @@ -99,6 +180,7 @@ export class ExtensionWatcher extends vscode.Disposable { return } + // If runner is persistent, the tests will be collected at runtime if (api.getPersistentProcessMeta() || api.isSpawningPersistentProcess) { return } @@ -118,50 +200,7 @@ export class ExtensionWatcher extends vscode.Disposable { return } createQueue.set(path, uri) - this.scheduleFlush(`create:${folder.name}`, async () => { - const batch = new Map(createQueue) - createQueue.clear() - log.verbose?.(`[VSCODE] Flushing ${batch.size} created items`) - - const apis = this.apisByFolder.get(folder) || [] - const roots = apis.flatMap((api) => - // TODO: resolve should be done on the worker side - api.config.projects.map((p) => normalize(resolve(api.config.cwd, p.dir || p.root))), - ) - const openedFiles = vscode.workspace.textDocuments.map((d) => normalize(d.uri.fsPath)) - - const allFiles: string[] = [] - for (const [path, uri] of batch) { - const type = await this.getFsType(api, path, uri) - if (!type) { - continue - } - if (type === 'file') { - allFiles.push(path) - } else { - allFiles.push(...(await this.readFilesRecursively(uri, roots))) - } - } - - allFiles.forEach((file) => { - apis.forEach((api) => { - const metadata = api.getPotentialTestFileMetadata(file) - metadata.forEach((meta) => { - this.testTree.getOrCreateFileTestItem(api, meta, file) - - // If file is open and not a continuous run, - // Collect its tests immidetly, otherwise ignore - if ( - openedFiles.includes(file) && - !api.getPersistentProcessMeta() && - !api.isSpawningPersistentProcess - ) { - api.collectTests(meta.project, file) - } - }) - }) - }) - }) + scheduleCreateDeleteFlush() }) } @@ -179,10 +218,6 @@ export class ExtensionWatcher extends vscode.Disposable { ) } - private relative(api: VitestProcessAPI, uri: vscode.Uri) { - return relative(api.workspaceFolder.uri.fsPath, uri.fsPath) - } - private isCommondIgnore(path: string) { return ( path.includes('/node_modules/') || @@ -201,6 +236,7 @@ export class ExtensionWatcher extends vscode.Disposable { } const entries = await vscode.workspace.fs.readDirectory(uri) const files: string[] = [] + const subdirs: vscode.Uri[] = [] for (const [name, type] of entries) { const childUri = vscode.Uri.joinPath(uri, name) if ( @@ -210,7 +246,7 @@ export class ExtensionWatcher extends vscode.Disposable { if (this.isCommondIgnore(normalize(childUri.fsPath))) { continue } - files.push(...(await this.readFilesRecursively(childUri, roots))) + subdirs.push(childUri) } else if ( type === vscode.FileType.File || type === (vscode.FileType.File | vscode.FileType.SymbolicLink) @@ -218,10 +254,18 @@ export class ExtensionWatcher extends vscode.Disposable { files.push(normalize(childUri.fsPath)) } } + const results = await Promise.allSettled( + subdirs.map((child) => this.readFilesRecursively(child, roots)), + ) + for (const result of results) { + if (result.status === 'fulfilled') { + files.push(...result.value) + } + } return files } - private async getFsType(api: VitestProcessAPI, path: string, uri: vscode.Uri) { + private async getFsType(path: string, uri: vscode.Uri) { if (this.isCommondIgnore(path)) { return null } diff --git a/test/e2e/runner.test.ts b/test/e2e/runner.test.ts index 8f4f09f..5066c21 100644 --- a/test/e2e/runner.test.ts +++ b/test/e2e/runner.test.ts @@ -2,7 +2,7 @@ import { readFileSync, rmSync } from 'node:fs' import { beforeAll, beforeEach, describe, onTestFailed } from 'vitest' import { expect } from '@playwright/test' import { test } from './utils/helper' -import { editFile } from './utils/tester' +import { editFile, renameFile } from './utils/tester' // Vitst extension doesn't work with CI flag beforeAll(() => { @@ -251,3 +251,28 @@ describe('continuous testing', () => { expect(errors).toEqual(['1000 != 2']) }) }) + +test('renaming a folder back preserves test items', async ({ launch }) => { + const { tester } = await launch({ + workspacePath: './samples/basic-v4', + }) + + await tester.tree.expand('test/deep/deeper') + + const deepTest = tester.tree.getFileItem('deep.test.ts') + await expect(deepTest.locator).toBeVisible() + + // Rename deeper -> deeperer + renameFile('samples/basic-v4/test/deep/deeper', 'samples/basic-v4/test/deep/deeperer') + + await tester.tree.expand('test/deep/deeperer') + const renamedTest = tester.tree.getFileItem('deep.test.ts') + await expect(renamedTest.locator).toBeVisible() + + // Rename back deeperer -> deeper + renameFile('samples/basic-v4/test/deep/deeperer', 'samples/basic-v4/test/deep/deeper') + + await tester.tree.expand('test/deep/deeper') + const restoredTest = tester.tree.getFileItem('deep.test.ts') + await expect(restoredTest.locator).toBeVisible() +}) diff --git a/test/e2e/utils/tester.ts b/test/e2e/utils/tester.ts index 31b315b..1558d81 100644 --- a/test/e2e/utils/tester.ts +++ b/test/e2e/utils/tester.ts @@ -139,6 +139,7 @@ export class TesterTestItem { const originalFiles = new Map() const createdFiles = new Set() +const renamedPaths = new Map() afterEach(() => { originalFiles.forEach((content, file) => { fs.writeFileSync(file, content, 'utf-8') @@ -146,8 +147,12 @@ afterEach(() => { createdFiles.forEach((file) => { if (fs.existsSync(file)) fs.unlinkSync(file) }) + renamedPaths.forEach((originalPath, currentPath) => { + if (fs.existsSync(currentPath)) fs.renameSync(currentPath, originalPath) + }) originalFiles.clear() createdFiles.clear() + renamedPaths.clear() }) export function editFile(file: string, callback: (content: string) => string) { @@ -155,3 +160,8 @@ export function editFile(file: string, callback: (content: string) => string) { if (!originalFiles.has(file)) originalFiles.set(file, content) fs.writeFileSync(file, callback(content), 'utf-8') } + +export function renameFile(from: string, to: string) { + if (!renamedPaths.has(from)) renamedPaths.set(to, from) + fs.renameSync(from, to) +} -- 2.51.2 From a767ffce83ebd621f9867d2b8725f2939a2113d6 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 10 Mar 2026 20:51:49 +0100 Subject: [PATCH 17/64] test: add deno test (#751) --- .github/workflows/ci.yml | 8 ++++++++ .vscode/launch.json | 11 +++++++++++ pnpm-lock.yaml | 6 ++++++ samples/deno/.vscode/settings.json | 3 +++ samples/deno/package.json | 12 ++++++++++++ samples/deno/test/deno.test.ts | 5 +++++ samples/deno/vitest.config.ts | 3 +++ test/e2e/runner.test.ts | 22 ++++++++++++++++++++++ 8 files changed, 70 insertions(+) create mode 100644 samples/deno/.vscode/settings.json create mode 100644 samples/deno/package.json create mode 100644 samples/deno/test/deno.test.ts create mode 100644 samples/deno/vitest.config.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c9b3b80..f463447 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,10 @@ jobs: node-version: ${{ matrix.node-version }} cache: pnpm + - uses: denoland/setup-deno@v2 + with: + deno-version: v2.x + - run: pnpm install --frozen-lockfile - run: pnpm build @@ -76,6 +80,10 @@ jobs: node-version: ${{ matrix.node-version }} cache: pnpm + - uses: denoland/setup-deno@v2 + with: + deno-version: v2.x + - name: vitest 3.2 run: node ./scripts/lower-vitest-version.js diff --git a/.vscode/launch.json b/.vscode/launch.json index 16d6565..f71f6a1 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -44,6 +44,17 @@ ], "outFiles": ["${workspaceFolder}/dist/**/*.js"] }, + { + "name": "Run Extension Deno Sample", + "type": "extensionHost", + "request": "launch", + "args": [ + "--disable-extensions", + "--extensionDevelopmentPath=${workspaceFolder}", + "${workspaceFolder}/samples/deno" + ], + "outFiles": ["${workspaceFolder}/dist/**/*.js"] + }, { "name": "Run Extension Imba Sample", "type": "extensionHost", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0357b01..a0f43ea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -416,6 +416,12 @@ importers: specifier: catalog:latest version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + samples/deno: + devDependencies: + vitest: + specifier: catalog:latest + version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + samples/e2e: devDependencies: vitest: diff --git a/samples/deno/.vscode/settings.json b/samples/deno/.vscode/settings.json new file mode 100644 index 0000000..87b087b --- /dev/null +++ b/samples/deno/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "vitest.runtime": "deno" +} diff --git a/samples/deno/package.json b/samples/deno/package.json new file mode 100644 index 0000000..bbb02cf --- /dev/null +++ b/samples/deno/package.json @@ -0,0 +1,12 @@ +{ + "name": "@vitest/vscode-sample-deno", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "test": "vitest" + }, + "devDependencies": { + "vitest": "catalog:latest" + } +} diff --git a/samples/deno/test/deno.test.ts b/samples/deno/test/deno.test.ts new file mode 100644 index 0000000..ee4aecd --- /dev/null +++ b/samples/deno/test/deno.test.ts @@ -0,0 +1,5 @@ +import { expect, it } from 'vitest' + +it('deno-exists', () => { + expect('Deno' in globalThis).toBe(true) +}) diff --git a/samples/deno/vitest.config.ts b/samples/deno/vitest.config.ts new file mode 100644 index 0000000..abed6b2 --- /dev/null +++ b/samples/deno/vitest.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({}) diff --git a/test/e2e/runner.test.ts b/test/e2e/runner.test.ts index 5066c21..34e41cb 100644 --- a/test/e2e/runner.test.ts +++ b/test/e2e/runner.test.ts @@ -252,6 +252,28 @@ describe('continuous testing', () => { }) }) +test('deno runtime', async ({ launch }) => { + const { tester } = await launch({ + workspacePath: './samples/deno', + }) + + await tester.tree.expand('test') + await tester.tree.expand('test/deno.test.ts') + + const denoTest = tester.tree.getFileItem('deno.test.ts') + await expect(denoTest).toHaveTests({ + 'deno-exists': 'waiting', + }) + + await tester.runAllTests() + + await expect(tester.tree.getResultsLocator()).toHaveText('1/1') + await expect(denoTest).toHaveState('passed') + await expect(denoTest).toHaveTests({ + 'deno-exists': 'passed', + }) +}) + test('renaming a folder back preserves test items', async ({ launch }) => { const { tester } = await launch({ workspacePath: './samples/basic-v4', -- 2.51.2 From c303626e7e79ce793b2cb713be53fa12674205e5 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 10 Mar 2026 20:52:09 +0100 Subject: [PATCH 18/64] test: add more watch mode tests --- test/e2e/runner.test.ts | 45 +++++++++++++++++++++++++++++++++++++++- test/e2e/utils/tester.ts | 11 ++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/test/e2e/runner.test.ts b/test/e2e/runner.test.ts index 34e41cb..cfb41de 100644 --- a/test/e2e/runner.test.ts +++ b/test/e2e/runner.test.ts @@ -2,7 +2,7 @@ import { readFileSync, rmSync } from 'node:fs' import { beforeAll, beforeEach, describe, onTestFailed } from 'vitest' import { expect } from '@playwright/test' import { test } from './utils/helper' -import { editFile, renameFile } from './utils/tester' +import { addFile, deleteFile, editFile, renameFile } from './utils/tester' // Vitst extension doesn't work with CI flag beforeAll(() => { @@ -274,6 +274,49 @@ test('deno runtime', async ({ launch }) => { }) }) +test('adding and deleting files updates the tree', async ({ launch }) => { + const sample = 'samples/basic-v4' + + const { tester } = await launch({ + workspacePath: `./${sample}`, + }) + + await tester.tree.expand('test/deep/deeper') + + const deepTest = tester.tree.getFileItem('deep.test.ts') + await expect(deepTest.locator).toBeVisible() + + // also expand a parallel branch so we can verify it stays + await tester.tree.expand('test/add.test.ts') + const addTest = tester.tree.getFileItem('add.test.ts') + await expect(addTest.locator).toBeVisible() + + // add a second file in deeper + addFile( + `${sample}/test/deep/deeper/second.test.ts`, + `import { expect, it } from 'vitest'\n\nit('second', () => {\n expect(2).toBe(2)\n})\n`, + ) + + await tester.tree.expand('test/deep/deeper') + const secondTest = tester.tree.getFileItem('second.test.ts') + await expect(secondTest.locator).toBeVisible() + await expect(deepTest.locator).toBeVisible() + + // delete the first file — second file should remain, folder stays + deleteFile(`${sample}/test/deep/deeper/deep.test.ts`) + + await expect(deepTest.locator).not.toBeVisible() + await expect(secondTest.locator).toBeVisible() + + // delete the second file — folder should disappear from the tree + deleteFile(`${sample}/test/deep/deeper/second.test.ts`) + + await expect(secondTest.locator).not.toBeVisible() + + // parallel tree branch is untouched + await expect(addTest.locator).toBeVisible() +}) + test('renaming a folder back preserves test items', async ({ launch }) => { const { tester } = await launch({ workspacePath: './samples/basic-v4', diff --git a/test/e2e/utils/tester.ts b/test/e2e/utils/tester.ts index 1558d81..42fa68a 100644 --- a/test/e2e/utils/tester.ts +++ b/test/e2e/utils/tester.ts @@ -155,6 +155,17 @@ afterEach(() => { renamedPaths.clear() }) +export function addFile(file: string, content: string) { + createdFiles.add(file) + fs.writeFileSync(file, content, 'utf-8') +} + +export function deleteFile(file: string) { + const content = fs.readFileSync(file, 'utf-8') + if (!originalFiles.has(file)) originalFiles.set(file, content) + fs.unlinkSync(file) +} + export function editFile(file: string, callback: (content: string) => string) { const content = fs.readFileSync(file, 'utf-8') if (!originalFiles.has(file)) originalFiles.set(file, content) -- 2.51.2 From 4cf0d2b434993af86e07c9c4e3b77900da49b0bb Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 10 Mar 2026 20:52:33 +0100 Subject: [PATCH 19/64] chore: release v1.48.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a2e50c7..5da7562 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "explorer", "displayName": "Vitest", - "version": "1.48.0", + "version": "1.48.1", "description": "A Vite-native testing framework. It's fast!", "categories": [ "Testing" -- 2.51.2 From e8b276d250110b6be2ff99b562357ce00d8f6917 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 11 Mar 2026 15:21:49 +0100 Subject: [PATCH 20/64] fix: correctly identify concurrent test during static analysis in older Vitest versions (#753) --- packages/worker-legacy/src/collect.ts | 80 +++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 10 deletions(-) diff --git a/packages/worker-legacy/src/collect.ts b/packages/worker-legacy/src/collect.ts index 449b7ef..9e2815e 100644 --- a/packages/worker-legacy/src/collect.ts +++ b/packages/worker-legacy/src/collect.ts @@ -32,6 +32,8 @@ interface LocalCallDefinition { mode: 'run' | 'skip' | 'only' | 'todo' | 'queued' task: ParsedSuite | ParsedFile | ParsedTest dynamic: boolean + concurrent: boolean + sequential: boolean } export interface FileInformation { @@ -107,8 +109,8 @@ export function astParseFile(filepath: string, code: string) { ) { return getName(callee.property) } - // call as `__vite_ssr__.test.skip()` - return getName(callee.object?.property) + // call as `__vite_ssr__.test.skip()` or `describe.concurrent.each()` + return getName(callee.object) } // unwrap (0, ...) if (callee.type === 'SequenceExpression' && callee.expressions.length === 2) { @@ -120,6 +122,29 @@ export function astParseFile(filepath: string, code: string) { return null } + const getProperties = (callee: any): string[] => { + if (!callee) { + return [] + } + if (callee.type === 'Identifier') { + return [] + } + if (callee.type === 'CallExpression') { + return getProperties(callee.callee) + } + if (callee.type === 'TaggedTemplateExpression') { + return getProperties(callee.tag) + } + if (callee.type === 'MemberExpression') { + const props = getProperties(callee.object) + if (callee.property?.name) { + props.push(callee.property.name) + } + return props + } + return [] + } + walkAst(ast as any, { CallExpression(node) { const { callee } = node as any @@ -131,12 +156,23 @@ export function astParseFile(filepath: string, code: string) { verbose?.(`Skipping ${name} (unknown call)`) return } + const properties = getProperties(callee) const property = callee?.property?.name - let mode = !property || property === name ? 'run' : property - // they will be picked up in the next iteration - if (['each', 'for', 'skipIf', 'runIf', 'extend', 'scoped'].includes(mode)) { + // intermediate calls like .each(), .for() will be picked up in the next iteration + if (property && ['each', 'for', 'skipIf', 'runIf', 'extend', 'scoped'].includes(property)) { return } + // derive mode from the full chain (handles any order like .skip.concurrent or .concurrent.skip) + let mode: 'run' | 'skip' | 'only' | 'todo' = 'run' + for (const prop of properties) { + if (prop === 'skip' || prop === 'only' || prop === 'todo') { + mode = prop + } else if (['skipIf', 'runIf'].includes(prop)) { + mode = 'skip' + } + } + let isConcurrent = properties.includes('concurrent') + let isSequential = properties.includes('sequential') let start: number const end = node.end @@ -177,11 +213,6 @@ export function astParseFile(filepath: string, code: string) { // Vitest module mocker injects these .replace(/__vi_import_\d+__\./g, '') - // cannot statically analyze, so we always skip it - if (mode === 'skipIf' || mode === 'runIf') { - mode = 'skip' - } - const parentCalleeName = typeof callee?.callee === 'object' && callee?.callee.type === 'MemberExpression' && @@ -192,6 +223,26 @@ export function astParseFile(filepath: string, code: string) { isDynamicEach = property === 'each' || property === 'for' } + // Extract options from the second argument if it's an options object + const secondArg = node.arguments?.[1] + if (secondArg?.type === 'ObjectExpression') { + for (const prop of (secondArg.properties || []) as any[]) { + if (prop.type !== 'Property' || prop.key?.type !== 'Identifier') { + continue + } + const keyName = prop.key.name + if (prop.value?.type === 'Literal' && prop.value.value === true) { + if (keyName === 'skip' || keyName === 'only' || keyName === 'todo') { + mode = keyName + } else if (keyName === 'concurrent') { + isConcurrent = true + } else if (keyName === 'sequential') { + isSequential = true + } + } + } + } + debug?.('Found', name, message, `(${mode})`) definitions.push({ start, @@ -201,6 +252,8 @@ export function astParseFile(filepath: string, code: string) { mode, task: null as any, dynamic: isDynamicEach, + concurrent: isConcurrent, + sequential: isSequential, } satisfies LocalCallDefinition) }, }) @@ -336,6 +389,11 @@ export function createFileTask( `${definition.start}`, ) } + // resolve concurrent/sequential: sequential cancels inherited concurrent + const concurrent = definition.sequential + ? undefined + : definition.concurrent || (latestSuite as any).concurrent || undefined + if (definition.type === 'suite') { const task: ParsedSuite = { type: definition.type, @@ -344,6 +402,7 @@ export function createFileTask( file, tasks: [], mode, + concurrent, name: definition.name, end: definition.end, start: definition.start, @@ -362,6 +421,7 @@ export function createFileTask( suite: latestSuite, file, mode, + concurrent, context: {} as any, // not used on the server name: definition.name, end: definition.end, -- 2.51.2 From 34d5fabdc95186100369c1f1233f328ebeebbe59 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 11 Mar 2026 15:47:30 +0100 Subject: [PATCH 21/64] test: add projects override test (#754) --- pnpm-lock.yaml | 9 ++++++++ samples/projects/package.json | 13 +++++++++++ samples/projects/test/basic.test.ts | 5 ++++ samples/projects/vitest.config.ts | 20 ++++++++++++++++ test/e2e/runner.test.ts | 36 +++++++++++++++++++++++++++++ test/e2e/utils/tester.ts | 2 +- 6 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 samples/projects/package.json create mode 100644 samples/projects/test/basic.test.ts create mode 100644 samples/projects/vitest.config.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0f43ea..ca8160d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -538,6 +538,15 @@ importers: specifier: catalog:latest version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + samples/projects: + devDependencies: + happy-dom: + specifier: ^15.7.4 + version: 15.11.7 + vitest: + specifier: catalog:latest + version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + samples/readme: devDependencies: vitest: diff --git a/samples/projects/package.json b/samples/projects/package.json new file mode 100644 index 0000000..c60f6e2 --- /dev/null +++ b/samples/projects/package.json @@ -0,0 +1,13 @@ +{ + "name": "@vitest/vscode-sample-projects", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "test": "vitest" + }, + "devDependencies": { + "happy-dom": "^15.7.4", + "vitest": "catalog:latest" + } +} diff --git a/samples/projects/test/basic.test.ts b/samples/projects/test/basic.test.ts new file mode 100644 index 0000000..9e641bb --- /dev/null +++ b/samples/projects/test/basic.test.ts @@ -0,0 +1,5 @@ +import { expect, test } from 'vitest' + +test('check', () => { + expect(1 + 1).toBe(2) +}) diff --git a/samples/projects/vitest.config.ts b/samples/projects/vitest.config.ts new file mode 100644 index 0000000..33da484 --- /dev/null +++ b/samples/projects/vitest.config.ts @@ -0,0 +1,20 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + projects: [ + { + test: { + name: 'node', + environment: 'node', + }, + }, + { + test: { + name: 'happy-dom', + environment: 'happy-dom', + }, + }, + ], + }, +}) diff --git a/test/e2e/runner.test.ts b/test/e2e/runner.test.ts index cfb41de..ed362c7 100644 --- a/test/e2e/runner.test.ts +++ b/test/e2e/runner.test.ts @@ -88,6 +88,42 @@ test('workspaces', async ({ launch }) => { await expect(tester.tree.getResultsLocator()).toHaveText('4/4') }) +test('running a project does not update other projects', async ({ launch }) => { + const { tester } = await launch({ + workspacePath: './samples/projects', + }) + + await tester.tree.expand('test') + + const nodeTest = tester.tree.getFileItem('basic.test.ts', 'node') + const happyDomTest = tester.tree.getFileItem('basic.test.ts', 'happy-dom') + + await expect(nodeTest.locator).toBeVisible() + await expect(happyDomTest.locator).toBeVisible() + + await tester.tree.expand('test/basic.test.ts [node]') + await tester.tree.expand('test/basic.test.ts [happy-dom]') + + await expect(nodeTest).toHaveTests({ + 'check|4': 'waiting', + }) + await expect(happyDomTest).toHaveTests({ + 'check|2': 'waiting', + }) + + await nodeTest.run() + + await expect(tester.tree.getResultsLocator()).toHaveText('1/1') + await expect(nodeTest).toHaveState('passed') + await expect(nodeTest).toHaveTests({ + check: 'passed', + }) + // happy-dom project should remain untouched + await expect(happyDomTest).toHaveTests({ + check: 'waiting', + }) +}) + test('custom imba language', async ({ launch }) => { const { tester } = await launch({ workspacePath: './samples/imba', diff --git a/test/e2e/utils/tester.ts b/test/e2e/utils/tester.ts index 42fa68a..9f77fbd 100644 --- a/test/e2e/utils/tester.ts +++ b/test/e2e/utils/tester.ts @@ -8,7 +8,7 @@ export class VSCodeTester { public errors: TesterErrorOutput constructor( - private page: Page, + public page: Page, private logPath: string, ) { this.tree = new TesterTree(page, logPath) -- 2.51.2 From 0059d7a188759d8e42e49c0c96005a16b7717b9d Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 11 Mar 2026 15:58:33 +0100 Subject: [PATCH 22/64] test: add test for editing imported files (#755) --- test/e2e/runner.test.ts | 51 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/test/e2e/runner.test.ts b/test/e2e/runner.test.ts index ed362c7..9308695 100644 --- a/test/e2e/runner.test.ts +++ b/test/e2e/runner.test.ts @@ -286,6 +286,57 @@ describe('continuous testing', () => { expect(errors).toEqual(['1000 != 2']) }) + + test('editing an imported file only reruns affected tests', async ({ launch }) => { + const { tester } = await launch({ + workspacePath: './samples/continuous', + }) + + await tester.tree.expand('test/imports-divide.test.ts') + await tester.tree.expand('test/imports-multiply.test.ts') + await tester.tree.expand('test/no-import.test.ts') + + const divideTest = tester.tree.getFileItem('imports-divide.test.ts') + const multiplyTest = tester.tree.getFileItem('imports-multiply.test.ts') + const noImportTest = tester.tree.getFileItem('no-import.test.ts') + + await expect(divideTest).toHaveTests({ divide: 'waiting' }) + await expect(multiplyTest).toHaveTests({ multiply: 'waiting' }) + await expect(noImportTest).toHaveTests({ + multiply: 'waiting', + divide: 'waiting', + }) + + await divideTest.toggleContinuousRun() + await multiplyTest.toggleContinuousRun() + await noImportTest.toggleContinuousRun() + + // trigger initial run by touching each test file + editFile('samples/continuous/test/imports-divide.test.ts', (content) => `${content}\n`) + await expect(divideTest).toHaveTests({ divide: 'passed' }) + + editFile('samples/continuous/test/imports-multiply.test.ts', (content) => `${content}\n`) + await expect(multiplyTest).toHaveTests({ multiply: 'passed' }) + + editFile('samples/continuous/test/no-import.test.ts', (content) => `${content}\n`) + await expect(noImportTest).toHaveTests({ + multiply: 'passed', + divide: 'passed', + }) + + // break calculator.ts — only importing tests should rerun and fail + editFile('samples/continuous/src/calculator.ts', (content) => + content.replace('a * b', '0').replace('a / b', '0'), + ) + + await expect(divideTest).toHaveTests({ divide: 'failed' }) + await expect(multiplyTest).toHaveTests({ multiply: 'failed' }) + // no-import.test.ts should remain passed — it doesn't import calculator + await expect(noImportTest).toHaveTests({ + multiply: 'passed', + divide: 'passed', + }) + }) }) test('deno runtime', async ({ launch }) => { -- 2.51.2 From f329ed6221e0344be155b138b516d5db4c85a9b1 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Wed, 11 Mar 2026 16:29:14 +0100 Subject: [PATCH 23/64] fix: reduce the padding in inlined console logs --- packages/extension/src/inlineConsoleLog.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/inlineConsoleLog.ts b/packages/extension/src/inlineConsoleLog.ts index 8eb7fef..08f8bcc 100644 --- a/packages/extension/src/inlineConsoleLog.ts +++ b/packages/extension/src/inlineConsoleLog.ts @@ -144,7 +144,7 @@ export class InlineConsoleLogManager extends vscode.Disposable { ) md.appendText('\n') } - return md.appendText(noAnsi[index]) + return md.appendCodeblock(noAnsi[index]) }) const lineRange = editor.document.lineAt(line).range -- 2.51.2 From 8d901810b97aaff57acc51c722caa215789f15c7 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Sat, 14 Mar 2026 20:54:20 +0100 Subject: [PATCH 24/64] docs: cleanup readme (#758) --- README.md | 9 ++++----- package.json | 9 +-------- packages/extension/src/config.ts | 1 - 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index cc76863..b6e25c3 100644 --- a/README.md +++ b/README.md @@ -101,9 +101,8 @@ These options are resolved relative to the [workspace file](https://code.visuals `process.env` - `vitest.debugNodeEnv`: Environment passed to the runner process in addition to `process.env` and `vitest.nodeEnv` when debugging tests - `vitest.debugExclude`: Excludes files matching specified glob patterns from debugging. Default: - `["/**"]` -- `vitest.debugOutFiles`: If source maps are enabled, these glob patterns specify the generated JavaScript files. If a pattern starts with `!` the files are excluded. If not specified, the generated code is expected in the same directory as its source. Default: `["${workspaceFolder}/**/*.(m|c|)js", "!**/node_modules/**"]` -- `vitest.maximumConfigs`: The maximum amount of configs that Vitest extension can load. If exceeded, the extension will show a warning suggesting to use a workspace config file. Default: `5` + `["/**", "vitest/dist/**"]` +- `vitest.debugOutFiles`: If source maps are enabled, these glob patterns specify the generated JavaScript files. If a pattern starts with `!` the files are excluded. If not specified, the generated code is expected in the same directory as its source. - `vitest.logLevel`: How verbose should the logger be in the "Output" channel. Default: `info` - `vitest.applyDiagnostic`: Show a squiggly line where the error was thrown. This also enables the error count in the File Tab. Default: `true` - `vitest.showInlineConsoleLog`: Show console.log messages inline in the editor next to the code that produced them. When disabled, console logs will still appear in the test output but not inline. Default: `true` @@ -125,7 +124,7 @@ You can run all tests that import the current file by using the "Run Related Tes ### Import Breakdown -If you use Vitest 4.0.15 or higher, during continuous runs the extension will show how long it took to load the module on the same line where the import is defined. This number includes transform time and evaluation time, including static imports. +If you use Vitest 4.1 or higher, during continuous runs the extension will show how long it took to load the module on the same line where the import is defined. This number includes transform time and evaluation time, including static imports. If you hover over it, you can get a more detailed diagnostic. @@ -178,4 +177,4 @@ Since 1.44.1, Vitest extension will forcefully stop any Vitest process after 1s ### I am using `vitest.shellType: terminal`, but I don't see the terminal -The extension uses a modified Vitest script that removes the reporter output. For this reason, the terminal is hidden by default. However, it might be useful to debug issues with the extension or Vitest itself - to open the terminal in the "Terminals" view you can use the "Vitest: Show Shell Terminal" command. +The terminal is hidden by default because the content is replicated in the "Test Results" window. However, it might be useful to debug issues with the extension or Vitest itself - to open the terminal in the "Terminals" view you can use the "Vitest: Show Shell Terminal" command. diff --git a/package.json b/package.json index 5da7562..517affa 100644 --- a/package.json +++ b/package.json @@ -299,18 +299,11 @@ "default": false, "scope": "resource" }, - "vitest.maximumConfigs": { - "description": "The maximum amount of configs that Vitest extension can load. If exceeded, the extension will show a warning suggesting to use a workspace config file.", - "type": "number", - "default": 5, - "scope": "window" - }, "vitest.debugExclude": { "markdownDescription": "Automatically skip files covered by these glob patterns.", "type": "array", "default": [ - "/**", - "**/node_modules/**" + "/**" ], "scope": "resource" }, diff --git a/packages/extension/src/config.ts b/packages/extension/src/config.ts index 95d6da8..55e605b 100644 --- a/packages/extension/src/config.ts +++ b/packages/extension/src/config.ts @@ -91,7 +91,6 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) { configSearchPatternInclude, configSearchPatternExclude, ignoreWorkspace, - // maximumConfigs: get('maximumConfigs', 5), nodeExecutable: resolveConfigPath(nodeExecutable), disableWorkspaceWarning: get('disableWorkspaceWarning', false), debuggerPort: get('debuggerPort') || undefined, -- 2.51.2 From 139dd9b6393c43477c7e5895934b337201ab5a46 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Mon, 16 Mar 2026 09:00:05 +0100 Subject: [PATCH 25/64] fix: log correct name when running in vite-plus (#760) --- packages/extension/src/spawn/pkg.ts | 6 +++++ packages/extension/src/spawn/resolve.ts | 34 ++++++++++++++++--------- packages/extension/src/utils.ts | 2 +- 3 files changed, 29 insertions(+), 13 deletions(-) diff --git a/packages/extension/src/spawn/pkg.ts b/packages/extension/src/spawn/pkg.ts index a7985fc..eb53638 100644 --- a/packages/extension/src/spawn/pkg.ts +++ b/packages/extension/src/spawn/pkg.ts @@ -21,6 +21,7 @@ export interface VitestPackage { id: string cwd: string version: string + name: string arguments?: string configFile?: string workspaceFile?: string @@ -90,6 +91,7 @@ function resolveVitestConfig( loader: vitest.pnp.loaderPath, pnp: vitest.pnp.pnpPath, runtime, + name: vitest.packageName, } } @@ -105,6 +107,7 @@ function resolveVitestConfig( vitestNodePath: vitest.vitestNodePath, version: pkg.version, runtime, + name: vitest.packageName, } } @@ -178,6 +181,7 @@ function resolveVitestWorkspacePackages(showWarning: boolean) { vitestNodePath: vitest.vitestNodePath, version: pkg.version, runtime, + name: vitest.packageName, }) }) return { @@ -240,6 +244,7 @@ export async function resolveVitestPackagesViaPackageJson( vitestNodePath: vitest.vitestNodePath, version: pkg.version, runtime, + name: vitest.packageName, }) }) @@ -314,6 +319,7 @@ async function resolveVitestConfigs(showWarning: boolean) { config.configSearchPatternInclude || configGlob, config.configSearchPatternExclude, ) + console.log(configs, vscode.workspace.workspaceFolders) const configsByFolder = configs.reduce>((acc, config) => { const dir = dirname(config.fsPath) diff --git a/packages/extension/src/spawn/resolve.ts b/packages/extension/src/spawn/resolve.ts index 8c3166a..10bd8f8 100644 --- a/packages/extension/src/spawn/resolve.ts +++ b/packages/extension/src/spawn/resolve.ts @@ -8,6 +8,7 @@ const _require = require export interface VitestResolution { vitestPackageJsonPath: string vitestNodePath: string + packageName: string pnp?: { loaderPath: string pnpPath: string @@ -23,6 +24,7 @@ export function resolveVitestPackage( return { vitestNodePath: resolveVitestNodePath(vitestPackageJsonPath), vitestPackageJsonPath, + packageName: 'Vitest', } } const vitePlus = resolveVitePlusPackagePath(cwd) @@ -30,24 +32,32 @@ export function resolveVitestPackage( return { vitestNodePath: resolveViePlusVitestNodePath(vitePlus), vitestPackageJsonPath: vitePlus, + packageName: 'VitePlus', } } const pnpCwd = folder?.uri.fsPath || cwd const pnp = resolvePnp(pnpCwd) if (!pnp) return null - const vitestNodePath = - resolvePnpPackagePath(pnp.pnpApi, 'vitest/node', pnpCwd) || - resolvePnpPackagePath(pnp.pnpApi, 'vite-plus/test/node', pnpCwd) - if (!vitestNodePath) return null - return { - vitestNodePath, - vitestPackageJsonPath: '', // we don't read pkg.json for pnp - pnp: { - loaderPath: pnp.pnpLoader, - pnpPath: pnp.pnpPath, - }, + const vitestNodePath = resolvePnpPackagePath(pnp.pnpApi, 'vitest/node', pnpCwd) + if (vitestNodePath) { + return { + vitestNodePath, + vitestPackageJsonPath: '', // we don't read pkg.json for pnp + pnp, + packageName: 'Vitest', + } + } + const vitePlusNodePath = resolvePnpPackagePath(pnp.pnpApi, 'vite-plus/test/node', pnpCwd) + if (vitePlusNodePath) { + return { + vitestNodePath: vitePlusNodePath, + vitestPackageJsonPath: '', // we don't read pkg.json for pnp + pnp, + packageName: 'VitePlus', + } } + return null } export function resolveVitestPackagePath(cwd: string, folder: vscode.WorkspaceFolder | undefined) { @@ -91,7 +101,7 @@ export function resolvePnp(cwd: string) { } const pnpApi = _require(pnpPath) return { - pnpLoader: require.resolve('./.pnp.loader.mjs', { + loaderPath: require.resolve('./.pnp.loader.mjs', { paths: [dirname(pnpPath)], }), pnpPath, diff --git a/packages/extension/src/utils.ts b/packages/extension/src/utils.ts index 4407d4b..1a75c79 100644 --- a/packages/extension/src/utils.ts +++ b/packages/extension/src/utils.ts @@ -13,7 +13,7 @@ import { getTestData, TestFile } from './testTreeData' export function noop() {} export function formatPkg(pkg: VitestPackage) { - return `Vitest v${pkg.version} (${relative(dirname(pkg.cwd), pkg.id)})` + return `${pkg.name} v${pkg.version} (${relative(dirname(pkg.cwd), pkg.id)})` } function _showVitestError(message: string, error?: any) { -- 2.51.2 From 620b65b25b6cd6555ee5ea59c80d3fb208b92e95 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Mon, 16 Mar 2026 09:00:36 +0100 Subject: [PATCH 26/64] chore: remove log --- packages/extension/src/spawn/pkg.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/extension/src/spawn/pkg.ts b/packages/extension/src/spawn/pkg.ts index eb53638..b910516 100644 --- a/packages/extension/src/spawn/pkg.ts +++ b/packages/extension/src/spawn/pkg.ts @@ -319,7 +319,6 @@ async function resolveVitestConfigs(showWarning: boolean) { config.configSearchPatternInclude || configGlob, config.configSearchPatternExclude, ) - console.log(configs, vscode.workspace.workspaceFolders) const configsByFolder = configs.reduce>((acc, config) => { const dir = dirname(config.fsPath) -- 2.51.2 From a2801c2ae1ca9374d241a1eeeabafe578b0f15d9 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Mon, 16 Mar 2026 10:44:22 +0100 Subject: [PATCH 27/64] fix(collect): don't treat extra props on test return as tests --- packages/worker-legacy/src/collect.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/worker-legacy/src/collect.ts b/packages/worker-legacy/src/collect.ts index 9e2815e..2e5e32a 100644 --- a/packages/worker-legacy/src/collect.ts +++ b/packages/worker-legacy/src/collect.ts @@ -162,6 +162,10 @@ export function astParseFile(filepath: string, code: string) { if (property && ['each', 'for', 'skipIf', 'runIf', 'extend', 'scoped'].includes(property)) { return } + // skip properties on return values of calls - e.g., test('name', fn).skip() + if (callee.type === 'MemberExpression' && callee.object?.type === 'CallExpression') { + return + } // derive mode from the full chain (handles any order like .skip.concurrent or .concurrent.skip) let mode: 'run' | 'skip' | 'only' | 'todo' = 'run' for (const prop of properties) { -- 2.51.2 From 0edd21472646f60f313202a81e462421ce30c8cc Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Mon, 16 Mar 2026 11:32:14 +0100 Subject: [PATCH 28/64] chore: update vitest --- pnpm-lock.yaml | 353 ++++++++++++++++++++------------------------ pnpm-workspace.yaml | 12 +- 2 files changed, 170 insertions(+), 195 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ca8160d..dc5c5ce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -116,20 +116,20 @@ catalogs: specifier: ^4.0.2 version: 4.0.2 '@vitest/browser': - specifier: ^4.1.0-beta.3 - version: 4.1.0-beta.3 + specifier: ^4.1.0 + version: 4.1.0 '@vitest/browser-playwright': - specifier: ^4.1.0-beta.3 - version: 4.1.0-beta.3 + specifier: ^4.1.0 + version: 4.1.0 '@vitest/coverage-istanbul': - specifier: ^4.1.0-beta.3 - version: 4.1.0-beta.6 + specifier: ^4.1.0 + version: 4.1.0 '@vitest/coverage-v8': - specifier: ^4.1.0-beta.3 - version: 4.1.0-beta.3 + specifier: ^4.1.0 + version: 4.1.0 '@vitest/utils': - specifier: ^4.1.0-beta.3 - version: 4.1.0-beta.3 + specifier: ^4.1.0 + version: 4.1.0 picomatch: specifier: ^4.0.3 version: 4.0.3 @@ -137,8 +137,8 @@ catalogs: specifier: ^7.2.6 version: 7.2.6 vitest: - specifier: ^4.1.0-beta.3 - version: 4.1.0-beta.3 + specifier: ^4.1.0 + version: 4.1.0 v3: '@vitest/browser': specifier: ^3.2.4 @@ -270,7 +270,7 @@ importers: version: 5.9.3 vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) which: specifier: 'catalog:' version: 4.0.0 @@ -312,10 +312,10 @@ importers: devDependencies: '@vitest/utils': specifier: catalog:latest - version: 4.1.0-beta.3 + version: 4.1.0 vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) vitest-vscode-shared: specifier: workspace:* version: link:../shared @@ -343,13 +343,13 @@ importers: devDependencies: '@vitest/coverage-v8': specifier: catalog:latest - version: 4.1.0-beta.3(@vitest/browser@4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3))(vitest@4.1.0-beta.3) + version: 4.1.0(@vitest/browser@4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0))(vitest@4.1.0) vite: specifier: catalog:latest version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/basic: dependencies: @@ -371,19 +371,19 @@ importers: devDependencies: '@vitest/browser': specifier: catalog:latest - version: 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) + version: 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0) '@vitest/coverage-istanbul': specifier: catalog:latest - version: 4.1.0-beta.6(vitest@4.1.0-beta.3) + version: 4.1.0(vitest@4.1.0) '@vitest/coverage-v8': specifier: catalog:latest - version: 4.1.0-beta.3(@vitest/browser@4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3))(vitest@4.1.0-beta.3) + version: 4.1.0(@vitest/browser@4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0))(vitest@4.1.0) vite: specifier: catalog:latest version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/browser: dependencies: @@ -393,13 +393,13 @@ importers: devDependencies: '@vitest/browser': specifier: catalog:latest - version: 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) + version: 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0) '@vitest/browser-playwright': specifier: catalog:latest - version: 4.1.0-beta.3(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) + version: 4.1.0(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0) '@vitest/coverage-v8': specifier: catalog:latest - version: 4.1.0-beta.3(@vitest/browser@4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3))(vitest@4.1.0-beta.3) + version: 4.1.0(@vitest/browser@4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0))(vitest@4.1.0) playwright: specifier: ^1.47.0 version: 1.57.0 @@ -408,25 +408,25 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/continuous: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/deno: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/e2e: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/imba: devDependencies: @@ -438,7 +438,7 @@ importers: version: 6.9.1 imba: specifier: ^2.0.0-alpha.235 - version: 2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.9.1)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) + version: 2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.9.1)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0) jsdom: specifier: ^24.0.0 version: 24.1.3 @@ -447,13 +447,13 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vite-plugin-imba: specifier: ^0.10.3 - version: 0.10.3(imba@2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.9.1)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 0.10.3(imba@2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.9.1)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@24.1.3)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@24.1.3)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) vitest-github-actions-reporter-temp: specifier: ^0.8.3 - version: 0.8.3(vitest@4.1.0-beta.3) + version: 0.8.3(vitest@4.1.0) samples/in-source: devDependencies: @@ -462,19 +462,19 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/monorepo-vitest-workspace: devDependencies: '@vitest/coverage-v8': specifier: catalog:latest - version: 4.1.0-beta.3(@vitest/browser@4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3))(vitest@4.1.0-beta.3) + version: 4.1.0(@vitest/browser@4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0))(vitest@4.1.0) happy-dom: specifier: ^15.7.4 version: 15.11.7 vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/monorepo-vitest-workspace/packages/react: dependencies: @@ -530,13 +530,13 @@ importers: dependencies: vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/no-config: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/projects: devDependencies: @@ -545,13 +545,13 @@ importers: version: 15.11.7 vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/readme: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/vue: dependencies: @@ -564,7 +564,7 @@ importers: version: 6.0.2(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.25(typescript@5.9.3)) '@vitest/coverage-v8': specifier: catalog:latest - version: 4.1.0-beta.3(@vitest/browser@4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3))(vitest@4.1.0-beta.3) + version: 4.1.0(@vitest/browser@4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0))(vitest@4.1.0) '@vue/test-utils': specifier: ^2.4.5 version: 2.4.6 @@ -576,7 +576,7 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@14.7.1)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@14.7.1)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) packages: @@ -856,6 +856,9 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} + '@blazediff/core@1.9.1': + resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==} + '@bramus/specificity@2.4.2': resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} hasBin: true @@ -1837,11 +1840,11 @@ packages: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 vue: ^3.2.25 - '@vitest/browser-playwright@4.1.0-beta.3': - resolution: {integrity: sha512-KS252VkJlqzt96A+w9aVgZJwxccJB975Z0MSVIB+1MGKsqbrs3C6PWPwRcyOnDGQmtd1Qz3K+KxG5ACflxgBiQ==} + '@vitest/browser-playwright@4.1.0': + resolution: {integrity: sha512-2RU7pZELY9/aVMLmABNy1HeZ4FX23FXGY1jRuHLHgWa2zaAE49aNW2GLzebW+BmbTZIKKyFF1QXvk7DEWViUCQ==} peerDependencies: playwright: '*' - vitest: 4.1.0-beta.3 + vitest: 4.1.0 '@vitest/browser@3.2.4': resolution: {integrity: sha512-tJxiPrWmzH8a+w9nLKlQMzAKX/7VjFs50MWgcAj7p9XQ7AQ9/35fByFYptgPELyLw+0aixTnC4pUWV+APcZ/kw==} @@ -1858,15 +1861,15 @@ packages: webdriverio: optional: true - '@vitest/browser@4.1.0-beta.3': - resolution: {integrity: sha512-h4v9BK3Mzhf9DVth3467tKfPp3pzqsHAD+Oa8v1pLt+mjvMNpURlyo5bn2mb0ECNHAuYvhl6Li3WmwG/vnAfSw==} + '@vitest/browser@4.1.0': + resolution: {integrity: sha512-tG/iOrgbiHQks0ew7CdelUyNEHkv8NLrt+CqdTivIuoSnXvO7scWMn4Kqo78/UGY1NJ6Hv+vp8BvRnED/bjFdQ==} peerDependencies: - vitest: 4.1.0-beta.3 + vitest: 4.1.0 - '@vitest/coverage-istanbul@4.1.0-beta.6': - resolution: {integrity: sha512-HYfxxux7y/U9Qo3pi0n2AwjTVIIexHoOtGOSeT3jhRea6CxrAUHZsxzZQDZnnqx85yNO9gSH0t3ConfdODuFQQ==} + '@vitest/coverage-istanbul@4.1.0': + resolution: {integrity: sha512-0+67gA94YToxd+Pc3XgIA/2c8HN2hXNSg3T+1FI4HW7W/2gPitYCtktsY6Ke7vrt5caboMq3TUf0/vwbHRb0og==} peerDependencies: - vitest: 4.1.0-beta.6 + vitest: 4.1.0 '@vitest/coverage-v8@3.2.4': resolution: {integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==} @@ -1877,11 +1880,11 @@ packages: '@vitest/browser': optional: true - '@vitest/coverage-v8@4.1.0-beta.3': - resolution: {integrity: sha512-6d64OanipKTcZHg7x+2eL+bGpPbHFNmoyGTkgn7PjG54MC7ERkAmFhhRgORDhUkrg8QVzVHCAAAllzwUbRy2aA==} + '@vitest/coverage-v8@4.1.0': + resolution: {integrity: sha512-nDWulKeik2bL2Va/Wl4x7DLuTKAXa906iRFooIRPR+huHkcvp9QDkPQ2RJdmjOFrqOqvNfoSQLF68deE3xC3CQ==} peerDependencies: - '@vitest/browser': 4.1.0-beta.3 - vitest: 4.1.0-beta.3 + '@vitest/browser': 4.1.0 + vitest: 4.1.0 peerDependenciesMeta: '@vitest/browser': optional: true @@ -1889,8 +1892,8 @@ packages: '@vitest/expect@3.2.4': resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} - '@vitest/expect@4.1.0-beta.3': - resolution: {integrity: sha512-rcWtKmpjGjtX+mDsP7N6cKg+YFdxO1CvGxaa3eZC1d0sdM9gXyZLWoJCdaxva85JC+WesY8ZPg4EBMMjTBf4tA==} + '@vitest/expect@4.1.0': + resolution: {integrity: sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==} '@vitest/mocker@3.2.4': resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} @@ -1903,11 +1906,11 @@ packages: vite: optional: true - '@vitest/mocker@4.1.0-beta.3': - resolution: {integrity: sha512-IrSyQwOCD6rN6k9Cst5jMjuO7h6rQmuWhS1tXdn2JbuYodUvG9RviTDPWDNFEgy5oJXgk1/2w2YDuJtMOSoHSQ==} + '@vitest/mocker@4.1.0': + resolution: {integrity: sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==} peerDependencies: msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 peerDependenciesMeta: msw: optional: true @@ -1917,32 +1920,32 @@ packages: '@vitest/pretty-format@3.2.4': resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - '@vitest/pretty-format@4.1.0-beta.3': - resolution: {integrity: sha512-1bdL7Ss91JUBAYDWol9Vq4oXndgpcIKSLGS2pHDqypX9tUCTOs7RTjY8rapr7SDKgNxM//QxQO954GshR3fvNA==} + '@vitest/pretty-format@4.1.0': + resolution: {integrity: sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==} '@vitest/runner@3.2.4': resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} - '@vitest/runner@4.1.0-beta.3': - resolution: {integrity: sha512-BDfhtQqYVNqn4D8dcepoCZpABhBLCqhL+u0J+h4K2il/EfmDB0hUZCfTS2qPdK4RxmgiikaTMbJHWarG5mGmmA==} + '@vitest/runner@4.1.0': + resolution: {integrity: sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==} '@vitest/snapshot@3.2.4': resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} - '@vitest/snapshot@4.1.0-beta.3': - resolution: {integrity: sha512-ucmbRDS7OZ4XwmrwZLOfFICQ3NNVHmABsJQ2R7ojCmF4CXCcoACB0tZZ6ElYlZso8qX8kbDzJidAKFYDgZ1ICw==} + '@vitest/snapshot@4.1.0': + resolution: {integrity: sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==} '@vitest/spy@3.2.4': resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - '@vitest/spy@4.1.0-beta.3': - resolution: {integrity: sha512-0ryUOShzAkss9ntxyYkEEcQHZk6sjginbHLyemmF/Bgp1R/VhYhxf4opOwmh+E+wSv33S3dY/X/Q2jsKdK+mrQ==} + '@vitest/spy@4.1.0': + resolution: {integrity: sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==} '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} - '@vitest/utils@4.1.0-beta.3': - resolution: {integrity: sha512-oCB98WogcLAoIcDci9ERWAED7mBosoRWRvWpAOUo9fv/QeBRSCFK+egoFQNoFLCLQkNCEHj8FxqC532HZGHBpQ==} + '@vitest/utils@4.1.0': + resolution: {integrity: sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==} '@vscode/test-cli@0.0.6': resolution: {integrity: sha512-4i61OUv5PQr3GxhHOuUgHdgBDfIO/kXTPCsEyFiMaY4SOqQTgkTmyZLagHehjOgCfsXdcrJa3zgQ7zoc+Dh6hQ==} @@ -2130,6 +2133,9 @@ packages: ast-v8-to-istanbul@0.3.11: resolution: {integrity: sha512-Qya9fkoofMjCBNVdWINMjB5KZvkYfaO9/anwkWnjxibpWUxo5iHl2sOdP7/uAqaRuUYuoo8rDwnbaaKVFxoUvw==} + ast-v8-to-istanbul@1.0.0: + resolution: {integrity: sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==} + astral-regex@2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} @@ -3530,9 +3536,6 @@ packages: magicast@0.3.5: resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} - magicast@0.5.1: - resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==} - magicast@0.5.2: resolution: {integrity: sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==} @@ -3924,10 +3927,6 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} - pixelmatch@7.1.0: - resolution: {integrity: sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng==} - hasBin: true - pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -4293,6 +4292,9 @@ packages: std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.0.0: + resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} + stdin-discarder@0.2.2: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} engines: {node: '>=18'} @@ -4734,20 +4736,21 @@ packages: jsdom: optional: true - vitest@4.1.0-beta.3: - resolution: {integrity: sha512-gADWD/N4mwrrEtNwB777C/9FdlaN1osb0bxl+sJiaZ3FzWbcENzuodxmK/OZrSUDU47U0Poy+wXjtvSPn51/Gw==} + vitest@4.1.0: + resolution: {integrity: sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.0-beta.3 - '@vitest/browser-preview': 4.1.0-beta.3 - '@vitest/browser-webdriverio': 4.1.0-beta.3 - '@vitest/ui': 4.1.0-beta.3 + '@vitest/browser-playwright': 4.1.0 + '@vitest/browser-preview': 4.1.0 + '@vitest/browser-webdriverio': 4.1.0 + '@vitest/ui': 4.1.0 happy-dom: '*' jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0-0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -5370,6 +5373,8 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} + '@blazediff/core@1.9.1': {} + '@bramus/specificity@2.4.2': dependencies: css-tree: 3.1.0 @@ -6102,13 +6107,13 @@ snapshots: vite: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vue: 3.5.25(typescript@5.9.3) - '@vitest/browser-playwright@4.1.0-beta.3(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3)': + '@vitest/browser-playwright@4.1.0(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0)': dependencies: - '@vitest/browser': 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) - '@vitest/mocker': 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/browser': 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0) + '@vitest/mocker': 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) playwright: 1.57.0 tinyrainbow: 3.0.3 - vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - bufferutil - msw @@ -6134,16 +6139,16 @@ snapshots: - utf-8-validate - vite - '@vitest/browser@4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3)': + '@vitest/browser@4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0)': dependencies: - '@vitest/mocker': 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) - '@vitest/utils': 4.1.0-beta.3 + '@blazediff/core': 1.9.1 + '@vitest/mocker': 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/utils': 4.1.0 magic-string: 0.30.21 - pixelmatch: 7.1.0 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.0.3 - vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) ws: 8.19.0 transitivePeerDependencies: - bufferutil @@ -6151,7 +6156,7 @@ snapshots: - utf-8-validate - vite - '@vitest/coverage-istanbul@4.1.0-beta.6(vitest@4.1.0-beta.3)': + '@vitest/coverage-istanbul@4.1.0(vitest@4.1.0)': dependencies: '@babel/core': 7.29.0 '@istanbuljs/schema': 0.1.3 @@ -6163,7 +6168,7 @@ snapshots: magicast: 0.5.2 obug: 2.1.1 tinyrainbow: 3.0.3 - vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - supports-color @@ -6188,21 +6193,21 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@4.1.0-beta.3(@vitest/browser@4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3))(vitest@4.1.0-beta.3)': + '@vitest/coverage-v8@4.1.0(@vitest/browser@4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0))(vitest@4.1.0)': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.1.0-beta.3 - ast-v8-to-istanbul: 0.3.11 + '@vitest/utils': 4.1.0 + ast-v8-to-istanbul: 1.0.0 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-reports: 3.2.0 - magicast: 0.5.1 + magicast: 0.5.2 obug: 2.1.1 - std-env: 3.10.0 + std-env: 4.0.0 tinyrainbow: 3.0.3 - vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) optionalDependencies: - '@vitest/browser': 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) + '@vitest/browser': 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0) '@vitest/expect@3.2.4': dependencies: @@ -6212,12 +6217,12 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/expect@4.1.0-beta.3': + '@vitest/expect@4.1.0': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.0-beta.3 - '@vitest/utils': 4.1.0-beta.3 + '@vitest/spy': 4.1.0 + '@vitest/utils': 4.1.0 chai: 6.2.2 tinyrainbow: 3.0.3 @@ -6229,9 +6234,9 @@ snapshots: optionalDependencies: vite: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) - '@vitest/mocker@4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: - '@vitest/spy': 4.1.0-beta.3 + '@vitest/spy': 4.1.0 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: @@ -6241,7 +6246,7 @@ snapshots: dependencies: tinyrainbow: 2.0.0 - '@vitest/pretty-format@4.1.0-beta.3': + '@vitest/pretty-format@4.1.0': dependencies: tinyrainbow: 3.0.3 @@ -6251,9 +6256,9 @@ snapshots: pathe: 2.0.3 strip-literal: 3.1.0 - '@vitest/runner@4.1.0-beta.3': + '@vitest/runner@4.1.0': dependencies: - '@vitest/utils': 4.1.0-beta.3 + '@vitest/utils': 4.1.0 pathe: 2.0.3 '@vitest/snapshot@3.2.4': @@ -6262,9 +6267,10 @@ snapshots: magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/snapshot@4.1.0-beta.3': + '@vitest/snapshot@4.1.0': dependencies: - '@vitest/pretty-format': 4.1.0-beta.3 + '@vitest/pretty-format': 4.1.0 + '@vitest/utils': 4.1.0 magic-string: 0.30.21 pathe: 2.0.3 @@ -6272,7 +6278,7 @@ snapshots: dependencies: tinyspy: 4.0.4 - '@vitest/spy@4.1.0-beta.3': {} + '@vitest/spy@4.1.0': {} '@vitest/utils@3.2.4': dependencies: @@ -6280,9 +6286,10 @@ snapshots: loupe: 3.2.1 tinyrainbow: 2.0.0 - '@vitest/utils@4.1.0-beta.3': + '@vitest/utils@4.1.0': dependencies: - '@vitest/pretty-format': 4.1.0-beta.3 + '@vitest/pretty-format': 4.1.0 + convert-source-map: 2.0.0 tinyrainbow: 3.0.3 '@vscode/test-cli@0.0.6': @@ -6522,6 +6529,12 @@ snapshots: estree-walker: 3.0.3 js-tokens: 10.0.0 + ast-v8-to-istanbul@1.0.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + astral-regex@2.0.0: {} asynckit@0.4.0: {} @@ -7639,7 +7652,7 @@ snapshots: ignore@7.0.5: {} - imba@2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.9.1)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3): + imba@2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.9.1)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0): dependencies: '@antfu/install-pkg': 0.1.1 chokidar: 3.6.0 @@ -7659,7 +7672,7 @@ snapshots: '@testing-library/jest-dom': 6.9.1 vite: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vite-node: 3.2.4(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) - vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@24.1.3)(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@24.1.3)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - picomatch - supports-color @@ -8072,12 +8085,6 @@ snapshots: '@babel/types': 7.28.5 source-map-js: 1.2.1 - magicast@0.5.1: - dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 - source-map-js: 1.2.1 - magicast@0.5.2: dependencies: '@babel/parser': 7.29.0 @@ -8474,10 +8481,6 @@ snapshots: picomatch@4.0.3: {} - pixelmatch@7.1.0: - dependencies: - pngjs: 7.0.0 - pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -8923,6 +8926,8 @@ snapshots: std-env@3.10.0: {} + std-env@4.0.0: {} + stdin-discarder@0.2.2: {} stop-iteration-iterator@1.1.0: @@ -9292,14 +9297,14 @@ snapshots: - tsx - yaml - vite-plugin-imba@0.10.3(imba@2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.9.1)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): + vite-plugin-imba@0.10.3(imba@2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.9.1)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: '@rollup/pluginutils': 4.2.1 cross-env: 7.0.3 debug: 4.4.3(supports-color@8.1.1) deepmerge: 4.3.1 diff: 5.2.0 - imba: 2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.9.1)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) + imba: 2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.9.1)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0) kleur: 4.1.5 magic-string: 0.26.7 vite: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) @@ -9321,11 +9326,11 @@ snapshots: tsx: 4.21.0 yaml: 2.8.2 - vitest-github-actions-reporter-temp@0.8.3(vitest@4.1.0-beta.3): + vitest-github-actions-reporter-temp@0.8.3(vitest@4.1.0): dependencies: '@actions/core': 1.11.1 source-map-js: 1.2.1 - vitest: 4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@24.1.3)(tsx@4.21.0)(yaml@2.8.2) + vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@24.1.3)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: @@ -9372,22 +9377,22 @@ snapshots: - tsx - yaml - vitest@4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@14.7.1)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2): + vitest@4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@14.7.1)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: - '@vitest/expect': 4.1.0-beta.3 - '@vitest/mocker': 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) - '@vitest/pretty-format': 4.1.0-beta.3 - '@vitest/runner': 4.1.0-beta.3 - '@vitest/snapshot': 4.1.0-beta.3 - '@vitest/spy': 4.1.0-beta.3 - '@vitest/utils': 4.1.0-beta.3 + '@vitest/expect': 4.1.0 + '@vitest/mocker': 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/pretty-format': 4.1.0 + '@vitest/runner': 4.1.0 + '@vitest/snapshot': 4.1.0 + '@vitest/spy': 4.1.0 + '@vitest/utils': 4.1.0 es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.3 - std-env: 3.10.0 + std-env: 4.0.0 tinybench: 2.9.0 tinyexec: 1.0.2 tinyglobby: 0.2.15 @@ -9396,38 +9401,28 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.10.1 - '@vitest/browser-playwright': 4.1.0-beta.3(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) + '@vitest/browser-playwright': 4.1.0(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0) happy-dom: 14.7.1 jsdom: 28.1.0 transitivePeerDependencies: - - jiti - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml - vitest@4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@24.1.3)(tsx@4.21.0)(yaml@2.8.2): + vitest@4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@24.1.3)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: - '@vitest/expect': 4.1.0-beta.3 - '@vitest/mocker': 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) - '@vitest/pretty-format': 4.1.0-beta.3 - '@vitest/runner': 4.1.0-beta.3 - '@vitest/snapshot': 4.1.0-beta.3 - '@vitest/spy': 4.1.0-beta.3 - '@vitest/utils': 4.1.0-beta.3 + '@vitest/expect': 4.1.0 + '@vitest/mocker': 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/pretty-format': 4.1.0 + '@vitest/runner': 4.1.0 + '@vitest/snapshot': 4.1.0 + '@vitest/spy': 4.1.0 + '@vitest/utils': 4.1.0 es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.3 - std-env: 3.10.0 + std-env: 4.0.0 tinybench: 2.9.0 tinyexec: 1.0.2 tinyglobby: 0.2.15 @@ -9436,38 +9431,28 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.10.1 - '@vitest/browser-playwright': 4.1.0-beta.3(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) + '@vitest/browser-playwright': 4.1.0(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0) happy-dom: 15.11.7 jsdom: 24.1.3 transitivePeerDependencies: - - jiti - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml - vitest@4.1.0-beta.3(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0-beta.3)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2): + vitest@4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: - '@vitest/expect': 4.1.0-beta.3 - '@vitest/mocker': 4.1.0-beta.3(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) - '@vitest/pretty-format': 4.1.0-beta.3 - '@vitest/runner': 4.1.0-beta.3 - '@vitest/snapshot': 4.1.0-beta.3 - '@vitest/spy': 4.1.0-beta.3 - '@vitest/utils': 4.1.0-beta.3 + '@vitest/expect': 4.1.0 + '@vitest/mocker': 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + '@vitest/pretty-format': 4.1.0 + '@vitest/runner': 4.1.0 + '@vitest/snapshot': 4.1.0 + '@vitest/spy': 4.1.0 + '@vitest/utils': 4.1.0 es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.3 - std-env: 3.10.0 + std-env: 4.0.0 tinybench: 2.9.0 tinyexec: 1.0.2 tinyglobby: 0.2.15 @@ -9476,21 +9461,11 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.10.1 - '@vitest/browser-playwright': 4.1.0-beta.3(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0-beta.3) + '@vitest/browser-playwright': 4.1.0(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0) happy-dom: 15.11.7 jsdom: 28.1.0 transitivePeerDependencies: - - jiti - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml vue-component-type-helpers@2.2.12: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 53b76af..c385450 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -47,14 +47,14 @@ catalog: catalogs: latest: '@types/picomatch': ^4.0.2 - '@vitest/browser': ^4.1.0-beta.3 - '@vitest/browser-playwright': ^4.1.0-beta.3 - '@vitest/coverage-istanbul': ^4.1.0-beta.3 - '@vitest/coverage-v8': ^4.1.0-beta.3 - '@vitest/utils': ^4.1.0-beta.3 + '@vitest/browser': ^4.1.0 + '@vitest/browser-playwright': ^4.1.0 + '@vitest/coverage-istanbul': ^4.1.0 + '@vitest/coverage-v8': ^4.1.0 + '@vitest/utils': ^4.1.0 picomatch: ^4.0.3 vite: ^7.2.6 - vitest: ^4.1.0-beta.3 + vitest: ^4.1.0 v3: '@vitest/browser': ^3.2.4 -- 2.51.2 From 68945b9dce34fd13982ad74d81e9672a138909e6 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Mon, 16 Mar 2026 11:51:58 +0100 Subject: [PATCH 29/64] chore: more logs --- packages/extension/src/spawn/child_process.ts | 2 +- packages/extension/src/utils.ts | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/extension/src/spawn/child_process.ts b/packages/extension/src/spawn/child_process.ts index feaf9c9..e8d555c 100644 --- a/packages/extension/src/spawn/child_process.ts +++ b/packages/extension/src/spawn/child_process.ts @@ -33,13 +33,13 @@ export async function createVitestProcess(pkg: VitestPackage, options?: ProcessS ...runtimeArgs, ] : runtimeArgs - const arvString = execArgv.join(' ') const executable = await findRuntimeExecutable(pkg.runtime, pkg.cwd) let executablePath = workerPath if (folderConfig.runtime === 'deno') { execArgv.push('-A') executablePath = pathToFileURL(workerPath).toString() } + const arvString = execArgv.join(' ') const script = `${executable} ${arvString ? `${arvString} ` : ''}${executablePath}`.trim() log.info('[API]', `Running ${formatPkg(pkg)} with "${script}"`) const logLevel = folderConfig.logLevel diff --git a/packages/extension/src/utils.ts b/packages/extension/src/utils.ts index 1a75c79..749dc44 100644 --- a/packages/extension/src/utils.ts +++ b/packages/extension/src/utils.ts @@ -113,14 +113,12 @@ async function findRuntimeViaShell(runtime: 'node' | 'deno', cwd: string): Promi const startToken = '___START_SHELL__' const endToken = '___END_SHELL__' try { - const childProcess = spawn( - `${vscode.env.shell} -i -c 'if [[ $(type ${runtime} 2>/dev/null) == *function* ]]; then ${runtime} --version; fi; echo ${startToken} && which ${runtime} && echo ${endToken}'`, - { - stdio: 'pipe', - shell: true, - cwd, - }, - ) + const command = `${vscode.env.shell} -i -c 'if [[ $(type ${runtime} 2>/dev/null) == *function* ]]; then ${runtime} --version; fi; echo ${startToken} && which ${runtime} && echo ${endToken}'` + const childProcess = spawn(command, { + stdio: 'pipe', + shell: true, + cwd, + }) let output = '' childProcess.stdout.on('data', (data) => (output += data.toString())) childProcess.on('error', () => resolve(null)) @@ -129,6 +127,7 @@ async function findRuntimeViaShell(runtime: 'node' | 'deno', cwd: string): Promi const start = output.indexOf(startToken) const end = output.indexOf(endToken) if (start === -1 || end === -1) return resolve(null) + log.verbose?.('[SHELL] Resolved runtime via shell command', command) return resolve(output.substring(start + startToken.length, end).trim()) }) } catch (e) { -- 2.51.2 From 0790ab7f954a1ae857d785b1d1b3386bd76482ec Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Mon, 16 Mar 2026 12:19:58 +0100 Subject: [PATCH 30/64] chore: print path if exec is CMD --- packages/extension/src/spawn/child_process.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/extension/src/spawn/child_process.ts b/packages/extension/src/spawn/child_process.ts index e8d555c..56b9345 100644 --- a/packages/extension/src/spawn/child_process.ts +++ b/packages/extension/src/spawn/child_process.ts @@ -34,6 +34,9 @@ export async function createVitestProcess(pkg: VitestPackage, options?: ProcessS ] : runtimeArgs const executable = await findRuntimeExecutable(pkg.runtime, pkg.cwd) + if (executable.endsWith('.CMD')) { + log.error(`Executable resolved to CMD instead of EXE. The PATH: ${process.env.PATH}`) + } let executablePath = workerPath if (folderConfig.runtime === 'deno') { execArgv.push('-A') -- 2.51.2 From b79f1048c453fb2720916ed5c6cca96e4b7ceb18 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Mon, 16 Mar 2026 12:20:16 +0100 Subject: [PATCH 31/64] chore: release v1.48.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 517affa..186905a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "explorer", "displayName": "Vitest", - "version": "1.48.1", + "version": "1.48.2", "description": "A Vite-native testing framework. It's fast!", "categories": [ "Testing" -- 2.51.2 From c6ab4bc09eef48c205d18947326df76b43111354 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Fri, 27 Mar 2026 11:52:21 +0100 Subject: [PATCH 32/64] fix: scope logs to test like before 1.40 (#768) --- .../commands/{copyErrors.ts => copyOutput.ts} | 5 +- packages/extension/src/debug.ts | 12 +- packages/extension/src/extension.ts | 12 +- packages/extension/src/inlineConsoleLog.ts | 183 ------------------ packages/extension/src/runQueue.ts | 4 - packages/extension/src/runner.ts | 25 ++- packages/worker-legacy/src/index.ts | 7 +- packages/worker/src/index.ts | 7 +- 8 files changed, 37 insertions(+), 218 deletions(-) rename packages/extension/src/commands/{copyErrors.ts => copyOutput.ts} (93%) delete mode 100644 packages/extension/src/inlineConsoleLog.ts diff --git a/packages/extension/src/commands/copyErrors.ts b/packages/extension/src/commands/copyOutput.ts similarity index 93% rename from packages/extension/src/commands/copyErrors.ts rename to packages/extension/src/commands/copyOutput.ts index 14f8844..e1c1cc2 100644 --- a/packages/extension/src/commands/copyErrors.ts +++ b/packages/extension/src/commands/copyOutput.ts @@ -2,6 +2,7 @@ import type { TestError } from 'vitest' import * as vscode from 'vscode' import { getTestData, TestCase } from '../testTreeData' import { createTestLabel, getErrorMessage, showVitestError } from '../utils' +import { stripVTControlCharacters } from 'node:util' export async function copyTestItemErrors( testController: vscode.TestController, @@ -47,7 +48,7 @@ function createTestItemErrors(item: vscode.TestItem, test: TestCase) { } } -export async function copyErrorOutput( +export async function copyOutput( arg1: { test: vscode.TestItem; message: vscode.TestMessage } | undefined, ) { if (!arg1) { @@ -63,7 +64,7 @@ export async function copyErrorOutput( const error = data.errors?.find((e) => e.__vscode_id === message.contextValue) if (!error) { - showVitestError('Cannot copy the error output. Please, open an issue with reproduction') + await vscode.env.clipboard.writeText(stripVTControlCharacters(message.message.toString())) return } diff --git a/packages/extension/src/debug.ts b/packages/extension/src/debug.ts index c72aea7..6553178 100644 --- a/packages/extension/src/debug.ts +++ b/packages/extension/src/debug.ts @@ -1,7 +1,6 @@ import type { WebSocket } from 'ws' import type { ExtensionDiagnostic } from './diagnostic' import type { ImportsBreakdownProvider } from './importsBreakdownProvider' -import type { InlineConsoleLogManager } from './inlineConsoleLog' import type { VitestPackage } from './spawn/pkg' import type { ExtensionWorkerProcess } from './spawn/types' import type { TestTree } from './testTree' @@ -30,7 +29,6 @@ export async function debugTests( pkg: VitestPackage, diagnostic: ExtensionDiagnostic | undefined, importsBreakdown: ImportsBreakdownProvider, - inlineConsoleLog: InlineConsoleLogManager, request: vscode.TestRunRequest, token: vscode.CancellationToken, @@ -155,15 +153,7 @@ export async function debugTests( process: new ExtensionDebugProcess(metadata.ws), }) const handle = await api.spawnForRun() - const runner = new TestRunner( - handle, - controller, - tree, - api, - diagnostic, - importsBreakdown, - inlineConsoleLog, - ) + const runner = new TestRunner(handle, controller, tree, api, diagnostic, importsBreakdown) disposables.push(api, runner) token.onCancellationRequested(async () => { diff --git a/packages/extension/src/extension.ts b/packages/extension/src/extension.ts index 0d87f22..84c8794 100644 --- a/packages/extension/src/extension.ts +++ b/packages/extension/src/extension.ts @@ -4,14 +4,13 @@ import { basename, normalize, relative } from 'pathe' import * as vscode from 'vscode' import { version } from '../../../package.json' import { resolveVitestAPI } from './api' -import { copyErrorOutput, copyTestItemErrors } from './commands/copyErrors' +import { copyOutput, copyTestItemErrors } from './commands/copyOutput' import { getConfig, testControllerId } from './config' import { configGlob, workspaceGlob } from './constants' import { coverageContext } from './coverage' import { DebugManager, debugTests } from './debug' import { ExtensionDiagnostic } from './diagnostic' import { ImportsBreakdownProvider } from './importsBreakdownProvider' -import { InlineConsoleLogManager } from './inlineConsoleLog' import { log } from './log' import { RunQueue } from './runQueue' import { TransformSchemaProvider } from './schemaProvider' @@ -50,7 +49,6 @@ class VitestExtension { private debugManager: DebugManager private schemaProvider: TransformSchemaProvider private importsBreakdownProvider: ImportsBreakdownProvider - private inlineConsoleLog: InlineConsoleLogManager /** @internal */ _debugDisposable: vscode.Disposable | undefined @@ -88,7 +86,6 @@ class VitestExtension { untrackedModules: [], }, ) - this.inlineConsoleLog = new InlineConsoleLogManager(this.testTree) } private _defineTestProfilePromise: Promise | undefined @@ -105,7 +102,6 @@ class VitestExtension { private async _defineTestProfiles(showWarning: boolean, cancelToken?: vscode.CancellationToken) { this.importsBreakdownProvider.clear() - this.inlineConsoleLog.clear() this.testTree.reset([]) this.runQueues.forEach((q) => q.dispose()) this.runQueues.clear() @@ -223,7 +219,6 @@ class VitestExtension { vitest, this.diagnostic, this.importsBreakdownProvider, - this.inlineConsoleLog, ) const runQueueId = `${vitest.id}:run` this.runQueues.set(runQueueId, runQueue) @@ -255,7 +250,6 @@ class VitestExtension { vitest.package, this.diagnostic, this.importsBreakdownProvider, - this.inlineConsoleLog, request, token, @@ -287,7 +281,6 @@ class VitestExtension { vitest, this.diagnostic, this.importsBreakdownProvider, - this.inlineConsoleLog, ) const coverageQueueId = `${vitest.id}:coverage` this.runQueues.set(coverageQueueId, coverageQueue) @@ -513,7 +506,7 @@ class VitestExtension { vscode.commands.registerCommand('vitest.copyTestItemErrors', (testItem) => copyTestItemErrors(this.testController, testItem), ), - vscode.commands.registerCommand('vitest.copyErrorOutput', copyErrorOutput), + vscode.commands.registerCommand('vitest.copyErrorOutput', copyOutput), vscode.commands.registerCommand('vitest.toggleConfigs', async () => { if (!this.api) { return @@ -633,7 +626,6 @@ class VitestExtension { this.testController.dispose() this.schemaProvider.dispose() this.importsBreakdownProvider.dispose() - this.inlineConsoleLog.dispose() this.runProfiles.forEach((p) => p.dispose()) this.runProfiles.clear() this.disposables.forEach((d) => d.dispose()) diff --git a/packages/extension/src/inlineConsoleLog.ts b/packages/extension/src/inlineConsoleLog.ts deleted file mode 100644 index 08f8bcc..0000000 --- a/packages/extension/src/inlineConsoleLog.ts +++ /dev/null @@ -1,183 +0,0 @@ -import type { ExtensionUserConsoleLog } from 'vitest-vscode-shared' -import type { TestTree } from './testTree' -import { stripVTControlCharacters } from 'node:util' -import * as vscode from 'vscode' -import { getConfig } from './config' -import { createTestLabel } from './utils' - -interface ConsoleLogEntry { - content: string - time: number - testItem: vscode.TestItem | undefined -} - -export class InlineConsoleLogManager extends vscode.Disposable { - private decorationType: vscode.TextEditorDecorationType - private consoleLogsByFile = new Map>() - private disposables: vscode.Disposable[] = [] - - constructor(private readonly testTree: TestTree) { - super(() => { - this.decorationType.dispose() - this.disposables.forEach((d) => d.dispose()) - this.disposables = [] - }) - - this.decorationType = vscode.window.createTextEditorDecorationType({ - after: { - margin: '0 0 0 3em', - textDecoration: 'none', - }, - rangeBehavior: vscode.DecorationRangeBehavior.ClosedOpen, - }) - - // Update decorations when active editor changes - this.disposables.push( - vscode.window.onDidChangeActiveTextEditor((editor) => { - if (editor) { - this.updateDecorations(editor) - } - }), - ) - - // Update decorations when configuration changes - this.disposables.push( - vscode.workspace.onDidChangeConfiguration((event) => { - if (event.affectsConfiguration('vitest.showInlineConsoleLog')) { - this.refresh() - } - }), - ) - } - - addConsoleLog(consoleLog: ExtensionUserConsoleLog): void { - const config = getConfig() - if (!config.showInlineConsoleLog) { - return - } - - // Use pre-parsed location from worker - if (!consoleLog.parsedLocation) { - return - } - - const { file, line } = consoleLog.parsedLocation - - // Store console log entry - if (!this.consoleLogsByFile.has(file)) { - this.consoleLogsByFile.set(file, new Map()) - } - - const fileMap = this.consoleLogsByFile.get(file)! - if (!fileMap.has(line)) { - fileMap.set(line, []) - } - - const testItem = consoleLog.taskId - ? this.testTree.getTestItemByTaskId(consoleLog.taskId) - : undefined - - fileMap.get(line)!.push({ - content: consoleLog.content, - time: consoleLog.time, - testItem, - }) - - // Update decorations for all visible editors showing this file - vscode.window.visibleTextEditors.forEach((editor) => { - if (editor.document.uri.fsPath === file) { - this.updateDecorations(editor) - } - }) - } - - clear(): void { - this.consoleLogsByFile.clear() - // Update all visible editors - vscode.window.visibleTextEditors.forEach((editor) => this.updateDecorations(editor)) - } - - clearFile(file: string): void { - this.consoleLogsByFile.delete(file) - // Update all visible editors showing this file - vscode.window.visibleTextEditors.forEach((editor) => { - if (editor.document.uri.fsPath === file) { - this.updateDecorations(editor) - } - }) - } - - private updateDecorations(editor: vscode.TextEditor): void { - const config = getConfig() - if (!config.showInlineConsoleLog) { - editor.setDecorations(this.decorationType, []) - return - } - - const file = editor.document.uri.fsPath - const fileMap = this.consoleLogsByFile.get(file) - - if (!fileMap || fileMap.size === 0) { - editor.setDecorations(this.decorationType, []) - return - } - - const decorations: vscode.DecorationOptions[] = [] - - fileMap.forEach((entries, line) => { - // Skip if line is out of range - if (line >= editor.document.lineCount) { - return - } - - const noAnsi = entries.map((e) => stripVTControlCharacters(e.content)) - // Combine multiple console logs on the same line - const content = noAnsi.map((e) => this.formatContent(e)).join(' ') - - const hoverMessage = entries.map((e, index) => { - const md = new vscode.MarkdownString() - if (e.testItem) { - md.supportHtml = true - const line = (e.testItem.range?.start.line ?? 0) + 1 - md.appendMarkdown( - `[${createTestLabel(e.testItem)}](${e.testItem.uri?.with({ fragment: `L${line}` })})`, - ) - md.appendText('\n') - } - return md.appendCodeblock(noAnsi[index]) - }) - - const lineRange = editor.document.lineAt(line).range - const decoration: vscode.DecorationOptions = { - range: lineRange, - hoverMessage, - renderOptions: { - after: { - contentText: content, - color: new vscode.ThemeColor('editorCodeLens.foreground'), - fontStyle: 'italic', - }, - }, - } - - decorations.push(decoration) - }) - - editor.setDecorations(this.decorationType, decorations) - } - - private formatContent(stripped: string): string { - // Remove trailing newlines and limit length - const cleaned = stripped.trim().replace(/\n/g, ' ') - const maxLength = 100 - if (cleaned.length > maxLength) { - return `${cleaned.substring(0, maxLength)}...` - } - return cleaned - } - - private refresh(): void { - // Update all visible editors - vscode.window.visibleTextEditors.forEach((editor) => this.updateDecorations(editor)) - } -} diff --git a/packages/extension/src/runQueue.ts b/packages/extension/src/runQueue.ts index 5e1ef66..89cb292 100644 --- a/packages/extension/src/runQueue.ts +++ b/packages/extension/src/runQueue.ts @@ -2,7 +2,6 @@ import type * as vscode from 'vscode' import type { RunHandle } from './apiProcess' import type { ExtensionDiagnostic } from './diagnostic' import type { ImportsBreakdownProvider } from './importsBreakdownProvider' -import type { InlineConsoleLogManager } from './inlineConsoleLog' import type { TestTree } from './testTree' import { VitestProcessAPI } from './apiProcess' import { log } from './log' @@ -34,7 +33,6 @@ export class RunQueue { private readonly api: VitestProcessAPI, private readonly diagnostic: ExtensionDiagnostic | undefined, private readonly importsBreakdown: ImportsBreakdownProvider, - private readonly inlineConsoleLog: InlineConsoleLogManager, ) {} public isContinuousTestItem(testItem: vscode.TestItem): boolean { @@ -207,7 +205,6 @@ export class RunQueue { api || this.api, this.diagnostic, this.importsBreakdown, - this.inlineConsoleLog, ) } @@ -219,7 +216,6 @@ export class RunQueue { this.api, this.diagnostic, this.importsBreakdown, - this.inlineConsoleLog, this.testRunProfile, this.continuousRequests, ) diff --git a/packages/extension/src/runner.ts b/packages/extension/src/runner.ts index 3e673dd..15e63fc 100644 --- a/packages/extension/src/runner.ts +++ b/packages/extension/src/runner.ts @@ -3,7 +3,6 @@ import type { ExtensionTestSpecification } from 'vitest-vscode-shared' import type { RunHandle, VitestProcessAPI } from './apiProcess' import type { ExtensionDiagnostic } from './diagnostic' import type { ImportsBreakdownProvider } from './importsBreakdownProvider' -import type { InlineConsoleLogManager } from './inlineConsoleLog' import type { TestTree } from './testTree' import crypto from 'node:crypto' import path from 'node:path' @@ -32,7 +31,6 @@ export class TestRunner extends vscode.Disposable { protected readonly api: VitestProcessAPI, protected readonly diagnostic: ExtensionDiagnostic | undefined, protected readonly importsBreakdown: ImportsBreakdownProvider, - protected readonly inlineConsoleLog: InlineConsoleLogManager, ) { super(() => { log.verbose?.('Disposing test runner') @@ -51,7 +49,6 @@ export class TestRunner extends vscode.Disposable { const uri = vscode.Uri.file(file) this.diagnostic?.deleteDiagnostic(uri) }) - this.inlineConsoleLog.clear() }) handle.handlers.onTaskUpdate((packs) => { @@ -124,7 +121,24 @@ export class TestRunner extends vscode.Disposable { }) handle.handlers.onConsoleLog((consoleLog) => { - this.inlineConsoleLog.addConsoleLog(consoleLog) + const testRun = this.testRun + if (!testRun) { + return + } + + const config = getConfig() + const test = consoleLog.taskId ? this.tree.getTestItemByTaskId(consoleLog.taskId) : undefined + const loc = consoleLog.parsedLocation + testRun.appendOutput( + formatTestOutput(consoleLog.content), + config.showInlineConsoleLog && loc + ? new vscode.Location( + vscode.Uri.file(loc.file), + new vscode.Position(loc.line, loc.column), + ) + : undefined, + test, + ) }) } @@ -336,11 +350,10 @@ export class ContinuousTestRunner extends TestRunner { api: VitestProcessAPI, diagnostic: ExtensionDiagnostic | undefined, importsBreakdown: ImportsBreakdownProvider, - inlineConsoleLog: InlineConsoleLogManager, private readonly testRunProfile: vscode.TestRunProfile, private readonly continuousRequests: Set, ) { - super(handle, controller, tree, api, diagnostic, importsBreakdown, inlineConsoleLog) + super(handle, controller, tree, api, diagnostic, importsBreakdown) handle.handlers.onTestRunStart((files) => { this.startTestRun(files) log.verbose?.( diff --git a/packages/worker-legacy/src/index.ts b/packages/worker-legacy/src/index.ts index 5c2e8b6..c1c9405 100644 --- a/packages/worker-legacy/src/index.ts +++ b/packages/worker-legacy/src/index.ts @@ -3,7 +3,7 @@ import type { WorkerRunnerOptions, WorkerWSEventEmitter, } from 'vitest-vscode-shared' -import type { UserConfig } from 'vitest/node' +import type { Reporter, UserConfig } from 'vitest/node' import { Console } from 'node:console' import { randomUUID } from 'node:crypto' import { tmpdir } from 'node:os' @@ -170,6 +170,11 @@ export async function initVitest( stdout, }, ) + ;((vitest as any).reporters as Reporter[]).forEach((reporter) => { + if (!(reporter instanceof VSCodeReporter)) { + reporter.onUserConsoleLog = undefined + } + }) const projects: SerializedProject[] = vitest.projects.map((project) => { const config = project.config diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts index 661fdf3..7cae4ba 100644 --- a/packages/worker/src/index.ts +++ b/packages/worker/src/index.ts @@ -3,7 +3,7 @@ import type { WorkerRunnerOptions, WorkerWSEventEmitter, } from 'vitest-vscode-shared' -import type { TestUserConfig } from 'vitest/node' +import type { Reporter, TestUserConfig } from 'vitest/node' import { Console } from 'node:console' import { Writable } from 'node:stream' import { toArray } from '@vitest/utils/helpers' @@ -138,6 +138,11 @@ export async function initVitest( stdout, }, ) + ;((vitest as any).reporters as Reporter[]).forEach((reporter) => { + if (!(reporter instanceof VSCodeReporter)) { + reporter.onUserConsoleLog = undefined + } + }) const projects: SerializedProject[] = vitest.projects.map((project) => { const config = project.config -- 2.51.2 From 5425e1c641fa66006cb81b0c1bf229a89e745a06 Mon Sep 17 00:00:00 2001 From: Jason DiMeo Date: Fri, 27 Mar 2026 07:00:30 -0400 Subject: [PATCH 33/64] fix: replace get-port with listen(0) to avoid EADDRINUSE under WSL2 mirrored networking (#767) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Vladimir Sheremet --- package.json | 1 - packages/extension/src/debug.ts | 9 ++-- packages/extension/src/net.ts | 28 +++++++++++ packages/extension/src/spawn/child_process.ts | 8 ++-- packages/extension/src/spawn/terminal.ts | 8 ++-- pnpm-lock.yaml | 12 ----- pnpm-workspace.yaml | 13 +++-- test/unit/net.test.ts | 48 +++++++++++++++++++ 8 files changed, 94 insertions(+), 33 deletions(-) create mode 100644 packages/extension/src/net.ts create mode 100644 test/unit/net.test.ts diff --git a/package.json b/package.json index 186905a..fb3dc69 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,6 @@ "changelogithub": "catalog:", "execa": "catalog:", "find-up": "catalog:", - "get-port": "catalog:", "istanbul-to-vscode": "catalog:", "micromatch": "catalog:", "mighty-promise": "catalog:", diff --git a/packages/extension/src/debug.ts b/packages/extension/src/debug.ts index 6553178..8562f21 100644 --- a/packages/extension/src/debug.ts +++ b/packages/extension/src/debug.ts @@ -1,14 +1,13 @@ -import type { WebSocket } from 'ws' +import type { AddressInfo, WebSocket } from 'ws' import type { ExtensionDiagnostic } from './diagnostic' import type { ImportsBreakdownProvider } from './importsBreakdownProvider' import type { VitestPackage } from './spawn/pkg' import type { ExtensionWorkerProcess } from './spawn/types' import type { TestTree } from './testTree' import crypto from 'node:crypto' -import { createServer } from 'node:http' import { pathToFileURL } from 'node:url' -import getPort from 'get-port' import * as vscode from 'vscode' +import { createBoundServer } from './net' import { WebSocketServer } from 'ws' import { VitestProcessAPI } from './apiProcess' import { getConfig } from './config' @@ -34,8 +33,8 @@ export async function debugTests( token: vscode.CancellationToken, debugManager: DebugManager, ) { - const port = await getPort() - const server = createServer().listen(port) + const server = await createBoundServer() + const { port } = server.address() as AddressInfo const wss = new WebSocketServer({ server }) const wsAddress = `ws://localhost:${port}` diff --git a/packages/extension/src/net.ts b/packages/extension/src/net.ts new file mode 100644 index 0000000..54d605c --- /dev/null +++ b/packages/extension/src/net.ts @@ -0,0 +1,28 @@ +import type { Server } from 'node:http' +import { createServer } from 'node:http' + +/** + * Creates an HTTP server bound to an OS-assigned port by listening on port 0. + * + * This avoids the TOCTOU race present in the get-port pattern + * (bind to 0 → read port → close → re-listen on that port) which causes + * `EADDRINUSE` under WSL2 mirrored networking, where the kernel holds a + * phantom listener on ephemeral ports for ~1 second after close. + * + * The returned server is already listening; callers should read the port via + * `(server.address() as AddressInfo).port`. + */ +export function createBoundServer(): Promise { + return new Promise((resolve, reject) => { + const server = createServer() + server.unref() + const onError = (err: unknown) => { + reject(err) + } + server.once('error', onError) + server.listen(0, () => { + server.off('error', onError) + resolve(server) + }) + }) +} diff --git a/packages/extension/src/spawn/child_process.ts b/packages/extension/src/spawn/child_process.ts index 56b9345..58c19e0 100644 --- a/packages/extension/src/spawn/child_process.ts +++ b/packages/extension/src/spawn/child_process.ts @@ -6,10 +6,10 @@ import type { VitestPackage } from './pkg' import type { ExtensionWorkerProcess } from './types' import type { ProcessSpawnOptions } from './ws' import { spawn } from 'node:child_process' -import { createServer } from 'node:http' import { pathToFileURL } from 'node:url' -import getPort from 'get-port' import { WebSocketServer } from 'ws' +import type { AddressInfo } from 'node:net' +import { createBoundServer } from '../net' import { getConfig } from '../config' import { workerPath } from '../constants' import { createErrorLogger, log } from '../log' @@ -46,8 +46,8 @@ export async function createVitestProcess(pkg: VitestPackage, options?: ProcessS const script = `${executable} ${arvString ? `${arvString} ` : ''}${executablePath}`.trim() log.info('[API]', `Running ${formatPkg(pkg)} with "${script}"`) const logLevel = folderConfig.logLevel - const port = await getPort() - const server = createServer().listen(port).unref() + const server = await createBoundServer() + const { port } = server.address() as AddressInfo const wss = new WebSocketServer({ server }) const wsAddress = `ws://localhost:${port}` const vitest = spawn(executable, [...execArgv, executablePath], { diff --git a/packages/extension/src/spawn/terminal.ts b/packages/extension/src/spawn/terminal.ts index 41bba9e..793efc4 100644 --- a/packages/extension/src/spawn/terminal.ts +++ b/packages/extension/src/spawn/terminal.ts @@ -1,13 +1,13 @@ import type { Server } from 'node:http' +import type { AddressInfo } from 'node:net' import type { WebSocket } from 'ws' import type { ResolvedMeta } from '../apiProcess' import type { VitestPackage } from './pkg' import type { ExtensionWorkerProcess } from './types' import type { ProcessSpawnOptions, WsConnectionMetadata } from './ws' -import { createServer } from 'node:http' import { pathToFileURL } from 'node:url' -import getPort from 'get-port' import * as vscode from 'vscode' +import { createBoundServer } from '../net' import { WebSocketServer } from 'ws' import { getConfig } from '../config' import { workerPath } from '../constants' @@ -22,8 +22,8 @@ export async function createVitestTerminalProcess( const pnpLoader = pkg.loader const pnp = pkg.pnp if (pnpLoader && !pnp) throw new Error('pnp file is required if loader option is used') - const port = await getPort() - const server = createServer().listen(port).unref() + const server = await createBoundServer() + const { port } = server.address() as AddressInfo const wss = new WebSocketServer({ server }) const wsAddress = `ws://localhost:${port}` const config = getConfig(pkg.folder) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc5c5ce..7b74981 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -69,9 +69,6 @@ catalogs: find-up: specifier: ^7.0.0 version: 7.0.0 - get-port: - specifier: ^6.1.2 - version: 6.1.2 istanbul-to-vscode: specifier: ^2.1.0 version: 2.1.1 @@ -229,9 +226,6 @@ importers: find-up: specifier: 'catalog:' version: 7.0.0 - get-port: - specifier: 'catalog:' - version: 6.1.2 istanbul-to-vscode: specifier: 'catalog:' version: 2.1.1 @@ -2945,10 +2939,6 @@ packages: resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} engines: {node: '>=8'} - get-port@6.1.2: - resolution: {integrity: sha512-BrGGraKm2uPqurfGVj/z97/zv8dPleC6x9JBNRTrDNtCkkRF4rPwrQXFgL7+I+q8QSdU4ntLQX2D7KIxSy8nGw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -7445,8 +7435,6 @@ snapshots: get-port@5.1.1: {} - get-port@6.1.2: {} - get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c385450..e0e76d8 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,13 +1,8 @@ -shellEmulator: true - -trustPolicy: no-downgrade - packages: - ./ - ./packages/* - samples/* - samples/monorepo-vitest-workspace/packages/* - catalog: '@playwright/test': ^1.42.1 '@types/chai': ^5.2.2 @@ -30,7 +25,6 @@ catalog: changelogithub: ^13.15.0 execa: ^8.0.1 find-up: ^7.0.0 - get-port: ^6.1.2 istanbul-to-vscode: ^2.1.0 micromatch: ^4.0.5 mighty-promise: ^0.0.8 @@ -55,7 +49,6 @@ catalogs: picomatch: ^4.0.3 vite: ^7.2.6 vitest: ^4.1.0 - v3: '@vitest/browser': ^3.2.4 '@vitest/coverage-v8': ^3.2.4 @@ -63,3 +56,9 @@ catalogs: '@vitest/utils': ^3.2.4 vite: ^7.2.6 vitest: ^3.2.4 +onlyBuiltDependencies: + - '@vscode/vsce-sign' + - esbuild + - keytar +shellEmulator: true +trustPolicy: no-downgrade diff --git a/test/unit/net.test.ts b/test/unit/net.test.ts new file mode 100644 index 0000000..4f0eee9 --- /dev/null +++ b/test/unit/net.test.ts @@ -0,0 +1,48 @@ +import net from 'node:net' +import { expect } from 'chai' +import { createBoundServer } from '../../packages/extension/src/net' +import type { AddressInfo } from 'node:net' + +function closeServer(server: import('node:http').Server): Promise { + return new Promise((resolve) => server.close(() => resolve())) +} + +it('createBoundServer resolves with a server bound to a valid port', async () => { + const server = await createBoundServer() + try { + const addr = server.address() as AddressInfo + expect(addr.port).to.be.a('number').and.greaterThan(0) + } finally { + await closeServer(server) + } +}) + +it('createBoundServer: port is already listening (no TOCTOU gap)', async () => { + const server = await createBoundServer() + const { port } = server.address() as AddressInfo + try { + // A TCP connection to the port must succeed immediately — the socket is + // never released and re-acquired, so there is no window for EADDRINUSE. + await new Promise((resolve, reject) => { + const sock = net.connect(port, '127.0.0.1', () => { + sock.destroy() + resolve() + }) + sock.on('error', reject) + }) + } finally { + await closeServer(server) + } +}) + +it('two concurrent createBoundServer calls receive different ports', async () => { + const [s1, s2] = await Promise.all([createBoundServer(), createBoundServer()]) + + try { + const p1 = (s1.address() as AddressInfo).port + const p2 = (s2.address() as AddressInfo).port + expect(p1).to.not.equal(p2) + } finally { + await Promise.all([closeServer(s1), closeServer(s2)]) + } +}) -- 2.51.2 From a1aaa4207d0a4a8cbd5b964628e5081e008ebabf Mon Sep 17 00:00:00 2001 From: rg <74761884+Gehbt@users.noreply.github.com> Date: Fri, 27 Mar 2026 19:02:05 +0800 Subject: [PATCH 34/64] feat(snapshot): add document symbols and folding support (#764) --- packages/extension/src/extension.ts | 14 ++- packages/extension/src/log.ts | 8 +- .../src/snapshot/documentSymbolProvider.ts | 45 ++++++++ .../src/snapshot/foldingRangeProvider.ts | 34 ++++++ packages/extension/src/snapshot/tools.ts | 101 ++++++++++++++++++ samples/basic/test/snapshot-case.test.ts | 52 +++++++++ test/e2e/utils/downloadSetup.ts | 4 +- 7 files changed, 251 insertions(+), 7 deletions(-) create mode 100644 packages/extension/src/snapshot/documentSymbolProvider.ts create mode 100644 packages/extension/src/snapshot/foldingRangeProvider.ts create mode 100644 packages/extension/src/snapshot/tools.ts create mode 100644 samples/basic/test/snapshot-case.test.ts diff --git a/packages/extension/src/extension.ts b/packages/extension/src/extension.ts index 84c8794..bf8bfdd 100644 --- a/packages/extension/src/extension.ts +++ b/packages/extension/src/extension.ts @@ -22,6 +22,9 @@ import { TestTree } from './testTree' import { getTestData, TestFile } from './testTreeData' import { clearCachedRuntime, debounce, showVitestError } from './utils' import './polyfills' +import { SnapshotEntryTool } from './snapshot/tools' +import { SnapshotDocumentSymbolProvider } from './snapshot/documentSymbolProvider' +import { SnapshotFoldingRangeProvider } from './snapshot/foldingRangeProvider' export async function activate(context: vscode.ExtensionContext) { const extension = new VitestExtension(context) @@ -320,6 +323,7 @@ class VitestExtension { 'vitest.runtime', 'deno.enabled', ] + const snapshotEntryTool = new SnapshotEntryTool() this.disposables = [ vscode.workspace.onDidChangeConfiguration((event) => { @@ -336,7 +340,7 @@ class VitestExtension { }), ), vscode.commands.registerCommand('vitest.openOutput', () => { - log.openOuput() + log.openOutput() }), vscode.commands.registerCommand('vitest.runRelatedTests', async (uri?: vscode.Uri) => { const currentUri = uri || vscode.window.activeTextEditor?.document.uri @@ -537,6 +541,14 @@ class VitestExtension { await this.defineTestProfiles(false) }), + vscode.languages.registerDocumentSymbolProvider( + { language: 'vitest-snapshot' }, + new SnapshotDocumentSymbolProvider(snapshotEntryTool), + ), + vscode.languages.registerFoldingRangeProvider( + { language: 'vitest-snapshot' }, + new SnapshotFoldingRangeProvider(snapshotEntryTool), + ), ] // if the config changes, re-define all test profiles diff --git a/packages/extension/src/log.ts b/packages/extension/src/log.ts index 14e27c1..070fd50 100644 --- a/packages/extension/src/log.ts +++ b/packages/extension/src/log.ts @@ -88,17 +88,17 @@ export const log = { workspaceError: (folder: string, ...args: any[]) => { log.error(`[Workspace ${folder}]`, ...args) }, - openOuput() { + openOutput() { channel.show() }, } as const -let exitsts = false +let exists = false function appendFile(log: string) { - if (!exitsts) { + if (!exists) { mkdirSync(dirname(logFile), { recursive: true }) writeFileSync(logFile, '') - exitsts = true + exists = true } appendFileSync(logFile, `${log}\n`) } diff --git a/packages/extension/src/snapshot/documentSymbolProvider.ts b/packages/extension/src/snapshot/documentSymbolProvider.ts new file mode 100644 index 0000000..bdba9bf --- /dev/null +++ b/packages/extension/src/snapshot/documentSymbolProvider.ts @@ -0,0 +1,45 @@ +import * as vscode from 'vscode' +import { createSnapshotSymbol, pushToDocumentSymbol, type SnapshotEntryTool } from './tools' + +export class SnapshotDocumentSymbolProvider implements vscode.DocumentSymbolProvider { + private latestUri: string | undefined = undefined + private latestVersion: number | undefined = undefined + latestDocumentSymbols: vscode.DocumentSymbol[] = [] + constructor(private snapshotEntryTool: SnapshotEntryTool) {} + provideDocumentSymbols( + document: vscode.TextDocument, + token: vscode.CancellationToken, + ): vscode.ProviderResult { + if (this.latestUri === document.uri.toString() && this.latestVersion === document.version) { + return this.latestDocumentSymbols + } + this.snapshotEntryTool.process(document, document.uri.toString(), document.version, token) + if (token.isCancellationRequested) return null // cancelled + + this.latestUri = document.uri.toString() + this.latestVersion = document.version + + const documentSymbols: vscode.DocumentSymbol[] = [] + forExportsSymbol: for (const entry of this.snapshotEntryTool.snapshotEntries) { + let currentLevel: vscode.DocumentSymbol[] = documentSymbols + let parent: vscode.DocumentSymbol[] | undefined + + for (let i = 0; i < entry.breadcrumb.length; i++) { + const existingSymbol = currentLevel.at(-1) + if (!existingSymbol || existingSymbol.name !== entry.breadcrumb[i]) { + const newSymbol = createSnapshotSymbol(entry.breadcrumb[i], entry, i) + currentLevel.push(newSymbol) + i + 1 < entry.breadcrumb.length && pushToDocumentSymbol(newSymbol, entry, i + 1) + continue forExportsSymbol + } + parent = currentLevel + currentLevel = existingSymbol.children + } + // last level - all breadcrumbs matched, create duplicate leaf + ;(parent || documentSymbols).push( + createSnapshotSymbol(entry.breadcrumb.at(-1)!, entry, entry.breadcrumb.length - 1), + ) + } + return (this.latestDocumentSymbols = documentSymbols) + } +} diff --git a/packages/extension/src/snapshot/foldingRangeProvider.ts b/packages/extension/src/snapshot/foldingRangeProvider.ts new file mode 100644 index 0000000..f634fc3 --- /dev/null +++ b/packages/extension/src/snapshot/foldingRangeProvider.ts @@ -0,0 +1,34 @@ +import * as vscode from 'vscode' +import { type SnapshotEntryTool } from './tools' + +export class SnapshotFoldingRangeProvider implements vscode.FoldingRangeProvider { + private latestUri: string | undefined = undefined + private latestVersion: number | undefined = undefined + latestFoldingRanges: vscode.FoldingRange[] = [] + constructor(private snapshotEntryTool: SnapshotEntryTool) {} + provideFoldingRanges( + document: vscode.TextDocument, + _: vscode.FoldingContext, + token: vscode.CancellationToken, + ): vscode.ProviderResult { + if (this.latestUri === document.uri.toString() && this.latestVersion === document.version) { + return this.latestFoldingRanges + } + this.snapshotEntryTool.process(document, document.uri.toString(), document.version, token) + if (token.isCancellationRequested) return null // cancelled + + this.latestUri = document.uri.toString() + this.latestVersion = document.version + const foldingRanges: vscode.FoldingRange[] = [] + for (const symbol of this.snapshotEntryTool.snapshotEntries) { + foldingRanges.push( + new vscode.FoldingRange( + document.positionAt(symbol.start).line, + document.positionAt(symbol.end).line, + vscode.FoldingRangeKind.Region, + ), + ) + } + return (this.latestFoldingRanges = foldingRanges) + } +} diff --git a/packages/extension/src/snapshot/tools.ts b/packages/extension/src/snapshot/tools.ts new file mode 100644 index 0000000..0f3eeda --- /dev/null +++ b/packages/extension/src/snapshot/tools.ts @@ -0,0 +1,101 @@ +import * as vscode from 'vscode' + +const ExportSymbolRegex = /^exports\[`([^`]*)`\]/gm +const RangeEndRegex = /`;$/m + +export interface SnapshotEntry { + name: string + breadcrumb: [...describeName: string[], itName: string] + start: number + end: number + fullRange: vscode.Range + keyRange: vscode.Range +} + +export class SnapshotEntryTool { + private latestUri: string | undefined = undefined + private latestVersion: number | undefined = undefined + snapshotEntries: SnapshotEntry[] = [] + process( + document: vscode.TextDocument, + uri: string, + version: number, + token: vscode.CancellationToken, + ): void { + let changeUri = false + let changeVersion = false + if (this.latestUri !== uri) { + this.latestUri = uri + this.latestVersion = version + changeUri = true + changeVersion = true + } else if (this.latestVersion !== version) { + this.latestVersion = version + changeVersion = true + } + + if (!changeUri && !changeVersion) { + return // cached + } else { + // reset snapshotEntries + this.snapshotEntries = [] + } + if (token.isCancellationRequested) return // cancelled + const text = document.getText() + const exportsSymbols = text.matchAll(ExportSymbolRegex) || [] + + for (const match of exportsSymbols) { + const name = match[1] + const snapshotDataStart = match.index + const snapshotDataEnd = + snapshotDataStart + + // find the nearest closing delimiter + (text.slice(snapshotDataStart).match(RangeEndRegex)?.index ?? + // fallback to empty snapshot + 'exports[`'.length + name.length + '`]'.length + ' = `'.length + '""'.length) + + '`;'.length + + this.snapshotEntries.push({ + name: name, + breadcrumb: name.split(' > ') as [...describeName: string[], itName: string], + start: snapshotDataStart, + end: snapshotDataEnd, + fullRange: new vscode.Range( + document.positionAt(snapshotDataStart), + document.positionAt(snapshotDataEnd), + ), + keyRange: new vscode.Range( + document.positionAt(snapshotDataStart + 'exports[`'.length), + document.positionAt(snapshotDataStart + 'exports[`'.length + name.length), + ), + }) + } + } +} + +export function createSnapshotSymbol( + name: string, + entry: SnapshotEntry, + index: number, +): vscode.DocumentSymbol { + const isLastRound = index === entry.breadcrumb.length - 1 + return new vscode.DocumentSymbol( + name, + isLastRound ? 'it' : 'describe', + vscode.SymbolKind.Function, + entry.fullRange, + entry.keyRange, + ) +} + +export function pushToDocumentSymbol( + parentDocumentSymbol: vscode.DocumentSymbol, + entry: SnapshotEntry, + startIndex: number = 1, +): void { + for (let i = startIndex; i < entry.breadcrumb.length; i++) { + const newDocumentSymbol = createSnapshotSymbol(entry.breadcrumb[i], entry, i) + parentDocumentSymbol.children.push(newDocumentSymbol) + parentDocumentSymbol = newDocumentSymbol + } +} diff --git a/samples/basic/test/snapshot-case.test.ts b/samples/basic/test/snapshot-case.test.ts new file mode 100644 index 0000000..b689ef6 --- /dev/null +++ b/samples/basic/test/snapshot-case.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it } from 'vitest' + +describe('fixture', () => { + describe('__fixtures__/file.spec.ts 1', () => { + it('snapshot', () => { + expect('').toMatchSnapshot() + }) + it('snapshot_1', () => { + expect('').toMatchSnapshot() + }) + }) + + describe('__fixtures__/file.spec.ts 2', () => { + it('snapshot_1', () => { + expect('').toMatchSnapshot() + }) + it('snapshot_2', () => { + expect('').toMatchSnapshot() + }) + it('snapshot', () => { + expect('\nsome content\n').toMatchSnapshot() + }) + }) + + // same name it + describe('__fixtures__/file.spec.ts 2', () => { + it('snapshot_1', () => { + expect('').toMatchSnapshot() + }) + it('snapshot_2', () => { + expect('').toMatchSnapshot() + }) + it('snapshot', () => { + expect('').toMatchSnapshot() + }) + }) + // same name expect + it('snapshot_2', () => { + expect('').toMatchSnapshot() + }) +}) + +describe('fixture2', () => { + it('__fixtures__/file.spec.ts 4', () => { + expect('').toMatchSnapshot() + expect('').toMatchSnapshot() + }) +}) + +it('fixture2', () => { + expect('').toMatchSnapshot() +}) diff --git a/test/e2e/utils/downloadSetup.ts b/test/e2e/utils/downloadSetup.ts index e5c810c..0d9dee3 100644 --- a/test/e2e/utils/downloadSetup.ts +++ b/test/e2e/utils/downloadSetup.ts @@ -1,7 +1,7 @@ import { download } from '@vscode/test-electron' -import type { GlobalSetupContext } from 'vitest/node' +import type { TestProject } from 'vitest/node' -export default async function downloadVscode({ provide }: GlobalSetupContext) { +export default async function downloadVscode({ provide }: TestProject) { if (process.env.VSCODE_E2E_DOWNLOAD_PATH) provide('executablePath', process.env.VSCODE_E2E_DOWNLOAD_PATH) else provide('executablePath', await download()) -- 2.51.2 From 3ddf6c4caa029729e25e419a67709a0cfb94b394 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Fri, 27 Mar 2026 12:04:21 +0100 Subject: [PATCH 35/64] chore: release v1.50.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fb3dc69..c76c130 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "explorer", "displayName": "Vitest", - "version": "1.48.2", + "version": "1.50.0", "description": "A Vite-native testing framework. It's fast!", "categories": [ "Testing" -- 2.51.2 From 1a759e8aa705e8626130f1e4cf1dbe3d3368982f Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Fri, 27 Mar 2026 12:53:12 +0100 Subject: [PATCH 36/64] chore: cleanup --- packages/extension/src/extension.ts | 2 +- packages/extension/src/utils.ts | 23 +++++------------------ 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/packages/extension/src/extension.ts b/packages/extension/src/extension.ts index bf8bfdd..0de203e 100644 --- a/packages/extension/src/extension.ts +++ b/packages/extension/src/extension.ts @@ -20,7 +20,7 @@ import { ExtensionState } from './state' import { TagsManager } from './tagsManager' import { TestTree } from './testTree' import { getTestData, TestFile } from './testTreeData' -import { clearCachedRuntime, debounce, showVitestError } from './utils' +import { debounce, showVitestError } from './utils' import './polyfills' import { SnapshotEntryTool } from './snapshot/tools' import { SnapshotDocumentSymbolProvider } from './snapshot/documentSymbolProvider' diff --git a/packages/extension/src/utils.ts b/packages/extension/src/utils.ts index 749dc44..cd0cea2 100644 --- a/packages/extension/src/utils.ts +++ b/packages/extension/src/utils.ts @@ -41,16 +41,6 @@ export function debounce void>(cb: T, wait = 20) { return (callable) } -// port from nanoid -// https://github.com/ai/nanoid -const urlAlphabet = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict' -export function nanoid(size = 21) { - let id = '' - let i = size - while (i--) id += urlAlphabet[(Math.random() * 64) | 0] - return id -} - export function waitUntilExists(file: string, timeoutMs = 5000) { return new Promise((resolve, reject) => { const timeout = setTimeout(() => { @@ -71,11 +61,6 @@ const pathToRuntime: { node?: string } = {} -export function clearCachedRuntime() { - pathToRuntime.deno = undefined - pathToRuntime.node = undefined -} - // based on https://github.com/microsoft/playwright-vscode/blob/main/src/utils.ts#L144 export async function findRuntimeExecutable( runtime: 'node' | 'deno', @@ -83,9 +68,11 @@ export async function findRuntimeExecutable( ): Promise { if (getConfig().nodeExecutable) // if empty string, keep as undefined - pathToRuntime[runtime] = getConfig().nodeExecutable || undefined + pathToRuntime[runtime] = getConfig().nodeExecutable - if (pathToRuntime[runtime]) return pathToRuntime[runtime] + if (pathToRuntime[runtime]) { + return pathToRuntime[runtime] + } // Stage 1: Try to find Node.js via process.env.PATH let node: string | null = await which(runtime, { nothrow: true }) @@ -99,7 +86,7 @@ export async function findRuntimeExecutable( node ??= await findRuntimeViaShell(runtime, cwd) if (!node) { - const msg = `Unable to find 'node' executable.\nMake sure to have Node.js installed and available in your PATH.\nCurrent PATH: '${process.env.PATH}'.` + const msg = `Unable to find '${runtime}' executable.\nMake sure to have ${runtime} installed and available in your PATH.\nCurrent PATH: '${process.env.PATH}'.` log.error(msg) throw new Error(msg) } -- 2.51.2 From 367abe54c957527645c555e5e9bccfeacf27e651 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Fri, 27 Mar 2026 19:47:08 +0100 Subject: [PATCH 37/64] docs: turn config into table --- README.md | 58 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index b6e25c3..d395024 100644 --- a/README.md +++ b/README.md @@ -73,40 +73,42 @@ You can identify if your config is loaded by the extension with `process.env.VIT These options are resolved relative to the [workspace file](https://code.visualstudio.com/docs/editor/workspaces#_multiroot-workspaces) if there is one. If you have a single folder open in Visual Studio Code, paths will be resolved relative to that folder. If there are multiple folders, but there is no workspace file, then paths are resolved as is (so they should be absolute) - this can happen if you change your user config to have multiple folders. -- `vitest.rootConfig`: The path to your root config file. If you have several Vitest configs, consider using a [Vitest workspace](https://vitest.dev/guide/workspace). -- `vitest.workspaceConfig`: The path to the [Vitest workspace](https://vitest.dev/guide/workspace) config file. You can only have a single workspace config per VSCode workspace. -- `vitest.ignoreWorkspace`: Ignores the workspace resolution step. The extension will only look for `vitest.config` files. -- `vitest.configSearchPatternInclude`: [Glob pattern](https://code.visualstudio.com/docs/editor/glob-patterns) that should be used when this extension looks for config files. Note that this is applied to _config_ files, not test files inside configs. Default: `**/*{vite,vitest}*.config*.{ts,js,mjs,cjs,cts,mts}`. -- `vitest.configSearchPatternExclude`: [Glob pattern](https://code.visualstudio.com/docs/editor/glob-patterns) that should be ignored when this extension looks for config files. Note that this is applied to _config_ files, not test files inside configs. Default: `{**/node_modules/**,**/vendor/**,**/.*/**,*.d.ts}`. If the extension cannot find Vitest, please open an issue. -- `vitest.runtime`: The default runtime to run tests in. Supported: `auto` (default) `node` and `deno`. If auto, the extension will looks for a `deno.enabled` config flag or a `deno.json` file in the root folder. -- `vitest.shellType`: The method the extension uses to spawn a long-running Vitest process. This is particularly useful if you are using a custom shell script to set up the environment. When using the `terminal` shell type, the websocket connection will be established. Can either be `terminal` or `child_process`. Default: `child_process`. -- `vitest.nodeExecutable`: The path to the Node.js executable. If not assigned, tries to find Node.js path via a PATH variable or a `which` command. This is applied only when `vitest.shellType` is `child_process` (the default). -- `vitest.nodeExecArgs`: The arguments to pass to the Node.js executable. This is applied only when `vitest.shellType` is `child_process` (the default). -- `vitest.terminalShellPath`: The path to the shell executable. This is applied only when `vitest.shellType` is `terminal`. -- `vitest.terminalShellArgs`: The arguments to pass to the shell executable. This is applied only when `vitest.shellType` is `terminal`. -- `vitest.debuggerPort`: Port that the debugger will be attached to. By default uses 9229 or tries to find a free port if it's not available. -- `vitest.debuggerAddress`: TCP/IP address of process to be debugged. Default: localhost -- `vitest.cliArguments`: Additional arguments to pass to the Vitest CLI. Note that some arguments will be ignored: `watch`, `reporter`, `api`, and `ui`. Example: `--mode=staging` -- `vitest.showImportsDuration`: Show how long it took to import and transform the modules. When hovering, the extension provides more diagnostics. -- `vitest.watchOnStartup`: Keep Vitest server running in the background at all times automatically on startup, rerunning tests when files change (default: `false`). This is the same as enabling continuous run. +| Key | Description | Type | Default | +|-----|-------------|------|---------| +| `vitest.rootConfig` | The path to your root config file. If you have several Vitest configs, consider using a [Vitest workspace](https://vitest.dev/guide/workspace). | `string` | — | +| `vitest.workspaceConfig` | The path to the [Vitest workspace](https://vitest.dev/guide/workspace) config file. You can only have a single workspace config per VSCode workspace. | `string` | — | +| `vitest.ignoreWorkspace` | Ignores the workspace resolution step. The extension will only look for `vitest.config` files. | `boolean` | — | +| `vitest.configSearchPatternInclude` | [Glob pattern](https://code.visualstudio.com/docs/editor/glob-patterns) used when looking for config files. Applied to _config_ files, not test files inside configs. | `string` | `**/*{vite,vitest}*.config*.{ts,js,mjs,cjs,cts,mts}` | +| `vitest.configSearchPatternExclude` | [Glob pattern](https://code.visualstudio.com/docs/editor/glob-patterns) ignored when looking for config files. Applied to _config_ files, not test files inside configs. If the extension cannot find Vitest, please open an issue. | `string` | `{**/node_modules/**, **/vendor/**, **/.*/**, *.d.ts}` | +| `vitest.runtime` | The default runtime to run tests in. Supported: `auto`, `node`, `deno`. If `auto`, the extension looks for a `deno.enabled` config flag or a `deno.json` file in the root folder. | `string` | `auto` | +| `vitest.shellType` | The method the extension uses to spawn a Vitest process. Useful if you use a custom shell script to set up the environment. When using `terminal`, a websocket connection is established. | `"child_process" \| "terminal"` | `child_process` | +| `vitest.nodeExecutable` | The path to the Node.js executable. If not set, tries to find it via `PATH` or `which`. Only applies when `vitest.shellType` is `child_process`. | `string` | — | +| `vitest.nodeExecArgs` | Arguments to pass to the Node.js executable. Only applies when `vitest.shellType` is `child_process`. | `string[]` | — | +| `vitest.terminalShellPath` | The path to the shell executable. Only applies when `vitest.shellType` is `terminal`. | `string` | — | +| `vitest.terminalShellArgs` | Arguments to pass to the shell executable. Only applies when `vitest.shellType` is `terminal`. | `string[]` | — | +| `vitest.debuggerPort` | Port the debugger will be attached to. Uses `9229` or finds a free port if unavailable. | `number` | `9229` | +| `vitest.debuggerAddress` | TCP/IP address of the process to be debugged. | `string` | `localhost` | +| `vitest.cliArguments` | Additional arguments to pass to the Vitest CLI. Note: `watch`, `reporter`, `api`, and `ui` are ignored. Example: `--mode=staging` | `string` | — | +| `vitest.showImportsDuration` | Show how long it took to import and transform modules. Hovering provides more diagnostics. | `boolean` | — | +| `vitest.watchOnStartup` | Keep Vitest running in the background on startup, rerunning tests when files change. Same as enabling continuous run. | `boolean` | `false` | > 💡 The `vitest.nodeExecutable` and `vitest.nodeExecArgs` settings are used as `execPath` and `execArgv` when spawning a new `child_process`, and as `runtimeExecutable` and `runtimeArgs` when [debugging a test](https://github.com/microsoft/vscode-js-debug/blob/main/OPTIONS.md). > The `vitest.terminalShellPath` and `vitest.terminalShellArgs` settings are used as `shellPath` and `shellArgs` when creating a new [terminal](https://code.visualstudio.com/api/references/vscode-api#Terminal) ### Other Options -- `vitest.filesWatcherInclude`: Glob pattern for the watcher that triggers a test rerun or collects changes. Default: `**/*` -- `vitest.vitestPackagePath`: The path to a `package.json` file of a Vitest executable (it's usually inside `node_modules`) in case the extension cannot find it. It will be used to resolve Vitest API paths. This should be used as a last resort fix. -- `vitest.nodeEnv`: Environment passed to the runner process in addition to - `process.env` -- `vitest.debugNodeEnv`: Environment passed to the runner process in addition to `process.env` and `vitest.nodeEnv` when debugging tests -- `vitest.debugExclude`: Excludes files matching specified glob patterns from debugging. Default: - `["/**", "vitest/dist/**"]` -- `vitest.debugOutFiles`: If source maps are enabled, these glob patterns specify the generated JavaScript files. If a pattern starts with `!` the files are excluded. If not specified, the generated code is expected in the same directory as its source. -- `vitest.logLevel`: How verbose should the logger be in the "Output" channel. Default: `info` -- `vitest.applyDiagnostic`: Show a squiggly line where the error was thrown. This also enables the error count in the File Tab. Default: `true` -- `vitest.showInlineConsoleLog`: Show console.log messages inline in the editor next to the code that produced them. When disabled, console logs will still appear in the test output but not inline. Default: `true` -- `vitest.forceCancelTimeout`: When the 'Stop' button is clicked, the extension tries to stop tests gracefully so they don't keep any hanging processes. By default, if tests didn't finish in 1 second, the extension will kill any Vitest process which may keep your test's 'child_process' alive. You can configure the timeout with this option, but consider using [`signal`](https://vitest.dev/guide/test-context#signal) API inside of your tests instead. +| Key | Description | Type | Default | +|-----|-------------|------|---------| +| `vitest.filesWatcherInclude` | Glob pattern for the watcher that triggers a test rerun or collects changes. | `string` | `**/*` | +| `vitest.vitestPackagePath` | Path to a `package.json` of a Vitest executable (usually in `node_modules`) if the extension cannot find it. Used to resolve Vitest API paths. Last resort fix. | `string` | — | +| `vitest.nodeEnv` | Environment passed to the runner process in addition to `process.env`. | `object` | — | +| `vitest.debugNodeEnv` | Environment passed to the runner process in addition to `process.env` and `vitest.nodeEnv` when debugging tests. | `object` | — | +| `vitest.debugExclude` | Glob patterns for files to exclude from debugging. | `string[]` | `["/**", "vitest/dist/**"]` | +| `vitest.debugOutFiles` | If source maps are enabled, glob patterns specifying the generated JavaScript files. Patterns starting with `!` exclude files. If not set, generated code is expected alongside its source. | `string[]` | — | +| `vitest.logLevel` | How verbose the logger is in the "Output" channel. | `string` | `info` | +| `vitest.applyDiagnostic` | Show a squiggly line where the error was thrown. Also enables the error count in the File Tab. | `boolean` | `true` | +| `vitest.showInlineConsoleLog` | Show `console.log` messages inline in the editor next to the code that produced them. Logs still appear in test output when disabled. | `boolean` | `true` | +| `vitest.forceCancelTimeout` | Milliseconds to wait for tests to stop gracefully after clicking "Stop" before force-killing Vitest. Consider using the [`signal`](https://vitest.dev/guide/test-context#signal) API in tests instead. | `number` | `1000` | ### Commands -- 2.51.2 From 94caa7c6bd6c1b45c7a4402bf28d6a4bfb67eb12 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Mon, 30 Mar 2026 11:45:08 +0200 Subject: [PATCH 38/64] chore(fmt): ignore md --- .oxfmtrc.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.oxfmtrc.json b/.oxfmtrc.json index 9da944d..d2de8c0 100644 --- a/.oxfmtrc.json +++ b/.oxfmtrc.json @@ -1,6 +1,6 @@ { "$schema": "./node_modules/oxfmt/configuration_schema.json", - "ignorePatterns": [], + "ignorePatterns": ["*.md"], "semi": false, "singleQuote": true } -- 2.51.2 From a0fe0c6944ff32c554e764819acc76bcb489801a Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 1 Apr 2026 13:12:48 +0200 Subject: [PATCH 39/64] fix: support coverage preview in Vitest 3 (#771) --- .vscode/launch.json | 6 +++++- packages/worker-legacy/src/reporter.ts | 9 +++++++-- packages/worker-legacy/src/worker.ts | 4 ++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index f71f6a1..aa81747 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,11 @@ "name": "Run Extension Basic Sample", "type": "extensionHost", "request": "launch", - "args": ["--extensionDevelopmentPath=${workspaceFolder}", "${workspaceFolder}/samples/basic"], + "args": [ + "--disable-extensions", + "--extensionDevelopmentPath=${workspaceFolder}", + "${workspaceFolder}/samples/basic" + ], "outFiles": ["${workspaceFolder}/dist/**/*.js"] }, { diff --git a/packages/worker-legacy/src/reporter.ts b/packages/worker-legacy/src/reporter.ts index 9c7f5c4..976a935 100644 --- a/packages/worker-legacy/src/reporter.ts +++ b/packages/worker-legacy/src/reporter.ts @@ -183,7 +183,7 @@ export class VSCodeReporter implements Reporter { async onFinished( files?: RunnerTestFile[], errors: unknown[] = this.vitest.state.getUnhandledErrors(), - coverage?: unknown, + coverage?: any, ) { const collecting = this.collecting @@ -214,7 +214,12 @@ export class VSCodeReporter implements Reporter { } nextTick(() => { - this.rpc.onTestRunEnd(files || [], output, collecting, coverage) + this.rpc.onTestRunEnd( + files || [], + output, + collecting, + coverage ? coverage.toJSON() : undefined, + ) }) } diff --git a/packages/worker-legacy/src/worker.ts b/packages/worker-legacy/src/worker.ts index 3c3724d..98d5d0c 100644 --- a/packages/worker-legacy/src/worker.ts +++ b/packages/worker-legacy/src/worker.ts @@ -141,6 +141,10 @@ export class ExtensionWorker implements ExtensionWorkerTransport { ) { // @ts-expect-error private method in Vitest <=2.1.5 await this.vitest.initBrowserProviders?.() + if (this.vitest.config.coverage.enabled) { + await (this.vitest as any).initCoverageProvider?.() + await (this.vitest as any).coverageProvider?.clean(this.vitest.config.coverage.clean) + } const specs = await this.resolveTestSpecs(specsOrPaths) -- 2.51.2 From 3b6768a7e779200b1aaadfe26f3f28580956f8d7 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Wed, 1 Apr 2026 13:13:07 +0200 Subject: [PATCH 40/64] chore: release v1.50.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c76c130..424fc1b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "explorer", "displayName": "Vitest", - "version": "1.50.0", + "version": "1.50.1", "description": "A Vite-native testing framework. It's fast!", "categories": [ "Testing" -- 2.51.2 From edd442e56799c8a715c8cad9067b24ca5c45fe52 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Thu, 16 Apr 2026 09:40:01 +0200 Subject: [PATCH 41/64] fix: only show the copy button if current file is a test Fixes #774 --- package.json | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 424fc1b..af04100 100644 --- a/package.json +++ b/package.json @@ -159,25 +159,22 @@ } ], "menus": { - "testing/item/result": [ - { - "command": "vitest.copyTestItemErrors" - } - ], "testing/message/content": [ { - "command": "vitest.copyErrorOutput" + "command": "vitest.copyErrorOutput", + "when": "resourcePath in vitest.testFiles" } ], "testing/message/context": [ { - "command": "vitest.copyErrorOutput" + "command": "vitest.copyErrorOutput", + "when": "resourcePath in vitest.testFiles" } ], "editor/title/context": [ { "command": "vitest.revealInTestExplorer", - "when": "vitest.testFiles && resourcePath in vitest.testFiles", + "when": "resourcePath in vitest.testFiles", "group": "vitest" }, { -- 2.51.2 From 096472d77fb02c5f8f5042f0664c75a1ab544005 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Fri, 17 Apr 2026 08:22:23 +0200 Subject: [PATCH 42/64] fix: don't filter tests when running in a folder (#777) --- packages/extension/src/runner.ts | 57 +++++++++++++++++++++++----- packages/shared/src/index.ts | 10 ++++- packages/worker-legacy/src/worker.ts | 2 +- packages/worker/src/runner.ts | 7 +++- packages/worker/src/watcher.ts | 23 +++++++---- 5 files changed, 78 insertions(+), 21 deletions(-) diff --git a/packages/extension/src/runner.ts b/packages/extension/src/runner.ts index 15e63fc..c49764a 100644 --- a/packages/extension/src/runner.ts +++ b/packages/extension/src/runner.ts @@ -14,7 +14,7 @@ import * as vscode from 'vscode' import { getConfig } from './config' import { coverageContext } from './coverage' import { log } from './log' -import { getTestData, TestCase, TestFile, TestFolder } from './testTreeData' +import { getTestData, TestCase, TestFile, TestFolder, TestSuite } from './testTreeData' import { getErrorMessage, showVitestError } from './utils' export class TestRunner extends vscode.Disposable { @@ -212,7 +212,13 @@ export class TestRunner extends vscode.Disposable { else log.info( `Running ${files.length} file(s):`, - files.map((f) => this.relative(f)), + files.map((f) => { + if (typeof f === 'string') return this.relative(f) + const parts = [this.relative(f)] + if (f[0]) parts.push(`[${f[0]}]`) + if (f[2]?.testNamePattern) parts.push(`(${f[2].testNamePattern})`) + return parts.join(' ') + }), ) await runTests(files, testNamePatern) } @@ -376,7 +382,7 @@ export class ContinuousTestRunner extends TestRunner { log.info('[RUNNER]', 'Watching all test files') } else { const files = getTestFiles(include) - const testNamePatern = formatTestPattern(include) + const testNamePatern = formatContinuousTestPattern(include) await this.handle.rpc.watchTests(files, testNamePatern) log.info( '[RUNNER]', @@ -600,7 +606,7 @@ function getTestFiles(tests: readonly vscode.TestItem[]): string[] | ExtensionTe ] } const testSpecs: ExtensionTestSpecification[] = [] - const testFiles = new Set() + const testFiles = new Map() for (const test of tests) { const fsPath = normalize(test.uri!.fsPath) const data = getTestData(test) @@ -608,19 +614,38 @@ function getTestFiles(tests: readonly vscode.TestItem[]): string[] | ExtensionTe if (data instanceof TestFolder) continue const project = data instanceof TestFile ? data.project : data.file.project const key = `${project}\0${fsPath}` - if (testFiles.has(key)) continue - testFiles.add(key) - testSpecs.push([project, fsPath]) + if (testFiles.has(key)) { + const specification = testFiles.get(key)! + if (data instanceof TestCase || data instanceof TestSuite) { + const options = specification[2]! + if (options.testNamePattern === '.+') { + options.testNamePattern = data.getTestNamePattern() + } else { + options.testNamePattern += `|${data.getTestNamePattern()}` + } + } + continue + } + const specification: ExtensionTestSpecification = [ + project, + fsPath, + { + // run every test by default + testNamePattern: data instanceof TestFile ? '.+' : data.getTestNamePattern(), + }, + ] + testFiles.set(key, specification) + testSpecs.push(specification) } return testSpecs } -function formatTestPattern(tests: readonly vscode.TestItem[], patterns: string[] = []) { +function formatContinuousTestPattern(tests: readonly vscode.TestItem[], patterns: string[] = []) { for (const test of tests) { const data = getTestData(test)! // file or a folder, try to include every test in there if (!('getTestNamePattern' in data)) { - formatTestPattern( + formatContinuousTestPattern( Array.from(test.children, (t) => t[1]), patterns, ) @@ -632,11 +657,23 @@ function formatTestPattern(tests: readonly vscode.TestItem[], patterns: string[] return patterns.join('|') } +function formatTestPattern(tests: readonly vscode.TestItem[], patterns: string[] = []) { + for (const test of tests) { + const data = getTestData(test)! + if (!('getTestNamePattern' in data)) { + return + } + patterns.push(data.getTestNamePattern()) + } + if (!patterns.length) return undefined + return patterns.join('|') +} + function formatTestOutput(output: string) { return output.replace(/(?' + if (!items?.length) return '' return items.map((p) => `"${p.label}"`).join(', ') } diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index d23e061..63dd8c6 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -11,7 +11,15 @@ export { normalizeDriveLetter, } from './utils' -export type ExtensionTestSpecification = [project: string, file: string] +export type ExtensionTestSpecificationOptions = { + testNamePattern?: string +} + +export type ExtensionTestSpecification = [ + project: string, + file: string, + options?: ExtensionTestSpecificationOptions, +] export interface ExtensionTestFileMetadata { project: string diff --git a/packages/worker-legacy/src/worker.ts b/packages/worker-legacy/src/worker.ts index 98d5d0c..87e22d1 100644 --- a/packages/worker-legacy/src/worker.ts +++ b/packages/worker-legacy/src/worker.ts @@ -70,7 +70,7 @@ export class ExtensionWorker implements ExtensionWorkerTransport { return (this.vitest as any).getCoreWorkspaceProject() } - public async collectTests(files: [projectName: string, filepath: string][]) { + public async collectTests(files: ExtensionTestSpecification[]) { const specifications: [project: WorkspaceProject, filepath: string][] = [] for (const [projectName, filepath] of files) { diff --git a/packages/worker/src/runner.ts b/packages/worker/src/runner.ts index 35c5e39..0b85716 100644 --- a/packages/worker/src/runner.ts +++ b/packages/worker/src/runner.ts @@ -106,9 +106,12 @@ export class ExtensionWorkerRunner { ): Promise { const specifications: TestSpecification[] = [] files.forEach((file) => { - const [projectName, filepath] = file + const [projectName, filepath, options] = file const project = this.vitest.getProjectByName(projectName) - specifications.push(project.createSpecification(filepath)) + const specification = project.createSpecification(filepath) + // @ts-expect-error testNamePattern is readonly and supported only in v4.1 + specification.testNamePattern = options?.testNamePattern + specifications.push(specification) }) return specifications } diff --git a/packages/worker/src/watcher.ts b/packages/worker/src/watcher.ts index 6a4e6b1..3276a69 100644 --- a/packages/worker/src/watcher.ts +++ b/packages/worker/src/watcher.ts @@ -1,4 +1,7 @@ -import type { ExtensionTestSpecification } from 'vitest-vscode-shared' +import type { + ExtensionTestSpecification, + ExtensionTestSpecificationOptions, +} from 'vitest-vscode-shared' import type { TestSpecification, Vitest } from 'vitest/node' import type { ExtensionWorkerRunner } from './runner' import { createQueuedHandler } from 'vitest-vscode-shared' @@ -6,7 +9,7 @@ import { createQueuedHandler } from 'vitest-vscode-shared' export class ExtensionWorkerWatcher { private enabled = false private trackingEveryFile = false - private trackedTestItems: Record = {} + private trackedTestItems: Record> = {} private trackedDirectories: string[] = [] constructor( @@ -42,11 +45,17 @@ export class ExtensionWorkerWatcher { const project = specification.project.name const files = this.trackedTestItems[project] - if (!files?.length) { + if (!files?.size) { return false } - return files.includes(specification.moduleId) + const options = files.get(specification.moduleId) + if (options) { + // @ts-expect-error testNamePattern is readonly and available only in v4.1 + specification.testNamePattern = options.testNamePattern + return true + } + return false } trackTestItems(filesOrDirectories: ExtensionTestSpecification[] | string[]) { @@ -54,11 +63,11 @@ export class ExtensionWorkerWatcher { if (typeof filesOrDirectories[0] === 'string') { this.trackedDirectories = filesOrDirectories as string[] } else { - for (const [project, file] of filesOrDirectories) { + for (const [project, file, options] of filesOrDirectories as ExtensionTestSpecification[]) { if (!this.trackedTestItems[project]) { - this.trackedTestItems[project] = [] + this.trackedTestItems[project] = new Map() } - this.trackedTestItems[project].push(file) + this.trackedTestItems[project].set(file, options || {}) } } } -- 2.51.2 From 4a7126b6493ef229b19871262bea6bba5f3ecd99 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Fri, 17 Apr 2026 08:22:47 +0200 Subject: [PATCH 43/64] chore: release v1.50.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index af04100..57f411a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "explorer", "displayName": "Vitest", - "version": "1.50.1", + "version": "1.50.2", "description": "A Vite-native testing framework. It's fast!", "categories": [ "Testing" -- 2.51.2 From 71cf6ab47829400df1c71ce030e0538473d9e536 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Apr 2026 13:29:56 +0200 Subject: [PATCH 44/64] fix: expand VS Code predefined variables in all path-valued settings (#779) Co-authored-by: sheremet-va <16173870+sheremet-va@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- packages/extension/src/config.ts | 51 +++++++++++++++++++--------- test/unit/config.test.ts | 57 ++++++++++++++++++++++++++++++-- 2 files changed, 91 insertions(+), 17 deletions(-) diff --git a/packages/extension/src/config.ts b/packages/extension/src/config.ts index 55e605b..60cf85e 100644 --- a/packages/extension/src/config.ts +++ b/packages/extension/src/config.ts @@ -1,12 +1,36 @@ import type { WorkspaceConfiguration, WorkspaceFolder } from 'vscode' import { homedir } from 'node:os' -import { dirname, isAbsolute, resolve } from 'node:path' +import { dirname, isAbsolute, resolve, sep } from 'node:path' import * as vscode from 'vscode' import { configGlob } from './constants' export const extensionId = 'vitest.explorer' export const testControllerId = 'vitest' +export function substituteVariables(value: string, workspaceFolder?: WorkspaceFolder): string { + const folder = workspaceFolder ?? vscode.workspace.workspaceFolders?.[0] + return ( + value + // eslint-disable-next-line no-template-curly-in-string + .replace(/\$\{workspaceFolder\}/g, folder?.uri.fsPath ?? '') + // eslint-disable-next-line no-template-curly-in-string + .replace(/\$\{workspaceFolderBasename\}/g, folder?.name ?? '') + // eslint-disable-next-line no-template-curly-in-string + .replace(/\$\{userHome\}/g, homedir()) + // eslint-disable-next-line no-template-curly-in-string + .replace(/\$\{env:([^}]+)\}/g, (_, name) => process.env[name] ?? '') + // eslint-disable-next-line no-template-curly-in-string + .replace(/\$\{pathSeparator\}/g, sep) + ) +} + +function resolvePathWithSubstitution(path: string | undefined, workspaceFolder?: WorkspaceFolder) { + return resolveConfigPath( + path ? substituteVariables(path, workspaceFolder) : path, + workspaceFolder, + ) +} + export function getConfigValue( rootConfig: WorkspaceConfiguration, folderConfig: WorkspaceConfiguration, @@ -42,14 +66,9 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) { get('configSearchPatternInclude', configGlob) || configGlob const vitestPackagePath = get('vitestPackagePath') - const resolvedVitestPackagePath = - workspaceFolder && vitestPackagePath - ? resolve( - workspaceFolder.uri.fsPath, - // eslint-disable-next-line no-template-curly-in-string - vitestPackagePath.replace('${workspaceFolder}', workspaceFolder.uri.fsPath), - ) - : vitestPackagePath + const resolvedVitestPackagePath = vitestPackagePath + ? resolvePathWithSubstitution(vitestPackagePath, workspaceFolder) + : vitestPackagePath const logLevel = get('logLevel', 'info') @@ -74,24 +93,24 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) { env: get>('nodeEnv', null), debugEnv: get>('debugNodeEnv', null), debugExclude: get('debugExclude'), - debugOutFiles, + debugOutFiles: debugOutFiles?.map((f) => substituteVariables(f, workspaceFolder)), filesWatcherInclude, runtime, forceCancelTimeout, watchOnStartup, terminalShellArgs, - terminalShellPath, + terminalShellPath: resolvePathWithSubstitution(terminalShellPath, workspaceFolder), shellType, applyDiagnostic, cliArguments, nodeExecArgs, vitestPackagePath: resolvedVitestPackagePath, - workspaceConfig: resolveConfigPath(workspaceConfig), - rootConfig: resolveConfigPath(rootConfigFile), + workspaceConfig: resolvePathWithSubstitution(workspaceConfig, workspaceFolder), + rootConfig: resolvePathWithSubstitution(rootConfigFile, workspaceFolder), configSearchPatternInclude, configSearchPatternExclude, ignoreWorkspace, - nodeExecutable: resolveConfigPath(nodeExecutable), + nodeExecutable: resolvePathWithSubstitution(nodeExecutable, workspaceFolder), disableWorkspaceWarning: get('disableWorkspaceWarning', false), debuggerPort: get('debuggerPort') || undefined, debuggerAddress: get('debuggerAddress', undefined) || undefined, @@ -101,11 +120,13 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) { } } -export function resolveConfigPath(path: string | undefined) { +export function resolveConfigPath(path: string | undefined, workspaceFolder?: WorkspaceFolder) { if (!path || isAbsolute(path)) return path if (path.startsWith('~/')) { return resolve(homedir(), path.slice(2)) } + // if a workspaceFolder was provided, resolve relative to it + if (workspaceFolder) return resolve(workspaceFolder.uri.fsPath, path) // if there is a workspace file, then it should be relative to it because // this option cannot be configured on a workspace folder level if (vscode.workspace.workspaceFile) diff --git a/test/unit/config.test.ts b/test/unit/config.test.ts index 639ce2c..47effe4 100644 --- a/test/unit/config.test.ts +++ b/test/unit/config.test.ts @@ -1,8 +1,61 @@ -import { resolve } from 'node:path' +import { resolve, sep } from 'node:path' import { homedir } from 'node:os' import { expect } from 'chai' -import { resolveConfigPath } from '../../packages/extension/src/config' +import type { Uri, WorkspaceFolder } from 'vscode' +import { resolveConfigPath, substituteVariables } from '../../packages/extension/src/config' + +function mockWorkspaceFolder(fsPath: string, name: string): WorkspaceFolder { + return { + uri: { fsPath } as Uri, + name, + index: 0, + } +} it('correctly resolves ~', () => { expect(resolveConfigPath('~/test')).to.equal(resolve(homedir(), 'test')) }) + +describe('substituteVariables', () => { + it('substitutes ${workspaceFolder}', () => { + const folder = mockWorkspaceFolder('/my/workspace', 'workspace') + // eslint-disable-next-line no-template-curly-in-string + expect(substituteVariables('${workspaceFolder}/src', folder)).to.equal('/my/workspace/src') + }) + + it('substitutes ${workspaceFolderBasename}', () => { + const folder = mockWorkspaceFolder('/my/workspace', 'myproject') + // eslint-disable-next-line no-template-curly-in-string + expect(substituteVariables('${workspaceFolderBasename}', folder)).to.equal('myproject') + }) + + it('substitutes ${userHome}', () => { + // eslint-disable-next-line no-template-curly-in-string + expect(substituteVariables('${userHome}/projects')).to.equal(`${homedir()}/projects`) + }) + + it('substitutes ${env:NAME}', () => { + process.env.TEST_VAR_VITEST_EXT = 'hello' + // eslint-disable-next-line no-template-curly-in-string + expect(substituteVariables('${env:TEST_VAR_VITEST_EXT}/path')).to.equal('hello/path') + delete process.env.TEST_VAR_VITEST_EXT + }) + + it('substitutes ${pathSeparator}', () => { + // eslint-disable-next-line no-template-curly-in-string + expect(substituteVariables('foo${pathSeparator}bar')).to.equal(`foo${sep}bar`) + }) + + it('substitutes multiple occurrences', () => { + const folder = mockWorkspaceFolder('/ws', 'myws') + // eslint-disable-next-line no-template-curly-in-string + expect( + substituteVariables('${workspaceFolder}/a/${workspaceFolderBasename}/b', folder), + ).to.equal('/ws/a/myws/b') + }) + + it('leaves unrecognized variables unchanged', () => { + // eslint-disable-next-line no-template-curly-in-string + expect(substituteVariables('${unknown}/path')).to.equal('${unknown}/path') + }) +}) -- 2.51.2 From 6ed8ab9f7da9e30b5c52dbf4b8d8518c1cead83c Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Mon, 27 Apr 2026 13:30:47 +0200 Subject: [PATCH 45/64] chore: release v1.50.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 57f411a..65b024c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "explorer", "displayName": "Vitest", - "version": "1.50.2", + "version": "1.50.3", "description": "A Vite-native testing framework. It's fast!", "categories": [ "Testing" -- 2.51.2 From 01987820c5a6b585e827ccb78c637831850abd13 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 5 May 2026 17:08:03 +0100 Subject: [PATCH 46/64] fix: hide logs if silent is configured (#782) --- packages/worker-legacy/src/reporter.ts | 74 ++++++++++++++++++++++++-- packages/worker/src/reporter.ts | 62 ++++++++++++++++++++- 2 files changed, 132 insertions(+), 4 deletions(-) diff --git a/packages/worker-legacy/src/reporter.ts b/packages/worker-legacy/src/reporter.ts index 976a935..7d7b873 100644 --- a/packages/worker-legacy/src/reporter.ts +++ b/packages/worker-legacy/src/reporter.ts @@ -1,7 +1,21 @@ import type { BirpcReturn } from 'birpc' -import type { ErrorWithDiff, RunnerTestFile, TaskResultPack, UserConsoleLog } from 'vitest' +import type { + ErrorWithDiff, + RunnerTask, + RunnerTestFile, + TaskResultPack, + UserConsoleLog, +} from 'vitest' import type { ExtensionWorkerEvents, ExtensionWorkerTransport } from 'vitest-vscode-shared' -import type { BrowserCommand, Vitest as VitestCore, WorkspaceProject } from 'vitest/node' +import type { + BrowserCommand, + TestCase, + TestModule, + TestResult, + TestSuite, + Vitest as VitestCore, + WorkspaceProject, +} from 'vitest/node' import type { Reporter } from 'vitest/reporters' import { Console } from 'node:console' import { nextTick } from 'node:process' @@ -17,6 +31,7 @@ export class VSCodeReporter implements Reporter { public rpc!: BirpcReturn private vitest!: VitestCore private setupFilePaths: string[] + private silent: boolean | 'passed-only' = false constructor(options: VSCodeReporterOptions) { this.setupFilePaths = options.setupFilePaths @@ -31,6 +46,7 @@ export class VSCodeReporter implements Reporter { onInit(vitest: VitestCore) { this.vitest = vitest + this.silent = vitest.config.silent const server = vitest.server.config.server this.setupFilePaths.forEach((setupFile) => { if (!server.fs.allow.includes(setupFile)) server.fs.allow.push(setupFile) @@ -95,7 +111,11 @@ export class VSCodeReporter implements Reporter { this.rpc = rpc } - onUserConsoleLog(log: UserConsoleLog) { + onUserConsoleLog(log: UserConsoleLog, taskState?: TestResult['state']) { + if (!this.shouldLog(log, taskState)) { + return + } + // Parse stack trace to extract file location for inline display const extendedLog = log as any if (log.origin) { @@ -118,6 +138,50 @@ export class VSCodeReporter implements Reporter { return this.rpc.onConsoleLog(extendedLog) } + onTestCaseResult(testCase: TestCase): void { + if (testCase.result().state === 'failed') { + this.logFailedTask(getRunnerTask(testCase)) + } + } + + onTestSuiteResult(testSuite: TestSuite): void { + if (testSuite.state() === 'failed') { + this.logFailedTask(getRunnerTask(testSuite)) + } + } + + onTestModuleEnd(testModule: TestModule): void { + if (testModule.state() === 'failed') { + this.logFailedTask(getRunnerTask(testModule)) + } + } + + private logFailedTask(task: RunnerTask) { + if (this.vitest.config.silent === 'passed-only') { + for (const log of task.logs || []) { + this.onUserConsoleLog(log, 'failed') + } + } + } + + shouldLog(log: UserConsoleLog, taskState?: TestResult['state']): boolean { + if (this.silent === true) { + return false + } + + if (this.silent === 'passed-only' && taskState !== 'failed') { + return false + } + + if (this.vitest.config.onConsoleLog) { + const shouldLog = this.vitest.config.onConsoleLog(log.content, log.type) + if (shouldLog === false) { + return false + } + } + return true + } + private logPromises = new Set>() sendTerminalLog(type: 'stderr' | 'stdout', message: string) { if (!this.rpc) { @@ -239,3 +303,7 @@ export class VSCodeReporter implements Reporter { function isPrimitive(value: unknown) { return value === null || (typeof value !== 'function' && typeof value !== 'object') } + +function getRunnerTask(value: any): RunnerTask { + return value.task +} diff --git a/packages/worker/src/reporter.ts b/packages/worker/src/reporter.ts index f447b34..d91536a 100644 --- a/packages/worker/src/reporter.ts +++ b/packages/worker/src/reporter.ts @@ -4,10 +4,14 @@ import type { BrowserCommand, Reporter, ResolvedConfig, + RunnerTask, RunnerTestFile, + TestCase, TestModule, TestProject, + TestResult, TestSpecification, + TestSuite, Vite, Vitest as VitestCore, } from 'vitest/node' @@ -24,6 +28,7 @@ export class VSCodeReporter implements Reporter { private debuggerAttached: boolean | undefined = undefined private coverageData: Record | undefined = undefined + private silent: boolean | 'passed-only' = false constructor(meta: WorkerInitMetadata, debug: WorkerRunnerOptions['debug']) { this.setupFilePaths = meta.setupFilePaths @@ -36,6 +41,7 @@ export class VSCodeReporter implements Reporter { onInit(vitest: VitestCore) { this.vitest = vitest this.configureAttachDebugging(vitest) + this.silent = vitest.config.silent vitest.projects.forEach((project) => { this.ensureSetupFileIsAllowed(project.vite.config) @@ -59,7 +65,11 @@ export class VSCodeReporter implements Reporter { project.browser!.parent.commands.__vscode_waitForDebugger = __vscode_waitForDebugger } - onUserConsoleLog(log: UserConsoleLog) { + onUserConsoleLog(log: UserConsoleLog, taskState?: TestResult['state']) { + if (!this.shouldLog(log, taskState)) { + return + } + // Parse stack trace to extract file location for inline display const extendedLog = log as any if (log.origin) { @@ -89,6 +99,52 @@ export class VSCodeReporter implements Reporter { return this.rpc.onConsoleLog(extendedLog) } + onTestCaseResult(testCase: TestCase): void { + if (testCase.result().state === 'failed') { + this.logFailedTask(getRunnerTask(testCase)) + } + } + + onTestSuiteResult(testSuite: TestSuite): void { + if (testSuite.state() === 'failed') { + this.logFailedTask(getRunnerTask(testSuite)) + } + } + + onTestModuleEnd(testModule: TestModule): void { + if (testModule.state() === 'failed') { + this.logFailedTask(getRunnerTask(testModule)) + } + } + + protected logFailedTask(task: RunnerTask): void { + if (this.silent === 'passed-only') { + for (const log of task.logs || []) { + this.onUserConsoleLog(log, 'failed') + } + } + } + + shouldLog(log: UserConsoleLog, taskState?: TestResult['state']): boolean { + if (this.silent === true) { + return false + } + + if (this.silent === 'passed-only' && taskState !== 'failed') { + return false + } + + if (this.vitest.config.onConsoleLog) { + const task = log.taskId ? this.vitest.state.idMap.get(log.taskId) : undefined + const entity = task && this.vitest.state.getReportedEntity(task) + const shouldLog = this.vitest.config.onConsoleLog(log.content, log.type, entity) + if (shouldLog === false) { + return false + } + } + return true + } + onTaskUpdate(packs: RunnerTaskResultPack[]) { this.rpc.onTaskUpdate( // remove the meta because it is not used, @@ -213,3 +269,7 @@ export class VSCodeReporter implements Reporter { function getEntityJSONTask(entity: TestModule) { return (entity as any).task as RunnerTestFile } + +function getRunnerTask(value: any): RunnerTask { + return value.task +} -- 2.51.2 From aa0b4546c002e4dc7bf7b76a168fc83c44ca5318 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 5 May 2026 17:08:15 +0100 Subject: [PATCH 47/64] chore: release v1.50.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 65b024c..ded090f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "explorer", "displayName": "Vitest", - "version": "1.50.3", + "version": "1.50.4", "description": "A Vite-native testing framework. It's fast!", "categories": [ "Testing" -- 2.51.2 From da5f5911d0de80dbf663beb37966da1c0ce8381f Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 19 May 2026 10:02:56 +0200 Subject: [PATCH 48/64] fix: don't crash with 'passed-only' Fixes #786 --- packages/worker/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts index 7cae4ba..d5c5583 100644 --- a/packages/worker/src/index.ts +++ b/packages/worker/src/index.ts @@ -140,7 +140,7 @@ export async function initVitest( ) ;((vitest as any).reporters as Reporter[]).forEach((reporter) => { if (!(reporter instanceof VSCodeReporter)) { - reporter.onUserConsoleLog = undefined + reporter.onUserConsoleLog = () => {} } }) -- 2.51.2 From 90db46fd805fcce02a2774fc8d176c4b4e0af999 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Tue, 19 May 2026 10:03:07 +0200 Subject: [PATCH 49/64] chore: release v1.50.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ded090f..d5defe1 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "explorer", "displayName": "Vitest", - "version": "1.50.4", + "version": "1.50.5", "description": "A Vite-native testing framework. It's fast!", "categories": [ "Testing" -- 2.51.2 From 7d56e9d8c382a55e25e6f95aa3fe209acaec50a2 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Thu, 21 May 2026 09:20:24 +0200 Subject: [PATCH 50/64] chore: update happy-dom (#787) --- .github/workflows/ci.yml | 26 +- .github/workflows/issue-close-require.yml | 31 +- .github/workflows/issue-labeled.yml | 18 +- .github/workflows/lock-closed-issues.yml | 2 +- .github/workflows/publish.yml | 19 +- pnpm-lock.yaml | 354 ++++-------------- .../monorepo-vitest-workspace/package.json | 2 +- .../packages/react copy/package.json | 2 +- .../packages/react/package.json | 2 +- samples/projects/package.json | 2 +- samples/vue/package.json | 2 +- 11 files changed, 133 insertions(+), 327 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f463447..6ae97ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,11 +11,11 @@ jobs: runs-on: macos-latest steps: - - uses: actions/checkout@v3 - - uses: pnpm/action-setup@v3 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3 + - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3 with: node-version: ${{ matrix.node-version }} cache: pnpm @@ -34,16 +34,16 @@ jobs: node-version: [22.x] steps: - - uses: actions/checkout@v3 - - uses: pnpm/action-setup@v3 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3 + - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3 with: node-version: ${{ matrix.node-version }} cache: pnpm - - uses: denoland/setup-deno@v2 + - uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2 with: deno-version: v2.x @@ -56,7 +56,7 @@ jobs: - name: test-e2e run: pnpm test-e2e - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 if: always() with: name: 'test-results-${{ matrix.os }}' @@ -71,16 +71,16 @@ jobs: node-version: [22.x] steps: - - uses: actions/checkout@v3 - - uses: pnpm/action-setup@v3 + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3 + - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3 with: node-version: ${{ matrix.node-version }} cache: pnpm - - uses: denoland/setup-deno@v2 + - uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2 with: deno-version: v2.x @@ -96,7 +96,7 @@ jobs: - name: test-e2e run: pnpm test-e2e:legacy - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 if: always() with: name: 'test-results-legacy-${{ matrix.os }}' diff --git a/.github/workflows/issue-close-require.yml b/.github/workflows/issue-close-require.yml index 66347d6..7fe1d13 100644 --- a/.github/workflows/issue-close-require.yml +++ b/.github/workflows/issue-close-require.yml @@ -4,14 +4,35 @@ on: schedule: - cron: '0 0 * * *' +permissions: + issues: write + jobs: close-issues: + if: github.repository == 'vitest-dev/vscode' runs-on: ubuntu-latest steps: - name: needs reproduction - uses: actions-cool/issues-helper@v3 + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 with: - actions: close-issues - token: ${{ secrets.GITHUB_TOKEN }} - labels: needs reproduction - inactive-day: 3 + script: | + const inactiveDays = 3; + const cutoff = Date.now() - inactiveDays * 24 * 60 * 60 * 1000; + const issues = await github.paginate(github.rest.issues.listForRepo, { + owner: context.repo.owner, + repo: context.repo.repo, + labels: 'needs reproduction', + state: 'open', + per_page: 100, + }); + for (const issue of issues) { + if (issue.pull_request) continue; + if (new Date(issue.updated_at).getTime() > cutoff) continue; + await github.rest.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issue.number, + state: 'closed', + state_reason: 'not_planned', + }); + } diff --git a/.github/workflows/issue-labeled.yml b/.github/workflows/issue-labeled.yml index 97de158..3f13b91 100644 --- a/.github/workflows/issue-labeled.yml +++ b/.github/workflows/issue-labeled.yml @@ -4,16 +4,22 @@ on: issues: types: [labeled] +permissions: + issues: write + jobs: reply-labeled: runs-on: ubuntu-latest steps: - name: needs reproduction if: github.event.label.name == 'needs reproduction' - uses: actions-cool/issues-helper@v3 + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 with: - actions: create-comment - token: ${{ secrets.GITHUB_TOKEN }} - issue-number: ${{ github.event.issue.number }} - body: | - Hello @${{ github.event.issue.user.login }}. Please provide a [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) using a GitHub repository or [StackBlitz](https://vitest.new) (you can also use [examples](https://github.com/vitest-dev/vitest/tree/main/examples)). Issues marked with `needs reproduction` will be closed if they have no activity within 3 days. + script: | + const author = context.payload.issue.user.login; + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.payload.issue.number, + body: `Hello @${author}. Please provide a [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) using a GitHub repository or [StackBlitz](https://vitest.new) (you can also use [examples](https://github.com/vitest-dev/vitest/tree/main/examples)). Issues marked with \`needs reproduction\` will be closed if they have no activity within 3 days.`, + }); diff --git a/.github/workflows/lock-closed-issues.yml b/.github/workflows/lock-closed-issues.yml index 41f51ab..5d20c3a 100644 --- a/.github/workflows/lock-closed-issues.yml +++ b/.github/workflows/lock-closed-issues.yml @@ -12,7 +12,7 @@ jobs: if: github.repository == 'vitest-dev/vscode' runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v5 + - uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5 with: github-token: ${{ secrets.GITHUB_TOKEN }} issue-inactive-days: '14' diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 342a5b1..0aa7acf 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -15,22 +15,23 @@ jobs: environment: Release steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: fetch-depth: 0 - - uses: pnpm/action-setup@v3 + - uses: pnpm/action-setup@a3252b78c470c02df07e9d59298aecedc3ccdd6d # v3 - - name: Use Node.js 20 - uses: actions/setup-node@v3 + - name: Use Node.js 22 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: - node-version: 20 + node-version: 22 registry-url: https://registry.npmjs.org/ - cache: pnpm + # disable cache to avoid cache poisoning + package-manager-cache: false - run: pnpm install --frozen-lockfile --prefer-offline - - uses: actions/github-script@v7 + - uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 id: checkPrerelease name: Check if prerelease with: @@ -40,7 +41,7 @@ jobs: - name: Publish to Visual Studio Marketplace id: publishToVSMarketplace - uses: HaaLeo/publish-vscode-extension@v1 + uses: HaaLeo/publish-vscode-extension@f4ece70f329f66686bd71c54b1671353fe320e49 # v1 with: preRelease: ${{ steps.checkPrerelease.outputs.preRelease == 'true' }} pat: ${{ secrets.VS_MARKETPLACE_TOKEN }} @@ -48,7 +49,7 @@ jobs: dependencies: false - name: Publish to Open VSX Registry - uses: HaaLeo/publish-vscode-extension@v1 + uses: HaaLeo/publish-vscode-extension@f4ece70f329f66686bd71c54b1671353fe320e49 # v1 with: extensionFile: ${{ steps.publishToVSMarketplace.outputs.vsixPath }} pat: ${{ secrets.OPEN_VSX_TOKEN }} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7b74981..5073f8c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -264,7 +264,7 @@ importers: version: 5.9.3 vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) which: specifier: 'catalog:' version: 4.0.0 @@ -288,7 +288,7 @@ importers: version: 4.0.3 vitest: specifier: catalog:v3 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) vitest-vscode-shared: specifier: workspace:* version: link:../shared @@ -300,7 +300,7 @@ importers: version: 2.4.0 vitest: specifier: catalog:v3 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) packages/worker: devDependencies: @@ -309,7 +309,7 @@ importers: version: 4.1.0 vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) vitest-vscode-shared: specifier: workspace:* version: link:../shared @@ -324,7 +324,7 @@ importers: version: 3.2.4 vitest: specifier: catalog:v3 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) vitest-vscode-shared: specifier: workspace:* version: link:../shared @@ -343,7 +343,7 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/basic: dependencies: @@ -359,7 +359,7 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:v3 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) samples/basic-v4: devDependencies: @@ -377,7 +377,7 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/browser: dependencies: @@ -402,25 +402,25 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/continuous: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/deno: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/e2e: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/imba: devDependencies: @@ -444,7 +444,7 @@ importers: version: 0.10.3(imba@2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.9.1)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0))(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@24.1.3)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@24.1.3)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) vitest-github-actions-reporter-temp: specifier: ^0.8.3 version: 0.8.3(vitest@4.1.0) @@ -456,7 +456,7 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/monorepo-vitest-workspace: devDependencies: @@ -464,11 +464,11 @@ importers: specifier: catalog:latest version: 4.1.0(@vitest/browser@4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0))(vitest@4.1.0) happy-dom: - specifier: ^15.7.4 - version: 15.11.7 + specifier: ^20.9.0 + version: 20.9.0 vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/monorepo-vitest-workspace/packages/react: dependencies: @@ -486,8 +486,8 @@ importers: specifier: 1.2.0 version: 1.2.0 happy-dom: - specifier: ^2.49.0 - version: 2.55.0 + specifier: ^20.9.0 + version: 20.9.0 jsdom: specifier: latest version: 28.1.0 @@ -511,8 +511,8 @@ importers: specifier: 1.2.0 version: 1.2.0 happy-dom: - specifier: ^2.49.0 - version: 2.55.0 + specifier: ^20.9.0 + version: 20.9.0 jsdom: specifier: latest version: 28.1.0 @@ -524,28 +524,28 @@ importers: dependencies: vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/no-config: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/projects: devDependencies: happy-dom: - specifier: ^15.7.4 - version: 15.11.7 + specifier: ^20.9.0 + version: 20.9.0 vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/readme: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/vue: dependencies: @@ -563,14 +563,14 @@ importers: specifier: ^2.4.5 version: 2.4.6 happy-dom: - specifier: 14.7.1 - version: 14.7.1 + specifier: ^20.9.0 + version: 20.9.0 vite: specifier: catalog:latest version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@14.7.1)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) packages: @@ -1741,9 +1741,6 @@ packages: '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - '@types/concat-stream@1.6.1': - resolution: {integrity: sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==} - '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -1753,9 +1750,6 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/form-data@0.0.33': - resolution: {integrity: sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==} - '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} @@ -1771,15 +1765,9 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@10.17.60': - resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} - '@types/node@24.10.1': resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} - '@types/node@8.10.66': - resolution: {integrity: sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==} - '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -1792,9 +1780,6 @@ packages: '@types/prop-types@15.7.15': resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} - '@types/qs@6.14.0': - resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} - '@types/react-test-renderer@17.0.9': resolution: {integrity: sha512-bOfxcu5oZ+KxvACScbkTwZ4eGCtZFTz4VZCOVAIfGbThxqiXSIGipKVG8ubaYBXquUSQROzNIUzviWdSnnAlzg==} @@ -1813,6 +1798,9 @@ packages: '@types/vscode@1.106.1': resolution: {integrity: sha512-R/HV8u2h8CAddSbX8cjpdd7B8/GnE4UjgjpuGuHcbp1xV6yh4OeqU4L1pKjlwujCrSFS0MOpwJAIs/NexMB1fQ==} + '@types/whatwg-mimetype@3.0.2': + resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} + '@types/which@3.0.4': resolution: {integrity: sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w==} @@ -2113,9 +2101,6 @@ packages: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} - asap@2.0.6: - resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -2215,9 +2200,6 @@ packages: buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -2274,9 +2256,6 @@ packages: caniuse-lite@1.0.30001757: resolution: {integrity: sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==} - caseless@0.12.0: - resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} - chai@5.3.3: resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} engines: {node: '>=18'} @@ -2378,10 +2357,6 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - concat-stream@1.6.2: - resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} - engines: {'0': node >= 0.8} - confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} @@ -2613,6 +2588,10 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + envinfo@7.21.0: resolution: {integrity: sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==} engines: {node: '>=4'} @@ -2877,10 +2856,6 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} - form-data@2.5.5: - resolution: {integrity: sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==} - engines: {node: '>= 0.12'} - form-data@4.0.5: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} @@ -2931,10 +2906,6 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} - get-port@3.2.0: - resolution: {integrity: sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==} - engines: {node: '>=4'} - get-port@5.1.1: resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} engines: {node: '>=8'} @@ -3004,16 +2975,9 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - happy-dom@14.7.1: - resolution: {integrity: sha512-v60Q0evZ4clvMcrAh5/F8EdxDdfHdFrtffz/CNe10jKD+nFweZVxM91tW+UyY2L4AtpgIaXdZ7TQmiO1pfcwbg==} - engines: {node: '>=16.0.0'} - - happy-dom@15.11.7: - resolution: {integrity: sha512-KyrFvnl+J9US63TEzwoiJOQzZBJY7KgBushJA8X61DMbNsH+2ONkDuLDnCnwUiPTF42tLoEmrPyoqbenVA5zrg==} - engines: {node: '>=18.0.0'} - - happy-dom@2.55.0: - resolution: {integrity: sha512-CHDMBRau+l/yKQL+ANmexRAC8FRCuYbXRSpu/GbLVyfqkrlBzV7OSNd5C5HZ+pVFtFv1bFJYC5r+xrqgGQuq5w==} + happy-dom@20.9.0: + resolution: {integrity: sha512-GZZ9mKe8r646NUAf/zemnGbjYh4Bt8/MqASJY+pSm5ZDtc3YQox+4gsLI7yi1hba6o+eCsGxpHn5+iEVn31/FQ==} + engines: {node: '>=20.0.0'} has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} @@ -3067,17 +3031,10 @@ packages: htmlparser2@10.0.0: resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} - http-basic@8.1.3: - resolution: {integrity: sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==} - engines: {node: '>=6.0.0'} - http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} - http-response-object@3.0.2: - resolution: {integrity: sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==} - https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -3680,15 +3637,6 @@ packages: node-fetch-native@1.6.7: resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} - node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} @@ -3826,9 +3774,6 @@ packages: pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - parse-cache-control@1.0.1: - resolution: {integrity: sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==} - parse-json@8.3.0: resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} engines: {node: '>=18'} @@ -3969,9 +3914,6 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - promise@8.3.0: - resolution: {integrity: sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==} - prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -4372,13 +4314,6 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - sync-request@6.1.0: - resolution: {integrity: sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==} - engines: {node: '>=8.0.0'} - - sync-rpc@1.3.6: - resolution: {integrity: sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==} - synckit@0.11.12: resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} engines: {node: ^14.18.0 || >=16.0.0} @@ -4422,10 +4357,6 @@ packages: resolution: {integrity: sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==} engines: {node: '>=4'} - then-request@6.0.2: - resolution: {integrity: sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==} - engines: {node: '>=6.0.0'} - tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -4487,9 +4418,6 @@ packages: resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} engines: {node: '>=16'} - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - tr46@5.1.1: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} @@ -4549,9 +4477,6 @@ packages: typed-rest-client@1.8.11: resolution: {integrity: sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==} - typedarray@0.0.6: - resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -4776,9 +4701,6 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -4787,11 +4709,6 @@ packages: resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} engines: {node: '>=20'} - whatwg-encoding@2.0.0: - resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} - engines: {node: '>=12'} - deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation - whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} @@ -4817,9 +4734,6 @@ packages: resolution: {integrity: sha512-9CcxtEKsf53UFwkSUZjG+9vydAsFO4lFHBpJUtjBcoJOCJpKnSJNwCw813zrYJHpCJ7sgfbtOe0V5Ku7Pa1XMQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -5995,10 +5909,6 @@ snapshots: '@types/deep-eql': 4.0.2 assertion-error: 2.0.1 - '@types/concat-stream@1.6.1': - dependencies: - '@types/node': 24.10.1 - '@types/debug@4.1.12': dependencies: '@types/ms': 2.1.0 @@ -6008,10 +5918,6 @@ snapshots: '@types/estree@1.0.8': {} - '@types/form-data@0.0.33': - dependencies: - '@types/node': 24.10.1 - '@types/istanbul-lib-coverage@2.0.6': {} '@types/jsesc@2.5.1': {} @@ -6025,14 +5931,10 @@ snapshots: '@types/ms@2.1.0': optional: true - '@types/node@10.17.60': {} - '@types/node@24.10.1': dependencies: undici-types: 7.16.0 - '@types/node@8.10.66': {} - '@types/normalize-package-data@2.4.4': {} '@types/picomatch@4.0.2': {} @@ -6044,8 +5946,6 @@ snapshots: '@types/prop-types@15.7.15': {} - '@types/qs@6.14.0': {} - '@types/react-test-renderer@17.0.9': dependencies: '@types/react': 17.0.90 @@ -6064,6 +5964,8 @@ snapshots: '@types/vscode@1.106.1': {} + '@types/whatwg-mimetype@3.0.2': {} + '@types/which@3.0.4': {} '@types/ws@8.18.1': @@ -6103,7 +6005,7 @@ snapshots: '@vitest/mocker': 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) playwright: 1.57.0 tinyrainbow: 3.0.3 - vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - bufferutil - msw @@ -6119,7 +6021,7 @@ snapshots: magic-string: 0.30.21 sirv: 3.0.2 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) ws: 8.19.0 optionalDependencies: playwright: 1.57.0 @@ -6138,7 +6040,7 @@ snapshots: pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.0.3 - vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) ws: 8.19.0 transitivePeerDependencies: - bufferutil @@ -6158,7 +6060,7 @@ snapshots: magicast: 0.5.2 obug: 2.1.1 tinyrainbow: 3.0.3 - vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - supports-color @@ -6177,7 +6079,7 @@ snapshots: std-env: 3.10.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) optionalDependencies: '@vitest/browser': 3.2.4(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) transitivePeerDependencies: @@ -6195,7 +6097,7 @@ snapshots: obug: 2.1.1 std-env: 4.0.0 tinyrainbow: 3.0.3 - vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) optionalDependencies: '@vitest/browser': 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0) @@ -6503,8 +6405,6 @@ snapshots: call-bound: 1.0.4 is-array-buffer: 3.0.5 - asap@2.0.6: {} - assertion-error@2.0.1: {} ast-kit@3.0.0-beta.1: @@ -6605,8 +6505,6 @@ snapshots: buffer-equal-constant-time@1.0.1: {} - buffer-from@1.1.2: {} - buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -6704,8 +6602,6 @@ snapshots: caniuse-lite@1.0.30001757: {} - caseless@0.12.0: {} - chai@5.3.3: dependencies: assertion-error: 2.0.1 @@ -6846,13 +6742,6 @@ snapshots: concat-map@0.0.1: {} - concat-stream@1.6.2: - dependencies: - buffer-from: 1.1.2 - inherits: 2.0.4 - readable-stream: 2.3.8 - typedarray: 0.0.6 - confbox@0.1.8: {} confbox@0.2.2: {} @@ -7080,6 +6969,8 @@ snapshots: entities@6.0.1: {} + entities@7.0.1: {} + envinfo@7.21.0: {} environment@1.1.0: {} @@ -7370,15 +7261,6 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - form-data@2.5.5: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.2 - mime-types: 2.1.35 - safe-buffer: 5.2.1 - form-data@4.0.5: dependencies: asynckit: 0.4.0 @@ -7431,8 +7313,6 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 - get-port@3.2.0: {} - get-port@5.1.1: {} get-proto@1.0.1: @@ -7527,29 +7407,17 @@ snapshots: graceful-fs@4.2.11: {} - happy-dom@14.7.1: - dependencies: - entities: 4.5.0 - webidl-conversions: 7.0.0 - whatwg-mimetype: 3.0.0 - - happy-dom@15.11.7: - dependencies: - entities: 4.5.0 - webidl-conversions: 7.0.0 - whatwg-mimetype: 3.0.0 - - happy-dom@2.55.0: + happy-dom@20.9.0: dependencies: - css.escape: 1.5.1 - he: 1.2.0 - node-fetch: 2.7.0 - sync-request: 6.1.0 - webidl-conversions: 7.0.0 - whatwg-encoding: 2.0.0 + '@types/node': 24.10.1 + '@types/whatwg-mimetype': 3.0.2 + '@types/ws': 8.18.1 + entities: 7.0.1 whatwg-mimetype: 3.0.0 + ws: 8.19.0 transitivePeerDependencies: - - encoding + - bufferutil + - utf-8-validate has-bigints@1.1.0: {} @@ -7600,13 +7468,6 @@ snapshots: domutils: 3.2.2 entities: 6.0.1 - http-basic@8.1.3: - dependencies: - caseless: 0.12.0 - concat-stream: 1.6.2 - http-response-object: 3.0.2 - parse-cache-control: 1.0.1 - http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 @@ -7614,10 +7475,6 @@ snapshots: transitivePeerDependencies: - supports-color - http-response-object@3.0.2: - dependencies: - '@types/node': 10.17.60 - https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 @@ -7660,7 +7517,7 @@ snapshots: '@testing-library/jest-dom': 6.9.1 vite: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vite-node: 3.2.4(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) - vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@24.1.3)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@24.1.3)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - picomatch - supports-color @@ -8222,10 +8079,6 @@ snapshots: node-fetch-native@1.6.7: {} - node-fetch@2.7.0: - dependencies: - whatwg-url: 5.0.0 - node-releases@2.0.27: {} node-sarif-builder@3.3.1: @@ -8396,8 +8249,6 @@ snapshots: pako@1.0.11: {} - parse-cache-control@1.0.1: {} - parse-json@8.3.0: dependencies: '@babel/code-frame': 7.27.1 @@ -8531,10 +8382,6 @@ snapshots: process-nextick-args@2.0.1: {} - promise@8.3.0: - dependencies: - asap: 2.0.6 - prompts@2.4.2: dependencies: kleur: 3.0.3 @@ -9000,16 +8847,6 @@ snapshots: symbol-tree@3.2.4: {} - sync-request@6.1.0: - dependencies: - http-response-object: 3.0.2 - sync-rpc: 1.3.6 - then-request: 6.0.2 - - sync-rpc@1.3.6: - dependencies: - get-port: 3.2.0 - synckit@0.11.12: dependencies: '@pkgr/core': 0.2.9 @@ -9074,20 +8911,6 @@ snapshots: dependencies: editions: 6.22.0 - then-request@6.0.2: - dependencies: - '@types/concat-stream': 1.6.1 - '@types/form-data': 0.0.33 - '@types/node': 8.10.66 - '@types/qs': 6.14.0 - caseless: 0.12.0 - concat-stream: 1.6.2 - form-data: 2.5.5 - http-basic: 8.1.3 - http-response-object: 3.0.2 - promise: 8.3.0 - qs: 6.14.0 - tinybench@2.9.0: {} tinyexec@0.3.2: {} @@ -9134,8 +8957,6 @@ snapshots: dependencies: tldts: 7.0.19 - tr46@0.0.3: {} - tr46@5.1.1: dependencies: punycode: 2.3.1 @@ -9197,8 +9018,6 @@ snapshots: tunnel: 0.0.6 underscore: 1.13.7 - typedarray@0.0.6: {} - typescript@5.9.3: {} uc.micro@2.1.0: {} @@ -9318,9 +9137,9 @@ snapshots: dependencies: '@actions/core': 1.11.1 source-map-js: 1.2.1 - vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@24.1.3)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@24.1.3)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) - vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 @@ -9349,7 +9168,7 @@ snapshots: '@types/debug': 4.1.12 '@types/node': 24.10.1 '@vitest/browser': 3.2.4(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) - happy-dom: 15.11.7 + happy-dom: 20.9.0 jsdom: 28.1.0 transitivePeerDependencies: - jiti @@ -9365,7 +9184,7 @@ snapshots: - tsx - yaml - vitest@4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@14.7.1)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): + vitest@4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@24.1.3)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: '@vitest/expect': 4.1.0 '@vitest/mocker': 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) @@ -9390,42 +9209,12 @@ snapshots: optionalDependencies: '@types/node': 24.10.1 '@vitest/browser-playwright': 4.1.0(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0) - happy-dom: 14.7.1 - jsdom: 28.1.0 - transitivePeerDependencies: - - msw - - vitest@4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@24.1.3)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): - dependencies: - '@vitest/expect': 4.1.0 - '@vitest/mocker': 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) - '@vitest/pretty-format': 4.1.0 - '@vitest/runner': 4.1.0 - '@vitest/snapshot': 4.1.0 - '@vitest/spy': 4.1.0 - '@vitest/utils': 4.1.0 - es-module-lexer: 2.0.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 4.0.0 - tinybench: 2.9.0 - tinyexec: 1.0.2 - tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 24.10.1 - '@vitest/browser-playwright': 4.1.0(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0) - happy-dom: 15.11.7 + happy-dom: 20.9.0 jsdom: 24.1.3 transitivePeerDependencies: - msw - vitest@4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@15.11.7)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): + vitest@4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: '@vitest/expect': 4.1.0 '@vitest/mocker': 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) @@ -9450,7 +9239,7 @@ snapshots: optionalDependencies: '@types/node': 24.10.1 '@vitest/browser-playwright': 4.1.0(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0) - happy-dom: 15.11.7 + happy-dom: 20.9.0 jsdom: 28.1.0 transitivePeerDependencies: - msw @@ -9471,16 +9260,10 @@ snapshots: dependencies: xml-name-validator: 5.0.0 - webidl-conversions@3.0.1: {} - webidl-conversions@7.0.0: {} webidl-conversions@8.0.1: {} - whatwg-encoding@2.0.0: - dependencies: - iconv-lite: 0.6.3 - whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 @@ -9504,11 +9287,6 @@ snapshots: transitivePeerDependencies: - '@noble/hashes' - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 diff --git a/samples/monorepo-vitest-workspace/package.json b/samples/monorepo-vitest-workspace/package.json index 114f64f..bad41bc 100644 --- a/samples/monorepo-vitest-workspace/package.json +++ b/samples/monorepo-vitest-workspace/package.json @@ -10,7 +10,7 @@ }, "devDependencies": { "@vitest/coverage-v8": "catalog:latest", - "happy-dom": "^15.7.4", + "happy-dom": "^20.9.0", "vitest": "catalog:latest" } } diff --git a/samples/monorepo-vitest-workspace/packages/react copy/package.json b/samples/monorepo-vitest-workspace/packages/react copy/package.json index 44dab2b..ea06669 100644 --- a/samples/monorepo-vitest-workspace/packages/react copy/package.json +++ b/samples/monorepo-vitest-workspace/packages/react copy/package.json @@ -13,7 +13,7 @@ "@types/react": "^17.0.41", "@types/react-test-renderer": "^17.0.1", "@vitejs/plugin-react": "1.2.0", - "happy-dom": "^2.49.0", + "happy-dom": "^20.9.0", "jsdom": "latest", "react-test-renderer": "17.0.2" }, diff --git a/samples/monorepo-vitest-workspace/packages/react/package.json b/samples/monorepo-vitest-workspace/packages/react/package.json index 910e7bf..1ed6fb9 100644 --- a/samples/monorepo-vitest-workspace/packages/react/package.json +++ b/samples/monorepo-vitest-workspace/packages/react/package.json @@ -13,7 +13,7 @@ "@types/react": "^17.0.41", "@types/react-test-renderer": "^17.0.1", "@vitejs/plugin-react": "1.2.0", - "happy-dom": "^2.49.0", + "happy-dom": "^20.9.0", "jsdom": "latest", "react-test-renderer": "17.0.2" }, diff --git a/samples/projects/package.json b/samples/projects/package.json index c60f6e2..531e48f 100644 --- a/samples/projects/package.json +++ b/samples/projects/package.json @@ -7,7 +7,7 @@ "test": "vitest" }, "devDependencies": { - "happy-dom": "^15.7.4", + "happy-dom": "^20.9.0", "vitest": "catalog:latest" } } diff --git a/samples/vue/package.json b/samples/vue/package.json index c77eb12..b641af2 100644 --- a/samples/vue/package.json +++ b/samples/vue/package.json @@ -12,7 +12,7 @@ "@vitejs/plugin-vue": "^6.0.2", "@vitest/coverage-v8": "catalog:latest", "@vue/test-utils": "^2.4.5", - "happy-dom": "14.7.1", + "happy-dom": "^20.9.0", "vite": "catalog:latest", "vitest": "catalog:latest" }, -- 2.51.2 From fc77d4f2fcf247be018eac4f148536a91f25ba8b Mon Sep 17 00:00:00 2001 From: Vladimir Date: Thu, 11 Jun 2026 10:57:12 +0200 Subject: [PATCH 51/64] fix: support Deno 2.8 (#794) * fix: support Deno 2.8 * fix: pass down `[]` * chore: fix legacy --- packages/extension/package.json | 3 + packages/extension/src/spawn/rpc.ts | 10 +- packages/extension/src/spawn/ws.ts | 17 ++ packages/extension/src/utils.ts | 2 +- packages/extension/src/watcher.ts | 2 +- packages/extension/src/worker/index.ts | 19 +- packages/shared/src/index.ts | 1 + packages/worker-legacy/src/worker.ts | 4 +- packages/worker/src/runner.ts | 4 +- pnpm-lock.yaml | 238 ++++++++++++++----------- 10 files changed, 184 insertions(+), 116 deletions(-) diff --git a/packages/extension/package.json b/packages/extension/package.json index ac1042f..f079b84 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -9,5 +9,8 @@ "picomatch": "catalog:latest", "vitest": "catalog:v3", "vitest-vscode-shared": "workspace:*" + }, + "devDependencies": { + "flatted": "^3.4.2" } } diff --git a/packages/extension/src/spawn/rpc.ts b/packages/extension/src/spawn/rpc.ts index 147e265..e413b6a 100644 --- a/packages/extension/src/spawn/rpc.ts +++ b/packages/extension/src/spawn/rpc.ts @@ -1,6 +1,7 @@ import type { ExtensionWorkerEvents, ExtensionWorkerTransport } from 'vitest-vscode-shared' import v8 from 'node:v8' import { createBirpc } from 'birpc' +import { log } from '../log' export type { ExtensionWorkerEvents, @@ -64,6 +65,8 @@ export function createRpcOptions() { export function createVitestRpc(options: { on: (listener: (message: any) => void) => void send: (message: any) => void + serialize?: (v: any) => any + deserialize?: (v: any) => any }) { const { events, handlers } = createRpcOptions() @@ -76,8 +79,11 @@ export function createVitestRpc(options: { post(message) { options.send(message) }, - serialize: v8.serialize, - deserialize: (v) => v8.deserialize(Buffer.from(v) as any), + serialize: options.serialize ?? v8.serialize, + deserialize: options.deserialize ?? ((v) => v8.deserialize(Buffer.from(v) as any)), + onGeneralError(error) { + log.error('RPC Error', error) + }, }) return { diff --git a/packages/extension/src/spawn/ws.ts b/packages/extension/src/spawn/ws.ts index 94e60bd..e21ee50 100644 --- a/packages/extension/src/spawn/ws.ts +++ b/packages/extension/src/spawn/ws.ts @@ -18,6 +18,7 @@ import { import { log } from '../log' import { createVitestRpc } from './rpc' import { resolve } from 'pathe' +import { parse, stringify } from 'flatted' export type WsConnectionMetadata = Omit & { ws: WebSocket @@ -83,6 +84,21 @@ export function onWsConnection( const { api, handlers } = createVitestRpc({ on: (listener) => ws.on('message', listener), send: (message) => ws.send(message), + serialize: + pkg.runtime !== 'node' + ? (e) => + stringify(e, (_, v) => { + if (v instanceof Error) { + return { + name: v.name, + message: v.message, + stack: v.stack, + } + } + return v + }) + : undefined, + deserialize: pkg.runtime !== 'node' ? parse : undefined, }) ws.once('close', () => { log.verbose?.('[API]', 'Vitest WebSocket connection closed, cannot call RPC anymore.') @@ -153,6 +169,7 @@ export function onWsConnection( env: getConfig(pkg.folder).env || undefined, configFile: pkg.configFile, cwd: pkg.cwd, + runtime: pkg.runtime, arguments: pkg.arguments, workspaceFile: pkg.workspaceFile, id: pkg.id, diff --git a/packages/extension/src/utils.ts b/packages/extension/src/utils.ts index cd0cea2..a86f935 100644 --- a/packages/extension/src/utils.ts +++ b/packages/extension/src/utils.ts @@ -17,7 +17,7 @@ export function formatPkg(pkg: VitestPackage) { } function _showVitestError(message: string, error?: any) { - if (error) log.error(error) + if (error) log.error(error.stack || error) vscode.window .showErrorMessage(`${message}. Check the output for more details.`, 'See error') diff --git a/packages/extension/src/watcher.ts b/packages/extension/src/watcher.ts index 32bf034..b73f6dc 100644 --- a/packages/extension/src/watcher.ts +++ b/packages/extension/src/watcher.ts @@ -2,7 +2,7 @@ import type { VitestProcessAPI } from './apiProcess' import type { TransformSchemaProvider } from './schemaProvider' import type { TestTree } from './testTree' import { relative } from 'node:path' -import { normalize, resolve } from 'pathe' +import { normalize } from 'pathe' import * as vscode from 'vscode' import { getConfig } from './config' import { log } from './log' diff --git a/packages/extension/src/worker/index.ts b/packages/extension/src/worker/index.ts index cbf3056..c44fc99 100644 --- a/packages/extension/src/worker/index.ts +++ b/packages/extension/src/worker/index.ts @@ -4,6 +4,7 @@ import { pathToFileURL } from 'node:url' import v8 from 'node:v8' import { createWorkerRPC, normalizeDriveLetter, WorkerWSEventEmitter } from 'vitest-vscode-shared' import { WebSocket } from 'ws' +import { parse, stringify } from 'flatted' // this is the file that will be executed with "node " @@ -53,8 +54,22 @@ emitter.on('message', async function onMessage(message: any) { post(message) { emitter.send(message) }, - serialize: v8.serialize, - deserialize: (v) => v8.deserialize(Buffer.from(v) as any), + serialize: + data.meta.runtime !== 'node' + ? (e) => + stringify(e, (_, v) => { + if (v instanceof Error) { + return { + name: v.name, + message: v.message, + stack: v.stack, + } + } + return v + }) + : v8.serialize, + deserialize: + data.meta.runtime !== 'node' ? parse : (v) => v8.deserialize(Buffer.from(v) as any), }) worker.initRpc(rpc) reporter.initRpc(rpc) diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index 63dd8c6..ec688eb 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -143,6 +143,7 @@ export interface WorkerInitMetadata { id: string cwd: string arguments?: string + runtime: 'node' | 'deno' configFile?: string workspaceFile?: string env: Record | undefined diff --git a/packages/worker-legacy/src/worker.ts b/packages/worker-legacy/src/worker.ts index 87e22d1..3205845 100644 --- a/packages/worker-legacy/src/worker.ts +++ b/packages/worker-legacy/src/worker.ts @@ -188,9 +188,9 @@ export class ExtensionWorker implements ExtensionWorkerTransport { private async globTestSpecifications(filters?: string[]): Promise { if ('globTestSpecifications' in this.vitest) { - return this.vitest.globTestSpecifications(filters) + return this.vitest.globTestSpecifications(filters || []) } - return await (this.vitest as any).globTestFiles(filters) + return await (this.vitest as any).globTestFiles(filters || []) } private invalidateTree(mod: any, seen = new Set()) { diff --git a/packages/worker/src/runner.ts b/packages/worker/src/runner.ts index 0b85716..5a1931d 100644 --- a/packages/worker/src/runner.ts +++ b/packages/worker/src/runner.ts @@ -73,7 +73,9 @@ export class ExtensionWorkerRunner { } if (!filesOrDirectories || this.isOnlyDirectories(filesOrDirectories)) { - const specifications = await this.vitest.getRelevantTestSpecifications(filesOrDirectories) + const specifications = await this.vitest.getRelevantTestSpecifications( + filesOrDirectories || [], + ) await this.vitest.rerunTestSpecifications(specifications, true) } else { const specifications = await this.resolveTestSpecifications(filesOrDirectories) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5073f8c..b043e63 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -264,7 +264,7 @@ importers: version: 5.9.3 vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) which: specifier: 'catalog:' version: 4.0.0 @@ -288,10 +288,14 @@ importers: version: 4.0.3 vitest: specifier: catalog:v3 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@29.1.1)(tsx@4.21.0)(yaml@2.8.2) vitest-vscode-shared: specifier: workspace:* version: link:../shared + devDependencies: + flatted: + specifier: ^3.4.2 + version: 3.4.2 packages/shared: devDependencies: @@ -300,7 +304,7 @@ importers: version: 2.4.0 vitest: specifier: catalog:v3 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@29.1.1)(tsx@4.21.0)(yaml@2.8.2) packages/worker: devDependencies: @@ -309,7 +313,7 @@ importers: version: 4.1.0 vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) vitest-vscode-shared: specifier: workspace:* version: link:../shared @@ -324,7 +328,7 @@ importers: version: 3.2.4 vitest: specifier: catalog:v3 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@29.1.1)(tsx@4.21.0)(yaml@2.8.2) vitest-vscode-shared: specifier: workspace:* version: link:../shared @@ -343,7 +347,7 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/basic: dependencies: @@ -359,7 +363,7 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:v3 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@29.1.1)(tsx@4.21.0)(yaml@2.8.2) samples/basic-v4: devDependencies: @@ -377,7 +381,7 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/browser: dependencies: @@ -402,25 +406,25 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/continuous: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/deno: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/e2e: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/imba: devDependencies: @@ -456,7 +460,7 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/monorepo-vitest-workspace: devDependencies: @@ -468,7 +472,7 @@ importers: version: 20.9.0 vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/monorepo-vitest-workspace/packages/react: dependencies: @@ -490,7 +494,7 @@ importers: version: 20.9.0 jsdom: specifier: latest - version: 28.1.0 + version: 29.1.1 react-test-renderer: specifier: 17.0.2 version: 17.0.2(react@17.0.2) @@ -515,7 +519,7 @@ importers: version: 20.9.0 jsdom: specifier: latest - version: 28.1.0 + version: 29.1.1 react-test-renderer: specifier: 17.0.2 version: 17.0.2(react@17.0.2) @@ -524,13 +528,13 @@ importers: dependencies: vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/no-config: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/projects: devDependencies: @@ -539,13 +543,13 @@ importers: version: 20.9.0 vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/readme: devDependencies: vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) samples/vue: dependencies: @@ -570,13 +574,10 @@ importers: version: 7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: catalog:latest - version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) packages: - '@acemir/cssom@0.9.31': - resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} - '@actions/core@1.11.1': resolution: {integrity: sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==} @@ -602,12 +603,17 @@ packages: '@asamuzakjp/css-color@3.2.0': resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} - '@asamuzakjp/css-color@5.0.1': - resolution: {integrity: sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==} + '@asamuzakjp/css-color@5.1.11': + resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - '@asamuzakjp/dom-selector@6.8.1': - resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==} + '@asamuzakjp/dom-selector@7.1.1': + resolution: {integrity: sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/generational-cache@1.0.1': + resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} '@asamuzakjp/nwsapi@2.3.9': resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} @@ -872,8 +878,8 @@ packages: '@csstools/css-parser-algorithms': ^3.0.5 '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-calc@3.1.1': - resolution: {integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==} + '@csstools/css-calc@3.2.1': + resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -886,8 +892,8 @@ packages: '@csstools/css-parser-algorithms': ^3.0.5 '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-color-parser@4.0.2': - resolution: {integrity: sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==} + '@csstools/css-color-parser@4.1.3': + resolution: {integrity: sha512-DOgvIPkikIOixQRlD4YF31VN6fLLUTdrzhfRbis8vm0kMTgIbEPX0Ip/YX9fOeV9iywAS4sUUbTclpan7yYP8Q==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -905,8 +911,13 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.0.29': - resolution: {integrity: sha512-jx9GjkkP5YHuTmko2eWAvpPnb0mB4mGRr2U7XwVNwevm8nlpobZEVk+GNmiYMk2VuA75v+plfXWyroWKmICZXg==} + '@csstools/css-syntax-patches-for-csstree@1.1.5': + resolution: {integrity: sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true '@csstools/css-tokenizer@3.0.4': resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} @@ -1249,8 +1260,8 @@ packages: cpu: [x64] os: [win32] - '@exodus/bytes@1.11.0': - resolution: {integrity: sha512-wO3vd8nsEHdumsXrjGO/v4p6irbg7hy9kvIeR6i2AwylZSk4HJdWgL0FNaVquW1+AweJcdvU1IEpuIWk/WaPnA==} + '@exodus/bytes@1.15.1': + resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: '@noble/hashes': ^1.8.0 || ^2.0.0 @@ -2391,8 +2402,8 @@ packages: css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} - css-tree@3.1.0: - resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} css-what@6.2.2: @@ -2406,10 +2417,6 @@ packages: resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} engines: {node: '>=18'} - cssstyle@6.2.0: - resolution: {integrity: sha512-Fm5NvhYathRnXNVndkUsCCuR63DCLVVwGOOwQw782coXFi5HhkXdu289l59HlXZBawsyNccXfWRYvLzcDCdDig==} - engines: {node: '>=20'} - csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -2592,6 +2599,10 @@ packages: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} + entities@8.0.0: + resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} + engines: {node: '>=20.19.0'} + envinfo@7.21.0: resolution: {integrity: sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==} engines: {node: '>=4'} @@ -2848,6 +2859,9 @@ packages: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} @@ -3336,9 +3350,9 @@ packages: canvas: optional: true - jsdom@28.1.0: - resolution: {integrity: sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + jsdom@29.1.1: + resolution: {integrity: sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 peerDependenciesMeta: @@ -3462,6 +3476,10 @@ packages: resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} engines: {node: 20 || >=22} + lru-cache@11.5.1: + resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} + engines: {node: 20 || >=22} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -3498,8 +3516,8 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - mdn-data@2.12.2: - resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} @@ -3794,8 +3812,8 @@ packages: parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - parse5@8.0.0: - resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} + parse5@8.0.1: + resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} @@ -4414,8 +4432,8 @@ packages: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} - tough-cookie@6.0.0: - resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} + tough-cookie@6.0.1: + resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} engines: {node: '>=16'} tr46@5.1.1: @@ -4505,6 +4523,10 @@ packages: resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==} engines: {node: '>=20.18.1'} + undici@7.27.2: + resolution: {integrity: sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA==} + engines: {node: '>=20.18.1'} + unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} @@ -4730,8 +4752,8 @@ packages: resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} engines: {node: '>=18'} - whatwg-url@16.0.0: - resolution: {integrity: sha512-9CcxtEKsf53UFwkSUZjG+9vydAsFO4lFHBpJUtjBcoJOCJpKnSJNwCw813zrYJHpCJ7sgfbtOe0V5Ku7Pa1XMQ==} + whatwg-url@16.0.1: + resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} which-boxed-primitive@1.1.1: @@ -4873,8 +4895,6 @@ packages: snapshots: - '@acemir/cssom@0.9.31': {} - '@actions/core@1.11.1': dependencies: '@actions/exec': 1.1.1 @@ -4911,21 +4931,23 @@ snapshots: '@csstools/css-tokenizer': 3.0.4 lru-cache: 10.4.3 - '@asamuzakjp/css-color@5.0.1': + '@asamuzakjp/css-color@5.1.11': dependencies: - '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-color-parser': 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@asamuzakjp/generational-cache': 1.0.1 + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.3(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - lru-cache: 11.2.6 - '@asamuzakjp/dom-selector@6.8.1': + '@asamuzakjp/dom-selector@7.1.1': dependencies: + '@asamuzakjp/generational-cache': 1.0.1 '@asamuzakjp/nwsapi': 2.3.9 bidi-js: 1.0.3 - css-tree: 3.1.0 + css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 - lru-cache: 11.2.6 + + '@asamuzakjp/generational-cache@1.0.1': {} '@asamuzakjp/nwsapi@2.3.9': {} @@ -5281,7 +5303,7 @@ snapshots: '@bramus/specificity@2.4.2': dependencies: - css-tree: 3.1.0 + css-tree: 3.2.1 '@csstools/color-helpers@5.1.0': {} @@ -5292,7 +5314,7 @@ snapshots: '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-calc@3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -5304,10 +5326,10 @@ snapshots: '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-color-parser@4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-color-parser@4.1.3(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/color-helpers': 6.0.2 - '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -5319,7 +5341,9 @@ snapshots: dependencies: '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.0.29': {} + '@csstools/css-syntax-patches-for-csstree@1.1.5(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 '@csstools/css-tokenizer@3.0.4': {} @@ -5503,7 +5527,7 @@ snapshots: '@esbuild/win32-x64@0.27.0': optional: true - '@exodus/bytes@1.11.0': {} + '@exodus/bytes@1.15.1': {} '@fastify/busboy@2.1.1': {} @@ -6005,7 +6029,7 @@ snapshots: '@vitest/mocker': 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) playwright: 1.57.0 tinyrainbow: 3.0.3 - vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - bufferutil - msw @@ -6021,7 +6045,7 @@ snapshots: magic-string: 0.30.21 sirv: 3.0.2 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@29.1.1)(tsx@4.21.0)(yaml@2.8.2) ws: 8.19.0 optionalDependencies: playwright: 1.57.0 @@ -6040,7 +6064,7 @@ snapshots: pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.0.3 - vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) ws: 8.19.0 transitivePeerDependencies: - bufferutil @@ -6060,7 +6084,7 @@ snapshots: magicast: 0.5.2 obug: 2.1.1 tinyrainbow: 3.0.3 - vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) transitivePeerDependencies: - supports-color @@ -6079,7 +6103,7 @@ snapshots: std-env: 3.10.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2) + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@29.1.1)(tsx@4.21.0)(yaml@2.8.2) optionalDependencies: '@vitest/browser': 3.2.4(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) transitivePeerDependencies: @@ -6097,7 +6121,7 @@ snapshots: obug: 2.1.1 std-env: 4.0.0 tinyrainbow: 3.0.3 - vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) optionalDependencies: '@vitest/browser': 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0) @@ -6777,9 +6801,9 @@ snapshots: domutils: 3.2.2 nth-check: 2.1.1 - css-tree@3.1.0: + css-tree@3.2.1: dependencies: - mdn-data: 2.12.2 + mdn-data: 2.27.1 source-map-js: 1.2.1 css-what@6.2.2: {} @@ -6791,13 +6815,6 @@ snapshots: '@asamuzakjp/css-color': 3.2.0 rrweb-cssom: 0.8.0 - cssstyle@6.2.0: - dependencies: - '@asamuzakjp/css-color': 5.0.1 - '@csstools/css-syntax-patches-for-csstree': 1.0.29 - css-tree: 3.1.0 - lru-cache: 11.2.6 - csstype@3.2.3: {} data-urls@5.0.0: @@ -6808,7 +6825,7 @@ snapshots: data-urls@7.0.0: dependencies: whatwg-mimetype: 5.0.0 - whatwg-url: 16.0.0 + whatwg-url: 16.0.1 transitivePeerDependencies: - '@noble/hashes' @@ -6971,6 +6988,8 @@ snapshots: entities@7.0.1: {} + entities@8.0.0: {} + envinfo@7.21.0: {} environment@1.1.0: {} @@ -7252,6 +7271,8 @@ snapshots: flat@5.0.2: {} + flatted@3.4.2: {} + for-each@0.3.5: dependencies: is-callable: 1.2.7 @@ -7455,7 +7476,7 @@ snapshots: html-encoding-sniffer@6.0.0: dependencies: - '@exodus/bytes': 1.11.0 + '@exodus/bytes': 1.15.1 transitivePeerDependencies: - '@noble/hashes' @@ -7764,32 +7785,31 @@ snapshots: - supports-color - utf-8-validate - jsdom@28.1.0: + jsdom@29.1.1: dependencies: - '@acemir/cssom': 0.9.31 - '@asamuzakjp/dom-selector': 6.8.1 + '@asamuzakjp/css-color': 5.1.11 + '@asamuzakjp/dom-selector': 7.1.1 '@bramus/specificity': 2.4.2 - '@exodus/bytes': 1.11.0 - cssstyle: 6.2.0 + '@csstools/css-syntax-patches-for-csstree': 1.1.5(css-tree@3.2.1) + '@exodus/bytes': 1.15.1 + css-tree: 3.2.1 data-urls: 7.0.0 decimal.js: 10.6.0 html-encoding-sniffer: 6.0.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 - parse5: 8.0.0 + lru-cache: 11.5.1 + parse5: 8.0.1 saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 6.0.0 - undici: 7.22.0 + tough-cookie: 6.0.1 + undici: 7.27.2 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 - whatwg-url: 16.0.0 + whatwg-url: 16.0.1 xml-name-validator: 5.0.0 transitivePeerDependencies: - '@noble/hashes' - - supports-color jsesc@3.1.0: {} @@ -7906,6 +7926,8 @@ snapshots: lru-cache@11.2.6: {} + lru-cache@11.5.1: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 @@ -7951,7 +7973,7 @@ snapshots: math-intrinsics@1.1.0: {} - mdn-data@2.12.2: {} + mdn-data@2.27.1: {} mdurl@2.0.0: {} @@ -8274,9 +8296,9 @@ snapshots: dependencies: entities: 6.0.1 - parse5@8.0.0: + parse5@8.0.1: dependencies: - entities: 6.0.1 + entities: 8.0.0 path-exists@4.0.0: {} @@ -8953,7 +8975,7 @@ snapshots: universalify: 0.2.0 url-parse: 1.5.10 - tough-cookie@6.0.0: + tough-cookie@6.0.1: dependencies: tldts: 7.0.19 @@ -9039,6 +9061,8 @@ snapshots: undici@7.22.0: {} + undici@7.27.2: {} + unicorn-magic@0.1.0: {} unicorn-magic@0.3.0: {} @@ -9139,7 +9163,7 @@ snapshots: source-map-js: 1.2.1 vitest: 4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@24.1.3)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) - vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@28.1.0)(tsx@4.21.0)(yaml@2.8.2): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(happy-dom@20.9.0)(jiti@2.6.1)(jsdom@29.1.1)(tsx@4.21.0)(yaml@2.8.2): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 @@ -9169,7 +9193,7 @@ snapshots: '@types/node': 24.10.1 '@vitest/browser': 3.2.4(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) happy-dom: 20.9.0 - jsdom: 28.1.0 + jsdom: 29.1.1 transitivePeerDependencies: - jiti - less @@ -9214,7 +9238,7 @@ snapshots: transitivePeerDependencies: - msw - vitest@4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@28.1.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): + vitest@4.1.0(@types/node@24.10.1)(@vitest/browser-playwright@4.1.0)(happy-dom@20.9.0)(jsdom@29.1.1)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)): dependencies: '@vitest/expect': 4.1.0 '@vitest/mocker': 4.1.0(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2)) @@ -9240,7 +9264,7 @@ snapshots: '@types/node': 24.10.1 '@vitest/browser-playwright': 4.1.0(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@4.1.0) happy-dom: 20.9.0 - jsdom: 28.1.0 + jsdom: 29.1.1 transitivePeerDependencies: - msw @@ -9279,9 +9303,9 @@ snapshots: tr46: 5.1.1 webidl-conversions: 7.0.0 - whatwg-url@16.0.0: + whatwg-url@16.0.1: dependencies: - '@exodus/bytes': 1.11.0 + '@exodus/bytes': 1.15.1 tr46: 6.0.0 webidl-conversions: 8.0.1 transitivePeerDependencies: -- 2.51.2 From 9338e8aa03bd15a38224cd933536a66651b68303 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Thu, 11 Jun 2026 11:06:42 +0200 Subject: [PATCH 52/64] fix: mark errors as test run errors (#793) --- packages/extension/src/runner.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/extension/src/runner.ts b/packages/extension/src/runner.ts index c49764a..6743c15 100644 --- a/packages/extension/src/runner.ts +++ b/packages/extension/src/runner.ts @@ -310,27 +310,31 @@ export class TestRunner extends vscode.Disposable { // we only change the state of test cases to keep the correct test count // ignoring test files, test folders and suites - these only report syntax errors - private markNonTestCase(test: vscode.TestItem, result?: RunnerTaskResult) { + private markNonTestCase( + testRun: vscode.TestRun, + test: vscode.TestItem, + result?: RunnerTaskResult, + ) { if (!result) { log.verbose?.(`No task result for "${test.label}", ignoring`) return } // errors in a suite are stored only if it happens during discovery - const errors = result.errors?.map((err) => err.stack || err.message) + const errors = result.errors?.map((err) => testMessageForTestError(test, err as TestError)) if (!errors?.length) { log.verbose?.(`No errors found for "${test.label}"`) return } log.verbose?.(`Marking "${test.label}" as failed with ${errors.length} errors`) - test.error = errors.join('\n') + testRun.errored(test, errors, result?.duration) } private markResult(testRun: vscode.TestRun, test: vscode.TestItem, result?: RunnerTaskResult) { const isTestCase = getTestData(test) instanceof TestCase if (!isTestCase) { - this.markNonTestCase(test, result) + this.markNonTestCase(testRun, test, result) return } -- 2.51.2 From cc9c1c8ebe24ab1fcde3dc7c8904d60c102b03b3 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Thu, 11 Jun 2026 11:07:06 +0200 Subject: [PATCH 53/64] chore: release v1.50.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d5defe1..f567d97 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "explorer", "displayName": "Vitest", - "version": "1.50.5", + "version": "1.50.6", "description": "A Vite-native testing framework. It's fast!", "categories": [ "Testing" -- 2.51.2 From 4d4ceaea79dda132c8d96ac06a4af2c4bea127f2 Mon Sep 17 00:00:00 2001 From: Anthony Frasso Date: Mon, 29 Jun 2026 03:46:58 -0400 Subject: [PATCH 54/64] fix: normalize Windows drive letter matching test failure stacks (#796) Co-authored-by: Your Name --- packages/extension/src/runner.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/runner.ts b/packages/extension/src/runner.ts index 6743c15..93ee93c 100644 --- a/packages/extension/src/runner.ts +++ b/packages/extension/src/runner.ts @@ -552,7 +552,7 @@ function parseLocationFromStacks( ): DebuggerLocation | undefined { if (stacks.length === 0) return undefined - const targetFilepath = testItem.uri!.fsPath + const targetFilepath = normalizeDriveLetter(testItem.uri!.fsPath) for (const stack of stacks) { const { sourceFilepath, line, column } = getSourceFilepathAndLocationFromStack(stack) const sourceNormalizedPath = sourceFilepath && normalizeDriveLetter(sourceFilepath) -- 2.51.2 From 78c0a13cde25b32e8a17fb5cb43d424011c852a4 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Mon, 29 Jun 2026 09:47:40 +0200 Subject: [PATCH 55/64] chore: release v1.50.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f567d97..cec6f18 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "explorer", "displayName": "Vitest", - "version": "1.50.6", + "version": "1.50.7", "description": "A Vite-native testing framework. It's fast!", "categories": [ "Testing" -- 2.51.2 From 26538b8a7d47e69bd006427965ed51d21a863136 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Mon, 20 Jul 2026 08:42:10 +0100 Subject: [PATCH 56/64] chore: remove extensions file --- .vscode/extensions.json | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 25f8914..0000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - // See http://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": ["dbaeumer.vscode-eslint", "EditorConfig.EditorConfig"] -} -- 2.51.2 From fff352df19544b318c2a60aaeba758f655a09787 Mon Sep 17 00:00:00 2001 From: BroadlyWhitaker <300154862+BroadlyWhitaker@users.noreply.github.com> Date: Mon, 20 Jul 2026 09:49:37 +0200 Subject: [PATCH 57/64] feat: set diagnostic.source="Vitest" (#797) Set the source field to "Vitest" when inserting new diagnostics into vscode. --- packages/extension/src/diagnostic.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/extension/src/diagnostic.ts b/packages/extension/src/diagnostic.ts index 7c67954..6f65ead 100644 --- a/packages/extension/src/diagnostic.ts +++ b/packages/extension/src/diagnostic.ts @@ -15,6 +15,7 @@ export class ExtensionDiagnostic { error.message.toString(), vscode.DiagnosticSeverity.Error, ) + diagnostic.source = 'Vitest'; diagnostics.push(diagnostic) }) this.diagnostic.set(testFile, diagnostics) -- 2.51.2 From 01d22c6e15b625209c8f9e1d8ad26f178bfc93c3 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Mon, 20 Jul 2026 08:50:29 +0100 Subject: [PATCH 58/64] chore: fix fmt --- packages/extension/src/diagnostic.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension/src/diagnostic.ts b/packages/extension/src/diagnostic.ts index 6f65ead..0f73894 100644 --- a/packages/extension/src/diagnostic.ts +++ b/packages/extension/src/diagnostic.ts @@ -15,7 +15,7 @@ export class ExtensionDiagnostic { error.message.toString(), vscode.DiagnosticSeverity.Error, ) - diagnostic.source = 'Vitest'; + diagnostic.source = 'Vitest' diagnostics.push(diagnostic) }) this.diagnostic.set(testFile, diagnostics) -- 2.51.2 From 9ce6d87f0747cf9fd4f44cf572e3584c83e76462 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Mon, 20 Jul 2026 08:50:53 +0100 Subject: [PATCH 59/64] chore: release v1.50.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cec6f18..3b7016b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "explorer", "displayName": "Vitest", - "version": "1.50.7", + "version": "1.50.8", "description": "A Vite-native testing framework. It's fast!", "categories": [ "Testing" -- 2.51.2 From 4a4561cf6b169cd5c921001a40e405842ef4c68f Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 4 Aug 2026 15:37:53 +0200 Subject: [PATCH 60/64] fix: support Vitest 5 `testNamePattern` format (#802) --- packages/extension/src/apiProcess.ts | 9 +++ packages/extension/src/spawn/pkg.ts | 9 +++ packages/extension/src/spawn/ws.ts | 5 ++ packages/extension/src/testTree.ts | 8 ++- packages/extension/src/testTreeData.ts | 12 ++-- packages/extension/src/worker/index.ts | 2 +- packages/shared/src/emitter.ts | 9 ++- packages/shared/src/index.ts | 3 + pnpm-lock.yaml | 14 ++--- pnpm-workspace.yaml | 2 +- test/e2e/utils/helper.ts | 5 +- test/unit/TestData.test.ts | 82 +++++++++++++++----------- 12 files changed, 108 insertions(+), 52 deletions(-) diff --git a/packages/extension/src/apiProcess.ts b/packages/extension/src/apiProcess.ts index 65461e4..b4799fe 100644 --- a/packages/extension/src/apiProcess.ts +++ b/packages/extension/src/apiProcess.ts @@ -1,5 +1,6 @@ import type { SerializedProject } from 'vitest-vscode-shared' import type { VitestPackage } from './spawn/pkg' +import { usesJestTestNamePattern } from './spawn/pkg' import type { ExtensionWorkerEvents, VitestExtensionRPC } from './spawn/rpc' import type { ExtensionWorkerProcess } from './spawn/types' import type { ProcessSpawnOptions } from './spawn/ws' @@ -45,6 +46,10 @@ export class VitestProjectConfig { return this.pkg.version } + get usesJestTestNamePattern() { + return usesJestTestNamePattern(this.pkg) + } + get package() { return this.pkg } @@ -142,6 +147,10 @@ export class VitestProcessAPI { return this.config.package } + get usesJestTestNamePattern() { + return this.config.usesJestTestNamePattern + } + getPersistentProcessMeta() { return this.currentMeta } diff --git a/packages/extension/src/spawn/pkg.ts b/packages/extension/src/spawn/pkg.ts index b910516..9b22bda 100644 --- a/packages/extension/src/spawn/pkg.ts +++ b/packages/extension/src/spawn/pkg.ts @@ -30,6 +30,15 @@ export interface VitestPackage { runtime: 'deno' | 'node' } +// Before 5.0.0-rc.1 `testNamePattern` was matched the same way Jest does it: +// against task names joined with " ", starting with the empty name of the root suite. +// Since 5.0.0-rc.1 the pattern is tested against names joined with " > " instead. +// If the version is not known yet ("pnp"), it is updated from the "ready" +// event when the worker reports the actual runtime version. +export function usesJestTestNamePattern(pkg: VitestPackage): boolean { + return pkg.version === 'pnp' || !gte(pkg.version, '5.0.0-rc.1') +} + function isVitestInPackageJson(root: string) { const pkgJson = resolve(dirname(root), 'package.json') if (existsSync(pkgJson)) { diff --git a/packages/extension/src/spawn/ws.ts b/packages/extension/src/spawn/ws.ts index e21ee50..b25b20a 100644 --- a/packages/extension/src/spawn/ws.ts +++ b/packages/extension/src/spawn/ws.ts @@ -81,6 +81,11 @@ export function onWsConnection( if (message.type === 'debug') log.worker('info', ...message.args) if (message.type === 'ready') { + // the worker reports the version it actually runs; "pkg.version" can be + // a "pnp" placeholder when the package.json is not readable from the fs + if (message.version) { + pkg.version = message.version + } const { api, handlers } = createVitestRpc({ on: (listener) => ws.on('message', listener), send: (message) => ws.send(message), diff --git a/packages/extension/src/testTree.ts b/packages/extension/src/testTree.ts index 7121bfe..8a85b84 100644 --- a/packages/extension/src/testTree.ts +++ b/packages/extension/src/testTree.ts @@ -394,7 +394,9 @@ export class TestTree extends vscode.Disposable { ids.add(fileId) }) } else if (task.each) { - const fullName = getTaskFullName(task) + // the separator has to match the one used in getTestNamePattern + const separator = fileData.api.usesJestTestNamePattern ? ' ' : ' > ' + const fullName = getTaskFullName(task, separator) // order in the opposite order so we only match one item with the longest name const orderedTests = Object.entries(fileCachedTests).sort(([a1], [a2]) => a2.localeCompare(a1), @@ -491,6 +493,6 @@ function getAPIFromTestItem(testItem: vscode.TestItem): VitestProcessAPI | null return data.file.api } -function getTaskFullName(task: RunnerTask): string { - return `${task.suite ? `${getTaskFullName(task.suite)} ` : ''}${task.name}` +function getTaskFullName(task: RunnerTask, separator: string): string { + return `${task.suite ? `${getTaskFullName(task.suite, separator)}${separator}` : ''}${task.name}` } diff --git a/packages/extension/src/testTreeData.ts b/packages/extension/src/testTreeData.ts index 50c9a64..54a6b31 100644 --- a/packages/extension/src/testTreeData.ts +++ b/packages/extension/src/testTreeData.ts @@ -82,7 +82,7 @@ export class TestFile extends BaseTestData { class TaskName { constructor( - private readonly data: TestData, + private readonly data: TestCase | TestSuite, public readonly dynamic: boolean, ) {} @@ -99,9 +99,13 @@ class TaskName { patterns.push(escapeTestName(iter.label, iter.name.dynamic)) iter = iter.parent } - // vitest's test task name starts with ' ' of root suite - // It's considered as a bug, but it's not fixed yet for backward compatibility - return `\\s?${patterns.reverse().join(' ')}` + if (this.data.file.api.usesJestTestNamePattern) { + // vitest's test task name starts with ' ' of root suite + // It's considered as a bug, but it's not fixed until Vitest 5 for backward compatibility + return `\\s?${patterns.reverse().join(' ')}` + } + // since 5.0.0-rc.1 the pattern is matched against names joined with " > " + return patterns.reverse().join(' > ') } } diff --git a/packages/extension/src/worker/index.ts b/packages/extension/src/worker/index.ts index c44fc99..91ca4b2 100644 --- a/packages/extension/src/worker/index.ts +++ b/packages/extension/src/worker/index.ts @@ -73,7 +73,7 @@ emitter.on('message', async function onMessage(message: any) { }) worker.initRpc(rpc) reporter.initRpc(rpc) - emitter.ready(projects, workspaceSource, isLegacy) + emitter.ready(projects, workspaceSource, isLegacy, vitestModule.version) await worker.vitest.report('onInit', worker.vitest) } catch (err: any) { diff --git a/packages/shared/src/emitter.ts b/packages/shared/src/emitter.ts index cb59f78..14e3ea2 100644 --- a/packages/shared/src/emitter.ts +++ b/packages/shared/src/emitter.ts @@ -8,8 +8,13 @@ abstract class WorkerEventEmitter { abstract on(event: string, listener: (...args: any[]) => void): void abstract off(event: string, listener: (...args: any[]) => void): void - ready(projects: SerializedProject[], workspaceSource: string | false, legacy: boolean) { - this.sendWorkerEvent({ type: 'ready', projects, workspaceSource, legacy }) + ready( + projects: SerializedProject[], + workspaceSource: string | false, + legacy: boolean, + version: string | undefined, + ) { + this.sendWorkerEvent({ type: 'ready', projects, workspaceSource, legacy, version }) } error(err: any) { diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index ec688eb..cfd4da4 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -194,6 +194,9 @@ export interface EventReady { projects: SerializedProject[] workspaceSource: string | false legacy: boolean + // the actual runtime version, unlike VitestPackage.version + // this is also defined when vitest is resolved via yarn pnp + version: string | undefined } export interface EventDebug { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b043e63..1c0423f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -37,8 +37,8 @@ catalogs: specifier: ^0.0.6 version: 0.0.6 '@vscode/test-electron': - specifier: ^2.3.9 - version: 2.5.2 + specifier: ^3.1.0 + version: 3.1.0 '@vscode/vsce': specifier: ^3.1.0 version: 3.7.1 @@ -195,7 +195,7 @@ importers: version: 0.0.6 '@vscode/test-electron': specifier: 'catalog:' - version: 2.5.2 + version: 3.1.0 '@vscode/vsce': specifier: 'catalog:' version: 3.7.1 @@ -1944,9 +1944,9 @@ packages: resolution: {integrity: sha512-4i61OUv5PQr3GxhHOuUgHdgBDfIO/kXTPCsEyFiMaY4SOqQTgkTmyZLagHehjOgCfsXdcrJa3zgQ7zoc+Dh6hQ==} hasBin: true - '@vscode/test-electron@2.5.2': - resolution: {integrity: sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==} - engines: {node: '>=16'} + '@vscode/test-electron@3.1.0': + resolution: {integrity: sha512-CRqv5u+YYoseuNVJ6Tyo4k0sF0mx4qnKMihRB0PjsUF8Dc0WKtCXo6CNL6nWWm5esfFQsQA/pejMj4ZbpJVLTw==} + engines: {node: '>=22'} '@vscode/vsce-sign-alpine-arm64@2.0.6': resolution: {integrity: sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==} @@ -6220,7 +6220,7 @@ snapshots: supports-color: 9.4.0 yargs: 17.7.2 - '@vscode/test-electron@2.5.2': + '@vscode/test-electron@3.1.0': dependencies: http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e0e76d8..460d486 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -14,7 +14,7 @@ catalog: '@types/which': ^3.0.3 '@types/ws': ^8.5.10 '@vscode/test-cli': ^0.0.6 - '@vscode/test-electron': ^2.3.9 + '@vscode/test-electron': ^3.1.0 '@vscode/vsce': ^3.1.0 '@vue/reactivity': ^3.2.33 acorn: ^8.12.0 diff --git a/test/e2e/utils/helper.ts b/test/e2e/utils/helper.ts index 1d5b5bf..6bfdd98 100644 --- a/test/e2e/utils/helper.ts +++ b/test/e2e/utils/helper.ts @@ -44,10 +44,13 @@ export const test = baseTest.extend<{ launch: LaunchFixture; taskName: string; l const trace = (options.trace ?? defaultConfig.VSCODE_E2E_TRACE) === 'on' const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'vscode-e2e-')) + // inherited from the extension host when tests run in a terminal inside + // VS Code; it would force the spawned VS Code to run as plain Node + const { ELECTRON_RUN_AS_NODE: _, ...env } = process.env const app = await _electron.launch({ executablePath, env: { - ...process.env, + ...env, VITEST_VSCODE_E2E_LOG_FILE: logPath, VITEST_VSCODE_LOG: 'verbose', }, diff --git a/test/unit/TestData.test.ts b/test/unit/TestData.test.ts index cf40040..1990950 100644 --- a/test/unit/TestData.test.ts +++ b/test/unit/TestData.test.ts @@ -11,53 +11,69 @@ import { describe('TestData', () => { const ctrl = vscode.tests.createTestController('mocha', 'Vitest') - describe('TestFile', () => { - it('getTestNamePattern', async () => { - const filepath = path.resolve(__dirname, './fixtures/discover/00_simple.ts') - const uri = vscode.Uri.file(filepath) - const folderItem = ctrl.createTestItem( - path.dirname(filepath), - path.basename(path.dirname(filepath)), - uri, - ) - TestFolder.register(folderItem) - const testItem = ctrl.createTestItem(filepath, path.basename(filepath), uri) - ctrl.items.add(testItem) - const file = TestFile.register( - testItem, - folderItem, - filepath, - null as any, // not used yet - { project: '', pool: 'trheads' }, - ) - const suiteItem = ctrl.createTestItem(`${filepath}_1`, 'describe', uri) - testItem.children.add(suiteItem) - const testItem1 = ctrl.createTestItem(`${filepath}_1_1`, 'test', uri) + function createTestTree(id: string, api: { usesJestTestNamePattern: boolean }) { + const filepath = path.resolve(__dirname, `./fixtures/discover/00_simple_${id}.ts`) + const uri = vscode.Uri.file(filepath) + const folderItem = ctrl.createTestItem( + path.dirname(filepath), + path.basename(path.dirname(filepath)), + uri, + ) + TestFolder.register(folderItem) + const testItem = ctrl.createTestItem(filepath, path.basename(filepath), uri) + ctrl.items.add(testItem) + const file = TestFile.register(testItem, folderItem, filepath, api as any, { + project: '', + pool: 'trheads', + }) + const suiteItem = ctrl.createTestItem(`${filepath}_1`, 'describe', uri) + testItem.children.add(suiteItem) - const testItem2 = ctrl.createTestItem(`${filepath}_1_2`, 'test 1', uri) + const testItem1 = ctrl.createTestItem(`${filepath}_1_1`, 'test', uri) - const testItem3 = ctrl.createTestItem(`${filepath}_1_3`, 'test 2', uri) + const testItem2 = ctrl.createTestItem(`${filepath}_1_2`, 'test 1', uri) - suiteItem.children.add(testItem1) - suiteItem.children.add(testItem2) - suiteItem.children.add(testItem3) + const testItem3 = ctrl.createTestItem(`${filepath}_1_3`, 'test 2', uri) - const suite = TestSuite.register(suiteItem, testItem, file, false) + suiteItem.children.add(testItem1) + suiteItem.children.add(testItem2) + suiteItem.children.add(testItem3) - expect(suite.getTestNamePattern()).to.equal('^\\s?describe') + const suite = TestSuite.register(suiteItem, testItem, file, false) + + const test1 = TestCase.register(testItem1, suiteItem, file, false) + const test2 = TestCase.register(testItem2, suiteItem, file, false) + const test3 = TestCase.register(testItem3, suiteItem, file, false) - const test1 = TestCase.register(testItem1, suiteItem, file, false) - const test2 = TestCase.register(testItem2, suiteItem, file, false) - const test3 = TestCase.register(testItem3, suiteItem, file, false) + expect(testItem1.parent).to.exist - expect(testItem1.parent).to.exist + return { suite, test1, test2, test3 } + } + describe('TestFile', () => { + it('getTestNamePattern with jest pattern (vitest < 5)', async () => { + const { suite, test1, test2, test3 } = createTestTree('jest', { + usesJestTestNamePattern: true, + }) + + expect(suite.getTestNamePattern()).to.equal('^\\s?describe') expect(test1.getTestNamePattern()).to.equal('^\\s?describe test$') expect(test2.getTestNamePattern()).to.equal('^\\s?describe test 1$') expect(test3.getTestNamePattern()).to.equal('^\\s?describe test 2$') }) + it('getTestNamePattern (vitest 5)', async () => { + const { suite, test1, test2, test3 } = createTestTree('vitest', { + usesJestTestNamePattern: false, + }) + + expect(suite.getTestNamePattern()).to.equal('^describe') + expect(test1.getTestNamePattern()).to.equal('^describe > test$') + expect(test2.getTestNamePattern()).to.equal('^describe > test 1$') + expect(test3.getTestNamePattern()).to.equal('^describe > test 2$') + }) + it('throws an error if data was not set', () => { expect(() => getTestData({ label: 'invalid test' } as any)).to.throw( /Test data not found for "invalid test"/, -- 2.51.2 From 1603e4d5aee7500df803898092d7ce3f3a0124a6 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Fri, 28 Aug 2026 16:53:58 +0200 Subject: [PATCH 61/64] fix: ignore computed member calls from esbuild `using` helper in legacy AST collector (#808) --- packages/worker-legacy/src/collect.ts | 8 ++++- test/unit/collect.test.ts | 48 +++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 test/unit/collect.test.ts diff --git a/packages/worker-legacy/src/collect.ts b/packages/worker-legacy/src/collect.ts index 2e5e32a..cfc1824 100644 --- a/packages/worker-legacy/src/collect.ts +++ b/packages/worker-legacy/src/collect.ts @@ -24,7 +24,7 @@ interface ParsedSuite extends RunnerTestSuite { dynamic: boolean } -interface LocalCallDefinition { +export interface LocalCallDefinition { start: number end: number name: string @@ -96,6 +96,12 @@ export function astParseFile(filepath: string, code: string) { return getName(callee.tag) } if (callee.type === 'MemberExpression') { + // Vitest chains always use dot access (`test.skip`, `describe.each`). + // A computed access like `it[1].call(it[2])` comes from esbuild's + // `using` helper (`__callDispose`) and is not a Vitest call. + if (callee.computed) { + return null + } if (callee.object?.type === 'Identifier' && isVitestFunctionName(callee.object.name)) { return callee.object?.name } diff --git a/test/unit/collect.test.ts b/test/unit/collect.test.ts new file mode 100644 index 0000000..426e1f6 --- /dev/null +++ b/test/unit/collect.test.ts @@ -0,0 +1,48 @@ +import { expect } from 'chai' +import { astParseFile, type LocalCallDefinition } from '../../packages/worker-legacy/src/collect' + +const sorted = (defs: LocalCallDefinition[]) => [...defs].sort((a, b) => a.start - b.start) + +describe('astParseFile', () => { + it('ignores esbuild "using" helper calls like it[1].call(it[2])', () => { + // simplified output of esbuild's `__callDispose` helper (target < esnext) + const code = ` +var __callDispose = (stack, error, hasError) => { + var next = (it) => { + while (it = stack.pop()) { + var result = it[1] && it[1].call(it[2]); + } + }; + return next(); +}; +describe('suite', () => { + test('case', () => { + var _stack = []; + try { + const x = __using(_stack, 1); + } finally { + __callDispose(_stack, _error, _hasError); + } + }); +}); +` + const { definitions } = astParseFile('/test.ts', code) + expect(sorted(definitions).map((d) => [d.type, d.name])).to.eql([ + ['suite', 'suite'], + ['test', 'case'], + ]) + }) + + it('still collects dot-access chains', () => { + const code = ` +describe.concurrent('suite', () => { + test.skip('case', () => {}) +}) +` + const { definitions } = astParseFile('/test.ts', code) + expect(sorted(definitions).map((d) => [d.type, d.name, d.mode])).to.eql([ + ['suite', 'suite', 'run'], + ['test', 'case', 'skip'], + ]) + }) +}) -- 2.51.2 From 31cb20fafe381a160b0cd812b98de18eea23354e Mon Sep 17 00:00:00 2001 From: Vladimir Date: Wed, 2 Sep 2026 22:17:25 +0200 Subject: [PATCH 62/64] chore: support ecosystem ci (#809) --- package.json | 6 +++--- packages/extension/package.json | 1 - packages/extension/src/runner.ts | 13 +++++++++++-- pnpm-lock.yaml | 26 +++++++++++++++++++++++--- scripts/ecosystem-ci.mts | 10 +++++++--- test/e2e/runner.test.ts | 15 ++++++++++++++- test/e2e/utils/assertions.ts | 6 ++++++ tsdown.config.mjs | 7 ++++++- 8 files changed, 70 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 3b7016b..85b2ea1 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,8 @@ "test:watch": "vscode-test --watch-files src/**/*.ts --watch-files test/**/*.test.ts", "test-e2e": "vitest --root test/e2e", "test-e2e:legacy": "TEST_LEGACY=true vitest --root test/e2e", - "ecosystem-ci:build": "pnpm build", - "ecosystem-ci:test": "tsx ./scripts/ecosystem-ci.mts", + "ecosystem-ci:build": "SKIP_LEGACY=true pnpm build", + "ecosystem-ci:test": "SKIP_LEGACY=true tsx ./scripts/ecosystem-ci.mts", "typecheck": "tsc -b ./ --noEmit", "fmt": "oxfmt --check", "fmt:fix": "oxfmt" @@ -414,6 +414,6 @@ "engines": { "vscode": "^1.88.0" }, - "packageManager": "pnpm@10.11.1", + "packageManager": "pnpm@10.34.5", "pricing": "Free" } diff --git a/packages/extension/package.json b/packages/extension/package.json index f079b84..b1f4936 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -5,7 +5,6 @@ "dependencies": { "@types/picomatch": "catalog:latest", "@vitest/browser": "catalog:v3", - "@vitest/runner": "catalog:v3", "picomatch": "catalog:latest", "vitest": "catalog:v3", "vitest-vscode-shared": "workspace:*" diff --git a/packages/extension/src/runner.ts b/packages/extension/src/runner.ts index 93ee93c..c38b076 100644 --- a/packages/extension/src/runner.ts +++ b/packages/extension/src/runner.ts @@ -1,4 +1,4 @@ -import type { ParsedStack, RunnerTaskResult, TestError } from 'vitest' +import type { ParsedStack, RunnerTask, RunnerTaskResult, TestError } from 'vitest' import type { ExtensionTestSpecification } from 'vitest-vscode-shared' import type { RunHandle, VitestProcessAPI } from './apiProcess' import type { ExtensionDiagnostic } from './diagnostic' @@ -7,7 +7,6 @@ import type { TestTree } from './testTree' import crypto from 'node:crypto' import path from 'node:path' import { stripVTControlCharacters } from 'node:util' -import { getTasks } from '@vitest/runner/utils' import { basename, normalize, relative } from 'pathe' import { normalizeDriveLetter } from 'vitest-vscode-shared' import * as vscode from 'vscode' @@ -17,6 +16,16 @@ import { log } from './log' import { getTestData, TestCase, TestFile, TestFolder, TestSuite } from './testTreeData' import { getErrorMessage, showVitestError } from './utils' +// Local copy of `getTasks` from `@vitest/runner/utils`. The extension host +// must not bundle `@vitest/runner` because the extension supports multiple +// Vitest majors and the bundled copy would not match the user's version. +function getTasks(task: RunnerTask): RunnerTask[] { + if (task.type === 'test') { + return [task] + } + return [task, ...task.tasks.flatMap(getTasks)] +} + export class TestRunner extends vscode.Disposable { protected testRun: vscode.TestRun | undefined // The request tied to the testRun diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1c0423f..261fb98 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -280,9 +280,6 @@ importers: '@vitest/browser': specifier: catalog:v3 version: 3.2.4(playwright@1.57.0)(vite@7.2.6(@types/node@24.10.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))(vitest@3.2.4) - '@vitest/runner': - specifier: catalog:v3 - version: 3.2.4 picomatch: specifier: catalog:latest version: 4.0.3 @@ -1365,48 +1362,56 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@oxfmt/binding-linux-arm64-musl@0.37.0': resolution: {integrity: sha512-EZj3TurW1iLbq+7tBr++wsxwFyD+pvjMrTNRuSynDrs8J7w46cu/ZIzU/lFw7OG1/tDRDZ9nrKXxwbvIKXo2zA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@oxfmt/binding-linux-ppc64-gnu@0.37.0': resolution: {integrity: sha512-ELXrDe1xRj+f7VpzJO2j54izMbi+Hov+kdqusXO3T1BwVEbA5sWgZrVMqkwEsj4k6Lw/obJK1SLUeNulR1D//g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] '@oxfmt/binding-linux-riscv64-gnu@0.37.0': resolution: {integrity: sha512-79gMZgLD62dGmo5Xl4gaMc6NHRFj3GuxPrchHBlW54tcRSXTtb3gLh/J6Bl8nbbzSFRQGR7dkNQ8yYadXt6txQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [glibc] '@oxfmt/binding-linux-riscv64-musl@0.37.0': resolution: {integrity: sha512-QFdi9OhyWxnh975jeG490atcINXZwZb7epyNASPaT4wcodOTuDitrDgSPT8CFl8BcGOFTGZ6c3P/s8Afeg1Ngg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] + libc: [musl] '@oxfmt/binding-linux-s390x-gnu@0.37.0': resolution: {integrity: sha512-qweAj7+pLFQXfe3UU7EZiOmo+/2SWjzVZjyyTDcrZAT0E92zEKJBvYpHinUAOqipfo2Xlp8GIfq0FSb5Tmqd8g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] '@oxfmt/binding-linux-x64-gnu@0.37.0': resolution: {integrity: sha512-Lqc/0vS20qzZLw1ThpWn1hQgRqj4rM+E7PuBzrqp+wLH5lYFqieAiontGpl2pMPvJ0QrmQYav9mslHlAB5kOSQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@oxfmt/binding-linux-x64-musl@0.37.0': resolution: {integrity: sha512-TnJm22+1cEcpYXzbcXS5Z9+9c+R0ronFdx5bG4OTdOL/wSpQQKzc2izgAXJ03QkP3tq7aAPhlhhxasvH3xgoUA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@oxfmt/binding-openharmony-arm64@0.37.0': resolution: {integrity: sha512-YLq27qMur3hPUponvV3Zr0oHxowox71j3+nc+/oCc1O+M0zFafhd6AoAoCiRrSYRW+asWhz3/UMPh0bYpimcMw==} @@ -1486,24 +1491,28 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-arm64-musl@1.0.0-rc.3': resolution: {integrity: sha512-Z03/wrqau9Bicfgb3Dbs6SYTHliELk2PM2LpG2nFd+cGupTMF5kanLEcj2vuuJLLhptNyS61rtk7SOZ+lPsTUA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] '@rolldown/binding-linux-x64-gnu@1.0.0-rc.3': resolution: {integrity: sha512-iSXXZsQp08CSilff/DCTFZHSVEpEwdicV3W8idHyrByrcsRDVh9sGC3sev6d8BygSGj3vt8GvUKBPCoyMA4tgQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] '@rolldown/binding-linux-x64-musl@1.0.0-rc.3': resolution: {integrity: sha512-qaj+MFudtdCv9xZo9znFvkgoajLdc+vwf0Kz5N44g+LU5XMe+IsACgn3UG7uTRlCCvhMAGXm1XlpEA5bZBrOcw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] '@rolldown/binding-openharmony-arm64@1.0.0-rc.3': resolution: {integrity: sha512-U662UnMETyjT65gFmG9ma+XziENrs7BBnENi/27swZPYagubfHRirXHG2oMl+pEax2WvO7Kb9gHZmMakpYqBHQ==} @@ -1572,56 +1581,67 @@ packages: resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.53.3': resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.53.3': resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.53.3': resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.53.3': resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.53.3': resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.53.3': resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.53.3': resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.53.3': resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.53.3': resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.53.3': resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-openharmony-arm64@4.53.3': resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} diff --git a/scripts/ecosystem-ci.mts b/scripts/ecosystem-ci.mts index 6c83bea..902908a 100644 --- a/scripts/ecosystem-ci.mts +++ b/scripts/ecosystem-ci.mts @@ -9,12 +9,16 @@ async function main() { await $`pnpm -C samples/browser i` await $`pnpm -C samples/imba i` - // setup pakcage overrides for samples used by test-e2e + // the ecosystem CI overrides every `@vitest/*` package with the latest + // version, so the legacy worker (Vitest 3) cannot be built or tested there + const unitTestArgs = + process.env.SKIP_LEGACY === 'true' ? ['--ignore', 'test/unit/collect.test.ts'] : [] + if (process.env.CI === 'true' && process.platform === 'linux') { - await $`xvfb-run pnpm test` + await $`xvfb-run pnpm test ${unitTestArgs}` await $`xvfb-run pnpm test-e2e --retry 2` } else { - await $`pnpm test` + await $`pnpm test ${unitTestArgs}` await $`pnpm test-e2e` } } diff --git a/test/e2e/runner.test.ts b/test/e2e/runner.test.ts index 9308695..642e0da 100644 --- a/test/e2e/runner.test.ts +++ b/test/e2e/runner.test.ts @@ -1,4 +1,6 @@ import { readFileSync, rmSync } from 'node:fs' +import { createRequire } from 'node:module' +import { resolve } from 'node:path' import { beforeAll, beforeEach, describe, onTestFailed } from 'vitest' import { expect } from '@playwright/test' import { test } from './utils/helper' @@ -193,8 +195,19 @@ test('watcher updates the file if there are several config files', async ({ laun }) }) +// Vitest 5 formats the values in `test.each` titles with pretty-format +// instead of loupe, so strings are no longer wrapped in quotes +function getVitestMajor(sample: string): number { + const { version } = createRequire(resolve(sample, 'package.json'))('vitest/package.json') + return Number(version.split('.')[0]) +} + test('ast collector keeps the pattern on rerun', async ({ launch }) => { const sample = 'samples/ast-collector' + const tableTestName = + getVitestMajor(sample) >= 5 + ? 'table1: returns ab when a is added b' + : "table1: returns 'ab' when 'a' is added 'b'" const { tester } = await launch({ workspacePath: sample, @@ -235,7 +248,7 @@ test('ast collector keeps the pattern on rerun', async ({ launch }) => { // table1: returns $expected when $a is added $b 'pattern|6': 'waiting', 'table1: returns 2 when 1 is added 1': 'passed', - "table1: returns 'ab' when 'a' is added 'b'": 'passed', + [tableTestName]: 'passed', }, // testing %s 'pattern|9': 'waiting', diff --git a/test/e2e/utils/assertions.ts b/test/e2e/utils/assertions.ts index 028bb94..81e5bd5 100644 --- a/test/e2e/utils/assertions.ts +++ b/test/e2e/utils/assertions.ts @@ -47,6 +47,12 @@ expect.extend({ }, async toHaveTests(item: TesterTestItem, tests: TestsTree) { const page = item.page + // the "Resolving Vitest..." item stays in the tree until every config is + // resolved, but roots are added as soon as their own config is resolved. + // Wait for it to go away, otherwise every row index shifts by one later. + await expect(page.locator('[aria-label*="Resolving Vitest..."]')).not.toBeAttached({ + timeout: 10_000, + }) const depth = Number(await item.locator.getAttribute('aria-level')) const currentIndex = Number(await item.locator.getAttribute('data-index')) diff --git a/tsdown.config.mjs b/tsdown.config.mjs index aa011f3..46ab597 100644 --- a/tsdown.config.mjs +++ b/tsdown.config.mjs @@ -2,12 +2,17 @@ import { defineConfig } from 'tsdown' +// The legacy worker bundles Vitest 3 packages. Set SKIP_LEGACY=true to leave it +// out of the build, e.g. in the Vitest ecosystem CI where every `@vitest/*` +// package is overridden with the latest version. +const skipLegacy = process.env.SKIP_LEGACY === 'true' + export default defineConfig([ { entry: { extension: './packages/extension/src/extension.ts', worker: './packages/extension/src/worker/index.ts', - workerLegacy: './packages/worker-legacy/src/index.ts', + ...(skipLegacy ? {} : { workerLegacy: './packages/worker-legacy/src/index.ts' }), workerNew: './packages/worker/src/index.ts', }, external: ['vscode'], -- 2.51.2 From 3ab6afba37f24b6de38b2226bdfe7c5c58441324 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 7 Sep 2026 05:04:38 +0900 Subject: [PATCH 63/64] refactor: group worker config metadata (#815) * refactor: group worker config metadata Co-authored-by: OpenCode (gpt-5.6-sol) * refactor: name worker ready metadata Co-authored-by: OpenCode (gpt-5.6-sol) --------- Co-authored-by: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Co-authored-by: OpenCode (gpt-5.6-sol) --- packages/extension/src/api.ts | 4 ++-- packages/extension/src/apiProcess.ts | 18 ++++++++++++------ packages/extension/src/spawn/terminal.ts | 3 +-- packages/extension/src/spawn/ws.ts | 16 +++++++++------- packages/extension/src/worker/index.ts | 4 ++-- packages/shared/src/emitter.ts | 11 +++-------- packages/shared/src/index.ts | 8 ++++++-- packages/worker-legacy/src/index.ts | 5 +++-- packages/worker/src/index.ts | 5 +++-- 9 files changed, 41 insertions(+), 33 deletions(-) diff --git a/packages/extension/src/api.ts b/packages/extension/src/api.ts index 60ce8cd..5a6a6eb 100644 --- a/packages/extension/src/api.ts +++ b/packages/extension/src/api.ts @@ -187,13 +187,13 @@ async function createVitestProcessAPI( pkg: VitestPackage, ): Promise { return withProcess(pkg, async (meta) => { - meta.projects.forEach((project) => { + meta.metadata.projects.forEach((project) => { if (project.config) { usedConfigs.add(project.config) } }) const files = await meta.rpc.getFiles() - const config = new VitestProjectConfig(pkg, meta.projects, meta.workspaceSource) + const config = new VitestProjectConfig(pkg, meta.metadata) const api = new VitestProcessAPI(config) return { api, files } }) diff --git a/packages/extension/src/apiProcess.ts b/packages/extension/src/apiProcess.ts index b4799fe..5801efa 100644 --- a/packages/extension/src/apiProcess.ts +++ b/packages/extension/src/apiProcess.ts @@ -1,4 +1,4 @@ -import type { SerializedProject } from 'vitest-vscode-shared' +import type { SerializedProject, WorkerReadyMetadata } from 'vitest-vscode-shared' import type { VitestPackage } from './spawn/pkg' import { usesJestTestNamePattern } from './spawn/pkg' import type { ExtensionWorkerEvents, VitestExtensionRPC } from './spawn/rpc' @@ -22,8 +22,7 @@ export class VitestProjectConfig { constructor( readonly pkg: VitestPackage, - readonly projects: SerializedProject[], - readonly workspaceSource: string | false, + readonly metadata: WorkerReadyMetadata, ) { this.id = normalize(pkg.id) this.workspaceFolder = pkg.folder @@ -42,6 +41,14 @@ export class VitestProjectConfig { return this.projects.map((p) => p.config).filter((n) => n != null) } + get projects(): SerializedProject[] { + return this.metadata.projects + } + + get workspaceSource() { + return this.metadata.workspaceSource + } + get version() { return this.pkg.version } @@ -111,7 +118,7 @@ export class VitestProcessAPI { * a handle wrapping the existing process (without closing it). */ static forDebug(pkg: VitestPackage, meta: ResolvedMeta): VitestProcessAPI { - const config = new VitestProjectConfig(pkg, meta.projects, meta.workspaceSource) + const config = new VitestProjectConfig(pkg, meta.metadata) const api = new VitestProcessAPI(config) api.currentMeta = meta return api @@ -321,10 +328,9 @@ export interface RunHandlers { export interface ResolvedMeta { rpc: VitestExtensionRPC + metadata: WorkerReadyMetadata process: ExtensionWorkerProcess - workspaceSource: string | false pkg: VitestPackage - projects: SerializedProject[] handlers: { onProcessLog: (listener: ExtensionWorkerEvents['onProcessLog']) => void onConsoleLog: (listener: ExtensionWorkerEvents['onConsoleLog']) => void diff --git a/packages/extension/src/spawn/terminal.ts b/packages/extension/src/spawn/terminal.ts index 793efc4..0c25b5b 100644 --- a/packages/extension/src/spawn/terminal.ts +++ b/packages/extension/src/spawn/terminal.ts @@ -93,11 +93,10 @@ export async function createVitestTerminalProcess( const vitestProcess = new ExtensionTerminalProcess(terminal, server, meta.ws) return { rpc: meta.rpc, + metadata: meta.metadata, handlers: meta.handlers, pkg, - workspaceSource: meta.workspaceSource, process: vitestProcess, - projects: meta.projects, dispose: meta.dispose, } } diff --git a/packages/extension/src/spawn/ws.ts b/packages/extension/src/spawn/ws.ts index b25b20a..65c157d 100644 --- a/packages/extension/src/spawn/ws.ts +++ b/packages/extension/src/spawn/ws.ts @@ -114,14 +114,16 @@ export function onWsConnection( } onStart({ rpc: api, - workspaceSource: message.workspaceSource, + metadata: { + ...message.metadata, + projects: message.metadata.projects.map((p) => { + if (p.dir) { + p.dir = resolve(pkg.cwd, p.dir) + } + return p + }), + }, handlers, - projects: message.projects.map((p) => { - if (p.dir) { - p.dir = resolve(pkg.cwd, p.dir) - } - return p - }), ws, pkg, async dispose() { diff --git a/packages/extension/src/worker/index.ts b/packages/extension/src/worker/index.ts index 91ca4b2..6b86cca 100644 --- a/packages/extension/src/worker/index.ts +++ b/packages/extension/src/worker/index.ts @@ -39,7 +39,7 @@ emitter.on('message', async function onMessage(message: any) { const workerPath = pathToFileURL(join(__dirname, workerName)) const initModule = await import(workerPath.toString()) - const { createWorker, reporter, projects, workspaceSource } = await initModule.initVitest( + const { createWorker, metadata, reporter } = await initModule.initVitest( vitestModule, data, emitter, @@ -73,7 +73,7 @@ emitter.on('message', async function onMessage(message: any) { }) worker.initRpc(rpc) reporter.initRpc(rpc) - emitter.ready(projects, workspaceSource, isLegacy, vitestModule.version) + emitter.ready(metadata, isLegacy, vitestModule.version) await worker.vitest.report('onInit', worker.vitest) } catch (err: any) { diff --git a/packages/shared/src/emitter.ts b/packages/shared/src/emitter.ts index 14e3ea2..fcd5ea2 100644 --- a/packages/shared/src/emitter.ts +++ b/packages/shared/src/emitter.ts @@ -1,4 +1,4 @@ -import type { SerializedProject, WorkerEvent } from 'vitest-vscode-shared' +import type { WorkerEvent, WorkerReadyMetadata } from 'vitest-vscode-shared' import type WebSocket from 'ws' abstract class WorkerEventEmitter { @@ -8,13 +8,8 @@ abstract class WorkerEventEmitter { abstract on(event: string, listener: (...args: any[]) => void): void abstract off(event: string, listener: (...args: any[]) => void): void - ready( - projects: SerializedProject[], - workspaceSource: string | false, - legacy: boolean, - version: string | undefined, - ) { - this.sendWorkerEvent({ type: 'ready', projects, workspaceSource, legacy, version }) + ready(metadata: WorkerReadyMetadata, legacy: boolean, version: string | undefined) { + this.sendWorkerEvent({ type: 'ready', metadata, legacy, version }) } error(err: any) { diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts index cfd4da4..9e1cd88 100644 --- a/packages/shared/src/index.ts +++ b/packages/shared/src/index.ts @@ -189,10 +189,14 @@ export interface SerializedProject { } } -export interface EventReady { - type: 'ready' +export interface WorkerReadyMetadata { projects: SerializedProject[] workspaceSource: string | false +} + +export interface EventReady { + type: 'ready' + metadata: WorkerReadyMetadata legacy: boolean // the actual runtime version, unlike VitestPackage.version // this is also defined when vitest is resolved via yarn pnp diff --git a/packages/worker-legacy/src/index.ts b/packages/worker-legacy/src/index.ts index c1c9405..cdb0bdb 100644 --- a/packages/worker-legacy/src/index.ts +++ b/packages/worker-legacy/src/index.ts @@ -1,5 +1,6 @@ import type { SerializedProject, + WorkerReadyMetadata, WorkerRunnerOptions, WorkerWSEventEmitter, } from 'vitest-vscode-shared' @@ -202,11 +203,11 @@ export async function initVitest( : vitest.config.workspace != null || vitest.config.projects != null ? vitest.server.config.configFile || false : false + const metadata: WorkerReadyMetadata = { projects, workspaceSource } return { vitest, reporter, - workspaceSource, - projects, + metadata, meta, createWorker() { return new ExtensionWorker(vitest, !!data.debug, emitter) diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts index d5c5583..727004a 100644 --- a/packages/worker/src/index.ts +++ b/packages/worker/src/index.ts @@ -1,5 +1,6 @@ import type { SerializedProject, + WorkerReadyMetadata, WorkerRunnerOptions, WorkerWSEventEmitter, } from 'vitest-vscode-shared' @@ -167,11 +168,11 @@ export async function initVitest( const workspaceSource: string | false = vitest.config.projects != null ? vitest.vite.config.configFile || false : false + const metadata: WorkerReadyMetadata = { projects, workspaceSource } return { vitest, reporter, - workspaceSource, - projects, + metadata, meta, createWorker() { return new ExtensionWorker(vitest, !!data.debug, emitter) -- 2.51.2 From fcff1c0ec9ed507359e4e7c0523818f1a1b4d3d7 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 7 Sep 2026 15:52:35 +0900 Subject: [PATCH 64/64] ci: cancel superseded workflow runs (#817) Co-authored-by: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Co-authored-by: OpenCode (gpt-5.6-sol) --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ae97ed..306480e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,10 @@ on: pull_request: branches: [main] +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: lint: runs-on: macos-latest