stylelint-webpack-plugin 2.1.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -5
- package/declarations/StylelintError.d.ts +2 -30
- package/declarations/cjs.d.ts +2 -1
- package/declarations/getStylelint.d.ts +72 -0
- package/declarations/index.d.ts +41 -3
- package/declarations/linter.d.ts +73 -38
- package/declarations/options.d.ts +110 -0
- package/declarations/utils.d.ts +12 -7
- package/declarations/worker.d.ts +51 -0
- package/dist/StylelintError.js +3 -18
- package/dist/getStylelint.js +132 -0
- package/dist/index.js +215 -31
- package/dist/linter.js +246 -80
- package/dist/options.js +95 -0
- package/dist/options.json +38 -4
- package/dist/utils.js +66 -11
- package/dist/worker.js +50 -0
- package/package.json +39 -37
- package/CHANGELOG.md +0 -137
- package/declarations/LintDirtyModulesPlugin.d.ts +0 -64
- package/declarations/getOptions.d.ts +0 -32
- package/dist/LintDirtyModulesPlugin.js +0 -118
- package/dist/getOptions.js +0 -74
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = getStylelint;
|
|
7
|
+
|
|
8
|
+
var _os = require("os");
|
|
9
|
+
|
|
10
|
+
var _jestWorker = require("jest-worker");
|
|
11
|
+
|
|
12
|
+
var _worker = require("./worker");
|
|
13
|
+
|
|
14
|
+
var _utils = require("./utils");
|
|
15
|
+
|
|
16
|
+
var _options = require("./options");
|
|
17
|
+
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
|
|
20
|
+
/** @type {{[key: string]: any}} */
|
|
21
|
+
const cache = {};
|
|
22
|
+
/** @typedef {import('stylelint')} Stylelint */
|
|
23
|
+
|
|
24
|
+
/** @typedef {import('stylelint').LintResult} LintResult */
|
|
25
|
+
|
|
26
|
+
/** @typedef {import('./options').Options} Options */
|
|
27
|
+
|
|
28
|
+
/** @typedef {() => Promise<void>} AsyncTask */
|
|
29
|
+
|
|
30
|
+
/** @typedef {(files: string|string[]) => Promise<LintResult[]>} LintTask */
|
|
31
|
+
|
|
32
|
+
/** @typedef {JestWorker & {lintFiles: LintTask}} Worker */
|
|
33
|
+
|
|
34
|
+
/** @typedef {{stylelint: Stylelint, lintFiles: LintTask, cleanup: AsyncTask, threads: number, }} Linter */
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {Options} options
|
|
38
|
+
* @returns {Linter}
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
function loadStylelint(options) {
|
|
42
|
+
const stylelint = (0, _worker.setup)(options, (0, _options.getStylelintOptions)(options));
|
|
43
|
+
return {
|
|
44
|
+
stylelint,
|
|
45
|
+
lintFiles: _worker.lintFiles,
|
|
46
|
+
cleanup: async () => {},
|
|
47
|
+
threads: 1
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* @param {string|undefined} key
|
|
52
|
+
* @param {number} poolSize
|
|
53
|
+
* @param {Options} options
|
|
54
|
+
* @returns {Linter}
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
function loadStylelintThreaded(key, poolSize, options) {
|
|
59
|
+
const cacheKey = getCacheKey(key, options);
|
|
60
|
+
|
|
61
|
+
const source = require.resolve('./worker');
|
|
62
|
+
|
|
63
|
+
const workerOptions = {
|
|
64
|
+
enableWorkerThreads: true,
|
|
65
|
+
numWorkers: poolSize,
|
|
66
|
+
setupArgs: [options, (0, _options.getStylelintOptions)(options)]
|
|
67
|
+
};
|
|
68
|
+
const local = loadStylelint(options);
|
|
69
|
+
/** @type {Worker?} */
|
|
70
|
+
// prettier-ignore
|
|
71
|
+
|
|
72
|
+
let worker =
|
|
73
|
+
/** @type {Worker} */
|
|
74
|
+
new _jestWorker.Worker(source, workerOptions);
|
|
75
|
+
/** @type {Linter} */
|
|
76
|
+
|
|
77
|
+
const context = { ...local,
|
|
78
|
+
threads: poolSize,
|
|
79
|
+
lintFiles: async files =>
|
|
80
|
+
/* istanbul ignore next */
|
|
81
|
+
worker ? worker.lintFiles(files) : local.lintFiles(files),
|
|
82
|
+
cleanup: async () => {
|
|
83
|
+
cache[cacheKey] = local;
|
|
84
|
+
|
|
85
|
+
context.lintFiles = files => local.lintFiles(files);
|
|
86
|
+
/* istanbul ignore next */
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
if (worker) {
|
|
90
|
+
worker.end();
|
|
91
|
+
worker = null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
return context;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* @param {string|undefined} key
|
|
99
|
+
* @param {Options} options
|
|
100
|
+
* @returns {Linter}
|
|
101
|
+
*/
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
function getStylelint(key, {
|
|
105
|
+
threads,
|
|
106
|
+
...options
|
|
107
|
+
}) {
|
|
108
|
+
const max = typeof threads !== 'number' ? threads ? (0, _os.cpus)().length - 1 : 1 : threads;
|
|
109
|
+
const cacheKey = getCacheKey(key, {
|
|
110
|
+
threads,
|
|
111
|
+
...options
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (!cache[cacheKey]) {
|
|
115
|
+
cache[cacheKey] = max > 1 ? loadStylelintThreaded(key, max, options) : loadStylelint(options);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return cache[cacheKey];
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* @param {string|undefined} key
|
|
122
|
+
* @param {Options} options
|
|
123
|
+
* @returns {string}
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
function getCacheKey(key, options) {
|
|
128
|
+
return JSON.stringify({
|
|
129
|
+
key,
|
|
130
|
+
options
|
|
131
|
+
}, _utils.jsonStringifyReplacerSortKeys);
|
|
132
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -7,9 +7,13 @@ exports.default = void 0;
|
|
|
7
7
|
|
|
8
8
|
var _path = require("path");
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var _arrify = _interopRequireDefault(require("arrify"));
|
|
11
11
|
|
|
12
|
-
var
|
|
12
|
+
var _globby = _interopRequireDefault(require("globby"));
|
|
13
|
+
|
|
14
|
+
var _micromatch = require("micromatch");
|
|
15
|
+
|
|
16
|
+
var _options = require("./options");
|
|
13
17
|
|
|
14
18
|
var _linter = _interopRequireDefault(require("./linter"));
|
|
15
19
|
|
|
@@ -17,10 +21,31 @@ var _utils = require("./utils");
|
|
|
17
21
|
|
|
18
22
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
19
23
|
|
|
24
|
+
// @ts-ignore
|
|
25
|
+
// @ts-ignore
|
|
26
|
+
|
|
20
27
|
/** @typedef {import('webpack').Compiler} Compiler */
|
|
28
|
+
|
|
29
|
+
/** @typedef {import('webpack').Module} Module */
|
|
30
|
+
|
|
31
|
+
/** @typedef {import('./options').Options} Options */
|
|
32
|
+
|
|
33
|
+
/** @typedef {Partial<{timestamp:number} | number>} FileSystemInfoEntry */
|
|
34
|
+
const STYLELINT_PLUGIN = 'StylelintWebpackPlugin';
|
|
35
|
+
let counter = 0;
|
|
36
|
+
|
|
21
37
|
class StylelintWebpackPlugin {
|
|
38
|
+
/**
|
|
39
|
+
* @param {Options} options
|
|
40
|
+
*/
|
|
22
41
|
constructor(options = {}) {
|
|
23
|
-
this.
|
|
42
|
+
this.key = STYLELINT_PLUGIN;
|
|
43
|
+
this.options = (0, _options.getOptions)(options);
|
|
44
|
+
this.run = this.run.bind(this);
|
|
45
|
+
this.startTime = Date.now();
|
|
46
|
+
/** @type {ReadonlyMap<string, null | FileSystemInfoEntry | "ignore" | undefined>} */
|
|
47
|
+
|
|
48
|
+
this.prevTimestamps = new Map();
|
|
24
49
|
}
|
|
25
50
|
/**
|
|
26
51
|
* @param {Compiler} compiler
|
|
@@ -29,35 +54,112 @@ class StylelintWebpackPlugin {
|
|
|
29
54
|
|
|
30
55
|
|
|
31
56
|
apply(compiler) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const {
|
|
41
|
-
name: plugin
|
|
42
|
-
} = this.constructor;
|
|
43
|
-
|
|
44
|
-
if (options.lintDirtyModulesOnly) {
|
|
45
|
-
const lintDirty = new _LintDirtyModulesPlugin.default(lint, compiler, options);
|
|
46
|
-
/* istanbul ignore next */
|
|
47
|
-
|
|
48
|
-
compiler.hooks.watchRun.tapAsync(plugin, (compilation, callback) => {
|
|
49
|
-
lintDirty.apply(compilation, callback);
|
|
50
|
-
});
|
|
51
|
-
} else {
|
|
52
|
-
compiler.hooks.run.tapAsync(plugin, (compilation, callback) => {
|
|
53
|
-
(0, _linter.default)(lint, options, compilation, callback);
|
|
54
|
-
});
|
|
55
|
-
/* istanbul ignore next */
|
|
56
|
-
|
|
57
|
-
compiler.hooks.watchRun.tapAsync(plugin, (compilation, callback) => {
|
|
58
|
-
(0, _linter.default)(lint, options, compilation, callback);
|
|
59
|
-
});
|
|
57
|
+
// Generate key for each compilation,
|
|
58
|
+
// this differentiates one from the other when being cached.
|
|
59
|
+
this.key = compiler.name || `${this.key}_${counter += 1}`; // If `lintDirtyModulesOnly` is disabled,
|
|
60
|
+
// execute the linter on the build
|
|
61
|
+
|
|
62
|
+
if (!this.options.lintDirtyModulesOnly) {
|
|
63
|
+
compiler.hooks.run.tapPromise(this.key, this.run);
|
|
60
64
|
}
|
|
65
|
+
|
|
66
|
+
let isFirstRun = this.options.lintDirtyModulesOnly;
|
|
67
|
+
compiler.hooks.watchRun.tapPromise(this.key, c => {
|
|
68
|
+
if (isFirstRun) {
|
|
69
|
+
isFirstRun = false;
|
|
70
|
+
return Promise.resolve();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return this.run(c);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* @param {Compiler} compiler
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
async run(compiler) {
|
|
82
|
+
// Do not re-hook
|
|
83
|
+
|
|
84
|
+
/* istanbul ignore if */
|
|
85
|
+
if ( // @ts-ignore
|
|
86
|
+
compiler.hooks.thisCompilation.taps.find(({
|
|
87
|
+
name
|
|
88
|
+
}) => name === this.key)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const context = this.getContext(compiler);
|
|
93
|
+
const options = { ...this.options,
|
|
94
|
+
exclude: (0, _utils.parseFiles)(this.options.exclude || ['**/node_modules/**', compiler.options.output.path], context),
|
|
95
|
+
extensions: (0, _arrify.default)(this.options.extensions),
|
|
96
|
+
files: (0, _utils.parseFiles)(this.options.files || '', context)
|
|
97
|
+
};
|
|
98
|
+
const wanted = (0, _utils.parseFoldersToGlobs)(options.files, options.extensions);
|
|
99
|
+
const exclude = (0, _utils.parseFoldersToGlobs)(options.exclude);
|
|
100
|
+
compiler.hooks.thisCompilation.tap(this.key, compilation => {
|
|
101
|
+
/** @type {import('./linter').Linter} */
|
|
102
|
+
let lint;
|
|
103
|
+
/** @type {import('./linter').Reporter} */
|
|
104
|
+
|
|
105
|
+
let report;
|
|
106
|
+
/** @type number */
|
|
107
|
+
|
|
108
|
+
let threads;
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
({
|
|
112
|
+
lint,
|
|
113
|
+
report,
|
|
114
|
+
threads
|
|
115
|
+
} = (0, _linter.default)(this.key, options, compilation));
|
|
116
|
+
} catch (e) {
|
|
117
|
+
compilation.errors.push(e);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
compilation.hooks.finishModules.tap(this.key, () => {
|
|
122
|
+
const files = this.getFiles(compiler, wanted, exclude);
|
|
123
|
+
|
|
124
|
+
if (threads > 1) {
|
|
125
|
+
for (const file of files) {
|
|
126
|
+
lint((0, _utils.parseFiles)(file, context));
|
|
127
|
+
}
|
|
128
|
+
} else if (files.length > 0) {
|
|
129
|
+
lint((0, _utils.parseFiles)(files, context));
|
|
130
|
+
}
|
|
131
|
+
}); // await and interpret results
|
|
132
|
+
|
|
133
|
+
compilation.hooks.additionalAssets.tapPromise(this.key, processResults);
|
|
134
|
+
|
|
135
|
+
async function processResults() {
|
|
136
|
+
const {
|
|
137
|
+
errors,
|
|
138
|
+
warnings,
|
|
139
|
+
generateReportAsset
|
|
140
|
+
} = await report();
|
|
141
|
+
|
|
142
|
+
if (warnings && !options.failOnWarning) {
|
|
143
|
+
// @ts-ignore
|
|
144
|
+
compilation.warnings.push(warnings);
|
|
145
|
+
} else if (warnings && options.failOnWarning) {
|
|
146
|
+
// @ts-ignore
|
|
147
|
+
compilation.errors.push(warnings);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (errors && options.failOnError) {
|
|
151
|
+
// @ts-ignore
|
|
152
|
+
compilation.errors.push(errors);
|
|
153
|
+
} else if (errors && !options.failOnError) {
|
|
154
|
+
// @ts-ignore
|
|
155
|
+
compilation.warnings.push(errors);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (generateReportAsset) {
|
|
159
|
+
await generateReportAsset(compilation);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
});
|
|
61
163
|
}
|
|
62
164
|
/**
|
|
63
165
|
*
|
|
@@ -77,6 +179,88 @@ class StylelintWebpackPlugin {
|
|
|
77
179
|
|
|
78
180
|
return this.options.context;
|
|
79
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* @param {Compiler} compiler
|
|
184
|
+
* @param {string[]} wanted
|
|
185
|
+
* @param {string[]} exclude
|
|
186
|
+
* @returns {string[]}
|
|
187
|
+
*/
|
|
188
|
+
// eslint-disable-next-line no-unused-vars
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
getFiles(compiler, wanted, exclude) {
|
|
192
|
+
// webpack 5
|
|
193
|
+
if (compiler.modifiedFiles) {
|
|
194
|
+
return Array.from(compiler.modifiedFiles).filter(file => (0, _micromatch.isMatch)(file, wanted, {
|
|
195
|
+
dot: true
|
|
196
|
+
}) && !(0, _micromatch.isMatch)(file, exclude, {
|
|
197
|
+
dot: true
|
|
198
|
+
}));
|
|
199
|
+
} // webpack 4
|
|
200
|
+
|
|
201
|
+
/* istanbul ignore next */
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
if (compiler.fileTimestamps && compiler.fileTimestamps.size > 0) {
|
|
205
|
+
return this.getChangedFiles(compiler.fileTimestamps).filter(file => (0, _micromatch.isMatch)(file, wanted, {
|
|
206
|
+
dot: true
|
|
207
|
+
}) && !(0, _micromatch.isMatch)(file, exclude, {
|
|
208
|
+
dot: true
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return _globby.default.sync(wanted, {
|
|
213
|
+
dot: true,
|
|
214
|
+
ignore: exclude
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* @param {ReadonlyMap<string, null | FileSystemInfoEntry | "ignore" | undefined>} fileTimestamps
|
|
219
|
+
* @returns {string[]}
|
|
220
|
+
*/
|
|
221
|
+
|
|
222
|
+
/* istanbul ignore next */
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
getChangedFiles(fileTimestamps) {
|
|
226
|
+
/**
|
|
227
|
+
* @param {null | FileSystemInfoEntry | "ignore" | undefined} fileSystemInfoEntry
|
|
228
|
+
* @returns {Partial<number>}
|
|
229
|
+
*/
|
|
230
|
+
const getTimestamps = fileSystemInfoEntry => {
|
|
231
|
+
// @ts-ignore
|
|
232
|
+
if (fileSystemInfoEntry && fileSystemInfoEntry.timestamp) {
|
|
233
|
+
// @ts-ignore
|
|
234
|
+
return fileSystemInfoEntry.timestamp;
|
|
235
|
+
} // @ts-ignore
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
return fileSystemInfoEntry;
|
|
239
|
+
};
|
|
240
|
+
/**
|
|
241
|
+
* @param {string} filename
|
|
242
|
+
* @param {null | FileSystemInfoEntry | "ignore" | undefined} fileSystemInfoEntry
|
|
243
|
+
* @returns {boolean}
|
|
244
|
+
*/
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
const hasFileChanged = (filename, fileSystemInfoEntry) => {
|
|
248
|
+
const prevTimestamp = getTimestamps(this.prevTimestamps.get(filename));
|
|
249
|
+
const timestamp = getTimestamps(fileSystemInfoEntry);
|
|
250
|
+
return (prevTimestamp || this.startTime) < (timestamp || Infinity);
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const changedFiles = [];
|
|
254
|
+
|
|
255
|
+
for (const [filename, timestamp] of fileTimestamps.entries()) {
|
|
256
|
+
if (hasFileChanged(filename, timestamp)) {
|
|
257
|
+
changedFiles.push(filename);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
this.prevTimestamps = fileTimestamps;
|
|
262
|
+
return changedFiles;
|
|
263
|
+
}
|
|
80
264
|
|
|
81
265
|
}
|
|
82
266
|
|