diff options
| author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2023-11-27 12:48:14 -0500 | 
|---|---|---|
| committer | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2023-11-27 12:48:14 -0500 | 
| commit | 4da4827bcbcdd1ef163f635d9b29416ff272b0bb (patch) | |
| tree | a8a0f1a8befdb78a554e1be91f2c6059ca3ad5f9 /ext/js/yomitan.js | |
| parent | fd6bba8a2a869eaf2b2c1fa49001f933fce3c618 (diff) | |
Add JSDoc type annotations to project (rebased)
Diffstat (limited to 'ext/js/yomitan.js')
| -rw-r--r-- | ext/js/yomitan.js | 76 | 
1 files changed, 60 insertions, 16 deletions
| diff --git a/ext/js/yomitan.js b/ext/js/yomitan.js index 5535aeb6..3c0f7cb9 100644 --- a/ext/js/yomitan.js +++ b/ext/js/yomitan.js @@ -18,7 +18,8 @@  import {API} from './comm/api.js';  import {CrossFrameAPI} from './comm/cross-frame-api.js'; -import {EventDispatcher, deferPromise, invokeMessageHandler, log, serializeError} from './core.js'; +import {EventDispatcher, deferPromise, invokeMessageHandler, log} from './core.js'; +import {ExtensionError} from './core/extension-error.js';  // Set up chrome alias if it's not available (Edge Legacy)  if ((() => { @@ -36,51 +37,66 @@ if ((() => {      }      return (hasBrowser && !hasChrome);  })()) { +    // @ts-ignore - objects should have roughly the same interface      chrome = browser;  }  /**   * The Yomitan class is a core component through which various APIs are handled and invoked. + * @augments EventDispatcher<import('extension').ExtensionEventType>   */ -class Yomitan extends EventDispatcher { +export class Yomitan extends EventDispatcher {      /**       * Creates a new instance. The instance should not be used until it has been fully prepare()'d.       */      constructor() {          super(); +        /** @type {string} */ +        this._extensionName = 'Yomitan';          try {              const manifest = chrome.runtime.getManifest();              this._extensionName = `${manifest.name} v${manifest.version}`;          } catch (e) { -            this._extensionName = 'Yomitan'; +            // NOP          } +        /** @type {?string} */ +        this._extensionUrlBase = null;          try {              this._extensionUrlBase = chrome.runtime.getURL('/');          } catch (e) { -            this._extensionUrlBase = null; +            // NOP          } +        /** @type {?boolean} */          this._isBackground = null; +        /** @type {?API} */          this._api = null; +        /** @type {?CrossFrameAPI} */          this._crossFrame = null; +        /** @type {boolean} */          this._isExtensionUnloaded = false; +        /** @type {boolean} */          this._isTriggeringExtensionUnloaded = false; +        /** @type {boolean} */          this._isReady = false; -        const {promise, resolve} = deferPromise(); +        const {promise, resolve} = /** @type {import('core').DeferredPromiseDetails<void>} */ (deferPromise()); +        /** @type {Promise<void>} */          this._isBackendReadyPromise = promise; +        /** @type {?(() => void)} */          this._isBackendReadyPromiseResolve = resolve; -        this._messageHandlers = new Map([ +        /** @type {import('core').MessageHandlerMap} */ +        this._messageHandlers = new Map(/** @type {import('core').MessageHandlerArray} */ ([              ['Yomitan.isReady',         {async: false, handler: this._onMessageIsReady.bind(this)}],              ['Yomitan.backendReady',    {async: false, handler: this._onMessageBackendReady.bind(this)}],              ['Yomitan.getUrl',          {async: false, handler: this._onMessageGetUrl.bind(this)}],              ['Yomitan.optionsUpdated',  {async: false, handler: this._onMessageOptionsUpdated.bind(this)}],              ['Yomitan.databaseUpdated', {async: false, handler: this._onMessageDatabaseUpdated.bind(this)}],              ['Yomitan.zoomChanged',     {async: false, handler: this._onMessageZoomChanged.bind(this)}] -        ]); +        ]));      }      /** @@ -88,7 +104,8 @@ class Yomitan extends EventDispatcher {       * @type {boolean}       */      get isBackground() { -        return this._isBackground; +        if (this._isBackground === null) { throw new Error('Not prepared'); } +        return /** @type {boolean} */ (this._isBackground);      }      /** @@ -105,6 +122,7 @@ class Yomitan extends EventDispatcher {       * @type {API}       */      get api() { +        if (this._api === null) { throw new Error('Not prepared'); }          return this._api;      } @@ -114,6 +132,7 @@ class Yomitan extends EventDispatcher {       * @type {CrossFrameAPI}       */      get crossFrame() { +        if (this._crossFrame === null) { throw new Error('Not prepared'); }          return this._crossFrame;      } @@ -158,19 +177,22 @@ class Yomitan extends EventDispatcher {      /**       * Runs `chrome.runtime.sendMessage()` with additional exception handling events. -     * @param {...*} args The arguments to be passed to `chrome.runtime.sendMessage()`. -     * @returns {void} The result of the `chrome.runtime.sendMessage()` call. +     * @param {import('extension').ChromeRuntimeSendMessageArgs} args The arguments to be passed to `chrome.runtime.sendMessage()`.       * @throws {Error} Errors thrown by `chrome.runtime.sendMessage()` are re-thrown.       */      sendMessage(...args) {          try { -            return chrome.runtime.sendMessage(...args); +            // @ts-ignore - issue with type conversion, somewhat difficult to resolve in pure JS +            chrome.runtime.sendMessage(...args);          } catch (e) {              this.triggerExtensionUnloaded();              throw e;          }      } +    /** +     * Triggers the extensionUnloaded event. +     */      triggerExtensionUnloaded() {          this._isExtensionUnloaded = true;          if (this._isTriggeringExtensionUnloaded) { return; } @@ -184,51 +206,73 @@ class Yomitan extends EventDispatcher {      // Private +    /** +     * @returns {string} +     */      _getUrl() {          return location.href;      } -    _getLogContext() { -        return {url: this._getUrl()}; -    } - +    /** @type {import('extension').ChromeRuntimeOnMessageCallback} */      _onMessage({action, params}, sender, callback) {          const messageHandler = this._messageHandlers.get(action);          if (typeof messageHandler === 'undefined') { return false; }          return invokeMessageHandler(messageHandler, params, callback, sender);      } +    /** +     * @returns {boolean} +     */      _onMessageIsReady() {          return this._isReady;      } +    /** +     * @returns {void} +     */      _onMessageBackendReady() {          if (this._isBackendReadyPromiseResolve === null) { return; }          this._isBackendReadyPromiseResolve();          this._isBackendReadyPromiseResolve = null;      } +    /** +     * @returns {{url: string}} +     */      _onMessageGetUrl() {          return {url: this._getUrl()};      } +    /** +     * @param {{source: string}} params +     */      _onMessageOptionsUpdated({source}) {          if (source !== 'background') {              this.trigger('optionsUpdated', {source});          }      } +    /** +     * @param {{type: string, cause: string}} params +     */      _onMessageDatabaseUpdated({type, cause}) {          this.trigger('databaseUpdated', {type, cause});      } +    /** +     * @param {{oldZoomFactor: number, newZoomFactor: number}} params +     */      _onMessageZoomChanged({oldZoomFactor, newZoomFactor}) {          this.trigger('zoomChanged', {oldZoomFactor, newZoomFactor});      } +    /** +     * @param {{error: unknown, level: import('log').LogLevel, context?: import('log').LogContext}} params +     */      async _onForwardLog({error, level, context}) {          try { -            await this._api.log(serializeError(error), level, context); +            const api = /** @type {API} */ (this._api); +            await api.log(ExtensionError.serialize(error), level, context);          } catch (e) {              // NOP          } |