ttsc 0.11.0 → 0.12.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 (79) hide show
  1. package/README.md +25 -6
  2. package/cmd/ttsc/api_compile.go +101 -106
  3. package/cmd/ttsc/api_compile_branches_test.go +81 -0
  4. package/cmd/ttsc/api_transform.go +49 -54
  5. package/cmd/ttsc/api_transform_branches_test.go +41 -0
  6. package/cmd/ttsc/build.go +108 -109
  7. package/cmd/ttsc/build_branches_test.go +130 -0
  8. package/cmd/ttsc/helpers_test.go +47 -0
  9. package/cmd/ttsc/main.go +104 -103
  10. package/cmd/ttsc/main_alias_branches_test.go +29 -0
  11. package/cmd/ttscserver/main.go +104 -96
  12. package/cmd/ttscserver/run_lsp_prefers_tsgo_flag_test.go +42 -0
  13. package/cmd/ttscserver/run_lsp_reports_server_error_test.go +23 -25
  14. package/cmd/ttscserver/run_lsp_uses_tsgo_environment_test.go +44 -0
  15. package/cmd/utility-host/main.go +42 -0
  16. package/driver/lsp.go +60 -0
  17. package/driver/plugins.go +122 -0
  18. package/driver/program.go +346 -356
  19. package/driver/rewrite.go +336 -339
  20. package/{driver → internal/lspserver}/lsp_envelope.go +70 -64
  21. package/{driver → internal/lspserver}/lsp_frame.go +62 -62
  22. package/{driver → internal/lspserver}/lsp_plugin.go +58 -58
  23. package/internal/lspserver/lsp_proxy.go +391 -0
  24. package/internal/lspserver/lsp_server.go +231 -0
  25. package/lib/TtscCompiler.d.ts +3 -3
  26. package/lib/TtscCompiler.js +20 -6
  27. package/lib/TtscCompiler.js.map +1 -1
  28. package/lib/compiler/internal/resolveTsgo.d.ts +1 -0
  29. package/lib/compiler/internal/resolveTsgo.js +15 -4
  30. package/lib/compiler/internal/resolveTsgo.js.map +1 -1
  31. package/lib/compiler/internal/runBuild.js +12 -4
  32. package/lib/compiler/internal/runBuild.js.map +1 -1
  33. package/lib/compiler/internal/sharedHostHelpers.d.ts +12 -33
  34. package/lib/compiler/internal/sharedHostHelpers.js +27 -70
  35. package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
  36. package/lib/compiler/internal/transformProjectInMemory.js +12 -5
  37. package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
  38. package/lib/index.d.ts +0 -1
  39. package/lib/index.js +0 -1
  40. package/lib/index.js.map +1 -1
  41. package/lib/launcher/internal/runTtsc.js +9 -4
  42. package/lib/launcher/internal/runTtsc.js.map +1 -1
  43. package/lib/launcher/internal/runTtscserver.d.ts +1 -0
  44. package/lib/launcher/internal/runTtscserver.js +38 -1
  45. package/lib/launcher/internal/runTtscserver.js.map +1 -1
  46. package/lib/plugin/internal/buildSourcePlugin.d.ts +3 -0
  47. package/lib/plugin/internal/buildSourcePlugin.js +444 -41
  48. package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
  49. package/lib/plugin/internal/loadProjectPlugins.js +153 -34
  50. package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
  51. package/lib/structures/ITtscCompilerContext.d.ts +2 -1
  52. package/lib/structures/ITtscPlugin.d.ts +8 -13
  53. package/lib/structures/ITtscPluginFactoryContext.d.ts +4 -5
  54. package/lib/structures/ITtscProjectPluginConfig.d.ts +2 -2
  55. package/lib/structures/internal/ITtscLoadedNativePlugin.d.ts +4 -2
  56. package/package.json +8 -8
  57. package/shim/lsp/shim.go +11 -17
  58. package/src/TtscCompiler.ts +20 -7
  59. package/src/compiler/internal/resolveTsgo.ts +18 -5
  60. package/src/compiler/internal/runBuild.ts +17 -4
  61. package/src/compiler/internal/sharedHostHelpers.ts +34 -67
  62. package/src/compiler/internal/transformProjectInMemory.ts +18 -5
  63. package/src/index.ts +0 -1
  64. package/src/launcher/internal/runTtsc.ts +9 -4
  65. package/src/launcher/internal/runTtscserver.ts +43 -1
  66. package/src/plugin/internal/buildSourcePlugin.ts +475 -41
  67. package/src/plugin/internal/loadProjectPlugins.ts +199 -39
  68. package/src/structures/ITtscCompilerContext.ts +2 -1
  69. package/src/structures/ITtscPlugin.ts +9 -13
  70. package/src/structures/ITtscPluginFactoryContext.ts +4 -5
  71. package/src/structures/ITtscProjectPluginConfig.ts +2 -2
  72. package/src/structures/internal/ITtscLoadedNativePlugin.ts +4 -2
  73. package/utility/host.go +267 -1296
  74. package/driver/lsp_proxy.go +0 -394
  75. package/driver/lsp_server.go +0 -197
  76. package/lib/defineTtscPlugin.d.ts +0 -18
  77. package/lib/defineTtscPlugin.js +0 -7
  78. package/lib/defineTtscPlugin.js.map +0 -1
  79. package/src/defineTtscPlugin.ts +0 -26
@@ -0,0 +1,29 @@
1
+ package main
2
+
3
+ import "testing"
4
+
5
+ /**
6
+ * Verifies main command aliases cover flag-shaped demo and cts project inputs.
7
+ *
8
+ * The top-level dispatcher treats extension-shaped arguments as build aliases
9
+ * while demo owns its own flag parser. These small branches keep compatibility
10
+ * with `ttsc ./tsconfig.cts`-style invocations and malformed demo flags.
11
+ *
12
+ * 1. Run demo with a missing flag value.
13
+ * 2. Dispatch a `.cts` argument through the build alias path.
14
+ * 3. Assert both routes fail through their intended parser.
15
+ */
16
+ func TestMainAliasBranches(t *testing.T) {
17
+ code, _, _ := captureCommand(t, func() int {
18
+ return run([]string{"demo", "--type"})
19
+ })
20
+ if code != 2 {
21
+ t.Fatalf("demo flag status mismatch: %d", code)
22
+ }
23
+ code, _, _ = captureCommand(t, func() int {
24
+ return run([]string{"project.cts"})
25
+ })
26
+ if code != 2 {
27
+ t.Fatalf("cts build alias status mismatch: %d", code)
28
+ }
29
+ }
@@ -1,50 +1,50 @@
1
- // Command ttscserver is the Go LSP host shipped by ttsc. It embeds
2
- // TypeScript-Go's LSP server unmodified and proxies traffic between the
3
- // editor and that server, splicing ttsc-plugin diagnostics, code actions,
4
- // and ttsc-owned executeCommand handling into the same stream.
1
+ // Command ttscserver is the Go LSP host shipped by ttsc. It wraps the
2
+ // project-selected TypeScript-Go LSP server process and proxies traffic
3
+ // between the editor and that server, splicing ttsc-plugin diagnostics,
4
+ // code actions, and ttsc-owned executeCommand handling into the same stream.
5
5
  //
6
6
  // The JavaScript launcher (`packages/ttsc/src/launcher/ttscserver.ts`)
7
7
  // resolves the native binary and forwards stdio so editors can spawn
8
8
  // `ttscserver --stdio` without worrying about platform helper packages.
9
9
  //
10
10
  // Everything here is deliberately small: flag parsing, version metadata,
11
- // and a single delegation to driver.RunLSPServer.
11
+ // and a single delegation to lspserver.RunLSPServer.
12
12
  package main
13
13
 
14
14
  import (
15
- "context"
16
- "errors"
17
- "flag"
18
- "fmt"
19
- "io"
20
- "os"
21
- "os/signal"
22
- "runtime"
23
- "strings"
24
- "syscall"
25
- "time"
26
-
27
- "github.com/samchon/ttsc/packages/ttsc/driver"
15
+ "context"
16
+ "errors"
17
+ "flag"
18
+ "fmt"
19
+ "io"
20
+ "os"
21
+ "os/signal"
22
+ "runtime"
23
+ "strings"
24
+ "syscall"
25
+ "time"
26
+
27
+ "github.com/samchon/ttsc/packages/ttsc/internal/lspserver"
28
28
  )
29
29
 
30
30
  // Build metadata; overwritten via -ldflags in release builds.
31
31
  var (
32
- version = "0.0.0-dev"
33
- commit = "dev"
34
- date = "unknown"
32
+ version = "0.0.0-dev"
33
+ commit = "dev"
34
+ date = "unknown"
35
35
  )
36
36
 
37
37
  // Package-level writers so command tests can capture output without
38
38
  // patching os.Stdout / os.Stderr globally.
39
39
  var (
40
- stdout io.Writer = os.Stdout
41
- stderr io.Writer = os.Stderr
42
- stdin io.Reader = os.Stdin
40
+ stdout io.Writer = os.Stdout
41
+ stderr io.Writer = os.Stderr
42
+ stdin io.Reader = os.Stdin
43
43
  )
44
44
 
45
45
  // runLSPServer is the seam command tests use to substitute a fake LSP
46
- // host. Production wires it to driver.RunLSPServer.
47
- var runLSPServer = driver.RunLSPServer
46
+ // host. Production wires it to lspserver.RunLSPServer.
47
+ var runLSPServer = lspserver.RunLSPServer
48
48
 
49
49
  // notifyContext is the seam command tests use to substitute a
50
50
  // deterministic context (no signal hookup) for the signal-aware default.
@@ -56,82 +56,89 @@ var notifyContext = signal.NotifyContext
56
56
  var getwd = os.Getwd
57
57
 
58
58
  func main() {
59
- os.Exit(run(os.Args[1:]))
59
+ os.Exit(run(os.Args[1:]))
60
60
  }
61
61
 
62
62
  func run(args []string) int {
63
- if len(args) == 0 {
64
- printHelp(stdout)
65
- return 0
66
- }
67
- switch args[0] {
68
- case "-h", "--help", "help":
69
- printHelp(stdout)
70
- return 0
71
- case "-v", "--version", "version":
72
- printVersion(stdout)
73
- return 0
74
- }
75
- return runLSP(args)
63
+ if len(args) == 0 {
64
+ printHelp(stdout)
65
+ return 0
66
+ }
67
+ switch args[0] {
68
+ case "-h", "--help", "help":
69
+ printHelp(stdout)
70
+ return 0
71
+ case "-v", "--version", "version":
72
+ printVersion(stdout)
73
+ return 0
74
+ }
75
+ return runLSP(args)
76
76
  }
77
77
 
78
78
  func runLSP(args []string) int {
79
- fs := flag.NewFlagSet("ttscserver", flag.ContinueOnError)
80
- fs.SetOutput(stderr)
81
- stdioFlag := fs.Bool("stdio", false, "communicate with the editor over stdin/stdout")
82
- cwdFlag := fs.String("cwd", "", "project root (defaults to process cwd)")
83
- progressDelayFlag := fs.Duration("progress-delay", 250*time.Millisecond, "delay before showing tsgo's progress UI")
84
- if err := fs.Parse(args); err != nil {
85
- return 2
86
- }
87
- if !*stdioFlag {
88
- fmt.Fprintln(stderr, "ttscserver: only --stdio transport is supported")
89
- return 2
90
- }
91
-
92
- cwd := strings.TrimSpace(*cwdFlag)
93
- if cwd == "" {
94
- resolved, err := getwd()
95
- if err != nil {
96
- fmt.Fprintf(stderr, "ttscserver: could not resolve working directory: %v\n", err)
97
- return 2
98
- }
99
- cwd = resolved
100
- }
101
-
102
- ctx, stop := notifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
103
- defer stop()
104
-
105
- err := runLSPServer(ctx, driver.LSPServerOptions{
106
- In: stdin,
107
- Out: stdout,
108
- Err: stderr,
109
- Cwd: cwd,
110
- Source: driver.NullPluginSource{},
111
- ProgressDelay: *progressDelayFlag,
112
- })
113
- if err != nil && !errors.Is(err, context.Canceled) {
114
- fmt.Fprintf(stderr, "ttscserver: %v\n", err)
115
- return 1
116
- }
117
- return 0
79
+ fs := flag.NewFlagSet("ttscserver", flag.ContinueOnError)
80
+ fs.SetOutput(stderr)
81
+ stdioFlag := fs.Bool("stdio", false, "communicate with the editor over stdin/stdout")
82
+ cwdFlag := fs.String("cwd", "", "project root (defaults to process cwd)")
83
+ tsgoFlag := fs.String("tsgo", "", "absolute tsgo binary path (defaults to TTSC_TSGO_BINARY)")
84
+ progressDelayFlag := fs.Duration("progress-delay", 250*time.Millisecond, "delay before showing tsgo's progress UI")
85
+ if err := fs.Parse(args); err != nil {
86
+ return 2
87
+ }
88
+ if !*stdioFlag {
89
+ fmt.Fprintln(stderr, "ttscserver: only --stdio transport is supported")
90
+ return 2
91
+ }
92
+
93
+ cwd := strings.TrimSpace(*cwdFlag)
94
+ if cwd == "" {
95
+ resolved, err := getwd()
96
+ if err != nil {
97
+ fmt.Fprintf(stderr, "ttscserver: could not resolve working directory: %v\n", err)
98
+ return 2
99
+ }
100
+ cwd = resolved
101
+ }
102
+
103
+ ctx, stop := notifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
104
+ defer stop()
105
+
106
+ tsgoBinary := strings.TrimSpace(*tsgoFlag)
107
+ if tsgoBinary == "" {
108
+ tsgoBinary = strings.TrimSpace(os.Getenv("TTSC_TSGO_BINARY"))
109
+ }
110
+
111
+ err := runLSPServer(ctx, lspserver.LSPServerOptions{
112
+ In: stdin,
113
+ Out: stdout,
114
+ Err: stderr,
115
+ Cwd: cwd,
116
+ TsgoBinary: tsgoBinary,
117
+ Source: lspserver.NullPluginSource{},
118
+ ProgressDelay: *progressDelayFlag,
119
+ })
120
+ if err != nil && !errors.Is(err, context.Canceled) {
121
+ fmt.Fprintf(stderr, "ttscserver: %v\n", err)
122
+ return 1
123
+ }
124
+ return 0
118
125
  }
119
126
 
120
127
  func printVersion(w io.Writer) {
121
- fmt.Fprintf(
122
- w,
123
- "ttscserver %s (commit %s, built %s, %s/%s, go %s)\n",
124
- version,
125
- commit,
126
- date,
127
- runtime.GOOS,
128
- runtime.GOARCH,
129
- runtime.Version(),
130
- )
128
+ fmt.Fprintf(
129
+ w,
130
+ "ttscserver %s (commit %s, built %s, %s/%s, go %s)\n",
131
+ version,
132
+ commit,
133
+ date,
134
+ runtime.GOOS,
135
+ runtime.GOARCH,
136
+ runtime.Version(),
137
+ )
131
138
  }
132
139
 
133
140
  func printHelp(w io.Writer) {
134
- fmt.Fprintln(w, strings.TrimSpace(`
141
+ fmt.Fprintln(w, strings.TrimSpace(`
135
142
  ttscserver — Language Server Protocol host for ttsc.
136
143
 
137
144
  Usage:
@@ -141,14 +148,15 @@ Usage:
141
148
 
142
149
  Options:
143
150
  --stdio Communicate with the editor over stdin/stdout.
144
- --cwd <dir> Project root passed to the embedded tsgo server.
145
- --progress-delay D Delay before tsgo shows its progress UI (default 250ms).
151
+ --cwd <dir> Project root used as the tsgo server working directory.
152
+ --tsgo <path> Absolute tsgo binary path (defaults to TTSC_TSGO_BINARY).
153
+ --progress-delay D Accepted for compatibility; tsgo currently owns this value.
146
154
 
147
155
  Typical embedding:
148
156
  Editors spawn ttscserver via the JavaScript launcher (resolves the
149
- per-platform native binary) and exchange LSP messages over stdio. The
150
- embedded tsgo server provides hover/completion/definitions/diagnostics;
151
- ttsc merges plugin (lint/format) diagnostics, code actions, and ttsc-
152
- owned executeCommand handlers into the same stream.
157
+ per-platform native binary and passes the project tsgo path) and exchange
158
+ LSP messages over stdio. The upstream tsgo server provides hover,
159
+ completion, definitions, and diagnostics; ttsc merges plugin diagnostics,
160
+ code actions, and ttsc-owned executeCommand handlers into the same stream.
153
161
  `))
154
162
  }
@@ -0,0 +1,42 @@
1
+ package main
2
+
3
+ import (
4
+ "bytes"
5
+ "context"
6
+ "testing"
7
+
8
+ "github.com/samchon/ttsc/packages/ttsc/internal/lspserver"
9
+ )
10
+
11
+ // TestRunLSPPrefersTsgoFlag verifies the native command forwards an explicit
12
+ // upstream tsgo path into the LSP host options.
13
+ //
14
+ // Editors normally enter through the JavaScript launcher, but direct native
15
+ // callers can pass --tsgo. This pins that flag path separately from the
16
+ // TTSC_TSGO_BINARY environment fallback.
17
+ //
18
+ // 1. Substitute the runLSPServer seam and capture its options.
19
+ // 2. Run runLSP with --stdio, --cwd, and --tsgo.
20
+ // 3. Assert the captured TsgoBinary is the flag value.
21
+ func TestRunLSPPrefersTsgoFlag(t *testing.T) {
22
+ const expected = "/tmp/tsgo-test-binary"
23
+ prev := runLSPServer
24
+ var captured lspserver.LSPServerOptions
25
+ runLSPServer = func(_ context.Context, opts lspserver.LSPServerOptions) error {
26
+ captured = opts
27
+ return nil
28
+ }
29
+ defer func() { runLSPServer = prev }()
30
+
31
+ outBuf := &bytes.Buffer{}
32
+ errBuf := &bytes.Buffer{}
33
+ withIO(t, outBuf, errBuf, nil, func() {
34
+ if code := runLSP([]string{"--stdio", "--cwd", t.TempDir(), "--tsgo", expected}); code != 0 {
35
+ t.Fatalf("expected exit 0, got %d (stderr=%q)", code, errBuf.String())
36
+ }
37
+ })
38
+
39
+ if captured.TsgoBinary != expected {
40
+ t.Fatalf("expected TsgoBinary %q, got %q", expected, captured.TsgoBinary)
41
+ }
42
+ }
@@ -1,18 +1,17 @@
1
1
  package main
2
2
 
3
3
  import (
4
- "bytes"
5
- "context"
6
- "errors"
7
- "io"
8
- "strings"
9
- "testing"
4
+ "bytes"
5
+ "context"
6
+ "errors"
7
+ "strings"
8
+ "testing"
10
9
 
11
- "github.com/samchon/ttsc/packages/ttsc/driver"
10
+ "github.com/samchon/ttsc/packages/ttsc/internal/lspserver"
12
11
  )
13
12
 
14
13
  // TestRunLSPReportsServerError pins the non-cancellation error path in
15
- // runLSP. When RunLSPServer reports a real failure (the embedded server
14
+ // runLSP. When RunLSPServer reports a real failure (the upstream server
16
15
  // crashed, an unrecoverable IO error, etc.) the launcher must exit 1
17
16
  // and copy the error message to stderr.
18
17
  //
@@ -20,23 +19,22 @@ import (
20
19
  // 2. Run runLSP with --stdio --cwd <tempdir>.
21
20
  // 3. Assert exit 1 and the sentinel text on stderr.
22
21
  func TestRunLSPReportsServerError(t *testing.T) {
23
- sentinel := errors.New("lsp host blew up")
24
- prev := runLSPServer
25
- runLSPServer = func(_ context.Context, _ driver.LSPServerOptions) error {
26
- return sentinel
27
- }
28
- defer func() { runLSPServer = prev }()
22
+ sentinel := errors.New("lsp host blew up")
23
+ prev := runLSPServer
24
+ runLSPServer = func(_ context.Context, _ lspserver.LSPServerOptions) error {
25
+ return sentinel
26
+ }
27
+ defer func() { runLSPServer = prev }()
29
28
 
30
- outBuf := &bytes.Buffer{}
31
- errBuf := &bytes.Buffer{}
32
- withIO(t, outBuf, errBuf, nil, func() {
33
- if code := runLSP([]string{"--stdio", "--cwd", t.TempDir()}); code != 1 {
34
- t.Fatalf("expected exit 1, got %d", code)
35
- }
36
- })
29
+ outBuf := &bytes.Buffer{}
30
+ errBuf := &bytes.Buffer{}
31
+ withIO(t, outBuf, errBuf, nil, func() {
32
+ if code := runLSP([]string{"--stdio", "--cwd", t.TempDir()}); code != 1 {
33
+ t.Fatalf("expected exit 1, got %d", code)
34
+ }
35
+ })
37
36
 
38
- if !strings.Contains(errBuf.String(), sentinel.Error()) {
39
- t.Fatalf("expected sentinel on stderr, got: %q", errBuf.String())
40
- }
41
- _ = io.Discard
37
+ if !strings.Contains(errBuf.String(), sentinel.Error()) {
38
+ t.Fatalf("expected sentinel on stderr, got: %q", errBuf.String())
39
+ }
42
40
  }
@@ -0,0 +1,44 @@
1
+ package main
2
+
3
+ import (
4
+ "bytes"
5
+ "context"
6
+ "testing"
7
+
8
+ "github.com/samchon/ttsc/packages/ttsc/internal/lspserver"
9
+ )
10
+
11
+ // TestRunLSPUsesTsgoEnvironment verifies TTSC_TSGO_BINARY feeds the native host
12
+ // when no --tsgo flag is present.
13
+ //
14
+ // The JavaScript launcher resolves @typescript/native-preview and passes the
15
+ // absolute path through this environment variable, keeping the Go binary thin
16
+ // and independent of Node's module resolver.
17
+ //
18
+ // 1. Set TTSC_TSGO_BINARY to a sentinel path.
19
+ // 2. Substitute runLSPServer and capture its options.
20
+ // 3. Assert TsgoBinary is populated from the environment.
21
+ func TestRunLSPUsesTsgoEnvironment(t *testing.T) {
22
+ const expected = "/tmp/tsgo-env-binary"
23
+ t.Setenv("TTSC_TSGO_BINARY", expected)
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 }()
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
+ })
40
+
41
+ if captured.TsgoBinary != expected {
42
+ t.Fatalf("expected TsgoBinary %q, got %q", expected, captured.TsgoBinary)
43
+ }
44
+ }
@@ -0,0 +1,42 @@
1
+ package main
2
+
3
+ import (
4
+ "fmt"
5
+ "os"
6
+
7
+ "github.com/samchon/ttsc/packages/ttsc/utility"
8
+ )
9
+
10
+ func main() {
11
+ os.Exit(run(os.Args[1:]))
12
+ }
13
+
14
+ 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
+ }
33
+ }
34
+
35
+ func printHelp() {
36
+ fmt.Fprintln(os.Stdout, `ttsc utility host
37
+
38
+ Usage:
39
+ utility-host build --tsconfig=tsconfig.json --plugins-json='[...]'
40
+ utility-host check --tsconfig=tsconfig.json --plugins-json='[...]'
41
+ utility-host transform --tsconfig=tsconfig.json --plugins-json='[...]'`)
42
+ }
package/driver/lsp.go ADDED
@@ -0,0 +1,60 @@
1
+ package driver
2
+
3
+ import (
4
+ "encoding/json"
5
+
6
+ "github.com/samchon/ttsc/packages/ttsc/internal/lspserver"
7
+ )
8
+
9
+ type LSPPosition = lspserver.LSPPosition
10
+ type LSPRange = lspserver.LSPRange
11
+ type LSPDiagnosticSeverity = lspserver.LSPDiagnosticSeverity
12
+
13
+ const (
14
+ LSPDiagnosticSeverityError = lspserver.LSPDiagnosticSeverityError
15
+ LSPDiagnosticSeverityWarning = lspserver.LSPDiagnosticSeverityWarning
16
+ LSPDiagnosticSeverityInformation = lspserver.LSPDiagnosticSeverityInformation
17
+ LSPDiagnosticSeverityHint = lspserver.LSPDiagnosticSeverityHint
18
+ )
19
+
20
+ type LSPDiagnostic = lspserver.LSPDiagnostic
21
+ type LSPCodeAction = lspserver.LSPCodeAction
22
+ type LSPCommand = lspserver.LSPCommand
23
+ type LSPCodeActionContext = lspserver.LSPCodeActionContext
24
+ type LSPWorkspaceEdit = lspserver.LSPWorkspaceEdit
25
+ type LSPTextEdit = lspserver.LSPTextEdit
26
+ type LSPDocumentVersion = lspserver.LSPDocumentVersion
27
+ type PluginSource = lspserver.PluginSource
28
+ type NullPluginSource = lspserver.NullPluginSource
29
+
30
+ type ProxyOptions = lspserver.ProxyOptions
31
+ type Proxy = lspserver.Proxy
32
+
33
+ type FrameReader = lspserver.FrameReader
34
+ type Envelope = lspserver.Envelope
35
+
36
+ type LSPServerOptions = lspserver.LSPServerOptions
37
+ type LSPUpstreamRunner = lspserver.LSPUpstreamRunner
38
+
39
+ const MaxFrameBytes = lspserver.MaxFrameBytes
40
+
41
+ var ErrCommandNotHandled = lspserver.ErrCommandNotHandled
42
+ var ErrFrameClosed = lspserver.ErrFrameClosed
43
+ var ErrFrameTooLarge = lspserver.ErrFrameTooLarge
44
+ var ErrInvalidJSONRPC = lspserver.ErrInvalidJSONRPC
45
+ var ErrLSPUpstreamPanic = lspserver.ErrLSPUpstreamPanic
46
+ var ErrLSPCwdRequired = lspserver.ErrLSPCwdRequired
47
+ var ErrLSPTsgoBinaryRequired = lspserver.ErrLSPTsgoBinaryRequired
48
+
49
+ var NewProxy = lspserver.NewProxy
50
+ var NewFrameReader = lspserver.NewFrameReader
51
+ var WriteFrame = lspserver.WriteFrame
52
+ var ParseEnvelope = lspserver.ParseEnvelope
53
+ var RecoverPanicAs = lspserver.RecoverPanicAs
54
+ var WithUpstreamRunnerForTest = lspserver.WithUpstreamRunnerForTest
55
+ var RunLSPServer = lspserver.RunLSPServer
56
+ var DenyNpmInstall = lspserver.DenyNpmInstall
57
+
58
+ func idKeyFromRaw(raw json.RawMessage) string {
59
+ return lspserver.IDKeyFromRaw(raw)
60
+ }
@@ -0,0 +1,122 @@
1
+ package driver
2
+
3
+ import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "os"
7
+ "strings"
8
+ )
9
+
10
+ const LinkedPluginsEnv = "TTSC_LINKED_PLUGINS_JSON"
11
+
12
+ // PluginEntry is the manifest shape ttsc passes to driver-level plugins.
13
+ type PluginEntry struct {
14
+ Config map[string]any `json:"config"`
15
+ Name string `json:"name"`
16
+ Stage string `json:"stage"`
17
+ }
18
+
19
+ // PluginContext is the per-entry context passed to registered linked plugins.
20
+ type PluginContext struct {
21
+ Cwd string
22
+ Entry PluginEntry
23
+ Tsconfig string
24
+ }
25
+
26
+ // SourcePreamblePlugin can inject source text before TypeScript-Go parses the
27
+ // project. This is intentionally generic: the driver knows only the registered
28
+ // plugin name and the project plugin manifest.
29
+ type SourcePreamblePlugin interface {
30
+ SourcePreamble(PluginContext) (string, error)
31
+ }
32
+
33
+ // ProgramPlugin can mutate a loaded Program before source output or emit.
34
+ type ProgramPlugin interface {
35
+ ApplyProgram(*Program, PluginContext) error
36
+ }
37
+
38
+ type linkedPluginState struct {
39
+ cwd string
40
+ entries []PluginEntry
41
+ tsconfig string
42
+ }
43
+
44
+ var pluginRegistry []any
45
+
46
+ // RegisterPlugin registers a driver-level plugin implementation. Linked Go
47
+ // packages call this from init(); ttsc pairs registrations with linked manifest
48
+ // entries by build order, not by package name.
49
+ func RegisterPlugin(plugin any) {
50
+ if plugin == nil {
51
+ panic("driver: RegisterPlugin called with nil plugin")
52
+ }
53
+ pluginRegistry = append(pluginRegistry, plugin)
54
+ }
55
+
56
+ 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
70
+ }
71
+
72
+ 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
90
+ }
91
+
92
+ 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
107
+ }
108
+
109
+ func registeredPlugin(index int) (any, bool) {
110
+ if index < 0 || index >= len(pluginRegistry) {
111
+ return nil, false
112
+ }
113
+ return pluginRegistry[index], true
114
+ }
115
+
116
+ func (state linkedPluginState) context(entry PluginEntry) PluginContext {
117
+ return PluginContext{
118
+ Cwd: state.cwd,
119
+ Entry: entry,
120
+ Tsconfig: state.tsconfig,
121
+ }
122
+ }