Parcourir la source

Replace strict-event-emitter with EventTarget (#16374)

GitOrigin-RevId: 3b0afc3cd7bf3d11f35a3de23cb94061d2d6c69b
Alf Eaton il y a 2 ans
Parent
commit
9804ebe12c

+ 0 - 14
package-lock.json

@@ -39473,12 +39473,6 @@
         "queue-tick": "^1.0.1"
       }
     },
-    "node_modules/strict-event-emitter": {
-      "version": "0.5.1",
-      "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz",
-      "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==",
-      "dev": true
-    },
     "node_modules/strict-uri-encode": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
@@ -46658,7 +46652,6 @@
         "sinon-mongoose": "^2.3.0",
         "socket.io-mock": "^1.3.1",
         "storybook": "^7.4.0",
-        "strict-event-emitter": "^0.5.1",
         "terser-webpack-plugin": "^5.3.9",
         "thread-loader": "^4.0.2",
         "timekeeper": "^2.2.0",
@@ -55432,7 +55425,6 @@
         "sinon-mongoose": "^2.3.0",
         "socket.io-mock": "^1.3.1",
         "storybook": "^7.4.0",
-        "strict-event-emitter": "^0.5.1",
         "terser-webpack-plugin": "^5.3.9",
         "thread-loader": "^4.0.2",
         "timekeeper": "^2.2.0",
@@ -80671,12 +80663,6 @@
         "queue-tick": "^1.0.1"
       }
     },
-    "strict-event-emitter": {
-      "version": "0.5.1",
-      "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz",
-      "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==",
-      "dev": true
-    },
     "strict-uri-encode": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",

+ 8 - 6
services/web/frontend/js/features/ide-react/connection/connection-manager.ts

@@ -1,7 +1,6 @@
 import { ConnectionError, ConnectionState } from './types/connection-state'
 import SocketIoShim from '../../../ide/connection/SocketIoShim'
 import getMeta from '../../../utils/meta'
-import { Emitter } from 'strict-event-emitter'
 import { Socket } from '@/features/ide-react/connection/types/socket'
 import { debugConsole } from '@/utils/debugging'
 
@@ -29,11 +28,12 @@ const initialState: ConnectionState = {
   error: '',
 }
 
-type Events = {
-  statechange: [{ state: ConnectionState; previousState: ConnectionState }]
-}
+export class StateChangeEvent extends CustomEvent<{
+  state: ConnectionState
+  previousState: ConnectionState
+}> {}
 
