-
-
Notifications
You must be signed in to change notification settings - Fork 385
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
feat: hmr error recovery #435
base: master
Are you sure you want to change the base?
Conversation
Not quite sure what's up here; I did sign the CLA but it remains pending as not signed? Looks like it doesn't quite understand users with multiple email addresses. I can (and have) signed using the e-mail under which commits were added, but then I can't sign anymore for the primary e-mail which GitHub uses to open the PR. |
@@ -152,6 +166,10 @@ export function pitch(request) { | |||
this.addContextDependency(dep); | |||
}, this); | |||
|
|||
if (compilation.errors.length > 0) { | |||
return callback(compilation.errors[0]); | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For catching errors, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. It was moved down passed the dependencies, to ensure those dependenices are always tracked.
Flipping the error state into a success state on the parent compilation, means that it will also update its registered dependencies. (If the parent compilation would fail, it would retain the previous set.)
If we'd drop out of the child compilation because of an error before the dependencies of the child compilation are moved into the parent, then we would 'lose' those dependencies, including any change watches on them. This way, we won't.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would there be a case where multiple errors need to be shown in the errors array?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ScriptedAlchemy
Not sure actually. The current version of the plugin only reports the first child compilation error as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need add tests, you can find hmr tests in test directory
I'll have a look for those and will try to figure out how to add a test for this. It looks like the tests for HMR.test.js are using mocking to perform only an isolated test of the There don't seem to be any tests using complete HMR middleware and execution flow, so I don't think I can actually fit a test for these changes as it requires an actual HMR client to execute within. (That's the exact bit I had problems with before when attempting to automate the repro-case, come to think of it.) [EDIT] |
Had another long look at how to make a test work. A proper test for the full HMR flow (which in general is indeed currently not actually tested) would, I think, require something more or less like the following:
An overarching test harness is then needed to close the loop between server and client:
Does that sound acceptable? I don't really like the idea of editing a file on disk for a test, but the alternative is creating a new in-memory file system and hooking that up as the input file system for Webpack. In which case such an in-memory file system would also need full support for watches and change monitoring, which afaik Webpack's |
You don't need this stuff, just mock |
If you're only interested in the code output containing the expected hot-loader along with the error, yes; that test I can build for you easily. Just a dummy loader which always errors, chained onto the MiniCssExtractPlugin's loader. |
@rjgotten this enough |
Added a test which verifies that a module with compilation errors first emits Of special note: [EDIT] [EDIT] |
Needed to erase stack trace location, which varies by system
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please look on notes
@@ -0,0 +1,4 @@ | |||
module.exports = function loader() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't use ~
in filename
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any other suggestion then for a moniker to use to indicate tests that are expected to fail?
Testcases.test.js
works by enumerating directory names and running all the cases, then bailing if one of them has a compilation error. But here we have a testcase which is expected to have a compilation error. First of its kind.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rjgotten just use fail
word in test name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adjusted the test pattern in TestCases.test.js
to /-fail$/
i.e. any test suffixed with -fail
is expected to error. And in accordance renamed the testcase to hmr-resilience-fail
, getting rid of the ~
.
output: { | ||
filename: '[name].js', | ||
}, | ||
optimization: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why it is necessary for test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
output.filename is a leftover. I'll remove it.
The optimization settings are there to force everything but the compiled module itself into a separate chunk (which is then also not emitted).
This limits the surface of the test, so we don't end up comparing code for e.g. the hot reloader client itself. (Which may actually be subject to change and cause false-negative failed unit tests if it is ever updated.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed the filename leftover, and left a comment explaining why the optimization chunk settings are being used.
// 1479427200000 | ||
var cssReload = __webpack_require__("../../../src/hmr/hotModuleReplacement.js")(module.i, {"hmr":true,"locals":false}); | ||
module.hot.dispose(cssReload); | ||
module.hot.accept(function(){}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A little more context please:
why what ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why we need this module.hot.accept(function(){});
? here, please add comment to source doe, because this line is misleading for other developers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. Will add a small description in the loader.js where this particular line of code is emitted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
- removes obsolete output filename from testcase; - renames testcase to remove ~ from directory name; - adds comment explaining use of optimization chunks in testcase; - adds comment explaining self-accept with empty error handler
Just a friendly ping to inform you that your noted remarks were processed. |
in todo list |
Hi, any progress on this? |
I'm having the exact same issue. |
@evilebottnawi has to approve it and merge it. There's a trivial merge-conflict in |
@rjgotten thank your for your message. |
Hey @evilebottnawi, this is just a friendly ping ! |
This PR contains a:
Motivation / Use-Case
This PR implements error-resilience/-recovery for HMR when dealing with failed CSS child compilations. (E.g. syntax errors in Less or Sass code.)
It enables failed compilations to continue to
hot.accept
future compilations, instead of requiring a full reload. This is a significant improvement to workflow, esp. for larger applications that can't deal well with reload and require manually recreating a certain application state.It fixes #431
Breaking Changes
None, as far as I am aware. All existing test scenarios in the suite continue to pass.
Additional Info
The NetMatch/mini-css-extract-plugin-hmr-testcase repository contains a testcase that illustrates the problem; includes a method to switch to the patched fork and illustrate the improvements; and has a rundown of the how/what/why of changes.
A test update will probably still be required to verify the resilience. But I'm having some problems wrapping my head around how the test suite is set up.
Some assistance there would be appreciated.