ttsc 0.18.3 → 0.19.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 (173) hide show
  1. package/README.md +5 -6
  2. package/cmd/graphbench/main.go +1 -1
  3. package/cmd/platform/main.go +1 -48
  4. package/cmd/ttsc/build_branches_test.go +5 -1
  5. package/cmd/ttsc/main.go +0 -50
  6. package/cmd/ttsc/main_alias_branches_test.go +6 -12
  7. package/cmd/ttscgraph/dump.go +13 -3
  8. package/cmd/ttscgraph/main.go +18 -12
  9. package/cmd/ttscgraph/run_help_version_and_bad_flags_exit_codes_test.go +23 -5
  10. package/cmd/ttscgraph/serve.go +863 -0
  11. package/cmd/ttscgraph/serve_protocol_rejects_invalid_request_line_test.go +43 -0
  12. package/cmd/ttscgraph/serve_protocol_retries_session_creation_test.go +83 -0
  13. package/cmd/ttscgraph/serve_protocol_reuses_session_test.go +35 -0
  14. package/cmd/ttscgraph/serve_session_applies_source_edit_incrementally_test.go +55 -0
  15. package/cmd/ttscgraph/serve_session_fails_closed_on_deleted_config_test.go +68 -0
  16. package/cmd/ttscgraph/serve_session_fails_closed_on_invalid_config_test.go +50 -0
  17. package/cmd/ttscgraph/serve_session_keeps_missing_literal_root_unchanged_test.go +46 -0
  18. package/cmd/ttscgraph/serve_session_rebuilds_import_graph_change_test.go +60 -0
  19. package/cmd/ttscgraph/serve_session_reloads_created_literal_root_test.go +44 -0
  20. package/cmd/ttscgraph/serve_session_reloads_extended_config_change_test.go +45 -0
  21. package/cmd/ttscgraph/serve_session_reloads_newly_resolved_conditional_export_test.go +60 -0
  22. package/cmd/ttscgraph/serve_session_reloads_newly_resolved_config_types_test.go +44 -0
  23. package/cmd/ttscgraph/serve_session_reloads_newly_resolved_excluded_import_test.go +62 -0
  24. package/cmd/ttscgraph/serve_session_reloads_newly_resolved_package_export_test.go +46 -0
  25. package/cmd/ttscgraph/serve_session_reloads_newly_resolved_package_import_test.go +46 -0
  26. package/cmd/ttscgraph/serve_session_reloads_newly_resolved_path_alias_test.go +47 -0
  27. package/cmd/ttscgraph/serve_session_reloads_newly_resolved_reference_path_test.go +42 -0
  28. package/cmd/ttscgraph/serve_session_reloads_newly_resolved_root_dirs_target_test.go +46 -0
  29. package/cmd/ttscgraph/serve_session_reloads_newly_resolved_type_reference_test.go +42 -0
  30. package/cmd/ttscgraph/serve_session_reloads_root_file_set_test.go +52 -0
  31. package/cmd/ttscgraph/serve_session_reloads_source_deleted_during_load_test.go +60 -0
  32. package/cmd/ttscgraph/serve_session_reloads_transitive_project_reference_test.go +55 -0
  33. package/cmd/ttscgraph/serve_session_reloads_when_program_plugin_linked_test.go +53 -0
  34. package/cmd/ttscgraph/serve_session_reuses_unchanged_preamble_source_test.go +45 -0
  35. package/cmd/ttscgraph/serve_session_reuses_unchanged_snapshot_test.go +52 -0
  36. package/cmd/ttscgraph/serve_session_revisits_source_edited_during_load_test.go +61 -0
  37. package/cmd/ttscgraph/serve_session_tolerates_os_invalid_resolution_candidate_test.go +56 -0
  38. package/cmd/ttscgraph/serve_session_tracks_all_module_specifier_forms_test.go +41 -0
  39. package/cmd/ttscgraph/serve_session_tracks_diamond_project_references_once_test.go +61 -0
  40. package/cmd/ttscserver/main.go +5 -3
  41. package/driver/lsp.go +13 -1
  42. package/driver/plugins.go +56 -5
  43. package/driver/program.go +25 -10
  44. package/driver/rewrite.go +381 -21
  45. package/driver/session.go +11 -3
  46. package/go.mod +1 -0
  47. package/internal/graph/build.go +297 -3
  48. package/internal/graph/build_records_a_node_per_top_level_declaration_test.go +16 -2
  49. package/internal/graph/diagnostics.go +5 -4
  50. package/internal/graph/dump.go +60 -4
  51. package/internal/graph/dump_is_a_function_of_the_source_test.go +71 -0
  52. package/internal/graph/dump_serializes_the_full_graph_test.go +16 -4
  53. package/internal/graph/edges.go +122 -11
  54. package/internal/graph/exports.go +74 -17
  55. package/internal/graph/graph.go +33 -1
  56. package/internal/graph/node_identity_is_position_invariant_test.go +4 -2
  57. package/internal/graph/resolve.go +24 -0
  58. package/internal/graphsymbols/provider.go +50 -14
  59. package/internal/lspserver/lsp_native_plugin_source.go +77 -23
  60. package/internal/lspserver/lsp_plugin.go +19 -2
  61. package/internal/lspserver/lsp_proxy.go +244 -52
  62. package/internal/lspserver/lsp_server.go +35 -27
  63. package/internal/lspserver/lsp_symbols.go +25 -16
  64. package/lib/TtscCompiler.d.ts +13 -2
  65. package/lib/TtscCompiler.js +30 -20
  66. package/lib/TtscCompiler.js.map +1 -1
  67. package/lib/TtscService.js +9 -7
  68. package/lib/TtscService.js.map +1 -1
  69. package/lib/compiler/internal/compileProjectInMemory.js +15 -1
  70. package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
  71. package/lib/compiler/internal/project/createNativeProjectContextArgs.d.ts +3 -0
  72. package/lib/compiler/internal/project/createNativeProjectContextArgs.js +14 -0
  73. package/lib/compiler/internal/project/createNativeProjectContextArgs.js.map +1 -0
  74. package/lib/compiler/internal/project/readProjectConfig.js +53 -4
  75. package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
  76. package/lib/compiler/internal/project/resolveProjectConfig.d.ts +9 -2
  77. package/lib/compiler/internal/project/resolveProjectConfig.js +45 -16
  78. package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
  79. package/lib/compiler/internal/residentTransformProcess.d.ts +16 -5
  80. package/lib/compiler/internal/residentTransformProcess.js +68 -20
  81. package/lib/compiler/internal/residentTransformProcess.js.map +1 -1
  82. package/lib/compiler/internal/runBuild.d.ts +18 -0
  83. package/lib/compiler/internal/runBuild.js +220 -24
  84. package/lib/compiler/internal/runBuild.js.map +1 -1
  85. package/lib/compiler/internal/runSingleFileEmit.js +7 -4
  86. package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
  87. package/lib/compiler/internal/sharedHostHelpers.d.ts +16 -0
  88. package/lib/compiler/internal/sharedHostHelpers.js +23 -0
  89. package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
  90. package/lib/compiler/internal/startResidentTransform.js +18 -1
  91. package/lib/compiler/internal/startResidentTransform.js.map +1 -1
  92. package/lib/compiler/internal/transformProjectInMemory.js +34 -8
  93. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  94. package/lib/flags/parser.d.ts +17 -0
  95. package/lib/flags/parser.js +11 -1
  96. package/lib/flags/parser.js.map +1 -1
  97. package/lib/flags/schema.js +7 -0
  98. package/lib/flags/schema.js.map +1 -1
  99. package/lib/launcher/internal/prepareExecution.js +21 -2
  100. package/lib/launcher/internal/prepareExecution.js.map +1 -1
  101. package/lib/launcher/internal/runTtsc.js +13 -53
  102. package/lib/launcher/internal/runTtsc.js.map +1 -1
  103. package/lib/launcher/internal/runTtscserver.js +11 -0
  104. package/lib/launcher/internal/runTtscserver.js.map +1 -1
  105. package/lib/launcher/internal/runTtsx.js +27 -11
  106. package/lib/launcher/internal/runTtsx.js.map +1 -1
  107. package/lib/launcher/internal/runtimeHooks.d.ts +81 -0
  108. package/lib/launcher/internal/runtimeHooks.js +632 -43
  109. package/lib/launcher/internal/runtimeHooks.js.map +1 -1
  110. package/lib/launcher/internal/servedSourceMap.d.ts +25 -0
  111. package/lib/launcher/internal/servedSourceMap.js +164 -0
  112. package/lib/launcher/internal/servedSourceMap.js.map +1 -0
  113. package/lib/plugin/internal/buildSourcePlugin.d.ts +146 -2
  114. package/lib/plugin/internal/buildSourcePlugin.js +651 -113
  115. package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
  116. package/lib/plugin/internal/loadProjectPlugins.d.ts +7 -0
  117. package/lib/plugin/internal/loadProjectPlugins.js +33 -10
  118. package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
  119. package/lib/structures/ITtscCompilerContext.d.ts +16 -2
  120. package/lib/structures/ITtscCompilerResult.d.ts +8 -5
  121. package/lib/structures/ITtscPluginCapabilities.d.ts +8 -0
  122. package/lib/structures/ITtscPluginFactoryContext.d.ts +14 -0
  123. package/lib/structures/internal/ITtscParsedProjectConfig.d.ts +3 -0
  124. package/lib/structures/internal/ITtscProjectIdentity.d.ts +17 -0
  125. package/lib/structures/internal/ITtscProjectIdentity.js +3 -0
  126. package/lib/structures/internal/ITtscProjectIdentity.js.map +1 -0
  127. package/lib/structures/internal/TtscCommonOptions.d.ts +8 -0
  128. package/package.json +11 -8
  129. package/shim/ast/lint.go +10 -0
  130. package/shim/ast/shim.go +44 -0
  131. package/shim/checker/enums_gen.go +19 -0
  132. package/shim/checker/shim.go +130 -0
  133. package/shim/diagnosticwriter/lint.go +30 -3
  134. package/shim/printer/shim.go +22 -4
  135. package/shim/scanner/regexp.go +27 -0
  136. package/src/TtscCompiler.ts +29 -22
  137. package/src/TtscService.ts +15 -7
  138. package/src/compiler/internal/compileProjectInMemory.ts +18 -1
  139. package/src/compiler/internal/project/createNativeProjectContextArgs.ts +15 -0
  140. package/src/compiler/internal/project/readProjectConfig.ts +61 -5
  141. package/src/compiler/internal/project/resolveProjectConfig.ts +51 -21
  142. package/src/compiler/internal/residentTransformProcess.ts +95 -21
  143. package/src/compiler/internal/runBuild.ts +313 -32
  144. package/src/compiler/internal/runSingleFileEmit.ts +7 -4
  145. package/src/compiler/internal/sharedHostHelpers.ts +24 -0
  146. package/src/compiler/internal/startResidentTransform.ts +21 -1
  147. package/src/compiler/internal/transformProjectInMemory.ts +49 -6
  148. package/src/flags/parser.ts +28 -1
  149. package/src/flags/schema.ts +8 -0
  150. package/src/launcher/internal/prepareExecution.ts +22 -2
  151. package/src/launcher/internal/runTtsc.ts +13 -61
  152. package/src/launcher/internal/runTtscserver.ts +14 -0
  153. package/src/launcher/internal/runTtsx.ts +28 -13
  154. package/src/launcher/internal/runtimeHooks.ts +771 -53
  155. package/src/launcher/internal/servedSourceMap.ts +183 -0
  156. package/src/plugin/internal/buildSourcePlugin.ts +859 -122
  157. package/src/plugin/internal/loadProjectPlugins.ts +36 -6
  158. package/src/structures/ITtscCompilerContext.ts +17 -2
  159. package/src/structures/ITtscCompilerResult.ts +8 -5
  160. package/src/structures/ITtscPluginCapabilities.ts +9 -0
  161. package/src/structures/ITtscPluginFactoryContext.ts +15 -0
  162. package/src/structures/internal/ITtscParsedProjectConfig.ts +3 -0
  163. package/src/structures/internal/ITtscProjectIdentity.ts +17 -0
  164. package/src/structures/internal/TtscCommonOptions.ts +8 -0
  165. package/tools/shim_audit/baseline.json +16 -7
  166. package/tools/shim_audit/main.go +777 -71
  167. package/tools/shim_audit/main_test.go +28 -1
  168. package/tools/shim_audit/producer_exemptions_require_rationale_test.go +42 -0
  169. package/tools/shim_audit/producer_surface_includes_exposed_methods_test.go +154 -0
  170. package/tools/shim_audit/producer_surface_rejects_unproducible_reference_test.go +142 -0
  171. package/utility/host.go +46 -20
  172. package/utility/serve.go +1 -1
  173. package/shim/checker/signature_api_completeness.go +0 -58
