Skip to content

Commit 188ddc0

Browse files
committed
feat: add support for websocket mock
1 parent e83523a commit 188ddc0

15 files changed

+683
-257
lines changed
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { MockRequest } from 'rspack-plugin-mock/helper'
2+
import { defineMock } from 'rspack-plugin-mock/helper'
3+
4+
export default defineMock({
5+
url: '/socket.io',
6+
ws: true,
7+
setup(wss) {
8+
wss.on('connection', (ws, _req: MockRequest) => {
9+
// req.query
10+
// req.params
11+
// req.getCookie
12+
ws.on('message', (raw) => {
13+
const message = JSON.parse(String(raw))
14+
15+
// eslint-disable-next-line no-console
16+
console.log(message)
17+
})
18+
ws.send(JSON.stringify({ type: 'connected123' }))
19+
})
20+
},
21+
})

examples/rsbuild-starter/rsbuild.config.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export default defineConfig({
1111
},
1212
},
1313
plugins: [
14-
pluginMockServer(),
14+
pluginMockServer({
15+
wsPrefix: '/socket.io',
16+
}),
1517
],
1618
})

examples/rsbuild-starter/src/index.ts

+16-16
Original file line numberDiff line numberDiff line change
@@ -113,21 +113,21 @@ async function bootstrap() {
113113
await delay()
114114
}
115115

116-
// function webSocketMock() {
117-
// const ws = new WebSocket('ws://localhost:5173/socket.io')
118-
// ws.addEventListener(
119-
// 'open',
120-
// () => {
121-
// // eslint-disable-next-line no-console
122-
// console.log('isOpen')
123-
// },
124-
// { once: true },
125-
// )
126-
// setTimeout(() => {
127-
// if (ws.readyState === ws.OPEN)
128-
// ws.send(JSON.stringify({ type: 'message', payload: { a: 1 } }))
129-
// }, 3000)
130-
// }
116+
function webSocketMock() {
117+
const ws = new WebSocket('ws://localhost:3000/socket.io')
118+
ws.addEventListener(
119+
'open',
120+
() => {
121+
// eslint-disable-next-line no-console
122+
console.log('isOpen')
123+
},
124+
{ once: true },
125+
)
126+
setTimeout(() => {
127+
if (ws.readyState === ws.OPEN)
128+
ws.send(JSON.stringify({ type: 'message', payload: { a: 1 } }))
129+
}, 3000)
130+
}
131131

132132
bootstrap()
133-
// webSocketMock()
133+
webSocketMock()
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { MockRequest } from 'rspack-plugin-mock/helper'
2+
import { defineMock } from 'rspack-plugin-mock/helper'
3+
4+
export default defineMock({
5+
url: '/socket.io',
6+
ws: true,
7+
setup(wss) {
8+
wss.on('connection', (ws, _req: MockRequest) => {
9+
// req.query
10+
// req.params
11+
// req.getCookie
12+
ws.on('message', (raw) => {
13+
const message = JSON.parse(String(raw))
14+
15+
// eslint-disable-next-line no-console
16+
console.log(message)
17+
})
18+
ws.send(JSON.stringify({ type: 'connected123' }))
19+
})
20+
},
21+
})

examples/rspack-starter/rspack.config.mjs

