Skip to content

Commit 4035ebe

Browse files
feat: support stylelint 15 (#325)
* feat: support stylelint 15 * ci: use npm v8 * ci: update
1 parent 83c8d62 commit 4035ebe

File tree

13 files changed

+3966
-3142
lines changed

13 files changed

+3966
-3142
lines changed

.github/workflows/nodejs.yml

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
node-version: ${{ matrix.node-version }}
3434

3535
- name: Use latest NPM
36-
run: sudo npm i -g npm
36+
run: sudo npm i -g npm@8
3737

3838
- name: Install dependencies
3939
run: npm ci
@@ -54,8 +54,11 @@ jobs:
5454
matrix:
5555
os: [ubuntu-latest, windows-latest, macos-latest]
5656
node-version: [12.x, 14.x, 16.x]
57-
stylelint-version: [13.x, 14.x]
57+
stylelint-version: [13.x, 14.x, 15.x]
5858
webpack-version: [4, latest]
59+
exclude:
60+
- node-version: 12.x
61+
stylelint-version: 15.x
5962

6063
runs-on: ${{ matrix.os }}
6164

@@ -73,11 +76,11 @@ jobs:
7376

7477
- name: Use latest NPM on ubuntu/macos
7578
if: matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest'
76-
run: sudo npm i -g npm
79+
run: sudo npm i -g npm@8
7780

7881
- name: Use latest NPM on windows
7982
if: matrix.os == 'windows-latest'
80-
run: npm i -g npm
83+
run: npm i -g npm@8
8184

8285
- name: Install dependencies
8386
run: npm ci

package-lock.json

Lines changed: 3816 additions & 2849 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -42,49 +42,49 @@
4242
"types"
4343
],
4444
"peerDependencies": {
45-
"stylelint": "^13.0.0 || ^14.0.0",
45+
"stylelint": "^13.0.0 || ^14.0.0 || ^15.0.0",
4646
"webpack": "^4.0.0 || ^5.0.0"
4747
},
4848
"dependencies": {
4949
"arrify": "^2.0.1",
50-
"globby": "^11.0.4",
51-
"jest-worker": "^28.1.0",
50+
"globby": "^11.1.0",
51+
"jest-worker": "^28.1.3",
5252
"micromatch": "^4.0.5",
5353
"normalize-path": "^3.0.0",
5454
"schema-utils": "^3.1.1"
5555
},
5656
"devDependencies": {
57-
"@babel/cli": "^7.17.10",
58-
"@babel/core": "^7.18.0",
59-
"@babel/preset-env": "^7.18.0",
60-
"@commitlint/cli": "^12.1.4",
61-
"@commitlint/config-conventional": "^12.1.4",
57+
"@babel/cli": "^7.21.0",
58+
"@babel/core": "^7.21.0",
59+
"@babel/preset-env": "^7.20.2",
60+
"@commitlint/cli": "^16.2.4",
61+
"@commitlint/config-conventional": "^16.2.4",
6262
"@types/fs-extra": "^9.0.13",
6363
"@types/micromatch": "^4.0.2",
64+
"@types/node": "^18.14.6",
6465
"@types/normalize-path": "^3.0.0",
6566
"@types/webpack": "^5.28.0",
6667
"@webpack-contrib/eslint-config-webpack": "^3.0.0",
6768
"babel-eslint": "^10.1.0",
68-
"babel-jest": "^28.1.0",
69-
"chokidar": "^3.5.3",
69+
"babel-jest": "^28.1.3",
7070
"cross-env": "^7.0.3",
71-
"del": "^6.1.0",
71+
"del": "^6.1.1",
7272
"del-cli": "^3.0.1",
7373
"eslint": "^7.32.0",
74-
"eslint-config-prettier": "^8.5.0",
75-
"eslint-plugin-import": "^2.26.0",
74+
"eslint-config-prettier": "^8.7.0",
75+
"eslint-plugin-import": "^2.27.5",
7676
"file-loader": "^6.2.0",
7777
"fs-extra": "^9.1.0",
7878
"husky": "^6.0.0",
79-
"jest": "^28.1.0",
79+
"jest": "^28.1.3",
8080
"lint-staged": "^11.2.6",
8181
"npm-run-all": "^4.1.5",
8282
"postcss-scss": "^3.0.5",
83-
"prettier": "^2.6.2",
83+
"prettier": "^2.8.4",
8484
"standard-version": "^9.5.0",
85-
"stylelint": "^14.8.2",
86-
"typescript": "^4.6.4",
87-
"webpack": "^5.72.1"
85+
"stylelint": "^15.2.0",
86+
"typescript": "^4.9.5",
87+
"webpack": "^5.76.0"
8888
},
8989
"keywords": [
9090
"stylelint",

src/getStylelint.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@ const { getStylelintOptions } = require('./options');
1010
/** @type {{[key: string]: any}} */
1111
const cache = {};
1212

13-
/** @typedef {import('stylelint')} Stylelint */
13+
/** @typedef {{lint: (options: LinterOptions) => Promise<LinterResult>, formatters: { [k: string]: Formatter }}} Stylelint */
1414
/** @typedef {import('stylelint').LintResult} LintResult */
15+
/** @typedef {import('stylelint').LinterOptions} LinterOptions */
16+
/** @typedef {import('stylelint').LinterResult} LinterResult */
17+
/** @typedef {import('stylelint').Formatter} Formatter */
18+
/** @typedef {import('stylelint').FormatterType} FormatterType */
1519
/** @typedef {import('./options').Options} Options */
20+
/** @typedef {(stylelint: Stylelint, filePath: string) => Promise<boolean>} isPathIgnored */
1621
/** @typedef {() => Promise<void>} AsyncTask */
1722
/** @typedef {(files: string|string[]) => Promise<LintResult[]>} LintTask */
18-
/** @typedef {{api: import('stylelint').InternalApi, stylelint: Stylelint, lintFiles: LintTask, cleanup: AsyncTask, threads: number, }} Linter */
23+
/** @typedef {{stylelint: Stylelint, isPathIgnored: isPathIgnored, lintFiles: LintTask, cleanup: AsyncTask, threads: number }} Linter */
1924
/** @typedef {JestWorker & {lintFiles: LintTask}} Worker */
2025

2126
/**
@@ -26,9 +31,23 @@ function loadStylelint(options) {
2631
const stylelintOptions = getStylelintOptions(options);
2732
const stylelint = setup(options, stylelintOptions);
2833

34+
/** @type {isPathIgnored} */
35+
let isPathIgnored;
36+
37+
try {
38+
isPathIgnored = require(`${options.stylelintPath}/lib/isPathIgnored`);
39+
} catch (e) {
40+
try {
41+
// @ts-ignore
42+
isPathIgnored = require('stylelint/lib/isPathIgnored');
43+
} catch (_) {
44+
isPathIgnored = () => Promise.resolve(false);
45+
}
46+
}
47+
2948
return {
3049
stylelint,
31-
api: stylelint.createLinter(stylelintOptions),
50+
isPathIgnored,
3251
lintFiles,
3352
cleanup: async () => {},
3453
threads: 1,

src/index.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,14 @@ class StylelintWebpackPlugin {
9393
}
9494

9595
compiler.hooks.thisCompilation.tap(this.key, (compilation) => {
96+
/** @type {import('./getStylelint').Stylelint} */
97+
let stylelint;
98+
9699
/** @type {import('./linter').Linter} */
97100
let lint;
98101

99-
/** @type {import('stylelint').InternalApi} */
100-
let api;
102+
/** @type {import('./linter').isPathIgnored} */
103+
let isPathIgnored;
101104

102105
/** @type {import('./linter').Reporter} */
103106
let report;
@@ -106,7 +109,7 @@ class StylelintWebpackPlugin {
106109
let threads;
107110

108111
try {
109-
({ lint, api, report, threads } = linter(
112+
({ stylelint, lint, isPathIgnored, report, threads } = linter(
110113
this.key,
111114
options,
112115
compilation
@@ -122,9 +125,9 @@ class StylelintWebpackPlugin {
122125
const files = (
123126
await Promise.all(
124127
this.getFiles(compiler, wanted, exclude).map(
125-
async (/** @type {string | undefined} */ file) => {
128+
async (/** @type {string} */ file) => {
126129
try {
127-
return (await api.isPathIgnored(file)) ? false : file;
130+
return (await isPathIgnored(stylelint, file)) ? false : file;
128131
} catch (e) {
129132
return file;
130133
}

src/linter.js

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ const arrify = require('arrify');
55
const StylelintError = require('./StylelintError');
66
const getStylelint = require('./getStylelint');
77

8-
/** @typedef {import('stylelint')} Stylelint */
9-
/** @typedef {import('stylelint').LintResult} LintResult */
10-
/** @typedef {import('stylelint').InternalApi} InternalApi */
118
/** @typedef {import('webpack').Compiler} Compiler */
129
/** @typedef {import('webpack').Compilation} Compilation */
10+
/** @typedef {import('./getStylelint').Stylelint} Stylelint */
11+
/** @typedef {import('./getStylelint').LintResult} LintResult */
12+
/** @typedef {import('./getStylelint').LinterResult} LinterResult */
13+
/** @typedef {import('./getStylelint').Formatter} Formatter */
14+
/** @typedef {import('./getStylelint').FormatterType} FormatterType */
15+
/** @typedef {import('./getStylelint').isPathIgnored} isPathIgnored */
1316
/** @typedef {import('./options').Options} Options */
14-
/** @typedef {import('./options').FormatterType} FormatterType */
15-
/** @typedef {((results: LintResult[]) => string)} FormatterFunction */
1617
/** @typedef {(compilation: Compilation) => Promise<void>} GenerateReport */
1718
/** @typedef {{errors?: StylelintError, warnings?: StylelintError, generateReportAsset?: GenerateReport}} Report */
1819
/** @typedef {() => Promise<Report>} Reporter */
@@ -26,14 +27,14 @@ const resultStorage = new WeakMap();
2627
* @param {string|undefined} key
2728
* @param {Options} options
2829
* @param {Compilation} compilation
29-
* @returns {{api: InternalApi, lint: Linter, report: Reporter, threads: number}}
30+
* @returns {{stylelint: Stylelint, isPathIgnored: isPathIgnored, lint: Linter, report: Reporter, threads: number}}
3031
*/
3132
function linter(key, options, compilation) {
3233
/** @type {Stylelint} */
3334
let stylelint;
3435

35-
/** @type {InternalApi} */
36-
let api;
36+
/** @type {isPathIgnored} */
37+
let isPathIgnored;
3738

3839
/** @type {(files: string|string[]) => Promise<LintResult[]>} */
3940
let lintFiles;
@@ -50,7 +51,7 @@ function linter(key, options, compilation) {
5051
const crossRunResultStorage = getResultStorage(compilation);
5152

5253
try {
53-
({ stylelint, api, lintFiles, cleanup, threads } = getStylelint(
54+
({ stylelint, isPathIgnored, lintFiles, cleanup, threads } = getStylelint(
5455
key,
5556
options
5657
));
@@ -59,8 +60,9 @@ function linter(key, options, compilation) {
5960
}
6061

6162
return {
63+
stylelint,
6264
lint,
63-
api,
65+
isPathIgnored,
6466
report,
6567
threads,
6668
};
@@ -102,9 +104,22 @@ function linter(key, options, compilation) {
102104
}
103105

104106
const formatter = loadFormatter(stylelint, options.formatter);
107+
108+
/** @type {LinterResult} */
109+
const returnValue = {
110+
// @ts-ignore
111+
cwd: options.cwd,
112+
errored: false,
113+
results: [],
114+
output: '',
115+
reportedDisables: [],
116+
ruleMetadata: getRuleMetadata(results),
117+
};
118+
105119
const { errors, warnings } = formatResults(
106120
formatter,
107-
parseResults(options, results)
121+
parseResults(options, results),
122+
returnValue
108123
);
109124

110125
return {
@@ -146,42 +161,42 @@ function linter(key, options, compilation) {
146161
return;
147162
}
148163

149-
const content = outputReport.formatter
150-
? loadFormatter(stylelint, outputReport.formatter)(results)
151-
: formatter(results);
164+
const content = outputReport.formatter;
165+
loadFormatter(stylelint, outputReport.formatter)(results, returnValue);
166+
formatter(results, returnValue);
152167

153168
let { filePath } = outputReport;
154169
if (!isAbsolute(filePath)) {
155170
filePath = join(compiler.outputPath, filePath);
156171
}
157172

158-
await save(filePath, content);
173+
await save(filePath, String(content));
159174
}
160175
}
161176
}
162177

163178
/**
164-
* @param {FormatterFunction} formatter
179+
* @param {Formatter} formatter
165180
* @param {{ errors: LintResult[]; warnings: LintResult[]; }} results
181+
* @param {LinterResult} returnValue
166182
* @returns {{errors?: StylelintError, warnings?: StylelintError}}
167183
*/
168-
function formatResults(formatter, results) {
184+
function formatResults(formatter, results, returnValue) {
169185
let errors;
170186
let warnings;
171187
if (results.warnings.length > 0) {
172-
warnings = new StylelintError(formatter(results.warnings));
188+
warnings = new StylelintError(formatter(results.warnings, returnValue));
173189
}
174190

175191
if (results.errors.length > 0) {
176-
errors = new StylelintError(formatter(results.errors));
192+
errors = new StylelintError(formatter(results.errors, returnValue));
177193
}
178194

179195
return {
180196
errors,
181197
warnings,
182198
};
183199
}
184-
185200
/**
186201
* @param {Options} options
187202
* @param {LintResult[]} results
@@ -227,7 +242,7 @@ function parseResults(options, results) {
227242
/**
228243
* @param {Stylelint} stylelint
229244
* @param {FormatterType=} formatter
230-
* @returns {FormatterFunction}
245+
* @returns {Formatter}
231246
*/
232247
function loadFormatter(stylelint, formatter) {
233248
if (typeof formatter === 'function') {
@@ -278,4 +293,21 @@ function getResultStorage({ compiler }) {
278293
return storage;
279294
}
280295

296+
/**
297+
* @param {LintResult[]} lintResults
298+
*/
299+
/* istanbul ignore next */
300+
function getRuleMetadata(lintResults) {
301+
const [lintResult] = lintResults;
302+
303+
// eslint-disable-next-line no-undefined
304+
if (lintResult === undefined) return {};
305+
306+
// eslint-disable-next-line no-underscore-dangle, no-undefined
307+
if (lintResult._postcssResult === undefined) return {};
308+
309+
// eslint-disable-next-line no-underscore-dangle
310+
return lintResult._postcssResult.stylelint.ruleMetadata;
311+
}
312+
281313
module.exports = linter;

src/options.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ const { validate } = require('schema-utils');
33
// @ts-ignore
44
const schema = require('./options.json');
55

6-
/** @typedef {import("stylelint")} stylelint */
7-
/** @typedef {import("stylelint").LinterOptions} StylelintOptions */
8-
/** @typedef {import("stylelint").FormatterType} FormatterType */
6+
/** @typedef {import('./getStylelint').LinterOptions} StylelintOptions */
7+
/** @typedef {import('./getStylelint').FormatterType} FormatterType */
98

109
/**
1110
* @typedef {Object} OutputReport

src/worker.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
/** @typedef {import('stylelint')} Stylelint */
2-
/** @typedef {import("stylelint").LinterOptions} StylelintOptions */
1+
/** @typedef {import('./getStylelint').Stylelint} Stylelint */
2+
/** @typedef {import('./getStylelint').LinterOptions} StylelintOptions */
33
/** @typedef {import('./options').Options} Options */
44

55
Object.assign(module.exports, {

test/fail-on-config.test.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ describe('fail on config', () => {
1212
expect(stats.hasWarnings()).toBe(false);
1313
expect(stats.hasErrors()).toBe(true);
1414
expect(errors).toHaveLength(1);
15-
expect(errors[0].message).toMatch(/Map keys must be unique/);
1615
done();
1716
});
1817
});

0 commit comments

Comments
 (0)