UNPKG

@addon24/eslint-config

Version:

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

158 lines (138 loc) 4.85 kB
/** * ESLint-Regel: enforce-module-existence * * Überprüft, ob importierte Module tatsächlich existieren. * Verhindert TS2307-Fehler zur Build-Zeit. */ import fs from "fs"; import path from "path"; export default { rules: { "enforce-module-existence": { meta: { type: "problem", docs: { description: "Enforce that imported modules actually exist", category: "Type Safety", recommended: true, }, messages: { moduleNotFound: "Module '{{moduleName}}' not found at path '{{resolvedPath}}'", cannotResolveModule: "Cannot resolve module '{{moduleName}}'", }, schema: [ { type: "object", properties: { ignorePatterns: { type: "array", items: { type: "string" }, description: "Patterns to ignore", default: [] } }, additionalProperties: false } ] }, create(context) { const options = context.options[0] || {}; const ignorePatterns = options.ignorePatterns || []; /** * Resolves a module path relative to the current file */ function resolveModulePath(moduleName, currentFilePath) { const projectRoot = context.getCwd(); // Handle @/ imports if (moduleName.startsWith("@/")) { const relativePath = moduleName.substring(2); return path.join(projectRoot, "src", relativePath); } // Handle relative imports if (moduleName.startsWith("./") || moduleName.startsWith("../")) { const currentDir = path.dirname(currentFilePath); return path.resolve(currentDir, moduleName); } // Handle absolute imports (node_modules) if (!moduleName.startsWith(".") && !moduleName.startsWith("/")) { return path.join(projectRoot, "node_modules", moduleName); } return null; } /** * Checks if a module exists */ function checkModuleExists(moduleName, currentFilePath) { // Ignore certain patterns for (const pattern of ignorePatterns) { if (moduleName.match(new RegExp(pattern))) { return; } } // Ignore npm packages (don't start with @/ or ./ or ../) if (!moduleName.startsWith("@/") && !moduleName.startsWith("./") && !moduleName.startsWith("../")) { return; } const resolvedPath = resolveModulePath(moduleName, currentFilePath); if (!resolvedPath) { return; } // Check for .ts, .tsx, .js, .jsx files const extensions = [".ts", ".tsx", ".js", ".jsx", "/index.ts", "/index.tsx", "/index.js", "/index.jsx"]; for (const ext of extensions) { const fullPath = resolvedPath + ext; if (fs.existsSync(fullPath)) { return; } } // Check if it's a directory with index file if (fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isDirectory()) { const indexPath = path.join(resolvedPath, "index.ts"); if (fs.existsSync(indexPath)) { return; } } return resolvedPath; } return { ImportDeclaration(node) { const moduleName = node.source.value; const currentFilePath = context.getFilename(); const missingPath = checkModuleExists(moduleName, currentFilePath); if (missingPath) { context.report({ node: node.source, messageId: "moduleNotFound", data: { moduleName, resolvedPath: missingPath } }); } }, CallExpression(node) { // Check dynamic imports if (node.callee.type === "Import" && node.arguments.length > 0) { const arg = node.arguments[0]; if (arg.type === "Literal" && typeof arg.value === "string") { const moduleName = arg.value; const currentFilePath = context.getFilename(); const missingPath = checkModuleExists(moduleName, currentFilePath); if (missingPath) { context.report({ node: arg, messageId: "moduleNotFound", data: { moduleName, resolvedPath: missingPath } }); } } } } }; } } } };