react-prune 1.2.1 โ†’ 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  > **Static analysis for identifying unused files and package usage in React-based codebases.**
8
8
 
9
- `react-prune` is a lightweight CLI tool that analyzes your React, Next.js, and React Native projects to surface **unused local files** and **package import usage**, helping you reduce dead code and dependency bloat.
9
+ `react-prune` is a lightweight CLI tool that analyzes your React, Next.js, Vite, and React Native projects to surface **unused local files**, **unused exports**, and **package import usage**, helping you reduce dead code and dependency bloat.
10
10
 
11
11
  ---
12
12
 
@@ -15,17 +15,23 @@
15
15
  - **๐Ÿ“ฆ Package Usage Analysis**
16
16
  Counts how often each external npm package is imported across your codebase.
17
17
 
18
+ - **๐Ÿšซ Unused Dependency Detection**
19
+ Leverages `depcheck` to identify dependencies in `package.json` that are completely unused.
20
+
18
21
  - **โš–๏ธ Optional Package Size Estimation**
19
22
  Estimates package sizes from `node_modules` to highlight heavy dependencies.
20
23
 
21
24
  - **๐Ÿงน Unused File Detection**
22
25
  Identifies local source files that are never imported anywhere in the project.
23
26
 
24
- - **โš›๏ธ React Ecosystem Support**
25
- Works with React, Next.js (Pages & App Router), and React Native projects.
27
+ - **๐Ÿ“ Unused Export Detection**
28
+ Finds named exports (functions, components, constants) that are defined but never used.
29
+
30
+ - **๐Ÿ” Export/Component Search**
31
+ Search for a component or function to see **how many times itโ€™s used** and **all file references with line numbers**.
26
32
 
27
33
  - **๐Ÿ“Š CLI-Friendly Output**
28
- Displays results in readable tables or as structured JSON for automation.
34
+ Displays results in readable tables.
29
35
 
30
36
  ---
31
37
 
@@ -61,29 +67,78 @@ npx react-prune
61
67
 
62
68
  ## ๐Ÿ›  Usage
63
69
 
64
- Run from the **root of your project**:
70
+ Run from the **root of your project**.
71
+
72
+ ### Analyze the project
65
73
 
66
74
  ```bash
67
- react-prune
75
+ react-prune analyze
68
76
  ```
69
77
 
70
- ### Options
78
+ #### Options
79
+
80
+ | Option | Description |
81
+ | -------------- | ------------------------------------------ |
82
+ | `--json` | Output the report as JSON |
83
+ | `--no-size` | Skip package size calculation |
84
+ | `--no-exports` | Skip unused export analysis |
85
+ | `--limit <n>` | Limit displayed package rows (default: 50) |
86
+
87
+ ---
88
+
89
+ ### Search for a component or export
90
+
91
+ ```bash
92
+ react-prune find <exportName>
93
+ ```
71
94
 
72
- | Option | Description |
73
- | ------------- | ------------------------------------------ |
74
- | `--json` | Output the report as JSON |
75
- | `--no-size` | Skip package size calculation |
76
- | `--limit <n>` | Limit displayed package rows (default: 50) |
95
+ Shows usage count and **file size** of each occurrence.
77
96
 
78
- ### Example
97
+ #### Example
79
98
 
80
99
  ```bash
81
- react-prune --limit 20
100
+ react-prune find Button
101
+ ```
102
+
103
+ Output:
104
+
105
+ ```
106
+ 'Button' is used 4 time(s):
107
+ - src/components/Header.tsx:12
108
+ - src/components/Footer.tsx:8
109
+ - src/pages/index.tsx:22
110
+ - src/pages/about.tsx:15
82
111
  ```
83
112
 
84
113
  ---
85
114
 
86
- ## ๐Ÿ“Š Example Output
115
+ ### Check the size of a specific npm package
116
+
117
+ ```bash
118
+ react-prune size <packageName>
119
+ ```
120
+
121
+ #### Example
122
+
123
+ ```bash
124
+ react-prune size react
125
+ ```
126
+
127
+ Output:
128
+
129
+ ```
130
+ ๐Ÿ“ฆ react size: 312 KB
131
+ ```
132
+
133
+ If the package is not installed:
134
+
135
+ ```
136
+ Package 'some-package' not found in node_modules.
137
+ ```
138
+
139
+ ---
140
+
141
+ ## ๐Ÿ“Š Example Output (Analyze)
87
142
 
88
143
  ```text
89
144
  โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
@@ -104,6 +159,14 @@ react-prune --limit 20
104
159
 
105
160
  src/components/OldButton.tsx
106
161
  src/utils/deprecated-helper.ts
162
+
163
+ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
164
+ โ”‚ โš ๏ธ Unused Exports (3) โ”‚
165
+ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
166
+
167
+ src/hooks/useMetrics.ts useOldMetric
168
+ src/utils/formatters.ts formatCurrency
169
+ src/components/OldButton.tsx OldButton
107
170
  ```
108
171
 
109
172
  ---
@@ -114,28 +177,26 @@ src/utils/deprecated-helper.ts
114
177
  Recursively scans `.js`, `.jsx`, `.ts`, and `.tsx` files (excluding `node_modules`, `.next`, `dist`, etc.).
115
178
 
116
179
  2. **AST Parsing**
117
- Uses `ts-morph` to parse TypeScript/JavaScript ASTs for accurate import analysis.
180
+ Uses `ts-morph` to parse TypeScript/JavaScript ASTs for accurate import/export analysis.
118
181
 
119
182
  3. **Dependency Resolution**
120
183
  Differentiates between local file imports and external package imports.
121
184
 
122
185
  4. **Static Usage Mapping**
123
- Tracks which files and packages are actually referenced in the project.
186
+ Tracks which files, exports, and packages are actually referenced in the project.
124
187
 
125
188
  ---
126
189
 
127
- ## โš ๏ธ Limitations (Important)
190
+ ## โš ๏ธ Limitations
128
191
 
129
192
  - This is **static analysis** โ€” dynamic imports and runtime usage may not be detected.
130
- - Files referenced only via tooling configuration (e.g. Storybook, tests, build scripts) may appear unused.
131
- - Package size estimates are approximate and based on disk size, not bundle size.
193
+ - Files referenced only via tooling (e.g., Storybook, tests) may appear unused.
194
+ - Package size estimates are **disk-based**, not bundle size.
132
195
 
133
196
  ---
134
197
 
135
198
  ## ๐Ÿค Contributing
136
199
 
137
- Contributions are welcome.
138
-
139
200
  ```bash
140
201
  git clone https://github.com/danieljohnson18/react-prune.git
141
202
  cd react-prune
@@ -146,7 +207,9 @@ npm run dev
146
207
  Test locally:
147
208
 
148
209
  ```bash
