@peripheries/engine 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env tsx
|
|
2
|
-
import{existsSync,readFileSync,readdirSync,statSync,writeFileSync}from"node:fs";import{dirname,join,relative,resolve}from"node:path";import{hasComponentJSDoc,parseJSDocFile,readExportsFromIndex,resolveComponentFilePath,toRepoRelative}from"../lib/jsdoc-parser.mjs";import{REPO_ROOT as REPO_ROOT_}from"../lib/root.mjs";const REPO_ROOT=REPO_ROOT_;const argv=process.argv.slice(2);const STRICT=argv.includes("--strict");const UPDATE_BASELINE=argv.includes("--update-baseline");const BASELINE_PATH=resolve(REPO_ROOT,"scripts/__baselines__/fixtures.json");function readBaseline(){if(!existsSync(BASELINE_PATH))return{classes:{}};return JSON.parse(readFileSync(BASELINE_PATH,"utf8"))}function barrels(){const out=["src/index.ts"];for(const f of readdirSync(join(REPO_ROOT,"src"))){if(/^index\.[a-z0-9-]+\.ts$/.test(f)&&!f.endsWith(".test.ts"))out.push(`src/${f}`)}return out}function walk(dir,pred,out=[]){if(!existsSync(dir))return out;for(const e of readdirSync(dir)){const full=join(dir,e);if(statSync(full).isDirectory())walk(full,pred,out);else if(pred(full))out.push(full)}return out}function storeDeclaringComponents(){const found=new Set;const pages=["src/project_templates","src/org_templates"].flatMap(r=>walk(join(REPO_ROOT,r),f=>f.endsWith("page.json")));for(const file of pages){let json;try{json=JSON.parse(readFileSync(file,"utf8"))}catch{continue}const cells=json?.content?.cells??[];for(const cell of cells){if(!(cell?.selections||cell?.viewState||cell?.live))continue;if(typeof cell.component==="string")found.add(cell.component)}}return found}const NARROW_PX=480;const isLoading=s=>s.status?.isLoading===true;const isError=s=>s.status?.isError===true||Boolean(s.status?.errors);const isNarrow=s=>typeof s.viewport?.width==="number"&&s.viewport.width<=NARROW_PX;const declaresStore=s=>Boolean(s.selections||s.viewState||s.live||s.storeFixture);function arrayProp(s,name){const v=s.props?.[name]??s.data?.[name];return Array.isArray(v)?v:void 0}async function coveredElsewhere(){const out=new Map;for(const file of walk(join(REPO_ROOT,"src
|
|
2
|
+
import{existsSync,readFileSync,readdirSync,statSync,writeFileSync}from"node:fs";import{dirname,join,relative,resolve}from"node:path";import{hasComponentJSDoc,parseJSDocFile,readExportsFromIndex,resolveComponentFilePath,toRepoRelative}from"../lib/jsdoc-parser.mjs";import{REPO_ROOT as REPO_ROOT_}from"../lib/root.mjs";import"../lib/css-stub.mjs";import{loadConfig}from"../lib/config.mjs";const HARNESS_IS_NARROW=(await loadConfig()).harness.widths.some(w=>w<480);const REPO_ROOT=REPO_ROOT_;const argv=process.argv.slice(2);const STRICT=argv.includes("--strict");const UPDATE_BASELINE=argv.includes("--update-baseline");const BASELINE_PATH=resolve(REPO_ROOT,"scripts/__baselines__/fixtures.json");function readBaseline(){if(!existsSync(BASELINE_PATH))return{classes:{}};return JSON.parse(readFileSync(BASELINE_PATH,"utf8"))}function barrels(){const out=existsSync(join(REPO_ROOT,"src/index.ts"))?["src/index.ts"]:[];for(const f of readdirSync(join(REPO_ROOT,"src"))){if(/^index\.[a-z0-9-]+\.ts$/.test(f)&&!f.endsWith(".test.ts"))out.push(`src/${f}`)}return out}function walk(dir,pred,out=[]){if(!existsSync(dir))return out;for(const e of readdirSync(dir)){const full=join(dir,e);if(statSync(full).isDirectory())walk(full,pred,out);else if(pred(full))out.push(full)}return out}function storeDeclaringComponents(){const found=new Set;const pages=["src/project_templates","src/org_templates"].flatMap(r=>walk(join(REPO_ROOT,r),f=>f.endsWith("page.json")));for(const file of pages){let json;try{json=JSON.parse(readFileSync(file,"utf8"))}catch{continue}const cells=json?.content?.cells??[];for(const cell of cells){if(!(cell?.selections||cell?.viewState||cell?.live))continue;if(typeof cell.component==="string")found.add(cell.component)}}return found}const NARROW_PX=480;const isLoading=s=>s.status?.isLoading===true;const isError=s=>s.status?.isError===true||Boolean(s.status?.errors);const isNarrow=s=>typeof s.viewport?.width==="number"&&s.viewport.width<=NARROW_PX;const declaresStore=s=>Boolean(s.selections||s.viewState||s.live||s.storeFixture);function arrayProp(s,name){const v=s.props?.[name]??s.data?.[name];return Array.isArray(v)?v:void 0}async function coveredElsewhere(){const out=new Map;for(const file of walk(join(REPO_ROOT,"src"),f=>/\.fixtures\.tsx?$/.test(f))){let mod;try{mod=await import(file)}catch{continue}for(const name of mod.covers??[])out.set(name,toRepoRelative(REPO_ROOT,file))}return out}const storeComponents=storeDeclaringComponents();const covered=await coveredElsewhere();const findings=[];const byClass=new Map;const seen=new Set;let components=0;function record(cls,ok,component,message){const bucket=byClass.get(cls)??{checked:0,failed:0};bucket.checked++;if(!ok){bucket.failed++;findings.push({component,cls,message})}byClass.set(cls,bucket)}for(const entry of barrels()){for(const{name,source}of readExportsFromIndex(REPO_ROOT,entry)){if(seen.has(name))continue;const filePath=resolveComponentFilePath(REPO_ROOT,source);if(!filePath||!hasComponentJSDoc(filePath,name))continue;seen.add(name);components++;const base=filePath.replace(/\.(tsx?|jsx?)$/,"");const byExport=join(dirname(filePath),name);const fixtureFile=[`${base}.fixtures.tsx`,`${base}.fixtures.ts`,`${byExport}.fixtures.tsx`,`${byExport}.fixtures.ts`].find(existsSync);if(!fixtureFile&&covered.has(name)){record("exists",true,name,"");continue}record("exists",Boolean(fixtureFile),name,`no fixtures file beside ${toRepoRelative(REPO_ROOT,filePath)}`);if(!fixtureFile)continue;let scenarios=[];try{const mod=await import(fixtureFile);scenarios=mod.scenarios??mod.default?.scenarios??[]}catch(err){const why=err instanceof Error?err.message:String(err);record("exists",false,name,`fixtures module failed to import: ${why.slice(0,120)}`);continue}record("exists",scenarios.length>0,name,"fixtures module declares no scenarios");const first=scenarios[0];if(!first)continue;record("intent",Boolean(first.designIntent),name,`default scenario "${first.name}" has no designIntent \u2014 the eval step skips it silently`);if(!HARNESS_IS_NARROW)record("narrow",scenarios.some(isNarrow),name,`no scenario under ${NARROW_PX}px \u2014 reflow is unproven`);const jsdoc=parseJSDocFile(filePath,name)??{};const props=jsdoc.props_schema?.properties??{};const propNames=Object.keys(props);const dataBearing=Boolean(jsdoc.props_schema?.data)||propNames.includes("data")||propNames.includes("status");if(dataBearing){record("loading",scenarios.some(isLoading),name,"declares host data but no loading scenario");record("error",scenarios.some(isError),name,"declares host data but no error scenario")}const required=jsdoc.props_schema?.required??[];const listProps=propNames.filter(p=>props[p]?.type==="array"&&required.includes(p));for(const p of listProps){const values=scenarios.map(s=>arrayProp(s,p)).filter(Boolean);if(values.length===0)continue;record("empty",values.some(v=>v.length===0),name,`array prop "${p}" is never empty in any scenario`);record("dense",values.some(v=>v.length>=5),name,`array prop "${p}" never carries 5+ items \u2014 density is unproven`)}if(storeComponents.has(name)){record("store",scenarios.some(declaresStore),name,"a page.json cell declares store channels for this component, but no scenario does")}}}const order=["exists","intent","narrow","loading","error","empty","dense","store"];const baseline=readBaseline();const observed={};for(const cls of order)observed[cls]=byClass.get(cls)?.failed??0;if(UPDATE_BASELINE){const raised=order.filter(c=>observed[c]>(baseline.classes[c]??0));writeFileSync(BASELINE_PATH,JSON.stringify({_comment:"Per-class fixture-coverage failure counts. A FLOOR TO BE LOWERED, never raised \u2014 exceeding one fails `npm run check:fixtures`. Regenerate with `npm run check:fixtures -- --update-baseline` only when a number goes DOWN. See the header of scripts/check-fixtures.mts.",classes:observed},null,2)+"\n","utf8");console.log(`[fixtures] baseline written to ${relative(REPO_ROOT,BASELINE_PATH)}`);if(raised.length>0){console.warn(`
|
|
3
3
|
[fixtures] WARNING \u2014 ${raised.length} class(es) went UP and were recorded anyway:
|
|
4
4
|
`+raised.map(c=>` ${c}: ${baseline.classes[c]??0} -> ${observed[c]}`).join("\n")+"\n\n A floor is meant to be lowered. Raising one records a regression as\n acceptable, which is exactly what this file exists to prevent. If that is\n deliberate, say why in the commit message.")}process.exit(0)}console.log(`[fixtures] ${components} components
|
|
5
5
|
`);const regressions=[];for(const cls of order){const b=byClass.get(cls);if(!b)continue;const floor=baseline.classes[cls]??0;const now=observed[cls];if(STRICT?now>0:now>floor)regressions.push({cls,was:floor,now});const verdict=now===0?"ok":now>floor?`${now} (floor ${floor}) OVER`:`${now}/${floor}`;console.log(` ${cls.padEnd(8)} ${String(b.checked-now).padStart(4)}/${String(b.checked).padEnd(4)} ${verdict}`)}function print(list,label){if(list.length===0)return;console.log(`
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{mkdir,rm,writeFile}from"node:fs/promises";import{createRequire}from"node:module";import{cpus}from"node:os";import{join,relative,resolve}from"node:path";import{pathToFileURL}from"node:url";import{loadConfig}from"../lib/config.mjs";import{parseFlagValue}from"../lib/env.mjs";import{REPO_ROOT}from"../lib/root.mjs";import{newDeterministicContext,prepare,settle}from"../harness/determinism.mjs";import{
|
|
2
|
+
import{mkdir,rm,writeFile}from"node:fs/promises";import{createRequire}from"node:module";import{cpus}from"node:os";import{join,relative,resolve}from"node:path";import{pathToFileURL}from"node:url";import{loadConfig}from"../lib/config.mjs";import{parseFlagValue}from"../lib/env.mjs";import{REPO_ROOT}from"../lib/root.mjs";import{newDeterministicContext,prepare,settle}from"../harness/determinism.mjs";import{buildAndServeHarness,createHarnessServer}from"../harness/server.mjs";const argv=process.argv.slice(2);const{harness}=await loadConfig();const OUTPUT=resolve(REPO_ROOT,harness.output);const only=parseFlagValue(argv,"only");const[shardIndex,shardTotal]=(parseFlagValue(argv,"shard")??"1/1").split("/").map(Number);if(!(shardTotal>=1&&shardIndex>=1&&shardIndex<=shardTotal))throw new Error("[test-visual] --shard expects <i>/<n>");const fromRepo=createRequire(resolve(REPO_ROOT,"package.json"));const playwright=await import(pathToFileURL(fromRepo.resolve("playwright")).href);const chromium=playwright.chromium??playwright.default.chromium;const skipped=[];const{server}=await createHarnessServer();const served=await buildAndServeHarness();const origin=served.origin;const HARNESS_PATH=served.path;let exitCode=0;try{const list=await listTargets(server);const targets=list.filter(t=>(!only||t.label.startsWith(`${only} `))&&t.index%shardTotal===shardIndex-1);await rm(OUTPUT,{recursive:true,force:true});await mkdir(OUTPUT,{recursive:true});console.log(`[test-visual] ${targets.length} target(s) (of ${list.length}) \xD7 ${harness.widths.length} width(s) + a ${harness.container}px container${skipped.length?`; ${skipped.length} public-page scenario(s) skipped (R6)`:""}`);const browser=await chromium.launch();const concurrency=Math.max(1,Number(process.env.ENGINE_VISUAL_CONCURRENCY)||Math.min(6,Math.max(2,cpus().length-1)));const reports=new Array(targets.length);let next=0;await Promise.all(Array.from({length:Math.min(concurrency,targets.length)},async()=>{while(next<targets.length){const i=next++;const t=targets[i];const renders=[...harness.widths.map(w=>({width:w,container:null})),{width:Math.max(...harness.widths),container:harness.container}];const report={target:t.label,kind:t.kind,...t.designIntent?{designIntent:t.designIntent}:{},widths:[]};for(const r of renders)report.widths.push(await renderOnce(browser,t,r));reports[i]=report;const bad=report.widths.filter(w=>w.errors.length);const over=report.widths.filter(w=>w.overflow);console.log(` ${bad.length?"\u2717":over.length?"\u25B3":"\u2713"} ${t.label}${bad.length?` \u2014 ${bad[0].errors[0].split("\n")[0].slice(0,160)}`:""}${over.length?` (overflow at ${over.map(w=>w.container?`${w.container}px container`:`${w.width}px`).join(", ")})`:""}`)}}));await browser.close();const failed=reports.filter(r=>r.widths.some(w=>w.errors.length));const overflowing=reports.filter(r=>r.widths.some(w=>w.overflow));await writeFile(join(OUTPUT,"report.json"),JSON.stringify({widths:harness.widths,container:harness.container,reports,skipped},null,2)+"\n");console.log(`[test-visual] ${reports.length-failed.length}/${reports.length} rendered clean; ${overflowing.length} overflow; report \u2192 ${relative(REPO_ROOT,join(OUTPUT,"report.json"))}`);if(failed.length||harness.overflow==="error"&&overflowing.length)exitCode=1}finally{await server.close();await served.close()}process.exit(exitCode);async function listTargets(vite){const{readdir}=await import("node:fs/promises");const walk=async dir=>{const out=[];for(const e of await readdir(dir,{withFileTypes:true}).catch(()=>[])){const p=join(dir,e.name);if(e.isDirectory())out.push(...await walk(p));else out.push(p)}return out};const targets=[];const fixtureFiles=(await walk(resolve(REPO_ROOT,"src"))).filter(f=>/\.fixtures\.tsx?$/.test(f)&&!f.includes("/node_modules/")).sort();for(const file of fixtureFiles){const name=file.split("/").pop().replace(/\.fixtures\.tsx?$/,"");const mod=await vite.ssrLoadModule(file);for(const s of mod.scenarios??[]){if(s.surface==="public-page"){skipped.push(`${name} \xB7 ${s.name}`);continue}targets.push({kind:"fixture",label:`${name} \xB7 ${s.name}`,query:`fixture=${encodeURIComponent(name)}&scenario=${encodeURIComponent(s.name)}`,designIntent:s.designIntent})}}const pageFiles=[...(await walk(resolve(REPO_ROOT,"src/pages"))).filter(f=>f.endsWith("/page.json")&&f.split("/").length-resolve(REPO_ROOT,"src/pages").split("/").length===2),...(await walk(resolve(REPO_ROOT,"src/project_templates"))).filter(f=>/\/pages\/[^/]+\/page\.json$/.test(f))].sort();for(const file of pageFiles){const m=file.match(/\/project_templates\/([^/]+)\/pages\/([^/]+)\/page\.json$/)??file.match(/\/pages\/([^/]+)\/page\.json$/);const key=m.slice(1).join("/");targets.push({kind:"page",label:`page ${key}`,query:`page=${encodeURIComponent(key)}`})}return targets.map((t,index)=>({...t,index}))}async function renderOnce(browser,target,{width,container}){const context=await newDeterministicContext(browser,{width,height:900});const page=await context.newPage();await prepare(page);const consoleErrors=[];const pageErrors=[];page.on("console",m=>m.type()==="error"&&consoleErrors.push(m.text()));page.on("pageerror",e=>pageErrors.push(e.stack||e.message));const url=`${origin}${HARNESS_PATH}?${target.query}${container?`&container=${container}`:""}`;const result={width,...container?{container}:{},errors:[],diagnostics:[],overflow:false};try{await page.goto(url,{waitUntil:"load",timeout:3e4});await settle(page,{timeout:15e3});const harnessError=await page.evaluate(()=>window.__ENGINE_HARNESS_ERROR??null);if(harnessError)result.errors.push(harnessError);result.diagnostics=await page.evaluate(()=>window.__ENGINE_HARNESS_DIAGNOSTICS??[]);result.overflow=await page.evaluate(inContainer=>{const el=inContainer?document.querySelector("[data-engine-container]"):document.scrollingElement;return!!el&&el.scrollWidth>el.clientWidth+1},Boolean(container));result.aria=await page.locator("#root").ariaSnapshot().catch(()=>null)}catch(err){result.errors.push(err instanceof Error?err.message:String(err))}const dir=join(OUTPUT,target.kind,target.label.replace(/[^A-Za-z0-9._-]+/g,"_"));await mkdir(dir,{recursive:true});const shot=join(dir,`${container?`c${container}`:width}.png`);await page.screenshot({path:shot,fullPage:true}).then(()=>result.screenshot=relative(REPO_ROOT,shot),()=>{});const attributeWarning=/Unknown event handler property|React does not recognize the|Invalid DOM property|Invalid value for prop|for a non-boolean attribute/;result.warnings=consoleErrors.filter(m=>attributeWarning.test(m));result.errors.push(...pageErrors,...consoleErrors.filter(m=>!attributeWarning.test(m)));for(const d of result.diagnostics)if(d.severity==="error")result.errors.push(`${d.code}: ${d.message}`);await context.close();return result}
|
package/src/harness/plugin.mjs
CHANGED
|
@@ -10,4 +10,4 @@ import{existsSync}from"node:fs";import{dirname,resolve}from"node:path";import{fi
|
|
|
10
10
|
<div id="root"></div>
|
|
11
11
|
<script type="module" src="/@id/${ENTRY}"><\/script>
|
|
12
12
|
</body>
|
|
13
|
-
</html>`;function
|
|
13
|
+
</html>`;function entryCode(wrapperPath,wrapperExport="default",appPath="/@fs"+APP,wrapperPrefix="/@fs"){return[`import { mountHarness } from ${JSON.stringify(appPath)};`,wrapperPath?`import { ${wrapperExport} as Wrapper } from ${JSON.stringify(wrapperPrefix+wrapperPath)};`:"const Wrapper = undefined;",`const fixtures = import.meta.glob('/src/**/*.fixtures.{ts,tsx}', { eager: true });`,`const barrels = import.meta.glob(['/src/index.ts', '/src/index.*.ts', '!/src/index.*.test.ts'], { eager: true });`,`const pages = import.meta.glob(['/src/pages/*/page.json', '/src/project_templates/*/pages/*/page.json'], { eager: true, import: 'default' });`,`const mocks = import.meta.glob(['/src/pages/*/mock.json', '/src/project_templates/*/pages/*/mock.json'], { eager: true, import: 'default' });`,`mountHarness({ fixtures, barrels, pages, mocks, Wrapper });`].join("\n")}function engineHarness({root,wrapper=null}){const[wrapperFile,wrapperExport="default"]=wrapper?wrapper.split("#"):[];const wrapperPath=wrapperFile?resolve(root,wrapperFile):null;return{name:"engine-harness",resolveId(id){return id===ENTRY?`\0${ENTRY}`:void 0},load(id){return id===`\0${ENTRY}`?entryCode(wrapperPath,wrapperExport):void 0},configureServer(server){server.middlewares.use(async(req,res,next)=>{if(!req.url||!req.url.startsWith(HARNESS_PATH))return next();const html=await server.transformIndexHtml(req.url,HTML);res.setHeader("content-type","text/html; charset=utf-8");res.end(html)})}}}export{APP,HARNESS_PATH,HTML,engineHarness,entryCode};
|
package/src/harness/server.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createRequire}from"node:module";import{dirname,resolve}from"node:path";import{fileURLToPath,pathToFileURL}from"node:url";import{loadConfig}from"../lib/config.mjs";import{REPO_ROOT}from"../lib/root.mjs";import{engineHarness}from"./plugin.mjs";const fromRepo=createRequire(resolve(REPO_ROOT,"package.json"));const importFromRepo=name=>import(pathToFileURL(fromRepo.resolve(name)).href);async function createHarnessServer({port=0,open=false}={}){const config=await loadConfig();const{createServer}=await importFromRepo("vite");const react=(await importFromRepo("@vitejs/plugin-react")).default;const server=await createServer({configFile:false,root:REPO_ROOT,logLevel:"warn",plugins:[react(),engineHarness({root:REPO_ROOT,wrapper:config.harness.wrapper})],define:{__OA_BUNDLE_NAME__:JSON.stringify(config.org)},resolve:{alias:{"@":resolve(REPO_ROOT,"src"),...Object.fromEntries(Object.entries(config.harness.alias).map(([k,v])=>[k,resolve(REPO_ROOT,v)]))}},server:{port,strictPort:Boolean(port),open,fs:{allow:[REPO_ROOT,dirname(fileURLToPath(import.meta.url))]}}});await server.listen();const address=server.httpServer?.address();if(!address||typeof address==="string")throw new Error("[harness] the Vite server did not bind a port");return{server,origin:`http://localhost:${address.port}`}}export{createHarnessServer};
|
|
1
|
+
import{createReadStream,existsSync,mkdirSync,statSync,writeFileSync}from"node:fs";import{createServer as createHttpServer}from"node:http";import{createRequire}from"node:module";import{dirname,extname,join,normalize,resolve}from"node:path";import{fileURLToPath,pathToFileURL}from"node:url";import{loadConfig}from"../lib/config.mjs";import{REPO_ROOT}from"../lib/root.mjs";import{engineHarness}from"./plugin.mjs";const fromRepo=createRequire(resolve(REPO_ROOT,"package.json"));const importFromRepo=name=>import(pathToFileURL(fromRepo.resolve(name)).href);async function createHarnessServer({port=0,open=false}={}){const config=await loadConfig();const{createServer}=await importFromRepo("vite");const react=(await importFromRepo("@vitejs/plugin-react")).default;const server=await createServer({configFile:false,root:REPO_ROOT,logLevel:"warn",plugins:[react(),engineHarness({root:REPO_ROOT,wrapper:config.harness.wrapper})],define:{__OA_BUNDLE_NAME__:JSON.stringify(config.org)},optimizeDeps:{include:["react","react-dom/client","@peripheries/renderer/core","@peripheries/renderer/react","@peripheries/renderer/runtime","@peripheries/renderer/testing"]},resolve:{dedupe:["react","react-dom","@peripheries/renderer","@peripheries/runtime"],alias:{"@":resolve(REPO_ROOT,"src"),...Object.fromEntries(Object.entries(config.harness.alias).map(([k,v])=>[k,resolve(REPO_ROOT,v)]))}},server:{port,strictPort:Boolean(port),open,fs:{allow:[REPO_ROOT,dirname(fileURLToPath(import.meta.url))]}}});await server.listen();const address=server.httpServer?.address();if(!address||typeof address==="string")throw new Error("[harness] the Vite server did not bind a port");return{server,origin:`http://localhost:${address.port}`}}async function buildAndServeHarness(){const config=await loadConfig();const{build}=await importFromRepo("vite");const react=(await importFromRepo("@vitejs/plugin-react")).default;const{entryCode,HTML,APP}=await import("./plugin.mjs");const cache=resolve(REPO_ROOT,"node_modules/.cache/engine-harness");const outDir=resolve(cache,"dist");mkdirSync(cache,{recursive:true});const[wrapperFile,wrapperExport="default"]=config.harness.wrapper?config.harness.wrapper.split("#"):[];writeFileSync(resolve(cache,"entry.mjs"),entryCode(wrapperFile?resolve(REPO_ROOT,wrapperFile):null,wrapperExport,APP,""));writeFileSync(resolve(cache,"harness.html"),HTML.replace(/<script type="module" src="[^"]*"><\/script>/,'<script type="module" src="./entry.mjs"><\/script>'));await build({configFile:false,root:REPO_ROOT,mode:"development",logLevel:"warn",plugins:[react()],define:{__OA_BUNDLE_NAME__:JSON.stringify(config.org),"process.env.NODE_ENV":JSON.stringify("development")},resolve:{dedupe:["react","react-dom","@peripheries/renderer","@peripheries/runtime"],alias:{"@":resolve(REPO_ROOT,"src"),...Object.fromEntries(Object.entries(config.harness.alias).map(([k,v])=>[k,resolve(REPO_ROOT,v)]))}},build:{outDir,emptyOutDir:true,minify:false,sourcemap:false,rollupOptions:{input:resolve(cache,"harness.html")}}});const TYPES={".html":"text/html; charset=utf-8",".js":"text/javascript",".mjs":"text/javascript",".css":"text/css",".json":"application/json",".svg":"image/svg+xml",".png":"image/png",".woff2":"font/woff2"};const server=createHttpServer((req,res)=>{const path=normalize(decodeURIComponent((req.url??"/").split("?")[0])).replace(/^(\.\.[/\\])+/,"");const file=join(outDir,path);if(!file.startsWith(outDir)||!existsSync(file)||statSync(file).isDirectory()){res.statusCode=404;return res.end("not found")}res.setHeader("content-type",TYPES[extname(file)]??"application/octet-stream");createReadStream(file).pipe(res)});await new Promise(r=>server.listen(0,"127.0.0.1",r));const{port}=server.address();return{origin:`http://127.0.0.1:${port}`,path:"/node_modules/.cache/engine-harness/harness.html",close:()=>new Promise(r=>server.close(r))}}export{buildAndServeHarness,createHarnessServer};
|
package/src/index.d.mts
CHANGED
|
@@ -48,7 +48,14 @@ export const MAIN_LINEAGE: 'main';
|
|
|
48
48
|
export const PUBLIC_LINEAGE: 'public';
|
|
49
49
|
export function defineConfig(config: EngineConfig): EngineConfig;
|
|
50
50
|
export function lineageDefaults(name: string, kind?: LineageKind): Lineage;
|
|
51
|
-
export
|
|
51
|
+
export type LoadedConfig = {
|
|
52
|
+
org: string;
|
|
53
|
+
lineages: Lineage[];
|
|
54
|
+
shared: string[];
|
|
55
|
+
checks: { run: string[]; peerRepos: string[]; gated: { projectTemplates: string[]; orgTemplates: string[] } };
|
|
56
|
+
harness: { wrapper: string | null; widths: number[]; container: number; overflow: 'warn' | 'error'; output: string; alias: Record<string, string> };
|
|
57
|
+
};
|
|
58
|
+
export function loadConfig(): Promise<LoadedConfig>;
|
|
52
59
|
export function lineageConfig(name: string): Promise<Lineage>;
|
|
53
60
|
|
|
54
61
|
export type Scenario<P extends Record<string, unknown> = Record<string, unknown>> = {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{register}from"node:module";register("data:text/javascript,"+encodeURIComponent("export async function load(url, context, next) { return url.endsWith('.css') || /\\.css\\?/.test(url) ? { format: 'module', source: '', shortCircuit: true } : next(url, context); }"));
|