Prüfstand: entry.jsx zeigt nur noch die echte Seite WorkersPage (BrowserRouter, Adresse mit ?tab= lesbar); shot.cjs neu mit den Szenarien A–M (Hochhaus, Regie, Handy, weniger Bewegung, Notbremse, Ausfall, Detail, Etagen, Pause, Pult P3, Pult P4, Keller, Handy Pult/Keller), harte Prüfungen und Bildern mit Szenario-Vorsilbe; Befunde an der App stehen getrennt von den Verstößen. Skript-API: Gründe der Bot-Leads wörtlich wie im Dienst. Kopfzeile am Handy: zwei Reihen (Geld + Pause/Zahnrad, dann die fünf Kennzahlen), schmaler Panel-Rand, 40-px-Knöpfe, Querformat unter 500 px Höhe in einer Reihe; hustlerKopf.test um die Zusicherungen dazu ergänzt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
67 lines
2.8 KiB
JavaScript
67 lines
2.8 KiB
JavaScript
// Baut den Prüfstand: node scripts/workers-harness/build.mjs <outDir>
|
|
// entry.jsx -> harness.js + harness.css (esbuild, Entwicklungsmodus, damit StrictMode wie in der Entwicklung zweimal einhängt), dazu index.html.
|
|
// outDir muss ein neuer oder leerer Ordner unter dem Temp-Ordner (TMPDIR, sonst /tmp) oder unter .superpowers/ im Repo sein.
|
|
// Die Schrift /fonts/* bleibt außen vor: der Server von shot.cjs liefert sie aus public/fonts.
|
|
// Drei Module der App hängen an Appwrite (Anmeldung, Mitarbeiterliste, Sitzungs-JWT) und werden nur im Prüfstand ausgetauscht, damit die
|
|
// echte Seite WorkersPage (Bildschirme Hochhaus, Pult und Keller samt Brief, Akte, ProspectPanel und Einstellungen) ohne Netz läuft: AuthContext -> stub-auth.js,
|
|
// useEmployees -> stub-employees.js, workersClient -> stub-client.js. Am Quelltext der App ändert das nichts.
|
|
import esbuild from 'esbuild'
|
|
import fs from 'node:fs'
|
|
import os from 'node:os'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
const here = path.dirname(fileURLToPath(import.meta.url))
|
|
const repo = path.resolve(here, '../..')
|
|
|
|
const arg = process.argv[2]
|
|
if (!arg) {
|
|
console.error('usage: node scripts/workers-harness/build.mjs <outDir>')
|
|
process.exit(2)
|
|
}
|
|
const out = path.resolve(arg)
|
|
const tmp = path.resolve(process.env.TMPDIR || os.tmpdir())
|
|
const allowed = [tmp, path.join(repo, '.superpowers')]
|
|
if (!allowed.some((base) => out.startsWith(base + path.sep))) {
|
|
console.error(`refusing: outDir must be below ${allowed.join(' or ')}`)
|
|
process.exit(2)
|
|
}
|
|
if (fs.existsSync(out) && fs.readdirSync(out).length > 0) {
|
|
console.error('refusing: outDir must be new or empty')
|
|
process.exit(2)
|
|
}
|
|
fs.mkdirSync(out, { recursive: true })
|
|
|
|
const STUBS = [
|
|
{ filter: /(^|\/)context\/AuthContext(\.jsx?)?$/, file: 'stub-auth.js' },
|
|
{ filter: /(^|\/)hooks\/useEmployees(\.jsx?)?$/, file: 'stub-employees.js' },
|
|
{ filter: /(^|\/)lib\/workersClient(\.jsx?)?$/, file: 'stub-client.js' },
|
|
]
|
|
const stubPlugin = {
|
|
name: 'harness-stubs',
|
|
setup(build) {
|
|
for (const stub of STUBS) {
|
|
build.onResolve({ filter: stub.filter }, () => ({ path: path.join(here, stub.file) }))
|
|
}
|
|
},
|
|
}
|
|
|
|
const result = await esbuild.build({
|
|
entryPoints: [path.join(here, 'entry.jsx')],
|
|
outfile: path.join(out, 'harness.js'),
|
|
bundle: true,
|
|
format: 'iife',
|
|
platform: 'browser',
|
|
jsx: 'automatic',
|
|
external: ['/fonts/*'],
|
|
// Bilder der Pixelwelt (url() in src/styles/hustler/*.css) landen als data:-URI im harness.css
|
|
loader: { '.png': 'dataurl' },
|
|
plugins: [stubPlugin],
|
|
define: { 'process.env.NODE_ENV': '"development"' },
|
|
logLevel: 'warning',
|
|
metafile: true,
|
|
})
|
|
fs.copyFileSync(path.join(here, 'index.html'), path.join(out, 'index.html'))
|
|
const sizes = Object.entries(result.metafile.outputs).map(([file, info]) => `${path.basename(file)} ${info.bytes}`)
|
|
console.log(`built ${sizes.join(', ')} -> ${out}`)
|