diff options
Diffstat (limited to 'ext/js')
| -rw-r--r-- | ext/js/accessibility/google-docs-xray.js | 2 | ||||
| -rw-r--r-- | ext/js/background/backend.js | 29 | ||||
| -rw-r--r-- | ext/js/background/offscreen-proxy.js | 1 | ||||
| -rw-r--r-- | ext/js/background/script-manager.js | 28 | ||||
| -rw-r--r-- | ext/js/comm/anki-connect.js | 7 | ||||
| -rw-r--r-- | ext/js/comm/frame-ancestry-handler.js | 1 | ||||
| -rw-r--r-- | ext/js/comm/frame-client.js | 1 | ||||
| -rw-r--r-- | ext/js/data/anki-note-builder.js | 1 | ||||
| -rw-r--r-- | ext/js/data/database.js | 10 | ||||
| -rw-r--r-- | ext/js/data/json-schema.js | 2 | ||||
| -rw-r--r-- | ext/js/data/options-util.js | 2 | ||||
| -rw-r--r-- | ext/js/dictionary/dictionary-data-util.js | 1 | ||||
| -rw-r--r-- | ext/js/display/display-history.js | 4 | ||||
| -rw-r--r-- | ext/js/input/hotkey-help-controller.js | 6 | ||||
| -rw-r--r-- | ext/js/language/translator.js | 19 | ||||
| -rw-r--r-- | ext/js/pages/settings/backup-controller.js | 10 | ||||
| -rw-r--r-- | ext/js/pages/settings/dictionary-import-controller.js | 1 | ||||
| -rw-r--r-- | ext/js/templates/anki-template-renderer.js | 17 | ||||
| -rw-r--r-- | ext/js/templates/template-renderer-media-provider.js | 2 | ||||
| -rw-r--r-- | ext/js/templates/template-renderer.js | 2 | 
20 files changed, 97 insertions, 49 deletions
| diff --git a/ext/js/accessibility/google-docs-xray.js b/ext/js/accessibility/google-docs-xray.js index 15e1d50b..6723a468 100644 --- a/ext/js/accessibility/google-docs-xray.js +++ b/ext/js/accessibility/google-docs-xray.js @@ -17,7 +17,7 @@  /** Entry point. */  function main() { -    /** @type {Window} */ +    /** @type {unknown} */      // @ts-expect-error - Firefox Xray vision      const window2 = window.wrappedJSObject;      if (!(typeof window2 === 'object' && window2 !== null)) { return; } diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index 294c11db..d042a253 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -1831,16 +1831,7 @@ export class Backend {          }          try { -            const tabWindow = await new Promise((resolve, reject) => { -                chrome.windows.get(tab.windowId, {}, (value) => { -                    const e = chrome.runtime.lastError; -                    if (e) { -                        reject(new Error(e.message)); -                    } else { -                        resolve(value); -                    } -                }); -            }); +            const tabWindow = await this._getWindow(tab.windowId);              if (!tabWindow.focused) {                  await /** @type {Promise<void>} */ (new Promise((resolve, reject) => {                      chrome.windows.update(tab.windowId, {focused: true}, () => { @@ -1859,6 +1850,23 @@ export class Backend {      }      /** +     * @param {number} windowId +     * @returns {Promise<chrome.windows.Window>} +     */ +    _getWindow(windowId) { +        return new Promise((resolve, reject) => { +            chrome.windows.get(windowId, {}, (value) => { +                const e = chrome.runtime.lastError; +                if (e) { +                    reject(new Error(e.message)); +                } else { +                    resolve(value); +                } +            }); +        }); +    } + +    /**       * @param {number} tabId       * @param {number} frameId       * @param {?number} [timeout=null] @@ -2208,6 +2216,7 @@ export class Backend {      async _injectAnkiNoteDictionaryMedia(ankiConnect, timestamp, dictionaryMediaDetails) {          const targets = [];          const detailsList = []; +        /** @type {Map<string, {dictionary: string, path: string, media: ?import('dictionary-database').MediaDataStringContent}>} */          const detailsMap = new Map();          for (const {dictionary, path} of dictionaryMediaDetails) {              const target = {dictionary, path}; diff --git a/ext/js/background/offscreen-proxy.js b/ext/js/background/offscreen-proxy.js index e65ec65e..9e7b5b74 100644 --- a/ext/js/background/offscreen-proxy.js +++ b/ext/js/background/offscreen-proxy.js @@ -88,6 +88,7 @@ export class OffscreenProxy {          if (!chrome.runtime.getContexts) { // Chrome version below 116              // Clients: https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/clients              // @ts-expect-error - Types not set up for service workers yet +            // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment              const matchedClients = await clients.matchAll();              // @ts-expect-error - Types not set up for service workers yet              return await matchedClients.some((client) => client.url === offscreenUrl); diff --git a/ext/js/background/script-manager.js b/ext/js/background/script-manager.js index 84213452..ea1702e9 100644 --- a/ext/js/background/script-manager.js +++ b/ext/js/background/script-manager.js @@ -60,16 +60,7 @@ export function injectStylesheet(type, content, tabId, frameId, allFrames) {   * @returns {Promise<boolean>} `true` if a script is registered, `false` otherwise.   */  export async function isContentScriptRegistered(id) { -    const scripts = await new Promise((resolve, reject) => { -        chrome.scripting.getRegisteredContentScripts({ids: [id]}, (result) => { -            const e = chrome.runtime.lastError; -            if (e) { -                reject(new Error(e.message)); -            } else { -                resolve(result); -            } -        }); -    }); +    const scripts = await getRegisteredContentScripts([id]);      for (const script of scripts) {          if (script.id === id) {              return true; @@ -155,3 +146,20 @@ function createContentScriptRegistrationOptions(details, id) {      }      return options;  } + +/** + * @param {string[]} ids + * @returns {Promise<chrome.scripting.RegisteredContentScript[]>} + */ +function getRegisteredContentScripts(ids) { +    return new Promise((resolve, reject) => { +        chrome.scripting.getRegisteredContentScripts({ids}, (result) => { +            const e = chrome.runtime.lastError; +            if (e) { +                reject(new Error(e.message)); +            } else { +                resolve(result); +            } +        }); +    }); +} diff --git a/ext/js/comm/anki-connect.js b/ext/js/comm/anki-connect.js index 6a008f40..23183e79 100644 --- a/ext/js/comm/anki-connect.js +++ b/ext/js/comm/anki-connect.js @@ -18,6 +18,7 @@  import {ExtensionError} from '../core/extension-error.js';  import {parseJson} from '../core/json.js'; +import {isObjectNotArray} from '../core/object-utilities.js';  import {getRootDeckName} from '../data/anki-util.js';  /** @@ -606,15 +607,15 @@ export class AnkiConnect {              if (typeof modelName !== 'string') {                  throw this._createError(`Unexpected result type at index ${i}, field modelName: expected string, received ${this._getTypeName(modelName)}`, result);              } -            if (typeof fields !== 'object' || fields === null) { -                throw this._createError(`Unexpected result type at index ${i}, field fields: expected string, received ${this._getTypeName(fields)}`, result); +            if (!isObjectNotArray(fields)) { +                throw this._createError(`Unexpected result type at index ${i}, field fields: expected object, received ${this._getTypeName(fields)}`, result);              }              const tags2 = /** @type {string[]} */ (this._normalizeArray(tags, -1, 'string', ', field tags'));              const cards2 = /** @type {number[]} */ (this._normalizeArray(cards, -1, 'number', ', field cards'));              /** @type {{[key: string]: import('anki').NoteFieldInfo}} */              const fields2 = {};              for (const [key, fieldInfo] of Object.entries(fields)) { -                if (typeof fieldInfo !== 'object' || fieldInfo === null) { continue; } +                if (!isObjectNotArray(fieldInfo)) { continue; }                  const {value, order} = fieldInfo;                  if (typeof value !== 'string' || typeof order !== 'number') { continue; }                  fields2[key] = {value, order}; diff --git a/ext/js/comm/frame-ancestry-handler.js b/ext/js/comm/frame-ancestry-handler.js index 39288738..1915f121 100644 --- a/ext/js/comm/frame-ancestry-handler.js +++ b/ext/js/comm/frame-ancestry-handler.js @@ -288,6 +288,7 @@ export class FrameAncestryHandler {                  }                  /** @type {?ShadowRoot|undefined} */ +                // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment                  const shadowRoot = (                      element.shadowRoot ||                      // @ts-expect-error - openOrClosedShadowRoot is available to Firefox 63+ for WebExtensions diff --git a/ext/js/comm/frame-client.js b/ext/js/comm/frame-client.js index a30efa29..effd3e7c 100644 --- a/ext/js/comm/frame-client.js +++ b/ext/js/comm/frame-client.js @@ -83,6 +83,7 @@ export class FrameClient {       */      _connectInternal(frame, targetOrigin, hostFrameId, setupFrame, timeout) {          return new Promise((resolve, reject) => { +            /** @type {Map<string, string>} */              const tokenMap = new Map();              /** @type {?import('core').Timeout} */              let timer = null; diff --git a/ext/js/data/anki-note-builder.js b/ext/js/data/anki-note-builder.js index 6a6a6177..aec8cdd9 100644 --- a/ext/js/data/anki-note-builder.js +++ b/ext/js/data/anki-note-builder.js @@ -88,6 +88,7 @@ export class AnkiNoteBuilder {          }          const formattedFieldValues = await Promise.all(formattedFieldValuePromises); +        /** @type {Map<string, import('anki-note-builder').Requirement>} */          const uniqueRequirements = new Map();          /** @type {import('anki').NoteFields} */          const noteFields = {}; diff --git a/ext/js/data/database.js b/ext/js/data/database.js index 7f37347b..a53c8ddb 100644 --- a/ext/js/data/database.js +++ b/ext/js/data/database.js @@ -194,10 +194,10 @@ export class Database {          request.onsuccess = (e) => {              const cursor = /** @type {IDBRequest<?IDBCursorWithValue>} */ (e.target).result;              if (cursor) { -                /** @type {TResult} */ +                /** @type {unknown} */                  const value = cursor.value; -                if (noPredicate || predicate(value, predicateArg)) { -                    resolve(value, data); +                if (noPredicate || predicate(/** @type {TResult} */ (value), predicateArg)) { +                    resolve(/** @type {TResult} */ (value), data);                  } else {                      cursor.continue();                  } @@ -424,9 +424,9 @@ export class Database {          request.onsuccess = (e) => {              const cursor = /** @type {IDBRequest<?IDBCursorWithValue>} */ (e.target).result;              if (cursor) { -                /** @type {TResult} */ +                /** @type {unknown} */                  const value = cursor.value; -                results.push(value); +                results.push(/** @type {TResult} */ (value));                  cursor.continue();              } else {                  onSuccess(results, data); diff --git a/ext/js/data/json-schema.js b/ext/js/data/json-schema.js index 9e1497e9..0a2b8d82 100644 --- a/ext/js/data/json-schema.js +++ b/ext/js/data/json-schema.js @@ -1263,7 +1263,7 @@ class JsonSchemaProxyHandler {      /**       * @param {import('ext/json-schema').ValueObjectOrArray} target       * @param {string|number|symbol} property -     * @param {import('core').SafeAny} value +     * @param {unknown} value       * @returns {boolean}       * @throws {Error}       */ diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js index b6fb6686..ba404bc2 100644 --- a/ext/js/data/options-util.js +++ b/ext/js/data/options-util.js @@ -27,6 +27,7 @@ import {JsonSchema} from './json-schema.js';  // of the options object to a newer format. SafeAny is used for much of this, since every single  // legacy format does not contain type definitions.  /* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */  export class OptionsUtil {      constructor() { @@ -1295,4 +1296,5 @@ export class OptionsUtil {      }  } +/* eslint-enable @typescript-eslint/no-unsafe-assignment */  /* eslint-enable @typescript-eslint/no-unsafe-argument */ diff --git a/ext/js/dictionary/dictionary-data-util.js b/ext/js/dictionary/dictionary-data-util.js index a90668f4..dfdd5601 100644 --- a/ext/js/dictionary/dictionary-data-util.js +++ b/ext/js/dictionary/dictionary-data-util.js @@ -24,6 +24,7 @@ export function groupTermTags(dictionaryEntry) {      const {headwords} = dictionaryEntry;      const headwordCount = headwords.length;      const uniqueCheck = (headwordCount > 1); +    /** @type {Map<string, number>} */      const resultsIndexMap = new Map();      const results = [];      for (let i = 0; i < headwordCount; ++i) { diff --git a/ext/js/display/display-history.js b/ext/js/display/display-history.js index 183368d0..0806e17b 100644 --- a/ext/js/display/display-history.js +++ b/ext/js/display/display-history.js @@ -37,6 +37,7 @@ export class DisplayHistory extends EventDispatcher {          /** @type {Map<string, import('display-history').Entry>} */          this._historyMap = new Map(); +        /** @type {unknown} */          const historyState = history.state;          const {id, state} = (              isObjectNotArray(historyState) ? @@ -188,6 +189,7 @@ export class DisplayHistory extends EventDispatcher {      /** */      _updateStateFromHistory() { +        /** @type {unknown} */          let state = history.state;          let id = null;          if (isObjectNotArray(state)) { @@ -208,7 +210,7 @@ export class DisplayHistory extends EventDispatcher {          // Fallback          this._current.id = (typeof id === 'string' ? id : this._generateId()); -        this._current.state = state; +        this._current.state = /** @type {import('display-history').EntryState} */ (state);          this._current.content = null;          this._clear();      } diff --git a/ext/js/input/hotkey-help-controller.js b/ext/js/input/hotkey-help-controller.js index b495365d..f85735fc 100644 --- a/ext/js/input/hotkey-help-controller.js +++ b/ext/js/input/hotkey-help-controller.js @@ -67,9 +67,10 @@ export class HotkeyHelpController {              const hotkey = (global ? this._globalActionHotkeys : this._localActionHotkeys).get(action);              for (let i = 0, ii = attributes.length; i < ii; ++i) {                  const attribute = attributes[i]; +                /** @type {unknown} */                  let value;                  if (typeof hotkey !== 'undefined') { -                    value = /** @type {unknown} */ (multipleValues ? values[i] : values); +                    value = multipleValues ? values[i] : values;                      if (typeof value === 'string') {                          value = value.replace(replacementPattern, hotkey);                      } @@ -158,7 +159,8 @@ export class HotkeyHelpController {          if (typeof hotkey !== 'string') { return null; }          const data = /** @type {unknown} */ (parseJson(hotkey));          if (!Array.isArray(data)) { return null; } -        const [action, attributes, values] = /** @type {unknown[]} */ (data); +        const dataArray = /** @type {unknown[]} */ (data); +        const [action, attributes, values] = dataArray;          if (typeof action !== 'string') { return null; }          /** @type {string[]} */          const attributesArray = []; diff --git a/ext/js/language/translator.js b/ext/js/language/translator.js index 845d53d5..0d26b2f0 100644 --- a/ext/js/language/translator.js +++ b/ext/js/language/translator.js @@ -289,6 +289,7 @@ export class Translator {              return false;          } +        /** @type {Map<string, number>} */          const frequencyCounter = new Map();          for (const element of array1) { @@ -400,6 +401,7 @@ export class Translator {       * @returns {Map<string, import('translation-internal').DatabaseDeinflection[]>}       */      _groupDeinflectionsByTerm(deinflections) { +        /** @type {Map<string, import('translation-internal').DatabaseDeinflection[]>} */          const result = new Map();          for (const deinflection of deinflections) {              const {deinflectedText} = deinflection; @@ -455,7 +457,7 @@ export class Translator {          /** @type {import('translation-internal').DatabaseDeinflection[]} */          const deinflections = [];          const used = new Set(); -        /** @type {Map<string, import('core').SafeAny>} */ +        /** @type {import('translation-internal').TextCache} */          const sourceCache = new Map(); // For reusing text processors' outputs          for ( @@ -498,14 +500,15 @@ export class Translator {      /**       * @param {import('language').TextProcessorWithId<unknown>[]} textProcessors -     * @param {Map<string, unknown>} processorVariant +     * @param {import('translation-internal').TextProcessorVariant} processorVariant       * @param {string} text -     * @param {Map<string, import('core').SafeAny>} textCache +     * @param {import('translation-internal').TextCache} textCache       * @returns {string}       */      _applyTextProcessors(textProcessors, processorVariant, text, textCache) {          for (const {id, textProcessor: {process}} of textProcessors) {              const setting = processorVariant.get(id); +              let level1 = textCache.get(text);              if (!level1) {                  level1 = new Map(); @@ -522,7 +525,7 @@ export class Translator {                  text = process(text, setting);                  level2.set(setting, text);              } else { -                text = level2.get(setting); +                text = level2.get(setting) || '';              }          } @@ -681,6 +684,7 @@ export class Translator {          /** @type {import('dictionary-database').TermExactRequest[]} */          const termList = [];          const targetList = []; +        /** @type {Map<string, {groups: import('translator').DictionaryEntryGroup[]}>} */          const targetMap = new Map();          for (const group of groupedDictionaryEntries) { @@ -1362,10 +1366,10 @@ export class Translator {      /**       * @param {Map<string, unknown[]>} arrayVariants -     * @returns {Map<string, unknown>[]} +     * @returns {import('translation-internal').TextProcessorVariant[]}       */      _getArrayVariants(arrayVariants) { -        /** @type {Map<string, unknown>[]} */ +        /** @type {import('translation-internal').TextProcessorVariant[]} */          const results = [];          const variantKeys = [...arrayVariants.keys()];          const entryVariantLengths = []; @@ -1376,7 +1380,7 @@ export class Translator {          const totalVariants = entryVariantLengths.reduce((acc, length) => acc * length, 1);          for (let variantIndex = 0; variantIndex < totalVariants; ++variantIndex) { -            /** @type {Map<string, unknown>} */ +            /** @type {import('translation-internal').TextProcessorVariant}} */              const variant = new Map();              let remainingIndex = variantIndex; @@ -2076,6 +2080,7 @@ export class Translator {       * @param {boolean} ascending       */      _updateSortFrequencies(dictionaryEntries, dictionary, ascending) { +        /** @type {Map<number, number>} */          const frequencyMap = new Map();          for (const dictionaryEntry of dictionaryEntries) {              const {definitions, frequencies} = dictionaryEntry; diff --git a/ext/js/pages/settings/backup-controller.js b/ext/js/pages/settings/backup-controller.js index 5c168849..f0876d3f 100644 --- a/ext/js/pages/settings/backup-controller.js +++ b/ext/js/pages/settings/backup-controller.js @@ -574,12 +574,16 @@ export class BackupController {       * @returns {Promise<Blob>}       */      async _exportDatabase(databaseName) { -        const db = await new Dexie(databaseName).open(); +        const DexieConstructor = /** @type {import('dexie').DexieConstructor} */ (/** @type {unknown} */ (Dexie)); +        const db = new DexieConstructor(databaseName); +        await db.open(); +        /** @type {unknown} */ +        // @ts-expect-error - The export function is declared as an extension which has no type information.          const blob = await db.export({              progressCallback: this._databaseExportProgressCallback.bind(this)          }); -        await db.close(); -        return blob; +        db.close(); +        return /** @type {Blob} */ (blob);      }      /** */ diff --git a/ext/js/pages/settings/dictionary-import-controller.js b/ext/js/pages/settings/dictionary-import-controller.js index f63eb49e..ecfadc1f 100644 --- a/ext/js/pages/settings/dictionary-import-controller.js +++ b/ext/js/pages/settings/dictionary-import-controller.js @@ -306,6 +306,7 @@ export class DictionaryImportController {       * @param {Error[]} errors       */      _showErrors(errors) { +        /** @type {Map<string, number>} */          const uniqueErrors = new Map();          for (const error of errors) {              log.error(error); diff --git a/ext/js/templates/anki-template-renderer.js b/ext/js/templates/anki-template-renderer.js index ae3e7a36..888be9b0 100644 --- a/ext/js/templates/anki-template-renderer.js +++ b/ext/js/templates/anki-template-renderer.js @@ -659,12 +659,19 @@ export class AnkiTemplateRenderer {      }      /** +     * @param {import('template-renderer').HelperOptions} options +     * @returns {import('anki-templates').NoteData} +     */ +    _getNoteDataFromOptions(options) { +        return options.data.root; +    } + +    /**       * @type {import('template-renderer').HelperFunction<string>}       */      _formatGlossary(args, _context, options) {          const [dictionary, content] = /** @type {[dictionary: string, content: import('dictionary-data').TermGlossaryContent]} */ (args); -        /** @type {import('anki-templates').NoteData} */ -        const data = options.data.root; +        const data = this._getNoteDataFromOptions(options);          if (typeof content === 'string') { return this._safeString(this._stringToMultiLineHtml(content)); }          if (!(typeof content === 'object' && content !== null)) { return ''; }          switch (content.type) { @@ -703,8 +710,7 @@ export class AnkiTemplateRenderer {       * @type {import('template-renderer').HelperFunction<boolean>}       */      _hasMedia(args, _context, options) { -        /** @type {import('anki-templates').NoteData} */ -        const data = options.data.root; +        const data = this._getNoteDataFromOptions(options);          return this._mediaProvider.hasMedia(data, args, options.hash);      } @@ -712,8 +718,7 @@ export class AnkiTemplateRenderer {       * @type {import('template-renderer').HelperFunction<?string>}       */      _getMedia(args, _context, options) { -        /** @type {import('anki-templates').NoteData} */ -        const data = options.data.root; +        const data = this._getNoteDataFromOptions(options);          return this._mediaProvider.getMedia(data, args, options.hash);      } diff --git a/ext/js/templates/template-renderer-media-provider.js b/ext/js/templates/template-renderer-media-provider.js index 2f238e20..c4b07369 100644 --- a/ext/js/templates/template-renderer-media-provider.js +++ b/ext/js/templates/template-renderer-media-provider.js @@ -81,6 +81,8 @@ export class TemplateRendererMediaProvider {          let {value} = data;          const {escape = true} = namedArgs;          if (escape) { +            // Handlebars is a custom version of the library without type information, so it's assumed to be "any". +            // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment              value = Handlebars.Utils.escapeExpression(value);          }          return value; diff --git a/ext/js/templates/template-renderer.js b/ext/js/templates/template-renderer.js index 7bb93aa2..c5b7cd63 100644 --- a/ext/js/templates/template-renderer.js +++ b/ext/js/templates/template-renderer.js @@ -117,6 +117,8 @@ export class TemplateRenderer {          let instance = cache.get(template);          if (typeof instance === 'undefined') {              this._updateCacheSize(this._cacheMaxSize - 1); +            // Handlebars is a custom version of the library without type information, so it's assumed to be "any". +            // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment              instance = /** @type {import('handlebars').TemplateDelegate<import('anki-templates').NoteData>} */ (Handlebars.compileAST(template));              cache.set(template, instance);          } |