@addon24/eslint-config
Version:
ESLint configuration rules for WorldOfTextcraft projects - Centralized configuration for all project types
140 lines (126 loc) • 4.74 kB
JavaScript
export default {
meta: {
type: "problem",
docs: {
description: "Prevent usage of Entity references in Swagger documentation",
category: "Best Practices",
recommended: true,
},
fixable: null,
schema: [],
messages: {
entityInSwaggerSchema: "Swagger schema reference '{{reference}}' should use DTO instead of Entity. Replace '{{entityName}}' with '{{dtoName}}'",
entityInApiProperty: "ApiProperty type '{{type}}' should use DTO instead of Entity. Replace '{{entityName}}' with '{{dtoName}}'",
entityInApiBody: "ApiBody type '{{type}}' should use DTO instead of Entity. Replace '{{entityName}}' with '{{dtoName}}'",
},
},
create(context) {
const entityPattern = /Entity$/;
const dtoPattern = /Dto$/;
function getEntityName(reference) {
const match = reference.match(/#\/components\/schemas\/(.+)/);
return match ? match[1] : reference;
}
function getDtoName(entityName) {
return entityName.replace(/Entity$/, "Dto");
}
function checkStringLiteral(node, messageId) {
if (node.type === "Literal" && typeof node.value === "string") {
const value = node.value;
// Check for schema references like "#/components/schemas/AbilityEntity"
if (value.includes("#/components/schemas/") && value.includes("Entity")) {
const entityName = getEntityName(value);
if (entityPattern.test(entityName)) {
const dtoName = getDtoName(entityName);
context.report({
node,
messageId,
data: {
reference: value,
entityName,
dtoName,
},
});
}
}
}
}
function checkTypeReference(node, messageId) {
// Check for type references in ApiProperty, ApiBody decorators
if (node.type === "Identifier" && entityPattern.test(node.name)) {
const dtoName = getDtoName(node.name);
context.report({
node,
messageId,
data: {
type: node.name,
entityName: node.name,
dtoName,
},
});
}
}
function checkObjectExpression(node) {
if (node.type === "ObjectExpression") {
node.properties.forEach((prop) => {
if (prop.type === "Property") {
// Check schema references in object properties
if (prop.key && prop.key.name === "$ref") {
checkStringLiteral(prop.value, "entityInSwaggerSchema");
}
// Check items property in arrays
if (prop.key && prop.key.name === "items" && prop.value.type === "ObjectExpression") {
prop.value.properties.forEach((itemProp) => {
if (itemProp.key && itemProp.key.name === "$ref") {
checkStringLiteral(itemProp.value, "entityInSwaggerSchema");
}
});
}
}
});
}
}
return {
Decorator(node) {
if (node.expression && node.expression.type === "CallExpression") {
const callee = node.expression.callee;
// Check ApiResponse, ApiBody, ApiProperty decorators
if (callee.type === "Identifier") {
const decoratorName = callee.name;
if (["ApiResponse", "ApiBody", "ApiProperty"].includes(decoratorName)) {
node.expression.arguments.forEach((arg) => {
if (arg.type === "ObjectExpression") {
arg.properties.forEach((prop) => {
if (prop.type === "Property") {
// Check type property
if (prop.key && prop.key.name === "type") {
checkTypeReference(prop.value, "entityInApiProperty");
}
// Check schema property
if (prop.key && prop.key.name === "schema") {
checkObjectExpression(prop.value);
}
}
});
}
});
}
}
}
},
// Check for $ref in any string literal
Literal(node) {
if (typeof node.value === "string" && node.value.includes("Entity")) {
const parent = node.parent;
// Check if this is in a swagger context
if (parent && parent.type === "Property" && parent.key) {
if (parent.key.name === "$ref" ||
(parent.key.type === "Literal" && parent.key.value === "$ref")) {
checkStringLiteral(node, "entityInSwaggerSchema");
}
}
}
},
};
},
};