diff --git a/README.md b/README.md index 2ab8263..3c91ed1 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,10 @@ React Hook designed to provide robust WebSocket integrations to your React Compo Pull requests welcomed! +## ESM-only package + +Starting with this version, `react-use-websocket` is published as ESM-only. Use `import` syntax when consuming the package. CommonJS `require('react-use-websocket')` is not supported. + ## New in 4.0.0 - `react-use-websocket` now supports (and depends on) React 18. If you are not ready to upgrade to React 18, please install version `3.0.0`: diff --git a/package.json b/package.json index babf933..6d269ce 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,14 @@ "name": "react-use-websocket", "version": "4.13.0", "description": "React Hook for WebSocket communication", + "type": "module", "main": "./dist/index.js", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, "files": [ "src", "dist" @@ -10,7 +17,7 @@ "scripts": { "test": "cross-env node_modules/.bin/jest --verbose --no-cache", "clear-dist": "cross-env rm -rf dist/", - "compile": "cross-env node_modules/.bin/tsc -p .", + "compile": "cross-env node_modules/.bin/tsc -p tsconfig.build.json", "push": "cross-env npm run test && npm run clear-dist && npm run compile && npm publish" }, "repository": { @@ -42,15 +49,28 @@ "typescript": "^5.6.2" }, "jest": { - "preset": "ts-jest", + "preset": "ts-jest/presets/default-esm", "clearMocks": true, "coverageDirectory": "coverage", "testEnvironment": "jsdom", + "extensionsToTreatAsEsm": [ + ".ts", + ".tsx" + ], "testMatch": [ "/src/lib/**/*.test.(ts|tsx)" ], "transform": { - "^.+\\.(ts|tsx)?$": "ts-jest" + "^.+\\.(ts|tsx)?$": [ + "ts-jest", + { + "useESM": true, + "tsconfig": "tsconfig.test.json" + } + ] + }, + "moduleNameMapper": { + "^(\\.{1,2}/.*)\\.js$": "$1" }, "setupFiles": [ "./__test__/configJSDom.ts" diff --git a/src/index.ts b/src/index.ts index 5c09663..d62b117 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,11 @@ -export { useWebSocket as default } from './lib/use-websocket'; +export { useWebSocket as default } from './lib/use-websocket.js'; -export { SendMessage, Options } from './lib/types'; +export { SendMessage, Options } from './lib/types.js'; -export { useSocketIO } from './lib/use-socket-io'; +export { useSocketIO } from './lib/use-socket-io.js'; -export { ReadyState } from './lib/constants'; +export { ReadyState } from './lib/constants.js'; -export { useEventSource } from './lib/use-event-source'; +export { useEventSource } from './lib/use-event-source.js'; -export { resetGlobalState } from './lib/util'; +export { resetGlobalState } from './lib/util.js'; diff --git a/src/lib/attach-listener.test.ts b/src/lib/attach-listener.test.ts index aeab13e..1718661 100644 --- a/src/lib/attach-listener.test.ts +++ b/src/lib/attach-listener.test.ts @@ -1,8 +1,8 @@ import { MutableRefObject } from 'react'; -import { attachListeners } from './attach-listener'; +import { attachListeners } from './attach-listener.js'; import WS from "jest-websocket-mock"; -import { Options } from './types'; -import { ReadyState } from './constants'; +import { Options } from './types.js'; +import { ReadyState } from './constants.js'; let server: WS; diff --git a/src/lib/attach-listener.ts b/src/lib/attach-listener.ts index d660d3b..d297e60 100644 --- a/src/lib/attach-listener.ts +++ b/src/lib/attach-listener.ts @@ -1,14 +1,14 @@ import { MutableRefObject } from 'react'; -import { setUpSocketIOPing } from './socket-io'; -import { heartbeat } from './heartbeat'; +import { setUpSocketIOPing } from './socket-io.js'; +import { heartbeat } from './heartbeat.js'; import { DEFAULT_RECONNECT_LIMIT, DEFAULT_RECONNECT_INTERVAL_MS, ReadyState, isEventSourceSupported, -} from './constants'; -import { Options, SendMessage, WebSocketLike } from './types'; -import { assertIsWebSocket } from './util'; +} from './constants.js'; +import { Options, SendMessage, WebSocketLike } from './types.js'; +import { assertIsWebSocket } from './util.js'; export interface Setters { setLastMessage: (message: WebSocketEventMap['message']) => void; diff --git a/src/lib/attach-shared-listeners.test.ts b/src/lib/attach-shared-listeners.test.ts index 0a3b36e..038be33 100644 --- a/src/lib/attach-shared-listeners.test.ts +++ b/src/lib/attach-shared-listeners.test.ts @@ -1,10 +1,10 @@ import { MutableRefObject } from 'react'; -import { attachSharedListeners } from './attach-shared-listeners'; +import { attachSharedListeners } from './attach-shared-listeners.js'; import WS from "jest-websocket-mock"; -import { Options, Subscriber } from './types'; -import { ReadyState } from './constants'; -import { sharedWebSockets } from './globals'; -import { addSubscriber, removeSubscriber, getSubscribers, hasSubscribers } from './manage-subscribers'; +import { Options, Subscriber } from './types.js'; +import { ReadyState } from './constants.js'; +import { sharedWebSockets } from './globals.js'; +import { addSubscriber, removeSubscriber, getSubscribers, hasSubscribers } from './manage-subscribers.js'; let server: WS; const URL = 'ws://localhost:1234'; diff --git a/src/lib/attach-shared-listeners.ts b/src/lib/attach-shared-listeners.ts index 48d2420..761210c 100644 --- a/src/lib/attach-shared-listeners.ts +++ b/src/lib/attach-shared-listeners.ts @@ -1,10 +1,10 @@ -import { sharedWebSockets } from './globals'; -import { DEFAULT_RECONNECT_LIMIT, DEFAULT_RECONNECT_INTERVAL_MS, ReadyState, isEventSourceSupported } from './constants'; -import { getSubscribers } from './manage-subscribers'; +import { sharedWebSockets } from './globals.js'; +import { DEFAULT_RECONNECT_LIMIT, DEFAULT_RECONNECT_INTERVAL_MS, ReadyState, isEventSourceSupported } from './constants.js'; +import { getSubscribers } from './manage-subscribers.js'; import { MutableRefObject } from 'react'; -import { HeartbeatOptions, Options, SendMessage, WebSocketLike } from './types'; -import { setUpSocketIOPing } from './socket-io'; -import { heartbeat } from './heartbeat'; +import { HeartbeatOptions, Options, SendMessage, WebSocketLike } from './types.js'; +import { setUpSocketIOPing } from './socket-io.js'; +import { heartbeat } from './heartbeat.js'; const bindMessageHandler = ( webSocketInstance: WebSocketLike, diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 301caba..15f10b3 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -1,4 +1,4 @@ -import { EventSourceEventHandlers, EventSourceOptions } from "./types"; +import { EventSourceEventHandlers, EventSourceOptions } from "./types.js"; const MILLISECONDS = 1; const SECONDS = 1000 * MILLISECONDS; diff --git a/src/lib/create-or-join.test.ts b/src/lib/create-or-join.test.ts index c31b8de..5024793 100644 --- a/src/lib/create-or-join.test.ts +++ b/src/lib/create-or-join.test.ts @@ -1,8 +1,8 @@ import { MutableRefObject } from 'react'; -import { createOrJoinSocket } from './create-or-join'; +import { createOrJoinSocket } from './create-or-join.js'; import WS from "jest-websocket-mock"; -import { Options } from './types'; -import { removeSubscriber, getSubscribers, hasSubscribers } from './manage-subscribers'; +import { Options } from './types.js'; +import { removeSubscriber, getSubscribers, hasSubscribers } from './manage-subscribers.js'; let server: WS; const URL = 'ws://localhost:1234'; diff --git a/src/lib/create-or-join.ts b/src/lib/create-or-join.ts index f22a57b..9fdf4fd 100644 --- a/src/lib/create-or-join.ts +++ b/src/lib/create-or-join.ts @@ -1,10 +1,10 @@ import { MutableRefObject } from 'react'; -import { sharedWebSockets } from './globals'; -import { Options, SendMessage, Subscriber, WebSocketLike } from './types'; -import { isEventSourceSupported, ReadyState, isReactNative } from './constants'; -import { attachListeners } from './attach-listener'; -import { attachSharedListeners } from './attach-shared-listeners'; -import { addSubscriber, removeSubscriber, hasSubscribers } from './manage-subscribers'; +import { sharedWebSockets } from './globals.js'; +import { Options, SendMessage, Subscriber, WebSocketLike } from './types.js'; +import { isEventSourceSupported, ReadyState, isReactNative } from './constants.js'; +import { attachListeners } from './attach-listener.js'; +import { attachSharedListeners } from './attach-shared-listeners.js'; +import { addSubscriber, removeSubscriber, hasSubscribers } from './manage-subscribers.js'; //TODO ensure that all onClose callbacks are called diff --git a/src/lib/get-url.test.ts b/src/lib/get-url.test.ts index 43c8ced..c0ff27b 100644 --- a/src/lib/get-url.test.ts +++ b/src/lib/get-url.test.ts @@ -1,6 +1,6 @@ import { MutableRefObject } from 'react'; -import { getUrl } from './get-url'; -import { Options } from './types'; +import { getUrl } from './get-url.js'; +import { Options } from './types.js'; let optionRef: MutableRefObject; diff --git a/src/lib/get-url.ts b/src/lib/get-url.ts index fe1e911..404fd5d 100644 --- a/src/lib/get-url.ts +++ b/src/lib/get-url.ts @@ -1,7 +1,7 @@ import { MutableRefObject } from 'react'; -import { parseSocketIOUrl, appendQueryParams } from './socket-io'; -import { Options } from './types'; -import { DEFAULT_RECONNECT_INTERVAL_MS, DEFAULT_RECONNECT_LIMIT } from './constants'; +import { parseSocketIOUrl, appendQueryParams } from './socket-io.js'; +import { Options } from './types.js'; +import { DEFAULT_RECONNECT_INTERVAL_MS, DEFAULT_RECONNECT_LIMIT } from './constants.js'; const waitFor = (duration: number) => new Promise(resolve => window.setTimeout(resolve, duration)); diff --git a/src/lib/globals.test.ts b/src/lib/globals.test.ts index 934aebc..28cb48d 100644 --- a/src/lib/globals.test.ts +++ b/src/lib/globals.test.ts @@ -1,5 +1,5 @@ -import { sharedWebSockets, resetWebSockets } from './globals'; -import { WebSocketLike } from './types'; +import { sharedWebSockets, resetWebSockets } from './globals.js'; +import { WebSocketLike } from './types.js'; const FIRST_URL = 'ws://localhost:1234'; const SECOND_URL = 'ws://localhost:4321'; diff --git a/src/lib/globals.ts b/src/lib/globals.ts index 6ed6c11..2c5e1ca 100644 --- a/src/lib/globals.ts +++ b/src/lib/globals.ts @@ -1,4 +1,4 @@ -import { WebSocketLike } from "./types"; +import { WebSocketLike } from "./types.js"; export interface SharedWebSockets { [url: string]: WebSocketLike; diff --git a/src/lib/heartbeat.test.ts b/src/lib/heartbeat.test.ts index 4afc644..dcac140 100644 --- a/src/lib/heartbeat.test.ts +++ b/src/lib/heartbeat.test.ts @@ -1,4 +1,4 @@ -import { heartbeat } from "./heartbeat"; +import { heartbeat } from "./heartbeat.js"; describe("heartbeat", () => { let ws: WebSocket; diff --git a/src/lib/heartbeat.ts b/src/lib/heartbeat.ts index 0b6bc48..b7dc4c3 100644 --- a/src/lib/heartbeat.ts +++ b/src/lib/heartbeat.ts @@ -1,6 +1,6 @@ import { MutableRefObject } from "react"; -import { DEFAULT_HEARTBEAT } from "./constants"; -import { HeartbeatOptions } from "./types"; +import { DEFAULT_HEARTBEAT } from "./constants.js"; +import { HeartbeatOptions } from "./types.js"; function getLastMessageTime(lastMessageTime: MutableRefObject | MutableRefObject[]): number { if (Array.isArray(lastMessageTime)) { diff --git a/src/lib/manage-subscribers.test.ts b/src/lib/manage-subscribers.test.ts index ea9ee33..5f0e42c 100644 --- a/src/lib/manage-subscribers.test.ts +++ b/src/lib/manage-subscribers.test.ts @@ -4,8 +4,8 @@ import { addSubscriber, removeSubscriber, resetSubscribers, -} from './manage-subscribers'; -import { Subscriber } from './types'; +} from './manage-subscribers.js'; +import { Subscriber } from './types.js'; const URL = 'ws://localhost:1234'; const SECOND_URL = 'ws://localhost:4321' diff --git a/src/lib/manage-subscribers.ts b/src/lib/manage-subscribers.ts index 473b2f3..acc153b 100644 --- a/src/lib/manage-subscribers.ts +++ b/src/lib/manage-subscribers.ts @@ -1,4 +1,4 @@ -import { Subscriber } from './types'; +import { Subscriber } from './types.js'; export type Subscribers = { [url: string]: Set, diff --git a/src/lib/proxy.test.ts b/src/lib/proxy.test.ts index beb0432..1e3e839 100644 --- a/src/lib/proxy.test.ts +++ b/src/lib/proxy.test.ts @@ -1,4 +1,4 @@ -import { websocketWrapper } from './proxy'; +import { websocketWrapper } from './proxy.js'; import WS from "jest-websocket-mock"; let server: WS; diff --git a/src/lib/socket-io.test.ts b/src/lib/socket-io.test.ts index 0388d48..cfc35aa 100644 --- a/src/lib/socket-io.test.ts +++ b/src/lib/socket-io.test.ts @@ -2,8 +2,8 @@ import { parseSocketIOUrl, appendQueryParams, setUpSocketIOPing, -} from './socket-io'; -import { SOCKET_IO_PING_CODE } from './constants'; +} from './socket-io.js'; +import { SOCKET_IO_PING_CODE } from './constants.js'; import WS from "jest-websocket-mock"; let server: WS; diff --git a/src/lib/socket-io.ts b/src/lib/socket-io.ts index fcffc6f..cfd6a71 100644 --- a/src/lib/socket-io.ts +++ b/src/lib/socket-io.ts @@ -1,5 +1,5 @@ -import { SOCKET_IO_PING_INTERVAL, SOCKET_IO_PATH, SOCKET_IO_PING_CODE } from './constants'; -import { QueryParams, SendMessage } from './types'; +import { SOCKET_IO_PING_INTERVAL, SOCKET_IO_PATH, SOCKET_IO_PING_CODE } from './constants.js'; +import { QueryParams, SendMessage } from './types.js'; export const parseSocketIOUrl = (url: string) => { if (url) { diff --git a/src/lib/types.ts b/src/lib/types.ts index 2d9423f..b3fd15e 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -1,5 +1,5 @@ import { MutableRefObject } from 'react'; -import { ReadyState } from './constants'; +import { ReadyState } from './constants.js'; export interface QueryParams { [key: string]: string | number; diff --git a/src/lib/use-event-source.test.ts b/src/lib/use-event-source.test.ts index d1cf4f7..f136aac 100644 --- a/src/lib/use-event-source.test.ts +++ b/src/lib/use-event-source.test.ts @@ -1,8 +1,8 @@ import { renderHook, waitFor } from '@testing-library/react'; -import { useEventSource } from './use-event-source'; +import { useEventSource } from './use-event-source.js'; import WS from "jest-websocket-mock"; -import { EventSourceOptions } from './types'; -import { DEFAULT_EVENT_SOURCE_OPTIONS } from './constants'; +import { EventSourceOptions } from './types.js'; +import { DEFAULT_EVENT_SOURCE_OPTIONS } from './constants.js'; let server: WS; const URL = 'ws://localhost:1234'; diff --git a/src/lib/use-event-source.ts b/src/lib/use-event-source.ts index aa54179..b0a3857 100644 --- a/src/lib/use-event-source.ts +++ b/src/lib/use-event-source.ts @@ -1,7 +1,7 @@ import { useEffect, useRef } from 'react' -import { useWebSocket } from './use-websocket' -import { DEFAULT_EVENT_SOURCE_OPTIONS, EMPTY_EVENT_HANDLERS } from './constants' -import { EventSourceOptions, Options, EventSourceHook, EventSourceEventHandlers } from './types'; +import { useWebSocket } from './use-websocket.js' +import { DEFAULT_EVENT_SOURCE_OPTIONS, EMPTY_EVENT_HANDLERS } from './constants.js' +import { EventSourceOptions, Options, EventSourceHook, EventSourceEventHandlers } from './types.js'; export const useEventSource = ( url: string | (() => string | Promise) | null, diff --git a/src/lib/use-socket-io.ts b/src/lib/use-socket-io.ts index 20227dc..346208f 100644 --- a/src/lib/use-socket-io.ts +++ b/src/lib/use-socket-io.ts @@ -1,7 +1,7 @@ import { useMemo } from 'react' -import { useWebSocket } from './use-websocket' -import { DEFAULT_OPTIONS } from './constants' -import { Options, WebSocketHook } from './types'; +import { useWebSocket } from './use-websocket.js' +import { DEFAULT_OPTIONS } from './constants.js' +import { Options, WebSocketHook } from './types.js'; export interface SocketIOMessageData { type: string, diff --git a/src/lib/use-websocket.test.ts b/src/lib/use-websocket.test.ts index 90ea64b..6e67f7f 100644 --- a/src/lib/use-websocket.test.ts +++ b/src/lib/use-websocket.test.ts @@ -1,9 +1,9 @@ import { renderHook, waitFor } from '@testing-library/react'; -import { useWebSocket } from './use-websocket'; +import { useWebSocket } from './use-websocket.js'; import WS from "jest-websocket-mock"; -import { Options } from './types'; -import { ReadyState } from './constants'; -import { parseSocketIOUrl } from './socket-io'; +import { Options } from './types.js'; +import { ReadyState } from './constants.js'; +import { parseSocketIOUrl } from './socket-io.js'; let server: WS; const URL = 'ws://localhost:1234'; diff --git a/src/lib/use-websocket.ts b/src/lib/use-websocket.ts index 82f60e5..bc07b2d 100644 --- a/src/lib/use-websocket.ts +++ b/src/lib/use-websocket.ts @@ -1,9 +1,9 @@ import { useEffect, useRef, useState, useCallback, useMemo } from 'react'; import { flushSync } from 'react-dom'; -import { DEFAULT_OPTIONS, isEventSourceSupported, ReadyState, UNPARSABLE_JSON_OBJECT } from './constants'; -import { createOrJoinSocket } from './create-or-join'; -import { getUrl } from './get-url'; -import websocketWrapper from './proxy'; +import { DEFAULT_OPTIONS, isEventSourceSupported, ReadyState, UNPARSABLE_JSON_OBJECT } from './constants.js'; +import { createOrJoinSocket } from './create-or-join.js'; +import { getUrl } from './get-url.js'; +import websocketWrapper from './proxy.js'; import { Options, ReadyStateState, @@ -12,8 +12,8 @@ import { WebSocketMessage, WebSocketHook, WebSocketLike, -} from './types'; -import { assertIsWebSocket } from './util'; +} from './types.js'; +import { assertIsWebSocket } from './util.js'; export const useWebSocket = ( url: string | (() => string | Promise) | null, diff --git a/src/lib/util.ts b/src/lib/util.ts index 1ab29a1..1ed8336 100644 --- a/src/lib/util.ts +++ b/src/lib/util.ts @@ -1,6 +1,6 @@ -import { WebSocketLike } from './types'; -import { resetWebSockets } from './globals'; -import { resetSubscribers } from './manage-subscribers'; +import { WebSocketLike } from './types.js'; +import { resetWebSockets } from './globals.js'; +import { resetSubscribers } from './manage-subscribers.js'; export function assertIsWebSocket ( webSocketInstance: WebSocketLike, diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..b681bd2 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "sourceMap": true, + "esModuleInterop": true, + "target": "es5", + "jsx": "react", + "skipLibCheck": true, + "lib": [ + "es2017", + "dom" + ], + "strict": true, + "declaration": true, + "baseUrl": ".", + "paths": { + "@lib/*": [ + "src/lib/*" + ] + } + }, + "files": [ + "./src/index.ts" + ], + "include": [ + "src/*" + ] +} diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000..f77319c --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "outDir": "./dist/", + "module": "ESNext", + "moduleResolution": "Bundler", + "types": [ + "node", + "websocket" + ] + } +} diff --git a/tsconfig.json b/tsconfig.json index 90c8ebf..452f262 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,27 +1,3 @@ { - "compilerOptions": { - "outDir": "./dist/", - "sourceMap": true, - "module": "commonjs", - "esModuleInterop": true, - "target": "es5", - "jsx": "react", - "skipLibCheck": true, - "lib": ["es2017", "dom"], - "types" : ["node", "websocket", "jest"], - "strict": true, - "declaration": true, - "baseUrl": ".", - "paths": { - "@lib/*": [ - "src/lib/*" - ] - } - }, - "files": [ - "./src/index.ts" - ], - "include": [ - "src/*" - ] + "extends": "./tsconfig.test.json" } diff --git a/tsconfig.test.json b/tsconfig.test.json new file mode 100644 index 0000000..5a3fb0e --- /dev/null +++ b/tsconfig.test.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "NodeNext", + "types": [ + "node", + "websocket", + "jest" + ], + "noEmit": true + } +}