Skip to content

Commit

Permalink
New: jsx-quotes rule (fixes #2011)
Browse files Browse the repository at this point in the history
  • Loading branch information
lo1tuma committed Sep 7, 2015
1 parent 2551594 commit 3988b47
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 14 deletions.
1 change: 1 addition & 0 deletions conf/eslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@
"id-length": 0,
"indent": 0,
"init-declarations": 0,
"jsx-quotes": [0, "prefer-double"],
"key-spacing": [0, { "beforeColon": false, "afterColon": true }],
"lines-around-comment": 0,
"max-depth": [0, 4],
Expand Down
1 change: 1 addition & 0 deletions docs/rules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ These rules are purely matters of style and are quite subjective.
* [id-length](id-length.md) - this option enforces minimum and maximum identifier lengths (variable names, property names etc.)
* [id-match](id-match.md) - require identifiers to match the provided regular expression
* [indent](indent.md) - specify tab or space width for your code
* [jsx-quotes](jsx-quotes.md) - specify whether double or single quotes should be used in JSX attributes
* [key-spacing](key-spacing.md) - enforce spacing between keys and values in object literal properties
* [lines-around-comment](lines-around-comment.md) - enforce empty lines around comments
* [linebreak-style](linebreak-style.md) - disallow mixed 'LF' and 'CRLF' as linebreaks
Expand Down
58 changes: 58 additions & 0 deletions docs/rules/jsx-quotes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Enforce JSX Quote Style (jsx-quotes)

JSX attribute values can contain string literals, which are delimited with single or double quotes.

```js
<a b='c' />
<a b="c" />
```

Unlike string literals in JavaScript, string literals within JSX attributes can’t contain escaped quotes.
If you want to have e.g. a double quote within a JSX attribute value, you have to use single quotes as string delimiter.

```js
<a b="'" />
<a b='"' />
```

## Rule Details

This rule takes one argument.
If it is `"prefer-double"` then the rule enforces the usage of double quotes for all JSX attribute values which doesn’t contain a double quote.
If `"prefer-single"` is configured then the rule enforces the usage of single quotes for all JSX attribute values which doesn’t contain a single quote.

The default is `"prefer-double"`.

The following patterns are considered warnings when set to `"prefer-double"`:

```js
<a b='c' />
```

The following patterns are not considered warnings when set to `"prefer-double"`:

```js
<a b="c" />
<a b='"' />
```

The following patterns are considered warnings when set to `"prefer-single"`:

```js
<a b="c" />
```

The following patterns are not considered warnings when set to `"prefer-single"`:

```js
<a b='c' />
<a b="'" />
```

## When Not To Use It

You can turn this rule off if you don’t use JSX or if you aren’t concerned with a consistent usage of quotes within JSX attributes.

## Related Rules

* [quotes](quotes.md)
11 changes: 11 additions & 0 deletions lib/ast-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,16 @@ module.exports = {
*/
getModifyingReferences: function(references) {
return references.filter(isModifyingReference);
},

/**
* Validate that a string passed in is surrounded by the specified character
* @param {string} val The text to check.
* @param {string} character The character to see if it's surrounded by.
* @returns {boolean} True if the text is surrounded by the character, false if not.
* @private
*/
isSurroundedBy: function(val, character) {
return val[0] === character && val[val.length - 1] === character;
}
};
63 changes: 63 additions & 0 deletions lib/rules/jsx-quotes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* @fileoverview A rule to ensure consistent quotes used in jsx syntax.
* @author Mathias Schreck <https://github.com/lo1tuma>
* @copyright 2015 Mathias Schreck
*/

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

var astUtils = require("../ast-utils");

//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------

var QUOTE_SETTINGS = {
"prefer-double": {
quote: "\"",
description: "singlequote"
},
"prefer-single": {
quote: "'",
description: "doublequote"
}
};

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

module.exports = function(context) {
var quoteOption = context.options[0] || "prefer-double",
setting = QUOTE_SETTINGS[quoteOption];

/**
* Checks if the given string literal node uses the expected quotes
* @param {ASTNode} node - A string literal node.
* @returns {boolean} Whether or not the string literal used the expected quotes.
* @public
*/
function usesExpectedQuotes(node) {
return node.value.indexOf(setting.quote) !== -1 || astUtils.isSurroundedBy(node.raw, setting.quote);
}

return {
"JSXAttribute": function(node) {
var attributeValue = node.value;

if (astUtils.isStringLiteral(attributeValue) && !usesExpectedQuotes(attributeValue)) {
context.report(attributeValue, "Unexpected usage of {{description}}.", setting);
}
}
};
};

module.exports.schema = [
{
"enum": [ "prefer-single", "prefer-double" ]
}
];
22 changes: 8 additions & 14 deletions lib/rules/quotes.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

var astUtils = require("../ast-utils");

//------------------------------------------------------------------------------
// Constants
//------------------------------------------------------------------------------
Expand Down Expand Up @@ -36,18 +42,6 @@ var FUNCTION_TYPE = /^(?:Arrow)?Function(?:Declaration|Expression)$/;
//------------------------------------------------------------------------------

module.exports = function(context) {

/**
* Validate that a string passed in is surrounded by the specified character
* @param {string} val The text to check.
* @param {string} character The character to see if it's surrounded by.
* @returns {boolean} True if the text is surrounded by the character, false if not.
* @private
*/
function isSurroundedBy(val, character) {
return val[0] === character && val[val.length - 1] === character;
}

/**
* Determines if a given node is part of JSX syntax.
* @param {ASTNode} node The node to check.
Expand Down Expand Up @@ -142,10 +136,10 @@ module.exports = function(context) {
isValid;

if (settings && typeof val === "string") {
isValid = (quoteOption === "backtick" && isAllowedAsNonBacktick(node)) || isJSXElement(node.parent) || isSurroundedBy(rawVal, settings.quote);
isValid = (quoteOption === "backtick" && isAllowedAsNonBacktick(node)) || isJSXElement(node.parent) || astUtils.isSurroundedBy(rawVal, settings.quote);

if (!isValid && avoidEscape) {
isValid = isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.indexOf(settings.quote) >= 0;
isValid = astUtils.isSurroundedBy(rawVal, settings.alternateQuote) && rawVal.indexOf(settings.quote) >= 0;
}

if (!isValid) {
Expand Down
72 changes: 72 additions & 0 deletions tests/lib/rules/jsx-quotes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* @fileoverview Tests for jsx-quotes rule.
* @author Mathias Schreck <https://github.com/lo1tuma>
* @copyright 2015 Mathias Schreck
*/

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

var rule = require("../../../lib/rules/jsx-quotes"),
RuleTester = require("../../../lib/testers/rule-tester");

var ruleTester = new RuleTester();
ruleTester.run("jsx-quotes", rule, {
valid: [
{
code: "<foo bar=\"baz\" />",
ecmaFeatures: { jsx: true }
},
{
code: "<foo bar='\"' />",
ecmaFeatures: { jsx: true }
},
{
code: "<foo bar='baz' />",
options: [ "prefer-single" ],
ecmaFeatures: { jsx: true }
},
{
code: "<foo bar=\"baz\">\"</foo>",
ecmaFeatures: { jsx: true }
},
{
code: "<foo bar='baz'>'</foo>",
options: [ "prefer-single" ],
ecmaFeatures: { jsx: true }
},
{
code: "<foo bar={'baz'} />",
ecmaFeatures: { jsx: true }
},
{
code: "<foo bar={\"baz\"} />",
options: [ "prefer-single" ],
ecmaFeatures: { jsx: true }
},
{
code: "<foo bar={baz} />",
ecmaFeatures: { jsx: true }
}
],
invalid: [
{
code: "<foo bar=\'baz\' />",
ecmaFeatures: { jsx: true },
errors: [
{ message: "Unexpected usage of singlequote.", line: 1, column: 10, type: "Literal" }
]
},
{
code: "<foo bar=\"baz\" />",
options: [ "prefer-single" ],
ecmaFeatures: { jsx: true },
errors: [
{ message: "Unexpected usage of doublequote.", line: 1, column: 10, type: "Literal" }
]
}
]
});

0 comments on commit 3988b47

Please sign in to comment.