diff options
author | Gar <gar+gh@danger.computer> | 2022-04-14 00:18:02 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-04-14 00:18:02 +0300 |
commit | 877138eb4d1c4470c4164fc6e057d082b120fb05 (patch) | |
tree | 84a36a0dfef55f107731dfe97bb9af17e77fd2c9 /scripts | |
parent | 1f2d89a4c90b94b02f233b334144ff3551c87726 (diff) |
chore: add DEPENDENCIES.md (#4710)
This adds a script to generate dependency information for the npm cli.
The dependency info shows all of the packages in npm that the npm-cli
team themselves manage.
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/dependency-graph.js | 174 | ||||
-rw-r--r-- | scripts/npm-cli-repos.txt | 88 |
2 files changed, 262 insertions, 0 deletions
diff --git a/scripts/dependency-graph.js b/scripts/dependency-graph.js new file mode 100644 index 000000000..318b9f39b --- /dev/null +++ b/scripts/dependency-graph.js @@ -0,0 +1,174 @@ +'use strict' + +// Generates our dependency graph documents in DEPENDENCIES.md. + +const Arborist = require('@npmcli/arborist') +const fs = require('fs') + +// To re-create npm-cli-repos.txt run: +/* eslint-disable-next-line max-len */ +// gh api "/graphql" -F query='query { search (query: "org:npm topic:npm-cli", type: REPOSITORY, first:100) { nodes { ... on Repository { name } } } }' --jq '.data.search.nodes[].name'|sort +const repos = fs.readFileSync('./scripts/npm-cli-repos.txt', 'utf8').trim().split('\n') + +// these have a different package name than the repo name, and are ours. +const aliases = { + semver: 'node-semver', + abbrev: 'abbrev-js', +} + +// These are entries in npm-cli-repos.txt that correlate to namespaced repos. +// If we see a bare package with just this name, it's NOT ours +const namespaced = [ + 'arborist', + 'ci-detect', + 'config', + 'disparity-colors', + 'eslint-config', + 'exec', + 'fs', + 'git', + 'installed-package-contents', + 'lint', + 'map-workspaces', + 'metavuln-calculator', + 'move-file', + 'name-from-folder', + 'node-gyp', + 'package-json', + 'promise-spawn', + 'run-script', + 'template-oss', +] + +function isOurs (name) { + if (name.startsWith('libnpm')) { + return true + } + if (name.startsWith('@npmcli')) { + return true + } + if (aliases[name]) { + return true + } + // this will prevent e.g. `fs` from being mistaken as ours + if (namespaced.includes(name)) { + return false + } + if (repos.includes(name)) { + return true + } + return false +} + +function escapeName (name) { + if (name.startsWith('@')) { + return `${stripName(name)}["${name}"]` + } + return name +} +function stripName (name) { + if (name.startsWith('@')) { + const parts = name.slice(1).split('/') + return `${parts[0]}-${parts[1]}` + } + return name +} + +const main = async function () { + const arborist = new Arborist({ + prefix: process.cwd(), + path: process.cwd(), + }) + const tree = await arborist.loadVirtual({ path: process.cwd(), name: 'npm' }) + tree.name = 'npm' + + const { + heirarchy: heirarchyOurs, + annotations: annotationsOurs, + } = walk(tree, true) + + const { + annotations: annotationsAll, + } = walk(tree, false) + + const out = [ + '# npm dependencies', + '', + '## `github.com/npm/` only', + '```mermaid', + 'graph LR;', + ...annotationsOurs.sort(), + '```', + '', + '## all dependencies', + '```mermaid', + 'graph LR;', + ...annotationsAll.sort(), + '```', + '', + '## npm dependency heirarchy', + '', + 'These are the groups of dependencies in npm that depend on each other.', + 'Each group depends on packages lower down the chain, nothing depends on', + 'packages higher up the chain.', + '', + ` - ${heirarchyOurs.reverse().join('\n - ')}`, + ] + fs.writeFileSync('DEPENDENCIES.md', out.join('\n')) + console.log('wrote to DEPENDENCIES.md') +} + +const walk = function (tree, onlyOurs) { + const annotations = [] // mermaid dependency annotations + const dependedBy = {} + iterate(tree, dependedBy, annotations, onlyOurs) + const allDeps = new Set(Object.keys(dependedBy)) + const foundDeps = new Set() + const heirarchy = [] + while (allDeps.size) { + const level = [] + for (const dep of allDeps) { + if (!dependedBy[dep].size) { + level.push(dep) + foundDeps.add(dep) + } + } + for (const dep of allDeps) { + for (const found of foundDeps) { + allDeps.delete(found) + dependedBy[dep].delete(found) + } + } + if (!level.length) { + throw new Error('Would do an infinite loop here, need to debug') + } + heirarchy.push(level.join(', ')) + } + + return { heirarchy, annotations } +} +const iterate = function (node, dependedBy, annotations, onlyOurs) { + if (!dependedBy[node.packageName]) { + dependedBy[node.packageName] = new Set() + } + for (const [name, edge] of node.edgesOut) { + if ( + (!onlyOurs || isOurs(name)) && !node.dev + ) { + if (!dependedBy[node.packageName].has(edge.name)) { + dependedBy[node.packageName].add(edge.name) + annotations.push(` ${stripName(node.packageName)}-->${escapeName(edge.name)};`) + if (edge.to) { + iterate(edge.to.target, dependedBy, annotations, onlyOurs) + } + } + } + } +} + +main().then(() => { + process.exit(0) +}).catch(err => { + console.error(err) + process.exit(1) +}) diff --git a/scripts/npm-cli-repos.txt b/scripts/npm-cli-repos.txt new file mode 100644 index 000000000..bbbf2e31c --- /dev/null +++ b/scripts/npm-cli-repos.txt @@ -0,0 +1,88 @@ +abbrev-js +arborist +are-we-there-yet +benchmarks +bin-links +cacache +ci-detect +cli +cmd-shim +config +create-oss +dezalgo +disparity-colors +doctornpm +documentation +eslint-config +exec +fs +fs-minipass +gauge +git +hosted-git-info +ignore-walk +infer-owner +inflight +ini +init-package-json +installed-package-contents +libnpmaccess +libnpmfund +libnpmhook +libnpmorg +libnpmpack +libnpmpublish +libnpmsearch +libnpmteam +libnpmversion +lint +make-fetch-happen +map-workspaces +metavuln-calculator +minipass-fetch +move-file +mute-stream +name-from-folder +nock-registry +node-gyp +node-semver +node-tar +node-which +nopt +normalize-package-data +npm-audit-report +npm-birthday +npm-bundled +npm-install-checks +npm-install-script +npm-normalize-package-bin +npm-package-arg +npm-packlist +npm-profile +npm-registry-fetch +npm-user-validate +npmlog +package-json +pacote +parse-conflict-json +proc-log +proggy +promise-spawn +promzard +read +read-cmd-shim +read-package-json +read-package-json-fast +readdir-scoped-modules +rfcs +run-script +ssri +statusboard +stringify-package +template-oss +treeverse +unique-filename +unique-slug +validate-npm-package-name +wrappy +write-file-atomic |