github.com/evanw/esbuild@v0.21.4/internal/cache/cache_ast.go (about) 1 package cache 2 3 import ( 4 "sync" 5 6 "github.com/evanw/esbuild/internal/css_ast" 7 "github.com/evanw/esbuild/internal/css_parser" 8 "github.com/evanw/esbuild/internal/js_ast" 9 "github.com/evanw/esbuild/internal/js_parser" 10 "github.com/evanw/esbuild/internal/logger" 11 ) 12 13 // This cache intends to avoid unnecessarily re-parsing files in subsequent 14 // builds. For a given path, parsing can be avoided if the contents of the file 15 // and the options for the parser are the same as last time. Even if the 16 // contents of the file are the same, the options for the parser may have 17 // changed if they depend on some other file ("package.json" for example). 18 // 19 // This cache checks if the file contents have changed even though we have 20 // the ability to detect if a file has changed on the file system by reading 21 // its metadata. First of all, if the file contents are cached then they should 22 // be the same pointer, which makes the comparison trivial. Also we want to 23 // cache the AST for plugins in the common case that the plugin output stays 24 // the same. 25 26 //////////////////////////////////////////////////////////////////////////////// 27 // CSS 28 29 type CSSCache struct { 30 entries map[logger.Path]*cssCacheEntry 31 mutex sync.Mutex 32 } 33 34 type cssCacheEntry struct { 35 source logger.Source 36 msgs []logger.Msg 37 ast css_ast.AST 38 options css_parser.Options 39 } 40 41 func (c *CSSCache) Parse(log logger.Log, source logger.Source, options css_parser.Options) css_ast.AST { 42 // Check the cache 43 entry := func() *cssCacheEntry { 44 c.mutex.Lock() 45 defer c.mutex.Unlock() 46 return c.entries[source.KeyPath] 47 }() 48 49 // Cache hit 50 if entry != nil && entry.source == source && entry.options.Equal(&options) { 51 for _, msg := range entry.msgs { 52 log.AddMsg(msg) 53 } 54 return entry.ast 55 } 56 57 // Cache miss 58 tempLog := logger.NewDeferLog(logger.DeferLogAll, log.Overrides) 59 ast := css_parser.Parse(tempLog, source, options) 60 msgs := tempLog.Done() 61 for _, msg := range msgs { 62 log.AddMsg(msg) 63 } 64 65 // Create the cache entry 66 entry = &cssCacheEntry{ 67 source: source, 68 options: options, 69 ast: ast, 70 msgs: msgs, 71 } 72 73 // Save for next time 74 c.mutex.Lock() 75 defer c.mutex.Unlock() 76 c.entries[source.KeyPath] = entry 77 return ast 78 } 79 80 //////////////////////////////////////////////////////////////////////////////// 81 // JSON 82 83 type JSONCache struct { 84 entries map[logger.Path]*jsonCacheEntry 85 mutex sync.Mutex 86 } 87 88 type jsonCacheEntry struct { 89 expr js_ast.Expr 90 msgs []logger.Msg 91 source logger.Source 92 options js_parser.JSONOptions 93 ok bool 94 } 95 96 func (c *JSONCache) Parse(log logger.Log, source logger.Source, options js_parser.JSONOptions) (js_ast.Expr, bool) { 97 // Check the cache 98 entry := func() *jsonCacheEntry { 99 c.mutex.Lock() 100 defer c.mutex.Unlock() 101 return c.entries[source.KeyPath] 102 }() 103 104 // Cache hit 105 if entry != nil && entry.source == source && entry.options == options { 106 for _, msg := range entry.msgs { 107 log.AddMsg(msg) 108 } 109 return entry.expr, entry.ok 110 } 111 112 // Cache miss 113 tempLog := logger.NewDeferLog(logger.DeferLogAll, log.Overrides) 114 expr, ok := js_parser.ParseJSON(tempLog, source, options) 115 msgs := tempLog.Done() 116 for _, msg := range msgs { 117 log.AddMsg(msg) 118 } 119 120 // Create the cache entry 121 entry = &jsonCacheEntry{ 122 source: source, 123 options: options, 124 expr: expr, 125 ok: ok, 126 msgs: msgs, 127 } 128 129 // Save for next time 130 c.mutex.Lock() 131 defer c.mutex.Unlock() 132 c.entries[source.KeyPath] = entry 133 return expr, ok 134 } 135 136 //////////////////////////////////////////////////////////////////////////////// 137 // JS 138 139 type JSCache struct { 140 entries map[logger.Path]*jsCacheEntry 141 mutex sync.Mutex 142 } 143 144 type jsCacheEntry struct { 145 source logger.Source 146 msgs []logger.Msg 147 options js_parser.Options 148 ast js_ast.AST 149 ok bool 150 } 151 152 func (c *JSCache) Parse(log logger.Log, source logger.Source, options js_parser.Options) (js_ast.AST, bool) { 153 // Check the cache 154 entry := func() *jsCacheEntry { 155 c.mutex.Lock() 156 defer c.mutex.Unlock() 157 return c.entries[source.KeyPath] 158 }() 159 160 // Cache hit 161 if entry != nil && entry.source == source && entry.options.Equal(&options) { 162 for _, msg := range entry.msgs { 163 log.AddMsg(msg) 164 } 165 return entry.ast, entry.ok 166 } 167 168 // Cache miss 169 tempLog := logger.NewDeferLog(logger.DeferLogAll, log.Overrides) 170 ast, ok := js_parser.Parse(tempLog, source, options) 171 msgs := tempLog.Done() 172 for _, msg := range msgs { 173 log.AddMsg(msg) 174 } 175 176 // Create the cache entry 177 entry = &jsCacheEntry{ 178 source: source, 179 options: options, 180 ast: ast, 181 ok: ok, 182 msgs: msgs, 183 } 184 185 // Save for next time 186 c.mutex.Lock() 187 defer c.mutex.Unlock() 188 c.entries[source.KeyPath] = entry 189 return ast, ok 190 }