@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,60 @@
|
|
|
1
|
+
import { matchOverride } from "./config.js";
|
|
2
|
+
export function classifyFile(file, topology, config, riskScores) {
|
|
3
|
+
const riskScore = riskScores?.get(file) ?? null;
|
|
4
|
+
const override = matchOverride(file, config.overrides);
|
|
5
|
+
if (override) {
|
|
6
|
+
const node = topology.files[file];
|
|
7
|
+
const ca = node?.ca ?? 0;
|
|
8
|
+
const tca = node?.tca ?? 0;
|
|
9
|
+
const ce = node?.ce ?? 0;
|
|
10
|
+
const total = ca + ce;
|
|
11
|
+
return {
|
|
12
|
+
file,
|
|
13
|
+
class: override.classification,
|
|
14
|
+
ca,
|
|
15
|
+
tca,
|
|
16
|
+
instability: total === 0 ? 0 : ce / total,
|
|
17
|
+
risk: riskScore,
|
|
18
|
+
reason: override.reason,
|
|
19
|
+
overridden: true,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
const node = topology.files[file];
|
|
23
|
+
if (!node) {
|
|
24
|
+
process.stderr.write(`warning: "${file}" not found in dependency graph — treating as leaf\n`);
|
|
25
|
+
return {
|
|
26
|
+
file,
|
|
27
|
+
class: "leaf",
|
|
28
|
+
ca: 0,
|
|
29
|
+
tca: 0,
|
|
30
|
+
instability: 0,
|
|
31
|
+
risk: riskScore,
|
|
32
|
+
reason: "not in dependency graph",
|
|
33
|
+
overridden: false,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const { ca, tca, ce } = node;
|
|
37
|
+
const total = ca + ce;
|
|
38
|
+
const instability = total === 0 ? 0 : ce / total;
|
|
39
|
+
let klass;
|
|
40
|
+
if (ca <= config.thresholds.leaf) {
|
|
41
|
+
klass = "leaf";
|
|
42
|
+
}
|
|
43
|
+
else if (ca <= config.thresholds.junction) {
|
|
44
|
+
klass = "branch";
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
klass = "hub";
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
file,
|
|
51
|
+
class: klass,
|
|
52
|
+
ca,
|
|
53
|
+
tca,
|
|
54
|
+
instability,
|
|
55
|
+
risk: riskScore,
|
|
56
|
+
reason: `Ca=${ca} (${ca} direct, ${tca} transitive)`,
|
|
57
|
+
overridden: false,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=classify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../../src/classify.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAiB5C,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,QAAkB,EAClB,MAAqB,EACrB,UAAmC;IAEnC,MAAM,SAAS,GAAG,UAAU,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;IAChD,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;IAEvD,IAAI,QAAQ,EAAE,CAAC;QACb,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;QACzB,MAAM,GAAG,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC;QAC3B,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;QACzB,MAAM,KAAK,GAAG,EAAE,GAAG,EAAE,CAAC;QACtB,OAAO;YACL,IAAI;YACJ,KAAK,EAAE,QAAQ,CAAC,cAAc;YAC9B,EAAE;YACF,GAAG;YACH,WAAW,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK;YACzC,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,UAAU,EAAE,IAAI;SACjB,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,aAAa,IAAI,sDAAsD,CACxE,CAAC;QACF,OAAO;YACL,IAAI;YACJ,KAAK,EAAE,MAAM;YACb,EAAE,EAAE,CAAC;YACL,GAAG,EAAE,CAAC;YACN,WAAW,EAAE,CAAC;YACd,IAAI,EAAE,SAAS;YACf,MAAM,EAAE,yBAAyB;YACjC,UAAU,EAAE,KAAK;SAClB,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,IAAI,CAAC;IAC7B,MAAM,KAAK,GAAG,EAAE,GAAG,EAAE,CAAC;IACtB,MAAM,WAAW,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC;IAEjD,IAAI,KAAY,CAAC;IACjB,IAAI,EAAE,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QACjC,KAAK,GAAG,MAAM,CAAC;IACjB,CAAC;SAAM,IAAI,EAAE,IAAI,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;QAC5C,KAAK,GAAG,QAAQ,CAAC;IACnB,CAAC;SAAM,CAAC;QACN,KAAK,GAAG,KAAK,CAAC;IAChB,CAAC;IAED,OAAO;QACL,IAAI;QACJ,KAAK,EAAE,KAAK;QACZ,EAAE;QACF,GAAG;QACH,WAAW;QACX,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,cAAc;QACpD,UAAU,EAAE,KAAK;KAClB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../../src/commands/check.ts"],"names":[],"mappings":"AAQA,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACxC,OAAO,CAAC,IAAI,CAAC,CA4Bf"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { resolveProject } from "../config.js";
|
|
2
|
+
import { classifyFile } from "../classify.js";
|
|
3
|
+
import { getFreshTopology, getFreshChurn } from "../cache.js";
|
|
4
|
+
import { buildTopology } from "../graph.js";
|
|
5
|
+
import { buildChurnMap } from "../churn.js";
|
|
6
|
+
import { computeRiskScores } from "../risk.js";
|
|
7
|
+
export async function checkCommand(files, opts) {
|
|
8
|
+
const { config, entry } = resolveProject(opts.config);
|
|
9
|
+
const { topology } = await getFreshTopology(entry, buildTopology);
|
|
10
|
+
const { churn: churnMap } = getFreshChurn(90, buildChurnMap);
|
|
11
|
+
const riskScores = computeRiskScores(topology, churnMap);
|
|
12
|
+
const results = files.map((f) => classifyFile(f, topology, config, riskScores));
|
|
13
|
+
const hubs = results.filter((r) => r.class === "hub");
|
|
14
|
+
const hasHubs = hubs.length > 0;
|
|
15
|
+
if (opts.json) {
|
|
16
|
+
console.log(JSON.stringify({ hasHubs, hubs, all: results }, null, 2));
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
if (hasHubs) {
|
|
20
|
+
console.log(`load-bearing (hub) files detected:`);
|
|
21
|
+
for (const h of hubs) {
|
|
22
|
+
const riskLabel = h.risk ? ` risk=${h.risk.risk}/100` : "";
|
|
23
|
+
console.log(` ${h.file} Ca=${h.ca} (${h.tca} transitive)${riskLabel}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
console.log("No hub files in changeset.");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (hasHubs)
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../../src/commands/check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAG/C,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAe,EACf,IAAyC;IAEzC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,gBAAgB,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IAElE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;IAC7D,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAEzD,MAAM,OAAO,GAAqB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAChD,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,CAC9C,CAAC;IACF,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;IACtD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IAEhC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC;SAAM,CAAC;QACN,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;YAClD,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;gBACrB,MAAM,SAAS,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,eAAe,SAAS,EAAE,CAAC,CAAC;YAC3E,CAAC;QACH,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,IAAI,OAAO;QAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../../../src/commands/classify.ts"],"names":[],"mappings":"AAQA,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACxC,OAAO,CAAC,IAAI,CAAC,CAyBf"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { resolveProject } from "../config.js";
|
|
2
|
+
import { classifyFile } from "../classify.js";
|
|
3
|
+
import { getFreshTopology, getFreshChurn } from "../cache.js";
|
|
4
|
+
import { buildTopology } from "../graph.js";
|
|
5
|
+
import { buildChurnMap } from "../churn.js";
|
|
6
|
+
import { computeRiskScores } from "../risk.js";
|
|
7
|
+
export async function classifyCommand(file, opts) {
|
|
8
|
+
const { config, entry } = resolveProject(opts.config);
|
|
9
|
+
const { topology, cacheHit } = await getFreshTopology(entry, buildTopology);
|
|
10
|
+
if (!opts.json) {
|
|
11
|
+
process.stderr.write(cacheHit ? "(cache hit)\n" : "(topology rebuilt)\n");
|
|
12
|
+
}
|
|
13
|
+
const { churn: churnMap } = getFreshChurn(90, buildChurnMap);
|
|
14
|
+
const riskScores = computeRiskScores(topology, churnMap);
|
|
15
|
+
const result = classifyFile(file, topology, config, riskScores);
|
|
16
|
+
if (opts.json) {
|
|
17
|
+
console.log(JSON.stringify(result, null, 2));
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
const instPct = (result.instability * 100).toFixed(0);
|
|
21
|
+
console.log(`${result.file}`);
|
|
22
|
+
console.log(` class: ${result.class}${result.overridden ? " (overridden)" : ""}`);
|
|
23
|
+
console.log(` ca: ${result.ca} direct, ${result.tca} transitive`);
|
|
24
|
+
console.log(` instability: ${instPct}%`);
|
|
25
|
+
if (result.risk) {
|
|
26
|
+
console.log(` risk: ${result.risk.risk}/100 (structural=${result.risk.structural}, churn=${result.risk.commits} commits/90d)`);
|
|
27
|
+
}
|
|
28
|
+
console.log(` reason: ${result.reason}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=classify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../../../src/commands/classify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAG/C,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAY,EACZ,IAAyC;IAEzC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,gBAAgB,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IAE5E,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACf,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC;IAC5E,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;IAC7D,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACzD,MAAM,MAAM,GAAmB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;IAEhF,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;SAAM,CAAC;QACN,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,WAAW,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACtD,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACzF,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,EAAE,YAAY,MAAM,CAAC,GAAG,aAAa,CAAC,CAAC;QAC5E,OAAO,CAAC,GAAG,CAAC,kBAAkB,OAAO,GAAG,CAAC,CAAC;QAC1C,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,IAAI,CAAC,IAAI,qBAAqB,MAAM,CAAC,IAAI,CAAC,UAAU,WAAW,MAAM,CAAC,IAAI,CAAC,OAAO,eAAe,CAAC,CAAC;QAC1I,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACjD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.d.ts","sourceRoot":"","sources":["../../../src/commands/explain.ts"],"names":[],"mappings":"AAKA,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACxC,OAAO,CAAC,IAAI,CAAC,CAkCf"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { resolveProject } from "../config.js";
|
|
2
|
+
import { classifyFile } from "../classify.js";
|
|
3
|
+
import { getFreshTopology } from "../cache.js";
|
|
4
|
+
import { buildTopology } from "../graph.js";
|
|
5
|
+
export async function explainCommand(file, opts) {
|
|
6
|
+
const { config, entry } = resolveProject(opts.config);
|
|
7
|
+
const { topology } = await getFreshTopology(entry, buildTopology);
|
|
8
|
+
const node = topology.files[file];
|
|
9
|
+
const classification = classifyFile(file, topology, config);
|
|
10
|
+
if (opts.json) {
|
|
11
|
+
console.log(JSON.stringify({
|
|
12
|
+
file,
|
|
13
|
+
classification,
|
|
14
|
+
dependents: node?.dependents ?? [],
|
|
15
|
+
}, null, 2));
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
console.log(`${file}`);
|
|
19
|
+
console.log(` class: ${classification.class}${classification.overridden ? " (overridden)" : ""}`);
|
|
20
|
+
console.log(` reason: ${classification.reason}`);
|
|
21
|
+
console.log(` ca: ${classification.ca} (${classification.ca} file${classification.ca === 1 ? "" : "s"} depend on this)`);
|
|
22
|
+
const deps = node?.dependents ?? [];
|
|
23
|
+
if (deps.length === 0) {
|
|
24
|
+
console.log(" dependents: none");
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
console.log(" dependents:");
|
|
28
|
+
for (const d of deps) {
|
|
29
|
+
console.log(` ${d}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=explain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explain.js","sourceRoot":"","sources":["../../../src/commands/explain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAY,EACZ,IAAyC;IAEzC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,gBAAgB,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IAElE,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,cAAc,GAAG,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAE5D,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ;YACE,IAAI;YACJ,cAAc;YACd,UAAU,EAAE,IAAI,EAAE,UAAU,IAAI,EAAE;SACnC,EACD,IAAI,EACJ,CAAC,CACF,CACF,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC;QACvB,OAAO,CAAC,GAAG,CAAC,gBAAgB,cAAc,CAAC,KAAK,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACvG,OAAO,CAAC,GAAG,CAAC,gBAAgB,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,gBAAgB,cAAc,CAAC,EAAE,KAAK,cAAc,CAAC,EAAE,QAAQ,cAAc,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC;QACjI,MAAM,IAAI,GAAG,IAAI,EAAE,UAAU,IAAI,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;YAC7B,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;gBACrB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ArchmapConfig } from "../config.js";
|
|
2
|
+
import type { Topology } from "../graph.js";
|
|
3
|
+
import type { RiskScore } from "../risk.js";
|
|
4
|
+
import type { Klass } from "../classify.js";
|
|
5
|
+
/** One file's fully-resolved verdict — everything an external consumer needs. */
|
|
6
|
+
export interface ExportedFile {
|
|
7
|
+
class: Klass;
|
|
8
|
+
ca: number;
|
|
9
|
+
tca: number;
|
|
10
|
+
instability: number;
|
|
11
|
+
risk: number | null;
|
|
12
|
+
overridden: boolean;
|
|
13
|
+
reason: string;
|
|
14
|
+
dependents: string[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Self-contained artifact for consumers that have no source tree (e.g. a
|
|
18
|
+
* hosted diff-only review bot). Keyed by `commit` so a consumer can verify it
|
|
19
|
+
* matches the PR head SHA before trusting the lookups.
|
|
20
|
+
*
|
|
21
|
+
* When built with a `scope` (the PR's changed paths), `files` is narrowed to
|
|
22
|
+
* just those paths — each still carrying its full `dependents` blast radius —
|
|
23
|
+
* and the `scope` block records which requested paths were not in the graph,
|
|
24
|
+
* so a missed lookup is loud rather than silent.
|
|
25
|
+
*/
|
|
26
|
+
export interface ExportArtifact {
|
|
27
|
+
version: 1;
|
|
28
|
+
commit: string | null;
|
|
29
|
+
generatedAt: string;
|
|
30
|
+
scope?: {
|
|
31
|
+
requested: string[];
|
|
32
|
+
missing: string[];
|
|
33
|
+
};
|
|
34
|
+
files: Record<string, ExportedFile>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Pure transform: topology + config + risk → a fully-classified artifact.
|
|
38
|
+
* No git, no graph build, no I/O — so it is trivially unit-testable.
|
|
39
|
+
*
|
|
40
|
+
* If `scope` is given, the graph is still built whole-repo (the caller does
|
|
41
|
+
* that), but the emitted `files` map is narrowed to the in-scope paths.
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildExportArtifact(topology: Topology, config: ArchmapConfig, riskScores: Map<string, RiskScore>, commit: string | null, scope?: string[], now?: Date): ExportArtifact;
|
|
44
|
+
export declare function exportCommand(opts: {
|
|
45
|
+
config?: string;
|
|
46
|
+
scope?: string;
|
|
47
|
+
}): Promise<void>;
|
|
48
|
+
//# sourceMappingURL=export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../../src/commands/export.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5C,iFAAiF;AACjF,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,KAAK,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,CAAC,CAAC;IACX,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACnD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACrC;AAOD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,EAClC,MAAM,EAAE,MAAM,GAAG,IAAI,EACrB,KAAK,CAAC,EAAE,MAAM,EAAE,EAChB,GAAG,GAAE,IAAiB,GACrB,cAAc,CAmChB;AAsBD,wBAAsB,aAAa,CAAC,IAAI,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAW5F"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { resolveProject } from "../config.js";
|
|
4
|
+
import { classifyFile } from "../classify.js";
|
|
5
|
+
import { getFreshTopology, getFreshChurn } from "../cache.js";
|
|
6
|
+
import { buildTopology } from "../graph.js";
|
|
7
|
+
import { buildChurnMap } from "../churn.js";
|
|
8
|
+
import { computeRiskScores } from "../risk.js";
|
|
9
|
+
/** Normalize an incoming path to the form dependency-cruiser uses as keys. */
|
|
10
|
+
function normalizePath(p) {
|
|
11
|
+
return p.trim().replace(/^\.\//, "");
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Pure transform: topology + config + risk → a fully-classified artifact.
|
|
15
|
+
* No git, no graph build, no I/O — so it is trivially unit-testable.
|
|
16
|
+
*
|
|
17
|
+
* If `scope` is given, the graph is still built whole-repo (the caller does
|
|
18
|
+
* that), but the emitted `files` map is narrowed to the in-scope paths.
|
|
19
|
+
*/
|
|
20
|
+
export function buildExportArtifact(topology, config, riskScores, commit, scope, now = new Date()) {
|
|
21
|
+
const keys = scope === undefined
|
|
22
|
+
? Object.keys(topology.files)
|
|
23
|
+
: scope.map(normalizePath).filter((p) => p in topology.files);
|
|
24
|
+
const files = {};
|
|
25
|
+
for (const file of keys) {
|
|
26
|
+
const c = classifyFile(file, topology, config, riskScores);
|
|
27
|
+
files[file] = {
|
|
28
|
+
class: c.class,
|
|
29
|
+
ca: c.ca,
|
|
30
|
+
tca: c.tca,
|
|
31
|
+
instability: c.instability,
|
|
32
|
+
risk: c.risk?.risk ?? null,
|
|
33
|
+
overridden: c.overridden,
|
|
34
|
+
reason: c.reason,
|
|
35
|
+
dependents: topology.files[file].dependents,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const artifact = {
|
|
39
|
+
version: 1,
|
|
40
|
+
commit,
|
|
41
|
+
generatedAt: now.toISOString(),
|
|
42
|
+
files,
|
|
43
|
+
};
|
|
44
|
+
if (scope !== undefined) {
|
|
45
|
+
const requested = scope.map(normalizePath).filter((p) => p.length > 0);
|
|
46
|
+
artifact.scope = {
|
|
47
|
+
requested,
|
|
48
|
+
missing: requested.filter((p) => !(p in topology.files)),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return artifact;
|
|
52
|
+
}
|
|
53
|
+
function currentCommit() {
|
|
54
|
+
try {
|
|
55
|
+
return execSync("git rev-parse HEAD", {
|
|
56
|
+
encoding: "utf8",
|
|
57
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
58
|
+
}).trim();
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/** Read a newline-delimited path list from a file, or from stdin when "-". */
|
|
65
|
+
function readScope(source) {
|
|
66
|
+
const raw = readFileSync(source === "-" ? 0 : source, "utf8");
|
|
67
|
+
return raw
|
|
68
|
+
.split("\n")
|
|
69
|
+
.map((l) => l.trim())
|
|
70
|
+
.filter((l) => l.length > 0);
|
|
71
|
+
}
|
|
72
|
+
export async function exportCommand(opts) {
|
|
73
|
+
const { config, entry } = resolveProject(opts.config);
|
|
74
|
+
const { topology } = await getFreshTopology(entry, buildTopology);
|
|
75
|
+
const { churn: churnMap } = getFreshChurn(90, buildChurnMap);
|
|
76
|
+
const riskScores = computeRiskScores(topology, churnMap);
|
|
77
|
+
const scope = opts.scope !== undefined ? readScope(opts.scope) : undefined;
|
|
78
|
+
const artifact = buildExportArtifact(topology, config, riskScores, currentCommit(), scope);
|
|
79
|
+
// export is inherently machine-readable: always emit JSON on stdout.
|
|
80
|
+
console.log(JSON.stringify(artifact, null, 2));
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../../src/commands/export.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"risk.d.ts","sourceRoot":"","sources":["../../../src/commands/risk.ts"],"names":[],"mappings":"AAMA,wBAAsB,WAAW,CAC/B,IAAI,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACtD,OAAO,CAAC,IAAI,CAAC,CAgCf"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { resolveProject } from "../config.js";
|
|
2
|
+
import { getFreshTopology, getFreshChurn } from "../cache.js";
|
|
3
|
+
import { buildTopology } from "../graph.js";
|
|
4
|
+
import { buildChurnMap } from "../churn.js";
|
|
5
|
+
import { computeRiskScores } from "../risk.js";
|
|
6
|
+
export async function riskCommand(opts) {
|
|
7
|
+
const { entry } = resolveProject(opts.config);
|
|
8
|
+
const { topology } = await getFreshTopology(entry, buildTopology);
|
|
9
|
+
const { churn: churnMap } = getFreshChurn(90, buildChurnMap);
|
|
10
|
+
const riskScores = computeRiskScores(topology, churnMap);
|
|
11
|
+
const n = opts.top ? parseInt(opts.top, 10) : 10;
|
|
12
|
+
const sorted = [...riskScores.entries()]
|
|
13
|
+
.sort((a, b) => b[1].risk - a[1].risk)
|
|
14
|
+
.slice(0, n);
|
|
15
|
+
if (opts.json) {
|
|
16
|
+
console.log(JSON.stringify(sorted.map(([file, score]) => ({ file, ...score })), null, 2));
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
console.log(`Top ${sorted.length} riskiest files:\n`);
|
|
20
|
+
for (const [file, score] of sorted) {
|
|
21
|
+
const node = topology.files[file];
|
|
22
|
+
console.log(` ${score.risk.toString().padStart(3)}/100 ${file}`);
|
|
23
|
+
console.log(` ca=${node?.ca ?? 0} tca=${score.tca} churn=${score.commits}c/90d structural=${score.structural}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=risk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"risk.js","sourceRoot":"","sources":["../../../src/commands/risk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAuD;IAEvD,MAAM,EAAE,KAAK,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,gBAAgB,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;IAElE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;IAC7D,MAAM,UAAU,GAAG,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAEzD,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjD,MAAM,MAAM,GAAG,CAAC,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC;SACrC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;SACrC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEf,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,EACnD,IAAI,EACJ,CAAC,CACF,CACF,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,OAAO,MAAM,CAAC,MAAM,oBAAoB,CAAC,CAAC;QACtD,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAClC,OAAO,CAAC,GAAG,CACT,KAAK,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CACtD,CAAC;YACF,OAAO,CAAC,GAAG,CACT,eAAe,IAAI,EAAE,EAAE,IAAI,CAAC,SAAS,KAAK,CAAC,GAAG,WAAW,KAAK,CAAC,OAAO,qBAAqB,KAAK,CAAC,UAAU,EAAE,CAC9G,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.d.ts","sourceRoot":"","sources":["../../../src/commands/scan.ts"],"names":[],"mappings":"AAOA,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GACvB,OAAO,CAAC,IAAI,CAAC,CAef"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { writeFileSync, mkdirSync } from "fs";
|
|
2
|
+
import { buildTopology } from "../graph.js";
|
|
3
|
+
import { writeCache } from "../cache.js";
|
|
4
|
+
import { createHash } from "crypto";
|
|
5
|
+
import { readFileSync } from "fs";
|
|
6
|
+
import { listTypeScriptFiles } from "../files.js";
|
|
7
|
+
export async function scanCommand(entry, opts) {
|
|
8
|
+
const topology = await buildTopology(entry);
|
|
9
|
+
mkdirSync(".archmap", { recursive: true });
|
|
10
|
+
writeFileSync(".archmap/topology.json", JSON.stringify(topology, null, 2));
|
|
11
|
+
// update cache
|
|
12
|
+
const hash = computeHash(entry);
|
|
13
|
+
writeCache(hash, topology);
|
|
14
|
+
if (opts.json) {
|
|
15
|
+
console.log(JSON.stringify({ ok: true, files: Object.keys(topology.files).length }));
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
console.log(`Scanned ${Object.keys(topology.files).length} files → .archmap/topology.json`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function computeHash(entry) {
|
|
22
|
+
const files = listTypeScriptFiles(entry);
|
|
23
|
+
const hasher = createHash("sha256");
|
|
24
|
+
for (const file of files) {
|
|
25
|
+
hasher.update(file + "\n");
|
|
26
|
+
try {
|
|
27
|
+
const src = readFileSync(file, "utf8");
|
|
28
|
+
const imports = src.match(/^(import|export).*from\s+['"].*['"]/gm) ?? [];
|
|
29
|
+
hasher.update(imports.join("\n") + "\n");
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// skip unreadable files
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return hasher.digest("hex");
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=scan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan.js","sourceRoot":"","sources":["../../../src/commands/scan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAa,EACb,IAAwB;IAExB,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;IAE5C,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,aAAa,CAAC,wBAAwB,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAE3E,eAAe;IACf,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAChC,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAE3B,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACvF,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,MAAM,iCAAiC,CAAC,CAAC;IAC9F,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,MAAM,KAAK,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACpC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;QAC3B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACvC,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,uCAAuC,CAAC,IAAI,EAAE,CAAC;YACzE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,wBAAwB;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Klass } from "./classify.js";
|
|
2
|
+
export interface Override {
|
|
3
|
+
path: string;
|
|
4
|
+
classification: Klass;
|
|
5
|
+
reason: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ArchmapConfig {
|
|
8
|
+
version: number;
|
|
9
|
+
thresholds: {
|
|
10
|
+
leaf: number;
|
|
11
|
+
junction: number;
|
|
12
|
+
};
|
|
13
|
+
overrides: Override[];
|
|
14
|
+
analyzers: Array<{
|
|
15
|
+
lang: string;
|
|
16
|
+
entry: string;
|
|
17
|
+
}>;
|
|
18
|
+
}
|
|
19
|
+
export declare function loadConfig(configPath?: string): ArchmapConfig;
|
|
20
|
+
/**
|
|
21
|
+
* Walk up from `startDir` to the filesystem root looking for `.archmap.yaml`.
|
|
22
|
+
* Returns the absolute path to the nearest config, or null if none is found.
|
|
23
|
+
*/
|
|
24
|
+
export declare function findConfigPath(startDir?: string): string | null;
|
|
25
|
+
export interface ResolvedProject {
|
|
26
|
+
config: ArchmapConfig;
|
|
27
|
+
entry: string;
|
|
28
|
+
root: string;
|
|
29
|
+
configPath: string | null;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the project context for a command.
|
|
33
|
+
*
|
|
34
|
+
* - With an explicit `--config` path: legacy behaviour — the config is loaded
|
|
35
|
+
* as given and `entry` paths resolve relative to the current working dir.
|
|
36
|
+
* - Without one: walk up from cwd to discover the nearest `.archmap.yaml`,
|
|
37
|
+
* chdir into its directory (the project root), and resolve `entry` paths
|
|
38
|
+
* relative to that directory. This lets `archmap` be run from any
|
|
39
|
+
* subdirectory of a project and still analyse the whole tree.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveProject(explicitConfig?: string): ResolvedProject;
|
|
42
|
+
export declare function matchOverride(file: string, overrides: Override[]): Override | undefined;
|
|
43
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3C,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,KAAK,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,SAAS,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD;AASD,wBAAgB,UAAU,CAAC,UAAU,SAAkB,GAAG,aAAa,CA2BtE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAS9E;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,aAAa,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,eAAe,CAkBvE;AAED,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,QAAQ,EAAE,GACpB,QAAQ,GAAG,SAAS,CAOtB"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { dirname, join, resolve } from "path";
|
|
3
|
+
import { parse } from "yaml";
|
|
4
|
+
import micromatch from "micromatch";
|
|
5
|
+
const DEFAULTS = {
|
|
6
|
+
version: 1,
|
|
7
|
+
thresholds: { leaf: 2, junction: 10 },
|
|
8
|
+
overrides: [],
|
|
9
|
+
analyzers: [{ lang: "typescript", entry: "src/" }],
|
|
10
|
+
};
|
|
11
|
+
export function loadConfig(configPath = ".archmap.yaml") {
|
|
12
|
+
let raw = {};
|
|
13
|
+
try {
|
|
14
|
+
raw = parse(readFileSync(configPath, "utf8")) ?? {};
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// no config file — use defaults
|
|
18
|
+
}
|
|
19
|
+
const config = {
|
|
20
|
+
version: raw.version ?? DEFAULTS.version,
|
|
21
|
+
thresholds: {
|
|
22
|
+
leaf: raw.thresholds?.leaf ?? DEFAULTS.thresholds.leaf,
|
|
23
|
+
junction: raw.thresholds?.junction ?? DEFAULTS.thresholds.junction,
|
|
24
|
+
},
|
|
25
|
+
overrides: raw.overrides ?? [],
|
|
26
|
+
analyzers: raw.analyzers ?? DEFAULTS.analyzers,
|
|
27
|
+
};
|
|
28
|
+
for (const ov of config.overrides) {
|
|
29
|
+
if (!["leaf", "branch", "hub"].includes(ov.classification)) {
|
|
30
|
+
throw new Error(`Invalid classification "${ov.classification}" in overrides for path "${ov.path}"`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return config;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Walk up from `startDir` to the filesystem root looking for `.archmap.yaml`.
|
|
37
|
+
* Returns the absolute path to the nearest config, or null if none is found.
|
|
38
|
+
*/
|
|
39
|
+
export function findConfigPath(startDir = process.cwd()) {
|
|
40
|
+
let dir = resolve(startDir);
|
|
41
|
+
while (true) {
|
|
42
|
+
const candidate = join(dir, ".archmap.yaml");
|
|
43
|
+
if (existsSync(candidate))
|
|
44
|
+
return candidate;
|
|
45
|
+
const parent = dirname(dir);
|
|
46
|
+
if (parent === dir)
|
|
47
|
+
return null; // reached filesystem root
|
|
48
|
+
dir = parent;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Resolve the project context for a command.
|
|
53
|
+
*
|
|
54
|
+
* - With an explicit `--config` path: legacy behaviour — the config is loaded
|
|
55
|
+
* as given and `entry` paths resolve relative to the current working dir.
|
|
56
|
+
* - Without one: walk up from cwd to discover the nearest `.archmap.yaml`,
|
|
57
|
+
* chdir into its directory (the project root), and resolve `entry` paths
|
|
58
|
+
* relative to that directory. This lets `archmap` be run from any
|
|
59
|
+
* subdirectory of a project and still analyse the whole tree.
|
|
60
|
+
*/
|
|
61
|
+
export function resolveProject(explicitConfig) {
|
|
62
|
+
let configPath = null;
|
|
63
|
+
let root = process.cwd();
|
|
64
|
+
if (explicitConfig) {
|
|
65
|
+
configPath = explicitConfig;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
configPath = findConfigPath();
|
|
69
|
+
if (configPath) {
|
|
70
|
+
root = dirname(resolve(configPath));
|
|
71
|
+
if (root !== process.cwd())
|
|
72
|
+
process.chdir(root);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const config = loadConfig(configPath ?? undefined);
|
|
76
|
+
const entry = config.analyzers.find((a) => a.lang === "typescript")?.entry ?? "src/";
|
|
77
|
+
return { config, entry, root, configPath };
|
|
78
|
+
}
|
|
79
|
+
export function matchOverride(file, overrides) {
|
|
80
|
+
for (const ov of overrides) {
|
|
81
|
+
if (micromatch.isMatch(file, ov.path, { dot: true })) {
|
|
82
|
+
return ov;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/files.ts"],"names":[],"mappings":"AAOA,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA2B3D"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readdirSync, statSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
const TS_FILE = /\.(ts|tsx)$/;
|
|
4
|
+
const TEST_FILE = /\.(test|spec)\.(ts|tsx)$/;
|
|
5
|
+
const SKIP_DIRS = new Set(["node_modules", "dist", ".archmap"]);
|
|
6
|
+
export function listTypeScriptFiles(entry) {
|
|
7
|
+
const files = [];
|
|
8
|
+
function walk(path) {
|
|
9
|
+
let stat;
|
|
10
|
+
try {
|
|
11
|
+
stat = statSync(path);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (stat.isDirectory()) {
|
|
17
|
+
for (const child of readdirSync(path)) {
|
|
18
|
+
if (!SKIP_DIRS.has(child)) {
|
|
19
|
+
walk(join(path, child));
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (stat.isFile() && TS_FILE.test(path) && !TEST_FILE.test(path)) {
|
|
25
|
+
files.push(path);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
walk(entry);
|
|
29
|
+
return files.sort();
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=files.js.map
|