[READ-ONLY] Mirror of https://github.com/FoxxMD/multi-scrobbler. Scrobble plays from multiple sources to multiple clients
docs.multi-scrobbler.app
deezer
docker
jellyfin
koito
lastfm
listenbrainz
maloja
mopidy
mpris
music
music-assistant
plex
scrobble
self-hosted
spotify
subsonic
tautulli
youtube-music
9.9 kB
254 lines
1// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format
2import storybook from "eslint-plugin-storybook";
3import boundaries from 'eslint-plugin-boundaries';
4import unusedImports from 'eslint-plugin-unused-imports';
5import { importX } from 'eslint-plugin-import-x'
6
7import { defineConfig, globalIgnores } from "eslint/config";
8
9// @ts-check
10
11import js from '@eslint/js';
12import globals from 'globals';
13import tsEslint from 'typescript-eslint';
14import arrow from 'eslint-plugin-prefer-arrow-functions';
15import hooks from 'eslint-plugin-react-hooks';
16import mochaPlugin from 'eslint-plugin-mocha';
17import unicorn from 'eslint-plugin-unicorn';
18
19const defaultRules = {
20 'no-useless-catch': 'off',
21 '@typescript-eslint/no-unused-vars': 'warn',
22 'no-unused-vars': [
23 'warn',
24 {
25 args: 'none',
26 caughtErrors: 'none',
27 destructuredArrayIgnorePattern: "^_",
28 "argsIgnorePattern": "^_",
29 ignoreRestSiblings: true,
30 ignoreUsingDeclarations: true
31 }
32 ],
33 "@typescript-eslint/no-unused-vars": [
34 "warn",
35 {
36 args: 'none',
37 caughtErrors: 'none',
38 destructuredArrayIgnorePattern: "^_",
39 "argsIgnorePattern": "^_",
40 ignoreRestSiblings: true,
41 ignoreUsingDeclarations: true
42 }
43 ],
44 "prefer-arrow-functions/prefer-arrow-functions": [
45 "warn",
46 {
47 "allowNamedFunctions": false,
48 "classPropertiesAllowed": false,
49 "disallowPrototype": false,
50 "returnStyle": "unchanged",
51 "singleReturnOnly": false
52 }
53 ],
54 "arrow-body-style": ["warn", "as-needed"],
55 "@typescript-eslint/no-explicit-any": "warn",
56 "@typescript-eslint/no-empty-object-type": [
57 "warn",
58 {
59 "allowInterfaces": 'with-single-extends'
60 }
61 ]
62};
63
64export default defineConfig([
65 globalIgnores([
66 'docsite/build',
67 'docsite/.docusaurus',
68 'public/mockServiceWorker.js',
69 'dist'
70 ]),
71 {
72 plugins: {
73 "prefer-arrow-functions": arrow,
74 js,
75 'unused-imports': unusedImports,
76 unicorn,
77 'import-x': importX,
78 },
79 rules: {
80 ...defaultRules,
81 // https://typescript-eslint.io/rules/consistent-type-imports/#comparison-with-importsnotusedasvalues--verbatimmodulesyntax
82 //
83 // when using tsconfig compiler verbatimModuleSyntax and EX import {type Foo, type Bar} from 'a';
84 // true => typescript will *still* import a module if all types are inline
85 // false => typescript will erase the entire module import
86 //
87 // we need to use verbatimModuleSyntax: true for nodejs type stripping compatibility so
88 // its important that we use top-level type imports so we don't accidentally import server-side modules into frontend
89 // but can still use types where necessary
90 //
91 // this rule *should* do this...it does detect imports that are typed but not explicitly declared
92 // but its not moving inline -> top-level
93 "@typescript-eslint/consistent-type-imports": [
94 "error",
95 {
96 prefer: 'type-imports',
97 fixStyle: "separate-type-imports"
98 }
99 ],
100 // however, import/consistent-type-specifier-style from eslint-plugin-import *does* move inline => top-level
101 // but it does not yet support eslint10 :(
102 //
103 // TODO eventually add this once plugin-import supports eslint10
104 // https://github.com/import-js/eslint-plugin-import
105 // https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/consistent-type-specifier-style.md
106 // -- this enforces that type-only imports are fixed to top-level type imports IE
107 // import {type Foo, type Bar} from 'a'; => import type { Foo, Bar} from 'a';
108 //
109 // USE eslint-plugin-import-x since its more actively developed?
110 // waiting on the status of this for supporting mixed inline types
111 // https://github.com/un-ts/eslint-plugin-import-x/issues/501
112 // "import-x/consistent-type-specifier-style": [
113 // "error", "prefer-top-level"
114 // ],
115 "unicorn/prefer-then-catch": "error",
116 "unicorn/consistent-destructuring": "warn",
117 "unicorn/consistent-function-scoping": "warn",
118 "unicorn/consistent-optional-chaining": "error",
119 "unicorn/no-array-callback-reference": "warn",
120 "unicorn/no-accidental-bitwise-operator": "warn",
121 "no-obj-calls": "error",
122 "unicorn/new-for-builtins": "error",
123 "unicorn/no-impossible-length-comparison": "error",
124 "unicorn/no-duplicate-loops": "warn",
125 "unicorn/no-duplicate-logical-operands": "warn",
126 "unicorn/no-declarations-before-early-exit": "warn",
127 "unicorn/prefer-negative-index": "warn",
128 "unicorn/prefer-import-meta-properties": "error",
129 "unicorn/prefer-array-from-async": "warn",
130 "unicorn/prefer-array-flat-map": "warn",
131 "unicorn/no-useless-else": "warn",
132 "unicorn/no-unused-array-method-return": "error",
133 //"unicorn/no-unreadable-object-destructuring": "warn",
134 "unicorn/prefer-object-destructuring-defaults": "warn"
135 },
136 extends: [
137 tsEslint.configs.recommended,
138 "js/recommended"
139 ],
140 languageOptions: {
141 globals: {
142 ...globals.node,
143 },
144 ecmaVersion: 'latest',
145 sourceType: 'module',
146 },
147 files: ['src/**/*.ts','src/**/*.tsx']
148 },
149 {
150 extends: [
151 storybook.configs["flat/recommended"],
152 ],
153 files: ['src/client/stories/**/*.tsx'],
154 },
155 {
156 extends: [
157 hooks.configs.flat.recommended,
158 ],
159 languageOptions: {
160 globals: {
161 ...globals.browser,
162 }
163 },
164 files: ['src/client/**/*.tsx'],
165 },
166 {
167 extends: [
168 mochaPlugin.configs.recommended,
169 ],
170 languageOptions: {
171 globals: {
172 ...globals.node,
173 }
174 },
175 files: ['src/backend/tests/**/*.ts'],
176 rules: {
177 ...defaultRules,
178 "prefer-arrow-functions/prefer-arrow-functions": ["off"],
179 "@typescript-eslint/no-unused-expressions": 'off',
180 'mocha/max-top-level-suites': 'off'
181 },
182 },
183 {
184 // https://typescript-eslint.io/troubleshooting/faqs/eslint#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors
185 files: ['**/*.{ts,tsx,mts,cts}'],
186 plugins: {
187 'unused-imports': unusedImports
188 },
189 rules: {
190 'no-undef': 'off',
191 'unused-imports/no-unused-imports': 'error'
192 }
193 },
194 // this ruleset helps keep backend, frontend, and core keep imports isolated
195 // in order to prevent frontend (vite) from accidentally bundling backend files + backend packages when importing directly (backend) or transitively (through core)
196 //
197 // folder (module?) boundaries should be like
198 //
199 // backend <-- all node/server side code
200 // core <-- shared types, utils, and logic between backend and client
201 // client <-- frontend code to be bundled by vite, SHOULD NOT import directly/transitively from backend
202 {
203 files: ['src/**/*.{ts,tsx}'],
204 plugins: { boundaries },
205 settings: {
206 // Define your three architectural layers
207 'boundaries/elements': [
208 { type: 'frontend', mode: 'file', pattern: 'src/client/**/*' },
209 { type: 'config', mode: 'file', pattern: 'config/*.example' },
210 { type: 'core', mode: 'file', pattern: ['src/core/!(tests)/**','src/core/!(tests)'] },
211 { type: 'core-tests', mode: 'file', pattern: ['src/core/tests/**'] },
212 { type: 'backend', mode: 'file', pattern: 'src/backend/**/*' },
213 { type: 'stories', mode: 'file', pattern: ['src/stories/**/*', '.storybook/**'] },
214 ],
215 // So it understands TS path aliases when resolving imports
216 'import/resolver': {
217 typescript: true,
218 },
219 },
220 rules: {
221 'boundaries/element-types': [
222 'warn',
223 {
224 default: 'disallow', // deny anything not explicitly allowed
225 rules: [
226 {
227 from: 'frontend',
228 allow: ['frontend', 'core'], // frontend can use itself + core, never backend
229 },
230 {
231 from: 'core',
232 allow: ['core'], // core can only use itself — never backend, never frontend
233 },
234 {
235 from: 'backend',
236 allow: ['backend', 'core', 'config', 'core-tests'], // backend can use itself + core + tests
237 },
238 {
239 from: 'stories',
240 allow: ['stories', 'core', 'frontend', 'core-tests'], // backend can use itself + core
241 },
242 {
243 from: 'core-tests',
244 allow: ['core', 'core-tests'], // backend can use itself + core
245 },
246 ],
247 },
248 ],
249 // Optional: flag any file under src/ that doesn't match one of the
250 // three element patterns above (catches stray/misplaced files)
251 'boundaries/no-unknown': 'warn'
252 },
253 }
254]);