@esbenwiberg/archmap 0.1.0
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 +361 -0
- package/dist/bin/archmap.d.ts +3 -0
- package/dist/bin/archmap.d.ts.map +1 -0
- package/dist/bin/archmap.js +72 -0
- package/dist/bin/archmap.js.map +1 -0
- package/dist/src/cache.d.ts +30 -0
- package/dist/src/cache.d.ts.map +1 -0
- package/dist/src/cache.js +78 -0
- package/dist/src/cache.js.map +1 -0
- package/dist/src/churn.d.ts +6 -0
- package/dist/src/churn.d.ts.map +1 -0
- package/dist/src/churn.js +32 -0
- package/dist/src/churn.js.map +1 -0
- package/dist/src/classify.d.ts +16 -0
- package/dist/src/classify.d.ts.map +1 -0
- package/dist/src/classify.js +60 -0
- package/dist/src/classify.js.map +1 -0
- package/dist/src/commands/check.d.ts +5 -0
- package/dist/src/commands/check.d.ts.map +1 -0
- package/dist/src/commands/check.js +33 -0
- package/dist/src/commands/check.js.map +1 -0
- package/dist/src/commands/classify.d.ts +5 -0
- package/dist/src/commands/classify.d.ts.map +1 -0
- package/dist/src/commands/classify.js +31 -0
- package/dist/src/commands/classify.js.map +1 -0
- package/dist/src/commands/explain.d.ts +5 -0
- package/dist/src/commands/explain.d.ts.map +1 -0
- package/dist/src/commands/explain.js +34 -0
- package/dist/src/commands/explain.js.map +1 -0
- package/dist/src/commands/export.d.ts +48 -0
- package/dist/src/commands/export.d.ts.map +1 -0
- package/dist/src/commands/export.js +82 -0
- package/dist/src/commands/export.js.map +1 -0
- package/dist/src/commands/risk.d.ts +6 -0
- package/dist/src/commands/risk.d.ts.map +1 -0
- package/dist/src/commands/risk.js +27 -0
- package/dist/src/commands/risk.js.map +1 -0
- package/dist/src/commands/scan.d.ts +4 -0
- package/dist/src/commands/scan.d.ts.map +1 -0
- package/dist/src/commands/scan.js +37 -0
- package/dist/src/commands/scan.js.map +1 -0
- package/dist/src/config.d.ts +43 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +87 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/files.d.ts +2 -0
- package/dist/src/files.d.ts.map +1 -0
- package/dist/src/files.js +31 -0
- package/dist/src/files.js.map +1 -0
- package/dist/src/graph.d.ts +11 -0
- package/dist/src/graph.d.ts.map +1 -0
- package/dist/src/graph.js +40 -0
- package/dist/src/graph.js.map +1 -0
- package/dist/src/risk.d.ts +11 -0
- package/dist/src/risk.d.ts.map +1 -0
- package/dist/src/risk.js +37 -0
- package/dist/src/risk.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../../src/files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,MAAM,OAAO,GAAG,aAAa,CAAC;AAC9B,MAAM,SAAS,GAAG,0BAA0B,CAAC;AAC7C,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;AAEhE,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,SAAS,IAAI,CAAC,IAAY;QACxB,IAAI,IAAI,CAAC;QACT,IAAI,CAAC;YACH,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACvB,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;oBAC1B,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;YACD,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACjE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IAED,IAAI,CAAC,KAAK,CAAC,CAAC;IACZ,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface FileNode {
|
|
2
|
+
ca: number;
|
|
3
|
+
ce: number;
|
|
4
|
+
tca: number;
|
|
5
|
+
dependents: string[];
|
|
6
|
+
}
|
|
7
|
+
export interface Topology {
|
|
8
|
+
files: Record<string, FileNode>;
|
|
9
|
+
}
|
|
10
|
+
export declare function buildTopology(entry: string): Promise<Topology>;
|
|
11
|
+
//# sourceMappingURL=graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/graph.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;CACjC;AAED,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CA0BpE"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { cruise } from "dependency-cruiser";
|
|
2
|
+
export async function buildTopology(entry) {
|
|
3
|
+
const result = await cruise([entry], {
|
|
4
|
+
doNotFollow: { path: "node_modules" },
|
|
5
|
+
exclude: { path: "\\.(test|spec)\\.tsx?$" },
|
|
6
|
+
});
|
|
7
|
+
const modules = result.output.modules;
|
|
8
|
+
const files = {};
|
|
9
|
+
for (const m of modules) {
|
|
10
|
+
// Skip non-repo modules: Node core (fs, path, …) and externals under
|
|
11
|
+
// node_modules. They surface as leaf nodes in the cruise output but are
|
|
12
|
+
// not files we classify, and counting them pollutes risk rankings.
|
|
13
|
+
if (m.coreModule || m.source.includes("node_modules/"))
|
|
14
|
+
continue;
|
|
15
|
+
const ce = m.dependencies.filter((d) => !d.coreModule).length;
|
|
16
|
+
const deps = m.dependents ?? [];
|
|
17
|
+
files[m.source] = { ca: deps.length, ce, tca: 0, dependents: deps };
|
|
18
|
+
}
|
|
19
|
+
computeTransitiveCa(files);
|
|
20
|
+
return { files };
|
|
21
|
+
}
|
|
22
|
+
function computeTransitiveCa(files) {
|
|
23
|
+
for (const file of Object.keys(files)) {
|
|
24
|
+
const visited = new Set();
|
|
25
|
+
const queue = [file];
|
|
26
|
+
while (queue.length > 0) {
|
|
27
|
+
const current = queue.pop();
|
|
28
|
+
for (const dep of files[current]?.dependents ?? []) {
|
|
29
|
+
if (!visited.has(dep)) {
|
|
30
|
+
visited.add(dep);
|
|
31
|
+
queue.push(dep);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// transitive Ca = all reachable dependents (excluding self)
|
|
36
|
+
visited.delete(file);
|
|
37
|
+
files[file].tca = visited.size;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph.js","sourceRoot":"","sources":["../../src/graph.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAa5C,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAa;IAC/C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,CAAC,KAAK,CAAC,EAAE;QACnC,WAAW,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE;QACrC,OAAO,EAAE,EAAE,IAAI,EAAE,wBAAwB,EAAE;KAC5C,CAAC,CAAC;IAEH,MAAM,OAAO,GAAI,MAAM,CAAC,MAAc,CAAC,OAKrC,CAAC;IAEH,MAAM,KAAK,GAAsB,EAAE,CAAC;IACpC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,qEAAqE;QACrE,wEAAwE;QACxE,mEAAmE;QACnE,IAAI,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC;YAAE,SAAS;QACjE,MAAM,EAAE,GAAG,CAAC,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC;QAC9D,MAAM,IAAI,GAAG,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC;QAChC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;IACtE,CAAC;IAED,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAC3B,OAAO,EAAE,KAAK,EAAE,CAAC;AACnB,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAwB;IACnD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC;QACrB,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,EAAG,CAAC;YAC7B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,EAAE,UAAU,IAAI,EAAE,EAAE,CAAC;gBACnD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBACtB,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBACjB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAClB,CAAC;YACH,CAAC;QACH,CAAC;QACD,4DAA4D;QAC5D,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IACjC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Topology } from "./graph.js";
|
|
2
|
+
import type { ChurnData } from "./churn.js";
|
|
3
|
+
export interface RiskScore {
|
|
4
|
+
risk: number;
|
|
5
|
+
structural: number;
|
|
6
|
+
churn: number;
|
|
7
|
+
tca: number;
|
|
8
|
+
commits: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function computeRiskScores(topology: Topology, churnMap: Map<string, ChurnData>): Map<string, RiskScore>;
|
|
11
|
+
//# sourceMappingURL=risk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"risk.d.ts","sourceRoot":"","sources":["../../src/risk.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,GAC/B,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAuCxB"}
|
package/dist/src/risk.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export function computeRiskScores(topology, churnMap) {
|
|
2
|
+
const files = Object.keys(topology.files);
|
|
3
|
+
// raw scores per file
|
|
4
|
+
const raws = new Map();
|
|
5
|
+
for (const file of files) {
|
|
6
|
+
const node = topology.files[file];
|
|
7
|
+
const commits = churnMap.get(file)?.commits ?? 0;
|
|
8
|
+
const structural = Math.log1p(node.ca) + 1.5 * Math.log1p(node.tca);
|
|
9
|
+
const churnRaw = Math.log1p(commits);
|
|
10
|
+
raws.set(file, { structural, churn: churnRaw, tca: node.tca, commits });
|
|
11
|
+
}
|
|
12
|
+
// combined raw risk
|
|
13
|
+
const rawRisks = [];
|
|
14
|
+
for (const [file, r] of raws) {
|
|
15
|
+
rawRisks.push({ file, raw: 0.6 * r.structural + 0.4 * r.churn });
|
|
16
|
+
}
|
|
17
|
+
rawRisks.sort((a, b) => a.raw - b.raw);
|
|
18
|
+
// percentile rank (0–100), ties share same percentile
|
|
19
|
+
const scores = new Map();
|
|
20
|
+
const n = rawRisks.length;
|
|
21
|
+
for (let i = 0; i < n; i++) {
|
|
22
|
+
const { file, raw } = rawRisks[i];
|
|
23
|
+
const r = raws.get(file);
|
|
24
|
+
// count how many have strictly lower raw risk
|
|
25
|
+
const rank = rawRisks.filter((x) => x.raw < raw).length;
|
|
26
|
+
const percentile = n === 1 ? 50 : Math.round((rank / (n - 1)) * 100);
|
|
27
|
+
scores.set(file, {
|
|
28
|
+
risk: percentile,
|
|
29
|
+
structural: Math.round(r.structural * 100) / 100,
|
|
30
|
+
churn: Math.round(r.churn * 100) / 100,
|
|
31
|
+
tca: r.tca,
|
|
32
|
+
commits: r.commits,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return scores;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=risk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"risk.js","sourceRoot":"","sources":["../../src/risk.ts"],"names":[],"mappings":"AAWA,MAAM,UAAU,iBAAiB,CAC/B,QAAkB,EAClB,QAAgC;IAEhC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE1C,sBAAsB;IACtB,MAAM,IAAI,GAAG,IAAI,GAAG,EAA+E,CAAC;IACpG,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,IAAI,CAAC,CAAC;QACjD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpE,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,oBAAoB;IACpB,MAAM,QAAQ,GAAyC,EAAE,CAAC;IAC1D,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;QAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC,CAAC,UAAU,GAAG,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IACnE,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;IAEvC,sDAAsD;IACtD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC5C,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC;QAC1B,8CAA8C;QAC9C,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,MAAM,CAAC;QACxD,MAAM,UAAU,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QACrE,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE;YACf,IAAI,EAAE,UAAU;YAChB,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,GAAG,GAAG,CAAC,GAAG,GAAG;YAChD,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG;YACtC,GAAG,EAAE,CAAC,CAAC,GAAG;YACV,OAAO,EAAE,CAAC,CAAC,OAAO;SACnB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@esbenwiberg/archmap",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Classify every file in a Node+TypeScript repo as leaf | branch | hub based on dependency topology",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=18"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"archmap": "./dist/bin/archmap.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist",
|
|
14
|
+
"!dist/src/__tests__",
|
|
15
|
+
"README.md"
|
|
16
|
+
],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"prepublishOnly": "npm run build",
|
|
23
|
+
"dev": "tsx bin/archmap.ts",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"commander": "^12.0.0",
|
|
29
|
+
"dependency-cruiser": "^16.0.0",
|
|
30
|
+
"micromatch": "^4.0.0",
|
|
31
|
+
"yaml": "^2.0.0"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/micromatch": "^4.0.0",
|
|
35
|
+
"@types/node": "^20.0.0",
|
|
36
|
+
"tsx": "^4.0.0",
|
|
37
|
+
"typescript": "^5.0.0",
|
|
38
|
+
"vitest": "^1.0.0"
|
|
39
|
+
}
|
|
40
|
+
}
|