From 695bd55bb6e148fbecfce1b9cf53099872c51b38 Mon Sep 17 00:00:00 2001 From: Max schwenk Date: Mon, 28 Apr 2025 23:21:14 -0400 Subject: [PATCH 1/5] Add support for extendedDiagnostics --- cmd/tsgo/main.go | 58 +++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/cmd/tsgo/main.go b/cmd/tsgo/main.go index 1dfac1881b..b0378b958d 100644 --- a/cmd/tsgo/main.go +++ b/cmd/tsgo/main.go @@ -6,6 +6,7 @@ import ( "flag" "fmt" "os" + "path/filepath" "runtime" "runtime/debug" "slices" @@ -64,10 +65,11 @@ type cliOptions struct { } devel struct { - quiet bool - singleThreaded bool - printTypes bool - pprofDir string + quiet bool + singleThreaded bool + printTypes bool + pprofDir string + extendedDiagnostics string } } @@ -112,6 +114,7 @@ func parseArgs() *cliOptions { flag.BoolVar(&opts.devel.singleThreaded, "singleThreaded", false, "Run in single threaded mode.") flag.BoolVar(&opts.devel.printTypes, "printTypes", false, "Print types defined in 'main.ts'.") flag.StringVar(&opts.devel.pprofDir, "pprofDir", "", "Generate pprof CPU/memory profiles to the given directory.") + flag.StringVar(&opts.devel.extendedDiagnostics, "extendedDiagnostics", "", "Output extended diagnostics. Use 'inline' for stdout or a '.json' file path to write to a file.") flag.Parse() if len(flag.Args()) > 0 { @@ -295,7 +298,44 @@ func runMain() int { } stats.add("Total time", totalTime) - stats.print() + if opts.devel.extendedDiagnostics != "" { + jsonData := stats.toJSON() + if opts.devel.extendedDiagnostics == "inline" { + // Output to stdout + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") + if err := enc.Encode(jsonData); err != nil { + fmt.Fprintf(os.Stderr, "Error encoding stats to JSON: %v\n", err) + } + } else if strings.HasSuffix(opts.devel.extendedDiagnostics, ".json") { + // Ensure directory exists + dir := filepath.Dir(opts.devel.extendedDiagnostics) + if dir != "." { + if err := os.MkdirAll(dir, 0755); err != nil { + fmt.Fprintf(os.Stderr, "Error creating directory for stats JSON file: %v\n", err) + return exitCode + } + } + + // Write to file + file, err := os.Create(opts.devel.extendedDiagnostics) + if err != nil { + fmt.Fprintf(os.Stderr, "Error creating stats JSON file: %v\n", err) + return exitCode + } + defer file.Close() + + enc := json.NewEncoder(file) + enc.SetIndent("", " ") + if err := enc.Encode(jsonData); err != nil { + fmt.Fprintf(os.Stderr, "Error writing stats to JSON file: %v\n", err) + } + } else { + fmt.Fprintf(os.Stderr, "Invalid value for --extendedDiagnostics. Use 'inline' or a path ending with '.json'\n") + } + } else { + stats.print() + } return exitCode } @@ -316,6 +356,14 @@ func (t *table) add(name string, value any) { t.rows = append(t.rows, tableRow{name, fmt.Sprint(value)}) } +func (t *table) toJSON() map[string]string { + result := make(map[string]string) + for _, row := range t.rows { + result[row.name] = row.value + } + return result +} + func (t *table) print() { nameWidth := 0 valueWidth := 0 From 7b2189a04cc6e694cbcf12cc22c0c48ca624f079 Mon Sep 17 00:00:00 2001 From: Max schwenk Date: Mon, 28 Apr 2025 23:32:38 -0400 Subject: [PATCH 2/5] Refactor a bit --- cmd/tsgo/diagnostics.go | 107 ++++++++++++++++++++++++++++++++++++++++ cmd/tsgo/main.go | 83 ++----------------------------- 2 files changed, 110 insertions(+), 80 deletions(-) create mode 100644 cmd/tsgo/diagnostics.go diff --git a/cmd/tsgo/diagnostics.go b/cmd/tsgo/diagnostics.go new file mode 100644 index 0000000000..206864e5d8 --- /dev/null +++ b/cmd/tsgo/diagnostics.go @@ -0,0 +1,107 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + "time" +) + +// max returns the larger of x or y +func max(x, y int) int { + if x > y { + return x + } + return y +} + +// tableRow represents a single row in the diagnostic table +type tableRow struct { + name string + value string +} + +// table collects and formats diagnostic data +type table struct { + rows []tableRow +} + +// add adds a statistic to the table, automatically formatting durations +func (t *table) add(name string, value any) { + if d, ok := value.(time.Duration); ok { + value = formatDuration(d) + } + t.rows = append(t.rows, tableRow{name, fmt.Sprint(value)}) +} + +// toJSON converts the table to a map suitable for JSON serialization +func (t *table) toJSON() map[string]string { + result := make(map[string]string) + for _, row := range t.rows { + result[row.name] = row.value + } + return result +} + +// print outputs the table in a formatted way to stdout +func (t *table) print() { + nameWidth := 0 + valueWidth := 0 + for _, r := range t.rows { + nameWidth = max(nameWidth, len(r.name)) + valueWidth = max(valueWidth, len(r.value)) + } + + for _, r := range t.rows { + fmt.Printf("%-*s %*s\n", nameWidth+1, r.name+":", valueWidth, r.value) + } +} + +// formatDuration formats a duration in seconds with 3 decimal places +func formatDuration(d time.Duration) string { + return fmt.Sprintf("%.3fs", d.Seconds()) +} + +// OutputStats handles the output of diagnostic statistics based on the extendedDiagnostics option +// Returns any error encountered during the output process +func OutputStats(stats *table, extendedDiagnostics string) error { + + if extendedDiagnostics == "" { + return nil + } + + if extendedDiagnostics == "inline" { + stats.print() + return nil + } + + jsonData := stats.toJSON() + + if strings.HasSuffix(extendedDiagnostics, ".json") { + // Ensure directory exists + dir := filepath.Dir(extendedDiagnostics) + if dir != "." { + if err := os.MkdirAll(dir, 0755); err != nil { + return fmt.Errorf("error creating directory for stats JSON file: %w", err) + } + } + + // Write to file + file, err := os.Create(extendedDiagnostics) + if err != nil { + return fmt.Errorf("error creating stats JSON file: %w", err) + } + defer file.Close() + + enc := json.NewEncoder(file) + enc.SetIndent("", " ") + if err := enc.Encode(jsonData); err != nil { + return fmt.Errorf("error writing stats to JSON file: %w", err) + } + return nil + } + + return fmt.Errorf("invalid value for --extendedDiagnostics. Use 'inline' or a path ending with '.json'") +} diff --git a/cmd/tsgo/main.go b/cmd/tsgo/main.go index b0378b958d..5aac6d7186 100644 --- a/cmd/tsgo/main.go +++ b/cmd/tsgo/main.go @@ -6,7 +6,6 @@ import ( "flag" "fmt" "os" - "path/filepath" "runtime" "runtime/debug" "slices" @@ -114,7 +113,7 @@ func parseArgs() *cliOptions { flag.BoolVar(&opts.devel.singleThreaded, "singleThreaded", false, "Run in single threaded mode.") flag.BoolVar(&opts.devel.printTypes, "printTypes", false, "Print types defined in 'main.ts'.") flag.StringVar(&opts.devel.pprofDir, "pprofDir", "", "Generate pprof CPU/memory profiles to the given directory.") - flag.StringVar(&opts.devel.extendedDiagnostics, "extendedDiagnostics", "", "Output extended diagnostics. Use 'inline' for stdout or a '.json' file path to write to a file.") + flag.StringVar(&opts.devel.extendedDiagnostics, "extendedDiagnostics", "", "Output extended diagnostics. Use 'inline' for stdout text output or a '.json' file path to write to a file.") flag.Parse() if len(flag.Args()) > 0 { @@ -298,89 +297,13 @@ func runMain() int { } stats.add("Total time", totalTime) - if opts.devel.extendedDiagnostics != "" { - jsonData := stats.toJSON() - if opts.devel.extendedDiagnostics == "inline" { - // Output to stdout - enc := json.NewEncoder(os.Stdout) - enc.SetIndent("", " ") - if err := enc.Encode(jsonData); err != nil { - fmt.Fprintf(os.Stderr, "Error encoding stats to JSON: %v\n", err) - } - } else if strings.HasSuffix(opts.devel.extendedDiagnostics, ".json") { - // Ensure directory exists - dir := filepath.Dir(opts.devel.extendedDiagnostics) - if dir != "." { - if err := os.MkdirAll(dir, 0755); err != nil { - fmt.Fprintf(os.Stderr, "Error creating directory for stats JSON file: %v\n", err) - return exitCode - } - } - - // Write to file - file, err := os.Create(opts.devel.extendedDiagnostics) - if err != nil { - fmt.Fprintf(os.Stderr, "Error creating stats JSON file: %v\n", err) - return exitCode - } - defer file.Close() - - enc := json.NewEncoder(file) - enc.SetIndent("", " ") - if err := enc.Encode(jsonData); err != nil { - fmt.Fprintf(os.Stderr, "Error writing stats to JSON file: %v\n", err) - } - } else { - fmt.Fprintf(os.Stderr, "Invalid value for --extendedDiagnostics. Use 'inline' or a path ending with '.json'\n") - } - } else { - stats.print() + if err := OutputStats(&stats, opts.devel.extendedDiagnostics); err != nil { + fmt.Fprintf(os.Stderr, "%v\n", err) } return exitCode } -type tableRow struct { - name string - value string -} - -type table struct { - rows []tableRow -} - -func (t *table) add(name string, value any) { - if d, ok := value.(time.Duration); ok { - value = formatDuration(d) - } - t.rows = append(t.rows, tableRow{name, fmt.Sprint(value)}) -} - -func (t *table) toJSON() map[string]string { - result := make(map[string]string) - for _, row := range t.rows { - result[row.name] = row.value - } - return result -} - -func (t *table) print() { - nameWidth := 0 - valueWidth := 0 - for _, r := range t.rows { - nameWidth = max(nameWidth, len(r.name)) - valueWidth = max(valueWidth, len(r.value)) - } - - for _, r := range t.rows { - fmt.Printf("%-*s %*s\n", nameWidth+1, r.name+":", valueWidth, r.value) - } -} - -func formatDuration(d time.Duration) string { - return fmt.Sprintf("%.3fs", d.Seconds()) -} - func identifierCount(p *compiler.Program) int { count := 0 for _, file := range p.SourceFiles() { From 3c263eaba80c524a34626fdea33a049a00be9189 Mon Sep 17 00:00:00 2001 From: Max schwenk Date: Mon, 28 Apr 2025 23:41:43 -0400 Subject: [PATCH 3/5] Remove method built into language --- cmd/tsgo/diagnostics.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/cmd/tsgo/diagnostics.go b/cmd/tsgo/diagnostics.go index 206864e5d8..f72d6d799d 100644 --- a/cmd/tsgo/diagnostics.go +++ b/cmd/tsgo/diagnostics.go @@ -9,14 +9,6 @@ import ( "time" ) -// max returns the larger of x or y -func max(x, y int) int { - if x > y { - return x - } - return y -} - // tableRow represents a single row in the diagnostic table type tableRow struct { name string From 5a36edd7204b563a20c9a263d6e90fe6422765c0 Mon Sep 17 00:00:00 2001 From: Max schwenk Date: Mon, 28 Apr 2025 23:58:53 -0400 Subject: [PATCH 4/5] Listen to my lint friend --- cmd/tsgo/diagnostics.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/tsgo/diagnostics.go b/cmd/tsgo/diagnostics.go index f72d6d799d..dea1f3c3aa 100644 --- a/cmd/tsgo/diagnostics.go +++ b/cmd/tsgo/diagnostics.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "errors" "fmt" "os" "path/filepath" @@ -95,5 +96,5 @@ func OutputStats(stats *table, extendedDiagnostics string) error { return nil } - return fmt.Errorf("invalid value for --extendedDiagnostics. Use 'inline' or a path ending with '.json'") + return errors.New("invalid value for --extendedDiagnostics. Use 'inline' or a path ending with '.json'") } From d8be0e18c2c8c6d256509ff321ea11b99f37ea7d Mon Sep 17 00:00:00 2001 From: Max schwenk Date: Tue, 29 Apr 2025 00:08:14 -0400 Subject: [PATCH 5/5] I cannot seem to run check:format locally --- cmd/tsgo/diagnostics.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmd/tsgo/diagnostics.go b/cmd/tsgo/diagnostics.go index dea1f3c3aa..8dd62fd015 100644 --- a/cmd/tsgo/diagnostics.go +++ b/cmd/tsgo/diagnostics.go @@ -60,7 +60,6 @@ func formatDuration(d time.Duration) string { // OutputStats handles the output of diagnostic statistics based on the extendedDiagnostics option // Returns any error encountered during the output process func OutputStats(stats *table, extendedDiagnostics string) error { - if extendedDiagnostics == "" { return nil } @@ -76,7 +75,7 @@ func OutputStats(stats *table, extendedDiagnostics string) error { // Ensure directory exists dir := filepath.Dir(extendedDiagnostics) if dir != "." { - if err := os.MkdirAll(dir, 0755); err != nil { + if err := os.MkdirAll(dir, 0o755); err != nil { return fmt.Errorf("error creating directory for stats JSON file: %w", err) } }