UNPKG

pastoralist

Version:

A tool to watch over node module resolutions and overrides 🐑 👩🏽‍🌾

5 lines (4 loc) 6.46 kB
#!/usr/bin/env node import{program as A}from"commander";import F from"ora";import _ from"gradient-string";import{readFileSync as R,writeFileSync as S}from"fs";import{resolve as w}from"path";import N from"fast-glob";import{satisfies as I}from"compare-versions";var m=process.env.DEBUG==="true"||!1,j="\u{1F411} \u{1F469}\u{1F3FD}\u200D\u{1F33E} Pastoralist:";var{sync:D}=N,P=async e=>{let s=e?.depPaths||["**/package.json"],t=e?.path||"package.json",o=e?.root||"./",d=e?.ignore||["**/node_modules/**/node_modules/**"],i=e?.isTesting||!1,p=await b(t);if(!p){a.debug("no config found","update");return}let u=L({options:e,config:p}),n=D(s,{cwd:o,ignore:d});a.debug(`Found ${n.length} package.json files`,"update"),n.forEach(g=>a.debug(`Package: ${g}`,"update"));let r=await J(n,u),c=r?C(r):[],l=E(u,c);if(i)return r;await G({appendix:r,path:t,config:p,overrides:l})},J=async(e,s)=>{let t=$(s)||{},o=Object.keys(t);if(!(o.length>0))return;let i={};try{let p={};await Promise.all(e.map(async n=>{let r=await b(n);if(!r||!r.name)return;let c={...r.dependencies||{},...r.devDependencies||{}};p[r.name]={dependencies:c,dependents:[],filePath:n}})),Object.keys(p).forEach(n=>{let r=p[n].dependencies||{};Object.entries(r).forEach(([c,l])=>{p[c]&&p[c].dependents.push({name:n,version:l})})}),o.forEach(n=>{let r=t[n];a.debug(`Processing override: ${n}@${r}`,"constructAppendix");let c=p[n]?.dependents||[];if(a.debug(`Found ${c.length} dependents for ${n}`,"constructAppendix"),c.length>0){let l=`${n}@${r}`,g=c.reduce((f,v)=>(a.debug(`Checking dependent: ${v.name} requires ${n}@${v.version}`,"constructAppendix"),a.debug(`Adding ${v.name} as a dependent for ${n}`,"constructAppendix"),{...f,[v.name]:`${n}@${v.version}`}),{}),O=Object.keys(g).length;a.debug(`Found ${O} dependents that need override for ${n}`,"constructAppendix"),O>0&&(i[l]={dependents:g},a.debug(`Added ${l} to appendix with ${O} dependents`,"constructAppendix"))}}),(await Promise.all(e.map(async n=>await M(n,t,o)))).filter(n=>n).forEach(n=>{let r=n?.appendix||{};i={...i,...r}})}catch(p){a.error("Error constructing appendix","constructAppendix",p)}return i},C=e=>{if(!e)return[];let s=Object.keys(e);return s.length===0?[]:s.filter(t=>{let o=e[t]?.dependents;return!o||Object.keys(o).length===0}).map(t=>t.split("@")[0])},E=(e,s=[])=>{if(!e)return;let t=$(e);if(!t||Object.keys(t).length===0){a.debug("No overrides found to update","updateOverrides");return}return Object.entries(t).reduce((o,[d,i])=>(s.includes(d)||(o[d]=i),o),{})};async function G({appendix:e,path:s,config:t,overrides:o,isTesting:d=!1}){if(!(o&&Object.keys(o).length>0)){let r=["pastoralist","resolutions","overrides","pnpm"];for(let c of r)delete t[c]}if(e&&Object.keys(e).length>0&&(t.pastoralist={appendix:e}),t?.resolutions&&(t.resolutions=o),t?.overrides&&(t.overrides=o),t?.pnpm?.overrides&&(t.pnpm.overrides=o),d)return t;let u=w(s),n=JSON.stringify(t,null,2);S(u,n)}function L({config:e={}}){let s="resolveOverrides",t="Pastorlist didn't find any overrides or resolutions!",o=T(e);if(!o){a.error(t,s);return}let{type:d,overrides:i}=o;if(!(Object.keys(i)?.length>0)||!d){a.error(t,s);return}let u=Object.keys(i)||[];if(u.some(c=>typeof i[c]=="object")){a.error("Pastoralist only supports simple overrides!",s),a.error("Pastoralist is bypassing the specified complex overrides. \u{1F44C}",s);return}let r=u.reduce((c,l)=>Object.assign(c,{[l]:i[l]}),{});return d==="pnpmOverrides"?{type:"pnpm",pnpm:{overrides:r}}:d==="resolutions"?{type:"resolutions",resolutions:r}:{type:"npm",overrides:r}}var T=({overrides:e={},pnpm:s={},resolutions:t={}}={})=>{let o=s?.overrides||{},d=[{type:"overrides",overrides:e},{type:"pnpmOverrides",overrides:o},{type:"resolutions",overrides:t}].filter(({overrides:n})=>Object.keys(n).length>0),i="defineOverride";if(!(d?.length>0)){a.debug("\u{1F411} \u{1F469}\u{1F3FD}\u200D\u{1F33E} Pastoralist didn't find any overrides!",i);return}if(d?.length>1){a.error("Only 1 override object allowed",i);return}return d[0]},$=e=>{let s=e?.type;if(!s){a.error("no type found","resolveOverridesProp");return}return s==="resolutions"?e?.resolutions:s==="pnpm"?e?.pnpm?.overrides:e?.overrides};async function M(e,s,t){let o=await b(e);if(!o)return;let{name:d,dependencies:i={},devDependencies:p={}}=o,u={...i,...p};if(!Object.keys(u).some(g=>t.includes(g)))return;let c=o?.pastoralist?.appendix||{},l=U({appendix:c,overrides:s,dependencies:i,devDependencies:p,packageName:d});return{name:d,dependencies:i,devDependencies:p,appendix:l}}var U=({overrides:e={},appendix:s={},dependencies:t={},devDependencies:o={},packageName:d="",cache:i=new Map})=>{let p=Object.keys(e),u={...t,...o},n=Object.keys(u);for(let r of p){if(!n.includes(r))continue;let l=e[r],g=u[r];if(I(l,g))continue;let f=`${r}@${l}`;if(i.has(f)){s[f]=i.get(f);continue}let k={dependents:{...s?.[f]?.dependents||{},[d]:`${r}@${g}`}};s[f]=k,i.set(f,k)}return Object.keys(s).forEach(r=>{(!s[r].dependents||Object.keys(s[r].dependents).length===0)&&delete s[r]}),s};function b(e){if(y.has(e))return y.get(e);try{let s=R(e,"utf8"),t=JSON.parse(s);return y.set(e,t),t}catch(s){console.log(s),a.error(`\u{1F411} \u{1F469}\u{1F3FD}\u200D\u{1F33E} Pastoralist found invalid JSON at: ${e}`,"resolveJSON",s);return}}var h=(e,s,t)=>(o,d,...i)=>{if(!s)return;let p=d?`[${d}]`:"";console[e](`${j}[${t}]${p} ${o}`,...i)},x=({file:e,isLogging:s=!1})=>({debug:h("debug",s,e),error:h("error",s,e),info:h("info",s,e)}),a=x({file:"scripts.ts",isLogging:m}),y=new Map;async function B(e={}){let s=m||e.debug,t=x({file:"program.ts",isLogging:s}),{isTestingCLI:o=!1,...d}=e;if(o){t.debug("action:options:","action",{options:e});return}try{let i=_("green","tan"),p=F(`\u{1F469}\u{1F3FD}\u200D\u{1F33E} ${i("pastoralist")} checking herd... `).start();await P(d),p.succeed(`\u{1F469}\u{1F3FD}\u200D\u{1F33E} ${i("pastoralist")} the herd is safe!`)}catch(i){t.error("action:fn","action",{error:i}),process.exit(1)}}A.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(B).parse(process.argv);