2025-10-15 10:12:44 +03:00

229 lines
5.7 KiB
Go

//go:build ignore
package main
import (
"bytes"
"fmt"
"go/format"
"log"
"os"
"path/filepath"
"slices"
"strings"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/parser"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/repo"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
)
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())
}
func readLibs() []lib {
type libsMeta struct {
libs []string
paths map[string]string
}
libsFile := tspath.NormalizeSlashes(filepath.Join(libInputDir, "libs.json"))
b, err := os.ReadFile(libsFile)
if err != nil {
log.Fatalf("failed to open libs.json: %v", err)
}
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: libsFile,
Path: tspath.Path(libsFile),
}, string(b), core.ScriptKindJSON)
diags := sourceFile.Diagnostics()
if len(diags) > 0 {
for _, diag := range diags {
log.Printf("%s", diag.Message())
}
log.Fatalf("failed to parse libs.json")
}
paths := make(map[string]string)
var libNames []string
props := sourceFile.Statements.Nodes[0].
AsExpressionStatement().
Expression.
AsObjectLiteralExpression().
Properties.
Nodes
for _, prop := range props {
assign := prop.AsPropertyAssignment()
name := assign.Name().Text()
switch name {
case "libs":
for _, lib := range assign.Initializer.AsArrayLiteralExpression().Elements.Nodes {
libNames = append(libNames, lib.AsStringLiteral().Text)
}
case "paths":
for _, path := range assign.Initializer.AsObjectLiteralExpression().Properties.Nodes {
prop := path.AsPropertyAssignment()
key := prop.Name().Text()
value := prop.Initializer.AsStringLiteral().Text
paths[key] = value
}
default:
log.Fatalf("unexpected property: %s", name)
}
}
var libs []lib
for _, libName := range libNames {
sources := []string{"header.d.ts", libName + ".d.ts"}
var target string
if path, ok := 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)
}
}