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 +25 -0
- package/dist/index.cjs +3 -3
- package/dist/index.js +3 -3
- package/package.json +17 -14
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
|
|
3
|
-
${e}`,"resolveJSON",
|
|
4
|
-
`).start();await
|
|
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
|
|
3
|
-
${e}`,"resolveJSON",
|
|
4
|
-
`).start();await
|
|
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.
|
|
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": "
|
|
67
|
+
"commander": "14.0.0",
|
|
65
68
|
"compare-versions": "6.1.1",
|
|
66
|
-
"fast-glob": "3.3.
|
|
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.
|
|
73
|
-
"@commitlint/config-conventional": "19.
|
|
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": "
|
|
80
|
+
"@types/node": "24.0.10",
|
|
78
81
|
"codependence": "^0.3.1",
|
|
79
82
|
"eslint": "9.14.0",
|
|
80
|
-
"eslint-config-prettier": "^
|
|
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": "
|
|
87
|
+
"lint-staged": "16.1.2",
|
|
85
88
|
"path-exists-cli": "^2.0.0",
|
|
86
|
-
"prettier": "3.
|
|
87
|
-
"release-it": "
|
|
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.
|
|
94
|
+
"tsup": "8.5.0",
|
|
92
95
|
"tsx": "^4.19.2",
|
|
93
|
-
"turbo": "2.
|
|
94
|
-
"typescript": "5.
|
|
96
|
+
"turbo": "2.5.4",
|
|
97
|
+
"typescript": "5.8.3",
|
|
95
98
|
"typescript-eslint": "^8.12.2",
|
|
96
|
-
"vite": "
|
|
99
|
+
"vite": "7.0.2"
|
|
97
100
|
},
|
|
98
101
|
"commitlint": {
|
|
99
102
|
"extends": [
|