Эх сурвалжийг харах

Improved EventEmitter for tauri api shell (#4697)

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
Danil Karpenko 3 жил өмнө
parent
commit
aa9f1243e6

+ 5 - 0
.changes/improve-event-emitter.md

@@ -0,0 +1,5 @@
+---
+"api": minor
+---
+
+Improve shell's `Command`, `Command.stdout` and `Command.stderr` events with new `once`, `off`, `listenerCount`, `prependListener`, `prependOnceListener` and `removeAllListeners` functions.

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
core/tauri/scripts/bundle.js


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
examples/api/dist/assets/index.css


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
examples/api/dist/assets/index.js


+ 1 - 8
examples/api/yarn.lock

@@ -93,9 +93,7 @@
     svelte-hmr "^0.14.12"
 
 "@tauri-apps/api@../../tooling/api/dist":
-  version "1.0.1"
-  dependencies:
-    type-fest "2.14.0"
+  version "1.0.2"
 
 "@unocss/cli@0.39.3":
   version "0.39.3"
@@ -888,11 +886,6 @@ totalist@^3.0.0:
   resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.0.tgz#4ef9c58c5f095255cdc3ff2a0a55091c57a3a1bd"
   integrity sha512-eM+pCBxXO/njtF7vdFsHuqb+ElbxqtI4r5EAvk6grfAFyJ6IvWlSkfZ5T9ozC6xWw3Fj1fGoSmrl0gUs46JVIw==
 
-type-fest@2.14.0:
-  version "2.14.0"
-  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.14.0.tgz#f990e19169517d689c98e16d128b231022b27e12"
-  integrity sha512-hQnTQkFjL5ik6HF2fTAM8ycbr94UbQXK364wF930VHb0dfBJ5JBP8qwrR8TaK9zwUEk7meruo2JAUDMwvuxd/w==
-
 ufo@^0.8.4:
   version "0.8.4"
   resolved "https://registry.yarnpkg.com/ufo/-/ufo-0.8.4.tgz#23e9ed82398d2116dcb378e8fba5ced8eca2ee40"

+ 140 - 27
tooling/api/src/shell.ts

@@ -134,46 +134,159 @@ async function execute(
 }
 
 class EventEmitter<E extends string> {
-  /** @ignore  */
+  /** @ignore */
   // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
-  private eventListeners: {
-    [key: string]: Array<(arg: any) => void>
-  } = Object.create(null)
+  private eventListeners: Record<E, Array<(...args: any[]) => void>> =
+    Object.create(null)
 
-  /** @ignore  */
-  private addEventListener(event: string, handler: (arg: any) => void): void {
-    if (event in this.eventListeners) {
+  /**
+   * Alias for `emitter.on(eventName, listener)`.
+   */
+  addListener(eventName: E, listener: (...args: any[]) => void): this {
+    return this.on(eventName, listener)
+  }
+
+  /**
+   * Alias for `emitter.off(eventName, listener)`.
+   */
+  removeListener(eventName: E, listener: (...args: any[]) => void): this {
+    return this.off(eventName, listener)
+  }
+
+  /**
+   * Adds the `listener` function to the end of the listeners array for the
+   * event named `eventName`. No checks are made to see if the `listener` has
+   * already been added. Multiple calls passing the same combination of `eventName`and `listener` will result in the `listener` being added, and called, multiple
+   * times.
+   *
+   * Returns a reference to the `EventEmitter`, so that calls can be chained.
+   * @param eventName The name of the event.
+   * @param listener The callback function
+   */
+  on(eventName: E, listener: (...args: any[]) => void): this {
+    if (eventName in this.eventListeners) {
       // eslint-disable-next-line security/detect-object-injection
-      this.eventListeners[event].push(handler)
+      this.eventListeners[eventName].push(listener)
     } else {
       // eslint-disable-next-line security/detect-object-injection
-      this.eventListeners[event] = [handler]
+      this.eventListeners[eventName] = [listener]
     }
+    return this
   }
 
-  /** @ignore  */
-  _emit(event: E, payload: any): void {
-    if (event in this.eventListeners) {
-      // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
-      const listeners = this.eventListeners[event as any]
-      for (const listener of listeners) {
-        listener(payload)
-      }
+  /**
+   * Adds a **one-time**`listener` function for the event named `eventName`. The
+   * next time `eventName` is triggered, this listener is removed and then invoked.
+   *
+   * Returns a reference to the `EventEmitter`, so that calls can be chained.
+   *
+   * @param eventName The name of the event.
+   * @param listener The callback function
+   */
+  once(eventName: E, listener: (...args: any[]) => void): this {
+    const wrapper = (...args: any[]): void => {
+      this.removeListener(eventName, wrapper)
+      // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
+      listener(...args)
+    }
+    return this.addListener(eventName, wrapper)
+  }
+
+  /**
+   * Removes the all specified listener from the listener array for the event eventName
+   * Returns a reference to the `EventEmitter`, so that calls can be chained.
+   */
+  off(eventName: E, listener: (...args: any[]) => void): this {
+    if (eventName in this.eventListeners) {
+      // eslint-disable-next-line security/detect-object-injection
+      this.eventListeners[eventName] = this.eventListeners[eventName].filter(
+        (l) => l !== listener
+      )
+    }
+    return this
+  }
+
+  /**
+   * Removes all listeners, or those of the specified eventName.
+   *
+   * Returns a reference to the `EventEmitter`, so that calls can be chained.
+   */
+  removeAllListeners(event?: E): this {
+    if (event) {
+      // eslint-disable-next-line @typescript-eslint/no-dynamic-delete,security/detect-object-injection
+      delete this.eventListeners[event]
+    } else {
+      // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+      this.eventListeners = Object.create(null)
     }
+    return this
   }
 
   /**
-   * Listen to an event from the child process.
+   * @ignore
+   * Synchronously calls each of the listeners registered for the event named`eventName`, in the order they were registered, passing the supplied arguments
+   * to each.
    *
-   * @param event The event name.
-   * @param handler The event handler.
+   * Returns `true` if the event had listeners, `false` otherwise.
+   */
+  emit(eventName: E, ...args: any[]): boolean {
+    if (eventName in this.eventListeners) {
+      // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,security/detect-object-injection
+      const listeners = this.eventListeners[eventName]
+      // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
+      for (const listener of listeners) listener(...args)
+      return true
+    }
+    return false
+  }
+
+  /**
+   * Returns the number of listeners listening to the event named `eventName`.
+   */
+  listenerCount(eventName: E): number {
+    if (eventName in this.eventListeners)
+      // eslint-disable-next-line security/detect-object-injection
+      return this.eventListeners[eventName].length
+    return 0
+  }
+
+  /**
+   * Adds the `listener` function to the _beginning_ of the listeners array for the
+   * event named `eventName`. No checks are made to see if the `listener` has
+   * already been added. Multiple calls passing the same combination of `eventName`and `listener` will result in the `listener` being added, and called, multiple
+   * times.
    *
-   * @return The `this` instance for chained calls.
+   * Returns a reference to the `EventEmitter`, so that calls can be chained.
+   * @param eventName The name of the event.
+   * @param listener The callback function
    */
-  on(event: E, handler: (arg: any) => void): EventEmitter<E> {
-    this.addEventListener(event, handler)
+  prependListener(eventName: E, listener: (...args: any[]) => void): this {
+    if (eventName in this.eventListeners) {
+      // eslint-disable-next-line security/detect-object-injection
+      this.eventListeners[eventName].unshift(listener)
+    } else {
+      // eslint-disable-next-line security/detect-object-injection
+      this.eventListeners[eventName] = [listener]
+    }
     return this
   }
+
+  /**
+   * Adds a **one-time**`listener` function for the event named `eventName` to the_beginning_ of the listeners array. The next time `eventName` is triggered, this
+   * listener is removed, and then invoked.
+   *
+   * Returns a reference to the `EventEmitter`, so that calls can be chained.
+   * @param eventName The name of the event.
+   * @param listener The callback function
+   */
+  prependOnceListener(eventName: E, listener: (...args: any[]) => void): this {
+    const wrapper = (...args: any[]): void => {
+      this.removeListener(eventName, wrapper)
+      // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
+      listener(...args)
+    }
+    return this.prependListener(eventName, wrapper)
+  }
 }
 
 class Child {
@@ -311,16 +424,16 @@ class Command extends EventEmitter<'close' | 'error'> {
       (event) => {
         switch (event.event) {
           case 'Error':
-            this._emit('error', event.payload)
+            this.emit('error', event.payload)
             break
           case 'Terminated':
-            this._emit('close', event.payload)
+            this.emit('close', event.payload)
             break
           case 'Stdout':
-            this.stdout._emit('data', event.payload)
+            this.stdout.emit('data', event.payload)
             break
           case 'Stderr':
-            this.stderr._emit('data', event.payload)
+            this.stderr.emit('data', event.payload)
             break
         }
       },

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно