@ttsc/evidence 0.25.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/LICENSE +21 -0
- package/README.md +279 -0
- package/lib/index.d.ts +65 -0
- package/lib/index.js +87 -0
- package/lib/internal/loadPrismaModels.d.ts +1 -0
- package/lib/internal/loadPrismaModels.js +197 -0
- package/lib/internal/loadSwaggerOperations.d.ts +1 -0
- package/lib/internal/loadSwaggerOperations.js +156 -0
- package/lib/internal/normalizeSwaggerDocument.d.ts +1 -0
- package/lib/internal/normalizeSwaggerDocument.js +7 -0
- package/lib/structures/ITtscEvidenceDocumentedConfig.d.ts +32 -0
- package/lib/structures/ITtscEvidenceDocumentedConfig.js +2 -0
- package/lib/structures/ITtscEvidenceGraphClaim.d.ts +14 -0
- package/lib/structures/ITtscEvidenceGraphClaim.js +2 -0
- package/lib/structures/ITtscEvidenceGraphClaimBase.d.ts +102 -0
- package/lib/structures/ITtscEvidenceGraphClaimBase.js +2 -0
- package/lib/structures/ITtscEvidenceGraphConfig.d.ts +19 -0
- package/lib/structures/ITtscEvidenceGraphConfig.js +2 -0
- package/lib/structures/ITtscEvidenceGraphMarkdownClaim.d.ts +40 -0
- package/lib/structures/ITtscEvidenceGraphMarkdownClaim.js +2 -0
- package/lib/structures/ITtscEvidenceGraphMarkdownReference.d.ts +76 -0
- package/lib/structures/ITtscEvidenceGraphMarkdownReference.js +2 -0
- package/lib/structures/ITtscEvidenceGraphPrismaClaim.d.ts +65 -0
- package/lib/structures/ITtscEvidenceGraphPrismaClaim.js +2 -0
- package/lib/structures/ITtscEvidenceGraphPrismaReference.d.ts +75 -0
- package/lib/structures/ITtscEvidenceGraphPrismaReference.js +2 -0
- package/lib/structures/ITtscEvidenceGraphReference.d.ts +14 -0
- package/lib/structures/ITtscEvidenceGraphReference.js +2 -0
- package/lib/structures/ITtscEvidenceGraphReferenceBase.d.ts +73 -0
- package/lib/structures/ITtscEvidenceGraphReferenceBase.js +2 -0
- package/lib/structures/ITtscEvidenceGraphSwaggerReference.d.ts +39 -0
- package/lib/structures/ITtscEvidenceGraphSwaggerReference.js +2 -0
- package/lib/structures/ITtscEvidenceGraphTypeScriptClaim.d.ts +54 -0
- package/lib/structures/ITtscEvidenceGraphTypeScriptClaim.js +2 -0
- package/lib/structures/ITtscEvidenceGraphTypeScriptReference.d.ts +83 -0
- package/lib/structures/ITtscEvidenceGraphTypeScriptReference.js +2 -0
- package/lib/structures/index.d.ts +13 -0
- package/lib/structures/index.js +29 -0
- package/lib/typings/ITtscLintContributorRules.d.ts +34 -0
- package/lib/typings/ITtscLintContributorRules.js +2 -0
- package/lib/typings/ITtscLintRuleOptionsMap.d.ts +20 -0
- package/lib/typings/ITtscLintRuleOptionsMap.js +2 -0
- package/lib/typings/TtscEvidenceGraphMarkdownSymbol.d.ts +32 -0
- package/lib/typings/TtscEvidenceGraphMarkdownSymbol.js +2 -0
- package/lib/typings/TtscEvidenceGraphPrismaSymbol.d.ts +38 -0
- package/lib/typings/TtscEvidenceGraphPrismaSymbol.js +2 -0
- package/lib/typings/TtscEvidenceGraphTypeScriptSymbol.d.ts +64 -0
- package/lib/typings/TtscEvidenceGraphTypeScriptSymbol.js +2 -0
- package/lib/typings/index.d.ts +5 -0
- package/lib/typings/index.js +21 -0
- package/native/base.go +298 -0
- package/native/config.go +798 -0
- package/native/declaration.go +240 -0
- package/native/documented.go +353 -0
- package/native/exports.go +484 -0
- package/native/glob.go +207 -0
- package/native/graph.go +1409 -0
- package/native/health.go +45 -0
- package/native/hints.go +261 -0
- package/native/imports.go +373 -0
- package/native/inputs.go +125 -0
- package/native/loader.go +424 -0
- package/native/markdown.go +423 -0
- package/native/model.go +296 -0
- package/native/prisma.go +719 -0
- package/native/prisma_cache.go +213 -0
- package/native/prisma_locator.go +417 -0
- package/native/singular.go +369 -0
- package/native/swagger.go +331 -0
- package/native/swagger_cache.go +274 -0
- package/native/todo.go +138 -0
- package/native/typescript.go +1199 -0
- package/native/typescript_cache.go +96 -0
- package/package.json +64 -0
|
@@ -0,0 +1,1199 @@
|
|
|
1
|
+
package evidence
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"path/filepath"
|
|
5
|
+
"sort"
|
|
6
|
+
"strings"
|
|
7
|
+
|
|
8
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
func loadTypeScriptInventories(
|
|
12
|
+
root string,
|
|
13
|
+
sources []*shimast.SourceFile,
|
|
14
|
+
config graphConfig,
|
|
15
|
+
) map[string]*artifactInventory {
|
|
16
|
+
inventories := map[string]*artifactInventory{}
|
|
17
|
+
extendTypeScriptInventories(root, sources, config, inventories)
|
|
18
|
+
return inventories
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// extendTypeScriptInventories adds only populations the caller has not already
|
|
22
|
+
// scanned.
|
|
23
|
+
//
|
|
24
|
+
// Graph activation first needs each TypeScript claim's own population, before
|
|
25
|
+
// it is allowed to inspect that claim's references. Once active claims are
|
|
26
|
+
// known, this second pass adds their reference bases without rescanning the
|
|
27
|
+
// claim bases already materialized.
|
|
28
|
+
func extendTypeScriptInventories(
|
|
29
|
+
root string,
|
|
30
|
+
sources []*shimast.SourceFile,
|
|
31
|
+
config graphConfig,
|
|
32
|
+
inventories map[string]*artifactInventory,
|
|
33
|
+
) {
|
|
34
|
+
bases := configuredBases(config, artifactTypeScript)
|
|
35
|
+
for _, file := range sources {
|
|
36
|
+
if file == nil || !isTypeScriptPath(file.FileName()) {
|
|
37
|
+
continue
|
|
38
|
+
}
|
|
39
|
+
for _, base := range bases {
|
|
40
|
+
relative, ok := relativeProjectPath(base.Absolute, file.FileName())
|
|
41
|
+
if !ok || !isTypeScriptPath(relative) {
|
|
42
|
+
continue
|
|
43
|
+
}
|
|
44
|
+
address := base.addressOf(relative)
|
|
45
|
+
if inventories[address.Key] != nil {
|
|
46
|
+
continue
|
|
47
|
+
}
|
|
48
|
+
inventories[address.Key] = typeScriptInventories.scan(address, file)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// isTypeScriptPath is asked once per source file per configured base on every
|
|
54
|
+
// rebuild, so it compares the suffix in place rather than lowercasing the whole
|
|
55
|
+
// path into a fresh string to answer four fixed questions.
|
|
56
|
+
func isTypeScriptPath(path string) bool {
|
|
57
|
+
for _, suffix := range []string{".ts", ".tsx", ".mts", ".cts"} {
|
|
58
|
+
if len(path) >= len(suffix) &&
|
|
59
|
+
strings.EqualFold(path[len(path)-len(suffix):], suffix) {
|
|
60
|
+
return true
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return false
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
func relativeProjectPath(root string, absolute string) (string, bool) {
|
|
67
|
+
if root == "" || absolute == "" {
|
|
68
|
+
return "", false
|
|
69
|
+
}
|
|
70
|
+
// A source file usually sits below the base that is asking, spelled the same
|
|
71
|
+
// way. Answering that from the two strings keeps the general path machinery
|
|
72
|
+
// off a loop that runs once per file per base on every rebuild.
|
|
73
|
+
if inside, ok := containedProjectPath(root, absolute); ok {
|
|
74
|
+
return inside, true
|
|
75
|
+
}
|
|
76
|
+
relative, err := filepath.Rel(root, absolute)
|
|
77
|
+
if err != nil {
|
|
78
|
+
return "", false
|
|
79
|
+
}
|
|
80
|
+
relative = strings.ReplaceAll(relative, "\\", "/")
|
|
81
|
+
if relative == ".." || strings.HasPrefix(relative, "../") {
|
|
82
|
+
return "", false
|
|
83
|
+
}
|
|
84
|
+
return strings.TrimPrefix(relative, "./"), true
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// containedProjectPath answers the ordinary case of one path sitting below
|
|
88
|
+
// another: same spelling, one separator between them, and nothing left to
|
|
89
|
+
// normalize. It declines anything else so the general form still decides.
|
|
90
|
+
//
|
|
91
|
+
// The prefix comparison is exact rather than case-insensitive, because the
|
|
92
|
+
// general form is `filepath.Rel`, which is lexical and case-sensitive on every
|
|
93
|
+
// platform. Folding case here would admit a differently-cased sibling that the
|
|
94
|
+
// path this shortcut stands in for rejects.
|
|
95
|
+
func containedProjectPath(root string, absolute string) (string, bool) {
|
|
96
|
+
if len(absolute) <= len(root)+1 ||
|
|
97
|
+
absolute[:len(root)] != root {
|
|
98
|
+
return "", false
|
|
99
|
+
}
|
|
100
|
+
if separator := absolute[len(root)]; separator != '/' && separator != '\\' {
|
|
101
|
+
return "", false
|
|
102
|
+
}
|
|
103
|
+
relative := absolute[len(root)+1:]
|
|
104
|
+
if strings.ContainsRune(relative, '\\') {
|
|
105
|
+
return "", false
|
|
106
|
+
}
|
|
107
|
+
for segment := range strings.SplitSeq(relative, "/") {
|
|
108
|
+
if segment == "" || segment == "." || segment == ".." {
|
|
109
|
+
return "", false
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return relative, true
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
func scanTypeScriptInventory(
|
|
116
|
+
path string,
|
|
117
|
+
file *shimast.SourceFile,
|
|
118
|
+
) *artifactInventory {
|
|
119
|
+
return scanTypeScriptInventoryAt(artifactAddress{
|
|
120
|
+
Base: populationBase{Default: true},
|
|
121
|
+
Relative: path,
|
|
122
|
+
Display: path,
|
|
123
|
+
Key: path,
|
|
124
|
+
}, file)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
func scanTypeScriptInventoryAt(
|
|
128
|
+
address artifactAddress,
|
|
129
|
+
file *shimast.SourceFile,
|
|
130
|
+
) *artifactInventory {
|
|
131
|
+
inventory := &artifactInventory{
|
|
132
|
+
Address: address.Key,
|
|
133
|
+
Path: address.Display,
|
|
134
|
+
Type: artifactTypeScript,
|
|
135
|
+
Imports: collectImportBindings(file),
|
|
136
|
+
Exports: collectModuleExports(file),
|
|
137
|
+
UnitNodes: map[string][]*shimast.Node{},
|
|
138
|
+
}
|
|
139
|
+
supportedHosts := map[*shimast.Node]symbolSet{}
|
|
140
|
+
unitsByID := map[string]*evidenceUnit{}
|
|
141
|
+
collectTypeScriptStatements(
|
|
142
|
+
file,
|
|
143
|
+
file.Statements,
|
|
144
|
+
nil,
|
|
145
|
+
"",
|
|
146
|
+
inventory,
|
|
147
|
+
supportedHosts,
|
|
148
|
+
unitsByID,
|
|
149
|
+
file.IsDeclarationFile,
|
|
150
|
+
false,
|
|
151
|
+
false,
|
|
152
|
+
"",
|
|
153
|
+
)
|
|
154
|
+
collectTypeScriptDeclarations(
|
|
155
|
+
file,
|
|
156
|
+
address.Key,
|
|
157
|
+
address.Display,
|
|
158
|
+
inventory,
|
|
159
|
+
supportedHosts,
|
|
160
|
+
)
|
|
161
|
+
// Policy evaluation needs semantic host IDs, not the AST node associations
|
|
162
|
+
// used to derive them. Declarations now retain those IDs directly, so release
|
|
163
|
+
// the transient index before this inventory enters the immutable graph.
|
|
164
|
+
inventory.UnitNodes = nil
|
|
165
|
+
sort.Slice(inventory.Units, func(left int, right int) bool {
|
|
166
|
+
if inventory.Units[left].Target != inventory.Units[right].Target {
|
|
167
|
+
return inventory.Units[left].Target < inventory.Units[right].Target
|
|
168
|
+
}
|
|
169
|
+
return inventory.Units[left].Line < inventory.Units[right].Line
|
|
170
|
+
})
|
|
171
|
+
return inventory
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// collectTypeScriptStatements materializes the public units one statement list
|
|
175
|
+
// declares.
|
|
176
|
+
//
|
|
177
|
+
// hidden carries the documentation tag by which an enclosing declaration
|
|
178
|
+
// withdrew itself from the public surface. It is inherited rather than
|
|
179
|
+
// recomputed, which is what makes `@internal` on a namespace reach every member
|
|
180
|
+
// beneath it without each member repeating the tag.
|
|
181
|
+
func collectTypeScriptStatements(
|
|
182
|
+
file *shimast.SourceFile,
|
|
183
|
+
statements *shimast.NodeList,
|
|
184
|
+
prefix []string,
|
|
185
|
+
parentID string,
|
|
186
|
+
inventory *artifactInventory,
|
|
187
|
+
supportedHosts map[*shimast.Node]symbolSet,
|
|
188
|
+
unitsByID map[string]*evidenceUnit,
|
|
189
|
+
ambientContext bool,
|
|
190
|
+
implicitlyExported bool,
|
|
191
|
+
typeOnlyProjection bool,
|
|
192
|
+
hidden string,
|
|
193
|
+
) {
|
|
194
|
+
if statements == nil {
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
exports := collectLocalExportNames(statements)
|
|
198
|
+
hiddenNames := collectHiddenDeclarationNames(file, statements)
|
|
199
|
+
// Built on the first namespace this list holds rather than up front, so a
|
|
200
|
+
// file declaring none pays nothing. Every rebuild scans every configured
|
|
201
|
+
// source, and most of them have no namespace at all.
|
|
202
|
+
var functionNames map[string]bool
|
|
203
|
+
for _, statement := range statements.Nodes {
|
|
204
|
+
if statement == nil {
|
|
205
|
+
continue
|
|
206
|
+
}
|
|
207
|
+
switch statement.Kind {
|
|
208
|
+
case shimast.KindInterfaceDeclaration:
|
|
209
|
+
name := declarationName(statement.Name())
|
|
210
|
+
if name == "" {
|
|
211
|
+
continue
|
|
212
|
+
}
|
|
213
|
+
targets := publicTypeScriptNames(
|
|
214
|
+
statement,
|
|
215
|
+
name,
|
|
216
|
+
exports,
|
|
217
|
+
true,
|
|
218
|
+
implicitlyExported,
|
|
219
|
+
)
|
|
220
|
+
if len(targets) == 0 {
|
|
221
|
+
continue
|
|
222
|
+
}
|
|
223
|
+
memberHidden := hidingTagFor(hidden, hiddenNames, name)
|
|
224
|
+
if memberHidden == "" {
|
|
225
|
+
addTypeScriptHost(supportedHosts, statement, "type")
|
|
226
|
+
}
|
|
227
|
+
for _, name := range targets {
|
|
228
|
+
identity := qualifyTypeScriptName(prefix, name)
|
|
229
|
+
unit := addTypeScriptUnit(
|
|
230
|
+
inventory,
|
|
231
|
+
unitsByID,
|
|
232
|
+
statement,
|
|
233
|
+
"type",
|
|
234
|
+
identity,
|
|
235
|
+
parentID,
|
|
236
|
+
memberHidden,
|
|
237
|
+
)
|
|
238
|
+
collectPropertyMembers(
|
|
239
|
+
file,
|
|
240
|
+
statement.AsInterfaceDeclaration().Members,
|
|
241
|
+
identity,
|
|
242
|
+
unit.ID,
|
|
243
|
+
inventory,
|
|
244
|
+
supportedHosts,
|
|
245
|
+
unitsByID,
|
|
246
|
+
memberHidden,
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
case shimast.KindTypeAliasDeclaration:
|
|
250
|
+
name := declarationName(statement.Name())
|
|
251
|
+
if name == "" {
|
|
252
|
+
continue
|
|
253
|
+
}
|
|
254
|
+
targets := publicTypeScriptNames(
|
|
255
|
+
statement,
|
|
256
|
+
name,
|
|
257
|
+
exports,
|
|
258
|
+
true,
|
|
259
|
+
implicitlyExported,
|
|
260
|
+
)
|
|
261
|
+
if len(targets) == 0 {
|
|
262
|
+
continue
|
|
263
|
+
}
|
|
264
|
+
memberHidden := hidingTagFor(hidden, hiddenNames, name)
|
|
265
|
+
if memberHidden == "" {
|
|
266
|
+
addTypeScriptHost(supportedHosts, statement, "type")
|
|
267
|
+
}
|
|
268
|
+
alias := statement.AsTypeAliasDeclaration()
|
|
269
|
+
for _, name := range targets {
|
|
270
|
+
identity := qualifyTypeScriptName(prefix, name)
|
|
271
|
+
unit := addTypeScriptUnit(
|
|
272
|
+
inventory,
|
|
273
|
+
unitsByID,
|
|
274
|
+
statement,
|
|
275
|
+
"type",
|
|
276
|
+
identity,
|
|
277
|
+
parentID,
|
|
278
|
+
memberHidden,
|
|
279
|
+
)
|
|
280
|
+
if alias.Type != nil && alias.Type.Kind == shimast.KindTypeLiteral {
|
|
281
|
+
collectPropertyMembers(
|
|
282
|
+
file,
|
|
283
|
+
alias.Type.AsTypeLiteralNode().Members,
|
|
284
|
+
identity,
|
|
285
|
+
unit.ID,
|
|
286
|
+
inventory,
|
|
287
|
+
supportedHosts,
|
|
288
|
+
unitsByID,
|
|
289
|
+
memberHidden,
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
case shimast.KindFunctionDeclaration:
|
|
294
|
+
if typeOnlyProjection {
|
|
295
|
+
continue
|
|
296
|
+
}
|
|
297
|
+
name := declarationName(statement.Name())
|
|
298
|
+
if name == "" {
|
|
299
|
+
continue
|
|
300
|
+
}
|
|
301
|
+
targets := publicTypeScriptNames(
|
|
302
|
+
statement,
|
|
303
|
+
name,
|
|
304
|
+
exports,
|
|
305
|
+
false,
|
|
306
|
+
implicitlyExported,
|
|
307
|
+
)
|
|
308
|
+
if len(targets) == 0 {
|
|
309
|
+
continue
|
|
310
|
+
}
|
|
311
|
+
memberHidden := hidingTagFor(hidden, hiddenNames, name)
|
|
312
|
+
if memberHidden == "" {
|
|
313
|
+
addTypeScriptHost(supportedHosts, statement, "function")
|
|
314
|
+
}
|
|
315
|
+
for _, name := range targets {
|
|
316
|
+
addTypeScriptUnit(
|
|
317
|
+
inventory,
|
|
318
|
+
unitsByID,
|
|
319
|
+
statement,
|
|
320
|
+
"function",
|
|
321
|
+
qualifyTypeScriptName(prefix, name),
|
|
322
|
+
parentID,
|
|
323
|
+
memberHidden,
|
|
324
|
+
)
|
|
325
|
+
}
|
|
326
|
+
case shimast.KindVariableStatement:
|
|
327
|
+
if typeOnlyProjection {
|
|
328
|
+
continue
|
|
329
|
+
}
|
|
330
|
+
memberHidden := typeScriptHidingTag(file, statement, hidden)
|
|
331
|
+
for symbol := range collectTypeScriptVariables(
|
|
332
|
+
statement,
|
|
333
|
+
prefix,
|
|
334
|
+
parentID,
|
|
335
|
+
exports,
|
|
336
|
+
inventory,
|
|
337
|
+
supportedHosts,
|
|
338
|
+
unitsByID,
|
|
339
|
+
implicitlyExported,
|
|
340
|
+
memberHidden,
|
|
341
|
+
) {
|
|
342
|
+
if memberHidden != "" {
|
|
343
|
+
continue
|
|
344
|
+
}
|
|
345
|
+
// TypeScript attaches the leading JSDoc of
|
|
346
|
+
// a variable declaration to the statement wrapper.
|
|
347
|
+
addTypeScriptHost(supportedHosts, statement, symbol)
|
|
348
|
+
}
|
|
349
|
+
case shimast.KindClassDeclaration:
|
|
350
|
+
if typeOnlyProjection {
|
|
351
|
+
continue
|
|
352
|
+
}
|
|
353
|
+
name := declarationName(statement.Name())
|
|
354
|
+
memberHidden := hidingTagFor(hidden, hiddenNames, name)
|
|
355
|
+
for _, publicName := range publicTypeScriptNames(
|
|
356
|
+
statement,
|
|
357
|
+
name,
|
|
358
|
+
exports,
|
|
359
|
+
false,
|
|
360
|
+
implicitlyExported,
|
|
361
|
+
) {
|
|
362
|
+
collectClassCallables(
|
|
363
|
+
file,
|
|
364
|
+
statement,
|
|
365
|
+
qualifyTypeScriptName(prefix, publicName),
|
|
366
|
+
parentID,
|
|
367
|
+
inventory,
|
|
368
|
+
supportedHosts,
|
|
369
|
+
unitsByID,
|
|
370
|
+
memberHidden,
|
|
371
|
+
)
|
|
372
|
+
}
|
|
373
|
+
case shimast.KindModuleDeclaration:
|
|
374
|
+
name := declarationName(statement.Name())
|
|
375
|
+
targets := publicTypeScriptExports(
|
|
376
|
+
statement,
|
|
377
|
+
name,
|
|
378
|
+
exports,
|
|
379
|
+
true,
|
|
380
|
+
implicitlyExported,
|
|
381
|
+
)
|
|
382
|
+
if len(targets) == 0 {
|
|
383
|
+
continue
|
|
384
|
+
}
|
|
385
|
+
memberHidden := hidingTagFor(hidden, hiddenNames, name)
|
|
386
|
+
if memberHidden == "" {
|
|
387
|
+
addTypeScriptHost(supportedHosts, statement, "type")
|
|
388
|
+
}
|
|
389
|
+
// A namespace merged with a same-named function is that function's
|
|
390
|
+
// static side, not an independent container. `get.path` is a
|
|
391
|
+
// property of the `get` function value and `get.Output` is the type
|
|
392
|
+
// its own signature spells; neither is authored contract, so the
|
|
393
|
+
// merged namespace contributes its identity and nothing beneath it.
|
|
394
|
+
// Selecting those members is also what promoted the namespace to an
|
|
395
|
+
// addressable aggregate scope, where it collided with the function
|
|
396
|
+
// unit of the same name and left the accessor with no spelling that
|
|
397
|
+
// resolves.
|
|
398
|
+
if functionNames == nil {
|
|
399
|
+
functionNames = collectFunctionDeclarationNames(statements)
|
|
400
|
+
}
|
|
401
|
+
staticSide := functionNames[name]
|
|
402
|
+
for _, target := range targets {
|
|
403
|
+
identity := qualifyTypeScriptName(prefix, target.Public)
|
|
404
|
+
unit := addTypeScriptUnit(
|
|
405
|
+
inventory,
|
|
406
|
+
unitsByID,
|
|
407
|
+
statement,
|
|
408
|
+
"type",
|
|
409
|
+
identity,
|
|
410
|
+
parentID,
|
|
411
|
+
memberHidden,
|
|
412
|
+
)
|
|
413
|
+
if !staticSide {
|
|
414
|
+
collectTypeScriptModule(
|
|
415
|
+
file,
|
|
416
|
+
statement,
|
|
417
|
+
identity,
|
|
418
|
+
unit.ID,
|
|
419
|
+
inventory,
|
|
420
|
+
supportedHosts,
|
|
421
|
+
unitsByID,
|
|
422
|
+
ambientContext,
|
|
423
|
+
typeOnlyProjection || target.TypeOnly,
|
|
424
|
+
memberHidden,
|
|
425
|
+
)
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// collectFunctionDeclarationNames indexes the local names a statement list
|
|
433
|
+
// declares as functions, which is what a namespace in the same list merges with.
|
|
434
|
+
//
|
|
435
|
+
// A namespace merges only with a function, a class, or an enum in the same
|
|
436
|
+
// scope; a `const` or `let` of the same name is `TS2451`, measured against the
|
|
437
|
+
// pinned compiler. Of the three legal partners only a function also
|
|
438
|
+
// materializes a unit under the merged name — a class registers no unit of its
|
|
439
|
+
// own and the collector has no enum case — so the function form is the one
|
|
440
|
+
// shape where a namespace and the declaration it merges with are the same
|
|
441
|
+
// public entity spelled twice.
|
|
442
|
+
//
|
|
443
|
+
// The name alone decides, without consulting export modifiers, because
|
|
444
|
+
// TypeScript refuses a merged declaration whose halves disagree on export
|
|
445
|
+
// (`TS2395`, measured). The whole list is indexed rather than only the
|
|
446
|
+
// statements already collected, because merging does not depend on which
|
|
447
|
+
// declaration is written first.
|
|
448
|
+
func collectFunctionDeclarationNames(
|
|
449
|
+
statements *shimast.NodeList,
|
|
450
|
+
) map[string]bool {
|
|
451
|
+
names := map[string]bool{}
|
|
452
|
+
if statements == nil {
|
|
453
|
+
return names
|
|
454
|
+
}
|
|
455
|
+
for _, statement := range statements.Nodes {
|
|
456
|
+
if statement == nil ||
|
|
457
|
+
statement.Kind != shimast.KindFunctionDeclaration {
|
|
458
|
+
continue
|
|
459
|
+
}
|
|
460
|
+
if name := declarationName(statement.Name()); name != "" {
|
|
461
|
+
names[name] = true
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return names
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
func collectTypeScriptVariables(
|
|
468
|
+
statement *shimast.Node,
|
|
469
|
+
prefix []string,
|
|
470
|
+
parentID string,
|
|
471
|
+
exports map[string][]exportedName,
|
|
472
|
+
inventory *artifactInventory,
|
|
473
|
+
supportedHosts map[*shimast.Node]symbolSet,
|
|
474
|
+
unitsByID map[string]*evidenceUnit,
|
|
475
|
+
implicitlyExported bool,
|
|
476
|
+
hidden string,
|
|
477
|
+
) symbolSet {
|
|
478
|
+
variable := statement.AsVariableStatement()
|
|
479
|
+
if variable.DeclarationList == nil {
|
|
480
|
+
return nil
|
|
481
|
+
}
|
|
482
|
+
list := variable.DeclarationList.AsVariableDeclarationList()
|
|
483
|
+
if list.Declarations == nil {
|
|
484
|
+
return nil
|
|
485
|
+
}
|
|
486
|
+
found := symbolSet{}
|
|
487
|
+
for _, declaration := range list.Declarations.Nodes {
|
|
488
|
+
if declaration == nil {
|
|
489
|
+
continue
|
|
490
|
+
}
|
|
491
|
+
value := declaration.AsVariableDeclaration()
|
|
492
|
+
symbol := "property"
|
|
493
|
+
if !shimast.IsBindingPattern(declaration.Name()) &&
|
|
494
|
+
shimast.IsConst(declaration) &&
|
|
495
|
+
isFunctionValue(value.Initializer) {
|
|
496
|
+
symbol = "function"
|
|
497
|
+
}
|
|
498
|
+
for _, binding := range bindingIdentifierNodes(declaration.Name()) {
|
|
499
|
+
name := declarationName(binding)
|
|
500
|
+
targets := publicTypeScriptNames(
|
|
501
|
+
statement,
|
|
502
|
+
name,
|
|
503
|
+
exports,
|
|
504
|
+
false,
|
|
505
|
+
implicitlyExported,
|
|
506
|
+
)
|
|
507
|
+
if len(targets) == 0 {
|
|
508
|
+
continue
|
|
509
|
+
}
|
|
510
|
+
if hidden == "" {
|
|
511
|
+
addTypeScriptHost(supportedHosts, declaration, symbol)
|
|
512
|
+
}
|
|
513
|
+
for _, name := range targets {
|
|
514
|
+
unit := addTypeScriptUnit(
|
|
515
|
+
inventory,
|
|
516
|
+
unitsByID,
|
|
517
|
+
binding,
|
|
518
|
+
symbol,
|
|
519
|
+
qualifyTypeScriptName(prefix, name),
|
|
520
|
+
parentID,
|
|
521
|
+
hidden,
|
|
522
|
+
)
|
|
523
|
+
// The binding names the unit, but TypeScript attaches a
|
|
524
|
+
// variable's leading JSDoc to the statement wrapper, so that
|
|
525
|
+
// is where a citation for this unit actually lives.
|
|
526
|
+
inventory.recordUnitNode(unit.ID, statement)
|
|
527
|
+
}
|
|
528
|
+
found[symbol] = true
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return found
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
func collectClassCallables(
|
|
535
|
+
file *shimast.SourceFile,
|
|
536
|
+
statement *shimast.Node,
|
|
537
|
+
classIdentity []string,
|
|
538
|
+
parentID string,
|
|
539
|
+
inventory *artifactInventory,
|
|
540
|
+
supportedHosts map[*shimast.Node]symbolSet,
|
|
541
|
+
unitsByID map[string]*evidenceUnit,
|
|
542
|
+
hidden string,
|
|
543
|
+
) {
|
|
544
|
+
class := statement.AsClassDeclaration()
|
|
545
|
+
if class.Members == nil {
|
|
546
|
+
return
|
|
547
|
+
}
|
|
548
|
+
for _, member := range class.Members.Nodes {
|
|
549
|
+
if member == nil || !isPublicClassMember(member) {
|
|
550
|
+
continue
|
|
551
|
+
}
|
|
552
|
+
callable := false
|
|
553
|
+
switch member.Kind {
|
|
554
|
+
case shimast.KindMethodDeclaration:
|
|
555
|
+
callable = true
|
|
556
|
+
case shimast.KindPropertyDeclaration:
|
|
557
|
+
if member.ModifierFlags()&shimast.ModifierFlagsAccessor != 0 {
|
|
558
|
+
continue
|
|
559
|
+
}
|
|
560
|
+
property := member.AsPropertyDeclaration()
|
|
561
|
+
callable = isFunctionValue(property.Initializer) ||
|
|
562
|
+
isDirectFunctionType(property.Type)
|
|
563
|
+
}
|
|
564
|
+
if !callable {
|
|
565
|
+
continue
|
|
566
|
+
}
|
|
567
|
+
memberName := declarationName(member.Name())
|
|
568
|
+
if memberName == "" {
|
|
569
|
+
continue
|
|
570
|
+
}
|
|
571
|
+
identity := qualifyTypeScriptName(classIdentity, "prototype", memberName)
|
|
572
|
+
if shimast.GetCombinedModifierFlags(member)&shimast.ModifierFlagsStatic != 0 {
|
|
573
|
+
identity = qualifyTypeScriptName(classIdentity, memberName)
|
|
574
|
+
}
|
|
575
|
+
memberHidden := typeScriptHidingTag(file, member, hidden)
|
|
576
|
+
addTypeScriptUnit(
|
|
577
|
+
inventory,
|
|
578
|
+
unitsByID,
|
|
579
|
+
member,
|
|
580
|
+
"function",
|
|
581
|
+
identity,
|
|
582
|
+
parentID,
|
|
583
|
+
memberHidden,
|
|
584
|
+
)
|
|
585
|
+
if memberHidden == "" {
|
|
586
|
+
addTypeScriptHost(supportedHosts, member, "function")
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
func isPublicClassMember(node *shimast.Node) bool {
|
|
592
|
+
flags := shimast.GetCombinedModifierFlags(node)
|
|
593
|
+
return flags&shimast.ModifierFlagsPrivate == 0 &&
|
|
594
|
+
flags&shimast.ModifierFlagsProtected == 0
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
func isFunctionValue(node *shimast.Node) bool {
|
|
598
|
+
for node != nil {
|
|
599
|
+
switch node.Kind {
|
|
600
|
+
case shimast.KindArrowFunction, shimast.KindFunctionExpression:
|
|
601
|
+
return true
|
|
602
|
+
case shimast.KindParenthesizedExpression,
|
|
603
|
+
shimast.KindAsExpression,
|
|
604
|
+
shimast.KindSatisfiesExpression,
|
|
605
|
+
shimast.KindNonNullExpression,
|
|
606
|
+
shimast.KindTypeAssertionExpression:
|
|
607
|
+
node = node.Expression()
|
|
608
|
+
default:
|
|
609
|
+
return false
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
return false
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
func isDirectFunctionType(node *shimast.Node) bool {
|
|
616
|
+
for node != nil && node.Kind == shimast.KindParenthesizedType {
|
|
617
|
+
parenthesized := node.AsParenthesizedTypeNode()
|
|
618
|
+
if parenthesized == nil {
|
|
619
|
+
return false
|
|
620
|
+
}
|
|
621
|
+
node = parenthesized.Type
|
|
622
|
+
}
|
|
623
|
+
return node != nil && node.Kind == shimast.KindFunctionType
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
func collectTypeScriptModule(
|
|
627
|
+
file *shimast.SourceFile,
|
|
628
|
+
node *shimast.Node,
|
|
629
|
+
qualified []string,
|
|
630
|
+
parentID string,
|
|
631
|
+
inventory *artifactInventory,
|
|
632
|
+
supportedHosts map[*shimast.Node]symbolSet,
|
|
633
|
+
unitsByID map[string]*evidenceUnit,
|
|
634
|
+
ambientContext bool,
|
|
635
|
+
typeOnlyProjection bool,
|
|
636
|
+
hidden string,
|
|
637
|
+
) {
|
|
638
|
+
if node == nil || node.Kind != shimast.KindModuleDeclaration {
|
|
639
|
+
return
|
|
640
|
+
}
|
|
641
|
+
module := node.AsModuleDeclaration()
|
|
642
|
+
if module.Body == nil {
|
|
643
|
+
return
|
|
644
|
+
}
|
|
645
|
+
switch module.Body.Kind {
|
|
646
|
+
case shimast.KindModuleBlock:
|
|
647
|
+
moduleAmbient := ambientContext ||
|
|
648
|
+
shimast.GetCombinedModifierFlags(node)&shimast.ModifierFlagsAmbient != 0
|
|
649
|
+
collectTypeScriptStatements(
|
|
650
|
+
file,
|
|
651
|
+
module.Body.AsModuleBlock().Statements,
|
|
652
|
+
qualified,
|
|
653
|
+
parentID,
|
|
654
|
+
inventory,
|
|
655
|
+
supportedHosts,
|
|
656
|
+
unitsByID,
|
|
657
|
+
moduleAmbient,
|
|
658
|
+
moduleAmbient,
|
|
659
|
+
typeOnlyProjection,
|
|
660
|
+
hidden,
|
|
661
|
+
)
|
|
662
|
+
case shimast.KindModuleDeclaration:
|
|
663
|
+
// `export namespace Outer.Inner {}` is represented as nested module
|
|
664
|
+
// declarations; the inner declaration inherits the outer export.
|
|
665
|
+
name := declarationName(module.Body.Name())
|
|
666
|
+
if name != "" {
|
|
667
|
+
identity := qualifyTypeScriptName(qualified, name)
|
|
668
|
+
innerHidden := typeScriptHidingTag(file, module.Body, hidden)
|
|
669
|
+
if innerHidden == "" {
|
|
670
|
+
addTypeScriptHost(supportedHosts, module.Body, "type")
|
|
671
|
+
}
|
|
672
|
+
unit := addTypeScriptUnit(
|
|
673
|
+
inventory,
|
|
674
|
+
unitsByID,
|
|
675
|
+
module.Body,
|
|
676
|
+
"type",
|
|
677
|
+
identity,
|
|
678
|
+
parentID,
|
|
679
|
+
innerHidden,
|
|
680
|
+
)
|
|
681
|
+
collectTypeScriptModule(
|
|
682
|
+
file,
|
|
683
|
+
module.Body,
|
|
684
|
+
identity,
|
|
685
|
+
unit.ID,
|
|
686
|
+
inventory,
|
|
687
|
+
supportedHosts,
|
|
688
|
+
unitsByID,
|
|
689
|
+
ambientContext ||
|
|
690
|
+
shimast.GetCombinedModifierFlags(node)&shimast.ModifierFlagsAmbient != 0,
|
|
691
|
+
typeOnlyProjection,
|
|
692
|
+
innerHidden,
|
|
693
|
+
)
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
func collectPropertyMembers(
|
|
699
|
+
file *shimast.SourceFile,
|
|
700
|
+
members *shimast.NodeList,
|
|
701
|
+
owner []string,
|
|
702
|
+
parentID string,
|
|
703
|
+
inventory *artifactInventory,
|
|
704
|
+
supportedHosts map[*shimast.Node]symbolSet,
|
|
705
|
+
unitsByID map[string]*evidenceUnit,
|
|
706
|
+
hidden string,
|
|
707
|
+
) {
|
|
708
|
+
if members == nil {
|
|
709
|
+
return
|
|
710
|
+
}
|
|
711
|
+
for _, member := range members.Nodes {
|
|
712
|
+
if member == nil || member.Kind != shimast.KindPropertySignature {
|
|
713
|
+
continue
|
|
714
|
+
}
|
|
715
|
+
name := declarationName(member.Name())
|
|
716
|
+
if name == "" {
|
|
717
|
+
continue
|
|
718
|
+
}
|
|
719
|
+
identity := qualifyTypeScriptName(owner, name)
|
|
720
|
+
memberHidden := typeScriptHidingTag(file, member, hidden)
|
|
721
|
+
addTypeScriptUnit(
|
|
722
|
+
inventory,
|
|
723
|
+
unitsByID,
|
|
724
|
+
member,
|
|
725
|
+
"property",
|
|
726
|
+
identity,
|
|
727
|
+
parentID,
|
|
728
|
+
memberHidden,
|
|
729
|
+
)
|
|
730
|
+
if memberHidden == "" {
|
|
731
|
+
addTypeScriptHost(supportedHosts, member, "property")
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// collectHiddenDeclarationNames indexes the local names a statement list
|
|
737
|
+
// withdraws from the public surface, by the tag that withdrew each.
|
|
738
|
+
//
|
|
739
|
+
// The index is over names rather than over nodes because declaration merging
|
|
740
|
+
// makes one name several declarations. `interface I` beside `namespace I` is
|
|
741
|
+
// one public identity and one unit, so a tag on either half withdraws the
|
|
742
|
+
// identity — and which half carries it is a matter of where the author wrote
|
|
743
|
+
// the comment. Reading only the node in hand would leave the identity
|
|
744
|
+
// withdrawn while its members stayed selected, depending on source order.
|
|
745
|
+
func collectHiddenDeclarationNames(
|
|
746
|
+
file *shimast.SourceFile,
|
|
747
|
+
statements *shimast.NodeList,
|
|
748
|
+
) map[string]string {
|
|
749
|
+
if file == nil || statements == nil {
|
|
750
|
+
return nil
|
|
751
|
+
}
|
|
752
|
+
var names map[string]string
|
|
753
|
+
for _, statement := range statements.Nodes {
|
|
754
|
+
if statement == nil {
|
|
755
|
+
continue
|
|
756
|
+
}
|
|
757
|
+
name := declarationName(statement.Name())
|
|
758
|
+
if name == "" {
|
|
759
|
+
continue
|
|
760
|
+
}
|
|
761
|
+
tag := typeScriptHidingTag(file, statement, "")
|
|
762
|
+
if tag == "" {
|
|
763
|
+
continue
|
|
764
|
+
}
|
|
765
|
+
if names == nil {
|
|
766
|
+
names = map[string]string{}
|
|
767
|
+
}
|
|
768
|
+
if names[name] == "" {
|
|
769
|
+
names[name] = tag
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return names
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// hidingTagFor answers for one local name, preferring an inherited tag.
|
|
776
|
+
func hidingTagFor(
|
|
777
|
+
inherited string,
|
|
778
|
+
names map[string]string,
|
|
779
|
+
local string,
|
|
780
|
+
) string {
|
|
781
|
+
if inherited != "" {
|
|
782
|
+
return inherited
|
|
783
|
+
}
|
|
784
|
+
return names[local]
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// typeScriptHidingTag reports the documentation tag that withdraws a
|
|
788
|
+
// declaration from the public surface, inheriting an enclosing one.
|
|
789
|
+
//
|
|
790
|
+
// An inherited tag wins outright and the node's own blocks are not consulted:
|
|
791
|
+
// once an ancestor is out of the surface, nothing beneath it can opt back in,
|
|
792
|
+
// and the cause an author has to be told about is the outermost tag.
|
|
793
|
+
func typeScriptHidingTag(
|
|
794
|
+
file *shimast.SourceFile,
|
|
795
|
+
node *shimast.Node,
|
|
796
|
+
inherited string,
|
|
797
|
+
) string {
|
|
798
|
+
if inherited != "" {
|
|
799
|
+
return inherited
|
|
800
|
+
}
|
|
801
|
+
if file == nil || node == nil {
|
|
802
|
+
return ""
|
|
803
|
+
}
|
|
804
|
+
content := file.Text()
|
|
805
|
+
for _, doc := range node.JSDoc(file) {
|
|
806
|
+
if doc == nil ||
|
|
807
|
+
doc.Pos() < 0 ||
|
|
808
|
+
doc.End() > len(content) ||
|
|
809
|
+
doc.Pos() >= doc.End() {
|
|
810
|
+
continue
|
|
811
|
+
}
|
|
812
|
+
if tag := commentHidingTag(content[doc.Pos():doc.End()]); tag != "" {
|
|
813
|
+
return tag
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
return ""
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
func addTypeScriptUnit(
|
|
820
|
+
inventory *artifactInventory,
|
|
821
|
+
unitsByID map[string]*evidenceUnit,
|
|
822
|
+
node *shimast.Node,
|
|
823
|
+
symbol string,
|
|
824
|
+
identity []string,
|
|
825
|
+
parentID string,
|
|
826
|
+
hidden string,
|
|
827
|
+
) *evidenceUnit {
|
|
828
|
+
target := strings.Join(identity, ".")
|
|
829
|
+
address := inventory.Address
|
|
830
|
+
if address == "" {
|
|
831
|
+
address = inventory.Path
|
|
832
|
+
}
|
|
833
|
+
id := "typescript:" + address + ":" + symbol + ":" + encodeTypeScriptIdentity(identity)
|
|
834
|
+
// Recorded before the dedupe below, so a merged identity keeps every
|
|
835
|
+
// declaration that spells it. `interface I` beside `namespace I` is one
|
|
836
|
+
// unit and two nodes, and a rule asking where that unit's JSDoc may live
|
|
837
|
+
// has to see both.
|
|
838
|
+
inventory.recordUnitNode(id, node)
|
|
839
|
+
if unit := unitsByID[id]; unit != nil {
|
|
840
|
+
// A merged identity is one unit, so one declaration marking itself
|
|
841
|
+
// internal marks the identity. Both halves of `interface I` beside
|
|
842
|
+
// `namespace I` name the same public thing, and honoring only the tagged
|
|
843
|
+
// half would leave the identity half in and half out of the surface.
|
|
844
|
+
if hidden != "" && unit.Hidden == "" {
|
|
845
|
+
unit.Hidden = hidden
|
|
846
|
+
}
|
|
847
|
+
return unit
|
|
848
|
+
}
|
|
849
|
+
unit := &evidenceUnit{
|
|
850
|
+
ID: id,
|
|
851
|
+
ParentID: parentID,
|
|
852
|
+
Target: target,
|
|
853
|
+
Identity: append([]string{}, identity...),
|
|
854
|
+
Type: artifactTypeScript,
|
|
855
|
+
Symbol: symbol,
|
|
856
|
+
Path: inventory.Path,
|
|
857
|
+
Line: lineAtNode(inventory.Path, node),
|
|
858
|
+
Readable: "TypeScript " + symbol + " '" + target + "'",
|
|
859
|
+
Hidden: hidden,
|
|
860
|
+
}
|
|
861
|
+
unitsByID[id] = unit
|
|
862
|
+
inventory.Units = append(inventory.Units, unit)
|
|
863
|
+
return unit
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
func addTypeScriptHost(
|
|
867
|
+
hosts map[*shimast.Node]symbolSet,
|
|
868
|
+
node *shimast.Node,
|
|
869
|
+
symbol string,
|
|
870
|
+
) {
|
|
871
|
+
if node == nil {
|
|
872
|
+
return
|
|
873
|
+
}
|
|
874
|
+
if hosts[node] == nil {
|
|
875
|
+
hosts[node] = symbolSet{}
|
|
876
|
+
}
|
|
877
|
+
hosts[node][symbol] = true
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// lineAtNode stores a byte offset until declarations are scanned against the
|
|
881
|
+
// complete source text. A position inside the name is on the declaration
|
|
882
|
+
// itself, while both the parent and name full starts may include leading trivia.
|
|
883
|
+
func lineAtNode(_ string, node *shimast.Node) int {
|
|
884
|
+
if node == nil {
|
|
885
|
+
return 0
|
|
886
|
+
}
|
|
887
|
+
if name := node.Name(); name != nil && name.End() > 0 {
|
|
888
|
+
return name.End() - 1
|
|
889
|
+
}
|
|
890
|
+
return node.Pos()
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
func collectTypeScriptDeclarations(
|
|
894
|
+
file *shimast.SourceFile,
|
|
895
|
+
address string,
|
|
896
|
+
location string,
|
|
897
|
+
inventory *artifactInventory,
|
|
898
|
+
supportedHosts map[*shimast.Node]symbolSet,
|
|
899
|
+
) {
|
|
900
|
+
type docHost struct {
|
|
901
|
+
node *shimast.Node
|
|
902
|
+
hosts symbolSet
|
|
903
|
+
hostIDs map[string]bool
|
|
904
|
+
semanticHostIDs map[string]bool
|
|
905
|
+
}
|
|
906
|
+
semanticHostsByNode := map[*shimast.Node]map[string]bool{}
|
|
907
|
+
for unitID, nodes := range inventory.UnitNodes {
|
|
908
|
+
for _, node := range nodes {
|
|
909
|
+
if node == nil {
|
|
910
|
+
continue
|
|
911
|
+
}
|
|
912
|
+
if semanticHostsByNode[node] == nil {
|
|
913
|
+
semanticHostsByNode[node] = map[string]bool{}
|
|
914
|
+
}
|
|
915
|
+
semanticHostsByNode[node][unitID] = true
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
docs := map[string]docHost{}
|
|
919
|
+
walkTypeScriptNode(file.AsNode(), func(node *shimast.Node) {
|
|
920
|
+
for _, doc := range node.JSDoc(file) {
|
|
921
|
+
if doc == nil {
|
|
922
|
+
continue
|
|
923
|
+
}
|
|
924
|
+
key := decimal(doc.Pos()) + ":" + decimal(doc.End())
|
|
925
|
+
candidate := docHost{
|
|
926
|
+
node: doc,
|
|
927
|
+
hosts: supportedHosts[node],
|
|
928
|
+
semanticHostIDs: semanticHostsByNode[node],
|
|
929
|
+
}
|
|
930
|
+
if len(candidate.hosts) != 0 {
|
|
931
|
+
candidate.hostIDs = map[string]bool{
|
|
932
|
+
address + ":" + decimal(node.Pos()) + ":" + decimal(node.End()): true,
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
current, exists := docs[key]
|
|
936
|
+
if !exists {
|
|
937
|
+
docs[key] = candidate
|
|
938
|
+
continue
|
|
939
|
+
}
|
|
940
|
+
for symbol := range candidate.hosts {
|
|
941
|
+
if current.hosts == nil {
|
|
942
|
+
current.hosts = symbolSet{}
|
|
943
|
+
}
|
|
944
|
+
current.hosts[symbol] = true
|
|
945
|
+
}
|
|
946
|
+
for hostID := range candidate.hostIDs {
|
|
947
|
+
if current.hostIDs == nil {
|
|
948
|
+
current.hostIDs = map[string]bool{}
|
|
949
|
+
}
|
|
950
|
+
current.hostIDs[hostID] = true
|
|
951
|
+
}
|
|
952
|
+
for semanticHostID := range candidate.semanticHostIDs {
|
|
953
|
+
if current.semanticHostIDs == nil {
|
|
954
|
+
current.semanticHostIDs = map[string]bool{}
|
|
955
|
+
}
|
|
956
|
+
current.semanticHostIDs[semanticHostID] = true
|
|
957
|
+
}
|
|
958
|
+
docs[key] = current
|
|
959
|
+
}
|
|
960
|
+
})
|
|
961
|
+
keys := make([]string, 0, len(docs))
|
|
962
|
+
for key := range docs {
|
|
963
|
+
keys = append(keys, key)
|
|
964
|
+
}
|
|
965
|
+
sort.Slice(keys, func(left int, right int) bool {
|
|
966
|
+
leftNode := docs[keys[left]].node
|
|
967
|
+
rightNode := docs[keys[right]].node
|
|
968
|
+
if leftNode.Pos() != rightNode.Pos() {
|
|
969
|
+
return leftNode.Pos() < rightNode.Pos()
|
|
970
|
+
}
|
|
971
|
+
return leftNode.End() < rightNode.End()
|
|
972
|
+
})
|
|
973
|
+
content := file.Text()
|
|
974
|
+
sequence := 0
|
|
975
|
+
for _, key := range keys {
|
|
976
|
+
entry := docs[key]
|
|
977
|
+
if entry.node.Pos() < 0 || entry.node.End() > len(content) || entry.node.Pos() >= entry.node.End() {
|
|
978
|
+
continue
|
|
979
|
+
}
|
|
980
|
+
baseLine := lineAt(content, entry.node.Pos())
|
|
981
|
+
hostIDs := make([]string, 0, len(entry.hostIDs))
|
|
982
|
+
for hostID := range entry.hostIDs {
|
|
983
|
+
hostIDs = append(hostIDs, hostID)
|
|
984
|
+
}
|
|
985
|
+
sort.Strings(hostIDs)
|
|
986
|
+
hostID := strings.Join(hostIDs, "|")
|
|
987
|
+
semanticHostIDs := make([]string, 0, len(entry.semanticHostIDs))
|
|
988
|
+
for semanticHostID := range entry.semanticHostIDs {
|
|
989
|
+
semanticHostIDs = append(semanticHostIDs, semanticHostID)
|
|
990
|
+
}
|
|
991
|
+
sort.Strings(semanticHostIDs)
|
|
992
|
+
for _, parsed := range parseDeclarations(content[entry.node.Pos():entry.node.End()]) {
|
|
993
|
+
sequence++
|
|
994
|
+
inventory.Declarations = append(inventory.Declarations, &evidenceDeclaration{
|
|
995
|
+
ID: "typescript:" + address + ":" + decimal(baseLine+parsed.LineOffset) + ":" + decimal(sequence),
|
|
996
|
+
HostID: hostID,
|
|
997
|
+
SemanticHostIDs: semanticHostIDs,
|
|
998
|
+
Type: artifactTypeScript,
|
|
999
|
+
Tag: parsed.Tag,
|
|
1000
|
+
Target: parsed.Target,
|
|
1001
|
+
Reason: parsed.Reason,
|
|
1002
|
+
Hosts: entry.hosts,
|
|
1003
|
+
ExclusionCarrier: len(entry.hosts) != 0,
|
|
1004
|
+
Path: location,
|
|
1005
|
+
Line: baseLine + parsed.LineOffset,
|
|
1006
|
+
Sequence: sequence,
|
|
1007
|
+
})
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
for _, unit := range inventory.Units {
|
|
1011
|
+
// TypeScript AST positions are byte offsets; translate them only after
|
|
1012
|
+
// the complete source text is available.
|
|
1013
|
+
unit.Line = lineAt(content, unit.Line)
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
func walkTypeScriptNode(node *shimast.Node, visit func(*shimast.Node)) {
|
|
1018
|
+
if node == nil {
|
|
1019
|
+
return
|
|
1020
|
+
}
|
|
1021
|
+
visit(node)
|
|
1022
|
+
node.ForEachChild(func(child *shimast.Node) bool {
|
|
1023
|
+
walkTypeScriptNode(child, visit)
|
|
1024
|
+
return false
|
|
1025
|
+
})
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
type exportedName struct {
|
|
1029
|
+
Public string
|
|
1030
|
+
TypeOnly bool
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
func collectLocalExportNames(
|
|
1034
|
+
statements *shimast.NodeList,
|
|
1035
|
+
) map[string][]exportedName {
|
|
1036
|
+
exports := map[string][]exportedName{}
|
|
1037
|
+
if statements == nil {
|
|
1038
|
+
return exports
|
|
1039
|
+
}
|
|
1040
|
+
for _, statement := range statements.Nodes {
|
|
1041
|
+
if statement == nil || statement.Kind != shimast.KindExportDeclaration {
|
|
1042
|
+
continue
|
|
1043
|
+
}
|
|
1044
|
+
declaration := statement.AsExportDeclaration()
|
|
1045
|
+
if declaration == nil ||
|
|
1046
|
+
declaration.ModuleSpecifier != nil ||
|
|
1047
|
+
declaration.ExportClause == nil ||
|
|
1048
|
+
declaration.ExportClause.Kind != shimast.KindNamedExports {
|
|
1049
|
+
continue
|
|
1050
|
+
}
|
|
1051
|
+
named := declaration.ExportClause.AsNamedExports()
|
|
1052
|
+
if named == nil || named.Elements == nil {
|
|
1053
|
+
continue
|
|
1054
|
+
}
|
|
1055
|
+
for _, element := range named.Elements.Nodes {
|
|
1056
|
+
if element == nil || element.Kind != shimast.KindExportSpecifier {
|
|
1057
|
+
continue
|
|
1058
|
+
}
|
|
1059
|
+
specifier := element.AsExportSpecifier()
|
|
1060
|
+
if specifier == nil {
|
|
1061
|
+
continue
|
|
1062
|
+
}
|
|
1063
|
+
localNode := specifier.PropertyName
|
|
1064
|
+
if localNode == nil {
|
|
1065
|
+
localNode = specifier.Name()
|
|
1066
|
+
}
|
|
1067
|
+
local := declarationName(localNode)
|
|
1068
|
+
public := declarationName(specifier.Name())
|
|
1069
|
+
if local == "" || public == "" || public == "default" {
|
|
1070
|
+
continue
|
|
1071
|
+
}
|
|
1072
|
+
exports[local] = append(exports[local], exportedName{
|
|
1073
|
+
Public: public,
|
|
1074
|
+
TypeOnly: declaration.IsTypeOnly || specifier.IsTypeOnly,
|
|
1075
|
+
})
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
return exports
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
func publicTypeScriptNames(
|
|
1082
|
+
node *shimast.Node,
|
|
1083
|
+
local string,
|
|
1084
|
+
exports map[string][]exportedName,
|
|
1085
|
+
allowTypeOnly bool,
|
|
1086
|
+
implicitlyExported bool,
|
|
1087
|
+
) []string {
|
|
1088
|
+
projected := publicTypeScriptExports(
|
|
1089
|
+
node,
|
|
1090
|
+
local,
|
|
1091
|
+
exports,
|
|
1092
|
+
allowTypeOnly,
|
|
1093
|
+
implicitlyExported,
|
|
1094
|
+
)
|
|
1095
|
+
result := make([]string, 0, len(projected))
|
|
1096
|
+
for _, exported := range projected {
|
|
1097
|
+
result = append(result, exported.Public)
|
|
1098
|
+
}
|
|
1099
|
+
return result
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
func publicTypeScriptExports(
|
|
1103
|
+
node *shimast.Node,
|
|
1104
|
+
local string,
|
|
1105
|
+
exports map[string][]exportedName,
|
|
1106
|
+
allowTypeOnly bool,
|
|
1107
|
+
implicitlyExported bool,
|
|
1108
|
+
) []exportedName {
|
|
1109
|
+
if local == "" {
|
|
1110
|
+
return nil
|
|
1111
|
+
}
|
|
1112
|
+
names := map[string]exportedName{}
|
|
1113
|
+
if implicitlyExported || isSyntacticallyExported(node) {
|
|
1114
|
+
names[local] = exportedName{Public: local}
|
|
1115
|
+
}
|
|
1116
|
+
for _, exported := range exports[local] {
|
|
1117
|
+
if exported.TypeOnly && !allowTypeOnly {
|
|
1118
|
+
continue
|
|
1119
|
+
}
|
|
1120
|
+
current, exists := names[exported.Public]
|
|
1121
|
+
if !exists || current.TypeOnly && !exported.TypeOnly {
|
|
1122
|
+
names[exported.Public] = exported
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
result := make([]string, 0, len(names))
|
|
1126
|
+
for name := range names {
|
|
1127
|
+
result = append(result, name)
|
|
1128
|
+
}
|
|
1129
|
+
sort.Strings(result)
|
|
1130
|
+
projected := make([]exportedName, 0, len(result))
|
|
1131
|
+
for _, name := range result {
|
|
1132
|
+
projected = append(projected, names[name])
|
|
1133
|
+
}
|
|
1134
|
+
return projected
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
func isSyntacticallyExported(node *shimast.Node) bool {
|
|
1138
|
+
return node != nil && shimast.GetCombinedModifierFlags(node)&shimast.ModifierFlagsExport != 0
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
func declarationName(node *shimast.Node) string {
|
|
1142
|
+
if node == nil {
|
|
1143
|
+
return ""
|
|
1144
|
+
}
|
|
1145
|
+
switch node.Kind {
|
|
1146
|
+
case shimast.KindIdentifier,
|
|
1147
|
+
shimast.KindStringLiteral,
|
|
1148
|
+
shimast.KindNumericLiteral:
|
|
1149
|
+
name := node.Text()
|
|
1150
|
+
if containsWhitespace(name) {
|
|
1151
|
+
return ""
|
|
1152
|
+
}
|
|
1153
|
+
return name
|
|
1154
|
+
default:
|
|
1155
|
+
return ""
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
func bindingIdentifierNodes(node *shimast.Node) []*shimast.Node {
|
|
1160
|
+
if node == nil {
|
|
1161
|
+
return nil
|
|
1162
|
+
}
|
|
1163
|
+
if declarationName(node) != "" {
|
|
1164
|
+
return []*shimast.Node{node}
|
|
1165
|
+
}
|
|
1166
|
+
if !shimast.IsBindingPattern(node) {
|
|
1167
|
+
return nil
|
|
1168
|
+
}
|
|
1169
|
+
pattern := node.AsBindingPattern()
|
|
1170
|
+
if pattern == nil || pattern.Elements == nil {
|
|
1171
|
+
return nil
|
|
1172
|
+
}
|
|
1173
|
+
nodes := []*shimast.Node{}
|
|
1174
|
+
for _, element := range pattern.Elements.Nodes {
|
|
1175
|
+
if element == nil || element.Kind != shimast.KindBindingElement {
|
|
1176
|
+
continue
|
|
1177
|
+
}
|
|
1178
|
+
nodes = append(nodes, bindingIdentifierNodes(element.Name())...)
|
|
1179
|
+
}
|
|
1180
|
+
return nodes
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
func qualifyTypeScriptName(prefix []string, names ...string) []string {
|
|
1184
|
+
qualified := make([]string, 0, len(prefix)+len(names))
|
|
1185
|
+
qualified = append(qualified, prefix...)
|
|
1186
|
+
qualified = append(qualified, names...)
|
|
1187
|
+
return qualified
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
func encodeTypeScriptIdentity(identity []string) string {
|
|
1191
|
+
var builder strings.Builder
|
|
1192
|
+
for _, segment := range identity {
|
|
1193
|
+
builder.WriteString(decimal(len(segment)))
|
|
1194
|
+
builder.WriteByte(':')
|
|
1195
|
+
builder.WriteString(segment)
|
|
1196
|
+
builder.WriteByte(';')
|
|
1197
|
+
}
|
|
1198
|
+
return builder.String()
|
|
1199
|
+
}
|