react-prune 1.2.2 → 2.0.1

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
@@ -12,8 +12,14 @@
12
12
 
13
13
  ## 🚀 Features
14
14
 
15
- - **📦 Package Usage Analysis**
16
- Counts how often each external npm package is imported across your codebase.
15
+ - **🔎 Component Usage Detection**
16
+ Find where any component or function is used, including line numbers and file sizes.
17
+
18
+ - **📦 Package Usage & Size Analysis**
19
+ Counts usage of external packages and checks **disk size** of dependencies in `node_modules` to spot bloat.
20
+
21
+ - **🚫 Unused Dependency Detection**
22
+ Leverages `depcheck` to identify dependencies in `package.json` that are completely unused.
17
23
 
18
24
  - **⚖️ Optional Package Size Estimation**
19
25
  Estimates package sizes from `node_modules` to highlight heavy dependencies.
@@ -83,13 +89,15 @@ react-prune analyze
83
89
 
84
90
  ---
85
91
 
86
- ### Search for a component or export
92
+ ## 🔎 Finding Usage
93
+
94
+ You can check where a specific component or function is used across your codebase.
87
95
 
88
96
  ```bash
89
- react-prune find <exportName>
97
+ react-prune find <Name>
90
98
  ```
91
99
 
92
- #### Example
100
+ Example:
93
101
 
94
102
  ```bash
95
103
  react-prune find Button
@@ -98,11 +106,13 @@ react-prune find Button
98
106
  Output:
99
107
 
100
108
  ```
