-
Notifications
You must be signed in to change notification settings - Fork 343
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: Added option to --watch-file
to address eager reloading
#1784
Conversation
--wip-- [skip ci] --wip-- [skip ci] fix flow errors
tests/unit/test.watcher.js
Outdated
return whenFilesChanged | ||
.then(() => { | ||
return Promise.race([ | ||
whenFilesChanged | ||
.then(() => { | ||
watcher.close(); | ||
// This delay seems to avoid stat errors from the watcher | ||
// which can happen when the temp dir is deleted (presumably | ||
// before watcher.close() has removed all listeners). | ||
return new Promise((resolve) => { | ||
setTimeout(resolve, 2, onChange); | ||
}); | ||
}), | ||
// Time out if no files are changed | ||
new Promise((resolve) => setTimeout(() => { | ||
watcher.close(); | ||
sinon.assert.calledOnce(onChange); | ||
// This delay seems to avoid stat errors from the watcher | ||
// which can happen when the temp dir is deleted (presumably | ||
// before watcher.close() has removed all listeners). | ||
return new Promise((resolve) => setTimeout(resolve, 2)); | ||
}); |
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.
The way this was written before, whenFilesChanged
will not resolve unless WatchPack actually detects the changes. This leads to a problem with the new feature because with the new option, if the detected file isn't modified, this will never resolve, which fails the test.
I added a "timeout" to the promise using Promise.race()
– which after experimenting with seems to correctly fail the assertions if I want them to. The only bad thing about this I guess is that one test will always take 500ms (the timeout in that second promise). I don't think that's too bad, but if there's a better way to do all of this I'd love to learn 😄
--watch-file
to address eager reloading
--watch-file
to address eager reloading--watch-file
to address eager reloading
I think the build is failing because of |
The error is unrelated to your change, so please create a new PR (or open a new issue). |
watcher.watch([], [sourceDir], Date.now()); | ||
|
||
if (watchFile) { |
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.
To avoid reloading too often, when watchFile
is specified, sourceDir
should not be watched any more. Could you update the implementation and tests with this expectation?
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.
Even if watcher.watch
ends up ignoring the previous call, the readability of the code would be better if you move the previous wacher.watch
call into an } else {
block.
Or something like this:
let watchedDirs = [];
let watchedFiles = [];
if (watchFile) {
watchedFiles.push(watchFile);
} else {
watchedDirs.push(sourceDir);
}
watcher.watch(watchedFiles, watchedDirs, Date.now();
42eb6a6
to
db080fe
Compare
@Rob--W I added a few more assertions to make sure the Watchers in WatchPack were only watching either the Implementation wise, I'm not sure if I need to add anything else. It looks like the WatchPack README says that calling |
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.
(forgot to submit those comments when I wrote them; apologies for the delay)
watcher.watch([], [sourceDir], Date.now()); | ||
|
||
if (watchFile) { |
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.
Even if watcher.watch
ends up ignoring the previous call, the readability of the code would be better if you move the previous wacher.watch
call into an } else {
block.
Or something like this:
let watchedDirs = [];
let watchedFiles = [];
if (watchFile) {
watchedFiles.push(watchFile);
} else {
watchedDirs.push(sourceDir);
}
watcher.watch(watchedFiles, watchedDirs, Date.now();
tests/functional/test.cli.run.js
Outdated
() => withTempAddonDir( | ||
{addonPath: minimalAddonPath}, | ||
(srcDir) => { | ||
const argv = [ | ||
'run', '--verbose', '--no-reload', | ||
'--source-dir', srcDir, | ||
'--watch-file', srcDir, |
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.
--watch-file
is expected to be a file. I would expect an error if a directory is passed.
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 – I threw a UsageError
here if a directory is passed in. Let me know if there's a better error to throw.
tests/functional/test.cli.run.js
Outdated
return execWebExt(argv, {}).waitForExit.then(({exitCode, stdout}) => { | ||
assert.notEqual(exitCode, 0); | ||
// eslint-disable-next-line no-console | ||
console.log(stdout); |
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.
Remove these two lines of code. You probably added them for debugging only.
src/cmd/run.js
Outdated
fs.existsSync(watchFile) && | ||
fs.lstatSync(watchFile).isDirectory() | ||
) { | ||
throw new UsageError(`The directory "${watchFile}" cannot be ` + |
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.
This check is not strictly necessary, but it will probably help against common mistakes.
Let's move this whole check to the src/watcher.js
, right before watchedFiles.push(watchFile);
.
And use isFile()
instead of isDirectory
. That would also defend against cases where the file path refers to non-files that are not directories, such as symlinks.
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.
One small comment, and then it looks good to me.
src/watcher.js
Outdated
|
||
if (watchFile) { | ||
if (fs.existsSync(watchFile) && !fs.lstatSync(watchFile).isFile()) { | ||
throw new UsageError(`"${watchFile}" cannot be passed` + |
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.
"cannot be passed" is vague. Could you rephrase the error message so that it is more actionable? E.g. to:
throw new UsageError(`"Invalid --watch-file value: ${watchFile}" is not a file`)
7d72399
to
afae5f9
Compare
@Rob--W do you think you can land this? I don't have write permissions for this repo 🙂 |
Thanks, merged! |
Fixes #1626
Fixes #1102
Fixes #1137
This PR adds a
--watch-file
parameter, which should resolve the problem ofweb-ext
reloading changes too eagerly when using a custom builder/bundler system like Webpack. It helps with this issue by only reloading the extension when the developer touches a specific file after building their extension (this require some configuration on the developer's part).I got confused in the implementation details since the issue describe the feature as watching a particular file, but some of the finer details seem to focus on watching a new directory.
I wasn't sure which one to implement – file or directory – so I did file because a) it provides a finer grain of control that I think solves the problem and b) the assertions in
test.watcher.js
were easier to write using a file.I think this isn't too bad for a first pass, but
test.watcher.js
ended up being a little tricker than I thought, so definitely need some second eyes on that. I'll annotate why I thought it was tricky below.