@ttsc/wasm 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 (39) hide show
  1. package/cmd/ttsc-wasm/main.go +101 -89
  2. package/cmd/ttsc-wasm/main_wasm.go +6 -4
  3. package/dist/ttsc.wasm +0 -0
  4. package/host/api.go +154 -145
  5. package/host/doc.go +12 -12
  6. package/host/host.go +236 -225
  7. package/host/host_native.go +5 -2
  8. package/host/plugin.go +31 -31
  9. package/lib/src/MemFS.d.ts +35 -0
  10. package/lib/src/MemFS.js +57 -4
  11. package/lib/src/MemFS.js.map +1 -1
  12. package/lib/src/api.d.ts +46 -13
  13. package/lib/src/api.js +7 -4
  14. package/lib/src/api.js.map +1 -1
  15. package/lib/src/index.d.ts +1 -1
  16. package/lib/src/index.js +10 -3
  17. package/lib/src/index.js.map +1 -1
  18. package/lib/src/instantiate.d.ts +10 -7
  19. package/lib/src/instantiate.js +20 -3
  20. package/lib/src/instantiate.js.map +1 -1
  21. package/package.json +2 -2
  22. package/shim-vendor/shim/ast/go.mod +9 -0
  23. package/shim-vendor/shim/ast/go.sum +20 -0
  24. package/shim-vendor/shim/ast/safe_text.go +86 -0
  25. package/shim-vendor/shim/ast/shim.go +24 -0
  26. package/shim-vendor/shim/ast/test/node_text_joins_multi_hop_qualified_name_test.go +32 -0
  27. package/shim-vendor/shim/ast/test/node_text_joins_one_hop_qualified_name_test.go +31 -0
  28. package/shim-vendor/shim/ast/test/node_text_returns_empty_for_nil_test.go +23 -0
  29. package/shim-vendor/shim/ast/test/node_text_returns_identifier_text_test.go +27 -0
  30. package/shim-vendor/shim/ast/test/node_text_skips_empty_qualified_left_test.go +30 -0
  31. package/shim-vendor/shim/checker/shim.go +35 -0
  32. package/shim-vendor/shim/core/shim.go +17 -0
  33. package/shim-vendor/shim/diagnosticwriter/shim.go +4 -0
  34. package/shim-vendor/shim/lsp/shim.go +3 -3
  35. package/shim-vendor/shim/printer/shim.go +20 -0
  36. package/src/MemFS.ts +92 -7
  37. package/src/api.ts +46 -13
  38. package/src/index.ts +1 -5
  39. package/src/instantiate.ts +37 -14
package/host/host.go CHANGED
@@ -10,20 +10,20 @@
10
10
  package host
11
11
 
12
12
  import (
13
- "fmt"
14
- "os"
15
- "runtime"
16
- "sync/atomic"
17
- "syscall/js"
18
- "time"
13
+ "fmt"
14
+ "os"
15
+ "runtime"
16
+ "sync/atomic"
17
+ "syscall/js"
18
+ "time"
19
19
  )
20
20
 
21
21
  // Build metadata. Override at link time via
22
22
  // `-ldflags "-X github.com/samchon/ttsc/packages/wasm/host.version=..."`.
23
23
  var (
24
- version = "0.0.0-dev"
25
- commit = "dev"
26
- date = "unknown"
24
+ version = "0.0.0-dev"
25
+ commit = "dev"
26
+ date = "unknown"
27
27
  )
28
28
 
29
29
  // API stability: experimental until v1.0; signatures may change between
@@ -44,153 +44,153 @@ var (
44
44
  // A matching readiness resolver is invoked: `globalThis[`${apiName}Ready`]`.
45
45
  // JS callers register this BEFORE go.run begins so they can await wasm boot.
46
46
  func Expose(apiName string, cfg Config) {
47
- plugins := map[string]Plugin{}
48
- pluginNames := make([]string, 0, len(cfg.Plugins))
49
- for _, p := range cfg.Plugins {
50
- if p == nil {
51
- continue
52
- }
53
- name := p.Name()
54
- if name == "" {
55
- continue
56
- }
57
- if _, dup := plugins[name]; dup {
58
- panic(fmt.Sprintf("host.Expose: duplicate plugin name %q", name))
59
- }
60
- plugins[name] = p
61
- pluginNames = append(pluginNames, name)
62
- }
47
+ plugins := map[string]Plugin{}
48
+ pluginNames := make([]string, 0, len(cfg.Plugins))
49
+ for _, p := range cfg.Plugins {
50
+ if p == nil {
51
+ continue
52
+ }
53
+ name := p.Name()
54
+ if name == "" {
55
+ continue
56
+ }
57
+ if _, dup := plugins[name]; dup {
58
+ panic(fmt.Sprintf("host.Expose: duplicate plugin name %q", name))
59
+ }
60
+ plugins[name] = p
61
+ pluginNames = append(pluginNames, name)
62
+ }
63
63
 
64
- api := map[string]any{
65
- "version": js.FuncOf(jsVersion),
66
- "build": js.FuncOf(jsBuild),
67
- "check": js.FuncOf(jsCheck),
68
- "transform": js.FuncOf(jsTransform),
69
- "plugin": js.FuncOf(jsPluginDispatch(plugins)),
70
- "plugins": js.FuncOf(jsPluginsList(pluginNames)),
71
- }
72
- js.Global().Set(apiName, js.ValueOf(api))
64
+ api := map[string]any{
65
+ "version": js.FuncOf(jsVersion),
66
+ "build": js.FuncOf(jsBuild),
67
+ "check": js.FuncOf(jsCheck),
68
+ "transform": js.FuncOf(jsTransform),
69
+ "plugin": js.FuncOf(jsPluginDispatch(plugins)),
70
+ "plugins": js.FuncOf(jsPluginsList(pluginNames)),
71
+ }
72
+ js.Global().Set(apiName, js.ValueOf(api))
73
73
 
74
- if ready := js.Global().Get(apiName + "Ready"); ready.Type() == js.TypeFunction {
75
- ready.Invoke()
76
- }
74
+ if ready := js.Global().Get(apiName + "Ready"); ready.Type() == js.TypeFunction {
75
+ ready.Invoke()
76
+ }
77
77
 
78
- // A perpetual idle goroutine keeps the wasm runtime alive without
79
- // tripping Go's deadlock detector while syscall.fsCall callbacks are in
80
- // flight. `select {}` alone is wrongly classified as "all goroutines
81
- // asleep" the moment the FS path hands a request to JS.
82
- go func() {
83
- for {
84
- time.Sleep(time.Hour)
85
- }
86
- }()
87
- select {}
78
+ // A perpetual idle goroutine keeps the wasm runtime alive without
79
+ // tripping Go's deadlock detector while syscall.fsCall callbacks are in
80
+ // flight. `select {}` alone is wrongly classified as "all goroutines
81
+ // asleep" the moment the FS path hands a request to JS.
82
+ go func() {
83
+ for {
84
+ time.Sleep(time.Hour)
85
+ }
86
+ }()
87
+ select {}
88
88
  }
89
89
 
90
90
  // jsVersion returns the build metadata for this wasm binary.
91
91
  func jsVersion(this js.Value, args []js.Value) any {
92
- return js.ValueOf(map[string]any{
93
- "version": version,
94
- "commit": commit,
95
- "date": date,
96
- "go": runtime.Version(),
97
- "goos": runtime.GOOS,
98
- "goarch": runtime.GOARCH,
99
- })
92
+ return js.ValueOf(map[string]any{
93
+ "version": version,
94
+ "commit": commit,
95
+ "date": date,
96
+ "go": runtime.Version(),
97
+ "goos": runtime.GOOS,
98
+ "goarch": runtime.GOARCH,
99
+ })
100
100
  }
101
101
 
102
102
  // jsBuild → Promise<{ code, stdout, stderr, result }>. `result` carries the
103
103
  // compile JSON; the surrounding shape mirrors plugin dispatch so JS callers
104
104
  // can write one error-handling branch.
105
105
  func jsBuild(this js.Value, args []js.Value) any {
106
- opts := optionsArg(args)
107
- return makePromise(func() any {
108
- return runProjectCommand(opts, Build)
109
- })
106
+ opts := optionsArg(args)
107
+ return makePromise(func() any {
108
+ return runProjectCommand(opts, Build)
109
+ })
110
110
  }
111
111
 
112
112
  // jsCheck → Promise<{ code, stdout, stderr, result }>.
113
113
  func jsCheck(this js.Value, args []js.Value) any {
114
- opts := optionsArg(args)
115
- return makePromise(func() any {
116
- return runProjectCommand(opts, Check)
117
- })
114
+ opts := optionsArg(args)
115
+ return makePromise(func() any {
116
+ return runProjectCommand(opts, Check)
117
+ })
118
118
  }
119
119
 
120
120
  // jsTransform → Promise<{ code, stdout, stderr, result }>.
121
121
  func jsTransform(this js.Value, args []js.Value) any {
122
- opts := optionsArg(args)
123
- return makePromise(func() any {
124
- return runProjectCommand(opts, Transform)
125
- })
122
+ opts := optionsArg(args)
123
+ return makePromise(func() any {
124
+ return runProjectCommand(opts, Transform)
125
+ })
126
126
  }
127
127
 
128
128
  // jsPluginDispatch routes `api.plugin({ name, command, ...opts })` to the
129
129
  // matching registered Plugin's Run. The returned shape is identical to the
130
130
  // base endpoints so a JS caller has one result type to handle.
131
131
  func jsPluginDispatch(plugins map[string]Plugin) func(this js.Value, args []js.Value) any {
132
- return func(this js.Value, args []js.Value) any {
133
- opts := optionsArg(args)
134
- name := stringProp(opts, "name")
135
- command := stringProp(opts, "command")
136
- argv := buildPluginArgv(opts)
137
- plugin, ok := plugins[name]
138
- if !ok {
139
- return errorPromise(2, fmt.Sprintf("host: unknown plugin %q", name))
140
- }
141
- return makePromise(func() any {
142
- res := runWithCapturedIO(func() int {
143
- return plugin.Run(command, argv)
144
- })
145
- return js.ValueOf(map[string]any{
146
- "code": res.Code,
147
- "stdout": res.Stdout,
148
- "stderr": res.Stderr,
149
- "result": "",
150
- })
151
- })
152
- }
132
+ return func(this js.Value, args []js.Value) any {
133
+ opts := optionsArg(args)
134
+ name := stringProp(opts, "name")
135
+ command := stringProp(opts, "command")
136
+ argv := buildPluginArgv(opts)
137
+ plugin, ok := plugins[name]
138
+ if !ok {
139
+ return errorPromise(2, fmt.Sprintf("host: unknown plugin %q", name))
140
+ }
141
+ return makePromise(func() any {
142
+ res := runWithCapturedIO(func() int {
143
+ return plugin.Run(command, argv)
144
+ })
145
+ return js.ValueOf(map[string]any{
146
+ "code": res.Code,
147
+ "stdout": res.Stdout,
148
+ "stderr": res.Stderr,
149
+ "result": "",
150
+ })
151
+ })
152
+ }
153
153
  }
154
154
 
155
155
  // jsPluginsList returns the registered plugin names so JS callers can probe
156
156
  // the wasm's contents at boot time.
157
157
  func jsPluginsList(names []string) func(this js.Value, args []js.Value) any {
158
- return func(this js.Value, args []js.Value) any {
159
- out := make([]any, len(names))
160
- for i, n := range names {
161
- out[i] = n
162
- }
163
- return js.ValueOf(out)
164
- }
158
+ return func(this js.Value, args []js.Value) any {
159
+ out := make([]any, len(names))
160
+ for i, n := range names {
161
+ out[i] = n
162
+ }
163
+ return js.ValueOf(out)
164
+ }
165
165
  }
166
166
 
167
167
  // runProjectCommand wraps the base build/check/transform endpoints into the
168
168
  // uniform `{ code, stdout, stderr, result }` shape. The result string is the
169
169
  // JSON the endpoint produced; JSON.parse it on the JS side.
170
170
  func runProjectCommand(opts js.Value, fn func(cwd, tsconfig string) ([]byte, int, error)) any {
171
- cwd := stringProp(opts, "cwd")
172
- tsconfig := stringProp(opts, "tsconfig")
173
- if tsconfig == "" {
174
- tsconfig = "tsconfig.json"
175
- }
176
- if cwd == "" {
177
- return errorResponse(2, "host: \"cwd\" is required")
178
- }
179
- data, code, err := fn(cwd, tsconfig)
180
- if err != nil {
181
- return js.ValueOf(map[string]any{
182
- "code": code,
183
- "stdout": "",
184
- "stderr": err.Error() + "\n",
185
- "result": "",
186
- })
187
- }
188
- return js.ValueOf(map[string]any{
189
- "code": code,
190
- "stdout": "",
191
- "stderr": "",
192
- "result": string(data),
193
- })
171
+ cwd := stringProp(opts, "cwd")
172
+ tsconfig := stringProp(opts, "tsconfig")
173
+ if tsconfig == "" {
174
+ tsconfig = "tsconfig.json"
175
+ }
176
+ if cwd == "" {
177
+ return errorResponse(2, "host: \"cwd\" is required")
178
+ }
179
+ data, code, err := fn(cwd, tsconfig)
180
+ if err != nil {
181
+ return js.ValueOf(map[string]any{
182
+ "code": code,
183
+ "stdout": "",
184
+ "stderr": err.Error() + "\n",
185
+ "result": "",
186
+ })
187
+ }
188
+ return js.ValueOf(map[string]any{
189
+ "code": code,
190
+ "stdout": "",
191
+ "stderr": "",
192
+ "result": string(data),
193
+ })
194
194
  }
195
195
 
196
196
  // buildPluginArgv translates the JS options object into a CLI-shaped argv. We
@@ -198,31 +198,31 @@ func runProjectCommand(opts js.Value, fn func(cwd, tsconfig string) ([]byte, int
198
198
  // `name` / `command` slots which the host consumes itself. This is the same
199
199
  // translation typia's wasm does for `--cwd / --tsconfig / --output / ...`.
200
200
  func buildPluginArgv(opts js.Value) []string {
201
- out := []string{}
202
- if opts.Type() != js.TypeObject {
203
- return out
204
- }
205
- keys := js.Global().Get("Object").Call("keys", opts)
206
- n := keys.Length()
207
- for i := 0; i < n; i++ {
208
- key := keys.Index(i).String()
209
- switch key {
210
- case "name", "command":
211
- continue
212
- }
213
- value := opts.Get(key)
214
- switch value.Type() {
215
- case js.TypeString:
216
- out = append(out, "--"+key+"="+value.String())
217
- case js.TypeBoolean:
218
- if value.Bool() {
219
- out = append(out, "--"+key)
220
- }
221
- case js.TypeNumber:
222
- out = append(out, fmt.Sprintf("--%s=%v", key, value.Float()))
223
- }
224
- }
225
- return out
201
+ out := []string{}
202
+ if opts.Type() != js.TypeObject {
203
+ return out
204
+ }
205
+ keys := js.Global().Get("Object").Call("keys", opts)
206
+ n := keys.Length()
207
+ for i := 0; i < n; i++ {
208
+ key := keys.Index(i).String()
209
+ switch key {
210
+ case "name", "command":
211
+ continue
212
+ }
213
+ value := opts.Get(key)
214
+ switch value.Type() {
215
+ case js.TypeString:
216
+ out = append(out, "--"+key+"="+value.String())
217
+ case js.TypeBoolean:
218
+ if value.Bool() {
219
+ out = append(out, "--"+key)
220
+ }
221
+ case js.TypeNumber:
222
+ out = append(out, fmt.Sprintf("--%s=%v", key, value.Float()))
223
+ }
224
+ }
225
+ return out
226
226
  }
227
227
 
228
228
  // makePromise wraps a Go computation as a JS Promise. The executor
@@ -230,51 +230,58 @@ func buildPluginArgv(opts js.Value) []string {
230
230
  // in a goroutine so the JS event loop can drive `fs.stat` and friends to
231
231
  // completion before we ask Go to receive the callback.
232
232
  func makePromise(work func() any) js.Value {
233
- var executor js.Func
234
- executor = js.FuncOf(func(this js.Value, args []js.Value) any {
235
- resolve := args[0]
236
- reject := args[1]
237
- go func() {
238
- defer func() {
239
- if r := recover(); r != nil {
240
- err := js.Global().Get("Error").New(fmt.Sprintf("%v", r))
241
- reject.Invoke(err)
242
- }
243
- executor.Release()
244
- }()
245
- resolve.Invoke(work())
246
- }()
247
- return nil
248
- })
249
- return js.Global().Get("Promise").New(executor)
233
+ var executor js.Func
234
+ executor = js.FuncOf(func(this js.Value, args []js.Value) any {
235
+ resolve := args[0]
236
+ reject := args[1]
237
+ go func() {
238
+ defer func() {
239
+ if r := recover(); r != nil {
240
+ err := js.Global().Get("Error").New(fmt.Sprintf("%v", r))
241
+ reject.Invoke(err)
242
+ }
243
+ executor.Release()
244
+ }()
245
+ resolve.Invoke(work())
246
+ }()
247
+ return nil
248
+ })
249
+ return js.Global().Get("Promise").New(executor)
250
250
  }
251
251
 
252
+ // errorPromise returns a pre-resolved Promise carrying an error response.
252
253
  func errorPromise(code int, message string) js.Value {
253
- return makePromise(func() any { return errorResponse(code, message) })
254
+ return makePromise(func() any { return errorResponse(code, message) })
254
255
  }
255
256
 
257
+ // errorResponse builds the uniform JS result object for error cases.
256
258
  func errorResponse(code int, message string) any {
257
- return js.ValueOf(map[string]any{
258
- "code": code,
259
- "stdout": "",
260
- "stderr": message + "\n",
261
- "result": "",
262
- })
259
+ return js.ValueOf(map[string]any{
260
+ "code": code,
261
+ "stdout": "",
262
+ "stderr": message + "\n",
263
+ "result": "",
264
+ })
263
265
  }
264
266
 
267
+ // optionsArg returns the first JS argument if it is an object, otherwise an
268
+ // empty JS object. Guards all endpoint handlers against missing or wrong-typed
269
+ // argument lists.
265
270
  func optionsArg(args []js.Value) js.Value {
266
- if len(args) == 0 || args[0].Type() != js.TypeObject {
267
- return js.ValueOf(map[string]any{})
268
- }
269
- return args[0]
271
+ if len(args) == 0 || args[0].Type() != js.TypeObject {
272
+ return js.ValueOf(map[string]any{})
273
+ }
274
+ return args[0]
270
275
  }
271
276
 
277
+ // stringProp reads a string property from a JS object. Returns "" if the
278
+ // property is absent or not a string.
272
279
  func stringProp(obj js.Value, key string) string {
273
- v := obj.Get(key)
274
- if v.Type() != js.TypeString {
275
- return ""
276
- }
277
- return v.String()
280
+ v := obj.Get(key)
281
+ if v.Type() != js.TypeString {
282
+ return ""
283
+ }
284
+ return v.String()
278
285
  }
279
286
 
280
287
  // runWithCapturedIO redirects os.Stdout / os.Stderr to temp MemFS files for
@@ -287,63 +294,67 @@ func stringProp(obj js.Value, key string) string {
287
294
  // supported on the wasm target. The temp-file approach works because the
288
295
  // MemFS shim implements file open/write/read.
289
296
  func runWithCapturedIO(task func() int) APIResult {
290
- prevOut, prevErr := os.Stdout, os.Stderr
291
- defer func() {
292
- os.Stdout = prevOut
293
- os.Stderr = prevErr
294
- }()
297
+ prevOut, prevErr := os.Stdout, os.Stderr
298
+ // The defer is a safety net: if an early return skips the explicit restore
299
+ // below, os.Stdout/os.Stderr are still restored. The explicit restore that
300
+ // follows the task call is a no-op for the defer but makes the happy path
301
+ // readable without tracing the defer.
302
+ defer func() {
303
+ os.Stdout = prevOut
304
+ os.Stderr = prevErr
305
+ }()
295
306
 
296
- stdoutPath := fmt.Sprintf("/tmp/ttsc-host-capture-%d-%d.stdout", os.Getpid(), captureCounter.Add(1))
297
- stderrPath := fmt.Sprintf("/tmp/ttsc-host-capture-%d-%d.stderr", os.Getpid(), captureCounter.Add(1))
307
+ stdoutPath := fmt.Sprintf("/tmp/ttsc-host-capture-%d-%d.stdout", os.Getpid(), captureCounter.Add(1))
308
+ stderrPath := fmt.Sprintf("/tmp/ttsc-host-capture-%d-%d.stderr", os.Getpid(), captureCounter.Add(1))
298
309
 
299
- outFile, outErr := os.Create(stdoutPath)
300
- errFile, errErr := os.Create(stderrPath)
301
- if outErr != nil || errErr != nil {
302
- // Fall back to the original streams. Better to lose capture than the
303
- // call. The MemFS writeSync shim surfaces the failure to the host
304
- // console so the regression is visible.
305
- if outErr != nil {
306
- fmt.Fprintf(prevErr, "host.runWithCapturedIO: stdout temp file failed: %v\n", outErr)
307
- }
308
- if errErr != nil {
309
- fmt.Fprintf(prevErr, "host.runWithCapturedIO: stderr temp file failed: %v\n", errErr)
310
- }
311
- if outFile != nil {
312
- _ = outFile.Close()
313
- _ = os.Remove(stdoutPath)
314
- }
315
- if errFile != nil {
316
- _ = errFile.Close()
317
- _ = os.Remove(stderrPath)
318
- }
319
- return APIResult{Code: task()}
320
- }
310
+ outFile, outErr := os.Create(stdoutPath)
311
+ errFile, errErr := os.Create(stderrPath)
312
+ if outErr != nil || errErr != nil {
313
+ // Fall back to the original streams. Better to lose capture than the
314
+ // call. The MemFS writeSync shim surfaces the failure to the host
315
+ // console so the regression is visible.
316
+ if outErr != nil {
317
+ fmt.Fprintf(prevErr, "host.runWithCapturedIO: stdout temp file failed: %v\n", outErr)
318
+ }
319
+ if errErr != nil {
320
+ fmt.Fprintf(prevErr, "host.runWithCapturedIO: stderr temp file failed: %v\n", errErr)
321
+ }
322
+ if outFile != nil {
323
+ _ = outFile.Close()
324
+ _ = os.Remove(stdoutPath)
325
+ }
326
+ if errFile != nil {
327
+ _ = errFile.Close()
328
+ _ = os.Remove(stderrPath)
329
+ }
330
+ return APIResult{Code: task()}
331
+ }
321
332
 
322
- os.Stdout = outFile
323
- os.Stderr = errFile
333
+ os.Stdout = outFile
334
+ os.Stderr = errFile
324
335
 
325
- code := task()
336
+ code := task()
326
337
 
327
- // Sync + close BEFORE swapping back, so the plugin's last writes hit the
328
- // file before we read it.
329
- _ = outFile.Sync()
330
- _ = errFile.Sync()
331
- _ = outFile.Close()
332
- _ = errFile.Close()
338
+ // Sync + close BEFORE swapping back, so the plugin's last writes hit the
339
+ // file before we read it.
340
+ _ = outFile.Sync()
341
+ _ = errFile.Sync()
342
+ _ = outFile.Close()
343
+ _ = errFile.Close()
333
344
 
334
- os.Stdout = prevOut
335
- os.Stderr = prevErr
345
+ os.Stdout = prevOut
346
+ os.Stderr = prevErr
336
347
 
337
- stdoutBytes, _ := os.ReadFile(stdoutPath)
338
- stderrBytes, _ := os.ReadFile(stderrPath)
339
- _ = os.Remove(stdoutPath)
340
- _ = os.Remove(stderrPath)
348
+ stdoutBytes, _ := os.ReadFile(stdoutPath)
349
+ stderrBytes, _ := os.ReadFile(stderrPath)
350
+ _ = os.Remove(stdoutPath)
351
+ _ = os.Remove(stderrPath)
341
352
 
342
- return APIResult{
343
- Code: code,
344
- Stdout: string(stdoutBytes),
345
- Stderr: string(stderrBytes),
346
- }
353
+ return APIResult{
354
+ Code: code,
355
+ Stdout: string(stdoutBytes),
356
+ Stderr: string(stderrBytes),
357
+ }
347
358
  }
348
359
 
349
360
  // captureCounter avoids temp-file name collisions when plugin dispatches
@@ -1,5 +1,8 @@
1
1
  //go:build !js
2
2
 
3
+ // Stub implementations of JS-only host helpers for non-wasm targets.
4
+ // This file keeps the package importable by `go build ./...` on linux/darwin
5
+ // without GOOS=js, which native CI jobs need.
3
6
  package host
4
7
 
5
8
  // Expose is a no-op on native targets. Consumers can `go build ./...` to
@@ -7,6 +10,6 @@ package host
7
10
  // stays in scope but the body short-circuits so it's safe to call from a
8
11
  // non-wasm sanity-test entrypoint.
9
12
  func Expose(apiName string, cfg Config) {
10
- _ = apiName
11
- _ = cfg
13
+ _ = apiName
14
+ _ = cfg
12
15
  }
package/host/plugin.go CHANGED
@@ -13,47 +13,47 @@ package host
13
13
  // A typical Plugin implementation in the consumer wasm is a thin adapter that
14
14
  // forwards to the same Run* function the native sidecar's `main.go` calls:
15
15
  //
16
- // type bannerPlugin struct{}
16
+ // type bannerPlugin struct{}
17
17
  //
18
- // func (bannerPlugin) Name() string { return "@ttsc/banner" }
19
- // func (bannerPlugin) Run(command string, args []string) int {
20
- // switch command {
21
- // case "build": return utility.RunBuild(args)
22
- // case "check": return utility.RunCheck(args)
23
- // case "transform": return utility.RunTransform(args)
24
- // default: return 2
25
- // }
26
- // }
18
+ // func (bannerPlugin) Name() string { return "@ttsc/banner" }
19
+ // func (bannerPlugin) Run(command string, args []string) int {
20
+ // switch command {
21
+ // case "build": return utility.RunBuild(args)
22
+ // case "check": return utility.RunCheck(args)
23
+ // case "transform": return utility.RunTransform(args)
24
+ // default: return 2
25
+ // }
26
+ // }
27
27
  //
28
28
  // The host installs the package-level writers in `runWithCapturedIO` before
29
29
  // calling Run, so anything the plugin writes to ttsc's `stdout` / `stderr`
30
30
  // streams is captured and returned to the JS caller.
31
31
  type Plugin interface {
32
- // Name is the npm-style plugin id (e.g. `@ttsc/banner`). The JS side
33
- // passes this exact string when dispatching: `api.plugin("@ttsc/banner",
34
- // "build", opts)`. Names must be unique within a Config.
35
- Name() string
32
+ // Name is the npm-style plugin id (e.g. `@ttsc/banner`). The JS side
33
+ // passes this exact string when dispatching: `api.plugin("@ttsc/banner",
34
+ // "build", opts)`. Names must be unique within a Config.
35
+ Name() string
36
36
 
37
- // Run dispatches a subcommand. `command` is the verb the JS caller asked
38
- // for (typically build / check / transform / fix / format / version);
39
- // `args` is the rest of the argv, already prefixed with `--flag=value`
40
- // pairs the host built from the JS options object.
41
- //
42
- // Return the exit code (0 for success, 2 for usage errors, 3 for runtime
43
- // errors — mirrors the native CLI exit-code contract). Anything written
44
- // to `os.Stdout` / `os.Stderr` is captured by the host.
45
- //
46
- // API stability: experimental until v1.0; the signature is expected to
47
- // change to `Run(ctx *PluginContext) int` in a follow-up release.
48
- Run(command string, args []string) int
37
+ // Run dispatches a subcommand. `command` is the verb the JS caller asked
38
+ // for (typically build / check / transform / fix / format / version);
39
+ // `args` is the rest of the argv, already prefixed with `--flag=value`
40
+ // pairs the host built from the JS options object.
41
+ //
42
+ // Return the exit code (0 for success, 2 for usage errors, 3 for runtime
43
+ // errors — mirrors the native CLI exit-code contract). Anything written
44
+ // to `os.Stdout` / `os.Stderr` is captured by the host.
45
+ //
46
+ // API stability: experimental until v1.0; the signature is expected to
47
+ // change to `Run(ctx *PluginContext) int` in a follow-up release.
48
+ Run(command string, args []string) int
49
49
  }
50
50
 
51
51
  // Config carries the optional registrations the host applies before binding
52
52
  // `globalThis[name]`. Pass `Config{}` for a vanilla ttsc + tsgo wasm.
53
53
  type Config struct {
54
- // Plugins are dispatched through `api.plugin(name, command, opts)` from
55
- // JS. Their Run methods share the same `os.Stdout` / `os.Stderr` streams
56
- // the base build/check/transform endpoints use, so diagnostics render
57
- // the same way no matter which lane produced them.
58
- Plugins []Plugin
54
+ // Plugins are dispatched through `api.plugin(name, command, opts)` from
55
+ // JS. Their Run methods share the same `os.Stdout` / `os.Stderr` streams
56
+ // the base build/check/transform endpoints use, so diagnostics render
57
+ // the same way no matter which lane produced them.
58
+ Plugins []Plugin
59
59
  }