ttsc 0.12.3 → 0.13.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.
- package/cmd/platform/main.go +9 -0
- package/cmd/ttsc/api_compile.go +122 -101
- package/cmd/ttsc/api_compile_branches_test.go +55 -57
- package/cmd/ttsc/api_transform.go +67 -49
- package/cmd/ttsc/api_transform_branches_test.go +30 -32
- package/cmd/ttsc/build.go +141 -108
- package/cmd/ttsc/build_branches_test.go +95 -97
- package/cmd/ttsc/filter.go +70 -0
- package/cmd/ttsc/filter_strips_unknown_forwarded_flag_test.go +61 -0
- package/cmd/ttsc/flags_gen.go +30 -0
- package/cmd/ttsc/helpers_test.go +39 -28
- package/cmd/ttsc/main.go +111 -104
- package/cmd/ttsc/main_alias_branches_test.go +21 -23
- package/cmd/ttscserver/main.go +96 -90
- package/cmd/ttscserver/run_lsp_prefers_tsgo_flag_test.go +22 -22
- package/cmd/ttscserver/run_lsp_reports_server_error_test.go +22 -22
- package/cmd/ttscserver/run_lsp_uses_tsgo_environment_test.go +23 -23
- package/cmd/utility-host/main.go +30 -23
- package/driver/lsp.go +53 -7
- package/driver/plugins.go +88 -72
- package/driver/program.go +464 -340
- package/driver/rewrite.go +477 -380
- package/internal/lspserver/lsp_envelope.go +64 -64
- package/internal/lspserver/lsp_frame.go +60 -60
- package/internal/lspserver/lsp_plugin.go +57 -57
- package/internal/lspserver/lsp_proxy.go +265 -251
- package/internal/lspserver/lsp_server.go +155 -144
- package/lib/TtscCompiler.d.ts +7 -0
- package/lib/TtscCompiler.js +7 -0
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/buildNativeCompiler.d.ts +11 -1
- package/lib/compiler/internal/buildNativeCompiler.js +23 -3
- package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
- package/lib/compiler/internal/compileProjectInMemory.d.ts +9 -0
- package/lib/compiler/internal/compileProjectInMemory.js +36 -21
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/paths.d.ts +27 -0
- package/lib/compiler/internal/paths.js +70 -0
- package/lib/compiler/internal/paths.js.map +1 -0
- package/lib/compiler/internal/project/readProjectConfig.d.ts +9 -1
- package/lib/compiler/internal/project/readProjectConfig.js +80 -1
- package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
- package/lib/compiler/internal/project/resolveProjectConfig.d.ts +13 -1
- package/lib/compiler/internal/project/resolveProjectConfig.js +42 -1
- package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
- package/lib/compiler/internal/resolveBinary.d.ts +9 -1
- package/lib/compiler/internal/resolveBinary.js +17 -1
- package/lib/compiler/internal/resolveBinary.js.map +1 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.d.ts +16 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.js +42 -10
- package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
- package/lib/compiler/internal/resolveTsgo.d.ts +21 -1
- package/lib/compiler/internal/resolveTsgo.js +23 -1
- package/lib/compiler/internal/resolveTsgo.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +24 -4
- package/lib/compiler/internal/runBuild.js +355 -93
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/runSingleFileEmit.d.ts +10 -1
- package/lib/compiler/internal/runSingleFileEmit.js +14 -1
- package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
- package/lib/compiler/internal/sharedHostHelpers.d.ts +5 -0
- package/lib/compiler/internal/sharedHostHelpers.js +6 -2
- package/lib/compiler/internal/sharedHostHelpers.js.map +1 -1
- package/lib/compiler/internal/spawnNative.d.ts +21 -0
- package/lib/compiler/internal/spawnNative.js +59 -0
- package/lib/compiler/internal/spawnNative.js.map +1 -0
- package/lib/compiler/internal/transformProjectInMemory.d.ts +15 -1
- package/lib/compiler/internal/transformProjectInMemory.js +57 -61
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/flags/parser.d.ts +63 -0
- package/lib/flags/parser.js +254 -0
- package/lib/flags/parser.js.map +1 -0
- package/lib/flags/schema.d.ts +147 -0
- package/lib/flags/schema.js +405 -0
- package/lib/flags/schema.js.map +1 -0
- package/lib/launcher/internal/getCompilerVersionText.js +7 -0
- package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
- package/lib/launcher/internal/prepareExecution.js +35 -0
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/resolveCacheDir.d.ts +9 -0
- package/lib/launcher/internal/resolveCacheDir.js +22 -0
- package/lib/launcher/internal/resolveCacheDir.js.map +1 -0
- package/lib/launcher/internal/resolveTtscserverBinary.js +2 -0
- package/lib/launcher/internal/resolveTtscserverBinary.js.map +1 -1
- package/lib/launcher/internal/runTtsc.d.ts +10 -0
- package/lib/launcher/internal/runTtsc.js +90 -153
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtscserver.js +10 -35
- package/lib/launcher/internal/runTtscserver.js.map +1 -1
- package/lib/launcher/internal/runTtsx.d.ts +8 -0
- package/lib/launcher/internal/runTtsx.js +137 -79
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.d.ts +38 -0
- package/lib/plugin/internal/buildSourcePlugin.js +40 -17
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.d.ts +26 -0
- package/lib/plugin/internal/loadProjectPlugins.js +90 -41
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscCompilerTransformation.d.ts +15 -2
- package/lib/structures/ITtscPlugin.d.ts +24 -0
- package/lib/structures/ITtscPluginCapabilities.d.ts +29 -0
- package/lib/structures/ITtscPluginCapabilities.js +3 -0
- package/lib/structures/ITtscPluginCapabilities.js.map +1 -0
- package/lib/structures/index.d.ts +1 -0
- package/lib/structures/index.js +1 -0
- package/lib/structures/index.js.map +1 -1
- package/lib/structures/internal/ITtscLoadedNativePlugin.d.ts +5 -0
- package/lib/structures/internal/TtscCommonOptions.d.ts +19 -0
- package/lib/structures/internal/TtscSingleFileEmitOptions.d.ts +0 -2
- package/package.json +9 -9
- package/shim/ast/shim.go +24 -0
- package/shim/checker/shim.go +35 -0
- package/shim/core/shim.go +17 -0
- package/shim/diagnosticwriter/shim.go +4 -0
- package/shim/lsp/shim.go +3 -3
- package/shim/printer/shim.go +20 -0
- package/src/TtscCompiler.ts +7 -0
- package/src/compiler/internal/buildNativeCompiler.ts +23 -3
- package/src/compiler/internal/compileProjectInMemory.ts +34 -25
- package/src/compiler/internal/paths.ts +68 -0
- package/src/compiler/internal/project/readProjectConfig.ts +86 -1
- package/src/compiler/internal/project/resolveProjectConfig.ts +44 -1
- package/src/compiler/internal/resolveBinary.ts +17 -1
- package/src/compiler/internal/resolveEmittedJavaScript.ts +43 -12
- package/src/compiler/internal/resolveTsgo.ts +29 -1
- package/src/compiler/internal/runBuild.ts +398 -112
- package/src/compiler/internal/runSingleFileEmit.ts +14 -1
- package/src/compiler/internal/sharedHostHelpers.ts +7 -7
- package/src/compiler/internal/spawnNative.ts +64 -0
- package/src/compiler/internal/transformProjectInMemory.ts +47 -67
- package/src/flags/parser.ts +367 -0
- package/src/flags/schema.ts +570 -0
- package/src/launcher/internal/getCompilerVersionText.ts +7 -0
- package/src/launcher/internal/prepareExecution.ts +35 -0
- package/src/launcher/internal/resolveCacheDir.ts +19 -0
- package/src/launcher/internal/resolveTtscserverBinary.ts +2 -0
- package/src/launcher/internal/runTtsc.ts +97 -143
- package/src/launcher/internal/runTtscserver.ts +9 -1
- package/src/launcher/internal/runTtsx.ts +149 -82
- package/src/plugin/internal/buildSourcePlugin.ts +40 -14
- package/src/plugin/internal/loadProjectPlugins.ts +116 -45
- package/src/structures/ITtscCompilerTransformation.ts +15 -2
- package/src/structures/ITtscPlugin.ts +26 -1
- package/src/structures/ITtscPluginCapabilities.ts +29 -0
- package/src/structures/index.ts +1 -0
- package/src/structures/internal/ITtscLoadedNativePlugin.ts +5 -0
- package/src/structures/internal/TtscCommonOptions.ts +19 -0
- package/src/structures/internal/TtscSingleFileEmitOptions.ts +0 -2
- package/tools/gen_shims/main.go +43 -8
- package/utility/flags_gen.go +30 -0
- package/utility/host.go +304 -245
package/cmd/ttsc/build.go
CHANGED
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
package main
|
|
9
9
|
|
|
10
10
|
import (
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
"encoding/json"
|
|
12
|
+
"flag"
|
|
13
|
+
"fmt"
|
|
14
|
+
"os"
|
|
15
|
+
"path/filepath"
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
|
|
18
18
|
|
|
19
|
-
|
|
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,143 @@ 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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
+
singleThreaded := fs.Bool("singleThreaded", false, "run TypeScript-Go single-threaded")
|
|
39
|
+
checkers := fs.Int("checkers", 0, "type-checker pool size (0 = TypeScript-Go default)")
|
|
40
|
+
tsgoArgsRaw := fs.String("tsgo-args", "", "JSON array of forwarded tsgo CLI flags")
|
|
41
|
+
// Strip unknown forwarded tsgo options before fs.Parse so a flag like
|
|
42
|
+
// `--strict` (which tsgo accepts but cmd/ttsc's FlagSet does not declare)
|
|
43
|
+
// does not exit 2 before reaching the tsgo lane via `--tsgo-args=<JSON>`.
|
|
44
|
+
// See packages/ttsc/cmd/ttsc/filter.go for the allow-list source.
|
|
45
|
+
if err := fs.Parse(filterHostArgs(args)); err != nil {
|
|
46
|
+
return 2
|
|
47
|
+
}
|
|
48
|
+
tsgoArgs, err := decodeTsgoArgs(*tsgoArgsRaw)
|
|
49
|
+
if err != nil {
|
|
50
|
+
fmt.Fprintf(stderr, "ttsc: %v\n", err)
|
|
51
|
+
return 2
|
|
52
|
+
}
|
|
53
|
+
if *emit && *noEmit {
|
|
54
|
+
fmt.Fprintln(stderr, "ttsc: --emit and --noEmit are mutually exclusive")
|
|
55
|
+
return 2
|
|
56
|
+
}
|
|
57
|
+
if *verbose {
|
|
58
|
+
*quiet = false
|
|
59
|
+
}
|
|
48
60
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
cwd := *cwdOverride
|
|
62
|
+
if cwd == "" {
|
|
63
|
+
var err error
|
|
64
|
+
cwd, err = getwd()
|
|
65
|
+
if err != nil {
|
|
66
|
+
fmt.Fprintf(stderr, "ttsc: could not get working directory: %v\n", err)
|
|
67
|
+
return 2
|
|
68
|
+
}
|
|
69
|
+
}
|
|
58
70
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
prog, diags, err := driver.LoadProgram(cwd, *tsconfigPath, driver.LoadProgramOptions{
|
|
72
|
+
ForceEmit: *emit,
|
|
73
|
+
ForceNoEmit: *noEmit,
|
|
74
|
+
OutDir: *outDir,
|
|
75
|
+
SingleThreaded: *singleThreaded,
|
|
76
|
+
Checkers: *checkers,
|
|
77
|
+
TsgoArgs: tsgoArgs,
|
|
78
|
+
})
|
|
79
|
+
if err != nil {
|
|
80
|
+
fmt.Fprintf(stderr, "ttsc: %v\n", err)
|
|
81
|
+
return 2
|
|
82
|
+
}
|
|
83
|
+
if len(diags) > 0 {
|
|
84
|
+
driver.WritePrettyDiagnostics(stderr, diags, cwd)
|
|
85
|
+
return 2
|
|
86
|
+
}
|
|
87
|
+
defer prog.Close()
|
|
88
|
+
if diags := prog.Diagnostics(); len(diags) > 0 {
|
|
89
|
+
driver.WritePrettyDiagnostics(stderr, diags, cwd)
|
|
90
|
+
return 2
|
|
91
|
+
}
|
|
77
92
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
93
|
+
rewrites := driver.NewRewriteSet()
|
|
94
|
+
// shouldEmit reflects the resolved tsconfig noEmit flag. The flag lives
|
|
95
|
+
// three levels deep in the parsed config because TypeScript-Go mirrors the
|
|
96
|
+
// tsconfig object structure verbatim, with a tri-state bool per option.
|
|
97
|
+
shouldEmit := !prog.ParsedConfig.ParsedConfig.CompilerOptions.NoEmit.IsTrue()
|
|
98
|
+
if !*quiet {
|
|
99
|
+
fmt.Fprintf(stdout, "// ttsc: tsconfig=%s cwd=%s sites=%d emit=%v\n", *tsconfigPath, cwd, 0, shouldEmit)
|
|
100
|
+
}
|
|
83
101
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
102
|
+
if shouldEmit {
|
|
103
|
+
// Emit is callback-driven in TypeScript-Go. ttsc keeps that shape and
|
|
104
|
+
// wraps only the final WriteFile step so native rewrites and custom output
|
|
105
|
+
// capture share the same path.
|
|
106
|
+
writeFile := shimcompiler.WriteFile(
|
|
107
|
+
func(fileName, text string, _ *shimcompiler.WriteFileData) error {
|
|
108
|
+
return driver.DefaultWriteFile(fileName, text)
|
|
109
|
+
},
|
|
110
|
+
)
|
|
111
|
+
res, eDiags, err := prog.EmitAll(rewrites, writeFile)
|
|
112
|
+
if err != nil {
|
|
113
|
+
fmt.Fprintf(stderr, "ttsc: emit failed: %v\n", err)
|
|
114
|
+
return 3
|
|
115
|
+
}
|
|
116
|
+
for _, d := range eDiags {
|
|
117
|
+
fmt.Fprintln(stderr, " -", d.String())
|
|
118
|
+
}
|
|
119
|
+
if driver.CountErrors(eDiags) > 0 {
|
|
120
|
+
return 2
|
|
121
|
+
}
|
|
122
|
+
if !*quiet {
|
|
123
|
+
fmt.Fprintf(stdout, "// ttsc: emitted=%d files\n", len(res.EmittedFiles))
|
|
124
|
+
for _, f := range res.EmittedFiles {
|
|
125
|
+
rel := f
|
|
126
|
+
if abs, err := filepath.Rel(cwd, f); err == nil {
|
|
127
|
+
rel = abs
|
|
128
|
+
}
|
|
129
|
+
fmt.Fprintln(stdout, " +", rel)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if *manifestPath != "" {
|
|
133
|
+
// The manifest is intentionally just the emitted file list. Higher
|
|
134
|
+
// layers already know the project and tsconfig, and tests compare this
|
|
135
|
+
// array as the build contract.
|
|
136
|
+
data, _ := json.Marshal(res.EmittedFiles)
|
|
137
|
+
if err := os.MkdirAll(filepath.Dir(*manifestPath), 0o755); err != nil {
|
|
138
|
+
fmt.Fprintf(stderr, "ttsc: manifest mkdir failed: %v\n", err)
|
|
139
|
+
return 3
|
|
140
|
+
}
|
|
141
|
+
if err := os.WriteFile(*manifestPath, data, 0o644); err != nil {
|
|
142
|
+
fmt.Fprintf(stderr, "ttsc: manifest write failed: %v\n", err)
|
|
143
|
+
return 3
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
129
147
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
148
|
+
if !*quiet {
|
|
149
|
+
fmt.Fprintf(stdout, "// ttsc: recognized=%d total=%d rewrites=%d\n", 0, 0, rewrites.Len())
|
|
150
|
+
}
|
|
151
|
+
return 0
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// decodeTsgoArgs decodes the JSON-array value of the `--tsgo-args` flag — the
|
|
155
|
+
// tsgo CLI flags the `ttsc` launcher forwarded — into a string slice. An empty
|
|
156
|
+
// flag yields a nil slice. Shared by the build / api-compile / api-transform
|
|
157
|
+
// subcommands, which all hand the result to driver.LoadProgram.
|
|
158
|
+
func decodeTsgoArgs(raw string) ([]string, error) {
|
|
159
|
+
if raw == "" {
|
|
160
|
+
return nil, nil
|
|
161
|
+
}
|
|
162
|
+
var args []string
|
|
163
|
+
if err := json.Unmarshal([]byte(raw), &args); err != nil {
|
|
164
|
+
return nil, fmt.Errorf("invalid --tsgo-args: %w", err)
|
|
165
|
+
}
|
|
166
|
+
return args, nil
|
|
134
167
|
}
|
|
@@ -1,130 +1,128 @@
|
|
|
1
1
|
package main
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
"errors"
|
|
5
|
+
"path/filepath"
|
|
6
|
+
"strings"
|
|
7
|
+
"testing"
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
|
|
15
|
+
return errors.New("build apply boom")
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
54
|
+
writeCommandProjectFile(t, root, "index.ts", `const value: number = "text";
|
|
57
55
|
export { value };
|
|
58
56
|
`)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
|
|
81
|
+
writeCommandProjectFile(t, root, "index.ts", `export const value = 1;
|
|
84
82
|
`)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Argument filtering for cmd/ttsc subcommand parsers.
|
|
2
|
+
//
|
|
3
|
+
// `flag.NewFlagSet(..., flag.ContinueOnError).Parse` exits with non-zero on
|
|
4
|
+
// the first unknown flag it sees, which is exactly the failure mode RCA
|
|
5
|
+
// #4 in `.discussions/cli-parser-rca/report.md` flagged: a forwarded tsgo
|
|
6
|
+
// option from the JS launcher would crash the build before tsgo's own
|
|
7
|
+
// option table got a chance to consume it. Mirror the strategy that
|
|
8
|
+
// `packages/ttsc/utility/host.go` already uses for the utility host —
|
|
9
|
+
// strip flags that the local FlagSet does not declare, swallowing the
|
|
10
|
+
// next token when the unknown flag clearly takes one. The allow-list is
|
|
11
|
+
// the same generated `HostFlagAllowList` shared by both consumers
|
|
12
|
+
// (`packages/ttsc/cmd/ttsc/flags_gen.go` and
|
|
13
|
+
// `packages/ttsc/utility/flags_gen.go`).
|
|
14
|
+
|
|
15
|
+
package main
|
|
16
|
+
|
|
17
|
+
import "strings"
|
|
18
|
+
|
|
19
|
+
// filterHostArgs strips flags that the cmd/ttsc FlagSet does not declare,
|
|
20
|
+
// so a forwarded tsgo option from the JS launcher (e.g. `--strict`) does
|
|
21
|
+
// not make `fs.Parse` exit 2 before the build can hand it to tsgo via
|
|
22
|
+
// `--tsgo-args=<JSON>`. Flags absent from `HostFlagAllowList` are
|
|
23
|
+
// dropped together with their value token when they clearly take one
|
|
24
|
+
// (no inline `=` and the next token does not start with `-`).
|
|
25
|
+
//
|
|
26
|
+
// The allow-list itself is generated from
|
|
27
|
+
// `packages/ttsc/src/flags/schema.ts` (see `flags_gen.go`); editing it
|
|
28
|
+
// means editing the schema and re-running `pnpm format`, not patching
|
|
29
|
+
// this file.
|
|
30
|
+
func filterHostArgs(args []string) []string {
|
|
31
|
+
filtered := make([]string, 0, len(args))
|
|
32
|
+
for i := 0; i < len(args); i++ {
|
|
33
|
+
current := args[i]
|
|
34
|
+
if current == "--" {
|
|
35
|
+
// Forward `--` and everything after verbatim. The Go flag parser
|
|
36
|
+
// treats `--` as "end of options" and stops there; mirroring that
|
|
37
|
+
// behavior here keeps trailing positional tokens intact.
|
|
38
|
+
filtered = append(filtered, args[i:]...)
|
|
39
|
+
break
|
|
40
|
+
}
|
|
41
|
+
if !strings.HasPrefix(current, "--") {
|
|
42
|
+
filtered = append(filtered, current)
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
45
|
+
name, hasInlineValue := splitFlagName(current)
|
|
46
|
+
takesValue, ok := HostFlagAllowList[name]
|
|
47
|
+
if ok {
|
|
48
|
+
filtered = append(filtered, current)
|
|
49
|
+
if takesValue && !hasInlineValue && i+1 < len(args) {
|
|
50
|
+
i++
|
|
51
|
+
filtered = append(filtered, args[i])
|
|
52
|
+
}
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
if !hasInlineValue && i+1 < len(args) && !strings.HasPrefix(args[i+1], "-") {
|
|
56
|
+
i++
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return filtered
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// splitFlagName strips the leading `--` from a flag argument and reports
|
|
63
|
+
// whether the argument carries an inline value (`--foo=value`).
|
|
64
|
+
func splitFlagName(arg string) (string, bool) {
|
|
65
|
+
name := strings.TrimPrefix(arg, "--")
|
|
66
|
+
if i := strings.IndexByte(name, '='); i != -1 {
|
|
67
|
+
return name[:i], true
|
|
68
|
+
}
|
|
69
|
+
return name, false
|
|
70
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
package main
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"reflect"
|
|
5
|
+
"testing"
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
// TestFilterHostArgsStripsUnknownForwardedFlag verifies cmd/ttsc's
|
|
9
|
+
// filterHostArgs drops forwarded tsgo options before fs.Parse so the
|
|
10
|
+
// FlagSet does not exit 2.
|
|
11
|
+
//
|
|
12
|
+
// `cmd/ttsc/{build,api_compile,api_transform}.go` use
|
|
13
|
+
// `flag.NewFlagSet(..., flag.ContinueOnError)` which exits non-zero on
|
|
14
|
+
// the first unknown flag it sees. The schema-generated allow-list in
|
|
15
|
+
// `flags_gen.go` (`HostFlagAllowList`) feeds this filter so a forwarded
|
|
16
|
+
// `--strict` from the JS launcher reaches the tsgo lane via
|
|
17
|
+
// `--tsgo-args=<JSON>` instead of crashing the build. This pins the
|
|
18
|
+
// behavior RCA #4 flagged as "next likely bug — `cmd/ttsc/build.go:42`
|
|
19
|
+
// uses bare `flag.FlagSet` with no filter and will exit".
|
|
20
|
+
//
|
|
21
|
+
// 1. Known flags + values survive (long form and `=` form).
|
|
22
|
+
// 2. Unknown flag with separate value (`--strict true`) is dropped
|
|
23
|
+
// together with its value.
|
|
24
|
+
// 3. Unknown flag with inline value (`--target=ES2022`) is dropped.
|
|
25
|
+
// 4. `--` separator and everything after survives verbatim.
|
|
26
|
+
func TestFilterHostArgsStripsUnknownForwardedFlag(t *testing.T) {
|
|
27
|
+
cases := []struct {
|
|
28
|
+
name string
|
|
29
|
+
in []string
|
|
30
|
+
want []string
|
|
31
|
+
}{
|
|
32
|
+
{
|
|
33
|
+
name: "keeps known flags including values",
|
|
34
|
+
in: []string{"--tsconfig", "tsconfig.json", "--cwd=.", "--emit"},
|
|
35
|
+
want: []string{"--tsconfig", "tsconfig.json", "--cwd=.", "--emit"},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: "drops unknown flag with separate value",
|
|
39
|
+
in: []string{"--tsconfig", "tsconfig.json", "--strict", "true", "--emit"},
|
|
40
|
+
want: []string{"--tsconfig", "tsconfig.json", "--emit"},
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "drops unknown flag with inline value",
|
|
44
|
+
in: []string{"--tsconfig=tsconfig.json", "--target=ES2022"},
|
|
45
|
+
want: []string{"--tsconfig=tsconfig.json"},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "double dash preserves trailing tokens",
|
|
49
|
+
in: []string{"--tsconfig=tsconfig.json", "--", "--anything", "src/main.ts"},
|
|
50
|
+
want: []string{"--tsconfig=tsconfig.json", "--", "--anything", "src/main.ts"},
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
for _, tc := range cases {
|
|
54
|
+
t.Run(tc.name, func(t *testing.T) {
|
|
55
|
+
got := filterHostArgs(tc.in)
|
|
56
|
+
if !reflect.DeepEqual(got, tc.want) {
|
|
57
|
+
t.Fatalf("filterHostArgs(%v):\n want %v\n got %v", tc.in, tc.want, got)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
}
|