pastoralist 1.2.0 → 1.3.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/README.md CHANGED
@@ -126,6 +126,31 @@ pastoralist
126
126
  }
127
127
  ```
128
128
 
129
+ ---
130
+
131
+ ## Testing
132
+
133
+ ### Unit Tests
134
+
135
+ ```bash
136
+ pnpm test
137
+ ```
138
+
139
+ ### End-to-End Tests
140
+
141
+ Run comprehensive e2e tests using Docker to verify real-world scenarios:
142
+
143
+ ```bash
144
+ pnpm run test-e2e
145
+ ```
146
+
147
+ The e2e tests create a realistic monorepo workspace with lodash dependencies and verify:
148
+
149
+ - Appendix creation and updates
150
+ - Override version changes
151
+ - Appendix preservation when overrides are removed (bug fix verification)
152
+ - Cross-package dependency tracking
153
+
129
154
  In the near feature, Pastoralist will fully support a config file but this is it for now!
130
155
 
131
156
  Read on to understand what is going on under the hood of Pastoralist!
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var G=Object.create;var P=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var $=Object.getPrototypeOf,C=Object.prototype.hasOwnProperty;var M=(e,r,t,s)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of T(r))!C.call(e,o)&&o!==t&&P(e,o,{get:()=>r[o],enumerable:!(s=L(r,o))||s.enumerable});return e};var m=(e,r,t)=>(t=e!=null?G($(e)):{},M(r||!e||!e.__esModule?P(t,"default",{value:e,enumerable:!0}):t,e));var x=require("commander"),J=m(require("ora"),1),D=m(require("gradient-string"),1);var g=require("fs"),N=require("path"),S=m(require("fast-glob"),1),I=require("compare-versions");var O=process.env.DEBUG==="true"||!1,j="\u{1F411} \u{1F469}\u{1F3FD}\u200D\u{1F33E} Pastoralist:";var{sync:E}=S.default,A=async e=>{let r=e?.depPaths||["**/package.json"],t=e?.path||"package.json",s=e?.root||"./",o=e?.ignore||["**/node_modules/**"],n=e?.isTesting||!1,p=await w(t);if(!p){l.debug("no config found","update");return}let a=V({options:e,config:p}),d=E(r,{cwd:s,ignore:o}),i=await U(d,a),c=i?_(i):[],u=B(a,c);if(n)return i;await F({appendix:i,path:t,config:p,overrides:u})},U=async(e,r)=>{let t=R(r)||{},s=Object.keys(t);if(!(s.length>0))return;let n={};try{for(let p of e){let a=await K(p,t,s);if(!a)continue;let d=a?.appendix||{};n={...n,...d}}}catch(p){l.error("Error constructing appendix","constructAppendix",p)}return n},_=e=>{if(!e)return[];let r=Object.keys(e);return r.length===0?[]:r.filter(t=>{let s=e[t]?.dependents;return!s||Object.keys(s).length===0}).map(t=>t.split("@")[0])},B=(e,r=[])=>{if(!e)return;let t=R(e);if(!t||Object.keys(t).length===0){l.debug("No overrides found to update","updateOverrides");return}return Object.entries(t).reduce((s,[o,n])=>(r.includes(o)||(s[o]=n),s),{})};async function F({appendix:e,path:r,config:t,overrides:s,isTesting:o=!1}){if(!(s&&Object.keys(s).length>0)){let i=["pastoralist","resolutions","overrides","pnpm"];for(let c of i)delete t[c]}if(e&&Object.keys(e).length>0&&(t.pastoralist={appendix:e}),t?.resolutions&&(t.resolutions=s),t?.overrides&&(t.overrides=s),t?.pnpm?.overrides&&(t.pnpm.overrides=s),o)return t;let a=(0,N.resolve)(r),d=JSON.stringify(t,null,2);(0,g.writeFileSync)(a,d)}function V({config:e={}}){let r="resolveOverrides",t="Pastorlist didn't find any overrides or resolutions!",s=X(e);if(!s){l.error(t,r);return}let{type:o,overrides:n}=s;if(!(Object.keys(n)?.length>0)||!o){l.error(t,r);return}let a=Object.keys(n)||[];if(a.some(c=>typeof n[c]=="object")){l.error("Pastoralist only supports simple overrides!",r),l.error("Pastoralist is bypassing the specified complex overrides. \u{1F44C}",r);return}let i=a.reduce((c,u)=>Object.assign(c,{[u]:n[u]}),{});return o==="pnpmOverrides"?{type:"pnpm",pnpm:{overrides:i}}:o==="resolutions"?{type:"resolutions",resolutions:i}:{type:"npm",overrides:i}}var X=({overrides:e={},pnpm:r={},resolutions:t={}}={})=>{let s=r?.overrides||{},o=[{type:"overrides",overrides:e},{type:"pnpmOverrides",overrides:s},{type:"resolutions",overrides:t}].filter(({overrides:d})=>Object.keys(d).length>0),n="defineOverride";if(!(o?.length>0)){l.debug("\u{1F411} \u{1F469}\u{1F3FD}\u200D\u{1F33E} Pastoralist didn't find any overrides!",n);return}if(o?.length>1){l.error("Only 1 override object allowed",n);return}return o[0]},R=e=>{let r=e?.type;if(!r){l.error("no type found","resolveOverridesProp");return}return r==="resolutions"?e?.resolutions:r==="pnpm"?e?.pnpm?.overrides:e?.overrides};async function K(e,r,t){let s=await w(e);if(!s)return;let{name:o,dependencies:n={},devDependencies:p={}}=s,a={...n,...p};if(!Object.keys(a).some(v=>t.includes(v)))return;let c=s?.pastoralist?.appendix||{},u=q({appendix:c,overrides:r,dependencies:n,devDependencies:p,packageName:o});return{name:o,dependencies:n,devDependencies:p,appendix:u}}var q=({overrides:e={},appendix:r={},dependencies:t={},devDependencies:s={},packageName:o="",cache:n=new Map})=>{let p=Object.keys(e),a={...t,...s},d=Object.keys(a);for(let i of p){if(!d.includes(i))continue;let u=e[i],v=a[i];if((0,I.satisfies)(u,v))continue;let f=`${i}@${u}`;if(n.has(f)){r[f]=n.get(f);continue}let k={dependents:{...r?.[f]?.dependents||{},[o]:`${i}@${v}`}};r[f]=k,n.set(f,k)}return Object.keys(r).forEach(i=>{(!r[i].dependents||Object.keys(r[i].dependents).length===0)&&delete r[i]}),r};function w(e){if(h.has(e))return h.get(e);try{let r=(0,g.readFileSync)(e,"utf8"),t=JSON.parse(r);return h.set(e,t),t}catch(r){console.log(r),l.error(`\u{1F411} \u{1F469}\u{1F3FD}\u200D\u{1F33E} Pastoralist found invalid JSON at:
3
- ${e}`,"resolveJSON",r);return}}var y=(e,r,t)=>(s,o,...n)=>{if(!r)return;let p=o?`[${o}]`:"";console[e](`${j}[${t}]${p} ${s}`,...n)},b=({file:e,isLogging:r=!1})=>({debug:y("debug",r,e),error:y("error",r,e),info:y("info",r,e)}),l=b({file:"scripts.ts",isLogging:O}),h=new Map;async function z(e={}){let r=O||e.debug,t=b({file:"program.ts",isLogging:r}),{isTestingCLI:s=!1,...o}=e;if(s){t.debug("action:options:","action",{options:e});return}try{let n=(0,D.default)("green","tan"),p=(0,J.default)(`\u{1F469}\u{1F3FD}\u200D\u{1F33E} ${n("pastoralist")} checking herd...
4
- `).start();await A(o),p.succeed(`\u{1F469}\u{1F3FD}\u200D\u{1F33E} ${n("pastoralist")} the herd is safe!`)}catch(n){t.error("action:fn","action",{error:n}),process.exit(1)}}x.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("-dp","--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(z).parse(process.argv);
2
+ "use strict";var E=Object.create;var A=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var T=Object.getPrototypeOf,M=Object.prototype.hasOwnProperty;var U=(e,s,t,r)=>{if(s&&typeof s=="object"||typeof s=="function")for(let d of L(s))!M.call(e,d)&&d!==t&&A(e,d,{get:()=>s[d],enumerable:!(r=G(s,d))||r.enumerable});return e};var y=(e,s,t)=>(t=e!=null?E(T(e)):{},U(s||!e||!e.__esModule?A(t,"default",{value:e,enumerable:!0}):t,e));var P=require("commander"),J=y(require("ora"),1),C=y(require("gradient-string"),1);var h=require("fs"),S=require("path"),w=y(require("fast-glob"),1),N=require("compare-versions");var m=process.env.DEBUG==="true"||!1,R="\u{1F411} \u{1F469}\u{1F3FD}\u200D\u{1F33E} Pastoralist:";var{sync:F}=w.default,I=async e=>{let s=e?.depPaths||["**/package.json"],t=e?.path||"package.json",r=e?.root||"./",d=e?.ignore||["**/node_modules/**/node_modules/**"],i=e?.isTesting||!1,p=await k(t);if(!p){a.debug("no config found","update");return}let u=q({options:e,config:p}),o=F(s,{cwd:r,ignore:d});a.debug(`Found ${o.length} package.json files`,"update"),o.forEach(g=>a.debug(`Package: ${g}`,"update"));let n=await _(o,u),c=n?B(n):[],l=V(u,c);if(i)return n;await X({appendix:n,path:t,config:p,overrides:l})},_=async(e,s)=>{let t=D(s)||{},r=Object.keys(t);if(!(r.length>0))return;let i={};try{let p={};await Promise.all(e.map(async o=>{let n=await k(o);if(!n||!n.name)return;let c={...n.dependencies||{},...n.devDependencies||{}};p[n.name]={dependencies:c,dependents:[],filePath:o}})),Object.keys(p).forEach(o=>{let n=p[o].dependencies||{};Object.entries(n).forEach(([c,l])=>{p[c]&&p[c].dependents.push({name:o,version:l})})}),r.forEach(o=>{let n=t[o];a.debug(`Processing override: ${o}@${n}`,"constructAppendix");let c=p[o]?.dependents||[];if(a.debug(`Found ${c.length} dependents for ${o}`,"constructAppendix"),c.length>0){let l=`${o}@${n}`,g=c.reduce((f,v)=>(a.debug(`Checking dependent: ${v.name} requires ${o}@${v.version}`,"constructAppendix"),a.debug(`Adding ${v.name} as a dependent for ${o}`,"constructAppendix"),{...f,[v.name]:`${o}@${v.version}`}),{}),O=Object.keys(g).length;a.debug(`Found ${O} dependents that need override for ${o}`,"constructAppendix"),O>0&&(i[l]={dependents:g},a.debug(`Added ${l} to appendix with ${O} dependents`,"constructAppendix"))}}),(await Promise.all(e.map(async o=>await z(o,t,r)))).filter(o=>o).forEach(o=>{let n=o?.appendix||{};i={...i,...n}})}catch(p){a.error("Error constructing appendix","constructAppendix",p)}return i},B=e=>{if(!e)return[];let s=Object.keys(e);return s.length===0?[]:s.filter(t=>{let r=e[t]?.dependents;return!r||Object.keys(r).length===0}).map(t=>t.split("@")[0])},V=(e,s=[])=>{if(!e)return;let t=D(e);if(!t||Object.keys(t).length===0){a.debug("No overrides found to update","updateOverrides");return}return Object.entries(t).reduce((r,[d,i])=>(s.includes(d)||(r[d]=i),r),{})};async function X({appendix:e,path:s,config:t,overrides:r,isTesting:d=!1}){if(!(r&&Object.keys(r).length>0)){let n=["pastoralist","resolutions","overrides","pnpm"];for(let c of n)delete t[c]}if(e&&Object.keys(e).length>0&&(t.pastoralist={appendix:e}),t?.resolutions&&(t.resolutions=r),t?.overrides&&(t.overrides=r),t?.pnpm?.overrides&&(t.pnpm.overrides=r),d)return t;let u=(0,S.resolve)(s),o=JSON.stringify(t,null,2);(0,h.writeFileSync)(u,o)}function q({config:e={}}){let s="resolveOverrides",t="Pastorlist didn't find any overrides or resolutions!",r=K(e);if(!r){a.error(t,s);return}let{type:d,overrides:i}=r;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 n=u.reduce((c,l)=>Object.assign(c,{[l]:i[l]}),{});return d==="pnpmOverrides"?{type:"pnpm",pnpm:{overrides:n}}:d==="resolutions"?{type:"resolutions",resolutions:n}:{type:"npm",overrides:n}}var K=({overrides:e={},pnpm:s={},resolutions:t={}}={})=>{let r=s?.overrides||{},d=[{type:"overrides",overrides:e},{type:"pnpmOverrides",overrides:r},{type:"resolutions",overrides:t}].filter(({overrides:o})=>Object.keys(o).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]},D=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 z(e,s,t){let r=await k(e);if(!r)return;let{name:d,dependencies:i={},devDependencies:p={}}=r,u={...i,...p};if(!Object.keys(u).some(g=>t.includes(g)))return;let c=r?.pastoralist?.appendix||{},l=H({appendix:c,overrides:s,dependencies:i,devDependencies:p,packageName:d});return{name:d,dependencies:i,devDependencies:p,appendix:l}}var H=({overrides:e={},appendix:s={},dependencies:t={},devDependencies:r={},packageName:d="",cache:i=new Map})=>{let p=Object.keys(e),u={...t,...r},o=Object.keys(u);for(let n of p){if(!o.includes(n))continue;let l=e[n],g=u[n];if((0,N.satisfies)(l,g))continue;let f=`${n}@${l}`;if(i.has(f)){s[f]=i.get(f);continue}let $={dependents:{...s?.[f]?.dependents||{},[d]:`${n}@${g}`}};s[f]=$,i.set(f,$)}return Object.keys(s).forEach(n=>{(!s[n].dependents||Object.keys(s[n].dependents).length===0)&&delete s[n]}),s};function k(e){if(x.has(e))return x.get(e);try{let s=(0,h.readFileSync)(e,"utf8"),t=JSON.parse(s);return x.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:
3
+ ${e}`,"resolveJSON",s);return}}var b=(e,s,t)=>(r,d,...i)=>{if(!s)return;let p=d?`[${d}]`:"";console[e](`${R}[${t}]${p} ${r}`,...i)},j=({file:e,isLogging:s=!1})=>({debug:b("debug",s,e),error:b("error",s,e),info:b("info",s,e)}),a=j({file:"scripts.ts",isLogging:m}),x=new Map;async function Q(e={}){let s=m||e.debug,t=j({file:"program.ts",isLogging:s}),{isTestingCLI:r=!1,...d}=e;if(r){t.debug("action:options:","action",{options:e});return}try{let i=(0,C.default)("green","tan"),p=(0,J.default)(`\u{1F469}\u{1F3FD}\u200D\u{1F33E} ${i("pastoralist")} checking herd...
4
+ `).start();await I(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)}}P.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(Q).parse(process.argv);
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import{program as j}from"commander";import E from"ora";import U from"gradient-string";import{readFileSync as N,writeFileSync as S}from"fs";import{resolve as I}from"path";import A from"fast-glob";import{satisfies as R}from"compare-versions";var O=process.env.DEBUG==="true"||!1,b="\u{1F411} \u{1F469}\u{1F3FD}\u200D\u{1F33E} Pastoralist:";var{sync:w}=A,x=async e=>{let r=e?.depPaths||["**/package.json"],t=e?.path||"package.json",s=e?.root||"./",n=e?.ignore||["**/node_modules/**"],o=e?.isTesting||!1,p=await P(t);if(!p){l.debug("no config found","update");return}let a=T({options:e,config:p}),d=w(r,{cwd:s,ignore:n}),i=await J(d,a),c=i?D(i):[],u=G(a,c);if(o)return i;await L({appendix:i,path:t,config:p,overrides:u})},J=async(e,r)=>{let t=k(r)||{},s=Object.keys(t);if(!(s.length>0))return;let o={};try{for(let p of e){let a=await C(p,t,s);if(!a)continue;let d=a?.appendix||{};o={...o,...d}}}catch(p){l.error("Error constructing appendix","constructAppendix",p)}return o},D=e=>{if(!e)return[];let r=Object.keys(e);return r.length===0?[]:r.filter(t=>{let s=e[t]?.dependents;return!s||Object.keys(s).length===0}).map(t=>t.split("@")[0])},G=(e,r=[])=>{if(!e)return;let t=k(e);if(!t||Object.keys(t).length===0){l.debug("No overrides found to update","updateOverrides");return}return Object.entries(t).reduce((s,[n,o])=>(r.includes(n)||(s[n]=o),s),{})};async function L({appendix:e,path:r,config:t,overrides:s,isTesting:n=!1}){if(!(s&&Object.keys(s).length>0)){let i=["pastoralist","resolutions","overrides","pnpm"];for(let c of i)delete t[c]}if(e&&Object.keys(e).length>0&&(t.pastoralist={appendix:e}),t?.resolutions&&(t.resolutions=s),t?.overrides&&(t.overrides=s),t?.pnpm?.overrides&&(t.pnpm.overrides=s),n)return t;let a=I(r),d=JSON.stringify(t,null,2);S(a,d)}function T({config:e={}}){let r="resolveOverrides",t="Pastorlist didn't find any overrides or resolutions!",s=$(e);if(!s){l.error(t,r);return}let{type:n,overrides:o}=s;if(!(Object.keys(o)?.length>0)||!n){l.error(t,r);return}let a=Object.keys(o)||[];if(a.some(c=>typeof o[c]=="object")){l.error("Pastoralist only supports simple overrides!",r),l.error("Pastoralist is bypassing the specified complex overrides. \u{1F44C}",r);return}let i=a.reduce((c,u)=>Object.assign(c,{[u]:o[u]}),{});return n==="pnpmOverrides"?{type:"pnpm",pnpm:{overrides:i}}:n==="resolutions"?{type:"resolutions",resolutions:i}:{type:"npm",overrides:i}}var $=({overrides:e={},pnpm:r={},resolutions:t={}}={})=>{let s=r?.overrides||{},n=[{type:"overrides",overrides:e},{type:"pnpmOverrides",overrides:s},{type:"resolutions",overrides:t}].filter(({overrides:d})=>Object.keys(d).length>0),o="defineOverride";if(!(n?.length>0)){l.debug("\u{1F411} \u{1F469}\u{1F3FD}\u200D\u{1F33E} Pastoralist didn't find any overrides!",o);return}if(n?.length>1){l.error("Only 1 override object allowed",o);return}return n[0]},k=e=>{let r=e?.type;if(!r){l.error("no type found","resolveOverridesProp");return}return r==="resolutions"?e?.resolutions:r==="pnpm"?e?.pnpm?.overrides:e?.overrides};async function C(e,r,t){let s=await P(e);if(!s)return;let{name:n,dependencies:o={},devDependencies:p={}}=s,a={...o,...p};if(!Object.keys(a).some(v=>t.includes(v)))return;let c=s?.pastoralist?.appendix||{},u=M({appendix:c,overrides:r,dependencies:o,devDependencies:p,packageName:n});return{name:n,dependencies:o,devDependencies:p,appendix:u}}var M=({overrides:e={},appendix:r={},dependencies:t={},devDependencies:s={},packageName:n="",cache:o=new Map})=>{let p=Object.keys(e),a={...t,...s},d=Object.keys(a);for(let i of p){if(!d.includes(i))continue;let u=e[i],v=a[i];if(R(u,v))continue;let f=`${i}@${u}`;if(o.has(f)){r[f]=o.get(f);continue}let h={dependents:{...r?.[f]?.dependents||{},[n]:`${i}@${v}`}};r[f]=h,o.set(f,h)}return Object.keys(r).forEach(i=>{(!r[i].dependents||Object.keys(r[i].dependents).length===0)&&delete r[i]}),r};function P(e){if(m.has(e))return m.get(e);try{let r=N(e,"utf8"),t=JSON.parse(r);return m.set(e,t),t}catch(r){console.log(r),l.error(`\u{1F411} \u{1F469}\u{1F3FD}\u200D\u{1F33E} Pastoralist found invalid JSON at:
3
- ${e}`,"resolveJSON",r);return}}var g=(e,r,t)=>(s,n,...o)=>{if(!r)return;let p=n?`[${n}]`:"";console[e](`${b}[${t}]${p} ${s}`,...o)},y=({file:e,isLogging:r=!1})=>({debug:g("debug",r,e),error:g("error",r,e),info:g("info",r,e)}),l=y({file:"scripts.ts",isLogging:O}),m=new Map;async function _(e={}){let r=O||e.debug,t=y({file:"program.ts",isLogging:r}),{isTestingCLI:s=!1,...n}=e;if(s){t.debug("action:options:","action",{options:e});return}try{let o=U("green","tan"),p=E(`\u{1F469}\u{1F3FD}\u200D\u{1F33E} ${o("pastoralist")} checking herd...
4
- `).start();await x(n),p.succeed(`\u{1F469}\u{1F3FD}\u200D\u{1F33E} ${o("pastoralist")} the herd is safe!`)}catch(o){t.error("action:fn","action",{error:o}),process.exit(1)}}j.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("-dp","--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(_).parse(process.argv);
2
+ 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:
3
+ ${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...
4
+ `).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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pastoralist",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "A tool to watch over node module resolutions and overrides 🐑 👩🏽‍🌾",
5
5
  "main": "dist/index.cjs",
6
6
  "module": "dist/index.js",
@@ -24,6 +24,8 @@
24
24
  "clean-dist": "rimraf dist",
25
25
  "commit": "git-cz",
26
26
  "commit-msg": "commitlint --edit $1",
27
+ "create-demo": "node demo/create-demo.js generate",
28
+ "demo-embed": "node demo/create-demo.js embed",
27
29
  "dev": "pnpm --filter @pastoralist/app run app-dev",
28
30
  "lint": "turbo run lint-src",
29
31
  "lint-fix": "eslint",
@@ -36,6 +38,7 @@
36
38
  "test": "tsx --test tests/*.ts",
37
39
  "test-resolutions": "tsx src/index.ts --debug",
38
40
  "test-resolutions-with-dist": "node dist/index.js --debug",
41
+ "test-e2e": "./e2e/scripts/run-e2e-tests.sh",
39
42
  "typecheck": "turbo run typecheck-src",
40
43
  "typecheck-src": "tsc --noEmit",
41
44
  "update": "codependence --update && pnpm run update-app-deps",
@@ -61,39 +64,39 @@
61
64
  },
62
65
  "homepage": "https://jeffry.in/pastoralist/",
63
66
  "dependencies": {
64
- "commander": "12.1.0",
67
+ "commander": "14.0.0",
65
68
  "compare-versions": "6.1.1",
66
- "fast-glob": "3.3.2",
69
+ "fast-glob": "3.3.3",
67
70
  "gradient-string": "^3.0.0",
68
71
  "ora": "^8.1.0",
69
72
  "p-limit": "^6.1.0"
70
73
  },
71
74
  "devDependencies": {
72
- "@commitlint/cli": "19.6.0",
73
- "@commitlint/config-conventional": "19.6.0",
75
+ "@commitlint/cli": "19.8.1",
76
+ "@commitlint/config-conventional": "19.8.1",
74
77
  "@commitlint/format": "^19.5.0",
75
78
  "@eslint/js": "^9.13.0",
76
79
  "@types/gradient-string": "^1.1.6",
77
- "@types/node": "22.10.2",
80
+ "@types/node": "24.0.10",
78
81
  "codependence": "^0.3.1",
79
82
  "eslint": "9.14.0",
80
- "eslint-config-prettier": "^9.1.0",
83
+ "eslint-config-prettier": "^10.1.5",
81
84
  "eslint-plugin-prettier": "^5.2.1",
82
85
  "husky": "9.1.7",
83
86
  "is-ci": "^4.1.0",
84
- "lint-staged": "15.2.11",
87
+ "lint-staged": "16.1.2",
85
88
  "path-exists-cli": "^2.0.0",
86
- "prettier": "3.4.2",
87
- "release-it": "17.10.0",
89
+ "prettier": "3.6.2",
90
+ "release-it": "19.0.3",
88
91
  "rimraf": "6.0.1",
89
92
  "stdouttojson": "1.0.0",
90
93
  "tslib": "2.8.1",
91
- "tsup": "8.3.5",
94
+ "tsup": "8.5.0",
92
95
  "tsx": "^4.19.2",
93
- "turbo": "2.3.3",
94
- "typescript": "5.7.2",
96
+ "turbo": "2.5.4",
97
+ "typescript": "5.8.3",
95
98
  "typescript-eslint": "^8.12.2",
96
- "vite": "6.0.3"
99
+ "vite": "7.0.2"
97
100
  },
98
101
  "commitlint": {
99
102
  "extends": [