Skip to content

Commit ba084f8

Browse files
authored
fix: remove dynamic imports to eliminate webpack warnings (#514)
* fix: remove dynamic imports to eliminate webpack warnings * chore: remove unused tests * chore: add extra tests for coverage
1 parent 0298b24 commit ba084f8

11 files changed

+858
-185
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ docs/v2
44
.env
55
.nyc_output
66
coverage/
7-
.claude/settings.local.json
7+
.claude/settings.local.json
8+
.DS_Store

src/RealtimeClient.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,16 +197,34 @@ export default class RealtimeClient {
197197
this._setAuthSafely('connect')
198198

199199
// Establish WebSocket connection
200-
if (!this.transport) {
200+
if (this.transport) {
201+
// Use custom transport if provided
202+
this.conn = new this.transport(this.endpointURL()) as WebSocketLike
203+
} else {
204+
// Try to use native WebSocket
201205
try {
202206
this.conn = WebSocketFactory.createWebSocket(this.endpointURL())
203207
} catch (error) {
204208
this._setConnectionState('disconnected')
205-
throw new Error(`WebSocket not available: ${(error as Error).message}`)
209+
const errorMessage = (error as Error).message
210+
211+
// Provide helpful error message based on environment
212+
if (errorMessage.includes('Node.js')) {
213+
throw new Error(
214+
`${errorMessage}\n\n` +
215+
'To use Realtime in Node.js, you need to provide a WebSocket implementation:\n\n' +
216+
'Option 1: Use Node.js 22+ which has native WebSocket support\n' +
217+
'Option 2: Install and provide the "ws" package:\n\n' +
218+
' npm install ws\n\n' +
219+
' import ws from "ws"\n' +
220+
' const client = new RealtimeClient(url, {\n' +
221+
' ...options,\n' +
222+
' transport: ws\n' +
223+
' })'
224+
)
225+
}
226+
throw new Error(`WebSocket not available: ${errorMessage}`)
206227
}
207-
} else {
208-
// Use custom transport if provided
209-
this.conn = new this.transport!(this.endpointURL()) as WebSocketLike
210228
}
211229
this._setupConnectionHandlers()
212230
}

src/lib/websocket-factory.ts

Lines changed: 19 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -33,32 +33,6 @@ export interface WebSocketEnvironment {
3333
}
3434

3535
export class WebSocketFactory {
36-
/**
37-
* Dynamic require that works in both CJS and ESM environments
38-
* Bulletproof against strict ESM environments where require might not be in scope
39-
* @private
40-
*/
41-
private static dynamicRequire(moduleId: string): any {
42-
try {
43-
// Check if we're in a Node.js environment first
44-
if (
45-
typeof process !== 'undefined' &&
46-
process.versions &&
47-
process.versions.node
48-
) {
49-
// In Node.js, both CJS and ESM support require for dynamic imports
50-
// Wrap in try/catch to handle strict ESM environments
51-
if (typeof require !== 'undefined') {
52-
return require(moduleId)
53-
}
54-
}
55-
return null
56-
} catch {
57-
// Catches any error from typeof require OR require() call in strict ESM
58-
return null
59-
}
60-
}
61-
6236
private static detectEnvironment(): WebSocketEnvironment {
6337
if (typeof WebSocket !== 'undefined') {
6438
return { type: 'native', constructor: WebSocket }
@@ -112,39 +86,31 @@ export class WebSocketFactory {
11286
process.versions.node
11387
) {
11488
const nodeVersion = parseInt(process.versions.node.split('.')[0])
89+
90+
// Node.js 22+ should have native WebSocket
11591
if (nodeVersion >= 22) {
116-
try {
117-
if (typeof globalThis.WebSocket !== 'undefined') {
118-
return { type: 'native', constructor: globalThis.WebSocket }
119-
}
120-
const undici = this.dynamicRequire('undici')
121-
if (undici && undici.WebSocket) {
122-
return { type: 'native', constructor: undici.WebSocket }
123-
}
124-
throw new Error('undici not available')
125-
} catch (err) {
126-
return {
127-
type: 'unsupported',
128-
error: `Node.js ${nodeVersion} detected but native WebSocket not found.`,
129-
workaround:
130-
'Install the "ws" package or check your Node.js installation.',
131-
}
92+
// Check if native WebSocket is available (should be in Node.js 22+)
93+
if (typeof globalThis.WebSocket !== 'undefined') {
94+
return { type: 'native', constructor: globalThis.WebSocket }
13295
}
133-
}
134-
try {
135-
// Use dynamic require to work in both CJS and ESM environments
136-
const ws = this.dynamicRequire('ws')
137-
if (ws) {
138-
return { type: 'ws', constructor: ws.WebSocket ?? ws }
139-
}
140-
throw new Error('ws package not available')
141-
} catch (err) {
96+
// If not available, user needs to provide it
14297
return {
14398
type: 'unsupported',
144-
error: `Node.js ${nodeVersion} detected without WebSocket support.`,
145-
workaround: 'Install the "ws" package: npm install ws',
99+
error: `Node.js ${nodeVersion} detected but native WebSocket not found.`,
100+
workaround:
101+
'Provide a WebSocket implementation via the transport option.',
146102
}
147103
}
104+
105+
// Node.js < 22 doesn't have native WebSocket
106+
return {
107+
type: 'unsupported',
108+
error: `Node.js ${nodeVersion} detected without native WebSocket support.`,
109+
workaround:
110+
'For Node.js < 22, install "ws" package and provide it via the transport option:\n' +
111+
'import ws from "ws"\n' +
112+
'new RealtimeClient(url, { transport: ws })',
113+
}
148114
}
149115

150116
return {

test/RealtimeChannel.lifecycle.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ describe('Channel Lifecycle Management', () => {
532532
test('_rejoin does nothing when channel state is leaving', () => {
533533
// Set up channel to be in 'leaving' state
534534
channel.state = CHANNEL_STATES.leaving
535-
535+
536536
// Spy on socket methods to verify no actions are taken
537537
const leaveOpenTopicSpy = vi.spyOn(testSetup.socket, '_leaveOpenTopic')
538538
const resendSpy = vi.spyOn(channel.joinPush, 'resend')

test/RealtimeChannel.postgres.test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,10 @@ describe('PostgreSQL payload transformation', () => {
599599
table: 'users',
600600
commit_timestamp: '2023-01-01T00:00:00Z',
601601
errors: [],
602-
columns: [{ name: 'id', type: 'int4' }, { name: 'name', type: 'text' }],
602+
columns: [
603+
{ name: 'id', type: 'int4' },
604+
{ name: 'name', type: 'text' },
605+
],
603606
record: { id: 1, name: 'updated' },
604607
old_record: { id: 1, name: 'original' },
605608
},
@@ -627,7 +630,10 @@ describe('PostgreSQL payload transformation', () => {
627630
table: 'users',
628631
commit_timestamp: '2023-01-01T00:00:00Z',
629632
errors: [],
630-
columns: [{ name: 'id', type: 'int4' }, { name: 'name', type: 'text' }],
633+
columns: [
634+
{ name: 'id', type: 'int4' },
635+
{ name: 'name', type: 'text' },
636+
],
631637
old_record: { id: 2, name: 'deleted' },
632638
},
633639
},

0 commit comments

Comments
 (0)