vendor tsgo
This commit is contained in:
159
tools/tsgo/internal/diagnostics/diagnostics.go
Normal file
159
tools/tsgo/internal/diagnostics/diagnostics.go
Normal file
@@ -0,0 +1,159 @@
|
||||
// Package diagnostics contains generated localizable diagnostic messages.
|
||||
package diagnostics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/locale"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
//go:generate go run generate.go -diagnostics ./diagnostics_generated.go -loc ./loc_generated.go -locdir ./loc
|
||||
//go:generate go tool golang.org/x/tools/cmd/stringer -type=Category -output=stringer_generated.go
|
||||
//go:generate npx dprint fmt diagnostics_generated.go loc_generated.go stringer_generated.go
|
||||
|
||||
type Category int32
|
||||
|
||||
const (
|
||||
CategoryWarning Category = iota
|
||||
CategoryError
|
||||
CategorySuggestion
|
||||
CategoryMessage
|
||||
)
|
||||
|
||||
func (category Category) Name() string {
|
||||
switch category {
|
||||
case CategoryWarning:
|
||||
return "warning"
|
||||
case CategoryError:
|
||||
return "error"
|
||||
case CategorySuggestion:
|
||||
return "suggestion"
|
||||
case CategoryMessage:
|
||||
return "message"
|
||||
}
|
||||
panic("Unhandled diagnostic category")
|
||||
}
|
||||
|
||||
type Key string
|
||||
|
||||
type Message struct {
|
||||
code int32
|
||||
category Category
|
||||
key Key
|
||||
text string
|
||||
reportsUnnecessary bool
|
||||
elidedInCompatibilityPyramid bool
|
||||
reportsDeprecated bool
|
||||
}
|
||||
|
||||
func (m *Message) Code() int32 { return m.code }
|
||||
func (m *Message) Category() Category { return m.category }
|
||||
func (m *Message) Key() Key { return m.key }
|
||||
func (m *Message) ReportsUnnecessary() bool { return m.reportsUnnecessary }
|
||||
func (m *Message) ElidedInCompatibilityPyramid() bool { return m.elidedInCompatibilityPyramid }
|
||||
func (m *Message) ReportsDeprecated() bool { return m.reportsDeprecated }
|
||||
|
||||
// For debugging only.
|
||||
func (m *Message) String() string {
|
||||
return m.text
|
||||
}
|
||||
|
||||
func (m *Message) Localize(locale locale.Locale, args ...any) string {
|
||||
return Localize(locale, m, "", StringifyArgs(args)...)
|
||||
}
|
||||
|
||||
func Localize(locale locale.Locale, message *Message, key Key, args ...string) string {
|
||||
if message == nil {
|
||||
message = keyToMessage(key)
|
||||
}
|
||||
if message == nil {
|
||||
panic("Unknown diagnostic message: " + string(key))
|
||||
}
|
||||
|
||||
text := message.text
|
||||
if localized, ok := getLocalizedMessages(language.Tag(locale))[message.key]; ok {
|
||||
text = localized
|
||||
}
|
||||
|
||||
return Format(text, args)
|
||||
}
|
||||
|
||||
var localizedMessagesCache sync.Map // map[language.Tag]map[Key]string
|
||||
|
||||
func getLocalizedMessages(loc language.Tag) map[Key]string {
|
||||
if loc == language.Und {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check cache first
|
||||
if cached, ok := localizedMessagesCache.Load(loc); ok {
|
||||
if cached == nil {
|
||||
return nil
|
||||
}
|
||||
return cached.(map[Key]string)
|
||||
}
|
||||
|
||||
var messages map[Key]string
|
||||
|
||||
_, index, confidence := matcher.Match(loc)
|
||||
if confidence >= language.Low && index >= 0 && index < len(localeFuncs) {
|
||||
if fn := localeFuncs[index]; fn != nil {
|
||||
messages = fn()
|
||||
}
|
||||
}
|
||||
|
||||
localizedMessagesCache.Store(loc, messages)
|
||||
return messages
|
||||
}
|
||||
|
||||
var placeholderRegexp = regexp.MustCompile(`{(\d+)}`)
|
||||
|
||||
func Format(text string, args []string) string {
|
||||
if len(args) == 0 {
|
||||
return text
|
||||
}
|
||||
|
||||
// Replace invalid UTF-8 with Unicode replacement character
|
||||
args = core.SameMap(args, func(arg string) string {
|
||||
return strings.ToValidUTF8(arg, "\uFFFD")
|
||||
})
|
||||
|
||||
return placeholderRegexp.ReplaceAllStringFunc(text, func(match string) string {
|
||||
index, err := strconv.ParseInt(match[1:len(match)-1], 10, 0)
|
||||
if err != nil || int(index) >= len(args) {
|
||||
panic("Invalid formatting placeholder")
|
||||
}
|
||||
return args[int(index)]
|
||||
})
|
||||
}
|
||||
|
||||
func StringifyArgs(args []any) []string {
|
||||
if len(args) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := make([]string, len(args))
|
||||
for i, arg := range args {
|
||||
if s, ok := arg.(string); ok {
|
||||
result[i] = s
|
||||
} else {
|
||||
result[i] = fmt.Sprintf("%v", arg)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func NewAdHocMessage(message string) *Message {
|
||||
return &Message{
|
||||
code: -1,
|
||||
category: CategoryError,
|
||||
key: "-1",
|
||||
text: message,
|
||||
}
|
||||
}
|
||||
8626
tools/tsgo/internal/diagnostics/diagnostics_generated.go
Normal file
8626
tools/tsgo/internal/diagnostics/diagnostics_generated.go
Normal file
File diff suppressed because it is too large
Load Diff
145
tools/tsgo/internal/diagnostics/diagnostics_test.go
Normal file
145
tools/tsgo/internal/diagnostics/diagnostics_test.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package diagnostics
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/locale"
|
||||
"golang.org/x/text/language"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestLocalize(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
message *Message
|
||||
locale locale.Locale
|
||||
args []any
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "english default",
|
||||
message: Identifier_expected,
|
||||
locale: locale.Locale(language.English),
|
||||
expected: "Identifier expected.",
|
||||
},
|
||||
{
|
||||
name: "undefined locale uses english",
|
||||
message: Identifier_expected,
|
||||
locale: locale.Locale(language.Und),
|
||||
expected: "Identifier expected.",
|
||||
},
|
||||
{
|
||||
name: "with single argument",
|
||||
message: X_0_expected,
|
||||
locale: locale.Locale(language.English),
|
||||
args: []any{")"},
|
||||
expected: "')' expected.",
|
||||
},
|
||||
{
|
||||
name: "with multiple arguments",
|
||||
message: The_parser_expected_to_find_a_1_to_match_the_0_token_here,
|
||||
locale: locale.Locale(language.English),
|
||||
args: []any{"{", "}"},
|
||||
expected: "The parser expected to find a '}' to match the '{' token here.",
|
||||
},
|
||||
{
|
||||
name: "fallback to english for unknown locale",
|
||||
message: Identifier_expected,
|
||||
locale: locale.Locale(language.MustParse("af-ZA")),
|
||||
expected: "Identifier expected.",
|
||||
},
|
||||
{
|
||||
name: "german",
|
||||
message: Identifier_expected,
|
||||
locale: locale.Locale(language.MustParse("de-DE")),
|
||||
expected: "Es wurde ein Bezeichner erwartet.",
|
||||
},
|
||||
{
|
||||
name: "french",
|
||||
message: Identifier_expected,
|
||||
locale: locale.Locale(language.MustParse("fr-FR")),
|
||||
expected: "Identificateur attendu.",
|
||||
},
|
||||
{
|
||||
name: "spanish",
|
||||
message: Identifier_expected,
|
||||
locale: locale.Locale(language.MustParse("es-ES")),
|
||||
expected: "Se esperaba un identificador.",
|
||||
},
|
||||
{
|
||||
name: "japanese",
|
||||
message: Identifier_expected,
|
||||
locale: locale.Locale(language.MustParse("ja-JP")),
|
||||
expected: "識別子が必要です。",
|
||||
},
|
||||
{
|
||||
name: "chinese simplified",
|
||||
message: Identifier_expected,
|
||||
locale: locale.Locale(language.MustParse("zh-CN")),
|
||||
expected: "应为标识符。",
|
||||
},
|
||||
{
|
||||
name: "korean",
|
||||
message: Identifier_expected,
|
||||
locale: locale.Locale(language.MustParse("ko-KR")),
|
||||
expected: "식별자가 필요합니다.",
|
||||
},
|
||||
{
|
||||
name: "russian",
|
||||
message: Identifier_expected,
|
||||
locale: locale.Locale(language.MustParse("ru-RU")),
|
||||
expected: "Ожидался идентификатор.",
|
||||
},
|
||||
{
|
||||
name: "german with args",
|
||||
message: X_0_expected,
|
||||
locale: locale.Locale(language.MustParse("de-DE")),
|
||||
args: []any{")"},
|
||||
expected: "\")\" wurde erwartet.",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
result := tt.message.Localize(tt.locale, tt.args...)
|
||||
assert.Equal(t, result, tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLocalize_ByKey(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
key Key
|
||||
locale locale.Locale
|
||||
args []string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "by key without args",
|
||||
key: "Identifier_expected_1003",
|
||||
locale: locale.Locale(language.English),
|
||||
expected: "Identifier expected.",
|
||||
},
|
||||
{
|
||||
name: "by key with args",
|
||||
key: "_0_expected_1005",
|
||||
locale: locale.Locale(language.English),
|
||||
args: []string{")"},
|
||||
expected: "')' expected.",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
result := Localize(tt.locale, nil, tt.key, tt.args...)
|
||||
assert.Equal(t, result, tt.expected)
|
||||
})
|
||||
}
|
||||
}
|
||||
134
tools/tsgo/internal/diagnostics/extraDiagnosticMessages.json
Normal file
134
tools/tsgo/internal/diagnostics/extraDiagnosticMessages.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"Do not print diagnostics.": {
|
||||
"category": "Message",
|
||||
"code": 100000
|
||||
},
|
||||
"Run in single threaded mode.": {
|
||||
"category": "Message",
|
||||
"code": 100001
|
||||
},
|
||||
"Generate pprof CPU/memory profiles to the given directory.": {
|
||||
"category": "Message",
|
||||
"code": 100002
|
||||
},
|
||||
"Set the number of checkers per project.": {
|
||||
"category": "Message",
|
||||
"code": 100003
|
||||
},
|
||||
"4, unless --singleThreaded is passed.": {
|
||||
"category": "Message",
|
||||
"code": 100004
|
||||
},
|
||||
"{0} references": {
|
||||
"category": "Message",
|
||||
"code": 100005
|
||||
},
|
||||
"1 reference": {
|
||||
"category": "Message",
|
||||
"code": 100006
|
||||
},
|
||||
"{0} implementations": {
|
||||
"category": "Message",
|
||||
"code": 100007
|
||||
},
|
||||
"1 implementation": {
|
||||
"category": "Message",
|
||||
"code": 100008
|
||||
},
|
||||
"Set the number of projects to build concurrently.": {
|
||||
"category": "Message",
|
||||
"code": 100009
|
||||
},
|
||||
"Non-relative paths are not allowed. Did you forget a leading './'?": {
|
||||
"category": "Error",
|
||||
"code": 5090
|
||||
},
|
||||
"A JSDoc '@type' tag on a function must have a signature with the correct number of arguments.": {
|
||||
"category": "Error",
|
||||
"code": 8030
|
||||
},
|
||||
"Failed to delete file '{0}'.": {
|
||||
"category": "Message",
|
||||
"code": 6353
|
||||
},
|
||||
"Project '{0}' is out of date because config file does not exist.": {
|
||||
"category": "Message",
|
||||
"code": 6401
|
||||
},
|
||||
"Project '{0}' is out of date because input '{1}' does not exist.": {
|
||||
"category": "Message",
|
||||
"code": 6420
|
||||
},
|
||||
"Project '{0}' is out of date because it has errors.": {
|
||||
"category": "Message",
|
||||
"code": 6423
|
||||
},
|
||||
"Multiple 'module.exports' assignments cannot be serialized for declaration emit.": {
|
||||
"category": "Error",
|
||||
"code": 6424
|
||||
},
|
||||
"Nested CommonJS export constructs cannot be serialized for declaration emit.": {
|
||||
"category": "Error",
|
||||
"code": 6425
|
||||
},
|
||||
"Locale must be an IETF BCP 47 language tag. Examples: '{0}', '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 6048
|
||||
},
|
||||
"Ignore the tsconfig found and build with commandline options and files.": {
|
||||
"category": "Message",
|
||||
"code": 1549
|
||||
},
|
||||
"tsconfig.json is present but will not be loaded if files are specified on commandline. Use '--ignoreConfig' to skip this error.": {
|
||||
"category": "Error",
|
||||
"code": 5112
|
||||
},
|
||||
"Option '--incremental' is only valid with a known configuration file (like 'tsconfig.json') or when '--tsBuildInfoFile' is explicitly provided.": {
|
||||
"category": "Error",
|
||||
"code": 5074
|
||||
},
|
||||
"Option '{0}' requires value to be greater than '{1}'.": {
|
||||
"category": "Error",
|
||||
"code": 5002
|
||||
},
|
||||
"Deduplicate packages with the same name and version.": {
|
||||
"category": "Message",
|
||||
"code": 100011
|
||||
},
|
||||
"Loading": {
|
||||
"category": "Message",
|
||||
"code": 100012
|
||||
},
|
||||
"Project '{0}'": {
|
||||
"category": "Message",
|
||||
"code": 100014
|
||||
},
|
||||
"Installing types for '{0}'": {
|
||||
"category": "Message",
|
||||
"code": 100013
|
||||
},
|
||||
"Fix All": {
|
||||
"category": "Message",
|
||||
"code": 100015
|
||||
},
|
||||
"Organize Imports": {
|
||||
"category": "Message",
|
||||
"code": 100016
|
||||
},
|
||||
"Remove Unused Imports": {
|
||||
"category": "Message",
|
||||
"code": 100017
|
||||
},
|
||||
"Sort Imports": {
|
||||
"category": "Message",
|
||||
"code": 100018
|
||||
},
|
||||
"File rename is not supported by the editor": {
|
||||
"category": "Error",
|
||||
"code": 8040
|
||||
},
|
||||
"JSDoc comment": {
|
||||
"category": "Message",
|
||||
"code": 100019
|
||||
}
|
||||
}
|
||||
460
tools/tsgo/internal/diagnostics/generate.go
Normal file
460
tools/tsgo/internal/diagnostics/generate.go
Normal file
@@ -0,0 +1,460 @@
|
||||
//go:build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"cmp"
|
||||
"compress/gzip"
|
||||
"encoding/xml"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"go/token"
|
||||
"log"
|
||||
"maps"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/collections"
|
||||
"github.com/microsoft/typescript-go/internal/json"
|
||||
"github.com/microsoft/typescript-go/internal/repo"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type LCX struct {
|
||||
TgtCul string `xml:"TgtCul,attr"`
|
||||
RootItems []RootItem `xml:"Item"`
|
||||
}
|
||||
|
||||
type RootItem struct {
|
||||
ItemId string `xml:"ItemId,attr"`
|
||||
Items []StringTableItem `xml:"Item"`
|
||||
}
|
||||
|
||||
type StringTableItem struct {
|
||||
ItemId string `xml:"ItemId,attr"`
|
||||
Items []LocalizedItem `xml:"Item"`
|
||||
}
|
||||
|
||||
type LocalizedItem struct {
|
||||
ItemId string `xml:"ItemId,attr"`
|
||||
Str Str `xml:"Str"`
|
||||
}
|
||||
|
||||
type Str struct {
|
||||
Val string `xml:"Val"`
|
||||
Tgt *Tgt `xml:"Tgt"`
|
||||
}
|
||||
|
||||
type Tgt struct {
|
||||
Val string `xml:"Val"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
diagnosticsOutput := flag.String("diagnostics", "", "path to the output diagnostics_generated.go file")
|
||||
locOutput := flag.String("loc", "", "path to the output loc_generated.go file")
|
||||
locDir := flag.String("locdir", "", "directory to write locale .json.gz files")
|
||||
flag.Parse()
|
||||
|
||||
if *diagnosticsOutput == "" || *locOutput == "" || *locDir == "" {
|
||||
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)
|
||||
})
|
||||
|
||||
// Collect known keys for filtering localizations
|
||||
knownKeys := make(map[string]bool, len(diagnosticMessages))
|
||||
for _, m := range diagnosticMessages {
|
||||
_, key := convertPropertyName(m.key, m.Code)
|
||||
knownKeys[key] = true
|
||||
}
|
||||
|
||||
// Generate diagnostics file
|
||||
diagnosticsBuf := generateDiagnostics(diagnosticMessages)
|
||||
|
||||
formatted, err := format.Source(diagnosticsBuf.Bytes())
|
||||
if err != nil {
|
||||
log.Fatalf("failed to format diagnostics output: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.WriteFile(*diagnosticsOutput, formatted, 0o666); err != nil {
|
||||
log.Fatalf("failed to write diagnostics output: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Generate localizations file
|
||||
locBuf := generateLocalizations(knownKeys, *locDir)
|
||||
|
||||
formatted, err = format.Source(locBuf.Bytes())
|
||||
if err != nil {
|
||||
log.Fatalf("failed to format localizations output: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.WriteFile(*locOutput, formatted, 0o666); err != nil {
|
||||
log.Fatalf("failed to write localizations output: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func generateDiagnostics(diagnosticMessages []*diagnosticMessage) *bytes.Buffer {
|
||||
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")
|
||||
}
|
||||
|
||||
buf.WriteString("func keyToMessage(key Key) *Message {\n")
|
||||
buf.WriteString("\tswitch key {\n")
|
||||
for _, m := range diagnosticMessages {
|
||||
_, key := convertPropertyName(m.key, m.Code)
|
||||
varName, _ := convertPropertyName(m.key, m.Code)
|
||||
fmt.Fprintf(&buf, "\tcase %q:\n\t\treturn %s\n", key, varName)
|
||||
}
|
||||
buf.WriteString("\tdefault:\n\t\treturn nil\n")
|
||||
buf.WriteString("\t}\n")
|
||||
buf.WriteString("}\n")
|
||||
|
||||
return &buf
|
||||
}
|
||||
|
||||
func generateLocalizations(knownKeys map[string]bool, locDir string) *bytes.Buffer {
|
||||
var buf bytes.Buffer
|
||||
|
||||
buf.WriteString("// Code generated by generate.go; DO NOT EDIT.\n")
|
||||
buf.WriteString("\n")
|
||||
buf.WriteString("package diagnostics\n")
|
||||
buf.WriteString("\n")
|
||||
buf.WriteString("import (\n")
|
||||
buf.WriteString("\t\"compress/gzip\"\n")
|
||||
buf.WriteString("\t_ \"embed\"\n")
|
||||
buf.WriteString("\t\"strings\"\n")
|
||||
buf.WriteString("\t\"sync\"\n")
|
||||
buf.WriteString("\t\"golang.org/x/text/language\"\n")
|
||||
buf.WriteString("\t\"github.com/microsoft/typescript-go/internal/json\"\n")
|
||||
buf.WriteString(")\n")
|
||||
|
||||
// Remove and recreate the loc directory for a clean state
|
||||
if err := os.RemoveAll(locDir); err != nil {
|
||||
log.Fatalf("failed to remove locale directory: %v", err)
|
||||
}
|
||||
if err := os.MkdirAll(locDir, 0o755); err != nil {
|
||||
log.Fatalf("failed to create locale directory: %v", err)
|
||||
}
|
||||
|
||||
// Generate locale maps
|
||||
localeFiles, err := filepath.Glob(filepath.Join(repo.TypeScriptSubmodulePath(), "src", "loc", "lcl", "*", "diagnosticMessages", "diagnosticMessages.generated.json.lcl"))
|
||||
if err != nil {
|
||||
log.Fatalf("failed to find locale files: %v", err)
|
||||
}
|
||||
if len(localeFiles) == 0 {
|
||||
log.Fatalf("no locale files found in %s", filepath.Join(repo.TypeScriptSubmodulePath(), "src", "loc", "lcl"))
|
||||
}
|
||||
slices.Sort(localeFiles)
|
||||
|
||||
type localeInfo struct {
|
||||
varName string
|
||||
tgtCul string
|
||||
canonical string // canonical lowercase form (e.g., "zh-cn", "pt-br")
|
||||
lang string
|
||||
messages map[string]string
|
||||
filename string
|
||||
}
|
||||
|
||||
var locales []localeInfo
|
||||
|
||||
for _, localeFile := range localeFiles {
|
||||
localizedMessages, tgtCul := readLocalizedMessages(localeFile)
|
||||
if len(localizedMessages) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Filter to only known keys
|
||||
for key := range localizedMessages {
|
||||
if !knownKeys[key] {
|
||||
delete(localizedMessages, key)
|
||||
}
|
||||
}
|
||||
|
||||
if len(localizedMessages) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Convert locale code to valid Go identifier
|
||||
localeVar := strings.ReplaceAll(strings.ReplaceAll(tgtCul, "-", ""), "_", "")
|
||||
|
||||
// Parse the locale using language.Tag to get canonical forms
|
||||
tag, err := language.Parse(tgtCul)
|
||||
if err != nil {
|
||||
log.Printf("failed to parse locale %q: %v", tgtCul, err)
|
||||
continue
|
||||
}
|
||||
|
||||
base, _ := tag.Base()
|
||||
lang := strings.ToLower(base.String())
|
||||
|
||||
// Get canonical form (lowercase with dash)
|
||||
canonical := strings.ToLower(tgtCul)
|
||||
|
||||
// Filename for the JSON.gz file (use the original tgtCul as standard language tag)
|
||||
filename := fmt.Sprintf("%s.json.gz", tgtCul)
|
||||
|
||||
// Write the JSON.gz file
|
||||
// Convert map to OrderedMap with sorted keys for consistent ordering
|
||||
keys := slices.Sorted(maps.Keys(localizedMessages))
|
||||
var orderedMessages collections.OrderedMap[string, string]
|
||||
for _, key := range keys {
|
||||
orderedMessages.Set(key, localizedMessages[key])
|
||||
}
|
||||
jsonData, err := json.Marshal(&orderedMessages)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to marshal locale %s: %v", tgtCul, err)
|
||||
}
|
||||
|
||||
var compressed bytes.Buffer
|
||||
gzipWriter, err := gzip.NewWriterLevel(&compressed, gzip.BestCompression)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create gzip writer for locale %s: %v", tgtCul, err)
|
||||
}
|
||||
if _, err := gzipWriter.Write(jsonData); err != nil {
|
||||
log.Fatalf("failed to compress locale %s: %v", tgtCul, err)
|
||||
}
|
||||
if err := gzipWriter.Close(); err != nil {
|
||||
log.Fatalf("failed to close gzip writer for locale %s: %v", tgtCul, err)
|
||||
}
|
||||
|
||||
outputPath := filepath.Join(locDir, filename)
|
||||
if err := os.WriteFile(outputPath, compressed.Bytes(), 0o644); err != nil {
|
||||
log.Fatalf("failed to write locale file %s: %v", outputPath, err)
|
||||
}
|
||||
|
||||
locales = append(locales, localeInfo{
|
||||
varName: localeVar,
|
||||
tgtCul: tgtCul,
|
||||
canonical: canonical,
|
||||
lang: lang,
|
||||
messages: localizedMessages,
|
||||
filename: filename,
|
||||
})
|
||||
}
|
||||
|
||||
// Generate matcher with inlined tags
|
||||
// English is first (index 0) as the default/fallback with no translation needed
|
||||
buf.WriteString("\nvar matcher = language.NewMatcher([]language.Tag{\n")
|
||||
buf.WriteString("\tlanguage.English,\n")
|
||||
for _, loc := range locales {
|
||||
fmt.Fprintf(&buf, "\tlanguage.MustParse(%q),\n", loc.tgtCul)
|
||||
}
|
||||
buf.WriteString("})\n")
|
||||
|
||||
// Generate index-to-function map for matcher results
|
||||
// English (index 0) returns nil since we use the default English text
|
||||
buf.WriteString("\nvar localeFuncs = []func() map[Key]string{\n")
|
||||
buf.WriteString("\tnil, // English (default)\n")
|
||||
for _, loc := range locales {
|
||||
fmt.Fprintf(&buf, "\t%s,\n", loc.varName)
|
||||
}
|
||||
buf.WriteString("}\n")
|
||||
|
||||
// Generate helper function for decompressing locale data
|
||||
buf.WriteString("\nfunc loadLocaleData(data string) map[Key]string {\n")
|
||||
buf.WriteString("\tgr, err := gzip.NewReader(strings.NewReader(data))\n")
|
||||
buf.WriteString("\tif err != nil {\n")
|
||||
buf.WriteString("\t\tpanic(\"failed to create gzip reader: \" + err.Error())\n")
|
||||
buf.WriteString("\t}\n")
|
||||
buf.WriteString("\tdefer gr.Close()\n")
|
||||
buf.WriteString("\tvar result map[Key]string\n")
|
||||
buf.WriteString("\tif err := json.UnmarshalRead(gr, &result); err != nil {\n")
|
||||
buf.WriteString("\t\tpanic(\"failed to unmarshal locale data: \" + err.Error())\n")
|
||||
buf.WriteString("\t}\n")
|
||||
buf.WriteString("\treturn result\n")
|
||||
buf.WriteString("}\n")
|
||||
|
||||
// Generate embed directives, vars, and loader functions interleaved at the bottom
|
||||
for _, loc := range locales {
|
||||
fmt.Fprintf(&buf, "\n//go:embed loc/%s\n", loc.filename)
|
||||
fmt.Fprintf(&buf, "var %sData string\n", loc.varName)
|
||||
fmt.Fprintf(&buf, "\nvar %s = sync.OnceValue(func() map[Key]string {\n", loc.varName)
|
||||
fmt.Fprintf(&buf, "\treturn loadLocaleData(%sData)\n", loc.varName)
|
||||
buf.WriteString("})\n")
|
||||
}
|
||||
|
||||
return &buf
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func readLocalizedMessages(p string) (map[string]string, string) {
|
||||
file, err := os.Open(p)
|
||||
if err != nil {
|
||||
log.Printf("failed to open locale file %s: %v", p, err)
|
||||
return nil, ""
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var lcx LCX
|
||||
if err := xml.NewDecoder(file).Decode(&lcx); err != nil {
|
||||
log.Printf("failed to decode locale file %s: %v", p, err)
|
||||
return nil, ""
|
||||
}
|
||||
|
||||
messages := make(map[string]string)
|
||||
|
||||
// Navigate the nested Item structure
|
||||
for _, rootItem := range lcx.RootItems {
|
||||
for _, stringTable := range rootItem.Items {
|
||||
for _, item := range stringTable.Items {
|
||||
// ItemId has format ";key_code", remove the leading semicolon
|
||||
itemId := strings.TrimPrefix(item.ItemId, ";")
|
||||
|
||||
// Get the localized text from Tgt if available, otherwise use Val
|
||||
var text string
|
||||
if item.Str.Tgt != nil && item.Str.Tgt.Val != "" {
|
||||
text = item.Str.Tgt.Val
|
||||
} else {
|
||||
text = item.Str.Val
|
||||
}
|
||||
|
||||
messages[itemId] = text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return messages, lcx.TgtCul
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
BIN
tools/tsgo/internal/diagnostics/loc/cs-CZ.json.gz
Normal file
BIN
tools/tsgo/internal/diagnostics/loc/cs-CZ.json.gz
Normal file
Binary file not shown.
BIN
tools/tsgo/internal/diagnostics/loc/de-DE.json.gz
Normal file
BIN
tools/tsgo/internal/diagnostics/loc/de-DE.json.gz
Normal file
Binary file not shown.
BIN
tools/tsgo/internal/diagnostics/loc/es-ES.json.gz
Normal file
BIN
tools/tsgo/internal/diagnostics/loc/es-ES.json.gz
Normal file
Binary file not shown.
BIN
tools/tsgo/internal/diagnostics/loc/fr-FR.json.gz
Normal file
BIN
tools/tsgo/internal/diagnostics/loc/fr-FR.json.gz
Normal file
Binary file not shown.
BIN
tools/tsgo/internal/diagnostics/loc/it-IT.json.gz
Normal file
BIN
tools/tsgo/internal/diagnostics/loc/it-IT.json.gz
Normal file
Binary file not shown.
BIN
tools/tsgo/internal/diagnostics/loc/ja-JP.json.gz
Normal file
BIN
tools/tsgo/internal/diagnostics/loc/ja-JP.json.gz
Normal file
Binary file not shown.
BIN
tools/tsgo/internal/diagnostics/loc/ko-KR.json.gz
Normal file
BIN
tools/tsgo/internal/diagnostics/loc/ko-KR.json.gz
Normal file
Binary file not shown.
BIN
tools/tsgo/internal/diagnostics/loc/pl-PL.json.gz
Normal file
BIN
tools/tsgo/internal/diagnostics/loc/pl-PL.json.gz
Normal file
Binary file not shown.
BIN
tools/tsgo/internal/diagnostics/loc/pt-BR.json.gz
Normal file
BIN
tools/tsgo/internal/diagnostics/loc/pt-BR.json.gz
Normal file
Binary file not shown.
BIN
tools/tsgo/internal/diagnostics/loc/ru-RU.json.gz
Normal file
BIN
tools/tsgo/internal/diagnostics/loc/ru-RU.json.gz
Normal file
Binary file not shown.
BIN
tools/tsgo/internal/diagnostics/loc/tr-TR.json.gz
Normal file
BIN
tools/tsgo/internal/diagnostics/loc/tr-TR.json.gz
Normal file
Binary file not shown.
BIN
tools/tsgo/internal/diagnostics/loc/zh-CN.json.gz
Normal file
BIN
tools/tsgo/internal/diagnostics/loc/zh-CN.json.gz
Normal file
Binary file not shown.
BIN
tools/tsgo/internal/diagnostics/loc/zh-TW.json.gz
Normal file
BIN
tools/tsgo/internal/diagnostics/loc/zh-TW.json.gz
Normal file
Binary file not shown.
151
tools/tsgo/internal/diagnostics/loc_generated.go
Normal file
151
tools/tsgo/internal/diagnostics/loc_generated.go
Normal file
@@ -0,0 +1,151 @@
|
||||
// Code generated by generate.go; DO NOT EDIT.
|
||||
|
||||
package diagnostics
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
_ "embed"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/json"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
var matcher = language.NewMatcher([]language.Tag{
|
||||
language.English,
|
||||
language.MustParse("zh-CN"),
|
||||
language.MustParse("zh-TW"),
|
||||
language.MustParse("cs-CZ"),
|
||||
language.MustParse("de-DE"),
|
||||
language.MustParse("es-ES"),
|
||||
language.MustParse("fr-FR"),
|
||||
language.MustParse("it-IT"),
|
||||
language.MustParse("ja-JP"),
|
||||
language.MustParse("ko-KR"),
|
||||
language.MustParse("pl-PL"),
|
||||
language.MustParse("pt-BR"),
|
||||
language.MustParse("ru-RU"),
|
||||
language.MustParse("tr-TR"),
|
||||
})
|
||||
|
||||
var localeFuncs = []func() map[Key]string{
|
||||
nil, // English (default)
|
||||
zhCN,
|
||||
zhTW,
|
||||
csCZ,
|
||||
deDE,
|
||||
esES,
|
||||
frFR,
|
||||
itIT,
|
||||
jaJP,
|
||||
koKR,
|
||||
plPL,
|
||||
ptBR,
|
||||
ruRU,
|
||||
trTR,
|
||||
}
|
||||
|
||||
func loadLocaleData(data string) map[Key]string {
|
||||
gr, err := gzip.NewReader(strings.NewReader(data))
|
||||
if err != nil {
|
||||
panic("failed to create gzip reader: " + err.Error())
|
||||
}
|
||||
defer gr.Close()
|
||||
var result map[Key]string
|
||||
if err := json.UnmarshalRead(gr, &result); err != nil {
|
||||
panic("failed to unmarshal locale data: " + err.Error())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
//go:embed loc/zh-CN.json.gz
|
||||
var zhCNData string
|
||||
|
||||
var zhCN = sync.OnceValue(func() map[Key]string {
|
||||
return loadLocaleData(zhCNData)
|
||||
})
|
||||
|
||||
//go:embed loc/zh-TW.json.gz
|
||||
var zhTWData string
|
||||
|
||||
var zhTW = sync.OnceValue(func() map[Key]string {
|
||||
return loadLocaleData(zhTWData)
|
||||
})
|
||||
|
||||
//go:embed loc/cs-CZ.json.gz
|
||||
var csCZData string
|
||||
|
||||
var csCZ = sync.OnceValue(func() map[Key]string {
|
||||
return loadLocaleData(csCZData)
|
||||
})
|
||||
|
||||
//go:embed loc/de-DE.json.gz
|
||||
var deDEData string
|
||||
|
||||
var deDE = sync.OnceValue(func() map[Key]string {
|
||||
return loadLocaleData(deDEData)
|
||||
})
|
||||
|
||||
//go:embed loc/es-ES.json.gz
|
||||
var esESData string
|
||||
|
||||
var esES = sync.OnceValue(func() map[Key]string {
|
||||
return loadLocaleData(esESData)
|
||||
})
|
||||
|
||||
//go:embed loc/fr-FR.json.gz
|
||||
var frFRData string
|
||||
|
||||
var frFR = sync.OnceValue(func() map[Key]string {
|
||||
return loadLocaleData(frFRData)
|
||||
})
|
||||
|
||||
//go:embed loc/it-IT.json.gz
|
||||
var itITData string
|
||||
|
||||
var itIT = sync.OnceValue(func() map[Key]string {
|
||||
return loadLocaleData(itITData)
|
||||
})
|
||||
|
||||
//go:embed loc/ja-JP.json.gz
|
||||
var jaJPData string
|
||||
|
||||
var jaJP = sync.OnceValue(func() map[Key]string {
|
||||
return loadLocaleData(jaJPData)
|
||||
})
|
||||
|
||||
//go:embed loc/ko-KR.json.gz
|
||||
var koKRData string
|
||||
|
||||
var koKR = sync.OnceValue(func() map[Key]string {
|
||||
return loadLocaleData(koKRData)
|
||||
})
|
||||
|
||||
//go:embed loc/pl-PL.json.gz
|
||||
var plPLData string
|
||||
|
||||
var plPL = sync.OnceValue(func() map[Key]string {
|
||||
return loadLocaleData(plPLData)
|
||||
})
|
||||
|
||||
//go:embed loc/pt-BR.json.gz
|
||||
var ptBRData string
|
||||
|
||||
var ptBR = sync.OnceValue(func() map[Key]string {
|
||||
return loadLocaleData(ptBRData)
|
||||
})
|
||||
|
||||
//go:embed loc/ru-RU.json.gz
|
||||
var ruRUData string
|
||||
|
||||
var ruRU = sync.OnceValue(func() map[Key]string {
|
||||
return loadLocaleData(ruRUData)
|
||||
})
|
||||
|
||||
//go:embed loc/tr-TR.json.gz
|
||||
var trTRData string
|
||||
|
||||
var trTR = sync.OnceValue(func() map[Key]string {
|
||||
return loadLocaleData(trTRData)
|
||||
})
|
||||
27
tools/tsgo/internal/diagnostics/stringer_generated.go
Normal file
27
tools/tsgo/internal/diagnostics/stringer_generated.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// Code generated by "stringer -type=Category -output=stringer_generated.go"; DO NOT EDIT.
|
||||
|
||||
package diagnostics
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[CategoryWarning-0]
|
||||
_ = x[CategoryError-1]
|
||||
_ = x[CategorySuggestion-2]
|
||||
_ = x[CategoryMessage-3]
|
||||
}
|
||||
|
||||
const _Category_name = "CategoryWarningCategoryErrorCategorySuggestionCategoryMessage"
|
||||
|
||||
var _Category_index = [...]uint8{0, 15, 28, 46, 61}
|
||||
|
||||
func (i Category) String() string {
|
||||
idx := int(i) - 0
|
||||
if i < 0 || idx >= len(_Category_index)-1 {
|
||||
return "Category(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _Category_name[_Category_index[idx]:_Category_index[idx+1]]
|
||||
}
|
||||
Reference in New Issue
Block a user