feat: persistent cache between runs (webpack@5 only)#541
feat: persistent cache between runs (webpack@5 only)#541alexander-akait merged 6 commits intomasterfrom
Conversation
Codecov Report
@@ Coverage Diff @@
## master #541 +/- ##
==========================================
- Coverage 97.72% 95.68% -2.05%
==========================================
Files 3 3
Lines 220 255 +35
Branches 73 86 +13
==========================================
+ Hits 215 244 +29
- Misses 5 11 +6
Continue to review full report at Codecov.
|
src/index.js
Outdated
| [], | ||
| [], |
There was a problem hiding this comment.
You can pass undefined here, that saves a bit memory.
src/index.js
Outdated
| try { | ||
| snapshot = await CopyPlugin.createSnapshot( | ||
| compilation, | ||
| file.absoluteFrom | ||
| ); | ||
| } catch (error) { | ||
| compilation.errors.push(error); | ||
|
|
||
| return; | ||
| } | ||
| return; | ||
| } | ||
|
|
||
| if (snapshot) { | ||
| let isValidSnapshot; | ||
|
|
||
| if (pattern.transform) { | ||
| logger.log(`transforming content for "${file.absoluteFrom}"`); | ||
|
|
||
| if (pattern.cacheTransform) { | ||
| const cacheDirectory = pattern.cacheTransform.directory | ||
| ? pattern.cacheTransform.directory | ||
| : typeof pattern.cacheTransform === 'string' | ||
| ? pattern.cacheTransform | ||
| : findCacheDir({ name: 'copy-webpack-plugin' }) || os.tmpdir(); | ||
| let defaultCacheKeys = { | ||
| version, | ||
| transform: pattern.transform, | ||
| contentHash: crypto.createHash('md4').update(data).digest('hex'), | ||
| }; | ||
|
|
||
| if (typeof pattern.cacheTransform.keys === 'function') { | ||
| defaultCacheKeys = await pattern.cacheTransform.keys( | ||
| defaultCacheKeys, | ||
| file.absoluteFrom | ||
| try { | ||
| isValidSnapshot = await CopyPlugin.checkSnapshotValid( | ||
| compilation, | ||
| snapshot | ||
| ); | ||
| } else { | ||
| defaultCacheKeys = { | ||
| ...defaultCacheKeys, | ||
| ...pattern.cacheTransform.keys, | ||
| }; | ||
| } catch (error) { | ||
| compilation.errors.push(error); | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| const cacheKeys = serialize(defaultCacheKeys); | ||
| itemCache = cache.getItemCache(file.relativeFrom, null); | ||
|
|
||
| try { | ||
| const result = await cacache.get(cacheDirectory, cacheKeys); | ||
| if (isValidSnapshot) { | ||
| try { | ||
| source = await itemCache.getPromise(); | ||
| } catch (error) { | ||
| compilation.errors.push(error); | ||
|
|
||
| logger.debug( | ||
| `getting cached transformation for "${file.absoluteFrom}"` | ||
| ); | ||
| return; | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
checkSnapshotValid on a fresh created snapshot will always be true. You need to call checkSnapshotValid on a snapshot that was created in the last run of webpack. The snapshot need to be stored and restored in and from the cache.
Here is some pseudo code:
// 1. check cache first, validate snapshot in cache entry
cacheEntry = await cache.getPromise(file.relativeFrom, null);
if (cacheEntry !== undefined) {
const { snapshot, source } = cacheEntry;
if (await checkSnapshotValid(snapshot)) {
// return early when cache entry can be used
return source;
}
}
// 2. create a new snapshot
const snapshot = await createSnapshot(file.relativeFrom);
// 3. create new data
const source = await createNewSource(file.relativeFrom);
// 4. store both in cache
await cache.storePromise(file.relativeFrom, null, { source, snapshot });
// 5. return new data
return source;You are in the good position that you can call 2. before 3. Usually that's not possible, because dependencies are not known before execution. In that case one would do this:
// 2a.
const startTime = Date.now();
// 2b. create new data
const source = await createNewSource(file.relativeFrom);
// 3. create a new snapshot
const snapshot = await createSnapshot(file.relativeFrom, startTime);| } | ||
|
|
||
| // eslint-disable-next-line consistent-return | ||
| return new Promise((resolve, reject) => { |
There was a problem hiding this comment.
https://nodejs.org/api/util.html#util_util_promisify_original
const checkSnapshotValid = util.promisify((compilation, snapshot, callback) =>
compilation.fileSystemInfo.checkSnapshotValid(snapshot, callback)
);
const createSnapshot = util.promisify((compilation, dependency, callback) =>
compilation.fileSystemInfo.createSnapshot(Date.now(), [dependency], undefined, undefined, null, callback)
);There was a problem hiding this comment.
Passing an undefined startTime, would assume worse case.
This PR contains a:
Motivation / Use-Case
fixes #504
Breaking Changes
No
Additional Info
only webpack@5