1// Run one lab's MATLAB layer through the numbl CLI, outside the browser.
2//
3// node scripts/runlab.mjs <driver.m> <script.m> '<params json>' [--show key,key]
4//
5// It composes main.m exactly as the app does (driver, newline, script), copies
6// the shared library into a scratch directory, runs it, and prints out.json.
7// NUMBL_DIR points at a numbl checkout (default ~/src/numbl).
8import { execFileSync } from 'node:child_process'
9import { mkdtempSync, mkdirSync, readFileSync, writeFileSync, readdirSync, copyFileSync } from 'node:fs'
10import { tmpdir } from 'node:os'
11import { join, dirname, resolve } from 'node:path'
12import { fileURLToPath } from 'node:url'
14const here = dirname(fileURLToPath(import.meta.url))
15const root = resolve(here, '..')
16const numbl = process.env.NUMBL_DIR ?? join(process.env.HOME, 'src', 'numbl')
17const cli = join(numbl, 'dist-cli', 'cli.js')
19export function runLab(driverPath, scriptPath, params, opts = {}) {
20 const dir = mkdtempSync(join(tmpdir(), 'lapl-'))
21 const lib = join(root, 'src', 'matlab')
22 for (const f of readdirSync(lib)) if (f.endsWith('.m')) copyFileSync(join(lib, f), join(dir, f))
23 const driver = readFileSync(driverPath, 'utf8')
24 const script = readFileSync(scriptPath, 'utf8')
25 writeFileSync(join(dir, 'main.m'), `${driver}\n${script}`)
26 writeFileSync(join(dir, 'params.json'), JSON.stringify(params))
27 const t0 = Date.now()
28 const stdout = execFileSync('node', [cli, 'run', 'main.m'], { cwd: dir, encoding: 'utf8' })
29 const ms = Date.now() - t0
30 const out = JSON.parse(readFileSync(join(dir, 'out.json'), 'utf8'))
31 if (opts.verbose && stdout.trim()) process.stdout.write(stdout)
32 return { out, stdout, ms, dir }
33}
35if (process.argv[1] === fileURLToPath(import.meta.url)) {
36 const [driverPath, scriptPath, paramsJson] = process.argv.slice(2)
37 if (!driverPath || !scriptPath) {
38 console.error('usage: node scripts/runlab.mjs <driver.m> <script.m> \'<params json>\'')
39 process.exit(2)
40 }
41 const { out, stdout, ms } = runLab(driverPath, scriptPath, JSON.parse(paramsJson ?? '{}'))
42 if (stdout.trim()) process.stdout.write(stdout)
43 const show = process.argv.includes('--full')
44 console.log(`[${ms} ms]`)
45 console.log(JSON.stringify(out, null, show ? 1 : 0).slice(0, show ? 1e9 : 4000))
46}
48mkdirSync(join(root, 'tmp'), { recursive: true })