tiddlywiki-plugin-dev
Version:
[](https://github.com/tiddly-gittly)
176 lines (175 loc) • 5.75 kB
JavaScript
import path from "path";
import tw from "tiddlywiki";
import chokidar from "chokidar";
import { rebuild } from "./packup.js";
import { createDevRefreshHandler } from "./dev-refresh.js";
import { createNotifyServer } from "./dev-ws-server.js";
import { buildWatchIgnored } from "./dev-ignored.js";
import { createWikiPortResolver } from "./dev-port.js";
import { buildListenArgs } from "./dev-listen-args.js";
import { renderDevWebListenerScript } from "./devweb-listener-template.js";
import { closeServerForRestart } from "./dev-server-close.js";
import { tiddlywiki } from "./utils.js";
const runDev = async (wiki, src, configs) => {
const { lan, writeWiki, excludeFilter } = configs;
const { attachToHttpServer, closeAllClients, notifyRefresh, setBuildGeneration, onSaveBusyChange } = await createNotifyServer();
let detachWs;
const watchRoots = Array.from(
new Set([src, wiki].map((target) => path.resolve(target)))
);
let buildGeneration = 0;
const nextGeneration = () => buildGeneration;
let saveBusy = false;
let deferredPaths;
onSaveBusyChange((busy) => {
saveBusy = busy;
if (busy) {
console.log(
"[Modern.TiddlyDev] [save] Browser is writing files — deferring server restarts"
);
} else {
console.log(
"[Modern.TiddlyDev] [save] Browser write finished — flushing deferred changes"
);
const paths = deferredPaths;
deferredPaths = void 0;
if (paths && paths.length > 0) {
console.log(
`[Modern.TiddlyDev] [save] ${paths.length} file(s) changed during save`
);
triggerRefresh();
}
}
});
const scannerPreload = writeWiki ? [] : [{ title: "$:/config/SyncFilter", text: "" }];
const $tw1 = tiddlywiki(scannerPreload, wiki);
let twServer;
const resolveStableWikiPort = createWikiPortResolver(lan);
const watcher = chokidar.watch(watchRoots, {
ignoreInitial: true,
followSymlinks: true,
ignored: buildWatchIgnored($tw1, src, wiki),
awaitWriteFinish: {
stabilityThreshold: 1e3,
pollInterval: 100
}
});
const reportRefreshError = (error, changedPaths) => {
const changed = changedPaths.filter(Boolean);
const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
console.error(
changed.length > 0 ? `[${timestamp}] [refresh] Compilation failed for: ${changed.join(", ")}` : `[${timestamp}] [refresh] Compilation failed during initial build.`
);
console.error("Waiting for the next change to retry...");
console.error(error);
};
const startWikiServer = async (wikiRuntime, changedPaths, generation) => {
const $tw = wikiRuntime.runtime;
let resolve;
let settled = false;
const finish = (started) => {
if (!settled) {
settled = true;
resolve(started);
}
};
const wait = new Promise((_resolve) => resolve = _resolve);
$tw.hooks.addHook(
"th-server-command-post-start",
(_listenCommand, newTwServer) => {
const onServerError = (error) => {
reportRefreshError(error, changedPaths);
finish(false);
};
newTwServer.once("error", onServerError);
newTwServer.once("listening", () => {
detachWs?.();
detachWs = attachToHttpServer(newTwServer);
setBuildGeneration(generation);
finish(true);
});
twServer = newTwServer;
}
);
const serve = async () => {
const port = await resolveStableWikiPort();
$tw.boot.argv = buildListenArgs({
wikiPath: wiki,
port,
lan,
writeWiki
});
$tw.boot.boot();
};
const startServer = () => {
serve().catch((error) => {
reportRefreshError(error, changedPaths);
finish(false);
});
};
if (twServer) {
closeServerForRestart(twServer, {
closeWsClients: closeAllClients,
onClosed: startServer
});
} else {
startServer();
}
return wait;
};
const refresh = createDevRefreshHandler({
renderListenerScript: renderDevWebListenerScript,
nextGeneration,
writeWiki,
rebuildPlugins: async (changedPaths) => {
$tw1.wiki.deleteTiddler("$:/Modern.TiddlyDev/devWebsocket/listener");
const plugins = await rebuild($tw1, src, changedPaths, true, excludeFilter);
buildGeneration++;
return plugins;
},
createWiki: () => {
const $tw = tw.TiddlyWiki();
return {
runtime: $tw,
preloadTiddler: (tiddler) => $tw.preloadTiddler(tiddler),
preloadTiddlerArray: (tiddlers) => $tw.preloadTiddlerArray(tiddlers),
appendExtraPlugins: (plugins) => {
$tw.boot.extraPlugins = [
...$tw.boot.extraPlugins ?? [],
...plugins
];
}
};
},
startServer: startWikiServer,
notifyRefresh,
reportError: reportRefreshError
});
const triggerRefresh = (changedPath) => {
if (saveBusy) {
if (changedPath) {
if (!deferredPaths)
deferredPaths = [];
deferredPaths.push(changedPath);
}
return;
}
const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
if (changedPath) {
console.log(
`[${timestamp}] [watch] File changed: ${changedPath}`
);
}
refresh(changedPath).catch((error) => reportRefreshError(error, []));
};
watcher.on("error", (error) => reportRefreshError(error, []));
watcher.on("ready", () => triggerRefresh());
watcher.on("add", triggerRefresh);
watcher.on("addDir", triggerRefresh);
watcher.on("change", triggerRefresh);
watcher.on("unlink", triggerRefresh);
watcher.on("unlinkDir", triggerRefresh);
};
export {
runDev
};