@ttsc/graph 0.27.0 → 0.28.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 +3 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +15 -3
- package/lib/index.js.map +1 -1
- package/lib/model/TtscGraphMemory.d.ts +26 -0
- package/lib/model/TtscGraphMemory.js +121 -2
- package/lib/model/TtscGraphMemory.js.map +1 -1
- package/lib/model/TtscGraphSession.d.ts +23 -0
- package/lib/model/TtscGraphSession.js +248 -96
- package/lib/model/TtscGraphSession.js.map +1 -1
- package/lib/model/TtscGraphShardStore.js +6 -1
- package/lib/model/TtscGraphShardStore.js.map +1 -1
- package/lib/model/TtscLintDaemon.d.ts +54 -0
- package/lib/model/TtscLintDaemon.js +166 -0
- package/lib/model/TtscLintDaemon.js.map +1 -0
- package/lib/model/loadGraph.d.ts +8 -1
- package/lib/model/loadGraph.js +144 -72
- package/lib/model/loadGraph.js.map +1 -1
- package/lib/model/publishedArtifacts.d.ts +142 -0
- package/lib/model/publishedArtifacts.js +403 -0
- package/lib/model/publishedArtifacts.js.map +1 -0
- package/lib/reduce.js +20 -7
- package/lib/reduce.js.map +1 -1
- package/lib/server/createServer.js +38 -2
- package/lib/server/createServer.js.map +1 -1
- package/lib/server/runDetails.d.ts +15 -0
- package/lib/server/runDetails.js +48 -0
- package/lib/server/runDetails.js.map +1 -1
- package/lib/server/runEntrypoints.js +7 -1
- package/lib/server/runEntrypoints.js.map +1 -1
- package/lib/server/runLookup.js +129 -11
- package/lib/server/runLookup.js.map +1 -1
- package/lib/structures/ITtscGraphApplication.d.ts +3 -1
- package/lib/structures/ITtscGraphDetails.d.ts +12 -0
- package/lib/structures/ITtscGraphDocTag.d.ts +44 -0
- package/lib/structures/ITtscGraphDocTag.js +3 -0
- package/lib/structures/ITtscGraphDocTag.js.map +1 -0
- package/lib/structures/ITtscGraphDump.d.ts +23 -4
- package/lib/structures/ITtscGraphLookup.d.ts +29 -0
- package/lib/structures/ITtscGraphNode.d.ts +25 -0
- package/lib/structures/TtscGraphArtifactNodeKind.d.ts +23 -0
- package/lib/structures/TtscGraphArtifactNodeKind.js +37 -0
- package/lib/structures/TtscGraphArtifactNodeKind.js.map +1 -0
- package/lib/structures/TtscGraphDumpEdgeKind.d.ts +1 -1
- package/lib/structures/TtscGraphDumpNodeKind.d.ts +10 -2
- package/lib/structures/TtscGraphEdgeKind.d.ts +8 -1
- package/lib/structures/TtscGraphNodeKind.d.ts +1 -1
- package/lib/structures/index.d.ts +2 -0
- package/lib/structures/index.js +2 -0
- package/lib/structures/index.js.map +1 -1
- package/lib/view.js +17 -1
- package/lib/view.js.map +1 -1
- package/lib/viewer/index.html +5 -13
- package/lib/viewer/viewer.js +190 -190
- package/package.json +5 -5
- package/src/index.ts +23 -6
- package/src/model/TtscGraphMemory.ts +118 -2
- package/src/model/TtscGraphSession.ts +112 -2
- package/src/model/TtscGraphShardStore.ts +6 -1
- package/src/model/TtscLintDaemon.ts +179 -0
- package/src/model/loadGraph.ts +29 -5
- package/src/model/publishedArtifacts.ts +548 -0
- package/src/reduce.ts +20 -7
- package/src/server/runDetails.ts +52 -1
- package/src/server/runEntrypoints.ts +7 -1
- package/src/server/runLookup.ts +139 -14
- package/src/structures/ITtscGraphApplication.ts +3 -1
- package/src/structures/ITtscGraphDetails.ts +13 -0
- package/src/structures/ITtscGraphDocTag.ts +45 -0
- package/src/structures/ITtscGraphDump.ts +25 -4
- package/src/structures/ITtscGraphLookup.ts +31 -0
- package/src/structures/ITtscGraphNode.ts +27 -0
- package/src/structures/TtscGraphArtifactNodeKind.ts +35 -0
- package/src/structures/TtscGraphDumpEdgeKind.ts +1 -0
- package/src/structures/TtscGraphDumpNodeKind.ts +16 -2
- package/src/structures/TtscGraphEdgeKind.ts +8 -0
- package/src/structures/TtscGraphNodeKind.ts +7 -1
- package/src/structures/index.ts +2 -0
- package/src/view.ts +17 -1
- package/src/viewer/index.html +5 -13
- package/src/viewer/legend.ts +107 -0
- package/src/viewer/main.ts +19 -21
package/src/server/runLookup.ts
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import { TtscGraphMemory } from "../model/TtscGraphMemory";
|
|
1
|
+
import { TtscGraphMemory, documentationTarget } from "../model/TtscGraphMemory";
|
|
2
2
|
import { ITtscGraphLookup } from "../structures/ITtscGraphLookup";
|
|
3
3
|
import { ITtscGraphNode } from "../structures/ITtscGraphNode";
|
|
4
|
+
import { isArtifactNodeKind } from "../structures/TtscGraphArtifactNodeKind";
|
|
4
5
|
import { exportFanIn } from "./exportSurface";
|
|
5
6
|
import { isExternalNode, isSupportPath } from "./pathPolicy";
|
|
6
7
|
import { IRunnerOutput, resultNext } from "./resultNext";
|
|
7
|
-
import { decoratorsOf, signatureOf } from "./runDetails";
|
|
8
|
+
import { decoratorsOf, docTagsOf, signatureOf } from "./runDetails";
|
|
8
9
|
|
|
9
10
|
// One file should not crowd out the rest of the ranking, so cap hits per file.
|
|
10
11
|
const PER_FILE = 3;
|
|
12
|
+
// The score a citation carrier reports. Citation hits are placed ahead of the
|
|
13
|
+
// ranked list rather than sorted into it, so this value decides no order: it
|
|
14
|
+
// exists because every hit owes a score, and it is high to say what kind of
|
|
15
|
+
// answer this is — an exact match on an address the caller and the author spell
|
|
16
|
+
// identically, not a better guess at a name.
|
|
17
|
+
const CITATION_SCORE = 1000;
|
|
11
18
|
const DEFAULT_LIMIT = 5;
|
|
12
19
|
const MAX_LIMIT = 6;
|
|
13
20
|
|
|
@@ -29,7 +36,12 @@ export function runLookup(
|
|
|
29
36
|
const wantsInternal = wantsInternalSymbol(queryLc, codeTerms);
|
|
30
37
|
const wantsSupport = wantsSupportSymbol(queryLc);
|
|
31
38
|
const includeExternal = props.includeExternal === true;
|
|
32
|
-
|
|
39
|
+
// The reverse citation question is answered before the name tokenizer gets a
|
|
40
|
+
// veto. `subwords` splits on ASCII alphanumerics, so a target written in
|
|
41
|
+
// another script, or one made only of punctuation, produces no term and used
|
|
42
|
+
// to return "no searchable terms" while the index held that exact address.
|
|
43
|
+
const cited = citationHits(graph, props.query, includeExternal);
|
|
44
|
+
if (terms.length === 0 && cited.length === 0)
|
|
33
45
|
return {
|
|
34
46
|
result: {
|
|
35
47
|
type: "lookup",
|
|
@@ -42,7 +54,12 @@ export function runLookup(
|
|
|
42
54
|
};
|
|
43
55
|
|
|
44
56
|
const scored: ITtscGraphLookup.IHit[] = [];
|
|
45
|
-
|
|
57
|
+
// With no term to match, nothing is a name match. The whole-query bonus below
|
|
58
|
+
// is awarded when every term landed, and no terms trivially satisfies that —
|
|
59
|
+
// so a query the tokenizer cannot read (an address in another script) would
|
|
60
|
+
// otherwise return the citation hit followed by arbitrary central symbols,
|
|
61
|
+
// presented as answers to a name it never matched.
|
|
62
|
+
for (const node of terms.length === 0 ? [] : graph.nodes) {
|
|
46
63
|
if (node.kind === "file") continue;
|
|
47
64
|
if (!includeExternal && isExternalNode(node)) continue;
|
|
48
65
|
const score = scoreNode(
|
|
@@ -69,18 +86,46 @@ export function runLookup(
|
|
|
69
86
|
scored.push(hit);
|
|
70
87
|
}
|
|
71
88
|
|
|
72
|
-
|
|
89
|
+
// A query naming a documentation target is the reverse citation question, and
|
|
90
|
+
// it is the one an index has to answer: the declarations implementing one
|
|
91
|
+
// specification are scattered across every file that implements it, so
|
|
92
|
+
// anything less than an index means searching the repository.
|
|
93
|
+
//
|
|
94
|
+
// These are prepended rather than scored in. A tag target is an exact token
|
|
95
|
+
// the caller typed — `docs/pricing.md#sale`, `POST:/orders` — so a carrier is
|
|
96
|
+
// not a better fuzzy match than the name hits, it is a different and certain
|
|
97
|
+
// kind of answer, and name scoring cannot rank the two against each other.
|
|
98
|
+
const citedIds = new Set(cited.map((hit) => hit.id));
|
|
99
|
+
const ranked = scored.filter((hit) => !citedIds.has(hit.id));
|
|
100
|
+
ranked.sort((a, b) => b.score - a.score);
|
|
101
|
+
ranked.unshift(...cited);
|
|
73
102
|
|
|
74
103
|
// Diversity: keep at most PER_FILE hits per file while filling up to the limit.
|
|
104
|
+
//
|
|
105
|
+
// A citation carrier is exempt. The cap exists so one file's roster cannot
|
|
106
|
+
// crowd out a name ranking, and a citation is not ranked against anything: it
|
|
107
|
+
// is an exact match on an address, and a module implementing one specification
|
|
108
|
+
// across four functions is the ordinary shape of the answer rather than a file
|
|
109
|
+
// dominating a shortlist. Capping those returned three of five carriers and
|
|
110
|
+
// called the result complete.
|
|
75
111
|
const limit = bound(props.limit, DEFAULT_LIMIT, 1, MAX_LIMIT);
|
|
76
112
|
const perFile = new Map<string, number>();
|
|
77
113
|
const hits: ITtscGraphLookup.IHit[] = [];
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (
|
|
81
|
-
|
|
114
|
+
let truncated = false;
|
|
115
|
+
for (const hit of ranked) {
|
|
116
|
+
if (hits.length >= limit) {
|
|
117
|
+
truncated = true;
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
if (!citedIds.has(hit.id)) {
|
|
121
|
+
const used = perFile.get(hit.file) ?? 0;
|
|
122
|
+
if (used >= PER_FILE) {
|
|
123
|
+
truncated = true;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
perFile.set(hit.file, used + 1);
|
|
127
|
+
}
|
|
82
128
|
hits.push(hit);
|
|
83
|
-
if (hits.length >= limit) break;
|
|
84
129
|
}
|
|
85
130
|
|
|
86
131
|
// Attach each kept hit's signature only for the shortlist, so the model can
|
|
@@ -95,6 +140,7 @@ export function runLookup(
|
|
|
95
140
|
result: {
|
|
96
141
|
type: "lookup",
|
|
97
142
|
hits,
|
|
143
|
+
...(truncated ? { truncated: true } : {}),
|
|
98
144
|
},
|
|
99
145
|
next:
|
|
100
146
|
hits.length === 0
|
|
@@ -102,10 +148,16 @@ export function runLookup(
|
|
|
102
148
|
"outside",
|
|
103
149
|
"No symbol matched, so the graph did not resolve this name.",
|
|
104
150
|
)
|
|
105
|
-
:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
151
|
+
: truncated
|
|
152
|
+
? resultNext(
|
|
153
|
+
"answer",
|
|
154
|
+
"The hits resolve the name, and `truncated` says more matched " +
|
|
155
|
+
"than the limit returned: raise `limit` for the rest.",
|
|
156
|
+
)
|
|
157
|
+
: resultNext(
|
|
158
|
+
"answer",
|
|
159
|
+
"The ranked hits and their signatures resolve the name.",
|
|
160
|
+
),
|
|
109
161
|
};
|
|
110
162
|
}
|
|
111
163
|
|
|
@@ -293,3 +345,76 @@ function subwords(text: string): string[] {
|
|
|
293
345
|
.filter((w) => w.length > 0)
|
|
294
346
|
.map((w) => w.toLowerCase());
|
|
295
347
|
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* The declarations whose documentation opens a tag with the address this query
|
|
351
|
+
* opens with, in file order.
|
|
352
|
+
*
|
|
353
|
+
* A caller writing `docs/pricing.md#sale` or `POST:/orders` is asking the
|
|
354
|
+
* reverse citation question: not what a symbol is, but which code answers to
|
|
355
|
+
* this specification. Nothing else in the graph can answer it, because the
|
|
356
|
+
* other end of the relation is a document section rather than a declaration.
|
|
357
|
+
*
|
|
358
|
+
* The match is exact, and only a token shaped like an address is one at all —
|
|
359
|
+
* `documentationTarget` decides that for the query and for every indexed tag,
|
|
360
|
+
* so the two sides can never disagree about what counts. Fuzzy matching is what
|
|
361
|
+
* the name scoring above does, and it is wrong here: an address is spelled
|
|
362
|
+
* identically by the author and the caller, and a near-miss would put a
|
|
363
|
+
* different specification's implementers in the answer under the same confident
|
|
364
|
+
* heading.
|
|
365
|
+
*
|
|
366
|
+
* Each returned hit carries the tags that matched, so the result says why the
|
|
367
|
+
* declaration is in it.
|
|
368
|
+
*/
|
|
369
|
+
function citationHits(
|
|
370
|
+
graph: TtscGraphMemory,
|
|
371
|
+
query: string,
|
|
372
|
+
includeExternal: boolean,
|
|
373
|
+
): ITtscGraphLookup.IHit[] {
|
|
374
|
+
const target = documentationTarget(query);
|
|
375
|
+
if (target === undefined) return [];
|
|
376
|
+
const hits: ITtscGraphLookup.IHit[] = [];
|
|
377
|
+
// The artifact itself leads, when one was published under this address. A
|
|
378
|
+
// caller asking about `docs/sale.md#pricing` wants what that anchor names —
|
|
379
|
+
// the heading's own text and where it starts — before it wants the list of
|
|
380
|
+
// declarations that cite it, and answering only the second half is the gap an
|
|
381
|
+
// index of tokens had. It is not name-ranked: the address matched exactly.
|
|
382
|
+
const artifact = graph.node(target);
|
|
383
|
+
if (artifact !== undefined && isArtifactNodeKind(artifact.kind))
|
|
384
|
+
hits.push({
|
|
385
|
+
id: artifact.id,
|
|
386
|
+
name: artifact.name,
|
|
387
|
+
kind: artifact.kind,
|
|
388
|
+
file: artifact.file,
|
|
389
|
+
line: artifact.evidence?.startLine,
|
|
390
|
+
score: CITATION_SCORE,
|
|
391
|
+
});
|
|
392
|
+
for (const node of graph.citing(target)) {
|
|
393
|
+
if (node.kind === "file") continue;
|
|
394
|
+
if (!includeExternal && isExternalNode(node)) continue;
|
|
395
|
+
// Matched on the node's own tags and elided afterwards. Filtering the
|
|
396
|
+
// elided text instead dropped the explanation from a hit whose address was
|
|
397
|
+
// longer than the cap: the node was found by the address and then returned
|
|
398
|
+
// without the tag that said why.
|
|
399
|
+
const matched = docTagsOf(
|
|
400
|
+
node,
|
|
401
|
+
(tag) => documentationTarget(tag.text) === target,
|
|
402
|
+
);
|
|
403
|
+
const hit: ITtscGraphLookup.IHit = {
|
|
404
|
+
id: node.id,
|
|
405
|
+
name: node.qualifiedName ?? node.name,
|
|
406
|
+
kind: node.kind,
|
|
407
|
+
file: node.file,
|
|
408
|
+
line: node.evidence?.startLine,
|
|
409
|
+
// An exact target match is a certain answer rather than a ranked guess.
|
|
410
|
+
// The score exists so the field is never absent; it does not compete with
|
|
411
|
+
// the name scores, because these hits are not ordered against them.
|
|
412
|
+
score: CITATION_SCORE,
|
|
413
|
+
};
|
|
414
|
+
const decorators = decoratorsOf(node);
|
|
415
|
+
if (decorators !== undefined) hit.decorators = decorators;
|
|
416
|
+
if (matched !== undefined && matched.length > 0) hit.docTags = matched;
|
|
417
|
+
hits.push(hit);
|
|
418
|
+
}
|
|
419
|
+
return hits;
|
|
420
|
+
}
|
|
@@ -34,7 +34,9 @@ import { ITtscGraphTrace } from "./ITtscGraphTrace";
|
|
|
34
34
|
* the whole answer; do not split it. Name the machinery you expect it to be
|
|
35
35
|
* made of in its `reinterpretations`, or send none.
|
|
36
36
|
* - `entrypoints`: find where execution starts when entry points are unknown.
|
|
37
|
-
* - `lookup`: locate a named symbol
|
|
37
|
+
* - `lookup`: locate a named symbol, or — with a documentation target as the
|
|
38
|
+
* query (`docs/pricing.md#sale`, `POST:/orders`) — the declarations whose
|
|
39
|
+
* documentation cites it.
|
|
38
40
|
* - `trace`: follow calls or data flow forward or backward from a symbol, or —
|
|
39
41
|
* with `to` — the path between two symbols when both ends are known, which is
|
|
40
42
|
* the one call that answers "how does A reach B".
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ITtscGraphDecorator } from "./ITtscGraphDecorator";
|
|
2
|
+
import { ITtscGraphDocTag } from "./ITtscGraphDocTag";
|
|
2
3
|
import { ITtscGraphEvidence } from "./ITtscGraphEvidence";
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -135,6 +136,18 @@ export namespace ITtscGraphDetails {
|
|
|
135
136
|
/** Decorators written on this declaration, when any. */
|
|
136
137
|
decorators?: ITtscGraphDecorator[];
|
|
137
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Documentation tags naming what outside the type system this declaration
|
|
141
|
+
* answers to: a specification section, an API operation, a reference
|
|
142
|
+
* document (`@evidence docs/pricing.md#sale`, `@reference …`).
|
|
143
|
+
*
|
|
144
|
+
* Part of what the declaration is, so it is returned whole like its members
|
|
145
|
+
* and its values — long text is elided, and nothing else is cut. The tag is
|
|
146
|
+
* reported as written and never interpreted: the graph does not say the
|
|
147
|
+
* text resolves to anything or that the claim is true.
|
|
148
|
+
*/
|
|
149
|
+
docTags?: ITtscGraphDocTag[];
|
|
150
|
+
|
|
138
151
|
/** Assigned implementation span, when source comes from one. */
|
|
139
152
|
implementation?: ITtscGraphEvidence;
|
|
140
153
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One documentation tag TypeScript itself does not recognize, written on a
|
|
3
|
+
* declaration and carried on its {@link ITtscGraphNode}'s `docTags`.
|
|
4
|
+
*
|
|
5
|
+
* A convention attaches a declaration to something outside the type system — a
|
|
6
|
+
* specification section, an API operation, a reference document — and writes
|
|
7
|
+
* that attachment as a tag, because the thing on the other end is not a
|
|
8
|
+
* TypeScript declaration and so cannot be an edge. `@evidence
|
|
9
|
+
* docs/pricing.md#sale`, `@reference https://…`, and a project's own `@spec`
|
|
10
|
+
* are one fact in three spellings.
|
|
11
|
+
*
|
|
12
|
+
* Reported faithfully, never interpreted. The graph does not say a tag's text
|
|
13
|
+
* resolves to anything, is covered, or is true — a target naming nothing is
|
|
14
|
+
* carried exactly as written, the way {@link ITtscGraphDecorator} carries
|
|
15
|
+
* `@Controller` without knowing what a controller is.
|
|
16
|
+
*
|
|
17
|
+
* Which tags arrive is the compiler's boundary, not a list kept here: these are
|
|
18
|
+
* the tags the parser had no meaning for. A known tag — `@param`, `@returns`,
|
|
19
|
+
* `@deprecated` — has its own shape and its own meaning and is not one of
|
|
20
|
+
* these.
|
|
21
|
+
*
|
|
22
|
+
* The parser also decides where a tag begins, and that decision is reported
|
|
23
|
+
* rather than second-guessed. TypeScript opens a tag at an `@` wherever it sits
|
|
24
|
+
* inside a documentation block, so a tag name written mid-sentence arrives here
|
|
25
|
+
* as a tag, while a `//` comment is not documentation and contributes nothing at
|
|
26
|
+
* all. A convention that wants the stricter rule enforces it in its own linter,
|
|
27
|
+
* where the author gets a diagnostic; the graph reports what the compiler
|
|
28
|
+
* parsed.
|
|
29
|
+
*/
|
|
30
|
+
export interface ITtscGraphDocTag {
|
|
31
|
+
/** The tag name without its `@`: `evidence`, `evidenceExclude`, `reference`. */
|
|
32
|
+
name: string;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Everything written after the tag name, joined into one line.
|
|
36
|
+
*
|
|
37
|
+
* An inline link keeps the braced form it was written in (`{@link ISale}`),
|
|
38
|
+
* so a consumer matching a citation target sees what the author typed. The
|
|
39
|
+
* same link is separately a resolved edge on the node, which is what says
|
|
40
|
+
* *which* `ISale` — a name can be declared twice and only the checker knows.
|
|
41
|
+
*
|
|
42
|
+
* Absent when the tag carries no text.
|
|
43
|
+
*/
|
|
44
|
+
text?: string;
|
|
45
|
+
}
|
|
@@ -16,7 +16,7 @@ import { TtscGraphDumpNodeKind } from "./TtscGraphDumpNodeKind";
|
|
|
16
16
|
* the same dump.
|
|
17
17
|
*
|
|
18
18
|
* `project` is the producer-local absolute locator. Every identity-bearing path
|
|
19
|
-
* uses one
|
|
19
|
+
* uses one portable coordinate relative to it: project files are ordinary
|
|
20
20
|
* relative paths; same-filesystem siblings use `../` segments; package files
|
|
21
21
|
* keep their full resolution context (including version/peer-store segments);
|
|
22
22
|
* and a virtual compiler source stays `bundled:///…`. Raw absolute identities
|
|
@@ -76,8 +76,8 @@ export namespace ITtscGraphDump {
|
|
|
76
76
|
* guessing from a field's emptiness, because an empty list and an
|
|
77
77
|
* uncollected one look identical on the wire.
|
|
78
78
|
*
|
|
79
|
-
* The known members are `universe`, `sourceDigests`, `diskDigests`,
|
|
80
|
-
* `diagnostics`. The type stays `string[]` rather than a union of those on
|
|
79
|
+
* The known members are `universe`, `sourceDigests`, `diskDigests`,
|
|
80
|
+
* `diagnostics`, and `docTags`. The type stays `string[]` rather than a union of those on
|
|
81
81
|
* purpose: a union would make `typia.assert` reject a newer producer for
|
|
82
82
|
* naming a capability this client has not heard of, turning "proves more
|
|
83
83
|
* than you know about" into a hard failure. An unknown capability is
|
|
@@ -88,6 +88,17 @@ export namespace ITtscGraphDump {
|
|
|
88
88
|
/** What built the snapshot. */
|
|
89
89
|
producer: IProducer;
|
|
90
90
|
|
|
91
|
+
/**
|
|
92
|
+
* The second producer behind the artifact nodes, absent when the dump
|
|
93
|
+
* carries none.
|
|
94
|
+
*
|
|
95
|
+
* Every other fact in this dump came from one Program. These did not: a
|
|
96
|
+
* plugin parsed documents that Program never read, in a process of its own.
|
|
97
|
+
* Saying so is what keeps the one-generation contract honest instead of
|
|
98
|
+
* letting an overlay ride the same claim as the compiler's facts.
|
|
99
|
+
*/
|
|
100
|
+
artifactProducer?: IProducer;
|
|
101
|
+
|
|
91
102
|
/** The inputs that decide which files are in the program at all. */
|
|
92
103
|
universe: IUniverse;
|
|
93
104
|
|
|
@@ -233,9 +244,19 @@ export namespace ITtscGraphDump {
|
|
|
233
244
|
ITtscGraphNode,
|
|
234
245
|
"evidence" | "implementation" | "kind"
|
|
235
246
|
> {
|
|
236
|
-
/**
|
|
247
|
+
/** Node kind written by the native producer. */
|
|
237
248
|
kind: TtscGraphDumpNodeKind;
|
|
238
249
|
|
|
250
|
+
/**
|
|
251
|
+
* The artifact containing this one, by id.
|
|
252
|
+
*
|
|
253
|
+
* Present only on an artifact node: a declaration's containment is
|
|
254
|
+
* synthesized by the memory layer, and two producers of one relation would
|
|
255
|
+
* put two answers in the graph. Absent at the top of a chain, and absent
|
|
256
|
+
* rather than invented when the parent was not published.
|
|
257
|
+
*/
|
|
258
|
+
parent?: string;
|
|
259
|
+
|
|
239
260
|
/** Declaration span; its file is this node's `file`. */
|
|
240
261
|
evidence?: ITtscGraphSpan;
|
|
241
262
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ITtscGraphDecorator } from "./ITtscGraphDecorator";
|
|
2
|
+
import { ITtscGraphDocTag } from "./ITtscGraphDocTag";
|
|
2
3
|
|
|
3
4
|
/** Targeted symbol lookup when a concrete name or handle is being resolved. */
|
|
4
5
|
export interface ITtscGraphLookup {
|
|
@@ -7,6 +8,19 @@ export interface ITtscGraphLookup {
|
|
|
7
8
|
|
|
8
9
|
/** Ranked symbol matches for the query. */
|
|
9
10
|
hits: ITtscGraphLookup.IHit[];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* True when a match was left out by the limit.
|
|
14
|
+
*
|
|
15
|
+
* The audit for the ranked operations already tells a reader that
|
|
16
|
+
* `truncated` marks where more was left out, and this result had no such
|
|
17
|
+
* field — so a cut looked exactly like a complete answer. That is tolerable
|
|
18
|
+
* for a name query, where the ranking is a shortlist by design, and it is not
|
|
19
|
+
* for a documentation target: those hits are an exact match on an address, so
|
|
20
|
+
* a caller asking which code implements a specification is owed the fact that
|
|
21
|
+
* it did not get all of it.
|
|
22
|
+
*/
|
|
23
|
+
truncated?: boolean;
|
|
10
24
|
}
|
|
11
25
|
export namespace ITtscGraphLookup {
|
|
12
26
|
/** Find a concrete class, method, function, property, type, or dotted handle. */
|
|
@@ -19,6 +33,14 @@ export namespace ITtscGraphLookup {
|
|
|
19
33
|
* short phrase (`request handler`). Exact names are not required, but this
|
|
20
34
|
* is not a second broad entrypoints call; use it for a missing or ambiguous
|
|
21
35
|
* named handle.
|
|
36
|
+
*
|
|
37
|
+
* It also answers the other direction. Give it a documentation target — a
|
|
38
|
+
* document section (`docs/pricing.md#sale`), an API operation
|
|
39
|
+
* (`POST:/orders`), a data model (`prisma:Sale`) — and the hits are the
|
|
40
|
+
* declarations whose documentation cites it, each carrying the tag that
|
|
41
|
+
* matched. That is the question a repository-wide search would otherwise
|
|
42
|
+
* answer, so it is worth asking here first; a target is matched exactly, so
|
|
43
|
+
* spell it as the code does.
|
|
22
44
|
*/
|
|
23
45
|
query: string;
|
|
24
46
|
|
|
@@ -63,6 +85,15 @@ export namespace ITtscGraphLookup {
|
|
|
63
85
|
/** Decorators written on this declaration, when any. */
|
|
64
86
|
decorators?: ITtscGraphDecorator[];
|
|
65
87
|
|
|
88
|
+
/**
|
|
89
|
+
* The documentation tags that matched the query, when the query named one.
|
|
90
|
+
*
|
|
91
|
+
* Present only on a hit found through its tags, so it says why this
|
|
92
|
+
* declaration is here: the query named a specification and this is the code
|
|
93
|
+
* that answers to it. A hit matched by name carries none.
|
|
94
|
+
*/
|
|
95
|
+
docTags?: ITtscGraphDocTag[];
|
|
96
|
+
|
|
66
97
|
/** Relative relevance; higher is a better match. */
|
|
67
98
|
score: number;
|
|
68
99
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ITtscGraphDecorator } from "./ITtscGraphDecorator";
|
|
2
|
+
import { ITtscGraphDocTag } from "./ITtscGraphDocTag";
|
|
2
3
|
import { ITtscGraphEvidence } from "./ITtscGraphEvidence";
|
|
3
4
|
import { TtscGraphNodeKind } from "./TtscGraphNodeKind";
|
|
4
5
|
import { TtscGraphNodeModifier } from "./TtscGraphNodeModifier";
|
|
@@ -18,6 +19,17 @@ export interface ITtscGraphNode {
|
|
|
18
19
|
/** What this node represents. */
|
|
19
20
|
kind: TtscGraphNodeKind;
|
|
20
21
|
|
|
22
|
+
/**
|
|
23
|
+
* The artifact containing this one, by id.
|
|
24
|
+
*
|
|
25
|
+
* Present only on an artifact node — a section's document or enclosing
|
|
26
|
+
* section, a column's model. A declaration's containment is synthesized by
|
|
27
|
+
* the memory layer from its owner and its file, which an artifact has no
|
|
28
|
+
* equivalent of: a Prisma model has no file in its address, and an API
|
|
29
|
+
* operation has no file at all.
|
|
30
|
+
*/
|
|
31
|
+
parent?: string;
|
|
32
|
+
|
|
21
33
|
/** The simple, unqualified declared name (`create`, `OrderService`, `App`). */
|
|
22
34
|
name: string;
|
|
23
35
|
|
|
@@ -105,6 +117,21 @@ export interface ITtscGraphNode {
|
|
|
105
117
|
*/
|
|
106
118
|
decorators?: ITtscGraphDecorator[];
|
|
107
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Documentation tags TypeScript does not recognize, in source order: raw
|
|
122
|
+
* facts (`@evidence docs/pricing.md#sale`, `@reference …`) a consumer
|
|
123
|
+
* interprets without re-parsing source.
|
|
124
|
+
*
|
|
125
|
+
* This is where a declaration says what outside the type system it answers
|
|
126
|
+
* to, and it is the only place the graph holds it: the other end of such a
|
|
127
|
+
* relation is a document section or an API operation, which is not a node.
|
|
128
|
+
*
|
|
129
|
+
* Read it only when `provenance.capabilities` lists `docTags`. Without that
|
|
130
|
+
* claim the field is absent because the producer never looked, which is a
|
|
131
|
+
* different fact from a declaration that carries none.
|
|
132
|
+
*/
|
|
133
|
+
docTags?: ITtscGraphDocTag[];
|
|
134
|
+
|
|
108
135
|
/**
|
|
109
136
|
* The declaration head, cut by the producer where the compiler says the body
|
|
110
137
|
* opens.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { TtscGraphNodeKind } from "./TtscGraphNodeKind";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The node kinds that are published artifacts rather than TypeScript
|
|
5
|
+
* declarations.
|
|
6
|
+
*
|
|
7
|
+
* A citation whose target is a TypeScript symbol is a relation the checker
|
|
8
|
+
* resolved. A citation whose target is a document section, a data model field,
|
|
9
|
+
* or an API operation is one of these: a plugin parsed the artifact and
|
|
10
|
+
* published what it is, and the graph indexes that without interpreting any of
|
|
11
|
+
* it. What the linter decided about the citation — covered, excluded, missing —
|
|
12
|
+
* never travels; that is its product and it delivers it as a compile error.
|
|
13
|
+
*/
|
|
14
|
+
export const TTSC_GRAPH_ARTIFACT_NODE_KINDS = [
|
|
15
|
+
"markdown_document",
|
|
16
|
+
"markdown_section",
|
|
17
|
+
"prisma_model",
|
|
18
|
+
"prisma_column",
|
|
19
|
+
"prisma_relation",
|
|
20
|
+
"swagger_operation",
|
|
21
|
+
] as const satisfies readonly TtscGraphNodeKind[];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Whether a node kind names a published artifact.
|
|
25
|
+
*
|
|
26
|
+
* It is the gate an id parser needs. An artifact's id is the address a citation
|
|
27
|
+
* writes — `docs/sale.md#pricing`, `prisma:Sale.price`, `POST:/orders` — and
|
|
28
|
+
* none of those follow the `path#qualifiedName:kind` grammar
|
|
29
|
+
* {@link TtscGraphNodeId} assumes: a Prisma address carries no path because a
|
|
30
|
+
* model name is unique across the schema folder, and an operation has no file
|
|
31
|
+
* at all.
|
|
32
|
+
*/
|
|
33
|
+
export function isArtifactNodeKind(kind: string): boolean {
|
|
34
|
+
return (TTSC_GRAPH_ARTIFACT_NODE_KINDS as readonly string[]).includes(kind);
|
|
35
|
+
}
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Node kinds the native Go dump producer can write.
|
|
3
|
+
*
|
|
4
|
+
* The last six are not declarations. A plugin materialized them from a Markdown
|
|
5
|
+
* document, a Prisma schema, or an API document — artifacts a citation can name
|
|
6
|
+
* that the type system holds nothing for — and their ids are the address a
|
|
7
|
+
* citation writes rather than the `path#name:kind` grammar, which is why an id
|
|
8
|
+
* is parsed only after its kind says it can be.
|
|
9
|
+
*/
|
|
2
10
|
export type TtscGraphDumpNodeKind =
|
|
3
11
|
| "module"
|
|
4
12
|
| "function"
|
|
@@ -7,4 +15,10 @@ export type TtscGraphDumpNodeKind =
|
|
|
7
15
|
| "type"
|
|
8
16
|
| "enum"
|
|
9
17
|
| "variable"
|
|
10
|
-
| "method"
|
|
18
|
+
| "method"
|
|
19
|
+
| "markdown_document"
|
|
20
|
+
| "markdown_section"
|
|
21
|
+
| "prisma_model"
|
|
22
|
+
| "prisma_column"
|
|
23
|
+
| "prisma_relation"
|
|
24
|
+
| "swagger_operation";
|
|
@@ -8,6 +8,13 @@
|
|
|
8
8
|
* — `renders` is a JSX component use. Decorators are facts on their target node,
|
|
9
9
|
* not edges.
|
|
10
10
|
*
|
|
11
|
+
* `doc_ref` is a declaration's own documentation naming a symbol through an
|
|
12
|
+
* inline link. The checker resolves that name and counts it as a use, so it is
|
|
13
|
+
* a compiler fact like the rest; it is its own kind rather than a `type_ref`
|
|
14
|
+
* because a link is not a type position and may name a function. The tag around
|
|
15
|
+
* a link decides nothing — one under `@evidence`, under `@see`, and in ordinary
|
|
16
|
+
* prose are one relation.
|
|
17
|
+
*
|
|
11
18
|
* `dispatches` is the runtime counterpart of `overrides`/`implements`: the
|
|
12
19
|
* checker resolves a call to the declaration it names, and where that
|
|
13
20
|
* declaration is abstract or an interface member, the code that runs is its
|
|
@@ -22,6 +29,7 @@ export type TtscGraphEdgeKind =
|
|
|
22
29
|
| "accesses"
|
|
23
30
|
| "instantiates"
|
|
24
31
|
| "type_ref"
|
|
32
|
+
| "doc_ref"
|
|
25
33
|
| "extends"
|
|
26
34
|
| "implements"
|
|
27
35
|
| "overrides"
|
package/src/structures/index.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
export * from "./ITtscGraphApplication";
|
|
8
8
|
export * from "./ITtscGraphDecorator";
|
|
9
|
+
export * from "./ITtscGraphDocTag";
|
|
9
10
|
export * from "./ITtscGraphDump";
|
|
10
11
|
export * from "./ITtscGraphEdge";
|
|
11
12
|
export * from "./ITtscGraphEvidence";
|
|
@@ -23,5 +24,6 @@ export * from "./ITtscGraphTour";
|
|
|
23
24
|
export * from "./TtscGraphEdgeKind";
|
|
24
25
|
export * from "./TtscGraphDumpEdgeKind";
|
|
25
26
|
export * from "./TtscGraphDumpNodeKind";
|
|
27
|
+
export * from "./TtscGraphArtifactNodeKind";
|
|
26
28
|
export * from "./TtscGraphNodeKind";
|
|
27
29
|
export * from "./TtscGraphNodeModifier";
|
package/src/view.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
projectOptions,
|
|
11
11
|
} from "./launcherArgs";
|
|
12
12
|
import { parseDump } from "./model/loadGraph";
|
|
13
|
+
import { publishArtifacts } from "./model/publishedArtifacts";
|
|
13
14
|
import { captureProcessOutput, ensureExecutable } from "./nativeExecutable";
|
|
14
15
|
import { reduce } from "./reduce";
|
|
15
16
|
import { resolveGraphBinary } from "./resolveGraphBinary";
|
|
@@ -76,9 +77,24 @@ export function runView(argv: readonly string[]): number | void {
|
|
|
76
77
|
let dumpStdout: string;
|
|
77
78
|
let dumpStderr: string;
|
|
78
79
|
try {
|
|
80
|
+
// The same artifacts the dump, the loader, and the resident session ask
|
|
81
|
+
// for. Without this the viewer draws a different graph from the one every
|
|
82
|
+
// other entry point answers with — and it already carries the colours for
|
|
83
|
+
// the nodes it would be missing.
|
|
84
|
+
const artifacts = publishArtifacts({
|
|
85
|
+
cwd: opts.cwd,
|
|
86
|
+
tsconfig: opts.tsconfig,
|
|
87
|
+
});
|
|
79
88
|
dump = spawnSync(
|
|
80
89
|
binary,
|
|
81
|
-
[
|
|
90
|
+
[
|
|
91
|
+
"dump",
|
|
92
|
+
"--cwd",
|
|
93
|
+
opts.cwd,
|
|
94
|
+
"--tsconfig",
|
|
95
|
+
opts.tsconfig,
|
|
96
|
+
...(artifacts.file === null ? [] : ["--artifacts", artifacts.file]),
|
|
97
|
+
],
|
|
82
98
|
{
|
|
83
99
|
stdio: ["ignore", capture.stdoutFd, capture.stderrFd],
|
|
84
100
|
windowsHide: true,
|
package/src/viewer/index.html
CHANGED
|
@@ -102,19 +102,11 @@
|
|
|
102
102
|
<span id="counts"></span>
|
|
103
103
|
</header>
|
|
104
104
|
<div id="graph"></div>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
<span class="dot"
|
|
111
|
-
><span class="swatch" style="background: #f5b042"></span
|
|
112
|
-
>type-ref</span
|
|
113
|
-
>
|
|
114
|
-
<span class="dot"
|
|
115
|
-
><span class="swatch" style="background: #6ea8ff"></span
|
|
116
|
-
>heritage</span
|
|
117
|
-
>
|
|
105
|
+
<!-- The edge-family swatches are prepended here from the one colour map
|
|
106
|
+
in legend.ts. They used to be written out by hand, so a new family
|
|
107
|
+
rendered in the graph with no entry beside it until someone opened
|
|
108
|
+
both files. -->
|
|
109
|
+
<footer id="legend">
|
|
118
110
|
<span>node size = connection count · color = declaration kind</span>
|
|
119
111
|
</footer>
|
|
120
112
|
</div>
|