import express from 'express'; import type { NextFunction, Request, Response } from 'express'; import cors from 'cors'; import { getBuildConfig, defaultOptions } from './util.js'; import { context } from 'esbuild'; import chokidar from 'chokidar'; import { generateLangium } from '../.build/generateLangium.js'; const parserCtx = await context( getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'parser' }) ); const mermaidCtx = await context( getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid' }) ); const mermaidIIFECtx = await context( getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid', format: 'iife', }) ); const externalCtx = await context( getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid-example-diagram', }) ); const zenumlCtx = await context( getBuildConfig({ ...defaultOptions, minify: false, core: false, entryName: 'mermaid-zenuml' }) ); const contexts = [parserCtx, mermaidCtx, mermaidIIFECtx, externalCtx, zenumlCtx]; const rebuildAll = async () => { console.time('Rebuild time'); await Promise.all(contexts.map((ctx) => ctx.rebuild())); console.timeEnd('Rebuild time'); }; let clients: { id: number; response: Response }[] = []; function eventsHandler(request: Request, response: Response, next: NextFunction) { const headers = { 'Content-Type': 'text/event-stream', Connection: 'keep-alive', 'Cache-Control': 'no-cache', }; response.writeHead(200, headers); const clientId = Date.now(); clients.push({ id: clientId, response, }); request.on('close', () => { clients = clients.filter((client) => client.id !== clientId); }); } let timeoutId: NodeJS.Timeout | undefined = undefined; /** * Debounce file change events to avoid rebuilding multiple times. */ function handleFileChange() { if (timeoutId !== undefined) { clearTimeout(timeoutId); } timeoutId = setTimeout(async () => { await rebuildAll(); sendEventsToAll(); timeoutId = undefined; }, 100); } function sendEventsToAll() { clients.forEach(({ response }) => response.write(`data: ${Date.now()}\n\n`)); } async function createServer() { generateLangium(); handleFileChange(); const app = express(); chokidar .watch('**/src/**/*.{js,ts,langium,yaml,json}', { ignoreInitial: true, ignored: [/node_modules/, /dist/, /docs/, /coverage/], }) .on('all', async (event, path) => { // Ignore other events. if (!['add', 'change'].includes(event)) { return; } if (/\.langium$/.test(path)) { generateLangium(); } console.log(`${path} changed. Rebuilding...`); handleFileChange(); }); app.use(cors()); app.get('/events', eventsHandler); app.use(express.static('./packages/parser/dist')); app.use(express.static('./packages/mermaid/dist')); app.use(express.static('./packages/mermaid-zenuml/dist')); app.use(express.static('./packages/mermaid-example-diagram/dist')); app.use(express.static('demos')); app.use(express.static('cypress/platform')); app.listen(9000, () => { console.log(`Listening on http://localhost:9000`); }); } createServer();