offline-first, p2p synced, atproto enabled, feed reader
at main 6.0 kB view raw
1import path from 'node:path' 2 3import {includeIgnoreFile} from '@eslint/compat' 4import js from '@eslint/js' 5import json from '@eslint/json' 6import restrictedGlobals from 'confusing-browser-globals' 7import {defineConfig} from 'eslint/config' 8import {importX} from 'eslint-plugin-import-x' 9import prettier from 'eslint-plugin-prettier/recommended' 10import solid from 'eslint-plugin-solid/configs/typescript' 11import globals from 'globals' 12import tseslint from 'typescript-eslint' 13 14const gitignore = path.resolve(import.meta.dirname, '.gitignore') 15 16export default defineConfig( 17 includeIgnoreFile(gitignore, '.gitignore'), 18 19 // all files by default get shared globals 20 { 21 name: 'javascript basics', 22 files: ['**/*.@(js|jsx|ts|tsx)'], 23 extends: [js.configs.recommended], 24 25 languageOptions: { 26 globals: { 27 ...globals.es2024, 28 ...globals['shared-node-browser'], 29 }, 30 }, 31 32 rules: { 33 // eg, `open` is a global, but probably not really intended that way in our code 34 'no-restricted-globals': ['error', ...restrictedGlobals], 35 'no-unused-vars': ['warn', {varsIgnorePattern: '(?:^_)'}], 36 }, 37 }, 38 39 { 40 name: 'typescript basics', 41 files: ['**/*.@(ts|tsx)'], 42 extends: [tseslint.configs.strictTypeChecked], 43 44 languageOptions: { 45 parserOptions: { 46 projectService: true, 47 tsconfigRootDir: import.meta.dirname, 48 }, 49 }, 50 rules: { 51 // allow leading underscore for marking unused vars 52 '@typescript-eslint/no-unused-vars': ['warn', {varsIgnorePattern: '(?:^_)'}], 53 54 // template literals default to calling toString(), but I mean what I say 55 '@typescript-eslint/restrict-template-expressions': 'off', 56 57 // I need to be able to do `while(true)`, come on yall... 58 '@typescript-eslint/no-unnecessary-condition': ['warn', {allowConstantLoopConditions: 'always'}], 59 60 // this breaks when I want to use a type parameter to allow type checking inline arguments 61 // it's "unnecessary type parameters" or an `as` declaration, which I don't like 62 '@typescript-eslint/no-unnecessary-type-parameters': 'off', 63 '@typescript-eslint/no-unnecessary-type-constraint': 'off', 64 65 // the breaks Promise.withResolvers<void>(), 66 // see https://github.com/typescript-eslint/typescript-eslint/issues/8113 67 '@typescript-eslint/no-invalid-void-type': 'off', 68 }, 69 }, 70 71 // import organization and validation 72 { 73 name: 'import rules', 74 files: ['**/*.@(js|jsx|ts|tsx)'], 75 plugins: {'import-x': importX}, 76 rules: { 77 // no file extensions on imports (TypeScript handles resolution) 78 'import-x/extensions': ['error', 'never'], 79 80 // merge duplicate imports from same module 81 'import-x/no-duplicates': 'warn', 82 83 // all imports at top of file 84 'import-x/first': 'error', 85 86 // blank line after import block 87 'import-x/newline-after-import': 'error', 88 89 // prevent importing a module from itself 90 'import-x/no-self-import': 'error', 91 92 // consistent import ordering: 93 // 1. node builtins (node:fs, etc) 94 // 2. external packages (zod, solid-js, etc) 95 // 3. internal aliases (#lib, #realm, #feedline, #app, #spec) 96 // 4. relative imports 97 'import-x/order': [ 98 'warn', 99 { 100 groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'], 101 pathGroups: [ 102 {pattern: '#lib/**', group: 'internal', position: 'before'}, 103 {pattern: '#realm/**', group: 'internal', position: 'before'}, 104 {pattern: '#feedline/**', group: 'internal', position: 'before'}, 105 {pattern: '#app/**', group: 'internal', position: 'before'}, 106 {pattern: '#spec/**', group: 'internal', position: 'after'}, 107 ], 108 pathGroupsExcludedImportTypes: ['builtin'], 109 'newlines-between': 'always', 110 alphabetize: {order: 'asc', caseInsensitive: true}, 111 }, 112 ], 113 114 // detect circular dependencies (can slow down linting on large codebases) 115 // set to warn since cycles in realm/feedline could cause subtle sync bugs 116 'import-x/no-cycle': ['warn', {maxDepth: 4}], 117 118 // enforce zod/mini over zod or zod/v4 for bundle size 119 'no-restricted-imports': [ 120 'error', 121 { 122 paths: [ 123 {name: 'zod', message: "Use 'zod/mini' instead for smaller bundle size."}, 124 {name: 'zod/v4', message: "Use 'zod/mini' instead for smaller bundle size."}, 125 ], 126 }, 127 ], 128 }, 129 }, 130 131 { 132 name: 'node files', 133 files: ['src/server/**/*.@(js|jsx|ts|tsx)', 'src/cmd/**/*.@(js|jsx|ts|tsx)'], 134 languageOptions: { 135 globals: { 136 ...globals.es2024, 137 ...globals.node, 138 }, 139 }, 140 }, 141 142 { 143 name: 'client files', 144 files: ['src/**/client/**/*.@(js|ts)', 'src/feedline/**/*.@(js|ts)', 'src/**/*.@(jsx|tsx)'], 145 ...solid, 146 languageOptions: { 147 globals: { 148 ...globals.es2024, 149 ...globals.browser, 150 }, 151 }, 152 rules: { 153 // our custom reactive query helpers 154 'solid/reactivity': ['warn', {customReactiveFunctions: ['makeSignalQuery', 'makeStoreQuery']}], 155 }, 156 }, 157 158 // tests don't have jsdoc requirements, and looser any/null assertion restrictions 159 { 160 files: ['src/**/*.spec.{js,jsx,ts,tsx}', 'src/spec/**/*.{js,ts,jsx,tsx}'], 161 rules: { 162 '@typescript-eslint/no-explicit-any': 'off', 163 '@typescript-eslint/no-unsafe-assignment': 'off', 164 '@typescript-eslint/no-unsafe-argument': 'off', 165 '@typescript-eslint/no-unsafe-member-access': 'off', 166 '@typescript-eslint/no-unsafe-return': 'off', 167 '@typescript-eslint/no-non-null-assertion': 'off', 168 }, 169 }, 170 171 // json (with comments in some files) 172 { 173 files: ['**/*.json'], 174 ignores: ['package-lock.json'], 175 176 plugins: {json}, 177 language: 'json/json', 178 extends: [json.configs.recommended], 179 }, 180 { 181 files: ['tsconfig.json'], 182 language: 'json/jsonc', 183 }, 184 185 // prettier last, so it can turn everything off 186 prettier, 187)