@webority/repo-checks 0.88.0 → 0.88.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/README.md CHANGED
@@ -72,7 +72,7 @@ A repository adopting the checks records what it has today, once:
72
72
  pnpm exec webority-repo-checks --baseline-init
73
73
  ```
74
74
 
75
- That writes `.webority/repo-checks-baseline.json`: the dependency findings as
75
+ That writes `.webority/baselines/repo-checks-baseline.json`, creating the folder: the dependency findings as
76
76
  `"<app folder> | <id>"`, and the code rules counted per file:
77
77
 
78
78
  ```json
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import{chmodSync as e,existsSync as t,mkdirSync as n,readFileSync as r,readdirSync as i,writeFileSync as a}from"node:fs";import{basename as o,dirname as s,extname as c,join as l,matchesGlob as u,relative as d,resolve as f}from"node:path";import{spawnSync as ee}from"node:child_process";var p=`.webority/repo-checks-baseline.json`,te=[`raw-measure:`,`palette-read:`,`raw-colour:`,`root-font-size:`],m=class extends Error{};function ne(e,t){return`${e} | ${t}`}function h(e){if(typeof e!=`string`)throw new m(`${p} is not a valid version 2 baseline: "findings" must be a list of "<app folder> | <finding id>" strings.`);let t=e.indexOf(` | `);if(t===-1)throw new m(`${p} is not a valid version 2 baseline: "findings" must be a list of "<app folder> | <finding id>" strings.`);return[e.slice(0,t),e.slice(t+3)]}function re(e){let n=l(e,p);if(!t(n))return null;let i;try{i=JSON.parse(r(n,`utf8`))}catch{throw new m(`${p} does not parse.`)}if(typeof i!=`object`||!i||Array.isArray(i)||i.version!==2)throw new m(`${p} has no "version": 2; it is the earlier shape without counts. Delete it and run webority-repo-checks --baseline-init once from the git root, then review and commit the new file.`);for(let e of Object.keys(i))if(e!==`version`&&e!==`findings`&&e!==`counts`)throw new m(`${p} is not a valid version 2 baseline: unknown key "${e}".`);if(!Array.isArray(i.findings))throw new m(`${p} is not a valid version 2 baseline: "findings" must be a list of "<app folder> | <finding id>" strings.`);for(let e of i.findings)if(typeof e!=`string`||!e.includes(` | `))throw new m(`${p} is not a valid version 2 baseline: "findings" must be a list of "<app folder> | <finding id>" strings.`);if(i.counts===null||typeof i.counts!=`object`||Array.isArray(i.counts))throw new m(`${p} is not a valid version 2 baseline: "counts" must be an object of "<rule>:<file>" ids and whole numbers above 0.`);for(let[e,t]of Object.entries(i.counts))if(!te.some(t=>e.startsWith(t))||typeof t!=`number`||!Number.isInteger(t)||t<=0)throw new m(`${p} is not a valid version 2 baseline: count "${e}" must be a raw-measure:, palette-read:, raw-colour: or root-font-size: id with a whole number above 0.`);return{version:2,findings:i.findings,counts:i.counts}}function ie(e,t){let r=l(e,p);n(s(r),{recursive:!0});let i=[...new Set(t.findings??[])].sort(),o={};for(let e of Object.keys(t.counts??{}).sort())o[e]=t.counts[e];a(r,`${JSON.stringify({version:2,findings:i,counts:o},null,4)}\n`)}function ae(e){let t=[...new Set(e.findings??[])].sort(),n={};for(let t of Object.keys(e.counts??{}).sort()){let r=e.counts[t];typeof r==`number`&&r>0&&(n[t]=r)}return{version:2,findings:t,counts:n}}function oe(e,t){if(!t)return e;let n=(e.findings??[]).filter(e=>{let[n]=h(e);return t.pkgs?.has?.(n)??!1}),r={};for(let[n,i]of Object.entries(e.counts??{})){let e=n.indexOf(`:`),a=(e===-1?n:n.slice(e+1)).split(`\\`).join(`/`);(t.dirs??[]).some(e=>{let t=e.split(`\\`).join(`/`);return t===`.`||t===a||a.startsWith(`${t}/`)})&&(r[n]=i)}return{version:2,findings:n,counts:r}}function g(e,t){let n=new Set(e.findings??[]),r=new Set(t.findings??[]),i=[...r].filter(e=>!n.has(e)).sort(),a=[...r].filter(e=>n.has(e)).sort(),o=[...n].filter(e=>!r.has(e)).sort(),s=new Set([...Object.keys(e.counts??{}),...Object.keys(t.counts??{})]),c=[],l=[],u=[];for(let n of[...s].sort()){let r=e.counts?.[n]??0,i=t.counts?.[n]??0,a={id:n,baseline:r,current:i};i>r&&c.push(a),i>0&&i<=r&&l.push(a),r>i&&u.push(a)}return{fresh:i,known:a,stale:o,over:c,within:l,fallen:u}}function se(e,t){let n=new Set(t.findings??[]),r=[...new Set((e.findings??[]).filter(e=>n.has(e)))].sort(),i={};for(let n of Object.keys(e.counts??{}).sort()){let r=e.counts[n],a=t.counts?.[n]??0,o=Math.min(r,a);o>0&&(i[n]=o)}return{version:2,findings:r,counts:i}}var _=`# Fast only. The expensive verification is \`corepack pnpm check\`, run by hand before pushing
2
+ import{chmodSync as e,existsSync as t,mkdirSync as n,readFileSync as r,readdirSync as i,writeFileSync as a}from"node:fs";import{basename as o,dirname as s,extname as c,join as l,matchesGlob as u,relative as d,resolve as f}from"node:path";import{spawnSync as ee}from"node:child_process";var p=`.webority/baselines/repo-checks-baseline.json`,te=[`raw-measure:`,`palette-read:`,`raw-colour:`,`root-font-size:`],m=class extends Error{};function ne(e,t){return`${e} | ${t}`}function h(e){if(typeof e!=`string`)throw new m(`${p} is not a valid version 2 baseline: "findings" must be a list of "<app folder> | <finding id>" strings.`);let t=e.indexOf(` | `);if(t===-1)throw new m(`${p} is not a valid version 2 baseline: "findings" must be a list of "<app folder> | <finding id>" strings.`);return[e.slice(0,t),e.slice(t+3)]}function re(e){let n=l(e,p);if(!t(n))return null;let i;try{i=JSON.parse(r(n,`utf8`))}catch{throw new m(`${p} does not parse.`)}if(typeof i!=`object`||!i||Array.isArray(i)||i.version!==2)throw new m(`${p} has no "version": 2; it is the earlier shape without counts. Delete it and run webority-repo-checks --baseline-init once from the git root, then review and commit the new file.`);for(let e of Object.keys(i))if(e!==`version`&&e!==`findings`&&e!==`counts`)throw new m(`${p} is not a valid version 2 baseline: unknown key "${e}".`);if(!Array.isArray(i.findings))throw new m(`${p} is not a valid version 2 baseline: "findings" must be a list of "<app folder> | <finding id>" strings.`);for(let e of i.findings)if(typeof e!=`string`||!e.includes(` | `))throw new m(`${p} is not a valid version 2 baseline: "findings" must be a list of "<app folder> | <finding id>" strings.`);if(i.counts===null||typeof i.counts!=`object`||Array.isArray(i.counts))throw new m(`${p} is not a valid version 2 baseline: "counts" must be an object of "<rule>:<file>" ids and whole numbers above 0.`);for(let[e,t]of Object.entries(i.counts))if(!te.some(t=>e.startsWith(t))||typeof t!=`number`||!Number.isInteger(t)||t<=0)throw new m(`${p} is not a valid version 2 baseline: count "${e}" must be a raw-measure:, palette-read:, raw-colour: or root-font-size: id with a whole number above 0.`);return{version:2,findings:i.findings,counts:i.counts}}function ie(e,t){let r=l(e,p);n(s(r),{recursive:!0});let i=[...new Set(t.findings??[])].sort(),o={};for(let e of Object.keys(t.counts??{}).sort())o[e]=t.counts[e];a(r,`${JSON.stringify({version:2,findings:i,counts:o},null,4)}\n`)}function ae(e){let t=[...new Set(e.findings??[])].sort(),n={};for(let t of Object.keys(e.counts??{}).sort()){let r=e.counts[t];typeof r==`number`&&r>0&&(n[t]=r)}return{version:2,findings:t,counts:n}}function oe(e,t){if(!t)return e;let n=(e.findings??[]).filter(e=>{let[n]=h(e);return t.pkgs?.has?.(n)??!1}),r={};for(let[n,i]of Object.entries(e.counts??{})){let e=n.indexOf(`:`),a=(e===-1?n:n.slice(e+1)).split(`\\`).join(`/`);(t.dirs??[]).some(e=>{let t=e.split(`\\`).join(`/`);return t===`.`||t===a||a.startsWith(`${t}/`)})&&(r[n]=i)}return{version:2,findings:n,counts:r}}function g(e,t){let n=new Set(e.findings??[]),r=new Set(t.findings??[]),i=[...r].filter(e=>!n.has(e)).sort(),a=[...r].filter(e=>n.has(e)).sort(),o=[...n].filter(e=>!r.has(e)).sort(),s=new Set([...Object.keys(e.counts??{}),...Object.keys(t.counts??{})]),c=[],l=[],u=[];for(let n of[...s].sort()){let r=e.counts?.[n]??0,i=t.counts?.[n]??0,a={id:n,baseline:r,current:i};i>r&&c.push(a),i>0&&i<=r&&l.push(a),r>i&&u.push(a)}return{fresh:i,known:a,stale:o,over:c,within:l,fallen:u}}function se(e,t){let n=new Set(t.findings??[]),r=[...new Set((e.findings??[]).filter(e=>n.has(e)))].sort(),i={};for(let n of Object.keys(e.counts??{}).sort()){let r=e.counts[n],a=t.counts?.[n]??0,o=Math.min(r,a);o>0&&(i[n]=o)}return{version:2,findings:r,counts:i}}var _=`# Fast only. The expensive verification is \`corepack pnpm check\`, run by hand before pushing
3
3
  # (CLAUDE.md -> Before pushing). A slow pre-commit gets bypassed within a day, and a hook people bypass is worse than none because
