diff --git a/docs/rules/forbid-component-props.md b/docs/rules/forbid-component-props.md index 8e0ece521e..b44b36d929 100644 --- a/docs/rules/forbid-component-props.md +++ b/docs/rules/forbid-component-props.md @@ -42,16 +42,17 @@ The following patterns are **not** considered warnings: ### `forbid` An array specifying the names of props that are forbidden. The default value of this option is `['className', 'style']`. -Each array element can either be a string with the property name or object specifying the property name and a component whitelist: +Each array element can either be a string with the property name or object specifying the property name, an optional +custom message, and a component whitelist: ```js { "propName": "someProp", - "allowedFor": [SomeComponent, AnotherComponent] + "allowedFor": [SomeComponent, AnotherComponent], + "message": "Avoid using someProp" } ``` - ### Related rules - [forbid-dom-props](./forbid-dom-props.md) diff --git a/docs/rules/forbid-dom-props.md b/docs/rules/forbid-dom-props.md index 8dca2826ae..600cd30b8c 100644 --- a/docs/rules/forbid-dom-props.md +++ b/docs/rules/forbid-dom-props.md @@ -36,14 +36,22 @@ The following patterns are **not** considered warnings: ```js ... -"react/forbid-dom-props": [, { "forbid": [] }] +"react/forbid-dom-props": [, { "forbid": [|] }] ... ``` ### `forbid` An array of strings, with the names of props that are forbidden. The default value of this option `[]`. +Each array element can either be a string with the property name or object specifying the property name and an optional +custom message: +```js +{ + "propName": "someProp", + "message": "Avoid using someProp" +} +``` ### Related rules diff --git a/lib/rules/forbid-component-props.js b/lib/rules/forbid-component-props.js index 6c11687315..e5d8185961 100644 --- a/lib/rules/forbid-component-props.js +++ b/lib/rules/forbid-component-props.js @@ -46,6 +46,9 @@ module.exports = { items: { type: 'string' } + }, + message: { + type: 'string' } } }] @@ -59,14 +62,18 @@ module.exports = { const configuration = context.options[0] || {}; const forbid = new Map((configuration.forbid || DEFAULTS).map((value) => { const propName = typeof value === 'string' ? value : value.propName; - const whitelist = typeof value === 'string' ? [] : (value.allowedFor || []); - return [propName, whitelist]; + const options = { + allowList: typeof value === 'string' ? [] : (value.allowedFor || []), + message: typeof value === 'string' ? null : value.message + }; + return [propName, options]; })); function isForbidden(prop, tagName) { - const whitelist = forbid.get(prop); + const options = forbid.get(prop); + const allowList = options ? options.allowList : undefined; // if the tagName is undefined (``), we assume it's a forbidden element - return typeof whitelist !== 'undefined' && (typeof tagName === 'undefined' || whitelist.indexOf(tagName) === -1); + return typeof allowList !== 'undefined' && (typeof tagName === 'undefined' || allowList.indexOf(tagName) === -1); } return { @@ -83,9 +90,12 @@ module.exports = { return; } + const customMessage = forbid.get(prop).message; + const errorMessage = customMessage || `Prop \`${prop}\` is forbidden on Components`; + context.report({ node, - message: `Prop \`${prop}\` is forbidden on Components` + message: errorMessage }); } }; diff --git a/lib/rules/forbid-dom-props.js b/lib/rules/forbid-dom-props.js index 08853a412d..d39121187a 100644 --- a/lib/rules/forbid-dom-props.js +++ b/lib/rules/forbid-dom-props.js @@ -32,7 +32,19 @@ module.exports = { forbid: { type: 'array', items: { - type: 'string', + onfOf: [{ + type: 'string' + }, { + type: 'object', + properties: { + propName: { + type: 'string' + }, + message: { + type: 'string' + } + } + }], minLength: 1 }, uniqueItems: true @@ -43,11 +55,17 @@ module.exports = { }, create(context) { - function isForbidden(prop) { - const configuration = context.options[0] || {}; + const configuration = context.options[0] || {}; + const forbid = new Map((configuration.forbid || DEFAULTS).map((value) => { + const propName = typeof value === 'string' ? value : value.propName; + const options = { + message: typeof value === 'string' ? null : value.message + }; + return [propName, options]; + })); - const forbid = configuration.forbid || DEFAULTS; - return forbid.indexOf(prop) >= 0; + function isForbidden(prop) { + return forbid.has(prop); } return { @@ -64,9 +82,12 @@ module.exports = { return; } + const customMessage = forbid.get(prop).message; + const errorMessage = customMessage || `Prop \`${prop}\` is forbidden on DOM Nodes`; + context.report({ node, - message: `Prop \`${prop}\` is forbidden on DOM Nodes` + message: errorMessage }); } }; diff --git a/tests/lib/rules/forbid-component-props.js b/tests/lib/rules/forbid-component-props.js index 7cb904999b..910d2c240f 100644 --- a/tests/lib/rules/forbid-component-props.js +++ b/tests/lib/rules/forbid-component-props.js @@ -190,5 +190,66 @@ ruleTester.run('forbid-component-props', rule, { column: 32, type: 'JSXAttribute' }] + }, { + code: 'const item = ();', + options: [{ + forbid: [{propName: 'className', message: 'Please use ourCoolClassName instead of ClassName'}] + }], + errors: [{ + message: 'Please use ourCoolClassName instead of ClassName', + line: 1, + column: 20, + type: 'JSXAttribute' + }] + }, { + code: [ + 'const item = () => (', + '', + ' ', + '', + ');' + ].join('\n'), + options: [{ + forbid: [ + {propName: 'className', message: 'Please use ourCoolClassName instead of ClassName'}, + {propName: 'option', message: 'Avoid using option'} + ] + }], + errors: [{ + message: 'Please use ourCoolClassName instead of ClassName', + line: 2, + column: 6, + type: 'JSXAttribute' + }, { + message: 'Avoid using option', + line: 3, + column: 8, + type: 'JSXAttribute' + }] + }, { + code: [ + 'const item = () => (', + '', + ' ', + '', + ');' + ].join('\n'), + options: [{ + forbid: [ + {propName: 'className'}, + {propName: 'option', message: 'Avoid using option'} + ] + }], + errors: [{ + message: 'Prop `className` is forbidden on Components', + line: 2, + column: 6, + type: 'JSXAttribute' + }, { + message: 'Avoid using option', + line: 3, + column: 8, + type: 'JSXAttribute' + }] }] }); diff --git a/tests/lib/rules/forbid-dom-props.js b/tests/lib/rules/forbid-dom-props.js index 0f3e737398..123c9e0e37 100644 --- a/tests/lib/rules/forbid-dom-props.js +++ b/tests/lib/rules/forbid-dom-props.js @@ -126,5 +126,70 @@ ruleTester.run('forbid-element-props', rule, { column: 8, type: 'JSXAttribute' }] + }, { + code: [ + 'const First = (props) => (', + '
', + ');' + ].join('\n'), + options: [{ + forbid: [{propName: 'className', message: 'Please use class instead of ClassName'}] + }], + errors: [{ + message: 'Please use class instead of ClassName', + line: 2, + column: 8, + type: 'JSXAttribute' + }] + }, { + code: [ + 'const First = (props) => (', + '
', + '
', + '
', + ');' + ].join('\n'), + options: [{ + forbid: [ + {propName: 'className', message: 'Please use class instead of ClassName'}, + {propName: 'otherProp', message: 'Avoid using otherProp'} + ] + }], + errors: [{ + message: 'Please use class instead of ClassName', + line: 2, + column: 8, + type: 'JSXAttribute' + }, { + message: 'Avoid using otherProp', + line: 3, + column: 10, + type: 'JSXAttribute' + }] + }, { + code: [ + 'const First = (props) => (', + '
', + '
', + '
', + ');' + ].join('\n'), + options: [{ + forbid: [ + {propName: 'className'}, + {propName: 'otherProp', message: 'Avoid using otherProp'} + ] + }], + errors: [{ + message: 'Prop `className` is forbidden on DOM Nodes', + line: 2, + column: 8, + type: 'JSXAttribute' + }, { + message: 'Avoid using otherProp', + line: 3, + column: 10, + type: 'JSXAttribute' + }] }] });