@addon24/eslint-config
Version:
ESLint configuration rules for WorldOfTextcraft projects - Centralized configuration for all project types
179 lines (147 loc) • 5.56 kB
JavaScript
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);
},
});
}
});
},
};
},
};