ttsc 0.12.2 → 0.12.4

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 (134) hide show
  1. package/cmd/platform/main.go +9 -0
  2. package/cmd/ttsc/api_compile.go +109 -101
  3. package/cmd/ttsc/api_compile_branches_test.go +55 -57
  4. package/cmd/ttsc/api_transform.go +54 -49
  5. package/cmd/ttsc/api_transform_branches_test.go +30 -32
  6. package/cmd/ttsc/build.go +111 -108
  7. package/cmd/ttsc/build_branches_test.go +95 -97
  8. package/cmd/ttsc/helpers_test.go +39 -28
  9. package/cmd/ttsc/main.go +111 -104
  10. package/cmd/ttsc/main_alias_branches_test.go +21 -23
  11. package/cmd/ttscserver/main.go +96 -90
  12. package/cmd/ttscserver/run_lsp_prefers_tsgo_flag_test.go +22 -22
  13. package/cmd/ttscserver/run_lsp_reports_server_error_test.go +22 -22
  14. package/cmd/ttscserver/run_lsp_uses_tsgo_environment_test.go +23 -23
  15. package/cmd/utility-host/main.go +30 -23
  16. package/driver/lsp.go +53 -7
  17. package/driver/plugins.go +88 -72
  18. package/driver/program.go +362 -339
  19. package/driver/rewrite.go +442 -380
  20. package/internal/lspserver/lsp_envelope.go +64 -64
  21. package/internal/lspserver/lsp_frame.go +60 -60
  22. package/internal/lspserver/lsp_plugin.go +57 -57
  23. package/internal/lspserver/lsp_proxy.go +265 -251
  24. package/internal/lspserver/lsp_server.go +155 -144
  25. package/lib/TtscCompiler.d.ts +7 -0
  26. package/lib/TtscCompiler.js +7 -0
  27. package/lib/TtscCompiler.js.map +1 -1
  28. package/lib/compiler/internal/buildNativeCompiler.d.ts +11 -1
  29. package/lib/compiler/internal/buildNativeCompiler.js +23 -3
  30. package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
  31. package/lib/compiler/internal/compileProjectInMemory.d.ts +9 -0
  32. package/lib/compiler/internal/compileProjectInMemory.js +36 -21
  33. package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
  34. package/lib/compiler/internal/paths.d.ts +27 -0
  35. package/lib/compiler/internal/paths.js +70 -0
  36. package/lib/compiler/internal/paths.js.map +1 -0
  37. package/lib/compiler/internal/project/readProjectConfig.d.ts +9 -1
  38. package/lib/compiler/internal/project/readProjectConfig.js +80 -1
  39. package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
  40. package/lib/compiler/internal/project/resolveProjectConfig.d.ts +13 -1
  41. package/lib/compiler/internal/project/resolveProjectConfig.js +24 -1
  42. package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
  43. package/lib/compiler/internal/resolveBinary.d.ts +9 -1
  44. package/lib/compiler/internal/resolveBinary.js +17 -1
  45. package/lib/compiler/internal/resolveBinary.js.map +1 -1
  46. package/lib/compiler/internal/resolveEmittedJavaScript.d.ts +16 -1
  47. package/lib/compiler/internal/resolveEmittedJavaScript.js +42 -10
  48. package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
  49. package/lib/compiler/internal/resolveTsgo.d.ts +21 -1
  50. package/lib/compiler/internal/resolveTsgo.js +23 -1
  51. package/lib/compiler/internal/resolveTsgo.js.map +1 -1
  52. package/lib/compiler/internal/runBuild.d.ts +19 -0
  53. package/lib/compiler/internal/runBuild.js +112 -46
  54. package/lib/compiler/internal/runBuild.js.map +1 -1
  55. package/lib/compiler/internal/runSingleFileEmit.d.ts +10 -1
  56. package/lib/compiler/internal/runSingleFileEmit.js +14 -1
  57. package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
  58. package/lib/compiler/internal/sharedHostHelpers.d.ts +5 -0
  59. package/lib/compiler/internal/sharedHostHelpers.js +6 -2
  60. package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
  61. package/lib/compiler/internal/spawnNative.d.ts +21 -0
  62. package/lib/compiler/internal/spawnNative.js +59 -0
  63. package/lib/compiler/internal/spawnNative.js.map +1 -0
  64. package/lib/compiler/internal/transformProjectInMemory.d.ts +15 -1
  65. package/lib/compiler/internal/transformProjectInMemory.js +57 -61
  66. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  67. package/lib/launcher/internal/getCompilerVersionText.js +7 -0
  68. package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
  69. package/lib/launcher/internal/prepareExecution.js +32 -0
  70. package/lib/launcher/internal/prepareExecution.js.map +1 -1
  71. package/lib/launcher/internal/resolveCacheDir.d.ts +9 -0
  72. package/lib/launcher/internal/resolveCacheDir.js +22 -0
  73. package/lib/launcher/internal/resolveCacheDir.js.map +1 -0
  74. package/lib/launcher/internal/resolveTtscserverBinary.js +2 -0
  75. package/lib/launcher/internal/resolveTtscserverBinary.js.map +1 -1
  76. package/lib/launcher/internal/runTtsc.d.ts +10 -0
  77. package/lib/launcher/internal/runTtsc.js +101 -16
  78. package/lib/launcher/internal/runTtsc.js.map +1 -1
  79. package/lib/launcher/internal/runTtscserver.js +10 -35
  80. package/lib/launcher/internal/runTtscserver.js.map +1 -1
  81. package/lib/launcher/internal/runTtsx.d.ts +8 -0
  82. package/lib/launcher/internal/runTtsx.js +33 -7
  83. package/lib/launcher/internal/runTtsx.js.map +1 -1
  84. package/lib/plugin/internal/buildSourcePlugin.d.ts +38 -0
  85. package/lib/plugin/internal/buildSourcePlugin.js +40 -17
  86. package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
  87. package/lib/plugin/internal/loadProjectPlugins.d.ts +26 -0
  88. package/lib/plugin/internal/loadProjectPlugins.js +32 -18
  89. package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
  90. package/lib/structures/ITtscCompilerTransformation.d.ts +15 -2
  91. package/lib/structures/internal/TtscSingleFileEmitOptions.d.ts +0 -2
  92. package/package.json +9 -9
  93. package/shim/ast/go.mod +9 -0
  94. package/shim/ast/go.sum +20 -0
  95. package/shim/ast/safe_text.go +86 -0
  96. package/shim/ast/shim.go +24 -0
  97. package/shim/ast/test/node_text_joins_multi_hop_qualified_name_test.go +32 -0
  98. package/shim/ast/test/node_text_joins_one_hop_qualified_name_test.go +31 -0
  99. package/shim/ast/test/node_text_returns_empty_for_nil_test.go +23 -0
  100. package/shim/ast/test/node_text_returns_identifier_text_test.go +27 -0
  101. package/shim/ast/test/node_text_skips_empty_qualified_left_test.go +30 -0
  102. package/shim/checker/shim.go +35 -0
  103. package/shim/core/shim.go +17 -0
  104. package/shim/diagnosticwriter/shim.go +4 -0
  105. package/shim/lsp/shim.go +3 -3
  106. package/shim/printer/shim.go +20 -0
  107. package/src/TtscCompiler.ts +7 -0
  108. package/src/compiler/internal/buildNativeCompiler.ts +23 -3
  109. package/src/compiler/internal/compileProjectInMemory.ts +34 -25
  110. package/src/compiler/internal/paths.ts +68 -0
  111. package/src/compiler/internal/project/readProjectConfig.ts +86 -1
  112. package/src/compiler/internal/project/resolveProjectConfig.ts +24 -1
  113. package/src/compiler/internal/resolveBinary.ts +17 -1
  114. package/src/compiler/internal/resolveEmittedJavaScript.ts +43 -12
  115. package/src/compiler/internal/resolveTsgo.ts +29 -1
  116. package/src/compiler/internal/runBuild.ts +110 -55
  117. package/src/compiler/internal/runSingleFileEmit.ts +14 -1
  118. package/src/compiler/internal/sharedHostHelpers.ts +7 -7
  119. package/src/compiler/internal/spawnNative.ts +64 -0
  120. package/src/compiler/internal/transformProjectInMemory.ts +47 -67
  121. package/src/launcher/internal/getCompilerVersionText.ts +7 -0
  122. package/src/launcher/internal/prepareExecution.ts +32 -0
  123. package/src/launcher/internal/resolveCacheDir.ts +19 -0
  124. package/src/launcher/internal/resolveTtscserverBinary.ts +2 -0
  125. package/src/launcher/internal/runTtsc.ts +120 -20
  126. package/src/launcher/internal/runTtscserver.ts +9 -1
  127. package/src/launcher/internal/runTtsx.ts +33 -8
  128. package/src/plugin/internal/buildSourcePlugin.ts +40 -14
  129. package/src/plugin/internal/loadProjectPlugins.ts +42 -18
  130. package/src/structures/ITtscCompilerTransformation.ts +15 -2
  131. package/src/structures/ITtscPlugin.ts +0 -1
  132. package/src/structures/internal/TtscSingleFileEmitOptions.ts +0 -2
  133. package/tools/gen_shims/main.go +43 -8
  134. package/utility/host.go +294 -245
