aboutsummaryrefslogtreecommitdiffstats
path: root/app/webserver
diff options
context:
space:
mode:
Diffstat (limited to 'app/webserver')
-rw-r--r--app/webserver/watcher.ts125
-rw-r--r--app/webserver/webserver.ts39
2 files changed, 164 insertions, 0 deletions
diff --git a/app/webserver/watcher.ts b/app/webserver/watcher.ts
new file mode 100644
index 0000000..a792473
--- /dev/null
+++ b/app/webserver/watcher.ts
@@ -0,0 +1,125 @@
+import chokidar, { FSWatcher } from 'chokidar';
+import { logger } from '../logger.js';
+import { PageDirectory } from '../builder/pages.js';
+import { rebuildSinglePage } from '../builder/build.js';
+import path from 'path';
+import fs from 'fs';
+
+function attachPageEvents(watcher: FSWatcher, pages: PageDirectory) {
+ const onPageChange = async (file: string) => {
+ logger.info(`File ${file} has been modified, rebuilding...`);
+ if (await rebuildSinglePage(file, pages)) {
+ logger.info(`...done`);
+ }
+ logger.info(``);
+ }
+
+ const onPageRemoval = (file: string) => {
+ logger.info(`File ${file} has been removed, deleting...`);
+ const page = pages.get(file.replace(/\.[^.]*$/,''));
+ if (!page) {
+ logger.error(`Failed to find page for ${file}`);
+ return;
+ }
+ const joinedPath = path.join(process.env.OUTPUT_DIR, `${page.route}.html`);
+ try {
+ fs.rmSync(joinedPath)
+ } catch (e) {
+ logger.error(`Failed to remove ${joinedPath}: ${e.message}`);
+ }
+ logger.info(`...done`);
+ logger.info(``);
+ }
+
+ watcher.on('add', onPageChange);
+ watcher.on('change', onPageChange);
+ watcher.on('unlink', onPageRemoval);
+}
+
+function attachStaticEvents(watcher: FSWatcher) {
+ const onStaticChange = async (file: string) => {
+ logger.info(`Static file ${file} has been modified, copying...`);
+ const joinedPath = path.join(process.env.STATIC_DIR, file);
+ const joinedOutputPath = path.join(process.env.OUTPUT_DIR, 'static', file);
+ try {
+ fs.copyFileSync(joinedPath, joinedOutputPath);
+ logger.info(`...done`);
+ } catch (e) {
+ logger.error(`Failed to copy ${joinedPath} to ${joinedOutputPath}: ${e.message}`);
+ }
+ logger.info(``);
+ }
+
+ const onStaticRemoval = (file: string) => {
+ logger.info(`Static file ${file} has been removed, deleting...`);
+ const joinedOutputPath = path.join(process.env.OUTPUT_DIR, 'static', file);
+ try {
+ fs.rmSync(joinedOutputPath)
+ logger.info(`...done`);
+ } catch (e) {
+ logger.error(`Failed to remove ${joinedOutputPath}: ${e.message}`);
+ }
+ logger.info(``);
+ }
+
+ watcher.on('add', onStaticChange);
+ watcher.on('change', onStaticChange);
+ watcher.on('unlink', onStaticRemoval);
+}
+
+function attachViewEvents(watcher: FSWatcher, pages: PageDirectory) {
+ const onViewChange = async (file: string) => {
+ logger.info(`View ${file} has been modified, rebuilding pages with view...`);
+ let pagesWithView = pages.getPages().filter(page => `${page.view}.ejs` === file);
+ logger.info(`Found ${pagesWithView.length} pages with view ${file}`);
+ for (const page of pagesWithView) {
+ logger.info(`Rebuilding page ${page.route}...`);
+ if (await rebuildSinglePage(page.originalPath, pages)) {
+ logger.info(`...done`);
+ }
+ }
+ logger.info(``);
+ }
+
+ const onViewRemoval = (file: string) => {
+ logger.info(``);
+ logger.info(`View ${file} has been removed`);
+ logger.info(``);
+ }
+
+ watcher.on('add', onViewChange);
+ watcher.on('change', onViewChange);
+ watcher.on('unlink', onViewRemoval);
+}
+
+export const start = (pages: PageDirectory) => {
+ const pagesWatcher = chokidar.watch('.', {
+ persistent: true,
+ cwd: process.env.PAGES_DIR,
+ ignoreInitial: true,
+ });
+ const staticWatcher = chokidar.watch('.', {
+ persistent: true,
+ cwd: process.env.STATIC_DIR,
+ ignoreInitial: true,
+ });
+ const viewsWatcher = chokidar.watch('.', {
+ persistent: true,
+ cwd: process.env.VIEWS_DIR,
+ ignoreInitial: true,
+ });
+
+ attachPageEvents(pagesWatcher, pages);
+ attachStaticEvents(staticWatcher);
+ attachViewEvents(viewsWatcher, pages);
+
+ const exitHandler = () => {
+ logger.info(`Stopping file watcher...`);
+ viewsWatcher.close();
+ staticWatcher.close();
+ pagesWatcher.close();
+ }
+
+ process.on('SIGINT', exitHandler);
+ process.on('SIGTERM', exitHandler);
+}
diff --git a/app/webserver/webserver.ts b/app/webserver/webserver.ts
new file mode 100644
index 0000000..82caa06
--- /dev/null
+++ b/app/webserver/webserver.ts
@@ -0,0 +1,39 @@
+import express from 'express';
+import { logger } from '../logger.js';
+import { AddressInfo } from 'net';
+import { PageDirectory } from '../builder/pages.js';
+
+const app = express();
+
+app.use(express.static(process.env.OUTPUT_DIR, { extensions: ['html'] }));
+
+export const start = (pages: PageDirectory) => {
+ const server = app.listen(process.env.WEBSERVER_PORT, () => {
+ const address = server.address() as AddressInfo;
+ logger.info(`Serving files from: ${process.env.OUTPUT_DIR}`);
+ logger.info(` Address: http://localhost:${address.port}`);
+ logger.info(` ^C to stop`);
+ logger.info('')
+
+ if (process.env.WEBSERVER_AUTOREBUILD === 'true') {
+ import('./watcher.js').then((watcher) => {
+ watcher.start(pages);
+ });
+ }
+ });
+
+ const closeServer = () => {
+ logger.info(`Stopping server...`);
+ server.close();
+ }
+
+ const exitHandler = () => {
+ if (server.listening) {
+ closeServer();
+ }
+ }
+
+ process.on('SIGINT', exitHandler);
+ process.on('SIGTERM', exitHandler);
+
+};