-export class ConnectionManager extends Emitter<Events> {
+export class ConnectionManager extends EventTarget {
   state: ConnectionState = initialState
   private connectionAttempt: number | null = null
   private gracefullyReconnectUntil = 0
@@ -113,7 +113,9 @@ export class ConnectionManager extends Emitter<Events> {
       previousState,
       state,
     })
-    this.emit('statechange', { state, previousState })
+    this.dispatchEvent(
+      new StateChangeEvent('statechange', { detail: { state, previousState } })
+    )
   }
 
   private onOnline() {

+ 9 - 6
services/web/frontend/js/features/ide-react/context/connection-context.tsx

@@ -8,7 +8,10 @@ import {
   useMemo,
 } from 'react'
 import { ConnectionState } from '../connection/types/connection-state'
-import { ConnectionManager } from '@/features/ide-react/connection/connection-manager'
+import {
+  ConnectionManager,
+  StateChangeEvent,
+} from '@/features/ide-react/connection/connection-manager'
 import { Socket } from '@/features/ide-react/connection/types/socket'
 import { secondsUntil } from '@/features/ide-react/connection/utils'
 import { useLocation } from '@/shared/hooks/use-location'
@@ -37,13 +40,13 @@ export const ConnectionProvider: FC = ({ children }) => {
   )
 
   useEffect(() => {
-    function handleStateChange(event: { state: ConnectionState }) {
-      setConnectionState(event.state)
-    }
-    connectionManager.on('statechange', handleStateChange)
+    const handleStateChange = ((event: StateChangeEvent) => {
+      setConnectionState(event.detail.state)
+    }) as EventListener
+    connectionManager.addEventListener('statechange', handleStateChange)
 
     return () => {
-      connectionManager.off('statechange', handleStateChange)
+      connectionManager.removeEventListener('statechange', handleStateChange)
     }
   }, [connectionManager])
 

+ 7 - 4
services/web/frontend/js/features/ide-react/context/metadata-context.tsx

@@ -18,11 +18,10 @@ import { useEditorContext } from '@/shared/context/editor-context'
 import { useIdeContext } from '@/shared/context/ide-context'
 import useSocketListener from '@/features/ide-react/hooks/use-socket-listener'
 import useEventListener from '@/shared/hooks/use-event-listener'
-import { FileTreeFindResult } from '@/features/ide-react/types/file-tree'
-import { Project } from '../../../../../types/project'
 import { useModalsContext } from '@/features/ide-react/context/modals-context'
 import { usePermissionsContext } from '@/features/ide-react/context/permissions-context'
 import { useTranslation } from 'react-i18next'
+import { IdeEvents } from '@/features/ide-react/create-ide-event-emitter'
 
 type DocumentMetadata = {
   labels: string[]
@@ -63,7 +62,9 @@ export const MetadataProvider: FC = ({ children }) => {
   const debouncerRef = useRef<Map<string, number>>(new Map()) // DocId => Timeout
 
   useEffect(() => {
-    const handleEntityDeleted = (entity: FileTreeFindResult) => {
+    const handleEntityDeleted = ({
+      detail: [entity],
+    }: CustomEvent<IdeEvents['entity:deleted']>) => {
       if (entity.type === 'doc') {
         setDocuments(documents => {
           return _.omit(documents, entity.entity._id)
@@ -184,7 +185,9 @@ export const MetadataProvider: FC = ({ children }) => {
   useEventListener('editor:metadata-outdated', handleMetadataOutdated)
 
   useEffect(() => {
-    const handleProjectJoined = ({ project }: { project: Project }) => {
+    const handleProjectJoined = ({
+      detail: [{ project }],
+    }: CustomEvent<IdeEvents['project:joined']>) => {
       if (project.deletedByExternalDataSource) {
         showGenericMessageModal(
           t('project_renamed_or_deleted'),

+ 4 - 1
services/web/frontend/js/features/ide-react/context/online-users-context.tsx

@@ -19,6 +19,7 @@ import { useFileTreeData } from '@/shared/context/file-tree-data-context'
 import { findDocEntityById } from '@/features/ide-react/util/find-doc-entity-by-id'
 import useSocketListener from '@/features/ide-react/hooks/use-socket-listener'
 import { debugConsole } from '@/utils/debugging'
+import { IdeEvents } from '@/features/ide-react/create-ide-event-emitter'
 
 type OnlineUser = {
   id: string
@@ -213,7 +214,9 @@ export const OnlineUsersProvider: FC = ({ children }) => {
 
   // Track the position of the main cursor
   useEffect(() => {
-    const handleCursorUpdate = (position: CursorPosition | null) => {
+    const handleCursorUpdate = ({
+      detail: [position],
+    }: CustomEvent<IdeEvents['cursor:editor:update']>) => {
       if (position) {
         setCurrentPosition(position)
       }

+ 4 - 1
services/web/frontend/js/features/ide-react/context/references-context.tsx

@@ -18,6 +18,7 @@ import useScopeValue from '@/shared/hooks/use-scope-value'
 import { ReactScopeValueStore } from '@/features/ide-react/scope-value-store/react-scope-value-store'
 import { useFileTreeData } from '@/shared/context/file-tree-data-context'
 import { findDocEntityById } from '@/features/ide-react/util/find-doc-entity-by-id'
+import { IdeEvents } from '@/features/ide-react/create-ide-event-emitter'
 
 type References = {
   keys: string[]
@@ -108,7 +109,9 @@ export const ReferencesProvider: FC = ({ children }) => {
   )
 
   useEffect(() => {
-    const handleDocClosed = (doc: ShareJsDoc) => {
+    const handleDocClosed = ({
+      detail: [doc],
+    }: CustomEvent<IdeEvents['document:closed']>) => {
       if (
         doc.doc_id &&
         findDocEntityById(fileTreeData, doc.doc_id)?.name?.endsWith('.bib')

+ 28 - 4
services/web/frontend/js/features/ide-react/create-ide-event-emitter.ts

@@ -1,4 +1,3 @@
-import { Emitter } from 'strict-event-emitter'
 import { Project } from '../../../../types/project'
 import { PermissionsLevel } from '@/features/ide-react/types/permissions'
 import { ShareJsDoc } from '@/features/ide-react/editor/share-js-doc'
@@ -26,8 +25,33 @@ export type IdeEvents = {
   'entity:deleted': [entity: FileTreeFindResult]
 }
 
-export type IdeEventEmitter = Emitter<IdeEvents>
+export class IdeEventEmitter extends EventTarget {
+  emit<T extends keyof IdeEvents>(eventName: T, ...detail: IdeEvents[T]) {
+    this.dispatchEvent(new CustomEvent<IdeEvents[T]>(eventName, { detail }))
+  }
 
-export function createIdeEventEmitter(): IdeEventEmitter {
-  return new Emitter<IdeEvents>()
+  on<T extends keyof IdeEvents>(
+    eventName: T,
+    listener: (event: CustomEvent<IdeEvents[T]>) => void
+  ) {
+    this.addEventListener(eventName, listener as EventListener)
+  }
+
+  once<T extends keyof IdeEvents>(
+    eventName: T,
+    listener: (event: CustomEvent<IdeEvents[T]>) => void
+  ) {
+    this.addEventListener(eventName, listener as EventListener, { once: true })
+  }
+
+  off<T extends keyof IdeEvents>(
+    eventName: T,
+    listener: (event: CustomEvent<IdeEvents[T]>) => void
+  ) {
+    this.removeEventListener(eventName, listener as EventListener)
+  }
+}
+
+export function createIdeEventEmitter() {
+  return new IdeEventEmitter()
 }

+ 4 - 5
services/web/frontend/js/features/ide-react/scope-event-emitter/react-scope-event-emitter.ts

@@ -2,14 +2,13 @@ import {
   ScopeEventEmitter,
   ScopeEventName,
 } from '../../../../../types/ide/scope-event-emitter'
-import EventEmitter from 'events'
 
 export class ReactScopeEventEmitter implements ScopeEventEmitter {
   // eslint-disable-next-line no-useless-constructor
-  constructor(private readonly eventEmitter: EventEmitter) {}
+  constructor(private readonly eventEmitter: EventTarget) {}
 
   emit(eventName: ScopeEventName, broadcast: boolean, ...detail: unknown[]) {
-    this.eventEmitter.emit(eventName, ...detail)
+    this.eventEmitter.dispatchEvent(new CustomEvent(eventName, { detail }))
   }
 
   on(eventName: ScopeEventName, listener: (...args: unknown[]) => void) {
@@ -18,9 +17,9 @@ export class ReactScopeEventEmitter implements ScopeEventEmitter {
     const wrappedListener = (...detail: unknown[]) => {
       listener({}, ...detail)
     }
-    this.eventEmitter.on(eventName, wrappedListener)
+    this.eventEmitter.addEventListener(eventName, wrappedListener)
     return () => {
-      this.eventEmitter.off(eventName, wrappedListener)
+      this.eventEmitter.removeEventListener(eventName, wrappedListener)
     }
   }
 }

+ 0 - 1
services/web/package.json

@@ -344,7 +344,6 @@
     "sinon-mongoose": "^2.3.0",
     "socket.io-mock": "^1.3.1",
     "storybook": "^7.4.0",
-    "strict-event-emitter": "^0.5.1",
     "terser-webpack-plugin": "^5.3.9",
     "thread-loader": "^4.0.2",
     "timekeeper": "^2.2.0",