ttsc 0.19.3 → 0.20.1

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.
Files changed (170) hide show
  1. package/README.md +36 -34
  2. package/cmd/graphdump/main.go +2 -4
  3. package/cmd/ttsc/filter.go +12 -4
  4. package/cmd/ttsc/filter_resolves_flag_names_case_insensitively_test.go +53 -0
  5. package/cmd/ttsc/flags_gen.go +3 -3
  6. package/cmd/ttscgraph/dump.go +5 -7
  7. package/cmd/ttscgraph/run_dump_emits_only_checker_verified_member_relations_test.go +118 -0
  8. package/cmd/ttscgraph/serve.go +58 -308
  9. package/cmd/ttscgraph/serve_session_keeps_base_url_fallback_unchanged_test.go +49 -0
  10. package/cmd/ttscgraph/serve_session_keeps_esm_directory_candidate_unchanged_test.go +61 -0
  11. package/cmd/ttscgraph/serve_session_keeps_exports_blocked_file_candidates_unchanged_test.go +64 -0
  12. package/cmd/ttscgraph/serve_session_keeps_inactive_conditional_export_unchanged_test.go +66 -0
  13. package/cmd/ttscgraph/serve_session_keeps_lower_priority_module_candidate_unchanged_test.go +67 -0
  14. package/cmd/ttscgraph/serve_session_keeps_lower_priority_paths_pattern_unchanged_test.go +58 -0
  15. package/cmd/ttscgraph/serve_session_keeps_nonlongest_root_dirs_candidate_unchanged_test.go +54 -0
  16. package/cmd/ttscgraph/serve_session_reloads_commonjs_package_entry_for_esm_test.go +50 -0
  17. package/cmd/ttscgraph/serve_session_reloads_module_suffix_candidate_test.go +41 -0
  18. package/cmd/ttscgraph/serve_session_reloads_superseding_module_candidates_test.go +216 -0
  19. package/cmd/ttscgraph/serve_session_retries_failed_dump_test.go +103 -0
  20. package/driver/graph.go +12 -6
  21. package/driver/lsp.go +30 -0
  22. package/driver/program.go +37 -1
  23. package/driver/resolution_candidates.go +819 -0
  24. package/driver/windowsjunction/windowsjunction.go +40 -0
  25. package/go.mod +2 -0
  26. package/internal/graph/build.go +45 -11
  27. package/internal/graph/dump.go +276 -100
  28. package/internal/graph/dump_checkout_paths_are_stable_test.go +210 -0
  29. package/internal/graph/dump_diagnostics_ride_the_generation_that_built_it_test.go +9 -3
  30. package/internal/graph/dump_paths.go +172 -0
  31. package/internal/graph/dump_paths_are_portable_and_injective_test.go +93 -0
  32. package/internal/graph/dump_uses_ecma_line_terminators_test.go +87 -0
  33. package/internal/graph/duplicate_variable_object_members_keep_first_declaration_test.go +61 -0
  34. package/internal/graph/edges.go +208 -0
  35. package/internal/graph/graph.go +143 -5
  36. package/internal/graph/line_model.go +122 -0
  37. package/internal/graph/member_relation_edges_follow_checker_validity_test.go +85 -0
  38. package/internal/graph/member_relation_edges_preserve_checker_boundaries_test.go +191 -0
  39. package/internal/graph/member_relation_edges_reject_checker_invalid_pairs_test.go +119 -0
  40. package/internal/graph/node_ids_preserve_hash_bearing_components_test.go +41 -0
  41. package/internal/graph/node_modifiers_emit_union_strings_test.go +4 -1
  42. package/internal/graph/object_literal_members_ride_on_the_snapshot_test.go +192 -0
  43. package/internal/graph/object_member_signatures_preserve_literal_whitespace_test.go +67 -0
  44. package/internal/graph/object_members.go +229 -0
  45. package/internal/graph/provenance.go +88 -30
  46. package/internal/graph/source_texts_cover_every_resident_program_source_test.go +73 -0
  47. package/internal/graphsymbols/position_helpers_follow_ecma_line_terminators_test.go +66 -0
  48. package/internal/graphsymbols/provider.go +26 -91
  49. package/internal/lspserver/lsp_clean_didopen_refreshes_compiler_state_test.go +144 -0
  50. package/internal/lspserver/lsp_completion.go +109 -0
  51. package/internal/lspserver/lsp_completion_gate_never_refuses_a_matching_line_test.go +50 -0
  52. package/internal/lspserver/lsp_completion_hints_longest_trigger_wins_test.go +149 -0
  53. package/internal/lspserver/lsp_completion_hints_nearest_trigger_wins_test.go +37 -0
  54. package/internal/lspserver/lsp_completion_merges_into_upstream_response_test.go +149 -0
  55. package/internal/lspserver/lsp_completion_nearest_hint_replaces_nearest_filter_test.go +43 -0
  56. package/internal/lspserver/lsp_completion_preserves_completion_list_extensions_test.go +99 -0
  57. package/internal/lspserver/lsp_completion_refuses_hints_inside_a_string_literal_test.go +69 -0
  58. package/internal/lspserver/lsp_completion_replaces_full_filter_in_rank_order_test.go +85 -0
  59. package/internal/lspserver/lsp_completion_request.go +455 -0
  60. package/internal/lspserver/lsp_completion_scope.go +530 -0
  61. package/internal/lspserver/lsp_completion_scope_bench_test.go +41 -0
  62. package/internal/lspserver/lsp_completion_scope_classifies_regex_statement_and_operator_boundaries_test.go +39 -0
  63. package/internal/lspserver/lsp_completion_scope_ignores_jsdoc_bytes_in_literals_test.go +108 -0
  64. package/internal/lspserver/lsp_completion_scope_refuses_regex_hints_test.go +52 -0
  65. package/internal/lspserver/lsp_completion_trigger_uses_last_rune_test.go +24 -0
  66. package/internal/lspserver/lsp_deleted_watched_file_withdraws_plugin_diagnostics_test.go +110 -0
  67. package/internal/lspserver/lsp_didsave_keeps_the_incremental_changed_uri_path_test.go +78 -0
  68. package/internal/lspserver/lsp_hints_absence_is_not_an_error_test.go +46 -0
  69. package/internal/lspserver/lsp_hints_refresh_coalesces_concurrent_requests_test.go +82 -0
  70. package/internal/lspserver/lsp_hints_refresh_ignores_a_superseded_generation_test.go +40 -0
  71. package/internal/lspserver/lsp_hints_refresh_keeps_each_producer_isolated_test.go +64 -0
  72. package/internal/lspserver/lsp_hints_refresh_replaces_the_session_snapshot_test.go +66 -0
  73. package/internal/lspserver/lsp_hints_refresh_scheduled_by_editor_events_test.go +90 -0
  74. package/internal/lspserver/lsp_hints_refresh_serves_completion_while_running_test.go +91 -0
  75. package/internal/lspserver/lsp_index_byte_from_bounds_its_search_start_test.go +38 -0
  76. package/internal/lspserver/lsp_initialize_constrains_position_encoding_to_utf16_test.go +205 -0
  77. package/internal/lspserver/lsp_late_completion_trigger_is_reported_once_test.go +105 -0
  78. package/internal/lspserver/lsp_native_completion_hints_decode_flat_lint_wire_test.go +66 -0
  79. package/internal/lspserver/lsp_native_completion_hints_grouped_compatibility_boundary_test.go +60 -0
  80. package/internal/lspserver/lsp_native_plugin_source.go +315 -2
  81. package/internal/lspserver/lsp_native_resident.go +304 -0
  82. package/internal/lspserver/lsp_plugin.go +85 -8
  83. package/internal/lspserver/lsp_plugin_completion_resolve_stays_local_test.go +82 -0
  84. package/internal/lspserver/lsp_position_conversions_span_astral_and_combining_runes_test.go +119 -0
  85. package/internal/lspserver/lsp_proxy.go +601 -9
  86. package/internal/lspserver/lsp_refresh.go +70 -0
  87. package/internal/lspserver/lsp_resident_falls_back_when_serve_unsupported_test.go +43 -0
  88. package/internal/lspserver/lsp_resident_maps_verb_args_test.go +34 -0
  89. package/internal/lspserver/lsp_server.go +13 -1
  90. package/internal/lspserver/lsp_watched_file_changes_reach_compiler_caches_test.go +172 -0
  91. package/lib/TtscCompiler.d.ts +3 -1
  92. package/lib/TtscCompiler.js +5 -1
  93. package/lib/TtscCompiler.js.map +1 -1
  94. package/lib/TtscService.d.ts +13 -3
  95. package/lib/TtscService.js +6 -6
  96. package/lib/TtscService.js.map +1 -1
  97. package/lib/compiler/internal/project/readConfigJson.d.ts +30 -0
  98. package/lib/compiler/internal/project/readConfigJson.js +208 -0
  99. package/lib/compiler/internal/project/readConfigJson.js.map +1 -0
  100. package/lib/compiler/internal/project/readProjectConfig.js +53 -149
  101. package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
  102. package/lib/compiler/internal/residentTransformProcess.d.ts +15 -2
  103. package/lib/compiler/internal/residentTransformProcess.js +159 -39
  104. package/lib/compiler/internal/residentTransformProcess.js.map +1 -1
  105. package/lib/compiler/internal/runBuild.d.ts +6 -0
  106. package/lib/compiler/internal/runBuild.js +143 -44
  107. package/lib/compiler/internal/runBuild.js.map +1 -1
  108. package/lib/compiler/internal/startResidentTransform.d.ts +5 -1
  109. package/lib/compiler/internal/startResidentTransform.js +3 -1
  110. package/lib/compiler/internal/startResidentTransform.js.map +1 -1
  111. package/lib/compiler/internal/transformProjectInMemory.js +12 -2
  112. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  113. package/lib/flags/parser.d.ts +19 -0
  114. package/lib/flags/parser.js +78 -36
  115. package/lib/flags/parser.js.map +1 -1
  116. package/lib/flags/schema.d.ts +49 -11
  117. package/lib/flags/schema.js +94 -22
  118. package/lib/flags/schema.js.map +1 -1
  119. package/lib/internal/assertSafeExplicitCacheDirectory.d.ts +5 -0
  120. package/lib/internal/assertSafeExplicitCacheDirectory.js +26 -0
  121. package/lib/internal/assertSafeExplicitCacheDirectory.js.map +1 -0
  122. package/lib/launcher/internal/runTtsc.js +107 -69
  123. package/lib/launcher/internal/runTtsc.js.map +1 -1
  124. package/lib/launcher/internal/runTtsx.js +28 -41
  125. package/lib/launcher/internal/runTtsx.js.map +1 -1
  126. package/lib/launcher/internal/runtimeHooks.d.ts +15 -0
  127. package/lib/launcher/internal/runtimeHooks.js +18 -1
  128. package/lib/launcher/internal/runtimeHooks.js.map +1 -1
  129. package/lib/launcher/internal/watchTopology.d.ts +43 -0
  130. package/lib/launcher/internal/watchTopology.js +360 -0
  131. package/lib/launcher/internal/watchTopology.js.map +1 -0
  132. package/lib/plugin/internal/buildSourcePlugin.d.ts +14 -3
  133. package/lib/plugin/internal/buildSourcePlugin.js +247 -61
  134. package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
  135. package/lib/plugin/internal/loadProjectPlugins.js +63 -2
  136. package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
  137. package/lib/structures/ITtscCompilerTransformation.d.ts +16 -0
  138. package/lib/structures/internal/ITtscParsedProjectConfig.d.ts +2 -0
  139. package/package.json +8 -8
  140. package/shim/ast/shim.go +48 -3
  141. package/shim/astnav/go.mod +14 -0
  142. package/shim/astnav/go.sum +20 -0
  143. package/shim/astnav/shim.go +15 -0
  144. package/shim/checker/shim.go +100 -0
  145. package/shim/core/shim.go +47 -0
  146. package/shim/diagnosticwriter/lint.go +50 -0
  147. package/src/TtscCompiler.ts +5 -1
  148. package/src/TtscService.ts +37 -10
  149. package/src/compiler/internal/project/readConfigJson.ts +209 -0
  150. package/src/compiler/internal/project/readProjectConfig.ts +62 -158
  151. package/src/compiler/internal/residentTransformProcess.ts +197 -39
  152. package/src/compiler/internal/runBuild.ts +169 -47
  153. package/src/compiler/internal/startResidentTransform.ts +12 -1
  154. package/src/compiler/internal/transformProjectInMemory.ts +12 -1
  155. package/src/flags/parser.ts +113 -42
  156. package/src/flags/schema.ts +113 -31
  157. package/src/internal/assertSafeExplicitCacheDirectory.ts +29 -0
  158. package/src/launcher/internal/runTtsc.ts +117 -68
  159. package/src/launcher/internal/runTtsx.ts +29 -44
  160. package/src/launcher/internal/runtimeHooks.ts +30 -2
  161. package/src/launcher/internal/watchTopology.ts +466 -0
  162. package/src/plugin/internal/buildSourcePlugin.ts +326 -64
  163. package/src/plugin/internal/loadProjectPlugins.ts +80 -2
  164. package/src/structures/ITtscCompilerTransformation.ts +17 -0
  165. package/src/structures/internal/ITtscParsedProjectConfig.ts +2 -0
  166. package/tools/shim_audit/baseline.json +0 -3
  167. package/tools/shim_audit/main.go +42 -14
  168. package/tools/shim_audit/shim_dir_coverage_test.go +25 -0
  169. package/utility/flags_gen.go +3 -3
  170. package/utility/host.go +9 -3
