next-ws 0.2.5-next.6ae959b → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -86
- package/client/context.js +2 -3
- package/package.json +58 -56
- package/server/index.d.ts +1 -1
- package/server/setup.d.ts +1 -1
- package/server/setup.js +7 -7
- package/server/utilities/next.d.ts +10 -10
- package/server/utilities/next.js +37 -13
- package/server/utilities/patch.js +3 -2
- package/server/utilities/workspace.js +10 -4
- package/server/utilities/ws.d.ts +15 -0
- package/server/utilities/ws.js +13 -0
package/README.md
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
<h1><strong>Next WS</strong></h1>
|
|
3
|
+
<i>Add support for WebSockets in Next.js 13 app directory</i><br>
|
|
4
|
+
<code>npm install next-ws</code>
|
|
5
5
|
</div>
|
|
6
6
|
|
|
7
7
|
<div align="center">
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
<img alt="package version" src="https://img.shields.io/npm/v/next-ws?label=version">
|
|
9
|
+
<img alt="total downloads" src="https://img.shields.io/npm/dt/next-ws">
|
|
10
|
+
<br>
|
|
11
|
+
<a href="https://github.com/apteryxxyz/next-ws"><img alt="next-ws repo stars" src="https://img.shields.io/github/stars/apteryxxyz/next-ws?style=social"></a>
|
|
12
|
+
<a href="https://github.com/apteryxxyz"><img alt="apteryxxyz followers" src="https://img.shields.io/github/followers/apteryxxyz?style=social"></a>
|
|
13
|
+
<a href="https://discord.gg/vZQbMhwsKY"><img src="https://discordapp.com/api/guilds/829836158007115806/widget.png?style=shield" alt="discord shield"/></a>
|
|
14
14
|
</div>
|
|
15
15
|
|
|
16
16
|
---
|
|
@@ -33,10 +33,10 @@ This module is inspired by the now outdated `next-plugin-websocket`, if you are
|
|
|
33
33
|
- [🏓 Table of Contents](#-table-of-contents)
|
|
34
34
|
- [📦 Installation](#-installation)
|
|
35
35
|
- [🚀 Usage](#-usage)
|
|
36
|
-
|
|
36
|
+
- [🚓 Verify Patch](#-verify-patch)
|
|
37
37
|
- [🌀 Example](#-example)
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
- [📁 Server](#-server)
|
|
39
|
+
- [📁 Client](#-client)
|
|
40
40
|
|
|
41
41
|
---
|
|
42
42
|
|
|
@@ -45,7 +45,7 @@ This module is inspired by the now outdated `next-plugin-websocket`, if you are
|
|
|
45
45
|
In order to setup a WebSocket server, Next WS needs to patch your local Next.js installation. Next WS provides a CLI command to do this for you, it will automatically detect your Next.js version and patch it accordingly, however a minimum version of Next.js 13.1.1 is required.
|
|
46
46
|
|
|
47
47
|
```sh
|
|
48
|
-
npx next-ws-cli patch
|
|
48
|
+
npx next-ws-cli@latest patch
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
> If at any point your local Next.js installation is changed or updated you will need to re-run the patch command.
|
|
@@ -56,6 +56,16 @@ Once the patch is complete, you will need to install the Next WS package into yo
|
|
|
56
56
|
npm install next-ws
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
### 🚓 Verify Patch (Optional)
|
|
60
|
+
|
|
61
|
+
It is recommended to add the following code to the top level of your `next.config.js`.
|
|
62
|
+
|
|
63
|
+
This will verify that Next WS has been patched correctly, and throw an error if it has not. Preventing you from accidentally deploying a broken setup.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
require('next-ws/server').verifyPatch();
|
|
67
|
+
```
|
|
68
|
+
|
|
59
69
|
---
|
|
60
70
|
|
|
61
71
|
## 🚀 Usage
|
|
@@ -66,26 +76,16 @@ The `SOCKET` function receives three arguments: the WebSocket client, the HTTP r
|
|
|
66
76
|
|
|
67
77
|
```ts
|
|
68
78
|
export function SOCKET(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
79
|
+
client: import('ws').WebSocket,
|
|
80
|
+
request: import('http').IncomingMessage,
|
|
81
|
+
server: import('ws').WebSocketServer,
|
|
72
82
|
) {
|
|
73
|
-
|
|
83
|
+
// ...
|
|
74
84
|
}
|
|
75
85
|
```
|
|
76
86
|
|
|
77
87
|
With this straightforward setup, you can fully leverage the capabilities of Next WS and efficiently handle WebSocket connections within your Next.js application.
|
|
78
88
|
|
|
79
|
-
### 🚓 Verify Patch
|
|
80
|
-
|
|
81
|
-
It is recommended to add the following code to the top level of your `next.config.js`.
|
|
82
|
-
|
|
83
|
-
This will verify that Next WS has been patched correctly, and throw an error if it has not. Preventing you from accidentally deploying a broken setup.
|
|
84
|
-
|
|
85
|
-
```ts
|
|
86
|
-
require('next-ws/server').verifyPatch();
|
|
87
|
-
```
|
|
88
|
-
|
|
89
89
|
---
|
|
90
90
|
|
|
91
91
|
## 🌀 Example
|
|
@@ -97,19 +97,19 @@ Create an API route anywhere within the app directory, and export a `SOCKET` fun
|
|
|
97
97
|
```ts
|
|
98
98
|
// app/api/ws/route.ts (can be any route file in the app directory)
|
|
99
99
|
export function SOCKET(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
client: import('ws').WebSocket,
|
|
101
|
+
request: import('http').IncomingMessage,
|
|
102
|
+
server: import('ws').WebSocketServer,
|
|
103
103
|
) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
104
|
+
console.log('A client connected!');
|
|
105
|
+
|
|
106
|
+
client.on('message', message => {
|
|
107
|
+
client.send(message);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
client.on('close', () => {
|
|
111
|
+
console.log('A client disconnected!');
|
|
112
|
+
});
|
|
113
113
|
}
|
|
114
114
|
```
|
|
115
115
|
|
|
@@ -126,27 +126,29 @@ To make it easier to connect to your new WebSocker server, Next WS also provides
|
|
|
126
126
|
import { WebSocketProvider } from 'next-ws/client';
|
|
127
127
|
|
|
128
128
|
export default function Layout() {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
129
|
+
return <WebSocketProvider
|
|
130
|
+
url="ws://localhost:3000/api/ws"
|
|
131
|
+
// ... other props
|
|
132
|
+
>
|
|
133
|
+
{...}
|
|
134
|
+
</WebSocketProvider>;
|
|
135
135
|
}
|
|
136
136
|
```
|
|
137
137
|
|
|
138
|
+
To make it easier to connect to your new WebSocker server, Next WS also provides some client-side utilities. These are completely optional, you can use your own implementation if you wish.
|
|
139
|
+
|
|
138
140
|
The following is the props interface for the `WebSocketProvider` component, containing all the available options.
|
|
139
141
|
|
|
140
142
|
```ts
|
|
141
143
|
interface WebSocketProviderProps {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
144
|
+
children: React.ReactNode;
|
|
145
|
+
|
|
146
|
+
/** The URL for the WebSocket to connect to. */
|
|
147
|
+
url: string;
|
|
148
|
+
/** The subprotocols to use. */
|
|
149
|
+
protocols?: string[] | string;
|
|
150
|
+
/** The binary type to use. */
|
|
151
|
+
binaryType?: BinaryType;
|
|
150
152
|
}
|
|
151
153
|
```
|
|
152
154
|
|
|
@@ -158,38 +160,38 @@ import { useWebSocket } from 'next-ws/client';
|
|
|
158
160
|
import { useCallback, useEffect, useState } from 'react';
|
|
159
161
|
|
|
160
162
|
export default function Page() {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
163
|
+
const ws = useWebSocket();
|
|
164
|
+
// ^? WebSocket on the client, null on the server
|
|
165
|
+
|
|
166
|
+
const [value, setValue] = useState('');
|
|
167
|
+
const [message, setMessage] = useState<string | null>('');
|
|
168
|
+
|
|
169
|
+
const onMessage = useCallback((event: MessageEvent) => {
|
|
170
|
+
const text = await event.data.text();
|
|
171
|
+
setMessage(text);
|
|
172
|
+
}, []);
|
|
173
|
+
|
|
174
|
+
useEffect(() => {
|
|
175
|
+
ws?.addEventListener('message', onMessage);
|
|
176
|
+
return () => ws?.removeEventListener('message', onMessage);
|
|
177
|
+
}, [ws]);
|
|
178
|
+
|
|
179
|
+
return <>
|
|
180
|
+
<input
|
|
181
|
+
type="text"
|
|
182
|
+
value={value}
|
|
183
|
+
onChange={event => setValue(event.target.value)}
|
|
184
|
+
/>
|
|
185
|
+
|
|
186
|
+
<button onClick={() => ws.send(value)}>
|
|
187
|
+
Send message to server
|
|
188
|
+
</button>
|
|
189
|
+
|
|
190
|
+
<p>
|
|
191
|
+
{message === null
|
|
192
|
+
? 'Waiting for server to send a message...'
|
|
193
|
+
: `Got message: ${message}`}
|
|
194
|
+
</p>
|
|
195
|
+
</>;
|
|
194
196
|
}
|
|
195
197
|
```
|
package/client/context.js
CHANGED
|
@@ -13,15 +13,14 @@ exports.WebSocketContext.displayName = 'WebSocketContext';
|
|
|
13
13
|
*/
|
|
14
14
|
function WebSocketProvider({ children, url, protocols, binaryType, }) {
|
|
15
15
|
const isBrowser = typeof window !== 'undefined';
|
|
16
|
-
const
|
|
16
|
+
const instance = (0, react_1.useMemo)(() => {
|
|
17
17
|
if (!isBrowser)
|
|
18
18
|
return null;
|
|
19
19
|
const client = new WebSocket(url, protocols);
|
|
20
20
|
if (binaryType)
|
|
21
21
|
client.binaryType = binaryType;
|
|
22
22
|
return client;
|
|
23
|
-
};
|
|
24
|
-
const instance = (0, react_1.useMemo)(setupClient, [isBrowser, url, protocols]);
|
|
23
|
+
}, [isBrowser, url, protocols]);
|
|
25
24
|
(0, react_1.useEffect)(() => {
|
|
26
25
|
return () => instance?.close();
|
|
27
26
|
}, []);
|
package/package.json
CHANGED
|
@@ -1,57 +1,59 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
2
|
+
"name": "next-ws",
|
|
3
|
+
"version": "0.2.5",
|
|
4
|
+
"description": "Add support for WebSockets in Next.js 13 app directory",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"next",
|
|
7
|
+
"websocket",
|
|
8
|
+
"ws",
|
|
9
|
+
"server",
|
|
10
|
+
"client"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"homepage": "https://github.com/apteryxxyz/next-ws#readme",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "git+https://github.com/apteryxxyz/next-ws.git",
|
|
17
|
+
"directory": "packages/core"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/apteryxxyz/next-ws/issues"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"index.js",
|
|
24
|
+
"index.d.ts",
|
|
25
|
+
"client",
|
|
26
|
+
"server"
|
|
27
|
+
],
|
|
28
|
+
"main": "./index.js",
|
|
29
|
+
"types": "./index.d.ts",
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "cp ../../README.md . && tsc",
|
|
32
|
+
"clean": "rimraf index.js index.d.ts client server README.md",
|
|
33
|
+
"lint": "eslint --ext .ts,.tsx src",
|
|
34
|
+
"format": "prettier --write src/**/*.{ts,tsx} && eslint --fix --ext .ts,.tsx src/"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"next": ">=13.1.1",
|
|
38
|
+
"react": "*",
|
|
39
|
+
"ws": "*"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/react": "^18",
|
|
43
|
+
"@types/react-dom": "^18",
|
|
44
|
+
"@types/ws": "^8",
|
|
45
|
+
"eslint": "^8.43.0",
|
|
46
|
+
"next": "^13.4.6",
|
|
47
|
+
"prettier": "^2.8.8",
|
|
48
|
+
"react": "^18.2.0",
|
|
49
|
+
"react-dom": "^18.2.0",
|
|
50
|
+
"rimraf": "^5.0.1",
|
|
51
|
+
"typescript": "<5.1.0"
|
|
52
|
+
},
|
|
53
|
+
"eslintConfig": {
|
|
54
|
+
"root": true,
|
|
55
|
+
"extends": [
|
|
56
|
+
"../../.eslintrc.js"
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
}
|
package/server/index.d.ts
CHANGED
package/server/setup.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import NextNodeServer from 'next/dist/server/next-server';
|
|
1
|
+
import type NextNodeServer from 'next/dist/server/next-server';
|
|
2
2
|
export declare function setupWebSocketServer(nextServer: NextNodeServer): void;
|
|
3
3
|
export declare function hookNextNodeServer(this: NextNodeServer): void;
|
package/server/setup.js
CHANGED
|
@@ -3,18 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.hookNextNodeServer = exports.setupWebSocketServer = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const Log = tslib_1.__importStar(require("next/dist/build/output/log"));
|
|
6
|
-
const ws_1 = require("ws");
|
|
7
6
|
const next_1 = require("./utilities/next");
|
|
7
|
+
const ws_1 = require("./utilities/ws");
|
|
8
8
|
function setupWebSocketServer(nextServer) {
|
|
9
9
|
const httpServer = (0, next_1.getHttpServer)(nextServer);
|
|
10
|
-
const wsServer =
|
|
10
|
+
const wsServer = (0, ws_1.getWsServer)();
|
|
11
11
|
Log.ready('[next-ws] websocket server started successfully');
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
12
13
|
httpServer.on('upgrade', async (request, socket, head) => {
|
|
13
|
-
const url = new URL(request.url ?? '', '
|
|
14
|
+
const url = new URL(request.url ?? '', 'ws://next');
|
|
14
15
|
const pathname = url.pathname;
|
|
15
16
|
if (pathname.startsWith('/_next'))
|
|
16
17
|
return;
|
|
17
|
-
const fsPathname =
|
|
18
|
+
const fsPathname = (0, next_1.resolvePathname)(nextServer, pathname);
|
|
18
19
|
if (!fsPathname) {
|
|
19
20
|
Log.error(`[next-ws] could not find module for page ${pathname}`);
|
|
20
21
|
return socket.destroy();
|
|
@@ -24,13 +25,12 @@ function setupWebSocketServer(nextServer) {
|
|
|
24
25
|
Log.error(`[next-ws] could not find module for page ${pathname}`);
|
|
25
26
|
return socket.destroy();
|
|
26
27
|
}
|
|
27
|
-
const socketHandler = pageModule?.routeModule?.
|
|
28
|
+
const socketHandler = pageModule?.routeModule?.userload?.SOCKET;
|
|
28
29
|
if (!socketHandler || typeof socketHandler !== 'function') {
|
|
29
30
|
Log.error(`[next-ws] ${pathname} does not export a SOCKET handler`);
|
|
30
31
|
return socket.destroy();
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
-
return wsServer.handleUpgrade(request, socket, head, (client, request) => socketHandler(client, request, wsServer));
|
|
33
|
+
return wsServer.handleUpgrade(request, socket, head, (client, request) => void socketHandler(client, request, wsServer));
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
36
|
exports.setupWebSocketServer = setupWebSocketServer;
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { Server } from 'node:http';
|
|
3
3
|
import NextNodeServer from 'next/dist/server/next-server';
|
|
4
|
-
|
|
5
|
-
export type SocketHandler = (
|
|
6
|
-
/** The WebSocket client that connected. */
|
|
7
|
-
client: import('ws').WebSocket,
|
|
8
|
-
/** The HTTP request that initiated the WebSocket connection. */
|
|
9
|
-
request: import('http').IncomingMessage,
|
|
10
|
-
/** The WebSocket server. */
|
|
11
|
-
server: import('ws').WebSocketServer) => any;
|
|
4
|
+
import type { SocketHandler } from './ws';
|
|
12
5
|
/**
|
|
13
6
|
* Get the http.Server instance from the NextNodeServer.
|
|
14
7
|
* @param nextServer The NextNodeServer instance.
|
|
@@ -21,11 +14,18 @@ export declare function getHttpServer(nextServer: NextNodeServer): Server<typeof
|
|
|
21
14
|
* @param pathname The pathname to resolve.
|
|
22
15
|
* @returns The resolved page, or null if the page could not be resolved.
|
|
23
16
|
*/
|
|
24
|
-
export declare function resolvePathname(nextServer: NextNodeServer, pathname: string):
|
|
17
|
+
export declare function resolvePathname(nextServer: NextNodeServer, pathname: string): string | null;
|
|
25
18
|
/**
|
|
26
19
|
* Get the page module for a page.
|
|
27
20
|
* @param nextServer The NextNodeServer instance.
|
|
28
21
|
* @param filename The filename of the page.
|
|
29
22
|
* @returns The page module.
|
|
30
23
|
*/
|
|
31
|
-
export declare function getPageModule(nextServer: NextNodeServer, filename: string): Promise<
|
|
24
|
+
export declare function getPageModule(nextServer: NextNodeServer, filename: string): Promise<PageModule>;
|
|
25
|
+
export interface PageModule {
|
|
26
|
+
routeModule?: {
|
|
27
|
+
userload?: {
|
|
28
|
+
SOCKET?: SocketHandler;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
}
|
package/server/utilities/next.js
CHANGED
|
@@ -9,11 +9,10 @@ const next_server_1 = tslib_1.__importDefault(require("next/dist/server/next-ser
|
|
|
9
9
|
* @param nextServer The NextNodeServer instance.
|
|
10
10
|
* @returns The http.Server instance.
|
|
11
11
|
*/
|
|
12
|
-
// prettier-ignore
|
|
13
12
|
function getHttpServer(nextServer) {
|
|
14
13
|
if (!nextServer || !(nextServer instanceof next_server_1.default))
|
|
15
14
|
throw new Error('Next WS is missing access to the NextNodeServer');
|
|
16
|
-
// @ts-expect-error serverOptions is protected
|
|
15
|
+
// @ts-expect-error - serverOptions is protected
|
|
17
16
|
const httpServer = nextServer.serverOptions?.httpServer;
|
|
18
17
|
if (!httpServer || !(httpServer instanceof node_http_1.Server))
|
|
19
18
|
throw new Error('Next WS is missing access to the http.Server');
|
|
@@ -26,21 +25,44 @@ exports.getHttpServer = getHttpServer;
|
|
|
26
25
|
* @param pathname The pathname to resolve.
|
|
27
26
|
* @returns The resolved page, or null if the page could not be resolved.
|
|
28
27
|
*/
|
|
29
|
-
|
|
28
|
+
function resolvePathname(nextServer, pathname) {
|
|
30
29
|
if (pathname.startsWith('/_next'))
|
|
31
30
|
return null;
|
|
31
|
+
const pathParts = pathname.split('/');
|
|
32
32
|
const appRoutes = {
|
|
33
|
-
// @ts-expect-error appPathRoutes is protected
|
|
33
|
+
// @ts-expect-error - appPathRoutes is protected
|
|
34
34
|
...nextServer.appPathRoutes,
|
|
35
|
-
// @ts-expect-error getAppPathRoutes is protected
|
|
35
|
+
// @ts-expect-error - getAppPathRoutes is protected
|
|
36
36
|
...nextServer.getAppPathRoutes(),
|
|
37
37
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
for (const [key, [path]] of Object.entries(appRoutes)) {
|
|
39
|
+
const hasDynamic = key.includes('[') && key.includes(']');
|
|
40
|
+
if (hasDynamic) {
|
|
41
|
+
const keyParts = key.split('/');
|
|
42
|
+
if (keyParts.length !== pathParts.length)
|
|
43
|
+
continue;
|
|
44
|
+
for (let i = 0; i < keyParts.length; i++) {
|
|
45
|
+
const keyPart = keyParts[i];
|
|
46
|
+
const pathPart = pathParts[i];
|
|
47
|
+
const isDynamic = keyPart.includes('[') && keyPart.includes(']');
|
|
48
|
+
if (isDynamic)
|
|
49
|
+
keyParts[i] = pathPart;
|
|
50
|
+
if (keyParts[i] !== pathParts[i])
|
|
51
|
+
break;
|
|
52
|
+
if (i === keyParts.length - 1) {
|
|
53
|
+
if (!path?.endsWith('/route'))
|
|
54
|
+
return null;
|
|
55
|
+
return path;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
if (key !== pathname)
|
|
61
|
+
continue;
|
|
62
|
+
if (!path?.endsWith('/route'))
|
|
63
|
+
return null;
|
|
64
|
+
return path;
|
|
65
|
+
}
|
|
44
66
|
}
|
|
45
67
|
return null;
|
|
46
68
|
}
|
|
@@ -52,13 +74,15 @@ exports.resolvePathname = resolvePathname;
|
|
|
52
74
|
* @returns The page module.
|
|
53
75
|
*/
|
|
54
76
|
async function getPageModule(nextServer, filename) {
|
|
55
|
-
// @ts-expect-error
|
|
77
|
+
// @ts-expect-error - hotReloader is private
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
56
79
|
await nextServer.hotReloader?.ensurePage({
|
|
57
80
|
page: filename,
|
|
58
81
|
clientOnly: false,
|
|
59
82
|
});
|
|
60
|
-
// @ts-expect-error getPagePath is protected
|
|
83
|
+
// @ts-expect-error - getPagePath is protected
|
|
61
84
|
const builtPagePath = nextServer.getPagePath(filename);
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
62
86
|
return require(builtPagePath);
|
|
63
87
|
}
|
|
64
88
|
exports.getPageModule = getPageModule;
|
|
@@ -24,12 +24,13 @@ exports.getPatch = getPatch;
|
|
|
24
24
|
* Verify that the Next WS patch has been applied to Next.js.
|
|
25
25
|
* @returns The patch version and Next.js version if the patch has been applied, otherwise null.
|
|
26
26
|
*/
|
|
27
|
-
// prettier-ignore
|
|
28
27
|
function verifyPatch() {
|
|
29
28
|
const patch = getPatch();
|
|
30
29
|
if (!patch)
|
|
31
30
|
throw new Error('Next.js has not been patched to support Next WS, please run `npx next-ws-cli patch`');
|
|
32
|
-
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
32
|
+
const packageJson = require('next/package.json');
|
|
33
|
+
const version = packageJson.version.split('-')[0];
|
|
33
34
|
if (patch.version !== version)
|
|
34
35
|
throw new Error(`Next.js version mismatch, expected ${patch.version} but found ${version}, try running \`npx next-ws-cli patch\``);
|
|
35
36
|
}
|
|
@@ -4,6 +4,7 @@ exports.findWorkspaceRoot = void 0;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const fs = tslib_1.__importStar(require("node:fs"));
|
|
6
6
|
const path = tslib_1.__importStar(require("node:path"));
|
|
7
|
+
const cache = new Map();
|
|
7
8
|
function findWorkspaceRoot(initalPath = process.cwd()) {
|
|
8
9
|
let currentPath = initalPath;
|
|
9
10
|
let lastPossiblePath = currentPath;
|
|
@@ -11,10 +12,10 @@ function findWorkspaceRoot(initalPath = process.cwd()) {
|
|
|
11
12
|
const isCurrentPathRoot = () => {
|
|
12
13
|
const files = fs.readdirSync(currentPath);
|
|
13
14
|
const lockFiles = ['pnpm-lock.yaml', 'yarn.lock', 'package-lock.json'];
|
|
14
|
-
const hasLockFile = files.some(file => lockFiles.includes(file));
|
|
15
|
+
const hasLockFile = files.some((file) => lockFiles.includes(file));
|
|
15
16
|
if (hasLockFile)
|
|
16
17
|
return 'true';
|
|
17
|
-
const packageJson = files.find(file => file === 'package.json');
|
|
18
|
+
const packageJson = files.find((file) => file === 'package.json');
|
|
18
19
|
if (packageJson) {
|
|
19
20
|
const packageContent = fs.readFileSync(path.resolve(currentPath, packageJson), 'utf8');
|
|
20
21
|
const packageObject = JSON.parse(packageContent);
|
|
@@ -24,7 +25,10 @@ function findWorkspaceRoot(initalPath = process.cwd()) {
|
|
|
24
25
|
}
|
|
25
26
|
return 'false';
|
|
26
27
|
};
|
|
27
|
-
|
|
28
|
+
const shouldContinue = true;
|
|
29
|
+
while (shouldContinue) {
|
|
30
|
+
if (cache.has(currentPath))
|
|
31
|
+
return cache.get(currentPath);
|
|
28
32
|
isRoot = isCurrentPathRoot();
|
|
29
33
|
const nextPath = path.resolve(currentPath, '..');
|
|
30
34
|
if (isRoot === 'true' || nextPath === currentPath)
|
|
@@ -33,6 +37,8 @@ function findWorkspaceRoot(initalPath = process.cwd()) {
|
|
|
33
37
|
lastPossiblePath = currentPath;
|
|
34
38
|
currentPath = nextPath;
|
|
35
39
|
}
|
|
36
|
-
|
|
40
|
+
const finalPath = isRoot === 'true' ? currentPath : lastPossiblePath;
|
|
41
|
+
cache.set(initalPath, finalPath);
|
|
42
|
+
return finalPath;
|
|
37
43
|
}
|
|
38
44
|
exports.findWorkspaceRoot = findWorkspaceRoot;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/// <reference types="ws" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
/** A function that handles a WebSocket connection. */
|
|
4
|
+
export type SocketHandler = (
|
|
5
|
+
/** The WebSocket client that connected. */
|
|
6
|
+
client: import('ws').WebSocket,
|
|
7
|
+
/** The HTTP request that initiated the WebSocket connection. */
|
|
8
|
+
request: import('http').IncomingMessage,
|
|
9
|
+
/** The WebSocket server. */
|
|
10
|
+
server: import('ws').WebSocketServer) => unknown;
|
|
11
|
+
/**
|
|
12
|
+
* Get the WebSocketServer instance.
|
|
13
|
+
* @returns The WebSocketServer instance.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getWsServer(): import("ws").Server<typeof import("ws"), typeof import("http").IncomingMessage>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getWsServer = void 0;
|
|
4
|
+
/* eslint-disable @typescript-eslint/consistent-type-imports */
|
|
5
|
+
const ws_1 = require("ws");
|
|
6
|
+
/**
|
|
7
|
+
* Get the WebSocketServer instance.
|
|
8
|
+
* @returns The WebSocketServer instance.
|
|
9
|
+
*/
|
|
10
|
+
function getWsServer() {
|
|
11
|
+
return new ws_1.WebSocketServer({ noServer: true });
|
|
12
|
+
}
|
|
13
|
+
exports.getWsServer = getWsServer;
|