/** * 独立headless SDK的产物生成脚本。先清理.headless-dist,再用tsconfig.headless.json编译四个库目录,修正声明与Worker引用。 * 最后复制包清单、无UI宿主示例和验收文档;失败立即抛出,不把半成品报告成可用包。执行会删除并重建该产物目录。 */ import { execFileSync } from 'node:child_process'; import { cp, readdir, readFile, rm, writeFile } from 'node:fs/promises'; import { fileURLToPath } from 'node:url'; import { join, resolve } from 'node:path'; // 路径基于脚本URL而非当前终端目录;tsc仍显式以仓库根作为cwd解析项目依赖。 const root=resolve(fileURLToPath(new URL('..',import.meta.url))),output=join(root,'.headless-dist'); await rm(output,{recursive:true,force:true}); // 同步等待TypeScript生成ESM及声明;继承终端输出,编译失败直接中止后续处理。 execFileSync(process.execPath,[join(root,'node_modules/typescript/bin/tsc'),'-p',join(root,'tsconfig.headless.json')],{cwd:root,stdio:'inherit'}); /** 递归只处理.d.ts的相对模块说明符,将源.ts后缀改为.js;普通schema/data字符串不做全局替换。 * 这是针对当前输出格式的正则修补,不是任意TypeScript语法的完整重写器。 */ async function declarations(directory){ for(const entry of await readdir(directory,{withFileTypes:true})){ const path=join(directory,entry.name); if(entry.isDirectory())await declarations(path); else if(entry.name.endsWith('.d.ts')){ const source=await readFile(path,'utf8'); // Native TS rewrites runtime imports, but leaves source suffixes in declarations. // Only module specifiers change; data/schema string literals remain intact. const next=source.replace(/((?:from\s+|import\s*\()\s*['"])(\.\.?\/[^'"]+)\.ts(['"])/g,'$1$2.js$3'); await writeFile(path,next); } } } // 原生编译器处理运行时import后缀,声明文件仍需此补充;完成后再修URL字面量里的Worker源后缀。 await declarations(join(output,'modules')); // 两个浏览器适配器各应含一个预期Worker URL;要求目标.js文件存在并且源字符串只出现一次才改写。 for(const [module,worker]of [['browser-digests','digest.worker'],['browser-images','image.worker']]){ const path=join(output,'modules/canvas-runtime',`${module}.js`),source=await readFile(path,'utf8'),before=`./${worker}.ts`; if(source.split(before).length!==2)throw Error(`Unexpected Worker URL in ${module}`); await readFile(join(output,'modules/canvas-runtime',`${worker}.js`)); await writeFile(path,source.replace(before,`./${worker}.js`)); } // 产物保留与headless/package.json的exports/files配套的模块布局;这里复制清单,不执行npm发布或包验收测试。 for(const name of ['package.json','consumer.mjs','README.md','STEP4_ACCEPTANCE.md','SERVICE_ACCEPTANCE.md','FINAL_ACCEPTANCE.md'])await cp(join(root,'headless',name),join(output,name)); console.log('Built .headless-dist: ESM, declarations, browser adapters and Worker modules');