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,229 @@
|
|
|
1
|
+
package graph
|
|
2
|
+
|
|
3
|
+
import shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
4
|
+
|
|
5
|
+
// collectObjectMembers records the direct, statically named members of the
|
|
6
|
+
// object literal bound by declaration on the variable node Build already owns.
|
|
7
|
+
// The AST is the structure oracle: no source text, brace counter, or regex is
|
|
8
|
+
// involved in deciding which properties belong to the outer object.
|
|
9
|
+
func (g *Graph) collectObjectMembers(path string, declaration *shimast.Node) {
|
|
10
|
+
if declaration == nil || declaration.Symbol() == nil {
|
|
11
|
+
return
|
|
12
|
+
}
|
|
13
|
+
variable := declaration.AsVariableDeclaration()
|
|
14
|
+
if variable == nil {
|
|
15
|
+
return
|
|
16
|
+
}
|
|
17
|
+
object := objectLiteralInitializer(variable.Initializer)
|
|
18
|
+
if object == nil || object.Properties == nil {
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
node := g.Nodes[nodeID(path, qualifiedName(declaration.Symbol()), NodeVariable)]
|
|
22
|
+
if node == nil || node.Pos != declaration.Pos() || node.End != declaration.End() {
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
members := make([]ObjectMember, 0, len(object.Properties.Nodes))
|
|
26
|
+
for _, property := range object.Properties.Nodes {
|
|
27
|
+
name, kind, ok := objectMemberIdentity(property)
|
|
28
|
+
if !ok {
|
|
29
|
+
continue
|
|
30
|
+
}
|
|
31
|
+
signatureEnd, signatureBoundary, signatureTokenLen := objectMemberSignatureSpan(property)
|
|
32
|
+
members = append(members, ObjectMember{
|
|
33
|
+
Name: name,
|
|
34
|
+
Kind: kind,
|
|
35
|
+
Pos: property.Pos(),
|
|
36
|
+
End: property.End(),
|
|
37
|
+
SignatureEnd: signatureEnd,
|
|
38
|
+
SignatureBoundary: signatureBoundary,
|
|
39
|
+
SignatureTokenLen: signatureTokenLen,
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
if len(members) > 0 {
|
|
43
|
+
node.ObjectMembers = members
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// objectMemberSignatureSpan finds the earliest AST boundary after which source
|
|
48
|
+
// belongs to a value body rather than the member outline. It keeps compact
|
|
49
|
+
// scalar initializers such as `real: 1`, but stops before function bodies,
|
|
50
|
+
// arrow expressions, nested object members, and array contents can be copied
|
|
51
|
+
// onto the wire.
|
|
52
|
+
func objectMemberSignatureSpan(property *shimast.Node) (end int, boundary bool, tokenLen int) {
|
|
53
|
+
end = property.End()
|
|
54
|
+
candidate, found := sourceBodyBoundary(property)
|
|
55
|
+
if !found || candidate.end <= property.Pos() || candidate.end > end {
|
|
56
|
+
return end, false, 0
|
|
57
|
+
}
|
|
58
|
+
return candidate.end, true, candidate.tokenLen
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type signatureBoundary struct {
|
|
62
|
+
end int
|
|
63
|
+
tokenLen int
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
func sourceBodyBoundary(node *shimast.Node) (signatureBoundary, bool) {
|
|
67
|
+
if node == nil {
|
|
68
|
+
return signatureBoundary{}, false
|
|
69
|
+
}
|
|
70
|
+
switch node.Kind {
|
|
71
|
+
case shimast.KindArrowFunction:
|
|
72
|
+
arrow := node.AsArrowFunction()
|
|
73
|
+
if arrow != nil && arrow.EqualsGreaterThanToken != nil {
|
|
74
|
+
return signatureBoundary{end: arrow.EqualsGreaterThanToken.End()}, true
|
|
75
|
+
}
|
|
76
|
+
case shimast.KindBlock, shimast.KindObjectLiteralExpression, shimast.KindArrayLiteralExpression:
|
|
77
|
+
return signatureBoundary{end: node.Pos(), tokenLen: 1}, true
|
|
78
|
+
case shimast.KindClassExpression:
|
|
79
|
+
return signatureBoundary{end: node.Pos(), tokenLen: len("class")}, true
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
var boundary signatureBoundary
|
|
83
|
+
found := false
|
|
84
|
+
node.ForEachChild(func(child *shimast.Node) bool {
|
|
85
|
+
candidate, ok := sourceBodyBoundary(child)
|
|
86
|
+
if ok {
|
|
87
|
+
boundary = candidate
|
|
88
|
+
found = true
|
|
89
|
+
// ForEachChild visits source order, so the first body boundary is the
|
|
90
|
+
// earliest and later children cannot improve the outline cut.
|
|
91
|
+
return true
|
|
92
|
+
}
|
|
93
|
+
return false
|
|
94
|
+
})
|
|
95
|
+
return boundary, found
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// objectLiteralInitializer unwraps the transparent expression wrappers a
|
|
99
|
+
// declaration commonly places around an object literal. None changes which
|
|
100
|
+
// direct properties the literal declares.
|
|
101
|
+
func objectLiteralInitializer(initializer *shimast.Node) *shimast.ObjectLiteralExpression {
|
|
102
|
+
for initializer != nil {
|
|
103
|
+
switch initializer.Kind {
|
|
104
|
+
case shimast.KindObjectLiteralExpression:
|
|
105
|
+
return initializer.AsObjectLiteralExpression()
|
|
106
|
+
case shimast.KindAsExpression:
|
|
107
|
+
expression := initializer.AsAsExpression()
|
|
108
|
+
if expression == nil {
|
|
109
|
+
return nil
|
|
110
|
+
}
|
|
111
|
+
initializer = expression.Expression
|
|
112
|
+
case shimast.KindSatisfiesExpression:
|
|
113
|
+
expression := initializer.AsSatisfiesExpression()
|
|
114
|
+
if expression == nil {
|
|
115
|
+
return nil
|
|
116
|
+
}
|
|
117
|
+
initializer = expression.Expression
|
|
118
|
+
case shimast.KindParenthesizedExpression:
|
|
119
|
+
expression := initializer.AsParenthesizedExpression()
|
|
120
|
+
if expression == nil {
|
|
121
|
+
return nil
|
|
122
|
+
}
|
|
123
|
+
initializer = expression.Expression
|
|
124
|
+
case shimast.KindTypeAssertionExpression:
|
|
125
|
+
expression := initializer.AsTypeAssertion()
|
|
126
|
+
if expression == nil {
|
|
127
|
+
return nil
|
|
128
|
+
}
|
|
129
|
+
initializer = expression.Expression
|
|
130
|
+
case shimast.KindNonNullExpression:
|
|
131
|
+
expression := initializer.AsNonNullExpression()
|
|
132
|
+
if expression == nil {
|
|
133
|
+
return nil
|
|
134
|
+
}
|
|
135
|
+
initializer = expression.Expression
|
|
136
|
+
default:
|
|
137
|
+
return nil
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return nil
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// objectMemberIdentity returns the source-visible name and details kind of one
|
|
144
|
+
// direct object-literal property. The boolean distinguishes an unsupported
|
|
145
|
+
// spread or dynamic computed key from the valid static property name `""`.
|
|
146
|
+
func objectMemberIdentity(property *shimast.Node) (string, NodeKind, bool) {
|
|
147
|
+
if property == nil {
|
|
148
|
+
return "", "", false
|
|
149
|
+
}
|
|
150
|
+
kind := NodeVariable
|
|
151
|
+
switch property.Kind {
|
|
152
|
+
case shimast.KindPropertyAssignment, shimast.KindShorthandPropertyAssignment:
|
|
153
|
+
case shimast.KindMethodDeclaration, shimast.KindGetAccessor, shimast.KindSetAccessor:
|
|
154
|
+
kind = NodeMethod
|
|
155
|
+
default:
|
|
156
|
+
return "", "", false
|
|
157
|
+
}
|
|
158
|
+
name, ok := staticObjectMemberName(property.Name())
|
|
159
|
+
return name, kind, ok
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// staticObjectMemberName resolves the declaration forms whose property name is
|
|
163
|
+
// fixed by syntax. A computed literal is fixed; a computed expression is not.
|
|
164
|
+
func staticObjectMemberName(name *shimast.Node) (string, bool) {
|
|
165
|
+
if name == nil {
|
|
166
|
+
return "", false
|
|
167
|
+
}
|
|
168
|
+
switch name.Kind {
|
|
169
|
+
case shimast.KindIdentifier:
|
|
170
|
+
if identifier := name.AsIdentifier(); identifier != nil {
|
|
171
|
+
return identifier.Text, true
|
|
172
|
+
}
|
|
173
|
+
case shimast.KindStringLiteral:
|
|
174
|
+
if literal := name.AsStringLiteral(); literal != nil {
|
|
175
|
+
return literal.Text, true
|
|
176
|
+
}
|
|
177
|
+
case shimast.KindNoSubstitutionTemplateLiteral:
|
|
178
|
+
if literal := name.AsNoSubstitutionTemplateLiteral(); literal != nil {
|
|
179
|
+
return literal.Text, true
|
|
180
|
+
}
|
|
181
|
+
case shimast.KindNumericLiteral:
|
|
182
|
+
if literal := name.AsNumericLiteral(); literal != nil {
|
|
183
|
+
return literal.Text, true
|
|
184
|
+
}
|
|
185
|
+
case shimast.KindBigIntLiteral:
|
|
186
|
+
if literal := name.AsBigIntLiteral(); literal != nil {
|
|
187
|
+
return literal.Text, true
|
|
188
|
+
}
|
|
189
|
+
case shimast.KindComputedPropertyName:
|
|
190
|
+
computed := name.AsComputedPropertyName()
|
|
191
|
+
if computed == nil {
|
|
192
|
+
return "", false
|
|
193
|
+
}
|
|
194
|
+
return staticComputedObjectMemberName(computed.Expression)
|
|
195
|
+
}
|
|
196
|
+
return "", false
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
func staticComputedObjectMemberName(expression *shimast.Node) (string, bool) {
|
|
200
|
+
for expression != nil && expression.Kind == shimast.KindParenthesizedExpression {
|
|
201
|
+
parenthesized := expression.AsParenthesizedExpression()
|
|
202
|
+
if parenthesized == nil {
|
|
203
|
+
return "", false
|
|
204
|
+
}
|
|
205
|
+
expression = parenthesized.Expression
|
|
206
|
+
}
|
|
207
|
+
if expression == nil {
|
|
208
|
+
return "", false
|
|
209
|
+
}
|
|
210
|
+
switch expression.Kind {
|
|
211
|
+
case shimast.KindStringLiteral:
|
|
212
|
+
if literal := expression.AsStringLiteral(); literal != nil {
|
|
213
|
+
return literal.Text, true
|
|
214
|
+
}
|
|
215
|
+
case shimast.KindNoSubstitutionTemplateLiteral:
|
|
216
|
+
if literal := expression.AsNoSubstitutionTemplateLiteral(); literal != nil {
|
|
217
|
+
return literal.Text, true
|
|
218
|
+
}
|
|
219
|
+
case shimast.KindNumericLiteral:
|
|
220
|
+
if literal := expression.AsNumericLiteral(); literal != nil {
|
|
221
|
+
return literal.Text, true
|
|
222
|
+
}
|
|
223
|
+
case shimast.KindBigIntLiteral:
|
|
224
|
+
if literal := expression.AsBigIntLiteral(); literal != nil {
|
|
225
|
+
return literal.Text, true
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return "", false
|
|
229
|
+
}
|
|
@@ -14,7 +14,7 @@ import (
|
|
|
14
14
|
// field is added, removed, or given a new meaning, independently of the serve
|
|
15
15
|
// envelope's protocol version: a one-shot `ttscgraph dump` written to a file has
|
|
16
16
|
// a schema but never rode the protocol.
|
|
17
|
-
const DumpSchemaVersion =
|
|
17
|
+
const DumpSchemaVersion = 6
|
|
18
18
|
|
|
19
19
|
// The capabilities a snapshot can declare. Each names one class of evidence a
|
|
20
20
|
// consumer may rely on when, and only when, the snapshot lists it.
|
|
@@ -119,16 +119,16 @@ type Universe struct {
|
|
|
119
119
|
// references mean two configs can name the same file, and they are not the same
|
|
120
120
|
// input, so the pair is the unit rather than the bare path.
|
|
121
121
|
type RootFile struct {
|
|
122
|
-
// Config is the tsconfig that named this root,
|
|
122
|
+
// Config is the tsconfig that named this root, in the dump's path vocabulary.
|
|
123
123
|
Config string `json:"config"`
|
|
124
124
|
|
|
125
|
-
// File is the root file,
|
|
125
|
+
// File is the root file, in the dump's path vocabulary.
|
|
126
126
|
File string `json:"file"`
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
// FileDigest pairs a file with the SHA-256 of its bytes, hex-encoded.
|
|
130
130
|
type FileDigest struct {
|
|
131
|
-
// File
|
|
131
|
+
// File uses the dump's path vocabulary.
|
|
132
132
|
File string `json:"file"`
|
|
133
133
|
|
|
134
134
|
// Digest is the hex-encoded SHA-256 of the file's on-disk bytes.
|
|
@@ -159,7 +159,7 @@ type FileDigest struct {
|
|
|
159
159
|
// Both are digests. Neither is text: the wire never carries a file's bytes, and
|
|
160
160
|
// the field names say digest so that stays true by reading.
|
|
161
161
|
type SourceDigest struct {
|
|
162
|
-
// File
|
|
162
|
+
// File uses the dump's path vocabulary.
|
|
163
163
|
File string `json:"file"`
|
|
164
164
|
|
|
165
165
|
// Checker is the hex-encoded SHA-256 of the text the checker resolved
|
|
@@ -180,7 +180,7 @@ type SourceDigest struct {
|
|
|
180
180
|
// Diagnostic is one compiler diagnostic riding the snapshot that produced the
|
|
181
181
|
// facts. Positions are 1-based, matching what tsgo reports.
|
|
182
182
|
type Diagnostic struct {
|
|
183
|
-
// File
|
|
183
|
+
// File uses the dump's path vocabulary.
|
|
184
184
|
File string `json:"file"`
|
|
185
185
|
|
|
186
186
|
// Line is the 1-based line.
|
|
@@ -207,13 +207,14 @@ func Digest(sum [sha256.Size]byte) string { return hex.EncodeToString(sum[:]) }
|
|
|
207
207
|
// implements.
|
|
208
208
|
func TypescriptVersion() string { return shimcore.Version() }
|
|
209
209
|
|
|
210
|
-
// NewProvenance assembles the evidence for a snapshot
|
|
211
|
-
//
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
//
|
|
210
|
+
// NewProvenance assembles the evidence for a snapshot while retaining the
|
|
211
|
+
// compiler's physical paths. NewDump projects those paths together with every
|
|
212
|
+
// node, edge, span, and diagnostic through its one cached schema-v6 mapper.
|
|
213
|
+
// texts maps a source file's path to the text the checker read (as SourceTexts
|
|
214
|
+
// returns it); disk maps that path to the hex digest of its on-disk bytes, and
|
|
215
|
+
// a path absent from it is reported with an empty Disk. configs and roots come
|
|
216
|
+
// from the same capture that produced texts.
|
|
215
217
|
func NewProvenance(
|
|
216
|
-
project string,
|
|
217
218
|
producer Producer,
|
|
218
219
|
capabilities []string,
|
|
219
220
|
configs []FileDigest,
|
|
@@ -221,32 +222,31 @@ func NewProvenance(
|
|
|
221
222
|
texts map[string]string,
|
|
222
223
|
disk map[string]string,
|
|
223
224
|
) Provenance {
|
|
224
|
-
ctx := newDumpContext(project, nil)
|
|
225
225
|
sources := make([]SourceDigest, 0, len(texts))
|
|
226
226
|
for path, text := range texts {
|
|
227
227
|
sources = append(sources, SourceDigest{
|
|
228
|
-
File:
|
|
228
|
+
File: path,
|
|
229
229
|
Checker: Digest(sha256.Sum256([]byte(text))),
|
|
230
230
|
Disk: disk[path],
|
|
231
231
|
})
|
|
232
232
|
}
|
|
233
233
|
sort.Slice(sources, func(i, j int) bool { return sources[i].File < sources[j].File })
|
|
234
234
|
|
|
235
|
-
|
|
235
|
+
capturedConfigs := make([]FileDigest, 0, len(configs))
|
|
236
236
|
for _, config := range configs {
|
|
237
|
-
|
|
237
|
+
capturedConfigs = append(capturedConfigs, FileDigest{File: config.File, Digest: config.Digest})
|
|
238
238
|
}
|
|
239
|
-
sort.Slice(
|
|
239
|
+
sort.Slice(capturedConfigs, func(i, j int) bool { return capturedConfigs[i].File < capturedConfigs[j].File })
|
|
240
240
|
|
|
241
|
-
|
|
241
|
+
capturedRoots := make([]RootFile, 0, len(roots))
|
|
242
242
|
for _, root := range roots {
|
|
243
|
-
|
|
243
|
+
capturedRoots = append(capturedRoots, RootFile{Config: root.Config, File: root.File})
|
|
244
244
|
}
|
|
245
|
-
sort.Slice(
|
|
246
|
-
if
|
|
247
|
-
return
|
|
245
|
+
sort.Slice(capturedRoots, func(i, j int) bool {
|
|
246
|
+
if capturedRoots[i].Config != capturedRoots[j].Config {
|
|
247
|
+
return capturedRoots[i].Config < capturedRoots[j].Config
|
|
248
248
|
}
|
|
249
|
-
return
|
|
249
|
+
return capturedRoots[i].File < capturedRoots[j].File
|
|
250
250
|
})
|
|
251
251
|
|
|
252
252
|
// Copy before sorting: the caller's slice is a shared package-level constant,
|
|
@@ -259,26 +259,25 @@ func NewProvenance(
|
|
|
259
259
|
SchemaVersion: DumpSchemaVersion,
|
|
260
260
|
Capabilities: declared,
|
|
261
261
|
Producer: producer,
|
|
262
|
-
Universe: Universe{Configs:
|
|
262
|
+
Universe: Universe{Configs: capturedConfigs, Roots: capturedRoots},
|
|
263
263
|
Sources: sources,
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
// NewDiagnostics projects the resident program's compiler diagnostics
|
|
268
|
-
//
|
|
269
|
-
//
|
|
267
|
+
// NewDiagnostics projects the resident program's compiler diagnostics while
|
|
268
|
+
// retaining physical file identities. NewDump maps and re-sorts them with the
|
|
269
|
+
// rest of the snapshot so every path uses one vocabulary.
|
|
270
270
|
//
|
|
271
271
|
// This is one Program.Diagnostics() call over the already-warm checker rather
|
|
272
272
|
// than a second compile, but it is not free: it forces the semantic check of
|
|
273
273
|
// every file the graph would otherwise only have bound. Callers that do not
|
|
274
274
|
// publish diagnostics should not call it.
|
|
275
|
-
func NewDiagnostics(prog *driver.Program
|
|
276
|
-
ctx := newDumpContext(project, nil)
|
|
275
|
+
func NewDiagnostics(prog *driver.Program) []Diagnostic {
|
|
277
276
|
raw := prog.Diagnostics()
|
|
278
277
|
out := make([]Diagnostic, 0, len(raw))
|
|
279
278
|
for _, diagnostic := range raw {
|
|
280
279
|
out = append(out, Diagnostic{
|
|
281
|
-
File:
|
|
280
|
+
File: diagnostic.File,
|
|
282
281
|
Line: diagnostic.Line,
|
|
283
282
|
Column: diagnostic.Column,
|
|
284
283
|
Code: int(diagnostic.Code),
|
|
@@ -301,6 +300,65 @@ func NewDiagnostics(prog *driver.Program, project string) []Diagnostic {
|
|
|
301
300
|
return out
|
|
302
301
|
}
|
|
303
302
|
|
|
303
|
+
// provenance maps every path-bearing provenance field through the dump's one
|
|
304
|
+
// mapper and sorts on the resulting wire identity.
|
|
305
|
+
func (c *dumpContext) provenance(value Provenance) Provenance {
|
|
306
|
+
sources := make([]SourceDigest, 0, len(value.Sources))
|
|
307
|
+
for _, source := range value.Sources {
|
|
308
|
+
sources = append(sources, SourceDigest{
|
|
309
|
+
File: c.rel(source.File),
|
|
310
|
+
Checker: source.Checker,
|
|
311
|
+
Disk: source.Disk,
|
|
312
|
+
})
|
|
313
|
+
}
|
|
314
|
+
sort.Slice(sources, func(i, j int) bool { return sources[i].File < sources[j].File })
|
|
315
|
+
|
|
316
|
+
configs := make([]FileDigest, 0, len(value.Universe.Configs))
|
|
317
|
+
for _, config := range value.Universe.Configs {
|
|
318
|
+
configs = append(configs, FileDigest{File: c.rel(config.File), Digest: config.Digest})
|
|
319
|
+
}
|
|
320
|
+
sort.Slice(configs, func(i, j int) bool { return configs[i].File < configs[j].File })
|
|
321
|
+
|
|
322
|
+
roots := make([]RootFile, 0, len(value.Universe.Roots))
|
|
323
|
+
for _, root := range value.Universe.Roots {
|
|
324
|
+
roots = append(roots, RootFile{Config: c.rel(root.Config), File: c.rel(root.File)})
|
|
325
|
+
}
|
|
326
|
+
sort.Slice(roots, func(i, j int) bool {
|
|
327
|
+
if roots[i].Config != roots[j].Config {
|
|
328
|
+
return roots[i].Config < roots[j].Config
|
|
329
|
+
}
|
|
330
|
+
return roots[i].File < roots[j].File
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
value.Capabilities = append([]string{}, value.Capabilities...)
|
|
334
|
+
value.Sources = sources
|
|
335
|
+
value.Universe = Universe{Configs: configs, Roots: roots}
|
|
336
|
+
return value
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// diagnostics maps and orders compiler findings after they join the dump, not
|
|
340
|
+
// in their constructor, so they cannot drift onto a second path vocabulary.
|
|
341
|
+
func (c *dumpContext) diagnostics(values []Diagnostic) []Diagnostic {
|
|
342
|
+
out := make([]Diagnostic, 0, len(values))
|
|
343
|
+
for _, diagnostic := range values {
|
|
344
|
+
diagnostic.File = c.rel(diagnostic.File)
|
|
345
|
+
out = append(out, diagnostic)
|
|
346
|
+
}
|
|
347
|
+
sort.Slice(out, func(i, j int) bool {
|
|
348
|
+
if out[i].File != out[j].File {
|
|
349
|
+
return out[i].File < out[j].File
|
|
350
|
+
}
|
|
351
|
+
if out[i].Line != out[j].Line {
|
|
352
|
+
return out[i].Line < out[j].Line
|
|
353
|
+
}
|
|
354
|
+
if out[i].Column != out[j].Column {
|
|
355
|
+
return out[i].Column < out[j].Column
|
|
356
|
+
}
|
|
357
|
+
return out[i].Code < out[j].Code
|
|
358
|
+
})
|
|
359
|
+
return out
|
|
360
|
+
}
|
|
361
|
+
|
|
304
362
|
// diagnosticCategory names a severity the way tsc labels it. The driver
|
|
305
363
|
// distinguishes exactly two, and SeverityError is the zero value, so anything
|
|
306
364
|
// that is not an explicit warning is an error.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
package graph
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"path/filepath"
|
|
5
|
+
"strings"
|
|
6
|
+
"testing"
|
|
7
|
+
|
|
8
|
+
shimtspath "github.com/microsoft/typescript-go/shim/tspath"
|
|
9
|
+
|
|
10
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
// TestSourceTextsCoverEveryResidentProgramSource verifies that snapshot
|
|
14
|
+
// evidence includes every class of source the checker loaded.
|
|
15
|
+
//
|
|
16
|
+
// External graph leaves still carry checker-derived facts and spans. Omitting
|
|
17
|
+
// their resident text from the source manifest leaves those facts without the
|
|
18
|
+
// byte identity that the sourceDigests capability promises. Virtual bundled
|
|
19
|
+
// libraries need the same checker digest even though they have no disk digest.
|
|
20
|
+
//
|
|
21
|
+
// 1. Compile a project that calls a function declared by an outside `.d.ts`.
|
|
22
|
+
// 2. Build the graph and capture the resident program's source texts.
|
|
23
|
+
// 3. Assert both the outside declaration and a virtual bundled library are in
|
|
24
|
+
// the checker-text manifest.
|
|
25
|
+
func TestSourceTextsCoverEveryResidentProgramSource(t *testing.T) {
|
|
26
|
+
workspace := t.TempDir()
|
|
27
|
+
root := filepath.Join(workspace, "app")
|
|
28
|
+
declaration := filepath.Join(workspace, "dependency", "index.d.ts")
|
|
29
|
+
writeFile(t, filepath.Join(root, "tsconfig.json"), fixtureTSConfig)
|
|
30
|
+
writeFile(t, filepath.Join(root, "src", "main.ts"), `import { external } from "../../dependency";
|
|
31
|
+
|
|
32
|
+
export const value = external();
|
|
33
|
+
`)
|
|
34
|
+
writeFile(t, declaration, `export declare function external(): number;
|
|
35
|
+
`)
|
|
36
|
+
declaration = shimtspath.ResolvePath(declaration)
|
|
37
|
+
|
|
38
|
+
prog, diags, err := driver.LoadProgram(root, "tsconfig.json", driver.LoadProgramOptions{})
|
|
39
|
+
if err != nil {
|
|
40
|
+
t.Fatal(err)
|
|
41
|
+
}
|
|
42
|
+
if len(diags) != 0 {
|
|
43
|
+
t.Fatalf("unexpected diagnostics: %v", diags)
|
|
44
|
+
}
|
|
45
|
+
defer func() { _ = prog.Close() }()
|
|
46
|
+
|
|
47
|
+
built := Build(prog)
|
|
48
|
+
hasExternalFact := false
|
|
49
|
+
for _, node := range built.Nodes {
|
|
50
|
+
if node.External && node.File == declaration {
|
|
51
|
+
hasExternalFact = true
|
|
52
|
+
break
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if !hasExternalFact {
|
|
56
|
+
t.Fatalf("graph has no external fact from %s", declaration)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
texts := SourceTexts(prog)
|
|
60
|
+
if got, ok := texts[declaration]; !ok || got != "export declare function external(): number;\n" {
|
|
61
|
+
t.Fatalf("declaration source text = %q, present %v", got, ok)
|
|
62
|
+
}
|
|
63
|
+
hasBundledSource := false
|
|
64
|
+
for path := range texts {
|
|
65
|
+
if strings.HasPrefix(path, "bundled:///") {
|
|
66
|
+
hasBundledSource = true
|
|
67
|
+
break
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if !hasBundledSource {
|
|
71
|
+
t.Fatal("source manifest has no virtual bundled library")
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
package graphsymbols
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"testing"
|
|
5
|
+
"unicode/utf8"
|
|
6
|
+
|
|
7
|
+
"github.com/samchon/ttsc/packages/ttsc/internal/graph"
|
|
8
|
+
"github.com/samchon/ttsc/packages/ttsc/internal/lspserver"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
// TestPositionHelpersFollowECMALineTerminators verifies graph-backed LSP
|
|
12
|
+
// positions use the compiler's LF, CRLF, CR, LS, and PS line boundaries.
|
|
13
|
+
//
|
|
14
|
+
// The old offset-to-position helper counted only LF while its inverse counted
|
|
15
|
+
// CR too, so an editor cursor could map to a graph offset that returned on a
|
|
16
|
+
// different line. The test walks every valid UTF-8 cursor boundary on each
|
|
17
|
+
// logical line, including a surrogate-pair column, and requires a round trip.
|
|
18
|
+
//
|
|
19
|
+
// 1. Build three lines with each ECMAScript terminator.
|
|
20
|
+
// 2. Enumerate every valid cursor boundary before each terminator.
|
|
21
|
+
// 3. Convert each offset to UTF-16 LSP position and back to the same offset.
|
|
22
|
+
func TestPositionHelpersFollowECMALineTerminators(t *testing.T) {
|
|
23
|
+
cases := []struct {
|
|
24
|
+
name string
|
|
25
|
+
terminator string
|
|
26
|
+
}{
|
|
27
|
+
{name: "LF", terminator: "\n"},
|
|
28
|
+
{name: "CRLF", terminator: "\r\n"},
|
|
29
|
+
{name: "CR", terminator: "\r"},
|
|
30
|
+
{name: "LS", terminator: "\u2028"},
|
|
31
|
+
{name: "PS", terminator: "\u2029"},
|
|
32
|
+
}
|
|
33
|
+
for _, tc := range cases {
|
|
34
|
+
t.Run(tc.name, func(t *testing.T) {
|
|
35
|
+
text := "alpha😀" + tc.terminator + "beta" + tc.terminator + "gamma"
|
|
36
|
+
starts := graph.ECMALineStarts(text)
|
|
37
|
+
if len(starts) != 3 {
|
|
38
|
+
t.Fatalf("line starts = %v, want three lines", starts)
|
|
39
|
+
}
|
|
40
|
+
for line, start := range starts {
|
|
41
|
+
end := graph.LineEnd(text, starts, line)
|
|
42
|
+
for offset := start; ; {
|
|
43
|
+
position := offsetToPosition(text, offset)
|
|
44
|
+
if position.Line != line {
|
|
45
|
+
t.Fatalf("offset %d position line = %d, want %d", offset, position.Line, line)
|
|
46
|
+
}
|
|
47
|
+
restored, ok := lspPositionToOffset(text, position)
|
|
48
|
+
if !ok || restored != offset {
|
|
49
|
+
t.Fatalf("offset %d -> %+v -> (%d, %t), want itself", offset, position, restored, ok)
|
|
50
|
+
}
|
|
51
|
+
if offset == end {
|
|
52
|
+
break
|
|
53
|
+
}
|
|
54
|
+
_, size := utf8.DecodeRuneInString(text[offset:])
|
|
55
|
+
if size == 0 {
|
|
56
|
+
t.Fatal("unexpected zero-width UTF-8 rune")
|
|
57
|
+
}
|
|
58
|
+
offset += size
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if _, ok := lspPositionToOffset(text, lspserver.LSPPosition{Line: len(starts), Character: 0}); ok {
|
|
62
|
+
t.Fatal("position on a nonexistent source line must not resolve")
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
}
|