Skip to content
This repository was archived by the owner on Jan 24, 2025. It is now read-only.

Commit 2d63e83

Browse files
committed
chore(docz-core): split socket from data server
1 parent f500971 commit 2d63e83

File tree

8 files changed

+122
-117
lines changed

8 files changed

+122
-117
lines changed

core/docz-core/src/bundler/server.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import chalk from 'chalk'
22
import logger from 'signale'
33
import WebpackDevServer from 'webpack-dev-server'
4+
45
import { Configuration as Config } from 'webpack'
56
import PrettyError from 'pretty-error'
67

core/docz-core/src/commands/build.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,16 @@ export const build = async (args: Arguments<any>) => {
2525

2626
try {
2727
await Entries.writeApp(config, true)
28-
await dataServer.init()
28+
await dataServer.start()
2929

3030
await run('onPreBuild', config)
3131
await bundler.build(bundlerConfig)
3232

3333
await run('onPostBuild', config)
34-
await dataServer.close()
34+
dataServer.close()
3535
} catch (err) {
3636
pe.render(err)
3737
process.exit(1)
38+
dataServer.close()
3839
}
3940
}

core/docz-core/src/commands/dev.ts

+17-11
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import PrettyError from 'pretty-error'
77

88
import { Entries } from '../lib/Entries'
99
import { DataServer } from '../lib/DataServer'
10+
import { Socket } from '../lib/Socket'
1011
import { parseConfig } from '../config/docz'
1112
import { onSignal } from '../utils/on-signal'
1213
import { bundler as webpack } from '../bundler'
@@ -18,6 +19,7 @@ export const dev = async (args: Arguments<any>) => {
1819
const config = await parseConfig(args)
1920
const bundler = webpack(config, env)
2021
const entries = new Entries(config)
22+
const { websocketHost, websocketPort } = config
2123

2224
const bundlerConfig = await bundler.mountConfig(env)
2325
const app = await bundler.createApp(bundlerConfig)
@@ -31,31 +33,35 @@ export const dev = async (args: Arguments<any>) => {
3133
}
3234

3335
const server = await app.start()
34-
const dataServer = new DataServer(
35-
server,
36-
config.websocketPort,
37-
config.websocketHost
38-
)
36+
const dataServer = new DataServer()
37+
const socket = new Socket(server, websocketHost, websocketPort)
3938

4039
if (args.propsParser) dataServer.register([states.props(config)])
4140
dataServer.register([states.config(config), states.entries(entries, config)])
4241

4342
try {
44-
await dataServer.init()
45-
await dataServer.listen()
43+
await dataServer.start()
4644
} catch (err) {
4745
logger.fatal('Failed to process data server')
48-
pe.render(err)
49-
await dataServer.close()
46+
logger.error(err)
47+
dataServer.close()
5048
process.exit(1)
5149
}
5250

51+
socket.onConnection((_, emit) => {
52+
const subscribe = dataServer.onStateChange(action => {
53+
emit(action.type, action.payload)
54+
})
55+
56+
return () => subscribe()
57+
})
58+
5359
onSignal(async () => {
54-
await dataServer.close()
60+
dataServer.close()
5561
server.close()
5662
})
5763

5864
server.on('close', async () => {
59-
await dataServer.close()
65+
dataServer.close()
6066
})
6167
}

core/docz-core/src/lib/DataServer.ts

+40-86
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,7 @@
11
import * as fs from 'fs-extra'
2-
import WS from 'ws'
32
import { isFunction } from 'lodash/fp'
43

54
import * as paths from '../config/paths'
6-
import { onSignal } from '../utils/on-signal'
7-
8-
export type Send = (type: string, payload: any) => void
9-
export type On = (type: string) => Promise<any>
10-
11-
const isSocketOpened = (socket: WS) => socket.readyState === WS.OPEN
12-
const sender = (socket?: WS) => (type: string, payload: any) => {
13-
if (socket && isSocketOpened(socket)) {
14-
socket.send(JSON.stringify({ type, payload }))
15-
}
16-
}
17-
18-
export interface Action {
19-
type: string
20-
payload: any
21-
}
225

