caracms 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.ts +43 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caracms",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "A visual CMS for Astro sites, with content in git.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/src/index.ts CHANGED
@@ -49,9 +49,9 @@
49
49
  import type { AstroIntegration } from 'astro';
50
50
  import { fontProviders } from 'astro/config';
51
51
  import { execFileSync } from 'node:child_process';
52
- import { existsSync, readFileSync, writeFileSync } from 'node:fs';
52
+ import { existsSync, readFileSync, watch, writeFileSync, type FSWatcher } from 'node:fs';
53
53
  import { createRequire } from 'node:module';
54
- import { isAbsolute, join, sep } from 'node:path';
54
+ import { basename, dirname, isAbsolute, join, sep } from 'node:path';
55
55
  import { fileURLToPath, pathToFileURL } from 'node:url';
56
56
  import { resolveSite, SITE_SETTINGS_FILE, type ResolvedSite, type SiteConfig } from './config.ts';
57
57
  import { lockupCss } from './lockup-css.ts';
@@ -219,6 +219,39 @@ export function contentWatchIgnores(site: Pick<ResolvedSite, 'collections' | 'ro
219
219
  ];
220
220
  }
221
221
 
222
+ /**
223
+ * The content contentWatchIgnores takes out of Vite's watch, watched apart, so the dev server
224
+ * learns a page was added, moved, hidden or removed. Astro's dev server caches each route's
225
+ * `getStaticPaths` and clears it only on its own content changes, so without this the public
226
+ * site kept its old list of pages until a restart (an old address even answered 500 rather
227
+ * than its redirect). Every collection folder that exists at start, recursively, and the
228
+ * Site Settings file; `onChange` once per burst of changes. Returns the stop.
229
+ */
230
+ export function watchContent(site: Pick<ResolvedSite, 'collections' | 'root'>, onChange: () => void, wait = 100): () => void {
231
+ let timer: ReturnType<typeof setTimeout> | undefined;
232
+ const changed = () => {
233
+ clearTimeout(timer);
234
+ timer = setTimeout(onChange, wait);
235
+ };
236
+ const watchers: FSWatcher[] = [];
237
+ const add = (path: string, recursive: boolean, only?: string) => {
238
+ if (!existsSync(path)) return;
239
+ const watcher = watch(path, { recursive, persistent: false }, (_event, name) => {
240
+ if (!only || name === only) changed();
241
+ });
242
+ // A watched folder that's removed mustn't take the dev server down with it.
243
+ watcher.on('error', () => watcher.close());
244
+ watchers.push(watcher);
245
+ };
246
+ for (const c of Object.values(site.collections)) if (c.kind !== 'settings') add(c.dir, true);
247
+ const settings = join(site.root, SITE_SETTINGS_FILE);
248
+ add(dirname(settings), false, basename(settings));
249
+ return () => {
250
+ clearTimeout(timer);
251
+ for (const watcher of watchers) watcher.close();
252
+ };
253
+ }
254
+
222
255
  /**
223
256
  * The hosts Astro may take a request's address from (`security.allowedDomains`).
224
257
  * Without them, Astro ignores the Host header and every request reads as
@@ -461,13 +494,20 @@ export default function caracms(config: SiteConfig): AstroIntegration {
461
494
  },
462
495
  // M8 Decision 14: a dev server (at start, and on the restart a config change makes) regenerates
463
496
  // schemas/ from the config. Never content files: a dev run only changes what your code change derives.
464
- 'astro:server:setup': ({ logger }) => {
497
+ 'astro:server:setup': ({ server, logger }) => {
465
498
  try {
466
499
  const changed = writeSchemaFiles(site);
467
500
  if (changed.length) logger.info(`updated ${changed.map((c) => c.file).join(', ')}`);
468
501
  } catch (error) {
469
502
  logger.warn(`couldn't write schemas/: ${(error as Error).message}`);
470
503
  }
504
+ // A content change re-runs every route's getStaticPaths, as Astro does for its own content
505
+ // collections (the same event, to the same server environments). Never a browser reload:
506
+ // the editor writes these files.
507
+ const stop = watchContent(site, () => {
508
+ for (const name of ['ssr', 'prerender']) server.environments[name]?.hot.send('astro:content-changed', {});
509
+ });
510
+ server.httpServer?.once('close', stop);
471
511
  },
472
512
  // Posts and tags, Decision 15: a production build reads each document once (read.ts). Never in dev.
473
513
  'astro:build:start': () => shareBuildReads(true),