Files
esbuild/pkg/cli/mangle_cache.go
T

153 lines
4.4 KiB
Go

package cli
// The mangle cache is a JSON file that remembers esbuild's property renaming
// decisions. It's a flat map where the keys are strings and the values are
// either strings or the boolean value "false". This is the case both in JSON
// and in Go (so the "interface{}" values are also either strings or "false").
import (
"fmt"
"sort"
"strings"
"syscall"
"github.com/evanw/esbuild/internal/fs"
"github.com/evanw/esbuild/internal/helpers"
"github.com/evanw/esbuild/internal/js_ast"
"github.com/evanw/esbuild/internal/js_lexer"
"github.com/evanw/esbuild/internal/js_parser"
"github.com/evanw/esbuild/internal/logger"
)
func parseMangleCache(osArgs []string, fs fs.FS, absPath string) (map[string]interface{}, []string) {
// Log problems with the mangle cache to stderr
log := logger.NewStderrLog(logger.OutputOptionsForArgs(osArgs))
defer log.Done()
// Try to read the existing file
prettyPath := absPath
if rel, ok := fs.Rel(fs.Cwd(), absPath); ok {
prettyPath = rel
}
prettyPath = strings.ReplaceAll(prettyPath, "\\", "/")
bytes, err, originalError := fs.ReadFile(absPath)
if err != nil {
// It's ok if it's just missing
if err == syscall.ENOENT {
return make(map[string]interface{}), []string{}
}
// Otherwise, report the error
log.AddError(nil, logger.Range{},
fmt.Sprintf("Failed to read from mangle cache file %q: %s", prettyPath, originalError.Error()))
return nil, nil
}
// Use our JSON parser so we get pretty-printed error messages
source := logger.Source{
KeyPath: logger.Path{Text: absPath, Namespace: "file"},
PrettyPath: prettyPath,
Contents: string(bytes),
}
result, ok := js_parser.ParseJSON(log, source, js_parser.JSONOptions{})
if !ok || log.HasErrors() {
// Stop if there were any errors so we don't continue and then overwrite this file
return nil, nil
}
tracker := logger.MakeLineColumnTracker(&source)
// Validate the top-level object
root, ok := result.Data.(*js_ast.EObject)
if !ok {
log.AddError(&tracker, logger.Range{Loc: result.Loc},
"Expected a top-level object in mangle cache file")
return nil, nil
}
mangleCache := make(map[string]interface{}, len(root.Properties))
order := make([]string, 0, len(root.Properties))
for _, property := range root.Properties {
key := helpers.UTF16ToString(property.Key.Data.(*js_ast.EString).Value)
order = append(order, key)
switch v := property.ValueOrNil.Data.(type) {
case *js_ast.EBoolean:
if v.Value {
log.AddError(&tracker, js_lexer.RangeOfIdentifier(source, property.ValueOrNil.Loc),
fmt.Sprintf("Expected %q in mangle cache file to map to either a string or false", key))
} else {
mangleCache[key] = false
}
case *js_ast.EString:
mangleCache[key] = helpers.UTF16ToString(v.Value)
default:
log.AddError(&tracker, logger.Range{Loc: property.ValueOrNil.Loc},
fmt.Sprintf("Expected %q in mangle cache file to map to either a string or false", key))
}
}
if log.HasErrors() {
return nil, nil
}
return mangleCache, order
}
func printMangleCache(mangleCache map[string]interface{}, originalOrder []string, asciiOnly bool) []byte {
j := helpers.Joiner{}
j.AddString("{")
// Determine the order to print the keys in
order := originalOrder
if len(mangleCache) > len(order) {
order = make([]string, 0, len(mangleCache))
if sort.StringsAreSorted(originalOrder) {
// If they came sorted, keep them sorted
for key := range mangleCache {
order = append(order, key)
}
sort.Strings(order)
} else {
// Otherwise add all new keys to the end, and only sort the new keys
originalKeys := make(map[string]bool, len(originalOrder))
for _, key := range originalOrder {
originalKeys[key] = true
}
order = append(order, originalOrder...)
for key := range mangleCache {
if !originalKeys[key] {
order = append(order, key)
}
}
sort.Strings(order[len(originalOrder):])
}
}
// Print the JSON while preserving the existing order of the keys
for i, key := range order {
// Print the key
if i > 0 {
j.AddString(",\n ")
} else {
j.AddString("\n ")
}
j.AddBytes(helpers.QuoteForJSON(key, asciiOnly))
// Print the value
if value := mangleCache[key]; value != false {
j.AddString(": ")
j.AddBytes(helpers.QuoteForJSON(value.(string), asciiOnly))
} else {
j.AddString(": false")
}
}
if len(order) > 0 {
j.AddString("\n")
}
j.AddString("}\n")
return j.Done()
}