UNPKG

@addon24/eslint-config

Version:

ESLint configuration rules for WorldOfTextcraft projects - Centralized configuration for all project types

179 lines (147 loc) 5.56 kB
export default { meta: { type: "problem", docs: { description: "Enforce eager loading for translation relations and prevent manual translation loading", category: "Best Practices", recommended: true, }, messages: { missingEagerTrue: "Translation relation 'translations' must have 'eager: true' option. This ensures translations are always loaded automatically.", manualTranslationLoading: "Do not manually load 'translations' in relations array. Translation relations use 'eager: true' and are loaded automatically.", addEagerTrue: "Add 'eager: true' to the @OneToMany decorator options for 'translations' relation.", removeFromRelations: "Remove 'translations' from relations array - it's loaded automatically via eager loading.", }, fixable: "code", schema: [], }, create (context) { return { "PropertyDefinition[key.name='translations']" (node) { const decorators = node.decorators; if (!decorators || decorators.length === 0) { return; } const oneToManyDecorator = decorators.find((decorator) => { if (decorator.expression.type !== "CallExpression") { return false; } const callee = decorator.expression.callee; return callee.type === "Identifier" && callee.name === "OneToMany"; }); if (!oneToManyDecorator) { return; } const args = oneToManyDecorator.expression.arguments; const optionsArg = args[2]; if (!optionsArg || optionsArg.type !== "ObjectExpression") { context.report({ messageId: "missingEagerTrue", node: oneToManyDecorator, fix (fixer) { const lastArg = args[args.length - 1]; return fixer.insertTextAfter( lastArg, ', {\n cascade: true,\n eager: true,\n }', ); }, }); return; } const hasEager = optionsArg.properties.some((prop) => { if (prop.type !== "Property") { return false; } if (prop.key.type !== "Identifier") { return false; } return prop.key.name === "eager"; }); const eagerProp = optionsArg.properties.find((prop) => { if (prop.type !== "Property") { return false; } if (prop.key.type !== "Identifier") { return false; } return prop.key.name === "eager"; }); if (!hasEager) { context.report({ messageId: "missingEagerTrue", node: oneToManyDecorator, fix (fixer) { const lastProperty = optionsArg.properties[optionsArg.properties.length - 1]; return fixer.insertTextAfter( lastProperty, ',\n eager: true', ); }, }); return; } if (eagerProp && eagerProp.type === "Property" && eagerProp.value.type === "Literal" && eagerProp.value.value !== true) { context.report({ messageId: "missingEagerTrue", node: eagerProp, fix (fixer) { return fixer.replaceText(eagerProp.value, "true"); }, }); } }, ArrayExpression (node) { const parent = node.parent; if (!parent || parent.type !== "Property") { return; } if (parent.key.type !== "Identifier" || parent.key.name !== "relations") { return; } node.elements.forEach((element) => { if (!element || element.type !== "Literal") { return; } const value = element.value; if (typeof value !== "string") { return; } if (value === "translations" || value.endsWith(".translations")) { context.report({ messageId: "manualTranslationLoading", node: element, fix (fixer) { const sourceCode = context.getSourceCode(); const elementIndex = node.elements.indexOf(element); const tokenBefore = sourceCode.getTokenBefore(element); const tokenAfter = sourceCode.getTokenAfter(element); if (node.elements.length === 1) { return fixer.remove(element); } if (elementIndex === 0) { if (tokenAfter && tokenAfter.value === ",") { return fixer.removeRange([element.range[0], tokenAfter.range[1]]); } return fixer.remove(element); } if (elementIndex === node.elements.length - 1) { if (tokenBefore && tokenBefore.value === ",") { return fixer.removeRange([tokenBefore.range[0], element.range[1]]); } return fixer.remove(element); } if (tokenBefore && tokenBefore.value === ",") { return fixer.removeRange([tokenBefore.range[0], element.range[1]]); } if (tokenAfter && tokenAfter.value === ",") { return fixer.removeRange([element.range[0], tokenAfter.range[1]]); } return fixer.remove(element); }, }); } }); }, }; }, };