ttsc 0.8.1 → 0.10.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/README.md +39 -68
- package/cmd/ttsc/api_compile.go +109 -115
- package/cmd/ttsc/api_transform.go +54 -51
- package/cmd/ttsc/main.go +103 -121
- package/driver/program.go +30 -2
- package/driver/rewrite.go +126 -9
- package/go.mod +2 -0
- package/lib/TtscCompiler.js +28 -46
- package/lib/TtscCompiler.js.map +1 -1
- package/lib/compiler/internal/buildNativeCompiler.js +8 -38
- package/lib/compiler/internal/buildNativeCompiler.js.map +1 -1
- package/lib/compiler/internal/compileProjectInMemory.js +36 -63
- package/lib/compiler/internal/compileProjectInMemory.js.map +1 -1
- package/lib/compiler/internal/project/readProjectConfig.js +108 -60
- package/lib/compiler/internal/project/readProjectConfig.js.map +1 -1
- package/lib/compiler/internal/project/resolveProjectConfig.js +15 -45
- package/lib/compiler/internal/project/resolveProjectConfig.js.map +1 -1
- package/lib/compiler/internal/resolveBinary.js +10 -40
- package/lib/compiler/internal/resolveBinary.js.map +1 -1
- package/lib/compiler/internal/resolveEmittedJavaScript.js +21 -48
- package/lib/compiler/internal/resolveEmittedJavaScript.js.map +1 -1
- package/lib/compiler/internal/resolveTsgo.js +14 -44
- package/lib/compiler/internal/resolveTsgo.js.map +1 -1
- package/lib/compiler/internal/runBuild.d.ts +7 -0
- package/lib/compiler/internal/runBuild.js +47 -66
- package/lib/compiler/internal/runBuild.js.map +1 -1
- package/lib/compiler/internal/runSingleFileEmit.js +18 -48
- package/lib/compiler/internal/runSingleFileEmit.js.map +1 -1
- package/lib/compiler/internal/transformProjectInMemory.js +41 -75
- package/lib/compiler/internal/transformProjectInMemory.js.map +1 -1
- package/lib/launcher/internal/getCompilerVersionText.js +7 -37
- package/lib/launcher/internal/getCompilerVersionText.js.map +1 -1
- package/lib/launcher/internal/prepareExecution.js +48 -69
- package/lib/launcher/internal/prepareExecution.js.map +1 -1
- package/lib/launcher/internal/runTtsc.js +53 -64
- package/lib/launcher/internal/runTtsc.js.map +1 -1
- package/lib/launcher/internal/runTtsx.js +328 -57
- package/lib/launcher/internal/runTtsx.js.map +1 -1
- package/lib/plugin/internal/buildSourcePlugin.d.ts +8 -0
- package/lib/plugin/internal/buildSourcePlugin.js +287 -139
- package/lib/plugin/internal/buildSourcePlugin.js.map +1 -1
- package/lib/plugin/internal/loadProjectPlugins.d.ts +2 -0
- package/lib/plugin/internal/loadProjectPlugins.js +197 -9
- package/lib/plugin/internal/loadProjectPlugins.js.map +1 -1
- package/lib/structures/ITtscCompilerContext.d.ts +16 -3
- package/lib/structures/ITtscCompilerResult.d.ts +4 -0
- package/lib/structures/ITtscCompilerTransformation.d.ts +2 -0
- package/lib/structures/ITtscPluginFactoryContext.d.ts +6 -3
- package/lib/structures/ITtscProjectPluginConfig.d.ts +8 -7
- package/lib/structures/internal/ITtscParsedProjectConfig.d.ts +2 -0
- package/lib/structures/internal/ITtscProjectLocatorOptions.d.ts +2 -0
- package/lib/structures/internal/TtscCommonOptions.d.ts +2 -0
- package/package.json +9 -14
- package/shim/diagnosticwriter/lint.go +8 -0
- package/shim/printer/go.mod +5 -0
- package/shim/printer/shim.go +23 -0
- package/src/TtscCompiler.ts +20 -5
- package/src/compiler/internal/buildNativeCompiler.ts +2 -2
- package/src/compiler/internal/compileProjectInMemory.ts +21 -16
- package/src/compiler/internal/project/readProjectConfig.ts +134 -35
- package/src/compiler/internal/project/resolveProjectConfig.ts +2 -2
- package/src/compiler/internal/resolveBinary.ts +2 -2
- package/src/compiler/internal/resolveEmittedJavaScript.ts +11 -7
- package/src/compiler/internal/resolveTsgo.ts +2 -2
- package/src/compiler/internal/runBuild.ts +34 -21
- package/src/compiler/internal/runSingleFileEmit.ts +3 -3
- package/src/compiler/internal/transformProjectInMemory.ts +37 -37
- package/src/launcher/internal/getCompilerVersionText.ts +2 -2
- package/src/launcher/internal/prepareExecution.ts +14 -4
- package/src/launcher/internal/runTtsc.ts +30 -4
- package/src/launcher/internal/runTtsx.ts +377 -26
- package/src/plugin/internal/buildSourcePlugin.ts +321 -50
- package/src/plugin/internal/loadProjectPlugins.ts +266 -10
- package/src/structures/ITtscCompilerContext.ts +17 -3
- package/src/structures/ITtscCompilerResult.ts +5 -0
- package/src/structures/ITtscCompilerTransformation.ts +3 -0
- package/src/structures/ITtscPluginFactoryContext.ts +6 -3
- package/src/structures/ITtscProjectPluginConfig.ts +8 -7
- package/src/structures/internal/ITtscParsedProjectConfig.ts +2 -0
- package/src/structures/internal/ITtscProjectLocatorOptions.ts +2 -0
- package/src/structures/internal/TtscCommonOptions.ts +2 -0
- package/utility/host.go +622 -20
- package/cmd/ttsc/main_test.go +0 -288
- package/test/compiler.test.ts +0 -422
- package/test/platform.test.ts +0 -12
- package/test/project.test.ts +0 -197
- package/test/source-build.test.ts +0 -45
- package/test/tsgo.test.ts +0 -69
package/utility/host.go
CHANGED
|
@@ -5,6 +5,7 @@ import (
|
|
|
5
5
|
"flag"
|
|
6
6
|
"fmt"
|
|
7
7
|
"os"
|
|
8
|
+
"os/exec"
|
|
8
9
|
"path/filepath"
|
|
9
10
|
"sort"
|
|
10
11
|
"strings"
|
|
@@ -12,6 +13,7 @@ import (
|
|
|
12
13
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
13
14
|
shimcompiler "github.com/microsoft/typescript-go/shim/compiler"
|
|
14
15
|
shimcore "github.com/microsoft/typescript-go/shim/core"
|
|
16
|
+
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
15
17
|
|
|
16
18
|
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
17
19
|
)
|
|
@@ -43,6 +45,22 @@ type transformResult struct {
|
|
|
43
45
|
TypeScript map[string]string `json:"typescript"`
|
|
44
46
|
}
|
|
45
47
|
|
|
48
|
+
// RunCheck validates the project and first-party utility plugin configuration
|
|
49
|
+
// without emitting output.
|
|
50
|
+
func RunCheck(args []string) int {
|
|
51
|
+
opts, ok := parseHostOptions("check", args)
|
|
52
|
+
if !ok {
|
|
53
|
+
return 2
|
|
54
|
+
}
|
|
55
|
+
opts.noEmit = true
|
|
56
|
+
prog, _, _, _, ok := loadUtilityProgram(opts)
|
|
57
|
+
if !ok {
|
|
58
|
+
return 2
|
|
59
|
+
}
|
|
60
|
+
defer prog.Close()
|
|
61
|
+
return 0
|
|
62
|
+
}
|
|
63
|
+
|
|
46
64
|
// RunBuild hosts first-party utility transform plugins inside one compiler emit.
|
|
47
65
|
func RunBuild(args []string) int {
|
|
48
66
|
opts, ok := parseHostOptions("build", args)
|
|
@@ -76,6 +94,9 @@ func RunBuild(args []string) int {
|
|
|
76
94
|
for _, d := range eDiags {
|
|
77
95
|
fmt.Fprintln(os.Stderr, " -", d.String())
|
|
78
96
|
}
|
|
97
|
+
if driver.CountErrors(eDiags) > 0 {
|
|
98
|
+
return 2
|
|
99
|
+
}
|
|
79
100
|
if res != nil && !opts.quiet {
|
|
80
101
|
fmt.Fprintf(os.Stdout, "// ttsc utility: emitted=%d files\n", len(res.EmittedFiles))
|
|
81
102
|
}
|
|
@@ -88,14 +109,19 @@ func RunTransform(args []string) int {
|
|
|
88
109
|
if !ok {
|
|
89
110
|
return 2
|
|
90
111
|
}
|
|
91
|
-
prog, _, _,
|
|
112
|
+
prog, _, _, sourcePreamble, ok := loadUtilityProgram(opts)
|
|
92
113
|
if !ok {
|
|
93
114
|
return 2
|
|
94
115
|
}
|
|
95
116
|
defer prog.Close()
|
|
117
|
+
printer := shimprinter.NewPrinter(shimprinter.PrinterOptions{}, shimprinter.PrintHandlers{}, nil)
|
|
96
118
|
out := transformResult{TypeScript: map[string]string{}}
|
|
97
119
|
for _, file := range prog.SourceFiles() {
|
|
98
|
-
|
|
120
|
+
text := shimprinter.EmitSourceFile(printer, file)
|
|
121
|
+
if sourcePreamble != "" && !shouldRemoveComments(prog) && !strings.Contains(text, sourcePreamble) {
|
|
122
|
+
text = driver.ApplySourcePreamble(text, sourcePreamble)
|
|
123
|
+
}
|
|
124
|
+
out.TypeScript[apiOutputKey(opts.cwd, file.FileName())] = text
|
|
99
125
|
}
|
|
100
126
|
data, err := json.Marshal(out)
|
|
101
127
|
if err != nil {
|
|
@@ -117,7 +143,11 @@ func parseHostOptions(command string, args []string) (hostOptions, bool) {
|
|
|
117
143
|
quiet := fs.Bool("quiet", true, "suppress summary")
|
|
118
144
|
tsconfig := fs.String("tsconfig", "tsconfig.json", "project tsconfig")
|
|
119
145
|
verbose := fs.Bool("verbose", false, "print summary")
|
|
120
|
-
if err := fs.Parse(args); err != nil {
|
|
146
|
+
if err := fs.Parse(filterHostArgs(args)); err != nil {
|
|
147
|
+
return hostOptions{}, false
|
|
148
|
+
}
|
|
149
|
+
if *emit && *noEmit {
|
|
150
|
+
fmt.Fprintln(os.Stderr, "ttsc utility: --emit and --noEmit are mutually exclusive")
|
|
121
151
|
return hostOptions{}, false
|
|
122
152
|
}
|
|
123
153
|
resolvedCwd := *cwd
|
|
@@ -149,13 +179,57 @@ func parseHostOptions(command string, args []string) (hostOptions, bool) {
|
|
|
149
179
|
}, true
|
|
150
180
|
}
|
|
151
181
|
|
|
182
|
+
func filterHostArgs(args []string) []string {
|
|
183
|
+
known := map[string]bool{
|
|
184
|
+
"cwd": true,
|
|
185
|
+
"emit": false,
|
|
186
|
+
"noEmit": false,
|
|
187
|
+
"outDir": true,
|
|
188
|
+
"plugins-json": true,
|
|
189
|
+
"quiet": false,
|
|
190
|
+
"tsconfig": true,
|
|
191
|
+
"verbose": false,
|
|
192
|
+
}
|
|
193
|
+
filtered := make([]string, 0, len(args))
|
|
194
|
+
for i := 0; i < len(args); i++ {
|
|
195
|
+
current := args[i]
|
|
196
|
+
if current == "--" {
|
|
197
|
+
break
|
|
198
|
+
}
|
|
199
|
+
if !strings.HasPrefix(current, "--") {
|
|
200
|
+
filtered = append(filtered, current)
|
|
201
|
+
continue
|
|
202
|
+
}
|
|
203
|
+
name, hasInlineValue := flagName(current)
|
|
204
|
+
takesValue, ok := known[name]
|
|
205
|
+
if ok {
|
|
206
|
+
filtered = append(filtered, current)
|
|
207
|
+
if takesValue && !hasInlineValue && i+1 < len(args) {
|
|
208
|
+
i++
|
|
209
|
+
filtered = append(filtered, args[i])
|
|
210
|
+
}
|
|
211
|
+
continue
|
|
212
|
+
}
|
|
213
|
+
if !hasInlineValue && i+1 < len(args) && !strings.HasPrefix(args[i+1], "-") {
|
|
214
|
+
i++
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return filtered
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
func flagName(arg string) (string, bool) {
|
|
221
|
+
name := strings.TrimPrefix(arg, "--")
|
|
222
|
+
before, _, found := strings.Cut(name, "=")
|
|
223
|
+
return before, found
|
|
224
|
+
}
|
|
225
|
+
|
|
152
226
|
func loadUtilityProgram(opts hostOptions) (*driver.Program, []pluginEntry, transformState, string, bool) {
|
|
153
227
|
entries, err := parsePluginEntries(opts.pluginsJSON)
|
|
154
228
|
if err != nil {
|
|
155
229
|
fmt.Fprintln(os.Stderr, err)
|
|
156
230
|
return nil, nil, transformState{}, "", false
|
|
157
231
|
}
|
|
158
|
-
sourcePreamble, err := prepareSourcePreamble(entries)
|
|
232
|
+
sourcePreamble, err := prepareSourcePreamble(entries, opts.cwd, opts.tsconfig)
|
|
159
233
|
if err != nil {
|
|
160
234
|
fmt.Fprintln(os.Stderr, err)
|
|
161
235
|
return nil, nil, transformState{}, "", false
|
|
@@ -172,19 +246,25 @@ func loadUtilityProgram(opts hostOptions) (*driver.Program, []pluginEntry, trans
|
|
|
172
246
|
}
|
|
173
247
|
if len(diags) > 0 {
|
|
174
248
|
driver.WritePrettyDiagnostics(os.Stderr, diags, opts.cwd)
|
|
249
|
+
if prog != nil {
|
|
250
|
+
_ = prog.Close()
|
|
251
|
+
}
|
|
175
252
|
return nil, nil, transformState{}, "", false
|
|
176
253
|
}
|
|
177
254
|
if diags := prog.Diagnostics(); len(diags) > 0 {
|
|
178
255
|
driver.WritePrettyDiagnostics(os.Stderr, diags, opts.cwd)
|
|
256
|
+
_ = prog.Close()
|
|
179
257
|
return nil, nil, transformState{}, "", false
|
|
180
258
|
}
|
|
181
259
|
state, err := prepareTransforms(prog, entries)
|
|
182
260
|
if err != nil {
|
|
183
261
|
fmt.Fprintln(os.Stderr, err)
|
|
262
|
+
_ = prog.Close()
|
|
184
263
|
return nil, nil, transformState{}, "", false
|
|
185
264
|
}
|
|
186
265
|
if err := applySourceTransforms(prog, state); err != nil {
|
|
187
266
|
fmt.Fprintln(os.Stderr, err)
|
|
267
|
+
_ = prog.Close()
|
|
188
268
|
return nil, nil, transformState{}, "", false
|
|
189
269
|
}
|
|
190
270
|
return prog, entries, state, sourcePreamble, true
|
|
@@ -218,13 +298,13 @@ func prepareTransforms(prog *driver.Program, entries []pluginEntry) (transformSt
|
|
|
218
298
|
return state, nil
|
|
219
299
|
}
|
|
220
300
|
|
|
221
|
-
func prepareSourcePreamble(entries []pluginEntry) (string, error) {
|
|
301
|
+
func prepareSourcePreamble(entries []pluginEntry, cwd, tsconfigPath string) (string, error) {
|
|
222
302
|
var preamble strings.Builder
|
|
223
303
|
for _, entry := range entries {
|
|
224
304
|
if entry.Name != "@ttsc/banner" {
|
|
225
305
|
continue
|
|
226
306
|
}
|
|
227
|
-
banner, err := parseBanner(entry.Config)
|
|
307
|
+
banner, err := parseBanner(entry.Config, cwd, tsconfigPath)
|
|
228
308
|
if err != nil {
|
|
229
309
|
return "", err
|
|
230
310
|
}
|
|
@@ -239,7 +319,7 @@ func makeSourcePreambleWriteFile(prog *driver.Program, sourcePreamble string) sh
|
|
|
239
319
|
}
|
|
240
320
|
return func(fileName, text string, _ *shimcompiler.WriteFileData) error {
|
|
241
321
|
if shouldEnsureSourcePreamble(fileName, text, sourcePreamble) {
|
|
242
|
-
text = sourcePreamble
|
|
322
|
+
text = driver.ApplySourcePreamble(text, sourcePreamble)
|
|
243
323
|
}
|
|
244
324
|
return driver.DefaultWriteFile(fileName, text)
|
|
245
325
|
}
|
|
@@ -280,20 +360,20 @@ func applySourceTransforms(prog *driver.Program, state transformState) error {
|
|
|
280
360
|
|
|
281
361
|
func apiOutputKey(cwd, fileName string) string {
|
|
282
362
|
rel, err := filepath.Rel(cwd, fileName)
|
|
283
|
-
if err != nil ||
|
|
363
|
+
if err != nil || isOutsideRelativePath(rel) {
|
|
284
364
|
return filepath.ToSlash(fileName)
|
|
285
365
|
}
|
|
286
366
|
return filepath.ToSlash(rel)
|
|
287
367
|
}
|
|
288
368
|
|
|
289
|
-
func
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
text,
|
|
295
|
-
if
|
|
296
|
-
return "",
|
|
369
|
+
func isOutsideRelativePath(rel string) bool {
|
|
370
|
+
return rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator))
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
func parseBanner(config map[string]any, cwd, tsconfigPath string) (string, error) {
|
|
374
|
+
text, err := resolveBannerText(config, cwd, tsconfigPath)
|
|
375
|
+
if err != nil {
|
|
376
|
+
return "", err
|
|
297
377
|
}
|
|
298
378
|
lines := strings.Split(strings.ReplaceAll(text, "\r\n", "\n"), "\n")
|
|
299
379
|
for len(lines) > 0 && strings.TrimSpace(lines[len(lines)-1]) == "" {
|
|
@@ -316,6 +396,437 @@ func parseBanner(config map[string]any) (string, error) {
|
|
|
316
396
|
return b.String(), nil
|
|
317
397
|
}
|
|
318
398
|
|
|
399
|
+
func resolveBannerText(config map[string]any, cwd, tsconfigPath string) (string, error) {
|
|
400
|
+
if text, ok, err := bannerTextFromConfigValue(config["text"], `"text"`); ok || err != nil {
|
|
401
|
+
return text, err
|
|
402
|
+
}
|
|
403
|
+
if rawConfigPath, ok := config["config"]; ok {
|
|
404
|
+
configPath, ok := rawConfigPath.(string)
|
|
405
|
+
if !ok || strings.TrimSpace(configPath) == "" {
|
|
406
|
+
return "", fmt.Errorf("@ttsc/banner: \"config\" must be a non-empty string path")
|
|
407
|
+
}
|
|
408
|
+
location := resolveBannerConfigPath(configPath, cwd, tsconfigPath)
|
|
409
|
+
raw, err := loadBannerConfigFile(location)
|
|
410
|
+
if err != nil {
|
|
411
|
+
return "", err
|
|
412
|
+
}
|
|
413
|
+
text, ok, err := bannerTextFromConfigValue(raw, filepath.Base(location))
|
|
414
|
+
if err != nil {
|
|
415
|
+
return "", err
|
|
416
|
+
}
|
|
417
|
+
if !ok {
|
|
418
|
+
return "", fmt.Errorf("@ttsc/banner: %s must export a non-empty string or an object with a non-empty \"text\" string", location)
|
|
419
|
+
}
|
|
420
|
+
return text, nil
|
|
421
|
+
}
|
|
422
|
+
location, err := findBannerConfigFile(cwd, tsconfigPath)
|
|
423
|
+
if err != nil {
|
|
424
|
+
return "", err
|
|
425
|
+
}
|
|
426
|
+
if location == "" {
|
|
427
|
+
return "", fmt.Errorf("@ttsc/banner: \"text\" must be a non-empty string or a banner.config.{js,cjs,mjs,ts,mts,cts} file must exist")
|
|
428
|
+
}
|
|
429
|
+
raw, err := loadBannerConfigFile(location)
|
|
430
|
+
if err != nil {
|
|
431
|
+
return "", err
|
|
432
|
+
}
|
|
433
|
+
text, ok, err := bannerTextFromConfigValue(raw, filepath.Base(location))
|
|
434
|
+
if err != nil {
|
|
435
|
+
return "", err
|
|
436
|
+
}
|
|
437
|
+
if !ok {
|
|
438
|
+
return "", fmt.Errorf("@ttsc/banner: %s must export a non-empty string or an object with a non-empty \"text\" string", location)
|
|
439
|
+
}
|
|
440
|
+
return text, nil
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
func bannerTextFromConfigValue(raw any, label string) (string, bool, error) {
|
|
444
|
+
if raw == nil {
|
|
445
|
+
return "", false, nil
|
|
446
|
+
}
|
|
447
|
+
text, ok := raw.(string)
|
|
448
|
+
if ok {
|
|
449
|
+
if strings.TrimSpace(text) == "" {
|
|
450
|
+
return "", true, fmt.Errorf("@ttsc/banner: %s must be a non-empty string", label)
|
|
451
|
+
}
|
|
452
|
+
return text, true, nil
|
|
453
|
+
}
|
|
454
|
+
object, ok := raw.(map[string]any)
|
|
455
|
+
if !ok {
|
|
456
|
+
return "", true, fmt.Errorf("@ttsc/banner: %s must be a string or an object with a non-empty \"text\" string", label)
|
|
457
|
+
}
|
|
458
|
+
rawText, ok := object["text"]
|
|
459
|
+
if !ok {
|
|
460
|
+
return "", false, nil
|
|
461
|
+
}
|
|
462
|
+
text, ok = rawText.(string)
|
|
463
|
+
if !ok || strings.TrimSpace(text) == "" {
|
|
464
|
+
return "", true, fmt.Errorf("@ttsc/banner: %s.text must be a non-empty string", label)
|
|
465
|
+
}
|
|
466
|
+
return text, true, nil
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
func findBannerConfigFile(cwd, tsconfigPath string) (string, error) {
|
|
470
|
+
dir := discoveryConfigBaseDir(cwd, tsconfigPath)
|
|
471
|
+
for {
|
|
472
|
+
matches := make([]string, 0, 1)
|
|
473
|
+
for _, name := range []string{
|
|
474
|
+
"banner.config.js",
|
|
475
|
+
"banner.config.cjs",
|
|
476
|
+
"banner.config.mjs",
|
|
477
|
+
"banner.config.ts",
|
|
478
|
+
"banner.config.cts",
|
|
479
|
+
"banner.config.mts",
|
|
480
|
+
} {
|
|
481
|
+
candidate := filepath.Join(dir, name)
|
|
482
|
+
if stat, err := os.Stat(candidate); err == nil && !stat.IsDir() {
|
|
483
|
+
matches = append(matches, candidate)
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
if len(matches) > 1 {
|
|
487
|
+
return "", fmt.Errorf("@ttsc/banner: multiple banner.config.* files found in %s", dir)
|
|
488
|
+
}
|
|
489
|
+
if len(matches) == 1 {
|
|
490
|
+
return matches[0], nil
|
|
491
|
+
}
|
|
492
|
+
parent := filepath.Dir(dir)
|
|
493
|
+
if parent == dir {
|
|
494
|
+
return "", nil
|
|
495
|
+
}
|
|
496
|
+
dir = parent
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
func resolveBannerConfigPath(configPath, cwd, tsconfigPath string) string {
|
|
501
|
+
if filepath.IsAbs(configPath) {
|
|
502
|
+
return configPath
|
|
503
|
+
}
|
|
504
|
+
return filepath.Join(tsconfigBaseDir(cwd, tsconfigPath), configPath)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
func tsconfigBaseDir(cwd, tsconfigPath string) string {
|
|
508
|
+
if tsconfigPath == "" {
|
|
509
|
+
return cwd
|
|
510
|
+
}
|
|
511
|
+
resolvedTsconfig := tsconfigPath
|
|
512
|
+
if !filepath.IsAbs(resolvedTsconfig) {
|
|
513
|
+
resolvedTsconfig = filepath.Join(cwd, resolvedTsconfig)
|
|
514
|
+
}
|
|
515
|
+
return filepath.Dir(resolvedTsconfig)
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
func discoveryConfigBaseDir(cwd, tsconfigPath string) string {
|
|
519
|
+
if tsconfigPath != "" {
|
|
520
|
+
resolvedTsconfig := tsconfigPath
|
|
521
|
+
if !filepath.IsAbs(resolvedTsconfig) {
|
|
522
|
+
resolvedTsconfig = filepath.Join(cwd, resolvedTsconfig)
|
|
523
|
+
}
|
|
524
|
+
return filepath.Dir(resolvedTsconfig)
|
|
525
|
+
}
|
|
526
|
+
return cwd
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
func loadBannerConfigFile(location string) (any, error) {
|
|
530
|
+
if !isBannerConfigFileName(filepath.Base(location)) {
|
|
531
|
+
return nil, fmt.Errorf("@ttsc/banner: config file must be named banner.config.{js,cjs,mjs,ts,mts,cts}: %s", location)
|
|
532
|
+
}
|
|
533
|
+
ext := strings.ToLower(filepath.Ext(location))
|
|
534
|
+
switch ext {
|
|
535
|
+
case ".js", ".cjs", ".mjs":
|
|
536
|
+
return loadBannerScriptConfigFile(location)
|
|
537
|
+
case ".ts", ".cts", ".mts":
|
|
538
|
+
return loadBannerTypeScriptConfigFile(location)
|
|
539
|
+
default:
|
|
540
|
+
return nil, fmt.Errorf("@ttsc/banner: unsupported config file extension %q for %s", ext, location)
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
func isBannerConfigFileName(name string) bool {
|
|
545
|
+
switch name {
|
|
546
|
+
case "banner.config.js",
|
|
547
|
+
"banner.config.cjs",
|
|
548
|
+
"banner.config.mjs",
|
|
549
|
+
"banner.config.ts",
|
|
550
|
+
"banner.config.cts",
|
|
551
|
+
"banner.config.mts":
|
|
552
|
+
return true
|
|
553
|
+
default:
|
|
554
|
+
return false
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
func loadBannerScriptConfigFile(location string) (any, error) {
|
|
559
|
+
const script = `
|
|
560
|
+
const { pathToFileURL } = require("node:url");
|
|
561
|
+
|
|
562
|
+
(async () => {
|
|
563
|
+
const mod = await import(pathToFileURL(process.argv[1]).href);
|
|
564
|
+
const candidate = Object.prototype.hasOwnProperty.call(mod, "default") ? mod.default : mod;
|
|
565
|
+
const value = typeof candidate === "function" ? await candidate() : candidate;
|
|
566
|
+
process.stdout.write(JSON.stringify(toSerializableBanner(value)));
|
|
567
|
+
})().catch((error) => {
|
|
568
|
+
process.stderr.write(error && error.stack ? error.stack : String(error));
|
|
569
|
+
process.exit(1);
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
function toSerializableBanner(value) {
|
|
573
|
+
if (typeof value === "string") {
|
|
574
|
+
return value;
|
|
575
|
+
}
|
|
576
|
+
if (value !== null && typeof value === "object" && typeof value.text === "string") {
|
|
577
|
+
return { text: value.text };
|
|
578
|
+
}
|
|
579
|
+
throw new Error("config file must export a string or an object with a text string");
|
|
580
|
+
}
|
|
581
|
+
`
|
|
582
|
+
node := os.Getenv("TTSC_NODE_BINARY")
|
|
583
|
+
if node == "" {
|
|
584
|
+
node = "node"
|
|
585
|
+
}
|
|
586
|
+
cmd := exec.Command(node, "-e", script, location)
|
|
587
|
+
cmd.Env = nodeConfigLoaderEnv(location)
|
|
588
|
+
output, err := cmd.Output()
|
|
589
|
+
if err != nil {
|
|
590
|
+
stderr := ""
|
|
591
|
+
if exit, ok := err.(*exec.ExitError); ok {
|
|
592
|
+
stderr = strings.TrimSpace(string(exit.Stderr))
|
|
593
|
+
}
|
|
594
|
+
if stderr != "" {
|
|
595
|
+
return nil, fmt.Errorf("@ttsc/banner: load config file %s: %s", location, stderr)
|
|
596
|
+
}
|
|
597
|
+
return nil, fmt.Errorf("@ttsc/banner: load config file %s: %w", location, err)
|
|
598
|
+
}
|
|
599
|
+
var out any
|
|
600
|
+
if err := json.Unmarshal(output, &out); err != nil {
|
|
601
|
+
return nil, fmt.Errorf("@ttsc/banner: parse config file %s output: %w", location, err)
|
|
602
|
+
}
|
|
603
|
+
return out, nil
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
func loadBannerTypeScriptConfigFile(location string) (any, error) {
|
|
607
|
+
tempDir, err := os.MkdirTemp("", "ttsc-banner-config-")
|
|
608
|
+
if err != nil {
|
|
609
|
+
return nil, fmt.Errorf("@ttsc/banner: create config loader tempdir: %w", err)
|
|
610
|
+
}
|
|
611
|
+
defer os.RemoveAll(tempDir)
|
|
612
|
+
|
|
613
|
+
if err := linkNearestNodeModules(tempDir, filepath.Dir(location)); err != nil {
|
|
614
|
+
return nil, err
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
loader := filepath.Join(tempDir, "loader.mts")
|
|
618
|
+
tsconfig := filepath.Join(tempDir, "tsconfig.json")
|
|
619
|
+
importSpecifier, err := relativeImportSpecifier(tempDir, location)
|
|
620
|
+
if err != nil {
|
|
621
|
+
return nil, err
|
|
622
|
+
}
|
|
623
|
+
importLiteral, err := json.Marshal(importSpecifier)
|
|
624
|
+
if err != nil {
|
|
625
|
+
return nil, fmt.Errorf("@ttsc/banner: encode config import %s: %w", location, err)
|
|
626
|
+
}
|
|
627
|
+
if err := os.WriteFile(loader, []byte(bannerTypeScriptConfigLoaderSource(string(importLiteral))), 0o644); err != nil {
|
|
628
|
+
return nil, fmt.Errorf("@ttsc/banner: write config loader: %w", err)
|
|
629
|
+
}
|
|
630
|
+
if err := os.WriteFile(tsconfig, []byte(typeScriptConfigLoaderTsconfig(loader, location, tempDir)), 0o644); err != nil {
|
|
631
|
+
return nil, fmt.Errorf("@ttsc/banner: write config loader tsconfig: %w", err)
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
args := []string{
|
|
635
|
+
"--project", tsconfig,
|
|
636
|
+
"--cwd", tempDir,
|
|
637
|
+
"--cache-dir", filepath.Join(tempDir, "cache"),
|
|
638
|
+
}
|
|
639
|
+
if tsgo := os.Getenv("TTSC_TSGO_BINARY"); tsgo != "" {
|
|
640
|
+
args = append(args, "--binary", tsgo)
|
|
641
|
+
}
|
|
642
|
+
args = append(args, loader)
|
|
643
|
+
|
|
644
|
+
cmd := ttsxCommand(args...)
|
|
645
|
+
cmd.Env = nodeConfigLoaderEnv(location)
|
|
646
|
+
output, err := cmd.Output()
|
|
647
|
+
if err != nil {
|
|
648
|
+
stderr := ""
|
|
649
|
+
if exit, ok := err.(*exec.ExitError); ok {
|
|
650
|
+
stderr = strings.TrimSpace(string(exit.Stderr))
|
|
651
|
+
}
|
|
652
|
+
if stderr != "" {
|
|
653
|
+
return nil, fmt.Errorf("@ttsc/banner: load TypeScript config file %s: %s", location, stderr)
|
|
654
|
+
}
|
|
655
|
+
return nil, fmt.Errorf("@ttsc/banner: load TypeScript config file %s: %w", location, err)
|
|
656
|
+
}
|
|
657
|
+
var out any
|
|
658
|
+
if err := json.Unmarshal(output, &out); err != nil {
|
|
659
|
+
return nil, fmt.Errorf("@ttsc/banner: parse TypeScript config file %s output: %w", location, err)
|
|
660
|
+
}
|
|
661
|
+
return out, nil
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
func relativeImportSpecifier(fromDir, location string) (string, error) {
|
|
665
|
+
relative, err := filepath.Rel(fromDir, location)
|
|
666
|
+
if err != nil {
|
|
667
|
+
return "", fmt.Errorf("@ttsc/banner: resolve relative config import %s: %w", location, err)
|
|
668
|
+
}
|
|
669
|
+
relative = filepath.ToSlash(relative)
|
|
670
|
+
if strings.HasPrefix(relative, "../") || strings.HasPrefix(relative, "./") {
|
|
671
|
+
return relative, nil
|
|
672
|
+
}
|
|
673
|
+
return "./" + relative, nil
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
func bannerTypeScriptConfigLoaderSource(importLiteral string) string {
|
|
677
|
+
return fmt.Sprintf(`import * as importedConfig from %s;
|
|
678
|
+
|
|
679
|
+
declare const process: {
|
|
680
|
+
stdout: { write(value: string): void };
|
|
681
|
+
stderr: { write(value: string): void };
|
|
682
|
+
exit(code?: number): never;
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
try {
|
|
686
|
+
const value = await resolveConfig(importedConfig);
|
|
687
|
+
process.stdout.write(JSON.stringify(toSerializableBanner(value)));
|
|
688
|
+
} catch (error) {
|
|
689
|
+
process.stderr.write(error instanceof Error && error.stack ? error.stack : String(error));
|
|
690
|
+
process.exit(1);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
async function resolveConfig(value: unknown): Promise<unknown> {
|
|
694
|
+
let current = value;
|
|
695
|
+
for (let i = 0; i < 8; i++) {
|
|
696
|
+
if (isObject(current) && hasOwn(current, "default")) {
|
|
697
|
+
current = current.default;
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
702
|
+
if (typeof current === "function") {
|
|
703
|
+
return await (current as () => unknown | Promise<unknown>)();
|
|
704
|
+
}
|
|
705
|
+
return current;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
function isObject(value: unknown): value is Record<string, unknown> {
|
|
709
|
+
return value !== null && typeof value === "object";
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function hasOwn(value: Record<string, unknown>, key: string): boolean {
|
|
713
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function toSerializableBanner(value: unknown): unknown {
|
|
717
|
+
if (typeof value === "string") {
|
|
718
|
+
return value;
|
|
719
|
+
}
|
|
720
|
+
if (isObject(value) && typeof value.text === "string") {
|
|
721
|
+
return { text: value.text };
|
|
722
|
+
}
|
|
723
|
+
throw new Error("config file must export a string or an object with a text string");
|
|
724
|
+
}
|
|
725
|
+
`, importLiteral)
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
func typeScriptConfigLoaderTsconfig(loader, location, outDir string) string {
|
|
729
|
+
content := map[string]any{
|
|
730
|
+
"compilerOptions": map[string]any{
|
|
731
|
+
"allowImportingTsExtensions": true,
|
|
732
|
+
"module": "ESNext",
|
|
733
|
+
"moduleResolution": "bundler",
|
|
734
|
+
"outDir": filepath.ToSlash(filepath.Join(outDir, "out")),
|
|
735
|
+
"rewriteRelativeImportExtensions": true,
|
|
736
|
+
"rootDir": "/",
|
|
737
|
+
"skipLibCheck": true,
|
|
738
|
+
"strict": true,
|
|
739
|
+
"target": "ES2022",
|
|
740
|
+
},
|
|
741
|
+
"files": []string{
|
|
742
|
+
filepath.ToSlash(loader),
|
|
743
|
+
filepath.ToSlash(location),
|
|
744
|
+
},
|
|
745
|
+
}
|
|
746
|
+
body, err := json.MarshalIndent(content, "", " ")
|
|
747
|
+
if err != nil {
|
|
748
|
+
panic(err)
|
|
749
|
+
}
|
|
750
|
+
return string(body)
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
func ttsxCommand(args ...string) *exec.Cmd {
|
|
754
|
+
ttsx := os.Getenv("TTSC_TTSX_BINARY")
|
|
755
|
+
if ttsx == "" {
|
|
756
|
+
ttsx = "ttsx"
|
|
757
|
+
}
|
|
758
|
+
if shouldRunTtsxThroughNode(ttsx) {
|
|
759
|
+
node := os.Getenv("TTSC_NODE_BINARY")
|
|
760
|
+
if node == "" {
|
|
761
|
+
node = "node"
|
|
762
|
+
}
|
|
763
|
+
return exec.Command(node, append([]string{ttsx}, args...)...)
|
|
764
|
+
}
|
|
765
|
+
return exec.Command(ttsx, args...)
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
func shouldRunTtsxThroughNode(binary string) bool {
|
|
769
|
+
switch strings.ToLower(filepath.Ext(binary)) {
|
|
770
|
+
case ".js", ".cjs", ".mjs", ".ts", ".cts", ".mts":
|
|
771
|
+
return true
|
|
772
|
+
default:
|
|
773
|
+
return false
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
func nodeConfigLoaderEnv(location string) []string {
|
|
778
|
+
env := os.Environ()
|
|
779
|
+
parts := make([]string, 0, 2)
|
|
780
|
+
if nodeModules := findNearestNodeModules(filepath.Dir(location)); nodeModules != "" {
|
|
781
|
+
parts = append(parts, nodeModules)
|
|
782
|
+
}
|
|
783
|
+
if existing := os.Getenv("NODE_PATH"); existing != "" {
|
|
784
|
+
parts = append(parts, existing)
|
|
785
|
+
}
|
|
786
|
+
if len(parts) == 0 {
|
|
787
|
+
return env
|
|
788
|
+
}
|
|
789
|
+
return setEnv(env, "NODE_PATH", strings.Join(parts, string(os.PathListSeparator)))
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
func linkNearestNodeModules(tempDir, sourceDir string) error {
|
|
793
|
+
nodeModules := findNearestNodeModules(sourceDir)
|
|
794
|
+
if nodeModules == "" {
|
|
795
|
+
return nil
|
|
796
|
+
}
|
|
797
|
+
link := filepath.Join(tempDir, "node_modules")
|
|
798
|
+
if err := os.Symlink(nodeModules, link); err != nil {
|
|
799
|
+
return fmt.Errorf("@ttsc/banner: link config node_modules %s: %w", nodeModules, err)
|
|
800
|
+
}
|
|
801
|
+
return nil
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
func findNearestNodeModules(start string) string {
|
|
805
|
+
dir := filepath.Clean(start)
|
|
806
|
+
for {
|
|
807
|
+
candidate := filepath.Join(dir, "node_modules")
|
|
808
|
+
if stat, err := os.Stat(candidate); err == nil && stat.IsDir() {
|
|
809
|
+
return candidate
|
|
810
|
+
}
|
|
811
|
+
parent := filepath.Dir(dir)
|
|
812
|
+
if parent == dir {
|
|
813
|
+
return ""
|
|
814
|
+
}
|
|
815
|
+
dir = parent
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
func setEnv(env []string, key, value string) []string {
|
|
820
|
+
prefix := key + "="
|
|
821
|
+
for i, entry := range env {
|
|
822
|
+
if strings.HasPrefix(entry, prefix) {
|
|
823
|
+
env[i] = prefix + value
|
|
824
|
+
return env
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return append(env, prefix+value)
|
|
828
|
+
}
|
|
829
|
+
|
|
319
830
|
func sanitizeJSDocLine(line string) string {
|
|
320
831
|
return strings.ReplaceAll(line, "*/", "* /")
|
|
321
832
|
}
|
|
@@ -402,9 +913,14 @@ func visitModuleSpecifiers(node *shimast.Node, visit func(*shimast.Node)) {
|
|
|
402
913
|
if arg != nil && arg.Kind == shimast.KindLiteralType {
|
|
403
914
|
visit(arg.AsLiteralTypeNode().Literal)
|
|
404
915
|
}
|
|
916
|
+
case shimast.KindModuleDeclaration:
|
|
917
|
+
decl := node.AsModuleDeclaration()
|
|
918
|
+
if decl != nil {
|
|
919
|
+
visit(decl.Name())
|
|
920
|
+
}
|
|
405
921
|
case shimast.KindCallExpression:
|
|
406
922
|
call := node.AsCallExpression()
|
|
407
|
-
if call
|
|
923
|
+
if isModuleSpecifierCall(call) && call.Arguments != nil && len(call.Arguments.Nodes) > 0 {
|
|
408
924
|
visit(call.Arguments.Nodes[0])
|
|
409
925
|
}
|
|
410
926
|
}
|
|
@@ -414,6 +930,20 @@ func visitModuleSpecifiers(node *shimast.Node, visit func(*shimast.Node)) {
|
|
|
414
930
|
})
|
|
415
931
|
}
|
|
416
932
|
|
|
933
|
+
func isModuleSpecifierCall(call *shimast.CallExpression) bool {
|
|
934
|
+
if call == nil || call.Expression == nil {
|
|
935
|
+
return false
|
|
936
|
+
}
|
|
937
|
+
switch call.Expression.Kind {
|
|
938
|
+
case shimast.KindImportKeyword:
|
|
939
|
+
return true
|
|
940
|
+
case shimast.KindIdentifier:
|
|
941
|
+
return call.Expression.Text() == "require"
|
|
942
|
+
default:
|
|
943
|
+
return false
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
417
947
|
func (r *pathsRewriter) rewrite(fromSource string, specifier string) (string, bool) {
|
|
418
948
|
if specifier == "" || strings.HasPrefix(specifier, ".") || strings.HasPrefix(specifier, "/") {
|
|
419
949
|
return specifier, false
|
|
@@ -468,6 +998,11 @@ func (r *pathsRewriter) lookupSource(candidate string) (string, bool) {
|
|
|
468
998
|
return source, true
|
|
469
999
|
}
|
|
470
1000
|
}
|
|
1001
|
+
for _, ext := range []string{".ts", ".tsx", ".mts", ".cts"} {
|
|
1002
|
+
if source, ok := r.sourceFiles[normalizePath(filepath.Join(stem, "index"+ext))]; ok {
|
|
1003
|
+
return source, true
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
471
1006
|
return "", false
|
|
472
1007
|
}
|
|
473
1008
|
|
|
@@ -476,10 +1011,21 @@ func (r *pathsRewriter) outputPathForSource(source string) string {
|
|
|
476
1011
|
return ""
|
|
477
1012
|
}
|
|
478
1013
|
rel, err := filepath.Rel(r.rootDir, source)
|
|
479
|
-
if err != nil ||
|
|
1014
|
+
if err != nil || isOutsideRelativePath(rel) {
|
|
480
1015
|
return ""
|
|
481
1016
|
}
|
|
482
|
-
return normalizePath(filepath.Join(r.outDir, replaceSourceExtension(rel,
|
|
1017
|
+
return normalizePath(filepath.Join(r.outDir, replaceSourceExtension(rel, emittedJavaScriptExtension(rel))))
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
func emittedJavaScriptExtension(source string) string {
|
|
1021
|
+
switch strings.ToLower(filepath.Ext(source)) {
|
|
1022
|
+
case ".mts":
|
|
1023
|
+
return ".mjs"
|
|
1024
|
+
case ".cts":
|
|
1025
|
+
return ".cjs"
|
|
1026
|
+
default:
|
|
1027
|
+
return ".js"
|
|
1028
|
+
}
|
|
483
1029
|
}
|
|
484
1030
|
|
|
485
1031
|
func matchPattern(pattern string, specifier string) (string, bool) {
|
|
@@ -557,6 +1103,14 @@ type callPattern struct {
|
|
|
557
1103
|
}
|
|
558
1104
|
|
|
559
1105
|
func parseStrip(config map[string]any) (*stripRewriter, error) {
|
|
1106
|
+
_, hasCalls := config["calls"]
|
|
1107
|
+
_, hasStatements := config["statements"]
|
|
1108
|
+
if !hasCalls && !hasStatements {
|
|
1109
|
+
config = map[string]any{
|
|
1110
|
+
"calls": []any{"console.log", "console.debug", "assert.*"},
|
|
1111
|
+
"statements": []any{"debugger"},
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
560
1114
|
calls, err := stringArrayConfig(config, "calls")
|
|
561
1115
|
if err != nil {
|
|
562
1116
|
return nil, fmt.Errorf("@ttsc/strip: %w", err)
|
|
@@ -595,7 +1149,7 @@ func filterStatements(list *shimast.NodeList, strip *stripRewriter) {
|
|
|
595
1149
|
if list == nil || len(list.Nodes) == 0 {
|
|
596
1150
|
return
|
|
597
1151
|
}
|
|
598
|
-
out := list.Nodes
|
|
1152
|
+
out := make([]*shimast.Node, 0, len(list.Nodes))
|
|
599
1153
|
for _, stmt := range list.Nodes {
|
|
600
1154
|
if shouldStripStatement(stmt, strip) {
|
|
601
1155
|
continue
|
|
@@ -610,6 +1164,7 @@ func filterChildStatements(node *shimast.Node, strip *stripRewriter) {
|
|
|
610
1164
|
if node == nil {
|
|
611
1165
|
return
|
|
612
1166
|
}
|
|
1167
|
+
filterEmbeddedStatements(node, strip)
|
|
613
1168
|
if node.CanHaveStatements() {
|
|
614
1169
|
filterStatements(node.StatementList(), strip)
|
|
615
1170
|
}
|
|
@@ -619,6 +1174,53 @@ func filterChildStatements(node *shimast.Node, strip *stripRewriter) {
|
|
|
619
1174
|
})
|
|
620
1175
|
}
|
|
621
1176
|
|
|
1177
|
+
func filterEmbeddedStatements(node *shimast.Node, strip *stripRewriter) {
|
|
1178
|
+
switch node.Kind {
|
|
1179
|
+
case shimast.KindIfStatement:
|
|
1180
|
+
stmt := node.AsIfStatement()
|
|
1181
|
+
stmt.ThenStatement = filterEmbeddedStatement(stmt.ThenStatement, strip)
|
|
1182
|
+
stmt.ElseStatement = filterEmbeddedStatement(stmt.ElseStatement, strip)
|
|
1183
|
+
case shimast.KindDoStatement:
|
|
1184
|
+
stmt := node.AsDoStatement()
|
|
1185
|
+
stmt.Statement = filterEmbeddedStatement(stmt.Statement, strip)
|
|
1186
|
+
case shimast.KindWhileStatement:
|
|
1187
|
+
stmt := node.AsWhileStatement()
|
|
1188
|
+
stmt.Statement = filterEmbeddedStatement(stmt.Statement, strip)
|
|
1189
|
+
case shimast.KindForStatement:
|
|
1190
|
+
stmt := node.AsForStatement()
|
|
1191
|
+
stmt.Statement = filterEmbeddedStatement(stmt.Statement, strip)
|
|
1192
|
+
case shimast.KindForInStatement, shimast.KindForOfStatement:
|
|
1193
|
+
stmt := node.AsForInOrOfStatement()
|
|
1194
|
+
stmt.Statement = filterEmbeddedStatement(stmt.Statement, strip)
|
|
1195
|
+
case shimast.KindWithStatement:
|
|
1196
|
+
stmt := node.AsWithStatement()
|
|
1197
|
+
stmt.Statement = filterEmbeddedStatement(stmt.Statement, strip)
|
|
1198
|
+
case shimast.KindLabeledStatement:
|
|
1199
|
+
stmt := node.AsLabeledStatement()
|
|
1200
|
+
stmt.Statement = filterEmbeddedStatement(stmt.Statement, strip)
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
func filterEmbeddedStatement(stmt *shimast.Statement, strip *stripRewriter) *shimast.Statement {
|
|
1205
|
+
if stmt == nil {
|
|
1206
|
+
return nil
|
|
1207
|
+
}
|
|
1208
|
+
if shouldStripStatement(stmt, strip) {
|
|
1209
|
+
return emptyStatement(stmt)
|
|
1210
|
+
}
|
|
1211
|
+
filterChildStatements(stmt, strip)
|
|
1212
|
+
return stmt
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
func emptyStatement(original *shimast.Node) *shimast.Statement {
|
|
1216
|
+
empty := shimast.NewNodeFactory(shimast.NodeFactoryHooks{}).NewEmptyStatement()
|
|
1217
|
+
empty.Flags |= shimast.NodeFlagsSynthesized
|
|
1218
|
+
if original != nil {
|
|
1219
|
+
empty.Loc = original.Loc
|
|
1220
|
+
}
|
|
1221
|
+
return empty
|
|
1222
|
+
}
|
|
1223
|
+
|
|
622
1224
|
func shouldStripStatement(node *shimast.Node, strip *stripRewriter) bool {
|
|
623
1225
|
if node == nil {
|
|
624
1226
|
return false
|