a post-component library for building user-interfaces on the web.
1import * as child_process from 'node:child_process'
2import { once } from 'node:events'
3import * as fs from 'node:fs/promises'
4import * as os from 'node:os'
5import * as path from 'node:path'
6import { fileURLToPath } from 'node:url'
7import type { Runtime } from './main.ts'
8
9export interface NodeRuntimeOptions {
10 collect_coverage?: boolean
11}
12
13export async function create_node_runtime(options: NodeRuntimeOptions = {}): Promise<Runtime> {
14 const collect_coverage = options.collect_coverage ?? true
15 const coverage_dir = collect_coverage ? await fs.mkdtemp(path.join(os.tmpdir(), 'coverage-')) : null
16 const child = child_process.fork(fileURLToPath(import.meta.resolve('./runtime.ts')), {
17 env: collect_coverage ? { ...process.env, NODE_V8_COVERAGE: coverage_dir! } : process.env,
18 stdio: 'inherit',
19 })
20
21 const { port1, port2 } = new MessageChannel()
22 port1.onmessage = e => child.send(e.data)
23 child.on('message', data => port1.postMessage(data))
24
25 await once(child, 'spawn')
26
27 return {
28 port: port2,
29 async coverage() {
30 if (!collect_coverage || !coverage_dir) return []
31 const [filename] = await fs.readdir(coverage_dir)
32 const { result } = JSON.parse(await fs.readFile(path.join(coverage_dir, filename), 'utf8'))
33 return result
34 },
35 async [Symbol.asyncDispose]() {
36 port1.close()
37 child.kill()
38 if (coverage_dir) await fs.rm(coverage_dir, { recursive: true })
39 },
40 }
41}