@peripheries/engine 0.6.2 → 0.8.0
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 +1 -1
- package/src/commands/build.mjs +3 -3
- package/src/commands/check-css-scope.mjs +2 -0
- package/src/commands/check-inline-styles.mjs +2 -0
- package/src/commands/check-shadowing.mjs +2 -0
- package/src/commands/check-theme.mjs +2 -0
- package/src/commands/check-tokens.mjs +2 -0
- package/src/commands/check.mjs +2 -2
- package/src/commands/upload-bundle.mjs +1 -1
- package/src/index.d.mts +8 -1
- package/src/lib/config.mjs +1 -1
- package/src/lib/layers.mjs +1 -0
- package/src/lib/run.mjs +1 -1
package/package.json
CHANGED
package/src/commands/build.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{spawnSync}from"node:child_process";import{existsSync}from"node:fs";import{createRequire}from"node:module";import{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{runCommand}from"../lib/run.mjs";import{EXTERNALS}from"./externals.mjs";const argv=process.argv.slice(2);const only=parseFlagValue(argv,"lineage");const config=await loadConfig();const lineages=only?config.lineages.filter(l=>l.name===only):config.lineages;if(only&&lineages.length===0)throw new Error(`[build] --lineage ${only} is not in the config's lineages`);const fromRepo=createRequire(resolve(REPO_ROOT,"package.json"));const importFromRepo=name=>import(pathToFileURL(fromRepo.resolve(name)).href);function check(label,status){if(status!==0){console.error(`[build] ${label} failed (exit ${status}).`);process.exit(status??1)}}if(!argv.includes("--no-typecheck")&&existsSync(resolve(REPO_ROOT,"tsconfig.build.json"))){console.log("[build] typecheck: tsc -p tsconfig.build.json --noEmit");const tsc=fromRepo.resolve("typescript/bin/tsc");check("typecheck",spawnSync(process.execPath,[tsc,"-p","tsconfig.build.json","--noEmit"],{cwd:REPO_ROOT,stdio:"inherit"}).status)}process.env.NODE_ENV="production";const{build}=await importFromRepo("vite");const react=(await importFromRepo("@vitejs/plugin-react")).default;for(const lineage of lineages){const entry=resolve(REPO_ROOT,lineage.entry);if(!existsSync(entry))throw new Error(`[build] no barrel at ${lineage.entry} for lineage "${lineage.name}"`);const external=[...new Set([...EXTERNALS,...config.shared,...lineage.shared??[]])].filter(s=>s!==lineage.specifier);console.log(`
|
|
3
|
-
[build] ${lineage.name}: ${lineage.entry} \u2192 ${lineage.dist}/index.js`);await build({configFile:false,mode:"production",root:REPO_ROOT,logLevel:"warn",plugins:[react()],define:{"process.env.NODE_ENV":JSON.stringify("production"),__OA_BUNDLE_NAME__:JSON.stringify(config.org)},resolve:{alias:{"@":resolve(REPO_ROOT,"src")}},optimizeDeps:{exclude:external},build:{lib:{entry,formats:["es"],fileName:()=>"index.js"},rollupOptions:{external},outDir:resolve(REPO_ROOT,lineage.dist),emptyOutDir:true,sourcemap:true}});check(`${lineage.name} contracts`,runCommand("contracts",["--entry",lineage.entry,"--out",`${lineage.dist}/contracts.json`]));check(`${lineage.name} knowledge`,runCommand("knowledge",["--bundle",lineage.name]))}console.log(`
|
|
4
|
-
[build] \u2705 ${lineages.map(l=>l.name).join(", ")}`);
|
|
2
|
+
import{spawnSync}from"node:child_process";import{existsSync,readdirSync,readFileSync,rmSync,writeFileSync}from"node:fs";import{createRequire}from"node:module";import{resolve}from"node:path";import{pathToFileURL}from"node:url";import{loadConfig}from"../lib/config.mjs";import{parseFlagValue}from"../lib/env.mjs";import{layerCss,layerFor}from"../lib/layers.mjs";import{REPO_ROOT}from"../lib/root.mjs";import{runCommand}from"../lib/run.mjs";import{EXTERNALS}from"./externals.mjs";const argv=process.argv.slice(2);const only=parseFlagValue(argv,"lineage");const config=await loadConfig();const lineages=only?config.lineages.filter(l=>l.name===only):config.lineages;if(only&&lineages.length===0)throw new Error(`[build] --lineage ${only} is not in the config's lineages`);const fromRepo=createRequire(resolve(REPO_ROOT,"package.json"));const importFromRepo=name=>import(pathToFileURL(fromRepo.resolve(name)).href);function check(label,status){if(status!==0){console.error(`[build] ${label} failed (exit ${status}).`);process.exit(status??1)}}if(!argv.includes("--no-typecheck")&&existsSync(resolve(REPO_ROOT,"tsconfig.build.json"))){console.log("[build] typecheck: tsc -p tsconfig.build.json --noEmit");const tsc=fromRepo.resolve("typescript/bin/tsc");check("typecheck",spawnSync(process.execPath,[tsc,"-p","tsconfig.build.json","--noEmit"],{cwd:REPO_ROOT,stdio:"inherit"}).status)}process.env.NODE_ENV="production";const{build}=await importFromRepo("vite");const react=(await importFromRepo("@vitejs/plugin-react")).default;for(const lineage of lineages){const entry=resolve(REPO_ROOT,lineage.entry);if(!existsSync(entry))throw new Error(`[build] no barrel at ${lineage.entry} for lineage "${lineage.name}"`);const external=[...new Set([...EXTERNALS,...config.shared,...lineage.shared??[]])].filter(s=>s!==lineage.specifier);console.log(`
|
|
3
|
+
[build] ${lineage.name}: ${lineage.entry} \u2192 ${lineage.dist}/index.js`);await build({configFile:false,mode:"production",root:REPO_ROOT,logLevel:"warn",plugins:[react()],define:{"process.env.NODE_ENV":JSON.stringify("production"),__OA_BUNDLE_NAME__:JSON.stringify(config.org)},resolve:{alias:{"@":resolve(REPO_ROOT,"src")}},optimizeDeps:{exclude:external},build:{lib:{entry,formats:["es"],fileName:()=>"index.js"},rollupOptions:{external},outDir:resolve(REPO_ROOT,lineage.dist),emptyOutDir:true,sourcemap:true}});await writeLayeredCss(lineage);check(`${lineage.name} contracts`,runCommand("contracts",["--entry",lineage.entry,"--out",`${lineage.dist}/contracts.json`]));check(`${lineage.name} knowledge`,runCommand("knowledge",["--bundle",lineage.name]))}console.log(`
|
|
4
|
+
[build] \u2705 ${lineages.map(l=>l.name).join(", ")}`);async function writeLayeredCss(lineage){const dist=resolve(REPO_ROOT,lineage.dist);const files=readdirSync(dist).filter(f=>f.endsWith(".css"));if(files.length===0)return;const css=files.map(f=>readFileSync(resolve(dist,f),"utf8")).join("\n");for(const f of files)rmSync(resolve(dist,f));const postcss=(await importFromRepo("postcss")).default;const layer=layerFor(lineage);writeFileSync(resolve(dist,"index.css"),layerCss(postcss,css,layer));console.log(`[build] ${lineage.name}: index.css in @layer ${layer}`)}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{existsSync,readdirSync,readFileSync,statSync}from"node:fs";import{createRequire}from"node:module";import{join,relative,resolve}from"node:path";import{pathToFileURL}from"node:url";import{loadConfig}from"../lib/config.mjs";import{REPO_ROOT}from"../lib/root.mjs";const strict=process.argv.includes("--strict");const{org,lineages}=await loadConfig();const prefixes=[...new Set(lineages.map(l=>l.cssPrefix??(l.kind==="shared"?`${l.name}-`:`${org}-`)))];const SKIP=/(^|\/)(node_modules|__output__|dist[^/]*)(\/|$)/;const walk=p=>{if(!existsSync(p)||SKIP.test(p))return[];return statSync(p).isDirectory()?readdirSync(p).flatMap(n=>walk(join(p,n))):[p]};const files=walk(resolve(REPO_ROOT,"src")).filter(f=>f.endsWith(".css"));const fromRepo=createRequire(resolve(REPO_ROOT,"package.json"));const postcss=files.length?(await import(pathToFileURL(fromRepo.resolve("postcss")).href)).default:null;const exempt=node=>{for(let p=node.parent;p;p=p.parent){if(p.type==="atrule"&&(/keyframes$/.test(p.name)||p.name==="font-face"||p.name==="layer"&&p.params.trim()==="tokens"))return true}return false};const scoped=selector=>prefixes.some(pre=>new RegExp(`\\.${pre.replace(/[-]/g,"\\-")}[\\w-]`).test(selector));const findings=[];for(const file of files){const root=postcss.parse(readFileSync(file,"utf8"),{from:file});root.walkRules(rule=>{if(exempt(rule))return;for(const selector of rule.selectors){if(!scoped(selector))findings.push(`${relative(REPO_ROOT,file)}:${rule.source?.start?.line??"?"}: "${selector}" is not under ${prefixes.map(p=>`.${p}*`).join(" or ")}`)}})}const shown=strict||process.argv.includes("--all")?findings:findings.slice(0,20);for(const f of shown)console[strict?"error":"warn"](`[css-scope] ${f}`);if(shown.length<findings.length)console.warn(`[css-scope] \u2026 ${findings.length-shown.length} more (--all lists them)`);console.log(`[css-scope] ${files.length} stylesheet(s), prefixes ${prefixes.join(", ")}; ${findings.length} finding(s)${findings.length&&!strict?" (warnings: --strict fails on them)":""}.`);process.exit(strict&&findings.length?1:0);
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{existsSync,readdirSync,readFileSync,statSync}from"node:fs";import{join,relative,resolve}from"node:path";import{fileURLToPath}from"node:url";import{budgetVerdict,loadConfig}from"../lib/config.mjs";import{REPO_ROOT}from"../lib/root.mjs";const SKIP=/(^|\/)(node_modules|__output__|dist[^/]*)(\/|$)|\.(fixtures|test|stories)\.[jt]sx?$|\.d\.ts$/;function onlyCustomProperties(ts,expr){while(ts.isParenthesizedExpression(expr)||ts.isAsExpression(expr)||ts.isSatisfiesExpression?.(expr))expr=expr.expression;if(ts.isCallExpression(expr)&&ts.isIdentifier(expr.expression)&&expr.expression.text==="vars")return true;if(ts.isIdentifier(expr)&&expr.text==="style"||ts.isPropertyAccessExpression(expr)&&expr.name.text==="style")return true;if(!ts.isObjectLiteralExpression(expr)||!expr.properties.length)return false;return expr.properties.every(p=>{if(ts.isSpreadAssignment(p))return onlyCustomProperties(ts,p.expression);const name=p.name&&(ts.isStringLiteral(p.name)||ts.isNoSubstitutionTemplateLiteral(p.name)?p.name.text:null);return typeof name==="string"&&name.startsWith("--")})}function inlineStyles(ts,code,fileName="x.tsx"){const sf=ts.createSourceFile(fileName,code,ts.ScriptTarget.Latest,true,ts.ScriptKind.TSX);const found=[];const visit=node=>{if(ts.isJsxAttribute(node)&&node.name.getText(sf)==="style"&&node.initializer&&ts.isJsxExpression(node.initializer)&&node.initializer.expression){if(!onlyCustomProperties(ts,node.initializer.expression)){const{line}=sf.getLineAndCharacterOfPosition(node.getStart(sf));found.push(`${line+1}: ${node.getText(sf).replace(/\s+/g," ").slice(0,80)}`)}}ts.forEachChild(node,visit)};visit(sf);return found}async function main(){const strict=process.argv.includes("--strict");const ts=(await import("typescript")).default;const walk=p=>{if(!existsSync(p)||SKIP.test(p))return[];return statSync(p).isDirectory()?readdirSync(p).flatMap(n=>walk(join(p,n))):[p]};const files=walk(resolve(REPO_ROOT,"src")).filter(f=>f.endsWith(".tsx"));const findings=files.flatMap(f=>inlineStyles(ts,readFileSync(f,"utf8"),f).map(x=>`${relative(REPO_ROOT,f)}:${x}`));const shown=strict||process.argv.includes("--all")?findings:findings.slice(0,20);for(const f of shown)console[strict?"error":"warn"](`[inline-styles] ${f}`);if(shown.length<findings.length)console.warn(`[inline-styles] \u2026 ${findings.length-shown.length} more (--all lists them)`);console.log(`[inline-styles] ${files.length} file(s), ${findings.length} inline style(s) beyond custom properties.`);const{checks}=await loadConfig();const budget=budgetVerdict("check-inline-styles",findings.length,checks.budgets["check-inline-styles"]);if(budget.message)console[budget.ok?"log":"error"](`[inline-styles] ${budget.message}`);return strict&&findings.length||!budget.ok?1:0}if(process.argv[1]&&resolve(process.argv[1])===fileURLToPath(import.meta.url)){process.exit(await main())}export{inlineStyles};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{existsSync}from"node:fs";import{resolve}from"node:path";import{fileURLToPath}from"node:url";import{loadConfig}from"../lib/config.mjs";import{REPO_ROOT}from"../lib/root.mjs";function shadowFindings(lineageExports,sharedExports,shadows={}){const findings=[];const colliding=new Set;for(const[lineage,names]of lineageExports){for(const name of names){const owner=[...sharedExports].find(([,shared])=>shared.has(name))?.[0];if(!owner)continue;colliding.add(name);if(!(name in shadows))findings.push(`lineage "${lineage}" exports ${name}, which ${owner} also exports: declare it in shadows with a reason, or rename it`);else if(typeof shadows[name]!=="string"||!shadows[name].trim())findings.push(`shadows.${name} needs a reason`)}}for(const name of Object.keys(shadows)){if(!colliding.has(name))findings.push(`shadows.${name} is declared, but no lineage exports ${name} over a shared lineage: remove it`)}return findings}async function main(){const{lineages,shared,shadows}=await loadConfig();const own=lineages.filter(l=>l.kind!=="shared");const specifiers=[...new Set([...shared,...own.flatMap(l=>l.shared??[])])];if(!own.length||!specifiers.length){console.log("[shadowing] no org lineage imports a shared lineage; nothing to check.");return 0}const ts=(await import("typescript")).default;const configPath=resolve(REPO_ROOT,"tsconfig.json");const options=existsSync(configPath)?ts.parseJsonConfigFileContent(ts.readConfigFile(configPath,ts.sys.readFile).config,ts.sys,REPO_ROOT).options:{module:ts.ModuleKind.ESNext,moduleResolution:ts.ModuleResolutionKind.Bundler,jsx:ts.JsxEmit.ReactJSX};const from=resolve(REPO_ROOT,own[0].entry);const sharedFiles=new Map;for(const spec of specifiers){const resolved=ts.resolveModuleName(spec,from,options,ts.sys).resolvedModule?.resolvedFileName;if(!resolved){console.error(`[shadowing] can't resolve ${spec}: install it (npm:@peripheries/\u2026) or map it in tsconfig paths`);return 1}sharedFiles.set(spec,resolved)}const entries=own.map(l=>resolve(REPO_ROOT,l.entry)).filter(f=>existsSync(f));const program=ts.createProgram({rootNames:[...entries,...sharedFiles.values()],options:{...options,noEmit:true}});const checker=program.getTypeChecker();const valuesOf=file=>{const sf=program.getSourceFile(file);const symbol=sf&&checker.getSymbolAtLocation(sf);if(!symbol)return new Set;return new Set(checker.getExportsOfModule(symbol).filter(s=>{const target=s.flags&ts.SymbolFlags.Alias?checker.getAliasedSymbol(s):s;return(target.flags&ts.SymbolFlags.Value)!==0}).map(s=>s.name).filter(n=>/^[A-Z]/.test(n)))};const sharedExports=new Map([...sharedFiles].map(([spec,file])=>[spec,valuesOf(file)]));const lineageExports=new Map(own.map(l=>[l.name,valuesOf(resolve(REPO_ROOT,l.entry))]));const findings=shadowFindings(lineageExports,sharedExports,shadows);for(const f of findings)console.error(`[shadowing] ${f}`);const declared=Object.keys(shadows).length;console.log(`[shadowing] ${own.length} lineage(s) against ${specifiers.join(", ")} (${[...sharedExports.values()].reduce((n,s)=>n+s.size,0)} shared names); ${declared} declared shadow(s), ${findings.length} finding(s).`);return findings.length?1:0}if(process.argv[1]&&resolve(process.argv[1])===fileURLToPath(import.meta.url)){process.exit(await main())}export{shadowFindings};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{existsSync,readdirSync,readFileSync,statSync}from"node:fs";import{createRequire}from"node:module";import{join,relative,resolve}from"node:path";import{fileURLToPath,pathToFileURL}from"node:url";import{REPO_ROOT}from"../lib/root.mjs";const ROOTS=["src/org_templates","src/project_templates"];function themesIn(value,path=""){if(!value||typeof value!=="object")return[];if(Array.isArray(value))return value.flatMap((v,i)=>themesIn(v,`${path}[${i}]`));return Object.entries(value).flatMap(([k,v])=>{const here=path?`${path}.${k}`:k;if(k==="theme"&&v&&typeof v==="object"&&!Array.isArray(v))return[{path:here,theme:v}];return themesIn(v,here)})}function failing(raw,diagnostics,strict){if(strict)return diagnostics;if(raw.version!==2)return[];return diagnostics.filter(d=>d.severity==="error"||d.code==="THEME_CONTRAST")}async function main(){const strict=process.argv.includes("--strict");let api;try{const entry=createRequire(resolve(REPO_ROOT,"package.json")).resolve("@peripheries/renderer/theme");api=await import(pathToFileURL(entry).href)}catch{console.log("[theme] @peripheries/renderer/theme not found (renderer 0.7 or later has it); nothing checked.");return 0}const walk=p=>statSync(p).isDirectory()?readdirSync(p).flatMap(n=>walk(join(p,n))):[p];const files=ROOTS.map(r=>resolve(REPO_ROOT,r)).filter(existsSync).flatMap(walk).filter(f=>f.endsWith("template.json"));let themes=0;let failed=0;for(const file of files){for(const{path,theme:raw}of themesIn(JSON.parse(readFileSync(file,"utf8")))){themes++;const where=`${relative(REPO_ROOT,file)} ${path}`;const{theme,diagnostics}=api.parseTheme(raw);const all=[...diagnostics,...api.checkTheme(theme)];const fails=new Set(failing(raw,all,strict));if(raw.version!==2)console.warn(`[theme] ${where}: version 1; move it to version 2 (design \xA76.1)`);for(const d of all)console[fails.has(d)?"error":"warn"](`[theme] ${where}: ${d.code} ${d.path}: ${d.message}`);failed+=fails.size}}console.log(`[theme] ${files.length} template(s), ${themes} theme(s), ${failed} failure(s).`);return failed?1:0}if(process.argv[1]&&resolve(process.argv[1])===fileURLToPath(import.meta.url)){process.exit(await main())}export{failing,themesIn};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{existsSync,readdirSync,readFileSync,statSync}from"node:fs";import{join,relative,resolve}from"node:path";import{budgetVerdict,loadConfig}from"../lib/config.mjs";import{REPO_ROOT}from"../lib/root.mjs";const strict=process.argv.includes("--strict");const SKIP=/(^|\/)(node_modules|__output__|dist[^/]*)(\/|$)|\.(fixtures|test|stories)\.[jt]sx?$|\.d\.ts$/;const RULES=[[/var\(\s*--palette-[\w-]+/,"a primitive token (read a semantic one)"],[/var\(\s*--oa-[\w-]+/,"a legacy --oa-* name"],[/(?<![\w-])#[0-9a-fA-F]{3,8}\b(?![\w-])/,"a hex colour (use a token)"],[/\b(rgba?|hsla?|oklch|oklab)\(\s*\d/,"a literal colour (use a token)"]];const walk=p=>{if(!existsSync(p)||SKIP.test(p))return[];return statSync(p).isDirectory()?readdirSync(p).flatMap(n=>walk(join(p,n))):[p]};const files=walk(resolve(REPO_ROOT,"src")).filter(f=>/\.(css|tsx?|mts)$/.test(f));const findings=[];for(const file of files){const lines=readFileSync(file,"utf8").split("\n");if(lines.slice(0,15).some(l=>l.includes("check-tokens: defines tokens")))continue;const css=file.endsWith(".css");lines.forEach((line,i)=>{const trimmed=line.trim();if(trimmed.startsWith("//")||trimmed.startsWith("*")||trimmed.startsWith("/*"))return;for(const[re,what]of RULES){if(!css&&re.source.startsWith("(?<!")&&!/['"`][^'"`]*#[0-9a-fA-F]{3,8}\b/.test(line))continue;const m=line.match(re);if(m)findings.push(`${relative(REPO_ROOT,file)}:${i+1}: ${what}: ${m[0]}`)}})}const shown=strict||process.argv.includes("--all")?findings:findings.slice(0,20);for(const f of shown)console[strict?"error":"warn"](`[tokens] ${f}`);if(shown.length<findings.length)console.warn(`[tokens] \u2026 ${findings.length-shown.length} more (--all lists them)`);console.log(`[tokens] ${files.length} file(s), ${findings.length} finding(s)${findings.length&&!strict?" (warnings: --strict fails on them)":""}.`);const{checks}=await loadConfig();const budget=budgetVerdict("check-tokens",findings.length,checks.budgets["check-tokens"]);if(budget.message)console[budget.ok?"log":"error"](`[tokens] ${budget.message}`);process.exit(strict&&findings.length||!budget.ok?1:0);
|
package/src/commands/check.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import{loadConfig}from"../lib/config.mjs";import{runCommand}from"../lib/run.mjs";const{lineages,checks}=await loadConfig();const named=process.argv.slice(2).filter(a=>!a.startsWith("--"));const list=named.length?named:checks.run;const results=[];for(const name of list){if(name==="knowledge"){for(const l of lineages){console.log(`
|
|
3
|
-
[check] knowledge --bundle ${l.name}`);results.push([`knowledge (${l.name})`,runCommand("knowledge",["--bundle",l.name,"--check"])])}continue}console.log(`
|
|
4
|
-
[check] ${name}`);results.push([name,runCommand(
|
|
3
|
+
[check] knowledge --bundle ${l.name}`);results.push([`knowledge (${l.name})`,runCommand("knowledge",["--bundle",l.name,"--check"])])}continue}const[command,...args]=name.split(/\s+/);console.log(`
|
|
4
|
+
[check] ${name}`);results.push([name,runCommand(command,args)])}const failed=results.filter(([,status])=>status!==0);console.log(`
|
|
5
5
|
[check] ${results.length-failed.length}/${results.length} passed${failed.length?`; failed: ${failed.map(([n])=>n).join(", ")}`:""}.`);process.exit(failed.length?1:0);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{readFileSync,existsSync,statSync}from"node:fs";import{resolve,dirname}from"node:path";import{fileURLToPath}from"node:url";import{BUNDLE_BUCKET,PUBLIC_BUNDLE_BUCKET,initFromArgv,parseFlagValue}from"../lib/env.mjs";import{REPO_ROOT as REPO_ROOT_}from"../lib/root.mjs";import{lineageConfig}from"../lib/config.mjs";const argv=process.argv.slice(2);const{target,orgSlug:ORG_SLUG,supabase}=initFromArgv(argv);const BUNDLE_NAME=parseFlagValue(argv,"bundle")??"main";const dryRun=argv.includes("--dry-run");const IS_MAIN=BUNDLE_NAME==="main";const IS_PUBLIC=BUNDLE_NAME==="public";const IS_ORG_WIDE=IS_MAIN||IS_PUBLIC;const IS_SHARED=(await lineageConfig(BUNDLE_NAME)).kind==="shared";const DEFAULT_BUCKET=IS_PUBLIC||IS_SHARED?PUBLIC_BUNDLE_BUCKET:BUNDLE_BUCKET;const STORAGE_BUCKET=process.env.STORAGE_BUCKET||DEFAULT_BUCKET;if(STORAGE_BUCKET!==DEFAULT_BUCKET){console.error(`[upload-bundle] STORAGE_BUCKET must be "${DEFAULT_BUCKET}" for lineage "${BUNDLE_NAME}", got "${STORAGE_BUCKET}".`);process.exit(2)}const VERSION=process.env.VERSION||`v_${target}_${new Date().toISOString().replace(/[-:T]/g,"").replace(/\..+$/,"").slice(0,14)}`;if(!process.env.VERSION){console.log(`[upload-bundle] VERSION auto-generated: ${VERSION}`);process.env.VERSION=VERSION}const distDir=resolve(REPO_ROOT_,(await lineageConfig(BUNDLE_NAME)).dist);const bundlePath=resolve(distDir,"index.js");if(!existsSync(bundlePath)){console.error(`[upload-bundle] ${bundlePath} does not exist. `+(IS_MAIN?"Did `npm run build` run?":`Did \`OA_PROJECT_BUNDLE=${BUNDLE_NAME} npm run build:bundle\` run?`));process.exit(1)}console.log(`[upload-bundle] lineage=${BUNDLE_NAME} dist=${distDir.split("/").slice(-2).join("/")}`);const{data:orgRow,error:orgError}=await supabase.from("organization").select("id").eq("metadata->>org_slug",ORG_SLUG).maybeSingle();if(orgError||!orgRow?.id){console.error(`[upload-bundle] Could not resolve org_id for org_slug="${ORG_SLUG}":`,orgError??"no matching organization row");process.exit(1)}const ORG_ID=orgRow.id;async function uploadOne(localPath,remotePath,contentType){const body=readFileSync(localPath);if(dryRun){console.log(`[upload-bundle] would upload ${STORAGE_BUCKET}/${remotePath} (${body.byteLength} bytes)`);return}const{error}=await supabase.storage.from(STORAGE_BUCKET).upload(remotePath,body,{contentType,upsert:false});if(error){console.error(`[upload-bundle] Failed to upload ${remotePath}:`,error);process.exit(1)}console.log(`[upload-bundle] \u2191 ${remotePath} (${body.byteLength} bytes)`)}const remoteBase=IS_ORG_WIDE?`${ORG_SLUG}/${VERSION}`:`${ORG_SLUG}/${BUNDLE_NAME}/${VERSION}`;await uploadOne(bundlePath,`${remoteBase}/index.js`,"application/javascript");const mapPath=resolve(distDir,"index.js.map");if(existsSync(mapPath)){await uploadOne(mapPath,`${remoteBase}/index.js.map`,"application/json")}const contractsPath=resolve(distDir,"contracts.json");if(existsSync(contractsPath)){await uploadOne(contractsPath,`${remoteBase}/contracts.json`,"application/json")}const{data:existingBundle,error:existingBundleError}=await supabase.from("component_bundle").select("id, status").eq("org_id",ORG_ID).eq("bundle_name",BUNDLE_NAME).eq("version",VERSION).maybeSingle();if(existingBundleError){console.error(`[upload-bundle] Failed to look up existing component_bundle for (org=${ORG_SLUG}, bundle=${BUNDLE_NAME}, version=${VERSION}):`,existingBundleError);process.exit(1)}const bundleBytes=statSync(bundlePath).size;const hasSourcemap=existsSync(mapPath);const bundlePayload={org_id:ORG_ID,org_slug:ORG_SLUG,bundle_name:BUNDLE_NAME,version:VERSION,status:"uploaded",storage_bucket:STORAGE_BUCKET,storage_path:`${remoteBase}/index.js`,...IS_SHARED?{is_public:true}:{},uploaded_at:new Date().toISOString(),registered_at:null,cutover_at:null,git_sha:process.env.DEPLOY_SHA??process.env.GITHUB_SHA??null,git_ref:process.env.DEPLOY_REF??process.env.GITHUB_REF??null,git_repo:process.env.GITHUB_REPOSITORY??null,process_id:process.env.PROCESS_ID||null,metadata:{bundle_bytes:bundleBytes,has_sourcemap:hasSourcemap,bundle_scope:BUNDLE_NAME,...process.env.GITHUB_RUN_ID?{github_run_id:process.env.GITHUB_RUN_ID}:{},...process.env.GITHUB_RUN_ATTEMPT?{github_run_attempt:process.env.GITHUB_RUN_ATTEMPT}:{}}};if(dryRun){console.log(`[upload-bundle] dry-run \u2014 would ${existingBundle?.id?`UPDATE ${existingBundle.id}`:"INSERT"} component_bundle:`);console.log(JSON.stringify(bundlePayload,null,2));console.log("[upload-bundle] dry-run \u2014 nothing written.")}else if(existingBundle?.id){const{error:updateError}=await supabase.from("component_bundle").update(bundlePayload).eq("id",existingBundle.id);if(updateError){console.error(`[upload-bundle] Failed to update component_bundle row:`,updateError);process.exit(1)}console.log(`[upload-bundle] ~ component_bundle ${existingBundle.id} reset to status='uploaded' (was '${existingBundle.status}').`)}else{const{data:inserted,error:insertError}=await supabase.from("component_bundle").insert(bundlePayload).select("id").single();if(insertError){console.error(`[upload-bundle] Failed to insert component_bundle row:`,insertError);process.exit(1)}console.log(`[upload-bundle] + component_bundle ${inserted.id} created (status='uploaded').`)}console.log(`[upload-bundle] Done \u2014 target=${target} org=${ORG_SLUG} bundle=${BUNDLE_NAME} version=${VERSION}. Run register-components.mjs next.`);
|
|
2
|
+
import{readFileSync,existsSync,statSync}from"node:fs";import{resolve,dirname}from"node:path";import{fileURLToPath}from"node:url";import{BUNDLE_BUCKET,PUBLIC_BUNDLE_BUCKET,initFromArgv,parseFlagValue}from"../lib/env.mjs";import{REPO_ROOT as REPO_ROOT_}from"../lib/root.mjs";import{lineageConfig}from"../lib/config.mjs";const argv=process.argv.slice(2);const{target,orgSlug:ORG_SLUG,supabase}=initFromArgv(argv);const BUNDLE_NAME=parseFlagValue(argv,"bundle")??"main";const dryRun=argv.includes("--dry-run");const IS_MAIN=BUNDLE_NAME==="main";const IS_PUBLIC=BUNDLE_NAME==="public";const IS_ORG_WIDE=IS_MAIN||IS_PUBLIC;const IS_SHARED=(await lineageConfig(BUNDLE_NAME)).kind==="shared";const DEFAULT_BUCKET=IS_PUBLIC||IS_SHARED?PUBLIC_BUNDLE_BUCKET:BUNDLE_BUCKET;const STORAGE_BUCKET=process.env.STORAGE_BUCKET||DEFAULT_BUCKET;if(STORAGE_BUCKET!==DEFAULT_BUCKET){console.error(`[upload-bundle] STORAGE_BUCKET must be "${DEFAULT_BUCKET}" for lineage "${BUNDLE_NAME}", got "${STORAGE_BUCKET}".`);process.exit(2)}const VERSION=process.env.VERSION||`v_${target}_${new Date().toISOString().replace(/[-:T]/g,"").replace(/\..+$/,"").slice(0,14)}`;if(!process.env.VERSION){console.log(`[upload-bundle] VERSION auto-generated: ${VERSION}`);process.env.VERSION=VERSION}const distDir=resolve(REPO_ROOT_,(await lineageConfig(BUNDLE_NAME)).dist);const bundlePath=resolve(distDir,"index.js");if(!existsSync(bundlePath)){console.error(`[upload-bundle] ${bundlePath} does not exist. `+(IS_MAIN?"Did `npm run build` run?":`Did \`OA_PROJECT_BUNDLE=${BUNDLE_NAME} npm run build:bundle\` run?`));process.exit(1)}console.log(`[upload-bundle] lineage=${BUNDLE_NAME} dist=${distDir.split("/").slice(-2).join("/")}`);const{data:orgRow,error:orgError}=await supabase.from("organization").select("id").eq("metadata->>org_slug",ORG_SLUG).maybeSingle();if(orgError||!orgRow?.id){console.error(`[upload-bundle] Could not resolve org_id for org_slug="${ORG_SLUG}":`,orgError??"no matching organization row");process.exit(1)}const ORG_ID=orgRow.id;async function uploadOne(localPath,remotePath,contentType){const body=readFileSync(localPath);if(dryRun){console.log(`[upload-bundle] would upload ${STORAGE_BUCKET}/${remotePath} (${body.byteLength} bytes)`);return}const{error}=await supabase.storage.from(STORAGE_BUCKET).upload(remotePath,body,{contentType,upsert:false});if(error){console.error(`[upload-bundle] Failed to upload ${remotePath}:`,error);process.exit(1)}console.log(`[upload-bundle] \u2191 ${remotePath} (${body.byteLength} bytes)`)}const remoteBase=IS_ORG_WIDE?`${ORG_SLUG}/${VERSION}`:`${ORG_SLUG}/${BUNDLE_NAME}/${VERSION}`;await uploadOne(bundlePath,`${remoteBase}/index.js`,"application/javascript");const cssPath=resolve(distDir,"index.css");if(existsSync(cssPath)){await uploadOne(cssPath,`${remoteBase}/index.css`,"text/css")}const mapPath=resolve(distDir,"index.js.map");if(existsSync(mapPath)){await uploadOne(mapPath,`${remoteBase}/index.js.map`,"application/json")}const contractsPath=resolve(distDir,"contracts.json");if(existsSync(contractsPath)){await uploadOne(contractsPath,`${remoteBase}/contracts.json`,"application/json")}const{data:existingBundle,error:existingBundleError}=await supabase.from("component_bundle").select("id, status").eq("org_id",ORG_ID).eq("bundle_name",BUNDLE_NAME).eq("version",VERSION).maybeSingle();if(existingBundleError){console.error(`[upload-bundle] Failed to look up existing component_bundle for (org=${ORG_SLUG}, bundle=${BUNDLE_NAME}, version=${VERSION}):`,existingBundleError);process.exit(1)}const bundleBytes=statSync(bundlePath).size;const hasSourcemap=existsSync(mapPath);const bundlePayload={org_id:ORG_ID,org_slug:ORG_SLUG,bundle_name:BUNDLE_NAME,version:VERSION,status:"uploaded",storage_bucket:STORAGE_BUCKET,storage_path:`${remoteBase}/index.js`,...IS_SHARED?{is_public:true}:{},uploaded_at:new Date().toISOString(),registered_at:null,cutover_at:null,git_sha:process.env.DEPLOY_SHA??process.env.GITHUB_SHA??null,git_ref:process.env.DEPLOY_REF??process.env.GITHUB_REF??null,git_repo:process.env.GITHUB_REPOSITORY??null,process_id:process.env.PROCESS_ID||null,metadata:{bundle_bytes:bundleBytes,has_sourcemap:hasSourcemap,bundle_scope:BUNDLE_NAME,files:["index.js",...existsSync(cssPath)?["index.css"]:[]],...process.env.GITHUB_RUN_ID?{github_run_id:process.env.GITHUB_RUN_ID}:{},...process.env.GITHUB_RUN_ATTEMPT?{github_run_attempt:process.env.GITHUB_RUN_ATTEMPT}:{}}};if(dryRun){console.log(`[upload-bundle] dry-run \u2014 would ${existingBundle?.id?`UPDATE ${existingBundle.id}`:"INSERT"} component_bundle:`);console.log(JSON.stringify(bundlePayload,null,2));console.log("[upload-bundle] dry-run \u2014 nothing written.")}else if(existingBundle?.id){const{error:updateError}=await supabase.from("component_bundle").update(bundlePayload).eq("id",existingBundle.id);if(updateError){console.error(`[upload-bundle] Failed to update component_bundle row:`,updateError);process.exit(1)}console.log(`[upload-bundle] ~ component_bundle ${existingBundle.id} reset to status='uploaded' (was '${existingBundle.status}').`)}else{const{data:inserted,error:insertError}=await supabase.from("component_bundle").insert(bundlePayload).select("id").single();if(insertError){console.error(`[upload-bundle] Failed to insert component_bundle row:`,insertError);process.exit(1)}console.log(`[upload-bundle] + component_bundle ${inserted.id} created (status='uploaded').`)}console.log(`[upload-bundle] Done \u2014 target=${target} org=${ORG_SLUG} bundle=${BUNDLE_NAME} version=${VERSION}. Run register-components.mjs next.`);
|
package/src/index.d.mts
CHANGED
|
@@ -11,12 +11,15 @@ export type LineageSpec =
|
|
|
11
11
|
specifier?: string;
|
|
12
12
|
shared?: string[];
|
|
13
13
|
build?: { script: string; env?: Record<string, string> };
|
|
14
|
+
layer?: 'reset' | 'tokens' | 'renderer' | 'core' | 'charts' | 'org' | 'app' | 'theme';
|
|
15
|
+
cssPrefix?: string;
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
export type EngineConfig = {
|
|
17
19
|
org: string;
|
|
18
20
|
lineages?: LineageSpec[];
|
|
19
21
|
shared?: string[];
|
|
22
|
+
shadows?: Record<string, string>;
|
|
20
23
|
harness?: {
|
|
21
24
|
wrapper?: string;
|
|
22
25
|
widths?: number[];
|
|
@@ -29,6 +32,7 @@ export type EngineConfig = {
|
|
|
29
32
|
run?: string[];
|
|
30
33
|
peerRepos?: string[];
|
|
31
34
|
gated?: { projectTemplates?: string[]; orgTemplates?: string[] };
|
|
35
|
+
budgets?: Partial<Record<'check-tokens' | 'check-inline-styles', number>>;
|
|
32
36
|
};
|
|
33
37
|
};
|
|
34
38
|
|
|
@@ -41,6 +45,8 @@ export type Lineage = {
|
|
|
41
45
|
build?: { script: string; env?: Record<string, string> };
|
|
42
46
|
specifier?: string;
|
|
43
47
|
shared?: string[];
|
|
48
|
+
layer?: string;
|
|
49
|
+
cssPrefix?: string;
|
|
44
50
|
};
|
|
45
51
|
|
|
46
52
|
export const DEFAULT_CHECKS: string[];
|
|
@@ -52,7 +58,8 @@ export type LoadedConfig = {
|
|
|
52
58
|
org: string;
|
|
53
59
|
lineages: Lineage[];
|
|
54
60
|
shared: string[];
|
|
55
|
-
checks: { run: string[]; peerRepos: string[]; gated: { projectTemplates: string[]; orgTemplates: string[] } };
|
|
61
|
+
checks: { run: string[]; peerRepos: string[]; gated: { projectTemplates: string[]; orgTemplates: string[] }; budgets: Record<string, number> };
|
|
62
|
+
shadows: Record<string, string>;
|
|
56
63
|
harness: { wrapper: string | null; widths: number[]; container: number; overflow: 'warn' | 'error'; output: string; alias: Record<string, string> };
|
|
57
64
|
};
|
|
58
65
|
export function loadConfig(): Promise<LoadedConfig>;
|
package/src/lib/config.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync,readFileSync}from"node:fs";import{resolve}from"node:path";import{pathToFileURL}from"node:url";import{REPO_ROOT}from"./root.mjs";const MAIN_LINEAGE="main";const DEFAULT_CHECKS=["validate-externals","check-wired","check-routes","check-design-refs","check-skills","check-work-orders","check-cutover-ownership","check-fixtures","knowledge"];const PUBLIC_LINEAGE="public";const CONFIG_FILES=["engine.config.ts","engine.config.mts","engine.config.mjs","engine.config.js"];function defineConfig(config){return config}function lineageDefaults(name,kind){if(name===MAIN_LINEAGE){return{name,kind:"main",entry:"src/index.ts",sourceRoot:null,dist:"dist"}}if(name===PUBLIC_LINEAGE){return{name,kind:"public",entry:"src/index.public.ts",sourceRoot:"components/public/",dist:"dist-public"}}const k=kind??"page";return{name,kind:k,entry:`src/index.${name}.ts`,sourceRoot:k==="form"?`components/form/${name}/`:k==="shared"?`${name}/`:`components/project_page/${name}/`,dist:`dist-bundles/${name}`}}function normalize(raw,origin){if(!raw||typeof raw!=="object")throw new Error(`[engine] ${origin} must export a config object`);if(typeof raw.org!=="string"||!raw.org)throw new Error(`[engine] ${origin}: \`org\` (the org slug) is required`);const list=raw.lineages??[MAIN_LINEAGE];const lineages=list.map(l=>{const spec=typeof l==="string"?{name:l}:l;if(!spec?.name)throw new Error(`[engine] ${origin}: every lineage needs a name`);return{...lineageDefaults(spec.name,spec.kind),...spec}});const mainAt=lineages.findIndex(l=>l.name===MAIN_LINEAGE);if(mainAt>0)throw new Error(`[engine] ${origin}: main must be the first lineage`);if(mainAt===-1&&!lineages.every(l=>l.kind==="shared")){throw new Error(`[engine] ${origin}: lineages must start with "main", unless every lineage is shared`)}for(const l of lineages){if(l.kind==="shared"&&!/^@[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/.test(l.specifier??"")){throw new Error(`[engine] ${origin}: shared lineage "${l.name}" needs a specifier like "@stack/${l.name}"`)}}const gated=raw.checks?.gated??{};const checks={run:raw.checks?.run??DEFAULT_CHECKS,peerRepos:raw.checks?.peerRepos??[],gated:{projectTemplates:gated.projectTemplates??[],orgTemplates:gated.orgTemplates??[]}};const shared=raw.shared??[];const harness={wrapper:raw.harness?.wrapper??null,widths:raw.harness?.widths??[390,768,1280],container:raw.harness?.container??320,overflow:raw.harness?.overflow??"warn",output:raw.harness?.output??"test/visual/__output__",alias:raw.harness?.alias??{}};return{org:raw.org,lineages,checks,shared,harness}}const normalizeForTest=raw=>normalize(raw,"test");let cached;async function loadConfig(){if(cached)return cached;const file=CONFIG_FILES.map(f=>resolve(REPO_ROOT,f)).find(f=>existsSync(f));if(file){const mod=await import(pathToFileURL(file).href);cached=normalize(mod.default??mod,file);return cached}const pkg=JSON.parse(readFileSync(resolve(REPO_ROOT,"package.json"),"utf8"));let org=pkg.oaBundle?.name;const orgJson=resolve(REPO_ROOT,"template/config/org.json");if(existsSync(orgJson))org=JSON.parse(readFileSync(orgJson,"utf8")).slug??org;const lineages=(pkg.oaBundle?.lineages??[MAIN_LINEAGE]).map(n=>n==="workbooks"?{name:n,kind:"form"}:n);cached=normalize({org,lineages},"package.json oaBundle");return cached}async function lineageConfig(name){const{lineages}=await loadConfig();return lineages.find(l=>l.name===name)??lineageDefaults(name)}export{DEFAULT_CHECKS,MAIN_LINEAGE,PUBLIC_LINEAGE,defineConfig,lineageConfig,lineageDefaults,loadConfig,normalizeForTest};
|
|
1
|
+
import{existsSync,readFileSync}from"node:fs";import{resolve}from"node:path";import{pathToFileURL}from"node:url";import{REPO_ROOT}from"./root.mjs";const MAIN_LINEAGE="main";const DEFAULT_CHECKS=["validate-externals","check-wired","check-routes","check-design-refs","check-skills","check-work-orders","check-cutover-ownership","check-fixtures","check-tokens","check-css-scope","check-inline-styles","check-shadowing","check-theme","knowledge"];function budgetVerdict(name,count,budget){if(budget===void 0||budget===null)return{ok:true,message:null};if(count>budget)return{ok:false,message:`${count} finding(s), over the budget of ${budget}: the count may only fall (checks.budgets['${name}'])`};if(count<budget)return{ok:true,message:`${count} finding(s), under the budget of ${budget}: lower checks.budgets['${name}'] to ${count}`};return{ok:true,message:`${count} finding(s), at the budget`}}const PUBLIC_LINEAGE="public";const CONFIG_FILES=["engine.config.ts","engine.config.mts","engine.config.mjs","engine.config.js"];function defineConfig(config){return config}function lineageDefaults(name,kind){if(name===MAIN_LINEAGE){return{name,kind:"main",entry:"src/index.ts",sourceRoot:null,dist:"dist"}}if(name===PUBLIC_LINEAGE){return{name,kind:"public",entry:"src/index.public.ts",sourceRoot:"components/public/",dist:"dist-public"}}const k=kind??"page";return{name,kind:k,entry:`src/index.${name}.ts`,sourceRoot:k==="form"?`components/form/${name}/`:k==="shared"?`${name}/`:`components/project_page/${name}/`,dist:`dist-bundles/${name}`}}function normalize(raw,origin){if(!raw||typeof raw!=="object")throw new Error(`[engine] ${origin} must export a config object`);if(typeof raw.org!=="string"||!raw.org)throw new Error(`[engine] ${origin}: \`org\` (the org slug) is required`);const list=raw.lineages??[MAIN_LINEAGE];const lineages=list.map(l=>{const spec=typeof l==="string"?{name:l}:l;if(!spec?.name)throw new Error(`[engine] ${origin}: every lineage needs a name`);return{...lineageDefaults(spec.name,spec.kind),...spec}});const mainAt=lineages.findIndex(l=>l.name===MAIN_LINEAGE);if(mainAt>0)throw new Error(`[engine] ${origin}: main must be the first lineage`);if(mainAt===-1&&!lineages.every(l=>l.kind==="shared")){throw new Error(`[engine] ${origin}: lineages must start with "main", unless every lineage is shared`)}for(const l of lineages){if(l.kind==="shared"&&!/^@[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/.test(l.specifier??"")){throw new Error(`[engine] ${origin}: shared lineage "${l.name}" needs a specifier like "@stack/${l.name}"`)}}const gated=raw.checks?.gated??{};const checks={run:raw.checks?.run??DEFAULT_CHECKS,peerRepos:raw.checks?.peerRepos??[],gated:{projectTemplates:gated.projectTemplates??[],orgTemplates:gated.orgTemplates??[]},budgets:raw.checks?.budgets??{}};const shadows=raw.shadows??{};const shared=raw.shared??[];const harness={wrapper:raw.harness?.wrapper??null,widths:raw.harness?.widths??[390,768,1280],container:raw.harness?.container??320,overflow:raw.harness?.overflow??"warn",output:raw.harness?.output??"test/visual/__output__",alias:raw.harness?.alias??{}};return{org:raw.org,lineages,checks,shared,shadows,harness}}const normalizeForTest=raw=>normalize(raw,"test");let cached;async function loadConfig(){if(cached)return cached;const file=CONFIG_FILES.map(f=>resolve(REPO_ROOT,f)).find(f=>existsSync(f));if(file){const mod=await import(pathToFileURL(file).href);cached=normalize(mod.default??mod,file);return cached}const pkg=JSON.parse(readFileSync(resolve(REPO_ROOT,"package.json"),"utf8"));let org=pkg.oaBundle?.name;const orgJson=resolve(REPO_ROOT,"template/config/org.json");if(existsSync(orgJson))org=JSON.parse(readFileSync(orgJson,"utf8")).slug??org;const lineages=(pkg.oaBundle?.lineages??[MAIN_LINEAGE]).map(n=>n==="workbooks"?{name:n,kind:"form"}:n);cached=normalize({org,lineages},"package.json oaBundle");return cached}async function lineageConfig(name){const{lineages}=await loadConfig();return lineages.find(l=>l.name===name)??lineageDefaults(name)}export{DEFAULT_CHECKS,MAIN_LINEAGE,PUBLIC_LINEAGE,budgetVerdict,defineConfig,lineageConfig,lineageDefaults,loadConfig,normalizeForTest};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const LAYER_ORDER=["reset","tokens","renderer","core","charts","org","app","theme"];function layerFor(lineage){if(lineage.layer){if(!LAYER_ORDER.includes(lineage.layer))throw new Error(`[layers] lineage "${lineage.name}": layer "${lineage.layer}" is not one of ${LAYER_ORDER.join(", ")}`);return lineage.layer}if(lineage.kind==="shared")return LAYER_ORDER.includes(lineage.name)?lineage.name:"core";if(lineage.kind==="main")return"org";return"app"}function layerCss(postcss,css,layer){const root=postcss.parse(css);const head=[];const kept=[];const wrapped=[];root.each(node=>{if(node.type==="atrule"&&(node.name==="charset"||node.name==="import"))head.push(node);else if(node.type==="atrule"&&node.name==="layer"&&LAYER_ORDER.includes(node.params.trim())&&node.nodes)kept.push(node);else if(node.type==="comment")return;else wrapped.push(node)});const out=postcss.root();for(const n of head)out.append(n.clone());for(const n of kept)out.append(n.clone());if(wrapped.length){const rule=postcss.atRule({name:"layer",params:layer});for(const n of wrapped)rule.append(n.clone());out.append(rule)}return out.toString()}export{LAYER_ORDER,layerCss,layerFor};
|
package/src/lib/run.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{spawnSync}from"node:child_process";import{existsSync}from"node:fs";import{dirname,resolve}from"node:path";import{fileURLToPath}from"node:url";import{REPO_ROOT}from"./root.mjs";const COMMANDS_DIR=resolve(dirname(fileURLToPath(import.meta.url)),"..","commands");const TSX=import.meta.resolve("tsx/esm");const COMMANDS={build:"build.mjs",deploy:"deploy.mjs",check:"check.mjs",harness:"harness.mjs","test-visual":"test-visual.mjs",template:"template.mjs",upload:"upload-bundle.mjs",register:"register-components.mjs",cutover:"cutover.mjs",knowledge:"emit-knowledge.mjs",contracts:"emit-contracts.mjs","bundle-size":"bundle-size.mjs","check-bundle-leak":"check-bundle-leak.mjs","validate-externals":"validate-externals.mjs","check-wired":"check-wired-bindings.mjs","check-routes":"check-template-routes.mjs","check-design-refs":"check-design-refs.mjs","check-skills":"check-skills.mjs","check-work-orders":"check-work-orders.mjs","check-cutover-ownership":"check-cutover-ownership.mjs","check-fixtures":"check-fixtures.mjs","check-props-schema":"check-props-schema.mjs"};function commandFile(name){if(!COMMANDS[name])return null;const file=resolve(COMMANDS_DIR,COMMANDS[name]);const ts=file.replace(/\.mjs$/,".mts");return existsSync(file)?file:existsSync(ts)?ts:null}function runCommand(name,args=[],env={}){const file=commandFile(name);if(!file)throw new Error(`[engine] unknown command "${name}"`);const r=spawnSync(process.execPath,["--import",TSX,file,...args],{cwd:REPO_ROOT,stdio:"inherit",env:{...process.env,ENGINE_ROOT:REPO_ROOT,...env}});return r.status??1}export{COMMANDS,commandFile,runCommand};
|
|
1
|
+
import{spawnSync}from"node:child_process";import{existsSync}from"node:fs";import{dirname,resolve}from"node:path";import{fileURLToPath}from"node:url";import{REPO_ROOT}from"./root.mjs";const COMMANDS_DIR=resolve(dirname(fileURLToPath(import.meta.url)),"..","commands");const TSX=import.meta.resolve("tsx/esm");const COMMANDS={build:"build.mjs",deploy:"deploy.mjs",check:"check.mjs",harness:"harness.mjs","test-visual":"test-visual.mjs",template:"template.mjs",upload:"upload-bundle.mjs",register:"register-components.mjs",cutover:"cutover.mjs",knowledge:"emit-knowledge.mjs",contracts:"emit-contracts.mjs","bundle-size":"bundle-size.mjs","check-bundle-leak":"check-bundle-leak.mjs","validate-externals":"validate-externals.mjs","check-wired":"check-wired-bindings.mjs","check-routes":"check-template-routes.mjs","check-design-refs":"check-design-refs.mjs","check-skills":"check-skills.mjs","check-work-orders":"check-work-orders.mjs","check-cutover-ownership":"check-cutover-ownership.mjs","check-fixtures":"check-fixtures.mjs","check-props-schema":"check-props-schema.mjs","check-tokens":"check-tokens.mjs","check-css-scope":"check-css-scope.mjs","check-inline-styles":"check-inline-styles.mjs","check-shadowing":"check-shadowing.mjs","check-theme":"check-theme.mjs"};function commandFile(name){if(!COMMANDS[name])return null;const file=resolve(COMMANDS_DIR,COMMANDS[name]);const ts=file.replace(/\.mjs$/,".mts");return existsSync(file)?file:existsSync(ts)?ts:null}function runCommand(name,args=[],env={}){const file=commandFile(name);if(!file)throw new Error(`[engine] unknown command "${name}"`);const r=spawnSync(process.execPath,["--import",TSX,file,...args],{cwd:REPO_ROOT,stdio:"inherit",env:{...process.env,ENGINE_ROOT:REPO_ROOT,...env}});return r.status??1}export{COMMANDS,commandFile,runCommand};
|