@generaltranslation/gt-next-lint
Version:
ESLint plugin for General Translation Next.js integration
153 lines (152 loc) • 6.7 kB
JavaScript
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const require_rules_utils = require("./utils.js");
//#region src/rules/no-dynamic-jsx.ts
const TRANSLATION_COMPONENTS = ["T"];
const VARIABLE_COMPONENTS = [
"Var",
"DateTime",
"Num",
"Currency"
];
function isTranslationComponent(name) {
return TRANSLATION_COMPONENTS.includes(name);
}
function isVariableComponent(name) {
return VARIABLE_COMPONENTS.includes(name);
}
const noDynamicJsx = {
meta: {
type: "problem",
docs: {
description: "Detect unwrapped dynamic content in GT-Next translation components",
category: "Best Practices",
recommended: true,
url: "https://github.com/generaltranslation/gt/tree/main/packages/next-lint#no-dynamic-jsx"
},
fixable: void 0,
schema: [],
messages: { dynamicJsx: "Dynamic content in <T> component should be wrapped in a variable component (<Var>, <DateTime>, <Num>, or <Currency>)" }
},
create(context) {
const state = {
translationStack: 0,
variableStack: 0,
imports: {
translationComponents: /* @__PURE__ */ new Set(),
variableComponents: /* @__PURE__ */ new Set(),
namespaceImports: /* @__PURE__ */ new Set(),
assignedTranslationComponents: /* @__PURE__ */ new Set(),
assignedVariableComponents: /* @__PURE__ */ new Set()
}
};
function getElementName(node) {
if (!require_rules_utils.isAstNode(node) || !require_rules_utils.isAstNode(node.openingElement)) return null;
const name = node.openingElement.name;
if (!require_rules_utils.isAstNode(name)) return null;
if (name.type === "JSXIdentifier") return require_rules_utils.getNodeName(name);
if (name.type === "JSXMemberExpression") {
const obj = name.object;
const prop = name.property;
if (require_rules_utils.isAstNode(obj) && require_rules_utils.isAstNode(prop) && obj.type === "JSXIdentifier" && prop.type === "JSXIdentifier") {
const objectName = require_rules_utils.getNodeName(obj);
const propertyName = require_rules_utils.getNodeName(prop);
return objectName && propertyName ? `${objectName}.${propertyName}` : null;
}
}
return null;
}
function isNamespaceTranslationComponent(elementName) {
const parts = elementName.split(".");
if (parts.length === 2) {
const [namespace, component] = parts;
return state.imports.namespaceImports.has(namespace) && isTranslationComponent(component);
}
return false;
}
function isNamespaceVariableComponent(elementName) {
const parts = elementName.split(".");
if (parts.length === 2) {
const [namespace, component] = parts;
return state.imports.namespaceImports.has(namespace) && isVariableComponent(component);
}
return false;
}
function isInsideJSXAttribute(node) {
if (!require_rules_utils.isAstNode(node)) return false;
let currentNode = require_rules_utils.isAstNode(node.parent) ? node.parent : null;
while (currentNode) {
if (currentNode.type === "JSXAttribute") return true;
if (currentNode.type === "JSXElement") return false;
currentNode = require_rules_utils.isAstNode(currentNode.parent) ? currentNode.parent : null;
}
return false;
}
function isComment(expression) {
if (!require_rules_utils.isAstNode(expression)) return false;
return expression.type === "Block" || expression.type === "Line";
}
function isEmpty(expression) {
if (!require_rules_utils.isAstNode(expression)) return false;
return expression.type === "JSXEmptyExpression";
}
return {
ImportDeclaration(node) {
if (!require_rules_utils.isAstNode(node) || !require_rules_utils.isAstNode(node.source)) return;
if (node.source?.type === "Literal" && typeof node.source.value === "string") {
const source = node.source.value;
if (require_rules_utils.isGTModule(source)) {
const specifiers = Array.isArray(node.specifiers) ? node.specifiers : [];
for (const specifier of specifiers) {
if (!require_rules_utils.isAstNode(specifier)) continue;
if (specifier.type === "ImportSpecifier") {
const importedName = require_rules_utils.getNodeName(specifier.imported) || "";
const localName = require_rules_utils.getNodeName(specifier.local) || "";
if (isTranslationComponent(importedName)) state.imports.translationComponents.add(localName);
else if (isVariableComponent(importedName)) state.imports.variableComponents.add(localName);
} else if (specifier.type === "ImportNamespaceSpecifier") {
const localName = require_rules_utils.getNodeName(specifier.local) || "";
state.imports.namespaceImports.add(localName);
}
}
}
}
},
VariableDeclarator(node) {
if (require_rules_utils.isAstNode(node) && require_rules_utils.isAstNode(node.id) && require_rules_utils.isAstNode(node.init) && node.id.type === "Identifier" && node.init.type === "Identifier") {
const varName = require_rules_utils.getNodeName(node.id);
const assignedFrom = require_rules_utils.getNodeName(node.init);
if (!varName || !assignedFrom) return;
if (state.imports.translationComponents.has(assignedFrom)) state.imports.assignedTranslationComponents.add(varName);
else if (state.imports.variableComponents.has(assignedFrom)) state.imports.assignedVariableComponents.add(varName);
}
},
JSXExpressionContainer(node) {
if (!require_rules_utils.isAstNode(node)) return;
if (isInsideJSXAttribute(node)) return;
if (node.expression && require_rules_utils.isStringLiteral(node.expression) || isComment(node.expression) || isEmpty(node.expression)) return;
let inTranslationComponent = false;
let inVariableComponent = false;
let currentNode = require_rules_utils.isAstNode(node.parent) ? node.parent : null;
while (currentNode) {
if (currentNode.type === "JSXElement") {
const elementName = getElementName(currentNode);
if (elementName) {
if (state.imports.variableComponents.has(elementName) || state.imports.assignedVariableComponents.has(elementName) || isNamespaceVariableComponent(elementName)) {
inVariableComponent = true;
break;
} else if (state.imports.translationComponents.has(elementName) || state.imports.assignedTranslationComponents.has(elementName) || isNamespaceTranslationComponent(elementName)) inTranslationComponent = true;
}
}
currentNode = require_rules_utils.isAstNode(currentNode.parent) ? currentNode.parent : null;
}
if (inTranslationComponent && !inVariableComponent) context.report({
node,
messageId: "dynamicJsx"
});
}
};
}
};
//#endregion
exports.noDynamicJsx = noDynamicJsx;
//# sourceMappingURL=no-dynamic-jsx.js.map