Pop-up dictionary browser extension for language learning. Successor to Yomichan. (PERSONAL FORK)
1/*
2 * Copyright (C) 2024-2025 Yomitan Authors
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18import {FlatCompat} from '@eslint/eslintrc';
19import js from '@eslint/js';
20import stylistic from '@stylistic/eslint-plugin';
21import vitest from '@vitest/eslint-plugin';
22import esbuild from 'esbuild';
23import header from 'eslint-plugin-header';
24// @ts-expect-error - Missing types https://github.com/import-js/eslint-plugin-import/issues/3133
25import importPlugin from 'eslint-plugin-import';
26import jsdoc from 'eslint-plugin-jsdoc';
27import jsonc from 'eslint-plugin-jsonc';
28import noUnsanitized from 'eslint-plugin-no-unsanitized';
29import sonarjs from 'eslint-plugin-sonarjs';
30import unicorn from 'eslint-plugin-unicorn';
31import unusedImports from 'eslint-plugin-unused-imports';
32import globals from 'globals';
33import parser from 'jsonc-eslint-parser';
34import path from 'node:path';
35import {fileURLToPath} from 'node:url';
36import typescriptEslint from 'typescript-eslint';
37
38const compat = new FlatCompat({
39 baseDirectory: path.dirname(fileURLToPath(import.meta.url)),
40 recommendedConfig: js.configs.recommended,
41 allConfig: js.configs.all,
42});
43
44// @ts-expect-error - This is a workaround https://github.com/Stuk/eslint-plugin-header/issues/57
45header.rules.header.meta.schema = false;
46
47/**
48 * @param {string[]} scriptPaths
49 * @returns {Promise<string[]>}
50 */
51async function getDependencies(scriptPaths) {
52 const v = await esbuild.build({
53 entryPoints: scriptPaths,
54 bundle: true,
55 minify: false,
56 sourcemap: true,
57 target: 'es2022',
58 format: 'esm',
59 write: false,
60 metafile: true,
61 });
62 const dependencies = Object.keys(v.metafile.inputs);
63 const stringComparer = new Intl.Collator('en-US'); // Invariant locale
64 dependencies.sort((a, b) => stringComparer.compare(a, b));
65 return dependencies;
66}
67
68/**
69 * @type {import('eslint').Linter.Config[]}
70 */
71export default [
72 {
73 ignores: ['ext/lib/', 'dev/lib/handlebars/', '**/node_modules/', '**/builds/', 'test-results/'],
74 },
75 ...compat.extends(
76 'eslint:recommended',
77 'plugin:jsonc/recommended-with-json',
78 'plugin:eslint-comments/recommended',
79 ),
80 {
81 plugins: {
82 'no-unsanitized': noUnsanitized,
83 header,
84 jsdoc,
85 jsonc,
86 'unused-imports': unusedImports,
87 '@typescript-eslint': typescriptEslint.plugin,
88 '@stylistic': stylistic,
89 unicorn,
90 sonarjs,
91 'import': importPlugin,
92 },
93
94 languageOptions: {
95 globals: {
96 ...globals.browser,
97 ...globals.webextensions,
98 },
99
100 parser: typescriptEslint.parser,
101 ecmaVersion: 2022,
102 sourceType: 'module',
103
104 parserOptions: {
105 ecmaFeatures: {
106 globalReturn: false,
107 impliedStrict: true,
108 },
109
110 project: [
111 './jsconfig.json',
112 './dev/jsconfig.json',
113 './test/jsconfig.json',
114 './benches/jsconfig.json',
115 ],
116 },
117 },
118
119 rules: {
120 'accessor-pairs': 'error',
121 'curly': ['error', 'all'],
122 'default-case-last': 'error',
123 'dot-notation': 'error',
124 'eqeqeq': 'error',
125 'func-names': ['error', 'always'],
126 'guard-for-in': 'error',
127 'grouped-accessor-pairs': 'error',
128 'new-cap': 'error',
129 'no-alert': 'error',
130 'no-case-declarations': 'error',
131 'no-caller': 'error',
132 'no-const-assign': 'error',
133
134 'no-constant-condition': ['error', {
135 checkLoops: false,
136 }],
137
138 'no-constructor-return': 'error',
139 'no-duplicate-imports': 'error',
140 'no-eval': 'error',
141 'no-extend-native': 'error',
142 'no-global-assign': 'error',
143 'no-implicit-globals': 'error',
144 'no-implied-eval': 'error',
145 'no-new': 'error',
146 'no-new-native-nonconstructor': 'error',
147 'no-octal': 'error',
148 'no-octal-escape': 'error',
149 'no-param-reassign': 'off',
150 'no-promise-executor-return': 'error',
151 'no-prototype-builtins': 'error',
152
153 'no-restricted-syntax': ['error', {
154 message: 'Avoid using JSON.parse(), prefer parseJson.',
155 selector: 'MemberExpression[object.name=JSON][property.name=parse]',
156 }, {
157 message: 'Avoid using Response.json(), prefer readResponseJson.',
158 selector: 'MemberExpression[property.name=json]',
159 }, {
160 message: 'Avoid using performance, prefer safePerformance.',
161 selector: 'MemberExpression[object.name=performance]',
162 }],
163
164 'no-self-compare': 'error',
165 'no-sequences': 'error',
166
167 'no-shadow': ['error', {
168 builtinGlobals: false,
169 }],
170
171 'no-shadow-restricted-names': 'error',
172 'no-template-curly-in-string': 'error',
173 'no-undef': 'error',
174 'no-undefined': 'error',
175
176 'no-underscore-dangle': ['error', {
177 allowAfterThis: true,
178 allowAfterSuper: false,
179 allowAfterThisConstructor: false,
180 }],
181
182 'no-unexpected-multiline': 'error',
183 'no-unneeded-ternary': 'error',
184
185 'no-unused-vars': ['error', {
186 vars: 'local',
187 args: 'after-used',
188 argsIgnorePattern: '^_',
189 caughtErrors: 'none',
190 }],
191
192 'no-unused-expressions': 'error',
193 'no-var': 'error',
194 'no-with': 'error',
195
196 'prefer-const': ['error', {
197 destructuring: 'all',
198 }],
199
200 'radix': 'error',
201 'require-atomic-updates': 'off',
202 'sort-imports': 'off',
203 'yoda': ['error', 'never'],
204 '@stylistic/array-bracket-newline': ['error', 'consistent'],
205 '@stylistic/array-bracket-spacing': ['error', 'never'],
206 '@stylistic/array-element-newline': ['error', 'consistent'],
207 '@stylistic/arrow-parens': ['error', 'always'],
208
209 '@stylistic/arrow-spacing': ['error', {
210 before: true,
211 after: true,
212 }],
213
214 '@stylistic/block-spacing': ['error', 'always'],
215
216 '@stylistic/brace-style': ['error', '1tbs', {
217 allowSingleLine: true,
218 }],
219
220 '@stylistic/comma-dangle': ['error', 'always-multiline'],
221
222 '@stylistic/comma-spacing': ['error', {
223 before: false,
224 after: true,
225 }],
226
227 '@stylistic/comma-style': ['error', 'last'],
228 '@stylistic/computed-property-spacing': ['error', 'never'],
229 '@stylistic/dot-location': ['error', 'property'],
230 '@stylistic/eol-last': ['error', 'always'],
231 '@stylistic/func-call-spacing': ['error', 'never'],
232 '@stylistic/function-call-argument-newline': ['error', 'consistent'],
233 '@stylistic/function-call-spacing': ['error', 'never'],
234 '@stylistic/function-paren-newline': ['error', 'multiline-arguments'],
235 '@stylistic/generator-star-spacing': ['error', 'before'],
236 '@stylistic/implicit-arrow-linebreak': ['error', 'beside'],
237
238 '@stylistic/indent': ['error', 4, {
239 SwitchCase: 1,
240 MemberExpression: 1,
241 flatTernaryExpressions: true,
242 ignoredNodes: ['ConditionalExpression'],
243 }],
244
245 '@stylistic/indent-binary-ops': ['error', 0],
246
247 '@stylistic/key-spacing': ['error', {
248 beforeColon: false,
249 afterColon: true,
250 mode: 'strict',
251 }],
252
253 '@stylistic/keyword-spacing': ['error', {
254 before: true,
255 after: true,
256 }],
257
258 '@stylistic/linebreak-style': ['error', 'unix'],
259 '@stylistic/lines-around-comment': 'off',
260
261 '@stylistic/lines-between-class-members': ['error', 'always', {
262 exceptAfterSingleLine: true,
263 }],
264
265 '@stylistic/max-len': 'off',
266
267 '@stylistic/max-statements-per-line': ['error', {
268 max: 2,
269 }],
270
271 '@stylistic/member-delimiter-style': ['error', {
272 multiline: {
273 delimiter: 'semi',
274 requireLast: true,
275 },
276
277 singleline: {
278 delimiter: 'comma',
279 requireLast: false,
280 },
281
282 multilineDetection: 'brackets',
283 }],
284
285 '@stylistic/multiline-ternary': ['error', 'always-multiline'],
286 '@stylistic/new-parens': 'error',
287
288 '@stylistic/newline-per-chained-call': ['error', {
289 ignoreChainWithDepth: 3,
290 }],
291
292 '@stylistic/no-confusing-arrow': 'error',
293 '@stylistic/no-extra-parens': 'off',
294 '@stylistic/no-extra-semi': 'error',
295 '@stylistic/no-floating-decimal': 'error',
296
297 '@stylistic/no-mixed-operators': ['error', {
298 allowSamePrecedence: true,
299 groups: [['&&', '||']],
300 }],
301
302 '@stylistic/no-mixed-spaces-and-tabs': 'error',
303 '@stylistic/no-multi-spaces': 'error',
304
305 '@stylistic/no-multiple-empty-lines': ['error', {
306 max: 2,
307 maxEOF: 0,
308 maxBOF: 0,
309 }],
310
311 '@stylistic/no-tabs': 'error',
312 '@stylistic/no-trailing-spaces': 'error',
313 '@stylistic/no-whitespace-before-property': 'error',
314 '@stylistic/nonblock-statement-body-position': ['error', 'beside'],
315 '@stylistic/object-curly-newline': 'error',
316 '@stylistic/object-curly-spacing': ['error', 'never'],
317
318 '@stylistic/object-property-newline': ['error', {
319 allowAllPropertiesOnSameLine: true,
320 }],
321
322 '@stylistic/one-var-declaration-per-line': ['error', 'initializations'],
323 '@stylistic/operator-linebreak': ['error', 'after'],
324 '@stylistic/padded-blocks': ['error', 'never'],
325
326 '@stylistic/padding-line-between-statements': ['error', {
327 blankLine: 'always',
328 prev: '*',
329 next: 'import',
330 }, {
331 blankLine: 'always',
332 prev: 'import',
333 next: '*',
334 }, {
335 blankLine: 'always',
336 prev: '*',
337 next: 'export',
338 }, {
339 blankLine: 'always',
340 prev: 'import',
341 next: 'let',
342 }, {
343 blankLine: 'always',
344 prev: 'import',
345 next: 'const',
346 }, {
347 blankLine: 'always',
348 prev: 'export',
349 next: 'let',
350 }, {
351 blankLine: 'always',
352 prev: 'export',
353 next: 'const',
354 }, {
355 blankLine: 'always',
356 prev: 'export',
357 next: 'export',
358 }, {
359 blankLine: 'always',
360 prev: 'export',
361 next: 'type',
362 }, {
363 blankLine: 'always',
364 prev: 'type',
365 next: 'export',
366 }, {
367 blankLine: 'always',
368 prev: 'type',
369 next: 'type',
370 }, {
371 blankLine: 'never',
372 prev: 'import',
373 next: 'import',
374 }],
375
376 '@stylistic/quote-props': ['error', 'consistent-as-needed', {
377 numbers: true,
378 }],
379
380 '@stylistic/quotes': ['error', 'single', 'avoid-escape'],
381 '@stylistic/rest-spread-spacing': ['error', 'never'],
382 '@stylistic/semi': 'error',
383
384 '@stylistic/semi-spacing': ['error', {
385 before: false,
386 after: true,
387 }],
388
389 '@stylistic/semi-style': ['error', 'last'],
390 '@stylistic/space-before-blocks': ['error', 'always'],
391
392 '@stylistic/space-before-function-paren': ['error', {
393 anonymous: 'never',
394 named: 'never',
395 asyncArrow: 'always',
396 }],
397
398 '@stylistic/space-in-parens': ['error', 'never'],
399
400 '@stylistic/space-infix-ops': ['error', {
401 int32Hint: false,
402 }],
403
404 '@stylistic/space-unary-ops': 'error',
405 '@stylistic/spaced-comment': ['error', 'always'],
406
407 '@stylistic/switch-colon-spacing': ['error', {
408 after: true,
409 before: false,
410 }],
411
412 '@stylistic/template-curly-spacing': ['error', 'never'],
413 '@stylistic/template-tag-spacing': ['error', 'never'],
414
415 '@stylistic/type-annotation-spacing': ['error', {
416 before: false,
417 after: true,
418
419 overrides: {
420 arrow: {
421 before: true,
422 after: true,
423 },
424 },
425 }],
426
427 '@stylistic/type-generic-spacing': 'error',
428 '@stylistic/type-named-tuple-spacing': 'error',
429 '@stylistic/wrap-iife': ['error', 'inside'],
430 '@stylistic/wrap-regex': 'off',
431
432 '@stylistic/yield-star-spacing': ['error', {
433 before: true,
434 after: false,
435 }],
436
437 'no-unsanitized/method': 'error',
438 'no-unsanitized/property': 'error',
439 'jsdoc/check-access': 'error',
440 'jsdoc/check-alignment': 'error',
441
442 'jsdoc/check-line-alignment': ['error', 'never', {
443 wrapIndent: ' ',
444 }],
445
446 'jsdoc/check-param-names': 'error',
447 'jsdoc/check-property-names': 'error',
448 'jsdoc/check-tag-names': 'error',
449 'jsdoc/empty-tags': 'error',
450 'jsdoc/check-types': 'error',
451 'jsdoc/check-values': 'error',
452 'jsdoc/implements-on-classes': 'error',
453 'jsdoc/multiline-blocks': 'error',
454 'jsdoc/no-bad-blocks': 'error',
455 'jsdoc/no-multi-asterisks': 'error',
456 'jsdoc/no-undefined-types': 'error',
457 'jsdoc/require-asterisk-prefix': 'error',
458 'jsdoc/require-description': 'off',
459 'jsdoc/require-hyphen-before-param-description': ['error', 'never'],
460
461 'jsdoc/require-jsdoc': ['error', {
462 require: {
463 ClassDeclaration: false,
464 FunctionDeclaration: true,
465 MethodDefinition: false,
466 },
467
468 contexts: [
469 'MethodDefinition[kind=constructor]>FunctionExpression>BlockStatement>ExpressionStatement>AssignmentExpression[left.object.type=ThisExpression]',
470 'ClassDeclaration>Classbody>PropertyDefinition',
471 'MethodDefinition[kind!=constructor][kind!=set]',
472 'MethodDefinition[kind=constructor][value.params.length>0]',
473 ],
474
475 checkGetters: 'no-setter',
476 checkSetters: 'no-getter',
477 }],
478
479 'jsdoc/require-param': 'error',
480 'jsdoc/require-param-description': 'off',
481 'jsdoc/require-param-name': 'error',
482 'jsdoc/require-param-type': 'error',
483 'jsdoc/require-property': 'error',
484 'jsdoc/require-property-description': 'off',
485 'jsdoc/require-property-name': 'error',
486 'jsdoc/require-property-type': 'error',
487 'jsdoc/require-returns': 'error',
488 'jsdoc/require-returns-check': 'error',
489 'jsdoc/require-returns-description': 'off',
490 'jsdoc/require-returns-type': 'error',
491 'jsdoc/require-throws': 'error',
492 'jsdoc/require-yields': 'error',
493 'jsdoc/require-yields-check': 'error',
494
495 'jsdoc/tag-lines': ['error', 'never', {
496 startLines: 0,
497 }],
498
499 'jsdoc/valid-types': 'error',
500 'jsonc/indent': ['error', 4],
501 'jsonc/array-bracket-newline': ['error', 'consistent'],
502 'jsonc/array-bracket-spacing': ['error', 'never'],
503 'jsonc/array-element-newline': ['error', 'consistent'],
504 'jsonc/comma-style': ['error', 'last'],
505
506 'jsonc/key-spacing': ['error', {
507 beforeColon: false,
508 afterColon: true,
509 mode: 'strict',
510 }],
511
512 'jsonc/no-octal-escape': 'error',
513
514 'jsonc/object-curly-newline': ['error', {
515 consistent: true,
516 }],
517
518 'jsonc/object-curly-spacing': ['error', 'never'],
519
520 'jsonc/object-property-newline': ['error', {
521 allowAllPropertiesOnSameLine: true,
522 }],
523
524 'eslint-comments/no-unused-disable': 'error',
525 'unused-imports/no-unused-imports': 'error',
526 'import/extensions': ['error', 'ignorePackages'],
527
528 'unicorn/catch-error-name': ['error', {
529 ignore: ['^(e|error2?)$'],
530 }],
531
532 'unicorn/custom-error-definition': 'error',
533 'unicorn/empty-brace-spaces': 'error',
534 'unicorn/error-message': 'error',
535 'unicorn/expiring-todo-comments': 'error',
536 'unicorn/explicit-length-check': 'error',
537 'unicorn/new-for-builtins': 'error',
538 'unicorn/no-abusive-eslint-disable': 'error',
539 'unicorn/no-array-for-each': 'error',
540 'unicorn/no-array-method-this-argument': 'error',
541 'unicorn/no-array-push-push': 'error',
542 'unicorn/no-array-reduce': 'error',
543 'unicorn/no-console-spaces': 'error',
544 'unicorn/no-document-cookie': 'error',
545 'unicorn/no-empty-file': 'error',
546 'unicorn/no-hex-escape': 'error',
547 'unicorn/no-instanceof-array': 'error',
548 'unicorn/no-invalid-remove-event-listener': 'error',
549 'unicorn/no-lonely-if': 'error',
550 'unicorn/no-nested-ternary': 'error',
551 'unicorn/no-new-buffer': 'error',
552 'unicorn/no-object-as-default-parameter': 'error',
553 'unicorn/no-static-only-class': 'error',
554 'unicorn/no-thenable': 'error',
555 'unicorn/no-unnecessary-await': 'error',
556 'unicorn/no-unnecessary-polyfills': 'error',
557 'unicorn/no-unreadable-array-destructuring': 'error',
558 'unicorn/no-unreadable-iife': 'error',
559 'unicorn/no-useless-fallback-in-spread': 'error',
560 'unicorn/no-useless-length-check': 'error',
561 'unicorn/no-useless-promise-resolve-reject': 'error',
562 'unicorn/no-useless-spread': 'error',
563 'unicorn/no-useless-switch-case': 'error',
564 'unicorn/no-useless-undefined': 'error',
565 'unicorn/no-zero-fractions': 'error',
566 'unicorn/prefer-array-find': 'error',
567 'unicorn/prefer-array-flat': 'error',
568 'unicorn/prefer-array-flat-map': 'error',
569 'unicorn/prefer-array-index-of': 'error',
570 'unicorn/prefer-array-some': 'error',
571 'unicorn/prefer-date-now': 'error',
572 'unicorn/prefer-default-parameters': 'error',
573 'unicorn/prefer-dom-node-dataset': 'error',
574 'unicorn/prefer-dom-node-text-content': 'error',
575 'unicorn/prefer-event-target': 'error',
576 'unicorn/prefer-export-from': 'error',
577 'unicorn/prefer-includes': 'error',
578 'unicorn/prefer-keyboard-event-key': 'error',
579 'unicorn/prefer-logical-operator-over-ternary': 'error',
580 'unicorn/prefer-modern-math-apis': 'error',
581 'unicorn/prefer-module': 'error',
582 'unicorn/prefer-native-coercion-functions': 'error',
583 'unicorn/prefer-negative-index': 'error',
584 'unicorn/prefer-number-properties': 'error',
585 'unicorn/prefer-object-from-entries': 'error',
586 'unicorn/prefer-prototype-methods': 'error',
587 'unicorn/prefer-reflect-apply': 'error',
588 'unicorn/prefer-regexp-test': 'error',
589 'unicorn/prefer-set-has': 'error',
590 'unicorn/prefer-set-size': 'error',
591 'unicorn/prefer-spread': 'error',
592 'unicorn/prefer-string-starts-ends-with': 'error',
593 'unicorn/prefer-string-trim-start-end': 'error',
594 'unicorn/prefer-switch': 'error',
595 'unicorn/prefer-ternary': 'error',
596 'unicorn/relative-url-style': 'error',
597 'unicorn/require-array-join-separator': 'error',
598 'unicorn/require-number-to-fixed-digits-argument': 'error',
599 'unicorn/template-indent': 'error',
600 'unicorn/throw-new-error': 'error',
601 'sonarjs/max-switch-cases': 'error',
602 'sonarjs/no-all-duplicated-branches': 'error',
603 'sonarjs/no-collapsible-if': 'error',
604 'sonarjs/no-collection-size-mischeck': 'error',
605 'sonarjs/no-duplicated-branches': 'error',
606 'sonarjs/no-element-overwrite': 'error',
607 'sonarjs/no-empty-collection': 'error',
608 'sonarjs/no-extra-arguments': 'error',
609 'sonarjs/no-gratuitous-expressions': 'error',
610 'sonarjs/no-identical-conditions': 'error',
611 'sonarjs/no-identical-expressions': 'error',
612 'sonarjs/no-identical-functions': 'error',
613 'sonarjs/no-ignored-return': 'error',
614 'sonarjs/no-inverted-boolean-check': 'error',
615 'sonarjs/no-one-iteration-loop': 'error',
616 'sonarjs/no-redundant-boolean': 'error',
617 'sonarjs/no-redundant-jump': 'error',
618 'sonarjs/no-same-line-conditional': 'error',
619 'sonarjs/no-unused-collection': 'error',
620 'sonarjs/no-use-of-empty-return-value': 'error',
621 'sonarjs/no-useless-catch': 'error',
622 'sonarjs/non-existent-operator': 'error',
623 'sonarjs/prefer-immediate-return': 'error',
624 'sonarjs/prefer-object-literal': 'error',
625 'sonarjs/prefer-single-boolean-return': 'error',
626 'sonarjs/prefer-while': 'error',
627 },
628 },
629 ...typescriptEslint.configs.recommendedTypeChecked.map((config) => ({
630 ...config,
631 files: [
632 '**/*.js',
633 '**/*.ts',
634 ],
635 })),
636 {
637 files: [
638 '**/*.js',
639 '**/*.ts',
640 ],
641
642 rules: {
643 '@typescript-eslint/no-floating-promises': ['error', {
644 ignoreIIFE: true,
645 }],
646
647 '@typescript-eslint/no-misused-promises': 'off',
648 '@typescript-eslint/no-redundant-type-constituents': 'error',
649 '@typescript-eslint/no-unsafe-argument': 'error',
650 '@typescript-eslint/no-unsafe-assignment': 'error',
651 '@typescript-eslint/no-unsafe-call': 'off',
652 '@typescript-eslint/no-unsafe-enum-comparison': 'off',
653 '@typescript-eslint/no-unsafe-member-access': 'off',
654 '@typescript-eslint/no-unsafe-return': 'off',
655 '@typescript-eslint/require-await': 'off',
656 '@typescript-eslint/restrict-template-expressions': 'off',
657 '@typescript-eslint/prefer-promise-reject-errors': 'off',
658
659 '@typescript-eslint/ban-ts-comment': ['error', {
660 'ts-expect-error': {
661 descriptionFormat: '^ - .+$',
662 },
663 }],
664
665 '@typescript-eslint/no-empty-object-type': 'error',
666 '@typescript-eslint/no-unsafe-function-type': 'error',
667 '@typescript-eslint/no-wrapper-object-types': 'error',
668 '@typescript-eslint/no-explicit-any': 'error',
669
670 '@typescript-eslint/no-shadow': ['error', {
671 builtinGlobals: false,
672 }],
673
674 '@typescript-eslint/no-this-alias': 'error',
675
676 '@typescript-eslint/no-unused-vars': ['error', {
677 vars: 'local',
678 args: 'after-used',
679 argsIgnorePattern: '^_',
680 caughtErrors: 'none',
681 }],
682 },
683 },
684 {
685 files: [
686 '**/*.ts',
687 ],
688
689 rules: {
690 '@stylistic/block-spacing': 'off',
691
692 '@stylistic/comma-dangle': ['error', {
693 arrays: 'always-multiline',
694 objects: 'always-multiline',
695 imports: 'always-multiline',
696 exports: 'always-multiline',
697 functions: 'always-multiline',
698 enums: 'always-multiline',
699 generics: 'always-multiline',
700 tuples: 'always-multiline',
701 }],
702
703 '@stylistic/indent-binary-ops': 'off',
704
705 '@stylistic/no-multiple-empty-lines': ['error', {
706 max: 1,
707 maxEOF: 0,
708 maxBOF: 0,
709 }],
710
711 '@stylistic/no-extra-parens': ['error', 'all'],
712 },
713 },
714 {
715 files: [
716 '**/*.json',
717 ],
718
719 languageOptions: {
720 parser: parser,
721 },
722 },
723 {
724 files: [
725 '.vscode/launch.json',
726 ],
727
728 rules: {
729 'jsonc/no-comments': 'off',
730 },
731 },
732 {
733 files: [
734 'ext/data/schemas/options-schema.json',
735 ],
736
737 rules: {
738 '@stylistic/no-multi-spaces': 'off',
739 },
740 },
741 {
742 files: [
743 'test/data/anki-note-builder-test-results.json',
744 'test/data/database-test-cases.json',
745 'test/data/translator-test-results-note-data1.json',
746 'test/data/translator-test-results.json',
747 ],
748
749 rules: {
750 'jsonc/indent': ['error', 2],
751 },
752 },
753 {
754 files: [
755 'test/data/dictionaries/valid-dictionary1/term_bank_1.json',
756 'test/data/dictionaries/valid-dictionary1/term_bank_2.json',
757 ],
758
759 rules: {
760 'jsonc/array-element-newline': 'off',
761 'jsonc/object-property-newline': 'off',
762 },
763 },
764 {
765 files: [
766 '**/*.js',
767 '**/*.ts',
768 ],
769
770 rules: {
771 'header/header': ['error', 'block', {
772 pattern: ' \\* Copyright \\(C\\) (20(23|24)-)?2025 Yomitan Authors(\n \\* Copyright \\(C\\) (20(16|17|18|19|20|21)-)?2022 Yomichan Authors)?\n \\*\n \\* This program is free software: you can redistribute it and/or modify\n \\* it under the terms of the GNU General Public License as published by\n \\* the Free Software Foundation, either version 3 of the License, or\n \\* \\(at your option\\) any later version\\.\n \\*\n \\* This program is distributed in the hope that it will be useful,\n \\* but WITHOUT ANY WARRANTY; without even the implied warranty of\n \\* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\\. See the\n \\* GNU General Public License for more details\\.\n \\*\n \\* You should have received a copy of the GNU General Public License\n \\* along with this program\\. If not, see <https://www\\.gnu\\.org/licenses/>\\.\n ',
773 }],
774 },
775 },
776 {
777 files: [
778 'ext/**/*.js',
779 ],
780
781 rules: {
782 'no-console': 'error',
783 },
784 },
785 {
786 files: [
787 'test/**/*.js',
788 'dev/**/*.js',
789 '**/integration.spec.js',
790 '**/playwright.config.js',
791 '**/playwright-util.js',
792 '**/visual.spec.js',
793 ],
794
795 languageOptions: {
796 globals: {
797 ...Object.fromEntries(Object.entries(globals.browser).map(([key]) => [key, 'off'])),
798 ...globals.node,
799 ...Object.fromEntries(Object.entries(globals.webextensions).map(([key]) => [key, 'off'])),
800 },
801 },
802 },
803 {
804 files: [
805 'test/data/html/**/*.js',
806 ],
807
808 languageOptions: {
809 globals: {
810 ...globals.browser,
811 ...Object.fromEntries(Object.entries(globals.node).map(([key]) => [key, 'off'])),
812 ...Object.fromEntries(Object.entries(globals.webextensions).map(([key]) => [key, 'off'])),
813 },
814
815 ecmaVersion: 5,
816 sourceType: 'script',
817 },
818 },
819 {
820 files: [
821 'test/data/html/**/*.js',
822 ],
823 ignores: [
824 'test/data/html/js/html-test-utilities.js',
825 ],
826
827 languageOptions: {
828 globals: {
829 HtmlTestUtilities: 'readonly',
830 },
831 },
832 },
833 {
834 files: [
835 'test/**/*.test.js',
836 ],
837 plugins: {
838 vitest,
839 },
840 ...vitest.configs.recommended,
841 rules: {
842 'vitest/prefer-to-be': 'off',
843 },
844 },
845 ...compat.extends('plugin:@typescript-eslint/disable-type-checked').map((config) => ({
846 ...config,
847 files: [
848 'dev/lib/**/*.js',
849 'eslint.config.js',
850 ],
851 })),
852 {
853 files: [
854 'ext/js/templates/template-renderer-frame-main.js',
855 ...await getDependencies(['ext/js/templates/template-renderer-frame-main.js']),
856 ],
857
858 languageOptions: {
859 globals: {
860 ...Object.fromEntries(Object.entries(globals.webextensions).map(([key]) => [key, 'off'])),
861 },
862 },
863 },
864 {
865 files: [
866 'ext/js/dictionary/dictionary-worker-main.js',
867 ...await getDependencies(['ext/js/dictionary/dictionary-worker-main.js']),
868 ],
869
870 languageOptions: {
871 globals: {
872 ...Object.fromEntries(Object.entries(globals.browser).map(([key]) => [key, 'off'])),
873 ...globals.worker,
874 },
875 },
876 },
877 {
878 files: [
879 'ext/js/background/background-main.js',
880 ...await getDependencies(['ext/js/background/background-main.js']),
881 ],
882
883 languageOptions: {
884 globals: {
885 ...Object.fromEntries(Object.entries(globals.browser).map(([key]) => [key, 'off'])),
886 ...globals.serviceworker,
887 FileReader: 'readonly',
888 Intl: 'readonly',
889 crypto: 'readonly',
890 AbortController: 'readonly',
891 },
892 },
893 },
894 {
895 files: [
896 'ext/data/recommended-dictionaries.json',
897 ],
898
899 rules: {
900 'jsonc/sort-keys': ['error', {
901 pathPattern: '.*',
902 hasProperties: ['name'],
903 order: ['name', 'description', 'homepage', 'downloadUrl'],
904 }, {
905 pathPattern: '.*',
906
907 order: {
908 type: 'asc',
909 },
910 }],
911 },
912 },
913 {
914 files: [
915 'ext/data/recommended-settings.json',
916 ],
917
918 rules: {
919 'jsonc/sort-keys': ['error', {
920 pathPattern: '.*',
921 order: ['modification', 'description'],
922 }, {
923 pathPattern: '.*',
924
925 order: {
926 type: 'asc',
927 },
928 }],
929 },
930 },
931];