Skip to content

Add support for extendedDiagnostics json output #835

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions cmd/tsgo/diagnostics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package main

import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"time"
)

// 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, 0o755); 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 errors.New("invalid value for --extendedDiagnostics. Use 'inline' or a path ending with '.json'")
}
47 changes: 9 additions & 38 deletions cmd/tsgo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,11 @@ type cliOptions struct {
}

devel struct {
quiet bool
singleThreaded bool
printTypes bool
pprofDir string
quiet bool
singleThreaded bool
printTypes bool
pprofDir string
extendedDiagnostics string
}
}

Expand Down Expand Up @@ -112,6 +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 text output or a '.json' file path to write to a file.")
flag.Parse()

if len(flag.Args()) > 0 {
Expand Down Expand Up @@ -295,42 +297,11 @@ func runMain() int {
}
stats.add("Total time", totalTime)

stats.print()

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) print() {
nameWidth := 0
valueWidth := 0
for _, r := range t.rows {
nameWidth = max(nameWidth, len(r.name))
valueWidth = max(valueWidth, len(r.value))
if err := OutputStats(&stats, opts.devel.extendedDiagnostics); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
}

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())
return exitCode
}

func identifierCount(p *compiler.Program) int {
Expand Down