Skip to content
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

Add experimental support for custom message arguments #6312

Merged
merged 10 commits into from Sep 12, 2022
3 changes: 2 additions & 1 deletion lib/rules/custom-property-pattern/index.js
Expand Up @@ -79,7 +79,8 @@ const rule = (primary) => {
report({
result,
ruleName,
message: messages.expected(primary),
message: messages.expected,
messageArgs: [primary],
node: decl,
index,
endIndex: index + length,
Expand Down
96 changes: 82 additions & 14 deletions lib/utils/__tests__/report.test.js
Expand Up @@ -2,15 +2,17 @@

const report = require('../report');

it('without disabledRanges', () => {
const defaultRangeBy = () => ({ start: { line: 2, column: 1 }, end: { line: 2, column: 2 } });

test('without disabledRanges', () => {
const v = {
ruleName: 'foo',
result: {
warn: jest.fn(),
},
message: 'bar',
node: {
rangeBy: () => ({ start: { line: 2, column: 1 }, end: { line: 2, column: 2 } }),
rangeBy: defaultRangeBy,
},
};

Expand All @@ -21,7 +23,7 @@ it('without disabledRanges', () => {
expect(spyArgs[1].node).toBe(v.node);
});

it('with irrelevant general disabledRange', () => {
test('with irrelevant general disabledRange', () => {
const v = {
ruleName: 'foo',
result: {
Expand All @@ -34,7 +36,7 @@ it('with irrelevant general disabledRange', () => {
},
message: 'bar',
node: {
rangeBy: () => ({ start: { line: 2, column: 1 }, end: { line: 2, column: 2 } }),
rangeBy: defaultRangeBy,
},
};

Expand All @@ -45,7 +47,7 @@ it('with irrelevant general disabledRange', () => {
expect(spyArgs[1].node).toBe(v.node);
});

it('with relevant general disabledRange', () => {
test('with relevant general disabledRange', () => {
const v = {
ruleName: 'foo',
result: {
Expand All @@ -66,7 +68,7 @@ it('with relevant general disabledRange', () => {
expect(v.result.warn).toHaveBeenCalledTimes(0);
});

it('with irrelevant rule-specific disabledRange', () => {
test('with irrelevant rule-specific disabledRange', () => {
const v = {
ruleName: 'foo',
result: {
Expand All @@ -91,7 +93,7 @@ it('with irrelevant rule-specific disabledRange', () => {
expect(spyArgs[1].node).toBe(v.node);
});

it('with relevant rule-specific disabledRange', () => {
test('with relevant rule-specific disabledRange', () => {
const v = {
ruleName: 'foo',
result: {
Expand All @@ -113,7 +115,7 @@ it('with relevant rule-specific disabledRange', () => {
expect(v.result.warn).toHaveBeenCalledTimes(0);
});

it('with relevant general disabledRange, among others', () => {
test('with relevant general disabledRange, among others', () => {
const v = {
ruleName: 'foo',
result: {
Expand All @@ -137,7 +139,7 @@ it('with relevant general disabledRange, among others', () => {
expect(v.result.warn).toHaveBeenCalledTimes(0);
});

it('with relevant rule-specific disabledRange, among others', () => {
test('with relevant rule-specific disabledRange, among others', () => {
const v = {
ruleName: 'foo',
result: {
Expand All @@ -162,7 +164,7 @@ it('with relevant rule-specific disabledRange, among others', () => {
expect(v.result.warn).toHaveBeenCalledTimes(0);
});

it('with relevant rule-specific disabledRange with range report', () => {
test('with relevant rule-specific disabledRange with range report', () => {
const v = {
ruleName: 'foo',
result: {
Expand All @@ -184,7 +186,7 @@ it('with relevant rule-specific disabledRange with range report', () => {
expect(v.result.warn).toHaveBeenCalledTimes(0);
});

it("with quiet mode on and rule severity of 'warning'", () => {
test("with quiet mode on and rule severity of 'warning'", () => {
const v = {
ruleName: 'foo',
result: {
Expand All @@ -198,15 +200,15 @@ it("with quiet mode on and rule severity of 'warning'", () => {
},
message: 'bar',
node: {
rangeBy: () => ({ start: { line: 6, column: 1 }, end: { line: 6, column: 2 } }),
rangeBy: defaultRangeBy,
},
};

report(v);
expect(v.result.warn).toHaveBeenCalledTimes(0);
});

it("with quiet mode on and rule severity of 'error'", () => {
test("with quiet mode on and rule severity of 'error'", () => {
const v = {
ruleName: 'foo',
result: {
Expand All @@ -220,10 +222,76 @@ it("with quiet mode on and rule severity of 'error'", () => {
},
message: 'bar',
node: {
rangeBy: () => ({ start: { line: 6, column: 1 }, end: { line: 6, column: 2 } }),
rangeBy: defaultRangeBy,
},
};

report(v);
expect(v.result.warn).toHaveBeenCalledTimes(1);
});

test('with message function', () => {
const v = {
ruleName: 'foo',
result: {
warn: jest.fn(),
},
message: (a, b, c, d) => `a=${a}, b=${b}, c=${c}, d=${d}`,
messageArgs: ['str', true, 10, /regex/],
node: {
rangeBy: defaultRangeBy,
},
};

report(v);
const spyArgs = v.result.warn.mock.calls[0];

expect(spyArgs[0]).toBe('a=str, b=true, c=10, d=/regex/');
expect(spyArgs[1].node).toBe(v.node);
});

test('with custom message', () => {
const v = {
ruleName: 'foo',
result: {
warn: jest.fn(),
stylelint: {
customMessages: { foo: 'A custom message: %s, %s, %d, %s' },
},
},
message: 'bar',
messageArgs: ['str', true, 10, /regex/],
node: {
rangeBy: defaultRangeBy,
},
};

report(v);
const spyArgs = v.result.warn.mock.calls[0];

expect(spyArgs[0]).toBe('A custom message: str, true, 10, /regex/');
expect(spyArgs[1].node).toBe(v.node);
});

test('with custom message function', () => {
const v = {
ruleName: 'foo',
result: {
warn: jest.fn(),
stylelint: {
customMessages: { foo: (a, b) => `a=${a}, b=${b}` },
},
},
message: 'bar',
messageArgs: ['str', 123],
node: {
rangeBy: defaultRangeBy,
},
};

report(v);
const spyArgs = v.result.warn.mock.calls[0];

expect(spyArgs[0]).toBe('a=str, b=123');
expect(spyArgs[1].node).toBe(v.node);
});
26 changes: 23 additions & 3 deletions lib/utils/report.js
@@ -1,5 +1,7 @@
'use strict';

const util = require('util');

/**
* Report a problem.
*
Expand All @@ -15,7 +17,7 @@
* @type {typeof import('stylelint').utils.report}
*/
module.exports = function report(problem) {
const { ruleName, result, message, line, node, index, endIndex, word } = problem;
const { ruleName, result, message, messageArgs, line, node, index, endIndex, word } = problem;

result.stylelint = result.stylelint || {
ruleSeverities: {},
Expand Down Expand Up @@ -104,8 +106,26 @@ module.exports = function report(problem) {
warningProperties.word = word;
}

const warningMessage =
(result.stylelint.customMessages && result.stylelint.customMessages[ruleName]) || message;
const { customMessages } = result.stylelint;
const warningMessage = buildWarningMessage(
(customMessages && customMessages[ruleName]) || message,
messageArgs,
);

result.warn(warningMessage, warningProperties);
};

/**
* @param {import('stylelint').RuleMessage} message
* @param {import('stylelint').Problem['messageArgs']} messageArgs
* @returns {string}
*/
function buildWarningMessage(message, messageArgs) {
const args = messageArgs || [];

if (typeof message === 'string') {
return util.format(message, ...args);
}

return message(...args);
}
2 changes: 1 addition & 1 deletion scripts/visual-config.json
Expand Up @@ -6,7 +6,7 @@
"custom-property-pattern": [
"^([a-z][a-z0-9]*)(-[a-z0-9]+)*$",
{
"message": "Expected custom property name to be kebab-case",
"message": "Expected custom property name to be kebab-case; pattern=/%s/",
"severity": "warning"
}
],
Expand Down
9 changes: 6 additions & 3 deletions types/stylelint/index.d.ts
Expand Up @@ -89,7 +89,7 @@ declare module 'stylelint' {

export type StylelintPostcssResult = {
ruleSeverities: { [ruleName: string]: Severity };
customMessages: { [ruleName: string]: any };
customMessages: { [ruleName: string]: RuleMessage };
ruleMetadata: { [ruleName: string]: Partial<RuleMeta> };
quiet?: boolean;
disabledRanges: DisabledRangeObject;
Expand Down Expand Up @@ -154,7 +154,9 @@ declare module 'stylelint' {
bivariance(...args: (string | number | boolean | RegExp)[]): string;
}['bivariance'];

export type RuleMessages = { [message: string]: string | RuleMessageFunc };
export type RuleMessage = string | RuleMessageFunc;

export type RuleMessages = { [message: string]: RuleMessage };

export type RuleOptionsPossibleFunc = (value: unknown) => boolean;

Expand Down Expand Up @@ -350,7 +352,8 @@ declare module 'stylelint' {
export type Problem = {
ruleName: string;
result: PostcssResult;
message: string;
message: RuleMessage;
messageArgs?: Parameters<RuleMessage> | undefined;
node: PostCSS.Node;
/**
* The inclusive start index of the problem, relative to the node's
Expand Down