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
package/cmd/ttscgraph/dump.go
CHANGED
|
@@ -23,6 +23,11 @@ func runDump(args []string) int {
|
|
|
23
23
|
cwdFlag := fs.String("cwd", "", "project root (defaults to process cwd)")
|
|
24
24
|
tsconfigFlag := fs.String("tsconfig", "tsconfig.json", "project tsconfig path")
|
|
25
25
|
prettyFlag := fs.Bool("pretty", false, "indent the JSON output")
|
|
26
|
+
artifactsFlag := fs.String(
|
|
27
|
+
"artifacts",
|
|
28
|
+
"",
|
|
29
|
+
"path to the artifacts a plugin published (JSON); absent or missing means none",
|
|
30
|
+
)
|
|
26
31
|
if err := fs.Parse(args); err != nil {
|
|
27
32
|
return 2
|
|
28
33
|
}
|
|
@@ -37,7 +42,7 @@ func runDump(args []string) int {
|
|
|
37
42
|
cwd = resolved
|
|
38
43
|
}
|
|
39
44
|
// Resolve the project root the same way LoadProgram does (absolute, then
|
|
40
|
-
// tsgo-normalized) so the
|
|
45
|
+
// tsgo-normalized) so the dump path mapper receives the same canonical root
|
|
41
46
|
// grammar and drive-letter case as the compiler's source paths.
|
|
42
47
|
if abs, err := filepath.Abs(cwd); err == nil {
|
|
43
48
|
cwd = abs
|
|
@@ -57,9 +62,19 @@ func runDump(args []string) int {
|
|
|
57
62
|
defer func() { _ = prog.Close() }()
|
|
58
63
|
|
|
59
64
|
g := graph.Build(prog)
|
|
65
|
+
// The artifacts arrive from a plugin that parsed documents this Program never
|
|
66
|
+
// read, so they are a second producer's facts and the origin says so. Applying
|
|
67
|
+
// them before the dump is what turns a citation of a document section from a
|
|
68
|
+
// token into a relation.
|
|
69
|
+
artifacts, err := graph.LoadArtifacts(strings.TrimSpace(*artifactsFlag))
|
|
70
|
+
if err != nil {
|
|
71
|
+
fmt.Fprintf(stderr, "ttscgraph: could not read the published artifacts: %v\n", err)
|
|
72
|
+
return 1
|
|
73
|
+
}
|
|
74
|
+
graph.ApplyArtifacts(g, artifacts)
|
|
60
75
|
ignored := graph.GitIgnoredFiles(cwd, g)
|
|
61
76
|
texts := graph.SourceTexts(prog)
|
|
62
|
-
origin, err := dumpOrigin(prog, texts)
|
|
77
|
+
origin, err := dumpOrigin(prog, texts, strings.TrimSpace(*artifactsFlag) != "")
|
|
63
78
|
if err != nil {
|
|
64
79
|
fmt.Fprintf(stderr, "ttscgraph: %v\n", err)
|
|
65
80
|
return 1
|
|
@@ -89,7 +104,16 @@ func runDump(args []string) int {
|
|
|
89
104
|
// saw the project, so it carries the same provenance a served snapshot does:
|
|
90
105
|
// without it the file is a pile of facts with no way to tell which program, or
|
|
91
106
|
// which day, they describe.
|
|
92
|
-
|
|
107
|
+
// dumpOrigin builds the provenance for one dump. askedForArtifacts is what
|
|
108
|
+
// turns the artifact capability from a guess into a claim: a producer that was
|
|
109
|
+
// never pointed at a publisher did not look, and saying it did would make a
|
|
110
|
+
// project with no artifacts indistinguishable from one whose artifacts were
|
|
111
|
+
// never requested.
|
|
112
|
+
func dumpOrigin(
|
|
113
|
+
prog *driver.Program,
|
|
114
|
+
texts map[string]string,
|
|
115
|
+
askedForArtifacts bool,
|
|
116
|
+
) (graph.DumpOrigin, error) {
|
|
93
117
|
configs, err := parsedConfigs(prog)
|
|
94
118
|
if err != nil {
|
|
95
119
|
return graph.DumpOrigin{}, err
|
|
@@ -102,15 +126,23 @@ func dumpOrigin(prog *driver.Program, texts map[string]string) (graph.DumpOrigin
|
|
|
102
126
|
if err != nil {
|
|
103
127
|
return graph.DumpOrigin{}, err
|
|
104
128
|
}
|
|
129
|
+
capabilities := fullSnapshotCapabilities
|
|
130
|
+
if askedForArtifacts {
|
|
131
|
+
capabilities = append(append([]string{}, capabilities...), graph.CapabilityArtifactNodes)
|
|
132
|
+
}
|
|
133
|
+
provenance := graph.NewProvenance(
|
|
134
|
+
serveProducer(),
|
|
135
|
+
capabilities,
|
|
136
|
+
fileDigests(configHashes),
|
|
137
|
+
rootFileEntries(projectRootFilesFromConfigs(configs, false)),
|
|
138
|
+
texts,
|
|
139
|
+
diskDigests,
|
|
140
|
+
)
|
|
141
|
+
if askedForArtifacts {
|
|
142
|
+
provenance.ArtifactProducer = &graph.Producer{Tool: "@ttsc/lint graph-nodes"}
|
|
143
|
+
}
|
|
105
144
|
return graph.DumpOrigin{
|
|
106
|
-
Provenance:
|
|
107
|
-
serveProducer(),
|
|
108
|
-
fullSnapshotCapabilities,
|
|
109
|
-
fileDigests(configHashes),
|
|
110
|
-
rootFileEntries(projectRootFilesFromConfigs(configs, false)),
|
|
111
|
-
texts,
|
|
112
|
-
diskDigests,
|
|
113
|
-
),
|
|
145
|
+
Provenance: provenance,
|
|
114
146
|
Diagnostics: graph.NewDiagnostics(prog),
|
|
115
147
|
}, nil
|
|
116
148
|
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"os"
|
|
7
|
+
"path/filepath"
|
|
8
|
+
"slices"
|
|
9
|
+
"strings"
|
|
10
|
+
"testing"
|
|
11
|
+
|
|
12
|
+
"github.com/samchon/ttsc/packages/ttsc/internal/graph"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
// TestRunDumpClaimsArtifactsOnlyWhenItAsked verifies that the artifact
|
|
16
|
+
// capability is a statement about what this producer did, not about what it
|
|
17
|
+
// found.
|
|
18
|
+
//
|
|
19
|
+
// The two states a consumer must distinguish are "this project publishes no
|
|
20
|
+
// artifacts" and "this producer was never pointed at a publisher". Both are the
|
|
21
|
+
// same absent nodes, so only the claim separates them — and a producer that
|
|
22
|
+
// claimed unconditionally would make a project with no lint install look like a
|
|
23
|
+
// project whose documents cite nothing. The second producer is named for the
|
|
24
|
+
// same reason: these facts did not come from this Program, and the dump's
|
|
25
|
+
// one-generation contract stays honest by saying so rather than by hiding it.
|
|
26
|
+
//
|
|
27
|
+
// 1. Dump a project with no `--artifacts`, and assert neither the claim nor the
|
|
28
|
+
// second producer appears, and that the citation stays a token.
|
|
29
|
+
// 2. Dump the same project with a published set, and assert both appear and the
|
|
30
|
+
// citation resolved to a node carrying its heading and its line.
|
|
31
|
+
func TestRunDumpClaimsArtifactsOnlyWhenItAsked(t *testing.T) {
|
|
32
|
+
root := t.TempDir()
|
|
33
|
+
writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
|
|
34
|
+
"compilerOptions": { "target": "ES2022", "module": "commonjs", "strict": true },
|
|
35
|
+
"files": ["src/main.ts"]
|
|
36
|
+
}
|
|
37
|
+
`)
|
|
38
|
+
writeGraphFile(t, filepath.Join(root, "src", "main.ts"), `/** @evidence docs/sale.md#pricing States the rule. */
|
|
39
|
+
export function priced(): void {}
|
|
40
|
+
`)
|
|
41
|
+
|
|
42
|
+
plain := decodeArtifactDump(t, runDumpForArtifacts(t, root, ""))
|
|
43
|
+
if slices.Contains(plain.Provenance.Capabilities, graph.CapabilityArtifactNodes) {
|
|
44
|
+
t.Fatalf("a dump that was never pointed at a publisher claimed %q", graph.CapabilityArtifactNodes)
|
|
45
|
+
}
|
|
46
|
+
if plain.Provenance.ArtifactProducer != nil {
|
|
47
|
+
t.Fatal("a dump with no artifacts named a second producer")
|
|
48
|
+
}
|
|
49
|
+
for _, node := range plain.Nodes {
|
|
50
|
+
if node.ID == "docs/sale.md#pricing" {
|
|
51
|
+
t.Fatal("an unrequested dump fabricated an artifact node")
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
published := filepath.Join(root, "artifacts.json")
|
|
56
|
+
if err := os.WriteFile(published, []byte(`[
|
|
57
|
+
{"address":"docs/sale.md","kind":"markdown_document","readable":"Sale","file":"docs/sale.md","line":1},
|
|
58
|
+
{"address":"docs/sale.md#pricing","kind":"markdown_section","readable":"Pricing","parent":"docs/sale.md","file":"docs/sale.md","line":7}
|
|
59
|
+
]`), 0o644); err != nil {
|
|
60
|
+
t.Fatal(err)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
indexed := decodeArtifactDump(t, runDumpForArtifacts(t, root, published))
|
|
64
|
+
if !slices.Contains(indexed.Provenance.Capabilities, graph.CapabilityArtifactNodes) {
|
|
65
|
+
t.Fatalf("a dump that asked declares %v, missing %q",
|
|
66
|
+
indexed.Provenance.Capabilities, graph.CapabilityArtifactNodes)
|
|
67
|
+
}
|
|
68
|
+
if indexed.Provenance.ArtifactProducer == nil {
|
|
69
|
+
t.Fatal("the artifact nodes came from another Program and the dump did not say so")
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
var section *graph.DumpNode
|
|
73
|
+
for index := range indexed.Nodes {
|
|
74
|
+
if indexed.Nodes[index].ID == "docs/sale.md#pricing" {
|
|
75
|
+
section = &indexed.Nodes[index]
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if section == nil {
|
|
79
|
+
t.Fatal("the cited section is not a node")
|
|
80
|
+
}
|
|
81
|
+
if section.Name != "Pricing" {
|
|
82
|
+
t.Fatalf("the section is named %q, not the heading text an index exists to hold", section.Name)
|
|
83
|
+
}
|
|
84
|
+
if section.Parent != "docs/sale.md" {
|
|
85
|
+
t.Fatalf("the section's parent is %q, want its document", section.Parent)
|
|
86
|
+
}
|
|
87
|
+
if section.Evidence == nil || section.Evidence.StartLine != 7 {
|
|
88
|
+
t.Fatalf("the section carries no span at its heading: %+v", section.Evidence)
|
|
89
|
+
}
|
|
90
|
+
cited := false
|
|
91
|
+
for _, edge := range indexed.Edges {
|
|
92
|
+
if edge.Kind == "doc_ref" && edge.To == "docs/sale.md#pricing" {
|
|
93
|
+
cited = true
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if !cited {
|
|
97
|
+
t.Fatal("the citation stayed a token; it has to resolve to the node now that one exists")
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// runDumpForArtifacts runs the dump subcommand and returns its stdout.
|
|
102
|
+
func runDumpForArtifacts(t *testing.T, root, artifacts string) string {
|
|
103
|
+
t.Helper()
|
|
104
|
+
args := []string{"dump", "--cwd", root, "--tsconfig", "tsconfig.json"}
|
|
105
|
+
if artifacts != "" {
|
|
106
|
+
args = append(args, "--artifacts", artifacts)
|
|
107
|
+
}
|
|
108
|
+
var out, errOut bytes.Buffer
|
|
109
|
+
restoreStdout, restoreStderr := stdout, stderr
|
|
110
|
+
stdout, stderr = &out, &errOut
|
|
111
|
+
defer func() { stdout, stderr = restoreStdout, restoreStderr }()
|
|
112
|
+
|
|
113
|
+
if code := run(args); code != 0 {
|
|
114
|
+
t.Fatalf("dump exited %d: %s", code, errOut.String())
|
|
115
|
+
}
|
|
116
|
+
return out.String()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
type artifactDump struct {
|
|
120
|
+
Provenance struct {
|
|
121
|
+
Capabilities []string `json:"capabilities"`
|
|
122
|
+
ArtifactProducer *graph.Producer `json:"artifactProducer"`
|
|
123
|
+
} `json:"provenance"`
|
|
124
|
+
Nodes []graph.DumpNode `json:"nodes"`
|
|
125
|
+
Edges []graph.DumpEdge `json:"edges"`
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
func decodeArtifactDump(t *testing.T, raw string) artifactDump {
|
|
129
|
+
t.Helper()
|
|
130
|
+
var parsed artifactDump
|
|
131
|
+
if err := json.Unmarshal([]byte(strings.TrimSpace(raw)), &parsed); err != nil {
|
|
132
|
+
t.Fatalf("dump is not JSON: %v", err)
|
|
133
|
+
}
|
|
134
|
+
return parsed
|
|
135
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"slices"
|
|
8
|
+
"strings"
|
|
9
|
+
"testing"
|
|
10
|
+
|
|
11
|
+
"github.com/samchon/ttsc/packages/ttsc/internal/graph"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
// TestRunDumpDeclaresTheCapabilityItsOutputCarries verifies that the shipped
|
|
15
|
+
// `dump` command's own output declares the documentation-tag capability, and
|
|
16
|
+
// that a project carrying no tag emits the field nowhere.
|
|
17
|
+
//
|
|
18
|
+
// The claim is the contract, and it can only be tested where it is made. A test
|
|
19
|
+
// that hands a capability list to the marshaller and reads it back proves the
|
|
20
|
+
// slice was copied; it leaves the production list free to lose the member with
|
|
21
|
+
// nothing failing. That matters because the two states a consumer must
|
|
22
|
+
// distinguish — "this declaration cites nothing" and "this producer never
|
|
23
|
+
// looked" — are the same absent field, so a dump that carries tags and forgets
|
|
24
|
+
// to say so is read as a repository where nothing cites anything.
|
|
25
|
+
//
|
|
26
|
+
// 1. Run the `dump` subcommand over a project with a tag, capturing stdout.
|
|
27
|
+
// 2. Assert the capability is declared and the tag is present in the output.
|
|
28
|
+
// 3. Run it over a project with no tag and assert the field appears nowhere,
|
|
29
|
+
// while the claim still does.
|
|
30
|
+
func TestRunDumpDeclaresTheCapabilityItsOutputCarries(t *testing.T) {
|
|
31
|
+
tagged := runDumpForCapabilityProbe(t, `/** @evidence docs/a.md#x Cited. */
|
|
32
|
+
export function subject(): void {}
|
|
33
|
+
`)
|
|
34
|
+
untagged := runDumpForCapabilityProbe(t, `/** Ordinary documentation. */
|
|
35
|
+
export function subject(): void {}
|
|
36
|
+
`)
|
|
37
|
+
|
|
38
|
+
for name, raw := range map[string]string{"tagged": tagged, "untagged": untagged} {
|
|
39
|
+
var parsed struct {
|
|
40
|
+
Provenance struct {
|
|
41
|
+
Capabilities []string `json:"capabilities"`
|
|
42
|
+
} `json:"provenance"`
|
|
43
|
+
}
|
|
44
|
+
if err := json.Unmarshal([]byte(raw), &parsed); err != nil {
|
|
45
|
+
t.Fatalf("%s dump is not JSON: %v", name, err)
|
|
46
|
+
}
|
|
47
|
+
if !slices.Contains(parsed.Provenance.Capabilities, graph.CapabilityDocTags) {
|
|
48
|
+
t.Fatalf("%s dump declares %v, missing %q; a consumer would then read a "+
|
|
49
|
+
"tagged declaration as citing nothing",
|
|
50
|
+
name, parsed.Provenance.Capabilities, graph.CapabilityDocTags)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if !strings.Contains(tagged, `"docs/a.md#x Cited."`) {
|
|
55
|
+
t.Fatalf("the tagged dump carried no tag text")
|
|
56
|
+
}
|
|
57
|
+
// Read the raw document rather than a decoded slice: an absent field and an
|
|
58
|
+
// empty one decode identically, and absent is what the wire contract says.
|
|
59
|
+
// The key is matched with its colon, because the capability shares the name
|
|
60
|
+
// and rides the same document as a bare string.
|
|
61
|
+
if strings.Contains(untagged, `"docTags":`) {
|
|
62
|
+
t.Fatalf("the untagged dump carried a docTags field; it must be absent")
|
|
63
|
+
}
|
|
64
|
+
if !strings.Contains(tagged, `"docTags":`) {
|
|
65
|
+
t.Fatalf("the tagged dump carried no docTags field, so the check above proves nothing")
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// runDumpForCapabilityProbe runs the dump subcommand over a one-file project and
|
|
70
|
+
// returns its stdout.
|
|
71
|
+
func runDumpForCapabilityProbe(t *testing.T, source string) string {
|
|
72
|
+
t.Helper()
|
|
73
|
+
root := t.TempDir()
|
|
74
|
+
writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
|
|
75
|
+
"compilerOptions": {
|
|
76
|
+
"target": "ES2022",
|
|
77
|
+
"module": "commonjs",
|
|
78
|
+
"strict": true,
|
|
79
|
+
"rootDir": "src",
|
|
80
|
+
"outDir": "dist"
|
|
81
|
+
},
|
|
82
|
+
"files": ["src/main.ts"]
|
|
83
|
+
}
|
|
84
|
+
`)
|
|
85
|
+
writeGraphFile(t, filepath.Join(root, "src", "main.ts"), source)
|
|
86
|
+
|
|
87
|
+
var out, errOut bytes.Buffer
|
|
88
|
+
restoreStdout, restoreStderr := stdout, stderr
|
|
89
|
+
stdout, stderr = &out, &errOut
|
|
90
|
+
defer func() { stdout, stderr = restoreStdout, restoreStderr }()
|
|
91
|
+
|
|
92
|
+
if code := run([]string{"dump", "--cwd", root, "--tsconfig", "tsconfig.json"}); code != 0 {
|
|
93
|
+
t.Fatalf("dump exited %d: %s", code, errOut.String())
|
|
94
|
+
}
|
|
95
|
+
return out.String()
|
|
96
|
+
}
|