Extending
Core stays framework-free. New frameworks = Driver packages + facade re-export.
defineDriver wraps your factory so every recipe gets a non-empty id and a valid share (always by default when omitted).
Local share: always|never servers must return a reclaimable child pid (e.g. via spawnManaged). Remote attach omits both stop and pid (see host).
ts
import { defineDriver, defineRecipe, spawnManaged } from 'untestutils'
export const myFw = defineDriver((opts: { id: string; root: string }) =>
defineRecipe({
id: opts.id,
prepare: async (ctx) => {
/* build into ctx.outDir */
},
start: async (ctx) => {
const managed = spawnManaged('node', ['server.js'], {
cwd: opts.root,
env: { ...process.env, PORT: String(ctx.port) },
server: true,
})
return {
kind: 'url',
url: `http://127.0.0.1:${ctx.port}/`,
stop: managed.stop,
pid: managed.pid,
}
},
}),
)Contract: Recipe (prepare?, start, ready?, hashInputs?, share?). Add a playground fixture + one e2e, document under Drivers.
Do not import Nuxt/Vite into @untestutils/core.
See API: Drivers · CONTRIBUTING.