Skip to content

Instantly share code, notes, and snippets.

@zthxxx
Last active August 29, 2022 07:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zthxxx/d118bb1b783209d925e0eaf4c77fe130 to your computer and use it in GitHub Desktop.
Save zthxxx/d118bb1b783209d925e0eaf4c77fe130 to your computer and use it in GitHub Desktop.
some scripts for analysis dependency graph from entry, with result of dependency-cruiser
import path from 'path'
import { argv, fs } from 'zx'
import type { IModule } from 'dependency-cruiser'
import {
keyBy,
} from 'lodash-es'
/**
* depcruise -p -c dependency-cruiser.config.js --output-type json src > .logs/depcruise/src.json
*/
import cruiserResult from './.logs/depcruise/src.json'
const selfPath = path.relative(process.cwd(), process.argv[1])
if (!(argv.track || argv['find-unused'])) {
console.log(`
Usage:
tsx ${selfPath} --track <track-file>
tsx ${selfPath} --track-all <track-file>
tsx ${selfPath} --find-unused
`)
process.exit(1)
}
const keeps = (await fs.readFile('.logs/keeps.log', { encoding: 'utf-8' }))
.trim()
.split('\n')
const entries = [
'src/.umi/umi.ts',
'src/app.tsx',
'src/global.ts',
'src/config/theme/index.js',
'src/config/routes/index.ts',
'src/pages/loginFail.tsx',
'src/pages/404.jsx',
'src/pages/loading.tsx',
'src/layouts/index.tsx',
// 'src/pages/xxx/xxx',
]
const modules: IModule[] = cruiserResult.modules as IModule[]
const moduleMap: Record<string, IModule> = keyBy(modules, 'source')
const track = (trackFile: string) => {
console.log('track file:', trackFile)
const links: string[] = []
const visited: Set<string> = new Set()
const queue: string[] = [trackFile]
while (queue.length) {
const file = queue.shift()!
if (visited.has(file)) {
continue
}
visited.add(file)
links.unshift(file)
const module = moduleMap[file]
if (!module) {
throw new Error(`not found module of "${trackFile}"`)
}
const parent = module.dependents[0]
if (parent) {
queue.push(parent)
}
}
console.log('links:', links)
}
const trackAll = (trackFile: string) => {
const getToRoot = (parent: IModule, source: string, refs: Set<string>): string[][] => {
const parents = parent.dependents
.filter(file => !refs.has(file))
.map(file => moduleMap[file])
.map(module => getToRoot(module, module.source, new Set([...refs, module.source])))
.flat()
if (!parent.dependents.length) return [[source]]
parents.forEach(list => list.push(source))
return parents
}
const module = moduleMap[trackFile]
if (!module) {
throw new Error(`not found module of "${trackFile}"`)
}
const roots = new Set(entries)
const links = getToRoot(module, trackFile, new Set([trackFile]))
// filter top to link
.filter(link => roots.has(link[0]))
.sort((a, b) => (a.length > b.length ? 1 : -1))
const fileCounts: Record<string, number> = {}
links.forEach(link => {
link.forEach(file => {
if (!fileCounts[file]) {
fileCounts[file] = 0
}
fileCounts[file] += 1
})
})
const maxCount = Math.max(...Object.values(fileCounts))
const maxCountLen = String(maxCount).length
console.log(`
total links count: ${links.length}
preview:
`)
links.slice(0, 10).forEach(link => {
console.log(
'link:',
link.map(file => {
const count = String(fileCounts[file]).padStart(maxCountLen, ' ')
return `${count} ${file}`
}),
)
})
}
const findUnusedFiles = () => {
const stack = [...entries]
const visited = new Set<string>()
while (stack.length) {
const file = stack.shift()!
if (visited.has(file)) continue
visited.add(file)
const module = moduleMap[file]
if (!module) {
throw new Error(`not find module: ${file}`)
}
const dependencies: string[] = module.dependencies
.map(({ resolved }) => resolved)
stack.push(...dependencies)
}
const allFiles = Object.keys(moduleMap)
const unused = allFiles
.filter(file => (
!visited.has(file)
&& file.startsWith('src/')
&& !keeps.some(prefix => file.startsWith(prefix))
))
console.log(unused.join('\n'))
}
if (argv.track && argv.all) {
trackAll(argv.track)
} else if (argv.track) {
track(argv.track)
} else if (argv['find-unused']) {
findUnusedFiles()
}
import path from 'path'
import { fs, argv } from 'zx'
const cwd = process.cwd()
const selfPath = path.relative(cwd, process.argv[1])
if (!argv.from) {
console.log(`
Usage:
tsx ${selfPath} --from <list logfile>
`)
process.exit(1)
}
const files = (await fs.readFile(argv.from, { encoding: 'utf-8' }))
.trim()
.split('\n')
.filter(Boolean)
.map(file => path.join(cwd, file))
await Promise.all(files.map(file => fs.remove(file)))

some dependencies graph analysis scripts, with result of dependency-cruiser

pnpm i tsx zx lodash-es dependency-cruiser

mkdir -p .logs/depcruise
touch .logs/keeps.log

depcruise -p -c dependency-cruiser.config.js --output-type json src > .logs/depcruise/src.json

tsx .deps.mts --track <track-file>
tsx .deps.mts --track-all <track-file>
tsx .deps.mts --find-unused

tsx .deps.mts --find-unused > .logs/unused-files.log
tsx .rm-files.mts --from .logs/unused-files.log

find src -empty -type d -delete
/** @type {import('dependency-cruiser').IConfiguration} */
module.exports = {
/** @type {import('https://github.com/sverweij/dependency-cruiser/blob/v10.3.0/types/options.d.ts').ICruiseOptions} */
options: {
// regular expression to match
exclude: [
'node_modules',
'coverage',
'packages',
'\\.(test|spec|mock)\\.ts',
'/es',
'/lib',
'\\.umi',
],
extraExtensionsToScan: ['.md', '.jpg', '.png', '.json'],
tsConfig: {
fileName: './tsconfig.json',
},
/** https://github.com/sverweij/dependency-cruiser/blob/v10.3.0/types/options.d.ts#L125 */
tsPreCompilationDeps: true,
/**
* if true combines the package.jsons found from the module up to the base
* folder the cruise is initiated from. Useful for how (some) mono-repos
* manage dependencies & dependency definitions.
*/
combinedDependencies: true,
prefix: `vscode://file/${process.cwd()}/`,
/** @type {import('https://github.com/sverweij/dependency-cruiser/blob/v10.3.0/types/reporter-options.d.ts').IReporterOptions} */
reporterOptions: {
dot: {
theme: {
graph: {
splines: 'ortho',
rankdir: 'TB',
},
},
},
ddot: {
theme: {
graph: {
splines: 'ortho',
rankdir: 'TB',
},
},
},
archi: {
theme: {
graph: {
splines: 'ortho',
rankdir: 'TB',
},
},
},
},
progress: { type: 'performance-log' },
},
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment