ttsc 0.11.0-dev.20260518-2 → 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 +27 -8
  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 +9 -9
  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
package/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # `ttsc`
2
2
 
3
- ![banner of ttsc](https://raw.githubusercontent.com/samchon/ttsc/refs/heads/master/assets/og.jpg)
3
+ ![banner of ttsc](https://ttsc.dev/og.jpg)
4
4
 
5
5
  [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/samchon/ttsc/blob/master/LICENSE)
6
6
  [![NPM Version](https://img.shields.io/npm/v/ttsc.svg)](https://www.npmjs.com/package/ttsc)
7
7
  [![NPM Downloads](https://img.shields.io/npm/dm/ttsc.svg)](https://www.npmjs.com/package/ttsc)
8
8
  [![Build Status](https://github.com/samchon/ttsc/workflows/test/badge.svg)](https://github.com/samchon/ttsc/actions?query=workflow%3Atest)
9
- [![Guide Documents](https://img.shields.io/badge/Guide-Documents-forestgreen)](https://github.com/samchon/ttsc/tree/master/docs)
9
+ [![Guide Documents](https://img.shields.io/badge/Guide-Documents-forestgreen)](https://ttsc.dev/docs)
10
10
  [![Discord Badge](https://img.shields.io/badge/discord-samchon-d91965?style=flat&labelColor=5866f2&logo=discord&logoColor=white&link=https://discord.gg/E94XhzrUCZ)](https://discord.gg/E94XhzrUCZ)
11
11
 
12
12
  A `typescript-go` toolchain for compiler-powered plugins and type-safe execution.
@@ -15,17 +15,19 @@ A `typescript-go` toolchain for compiler-powered plugins and type-safe execution
15
15
  - **`ttsx`**: execute TypeScript with type checking.
16
16
  - native TypeScript-Go execution instead of transpile-only runners.
17
17
  - type checking that `tsx` does not provide.
18
+ - **`@ttsc/lint`**: replaces `eslint` and `prettier`.
19
+ - lint violations as TS compile errors.
20
+ - format autofixes via `ttsc format`.
18
21
  - **plugin support**: compiler-powered libraries, such as `typia`.
19
- - `@ttsc/lint`: lint violations as TS compile errors.
20
22
 
21
23
  ## Setup
22
24
 
23
25
  ### Install
24
26
 
25
- Install the native TypeScript preview package with `ttsc`:
27
+ Install `ttsc`, `@ttsc/lint`, and the native TypeScript preview package:
26
28
 
27
29
  ```bash
28
- npm install -D ttsc @typescript/native-preview
30
+ npm install -D ttsc @ttsc/lint @typescript/native-preview
29
31
  ```
30
32
 
31
33
  ### Commands
@@ -44,6 +46,23 @@ npx ttsc --noEmit
44
46
  npx ttsc --watch
45
47
  ```
46
48
 
49
+ Rewrite source files in place with the `@ttsc/lint` format rules:
50
+
51
+ ```bash
52
+ npx ttsc format
53
+ ```
54
+
55
+ ### Editor
56
+
57
+ Install the VS Code extension to see lint and plugin diagnostics live as you type:
58
+
59
+ ```bash
60
+ npm install -D @ttsc/vscode
61
+ npx ttsc-vscode
62
+ ```
63
+
64
+ See [`@ttsc/vscode`](https://github.com/samchon/ttsc/tree/master/packages/vscode) for requirements, settings, and uninstall.
65
+
47
66
  ### Bundlers
48
67
 
49
68
  Use `@ttsc/unplugin` when a bundler owns your build.
@@ -51,7 +70,7 @@ Use `@ttsc/unplugin` when a bundler owns your build.
51
70
  It runs `ttsc` plugins inside supported bundlers.
52
71
 
53
72
  ```bash
54
- npm install -D ttsc @typescript/native-preview
73
+ npm install -D ttsc @ttsc/lint @typescript/native-preview
55
74
  npm install -D @ttsc/unplugin
56
75
  ```
57
76
 
@@ -150,12 +169,12 @@ console.log(matched); // true
150
169
  `ttsc` ships a few small utility plugins in this repository.
151
170
 
152
171
  - [`@ttsc/banner`](https://github.com/samchon/ttsc/tree/master/packages/banner): adds `@packageDocumentation` JSDoc banners.
153
- - [`@ttsc/lint`](https://github.com/samchon/ttsc/tree/master/packages/lint): reports lint violations as TypeScript compile errors.
172
+ - [`@ttsc/lint`](https://github.com/samchon/ttsc/tree/master/packages/lint): lints and formats TypeScript source.
154
173
  - [`@ttsc/paths`](https://github.com/samchon/ttsc/tree/master/packages/paths): rewrites source path aliases so JS and declaration emit receive relative imports.
155
174
  - [`@ttsc/strip`](https://github.com/samchon/ttsc/tree/master/packages/strip): removes configured calls and `debugger` statements.
156
175
  - [`@ttsc/unplugin`](https://github.com/samchon/ttsc/tree/master/packages/unplugin): runs `ttsc` plugins inside bundlers supported by `unplugin`.
157
176
 
158
- Plugin authors should start from the [`Guide Documents`](https://github.com/samchon/ttsc/tree/master/docs).
177
+ Plugin authors should start from the [`Guide Documents`](https://ttsc.dev/docs).
159
178
 
160
179
  Ecosystem plugins are listed below; PRs adding `ttsc` plugins are welcome.
161
180
 
@@ -7,25 +7,24 @@
7
7
  package main
8
8
 
9
9
  import (
10
- "encoding/json"
11
- "flag"
12
- "fmt"
13
- "os"
14
- "path/filepath"
15
- "strings"
10
+ "encoding/json"
11
+ "flag"
12
+ "fmt"
13
+ "path/filepath"
14
+ "strings"
16
15
 
17
- shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
16
+ shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
18
17
 
19
- "github.com/samchon/ttsc/packages/ttsc/driver"
20
- cwdutil "github.com/samchon/ttsc/packages/ttsc/internal/cwd"
18
+ "github.com/samchon/ttsc/packages/ttsc/driver"
19
+ cwdutil "github.com/samchon/ttsc/packages/ttsc/internal/cwd"
21
20
  )
22
21
 
23
22
  type apiCompileResult struct {
24
- // Diagnostics is omitted only when the compiler produced no diagnostics.
25
- // Output is still returned in diagnostic cases because TypeScript-Go may
26
- // produce partial emit text before a build-failing error is reported.
27
- Diagnostics []apiCompileDiagnostic `json:"diagnostics,omitempty"`
28
- Output map[string]string `json:"output"`
23
+ // Diagnostics is omitted only when the compiler produced no diagnostics.
24
+ // Output is still returned in diagnostic cases because TypeScript-Go may
25
+ // produce partial emit text before a build-failing error is reported.
26
+ Diagnostics []apiCompileDiagnostic `json:"diagnostics,omitempty"`
27
+ Output map[string]string `json:"output"`
29
28
  }
30
29
 
31
30
  // apiCompileDiagnostic mirrors the public TypeScript-side diagnostic DTO.
@@ -33,113 +32,109 @@ type apiCompileResult struct {
33
32
  // `character`) instead of Go naming so callers can pass the data through
34
33
  // without remapping.
35
34
  type apiCompileDiagnostic struct {
36
- File *string `json:"file"`
37
- Category string `json:"category"`
38
- Code int32 `json:"code"`
39
- Start *int `json:"start,omitempty"`
40
- Length *int `json:"length,omitempty"`
41
- Line int `json:"line,omitempty"`
42
- Character int `json:"character,omitempty"`
43
- MessageText string `json:"messageText"`
35
+ File *string `json:"file"`
36
+ Category string `json:"category"`
37
+ Code int32 `json:"code"`
38
+ Start *int `json:"start,omitempty"`
39
+ Length *int `json:"length,omitempty"`
40
+ Line int `json:"line,omitempty"`
41
+ Character int `json:"character,omitempty"`
42
+ MessageText string `json:"messageText"`
44
43
  }
45
44
 
46
45
  func runAPICompile(args []string) int {
47
- fs := flag.NewFlagSet("api-compile", flag.ContinueOnError)
48
- fs.SetOutput(stderr)
49
- tsconfigPath := fs.String("tsconfig", "tsconfig.json", "path to tsconfig.json")
50
- cwdOverride := fs.String("cwd", "", "override the working directory")
51
- if err := fs.Parse(args); err != nil {
52
- return 2
53
- }
46
+ fs := flag.NewFlagSet("api-compile", flag.ContinueOnError)
47
+ fs.SetOutput(stderr)
48
+ tsconfigPath := fs.String("tsconfig", "tsconfig.json", "path to tsconfig.json")
49
+ cwdOverride := fs.String("cwd", "", "override the working directory")
50
+ if err := fs.Parse(args); err != nil {
51
+ return 2
52
+ }
54
53
 
55
- cwd, err := cwdutil.Resolve(*cwdOverride, os.Getwd)
56
- if err != nil {
57
- fmt.Fprintf(stderr, "ttsc: %v\n", err)
58
- return 2
59
- }
54
+ cwd, err := cwdutil.Resolve(*cwdOverride, getwd)
55
+ if err != nil {
56
+ fmt.Fprintf(stderr, "ttsc: %v\n", err)
57
+ return 2
58
+ }
60
59
 
61
- prog, diags, err := driver.LoadProgram(cwd, *tsconfigPath, driver.LoadProgramOptions{
62
- ForceEmit: true,
63
- })
64
- if err != nil {
65
- fmt.Fprintf(stderr, "ttsc api-compile: %v\n", err)
66
- return 2
67
- }
68
- if prog != nil {
69
- defer prog.Close()
70
- diags = append(diags, prog.Diagnostics()...)
71
- }
60
+ prog, diags, err := driver.LoadProgram(cwd, *tsconfigPath, driver.LoadProgramOptions{
61
+ ForceEmit: true,
62
+ })
63
+ if err != nil {
64
+ fmt.Fprintf(stderr, "ttsc api-compile: %v\n", err)
65
+ return 2
66
+ }
67
+ if prog != nil {
68
+ defer prog.Close()
69
+ diags = append(diags, prog.Diagnostics()...)
70
+ }
72
71
 
73
- output := map[string]string{}
74
- if prog != nil {
75
- rewrites := driver.NewRewriteSet()
76
- // Capture WriteFile output in a map keyed by project-relative paths. This
77
- // gives the JS API a deterministic object and avoids touching outDir.
78
- writeFile := shimcompiler.WriteFile(
79
- func(fileName, text string, _ *shimcompiler.WriteFileData) error {
80
- output[apiOutputKey(cwd, fileName)] = text
81
- return nil
82
- },
83
- )
84
- _, emitDiags, err := prog.EmitAll(rewrites, writeFile)
85
- if err != nil {
86
- fmt.Fprintf(stderr, "ttsc api-compile: emit failed: %v\n", err)
87
- return 3
88
- }
89
- diags = append(diags, emitDiags...)
90
- }
72
+ output := map[string]string{}
73
+ if prog != nil {
74
+ rewrites := driver.NewRewriteSet()
75
+ // Capture WriteFile output in a map keyed by project-relative paths. This
76
+ // gives the JS API a deterministic object and avoids touching outDir.
77
+ writeFile := shimcompiler.WriteFile(
78
+ func(fileName, text string, _ *shimcompiler.WriteFileData) error {
79
+ output[apiOutputKey(cwd, fileName)] = text
80
+ return nil
81
+ },
82
+ )
83
+ _, emitDiags, err := prog.EmitAll(rewrites, writeFile)
84
+ if err != nil {
85
+ fmt.Fprintf(stderr, "ttsc api-compile: emit failed: %v\n", err)
86
+ return 3
87
+ }
88
+ diags = append(diags, emitDiags...)
89
+ }
91
90
 
92
- result := apiCompileResult{
93
- Diagnostics: make([]apiCompileDiagnostic, 0, len(diags)),
94
- Output: output,
95
- }
96
- for _, diag := range diags {
97
- result.Diagnostics = append(result.Diagnostics, toAPICompileDiagnostic(diag))
98
- }
91
+ result := apiCompileResult{
92
+ Diagnostics: make([]apiCompileDiagnostic, 0, len(diags)),
93
+ Output: output,
94
+ }
95
+ for _, diag := range diags {
96
+ result.Diagnostics = append(result.Diagnostics, toAPICompileDiagnostic(diag))
97
+ }
99
98
 
100
- data, err := json.Marshal(result)
101
- if err != nil {
102
- fmt.Fprintf(stderr, "ttsc api-compile: result marshal failed: %v\n", err)
103
- return 3
104
- }
105
- fmt.Fprintln(stdout, string(data))
106
- if driver.CountErrors(diags) > 0 {
107
- return 2
108
- }
109
- return 0
99
+ data, _ := json.Marshal(result)
100
+ fmt.Fprintln(stdout, string(data))
101
+ if driver.CountErrors(diags) > 0 {
102
+ return 2
103
+ }
104
+ return 0
110
105
  }
111
106
 
112
107
  func toAPICompileDiagnostic(diag driver.Diagnostic) apiCompileDiagnostic {
113
- var file *string
114
- if diag.File != "" {
115
- value := diag.File
116
- file = &value
117
- }
118
- category := "error"
119
- if diag.Severity == driver.SeverityWarning {
120
- category = "warning"
121
- }
122
- return apiCompileDiagnostic{
123
- File: file,
124
- Category: category,
125
- Code: diag.Code,
126
- Start: diag.Start,
127
- Length: diag.Length,
128
- Line: diag.Line,
129
- Character: diag.Column,
130
- MessageText: diag.Message,
131
- }
108
+ var file *string
109
+ if diag.File != "" {
110
+ value := diag.File
111
+ file = &value
112
+ }
113
+ category := "error"
114
+ if diag.Severity == driver.SeverityWarning {
115
+ category = "warning"
116
+ }
117
+ return apiCompileDiagnostic{
118
+ File: file,
119
+ Category: category,
120
+ Code: diag.Code,
121
+ Start: diag.Start,
122
+ Length: diag.Length,
123
+ Line: diag.Line,
124
+ Character: diag.Column,
125
+ MessageText: diag.Message,
126
+ }
132
127
  }
133
128
 
134
129
  func apiOutputKey(cwd, fileName string) string {
135
- // Relative keys are the API contract for files inside the project. Absolute
136
- // keys are preserved for uncommon compiler outputs outside cwd.
137
- if rel, err := filepath.Rel(cwd, fileName); err == nil && !isOutsideRelativePath(rel) {
138
- return filepath.ToSlash(rel)
139
- }
140
- return filepath.ToSlash(fileName)
130
+ // Relative keys are the API contract for files inside the project. Absolute
131
+ // keys are preserved for uncommon compiler outputs outside cwd.
132
+ if rel, err := filepath.Rel(cwd, fileName); err == nil && !isOutsideRelativePath(rel) {
133
+ return filepath.ToSlash(rel)
134
+ }
135
+ return filepath.ToSlash(fileName)
141
136
  }
142
137
 
143
138
  func isOutsideRelativePath(rel string) bool {
144
- return rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator))
139
+ return rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator))
145
140
  }
@@ -0,0 +1,81 @@
1
+ package main
2
+
3
+ import (
4
+ "errors"
5
+ "strings"
6
+ "testing"
7
+
8
+ "github.com/samchon/ttsc/packages/ttsc/driver"
9
+ )
10
+
11
+ type apiCompileApplyErrorPlugin struct{}
12
+
13
+ func (apiCompileApplyErrorPlugin) ApplyProgram(*driver.Program, driver.PluginContext) error {
14
+ return errors.New("api compile apply boom")
15
+ }
16
+
17
+ /**
18
+ * Verifies api-compile handles parse, cwd, diagnostic, and emit failures.
19
+ *
20
+ * The JSON API path is used by the JS wrapper, so its command entrypoint must
21
+ * return stable status codes for wrapper misuse, project diagnostics, and
22
+ * linked transform failures.
23
+ *
24
+ * 1. Exercise bad flags and a failing cwd resolver.
25
+ * 2. Compile missing and type-invalid projects.
26
+ * 3. Assert linked ApplyProgram errors surface through the emit branch.
27
+ */
28
+ func TestAPICompileBranches(t *testing.T) {
29
+ code, _, _ := captureCommand(t, func() int {
30
+ return runAPICompile([]string{"--cwd"})
31
+ })
32
+ if code != 2 {
33
+ t.Fatalf("bad flag status mismatch: %d", code)
34
+ }
35
+
36
+ code, _, errText := captureCommand(t, func() int {
37
+ getwd = failGetwd
38
+ return runAPICompile(nil)
39
+ })
40
+ if code != 2 || !strings.Contains(errText, "cwd boom") {
41
+ t.Fatalf("cwd error mismatch: code=%d stderr=%q", code, errText)
42
+ }
43
+
44
+ root := t.TempDir()
45
+ code, _, errText = captureCommand(t, func() int {
46
+ return runAPICompile([]string{"--cwd", root, "--tsconfig", "missing.json"})
47
+ })
48
+ if code != 2 || !strings.Contains(errText, "tsconfig not found") {
49
+ t.Fatalf("missing config mismatch: code=%d stderr=%q", code, errText)
50
+ }
51
+
52
+ writeCommandProjectFile(t, root, "tsconfig.json", `{
53
+ "compilerOptions": { "module": "commonjs", "target": "es2020", "strict": true },
54
+ "files": ["index.ts"]
55
+ }
56
+ `)
57
+ writeCommandProjectFile(t, root, "index.ts", `const value: number = "text";
58
+ export { value };
59
+ `)
60
+ code, out, _ := captureCommand(t, func() int {
61
+ return runAPICompile([]string{"--cwd", root, "--tsconfig", "tsconfig.json"})
62
+ })
63
+ if code != 2 || !strings.Contains(out, `"diagnostics"`) {
64
+ t.Fatalf("diagnostic compile mismatch: code=%d stdout=%q", code, out)
65
+ }
66
+ if got := toAPICompileDiagnostic(driver.Diagnostic{Severity: driver.SeverityWarning}).Category; got != "warning" {
67
+ t.Fatalf("warning category mismatch: %q", got)
68
+ }
69
+
70
+ resetCommandLinkedPluginRegistry()
71
+ driver.RegisterPlugin(apiCompileApplyErrorPlugin{})
72
+ t.Setenv(driver.LinkedPluginsEnv, `[{"name":"error","stage":"transform","config":{}}]`)
73
+ writeCommandProjectFile(t, root, "index.ts", `export const value = 1;
74
+ `)
75
+ code, _, errText = captureCommand(t, func() int {
76
+ return runAPICompile([]string{"--cwd", root, "--tsconfig", "tsconfig.json"})
77
+ })
78
+ if code != 3 || !strings.Contains(errText, "api compile apply boom") {
79
+ t.Fatalf("emit error mismatch: code=%d stderr=%q", code, errText)
80
+ }
81
+ }
@@ -7,69 +7,64 @@
7
7
  package main
8
8
 
9
9
  import (
10
- "encoding/json"
11
- "flag"
12
- "fmt"
13
- "os"
10
+ "encoding/json"
11
+ "flag"
12
+ "fmt"
14
13
 
15
- "github.com/samchon/ttsc/packages/ttsc/driver"
16
- cwdutil "github.com/samchon/ttsc/packages/ttsc/internal/cwd"
14
+ "github.com/samchon/ttsc/packages/ttsc/driver"
15
+ cwdutil "github.com/samchon/ttsc/packages/ttsc/internal/cwd"
17
16
  )
18
17
 
19
18
  type apiTransformResult struct {
20
- // TypeScript contains every non-library source file visible through the
21
- // Program facade, keyed the same way api-compile keys emitted files.
22
- Diagnostics []apiCompileDiagnostic `json:"diagnostics,omitempty"`
23
- TypeScript map[string]string `json:"typescript"`
19
+ // TypeScript contains every non-library source file visible through the
20
+ // Program facade, keyed the same way api-compile keys emitted files.
21
+ Diagnostics []apiCompileDiagnostic `json:"diagnostics,omitempty"`
22
+ TypeScript map[string]string `json:"typescript"`
24
23
  }
25
24
 
26
25
  func runAPITransform(args []string) int {
27
- fs := flag.NewFlagSet("api-transform", flag.ContinueOnError)
28
- fs.SetOutput(stderr)
29
- tsconfigPath := fs.String("tsconfig", "tsconfig.json", "path to tsconfig.json")
30
- cwdOverride := fs.String("cwd", "", "override the working directory")
31
- if err := fs.Parse(args); err != nil {
32
- return 2
33
- }
26
+ fs := flag.NewFlagSet("api-transform", flag.ContinueOnError)
27
+ fs.SetOutput(stderr)
28
+ tsconfigPath := fs.String("tsconfig", "tsconfig.json", "path to tsconfig.json")
29
+ cwdOverride := fs.String("cwd", "", "override the working directory")
30
+ if err := fs.Parse(args); err != nil {
31
+ return 2
32
+ }
34
33
 
35
- cwd, err := cwdutil.Resolve(*cwdOverride, os.Getwd)
36
- if err != nil {
37
- fmt.Fprintf(stderr, "ttsc: %v\n", err)
38
- return 2
39
- }
34
+ cwd, err := cwdutil.Resolve(*cwdOverride, getwd)
35
+ if err != nil {
36
+ fmt.Fprintf(stderr, "ttsc: %v\n", err)
37
+ return 2
38
+ }
40
39
 
41
- prog, diags, err := driver.LoadProgram(cwd, *tsconfigPath, driver.LoadProgramOptions{
42
- ForceNoEmit: true,
43
- })
44
- if err != nil {
45
- fmt.Fprintf(stderr, "ttsc api-transform: %v\n", err)
46
- return 2
47
- }
48
- typescript := map[string]string{}
49
- if prog != nil {
50
- defer prog.Close()
51
- for _, file := range prog.SourceFiles() {
52
- typescript[apiOutputKey(cwd, file.FileName())] = file.Text()
53
- }
54
- diags = append(diags, prog.Diagnostics()...)
55
- }
40
+ prog, diags, err := driver.LoadProgram(cwd, *tsconfigPath, driver.LoadProgramOptions{
41
+ ForceNoEmit: true,
42
+ })
43
+ if err != nil {
44
+ fmt.Fprintf(stderr, "ttsc api-transform: %v\n", err)
45
+ return 2
46
+ }
47
+ typescript := map[string]string{}
48
+ if prog != nil {
49
+ defer prog.Close()
50
+ for _, file := range prog.SourceFiles() {
51
+ typescript[apiOutputKey(cwd, file.FileName())] = file.Text()
52
+ }
53
+ diags = append(diags, prog.Diagnostics()...)
54
+ }
56
55
 
57
- result := apiTransformResult{
58
- Diagnostics: make([]apiCompileDiagnostic, 0, len(diags)),
59
- TypeScript: typescript,
60
- }
61
- for _, diag := range diags {
62
- result.Diagnostics = append(result.Diagnostics, toAPICompileDiagnostic(diag))
63
- }
56
+ result := apiTransformResult{
57
+ Diagnostics: make([]apiCompileDiagnostic, 0, len(diags)),
58
+ TypeScript: typescript,
59
+ }
60
+ for _, diag := range diags {
61
+ result.Diagnostics = append(result.Diagnostics, toAPICompileDiagnostic(diag))
62
+ }
64
63
 
65
- data, err := json.Marshal(result)
66
- if err != nil {
67
- fmt.Fprintf(stderr, "ttsc api-transform: result marshal failed: %v\n", err)
68
- return 3
69
- }
70
- fmt.Fprintln(stdout, string(data))
71
- if driver.CountErrors(diags) > 0 {
72
- return 2
73
- }
74
- return 0
64
+ data, _ := json.Marshal(result)
65
+ fmt.Fprintln(stdout, string(data))
66
+ if driver.CountErrors(diags) > 0 {
67
+ return 2
68
+ }
69
+ return 0
75
70
  }
@@ -0,0 +1,41 @@
1
+ package main
2
+
3
+ import (
4
+ "strings"
5
+ "testing"
6
+ )
7
+
8
+ /**
9
+ * Verifies api-transform rejects invalid command and project setup.
10
+ *
11
+ * Source-to-source API callers need the same stable wrapper diagnostics as the
12
+ * compile API, but without touching emit. These branches are command-frontdoor
13
+ * behavior rather than TypeScript-Go internals.
14
+ *
15
+ * 1. Pass an incomplete flag.
16
+ * 2. Force cwd resolution to fail.
17
+ * 3. Point the API at a missing tsconfig.
18
+ */
19
+ func TestAPITransformBranches(t *testing.T) {
20
+ code, _, _ := captureCommand(t, func() int {
21
+ return runAPITransform([]string{"--cwd"})
22
+ })
23
+ if code != 2 {
24
+ t.Fatalf("bad flag status mismatch: %d", code)
25
+ }
26
+
27
+ code, _, errText := captureCommand(t, func() int {
28
+ getwd = failGetwd
29
+ return runAPITransform(nil)
30
+ })
31
+ if code != 2 || !strings.Contains(errText, "cwd boom") {
32
+ t.Fatalf("cwd error mismatch: code=%d stderr=%q", code, errText)
33
+ }
34
+
35
+ code, _, errText = captureCommand(t, func() int {
36
+ return runAPITransform([]string{"--cwd", t.TempDir(), "--tsconfig", "missing.json"})
37
+ })
38
+ if code != 2 || !strings.Contains(errText, "tsconfig not found") {
39
+ t.Fatalf("missing config mismatch: code=%d stderr=%q", code, errText)
40
+ }
41
+ }