@untangle-code/complexity 0.0.0-stage → 0.1.1
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/LICENSE +661 -0
- package/NOTICE +90 -0
- package/README.md +235 -2
- package/THIRD_PARTY_LICENSES.md +24 -0
- package/dist/aggregator.d.ts +28 -0
- package/dist/aggregator.d.ts.map +1 -0
- package/dist/aggregator.js +77 -0
- package/dist/aggregator.js.map +1 -0
- package/dist/assemble.d.ts +50 -0
- package/dist/assemble.d.ts.map +1 -0
- package/dist/assemble.js +43 -0
- package/dist/assemble.js.map +1 -0
- package/dist/cognitive.d.ts +62 -0
- package/dist/cognitive.d.ts.map +1 -0
- package/dist/cognitive.js +62 -0
- package/dist/cognitive.js.map +1 -0
- package/dist/cyclomatic.d.ts +29 -0
- package/dist/cyclomatic.d.ts.map +1 -0
- package/dist/cyclomatic.js +52 -0
- package/dist/cyclomatic.js.map +1 -0
- package/dist/entropy.d.ts +5 -0
- package/dist/entropy.d.ts.map +1 -0
- package/dist/entropy.js +29 -0
- package/dist/entropy.js.map +1 -0
- package/dist/explain.d.ts +22 -0
- package/dist/explain.d.ts.map +1 -0
- package/dist/explain.js +35 -0
- package/dist/explain.js.map +1 -0
- package/dist/halstead.d.ts +23 -0
- package/dist/halstead.d.ts.map +1 -0
- package/dist/halstead.js +24 -0
- package/dist/halstead.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/ir/build-ir.d.ts +105 -0
- package/dist/ir/build-ir.d.ts.map +1 -0
- package/dist/ir/build-ir.js +267 -0
- package/dist/ir/build-ir.js.map +1 -0
- package/dist/maintainability.d.ts +15 -0
- package/dist/maintainability.d.ts.map +1 -0
- package/dist/maintainability.js +10 -0
- package/dist/maintainability.js.map +1 -0
- package/dist/node-kind-heuristics.d.ts +24 -0
- package/dist/node-kind-heuristics.d.ts.map +1 -0
- package/dist/node-kind-heuristics.js +37 -0
- package/dist/node-kind-heuristics.js.map +1 -0
- package/dist/prose/metrics.d.ts +81 -0
- package/dist/prose/metrics.d.ts.map +1 -0
- package/dist/prose/metrics.js +103 -0
- package/dist/prose/metrics.js.map +1 -0
- package/dist/scope-aggregate.d.ts +229 -0
- package/dist/scope-aggregate.d.ts.map +1 -0
- package/dist/scope-aggregate.js +588 -0
- package/dist/scope-aggregate.js.map +1 -0
- package/dist/scope-identity.d.ts +85 -0
- package/dist/scope-identity.d.ts.map +1 -0
- package/dist/scope-identity.js +144 -0
- package/dist/scope-identity.js.map +1 -0
- package/dist/scope.d.ts +36 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +60 -0
- package/dist/scope.js.map +1 -0
- package/dist/structural.d.ts +30 -0
- package/dist/structural.d.ts.map +1 -0
- package/dist/structural.js +98 -0
- package/dist/structural.js.map +1 -0
- package/dist/syntactic.d.ts +35 -0
- package/dist/syntactic.d.ts.map +1 -0
- package/dist/syntactic.js +143 -0
- package/dist/syntactic.js.map +1 -0
- package/dist/text-lines.d.ts +34 -0
- package/dist/text-lines.d.ts.map +1 -0
- package/dist/text-lines.js +60 -0
- package/dist/text-lines.js.map +1 -0
- package/dist/textual.d.ts +28 -0
- package/dist/textual.d.ts.map +1 -0
- package/dist/textual.js +75 -0
- package/dist/textual.js.map +1 -0
- package/dist/types.d.ts +112 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +60 -3
- package/src/aggregator.ts +105 -0
- package/src/assemble.ts +109 -0
- package/src/cognitive.ts +127 -0
- package/src/cyclomatic.ts +78 -0
- package/src/entropy.ts +30 -0
- package/src/explain.ts +64 -0
- package/src/halstead.ts +54 -0
- package/src/index.ts +85 -0
- package/src/ir/build-ir.ts +411 -0
- package/src/maintainability.ts +29 -0
- package/src/node-kind-heuristics.ts +55 -0
- package/src/prose/metrics.ts +201 -0
- package/src/scope-aggregate.ts +775 -0
- package/src/scope-identity.ts +240 -0
- package/src/scope.ts +77 -0
- package/src/structural.ts +132 -0
- package/src/syntactic.ts +183 -0
- package/src/text-lines.ts +90 -0
- package/src/textual.ts +119 -0
- package/src/types.ts +121 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Eindeutige Scope-Identitaet fuer die Aggregation (WP-82).
|
|
3
|
+
*
|
|
4
|
+
* ## Warum es dieses Modul gibt
|
|
5
|
+
*
|
|
6
|
+
* spec.md §16.10 verlangt die Deduplizierung ueber `metrics_scope_id`. Das
|
|
7
|
+
* setzt voraus, dass diese Id GENAU EINEN Scope bezeichnet. Der Bezeichner,
|
|
8
|
+
* den die Sprachadapter heute liefern — `SymbolScopeFact.symbolScopeId` — tut
|
|
9
|
+
* das innerhalb einer Datei NICHT: er wird aus dem qualifizierten Namen plus
|
|
10
|
+
* einem Ordinal gebildet, das je Elternscope zaehlt. Zwei anonyme Callbacks
|
|
11
|
+
* unter zwei verschiedenen, ebenfalls anonymen Eltern bekommen damit beide den
|
|
12
|
+
* qualifizierten Namen `<anonymous>.<anonymous>` und beide das Ordinal 0 —
|
|
13
|
+
* dieselbe Id fuer zwei verschiedene Scopes. Am eigenen Repo betrifft das rund
|
|
14
|
+
* ein Fuenftel aller Scopes.
|
|
15
|
+
*
|
|
16
|
+
* Die Folge waere nicht nur eine schiefe Summe, sondern stiller Inhaltsverlust
|
|
17
|
+
* (E4): `aggregate()` verwirft den zweiten Scope als Dublette,
|
|
18
|
+
* `selectSymbolFacts()` liefert die VEREINIGUNG der Facts beider Scopes, und
|
|
19
|
+
* `buildSymbolIR()` baut die IR fuer denjenigen, der die interne Map zuletzt
|
|
20
|
+
* gewonnen hat.
|
|
21
|
+
*
|
|
22
|
+
* ## Was dieses Modul tut
|
|
23
|
+
*
|
|
24
|
+
* `withUniqueScopeIds()` schreibt einen Facts-Satz auf nachweislich eindeutige
|
|
25
|
+
* Scope-Ids um: Byte-Range statt Name plus Ordinal. Jede Fact-Zuordnung wird
|
|
26
|
+
* ueber Byte-Containment neu aufgeloest (der ENGSTE Scope, der die Id des
|
|
27
|
+
* Facts traegt und dessen Range den Fact enthaelt). Die aufgetretenen
|
|
28
|
+
* Kollisionen werden GEZAEHLT UND ZURUECKGEGEBEN, nicht stillschweigend
|
|
29
|
+
* geheilt — wer den Bericht liest, sieht, dass die Adapter-Id das Problem hat,
|
|
30
|
+
* und nicht nur, dass jemand daran vorbeigearbeitet hat.
|
|
31
|
+
*
|
|
32
|
+
* Das ist eine Reparatur an der Datenkante, KEIN Ersatz fuer die Behebung im
|
|
33
|
+
* Adapter. Sobald `symbolScopeId` je Datei eindeutig ist, bleibt `collisions`
|
|
34
|
+
* leer und dieses Modul kann entfallen.
|
|
35
|
+
*
|
|
36
|
+
* ## Grenze (bewusst, nicht verschwiegen)
|
|
37
|
+
*
|
|
38
|
+
* `NodeKindFact` traegt als einzige Fact-Art KEINE `SourceRange` (domain
|
|
39
|
+
* `analysis.ts`); ihre Zuordnung ist ueber Containment daher nicht
|
|
40
|
+
* reparierbar. Sie wird auf den ERSTEN Scope mit der jeweiligen Adapter-Id
|
|
41
|
+
* abgebildet und die Zahl der so behandelten Facts in
|
|
42
|
+
* `nodeKindsAttributedByFallback` ausgewiesen. Betroffen sind nur die
|
|
43
|
+
* syntaktischen Metriken (`computeSyntactic`), nicht Cyclomatic/Cognitive.
|
|
44
|
+
*/
|
|
45
|
+
import type { ComplexityFacts, RawCall } from "@untangle-code/domain";
|
|
46
|
+
/** Eine Adapter-Id, die mehr als einen Scope bezeichnet. */
|
|
47
|
+
export interface ScopeIdCollisionV1 {
|
|
48
|
+
/** Die mehrdeutige `symbolScopeId` aus dem Sprachadapter. */
|
|
49
|
+
adapterScopeId: string;
|
|
50
|
+
/** Wie viele verschiedene Scopes sie bezeichnet (>= 2). */
|
|
51
|
+
scopeCount: number;
|
|
52
|
+
/** Startzeilen der betroffenen Scopes, aufsteigend — zum Nachschlagen. */
|
|
53
|
+
startLines: number[];
|
|
54
|
+
}
|
|
55
|
+
export interface UniqueScopeIdsResultV1 {
|
|
56
|
+
/** Facts mit eindeutigen Scope-Ids; sonst strukturgleich zur Eingabe. */
|
|
57
|
+
facts: ComplexityFacts;
|
|
58
|
+
/** Aufrufe mit umgeschriebenem `callerScopeId`. */
|
|
59
|
+
calls: RawCall[];
|
|
60
|
+
/** Abbildung neue Id -> urspruengliche Adapter-Id, fuer Diagnosen. */
|
|
61
|
+
originalScopeIds: Map<string, string>;
|
|
62
|
+
/** Jede mehrdeutige Adapter-Id, nach Codepoints sortiert. Leer heisst: alles war eindeutig. */
|
|
63
|
+
collisions: ScopeIdCollisionV1[];
|
|
64
|
+
/** `NodeKindFact`s ohne Range, die per Fallback zugeordnet wurden (siehe Modul-Doku). */
|
|
65
|
+
nodeKindsAttributedByFallback: number;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Schreibt einen Facts-Satz auf eindeutige Scope-Ids um.
|
|
69
|
+
*
|
|
70
|
+
* @param facts Facts einer Datei, wie der Sprachadapter sie liefert.
|
|
71
|
+
* @param calls Rohaufrufe derselben Datei (`extractCalls`); ihr
|
|
72
|
+
* `callerScopeId` wird mit umgeschrieben, damit die Rekursionsmarkierung in
|
|
73
|
+
* `buildSymbolIR` weiter greift.
|
|
74
|
+
* @param idPrefix Praefix der neuen Ids, ueblicherweise der repo-relative
|
|
75
|
+
* Pfad. Er macht die Ids ueber Dateigrenzen hinweg eindeutig; innerhalb der
|
|
76
|
+
* Datei sorgt die Byte-Range dafuer.
|
|
77
|
+
*
|
|
78
|
+
* Kann ein Fact ueber Containment keinem Scope zugeordnet werden (er laege
|
|
79
|
+
* ausserhalb jeder Range seines eigenen Scopes — ein Adapterfehler anderer
|
|
80
|
+
* Art), bleibt seine Adapter-Id stehen. Die Aggregation sieht dann einen
|
|
81
|
+
* unbekannten Eltern-Scope und meldet ihn als Invariantenverstoss, statt eine
|
|
82
|
+
* saubere Zahl vorzutaeuschen.
|
|
83
|
+
*/
|
|
84
|
+
export declare function withUniqueScopeIds(facts: ComplexityFacts, calls: readonly RawCall[], idPrefix: string): UniqueScopeIdsResultV1;
|
|
85
|
+
//# sourceMappingURL=scope-identity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-identity.d.ts","sourceRoot":"","sources":["../src/scope-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,OAAO,KAAK,EACV,eAAe,EAEf,OAAO,EAGR,MAAM,uBAAuB,CAAC;AAK/B,4DAA4D;AAC5D,MAAM,WAAW,kBAAkB;IACjC,6DAA6D;IAC7D,cAAc,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,0EAA0E;IAC1E,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,sBAAsB;IACrC,yEAAyE;IACzE,KAAK,EAAE,eAAe,CAAC;IACvB,mDAAmD;IACnD,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,sEAAsE;IACtE,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,+FAA+F;IAC/F,UAAU,EAAE,kBAAkB,EAAE,CAAC;IACjC,yFAAyF;IACzF,6BAA6B,EAAE,MAAM,CAAC;CACvC;AAiCD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE,SAAS,OAAO,EAAE,EACzB,QAAQ,EAAE,MAAM,GACf,sBAAsB,CA6GxB"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { ID_COMPONENT_SEPARATOR, compareCodepoints } from "@untangle-code/domain";
|
|
2
|
+
import { containsRange } from "./scope.js";
|
|
3
|
+
/** Byte-Range-basierte, innerhalb einer Datei eindeutige Id. */
|
|
4
|
+
function uniqueIdFor(prefix, range) {
|
|
5
|
+
return [prefix, "scope", String(range.startByte), String(range.endByte)].join(ID_COMPONENT_SEPARATOR);
|
|
6
|
+
}
|
|
7
|
+
/** Innerster Scope aus `candidates`, dessen Range `range` enthaelt. */
|
|
8
|
+
function innermostOwner(candidates, range) {
|
|
9
|
+
let best;
|
|
10
|
+
for (const candidate of candidates) {
|
|
11
|
+
if (!containsRange(candidate.scope.range, range))
|
|
12
|
+
continue;
|
|
13
|
+
if (best === undefined ||
|
|
14
|
+
candidate.scope.range.endByte - candidate.scope.range.startByte <
|
|
15
|
+
best.scope.range.endByte - best.scope.range.startByte) {
|
|
16
|
+
best = candidate;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return best;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Schreibt einen Facts-Satz auf eindeutige Scope-Ids um.
|
|
23
|
+
*
|
|
24
|
+
* @param facts Facts einer Datei, wie der Sprachadapter sie liefert.
|
|
25
|
+
* @param calls Rohaufrufe derselben Datei (`extractCalls`); ihr
|
|
26
|
+
* `callerScopeId` wird mit umgeschrieben, damit die Rekursionsmarkierung in
|
|
27
|
+
* `buildSymbolIR` weiter greift.
|
|
28
|
+
* @param idPrefix Praefix der neuen Ids, ueblicherweise der repo-relative
|
|
29
|
+
* Pfad. Er macht die Ids ueber Dateigrenzen hinweg eindeutig; innerhalb der
|
|
30
|
+
* Datei sorgt die Byte-Range dafuer.
|
|
31
|
+
*
|
|
32
|
+
* Kann ein Fact ueber Containment keinem Scope zugeordnet werden (er laege
|
|
33
|
+
* ausserhalb jeder Range seines eigenen Scopes — ein Adapterfehler anderer
|
|
34
|
+
* Art), bleibt seine Adapter-Id stehen. Die Aggregation sieht dann einen
|
|
35
|
+
* unbekannten Eltern-Scope und meldet ihn als Invariantenverstoss, statt eine
|
|
36
|
+
* saubere Zahl vorzutaeuschen.
|
|
37
|
+
*/
|
|
38
|
+
export function withUniqueScopeIds(facts, calls, idPrefix) {
|
|
39
|
+
const rewritten = facts.symbolScopes.map((scope) => ({
|
|
40
|
+
scope,
|
|
41
|
+
uniqueId: uniqueIdFor(idPrefix, scope.range),
|
|
42
|
+
}));
|
|
43
|
+
const byAdapterId = new Map();
|
|
44
|
+
for (const entry of rewritten) {
|
|
45
|
+
const bucket = byAdapterId.get(entry.scope.symbolScopeId) ?? [];
|
|
46
|
+
bucket.push(entry);
|
|
47
|
+
byAdapterId.set(entry.scope.symbolScopeId, bucket);
|
|
48
|
+
}
|
|
49
|
+
const collisions = [];
|
|
50
|
+
for (const [adapterScopeId, bucket] of byAdapterId) {
|
|
51
|
+
if (bucket.length < 2)
|
|
52
|
+
continue;
|
|
53
|
+
collisions.push({
|
|
54
|
+
adapterScopeId,
|
|
55
|
+
scopeCount: bucket.length,
|
|
56
|
+
startLines: bucket.map((entry) => entry.scope.range.startLine).sort((a, b) => a - b),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
collisions.sort((a, b) => compareCodepoints(a.adapterScopeId, b.adapterScopeId));
|
|
60
|
+
const originalScopeIds = new Map();
|
|
61
|
+
for (const entry of rewritten)
|
|
62
|
+
originalScopeIds.set(entry.uniqueId, entry.scope.symbolScopeId);
|
|
63
|
+
/** Neue Id fuer einen Fact mit Range. */
|
|
64
|
+
const resolve = (adapterScopeId, range) => {
|
|
65
|
+
const bucket = byAdapterId.get(adapterScopeId);
|
|
66
|
+
if (bucket === undefined || bucket.length === 0)
|
|
67
|
+
return adapterScopeId;
|
|
68
|
+
const first = bucket[0];
|
|
69
|
+
if (bucket.length === 1)
|
|
70
|
+
return first.uniqueId;
|
|
71
|
+
const owner = innermostOwner(bucket, range);
|
|
72
|
+
return owner === undefined ? adapterScopeId : owner.uniqueId;
|
|
73
|
+
};
|
|
74
|
+
let nodeKindsAttributedByFallback = 0;
|
|
75
|
+
const mappedNodeKinds = facts.nodeKinds.map((fact) => {
|
|
76
|
+
const bucket = byAdapterId.get(fact.symbolScopeId);
|
|
77
|
+
if (bucket === undefined || bucket.length === 0)
|
|
78
|
+
return fact;
|
|
79
|
+
if (bucket.length > 1)
|
|
80
|
+
nodeKindsAttributedByFallback++;
|
|
81
|
+
return { ...fact, symbolScopeId: bucket[0].uniqueId };
|
|
82
|
+
});
|
|
83
|
+
const mappedScopes = rewritten.map((entry) => {
|
|
84
|
+
const parentAdapterId = entry.scope.parentScopeId;
|
|
85
|
+
if (parentAdapterId === undefined) {
|
|
86
|
+
return { ...entry.scope, symbolScopeId: entry.uniqueId };
|
|
87
|
+
}
|
|
88
|
+
const parentBucket = byAdapterId.get(parentAdapterId);
|
|
89
|
+
let parentId;
|
|
90
|
+
if (parentBucket === undefined || parentBucket.length === 0) {
|
|
91
|
+
parentId = parentAdapterId;
|
|
92
|
+
}
|
|
93
|
+
else if (parentBucket.length === 1) {
|
|
94
|
+
parentId = parentBucket[0].uniqueId;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const owner = innermostOwner(parentBucket.filter((candidate) => candidate !== entry), entry.scope.range);
|
|
98
|
+
parentId = owner === undefined ? parentAdapterId : owner.uniqueId;
|
|
99
|
+
}
|
|
100
|
+
return { ...entry.scope, symbolScopeId: entry.uniqueId, parentScopeId: parentId };
|
|
101
|
+
});
|
|
102
|
+
const nextFacts = {
|
|
103
|
+
symbolScopes: mappedScopes,
|
|
104
|
+
decisions: facts.decisions.map((fact) => ({
|
|
105
|
+
...fact,
|
|
106
|
+
symbolScopeId: resolve(fact.symbolScopeId, fact.range),
|
|
107
|
+
})),
|
|
108
|
+
nestingEvents: facts.nestingEvents.map((fact) => ({
|
|
109
|
+
...fact,
|
|
110
|
+
symbolScopeId: resolve(fact.symbolScopeId, fact.range),
|
|
111
|
+
})),
|
|
112
|
+
operators: facts.operators.map((fact) => ({
|
|
113
|
+
...fact,
|
|
114
|
+
symbolScopeId: resolve(fact.symbolScopeId, fact.range),
|
|
115
|
+
})),
|
|
116
|
+
operands: facts.operands.map((fact) => ({
|
|
117
|
+
...fact,
|
|
118
|
+
symbolScopeId: resolve(fact.symbolScopeId, fact.range),
|
|
119
|
+
})),
|
|
120
|
+
nodeKinds: mappedNodeKinds,
|
|
121
|
+
comments: facts.comments,
|
|
122
|
+
identifiers: facts.identifiers.map((fact) => ({
|
|
123
|
+
...fact,
|
|
124
|
+
symbolScopeId: resolve(fact.symbolScopeId, fact.range),
|
|
125
|
+
})),
|
|
126
|
+
literals: facts.literals.map((fact) => ({
|
|
127
|
+
...fact,
|
|
128
|
+
symbolScopeId: resolve(fact.symbolScopeId, fact.range),
|
|
129
|
+
})),
|
|
130
|
+
parseDiagnostics: facts.parseDiagnostics,
|
|
131
|
+
};
|
|
132
|
+
const nextCalls = calls.map((call) => ({
|
|
133
|
+
...call,
|
|
134
|
+
callerScopeId: resolve(call.callerScopeId, call.range),
|
|
135
|
+
}));
|
|
136
|
+
return {
|
|
137
|
+
facts: nextFacts,
|
|
138
|
+
calls: nextCalls,
|
|
139
|
+
originalScopeIds,
|
|
140
|
+
collisions,
|
|
141
|
+
nodeKindsAttributedByFallback,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=scope-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-identity.js","sourceRoot":"","sources":["../src/scope-identity.ts"],"names":[],"mappings":"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"}
|
package/dist/scope.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Facts scoping and byte/line utilities shared by every calculator
|
|
3
|
+
* (architecture.md §15.1). Every calculator is scope-agnostic: it operates on
|
|
4
|
+
* whatever `ComplexityFacts` it is handed. `selectSymbolFacts` is how a
|
|
5
|
+
* caller narrows file-level facts down to one symbol's facts before calling
|
|
6
|
+
* a calculator for `scope: "symbol"`; passing the file's facts unfiltered is
|
|
7
|
+
* how a caller computes `scope: "file"`.
|
|
8
|
+
*/
|
|
9
|
+
import type { ComplexityFacts, SourceRange } from "@untangle-code/domain";
|
|
10
|
+
/** True iff `inner` lies fully inside `outer` (byte-interval containment, both inclusive/exclusive per domain convention). */
|
|
11
|
+
export declare function containsRange(outer: SourceRange, inner: SourceRange): boolean;
|
|
12
|
+
/** True iff `outer` strictly contains `inner` (contains, but is not the identical range). */
|
|
13
|
+
export declare function strictlyContainsRange(outer: SourceRange, inner: SourceRange): boolean;
|
|
14
|
+
/** Stable identity key for deduplicating facts that reference the same source span. */
|
|
15
|
+
export declare function rangeKey(range: SourceRange): string;
|
|
16
|
+
/**
|
|
17
|
+
* Narrows file-level `ComplexityFacts` to the facts belonging to one symbol
|
|
18
|
+
* scope. Fact kinds that carry `symbolScopeId` (decisions, nestingEvents,
|
|
19
|
+
* operators, operands, nodeKinds, identifiers, literals) are filtered by
|
|
20
|
+
* exact id match — a symbol's own facts are attributed directly to it, never
|
|
21
|
+
* inherited from nested named symbols, which get their own `SymbolScopeFact`
|
|
22
|
+
* and their own facts (architecture.md §15.1; Untangle decision, see
|
|
23
|
+
* README "Entscheidungen" #1). `comments` and `parseDiagnostics` carry no
|
|
24
|
+
* `symbolScopeId` and are instead filtered by byte-range containment inside
|
|
25
|
+
* the symbol's own range.
|
|
26
|
+
*
|
|
27
|
+
* Returns empty fact arrays (rather than throwing) when `symbolScopeId` is
|
|
28
|
+
* not present in `facts.symbolScopes` — a caller passing a stale id gets an
|
|
29
|
+
* empty-but-valid `ComplexityFacts`, not a crash.
|
|
30
|
+
*/
|
|
31
|
+
export declare function selectSymbolFacts(facts: ComplexityFacts, symbolScopeId: string): ComplexityFacts;
|
|
32
|
+
/** Slices `sourceText` by a byte-offset `SourceRange` (UTF-8, per domain convention). */
|
|
33
|
+
export declare function sliceByRange(sourceText: string, range: SourceRange): string;
|
|
34
|
+
/** Splits text into physical lines, normalizing CRLF/CR to LF first. */
|
|
35
|
+
export declare function splitLines(text: string): string[];
|
|
36
|
+
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAS1E,8HAA8H;AAC9H,wBAAgB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAE7E;AAED,6FAA6F;AAC7F,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAGrF;AAED,uFAAuF;AACvF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAEnD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,GAAG,eAAe,CAiBhG;AAED,yFAAyF;AACzF,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,MAAM,CAG3E;AAED,wEAAwE;AACxE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAEjD"}
|
package/dist/scope.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
function byScope(items, symbolScopeId) {
|
|
2
|
+
return items.filter((item) => item.symbolScopeId === symbolScopeId);
|
|
3
|
+
}
|
|
4
|
+
/** True iff `inner` lies fully inside `outer` (byte-interval containment, both inclusive/exclusive per domain convention). */
|
|
5
|
+
export function containsRange(outer, inner) {
|
|
6
|
+
return outer.startByte <= inner.startByte && inner.endByte <= outer.endByte;
|
|
7
|
+
}
|
|
8
|
+
/** True iff `outer` strictly contains `inner` (contains, but is not the identical range). */
|
|
9
|
+
export function strictlyContainsRange(outer, inner) {
|
|
10
|
+
if (!containsRange(outer, inner))
|
|
11
|
+
return false;
|
|
12
|
+
return outer.startByte !== inner.startByte || outer.endByte !== inner.endByte;
|
|
13
|
+
}
|
|
14
|
+
/** Stable identity key for deduplicating facts that reference the same source span. */
|
|
15
|
+
export function rangeKey(range) {
|
|
16
|
+
return `${range.startByte}:${range.endByte}`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Narrows file-level `ComplexityFacts` to the facts belonging to one symbol
|
|
20
|
+
* scope. Fact kinds that carry `symbolScopeId` (decisions, nestingEvents,
|
|
21
|
+
* operators, operands, nodeKinds, identifiers, literals) are filtered by
|
|
22
|
+
* exact id match — a symbol's own facts are attributed directly to it, never
|
|
23
|
+
* inherited from nested named symbols, which get their own `SymbolScopeFact`
|
|
24
|
+
* and their own facts (architecture.md §15.1; Untangle decision, see
|
|
25
|
+
* README "Entscheidungen" #1). `comments` and `parseDiagnostics` carry no
|
|
26
|
+
* `symbolScopeId` and are instead filtered by byte-range containment inside
|
|
27
|
+
* the symbol's own range.
|
|
28
|
+
*
|
|
29
|
+
* Returns empty fact arrays (rather than throwing) when `symbolScopeId` is
|
|
30
|
+
* not present in `facts.symbolScopes` — a caller passing a stale id gets an
|
|
31
|
+
* empty-but-valid `ComplexityFacts`, not a crash.
|
|
32
|
+
*/
|
|
33
|
+
export function selectSymbolFacts(facts, symbolScopeId) {
|
|
34
|
+
const symbolScope = facts.symbolScopes.find((s) => s.symbolScopeId === symbolScopeId);
|
|
35
|
+
const range = symbolScope?.range;
|
|
36
|
+
return {
|
|
37
|
+
symbolScopes: facts.symbolScopes.filter((s) => s.symbolScopeId === symbolScopeId),
|
|
38
|
+
decisions: byScope(facts.decisions, symbolScopeId),
|
|
39
|
+
nestingEvents: byScope(facts.nestingEvents, symbolScopeId),
|
|
40
|
+
operators: byScope(facts.operators, symbolScopeId),
|
|
41
|
+
operands: byScope(facts.operands, symbolScopeId),
|
|
42
|
+
nodeKinds: byScope(facts.nodeKinds, symbolScopeId),
|
|
43
|
+
comments: range ? facts.comments.filter((r) => containsRange(range, r)) : [],
|
|
44
|
+
identifiers: byScope(facts.identifiers, symbolScopeId),
|
|
45
|
+
literals: byScope(facts.literals, symbolScopeId),
|
|
46
|
+
parseDiagnostics: range
|
|
47
|
+
? facts.parseDiagnostics.filter((d) => containsRange(range, d.range))
|
|
48
|
+
: [],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** Slices `sourceText` by a byte-offset `SourceRange` (UTF-8, per domain convention). */
|
|
52
|
+
export function sliceByRange(sourceText, range) {
|
|
53
|
+
const buf = Buffer.from(sourceText, "utf8");
|
|
54
|
+
return buf.subarray(range.startByte, range.endByte).toString("utf8");
|
|
55
|
+
}
|
|
56
|
+
/** Splits text into physical lines, normalizing CRLF/CR to LF first. */
|
|
57
|
+
export function splitLines(text) {
|
|
58
|
+
return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.js","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StructuralCalculator (spec.md §16.5).
|
|
3
|
+
*
|
|
4
|
+
* Facts-derivable metrics are computed directly from `ComplexityFacts` (plus
|
|
5
|
+
* `sourceText`/`scopeRange` for the line-based ones). Coupling and API-surface
|
|
6
|
+
* metrics (`fan_in`, `fan_out`, `import_in_degree`, `import_out_degree`,
|
|
7
|
+
* `afferent_coupling`, `efferent_coupling`, `instability`,
|
|
8
|
+
* `inheritance_depth`, `public_api_count`) need whole-repository graph/export
|
|
9
|
+
* knowledge this facts-only package does not have — per the work order they
|
|
10
|
+
* are accepted as `StructuralExternalInput` and are only ever present in the
|
|
11
|
+
* output when the caller actually supplies them; they are never defaulted or
|
|
12
|
+
* guessed (see README "Entscheidungen" #4 for `instability`'s one derived
|
|
13
|
+
* exception).
|
|
14
|
+
*
|
|
15
|
+
* `parameter_count`, `return_count` and `exit_point_count` use a best-effort
|
|
16
|
+
* node-kind classifier (see `node-kind-heuristics.ts`) since `SymbolScopeFact`
|
|
17
|
+
* carries no structured parameter list — see README "Entscheidungen" #5 and
|
|
18
|
+
* `openIssues`.
|
|
19
|
+
*/
|
|
20
|
+
import type { ComplexityFacts, SourceRange } from "@untangle-code/domain";
|
|
21
|
+
import type { MetricsResult, StructuralExternalInput } from "./types.js";
|
|
22
|
+
/**
|
|
23
|
+
* @param facts Facts already narrowed to the target scope (see `selectSymbolFacts`).
|
|
24
|
+
* @param sourceText Full original file content (UTF-8) — needed because
|
|
25
|
+
* `SourceRange` byte offsets are file-absolute.
|
|
26
|
+
* @param scopeRange The scope's own range (a `SymbolScopeFact.range` for
|
|
27
|
+
* `scope: "symbol"`, or the whole-file range for `scope: "file"`).
|
|
28
|
+
*/
|
|
29
|
+
export declare function computeStructural(facts: ComplexityFacts, sourceText: string, scopeRange: SourceRange, external?: StructuralExternalInput, algorithmVersion?: string): MetricsResult;
|
|
30
|
+
//# sourceMappingURL=structural.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structural.d.ts","sourceRoot":"","sources":["../src/structural.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAK1E,OAAO,KAAK,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AA0BzE;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,eAAe,EACtB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,WAAW,EACvB,QAAQ,GAAE,uBAA4B,EACtC,gBAAgB,SAA2B,GAC1C,aAAa,CAoEf"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { classifyNodeKind } from "./node-kind-heuristics.js";
|
|
2
|
+
import { strictlyContainsRange } from "./scope.js";
|
|
3
|
+
import { classifyLines } from "./text-lines.js";
|
|
4
|
+
const FUNCTION_KINDS = new Set(["function", "arrow_function", "function_expression"]);
|
|
5
|
+
const METHOD_KINDS = new Set(["method", "constructor"]);
|
|
6
|
+
const CLASS_KINDS = new Set(["class"]);
|
|
7
|
+
const INTERFACE_KINDS = new Set(["interface"]);
|
|
8
|
+
function countByScopeKind(facts, kinds) {
|
|
9
|
+
return facts.symbolScopes.filter((s) => kinds.has(s.kind.toLowerCase())).length;
|
|
10
|
+
}
|
|
11
|
+
/** Interval-nesting depth of `target` among `pool`: 1 + number of other ranges in `pool` that strictly contain it. */
|
|
12
|
+
function nestingDepthOf(target, pool) {
|
|
13
|
+
let depth = 1;
|
|
14
|
+
for (const other of pool) {
|
|
15
|
+
if (other === target)
|
|
16
|
+
continue;
|
|
17
|
+
if (strictlyContainsRange(other, target))
|
|
18
|
+
depth++;
|
|
19
|
+
}
|
|
20
|
+
return depth;
|
|
21
|
+
}
|
|
22
|
+
function mean(values) {
|
|
23
|
+
if (values.length === 0)
|
|
24
|
+
return 0;
|
|
25
|
+
return values.reduce((sum, v) => sum + v, 0) / values.length;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* @param facts Facts already narrowed to the target scope (see `selectSymbolFacts`).
|
|
29
|
+
* @param sourceText Full original file content (UTF-8) — needed because
|
|
30
|
+
* `SourceRange` byte offsets are file-absolute.
|
|
31
|
+
* @param scopeRange The scope's own range (a `SymbolScopeFact.range` for
|
|
32
|
+
* `scope: "symbol"`, or the whole-file range for `scope: "file"`).
|
|
33
|
+
*/
|
|
34
|
+
export function computeStructural(facts, sourceText, scopeRange, external = {}, algorithmVersion = "untangle-structural-v1") {
|
|
35
|
+
const { physicalLoc, commentLines, docLines, blankLines } = classifyLines(sourceText, scopeRange, facts.comments);
|
|
36
|
+
const sloc = Math.max(physicalLoc - blankLines.size - commentLines.size, 0);
|
|
37
|
+
const nonContinuationNesting = facts.nestingEvents
|
|
38
|
+
.filter((n) => !n.continuation)
|
|
39
|
+
.map((n) => n.range);
|
|
40
|
+
const nestingDepths = nonContinuationNesting.map((range) => nestingDepthOf(range, nonContinuationNesting));
|
|
41
|
+
let parameterCount = 0;
|
|
42
|
+
let returnCount = 0;
|
|
43
|
+
let exitPointCount = 0;
|
|
44
|
+
for (const node of facts.nodeKinds) {
|
|
45
|
+
const classified = classifyNodeKind(node.kind);
|
|
46
|
+
if (classified === "parameter")
|
|
47
|
+
parameterCount++;
|
|
48
|
+
if (classified === "return") {
|
|
49
|
+
returnCount++;
|
|
50
|
+
exitPointCount++;
|
|
51
|
+
}
|
|
52
|
+
if (classified === "exit")
|
|
53
|
+
exitPointCount++;
|
|
54
|
+
}
|
|
55
|
+
const metrics = {
|
|
56
|
+
physical_loc: physicalLoc,
|
|
57
|
+
sloc,
|
|
58
|
+
blank_lines: blankLines.size,
|
|
59
|
+
comment_lines: commentLines.size,
|
|
60
|
+
doc_lines: docLines.size,
|
|
61
|
+
function_count: countByScopeKind(facts, FUNCTION_KINDS),
|
|
62
|
+
method_count: countByScopeKind(facts, METHOD_KINDS),
|
|
63
|
+
class_count: countByScopeKind(facts, CLASS_KINDS),
|
|
64
|
+
interface_count: countByScopeKind(facts, INTERFACE_KINDS),
|
|
65
|
+
parameter_count: parameterCount,
|
|
66
|
+
return_count: returnCount,
|
|
67
|
+
exit_point_count: exitPointCount,
|
|
68
|
+
max_nesting: nestingDepths.length > 0 ? Math.max(...nestingDepths) : 0,
|
|
69
|
+
avg_nesting: mean(nestingDepths),
|
|
70
|
+
};
|
|
71
|
+
if (external.fanIn !== undefined)
|
|
72
|
+
metrics["fan_in"] = external.fanIn;
|
|
73
|
+
if (external.fanOut !== undefined)
|
|
74
|
+
metrics["fan_out"] = external.fanOut;
|
|
75
|
+
if (external.importInDegree !== undefined)
|
|
76
|
+
metrics["import_in_degree"] = external.importInDegree;
|
|
77
|
+
if (external.importOutDegree !== undefined)
|
|
78
|
+
metrics["import_out_degree"] = external.importOutDegree;
|
|
79
|
+
if (external.afferentCoupling !== undefined)
|
|
80
|
+
metrics["afferent_coupling"] = external.afferentCoupling;
|
|
81
|
+
if (external.efferentCoupling !== undefined)
|
|
82
|
+
metrics["efferent_coupling"] = external.efferentCoupling;
|
|
83
|
+
if (external.inheritanceDepth !== undefined)
|
|
84
|
+
metrics["inheritance_depth"] = external.inheritanceDepth;
|
|
85
|
+
if (external.publicApiCount !== undefined)
|
|
86
|
+
metrics["public_api_count"] = external.publicApiCount;
|
|
87
|
+
if (external.instability !== undefined) {
|
|
88
|
+
metrics["instability"] = external.instability;
|
|
89
|
+
}
|
|
90
|
+
else if (external.afferentCoupling !== undefined && external.efferentCoupling !== undefined) {
|
|
91
|
+
const denom = external.afferentCoupling + external.efferentCoupling;
|
|
92
|
+
if (denom > 0) {
|
|
93
|
+
metrics["instability"] = external.efferentCoupling / denom;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return { metrics, algorithmVersion };
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=structural.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structural.js","sourceRoot":"","sources":["../src/structural.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SyntacticCalculator (spec.md §16.8).
|
|
3
|
+
*
|
|
4
|
+
* `ast_node_count`/`ast_max_depth`/`ast_avg_depth`/`node_kind_entropy` and the
|
|
5
|
+
* construct counters are derived straight from `ComplexityFacts.nodeKinds`
|
|
6
|
+
* (each entry already carries its own AST depth) and the other typed fact
|
|
7
|
+
* arrays.
|
|
8
|
+
*
|
|
9
|
+
* `ast_branching_factor_avg`/`ast_branching_factor_max` (spec.md §16.8,
|
|
10
|
+
* "Mindestens") are reconstructed EXACTLY from the `nodeKinds` sequence:
|
|
11
|
+
* the adapter's single walk emits one `NodeKindFact` per visited node in
|
|
12
|
+
* DFS PRE-ORDER (that ordering is this calculator's input contract for the
|
|
13
|
+
* two branching keys; `selectSymbolFacts` filtering preserves it), and in a
|
|
14
|
+
* pre-order depth sequence every node's parent is the nearest preceding
|
|
15
|
+
* entry with a smaller depth — so per-node child counts, and from them the
|
|
16
|
+
* branching factors, follow without any parent pointers. This is the real
|
|
17
|
+
* AST branching factor, not an approximation (see README "Entscheidungen"
|
|
18
|
+
* #7); the optional `irRoot` is only used as a fallback when `nodeKinds` is
|
|
19
|
+
* empty (e.g. hand-built IR fixtures), because the sparse control-flow IR
|
|
20
|
+
* would otherwise understate branching.
|
|
21
|
+
*/
|
|
22
|
+
import type { ComplexityFacts, ComplexityIRNode } from "@untangle-code/domain";
|
|
23
|
+
import type { MetricsResult } from "./types.js";
|
|
24
|
+
/**
|
|
25
|
+
* @param facts Facts already narrowed to the target scope.
|
|
26
|
+
* @param sloc The scope's own SLOC, as computed by `computeStructural` —
|
|
27
|
+
* passed in rather than recomputed so `ast_nodes_per_sloc` is consistent
|
|
28
|
+
* with the structural snapshot instead of a second, possibly divergent,
|
|
29
|
+
* line-scan.
|
|
30
|
+
* @param irRoot Optional IR subtree for the same scope; only used as the
|
|
31
|
+
* branching-factor source when `facts.nodeKinds` is empty (the pre-order
|
|
32
|
+
* reconstruction over real node-kind facts is exact and takes precedence).
|
|
33
|
+
*/
|
|
34
|
+
export declare function computeSyntactic(facts: ComplexityFacts, sloc: number, irRoot?: ComplexityIRNode, algorithmVersion?: string): MetricsResult;
|
|
35
|
+
//# sourceMappingURL=syntactic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syntactic.d.ts","sourceRoot":"","sources":["../src/syntactic.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAgB,MAAM,uBAAuB,CAAC;AAK7F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AA6DhD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,gBAAgB,EACzB,gBAAgB,SAA0B,GACzC,aAAa,CAgFf"}
|