import {includeIgnoreFile} from '@eslint/compat' import js from '@eslint/js' import json from '@eslint/json' import restrictedGlobals from 'confusing-browser-globals' import prettier from 'eslint-plugin-prettier/recommended' import react from 'eslint-plugin-react' import reactHooks from 'eslint-plugin-react-hooks' import tsdoc from 'eslint-plugin-tsdoc' import {defineConfig} from 'eslint/config' import globals from 'globals' import path from 'node:path' import tseslint from 'typescript-eslint' const gitignore = path.resolve(import.meta.dirname, '.gitignore') export default defineConfig( includeIgnoreFile(gitignore, '.gitignore'), // all files by default get shared globals { name: 'javascript basics', files: ['**/*.@(js|jsx|ts|tsx)'], extends: [js.configs.recommended], languageOptions: { globals: { ...globals.es2024, ...globals['shared-node-browser'], }, }, rules: { // eg, `open` is a global, but probably not really intended that way in our code 'no-restricted-globals': ['error', ...restrictedGlobals], 'no-unused-vars': ['warn', {varsIgnorePattern: '(?:^_)'}], }, }, { name: 'typescript basics', files: ['**/*.@(ts|tsx)'], extends: [tseslint.configs.strictTypeChecked], plugins: {tsdoc}, languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, rules: { // allow leading underscore for marking unused vars '@typescript-eslint/no-unused-vars': ['warn', {varsIgnorePattern: '(?:^_)'}], // template literals default to calling toString(), but I mean what I say '@typescript-eslint/restrict-template-expressions': 'off', // I need to be able to do `while(true)`, come on yall... '@typescript-eslint/no-unnecessary-condition': ['warn', {allowConstantLoopConditions: 'always'}], // this breaks when I want to use a type parameter to allow type checking inline arguments // it's "unnecessary type parameters" or an `as` declaration, which I don't like '@typescript-eslint/no-unnecessary-type-parameters': 'off', '@typescript-eslint/no-unnecessary-type-constraint': 'off', // the breaks Promise.withResolvers(), // see https://github.com/typescript-eslint/typescript-eslint/issues/8113 '@typescript-eslint/no-invalid-void-type': 'off', // make sure the docs look good 'tsdoc/syntax': 'warn', }, }, { name: 'node files', files: ['src/server/**/*.@(js|jsx|ts|tsx)', 'src/cmd/**/*.@(js|jsx|ts|tsx)'], languageOptions: { globals: { ...globals.es2024, ...globals.node, }, }, }, { // mostly cribbed from preact's config, but that's not setup to handle eslint9 // https://github.com/preactjs/eslint-config-preact/blob/master/index.js name: 'client files', files: ['src/client/**/*.@(js|ts)', 'src/**/*.@(jsx|tsx)'], languageOptions: { globals: { ...globals.es2024, ...globals.browser, }, }, plugins: { react, reactHooks, }, settings: { react: { pragma: 'h', version: '16.0', }, }, rules: { // preact / jsx rules 'react/no-deprecated': 2, 'react/react-in-jsx-scope': 0, // handled this automatically 'react/display-name': [1, {ignoreTranspilerName: false}], 'react/jsx-no-bind': [ 1, { ignoreRefs: true, allowFunctions: true, allowArrowFunctions: true, }, ], 'react/jsx-no-comment-textnodes': 2, 'react/jsx-no-duplicate-props': 2, 'react/jsx-no-target-blank': 2, 'react/jsx-no-undef': 2, 'react/jsx-tag-spacing': [2, {beforeSelfClosing: 'always'}], 'react/jsx-uses-react': 1, // debatable 'react/jsx-uses-vars': 2, 'react/jsx-key': [2, {checkFragmentShorthand: true}], 'react/self-closing-comp': 2, 'react/prefer-es6-class': 2, 'react/prefer-stateless-function': 1, 'react/require-render-return': 2, 'react/no-danger': 1, // Legacy APIs not supported in Preact: 'react/no-did-mount-set-state': 2, 'react/no-did-update-set-state': 2, 'react/no-find-dom-node': 2, 'react/no-is-mounted': 2, 'react/no-string-refs': 2, // hooks 'reactHooks/rules-of-hooks': 2, 'reactHooks/exhaustive-deps': 1, }, }, // tests don't have jsdoc requirements { files: ['src/**/*.spec.{js,jsx}'], rules: {}, }, // json (with comments in some files) { files: ['**/*.json'], ignores: ['package-lock.json'], plugins: {json}, language: 'json/json', extends: [json.configs.recommended], }, { files: ['tsconfig.json'], language: 'json/jsonc', }, // prettier last, so it can turn everything off prettier, )