//go:build ignore package main import ( "bytes" "cmp" "flag" "fmt" "go/format" "go/token" "log" "maps" "os" "path/filepath" "regexp" "runtime" "slices" "strconv" "strings" "unicode" "github.com/go-json-experiment/json" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/repo" ) type diagnosticMessage struct { Category string `json:"category"` Code int `json:"code"` ReportsUnnecessary bool `json:"reportsUnnecessary"` ReportsDeprecated bool `json:"reportsDeprecated"` // spelling error here is [sic] in Strada ElidedInCompatibilityPyramid bool `json:"elidedInCompatabilityPyramid"` key string } func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) output := flag.String("output", "", "path to the output diagnostics_generated.go file") flag.Parse() if *output == "" { flag.Usage() return } rawDiagnosticMessages := readRawMessages(filepath.Join(repo.TypeScriptSubmodulePath, "src", "compiler", "diagnosticMessages.json")) _, filename, _, ok := runtime.Caller(0) if !ok { panic("could not get current filename") } filename = filepath.FromSlash(filename) // runtime.Caller always returns forward slashes; https://go.dev/issues/3335, https://go.dev/cl/603275 rawExtraMessages := readRawMessages(filepath.Join(filepath.Dir(filename), "extraDiagnosticMessages.json")) maps.Copy(rawDiagnosticMessages, rawExtraMessages) diagnosticMessages := slices.Collect(maps.Values(rawDiagnosticMessages)) slices.SortFunc(diagnosticMessages, func(a *diagnosticMessage, b *diagnosticMessage) int { return cmp.Compare(a.Code, b.Code) }) var buf bytes.Buffer buf.WriteString("// Code generated by generate.go; DO NOT EDIT.\n") buf.WriteString("\n") buf.WriteString("package diagnostics\n") for _, m := range diagnosticMessages { varName, key := convertPropertyName(m.key, m.Code) fmt.Fprintf(&buf, "var %s = &Message{code: %d, category: Category%s, key: %q, text: %q", varName, m.Code, m.Category, key, m.key) if m.ReportsUnnecessary { buf.WriteString(`, reportsUnnecessary: true`) } if m.ElidedInCompatibilityPyramid { buf.WriteString(`, elidedInCompatibilityPyramid: true`) } if m.ReportsDeprecated { buf.WriteString(`, reportsDeprecated: true`) } buf.WriteString("}\n\n") } formatted, err := format.Source(buf.Bytes()) if err != nil { log.Fatalf("failed to format output: %v", err) return } if err := os.WriteFile(*output, formatted, 0o666); err != nil { log.Fatalf("failed to write output: %v", err) return } } func readRawMessages(p string) map[int]*diagnosticMessage { file, err := os.Open(p) if err != nil { log.Fatalf("failed to open file: %v", err) return nil } defer file.Close() var rawMessages map[string]*diagnosticMessage if err := json.UnmarshalRead(file, &rawMessages); err != nil { log.Fatalf("failed to decode file: %v", err) return nil } codeToMessage := make(map[int]*diagnosticMessage, len(rawMessages)) for k, m := range rawMessages { m.key = k codeToMessage[m.Code] = m } return codeToMessage } var ( multipleUnderscoreRegexp = regexp.MustCompile(`_+`) leadingUnderscoreUnlessDigitRegexp = regexp.MustCompile(`^_+(\D)`) trailingUnderscoreRegexp = regexp.MustCompile(`_$`) ) func convertPropertyName(origName string, code int) (varName string, key string) { var b strings.Builder b.Grow(len(origName)) for _, r := range origName { switch r { case '*': b.WriteString("_Asterisk") case '/': b.WriteString("_Slash") case ':': b.WriteString("_Colon") default: if !unicode.IsLetter(r) && !unicode.IsDigit(r) { b.WriteRune('_') } else { b.WriteRune(r) } } } varName = b.String() // get rid of all multi-underscores varName = multipleUnderscoreRegexp.ReplaceAllString(varName, "_") // remove any leading underscore, unless it is followed by a number. varName = leadingUnderscoreUnlessDigitRegexp.ReplaceAllString(varName, "$1") // get rid of all trailing underscores. varName = trailingUnderscoreRegexp.ReplaceAllString(varName, "") key = varName if len(key) > 100 { key = key[:100] } key = key + "_" + strconv.Itoa(code) if !token.IsExported(varName) { var b strings.Builder b.Grow(len(varName) + 2) if varName[0] == '_' { b.WriteString("X") } else { b.WriteString("X_") } b.WriteString(varName) varName = b.String() } if !token.IsIdentifier(varName) || !token.IsExported(varName) { log.Fatalf("failed to convert property name to exported identifier: %q", origName) } return varName, key }