Skip to content

Commit be74e50

Browse files
philibealisalupi
andcommitted
docs(deps): script analyse deps (#5470)
* docs(deps): script analyse deps Signed-off-by: protobuf-ci-cd <[email protected]> * feat: add sorted record * fix: gitignore --------- Signed-off-by: protobuf-ci-cd <[email protected]> Co-authored-by: lisa <[email protected]>
1 parent f7a0e49 commit be74e50

File tree

4 files changed

+147
-8
lines changed

4 files changed

+147
-8
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ storybook-static
1717
.reports
1818
.pnpm-store
1919

20+
deps.json
21+
depsFiltered.json
22+
2023
# VSCode extension "Local History"
2124
.history
2225

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
"oxc": "oxlint -c .oxlintrc.json",
3434
"oxc:fix": "pnpm run oxc --fix",
3535
"prepare": "husky",
36+
"analyse:deps:ui": "tsx ./utils/scripts/analyse-deps.ts packages/ui/src/components",
37+
"analyse:deps:form": "tsx ./utils/scripts/analyse-deps.ts packages/form/src/components",
3638
"tokens:update": "tsx ./utils/scripts/figma-synchronise-tokens.tsx && pnpm run format packages/themes/src/themes/console",
3739
"icons:update": "tsx ./utils/scripts/generate-icons-file.tsx && pnpm run format packages/icons",
3840
"release": "pnpm build && pnpm changeset publish",

pnpm-lock.yaml

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

utils/scripts/analyse-deps.ts

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import fs from 'node:fs'
2+
import path from 'node:path'
3+
4+
const cwd = process.cwd()
5+
const args = process.argv.slice(2)
6+
7+
const srcDir = path.join(cwd, args[0] ?? 'packages/ui/src/components')
8+
9+
type DependencyGraph = {
10+
[componentName: string]: {
11+
dependsOn: string[]
12+
}
13+
}
14+
15+
const graph: DependencyGraph = {}
16+
17+
const walk = (dir: string): string[] => {
18+
const results = []
19+
const list = fs.readdirSync(dir)
20+
21+
for (const file of list) {
22+
const filePath = path.join(dir, file)
23+
24+
const stat = fs.statSync(filePath)
25+
if (stat.isDirectory()) {
26+
const subDirResult = walk(filePath)
27+
28+
results.push(...subDirResult)
29+
} else {
30+
results.push(filePath)
31+
}
32+
}
33+
34+
return results
35+
}
36+
37+
const filesToAnalyze = walk(srcDir)
38+
39+
for (const file of filesToAnalyze) {
40+
if (
41+
(file.endsWith('.ts') || file.endsWith('.tsx')) &&
42+
!['stories.tsx', 'test.tsx'].some(end => file.endsWith(end))
43+
) {
44+
const relativePath = path.relative(srcDir, file)
45+
const componentName = relativePath.split('/')[0]
46+
47+
const content = fs.readFileSync(file, 'utf8')
48+
49+
const matches = content.matchAll(
50+
/import\s+(?:.*?from\s+)?(['"])(.*?)(['"])|export\s+{?\s*(\w+)?\s*}?$/g,
51+
)
52+
53+
for (const match of matches) {
54+
const importedFile = match[2] || null
55+
if (importedFile) {
56+
const normalizedFile = path
57+
.relative(srcDir, path.join(path.dirname(file), importedFile))
58+
.replace(/\.tsx?$/, '')
59+
60+
if (
61+
![
62+
'react',
63+
'react-vite',
64+
'vitest',
65+
'styled',
66+
'@emotion/styled',
67+
'components/',
68+
].some(string => normalizedFile.endsWith(string))
69+
) {
70+
const importedComponent = normalizedFile.split('/').reverse()[0]
71+
72+
if (!graph[componentName]) {
73+
graph[componentName] = { dependsOn: [] }
74+
}
75+
76+
if (importedComponent !== componentName) {
77+
const { dependsOn } = graph[componentName]
78+
const newDeps = [...new Set([...dependsOn, importedComponent])]
79+
graph[componentName].dependsOn = newDeps
80+
}
81+
}
82+
}
83+
}
84+
}
85+
}
86+
87+
const sortGraphByDependencySize = ({
88+
currentGraph,
89+
order = 'asc',
90+
}: {
91+
currentGraph: DependencyGraph
92+
order?: 'asc' | 'desc'
93+
}) => {
94+
const entries = Object.entries(currentGraph)
95+
96+
entries.sort((a, b) => {
97+
const aSize = a[1].dependsOn.length
98+
const bSize = b[1].dependsOn.length
99+
100+
return order === 'asc' ? bSize - aSize : aSize - bSize
101+
})
102+
103+
return {
104+
order,
105+
sortedComponent: entries,
106+
}
107+
}
108+
109+
const sortedComponent = sortGraphByDependencySize({
110+
currentGraph: graph,
111+
order: 'desc',
112+
})
113+
114+
const { info } = console
115+
info('sortedComponent', JSON.stringify(graph, null, 2))
116+
fs.writeFileSync('deps.json', JSON.stringify(sortedComponent, null, 2))
117+
118+
const componentNames = new Set(
119+
sortedComponent.sortedComponent.map(([name]) => name),
120+
)
121+
const asRecord = Object.fromEntries(
122+
sortedComponent.sortedComponent
123+
.map(([name, count]) => {
124+
const { dependsOn } = count as { dependsOn: string[] }
125+
const filteredDeps = dependsOn.filter(dependency =>
126+
componentNames.has(dependency),
127+
)
128+
129+
return [name, filteredDeps.length] as const
130+
})
131+
.sort(([, aCount], [, bCount]) => aCount - bCount),
132+
)
133+
134+
fs.writeFileSync('depsFiltered.json', JSON.stringify(asRecord, null, 2))

0 commit comments

Comments
 (0)