ttsc 0.8.0 → 0.9.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 +75 -66
- package/cmd/ttsc/api_compile.go +5 -1
- package/driver/program.go +23 -1
- package/driver/rewrite.go +126 -9
- package/driver/rewrite_test.go +52 -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 +179 -125
- 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/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 +127 -32
- 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 +610 -18
- package/utility/host_test.go +162 -0
- 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"
|
|
@@ -43,6 +44,22 @@ type transformResult struct {
|
|
|
43
44
|
TypeScript map[string]string `json:"typescript"`
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
// RunCheck validates the project and first-party utility plugin configuration
|
|
48
|
+
// without emitting output.
|
|
49
|
+
func RunCheck(args []string) int {
|
|
50
|
+
opts, ok := parseHostOptions("check", args)
|
|
51
|
+
if !ok {
|
|
52
|
+
return 2
|
|
53
|
+
}
|
|
54
|
+
opts.noEmit = true
|
|
55
|
+
prog, _, _, _, ok := loadUtilityProgram(opts)
|
|
56
|
+
if !ok {
|
|
57
|
+
return 2
|
|
58
|
+
}
|
|
59
|
+
defer prog.Close()
|
|
60
|
+
return 0
|
|
61
|
+
}
|
|
62
|
+
|
|
46
63
|
// RunBuild hosts first-party utility transform plugins inside one compiler emit.
|
|
47
64
|
func RunBuild(args []string) int {
|
|
48
65
|
opts, ok := parseHostOptions("build", args)
|
|
@@ -76,6 +93,9 @@ func RunBuild(args []string) int {
|
|
|
76
93
|
for _, d := range eDiags {
|
|
77
94
|
fmt.Fprintln(os.Stderr, " -", d.String())
|
|
78
95
|
}
|
|
96
|
+
if driver.CountErrors(eDiags) > 0 {
|
|
97
|
+
return 2
|
|
98
|
+
}
|
|
79
99
|
if res != nil && !opts.quiet {
|
|
80
100
|
fmt.Fprintf(os.Stdout, "// ttsc utility: emitted=%d files\n", len(res.EmittedFiles))
|
|
81
101
|
}
|
|
@@ -117,7 +137,7 @@ func parseHostOptions(command string, args []string) (hostOptions, bool) {
|
|
|
117
137
|
quiet := fs.Bool("quiet", true, "suppress summary")
|
|
118
138
|
tsconfig := fs.String("tsconfig", "tsconfig.json", "project tsconfig")
|
|
119
139
|
verbose := fs.Bool("verbose", false, "print summary")
|
|
120
|
-
if err := fs.Parse(args); err != nil {
|
|
140
|
+
if err := fs.Parse(filterHostArgs(args)); err != nil {
|
|
121
141
|
return hostOptions{}, false
|
|
122
142
|
}
|
|
123
143
|
resolvedCwd := *cwd
|
|
@@ -149,13 +169,57 @@ func parseHostOptions(command string, args []string) (hostOptions, bool) {
|
|
|
149
169
|
}, true
|
|
150
170
|
}
|
|
151
171
|
|
|
172
|
+
func filterHostArgs(args []string) []string {
|
|
173
|
+
known := map[string]bool{
|
|
174
|
+
"cwd": true,
|
|
175
|
+
"emit": false,
|
|
176
|
+
"noEmit": false,
|
|
177
|
+
"outDir": true,
|
|
178
|
+
"plugins-json": true,
|
|
179
|
+
"quiet": false,
|
|
180
|
+
"tsconfig": true,
|
|
181
|
+
"verbose": false,
|
|
182
|
+
}
|
|
183
|
+
filtered := make([]string, 0, len(args))
|
|
184
|
+
for i := 0; i < len(args); i++ {
|
|
185
|
+
current := args[i]
|
|
186
|
+
if current == "--" {
|
|
187
|
+
break
|
|
188
|
+
}
|
|
189
|
+
if !strings.HasPrefix(current, "--") {
|
|
190
|
+
filtered = append(filtered, current)
|
|
191
|
+
continue
|
|
192
|
+
}
|
|
193
|
+
name, hasInlineValue := flagName(current)
|
|
194
|
+
takesValue, ok := known[name]
|
|
195
|
+
if ok {
|
|
196
|
+
filtered = append(filtered, current)
|
|
197
|
+
if takesValue && !hasInlineValue && i+1 < len(args) {
|
|
198
|
+
i++
|
|
199
|
+
filtered = append(filtered, args[i])
|
|
200
|
+
}
|
|
201
|
+
continue
|
|
202
|
+
}
|
|
203
|
+
if !hasInlineValue && i+1 < len(args) && !strings.HasPrefix(args[i+1], "-") {
|
|
204
|
+
i++
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return filtered
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
func flagName(arg string) (string, bool) {
|
|
211
|
+
name := strings.TrimPrefix(arg, "--")
|
|
212
|
+
before, _, found := strings.Cut(name, "=")
|
|
213
|
+
return before, found
|
|
214
|
+
}
|
|
215
|
+
|
|
152
216
|
func loadUtilityProgram(opts hostOptions) (*driver.Program, []pluginEntry, transformState, string, bool) {
|
|
153
217
|
entries, err := parsePluginEntries(opts.pluginsJSON)
|
|
154
218
|
if err != nil {
|
|
155
219
|
fmt.Fprintln(os.Stderr, err)
|
|
156
220
|
return nil, nil, transformState{}, "", false
|
|
157
221
|
}
|
|
158
|
-
sourcePreamble, err := prepareSourcePreamble(entries)
|
|
222
|
+
sourcePreamble, err := prepareSourcePreamble(entries, opts.cwd, opts.tsconfig)
|
|
159
223
|
if err != nil {
|
|
160
224
|
fmt.Fprintln(os.Stderr, err)
|
|
161
225
|
return nil, nil, transformState{}, "", false
|
|
@@ -172,19 +236,25 @@ func loadUtilityProgram(opts hostOptions) (*driver.Program, []pluginEntry, trans
|
|
|
172
236
|
}
|
|
173
237
|
if len(diags) > 0 {
|
|
174
238
|
driver.WritePrettyDiagnostics(os.Stderr, diags, opts.cwd)
|
|
239
|
+
if prog != nil {
|
|
240
|
+
_ = prog.Close()
|
|
241
|
+
}
|
|
175
242
|
return nil, nil, transformState{}, "", false
|
|
176
243
|
}
|
|
177
244
|
if diags := prog.Diagnostics(); len(diags) > 0 {
|
|
178
245
|
driver.WritePrettyDiagnostics(os.Stderr, diags, opts.cwd)
|
|
246
|
+
_ = prog.Close()
|
|
179
247
|
return nil, nil, transformState{}, "", false
|
|
180
248
|
}
|
|
181
249
|
state, err := prepareTransforms(prog, entries)
|
|
182
250
|
if err != nil {
|
|
183
251
|
fmt.Fprintln(os.Stderr, err)
|
|
252
|
+
_ = prog.Close()
|
|
184
253
|
return nil, nil, transformState{}, "", false
|
|
185
254
|
}
|
|
186
255
|
if err := applySourceTransforms(prog, state); err != nil {
|
|
187
256
|
fmt.Fprintln(os.Stderr, err)
|
|
257
|
+
_ = prog.Close()
|
|
188
258
|
return nil, nil, transformState{}, "", false
|
|
189
259
|
}
|
|
190
260
|
return prog, entries, state, sourcePreamble, true
|
|
@@ -218,13 +288,13 @@ func prepareTransforms(prog *driver.Program, entries []pluginEntry) (transformSt
|
|
|
218
288
|
return state, nil
|
|
219
289
|
}
|
|
220
290
|
|
|
221
|
-
func prepareSourcePreamble(entries []pluginEntry) (string, error) {
|
|
291
|
+
func prepareSourcePreamble(entries []pluginEntry, cwd, tsconfigPath string) (string, error) {
|
|
222
292
|
var preamble strings.Builder
|
|
223
293
|
for _, entry := range entries {
|
|
224
294
|
if entry.Name != "@ttsc/banner" {
|
|
225
295
|
continue
|
|
226
296
|
}
|
|
227
|
-
banner, err := parseBanner(entry.Config)
|
|
297
|
+
banner, err := parseBanner(entry.Config, cwd, tsconfigPath)
|
|
228
298
|
if err != nil {
|
|
229
299
|
return "", err
|
|
230
300
|
}
|
|
@@ -239,7 +309,7 @@ func makeSourcePreambleWriteFile(prog *driver.Program, sourcePreamble string) sh
|
|
|
239
309
|
}
|
|
240
310
|
return func(fileName, text string, _ *shimcompiler.WriteFileData) error {
|
|
241
311
|
if shouldEnsureSourcePreamble(fileName, text, sourcePreamble) {
|
|
242
|
-
text = sourcePreamble
|
|
312
|
+
text = driver.ApplySourcePreamble(text, sourcePreamble)
|
|
243
313
|
}
|
|
244
314
|
return driver.DefaultWriteFile(fileName, text)
|
|
245
315
|
}
|
|
@@ -280,20 +350,20 @@ func applySourceTransforms(prog *driver.Program, state transformState) error {
|
|
|
280
350
|
|
|
281
351
|
func apiOutputKey(cwd, fileName string) string {
|
|
282
352
|
rel, err := filepath.Rel(cwd, fileName)
|
|
283
|
-
if err != nil ||
|
|
353
|
+
if err != nil || isOutsideRelativePath(rel) {
|
|
284
354
|
return filepath.ToSlash(fileName)
|
|
285
355
|
}
|
|
286
356
|
return filepath.ToSlash(rel)
|
|
287
357
|
}
|
|
288
358
|
|
|
289
|
-
func
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
text,
|
|
295
|
-
if
|
|
296
|
-
return "",
|
|
359
|
+
func isOutsideRelativePath(rel string) bool {
|
|
360
|
+
return rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator))
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
func parseBanner(config map[string]any, cwd, tsconfigPath string) (string, error) {
|
|
364
|
+
text, err := resolveBannerText(config, cwd, tsconfigPath)
|
|
365
|
+
if err != nil {
|
|
366
|
+
return "", err
|
|
297
367
|
}
|
|
298
368
|
lines := strings.Split(strings.ReplaceAll(text, "\r\n", "\n"), "\n")
|
|
299
369
|
for len(lines) > 0 && strings.TrimSpace(lines[len(lines)-1]) == "" {
|
|
@@ -316,6 +386,437 @@ func parseBanner(config map[string]any) (string, error) {
|
|
|
316
386
|
return b.String(), nil
|
|
317
387
|
}
|
|
318
388
|
|
|
389
|
+
func resolveBannerText(config map[string]any, cwd, tsconfigPath string) (string, error) {
|
|
390
|
+
if text, ok, err := bannerTextFromConfigValue(config["text"], `"text"`); ok || err != nil {
|
|
391
|
+
return text, err
|
|
392
|
+
}
|
|
393
|
+
if rawConfigPath, ok := config["config"]; ok {
|
|
394
|
+
configPath, ok := rawConfigPath.(string)
|
|
395
|
+
if !ok || strings.TrimSpace(configPath) == "" {
|
|
396
|
+
return "", fmt.Errorf("@ttsc/banner: \"config\" must be a non-empty string path")
|
|
397
|
+
}
|
|
398
|
+
location := resolveBannerConfigPath(configPath, cwd, tsconfigPath)
|
|
399
|
+
raw, err := loadBannerConfigFile(location)
|
|
400
|
+
if err != nil {
|
|
401
|
+
return "", err
|
|
402
|
+
}
|
|
403
|
+
text, ok, err := bannerTextFromConfigValue(raw, filepath.Base(location))
|
|
404
|
+
if err != nil {
|
|
405
|
+
return "", err
|
|
406
|
+
}
|
|
407
|
+
if !ok {
|
|
408
|
+
return "", fmt.Errorf("@ttsc/banner: %s must export a non-empty string or an object with a non-empty \"text\" string", location)
|
|
409
|
+
}
|
|
410
|
+
return text, nil
|
|
411
|
+
}
|
|
412
|
+
location, err := findBannerConfigFile(cwd, tsconfigPath)
|
|
413
|
+
if err != nil {
|
|
414
|
+
return "", err
|
|
415
|
+
}
|
|
416
|
+
if location == "" {
|
|
417
|
+
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")
|
|
418
|
+
}
|
|
419
|
+
raw, err := loadBannerConfigFile(location)
|
|
420
|
+
if err != nil {
|
|
421
|
+
return "", err
|
|
422
|
+
}
|
|
423
|
+
text, ok, err := bannerTextFromConfigValue(raw, filepath.Base(location))
|
|
424
|
+
if err != nil {
|
|
425
|
+
return "", err
|
|
426
|
+
}
|
|
427
|
+
if !ok {
|
|
428
|
+
return "", fmt.Errorf("@ttsc/banner: %s must export a non-empty string or an object with a non-empty \"text\" string", location)
|
|
429
|
+
}
|
|
430
|
+
return text, nil
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
func bannerTextFromConfigValue(raw any, label string) (string, bool, error) {
|
|
434
|
+
if raw == nil {
|
|
435
|
+
return "", false, nil
|
|
436
|
+
}
|
|
437
|
+
text, ok := raw.(string)
|
|
438
|
+
if ok {
|
|
439
|
+
if strings.TrimSpace(text) == "" {
|
|
440
|
+
return "", true, fmt.Errorf("@ttsc/banner: %s must be a non-empty string", label)
|
|
441
|
+
}
|
|
442
|
+
return text, true, nil
|
|
443
|
+
}
|
|
444
|
+
object, ok := raw.(map[string]any)
|
|
445
|
+
if !ok {
|
|
446
|
+
return "", true, fmt.Errorf("@ttsc/banner: %s must be a string or an object with a non-empty \"text\" string", label)
|
|
447
|
+
}
|
|
448
|
+
rawText, ok := object["text"]
|
|
449
|
+
if !ok {
|
|
450
|
+
return "", false, nil
|
|
451
|
+
}
|
|
452
|
+
text, ok = rawText.(string)
|
|
453
|
+
if !ok || strings.TrimSpace(text) == "" {
|
|
454
|
+
return "", true, fmt.Errorf("@ttsc/banner: %s.text must be a non-empty string", label)
|
|
455
|
+
}
|
|
456
|
+
return text, true, nil
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
func findBannerConfigFile(cwd, tsconfigPath string) (string, error) {
|
|
460
|
+
dir := discoveryConfigBaseDir(cwd, tsconfigPath)
|
|
461
|
+
for {
|
|
462
|
+
matches := make([]string, 0, 1)
|
|
463
|
+
for _, name := range []string{
|
|
464
|
+
"banner.config.js",
|
|
465
|
+
"banner.config.cjs",
|
|
466
|
+
"banner.config.mjs",
|
|
467
|
+
"banner.config.ts",
|
|
468
|
+
"banner.config.cts",
|
|
469
|
+
"banner.config.mts",
|
|
470
|
+
} {
|
|
471
|
+
candidate := filepath.Join(dir, name)
|
|
472
|
+
if stat, err := os.Stat(candidate); err == nil && !stat.IsDir() {
|
|
473
|
+
matches = append(matches, candidate)
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
if len(matches) > 1 {
|
|
477
|
+
return "", fmt.Errorf("@ttsc/banner: multiple banner.config.* files found in %s", dir)
|
|
478
|
+
}
|
|
479
|
+
if len(matches) == 1 {
|
|
480
|
+
return matches[0], nil
|
|
481
|
+
}
|
|
482
|
+
parent := filepath.Dir(dir)
|
|
483
|
+
if parent == dir {
|
|
484
|
+
return "", nil
|
|
485
|
+
}
|
|
486
|
+
dir = parent
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
func resolveBannerConfigPath(configPath, cwd, tsconfigPath string) string {
|
|
491
|
+
if filepath.IsAbs(configPath) {
|
|
492
|
+
return configPath
|
|
493
|
+
}
|
|
494
|
+
return filepath.Join(tsconfigBaseDir(cwd, tsconfigPath), configPath)
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
func tsconfigBaseDir(cwd, tsconfigPath string) string {
|
|
498
|
+
if tsconfigPath == "" {
|
|
499
|
+
return cwd
|
|
500
|
+
}
|
|
501
|
+
resolvedTsconfig := tsconfigPath
|
|
502
|
+
if !filepath.IsAbs(resolvedTsconfig) {
|
|
503
|
+
resolvedTsconfig = filepath.Join(cwd, resolvedTsconfig)
|
|
504
|
+
}
|
|
505
|
+
return filepath.Dir(resolvedTsconfig)
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
func discoveryConfigBaseDir(cwd, tsconfigPath string) string {
|
|
509
|
+
if tsconfigPath != "" {
|
|
510
|
+
resolvedTsconfig := tsconfigPath
|
|
511
|
+
if !filepath.IsAbs(resolvedTsconfig) {
|
|
512
|
+
resolvedTsconfig = filepath.Join(cwd, resolvedTsconfig)
|
|
513
|
+
}
|
|
514
|
+
return filepath.Dir(resolvedTsconfig)
|
|
515
|
+
}
|
|
516
|
+
return cwd
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
func loadBannerConfigFile(location string) (any, error) {
|
|
520
|
+
if !isBannerConfigFileName(filepath.Base(location)) {
|
|
521
|
+
return nil, fmt.Errorf("@ttsc/banner: config file must be named banner.config.{js,cjs,mjs,ts,mts,cts}: %s", location)
|
|
522
|
+
}
|
|
523
|
+
ext := strings.ToLower(filepath.Ext(location))
|
|
524
|
+
switch ext {
|
|
525
|
+
case ".js", ".cjs", ".mjs":
|
|
526
|
+
return loadBannerScriptConfigFile(location)
|
|
527
|
+
case ".ts", ".cts", ".mts":
|
|
528
|
+
return loadBannerTypeScriptConfigFile(location)
|
|
529
|
+
default:
|
|
530
|
+
return nil, fmt.Errorf("@ttsc/banner: unsupported config file extension %q for %s", ext, location)
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
func isBannerConfigFileName(name string) bool {
|
|
535
|
+
switch name {
|
|
536
|
+
case "banner.config.js",
|
|
537
|
+
"banner.config.cjs",
|
|
538
|
+
"banner.config.mjs",
|
|
539
|
+
"banner.config.ts",
|
|
540
|
+
"banner.config.cts",
|
|
541
|
+
"banner.config.mts":
|
|
542
|
+
return true
|
|
543
|
+
default:
|
|
544
|
+
return false
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
func loadBannerScriptConfigFile(location string) (any, error) {
|
|
549
|
+
const script = `
|
|
550
|
+
const { pathToFileURL } = require("node:url");
|
|
551
|
+
|
|
552
|
+
(async () => {
|
|
553
|
+
const mod = await import(pathToFileURL(process.argv[1]).href);
|
|
554
|
+
const candidate = Object.prototype.hasOwnProperty.call(mod, "default") ? mod.default : mod;
|
|
555
|
+
const value = typeof candidate === "function" ? await candidate() : candidate;
|
|
556
|
+
process.stdout.write(JSON.stringify(toSerializableBanner(value)));
|
|
557
|
+
})().catch((error) => {
|
|
558
|
+
process.stderr.write(error && error.stack ? error.stack : String(error));
|
|
559
|
+
process.exit(1);
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
function toSerializableBanner(value) {
|
|
563
|
+
if (typeof value === "string") {
|
|
564
|
+
return value;
|
|
565
|
+
}
|
|
566
|
+
if (value !== null && typeof value === "object" && typeof value.text === "string") {
|
|
567
|
+
return { text: value.text };
|
|
568
|
+
}
|
|
569
|
+
throw new Error("config file must export a string or an object with a text string");
|
|
570
|
+
}
|
|
571
|
+
`
|
|
572
|
+
node := os.Getenv("TTSC_NODE_BINARY")
|
|
573
|
+
if node == "" {
|
|
574
|
+
node = "node"
|
|
575
|
+
}
|
|
576
|
+
cmd := exec.Command(node, "-e", script, location)
|
|
577
|
+
cmd.Env = nodeConfigLoaderEnv(location)
|
|
578
|
+
output, err := cmd.Output()
|
|
579
|
+
if err != nil {
|
|
580
|
+
stderr := ""
|
|
581
|
+
if exit, ok := err.(*exec.ExitError); ok {
|
|
582
|
+
stderr = strings.TrimSpace(string(exit.Stderr))
|
|
583
|
+
}
|
|
584
|
+
if stderr != "" {
|
|
585
|
+
return nil, fmt.Errorf("@ttsc/banner: load config file %s: %s", location, stderr)
|
|
586
|
+
}
|
|
587
|
+
return nil, fmt.Errorf("@ttsc/banner: load config file %s: %w", location, err)
|
|
588
|
+
}
|
|
589
|
+
var out any
|
|
590
|
+
if err := json.Unmarshal(output, &out); err != nil {
|
|
591
|
+
return nil, fmt.Errorf("@ttsc/banner: parse config file %s output: %w", location, err)
|
|
592
|
+
}
|
|
593
|
+
return out, nil
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
func loadBannerTypeScriptConfigFile(location string) (any, error) {
|
|
597
|
+
tempDir, err := os.MkdirTemp("", "ttsc-banner-config-")
|
|
598
|
+
if err != nil {
|
|
599
|
+
return nil, fmt.Errorf("@ttsc/banner: create config loader tempdir: %w", err)
|
|
600
|
+
}
|
|
601
|
+
defer os.RemoveAll(tempDir)
|
|
602
|
+
|
|
603
|
+
if err := linkNearestNodeModules(tempDir, filepath.Dir(location)); err != nil {
|
|
604
|
+
return nil, err
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
loader := filepath.Join(tempDir, "loader.mts")
|
|
608
|
+
tsconfig := filepath.Join(tempDir, "tsconfig.json")
|
|
609
|
+
importSpecifier, err := relativeImportSpecifier(tempDir, location)
|
|
610
|
+
if err != nil {
|
|
611
|
+
return nil, err
|
|
612
|
+
}
|
|
613
|
+
importLiteral, err := json.Marshal(importSpecifier)
|
|
614
|
+
if err != nil {
|
|
615
|
+
return nil, fmt.Errorf("@ttsc/banner: encode config import %s: %w", location, err)
|
|
616
|
+
}
|
|
617
|
+
if err := os.WriteFile(loader, []byte(bannerTypeScriptConfigLoaderSource(string(importLiteral))), 0o644); err != nil {
|
|
618
|
+
return nil, fmt.Errorf("@ttsc/banner: write config loader: %w", err)
|
|
619
|
+
}
|
|
620
|
+
if err := os.WriteFile(tsconfig, []byte(typeScriptConfigLoaderTsconfig(loader, location, tempDir)), 0o644); err != nil {
|
|
621
|
+
return nil, fmt.Errorf("@ttsc/banner: write config loader tsconfig: %w", err)
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
args := []string{
|
|
625
|
+
"--project", tsconfig,
|
|
626
|
+
"--cwd", tempDir,
|
|
627
|
+
"--cache-dir", filepath.Join(tempDir, "cache"),
|
|
628
|
+
}
|
|
629
|
+
if tsgo := os.Getenv("TTSC_TSGO_BINARY"); tsgo != "" {
|
|
630
|
+
args = append(args, "--binary", tsgo)
|
|
631
|
+
}
|
|
632
|
+
args = append(args, loader)
|
|
633
|
+
|
|
634
|
+
cmd := ttsxCommand(args...)
|
|
635
|
+
cmd.Env = nodeConfigLoaderEnv(location)
|
|
636
|
+
output, err := cmd.Output()
|
|
637
|
+
if err != nil {
|
|
638
|
+
stderr := ""
|
|
639
|
+
if exit, ok := err.(*exec.ExitError); ok {
|
|
640
|
+
stderr = strings.TrimSpace(string(exit.Stderr))
|
|
641
|
+
}
|
|
642
|
+
if stderr != "" {
|
|
643
|
+
return nil, fmt.Errorf("@ttsc/banner: load TypeScript config file %s: %s", location, stderr)
|
|
644
|
+
}
|
|
645
|
+
return nil, fmt.Errorf("@ttsc/banner: load TypeScript config file %s: %w", location, err)
|
|
646
|
+
}
|
|
647
|
+
var out any
|
|
648
|
+
if err := json.Unmarshal(output, &out); err != nil {
|
|
649
|
+
return nil, fmt.Errorf("@ttsc/banner: parse TypeScript config file %s output: %w", location, err)
|
|
650
|
+
}
|
|
651
|
+
return out, nil
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
func relativeImportSpecifier(fromDir, location string) (string, error) {
|
|
655
|
+
relative, err := filepath.Rel(fromDir, location)
|
|
656
|
+
if err != nil {
|
|
657
|
+
return "", fmt.Errorf("@ttsc/banner: resolve relative config import %s: %w", location, err)
|
|
658
|
+
}
|
|
659
|
+
relative = filepath.ToSlash(relative)
|
|
660
|
+
if strings.HasPrefix(relative, "../") || strings.HasPrefix(relative, "./") {
|
|
661
|
+
return relative, nil
|
|
662
|
+
}
|
|
663
|
+
return "./" + relative, nil
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
func bannerTypeScriptConfigLoaderSource(importLiteral string) string {
|
|
667
|
+
return fmt.Sprintf(`import * as importedConfig from %s;
|
|
668
|
+
|
|
669
|
+
declare const process: {
|
|
670
|
+
stdout: { write(value: string): void };
|
|
671
|
+
stderr: { write(value: string): void };
|
|
672
|
+
exit(code?: number): never;
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
try {
|
|
676
|
+
const value = await resolveConfig(importedConfig);
|
|
677
|
+
process.stdout.write(JSON.stringify(toSerializableBanner(value)));
|
|
678
|
+
} catch (error) {
|
|
679
|
+
process.stderr.write(error instanceof Error && error.stack ? error.stack : String(error));
|
|
680
|
+
process.exit(1);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
async function resolveConfig(value: unknown): Promise<unknown> {
|
|
684
|
+
let current = value;
|
|
685
|
+
for (let i = 0; i < 8; i++) {
|
|
686
|
+
if (isObject(current) && hasOwn(current, "default")) {
|
|
687
|
+
current = current.default;
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
break;
|
|
691
|
+
}
|
|
692
|
+
if (typeof current === "function") {
|
|
693
|
+
return await (current as () => unknown | Promise<unknown>)();
|
|
694
|
+
}
|
|
695
|
+
return current;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function isObject(value: unknown): value is Record<string, unknown> {
|
|
699
|
+
return value !== null && typeof value === "object";
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
function hasOwn(value: Record<string, unknown>, key: string): boolean {
|
|
703
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
function toSerializableBanner(value: unknown): unknown {
|
|
707
|
+
if (typeof value === "string") {
|
|
708
|
+
return value;
|
|
709
|
+
}
|
|
710
|
+
if (isObject(value) && typeof value.text === "string") {
|
|
711
|
+
return { text: value.text };
|
|
712
|
+
}
|
|
713
|
+
throw new Error("config file must export a string or an object with a text string");
|
|
714
|
+
}
|
|
715
|
+
`, importLiteral)
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
func typeScriptConfigLoaderTsconfig(loader, location, outDir string) string {
|
|
719
|
+
content := map[string]any{
|
|
720
|
+
"compilerOptions": map[string]any{
|
|
721
|
+
"allowImportingTsExtensions": true,
|
|
722
|
+
"module": "ESNext",
|
|
723
|
+
"moduleResolution": "bundler",
|
|
724
|
+
"outDir": filepath.ToSlash(filepath.Join(outDir, "out")),
|
|
725
|
+
"rewriteRelativeImportExtensions": true,
|
|
726
|
+
"rootDir": "/",
|
|
727
|
+
"skipLibCheck": true,
|
|
728
|
+
"strict": true,
|
|
729
|
+
"target": "ES2022",
|
|
730
|
+
},
|
|
731
|
+
"files": []string{
|
|
732
|
+
filepath.ToSlash(loader),
|
|
733
|
+
filepath.ToSlash(location),
|
|
734
|
+
},
|
|
735
|
+
}
|
|
736
|
+
body, err := json.MarshalIndent(content, "", " ")
|
|
737
|
+
if err != nil {
|
|
738
|
+
panic(err)
|
|
739
|
+
}
|
|
740
|
+
return string(body)
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
func ttsxCommand(args ...string) *exec.Cmd {
|
|
744
|
+
ttsx := os.Getenv("TTSC_TTSX_BINARY")
|
|
745
|
+
if ttsx == "" {
|
|
746
|
+
ttsx = "ttsx"
|
|
747
|
+
}
|
|
748
|
+
if shouldRunTtsxThroughNode(ttsx) {
|
|
749
|
+
node := os.Getenv("TTSC_NODE_BINARY")
|
|
750
|
+
if node == "" {
|
|
751
|
+
node = "node"
|
|
752
|
+
}
|
|
753
|
+
return exec.Command(node, append([]string{ttsx}, args...)...)
|
|
754
|
+
}
|
|
755
|
+
return exec.Command(ttsx, args...)
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
func shouldRunTtsxThroughNode(binary string) bool {
|
|
759
|
+
switch strings.ToLower(filepath.Ext(binary)) {
|
|
760
|
+
case ".js", ".cjs", ".mjs", ".ts", ".cts", ".mts":
|
|
761
|
+
return true
|
|
762
|
+
default:
|
|
763
|
+
return false
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
func nodeConfigLoaderEnv(location string) []string {
|
|
768
|
+
env := os.Environ()
|
|
769
|
+
parts := make([]string, 0, 2)
|
|
770
|
+
if nodeModules := findNearestNodeModules(filepath.Dir(location)); nodeModules != "" {
|
|
771
|
+
parts = append(parts, nodeModules)
|
|
772
|
+
}
|
|
773
|
+
if existing := os.Getenv("NODE_PATH"); existing != "" {
|
|
774
|
+
parts = append(parts, existing)
|
|
775
|
+
}
|
|
776
|
+
if len(parts) == 0 {
|
|
777
|
+
return env
|
|
778
|
+
}
|
|
779
|
+
return setEnv(env, "NODE_PATH", strings.Join(parts, string(os.PathListSeparator)))
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
func linkNearestNodeModules(tempDir, sourceDir string) error {
|
|
783
|
+
nodeModules := findNearestNodeModules(sourceDir)
|
|
784
|
+
if nodeModules == "" {
|
|
785
|
+
return nil
|
|
786
|
+
}
|
|
787
|
+
link := filepath.Join(tempDir, "node_modules")
|
|
788
|
+
if err := os.Symlink(nodeModules, link); err != nil {
|
|
789
|
+
return fmt.Errorf("@ttsc/banner: link config node_modules %s: %w", nodeModules, err)
|
|
790
|
+
}
|
|
791
|
+
return nil
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
func findNearestNodeModules(start string) string {
|
|
795
|
+
dir := filepath.Clean(start)
|
|
796
|
+
for {
|
|
797
|
+
candidate := filepath.Join(dir, "node_modules")
|
|
798
|
+
if stat, err := os.Stat(candidate); err == nil && stat.IsDir() {
|
|
799
|
+
return candidate
|
|
800
|
+
}
|
|
801
|
+
parent := filepath.Dir(dir)
|
|
802
|
+
if parent == dir {
|
|
803
|
+
return ""
|
|
804
|
+
}
|
|
805
|
+
dir = parent
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
func setEnv(env []string, key, value string) []string {
|
|
810
|
+
prefix := key + "="
|
|
811
|
+
for i, entry := range env {
|
|
812
|
+
if strings.HasPrefix(entry, prefix) {
|
|
813
|
+
env[i] = prefix + value
|
|
814
|
+
return env
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
return append(env, prefix+value)
|
|
818
|
+
}
|
|
819
|
+
|
|
319
820
|
func sanitizeJSDocLine(line string) string {
|
|
320
821
|
return strings.ReplaceAll(line, "*/", "* /")
|
|
321
822
|
}
|
|
@@ -402,9 +903,14 @@ func visitModuleSpecifiers(node *shimast.Node, visit func(*shimast.Node)) {
|
|
|
402
903
|
if arg != nil && arg.Kind == shimast.KindLiteralType {
|
|
403
904
|
visit(arg.AsLiteralTypeNode().Literal)
|
|
404
905
|
}
|
|
906
|
+
case shimast.KindModuleDeclaration:
|
|
907
|
+
decl := node.AsModuleDeclaration()
|
|
908
|
+
if decl != nil {
|
|
909
|
+
visit(decl.Name())
|
|
910
|
+
}
|
|
405
911
|
case shimast.KindCallExpression:
|
|
406
912
|
call := node.AsCallExpression()
|
|
407
|
-
if call
|
|
913
|
+
if isModuleSpecifierCall(call) && call.Arguments != nil && len(call.Arguments.Nodes) > 0 {
|
|
408
914
|
visit(call.Arguments.Nodes[0])
|
|
409
915
|
}
|
|
410
916
|
}
|
|
@@ -414,6 +920,20 @@ func visitModuleSpecifiers(node *shimast.Node, visit func(*shimast.Node)) {
|
|
|
414
920
|
})
|
|
415
921
|
}
|
|
416
922
|
|
|
923
|
+
func isModuleSpecifierCall(call *shimast.CallExpression) bool {
|
|
924
|
+
if call == nil || call.Expression == nil {
|
|
925
|
+
return false
|
|
926
|
+
}
|
|
927
|
+
switch call.Expression.Kind {
|
|
928
|
+
case shimast.KindImportKeyword:
|
|
929
|
+
return true
|
|
930
|
+
case shimast.KindIdentifier:
|
|
931
|
+
return call.Expression.Text() == "require"
|
|
932
|
+
default:
|
|
933
|
+
return false
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
|
|
417
937
|
func (r *pathsRewriter) rewrite(fromSource string, specifier string) (string, bool) {
|
|
418
938
|
if specifier == "" || strings.HasPrefix(specifier, ".") || strings.HasPrefix(specifier, "/") {
|
|
419
939
|
return specifier, false
|
|
@@ -468,6 +988,11 @@ func (r *pathsRewriter) lookupSource(candidate string) (string, bool) {
|
|
|
468
988
|
return source, true
|
|
469
989
|
}
|
|
470
990
|
}
|
|
991
|
+
for _, ext := range []string{".ts", ".tsx", ".mts", ".cts"} {
|
|
992
|
+
if source, ok := r.sourceFiles[normalizePath(filepath.Join(stem, "index"+ext))]; ok {
|
|
993
|
+
return source, true
|
|
994
|
+
}
|
|
995
|
+
}
|
|
471
996
|
return "", false
|
|
472
997
|
}
|
|
473
998
|
|
|
@@ -476,10 +1001,21 @@ func (r *pathsRewriter) outputPathForSource(source string) string {
|
|
|
476
1001
|
return ""
|
|
477
1002
|
}
|
|
478
1003
|
rel, err := filepath.Rel(r.rootDir, source)
|
|
479
|
-
if err != nil ||
|
|
1004
|
+
if err != nil || isOutsideRelativePath(rel) {
|
|
480
1005
|
return ""
|
|
481
1006
|
}
|
|
482
|
-
return normalizePath(filepath.Join(r.outDir, replaceSourceExtension(rel,
|
|
1007
|
+
return normalizePath(filepath.Join(r.outDir, replaceSourceExtension(rel, emittedJavaScriptExtension(rel))))
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
func emittedJavaScriptExtension(source string) string {
|
|
1011
|
+
switch strings.ToLower(filepath.Ext(source)) {
|
|
1012
|
+
case ".mts":
|
|
1013
|
+
return ".mjs"
|
|
1014
|
+
case ".cts":
|
|
1015
|
+
return ".cjs"
|
|
1016
|
+
default:
|
|
1017
|
+
return ".js"
|
|
1018
|
+
}
|
|
483
1019
|
}
|
|
484
1020
|
|
|
485
1021
|
func matchPattern(pattern string, specifier string) (string, bool) {
|
|
@@ -557,6 +1093,14 @@ type callPattern struct {
|
|
|
557
1093
|
}
|
|
558
1094
|
|
|
559
1095
|
func parseStrip(config map[string]any) (*stripRewriter, error) {
|
|
1096
|
+
_, hasCalls := config["calls"]
|
|
1097
|
+
_, hasStatements := config["statements"]
|
|
1098
|
+
if !hasCalls && !hasStatements {
|
|
1099
|
+
config = map[string]any{
|
|
1100
|
+
"calls": []any{"console.log", "console.debug", "assert.*"},
|
|
1101
|
+
"statements": []any{"debugger"},
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
560
1104
|
calls, err := stringArrayConfig(config, "calls")
|
|
561
1105
|
if err != nil {
|
|
562
1106
|
return nil, fmt.Errorf("@ttsc/strip: %w", err)
|
|
@@ -595,7 +1139,7 @@ func filterStatements(list *shimast.NodeList, strip *stripRewriter) {
|
|
|
595
1139
|
if list == nil || len(list.Nodes) == 0 {
|
|
596
1140
|
return
|
|
597
1141
|
}
|
|
598
|
-
out := list.Nodes
|
|
1142
|
+
out := make([]*shimast.Node, 0, len(list.Nodes))
|
|
599
1143
|
for _, stmt := range list.Nodes {
|
|
600
1144
|
if shouldStripStatement(stmt, strip) {
|
|
601
1145
|
continue
|
|
@@ -610,6 +1154,7 @@ func filterChildStatements(node *shimast.Node, strip *stripRewriter) {
|
|
|
610
1154
|
if node == nil {
|
|
611
1155
|
return
|
|
612
1156
|
}
|
|
1157
|
+
filterEmbeddedStatements(node, strip)
|
|
613
1158
|
if node.CanHaveStatements() {
|
|
614
1159
|
filterStatements(node.StatementList(), strip)
|
|
615
1160
|
}
|
|
@@ -619,6 +1164,53 @@ func filterChildStatements(node *shimast.Node, strip *stripRewriter) {
|
|
|
619
1164
|
})
|
|
620
1165
|
}
|
|
621
1166
|
|
|
1167
|
+
func filterEmbeddedStatements(node *shimast.Node, strip *stripRewriter) {
|
|
1168
|
+
switch node.Kind {
|
|
1169
|
+
case shimast.KindIfStatement:
|
|
1170
|
+
stmt := node.AsIfStatement()
|
|
1171
|
+
stmt.ThenStatement = filterEmbeddedStatement(stmt.ThenStatement, strip)
|
|
1172
|
+
stmt.ElseStatement = filterEmbeddedStatement(stmt.ElseStatement, strip)
|
|
1173
|
+
case shimast.KindDoStatement:
|
|
1174
|
+
stmt := node.AsDoStatement()
|
|
1175
|
+
stmt.Statement = filterEmbeddedStatement(stmt.Statement, strip)
|
|
1176
|
+
case shimast.KindWhileStatement:
|
|
1177
|
+
stmt := node.AsWhileStatement()
|
|
1178
|
+
stmt.Statement = filterEmbeddedStatement(stmt.Statement, strip)
|
|
1179
|
+
case shimast.KindForStatement:
|
|
1180
|
+
stmt := node.AsForStatement()
|
|
1181
|
+
stmt.Statement = filterEmbeddedStatement(stmt.Statement, strip)
|
|
1182
|
+
case shimast.KindForInStatement, shimast.KindForOfStatement:
|
|
1183
|
+
stmt := node.AsForInOrOfStatement()
|
|
1184
|
+
stmt.Statement = filterEmbeddedStatement(stmt.Statement, strip)
|
|
1185
|
+
case shimast.KindWithStatement:
|
|
1186
|
+
stmt := node.AsWithStatement()
|
|
1187
|
+
stmt.Statement = filterEmbeddedStatement(stmt.Statement, strip)
|
|
1188
|
+
case shimast.KindLabeledStatement:
|
|
1189
|
+
stmt := node.AsLabeledStatement()
|
|
1190
|
+
stmt.Statement = filterEmbeddedStatement(stmt.Statement, strip)
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
func filterEmbeddedStatement(stmt *shimast.Statement, strip *stripRewriter) *shimast.Statement {
|
|
1195
|
+
if stmt == nil {
|
|
1196
|
+
return nil
|
|
1197
|
+
}
|
|
1198
|
+
if shouldStripStatement(stmt, strip) {
|
|
1199
|
+
return emptyStatement(stmt)
|
|
1200
|
+
}
|
|
1201
|
+
filterChildStatements(stmt, strip)
|
|
1202
|
+
return stmt
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
func emptyStatement(original *shimast.Node) *shimast.Statement {
|
|
1206
|
+
empty := shimast.NewNodeFactory(shimast.NodeFactoryHooks{}).NewEmptyStatement()
|
|
1207
|
+
empty.Flags |= shimast.NodeFlagsSynthesized
|
|
1208
|
+
if original != nil {
|
|
1209
|
+
empty.Loc = original.Loc
|
|
1210
|
+
}
|
|
1211
|
+
return empty
|
|
1212
|
+
}
|
|
1213
|
+
|
|
622
1214
|
func shouldStripStatement(node *shimast.Node, strip *stripRewriter) bool {
|
|
623
1215
|
if node == nil {
|
|
624
1216
|
return false
|