Skip to content

Commit 1ca6062

Browse files
committed
Merge pull request #161 from hummlas/no-dupe-props
Add jsx-no-duplicate-props rule (fixes #143)
2 parents 7a62fde + 0dbd28c commit 1ca6062

File tree

3 files changed

+111
-2
lines changed

3 files changed

+111
-2
lines changed

index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ module.exports = {
2121
'jsx-sort-prop-types': require('./lib/rules/jsx-sort-prop-types'),
2222
'jsx-boolean-value': require('./lib/rules/jsx-boolean-value'),
2323
'sort-comp': require('./lib/rules/sort-comp'),
24-
'require-extension': require('./lib/rules/require-extension')
24+
'require-extension': require('./lib/rules/require-extension'),
25+
'jsx-no-duplicate-props': require('./lib/rules/jsx-no-duplicate-props')
2526
},
2627
rulesConfig: {
2728
'jsx-uses-react': 0,
@@ -43,6 +44,7 @@ module.exports = {
4344
'jsx-sort-prop-types': 0,
4445
'jsx-boolean-value': 0,
4546
'sort-comp': 0,
46-
'require-extension': 0
47+
'require-extension': 0,
48+
'jsx-no-duplicate-props': 0
4749
}
4850
};

lib/rules/jsx-no-duplicate-props.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* @fileoverview Enforce no duplicate props
3+
* @author Markus Ånöstam
4+
*/
5+
6+
'use strict';
7+
8+
// ------------------------------------------------------------------------------
9+
// Rule Definition
10+
// ------------------------------------------------------------------------------
11+
12+
module.exports = function (context) {
13+
14+
var configuration = context.options[0] || {};
15+
var ignoreCase = configuration.ignoreCase || false;
16+
17+
return {
18+
JSXOpeningElement: function (node) {
19+
var props = {};
20+
21+
node.attributes.forEach(function(decl) {
22+
if (decl.type === 'JSXSpreadAttribute') {
23+
return;
24+
}
25+
26+
var name = decl.name.name;
27+
28+
if (ignoreCase) {
29+
name = name.toLowerCase();
30+
}
31+
32+
if (props.hasOwnProperty(name)) {
33+
context.report(decl, 'No duplicate props allowed');
34+
} else {
35+
props[name] = 1;
36+
}
37+
});
38+
}
39+
};
40+
};
41+
42+
module.exports.schema = [{
43+
type: 'object',
44+
properties: {
45+
ignoreCase: {
46+
type: 'boolean'
47+
}
48+
},
49+
additionalProperties: false
50+
}];
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* @fileoverview Enforce no duplicate props
3+
* @author Markus Ånöstam
4+
*/
5+
6+
'use strict';
7+
8+
// -----------------------------------------------------------------------------
9+
// Requirements
10+
// -----------------------------------------------------------------------------
11+
12+
var eslint = require('eslint').linter;
13+
var ESLintTester = require('eslint-tester');
14+
15+
// -----------------------------------------------------------------------------
16+
// Tests
17+
// -----------------------------------------------------------------------------
18+
19+
var eslintTester = new ESLintTester(eslint);
20+
21+
var expectedError = {
22+
message: 'No duplicate props allowed',
23+
type: 'JSXAttribute'
24+
};
25+
26+
var ignoreCaseArgs = [1, {
27+
ignoreCase: true
28+
}];
29+
30+
var features = {
31+
jsx: true
32+
};
33+
34+
eslintTester.addRuleTest('lib/rules/jsx-no-duplicate-props', {
35+
valid: [
36+
{code: '<App />;', ecmaFeatures: features},
37+
{code: '<App {...this.props} />;', ecmaFeatures: features},
38+
{code: '<App a b c />;', ecmaFeatures: features},
39+
{code: '<App a b c A />;', ecmaFeatures: features},
40+
{code: '<App {...this.props} a b c />;', ecmaFeatures: features},
41+
{code: '<App c {...this.props} a b />;', ecmaFeatures: features},
42+
{code: '<App a="c" b="b" c="a" />;', ecmaFeatures: features},
43+
{code: '<App {...this.props} a="c" b="b" c="a" />;', ecmaFeatures: features},
44+
{code: '<App c="a" {...this.props} a="c" b="b" />;', ecmaFeatures: features},
45+
{code: '<App A a />;', ecmaFeatures: features},
46+
{code: '<App A b a />;', ecmaFeatures: features},
47+
{code: '<App A="a" b="b" B="B" />;', ecmaFeatures: features}
48+
],
49+
invalid: [
50+
{code: '<App a a />;', errors: [expectedError], ecmaFeatures: features},
51+
{code: '<App A b c A />;', errors: [expectedError], ecmaFeatures: features},
52+
{code: '<App a="a" b="b" a="a" />;', errors: [expectedError], ecmaFeatures: features},
53+
{code: '<App A a />;', args: ignoreCaseArgs, errors: [expectedError], ecmaFeatures: features},
54+
{code: '<App a b c A />;', args: ignoreCaseArgs, errors: [expectedError], ecmaFeatures: features},
55+
{code: '<App A="a" b="b" B="B" />;', args: ignoreCaseArgs, errors: [expectedError], ecmaFeatures: features}
56+
]
57+
});

0 commit comments

Comments
 (0)