/*
* Copyright (C) 2024-2025 Yomitan Authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import {FlatCompat} from '@eslint/eslintrc';
import js from '@eslint/js';
import stylistic from '@stylistic/eslint-plugin';
import vitest from '@vitest/eslint-plugin';
import esbuild from 'esbuild';
import header from 'eslint-plugin-header';
// @ts-expect-error - Missing types https://github.com/import-js/eslint-plugin-import/issues/3133
import importPlugin from 'eslint-plugin-import';
import jsdoc from 'eslint-plugin-jsdoc';
import jsonc from 'eslint-plugin-jsonc';
import noUnsanitized from 'eslint-plugin-no-unsanitized';
import sonarjs from 'eslint-plugin-sonarjs';
import unicorn from 'eslint-plugin-unicorn';
import unusedImports from 'eslint-plugin-unused-imports';
import globals from 'globals';
import parser from 'jsonc-eslint-parser';
import path from 'node:path';
import {fileURLToPath} from 'node:url';
import typescriptEslint from 'typescript-eslint';
const compat = new FlatCompat({
baseDirectory: path.dirname(fileURLToPath(import.meta.url)),
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all,
});
// @ts-expect-error - This is a workaround https://github.com/Stuk/eslint-plugin-header/issues/57
header.rules.header.meta.schema = false;
/**
* @param {string[]} scriptPaths
* @returns {Promise}
*/
async function getDependencies(scriptPaths) {
const v = await esbuild.build({
entryPoints: scriptPaths,
bundle: true,
minify: false,
sourcemap: true,
target: 'es2022',
format: 'esm',
write: false,
metafile: true,
});
const dependencies = Object.keys(v.metafile.inputs);
const stringComparer = new Intl.Collator('en-US'); // Invariant locale
dependencies.sort((a, b) => stringComparer.compare(a, b));
return dependencies;
}
/**
* @type {import('eslint').Linter.Config[]}
*/
export default [
{
ignores: ['ext/lib/', 'dev/lib/handlebars/', '**/node_modules/', '**/builds/', 'test-results/'],
},
...compat.extends(
'eslint:recommended',
'plugin:jsonc/recommended-with-json',
'plugin:eslint-comments/recommended',
),
{
plugins: {
'no-unsanitized': noUnsanitized,
header,
jsdoc,
jsonc,
'unused-imports': unusedImports,
'@typescript-eslint': typescriptEslint.plugin,
'@stylistic': stylistic,
unicorn,
sonarjs,
'import': importPlugin,
},
languageOptions: {
globals: {
...globals.browser,
...globals.webextensions,
},
parser: typescriptEslint.parser,
ecmaVersion: 2022,
sourceType: 'module',
parserOptions: {
ecmaFeatures: {
globalReturn: false,
impliedStrict: true,
},
project: [
'./jsconfig.json',
'./dev/jsconfig.json',
'./test/jsconfig.json',
'./benches/jsconfig.json',
],
},
},
rules: {
'accessor-pairs': 'error',
'curly': ['error', 'all'],
'default-case-last': 'error',
'dot-notation': 'error',
'eqeqeq': 'error',
'func-names': ['error', 'always'],
'guard-for-in': 'error',
'grouped-accessor-pairs': 'error',
'new-cap': 'error',
'no-alert': 'error',
'no-case-declarations': 'error',
'no-caller': 'error',
'no-const-assign': 'error',
'no-constant-condition': ['error', {
checkLoops: false,
}],
'no-constructor-return': 'error',
'no-duplicate-imports': 'error',
'no-eval': 'error',
'no-extend-native': 'error',
'no-global-assign': 'error',
'no-implicit-globals': 'error',
'no-implied-eval': 'error',
'no-new': 'error',
'no-new-native-nonconstructor': 'error',
'no-octal': 'error',
'no-octal-escape': 'error',
'no-param-reassign': 'off',
'no-promise-executor-return': 'error',
'no-prototype-builtins': 'error',
'no-restricted-syntax': ['error', {
message: 'Avoid using JSON.parse(), prefer parseJson.',
selector: 'MemberExpression[object.name=JSON][property.name=parse]',
}, {
message: 'Avoid using Response.json(), prefer readResponseJson.',
selector: 'MemberExpression[property.name=json]',
}, {
message: 'Avoid using performance, prefer safePerformance.',
selector: 'MemberExpression[object.name=performance]',
}],
'no-self-compare': 'error',
'no-sequences': 'error',
'no-shadow': ['error', {
builtinGlobals: false,
}],
'no-shadow-restricted-names': 'error',
'no-template-curly-in-string': 'error',
'no-undef': 'error',
'no-undefined': 'error',
'no-underscore-dangle': ['error', {
allowAfterThis: true,
allowAfterSuper: false,
allowAfterThisConstructor: false,
}],
'no-unexpected-multiline': 'error',
'no-unneeded-ternary': 'error',
'no-unused-vars': ['error', {
vars: 'local',
args: 'after-used',
argsIgnorePattern: '^_',
caughtErrors: 'none',
}],
'no-unused-expressions': 'error',
'no-var': 'error',
'no-with': 'error',
'prefer-const': ['error', {
destructuring: 'all',
}],
'radix': 'error',
'require-atomic-updates': 'off',
'sort-imports': 'off',
'yoda': ['error', 'never'],
'@stylistic/array-bracket-newline': ['error', 'consistent'],
'@stylistic/array-bracket-spacing': ['error', 'never'],
'@stylistic/array-element-newline': ['error', 'consistent'],
'@stylistic/arrow-parens': ['error', 'always'],
'@stylistic/arrow-spacing': ['error', {
before: true,
after: true,
}],
'@stylistic/block-spacing': ['error', 'always'],
'@stylistic/brace-style': ['error', '1tbs', {
allowSingleLine: true,
}],
'@stylistic/comma-dangle': ['error', 'always-multiline'],
'@stylistic/comma-spacing': ['error', {
before: false,
after: true,
}],
'@stylistic/comma-style': ['error', 'last'],
'@stylistic/computed-property-spacing': ['error', 'never'],
'@stylistic/dot-location': ['error', 'property'],
'@stylistic/eol-last': ['error', 'always'],
'@stylistic/func-call-spacing': ['error', 'never'],
'@stylistic/function-call-argument-newline': ['error', 'consistent'],
'@stylistic/function-call-spacing': ['error', 'never'],
'@stylistic/function-paren-newline': ['error', 'multiline-arguments'],
'@stylistic/generator-star-spacing': ['error', 'before'],
'@stylistic/implicit-arrow-linebreak': ['error', 'beside'],
'@stylistic/indent': ['error', 4, {
SwitchCase: 1,
MemberExpression: 1,
flatTernaryExpressions: true,
ignoredNodes: ['ConditionalExpression'],
}],
'@stylistic/indent-binary-ops': ['error', 0],
'@stylistic/key-spacing': ['error', {
beforeColon: false,
afterColon: true,
mode: 'strict',
}],
'@stylistic/keyword-spacing': ['error', {
before: true,
after: true,
}],
'@stylistic/linebreak-style': ['error', 'unix'],
'@stylistic/lines-around-comment': 'off',
'@stylistic/lines-between-class-members': ['error', 'always', {
exceptAfterSingleLine: true,
}],
'@stylistic/max-len': 'off',
'@stylistic/max-statements-per-line': ['error', {
max: 2,
}],
'@stylistic/member-delimiter-style': ['error', {
multiline: {
delimiter: 'semi',
requireLast: true,
},
singleline: {
delimiter: 'comma',
requireLast: false,
},
multilineDetection: 'brackets',
}],
'@stylistic/multiline-ternary': ['error', 'always-multiline'],
'@stylistic/new-parens': 'error',
'@stylistic/newline-per-chained-call': ['error', {
ignoreChainWithDepth: 3,
}],
'@stylistic/no-confusing-arrow': 'error',
'@stylistic/no-extra-parens': 'off',
'@stylistic/no-extra-semi': 'error',
'@stylistic/no-floating-decimal': 'error',
'@stylistic/no-mixed-operators': ['error', {
allowSamePrecedence: true,
groups: [['&&', '||']],
}],
'@stylistic/no-mixed-spaces-and-tabs': 'error',
'@stylistic/no-multi-spaces': 'error',
'@stylistic/no-multiple-empty-lines': ['error', {
max: 2,
maxEOF: 0,
maxBOF: 0,
}],
'@stylistic/no-tabs': 'error',
'@stylistic/no-trailing-spaces': 'error',
'@stylistic/no-whitespace-before-property': 'error',
'@stylistic/nonblock-statement-body-position': ['error', 'beside'],
'@stylistic/object-curly-newline': 'error',
'@stylistic/object-curly-spacing': ['error', 'never'],
'@stylistic/object-property-newline': ['error', {
allowAllPropertiesOnSameLine: true,
}],
'@stylistic/one-var-declaration-per-line': ['error', 'initializations'],
'@stylistic/operator-linebreak': ['error', 'after'],
'@stylistic/padded-blocks': ['error', 'never'],
'@stylistic/padding-line-between-statements': ['error', {
blankLine: 'always',
prev: '*',
next: 'import',
}, {
blankLine: 'always',
prev: 'import',
next: '*',
}, {
blankLine: 'always',
prev: '*',
next: 'export',
}, {
blankLine: 'always',
prev: 'import',
next: 'let',
}, {
blankLine: 'always',
prev: 'import',
next: 'const',
}, {
blankLine: 'always',
prev: 'export',
next: 'let',
}, {
blankLine: 'always',
prev: 'export',
next: 'const',
}, {
blankLine: 'always',
prev: 'export',
next: 'export',
}, {
blankLine: 'always',
prev: 'export',
next: 'type',
}, {
blankLine: 'always',
prev: 'type',
next: 'export',
}, {
blankLine: 'always',
prev: 'type',
next: 'type',
}, {
blankLine: 'never',
prev: 'import',
next: 'import',
}],
'@stylistic/quote-props': ['error', 'consistent-as-needed', {
numbers: true,
}],
'@stylistic/quotes': ['error', 'single', 'avoid-escape'],
'@stylistic/rest-spread-spacing': ['error', 'never'],
'@stylistic/semi': 'error',
'@stylistic/semi-spacing': ['error', {
before: false,
after: true,
}],
'@stylistic/semi-style': ['error', 'last'],
'@stylistic/space-before-blocks': ['error', 'always'],
'@stylistic/space-before-function-paren': ['error', {
anonymous: 'never',
named: 'never',
asyncArrow: 'always',
}],
'@stylistic/space-in-parens': ['error', 'never'],
'@stylistic/space-infix-ops': ['error', {
int32Hint: false,
}],
'@stylistic/space-unary-ops': 'error',
'@stylistic/spaced-comment': ['error', 'always'],
'@stylistic/switch-colon-spacing': ['error', {
after: true,
before: false,
}],
'@stylistic/template-curly-spacing': ['error', 'never'],
'@stylistic/template-tag-spacing': ['error', 'never'],
'@stylistic/type-annotation-spacing': ['error', {
before: false,
after: true,
overrides: {
arrow: {
before: true,
after: true,
},
},
}],
'@stylistic/type-generic-spacing': 'error',
'@stylistic/type-named-tuple-spacing': 'error',
'@stylistic/wrap-iife': ['error', 'inside'],
'@stylistic/wrap-regex': 'off',
'@stylistic/yield-star-spacing': ['error', {
before: true,
after: false,
}],
'no-unsanitized/method': 'error',
'no-unsanitized/property': 'error',
'jsdoc/check-access': 'error',
'jsdoc/check-alignment': 'error',
'jsdoc/check-line-alignment': ['error', 'never', {
wrapIndent: ' ',
}],
'jsdoc/check-param-names': 'error',
'jsdoc/check-property-names': 'error',
'jsdoc/check-tag-names': 'error',
'jsdoc/empty-tags': 'error',
'jsdoc/check-types': 'error',
'jsdoc/check-values': 'error',
'jsdoc/implements-on-classes': 'error',
'jsdoc/multiline-blocks': 'error',
'jsdoc/no-bad-blocks': 'error',
'jsdoc/no-multi-asterisks': 'error',
'jsdoc/no-undefined-types': 'error',
'jsdoc/require-asterisk-prefix': 'error',
'jsdoc/require-description': 'off',
'jsdoc/require-hyphen-before-param-description': ['error', 'never'],
'jsdoc/require-jsdoc': ['error', {
require: {
ClassDeclaration: false,
FunctionDeclaration: true,
MethodDefinition: false,
},
contexts: [
'MethodDefinition[kind=constructor]>FunctionExpression>BlockStatement>ExpressionStatement>AssignmentExpression[left.object.type=ThisExpression]',
'ClassDeclaration>Classbody>PropertyDefinition',
'MethodDefinition[kind!=constructor][kind!=set]',
'MethodDefinition[kind=constructor][value.params.length>0]',
],
checkGetters: 'no-setter',
checkSetters: 'no-getter',
}],
'jsdoc/require-param': 'error',
'jsdoc/require-param-description': 'off',
'jsdoc/require-param-name': 'error',
'jsdoc/require-param-type': 'error',
'jsdoc/require-property': 'error',
'jsdoc/require-property-description': 'off',
'jsdoc/require-property-name': 'error',
'jsdoc/require-property-type': 'error',
'jsdoc/require-returns': 'error',
'jsdoc/require-returns-check': 'error',
'jsdoc/require-returns-description': 'off',
'jsdoc/require-returns-type': 'error',
'jsdoc/require-throws': 'error',
'jsdoc/require-yields': 'error',
'jsdoc/require-yields-check': 'error',
'jsdoc/tag-lines': ['error', 'never', {
startLines: 0,
}],
'jsdoc/valid-types': 'error',
'jsonc/indent': ['error', 4],
'jsonc/array-bracket-newline': ['error', 'consistent'],
'jsonc/array-bracket-spacing': ['error', 'never'],
'jsonc/array-element-newline': ['error', 'consistent'],
'jsonc/comma-style': ['error', 'last'],
'jsonc/key-spacing': ['error', {
beforeColon: false,
afterColon: true,
mode: 'strict',
}],
'jsonc/no-octal-escape': 'error',
'jsonc/object-curly-newline': ['error', {
consistent: true,
}],
'jsonc/object-curly-spacing': ['error', 'never'],
'jsonc/object-property-newline': ['error', {
allowAllPropertiesOnSameLine: true,
}],
'eslint-comments/no-unused-disable': 'error',
'unused-imports/no-unused-imports': 'error',
'import/extensions': ['error', 'ignorePackages'],
'unicorn/catch-error-name': ['error', {
ignore: ['^(e|error2?)$'],
}],
'unicorn/custom-error-definition': 'error',
'unicorn/empty-brace-spaces': 'error',
'unicorn/error-message': 'error',
'unicorn/expiring-todo-comments': 'error',
'unicorn/explicit-length-check': 'error',
'unicorn/new-for-builtins': 'error',
'unicorn/no-abusive-eslint-disable': 'error',
'unicorn/no-array-for-each': 'error',
'unicorn/no-array-method-this-argument': 'error',
'unicorn/no-array-push-push': 'error',
'unicorn/no-array-reduce': 'error',
'unicorn/no-console-spaces': 'error',
'unicorn/no-document-cookie': 'error',
'unicorn/no-empty-file': 'error',
'unicorn/no-hex-escape': 'error',
'unicorn/no-instanceof-array': 'error',
'unicorn/no-invalid-remove-event-listener': 'error',
'unicorn/no-lonely-if': 'error',
'unicorn/no-nested-ternary': 'error',
'unicorn/no-new-buffer': 'error',
'unicorn/no-object-as-default-parameter': 'error',
'unicorn/no-static-only-class': 'error',
'unicorn/no-thenable': 'error',
'unicorn/no-unnecessary-await': 'error',
'unicorn/no-unnecessary-polyfills': 'error',
'unicorn/no-unreadable-array-destructuring': 'error',
'unicorn/no-unreadable-iife': 'error',
'unicorn/no-useless-fallback-in-spread': 'error',
'unicorn/no-useless-length-check': 'error',
'unicorn/no-useless-promise-resolve-reject': 'error',
'unicorn/no-useless-spread': 'error',
'unicorn/no-useless-switch-case': 'error',
'unicorn/no-useless-undefined': 'error',
'unicorn/no-zero-fractions': 'error',
'unicorn/prefer-array-find': 'error',
'unicorn/prefer-array-flat': 'error',
'unicorn/prefer-array-flat-map': 'error',
'unicorn/prefer-array-index-of': 'error',
'unicorn/prefer-array-some': 'error',
'unicorn/prefer-date-now': 'error',
'unicorn/prefer-default-parameters': 'error',
'unicorn/prefer-dom-node-dataset': 'error',
'unicorn/prefer-dom-node-text-content': 'error',
'unicorn/prefer-event-target': 'error',
'unicorn/prefer-export-from': 'error',
'unicorn/prefer-includes': 'error',
'unicorn/prefer-keyboard-event-key': 'error',
'unicorn/prefer-logical-operator-over-ternary': 'error',
'unicorn/prefer-modern-math-apis': 'error',
'unicorn/prefer-module': 'error',
'unicorn/prefer-native-coercion-functions': 'error',
'unicorn/prefer-negative-index': 'error',
'unicorn/prefer-number-properties': 'error',
'unicorn/prefer-object-from-entries': 'error',
'unicorn/prefer-prototype-methods': 'error',
'unicorn/prefer-reflect-apply': 'error',
'unicorn/prefer-regexp-test': 'error',
'unicorn/prefer-set-has': 'error',
'unicorn/prefer-set-size': 'error',
'unicorn/prefer-spread': 'error',
'unicorn/prefer-string-starts-ends-with': 'error',
'unicorn/prefer-string-trim-start-end': 'error',
'unicorn/prefer-switch': 'error',
'unicorn/prefer-ternary': 'error',
'unicorn/relative-url-style': 'error',
'unicorn/require-array-join-separator': 'error',
'unicorn/require-number-to-fixed-digits-argument': 'error',
'unicorn/template-indent': 'error',
'unicorn/throw-new-error': 'error',
'sonarjs/max-switch-cases': 'error',
'sonarjs/no-all-duplicated-branches': 'error',
'sonarjs/no-collapsible-if': 'error',
'sonarjs/no-collection-size-mischeck': 'error',
'sonarjs/no-duplicated-branches': 'error',
'sonarjs/no-element-overwrite': 'error',
'sonarjs/no-empty-collection': 'error',
'sonarjs/no-extra-arguments': 'error',
'sonarjs/no-gratuitous-expressions': 'error',
'sonarjs/no-identical-conditions': 'error',
'sonarjs/no-identical-expressions': 'error',
'sonarjs/no-identical-functions': 'error',
'sonarjs/no-ignored-return': 'error',
'sonarjs/no-inverted-boolean-check': 'error',
'sonarjs/no-one-iteration-loop': 'error',
'sonarjs/no-redundant-boolean': 'error',
'sonarjs/no-redundant-jump': 'error',
'sonarjs/no-same-line-conditional': 'error',
'sonarjs/no-unused-collection': 'error',
'sonarjs/no-use-of-empty-return-value': 'error',
'sonarjs/no-useless-catch': 'error',
'sonarjs/non-existent-operator': 'error',
'sonarjs/prefer-immediate-return': 'error',
'sonarjs/prefer-object-literal': 'error',
'sonarjs/prefer-single-boolean-return': 'error',
'sonarjs/prefer-while': 'error',
},
},
...typescriptEslint.configs.recommendedTypeChecked.map((config) => ({
...config,
files: [
'**/*.js',
'**/*.ts',
],
})),
{
files: [
'**/*.js',
'**/*.ts',
],
rules: {
'@typescript-eslint/no-floating-promises': ['error', {
ignoreIIFE: true,
}],
'@typescript-eslint/no-misused-promises': 'off',
'@typescript-eslint/no-redundant-type-constituents': 'error',
'@typescript-eslint/no-unsafe-argument': 'error',
'@typescript-eslint/no-unsafe-assignment': 'error',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-enum-comparison': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-return': 'off',
'@typescript-eslint/require-await': 'off',
'@typescript-eslint/restrict-template-expressions': 'off',
'@typescript-eslint/prefer-promise-reject-errors': 'off',
'@typescript-eslint/ban-ts-comment': ['error', {
'ts-expect-error': {
descriptionFormat: '^ - .+$',
},
}],
'@typescript-eslint/no-empty-object-type': 'error',
'@typescript-eslint/no-unsafe-function-type': 'error',
'@typescript-eslint/no-wrapper-object-types': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-shadow': ['error', {
builtinGlobals: false,
}],
'@typescript-eslint/no-this-alias': 'error',
'@typescript-eslint/no-unused-vars': ['error', {
vars: 'local',
args: 'after-used',
argsIgnorePattern: '^_',
caughtErrors: 'none',
}],
},
},
{
files: [
'**/*.ts',
],
rules: {
'@stylistic/block-spacing': 'off',
'@stylistic/comma-dangle': ['error', {
arrays: 'always-multiline',
objects: 'always-multiline',
imports: 'always-multiline',
exports: 'always-multiline',
functions: 'always-multiline',
enums: 'always-multiline',
generics: 'always-multiline',
tuples: 'always-multiline',
}],
'@stylistic/indent-binary-ops': 'off',
'@stylistic/no-multiple-empty-lines': ['error', {
max: 1,
maxEOF: 0,
maxBOF: 0,
}],
'@stylistic/no-extra-parens': ['error', 'all'],
},
},
{
files: [
'**/*.json',
],
languageOptions: {
parser: parser,
},
},
{
files: [
'.vscode/launch.json',
],
rules: {
'jsonc/no-comments': 'off',
},
},
{
files: [
'ext/data/schemas/options-schema.json',
],
rules: {
'@stylistic/no-multi-spaces': 'off',
},
},
{
files: [
'test/data/anki-note-builder-test-results.json',
'test/data/database-test-cases.json',
'test/data/translator-test-results-note-data1.json',
'test/data/translator-test-results.json',
],
rules: {
'jsonc/indent': ['error', 2],
},
},
{
files: [
'test/data/dictionaries/valid-dictionary1/term_bank_1.json',
'test/data/dictionaries/valid-dictionary1/term_bank_2.json',
],
rules: {
'jsonc/array-element-newline': 'off',
'jsonc/object-property-newline': 'off',
},
},
{
files: [
'**/*.js',
'**/*.ts',
],
rules: {
'header/header': ['error', 'block', {
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 \\.\n ',
}],
},
},
{
files: [
'ext/**/*.js',
],
rules: {
'no-console': 'error',
},
},
{
files: [
'test/**/*.js',
'dev/**/*.js',
'**/integration.spec.js',
'**/playwright.config.js',
'**/playwright-util.js',
'**/visual.spec.js',
],
languageOptions: {
globals: {
...Object.fromEntries(Object.entries(globals.browser).map(([key]) => [key, 'off'])),
...globals.node,
...Object.fromEntries(Object.entries(globals.webextensions).map(([key]) => [key, 'off'])),
},
},
},
{
files: [
'test/data/html/**/*.js',
],
languageOptions: {
globals: {
...globals.browser,
...Object.fromEntries(Object.entries(globals.node).map(([key]) => [key, 'off'])),
...Object.fromEntries(Object.entries(globals.webextensions).map(([key]) => [key, 'off'])),
},
ecmaVersion: 5,
sourceType: 'script',
},
},
{
files: [
'test/data/html/**/*.js',
],
ignores: [
'test/data/html/js/html-test-utilities.js',
],
languageOptions: {
globals: {
HtmlTestUtilities: 'readonly',
},
},
},
{
files: [
'test/**/*.test.js',
],
plugins: {
vitest,
},
...vitest.configs.recommended,
rules: {
'vitest/prefer-to-be': 'off',
},
},
...compat.extends('plugin:@typescript-eslint/disable-type-checked').map((config) => ({
...config,
files: [
'dev/lib/**/*.js',
'eslint.config.js',
],
})),
{
files: [
'ext/js/templates/template-renderer-frame-main.js',
...await getDependencies(['ext/js/templates/template-renderer-frame-main.js']),
],
languageOptions: {
globals: {
...Object.fromEntries(Object.entries(globals.webextensions).map(([key]) => [key, 'off'])),
},
},
},
{
files: [
'ext/js/dictionary/dictionary-worker-main.js',
...await getDependencies(['ext/js/dictionary/dictionary-worker-main.js']),
],
languageOptions: {
globals: {
...Object.fromEntries(Object.entries(globals.browser).map(([key]) => [key, 'off'])),
...globals.worker,
},
},
},
{
files: [
'ext/js/background/background-main.js',
...await getDependencies(['ext/js/background/background-main.js']),
],
languageOptions: {
globals: {
...Object.fromEntries(Object.entries(globals.browser).map(([key]) => [key, 'off'])),
...globals.serviceworker,
FileReader: 'readonly',
Intl: 'readonly',
crypto: 'readonly',
AbortController: 'readonly',
},
},
},
{
files: [
'ext/data/recommended-dictionaries.json',
],
rules: {
'jsonc/sort-keys': ['error', {
pathPattern: '.*',
hasProperties: ['name'],
order: ['name', 'description', 'homepage', 'downloadUrl'],
}, {
pathPattern: '.*',
order: {
type: 'asc',
},
}],
},
},
{
files: [
'ext/data/recommended-settings.json',
],
rules: {
'jsonc/sort-keys': ['error', {
pathPattern: '.*',
order: ['modification', 'description'],
}, {
pathPattern: '.*',
order: {
type: 'asc',
},
}],
},
},
];