ttsc 0.26.2 → 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 +9 -0
- package/cmd/graphbench/main.go +26 -16
- package/cmd/graphdump/main.go +37 -12
- package/cmd/graphdump/run_declares_the_capability_its_output_carries_test.go +137 -0
- package/cmd/graphdump/run_reports_an_unknown_flag_without_exiting_the_process_test.go +56 -0
- package/cmd/graphdump/run_reports_an_unloadable_project_without_printing_a_dump_test.go +46 -0
- package/cmd/ttsc/api_transform.go +28 -3
- package/cmd/ttscgraph/auxiliary_identity_state_tracks_retargets_not_source_contents_test.go +85 -0
- package/cmd/ttscgraph/disk_realpath_other.go +16 -0
- package/cmd/ttscgraph/disk_realpath_windows.go +56 -0
- package/cmd/ttscgraph/dump.go +43 -11
- package/cmd/ttscgraph/run_dump_claims_artifacts_only_when_it_asked_test.go +135 -0
- package/cmd/ttscgraph/run_dump_declares_the_capability_its_output_carries_test.go +96 -0
- package/cmd/ttscgraph/serve.go +357 -60
- package/cmd/ttscgraph/serve_adopts_a_republished_artifact_set_test.go +348 -0
- package/cmd/ttscgraph/serve_projects_an_artifact_no_source_owns_test.go +90 -0
- package/cmd/ttscgraph/serve_shards.go +31 -9
- package/driver/config_discovery.go +121 -0
- package/driver/emit_plugin.go +77 -11
- package/driver/graph.go +64 -11
- package/driver/input_observation.go +273 -0
- package/driver/input_observation_test.go +149 -0
- package/driver/plugins.go +506 -15
- package/driver/program.go +54 -1
- package/driver/resolution_candidates.go +13 -5
- package/driver/rewrite.go +45 -2
- package/driver/transform_dependencies.go +283 -0
- package/driver/transform_dependencies_declares_every_file_without_a_contributor_test.go +28 -0
- package/driver/transform_dependencies_drops_a_self_reported_dependency_test.go +21 -0
- package/driver/transform_dependencies_keys_a_reported_path_like_the_envelope_test.go +46 -0
- package/driver/transform_dependencies_requires_every_contributor_to_declare_test.go +38 -0
- package/driver/transform_dependencies_stays_silent_for_an_undeclared_contributor_test.go +23 -0
- package/driver/transform_dependencies_withdraws_completeness_after_an_unusable_dependency_test.go +37 -0
- package/internal/graph/artifacts.go +230 -0
- package/internal/graph/artifacts_index_what_a_plugin_published_test.go +154 -0
- package/internal/graph/artifacts_survive_a_partial_rebuild_test.go +89 -0
- package/internal/graph/build.go +35 -0
- package/internal/graph/build_releases_the_scratch_it_no_longer_reads_test.go +118 -0
- package/internal/graph/doc_refs_are_the_use_the_checker_already_counts_test.go +93 -0
- package/internal/graph/doc_refs_resolve_documentation_links_test.go +335 -0
- package/internal/graph/doc_tags.go +416 -0
- package/internal/graph/doc_tags_are_claimed_before_they_are_read_test.go +131 -0
- package/internal/graph/doc_tags_capture_every_unrecognized_tag_test.go +165 -0
- package/internal/graph/doc_tags_read_only_what_the_parser_attached_test.go +97 -0
- package/internal/graph/doc_tags_stop_at_the_external_boundary_test.go +59 -0
- package/internal/graph/doc_tags_survive_line_endings_and_repeated_blocks_test.go +146 -0
- package/internal/graph/dump.go +60 -13
- package/internal/graph/dump_checkout_paths_are_stable_test.go +2 -2
- package/internal/graph/dump_paths.go +2 -2
- package/internal/graph/dump_paths_are_portable_and_injective_test.go +1 -1
- package/internal/graph/edge_kinds_names_every_declared_kind_test.go +49 -0
- package/internal/graph/edges.go +1 -0
- package/internal/graph/graph.go +79 -4
- package/internal/graph/provenance.go +29 -2
- package/lib/TtscCompiler.d.ts +12 -11
- package/lib/TtscCompiler.js +44 -29
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/captureProcessOutput.js +2 -2
- package/lib/compiler/internal/captureProcessOutput.js.map +1 -1
- package/lib/compiler/internal/compileProjectInMemory.js +4 -3
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/project/createNativeProjectContextArgs.d.ts +9 -0
- package/lib/compiler/internal/project/createNativeProjectContextArgs.js +16 -4
- package/lib/compiler/internal/project/createNativeProjectContextArgs.js.map +1 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.d.ts +2 -0
- package/lib/compiler/internal/resolveEmittedJavaScript.js +3 -0
- package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +1 -1
- package/lib/compiler/internal/runBuild.js +80 -34
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/runSingleFileEmit.js +2 -2
- package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
- package/lib/compiler/internal/sharedHostHelpers.d.ts +28 -0
- package/lib/compiler/internal/sharedHostHelpers.js +39 -0
- package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
- package/lib/compiler/internal/startResidentTransform.js +12 -4
- package/lib/compiler/internal/startResidentTransform.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.d.ts +3 -0
- package/lib/compiler/internal/transformProjectInMemory.js +239 -35
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/flags/schema.js +12 -5
- package/lib/flags/schema.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/internal/createCanonicalTempDirectory.d.ts +17 -0
- package/lib/internal/createCanonicalTempDirectory.js +42 -0
- package/lib/internal/createCanonicalTempDirectory.js.map +1 -0
- package/lib/internal/projectInputPathIdentity.d.ts +3 -0
- package/lib/internal/projectInputPathIdentity.js +11 -5
- package/lib/internal/projectInputPathIdentity.js.map +1 -1
- package/lib/internal/resolveNodeBinary.d.ts +14 -0
- package/lib/internal/resolveNodeBinary.js +150 -0
- package/lib/internal/resolveNodeBinary.js.map +1 -0
- package/lib/internal/resolveSafeCacheCleanupTargets.d.ts +15 -0
- package/lib/internal/resolveSafeCacheCleanupTargets.js +61 -0
- package/lib/internal/resolveSafeCacheCleanupTargets.js.map +1 -0
- package/lib/launcher/internal/prepareExecution.d.ts +3 -0
- package/lib/launcher/internal/prepareExecution.js +42 -14
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/runTtsc.js +25 -13
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtscserver.js +18 -3
- package/lib/launcher/internal/runTtscserver.js.map +1 -1
- package/lib/launcher/internal/runTtsx.js +2 -0
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/launcher/internal/runtimeHooks.d.ts +47 -3
- package/lib/launcher/internal/runtimeHooks.js +746 -64
- package/lib/launcher/internal/runtimeHooks.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.d.ts +62 -8
- package/lib/plugin/internal/buildSourcePlugin.js +754 -72
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/capabilityResolutionCache.d.ts +76 -0
- package/lib/plugin/internal/capabilityResolutionCache.js +224 -0
- package/lib/plugin/internal/capabilityResolutionCache.js.map +1 -0
- package/lib/plugin/internal/loadProjectPlugins.d.ts +17 -5
- package/lib/plugin/internal/loadProjectPlugins.js +1339 -77
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/plugin/resolveCapabilityPlugins.d.ts +55 -0
- package/lib/plugin/resolveCapabilityPlugins.js +118 -0
- package/lib/plugin/resolveCapabilityPlugins.js.map +1 -0
- package/lib/register.d.ts +1 -0
- package/lib/register.js +46 -0
- package/lib/register.js.map +1 -0
- package/lib/structures/ITtscCompilerContext.d.ts +6 -3
- package/lib/structures/ITtscCompilerTransformation.d.ts +38 -0
- package/lib/structures/ITtscPlugin.d.ts +33 -0
- package/package.json +16 -9
- package/shim/ast/parent.go +16 -3
- package/shim/checker/enums_gen.go +11 -0
- package/shim/checker/go.mod +9 -0
- package/shim/checker/go.sum +22 -0
- package/shim/checker/shim.go +78 -0
- package/shim/compiler/emithook.go +10 -0
- package/shim/compiler/incremental.go +45 -8
- package/src/TtscCompiler.ts +55 -33
- package/src/compiler/internal/captureProcessOutput.ts +3 -2
- package/src/compiler/internal/compileProjectInMemory.ts +4 -3
- package/src/compiler/internal/project/createNativeProjectContextArgs.ts +19 -4
- package/src/compiler/internal/resolveEmittedJavaScript.ts +6 -0
- package/src/compiler/internal/runBuild.ts +102 -39
- package/src/compiler/internal/runSingleFileEmit.ts +2 -2
- package/src/compiler/internal/sharedHostHelpers.ts +44 -0
- package/src/compiler/internal/startResidentTransform.ts +12 -3
- package/src/compiler/internal/transformProjectInMemory.ts +410 -39
- package/src/flags/schema.ts +12 -5
- package/src/index.ts +1 -0
- package/src/internal/createCanonicalTempDirectory.ts +53 -0
- package/src/internal/projectInputPathIdentity.ts +17 -4
- package/src/internal/resolveNodeBinary.ts +173 -0
- package/src/internal/resolveSafeCacheCleanupTargets.ts +85 -0
- package/src/launcher/internal/prepareExecution.ts +50 -14
- package/src/launcher/internal/runTtsc.ts +24 -13
- package/src/launcher/internal/runTtscserver.ts +18 -3
- package/src/launcher/internal/runTtsx.ts +2 -0
- package/src/launcher/internal/runtimeHooks.ts +934 -68
- package/src/plugin/internal/buildSourcePlugin.ts +984 -79
- package/src/plugin/internal/capabilityResolutionCache.ts +305 -0
- package/src/plugin/internal/loadProjectPlugins.ts +1718 -104
- package/src/plugin/resolveCapabilityPlugins.ts +171 -0
- package/src/register.ts +47 -0
- package/src/structures/ITtscCompilerContext.ts +6 -3
- package/src/structures/ITtscCompilerTransformation.ts +46 -0
- package/src/structures/ITtscPlugin.ts +36 -0
- package/tools/shim_audit/baseline.json +0 -2
- package/utility/host.go +18 -3
- package/lib/internal/assertSafeExplicitCacheDirectory.d.ts +0 -5
- package/lib/internal/assertSafeExplicitCacheDirectory.js +0 -26
- package/lib/internal/assertSafeExplicitCacheDirectory.js.map +0 -1
- package/src/internal/assertSafeExplicitCacheDirectory.ts +0 -29
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
package graph
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"path/filepath"
|
|
5
|
+
"testing"
|
|
6
|
+
|
|
7
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// TestDocTagsCaptureEveryUnrecognizedTag verifies that the build pass records a
|
|
11
|
+
// DocTag for each documentation tag TypeScript does not recognize, on every
|
|
12
|
+
// declaration form, and records nothing for the tags it does.
|
|
13
|
+
//
|
|
14
|
+
// The population boundary is the whole design: naming particular tags would make
|
|
15
|
+
// the compiler host know one convention, so the pass collects exactly what the
|
|
16
|
+
// parser could not interpret. That makes the known-tag half of this test
|
|
17
|
+
// load-bearing rather than incidental — if `@param` ever arrived here, the field
|
|
18
|
+
// would silently become a second, worse copy of facts the AST already models.
|
|
19
|
+
//
|
|
20
|
+
// 1. Build a fixture whose declarations carry convention tags, known tags, a
|
|
21
|
+
// tag with no text, and a multi-line reason, across a function, a class, a
|
|
22
|
+
// class member, an interface member, a variable, a namespace member, and a
|
|
23
|
+
// closure.
|
|
24
|
+
// 2. Assert every unrecognized tag is recorded on its own declaration's node.
|
|
25
|
+
// 3. Assert no known tag is recorded, and that an untagged declaration
|
|
26
|
+
// contributes nothing.
|
|
27
|
+
func TestDocTagsCaptureEveryUnrecognizedTag(t *testing.T) {
|
|
28
|
+
root := t.TempDir()
|
|
29
|
+
writeFile(t, filepath.Join(root, "tsconfig.json"), fixtureTSConfig)
|
|
30
|
+
writeFile(t, filepath.Join(root, "src", "main.ts"), `/**
|
|
31
|
+
* Renders the notice.
|
|
32
|
+
*
|
|
33
|
+
* @evidence docs/discount.md#coupon-stacking States the per-issuer limit
|
|
34
|
+
* this section defines, in the buyer's words.
|
|
35
|
+
* @evidenceExclude prisma:Coupon.issuedAt Not surfaced here.
|
|
36
|
+
* @param unused Ignored by the graph.
|
|
37
|
+
* @returns Nothing the graph records.
|
|
38
|
+
*/
|
|
39
|
+
export function renderNotice(): string {
|
|
40
|
+
return "notice";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** @reference https://example.com/spec Reference document. */
|
|
44
|
+
export class Notice {
|
|
45
|
+
/** @evidence docs/discount.md#member Member-level citation. */
|
|
46
|
+
public render(): void {}
|
|
47
|
+
|
|
48
|
+
/** @spec invented Any convention is one fact. */
|
|
49
|
+
public flag: boolean = false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface INotice {
|
|
53
|
+
/** @evidence docs/discount.md#signature Interface member citation. */
|
|
54
|
+
render(): void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** @evidence docs/discount.md#variable Variable citation. */
|
|
58
|
+
export const NOTICE_KIND = "notice";
|
|
59
|
+
|
|
60
|
+
export namespace Shopping {
|
|
61
|
+
/** @evidence docs/discount.md#namespace Namespace member citation. */
|
|
62
|
+
export function inner(): void {}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function withClosure(): void {
|
|
66
|
+
/** @evidence docs/discount.md#closure Declared inside a body. */
|
|
67
|
+
function inner(): void {}
|
|
68
|
+
inner();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Ordinary documentation with no tag at all. */
|
|
72
|
+
export function untagged(): void {}
|
|
73
|
+
|
|
74
|
+
/** @bare */
|
|
75
|
+
export function bareTag(): void {}
|
|
76
|
+
`)
|
|
77
|
+
|
|
78
|
+
prog, diags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
|
|
79
|
+
if err != nil {
|
|
80
|
+
t.Fatal(err)
|
|
81
|
+
}
|
|
82
|
+
if len(diags) != 0 {
|
|
83
|
+
t.Fatalf("unexpected diagnostics: %v", diags)
|
|
84
|
+
}
|
|
85
|
+
defer func() { _ = prog.Close() }()
|
|
86
|
+
|
|
87
|
+
tags := docTagsByTargetSuffix(Build(prog))
|
|
88
|
+
|
|
89
|
+
assertDocTag(t, tags, "#renderNotice:function", "evidence",
|
|
90
|
+
"docs/discount.md#coupon-stacking States the per-issuer limit this section defines, in the buyer's words.")
|
|
91
|
+
assertDocTag(t, tags, "#renderNotice:function", "evidenceExclude",
|
|
92
|
+
"prisma:Coupon.issuedAt Not surfaced here.")
|
|
93
|
+
assertDocTag(t, tags, "#Notice:class", "reference",
|
|
94
|
+
"https://example.com/spec Reference document.")
|
|
95
|
+
assertDocTag(t, tags, "#Notice.render:method", "evidence",
|
|
96
|
+
"docs/discount.md#member Member-level citation.")
|
|
97
|
+
assertDocTag(t, tags, "#Notice.flag:variable", "spec",
|
|
98
|
+
"invented Any convention is one fact.")
|
|
99
|
+
assertDocTag(t, tags, "#INotice.render:method", "evidence",
|
|
100
|
+
"docs/discount.md#signature Interface member citation.")
|
|
101
|
+
assertDocTag(t, tags, "#NOTICE_KIND:variable", "evidence",
|
|
102
|
+
"docs/discount.md#variable Variable citation.")
|
|
103
|
+
assertDocTag(t, tags, "#Shopping.inner:function", "evidence",
|
|
104
|
+
"docs/discount.md#namespace Namespace member citation.")
|
|
105
|
+
// A function declared inside a body is a node the graph records, so a tag on
|
|
106
|
+
// it is a fact like any other. It is reached through the same single
|
|
107
|
+
// collection point, which is why no declaration form needs its own handling.
|
|
108
|
+
assertDocTag(t, tags, "#withClosure.inner:function", "evidence",
|
|
109
|
+
"docs/discount.md#closure Declared inside a body.")
|
|
110
|
+
// A tag with a name and no text is a written fact; dropping it would report
|
|
111
|
+
// the declaration as carrying nothing, which is a different claim.
|
|
112
|
+
assertDocTag(t, tags, "#bareTag:function", "bare", "")
|
|
113
|
+
|
|
114
|
+
for _, known := range []string{"param", "returns"} {
|
|
115
|
+
for target, list := range tags {
|
|
116
|
+
for _, tag := range list {
|
|
117
|
+
if tag.Name == known {
|
|
118
|
+
t.Fatalf("recorded known tag %q on %s; only tags the parser could not interpret belong here", known, target)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
for target, list := range tags {
|
|
124
|
+
if suffixMatch(target, "#untagged:function") && len(list) > 0 {
|
|
125
|
+
t.Fatalf("untagged declaration carries %d tags", len(list))
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// docTagsByTargetSuffix groups a built graph's tags by their target node id.
|
|
131
|
+
func docTagsByTargetSuffix(g *Graph) map[string][]*DocTag {
|
|
132
|
+
out := map[string][]*DocTag{}
|
|
133
|
+
for _, tag := range g.DocTags {
|
|
134
|
+
out[tag.Target] = append(out[tag.Target], tag)
|
|
135
|
+
}
|
|
136
|
+
return out
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// assertDocTag fails unless exactly one tag with this name sits on the node
|
|
140
|
+
// whose id ends with suffix, carrying text.
|
|
141
|
+
func assertDocTag(t *testing.T, tags map[string][]*DocTag, suffix, name, text string) {
|
|
142
|
+
t.Helper()
|
|
143
|
+
found := 0
|
|
144
|
+
for target, list := range tags {
|
|
145
|
+
if !suffixMatch(target, suffix) {
|
|
146
|
+
continue
|
|
147
|
+
}
|
|
148
|
+
for _, tag := range list {
|
|
149
|
+
if tag.Name != name {
|
|
150
|
+
continue
|
|
151
|
+
}
|
|
152
|
+
found++
|
|
153
|
+
if tag.Text != text {
|
|
154
|
+
t.Fatalf("%s @%s text = %q, want %q", suffix, name, tag.Text, text)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if found != 1 {
|
|
159
|
+
t.Fatalf("%s @%s recorded %d times, want 1", suffix, name, found)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
func suffixMatch(value, suffix string) bool {
|
|
164
|
+
return len(value) >= len(suffix) && value[len(value)-len(suffix):] == suffix
|
|
165
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
package graph
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"path/filepath"
|
|
5
|
+
"testing"
|
|
6
|
+
|
|
7
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// TestDocTagsReadOnlyWhatTheParserAttached verifies the boundary cases that
|
|
11
|
+
// decide whether a tag-shaped run of text is a fact about a declaration.
|
|
12
|
+
//
|
|
13
|
+
// Every case here is one a text scan would get wrong, and a wrong answer is
|
|
14
|
+
// silent in both directions: a tag adopted from a line comment or from prose
|
|
15
|
+
// attributes a citation to code that never made it, while a tag lost from a
|
|
16
|
+
// merged declaration reports the declaration as citing nothing. The parser
|
|
17
|
+
// already decides which block belongs to which declaration, so this pins that
|
|
18
|
+
// the collection follows it rather than re-deciding.
|
|
19
|
+
//
|
|
20
|
+
// 1. Build a fixture with a tag in a line comment, a tag written mid-sentence
|
|
21
|
+
// inside a documentation block, an overload run documented on its
|
|
22
|
+
// signature, and a link tag whose braced form must survive verbatim.
|
|
23
|
+
// 2. Assert the line comment contributes nothing and the mid-sentence one is
|
|
24
|
+
// the tag the parser says it is.
|
|
25
|
+
// 3. Assert the overload keeps its signature's tag and the link keeps its
|
|
26
|
+
// braces.
|
|
27
|
+
func TestDocTagsReadOnlyWhatTheParserAttached(t *testing.T) {
|
|
28
|
+
root := t.TempDir()
|
|
29
|
+
writeFile(t, filepath.Join(root, "tsconfig.json"), fixtureTSConfig)
|
|
30
|
+
writeFile(t, filepath.Join(root, "src", "main.ts"), `export interface ISale {
|
|
31
|
+
price: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// @evidence docs/a.md#line A line comment is not documentation.
|
|
35
|
+
export function fromLineComment(): void {}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Explains that an @evidence tag names a document, without being one.
|
|
39
|
+
*/
|
|
40
|
+
export function fromProse(): void {}
|
|
41
|
+
|
|
42
|
+
/** @evidence docs/a.md#overload Documented on the signature. */
|
|
43
|
+
export function overloaded(value: string): string;
|
|
44
|
+
export function overloaded(value: number): string;
|
|
45
|
+
export function overloaded(value: string | number): string {
|
|
46
|
+
return String(value);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** @evidence {@link ISale} The contract is mirrored here. */
|
|
50
|
+
export function linked(): void {}
|
|
51
|
+
|
|
52
|
+
/** @evidence {@linkcode ISale} And in code form. */
|
|
53
|
+
export function linkedCode(): void {}
|
|
54
|
+
`)
|
|
55
|
+
|
|
56
|
+
prog, diags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
|
|
57
|
+
if err != nil {
|
|
58
|
+
t.Fatal(err)
|
|
59
|
+
}
|
|
60
|
+
if len(diags) != 0 {
|
|
61
|
+
t.Fatalf("unexpected diagnostics: %v", diags)
|
|
62
|
+
}
|
|
63
|
+
defer func() { _ = prog.Close() }()
|
|
64
|
+
|
|
65
|
+
tags := docTagsByTargetSuffix(Build(prog))
|
|
66
|
+
|
|
67
|
+
// A `//` comment is not documentation, so the parser attaches it to nothing
|
|
68
|
+
// and no tag can be read out of it.
|
|
69
|
+
for target, list := range tags {
|
|
70
|
+
if suffixMatch(target, "#fromLineComment:function") && len(list) > 0 {
|
|
71
|
+
t.Fatalf("line comment produced %d tags; only a documentation block attaches one", len(list))
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// A tag written mid-sentence is still a tag, because TypeScript's own parser
|
|
76
|
+
// opens one at the `@` wherever it sits. This is deliberately not second-
|
|
77
|
+
// guessed: the population is "what the parser could not interpret", and a
|
|
78
|
+
// graph that re-decided where a tag may begin would be reinterpreting the
|
|
79
|
+
// compiler rather than reporting it. A convention that wants the stricter
|
|
80
|
+
// rule — the tag must open its line — enforces that in its own rule, where the
|
|
81
|
+
// author gets a diagnostic; here the fact is simply reported as parsed.
|
|
82
|
+
assertDocTag(t, tags, "#fromProse:function", "evidence",
|
|
83
|
+
"tag names a document, without being one.")
|
|
84
|
+
|
|
85
|
+
// The implementation signature carries no block of its own. Collecting tags
|
|
86
|
+
// outside the node-creation branch is what keeps the overload run's
|
|
87
|
+
// documentation, which is written on the first signature.
|
|
88
|
+
assertDocTag(t, tags, "#overloaded:function", "evidence",
|
|
89
|
+
"docs/a.md#overload Documented on the signature.")
|
|
90
|
+
|
|
91
|
+
// A citation target is matched by the token the author wrote, so the braces
|
|
92
|
+
// and the link keyword have to survive the round trip through the AST.
|
|
93
|
+
assertDocTag(t, tags, "#linked:function", "evidence",
|
|
94
|
+
"{@link ISale} The contract is mirrored here.")
|
|
95
|
+
assertDocTag(t, tags, "#linkedCode:function", "evidence",
|
|
96
|
+
"{@linkcode ISale} And in code form.")
|
|
97
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
package graph
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"path/filepath"
|
|
5
|
+
"strings"
|
|
6
|
+
"testing"
|
|
7
|
+
|
|
8
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// TestDocTagsStopAtTheExternalBoundary verifies that a documentation tag written
|
|
12
|
+
// in a dependency contributes nothing.
|
|
13
|
+
//
|
|
14
|
+
// The graph's boundary is the workspace: a dependency's declaration enters only
|
|
15
|
+
// as a named endpoint, never walked into. A tag read from one would put a
|
|
16
|
+
// citation the consumer did not write into their index — answering "which code
|
|
17
|
+
// implements this specification" with somebody else's code — and it would do so
|
|
18
|
+
// silently, because the address would look like any other. The collector reaches
|
|
19
|
+
// only declarations the build pass records, so this holds by construction; the
|
|
20
|
+
// test is here because "by construction" is what stops being true when the
|
|
21
|
+
// construction changes.
|
|
22
|
+
//
|
|
23
|
+
// 1. Build a fixture whose `node_modules` dependency carries a tag and whose
|
|
24
|
+
// workspace source carries another.
|
|
25
|
+
// 2. Assert the workspace tag is recorded.
|
|
26
|
+
// 3. Assert no tag is recorded from the dependency, under any target.
|
|
27
|
+
func TestDocTagsStopAtTheExternalBoundary(t *testing.T) {
|
|
28
|
+
root := t.TempDir()
|
|
29
|
+
writeFile(t, filepath.Join(root, "tsconfig.json"), nodeModulesGlobalFixtureTSConfig)
|
|
30
|
+
writeFile(t, filepath.Join(root, "node_modules", "dep", "globals.ts"), `/** @evidence docs/vendor.md#theirs Written by the dependency. */
|
|
31
|
+
export function vendored(): void {}
|
|
32
|
+
`)
|
|
33
|
+
writeFile(t, filepath.Join(root, "src", "main.ts"), `/** @evidence docs/ours.md#mine Written here. */
|
|
34
|
+
export function ours(): void {}
|
|
35
|
+
`)
|
|
36
|
+
|
|
37
|
+
prog, diags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
|
|
38
|
+
if err != nil {
|
|
39
|
+
t.Fatal(err)
|
|
40
|
+
}
|
|
41
|
+
if len(diags) != 0 {
|
|
42
|
+
t.Fatalf("unexpected diagnostics: %v", diags)
|
|
43
|
+
}
|
|
44
|
+
defer func() { _ = prog.Close() }()
|
|
45
|
+
|
|
46
|
+
tags := docTagsByTargetSuffix(Build(prog))
|
|
47
|
+
|
|
48
|
+
assertDocTag(t, tags, "#ours:function", "evidence", "docs/ours.md#mine Written here.")
|
|
49
|
+
|
|
50
|
+
for target, list := range tags {
|
|
51
|
+
for _, tag := range list {
|
|
52
|
+
if strings.Contains(target, "node_modules") ||
|
|
53
|
+
strings.Contains(tag.Text, "docs/vendor.md") {
|
|
54
|
+
t.Fatalf("recorded %s from %s; a dependency's citation is not this project's",
|
|
55
|
+
tag.Text, target)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
package graph
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"path/filepath"
|
|
5
|
+
"strings"
|
|
6
|
+
"testing"
|
|
7
|
+
|
|
8
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// TestDocTagsSurviveLineEndingsAndRepeatedBlocks verifies the boundary cases
|
|
12
|
+
// where one declaration's tags can be silently lost or doubled.
|
|
13
|
+
//
|
|
14
|
+
// Each is invisible when wrong. A CRLF checkout that joined a reason
|
|
15
|
+
// differently would make the same source produce different index entries on
|
|
16
|
+
// Windows and POSIX, so a citation found on one machine would be missing on the
|
|
17
|
+
// other. A merged identity declared twice keeps whichever declarations the walk
|
|
18
|
+
// reached, and a `var` redeclaration is the one shape where the same identity
|
|
19
|
+
// carries two documentation blocks that both have to survive. A link written
|
|
20
|
+
// with trailing text inside its braces is reassembled from two fields, so the
|
|
21
|
+
// text can be dropped without the target changing.
|
|
22
|
+
//
|
|
23
|
+
// 1. Build a fixture with CRLF sources, a redeclared `var` documented twice, a
|
|
24
|
+
// multi-binding statement, a declaration carrying two documentation blocks,
|
|
25
|
+
// and a link carrying trailing text.
|
|
26
|
+
// 2. Assert the CRLF reason joins exactly as its LF twin does.
|
|
27
|
+
// 3. Assert both blocks of the merged identity are kept, every binding of the
|
|
28
|
+
// statement carries its documentation, and the link keeps its text.
|
|
29
|
+
func TestDocTagsSurviveLineEndingsAndRepeatedBlocks(t *testing.T) {
|
|
30
|
+
root := t.TempDir()
|
|
31
|
+
writeFile(t, filepath.Join(root, "tsconfig.json"), fixtureTSConfig)
|
|
32
|
+
|
|
33
|
+
lf := `export interface ISale {
|
|
34
|
+
price: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Renders it.
|
|
39
|
+
*
|
|
40
|
+
* @evidence docs/a.md#reason States the limit
|
|
41
|
+
* this section defines.
|
|
42
|
+
*/
|
|
43
|
+
export function joined(): void {}
|
|
44
|
+
|
|
45
|
+
/** @evidence {@link ISale the sale contract} And prose after it. */
|
|
46
|
+
export function linkText(): void {}
|
|
47
|
+
|
|
48
|
+
/** @evidence docs/a.md#first The first declaration. */
|
|
49
|
+
export var merged: number;
|
|
50
|
+
/** @evidence docs/a.md#second The second declaration. */
|
|
51
|
+
export var merged: number;
|
|
52
|
+
|
|
53
|
+
/** @evidence docs/a.md#shared Documents the whole statement. */
|
|
54
|
+
export var first = 1,
|
|
55
|
+
second = 2;
|
|
56
|
+
|
|
57
|
+
/** @evidence docs/a.md#one First block. */
|
|
58
|
+
/** @evidence docs/a.md#two Second block on the same declaration. */
|
|
59
|
+
export function twoBlocks(): void {}
|
|
60
|
+
`
|
|
61
|
+
// The shared fixture config names one root, and the CRLF twin has to be the
|
|
62
|
+
// same declarations rather than a second set, so the two spellings compile as
|
|
63
|
+
// two projects instead of two files of one.
|
|
64
|
+
writeFile(t, filepath.Join(root, "src", "main.ts"), lf)
|
|
65
|
+
crlfRoot := t.TempDir()
|
|
66
|
+
writeFile(t, filepath.Join(crlfRoot, "tsconfig.json"), fixtureTSConfig)
|
|
67
|
+
writeFile(t, filepath.Join(crlfRoot, "src", "main.ts"),
|
|
68
|
+
strings.ReplaceAll(strings.ReplaceAll(lf, "\r\n", "\n"), "\n", "\r\n"))
|
|
69
|
+
|
|
70
|
+
prog, diags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
|
|
71
|
+
if err != nil {
|
|
72
|
+
t.Fatal(err)
|
|
73
|
+
}
|
|
74
|
+
if len(diags) != 0 {
|
|
75
|
+
t.Fatalf("unexpected diagnostics: %v", diags)
|
|
76
|
+
}
|
|
77
|
+
defer func() { _ = prog.Close() }()
|
|
78
|
+
|
|
79
|
+
crlfProg, crlfDiags, err := driver.LoadProgram(crlfRoot, "tsconfig.json", driver.LoadProgramOptions{})
|
|
80
|
+
if err != nil {
|
|
81
|
+
t.Fatal(err)
|
|
82
|
+
}
|
|
83
|
+
if len(crlfDiags) != 0 {
|
|
84
|
+
t.Fatalf("unexpected CRLF diagnostics: %v", crlfDiags)
|
|
85
|
+
}
|
|
86
|
+
defer func() { _ = crlfProg.Close() }()
|
|
87
|
+
|
|
88
|
+
byRoot := map[string]map[string][]*DocTag{
|
|
89
|
+
"LF": docTagsByTargetSuffix(Build(prog)),
|
|
90
|
+
"CRLF": docTagsByTargetSuffix(Build(crlfProg)),
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// A reason written across two comment lines is one string, under both
|
|
94
|
+
// spellings of a line terminator.
|
|
95
|
+
for spelling, tags := range byRoot {
|
|
96
|
+
assertDocTagIn(t, tags, spelling, "#joined:function", "evidence",
|
|
97
|
+
"docs/a.md#reason States the limit this section defines.")
|
|
98
|
+
assertDocTagIn(t, tags, spelling, "#linkText:function", "evidence",
|
|
99
|
+
"{@link ISale the sale contract} And prose after it.")
|
|
100
|
+
// Both declarations of one identity contribute: collecting inside the
|
|
101
|
+
// node-creation branch would have kept whichever won the display span.
|
|
102
|
+
assertDocTagIn(t, tags, spelling, "#merged:variable", "evidence",
|
|
103
|
+
"docs/a.md#first The first declaration.")
|
|
104
|
+
assertDocTagIn(t, tags, spelling, "#merged:variable", "evidence",
|
|
105
|
+
"docs/a.md#second The second declaration.")
|
|
106
|
+
// One statement documents every binding it declares: the text sits above
|
|
107
|
+
// the statement and nothing in the source assigns it to the first name.
|
|
108
|
+
assertDocTagIn(t, tags, spelling, "#first:variable", "evidence",
|
|
109
|
+
"docs/a.md#shared Documents the whole statement.")
|
|
110
|
+
assertDocTagIn(t, tags, spelling, "#second:variable", "evidence",
|
|
111
|
+
"docs/a.md#shared Documents the whole statement.")
|
|
112
|
+
// TypeScript attaches more than one documentation block to a declaration
|
|
113
|
+
// when more than one is written, and each is a place a tag can sit. Reading
|
|
114
|
+
// only the last would silently drop the first.
|
|
115
|
+
assertDocTagIn(t, tags, spelling, "#twoBlocks:function", "evidence",
|
|
116
|
+
"docs/a.md#one First block.")
|
|
117
|
+
assertDocTagIn(t, tags, spelling, "#twoBlocks:function", "evidence",
|
|
118
|
+
"docs/a.md#two Second block on the same declaration.")
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// assertDocTagIn fails unless a tag with this name and text sits on the node
|
|
123
|
+
// whose id ends with suffix, naming the source spelling in the failure.
|
|
124
|
+
func assertDocTagIn(t *testing.T, tags map[string][]*DocTag, spelling, suffix, name, text string) {
|
|
125
|
+
t.Helper()
|
|
126
|
+
for target, list := range tags {
|
|
127
|
+
if !suffixMatch(target, suffix) {
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
130
|
+
for _, tag := range list {
|
|
131
|
+
if tag.Name == name && tag.Text == text {
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
t.Fatalf("%s %s carries %s, want @%s %q", spelling, suffix, renderDocTags(list), name, text)
|
|
136
|
+
}
|
|
137
|
+
t.Fatalf("%s %s recorded no tags at all, want @%s %q", spelling, suffix, name, text)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
func renderDocTags(list []*DocTag) string {
|
|
141
|
+
out := make([]string, 0, len(list))
|
|
142
|
+
for _, tag := range list {
|
|
143
|
+
out = append(out, "@"+tag.Name+" "+tag.Text)
|
|
144
|
+
}
|
|
145
|
+
return "[" + strings.Join(out, " | ") + "]"
|
|
146
|
+
}
|
package/internal/graph/dump.go
CHANGED
|
@@ -57,6 +57,15 @@ type DumpDecorator struct {
|
|
|
57
57
|
Arguments []DumpDecoratorArgument `json:"arguments"`
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
// DumpDocTag is one documentation tag TypeScript does not recognize, carried on
|
|
61
|
+
// the declaration it was written on. `name` is the tag without its `@` and
|
|
62
|
+
// `text` is everything after it, joined into one line. Neither is interpreted:
|
|
63
|
+
// which part of a text names a thing belongs to whichever convention wrote it.
|
|
64
|
+
type DumpDocTag struct {
|
|
65
|
+
Name string `json:"name"`
|
|
66
|
+
Text string `json:"text,omitempty"`
|
|
67
|
+
}
|
|
68
|
+
|
|
60
69
|
// DumpEnumMember is one member of an enum on the wire: the name a caller writes
|
|
61
70
|
// and the value it carries. `value` is omitted for a member the checker could
|
|
62
71
|
// not fold to a constant — the name still stands, and the name is what a caller
|
|
@@ -89,19 +98,25 @@ type DumpNode struct {
|
|
|
89
98
|
// and stopped early when the head itself contained a brace — a type-literal
|
|
90
99
|
// parameter, an object return type, a destructured parameter. Neither is a
|
|
91
100
|
// guess the consumer can win, so the producer renders it here.
|
|
92
|
-
Signature
|
|
93
|
-
File
|
|
94
|
-
External
|
|
95
|
-
Ignored
|
|
96
|
-
Exported
|
|
97
|
-
Closure
|
|
98
|
-
Modifiers
|
|
99
|
-
Literals
|
|
100
|
-
EnumMembers
|
|
101
|
-
ObjectMembers
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
101
|
+
Signature string `json:"signature,omitempty"`
|
|
102
|
+
File string `json:"file"`
|
|
103
|
+
External bool `json:"external"`
|
|
104
|
+
Ignored bool `json:"ignored,omitempty"`
|
|
105
|
+
Exported bool `json:"exported,omitempty"`
|
|
106
|
+
Closure bool `json:"closure,omitempty"`
|
|
107
|
+
Modifiers []string `json:"modifiers,omitempty"`
|
|
108
|
+
Literals []string `json:"literals,omitempty"`
|
|
109
|
+
EnumMembers []DumpEnumMember `json:"enumMembers,omitempty"`
|
|
110
|
+
ObjectMembers []DumpObjectMember `json:"objectMembers,omitempty"`
|
|
111
|
+
// Parent is the artifact containing this one, and is empty for every
|
|
112
|
+
// declaration: a declaration's containment is synthesized by the TypeScript
|
|
113
|
+
// memory layer, and two producers of one relation would put two answers in
|
|
114
|
+
// the graph.
|
|
115
|
+
Parent string `json:"parent,omitempty"`
|
|
116
|
+
Evidence *DumpEvidence `json:"evidence,omitempty"`
|
|
117
|
+
Implementation *DumpEvidence `json:"implementation,omitempty"`
|
|
118
|
+
Decorators []DumpDecorator `json:"decorators,omitempty"`
|
|
119
|
+
DocTags []DumpDocTag `json:"docTags,omitempty"`
|
|
105
120
|
}
|
|
106
121
|
|
|
107
122
|
// DumpEdge is the wire shape of a graph edge. Lowercase json keys are the
|
|
@@ -233,6 +248,13 @@ func newDumpFacts(g *Graph, project string, ignored map[string]bool, sources map
|
|
|
233
248
|
decByNode[d.Target] = append(decByNode[d.Target], DumpDecorator{Name: d.Name, Arguments: args})
|
|
234
249
|
}
|
|
235
250
|
|
|
251
|
+
// Documentation tags ride on their target node the same way, grouped before
|
|
252
|
+
// relativization for the same reason.
|
|
253
|
+
tagsByNode := make(map[string][]DumpDocTag, len(g.DocTags))
|
|
254
|
+
for _, t := range g.DocTags {
|
|
255
|
+
tagsByNode[t.Target] = append(tagsByNode[t.Target], DumpDocTag{Name: t.Name, Text: t.Text})
|
|
256
|
+
}
|
|
257
|
+
|
|
236
258
|
nodes := make([]DumpNode, 0, len(g.Nodes))
|
|
237
259
|
for _, n := range g.Nodes {
|
|
238
260
|
name, qualified := nodeNames(n)
|
|
@@ -241,6 +263,28 @@ func newDumpFacts(g *Graph, project string, ignored map[string]bool, sources map
|
|
|
241
263
|
if n.Kind == NodeModule {
|
|
242
264
|
name, qualified = ctx.rel(name), ""
|
|
243
265
|
}
|
|
266
|
+
// An artifact's id IS the address a citation names, so it is emitted
|
|
267
|
+
// verbatim: relativizing it would rewrite `prisma:Sale.price`, which carries
|
|
268
|
+
// no path on purpose, and `POST:/orders`, which has no file at all. Its name
|
|
269
|
+
// is the readable heading the plugin reported, and its span is the single
|
|
270
|
+
// line where that heading starts — never the section's content.
|
|
271
|
+
if IsArtifactKind(n.Kind) {
|
|
272
|
+
artifact := DumpNode{
|
|
273
|
+
ID: n.ID,
|
|
274
|
+
Kind: string(n.Kind),
|
|
275
|
+
Name: n.Simple,
|
|
276
|
+
File: ctx.rel(n.File),
|
|
277
|
+
Parent: n.ArtifactParent,
|
|
278
|
+
}
|
|
279
|
+
if artifact.Name == "" {
|
|
280
|
+
artifact.Name = n.Name
|
|
281
|
+
}
|
|
282
|
+
if n.ArtifactLine > 0 {
|
|
283
|
+
artifact.Evidence = &DumpEvidence{StartLine: n.ArtifactLine}
|
|
284
|
+
}
|
|
285
|
+
nodes = append(nodes, artifact)
|
|
286
|
+
continue
|
|
287
|
+
}
|
|
244
288
|
nodes = append(nodes, DumpNode{
|
|
245
289
|
ID: ctx.relID(n.ID),
|
|
246
290
|
Kind: string(n.Kind),
|
|
@@ -261,6 +305,7 @@ func newDumpFacts(g *Graph, project string, ignored map[string]bool, sources map
|
|
|
261
305
|
Evidence: withoutFile(ctx.evidence(n.File, n.Pos, n.End)),
|
|
262
306
|
Implementation: ctx.evidence(n.ImplementationFile, n.ImplementationPos, n.ImplementationEnd),
|
|
263
307
|
Decorators: decByNode[n.ID],
|
|
308
|
+
DocTags: tagsByNode[n.ID],
|
|
264
309
|
})
|
|
265
310
|
}
|
|
266
311
|
sort.Slice(nodes, func(i, j int) bool { return nodes[i].ID < nodes[j].ID })
|
|
@@ -398,6 +443,8 @@ func wireEdgeKind(kind EdgeKind, origin string) string {
|
|
|
398
443
|
return "accesses"
|
|
399
444
|
case EdgeTypeRef:
|
|
400
445
|
return "type_ref"
|
|
446
|
+
case EdgeDocRef:
|
|
447
|
+
return "doc_ref"
|
|
401
448
|
case EdgeHeritage:
|
|
402
449
|
if origin == "extends" {
|
|
403
450
|
return "extends"
|
|
@@ -20,7 +20,7 @@ const portableDumpTSConfig = `{
|
|
|
20
20
|
}
|
|
21
21
|
`
|
|
22
22
|
|
|
23
|
-
// TestDumpCheckoutPathsAreStable verifies the whole
|
|
23
|
+
// TestDumpCheckoutPathsAreStable verifies the whole dump-identity consequence
|
|
24
24
|
// surface, not only the path helper.
|
|
25
25
|
//
|
|
26
26
|
// 1. Build the same project plus sibling .ts/.d.ts under two checkout roots.
|
|
@@ -51,7 +51,7 @@ func TestDumpCheckoutPathsAreStable(t *testing.T) {
|
|
|
51
51
|
assertPortableFixturePaths(t, first)
|
|
52
52
|
if strings.Contains(string(firstJSON), filepath.ToSlash(firstCheckout)) ||
|
|
53
53
|
strings.Contains(string(secondJSON), filepath.ToSlash(secondCheckout)) {
|
|
54
|
-
t.Fatal("a producer-local checkout path escaped into
|
|
54
|
+
t.Fatal("a producer-local checkout path escaped into dump identity")
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -9,7 +9,7 @@ import (
|
|
|
9
9
|
shimtspath "github.com/microsoft/typescript-go/shim/tspath"
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
// dumpPathMapper owns the
|
|
12
|
+
// dumpPathMapper owns the dump path vocabulary for one dump. Every
|
|
13
13
|
// identity-bearing path passes through this one cache, so the producer can
|
|
14
14
|
// reject both an unportable filesystem root and a non-injective projection
|
|
15
15
|
// before any JSON is written.
|
|
@@ -50,7 +50,7 @@ func WireProject(project string) (string, error) {
|
|
|
50
50
|
return mapper.project, mapper.err()
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
// WirePath maps one compiler path into the portable
|
|
53
|
+
// WirePath maps one compiler path into the portable dump vocabulary used
|
|
54
54
|
// by dumps and resident graph shards. Callers that project a complete graph
|
|
55
55
|
// keep one dumpPathMapper so collision detection spans every path; callers
|
|
56
56
|
// shaping a single source/config coordinate use this helper and receive the
|
|
@@ -5,7 +5,7 @@ import (
|
|
|
5
5
|
"testing"
|
|
6
6
|
)
|
|
7
7
|
|
|
8
|
-
// TestDumpPathMapperUsesPortableCoordinates verifies the
|
|
8
|
+
// TestDumpPathMapperUsesPortableCoordinates verifies the dump path vocabulary
|
|
9
9
|
// directly, independent of the host OS running the test.
|
|
10
10
|
//
|
|
11
11
|
// 1. Map in-project and sibling paths for POSIX, drive, and UNC layouts.
|