UNPKG

myst-cli

Version:
131 lines (130 loc) 5.54 kB
import chalk from 'chalk'; import cors from 'cors'; import express from 'express'; import getPort, { portNumbers } from 'get-port'; import { makeExecutable } from 'myst-cli-utils'; import { nanoid } from 'nanoid'; import { join } from 'node:path'; import { WebSocketServer } from 'ws'; import version from '../../version.js'; import { createServerLogger } from './logger.js'; import { buildSite } from './prepare.js'; import { installSiteTemplate, getSiteTemplate } from './template.js'; import { watchContent } from './watch.js'; const DEFAULT_START_COMMAND = 'npm run start'; /** * Creates a content server and a websocket that can reload and log messages to the client. */ export async function startContentServer(session, opts) { var _a; const port = (_a = opts === null || opts === void 0 ? void 0 : opts.serverPort) !== null && _a !== void 0 ? _a : (await getPort({ port: portNumbers(3100, 3200) })); const app = express(); app.use(cors()); app.get('/', (req, res) => { res.json({ version, links: { site: `http://localhost:${port}/config.json`, }, }); }); app.use('/', express.static(session.publicPath())); app.use('/content', express.static(session.contentPath())); app.use('/config.json', express.static(join(session.sitePath(), 'config.json'))); app.use('/objects.inv', express.static(join(session.sitePath(), 'objects.inv'))); app.use('/myst.xref.json', express.static(join(session.sitePath(), 'myst.xref.json'))); app.use('/myst.search.json', express.static(join(session.sitePath(), 'myst.search.json'))); const server = app.listen(port, () => { session.log.debug(`Content server listening on port ${port}`); }); const wss = new WebSocketServer({ noServer: true, path: '/socket', }); const connections = {}; wss.on('connection', function connection(ws) { const id = nanoid(); session.log.debug(`Content server websocket connected ${id}`); connections[id] = ws; ws.on('close', () => { session.log.debug(`Content server websocket disconnected ${id}`); delete connections[id]; }); }); /** * Send a message to all connections. */ const sendJson = (data) => { Object.entries(connections).forEach(([, ws]) => { ws.send(JSON.stringify(data)); }); }; // Create log and reload functions for later const log = (message) => sendJson({ type: 'LOG', message }); const reload = () => sendJson({ type: 'RELOAD' }); server.on('upgrade', (request, socket, head) => { wss.handleUpgrade(request, socket, head, (websocket) => { wss.emit('connection', websocket, request); }); }); const stop = () => { server.close(); wss.close(); }; return { port, reload, log, stop }; } export function warnOnHostEnvironmentVariable(session, opts) { if (process.env.HOST && process.env.HOST !== 'localhost') { if (opts === null || opts === void 0 ? void 0 : opts.keepHost) { session.log.warn(`\nThe HOST environment variable is set to "${process.env.HOST}", this may cause issues for the web server.\n`); } else { session.log.warn(`\nThe HOST environment variable is set to "${process.env.HOST}", we are overwriting this to "localhost".\nTo keep this value use the \`--keep-host\` flag.\n`); process.env.HOST = 'localhost'; } } } export async function startServer(session, opts) { var _a; // Ensure we are on the latest version of the configs await session.reload(); warnOnHostEnvironmentVariable(session, opts); const mystTemplate = await getSiteTemplate(session, opts); if (!opts.headless && !opts.template) await installSiteTemplate(session, mystTemplate); await buildSite(session, opts); const server = await startContentServer(session, opts); if (!opts.buildStatic) { watchContent(session, server.reload, opts); } if (opts.headless) { const local = chalk.green(`http://localhost:${server.port}`); session.log.info(`\n🔌 Content server started on port ${server.port}! 🥳 🎉\n\n\n\t👉 ${local} 👈\n\n`); return undefined; } session.log.info(`\n\n\t✨✨✨ Starting ${mystTemplate.getValidatedTemplateYml().title} ✨✨✨\n\n`); const port = (_a = opts === null || opts === void 0 ? void 0 : opts.port) !== null && _a !== void 0 ? _a : (await getPort({ port: portNumbers(3000, 3100) })); const appServer = { port }; await new Promise((resolve) => { var _a, _b; const start = makeExecutable((_b = (_a = mystTemplate.getValidatedTemplateYml().build) === null || _a === void 0 ? void 0 : _a.start) !== null && _b !== void 0 ? _b : DEFAULT_START_COMMAND, createServerLogger(session, resolve), { cwd: mystTemplate.templatePath, env: { ...process.env, CONTENT_CDN_PORT: String(server.port), PORT: String(port), MODE: opts.buildStatic ? 'static' : 'app', BASE_URL: opts.baseurl || undefined, }, getProcess(process) { appServer.process = process; }, }); start().catch((e) => session.log.debug(e)); }); appServer.stop = () => { appServer.process.kill(); server.stop(); }; return appServer; }