diff --git a/docs/rules/jsx-no-literals.md b/docs/rules/jsx-no-literals.md
index ab920844df..4a223582f5 100644
--- a/docs/rules/jsx-no-literals.md
+++ b/docs/rules/jsx-no-literals.md
@@ -28,16 +28,17 @@ var Hello =
There are two options:
-* `noStrings` - Enforces no string literals used as children, wrapped or unwrapped.
+* `noStrings`(`false` default) - Enforces no string literals used as children, wrapped or unwrapped.
* `allowedStrings` - An array of unique string values that would otherwise warn, but will be ignored.
+* `validateProps`(`false` default) - Enforces no literals used in props, wrapped or unwrapped.
To use, you can specify as follows:
```js
-"react/jsx-no-literals": [
, {"noStrings": true, "allowedStrings": ["allowed"]}]
+"react/jsx-no-literals": [, {"noStrings": true, "allowedStrings": ["allowed"], "validateProps": true}]
```
-In this configuration, the following are considered warnings:
+With `noStrings` set to `true`, the following are considered warnings:
```jsx
var Hello = test
;
@@ -65,6 +66,7 @@ var Hello =
var Hello = {translate('my.translation.key')}
```
+
```jsx
// an allowed string
var Hello = allowed
@@ -77,6 +79,50 @@ var Hello =
;
```
+With `validateProps` set to `true`, the following are considered warnings:
+
+```jsx
+var Hello = ;
+```
+
+```jsx
+var Hello = ;
+```
+
+```jsx
+var Hello = ;
+```
+
+The following are **not** considered warnings:
+
+```jsx
+// spread props object
+var Hello =
+```
+
+```jsx
+// use variable for prop values
+var Hello =
+```
+
+```jsx
+// cache
+class Comp1 extends Component {
+ asdf() {}
+
+ render() {
+ return (
+
+ {'asdjfl'}
+ test
+ {'foo'}
+
+ );
+ }
+}
+```
+
## When Not To Use It
If you do not want to enforce any style JSX literals, then you can disable this rule.
+
diff --git a/lib/rules/jsx-no-literals.js b/lib/rules/jsx-no-literals.js
index f2bb83dd01..11ca0d3f58 100644
--- a/lib/rules/jsx-no-literals.js
+++ b/lib/rules/jsx-no-literals.js
@@ -12,6 +12,10 @@ const docsUrl = require('../util/docsUrl');
// Rule Definition
// ------------------------------------------------------------------------------
+function trimIfString(val) {
+ return typeof val === 'string' ? val.trim() : val;
+}
+
module.exports = {
meta: {
docs: {
@@ -33,6 +37,9 @@ module.exports = {
items: {
type: 'string'
}
+ },
+ validateProps: {
+ type: 'boolean'
}
},
additionalProperties: false
@@ -40,11 +47,7 @@ module.exports = {
},
create(context) {
- function trimIfString(val) {
- return typeof val === 'string' ? val.trim() : val;
- }
-
- const defaults = {noStrings: false, allowedStrings: []};
+ const defaults = {noStrings: false, allowedStrings: [], validateProps: false};
const config = Object.assign({}, defaults, context.options[0] || {});
config.allowedStrings = new Set(config.allowedStrings.map(trimIfString));
@@ -52,10 +55,12 @@ module.exports = {
'Strings not allowed in JSX files' :
'Missing JSX expression container around literal string';
- function reportLiteralNode(node) {
+ function reportLiteralNode(node, customMessage) {
+ const errorMessage = customMessage || message;
+
context.report({
node,
- message: `${message}: “${context.getSourceCode().getText(node).trim()}”`
+ message: `${errorMessage}: “${context.getSourceCode().getText(node).trim()}”`
});
}
@@ -82,18 +87,47 @@ module.exports = {
return standard && parent.type !== 'JSXExpressionContainer';
}
+ function getParentAndGrandParentType(node) {
+ const parent = getParentIgnoringBinaryExpressions(node);
+ const parentType = parent.type;
+ const grandParentType = parent.parent.type;
+
+ return {
+ parent,
+ parentType,
+ grandParentType,
+ grandParent: parent.parent
+ };
+ }
+
+ function hasJSXElementParentOrGrandParent(node) {
+ const parents = getParentAndGrandParentType(node);
+ const parentType = parents.parentType;
+ const grandParentType = parents.grandParentType;
+
+ return (parentType === 'JSXFragment') || (parentType === 'JSXElement' || grandParentType === 'JSXElement');
+ }
+
// --------------------------------------------------------------------------
// Public
// --------------------------------------------------------------------------
return {
-
Literal(node) {
- if (getValidation(node)) {
+ if (getValidation(node) && (hasJSXElementParentOrGrandParent(node) || config.validateProps)) {
reportLiteralNode(node);
}
},
+ JSXAttribute(node) {
+ const isNodeValueString = node.value && node.value && node.value.type === 'Literal' && typeof node.value.value === 'string';
+
+ if (config.noStrings && config.validateProps && isNodeValueString) {
+ const customMessage = 'Invalid attribute value';
+ reportLiteralNode(node, customMessage);
+ }
+ },
+
JSXText(node) {
if (getValidation(node)) {
reportLiteralNode(node);
@@ -101,12 +135,16 @@ module.exports = {
},
TemplateLiteral(node) {
- const parent = getParentIgnoringBinaryExpressions(node);
- if (config.noStrings && parent.type === 'JSXExpressionContainer') {
+ const parents = getParentAndGrandParentType(node);
+ const parentType = parents.parentType;
+ const grandParentType = parents.grandParentType;
+ const isParentJSXExpressionCont = parentType === 'JSXExpressionContainer';
+ const isParentJSXElement = parentType === 'JSXElement' || grandParentType === 'JSXElement';
+
+ if (isParentJSXExpressionCont && config.noStrings && (isParentJSXElement || config.validateProps)) {
reportLiteralNode(node);
}
}
-
};
}
};
diff --git a/tests/lib/rules/jsx-no-literals.js b/tests/lib/rules/jsx-no-literals.js
index b620571b7a..ada3cd6ea6 100644
--- a/tests/lib/rules/jsx-no-literals.js
+++ b/tests/lib/rules/jsx-no-literals.js
@@ -183,7 +183,7 @@ ruleTester.run('jsx-no-literals', rule, {
class Comp1 extends Component {
asdf() {}
render() {
- return ;
+ return ;
}
}
`,
@@ -260,6 +260,18 @@ ruleTester.run('jsx-no-literals', rule, {
}
`,
options: [{noStrings: true, allowedStrings: [' foo ']}]
+ }, {
+ code: `
+ class Comp1 extends Component {
+ asdf() {}
+ render() {
+ const xx = 'xx';
+
+ return ;
+ }
+ } `,
+ parser: parsers.BABEL_ESLINT,
+ options: [{noStrings: true, validateProps: true}]
}
],
@@ -415,33 +427,33 @@ ruleTester.run('jsx-no-literals', rule, {
errors: [{message: stringsMessage('`Test`')}]
}, {
code: '',
- options: [{noStrings: true}],
+ options: [{noStrings: true, validateProps: true}],
errors: [{message: stringsMessage('`Test`')}]
}, {
code: '',
- options: [{noStrings: true}],
+ options: [{noStrings: true, validateProps: true}],
errors: [{message: stringsMessage('`${baz}`')}]
}, {
code: '',
- options: [{noStrings: true}],
+ options: [{noStrings: true, validateProps: true}],
errors: [{message: stringsMessage('`Test ${baz}`')}]
}, {
code: '',
- options: [{noStrings: true}],
+ options: [{noStrings: true, validateProps: true}],
errors: [
{message: stringsMessage('`foo`')},
{message: stringsMessage('\'bar\'')}
]
}, {
code: '',
- options: [{noStrings: true}],
+ options: [{noStrings: true, validateProps: true}],
errors: [
{message: stringsMessage('`foo`')},
{message: stringsMessage('`bar`')}
]
}, {
code: '',
- options: [{noStrings: true}],
+ options: [{noStrings: true, validateProps: true}],
errors: [
{message: stringsMessage('\'foo\'')},
{message: stringsMessage('`bar`')}
@@ -455,10 +467,28 @@ ruleTester.run('jsx-no-literals', rule, {
}
`,
options: [{noStrings: true, allowedStrings: ['asd']}],
+ errors: [
+ {message: stringsMessage('asdf')}
+ ]
+ }, {
+ code: `
+ class Comp1 extends Component {
+ render() {
+ return asdf
+ }
+ }
+ `,
+ options: [{noStrings: true, allowedStrings: ['asd'], validateProps: true}],
errors: [
{message: stringsMessage('\'foo\'')},
{message: stringsMessage('asdf')}
]
+ }, {
+ code: '',
+ options: [{noStrings: true, validateProps: true}],
+ errors: [
+ {message: stringsMessage('\'bar\'')}
+ ]
}
]
});