mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
189 lines
6.2 KiB
Diff
189 lines
6.2 KiB
Diff
diff --git a/lib/rules/no-conditional-text-nodes-with-siblings.js b/lib/rules/no-conditional-text-nodes-with-siblings.js
|
|
index b8d1c57eabd898b1e9bef7ae0da62c9c789df478..1a4daed2faebeb43cc90d661fd8a92cc58bc68c6 100644
|
|
--- a/lib/rules/no-conditional-text-nodes-with-siblings.js
|
|
+++ b/lib/rules/no-conditional-text-nodes-with-siblings.js
|
|
@@ -6,6 +6,8 @@
|
|
|
|
const { ESLintUtils } = require('@typescript-eslint/utils');
|
|
|
|
+const DEFAULT_IGNORE_PARENTS = ['Trans', 'Plural', 'Select', 'SelectOrdinal'];
|
|
+
|
|
/** @type {import('eslint').Rule.RuleModule} */
|
|
module.exports = {
|
|
meta: {
|
|
@@ -15,7 +17,19 @@ module.exports = {
|
|
'Conditionally rendered text nodes should be wrapped in an element (for example, a `<span>`), otherwise Google Translate can cause a browser error.',
|
|
url: 'https://github.com/getcouped/eslint-plugin-react-google-translate#eslint-plugin-react-google-translate',
|
|
},
|
|
- schema: [],
|
|
+ schema: [
|
|
+ {
|
|
+ type: 'object',
|
|
+ properties: {
|
|
+ ignoreParents: {
|
|
+ type: 'array',
|
|
+ items: { type: 'string' },
|
|
+ uniqueItems: true,
|
|
+ },
|
|
+ },
|
|
+ additionalProperties: false,
|
|
+ },
|
|
+ ],
|
|
messages: {
|
|
'conditional-text-node':
|
|
'Conditionally rendered text nodes with siblings (elements or nodes), when rendered as a direct child of a JSX element, should be wrapped in an element (for example, a `<span>`) to prevent Google Translate causing a browser error while manipulating the DOM. This also applies to return values from functions, so `getString()` should become `<span>{getString()}</span>`.',
|
|
@@ -25,6 +39,45 @@ module.exports = {
|
|
},
|
|
|
|
create(context) {
|
|
+ const ignoreParents =
|
|
+ (context.options &&
|
|
+ context.options[0] &&
|
|
+ context.options[0].ignoreParents) ||
|
|
+ DEFAULT_IGNORE_PARENTS;
|
|
+
|
|
+ function getJSXElementName(node) {
|
|
+ const name = node.openingElement && node.openingElement.name;
|
|
+ if (!name) return null;
|
|
+ if (name.type === 'JSXIdentifier') return name.name;
|
|
+ if (name.type === 'JSXMemberExpression' && name.property) {
|
|
+ return name.property.name;
|
|
+ }
|
|
+ if (name.type === 'JSXNamespacedName' && name.name) {
|
|
+ return name.name.name;
|
|
+ }
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ function hasIgnoredAncestor(node) {
|
|
+ if (ignoreParents.length === 0) return false;
|
|
+ let current = node;
|
|
+ while (current) {
|
|
+ if (
|
|
+ current.type === 'JSXElement' &&
|
|
+ ignoreParents.includes(getJSXElementName(current))
|
|
+ ) {
|
|
+ return true;
|
|
+ }
|
|
+ current = current.parent;
|
|
+ }
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ function report(descriptor) {
|
|
+ if (hasIgnoredAncestor(descriptor.node)) return;
|
|
+ context.report(descriptor);
|
|
+ }
|
|
+
|
|
// when type checking is unavailable, `parserServices` will be `null`
|
|
let parserServices = null;
|
|
let checker = null;
|
|
@@ -193,7 +246,7 @@ module.exports = {
|
|
!isWhitespace(node) &&
|
|
isProblematicConditional(node)
|
|
) {
|
|
- context.report({
|
|
+ report({
|
|
node,
|
|
messageId: 'conditional-text-node',
|
|
});
|
|
@@ -203,7 +256,7 @@ module.exports = {
|
|
// expressions
|
|
TemplateLiteral(node) {
|
|
if (!isWhitespace(node) && isProblematicConditional(node)) {
|
|
- context.report({
|
|
+ report({
|
|
node,
|
|
messageId: 'conditional-text-node',
|
|
});
|
|
@@ -216,7 +269,7 @@ module.exports = {
|
|
hasSiblings(node) &&
|
|
conditionalSiblingsPrecedeNode(node)
|
|
) {
|
|
- context.report({
|
|
+ report({
|
|
node,
|
|
messageId: 'text-node-preceded-by-conditional',
|
|
});
|
|
@@ -230,7 +283,7 @@ module.exports = {
|
|
isProblematicConditional(node) &&
|
|
returnsStringifiableValue(node)
|
|
) {
|
|
- context.report({
|
|
+ report({
|
|
node,
|
|
messageId: 'conditional-text-node',
|
|
});
|
|
@@ -241,7 +294,7 @@ module.exports = {
|
|
conditionalSiblingsPrecedeNode(node.parent) &&
|
|
returnsStringifiableValue(node)
|
|
) {
|
|
- context.report({
|
|
+ report({
|
|
node,
|
|
messageId: 'text-node-preceded-by-conditional',
|
|
});
|
|
@@ -254,7 +307,7 @@ module.exports = {
|
|
node.arguments.length > 0 &&
|
|
isProblematicConditional(node)
|
|
) {
|
|
- context.report({
|
|
+ report({
|
|
node,
|
|
messageId: 'conditional-text-node',
|
|
});
|
|
@@ -267,7 +320,7 @@ module.exports = {
|
|
hasSiblings(node.parent) &&
|
|
conditionalSiblingsPrecedeNode(node.parent)
|
|
) {
|
|
- context.report({
|
|
+ report({
|
|
node,
|
|
messageId: 'text-node-preceded-by-conditional',
|
|
});
|
|
@@ -279,7 +332,7 @@ module.exports = {
|
|
return;
|
|
}
|
|
if (isProblematicConditional(node)) {
|
|
- context.report({
|
|
+ report({
|
|
node,
|
|
messageId: 'conditional-text-node',
|
|
});
|
|
@@ -291,7 +344,7 @@ module.exports = {
|
|
return;
|
|
}
|
|
if (isProblematicConditional(node)) {
|
|
- context.report({
|
|
+ report({
|
|
node,
|
|
messageId: 'conditional-text-node',
|
|
});
|
|
@@ -306,7 +359,7 @@ module.exports = {
|
|
isProblematicConditional(node) &&
|
|
returnsStringifiableValue(node)
|
|
) {
|
|
- context.report({
|
|
+ report({
|
|
node,
|
|
messageId: 'conditional-text-node',
|
|
});
|
|
@@ -317,7 +370,7 @@ module.exports = {
|
|
conditionalSiblingsPrecedeNode(node.parent) &&
|
|
returnsStringifiableValue(node)
|
|
) {
|
|
- context.report({
|
|
+ report({
|
|
node,
|
|
messageId: 'text-node-preceded-by-conditional',
|
|
});
|
|
@@ -332,7 +385,7 @@ module.exports = {
|
|
) {
|
|
const callExpression = getCallExpression(node);
|
|
if (callExpression && isProblematicConditional(callExpression)) {
|
|
- context.report({
|
|
+ report({
|
|
node: callExpression,
|
|
messageId: 'conditional-text-node',
|
|
});
|