diff options
author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2024-02-08 06:52:06 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-08 11:52:06 +0000 |
commit | d4381831209dfbbbddd6d238c68461c9601573e2 (patch) | |
tree | 87839d80f838464ff92ebc7fa652029b9329cc96 | |
parent | 725a90dd6570044a3df6631051aaab8b026ca6c2 (diff) |
Update eslint (#638)
* Add json test
* Update vscode settings to better handle json
* Collapse eslint rules for easier readability
* Reorganize
* Update no-multi-spaces rule for JSON
* Rules updates
* Switch to @stylistic/eslint-plugin
* Update deprecated stylistic rules
* Group stylistic rules
* Simplify rules
* Move eslint env overrides to end of file
* Add test
* Move promiseAnimationFrame to separate file
* Remove unneeded eslint disable
* Remove unneeded
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; |