101
- 'Button' is used 4 time(s):
102
- - src/components/Header.tsx:12
103
- - src/components/Footer.tsx:8
104
- - src/pages/index.tsx:22
105
- - src/pages/about.tsx:15
109
+ 🔎 Searching for usage of 'Button'...
110
+ Found 5 occurrences:
111
+
112
+ src/app/page.tsx: 10: <Button>Click me</Button> [5 KB]
113
+ src/components/ui/button.tsx: 12: export { Button } [2 KB]
114
+
115
+ Total: 5 times
106
116
  ```
107
117
 
108
118
  ---
@@ -0,0 +1,52 @@
1
+ #!/bin/bash
2
+
3
+ # bin/react-prune
4
+
5
+ # Get the directory where the script is located
6
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+ PROJECT_ROOT="$(pwd)"
8
+
9
+ CORE_DIR="$SCRIPT_DIR/../scripts/core"
10
+
11
+ function show_help {
12
+ echo "react-prune - Bash Edition"
13
+ echo "Usage:"
14
+ echo " react-prune analyze Run full analysis (deps, files, exports)"
15
+ echo " react-prune help Show this help"
16
+ }
17
+
18
+ if [ "$1" == "analyze" ]; then
19
+ echo "🚀 Starting React Prune Analysis..."
20
+
21
+ # 1. Check Deps
22
+ "$CORE_DIR/check_deps.sh"
23
+ echo ""
24
+
25
+ # 1b. Check Package Sizes
26
+ "$CORE_DIR/check_package_sizes.sh" "$PROJECT_ROOT"
27
+ echo ""
28
+
29
+ # 2. Check Unused Files
30
+ "$CORE_DIR/check_unused_files.sh" "$PROJECT_ROOT"
31
+ echo ""
32
+
33
+ # 3. Check Unused Exports
34
+ "$CORE_DIR/check_unused_exports.sh" "$PROJECT_ROOT"
35
+
36
+ elif [ "$1" == "find" ]; then
37
+ shift
38
+ "$CORE_DIR/find_usage.sh" "$1" "$PROJECT_ROOT"
39
+
40
+ elif [ "$1" == "--version" ] || [ "$1" == "-v" ]; then
41
+ # Read version from package.json using grep/sed to avoid jq dependency
42
+ VERSION=$(grep '"version":' "$CORE_DIR/../../package.json" | head -1 | sed -E 's/.*"version": "([^"]+)".*/\1/')
43
+ echo "react-prune version $VERSION"
44
+ exit 0
45
+
46
+ elif [ "$1" == "help" ] || [ -z "$1" ]; then
47
+ show_help
48
+ else
49
+ echo "Unknown command: $1"
50
+ show_help
51
+ exit 1
52
+ fi
package/dist/cli.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- 'use strict';var b=require('path'),commander=require('commander'),h=require('picocolors'),k=require('cli-table3'),P=require('boxen'),tsMorph=require('ts-morph'),q=require('fast-glob'),w=require('fs');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var b__default=/*#__PURE__*/_interopDefault(b);var h__default=/*#__PURE__*/_interopDefault(h);var k__default=/*#__PURE__*/_interopDefault(k);var P__default=/*#__PURE__*/_interopDefault(P);var q__default=/*#__PURE__*/_interopDefault(q);var w__default=/*#__PURE__*/_interopDefault(w);var K=Object.defineProperty;var $=(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 M=(e,t)=>()=>(e&&(t=e(e=0)),t);var B=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),L=(e,t)=>{for(var s in t)K(e,s,{get:t[s],enumerable:true});};var U=B((ae,G)=>{G.exports={name:"react-prune",version:"1.2.2",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"}};});var I={};L(I,{getPackageSize:()=>Q});function Q(e,t){try{let s=$(b__default.default.join(e,"node_modules",t,"package.json"));if(typeof s.size=="number")return `${(s.size/1024).toFixed(2)} KB`}catch{}}var N=M(()=>{});function D(e){let t=0;try{let s=w__default.default.readdirSync(e);for(let i of s){let l=b__default.default.join(e,i),n=w__default.default.statSync(l);n.isDirectory()?t+=D(l):t+=n.size;}}catch{}return t}function J(e,t=2){if(e===0)return "0 Bytes";let s=1024,i=t<0?0:t,l=["Bytes","KB","MB","GB","TB"],n=Math.floor(Math.log(e)/Math.log(s));return parseFloat((e/Math.pow(s,n)).toFixed(i))+" "+l[n]}function V(e,t){let s=b__default.default.join(e,"node_modules",t);return w__default.default.existsSync(s)?J(D(s)):"N/A"}async function F(e){let{rootPath:t,includeSizes:s=true,analyzeExports:i=true,silent:l=false}=e;l||console.log(h__default.default.green(`Analyzing project at ${t}`));let n=await q__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(h__default.default.blue(`Found ${n.length} files to analyze.`));let r=new tsMorph.Project({skipAddingFilesFromTsConfig:true}),m=b__default.default.join(t,"tsconfig.json");w__default.default.existsSync(m)&&r.addSourceFilesFromTsConfig(m),n.forEach(o=>{try{r.addSourceFileAtPath(o);}catch{console.warn(h__default.default.yellow(`Skipping ${o}`));}});let u={},f={},d={},v={};for(let o of r.getSourceFiles()){let a=b__default.default.relative(t,o.getFilePath()).replace(/\\/g,"/");v[a]=o;let y=o.getExportedDeclarations(),g=new Set;y.forEach((j,c)=>{c!=="default"&&g.add(c);});let x=y.has("default");f[a]={named:g,default:x},d[a]=new Set,o.getImportDeclarations().forEach(j=>{let c=j.getModuleSpecifierValue();if(!c.startsWith(".")&&!c.startsWith("/")){let p=c.startsWith("@")?c.split("/").slice(0,2).join("/"):c.split("/")[0];u[p]=(u[p]||0)+1;}}),o.getDescendantsOfKind(tsMorph.SyntaxKind.CallExpression).forEach(j=>{if(j.getExpression().getText()==="require"){let c=j.getArguments();if(c.length&&c[0].getKind()===tsMorph.SyntaxKind.StringLiteral){let p=c[0].getText().replace(/['"`]/g,"");if(!p.startsWith(".")&&!p.startsWith("/")){let S=p.startsWith("@")?p.split("/").slice(0,2).join("/"):p.split("/")[0];u[S]=(u[S]||0)+1;}}}});}if(i)for(let o of r.getSourceFiles()){let a=o.getImportDeclarations();b__default.default.relative(t,o.getFilePath()).replace(/\\/g,"/");a.forEach(g=>{let x=g.getModuleSpecifierValue();if(x.startsWith(".")||x.startsWith("/")){let A=b__default.default.dirname(o.getFilePath()),W=b__default.default.resolve(A,x),j=["",".ts",".tsx",".js",".jsx","/index.ts","/index.tsx","/index.js","/index.jsx"],c;for(let p of j){let S=b__default.default.relative(t,W+p).replace(/\\/g,"/");if(f[S]){c=S;break}}c&&(g.getNamedImports().forEach(p=>d[c].add(p.getName())),g.getDefaultImport()&&d[c].add("default"));}});}let T=Object.entries(f).filter(([o,a])=>{if(o.includes("pages/")||o.includes("app/")||o.endsWith("index.tsx")||o.endsWith("App.tsx"))return false;let y=d[o];return a.named.size===0&&!a.default?false:y.size===0}).map(([o])=>o),R={};i&&Object.entries(f).forEach(([o,a])=>{let y=d[o],g=[];a.default&&!y.has("default")&&g.push("default"),a.named.forEach(x=>{y.has(x)||g.push(x);}),g.length&&(R[o]=g);});let O={};return Object.entries(u).forEach(([o,a])=>{O[o]={count:a,size:s?V(t,o):"\u2014"};}),{packages:O,unusedFiles:T,unusedExports:R,usedExports:d,sourceFiles:v}}var z=new commander.Command;z.name("react-prune").description("Analyze React/Next/Vite/React Native projects").version(U().version).option("--no-size","Skip package size calculation").option("--no-exports","Skip export usage analysis").option("--limit <n>","Limit output rows per table","50");z.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 F({rootPath:t,includeSizes:e.size,analyzeExports:e.exports,silent:e.json});if(e.json){let{sourceFiles:n,usedExports:r,...m}=i,u={};if(r)for(let[f,d]of Object.entries(r))u[f]=Array.from(d);console.log(JSON.stringify({...m,usedExports:u},null,2));return}let l=new k__default.default({head:[h__default.default.cyan("Package"),"Count","Size"],colWidths:[40,10,15]});if(Object.entries(i.packages).sort((n,r)=>r[1].count-n[1].count).slice(0,s).forEach(([n,r])=>l.push([n,r.count,r.size])),console.log(P__default.default(h__default.default.bold("\u{1F4E6} Package Usage"),{padding:1,borderColor:"green",borderStyle:"round"})),console.log(l.toString()),i.unusedFiles.length){let n=new k__default.default({head:[h__default.default.yellow("Unused Files")],colWidths:[80]});i.unusedFiles.slice(0,s).forEach(r=>n.push([r])),console.log(P__default.default(h__default.default.bold(`\u26A0\uFE0F Unused Files (${i.unusedFiles.length})`),{padding:1,borderColor:"yellow",borderStyle:"round"})),console.log(n.toString());}if(e.exports){let n=Object.entries(i.unusedExports);if(n.length){let r=new k__default.default({head:["File","Unused Exports"],colWidths:[50,40],wordWrap:true});n.slice(0,s).forEach(([m,u])=>r.push([m,u.join(", ")])),console.log(P__default.default(h__default.default.bold("\u26A0\uFE0F Unused Exports"),{padding:1,borderColor:"yellow",borderStyle:"round"})),console.log(r.toString());}}});z.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(()=>(N(),I)),i=s(t,e);console.log(i==="N/A"?h__default.default.yellow(`Package '${e}' not found in node_modules.`):h__default.default.green(`\u{1F4E6} ${e} size: ${i}`));});z.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 F({rootPath:t,analyzeExports:true,includeSizes:false}),i=[],l=s.usedExports||{};for(let[n,r]of Object.entries(l)){let m=s.sourceFiles?.[n];if(!m||!r.has(e))continue;m.getDescendantsOfKind(tsMorph.SyntaxKind.Identifier).forEach(f=>{if(f.getText()===e){let d=f.getParentOrThrow().getKindName();(d.includes("Import")||d.includes("PropertyAccess")||d.includes("Identifier"))&&i.push({file:n,line:f.getStartLineNumber()});}});}i.length?(console.log(h__default.default.green(`'${e}' is used ${i.length} time(s):`)),i.forEach(n=>console.log(` - ${n.file}:${n.line}`))):console.log(h__default.default.yellow(`'${e}' is not used anywhere.`));});z.parse(process.argv);
2
+ 'use strict';var x=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 x__default=/*#__PURE__*/_interopDefault(x);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:"2.0.1",main:"dist/index.js",bin:{"react-prune":"./bin/react-prune"},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:["bin","scripts","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(x__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=x__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=x__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=x__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 u={},h={},d={},D={};for(let n of r.getSourceFiles()){let a=x__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 b=y.has("default");h[a]={named:g,default:b},d[a]=new Set,n.getImportDeclarations().forEach(S=>{let c=S.getModuleSpecifierValue();if(!c.startsWith(".")&&!c.startsWith("/")){let p=c.startsWith("@")?c.split("/").slice(0,2).join("/"):c.split("/")[0];u[p]=(u[p]||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 p=c[0].getText().replace(/['"`]/g,"");if(!p.startsWith(".")&&!p.startsWith("/")){let z=p.startsWith("@")?p.split("/").slice(0,2).join("/"):p.split("/")[0];u[z]=(u[z]||0)+1;}}}});}if(i)for(let n of r.getSourceFiles()){let a=n.getImportDeclarations();x__default.default.relative(t,n.getFilePath()).replace(/\\/g,"/");a.forEach(g=>{let b=g.getModuleSpecifierValue();if(b.startsWith(".")||b.startsWith("/")){let A=x__default.default.dirname(n.getFilePath()),W=x__default.default.resolve(A,b),S=["",".ts",".tsx",".js",".jsx","/index.ts","/index.tsx","/index.js","/index.jsx"],c;for(let p of S){let z=x__default.default.relative(t,W+p).replace(/\\/g,"/");if(h[z]){c=z;break}}c&&(g.getNamedImports().forEach(p=>d[c].add(p.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(b=>{y.has(b)||g.push(b);}),g.length&&(R[n]=g);});let $={};Object.entries(u).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,u={};if(r)for(let[h,d]of Object.entries(r))u[h]=Array.from(d);console.log(JSON.stringify({...m,usedExports:u},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,u])=>r.push([m,u.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(x__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/package-size.ts","../src/analyzer/index.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","program","Command","opts","limit","report","rest","sanitizedUsedExports","key","value","packageTable","Table","a","b","data","boxen","table","entries","exportName","usageDetails","usedSet","id","parentKind","d"],"mappings":"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","file":"cli.js","sourcesContent":["{\n \"name\": \"react-prune\",\n \"version\": \"1.2.2\",\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 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 return {\n packages,\n unusedFiles,\n unusedExports,\n usedExports,\n sourceFiles\n };\n}\n\nexport { getPackageSize };\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 { 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\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) => console.log(` - ${d.file}:${d.line}`));\n } else {\n console.log(pc.yellow(`'${exportName}' is not used anywhere.`));\n }\n });\n\nprogram.parse(process.argv);\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\": \"2.0.1\",\n \"main\": \"dist/index.js\",\n \"bin\": {\n \"react-prune\": \"./bin/react-prune\"\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 \"bin\",\n \"scripts\",\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,9 +1,9 @@
1
1
  {
2
2
  "name": "react-prune",
3
- "version": "1.2.2",
3
+ "version": "2.0.1",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
- "react-prune": "./dist/cli.js"
6
+ "react-prune": "./bin/react-prune"
7
7
  },
8
8
  "scripts": {
9
9
  "build": "tsup src/cli.ts --format cjs --dts",
@@ -34,6 +34,8 @@
34
34
  "author": "Daniel Arikawe",
35
35
  "license": "MIT",
36
36
  "files": [
37
+ "bin",
38
+ "scripts",
37
39
  "dist"
38
40
  ],
39
41
  "repository": {
@@ -52,12 +54,14 @@
52
54
  "boxen": "^8.0.1",
53
55
  "cli-table3": "^0.6.5",
54
56
  "commander": "^14.0.2",
57
+ "depcheck": "^1.4.7",
55
58
  "fast-glob": "^3.3.3",
56
59
  "picocolors": "^1.1.1",
57
60
  "ts-morph": "^27.0.2"
58
61
  },
59
62
  "devDependencies": {
60
63
  "@changesets/cli": "^2.29.8",
64
+ "@types/depcheck": "^0.9.0",
61
65
  "@types/glob": "^8.1.0",
62
66
  "@types/node": "^25.0.10",
63
67
  "eslint": "^9.39.2",
@@ -0,0 +1,15 @@
1
+ #!/bin/bash
2
+
3
+ # scripts/core/check_deps.sh
4
+
5
+ echo "📦 Checking for unused dependencies..."
6
+
7
+ if ! command -v depcheck &> /dev/null; then
8
+ DEPCHECK_CMD="npx depcheck"
9
+ else
10
+ DEPCHECK_CMD="depcheck"
11
+ fi
12
+
13
+ # Run depcheck outputting JSON for parsing if needed, but for now human readable is fine for CLI
14
+ # Using --skip-missing to avoid erroring on missing peers etc.
15
+ $DEPCHECK_CMD --skip-missing=true --ignores="react-prune" .
@@ -0,0 +1,31 @@
1
+ #!/bin/bash
2
+
3
+ # scripts/core/check_package_sizes.sh
4
+
5
+ ROOT_DIR="${1:-.}"
6
+
7
+ echo "⚖️ Checking package sizes (top 15 heaviest)..."
8
+
9
+ if [ ! -d "$ROOT_DIR/node_modules" ]; then
10
+ echo "⚠️ node_modules not found. Cannot calculate sizes. Please install dependencies."
11
+ exit 0
12
+ fi
13
+
14
+ # We use du (disk usage) to get sizes of folders in node_modules.
15
+ # We explicitly check folders that match dependencies in package.json to be cleaner,
16
+ # OR we just check immediate children of node_modules to find the biggest ones (simplest and most useful for bloat).
17
+
18
+ # Approach: List usage of all folders in node_modules, sort by size, take top 15.
19
+ # du -h -d 1 node_modules | sort -h -r | head -n 15
20
+
21
+ # Note: -d 1 is for depth 1. -h is human readable. sort -h sorts human readable (e.g. 1G > 500M).
22
+ # Mac stats might differ slightly but modern du/sort often support -h.
23
+ # If separate sort is needed we can try standard bytes.
24
+
25
+ if [[ "$OSTYPE" == "darwin"* ]]; then
26
+ # MacOS 'du' uses -d for depth. 'sort' supports -h.
27
+ du -h -d 1 "$ROOT_DIR/node_modules" 2>/dev/null | sort -h -r | head -n 15 | awk '{print $1, $2}' | sed "s|$ROOT_DIR/node_modules/||"
28
+ else
29
+ # Linux (GNU)
30
+ du -h --max-depth=1 "$ROOT_DIR/node_modules" 2>/dev/null | sort -h -r | head -n 15 | awk '{print $1, $2}' | sed "s|$ROOT_DIR/node_modules/||"
31
+ fi
@@ -0,0 +1,45 @@
1
+ #!/bin/bash
2
+
3
+ # scripts/core/check_unused_exports.sh
4
+
5
+ ROOT_DIR="${1:-.}"
6
+ FIND_SCRIPT="${0%/*}/find_files.sh"
7
+
8
+ echo "🔎 Checking for unused exports (heuristic)..."
9
+
10
+ FILES=$($FIND_SCRIPT "$ROOT_DIR")
11
+ UNUSED_COUNT=0
12
+
13
+ for file in $FILES; do
14
+ # 1. Extract exports
15
+ # Matches: export const|function|class|let|var|type|interface Name ...
16
+ # Exclude 'default' from regex capture immediately
17
+ EXPORTS=$(grep -E "^export (const|function|class|let|var|type|interface) " "$file" | sed -E 's/^export (const|function|class|let|var|type|interface) ([a-zA-Z0-9_]+).*/\2/')
18
+
19
+ for exp in $EXPORTS; do
20
+ # Ignore common false positives
21
+ if [[ "$exp" == "default" ]] || [[ "$exp" == "metadata" ]] || [[ "$exp" == "generateMetadata" ]] || [[ "$exp" == "viewport" ]]; then
22
+ continue
23
+ fi
24
+
25
+ # Ignore Next.js API route handlers
26
+ if [[ "$exp" == "GET" ]] || [[ "$exp" == "POST" ]] || [[ "$exp" == "PUT" ]] || [[ "$exp" == "DELETE" ]] || [[ "$exp" == "PATCH" ]] || [[ "$exp" == "HEAD" ]] || [[ "$exp" == "OPTIONS" ]]; then
27
+ continue
28
+ fi
29
+
30
+ usage=$(grep -r --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" \
31
+ --exclude-dir="node_modules" --exclude-dir=".git" --exclude-dir="dist" --exclude-dir="build" \
32
+ "\b$exp\b" "$ROOT_DIR" | grep -v "$file" | wc -l)
33
+
34
+ if [ "$usage" -eq 0 ]; then
35
+ echo "⚠️ Unused export: $exp in $file"
36
+ UNUSED_COUNT=$((UNUSED_COUNT + 1))
37
+ fi
38
+ done
39
+ done
40
+
41
+ if [ "$UNUSED_COUNT" -eq 0 ]; then
42
+ echo "✅ No unused exports found!"
43
+ else
44
+ echo "found $UNUSED_COUNT unused exports."
45
+ fi
@@ -0,0 +1,64 @@
1
+ #!/bin/bash
2
+
3
+ # scripts/core/check_unused_files.sh
4
+
5
+ ROOT_DIR="${1:-.}"
6
+ FIND_SCRIPT="${0%/*}/find_files.sh"
7
+
8
+ echo "🔍 Checking for unused files..."
9
+
10
+ FILES=$($FIND_SCRIPT "$ROOT_DIR")
11
+ UNUSED_COUNT=0
12
+
13
+ for file in $FILES; do
14
+ BASENAME=$(basename "$file")
15
+
16
+ # Skip common config files
17
+ if [[ "$BASENAME" =~ \.config\.(js|ts|mjs|cjs)$ ]]; then
18
+ continue
19
+ fi
20
+
21
+ # Skip Next.js App Router conventions
22
+ if [[ "$file" == *"app/"* ]] || [[ "$file" == *"pages/"* ]]; then
23
+ if [[ "$BASENAME" =~ ^(page|layout|loading|error|not-found|template|route|middleware|default|global-error)\.(tsx|ts|js|jsx)$ ]]; then
24
+ continue
25
+ fi
26
+ fi
27
+
28
+ # Skip middleware at root
29
+ if [[ "$BASENAME" == "middleware.ts" ]] || [[ "$BASENAME" == "middleware.js" ]]; then
30
+ continue
31
+ fi
32
+
33
+ NAME_WITHOUT_EXT="${BASENAME%.*}"
34
+
35
+ # Grep for the filename (without extension) in all files
36
+ # We exclude the file itself from the search
37
+ USAGE_COUNT=$(grep -r --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" \
38
+ --exclude-dir="node_modules" --exclude-dir=".git" --exclude-dir="dist" --exclude-dir="build" \
39
+ "$NAME_WITHOUT_EXT" "$ROOT_DIR" | grep -v "$file" | wc -l)
40
+
41
+ if [ "$USAGE_COUNT" -eq 0 ]; then
42
+ # Heuristic: if file is index.ts, check if parent folder is imported
43
+ if [[ "$BASENAME" == "index.ts" ]] || [[ "$BASENAME" == "index.tsx" ]] || [[ "$BASENAME" == "index.js" ]]; then
44
+ PARENT_DIR=$(basename "$(dirname "$file")")
45
+ USAGE_COUNT=$(grep -r --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" \
46
+ --exclude-dir="node_modules" --exclude-dir=".git" --exclude-dir="dist" --exclude-dir="build" \
47
+ "$PARENT_DIR" "$ROOT_DIR" | grep -v "$file" | wc -l)
48
+
49
+ if [ "$USAGE_COUNT" -eq 0 ]; then
50
+ echo "⚠️ Unused file: $file"
51
+ UNUSED_COUNT=$((UNUSED_COUNT + 1))
52
+ fi
53
+ else
54
+ echo "⚠️ Unused file: $file"
55
+ UNUSED_COUNT=$((UNUSED_COUNT + 1))
56
+ fi
57
+ fi
58
+ done
59
+
60
+ if [ "$UNUSED_COUNT" -eq 0 ]; then
61
+ echo "✅ No unused files found!"
62
+ else
63
+ echo "found $UNUSED_COUNT unused files."
64
+ fi
@@ -0,0 +1,22 @@
1
+ #!/bin/bash
2
+
3
+ # scripts/core/find_files.sh
4
+
5
+ # Finds all relevant source files in the project, excluding ignored directories.
6
+ # Arguments:
7
+ # $1: Root directory (optional, defaults to current dir)
8
+
9
+ ROOT_DIR="${1:-.}"
10
+
11
+ find "$ROOT_DIR" \
12
+ -type f \
13
+ \( -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" \) \
14
+ -not -path "*/node_modules/*" \
15
+ -not -path "*/dist/*" \
16
+ -not -path "*/build/*" \
17
+ -not -path "*/.next/*" \
18
+ -not -path "*/coverage/*" \
19
+ -not -path "*/.git/*" \
20
+ -not -name "*.d.ts" \
21
+ -not -name "*.config.js" \
22
+ -not -name "*.config.ts"
@@ -0,0 +1,65 @@
1
+ #!/bin/bash
2
+
3
+ # scripts/core/find_usage.sh
4
+
5
+ TERM="$1"
6
+ ROOT_DIR="${2:-.}"
7
+ FIND_SCRIPT="${0%/*}/find_files.sh"
8
+
9
+ if [ -z "$TERM" ]; then
10
+ echo "Usage: react-prune find <ComponentOrFunction> [RootDir]"
11
+ exit 1
12
+ fi
13
+
14
+ echo "🔎 Searching for usage of '${TERM}'..."
15
+
16
+ FILES=$($FIND_SCRIPT "$ROOT_DIR")
17
+ TOTAL_COUNT=0
18
+
19
+ # Temporary file to store results
20
+ RESULTS_FILE=$(mktemp)
21
+
22
+ for file in $FILES; do
23
+ # Grep for the term, print line number (-n).
24
+ # Exclude the definition if possible? Hard in bash without better context,
25
+ # but we can try to exclude "export const searchTerm =" etc if we wanted,
26
+ # but simplest is just showing all occurrences and letting user judge.
27
+
28
+ # We use grep with line numbers.
29
+ MATCHES=$(grep -n "\b${TERM}\b" "$file")
30
+
31
+ if [ ! -z "$MATCHES" ]; then
32
+ # Check file size
33
+ if [[ "$OSTYPE" == "darwin"* ]]; then
34
+ FILE_SIZE=$(stat -f%z "$file")
35
+ else
36
+ FILE_SIZE=$(stat -c%s "$file")
37
+ fi
38
+
39
+ # Format size human readable (basic logic)
40
+ if [ "$FILE_SIZE" -lt 1024 ]; then
41
+ SIZE_STR="${FILE_SIZE} B"
42
+ else
43
+ SIZE_STR="$((FILE_SIZE / 1024)) KB"
44
+ fi
45
+
46
+ # Process each match
47
+ while IFS= read -r line; do
48
+ # Use | as delimiter to avoid conflict with / in paths
49
+ echo "$line" | sed "s|^|${file}: |" | sed "s|$| [${SIZE_STR}]|" >> "$RESULTS_FILE"
50
+ TOTAL_COUNT=$((TOTAL_COUNT + 1))
51
+ done <<< "$MATCHES"
52
+ fi
53
+ done
54
+
55
+ if [ "$TOTAL_COUNT" -eq 0 ]; then
56
+ echo "❌ No usage found for '${TERM}'."
57
+ else
58
+ echo "✅ Found ${TOTAL_COUNT} occurrences:"
59
+ echo ""
60
+ cat "$RESULTS_FILE"
61
+ echo ""
62
+ echo "Total: ${TOTAL_COUNT} times"
63
+ fi
64
+
65
+ rm "$RESULTS_FILE"
@@ -0,0 +1,58 @@
1
+ #!/bin/bash
2
+
3
+ # scripts/prune-check.sh
4
+
5
+ # Colors
6
+ GREEN='\033[0;32m'
7
+ RED='\033[0;31m'
8
+ YELLOW='\033[1;33m'
9
+ NC='\033[0m' # No Color
10
+
11
+ echo -e "${YELLOW}🔍 Running Project Health Check...${NC}\n"
12
+
13
+ # 1. Run Depcheck
14
+ echo -e "${YELLOW}📦 Checking for unused dependencies (depcheck)...${NC}"
15
+ if ! command -v depcheck &> /dev/null; then
16
+ echo "depcheck could not be found, running via npx..."
17
+ DEPCHECK_CMD="npx depcheck"
18
+ else
19
+ DEPCHECK_CMD="depcheck"
20
+ fi
21
+
22
+ # Run depcheck and capture output
23
+ # We use --json to parse, or just run it plainly for the user to see
24
+ $DEPCHECK_CMD --skip-missing=true --ignores="react-prune" .
25
+
26
+ DEPCHECK_EXIT_CODE=$?
27
+
28
+ if [ $DEPCHECK_EXIT_CODE -eq 0 ]; then
29
+ echo -e "${GREEN}✅ Dependencies look clean!${NC}\n"
30
+ else
31
+ echo -e "${RED}❌ Unused dependencies found.${NC}\n"
32
+ fi
33
+
34
+ # 2. Run React Prune
35
+ echo -e "${YELLOW}✂️ Running React Prune Analysis...${NC}"
36
+
37
+ # Assuming we are running this from the root of the repo where react-prune is being developed
38
+ # We can use the local built CLI or npx react-prune if installed
39
+ # For this dev context, we'll try to use the local build
40
+ if [ -f "./dist/cli.js" ]; then
41
+ echo "Using local build..."
42
+ node ./dist/cli.js analyze
43
+ else
44
+ echo "Local build not found, running build..."
45
+ npm run build --silent
46
+ node ./dist/cli.js analyze
47
+ fi
48
+
49
+ PRUNE_EXIT_CODE=$?
50
+
51
+ echo -e "\n${YELLOW}📊 Summary:${NC}"
52
+ if [ $DEPCHECK_EXIT_CODE -eq 0 ] && [ $PRUNE_EXIT_CODE -eq 0 ]; then
53
+ echo -e "${GREEN}🎉 Project is clean!${NC}"
54
+ exit 0
55
+ else
56
+ echo -e "${RED}⚠️ Issues detected. Please review above output.${NC}"
57
+ exit 1
58
+ fi