-
-
Notifications
You must be signed in to change notification settings - Fork 70
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
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
4a54385
Issue #30 - add lint dirty files only flag
0f976ad
Separate logic to extract changed style files.
2b1083f
Added a test case for non style file change
f3e08b9
Merge branch 'master' of github.com:vieron/stylelint-webpack-plugin i…
a3c6397
Add unit test for getChangedFiles
02c1feb
Remove unused dependencies
fa6ee14
Merge branch 'master' of github.com:vieron/stylelint-webpack-plugin i…
e0f19c7
Quiet Stylelint during tests
f34497a
Refactore lint dirty modules into plugin in separate file
8ea8ef6
Merge branch 'master' of github.com:vieron/stylelint-webpack-plugin i…
9423cdc
Add testdouble
4697c3e
Fix lint warnings
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| 'use strict'; | ||
| var minimatch = require('minimatch'); | ||
| var reduce = require('lodash.reduce'); | ||
| var assign = require('object-assign'); | ||
| var runCompilation = require('./run-compilation'); | ||
|
|
||
| /** | ||
| * Binds callback with provided options and stores initial values. | ||
| * | ||
| * @param compiler - webpack compiler object | ||
| * @param options - stylelint nodejs options | ||
| * @param callback <function(options, compilitaion)> - callback to call on emit | ||
| */ | ||
| function LintDirtyModulesPlugin (compiler, options) { | ||
| this.startTime = Date.now(); | ||
| this.prevTimestamps = {}; | ||
| this.isFirstRun = true; | ||
| this.compiler = compiler; | ||
| this.options = options; | ||
| compiler.plugin('emit', | ||
| this.lint.bind(this) // bind(this) is here to prevent context overriding by webpack | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Lints changed files provided by compilation object. | ||
| * Fully executed only after initial run. | ||
| * | ||
| * @param options - stylelint options | ||
| * @param compilation - webpack compilation object | ||
| * @param callback - to be called when execution is done | ||
| * @returns {*} | ||
| */ | ||
| LintDirtyModulesPlugin.prototype.lint = function (compilation, callback) { | ||
| if (this.isFirstRun) { | ||
| this.isFirstRun = false; | ||
| this.prevTimestamps = compilation.fileTimestamps; | ||
| return callback(); | ||
| } | ||
| var dirtyOptions = assign({}, this.options); | ||
| var glob = dirtyOptions.files.join('|'); | ||
| var changedFiles = this.getChangedFiles(compilation.fileTimestamps, glob); | ||
| this.prevTimestamps = compilation.fileTimestamps; | ||
| if (changedFiles.length) { | ||
| dirtyOptions.files = changedFiles; | ||
| runCompilation.call(this, dirtyOptions, this.compiler, callback); | ||
| } else { | ||
| callback(); | ||
| } | ||
| }; | ||
|
|
||
| /** | ||
| * Returns an array of changed files comparing current timestamps | ||
| * against cached timestamps from previous run. | ||
| * | ||
| * @param plugin - stylelint-webpack-plugin this scopr | ||
| * @param fileTimestamps - an object with keys as filenames and values as their timestamps. | ||
| * e.g. {'/filename.scss': 12444222000} | ||
| * @param glob - glob pattern to match files | ||
| */ | ||
| LintDirtyModulesPlugin.prototype.getChangedFiles = function (fileTimestamps, glob) { | ||
| return reduce(fileTimestamps, function (changedStyleFiles, timestamp, filename) { | ||
| // Check if file has been changed first ... | ||
| if ((this.prevTimestamps[filename] || this.startTime) < (fileTimestamps[filename] || Infinity) && | ||
| // ... then validate by the glob pattern. | ||
| minimatch(filename, glob, {matchBase: true}) | ||
| ) { | ||
| changedStyleFiles = changedStyleFiles.concat(filename); | ||
| } | ||
| return changedStyleFiles; | ||
| }.bind(this), []); | ||
| }; | ||
|
|
||
| module.exports = LintDirtyModulesPlugin; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| 'use strict'; | ||
|
|
||
| var td = require('testdouble'); | ||
| var formatter = require('stylelint').formatters.string; | ||
|
|
||
| var runCompilation = td.replace('../../lib/run-compilation'); | ||
|
|
||
| var LintDirtyModulesPlugin = require('../../lib/lint-dirty-modules-plugin'); | ||
|
|
||
| var configFilePath = getPath('./.stylelintrc'); | ||
| var glob = '/**/*.s?(c|a)ss'; | ||
|
|
||
| describe('lint-dirty-modules-plugin', function () { | ||
| context('lintDirtyModulesOnly flag is enabled', function () { | ||
| var LintDirtyModulesPluginCloned; | ||
| var compilerMock; | ||
| var optionsMock; | ||
|
|
||
| beforeEach(function () { | ||
| LintDirtyModulesPluginCloned = function () { | ||
| LintDirtyModulesPlugin.apply(this, arguments); | ||
| }; | ||
| LintDirtyModulesPluginCloned.prototype = Object.create(LintDirtyModulesPlugin.prototype); | ||
| LintDirtyModulesPluginCloned.prototype.constructor = LintDirtyModulesPlugin; | ||
|
|
||
| compilerMock = { | ||
| callback: null, | ||
| plugin: function plugin (event, callback) { | ||
| this.callback = callback; | ||
| } | ||
| }; | ||
|
|
||
| optionsMock = { | ||
| configFile: configFilePath, | ||
| lintDirtyModulesOnly: true, | ||
| fomatter: formatter, | ||
| files: [glob] | ||
| }; | ||
| }); | ||
|
|
||
| it('lint is called on \'emit\'', function () { | ||
| var lintStub = td.function(); | ||
| var doneStub = td.function(); | ||
| LintDirtyModulesPluginCloned.prototype.lint = lintStub; | ||
| var plugin = new LintDirtyModulesPluginCloned(compilerMock, optionsMock); | ||
|
|
||
| var compilationMock = { | ||
| fileTimestamps: { | ||
| '/udpated.scss': 5 | ||
| } | ||
| }; | ||
| compilerMock.callback(compilationMock, doneStub); | ||
|
|
||
| expect(plugin.isFirstRun).to.eql(true); | ||
| td.verify(lintStub(compilationMock, doneStub)); | ||
| }); | ||
|
|
||
| context('LintDirtyModulesPlugin.prototype.lint()', function () { | ||
| var getChangedFilesStub; | ||
| var doneStub; | ||
| var compilationMock; | ||
| var fileTimestamps = { | ||
| '/test/changed.scss': 5, | ||
| '/test/newly-created.scss': 5 | ||
| }; | ||
| var pluginMock; | ||
| beforeEach(function () { | ||
| getChangedFilesStub = td.function(); | ||
| doneStub = td.function(); | ||
| compilationMock = { | ||
| fileTimestamps: {} | ||
| }; | ||
| td.when(getChangedFilesStub({}, glob)).thenReturn([]); | ||
| td.when(getChangedFilesStub(fileTimestamps, glob)).thenReturn(Object.keys(fileTimestamps)); | ||
| pluginMock = { | ||
| getChangedFiles: getChangedFilesStub, | ||
| compiler: compilerMock, | ||
| options: optionsMock, | ||
| isFirstRun: true | ||
| }; | ||
| }); | ||
|
|
||
| it('skips compilation on first run', function () { | ||
| expect(pluginMock.isFirstRun).to.eql(true); | ||
| LintDirtyModulesPluginCloned.prototype.lint.call(pluginMock, compilationMock, doneStub); | ||
| td.verify(doneStub()); | ||
| expect(pluginMock.isFirstRun).to.eql(false); | ||
| td.verify(getChangedFilesStub, {times: 0, ignoreExtraArgs: true}); | ||
| td.verify(runCompilation, {times: 0, ignoreExtraArgs: true}); | ||
| }); | ||
|
|
||
| it('runCompilation is not called if files are not changed', function () { | ||
| pluginMock.isFirstRun = false; | ||
| LintDirtyModulesPluginCloned.prototype.lint.call(pluginMock, compilationMock, doneStub); | ||
| td.verify(doneStub()); | ||
| td.verify(runCompilation, {times: 0, ignoreExtraArgs: true}); | ||
| }); | ||
|
|
||
| it('runCompilation is called if styles are changed', function () { | ||
| pluginMock.isFirstRun = false; | ||
| compilationMock = { | ||
| fileTimestamps: fileTimestamps | ||
| }; | ||
| LintDirtyModulesPluginCloned.prototype.lint.call(pluginMock, compilationMock, doneStub); | ||
| optionsMock.files = Object.keys(fileTimestamps); | ||
| td.verify(runCompilation(optionsMock, compilerMock, doneStub)); | ||
| }); | ||
| }); | ||
|
|
||
| context('LintDirtyModulesPlugin.prototype.getChangedFiles()', function () { | ||
| var pluginMock; | ||
| before(function () { | ||
| pluginMock = { | ||
| compiler: compilerMock, | ||
| options: optionsMock, | ||
| isFirstRun: true, | ||
| startTime: 10, | ||
| prevTimestamps: { | ||
| '/test/changed.scss': 5, | ||
| '/test/removed.scss': 5, | ||
| '/test/changed.js': 5 | ||
| } | ||
| }; | ||
| }); | ||
| it('returns changed style files', function () { | ||
| var fileTimestamps = { | ||
| '/test/changed.scss': 20, | ||
| '/test/changed.js': 20, | ||
| '/test/newly-created.scss': 15 | ||
| }; | ||
|
|
||
| expect( | ||
| LintDirtyModulesPluginCloned.prototype.getChangedFiles.call(pluginMock, fileTimestamps, glob)).to.eql([ | ||
| '/test/changed.scss', | ||
| '/test/newly-created.scss' | ||
| ] | ||
| ); | ||
| }); | ||
| }); | ||
| }); | ||
| }); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back 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.
please use
yarn add minimatchso theyarn.lockfile is updated.Uh oh!
There was an error while loading. Please reload this page.
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
minimatchavailable fromnode-glob? could remove unnecessary dependencies :)Uh oh!
There was an error while loading. Please reload this page.
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-globwhich doesn't export any kind of function to use here :( But I ranyarn add minimatch, surprisingly, I did not cause any updates ofyarn.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!