github.com/evanw/esbuild@v0.21.4/internal/cache/cache.go (about)

     1  package cache
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/evanw/esbuild/internal/logger"
     7  	"github.com/evanw/esbuild/internal/runtime"
     8  )
     9  
    10  // This is a cache of the parsed contents of a set of files. The idea is to be
    11  // able to reuse the results of parsing between builds and make subsequent
    12  // builds faster by avoiding redundant parsing work. This only works if:
    13  //
    14  //   - The AST information in the cache must be considered immutable. There is
    15  //     no way to enforce this in Go, but please be disciplined about this. The
    16  //     ASTs are shared in between builds. Any information that must be mutated
    17  //     in the AST during a build must be done on a shallow clone of the data if
    18  //     the mutation happens after parsing (i.e. a clone that clones everything
    19  //     that will be mutated and shares only the parts that won't be mutated).
    20  //
    21  //   - The information in the cache must not depend at all on the contents of
    22  //     any file other than the file being cached. Invalidating an entry in the
    23  //     cache does not also invalidate any entries that depend on that file, so
    24  //     caching information that depends on other files can result in incorrect
    25  //     results due to reusing stale data. For example, do not "bake in" some
    26  //     value imported from another file.
    27  //
    28  //   - Cached ASTs must only be reused if the parsing options are identical
    29  //     between builds. For example, it would be bad if the AST parser depended
    30  //     on options inherited from a nearby "package.json" file but those options
    31  //     were not part of the cache key. Then the cached AST could incorrectly be
    32  //     reused even if the contents of that "package.json" file have changed.
    33  type CacheSet struct {
    34  	FSCache          FSCache
    35  	CSSCache         CSSCache
    36  	JSONCache        JSONCache
    37  	JSCache          JSCache
    38  	SourceIndexCache SourceIndexCache
    39  }
    40  
    41  func MakeCacheSet() *CacheSet {
    42  	return &CacheSet{
    43  		SourceIndexCache: SourceIndexCache{
    44  			globEntries:     make(map[uint64]uint32),
    45  			entries:         make(map[sourceIndexKey]uint32),
    46  			nextSourceIndex: runtime.SourceIndex + 1,
    47  		},
    48  		FSCache: FSCache{
    49  			entries: make(map[string]*fsEntry),
    50  		},
    51  		CSSCache: CSSCache{
    52  			entries: make(map[logger.Path]*cssCacheEntry),
    53  		},
    54  		JSONCache: JSONCache{
    55  			entries: make(map[logger.Path]*jsonCacheEntry),
    56  		},
    57  		JSCache: JSCache{
    58  			entries: make(map[logger.Path]*jsCacheEntry),
    59  		},
    60  	}
    61  }
    62  
    63  type SourceIndexCache struct {
    64  	globEntries     map[uint64]uint32
    65  	entries         map[sourceIndexKey]uint32
    66  	mutex           sync.Mutex
    67  	nextSourceIndex uint32
    68  }
    69  
    70  type SourceIndexKind uint8
    71  
    72  const (
    73  	SourceIndexNormal SourceIndexKind = iota
    74  	SourceIndexJSStubForCSS
    75  )
    76  
    77  type sourceIndexKey struct {
    78  	path logger.Path
    79  	kind SourceIndexKind
    80  }
    81  
    82  func (c *SourceIndexCache) LenHint() uint32 {
    83  	c.mutex.Lock()
    84  	defer c.mutex.Unlock()
    85  
    86  	// Add some extra room at the end for a new file or two without reallocating
    87  	const someExtraRoom = 16
    88  	return c.nextSourceIndex + someExtraRoom
    89  }
    90  
    91  func (c *SourceIndexCache) Get(path logger.Path, kind SourceIndexKind) uint32 {
    92  	key := sourceIndexKey{path: path, kind: kind}
    93  	c.mutex.Lock()
    94  	defer c.mutex.Unlock()
    95  	if sourceIndex, ok := c.entries[key]; ok {
    96  		return sourceIndex
    97  	}
    98  	sourceIndex := c.nextSourceIndex
    99  	c.nextSourceIndex++
   100  	c.entries[key] = sourceIndex
   101  	return sourceIndex
   102  }
   103  
   104  func (c *SourceIndexCache) GetGlob(parentSourceIndex uint32, globIndex uint32) uint32 {
   105  	key := (uint64(parentSourceIndex) << 32) | uint64(globIndex)
   106  	c.mutex.Lock()
   107  	defer c.mutex.Unlock()
   108  	if sourceIndex, ok := c.globEntries[key]; ok {
   109  		return sourceIndex
   110  	}
   111  	sourceIndex := c.nextSourceIndex
   112  	c.nextSourceIndex++
   113  	c.globEntries[key] = sourceIndex
   114  	return sourceIndex
   115  }