pastoralist
Version:
A tool to watch over node module resolutions and overrides 🐑 👩🏽🌾
9 lines (8 loc) • 8.71 kB
JavaScript
;var B=Object.create;var R=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var V=Object.getPrototypeOf,X=Object.prototype.hasOwnProperty;var q=(e,t,s,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of W(t))!X.call(e,n)&&n!==s&&R(e,n,{get:()=>t[n],enumerable:!(r=_(t,n))||r.enumerable});return e};var N=(e,t,s)=>(s=e!=null?B(V(e)):{},q(t||!e||!e.__esModule?R(s,"default",{value:e,enumerable:!0}):s,e));var w=require("commander"),G=N(require("ora"),1),M=N(require("gradient-string"),1);var O=require("fs"),I=require("path"),L=N(require("fast-glob"),1);var v=process.env.DEBUG==="true"||!1,D="\u{1F411} \u{1F469}\u{1F3FD}\u200D\u{1F33E} Pastoralist:";var{writeFile:z}=O.promises,{sync:E}=L.default,C=async e=>{let t=e?.path||"package.json",s=e?.root||"./",r=e?.isTesting||!1,n=v||e?.debug||!1,o=P({file:"scripts.ts",isLogging:n}),i=await U(t);if(!i){o.debug("no config found","update");return}let d=ee(s),p=Object.keys(d);p.length>0&&o.debug(`Found patches for packages: ${p.join(", ")}`,"update");let u=K({options:e,config:i}),a=J(u);if(!a||Object.keys(a).length===0){o.debug("No overrides found","update"),await F({appendix:{},path:t,config:i,overrides:{}});return}let g={};if(e?.depPaths&&e?.depPaths.length>0){o.debug(`Using depPaths to find package.json files: ${e.depPaths.join(", ")}`,"update");let l=Z(e.depPaths,e.ignore||[],s,o);l.length>0?(o.debug(`Processing ${l.length} package.json files from depPaths`,"update"),g=await ne(l,u,o)):o.debug("No package.json files found matching depPaths","update")}else{let{dependencies:l={},devDependencies:x={},peerDependencies:k={}}=i;g=await T({overrides:a,dependencies:l,devDependencies:x,peerDependencies:k,packageName:i.name||"root"})}for(let l of Object.keys(g)){let x=l.split("@")[0],k=te(x,d);k.length>0&&(g[l].patches=k)}let{dependencies:y={},devDependencies:m={},peerDependencies:f={}}=i,j={...y,...m,...f},b=se(d,j);b.length>0&&(o.info(`Found ${b.length} potentially unused patch files:`,"update"),b.forEach(l=>o.info(` - ${l}`,"update")),o.info("Consider removing these patches if the packages are no longer used.","update"));let h=re(a,j),A=a;h.length>0&&(o.debug(`Found ${h.length} packages to remove from overrides: ${h.join(", ")}`,"update"),A=H(u,h)||a),!r&&await F({appendix:g,path:t,config:i,overrides:A})};var H=(e,t=[])=>{if(!e)return;let s=J(e);if(!s||Object.keys(s).length===0){c.debug("No overrides found to update","updateOverrides");return}return Object.entries(s).reduce((r,[n,o])=>(t.includes(n)||(r[n]=o),r),{})};async function F({appendix:e,path:t,config:s,overrides:r,isTesting:n=!1}){let o=r&&Object.keys(r).length>0,i=e&&Object.keys(e).length>0;if(v&&c.debug(`Processing package.json update:
hasOverrides=${o}, hasAppendix=${i}
Current config:
${JSON.stringify(s,null,2)}`,"updatePackageJSON"),!o&&!i){let u=["pastoralist","resolutions","overrides","pnpm"];for(let a of u)a==="pnpm"&&s.pnpm?(delete s.pnpm.overrides,Object.keys(s.pnpm).length===0&&delete s[a]):delete s[a]}else i&&(s.pastoralist={appendix:e});if(s?.resolutions&&(s.resolutions=r),s?.overrides&&(s.overrides=r),s?.pnpm?.overrides&&(s.pnpm.overrides=r),n)return s;let d=(0,I.resolve)(t),p=JSON.stringify(s,null,2);v&&c.debug(`Writing updated package.json:
${p}`,"updatePackageJSON"),(0,O.writeFileSync)(d,p)}function K({config:e={}}){let t="resolveOverrides",s=Q(e);if(!s){c.debug("No overrides configuration found",t);return}let{type:r,overrides:n}=s;if(!(Object.keys(n)?.length>0)||!r){c.debug("No active overrides found",t);return}let i=Object.keys(n)||[];if(i.some(u=>typeof n[u]=="object")){c.error("Pastoralist only supports simple overrides!",t),c.error("Pastoralist is bypassing the specified complex overrides. \u{1F44C}",t);return}let p=i.reduce((u,a)=>Object.assign(u,{[a]:n[a]}),{});return r==="pnpmOverrides"?{type:"pnpm",pnpm:{overrides:p}}:r==="resolutions"?{type:"resolutions",resolutions:p}:{type:"npm",overrides:p}}var Q=({overrides:e={},pnpm:t={},resolutions:s={}}={})=>{let r=t?.overrides||{},n=Object.keys(e).length>0,o=Object.keys(r).length>0,i=Object.keys(s).length>0;if(!n&&!o&&!i)return;let d=[{type:"overrides",overrides:e},{type:"pnpmOverrides",overrides:r},{type:"resolutions",overrides:s}].filter(({overrides:a})=>Object.keys(a).length>0),p="defineOverride";if(d?.length>1){c.error("Only 1 override object allowed",p);return}return d[0]},J=e=>{let t=e?.type;if(!t){c.error("no type found","resolveOverridesProp");return}return t==="resolutions"?e?.resolutions:t==="pnpm"?e?.pnpm?.overrides:e?.overrides};async function Y(e,t,s){let r=await U(e);if(!r)return;let{name:n,dependencies:o={},devDependencies:i={},peerDependencies:d={}}=r,p={...o,...i,...d};if(!Object.keys(p).some(m=>s.includes(m)))return;let g=T({overrides:t,dependencies:o,devDependencies:i,peerDependencies:d,packageName:n});return g&&Object.keys(g).length>0&&(r.pastoralist={appendix:g},await z(e,JSON.stringify(r,null,2))),{name:n,dependencies:o,devDependencies:i,appendix:g}}var T=({overrides:e={},appendix:t={},dependencies:s={},devDependencies:r={},peerDependencies:n={},packageName:o="",cache:i=new Map})=>{let d=Object.keys(e),p={...s,...r,...n},u=Object.keys(p);for(let a of d){if(!u.includes(a))continue;let y=e[a],m=p[a],f=`${a}@${y}`;if(i.has(f)){t[f]=i.get(f);continue}let h={dependents:{...t?.[f]?.dependents||{},[o]:`${a}@${m}`}};t[f]=h,i.set(f,h)}return Object.keys(t).forEach(a=>{(!t[a].dependents||Object.keys(t[a].dependents).length===0)&&delete t[a]}),t};function U(e){if(S.has(e))return S.get(e);try{let t=(0,O.readFileSync)(e,"utf8"),s=JSON.parse(t);return S.set(e,s),s}catch(t){c.error(`\u{1F411} \u{1F469}\u{1F3FD}\u200D\u{1F33E} Pastoralist found invalid JSON at:
${e}`,"resolveJSON",t);return}}var $=(e,t,s)=>(r,n,...o)=>{if(!t)return;let i=n?`[${n}]`:"";console[e](`${D}[${s}]${i} ${r}`,...o)},P=({file:e,isLogging:t=!1})=>({debug:$("debug",t,e),error:$("error",t,e),info:$("info",t,e)}),c=P({file:"scripts.ts",isLogging:v}),S=new Map,Z=(e=[],t=[],s="./",r=c)=>{if(e.length===0)return r.debug("No depPaths provided","findPackageJsonFiles"),[];try{r.debug(`Searching with patterns: ${e.join(", ")}, ignoring: ${t.join(", ")}`,"findPackageJsonFiles");let n=E(e,{cwd:s,ignore:t,absolute:!1,onlyFiles:!0});return r.debug(`Found ${n.length} files`,"findPackageJsonFiles"),n}catch(n){return r.error("Error finding package.json files","findPackageJsonFiles",n),[]}},ee=(e="./")=>{let t=["patches/*.patch",".patches/*.patch","*.patch","patches/**/*.patch"];try{let s=E(t,{cwd:e}),r={};return s.forEach(n=>{let o=n.split("/").pop()||"";if(!o.endsWith(".patch"))return;let i=o.replace(".patch",""),d;if(!i.includes("+"))d=i;else{let p=i.split("+");i.startsWith("@")&&p.length>=2?d=`${p[0]}/${p[1]}`:d=p[0]}d&&(r[d]||(r[d]=[]),r[d].push(n),c.debug(`Found patch for ${d}: ${n}`,"detectPatches"))}),r}catch(s){return c.error("Error detecting patches","detectPatches",s),{}}},te=(e,t)=>t[e]||[],se=(e,t)=>{let s=[];return Object.entries(e).forEach(([r,n])=>{t[r]||(s.push(...n),c.debug(`Found unused patches for ${r}: ${n.join(", ")}`,"findUnusedPatches"))}),s},re=(e={},t={})=>{let s=[];return Object.keys(e).forEach(r=>{t[r]||(s.push(r),c.debug(`Found unused override for ${r}: no longer in dependencies`,"findUnusedOverrides"))}),s};async function ne(e,t,s=c){if(!t)return s.debug("No overrides data provided","constructAppendix"),{};let r=J(t);if(!r||Object.keys(r).length===0)return s.debug("No overrides found","constructAppendix"),{};let n=Object.keys(r),o={};for(let i of e){let d=await Y(i,r,n);if(d?.appendix)for(let[p,u]of Object.entries(d.appendix))o[p]||(o[p]={dependents:{}}),o[p].dependents={...o[p].dependents,...u.dependents}}return o}async function oe(e={}){let t=v||e.debug,s=P({file:"program.ts",isLogging:t}),{isTestingCLI:r=!1,...n}=e;if(r){s.debug("action:options:","action",{options:e});return}try{let o=(0,M.default)("green","tan"),i=(0,G.default)(`\u{1F469}\u{1F3FD}\u200D\u{1F33E} ${o("pastoralist")} checking herd...
`).start();await C(n),i.succeed(`\u{1F469}\u{1F3FD}\u200D\u{1F33E} ${o("pastoralist")} the herd is safe!`)}catch(o){s.error("action:fn","action",{error:o}),process.exit(1)}}w.program.description("Pastoralist, a utility CLI to manage your dependency overrides").option("--debug","enables debug mode").option("-p, --path <path>","specifies a path to a package.json").option("-d, --depPaths [depPaths...]","specifies a glob path to a package.jsons").option("--ignore [ignore...]","specifies a glob path to ignore").option("-r, --root <root>","specifies a root path").option("-t, --isTestingCLI","enables CLI testing, no scripts are run").option("--isTesting","enables testing, no scripts are run").action(oe).parse(process.argv);