ttsc 0.19.2 → 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.
Files changed (178) hide show
  1. package/README.md +7 -5
  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 +312 -108
  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_schema_version_matches_the_typescript_client_test.go +1 -1
  33. package/internal/graph/dump_uses_ecma_line_terminators_test.go +87 -0
  34. package/internal/graph/duplicate_variable_object_members_keep_first_declaration_test.go +61 -0
  35. package/internal/graph/edges.go +220 -5
  36. package/internal/graph/enum_members_carry_their_names_on_the_enum_node_test.go +131 -0
  37. package/internal/graph/graph.go +177 -5
  38. package/internal/graph/line_model.go +122 -0
  39. package/internal/graph/literals.go +175 -0
  40. package/internal/graph/literals_are_absent_when_the_type_admits_more_than_it_can_name_test.go +74 -0
  41. package/internal/graph/literals_cover_enum_members_the_graph_has_no_nodes_for_test.go +79 -0
  42. package/internal/graph/literals_do_not_depend_on_declaration_line_wrapping_test.go +84 -0
  43. package/internal/graph/literals_render_every_value_in_typescript_source_form_test.go +77 -0
  44. package/internal/graph/literals_resolve_through_alias_indirection_test.go +64 -0
  45. package/internal/graph/member_relation_edges_follow_checker_validity_test.go +85 -0
  46. package/internal/graph/member_relation_edges_preserve_checker_boundaries_test.go +191 -0
  47. package/internal/graph/member_relation_edges_reject_checker_invalid_pairs_test.go +119 -0
  48. package/internal/graph/node_ids_preserve_hash_bearing_components_test.go +41 -0
  49. package/internal/graph/node_modifiers_emit_union_strings_test.go +4 -1
  50. package/internal/graph/object_literal_members_ride_on_the_snapshot_test.go +192 -0
  51. package/internal/graph/object_member_signatures_preserve_literal_whitespace_test.go +67 -0
  52. package/internal/graph/object_members.go +229 -0
  53. package/internal/graph/provenance.go +88 -30
  54. package/internal/graph/source_texts_cover_every_resident_program_source_test.go +73 -0
  55. package/internal/graphsymbols/position_helpers_follow_ecma_line_terminators_test.go +66 -0
  56. package/internal/graphsymbols/provider.go +26 -91
  57. package/internal/lspserver/lsp_clean_didopen_refreshes_compiler_state_test.go +144 -0
  58. package/internal/lspserver/lsp_completion.go +109 -0
  59. package/internal/lspserver/lsp_completion_gate_never_refuses_a_matching_line_test.go +50 -0
  60. package/internal/lspserver/lsp_completion_hints_longest_trigger_wins_test.go +149 -0
  61. package/internal/lspserver/lsp_completion_hints_nearest_trigger_wins_test.go +37 -0
  62. package/internal/lspserver/lsp_completion_merges_into_upstream_response_test.go +149 -0
  63. package/internal/lspserver/lsp_completion_nearest_hint_replaces_nearest_filter_test.go +43 -0
  64. package/internal/lspserver/lsp_completion_preserves_completion_list_extensions_test.go +99 -0
  65. package/internal/lspserver/lsp_completion_refuses_hints_inside_a_string_literal_test.go +69 -0
  66. package/internal/lspserver/lsp_completion_replaces_full_filter_in_rank_order_test.go +85 -0
  67. package/internal/lspserver/lsp_completion_request.go +455 -0
  68. package/internal/lspserver/lsp_completion_scope.go +530 -0
  69. package/internal/lspserver/lsp_completion_scope_bench_test.go +41 -0
  70. package/internal/lspserver/lsp_completion_scope_classifies_regex_statement_and_operator_boundaries_test.go +39 -0
  71. package/internal/lspserver/lsp_completion_scope_ignores_jsdoc_bytes_in_literals_test.go +108 -0
  72. package/internal/lspserver/lsp_completion_scope_refuses_regex_hints_test.go +52 -0
  73. package/internal/lspserver/lsp_completion_trigger_uses_last_rune_test.go +24 -0
  74. package/internal/lspserver/lsp_deleted_watched_file_withdraws_plugin_diagnostics_test.go +110 -0
  75. package/internal/lspserver/lsp_didsave_keeps_the_incremental_changed_uri_path_test.go +78 -0
  76. package/internal/lspserver/lsp_hints_absence_is_not_an_error_test.go +46 -0
  77. package/internal/lspserver/lsp_hints_refresh_coalesces_concurrent_requests_test.go +82 -0
  78. package/internal/lspserver/lsp_hints_refresh_ignores_a_superseded_generation_test.go +40 -0
  79. package/internal/lspserver/lsp_hints_refresh_keeps_each_producer_isolated_test.go +64 -0
  80. package/internal/lspserver/lsp_hints_refresh_replaces_the_session_snapshot_test.go +66 -0
  81. package/internal/lspserver/lsp_hints_refresh_scheduled_by_editor_events_test.go +90 -0
  82. package/internal/lspserver/lsp_hints_refresh_serves_completion_while_running_test.go +91 -0
  83. package/internal/lspserver/lsp_index_byte_from_bounds_its_search_start_test.go +38 -0
  84. package/internal/lspserver/lsp_initialize_constrains_position_encoding_to_utf16_test.go +205 -0
  85. package/internal/lspserver/lsp_late_completion_trigger_is_reported_once_test.go +105 -0
  86. package/internal/lspserver/lsp_native_completion_hints_decode_flat_lint_wire_test.go +66 -0
  87. package/internal/lspserver/lsp_native_completion_hints_grouped_compatibility_boundary_test.go +60 -0
  88. package/internal/lspserver/lsp_native_plugin_source.go +315 -2
  89. package/internal/lspserver/lsp_native_resident.go +304 -0
  90. package/internal/lspserver/lsp_plugin.go +85 -8
  91. package/internal/lspserver/lsp_plugin_completion_resolve_stays_local_test.go +82 -0
  92. package/internal/lspserver/lsp_position_conversions_span_astral_and_combining_runes_test.go +119 -0
  93. package/internal/lspserver/lsp_proxy.go +601 -9
  94. package/internal/lspserver/lsp_refresh.go +70 -0
  95. package/internal/lspserver/lsp_resident_falls_back_when_serve_unsupported_test.go +43 -0
  96. package/internal/lspserver/lsp_resident_maps_verb_args_test.go +34 -0
  97. package/internal/lspserver/lsp_server.go +13 -1
  98. package/internal/lspserver/lsp_watched_file_changes_reach_compiler_caches_test.go +172 -0
  99. package/lib/TtscCompiler.d.ts +3 -1
  100. package/lib/TtscCompiler.js +5 -1
  101. package/lib/TtscCompiler.js.map +1 -1
  102. package/lib/TtscService.d.ts +13 -3
  103. package/lib/TtscService.js +6 -6
  104. package/lib/TtscService.js.map +1 -1
  105. package/lib/compiler/internal/project/readConfigJson.d.ts +30 -0
  106. package/lib/compiler/internal/project/readConfigJson.js +208 -0
  107. package/lib/compiler/internal/project/readConfigJson.js.map +1 -0
  108. package/lib/compiler/internal/project/readProjectConfig.js +53 -149
  109. package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
  110. package/lib/compiler/internal/residentTransformProcess.d.ts +15 -2
  111. package/lib/compiler/internal/residentTransformProcess.js +159 -39
  112. package/lib/compiler/internal/residentTransformProcess.js.map +1 -1
  113. package/lib/compiler/internal/runBuild.d.ts +6 -0
  114. package/lib/compiler/internal/runBuild.js +143 -44
  115. package/lib/compiler/internal/runBuild.js.map +1 -1
  116. package/lib/compiler/internal/startResidentTransform.d.ts +5 -1
  117. package/lib/compiler/internal/startResidentTransform.js +3 -1
  118. package/lib/compiler/internal/startResidentTransform.js.map +1 -1
  119. package/lib/compiler/internal/transformProjectInMemory.js +12 -2
  120. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  121. package/lib/flags/parser.d.ts +19 -0
  122. package/lib/flags/parser.js +78 -36
  123. package/lib/flags/parser.js.map +1 -1
  124. package/lib/flags/schema.d.ts +49 -11
  125. package/lib/flags/schema.js +94 -22
  126. package/lib/flags/schema.js.map +1 -1
  127. package/lib/internal/assertSafeExplicitCacheDirectory.d.ts +5 -0
  128. package/lib/internal/assertSafeExplicitCacheDirectory.js +26 -0
  129. package/lib/internal/assertSafeExplicitCacheDirectory.js.map +1 -0
  130. package/lib/launcher/internal/runTtsc.js +107 -69
  131. package/lib/launcher/internal/runTtsc.js.map +1 -1
  132. package/lib/launcher/internal/runTtsx.js +28 -41
  133. package/lib/launcher/internal/runTtsx.js.map +1 -1
  134. package/lib/launcher/internal/runtimeHooks.d.ts +15 -0
  135. package/lib/launcher/internal/runtimeHooks.js +18 -1
  136. package/lib/launcher/internal/runtimeHooks.js.map +1 -1
  137. package/lib/launcher/internal/watchTopology.d.ts +43 -0
  138. package/lib/launcher/internal/watchTopology.js +360 -0
  139. package/lib/launcher/internal/watchTopology.js.map +1 -0
  140. package/lib/plugin/internal/buildSourcePlugin.d.ts +14 -3
  141. package/lib/plugin/internal/buildSourcePlugin.js +247 -61
  142. package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
  143. package/lib/plugin/internal/loadProjectPlugins.js +63 -2
  144. package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
  145. package/lib/structures/ITtscCompilerTransformation.d.ts +16 -0
  146. package/lib/structures/internal/ITtscParsedProjectConfig.d.ts +2 -0
  147. package/package.json +8 -8
  148. package/shim/ast/shim.go +48 -3
  149. package/shim/astnav/go.mod +14 -0
  150. package/shim/astnav/go.sum +20 -0
  151. package/shim/astnav/shim.go +15 -0
  152. package/shim/checker/shim.go +113 -0
  153. package/shim/core/shim.go +47 -0
  154. package/shim/diagnosticwriter/lint.go +50 -0
  155. package/src/TtscCompiler.ts +5 -1
  156. package/src/TtscService.ts +37 -10
  157. package/src/compiler/internal/project/readConfigJson.ts +209 -0
  158. package/src/compiler/internal/project/readProjectConfig.ts +62 -158
  159. package/src/compiler/internal/residentTransformProcess.ts +197 -39
  160. package/src/compiler/internal/runBuild.ts +169 -47
  161. package/src/compiler/internal/startResidentTransform.ts +12 -1
  162. package/src/compiler/internal/transformProjectInMemory.ts +12 -1
  163. package/src/flags/parser.ts +113 -42
  164. package/src/flags/schema.ts +113 -31
  165. package/src/internal/assertSafeExplicitCacheDirectory.ts +29 -0
  166. package/src/launcher/internal/runTtsc.ts +117 -68
  167. package/src/launcher/internal/runTtsx.ts +29 -44
  168. package/src/launcher/internal/runtimeHooks.ts +30 -2
  169. package/src/launcher/internal/watchTopology.ts +466 -0
  170. package/src/plugin/internal/buildSourcePlugin.ts +326 -64
  171. package/src/plugin/internal/loadProjectPlugins.ts +80 -2
  172. package/src/structures/ITtscCompilerTransformation.ts +17 -0
  173. package/src/structures/internal/ITtscParsedProjectConfig.ts +2 -0
  174. package/tools/shim_audit/baseline.json +0 -3
  175. package/tools/shim_audit/main.go +42 -14
  176. package/tools/shim_audit/shim_dir_coverage_test.go +25 -0
  177. package/utility/flags_gen.go +3 -3
  178. package/utility/host.go +9 -3
