191 lines
4.5 KiB
Go
191 lines
4.5 KiB
Go
|
package cache
|
||
|
|
||
|
import (
|
||
|
"sync"
|
||
|
|
||
|
"github.com/evanw/esbuild/internal/css_ast"
|
||
|
"github.com/evanw/esbuild/internal/css_parser"
|
||
|
"github.com/evanw/esbuild/internal/js_ast"
|
||
|
"github.com/evanw/esbuild/internal/js_parser"
|
||
|
"github.com/evanw/esbuild/internal/logger"
|
||
|
)
|
||
|
|
||
|
// This cache intends to avoid unnecessarily re-parsing files in subsequent
|
||
|
// builds. For a given path, parsing can be avoided if the contents of the file
|
||
|
// and the options for the parser are the same as last time. Even if the
|
||
|
// contents of the file are the same, the options for the parser may have
|
||
|
// changed if they depend on some other file ("package.json" for example).
|
||
|
//
|
||
|
// This cache checks if the file contents have changed even though we have
|
||
|
// the ability to detect if a file has changed on the file system by reading
|
||
|
// its metadata. First of all, if the file contents are cached then they should
|
||
|
// be the same pointer, which makes the comparison trivial. Also we want to
|
||
|
// cache the AST for plugins in the common case that the plugin output stays
|
||
|
// the same.
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// CSS
|
||
|
|
||
|
type CSSCache struct {
|
||
|
mutex sync.Mutex
|
||
|
entries map[logger.Path]*cssCacheEntry
|
||
|
}
|
||
|
|
||
|
type cssCacheEntry struct {
|
||
|
source logger.Source
|
||
|
options css_parser.Options
|
||
|
ast css_ast.AST
|
||
|
msgs []logger.Msg
|
||
|
}
|
||
|
|
||
|
func (c *CSSCache) Parse(log logger.Log, source logger.Source, options css_parser.Options) css_ast.AST {
|
||
|
// Check the cache
|
||
|
entry := func() *cssCacheEntry {
|
||
|
c.mutex.Lock()
|
||
|
defer c.mutex.Unlock()
|
||
|
return c.entries[source.KeyPath]
|
||
|
}()
|
||
|
|
||
|
// Cache hit
|
||
|
if entry != nil && entry.source == source && entry.options == options {
|
||
|
for _, msg := range entry.msgs {
|
||
|
log.AddMsg(msg)
|
||
|
}
|
||
|
return entry.ast
|
||
|
}
|
||
|
|
||
|
// Cache miss
|
||
|
tempLog := logger.NewDeferLog(logger.DeferLogAll)
|
||
|
ast := css_parser.Parse(tempLog, source, options)
|
||
|
msgs := tempLog.Done()
|
||
|
for _, msg := range msgs {
|
||
|
log.AddMsg(msg)
|
||
|
}
|
||
|
|
||
|
// Create the cache entry
|
||
|
entry = &cssCacheEntry{
|
||
|
source: source,
|
||
|
options: options,
|
||
|
ast: ast,
|
||
|
msgs: msgs,
|
||
|
}
|
||
|
|
||
|
// Save for next time
|
||
|
c.mutex.Lock()
|
||
|
defer c.mutex.Unlock()
|
||
|
c.entries[source.KeyPath] = entry
|
||
|
return ast
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// JSON
|
||
|
|
||
|
type JSONCache struct {
|
||
|
mutex sync.Mutex
|
||
|
entries map[logger.Path]*jsonCacheEntry
|
||
|
}
|
||
|
|
||
|
type jsonCacheEntry struct {
|
||
|
source logger.Source
|
||
|
options js_parser.JSONOptions
|
||
|
expr js_ast.Expr
|
||
|
ok bool
|
||
|
msgs []logger.Msg
|
||
|
}
|
||
|
|
||
|
func (c *JSONCache) Parse(log logger.Log, source logger.Source, options js_parser.JSONOptions) (js_ast.Expr, bool) {
|
||
|
// Check the cache
|
||
|
entry := func() *jsonCacheEntry {
|
||
|
c.mutex.Lock()
|
||
|
defer c.mutex.Unlock()
|
||
|
return c.entries[source.KeyPath]
|
||
|
}()
|
||
|
|
||
|
// Cache hit
|
||
|
if entry != nil && entry.source == source && entry.options == options {
|
||
|
for _, msg := range entry.msgs {
|
||
|
log.AddMsg(msg)
|
||
|
}
|
||
|
return entry.expr, entry.ok
|
||
|
}
|
||
|
|
||
|
// Cache miss
|
||
|
tempLog := logger.NewDeferLog(logger.DeferLogAll)
|
||
|
expr, ok := js_parser.ParseJSON(tempLog, source, options)
|
||
|
msgs := tempLog.Done()
|
||
|
for _, msg := range msgs {
|
||
|
log.AddMsg(msg)
|
||
|
}
|
||
|
|
||
|
// Create the cache entry
|
||
|
entry = &jsonCacheEntry{
|
||
|
source: source,
|
||
|
options: options,
|
||
|
expr: expr,
|
||
|
ok: ok,
|
||
|
msgs: msgs,
|
||
|
}
|
||
|
|
||
|
// Save for next time
|
||
|
c.mutex.Lock()
|
||
|
defer c.mutex.Unlock()
|
||
|
c.entries[source.KeyPath] = entry
|
||
|
return expr, ok
|
||
|
}
|
||
|
|
||
|
////////////////////////////////////////////////////////////////////////////////
|
||
|
// JS
|
||
|
|
||
|
type JSCache struct {
|
||
|
mutex sync.Mutex
|
||
|
entries map[logger.Path]*jsCacheEntry
|
||
|
}
|
||
|
|
||
|
type jsCacheEntry struct {
|
||
|
source logger.Source
|
||
|
options js_parser.Options
|
||
|
ast js_ast.AST
|
||
|
ok bool
|
||
|
msgs []logger.Msg
|
||
|
}
|
||
|
|
||
|
func (c *JSCache) Parse(log logger.Log, source logger.Source, options js_parser.Options) (js_ast.AST, bool) {
|
||
|
// Check the cache
|
||
|
entry := func() *jsCacheEntry {
|
||
|
c.mutex.Lock()
|
||
|
defer c.mutex.Unlock()
|
||
|
return c.entries[source.KeyPath]
|
||
|
}()
|
||
|
|
||
|
// Cache hit
|
||
|
if entry != nil && entry.source == source && entry.options.Equal(&options) {
|
||
|
for _, msg := range entry.msgs {
|
||
|
log.AddMsg(msg)
|
||
|
}
|
||
|
return entry.ast, entry.ok
|
||
|
}
|
||
|
|
||
|
// Cache miss
|
||
|
tempLog := logger.NewDeferLog(logger.DeferLogAll)
|
||
|
ast, ok := js_parser.Parse(tempLog, source, options)
|
||
|
msgs := tempLog.Done()
|
||
|
for _, msg := range msgs {
|
||
|
log.AddMsg(msg)
|
||
|
}
|
||
|
|
||
|
// Create the cache entry
|
||
|
entry = &jsCacheEntry{
|
||
|
source: source,
|
||
|
options: options,
|
||
|
ast: ast,
|
||
|
ok: ok,
|
||
|
msgs: msgs,
|
||
|
}
|
||
|
|
||
|
// Save for next time
|
||
|
c.mutex.Lock()
|
||
|
defer c.mutex.Unlock()
|
||
|
c.entries[source.KeyPath] = entry
|
||
|
return ast, ok
|
||
|
}
|