dep-report 0.0.1 → 0.0.2
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 +47 -18
- package/dist/chunk-KNT3JZ7P.js +1 -0
- package/dist/cli.js +304 -46
- package/dist/presets-BJSOBSDA.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# dep-report
|
|
2
2
|
|
|
3
|
+
> **Turn your dependency chaos into a daily, version-controlled risk brief.**
|
|
4
|
+
|
|
3
5
|
Zero-config CLI tool that generates version-controlled snapshots of dependency risk.
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
@@ -18,7 +20,19 @@ Or use with npx (no installation needed):
|
|
|
18
20
|
npx dep-report
|
|
19
21
|
```
|
|
20
22
|
|
|
21
|
-
##
|
|
23
|
+
## The Problem
|
|
24
|
+
|
|
25
|
+
Dependency drift is invisible until it explodes.
|
|
26
|
+
|
|
27
|
+
You get bombarded with automated PRs but can't tell which ones actually matter. No context, just version bumps. A security advisory drops and you're scrambling to figure out your exposure. A build breaks because some package hasn't been touched in 3 years. Management asks about technical debt and you're pulling together an answer on the spot.
|
|
28
|
+
|
|
29
|
+
The real issue isn't automation—it's visibility. You need to see what's outdated, understand the risk, and have evidence for your decisions. Not just a flood of PRs.
|
|
30
|
+
|
|
31
|
+
**Renovate/Dependabot create noise. dep-report creates evidence.**
|
|
32
|
+
|
|
33
|
+
## The Solution
|
|
34
|
+
|
|
35
|
+
dep-report gives you a daily, human-readable risk brief, checked into your repo.
|
|
22
36
|
|
|
23
37
|
Run in your project directory:
|
|
24
38
|
|
|
@@ -32,6 +46,15 @@ This will:
|
|
|
32
46
|
3. Enrich with registry metadata (publish dates, age)
|
|
33
47
|
4. Generate reports in `.dep-report/reports/`
|
|
34
48
|
|
|
49
|
+
## The Outcome
|
|
50
|
+
|
|
51
|
+
You can see at a glance whether you're getting healthier or rotting.
|
|
52
|
+
|
|
53
|
+
**Proof Point:**
|
|
54
|
+
> "Six months ago we had 34 stale dependencies, 9 majors ignored. Today we're at 3 and 1—and we can prove it from the reports in `/.dep-report/reports`."
|
|
55
|
+
|
|
56
|
+
## Quick Start
|
|
57
|
+
|
|
35
58
|
## Usage
|
|
36
59
|
|
|
37
60
|
### Initialize Configuration
|
|
@@ -118,18 +141,19 @@ Exit code conditions for CI/CD integration:
|
|
|
118
141
|
|
|
119
142
|
Whether to generate reports when no outdated packages are found. Default: `true`.
|
|
120
143
|
|
|
121
|
-
### Notes
|
|
144
|
+
### Notes (Decision Log)
|
|
122
145
|
|
|
123
|
-
Add
|
|
146
|
+
Transform tribal knowledge into auditable decisions. Add notes to packages in `.dep-report/notes.json`:
|
|
124
147
|
|
|
125
148
|
```json
|
|
126
149
|
{
|
|
127
|
-
"
|
|
128
|
-
"
|
|
150
|
+
"react": "BLOCKED: waiting for team migration",
|
|
151
|
+
"lodash": "DEFERRED: Q2 2026 - requires architecture refactor",
|
|
152
|
+
"axios": "ACCEPTED RISK: pinned for stability @platform-team"
|
|
129
153
|
}
|
|
130
154
|
```
|
|
131
155
|
|
|
132
|
-
Notes
|
|
156
|
+
Notes with keywords (`BLOCKED:`, `DEFERRED:`, `ACCEPTED RISK:`) are automatically highlighted in reports with badges, creating a self-documenting decision log.
|
|
133
157
|
|
|
134
158
|
## Usage Examples
|
|
135
159
|
|
|
@@ -210,6 +234,23 @@ The tool respects npm registry rate limits by:
|
|
|
210
234
|
|
|
211
235
|
Age is calculated based on when the **currently installed version** was published, not when the latest version was published. This answers: "How old is the dependency we're actively using?"
|
|
212
236
|
|
|
237
|
+
## Guarantees
|
|
238
|
+
|
|
239
|
+
**Privacy:**
|
|
240
|
+
- No tracking, no analytics, no phoning home
|
|
241
|
+
- All data stays local in your repository
|
|
242
|
+
- Registry queries are read-only package metadata
|
|
243
|
+
|
|
244
|
+
**Control:**
|
|
245
|
+
- Outputs are plain files under your version control
|
|
246
|
+
- No vendor lock-in, no proprietary formats
|
|
247
|
+
- Works offline with cached data
|
|
248
|
+
|
|
249
|
+
**Transparency:**
|
|
250
|
+
- Open source (MIT license)
|
|
251
|
+
- Readable templates, editable notes
|
|
252
|
+
- Deterministic output (same input = same report)
|
|
253
|
+
|
|
213
254
|
## Development
|
|
214
255
|
|
|
215
256
|
```bash
|
|
@@ -248,15 +289,3 @@ See [sandbox/README.md](sandbox/README.md) for details.
|
|
|
248
289
|
2. Run `bun run test:sandbox` (must pass)
|
|
249
290
|
3. Visually inspect HTML reports
|
|
250
291
|
4. Run `npm run build` and test CLI manually
|
|
251
|
-
|
|
252
|
-
## Project Status
|
|
253
|
-
|
|
254
|
-
✅ **Production Ready**
|
|
255
|
-
- ✅ Package manager detection (npm, pnpm, bun)
|
|
256
|
-
- ✅ Outdated package scanning
|
|
257
|
-
- ✅ Registry enrichment with age calculation
|
|
258
|
-
- ✅ Risk & age calculation
|
|
259
|
-
- ✅ Markdown & HTML report generation
|
|
260
|
-
- ✅ Configuration system
|
|
261
|
-
- ✅ Notes system
|
|
262
|
-
- ✅ Comprehensive test suite
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var t={starter:{name:"starter",displayName:"Starter",description:"Just getting visibility - no CI failures",config:{staleThreshold:"24 months",ignorePatterns:[],formats:{markdown:!0,html:!0},concurrency:5,failConditions:{stale:!1,major:!1},reportEmptyState:!0}},production:{name:"production",displayName:"Production",description:"Prevent major upgrades from rotting indefinitely (recommended)",config:{staleThreshold:"12 months",ignorePatterns:[],formats:{markdown:!0,html:!0},concurrency:5,failConditions:{stale:!1,major:!0},reportEmptyState:!0}},strict:{name:"strict",displayName:"Strict",description:"Old dependencies break builds",config:{staleThreshold:"6 months",ignorePatterns:[],formats:{markdown:!0,html:!0},concurrency:5,failConditions:{stale:!0,major:!0},reportEmptyState:!0}}};function r(e){return t[e]}function n(){return Object.values(t)}function s(e){return e in t}export{t as a,r as b,n as c,s as d};
|
package/dist/cli.js
CHANGED
|
@@ -1,37 +1,121 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{Command as
|
|
2
|
+
import{b as oe}from"./chunk-KNT3JZ7P.js";import{Command as $t}from"commander";import{existsSync as Ie,mkdirSync as Ae,writeFileSync as T,readFileSync as wt}from"fs";import{join as v}from"path";import{existsSync as Be}from"fs";import{join as ze}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 i=ze(e,r);if(Be(i))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 _e}from"child_process";import{promisify as He}from"util";var Ke=He(_e);async function ie(e,t=process.cwd()){let r=ae(e);try{let{stdout:n,stderr:i}=await Ke(r,{cwd:t,maxBuffer:10485760}),m=n.trim()||i.trim();if(!m)return{};try{return JSON.parse(m)}catch(a){if(m.includes("All packages are up to date")||m==="{}")return{};throw new Error(`Failed to parse outdated output: ${a instanceof Error?a.message:String(a)}`)}}catch(n){if(n.stdout)try{return JSON.parse(n.stdout)}catch{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 ce(e){let t=[];for(let[r,n]of Object.entries(e)){if(typeof n!="object"||n===null||Array.isArray(n))continue;let i=n.current||n.installed||n.version||"-",m=n.latest||n.wanted||i,a=n.wanted||n.current||m,w=n.type||n.dependencyType||Je(r);!r||!m||t.push({name:r,current:String(i),wanted:String(a),latest:String(m),type:w})}return t}function Je(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 de=new L;async function We(e,t=de,r=new Date){let i=await(typeof t=="string"?new L(t):t).getMetadata(e.name);if(!i||!i.time)return{...e,currentPublishedAt:null,latestPublishedAt:null,age:null,behindByDays:null,isStale:!1,risk:"Exotic"};let m=i.time[e.current]?new Date(i.time[e.current]):null,a=i.time[e.latest]?new Date(i.time[e.latest]):null,w=null;if(m){let k=r.getTime()-m.getTime();w=Math.floor(k/(1e3*60*60*24))}let p=null;if(m&&a){let k=a.getTime()-m.getTime();p=Math.floor(k/(1e3*60*60*24))}return{...e,currentPublishedAt:m,latestPublishedAt:a,age:w,behindByDays:p,isStale:!1,risk:"Exotic"}}async function le(e,t=5,r=de,n=new Date){let i=typeof r=="string"?new L(r):r,m=[];for(let a=0;a<e.length;a+=t){let w=e.slice(a,a+t),p=await Promise.all(w.map(k=>We(k,i,n)));m.push(...p),a+t<e.length&&await new Promise(k=>setTimeout(k,500))}return m}import{diff as Ye,valid as me}from"semver";function Ge(e){return!e||e==="-"||e==="missing"?!1:/^(file:|git\+|https?:|link:|workspace:)/.test(e)}function qe(e,t){if(Ge(e))return"Exotic";if(!e||e==="-"||e==="missing")return"NotInstalled";if(!me(e)||!me(t))return"Exotic";try{switch(Ye(e,t)){case"major":return"Major";case"minor":return"Minor";case"patch":return"Patch";default:return"Patch"}}catch{return"Exotic"}}function ue(e,t=null){return e.map(r=>{let n=qe(r.current,r.latest),i=t!==null&&r.age!==null&&r.age>t;return{...r,risk:n,isStale:i}})}import{format as pe}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,i=e.filter(s=>s.isStale).length,m=t-n,a=e.filter(s=>s.risk==="Major").length,w=e.filter(s=>s.risk==="Minor").length,p=e.filter(s=>s.risk==="Patch").length,k=e.filter(s=>C(s.note)==="blocked").length,c=e.filter(s=>C(s.note)==="deferred").length,y=e.filter(s=>C(s.note)==="accepted").length,d="healthy",g="\u{1F7E2}",o="Healthy";if(r){let s=r.method,h=t>0?i/t*100:0,f=e.filter(u=>u.risk==="Major").length,b=t>0?f/t*100:0;s==="percentage"?h>=r.atRisk.stalePercent||b>=r.atRisk.majorPercent?(d="atRisk",g="\u{1F534}",o=`At Risk (${Math.round(h)}% stale)`):h>=r.degrading.stalePercent||b>=r.degrading.majorPercent?(d="degrading",g="\u{1F7E1}",o=`Degrading (${Math.round(h)}% stale)`):(d="healthy",g="\u{1F7E2}",o="Healthy"):i>=r.atRisk.stalePercent||f>=r.atRisk.majorPercent?(d="atRisk",g="\u{1F534}",o=`At Risk (${i} stale)`):i>=r.degrading.stalePercent||f>=r.degrading.majorPercent?(d="degrading",g="\u{1F7E1}",o=`Degrading (${i} stale)`):(d="healthy",g="\u{1F7E2}",o="Healthy")}else{let s=t>0?i/t*100:0,h=e.filter(b=>b.risk==="Major").length,f=t>0?h/t*100:0;s>=15||f>=20?(d="atRisk",g="\u{1F534}",o=`At Risk (${Math.round(s)}% stale)`):s>=5||f>=10?(d="degrading",g="\u{1F7E1}",o=`Degrading (${Math.round(s)}% stale)`):(d="healthy",g="\u{1F7E2}",o="Healthy")}return{total:t,outdated:n,stale:i,upToDate:m,blocked:k,deferred:c,accepted:y,major:a,minor:w,patch:p,riskStatus:d,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=pe(t,"yyyy-MM-dd"),i=pe(t,"yyyy-MM-dd HH:mm:ss");if(e.length===0)return`# Dependency Report (${n})
|
|
3
3
|
|
|
4
|
-
Generated at: ${
|
|
4
|
+
Generated at: ${i}
|
|
5
5
|
|
|
6
6
|
\u2705 All dependencies are up to date
|
|
7
|
-
`;let
|
|
7
|
+
`;let m=r??e.length,a=F(e,m),w=[...e].sort((c,y)=>{let d={Major:0,Minor:1,Patch:2,Exotic:3,NotInstalled:4},g=(d[c.risk]||99)-(d[y.risk]||99);return g!==0?g:c.age===null&&y.age===null?0:c.age===null?1:y.age===null?-1:y.age-c.age}),p=`# Dependency Report (${n})
|
|
8
8
|
|
|
9
|
-
Generated at: ${
|
|
9
|
+
Generated at: ${i}
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
\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
|
+
${a.riskStatusEmoji} ${a.riskStatusText}
|
|
13
|
+
\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
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
15
|
+
Total: ${a.total} | Outdated: ${a.outdated} | Stale: ${a.stale} | Up-to-date: ${a.upToDate}
|
|
16
|
+
`;(a.blocked>0||a.deferred>0||a.accepted>0)&&(p+=`Blocked: ${a.blocked} | Deferred: ${a.deferred} | Accepted Risk: ${a.accepted}
|
|
17
|
+
`),p+=`
|
|
18
|
+
**Risk Assessment:** ${a.stale} stale dependencies and ${a.major} unaddressed major upgrades detected.
|
|
19
|
+
|
|
20
|
+
`;let k=w.map(c=>({pkg:c,score:O(c)})).filter(({score:c})=>c>15).sort((c,y)=>y.score-c.score).slice(0,7).map(({pkg:c})=>c);if(k.length>0){p+=`\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
|
+
`;let c=k.filter(d=>O(d)>=20||d.risk==="Major"||d.note&&/BLOCKED/i.test(d.note)),y=k.filter(d=>!c.includes(d));if(c.length>0){p+=`\u{1F534} Critical Risk
|
|
23
|
+
`;for(let d of c){let g=q(d.age),o=V(d.behindByDays),s=d.risk==="Major"?"Major update":d.risk==="Minor"?"Minor update":"Patch update",h=d.note?`
|
|
24
|
+
${I(d.note)}`:"";p+=` \u2022 ${d.name} (${d.current} \u2192 ${d.latest})
|
|
25
|
+
`,p+=` ${g} old, behind by ${o} | ${s}${h}
|
|
26
|
+
|
|
27
|
+
`}}if(y.length>0){p+=`\u{1F7E1} Review Soon
|
|
28
|
+
`;for(let d of y){let g=q(d.age),o=V(d.behindByDays),s=d.risk==="Major"?"Major update":d.risk==="Minor"?"Minor update":"Patch update",h=d.note?`
|
|
29
|
+
${I(d.note)}`:"";p+=` \u2022 ${d.name} (${d.current} \u2192 ${d.latest})
|
|
30
|
+
`,p+=` ${g} old, behind by ${o} | ${s}${h}
|
|
31
|
+
|
|
32
|
+
`}}p+=`
|
|
33
|
+
`}else p+=`\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
|
+
`,p+=`\u2705 No critical actions required
|
|
36
|
+
|
|
37
|
+
`;p+=`\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
|
+
`,p+=`| Package | Current | Latest | Age | Behind | Risk | Status | Notes |
|
|
40
|
+
`,p+=`|---------|---------|--------|-----|---------|------|--------|-------|
|
|
41
|
+
`;for(let c of w){let y=q(c.age),d=V(c.behindByDays),g=c.risk==="Major"?"\u{1F534} Major":c.risk==="Minor"?"\u{1F7E1} Minor":c.risk==="Patch"?"\u{1F7E2} Patch":c.risk,o=A(c)?"\u2705 Stable":"Outdated",s=c.note?I(c.note):"";p+=`| ${c.name} | ${c.current} | ${c.latest} | ${y} | ${d} | ${g} | ${o} | ${s} |
|
|
42
|
+
`}return p}import{format as fe}from"date-fns";function R(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 Ve(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=fe(t,"yyyy-MM-dd"),i=fe(t,"yyyy-MM-dd HH:mm:ss");if(e.length===0)return`<!DOCTYPE html>
|
|
43
|
+
<html lang="en">
|
|
44
|
+
<head>
|
|
45
|
+
<meta charset="UTF-8">
|
|
46
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
47
|
+
<title>Dependency Report (${n})</title>
|
|
48
|
+
<style>
|
|
49
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
50
|
+
body {
|
|
51
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
52
|
+
line-height: 1.6;
|
|
53
|
+
color: #1f2937;
|
|
54
|
+
background-color: #f9fafb;
|
|
55
|
+
padding: 2rem;
|
|
56
|
+
}
|
|
57
|
+
.container {
|
|
58
|
+
max-width: 1400px;
|
|
59
|
+
margin: 0 auto;
|
|
60
|
+
background: white;
|
|
61
|
+
border-radius: 8px;
|
|
62
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
63
|
+
padding: 2rem;
|
|
64
|
+
}
|
|
65
|
+
.empty-state {
|
|
66
|
+
text-align: center;
|
|
67
|
+
padding: 4rem 2rem;
|
|
68
|
+
color: #059669;
|
|
69
|
+
}
|
|
70
|
+
.empty-state h2 {
|
|
71
|
+
font-size: 1.5rem;
|
|
72
|
+
font-weight: 500;
|
|
73
|
+
}
|
|
74
|
+
</style>
|
|
75
|
+
</head>
|
|
76
|
+
<body>
|
|
77
|
+
<div class="container">
|
|
78
|
+
<h1>Dependency Report (${n})</h1>
|
|
79
|
+
<div class="timestamp">Generated at: ${i}</div>
|
|
26
80
|
<div class="empty-state">
|
|
27
81
|
<h2>\u2705 All dependencies are up to date</h2>
|
|
28
82
|
</div>
|
|
29
|
-
|
|
83
|
+
</div>
|
|
84
|
+
</body>
|
|
85
|
+
</html>`;let m=r??e.length,a=F(e,m),w=[...e].sort((o,s)=>{let h={Major:0,Minor:1,Patch:2,Exotic:3,NotInstalled:4},f=(h[o.risk]||99)-(h[s.risk]||99);return f!==0?f:o.age===null&&s.age===null?0:o.age===null?1:s.age===null?-1:s.age-o.age}),p=w.map(o=>({pkg:o,score:O(o)})).filter(({score:o})=>o>15).sort((o,s)=>s.score-o.score).slice(0,7).map(({pkg:o})=>o),k=p.filter(o=>O(o)>=20||o.risk==="Major"||o.note&&/BLOCKED/i.test(o.note)),c=p.filter(o=>!k.includes(o)),y="";if(p.length>0){if(k.length>0){y+='<div class="action-group critical">',y+='<h3 class="action-title critical">\u{1F534} Critical Risk</h3>';for(let o of k){let s=X(o.age),h=Z(o.behindByDays),f=o.risk==="Major"?"Major update":o.risk==="Minor"?"Minor update":"Patch update",b=C(o.note),u=b?`<span class="badge badge-${b}">${b.toUpperCase()}</span>`:"",$=o.note?R(o.note.replace(/^(BLOCKED|DEFERRED|ACCEPTED(\s+RISK)?)[:\-\s]+/i,"").trim()):"";y+=`
|
|
86
|
+
<div class="package-card">
|
|
87
|
+
<div class="package-header">
|
|
88
|
+
<strong>${R(o.name)}</strong> (${R(o.current)} \u2192 ${R(o.latest)})
|
|
89
|
+
</div>
|
|
90
|
+
<div class="package-details">
|
|
91
|
+
${s} old, behind by ${h} | ${f}
|
|
92
|
+
${u?`<br>${u} ${$?R($):""}`:""}
|
|
93
|
+
</div>
|
|
94
|
+
</div>`}y+="</div>"}if(c.length>0){y+='<div class="action-group review">',y+='<h3 class="action-title review">\u{1F7E1} Review Soon</h3>';for(let o of c){let s=X(o.age),h=Z(o.behindByDays),f=o.risk==="Major"?"Major update":o.risk==="Minor"?"Minor update":"Patch update",b=C(o.note),u=b?`<span class="badge badge-${b}">${b.toUpperCase()}</span>`:"",$=o.note?R(o.note.replace(/^(BLOCKED|DEFERRED|ACCEPTED(\s+RISK)?)[:\-\s]+/i,"").trim()):"";y+=`
|
|
95
|
+
<div class="package-card">
|
|
96
|
+
<div class="package-header">
|
|
97
|
+
<strong>${R(o.name)}</strong> (${R(o.current)} \u2192 ${R(o.latest)})
|
|
98
|
+
</div>
|
|
99
|
+
<div class="package-details">
|
|
100
|
+
${s} old, behind by ${h} | ${f}
|
|
101
|
+
${u?`<br>${u} ${$?R($):""}`:""}
|
|
102
|
+
</div>
|
|
103
|
+
</div>`}y+="</div>"}}else y='<div class="no-actions">\u2705 No critical actions required</div>';let d="";for(let o of w){let s=X(o.age),h=Z(o.behindByDays),f=Ve(o.risk),b=A(o)?'<span class="status-stable">\u2705 Stable</span>':"Outdated",u=C(o.note),$=u?`<span class="badge badge-${u}">${u.toUpperCase()}</span>`:"",E=o.note?R(o.note.replace(/^(BLOCKED|DEFERRED|ACCEPTED(\s+RISK)?)[:\-\s]+/i,"").trim()):"",M=$&&E?`${$} ${E}`:$||E||"";d+=`
|
|
104
|
+
<tr>
|
|
105
|
+
<td class="package-name">${R(o.name)}</td>
|
|
106
|
+
<td>${R(o.current)}</td>
|
|
107
|
+
<td>${R(o.latest)}</td>
|
|
108
|
+
<td>${R(s)}</td>
|
|
109
|
+
<td>${R(h)}</td>
|
|
110
|
+
<td><span class="risk-badge" style="background-color: ${f}">${R(o.risk)}</span></td>
|
|
111
|
+
<td>${b}</td>
|
|
112
|
+
<td class="notes">${M}</td>
|
|
113
|
+
</tr>`}let g=a.riskStatus==="healthy"?"#10b981":a.riskStatus==="degrading"?"#f59e0b":"#ef4444";return`<!DOCTYPE html>
|
|
30
114
|
<html lang="en">
|
|
31
115
|
<head>
|
|
32
116
|
<meta charset="UTF-8">
|
|
33
117
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
34
|
-
<title>Dependency Report (${
|
|
118
|
+
<title>Dependency Report (${n})</title>
|
|
35
119
|
<style>
|
|
36
120
|
* {
|
|
37
121
|
margin: 0;
|
|
@@ -61,24 +145,117 @@ Generated at: ${o}
|
|
|
61
145
|
.timestamp {
|
|
62
146
|
color: #6b7280;
|
|
63
147
|
font-size: 0.875rem;
|
|
64
|
-
margin-bottom:
|
|
148
|
+
margin-bottom: 1rem;
|
|
65
149
|
}
|
|
66
|
-
.
|
|
150
|
+
.status-badge {
|
|
151
|
+
background: ${g};
|
|
152
|
+
color: white;
|
|
153
|
+
padding: 0.5rem 1rem;
|
|
154
|
+
border-radius: 6px;
|
|
155
|
+
font-weight: 600;
|
|
156
|
+
font-size: 1.1rem;
|
|
67
157
|
text-align: center;
|
|
68
|
-
|
|
69
|
-
color: #059669;
|
|
158
|
+
margin: 1.5rem 0;
|
|
70
159
|
}
|
|
71
|
-
.
|
|
160
|
+
.summary-grid {
|
|
161
|
+
display: grid;
|
|
162
|
+
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
163
|
+
gap: 1rem;
|
|
164
|
+
margin: 1.5rem 0;
|
|
165
|
+
}
|
|
166
|
+
.summary-card {
|
|
167
|
+
background: #f9fafb;
|
|
168
|
+
padding: 1rem;
|
|
169
|
+
border-radius: 6px;
|
|
170
|
+
border: 1px solid #e5e7eb;
|
|
171
|
+
}
|
|
172
|
+
.summary-card strong {
|
|
173
|
+
display: block;
|
|
72
174
|
font-size: 1.5rem;
|
|
73
|
-
|
|
175
|
+
color: #111827;
|
|
176
|
+
margin-bottom: 0.25rem;
|
|
177
|
+
}
|
|
178
|
+
.summary-card span {
|
|
179
|
+
color: #6b7280;
|
|
180
|
+
font-size: 0.875rem;
|
|
181
|
+
}
|
|
182
|
+
.risk-assessment {
|
|
183
|
+
background: #fef3c7;
|
|
184
|
+
border-left: 4px solid #f59e0b;
|
|
185
|
+
padding: 1rem;
|
|
186
|
+
margin: 1.5rem 0;
|
|
187
|
+
border-radius: 4px;
|
|
188
|
+
}
|
|
189
|
+
.action-section {
|
|
190
|
+
margin: 2rem 0;
|
|
191
|
+
}
|
|
192
|
+
.action-group {
|
|
193
|
+
margin: 1.5rem 0;
|
|
194
|
+
}
|
|
195
|
+
.action-title {
|
|
196
|
+
font-size: 1.25rem;
|
|
197
|
+
margin-bottom: 1rem;
|
|
198
|
+
padding-bottom: 0.5rem;
|
|
199
|
+
border-bottom: 2px solid #e5e7eb;
|
|
200
|
+
}
|
|
201
|
+
.action-title.critical {
|
|
202
|
+
color: #dc2626;
|
|
203
|
+
border-bottom-color: #dc2626;
|
|
204
|
+
}
|
|
205
|
+
.action-title.review {
|
|
206
|
+
color: #f59e0b;
|
|
207
|
+
border-bottom-color: #f59e0b;
|
|
208
|
+
}
|
|
209
|
+
.package-card {
|
|
210
|
+
background: #f9fafb;
|
|
211
|
+
border: 1px solid #e5e7eb;
|
|
212
|
+
border-radius: 6px;
|
|
213
|
+
padding: 1rem;
|
|
214
|
+
margin-bottom: 0.75rem;
|
|
215
|
+
}
|
|
216
|
+
.package-header {
|
|
217
|
+
font-size: 1rem;
|
|
218
|
+
margin-bottom: 0.5rem;
|
|
219
|
+
}
|
|
220
|
+
.package-details {
|
|
221
|
+
color: #6b7280;
|
|
222
|
+
font-size: 0.875rem;
|
|
223
|
+
}
|
|
224
|
+
.badge {
|
|
225
|
+
display: inline-block;
|
|
226
|
+
padding: 0.25rem 0.5rem;
|
|
227
|
+
border-radius: 4px;
|
|
228
|
+
font-size: 0.75rem;
|
|
229
|
+
font-weight: 600;
|
|
230
|
+
margin-right: 0.5rem;
|
|
231
|
+
}
|
|
232
|
+
.badge-blocked {
|
|
233
|
+
background: #dc2626;
|
|
234
|
+
color: white;
|
|
235
|
+
}
|
|
236
|
+
.badge-deferred {
|
|
237
|
+
background: #f59e0b;
|
|
238
|
+
color: white;
|
|
239
|
+
}
|
|
240
|
+
.badge-accepted {
|
|
241
|
+
background: #3b82f6;
|
|
242
|
+
color: white;
|
|
243
|
+
}
|
|
244
|
+
.no-actions {
|
|
245
|
+
text-align: center;
|
|
246
|
+
padding: 2rem;
|
|
247
|
+
color: #059669;
|
|
248
|
+
font-size: 1.1rem;
|
|
74
249
|
}
|
|
75
250
|
table {
|
|
76
251
|
width: 100%;
|
|
77
252
|
border-collapse: collapse;
|
|
78
|
-
margin-top:
|
|
253
|
+
margin-top: 2rem;
|
|
79
254
|
}
|
|
80
255
|
thead {
|
|
81
256
|
background-color: #f3f4f6;
|
|
257
|
+
position: sticky;
|
|
258
|
+
top: 0;
|
|
82
259
|
}
|
|
83
260
|
th {
|
|
84
261
|
text-align: left;
|
|
@@ -111,16 +288,13 @@ Generated at: ${o}
|
|
|
111
288
|
text-transform: uppercase;
|
|
112
289
|
letter-spacing: 0.05em;
|
|
113
290
|
}
|
|
114
|
-
.
|
|
115
|
-
color: #
|
|
291
|
+
.status-stable {
|
|
292
|
+
color: #10b981;
|
|
116
293
|
font-weight: 600;
|
|
117
294
|
}
|
|
118
|
-
.stale-no {
|
|
119
|
-
color: #059669;
|
|
120
|
-
}
|
|
121
295
|
.notes {
|
|
122
296
|
color: #6b7280;
|
|
123
|
-
font-
|
|
297
|
+
font-size: 0.875rem;
|
|
124
298
|
}
|
|
125
299
|
@media (max-width: 768px) {
|
|
126
300
|
body {
|
|
@@ -129,6 +303,9 @@ Generated at: ${o}
|
|
|
129
303
|
.container {
|
|
130
304
|
padding: 1rem;
|
|
131
305
|
}
|
|
306
|
+
.summary-grid {
|
|
307
|
+
grid-template-columns: repeat(2, 1fr);
|
|
308
|
+
}
|
|
132
309
|
table {
|
|
133
310
|
font-size: 0.875rem;
|
|
134
311
|
}
|
|
@@ -136,35 +313,116 @@ Generated at: ${o}
|
|
|
136
313
|
padding: 0.5rem;
|
|
137
314
|
}
|
|
138
315
|
}
|
|
316
|
+
@media print {
|
|
317
|
+
body {
|
|
318
|
+
background: white;
|
|
319
|
+
}
|
|
320
|
+
.container {
|
|
321
|
+
box-shadow: none;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
139
324
|
</style>
|
|
140
325
|
</head>
|
|
141
326
|
<body>
|
|
142
327
|
<div class="container">
|
|
143
|
-
<h1>Dependency Report (${
|
|
144
|
-
<div class="timestamp">Generated at: ${
|
|
145
|
-
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
</
|
|
328
|
+
<h1>Dependency Report (${n})</h1>
|
|
329
|
+
<div class="timestamp">Generated at: ${i}</div>
|
|
330
|
+
|
|
331
|
+
<div class="status-badge">
|
|
332
|
+
${a.riskStatusEmoji} ${a.riskStatusText}
|
|
333
|
+
</div>
|
|
334
|
+
|
|
335
|
+
<div class="summary-grid">
|
|
336
|
+
<div class="summary-card">
|
|
337
|
+
<strong>${a.total}</strong>
|
|
338
|
+
<span>Total dependencies</span>
|
|
339
|
+
</div>
|
|
340
|
+
<div class="summary-card">
|
|
341
|
+
<strong>${a.outdated}</strong>
|
|
342
|
+
<span>Outdated (${a.major} major, ${a.minor} minor, ${a.patch} patch)</span>
|
|
343
|
+
</div>
|
|
344
|
+
<div class="summary-card">
|
|
345
|
+
<strong>${a.stale}</strong>
|
|
346
|
+
<span>Stale (>12 months)</span>
|
|
347
|
+
</div>
|
|
348
|
+
<div class="summary-card">
|
|
349
|
+
<strong>${a.upToDate}</strong>
|
|
350
|
+
<span>Up-to-date</span>
|
|
351
|
+
</div>
|
|
352
|
+
${a.blocked>0||a.deferred>0||a.accepted>0?`
|
|
353
|
+
<div class="summary-card">
|
|
354
|
+
<strong>${a.blocked}</strong>
|
|
355
|
+
<span>Blocked upgrades</span>
|
|
356
|
+
</div>
|
|
357
|
+
<div class="summary-card">
|
|
358
|
+
<strong>${a.deferred}</strong>
|
|
359
|
+
<span>Deferred upgrades</span>
|
|
360
|
+
</div>
|
|
361
|
+
<div class="summary-card">
|
|
362
|
+
<strong>${a.accepted}</strong>
|
|
363
|
+
<span>Accepted risks</span>
|
|
364
|
+
</div>
|
|
365
|
+
`:""}
|
|
366
|
+
</div>
|
|
367
|
+
|
|
368
|
+
<div class="risk-assessment">
|
|
369
|
+
<strong>Risk Assessment:</strong> ${a.stale} stale dependencies and ${a.major} unaddressed major upgrades detected.
|
|
370
|
+
</div>
|
|
371
|
+
|
|
372
|
+
<div class="action-section">
|
|
373
|
+
<h2 style="margin-bottom: 1rem; font-size: 1.25rem; color: #374151;">Action Required</h2>
|
|
374
|
+
${y}
|
|
375
|
+
</div>
|
|
376
|
+
|
|
377
|
+
<h2 style="margin-top: 2rem; margin-bottom: 1rem; font-size: 1.25rem; color: #374151;">Full Dependency List</h2>
|
|
149
378
|
<table>
|
|
150
379
|
<thead>
|
|
151
380
|
<tr>
|
|
152
381
|
<th>Package</th>
|
|
153
382
|
<th>Current</th>
|
|
154
383
|
<th>Latest</th>
|
|
155
|
-
<th>Risk</th>
|
|
156
384
|
<th>Age</th>
|
|
157
|
-
<th>
|
|
385
|
+
<th>Behind</th>
|
|
386
|
+
<th>Risk</th>
|
|
387
|
+
<th>Status</th>
|
|
158
388
|
<th>Notes</th>
|
|
159
389
|
</tr>
|
|
160
390
|
</thead>
|
|
161
391
|
<tbody>
|
|
162
|
-
${
|
|
392
|
+
${d}
|
|
163
393
|
</tbody>
|
|
164
394
|
</table>
|
|
165
|
-
`}
|
|
166
395
|
</div>
|
|
167
396
|
</body>
|
|
168
|
-
</html>`}import{existsSync as
|
|
169
|
-
`)},startSpinner:
|
|
170
|
-
`,"utf-8"),
|
|
397
|
+
</html>`}import{existsSync as ge,access as Qe,constants as Xe,mkdirSync as Ze,writeFileSync as et,unlinkSync as tt}from"fs";import{join as he}from"path";import{promisify as rt}from"util";var nt=rt(Qe);async function ye(e=process.cwd()){let t=he(e,"node_modules");if(!ge(t))throw new Error('node_modules directory not found. Please run "npm install", "pnpm install", or "bun install" first.')}async function be(e){if(!ge(e))try{Ze(e,{recursive:!0})}catch(r){throw new Error(`Cannot create directory ${e}: ${r instanceof Error?r.message:String(r)}`)}try{await nt(e,Xe.W_OK)}catch{throw new Error(`No write permission for directory: ${e}`)}let t=he(e,`.write-test-${Date.now()}.tmp`);try{et(t,"ok"),tt(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"},ke=["-","\\","|","/"],K=null,$e="",l={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(`
|
|
398
|
+
`)},startSpinner:e=>{$e=e;let t=0;K=setInterval(()=>{process.stdout.write(`\r${j.cyan}${ke[t]}${j.reset} ${$e}`),t=(t+1)%ke.length},100)},stopSpinner:()=>{K&&(clearInterval(K),K=null,process.stdout.write("\r"+" ".repeat(process.stdout.columns||80)+"\r"))}};import{format as Fe}from"date-fns";import{existsSync as st,readFileSync as at}from"fs";import{join as it}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},ot=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 we(e){try{return ot.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 Se(e=process.cwd()){let t=it(e,".dep-report","config.json");if(!st(t))return l.info("No config.json found, using defaults"),P;try{let r=at(t,"utf-8"),n=H(r,t),i={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},m=we(i);return l.info("Loaded config from .dep-report/config.json"),m}catch(r){return l.warn(`Failed to load config: ${r instanceof Error?r.message:String(r)}`),l.warn("Using default configuration"),P}}function ct(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),i=r[2];switch(i){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: ${i}`)}}function Pe(e){let t=ct(e);return Math.floor(t/(1e3*60*60*24))}import{existsSync as dt,readFileSync as lt}from"fs";import{join as mt}from"path";function xe(e=process.cwd()){let t=mt(e,".dep-report","notes.json");if(!dt(t))return{};try{let r=lt(t,"utf-8"),n=H(r,t);if(typeof n!="object"||n===null||Array.isArray(n))return l.warn("Invalid notes.json format, expected object"),{};for(let[i,m]of Object.entries(n))typeof m!="string"&&(l.warn(`Invalid note for ${i}, expected string`),delete n[i]);return l.info(`Loaded ${Object.keys(n).length} notes from notes.json`),n}catch(r){return l.warn(`Failed to load notes: ${r instanceof Error?r.message:String(r)}`),{}}}function je(e,t){return e.map(r=>{let n=t[r.name];return n?{...r,note:n}:r})}import{minimatch as ut}from"minimatch";function Ee(e,t){return t.length===0?e:e.filter(r=>{for(let n of t)if(ut(r.name,n))return!1;return!0})}import{existsSync as U,mkdirSync as Re,writeFileSync as te}from"fs";import{join as B}from"path";async function Me(e=process.cwd(),t=!1,r){let n=B(e,".dep-report"),i=B(n,"config.json"),m=B(n,"notes.json"),a=B(n,"reports"),w=B(n,".gitignore");if(U(n)?l.info(".dep-report/ directory already exists"):(Re(n,{recursive:!0}),l.success("Created .dep-report/ directory")),U(a)||(Re(a,{recursive:!0}),l.success("Created .dep-report/reports/ directory")),!U(i)||t){let p=P;if(r){let c=oe(r);p=c.config,l.info(`Using preset: ${c.displayName} - ${c.description}`)}let k=JSON.stringify(p,null,2);te(i,k,"utf-8"),l.success("Created .dep-report/config.json")}else l.info("config.json already exists, skipping");if(U(m))l.info("notes.json already exists, skipping");else{let p=JSON.stringify({},null,2);te(m,p,"utf-8"),l.success("Created .dep-report/notes.json")}U(w)?l.info(".gitignore already exists, skipping"):(te(w,`.cache.json
|
|
399
|
+
`,"utf-8"),l.success("Created .dep-report/.gitignore")),l.success("Initialization complete!"),l.info("You can now customize .dep-report/config.json and add notes to .dep-report/notes.json")}import{readFileSync as De,existsSync as re,readdirSync as pt}from"fs";import{join as J}from"path";import{format as ft,parse as W,subDays as gt,differenceInDays as ne}from"date-fns";function ve(e,t){let r=e.match(/\| Package \|.*?\n\|-+\|\n([\s\S]*?)\n\n/);if(!r)return null;let n=r[1].trim().split(`
|
|
400
|
+
`),i=[];for(let c of n){let y=c.split("|").map(S=>S.trim()).filter(S=>S);if(y.length<6)continue;let[d,g,o,s,h,f,b,u]=y;if(b.includes("Stable"))continue;let $=null;if(s&&s!=="Unknown"){let S=s.match(/(\d+)d|(\d+)m|(\d+)y/);S&&(S[1]?$=parseInt(S[1]):S[2]?$=parseInt(S[2])*30:S[3]&&($=parseInt(S[3])*365))}let E=null;if(h&&h!=="\u2014"){let S=h.match(/(\d+)d|(\d+)m|(\d+)y/);S&&(S[1]?E=parseInt(S[1]):S[2]?E=parseInt(S[2])*30:S[3]&&(E=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"),i.push({name:d,current:g,latest:o,wanted:o,type:"dependencies",currentPublishedAt:null,latestPublishedAt:null,age:$,behindByDays:E,isStale:$!==null&&$>365,risk:M,note:u||void 0})}let m=e.match(/Total: (\d+) \| Outdated: (\d+) \| Stale: (\d+)/),a=m?parseInt(m[1]):i.length,w=m?parseInt(m[2]):i.length,p=m?parseInt(m[3]):i.filter(c=>c.isStale).length,k=i.filter(c=>c.risk==="Major").length;return{date:t,packages:i,summary:{total:a,outdated:w,stale:p,major:k}}}function Ce(e,t){if(t==="latest"){let r=J(e,"latest.md");return re(r)?r:null}if(t==="last-month"){let n=gt(new Date,30),i=pt(e).filter(m=>m.endsWith("_outdated.md")).map(m=>{let a=m.match(/(\d{4}-\d{2}-\d{2})_outdated\.md/);return a?{file:m,date:W(a[1],"yyyy-MM-dd",new Date)}:null}).filter(m=>m!==null).sort((m,a)=>Math.abs(ne(m.date,n))-Math.abs(ne(a.date,n)));return i.length>0?J(e,i[0].file):null}try{let r=W(t,"yyyy-MM-dd",new Date),n=ft(r,"yyyy-MM-dd"),i=J(e,`${n}_outdated.md`);if(re(i))return i}catch{}return null}function Oe(e){return 100-e.stale*5-e.major*3}async function Te(e,t,r=process.cwd()){let n=J(r,".dep-report","reports");re(n)||(l.error("No reports directory found. Run dep-report first to generate reports."),process.exit(1));let i=Ce(n,e),m=Ce(n,t);i||(l.error(`Report not found for: ${e}`),process.exit(1)),m||(l.error(`Report not found for: ${t}`),process.exit(1));let a=De(i,"utf-8"),w=De(m,"utf-8"),p=i.match(/(\d{4}-\d{2}-\d{2})_outdated\.md/),k=m.match(/(\d{4}-\d{2}-\d{2})_outdated\.md/),c=p?p[1]:e,y=k?k[1]:t,d=ve(a,c),g=ve(w,y);(!d||!g)&&(l.error("Failed to parse reports"),process.exit(1));let o=ne(W(y,"yyyy-MM-dd",new Date),W(c,"yyyy-MM-dd",new Date)),s=g.summary.stale-d.summary.stale,h=g.summary.major-d.summary.major,f=new Set(d.packages.map(x=>x.name)),b=new Set(g.packages.map(x=>x.name)),u=g.packages.filter(x=>!f.has(x.name)),$=d.packages.filter(x=>!b.has(x.name)),E=g.packages.filter(x=>{let _=d.packages.find(G=>G.name===x.name);return _&&_.current!==x.current}),M=Oe(d.summary),S=Oe(g.summary),N=M>0?(S-M)/M*100:0;if(console.log(`
|
|
401
|
+
Dependency Health Comparison`),console.log(`From: ${c} \u2192 ${y} (${o} days)
|
|
402
|
+
`),E.length>0){console.log("\u{1F4C8} Improvements:");for(let x of E.slice(0,5)){let _=d.packages.find(G=>G.name===x.name);console.log(` \u2022 ${x.name}: ${_?.current} \u2192 ${x.current}`)}E.length>5&&console.log(` ... and ${E.length-5} more`),console.log("")}if(s!==0||h!==0){if(console.log("\u{1F4CA} Metrics:"),s!==0){let x=s>0?"+":"";console.log(` \u2022 Stale packages: ${d.summary.stale} \u2192 ${g.summary.stale} (${x}${s})`)}if(h!==0){let x=h>0?"+":"";console.log(` \u2022 Major upgrades pending: ${d.summary.major} \u2192 ${g.summary.major} (${x}${h})`)}console.log("")}u.length>0&&console.log(`\u2795 Added: ${u.map(x=>x.name).join(", ")}`),$.length>0&&console.log(`\u2796 Removed: ${$.map(x=>x.name).join(", ")}`),(u.length>0||$.length>0)&&console.log("");let Le=N>0?"\u2705":N<0?"\u26A0\uFE0F":"\u27A1\uFE0F",Ue=N>0?"improved":N<0?"regressed":"unchanged";console.log(`${Le} Overall: Health ${Ue} by ${Math.abs(N).toFixed(1)}%`),console.log(` Score: ${M.toFixed(1)} \u2192 ${S.toFixed(1)}
|
|
403
|
+
`),process.exit(N<0?1:0)}var ht="https://registry.npmjs.org";async function Ne(e=ht,t=5e3){let r=new AbortController,n=setTimeout(()=>r.abort(),t);try{let i=`${e}/lodash`;return(await fetch(i,{headers:{Accept:"application/json"},signal:r.signal})).ok}catch{return!1}finally{clearTimeout(n)}}import{readFileSync as yt,existsSync as bt}from"fs";import{join as kt}from"path";function Y(e=process.cwd()){let t=kt(e,"package.json");if(!bt(t))return 0;try{let r=yt(t,"utf-8"),n=JSON.parse(r),i=Object.keys(n.dependencies||{}).length,m=Object.keys(n.devDependencies||{}).length,a=Object.keys(n.peerDependencies||{}).length,w=Object.keys(n.optionalDependencies||{}).length;return i+m+a+w}catch{return 0}}var z=new $t,St=new URL("../package.json",import.meta.url),Pt=JSON.parse(wt(St,"utf-8")),xt=Pt.version??"0.0.0";z.name("dep-report").description("Generate dependency risk reports").version(xt).addHelpText("before",`
|
|
404
|
+
dep-report - Generate dependency risk reports
|
|
405
|
+
|
|
406
|
+
USAGE
|
|
407
|
+
dep-report [options]
|
|
408
|
+
|
|
409
|
+
DESCRIPTION
|
|
410
|
+
Scans for outdated packages and generates a daily risk brief
|
|
411
|
+
showing age, staleness, and major upgrades. Reports are
|
|
412
|
+
version-controlled in .dep-report/reports/
|
|
413
|
+
|
|
414
|
+
QUICK START
|
|
415
|
+
dep-report # Run audit, generate reports
|
|
416
|
+
dep-report init # Create config files
|
|
417
|
+
`).addHelpText("after",`
|
|
418
|
+
EXAMPLES
|
|
419
|
+
dep-report # Daily audit
|
|
420
|
+
dep-report init # Initialize configuration
|
|
421
|
+
|
|
422
|
+
LEARN MORE
|
|
423
|
+
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)||(l.error(`Invalid preset: ${e.preset}. Valid options: starter, production, strict`),process.exit(1)),r=e.preset),await Me(process.cwd(),e.includeConfig,r),process.exit(0)}catch(t){l.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 Te(e,t)}catch(r){l.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=Se(t);l.info("Checking prerequisites..."),await ye(t);let n=v(t,".dep-report");await be(n),l.info("Detecting package manager...");let i=se(t);i||(l.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)),l.success(`Detected: ${i.manager}`),l.info("Scanning for outdated packages...");let m=await ie(i.manager,t);if(Object.keys(m).length===0){if(l.success("No outdated packages found!"),r.reportEmptyState){let o=v(t,".dep-report","reports");Ie(o)||Ae(o,{recursive:!0});let s=Fe(new Date,"yyyy-MM-dd"),h=Y(t);if(r.formats.markdown){let f=Q([],new Date,h);T(v(o,`${s}_outdated.md`),f),T(v(o,"latest.md"),f),l.success(`Report generated: .dep-report/reports/${s}_outdated.md`)}if(r.formats.html){let f=ee([],new Date,h);T(v(o,`${s}_outdated.html`),f),T(v(o,"latest.html"),f),l.success(`HTML report generated: .dep-report/reports/${s}_outdated.html`)}}process.exit(0)}l.info(`Found ${Object.keys(m).length} outdated packages`);let a=ce(m);l.startSpinner("Checking registry connectivity...");let w=await Ne();l.stopSpinner(),w||(l.error("Unable to reach the npm registry."),l.info("If you have a cache, try running with --refresh."),process.exit(1)),l.success("Registry connectivity confirmed"),l.startSpinner(`Enriching ${a.length} packages with registry metadata...`);let p=await le(a,r.concurrency);l.stopSpinner(),l.success("Enrichment complete");let k=Pe(r.staleThreshold),c=ue(p,k),y=c.length;c=Ee(c,r.ignorePatterns),y>c.length&&l.info(`Filtered out ${y-c.length} packages based on ignorePatterns`);let d=xe(t);if(c=je(c,d),e.dryRun){let o=Y(t),s=F(c,o),h=typeof e.dryRun=="string"?e.dryRun:"actions";if(console.log(`
|
|
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: ${s.riskStatusEmoji} ${s.riskStatusText}
|
|
426
|
+
`),console.log(`Total dependencies: ${s.total}`),console.log(`Outdated: ${s.outdated} (${s.major} major, ${s.minor} minor, ${s.patch} patch)`),console.log(`Stale (>12 months): ${s.stale}
|
|
427
|
+
`),h==="actions"||h==="full"){let b=c.map(u=>({pkg:u,score:O(u)})).filter(({score:u})=>u>15).sort((u,$)=>$.score-u.score).slice(0,7).map(({pkg:u})=>u);if(b.length>0){console.log("Action Required:");for(let u of b){let $=u.age!==null?`${u.age}d`:"Unknown",E=u.behindByDays!==null?`${u.behindByDays}d`:"\u2014",M=u.note?I(u.note):"",S=u.risk==="Major"?"\u{1F534}":u.risk==="Minor"?"\u{1F7E1}":"\u{1F7E2}";console.log(` ${S} ${u.name} (${$}, behind by ${E}) - ${u.risk} ${u.current} \u2192 ${u.latest}${M?`, ${M}`:""}`)}console.log("")}}if(h==="full"){console.log("Full Dependency List:");for(let b of c){let u=b.age!==null?`${b.age}d`:"Unknown",$=b.behindByDays!==null?`${b.behindByDays}d`:"\u2014",E=A(b)?"\u2705 Stable":"Outdated";console.log(` ${b.name}: ${b.current} \u2192 ${b.latest} (${u}, ${$}, ${b.risk}, ${E})`)}console.log("")}console.log(`[No files written]
|
|
428
|
+
`);let f=!1;r.failConditions.stale&&c.some(u=>u.isStale)&&(l.error("Found stale packages (--fail-if-stale)"),f=!0),r.failConditions.major&&c.some(u=>u.risk==="Major")&&(l.error("Found major version updates (--fail-if-major)"),f=!0),process.exit(f?1:0)}if(r.formats.markdown||r.formats.html){l.info("Generating reports...");let o=v(t,".dep-report","reports");Ie(o)||Ae(o,{recursive:!0});let s=Fe(new Date,"yyyy-MM-dd"),h=Y(t);if(r.formats.markdown){let f=Q(c,new Date,h);T(v(o,`${s}_outdated.md`),f),T(v(o,"latest.md"),f),l.success(`Report generated: .dep-report/reports/${s}_outdated.md`),l.success("Latest report: .dep-report/reports/latest.md")}if(r.formats.html){let f=ee(c,new Date,h);T(v(o,`${s}_outdated.html`),f),T(v(o,"latest.html"),f),l.success(`HTML report generated: .dep-report/reports/${s}_outdated.html`),l.success("Latest HTML report: .dep-report/reports/latest.html")}}let g=!1;r.failConditions.stale&&c.some(s=>s.isStale)&&(l.error("Found stale packages (--fail-if-stale)"),g=!0),r.failConditions.major&&c.some(s=>s.risk==="Major")&&(l.error("Found major version updates (--fail-if-major)"),g=!0),process.exit(g?1:0)}catch(t){l.error(t instanceof Error?t.message:String(t)),process.exit(1)}});z.parse();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a,b,c,d}from"./chunk-KNT3JZ7P.js";export{a as PRESETS,b as getPreset,d as isValidPresetName,c as listPresets};
|