UNPKG

@oeyoews/tiddlywiki-plugin-dev

Version:

[![](https://img.shields.io/badge/Join-TiddlyWiki_CN-blue)](https://github.com/tiddly-gittly)

132 lines (127 loc) 4.05 kB
import fs from 'fs'; import path from 'path'; import tw from 'tiddlywiki'; import chokidar from 'chokidar'; import { Server } from 'tw5-typed'; import { getPort } from 'get-port-please'; import { WebSocketServer, WebSocket } from 'ws'; import { rebuild } from './packup'; import { tiddlywiki } from './utils'; // WebSocket with TiddlyWiki on broswer const runServer = async () => { const port = await getPort({ port: 8081 }); const server = new WebSocketServer({ port }); const refreshHeartBeat = (ws: any) => { ws.isAlive = true; if (ws.heartBeatInterval) { clearInterval(ws.heartBeatInterval); } // eslint-disable-next-line consistent-return ws.heartBeatInterval = setInterval(() => { if (ws.isAlive === false) { clearInterval(ws.heartBeatInterval); delete ws.heartBeatInterval; return ws.terminate(); } ws.isAlive = false; ws.ping(); }, 5_000); }; server.on('connection', ws => { refreshHeartBeat(ws); ws.ping(); ws.on('pong', () => refreshHeartBeat(ws)); ws.on('close', () => { if ((ws as any).heartBeatInterval) { clearInterval((ws as any).heartBeatInterval); } }); }); server.on('close', () => { server.clients.forEach(ws => { if ((ws as any).heartBeatInterval) { clearInterval((ws as any).heartBeatInterval); } ws.send('bye'); }); }); return { server, port }; }; // Run refresh server export const runDev = async ( wiki: string, src: string, excludeFilter?: string, ) => { const { server, port } = await runServer(); const devWebListnerScript = fs .readFileSync(path.resolve(__dirname, 'src/devweb-listener.js'), 'utf-8') .replace('$$$$port$$$$', `${port}`); // Watch source files change const $tw1 = tiddlywiki([], wiki); let twServer: Server; const watcher = chokidar.watch(src, { ignoreInitial: true, followSymlinks: true, ignored: $tw1.boot.excludeRegExp, awaitWriteFinish: { stabilityThreshold: 1000, pollInterval: 100, }, }); let updateFiles: string[] | undefined; const refresh = async (path: string) => { // 因为 build 是异步的,这里给 refresh 加一个资源锁,否则会出现奇怪的问题 if (updateFiles !== undefined) { updateFiles.push(path); return; } else { updateFiles = [path]; } while (updateFiles?.length) { let resolve: (value: void | PromiseLike<void>) => void; const wait = new Promise<void>(_resolve => (resolve = _resolve)); const tmp = updateFiles; updateFiles = []; $tw1.wiki.deleteTiddler('$:/Modern.TiddlyDev/devWebsocket/listener'); const plugins = await rebuild($tw1, src, tmp, true, excludeFilter); const $tw = tw.TiddlyWiki(); $tw.preloadTiddler({ title: '$:/Modern.TiddlyDev/devWebsocket/listener', text: devWebListnerScript, type: 'application/javascript', 'module-type': 'startup', }); $tw.preloadTiddlerArray(plugins); $tw.hooks.addHook( 'th-server-command-post-start', // eslint-disable-next-line @typescript-eslint/no-loop-func (_listenCommand, newTwServer) => { newTwServer.on('listening', () => resolve()); twServer = newTwServer; }, ); const serve = async () => { const port = await getPort({ port: 8080 }); $tw.boot.argv = [wiki, '--listen', `port=${port}`]; $tw.boot.boot(); }; if (twServer) { twServer.on('close', serve); twServer.close(); } else { serve(); } await wait; } server.clients.forEach(ws => { if (ws.readyState === WebSocket.OPEN) { ws.send('refresh'); } }); updateFiles = undefined; }; watcher.on('ready', refresh); watcher.on('add', refresh); watcher.on('change', refresh); };