ttsc 0.12.3 → 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 (126) 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 +22 -9
  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/shim.go +24 -0
  94. package/shim/checker/shim.go +35 -0
  95. package/shim/core/shim.go +17 -0
  96. package/shim/diagnosticwriter/shim.go +4 -0
  97. package/shim/lsp/shim.go +3 -3
  98. package/shim/printer/shim.go +20 -0
  99. package/src/TtscCompiler.ts +7 -0
  100. package/src/compiler/internal/buildNativeCompiler.ts +23 -3
  101. package/src/compiler/internal/compileProjectInMemory.ts +34 -25
  102. package/src/compiler/internal/paths.ts +68 -0
  103. package/src/compiler/internal/project/readProjectConfig.ts +86 -1
  104. package/src/compiler/internal/project/resolveProjectConfig.ts +24 -1
  105. package/src/compiler/internal/resolveBinary.ts +17 -1
  106. package/src/compiler/internal/resolveEmittedJavaScript.ts +43 -12
  107. package/src/compiler/internal/resolveTsgo.ts +29 -1
  108. package/src/compiler/internal/runBuild.ts +110 -55
  109. package/src/compiler/internal/runSingleFileEmit.ts +14 -1
  110. package/src/compiler/internal/sharedHostHelpers.ts +7 -7
  111. package/src/compiler/internal/spawnNative.ts +64 -0
  112. package/src/compiler/internal/transformProjectInMemory.ts +47 -67
  113. package/src/launcher/internal/getCompilerVersionText.ts +7 -0
  114. package/src/launcher/internal/prepareExecution.ts +32 -0
  115. package/src/launcher/internal/resolveCacheDir.ts +19 -0
  116. package/src/launcher/internal/resolveTtscserverBinary.ts +2 -0
  117. package/src/launcher/internal/runTtsc.ts +22 -10
  118. package/src/launcher/internal/runTtscserver.ts +9 -1
  119. package/src/launcher/internal/runTtsx.ts +33 -8
  120. package/src/plugin/internal/buildSourcePlugin.ts +40 -14
  121. package/src/plugin/internal/loadProjectPlugins.ts +42 -18
  122. package/src/structures/ITtscCompilerTransformation.ts +15 -2
  123. package/src/structures/ITtscPlugin.ts +0 -1
  124. package/src/structures/internal/TtscSingleFileEmitOptions.ts +0 -2
  125. package/tools/gen_shims/main.go +43 -8
  126. package/utility/host.go +294 -245
package/cmd/ttsc/build.go CHANGED
@@ -8,15 +8,15 @@
8
8
  package main
9
9
 
10
10
  import (
11
- "encoding/json"
12
- "flag"
13
- "fmt"
14
- "os"
15
- "path/filepath"
11
+ "encoding/json"
12
+ "flag"
13
+ "fmt"
14
+ "os"
15
+ "path/filepath"
16
16
 
17
- shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
17
+ shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
18
18
 
19
- "github.com/samchon/ttsc/packages/ttsc/driver"
19
+ "github.com/samchon/ttsc/packages/ttsc/driver"
20
20
  )
21
21
 
22
22
  // runBuild implements the `ttsc` project-build lane. Two modes:
