import { build } from 'esbuild' import { readFile, writeFile, mkdir, mkdtemp, rm } from 'node:fs/promises' import { dirname, resolve, join } from 'node:path' import { fileURLToPath, pathToFileURL } from 'node:url' import { tmpdir } from 'node:os' import { gzipSync } from 'node:zlib' const here = dirname(fileURLToPath(import.meta.url)), root = resolve(here, '../..') const temp = await mkdtemp(join(tmpdir(), 'reformer-build-')) try { await build({ entryPoints: [join(here, 'src/geometry.ts')], bundle: true, platform: 'node', format: 'esm', outfile: join(temp, 'geometry.mjs') }) const { createFigure, partsInventory } = await import(pathToFileURL(join(temp, 'geometry.mjs')).href) await build({ entryPoints: [join(root, 'packages/app/src/content/reformer.ts')], bundle: true, platform: 'node', format: 'esm', outfile: join(temp, 'content.mjs') }) const { reformerParts, reformerLesson } = await import(pathToFileURL(join(temp, 'content.mjs')).href) const live = await build({ entryPoints: [join(here, 'src/controller.ts')], bundle: true, platform: 'browser', format: 'iife', target: ['es2019'], minify: true, write: false }) const [template, css, illustration] = await Promise.all(['page.html', 'page.css', 'illustration.css'].map(name => readFile(join(here, 'src', name), 'utf8'))) const approved = reformerLesson.review.status === 'approved' && reformerLesson.review.coach && reformerLesson.review.reviewedAt if (process.argv.includes('--release') && !approved) throw new Error('正式发布构建需要教练审核记录:status=approved、coach、reviewedAt。审阅预览请使用 pnpm build:education。') const figure = createFigure() const replacements = { __THIRD_PARTY_LICENSE__: `Anatomy SVG kit · https://github.com/wheresryan22/anatomy\n${await readFile(join(here, 'vendor/anatomy/LICENSE'), 'utf8')}`, '/*__CSS__*/': `${css}\n${illustration}`, '/*__SCRIPT__*/': live.outputFiles[0].text.replaceAll(' ``).join(''), __STATIC_PARTS__: reformerParts.map(p => `
${p.number} · ${p.name}
${p.description} ${p.observation}
`).join(''), __SPRING_BUTTONS__: reformerLesson.springs.map((s, i) => ``).join(''), __REFERENCE__: reformerLesson.reference, __SOURCES__: reformerLesson.sources.map(s => `${s.title} ↗`).join(''), } let html = template for (const [key, value] of Object.entries(replacements)) html = html.replace(key, () => value) const output = join(root, 'packages/server/public/learn/reformer') await mkdir(output, { recursive: true }) await writeFile(join(output, 'index.html'), html) // Keep an inspectable standalone SVG. Native mini-programs use a checked-in PNG raster. const svg = figure.replace('>