Skip to content

Commit f9fde25

Browse files
committed
Add rule name to custom messages
Ensures that when a custom message is specified in a Stylelint configuration, the rule name is automatically appended in parentheses at the end of the message if not already present, improving traceability of reported problems.
1 parent 6342d1e commit f9fde25

File tree

9 files changed

+109
-30
lines changed

9 files changed

+109
-30
lines changed

.changeset/violet-beds-care.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"stylelint": minor
3+
---
4+
5+
Added: rule name to custom messages

lib/__tests__/integration.test.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ describe('integration test expecting warnings', () => {
9494
expect(errors).toHaveLength(2);
9595

9696
for (const error of errors) {
97-
expect(error.text).toBe('You made a mistake');
97+
expect(error.text).toBe('You made a mistake (color-no-invalid-hex)');
9898
expect(error.severity).toBe('warning');
9999
}
100100
});

lib/__tests__/message.test.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ it('standalone loading YAML with custom message', async () => {
1212
});
1313

1414
expect(results[0].warnings).toHaveLength(1);
15-
expect(results[0].warnings[0].text).toBe('Unacceptable');
15+
expect(results[0].warnings[0].text).toBe('Unacceptable (color-named)');
1616
});

lib/rules/rule-selector-property-disallowed-list/__tests__/index.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ testRule({
130130
reject: [
131131
{
132132
code: 'a { color: red; }',
133-
message: 'foo',
133+
message: `foo (${ruleName})`,
134134
description: 'custom message',
135135
},
136136
],
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import appendRuleName from '../appendRuleName.mjs';
2+
3+
describe('appendRuleName', () => {
4+
const expectedString = 'Unexpected empty block (block-no-empty)';
5+
6+
it('should append rule name in parentheses if not present', () => {
7+
expect(appendRuleName('Unexpected empty block', 'block-no-empty')).toBe(expectedString);
8+
});
9+
10+
it('should not append rule name if already present', () => {
11+
expect(appendRuleName('Unexpected empty block (block-no-empty)', 'block-no-empty')).toBe(
12+
expectedString,
13+
);
14+
});
15+
});

lib/utils/__tests__/report.test.mjs

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ test('without disabledRanges', () => {
4444
report(v);
4545
const spyArgs = v.result.warn.mock.calls[0];
4646

47-
expect(spyArgs[0]).toBe('bar');
47+
expect(spyArgs[0]).toBe('bar (foo)');
4848
expect(spyArgs[1].node).toBe(v.node);
4949
});
5050

@@ -68,7 +68,7 @@ test('with irrelevant general disabledRange', () => {
6868
report(v);
6969
const spyArgs = v.result.warn.mock.calls[0];
7070

71-
expect(spyArgs[0]).toBe('bar');
71+
expect(spyArgs[0]).toBe('bar (foo)');
7272
expect(spyArgs[1].node).toBe(v.node);
7373
});
7474

@@ -114,7 +114,7 @@ test('with irrelevant rule-specific disabledRange', () => {
114114
report(v);
115115
const spyArgs = v.result.warn.mock.calls[0];
116116

117-
expect(spyArgs[0]).toBe('bar');
117+
expect(spyArgs[0]).toBe('bar (foo)');
118118
expect(spyArgs[1].node).toBe(v.node);
119119
});
120120

@@ -276,7 +276,7 @@ test('with custom rule severity', () => {
276276
report(v);
277277
const spyArgs = v.result.warn.mock.calls[0];
278278

279-
expect(spyArgs[0]).toBe('bar');
279+
expect(spyArgs[0]).toBe('bar (foo)');
280280
expect(spyArgs[1].severity).toBe('warning');
281281
expect(spyArgs[1].node).toBe(v.node);
282282
});
@@ -396,7 +396,7 @@ test('with message function', () => {
396396
report(v);
397397
const spyArgs = v.result.warn.mock.calls[0];
398398

399-
expect(spyArgs[0]).toBe('a=str, b=true, c=10, d=/regex/');
399+
expect(spyArgs[0]).toBe('a=str, b=true, c=10, d=/regex/ (foo)');
400400
expect(spyArgs[1].node).toBe(v.node);
401401
});
402402

@@ -529,7 +529,7 @@ describe('with fix object', () => {
529529

530530
const spyArgs1 = v.result.warn.mock.calls[0];
531531

532-
expect(spyArgs1[0]).toBe('bar');
532+
expect(spyArgs1[0]).toBe('bar (foo)');
533533
expect(spyArgs1[1].fix.range).toMatchObject([0, 3]);
534534
expect(spyArgs1[1].fix.text).toBe('bar');
535535

@@ -543,7 +543,7 @@ describe('with fix object', () => {
543543

544544
const spyArgs2 = v.result.warn.mock.calls[1];
545545

546-
expect(spyArgs2[0]).toBe('qux');
546+
expect(spyArgs2[0]).toBe('qux (foo)');
547547
expect(spyArgs2[1].fix).toBeUndefined();
548548
});
549549

@@ -579,7 +579,7 @@ describe('with fix object', () => {
579579

580580
const spyArgs = v.result.warn.mock.calls[0];
581581

582-
expect(spyArgs[0]).toBe('bar');
582+
expect(spyArgs[0]).toBe('bar (foo)');
583583
expect(spyArgs[1].fix).toBeUndefined();
584584
});
585585

@@ -618,7 +618,7 @@ describe('with fix object', () => {
618618

619619
const spyArgs = v.result.warn.mock.calls[0];
620620

621-
expect(spyArgs[0]).toBe('bar');
621+
expect(spyArgs[0]).toBe('bar (foo)');
622622
expect(spyArgs[1].fix).toBeUndefined();
623623
});
624624

@@ -652,7 +652,7 @@ describe('with fix object', () => {
652652

653653
const spyArgs = v.result.warn.mock.calls[0];
654654

655-
expect(spyArgs[0]).toBe('bar');
655+
expect(spyArgs[0]).toBe('bar (foo)');
656656
expect(spyArgs[1].fix).toBeUndefined();
657657
});
658658

@@ -686,7 +686,7 @@ describe('with fix object', () => {
686686

687687
const spyArgs = v.result.warn.mock.calls[0];
688688

689-
expect(spyArgs[0]).toBe('bar');
689+
expect(spyArgs[0]).toBe('bar (foo)');
690690
expect(spyArgs[1].fix).toBeUndefined();
691691
});
692692
});
@@ -710,7 +710,51 @@ test('with custom message', () => {
710710
report(v);
711711
const spyArgs = v.result.warn.mock.calls[0];
712712

713-
expect(spyArgs[0]).toBe('A custom message: str, true, 10, /regex/');
713+
expect(spyArgs[0]).toBe('A custom message: str, true, 10, /regex/ (foo)');
714+
expect(spyArgs[1].node).toBe(v.node);
715+
});
716+
717+
test('with custom message (missing rule name)', () => {
718+
const v = {
719+
ruleName: 'foo',
720+
result: {
721+
warn: jest.fn(),
722+
stylelint: resultStylelint({
723+
customMessages: { foo: 'A custom message with appended rule name' },
724+
}),
725+
},
726+
message: 'bar',
727+
node: {
728+
rangeBy: defaultRangeBy,
729+
},
730+
};
731+
732+
report(v);
733+
const spyArgs = v.result.warn.mock.calls[0];
734+
735+
expect(spyArgs[0]).toBe('A custom message with appended rule name (foo)');
736+
expect(spyArgs[1].node).toBe(v.node);
737+
});
738+
739+
test('with custom message (appended rule name)', () => {
740+
const v = {
741+
ruleName: 'foo',
742+
result: {
743+
warn: jest.fn(),
744+
stylelint: resultStylelint({
745+
customMessages: { foo: 'A custom message with appended rule name (foo)' },
746+
}),
747+
},
748+
message: 'bar',
749+
node: {
750+
rangeBy: defaultRangeBy,
751+
},
752+
};
753+
754+
report(v);
755+
const spyArgs = v.result.warn.mock.calls[0];
756+
757+
expect(spyArgs[0]).toBe('A custom message with appended rule name (foo)');
714758
expect(spyArgs[1].node).toBe(v.node);
715759
});
716760

@@ -733,7 +777,7 @@ test('with custom message function', () => {
733777
report(v);
734778
const spyArgs = v.result.warn.mock.calls[0];
735779

736-
expect(spyArgs[0]).toBe('a=str, b=123');
780+
expect(spyArgs[0]).toBe('a=str, b=123 (foo)');
737781
expect(spyArgs[1].node).toBe(v.node);
738782
});
739783

lib/utils/appendRuleName.mjs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* Ensures the given rule name is appended to the provided string.
3+
*
4+
* If the string does not already end with the rule name in parentheses, the rule name is appended in parentheses.
5+
*
6+
* @param {string} string - The message to which the rule name may be appended
7+
* @param {string} ruleName - The rule name to append
8+
*
9+
* @returns {string} The provided string with the given rule name appended
10+
*/
11+
export default function appendRuleName(string, ruleName) {
12+
return !string.endsWith(`(${ruleName})`) ? `${string} (${ruleName})` : string;
13+
}

lib/utils/report.cjs

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

lib/utils/report.mjs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
SEVERITY_WARNING,
1414
} from '../constants.mjs';
1515
import addSemicolonForEditInfo from './addSemicolonForEditInfo.mjs';
16+
import appendRuleName from './appendRuleName.mjs';
1617
import { emitDeprecationWarning } from './emitWarning.mjs';
1718
import narrowFixRange from './narrowFixRange.mjs';
1819
import rangesOverlap from './rangesOverlap.mjs';
@@ -126,7 +127,7 @@ export default function report(problem) {
126127

127128
warningProperties.fix = computeEditInfo({ ...problem, line: startLine });
128129

129-
const warningMessage = buildWarningMessage(message, messageArgs);
130+
const warningMessage = buildWarningMessage(message, messageArgs, ruleName);
130131

131132
result.warn(warningMessage, warningProperties);
132133
}
@@ -173,14 +174,14 @@ function checkProblemRangeDeprecations(problem) {
173174
/**
174175
* @param {RuleMessage} message
175176
* @param {NonNullable<Problem['messageArgs']>} messageArgs
177+
* @param {string} ruleName
176178
* @returns {string}
177179
*/
178-
function buildWarningMessage(message, messageArgs) {
179-
if (isString(message)) {
180-
return printfLike(message, ...messageArgs);
181-
}
182-
183-
return message(...messageArgs);
180+
function buildWarningMessage(message, messageArgs, ruleName) {
181+
return appendRuleName(
182+
isString(message) ? printfLike(message, ...messageArgs) : message(...messageArgs),
183+
ruleName,
184+
);
184185
}
185186

186187
/**

0 commit comments

Comments
 (0)