Skip to content

Commit 467ea88

Browse files
committed
chore: fix some bugs, eslint, knip
1 parent 8d323f3 commit 467ea88

File tree

3 files changed

+64
-30
lines changed

3 files changed

+64
-30
lines changed

packages/devtools/src/client/client.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ export class DevtoolsClient {
1212
new Map()
1313
#eventTarget: EventTarget
1414

15-
constructor({ port = 42069, eventTarget = new EventTarget() } = {}) {
15+
constructor({ port = 42069 } = {}) {
1616
this.#port = port
1717
this.#socket = null
1818
this.#eventSource = null
1919
this.#globalListeners = new Set()
20-
this.#eventTarget = eventTarget
20+
this.#eventTarget =
21+
typeof window !== 'undefined' ? window : new EventTarget()
2122
}
2223

2324
private connectSSE() {
@@ -134,10 +135,8 @@ export class DevtoolsClient {
134135
on(eventName: string, cb: (event: DevtoolsEvent<string>) => void) {
135136
const handler = (e: Event) => cb((e as CustomEvent).detail)
136137
this.#eventTarget.addEventListener(eventName, handler)
137-
this.#globalListeners.add(cb)
138138
return () => {
139139
this.#eventTarget.removeEventListener(eventName, handler)
140-
this.#globalListeners.delete(cb)
141140
}
142141
}
143142
/**

packages/devtools/src/server/plugin.ts

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { StandardSchemaV1 } from '@standard-schema/spec'
2+
23
interface DevtoolsEvent<TEventName extends string, TPayload = any> {
34
type: TEventName
45
payload: TPayload
@@ -11,9 +12,9 @@ export type EventMap<TEventPrefix extends string> = Record<
1112
>
1213

1314
type AllDevtoolsEvents<TEventMap extends Record<string, any>> = {
14-
[K in keyof TEventMap]: DevtoolsEvent<
15-
K & string,
16-
StandardSchemaV1.InferOutput<TEventMap[K]>
15+
[Key in keyof TEventMap]: DevtoolsEvent<
16+
Key & string,
17+
StandardSchemaV1.InferOutput<TEventMap[Key]>
1718
>
1819
}[keyof TEventMap]
1920

@@ -32,7 +33,7 @@ export class DevtoolsPlugin<
3233
#globalListeners: Set<(msg: DevtoolsEvent<string, any>) => void>
3334
#eventListeners: Map<string, Set<(msg: DevtoolsEvent<string, any>) => void>> =
3435
new Map()
35-
36+
#eventSource: EventSource | null
3637
constructor({
3738
port = 42069,
3839
pluginId,
@@ -42,12 +43,28 @@ export class DevtoolsPlugin<
4243
pluginId: TPluginId
4344
eventTarget?: EventTarget
4445
}) {
46+
this.#eventSource = null
4547
this.#port = port
4648
this.#socket = null
4749
this.#globalListeners = new Set()
4850
this.#pluginId = pluginId
49-
this.#eventTarget =
50-
eventTarget || (globalThis as any).__EVENT_TARGET__ || new EventTarget()
51+
this.#eventTarget = this.getGlobalTarget(eventTarget)
52+
}
53+
54+
private connectSSE() {
55+
this.#eventSource = new EventSource(
56+
`http://localhost:${this.#port}/__devtools/sse`,
57+
)
58+
this.#eventSource.onmessage = (e) => this.handleEventReceived(e.data)
59+
}
60+
private getGlobalTarget(eventTarget?: EventTarget) {
61+
if (typeof window !== 'undefined') {
62+
return window
63+
}
64+
if (typeof globalThis !== 'undefined' && globalThis.__EVENT_TARGET__) {
65+
return globalThis.__EVENT_TARGET__
66+
}
67+
return eventTarget || new EventTarget()
5168
}
5269

5370
private connectWebSocket() {
@@ -63,10 +80,13 @@ export class DevtoolsPlugin<
6380
}
6481

6582
connect() {
66-
if (typeof window === 'undefined') return
6783
try {
6884
this.connectWebSocket()
69-
} catch {}
85+
} catch {
86+
// Do not try to connect to SSE if we're on the server side
87+
if (typeof window === 'undefined') return
88+
this.connectSSE()
89+
}
7090
}
7191

7292
private emitToGlobalListeners(event: DevtoolsEvent<string, any>) {
@@ -94,35 +114,51 @@ export class DevtoolsPlugin<
94114

95115
private emitEventToBus(event: DevtoolsEvent<string, any>) {
96116
const json = JSON.stringify(event)
117+
// try to emit it to the event bus first
97118
if (this.#socket && this.#socket.readyState === WebSocket.OPEN) {
98119
this.#socket.send(json)
120+
// try to emit to SSE if WebSocket is not available (this will only happen on the client side)
121+
} else if (this.#eventSource) {
122+
fetch(`http://localhost:${this.#port}/__devtools/send`, {
123+
method: 'POST',
124+
headers: { 'Content-Type': 'application/json' },
125+
body: json,
126+
}).catch(() => {})
127+
// otherwise, emit it to the event target
128+
} else {
129+
this.emitEventToEventTarget(event)
99130
}
100131
}
101132

102-
emit<K extends keyof TEventMap>(
133+
private emitEventToEventTarget(event: DevtoolsEvent<string, any>) {
134+
this.#eventTarget.dispatchEvent(
135+
new CustomEvent(event.type, { detail: event }),
136+
)
137+
}
138+
139+
emit<TKey extends keyof TEventMap>(
103140
event: DevtoolsEvent<
104-
K & string,
105-
StandardSchemaV1.InferOutput<TEventMap[K]>
141+
TKey & string,
142+
StandardSchemaV1.InferOutput<TEventMap[TKey]>
106143
>,
107144
) {
108145
this.emitEventToBus(event)
109146
}
110147

111-
on<K extends keyof TEventMap>(
112-
eventName: K,
148+
on<TKey extends keyof TEventMap>(
149+
eventName: TKey,
113150
cb: (
114151
event: DevtoolsEvent<
115-
K & string,
116-
StandardSchemaV1.InferOutput<TEventMap[K]>
152+
TKey & string,
153+
StandardSchemaV1.InferOutput<TEventMap[TKey]>
117154
>,
118155
) => void,
119156
) {
120157
const handler = (e: Event) => cb((e as CustomEvent).detail)
121158
this.#eventTarget.addEventListener(eventName as string, handler)
122-
this.#globalListeners.add(cb as any)
159+
123160
return () => {
124161
this.#eventTarget.removeEventListener(eventName as string, handler)
125-
this.#globalListeners.delete(cb as any)
126162
}
127163
}
128164

packages/devtools/src/server/server.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,18 @@ import { WebSocket, WebSocketServer } from 'ws'
33

44
// Shared types
55

6-
export interface DevtoolsMessage {
7-
pluginId: string
8-
type: string
9-
payload?: any
10-
}
11-
export interface DevtoolsEvent<TEventName extends string, TPayload = any> {
6+
interface DevtoolsEvent<TEventName extends string, TPayload = any> {
127
type: TEventName
138
payload: TPayload
149
pluginId?: string // Optional pluginId to filter events by plugin
1510
}
1611
// Used so no new server starts up when HMR happens
1712
declare global {
13+
// eslint-disable-next-line no-var
1814
var __DEVTOOLS_SERVER__: http.Server | null
15+
// eslint-disable-next-line no-var
1916
var __DEVTOOLS_WSS_SERVER__: WebSocketServer | null
17+
// eslint-disable-next-line no-var
2018
var __EVENT_TARGET__: EventTarget | null
2119
}
2220

@@ -30,14 +28,15 @@ export class DevtoolsServer {
3028
#globalListeners = new Set<(msg: DevtoolsEvent<string>) => void>()
3129
constructor({ port = 42069, eventTarget = new EventTarget() } = {}) {
3230
this.#port = port
33-
this.#eventTarget = eventTarget
34-
globalThis.__EVENT_TARGET__ = eventTarget
31+
this.#eventTarget = globalThis.__EVENT_TARGET__ ?? eventTarget
32+
if (!globalThis.__EVENT_TARGET__) {
33+
globalThis.__EVENT_TARGET__ = eventTarget
34+
}
3535
this.#server = globalThis.__DEVTOOLS_SERVER__ ?? null
3636
this.#wssServer = globalThis.__DEVTOOLS_WSS_SERVER__ ?? null
3737
}
3838

3939
private emitToServer(event: DevtoolsEvent<string>) {
40-
console.log(this.#globalListeners.entries())
4140
this.#globalListeners.forEach((l) => l(event))
4241
this.#eventTarget.dispatchEvent(
4342
new CustomEvent(event.type, { detail: event }),

0 commit comments

Comments
 (0)