import js from '@eslint/js'; import prettier from 'eslint-config-prettier'; import globals from 'globals'; import tseslint from 'typescript-eslint'; /** * Formatting is Prettier's job – `eslint-config-prettier` switches every stylistic rule off, * so a rule here always means "this is a mistake", never "this looks different". */ export default tseslint.config( // `rpgmz/` is gitignored: a copy of the RPG Maker MZ runtime kept next to the project to read the // autotile tables from (#118). It is nobody's source, so it is not linted either. { ignores: ['dist/**', 'data/**', 'node_modules/**', 'rpgmz/**', 'wiki/**', 'index.html', 'play.html'] }, // Type-aware linting for everything the tsconfig covers (src, tests, vite.config.ts). { files: ['src/**/*.ts', 'tests/**/*.ts', 'vite.config.ts'], extends: [js.configs.recommended, ...tseslint.configs.recommendedTypeChecked], languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname }, globals: globals.browser, }, rules: { // The duplicate `import` statements the last refactoring round had to find by hand. 'no-duplicate-imports': 'error', // `tsc` already reports unused locals; keep ESLint quiet about the same thing. '@typescript-eslint/no-unused-vars': 'off', // Off by design: the codebase builds objects from closures (`createFieldRow` returns // `{ setValue }`, test fixtures return `{ sceneOf, object }`). Those have no `this`, // but the rule cannot tell them from class methods and fired ~50 times on them. '@typescript-eslint/unbound-method': 'off', // A `?? []` on something that is never nullish hides that the type says otherwise. '@typescript-eslint/no-unnecessary-condition': 'error', '@typescript-eslint/no-unnecessary-type-assertion': 'error', '@typescript-eslint/consistent-type-imports': ['error', { fixStyle: 'inline-type-imports' }], '@typescript-eslint/no-import-type-side-effects': 'error', // Async work that nobody awaits silently swallows its rejection. '@typescript-eslint/no-floating-promises': 'error', '@typescript-eslint/no-misused-promises': ['error', { checksVoidReturn: false }], eqeqeq: ['error', 'always', { null: 'ignore' }], 'no-console': ['error', { allow: ['warn', 'error'] }], 'prefer-const': 'error', 'object-shorthand': ['error', 'properties'], }, }, // Node scripts: plain JavaScript, no type information available. { files: ['tools/**/*.mjs'], extends: [js.configs.recommended], languageOptions: { ecmaVersion: 2023, sourceType: 'module', globals: globals.node, }, rules: { 'no-duplicate-imports': 'error', eqeqeq: ['error', 'always', { null: 'ignore' }], 'prefer-const': 'error', }, }, // Editor plugins: browser modules that get everything through `api`, so they import nothing. { files: ['plugins/**/*.js'], extends: [js.configs.recommended], languageOptions: { ecmaVersion: 2023, sourceType: 'module', globals: globals.browser, }, rules: { 'no-restricted-syntax': [ 'error', { selector: 'ImportDeclaration', message: 'A plugin gets everything through `api`.' }, ], eqeqeq: ['error', 'always', { null: 'ignore' }], 'prefer-const': 'error', }, }, prettier, );