+8-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ export default {
2222
context: '/api',
2323
target: 'http://localhost:3000',
2424
},
25+
{
26+
context: '/socket.io',
27+
target: 'ws://localhost:3000',
28+
ws: true,
29+
},
2530
],
2631
},
2732
module: {
@@ -61,7 +66,9 @@ export default {
6166
],
6267
},
6368
plugins: [
64-
new MockServerPlugin(),
69+
new MockServerPlugin({
70+
wsPrefix: '/socket.io',
71+
}),
6572
new rspack.HtmlRspackPlugin({ template: './index.html' }),
6673
],
6774
experiments: {

examples/rspack-starter/src/index.ts

+16-16
Original file line numberDiff line numberDiff line change
@@ -113,21 +113,21 @@ async function bootstrap() {
113113
await delay()
114114
}
115115

116-
// function webSocketMock() {
117-
// const ws = new WebSocket('ws://localhost:5173/socket.io')
118-
// ws.addEventListener(
119-
// 'open',
120-
// () => {
121-
// // eslint-disable-next-line no-console
122-
// console.log('isOpen')
123-
// },
124-
// { once: true },
125-
// )
126-
// setTimeout(() => {
127-
// if (ws.readyState === ws.OPEN)
128-
// ws.send(JSON.stringify({ type: 'message', payload: { a: 1 } }))
129-
// }, 3000)
130-
// }
116+
function webSocketMock() {
117+
const ws = new WebSocket('ws://localhost:8080/socket.io')
118+
ws.addEventListener(
119+
'open',
120+
() => {
121+
// eslint-disable-next-line no-console
122+
console.log('isOpen')
123+
},
124+
{ once: true },
125+
)
126+
setTimeout(() => {
127+
if (ws.readyState === ws.OPEN)
128+
ws.send(JSON.stringify({ type: 'message', payload: { a: 1 } }))
129+
}, 3000)
130+
}
131131

132132
bootstrap()
133-
// webSocketMock()
133+
webSocketMock()

src/core/mockCompiler.ts

+41-20
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import chokidar from 'chokidar'
99
import { createFilter } from '@rollup/pluginutils'
1010
import * as rspackCore from '@rspack/core'
1111
import { Volume, createFsFromVolume } from 'memfs'
12+
import { toArray } from '@pengzhanbo/utils'
13+
import color from 'picocolors'
1214
import type { MockOptions } from '../types'
1315
import { lookupFile, normalizePath } from './utils'
1416
import { loadFromCode } from './loadFromCode'
@@ -19,8 +21,8 @@ export interface MockCompilerOptions {
1921
alias?: Record<string, false | string | (string | false)[]>
2022
plugins: RspackPluginInstance[]
2123
cwd?: string
22-
include: string[]
23-
exclude: string[]
24+
include: string | string[]
25+
exclude: string | string[]
2426
}
2527

2628
const vfs = createFsFromVolume(new Volume())
@@ -38,16 +40,19 @@ export class MockCompiler extends EventEmitter {
3840
private _mockData: Record<string, MockOptions> = {}
3941
private fileFilter!: (file: string) => boolean
4042

43+
private watchInfo?: {
44+
filepath: string
45+
type: 'add' | 'change' | 'unlink'
46+
}
47+
4148
compiler?: Compiler | null
4249

4350
constructor(public options: MockCompilerOptions) {
4451
super()
4552
this.cwd = options.cwd || process.cwd()
4653
const { include, exclude } = this.options
4754

48-
this.fileFilter = createFilter(include, exclude, {
49-
resolve: false,
50-
})
55+
this.fileFilter = createFilter(include, exclude, { resolve: false })
5156

5257
try {
5358
const pkg = lookupFile(this.cwd, ['package.json'])
@@ -69,22 +74,27 @@ export class MockCompiler extends EventEmitter {
6974

7075
this.createCompiler(async (err, stats) => {
7176
const name = '[rspack:mock]'
72-
if (err) {
73-
const error = stats?.compilation.getLogger(name).error
74-
|| ((...args: string[]) => console.error(name, ...args))
77+
const logError = stats?.compilation.getLogger(name).error
78+
|| ((...args: string[]) => console.error(color.red(name), ...args))
7579

76-
error(err.stack || err)
80+
if (err) {
81+
logError(err.stack || err)
7782
if ('details' in err) {
78-
error(err.details)
83+
logError(err.details)
7984
}
8085
return
8186
}
8287

8388
if (stats?.hasErrors()) {
84-
stats.compilation.getLogger(name).error(stats.toString({ colors: true }))
89+
const info = stats.toJson()
90+
logError(info.errors)
8591
return
8692
}
8793

94+
// if (stats) {
95+
// console.log('json name', stats.toJson().modules?.map(m => m.name).filter(name => name.startsWith('external')))
96+
// }
97+
8898
const content = vfs.readFileSync(`/${this.outputFile}`, 'utf-8') as string
8999
try {
90100
const result = await loadFromCode({
@@ -94,10 +104,10 @@ export class MockCompiler extends EventEmitter {
94104
cwd: this.cwd,
95105
})
96106
this._mockData = transformMockData(transformRawData(result))
97-
this.emit('update')
107+
this.emit('update', this.watchInfo || {})
98108
}
99109
catch (e) {
100-
console.error('[rspack:mock-server]', e)
110+
logError(e)
101111
}
102112
})
103113
}
@@ -128,7 +138,7 @@ export class MockCompiler extends EventEmitter {
128138

129139
watchMockFiles() {
130140
const { include } = this.options
131-
const [firstGlob, ...otherGlob] = include
141+
const [firstGlob, ...otherGlob] = toArray(include)
132142
const watcher = (this.mockWatcher = chokidar.watch(firstGlob, {
133143
ignoreInitial: true,
134144
cwd: this.cwd,
@@ -137,11 +147,21 @@ export class MockCompiler extends EventEmitter {
137147
if (otherGlob.length > 0)
138148
otherGlob.forEach(glob => watcher.add(glob))
139149

140-
watcher.on('add', () => {
141-
this.updateMockEntry()
150+
watcher.on('add', (filepath) => {
151+
if (this.fileFilter(filepath)) {
152+
this.watchInfo = { filepath, type: 'add' }
153+
this.updateMockEntry()
154+
}
155+
})
156+
157+
watcher.on('change', (filepath) => {
158+
if (this.fileFilter(filepath)) {
159+
this.watchInfo = { filepath, type: 'change' }
160+
}
142161
})
143162

144-
watcher.on('unlink', async () => {
163+
watcher.on('unlink', async (filepath) => {
164+
this.watchInfo = { filepath, type: 'unlink' }
145165
this.updateMockEntry()
146166
})
147167
}
@@ -163,14 +183,15 @@ export class MockCompiler extends EventEmitter {
163183
await fsp.writeFile(this.entryFile, code, 'utf8')
164184
}
165185

166-
createCompiler(callback: (e: Error | null, res?: rspackCore.Stats) => void) {
186+
createCompiler(callback: (e: Error | null, stats?: rspackCore.Stats) => void) {
187+
const { plugins, alias } = this.options
167188
const options = resolveRspackOptions({
168189
isEsm: this.moduleType === 'esm',
169190
cwd: this.cwd,
170-
plugins: this.options.plugins,
191+
plugins,
171192
entryFile: this.entryFile,
172193
outputFile: this.outputFile,
173-
alias: this.options.alias,
194+
alias,
174195
watch: true,
175196
})
176197

0 commit comments

Comments
 (0)