From e247e14c0ef2a635c767c2ac6cf554c73f2acb23 Mon Sep 17 00:00:00 2001 From: Cross-sama Date: Wed, 4 Jun 2025 13:04:38 +0300 Subject: [PATCH] Add a castom lint rule to check for duplicates, and eslint itself. --- eslint-rules/no-duplicate-manifest-entries.js | 65 +++++++++++++++++++ eslint.config.js | 25 +++++++ package.json | 3 +- 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 eslint-rules/no-duplicate-manifest-entries.js create mode 100644 eslint.config.js diff --git a/eslint-rules/no-duplicate-manifest-entries.js b/eslint-rules/no-duplicate-manifest-entries.js new file mode 100644 index 0000000..1c9ccef --- /dev/null +++ b/eslint-rules/no-duplicate-manifest-entries.js @@ -0,0 +1,65 @@ +import fs from 'fs'; +import path from 'path'; + +const manifestDir = path.resolve(process.cwd(), 'manifests'); + +function loadManifest(fileName) { + const filePath = path.join(manifestDir, fileName); + return JSON.parse(fs.readFileSync(filePath, 'utf-8')); +} + +function getDuplicates(manifests) { + const seen = new Map(); + const duplicates = []; + + for (const [manifestName, entries] of Object.entries(manifests)) { + for (const moduleName of Object.keys(entries)) { + const key = moduleName.toLowerCase(); + if (seen.has(key)) { + duplicates.push({ + moduleName, + first: seen.get(key), + second: manifestName, + }); + } else { + seen.set(key, manifestName); + } + } + } + + return duplicates; +} + +export default { + meta: { + type: 'problem', + docs: { + description: 'disallow duplicate modules across manifests', + }, + schema: [], + }, + create(context) { + // only run once per lint run (on Program) + if (context.getFilename().endsWith('.js') === false) return {}; + + const manifestFiles = fs.readdirSync(manifestDir).filter(f => f.endsWith('.json')); + + const manifests = {}; + for (const file of manifestFiles) { + manifests[file] = loadManifest(file); + } + + const duplicates = getDuplicates(manifests); + + if (duplicates.length > 0) { + for (const dup of duplicates) { + context.report({ + loc: { line: 1, column: 0 }, + message: `Module "${dup.moduleName}" found in both "${dup.first}" and "${dup.second}"`, + }); + } + } + + return {}; + }, +}; \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..7048765 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,25 @@ +import js from "@eslint/js"; +import globals from "globals"; +import tseslint from "typescript-eslint"; +import { defineConfig } from "eslint/config"; + + +export default defineConfig([ + { files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], plugins: { js }, extends: ["js/recommended"] }, + { files: ["**/*.{js,mjs,cjs,ts,mts,cts}"], languageOptions: { globals: globals.node } }, + tseslint.configs.recommended, + { + files: ['**/*.js'], + rules: { + 'no-duplicate-manifest-entries': 'error', + }, + plugins: { + 'no-duplicate-manifest-entries': { + rules: { + 'no-duplicate-manifest-entries': noDuplicateManifestEntries, + }, + }, + }, + }, +]); + diff --git a/package.json b/package.json index bcb7ea4..d9eac95 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "generate-schema": "node scripts/generate-schema.js", "prepare": "tshy && npm run build:update-manifest-paths", "test:validate": "node scripts/validate-modules.js", - "sort-manifests": "node scripts/sort-manifests.js" + "sort-manifests": "node scripts/sort-manifests.js", + "lint": "eslint ." }, "tshy": { "exports": {