4
4
  # it manufactures confidence without providing it.
5
5
  #
@@ -69,4 +69,4 @@ fi
69
69
  `&&n.push(t.length),l===`\\`?(t.push(e[r+1]),r+=2):(l===`"`&&(a=!1),r++);continue}if(o){t.push(l),l===`
70
70
  `&&n.push(t.length),l===`\\`?(t.push(e[r+1]),r+=2):(l==="`"&&(o=!1),r++);continue}if(l===`/`&&u===`/`){s=!0,t.push(` `,` `),r+=2;continue}if(l===`/`&&u===`*`){c=!0,t.push(` `,` `),r+=2;continue}if(l===`'`){i=!0,t.push(l),r++;continue}if(l===`"`){a=!0,t.push(l),r++;continue}if(l==="`"){o=!0,t.push(l),r++;continue}t.push(l),l===`
71
71
  `&&n.push(t.length),r++}return{blanked:t.join(``),lineStarts:n}}function L(e,t){let n=0,r=t.length-1;for(;n<=r;){let i=n+r>>1;t[i]<=e?n=i+1:r=i-1}return r+1}function R(e,t){let n=e.split(`
72
- `);return t>=1&&t<=n.length?n[t-1].trim().slice(0,160):``}function Ht(e,t,n,r){let i;for(;(i=Dt.exec(e))!==null;){let a=i.index,o=a-1;for(;o>=0&&/\s/.test(e[o]);)o--;if(o<0||e[o]!==`{`&&e[o]!==`,`)continue;let s=i[1],c=i[3],l=s||c,u=!!c,d=i.index+i[0].length-1+1,f=Wt(e,d);if(f!==-1&&Kt(e.slice(d,f),u?l:Gt(l))){let e=L(a,t);r.push({rule:`raw-measure`,line:e,text:R(n,e),hint:null})}}}function Ut(e,t,n,r){Mt.lastIndex=0;let i;for(;(i=Mt.exec(e))!==null;){let a=i.index,o=a-1;for(;o>=0&&/\s/.test(e[o]);)o--;if(o<0||e[o]!==`{`&&e[o]!==`,`)continue;let s=i.index+i[0].length-1+1,c=Wt(e,s);if(c===-1||!qt(e.slice(s,c)).some(({content:e})=>xt(e)))continue;let l=L(a,t);r.push({rule:`raw-colour`,line:l,text:R(n,l),hint:`read a meaning: theme.colors["<name>"] on phone, var(--color-<name>) on web; a product-only colour goes into theme/brand.tokens.json`})}}function Wt(e,t){let n=0,r=t,i=!1,a=!1,o=!1;for(;r<e.length;){let t=e[r];if(i){t===`\\`?r+=2:(t===`'`&&(i=!1),r++);continue}if(a){t===`\\`?r+=2:(t===`"`&&(a=!1),r++);continue}if(o){t===`\\`?r+=2:(t==="`"&&(o=!1),r++);continue}if(t===`'`){i=!0,r++;continue}if(t===`"`){a=!0,r++;continue}if(t==="`"){o=!0,r++;continue}if(t===`(`||t===`[`||t===`{`){n++,r++;continue}if(t===`)`||t===`]`||t===`}`){if(n===0)return r;n--,r++;continue}if(n===0&&(t===`,`||t===`}`||t===`)`||t===`]`||t===`;`))return r;r++}return-1}function Gt(e){return e.replace(/([A-Z])/g,(e,t)=>`-`+t.toLowerCase())}function Kt(e,t){let n=qt(e);for(let{content:e}of n)if(bt(t,e))return!0;let r=e;for(let{start:e,end:t}of n)r=r.slice(0,e)+`""`+r.slice(t);Rt.lastIndex=0;let i;for(;(i=Rt.exec(r))!==null;){if(parseFloat(i[0])===0)continue;let e=i.index-1;for(;e>=0&&/\s/.test(r[e]);)e--;if(e>=0&&`[*/`.includes(r[e]))continue;let t=i.index+i[0].length;for(;t<r.length&&/\s/.test(r[t]);)t++;if(!(t<r.length&&`*/`.includes(r[t])))return!0}return!1}function qt(e){let t=[],n=0,r=!1,i=!1,a=!1,o=-1;for(;n<e.length;){let s=e[n];if(r){s===`\\`?n+=2:s===`'`?(r=!1,t.push({start:o,end:n+1,content:e.slice(o+1,n)}),n++):n++;continue}if(i){s===`\\`?n+=2:s===`"`?(i=!1,t.push({start:o,end:n+1,content:e.slice(o+1,n)}),n++):n++;continue}if(a){if(s===`\\`)n+=2;else if(s==="`"){a=!1;let r=e.slice(o+1,n);r=r.replace(/\$\{[^}]*\}/g,``),t.push({start:o,end:n+1,content:r}),n++}else s===`$`&&e[n+1]===`{`?n+=2:n++;continue}if(s===`'`){r=!0,o=n,n++;continue}if(s===`"`){i=!0,o=n,n++;continue}if(s==="`"){a=!0,o=n,n++;continue}n++}return t}function Jt(e,t,n,r){let i;for(;(i=It.exec(e))!==null;){let e=i[1];if($e.has(e)){let a=L(i.index,t);r.push({rule:`palette-read`,line:a,text:R(n,a),hint:P(e,`web`)})}}}function Yt(e,t,n,r){let i;for(;(i=Ft.exec(e))!==null;){let e=i[1],a=L(i.index,t);r.push({rule:`palette-read`,line:a,text:R(n,a),hint:F(e)})}}function Xt(e,t,n,r){let i;for(;(i=Lt.exec(e))!==null;){let a=``,o=e.slice(i.index+i[0].length);if(i[0].endsWith(`[`)){let e=o.match(/^\s*(["'])([^"']*)\1/);e&&(a=e[2])}else{let e=o.match(/^\s*([\w$]+)/);e&&(a=e[1])}let s=L(i.index,t);r.push({rule:`palette-read`,line:s,text:R(n,s),hint:P(a,`phone`)})}}function Zt(e){let t=o(e);return!!(e.split(`/`).slice(0,-1).some(e=>He.includes(e))||Ue.some(t=>e.toLowerCase().includes(t))||t.includes(`.generated.`)||t.endsWith(`.min.css`)||/\.(?:test|spec)\.[cm]?[jt]sx?$/i.test(t))}function Qt(e,n=null){let i=n?n.map(t=>C(e,t)):null,a=T(e,Object.keys(Be).map(e=>`*${e}`)).filter(n=>t(l(e,n))&&!Zt(n)&&(!i||i.some(e=>e===`.`||n===e||n.startsWith(`${e}/`)))),o=[];for(let t of a){let n=c(t).toLowerCase(),i=Be[n],a=r(l(e,t),`utf8`),s=[];if(i===`scss`||i===`css`||i===`markup`)s=yt(a,i);else if(i===`script`){let e=zt(a);if(Ve.includes(n)&&!e)continue;s=Bt(a,{phone:e})}for(let e of s)o.push({id:`${e.rule}:${t}`,line:e.line,text:e.text,hint:e.hint})}return o.sort((e,t)=>e.id<t.id?-1:e.id>t.id?1:e.line-t.line)}function $t(e){let t={};for(let n of e)t[n.id]=(t[n.id]||0)+1;return t}var en=process.argv.slice(2),z=new Set(en.filter(e=>e.startsWith(`--`))),B=en.filter(e=>!e.startsWith(`--`)),tn=z.has(`--sync-hook`),V=e=>process.stdout.write(`${e}\n`),H=e=>process.stderr.write(`${e}\n`),nn=new Set([`--json`,`--baseline-init`,`--baseline-shrink`,`--sync-hook`]),rn=[...z].filter(e=>!nn.has(e));(rn.length||z.has(`--baseline-init`)&&z.has(`--baseline-shrink`)||tn&&(z.size>1||B.length))&&(H(`repo-checks: unknown or conflicting options ${rn.join(` `)}`.trim()),H(`usage: webority-repo-checks [--json] [--baseline-init | --baseline-shrink] [app folder ...]`),H(` webority-repo-checks --sync-hook`),process.exit(2));var U=be(process.cwd());if(U||(H(`repo-checks: not inside a git repository.`),process.exit(2)),tn){let t=l(U,O);n(s(t),{recursive:!0}),a(t,_),e(t,493),V(`repo-checks: wrote ${t}`),process.exit(0)}var W=Pe(U,B.length?B.map(e=>f(process.cwd(),e)):null).sort((e,t)=>e.pkg===t.pkg?0:e.pkg<t.pkg?-1:1),G=Qt(U,B.length?B.map(e=>f(process.cwd(),e)):null),K={findings:W.map(e=>ne(e.pkg,e.id)),counts:$t(G)},an=new Map(W.map(e=>[ne(e.pkg,e.id),e]));if(z.has(`--baseline-init`)){B.length&&(H(`repo-checks: --baseline-init reads the whole repository; run it without folder arguments.`),process.exit(2)),t(l(U,`.webority/repo-checks-baseline.json`))&&(H(`repo-checks: ${p} already exists. It only shrinks: fix a new finding, or run --baseline-shrink after fixing old ones.`),process.exit(2)),ie(U,ae(K));let e=Object.keys(K.counts).length;V(`repo-checks: recorded ${W.length} finding(s) and ${e} counted file(s) (${G.length} code finding(s)) in ${p}.`),process.exit(0)}var q=(()=>{try{return re(U)}catch(e){throw e instanceof m&&(H(`repo-checks: ${e.message}`),process.exit(2)),e}})();if(z.has(`--baseline-shrink`)){B.length&&(H(`repo-checks: --baseline-shrink reads the whole repository; run it without folder arguments.`),process.exit(2)),q===null&&(H(`repo-checks: no ${p}; run webority-repo-checks --baseline-init first.`),process.exit(2));let e=g(q,K);ie(U,se(q,K)),V(`repo-checks: dropped ${e.stale.length} fixed finding(s) and lowered ${e.fallen.length} count(s) in ${p}.`),process.exit(0)}var J=new Set(B.map(e=>d(U,f(process.cwd(),e)).split(`\\`).join(`/`)||`.`)),on=B.length?{pkgs:J,dirs:[...J]}:null,Y=g(oe(q??{version:2,findings:[],counts:{}},on),K),sn={"raw-measure":We,"palette-read":qe,"raw-colour":Ge,"root-font-size":Ke},cn=e=>sn[e.slice(0,e.indexOf(`:`))],X=Y.fresh.map(e=>an.get(e)),Z=Y.known.map(e=>an.get(e)),Q=Y.over.map(e=>({pkg:`.`,id:e.id,message:`${e.current} found, ${e.baseline} recorded. ${cn(e.id)}`,lines:G.filter(t=>t.id===e.id).map(({line:e,text:t,hint:n})=>({line:e,text:t,hint:n}))})),$=Y.within.map(e=>({pkg:`.`,id:e.id,message:`${e.current} found, ${e.baseline} recorded.`})),ln=[...Y.stale.map(h),...Y.fallen.map(e=>[`.`,e.id])];if(z.has(`--json`)&&(V(JSON.stringify({root:U,new:[...X,...Q],known:[...Z,...$],stale:ln},null,2)),process.exit(+!![...X,...Q].length)),X.length||Q.length){if(X.length){H(`repo-checks: ${X.length} new finding(s)`);let e=null;for(let t of X)t.pkg!==e&&(e=t.pkg,H(` ${e}`)),H(` - ${t.id}: ${t.message}`)}if(Q.length){H(`repo-checks: ${Q.length} file(s) above their recorded count`);for(let e of Q){H(` ${e.id}: ${e.message}`);for(let t of e.lines)H(` ${t.line}: ${t.text}`),t.hint!==null&&H(` ${t.hint}`)}}process.exit(1)}if(V(`repo-checks: no new findings.`),Z.length||$.length){let e=Y.within.reduce((e,t)=>e+t.current,0);V(`repo-checks: ${Z.length} known finding(s) and ${e} counted code finding(s) in ${$.length} file(s) recorded in ${p}.`)}if(Y.stale.length){V(`repo-checks: ${Y.stale.length} baseline entr${Y.stale.length===1?`y is`:`ies are`} fixed; drop them with webority-repo-checks --baseline-shrink:`);for(let e of Y.stale.map(h))V(` ${e[0]} ${e[1]}`)}if(Y.fallen.length){V(`repo-checks: ${Y.fallen.length} count(s) fell; record them with webority-repo-checks --baseline-shrink:`);for(let e of Y.fallen)V(` ${e.id} ${e.baseline} -> ${e.current}`)}process.exit(0);export{};
72
+ `);return t>=1&&t<=n.length?n[t-1].trim().slice(0,160):``}function Ht(e,t,n,r){let i;for(;(i=Dt.exec(e))!==null;){let a=i.index,o=a-1;for(;o>=0&&/\s/.test(e[o]);)o--;if(o<0||e[o]!==`{`&&e[o]!==`,`)continue;let s=i[1],c=i[3],l=s||c,u=!!c,d=i.index+i[0].length-1+1,f=Wt(e,d);if(f!==-1&&Kt(e.slice(d,f),u?l:Gt(l))){let e=L(a,t);r.push({rule:`raw-measure`,line:e,text:R(n,e),hint:null})}}}function Ut(e,t,n,r){Mt.lastIndex=0;let i;for(;(i=Mt.exec(e))!==null;){let a=i.index,o=a-1;for(;o>=0&&/\s/.test(e[o]);)o--;if(o<0||e[o]!==`{`&&e[o]!==`,`)continue;let s=i.index+i[0].length-1+1,c=Wt(e,s);if(c===-1||!qt(e.slice(s,c)).some(({content:e})=>xt(e)))continue;let l=L(a,t);r.push({rule:`raw-colour`,line:l,text:R(n,l),hint:`read a meaning: theme.colors["<name>"] on phone, var(--color-<name>) on web; a product-only colour goes into theme/brand.tokens.json`})}}function Wt(e,t){let n=0,r=t,i=!1,a=!1,o=!1;for(;r<e.length;){let t=e[r];if(i){t===`\\`?r+=2:(t===`'`&&(i=!1),r++);continue}if(a){t===`\\`?r+=2:(t===`"`&&(a=!1),r++);continue}if(o){t===`\\`?r+=2:(t==="`"&&(o=!1),r++);continue}if(t===`'`){i=!0,r++;continue}if(t===`"`){a=!0,r++;continue}if(t==="`"){o=!0,r++;continue}if(t===`(`||t===`[`||t===`{`){n++,r++;continue}if(t===`)`||t===`]`||t===`}`){if(n===0)return r;n--,r++;continue}if(n===0&&(t===`,`||t===`}`||t===`)`||t===`]`||t===`;`))return r;r++}return-1}function Gt(e){return e.replace(/([A-Z])/g,(e,t)=>`-`+t.toLowerCase())}function Kt(e,t){let n=qt(e);for(let{content:e}of n)if(bt(t,e))return!0;let r=e;for(let{start:e,end:t}of n)r=r.slice(0,e)+`""`+r.slice(t);Rt.lastIndex=0;let i;for(;(i=Rt.exec(r))!==null;){if(parseFloat(i[0])===0)continue;let e=i.index-1;for(;e>=0&&/\s/.test(r[e]);)e--;if(e>=0&&`[*/`.includes(r[e]))continue;let t=i.index+i[0].length;for(;t<r.length&&/\s/.test(r[t]);)t++;if(!(t<r.length&&`*/`.includes(r[t])))return!0}return!1}function qt(e){let t=[],n=0,r=!1,i=!1,a=!1,o=-1;for(;n<e.length;){let s=e[n];if(r){s===`\\`?n+=2:s===`'`?(r=!1,t.push({start:o,end:n+1,content:e.slice(o+1,n)}),n++):n++;continue}if(i){s===`\\`?n+=2:s===`"`?(i=!1,t.push({start:o,end:n+1,content:e.slice(o+1,n)}),n++):n++;continue}if(a){if(s===`\\`)n+=2;else if(s==="`"){a=!1;let r=e.slice(o+1,n);r=r.replace(/\$\{[^}]*\}/g,``),t.push({start:o,end:n+1,content:r}),n++}else s===`$`&&e[n+1]===`{`?n+=2:n++;continue}if(s===`'`){r=!0,o=n,n++;continue}if(s===`"`){i=!0,o=n,n++;continue}if(s==="`"){a=!0,o=n,n++;continue}n++}return t}function Jt(e,t,n,r){let i;for(;(i=It.exec(e))!==null;){let e=i[1];if($e.has(e)){let a=L(i.index,t);r.push({rule:`palette-read`,line:a,text:R(n,a),hint:P(e,`web`)})}}}function Yt(e,t,n,r){let i;for(;(i=Ft.exec(e))!==null;){let e=i[1],a=L(i.index,t);r.push({rule:`palette-read`,line:a,text:R(n,a),hint:F(e)})}}function Xt(e,t,n,r){let i;for(;(i=Lt.exec(e))!==null;){let a=``,o=e.slice(i.index+i[0].length);if(i[0].endsWith(`[`)){let e=o.match(/^\s*(["'])([^"']*)\1/);e&&(a=e[2])}else{let e=o.match(/^\s*([\w$]+)/);e&&(a=e[1])}let s=L(i.index,t);r.push({rule:`palette-read`,line:s,text:R(n,s),hint:P(a,`phone`)})}}function Zt(e){let t=o(e);return!!(e.split(`/`).slice(0,-1).some(e=>He.includes(e))||Ue.some(t=>e.toLowerCase().includes(t))||t.includes(`.generated.`)||t.endsWith(`.min.css`)||/\.(?:test|spec)\.[cm]?[jt]sx?$/i.test(t))}function Qt(e,n=null){let i=n?n.map(t=>C(e,t)):null,a=T(e,Object.keys(Be).map(e=>`*${e}`)).filter(n=>t(l(e,n))&&!Zt(n)&&(!i||i.some(e=>e===`.`||n===e||n.startsWith(`${e}/`)))),o=[];for(let t of a){let n=c(t).toLowerCase(),i=Be[n],a=r(l(e,t),`utf8`),s=[];if(i===`scss`||i===`css`||i===`markup`)s=yt(a,i);else if(i===`script`){let e=zt(a);if(Ve.includes(n)&&!e)continue;s=Bt(a,{phone:e})}for(let e of s)o.push({id:`${e.rule}:${t}`,line:e.line,text:e.text,hint:e.hint})}return o.sort((e,t)=>e.id<t.id?-1:e.id>t.id?1:e.line-t.line)}function $t(e){let t={};for(let n of e)t[n.id]=(t[n.id]||0)+1;return t}var en=process.argv.slice(2),z=new Set(en.filter(e=>e.startsWith(`--`))),B=en.filter(e=>!e.startsWith(`--`)),tn=z.has(`--sync-hook`),V=e=>process.stdout.write(`${e}\n`),H=e=>process.stderr.write(`${e}\n`),nn=new Set([`--json`,`--baseline-init`,`--baseline-shrink`,`--sync-hook`]),rn=[...z].filter(e=>!nn.has(e));(rn.length||z.has(`--baseline-init`)&&z.has(`--baseline-shrink`)||tn&&(z.size>1||B.length))&&(H(`repo-checks: unknown or conflicting options ${rn.join(` `)}`.trim()),H(`usage: webority-repo-checks [--json] [--baseline-init | --baseline-shrink] [app folder ...]`),H(` webority-repo-checks --sync-hook`),process.exit(2));var U=be(process.cwd());if(U||(H(`repo-checks: not inside a git repository.`),process.exit(2)),tn){let t=l(U,O);n(s(t),{recursive:!0}),a(t,_),e(t,493),V(`repo-checks: wrote ${t}`),process.exit(0)}var W=Pe(U,B.length?B.map(e=>f(process.cwd(),e)):null).sort((e,t)=>e.pkg===t.pkg?0:e.pkg<t.pkg?-1:1),G=Qt(U,B.length?B.map(e=>f(process.cwd(),e)):null),K={findings:W.map(e=>ne(e.pkg,e.id)),counts:$t(G)},an=new Map(W.map(e=>[ne(e.pkg,e.id),e]));if(z.has(`--baseline-init`)){B.length&&(H(`repo-checks: --baseline-init reads the whole repository; run it without folder arguments.`),process.exit(2)),t(l(U,`.webority/baselines/repo-checks-baseline.json`))&&(H(`repo-checks: ${p} already exists. It only shrinks: fix a new finding, or run --baseline-shrink after fixing old ones.`),process.exit(2)),ie(U,ae(K));let e=Object.keys(K.counts).length;V(`repo-checks: recorded ${W.length} finding(s) and ${e} counted file(s) (${G.length} code finding(s)) in ${p}.`),process.exit(0)}var q=(()=>{try{return re(U)}catch(e){throw e instanceof m&&(H(`repo-checks: ${e.message}`),process.exit(2)),e}})();if(z.has(`--baseline-shrink`)){B.length&&(H(`repo-checks: --baseline-shrink reads the whole repository; run it without folder arguments.`),process.exit(2)),q===null&&(H(`repo-checks: no ${p}; run webority-repo-checks --baseline-init first.`),process.exit(2));let e=g(q,K);ie(U,se(q,K)),V(`repo-checks: dropped ${e.stale.length} fixed finding(s) and lowered ${e.fallen.length} count(s) in ${p}.`),process.exit(0)}var J=new Set(B.map(e=>d(U,f(process.cwd(),e)).split(`\\`).join(`/`)||`.`)),on=B.length?{pkgs:J,dirs:[...J]}:null,Y=g(oe(q??{version:2,findings:[],counts:{}},on),K),sn={"raw-measure":We,"palette-read":qe,"raw-colour":Ge,"root-font-size":Ke},cn=e=>sn[e.slice(0,e.indexOf(`:`))],X=Y.fresh.map(e=>an.get(e)),Z=Y.known.map(e=>an.get(e)),Q=Y.over.map(e=>({pkg:`.`,id:e.id,message:`${e.current} found, ${e.baseline} recorded. ${cn(e.id)}`,lines:G.filter(t=>t.id===e.id).map(({line:e,text:t,hint:n})=>({line:e,text:t,hint:n}))})),$=Y.within.map(e=>({pkg:`.`,id:e.id,message:`${e.current} found, ${e.baseline} recorded.`})),ln=[...Y.stale.map(h),...Y.fallen.map(e=>[`.`,e.id])];if(z.has(`--json`)&&(V(JSON.stringify({root:U,new:[...X,...Q],known:[...Z,...$],stale:ln},null,2)),process.exit(+!![...X,...Q].length)),X.length||Q.length){if(X.length){H(`repo-checks: ${X.length} new finding(s)`);let e=null;for(let t of X)t.pkg!==e&&(e=t.pkg,H(` ${e}`)),H(` - ${t.id}: ${t.message}`)}if(Q.length){H(`repo-checks: ${Q.length} file(s) above their recorded count`);for(let e of Q){H(` ${e.id}: ${e.message}`);for(let t of e.lines)H(` ${t.line}: ${t.text}`),t.hint!==null&&H(` ${t.hint}`)}}process.exit(1)}if(V(`repo-checks: no new findings.`),Z.length||$.length){let e=Y.within.reduce((e,t)=>e+t.current,0);V(`repo-checks: ${Z.length} known finding(s) and ${e} counted code finding(s) in ${$.length} file(s) recorded in ${p}.`)}if(Y.stale.length){V(`repo-checks: ${Y.stale.length} baseline entr${Y.stale.length===1?`y is`:`ies are`} fixed; drop them with webority-repo-checks --baseline-shrink:`);for(let e of Y.stale.map(h))V(` ${e[0]} ${e[1]}`)}if(Y.fallen.length){V(`repo-checks: ${Y.fallen.length} count(s) fell; record them with webority-repo-checks --baseline-shrink:`);for(let e of Y.fallen)V(` ${e.id} ${e.baseline} -> ${e.current}`)}process.exit(0);export{};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webority/repo-checks",
3
- "version": "0.88.0",
3
+ "version": "0.88.1",
4
4
  "description": "Repository checks for Webority React web portals, React Native apps and Razor sites: banned and required dependencies, pnpm only, exact pins, the shared Biome preset, and token use in style, markup and component code. Run before a push; existing findings and per-file counts sit in a baseline that only shrinks.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",