/
mangle_cache.go
152 lines (133 loc) · 4.36 KB
/
mangle_cache.go
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
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()
}