ttsc 0.11.0 → 0.12.1

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
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,111 +25,110 @@ 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 = os.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 := !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
+ }
83
83
 
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 !*quiet {
102
- fmt.Fprintf(stdout, "// ttsc: emitted=%d files\n", len(res.EmittedFiles))
103
- for _, f := range res.EmittedFiles {
104
- rel := f
105
- if abs, err := filepath.Rel(cwd, f); err == nil {
106
- rel = abs
107
- }
108
- fmt.Fprintln(stdout, " +", rel)
109
- }
110
- }
111
- if *manifestPath != "" {
112
- // The manifest is intentionally just the emitted file list. Higher
113
- // layers already know the project and tsconfig, and tests compare this
114
- // array as the build contract.
115
- data, err := json.Marshal(res.EmittedFiles)
116
- if err != nil {
117
- fmt.Fprintf(stderr, "ttsc: manifest marshal failed: %v\n", err)
118
- return 3
119
- }
120
- if err := os.MkdirAll(filepath.Dir(*manifestPath), 0o755); err != nil {
121
- fmt.Fprintf(stderr, "ttsc: manifest mkdir failed: %v\n", err)
122
- return 3
123
- }
124
- if err := os.WriteFile(*manifestPath, data, 0o644); err != nil {
125
- fmt.Fprintf(stderr, "ttsc: manifest write failed: %v\n", err)
126
- return 3
127
- }
128
- }
129
- }
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
+ }
130
129
 
131
- if !*quiet {
132
- fmt.Fprintf(stdout, "// ttsc: recognized=%d total=%d rewrites=%d\n", 0, 0, rewrites.Len())
133
- }
134
- return 0
130
+ if !*quiet {
131
+ fmt.Fprintf(stdout, "// ttsc: recognized=%d total=%d rewrites=%d\n", 0, 0, rewrites.Len())
132
+ }
133
+ return 0
135
134
  }
@@ -0,0 +1,130 @@
1
+ package main
2
+
3
+ import (
4
+ "errors"
5
+ "path/filepath"
6
+ "strings"
7
+ "testing"
8
+
9
+ "github.com/samchon/ttsc/packages/ttsc/driver"
10
+ )
11
+
12
+ type buildApplyErrorPlugin struct{}
13
+
14
+ func (buildApplyErrorPlugin) ApplyProgram(*driver.Program, driver.PluginContext) error {
15
+ return errors.New("build apply boom")
16
+ }
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
+ */
29
+ 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
+ }
50
+
51
+ writeCommandProjectFile(t, root, "tsconfig.json", `{
52
+ "compilerOptions": { "module": "commonjs", "target": "es2020", "strict": true },
53
+ "files": ["index.ts"]
54
+ }
55
+ `)
56
+ writeCommandProjectFile(t, root, "index.ts", `const value: number = "text";
57
+ export { value };
58
+ `)
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
+ }
65
+
66
+ writeCommandProjectFile(t, root, "tsconfig.json", `{
67
+ "compilerOptions": { "module": "not-a-module-kind", "target": "es2020" },
68
+ "files": ["index.ts"]
69
+ }
70
+ `)
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
+ }
77
+
78
+ writeCommandProjectFile(t, root, "tsconfig.json", `{
79
+ "compilerOptions": { "module": "commonjs", "target": "es2020", "strict": true },
80
+ "files": ["index.ts"]
81
+ }
82
+ `)
83
+ writeCommandProjectFile(t, root, "index.ts", `export const value = 1;
84
+ `)
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
+ }
92
+
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, "")
105
+
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
+ }
130
+ }
@@ -0,0 +1,47 @@
1
+ package main
2
+
3
+ import (
4
+ "bytes"
5
+ "errors"
6
+ "os"
7
+ "path/filepath"
8
+ "testing"
9
+ _ "unsafe"
10
+ )
11
+
12
+ //go:linkname driverPluginRegistry github.com/samchon/ttsc/packages/ttsc/driver.pluginRegistry
13
+ var driverPluginRegistry []any
14
+
15
+ 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()
28
+ }
29
+
30
+ func failGetwd() (string, error) {
31
+ return "", errors.New("cwd boom")
32
+ }
33
+
34
+ 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
+ }
43
+ }
44
+
45
+ func resetCommandLinkedPluginRegistry() {
46
+ driverPluginRegistry = nil
47
+ }
package/cmd/ttsc/main.go CHANGED
@@ -12,103 +12,104 @@
12
12
  package main
13
13
 
