From 8da5d372d5d8174980556543adbe0df1313352a4 Mon Sep 17 00:00:00 2001 From: andwati Date: Mon, 31 Aug 2026 08:43:08 +0300 Subject: [PATCH] Fix: content changes weren't showing up without a dev server restart toml-content-loader.ts only read content/ once in load(), so a new entry created through Strapi (or any hand-edit) sat on disk correctly but never appeared on the site until astro dev was restarted. Astro's content layer only auto-refreshes for loaders that wire up the watcher it hands them. Refactored the read into a loadAll() function called both on initial load and on every add/change/unlink event from Astro's dev-mode watcher, scoped to the content directory. Verified live: created a file mid-session, it appeared within ~2s with no restart; deleting it disappeared just as fast. Production builds were never affected (astro build only calls load() once anyway, and always sees the current state of content/). --- apps/site/src/lib/toml-content-loader.ts | 80 +++++++++++++++--------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/apps/site/src/lib/toml-content-loader.ts b/apps/site/src/lib/toml-content-loader.ts index 39966a7..269f475 100644 --- a/apps/site/src/lib/toml-content-loader.ts +++ b/apps/site/src/lib/toml-content-loader.ts @@ -66,40 +66,60 @@ function walk(dir: string): string[] { export function tomlContentLoader(contentDir: string): Loader { return { name: "toml-content-loader", - load: async ({ store, config, logger }) => { - store.clear(); + load: async ({ store, config, logger, watcher }) => { const dirUrl = new URL(contentDir, config.root); - let files: string[] = []; - try { - statSync(dirUrl); - files = walk(dirUrl.pathname); - } catch { - logger.warn(`toml-content-loader: no directory at ${dirUrl.pathname}`); - return; - } - for (const file of files) { - const raw = readFileSync(file, "utf8"); - const match = raw.match(FRONTMATTER_RE); - if (!match) { + + async function loadAll() { + store.clear(); + let files: string[] = []; + try { + statSync(dirUrl); + files = walk(dirUrl.pathname); + } catch { logger.warn( - `toml-content-loader: skipping ${file}, no +++ frontmatter found`, + `toml-content-loader: no directory at ${dirUrl.pathname}`, ); - continue; + return; } - const [, frontmatter, body] = match; - const data = parseToml(frontmatter) as Record; - const relPath = relative(dirUrl.pathname, file); - const id = relPath.replace(/\/index\.md$/, "").replace(/\.md$/, ""); - currentHeadings = []; - const trimmedBody = body.trim(); - const html = await marked.parse(trimmedBody); - const wordCount = trimmedBody.split(/\s+/).filter(Boolean).length; - const readingMinutes = Math.max(1, Math.round(wordCount / 200)); - store.set({ - id, - data: { ...data, wordCount, readingMinutes }, - rendered: { html, metadata: { headings: currentHeadings } }, - }); + for (const file of files) { + const raw = readFileSync(file, "utf8"); + const match = raw.match(FRONTMATTER_RE); + if (!match) { + logger.warn( + `toml-content-loader: skipping ${file}, no +++ frontmatter found`, + ); + continue; + } + const [, frontmatter, body] = match; + const data = parseToml(frontmatter) as Record; + const relPath = relative(dirUrl.pathname, file); + const id = relPath.replace(/\/index\.md$/, "").replace(/\.md$/, ""); + currentHeadings = []; + const trimmedBody = body.trim(); + const html = await marked.parse(trimmedBody); + const wordCount = trimmedBody.split(/\s+/).filter(Boolean).length; + const readingMinutes = Math.max(1, Math.round(wordCount / 200)); + store.set({ + id, + data: { ...data, wordCount, readingMinutes }, + rendered: { html, metadata: { headings: currentHeadings } }, + }); + } + } + + await loadAll(); + + // In `astro dev`, re-scan the whole directory on any add/change/unlink + // so edits made outside Astro (Strapi's sync provider, or a file + // edited by hand) show up without restarting the dev server. + if (watcher) { + watcher.add(dirUrl.pathname); + const onChange = (changedPath: string) => { + if (changedPath.startsWith(dirUrl.pathname)) loadAll(); + }; + watcher.on("add", onChange); + watcher.on("change", onChange); + watcher.on("unlink", onChange); } }, };