From 4da4827bcbcdd1ef163f635d9b29416ff272b0bb Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Mon, 27 Nov 2023 12:48:14 -0500 Subject: Add JSDoc type annotations to project (rebased) --- .eslintrc.json | 258 +- .vscode/extensions.json | 7 + .vscode/settings.json | 12 +- dev/bin/generate-css-json.js | 1 + dev/data-error.js | 35 + dev/jsconfig.json | 77 + dev/lint/global-declarations.js | 157 + dev/lint/html-scripts.js | 202 + docs/interfaces/dictionary-entry.ts | 479 - docs/interfaces/translator-types.ts | 178 - .../schemas/dictionary-term-bank-v3-schema.json | 4 + ext/data/schemas/options-schema.json | 3 + ext/js/accessibility/accessibility-controller.js | 14 +- ext/js/accessibility/google-docs-util.js | 46 +- ext/js/accessibility/google-docs.js | 14 +- ext/js/app/content-script-main.js | 2 + ext/js/app/frontend.js | 308 +- ext/js/app/popup-factory.js | 182 +- ext/js/app/popup-proxy.js | 102 +- ext/js/app/popup-window.js | 67 +- ext/js/app/popup.js | 333 +- ext/js/app/theme-controller.js | 28 +- ext/js/background/backend.js | 1001 +- ext/js/background/profile-conditions-util.js | 155 +- ext/js/background/request-builder.js | 50 + ext/js/background/script-manager.js | 186 +- ext/js/comm/anki-connect.js | 310 +- ext/js/comm/api.js | 356 +- ext/js/comm/clipboard-monitor.js | 24 +- ext/js/comm/clipboard-reader.js | 54 +- ext/js/comm/cross-frame-api.js | 156 +- ext/js/comm/frame-ancestry-handler.js | 87 +- ext/js/comm/frame-client.js | 68 +- ext/js/comm/frame-endpoint.js | 49 +- ext/js/comm/frame-offset-forwarder.js | 16 + ext/js/comm/mecab.js | 84 +- ext/js/core.js | 343 +- ext/js/core/extension-error.js | 85 + ext/js/data/anki-note-builder.js | 143 +- ext/js/data/anki-util.js | 2 +- ext/js/data/database.js | 206 +- ext/js/data/json-schema.js | 927 +- ext/js/data/options-util.js | 185 +- ext/js/data/permissions-util.js | 22 + ext/js/data/sandbox/anki-note-data-creator.js | 243 +- ext/js/debug/timer.js | 31 +- ext/js/display/display-anki.js | 328 +- ext/js/display/display-audio.js | 320 +- ext/js/display/display-content-manager.js | 101 +- ext/js/display/display-generator.js | 429 +- ext/js/display/display-history.js | 73 +- ext/js/display/display-notification.js | 32 +- ext/js/display/display-profile-selection.js | 50 +- ext/js/display/display-resizer.js | 58 +- ext/js/display/display.js | 678 +- ext/js/display/element-overflow-controller.js | 39 +- ext/js/display/option-toggle-hotkey-handler.js | 62 +- ext/js/display/query-parser.js | 127 +- ext/js/display/sandbox/pronunciation-generator.js | 56 +- .../sandbox/structured-content-generator.js | 101 +- ext/js/display/search-action-popup-controller.js | 5 + ext/js/display/search-display-controller.js | 171 +- ext/js/display/search-main.js | 4 +- .../display/search-persistent-state-controller.js | 27 +- ext/js/dom/document-focus-controller.js | 22 +- ext/js/dom/document-util.js | 290 +- ext/js/dom/dom-data-binder.js | 144 +- ext/js/dom/dom-text-scanner.js | 55 +- ext/js/dom/html-template-collection.js | 39 +- ext/js/dom/native-simple-dom-parser.js | 74 +- ext/js/dom/panel-element.js | 34 + ext/js/dom/popup-menu.js | 71 +- ext/js/dom/sandbox/css-style-applier.js | 43 +- ext/js/dom/scroll-element.js | 50 + ext/js/dom/selector-observer.js | 113 +- ext/js/dom/simple-dom-parser.js | 122 +- ext/js/dom/text-source-element.js | 41 +- ext/js/dom/text-source-range.js | 33 +- ext/js/extension/environment.js | 24 + ext/js/general/cache-map.js | 45 +- ext/js/general/object-property-accessor.js | 29 +- ext/js/general/regex-util.js | 8 +- ext/js/general/task-accumulator.js | 46 +- ext/js/general/text-source-map.js | 34 + ext/js/input/hotkey-handler.js | 102 +- ext/js/input/hotkey-help-controller.js | 92 +- ext/js/input/hotkey-util.js | 64 +- ext/js/language/deinflector.js | 39 +- ext/js/language/dictionary-database.js | 255 +- .../language/dictionary-importer-media-loader.js | 10 +- ext/js/language/dictionary-importer.js | 338 +- ext/js/language/dictionary-worker-handler.js | 38 +- ext/js/language/dictionary-worker-media-loader.js | 16 +- ext/js/language/dictionary-worker.js | 110 +- ext/js/language/sandbox/dictionary-data-util.js | 161 +- ext/js/language/sandbox/japanese-util.js | 1371 ++- ext/js/language/text-scanner.js | 534 +- ext/js/language/translator.js | 788 +- ext/js/media/audio-downloader.js | 125 +- ext/js/media/audio-system.js | 41 +- ext/js/media/media-util.js | 2 +- ext/js/media/text-to-speech-audio.js | 16 + ext/js/pages/action-popup-main.js | 103 +- .../pages/common/extension-content-controller.js | 32 +- ext/js/pages/info-main.js | 34 +- ext/js/pages/permissions-main.js | 41 +- ext/js/pages/settings/anki-controller.js | 426 +- ext/js/pages/settings/anki-templates-controller.js | 161 +- ext/js/pages/settings/audio-controller.js | 181 +- ext/js/pages/settings/backup-controller.js | 180 +- .../settings/collapsible-dictionary-controller.js | 86 +- ext/js/pages/settings/dictionary-controller.js | 327 +- .../pages/settings/dictionary-import-controller.js | 150 +- .../extension-keyboard-shortcuts-controller.js | 135 +- .../pages/settings/generic-setting-controller.js | 180 +- .../pages/settings/keyboard-mouse-input-field.js | 92 +- .../settings/keyboard-shortcuts-controller.js | 268 +- ext/js/pages/settings/mecab-controller.js | 37 +- ext/js/pages/settings/modal-controller.js | 12 +- ext/js/pages/settings/modal.js | 28 +- ext/js/pages/settings/nested-popups-controller.js | 47 +- .../settings/permissions-origin-controller.js | 73 +- .../settings/permissions-toggle-controller.js | 45 +- .../settings/persistent-storage-controller.js | 29 +- ext/js/pages/settings/popup-preview-controller.js | 32 +- ext/js/pages/settings/popup-preview-frame-main.js | 6 + ext/js/pages/settings/popup-preview-frame.js | 102 +- ext/js/pages/settings/popup-window-controller.js | 7 +- ext/js/pages/settings/profile-conditions-ui.js | 395 +- ext/js/pages/settings/profile-controller.js | 287 +- ext/js/pages/settings/scan-inputs-controller.js | 175 +- .../settings/scan-inputs-simple-controller.js | 90 +- .../secondary-search-dictionary-controller.js | 40 +- .../sentence-termination-characters-controller.js | 127 +- ext/js/pages/settings/settings-controller.js | 172 +- .../pages/settings/settings-display-controller.js | 152 +- ext/js/pages/settings/settings-main.js | 8 +- .../sort-frequency-dictionary-controller.js | 93 +- ext/js/pages/settings/status-footer.js | 34 +- ext/js/pages/settings/storage-controller.js | 67 +- .../translation-text-replacements-controller.js | 107 +- ext/js/pages/welcome-main.js | 6 +- ext/js/script/dynamic-loader-sentinel.js | 3 +- ext/js/script/dynamic-loader.js | 43 +- .../anki-template-renderer-content-manager.js | 23 +- ext/js/templates/sandbox/anki-template-renderer.js | 347 +- .../sandbox/template-renderer-frame-api.js | 86 +- .../sandbox/template-renderer-media-provider.js | 67 +- ext/js/templates/sandbox/template-renderer.js | 100 +- ext/js/templates/template-patcher.js | 18 + ext/js/templates/template-renderer-proxy.js | 84 +- ext/js/yomitan.js | 76 +- jsconfig.json | 28 +- package-lock.json | 11895 ++++++------------- package.json | 27 +- test/data/anki-note-builder-test-results.json | 48 +- test/data/html/test-document2-script.js | 40 +- test/data/translator-test-results-note-data1.json | 280 +- test/data/translator-test-results.json | 236 +- test/dictionary.test.js | 6 + test/jsconfig.json | 39 + test/playwright/visual.spec.js | 12 +- test/test-all.js | 71 + test/test-anki-note-builder.js | 322 + test/test-cache-map.js | 137 + test/test-core.js | 300 + test/test-database.js | 982 ++ test/test-document-util.js | 339 + test/test-hotkey-util.js | 189 + test/test-japanese-util.js | 915 ++ test/test-json-schema.js | 1048 ++ test/test-manifest.js | 49 + test/test-object-property-accessor.js | 458 + test/test-profile-conditions-util.js | 1136 ++ test/test-text-source-map.js | 244 + test/test-translator.js | 102 + test/test-workers.js | 168 + types/dev/dictionary-validate.d.ts | 31 + types/dev/manifest.d.ts | 112 + types/dev/schema-validate.d.ts | 24 + types/dev/vm.d.ts | 59 + types/ext/anki-controller.d.ts | 27 + types/ext/anki-note-builder.d.ts | 120 + .../anki-template-renderer-content-manager.d.ts | 28 + types/ext/anki-templates-internal.d.ts | 60 + types/ext/anki-templates.d.ts | 276 + types/ext/anki.d.ts | 75 + types/ext/api.d.ts | 460 + types/ext/audio-controller.d.ts | 24 + types/ext/audio-downloader.d.ts | 44 + types/ext/audio-system.d.ts | 18 + types/ext/audio.d.ts | 20 + types/ext/backend.d.ts | 123 + types/ext/backup-controller.d.ts | 35 + types/ext/cache-map.d.ts | 23 + types/ext/clipboard-monitor.d.ts | 26 + types/ext/core.d.ts | 102 + types/ext/cross-frame-api.d.ts | 54 + types/ext/css-style-applier.d.ts | 46 + types/ext/database.d.ts | 38 + types/ext/deinflector.d.ts | 41 + types/ext/dictionary-data-util.d.ts | 94 + types/ext/dictionary-data.d.ts | 147 + types/ext/dictionary-database.d.ts | 240 + types/ext/dictionary-importer-media-loader.d.ts | 36 + types/ext/dictionary-importer.d.ts | 95 + types/ext/dictionary-worker-handler.d.ts | 62 + types/ext/dictionary-worker-media-loader.d.ts | 26 + types/ext/dictionary-worker.d.ts | 73 + types/ext/dictionary.d.ts | 477 + types/ext/display-anki.d.ts | 58 + types/ext/display-audio.d.ts | 99 + types/ext/display-content-manager.d.ts | 40 + types/ext/display-history.d.ts | 37 + types/ext/display.d.ts | 202 + types/ext/document-util.d.ts | 53 + types/ext/dom-data-binder.d.ts | 98 + types/ext/dynamic-loader.d.ts | 20 + types/ext/dynamic-property.d.ts | 22 + types/ext/environment.d.ts | 25 + types/ext/event-listener-collection.d.ts | 81 + types/ext/extension.d.ts | 74 + types/ext/frame-ancestry-handler.d.ts | 26 + types/ext/frame-client.d.ts | 35 + types/ext/frame-offset-forwarder.d.ts | 23 + types/ext/frontend.d.ts | 58 + types/ext/generic-setting-controller.d.ts | 101 + types/ext/hotkey-handler.d.ts | 30 + types/ext/input.d.ts | 21 + types/ext/japanese-util.d.ts | 42 + types/ext/json-schema.d.ts | 75 + types/ext/keyboard-mouse-input-field.d.ts | 25 + types/ext/keyboard-shortcut-controller.d.ts | 26 + types/ext/log.d.ts | 24 + types/ext/mecab.d.ts | 46 + types/ext/options-util.d.ts | 35 + types/ext/panel-element.d.ts | 39 + types/ext/popup-factory.d.ts | 32 + types/ext/popup-menu.d.ts | 39 + types/ext/popup.d.ts | 132 + types/ext/profile-conditions-ui.d.ts | 70 + types/ext/profile-conditions-util.d.ts | 38 + types/ext/request-builder.d.ts | 21 + types/ext/script-manager.d.ts | 56 + types/ext/selector-observer.d.ts | 55 + types/ext/settings-controller.d.ts | 51 + types/ext/settings-modifications.d.ts | 97 + types/ext/settings.d.ts | 382 + types/ext/simple-dom-parser.d.ts | 37 + types/ext/structured-content.d.ts | 205 + types/ext/task-accumulator.d.ts | 21 + types/ext/template-patcher.d.ts | 24 + types/ext/template-renderer-frame-api.d.ts | 24 + types/ext/template-renderer.d.ts | 75 + types/ext/text-scanner.d.ts | 185 + types/ext/text-source.d.ts | 18 + types/ext/translation-internal.d.ts | 65 + types/ext/translation.d.ts | 171 + types/ext/translator.d.ts | 83 + types/other/web-set-timeout.d.ts | 24 + types/test/document-types.d.ts | 18 + 261 files changed, 32766 insertions(+), 13545 deletions(-) create mode 100644 .vscode/extensions.json create mode 100644 dev/data-error.js create mode 100644 dev/jsconfig.json create mode 100644 dev/lint/global-declarations.js create mode 100644 dev/lint/html-scripts.js delete mode 100644 docs/interfaces/dictionary-entry.ts delete mode 100644 docs/interfaces/translator-types.ts create mode 100644 ext/js/core/extension-error.js create mode 100644 test/jsconfig.json create mode 100644 test/test-all.js create mode 100644 test/test-anki-note-builder.js create mode 100644 test/test-cache-map.js create mode 100644 test/test-core.js create mode 100644 test/test-database.js create mode 100644 test/test-document-util.js create mode 100644 test/test-hotkey-util.js create mode 100644 test/test-japanese-util.js create mode 100644 test/test-json-schema.js create mode 100644 test/test-manifest.js create mode 100644 test/test-object-property-accessor.js create mode 100644 test/test-profile-conditions-util.js create mode 100644 test/test-text-source-map.js create mode 100644 test/test-translator.js create mode 100644 test/test-workers.js create mode 100644 types/dev/dictionary-validate.d.ts create mode 100644 types/dev/manifest.d.ts create mode 100644 types/dev/schema-validate.d.ts create mode 100644 types/dev/vm.d.ts create mode 100644 types/ext/anki-controller.d.ts create mode 100644 types/ext/anki-note-builder.d.ts create mode 100644 types/ext/anki-template-renderer-content-manager.d.ts create mode 100644 types/ext/anki-templates-internal.d.ts create mode 100644 types/ext/anki-templates.d.ts create mode 100644 types/ext/anki.d.ts create mode 100644 types/ext/api.d.ts create mode 100644 types/ext/audio-controller.d.ts create mode 100644 types/ext/audio-downloader.d.ts create mode 100644 types/ext/audio-system.d.ts create mode 100644 types/ext/audio.d.ts create mode 100644 types/ext/backend.d.ts create mode 100644 types/ext/backup-controller.d.ts create mode 100644 types/ext/cache-map.d.ts create mode 100644 types/ext/clipboard-monitor.d.ts create mode 100644 types/ext/core.d.ts create mode 100644 types/ext/cross-frame-api.d.ts create mode 100644 types/ext/css-style-applier.d.ts create mode 100644 types/ext/database.d.ts create mode 100644 types/ext/deinflector.d.ts create mode 100644 types/ext/dictionary-data-util.d.ts create mode 100644 types/ext/dictionary-data.d.ts create mode 100644 types/ext/dictionary-database.d.ts create mode 100644 types/ext/dictionary-importer-media-loader.d.ts create mode 100644 types/ext/dictionary-importer.d.ts create mode 100644 types/ext/dictionary-worker-handler.d.ts create mode 100644 types/ext/dictionary-worker-media-loader.d.ts create mode 100644 types/ext/dictionary-worker.d.ts create mode 100644 types/ext/dictionary.d.ts create mode 100644 types/ext/display-anki.d.ts create mode 100644 types/ext/display-audio.d.ts create mode 100644 types/ext/display-content-manager.d.ts create mode 100644 types/ext/display-history.d.ts create mode 100644 types/ext/display.d.ts create mode 100644 types/ext/document-util.d.ts create mode 100644 types/ext/dom-data-binder.d.ts create mode 100644 types/ext/dynamic-loader.d.ts create mode 100644 types/ext/dynamic-property.d.ts create mode 100644 types/ext/environment.d.ts create mode 100644 types/ext/event-listener-collection.d.ts create mode 100644 types/ext/extension.d.ts create mode 100644 types/ext/frame-ancestry-handler.d.ts create mode 100644 types/ext/frame-client.d.ts create mode 100644 types/ext/frame-offset-forwarder.d.ts create mode 100644 types/ext/frontend.d.ts create mode 100644 types/ext/generic-setting-controller.d.ts create mode 100644 types/ext/hotkey-handler.d.ts create mode 100644 types/ext/input.d.ts create mode 100644 types/ext/japanese-util.d.ts create mode 100644 types/ext/json-schema.d.ts create mode 100644 types/ext/keyboard-mouse-input-field.d.ts create mode 100644 types/ext/keyboard-shortcut-controller.d.ts create mode 100644 types/ext/log.d.ts create mode 100644 types/ext/mecab.d.ts create mode 100644 types/ext/options-util.d.ts create mode 100644 types/ext/panel-element.d.ts create mode 100644 types/ext/popup-factory.d.ts create mode 100644 types/ext/popup-menu.d.ts create mode 100644 types/ext/popup.d.ts create mode 100644 types/ext/profile-conditions-ui.d.ts create mode 100644 types/ext/profile-conditions-util.d.ts create mode 100644 types/ext/request-builder.d.ts create mode 100644 types/ext/script-manager.d.ts create mode 100644 types/ext/selector-observer.d.ts create mode 100644 types/ext/settings-controller.d.ts create mode 100644 types/ext/settings-modifications.d.ts create mode 100644 types/ext/settings.d.ts create mode 100644 types/ext/simple-dom-parser.d.ts create mode 100644 types/ext/structured-content.d.ts create mode 100644 types/ext/task-accumulator.d.ts create mode 100644 types/ext/template-patcher.d.ts create mode 100644 types/ext/template-renderer-frame-api.d.ts create mode 100644 types/ext/template-renderer.d.ts create mode 100644 types/ext/text-scanner.d.ts create mode 100644 types/ext/text-source.d.ts create mode 100644 types/ext/translation-internal.d.ts create mode 100644 types/ext/translation.d.ts create mode 100644 types/ext/translator.d.ts create mode 100644 types/other/web-set-timeout.d.ts create mode 100644 types/test/document-types.d.ts diff --git a/.eslintrc.json b/.eslintrc.json index f1a79770..3200d292 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -2,15 +2,22 @@ "root": true, "extends": [ "eslint:recommended", - "plugin:jsonc/recommended-with-json" + "plugin:jsonc/recommended-with-json", + "plugin:@typescript-eslint/recommended" ], + "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": 2022, "sourceType": "module", "ecmaFeatures": { "globalReturn": false, "impliedStrict": true - } + }, + "project": [ + "./jsconfig.json", + "./dev/jsconfig.json", + "./test/jsconfig.json" + ] }, "env": { "browser": true, @@ -21,7 +28,10 @@ "no-unsanitized", "header", "jsdoc", - "jsonc" + "jsonc", + "unused-imports", + "@typescript-eslint", + "@stylistic/ts" ], "ignorePatterns": [ "/ext/lib/", @@ -56,7 +66,7 @@ "no-param-reassign": "off", "no-prototype-builtins": "error", "no-shadow": [ - "error", + "off", { "builtinGlobals": false } @@ -259,23 +269,41 @@ "error", "never" ], - "jsdoc/require-jsdoc": "off", + "jsdoc/require-jsdoc": [ + "error", + { + "require": { + "ClassDeclaration": false, + "FunctionDeclaration": true, + "MethodDefinition": false + }, + "contexts": [ + "MethodDefinition[kind=constructor]>FunctionExpression>BlockStatement>ExpressionStatement>AssignmentExpression[left.object.type=ThisExpression]", + "ClassDeclaration>Classbody>PropertyDefinition", + "MethodDefinition[kind!=constructor][kind!=set]", + "MethodDefinition[kind=constructor][value.params.length>0]" + ], + "checkGetters": "no-setter", + "checkSetters": "no-getter" + } + ], + "jsdoc/require-description": "off", "jsdoc/require-param": "error", - "jsdoc/require-param-description": "error", + "jsdoc/require-param-description": "off", "jsdoc/require-param-name": "error", "jsdoc/require-param-type": "error", "jsdoc/require-property": "error", - "jsdoc/require-property-description": "error", + "jsdoc/require-property-description": "off", "jsdoc/require-property-name": "error", "jsdoc/require-property-type": "error", "jsdoc/require-returns": "error", "jsdoc/require-returns-check": "error", - "jsdoc/require-returns-description": "error", + "jsdoc/require-returns-description": "off", "jsdoc/require-returns-type": "error", "jsdoc/require-throws": "error", "jsdoc/require-yields": "error", "jsdoc/require-yields-check": "error", - "jsdoc/tag-lines": "error", + "jsdoc/tag-lines": ["error", "never", {"startLines": 0}], "jsdoc/valid-types": "error", "jsonc/indent": [ "error", @@ -321,9 +349,162 @@ { "allowAllPropertiesOnSameLine": true } - ] + ], + "@typescript-eslint/ban-ts-comment": "off", + "@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-this-alias": "error", + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-var-requires": "off" }, "overrides": [ + { + "files": [ + "*.ts" + ], + "rules": { + "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": [ + "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/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": [ + "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 + }, + "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" + } + }, { "files": [ "*.json" @@ -373,12 +554,67 @@ { "files": [ "ext/js/core.js", + "ext/js/core/extension-error.js", "ext/js/**/sandbox/**/*.js" ], "env": { "webextensions": false } }, + { + "files": [ + "ext/**/*.js" + ], + "excludedFiles": [ + "ext/js/core/extension-error.js" + ], + "globals": { + "ExtensionError": "readonly" + } + }, + { + "files": [ + "ext/**/*.js" + ], + "excludedFiles": [ + "ext/js/core.js", + "ext/js/core/extension-error.js", + "ext/js/accessibility/google-docs.js", + "ext/js/**/sandbox/**/*.js" + ], + "globals": { + "isObject": "readonly", + "stringReverse": "readonly", + "promiseTimeout": "readonly", + "escapeRegExp": "readonly", + "deferPromise": "readonly", + "clone": "readonly", + "deepEqual": "readonly", + "generateId": "readonly", + "promiseAnimationFrame": "readonly", + "invokeMessageHandler": "readonly", + "log": "readonly", + "DynamicProperty": "readonly", + "EventDispatcher": "readonly", + "EventListenerCollection": "readonly", + "Logger": "readonly" + } + }, + { + "files": [ + "ext/**/*.js" + ], + "excludedFiles": [ + "ext/js/core.js", + "ext/js/core/extension-error.js", + "ext/js/accessibility/google-docs.js", + "ext/js/yomitan.js", + "ext/js/**/sandbox/**/*.js" + ], + "globals": { + "yomitan": "readonly" + } + }, { "files": [ "ext/js/yomitan.js" @@ -417,6 +653,7 @@ { "files": [ "ext/js/core.js", + "ext/js/core/extension-error.js", "ext/js/yomitan.js", "ext/js/accessibility/accessibility-controller.js", "ext/js/background/backend.js", @@ -466,6 +703,7 @@ { "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", diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..a5e24321 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "html-validate.vscode-html-validate", + "stylelint.vscode-stylelint" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 6dea7915..ecfe13c5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,11 +1,21 @@ { "markdown.extension.toc.levels": "1..3", "editor.codeActionsOnSave": { - "source.addMissingImports": true, + "source.addMissingImports": false, "source.organizeImports": true, "source.fixAll.eslint": true, }, "eslint.format.enable": true, "javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces": false, "javascript.preferences.importModuleSpecifierEnding": "js", + "editor.tabSize": 4, + "editor.insertSpaces": true, + "eslint.validate": [ + "javascript", + "typescript" + ], + "files.eol": "\n", + "html-validate.validate": [ + "html" + ] } diff --git a/dev/bin/generate-css-json.js b/dev/bin/generate-css-json.js index 48b42c65..73c406e0 100644 --- a/dev/bin/generate-css-json.js +++ b/dev/bin/generate-css-json.js @@ -19,6 +19,7 @@ import fs from 'fs'; import {formatRulesJson, generateRules, getTargets} from '../generate-css-json.js'; +/** */ function main() { for (const {cssFile, overridesCssFile, outputPath} of getTargets()) { const json = formatRulesJson(generateRules(cssFile, overridesCssFile)); diff --git a/dev/data-error.js b/dev/data-error.js new file mode 100644 index 00000000..5034e3fd --- /dev/null +++ b/dev/data-error.js @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2023 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 . + */ + +class DataError extends Error { + /** + * @param {string} message + */ + constructor(message) { + super(message); + /** @type {unknown} */ + this._data = void 0; + } + + /** @type {unknown} */ + get data() { return this._data; } + set data(value) { this._data = value; } +} + +module.exports = { + DataError +}; diff --git a/dev/jsconfig.json b/dev/jsconfig.json new file mode 100644 index 00000000..58eba952 --- /dev/null +++ b/dev/jsconfig.json @@ -0,0 +1,77 @@ +{ + "compilerOptions": { + "module": "ES2015", + "target": "ES2022", + "checkJs": true, + "moduleResolution": "node", + "strict": true, + "strictNullChecks": true, + "noImplicitAny": true, + "strictPropertyInitialization": true, + "suppressImplicitAnyIndexErrors": false, + "skipLibCheck": false, + "baseUrl": ".", + "paths": { + "anki-templates": ["../types/ext/anki-templates"], + "anki-templates-internal": ["../types/ext/anki-templates-internal"], + "cache-map": ["../types/ext/cache-map"], + "core": ["../types/ext/core"], + "css-style-applier": ["../types/ext/css-style-applier"], + "database": ["../types/ext/database"], + "deinflector": ["../types/ext/deinflector"], + "dictionary": ["../types/ext/dictionary"], + "dictionary-data": ["../types/ext/dictionary-data"], + "dictionary-data-util": ["../types/ext/dictionary-data-util"], + "dictionary-database": ["../types/ext/dictionary-database"], + "dictionary-importer": ["../types/ext/dictionary-importer"], + "dictionary-importer-media-loader": ["../types/ext/dictionary-importer-media-loader"], + "dynamic-property": ["../types/ext/dynamic-property"], + "error": ["../types/ext/error"], + "event-listener-collection": ["../types/ext/event-listener-collection"], + "japanese-util": ["../types/ext/japanese-util"], + "json-schema": ["../types/ext/json-schema"], + "log": ["../types/ext/log"], + "settings": ["../types/ext/settings"], + "structured-content": ["../types/ext/structured-content"], + "translator": ["../types/ext/translator"], + "translation": ["../types/ext/translation"], + "translation-internal": ["../types/ext/translation-internal"], + "dev/*": ["../types/dev/*"] + }, + "types": [ + "node", + "events", + "browserify", + "jsdom", + "assert", + "css", + "chrome", + "ajv" + ] + }, + "include": [ + "**/*.js", + "../playwright.config.js", + "../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/data/sandbox/anki-note-data-creator.js", + "../ext/js/general/cache-map.js", + "../ext/js/general/regex-util.js", + "../ext/js/general/text-source-map.js", + "../ext/js/language/deinflector.js", + "../ext/js/language/dictionary-importer.js", + "../ext/js/language/dictionary-database.js", + "../ext/js/language/sandbox/dictionary-data-util.js", + "../ext/js/language/sandbox/japanese-util.js", + "../ext/js/language/translator.js", + "../ext/js/media/media-util.js", + "../types/dev/**/*.ts", + "../types/other/web-set-timeout.d.ts" + ], + "exclude": [ + "../node_modules" + ] +} \ No newline at end of file diff --git a/dev/lint/global-declarations.js b/dev/lint/global-declarations.js new file mode 100644 index 00000000..648ad368 --- /dev/null +++ b/dev/lint/global-declarations.js @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2023 Yomitan Authors + * Copyright (C) 2020-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 . + */ + +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const {getAllFiles} = require('../util'); + + +/** + * @param {string} string + * @returns {string} + */ +function escapeRegExp(string) { + return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); +} + +/** + * @param {string} string + * @param {RegExp} pattern + * @returns {number} + */ +function countOccurences(string, pattern) { + return (string.match(pattern) || []).length; +} + +/** + * @param {string} string + * @returns {'\r'|'\n'|'\r\n'} + */ +function getNewline(string) { + const count1 = countOccurences(string, /(?:^|[^\r])\n/g); + const count2 = countOccurences(string, /\r\n/g); + const count3 = countOccurences(string, /\r(?:[^\n]|$)/g); + if (count2 > count1) { + return (count3 > count2) ? '\r' : '\r\n'; + } else { + return (count3 > count1) ? '\r' : '\n'; + } +} + +/** + * @param {string} string + * @param {string} substring + * @returns {number} + */ +function getSubstringCount(string, substring) { + let count = 0; + const pattern = new RegExp(`\\b${escapeRegExp(substring)}\\b`, 'g'); + while (true) { + const match = pattern.exec(string); + if (match === null) { break; } + ++count; + } + return count; +} + + +/** + * @param {string} fileName + * @param {boolean} fix + * @returns {boolean} + */ +function validateGlobals(fileName, fix) { + const pattern = /\/\*\s*global\s+([\w\W]*?)\*\//g; + const trimPattern = /^[\s,*]+|[\s,*]+$/g; + const splitPattern = /[\s,*]+/; + const source = fs.readFileSync(fileName, {encoding: 'utf8'}); + let match; + let first = true; + let endIndex = 0; + let newSource = ''; + const allGlobals = []; + const newline = getNewline(source); + while ((match = pattern.exec(source)) !== null) { + if (!first) { + console.error(`Encountered more than one global declaration in ${fileName}`); + return false; + } + first = false; + + const parts = match[1].replace(trimPattern, '').split(splitPattern); + parts.sort(); + + const actual = match[0]; + const expected = `/* global${parts.map((v) => `${newline} * ${v}`).join('')}${newline} */`; + + try { + assert.strictEqual(actual, expected); + } catch (e) { + console.error(`Global declaration error encountered in ${fileName}:`); + console.error(e instanceof Error ? e.message : `${e}`); + if (!fix) { + return false; + } + } + + newSource += source.substring(0, match.index); + newSource += expected; + endIndex = match.index + match[0].length; + + allGlobals.push(...parts); + } + + newSource += source.substring(endIndex); + + // This is an approximate check to see if a global variable is unused. + // If the global appears in a comment, string, or similar, the check will pass. + let errorCount = 0; + for (const global of allGlobals) { + if (getSubstringCount(newSource, global) <= 1) { + console.error(`Global variable ${global} appears to be unused in ${fileName}`); + ++errorCount; + } + } + + if (fix) { + fs.writeFileSync(fileName, newSource, {encoding: 'utf8'}); + } + + return errorCount === 0; +} + + +/** */ +function main() { + const fix = (process.argv.length >= 2 && process.argv[2] === '--fix'); + const directory = path.resolve(__dirname, '..', '..', 'ext'); + const pattern = /\.js$/; + const ignorePattern = /^lib[\\/]/; + const fileNames = getAllFiles(directory, (f) => pattern.test(f) && !ignorePattern.test(f)); + for (const fileName of fileNames) { + if (!validateGlobals(path.join(directory, fileName), fix)) { + process.exit(-1); + return; + } + } + process.exit(0); +} + + +if (require.main === module) { main(); } diff --git a/dev/lint/html-scripts.js b/dev/lint/html-scripts.js new file mode 100644 index 00000000..da8c2c71 --- /dev/null +++ b/dev/lint/html-scripts.js @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2023 Yomitan Authors + * Copyright (C) 2020-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 . + */ + +const fs = require('fs'); +const path = require('path'); +const assert = require('assert'); +const {JSDOM} = require('jsdom'); +const {getAllFiles} = require('../util'); + + +/** + * @param {string} fileName + * @returns {?fs.Stats} + */ +function lstatSyncSafe(fileName) { + try { + return fs.lstatSync(fileName); + } catch (e) { + return null; + } +} + +/** + * @param {string} src + * @param {string} fileName + * @param {string} extDir + */ +function validatePath(src, fileName, extDir) { + assert.ok(typeof src === 'string', `