Skip to content

Commit a4dd47a

Browse files
committed
Add jsx-quotes rule (fixes #12)
1 parent 1062025 commit a4dd47a

File tree

5 files changed

+171
-2
lines changed

5 files changed

+171
-2
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Finally, enable all of the rules that you would like to use.
4343
{
4444
"rules": {
4545
"react/display-name": 1,
46+
"react/jsx-quotes": 1,
4647
"react/jsx-no-undef": 1,
4748
"react/jsx-uses-react": 1,
4849
"react/jsx-uses-vars": 1,
@@ -60,6 +61,7 @@ Finally, enable all of the rules that you would like to use.
6061
# List of supported rules
6162

6263
* [display-name](docs/rules/display-name.md): Prevent missing displayName in a React component definition
64+
* [jsx-quotes](docs/rules/jsx-quotes.md): Enforce quote style for JSX attributes
6365
* [jsx-no-undef](docs/rules/jsx-no-undef.md): Disallow undeclared variables in JSX
6466
* [jsx-uses-react](docs/rules/jsx-uses-react.md): Prevent React to be incorrectly marked as unused
6567
* [jsx-uses-vars](docs/rules/jsx-uses-vars.md): Prevent variables used in JSX to be incorrectly marked as unused

docs/rules/jsx-quotes.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Enforce quote style for JSX attributes (jsx-quotes)
2+
3+
Enforces coding style that JSX attributes are delimited with single or double quotes.
4+
5+
It takes an option as the second parameter which can be `"double"` or `"single"` for double-quotes or single-quotes respectively. There is no default.
6+
7+
```js
8+
var HelloJohn = <Hello name="John" />;
9+
var HelloJohn = <Hello name='John' />;
10+
```
11+
12+
The third parameter enables an exception to the rule to avoid escaping quotes. For example, when `"single"` is the standard, this option allows the use of double quotes to avoid escaping single quotes. This option can have the value `"avoid-escape"` and is off by default.
13+
14+
```js
15+
[2, "single", "avoid-escape"]
16+
```
17+
18+
## Rule Details
19+
20+
This rule will throw warnings when the wrong type of quote is used.
21+
22+
The following patterns are considered warnings:
23+
24+
```js
25+
// When [1, "double"]
26+
var HelloJohn = <Hello name='John' />;
27+
28+
// When [1, "single"]
29+
var HelloJohn = <Hello name="John" />;
30+
31+
// When [1, "double", "avoid-escape"]
32+
var HelloJohn = <Hello name='John' />;
33+
34+
// When [1, "single", "avoid-escape"]
35+
var HelloJohn = <Hello name="John" />;
36+
```
37+
38+
The follow patterns are not considered warnings:
39+
40+
```js
41+
// When [1, "double"]
42+
var HelloJohn = <Hello name="John" />;
43+
44+
// When [1, "single"]
45+
var HelloJohn = <Hello name='John' />;
46+
47+
// When [1, "double", "avoid-escape"]
48+
var HelloJohn = <Hello name='John "FooBar" Smith' />;
49+
50+
// When [1, "single", "avoid-escape"]
51+
var HelloJohn = <Hello name="John 'FooBar' Smith" />;
52+
```

index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ module.exports = {
1212
'no-did-update-set-state': require('./lib/rules/no-did-update-set-state'),
1313
'react-in-jsx-scope': require('./lib/rules/react-in-jsx-scope'),
1414
'jsx-uses-vars': require('./lib/rules/jsx-uses-vars'),
15-
'jsx-no-undef': require('./lib/rules/jsx-no-undef')
15+
'jsx-no-undef': require('./lib/rules/jsx-no-undef'),
16+
'jsx-quotes': require('./lib/rules/jsx-quotes')
1617
},
1718
rulesConfig: {
1819
'jsx-uses-react': 0,
@@ -25,6 +26,7 @@ module.exports = {
2526
'no-did-update-set-state': 0,
2627
'react-in-jsx-scope': 0,
2728
'jsx-uses-vars': 0,
28-
'jsx-no-undef': 0
29+
'jsx-no-undef': 0,
30+
'jsx-quotes': 0
2931
}
3032
};

lib/rules/jsx-quotes.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* @fileoverview Enforce props quotes style
3+
* @author Matt DuVall <http://www.mattduvall.com/>, Brandon Payton, Yannick Croissant
4+
*/
5+
'use strict';
6+
7+
// ------------------------------------------------------------------------------
8+
// Constants
9+
// ------------------------------------------------------------------------------
10+
11+
var QUOTE_SETTINGS = {
12+
double: {
13+
quote: '"',
14+
alternateQuote: '\'',
15+
description: 'doublequote'
16+
},
17+
single: {
18+
quote: '\'',
19+
alternateQuote: '"',
20+
description: 'singlequote'
21+
}
22+
};
23+
24+
var AVOID_ESCAPE = 'avoid-escape';
25+
26+
// ------------------------------------------------------------------------------
27+
// Rule Definition
28+
// ------------------------------------------------------------------------------
29+
30+
module.exports = function(context) {
31+
32+
/**
33+
* Validate that a string passed in is surrounded by the specified character
34+
* @param {string} val The text to check.
35+
* @param {string} character The character to see if it's surrounded by.
36+
* @returns {boolean} True if the text is surrounded by the character, false if not.
37+
* @private
38+
*/
39+
function isSurroundedBy(val, character) {
40+
return val[0] === character && val[val.length - 1] === character;
41+
}
42+
43+
/**
44+
* Determines if a given node is part of JSX syntax.
45+
* @param {ASTNode} node The node to check.
46+
* @returns {boolean} True if the node is a JSX node, false if not.
47+
* @private
48+
*/
49+
function isJSXElement(node) {
50+
return node.type.indexOf('JSX') === 0;
51+
}
52+
53+
return {
54+
55+
Literal: function(node) {
56+
if (!isJSXElement(node.parent)) {
57+
return;
58+
}
59+
var val = node.value;
60+
var rawVal = node.raw;
61+
var quoteOption = context.options[0];
62+
var settings = QUOTE_SETTINGS[quoteOption];
63+
var avoidEscape = context.options[1] === AVOID_ESCAPE;
64+
var isValid;
65+
66+
if (settings && typeof val === 'string') {
67+
isValid = isSurroundedBy(rawVal, settings.quote);
68+
69+
if (!isValid && avoidEscape) {
70+
isValid = isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.indexOf(settings.quote) >= 0;
71+
}
72+
73+
if (!isValid) {
74+
context.report(node, 'JSX attributes must use ' + settings.description + '.');
75+
}
76+
}
77+
}
78+
};
79+
80+
};

tests/lib/rules/jsx-quotes.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* @fileoverview Tests for jsx-quotes
3+
* @author Yannick Croissant
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+
eslintTester.addRuleTest('lib/rules/jsx-quotes', {
21+
valid: [
22+
{code: '<App foo=\'bar\' />;', args: [1, 'single'], ecmaFeatures: {jsx: true}},
23+
{code: '<App foo="bar" />;', args: [1, 'double'], ecmaFeatures: {jsx: true}},
24+
{code: '<App foo="ba\'r" />;', args: [1, 'single', 'avoid-escape'], ecmaFeatures: {jsx: true}},
25+
{code: '<App foo=\'ba"r\' />;', args: [1, 'double', 'avoid-escape'], ecmaFeatures: {jsx: true}}
26+
],
27+
invalid: [
28+
{code: '<App foo="bar" />;',
29+
errors: [{message: 'JSX attributes must use singlequote.'}], args: [1, 'single'], ecmaFeatures: {jsx: true}},
30+
{code: '<App foo=\'bar\' />;',
31+
errors: [{message: 'JSX attributes must use doublequote.'}], args: [1, 'double'], ecmaFeatures: {jsx: true}}
32+
]
33+
});

0 commit comments

Comments
 (0)