-
-
Notifications
You must be signed in to change notification settings - Fork 72
Issue #30 - add lint dirty files only flag #53
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
Conversation
@sergesemashko thanks for taking a crack at this. My first question is from the coveralls failure: can you think of ways to test this? Otherwise I like the way this is going. |
|
||
var changedFiles = Object.keys(compilation.fileTimestamps).filter(function (watchfile) { | ||
return (this.prevTimestamps[watchfile] || this.startTime) < (compilation.fileTimestamps[watchfile] || Infinity); | ||
}.bind(this)).filter(minimatch.filter(globPatterb.join('|'), {matchBase: true})); |
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.
Can this be re-writtern with a reduce
on Object.keys(compilation.fileTimestamps)
? it would be a bit clearer IMO and would be fewer calls/binds. changedFiles
just needs to be an updated glob or array of globs that's passed to stylelint and ultimately to node-glob.
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.
You could even bring in lodash.reduce
and split out this function into a private module in lib
, since it's quite complex, and probably will have some compatibility issues with webpack 2 (#46).
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.
Sorry, did not quite get where to put reduce. Would it be some like this?
function getChangedFiles(plugin, compilation, glob) {
var updatedStyles = reduce(compilation.fileTimestamps, function (changedStyleFiles, timestamp, filename) {
// Check if file has been changed first ...
if ((plugin.prevTimestamps[filename] || plugin.startTime) < (compilation.fileTimestamps[filename] || Infinity)
// ... then validate by the glob pattern.
&& minimatch(glob, {matchBase: true})
) {
changedStyleFiles.push(filename);
}
return changedStyleFiles;
}, []);
return updatedStyles;
}
An example without reduce:
function getChangedFiles(plugin, compilation, glob) {
// Filter only changed files first ...
var updated = Object.keys(compilation.fileTimestamps).filter(function (filename) {
return (plugin.prevTimestamps[filename] || plugin.startTime) < (compilation.fileTimestamps[filename] || Infinity);
});
// ... then filter files by the glob pattern.
var updatedStyles = updated.filter(minimatch.filter(glob, {matchBase: true}));
return updatedStyles;
}
The actual call would look like:
function apply(options, compiler) {
var plugin = this;
...
compiler.plugin('emit', function (compilation, callback) {
...
var changedFiles = getChangedFiles(plugin, compilation, globPatterb.join('|'))
...
}
...
}
@JaKXz, Let me what option you'd prefer to go with
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.
@sergesemashko I prefer the slightly purer first version, but I would use .concat
instead of .push
. I would also just return the result of reduce(obj, fn, [])
.
Another nitpick: would it be clearer to check if compilation.fileTimestamps[filename]
is falsy? Probably not, but what do you think?
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.
Also, the emit
function should be bound to this
so that it's consistent with the other lib functions.
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.
reduce iterates through the initial keys of compilation.fileTimestamps
, so filename
is the key of each entry and is there for sure 😉
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.
👍 let's see how the implementation goes and discuss further there :)
@@ -32,6 +32,7 @@ | |||
"dependencies": { | |||
"arrify": "^1.0.1", | |||
"chalk": "^1.1.3", | |||
"minimatch": "^3.0.3", |
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 use yarn add minimatch
so the yarn.lock
file is 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.
@sergesemashko just curious, is the filter fn that you're using from minimatch
available from node-glob
? could remove unnecessary dependencies :)
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.
good catch. Minimatch is actually a dependency of node-glob
which doesn't export any kind of function to use here :( But I ran yarn add minimatch
, surprisingly, I did not cause any updates of yarn.lock
, because latest minimatch version is already installed in ./node_modules
.
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.
Great!
@JaKXz, thanks for the feedback. |
- Add tests for lintDirtyFilesOnly flag
…nto lintdirtyfiles # Conflicts: # package.json # test/index.js # yarn.lock
@JaKXz, please, take a look once again. |
Changes Unknown when pulling f3e08b9 on sergesemashko:lintdirtyfiles into ** on vieron:master**. |
} else | ||
if (runsCount === 2) { | ||
// Check that on JS file change styleling is not triggered. | ||
expect(stats.compilation.errors).to.have.length(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.
@JaKXz, initially, I had separate test for testing non-style file change. But seems like it's not possible to run multiple webpack watch instances simultaneously as tests are executed in parallel mode. So, I had to squash all watch cases into one.
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.
Can we just mock out the complexity and assert that we are seeing the right thing? I don't want to test webpack (the framework).
It's definitely going to be easier to write a unit test for get-changed-files
. I would start there in a new test file, and we can come back to the integration test later.
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.
Great work so far, we just need to iron out some details and then this should be mergeable :)
|
||
compiler.plugin('emit', function (compilation, callback) { | ||
var dirtyOptions = assign({}, options); | ||
var globPatterb = dirtyOptions.files; |
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.
Nit: typo
compiler.plugin('emit', function (compilation, callback) { | ||
var dirtyOptions = assign({}, options); | ||
var globPatterb = dirtyOptions.files; | ||
var changedFiles = getChangedFiles(this, compilation, globPatterb.join('|')); |
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.
Can you pass an object here, naming the parameters? At some point I'll rewrite this plugin in the latest language features and use destructuring. For now it'll make it clearer why these args are being passed in.
👍🏽 thanks for taking my comments well. Liking the way this is written :)
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.
good call
@@ -0,0 +1,19 @@ | |||
'use strict'; | |||
|
|||
var webpack = require('webpack'); |
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 rebase to capture our changes to support webpack 1&2.
} else | ||
if (runsCount === 2) { | ||
// Check that on JS file change styleling is not triggered. | ||
expect(stats.compilation.errors).to.have.length(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.
Can we just mock out the complexity and assert that we are seeing the right thing? I don't want to test webpack (the framework).
It's definitely going to be easier to write a unit test for get-changed-files
. I would start there in a new test file, and we can come back to the integration test later.
…nto lintdirtyfiles # Conflicts: # lib/run-compilation.js # test/index.js
@JaKXz, again, thanks for the feedback. |
*/ | ||
}); | ||
|
||
context('getChangedFiles', 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.
This should be in its own file [since getChangedFiles
is its own module]. 👍 looks great though 😄
- `../helpers/watch` | ||
- `path` | ||
// Webpack integration test. | ||
it('lints only changed files in watch mode', function (done) { |
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 could be marked with it.skip
so we don't have to commit commented out code. In fact it could be inside it's own missed that above, my bad!context
block.
In that block we should stub out the parts of webpack that we can assume will operate as a black box.
Then, I believe the watchCallback
can be spied on and assertions can be made on individual calls of that fn. I don't mean to prescribe sinon
as a solution since other libraries exist (e.g. I prefer testdouble) but I just wanted to demonstrate that I think it's possible to write this test without all the imperative code and then we can merge it :)
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.
good call, I'll try to mock blackbox logic then
#stuff | ||
{display: block | ||
;; | ||
} |
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.
another nitpick: please make sure your editor is using EditorConfig - thanks!
@@ -8,11 +8,11 @@ var formatter = require('stylelint').formatters.string; | |||
|
|||
// Modules | |||
var runCompilation = require('./lib/run-compilation'); | |||
var getChangedFiles = require('./lib/get-chaged-files'); |
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.
nit: typo
* against cached timestamps from previous run. | ||
* | ||
* @param plugin - stylelint-webpack-plugin this scopr | ||
* @param compilation |
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.
Do we need the entire compilation
object here? If I haven't misread the code looks like we're just using the fileTimestamps
property so I would just accept that as a param.
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.
you are right, fileTimestamps should be enough
@sergesemashko one thing I would suggest in general is to try to extract as many operations into pure "components" or functions that can be easily tested (like |
…nto lintdirtyfiles # Conflicts: # index.js # lib/run-compilation.js # package.json # yarn.lock
1 similar comment
What's done:
@JaKXz, please, take a look once again |
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.
@sergesemashko great work! 🥇
I'm going to merge this, would you mind installing stylelint-webpack-plugin#master
in your project(s) and kick the tires with both the lintDirtyModulesOnly
flag on and off?
@JaKXz, sure, I'll take a look either today or tomorrow |
* resolves #30 - add lint dirty files only flag * Separate logic to extract changed style files. - Add tests for lintDirtyFilesOnly flag * Added a test case for non style file change * Add unit test for getChangedFiles * Remove unused dependencies * Quiet Stylelint during tests * Refactore lint dirty modules into plugin in separate file * Add testdouble * Fix lint warnings
@JaKXz, please, take a look
cc/ @athyuttamre