diff options
| author | Darius Jahandarie <djahandarie@gmail.com> | 2023-12-06 03:53:16 +0000 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-12-06 03:53:16 +0000 | 
| commit | bd5bc1a5db29903bc098995cd9262c4576bf76af (patch) | |
| tree | c9214189e0214480fcf6539ad1c6327aef6cbd1c /ext/js/comm/mecab.js | |
| parent | fd6bba8a2a869eaf2b2c1fa49001f933fce3c618 (diff) | |
| parent | 23e6fb76319c9ed7c9bcdc3efba39bc5dd38f288 (diff) | |
Merge pull request #339 from toasted-nutbread/type-annotations
Type annotations
Diffstat (limited to 'ext/js/comm/mecab.js')
| -rw-r--r-- | ext/js/comm/mecab.js | 84 | 
1 files changed, 60 insertions, 24 deletions
| diff --git a/ext/js/comm/mecab.js b/ext/js/comm/mecab.js index c7314605..0a87463b 100644 --- a/ext/js/comm/mecab.js +++ b/ext/js/comm/mecab.js @@ -24,32 +24,26 @@ import {EventListenerCollection} from '../core.js';   */  export class Mecab {      /** -     * The resulting data from an invocation of `parseText`. -     * @typedef {object} ParseResult -     * @property {string} name The dictionary name for the parsed result. -     * @property {ParseTerm[]} lines The resulting parsed terms. -     */ - -    /** -     * A fragment of the parsed text. -     * @typedef {object} ParseFragment -     * @property {string} term The term. -     * @property {string} reading The reading of the term. -     * @property {string} source The source text. -     */ - -    /**       * Creates a new instance of the class.       */      constructor() { +        /** @type {?chrome.runtime.Port} */          this._port = null; +        /** @type {number} */          this._sequence = 0; +        /** @type {Map<number, {resolve: (value: unknown) => void, reject: (reason?: unknown) => void, timer: import('core').Timeout}>} */          this._invocations = new Map(); +        /** @type {EventListenerCollection} */          this._eventListeners = new EventListenerCollection(); +        /** @type {number} */          this._timeout = 5000; +        /** @type {number} */          this._version = 1; +        /** @type {?number} */          this._remoteVersion = null; +        /** @type {boolean} */          this._enabled = false; +        /** @type {?Promise<void>} */          this._setupPortPromise = null;      } @@ -107,7 +101,7 @@ export class Mecab {      /**       * Gets the version of the MeCab component. -     * @returns {?number} The version of the MeCab component, or `null` if the component was not found. +     * @returns {Promise<?number>} The version of the MeCab component, or `null` if the component was not found.       */      async getVersion() {          try { @@ -135,17 +129,26 @@ export class Mecab {       * ]       * ```       * @param {string} text The string to parse. -     * @returns {ParseResult[]} A collection of parsing results of the text. +     * @returns {Promise<import('mecab').ParseResult[]>} A collection of parsing results of the text.       */      async parseText(text) {          await this._setupPort();          const rawResults = await this._invoke('parse_text', {text}); -        return this._convertParseTextResults(rawResults); +        // Note: The format of rawResults is not validated +        return this._convertParseTextResults(/** @type {import('mecab').ParseResultRaw} */ (rawResults));      }      // Private -    _onMessage({sequence, data}) { +    /** +     * @param {unknown} message +     */ +    _onMessage(message) { +        if (typeof message !== 'object' || message === null) { return; } + +        const {sequence, data} = /** @type {import('core').SerializableObject} */ (message); +        if (typeof sequence !== 'number') { return; } +          const invocation = this._invocations.get(sequence);          if (typeof invocation === 'undefined') { return; } @@ -155,6 +158,9 @@ export class Mecab {          this._invocations.delete(sequence);      } +    /** +     * @returns {void} +     */      _onDisconnect() {          if (this._port === null) { return; }          const e = chrome.runtime.lastError; @@ -166,10 +172,16 @@ export class Mecab {          this._clearPort();      } +    /** +     * @param {string} action +     * @param {import('core').SerializableObject} params +     * @returns {Promise<unknown>} +     */      _invoke(action, params) {          return new Promise((resolve, reject) => {              if (this._port === null) {                  reject(new Error('Port disconnected')); +                return;              }              const sequence = this._sequence++; @@ -179,15 +191,21 @@ export class Mecab {                  reject(new Error(`MeCab invoke timed out after ${this._timeout}ms`));              }, this._timeout); -            this._invocations.set(sequence, {resolve, reject, timer}, this._timeout); +            this._invocations.set(sequence, {resolve, reject, timer});              this._port.postMessage({action, params, sequence});          });      } +    /** +     * @param {import('mecab').ParseResultRaw} rawResults +     * @returns {import('mecab').ParseResult[]} +     */      _convertParseTextResults(rawResults) { +        /** @type {import('mecab').ParseResult[]} */          const results = [];          for (const [name, rawLines] of Object.entries(rawResults)) { +            /** @type {import('mecab').ParseFragment[][]} */              const lines = [];              for (const rawLine of rawLines) {                  const line = []; @@ -204,6 +222,9 @@ export class Mecab {          return results;      } +    /** +     * @returns {Promise<void>} +     */      async _setupPort() {          if (!this._enabled) {              throw new Error('MeCab not enabled'); @@ -214,10 +235,13 @@ export class Mecab {          try {              await this._setupPortPromise;          } catch (e) { -            throw new Error(e.message); +            throw new Error(e instanceof Error ? e.message : `${e}`);          }      } +    /** +     * @returns {Promise<void>} +     */      async _setupPort2() {          const port = chrome.runtime.connectNative('yomitan_mecab');          this._eventListeners.addListener(port.onMessage, this._onMessage.bind(this)); @@ -225,7 +249,14 @@ export class Mecab {          this._port = port;          try { -            const {version} = await this._invoke('get_version', {}); +            const data = await this._invoke('get_version', {}); +            if (typeof data !== 'object' || data === null) { +                throw new Error('Invalid version'); +            } +            const {version} = /** @type {import('core').SerializableObject} */ (data); +            if (typeof version !== 'number') { +                throw new Error('Invalid version'); +            }              this._remoteVersion = version;              if (version !== this._version) {                  throw new Error(`Unsupported MeCab native messenger version ${version}. Yomitan supports version ${this._version}.`); @@ -238,9 +269,14 @@ export class Mecab {          }      } +    /** +     * @returns {void} +     */      _clearPort() { -        this._port.disconnect(); -        this._port = null; +        if (this._port !== null) { +            this._port.disconnect(); +            this._port = null; +        }          this._invocations.clear();          this._eventListeners.removeAllEventListeners();          this._sequence = 0; |