Skip to content

Commit

Permalink
chore: improve API
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va committed Feb 19, 2024
1 parent e8311bb commit a8dd1ef
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 36 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@
"scripts": {
"vscode:prepublish": "pnpm compile",
"release": "bumpp package.json --commit --push --tag && git update-ref refs/heads/release refs/heads/main && git push origin release",
"compile": "tsup",
"compile": "tsup --minify",
"package": "vsce package --no-dependencies",
"watch": "tsup --watch --sourcemap",
"test": "vitest run --config vite.global-config.ts",
Expand Down
73 changes: 73 additions & 0 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Worker } from 'node:worker_threads'
import type * as vscode from 'vscode'
import { dirname, resolve } from 'pathe'
import { type BirpcReturn, createBirpc } from 'birpc'
import type { ResolvedConfig } from 'vitest'
import { log } from './log'
import { workerPath } from './constants'

export interface BirpcMethods {
getFiles: () => Promise<string[]>
getConfig: () => Promise<ResolvedConfig>
terminate: () => void
}

export interface BirpcEvents {
onReady: () => void
onError: (err: object) => void
}

type VitestAPI = BirpcReturn<BirpcMethods, BirpcEvents>

export async function createVitestAPI(workspace: vscode.WorkspaceFolder) {
try {
const root = workspace.uri.fsPath
const vitestPath = require.resolve('vitest', { paths: [root] }) // resolves to cjs
const vitestNodePath = resolve(dirname(vitestPath), './dist/node.js')
log.info('[API]', 'Running Vitest from', vitestNodePath)
const worker = new Worker(workerPath, {
workerData: {
root,
vitestPath: vitestNodePath,
},
})
worker.stdout.on('data', (d) => {
log.info('[Worker]', d.toString())
})
worker.stderr.on('data', (d) => {
log.error('[Worker]', d.toString())
})

return await new Promise<VitestAPI>((resolve, reject) => {
const api = createBirpc<BirpcMethods, BirpcEvents>(
{
onReady() {
log.info('[API]', `Vitest for "${workspace.name}" workspace folder is resolved.`)
resolve(new Proxy(api, {
get(target, prop, receiver) {
if (prop === 'then')
return undefined
return Reflect.get(target, prop, receiver)
},
}))
},
onError(err) {
reject(err)
},
},
{
on(listener) {
worker.on('message', listener)
},
post(message) {
worker.postMessage(message)
},
},
)
})
}
catch (err: any) {
log.error('[API]', err?.stack)
return null
}
}
40 changes: 17 additions & 23 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { Worker } from 'node:worker_threads'
import { dirname, resolve } from 'node:path'
import * as vscode from 'vscode'

import { effect } from '@vue/reactivity'
Expand Down Expand Up @@ -28,30 +26,26 @@ import { TestWatcher } from './watch'
import type { VitestWorkspaceConfig } from './config'
import { fetchVitestConfig } from './pure/watch/vitestConfig'
import { openTestTag } from './tags'
import { workerPath } from './constants'
import { createVitestAPI } from './api'

export async function activate(context: vscode.ExtensionContext) {
const workspacePaths = vscode.workspace.workspaceFolders?.map(x => x.uri.fsPath)
if (workspacePaths?.length) {
const vitestPath = require.resolve('vitest', { paths: workspacePaths }) // resolves to cjs
log.info('vitest path', vitestPath, resolve(dirname(vitestPath), './dist/node.js'))
const worker = new Worker(workerPath, {
workerData: {
root: workspacePaths[0],
vitestPath: resolve(dirname(vitestPath), './dist/node.js'),
},
})
worker.stdout.on('data', (d) => {
log.info('worker log', d.toString())
})
worker.stderr.on('data', (d) => {
log.error('worker error', d.toString())
})
worker.on('message', (d) => {
log.info(JSON.stringify(d))
})
const workspaces = vscode.workspace.workspaceFolders
if (workspaces) {
try {
const vitestAPIs = await Promise.all(workspaces.map(async (folder) => {
const api = await createVitestAPI(folder)
return {
folder,
api,
config: await api?.getConfig(),
}
}))
log.info(vitestAPIs.map(v => `${v.folder.name}: resolved as ${v.config ? (v.config.name || 'core') : 'Failed'}`))
}
catch (err: any) {
log.error('[Vitest API]', err?.stack)
}
}

await detectVitestEnvironmentFolders()
if (vitestEnvironmentFolders.length === 0) {
log.info('The extension is not activated because no Vitest environment was detected.')
Expand Down
40 changes: 40 additions & 0 deletions src/worker/rpc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { parentPort } from 'node:worker_threads'
import { createBirpc } from 'birpc'
import type { Vitest } from 'vitest'
import type { BirpcEvents, BirpcMethods } from '../api'

export function createWorkerRPC(vitest: Vitest) {
return createBirpc<BirpcEvents, BirpcMethods>({
async getFiles() {
const files = await vitest.globTestFiles()
return files.map(([_, spec]) => spec)
},
async getConfig() {
const config = vitest.getCoreWorkspaceProject().getSerializableConfig()
return config
},
async terminate() {
await vitest.close()
},
}, {
eventNames: ['onReady', 'onError'],
on(listener) {
parentPort!.on('message', listener)
},
post(message) {
parentPort!.postMessage(message)
},
})
}

export function createErrorRPC() {
return createBirpc<BirpcEvents>({}, {
eventNames: ['onError'],
on(listener) {
parentPort!.on('message', listener)
},
post(message) {
parentPort!.postMessage(message)
},
})
}
21 changes: 11 additions & 10 deletions src/worker/worker.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import { parentPort, workerData } from 'node:worker_threads'
import { workerData } from 'node:worker_threads'
import { createErrorRPC, createWorkerRPC } from './rpc'

(async () => {
parentPort!.postMessage({ msg: 'hello from worker', p: workerData.vitestPath })
try {
const vitestMode = await import(workerData.vitestPath) as typeof import('vitest/node')
const vitest = await vitestMode.createVitest('test', {
watch: false,
watch: true,
root: workerData.root,
})
parentPort!.postMessage({ msg: 'vitest created' })
const files = await vitest.globTestFiles()
parentPort!.postMessage({
files: files.map(([_, spec]) => spec),
})
await vitest.close()
const rpc = createWorkerRPC(vitest)
await rpc.onReady()
}
catch (err: any) {
parentPort!.postMessage({ error: err.message, stack: String(err.stack) })
const closeRpc = createErrorRPC()
closeRpc.onError({
message: err.message,
stack: String(err.stack),
name: err.name,
})
}
})()
2 changes: 0 additions & 2 deletions tsup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ import { defineConfig } from 'tsup'
export default defineConfig([
{
entry: ['./src/extension.ts'],
minify: true,
external: ['vscode'],
format: 'cjs',
},
{
entry: ['./src/worker/worker.ts'],
minify: true,
external: ['vitest/node'],
format: 'esm',
},
Expand Down

0 comments on commit a8dd1ef

Please sign in to comment.