218 lines
5.5 KiB
Go
218 lines
5.5 KiB
Go
//go:build ignore
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"go/format"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/microsoft/typescript-go/internal/repo"
|
|
)
|
|
|
|
var (
|
|
libInputDir = filepath.Join(repo.TypeScriptSubmodulePath(), "src", "lib")
|
|
copyrightNotice = filepath.Join(repo.TypeScriptSubmodulePath(), "scripts", "CopyrightNotice.txt")
|
|
)
|
|
|
|
func main() {
|
|
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
|
|
|
libs := readLibs()
|
|
generateLibs(libs)
|
|
generateLibList(libs)
|
|
generateEmbedded(libs)
|
|
}
|
|
|
|
type lib struct {
|
|
target string // target relative to libs dir
|
|
sources []string // sources relative to src/lib dir
|
|
}
|
|
|
|
func generateLibs(libs []lib) {
|
|
const outputDir = "libs"
|
|
|
|
copyright := readCopyright()
|
|
|
|
if err := os.RemoveAll(outputDir); err != nil {
|
|
log.Fatalf("failed to remove libs directory: %v", err)
|
|
}
|
|
|
|
if err := os.MkdirAll(outputDir, 0o755); err != nil {
|
|
log.Fatalf("failed to create libs directory: %v", err)
|
|
}
|
|
|
|
for _, lib := range libs {
|
|
var output bytes.Buffer
|
|
output.Write(copyright)
|
|
|
|
for _, source := range lib.sources {
|
|
sourcePath := filepath.Join(libInputDir, source)
|
|
b, err := os.ReadFile(sourcePath)
|
|
if err != nil {
|
|
log.Fatalf("failed to read %s: %v", sourcePath, err)
|
|
}
|
|
|
|
output.WriteByte('\n')
|
|
output.Write(removeCRLF(b))
|
|
}
|
|
|
|
outputPath := filepath.Join(outputDir, lib.target)
|
|
if err := os.WriteFile(outputPath, output.Bytes(), 0o644); err != nil {
|
|
log.Fatalf("failed to write %s: %v", outputPath, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func generateLibList(libs []lib) {
|
|
var code bytes.Buffer
|
|
code.WriteString("// Code generated by generate.go; DO NOT EDIT.\n\n")
|
|
code.WriteString("package bundled\n\n")
|
|
|
|
code.WriteString("// LibNames is the list of all bundled lib files, sorted by name.\n")
|
|
code.WriteString("// For the list of libs sorted by load order, use [tsoptions.Libs].\n")
|
|
code.WriteString("var LibNames = []string{\n")
|
|
for _, lib := range libs {
|
|
code.WriteString("\t\"" + lib.target + "\",\n")
|
|
}
|
|
code.WriteString("}\n")
|
|
|
|
writeCode("libs_generated.go", code.Bytes())
|
|
}
|
|
|
|
func generateEmbedded(libs []lib) {
|
|
libVarNames := make([]string, len(libs))
|
|
for i, lib := range libs {
|
|
libVarNames[i] = "libs_" + strings.ReplaceAll(lib.target, ".", "_")
|
|
}
|
|
|
|
var code bytes.Buffer
|
|
code.WriteString("//go:build !noembed\n\n")
|
|
code.WriteString("// Code generated by generate.go; DO NOT EDIT.\n\n")
|
|
code.WriteString("package bundled\n\n")
|
|
code.WriteString("import (\n")
|
|
code.WriteString("\"io/fs\"\n\n")
|
|
code.WriteString("_ \"embed\"\n")
|
|
code.WriteString(")\n\n")
|
|
|
|
code.WriteString("var (\n")
|
|
for i, lib := range libs {
|
|
varName := libVarNames[i]
|
|
code.WriteString("//go:embed libs/" + lib.target + "\n")
|
|
code.WriteString("" + varName + " string\n")
|
|
}
|
|
code.WriteString(")\n\n")
|
|
|
|
code.WriteString("var embeddedContents = map[string]string{\n")
|
|
for i, lib := range libs {
|
|
varName := libVarNames[i]
|
|
code.WriteString("\t\"libs/" + lib.target + "\": " + varName + ",\n")
|
|
}
|
|
code.WriteString("}\n\n")
|
|
|
|
code.WriteString("var libsEntries = []fs.DirEntry{\n")
|
|
for i, lib := range libs {
|
|
varName := libVarNames[i]
|
|
fmt.Fprintf(&code, "\t&fileInfo{name: %q, size: int64(len(%s))},\n", lib.target, varName)
|
|
}
|
|
code.WriteString("}\n")
|
|
|
|
writeCode("embed_generated.go", code.Bytes())
|
|
}
|
|
|
|
var (
|
|
// Match escaped characters, double-quoted strings, single-line comments, and multi-line comments.
|
|
reJSONComments = regexp.MustCompile(`\\.|"(?:\\.|[^"])*"|//.*|/\*[\s\S]*?\*/`)
|
|
// Match double-quoted strings (to skip) or trailing commas before ] or }.
|
|
reTrailingComma = regexp.MustCompile(`"(?:\\.|[^"])*"|,\s*([}\]])`)
|
|
)
|
|
|
|
// stripJSONC replaces comments and trailing commas with spaces in JSONC content,
|
|
// producing valid JSON. The input slice is mutated in place.
|
|
func stripJSONC(b []byte) {
|
|
for _, loc := range reJSONComments.FindAllIndex(b, -1) {
|
|
if b[loc[0]] == '/' {
|
|
for i := loc[0]; i < loc[1]; i++ {
|
|
if b[i] != '\n' {
|
|
b[i] = ' '
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for _, loc := range reTrailingComma.FindAllSubmatchIndex(b, -1) {
|
|
// loc[2]:loc[3] is the capture group; -1 means this matched a string, not a comma.
|
|
if loc[2] < 0 {
|
|
continue
|
|
}
|
|
// Blank the comma (at loc[0]), keep whitespace and closing bracket.
|
|
b[loc[0]] = ' '
|
|
}
|
|
}
|
|
|
|
func readLibs() []lib {
|
|
libsFile := filepath.Join(libInputDir, "libs.json")
|
|
|
|
b, err := os.ReadFile(libsFile)
|
|
if err != nil {
|
|
log.Fatalf("failed to open libs.json: %v", err)
|
|
}
|
|
stripJSONC(b)
|
|
|
|
var meta struct {
|
|
Libs []string `json:"libs"`
|
|
Paths map[string]string `json:"paths"`
|
|
}
|
|
|
|
if err := json.Unmarshal(b, &meta); err != nil {
|
|
log.Fatalf("failed to parse libs.json: %v", err)
|
|
}
|
|
|
|
var libs []lib
|
|
for _, libName := range meta.Libs {
|
|
sources := []string{libName + ".d.ts"}
|
|
var target string
|
|
if path, ok := meta.Paths[libName]; ok {
|
|
target = path
|
|
} else {
|
|
target = "lib." + libName + ".d.ts"
|
|
}
|
|
libs = append(libs, lib{target: target, sources: sources})
|
|
}
|
|
|
|
slices.SortFunc(libs, func(a lib, b lib) int {
|
|
return strings.Compare(a.target, b.target)
|
|
})
|
|
|
|
return libs
|
|
}
|
|
|
|
func readCopyright() []byte {
|
|
b, err := os.ReadFile(copyrightNotice)
|
|
if err != nil {
|
|
log.Fatalf("failed to read copyright notice: %v", err)
|
|
}
|
|
return removeCRLF(b)
|
|
}
|
|
|
|
func removeCRLF(b []byte) []byte {
|
|
return bytes.ReplaceAll(b, []byte("\r\n"), []byte("\n"))
|
|
}
|
|
|
|
func writeCode(filename string, code []byte) {
|
|
formatted, err := format.Source(code)
|
|
if err != nil {
|
|
log.Fatalf("failed to format source: %v", err)
|
|
}
|
|
|
|
if err := os.WriteFile(filename, formatted, 0o644); err != nil {
|
|
log.Fatalf("failed to write %s: %v", filename, err)
|
|
}
|
|
}
|