14
14
  import (
15
- "flag"
16
- "fmt"
17
- "io"
18
- "os"
19
- "runtime"
20
- "strings"
15
+ "flag"
16
+ "fmt"
17
+ "io"
18
+ "os"
19
+ "runtime"
20
+ "strings"
21
21
  )
22
22
 
23
23
  // These are overridden via `-ldflags "-X main.version=... -X main.commit=..."`
24
24
  // in CI. Sensible defaults keep local `go run ./cmd/ttsc` usable.
25
25
  var (
26
- version = "0.0.0-dev"
27
- commit = "dev"
28
- date = "unknown"
26
+ version = "0.0.0-dev"
27
+ commit = "dev"
28
+ date = "unknown"
29
29
  )
30
30
 
31
31
  // stdout / stderr are package-level to keep the CLI testable.
32
32
  var (
33
- stdout io.Writer = os.Stdout
34
- stderr io.Writer = os.Stderr
33
+ stdout io.Writer = os.Stdout
34
+ stderr io.Writer = os.Stderr
35
+ getwd = os.Getwd
35
36
  )
36
37
 
37
38
  func main() {
38
- os.Exit(run(os.Args[1:]))
39
+ os.Exit(run(os.Args[1:]))
39
40
  }
40
41
 
41
42
  func run(args []string) int {
42
- if len(args) == 0 {
43
- return runBuild(nil)
44
- }
45
-
46
- switch args[0] {
47
- case "-h", "--help", "help":
48
- printHelp(stdout)
49
- return 0
50
- case "-v", "--version", "version":
51
- printVersion(stdout)
52
- return 0
53
- case "demo":
54
- return runDemo(args[1:])
55
- case "build":
56
- return runBuild(args[1:])
57
- case "api-compile":
58
- return runAPICompile(args[1:])
59
- case "api-transform":
60
- return runAPITransform(args[1:])
61
- case "check":
62
- // `ttsc check` runs the analyze pipeline without emitting JS — useful
63
- // in CI and pre-commit checks that only need schema validation.
64
- return runBuild(append([]string{"--noEmit"}, args[1:]...))
65
- case "-p", "--project":
66
- if len(args) < 2 {
67
- fmt.Fprintln(stderr, "ttsc: -p/--project requires a path argument")
68
- return 2
69
- }
70
- return runBuild(append([]string{"--tsconfig=" + args[1]}, args[2:]...))
71
- default:
72
- if isBuildAlias(args[0]) {
73
- return runBuild(args)
74
- }
75
- fmt.Fprintf(stderr, "ttsc: unknown command %q\n", args[0])
76
- fmt.Fprintln(stderr, `ttsc: run "ttsc --help" to see supported commands`)
77
- return 2
78
- }
43
+ if len(args) == 0 {
44
+ return runBuild(nil)
45
+ }
46
+
47
+ switch args[0] {
48
+ case "-h", "--help", "help":
49
+ printHelp(stdout)
50
+ return 0
51
+ case "-v", "--version", "version":
52
+ printVersion(stdout)
53
+ return 0
54
+ case "demo":
55
+ return runDemo(args[1:])
56
+ case "build":
57
+ return runBuild(args[1:])
58
+ case "api-compile":
59
+ return runAPICompile(args[1:])
60
+ case "api-transform":
61
+ return runAPITransform(args[1:])
62
+ case "check":
63
+ // `ttsc check` runs the analyze pipeline without emitting JS — useful
64
+ // in CI and pre-commit checks that only need schema validation.
65
+ return runBuild(append([]string{"--noEmit"}, args[1:]...))
66
+ case "-p", "--project":
67
+ if len(args) < 2 {
68
+ fmt.Fprintln(stderr, "ttsc: -p/--project requires a path argument")
69
+ return 2
70
+ }
71
+ return runBuild(append([]string{"--tsconfig=" + args[1]}, args[2:]...))
72
+ default:
73
+ if isBuildAlias(args[0]) {
74
+ return runBuild(args)
75
+ }
76
+ fmt.Fprintf(stderr, "ttsc: unknown command %q\n", args[0])
77
+ fmt.Fprintln(stderr, `ttsc: run "ttsc --help" to see supported commands`)
78
+ return 2
79
+ }
79
80
  }
80
81
 
