source: imaps-frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-literals.js

main
Last change on this file was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 5.8 KB
Line 
1/**
2 * @fileoverview Prevent using string literals in React component definition
3 * @author Caleb Morris
4 * @author David Buchan-Swanson
5 */
6
7'use strict';
8
9const iterFrom = require('es-iterator-helpers/Iterator.from');
10const map = require('es-iterator-helpers/Iterator.prototype.map');
11
12const docsUrl = require('../util/docsUrl');
13const report = require('../util/report');
14const getText = require('../util/eslint').getText;
15
16// ------------------------------------------------------------------------------
17// Rule Definition
18// ------------------------------------------------------------------------------
19
20function trimIfString(val) {
21 return typeof val === 'string' ? val.trim() : val;
22}
23
24const messages = {
25 invalidPropValue: 'Invalid prop value: "{{text}}"',
26 noStringsInAttributes: 'Strings not allowed in attributes: "{{text}}"',
27 noStringsInJSX: 'Strings not allowed in JSX files: "{{text}}"',
28 literalNotInJSXExpression: 'Missing JSX expression container around literal string: "{{text}}"',
29};
30
31/** @type {import('eslint').Rule.RuleModule} */
32module.exports = {
33 meta: {
34 docs: {
35 description: 'Disallow usage of string literals in JSX',
36 category: 'Stylistic Issues',
37 recommended: false,
38 url: docsUrl('jsx-no-literals'),
39 },
40
41 messages,
42
43 schema: [{
44 type: 'object',
45 properties: {
46 noStrings: {
47 type: 'boolean',
48 },
49 allowedStrings: {
50 type: 'array',
51 uniqueItems: true,
52 items: {
53 type: 'string',
54 },
55 },
56 ignoreProps: {
57 type: 'boolean',
58 },
59 noAttributeStrings: {
60 type: 'boolean',
61 },
62 },
63 additionalProperties: false,
64 }],
65 },
66
67 create(context) {
68 const defaults = {
69 noStrings: false,
70 allowedStrings: [],
71 ignoreProps: false,
72 noAttributeStrings: false,
73 };
74 const config = Object.assign({}, defaults, context.options[0] || {});
75 config.allowedStrings = new Set(map(iterFrom(config.allowedStrings), trimIfString));
76
77 function defaultMessageId() {
78 const ancestorIsJSXElement = arguments.length >= 1 && arguments[0];
79 if (config.noAttributeStrings && !ancestorIsJSXElement) {
80 return 'noStringsInAttributes';
81 }
82 if (config.noStrings) {
83 return 'noStringsInJSX';
84 }
85 return 'literalNotInJSXExpression';
86 }
87
88 function getParentIgnoringBinaryExpressions(node) {
89 let current = node;
90 while (current.parent.type === 'BinaryExpression') {
91 current = current.parent;
92 }
93 return current.parent;
94 }
95
96 function getValidation(node) {
97 const values = [trimIfString(node.raw), trimIfString(node.value)];
98 if (values.some((value) => config.allowedStrings.has(value))) {
99 return false;
100 }
101
102 const parent = getParentIgnoringBinaryExpressions(node);
103
104 function isParentNodeStandard() {
105 if (!/^[\s]+$/.test(node.value) && typeof node.value === 'string' && parent.type.includes('JSX')) {
106 if (config.noAttributeStrings) {
107 return parent.type === 'JSXAttribute' || parent.type === 'JSXElement';
108 }
109 if (!config.noAttributeStrings) {
110 return parent.type !== 'JSXAttribute';
111 }
112 }
113
114 return false;
115 }
116
117 const standard = isParentNodeStandard();
118
119 if (config.noStrings) {
120 return standard;
121 }
122 return standard && parent.type !== 'JSXExpressionContainer';
123 }
124
125 function getParentAndGrandParentType(node) {
126 const parent = getParentIgnoringBinaryExpressions(node);
127 const parentType = parent.type;
128 const grandParentType = parent.parent.type;
129
130 return {
131 parent,
132 parentType,
133 grandParentType,
134 grandParent: parent.parent,
135 };
136 }
137
138 function hasJSXElementParentOrGrandParent(node) {
139 const parents = getParentAndGrandParentType(node);
140 const parentType = parents.parentType;
141 const grandParentType = parents.grandParentType;
142
143 return parentType === 'JSXFragment' || parentType === 'JSXElement' || grandParentType === 'JSXElement';
144 }
145
146 function reportLiteralNode(node, messageId) {
147 const ancestorIsJSXElement = hasJSXElementParentOrGrandParent(node);
148 messageId = messageId || defaultMessageId(ancestorIsJSXElement);
149
150 report(context, messages[messageId], messageId, {
151 node,
152 data: {
153 text: getText(context, node).trim(),
154 },
155 });
156 }
157
158 // --------------------------------------------------------------------------
159 // Public
160 // --------------------------------------------------------------------------
161
162 return {
163 Literal(node) {
164 if (getValidation(node) && (hasJSXElementParentOrGrandParent(node) || !config.ignoreProps)) {
165 reportLiteralNode(node);
166 }
167 },
168
169 JSXAttribute(node) {
170 const isNodeValueString = node && node.value && node.value.type === 'Literal' && typeof node.value.value === 'string' && !config.allowedStrings.has(node.value.value);
171
172 if (config.noStrings && !config.ignoreProps && isNodeValueString) {
173 const messageId = 'invalidPropValue';
174 reportLiteralNode(node, messageId);
175 }
176 },
177
178 JSXText(node) {
179 if (getValidation(node)) {
180 reportLiteralNode(node);
181 }
182 },
183
184 TemplateLiteral(node) {
185 const parents = getParentAndGrandParentType(node);
186 const parentType = parents.parentType;
187 const grandParentType = parents.grandParentType;
188 const isParentJSXExpressionCont = parentType === 'JSXExpressionContainer';
189 const isParentJSXElement = parentType === 'JSXElement' || grandParentType === 'JSXElement';
190
191 if (isParentJSXExpressionCont && config.noStrings && (isParentJSXElement || !config.ignoreProps)) {
192 reportLiteralNode(node);
193 }
194 },
195 };
196 },
197};
Note: See TracBrowser for help on using the repository browser.