@addon24/eslint-config
Version: 
ESLint configuration rules for WorldOfTextcraft projects - Centralized configuration for all project types
183 lines (166 loc) • 6.82 kB
JavaScript
export default {
  meta: {
    type: "problem",
    docs: {
      description: "Entity-DTOs müssen fromEntity-Methoden bereitstellen",
      category: "Architecture",
      recommended: true,
    },
    schema: [
      {
        type: "object",
        properties: {
          entityDtoPath: {
            type: "string",
            description: "Path pattern for Entity DTO files",
            default: "/dto/Entity/"
          },
          requireFromEntityArray: {
            type: "boolean",
            description: "Whether fromEntityArray method is required",
            default: false
          },
          allowCreateMethod: {
            type: "boolean",
            description: "Whether create method is allowed in Entity DTOs",
            default: false
          },
          allowGenericMethods: {
            type: "boolean",
            description: "Whether generic-specific methods are allowed in Entity DTOs",
            default: true
          }
        },
        additionalProperties: false
      }
    ],
    messages: {
      missingFromEntityMethod: "Entity-DTO-Klasse '{{dtoName}}' muss eine statische fromEntity-Methode bereitstellen",
      forbiddenCreateMethod: "Entity-DTO-Klasse '{{dtoName}}' darf keine create-Methode haben. Verwende stattdessen fromEntity-Methoden.",
      missingGenericFromEntityMethod: "Entity-DTO-Klasse '{{dtoName}}' mit Generics muss spezifische fromEntity-Methoden für jeden Generic-Typ bereitstellen",
      invalidGenericMethodSignature: "Generic-spezifische Methode '{{methodName}}' hat eine ungültige Signatur. Erwartet: {{expectedSignature}}",
    },
  },
  create(context) {
    const filename = context.getFilename();
    const options = context.options[0] || {};
    const entityDtoPath = options.entityDtoPath || "/dto/Entity/";
    const requireFromEntityArray = options.requireFromEntityArray || false;
    const allowCreateMethod = options.allowCreateMethod || false;
    const allowGenericMethods = options.allowGenericMethods !== false;
    // Prüfe, ob es sich um eine Entity-DTO-Datei handelt
    const isEntityDtoFile = filename.includes(entityDtoPath) || filename.includes("test-fixtures");
    if (!isEntityDtoFile) {
      return {};
    }
    // Skip test files and fixtures
    if (filename.includes('test') || filename.includes('__tests__') || filename.includes('.test.')) {
      return {};
    }
    let classNode = null;
    let dtoClassName = "";
    let hasGenericEntity = false;
    let genericTypes = [];
    return {
      ClassDeclaration(node) {
        classNode = node;
        dtoClassName = node.id?.name;
      },
      ExportDefaultDeclaration(node) {
        if (node.declaration.type === "ClassDeclaration") {
          classNode = node.declaration;
          dtoClassName = node.declaration.id?.name;
        }
      },
      ImportDeclaration(node) {
        // Prüfe, ob eine Generic-Entity importiert wird
        if (node.source.value.includes("BackpackRefItemEntity")) {
          hasGenericEntity = true;
          // Extrahiere Generic-Typen aus der Entity
          const specifiers = node.specifiers;
          specifiers.forEach(spec => {
            if (spec.type === "ImportDefaultSpecifier" && spec.local.name === "BackpackRefItemEntity") {
              // BackpackRefItemEntity hat die Generic-Typen: IsBackpack, IsEquipped, IsItem
              genericTypes = ["IsBackpack", "IsEquipped", "IsItem"];
            }
          });
        }
      },
      "Program:exit"(node) {
        if (!classNode || !dtoClassName) return;
        // Skip if class has test fixture comment
        const sourceCode = context.getSourceCode();
        const classText = sourceCode.getText(classNode);
        if (classText.includes('Test-Fixture') || classText.includes('keine Validierung erforderlich')) {
          return;
        }
        const methods = classNode.body.body.filter(member =>
          member.type === "MethodDefinition" &&
          member.static === true
        );
        const hasFromEntityMethod = methods.some(method =>
          method.key?.name === "fromEntity"
        );
        const hasCreateMethod = methods.some(method =>
          method.key?.name === "create"
        );
        // Prüfe auf Generic-spezifische Methoden
        const hasGenericMethods = methods.some(method =>
          method.key?.name === "createInventorySlot" ||
          method.key?.name === "createBackpackSlot" ||
          method.key?.name === "createEquippedSlot"
        );
        // Request-DTOs sind von der fromEntity-Regel ausgenommen
        const isRequestDto = filename.includes("/dto/Request/");
        // Prüfe, ob es sich um eine Entity-DTO handelt
        const isEntityDto = dtoClassName.includes("EntityDto") ||
                           (filename.includes("test-fixtures") && dtoClassName.includes("EntityDto"));
        if (!isRequestDto && isEntityDto) {
          // Standard Entity-DTO Validierung
          if (!hasFromEntityMethod) {
            context.report({
              node: classNode,
              messageId: "missingFromEntityMethod",
              data: { dtoName: dtoClassName }
            });
          }
          if (hasCreateMethod && !allowCreateMethod) {
            context.report({
              node: classNode,
              messageId: "forbiddenCreateMethod",
              data: { dtoName: dtoClassName }
            });
          }
          // Generic-spezifische Validierung
          if (hasGenericEntity && allowGenericMethods) {
            // Prüfe, ob Generic-spezifische Methoden vorhanden sind
            if (!hasGenericMethods) {
              context.report({
                node: classNode,
                messageId: "missingGenericFromEntityMethod",
                data: { dtoName: dtoClassName }
              });
            }
            // Validiere Generic-Methoden-Signaturen
            methods.forEach(method => {
              if (method.key?.name === "createInventorySlot") {
                // Erwartete Signatur: createInventorySlot(data: { backpackRefItem: BackpackRefItemEntity; character: CharacterEntity; item: ItemEntity; slotNumber: number; })
                const params = method.value.params;
                if (!params || params.length !== 1) {
                  context.report({
                    node: method,
                    messageId: "invalidGenericMethodSignature",
                    data: {
                      methodName: "createInventorySlot",
                      expectedSignature: "createInventorySlot(data: { backpackRefItem: BackpackRefItemEntity; character: CharacterEntity; item: ItemEntity; slotNumber: number; })"
                    }
                  });
                }
              }
            });
          }
        }
      },
    };
  },
};