@@ -0,0 +1,66 @@
1
+ package main
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+ )
7
+
8
+ // TestServeSessionKeepsInactiveConditionalExportUnchanged verifies a
9
+ // condition the active TypeScript-Go lookup cannot select is not a graph
10
+ // freshness input.
11
+ //
12
+ // An inactive branch can be present ahead of the active `types` and `default`
13
+ // branches in package.json, but creating that target must not reload a
14
+ // resident ESM session. Creating the missing active `types` target still must.
15
+ func TestServeSessionKeepsInactiveConditionalExportUnchanged(t *testing.T) {
16
+ root := t.TempDir()
17
+ writeGraphFile(t, filepath.Join(root, "package.json"), `{"type":"module"}`)
18
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
19
+ "compilerOptions": {
20
+ "allowJs": true,
21
+ "module": "nodenext",
22
+ "moduleResolution": "nodenext",
23
+ "target": "ES2022"
24
+ },
25
+ "files": ["src/main.ts"]
26
+ }`)
27
+ writeGraphFile(t, filepath.Join(root, "src", "main.ts"), "import { winner } from 'fixture-package/feature';\nexport function main(): void { winner(); }\n")
28
+ writeGraphFile(t, filepath.Join(root, "node_modules", "fixture-package", "package.json"), `{
29
+ "name": "fixture-package",
30
+ "exports": {
31
+ "./feature": {
32
+ "browser": "./dist/browser.js",
33
+ "types": "./dist/types.d.ts",
34
+ "default": "./dist/fallback.js"
35
+ }
36
+ }
37
+ }`)
38
+ writeGraphFile(t, filepath.Join(root, "node_modules", "fixture-package", "dist", "fallback.js"), "export function winner() {}\n")
39
+
40
+ session, err := newGraphSession(root, "tsconfig.json")
41
+ if err != nil {
42
+ t.Fatal(err)
43
+ }
44
+ defer session.Close()
45
+ if _, _, _, err := session.Snapshot(); err != nil {
46
+ t.Fatal(err)
47
+ }
48
+
49
+ writeGraphFile(t, filepath.Join(root, "node_modules", "fixture-package", "dist", "browser.js"), "export function winner() {}\nexport function browserOnly() {}\n")
50
+ dump, mode, changed, err := session.Snapshot()
51
+ if err != nil {
52
+ t.Fatal(err)
53
+ }
54
+ if dump != nil || mode != serveModeUnchanged || changed {
55
+ t.Fatalf("inactive browser condition = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
56
+ }
57
+
58
+ writeGraphFile(t, filepath.Join(root, "node_modules", "fixture-package", "dist", "types.d.ts"), "export declare function winner(): void;\nexport declare function typesWinner(): void;\n")
59
+ dump, mode, changed, err = session.Snapshot()
60
+ if err != nil {
61
+ t.Fatal(err)
62
+ }
63
+ if dump == nil || mode != serveModeReload || !changed {
64
+ t.Fatalf("active types condition = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
65
+ }
66
+ }
@@ -0,0 +1,67 @@
1
+ package main
2
+
3
+ import (
4
+ "os"
5
+ "path/filepath"
6
+ "testing"
7
+ )
8
+
9
+ // TestServeSessionKeepsLowerPriorityModuleCandidateUnchanged verifies a
10
+ // resident session does not reload when a candidate after its selected target
11
+ // appears.
12
+ //
13
+ // Directory index probes are lower priority than every direct-file probe. An
14
+ // interleaved candidate list would turn the creation of an irrelevant index
15
+ // file into a full program reload and erase the bounded-freshness guarantee.
16
+ //
17
+ // 1. Load an extensionless commonjs import that resolves directly to value.js.
18
+ // 2. Create only lower-priority value/index.ts and value.mts candidates.
19
+ // 3. Assert the resident session remains unchanged, then create value.ts and
20
+ // assert the strictly higher-priority candidate does reload it.
21
+ func TestServeSessionKeepsLowerPriorityModuleCandidateUnchanged(t *testing.T) {
22
+ root := t.TempDir()
23
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
24
+ "compilerOptions": { "allowJs": true, "module": "commonjs", "target": "ES2022" },
25
+ "files": ["src/main.ts"]
26
+ }`)
27
+ writeGraphFile(t, filepath.Join(root, "src", "main.ts"), "import { winner } from './value';\nexport function main(): void { winner(); }\n")
28
+ writeGraphFile(t, filepath.Join(root, "src", "value.js"), "export function winner() {}\n")
29
+
30
+ session, err := newGraphSession(root, "tsconfig.json")
31
+ if err != nil {
32
+ t.Fatal(err)
33
+ }
34
+ defer session.Close()
35
+ if _, _, _, err := session.Snapshot(); err != nil {
36
+ t.Fatal(err)
37
+ }
38
+ if err := os.MkdirAll(filepath.Join(root, "src", "value"), 0o755); err != nil {
39
+ t.Fatal(err)
40
+ }
41
+ writeGraphFile(t, filepath.Join(root, "src", "value", "index.ts"), "export function winner(): void {}\n")
42
+ dump, mode, changed, err := session.Snapshot()
43
+ if err != nil {
44
+ t.Fatal(err)
45
+ }
46
+ if dump != nil || mode != serveModeUnchanged || changed {
47
+ t.Fatalf("lower-priority index = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
48
+ }
49
+
50
+ writeGraphFile(t, filepath.Join(root, "src", "value.mts"), "export function winner(): void {}\n")
51
+ dump, mode, changed, err = session.Snapshot()
52
+ if err != nil {
53
+ t.Fatal(err)
54
+ }
55
+ if dump != nil || mode != serveModeUnchanged || changed {
56
+ t.Fatalf("commonjs-lower-priority .mts = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
57
+ }
58
+
59
+ writeGraphFile(t, filepath.Join(root, "src", "value.ts"), "export function winner(): void {}\nexport function typescriptWinner(): void {}\n")
60
+ dump, mode, changed, err = session.Snapshot()
61
+ if err != nil {
62
+ t.Fatal(err)
63
+ }
64
+ if dump == nil || mode != serveModeReload || !changed || !hasDumpNode(*dump, "typescriptWinner") {
65
+ t.Fatalf("higher-priority .ts = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
66
+ }
67
+ }
@@ -0,0 +1,58 @@
1
+ package main
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+ )
7
+
8
+ // TestServeSessionKeepsLowerPriorityPathsPatternUnchanged verifies only the
9
+ // most-specific TypeScript-Go paths pattern contributes freshness candidates.
10
+ //
11
+ // `@/special/*` outranks `@/*`; a file under the broad substitution never
12
+ // participates in this lookup, while a missing .ts sibling of the selected
13
+ // specific substitution does.
14
+ func TestServeSessionKeepsLowerPriorityPathsPatternUnchanged(t *testing.T) {
15
+ root := t.TempDir()
16
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
17
+ "compilerOptions": {
18
+ "allowJs": true,
19
+ "baseUrl": ".",
20
+ "module": "commonjs",
21
+ "paths": {
22
+ "@/*": ["broad/*"],
23
+ "@/special/*": ["specific/*"]
24
+ },
25
+ "target": "ES2022"
26
+ },
27
+ "files": ["src/main.ts"]
28
+ }`)
29
+ writeGraphFile(t, filepath.Join(root, "src", "main.ts"), "import { winner } from '@/special/value';\nexport function main(): void { winner(); }\n")
30
+ writeGraphFile(t, filepath.Join(root, "specific", "value.js"), "export function winner() {}\n")
31
+
32
+ session, err := newGraphSession(root, "tsconfig.json")
33
+ if err != nil {
34
+ t.Fatal(err)
35
+ }
36
+ defer session.Close()
37
+ if _, _, _, err := session.Snapshot(); err != nil {
38
+ t.Fatal(err)
39
+ }
40
+
41
+ writeGraphFile(t, filepath.Join(root, "broad", "special", "value.ts"), "export function winner(): void {}\n")
42
+ dump, mode, changed, err := session.Snapshot()
43
+ if err != nil {
44
+ t.Fatal(err)
45
+ }
46
+ if dump != nil || mode != serveModeUnchanged || changed {
47
+ t.Fatalf("broad paths pattern = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
48
+ }
49
+
50
+ writeGraphFile(t, filepath.Join(root, "specific", "value.ts"), "export function winner(): void {}\nexport function specificPathsWinner(): void {}\n")
51
+ dump, mode, changed, err = session.Snapshot()
52
+ if err != nil {
53
+ t.Fatal(err)
54
+ }
55
+ if dump == nil || mode != serveModeReload || !changed || !hasDumpNode(*dump, "specificPathsWinner") {
56
+ t.Fatalf("specific paths pattern = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
57
+ }
58
+ }
@@ -0,0 +1,54 @@
1
+ package main
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+ )
7
+
8
+ // TestServeSessionKeepsNonLongestRootDirsCandidateUnchanged verifies rootDirs
9
+ // starts from TypeScript-Go's longest matching source root.
10
+ //
11
+ // The containing source lies under both src and src/generated. Only the
12
+ // longest root makes out/views/template a peer candidate; out/generated/views
13
+ // comes from the shorter root and is never tried by this resolution.
14
+ func TestServeSessionKeepsNonLongestRootDirsCandidateUnchanged(t *testing.T) {
15
+ root := t.TempDir()
16
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
17
+ "compilerOptions": {
18
+ "allowJs": true,
19
+ "module": "commonjs",
20
+ "rootDirs": ["src", "src/generated", "out"],
21
+ "target": "ES2022"
22
+ },
23
+ "files": ["src/generated/views/main.ts"]
24
+ }`)
25
+ writeGraphFile(t, filepath.Join(root, "src", "generated", "views", "main.ts"), "import { winner } from './template';\nexport function main(): void { winner(); }\n")
26
+ writeGraphFile(t, filepath.Join(root, "out", "views", "template.js"), "export function winner() {}\n")
27
+
28
+ session, err := newGraphSession(root, "tsconfig.json")
29
+ if err != nil {
30
+ t.Fatal(err)
31
+ }
32
+ defer session.Close()
33
+ if _, _, _, err := session.Snapshot(); err != nil {
34
+ t.Fatal(err)
35
+ }
36
+
37
+ writeGraphFile(t, filepath.Join(root, "out", "generated", "views", "template.ts"), "export function winner(): void {}\n")
38
+ dump, mode, changed, err := session.Snapshot()
39
+ if err != nil {
40
+ t.Fatal(err)
41
+ }
42
+ if dump != nil || mode != serveModeUnchanged || changed {
43
+ t.Fatalf("non-longest rootDirs candidate = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
44
+ }
45
+
46
+ writeGraphFile(t, filepath.Join(root, "src", "views", "template.ts"), "export function winner(): void {}\nexport function rootDirsWinner(): void {}\n")
47
+ dump, mode, changed, err = session.Snapshot()
48
+ if err != nil {
49
+ t.Fatal(err)
50
+ }
51
+ if dump == nil || mode != serveModeReload || !changed || !hasDumpNode(*dump, "rootDirsWinner") {
52
+ t.Fatalf("longest-root peer candidate = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
53
+ }
54
+ }
@@ -0,0 +1,50 @@
1
+ package main
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+ )
7
+
8
+ // TestServeSessionReloadsCommonJSPackageEntryForESM verifies an ESM importer
9
+ // preserves the CommonJS fallback rules of a package's extensionless main.
10
+ //
11
+ // The importing source is ESM, but TypeScript-Go temporarily uses CommonJS
12
+ // lookup for a package without `type: module`. A missing main.ts therefore
13
+ // precedes main.js and must invalidate the resident session when it appears.
14
+ func TestServeSessionReloadsCommonJSPackageEntryForESM(t *testing.T) {
15
+ root := t.TempDir()
16
+ writeGraphFile(t, filepath.Join(root, "package.json"), `{"type":"module"}`)
17
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
18
+ "compilerOptions": {
19
+ "allowJs": true,
20
+ "module": "nodenext",
21
+ "moduleResolution": "nodenext",
22
+ "target": "ES2022"
23
+ },
24
+ "files": ["src/main.ts"]
25
+ }`)
26
+ writeGraphFile(t, filepath.Join(root, "src", "main.ts"), "import { winner } from 'fixture-package';\nexport function main(): void { winner(); }\n")
27
+ writeGraphFile(t, filepath.Join(root, "node_modules", "fixture-package", "package.json"), `{
28
+ "name": "fixture-package",
29
+ "main": "./dist/main"
30
+ }`)
31
+ writeGraphFile(t, filepath.Join(root, "node_modules", "fixture-package", "dist", "main.js"), "export function winner() {}\n")
32
+
33
+ session, err := newGraphSession(root, "tsconfig.json")
34
+ if err != nil {
35
+ t.Fatal(err)
36
+ }
37
+ defer session.Close()
38
+ if _, _, _, err := session.Snapshot(); err != nil {
39
+ t.Fatal(err)
40
+ }
41
+
42
+ writeGraphFile(t, filepath.Join(root, "node_modules", "fixture-package", "dist", "main.ts"), "export function winner(): void {}\nexport function packageEntryWinner(): void {}\n")
43
+ dump, mode, changed, err := session.Snapshot()
44
+ if err != nil {
45
+ t.Fatal(err)
46
+ }
47
+ if dump == nil || mode != serveModeReload || !changed || !hasDumpNode(*dump, "packageEntryWinner") {
48
+ t.Fatalf("CommonJS package main.ts = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
49
+ }
50
+ }
@@ -0,0 +1,41 @@
1
+ package main
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+ )
7
+
8
+ // TestServeSessionReloadsModuleSuffixCandidate verifies a configured module
9
+ // suffix is considered before the unsuffixed extension candidate.
10
+ func TestServeSessionReloadsModuleSuffixCandidate(t *testing.T) {
11
+ root := t.TempDir()
12
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
13
+ "compilerOptions": {
14
+ "allowJs": true,
15
+ "module": "commonjs",
16
+ "moduleSuffixes": [".native", ""],
17
+ "target": "ES2022"
18
+ },
19
+ "files": ["src/main.ts"]
20
+ }`)
21
+ writeGraphFile(t, filepath.Join(root, "src", "main.ts"), "import { winner } from './value';\nexport function main(): void { winner(); }\n")
22
+ writeGraphFile(t, filepath.Join(root, "src", "value.js"), "export function winner() {}\n")
23
+
24
+ session, err := newGraphSession(root, "tsconfig.json")
25
+ if err != nil {
26
+ t.Fatal(err)
27
+ }
28
+ defer session.Close()
29
+ if _, _, _, err := session.Snapshot(); err != nil {
30
+ t.Fatal(err)
31
+ }
32
+
33
+ writeGraphFile(t, filepath.Join(root, "src", "value.native.ts"), "export function winner(): void {}\nexport function nativeWinner(): void {}\n")
34
+ dump, mode, changed, err := session.Snapshot()
35
+ if err != nil {
36
+ t.Fatal(err)
37
+ }
38
+ if dump == nil || mode != serveModeReload || !changed || !hasDumpNode(*dump, "nativeWinner") {
39
+ t.Fatalf("module-suffix candidate = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
40
+ }
41
+ }
@@ -0,0 +1,216 @@
1
+ package main
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+ )
7
+
8
+ // TestServeSessionReloadsSupersedingModuleCandidates verifies a resident
9
+ // session reloads when an unchanged specifier gains a missing candidate that
10
+ // outranks the file TypeScript-Go selected for the first snapshot.
11
+ //
12
+ // A resident session cannot rely on a changed importer or tsconfig to notice a
13
+ // resolution precedence change. Each case pins a distinct resolver family while
14
+ // preserving both files between observations.
15
+ //
16
+ // 1. Load a project whose specifier resolves to the lower-priority target.
17
+ // 2. Create only the absent target that precedes it in that resolver's search.
18
+ // 3. Assert the resident snapshot reloads and exposes the new target when one
19
+ // carries a distinguishing declaration.
20
+ func TestServeSessionReloadsSupersedingModuleCandidates(t *testing.T) {
21
+ cases := []struct {
22
+ name string
23
+ setup func(*testing.T, string) string
24
+ add func(*testing.T, string)
25
+ }{
26
+ {
27
+ name: "relative_extension",
28
+ setup: func(t *testing.T, root string) string {
29
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
30
+ "compilerOptions": { "allowJs": true, "module": "commonjs", "target": "ES2022" },
31
+ "files": ["src/main.ts"]
32
+ }`)
33
+ writeGraphFile(t, filepath.Join(root, "src", "main.ts"), "import { winner } from './value';\nexport function main(): void { winner(); }\n")
34
+ writeGraphFile(t, filepath.Join(root, "src", "value.js"), "export function winner() {}\n")
35
+ return "typescriptWinner"
36
+ },
37
+ add: func(t *testing.T, root string) {
38
+ writeGraphFile(t, filepath.Join(root, "src", "value.ts"), "export function winner(): void {}\nexport function typescriptWinner(): void {}\n")
39
+ },
40
+ },
41
+ {
42
+ name: "ordered_paths",
43
+ setup: func(t *testing.T, root string) string {
44
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
45
+ "compilerOptions": {
46
+ "baseUrl": ".",
47
+ "module": "commonjs",
48
+ "paths": { "@generated/*": ["first/*", "fallback/*"] },
49
+ "target": "ES2022"
50
+ },
51
+ "files": ["src/main.ts"]
52
+ }`)
53
+ writeGraphFile(t, filepath.Join(root, "src", "main.ts"), "import { winner } from '@generated/value';\nexport function main(): void { winner(); }\n")
54
+ writeGraphFile(t, filepath.Join(root, "fallback", "value.ts"), "export function winner(): void {}\n")
55
+ return "firstPathsWinner"
56
+ },
57
+ add: func(t *testing.T, root string) {
58
+ writeGraphFile(t, filepath.Join(root, "first", "value.ts"), "export function winner(): void {}\nexport function firstPathsWinner(): void {}\n")
59
+ },
60
+ },
61
+ {
62
+ name: "root_dirs",
63
+ setup: func(t *testing.T, root string) string {
64
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
65
+ "compilerOptions": {
66
+ "module": "commonjs",
67
+ "rootDirs": ["src", "generated"],
68
+ "target": "ES2022"
69
+ },
70
+ "files": ["src/views/main.ts"]
71
+ }`)
72
+ writeGraphFile(t, filepath.Join(root, "src", "views", "main.ts"), "import { winner } from './template';\nexport function main(): void { winner(); }\n")
73
+ writeGraphFile(t, filepath.Join(root, "generated", "views", "template.ts"), "export function winner(): void {}\n")
74
+ return "rootDirsWinner"
75
+ },
76
+ add: func(t *testing.T, root string) {
77
+ writeGraphFile(t, filepath.Join(root, "src", "views", "template.ts"), "export function winner(): void {}\nexport function rootDirsWinner(): void {}\n")
78
+ },
79
+ },
80
+ {
81
+ name: "nearer_node_modules",
82
+ setup: func(t *testing.T, root string) string {
83
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
84
+ "compilerOptions": { "allowJs": true, "module": "commonjs", "target": "ES2022" },
85
+ "files": ["src/deep/main.ts"]
86
+ }`)
87
+ writeGraphFile(t, filepath.Join(root, "src", "deep", "main.ts"), "import { winner } from 'fixture-package';\nexport function main(): void { winner(); }\n")
88
+ writeGraphFile(t, filepath.Join(root, "node_modules", "fixture-package", "index.js"), "export function winner() {}\n")
89
+ return "nearerNodeModulesWinner"
90
+ },
91
+ add: func(t *testing.T, root string) {
92
+ writeGraphFile(t, filepath.Join(root, "src", "node_modules", "fixture-package", "index.ts"), "export function winner(): void {}\nexport function nearerNodeModulesWinner(): void {}\n")
93
+ },
94
+ },
95
+ {
96
+ name: "package_exports",
97
+ setup: func(t *testing.T, root string) string {
98
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
99
+ "compilerOptions": {
100
+ "allowJs": true,
101
+ "module": "nodenext",
102
+ "moduleResolution": "nodenext",
103
+ "target": "ES2022"
104
+ },
105
+ "files": ["src/main.ts"]
106
+ }`)
107
+ writeGraphFile(t, filepath.Join(root, "src", "main.ts"), "import { winner } from 'fixture-package/feature';\nexport function main(): void { winner(); }\n")
108
+ writeGraphFile(t, filepath.Join(root, "node_modules", "fixture-package", "package.json"), `{
109
+ "name": "fixture-package",
110
+ "exports": { "./feature": ["./dist/first.js", "./dist/fallback.js"] }
111
+ }`)
112
+ writeGraphFile(t, filepath.Join(root, "node_modules", "fixture-package", "dist", "fallback.js"), "export function winner() {}\n")
113
+ return ""
114
+ },
115
+ add: func(t *testing.T, root string) {
116
+ writeGraphFile(t, filepath.Join(root, "node_modules", "fixture-package", "dist", "first.js"), "export function winner() {}\nexport function exportsWinner() {}\n")
117
+ },
118
+ },
119
+ {
120
+ name: "conditional_package_exports",
121
+ setup: func(t *testing.T, root string) string {
122
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
123
+ "compilerOptions": {
124
+ "allowJs": true,
125
+ "module": "nodenext",
126
+ "moduleResolution": "nodenext",
127
+ "target": "ES2022"
128
+ },
129
+ "files": ["src/main.ts"]
130
+ }`)
131
+ writeGraphFile(t, filepath.Join(root, "src", "main.ts"), "import { winner } from 'fixture-package/feature';\nexport function main(): void { winner(); }\n")
132
+ writeGraphFile(t, filepath.Join(root, "node_modules", "fixture-package", "package.json"), `{
133
+ "name": "fixture-package",
134
+ "exports": {
135
+ "./feature": {
136
+ "types": "./dist/types.d.ts",
137
+ "default": "./dist/fallback.js"
138
+ }
139
+ }
140
+ }`)
141
+ writeGraphFile(t, filepath.Join(root, "node_modules", "fixture-package", "dist", "fallback.js"), "export function winner() {}\n")
142
+ return ""
143
+ },
144
+ add: func(t *testing.T, root string) {
145
+ writeGraphFile(t, filepath.Join(root, "node_modules", "fixture-package", "dist", "types.d.ts"), "export declare function winner(): void;\n")
146
+ },
147
+ },
148
+ {
149
+ name: "conditional_package_imports",
150
+ setup: func(t *testing.T, root string) string {
151
+ writeGraphFile(t, filepath.Join(root, "package.json"), `{
152
+ "name": "fixture-project",
153
+ "imports": {
154
+ "#feature": {
155
+ "types": "./generated/types.d.ts",
156
+ "default": "./generated/fallback.js"
157
+ }
158
+ }
159
+ }`)
160
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
161
+ "compilerOptions": {
162
+ "allowJs": true,
163
+ "module": "nodenext",
164
+ "moduleResolution": "nodenext",
165
+ "target": "ES2022"
166
+ },
167
+ "files": ["src/main.ts"]
168
+ }`)
169
+ writeGraphFile(t, filepath.Join(root, "src", "main.ts"), "import { winner } from '#feature';\nexport function main(): void { winner(); }\n")
170
+ writeGraphFile(t, filepath.Join(root, "generated", "fallback.js"), "export function winner() {}\n")
171
+ return ""
172
+ },
173
+ add: func(t *testing.T, root string) {
174
+ writeGraphFile(t, filepath.Join(root, "generated", "types.d.ts"), "export declare function winner(): void;\n")
175
+ },
176
+ },
177
+ {
178
+ name: "file_over_directory_index",
179
+ setup: func(t *testing.T, root string) string {
180
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
181
+ "compilerOptions": { "allowJs": true, "module": "commonjs", "target": "ES2022" },
182
+ "files": ["src/main.ts"]
183
+ }`)
184
+ writeGraphFile(t, filepath.Join(root, "src", "main.ts"), "import { winner } from './feature';\nexport function main(): void { winner(); }\n")
185
+ writeGraphFile(t, filepath.Join(root, "src", "feature", "index.js"), "export function winner() {}\n")
186
+ return "fileWinner"
187
+ },
188
+ add: func(t *testing.T, root string) {
189
+ writeGraphFile(t, filepath.Join(root, "src", "feature.js"), "export function winner() {}\nexport function fileWinner() {}\n")
190
+ },
191
+ },
192
+ }
193
+ for _, test := range cases {
194
+ t.Run(test.name, func(t *testing.T) {
195
+ root := t.TempDir()
196
+ want := test.setup(t, root)
197
+ session, err := newGraphSession(root, "tsconfig.json")
198
+ if err != nil {
199
+ t.Fatal(err)
200
+ }
201
+ defer session.Close()
202
+ if _, _, _, err := session.Snapshot(); err != nil {
203
+ t.Fatal(err)
204
+ }
205
+
206
+ test.add(t, root)
207
+ dump, mode, changed, err := session.Snapshot()
208
+ if err != nil {
209
+ t.Fatal(err)
210
+ }
211
+ if dump == nil || mode != serveModeReload || !changed || (want != "" && !hasDumpNode(*dump, want)) {
212
+ t.Fatalf("superseding candidate = dump:%v mode:%q changed:%v want-node:%q", dump != nil, mode, changed, want)
213
+ }
214
+ })
215
+ }
216
+ }
@@ -0,0 +1,103 @@
1
+ package main
2
+
3
+ import (
4
+ "strings"
5
+ "testing"
6
+
7
+ "github.com/samchon/ttsc/packages/ttsc/driver"
8
+ )
9
+
10
+ // TestServeSessionKeepsChangeDetectionLiveAfterInitialDumpError verifies a
11
+ // rejected first dump still advances the session into its change-detection
12
+ // state. The invalid relative cwd is a deterministic path-mapper failure; once
13
+ // corrected, the same captured generation is retried and can become the first
14
+ // published snapshot.
15
+ func TestServeSessionKeepsChangeDetectionLiveAfterInitialDumpError(t *testing.T) {
16
+ root := graphSessionFixture(t)
17
+ compiler, diags, err := driver.NewSession(root, "tsconfig.json", driver.LoadProgramOptions{})
18
+ if err != nil {
19
+ t.Fatal(err)
20
+ }
21
+ if compiler == nil {
22
+ t.Fatalf("NewSession returned nil session (diagnostics: %v)", diags)
23
+ }
24
+ session := &graphSession{
25
+ cwd: root,
26
+ tsconfig: "tsconfig.json",
27
+ compiler: compiler,
28
+ }
29
+ defer session.Close()
30
+ if err := session.captureState(); err != nil {
31
+ t.Fatal(err)
32
+ }
33
+ session.cwd = "relative-project"
34
+
35
+ dump, _, changed, err := session.Snapshot()
36
+ if err == nil || !strings.Contains(err.Error(), "project root") {
37
+ t.Fatalf("initial dump error = %v, want absolute-root rejection", err)
38
+ }
39
+ if dump != nil || changed || !session.initialized || session.pendingDumpMode != serveModeInitial {
40
+ t.Fatalf(
41
+ "failed initial state = dump:%v changed:%v initialized:%v pending:%q",
42
+ dump != nil,
43
+ changed,
44
+ session.initialized,
45
+ session.pendingDumpMode,
46
+ )
47
+ }
48
+
49
+ session.cwd = root
50
+ dump, mode, changed, err := session.Snapshot()
51
+ if err != nil {
52
+ t.Fatal(err)
53
+ }
54
+ if dump == nil || mode != serveModeInitial || !changed {
55
+ t.Fatalf("repaired initial dump = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
56
+ }
57
+ }
58
+
59
+ // TestServeSessionRetriesAPendingDumpBeforeUnchanged pins the state boundary
60
+ // introduced by dump-time path validation. A failed dump may occur after the
61
+ // compiler generation and its hashes were captured; the next request must
62
+ // retry that generation instead of confirming the older client graph as
63
+ // unchanged.
64
+ func TestServeSessionRetriesAPendingDumpBeforeUnchanged(t *testing.T) {
65
+ root := graphSessionFixture(t)
66
+ compiler, diags, err := driver.NewSession(root, "tsconfig.json", driver.LoadProgramOptions{})
67
+ if err != nil {
68
+ t.Fatal(err)
69
+ }
70
+ if compiler == nil {
71
+ t.Fatalf("NewSession returned nil session (diagnostics: %v)", diags)
72
+ }
73
+ session := &graphSession{
74
+ cwd: root,
75
+ tsconfig: "tsconfig.json",
76
+ compiler: compiler,
77
+ initialized: true,
78
+ pendingDumpMode: serveModeReload,
79
+ }
80
+ defer session.Close()
81
+ if err := session.captureState(); err != nil {
82
+ t.Fatal(err)
83
+ }
84
+
85
+ dump, mode, changed, err := session.Snapshot()
86
+ if err != nil {
87
+ t.Fatal(err)
88
+ }
89
+ if dump == nil || mode != serveModeReload || !changed {
90
+ t.Fatalf("pending dump retry = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
91
+ }
92
+ if session.pendingDumpMode != "" {
93
+ t.Fatalf("successful retry left pending mode %q", session.pendingDumpMode)
94
+ }
95
+
96
+ dump, mode, changed, err = session.Snapshot()
97
+ if err != nil {
98
+ t.Fatal(err)
99
+ }
100
+ if dump != nil || mode != serveModeUnchanged || changed {
101
+ t.Fatalf("retry did not converge: dump:%v mode:%q changed:%v", dump != nil, mode, changed)
102
+ }
103
+ }