Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(worker): workaround webpack4 fs write caching #133

Merged
merged 1 commit into from
Nov 8, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 36 additions & 18 deletions webpack/private/webpack_worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,26 +92,44 @@ class WebpackWorker extends WebpackCLI {
this.compiler = await super.createCompiler(options, cb)
this.options = options

// Necessary cache-clearing after each emit to ensure files are re-written to disk
// on each incremental build.
this.compiler.hooks.afterEmit.tap("rules_webpack", compilation => {
// The output directory will be cleaned between runs, webpack assumes the output directory will not be modified:
// Webpack5: https://github.com/webpack/webpack/blob/v5.36.2/lib/Compiler.js#L783-L788
if (this.compiler._assetEmittingPreviousFiles) {
this.compiler._assetEmittingPreviousFiles.clear()
}
// The output directory will be cleaned between runs, however webpack assumes the
// output directory will not be modified and caches the file system state.

// Webpack5: clear the "previously emitted" cache
// https://github.com/webpack/webpack/blob/v5.36.2/lib/Compiler.js#L783-L788
if (this.compiler._assetEmittingPreviousFiles) {
this.compiler.hooks.afterEmit.tap("rules_webpack", () => this.compiler._assetEmittingPreviousFiles.clear());
} else {
// Webpack4: patch the compiler to ensure all assets are re-written on each completion
const fsCache = new Map()
const fsWrites = new Set()

// Clear the "writes" before compilation.
this.compiler.hooks.emit.tap('rules_webpack', () => fsWrites.clear())

// Clear the full "emitting sources" cache in <v5 'futureEmitAssets' mode:
Copy link
Member Author

@jbedard jbedard Nov 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out the previous webpack 4 solution wasn't enough.

As @thesayyn discovered webpack4 doesn't even reach the asset-emitting logic for some cases such as when only html files change (then it seems .js files won't even make it to the code the previous fix was for).

// Webpack4: https://github.com/webpack/webpack/blob/v4.47.0/lib/Compiler.js#L374-L404
//
// Or clear the "emitted assets" 'existsAt' cache:
// Webpack4: https://github.com/webpack/webpack/blob/v4.47.0/lib/Compiler.js#L450-L453
if (this.compiler.options.output.futureEmitAssets && this.compiler._assetEmittingSourceCache) {
this.compiler._assetEmittingSourceCache = new WeakMap()
} else {
compilation.emittedAssets.clear()
// Cache all file write operations throughout compilation
const originalWriteFile = this.compiler.outputFileSystem.writeFile
this.compiler.outputFileSystem.writeFile = function workerWriteFile(p, data, cb) {
fsCache.set(p, data)
fsWrites.add(p)
return originalWriteFile.apply(this, arguments)
}
})

// Ensure all past write operations are rewritten.
this.compiler.hooks.afterEmit.tap('rules_webpack', () => {
for (const [p, data] of fsCache.entries()) {
if (!fsWrites.has(p)) {
// TODO: wait for async write?
originalWriteFile.call(this.compiler.outputFileSystem, p, data, () => {})
}
}

// Clear the writes-done for the next compilation.
fsWrites.clear()
})

// TODO: clear entries when no longer outputted by webpack
}
}
}
}
Expand Down