@@ -25,110 +25,113 @@ import (
25
25
  // - --emit: also run tsgo's emit pipeline, patching every recognized native
26
26
  // consumer call in the resulting .js files.
27
27
  func runBuild(args []string) int {
28
- fs := flag.NewFlagSet("build", flag.ContinueOnError)
29
- fs.SetOutput(stderr)
30
- tsconfigPath := fs.String("tsconfig", "tsconfig.json", "path to tsconfig.json")
31
- cwdOverride := fs.String("cwd", "", "override the working directory (defaults to process cwd)")
32
- quiet := fs.Bool("quiet", true, "suppress the per-call diagnostic summary")
33
- verbose := fs.Bool("verbose", false, "print the per-call diagnostic summary")
34
- emit := fs.Bool("emit", false, "force emitted .js files (runs tsgo + ttsc rewrite)")
35
- noEmit := fs.Bool("noEmit", false, "force analysis-only run with no file writes")
36
- outDir := fs.String("outDir", "", "override compilerOptions.outDir for this build")
37
- manifestPath := fs.String("manifest", "", "write emitted file list as JSON to this path")
38
- if err := fs.Parse(args); err != nil {
39
- return 2
40
- }
41
- if *emit && *noEmit {
42
- fmt.Fprintln(stderr, "ttsc: --emit and --noEmit are mutually exclusive")
43
- return 2
44
- }
45
- if *verbose {
46
- *quiet = false
47
- }
28
+ fs := flag.NewFlagSet("build", flag.ContinueOnError)
29
+ fs.SetOutput(stderr)
30
+ tsconfigPath := fs.String("tsconfig", "tsconfig.json", "path to tsconfig.json")
31
+ cwdOverride := fs.String("cwd", "", "override the working directory (defaults to process cwd)")
32
+ quiet := fs.Bool("quiet", true, "suppress the per-call diagnostic summary")
33
+ verbose := fs.Bool("verbose", false, "print the per-call diagnostic summary")
34
+ emit := fs.Bool("emit", false, "force emitted .js files (runs tsgo + ttsc rewrite)")
35
+ noEmit := fs.Bool("noEmit", false, "force analysis-only run with no file writes")
36
+ outDir := fs.String("outDir", "", "override compilerOptions.outDir for this build")
37
+ manifestPath := fs.String("manifest", "", "write emitted file list as JSON to this path")
38
+ if err := fs.Parse(args); err != nil {
39
+ return 2
40
+ }
41
+ if *emit && *noEmit {
42
+ fmt.Fprintln(stderr, "ttsc: --emit and --noEmit are mutually exclusive")
43
+ return 2
44
+ }
45
+ if *verbose {
46
+ *quiet = false
47
+ }
48
48
 
49
- cwd := *cwdOverride
50
- if cwd == "" {
51
- var err error
52
- cwd, err = getwd()
53
- if err != nil {
54
- fmt.Fprintf(stderr, "ttsc: could not get working directory: %v\n", err)
55
- return 2
56
- }
57
- }
49
+ cwd := *cwdOverride
50
+ if cwd == "" {
51
+ var err error
52
+ cwd, err = getwd()
53
+ if err != nil {
54
+ fmt.Fprintf(stderr, "ttsc: could not get working directory: %v\n", err)
55
+ return 2
56
+ }
57
+ }
58
58
 
59
- prog, diags, err := driver.LoadProgram(cwd, *tsconfigPath, driver.LoadProgramOptions{
60
- ForceEmit: *emit,
61
- ForceNoEmit: *noEmit,
62
- OutDir: *outDir,
63
- })
64
- if err != nil {
65
- fmt.Fprintf(stderr, "ttsc: %v\n", err)
66
- return 2
67
- }
68
- if len(diags) > 0 {
69
- driver.WritePrettyDiagnostics(stderr, diags, cwd)
70
- return 2
71
- }
72
- defer prog.Close()
73
- if diags := prog.Diagnostics(); len(diags) > 0 {
74
- driver.WritePrettyDiagnostics(stderr, diags, cwd)
75
- return 2
76
- }
59
+ prog, diags, err := driver.LoadProgram(cwd, *tsconfigPath, driver.LoadProgramOptions{
60
+ ForceEmit: *emit,
61
+ ForceNoEmit: *noEmit,
62
+ OutDir: *outDir,
63
+ })
64
+ if err != nil {
65
+ fmt.Fprintf(stderr, "ttsc: %v\n", err)
66
+ return 2
67
+ }
68
+ if len(diags) > 0 {
69
+ driver.WritePrettyDiagnostics(stderr, diags, cwd)
70
+ return 2
71
+ }
72
+ defer prog.Close()
73
+ if diags := prog.Diagnostics(); len(diags) > 0 {
74
+ driver.WritePrettyDiagnostics(stderr, diags, cwd)
75
+ return 2
76
+ }
77
77
 
78
- rewrites := driver.NewRewriteSet()
79
- shouldEmit := !prog.ParsedConfig.ParsedConfig.CompilerOptions.NoEmit.IsTrue()
80
- if !*quiet {
81
- fmt.Fprintf(stdout, "// ttsc: tsconfig=%s cwd=%s sites=%d emit=%v\n", *tsconfigPath, cwd, 0, shouldEmit)
82
- }
78
+ rewrites := driver.NewRewriteSet()
79
+ // shouldEmit reflects the resolved tsconfig noEmit flag. The flag lives
80
+ // three levels deep in the parsed config because TypeScript-Go mirrors the
81
+ // tsconfig object structure verbatim, with a tri-state bool per option.
82
+ shouldEmit := !prog.ParsedConfig.ParsedConfig.CompilerOptions.NoEmit.IsTrue()
83
+ if !*quiet {
84
+ fmt.Fprintf(stdout, "// ttsc: tsconfig=%s cwd=%s sites=%d emit=%v\n", *tsconfigPath, cwd, 0, shouldEmit)
85
+ }
83
86
 
84
- if shouldEmit {
85
- // Emit is callback-driven in TypeScript-Go. ttsc keeps that shape and
86
- // wraps only the final WriteFile step so native rewrites and custom output
87
- // capture share the same path.
88
- writeFile := shimcompiler.WriteFile(
89
- func(fileName, text string, _ *shimcompiler.WriteFileData) error {
90
- return driver.DefaultWriteFile(fileName, text)
91
- },
92
- )
93
- res, eDiags, err := prog.EmitAll(rewrites, writeFile)
94
- if err != nil {
95
- fmt.Fprintf(stderr, "ttsc: emit failed: %v\n", err)
96
- return 3
97
- }
98
- for _, d := range eDiags {
99
- fmt.Fprintln(stderr, " -", d.String())
100
- }
101
- if driver.CountErrors(eDiags) > 0 {
102
- return 2
103
- }
104
- if !*quiet {
105
- fmt.Fprintf(stdout, "// ttsc: emitted=%d files\n", len(res.EmittedFiles))
106
- for _, f := range res.EmittedFiles {
107
- rel := f
108
- if abs, err := filepath.Rel(cwd, f); err == nil {
109
- rel = abs
110
- }
111
- fmt.Fprintln(stdout, " +", rel)
112
- }
113
- }
114
- if *manifestPath != "" {
115
- // The manifest is intentionally just the emitted file list. Higher
116
- // layers already know the project and tsconfig, and tests compare this
117
- // array as the build contract.
118
- data, _ := json.Marshal(res.EmittedFiles)
119
- if err := os.MkdirAll(filepath.Dir(*manifestPath), 0o755); err != nil {
120
- fmt.Fprintf(stderr, "ttsc: manifest mkdir failed: %v\n", err)
121
- return 3
122
- }
123
- if err := os.WriteFile(*manifestPath, data, 0o644); err != nil {
124
- fmt.Fprintf(stderr, "ttsc: manifest write failed: %v\n", err)
125
- return 3
126
- }
127
- }
128
- }
87
+ if shouldEmit {
88
+ // Emit is callback-driven in TypeScript-Go. ttsc keeps that shape and
89
+ // wraps only the final WriteFile step so native rewrites and custom output
90
+ // capture share the same path.
91
+ writeFile := shimcompiler.WriteFile(
92
+ func(fileName, text string, _ *shimcompiler.WriteFileData) error {
93
+ return driver.DefaultWriteFile(fileName, text)
94
+ },
95
+ )
96
+ res, eDiags, err := prog.EmitAll(rewrites, writeFile)
97
+ if err != nil {
98
+ fmt.Fprintf(stderr, "ttsc: emit failed: %v\n", err)
99
+ return 3
100
+ }
101
+ for _, d := range eDiags {
102
+ fmt.Fprintln(stderr, " -", d.String())
103
+ }
104
+ if driver.CountErrors(eDiags) > 0 {
105
+ return 2
106
+ }
107
+ if !*quiet {
108
+ fmt.Fprintf(stdout, "// ttsc: emitted=%d files\n", len(res.EmittedFiles))
109
+ for _, f := range res.EmittedFiles {
110
+ rel := f
111
+ if abs, err := filepath.Rel(cwd, f); err == nil {
112
+ rel = abs
113
+ }
114
+ fmt.Fprintln(stdout, " +", rel)
115
+ }
116
+ }
117
+ if *manifestPath != "" {
118
+ // The manifest is intentionally just the emitted file list. Higher
119
+ // layers already know the project and tsconfig, and tests compare this
120
+ // array as the build contract.
121
+ data, _ := json.Marshal(res.EmittedFiles)
122
+ if err := os.MkdirAll(filepath.Dir(*manifestPath), 0o755); err != nil {
123
+ fmt.Fprintf(stderr, "ttsc: manifest mkdir failed: %v\n", err)
124
+ return 3
125
+ }
126
+ if err := os.WriteFile(*manifestPath, data, 0o644); err != nil {
127
+ fmt.Fprintf(stderr, "ttsc: manifest write failed: %v\n", err)
128
+ return 3
129
+ }
130
+ }
131
+ }
129
132
 
130
- if !*quiet {
131
- fmt.Fprintf(stdout, "// ttsc: recognized=%d total=%d rewrites=%d\n", 0, 0, rewrites.Len())
132
- }
133
- return 0
133
+ if !*quiet {
134
+ fmt.Fprintf(stdout, "// ttsc: recognized=%d total=%d rewrites=%d\n", 0, 0, rewrites.Len())
135
+ }
136
+ return 0
134
137
  }
@@ -1,130 +1,128 @@
1
1
  package main
2
2
 
3
3
  import (
4
- "errors"
5
- "path/filepath"
6
- "strings"
7
- "testing"
4
+ "errors"
5
+ "path/filepath"
6
+ "strings"
7
+ "testing"
8
8
 
9
- "github.com/samchon/ttsc/packages/ttsc/driver"
9
+ "github.com/samchon/ttsc/packages/ttsc/driver"
10
10
  )
11
11
 
12
12
  type buildApplyErrorPlugin struct{}
13
13
 
14
14
  func (buildApplyErrorPlugin) ApplyProgram(*driver.Program, driver.PluginContext) error {
15
- return errors.New("build apply boom")
15
+ return errors.New("build apply boom")
16
16
  }
17
17
 
18
- /**
19
- * Verifies build command branches report setup, emit, and manifest failures.
20
- *
21
- * The native build command is the fallback compiler host behind ttsc. It must
22
- * fail before writing partial output when flags, cwd, tsconfig, disk emit, or
23
- * manifest paths are invalid.
24
- *
25
- * 1. Exercise bad flags, cwd failure, and project-load failures.
26
- * 2. Force emit through an invalid outDir.
27
- * 3. Force manifest mkdir and write failures after a valid emit.
28
- */
18
+ // TestBuildBranches verifies build command branches report setup, emit, and manifest failures.
19
+ //
20
+ // The native build command is the fallback compiler host behind ttsc. It must
21
+ // fail before writing partial output when flags, cwd, tsconfig, disk emit, or
22
+ // manifest paths are invalid.
23
+ //
24
+ // 1. Exercise bad flags, cwd failure, and project-load failures.
25
+ // 2. Force emit through an invalid outDir.
26
+ // 3. Force manifest mkdir and write failures after a valid emit.
29
27
  func TestBuildBranches(t *testing.T) {
30
- code, _, _ := captureCommand(t, func() int {
31
- return runBuild([]string{"--cwd"})
32
- })
33
- if code != 2 {
34
- t.Fatalf("bad flag status mismatch: %d", code)
35
- }
36
- code, _, errText := captureCommand(t, func() int {
37
- getwd = failGetwd
38
- return runBuild(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
- root := t.TempDir()
44
- code, _, errText = captureCommand(t, func() int {
45
- return runBuild([]string{"--cwd", root, "--tsconfig", "missing.json"})
46
- })
47
- if code != 2 || !strings.Contains(errText, "tsconfig not found") {
48
- t.Fatalf("missing config mismatch: code=%d stderr=%q", code, errText)
49
- }
28
+ code, _, _ := captureCommand(t, func() int {
29
+ return runBuild([]string{"--cwd"})
30
+ })
31
+ if code != 2 {
32
+ t.Fatalf("bad flag status mismatch: %d", code)
33
+ }
34
+ code, _, errText := captureCommand(t, func() int {
35
+ getwd = failGetwd
36
+ return runBuild(nil)
37
+ })
38
+ if code != 2 || !strings.Contains(errText, "cwd boom") {
39
+ t.Fatalf("cwd error mismatch: code=%d stderr=%q", code, errText)
40
+ }
41
+ root := t.TempDir()
42
+ code, _, errText = captureCommand(t, func() int {
43
+ return runBuild([]string{"--cwd", root, "--tsconfig", "missing.json"})
44
+ })
45
+ if code != 2 || !strings.Contains(errText, "tsconfig not found") {
46
+ t.Fatalf("missing config mismatch: code=%d stderr=%q", code, errText)
47
+ }
50
48
 
51
- writeCommandProjectFile(t, root, "tsconfig.json", `{
49
+ writeCommandProjectFile(t, root, "tsconfig.json", `{
52
50
  "compilerOptions": { "module": "commonjs", "target": "es2020", "strict": true },
53
51
  "files": ["index.ts"]
54
52
  }
55
53
  `)
56
- writeCommandProjectFile(t, root, "index.ts", `const value: number = "text";
54
+ writeCommandProjectFile(t, root, "index.ts", `const value: number = "text";
57
55
  export { value };
58
56
  `)
59
- code, _, errText = captureCommand(t, func() int {
60
- return runBuild([]string{"--cwd", root, "--tsconfig", "tsconfig.json"})
61
- })
62
- if code != 2 || !strings.Contains(errText, "number") {
63
- t.Fatalf("semantic diagnostics mismatch: code=%d stderr=%q", code, errText)
64
- }
57
+ code, _, errText = captureCommand(t, func() int {
58
+ return runBuild([]string{"--cwd", root, "--tsconfig", "tsconfig.json"})
59
+ })
60
+ if code != 2 || !strings.Contains(errText, "number") {
61
+ t.Fatalf("semantic diagnostics mismatch: code=%d stderr=%q", code, errText)
62
+ }
65
63
 
66
- writeCommandProjectFile(t, root, "tsconfig.json", `{
64
+ writeCommandProjectFile(t, root, "tsconfig.json", `{
67
65
  "compilerOptions": { "module": "not-a-module-kind", "target": "es2020" },
68
66
  "files": ["index.ts"]
69
67
  }
70
68
  `)
71
- code, _, errText = captureCommand(t, func() int {
72
- return runBuild([]string{"--cwd", root, "--tsconfig", "tsconfig.json"})
73
- })
74
- if code != 2 || !strings.Contains(errText, "not-a-module-kind") {
75
- t.Fatalf("invalid config mismatch: code=%d stderr=%q", code, errText)
76
- }
69
+ code, _, errText = captureCommand(t, func() int {
70
+ return runBuild([]string{"--cwd", root, "--tsconfig", "tsconfig.json"})
71
+ })
72
+ if code != 2 || !strings.Contains(errText, "not-a-module-kind") {
73
+ t.Fatalf("invalid config mismatch: code=%d stderr=%q", code, errText)
74
+ }
77
75
 
78
- writeCommandProjectFile(t, root, "tsconfig.json", `{
76
+ writeCommandProjectFile(t, root, "tsconfig.json", `{
79
77
  "compilerOptions": { "module": "commonjs", "target": "es2020", "strict": true },
80
78
  "files": ["index.ts"]
81
79
  }
82
80
  `)
83
- writeCommandProjectFile(t, root, "index.ts", `export const value = 1;
81
+ writeCommandProjectFile(t, root, "index.ts", `export const value = 1;
84
82
  `)
85
- writeCommandProjectFile(t, root, "blocked", `not a directory`)
86
- code, _, errText = captureCommand(t, func() int {
87
- return runBuild([]string{"--cwd", root, "--tsconfig", "tsconfig.json", "--emit", "--outDir", "blocked"})
88
- })
89
- if code != 2 || !strings.Contains(errText, "not a directory") {
90
- t.Fatalf("emit failure mismatch: code=%d stderr=%q", code, errText)
91
- }
83
+ writeCommandProjectFile(t, root, "blocked", `not a directory`)
84
+ code, _, errText = captureCommand(t, func() int {
85
+ return runBuild([]string{"--cwd", root, "--tsconfig", "tsconfig.json", "--emit", "--outDir", "blocked"})
86
+ })
87
+ if code != 2 || !strings.Contains(errText, "not a directory") {
88
+ t.Fatalf("emit failure mismatch: code=%d stderr=%q", code, errText)
89
+ }
92
90
 
93
- resetCommandLinkedPluginRegistry()
94
- driver.RegisterPlugin(buildApplyErrorPlugin{})
95
- t.Cleanup(resetCommandLinkedPluginRegistry)
96
- t.Setenv(driver.LinkedPluginsEnv, `[{"name":"error","stage":"transform","config":{}}]`)
97
- code, _, errText = captureCommand(t, func() int {
98
- return runBuild([]string{"--cwd", root, "--tsconfig", "tsconfig.json", "--emit", "--outDir", "dist"})
99
- })
100
- if code != 3 || !strings.Contains(errText, "build apply boom") {
101
- t.Fatalf("linked apply failure mismatch: code=%d stderr=%q", code, errText)
102
- }
103
- resetCommandLinkedPluginRegistry()
104
- t.Setenv(driver.LinkedPluginsEnv, "")
91
+ resetCommandLinkedPluginRegistry()
92
+ driver.RegisterPlugin(buildApplyErrorPlugin{})
93
+ t.Cleanup(resetCommandLinkedPluginRegistry)
94
+ t.Setenv(driver.LinkedPluginsEnv, `[{"name":"error","stage":"transform","config":{}}]`)
95
+ code, _, errText = captureCommand(t, func() int {
96
+ return runBuild([]string{"--cwd", root, "--tsconfig", "tsconfig.json", "--emit", "--outDir", "dist"})
97
+ })
98
+ if code != 3 || !strings.Contains(errText, "build apply boom") {
99
+ t.Fatalf("linked apply failure mismatch: code=%d stderr=%q", code, errText)
100
+ }
101
+ resetCommandLinkedPluginRegistry()
102
+ t.Setenv(driver.LinkedPluginsEnv, "")
105
103
 
106
- code, _, errText = captureCommand(t, func() int {
107
- return runBuild([]string{
108
- "--cwd", root,
109
- "--tsconfig", "tsconfig.json",
110
- "--emit",
111
- "--outDir", "dist",
112
- "--manifest", filepath.Join(root, "index.ts", "manifest.json"),
113
- })
114
- })
115
- if code != 3 || !strings.Contains(errText, "manifest mkdir failed") {
116
- t.Fatalf("manifest mkdir mismatch: code=%d stderr=%q", code, errText)
117
- }
118
- code, _, errText = captureCommand(t, func() int {
119
- return runBuild([]string{
120
- "--cwd", root,
121
- "--tsconfig", "tsconfig.json",
122
- "--emit",
123
- "--outDir", "dist",
124
- "--manifest", root,
125
- })
126
- })
127
- if code != 3 || !strings.Contains(errText, "manifest write failed") {
128
- t.Fatalf("manifest write mismatch: code=%d stderr=%q", code, errText)
129
- }
104
+ code, _, errText = captureCommand(t, func() int {
105
+ return runBuild([]string{
106
+ "--cwd", root,
107
+ "--tsconfig", "tsconfig.json",
108
+ "--emit",
109
+ "--outDir", "dist",
110
+ "--manifest", filepath.Join(root, "index.ts", "manifest.json"),
111
+ })
112
+ })
113
+ if code != 3 || !strings.Contains(errText, "manifest mkdir failed") {
114
+ t.Fatalf("manifest mkdir mismatch: code=%d stderr=%q", code, errText)
115
+ }
116
+ code, _, errText = captureCommand(t, func() int {
117
+ return runBuild([]string{
118
+ "--cwd", root,
119
+ "--tsconfig", "tsconfig.json",
120
+ "--emit",
121
+ "--outDir", "dist",
122
+ "--manifest", root,
123
+ })
124
+ })
125
+ if code != 3 || !strings.Contains(errText, "manifest write failed") {
126
+ t.Fatalf("manifest write mismatch: code=%d stderr=%q", code, errText)
127
+ }
130
128
  }
@@ -1,47 +1,58 @@
1
1
  package main
2
2
 
3
3
  import (
4
- "bytes"
5
- "errors"
6
- "os"
7
- "path/filepath"
8
- "testing"
9
- _ "unsafe"
4
+ "bytes"
5
+ "errors"
6
+ "os"
7
+ "path/filepath"
8
+ "testing"
9
+ _ "unsafe"
10
10
  )
11
11
 
12
+ // driverPluginRegistry is a linkname alias for the unexported plugin slice in
13
+ // the driver package, allowing tests to reset registered plugins between cases.
14
+ //
12
15
  //go:linkname driverPluginRegistry github.com/samchon/ttsc/packages/ttsc/driver.pluginRegistry
13
16
  var driverPluginRegistry []any
14
17
 
18
+ // captureCommand redirects the package-level stdout/stderr/getwd seams around
19
+ // fn, then returns the exit code and the captured output strings.
15
20
  func captureCommand(t *testing.T, fn func() int) (int, string, string) {
16
- t.Helper()
17
- prevOut, prevErr, prevGetwd := stdout, stderr, getwd
18
- var out, err bytes.Buffer
19
- stdout = &out
20
- stderr = &err
21
- defer func() {
22
- stdout = prevOut
23
- stderr = prevErr
24
- getwd = prevGetwd
25
- }()
26
- code := fn()
27
- return code, out.String(), err.String()
21
+ t.Helper()
22
+ prevOut, prevErr, prevGetwd := stdout, stderr, getwd
23
+ var out, err bytes.Buffer
24
+ stdout = &out
25
+ stderr = &err
26
+ defer func() {
27
+ stdout = prevOut
28
+ stderr = prevErr
29
+ getwd = prevGetwd
30
+ }()
31
+ code := fn()
32
+ return code, out.String(), err.String()
28
33
  }
29
34
 
35
+ // failGetwd is a getwd stub that always returns an error, used to exercise the
36
+ // cwd-resolution failure path in command entrypoints.
30
37
  func failGetwd() (string, error) {
31
- return "", errors.New("cwd boom")
38
+ return "", errors.New("cwd boom")
32
39
  }
33
40
 
41
+ // writeCommandProjectFile writes contents to root/name, creating intermediate
42
+ // directories as needed. Slashes in name are normalised to the OS separator.
34
43
  func writeCommandProjectFile(t *testing.T, root, name, contents string) {
35
- t.Helper()
36
- file := filepath.Join(root, filepath.FromSlash(name))
37
- if err := os.MkdirAll(filepath.Dir(file), 0o755); err != nil {
38
- t.Fatal(err)
39
- }
40
- if err := os.WriteFile(file, []byte(contents), 0o644); err != nil {
41
- t.Fatal(err)
42
- }
44
+ t.Helper()
45
+ file := filepath.Join(root, filepath.FromSlash(name))
46
+ if err := os.MkdirAll(filepath.Dir(file), 0o755); err != nil {
47
+ t.Fatal(err)
48
+ }
49
+ if err := os.WriteFile(file, []byte(contents), 0o644); err != nil {
50
+ t.Fatal(err)
51
+ }
43
52
  }
44
53
 
54
+ // resetCommandLinkedPluginRegistry clears the driver plugin registry so that
55
+ // tests that register plugins do not pollute subsequent test cases.
45
56
  func resetCommandLinkedPluginRegistry() {
46
- driverPluginRegistry = nil
57
+ driverPluginRegistry = nil
47
58
  }