dep-report 0.0.2 → 0.0.3
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/dist/cli.js +54 -53
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,45 +1,46 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{b as oe}from"./chunk-KNT3JZ7P.js";import{Command as
|
|
2
|
+
import{b as oe}from"./chunk-KNT3JZ7P.js";import{Command as wt}from"commander";import{existsSync as Fe,mkdirSync as Le,writeFileSync as T,readFileSync as St}from"fs";import{join as v}from"path";import{existsSync as _e}from"fs";import{join as He}from"path";function se(e=process.cwd()){let t=[{file:"pnpm-lock.yaml",manager:"pnpm"},{file:"bun.lock",manager:"bun"},{file:"bun.lockb",manager:"bun"},{file:"package-lock.json",manager:"npm"}];for(let{file:r,manager:n}of t){let s=He(e,r);if(_e(s))return{manager:n,lockfile:r}}return null}function ae(e){return{npm:"npm outdated --json",pnpm:"pnpm outdated --json",bun:"bun outdated --json"}[e]}import{exec as Ke}from"child_process";import{promisify as Je}from"util";var We=Je(Ke);function ie(e){if(!/\|\s*Package\s*\|/i.test(e))return null;let t=[],r=e.split(`
|
|
3
|
+
`).map(n=>n.trim()).filter(Boolean);for(let n of r){if(!n.startsWith("|"))continue;let s=n.split("|").map($=>$.trim()).filter(Boolean);if(s.length<4)continue;let[c,a,k,u]=s;!c||/^-+$/.test(c)||c.toLowerCase()==="package"||t.push({name:c,current:a,update:k,latest:u})}return t.length>0?t:null}async function ce(e,t=process.cwd()){let r=ae(e);try{let{stdout:n,stderr:s}=await We(r,{cwd:t,maxBuffer:10485760}),c=n.trim()||s.trim();if(!c)return{};try{return JSON.parse(c)}catch(a){if(e==="bun"){let k=ie(c);if(k)return k}if(c.includes("All packages are up to date")||c==="{}")return{};throw new Error(`Failed to parse outdated output: ${a instanceof Error?a.message:String(a)}`)}}catch(n){let s=String(n.stdout||n.stderr||"").trim();if(s)try{return JSON.parse(s)}catch{if(e==="bun"){let c=ie(s);if(c)return c}if(/no outdated|up to date/i.test(s)||s==="{}")return{};throw n.code==="ENOENT"?new Error(`Package manager "${e}" not found. Please install it first.`):new Error(`Failed to execute outdated command: ${n.message}`)}if(n.code==="ENOENT")throw new Error(`Package manager "${e}" not found. Please install it first.`);return{}}}function le(e){let t=[],r=Array.isArray(e)?e:Array.isArray(e.packages)?e.packages:null;if(r){for(let n of r){if(!n||typeof n!="object")continue;let s=n.name||n.package||n.pkg,c=n.current||n.installed||n.version||"-",a=n.latest||n.update||n.wanted||c,k=n.wanted||n.update||n.current||a,u=n.type||n.dependencyType||n.kind||de(String(s));!s||!a||t.push({name:String(s),current:String(c),wanted:String(k),latest:String(a),type:u})}return t}for(let[n,s]of Object.entries(e)){if(typeof s!="object"||s===null||Array.isArray(s))continue;let c=s.current||s.installed||s.version||"-",a=s.latest||s.wanted||c,k=s.wanted||s.current||a,u=s.type||s.dependencyType||de(n);!n||!a||t.push({name:n,current:String(c),wanted:String(k),latest:String(a),type:u})}return t}function de(e){return e.startsWith("@types/")||e.includes("-test")||e.includes("-spec")||e==="typescript"||e==="eslint"||e.startsWith("eslint-")||e.startsWith("@eslint/")?"devDependencies":"dependencies"}var L=class{constructor(t="https://registry.npmjs.org"){this.baseUrl=t}async getMetadata(t){try{let r=`${this.baseUrl}/${encodeURIComponent(t)}`,n=await fetch(r,{headers:{Accept:"application/json"}});if(!n.ok){if(n.status===404)return null;throw new Error(`Registry request failed: ${n.status}`)}return await n.json()}catch{return null}}};var me=new L;async function Ye(e,t=me,r=new Date){let s=await(typeof t=="string"?new L(t):t).getMetadata(e.name);if(!s||!s.time)return{...e,currentPublishedAt:null,latestPublishedAt:null,age:null,behindByDays:null,isStale:!1,risk:"Exotic"};let c=s.time[e.current]?new Date(s.time[e.current]):null,a=s.time[e.latest]?new Date(s.time[e.latest]):null,k=null;if(c){let $=r.getTime()-c.getTime();k=Math.floor($/(1e3*60*60*24))}let u=null;if(c&&a){let $=a.getTime()-c.getTime();u=Math.floor($/(1e3*60*60*24))}return{...e,currentPublishedAt:c,latestPublishedAt:a,age:k,behindByDays:u,isStale:!1,risk:"Exotic"}}async function ue(e,t=5,r=me,n=new Date){let s=typeof r=="string"?new L(r):r,c=[];for(let a=0;a<e.length;a+=t){let k=e.slice(a,a+t),u=await Promise.all(k.map($=>Ye($,s,n)));c.push(...u),a+t<e.length&&await new Promise($=>setTimeout($,500))}return c}import{diff as Ge,valid as pe}from"semver";function qe(e){return!e||e==="-"||e==="missing"?!1:/^(file:|git\+|https?:|link:|workspace:)/.test(e)}function Ve(e,t){if(qe(e))return"Exotic";if(!e||e==="-"||e==="missing")return"NotInstalled";if(!pe(e)||!pe(t))return"Exotic";try{switch(Ge(e,t)){case"major":return"Major";case"minor":return"Minor";case"patch":return"Patch";default:return"Patch"}}catch{return"Exotic"}}function fe(e,t=null){return e.map(r=>{let n=Ve(r.current,r.latest),s=t!==null&&r.age!==null&&r.age>t;return{...r,risk:n,isStale:s}})}import{format as ge}from"date-fns";function C(e){if(!e)return null;let t={blocked:/^BLOCKED[:\-\s]/i,deferred:/^DEFERRED[:\-\s]/i,accepted:/^ACCEPTED(\s+RISK)?[:\-\s]/i};return t.blocked.test(e)?"blocked":t.deferred.test(e)?"deferred":t.accepted.test(e)?"accepted":null}function I(e){if(!e)return"";let t=C(e);if(!t)return e;let r={blocked:"\u{1F534} BLOCKED",deferred:"\u{1F7E1} DEFERRED",accepted:"\u{1F535} ACCEPTED RISK"},n=e.replace(/^(BLOCKED|DEFERRED|ACCEPTED(\s+RISK)?)[:\-\s]+/i,"").trim();return`${r[t]}: ${n}`}function O(e){let t=0;e.age!==null&&(e.age>730?t+=10:e.age>365&&(t+=5)),e.risk==="Major"?t+=8:e.risk==="Minor"&&(t+=3);let r=C(e.note);return r==="blocked"?t+=15:r==="deferred"&&(t+=5),e.behindByDays!==null&&e.behindByDays>365&&(t+=7),t}function A(e){return e.current===e.latest}function F(e,t,r){let n=e.length,s=e.filter(i=>i.isStale).length,c=t-n,a=e.filter(i=>i.risk==="Major").length,k=e.filter(i=>i.risk==="Minor").length,u=e.filter(i=>i.risk==="Patch").length,$=e.filter(i=>C(i.note)==="blocked").length,d=e.filter(i=>C(i.note)==="deferred").length,y=e.filter(i=>C(i.note)==="accepted").length,l="healthy",g="\u{1F7E2}",o="Healthy";if(r){let i=r.method,h=t>0?s/t*100:0,f=e.filter(p=>p.risk==="Major").length,b=t>0?f/t*100:0;i==="percentage"?h>=r.atRisk.stalePercent||b>=r.atRisk.majorPercent?(l="atRisk",g="\u{1F534}",o=`At Risk (${Math.round(h)}% stale)`):h>=r.degrading.stalePercent||b>=r.degrading.majorPercent?(l="degrading",g="\u{1F7E1}",o=`Degrading (${Math.round(h)}% stale)`):(l="healthy",g="\u{1F7E2}",o="Healthy"):s>=r.atRisk.stalePercent||f>=r.atRisk.majorPercent?(l="atRisk",g="\u{1F534}",o=`At Risk (${s} stale)`):s>=r.degrading.stalePercent||f>=r.degrading.majorPercent?(l="degrading",g="\u{1F7E1}",o=`Degrading (${s} stale)`):(l="healthy",g="\u{1F7E2}",o="Healthy")}else{let i=t>0?s/t*100:0,h=e.filter(b=>b.risk==="Major").length,f=t>0?h/t*100:0;i>=15||f>=20?(l="atRisk",g="\u{1F534}",o=`At Risk (${Math.round(i)}% stale)`):i>=5||f>=10?(l="degrading",g="\u{1F7E1}",o=`Degrading (${Math.round(i)}% stale)`):(l="healthy",g="\u{1F7E2}",o="Healthy")}return{total:t,outdated:n,stale:s,upToDate:c,blocked:$,deferred:d,accepted:y,major:a,minor:k,patch:u,riskStatus:l,riskStatusEmoji:g,riskStatusText:o}}function q(e){if(e===null)return"Unknown";if(e<30)return`${e}d`;if(e<365)return`${Math.floor(e/30)}m`;let t=Math.floor(e/365),r=Math.floor(e%365/30);return r>0?`${t}y ${r}m`:`${t}y`}function V(e){if(e===null)return"\u2014";if(e<30)return`${e}d`;if(e<365)return`${Math.floor(e/30)}m`;let t=Math.floor(e/365),r=Math.floor(e%365/30);return r>0?`${t}y ${r}m`:`${t}y`}function Q(e,t=new Date,r){let n=ge(t,"yyyy-MM-dd"),s=ge(t,"yyyy-MM-dd HH:mm:ss");if(e.length===0)return`# Dependency Report (${n})
|
|
3
4
|
|
|
4
|
-
Generated at: ${
|
|
5
|
+
Generated at: ${s}
|
|
5
6
|
|
|
6
7
|
\u2705 All dependencies are up to date
|
|
7
|
-
`;let
|
|
8
|
+
`;let c=r??e.length,a=F(e,c),k=[...e].sort((d,y)=>{let l={Major:0,Minor:1,Patch:2,Exotic:3,NotInstalled:4},g=(l[d.risk]||99)-(l[y.risk]||99);return g!==0?g:d.age===null&&y.age===null?0:d.age===null?1:y.age===null?-1:y.age-d.age}),u=`# Dependency Report (${n})
|
|
8
9
|
|
|
9
|
-
Generated at: ${
|
|
10
|
+
Generated at: ${s}
|
|
10
11
|
|
|
11
12
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
12
13
|
${a.riskStatusEmoji} ${a.riskStatusText}
|
|
13
14
|
\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
14
15
|
|
|
15
16
|
Total: ${a.total} | Outdated: ${a.outdated} | Stale: ${a.stale} | Up-to-date: ${a.upToDate}
|
|
16
|
-
`;(a.blocked>0||a.deferred>0||a.accepted>0)&&(
|
|
17
|
-
`),
|
|
17
|
+
`;(a.blocked>0||a.deferred>0||a.accepted>0)&&(u+=`Blocked: ${a.blocked} | Deferred: ${a.deferred} | Accepted Risk: ${a.accepted}
|
|
18
|
+
`),u+=`
|
|
18
19
|
**Risk Assessment:** ${a.stale} stale dependencies and ${a.major} unaddressed major upgrades detected.
|
|
19
20
|
|
|
20
|
-
`;let k
|
|
21
|
+
`;let $=k.map(d=>({pkg:d,score:O(d)})).filter(({score:d})=>d>15).sort((d,y)=>y.score-d.score).slice(0,7).map(({pkg:d})=>d);if($.length>0){u+=`\u2501\u2501\u2501 ACTION REQUIRED \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
21
22
|
|
|
22
|
-
`;let
|
|
23
|
-
`;for(let
|
|
24
|
-
${I(
|
|
25
|
-
`,
|
|
23
|
+
`;let d=$.filter(l=>O(l)>=20||l.risk==="Major"||l.note&&/BLOCKED/i.test(l.note)),y=$.filter(l=>!d.includes(l));if(d.length>0){u+=`\u{1F534} Critical Risk
|
|
24
|
+
`;for(let l of d){let g=q(l.age),o=V(l.behindByDays),i=l.risk==="Major"?"Major update":l.risk==="Minor"?"Minor update":"Patch update",h=l.note?`
|
|
25
|
+
${I(l.note)}`:"";u+=` \u2022 ${l.name} (${l.current} \u2192 ${l.latest})
|
|
26
|
+
`,u+=` ${g} old, behind by ${o} | ${i}${h}
|
|
26
27
|
|
|
27
|
-
`}}if(y.length>0){
|
|
28
|
-
`;for(let
|
|
29
|
-
${I(
|
|
30
|
-
`,
|
|
28
|
+
`}}if(y.length>0){u+=`\u{1F7E1} Review Soon
|
|
29
|
+
`;for(let l of y){let g=q(l.age),o=V(l.behindByDays),i=l.risk==="Major"?"Major update":l.risk==="Minor"?"Minor update":"Patch update",h=l.note?`
|
|
30
|
+
${I(l.note)}`:"";u+=` \u2022 ${l.name} (${l.current} \u2192 ${l.latest})
|
|
31
|
+
`,u+=` ${g} old, behind by ${o} | ${i}${h}
|
|
31
32
|
|
|
32
|
-
`}}
|
|
33
|
-
`}else
|
|
33
|
+
`}}u+=`
|
|
34
|
+
`}else u+=`\u2501\u2501\u2501 ACTION REQUIRED \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
34
35
|
|
|
35
|
-
`,
|
|
36
|
+
`,u+=`\u2705 No critical actions required
|
|
36
37
|
|
|
37
|
-
`;
|
|
38
|
+
`;u+=`\u2501\u2501\u2501 FULL DEPENDENCY LIST \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
|
|
38
39
|
|
|
39
|
-
`,
|
|
40
|
-
`,
|
|
41
|
-
`;for(let
|
|
42
|
-
`}return
|
|
40
|
+
`,u+=`| Package | Current | Latest | Age | Behind | Risk | Status | Notes |
|
|
41
|
+
`,u+=`|---------|---------|--------|-----|---------|------|--------|-------|
|
|
42
|
+
`;for(let d of k){let y=q(d.age),l=V(d.behindByDays),g=d.risk==="Major"?"\u{1F534} Major":d.risk==="Minor"?"\u{1F7E1} Minor":d.risk==="Patch"?"\u{1F7E2} Patch":d.risk,o=A(d)?"\u2705 Stable":"Outdated",i=d.note?I(d.note):"";u+=`| ${d.name} | ${d.current} | ${d.latest} | ${y} | ${l} | ${g} | ${o} | ${i} |
|
|
43
|
+
`}return u}import{format as he}from"date-fns";function E(e){let t={"&":"&","<":"<",">":">",'"':""","'":"'"};return e.replace(/[&<>"']/g,r=>t[r])}function X(e){if(e===null)return"Unknown";if(e<30)return`${e}d`;if(e<365)return`${Math.floor(e/30)}m`;let t=Math.floor(e/365),r=Math.floor(e%365/30);return r>0?`${t}y ${r}m`:`${t}y`}function Z(e){if(e===null)return"\u2014";if(e<30)return`${e}d`;if(e<365)return`${Math.floor(e/30)}m`;let t=Math.floor(e/365),r=Math.floor(e%365/30);return r>0?`${t}y ${r}m`:`${t}y`}function Qe(e){switch(e){case"Major":return"#dc2626";case"Minor":return"#ea580c";case"Patch":return"#ca8a04";case"Exotic":return"#6b7280";case"NotInstalled":return"#9ca3af";default:return"#6b7280"}}function ee(e,t=new Date,r){let n=he(t,"yyyy-MM-dd"),s=he(t,"yyyy-MM-dd HH:mm:ss");if(e.length===0)return`<!DOCTYPE html>
|
|
43
44
|
<html lang="en">
|
|
44
45
|
<head>
|
|
45
46
|
<meta charset="UTF-8">
|
|
@@ -76,38 +77,38 @@ Total: ${a.total} | Outdated: ${a.outdated} | Stale: ${a.stale} | Up-to-date: ${
|
|
|
76
77
|
<body>
|
|
77
78
|
<div class="container">
|
|
78
79
|
<h1>Dependency Report (${n})</h1>
|
|
79
|
-
<div class="timestamp">Generated at: ${
|
|
80
|
+
<div class="timestamp">Generated at: ${s}</div>
|
|
80
81
|
<div class="empty-state">
|
|
81
82
|
<h2>\u2705 All dependencies are up to date</h2>
|
|
82
83
|
</div>
|
|
83
84
|
</div>
|
|
84
85
|
</body>
|
|
85
|
-
</html>`;let
|
|
86
|
+
</html>`;let c=r??e.length,a=F(e,c),k=[...e].sort((o,i)=>{let h={Major:0,Minor:1,Patch:2,Exotic:3,NotInstalled:4},f=(h[o.risk]||99)-(h[i.risk]||99);return f!==0?f:o.age===null&&i.age===null?0:o.age===null?1:i.age===null?-1:i.age-o.age}),u=k.map(o=>({pkg:o,score:O(o)})).filter(({score:o})=>o>15).sort((o,i)=>i.score-o.score).slice(0,7).map(({pkg:o})=>o),$=u.filter(o=>O(o)>=20||o.risk==="Major"||o.note&&/BLOCKED/i.test(o.note)),d=u.filter(o=>!$.includes(o)),y="";if(u.length>0){if($.length>0){y+='<div class="action-group critical">',y+='<h3 class="action-title critical">\u{1F534} Critical Risk</h3>';for(let o of $){let i=X(o.age),h=Z(o.behindByDays),f=o.risk==="Major"?"Major update":o.risk==="Minor"?"Minor update":"Patch update",b=C(o.note),p=b?`<span class="badge badge-${b}">${b.toUpperCase()}</span>`:"",w=o.note?E(o.note.replace(/^(BLOCKED|DEFERRED|ACCEPTED(\s+RISK)?)[:\-\s]+/i,"").trim()):"";y+=`
|
|
86
87
|
<div class="package-card">
|
|
87
88
|
<div class="package-header">
|
|
88
|
-
<strong>${
|
|
89
|
+
<strong>${E(o.name)}</strong> (${E(o.current)} \u2192 ${E(o.latest)})
|
|
89
90
|
</div>
|
|
90
91
|
<div class="package-details">
|
|
91
|
-
${
|
|
92
|
-
${
|
|
92
|
+
${i} old, behind by ${h} | ${f}
|
|
93
|
+
${p?`<br>${p} ${w?E(w):""}`:""}
|
|
93
94
|
</div>
|
|
94
|
-
</div>`}y+="</div>"}if(
|
|
95
|
+
</div>`}y+="</div>"}if(d.length>0){y+='<div class="action-group review">',y+='<h3 class="action-title review">\u{1F7E1} Review Soon</h3>';for(let o of d){let i=X(o.age),h=Z(o.behindByDays),f=o.risk==="Major"?"Major update":o.risk==="Minor"?"Minor update":"Patch update",b=C(o.note),p=b?`<span class="badge badge-${b}">${b.toUpperCase()}</span>`:"",w=o.note?E(o.note.replace(/^(BLOCKED|DEFERRED|ACCEPTED(\s+RISK)?)[:\-\s]+/i,"").trim()):"";y+=`
|
|
95
96
|
<div class="package-card">
|
|
96
97
|
<div class="package-header">
|
|
97
|
-
<strong>${
|
|
98
|
+
<strong>${E(o.name)}</strong> (${E(o.current)} \u2192 ${E(o.latest)})
|
|
98
99
|
</div>
|
|
99
100
|
<div class="package-details">
|
|
100
|
-
${
|
|
101
|
-
${
|
|
101
|
+
${i} old, behind by ${h} | ${f}
|
|
102
|
+
${p?`<br>${p} ${w?E(w):""}`:""}
|
|
102
103
|
</div>
|
|
103
|
-
</div>`}y+="</div>"}}else y='<div class="no-actions">\u2705 No critical actions required</div>';let
|
|
104
|
+
</div>`}y+="</div>"}}else y='<div class="no-actions">\u2705 No critical actions required</div>';let l="";for(let o of k){let i=X(o.age),h=Z(o.behindByDays),f=Qe(o.risk),b=A(o)?'<span class="status-stable">\u2705 Stable</span>':"Outdated",p=C(o.note),w=p?`<span class="badge badge-${p}">${p.toUpperCase()}</span>`:"",R=o.note?E(o.note.replace(/^(BLOCKED|DEFERRED|ACCEPTED(\s+RISK)?)[:\-\s]+/i,"").trim()):"",M=w&&R?`${w} ${R}`:w||R||"";l+=`
|
|
104
105
|
<tr>
|
|
105
|
-
<td class="package-name">${
|
|
106
|
-
<td>${
|
|
107
|
-
<td>${
|
|
108
|
-
<td>${
|
|
109
|
-
<td>${
|
|
110
|
-
<td><span class="risk-badge" style="background-color: ${f}">${
|
|
106
|
+
<td class="package-name">${E(o.name)}</td>
|
|
107
|
+
<td>${E(o.current)}</td>
|
|
108
|
+
<td>${E(o.latest)}</td>
|
|
109
|
+
<td>${E(i)}</td>
|
|
110
|
+
<td>${E(h)}</td>
|
|
111
|
+
<td><span class="risk-badge" style="background-color: ${f}">${E(o.risk)}</span></td>
|
|
111
112
|
<td>${b}</td>
|
|
112
113
|
<td class="notes">${M}</td>
|
|
113
114
|
</tr>`}let g=a.riskStatus==="healthy"?"#10b981":a.riskStatus==="degrading"?"#f59e0b":"#ef4444";return`<!DOCTYPE html>
|
|
@@ -326,7 +327,7 @@ Total: ${a.total} | Outdated: ${a.outdated} | Stale: ${a.stale} | Up-to-date: ${
|
|
|
326
327
|
<body>
|
|
327
328
|
<div class="container">
|
|
328
329
|
<h1>Dependency Report (${n})</h1>
|
|
329
|
-
<div class="timestamp">Generated at: ${
|
|
330
|
+
<div class="timestamp">Generated at: ${s}</div>
|
|
330
331
|
|
|
331
332
|
<div class="status-badge">
|
|
332
333
|
${a.riskStatusEmoji} ${a.riskStatusText}
|
|
@@ -389,18 +390,18 @@ Total: ${a.total} | Outdated: ${a.outdated} | Stale: ${a.stale} | Up-to-date: ${
|
|
|
389
390
|
</tr>
|
|
390
391
|
</thead>
|
|
391
392
|
<tbody>
|
|
392
|
-
${
|
|
393
|
+
${l}
|
|
393
394
|
</tbody>
|
|
394
395
|
</table>
|
|
395
396
|
</div>
|
|
396
397
|
</body>
|
|
397
|
-
</html>`}import{existsSync as
|
|
398
|
-
`)},startSpinner:e=>{
|
|
399
|
-
`,"utf-8"),
|
|
400
|
-
`),
|
|
401
|
-
Dependency Health Comparison`),console.log(`From: ${
|
|
402
|
-
`),
|
|
403
|
-
`),process.exit(N<0?1:0)}var
|
|
398
|
+
</html>`}import{existsSync as ye,access as Xe,constants as Ze,mkdirSync as et,writeFileSync as tt,unlinkSync as rt}from"fs";import{join as be}from"path";import{promisify as nt}from"util";var ot=nt(Xe);async function ke(e=process.cwd()){let t=be(e,"node_modules");if(!ye(t))throw new Error('node_modules directory not found. Please run "npm install", "pnpm install", or "bun install" first.')}async function $e(e){if(!ye(e))try{et(e,{recursive:!0})}catch(r){throw new Error(`Cannot create directory ${e}: ${r instanceof Error?r.message:String(r)}`)}try{await ot(e,Ze.W_OK)}catch{throw new Error(`No write permission for directory: ${e}`)}let t=be(e,`.write-test-${Date.now()}.tmp`);try{tt(t,"ok"),rt(t)}catch(r){throw new Error(`Cannot write to directory ${e}: ${r instanceof Error?r.message:String(r)}`)}}function H(e,t){try{return JSON.parse(e)}catch(r){throw r instanceof SyntaxError?new Error(`Invalid JSON in ${t}: ${r.message}`):r}}var j={reset:"\x1B[0m",bright:"\x1B[1m",dim:"\x1B[2m",red:"\x1B[31m",green:"\x1B[32m",yellow:"\x1B[33m",blue:"\x1B[34m",cyan:"\x1B[36m"},we=["-","\\","|","/"],K=null,Se="",m={info:e=>{console.log(`${j.cyan}[INFO]${j.reset} ${e}`)},success:e=>{console.log(`${j.green}${j.bright}[OK]${j.reset} ${j.green}${e}${j.reset}`)},warn:e=>{console.warn(`${j.yellow}[WARN]${j.reset} ${e}`)},error:e=>{console.error(`${j.red}${j.bright}[ERR]${j.reset} ${j.red}${e}${j.reset}`)},progress:(e,t,r)=>{let n=Math.round(e/t*100);process.stdout.write(`\r${j.cyan}${r}${j.reset} ${j.dim}(${e}/${t}, ${n}%)${j.reset}`),e===t&&process.stdout.write(`
|
|
399
|
+
`)},startSpinner:e=>{Se=e;let t=0;K=setInterval(()=>{process.stdout.write(`\r${j.cyan}${we[t]}${j.reset} ${Se}`),t=(t+1)%we.length},100)},stopSpinner:()=>{K&&(clearInterval(K),K=null,process.stdout.write("\r"+" ".repeat(process.stdout.columns||80)+"\r"))}};import{format as Be}from"date-fns";import{existsSync as at,readFileSync as it}from"fs";import{join as ct}from"path";import{z as D}from"zod";var P={staleThreshold:"18 months",ignorePatterns:[],formats:{markdown:!0,html:!0},concurrency:5,failConditions:{stale:!1,major:!1},reportEmptyState:!0},st=D.object({staleThreshold:D.string().default(P.staleThreshold),ignorePatterns:D.array(D.string()).default(P.ignorePatterns),formats:D.object({markdown:D.boolean().default(P.formats.markdown),html:D.boolean().default(P.formats.html)}).default(P.formats),concurrency:D.number().int().positive().default(P.concurrency),failConditions:D.object({stale:D.boolean().default(P.failConditions.stale),major:D.boolean().default(P.failConditions.major)}).default(P.failConditions),reportEmptyState:D.boolean().default(P.reportEmptyState)}).passthrough();function Pe(e){try{return st.parse(e)}catch(t){if(t instanceof D.ZodError){let r=t.errors.map(n=>`${n.path.join(".")}: ${n.message}`).join(", ");throw new Error(`Invalid config: ${r}`)}throw t}}function xe(e=process.cwd()){let t=ct(e,".dep-report","config.json");if(!at(t))return m.info("No config.json found, using defaults"),P;try{let r=it(t,"utf-8"),n=H(r,t),s={staleThreshold:n.staleThreshold??P.staleThreshold,ignorePatterns:n.ignorePatterns??P.ignorePatterns,formats:{markdown:n.formats?.markdown??P.formats.markdown,html:n.formats?.html??P.formats.html},concurrency:n.concurrency??P.concurrency,failConditions:{stale:n.failConditions?.stale??P.failConditions.stale,major:n.failConditions?.major??P.failConditions.major},reportEmptyState:n.reportEmptyState??P.reportEmptyState},c=Pe(s);return m.info("Loaded config from .dep-report/config.json"),c}catch(r){return m.warn(`Failed to load config: ${r instanceof Error?r.message:String(r)}`),m.warn("Using default configuration"),P}}function dt(e){let r=e.trim().toLowerCase().match(/^(\d+)\s+(year|years|month|months|day|days|week|weeks)$/);if(!r)throw new Error(`Invalid duration format: "${e}". Expected format: "18 months", "2 years", "90 days", etc.`);let n=parseInt(r[1],10),s=r[2];switch(s){case"day":case"days":return n*24*60*60*1e3;case"week":case"weeks":return n*7*24*60*60*1e3;case"month":case"months":return n*30*24*60*60*1e3;case"year":case"years":return n*365*24*60*60*1e3;default:throw new Error(`Unsupported time unit: ${s}`)}}function je(e){let t=dt(e);return Math.floor(t/(1e3*60*60*24))}import{existsSync as lt,readFileSync as mt}from"fs";import{join as ut}from"path";function Re(e=process.cwd()){let t=ut(e,".dep-report","notes.json");if(!lt(t))return{};try{let r=mt(t,"utf-8"),n=H(r,t);if(typeof n!="object"||n===null||Array.isArray(n))return m.warn("Invalid notes.json format, expected object"),{};for(let[s,c]of Object.entries(n))typeof c!="string"&&(m.warn(`Invalid note for ${s}, expected string`),delete n[s]);return m.info(`Loaded ${Object.keys(n).length} notes from notes.json`),n}catch(r){return m.warn(`Failed to load notes: ${r instanceof Error?r.message:String(r)}`),{}}}function Ee(e,t){return e.map(r=>{let n=t[r.name];return n?{...r,note:n}:r})}import{minimatch as pt}from"minimatch";function Me(e,t){return t.length===0?e:e.filter(r=>{for(let n of t)if(pt(r.name,n))return!1;return!0})}import{existsSync as B,mkdirSync as De,writeFileSync as te}from"fs";import{join as U}from"path";async function ve(e=process.cwd(),t=!1,r){let n=U(e,".dep-report"),s=U(n,"config.json"),c=U(n,"notes.json"),a=U(n,"reports"),k=U(n,".gitignore");if(B(n)?m.info(".dep-report/ directory already exists"):(De(n,{recursive:!0}),m.success("Created .dep-report/ directory")),B(a)||(De(a,{recursive:!0}),m.success("Created .dep-report/reports/ directory")),!B(s)||t){let u=P;if(r){let d=oe(r);u=d.config,m.info(`Using preset: ${d.displayName} - ${d.description}`)}let $=JSON.stringify(u,null,2);te(s,$,"utf-8"),m.success("Created .dep-report/config.json")}else m.info("config.json already exists, skipping");if(B(c))m.info("notes.json already exists, skipping");else{let u=JSON.stringify({},null,2);te(c,u,"utf-8"),m.success("Created .dep-report/notes.json")}B(k)?m.info(".gitignore already exists, skipping"):(te(k,`.cache.json
|
|
400
|
+
`,"utf-8"),m.success("Created .dep-report/.gitignore")),m.success("Initialization complete!"),m.info("You can now customize .dep-report/config.json and add notes to .dep-report/notes.json")}import{readFileSync as Ce,existsSync as re,readdirSync as ft}from"fs";import{join as J}from"path";import{format as gt,parse as W,subDays as ht,differenceInDays as ne}from"date-fns";function Oe(e,t){let r=e.match(/\| Package \|.*?\n\|-+\|\n([\s\S]*?)\n\n/);if(!r)return null;let n=r[1].trim().split(`
|
|
401
|
+
`),s=[];for(let d of n){let y=d.split("|").map(S=>S.trim()).filter(S=>S);if(y.length<6)continue;let[l,g,o,i,h,f,b,p]=y;if(b.includes("Stable"))continue;let w=null;if(i&&i!=="Unknown"){let S=i.match(/(\d+)d|(\d+)m|(\d+)y/);S&&(S[1]?w=parseInt(S[1]):S[2]?w=parseInt(S[2])*30:S[3]&&(w=parseInt(S[3])*365))}let R=null;if(h&&h!=="\u2014"){let S=h.match(/(\d+)d|(\d+)m|(\d+)y/);S&&(S[1]?R=parseInt(S[1]):S[2]?R=parseInt(S[2])*30:S[3]&&(R=parseInt(S[3])*365))}let M="Patch";f.includes("Major")?M="Major":f.includes("Minor")?M="Minor":f.includes("Patch")?M="Patch":f.includes("Exotic")?M="Exotic":f.includes("NotInstalled")&&(M="NotInstalled"),s.push({name:l,current:g,latest:o,wanted:o,type:"dependencies",currentPublishedAt:null,latestPublishedAt:null,age:w,behindByDays:R,isStale:w!==null&&w>365,risk:M,note:p||void 0})}let c=e.match(/Total: (\d+) \| Outdated: (\d+) \| Stale: (\d+)/),a=c?parseInt(c[1]):s.length,k=c?parseInt(c[2]):s.length,u=c?parseInt(c[3]):s.filter(d=>d.isStale).length,$=s.filter(d=>d.risk==="Major").length;return{date:t,packages:s,summary:{total:a,outdated:k,stale:u,major:$}}}function Te(e,t){if(t==="latest"){let r=J(e,"latest.md");return re(r)?r:null}if(t==="last-month"){let n=ht(new Date,30),s=ft(e).filter(c=>c.endsWith("_outdated.md")).map(c=>{let a=c.match(/(\d{4}-\d{2}-\d{2})_outdated\.md/);return a?{file:c,date:W(a[1],"yyyy-MM-dd",new Date)}:null}).filter(c=>c!==null).sort((c,a)=>Math.abs(ne(c.date,n))-Math.abs(ne(a.date,n)));return s.length>0?J(e,s[0].file):null}try{let r=W(t,"yyyy-MM-dd",new Date),n=gt(r,"yyyy-MM-dd"),s=J(e,`${n}_outdated.md`);if(re(s))return s}catch{}return null}function Ne(e){return 100-e.stale*5-e.major*3}async function Ie(e,t,r=process.cwd()){let n=J(r,".dep-report","reports");re(n)||(m.error("No reports directory found. Run dep-report first to generate reports."),process.exit(1));let s=Te(n,e),c=Te(n,t);s||(m.error(`Report not found for: ${e}`),process.exit(1)),c||(m.error(`Report not found for: ${t}`),process.exit(1));let a=Ce(s,"utf-8"),k=Ce(c,"utf-8"),u=s.match(/(\d{4}-\d{2}-\d{2})_outdated\.md/),$=c.match(/(\d{4}-\d{2}-\d{2})_outdated\.md/),d=u?u[1]:e,y=$?$[1]:t,l=Oe(a,d),g=Oe(k,y);(!l||!g)&&(m.error("Failed to parse reports"),process.exit(1));let o=ne(W(y,"yyyy-MM-dd",new Date),W(d,"yyyy-MM-dd",new Date)),i=g.summary.stale-l.summary.stale,h=g.summary.major-l.summary.major,f=new Set(l.packages.map(x=>x.name)),b=new Set(g.packages.map(x=>x.name)),p=g.packages.filter(x=>!f.has(x.name)),w=l.packages.filter(x=>!b.has(x.name)),R=g.packages.filter(x=>{let _=l.packages.find(G=>G.name===x.name);return _&&_.current!==x.current}),M=Ne(l.summary),S=Ne(g.summary),N=M>0?(S-M)/M*100:0;if(console.log(`
|
|
402
|
+
Dependency Health Comparison`),console.log(`From: ${d} \u2192 ${y} (${o} days)
|
|
403
|
+
`),R.length>0){console.log("\u{1F4C8} Improvements:");for(let x of R.slice(0,5)){let _=l.packages.find(G=>G.name===x.name);console.log(` \u2022 ${x.name}: ${_?.current} \u2192 ${x.current}`)}R.length>5&&console.log(` ... and ${R.length-5} more`),console.log("")}if(i!==0||h!==0){if(console.log("\u{1F4CA} Metrics:"),i!==0){let x=i>0?"+":"";console.log(` \u2022 Stale packages: ${l.summary.stale} \u2192 ${g.summary.stale} (${x}${i})`)}if(h!==0){let x=h>0?"+":"";console.log(` \u2022 Major upgrades pending: ${l.summary.major} \u2192 ${g.summary.major} (${x}${h})`)}console.log("")}p.length>0&&console.log(`\u2795 Added: ${p.map(x=>x.name).join(", ")}`),w.length>0&&console.log(`\u2796 Removed: ${w.map(x=>x.name).join(", ")}`),(p.length>0||w.length>0)&&console.log("");let Ue=N>0?"\u2705":N<0?"\u26A0\uFE0F":"\u27A1\uFE0F",ze=N>0?"improved":N<0?"regressed":"unchanged";console.log(`${Ue} Overall: Health ${ze} by ${Math.abs(N).toFixed(1)}%`),console.log(` Score: ${M.toFixed(1)} \u2192 ${S.toFixed(1)}
|
|
404
|
+
`),process.exit(N<0?1:0)}var yt="https://registry.npmjs.org";async function Ae(e=yt,t=5e3){let r=new AbortController,n=setTimeout(()=>r.abort(),t);try{let s=`${e}/lodash`;return(await fetch(s,{headers:{Accept:"application/json"},signal:r.signal})).ok}catch{return!1}finally{clearTimeout(n)}}import{readFileSync as bt,existsSync as kt}from"fs";import{join as $t}from"path";function Y(e=process.cwd()){let t=$t(e,"package.json");if(!kt(t))return 0;try{let r=bt(t,"utf-8"),n=JSON.parse(r),s=Object.keys(n.dependencies||{}).length,c=Object.keys(n.devDependencies||{}).length,a=Object.keys(n.peerDependencies||{}).length,k=Object.keys(n.optionalDependencies||{}).length;return s+c+a+k}catch{return 0}}var z=new wt,Pt=new URL("../package.json",import.meta.url),xt=JSON.parse(St(Pt,"utf-8")),jt=xt.version??"0.0.0";z.name("dep-report").description("Generate dependency risk reports").version(jt).addHelpText("before",`
|
|
404
405
|
dep-report - Generate dependency risk reports
|
|
405
406
|
|
|
406
407
|
USAGE
|
|
@@ -421,8 +422,8 @@ EXAMPLES
|
|
|
421
422
|
|
|
422
423
|
LEARN MORE
|
|
423
424
|
https://github.com/hussmarsidi/dep-reports
|
|
424
|
-
`);z.command("init").description("Scaffold .dep-report/ directory structure").option("--include-config","Force overwrite config.json even if it exists").option("--preset <preset>","Use a preset configuration (starter|production|strict)","production").action(async e=>{try{let{isValidPresetName:t}=await import("./presets-BJSOBSDA.js"),r;e.preset&&(t(e.preset)||(
|
|
425
|
-
Dry Run Summary`),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),console.log(`Status: ${
|
|
426
|
-
`),console.log(`Total dependencies: ${
|
|
427
|
-
`),h==="actions"||h==="full"){let b=
|
|
428
|
-
`);let f=!1;r.failConditions.stale&&
|
|
425
|
+
`);z.command("init").description("Scaffold .dep-report/ directory structure").option("--include-config","Force overwrite config.json even if it exists").option("--preset <preset>","Use a preset configuration (starter|production|strict)","production").action(async e=>{try{let{isValidPresetName:t}=await import("./presets-BJSOBSDA.js"),r;e.preset&&(t(e.preset)||(m.error(`Invalid preset: ${e.preset}. Valid options: starter, production, strict`),process.exit(1)),r=e.preset),await ve(process.cwd(),e.includeConfig,r),process.exit(0)}catch(t){m.error(t instanceof Error?t.message:String(t)),process.exit(1)}});z.command("compare").description("Compare two dependency reports to track health over time").argument("<from>",'Start date (YYYY-MM-DD), "latest", or "last-month"').argument("<to>",'End date (YYYY-MM-DD) or "latest"').action(async(e,t)=>{try{await Ie(e,t)}catch(r){m.error(r instanceof Error?r.message:String(r)),process.exit(1)}});z.option("--dry-run [level]","Preview summary without writing files (summary|actions|full)","actions").action(async e=>{try{let t=process.cwd(),r=xe(t);m.info("Checking prerequisites..."),await ke(t);let n=v(t,".dep-report");await $e(n),m.info("Detecting package manager...");let s=se(t);s||(m.error("No package manager detected. Please ensure you have package-lock.json, pnpm-lock.yaml, bun.lock, or bun.lockb in your project."),process.exit(1)),m.success(`Detected: ${s.manager}`),m.info("Scanning for outdated packages...");let c=await ce(s.manager,t);if(Object.keys(c).length===0){if(m.success("No outdated packages found!"),r.reportEmptyState){let o=v(t,".dep-report","reports");Fe(o)||Le(o,{recursive:!0});let i=Be(new Date,"yyyy-MM-dd"),h=Y(t);if(r.formats.markdown){let f=Q([],new Date,h);T(v(o,`${i}_outdated.md`),f),T(v(o,"latest.md"),f),m.success(`Report generated: .dep-report/reports/${i}_outdated.md`)}if(r.formats.html){let f=ee([],new Date,h);T(v(o,`${i}_outdated.html`),f),T(v(o,"latest.html"),f),m.success(`HTML report generated: .dep-report/reports/${i}_outdated.html`)}}process.exit(0)}m.info(`Found ${Object.keys(c).length} outdated packages`);let a=le(c);m.startSpinner("Checking registry connectivity...");let k=await Ae();m.stopSpinner(),k||(m.error("Unable to reach the npm registry."),m.info("If you have a cache, try running with --refresh."),process.exit(1)),m.success("Registry connectivity confirmed"),m.startSpinner(`Enriching ${a.length} packages with registry metadata...`);let u=await ue(a,r.concurrency);m.stopSpinner(),m.success("Enrichment complete");let $=je(r.staleThreshold),d=fe(u,$),y=d.length;d=Me(d,r.ignorePatterns),y>d.length&&m.info(`Filtered out ${y-d.length} packages based on ignorePatterns`);let l=Re(t);if(d=Ee(d,l),e.dryRun){let o=Y(t),i=F(d,o),h=typeof e.dryRun=="string"?e.dryRun:"actions";if(console.log(`
|
|
426
|
+
Dry Run Summary`),console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"),console.log(`Status: ${i.riskStatusEmoji} ${i.riskStatusText}
|
|
427
|
+
`),console.log(`Total dependencies: ${i.total}`),console.log(`Outdated: ${i.outdated} (${i.major} major, ${i.minor} minor, ${i.patch} patch)`),console.log(`Stale (>12 months): ${i.stale}
|
|
428
|
+
`),h==="actions"||h==="full"){let b=d.map(p=>({pkg:p,score:O(p)})).filter(({score:p})=>p>15).sort((p,w)=>w.score-p.score).slice(0,7).map(({pkg:p})=>p);if(b.length>0){console.log("Action Required:");for(let p of b){let w=p.age!==null?`${p.age}d`:"Unknown",R=p.behindByDays!==null?`${p.behindByDays}d`:"\u2014",M=p.note?I(p.note):"",S=p.risk==="Major"?"\u{1F534}":p.risk==="Minor"?"\u{1F7E1}":"\u{1F7E2}";console.log(` ${S} ${p.name} (${w}, behind by ${R}) - ${p.risk} ${p.current} \u2192 ${p.latest}${M?`, ${M}`:""}`)}console.log("")}}if(h==="full"){console.log("Full Dependency List:");for(let b of d){let p=b.age!==null?`${b.age}d`:"Unknown",w=b.behindByDays!==null?`${b.behindByDays}d`:"\u2014",R=A(b)?"\u2705 Stable":"Outdated";console.log(` ${b.name}: ${b.current} \u2192 ${b.latest} (${p}, ${w}, ${b.risk}, ${R})`)}console.log("")}console.log(`[No files written]
|
|
429
|
+
`);let f=!1;r.failConditions.stale&&d.some(p=>p.isStale)&&(m.error("Found stale packages (--fail-if-stale)"),f=!0),r.failConditions.major&&d.some(p=>p.risk==="Major")&&(m.error("Found major version updates (--fail-if-major)"),f=!0),process.exit(f?1:0)}if(r.formats.markdown||r.formats.html){m.info("Generating reports...");let o=v(t,".dep-report","reports");Fe(o)||Le(o,{recursive:!0});let i=Be(new Date,"yyyy-MM-dd"),h=Y(t);if(r.formats.markdown){let f=Q(d,new Date,h);T(v(o,`${i}_outdated.md`),f),T(v(o,"latest.md"),f),m.success(`Report generated: .dep-report/reports/${i}_outdated.md`),m.success("Latest report: .dep-report/reports/latest.md")}if(r.formats.html){let f=ee(d,new Date,h);T(v(o,`${i}_outdated.html`),f),T(v(o,"latest.html"),f),m.success(`HTML report generated: .dep-report/reports/${i}_outdated.html`),m.success("Latest HTML report: .dep-report/reports/latest.html")}}let g=!1;r.failConditions.stale&&d.some(i=>i.isStale)&&(m.error("Found stale packages (--fail-if-stale)"),g=!0),r.failConditions.major&&d.some(i=>i.risk==="Major")&&(m.error("Found major version updates (--fail-if-major)"),g=!0),process.exit(g?1:0)}catch(t){m.error(t instanceof Error?t.message:String(t)),process.exit(1)}});z.parse();
|