@backmatter/next 0.0.11 → 0.0.13
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 +42 -2
- package/dist/loader.cjs +38 -2
- package/package.json +2 -2
- package/src/cached-doc.ts +40 -3
- package/src/config.ts +6 -0
- package/src/create-backmatter.ts +16 -2
- package/src/devbar-client.tsx +20 -11
- package/src/devbar.tsx +1 -0
- package/src/preview-route.ts +16 -5
- package/src/routes.ts +23 -12
package/dist/config.cjs
CHANGED
|
@@ -180,8 +180,40 @@ function detectSdk(root) {
|
|
|
180
180
|
} catch {}
|
|
181
181
|
return "@backmatter/core/node";
|
|
182
182
|
}
|
|
183
|
+
function renderCachedCmsClient() {
|
|
184
|
+
return [
|
|
185
|
+
"const raw = createBackmatter({ ...config, models });",
|
|
186
|
+
"",
|
|
187
|
+
"async function getDoc(locale: string, type: string, slug: string) {",
|
|
188
|
+
' "use cache";',
|
|
189
|
+
' cacheLife("hours");',
|
|
190
|
+
" cacheTag(...contentCacheTags(locale, type, slug));",
|
|
191
|
+
" return raw.getDoc(locale, type, slug);",
|
|
192
|
+
"}",
|
|
193
|
+
"",
|
|
194
|
+
"async function listDocs(filter?: { type?: string; locale?: string }) {",
|
|
195
|
+
' "use cache";',
|
|
196
|
+
' cacheLife("hours");',
|
|
197
|
+
" cacheTag(...listCacheTags(filter?.type));",
|
|
198
|
+
" return raw.listDocs(filter);",
|
|
199
|
+
"}",
|
|
200
|
+
"",
|
|
201
|
+
"/** Typed content client: cms.query.{type}, cms.getDoc, cms.listDocs. */",
|
|
202
|
+
"export const cms = {",
|
|
203
|
+
" ...raw,",
|
|
204
|
+
" getDoc,",
|
|
205
|
+
" listDocs,",
|
|
206
|
+
` query: createQuery(models, { getDoc, listDocs }),`,
|
|
207
|
+
"};"
|
|
208
|
+
];
|
|
209
|
+
}
|
|
183
210
|
function renderModelsBarrel(input) {
|
|
211
|
+
const nextHost = input.sdk === "@backmatter/next";
|
|
184
212
|
const lines = [GENERATED_HEADER, `import { createBackmatter } from "${input.sdk}";`];
|
|
213
|
+
if (nextHost) {
|
|
214
|
+
lines.push('import { contentCacheTags, createQuery, listCacheTags } from "@backmatter/core";');
|
|
215
|
+
lines.push('import { cacheLife, cacheTag } from "next/cache";');
|
|
216
|
+
}
|
|
185
217
|
if (input.configFile) {
|
|
186
218
|
lines.push(`import config from "${importSpecifier(input.outDir, input.configFile)}";`);
|
|
187
219
|
} else {
|
|
@@ -194,8 +226,12 @@ function renderModelsBarrel(input) {
|
|
|
194
226
|
lines.push(`export const models = [${input.models.map((m) => m.importName).join(", ")}] as const;`);
|
|
195
227
|
lines.push("export type Models = typeof models;");
|
|
196
228
|
lines.push("");
|
|
197
|
-
|
|
198
|
-
|
|
229
|
+
if (nextHost) {
|
|
230
|
+
lines.push(...renderCachedCmsClient());
|
|
231
|
+
} else {
|
|
232
|
+
lines.push("/** Typed content client: cms.query.{type}, cms.getDoc, cms.listDocs. */");
|
|
233
|
+
lines.push("export const cms = createBackmatter({ ...config, models });");
|
|
234
|
+
}
|
|
199
235
|
return `${lines.join(`
|
|
200
236
|
`)}
|
|
201
237
|
`;
|
|
@@ -395,10 +431,14 @@ function withBackmatter(nextConfig = {}, options = {}) {
|
|
|
395
431
|
return cfg.webpack ? cfg.webpack(config, context) : config;
|
|
396
432
|
};
|
|
397
433
|
const cacheComponents = cfg.cacheComponents ?? true;
|
|
434
|
+
const transpilePackages = [
|
|
435
|
+
...new Set([...cfg.transpilePackages ?? [], "@backmatter/next", "@backmatter/core"])
|
|
436
|
+
];
|
|
398
437
|
return {
|
|
399
438
|
...nextConfig,
|
|
400
439
|
cacheComponents,
|
|
401
440
|
partialPrefetching: cfg.partialPrefetching ?? cacheComponents,
|
|
441
|
+
transpilePackages,
|
|
402
442
|
turbopack,
|
|
403
443
|
webpack
|
|
404
444
|
};
|
package/dist/loader.cjs
CHANGED
|
@@ -174,8 +174,40 @@ function detectSdk(root) {
|
|
|
174
174
|
} catch {}
|
|
175
175
|
return "@backmatter/core/node";
|
|
176
176
|
}
|
|
177
|
+
function renderCachedCmsClient() {
|
|
178
|
+
return [
|
|
179
|
+
"const raw = createBackmatter({ ...config, models });",
|
|
180
|
+
"",
|
|
181
|
+
"async function getDoc(locale: string, type: string, slug: string) {",
|
|
182
|
+
' "use cache";',
|
|
183
|
+
' cacheLife("hours");',
|
|
184
|
+
" cacheTag(...contentCacheTags(locale, type, slug));",
|
|
185
|
+
" return raw.getDoc(locale, type, slug);",
|
|
186
|
+
"}",
|
|
187
|
+
"",
|
|
188
|
+
"async function listDocs(filter?: { type?: string; locale?: string }) {",
|
|
189
|
+
' "use cache";',
|
|
190
|
+
' cacheLife("hours");',
|
|
191
|
+
" cacheTag(...listCacheTags(filter?.type));",
|
|
192
|
+
" return raw.listDocs(filter);",
|
|
193
|
+
"}",
|
|
194
|
+
"",
|
|
195
|
+
"/** Typed content client: cms.query.{type}, cms.getDoc, cms.listDocs. */",
|
|
196
|
+
"export const cms = {",
|
|
197
|
+
" ...raw,",
|
|
198
|
+
" getDoc,",
|
|
199
|
+
" listDocs,",
|
|
200
|
+
` query: createQuery(models, { getDoc, listDocs }),`,
|
|
201
|
+
"};"
|
|
202
|
+
];
|
|
203
|
+
}
|
|
177
204
|
function renderModelsBarrel(input) {
|
|
205
|
+
const nextHost = input.sdk === "@backmatter/next";
|
|
178
206
|
const lines = [GENERATED_HEADER, `import { createBackmatter } from "${input.sdk}";`];
|
|
207
|
+
if (nextHost) {
|
|
208
|
+
lines.push('import { contentCacheTags, createQuery, listCacheTags } from "@backmatter/core";');
|
|
209
|
+
lines.push('import { cacheLife, cacheTag } from "next/cache";');
|
|
210
|
+
}
|
|
179
211
|
if (input.configFile) {
|
|
180
212
|
lines.push(`import config from "${importSpecifier(input.outDir, input.configFile)}";`);
|
|
181
213
|
} else {
|
|
@@ -188,8 +220,12 @@ function renderModelsBarrel(input) {
|
|
|
188
220
|
lines.push(`export const models = [${input.models.map((m) => m.importName).join(", ")}] as const;`);
|
|
189
221
|
lines.push("export type Models = typeof models;");
|
|
190
222
|
lines.push("");
|
|
191
|
-
|
|
192
|
-
|
|
223
|
+
if (nextHost) {
|
|
224
|
+
lines.push(...renderCachedCmsClient());
|
|
225
|
+
} else {
|
|
226
|
+
lines.push("/** Typed content client: cms.query.{type}, cms.getDoc, cms.listDocs. */");
|
|
227
|
+
lines.push("export const cms = createBackmatter({ ...config, models });");
|
|
228
|
+
}
|
|
193
229
|
return `${lines.join(`
|
|
194
230
|
`)}
|
|
195
231
|
`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backmatter/next",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
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.
|
|
41
|
+
"@backmatter/core": "^0.0.12",
|
|
42
42
|
"@mdx-js/mdx": "^3.1.1",
|
|
43
43
|
"remark-gfm": "^4.0.1"
|
|
44
44
|
},
|
package/src/cached-doc.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// packages/next/src/cached-doc.ts
|
|
2
|
-
import { contentCacheTags, type BackmatterDoc } from "@backmatter/core";
|
|
2
|
+
import { contentCacheTags, listCacheTags, type BackmatterDoc, type BackmatterDocSummary } from "@backmatter/core";
|
|
3
3
|
|
|
4
4
|
export type CacheFns = {
|
|
5
5
|
cacheLife: (profile: string) => void;
|
|
@@ -12,13 +12,23 @@ export type GetDocFn = (
|
|
|
12
12
|
slug: string,
|
|
13
13
|
) => Promise<BackmatterDoc | null>;
|
|
14
14
|
|
|
15
|
+
export type ListDocsFn = (filter?: {
|
|
16
|
+
type?: string;
|
|
17
|
+
locale?: string;
|
|
18
|
+
}) => Promise<BackmatterDocSummary[]>;
|
|
19
|
+
|
|
15
20
|
let registeredGetDoc: GetDocFn | null = null;
|
|
21
|
+
let registeredListDocs: ListDocsFn | null = null;
|
|
16
22
|
|
|
17
|
-
/** Called from `
|
|
23
|
+
/** Called from `createBackmatter` so `"use cache"` only closes over serializable args. */
|
|
18
24
|
export function registerCachedDocGet(getDoc: GetDocFn): void {
|
|
19
25
|
registeredGetDoc = getDoc;
|
|
20
26
|
}
|
|
21
27
|
|
|
28
|
+
export function registerCachedListDocs(listDocs: ListDocsFn): void {
|
|
29
|
+
registeredListDocs = listDocs;
|
|
30
|
+
}
|
|
31
|
+
|
|
22
32
|
async function loadCacheFns(): Promise<CacheFns> {
|
|
23
33
|
try {
|
|
24
34
|
const mod = (await import("next/cache" as string)) as CacheFns;
|
|
@@ -49,7 +59,34 @@ export async function getCachedDoc(
|
|
|
49
59
|
fns.cacheTag(...contentCacheTags(locale, type, slug));
|
|
50
60
|
const getDoc = registeredGetDoc;
|
|
51
61
|
if (!getDoc) {
|
|
52
|
-
throw new Error("[backmatter] getCachedDoc called before
|
|
62
|
+
throw new Error("[backmatter] getCachedDoc called before createBackmatter registered getDoc");
|
|
53
63
|
}
|
|
54
64
|
return getDoc(locale, type, slug);
|
|
55
65
|
}
|
|
66
|
+
|
|
67
|
+
export async function getCachedDocs(
|
|
68
|
+
type?: string,
|
|
69
|
+
locale?: string,
|
|
70
|
+
cache?: CacheFns,
|
|
71
|
+
): Promise<BackmatterDocSummary[]> {
|
|
72
|
+
"use cache";
|
|
73
|
+
const fns = cache ?? (await loadCacheFns());
|
|
74
|
+
fns.cacheLife("hours");
|
|
75
|
+
fns.cacheTag(...listCacheTags(type));
|
|
76
|
+
const listDocs = registeredListDocs;
|
|
77
|
+
if (!listDocs) {
|
|
78
|
+
throw new Error("[backmatter] getCachedDocs called before createBackmatter registered listDocs");
|
|
79
|
+
}
|
|
80
|
+
return listDocs({ type, locale });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Unwrap Next `params` inside `"use cache"` so Cache Components can prerender. */
|
|
84
|
+
export async function cachedRouteParams<T extends object>(
|
|
85
|
+
params: Promise<T> | T,
|
|
86
|
+
cache?: CacheFns,
|
|
87
|
+
): Promise<T> {
|
|
88
|
+
"use cache";
|
|
89
|
+
const fns = cache ?? (await loadCacheFns());
|
|
90
|
+
fns.cacheLife("hours");
|
|
91
|
+
return await params;
|
|
92
|
+
}
|
package/src/config.ts
CHANGED
|
@@ -43,6 +43,7 @@ type TurbopackRule = { loaders: Array<{ loader: string; options: Record<string,
|
|
|
43
43
|
export type NextConfigLike = {
|
|
44
44
|
cacheComponents?: boolean;
|
|
45
45
|
partialPrefetching?: boolean | "unstable_eager";
|
|
46
|
+
transpilePackages?: string[];
|
|
46
47
|
turbopack?: Record<string, unknown>;
|
|
47
48
|
webpack?: ((config: WebpackConfigLike, context: any) => WebpackConfigLike) | null;
|
|
48
49
|
} & Record<string, unknown>;
|
|
@@ -119,6 +120,7 @@ export function registryMatchers(loaderOptions: BackmatterLoaderOptions): { glob
|
|
|
119
120
|
export type WithBackmatterResult<T extends object> = Omit<T, "turbopack" | "webpack"> & {
|
|
120
121
|
cacheComponents: boolean;
|
|
121
122
|
partialPrefetching: boolean | "unstable_eager";
|
|
123
|
+
transpilePackages: string[];
|
|
122
124
|
turbopack: Record<string, unknown>;
|
|
123
125
|
webpack: NonNullable<NextConfigLike["webpack"]>;
|
|
124
126
|
};
|
|
@@ -150,10 +152,14 @@ export function withBackmatter<T extends object>(
|
|
|
150
152
|
};
|
|
151
153
|
|
|
152
154
|
const cacheComponents = cfg.cacheComponents ?? true;
|
|
155
|
+
const transpilePackages = [
|
|
156
|
+
...new Set([...(cfg.transpilePackages ?? []), "@backmatter/next", "@backmatter/core"]),
|
|
157
|
+
];
|
|
153
158
|
return {
|
|
154
159
|
...nextConfig,
|
|
155
160
|
cacheComponents,
|
|
156
161
|
partialPrefetching: cfg.partialPrefetching ?? cacheComponents,
|
|
162
|
+
transpilePackages,
|
|
157
163
|
turbopack,
|
|
158
164
|
webpack,
|
|
159
165
|
};
|
package/src/create-backmatter.ts
CHANGED
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
// packages/next/src/create-backmatter.ts
|
|
2
2
|
// Next hosts cache API fetches with tags so `content.committed` can
|
|
3
3
|
// `revalidateTag("backmatter")`. Core/CLI keep `cache: "no-store"`.
|
|
4
|
+
// getDoc/listDocs also go through `"use cache"` so Cache Components can prerender.
|
|
5
|
+
import { createQuery, type ModelDef } from "@backmatter/core";
|
|
4
6
|
import {
|
|
5
7
|
createBackmatter as createCore,
|
|
6
8
|
type CreateBackmatterOptions,
|
|
7
9
|
type BackmatterClient,
|
|
8
10
|
} from "@backmatter/core/node/create-backmatter";
|
|
9
|
-
import
|
|
11
|
+
import { getCachedDoc, getCachedDocs, registerCachedDocGet, registerCachedListDocs } from "./cached-doc";
|
|
10
12
|
|
|
11
13
|
export function createBackmatter<const Models extends readonly ModelDef[]>(
|
|
12
14
|
options: CreateBackmatterOptions<Models>,
|
|
13
15
|
): BackmatterClient<Models> {
|
|
14
|
-
|
|
16
|
+
const core = createCore({ ...options, taggedApiFetch: true });
|
|
17
|
+
registerCachedDocGet(core.getDoc);
|
|
18
|
+
registerCachedListDocs(core.listDocs);
|
|
19
|
+
const getDoc: BackmatterClient<Models>["getDoc"] = (locale, type, slug) =>
|
|
20
|
+
getCachedDoc(locale, type, slug);
|
|
21
|
+
const listDocs: BackmatterClient<Models>["listDocs"] = (filter) =>
|
|
22
|
+
getCachedDocs(filter?.type, filter?.locale);
|
|
23
|
+
return {
|
|
24
|
+
...core,
|
|
25
|
+
getDoc,
|
|
26
|
+
listDocs,
|
|
27
|
+
query: createQuery(options.models, { getDoc, listDocs }),
|
|
28
|
+
};
|
|
15
29
|
}
|
package/src/devbar-client.tsx
CHANGED
|
@@ -16,7 +16,7 @@ export type DevModeState =
|
|
|
16
16
|
| { status: "hidden" }
|
|
17
17
|
| {
|
|
18
18
|
status: "ready";
|
|
19
|
-
user
|
|
19
|
+
user?: CmsSessionUser;
|
|
20
20
|
mode: "preview" | "live";
|
|
21
21
|
branch?: string;
|
|
22
22
|
tree?: string;
|
|
@@ -26,28 +26,37 @@ export function devModeState(
|
|
|
26
26
|
user: CmsSessionUser | null,
|
|
27
27
|
session: { enabled: boolean; branch?: string; tree?: string },
|
|
28
28
|
): DevModeState {
|
|
29
|
-
if (!user) return { status: "hidden" };
|
|
29
|
+
if (!user && !session.enabled) return { status: "hidden" };
|
|
30
30
|
const resolved = resolveDevMode(session);
|
|
31
31
|
return {
|
|
32
32
|
status: "ready",
|
|
33
|
-
user,
|
|
33
|
+
...(user ? { user } : {}),
|
|
34
34
|
...resolved,
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
export async function loadDevModeState(
|
|
39
|
+
apiUrl: string,
|
|
40
|
+
fetchUser: typeof fetchCmsSession = fetchCmsSession,
|
|
41
|
+
fetchSession: typeof fetchHostPreviewSession = fetchHostPreviewSession,
|
|
42
|
+
): Promise<DevModeState> {
|
|
43
|
+
const trimmed = apiUrl.trim();
|
|
44
|
+
const [user, session] = await Promise.all([
|
|
45
|
+
trimmed ? fetchUser(trimmed) : Promise.resolve(null),
|
|
46
|
+
fetchSession(""),
|
|
47
|
+
]);
|
|
48
|
+
return devModeState(user, session);
|
|
49
|
+
}
|
|
50
|
+
|
|
38
51
|
export function useDevMode(options: { apiUrl: string }): DevModeState {
|
|
39
52
|
const [state, setState] = useState<DevModeState>({ status: "hidden" });
|
|
40
53
|
|
|
41
54
|
useEffect(() => {
|
|
42
|
-
const apiUrl = options.apiUrl.trim();
|
|
43
|
-
if (!apiUrl) return;
|
|
44
55
|
let cancelled = false;
|
|
45
|
-
void
|
|
46
|
-
(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
},
|
|
50
|
-
);
|
|
56
|
+
void loadDevModeState(options.apiUrl).then((next) => {
|
|
57
|
+
if (cancelled) return;
|
|
58
|
+
setState(next);
|
|
59
|
+
});
|
|
51
60
|
return () => {
|
|
52
61
|
cancelled = true;
|
|
53
62
|
};
|
package/src/devbar.tsx
CHANGED
package/src/preview-route.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type { BackmatterClient } from "@backmatter/core/node/create-backmatter";
|
|
|
15
15
|
export type PreviewRouteDeps = {
|
|
16
16
|
enableDraft?: () => void | Promise<void>;
|
|
17
17
|
disableDraft?: () => void | Promise<void>;
|
|
18
|
+
isDraftEnabled?: () => boolean | Promise<boolean>;
|
|
18
19
|
now?: () => number;
|
|
19
20
|
};
|
|
20
21
|
|
|
@@ -45,6 +46,13 @@ async function nextDraft(enable: boolean): Promise<void> {
|
|
|
45
46
|
else draft.disable();
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
async function nextDraftEnabled(): Promise<boolean> {
|
|
50
|
+
const mod = (await import("next/headers" as string)) as {
|
|
51
|
+
draftMode: () => Promise<{ isEnabled: boolean }>;
|
|
52
|
+
};
|
|
53
|
+
return (await mod.draftMode()).isEnabled;
|
|
54
|
+
}
|
|
55
|
+
|
|
48
56
|
function cookieFrom(req: Request): string | undefined {
|
|
49
57
|
const header = req.headers.get("cookie");
|
|
50
58
|
if (!header) return undefined;
|
|
@@ -75,11 +83,14 @@ export function createPreviewRoute(
|
|
|
75
83
|
|
|
76
84
|
if (url.searchParams.get("session") === "1") {
|
|
77
85
|
const checkout = secret ? await checkoutFrom(req, secret, url) : null;
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
86
|
+
if (checkout) {
|
|
87
|
+
return Response.json({
|
|
88
|
+
mode: "preview",
|
|
89
|
+
...checkout,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
const draftOn = await (deps.isDraftEnabled ?? nextDraftEnabled)();
|
|
93
|
+
return Response.json({ mode: draftOn ? "preview" : "live" });
|
|
83
94
|
}
|
|
84
95
|
|
|
85
96
|
if (url.searchParams.get("stream") === "1") {
|
package/src/routes.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { matchRoute, modelFormat, PREVIEW_CHECKOUT_QUERY, PREVIEW_COOKIE, readPr
|
|
|
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";
|
|
11
|
-
import {
|
|
11
|
+
import { cachedRouteParams } from "./cached-doc";
|
|
12
12
|
import { renderPage, type BlockComponents, type BlockFallbacks } from "./page-render";
|
|
13
13
|
import { previewUnavailable } from "./preview-ui";
|
|
14
14
|
|
|
@@ -44,6 +44,7 @@ export type CatchAllOptions = {
|
|
|
44
44
|
preview?: {
|
|
45
45
|
enabled: boolean;
|
|
46
46
|
checkout?: { branch?: string; tree?: string };
|
|
47
|
+
error?: string;
|
|
47
48
|
getDoc?: (ref: {
|
|
48
49
|
locale: string;
|
|
49
50
|
type: string;
|
|
@@ -87,6 +88,13 @@ type PreviewState =
|
|
|
87
88
|
| { enabled: false }
|
|
88
89
|
| { enabled: true; checkout: { branch?: string; tree?: string }; error?: string };
|
|
89
90
|
|
|
91
|
+
/** Draft without a readable checkout stays on the published page. */
|
|
92
|
+
export function previewReplacesPublished(
|
|
93
|
+
preview: PreviewState,
|
|
94
|
+
): preview is Extract<PreviewState, { enabled: true }> {
|
|
95
|
+
return preview.enabled && preview.error !== "invalid_session";
|
|
96
|
+
}
|
|
97
|
+
|
|
90
98
|
type RouteHit = { locale: string; type: string; slug: string };
|
|
91
99
|
|
|
92
100
|
async function checkoutQueryToken(
|
|
@@ -107,7 +115,11 @@ async function resolvePreviewState(
|
|
|
107
115
|
): Promise<PreviewState> {
|
|
108
116
|
if (injected) {
|
|
109
117
|
if (!injected.enabled) return { enabled: false };
|
|
110
|
-
return {
|
|
118
|
+
return {
|
|
119
|
+
enabled: true,
|
|
120
|
+
checkout: injected.checkout ?? {},
|
|
121
|
+
...(injected.error ? { error: injected.error } : {}),
|
|
122
|
+
};
|
|
111
123
|
}
|
|
112
124
|
try {
|
|
113
125
|
const mod = (await import("next/headers" as string)) as {
|
|
@@ -133,7 +145,6 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
|
|
|
133
145
|
cms: BackmatterClient<Models>,
|
|
134
146
|
options: CatchAllOptions = {},
|
|
135
147
|
): CatchAllRoute {
|
|
136
|
-
registerCachedDocGet((locale, type, slug) => cms.getDoc(locale, type, slug));
|
|
137
148
|
const models = cms.models as readonly ModelDef[];
|
|
138
149
|
const byType = new Map(models.map((m) => [m.type, m]));
|
|
139
150
|
const matchOptions = {
|
|
@@ -221,12 +232,12 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
|
|
|
221
232
|
}
|
|
222
233
|
|
|
223
234
|
const CmsRoute: CatchAllRoute["CmsRoute"] = async ({ params, searchParams }) => {
|
|
224
|
-
const resolved = await params;
|
|
235
|
+
const resolved = await cachedRouteParams(params);
|
|
225
236
|
const hit = resolve(resolved[CATCH_ALL_PARAM]);
|
|
226
237
|
if (!hit) return failFor();
|
|
227
238
|
const preview = await resolvePreviewState(cms, options.preview, searchParams);
|
|
228
|
-
if (preview
|
|
229
|
-
const doc = await
|
|
239
|
+
if (previewReplacesPublished(preview)) return loadPreviewDoc(hit, preview);
|
|
240
|
+
const doc = await cms.getDoc(hit.locale, hit.type, hit.slug);
|
|
230
241
|
if (!doc) return failFor();
|
|
231
242
|
return layoutDoc(doc, false);
|
|
232
243
|
};
|
|
@@ -237,10 +248,10 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
|
|
|
237
248
|
params: Promise<CatchAllParams> | CatchAllParams;
|
|
238
249
|
searchParams?: Promise<CatchAllSearchParams> | CatchAllSearchParams;
|
|
239
250
|
}) => {
|
|
240
|
-
const resolved = await params;
|
|
251
|
+
const resolved = await cachedRouteParams(params);
|
|
241
252
|
const hit = resolve(resolved[CATCH_ALL_PARAM]);
|
|
242
253
|
if (!hit) return failFor();
|
|
243
|
-
const doc = await
|
|
254
|
+
const doc = await cms.getDoc(hit.locale, hit.type, hit.slug);
|
|
244
255
|
if (!doc) return failFor();
|
|
245
256
|
return createElement(
|
|
246
257
|
"div",
|
|
@@ -256,11 +267,11 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
|
|
|
256
267
|
params: Promise<CatchAllParams> | CatchAllParams;
|
|
257
268
|
searchParams?: Promise<CatchAllSearchParams> | CatchAllSearchParams;
|
|
258
269
|
}) => {
|
|
259
|
-
const resolved = await params;
|
|
270
|
+
const resolved = await cachedRouteParams(params);
|
|
260
271
|
const hit = resolve(resolved[CATCH_ALL_PARAM]);
|
|
261
272
|
if (!hit) return null;
|
|
262
273
|
const preview = await resolvePreviewState(cms, options.preview, searchParams);
|
|
263
|
-
if (!preview
|
|
274
|
+
if (!previewReplacesPublished(preview)) return null;
|
|
264
275
|
return createElement(
|
|
265
276
|
Fragment,
|
|
266
277
|
null,
|
|
@@ -282,10 +293,10 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
|
|
|
282
293
|
);
|
|
283
294
|
|
|
284
295
|
const generateMetadata: CatchAllRoute["generateMetadata"] = async ({ params }) => {
|
|
285
|
-
const resolved = await params;
|
|
296
|
+
const resolved = await cachedRouteParams(params);
|
|
286
297
|
const hit = resolve(resolved[CATCH_ALL_PARAM]);
|
|
287
298
|
if (!hit) return {};
|
|
288
|
-
const doc = await
|
|
299
|
+
const doc = await cms.getDoc(hit.locale, hit.type, hit.slug);
|
|
289
300
|
if (!doc) return {};
|
|
290
301
|
const title = doc.frontmatter.title;
|
|
291
302
|
return { title: typeof title === "string" ? title : hit.slug };
|