236
export interface Params {
247
state: Record<string, any>
@@ -27,103 +10,74 @@ export interface Params {
2710

2811
export interface State {
2912
id: string
30-
init: (params: Params) => Promise<any>
31-
update: (params: Params) => any
32-
close: (params: Params) => any
13+
start: (params: Params) => Promise<void>
14+
close: () => void
15+
}
16+
17+
export interface Action {
18+
type: string
19+
payload: any
3320
}
3421

22+
export type Listener = (action: Action) => void
23+
3524
export class DataServer {
36-
private client?: WS.Server
3725
private states: Set<State>
38-
private state: Record<string, any>
26+
private state: Map<string, any>
27+
private listeners: Set<Listener>
3928

40-
constructor(server?: any, port?: number, host?: string) {
29+
constructor() {
4130
this.states = new Set()
42-
this.state = {}
43-
44-
if (server) {
45-
this.client = new WS.Server({
46-
server,
47-
port,
48-
host,
49-
})
50-
}
31+
this.state = new Map()
32+
this.listeners = new Set<Listener>()
5133
}
5234

5335
public register(states: State[]): DataServer {
5436
for (const state of states) this.states.add(state)
5537
return this
5638
}
5739

58-
public async init(): Promise<void> {
40+
public async start(): Promise<void> {
41+
const setState = (key: string, val: any) => this.setState(key, val)
42+
5943
await Promise.all(
6044
Array.from(this.states).map(async state => {
61-
if (!isFunction(state.init)) return
62-
return state.init({
63-
state: { ...this.state },
64-
setState: this.setState(),
45+
if (!isFunction(state.start)) return
46+
return state.start({
47+
setState,
48+
state: this.mapToObject(this.state),
6549
})
6650
})
6751
)
68-
69-
this.updateStateFile()
7052
}
7153

72-
public async listen(): Promise<void> {
73-
if (this.client) {
74-
this.client.on('connection', socket => {
75-
const close = this.handleConnection(socket)
76-
const handleClose = async () => {
77-
await close()
78-
socket.terminate()
79-
}
80-
81-
this.client!.on('close', handleClose)
82-
onSignal(handleClose)
83-
})
54+
public close(): void {
55+
for (const state of this.states) {
56+
isFunction(state.close) && state.close()
8457
}
8558
}
8659

87-
public async close(): Promise<void> {
88-
await Promise.all(
89-
Array.from(this.states).map(
90-
async state =>
91-
isFunction(state.close) &&
92-
state.close({
93-
state: { ...this.state },
94-
setState: this.setState(),
95-
})
96-
)
97-
)
60+
public onStateChange(listener: Listener): () => void {
61+
this.listeners.add(listener)
62+
return () => this.listeners.clear()
9863
}
9964

100-
private handleConnection(socket: WS): () => void {
101-
const states = Array.from(this.states).map(
102-
async state =>
103-
isFunction(state.update) &&
104-
state.update({
105-
state: this.state,
106-
setState: this.setState(socket),
107-
})
108-
)
109-
110-
return async () => {
111-
const fns = await Promise.all(states.filter(Boolean))
112-
for (const fn of fns) isFunction(fn) && fn()
113-
}
65+
private setState(key: string, val: any): void {
66+
this.state.set(key, val)
67+
this.writeDbFile()
68+
this.listeners.forEach(listener => {
69+
listener({ type: `state.${key}`, payload: val })
70+
})
11471
}
11572

116-
private setState(socket?: WS): (key: string, val: any) => void {
117-
const send = sender(socket)
118-
119-
return (key: string, val: any): void => {
120-
this.state[key] = val
121-
send(`state.${key}`, val)
122-
this.updateStateFile()
123-
}
73+
private async writeDbFile(): Promise<void> {
74+
fs.outputJSONSync(paths.db, this.mapToObject(this.state), { spaces: 2 })
12475
}
12576

126-
private async updateStateFile(): Promise<void> {
127-
await fs.outputJSON(paths.db, this.state, { spaces: 2 })
77+
private mapToObject<T>(map: Map<string, any>): T {
78+
return Array.from(map.entries()).reduce(
79+
(obj, [key, val]) => ({ ...obj, [key]: val }),
80+
{} as T
81+
)
12882
}
12983
}

core/docz-core/src/lib/Socket.ts

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import WS from 'ws'
2+
import { onSignal } from '../utils/on-signal'
3+
4+
export type Send = (type: string, payload: any) => void
5+
export type On = (type: string) => Promise<any>
6+
7+
const isSocketOpened = (socket: WS) => socket.readyState === WS.OPEN
8+
const sender = (socket?: WS) => (type: string, payload: any) => {
9+
if (socket && isSocketOpened(socket)) {
10+
socket.send(JSON.stringify({ type, payload }))
11+
}
12+
}
13+
14+
export class Socket {
15+
private client?: WS.Server
16+
17+
constructor(server?: any, host?: string, port?: number) {
18+
if (server) {
19+
this.client = new WS.Server({
20+
server,
21+
host,
22+
port,
23+
})
24+
}
25+
}
26+
27+
public onConnection(listener: (socket: WS, emit: Send) => () => void): void {
28+
if (!this.client) return
29+
30+
this.client.on('connection', socket => {
31+
const emit = sender(socket)
32+
const subs = listener(socket, emit)
33+
34+
const handleClose = async () => {
35+
subs()
36+
socket.terminate()
37+
}
38+
39+
this.client!.on('close', handleClose)
40+
onSignal(handleClose)
41+
})
42+
}
43+
}

core/docz-core/src/states/config.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,17 @@ export const state = (config: Config): State => {
5656

5757
return {
5858
id: 'config',
59-
init: updateConfig(config),
60-
close: () => watcher.close(),
61-
update: async params => {
59+
start: async params => {
6260
const update = updateConfig(config)
6361
const fn = async () => update(params)
6462

63+
await update(params)
6564
watcher.on('add', fn)
6665
watcher.on('change', fn)
6766
watcher.on('unlink', fn)
68-
69-
return () => watcher.close()
67+
},
68+
close: () => {
69+
watcher.close()
7070
},
7171
}
7272
}

core/docz-core/src/states/entries.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -41,20 +41,20 @@ export const state = (entries: Entries, config: Config): State => {
4141

4242
return {
4343
id: 'entries',
44-
init: updateEntries(entries),
45-
close: () => watcher.close(),
46-
update: async params => {
44+
start: async params => {
4745
const update = updateEntries(entries)
4846

47+
watcher.on('add', async () => update(params))
4948
watcher.on('change', async () => update(params))
5049
watcher.on('unlink', async () => update(params))
5150
watcher.on('raw', async (event: string, path: string, details: any) => {
5251
if (details.event === 'moved' && details.type === 'directory') {
5352
await update(params)
5453
}
5554
})
56-
57-
return () => watcher.close()
55+
},
56+
close: () => {
57+
watcher.close()
5858
},
5959
}
6060
}

core/docz-core/src/states/props.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ const add = (config: Config) => (p: Params) => async (filepath: string) => {
3333
}
3434
}
3535

36-
const remove = (config: Config) => (p: Params) => async (filepath: string) =>
36+
const remove = (p: Params) => async (filepath: string) =>
3737
p.setState('props', omit(filepath, get('state.props', p)))
3838

3939
export const state = (config: Config): State => {
@@ -48,17 +48,17 @@ export const state = (config: Config): State => {
4848

4949
return {
5050
id: 'props',
51-
init: initial(config),
52-
close: () => watcher.close(),
53-
update: async params => {
51+
start: async params => {
5452
const addFilepath = add(config)
55-
const removeFilepath = remove(config)
53+
const addInitial = initial(config)
5654

55+
await addInitial(params)
5756
watcher.on('add', addFilepath(params))
5857
watcher.on('change', addFilepath(params))
59-
watcher.on('unlink', removeFilepath(params))
60-
61-
return () => watcher.close()
58+
watcher.on('unlink', remove(params))
59+
},
60+
close: () => {
61+
watcher.close()
6262
},
6363
}
6464
}

0 commit comments

Comments
 (0)