149
- node dist/cli.js
210
+ node dist/cli.js analyze
211
+ node dist/cli.js find Button
212
+ node dist/cli.js size react
150
213
  ```
151
214
 
152
215
  ---
package/dist/cli.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';var commander=require('commander'),j=require('picocolors'),U=require('cli-table3'),F=require('boxen'),tsMorph=require('ts-morph'),M=require('fast-glob'),h=require('path'),k=require('fs');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var j__default=/*#__PURE__*/_interopDefault(j);var U__default=/*#__PURE__*/_interopDefault(U);var F__default=/*#__PURE__*/_interopDefault(F);var M__default=/*#__PURE__*/_interopDefault(M);var h__default=/*#__PURE__*/_interopDefault(h);var k__default=/*#__PURE__*/_interopDefault(k);var O=(o,t)=>()=>(t||o((t={exports:{}}).exports,t),t.exports);var T=O((X,K)=>{K.exports={name:"react-prune",version:"1.2.1",main:"dist/index.js",bin:{"react-prune":"./dist/cli.js"},scripts:{build:"tsup src/cli.ts --format cjs --dts",dev:"tsup src/cli.ts --watch",lint:"eslint src/**",format:"prettier --write .",prepublishOnly:"npm run build",release:"changeset publish",test:"vitest"},publishConfig:{access:"public"},keywords:["react","react-native","nextjs","analysis","dead-code","imports","dependency-analysis","bundle-size","prune","typescript","developer-tools","cli"],author:"Daniel Arikawe",license:"MIT",files:["dist"],repository:{type:"git",url:"git+https://github.com/danieljohnson18/react-prune.git"},engines:{node:">=18"},bugs:{url:"https://github.com/danieljohnson18/react-prune/issues"},homepage:"https://github.com/danieljohnson18/react-prune#readme",description:"A powerful CLI tool to monitor package usage, analyze component imports, and detect dead code across React, Next.js, and React Native applications.",dependencies:{boxen:"^8.0.1","cli-table3":"^0.6.5",commander:"^14.0.2","fast-glob":"^3.3.3",picocolors:"^1.1.1","ts-morph":"^27.0.2"},devDependencies:{"@changesets/cli":"^2.29.8","@types/glob":"^8.1.0","@types/node":"^25.0.10",eslint:"^9.39.2",prettier:"^3.8.1",tsup:"^8.5.1",typescript:"^5.9.3",vitest:"^4.0.18"}};});function C(o){let t=0;try{let i=k__default.default.readdirSync(o);for(let n of i){let a=h__default.default.join(o,n),r=k__default.default.statSync(a);r.isDirectory()?t+=C(a):t+=r.size;}}catch{return 0}return t}function $(o,t=2){if(o===0)return "0 Bytes";let i=1024,n=t<0?0:t,a=["Bytes","KB","MB","GB","TB"],r=Math.floor(Math.log(o)/Math.log(i));return parseFloat((o/Math.pow(i,r)).toFixed(n))+" "+a[r]}function I(o,t){let i=h__default.default.join(o,"node_modules",t);if(k__default.default.existsSync(i)){let n=C(i);return $(n)}return "N/A"}async function D(o){let{rootPath:t,includeSizes:i=true,analyzeExports:n=true}=o;console.log(j__default.default.green(`Analyzing project at ${t}`));let a=await M__default.default("**/*.{js,jsx,ts,tsx,ios.tsx,android.tsx,native.tsx}",{cwd:t,ignore:["**/node_modules/**","**/dist/**","**/build/**","**/.next/**","**/coverage/**","**/*.config.{js,ts,cjs,mjs}","**/*.d.ts"],absolute:true});console.log(j__default.default.blue(`Found ${a.length} files to analyze.`));let r=h__default.default.join(t,"tsconfig.json"),c={skipAddingFilesFromTsConfig:true};k__default.default.existsSync(r)&&(c.tsConfigFilePath=r);let d=new tsMorph.Project(c);a.forEach(e=>{try{d.addSourceFileAtPath(e);}catch(s){console.warn(j__default.default.yellow(`Skipping file ${e}:`),s);}});let m={},y={};a.forEach(e=>{let s=h__default.default.relative(t,e).replace(/\\/g,"/");y[s]=0;});for(let e of d.getSourceFiles()){let s=e.getImportDeclarations();for(let x of s){let l=x.getModuleSpecifierValue();if(l.startsWith(".")||l.startsWith("/")){let u=h__default.default.dirname(e.getFilePath()),g=h__default.default.resolve(u,l),p=["",".ts",".tsx",".js",".jsx","/index.ts","/index.tsx","/index.js","/index.jsx"];for(let f of p){let S=g+f,z=h__default.default.relative(t,S).replace(/\\/g,"/");if(y.hasOwnProperty(z)){y[z]++;break}}}else {let u=l.startsWith("@")?l.split("/").slice(0,2).join("/"):l.split("/")[0];m[u]=(m[u]||0)+1;}}let P=e.getDescendantsOfKind(tsMorph.SyntaxKind.CallExpression);for(let x of P)if(x.getExpression().getText()==="require"){let u=x.getArguments();if(u.length&&u[0].getKind()===tsMorph.SyntaxKind.StringLiteral){let g=u[0].getText().replace(/['"`]/g,"");if(!g.startsWith(".")&&!g.startsWith("/")){let p=g.startsWith("@")?g.split("/").slice(0,2).join("/"):g.split("/")[0];m[p]=(m[p]||0)+1;}}}}let E={};for(let[e,s]of Object.entries(m))E[e]={count:s,size:i?I(t,e):"\u2014"};let W=Object.entries(y).filter(([e,s])=>e.includes("pages/")||e.includes("app/")||e.endsWith("main.tsx")||e.endsWith("index.tsx")||e.endsWith("index.js")||e.endsWith("App.tsx")||e.endsWith("App.js")||!e.includes(h__default.default.sep)&&!e.endsWith(".ts")&&!e.endsWith(".tsx")&&!e.endsWith(".js")&&!e.endsWith(".jsx")?false:s===0).map(([e])=>e),A={};if(n)for(let e of d.getSourceFiles()){let s=e.getFilePath(),P=h__default.default.relative(t,s).replace(/\\/g,"/");if(s.includes("pages/")||s.includes("app/")||s.endsWith("main.tsx")||s.endsWith("index.tsx")||s.endsWith("index.js")||s.endsWith("App.tsx")||s.endsWith("App.js")||W.includes(s))continue;let x=e.getExportedDeclarations(),l=[];for(let[u,g]of x){let p=false;for(let f of g){if(tsMorph.Node.isFunctionDeclaration(f)||tsMorph.Node.isClassDeclaration(f)||tsMorph.Node.isVariableDeclaration(f)||tsMorph.Node.isEnumDeclaration(f)||tsMorph.Node.isInterfaceDeclaration(f)||tsMorph.Node.isTypeAliasDeclaration(f))try{let S=f.findReferences();for(let z of S){for(let N of z.getReferences())if(N.getSourceFile().getFilePath()!==s){p=!0;break}if(p)break}}catch{p=true;}else p=true;if(p)break}p||l.push(u);}l.length&&(A[P]=l);}return {packages:E,components:y,unusedFiles:W,unusedExports:A}}var v=new commander.Command;v.name("react-prune").description("Analyze React/Next/Vite/React Native codebases for unused files, exports, and packages").version(T().version).option("--no-size","Skip calculating package sizes").option("--no-exports","Skip analyzing unused exports").option("--limit <n>","Limit output rows","50");v.action(async o=>{let t=process.cwd(),i=Number(o.limit),n=await D({rootPath:t,includeSizes:o.size,analyzeExports:o.exports});console.log(F__default.default(j__default.default.bold("\u{1F4E6} Package Usage"),{padding:1,borderColor:"green",borderStyle:"round"}));let a=new U__default.default({head:["Package","Count","Size"]});Object.entries(n.packages).sort((c,d)=>d[1].count-c[1].count).slice(0,i).forEach(([c,d])=>a.push([c,d.count,d.size])),console.log(a.toString()),n.unusedFiles.length?(console.log(F__default.default(j__default.default.bold(`\u26A0\uFE0F Unused Files (${n.unusedFiles.length})`),{padding:1,borderColor:"yellow"})),n.unusedFiles.forEach(c=>console.log(j__default.default.yellow(c)))):console.log(F__default.default(j__default.default.green("\u2705 No unused files detected!"),{padding:1,borderColor:"green"}));let r=Object.entries(n.unusedExports);if(r.length){console.log(F__default.default(j__default.default.bold(`\u26A0\uFE0F Potential Unused Exports (${r.length})`),{padding:1,borderColor:"yellow"}));let c=new U__default.default({head:["File","Unused Exports"],wordWrap:true});r.slice(0,i).forEach(([d,m])=>{c.push([d,m.join(", ")]);}),console.log(c.toString());}});v.parse();
2
+ 'use strict';var b=require('path'),commander=require('commander'),f=require('picocolors'),F=require('cli-table3'),E=require('boxen'),tsMorph=require('ts-morph'),V=require('fast-glob'),k=require('fs');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var b__default=/*#__PURE__*/_interopDefault(b);var f__default=/*#__PURE__*/_interopDefault(f);var F__default=/*#__PURE__*/_interopDefault(F);var E__default=/*#__PURE__*/_interopDefault(E);var V__default=/*#__PURE__*/_interopDefault(V);var k__default=/*#__PURE__*/_interopDefault(k);var T=Object.defineProperty;var P=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,s)=>(typeof require<"u"?require:t)[s]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+e+'" is not supported')});var L=(e,t)=>()=>(e&&(t=e(e=0)),t);var _=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),q=(e,t)=>{for(var s in t)T(e,s,{get:t[s],enumerable:true});};var I=_((fe,X)=>{X.exports={name:"react-prune",version:"1.3.0",main:"dist/index.js",bin:{"react-prune":"./dist/cli.js"},scripts:{build:"tsup src/cli.ts --format cjs --dts",dev:"tsup src/cli.ts --watch",lint:"eslint src/**",format:"prettier --write .",prepublishOnly:"npm run build",release:"changeset publish",test:"vitest"},publishConfig:{access:"public"},keywords:["react","react-native","nextjs","analysis","dead-code","imports","dependency-analysis","bundle-size","prune","typescript","developer-tools","cli"],author:"Daniel Arikawe",license:"MIT",files:["dist"],repository:{type:"git",url:"git+https://github.com/danieljohnson18/react-prune.git"},engines:{node:">=18"},bugs:{url:"https://github.com/danieljohnson18/react-prune/issues"},homepage:"https://github.com/danieljohnson18/react-prune#readme",description:"A powerful CLI tool to monitor package usage, analyze component imports, and detect dead code across React, Next.js, and React Native applications.",dependencies:{boxen:"^8.0.1","cli-table3":"^0.6.5",commander:"^14.0.2",depcheck:"^1.4.7","fast-glob":"^3.3.3",picocolors:"^1.1.1","ts-morph":"^27.0.2"},devDependencies:{"@changesets/cli":"^2.29.8","@types/depcheck":"^0.9.0","@types/glob":"^8.1.0","@types/node":"^25.0.10",eslint:"^9.39.2",prettier:"^3.8.1",tsup:"^8.5.1",typescript:"^5.9.3",vitest:"^4.0.18"}};});var N={};q(N,{getPackageSize:()=>Z});function Z(e,t){try{let s=P(b__default.default.join(e,"node_modules",t,"package.json"));if(typeof s.size=="number")return `${(s.size/1024).toFixed(2)} KB`}catch{}}var B=L(()=>{});function C(e){let t=0;try{let s=k__default.default.readdirSync(e);for(let i of s){let l=b__default.default.join(e,i),o=k__default.default.statSync(l);o.isDirectory()?t+=C(l):t+=o.size;}}catch{}return t}function G(e,t=2){if(e===0)return "0 Bytes";let s=1024,i=t<0?0:t,l=["Bytes","KB","MB","GB","TB"],o=Math.floor(Math.log(e)/Math.log(s));return parseFloat((e/Math.pow(s,o)).toFixed(i))+" "+l[o]}function H(e,t){let s=b__default.default.join(e,"node_modules",t);return k__default.default.existsSync(s)?G(C(s)):"N/A"}async function v(e){let{rootPath:t,includeSizes:s=true,analyzeExports:i=true,silent:l=false}=e;l||console.log(f__default.default.green(`Analyzing project at ${t}`));let o=await V__default.default("**/*.{js,jsx,ts,tsx}",{cwd:t,ignore:["**/node_modules/**","**/dist/**","**/build/**","**/.next/**","**/coverage/**","**/*.config.{js,ts,cjs,mjs}","**/*.d.ts"],absolute:true});l||console.log(f__default.default.blue(`Found ${o.length} files to analyze.`));let r=new tsMorph.Project({skipAddingFilesFromTsConfig:true}),m=b__default.default.join(t,"tsconfig.json");k__default.default.existsSync(m)&&r.addSourceFilesFromTsConfig(m),o.forEach(n=>{try{r.addSourceFileAtPath(n);}catch{console.warn(f__default.default.yellow(`Skipping ${n}`));}});let p={},h={},d={},D={};for(let n of r.getSourceFiles()){let a=b__default.default.relative(t,n.getFilePath()).replace(/\\/g,"/");D[a]=n;let y=n.getExportedDeclarations(),g=new Set;y.forEach((S,c)=>{c!=="default"&&g.add(c);});let x=y.has("default");h[a]={named:g,default:x},d[a]=new Set,n.getImportDeclarations().forEach(S=>{let c=S.getModuleSpecifierValue();if(!c.startsWith(".")&&!c.startsWith("/")){let u=c.startsWith("@")?c.split("/").slice(0,2).join("/"):c.split("/")[0];p[u]=(p[u]||0)+1;}}),n.getDescendantsOfKind(tsMorph.SyntaxKind.CallExpression).forEach(S=>{if(S.getExpression().getText()==="require"){let c=S.getArguments();if(c.length&&c[0].getKind()===tsMorph.SyntaxKind.StringLiteral){let u=c[0].getText().replace(/['"`]/g,"");if(!u.startsWith(".")&&!u.startsWith("/")){let z=u.startsWith("@")?u.split("/").slice(0,2).join("/"):u.split("/")[0];p[z]=(p[z]||0)+1;}}}});}if(i)for(let n of r.getSourceFiles()){let a=n.getImportDeclarations();b__default.default.relative(t,n.getFilePath()).replace(/\\/g,"/");a.forEach(g=>{let x=g.getModuleSpecifierValue();if(x.startsWith(".")||x.startsWith("/")){let A=b__default.default.dirname(n.getFilePath()),W=b__default.default.resolve(A,x),S=["",".ts",".tsx",".js",".jsx","/index.ts","/index.tsx","/index.js","/index.jsx"],c;for(let u of S){let z=b__default.default.relative(t,W+u).replace(/\\/g,"/");if(h[z]){c=z;break}}c&&(g.getNamedImports().forEach(u=>d[c].add(u.getName())),g.getDefaultImport()&&d[c].add("default"));}});}let K=Object.entries(h).filter(([n,a])=>{if(n.includes("pages/")||n.includes("app/")||n.endsWith("index.tsx")||n.endsWith("App.tsx"))return false;let y=d[n];return a.named.size===0&&!a.default?false:y.size===0}).map(([n])=>n),R={};i&&Object.entries(h).forEach(([n,a])=>{let y=d[n],g=[];a.default&&!y.has("default")&&g.push("default"),a.named.forEach(x=>{y.has(x)||g.push(x);}),g.length&&(R[n]=g);});let $={};Object.entries(p).forEach(([n,a])=>{$[n]={count:a,size:s?H(t,n):"\u2014"};});let O=[];try{O=(await P("depcheck")(t,{ignoreMatches:["react-prune",...e.ignoreMatches||[]],skipMissing:!0})).dependencies;}catch{l||console.warn(f__default.default.yellow("Failed to run depcheck."));}return {packages:$,unusedFiles:K,unusedExports:R,usedExports:d,unusedDependencies:O,sourceFiles:D}}function M(e){try{let s=k__default.default.statSync(e).size;return s<1024?`${s} B`:`${(s/1024).toFixed(2)} KB`}catch{return "N/A"}}var j=new commander.Command;j.name("react-prune").description("Analyze React/Next/Vite/React Native projects").version(I().version).option("--no-size","Skip package size calculation").option("--no-exports","Skip export usage analysis").option("--limit <n>","Limit output rows per table","50");j.command("analyze").description("Run full project analysis").option("--no-size","Skip package size calculation").option("--no-exports","Skip export usage analysis").option("--limit <n>","Limit output rows per table","50").option("--json","Output results as JSON").action(async e=>{let t=process.cwd(),s=Number(e.limit),i=await v({rootPath:t,includeSizes:e.size,analyzeExports:e.exports,silent:e.json});if(e.json){let{sourceFiles:o,usedExports:r,...m}=i,p={};if(r)for(let[h,d]of Object.entries(r))p[h]=Array.from(d);console.log(JSON.stringify({...m,usedExports:p},null,2));return}let l=new F__default.default({head:[f__default.default.cyan("Package"),"Count","Size"],colWidths:[40,10,15]});if(Object.entries(i.packages).sort((o,r)=>r[1].count-o[1].count).slice(0,s).forEach(([o,r])=>l.push([o,r.count,r.size])),console.log(E__default.default(f__default.default.bold("\u{1F4E6} Package Usage"),{padding:1,borderColor:"green",borderStyle:"round"})),console.log(l.toString()),i.unusedFiles.length){let o=new F__default.default({head:[f__default.default.yellow("Unused Files")],colWidths:[80]});i.unusedFiles.slice(0,s).forEach(r=>o.push([r])),console.log(E__default.default(f__default.default.bold(`\u26A0\uFE0F Unused Files (${i.unusedFiles.length})`),{padding:1,borderColor:"yellow",borderStyle:"round"})),console.log(o.toString());}if(e.exports){let o=Object.entries(i.unusedExports);if(o.length){let r=new F__default.default({head:["File","Unused Exports"],colWidths:[50,40],wordWrap:true});o.slice(0,s).forEach(([m,p])=>r.push([m,p.join(", ")])),console.log(E__default.default(f__default.default.bold("\u26A0\uFE0F Unused Exports"),{padding:1,borderColor:"yellow",borderStyle:"round"})),console.log(r.toString());}}if(i.unusedDependencies&&i.unusedDependencies.length){let o=new F__default.default({head:[f__default.default.yellow("Unused Dependencies")],colWidths:[80]});i.unusedDependencies.slice(0,s).forEach(r=>o.push([r])),console.log(E__default.default(f__default.default.bold(`\u26A0\uFE0F Unused Dependencies (${i.unusedDependencies.length})`),{padding:1,borderColor:"yellow",borderStyle:"round"})),console.log(o.toString());}});j.command("size <packageName>").description("Check the size of a specific npm package in node_modules").action(async e=>{let t=process.cwd(),{getPackageSize:s}=await Promise.resolve().then(()=>(B(),N)),i=s(t,e);console.log(i==="N/A"?f__default.default.yellow(`Package '${e}' not found in node_modules.`):f__default.default.green(`\u{1F4E6} ${e} size: ${i}`));});j.command("find <exportName>").description("Find usage count and references (with line numbers) for a component/function/export").action(async e=>{let t=process.cwd(),s=await v({rootPath:t,analyzeExports:true,includeSizes:false}),i=[],l=s.usedExports||{};for(let[o,r]of Object.entries(l)){let m=s.sourceFiles?.[o];if(!m||!r.has(e))continue;m.getDescendantsOfKind(tsMorph.SyntaxKind.Identifier).forEach(h=>{if(h.getText()===e){let d=h.getParentOrThrow().getKindName();(d.includes("Import")||d.includes("PropertyAccess")||d.includes("Identifier"))&&i.push({file:o,line:h.getStartLineNumber()});}});}i.length?(console.log(f__default.default.green(`'${e}' is used ${i.length} time(s):`)),i.forEach(o=>{let r=M(b__default.default.join(t,o.file));console.log(` - ${o.file}:${o.line} (${r})`);})):console.log(f__default.default.yellow(`'${e}' is not used anywhere.`));});j.parse(process.argv);
3
3
  //# sourceMappingURL=cli.js.map
4
4
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../package.json","../src/analyzer/index.ts","../src/cli.ts"],"names":["require_package","__commonJSMin","exports","module","getFolderSize","dirPath","size","files","fs","file","filePath","path","stats","formatBytes","bytes","decimals","k","dm","sizes","i","getPackageSize","rootPath","packageName","pkgPath","analyzeProject","options","includeSizes","analyzeExports","pc","glob","tsConfigPath","projectConfig","project","Project","e","packageUsage","localUsage","f","relative","sourceFile","imports","imp","moduleSpecifier","sourceDir","resolvedPath","extensions","ext","tryPath","relativeTry","callExpressions","SyntaxKind","call","args","rawArg","pkg","reportPackages","count","unused","unusedExports","relativePath","exportedDeclarations","fileUnusedExports","name","declarations","isUsed","decl","Node","refs","ref","entry","program","Command","opts","limit","report","boxen","packageTable","Table","a","b","data","unusedExportsEntries","exportsTable"],"mappings":"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","file":"cli.js","sourcesContent":["{\n \"name\": \"react-prune\",\n \"version\": \"1.2.1\",\n \"main\": \"dist/index.js\",\n \"bin\": {\n \"react-prune\": \"./dist/cli.js\"\n },\n \"scripts\": {\n \"build\": \"tsup src/cli.ts --format cjs --dts\",\n \"dev\": \"tsup src/cli.ts --watch\",\n \"lint\": \"eslint src/**\",\n \"format\": \"prettier --write .\",\n \"prepublishOnly\": \"npm run build\",\n \"release\": \"changeset publish\",\n \"test\": \"vitest\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"keywords\": [\n \"react\",\n \"react-native\",\n \"nextjs\",\n \"analysis\",\n \"dead-code\",\n \"imports\",\n \"dependency-analysis\",\n \"bundle-size\",\n \"prune\",\n \"typescript\",\n \"developer-tools\",\n \"cli\"\n ],\n \"author\": \"Daniel Arikawe\",\n \"license\": \"MIT\",\n \"files\": [\n \"dist\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/danieljohnson18/react-prune.git\"\n },\n \"engines\": {\n \"node\": \">=18\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/danieljohnson18/react-prune/issues\"\n },\n \"homepage\": \"https://github.com/danieljohnson18/react-prune#readme\",\n \"description\": \"A powerful CLI tool to monitor package usage, analyze component imports, and detect dead code across React, Next.js, and React Native applications.\",\n \"dependencies\": {\n \"boxen\": \"^8.0.1\",\n \"cli-table3\": \"^0.6.5\",\n \"commander\": \"^14.0.2\",\n \"fast-glob\": \"^3.3.3\",\n \"picocolors\": \"^1.1.1\",\n \"ts-morph\": \"^27.0.2\"\n },\n \"devDependencies\": {\n \"@changesets/cli\": \"^2.29.8\",\n \"@types/glob\": \"^8.1.0\",\n \"@types/node\": \"^25.0.10\",\n \"eslint\": \"^9.39.2\",\n \"prettier\": \"^3.8.1\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^5.9.3\",\n \"vitest\": \"^4.0.18\"\n }\n}\n","import { Project, SyntaxKind, Node } from \"ts-morph\";\nimport glob from \"fast-glob\";\nimport path from \"path\";\nimport fs from \"fs\";\nimport pc from \"picocolors\";\n\nexport interface UsageReport {\n packages: Record<string, { count: number; size: string }>;\n components: Record<string, number>;\n unusedFiles: string[];\n unusedExports: Record<string, string[]>;\n}\n\nexport interface AnalyzerOptions {\n rootPath: string;\n includeSizes?: boolean;\n analyzeExports?: boolean;\n}\n\n// --- Helper: calculate folder size recursively\nfunction getFolderSize(dirPath: string): number {\n let size = 0;\n try {\n const files = fs.readdirSync(dirPath);\n for (const file of files) {\n const filePath = path.join(dirPath, file);\n const stats = fs.statSync(filePath);\n if (stats.isDirectory()) size += getFolderSize(filePath);\n else size += stats.size;\n }\n } catch {\n return 0;\n }\n return size;\n}\n\nfunction formatBytes(bytes: number, decimals = 2) {\n if (bytes === 0) return \"0 Bytes\";\n const k = 1024;\n const dm = decimals < 0 ? 0 : decimals;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + \" \" + sizes[i];\n}\n\nfunction getPackageSize(rootPath: string, packageName: string): string {\n const pkgPath = path.join(rootPath, \"node_modules\", packageName);\n if (fs.existsSync(pkgPath)) {\n const size = getFolderSize(pkgPath);\n return formatBytes(size);\n }\n return \"N/A\";\n}\n\n// --- Analyzer function\nexport async function analyzeProject(\n options: AnalyzerOptions\n): Promise<UsageReport> {\n const { rootPath, includeSizes = true, analyzeExports = true } = options;\n\n console.log(pc.green(`Analyzing project at ${rootPath}`));\n\n // --- 1. Find all JS/TS files\n const files = await glob(\n \"**/*.{js,jsx,ts,tsx,ios.tsx,android.tsx,native.tsx}\",\n {\n cwd: rootPath,\n ignore: [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.next/**\",\n \"**/coverage/**\",\n \"**/*.config.{js,ts,cjs,mjs}\",\n \"**/*.d.ts\"\n ],\n absolute: true\n }\n );\n\n console.log(pc.blue(`Found ${files.length} files to analyze.`));\n\n // --- 2. Initialize ts-morph project\n const tsConfigPath = path.join(rootPath, \"tsconfig.json\");\n const projectConfig: any = { skipAddingFilesFromTsConfig: true };\n if (fs.existsSync(tsConfigPath))\n projectConfig.tsConfigFilePath = tsConfigPath;\n\n const project = new Project(projectConfig);\n\n files.forEach((file) => {\n try {\n project.addSourceFileAtPath(file);\n } catch (e) {\n console.warn(pc.yellow(`Skipping file ${file}:`), e);\n }\n });\n\n const packageUsage: Record<string, number> = {};\n const localUsage: Record<string, number> = {};\n\n files.forEach((f) => {\n const relative = path.relative(rootPath, f).replace(/\\\\/g, \"/\");\n localUsage[relative] = 0;\n });\n\n // --- 3. Analyze imports\n for (const sourceFile of project.getSourceFiles()) {\n const imports = sourceFile.getImportDeclarations();\n\n for (const imp of imports) {\n const moduleSpecifier = imp.getModuleSpecifierValue();\n\n if (moduleSpecifier.startsWith(\".\") || moduleSpecifier.startsWith(\"/\")) {\n // Local import\n const sourceDir = path.dirname(sourceFile.getFilePath());\n const resolvedPath = path.resolve(sourceDir, moduleSpecifier);\n const extensions = [\n \"\",\n \".ts\",\n \".tsx\",\n \".js\",\n \".jsx\",\n \"/index.ts\",\n \"/index.tsx\",\n \"/index.js\",\n \"/index.jsx\"\n ];\n\n for (const ext of extensions) {\n const tryPath = resolvedPath + ext;\n const relativeTry = path\n .relative(rootPath, tryPath)\n .replace(/\\\\/g, \"/\");\n if (localUsage.hasOwnProperty(relativeTry)) {\n localUsage[relativeTry]++;\n break;\n }\n }\n } else {\n // Package import\n const packageName = moduleSpecifier.startsWith(\"@\")\n ? moduleSpecifier.split(\"/\").slice(0, 2).join(\"/\")\n : moduleSpecifier.split(\"/\")[0];\n packageUsage[packageName] = (packageUsage[packageName] || 0) + 1;\n }\n }\n\n // --- Require() CommonJS\n const callExpressions = sourceFile.getDescendantsOfKind(\n SyntaxKind.CallExpression\n );\n for (const call of callExpressions) {\n const expression = call.getExpression();\n if (expression.getText() === \"require\") {\n const args = call.getArguments();\n if (args.length && args[0].getKind() === SyntaxKind.StringLiteral) {\n const rawArg = args[0].getText().replace(/['\"`]/g, \"\");\n if (!rawArg.startsWith(\".\") && !rawArg.startsWith(\"/\")) {\n const pkg = rawArg.startsWith(\"@\")\n ? rawArg.split(\"/\").slice(0, 2).join(\"/\")\n : rawArg.split(\"/\")[0];\n packageUsage[pkg] = (packageUsage[pkg] || 0) + 1;\n }\n }\n }\n }\n }\n\n // --- 4. Packages\n const reportPackages: Record<string, { count: number; size: string }> = {};\n for (const [pkg, count] of Object.entries(packageUsage)) {\n reportPackages[pkg] = {\n count,\n size: includeSizes ? getPackageSize(rootPath, pkg) : \"โ€”\"\n };\n }\n\n // --- 5. Unused files\n const unused = Object.entries(localUsage)\n .filter(([file, count]) => {\n if (\n file.includes(\"pages/\") ||\n file.includes(\"app/\") ||\n file.endsWith(\"main.tsx\") ||\n file.endsWith(\"index.tsx\") ||\n file.endsWith(\"index.js\") ||\n file.endsWith(\"App.tsx\") ||\n file.endsWith(\"App.js\")\n )\n return false;\n if (\n !file.includes(path.sep) &&\n !file.endsWith(\".ts\") &&\n !file.endsWith(\".tsx\") &&\n !file.endsWith(\".js\") &&\n !file.endsWith(\".jsx\")\n )\n return false;\n return count === 0;\n })\n .map(([file]) => file);\n\n // --- 6. Unused exports\n const unusedExports: Record<string, string[]> = {};\n if (analyzeExports) {\n for (const sourceFile of project.getSourceFiles()) {\n const filePath = sourceFile.getFilePath();\n const relativePath = path\n .relative(rootPath, filePath)\n .replace(/\\\\/g, \"/\");\n\n if (\n filePath.includes(\"pages/\") ||\n filePath.includes(\"app/\") ||\n filePath.endsWith(\"main.tsx\") ||\n filePath.endsWith(\"index.tsx\") ||\n filePath.endsWith(\"index.js\") ||\n filePath.endsWith(\"App.tsx\") ||\n filePath.endsWith(\"App.js\") ||\n unused.includes(filePath)\n )\n continue;\n\n const exportedDeclarations = sourceFile.getExportedDeclarations();\n const fileUnusedExports: string[] = [];\n\n for (const [name, declarations] of exportedDeclarations) {\n let isUsed = false;\n\n for (const decl of declarations) {\n // --- Type guard: only check certain declaration types\n if (\n Node.isFunctionDeclaration(decl) ||\n Node.isClassDeclaration(decl) ||\n Node.isVariableDeclaration(decl) ||\n Node.isEnumDeclaration(decl) ||\n Node.isInterfaceDeclaration(decl) ||\n Node.isTypeAliasDeclaration(decl)\n ) {\n try {\n const refs = decl.findReferences();\n for (const ref of refs) {\n for (const entry of ref.getReferences()) {\n if (entry.getSourceFile().getFilePath() !== filePath) {\n isUsed = true;\n break;\n }\n }\n if (isUsed) break;\n }\n } catch {\n isUsed = true;\n }\n } else {\n // Other export types: assume used\n isUsed = true;\n }\n\n if (isUsed) break;\n }\n\n if (!isUsed) fileUnusedExports.push(name);\n }\n\n if (fileUnusedExports.length)\n unusedExports[relativePath] = fileUnusedExports;\n }\n }\n\n return {\n packages: reportPackages,\n components: localUsage,\n unusedFiles: unused,\n unusedExports\n };\n}\n","#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport pc from \"picocolors\";\nimport path from \"path\";\nimport Table from \"cli-table3\";\nimport boxen from \"boxen\";\nimport { analyzeProject } from \"./analyzer\";\n\nconst program = new Command();\n\nprogram\n .name(\"react-prune\")\n .description(\n \"Analyze React/Next/Vite/React Native codebases for unused files, exports, and packages\"\n )\n .version(require(\"../package.json\").version)\n .option(\"--no-size\", \"Skip calculating package sizes\")\n .option(\"--no-exports\", \"Skip analyzing unused exports\")\n .option(\"--limit <n>\", \"Limit output rows\", \"50\");\n\nprogram.action(async (opts) => {\n const rootPath = process.cwd();\n const limit = Number(opts.limit);\n\n const report = await analyzeProject({\n rootPath,\n includeSizes: opts.size,\n analyzeExports: opts.exports\n });\n\n // ---------- Packages ----------\n console.log(\n boxen(pc.bold(\"๐Ÿ“ฆ Package Usage\"), {\n padding: 1,\n borderColor: \"green\",\n borderStyle: \"round\"\n })\n );\n\n const packageTable = new Table({ head: [\"Package\", \"Count\", \"Size\"] });\n Object.entries(report.packages)\n .sort((a, b) => b[1].count - a[1].count)\n .slice(0, limit)\n .forEach(([pkg, data]) => packageTable.push([pkg, data.count, data.size]));\n\n console.log(packageTable.toString());\n\n // ---------- Unused Files ----------\n if (report.unusedFiles.length) {\n console.log(\n boxen(pc.bold(`โš ๏ธ Unused Files (${report.unusedFiles.length})`), {\n padding: 1,\n borderColor: \"yellow\"\n })\n );\n report.unusedFiles.forEach((f) => console.log(pc.yellow(f)));\n } else {\n console.log(\n boxen(pc.green(\"โœ… No unused files detected!\"), {\n padding: 1,\n borderColor: \"green\"\n })\n );\n }\n\n // ---------- Unused Exports ----------\n const unusedExportsEntries = Object.entries(report.unusedExports);\n if (unusedExportsEntries.length) {\n console.log(\n boxen(\n pc.bold(`โš ๏ธ Potential Unused Exports (${unusedExportsEntries.length})`),\n {\n padding: 1,\n borderColor: \"yellow\"\n }\n )\n );\n const exportsTable = new Table({\n head: [\"File\", \"Unused Exports\"],\n wordWrap: true\n });\n unusedExportsEntries.slice(0, limit).forEach(([file, exports]) => {\n exportsTable.push([file, exports.join(\", \")]);\n });\n console.log(exportsTable.toString());\n }\n});\n\nprogram.parse();\n"]}
1
+ {"version":3,"sources":["../package.json","../src/analyzer/package-size.ts","../src/analyzer/index.ts","../src/analyzer/file-size.ts","../src/cli.ts"],"names":["require_package","__commonJSMin","exports","module","package_size_exports","__export","getPackageSize","rootPath","pkg","pkgJson","__require","path","init_package_size","__esmMin","getFolderSize","dirPath","size","files","fs","file","filePath","stats","formatBytes","bytes","decimals","k","dm","sizes","i","packageName","pkgPath","analyzeProject","options","includeSizes","analyzeExports","silent","pc","glob","project","Project","tsConfigPath","f","packageUsage","fileExports","usedExports","sourceFiles","sourceFile","relativePath","exportsMap","namedExports","decls","name","hasDefault","imp","mod","SyntaxKind","call","args","imports","sourceDir","resolvedPath","extensions","foundFile","ext","tryPath","ni","unusedFiles","used","unusedExports","unused","packages","count","unusedDependencies","getFileSize","program","Command","opts","limit","report","rest","sanitizedUsedExports","key","value","packageTable","Table","a","b","data","boxen","table","entries","d","exportName","usageDetails","usedSet","id","parentKind"],"mappings":"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","file":"cli.js","sourcesContent":["{\n \"name\": \"react-prune\",\n \"version\": \"1.3.0\",\n \"main\": \"dist/index.js\",\n \"bin\": {\n \"react-prune\": \"./dist/cli.js\"\n },\n \"scripts\": {\n \"build\": \"tsup src/cli.ts --format cjs --dts\",\n \"dev\": \"tsup src/cli.ts --watch\",\n \"lint\": \"eslint src/**\",\n \"format\": \"prettier --write .\",\n \"prepublishOnly\": \"npm run build\",\n \"release\": \"changeset publish\",\n \"test\": \"vitest\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"keywords\": [\n \"react\",\n \"react-native\",\n \"nextjs\",\n \"analysis\",\n \"dead-code\",\n \"imports\",\n \"dependency-analysis\",\n \"bundle-size\",\n \"prune\",\n \"typescript\",\n \"developer-tools\",\n \"cli\"\n ],\n \"author\": \"Daniel Arikawe\",\n \"license\": \"MIT\",\n \"files\": [\n \"dist\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/danieljohnson18/react-prune.git\"\n },\n \"engines\": {\n \"node\": \">=18\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/danieljohnson18/react-prune/issues\"\n },\n \"homepage\": \"https://github.com/danieljohnson18/react-prune#readme\",\n \"description\": \"A powerful CLI tool to monitor package usage, analyze component imports, and detect dead code across React, Next.js, and React Native applications.\",\n \"dependencies\": {\n \"boxen\": \"^8.0.1\",\n \"cli-table3\": \"^0.6.5\",\n \"commander\": \"^14.0.2\",\n \"depcheck\": \"^1.4.7\",\n \"fast-glob\": \"^3.3.3\",\n \"picocolors\": \"^1.1.1\",\n \"ts-morph\": \"^27.0.2\"\n },\n \"devDependencies\": {\n \"@changesets/cli\": \"^2.29.8\",\n \"@types/depcheck\": \"^0.9.0\",\n \"@types/glob\": \"^8.1.0\",\n \"@types/node\": \"^25.0.10\",\n \"eslint\": \"^9.39.2\",\n \"prettier\": \"^3.8.1\",\n \"tsup\": \"^8.5.1\",\n \"typescript\": \"^5.9.3\",\n \"vitest\": \"^4.0.18\"\n }\n}\n","import path from \"path\";\n\nexport function getPackageSize(\n rootPath: string,\n pkg: string\n): string | undefined {\n try {\n const pkgJson = require(\n path.join(rootPath, \"node_modules\", pkg, \"package.json\")\n );\n if (typeof pkgJson.size === \"number\") {\n return `${(pkgJson.size / 1024).toFixed(2)} KB`;\n }\n } catch {}\n return undefined;\n}\n","import { Project, SyntaxKind, SourceFile } from \"ts-morph\";\nimport glob from \"fast-glob\";\nimport path from \"path\";\nimport fs from \"fs\";\nimport pc from \"picocolors\";\nimport { AnalyzerOptions, UsageReport } from \"./types\";\n\nexport * from \"./types\";\n\nfunction getFolderSize(dirPath: string): number {\n let size = 0;\n try {\n const files = fs.readdirSync(dirPath);\n for (const file of files) {\n const filePath = path.join(dirPath, file);\n const stats = fs.statSync(filePath);\n if (stats.isDirectory()) size += getFolderSize(filePath);\n else size += stats.size;\n }\n } catch {}\n return size;\n}\n\nfunction formatBytes(bytes: number, decimals = 2) {\n if (bytes === 0) return \"0 Bytes\";\n const k = 1024;\n const dm = decimals < 0 ? 0 : decimals;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + \" \" + sizes[i];\n}\n\nfunction getPackageSize(rootPath: string, packageName: string): string {\n const pkgPath = path.join(rootPath, \"node_modules\", packageName);\n if (fs.existsSync(pkgPath)) return formatBytes(getFolderSize(pkgPath));\n return \"N/A\";\n}\n\nexport async function analyzeProject(\n options: AnalyzerOptions\n): Promise<UsageReport> {\n const {\n rootPath,\n includeSizes = true,\n analyzeExports = true,\n silent = false\n } = options;\n\n if (!silent) {\n console.log(pc.green(`Analyzing project at ${rootPath}`));\n }\n\n // 1๏ธโƒฃ Collect all JS/TS files\n const files = await glob(\"**/*.{js,jsx,ts,tsx}\", {\n cwd: rootPath,\n ignore: [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/build/**\",\n \"**/.next/**\",\n \"**/coverage/**\",\n \"**/*.config.{js,ts,cjs,mjs}\",\n \"**/*.d.ts\"\n ],\n absolute: true\n });\n\n if (!silent) {\n console.log(pc.blue(`Found ${files.length} files to analyze.`));\n }\n\n const project = new Project({ skipAddingFilesFromTsConfig: true });\n const tsConfigPath = path.join(rootPath, \"tsconfig.json\");\n if (fs.existsSync(tsConfigPath)) {\n project.addSourceFilesFromTsConfig(tsConfigPath);\n }\n files.forEach((f) => {\n try {\n project.addSourceFileAtPath(f);\n } catch {\n console.warn(pc.yellow(`Skipping ${f}`));\n }\n });\n\n // 2๏ธโƒฃ Track package usage and file exports\n const packageUsage: Record<string, number> = {};\n const fileExports: Record<string, { named: Set<string>; default: boolean }> =\n {};\n const usedExports: Record<string, Set<string>> = {};\n const sourceFiles: Record<string, SourceFile> = {};\n\n for (const sourceFile of project.getSourceFiles()) {\n const relativePath = path\n .relative(rootPath, sourceFile.getFilePath())\n .replace(/\\\\/g, \"/\");\n sourceFiles[relativePath] = sourceFile;\n\n const exportsMap = sourceFile.getExportedDeclarations();\n const namedExports = new Set<string>();\n exportsMap.forEach((decls, name) => {\n if (name !== \"default\") namedExports.add(name);\n });\n const hasDefault = exportsMap.has(\"default\");\n\n fileExports[relativePath] = { named: namedExports, default: hasDefault };\n usedExports[relativePath] = new Set();\n\n // Track package imports\n const imports = sourceFile.getImportDeclarations();\n imports.forEach((imp) => {\n const mod = imp.getModuleSpecifierValue();\n if (!mod.startsWith(\".\") && !mod.startsWith(\"/\")) {\n const pkg = mod.startsWith(\"@\")\n ? mod.split(\"/\").slice(0, 2).join(\"/\")\n : mod.split(\"/\")[0];\n packageUsage[pkg] = (packageUsage[pkg] || 0) + 1;\n }\n });\n\n // Track require()\n const calls = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);\n calls.forEach((call) => {\n if (call.getExpression().getText() === \"require\") {\n const args = call.getArguments();\n if (args.length && args[0].getKind() === SyntaxKind.StringLiteral) {\n const mod = args[0].getText().replace(/['\"`]/g, \"\");\n if (!mod.startsWith(\".\") && !mod.startsWith(\"/\")) {\n const pkg = mod.startsWith(\"@\")\n ? mod.split(\"/\").slice(0, 2).join(\"/\")\n : mod.split(\"/\")[0];\n packageUsage[pkg] = (packageUsage[pkg] || 0) + 1;\n }\n }\n }\n });\n }\n\n // 3๏ธโƒฃ Track used exports across files\n if (analyzeExports) {\n for (const sourceFile of project.getSourceFiles()) {\n const imports = sourceFile.getImportDeclarations();\n const relativePath = path\n .relative(rootPath, sourceFile.getFilePath())\n .replace(/\\\\/g, \"/\");\n\n imports.forEach((imp) => {\n const mod = imp.getModuleSpecifierValue();\n if (mod.startsWith(\".\") || mod.startsWith(\"/\")) {\n const sourceDir = path.dirname(sourceFile.getFilePath());\n let resolvedPath = path.resolve(sourceDir, mod);\n const extensions = [\n \"\",\n \".ts\",\n \".tsx\",\n \".js\",\n \".jsx\",\n \"/index.ts\",\n \"/index.tsx\",\n \"/index.js\",\n \"/index.jsx\"\n ];\n let foundFile: string | undefined;\n\n for (const ext of extensions) {\n const tryPath = path\n .relative(rootPath, resolvedPath + ext)\n .replace(/\\\\/g, \"/\");\n if (fileExports[tryPath]) {\n foundFile = tryPath;\n break;\n }\n }\n\n if (foundFile) {\n imp\n .getNamedImports()\n .forEach((ni) => usedExports[foundFile].add(ni.getName()));\n if (imp.getDefaultImport()) usedExports[foundFile].add(\"default\");\n }\n }\n });\n }\n }\n\n // 4๏ธโƒฃ Determine unused files and exports\n const unusedFiles = Object.entries(fileExports)\n .filter(([file, exports]) => {\n if (\n file.includes(\"pages/\") ||\n file.includes(\"app/\") ||\n file.endsWith(\"index.tsx\") ||\n file.endsWith(\"App.tsx\")\n )\n return false;\n const used = usedExports[file];\n if (exports.named.size === 0 && !exports.default) return false;\n return used.size === 0;\n })\n .map(([file]) => file);\n\n const unusedExports: Record<string, string[]> = {};\n if (analyzeExports) {\n Object.entries(fileExports).forEach(([file, exports]) => {\n const used = usedExports[file];\n const unused: string[] = [];\n if (exports.default && !used.has(\"default\")) unused.push(\"default\");\n exports.named.forEach((name) => {\n if (!used.has(name)) unused.push(name);\n });\n if (unused.length) unusedExports[file] = unused;\n });\n }\n\n // 5๏ธโƒฃ Build package report\n const packages: Record<string, { count: number; size: string }> = {};\n Object.entries(packageUsage).forEach(([pkg, count]) => {\n packages[pkg] = {\n count,\n size: includeSizes ? getPackageSize(rootPath, pkg) : \"โ€”\"\n };\n });\n\n // 6๏ธโƒฃ Run depcheck\n let unusedDependencies: string[] = [];\n try {\n const depcheck = require(\"depcheck\");\n // We can't easily wait for this if not async, but analyzeProject is async.\n const depcheckResult = await depcheck(rootPath, {\n ignoreMatches: [\"react-prune\", ...((options as any).ignoreMatches || [])],\n skipMissing: true\n });\n unusedDependencies = depcheckResult.dependencies;\n } catch (e) {\n if (!silent) console.warn(pc.yellow(\"Failed to run depcheck.\"));\n }\n\n return {\n packages,\n unusedFiles,\n unusedExports,\n usedExports,\n unusedDependencies,\n sourceFiles\n };\n}\n\nexport { getPackageSize };\n","import fs from \"fs\";\n\nexport function getFileSize(filePath: string): string {\n try {\n const stats = fs.statSync(filePath);\n const bytes = stats.size;\n if (bytes < 1024) return `${bytes} B`;\n return `${(bytes / 1024).toFixed(2)} KB`;\n } catch {\n return \"N/A\";\n }\n}\n","#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport pc from \"picocolors\";\nimport Table from \"cli-table3\";\nimport boxen from \"boxen\";\nimport path from \"path\";\nimport { analyzeProject, UsageReport } from \"./analyzer\";\nimport { getFileSize } from \"./analyzer/file-size\";\nimport { SyntaxKind, Identifier } from \"ts-morph\";\n\nconst program = new Command();\n\nprogram\n .name(\"react-prune\")\n .description(\"Analyze React/Next/Vite/React Native projects\")\n .version(require(\"../package.json\").version)\n .option(\"--no-size\", \"Skip package size calculation\")\n .option(\"--no-exports\", \"Skip export usage analysis\")\n .option(\"--limit <n>\", \"Limit output rows per table\", \"50\");\n\nprogram\n .command(\"analyze\")\n .description(\"Run full project analysis\")\n .option(\"--no-size\", \"Skip package size calculation\")\n .option(\"--no-exports\", \"Skip export usage analysis\")\n .option(\"--limit <n>\", \"Limit output rows per table\", \"50\")\n .option(\"--json\", \"Output results as JSON\")\n .action(async (opts) => {\n const rootPath = process.cwd();\n const limit = Number(opts.limit);\n\n const report: UsageReport = await analyzeProject({\n rootPath,\n includeSizes: opts.size,\n analyzeExports: opts.exports,\n silent: opts.json\n });\n\n if (opts.json) {\n // 1. Remove circular references (sourceFiles)\n // 2. Convert Sets to Arrays for valid JSON\n const { sourceFiles, usedExports, ...rest } = report;\n\n const sanitizedUsedExports: Record<string, string[]> = {};\n if (usedExports) {\n for (const [key, value] of Object.entries(usedExports)) {\n sanitizedUsedExports[key] = Array.from(value);\n }\n }\n\n console.log(\n JSON.stringify(\n {\n ...rest,\n usedExports: sanitizedUsedExports\n },\n null,\n 2\n )\n );\n return;\n }\n\n // Packages\n const packageTable = new Table({\n head: [pc.cyan(\"Package\"), \"Count\", \"Size\"],\n colWidths: [40, 10, 15]\n });\n Object.entries(report.packages)\n .sort((a, b) => b[1].count - a[1].count)\n .slice(0, limit)\n .forEach(([pkg, data]) =>\n packageTable.push([pkg, data.count, data.size])\n );\n console.log(\n boxen(pc.bold(\"๐Ÿ“ฆ Package Usage\"), {\n padding: 1,\n borderColor: \"green\",\n borderStyle: \"round\"\n })\n );\n console.log(packageTable.toString());\n\n // Unused Files\n if (report.unusedFiles.length) {\n const table = new Table({\n head: [pc.yellow(\"Unused Files\")],\n colWidths: [80]\n });\n report.unusedFiles.slice(0, limit).forEach((f) => table.push([f]));\n console.log(\n boxen(pc.bold(`โš ๏ธ Unused Files (${report.unusedFiles.length})`), {\n padding: 1,\n borderColor: \"yellow\",\n borderStyle: \"round\"\n })\n );\n console.log(table.toString());\n }\n\n // Unused Exports\n if (opts.exports) {\n const entries = Object.entries(report.unusedExports);\n if (entries.length) {\n const table = new Table({\n head: [\"File\", \"Unused Exports\"],\n colWidths: [50, 40],\n wordWrap: true\n });\n entries\n .slice(0, limit)\n .forEach(([file, exports]) => table.push([file, exports.join(\", \")]));\n console.log(\n boxen(pc.bold(`โš ๏ธ Unused Exports`), {\n padding: 1,\n borderColor: \"yellow\",\n borderStyle: \"round\"\n })\n );\n console.log(table.toString());\n }\n }\n\n // Unused Dependencies\n if (report.unusedDependencies && report.unusedDependencies.length) {\n const table = new Table({\n head: [pc.yellow(\"Unused Dependencies\")],\n colWidths: [80]\n });\n report.unusedDependencies.slice(0, limit).forEach((d) => table.push([d]));\n console.log(\n boxen(\n pc.bold(\n `โš ๏ธ Unused Dependencies (${report.unusedDependencies.length})`\n ),\n {\n padding: 1,\n borderColor: \"yellow\",\n borderStyle: \"round\"\n }\n )\n );\n console.log(table.toString());\n }\n });\n\nprogram\n .command(\"size <packageName>\")\n .description(\"Check the size of a specific npm package in node_modules\")\n .action(async (packageName) => {\n const rootPath = process.cwd();\n\n // Reuse helper from analyzer\n const { getPackageSize } = await import(\"./analyzer/package-size\");\n\n const size = getPackageSize(rootPath, packageName);\n\n if (size === \"N/A\") {\n console.log(\n pc.yellow(`Package '${packageName}' not found in node_modules.`)\n );\n } else {\n console.log(pc.green(`๐Ÿ“ฆ ${packageName} size: ${size}`));\n }\n });\n// --- New find command with line numbers\nprogram\n .command(\"find <exportName>\")\n .description(\n \"Find usage count and references (with line numbers) for a component/function/export\"\n )\n .action(async (exportName) => {\n const rootPath = process.cwd();\n const report: UsageReport = await analyzeProject({\n rootPath,\n analyzeExports: true,\n includeSizes: false\n });\n\n const usageDetails: { file: string; line: number }[] = [];\n\n const usedExports = report.usedExports || {};\n\n for (const [file, usedSet] of Object.entries(usedExports)) {\n const sourceFile = report.sourceFiles?.[file];\n if (!sourceFile) continue;\n\n // Only process if this file actually uses the export\n if (!usedSet.has(exportName)) continue;\n\n // Traverse identifiers in the file\n const identifiers = sourceFile.getDescendantsOfKind(\n SyntaxKind.Identifier\n );\n\n identifiers.forEach((id: Identifier) => {\n // Check if identifier matches the export name\n if (id.getText() === exportName) {\n // Make sure this usage is actually an import/reference, not a declaration\n const parentKind = id.getParentOrThrow().getKindName();\n\n if (\n parentKind.includes(\"Import\") || // ImportSpecifier, ImportClause, etc\n parentKind.includes(\"PropertyAccess\") || // obj.exportName\n parentKind.includes(\"Identifier\") // usage in code\n ) {\n usageDetails.push({ file, line: id.getStartLineNumber() });\n }\n }\n });\n }\n\n if (usageDetails.length) {\n console.log(\n pc.green(`'${exportName}' is used ${usageDetails.length} time(s):`)\n );\n usageDetails.forEach((d) => {\n const size = getFileSize(path.join(rootPath, d.file));\n console.log(` - ${d.file}:${d.line} (${size})`);\n });\n } else {\n console.log(pc.yellow(`'${exportName}' is not used anywhere.`));\n }\n });\n\nprogram.parse(process.argv);\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-prune",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "react-prune": "./dist/cli.js"
@@ -52,12 +52,14 @@
52
52
  "boxen": "^8.0.1",
53
53
  "cli-table3": "^0.6.5",
54
54
  "commander": "^14.0.2",
55
+ "depcheck": "^1.4.7",
55
56
  "fast-glob": "^3.3.3",
56
57
  "picocolors": "^1.1.1",
57
58
  "ts-morph": "^27.0.2"
58
59
  },
59
60
  "devDependencies": {
60
61
  "@changesets/cli": "^2.29.8",
62
+ "@types/depcheck": "^0.9.0",
61
63
  "@types/glob": "^8.1.0",
62
64
  "@types/node": "^25.0.10",
63
65
  "eslint": "^9.39.2",