@backmatter/next 0.0.15 → 0.0.17

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.
package/dist/config.cjs CHANGED
@@ -84,8 +84,10 @@ var import_node_path2 = __toESM(require("node:path"));
84
84
  var DEFAULT_DIRS = {
85
85
  models: "models",
86
86
  blocks: "blocks",
87
+ richtext: "richtext",
87
88
  mdx: "mdx",
88
- layouts: "layouts"
89
+ layouts: "layouts",
90
+ objects: "objects"
89
91
  };
90
92
 
91
93
  // ../core/src/node/discover.ts
@@ -146,6 +148,9 @@ function listBlockFallbackFiles(root, blocksDir = "blocks") {
146
148
  function listLayoutFiles(root, layoutsDir = "layouts") {
147
149
  return listModelFiles(root, layoutsDir);
148
150
  }
151
+ function listObjectFiles(root, objectsDir = "objects") {
152
+ return listModelFiles(root, objectsDir);
153
+ }
149
154
  function listBlockFiles(root, blocksDir = "blocks") {
150
155
  const { files, skipped } = listPascalCaseFiles(root, blocksDir);
151
156
  return { blocks: files, skipped };
@@ -182,7 +187,7 @@ function detectSdk(root) {
182
187
  }
183
188
  function renderCachedCmsClient() {
184
189
  return [
185
- "const raw = createBackmatter({ ...config, models });",
190
+ "const raw = createBackmatter({ ...config, models, sync: false });",
186
191
  "",
187
192
  "async function getDoc(locale: string, type: string, slug: string) {",
188
193
  ' "use cache";',
@@ -237,6 +242,8 @@ function renderModelsBarrel(input) {
237
242
  `;
238
243
  }
239
244
  function renderSyncBarrel(input) {
245
+ const objects = input.objects ?? [];
246
+ const richtext = input.richtext ?? [];
240
247
  const lines = [GENERATED_HEADER, `import { createBackmatter } from "${input.sdk}";`];
241
248
  if (input.configFile) {
242
249
  lines.push(`import config from "${importSpecifier(input.outDir, input.configFile)}";`);
@@ -249,14 +256,24 @@ function renderSyncBarrel(input) {
249
256
  for (const b of input.blocks) {
250
257
  lines.push(`import ${b.name} from "${importSpecifier(input.outDir, b.file)}";`);
251
258
  }
259
+ for (const b of richtext) {
260
+ lines.push(`import richtext${b.name} from "${importSpecifier(input.outDir, b.file)}";`);
261
+ }
262
+ for (const object of objects) {
263
+ lines.push(`import ${object.importName} from "${importSpecifier(input.outDir, object.file)}";`);
264
+ }
252
265
  lines.push("");
253
266
  lines.push(`export const models = [${input.models.map((m) => m.importName).join(", ")}] as const;`);
254
267
  lines.push("export type Models = typeof models;");
255
268
  lines.push("");
256
269
  lines.push(`export const blocks = [${input.blocks.map((b) => b.name).join(", ")}] as const;`);
257
270
  lines.push("");
271
+ lines.push(`export const richtext = [${richtext.map((b) => `richtext${b.name}`).join(", ")}] as const;`);
272
+ lines.push("");
273
+ lines.push(`export const objects = [${objects.map((o) => o.importName).join(", ")}] as const;`);
274
+ lines.push("");
258
275
  lines.push("/** Typed content client: cms.query.{type}, cms.getDoc, cms.listDocs. */");
259
- lines.push("export const cms = createBackmatter({ ...config, models, blocks });");
276
+ lines.push("export const cms = createBackmatter({ ...config, models, blocks, richtext, objects, sync: true });");
260
277
  return `${lines.join(`
261
278
  `)}
262
279
  `;
@@ -274,6 +291,19 @@ function renderMdxBarrel(input) {
274
291
  `)}
275
292
  `;
276
293
  }
294
+ function renderRichtextBarrel(input) {
295
+ const lines = [GENERATED_HEADER];
296
+ for (const b of input.richtext) {
297
+ lines.push(`import ${b.name} from "${importSpecifier(input.outDir, b.file)}";`);
298
+ }
299
+ lines.push("");
300
+ lines.push("/** In-content blocks: <Image /> in MDX resolves to richtext/Image.tsx. */");
301
+ const entries = input.richtext.map((b) => `${b.name}: ${b.name}.component`);
302
+ lines.push(`export const richtextComponents = ${entries.length ? `{ ${entries.join(", ")} }` : "{}"};`);
303
+ return `${lines.join(`
304
+ `)}
305
+ `;
306
+ }
277
307
  function renderBlocksBarrel(input) {
278
308
  const lines = [GENERATED_HEADER, ""];
279
309
  lines.push('/** Composer loaders: JSON `{ type: "Hero" }` → dynamic import of blocks/Hero.tsx. */');
@@ -316,6 +346,7 @@ var REGISTRY_FILES = [
316
346
  "blocks.ts",
317
347
  "block-fallbacks.ts",
318
348
  "mdx.ts",
349
+ "richtext.ts",
319
350
  "layouts.ts",
320
351
  "sync.ts"
321
352
  ];
@@ -324,9 +355,11 @@ function generate(options) {
324
355
  const outDir = import_node_path2.default.resolve(root, options.outDir ?? DEFAULT_OUT_DIR);
325
356
  const models = listModelFiles(root, options.modelsDir ?? DEFAULT_DIRS.models);
326
357
  const { blocks, skipped: skippedBlocks } = listBlockFiles(root, options.blocksDir ?? DEFAULT_DIRS.blocks);
358
+ const { blocks: richtext, skipped: skippedRichtext } = listBlockFiles(root, options.richtextDir ?? DEFAULT_DIRS.richtext);
327
359
  const fallbacks = listBlockFallbackFiles(root, options.blocksDir ?? DEFAULT_DIRS.blocks);
328
360
  const { blocks: mdx, skipped: skippedMdx } = listMdxFiles(root, options.mdxDir ?? DEFAULT_DIRS.mdx);
329
361
  const layouts = listLayoutFiles(root, options.layoutsDir ?? DEFAULT_DIRS.layouts);
362
+ const objects = listObjectFiles(root, options.objectsDir ?? DEFAULT_DIRS.objects);
330
363
  const sdk = options.sdk ?? detectSdk(root);
331
364
  const configFile = findConfigFile(root);
332
365
  const write = options.write ?? true;
@@ -335,8 +368,9 @@ function generate(options) {
335
368
  "blocks.ts": renderBlocksBarrel({ outDir, blocks }),
336
369
  "block-fallbacks.ts": renderBlockFallbacksBarrel({ outDir, fallbacks }),
337
370
  "mdx.ts": renderMdxBarrel({ outDir, mdx }),
371
+ "richtext.ts": renderRichtextBarrel({ outDir, richtext }),
338
372
  "layouts.ts": renderLayoutsBarrel({ outDir, layouts }),
339
- "sync.ts": renderSyncBarrel({ outDir, models, blocks, configFile, sdk })
373
+ "sync.ts": renderSyncBarrel({ outDir, models, blocks, richtext, objects, configFile, sdk })
340
374
  };
341
375
  const outputs = REGISTRY_FILES.map((name) => {
342
376
  const file = import_node_path2.default.join(outDir, name);
@@ -354,9 +388,11 @@ function generate(options) {
354
388
  changed: outputs.filter((o) => o.changed).map((o) => o.rel),
355
389
  models,
356
390
  blocks,
391
+ richtext,
357
392
  mdx,
358
393
  layouts,
359
- skippedBlocks,
394
+ objects,
395
+ skippedBlocks: [...skippedBlocks, ...skippedRichtext],
360
396
  skippedMdx,
361
397
  configFile
362
398
  };
@@ -391,8 +427,10 @@ function resolveLoaderOptions(options = {}) {
391
427
  outDir: options.outDir ?? DEFAULT_OUT_DIR,
392
428
  modelsDir: options.modelsDir ?? DEFAULT_DIRS.models,
393
429
  blocksDir: options.blocksDir ?? DEFAULT_DIRS.blocks,
430
+ richtextDir: options.richtextDir ?? DEFAULT_DIRS.richtext,
394
431
  mdxDir: options.mdxDir ?? DEFAULT_DIRS.mdx,
395
432
  layoutsDir: options.layoutsDir ?? DEFAULT_DIRS.layouts,
433
+ objectsDir: options.objectsDir ?? DEFAULT_DIRS.objects,
396
434
  sdk: SDK
397
435
  };
398
436
  }
@@ -408,7 +446,7 @@ function registryMatchers(loaderOptions) {
408
446
  const escaped = rel.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
409
447
  return {
410
448
  glob: `**/${rel}/*.ts`,
411
- test: new RegExp(`(^|[\\\\/])${escaped.replace(/\//g, "[\\\\/]")}[\\\\/](index|blocks|block-fallbacks|mdx|layouts|sync)\\.ts$`)
449
+ test: new RegExp(`(^|[\\\\/])${escaped.replace(/\//g, "[\\\\/]")}[\\\\/](index|blocks|block-fallbacks|mdx|richtext|layouts|sync)\\.ts$`)
412
450
  };
413
451
  }
414
452
  function withBackmatter(nextConfig = {}, options = {}) {
package/dist/loader.cjs CHANGED
@@ -78,8 +78,10 @@ var import_node_path2 = __toESM(require("node:path"));
78
78
  var DEFAULT_DIRS = {
79
79
  models: "models",
80
80
  blocks: "blocks",
81
+ richtext: "richtext",
81
82
  mdx: "mdx",
82
- layouts: "layouts"
83
+ layouts: "layouts",
84
+ objects: "objects"
83
85
  };
84
86
 
85
87
  // ../core/src/node/discover.ts
@@ -140,6 +142,9 @@ function listBlockFallbackFiles(root, blocksDir = "blocks") {
140
142
  function listLayoutFiles(root, layoutsDir = "layouts") {
141
143
  return listModelFiles(root, layoutsDir);
142
144
  }
145
+ function listObjectFiles(root, objectsDir = "objects") {
146
+ return listModelFiles(root, objectsDir);
147
+ }
143
148
  function listBlockFiles(root, blocksDir = "blocks") {
144
149
  const { files, skipped } = listPascalCaseFiles(root, blocksDir);
145
150
  return { blocks: files, skipped };
@@ -176,7 +181,7 @@ function detectSdk(root) {
176
181
  }
177
182
  function renderCachedCmsClient() {
178
183
  return [
179
- "const raw = createBackmatter({ ...config, models });",
184
+ "const raw = createBackmatter({ ...config, models, sync: false });",
180
185
  "",
181
186
  "async function getDoc(locale: string, type: string, slug: string) {",
182
187
  ' "use cache";',
@@ -231,6 +236,8 @@ function renderModelsBarrel(input) {
231
236
  `;
232
237
  }
233
238
  function renderSyncBarrel(input) {
239
+ const objects = input.objects ?? [];
240
+ const richtext = input.richtext ?? [];
234
241
  const lines = [GENERATED_HEADER, `import { createBackmatter } from "${input.sdk}";`];
235
242
  if (input.configFile) {
236
243
  lines.push(`import config from "${importSpecifier(input.outDir, input.configFile)}";`);
@@ -243,14 +250,24 @@ function renderSyncBarrel(input) {
243
250
  for (const b of input.blocks) {
244
251
  lines.push(`import ${b.name} from "${importSpecifier(input.outDir, b.file)}";`);
245
252
  }
253
+ for (const b of richtext) {
254
+ lines.push(`import richtext${b.name} from "${importSpecifier(input.outDir, b.file)}";`);
255
+ }
256
+ for (const object of objects) {
257
+ lines.push(`import ${object.importName} from "${importSpecifier(input.outDir, object.file)}";`);
258
+ }
246
259
  lines.push("");
247
260
  lines.push(`export const models = [${input.models.map((m) => m.importName).join(", ")}] as const;`);
248
261
  lines.push("export type Models = typeof models;");
249
262
  lines.push("");
250
263
  lines.push(`export const blocks = [${input.blocks.map((b) => b.name).join(", ")}] as const;`);
251
264
  lines.push("");
265
+ lines.push(`export const richtext = [${richtext.map((b) => `richtext${b.name}`).join(", ")}] as const;`);
266
+ lines.push("");
267
+ lines.push(`export const objects = [${objects.map((o) => o.importName).join(", ")}] as const;`);
268
+ lines.push("");
252
269
  lines.push("/** Typed content client: cms.query.{type}, cms.getDoc, cms.listDocs. */");
253
- lines.push("export const cms = createBackmatter({ ...config, models, blocks });");
270
+ lines.push("export const cms = createBackmatter({ ...config, models, blocks, richtext, objects, sync: true });");
254
271
  return `${lines.join(`
255
272
  `)}
256
273
  `;
@@ -268,6 +285,19 @@ function renderMdxBarrel(input) {
268
285
  `)}
269
286
  `;
270
287
  }
288
+ function renderRichtextBarrel(input) {
289
+ const lines = [GENERATED_HEADER];
290
+ for (const b of input.richtext) {
291
+ lines.push(`import ${b.name} from "${importSpecifier(input.outDir, b.file)}";`);
292
+ }
293
+ lines.push("");
294
+ lines.push("/** In-content blocks: <Image /> in MDX resolves to richtext/Image.tsx. */");
295
+ const entries = input.richtext.map((b) => `${b.name}: ${b.name}.component`);
296
+ lines.push(`export const richtextComponents = ${entries.length ? `{ ${entries.join(", ")} }` : "{}"};`);
297
+ return `${lines.join(`
298
+ `)}
299
+ `;
300
+ }
271
301
  function renderBlocksBarrel(input) {
272
302
  const lines = [GENERATED_HEADER, ""];
273
303
  lines.push('/** Composer loaders: JSON `{ type: "Hero" }` → dynamic import of blocks/Hero.tsx. */');
@@ -310,6 +340,7 @@ var REGISTRY_FILES = [
310
340
  "blocks.ts",
311
341
  "block-fallbacks.ts",
312
342
  "mdx.ts",
343
+ "richtext.ts",
313
344
  "layouts.ts",
314
345
  "sync.ts"
315
346
  ];
@@ -318,9 +349,11 @@ function generate(options) {
318
349
  const outDir = import_node_path2.default.resolve(root, options.outDir ?? DEFAULT_OUT_DIR);
319
350
  const models = listModelFiles(root, options.modelsDir ?? DEFAULT_DIRS.models);
320
351
  const { blocks, skipped: skippedBlocks } = listBlockFiles(root, options.blocksDir ?? DEFAULT_DIRS.blocks);
352
+ const { blocks: richtext, skipped: skippedRichtext } = listBlockFiles(root, options.richtextDir ?? DEFAULT_DIRS.richtext);
321
353
  const fallbacks = listBlockFallbackFiles(root, options.blocksDir ?? DEFAULT_DIRS.blocks);
322
354
  const { blocks: mdx, skipped: skippedMdx } = listMdxFiles(root, options.mdxDir ?? DEFAULT_DIRS.mdx);
323
355
  const layouts = listLayoutFiles(root, options.layoutsDir ?? DEFAULT_DIRS.layouts);
356
+ const objects = listObjectFiles(root, options.objectsDir ?? DEFAULT_DIRS.objects);
324
357
  const sdk = options.sdk ?? detectSdk(root);
325
358
  const configFile = findConfigFile(root);
326
359
  const write = options.write ?? true;
@@ -329,8 +362,9 @@ function generate(options) {
329
362
  "blocks.ts": renderBlocksBarrel({ outDir, blocks }),
330
363
  "block-fallbacks.ts": renderBlockFallbacksBarrel({ outDir, fallbacks }),
331
364
  "mdx.ts": renderMdxBarrel({ outDir, mdx }),
365
+ "richtext.ts": renderRichtextBarrel({ outDir, richtext }),
332
366
  "layouts.ts": renderLayoutsBarrel({ outDir, layouts }),
333
- "sync.ts": renderSyncBarrel({ outDir, models, blocks, configFile, sdk })
367
+ "sync.ts": renderSyncBarrel({ outDir, models, blocks, richtext, objects, configFile, sdk })
334
368
  };
335
369
  const outputs = REGISTRY_FILES.map((name) => {
336
370
  const file = import_node_path2.default.join(outDir, name);
@@ -348,9 +382,11 @@ function generate(options) {
348
382
  changed: outputs.filter((o) => o.changed).map((o) => o.rel),
349
383
  models,
350
384
  blocks,
385
+ richtext,
351
386
  mdx,
352
387
  layouts,
353
- skippedBlocks,
388
+ objects,
389
+ skippedBlocks: [...skippedBlocks, ...skippedRichtext],
354
390
  skippedMdx,
355
391
  configFile
356
392
  };
@@ -362,20 +398,26 @@ function backmatterLoader(source) {
362
398
  const root = import_node_path3.default.resolve(options.root);
363
399
  const modelsDir = import_node_path3.default.resolve(root, options.modelsDir);
364
400
  const blocksDir = import_node_path3.default.resolve(root, options.blocksDir);
401
+ const richtextDir = import_node_path3.default.resolve(root, options.richtextDir);
365
402
  const mdxDir = import_node_path3.default.resolve(root, options.mdxDir);
366
403
  const layoutsDir = import_node_path3.default.resolve(root, options.layoutsDir);
404
+ const objectsDir = import_node_path3.default.resolve(root, options.objectsDir);
367
405
  this.addContextDependency(modelsDir);
368
406
  this.addContextDependency(blocksDir);
407
+ this.addContextDependency(richtextDir);
369
408
  this.addContextDependency(mdxDir);
370
409
  this.addContextDependency(layoutsDir);
410
+ this.addContextDependency(objectsDir);
371
411
  this.cacheable?.(true);
372
412
  const result = generate({
373
413
  root,
374
414
  outDir: options.outDir,
375
415
  modelsDir: options.modelsDir,
376
416
  blocksDir: options.blocksDir,
417
+ richtextDir: options.richtextDir,
377
418
  mdxDir: options.mdxDir,
378
419
  layoutsDir: options.layoutsDir,
420
+ objectsDir: options.objectsDir,
379
421
  sdk: options.sdk
380
422
  });
381
423
  if (result.configFile)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backmatter/next",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "Experimental Next.js adapter for Backmatter. APIs will break. Not ready for production.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -38,7 +38,7 @@
38
38
  "test": "bun test"
39
39
  },
40
40
  "dependencies": {
41
- "@backmatter/core": "^0.0.14",
41
+ "@backmatter/core": "^0.0.16",
42
42
  "@mdx-js/mdx": "^3.1.1",
43
43
  "remark-gfm": "^4.0.1"
44
44
  },
package/src/config.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  // resolve `import { cms } from ".backmatter"`.
6
6
  // 2. Registers `@backmatter/next/loader` for the registry files with Turbopack
7
7
  // (Next 16 default) and webpack (`next build --webpack`). The loader declares
8
- // `models/`, `blocks/`, and `mdx/` as context dependencies, so the bundler's
8
+ // `models/`, `blocks/`, `richtext/`, and `mdx/` as context dependencies, so the bundler's
9
9
  // own file watcher keeps the registry fresh — no polling, no second watcher process.
10
10
  import { existsSync } from "node:fs";
11
11
  import path from "node:path";
@@ -24,8 +24,10 @@ export type WithBackmatterOptions = {
24
24
  outDir?: string;
25
25
  modelsDir?: string;
26
26
  blocksDir?: string;
27
+ richtextDir?: string;
27
28
  mdxDir?: string;
28
29
  layoutsDir?: string;
30
+ objectsDir?: string;
29
31
  /** Called after the boot-time generation. Default: log what changed. */
30
32
  onGenerate?: (result: GenerateResult) => void;
31
33
  };
@@ -80,8 +82,10 @@ export function resolveLoaderOptions(options: WithBackmatterOptions = {}): Backm
80
82
  outDir: options.outDir ?? DEFAULT_OUT_DIR,
81
83
  modelsDir: options.modelsDir ?? DEFAULT_DIRS.models,
82
84
  blocksDir: options.blocksDir ?? DEFAULT_DIRS.blocks,
85
+ richtextDir: options.richtextDir ?? DEFAULT_DIRS.richtext,
83
86
  mdxDir: options.mdxDir ?? DEFAULT_DIRS.mdx,
84
87
  layoutsDir: options.layoutsDir ?? DEFAULT_DIRS.layouts,
88
+ objectsDir: options.objectsDir ?? DEFAULT_DIRS.objects,
85
89
  sdk: SDK,
86
90
  };
87
91
  }
@@ -104,7 +108,7 @@ export function registryMatchers(loaderOptions: BackmatterLoaderOptions): { glob
104
108
  const escaped = rel.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
105
109
  return {
106
110
  glob: `**/${rel}/*.ts`,
107
- test: new RegExp(`(^|[\\\\/])${escaped.replace(/\//g, "[\\\\/]")}[\\\\/](index|blocks|block-fallbacks|mdx|layouts|sync)\\.ts$`),
111
+ test: new RegExp(`(^|[\\\\/])${escaped.replace(/\//g, "[\\\\/]")}[\\\\/](index|blocks|block-fallbacks|mdx|richtext|layouts|sync)\\.ts$`),
108
112
  };
109
113
  }
110
114
 
package/src/loader.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  // packages/next/src/loader.ts
2
- // Bundler loader for `.backmatter/index.ts`, `.backmatter/blocks.ts`, `.backmatter/mdx.ts`,
3
- // and `.backmatter/layouts.ts`.
2
+ // Bundler loader for `.backmatter/index.ts`, `.backmatter/blocks.ts`,
3
+ // `.backmatter/mdx.ts`, `.backmatter/richtext.ts`, and `.backmatter/layouts.ts`.
4
4
  //
5
5
  // This is the Tailwind-JIT mechanism: instead of running our own watcher we
6
6
  // tell the bundler that these modules depend on the `models/`, `blocks/`,
7
- // `mdx/`, and `layouts/` directories. webpack and Turbopack already watch the filesystem;
7
+ // `richtext/`, `mdx/`, and `layouts/` directories. webpack and Turbopack already watch the filesystem;
8
8
  // when a file is added, removed or renamed there they invalidate the module,
9
9
  // re-run this loader, and we regenerate the barrel from a fresh `readdir`.
10
10
  import path from "node:path";
@@ -15,8 +15,10 @@ export type BackmatterLoaderOptions = {
15
15
  outDir: string;
16
16
  modelsDir: string;
17
17
  blocksDir: string;
18
+ richtextDir: string;
18
19
  mdxDir: string;
19
20
  layoutsDir: string;
21
+ objectsDir: string;
20
22
  sdk: string;
21
23
  };
22
24
 
@@ -34,13 +36,17 @@ export default function backmatterLoader(this: BackmatterLoaderContext, source:
34
36
  const root = path.resolve(options.root);
35
37
  const modelsDir = path.resolve(root, options.modelsDir);
36
38
  const blocksDir = path.resolve(root, options.blocksDir);
39
+ const richtextDir = path.resolve(root, options.richtextDir);
37
40
  const mdxDir = path.resolve(root, options.mdxDir);
38
41
  const layoutsDir = path.resolve(root, options.layoutsDir);
42
+ const objectsDir = path.resolve(root, options.objectsDir);
39
43
 
40
44
  this.addContextDependency(modelsDir);
41
45
  this.addContextDependency(blocksDir);
46
+ this.addContextDependency(richtextDir);
42
47
  this.addContextDependency(mdxDir);
43
48
  this.addContextDependency(layoutsDir);
49
+ this.addContextDependency(objectsDir);
44
50
  this.cacheable?.(true);
45
51
 
46
52
  const result = generate({
@@ -48,8 +54,10 @@ export default function backmatterLoader(this: BackmatterLoaderContext, source:
48
54
  outDir: options.outDir,
49
55
  modelsDir: options.modelsDir,
50
56
  blocksDir: options.blocksDir,
57
+ richtextDir: options.richtextDir,
51
58
  mdxDir: options.mdxDir,
52
59
  layoutsDir: options.layoutsDir,
60
+ objectsDir: options.objectsDir,
53
61
  sdk: options.sdk,
54
62
  });
55
63
  if (result.configFile) this.addDependency(result.configFile);
package/src/routes.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  // `route` template (including `/` via indexSlug), loads it and renders MDX
5
5
  // or JSON pages. An optional catch-all is required so `/` is a CMS route.
6
6
  import { Fragment, Suspense, createElement, type ReactNode } from "react";
7
- import { matchRoute, modelFormat, PREVIEW_CHECKOUT_QUERY, PREVIEW_COOKIE, readPreviewCookie, routeSegments, type BackmatterDoc, type ModelDef } from "@backmatter/core";
7
+ import { isRoutable, matchRoute, modelFormat, PREVIEW_CHECKOUT_QUERY, PREVIEW_COOKIE, readPreviewCookie, routeSegments, type BackmatterDoc, type ModelDef } from "@backmatter/core";
8
8
  import type { BackmatterClient } from "@backmatter/core/node/create-backmatter";
9
9
  import { fetchPreviewDoc } from "@backmatter/core/preview";
10
10
  import { renderDoc, type MdxComponents } from "./mdx-compile";
@@ -63,7 +63,7 @@ export type CatchAllRoute = {
63
63
  Page: (props: {
64
64
  params: Promise<CatchAllParams>;
65
65
  searchParams?: Promise<CatchAllSearchParams>;
66
- }) => Promise<ReactNode>;
66
+ }) => ReactNode;
67
67
  CmsRoute: (props: {
68
68
  params: Promise<CatchAllParams> | CatchAllParams;
69
69
  searchParams?: Promise<CatchAllSearchParams> | CatchAllSearchParams;
@@ -162,7 +162,7 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
162
162
  const params: CatchAllParams[] = [];
163
163
  for (const doc of docs) {
164
164
  const model = byType.get(doc.type);
165
- if (!model) continue;
165
+ if (!model || !isRoutable(model)) continue;
166
166
  params.push({ [CATCH_ALL_PARAM]: routeSegments(model, doc.locale, doc.slug) });
167
167
  }
168
168
  return params;
@@ -248,7 +248,7 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
248
248
  params: Promise<CatchAllParams> | CatchAllParams;
249
249
  searchParams?: Promise<CatchAllSearchParams> | CatchAllSearchParams;
250
250
  }) => {
251
- const resolved = await cachedRouteParams(params);
251
+ const resolved = await params;
252
252
  const hit = resolve(resolved[CATCH_ALL_PARAM]);
253
253
  if (!hit) return failFor();
254
254
  const doc = await cms.getDoc(hit.locale, hit.type, hit.slug);
@@ -267,7 +267,7 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
267
267
  params: Promise<CatchAllParams> | CatchAllParams;
268
268
  searchParams?: Promise<CatchAllSearchParams> | CatchAllSearchParams;
269
269
  }) => {
270
- const resolved = await cachedRouteParams(params);
270
+ const resolved = await params;
271
271
  const hit = resolve(resolved[CATCH_ALL_PARAM]);
272
272
  if (!hit) return null;
273
273
  const preview = await resolvePreviewState(cms, options.preview, searchParams);
@@ -284,11 +284,15 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
284
284
  );
285
285
  };
286
286
 
287
- const Page: CatchAllRoute["Page"] = async (props) =>
287
+ const Page: CatchAllRoute["Page"] = (props) =>
288
288
  createElement(
289
289
  Fragment,
290
290
  null,
291
- await PublishedRoute(props),
291
+ createElement(
292
+ Suspense,
293
+ { fallback: createElement("div", { "aria-busy": "true", style: { minHeight: "100vh" } }) },
294
+ createElement(PublishedRoute, props),
295
+ ),
292
296
  createElement(Suspense, { fallback: null }, createElement(PreviewBranch, props)),
293
297
  );
294
298