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  }