File: mangle_cache.go

package info (click to toggle)
golang-github-evanw-esbuild 0.27.2-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 10,260 kB
  • sloc: javascript: 28,782; makefile: 820; sh: 17
file content (154 lines) | stat: -rw-r--r-- 4,558 bytes parent folder | download | duplicates (2)
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()
}