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 }