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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
import {
Logger
} from "@gnu-taler/taler-util";
import chokidar from 'chokidar';
import express from "express";
import https from "https";
import { parse } from 'url';
import WebSocket, { Server } from 'ws';
import locahostCrt from './keys/localhost.crt';
import locahostKey from './keys/localhost.key';
import storiesHtml from './stories.html';
import path from "path";
const httpServerOptions = {
key: locahostKey,
cert: locahostCrt
};
const logger = new Logger("serve.ts");
const PATHS = {
WS: "/ws",
NOTIFY: "/notify",
EXAMPLE: "/examples",
APP: "/app",
}
export async function serve(opts: {
folder: string,
port: number,
source?: string,
development?: boolean,
examplesLocationJs?: string,
examplesLocationCss?: string,
onUpdate?: () => Promise<void>;
}): Promise<void> {
const app = express()
app.use(PATHS.APP, express.static(opts.folder))
const server = https.createServer(httpServerOptions, app)
server.listen(opts.port);
logger.info(`serving ${opts.folder} on ${opts.port}`)
logger.info(` ${PATHS.APP}: application`)
logger.info(` ${PATHS.EXAMPLE}: examples`)
logger.info(` ${PATHS.WS}: websocket`)
logger.info(` ${PATHS.NOTIFY}: broadcast`)
if (opts.development) {
const wss = new Server({ noServer: true });
wss.on('connection', function connection(ws) {
ws.send('welcome');
});
server.on('upgrade', function upgrade(request, socket, head) {
const { pathname } = parse(request.url || "");
if (pathname === PATHS.WS) {
wss.handleUpgrade(request, socket, head, function done(ws) {
wss.emit('connection', ws, request);
});
} else {
socket.destroy();
}
});
const sendToAllClients = function (data: object): void {
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(data));
}
})
}
const watchingFolder = opts.source ?? opts.folder
logger.info(`watching ${watchingFolder} for change`)
chokidar.watch(watchingFolder).on('change', (path, stats) => {
logger.trace(`changed ${path}`)
sendToAllClients({ type: 'file-updated-start', data: { path } })
if (opts.onUpdate) {
opts.onUpdate().then(result => {
sendToAllClients({ type: 'file-updated-done', data: { path, result } })
})
} else {
sendToAllClients({ type: 'file-change-done', data: { path } })
}
})
app.get(PATHS.EXAMPLE, function (req: any, res: any) {
res.set('Content-Type', 'text/html')
res.send(storiesHtml
.replace('__EXAMPLES_JS_FILE_LOCATION__', opts.examplesLocationJs ?? `.${PATHS.APP}/stories.js`)
.replace('__EXAMPLES_CSS_FILE_LOCATION__', opts.examplesLocationCss ?? `.${PATHS.APP}/stories.css`))
})
app.get(PATHS.NOTIFY, function (req: any, res: any) {
res.send('ok')
})
}
}
|