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
|
@@ -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
|
+
}
|