-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathanalyze-packages.js
145 lines (127 loc) · 4.29 KB
/
analyze-packages.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
const fs = require("fs")
const path = require("path")
const parser = require("@babel/parser")
const traverse = require("@babel/traverse").default
const glob = require("glob")
// Function to get all JS/TS files in the project
function getProjectFiles(projectRoot) {
return glob.sync("**/*.{js,jsx,ts,tsx}", {
cwd: projectRoot,
ignore: ["node_modules/**", ".next/**", "out/**", "build/**", "dist/**"],
})
}
// Function to parse package.json and get dependencies
function getDependencies(projectRoot) {
const packageJson = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8"))
return {
...packageJson.dependencies,
...packageJson.devDependencies,
}
}
// Function to get all exports from a package
function getPackageExports(packageName) {
try {
const packagePath = require.resolve(packageName)
const pkg = require(packagePath)
return Object.keys(pkg)
} catch (error) {
console.warn(`Could not analyze exports for package: ${packageName}`)
return []
}
}
// Function to analyze imports in a file
function analyzeFileImports(filePath, projectRoot) {
const content = fs.readFileSync(path.join(projectRoot, filePath), "utf8")
const imports = new Set()
try {
const ast = parser.parse(content, {
sourceType: "module",
plugins: ["jsx", "typescript", "decorators-legacy"],
})
traverse(ast, {
ImportDeclaration(path) {
const source = path.node.source.value
if (!source.startsWith(".") && !source.startsWith("/")) {
const packageName = source.split("/")[0]
path.node.specifiers.forEach((specifier) => {
if (specifier.type === "ImportSpecifier") {
imports.add(`${packageName}:${specifier.imported.name}`)
}
})
}
},
})
} catch (error) {
console.warn(`Error parsing file ${filePath}:`, error.message)
}
return imports
}
// Main function to analyze the project
async function analyzeProject(projectRoot) {
const files = getProjectFiles(projectRoot)
const dependencies = getDependencies(projectRoot)
const packageImports = new Map()
const unusedExports = new Map()
// Initialize package exports
for (const pkg of Object.keys(dependencies)) {
const exports = getPackageExports(pkg)
if (exports.length > 0) {
unusedExports.set(pkg, new Set(exports))
}
}
// Analyze each file
for (const file of files) {
const imports = analyzeFileImports(file, projectRoot)
for (const imp of imports) {
const [pkg, exportName] = imp.split(":")
if (unusedExports.has(pkg)) {
unusedExports.get(pkg).delete(exportName)
}
if (!packageImports.has(pkg)) {
packageImports.set(pkg, new Set())
}
packageImports.get(pkg).add(exportName)
}
}
// Find packages with unused exports
const packagesWithUnusedExports = []
for (const [pkg, unused] of unusedExports) {
if (unused.size > 0) {
packagesWithUnusedExports.push({
package: pkg,
unusedCount: unused.size,
totalExports: getPackageExports(pkg).length,
unusedExports: Array.from(unused),
})
}
}
return packagesWithUnusedExports
}
// Run the analysis
const projectRoot = process.cwd()
analyzeProject(projectRoot)
.then((results) => {
console.log("\nPackages with unused exports:")
console.log("============================")
const optimizePackageImports = results
.filter((pkg) => pkg.unusedCount > 5) // Threshold for significant unused exports
.map((pkg) => pkg.package)
results.forEach((pkg) => {
console.log(`\n${pkg.package}:`)
console.log(`- ${pkg.unusedCount} unused exports out of ${pkg.totalExports} total exports`)
console.log(
"- Unused exports:",
pkg.unusedExports.slice(0, 5).join(", ") +
(pkg.unusedExports.length > 5 ? ` and ${pkg.unusedExports.length - 5} more...` : ""),
)
})
console.log("\nRecommended optimizePackageImports configuration:")
console.log("=============================================")
console.log("Add this to your next.config.js:")
console.log("optimizePackageImports: [")
optimizePackageImports.forEach((pkg) => console.log(` '${pkg}',`))
console.log("]")
})
.catch((error) => {
console.error("Error analyzing project:", error)
})