1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
|
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"
"github.com/evanw/esbuild/internal/resolver"
)
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
keyPath := logger.Path{Text: absPath, Namespace: "file"}
source := logger.Source{
KeyPath: keyPath,
PrettyPaths: resolver.MakePrettyPaths(fs, keyPath),
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()
}
|