@ttsc/evidence 0.28.6 → 0.30.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/lib/structures/ITtscEvidenceGraphClaimBase.d.ts +7 -0
- package/lib/structures/ITtscEvidenceGraphReferenceBase.d.ts +7 -0
- package/lib/structures/ITtscEvidenceGraphTypeScriptReference.d.ts +18 -6
- package/lib/typings/TtscEvidenceGraphTypeScriptSymbol.d.ts +11 -12
- package/native/config.go +43 -47
- package/native/declaration.go +50 -1
- package/native/default_exports.go +62 -0
- package/native/export_resolution.go +275 -0
- package/native/exports.go +62 -144
- package/native/file_link.go +370 -0
- package/native/file_link_diagnostics.go +160 -0
- package/native/fingerprint.go +33 -4
- package/native/graph.go +119 -79
- package/native/imports.go +21 -8
- package/native/inputs.go +13 -4
- package/native/loader.go +170 -23
- package/native/markdown.go +17 -11
- package/native/model.go +16 -4
- package/native/prisma.go +33 -26
- package/native/prisma_cache.go +16 -4
- package/native/rooted_typescript.go +116 -0
- package/native/severity.go +156 -0
- package/native/swagger.go +23 -16
- package/native/typescript.go +33 -14
- package/native/typescript_cache.go +5 -4
- package/native/unreadable.go +5 -6
- package/package.json +6 -6
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TtscLintSeverity } from "@ttsc/lint";
|
|
1
2
|
import type { ITtscEvidenceGraphReference } from "./ITtscEvidenceGraphReference";
|
|
2
3
|
/**
|
|
3
4
|
* What every claim declares: which artifact kind makes the claim, where its
|
|
@@ -21,6 +22,12 @@ export interface ITtscEvidenceGraphClaimBase<Type extends string> {
|
|
|
21
22
|
* configuration entries.
|
|
22
23
|
*/
|
|
23
24
|
name?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Diagnostic level for this claim. Omit it or use `undefined` to inherit the
|
|
27
|
+
* outer `evidence/graph` rule level. A reference may override this level
|
|
28
|
+
* again. `"off"` disables the entire claim, including its references.
|
|
29
|
+
*/
|
|
30
|
+
severity?: TtscLintSeverity | undefined;
|
|
24
31
|
/**
|
|
25
32
|
* Excludes this claim from graph loading and evaluation.
|
|
26
33
|
*
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TtscLintSeverity } from "@ttsc/lint";
|
|
1
2
|
/**
|
|
2
3
|
* What every evidence population declares: which artifact kind it materializes,
|
|
3
4
|
* and how strictly the owning claim must acknowledge it.
|
|
@@ -25,6 +26,12 @@
|
|
|
25
26
|
export interface ITtscEvidenceGraphReferenceBase<Type extends string> {
|
|
26
27
|
/** Identifies the artifact kind this population materializes. */
|
|
27
28
|
type: Type;
|
|
29
|
+
/**
|
|
30
|
+
* Diagnostic level for this reference's obligation. Omit it or use
|
|
31
|
+
* `undefined` to inherit the claim level, then the outer `evidence/graph`
|
|
32
|
+
* rule level. `"off"` disables this reference's population and obligation.
|
|
33
|
+
*/
|
|
34
|
+
severity?: TtscLintSeverity | undefined;
|
|
28
35
|
/**
|
|
29
36
|
* Whether this reference refuses `@evidenceExclude` as an acknowledgement.
|
|
30
37
|
*
|
|
@@ -13,13 +13,25 @@ import type { ITtscEvidenceGraphReferenceBase } from "./ITtscEvidenceGraphRefere
|
|
|
13
13
|
* contracts deserve documentary proof.
|
|
14
14
|
*/
|
|
15
15
|
export interface ITtscEvidenceGraphTypeScriptReference extends ITtscEvidenceGraphReferenceBase<"typescript"> {
|
|
16
|
+
/**
|
|
17
|
+
* Explicit directory whose TypeScript exports are read from disk.
|
|
18
|
+
*
|
|
19
|
+
* Resolves relative to the lint project's root, or accepts an absolute path.
|
|
20
|
+
* Files patterns select entry modules relative to this directory. Their
|
|
21
|
+
* re-exports must remain inside it. Active Program sources take precedence
|
|
22
|
+
* over disk snapshots. The directory is watched for changes and new files.
|
|
23
|
+
*
|
|
24
|
+
* Cannot combine with package. Omit both to select only the active Program.
|
|
25
|
+
* File-qualified citation paths remain relative to the citing file.
|
|
26
|
+
*/
|
|
27
|
+
root?: string;
|
|
16
28
|
/**
|
|
17
29
|
* Installed package whose declarations form this population.
|
|
18
30
|
*
|
|
19
|
-
* Omit it to select the active project
|
|
20
|
-
* against the package root instead of the
|
|
21
|
-
* a consumer thinks of the package rather
|
|
22
|
-
* location.
|
|
31
|
+
* Omit it to select the active project or the explicit {@link root}. When
|
|
32
|
+
* present, {@link files} resolves against the package root instead of the
|
|
33
|
+
* project root, so the globs read as a consumer thinks of the package rather
|
|
34
|
+
* than carrying its `node_modules` location.
|
|
23
35
|
*
|
|
24
36
|
* A package population is read from disk rather than from the `ttsc` program.
|
|
25
37
|
* That is the point: a symbol nothing imports is absent from the program by
|
|
@@ -39,8 +51,8 @@ export interface ITtscEvidenceGraphTypeScriptReference extends ITtscEvidenceGrap
|
|
|
39
51
|
* `ttsc` project. A matching file outside the project's `tsconfig` program is
|
|
40
52
|
* not available to the rule and does not count as a match.
|
|
41
53
|
*
|
|
42
|
-
*
|
|
43
|
-
* files
|
|
54
|
+
* With {@link root} or {@link package}, these are relative to that directory or
|
|
55
|
+
* package and entry files can be read from disk outside the Program.
|
|
44
56
|
*
|
|
45
57
|
* These are globs, not regular expressions. `*` matches within one path
|
|
46
58
|
* segment, `**` crosses any number of path segments, and `?` matches one
|
|
@@ -58,8 +58,9 @@
|
|
|
58
58
|
* prepend their namespace, such as `Orders.create`. A namespace itself uses its
|
|
59
59
|
* qualified name, such as `Orders` or `Outer.Inner`. A local declaration
|
|
60
60
|
* exposed as `export { Local as Public }` therefore uses `Public`. A named
|
|
61
|
-
* default declaration
|
|
62
|
-
*
|
|
61
|
+
* default declaration or default alias is publicly addressed as `default`;
|
|
62
|
+
* anonymous default classes and functions also have that stable address. A
|
|
63
|
+
* default-only local binding is not a named export. Members of an ambient
|
|
63
64
|
* namespace are public without their own `export` modifier. A type-only alias
|
|
64
65
|
* exposes a namespace or a class, its public type-space descendants, and
|
|
65
66
|
* everything an object-shaped type alias, or an interface no class merges with,
|
|
@@ -79,17 +80,15 @@
|
|
|
79
80
|
* form. Static class members use `ClassName.member`; instance members use
|
|
80
81
|
* `ClassName.prototype.member`, and a constructor parameter property is an
|
|
81
82
|
* instance member addressed that way. Computed names are not selected, even
|
|
82
|
-
* when their expression is a literal.
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* qualification, the target is ambiguous.
|
|
83
|
+
* when their expression is a literal. File-qualified accessors use bracketed
|
|
84
|
+
* JSON strings for literal names, including spaces and dots. Existing inline
|
|
85
|
+
* links keep their dotted spelling and report ambiguous literal qualification.
|
|
86
86
|
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* does not rewrite literal symbol names.
|
|
87
|
+
* A code citation identifies its module through a file path or the citing
|
|
88
|
+
* TypeScript module's imports. A re-export whose declaration lives in another
|
|
89
|
+
* file does not create a second unit in the barrel file. TypeScript target
|
|
90
|
+
* characters are matched exactly; Markdown path-separator normalization does
|
|
91
|
+
* not rewrite literal symbol names.
|
|
93
92
|
*
|
|
94
93
|
* Every supported public declaration described here may carry
|
|
95
94
|
* `@evidenceExclude` when its file belongs to a claim, even when that claim's
|
package/native/config.go
CHANGED
|
@@ -8,6 +8,8 @@ import (
|
|
|
8
8
|
"path"
|
|
9
9
|
"sort"
|
|
10
10
|
"strings"
|
|
11
|
+
|
|
12
|
+
"github.com/samchon/ttsc/packages/lint/rule"
|
|
11
13
|
)
|
|
12
14
|
|
|
13
15
|
func decodeGraphConfig(raw json.RawMessage) (graphConfig, []string) {
|
|
@@ -54,7 +56,7 @@ func decodeClaim(raw json.RawMessage, index int) (claimSpec, []string) {
|
|
|
54
56
|
}
|
|
55
57
|
problems := rejectUnknownFields(
|
|
56
58
|
object,
|
|
57
|
-
[]string{"type", "name", "disabled", "root", "files", "evidenceExcludeCarriers", "symbol", "reference"},
|
|
59
|
+
[]string{"type", "name", "severity", "disabled", "root", "files", "evidenceExcludeCarriers", "symbol", "reference"},
|
|
58
60
|
graphRuleName,
|
|
59
61
|
path,
|
|
60
62
|
)
|
|
@@ -78,6 +80,8 @@ func decodeClaim(raw json.RawMessage, index int) (claimSpec, []string) {
|
|
|
78
80
|
problems = append(problems, "Invalid evidence/graph configuration at "+path+".disabled: expected a boolean.")
|
|
79
81
|
}
|
|
80
82
|
}
|
|
83
|
+
severity, severityProblems := decodeGraphSeverity(object["severity"], path+".severity")
|
|
84
|
+
problems = append(problems, severityProblems...)
|
|
81
85
|
root, rootProblems := decodeClaimRoot(object["root"], kind, path+".root")
|
|
82
86
|
problems = append(problems, rootProblems...)
|
|
83
87
|
files, fileProblems := decodeFiles(object["files"], path+".files")
|
|
@@ -98,6 +102,7 @@ func decodeClaim(raw json.RawMessage, index int) (claimSpec, []string) {
|
|
|
98
102
|
}
|
|
99
103
|
return claimSpec{
|
|
100
104
|
Index: index,
|
|
105
|
+
Severity: severity,
|
|
101
106
|
Type: kind,
|
|
102
107
|
Name: name,
|
|
103
108
|
Disabled: disabled,
|
|
@@ -109,16 +114,26 @@ func decodeClaim(raw json.RawMessage, index int) (claimSpec, []string) {
|
|
|
109
114
|
}, nil
|
|
110
115
|
}
|
|
111
116
|
|
|
112
|
-
// enabledGraphConfig removes
|
|
117
|
+
// enabledGraphConfig removes disabled claims and references after their public
|
|
113
118
|
// configuration has been validated. Keeping the filter separate from decoding
|
|
114
|
-
// preserves original
|
|
119
|
+
// preserves original indexes and prevents disabled entries from hiding a
|
|
115
120
|
// malformed shape.
|
|
116
121
|
func enabledGraphConfig(config graphConfig) graphConfig {
|
|
117
122
|
claims := make([]claimSpec, 0, len(config.Claims))
|
|
118
123
|
for _, claim := range config.Claims {
|
|
119
|
-
if claim.Disabled {
|
|
124
|
+
if claim.Disabled || claim.Severity != nil && *claim.Severity == rule.SeverityOff {
|
|
120
125
|
continue
|
|
121
126
|
}
|
|
127
|
+
references := make([]referenceSpec, 0, len(claim.References))
|
|
128
|
+
for _, reference := range claim.References {
|
|
129
|
+
if reference.Severity == nil || *reference.Severity != rule.SeverityOff {
|
|
130
|
+
references = append(references, reference)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if len(references) == 0 {
|
|
134
|
+
continue
|
|
135
|
+
}
|
|
136
|
+
claim.References = references
|
|
122
137
|
claims = append(claims, claim)
|
|
123
138
|
}
|
|
124
139
|
config.Claims = claims
|
|
@@ -183,6 +198,7 @@ func decodeReference(
|
|
|
183
198
|
object,
|
|
184
199
|
[]string{
|
|
185
200
|
"type",
|
|
201
|
+
"severity",
|
|
186
202
|
"noEvidenceExclude",
|
|
187
203
|
"uniqueEvidence",
|
|
188
204
|
"singleEvidencePerSymbol",
|
|
@@ -204,6 +220,8 @@ func decodeReference(
|
|
|
204
220
|
if problem := rejectForeignTypeScriptReference(claimKind, kind, path); problem != "" {
|
|
205
221
|
problems = append(problems, problem)
|
|
206
222
|
}
|
|
223
|
+
severity, severityProblems := decodeGraphSeverity(object["severity"], path+".severity")
|
|
224
|
+
problems = append(problems, severityProblems...)
|
|
207
225
|
root, rootProblems := decodeRoot(object["root"], kind, path+".root")
|
|
208
226
|
problems = append(problems, rootProblems...)
|
|
209
227
|
policy, policyProblems := decodeReferencePolicy(object, kind, path)
|
|
@@ -265,14 +283,16 @@ func decodeReference(
|
|
|
265
283
|
return referenceSpec{}, problems
|
|
266
284
|
}
|
|
267
285
|
return referenceSpec{
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
286
|
+
Severity: severity,
|
|
287
|
+
Index: index,
|
|
288
|
+
Type: kind,
|
|
289
|
+
Policy: policy,
|
|
290
|
+
Root: root,
|
|
291
|
+
Rooted: kind == artifactTypeScript && object["root"] != nil,
|
|
292
|
+
Files: files,
|
|
293
|
+
Source: source,
|
|
294
|
+
Package: packageName,
|
|
295
|
+
Symbols: symbols,
|
|
276
296
|
}, nil
|
|
277
297
|
}
|
|
278
298
|
|
|
@@ -415,24 +435,8 @@ func rejectChecklistConflicts(
|
|
|
415
435
|
return problems
|
|
416
436
|
}
|
|
417
437
|
|
|
418
|
-
//
|
|
419
|
-
//
|
|
420
|
-
//
|
|
421
|
-
// A symbol is cited through an inline link, and that link resolves in the
|
|
422
|
-
// citing module's import scope — which only a TypeScript file has. Every other
|
|
423
|
-
// claim would have to fall back to matching the bare name against one
|
|
424
|
-
// repository-wide table, and that makes symbol-name uniqueness across the whole
|
|
425
|
-
// repository load-bearing: two modules exporting `IPage` make the citation
|
|
426
|
-
// impossible, and the only repair such a diagnostic can offer is renaming
|
|
427
|
-
// production code to suit a lint rule.
|
|
428
|
-
//
|
|
429
|
-
// The check is here rather than at resolution because a configuration error
|
|
430
|
-
// belongs where the configuration is read. Reported once per reference, before
|
|
431
|
-
// any file is opened, instead of once per citation that later fails to resolve.
|
|
432
|
-
//
|
|
433
|
-
// What this gives up is the decision it reverses: documentation can no longer
|
|
434
|
-
// cite code, and the inverse obligation is not the same one. Do not restore the fallback
|
|
435
|
-
// without restoring that record.
|
|
438
|
+
// Code citations require an import scope or the Markdown/TypeScript file-link
|
|
439
|
+
// grammar. Bare repository-wide symbol lookup remains unavailable.
|
|
436
440
|
func rejectForeignTypeScriptReference(
|
|
437
441
|
claimKind artifactKind,
|
|
438
442
|
referenceKind artifactKind,
|
|
@@ -440,13 +444,13 @@ func rejectForeignTypeScriptReference(
|
|
|
440
444
|
) string {
|
|
441
445
|
if referenceKind != artifactTypeScript ||
|
|
442
446
|
claimKind == artifactTypeScript ||
|
|
447
|
+
claimKind == artifactMarkdown ||
|
|
443
448
|
claimKind == "" {
|
|
444
449
|
return ""
|
|
445
450
|
}
|
|
446
451
|
return "Invalid evidence/graph configuration at " + path +
|
|
447
|
-
".type:
|
|
448
|
-
|
|
449
|
-
" comment has none. Invert the obligation so the code cites this artifact, or move the citation into TypeScript."
|
|
452
|
+
".type: TypeScript evidence requires a Markdown or TypeScript claim; a " + string(claimKind) +
|
|
453
|
+
" claim does not support file-qualified code citations. Move the citation to a supported claim."
|
|
450
454
|
}
|
|
451
455
|
|
|
452
456
|
// decodeTypeScriptReference reads the two ways a TypeScript population is
|
|
@@ -463,6 +467,9 @@ func decodeTypeScriptReference(
|
|
|
463
467
|
problems := []string{}
|
|
464
468
|
reference := referenceSpec{}
|
|
465
469
|
if raw, exists := object["package"]; exists {
|
|
470
|
+
if _, rooted := object["root"]; rooted {
|
|
471
|
+
problems = append(problems, "Invalid evidence/graph configuration at "+path+": 'root' and 'package' select different TypeScript populations and cannot be combined. Choose one base and its 'files' patterns.")
|
|
472
|
+
}
|
|
466
473
|
var value string
|
|
467
474
|
if err := json.Unmarshal(raw, &value); err != nil {
|
|
468
475
|
problems = append(problems, "Invalid evidence/graph configuration at "+path+".package: expected an installed package name.")
|
|
@@ -569,15 +576,8 @@ func decodeClaimRoot(
|
|
|
569
576
|
return decodePopulationRoot(raw, configPath)
|
|
570
577
|
}
|
|
571
578
|
|
|
572
|
-
// decodeRoot reads
|
|
573
|
-
//
|
|
574
|
-
//
|
|
575
|
-
// The property is refused on the two artifact kinds that cannot use it, and
|
|
576
|
-
// each refusal names the channel that artifact does have. A TypeScript
|
|
577
|
-
// population is materialized from the ttsc Program, so a directory outside the
|
|
578
|
-
// project contains no file it could ever reach — `package` is the escape there,
|
|
579
|
-
// and it already exists. A Swagger reference names one exact document, so the
|
|
580
|
-
// location belongs in `file`, where it is visible without a second property.
|
|
579
|
+
// decodeRoot reads a population directory. A TypeScript reference uses its
|
|
580
|
+
// presence to opt into disk loading; Swagger still owns one exact file.
|
|
581
581
|
func decodeRoot(
|
|
582
582
|
raw json.RawMessage,
|
|
583
583
|
kind artifactKind,
|
|
@@ -587,11 +587,7 @@ func decodeRoot(
|
|
|
587
587
|
return "", nil
|
|
588
588
|
}
|
|
589
589
|
switch kind {
|
|
590
|
-
case artifactMarkdown, artifactPrisma:
|
|
591
|
-
case artifactTypeScript:
|
|
592
|
-
return "", []string{
|
|
593
|
-
"Invalid evidence/graph configuration at " + configPath + ": a TypeScript reference selects the active ttsc program with 'files', or an installed package with 'package'; only a TypeScript claim accepts 'root'.",
|
|
594
|
-
}
|
|
590
|
+
case artifactMarkdown, artifactPrisma, artifactTypeScript:
|
|
595
591
|
case artifactSwagger:
|
|
596
592
|
return "", []string{
|
|
597
593
|
"Invalid evidence/graph configuration at " + configPath + ": a Swagger reference owns one exact document; write the ancestor-relative or absolute location in 'file' instead.",
|
package/native/declaration.go
CHANGED
|
@@ -35,10 +35,14 @@ func commentHidingTag(comment string) string {
|
|
|
35
35
|
comment = strings.TrimPrefix(comment, "/**")
|
|
36
36
|
comment = strings.TrimPrefix(comment, "/*")
|
|
37
37
|
comment = strings.TrimSuffix(comment, "*/")
|
|
38
|
+
fence := commentFence{}
|
|
38
39
|
for _, rawLine := range strings.Split(comment, "\n") {
|
|
39
40
|
line := strings.TrimSpace(rawLine)
|
|
40
41
|
line = strings.TrimSpace(strings.TrimPrefix(line, "*"))
|
|
41
42
|
line = strings.TrimSpace(strings.TrimPrefix(line, "///"))
|
|
43
|
+
if fence.consume(line) {
|
|
44
|
+
continue
|
|
45
|
+
}
|
|
42
46
|
for _, tag := range hiddenDeclarationTags {
|
|
43
47
|
if line != tag && !strings.HasPrefix(line, tag+" ") {
|
|
44
48
|
continue
|
|
@@ -75,6 +79,7 @@ func parseCommentDeclarations(
|
|
|
75
79
|
lineOffset int
|
|
76
80
|
}
|
|
77
81
|
var pending *pendingDeclaration
|
|
82
|
+
fence := commentFence{}
|
|
78
83
|
parsed := []parsedDeclaration{}
|
|
79
84
|
flush := func() {
|
|
80
85
|
if pending == nil {
|
|
@@ -92,6 +97,12 @@ func parseCommentDeclarations(
|
|
|
92
97
|
for index, rawLine := range lines {
|
|
93
98
|
line := strings.TrimSpace(rawLine)
|
|
94
99
|
line = strings.TrimSpace(strings.TrimPrefix(line, "*"))
|
|
100
|
+
if fence.consume(line) {
|
|
101
|
+
if pending != nil {
|
|
102
|
+
pending.body = append(pending.body, line)
|
|
103
|
+
}
|
|
104
|
+
continue
|
|
105
|
+
}
|
|
95
106
|
tag, body, found := declarationLine(line)
|
|
96
107
|
if found {
|
|
97
108
|
flush()
|
|
@@ -136,6 +147,7 @@ func declarationLine(line string) (tagKind, string, bool) {
|
|
|
136
147
|
}{
|
|
137
148
|
{marker: "@evidenceExclude", tag: tagExclude},
|
|
138
149
|
{marker: "@evidence", tag: tagEvidence},
|
|
150
|
+
{marker: "@link", tag: tagEvidence},
|
|
139
151
|
} {
|
|
140
152
|
if !strings.HasPrefix(line, candidate.marker) {
|
|
141
153
|
continue
|
|
@@ -144,7 +156,11 @@ func declarationLine(line string) (tagKind, string, bool) {
|
|
|
144
156
|
if remainder != "" && remainder[0] != ' ' && remainder[0] != '\t' {
|
|
145
157
|
continue
|
|
146
158
|
}
|
|
147
|
-
|
|
159
|
+
body := strings.TrimSpace(remainder)
|
|
160
|
+
if candidate.marker == "@link" {
|
|
161
|
+
body = fileLinkPrefix + body
|
|
162
|
+
}
|
|
163
|
+
return candidate.tag, body, true
|
|
148
164
|
}
|
|
149
165
|
return "", "", false
|
|
150
166
|
}
|
|
@@ -161,6 +177,9 @@ func splitDeclarationBody(body string) (string, string) {
|
|
|
161
177
|
if body == "" {
|
|
162
178
|
return "", ""
|
|
163
179
|
}
|
|
180
|
+
if target, reason, found := splitFileLinkBody(body); found {
|
|
181
|
+
return target, reason
|
|
182
|
+
}
|
|
164
183
|
if target, reason, found := splitInlineLinkBody(body); found {
|
|
165
184
|
return target, reason
|
|
166
185
|
}
|
|
@@ -221,6 +240,9 @@ func inlineLinkTarget(target string) string {
|
|
|
221
240
|
|
|
222
241
|
// displayTarget renders a target the way its author wrote it.
|
|
223
242
|
func displayTarget(target string) string {
|
|
243
|
+
if isFileLinkTarget(target) {
|
|
244
|
+
return strings.TrimPrefix(target, fileLinkPrefix)
|
|
245
|
+
}
|
|
224
246
|
if isInlineLinkTarget(target) {
|
|
225
247
|
return "{@link " + inlineLinkTarget(target) + "}"
|
|
226
248
|
}
|
|
@@ -315,6 +337,7 @@ func parseReviews(comment string) []parsedReview {
|
|
|
315
337
|
comment = strings.TrimSuffix(comment, "*/")
|
|
316
338
|
reviews := []parsedReview{}
|
|
317
339
|
var pending *parsedReview
|
|
340
|
+
fence := commentFence{}
|
|
318
341
|
var body []string
|
|
319
342
|
flush := func() {
|
|
320
343
|
if pending == nil {
|
|
@@ -333,6 +356,12 @@ func parseReviews(comment string) []parsedReview {
|
|
|
333
356
|
line := strings.TrimSpace(rawLine)
|
|
334
357
|
line = strings.TrimSpace(strings.TrimPrefix(line, "*"))
|
|
335
358
|
line = strings.TrimSpace(strings.TrimPrefix(line, "///"))
|
|
359
|
+
if fence.consume(line) {
|
|
360
|
+
if pending != nil {
|
|
361
|
+
body = append(body, line)
|
|
362
|
+
}
|
|
363
|
+
continue
|
|
364
|
+
}
|
|
336
365
|
if reviews, remainder, opened := reviewLine(line); opened {
|
|
337
366
|
flush()
|
|
338
367
|
pending = &parsedReview{
|
|
@@ -442,6 +471,26 @@ func containsWhitespace(value string) bool {
|
|
|
442
471
|
return false
|
|
443
472
|
}
|
|
444
473
|
|
|
474
|
+
// Fences inside documentation are examples, not declarations. Their text is
|
|
475
|
+
// still retained when it belongs to a pending reason or review description.
|
|
476
|
+
type commentFence struct {
|
|
477
|
+
marker rune
|
|
478
|
+
length int
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
func (fence *commentFence) consume(line string) bool {
|
|
482
|
+
marker, length, remainder, opened := markdownFence(line)
|
|
483
|
+
if opened {
|
|
484
|
+
if fence.marker == 0 {
|
|
485
|
+
fence.marker, fence.length = marker, length
|
|
486
|
+
} else if marker == fence.marker && length >= fence.length && strings.TrimSpace(remainder) == "" {
|
|
487
|
+
fence.marker, fence.length = 0, 0
|
|
488
|
+
}
|
|
489
|
+
return true
|
|
490
|
+
}
|
|
491
|
+
return fence.marker != 0
|
|
492
|
+
}
|
|
493
|
+
|
|
445
494
|
func lineAt(content string, offset int) int {
|
|
446
495
|
if offset < 0 {
|
|
447
496
|
return 1
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
package evidence
|
|
2
|
+
|
|
3
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
4
|
+
|
|
5
|
+
// Default aliases retain the named declaration's identity. Anonymous class
|
|
6
|
+
// and function declarations own the module's default identity directly.
|
|
7
|
+
func typeScriptDeclarationName(node *shimast.Node) string {
|
|
8
|
+
if node == nil {
|
|
9
|
+
return ""
|
|
10
|
+
}
|
|
11
|
+
if name := declarationName(node.Name()); name != "" {
|
|
12
|
+
return name
|
|
13
|
+
}
|
|
14
|
+
if isDefaultExported(node) {
|
|
15
|
+
return "default"
|
|
16
|
+
}
|
|
17
|
+
return ""
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
func collectDefaultExportBindings(statements *shimast.NodeList) map[string]exportedName {
|
|
21
|
+
bindings := map[string]exportedName{}
|
|
22
|
+
if statements == nil {
|
|
23
|
+
return bindings
|
|
24
|
+
}
|
|
25
|
+
for _, statement := range statements.Nodes {
|
|
26
|
+
if statement == nil {
|
|
27
|
+
continue
|
|
28
|
+
}
|
|
29
|
+
if statement.Kind == shimast.KindExportAssignment {
|
|
30
|
+
assignment := statement.AsExportAssignment()
|
|
31
|
+
expression := assignment.Expression
|
|
32
|
+
if !assignment.IsExportEquals && expression != nil && expression.Kind == shimast.KindIdentifier {
|
|
33
|
+
local := declarationName(expression)
|
|
34
|
+
bindings[local] = exportedName{Public: local}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if statement.Kind != shimast.KindExportDeclaration {
|
|
38
|
+
continue
|
|
39
|
+
}
|
|
40
|
+
declaration := statement.AsExportDeclaration()
|
|
41
|
+
if declaration.ModuleSpecifier != nil || declaration.ExportClause == nil || declaration.ExportClause.Kind != shimast.KindNamedExports {
|
|
42
|
+
continue
|
|
43
|
+
}
|
|
44
|
+
for _, element := range declaration.ExportClause.AsNamedExports().Elements.Nodes {
|
|
45
|
+
specifier := element.AsExportSpecifier()
|
|
46
|
+
if declarationName(specifier.Name()) != "default" {
|
|
47
|
+
continue
|
|
48
|
+
}
|
|
49
|
+
local := specifier.PropertyName
|
|
50
|
+
if local == nil {
|
|
51
|
+
local = specifier.Name()
|
|
52
|
+
}
|
|
53
|
+
name := declarationName(local)
|
|
54
|
+
typeOnly := declaration.IsTypeOnly || specifier.IsTypeOnly
|
|
55
|
+
if previous, exists := bindings[name]; exists {
|
|
56
|
+
typeOnly = typeOnly && previous.TypeOnly
|
|
57
|
+
}
|
|
58
|
+
bindings[name] = exportedName{Public: name, TypeOnly: typeOnly}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return bindings
|
|
62
|
+
}
|