Skip to content

Commit 72937f2

Browse files
authored
refactor(plugin-stylelint): wip (#912)
1 parent a11dfbc commit 72937f2

File tree

5 files changed

+118
-20
lines changed

5 files changed

+118
-20
lines changed
Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
import type { ConfigRuleSettings, Primary, Severity } from 'stylelint';
2+
3+
// Typing resource https://stylelint.io/user-guide/configure/
4+
/** Config rule setting of Stylelint excluding null and undefined values */
5+
export type ActiveConfigRuleSetting = Exclude<
6+
ConfigRuleSettings<Primary, Record<string, unknown>>,
7+
null | undefined
8+
>;
9+
10+
/** Output of the `getNormalizedConfigForFile` function. Config file of Stylelint */
111
export type NormalizedStyleLintConfig = {
2-
config: { rules: Record<string, unknown[]> };
12+
config: {
13+
rules: Record<string, ConfigRuleSettings<Primary, Record<string, any>>>;
14+
defaultSeverity?: Severity;
15+
};
316
};

packages/plugin-stylelint/src/lib/runner/normalize-config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export function getNormalizedConfigForFile({
1010
cwd,
1111
}: Required<Pick<StyleLintTarget, 'stylelintrc'>> & {
1212
cwd?: string;
13-
}) {
13+
}): Promise<NormalizedStyleLintConfig> {
1414
const _linter = stylelint._createLinter({ configFile: stylelintrc });
1515
const configFile =
1616
stylelintrc ?? path.join(cwd ?? process.cwd(), '.stylelintrc.json');

packages/plugin-stylelint/src/lib/runner/utils.ts

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import type { ConfigRuleSettings, LintResult, Warning } from 'stylelint';
1+
import type { LintResult, Secondary, Severity, Warning } from 'stylelint';
22
import type { Audit, AuditOutputs, AuditReport } from '@code-pushup/models';
3+
import type { ActiveConfigRuleSetting } from './model.js';
34

45
export function mapStylelintResultsToAudits(
56
results: LintResult[],
@@ -73,24 +74,39 @@ export function getSeverityFromWarning(warning: Warning): 'error' | 'warning' {
7374
throw new Error(`Unknown severity: ${severity}`);
7475
}
7576

77+
/**
78+
* Function that returns the severity from a ruleConfig.
79+
* If the ruleConfig is not an array, the default severity of the config file must be returned, since the custom severity can be only specified in an array.
80+
* If the ruleConfig is an array, a custom severity might have been set, in that case, it must be returned
81+
* @param ruleConfig - The Stylelint rule config value
82+
* @param defaultSeverity - The default severity of the config file. By default, it's 'error'
83+
* @returns The severity (EX: 'error' | 'warning')
84+
*/
7685
export function getSeverityFromRuleConfig(
77-
ruleConfig: ConfigRuleSettings<unknown, { severity?: 'error' | 'warning' }>,
78-
): 'error' | 'warning' {
79-
if (!ruleConfig) {
80-
// Default severity if the ruleConfig is null or undefined
81-
return 'error';
86+
ruleConfig: ActiveConfigRuleSetting,
87+
defaultSeverity: Severity = 'error',
88+
): Severity {
89+
//If it's not an array, the default severity of the config file must be returned, since the custom severity can be only specified in an array.
90+
if (!Array.isArray(ruleConfig)) {
91+
return defaultSeverity;
8292
}
8393

84-
if (Array.isArray(ruleConfig)) {
85-
const options = ruleConfig[1];
86-
if (options && typeof options === 'object' && 'severity' in options) {
87-
const severity = options.severity;
88-
if (severity === 'warning') {
89-
return 'warning';
90-
}
91-
}
94+
// If it's an array, a custom severity might have been set, in that case, it must be returned
95+
96+
const secondary: Secondary = ruleConfig.at(1);
97+
98+
if (secondary == null) {
99+
return defaultSeverity;
100+
}
101+
102+
if (!secondary['severity']) {
103+
return defaultSeverity;
104+
}
105+
106+
if (typeof secondary['severity'] === 'function') {
107+
console.warn('Function severity is not supported');
108+
return defaultSeverity;
92109
}
93110

94-
// Default severity if severity is not explicitly set
95-
return 'error';
111+
return secondary['severity'];
96112
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { describe, expect, it } from 'vitest';
2+
import type { ActiveConfigRuleSetting } from './model.js';
3+
import { getSeverityFromRuleConfig } from './utils.js';
4+
5+
describe('getSeverityFromRuleConfig', () => {
6+
it('should respect the default severity when from the default', () => {
7+
expect(getSeverityFromRuleConfig([true])).toBe('error');
8+
});
9+
10+
it('should consider the default severity when its different from the default', () => {
11+
expect(getSeverityFromRuleConfig([true], 'warning')).toBe('warning');
12+
});
13+
14+
it.each([true, 5, 'percentage', ['/\\[.+]/', 'percentage'], { a: 1 }])(
15+
'should return the default severity for a primary value %s',
16+
ruleConfig => {
17+
expect(
18+
getSeverityFromRuleConfig(ruleConfig as ActiveConfigRuleSetting),
19+
).toBe('error');
20+
},
21+
);
22+
23+
it('should return the default severity when the rule config does not have a secondary item', () => {
24+
expect(getSeverityFromRuleConfig([true])).toBe('error');
25+
});
26+
27+
it('should return the default severity when the secondary item is missing the `severity` property', () => {
28+
expect(getSeverityFromRuleConfig([true, {}])).toBe('error');
29+
});
30+
31+
it('should return the default severity when `severity` property is of type function', () => {
32+
expect(getSeverityFromRuleConfig([true, { severity: () => {} }])).toBe(
33+
'error',
34+
);
35+
});
36+
37+
it.each([
38+
{ ruleConfig: [true, { severity: 'warning' }], expected: 'warning' },
39+
{ ruleConfig: [true, { severity: 'error' }], expected: 'error' },
40+
])('should return the set severity `%s`', ({ ruleConfig, expected }) => {
41+
expect(getSeverityFromRuleConfig(ruleConfig)).toBe(expected);
42+
});
43+
44+
it.each([null, undefined])(
45+
'should return the default severity for disabled rules %s',
46+
ruleConfig => {
47+
expect(
48+
getSeverityFromRuleConfig(
49+
ruleConfig as unknown as ActiveConfigRuleSetting,
50+
),
51+
).toBe('error');
52+
},
53+
);
54+
});

packages/plugin-stylelint/src/lib/utils.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { ConfigRuleSettings } from 'stylelint';
12
import type { Audit, CategoryRef } from '@code-pushup/models';
23
import {
34
type StyleLintPluginConfig,
@@ -9,6 +10,7 @@ import {
910
GROUPS,
1011
STYLELINT_PLUGIN_SLUG,
1112
} from './constants.js';
13+
import type { ActiveConfigRuleSetting } from './runner/model.js';
1214
import { getNormalizedConfigForFile } from './runner/normalize-config.js';
1315
import { getSeverityFromRuleConfig } from './runner/utils.js';
1416

@@ -31,12 +33,16 @@ export async function getGroups(
3133
options: Required<Pick<StyleLintTarget, 'stylelintrc'>>,
3234
) {
3335
const { config } = await getNormalizedConfigForFile(options);
34-
const { rules } = config;
36+
const { rules, defaultSeverity } = config;
3537
return GROUPS.map(group => ({
3638
...group,
3739
refs: Object.entries(rules)
40+
.filter(filterNonNull) // TODO Type Narrowing is not fully working for the nulls / undefineds
3841
.filter(([_, ruleConfig]) => {
39-
const severity = getSeverityFromRuleConfig(ruleConfig);
42+
const severity = getSeverityFromRuleConfig(
43+
ruleConfig as ActiveConfigRuleSetting,
44+
defaultSeverity,
45+
);
4046
if (severity === 'error' && group.slug === 'problems') {
4147
return true;
4248
} else if (severity === 'warning' && group.slug === 'suggestions') {
@@ -75,3 +81,12 @@ export async function getCategoryRefsFromAudits(
7581
type: 'audit',
7682
}));
7783
}
84+
85+
function filterNonNull<T, O extends object = object>(
86+
settings: Array<ConfigRuleSettings<T, O>>,
87+
): Exclude<ConfigRuleSettings<T, O>, null | undefined>[] {
88+
return settings.filter(
89+
(setting): setting is Exclude<ConfigRuleSettings<T, O>, null | undefined> =>
90+
setting !== null && setting !== undefined,
91+
);
92+
}

0 commit comments

Comments
 (0)