mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 17:37:40 +00:00
* feat(plugins): orca/testing kit, `orca plugin test` and `orca plugin check` - `orca/testing` mounts a components panel in plain Node (node:test, vitest, bun) with the real runtime and Orca's own tree validator: find by text/role/label/testId, press/type/submit/toggle/ select that resolve once the panel settles, tree() snapshots, errors (with stacks) and warnings, persisted state (with stored() for migrations), emit/setVisible/rerender, and unmount running setup cleanup. It re-exports node:test and strict assert so tests type-check without @types/node. - mockHost refuses what Orca refuses with the same codes (surface, capability from the manifest, params, a gesture method without a press), checks configured results against the host API, and records calls. - `orca plugin test [dir]` discovers *.test.ts(x) and runs them with node:test through Node module hooks that resolve `orca` like the guest's import map and transpile/resolve sources exactly as Orca's build-less loader, or with the plugin's own vitest via a generated config. - `orca plugin check [dir]` validates, then renders each components panel once and reports what it threw (file:line), layouts Orca would refuse, and props it would drop. - The kit, hooks and register entry ship minified beside orca-runtime.mjs and are unpacked from asar; typings land in .orca/types/orca-testing. The components template and hello-tsx get sample tests. - ui.error carries an optional stack; a new runtime starts every atom from its initial value; the hydration fallback timer is cleared once hydrated. * plugins: run plugin test/check on a system Node or Orca's own, and warn they are unsandboxed * test(plugins): run the shipped example plugin tests from the unit suite
104 lines
3.3 KiB
JavaScript
104 lines
3.3 KiB
JavaScript
// Bundle the files Orca ships for components panels into resources/plugins/declarative/:
|
|
// the guest `orca` module, the `orca/testing` kit, and the Node hooks `orca plugin test` runs with.
|
|
// `--check` fails when a checked-in file is stale instead of rewriting it.
|
|
import { readFileSync, writeFileSync } from 'node:fs'
|
|
import path from 'node:path'
|
|
import { build } from 'esbuild'
|
|
|
|
const ROOT = path.resolve(import.meta.dirname, '..', '..')
|
|
const OUTPUT_DIR = path.join(ROOT, 'resources', 'plugins', 'declarative')
|
|
const DECLARATIVE = path.join(ROOT, 'src', 'shared', 'plugins', 'declarative')
|
|
const TESTING = path.join(ROOT, 'src', 'main', 'plugins', 'testing')
|
|
|
|
export const DECLARATIVE_RUNTIME_OUTPUT = path.join(OUTPUT_DIR, 'orca-runtime.mjs')
|
|
|
|
/** The kit and the panel must share one runtime instance, so the kit imports the shipped file. */
|
|
const runtimeAsSibling = {
|
|
name: 'orca-runtime-as-sibling',
|
|
setup(builder) {
|
|
builder.onResolve({ filter: /runtime\/orca-module$/ }, () => ({
|
|
path: './orca-runtime.mjs',
|
|
external: true
|
|
}))
|
|
}
|
|
}
|
|
|
|
const BUNDLES = [
|
|
{
|
|
file: 'orca-runtime.mjs',
|
|
entry: path.join(DECLARATIVE, 'runtime', 'orca-module.ts'),
|
|
source: 'src/shared/plugins/declarative/runtime/',
|
|
platform: 'browser'
|
|
},
|
|
{
|
|
file: 'orca-testing.mjs',
|
|
entry: path.join(DECLARATIVE, 'testing', 'orca-testing.ts'),
|
|
source: 'src/shared/plugins/declarative/testing/',
|
|
platform: 'node',
|
|
plugins: [runtimeAsSibling]
|
|
},
|
|
{
|
|
file: 'orca-test-hooks.mjs',
|
|
entry: path.join(TESTING, 'plugin-test-hooks.ts'),
|
|
source: 'src/main/plugins/testing/',
|
|
platform: 'node'
|
|
},
|
|
{
|
|
file: 'orca-test-register.mjs',
|
|
entry: path.join(TESTING, 'plugin-test-register.ts'),
|
|
source: 'src/main/plugins/testing/',
|
|
platform: 'node'
|
|
}
|
|
]
|
|
|
|
async function bundle({ file, entry, source, platform, plugins = [] }) {
|
|
const result = await build({
|
|
entryPoints: [entry],
|
|
bundle: true,
|
|
format: 'esm',
|
|
platform,
|
|
target: platform === 'node' ? 'node20' : 'es2022',
|
|
charset: 'utf8',
|
|
legalComments: 'none',
|
|
// The Node files bundle zod and sucrase; minified they stay small in Git and on disk.
|
|
minify: platform === 'node',
|
|
plugins,
|
|
banner: {
|
|
js:
|
|
`// Generated by config/scripts/build-plugin-declarative-runtime.mjs from\n` +
|
|
`// ${source}. Do not edit.`
|
|
},
|
|
write: false,
|
|
logLevel: 'silent'
|
|
})
|
|
return { output: path.join(OUTPUT_DIR, file), text: result.outputFiles[0].text }
|
|
}
|
|
|
|
/** Every shipped bundle, as `{ output, text }`. */
|
|
export function bundleDeclarativeFiles() {
|
|
return Promise.all(BUNDLES.map(bundle))
|
|
}
|
|
|
|
export async function bundleDeclarativeRuntime() {
|
|
return (await bundle(BUNDLES[0])).text
|
|
}
|
|
|
|
async function main() {
|
|
const bundles = await bundleDeclarativeFiles()
|
|
const check = process.argv.includes('--check')
|
|
for (const { output, text } of bundles) {
|
|
const relativeOutput = path.relative(ROOT, output)
|
|
if (!check) {
|
|
writeFileSync(output, text)
|
|
console.log(`wrote ${relativeOutput}`)
|
|
} else if (readFileSync(output, 'utf8') !== text) {
|
|
console.error(`${relativeOutput} is stale; run pnpm generate:plugin-declarative-runtime`)
|
|
process.exitCode = 1
|
|
}
|
|
}
|
|
}
|
|
|
|
if (process.argv[1] && path.resolve(process.argv[1]) === import.meta.filename) {
|
|
await main()
|
|
}
|