@@ -0,0 +1,191 @@
1
+ package graph
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+
7
+ "github.com/samchon/ttsc/packages/ttsc/driver"
8
+ )
9
+
10
+ // TestMemberRelationEdgesPreserveCheckerBoundaries verifies the pair query
11
+ // retains instantiated, overloaded, optional, declaration-merged, mixed-symbol,
12
+ // and class-override semantics.
13
+ //
14
+ // These boundaries are exactly where comparing member names, syntax kinds, or
15
+ // already-extracted property types diverges from the TypeScript checker. The
16
+ // fixture intentionally contains diagnostics; every pair is judged locally so
17
+ // valid siblings and unrelated valid declarations still produce edges.
18
+ //
19
+ // 1. Build generic, overloaded, optional, merged, property/method, and
20
+ // accessor cases.
21
+ // 2. Require checker diagnostics for the deliberately rejected declarations.
22
+ // 3. Assert accepted pairs produce one member relation and rejected pairs none.
23
+ func TestMemberRelationEdgesPreserveCheckerBoundaries(t *testing.T) {
24
+ root := t.TempDir()
25
+ writeFile(t, filepath.Join(root, "tsconfig.json"), fixtureTSConfig)
26
+ writeFile(t, filepath.Join(root, "src", "main.ts"), `export interface Generic<T> {
27
+ map(input: T): T;
28
+ required: T;
29
+ }
30
+ export class StringGeneric implements Generic<string> {
31
+ map(input: string): string { return input; }
32
+ required = "valid";
33
+ }
34
+ export class WrongGeneric implements Generic<string> {
35
+ map(input: number): number { return input; }
36
+ required = "valid sibling";
37
+ }
38
+
39
+ export interface Parser {
40
+ parse(input: string): string;
41
+ parse(input: number): number;
42
+ }
43
+ export class ParserImpl implements Parser {
44
+ parse(input: string): string;
45
+ parse(input: number): number;
46
+ parse(input: string | number): string | number { return input; }
47
+ }
48
+
49
+ export interface OptionalContract {
50
+ value?: string;
51
+ }
52
+ export class RequiredGood implements OptionalContract {
53
+ value = "valid";
54
+ }
55
+ export interface RequiredContract {
56
+ value: string;
57
+ }
58
+ export class OptionalWrong implements RequiredContract {
59
+ value?: string;
60
+ }
61
+
62
+ export class MethodBase {
63
+ run(): void {}
64
+ }
65
+ export class PropertyDerived extends MethodBase {
66
+ run = (): void => {};
67
+ }
68
+ export class PropertyBase {
69
+ run: () => void = () => {};
70
+ }
71
+ export class MethodWrong extends PropertyBase {
72
+ run(): void {}
73
+ }
74
+
75
+ export abstract class AbstractAccessorBase {
76
+ abstract get label(): string;
77
+ }
78
+ export class AbstractPropertyDerived extends AbstractAccessorBase {
79
+ label = "valid";
80
+ }
81
+ export class ConcreteAccessorBase {
82
+ get label(): string { return "base"; }
83
+ }
84
+ export class ConcretePropertyWrong extends ConcreteAccessorBase {
85
+ label = "invalid";
86
+ }
87
+
88
+ export class MixedFlagBase {
89
+ item = "base";
90
+ }
91
+ export class MixedFlagDerived extends MixedFlagBase {
92
+ get item(): string { return "invalid"; }
93
+ }
94
+ export interface MixedFlagDerived {
95
+ item: string;
96
+ }
97
+
98
+ export interface SplitBase {
99
+ alpha(): string;
100
+ }
101
+ export interface SplitBase {
102
+ beta(): string;
103
+ }
104
+ export interface SplitDerived extends SplitBase {
105
+ alpha(): string;
106
+ }
107
+ export interface SplitDerived {
108
+ beta(): string;
109
+ }
110
+
111
+ export class MergedBase {}
112
+ export interface MergedBase {
113
+ augment(): void;
114
+ }
115
+ export class MergedDerived extends MergedBase {
116
+ augment(): void {}
117
+ }
118
+
119
+ export class ProtectedBase {
120
+ protected token = "base";
121
+ }
122
+ export class ProtectedGood extends ProtectedBase {
123
+ protected token = "valid";
124
+ }
125
+ export class ProtectedPretender implements ProtectedBase {
126
+ protected token = "invalid";
127
+ }
128
+ `)
129
+
130
+ prog, loadDiags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
131
+ if err != nil {
132
+ t.Fatal(err)
133
+ }
134
+ if len(loadDiags) != 0 {
135
+ t.Fatalf("unexpected load diagnostics: %v", loadDiags)
136
+ }
137
+ defer func() { _ = prog.Close() }()
138
+ if diagnostics := prog.Diagnostics(); len(diagnostics) == 0 {
139
+ t.Fatal("fixture must contain checker-rejected boundary cases")
140
+ }
141
+
142
+ built := Build(prog)
143
+ path := sourceFile(t, prog, "main.ts").FileName()
144
+ for _, id := range []string{
145
+ nodeID(path, "MixedFlagDerived.item", NodeMethod),
146
+ nodeID(path, "MixedFlagBase.item", NodeVariable),
147
+ } {
148
+ if _, ok := built.Nodes[id]; !ok {
149
+ t.Fatalf("mixed-symbol boundary node %s is missing; nodes: %v", id, built.Nodes)
150
+ }
151
+ }
152
+ expected := []struct {
153
+ from string
154
+ to string
155
+ origin string
156
+ }{
157
+ {nodeID(path, "StringGeneric.map", NodeMethod), nodeID(path, "Generic.map", NodeMethod), "implements"},
158
+ {nodeID(path, "StringGeneric.required", NodeVariable), nodeID(path, "Generic.required", NodeVariable), "implements"},
159
+ {nodeID(path, "WrongGeneric.required", NodeVariable), nodeID(path, "Generic.required", NodeVariable), "implements"},
160
+ {nodeID(path, "ParserImpl.parse", NodeMethod), nodeID(path, "Parser.parse", NodeMethod), "implements"},
161
+ {nodeID(path, "RequiredGood.value", NodeVariable), nodeID(path, "OptionalContract.value", NodeVariable), "implements"},
162
+ {nodeID(path, "PropertyDerived.run", NodeVariable), nodeID(path, "MethodBase.run", NodeMethod), "overrides"},
163
+ {nodeID(path, "AbstractPropertyDerived.label", NodeVariable), nodeID(path, "AbstractAccessorBase.label", NodeMethod), "overrides"},
164
+ {nodeID(path, "SplitDerived.alpha", NodeMethod), nodeID(path, "SplitBase.alpha", NodeMethod), "overrides"},
165
+ {nodeID(path, "SplitDerived.beta", NodeMethod), nodeID(path, "SplitBase.beta", NodeMethod), "overrides"},
166
+ {nodeID(path, "MergedDerived.augment", NodeMethod), nodeID(path, "MergedBase.augment", NodeMethod), "overrides"},
167
+ {nodeID(path, "ProtectedGood.token", NodeVariable), nodeID(path, "ProtectedBase.token", NodeVariable), "overrides"},
168
+ }
169
+ for _, pair := range expected {
170
+ if got := edgeOrigin(built, pair.from, pair.to, EdgeMemberRelation); got != pair.origin {
171
+ t.Fatalf("%s -> %s: want %q, got %q; edges: %v", pair.from, pair.to, pair.origin, got, built.Edges)
172
+ }
173
+ }
174
+
175
+ rejected := []struct {
176
+ from string
177
+ to string
178
+ }{
179
+ {nodeID(path, "WrongGeneric.map", NodeMethod), nodeID(path, "Generic.map", NodeMethod)},
180
+ {nodeID(path, "OptionalWrong.value", NodeVariable), nodeID(path, "RequiredContract.value", NodeVariable)},
181
+ {nodeID(path, "MethodWrong.run", NodeMethod), nodeID(path, "PropertyBase.run", NodeVariable)},
182
+ {nodeID(path, "ConcretePropertyWrong.label", NodeVariable), nodeID(path, "ConcreteAccessorBase.label", NodeMethod)},
183
+ {nodeID(path, "MixedFlagDerived.item", NodeMethod), nodeID(path, "MixedFlagBase.item", NodeVariable)},
184
+ {nodeID(path, "ProtectedPretender.token", NodeVariable), nodeID(path, "ProtectedBase.token", NodeVariable)},
185
+ }
186
+ for _, pair := range rejected {
187
+ if hasEdge(built, pair.from, pair.to, EdgeMemberRelation) {
188
+ t.Fatalf("checker-rejected boundary gained a member edge %s -> %s; edges: %v", pair.from, pair.to, built.Edges)
189
+ }
190
+ }
191
+ }
@@ -0,0 +1,119 @@
1
+ package graph
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+
7
+ "github.com/samchon/ttsc/packages/ttsc/driver"
8
+ )
9
+
10
+ // TestMemberRelationEdgesRejectCheckerInvalidPairs verifies that an invalid
11
+ // heritage relation cannot still manufacture authoritative member edges.
12
+ //
13
+ // The former TypeScript-memory synthesis compared only names and the broad
14
+ // method/property set, so TS2416 and TS2425 diagnostics coexisted with
15
+ // `implements`/`overrides` edges and those edges could become runtime dispatch
16
+ // hops. A valid class in the same erroneous Program is the negative twin: an
17
+ // unrelated diagnostic must not globally disable checker-owned relations.
18
+ //
19
+ // 1. Compile invalid method/property, same-kind signature, and static/instance
20
+ // implementations plus an invalid class override and one valid class.
21
+ // 2. Require the checker diagnostics that prove the fixture is rejected.
22
+ // 3. Assert invalid pairs have no member edges while the valid class still
23
+ // carries its checker-backed implementation edges.
24
+ func TestMemberRelationEdgesRejectCheckerInvalidPairs(t *testing.T) {
25
+ root := t.TempDir()
26
+ writeFile(t, filepath.Join(root, "tsconfig.json"), fixtureTSConfig)
27
+ writeFile(t, filepath.Join(root, "src", "main.ts"), `export interface Contract {
28
+ kept: string;
29
+ value: string;
30
+ run(input: string): string;
31
+ }
32
+ export class WrongKind implements Contract {
33
+ kept = "checker-valid sibling";
34
+ value(): void {}
35
+ run(input: number): number { return input; }
36
+ }
37
+ export class StaticOnly implements Contract {
38
+ static kept = "static";
39
+ static value = "static";
40
+ static run(input: string): string { return input; }
41
+ }
42
+ export class Valid implements Contract {
43
+ kept = "valid";
44
+ value = "valid";
45
+ run(input: string): string { return input; }
46
+ }
47
+ export class Base {
48
+ kept = "base";
49
+ value = "base";
50
+ run(input: string): string { return input; }
51
+ }
52
+ export class InvalidDerived extends Base {
53
+ kept = "checker-valid sibling";
54
+ value(): void {}
55
+ run(input: number): number { return input; }
56
+ }
57
+ `)
58
+
59
+ prog, loadDiags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
60
+ if err != nil {
61
+ t.Fatal(err)
62
+ }
63
+ if len(loadDiags) != 0 {
64
+ t.Fatalf("unexpected load diagnostics: %v", loadDiags)
65
+ }
66
+ defer func() { _ = prog.Close() }()
67
+
68
+ diagnostics := prog.Diagnostics()
69
+ seen2416 := false
70
+ seen2425 := false
71
+ for _, diagnostic := range diagnostics {
72
+ seen2416 = seen2416 || diagnostic.Code == 2416
73
+ seen2425 = seen2425 || diagnostic.Code == 2425
74
+ }
75
+ if !seen2416 || !seen2425 {
76
+ t.Fatalf("fixture must prove incompatible member diagnostics TS2416 and TS2425; got %v", diagnostics)
77
+ }
78
+
79
+ built := Build(prog)
80
+ path := sourceFile(t, prog, "main.ts").FileName()
81
+ absent := []struct {
82
+ from string
83
+ to string
84
+ }{
85
+ {nodeID(path, "WrongKind.value", NodeMethod), nodeID(path, "Contract.value", NodeVariable)},
86
+ {nodeID(path, "WrongKind.run", NodeMethod), nodeID(path, "Contract.run", NodeMethod)},
87
+ {nodeID(path, "StaticOnly.value", NodeVariable), nodeID(path, "Contract.value", NodeVariable)},
88
+ {nodeID(path, "StaticOnly.run", NodeMethod), nodeID(path, "Contract.run", NodeMethod)},
89
+ {nodeID(path, "InvalidDerived.value", NodeMethod), nodeID(path, "Base.value", NodeVariable)},
90
+ {nodeID(path, "InvalidDerived.run", NodeMethod), nodeID(path, "Base.run", NodeMethod)},
91
+ }
92
+ for _, pair := range absent {
93
+ if hasEdge(built, pair.from, pair.to, EdgeMemberRelation) {
94
+ t.Fatalf("checker-invalid pair gained an authoritative member edge %s -> %s; edges: %v", pair.from, pair.to, built.Edges)
95
+ }
96
+ }
97
+
98
+ valid := []struct {
99
+ from string
100
+ to string
101
+ }{
102
+ {nodeID(path, "WrongKind.kept", NodeVariable), nodeID(path, "Contract.kept", NodeVariable)},
103
+ {nodeID(path, "Valid.value", NodeVariable), nodeID(path, "Contract.value", NodeVariable)},
104
+ {nodeID(path, "Valid.run", NodeMethod), nodeID(path, "Contract.run", NodeMethod)},
105
+ }
106
+ for _, pair := range valid {
107
+ if got := edgeOrigin(built, pair.from, pair.to, EdgeMemberRelation); got != "implements" {
108
+ t.Fatalf("valid implementation pair %s -> %s lost its checker-owned edge: got %q; edges: %v", pair.from, pair.to, got, built.Edges)
109
+ }
110
+ }
111
+ if got := edgeOrigin(
112
+ built,
113
+ nodeID(path, "InvalidDerived.kept", NodeVariable),
114
+ nodeID(path, "Base.kept", NodeVariable),
115
+ EdgeMemberRelation,
116
+ ); got != "overrides" {
117
+ t.Fatalf("valid override sibling in an invalid class was suppressed: got %q; edges: %v", got, built.Edges)
118
+ }
119
+ }
@@ -0,0 +1,41 @@
1
+ package graph
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+ )
7
+
8
+ // TestNodeIDsPreserveHashBearingComponents verifies graph identity: quoted
9
+ // path and symbol hashes remain structured facts through dump relativization.
10
+ //
11
+ // A source path and an authored private member may both contain '#'. Splitting
12
+ // the display id at either raw occurrence made the dump lose its project
13
+ // relative path and made consumers read the member as an id separator. The
14
+ // codec must escape those components while keeping ordinary ids byte-identical.
15
+ //
16
+ // 1. Build ordinary and hash-bearing ids from their raw structured facts.
17
+ // 2. Relativize the hash-bearing id as the dump does.
18
+ // 3. Recover its raw file component and assert the escaped wire spelling.
19
+ func TestNodeIDsPreserveHashBearingComponents(t *testing.T) {
20
+ if got, want := nodeID("src/main.ts", "main", NodeFunction), "src/main.ts#main:function"; got != want {
21
+ t.Fatalf("ordinary id = %q, want %q", got, want)
22
+ }
23
+ if got, want := nodeID(`C:\work\a#b\main.ts`, "label:part", NodeVariable), `C:\\work\\a\#b\\main.ts#label:part:variable`; got != want {
24
+ t.Fatalf("escaped id = %q, want %q", got, want)
25
+ } else if parts, ok := parseNodeID(got); !ok || parts.path != `C:\work\a#b\main.ts` || parts.name != "label:part" {
26
+ t.Fatalf("decoded id = %#v, %t; want raw path and name", parts, ok)
27
+ }
28
+
29
+ root := filepath.Join(t.TempDir(), "a#b")
30
+ file := filepath.Join(root, "src#generated", "main#file.ts")
31
+ id := nodeID(file, "Counter.#count", NodeVariable)
32
+ context := newDumpContext(root, nil)
33
+ got := context.relID(id)
34
+ want := "src\\#generated/main\\#file.ts#Counter.\\#count:variable"
35
+ if got != want {
36
+ t.Fatalf("relativized id = %q, want %q", got, want)
37
+ }
38
+ if file := nodeFile(got); file != "src#generated/main#file.ts" {
39
+ t.Fatalf("decoded file = %q, want raw hash-bearing relative path", file)
40
+ }
41
+ }
@@ -47,7 +47,10 @@ export const enum Mode {
47
47
  defer func() { _ = prog.Close() }()
48
48
 
49
49
  g := Build(prog)
50
- dump := NewDump(g, root, "tsconfig.json", nil, SourceTexts(prog), DumpOrigin{})
50
+ dump, err := NewDump(g, root, "tsconfig.json", nil, SourceTexts(prog), DumpOrigin{})
51
+ if err != nil {
52
+ t.Fatal(err)
53
+ }
51
54
 
52
55
  byID := make(map[string]DumpNode, len(dump.Nodes))
53
56
  for _, n := range dump.Nodes {
@@ -0,0 +1,192 @@
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
+ // TestObjectLiteralMembersRideOnTheSnapshot verifies that object-literal
12
+ // member identity is captured from the compiler AST and projected from the
13
+ // same source snapshot as the graph.
14
+ //
15
+ // The details server used to reopen the live file and reconstruct this list
16
+ // with a brace counter and regular expressions. A brace inside a block comment
17
+ // changed that counter, while valid shorthand, literal-keyed, computed, and
18
+ // multiline members depended on which line happened to match. This test pins
19
+ // the ownership boundary instead: the native graph records direct AST members,
20
+ // and the dump renders snapshot-owned, body-bounded compact signatures from
21
+ // Program-owned text.
22
+ //
23
+ // 1. Compile a wrapped object literal containing comment braces, every direct
24
+ // static member shape, a dynamic key, a spread, and a nested object.
25
+ // 2. Assert the variable node records only its direct statically named members
26
+ // in declaration order, with method/property kinds independent of trivia.
27
+ // 3. Dump the graph and assert member lines/signatures come from the snapshot,
28
+ // while spread and nested members are not fabricated as outer identity.
29
+ func TestObjectLiteralMembersRideOnTheSnapshot(t *testing.T) {
30
+ root := t.TempDir()
31
+ writeFile(t, filepath.Join(root, "tsconfig.json"), fixtureTSConfig)
32
+ source := `const shorthand = 1;
33
+ const dynamic = Math.random() > 0.5 ? "a" : "b";
34
+ const spread = { fromSpread: true };
35
+
36
+ export const shape = (({
37
+ /* { */
38
+ real: 1,
39
+ close: "}",
40
+ text: "{",
41
+ template: ` + "`{${shorthand}}`" + `,
42
+ long: "` + strings.Repeat("x", 200) + `",
43
+ shorthand,
44
+ ["static-key"]: 2,
45
+ [""]: 4,
46
+ [1]: true,
47
+ [dynamic]: 3,
48
+ method() {
49
+ return shorthand;
50
+ },
51
+ get value() {
52
+ return shorthand;
53
+ },
54
+ set value(input: number) {
55
+ void "SETTER_BODY_MUST_NOT_APPEAR";
56
+ },
57
+ oneLineMethod() { return "METHOD_BODY_MUST_NOT_APPEAR"; },
58
+ get oneLineValue() { return "ACCESSOR_BODY_MUST_NOT_APPEAR"; },
59
+ run: () => "ARROW_BODY_MUST_NOT_APPEAR",
60
+ classic: function () { return "FUNCTION_BODY_MUST_NOT_APPEAR"; },
61
+ klass: class { method() { return "CLASS_BODY_MUST_NOT_APPEAR"; } },
62
+ list: ["ARRAY_CONTENT_MUST_NOT_APPEAR"],
63
+ nested: {
64
+ inner: "NESTED_BODY_MUST_NOT_APPEAR",
65
+ },
66
+ ...spread,
67
+ /* } */
68
+ afterSpread: true,
69
+ }) as const) satisfies Record<PropertyKey, unknown>;
70
+ `
71
+ writeFile(t, filepath.Join(root, "src", "main.ts"), source)
72
+
73
+ prog, diags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
74
+ if err != nil {
75
+ t.Fatal(err)
76
+ }
77
+ if len(diags) != 0 {
78
+ t.Fatalf("unexpected diagnostics: %v", diags)
79
+ }
80
+ defer func() { _ = prog.Close() }()
81
+
82
+ graph := Build(prog)
83
+ path := sourceFile(t, prog, "main.ts").FileName()
84
+ node := graph.Nodes[nodeID(path, "shape", NodeVariable)]
85
+ if node == nil {
86
+ t.Fatalf("missing shape variable; nodes: %v", nodeIDSet(graph))
87
+ }
88
+
89
+ want := []ObjectMember{
90
+ {Name: "real", Kind: NodeVariable},
91
+ {Name: "close", Kind: NodeVariable},
92
+ {Name: "text", Kind: NodeVariable},
93
+ {Name: "template", Kind: NodeVariable},
94
+ {Name: "long", Kind: NodeVariable},
95
+ {Name: "shorthand", Kind: NodeVariable},
96
+ {Name: "static-key", Kind: NodeVariable},
97
+ {Name: "", Kind: NodeVariable},
98
+ {Name: "1", Kind: NodeVariable},
99
+ {Name: "method", Kind: NodeMethod},
100
+ {Name: "value", Kind: NodeMethod},
101
+ {Name: "value", Kind: NodeMethod},
102
+ {Name: "oneLineMethod", Kind: NodeMethod},
103
+ {Name: "oneLineValue", Kind: NodeMethod},
104
+ {Name: "run", Kind: NodeVariable},
105
+ {Name: "classic", Kind: NodeVariable},
106
+ {Name: "klass", Kind: NodeVariable},
107
+ {Name: "list", Kind: NodeVariable},
108
+ {Name: "nested", Kind: NodeVariable},
109
+ {Name: "afterSpread", Kind: NodeVariable},
110
+ }
111
+ if len(node.ObjectMembers) != len(want) {
112
+ t.Fatalf("object members = %v, want %v", node.ObjectMembers, want)
113
+ }
114
+ for i, expected := range want {
115
+ actual := node.ObjectMembers[i]
116
+ if actual.Name != expected.Name || actual.Kind != expected.Kind {
117
+ t.Fatalf("object member %d = %+v, want %+v", i, actual, expected)
118
+ }
119
+ if actual.Pos < 0 || actual.End <= actual.Pos {
120
+ t.Fatalf("object member %q has no snapshot span: %+v", actual.Name, actual)
121
+ }
122
+ }
123
+
124
+ dump, err := NewDump(graph, root, "tsconfig.json", nil, SourceTexts(prog), DumpOrigin{})
125
+ if err != nil {
126
+ t.Fatal(err)
127
+ }
128
+ var dumped *DumpNode
129
+ for i := range dump.Nodes {
130
+ if dump.Nodes[i].ID == "src/main.ts#shape:variable" {
131
+ dumped = &dump.Nodes[i]
132
+ break
133
+ }
134
+ }
135
+ if dumped == nil {
136
+ t.Fatalf("dump omitted shape: %+v", dump.Nodes)
137
+ }
138
+ if len(dumped.ObjectMembers) != len(want) {
139
+ t.Fatalf("dumped object members = %+v, want %d", dumped.ObjectMembers, len(want))
140
+ }
141
+ for _, member := range dumped.ObjectMembers {
142
+ if member.Line <= 0 || member.Signature == "" {
143
+ t.Fatalf("member lacks snapshot line/signature: %+v", member)
144
+ }
145
+ if member.Name == "inner" || member.Name == "fromSpread" || member.Name == "dynamic" {
146
+ t.Fatalf("non-direct or non-static member was fabricated: %+v", member)
147
+ }
148
+ }
149
+ if got := dumped.ObjectMembers[0]; got.Name != "real" || got.Line != 7 || got.Signature != "real: 1" {
150
+ t.Fatalf("comment brace corrupted the first real member: %+v", got)
151
+ }
152
+ if got := dumped.ObjectMembers[len(dumped.ObjectMembers)-1]; got.Name != "afterSpread" || got.Signature != "afterSpread: true" {
153
+ t.Fatalf("spread/comment boundary corrupted the following member: %+v", got)
154
+ }
155
+ signatures := map[string]string{}
156
+ for _, member := range dumped.ObjectMembers {
157
+ signatures[member.Name] = member.Signature
158
+ }
159
+ expectedSignatures := map[string]string{
160
+ "oneLineMethod": "oneLineMethod() {",
161
+ "oneLineValue": "get oneLineValue() {",
162
+ "run": "run: () =>",
163
+ "classic": "classic: function () {",
164
+ "klass": "klass: class",
165
+ "list": "list: [",
166
+ "nested": "nested: {",
167
+ }
168
+ for name, expected := range expectedSignatures {
169
+ if actual := signatures[name]; actual != expected {
170
+ t.Fatalf("signature for %q = %q, want %q", name, actual, expected)
171
+ }
172
+ }
173
+ for _, forbidden := range []string{
174
+ "METHOD_BODY_MUST_NOT_APPEAR",
175
+ "ACCESSOR_BODY_MUST_NOT_APPEAR",
176
+ "SETTER_BODY_MUST_NOT_APPEAR",
177
+ "ARROW_BODY_MUST_NOT_APPEAR",
178
+ "FUNCTION_BODY_MUST_NOT_APPEAR",
179
+ "CLASS_BODY_MUST_NOT_APPEAR",
180
+ "ARRAY_CONTENT_MUST_NOT_APPEAR",
181
+ "NESTED_BODY_MUST_NOT_APPEAR",
182
+ } {
183
+ for _, member := range dumped.ObjectMembers {
184
+ if strings.Contains(member.Signature, forbidden) {
185
+ t.Fatalf("signature inlined source body %q: %+v", forbidden, member)
186
+ }
187
+ }
188
+ }
189
+ if signature := signatures["long"]; len([]rune(signature)) != 160 || !strings.HasSuffix(signature, "...") {
190
+ t.Fatalf("long member signature is not bounded to 160 runes: %q", signature)
191
+ }
192
+ }
@@ -0,0 +1,67 @@
1
+ package graph
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+
7
+ "github.com/samchon/ttsc/packages/ttsc/driver"
8
+ )
9
+
10
+ // TestObjectMemberSignaturesPreserveLiteralWhitespace verifies that compact
11
+ // display text never rewrites whitespace owned by a lexical value.
12
+ //
13
+ // strings.Fields treated quoted and template contents as trivia. It changed
14
+ // string and regular-expression values, and its first-line cut left a multiline
15
+ // template without a closing backtick. The outline may compact surrounding
16
+ // syntax only when the literal itself remains byte-identical and complete.
17
+ //
18
+ // 1. Compile object members with spaced strings, a regexp, and a template.
19
+ // 2. Dump the graph from the Program-owned source snapshot.
20
+ // 3. Assert literal whitespace and the complete template delimiter survive.
21
+ func TestObjectMemberSignaturesPreserveLiteralWhitespace(t *testing.T) {
22
+ root := t.TempDir()
23
+ writeFile(t, filepath.Join(root, "tsconfig.json"), fixtureTSConfig)
24
+ source := `export const shape = {
25
+ double: "a b",
26
+ single: 'c d',
27
+ regexp: /e f/,
28
+ template: ` + "`left\n right`" + `,
29
+ };
30
+ `
31
+ writeFile(t, filepath.Join(root, "src", "main.ts"), source)
32
+
33
+ prog, diags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
34
+ if err != nil {
35
+ t.Fatal(err)
36
+ }
37
+ if len(diags) != 0 {
38
+ t.Fatalf("unexpected diagnostics: %v", diags)
39
+ }
40
+ defer func() { _ = prog.Close() }()
41
+
42
+ graph := Build(prog)
43
+ dump, err := NewDump(graph, root, "tsconfig.json", nil, SourceTexts(prog), DumpOrigin{})
44
+ if err != nil {
45
+ t.Fatal(err)
46
+ }
47
+ signatures := map[string]string{}
48
+ for _, node := range dump.Nodes {
49
+ if node.ID != "src/main.ts#shape:variable" {
50
+ continue
51
+ }
52
+ for _, member := range node.ObjectMembers {
53
+ signatures[member.Name] = member.Signature
54
+ }
55
+ }
56
+ expected := map[string]string{
57
+ "double": `double: "a b"`,
58
+ "single": `single: 'c d'`,
59
+ "regexp": `regexp: /e f/`,
60
+ "template": "template: `left\n right`",
61
+ }
62
+ for name, want := range expected {
63
+ if got := signatures[name]; got != want {
64
+ t.Fatalf("signature for %q = %q, want %q", name, got, want)
65
+ }
66
+ }
67
+ }