diff --git a/.gitignore b/.gitignore
index 123ae94..d532efc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,5 @@ build/Release
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
+
+.idea
diff --git a/README.md b/README.md
index d674389..c64fc48 100644
--- a/README.md
+++ b/README.md
@@ -71,6 +71,7 @@ Finally, enable all of the rules that you would like to use.
"react-native/split-platform-components": 2,
"react-native/no-inline-styles": 2,
"react-native/no-color-literals": 2,
+ "react-native/no-raw-text": 2,
}
}
```
@@ -81,6 +82,7 @@ Finally, enable all of the rules that you would like to use.
* [split-platform-components](docs/rules/split-platform-components.md): Enforce using platform specific filenames when necessary
* [no-inline-styles](docs/rules/no-inline-styles.md): Detect JSX components with inline styles that contain literal values
* [no-color-literals](docs/rules/no-color-literals.md): Detect `StyleSheet` rules and inline styles containing color literals instead of variables
+* [no-raw-text](docs/rules/no-raw-text.md): Detect raw text outside of `Text` component
[npm-url]: https://npmjs.org/package/eslint-plugin-react-native
[npm-image]: http://img.shields.io/npm/v/eslint-plugin-react-native.svg?style=flat-square
diff --git a/docs/rules/no-raw-text.md b/docs/rules/no-raw-text.md
new file mode 100644
index 0000000..c5a038e
--- /dev/null
+++ b/docs/rules/no-raw-text.md
@@ -0,0 +1,26 @@
+# Detect raw text outside of Text component
+All strings in React Native should be wrapped with a Text component.
+
+## Rule Details
+
+The following patterns are considered warnings:
+
+```js
+some text
+```
+
+```js
+const text = 'some text';
+{`${text}`}
+```
+
+The following patterns are not considered warnings:
+
+```js
+some text
+```
+
+```js
+const text = 'some text';
+{`${text}`}
+```
diff --git a/index.js b/index.js
index ddbc6e1..abc99bc 100644
--- a/index.js
+++ b/index.js
@@ -7,6 +7,7 @@ const allRules = {
'no-inline-styles': require('./lib/rules/no-inline-styles'),
'no-color-literals': require('./lib/rules/no-color-literals'),
'split-platform-components': require('./lib/rules/split-platform-components'),
+ 'no-raw-text': require('./lib/rules/no-raw-text'),
};
function configureAsError(rules) {
@@ -30,6 +31,7 @@ module.exports = {
'no-inline-styles': 0,
'no-color-literals': 0,
'split-platform-components': 0,
+ 'no-raw-text': 0,
},
environments: {
'react-native': {
diff --git a/lib/rules/no-raw-text.js b/lib/rules/no-raw-text.js
new file mode 100644
index 0000000..4c1340c
--- /dev/null
+++ b/lib/rules/no-raw-text.js
@@ -0,0 +1,63 @@
+/**
+ * @fileoverview Detects raw text outside of Text component
+ * @author Alex Zhukov
+ */
+
+'use strict';
+
+module.exports = (context) => {
+ const elementName = node => (
+ node.openingElement &&
+ node.openingElement.name &&
+ node.openingElement.name.type === 'JSXIdentifier' &&
+ node.openingElement.name.name
+ );
+
+ const report = (node) => {
+ const errorValue = node.type === 'TemplateLiteral'
+ ? `TemplateLiteral: ${node.expressions[0].name}`
+ : node.value;
+ context.report({
+ node,
+ message: `Raw text (${errorValue.trim()}) cannot be used outside of a tag`,
+ });
+ };
+
+ const getValidation = node => elementName(node.parent) !== 'Text';
+
+ return {
+ Literal(node) {
+ const parentType = node.parent.type;
+ const onlyFor = ['JSXExpressionContainer', 'JSXElement'];
+ if (/^[\s]+$/.test(node.value) ||
+ typeof node.value !== 'string' ||
+ !onlyFor.includes(parentType) ||
+ (node.parent.parent && node.parent.parent.type === 'JSXAttribute')
+ ) return;
+
+ const isStringLiteral = parentType === 'JSXExpressionContainer';
+ if (getValidation(isStringLiteral ? node.parent : node)) {
+ report(node);
+ }
+ },
+
+ JSXText(node) {
+ if (getValidation(node)) {
+ report(node);
+ }
+ },
+
+ TemplateLiteral(node) {
+ if (
+ node.parent.type !== 'JSXExpressionContainer' ||
+ (node.parent.parent && node.parent.parent.type === 'JSXAttribute')
+ ) return;
+
+ if (getValidation(node.parent)) {
+ report(node);
+ }
+ },
+ };
+};
+
+module.exports.schema = [];
diff --git a/tests/lib/rules/no-raw-text.js b/tests/lib/rules/no-raw-text.js
new file mode 100644
index 0000000..dc25f8b
--- /dev/null
+++ b/tests/lib/rules/no-raw-text.js
@@ -0,0 +1,108 @@
+/**
+ * @fileoverview Detects raw text outside of Text component
+ * @author Alex Zhukov
+ */
+
+'use strict';
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/no-raw-text');
+const RuleTester = require('eslint').RuleTester;
+
+require('babel-eslint');
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester();
+
+const tests = {
+ valid: [
+ {
+ code: `
+ export default class MyComponent extends Component {
+ render() {
+ return (some text);
+ }
+ }
+ `,
+ },
+ {
+ code: `
+ export default class MyComponent extends Component {
+ render() {
+ const text = 'some text';
+ return ({\`\${text}\`});
+ }
+ }
+ `,
+ },
+ {
+ code: `
+ export default class MyComponent extends Component {
+ render() {
+ return ({'some text'});
+ }
+ }
+ `,
+ },
+ ],
+ invalid: [
+ {
+ code: `
+ export default class MyComponent extends Component {
+ render() {
+ return (some text);
+ }
+ }
+ `,
+ errors: [{
+ message: 'Raw text (some text) cannot be used outside of a tag',
+ }],
+ },
+ {
+ code: `
+ export default class MyComponent extends Component {
+ render() {
+ const text = 'some text';
+ return ({\`\${text}\`});
+ }
+ }
+ `,
+ errors: [{
+ message: 'Raw text (TemplateLiteral: text) cannot be used outside of a tag',
+ }],
+ },
+ {
+ code: `
+ export default class MyComponent extends Component {
+ render() {
+ return ({'some text'});
+ }
+ }
+ `,
+ errors: [{
+ message: 'Raw text (some text) cannot be used outside of a tag',
+ }],
+ },
+ ],
+};
+
+const config = {
+ parser: 'babel-eslint',
+ parserOptions: {
+ ecmaFeatures: {
+ classes: true,
+ jsx: true,
+ },
+ },
+};
+
+tests.valid.forEach(t => Object.assign(t, config));
+tests.invalid.forEach(t => Object.assign(t, config));
+
+ruleTester.run('no-raw-text', rule, tests);