@@ -1,11 +1,11 @@
1
1
  package main
2
2
 
3
3
  import (
4
- "bytes"
5
- "context"
6
- "testing"
4
+ "bytes"
5
+ "context"
6
+ "testing"
7
7
 
8
- "github.com/samchon/ttsc/packages/ttsc/internal/lspserver"
8
+ "github.com/samchon/ttsc/packages/ttsc/internal/lspserver"
9
9
  )
10
10
 
11
11
  // TestRunLSPUsesTsgoEnvironment verifies TTSC_TSGO_BINARY feeds the native host
@@ -19,26 +19,26 @@ import (
19
19
  // 2. Substitute runLSPServer and capture its options.
20
20
  // 3. Assert TsgoBinary is populated from the environment.
21
21
  func TestRunLSPUsesTsgoEnvironment(t *testing.T) {
22
- const expected = "/tmp/tsgo-env-binary"
23
- t.Setenv("TTSC_TSGO_BINARY", expected)
22
+ const expected = "/tmp/tsgo-env-binary"
23
+ t.Setenv("TTSC_TSGO_BINARY", expected)
24
24
 
25
- prev := runLSPServer
26
- var captured lspserver.LSPServerOptions
27
- runLSPServer = func(_ context.Context, opts lspserver.LSPServerOptions) error {
28
- captured = opts
29
- return nil
30
- }
31
- defer func() { runLSPServer = prev }()
25
+ prev := runLSPServer
26
+ var captured lspserver.LSPServerOptions
27
+ runLSPServer = func(_ context.Context, opts lspserver.LSPServerOptions) error {
28
+ captured = opts
29
+ return nil
30
+ }
31
+ defer func() { runLSPServer = prev }()
32
32
 
33
- outBuf := &bytes.Buffer{}
34
- errBuf := &bytes.Buffer{}
35
- withIO(t, outBuf, errBuf, nil, func() {
36
- if code := runLSP([]string{"--stdio", "--cwd", t.TempDir()}); code != 0 {
37
- t.Fatalf("expected exit 0, got %d (stderr=%q)", code, errBuf.String())
38
- }
39
- })
33
+ outBuf := &bytes.Buffer{}
34
+ errBuf := &bytes.Buffer{}
35
+ withIO(t, outBuf, errBuf, nil, func() {
36
+ if code := runLSP([]string{"--stdio", "--cwd", t.TempDir()}); code != 0 {
37
+ t.Fatalf("expected exit 0, got %d (stderr=%q)", code, errBuf.String())
38
+ }
39
+ })
40
40
 
41
- if captured.TsgoBinary != expected {
42
- t.Fatalf("expected TsgoBinary %q, got %q", expected, captured.TsgoBinary)
43
- }
41
+ if captured.TsgoBinary != expected {
42
+ t.Fatalf("expected TsgoBinary %q, got %q", expected, captured.TsgoBinary)
43
+ }
44
44
  }
@@ -1,39 +1,46 @@
1
+ // Command utility-host is the Go sidecar invoked by the JavaScript ttsc
2
+ // launcher to host linked transform plugins. It is not meant to be run by
3
+ // end users directly; the JavaScript launcher spawns it with a pre-resolved
4
+ // plugin manifest and a tsconfig path.
1
5
  package main
2
6
 
3
7
  import (
4
- "fmt"
5
- "os"
8
+ "fmt"
9
+ "os"
6
10
 
7
- "github.com/samchon/ttsc/packages/ttsc/utility"
11
+ "github.com/samchon/ttsc/packages/ttsc/utility"
8
12
  )
9
13
 
10
14
  func main() {
11
- os.Exit(run(os.Args[1:]))
15
+ os.Exit(run(os.Args[1:]))
12
16
  }
13
17
 
18
+ // run dispatches the first positional argument to the matching utility command.
19
+ // With no arguments it defaults to RunBuild so the launcher can omit the verb.
14
20
  func run(args []string) int {
15
- if len(args) == 0 {
16
- return utility.RunBuild(nil)
17
- }
18
- switch args[0] {
19
- case "build":
20
- return utility.RunBuild(args[1:])
21
- case "check":
22
- return utility.RunCheck(args[1:])
23
- case "transform":
24
- return utility.RunTransform(args[1:])
25
- case "-h", "--help", "help":
26
- printHelp()
27
- return 0
28
- default:
29
- fmt.Fprintf(os.Stderr, "ttsc utility: unknown command %q\n", args[0])
30
- fmt.Fprintln(os.Stderr, `ttsc utility: run "help" to see supported commands`)
31
- return 2
32
- }
21
+ if len(args) == 0 {
22
+ return utility.RunBuild(nil)
23
+ }
24
+ switch args[0] {
25
+ case "build":
26
+ return utility.RunBuild(args[1:])
27
+ case "check":
28
+ return utility.RunCheck(args[1:])
29
+ case "transform":
30
+ return utility.RunTransform(args[1:])
31
+ case "-h", "--help", "help":
32
+ printHelp()
33
+ return 0
34
+ default:
35
+ fmt.Fprintf(os.Stderr, "ttsc utility: unknown command %q\n", args[0])
36
+ fmt.Fprintln(os.Stderr, `ttsc utility: run "help" to see supported commands`)
37
+ return 2
38
+ }
33
39
  }
34
40
 
41
+ // printHelp writes the utility-host usage summary to stdout.
35
42
  func printHelp() {
36
- fmt.Fprintln(os.Stdout, `ttsc utility host
43
+ fmt.Fprintln(os.Stdout, `ttsc utility host
37
44
 
38
45
  Usage:
39
46
  utility-host build --tsconfig=tsconfig.json --plugins-json='[...]'
package/driver/lsp.go CHANGED
@@ -1,43 +1,85 @@
1
+ // lsp.go re-exports the public LSP surface from internal/lspserver so that
2
+ // downstream consumers (plugins, the VSCode extension, tests) import only the
3
+ // driver package rather than reaching into the internal package directly. All
4
+ // symbols are type aliases or var assignments so they are interchangeable with
5
+ // the originals at the call site.
1
6
  package driver
2
7
 
3
8
  import (
4
- "encoding/json"
9
+ "encoding/json"
5
10
 
6
- "github.com/samchon/ttsc/packages/ttsc/internal/lspserver"
11
+ "github.com/samchon/ttsc/packages/ttsc/internal/lspserver"
7
12
  )
8
13
 
14
+ // LSPPosition is the driver-level alias for lspserver.LSPPosition.
9
15
  type LSPPosition = lspserver.LSPPosition
16
+
17
+ // LSPRange is the driver-level alias for lspserver.LSPRange.
10
18
  type LSPRange = lspserver.LSPRange
19
+
20
+ // LSPDiagnosticSeverity is the driver-level alias for lspserver.LSPDiagnosticSeverity.
11
21
  type LSPDiagnosticSeverity = lspserver.LSPDiagnosticSeverity
12
22
 
23
+ // LSP diagnostic severity constants forwarded from lspserver.
13
24
  const (
14
- LSPDiagnosticSeverityError = lspserver.LSPDiagnosticSeverityError
15
- LSPDiagnosticSeverityWarning = lspserver.LSPDiagnosticSeverityWarning
16
- LSPDiagnosticSeverityInformation = lspserver.LSPDiagnosticSeverityInformation
17
- LSPDiagnosticSeverityHint = lspserver.LSPDiagnosticSeverityHint
25
+ LSPDiagnosticSeverityError = lspserver.LSPDiagnosticSeverityError
26
+ LSPDiagnosticSeverityWarning = lspserver.LSPDiagnosticSeverityWarning
27
+ LSPDiagnosticSeverityInformation = lspserver.LSPDiagnosticSeverityInformation
28
+ LSPDiagnosticSeverityHint = lspserver.LSPDiagnosticSeverityHint
18
29
  )
19
30
 
31
+ // LSPDiagnostic is the driver-level alias for lspserver.LSPDiagnostic.
20
32
  type LSPDiagnostic = lspserver.LSPDiagnostic
33
+
34
+ // LSPCodeAction is the driver-level alias for lspserver.LSPCodeAction.
21
35
  type LSPCodeAction = lspserver.LSPCodeAction
36
+
37
+ // LSPCommand is the driver-level alias for lspserver.LSPCommand.
22
38
  type LSPCommand = lspserver.LSPCommand
39
+
40
+ // LSPCodeActionContext is the driver-level alias for lspserver.LSPCodeActionContext.
23
41
  type LSPCodeActionContext = lspserver.LSPCodeActionContext
42
+
43
+ // LSPWorkspaceEdit is the driver-level alias for lspserver.LSPWorkspaceEdit.
24
44
  type LSPWorkspaceEdit = lspserver.LSPWorkspaceEdit
45
+
46
+ // LSPTextEdit is the driver-level alias for lspserver.LSPTextEdit.
25
47
  type LSPTextEdit = lspserver.LSPTextEdit
48
+
49
+ // LSPDocumentVersion is the driver-level alias for lspserver.LSPDocumentVersion.
26
50
  type LSPDocumentVersion = lspserver.LSPDocumentVersion
51
+
52
+ // PluginSource is the driver-level alias for lspserver.PluginSource.
53
+ // It is the public seam downstream pipelines implement to contribute
54
+ // diagnostics, code actions, and workspace commands to the LSP proxy.
27
55
  type PluginSource = lspserver.PluginSource
56
+
57
+ // NullPluginSource is the driver-level alias for lspserver.NullPluginSource.
28
58
  type NullPluginSource = lspserver.NullPluginSource
29
59
 
60
+ // ProxyOptions is the driver-level alias for lspserver.ProxyOptions.
30
61
  type ProxyOptions = lspserver.ProxyOptions
62
+
63
+ // Proxy is the driver-level alias for lspserver.Proxy.
31
64
  type Proxy = lspserver.Proxy
32
65
 
66
+ // FrameReader is the driver-level alias for lspserver.FrameReader.
33
67
  type FrameReader = lspserver.FrameReader
68
+
69
+ // Envelope is the driver-level alias for lspserver.Envelope.
34
70
  type Envelope = lspserver.Envelope
35
71
 
72
+ // LSPServerOptions is the driver-level alias for lspserver.LSPServerOptions.
36
73
  type LSPServerOptions = lspserver.LSPServerOptions
74
+
75
+ // LSPUpstreamRunner is the driver-level alias for lspserver.LSPUpstreamRunner.
37
76
  type LSPUpstreamRunner = lspserver.LSPUpstreamRunner
38
77
 
78
+ // MaxFrameBytes is the maximum byte length of a single JSON-RPC frame
79
+ // the proxy will read without returning ErrFrameTooLarge.
39
80
  const MaxFrameBytes = lspserver.MaxFrameBytes
40
81
 
82
+ // Sentinel errors forwarded from lspserver.
41
83
  var ErrCommandNotHandled = lspserver.ErrCommandNotHandled
42
84
  var ErrFrameClosed = lspserver.ErrFrameClosed
43
85
  var ErrFrameTooLarge = lspserver.ErrFrameTooLarge
@@ -46,6 +88,7 @@ var ErrLSPUpstreamPanic = lspserver.ErrLSPUpstreamPanic
46
88
  var ErrLSPCwdRequired = lspserver.ErrLSPCwdRequired
47
89
  var ErrLSPTsgoBinaryRequired = lspserver.ErrLSPTsgoBinaryRequired
48
90
 
91
+ // Constructor and utility functions forwarded from lspserver.
49
92
  var NewProxy = lspserver.NewProxy
50
93
  var NewFrameReader = lspserver.NewFrameReader
51
94
  var WriteFrame = lspserver.WriteFrame
@@ -55,6 +98,9 @@ var WithUpstreamRunnerForTest = lspserver.WithUpstreamRunnerForTest
55
98
  var RunLSPServer = lspserver.RunLSPServer
56
99
  var DenyNpmInstall = lspserver.DenyNpmInstall
57
100
 
101
+ // idKeyFromRaw normalizes a raw JSON-RPC id to a string map key. It is
102
+ // exposed here (unexported) so tests can access it via go:linkname without
103
+ // importing the internal package.
58
104
  func idKeyFromRaw(raw json.RawMessage) string {
59
- return lspserver.IDKeyFromRaw(raw)
105
+ return lspserver.IDKeyFromRaw(raw)
60
106
  }
package/driver/plugins.go CHANGED
@@ -1,44 +1,48 @@
1
1
  package driver
2
2
 
3
3
  import (
4
- "encoding/json"
5
- "fmt"
6
- "os"
7
- "strings"
4
+ "encoding/json"
5
+ "fmt"
6
+ "os"
7
+ "strings"
8
8
  )
9
9
 
10
+ // LinkedPluginsEnv is the environment variable ttsc sets to pass the JSON
11
+ // manifest of linked plugins to a natively-linked host binary. The value is
12
+ // a JSON array of PluginEntry objects; an empty or absent value means no
13
+ // linked plugins are active.
10
14
  const LinkedPluginsEnv = "TTSC_LINKED_PLUGINS_JSON"
11
15
 
12
16
  // PluginEntry is the manifest shape ttsc passes to driver-level plugins.
13
17
  type PluginEntry struct {
14
- Config map[string]any `json:"config"`
15
- Name string `json:"name"`
16
- Stage string `json:"stage"`
18
+ Config map[string]any `json:"config"`
19
+ Name string `json:"name"`
20
+ Stage string `json:"stage"`
17
21
  }
18
22
 
19
23
  // PluginContext is the per-entry context passed to registered linked plugins.
20
24
  type PluginContext struct {
21
- Cwd string
22
- Entry PluginEntry
23
- Tsconfig string
25
+ Cwd string
26
+ Entry PluginEntry
27
+ Tsconfig string
24
28
  }
25
29
 
26
30
  // SourcePreamblePlugin can inject source text before TypeScript-Go parses the
27
31
  // project. This is intentionally generic: the driver knows only the registered
28
32
  // plugin name and the project plugin manifest.
29
33
  type SourcePreamblePlugin interface {
30
- SourcePreamble(PluginContext) (string, error)
34
+ SourcePreamble(PluginContext) (string, error)
31
35
  }
32
36
 
33
37
  // ProgramPlugin can mutate a loaded Program before source output or emit.
34
38
  type ProgramPlugin interface {
35
- ApplyProgram(*Program, PluginContext) error
39
+ ApplyProgram(*Program, PluginContext) error
36
40
  }
37
41
 
38
42
  type linkedPluginState struct {
39
- cwd string
40
- entries []PluginEntry
41
- tsconfig string
43
+ cwd string
44
+ entries []PluginEntry
45
+ tsconfig string
42
46
  }
43
47
 
44
48
  var pluginRegistry []any
@@ -47,76 +51,88 @@ var pluginRegistry []any
47
51
  // packages call this from init(); ttsc pairs registrations with linked manifest
48
52
  // entries by build order, not by package name.
49
53
  func RegisterPlugin(plugin any) {
50
- if plugin == nil {
51
- panic("driver: RegisterPlugin called with nil plugin")
52
- }
53
- pluginRegistry = append(pluginRegistry, plugin)
54
+ if plugin == nil {
55
+ panic("driver: RegisterPlugin called with nil plugin")
56
+ }
57
+ pluginRegistry = append(pluginRegistry, plugin)
54
58
  }
55
59
 
60
+ // loadLinkedPluginState reads the linked-plugin manifest from the environment
61
+ // and returns the hydrated state. Returns a zero-entry state (not an error)
62
+ // when the environment variable is absent or empty.
56
63
  func loadLinkedPluginState(cwd, tsconfigPath string) (linkedPluginState, error) {
57
- input := strings.TrimSpace(os.Getenv(LinkedPluginsEnv))
58
- if input == "" {
59
- return linkedPluginState{cwd: cwd, tsconfig: tsconfigPath}, nil
60
- }
61
- var entries []PluginEntry
62
- if err := json.Unmarshal([]byte(input), &entries); err != nil {
63
- return linkedPluginState{}, fmt.Errorf("ttsc driver: invalid %s: %w", LinkedPluginsEnv, err)
64
- }
65
- return linkedPluginState{
66
- cwd: cwd,
67
- entries: entries,
68
- tsconfig: tsconfigPath,
69
- }, nil
64
+ input := strings.TrimSpace(os.Getenv(LinkedPluginsEnv))
65
+ if input == "" {
66
+ return linkedPluginState{cwd: cwd, tsconfig: tsconfigPath}, nil
67
+ }
68
+ var entries []PluginEntry
69
+ if err := json.Unmarshal([]byte(input), &entries); err != nil {
70
+ return linkedPluginState{}, fmt.Errorf("ttsc driver: invalid %s: %w", LinkedPluginsEnv, err)
71
+ }
72
+ return linkedPluginState{
73
+ cwd: cwd,
74
+ entries: entries,
75
+ tsconfig: tsconfigPath,
76
+ }, nil
70
77
  }
71
78
 
79
+ // sourcePreamble calls SourcePreamble on every SourcePreamblePlugin in
80
+ // registration order and concatenates the results. An entry that does not
81
+ // implement SourcePreamblePlugin is silently skipped.
72
82
  func (state linkedPluginState) sourcePreamble() (string, error) {
73
- var out strings.Builder
74
- for index, entry := range state.entries {
75
- plugin, ok := registeredPlugin(index)
76
- if !ok {
77
- return "", fmt.Errorf("ttsc driver: linked plugin entry %d was requested but no linked plugin registered at that position", index)
78
- }
79
- preamble, ok := plugin.(SourcePreamblePlugin)
80
- if !ok {
81
- continue
82
- }
83
- text, err := preamble.SourcePreamble(state.context(entry))
84
- if err != nil {
85
- return "", err
86
- }
87
- out.WriteString(text)
88
- }
89
- return out.String(), nil
83
+ var out strings.Builder
84
+ for index, entry := range state.entries {
85
+ plugin, ok := registeredPlugin(index)
86
+ if !ok {
87
+ return "", fmt.Errorf("ttsc driver: linked plugin entry %d was requested but no linked plugin registered at that position", index)
88
+ }
89
+ preamble, ok := plugin.(SourcePreamblePlugin)
90
+ if !ok {
91
+ continue
92
+ }
93
+ text, err := preamble.SourcePreamble(state.context(entry))
94
+ if err != nil {
95
+ return "", err
96
+ }
97
+ out.WriteString(text)
98
+ }
99
+ return out.String(), nil
90
100
  }
91
101
 
102
+ // apply calls ApplyProgram on every ProgramPlugin in registration order.
103
+ // An entry that does not implement ProgramPlugin is silently skipped.
92
104
  func (state linkedPluginState) apply(prog *Program) error {
93
- for index, entry := range state.entries {
94
- plugin, ok := registeredPlugin(index)
95
- if !ok {
96
- return fmt.Errorf("ttsc driver: linked plugin entry %d was requested but no linked plugin registered at that position", index)
97
- }
98
- transform, ok := plugin.(ProgramPlugin)
99
- if !ok {
100
- continue
101
- }
102
- if err := transform.ApplyProgram(prog, state.context(entry)); err != nil {
103
- return err
104
- }
105
- }
106
- return nil
105
+ for index, entry := range state.entries {
106
+ plugin, ok := registeredPlugin(index)
107
+ if !ok {
108
+ return fmt.Errorf("ttsc driver: linked plugin entry %d was requested but no linked plugin registered at that position", index)
109
+ }
110
+ transform, ok := plugin.(ProgramPlugin)
111
+ if !ok {
112
+ continue
113
+ }
114
+ if err := transform.ApplyProgram(prog, state.context(entry)); err != nil {
115
+ return err
116
+ }
117
+ }
118
+ return nil
107
119
  }
108
120
 
121
+ // registeredPlugin returns the plugin registered at position index, or
122
+ // (nil, false) when the index is out of range. Registration order matches
123
+ // the order of linked Go init() calls.
109
124
  func registeredPlugin(index int) (any, bool) {
110
- if index < 0 || index >= len(pluginRegistry) {
111
- return nil, false
112
- }
113
- return pluginRegistry[index], true
125
+ if index < 0 || index >= len(pluginRegistry) {
126
+ return nil, false
127
+ }
128
+ return pluginRegistry[index], true
114
129
  }
115
130
 
131
+ // context builds the PluginContext the driver passes to each plugin hook.
116
132
  func (state linkedPluginState) context(entry PluginEntry) PluginContext {
117
- return PluginContext{
118
- Cwd: state.cwd,
119
- Entry: entry,
120
- Tsconfig: state.tsconfig,
121
- }
133
+ return PluginContext{
134
+ Cwd: state.cwd,
135
+ Entry: entry,
136
+ Tsconfig: state.tsconfig,
137
+ }
122
138
  }