github.com/evanw/esbuild@v0.21.4/pkg/cli/mangle_cache.go (about) 1 package cli 2 3 // The mangle cache is a JSON file that remembers esbuild's property renaming 4 // decisions. It's a flat map where the keys are strings and the values are 5 // either strings or the boolean value "false". This is the case both in JSON 6 // and in Go (so the "interface{}" values are also either strings or "false"). 7 8 import ( 9 "fmt" 10 "sort" 11 "strings" 12 "syscall" 13 14 "github.com/evanw/esbuild/internal/fs" 15 "github.com/evanw/esbuild/internal/helpers" 16 "github.com/evanw/esbuild/internal/js_ast" 17 "github.com/evanw/esbuild/internal/js_lexer" 18 "github.com/evanw/esbuild/internal/js_parser" 19 "github.com/evanw/esbuild/internal/logger" 20 ) 21 22 func parseMangleCache(osArgs []string, fs fs.FS, absPath string) (map[string]interface{}, []string) { 23 // Log problems with the mangle cache to stderr 24 log := logger.NewStderrLog(logger.OutputOptionsForArgs(osArgs)) 25 defer log.Done() 26 27 // Try to read the existing file 28 prettyPath := absPath 29 if rel, ok := fs.Rel(fs.Cwd(), absPath); ok { 30 prettyPath = rel 31 } 32 prettyPath = strings.ReplaceAll(prettyPath, "\\", "/") 33 bytes, err, originalError := fs.ReadFile(absPath) 34 if err != nil { 35 // It's ok if it's just missing 36 if err == syscall.ENOENT { 37 return make(map[string]interface{}), []string{} 38 } 39 40 // Otherwise, report the error 41 log.AddError(nil, logger.Range{}, 42 fmt.Sprintf("Failed to read from mangle cache file %q: %s", prettyPath, originalError.Error())) 43 return nil, nil 44 } 45 46 // Use our JSON parser so we get pretty-printed error messages 47 source := logger.Source{ 48 KeyPath: logger.Path{Text: absPath, Namespace: "file"}, 49 PrettyPath: prettyPath, 50 Contents: string(bytes), 51 } 52 result, ok := js_parser.ParseJSON(log, source, js_parser.JSONOptions{}) 53 if !ok || log.HasErrors() { 54 // Stop if there were any errors so we don't continue and then overwrite this file 55 return nil, nil 56 } 57 tracker := logger.MakeLineColumnTracker(&source) 58 59 // Validate the top-level object 60 root, ok := result.Data.(*js_ast.EObject) 61 if !ok { 62 log.AddError(&tracker, logger.Range{Loc: result.Loc}, 63 "Expected a top-level object in mangle cache file") 64 return nil, nil 65 } 66 67 mangleCache := make(map[string]interface{}, len(root.Properties)) 68 order := make([]string, 0, len(root.Properties)) 69 70 for _, property := range root.Properties { 71 key := helpers.UTF16ToString(property.Key.Data.(*js_ast.EString).Value) 72 order = append(order, key) 73 74 switch v := property.ValueOrNil.Data.(type) { 75 case *js_ast.EBoolean: 76 if v.Value { 77 log.AddError(&tracker, js_lexer.RangeOfIdentifier(source, property.ValueOrNil.Loc), 78 fmt.Sprintf("Expected %q in mangle cache file to map to either a string or false", key)) 79 } else { 80 mangleCache[key] = false 81 } 82 83 case *js_ast.EString: 84 mangleCache[key] = helpers.UTF16ToString(v.Value) 85 86 default: 87 log.AddError(&tracker, logger.Range{Loc: property.ValueOrNil.Loc}, 88 fmt.Sprintf("Expected %q in mangle cache file to map to either a string or false", key)) 89 } 90 } 91 92 if log.HasErrors() { 93 return nil, nil 94 } 95 return mangleCache, order 96 } 97 98 func printMangleCache(mangleCache map[string]interface{}, originalOrder []string, asciiOnly bool) []byte { 99 j := helpers.Joiner{} 100 j.AddString("{") 101 102 // Determine the order to print the keys in 103 order := originalOrder 104 if len(mangleCache) > len(order) { 105 order = make([]string, 0, len(mangleCache)) 106 if sort.StringsAreSorted(originalOrder) { 107 // If they came sorted, keep them sorted 108 for key := range mangleCache { 109 order = append(order, key) 110 } 111 sort.Strings(order) 112 } else { 113 // Otherwise add all new keys to the end, and only sort the new keys 114 originalKeys := make(map[string]bool, len(originalOrder)) 115 for _, key := range originalOrder { 116 originalKeys[key] = true 117 } 118 order = append(order, originalOrder...) 119 for key := range mangleCache { 120 if !originalKeys[key] { 121 order = append(order, key) 122 } 123 } 124 sort.Strings(order[len(originalOrder):]) 125 } 126 } 127 128 // Print the JSON while preserving the existing order of the keys 129 for i, key := range order { 130 // Print the key 131 if i > 0 { 132 j.AddString(",\n ") 133 } else { 134 j.AddString("\n ") 135 } 136 j.AddBytes(helpers.QuoteForJSON(key, asciiOnly)) 137 138 // Print the value 139 if value := mangleCache[key]; value != false { 140 j.AddString(": ") 141 j.AddBytes(helpers.QuoteForJSON(value.(string), asciiOnly)) 142 } else { 143 j.AddString(": false") 144 } 145 } 146 147 if len(order) > 0 { 148 j.AddString("\n") 149 } 150 j.AddString("}\n") 151 return j.Done() 152 }