ttsc 0.19.3 → 0.20.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 +7 -5
- package/cmd/graphdump/main.go +2 -4
- package/cmd/ttsc/filter.go +12 -4
- package/cmd/ttsc/filter_resolves_flag_names_case_insensitively_test.go +53 -0
- package/cmd/ttsc/flags_gen.go +3 -3
- package/cmd/ttscgraph/dump.go +5 -7
- package/cmd/ttscgraph/run_dump_emits_only_checker_verified_member_relations_test.go +118 -0
- package/cmd/ttscgraph/serve.go +58 -308
- package/cmd/ttscgraph/serve_session_keeps_base_url_fallback_unchanged_test.go +49 -0
- package/cmd/ttscgraph/serve_session_keeps_esm_directory_candidate_unchanged_test.go +61 -0
- package/cmd/ttscgraph/serve_session_keeps_exports_blocked_file_candidates_unchanged_test.go +64 -0
- package/cmd/ttscgraph/serve_session_keeps_inactive_conditional_export_unchanged_test.go +66 -0
- package/cmd/ttscgraph/serve_session_keeps_lower_priority_module_candidate_unchanged_test.go +67 -0
- package/cmd/ttscgraph/serve_session_keeps_lower_priority_paths_pattern_unchanged_test.go +58 -0
- package/cmd/ttscgraph/serve_session_keeps_nonlongest_root_dirs_candidate_unchanged_test.go +54 -0
- package/cmd/ttscgraph/serve_session_reloads_commonjs_package_entry_for_esm_test.go +50 -0
- package/cmd/ttscgraph/serve_session_reloads_module_suffix_candidate_test.go +41 -0
- package/cmd/ttscgraph/serve_session_reloads_superseding_module_candidates_test.go +216 -0
- package/cmd/ttscgraph/serve_session_retries_failed_dump_test.go +103 -0
- package/driver/graph.go +12 -6
- package/driver/lsp.go +30 -0
- package/driver/program.go +37 -1
- package/driver/resolution_candidates.go +819 -0
- package/driver/windowsjunction/windowsjunction.go +40 -0
- package/go.mod +2 -0
- package/internal/graph/build.go +45 -11
- package/internal/graph/dump.go +276 -100
- package/internal/graph/dump_checkout_paths_are_stable_test.go +210 -0
- package/internal/graph/dump_diagnostics_ride_the_generation_that_built_it_test.go +9 -3
- package/internal/graph/dump_paths.go +172 -0
- package/internal/graph/dump_paths_are_portable_and_injective_test.go +93 -0
- package/internal/graph/dump_uses_ecma_line_terminators_test.go +87 -0
- package/internal/graph/duplicate_variable_object_members_keep_first_declaration_test.go +61 -0
- package/internal/graph/edges.go +208 -0
- package/internal/graph/graph.go +143 -5
- package/internal/graph/line_model.go +122 -0
- package/internal/graph/member_relation_edges_follow_checker_validity_test.go +85 -0
- package/internal/graph/member_relation_edges_preserve_checker_boundaries_test.go +191 -0
- package/internal/graph/member_relation_edges_reject_checker_invalid_pairs_test.go +119 -0
- package/internal/graph/node_ids_preserve_hash_bearing_components_test.go +41 -0
- package/internal/graph/node_modifiers_emit_union_strings_test.go +4 -1
- package/internal/graph/object_literal_members_ride_on_the_snapshot_test.go +192 -0
- package/internal/graph/object_member_signatures_preserve_literal_whitespace_test.go +67 -0
- package/internal/graph/object_members.go +229 -0
- package/internal/graph/provenance.go +88 -30
- package/internal/graph/source_texts_cover_every_resident_program_source_test.go +73 -0
- package/internal/graphsymbols/position_helpers_follow_ecma_line_terminators_test.go +66 -0
- package/internal/graphsymbols/provider.go +26 -91
- package/internal/lspserver/lsp_clean_didopen_refreshes_compiler_state_test.go +144 -0
- package/internal/lspserver/lsp_completion.go +109 -0
- package/internal/lspserver/lsp_completion_gate_never_refuses_a_matching_line_test.go +50 -0
- package/internal/lspserver/lsp_completion_hints_longest_trigger_wins_test.go +149 -0
- package/internal/lspserver/lsp_completion_hints_nearest_trigger_wins_test.go +37 -0
- package/internal/lspserver/lsp_completion_merges_into_upstream_response_test.go +149 -0
- package/internal/lspserver/lsp_completion_nearest_hint_replaces_nearest_filter_test.go +43 -0
- package/internal/lspserver/lsp_completion_preserves_completion_list_extensions_test.go +99 -0
- package/internal/lspserver/lsp_completion_refuses_hints_inside_a_string_literal_test.go +69 -0
- package/internal/lspserver/lsp_completion_replaces_full_filter_in_rank_order_test.go +85 -0
- package/internal/lspserver/lsp_completion_request.go +455 -0
- package/internal/lspserver/lsp_completion_scope.go +530 -0
- package/internal/lspserver/lsp_completion_scope_bench_test.go +41 -0
- package/internal/lspserver/lsp_completion_scope_classifies_regex_statement_and_operator_boundaries_test.go +39 -0
- package/internal/lspserver/lsp_completion_scope_ignores_jsdoc_bytes_in_literals_test.go +108 -0
- package/internal/lspserver/lsp_completion_scope_refuses_regex_hints_test.go +52 -0
- package/internal/lspserver/lsp_completion_trigger_uses_last_rune_test.go +24 -0
- package/internal/lspserver/lsp_deleted_watched_file_withdraws_plugin_diagnostics_test.go +110 -0
- package/internal/lspserver/lsp_didsave_keeps_the_incremental_changed_uri_path_test.go +78 -0
- package/internal/lspserver/lsp_hints_absence_is_not_an_error_test.go +46 -0
- package/internal/lspserver/lsp_hints_refresh_coalesces_concurrent_requests_test.go +82 -0
- package/internal/lspserver/lsp_hints_refresh_ignores_a_superseded_generation_test.go +40 -0
- package/internal/lspserver/lsp_hints_refresh_keeps_each_producer_isolated_test.go +64 -0
- package/internal/lspserver/lsp_hints_refresh_replaces_the_session_snapshot_test.go +66 -0
- package/internal/lspserver/lsp_hints_refresh_scheduled_by_editor_events_test.go +90 -0
- package/internal/lspserver/lsp_hints_refresh_serves_completion_while_running_test.go +91 -0
- package/internal/lspserver/lsp_index_byte_from_bounds_its_search_start_test.go +38 -0
- package/internal/lspserver/lsp_initialize_constrains_position_encoding_to_utf16_test.go +205 -0
- package/internal/lspserver/lsp_late_completion_trigger_is_reported_once_test.go +105 -0
- package/internal/lspserver/lsp_native_completion_hints_decode_flat_lint_wire_test.go +66 -0
- package/internal/lspserver/lsp_native_completion_hints_grouped_compatibility_boundary_test.go +60 -0
- package/internal/lspserver/lsp_native_plugin_source.go +315 -2
- package/internal/lspserver/lsp_native_resident.go +304 -0
- package/internal/lspserver/lsp_plugin.go +85 -8
- package/internal/lspserver/lsp_plugin_completion_resolve_stays_local_test.go +82 -0
- package/internal/lspserver/lsp_position_conversions_span_astral_and_combining_runes_test.go +119 -0
- package/internal/lspserver/lsp_proxy.go +601 -9
- package/internal/lspserver/lsp_refresh.go +70 -0
- package/internal/lspserver/lsp_resident_falls_back_when_serve_unsupported_test.go +43 -0
- package/internal/lspserver/lsp_resident_maps_verb_args_test.go +34 -0
- package/internal/lspserver/lsp_server.go +13 -1
- package/internal/lspserver/lsp_watched_file_changes_reach_compiler_caches_test.go +172 -0
- package/lib/TtscCompiler.d.ts +3 -1
- package/lib/TtscCompiler.js +5 -1
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/TtscService.d.ts +13 -3
- package/lib/TtscService.js +6 -6
- package/lib/TtscService.js.map +1 -1
- package/lib/compiler/internal/project/readConfigJson.d.ts +30 -0
- package/lib/compiler/internal/project/readConfigJson.js +208 -0
- package/lib/compiler/internal/project/readConfigJson.js.map +1 -0
- package/lib/compiler/internal/project/readProjectConfig.js +53 -149
- package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
- package/lib/compiler/internal/residentTransformProcess.d.ts +15 -2
- package/lib/compiler/internal/residentTransformProcess.js +159 -39
- package/lib/compiler/internal/residentTransformProcess.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +6 -0
- package/lib/compiler/internal/runBuild.js +143 -44
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/startResidentTransform.d.ts +5 -1
- package/lib/compiler/internal/startResidentTransform.js +3 -1
- package/lib/compiler/internal/startResidentTransform.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +12 -2
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/flags/parser.d.ts +19 -0
- package/lib/flags/parser.js +78 -36
- package/lib/flags/parser.js.map +1 -1
- package/lib/flags/schema.d.ts +49 -11
- package/lib/flags/schema.js +94 -22
- package/lib/flags/schema.js.map +1 -1
- package/lib/internal/assertSafeExplicitCacheDirectory.d.ts +5 -0
- package/lib/internal/assertSafeExplicitCacheDirectory.js +26 -0
- package/lib/internal/assertSafeExplicitCacheDirectory.js.map +1 -0
- package/lib/launcher/internal/runTtsc.js +107 -69
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtsx.js +28 -41
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/launcher/internal/runtimeHooks.d.ts +15 -0
- package/lib/launcher/internal/runtimeHooks.js +18 -1
- package/lib/launcher/internal/runtimeHooks.js.map +1 -1
- package/lib/launcher/internal/watchTopology.d.ts +43 -0
- package/lib/launcher/internal/watchTopology.js +360 -0
- package/lib/launcher/internal/watchTopology.js.map +1 -0
- package/lib/plugin/internal/buildSourcePlugin.d.ts +14 -3
- package/lib/plugin/internal/buildSourcePlugin.js +247 -61
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.js +63 -2
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscCompilerTransformation.d.ts +16 -0
- package/lib/structures/internal/ITtscParsedProjectConfig.d.ts +2 -0
- package/package.json +8 -8
- package/shim/ast/shim.go +48 -3
- package/shim/astnav/go.mod +14 -0
- package/shim/astnav/go.sum +20 -0
- package/shim/astnav/shim.go +15 -0
- package/shim/checker/shim.go +100 -0
- package/shim/core/shim.go +47 -0
- package/shim/diagnosticwriter/lint.go +50 -0
- package/src/TtscCompiler.ts +5 -1
- package/src/TtscService.ts +37 -10
- package/src/compiler/internal/project/readConfigJson.ts +209 -0
- package/src/compiler/internal/project/readProjectConfig.ts +62 -158
- package/src/compiler/internal/residentTransformProcess.ts +197 -39
- package/src/compiler/internal/runBuild.ts +169 -47
- package/src/compiler/internal/startResidentTransform.ts +12 -1
- package/src/compiler/internal/transformProjectInMemory.ts +12 -1
- package/src/flags/parser.ts +113 -42
- package/src/flags/schema.ts +113 -31
- package/src/internal/assertSafeExplicitCacheDirectory.ts +29 -0
- package/src/launcher/internal/runTtsc.ts +117 -68
- package/src/launcher/internal/runTtsx.ts +29 -44
- package/src/launcher/internal/runtimeHooks.ts +30 -2
- package/src/launcher/internal/watchTopology.ts +466 -0
- package/src/plugin/internal/buildSourcePlugin.ts +326 -64
- package/src/plugin/internal/loadProjectPlugins.ts +80 -2
- package/src/structures/ITtscCompilerTransformation.ts +17 -0
- package/src/structures/internal/ITtscParsedProjectConfig.ts +2 -0
- package/tools/shim_audit/baseline.json +0 -3
- package/tools/shim_audit/main.go +42 -14
- package/tools/shim_audit/shim_dir_coverage_test.go +25 -0
- package/utility/flags_gen.go +3 -3
- package/utility/host.go +9 -3
package/README.md
CHANGED
|
@@ -75,15 +75,17 @@ let total = count;
|
|
|
75
75
|
|
|
76
76
|
```text
|
|
77
77
|
$ npx ttsc --noEmit
|
|
78
|
+
src/index.ts:1:1 - error TS11966: [no-var] Unexpected var, use let or const instead.
|
|
79
|
+
|
|
80
|
+
1 var count = 3;
|
|
81
|
+
~~~
|
|
82
|
+
|
|
78
83
|
src/index.ts:2:5 - error TS17397: [prefer-const] Use const instead of let.
|
|
79
84
|
|
|
80
85
|
2 let total = count;
|
|
81
|
-
|
|
86
|
+
~~~~~
|
|
82
87
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
1 var count = 3;
|
|
86
|
-
~~~~~~~~~~~~~~
|
|
88
|
+
Found 2 errors in the same file, starting at: src/index.ts:1
|
|
87
89
|
```
|
|
88
90
|
|
|
89
91
|
Clean the project in place:
|
package/cmd/graphdump/main.go
CHANGED
|
@@ -28,9 +28,8 @@ func run() int {
|
|
|
28
28
|
flag.Parse()
|
|
29
29
|
|
|
30
30
|
// Resolve the project root the same way LoadProgram does (absolute, then
|
|
31
|
-
// tsgo-normalized) so
|
|
32
|
-
//
|
|
33
|
-
// leave every path absolute.
|
|
31
|
+
// tsgo-normalized) so the schema-v6 mapper receives the same canonical root
|
|
32
|
+
// grammar and drive-letter case as the compiler's source paths.
|
|
34
33
|
root := *cwd
|
|
35
34
|
if abs, err := filepath.Abs(root); err == nil {
|
|
36
35
|
root = abs
|
|
@@ -60,7 +59,6 @@ func run() int {
|
|
|
60
59
|
// `ttscgraph dump` is the one that proves the whole contract.
|
|
61
60
|
data, err := graph.MarshalDump(g, root, *tsconfig, ignored, texts, graph.DumpOrigin{
|
|
62
61
|
Provenance: graph.NewProvenance(
|
|
63
|
-
root,
|
|
64
62
|
// No version: this tool is built from the tree on demand and never
|
|
65
63
|
// stamped, and an invented one would be worse than an absent one.
|
|
66
64
|
graph.Producer{Tool: "graphdump", Typescript: graph.TypescriptVersion()},
|
package/cmd/ttsc/filter.go
CHANGED
|
@@ -59,12 +59,20 @@ func filterHostArgs(args []string) []string {
|
|
|
59
59
|
return filtered
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
// splitFlagName strips the leading `--` from a flag argument
|
|
63
|
-
// whether the argument carries an inline value
|
|
62
|
+
// splitFlagName strips the leading `--` from a flag argument, lower-cases the
|
|
63
|
+
// result, and reports whether the argument carries an inline value
|
|
64
|
+
// (`--foo=value`).
|
|
65
|
+
//
|
|
66
|
+
// Lower-casing is the same normalization `normalizeFlagToken` in
|
|
67
|
+
// `packages/ttsc/src/flags/schema.ts` applies, and the generated
|
|
68
|
+
// `HostFlagAllowList` keys are produced by it. TypeScript's option parser
|
|
69
|
+
// matches option names case-insensitively, so keying this lookup on the exact
|
|
70
|
+
// spelling would make the Go layer stop recognising a flag the launcher and the
|
|
71
|
+
// compiler both resolve.
|
|
64
72
|
func splitFlagName(arg string) (string, bool) {
|
|
65
73
|
name := strings.TrimPrefix(arg, "--")
|
|
66
74
|
if i := strings.IndexByte(name, '='); i != -1 {
|
|
67
|
-
return name[:i], true
|
|
75
|
+
return strings.ToLower(name[:i]), true
|
|
68
76
|
}
|
|
69
|
-
return name, false
|
|
77
|
+
return strings.ToLower(name), false
|
|
70
78
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"reflect"
|
|
5
|
+
"testing"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// TestFilterHostArgsResolvesFlagNamesCaseInsensitively verifies the host
|
|
9
|
+
// allow-list lookup applies the same flag-name normalization the schema uses
|
|
10
|
+
// when it generates that allow-list.
|
|
11
|
+
//
|
|
12
|
+
// TypeScript's option parser matches option names case-insensitively, and
|
|
13
|
+
// `normalizeFlagToken` in `packages/ttsc/src/flags/schema.ts` is the one
|
|
14
|
+
// normalization every layer keys off — including `buildGoAllowList`, whose
|
|
15
|
+
// output is `flags_gen.go`. Keying this lookup on the exact spelling while the
|
|
16
|
+
// generated keys are normalized would recreate the split-brain one layer
|
|
17
|
+
// further down: a flag the launcher and the compiler both resolve would be
|
|
18
|
+
// dropped here, silently taking the following token with it.
|
|
19
|
+
//
|
|
20
|
+
// 1. A case-variant known flag survives with its value.
|
|
21
|
+
// 2. A case-variant boolean known flag survives and does not eat the next token.
|
|
22
|
+
// 3. An unknown flag is still dropped in every casing, with its value.
|
|
23
|
+
func TestFilterHostArgsResolvesFlagNamesCaseInsensitively(t *testing.T) {
|
|
24
|
+
cases := []struct {
|
|
25
|
+
name string
|
|
26
|
+
in []string
|
|
27
|
+
want []string
|
|
28
|
+
}{
|
|
29
|
+
{
|
|
30
|
+
name: "keeps a case-variant value flag with its value",
|
|
31
|
+
in: []string{"--TSCONFIG", "tsconfig.json", "--CWD=."},
|
|
32
|
+
want: []string{"--TSCONFIG", "tsconfig.json", "--CWD=."},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "keeps a case-variant boolean flag without eating the next token",
|
|
36
|
+
in: []string{"--NOEMIT", "--tsconfig", "tsconfig.json"},
|
|
37
|
+
want: []string{"--NOEMIT", "--tsconfig", "tsconfig.json"},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "still drops an unknown flag in any casing",
|
|
41
|
+
in: []string{"--tsconfig=tsconfig.json", "--sTrIcT", "true"},
|
|
42
|
+
want: []string{"--tsconfig=tsconfig.json"},
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
for _, tc := range cases {
|
|
46
|
+
t.Run(tc.name, func(t *testing.T) {
|
|
47
|
+
got := filterHostArgs(tc.in)
|
|
48
|
+
if !reflect.DeepEqual(got, tc.want) {
|
|
49
|
+
t.Fatalf("filterHostArgs(%v):\n want %v\n got %v", tc.in, tc.want, got)
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
}
|
package/cmd/ttsc/flags_gen.go
CHANGED
|
@@ -17,13 +17,13 @@ var HostFlagAllowList = map[string]bool{
|
|
|
17
17
|
"cwd": true,
|
|
18
18
|
"emit": false,
|
|
19
19
|
"manifest": true,
|
|
20
|
-
"
|
|
21
|
-
"
|
|
20
|
+
"noemit": false,
|
|
21
|
+
"outdir": true,
|
|
22
22
|
"p": true,
|
|
23
23
|
"plugins-json": true,
|
|
24
24
|
"project": true,
|
|
25
25
|
"quiet": false,
|
|
26
|
-
"
|
|
26
|
+
"singlethreaded": false,
|
|
27
27
|
"tsconfig": true,
|
|
28
28
|
"tsgo-args": true,
|
|
29
29
|
"verbose": false,
|
package/cmd/ttscgraph/dump.go
CHANGED
|
@@ -37,9 +37,8 @@ func runDump(args []string) int {
|
|
|
37
37
|
cwd = resolved
|
|
38
38
|
}
|
|
39
39
|
// Resolve the project root the same way LoadProgram does (absolute, then
|
|
40
|
-
// tsgo-normalized) so
|
|
41
|
-
//
|
|
42
|
-
// leave every path absolute.
|
|
40
|
+
// tsgo-normalized) so the schema-v6 mapper receives the same canonical root
|
|
41
|
+
// grammar and drive-letter case as the compiler's source paths.
|
|
43
42
|
if abs, err := filepath.Abs(cwd); err == nil {
|
|
44
43
|
cwd = abs
|
|
45
44
|
}
|
|
@@ -60,7 +59,7 @@ func runDump(args []string) int {
|
|
|
60
59
|
g := graph.Build(prog)
|
|
61
60
|
ignored := graph.GitIgnoredFiles(cwd, g)
|
|
62
61
|
texts := graph.SourceTexts(prog)
|
|
63
|
-
origin, err := dumpOrigin(prog,
|
|
62
|
+
origin, err := dumpOrigin(prog, texts)
|
|
64
63
|
if err != nil {
|
|
65
64
|
fmt.Fprintf(stderr, "ttscgraph: %v\n", err)
|
|
66
65
|
return 1
|
|
@@ -90,7 +89,7 @@ func runDump(args []string) int {
|
|
|
90
89
|
// saw the project, so it carries the same provenance a served snapshot does:
|
|
91
90
|
// without it the file is a pile of facts with no way to tell which program, or
|
|
92
91
|
// which day, they describe.
|
|
93
|
-
func dumpOrigin(prog *driver.Program,
|
|
92
|
+
func dumpOrigin(prog *driver.Program, texts map[string]string) (graph.DumpOrigin, error) {
|
|
94
93
|
configs, err := parsedConfigs(prog)
|
|
95
94
|
if err != nil {
|
|
96
95
|
return graph.DumpOrigin{}, err
|
|
@@ -105,7 +104,6 @@ func dumpOrigin(prog *driver.Program, cwd string, texts map[string]string) (grap
|
|
|
105
104
|
}
|
|
106
105
|
return graph.DumpOrigin{
|
|
107
106
|
Provenance: graph.NewProvenance(
|
|
108
|
-
cwd,
|
|
109
107
|
serveProducer(),
|
|
110
108
|
fullSnapshotCapabilities,
|
|
111
109
|
fileDigests(configHashes),
|
|
@@ -113,6 +111,6 @@ func dumpOrigin(prog *driver.Program, cwd string, texts map[string]string) (grap
|
|
|
113
111
|
texts,
|
|
114
112
|
diskDigests,
|
|
115
113
|
),
|
|
116
|
-
Diagnostics: graph.NewDiagnostics(prog
|
|
114
|
+
Diagnostics: graph.NewDiagnostics(prog),
|
|
117
115
|
}, nil
|
|
118
116
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"encoding/json"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"testing"
|
|
8
|
+
|
|
9
|
+
"github.com/samchon/ttsc/packages/ttsc/internal/graph"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// TestRunDumpEmitsOnlyCheckerVerifiedMemberRelations verifies the shipped dump
|
|
13
|
+
// command serializes native member facts and does not revive a rejected pair.
|
|
14
|
+
//
|
|
15
|
+
// The graph reader no longer synthesizes member edges from equal names, so the
|
|
16
|
+
// command boundary must prove all three layers together: compiler diagnostics,
|
|
17
|
+
// native pair validation, and the public implements/overrides wire vocabulary.
|
|
18
|
+
//
|
|
19
|
+
// 1. Dump a program with one valid and one TS2416-invalid implementation.
|
|
20
|
+
// 2. Locate the container and member nodes in the serialized document.
|
|
21
|
+
// 3. Require the valid member edge and its evidence, reject the invalid edge,
|
|
22
|
+
// and ensure the internal native edge kind never reaches JSON.
|
|
23
|
+
func TestRunDumpEmitsOnlyCheckerVerifiedMemberRelations(t *testing.T) {
|
|
24
|
+
root := t.TempDir()
|
|
25
|
+
writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
|
|
26
|
+
"compilerOptions": {
|
|
27
|
+
"target": "ES2022",
|
|
28
|
+
"module": "commonjs",
|
|
29
|
+
"strict": true,
|
|
30
|
+
"rootDir": "src",
|
|
31
|
+
"outDir": "dist"
|
|
32
|
+
},
|
|
33
|
+
"files": ["src/main.ts"]
|
|
34
|
+
}
|
|
35
|
+
`)
|
|
36
|
+
writeGraphFile(t, filepath.Join(root, "src", "main.ts"), `export interface Contract {
|
|
37
|
+
execute(input: number): void;
|
|
38
|
+
}
|
|
39
|
+
export class Good implements Contract {
|
|
40
|
+
execute(input: number): void {}
|
|
41
|
+
}
|
|
42
|
+
export class Bad implements Contract {
|
|
43
|
+
execute(input: string): void {}
|
|
44
|
+
}
|
|
45
|
+
`)
|
|
46
|
+
|
|
47
|
+
oldStdout, oldStderr := stdout, stderr
|
|
48
|
+
defer func() { stdout, stderr = oldStdout, oldStderr }()
|
|
49
|
+
var out, errBuf bytes.Buffer
|
|
50
|
+
stdout, stderr = &out, &errBuf
|
|
51
|
+
|
|
52
|
+
if code := run([]string{"dump", "--cwd", root, "--tsconfig", "tsconfig.json"}); code != 0 {
|
|
53
|
+
t.Fatalf("run dump exit = %d, want 0; stderr:\n%s", code, errBuf.String())
|
|
54
|
+
}
|
|
55
|
+
if bytes.Contains(out.Bytes(), []byte("member-relation")) {
|
|
56
|
+
t.Fatalf("internal edge kind leaked into the public dump:\n%s", out.String())
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
var dump graph.Dump
|
|
60
|
+
if err := json.Unmarshal(out.Bytes(), &dump); err != nil {
|
|
61
|
+
t.Fatalf("dump output is not valid JSON: %v\n%s", err, out.String())
|
|
62
|
+
}
|
|
63
|
+
seen2416 := false
|
|
64
|
+
for _, diagnostic := range dump.Diagnostics {
|
|
65
|
+
seen2416 = seen2416 || diagnostic.Code == 2416
|
|
66
|
+
}
|
|
67
|
+
if !seen2416 {
|
|
68
|
+
t.Fatalf("fixture must retain TS2416 in the same snapshot: %v", dump.Diagnostics)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
node := func(name, kind string) *graph.DumpNode {
|
|
72
|
+
t.Helper()
|
|
73
|
+
for i := range dump.Nodes {
|
|
74
|
+
candidate := &dump.Nodes[i]
|
|
75
|
+
declaredName := candidate.QualifiedName
|
|
76
|
+
if declaredName == "" {
|
|
77
|
+
declaredName = candidate.Name
|
|
78
|
+
}
|
|
79
|
+
if declaredName == name && candidate.Kind == kind {
|
|
80
|
+
return candidate
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
t.Fatalf("missing %s %s; nodes: %v", kind, name, dump.Nodes)
|
|
84
|
+
return nil
|
|
85
|
+
}
|
|
86
|
+
contract := node("Contract", "interface")
|
|
87
|
+
good := node("Good", "class")
|
|
88
|
+
contractExecute := node("Contract.execute", "method")
|
|
89
|
+
goodExecute := node("Good.execute", "method")
|
|
90
|
+
badExecute := node("Bad.execute", "method")
|
|
91
|
+
|
|
92
|
+
countEdge := func(from, to, kind string) int {
|
|
93
|
+
count := 0
|
|
94
|
+
for _, edge := range dump.Edges {
|
|
95
|
+
if edge.From == from && edge.To == to && edge.Kind == kind {
|
|
96
|
+
count++
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return count
|
|
100
|
+
}
|
|
101
|
+
if count := countEdge(good.ID, contract.ID, "implements"); count != 1 {
|
|
102
|
+
t.Fatalf("container implementation edge count = %d, want 1; edges: %v", count, dump.Edges)
|
|
103
|
+
}
|
|
104
|
+
if count := countEdge(goodExecute.ID, contractExecute.ID, "implements"); count != 1 {
|
|
105
|
+
t.Fatalf("valid member implementation edge count = %d, want 1; edges: %v", count, dump.Edges)
|
|
106
|
+
}
|
|
107
|
+
if count := countEdge(badExecute.ID, contractExecute.ID, "implements"); count != 0 {
|
|
108
|
+
t.Fatalf("checker-invalid member implementation edge count = %d, want 0; edges: %v", count, dump.Edges)
|
|
109
|
+
}
|
|
110
|
+
for _, edge := range dump.Edges {
|
|
111
|
+
if edge.From == goodExecute.ID && edge.To == contractExecute.ID && edge.Kind == "implements" {
|
|
112
|
+
if edge.Evidence == nil || goodExecute.Evidence == nil ||
|
|
113
|
+
edge.Evidence.StartLine != goodExecute.Evidence.StartLine {
|
|
114
|
+
t.Fatalf("member edge evidence does not identify Good.execute: edge=%v node=%v", edge.Evidence, goodExecute.Evidence)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|