81
82
  func isBuildAlias(arg string) bool {
82
- if strings.HasPrefix(arg, "-") {
83
- return true
84
- }
85
- switch {
86
- case strings.HasSuffix(arg, ".json"),
87
- strings.HasSuffix(arg, ".ts"),
88
- strings.HasSuffix(arg, ".tsx"),
89
- strings.HasSuffix(arg, ".mts"),
90
- strings.HasSuffix(arg, ".cts"):
91
- return true
92
- default:
93
- return false
94
- }
83
+ if strings.HasPrefix(arg, "-") {
84
+ return true
85
+ }
86
+ switch {
87
+ case strings.HasSuffix(arg, ".json"),
88
+ strings.HasSuffix(arg, ".ts"),
89
+ strings.HasSuffix(arg, ".tsx"),
90
+ strings.HasSuffix(arg, ".mts"),
91
+ strings.HasSuffix(arg, ".cts"):
92
+ return true
93
+ default:
94
+ return false
95
+ }
95
96
  }
96
97
 
97
98
  func printVersion(w io.Writer) {
98
- fmt.Fprintf(
99
- w,
100
- "ttsc %s (commit %s, built %s, %s/%s, go %s)\n",
101
- version,
102
- commit,
103
- date,
104
- runtime.GOOS,
105
- runtime.GOARCH,
106
- runtime.Version(),
107
- )
99
+ fmt.Fprintf(
100
+ w,
101
+ "ttsc %s (commit %s, built %s, %s/%s, go %s)\n",
102
+ version,
103
+ commit,
104
+ date,
105
+ runtime.GOOS,
106
+ runtime.GOARCH,
107
+ runtime.Version(),
108
+ )
108
109
  }
109
110
 
110
111
  func printHelp(w io.Writer) {
111
- fmt.Fprintln(w, strings.TrimSpace(`
112
+ fmt.Fprintln(w, strings.TrimSpace(`
112
113
  ttsc — standalone typescript-go host.
113
114
 
114
115
  Usage:
@@ -154,37 +155,37 @@ Integration guide (bundlers):
154
155
  }
155
156
 
156
157
  func runDemo(args []string) int {
157
- fs := flag.NewFlagSet("demo", flag.ContinueOnError)
158
- fs.SetOutput(stderr)
159
- typ := fs.String("type", "string", "atomic type to simulate")
160
- if err := fs.Parse(args); err != nil {
161
- return 2
162
- }
163
-
164
- arrow, err := demoArrow(*typ)
165
- if err != nil {
166
- fmt.Fprintf(stderr, "ttsc demo: %v\n", err)
167
- return 2
168
- }
169
-
170
- fmt.Fprintf(stdout, "// demo<%s> -> emitted by ttsc %s\n", *typ, version)
171
- fmt.Fprintln(stdout, arrow)
172
- return 0
158
+ fs := flag.NewFlagSet("demo", flag.ContinueOnError)
159
+ fs.SetOutput(stderr)
160
+ typ := fs.String("type", "string", "atomic type to simulate")
161
+ if err := fs.Parse(args); err != nil {
162
+ return 2
163
+ }
164
+
165
+ arrow, err := demoArrow(*typ)
166
+ if err != nil {
167
+ fmt.Fprintf(stderr, "ttsc demo: %v\n", err)
168
+ return 2
169
+ }
170
+
171
+ fmt.Fprintf(stdout, "// demo<%s> -> emitted by ttsc %s\n", *typ, version)
172
+ fmt.Fprintln(stdout, arrow)
173
+ return 0
173
174
  }
174
175
 
175
176
  func demoArrow(name string) (string, error) {
176
- switch strings.ToLower(name) {
177
- case "any":
178
- return "(input) => true", nil
179
- case "boolean":
180
- return `(input) => "boolean" === typeof input`, nil
181
- case "number":
182
- return `(input) => "number" === typeof input`, nil
183
- case "bigint":
184
- return `(input) => "bigint" === typeof input`, nil
185
- case "string", "":
186
- return `(input) => "string" === typeof input`, nil
187
- default:
188
- return "", fmt.Errorf("unknown --type value %q (want: string|number|boolean|bigint|any)", name)
189
- }
177
+ switch strings.ToLower(name) {
178
+ case "any":
179
+ return "(input) => true", nil
180
+ case "boolean":
181
+ return `(input) => "boolean" === typeof input`, nil
182
+ case "number":
183
+ return `(input) => "number" === typeof input`, nil
184
+ case "bigint":
185
+ return `(input) => "bigint" === typeof input`, nil
186
+ case "string", "":
187
+ return `(input) => "string" === typeof input`, nil
188
+ default:
189
+ return "", fmt.Errorf("unknown --type value %q (want: string|number|boolean|bigint|any)", name)
190
+ }
190
191
  }