@@ -0,0 +1,61 @@
1
+ package main
2
+
3
+ import (
4
+ "os"
5
+ "path/filepath"
6
+ "testing"
7
+
8
+ "github.com/samchon/ttsc/packages/ttsc/driver"
9
+ )
10
+
11
+ // TestServeSessionRevisitsSourceEditedDuringLoad verifies a source edited
12
+ // between compiler load and state capture is refreshed on the next snapshot.
13
+ //
14
+ // Hashing the raw disk bytes at capture time would bless content the resident
15
+ // program never parsed: disk and graph would disagree until an unrelated
16
+ // change. The capture must hash the resident text instead, so the next
17
+ // snapshot sees the disk mismatch and applies the missed edit.
18
+ //
19
+ // 1. Load a session, then rewrite the source before capturing state.
20
+ // 2. Take a snapshot and assert it refreshes to the on-disk declaration.
21
+ // 3. Take another snapshot and assert the session has converged (unchanged).
22
+ func TestServeSessionRevisitsSourceEditedDuringLoad(t *testing.T) {
23
+ root := graphSessionFixture(t)
24
+ compiler, diags, err := driver.NewSession(root, "tsconfig.json", driver.LoadProgramOptions{})
25
+ if err != nil {
26
+ t.Fatal(err)
27
+ }
28
+ if compiler == nil {
29
+ t.Fatalf("NewSession returned nil session (diagnostics: %v)", diags)
30
+ }
31
+ file := filepath.Join(root, "src", "index.ts")
32
+ if err := os.WriteFile(file, []byte("export class EditedDuringLoad {}\n"), 0o644); err != nil {
33
+ t.Fatal(err)
34
+ }
35
+ session := &graphSession{
36
+ cwd: root,
37
+ tsconfig: "tsconfig.json",
38
+ compiler: compiler,
39
+ initialized: true,
40
+ }
41
+ defer session.Close()
42
+ if err := session.captureState(); err != nil {
43
+ t.Fatal(err)
44
+ }
45
+
46
+ dump, _, changed, err := session.Snapshot()
47
+ if err != nil {
48
+ t.Fatal(err)
49
+ }
50
+ if dump == nil || !changed || !hasDumpNode(*dump, "EditedDuringLoad") || hasDumpNode(*dump, "BeforeEdit") {
51
+ t.Fatalf("edited-during-load source was not refreshed: dump:%v changed:%v", dump != nil, changed)
52
+ }
53
+
54
+ dump, mode, changed, err := session.Snapshot()
55
+ if err != nil {
56
+ t.Fatal(err)
57
+ }
58
+ if dump != nil || mode != "unchanged" || changed {
59
+ t.Fatalf("session did not converge after the missed edit: dump:%v mode:%q changed:%v", dump != nil, mode, changed)
60
+ }
61
+ }
@@ -0,0 +1,56 @@
1
+ package main
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+ )
7
+
8
+ // TestServeSessionToleratesOsInvalidResolutionCandidate verifies unresolved
9
+ // module specifiers that name OS-unparseable paths never break the session.
10
+ //
11
+ // Bundler-style specifiers (`./style.css?inline`) and URL imports (`data:`)
12
+ // stay unresolved in tsgo and become freshness candidates. On Windows those
13
+ // candidate paths fail os.ReadFile with ERROR_INVALID_NAME, which is neither
14
+ // ErrNotExist nor ErrInvalid; treating that as a snapshot error would make
15
+ // every graph request fail forever on such a project.
16
+ //
17
+ // 1. Open a session whose only root imports a query-suffixed CSS path and a
18
+ // data: URL.
19
+ // 2. Assert the session initializes and serves its initial dump.
20
+ // 3. Assert an untouched second snapshot reports unchanged.
21
+ func TestServeSessionToleratesOsInvalidResolutionCandidate(t *testing.T) {
22
+ root := t.TempDir()
23
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
24
+ "compilerOptions": { "target": "ES2022", "module": "commonjs" },
25
+ "files": ["src/index.ts"]
26
+ }`)
27
+ writeGraphFile(t, filepath.Join(root, "src", "index.ts"), `
28
+ // @ts-expect-error bundler-only specifier stays unresolved for tsgo
29
+ import styles from "./style.css?inline";
30
+ // @ts-expect-error data: URLs resolve at runtime, not through the compiler
31
+ import remote from "data:text/plain,hello";
32
+ export const value: unknown[] = [styles, remote];
33
+ `)
34
+
35
+ session, err := newGraphSession(root, "tsconfig.json")
36
+ if err != nil {
37
+ t.Fatal(err)
38
+ }
39
+ defer session.Close()
40
+
41
+ dump, mode, changed, err := session.Snapshot()
42
+ if err != nil {
43
+ t.Fatal(err)
44
+ }
45
+ if dump == nil || mode != "initial" || !changed {
46
+ t.Fatalf("initial snapshot = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
47
+ }
48
+
49
+ dump, mode, changed, err = session.Snapshot()
50
+ if err != nil {
51
+ t.Fatal(err)
52
+ }
53
+ if dump != nil || mode != "unchanged" || changed {
54
+ t.Fatalf("untouched snapshot = dump:%v mode:%q changed:%v", dump != nil, mode, changed)
55
+ }
56
+ }
@@ -0,0 +1,41 @@
1
+ package main
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+ )
7
+
8
+ // TestServeSessionTracksAllModuleSpecifierForms verifies that every TypeScript
9
+ // syntax which can trigger module resolution contributes missing-file
10
+ // candidates to the freshness snapshot.
11
+ func TestServeSessionTracksAllModuleSpecifierForms(t *testing.T) {
12
+ root := t.TempDir()
13
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
14
+ "compilerOptions": { "target": "ES2022", "module": "commonjs", "strict": true },
15
+ "files": ["src/index.ts"]
16
+ }`)
17
+ writeGraphFile(t, filepath.Join(root, "src", "index.ts"), `
18
+ import legacy = require("./legacy");
19
+ export { exported } from "./exported";
20
+ export type Deferred = import("./types").Deferred;
21
+ export const lazy = import("./lazy");
22
+ export const common = require("./common");
23
+ export const result = legacy();
24
+ `)
25
+
26
+ session, err := newGraphSession(root, "tsconfig.json")
27
+ if err != nil {
28
+ t.Fatal(err)
29
+ }
30
+ defer session.Close()
31
+
32
+ for _, name := range []string{"legacy.ts", "exported.ts", "types.ts", "lazy.ts", "common.ts"} {
33
+ candidate := filepath.Join(root, "src", name)
34
+ state, ok := session.auxStates[candidate]
35
+ if !ok {
36
+ t.Errorf("module candidate %s was not tracked", name)
37
+ } else if state.Exists {
38
+ t.Errorf("missing module candidate %s unexpectedly exists", name)
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,61 @@
1
+ package main
2
+
3
+ import (
4
+ "path/filepath"
5
+ "testing"
6
+ )
7
+
8
+ // TestServeSessionTracksDiamondProjectReferencesOnce verifies the config
9
+ // traversal terminates and deduplicates when two references share a child.
10
+ //
11
+ // A diamond (root -> left, root -> right, both -> shared) visits the shared
12
+ // config twice; without the seen-set the traversal would duplicate tracked
13
+ // state, and on a reference cycle it would never terminate. The shared leaf
14
+ // must appear in the freshness state exactly once and the session must stay
15
+ // stable across untouched snapshots.
16
+ //
17
+ // 1. Open a session over a diamond of project references.
18
+ // 2. Assert the shared leaf config is hash-tracked.
19
+ // 3. Assert an untouched snapshot reports unchanged.
20
+ func TestServeSessionTracksDiamondProjectReferencesOnce(t *testing.T) {
21
+ root := t.TempDir()
22
+ writeGraphFile(t, filepath.Join(root, "tsconfig.json"), `{
23
+ "files": ["src/index.ts"],
24
+ "references": [{ "path": "./left" }, { "path": "./right" }]
25
+ }`)
26
+ writeGraphFile(t, filepath.Join(root, "src", "index.ts"), "export const root = 1;\n")
27
+ for _, side := range []string{"left", "right"} {
28
+ writeGraphFile(t, filepath.Join(root, side, "tsconfig.json"), `{
29
+ "compilerOptions": { "composite": true },
30
+ "files": ["src/index.ts"],
31
+ "references": [{ "path": "../shared" }]
32
+ }`)
33
+ writeGraphFile(t, filepath.Join(root, side, "src", "index.ts"), "export const side = 1;\n")
34
+ }
35
+ writeGraphFile(t, filepath.Join(root, "shared", "tsconfig.json"), `{
36
+ "compilerOptions": { "composite": true },
37
+ "files": ["src/index.ts"]
38
+ }`)
39
+ writeGraphFile(t, filepath.Join(root, "shared", "src", "index.ts"), "export const shared = 1;\n")
40
+
41
+ session, err := newGraphSession(root, "tsconfig.json")
42
+ if err != nil {
43
+ t.Fatal(err)
44
+ }
45
+ defer session.Close()
46
+ sharedConfig := filepath.ToSlash(filepath.Join(root, "shared", "tsconfig.json"))
47
+ if _, ok := session.configHashes[sharedConfig]; !ok {
48
+ t.Fatalf("shared diamond config was not tracked: %s; got %#v", sharedConfig, session.configHashes)
49
+ }
50
+ if _, _, _, err := session.Snapshot(); err != nil {
51
+ t.Fatal(err)
52
+ }
53
+
54
+ dump, mode, changed, err := session.Snapshot()
55
+ if err != nil {
56
+ t.Fatal(err)
57
+ }
58
+ if dump != nil || mode != "unchanged" || changed {
59
+ t.Fatalf("diamond references churned: dump:%v mode:%q changed:%v", dump != nil, mode, changed)
60
+ }
61
+ }
@@ -133,9 +133,11 @@ func runLSP(args []string) int {
133
133
  }
134
134
 
135
135
  // The SymbolProvider answers textDocument/documentSymbol and
136
- // textDocument/references locally from ttsc's compiler-backed code graph;
137
- // tsgo's native LSP does not implement them. It loads the program lazily on
138
- // the first such request, so wiring it here adds no startup cost.
136
+ // textDocument/references from ttsc's compiler-backed code graph. tsgo
137
+ // implements both methods itself, so the proxy forwards to tsgo whenever it
138
+ // advertises the capability and consults this provider only as a fallback
139
+ // (tsgo did not advertise). It loads the program lazily on the first such
140
+ // request, so wiring it here adds no startup cost.
139
141
  symbolProvider := graphsymbols.NewProvider(cwd, strings.TrimSpace(*tsconfigFlag))
140
142
 
141
143
  err = runLSPServer(ctx, lspserver.LSPServerOptions{
package/driver/lsp.go CHANGED
@@ -49,6 +49,12 @@ type LSPTextEdit = lspserver.LSPTextEdit
49
49
  // LSPDocumentVersion is the driver-level alias for lspserver.LSPDocumentVersion.
50
50
  type LSPDocumentVersion = lspserver.LSPDocumentVersion
51
51
 
52
+ // LSPProjectDiagnostics is the driver-level alias for a project publication.
53
+ type LSPProjectDiagnostics = lspserver.LSPProjectDiagnostics
54
+
55
+ // LSPDiagnosticsResult separates document and project plugin diagnostics.
56
+ type LSPDiagnosticsResult = lspserver.LSPDiagnosticsResult
57
+
52
58
  // LSPSymbolKind is the driver-level alias for lspserver.LSPSymbolKind.
53
59
  type LSPSymbolKind = lspserver.LSPSymbolKind
54
60
 
@@ -104,6 +110,12 @@ type LSPServerOptions = lspserver.LSPServerOptions
104
110
  // LSPUpstreamRunner is the driver-level alias for lspserver.LSPUpstreamRunner.
105
111
  type LSPUpstreamRunner = lspserver.LSPUpstreamRunner
106
112
 
113
+ // LSPUpstreamValidator is the driver-level alias for lspserver.LSPUpstreamValidator.
114
+ type LSPUpstreamValidator = lspserver.LSPUpstreamValidator
115
+
116
+ // LSPUpstream is the driver-level alias for lspserver.LSPUpstream.
117
+ type LSPUpstream = lspserver.LSPUpstream
118
+
107
119
  // MaxFrameBytes is the maximum byte length of a single JSON-RPC frame
108
120
  // the proxy will read without returning ErrFrameTooLarge.
109
121
  const MaxFrameBytes = lspserver.MaxFrameBytes
@@ -118,6 +130,7 @@ var ErrFrameClosed = lspserver.ErrFrameClosed
118
130
  var ErrFrameTooLarge = lspserver.ErrFrameTooLarge
119
131
  var ErrInvalidJSONRPC = lspserver.ErrInvalidJSONRPC
120
132
  var ErrLSPUpstreamPanic = lspserver.ErrLSPUpstreamPanic
133
+ var ErrLSPUpstreamRunnerRequired = lspserver.ErrLSPUpstreamRunnerRequired
121
134
  var ErrLSPCwdRequired = lspserver.ErrLSPCwdRequired
122
135
  var ErrLSPTsgoBinaryRequired = lspserver.ErrLSPTsgoBinaryRequired
123
136
 
@@ -128,7 +141,6 @@ var NewFrameReader = lspserver.NewFrameReader
128
141
  var WriteFrame = lspserver.WriteFrame
129
142
  var ParseEnvelope = lspserver.ParseEnvelope
130
143
  var RecoverPanicAs = lspserver.RecoverPanicAs
131
- var WithUpstreamRunnerForTest = lspserver.WithUpstreamRunnerForTest
132
144
  var RunLSPServer = lspserver.RunLSPServer
133
145
  var DenyNpmInstall = lspserver.DenyNpmInstall
134
146
 
package/driver/plugins.go CHANGED
@@ -4,6 +4,7 @@ import (
4
4
  "encoding/json"
5
5
  "fmt"
6
6
  "os"
7
+ "path/filepath"
7
8
  "strings"
8
9
  )
9
10
 
@@ -13,6 +14,40 @@ import (
13
14
  // linked plugins are active.
14
15
  const LinkedPluginsEnv = "TTSC_LINKED_PLUGINS_JSON"
15
16
 
17
+ // PluginConfigDirEnv is the environment variable through which the ttsc
18
+ // launcher passes the project root that plugin config-file discovery and
19
+ // relative "configFile" resolution anchor at. The launcher sets it on every
20
+ // native plugin spawn; it matters when the compiled tsconfig is a generated
21
+ // wrapper outside the project — e.g. @ttsc/unplugin writes a compiler-options
22
+ // overlay into the system temp directory that `extends` the real project
23
+ // config — where the tsconfig directory no longer identifies the project and
24
+ // an unanchored discovery walk would climb the temp tree instead. It rides
25
+ // the environment rather than a CLI flag so third-party native hosts with
26
+ // strict flag sets are unaffected and linked plugins running inside them
27
+ // still receive it.
28
+ const PluginConfigDirEnv = "TTSC_PLUGIN_CONFIG_DIR"
29
+
30
+ // PluginConfigBaseDir returns the directory where a plugin anchors its
31
+ // config-file discovery walk and resolves relative "configFile" paths.
32
+ // The explicit PluginConfigDirEnv channel wins when set; otherwise the
33
+ // tsconfig's directory is used, falling back to cwd when no tsconfig is set.
34
+ func PluginConfigBaseDir(cwd, tsconfigPath string) string {
35
+ if dir := strings.TrimSpace(os.Getenv(PluginConfigDirEnv)); dir != "" {
36
+ if !filepath.IsAbs(dir) && cwd != "" {
37
+ dir = filepath.Join(cwd, dir)
38
+ }
39
+ return filepath.Clean(dir)
40
+ }
41
+ if tsconfigPath != "" {
42
+ resolved := tsconfigPath
43
+ if !filepath.IsAbs(resolved) {
44
+ resolved = filepath.Join(cwd, resolved)
45
+ }
46
+ return filepath.Dir(resolved)
47
+ }
48
+ return cwd
49
+ }
50
+
16
51
  // PluginEntry is the manifest shape ttsc passes to driver-level plugins.
17
52
  type PluginEntry struct {
18
53
  Config map[string]any `json:"config"`
@@ -42,11 +77,14 @@ type ProgramPlugin interface {
42
77
  // EmitTransformPlugin contributes an emit-phase AST transformer. The returned
43
78
  // PluginTransform runs first in tsgo's per-file emit chain, sharing the emit
44
79
  // EmitContext with the builtin transformers, so a plugin returns AST instead of
45
- // spliced text: it injects its own imports with ec.Factory and references them
46
- // via NewGeneratedNameForNode, and tsgo's module-transform emits the require and
47
- // aliases the references itself. This is the AST-integration replacement for the
48
- // ProgramPlugin + RewriteSet text-splice model. A plugin whose returned
49
- // transform is nil contributes nothing.
80
+ // spliced text. For an injected import, allocate its binding once with
81
+ // ec.Factory.NewUniqueNameEx and the Optimistic | FileLevel flags, then reuse
82
+ // that identifier for every reference. NewGeneratedNameForNode on a string
83
+ // literal uses tsgo's temp-name channel and can be shadowed by downlevel temps.
84
+ // Tsgo's module-transform emits the require and aliases the references itself.
85
+ // This is the AST-integration replacement for the ProgramPlugin + RewriteSet
86
+ // text-splice model. A plugin whose returned transform is nil contributes
87
+ // nothing.
50
88
  type EmitTransformPlugin interface {
51
89
  EmitTransform(PluginContext) (PluginTransform, error)
52
90
  }
@@ -130,6 +168,19 @@ func (state linkedPluginState) apply(prog *Program) error {
130
168
  return nil
131
169
  }
132
170
 
171
+ func (state linkedPluginState) hasProgramPlugins() bool {
172
+ for index := range state.entries {
173
+ plugin, ok := registeredPlugin(index)
174
+ if !ok {
175
+ continue
176
+ }
177
+ if _, ok := plugin.(ProgramPlugin); ok {
178
+ return true
179
+ }
180
+ }
181
+ return false
182
+ }
183
+
133
184
  // emitTransforms collects an emit-phase PluginTransform from every registered
134
185
  // EmitTransformPlugin, in registration order. Entries that do not implement
135
186
  // EmitTransformPlugin, or whose transform is nil, are skipped.
package/driver/program.go CHANGED
@@ -81,15 +81,14 @@ func NewLintDiagnostic(
81
81
  lint: lint,
82
82
  }
83
83
  if file != nil {
84
+ pos = lint.Pos()
84
85
  d.File = file.FileName()
85
- if pos >= 0 {
86
- length := lint.Len()
87
- d.Start = &pos
88
- d.Length = &length
89
- line, col := shimscanner.GetECMALineAndByteOffsetOfPosition(file, pos)
90
- d.Line = line + 1
91
- d.Column = col + 1
92
- }
86
+ length := lint.Len()
87
+ d.Start = &pos
88
+ d.Length = &length
89
+ line, col := shimscanner.GetECMALineAndByteOffsetOfPosition(file, pos)
90
+ d.Line = line + 1
91
+ d.Column = col + 1
93
92
  }
94
93
  return d
95
94
  }
@@ -456,10 +455,10 @@ type sourcePreambleFS struct {
456
455
 
457
456
  func (fs sourcePreambleFS) ReadFile(filePath string) (string, bool) {
458
457
  contents, ok := fs.FS.ReadFile(filePath)
459
- if !ok || !isSourcePreambleTarget(filePath) {
458
+ if !ok {
460
459
  return contents, ok
461
460
  }
462
- return ApplySourcePreamble(contents, fs.preamble), true
461
+ return ApplySourcePreambleToFile(filePath, contents, fs.preamble), true
463
462
  }
464
463
 
465
464
  // isSourcePreambleTarget reports whether the preamble should be injected into
@@ -480,6 +479,15 @@ func isSourcePreambleTarget(filePath string) bool {
480
479
  return false
481
480
  }
482
481
 
482
+ // ApplySourcePreambleToFile applies a generated preamble only when filePath is
483
+ // a non-declaration TypeScript or JavaScript source, matching sourcePreambleFS.
484
+ func ApplySourcePreambleToFile(filePath string, text string, preamble string) string {
485
+ if !isSourcePreambleTarget(filePath) {
486
+ return text
487
+ }
488
+ return ApplySourcePreamble(text, preamble)
489
+ }
490
+
483
491
  // ApplySourcePreamble inserts a generated source preamble without moving the
484
492
  // file's BOM or hashbang away from the first bytes of the physical output.
485
493
  func ApplySourcePreamble(text string, preamble string) string {
@@ -542,6 +550,13 @@ func (p *Program) ApplyLinkedPlugins() error {
542
550
  return p.pluginsApplyErr
543
551
  }
544
552
 
553
+ // HasLinkedProgramPlugins reports whether the loaded project has an active
554
+ // ProgramPlugin. Those hooks mutate parsed ASTs in place and therefore require
555
+ // a fresh Program rather than Session's incremental source replacement.
556
+ func (p *Program) HasLinkedProgramPlugins() bool {
557
+ return p != nil && p.plugins.hasProgramPlugins()
558
+ }
559
+
545
560
  // Diagnostics returns project diagnostics that must block compilation or
546
561
  // runtime execution before any JavaScript is emitted or evaluated.
547
562
  func (p *Program) Diagnostics() []Diagnostic {