From 475b34e0b11aec6abab670b28e672b1ad8f9e946 Mon Sep 17 00:00:00 2001 From: Eric Selin Date: Sun, 20 Feb 2022 13:40:57 +0100 Subject: [PATCH] fix: Server now rebuilds correctly when layouts changed Closes #18 --- CHANGELOG.md | 6 +++- cli.ts | 60 ++++++++++++++++++++++++--------- core/change-providers/fs-mod.ts | 35 ++++++++++++++++--- 3 files changed, 80 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47f4e0f..2504966 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Server no longer crashes when saving files in nvim - A bug in the file modification watcher caused the change list to include temporary (non-existent) files when saving with certain editors, such as vim. This is now fixed. + A bug in the file modification watcher caused the change list to include temporary (non-existent) files when saving with certain editors, such as nvim, causing an error. This is now fixed. + +- Server now re-builds correctly when layout files changed + + Because of the caching in Deno, the server was previously unable to re-build correctly when layout files were changed. Now changes in layout files will result in a complete site re-build with the new layouts. ### Changed diff --git a/cli.ts b/cli.ts index d70123c..d3abf5d 100644 --- a/cli.ts +++ b/cli.ts @@ -29,7 +29,10 @@ import { } from "./functions/mod.ts"; import type { Functions } from "./functions/mod.ts"; import { createChangesApplier } from "./core/api.ts"; -import changeOnFileModifications from "./core/change-providers/fs-mod.ts"; +import { + changeOnFileModifications, + restartOnFileModifications, +} from "./core/change-providers/fs-mod.ts"; import { FileCache } from "./core/cache.ts"; import { loadIfExists } from "./core/module-loader.ts"; import { importContent } from "./core/import-content.ts"; @@ -44,24 +47,34 @@ Builds are by default incremental, i.e. build only what is needed. USAGE: - bob [ACTION] [OPTIONS] + bob [COMMAND] [OPTIONS] + +COMMAND \`build\` (default if command not specified) + Build (render) site into public directory. Will do an incremental build - + use \`--force\` to re-build everything. -ACTION \`functions\` +COMMAND \`watch\` + Build site into public directory and re-build content file changes. + +COMMAND \`functions\` Start production server in order to service requests for fuctions. + OPTIONS specific to this command: + --fn-nginx-conf write function locations to nginx configuration file + --fn-hostname hostname to use for nginx proxing -ACTION \`server\` +COMMAND \`server\` Start development server serving both static files and functions. + Will re-build the site on content file and layout changes. + (Internally, this uses the \`watch\` command, restarting it on layout changes.) OPTIONS: - -p, --public path to public directory - -f, --force force build everything - will clean the current public directory - -d, --drafts build draft pages - --fn-nginx-conf write function locations to nginx configuration file - --fn-hostname hostname to use for nginx proxing - -v, --verbose verbose logging - -h, --help show help - -l, --license show license information`; + -i, --import import content before building + -p, --public path to public directory + -f, --force re-build from scratch (deletes public dir and cache) + -d, --drafts build draft pages + -v, --verbose verbose logging + -h, --help show help + -l, --license show license information`; const license = `Copyright 2021 Eric Selin @@ -108,13 +121,17 @@ const { const SERVER_ARG = "server"; const server = action === SERVER_ARG; const functions = action === "functions"; +// this is a hidden internal command +const watcher = action === "watch"; if (args.license) { console.log(license); Deno.exit(); } -console.log(licenseShort); +if (!watcher) { + console.log(licenseShort); +} if (args.help) { console.log(usage); @@ -194,10 +211,23 @@ if (server) { proxy404: `localhost:${functionsPort}`, }); + /** + Command to run. Always run via the `bob` executable. + Pass in original arguments, except for the "server" argument. + */ + const cmd = [ + "bob", + ...Deno.args.map((arg) => arg === SERVER_ARG ? "watch" : arg), + ]; + + restartOnFileModifications(buildOptions.layoutDir, cmd); +} + +if (watcher) { const applyChanges = createChangesApplier(buildOptions); changeOnFileModifications(buildOptions, applyChanges); } -if (!functions) { +if (!functions && !server) { await build(buildOptions); } diff --git a/core/change-providers/fs-mod.ts b/core/change-providers/fs-mod.ts index 8247820..3e22bc9 100644 --- a/core/change-providers/fs-mod.ts +++ b/core/change-providers/fs-mod.ts @@ -9,18 +9,16 @@ type ChangeArrayFn = ( ) => R; type ChangeArrayFilter = ChangeArrayFn; -export default async function changerFileModifications( +export const changeOnFileModifications = async ( buildOptions: BuildOptions, applyChanges: (changes: Change[]) => Promise, -) { +) => { /** File watcher for content and layout dirs. */ const watcher = Deno.watchFs([ //buildOptions.layoutDir, buildOptions.contentDir, ]); - buildOptions.log?.warning("Not updating on layout changes"); - const pendingEvents: Deno.FsEvent[] = []; const getChanges = _changesFromFsEvents(buildOptions); const applyEvents = debounce( @@ -34,7 +32,34 @@ export default async function changerFileModifications( // apply events (debounced, as per above) applyEvents(pendingEvents); } -} +}; + +export const restartOnFileModifications = async ( + directory: string, + cmd: string[], +) => { + /** File watcher for content and layout dirs. */ + const watcher = Deno.watchFs([ + directory, + ]); + + let process = Deno.run({ cmd }); + + const restart = debounce( + async () => { + process.kill("SIGTERM"); + await process.status(); + process = Deno.run({ cmd }); + }, + 100, + ); + + for await (const event of watcher) { + if (event.kind !== "access") { + restart(); + } + } +}; /** Create one or many changes based on a filesystem event */ const _eventToChange = (event: Deno.FsEvent): Change | Change[] => {