diff options
31 files changed, 1000 insertions, 641 deletions
| diff --git a/.eslintrc.json b/.eslintrc.json index 3557f83a..440fe53b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -33,175 +33,26 @@          "jsonc",          "unused-imports",          "@typescript-eslint", -        "@stylistic/ts" +        "@stylistic"      ],      "ignorePatterns": [          "/ext/lib/",          "/dev/lib/handlebars/"      ],      "rules": { -        "arrow-parens": [ -            "error", -            "always" -        ], -        "comma-dangle": [ -            "error", -            "never" -        ], -        "curly": [ -            "error", -            "all" -        ], +        "curly": ["error", "all"],          "dot-notation": "error",          "eqeqeq": "error", -        "func-names": [ -            "error", -            "always" -        ], +        "func-names": ["error", "always"],          "guard-for-in": "error", -        "new-parens": "error",          "no-case-declarations": "error",          "no-const-assign": "error",          "no-constant-condition": "off", -        "no-multiple-empty-lines": [ -            "error", -            { -                "max": 2 -            } -        ],          "no-global-assign": "error", +        "no-implicit-globals": "error",          "no-new": "error",          "no-param-reassign": "off",          "no-prototype-builtins": "error", -        "no-shadow": [ -            "off", -            { -                "builtinGlobals": false -            } -        ], -        "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", -        "prefer-const": [ -            "error", -            { -                "destructuring": "all" -            } -        ], -        "quote-props": [ -            "error", -            "consistent" -        ], -        "quotes": [ -            "error", -            "single", -            "avoid-escape" -        ], -        "require-atomic-updates": "off", -        "semi": "error", -        "wrap-iife": [ -            "error", -            "inside" -        ], -        "brace-style": [ -            "error", -            "1tbs", -            { -                "allowSingleLine": true -            } -        ], -        "indent": [ -            "error", -            4, -            { -                "SwitchCase": 1, -                "MemberExpression": 1, -                "flatTernaryExpressions": true, -                "ignoredNodes": [ -                    "ConditionalExpression" -                ] -            } -        ], -        "object-curly-newline": "error", -        "padded-blocks": [ -            "error", -            "never" -        ], -        "array-bracket-spacing": [ -            "error", -            "never" -        ], -        "arrow-spacing": [ -            "error", -            { -                "before": true, -                "after": true -            } -        ], -        "block-spacing": [ -            "error", -            "always" -        ], -        "comma-spacing": [ -            "error", -            { -                "before": false, -                "after": true -            } -        ], -        "computed-property-spacing": [ -            "error", -            "never" -        ], -        "func-call-spacing": [ -            "error", -            "never" -        ], -        "function-paren-newline": [ -            "error", -            "multiline-arguments" -        ], -        "generator-star-spacing": [ -            "error", -            "before" -        ], -        "key-spacing": [ -            "error", -            { -                "beforeColon": false, -                "afterColon": true, -                "mode": "strict" -            } -        ], -        "keyword-spacing": [ -            "error", -            { -                "before": true, -                "after": true -            } -        ], -        "no-implicit-globals": "error", -        "no-multi-spaces": "error",          "no-restricted-syntax": [              "error",              { @@ -213,92 +64,76 @@                  "selector": "MemberExpression[property.name=json]"              }          ], -        "no-trailing-spaces": "error", -        "no-whitespace-before-property": "error", -        "object-curly-spacing": [ -            "error", -            "never" -        ], -        "rest-spread-spacing": [ -            "error", -            "never" -        ], -        "semi-spacing": [ -            "error", -            { -                "before": false, -                "after": true -            } -        ], -        "space-before-blocks": [ -            "error", -            "always" -        ], -        "space-before-function-paren": [ -            "error", -            { -                "anonymous": "never", -                "named": "never", -                "asyncArrow": "always" -            } -        ], -        "space-in-parens": [ -            "error", -            "never" -        ], -        "space-infix-ops": [ -            "error", -            { -                "int32Hint": false -            } -        ], -        "space-unary-ops": "error", -        "spaced-comment": [ -            "error", -            "always" -        ], -        "switch-colon-spacing": [ -            "error", -            { -                "after": true, -                "before": false -            } -        ], -        "template-curly-spacing": [ -            "error", -            "never" -        ], -        "template-tag-spacing": [ -            "error", -            "never" -        ], +        "no-shadow": ["off", {"builtinGlobals": false}], +        "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", +        "prefer-const": ["error", {"destructuring": "all"}], +        "require-atomic-updates": "off", + +        "@stylistic/array-bracket-spacing": ["error", "never"], +        "@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", "never"], +        "@stylistic/comma-spacing": ["error", {"before": false, "after": true}], +        "@stylistic/computed-property-spacing": ["error", "never"], +        "@stylistic/func-call-spacing": ["error", "never"], +        "@stylistic/function-paren-newline": ["error", "multiline-arguments"], +        "@stylistic/generator-star-spacing": ["error", "before"], +        "@stylistic/indent": ["error", 4, {"SwitchCase": 1, "MemberExpression": 1, "flatTernaryExpressions": true, "ignoredNodes": ["ConditionalExpression"]}], +        "@stylistic/key-spacing": ["error", {"beforeColon": false, "afterColon": true, "mode": "strict"}], +        "@stylistic/keyword-spacing": ["error", {"before": true, "after": true}], +        "@stylistic/new-parens": "error", +        "@stylistic/no-multi-spaces": "error", +        "@stylistic/no-multiple-empty-lines": ["error", {"max": 2}], +        "@stylistic/no-trailing-spaces": "error", +        "@stylistic/no-whitespace-before-property": "error", +        "@stylistic/object-curly-newline": "error", +        "@stylistic/object-curly-spacing": ["error", "never"], +        "@stylistic/padded-blocks": ["error", "never"], +        "@stylistic/quote-props": ["error", "consistent"], +        "@stylistic/quotes": ["error", "single", "avoid-escape"], +        "@stylistic/rest-spread-spacing": ["error", "never"], +        "@stylistic/semi": "error", +        "@stylistic/semi-spacing": ["error", {"before": false, "after": true}], +        "@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/wrap-iife": ["error", "inside"], +          "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-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/empty-tags": "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-hyphen-before-param-description": [ -            "error", -            "never" -        ], +        "jsdoc/require-description": "off", +        "jsdoc/require-hyphen-before-param-description": ["error", "never"],          "jsdoc/require-jsdoc": [              "error",              { @@ -317,7 +152,6 @@                  "checkSetters": "no-getter"              }          ], -        "jsdoc/require-description": "off",          "jsdoc/require-param": "error",          "jsdoc/require-param-description": "off",          "jsdoc/require-param-name": "error", @@ -335,77 +169,27 @@          "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/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 -            } -        ], +        "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", -        "@typescript-eslint/ban-ts-comment": [ -            "error", -            { -                "ts-expect-error": { -                    "descriptionFormat": "^ - .+$" -                } -            } -        ], -        "@typescript-eslint/ban-types": [ -            "error", -            { -                "types": { -                    "object": true -                }, -                "extendDefaults": true -            } -        ], + +        "unused-imports/no-unused-imports": "error", + +        "@typescript-eslint/ban-ts-comment": ["error", {"ts-expect-error": {"descriptionFormat": "^ - .+$"}}], +        "@typescript-eslint/ban-types": ["error", {"types": {"object": true}, "extendDefaults": true}],          "@typescript-eslint/consistent-type-exports": "off",          "@typescript-eslint/no-explicit-any": "error", -        "@typescript-eslint/no-shadow": [ -            "error", -            { -                "builtinGlobals": false -            } -        ], +        "@typescript-eslint/no-shadow": ["error", {"builtinGlobals": false}],          "@typescript-eslint/no-this-alias": "error",          "@typescript-eslint/no-unused-vars": "off",          "@typescript-eslint/no-var-requires": "off" @@ -416,47 +200,13 @@                  "*.ts"              ],              "rules": { -                "no-multiple-empty-lines": [ -                    "error", -                    { -                        "max": 1, -                        "maxEOF": 0 -                    } -                ],                  "no-undef": "off", -                "unused-imports/no-unused-imports": "error", -                "@typescript-eslint/no-unused-vars": [ -                    "error", -                    { -                        "vars": "local", -                        "args": "after-used", -                        "argsIgnorePattern": "^_", -                        "caughtErrors": "none" -                    } -                ], -                "comma-dangle": "off", -                "@typescript-eslint/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/ts/block-spacing": "off", -                "@stylistic/ts/brace-style": [ -                    "error", -                    "1tbs", -                    { -                        "allowSingleLine": true -                    } -                ], -                "@stylistic/ts/comma-dangle": [ + +                "@typescript-eslint/no-unused-vars": ["error", {"vars": "local", "args": "after-used", "argsIgnorePattern": "^_", "caughtErrors": "none"}], + +                "@stylistic/block-spacing": "off", +                "@stylistic/brace-style": ["error", "1tbs", {"allowSingleLine": true}], +                "@stylistic/comma-dangle": [                      "error",                      {                          "arrays": "always-multiline", @@ -469,94 +219,46 @@                          "tuples": "always-multiline"                      }                  ], -                "@stylistic/ts/comma-spacing": [ -                    "error", -                    { -                        "before": false, -                        "after": true -                    } -                ], -                "@stylistic/ts/function-call-spacing": [ -                    "error", -                    "never" -                ], -                "@stylistic/ts/indent": [ -                    "error", -                    4 -                ], -                "@stylistic/ts/key-spacing": [ -                    "error", -                    { -                        "beforeColon": false, -                        "afterColon": true, -                        "mode": "strict" -                    } -                ], -                "@stylistic/ts/keyword-spacing": [ +                "@stylistic/comma-spacing": ["error", {"before": false, "after": true}], +                "@stylistic/function-call-spacing": ["error", "never"], +                "@stylistic/indent": ["error", 4], +                "@stylistic/key-spacing": ["error", {"beforeColon": false, "afterColon": true, "mode": "strict"}], +                "@stylistic/keyword-spacing": ["error", {"before": true, "after": true}], +                "@stylistic/lines-around-comment": "off", +                "@stylistic/lines-between-class-members": ["error", "always"], +                "@stylistic/member-delimiter-style": [                      "error",                      { -                        "before": true, -                        "after": true -                    } -                ], -                "@stylistic/ts/lines-around-comment": "off", -                "@stylistic/ts/lines-between-class-members": [ -                    "error", -                    "always" -                ], -                "@stylistic/ts/member-delimiter-style": [ -                    "error", -                    { -                        "multiline": { -                            "delimiter": "semi", -                            "requireLast": true -                        }, -                        "singleline": { -                            "delimiter": "comma", -                            "requireLast": false -                        }, +                        "multiline": {"delimiter": "semi", "requireLast": true}, +                        "singleline": {"delimiter": "comma", "requireLast": false},                          "multilineDetection": "brackets"                      }                  ], -                "@stylistic/ts/no-extra-parens": [ -                    "error", -                    "all" -                ], -                "@stylistic/ts/no-extra-semi": "error", -                "@stylistic/ts/object-curly-spacing": [ -                    "error", -                    "never" -                ], -                "@stylistic/ts/padding-line-between-statements": "off", -                "@stylistic/ts/quotes": [ -                    "error", -                    "single", -                    "avoid-escape" -                ], -                "@stylistic/ts/semi": "error", -                "@stylistic/ts/space-before-blocks": [ -                    "error", -                    "always" -                ], -                "@stylistic/ts/space-before-function-paren": [ -                    "error", -                    { -                        "anonymous": "never", -                        "named": "never", -                        "asyncArrow": "always" -                    } -                ], -                "@stylistic/ts/space-infix-ops": "error", -                "@stylistic/ts/type-annotation-spacing": "error" +                "@stylistic/no-multiple-empty-lines": ["error", {"max": 1, "maxEOF": 0}], +                "@stylistic/no-extra-parens": ["error", "all"], +                "@stylistic/no-extra-semi": "error", +                "@stylistic/object-curly-spacing": ["error", "never"], +                "@stylistic/padding-line-between-statements": "off", +                "@stylistic/quotes": ["error", "single", "avoid-escape"], +                "@stylistic/semi": "error", +                "@stylistic/space-before-blocks": ["error", "always"], +                "@stylistic/space-before-function-paren": ["error", {"anonymous": "never", "named": "never", "asyncArrow": "always"}], +                "@stylistic/space-infix-ops": "error", +                "@stylistic/type-annotation-spacing": "error"              }          },          {              "files": [                  "*.json"              ], -            "parser": "jsonc-eslint-parser", +            "parser": "jsonc-eslint-parser" +        }, +        { +            "files": [ +                "ext/data/schemas/options-schema.json" +            ],              "rules": { -                "no-multi-spaces": "off" +                "@stylistic/no-multi-spaces": "off"              }          },          { @@ -567,10 +269,7 @@                  "test/data/translator-test-results.json"              ],              "rules": { -                "jsonc/indent": [ -                    "error", -                    2 -                ] +                "jsonc/indent": ["error", 2]              }          },          { @@ -579,12 +278,8 @@                  "test/data/dictionaries/valid-dictionary1/term_bank_2.json"              ],              "rules": { -                "jsonc/array-element-newline": [ -                    "off" -                ], -                "jsonc/object-property-newline": [ -                    "off" -                ] +                "jsonc/array-element-newline": "off", +                "jsonc/object-property-newline": "off"              }          },          { @@ -612,20 +307,12 @@          },          {              "files": [ -                "ext/js/core.js", -                "ext/js/core/extension-error.js", -                "ext/js/**/sandbox/**/*.js", -                "ext/js/language/ja/japanese.js", -                "ext/js/language/ja/japanese-wanakana.js" -            ], -            "env": { -                "webextensions": false -            } -        }, -        { -            "files": [                  "test/**/*.js", -                "dev/**/*.js" +                "dev/**/*.js", +                "integration.spec.js", +                "playwright.config.js", +                "playwright-util.js", +                "visual.spec.js"              ],              "env": {                  "browser": false, @@ -659,11 +346,74 @@          },          {              "files": [ +                "test/**/*.test.js" +            ], +            "plugins": [ +                "vitest" +            ], +            "extends": [ +                "plugin:vitest/recommended" +            ], +            "rules": { +                "vitest/prefer-to-be": "off" +            } +        }, +        { +            "files": [ +                "dev/lib/**/*.js" +            ], +            "extends": [ +                "plugin:@typescript-eslint/disable-type-checked" +            ] +        }, +        { +            "files": [ +                "ext/js/core/api-map.js", +                "ext/js/core/extension-error.js", +                "ext/js/core/json.js", +                "ext/js/data/sandbox/anki-note-data-creator.js", +                "ext/js/dictionary/dictionary-data-util.js", +                "ext/js/display/sandbox/pronunciation-generator.js", +                "ext/js/display/sandbox/structured-content-generator.js", +                "ext/js/dom/sandbox/css-style-applier.js", +                "ext/js/language/ja/japanese.js", +                "ext/js/templates/sandbox/anki-template-renderer-content-manager.js", +                "ext/js/templates/sandbox/anki-template-renderer.js", +                "ext/js/templates/sandbox/template-renderer-frame-api.js", +                "ext/js/templates/sandbox/template-renderer-frame-main.js", +                "ext/js/templates/sandbox/template-renderer-media-provider.js", +                "ext/js/templates/sandbox/template-renderer.js" +            ], +            "env": { +                "webextensions": false +            } +        }, +        { +            "files": [ +                "ext/js/core/event-dispatcher.js",                  "ext/js/core/extension-error.js", -                "ext/js/application.js", +                "ext/js/core/json.js", +                "ext/js/core/logger.js", +                "ext/js/core/to-error.js", +                "ext/js/core/utilities.js", +                "ext/js/data/database.js", +                "ext/js/dictionary/dictionary-database.js", +                "ext/js/dictionary/dictionary-importer.js", +                "ext/js/dictionary/dictionary-worker-handler.js", +                "ext/js/dictionary/dictionary-worker-main.js", +                "ext/js/dictionary/dictionary-worker-media-loader.js", +                "ext/js/media/media-util.js" +            ], +            "env": { +                "browser": false, +                "worker": true +            } +        }, +        { +            "files": [                  "ext/js/accessibility/accessibility-controller.js",                  "ext/js/background/backend.js", -                "ext/js/background/offscreen.js", +                "ext/js/background/background-main.js",                  "ext/js/background/offscreen-proxy.js",                  "ext/js/background/profile-conditions-util.js",                  "ext/js/background/request-builder.js", @@ -672,32 +422,41 @@                  "ext/js/comm/clipboard-monitor.js",                  "ext/js/comm/clipboard-reader.js",                  "ext/js/comm/mecab.js", +                "ext/js/core/api-map.js", +                "ext/js/core/event-dispatcher.js", +                "ext/js/core/event-listener-collection.js", +                "ext/js/core/extension-error.js", +                "ext/js/core/fetch-utilities.js", +                "ext/js/core/json.js", +                "ext/js/core/logger.js", +                "ext/js/core/to-error.js", +                "ext/js/core/utilities.js",                  "ext/js/data/anki-util.js",                  "ext/js/data/database.js",                  "ext/js/data/json-schema.js",                  "ext/js/data/options-util.js",                  "ext/js/data/permissions-util.js",                  "ext/js/data/sandbox/array-buffer-util.js", +                "ext/js/dictionary/dictionary-database.js", +                "ext/js/dom/native-simple-dom-parser.js",                  "ext/js/dom/simple-dom-parser.js",                  "ext/js/extension/environment.js", +                "ext/js/extension/web-extension.js",                  "ext/js/general/cache-map.js",                  "ext/js/general/object-property-accessor.js",                  "ext/js/general/regex-util.js",                  "ext/js/general/text-source-map.js", +                "ext/js/language/ja/japanese-wanakana.js", +                "ext/js/language/ja/japanese.js",                  "ext/js/language/language-transformer.js", -                "ext/js/dictionary/dictionary-database.js", -                "ext/js/dictionary/dictionary-data-util.js", -                "ext/js/language/sandbox/japanese-util.js",                  "ext/js/language/translator.js",                  "ext/js/media/audio-downloader.js",                  "ext/js/media/media-util.js", -                "ext/js/templates/template-patcher.js", -                "ext/js/background/background-main.js" +                "ext/js/templates/template-patcher.js"              ],              "env": {                  "browser": false, -                "serviceworker": true, -                "webextensions": true +                "serviceworker": true              },              "globals": {                  "FileReader": "readonly", @@ -705,77 +464,6 @@                  "crypto": "readonly",                  "AbortController": "readonly"              } -        }, -        { -            "files": [ -                "ext/js/core.js", -                "ext/js/core/extension-error.js", -                "ext/js/data/database.js", -                "ext/js/data/json-schema.js", -                "ext/js/general/cache-map.js", -                "ext/js/dictionary/dictionary-database.js", -                "ext/js/dictionary/dictionary-importer.js", -                "ext/js/dictionary/dictionary-worker-handler.js", -                "ext/js/dictionary/dictionary-worker-media-loader.js", -                "ext/js/media/media-util.js" -            ], -            "env": { -                "browser": false, -                "worker": true, -                "webextensions": true -            } -        }, -        { -            "files": [ -                "playwright.config.js" -            ], -            "env": { -                "browser": false, -                "node": true, -                "webextensions": false -            }, -            "rules": { -                "no-undefined": "off" -            } -        }, -        { -            "files": [ -                "integration.spec.js", -                "playwright-util.js", -                "visual.spec.js" -            ], -            "env": { -                "browser": false, -                "node": true, -                "webextensions": false -            }, -            "rules": { -                "no-undefined": "off", -                "no-empty-pattern": "off" -            } -        }, -        { -            "files": [ -                "test/**/*.test.js" -            ], -            "plugins": [ -                "vitest" -            ], -            "extends": [ -                "plugin:vitest/recommended" -            ], -            "rules": { -                "vitest/prefer-to-be": "off" -            }, -            "env": {} -        }, -        { -            "files": [ -                "dev/lib/**/*.js" -            ], -            "extends": [ -                "plugin:@typescript-eslint/disable-type-checked" -            ]          }      ]  } diff --git a/.vscode/settings.json b/.vscode/settings.json index 5aa6512c..bb9b3c80 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@  {      "markdown.extension.toc.levels": "1..3", -    "[javascript]": { +    "[javascript][json][jsonc]": {          "editor.codeActionsOnSave": {              "source.addMissingImports": "explicit",              "source.organizeImports": "explicit", @@ -22,7 +22,9 @@      "editor.insertSpaces": true,      "eslint.validate": [          "javascript", -        "typescript" +        "typescript", +        "json", +        "jsonc"      ],      "files.eol": "\n",      "files.trimTrailingWhitespace": true, diff --git a/ext/js/app/frontend.js b/ext/js/app/frontend.js index 5f412340..27e7700e 100644 --- a/ext/js/app/frontend.js +++ b/ext/js/app/frontend.js @@ -19,7 +19,7 @@  import {createApiMap, invokeApiMapHandler} from '../core/api-map.js';  import {EventListenerCollection} from '../core/event-listener-collection.js';  import {log} from '../core/logger.js'; -import {promiseAnimationFrame} from '../core/utilities.js'; +import {promiseAnimationFrame} from '../core/promise-animation-frame.js';  import {DocumentUtil} from '../dom/document-util.js';  import {TextSourceElement} from '../dom/text-source-element.js';  import {TextSourceGenerator} from '../dom/text-source-generator.js'; @@ -115,7 +115,7 @@ export class Frontend {          /** @type {?import('settings').OptionsContext} */          this._optionsContextOverride = null; -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          /** @type {import('application').ApiMap} */          this._runtimeApiMap = createApiMap([              ['frontendRequestReadyBroadcast',   this._onMessageRequestFrontendReadyBroadcast.bind(this)], @@ -127,7 +127,7 @@ export class Frontend {              ['scanSelectedText', this._onActionScanSelectedText.bind(this)],              ['scanTextAtCaret',  this._onActionScanTextAtCaret.bind(this)]          ]); -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */      }      /** @@ -187,7 +187,7 @@ export class Frontend {          this._textScanner.on('searchEmpty', this._onSearchEmpty.bind(this));          this._textScanner.on('searchError', this._onSearchError.bind(this)); -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          this._application.crossFrame.registerHandlers([              ['frontendClosePopup',       this._onApiClosePopup.bind(this)],              ['frontendCopySelection',    this._onApiCopySelection.bind(this)], @@ -195,7 +195,7 @@ export class Frontend {              ['frontendGetPopupInfo',     this._onApiGetPopupInfo.bind(this)],              ['frontendGetPageInfo',      this._onApiGetPageInfo.bind(this)]          ]); -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */          this._prepareSiteSpecific();          this._updateContentScale(); diff --git a/ext/js/app/popup-factory.js b/ext/js/app/popup-factory.js index 1b7d21db..c5187291 100644 --- a/ext/js/app/popup-factory.js +++ b/ext/js/app/popup-factory.js @@ -49,7 +49,7 @@ export class PopupFactory {       */      prepare() {          this._frameOffsetForwarder.prepare(); -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          this._application.crossFrame.registerHandlers([              ['popupFactoryGetOrCreatePopup',     this._onApiGetOrCreatePopup.bind(this)],              ['popupFactorySetOptionsContext',    this._onApiSetOptionsContext.bind(this)], @@ -67,7 +67,7 @@ export class PopupFactory {              ['popupFactoryGetFrameSize',         this._onApiGetFrameSize.bind(this)],              ['popupFactorySetFrameSize',         this._onApiSetFrameSize.bind(this)]          ]); -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */      }      /** diff --git a/ext/js/application.js b/ext/js/application.js index 13938aa8..227e9d5e 100644 --- a/ext/js/application.js +++ b/ext/js/application.js @@ -93,7 +93,7 @@ export class Application extends EventDispatcher {          /** @type {boolean} */          this._isReady = false; -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          /** @type {import('application').ApiMap} */          this._apiMap = createApiMap([              ['applicationIsReady',         this._onMessageIsReady.bind(this)], @@ -102,7 +102,7 @@ export class Application extends EventDispatcher {              ['applicationDatabaseUpdated', this._onMessageDatabaseUpdated.bind(this)],              ['applicationZoomChanged',     this._onMessageZoomChanged.bind(this)]          ]); -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */      }      /** @type {WebExtension} */ diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index 85acac89..909faf29 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -139,7 +139,7 @@ export class Backend {          /** @type {Map<string, (() => void)[]>} */          this._applicationReadyHandlers = new Map(); -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          /** @type {import('api').ApiMap} */          this._apiMap = createApiMap([              ['applicationReady',             this._onApiApplicationReady.bind(this)], @@ -185,7 +185,7 @@ export class Backend {              ['findAnkiNotes',                this._onApiFindAnkiNotes.bind(this)],              ['openCrossFramePort',           this._onApiOpenCrossFramePort.bind(this)]          ]); -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */          /** @type {Map<string, (params?: import('core').SerializableObject) => void>} */          this._commandHandlers = new Map(/** @type {[name: string, handler: (params?: import('core').SerializableObject) => void][]} */ ([ diff --git a/ext/js/background/offscreen.js b/ext/js/background/offscreen.js index b203e326..754db517 100644 --- a/ext/js/background/offscreen.js +++ b/ext/js/background/offscreen.js @@ -39,14 +39,13 @@ export class Offscreen {          });          /** @type {ClipboardReader} */          this._clipboardReader = new ClipboardReader({ -            // eslint-disable-next-line no-undef              document: (typeof document === 'object' && document !== null ? document : null),              pasteTargetSelector: '#clipboard-paste-target',              richContentPasteTargetSelector: '#clipboard-rich-content-paste-target'          }); -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          /** @type {import('offscreen').ApiMap} */          this._apiMap = createApiMap([              ['clipboardGetTextOffscreen',    this._getTextHandler.bind(this)], @@ -62,7 +61,7 @@ export class Offscreen {              ['getTermFrequenciesOffscreen',  this._getTermFrequenciesHandler.bind(this)],              ['clearDatabaseCachesOffscreen', this._clearDatabaseCachesHandler.bind(this)]          ]); -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */          /** @type {?Promise<void>} */          this._prepareDatabasePromise = null; diff --git a/ext/js/core/promise-animation-frame.js b/ext/js/core/promise-animation-frame.js new file mode 100644 index 00000000..0bcd6970 --- /dev/null +++ b/ext/js/core/promise-animation-frame.js @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023-2024  Yomitan Authors + * Copyright (C) 2019-2022  Yomichan 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 <https://www.gnu.org/licenses/>. + */ + +/** + * Creates a promise that will resolve after the next animation frame, using `requestAnimationFrame`. + * @param {number} [timeout] A maximum duration (in milliseconds) to wait until the promise resolves. If null or omitted, no timeout is used. + * @returns {Promise<{time: number, timeout: boolean}>} A promise that is resolved with `{time, timeout}`, where `time` is the timestamp from `requestAnimationFrame`, + *   and `timeout` is a boolean indicating whether the cause was a timeout or not. + * @throws The promise throws an error if animation is not supported in this context, such as in a service worker. + */ +export function promiseAnimationFrame(timeout) { +    return new Promise((resolve, reject) => { +        if (typeof cancelAnimationFrame !== 'function' || typeof requestAnimationFrame !== 'function') { +            reject(new Error('Animation not supported in this context')); +            return; +        } + +        /** @type {?import('core').Timeout} */ +        let timer = null; +        /** @type {?number} */ +        let frameRequest = null; +        /** +         * @param {number} time +         */ +        const onFrame = (time) => { +            frameRequest = null; +            if (timer !== null) { +                clearTimeout(timer); +                timer = null; +            } +            resolve({time, timeout: false}); +        }; +        const onTimeout = () => { +            timer = null; +            if (frameRequest !== null) { +                cancelAnimationFrame(frameRequest); +                frameRequest = null; +            } +            resolve({time: performance.now(), timeout: true}); +        }; + +        frameRequest = requestAnimationFrame(onFrame); +        if (typeof timeout === 'number') { +            timer = setTimeout(onTimeout, timeout); +        } +    }); +} diff --git a/ext/js/core/utilities.js b/ext/js/core/utilities.js index 784acdaf..1b785e79 100644 --- a/ext/js/core/utilities.js +++ b/ext/js/core/utilities.js @@ -270,48 +270,3 @@ export function deferPromise() {  export function promiseTimeout(delay) {      return delay <= 0 ? Promise.resolve() : new Promise((resolve) => { setTimeout(resolve, delay); });  } - -/** - * Creates a promise that will resolve after the next animation frame, using `requestAnimationFrame`. - * @param {number} [timeout] A maximum duration (in milliseconds) to wait until the promise resolves. If null or omitted, no timeout is used. - * @returns {Promise<{time: number, timeout: boolean}>} A promise that is resolved with `{time, timeout}`, where `time` is the timestamp from `requestAnimationFrame`, - *   and `timeout` is a boolean indicating whether the cause was a timeout or not. - * @throws The promise throws an error if animation is not supported in this context, such as in a service worker. - */ -export function promiseAnimationFrame(timeout) { -    return new Promise((resolve, reject) => { -        if (typeof cancelAnimationFrame !== 'function' || typeof requestAnimationFrame !== 'function') { -            reject(new Error('Animation not supported in this context')); -            return; -        } - -        /** @type {?import('core').Timeout} */ -        let timer = null; -        /** @type {?number} */ -        let frameRequest = null; -        /** -         * @param {number} time -         */ -        const onFrame = (time) => { -            frameRequest = null; -            if (timer !== null) { -                clearTimeout(timer); -                timer = null; -            } -            resolve({time, timeout: false}); -        }; -        const onTimeout = () => { -            timer = null; -            if (frameRequest !== null) { -                cancelAnimationFrame(frameRequest); -                frameRequest = null; -            } -            resolve({time: performance.now(), timeout: true}); -        }; - -        frameRequest = requestAnimationFrame(onFrame); -        if (typeof timeout === 'number') { -            timer = setTimeout(onTimeout, timeout); -        } -    }); -} diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js index 3f3a5ab8..d93927a7 100644 --- a/ext/js/data/options-util.js +++ b/ext/js/data/options-util.js @@ -687,7 +687,7 @@ export class OptionsUtil {          const rawPattern1 = '{{~#if definitionTags~}}<i>({{#each definitionTags}}{{name}}{{#unless @last}}, {{/unless}}{{/each}})</i> {{/if~}}';          const pattern1 = new RegExp(`((\r?\n)?[ \t]*)${escapeRegExp(rawPattern1)}`, 'g');          const replacement1 = ( -        // eslint-disable-next-line indent +        // eslint-disable-next-line @stylistic/indent  `{{~#scope~}}      {{~#set "any" false}}{{/set~}}      {{~#if definitionTags~}}{{#each definitionTags~}} @@ -771,7 +771,7 @@ export class OptionsUtil {              };              delete profile.options.anki.sentenceExt;              profile.options.general.popupActionBarLocation = 'top'; -            /* eslint-disable no-multi-spaces */ +            /* eslint-disable @stylistic/no-multi-spaces */              profile.options.inputs = {                  hotkeys: [                      {action: 'close',             key: 'Escape',    modifiers: [],       scopes: ['popup'], enabled: true}, @@ -792,7 +792,7 @@ export class OptionsUtil {                      {action: 'copyHostSelection', key: 'KeyC',      modifiers: ['ctrl'], scopes: ['popup'], enabled: true}                  ]              }; -            /* eslint-enable no-multi-spaces */ +            /* eslint-enable @stylistic/no-multi-spaces */              profile.options.anki.suspendNewCards = false;              profile.options.popupWindow = {                  width: profile.options.general.popupWidth, diff --git a/ext/js/display/display-anki.js b/ext/js/display/display-anki.js index c19cfa22..665521dd 100644 --- a/ext/js/display/display-anki.js +++ b/ext/js/display/display-anki.js @@ -109,14 +109,14 @@ export class DisplayAnki {      /** */      prepare() {          this._noteContext = this._getNoteContext(); -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          this._display.hotkeyHandler.registerActions([              ['addNoteKanji',      () => { this._tryAddAnkiNoteForSelectedEntry('kanji'); }],              ['addNoteTermKanji',  () => { this._tryAddAnkiNoteForSelectedEntry('term-kanji'); }],              ['addNoteTermKana',   () => { this._tryAddAnkiNoteForSelectedEntry('term-kana'); }],              ['viewNote',          this._viewNoteForSelectedEntry.bind(this)]          ]); -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */          this._display.on('optionsUpdated', this._onOptionsUpdated.bind(this));          this._display.on('contentClear', this._onContentClear.bind(this));          this._display.on('contentUpdateStart', this._onContentUpdateStart.bind(this)); diff --git a/ext/js/display/display-audio.js b/ext/js/display/display-audio.js index 4acd6494..deaa0976 100644 --- a/ext/js/display/display-audio.js +++ b/ext/js/display/display-audio.js @@ -82,7 +82,7 @@ export class DisplayAudio {      /** */      prepare() {          this._audioSystem.prepare(); -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          this._display.hotkeyHandler.registerActions([              ['playAudio',           this._onHotkeyActionPlayAudio.bind(this)],              ['playAudioFromSource', this._onHotkeyActionPlayAudioFromSource.bind(this)] @@ -90,7 +90,7 @@ export class DisplayAudio {          this._display.registerDirectMessageHandlers([              ['displayAudioClearAutoPlayTimer', this._onMessageClearAutoPlayTimer.bind(this)]          ]); -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */          this._display.on('optionsUpdated', this._onOptionsUpdated.bind(this));          this._display.on('contentClear', this._onContentClear.bind(this));          this._display.on('contentUpdateEntry', this._onContentUpdateEntry.bind(this)); diff --git a/ext/js/display/display.js b/ext/js/display/display.js index 676f1a4f..f05feac8 100644 --- a/ext/js/display/display.js +++ b/ext/js/display/display.js @@ -200,7 +200,7 @@ export class Display extends EventDispatcher {          /** @type {ThemeController} */          this._themeController = new ThemeController(document.documentElement); -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          this._hotkeyHandler.registerActions([              ['close',             () => { this._onHotkeyClose(); }],              ['nextEntry',         this._onHotkeyActionMoveRelative.bind(this, 1)], @@ -224,7 +224,7 @@ export class Display extends EventDispatcher {          this.registerWindowMessageHandlers([              ['displayExtensionUnloaded', this._onMessageExtensionUnloaded.bind(this)]          ]); -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */      }      /** @type {import('../application.js').Application} */ diff --git a/ext/js/pages/settings/keyboard-shortcuts-controller.js b/ext/js/pages/settings/keyboard-shortcuts-controller.js index 9392f768..24d34d5b 100644 --- a/ext/js/pages/settings/keyboard-shortcuts-controller.js +++ b/ext/js/pages/settings/keyboard-shortcuts-controller.js @@ -45,7 +45,7 @@ export class KeyboardShortcutController {          this._stringComparer = new Intl.Collator('en-US'); // Invariant locale          /** @type {HTMLElement} */          this._scrollContainer = querySelectorNotNull(document, '#keyboard-shortcuts-modal .modal-body'); -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          /** @type {Map<string, import('keyboard-shortcut-controller').ActionDetails>} */          this._actionDetails = new Map([              ['',                                 {scopes: new Set()}], @@ -70,7 +70,7 @@ export class KeyboardShortcutController {              ['scanTextAtCaret',                  {scopes: new Set(['web'])}],              ['toggleOption',                     {scopes: new Set(['popup', 'search']), argument: {template: 'hotkey-argument-setting-path', default: ''}}]          ]); -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */      }      /** @type {import('./settings-controller.js').SettingsController} */ diff --git a/ext/js/pages/settings/popup-preview-frame.js b/ext/js/pages/settings/popup-preview-frame.js index e9cfa541..1ad4859b 100644 --- a/ext/js/pages/settings/popup-preview-frame.js +++ b/ext/js/pages/settings/popup-preview-frame.js @@ -59,7 +59,7 @@ export class PopupPreviewFrame {          /** @type {string} */          this._targetOrigin = chrome.runtime.getURL('/').replace(/\/$/, ''); -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          /** @type {Map<string, (params: import('core').SerializableObjectAny) => void>} */          this._windowMessageHandlers = new Map(/** @type {[key: string, handler: (params: import('core').SerializableObjectAny) => void][]} */ ([              ['PopupPreviewFrame.setText',              this._onSetText.bind(this)], @@ -67,7 +67,7 @@ export class PopupPreviewFrame {              ['PopupPreviewFrame.setCustomOuterCss',    this._setCustomOuterCss.bind(this)],              ['PopupPreviewFrame.updateOptionsContext', this._updateOptionsContext.bind(this)]          ])); -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */      }      /** */ diff --git a/ext/js/pages/settings/profile-conditions-ui.js b/ext/js/pages/settings/profile-conditions-ui.js index d07751fb..5801af17 100644 --- a/ext/js/pages/settings/profile-conditions-ui.js +++ b/ext/js/pages/settings/profile-conditions-ui.js @@ -51,7 +51,7 @@ export class ProfileConditionsUI extends EventDispatcher {          const normalizeInteger = this._normalizeInteger.bind(this);          const validateFlags = this._validateFlags.bind(this);          const normalizeFlags = this._normalizeFlags.bind(this); -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          /** @type {Map<import('profile-conditions-ui').DescriptorType, import('profile-conditions-ui').Descriptor>} */          this._descriptors = new Map([              [ @@ -107,7 +107,7 @@ export class ProfileConditionsUI extends EventDispatcher {                  }              ]          ]); -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */          /** @type {Set<string>} */          this._validFlags = new Set([              'clipboard' diff --git a/ext/js/templates/sandbox/anki-template-renderer.js b/ext/js/templates/sandbox/anki-template-renderer.js index 8ece8e24..135200da 100644 --- a/ext/js/templates/sandbox/anki-template-renderer.js +++ b/ext/js/templates/sandbox/anki-template-renderer.js @@ -68,7 +68,7 @@ export class AnkiTemplateRenderer {       * Prepares the data that is necessary before the template renderer can be safely used.       */      async prepare() { -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          this._templateRenderer.registerHelpers([              ['dumpObject',       this._dumpObject.bind(this)],              ['furigana',         this._furigana.bind(this)], @@ -98,7 +98,7 @@ export class AnkiTemplateRenderer {              ['hiragana',         this._hiragana.bind(this)],              ['katakana',         this._katakana.bind(this)]          ]); -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */          this._templateRenderer.registerDataType('ankiNote', {              modifier: ({marker, commonData}) => createAnkiNoteData(marker, commonData),              composeData: ({marker}, commonData) => ({marker, commonData}) diff --git a/package-lock.json b/package-lock.json index 74901853..8b27c630 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@              "devDependencies": {                  "@codspeed/vitest-plugin": "^3.1.0",                  "@playwright/test": "^1.39.0", -                "@stylistic/eslint-plugin-ts": "^1.5.1", +                "@stylistic/eslint-plugin": "^1.5.4",                  "@stylistic/stylelint-plugin": "^2.0.0",                  "@types/assert": "^1.5.10",                  "@types/browserify": "^12.0.40", @@ -1386,13 +1386,31 @@                  "url": "https://github.com/sindresorhus/is?sponsor=1"              }          }, +        "node_modules/@stylistic/eslint-plugin": { +            "version": "1.5.4", +            "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-1.5.4.tgz", +            "integrity": "sha512-zWPXr+O67GC9KDAFkbL1U9UVqE6Iv69YMKhkIECCmE0GvClUJwdfsimm4XebEDondV7kfjMrTDZaYfrI5aS0Jg==", +            "dev": true, +            "dependencies": { +                "@stylistic/eslint-plugin-js": "1.5.4", +                "@stylistic/eslint-plugin-jsx": "1.5.4", +                "@stylistic/eslint-plugin-plus": "1.5.4", +                "@stylistic/eslint-plugin-ts": "1.5.4" +            }, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "peerDependencies": { +                "eslint": ">=8.40.0" +            } +        },          "node_modules/@stylistic/eslint-plugin-js": { -            "version": "1.5.1", -            "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.5.1.tgz", -            "integrity": "sha512-iZF0rF+uOhAmOJYOJx1Yvmm3CZ1uz9n0SRd9dpBYHA3QAvfABUORh9LADWwZCigjHJkp2QbCZelGFJGwGz7Siw==", +            "version": "1.5.4", +            "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.5.4.tgz", +            "integrity": "sha512-3ctWb3NvJNV1MsrZN91cYp2EGInLPSoZKphXIbIRx/zjZxKwLDr9z4LMOWtqjq14li/OgqUUcMq5pj8fgbLoTw==",              "dev": true,              "dependencies": { -                "acorn": "^8.11.2", +                "acorn": "^8.11.3",                  "escape-string-regexp": "^4.0.0",                  "eslint-visitor-keys": "^3.4.3",                  "espree": "^9.6.1" @@ -1404,14 +1422,166 @@                  "eslint": ">=8.40.0"              }          }, +        "node_modules/@stylistic/eslint-plugin-jsx": { +            "version": "1.5.4", +            "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-jsx/-/eslint-plugin-jsx-1.5.4.tgz", +            "integrity": "sha512-JUfrpCkeBCqt1IZ4QsP4WgxGza4PhK4LPbc0VnCjHKygl+rgqoDAovqOuzFJ49wJ4Ix3r6OIHFuwiBGswZEVvg==", +            "dev": true, +            "dependencies": { +                "@stylistic/eslint-plugin-js": "^1.5.4", +                "estraverse": "^5.3.0" +            }, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "peerDependencies": { +                "eslint": ">=8.40.0" +            } +        }, +        "node_modules/@stylistic/eslint-plugin-plus": { +            "version": "1.5.4", +            "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-plus/-/eslint-plugin-plus-1.5.4.tgz", +            "integrity": "sha512-dI0Cs5vYX/0uMhQDY+NK0cKQ0Pe9B6jWYxd0Ndud+mNloDaVLrsmJocK4zn+YfhGEDs1E4Nk5uAPZEumIpDuSg==", +            "dev": true, +            "dependencies": { +                "@typescript-eslint/utils": "^6.19.0" +            }, +            "peerDependencies": { +                "eslint": "*" +            } +        }, +        "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/scope-manager": { +            "version": "6.20.0", +            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", +            "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", +            "dev": true, +            "dependencies": { +                "@typescript-eslint/types": "6.20.0", +                "@typescript-eslint/visitor-keys": "6.20.0" +            }, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "funding": { +                "type": "opencollective", +                "url": "https://opencollective.com/typescript-eslint" +            } +        }, +        "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/types": { +            "version": "6.20.0", +            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", +            "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", +            "dev": true, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "funding": { +                "type": "opencollective", +                "url": "https://opencollective.com/typescript-eslint" +            } +        }, +        "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/typescript-estree": { +            "version": "6.20.0", +            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", +            "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", +            "dev": true, +            "dependencies": { +                "@typescript-eslint/types": "6.20.0", +                "@typescript-eslint/visitor-keys": "6.20.0", +                "debug": "^4.3.4", +                "globby": "^11.1.0", +                "is-glob": "^4.0.3", +                "minimatch": "9.0.3", +                "semver": "^7.5.4", +                "ts-api-utils": "^1.0.1" +            }, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "funding": { +                "type": "opencollective", +                "url": "https://opencollective.com/typescript-eslint" +            }, +            "peerDependenciesMeta": { +                "typescript": { +                    "optional": true +                } +            } +        }, +        "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/utils": { +            "version": "6.20.0", +            "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", +            "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", +            "dev": true, +            "dependencies": { +                "@eslint-community/eslint-utils": "^4.4.0", +                "@types/json-schema": "^7.0.12", +                "@types/semver": "^7.5.0", +                "@typescript-eslint/scope-manager": "6.20.0", +                "@typescript-eslint/types": "6.20.0", +                "@typescript-eslint/typescript-estree": "6.20.0", +                "semver": "^7.5.4" +            }, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "funding": { +                "type": "opencollective", +                "url": "https://opencollective.com/typescript-eslint" +            }, +            "peerDependencies": { +                "eslint": "^7.0.0 || ^8.0.0" +            } +        }, +        "node_modules/@stylistic/eslint-plugin-plus/node_modules/@typescript-eslint/visitor-keys": { +            "version": "6.20.0", +            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", +            "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", +            "dev": true, +            "dependencies": { +                "@typescript-eslint/types": "6.20.0", +                "eslint-visitor-keys": "^3.4.1" +            }, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "funding": { +                "type": "opencollective", +                "url": "https://opencollective.com/typescript-eslint" +            } +        }, +        "node_modules/@stylistic/eslint-plugin-plus/node_modules/brace-expansion": { +            "version": "2.0.1", +            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", +            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", +            "dev": true, +            "dependencies": { +                "balanced-match": "^1.0.0" +            } +        }, +        "node_modules/@stylistic/eslint-plugin-plus/node_modules/minimatch": { +            "version": "9.0.3", +            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", +            "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", +            "dev": true, +            "dependencies": { +                "brace-expansion": "^2.0.1" +            }, +            "engines": { +                "node": ">=16 || 14 >=14.17" +            }, +            "funding": { +                "url": "https://github.com/sponsors/isaacs" +            } +        },          "node_modules/@stylistic/eslint-plugin-ts": { -            "version": "1.5.1", -            "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-1.5.1.tgz", -            "integrity": "sha512-oXM1V7Jp8G9+udxQTy+Igo79LR2e5HXiWqlA/3v+/PAqWxniR9nJqJSBjtQKJTPsGplDqn/ASpHUOETP4EI/4A==", +            "version": "1.5.4", +            "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-1.5.4.tgz", +            "integrity": "sha512-NZDFVIlVNjuPvhT+0Cidm5IS3emtx338xbJTqs2xfOVRDGTpYwRHhNVEGa1rFOpYHmv0sAj6+OXbMDn7ul0K/g==",              "dev": true,              "dependencies": { -                "@stylistic/eslint-plugin-js": "1.5.1", -                "@typescript-eslint/utils": "^6.13.2" +                "@stylistic/eslint-plugin-js": "1.5.4", +                "@typescript-eslint/utils": "^6.19.0"              },              "engines": {                  "node": "^16.0.0 || >=18.0.0" @@ -1420,6 +1590,130 @@                  "eslint": ">=8.40.0"              }          }, +        "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/scope-manager": { +            "version": "6.20.0", +            "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", +            "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", +            "dev": true, +            "dependencies": { +                "@typescript-eslint/types": "6.20.0", +                "@typescript-eslint/visitor-keys": "6.20.0" +            }, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "funding": { +                "type": "opencollective", +                "url": "https://opencollective.com/typescript-eslint" +            } +        }, +        "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/types": { +            "version": "6.20.0", +            "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", +            "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", +            "dev": true, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "funding": { +                "type": "opencollective", +                "url": "https://opencollective.com/typescript-eslint" +            } +        }, +        "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/typescript-estree": { +            "version": "6.20.0", +            "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", +            "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", +            "dev": true, +            "dependencies": { +                "@typescript-eslint/types": "6.20.0", +                "@typescript-eslint/visitor-keys": "6.20.0", +                "debug": "^4.3.4", +                "globby": "^11.1.0", +                "is-glob": "^4.0.3", +                "minimatch": "9.0.3", +                "semver": "^7.5.4", +                "ts-api-utils": "^1.0.1" +            }, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "funding": { +                "type": "opencollective", +                "url": "https://opencollective.com/typescript-eslint" +            }, +            "peerDependenciesMeta": { +                "typescript": { +                    "optional": true +                } +            } +        }, +        "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/utils": { +            "version": "6.20.0", +            "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", +            "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", +            "dev": true, +            "dependencies": { +                "@eslint-community/eslint-utils": "^4.4.0", +                "@types/json-schema": "^7.0.12", +                "@types/semver": "^7.5.0", +                "@typescript-eslint/scope-manager": "6.20.0", +                "@typescript-eslint/types": "6.20.0", +                "@typescript-eslint/typescript-estree": "6.20.0", +                "semver": "^7.5.4" +            }, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "funding": { +                "type": "opencollective", +                "url": "https://opencollective.com/typescript-eslint" +            }, +            "peerDependencies": { +                "eslint": "^7.0.0 || ^8.0.0" +            } +        }, +        "node_modules/@stylistic/eslint-plugin-ts/node_modules/@typescript-eslint/visitor-keys": { +            "version": "6.20.0", +            "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", +            "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", +            "dev": true, +            "dependencies": { +                "@typescript-eslint/types": "6.20.0", +                "eslint-visitor-keys": "^3.4.1" +            }, +            "engines": { +                "node": "^16.0.0 || >=18.0.0" +            }, +            "funding": { +                "type": "opencollective", +                "url": "https://opencollective.com/typescript-eslint" +            } +        }, +        "node_modules/@stylistic/eslint-plugin-ts/node_modules/brace-expansion": { +            "version": "2.0.1", +            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", +            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", +            "dev": true, +            "dependencies": { +                "balanced-match": "^1.0.0" +            } +        }, +        "node_modules/@stylistic/eslint-plugin-ts/node_modules/minimatch": { +            "version": "9.0.3", +            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", +            "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", +            "dev": true, +            "dependencies": { +                "brace-expansion": "^2.0.1" +            }, +            "engines": { +                "node": ">=16 || 14 >=14.17" +            }, +            "funding": { +                "url": "https://github.com/sponsors/isaacs" +            } +        },          "node_modules/@stylistic/stylelint-plugin": {              "version": "2.0.0",              "resolved": "https://registry.npmjs.org/@stylistic/stylelint-plugin/-/stylelint-plugin-2.0.0.tgz", @@ -1950,9 +2244,9 @@              }          },          "node_modules/acorn": { -            "version": "8.11.2", -            "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", -            "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", +            "version": "8.11.3", +            "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", +            "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",              "dev": true,              "bin": {                  "acorn": "bin/acorn" @@ -7865,26 +8159,211 @@              "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==",              "dev": true          }, +        "@stylistic/eslint-plugin": { +            "version": "1.5.4", +            "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-1.5.4.tgz", +            "integrity": "sha512-zWPXr+O67GC9KDAFkbL1U9UVqE6Iv69YMKhkIECCmE0GvClUJwdfsimm4XebEDondV7kfjMrTDZaYfrI5aS0Jg==", +            "dev": true, +            "requires": { +                "@stylistic/eslint-plugin-js": "1.5.4", +                "@stylistic/eslint-plugin-jsx": "1.5.4", +                "@stylistic/eslint-plugin-plus": "1.5.4", +                "@stylistic/eslint-plugin-ts": "1.5.4" +            } +        },          "@stylistic/eslint-plugin-js": { -            "version": "1.5.1", -            "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.5.1.tgz", -            "integrity": "sha512-iZF0rF+uOhAmOJYOJx1Yvmm3CZ1uz9n0SRd9dpBYHA3QAvfABUORh9LADWwZCigjHJkp2QbCZelGFJGwGz7Siw==", +            "version": "1.5.4", +            "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.5.4.tgz", +            "integrity": "sha512-3ctWb3NvJNV1MsrZN91cYp2EGInLPSoZKphXIbIRx/zjZxKwLDr9z4LMOWtqjq14li/OgqUUcMq5pj8fgbLoTw==",              "dev": true,              "requires": { -                "acorn": "^8.11.2", +                "acorn": "^8.11.3",                  "escape-string-regexp": "^4.0.0",                  "eslint-visitor-keys": "^3.4.3",                  "espree": "^9.6.1"              }          }, +        "@stylistic/eslint-plugin-jsx": { +            "version": "1.5.4", +            "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-jsx/-/eslint-plugin-jsx-1.5.4.tgz", +            "integrity": "sha512-JUfrpCkeBCqt1IZ4QsP4WgxGza4PhK4LPbc0VnCjHKygl+rgqoDAovqOuzFJ49wJ4Ix3r6OIHFuwiBGswZEVvg==", +            "dev": true, +            "requires": { +                "@stylistic/eslint-plugin-js": "^1.5.4", +                "estraverse": "^5.3.0" +            } +        }, +        "@stylistic/eslint-plugin-plus": { +            "version": "1.5.4", +            "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-plus/-/eslint-plugin-plus-1.5.4.tgz", +            "integrity": "sha512-dI0Cs5vYX/0uMhQDY+NK0cKQ0Pe9B6jWYxd0Ndud+mNloDaVLrsmJocK4zn+YfhGEDs1E4Nk5uAPZEumIpDuSg==", +            "dev": true, +            "requires": { +                "@typescript-eslint/utils": "^6.19.0" +            }, +            "dependencies": { +                "@typescript-eslint/scope-manager": { +                    "version": "6.20.0", +                    "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", +                    "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", +                    "dev": true, +                    "requires": { +                        "@typescript-eslint/types": "6.20.0", +                        "@typescript-eslint/visitor-keys": "6.20.0" +                    } +                }, +                "@typescript-eslint/types": { +                    "version": "6.20.0", +                    "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", +                    "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", +                    "dev": true +                }, +                "@typescript-eslint/typescript-estree": { +                    "version": "6.20.0", +                    "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", +                    "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", +                    "dev": true, +                    "requires": { +                        "@typescript-eslint/types": "6.20.0", +                        "@typescript-eslint/visitor-keys": "6.20.0", +                        "debug": "^4.3.4", +                        "globby": "^11.1.0", +                        "is-glob": "^4.0.3", +                        "minimatch": "9.0.3", +                        "semver": "^7.5.4", +                        "ts-api-utils": "^1.0.1" +                    } +                }, +                "@typescript-eslint/utils": { +                    "version": "6.20.0", +                    "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", +                    "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", +                    "dev": true, +                    "requires": { +                        "@eslint-community/eslint-utils": "^4.4.0", +                        "@types/json-schema": "^7.0.12", +                        "@types/semver": "^7.5.0", +                        "@typescript-eslint/scope-manager": "6.20.0", +                        "@typescript-eslint/types": "6.20.0", +                        "@typescript-eslint/typescript-estree": "6.20.0", +                        "semver": "^7.5.4" +                    } +                }, +                "@typescript-eslint/visitor-keys": { +                    "version": "6.20.0", +                    "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", +                    "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", +                    "dev": true, +                    "requires": { +                        "@typescript-eslint/types": "6.20.0", +                        "eslint-visitor-keys": "^3.4.1" +                    } +                }, +                "brace-expansion": { +                    "version": "2.0.1", +                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", +                    "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", +                    "dev": true, +                    "requires": { +                        "balanced-match": "^1.0.0" +                    } +                }, +                "minimatch": { +                    "version": "9.0.3", +                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", +                    "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", +                    "dev": true, +                    "requires": { +                        "brace-expansion": "^2.0.1" +                    } +                } +            } +        },          "@stylistic/eslint-plugin-ts": { -            "version": "1.5.1", -            "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-1.5.1.tgz", -            "integrity": "sha512-oXM1V7Jp8G9+udxQTy+Igo79LR2e5HXiWqlA/3v+/PAqWxniR9nJqJSBjtQKJTPsGplDqn/ASpHUOETP4EI/4A==", +            "version": "1.5.4", +            "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-1.5.4.tgz", +            "integrity": "sha512-NZDFVIlVNjuPvhT+0Cidm5IS3emtx338xbJTqs2xfOVRDGTpYwRHhNVEGa1rFOpYHmv0sAj6+OXbMDn7ul0K/g==",              "dev": true,              "requires": { -                "@stylistic/eslint-plugin-js": "1.5.1", -                "@typescript-eslint/utils": "^6.13.2" +                "@stylistic/eslint-plugin-js": "1.5.4", +                "@typescript-eslint/utils": "^6.19.0" +            }, +            "dependencies": { +                "@typescript-eslint/scope-manager": { +                    "version": "6.20.0", +                    "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", +                    "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", +                    "dev": true, +                    "requires": { +                        "@typescript-eslint/types": "6.20.0", +                        "@typescript-eslint/visitor-keys": "6.20.0" +                    } +                }, +                "@typescript-eslint/types": { +                    "version": "6.20.0", +                    "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", +                    "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", +                    "dev": true +                }, +                "@typescript-eslint/typescript-estree": { +                    "version": "6.20.0", +                    "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", +                    "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", +                    "dev": true, +                    "requires": { +                        "@typescript-eslint/types": "6.20.0", +                        "@typescript-eslint/visitor-keys": "6.20.0", +                        "debug": "^4.3.4", +                        "globby": "^11.1.0", +                        "is-glob": "^4.0.3", +                        "minimatch": "9.0.3", +                        "semver": "^7.5.4", +                        "ts-api-utils": "^1.0.1" +                    } +                }, +                "@typescript-eslint/utils": { +                    "version": "6.20.0", +                    "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", +                    "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", +                    "dev": true, +                    "requires": { +                        "@eslint-community/eslint-utils": "^4.4.0", +                        "@types/json-schema": "^7.0.12", +                        "@types/semver": "^7.5.0", +                        "@typescript-eslint/scope-manager": "6.20.0", +                        "@typescript-eslint/types": "6.20.0", +                        "@typescript-eslint/typescript-estree": "6.20.0", +                        "semver": "^7.5.4" +                    } +                }, +                "@typescript-eslint/visitor-keys": { +                    "version": "6.20.0", +                    "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", +                    "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", +                    "dev": true, +                    "requires": { +                        "@typescript-eslint/types": "6.20.0", +                        "eslint-visitor-keys": "^3.4.1" +                    } +                }, +                "brace-expansion": { +                    "version": "2.0.1", +                    "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", +                    "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", +                    "dev": true, +                    "requires": { +                        "balanced-match": "^1.0.0" +                    } +                }, +                "minimatch": { +                    "version": "9.0.3", +                    "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", +                    "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", +                    "dev": true, +                    "requires": { +                        "brace-expansion": "^2.0.1" +                    } +                }              }          },          "@stylistic/stylelint-plugin": { @@ -8279,9 +8758,9 @@              "integrity": "sha512-9Ox1meDIvIKE23LLA7Fxd/ewJpKjj2KryH92doHRqx2406LmIzorsiMawL0qIK7dvwN9K+mfk47lauoIE0o1zQ=="          },          "acorn": { -            "version": "8.11.2", -            "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", -            "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", +            "version": "8.11.3", +            "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", +            "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",              "dev": true          },          "acorn-jsx": { diff --git a/package.json b/package.json index 9ba91ba2..67344512 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@          "build-libs": "node ./dev/bin/build-libs.js",          "test": "npm run test-lint-js && npm run test-ts && npm run test-lint-css && npm run test-lint-html && npm run test-code && npm run test-build",          "test-lint-js": "npx eslint .", +        "test-lint-json": "npx eslint **/*.json",          "test-lint-css": "npx stylelint \"ext/**/*.css\" \"test/**/*.css\" \"dev/**/*.css\"",          "test-lint-html": "npx html-validate \"ext/**/*.html\" \"test/**/*.html\" \"dev/**/*.html\"",          "test-ts": "npm run test-ts-main && npm run test-ts-dev && npm run test-ts-test", @@ -49,7 +50,7 @@      "devDependencies": {          "@codspeed/vitest-plugin": "^3.1.0",          "@playwright/test": "^1.39.0", -        "@stylistic/eslint-plugin-ts": "^1.5.1", +        "@stylistic/eslint-plugin": "^1.5.4",          "@stylistic/stylelint-plugin": "^2.0.0",          "@types/assert": "^1.5.10",          "@types/browserify": "^12.0.40", diff --git a/playwright.config.js b/playwright.config.js index 4658ceb2..a13eb710 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -45,7 +45,7 @@ export default defineConfig({      /* Retry on CI only */      retries: process.env.CI ? 2 : 0,      /* Opt out of parallel tests on CI. */ -    workers: process.env.CI ? 1 : undefined, +    workers: process.env.CI ? 1 : void 0,      /* Reporter to use. See https://playwright.dev/docs/test-reporters */      reporter: 'html',      /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ diff --git a/test/cache-map.test.js b/test/cache-map.test.js index 868c5d9a..9be5363a 100644 --- a/test/cache-map.test.js +++ b/test/cache-map.test.js @@ -37,7 +37,7 @@ function testConstructor() {  /** */  function testApi() {      describe('api', () => { -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          const data = [              {                  maxSize: 1, @@ -89,7 +89,7 @@ function testApi() {                  ]              }          ]; -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */          test.each(data)('api-test-%#', ({maxSize, expectedSize, calls}) => {              const cache = new CacheMap(maxSize); diff --git a/test/data/dictionaries/valid-dictionary1/term_bank_1.json b/test/data/dictionaries/valid-dictionary1/term_bank_1.json index d2012d33..ae32d146 100644 --- a/test/data/dictionaries/valid-dictionary1/term_bank_1.json +++ b/test/data/dictionaries/valid-dictionary1/term_bank_1.json @@ -2,13 +2,13 @@      ["打", "だ", "n", "n", 1, ["da definition 1", "da definition 2"], 1, "E1"],      ["打", "ダース", "n abbr", "n", 1, ["daasu definition 1", "daasu definition 2"], 2, "E1"],      ["打つ", "うつ", "vt", "v5", 10, ["utsu definition 1", "utsu definition 2"], 3, "P E1"], -    ["打つ", "うつ", "vt", "v5", 1,  ["utsu definition 3", "utsu definition 4"], 3, "P E2"], +    ["打つ", "うつ", "vt", "v5", 1, ["utsu definition 3", "utsu definition 4"], 3, "P E2"],      ["打つ", "ぶつ", "vt", "v5", 10, ["butsu definition 1", "butsu definition 2"], 3, "P E1"], -    ["打つ", "ぶつ", "vt", "v5", 1,  ["butsu definition 3", "butsu definition 4"], 3, "P E2"], +    ["打つ", "ぶつ", "vt", "v5", 1, ["butsu definition 3", "butsu definition 4"], 3, "P E2"],      ["打ち込む", "うちこむ", "vt", "v5", 10, ["uchikomu definition 1", "uchikomu definition 2"], 4, "P E1"], -    ["打ち込む", "うちこむ", "vt", "v5", 1,  ["uchikomu definition 3", "uchikomu definition 4"], 4, "P E2"], +    ["打ち込む", "うちこむ", "vt", "v5", 1, ["uchikomu definition 3", "uchikomu definition 4"], 4, "P E2"],      ["打ち込む", "ぶちこむ", "vt", "v5", 10, ["buchikomu definition 1", "buchikomu definition 2"], 4, "P E1"], -    ["打ち込む", "ぶちこむ", "vt", "v5", 1,  ["buchikomu definition 3", "buchikomu definition 4"], 4, "P E2"], +    ["打ち込む", "ぶちこむ", "vt", "v5", 1, ["buchikomu definition 3", "buchikomu definition 4"], 4, "P E2"],      ["画像", "がぞう", "n", "n", 1, ["gazou definition 1", {"type": "image", "path": "image.gif", "width": 350, "height": 350, "description": "gazou definition 2", "pixelated": true}], 5, "P E1"],      ["読む", "よむ", "vt", "v5", 100, ["to read"], 6, "P E1"],      ["強み", "つよみ", "n", "n", 90, ["strong point"], 7, "P E1"], diff --git a/test/eslint-config.test.js b/test/eslint-config.test.js new file mode 100644 index 00000000..bddde695 --- /dev/null +++ b/test/eslint-config.test.js @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2024  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 <https://www.gnu.org/licenses/>. + */ + +import esbuild from 'esbuild'; +import {readFileSync} from 'fs'; +import {dirname, join} from 'path'; +import {fileURLToPath} from 'url'; +import {describe, test} from 'vitest'; +import {parseJson} from '../dev/json.js'; + +const rootDir = join(dirname(fileURLToPath(import.meta.url)), '..'); + +/** + * @param {string[]} scriptPaths + * @returns {Promise<string[]>} + */ +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; +} + +/** + * @param {string[]} dependencies + * @returns {string[]} + */ +function removeLibraryDependencies(dependencies) { +    const pattern = /^ext\/lib\//; +    return dependencies.filter((v) => !pattern.test(v)); +} + +/** + * @param {{[key: string]: boolean}|undefined} env1 + * @param {{[key: string]: boolean}} env2 + * @returns {boolean} + */ +function envMatches(env1, env2) { +    if (typeof env1 !== 'object' || env1 === null) { return false; } +    const map1 = new Map(Object.entries(env1)); +    const map2 = new Map(Object.entries(env2)); +    if (map1.size !== map2.size) { return false; } +    for (const [k1, v1] of map1) { +        if (map2.get(k1) !== v1) { return false; } +    } +    return true; +} + +/** + * @param {string[]} files1 + * @param {string[]} files2 + * @returns {boolean} + */ +function filesMatch(files1, files2) { +    if (!Array.isArray(files1)) { return false; } +    const set1 = new Set(files1); +    const set2 = new Set(files2); +    if (set1.size !== set2.size) { return false; } +    for (const v of set1) { +        if (!set2.has(v)) { return false; } +    } +    return true; +} + +const targets = [ +    { +        name: 'sandbox', +        paths: [ +            'ext/js/templates/sandbox/template-renderer-frame-main.js' +        ], +        /** @type {{[key: string]: boolean}} */ +        env: { +            webextensions: false +        } +    }, +    { +        name: 'worker', +        paths: [ +            'ext/js/dictionary/dictionary-worker-main.js' +        ], +        /** @type {{[key: string]: boolean}} */ +        env: { +            browser: false, +            worker: true +        } +    }, +    { +        name: 'serviceworker', +        paths: [ +            'ext/js/background/background-main.js' +        ], +        /** @type {{[key: string]: boolean}} */ +        env: { +            browser: false, +            serviceworker: true +        } +    } +]; + +describe('Eslint configuration', () => { +    const eslintConfigPath = '.eslintrc.json'; +    /** @type {import('core').SafeAny} */ +    const eslintConfig = parseJson(readFileSync(join(rootDir, eslintConfigPath), 'utf8')); +    describe.each(targets)('Environment is $name', ({name, paths, env}) => { +        test('Entry exists', async ({expect}) => { +            const fullPaths = paths.map((v) => join(rootDir, v)); +            const dependencies = removeLibraryDependencies(await getDependencies(fullPaths)); + +            let okay = false; +            const candidates = []; +            const {overrides} = eslintConfig; +            for (let i = 0, ii = overrides.length; i < ii; ++i) { +                const override = overrides[i]; +                if (!envMatches(override.env, env)) { continue; } +                const {files} = override; +                if (!Array.isArray(files)) { continue; } +                candidates.push(i); +                if (filesMatch(files, dependencies)) { +                    okay = true; +                    break; +                } +            } + +            if (okay) { return; } +            switch (candidates.length) { +                case 0: +                    { +                        const message = `No override found with "${name}" environment: ${JSON.stringify(env)}`; +                        expect(false, message).toStrictEqual(true); +                    } +                    break; +                case 1: +                    { +                        const index = candidates[0]; +                        const message = `Override at index ${index} does not have the expected files for the "${name}" environment.`; +                        expect(overrides[index].files, message).toStrictEqual(dependencies); +                    } +                    break; +                default: +                    { +                        const message = `No override found with the correct file list for the "${name}" environment; candidate indices: [${candidates.join(', ')}].`; +                        expect([], message).toStrictEqual(dependencies); +                    } +                    break; +            } +        }); +    }); +}); diff --git a/test/hotkey-util.test.js b/test/hotkey-util.test.js index 0a76d0f2..bf1124a5 100644 --- a/test/hotkey-util.test.js +++ b/test/hotkey-util.test.js @@ -22,7 +22,7 @@ import {HotkeyUtil} from '../ext/js/input/hotkey-util.js';  /** */  function testCommandConversions() {      describe('CommandConversions', () => { -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          /** @type {{os: import('environment').OperatingSystem, command: string, expectedCommand: string, expectedInput: {key: string, modifiers: import('input').Modifier[]}}[]} */          const data = [              {os: 'win', command: 'Alt+F', expectedCommand: 'Alt+F', expectedInput: {key: 'KeyF', modifiers: ['alt']}}, @@ -40,7 +40,7 @@ function testCommandConversions() {              {os: 'linux', command: 'MacCtrl+Alt+Shift+F1', expectedCommand: 'Ctrl+Alt+Shift+F1',    expectedInput: {key: 'F1', modifiers: ['ctrl', 'alt', 'shift']}},              {os: 'linux', command: 'Command+Alt+Shift+F1', expectedCommand: 'Command+Alt+Shift+F1', expectedInput: {key: 'F1', modifiers: ['meta', 'alt', 'shift']}}          ]; -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */          const hotkeyUtil = new HotkeyUtil();          for (const {command, os, expectedInput, expectedCommand} of data) { @@ -58,7 +58,7 @@ function testCommandConversions() {  /** */  function testDisplayNames() {      describe('DisplayNames', () => { -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          /** @type {{os: import('environment').OperatingSystem, key: ?string, modifiers: import('input').Modifier[], expected: string}[]} */          const data = [              {os: 'win', key: null,   modifiers: [], expected: ''}, @@ -137,7 +137,7 @@ function testDisplayNames() {              {os: 'unknown', key: 'KeyF', modifiers: ['mouse1'], expected: 'Mouse 1 + F'},              {os: 'unknown', key: 'F1',   modifiers: ['mouse1'], expected: 'Mouse 1 + F1'}          ]; -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */          const hotkeyUtil = new HotkeyUtil(); diff --git a/test/json-schema.test.js b/test/json-schema.test.js index a3686758..ab2c0c65 100644 --- a/test/json-schema.test.js +++ b/test/json-schema.test.js @@ -123,7 +123,7 @@ function testValidate1() {  /** */  function testValidate2() {      describe('Validate2', () => { -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          /** @type {{schema: import('ext/json-schema').Schema, inputs: {expected: boolean, value: unknown}[]}[]} */          const data = [              // String tests @@ -517,7 +517,7 @@ function testValidate2() {                  ]              }          ]; -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */          describe.each(data)('Schema %#', ({schema, inputs}) => {              test.each(inputs)(`schemaValidate(${schema}, $value) -> $expected`, ({expected, value}) => { @@ -890,7 +890,7 @@ function testGetValidValueOrDefault1() {  /** */  function testProxy1() {      describe('Proxy1', () => { -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          /** @type {{schema: import('ext/json-schema').Schema, tests: {error: boolean, value?: import('ext/json-schema').Value, action: (value: import('core').SafeAny) => void}[]}[]} */          const data = [              // Object tests @@ -1020,7 +1020,7 @@ function testProxy1() {                  ]              }          ]; -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */          describe.each(data)('Schema %#', ({schema, tests}) => {              test.each(tests)('proxy %#', ({error, value, action}) => { diff --git a/test/language-transformer.test.js b/test/language-transformer.test.js index 9e9a9ee3..857b5ed0 100644 --- a/test/language-transformer.test.js +++ b/test/language-transformer.test.js @@ -64,7 +64,7 @@ function hasTermReasons(languageTransformer, source, expectedTerm, expectedCondi  /** */  function testDeinflections() { -    /* eslint-disable no-multi-spaces */ +    /* eslint-disable @stylistic/no-multi-spaces */      const data = [          {              category: 'adjectives', @@ -1146,7 +1146,7 @@ function testDeinflections() {              ]          }      ]; -    /* eslint-enable no-multi-spaces */ +    /* eslint-enable @stylistic/no-multi-spaces */      /** @type {import('language-transformer').LanguageTransformDescriptor} */      const descriptor = parseJson(fs.readFileSync(path.join(dirname, '..', 'ext', 'data/language/japanese-transforms.json'), {encoding: 'utf8'})); diff --git a/test/options-util.test.js b/test/options-util.test.js index 25abe715..4a75fa14 100644 --- a/test/options-util.test.js +++ b/test/options-util.test.js @@ -475,7 +475,7 @@ function createProfileOptionsUpdatedTestData1() {              ]          },          inputs: { -            /* eslint-disable no-multi-spaces */ +            /* eslint-disable @stylistic/no-multi-spaces */              hotkeys: [                  {action: 'close',             argument: '',  key: 'Escape',    modifiers: [],       scopes: ['popup'], enabled: true},                  {action: 'focusSearchBox',    argument: '',  key: 'Escape',    modifiers: [],       scopes: ['search'], enabled: true}, @@ -494,7 +494,7 @@ function createProfileOptionsUpdatedTestData1() {                  {action: 'viewNote',          argument: '',  key: 'KeyV',      modifiers: ['alt'],  scopes: ['popup', 'search'], enabled: true},                  {action: 'copyHostSelection', argument: '',  key: 'KeyC',      modifiers: ['ctrl'], scopes: ['popup'], enabled: true}              ] -            /* eslint-enable no-multi-spaces */ +            /* eslint-enable @stylistic/no-multi-spaces */          },          popupWindow: {              width: 400, diff --git a/test/playwright/playwright-util.js b/test/playwright/playwright-util.js index 425d6140..bf171251 100644 --- a/test/playwright/playwright-util.js +++ b/test/playwright/playwright-util.js @@ -23,7 +23,8 @@ const dirname = path.dirname(fileURLToPath(import.meta.url));  export const root = path.join(dirname, '..', '..');  export const test = base.extend({ -    context: async ({ }, use) => { +    // eslint-disable-next-line no-empty-pattern +    context: async ({}, use) => {          const pathToExtension = path.join(root, 'ext');          const context = await chromium.launchPersistentContext('', {              // headless: false, diff --git a/test/playwright/visual.spec.js b/test/playwright/visual.spec.js index 3ecf4c6e..cc2a50d0 100644 --- a/test/playwright/visual.spec.js +++ b/test/playwright/visual.spec.js @@ -60,11 +60,11 @@ test('visual', async ({page, extensionId}) => {          // Otherwise prepare for it to be attached          let frame_attached; -        if (popup_frame === undefined) { +        if (typeof popup_frame === 'undefined') {              frame_attached = page.waitForEvent('frameattached');          }          await page.mouse.move(box.x + offset.x, box.y + offset.y, {steps: 10}); // hover over the test -        if (popup_frame === undefined) { +        if (typeof popup_frame === 'undefined') {              popup_frame = await frame_attached; // wait for popup to be attached          }          try { diff --git a/test/profile-conditions-util.test.js b/test/profile-conditions-util.test.js index fcd53939..261225d9 100644 --- a/test/profile-conditions-util.test.js +++ b/test/profile-conditions-util.test.js @@ -59,7 +59,7 @@ function testNormalizeContext() {  /** */  function testSchemas() {      describe('Schemas', () => { -        /* eslint-disable no-multi-spaces */ +        /* eslint-disable @stylistic/no-multi-spaces */          /** @type {{conditionGroups: import('settings').ProfileConditionGroup[], expectedSchema?: import('ext/json-schema').Schema, inputs?: {expected: boolean, context: import('settings').OptionsContext}[]}[]} */          const data = [              // Empty @@ -1097,7 +1097,7 @@ function testSchemas() {                  ]              }          ]; -        /* eslint-enable no-multi-spaces */ +        /* eslint-enable @stylistic/no-multi-spaces */          test.each(data)('schemas-test-%#', ({conditionGroups, expectedSchema, inputs}) => {              const schema = createSchema(conditionGroups); diff --git a/types/ext/dictionary-database.d.ts b/types/ext/dictionary-database.d.ts index 2c416c68..1ae4603f 100644 --- a/types/ext/dictionary-database.d.ts +++ b/types/ext/dictionary-database.d.ts @@ -198,7 +198,7 @@ export type ObjectStoreName = (      'media'  ); -/* eslint-disable @stylistic/ts/indent */ +/* eslint-disable @stylistic/indent */  export type ObjectStoreData<T extends ObjectStoreName> = (      T extends 'dictionaries' ? DictionaryImporter.Summary :      T extends 'terms' ? DatabaseTermEntry : @@ -209,7 +209,7 @@ export type ObjectStoreData<T extends ObjectStoreName> = (      T extends 'media' ? MediaDataArrayBufferContent :      never  ); -/* eslint-enable @stylistic/ts/indent */ +/* eslint-enable @stylistic/indent */  export type DeleteDictionaryProgressData = {      count: number; |