@backmatter/next 0.0.36 → 0.0.37

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
@@ -194,27 +194,27 @@ function renderCachedCmsClient() {
194
194
  return [
195
195
  "const raw = createBackmatter({ ...config, models, sync: false });",
196
196
  "",
197
- "async function cachedGetDoc(locale: string, type: string, slug: string) {",
197
+ "async function cachedGetDoc(type: string, slug: string) {",
198
198
  ' "use cache";',
199
199
  ' cacheLife("hours");',
200
- " cacheTag(...contentCacheTags(locale, type, slug));",
201
- " return raw.getDoc(locale, type, slug);",
200
+ " cacheTag(...contentCacheTags(type, slug));",
201
+ " return raw.getDoc(type, slug);",
202
202
  "}",
203
203
  "",
204
- "async function cachedListDocs(filter?: { type?: string; locale?: string }) {",
204
+ "async function cachedListDocs(filter?: { type?: string }) {",
205
205
  ' "use cache";',
206
206
  ' cacheLife("hours");',
207
207
  " cacheTag(...listCacheTags(filter?.type));",
208
208
  " return raw.listDocs(filter);",
209
209
  "}",
210
210
  "",
211
- "async function getDoc(locale: string, type: string, slug: string) {",
211
+ "async function getDoc(type: string, slug: string) {",
212
212
  " const preview = currentPreviewContent();",
213
- " if (preview) return preview.getDoc(locale, type, slug);",
214
- " return cachedGetDoc(locale, type, slug);",
213
+ " if (preview) return preview.getDoc(type, slug);",
214
+ " return cachedGetDoc(type, slug);",
215
215
  "}",
216
216
  "",
217
- "async function listDocs(filter?: { type?: string; locale?: string }) {",
217
+ "async function listDocs(filter?: { type?: string }) {",
218
218
  " const preview = currentPreviewContent();",
219
219
  " if (preview) return preview.listDocs(filter);",
220
220
  " return cachedListDocs(filter);",
package/dist/loader.cjs CHANGED
@@ -187,27 +187,27 @@ function renderCachedCmsClient() {
187
187
  return [
188
188
  "const raw = createBackmatter({ ...config, models, sync: false });",
189
189
  "",
190
- "async function cachedGetDoc(locale: string, type: string, slug: string) {",
190
+ "async function cachedGetDoc(type: string, slug: string) {",
191
191
  ' "use cache";',
192
192
  ' cacheLife("hours");',
193
- " cacheTag(...contentCacheTags(locale, type, slug));",
194
- " return raw.getDoc(locale, type, slug);",
193
+ " cacheTag(...contentCacheTags(type, slug));",
194
+ " return raw.getDoc(type, slug);",
195
195
  "}",
196
196
  "",
197
- "async function cachedListDocs(filter?: { type?: string; locale?: string }) {",
197
+ "async function cachedListDocs(filter?: { type?: string }) {",
198
198
  ' "use cache";',
199
199
  ' cacheLife("hours");',
200
200
  " cacheTag(...listCacheTags(filter?.type));",
201
201
  " return raw.listDocs(filter);",
202
202
  "}",
203
203
  "",
204
- "async function getDoc(locale: string, type: string, slug: string) {",
204
+ "async function getDoc(type: string, slug: string) {",
205
205
  " const preview = currentPreviewContent();",
206
- " if (preview) return preview.getDoc(locale, type, slug);",
207
- " return cachedGetDoc(locale, type, slug);",
206
+ " if (preview) return preview.getDoc(type, slug);",
207
+ " return cachedGetDoc(type, slug);",
208
208
  "}",
209
209
  "",
210
- "async function listDocs(filter?: { type?: string; locale?: string }) {",
210
+ "async function listDocs(filter?: { type?: string }) {",
211
211
  " const preview = currentPreviewContent();",
212
212
  " if (preview) return preview.listDocs(filter);",
213
213
  " return cachedListDocs(filter);",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backmatter/next",
3
- "version": "0.0.36",
3
+ "version": "0.0.37",
4
4
  "description": "Experimental Next.js adapter for Backmatter. APIs will break. Not ready for production.",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -47,7 +47,7 @@
47
47
  "test": "bun test"
48
48
  },
49
49
  "dependencies": {
50
- "@backmatter/core": "^0.0.27",
50
+ "@backmatter/core": "^0.0.28",
51
51
  "@mdx-js/mdx": "^3.1.1",
52
52
  "remark-gfm": "^4.0.1",
53
53
  "@backmatter/studio": "^0.0.13"
package/src/cached-doc.ts CHANGED
@@ -7,22 +7,11 @@ export type CacheFns = {
7
7
  cacheTag: (...tags: string[]) => void;
8
8
  };
9
9
 
10
- export type GetDocFn = (
11
- locale: string,
12
- type: string,
13
- slug: string,
14
- ) => Promise<BackmatterDoc | null>;
10
+ export type GetDocFn = (type: string, slug: string) => Promise<BackmatterDoc | null>;
15
11
 
16
- export type ListDocsFn = (filter?: {
17
- type?: string;
18
- locale?: string;
19
- }) => Promise<BackmatterDocSummary[]>;
12
+ export type ListDocsFn = (filter?: { type?: string }) => Promise<BackmatterDocSummary[]>;
20
13
 
21
- export type PublishedRender = (
22
- locale: string,
23
- type: string,
24
- slug: string,
25
- ) => Promise<ReactNode>;
14
+ export type PublishedRender = (type: string, slug: string) => Promise<ReactNode>;
26
15
 
27
16
  let registeredGetDoc: GetDocFn | null = null;
28
17
  let registeredListDocs: ListDocsFn | null = null;
@@ -61,7 +50,6 @@ async function loadCacheFns(): Promise<CacheFns> {
61
50
  }
62
51
 
63
52
  export async function getCachedDoc(
64
- locale: string,
65
53
  type: string,
66
54
  slug: string,
67
55
  cache?: CacheFns,
@@ -69,17 +57,16 @@ export async function getCachedDoc(
69
57
  "use cache";
70
58
  const fns = cache ?? (await loadCacheFns());
71
59
  fns.cacheLife("hours");
72
- fns.cacheTag(...contentCacheTags(locale, type, slug));
60
+ fns.cacheTag(...contentCacheTags(type, slug));
73
61
  const getDoc = registeredGetDoc;
74
62
  if (!getDoc) {
75
63
  throw new Error("[backmatter] getCachedDoc called before createBackmatter registered getDoc");
76
64
  }
77
- return getDoc(locale, type, slug);
65
+ return getDoc(type, slug);
78
66
  }
79
67
 
80
68
  export async function getCachedDocs(
81
69
  type?: string,
82
- locale?: string,
83
70
  cache?: CacheFns,
84
71
  ): Promise<BackmatterDocSummary[]> {
85
72
  "use cache";
@@ -90,7 +77,7 @@ export async function getCachedDocs(
90
77
  if (!listDocs) {
91
78
  throw new Error("[backmatter] getCachedDocs called before createBackmatter registered listDocs");
92
79
  }
93
- return listDocs({ type, locale });
80
+ return listDocs({ type });
94
81
  }
95
82
 
96
83
  /**
@@ -98,7 +85,6 @@ export async function getCachedDocs(
98
85
  * The cache entry is what a static prerender and a normal link prefetch store.
99
86
  */
100
87
  export async function getCachedPublished(
101
- locale: string,
102
88
  type: string,
103
89
  slug: string,
104
90
  cache?: CacheFns,
@@ -106,12 +92,12 @@ export async function getCachedPublished(
106
92
  "use cache";
107
93
  const fns = cache ?? (await loadCacheFns());
108
94
  fns.cacheLife("hours");
109
- fns.cacheTag(...contentCacheTags(locale, type, slug));
95
+ fns.cacheTag(...contentCacheTags(type, slug));
110
96
  const render = registeredPublishedRender;
111
97
  if (!render) {
112
98
  throw new Error("[backmatter] getCachedPublished called before a published render was registered");
113
99
  }
114
- return render(locale, type, slug);
100
+ return render(type, slug);
115
101
  }
116
102
 
117
103
  /** Unwrap Next `params` inside `"use cache"` so Cache Components can prerender. */
@@ -14,8 +14,8 @@ export type PreviewContentConfig = {
14
14
  };
15
15
 
16
16
  export type PreviewContent = {
17
- getDoc(locale: string, type: string, slug: string): Promise<BackmatterDoc | null>;
18
- listDocs(filter?: { type?: string; locale?: string }): Promise<BackmatterDocSummary[]>;
17
+ getDoc(type: string, slug: string): Promise<BackmatterDoc | null>;
18
+ listDocs(filter?: { type?: string }): Promise<BackmatterDocSummary[]>;
19
19
  };
20
20
 
21
21
  const current = new AsyncLocalStorage<PreviewContent>();
@@ -30,8 +30,8 @@ export function previewContentForCheckout(
30
30
  previewToken: config.previewToken,
31
31
  };
32
32
  return {
33
- getDoc(locale, type, slug) {
34
- return fetchPreviewDoc(options, { locale, type, slug, ...checkout });
33
+ getDoc(type, slug) {
34
+ return fetchPreviewDoc(options, { type, slug, ...checkout });
35
35
  },
36
36
  listDocs(filter) {
37
37
  return fetchPreviewDocs(options, { ...checkout, ...filter });
@@ -15,10 +15,9 @@ export function PreviewUnavailable({ message }: { message?: string }) {
15
15
  );
16
16
  }
17
17
 
18
- function streamUrl(ref: { locale: string; type: string; slug: string }) {
18
+ function streamUrl(ref: { type: string; slug: string }) {
19
19
  const params = new URLSearchParams({
20
20
  stream: "1",
21
- locale: ref.locale,
22
21
  type: ref.type,
23
22
  slug: ref.slug,
24
23
  });
@@ -28,7 +27,7 @@ function streamUrl(ref: { locale: string; type: string; slug: string }) {
28
27
  }
29
28
 
30
29
  export function listenForPreviewChanges(
31
- ref: { locale: string; type: string; slug: string },
30
+ ref: { type: string; slug: string },
32
31
  onChange: () => void,
33
32
  options?: { debounceMs?: number },
34
33
  ): () => void {
@@ -52,7 +51,7 @@ export function PreviewBody({
52
51
  components,
53
52
  children,
54
53
  }: {
55
- doc: Pick<BackmatterDoc, "locale" | "type" | "slug" | "frontmatter" | "body">;
54
+ doc: Pick<BackmatterDoc, "type" | "slug" | "frontmatter" | "body">;
56
55
  components?: MdxComponents;
57
56
  /** RSC first paint. Replaced in place when SSE sends a new body. */
58
57
  children?: ReactNode;
@@ -76,19 +75,17 @@ export function PreviewBody({
76
75
  source.removeEventListener("change", onMessage as EventListener);
77
76
  source.close();
78
77
  };
79
- }, [doc.locale, doc.type, doc.slug]);
78
+ }, [doc.type, doc.slug]);
80
79
 
81
80
  if (body === null) return children ?? <MdxBody source={doc.body} components={components} />;
82
81
  return <MdxBody source={body} components={components} />;
83
82
  }
84
83
 
85
84
  export function PreviewPageReloader({
86
- locale,
87
85
  type,
88
86
  slug,
89
87
  refresh,
90
88
  }: {
91
- locale: string;
92
89
  type: string;
93
90
  slug: string;
94
91
  /** Hosts omit this; the Next wrapper passes `router.refresh` in a transition. */
@@ -96,11 +93,11 @@ export function PreviewPageReloader({
96
93
  }) {
97
94
  useEffect(
98
95
  () =>
99
- listenForPreviewChanges({ locale, type, slug }, () => {
96
+ listenForPreviewChanges({ type, slug }, () => {
100
97
  if (refresh) refresh();
101
98
  else window.location.reload();
102
99
  }),
103
- [locale, type, slug, refresh],
100
+ [type, slug, refresh],
104
101
  );
105
102
  return null;
106
103
  }
@@ -20,7 +20,7 @@ import { fanInPreviewTransports, sitePageTransport } from "./site-channel";
20
20
  import { PreviewPageReloader as PreviewStreamReloader, PreviewRemoved } from "./preview-live";
21
21
 
22
22
  /** Keeps the current page visible while preview content is refetched. */
23
- export function PreviewPageReloader(props: { locale: string; type: string; slug: string }) {
23
+ export function PreviewPageReloader(props: { type: string; slug: string }) {
24
24
  const router = useRouter();
25
25
  const refresh = useCallback(() => {
26
26
  startTransition(() => {
@@ -30,8 +30,8 @@ export function PreviewPageReloader(props: { locale: string; type: string; slug:
30
30
  // HEAD and other documents in this checkout. This document's own saves
31
31
  // arrive on the doc stream below and must not refresh the page twice.
32
32
  useEffect(
33
- () => subscribeTreeStream({ locale: props.locale, type: props.type, slug: props.slug }, refresh),
34
- [props.locale, props.type, props.slug, refresh],
33
+ () => subscribeTreeStream({ type: props.type, slug: props.slug }, refresh),
34
+ [props.type, props.slug, refresh],
35
35
  );
36
36
  return <PreviewStreamReloader {...props} refresh={refresh} />;
37
37
  }
@@ -51,12 +51,11 @@ function referrerOrigin(): string {
51
51
  }
52
52
 
53
53
  function subscribeDocStream(
54
- ref: { locale: string; type: string; slug: string },
54
+ ref: { type: string; slug: string },
55
55
  onEvent: (event: { raw?: string; body?: string; frontmatter?: Record<string, unknown> }) => void,
56
56
  ): () => void {
57
57
  const params = new URLSearchParams({
58
58
  stream: "1",
59
- locale: ref.locale,
60
59
  type: ref.type,
61
60
  slug: ref.slug,
62
61
  });
@@ -90,7 +89,7 @@ function subscribeDocStream(
90
89
 
91
90
  /** Refresh when some other document in this checkout changes, or HEAD moves. */
92
91
  function subscribeTreeStream(
93
- ref: { locale: string; type: string; slug: string },
92
+ ref: { type: string; slug: string },
94
93
  refresh: () => void,
95
94
  ): () => void {
96
95
  const params = new URLSearchParams({ stream: "tree" });
@@ -107,14 +106,9 @@ function subscribeTreeStream(
107
106
  };
108
107
  const onChange = (ev: MessageEvent) => {
109
108
  try {
110
- const data = JSON.parse(String(ev.data)) as { ref?: { locale?: string; type?: string; slug?: string } };
109
+ const data = JSON.parse(String(ev.data)) as { ref?: { type?: string; slug?: string } };
111
110
  const changed = data.ref;
112
- if (
113
- changed &&
114
- changed.locale === ref.locale &&
115
- changed.type === ref.type &&
116
- changed.slug === ref.slug
117
- ) {
111
+ if (changed && changed.type === ref.type && changed.slug === ref.slug) {
118
112
  return;
119
113
  }
120
114
  } catch {
@@ -137,7 +131,6 @@ function subscribeTreeStream(
137
131
  * and swaps the body. First paint is `children`.
138
132
  */
139
133
  export function PreviewClient({
140
- locale,
141
134
  type,
142
135
  slug,
143
136
  branch,
@@ -146,7 +139,6 @@ export function PreviewClient({
146
139
  renderPreview,
147
140
  children,
148
141
  }: {
149
- locale: string;
150
142
  type: string;
151
143
  slug: string;
152
144
  branch?: string;
@@ -163,7 +155,6 @@ export function PreviewClient({
163
155
 
164
156
  useEffect(() => {
165
157
  const ref: PreviewChannelRef = {
166
- locale,
167
158
  type,
168
159
  slug,
169
160
  ...(branch ? { branch } : {}),
@@ -266,7 +257,7 @@ export function PreviewClient({
266
257
  session.close();
267
258
  windowTransport?.close();
268
259
  };
269
- }, [locale, type, slug, branch, tree, renderPreview, router, originsKey]);
260
+ }, [type, slug, branch, tree, renderPreview, router, originsKey]);
270
261
 
271
262
  if (removed) return <PreviewRemoved />;
272
263
  return (
@@ -277,13 +277,12 @@ export function createPreviewRoute(
277
277
  }
278
278
  const checkout = await checkoutFrom(req, secret, url);
279
279
  if (!checkout) return new Response("preview session missing", { status: 401 });
280
- const locale = url.searchParams.get("locale") ?? cms.config.defaultLocale;
281
280
  const type = url.searchParams.get("type") ?? "";
282
281
  const slug = url.searchParams.get("slug") ?? "";
283
282
  if (!type || !slug) return new Response("missing type or slug", { status: 400 });
284
283
  const upstream = previewStreamUrl(
285
284
  { apiUrl: cms.config.apiUrl, workspaceId: cms.config.workspaceId, previewToken: secret },
286
- { locale, type, slug, ...checkout },
285
+ { type, slug, ...checkout },
287
286
  );
288
287
  const res = await fetch(upstream);
289
288
  return new Response(res.body, {
@@ -17,7 +17,6 @@ import { requireSource, resolveSourceData } from "./source-data";
17
17
  export const RENDER_PREVIEW_PER_SECOND = 20;
18
18
 
19
19
  export type RenderPreviewRef = {
20
- locale: string;
21
20
  type: string;
22
21
  slug: string;
23
22
  branch?: string;
@@ -165,7 +164,6 @@ export function createRenderPreview(
165
164
  const node = await runWithPreviewContent(content, () =>
166
165
  renderLayoutDoc(
167
166
  {
168
- locale: ref.locale,
169
167
  type: ref.type,
170
168
  slug: ref.slug,
171
169
  source: "tree",
package/src/routes.ts CHANGED
@@ -6,19 +6,17 @@
6
6
  import { Fragment, Suspense, createElement, type ReactNode } from "react";
7
7
  import { HeldPreview, HoldPreview } from "./preview-hold";
8
8
  import {
9
- decodeDocLocale,
10
9
  DEFAULT_TEMPLATE_SLUG,
11
10
  isRoutable,
12
11
  isTemplateSlug,
13
12
  matchRoute,
14
13
  PREVIEW_CHECKOUT_QUERY,
15
14
  PREVIEW_COOKIE,
16
- PREVIEW_LOCALE_QUERY,
17
15
  readPreviewCookie,
18
16
  routeSegments,
19
- routeTemplate,
20
17
  type BackmatterDoc,
21
18
  type ModelDef,
19
+ type RouteMatch,
22
20
  type SourceDef,
23
21
  } from "@backmatter/core";
24
22
  import type { BackmatterClient } from "@backmatter/core/node/create-backmatter";
@@ -53,10 +51,6 @@ export type CatchAllOptions = {
53
51
  * third argument. A missing record is a 404.
54
52
  */
55
53
  sources?: Record<string, SourceDef>;
56
- /** Overrides `cms.config.locales`. */
57
- locales?: readonly string[];
58
- /** Overrides `cms.config.defaultLocale`. */
59
- defaultLocale?: string;
60
54
  /**
61
55
  * Wrap the rendered body (layout, title, metadata). Default: the body only.
62
56
  */
@@ -69,7 +63,6 @@ export type CatchAllOptions = {
69
63
  */
70
64
  renderPreview?: (
71
65
  ref: {
72
- locale: string;
73
66
  type: string;
74
67
  slug: string;
75
68
  branch?: string;
@@ -89,7 +82,6 @@ export type CatchAllOptions = {
89
82
  checkout?: { branch?: string; tree?: string };
90
83
  error?: string;
91
84
  getDoc?: (ref: {
92
- locale: string;
93
85
  type: string;
94
86
  slug: string;
95
87
  branch?: string;
@@ -138,19 +130,7 @@ export function previewReplacesPublished(
138
130
  return preview.enabled && preview.error !== "invalid_session";
139
131
  }
140
132
 
141
- type RouteHit = { locale: string; type: string; slug: string };
142
-
143
- async function previewLocaleQuery(
144
- searchParams: Promise<CatchAllSearchParams> | CatchAllSearchParams | undefined,
145
- ): Promise<string | undefined> {
146
- if (!searchParams) return undefined;
147
- const query = await searchParams;
148
- const raw = query[PREVIEW_LOCALE_QUERY];
149
- if (raw === undefined) return undefined;
150
- const value = Array.isArray(raw) ? raw[0] : raw;
151
- if (value === undefined) return undefined;
152
- return decodeDocLocale(value);
153
- }
133
+ type RouteHit = RouteMatch;
154
134
 
155
135
  async function checkoutQueryToken(
156
136
  searchParams: Promise<CatchAllSearchParams> | CatchAllSearchParams | undefined,
@@ -202,23 +182,18 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
202
182
  ): CatchAllRoute {
203
183
  const models = cms.models as readonly ModelDef[];
204
184
  const byType = new Map(models.map((m) => [m.type, m]));
205
- const matchOptions = {
206
- defaultLocale: options.defaultLocale ?? cms.config.defaultLocale,
207
- locales: options.locales ?? cms.config.locales,
208
- };
209
185
 
210
186
  const resolve: CatchAllRoute["resolve"] = (segments) => {
211
187
  const pathname = `/${(segments ?? []).join("/")}`;
212
- return matchRoute(models, pathname, matchOptions);
188
+ return matchRoute(models, pathname);
213
189
  };
214
190
 
215
191
  const generateStaticParams: CatchAllRoute["generateStaticParams"] = async () => {
216
192
  const docs = await cms.listDocs();
217
193
  const params: CatchAllParams[] = [];
218
194
  const seen = new Set<string>();
219
- // Keyed on the URL: a route without `{locale}` yields one page per slug.
220
- const push = (model: ModelDef, locale: string, slug: string) => {
221
- const segments = routeSegments(model, locale, slug);
195
+ const push = (model: ModelDef, slug: string) => {
196
+ const segments = routeSegments(model, slug);
222
197
  const key = segments.join("/");
223
198
  if (seen.has(key)) return;
224
199
  seen.add(key);
@@ -227,32 +202,22 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
227
202
  for (const doc of docs) {
228
203
  const model = byType.get(doc.type);
229
204
  if (!model || !isRoutable(model)) continue;
230
- // The template document has no page of its own.
231
205
  if (isTemplateSlug(model, doc.slug)) continue;
232
- push(model, doc.locale, doc.slug);
206
+ push(model, doc.slug);
233
207
  }
234
- // Catalog records that never got a CMS file still prerender through the template.
235
- const locales = matchOptions.locales ?? [matchOptions.defaultLocale];
236
208
  for (const model of models) {
237
209
  if (!isRoutable(model)) continue;
238
210
  const list = options.sources?.[model.type]?.list;
239
211
  if (!list) continue;
240
- for (const locale of locales) {
241
- for (const slug of await list(locale)) push(model, locale, slug);
242
- }
212
+ for (const slug of await list()) push(model, slug);
243
213
  }
244
214
  return params;
245
215
  };
246
216
 
247
217
  const failFor = options.notFound ?? nextNotFound;
248
218
 
249
- type GetDocFn = (locale: string, type: string, slug: string) => Promise<BackmatterDoc | null>;
219
+ type GetDocFn = (type: string, slug: string) => Promise<BackmatterDoc | null>;
250
220
 
251
- /**
252
- * Document plus external record for a route hit, or null for a 404.
253
- * External types: no record → null; no per-slug doc → the template doc.
254
- * The template slug itself only renders when `allowTemplate` (preview).
255
- */
256
221
  async function loadRouteDoc(
257
222
  hit: RouteHit,
258
223
  getDoc: GetDocFn,
@@ -265,13 +230,13 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
265
230
  if (onTemplate && !allowTemplate) return null;
266
231
  const resolved = await resolveSourceData(source, model, hit);
267
232
  if (!resolved.ok) return null;
268
- let doc = await getDoc(hit.locale, hit.type, hit.slug);
233
+ let doc = await getDoc(hit.type, hit.slug);
269
234
  if (!doc && model.external && !onTemplate) {
270
235
  const template = model.template ?? DEFAULT_TEMPLATE_SLUG;
271
- doc = await getDoc(hit.locale, hit.type, template);
236
+ doc = await getDoc(hit.type, template);
272
237
  if (!doc) {
273
238
  console.warn(
274
- `[backmatter] ${hit.type}/${hit.slug} has a record but no document; add content/${hit.locale}/${hit.type}/${template} to render every ${hit.type}`,
239
+ `[backmatter] ${hit.type}/${hit.slug} has a record but no document; add content/${hit.type}/${template} to render every ${hit.type}`,
275
240
  );
276
241
  }
277
242
  }
@@ -313,7 +278,6 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
313
278
  return createElement(
314
279
  PreviewClient,
315
280
  {
316
- locale: doc.locale,
317
281
  type: doc.type,
318
282
  slug: doc.slug,
319
283
  branch: preview.branch,
@@ -324,13 +288,11 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
324
288
  inner,
325
289
  );
326
290
  }
327
- // Hosts that have not generated `preview-action` keep the refresh fallback.
328
291
  const { PreviewPageReloader } = await import("./preview-refresh");
329
292
  return createElement(
330
293
  "div",
331
294
  null,
332
295
  createElement(PreviewPageReloader, {
333
- locale: doc.locale,
334
296
  type: doc.type,
335
297
  slug: doc.slug,
336
298
  }),
@@ -357,7 +319,7 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
357
319
  ));
358
320
  const loaded = await loadRouteDoc(
359
321
  hit,
360
- (locale, type, slug) => getDoc({ locale, type, slug, ...preview.checkout }),
322
+ (type, slug) => getDoc({ type, slug, ...preview.checkout }),
361
323
  true,
362
324
  );
363
325
  if (!loaded) return previewUnavailable("Document not found");
@@ -367,8 +329,8 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
367
329
  }
368
330
  }
369
331
 
370
- async function renderPublished(locale: string, type: string, slug: string): Promise<ReactNode> {
371
- const loaded = await loadRouteDoc({ locale, type, slug }, cms.getDoc, false);
332
+ async function renderPublished(type: string, slug: string): Promise<ReactNode> {
333
+ const loaded = await loadRouteDoc({ type, slug }, cms.getDoc, false);
372
334
  if (!loaded) return null;
373
335
  return createElement(
374
336
  "div",
@@ -377,12 +339,10 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
377
339
  );
378
340
  }
379
341
 
380
- async function publishedBody(locale: string, type: string, slug: string): Promise<ReactNode> {
342
+ async function publishedBody(type: string, slug: string): Promise<ReactNode> {
381
343
  registerPublishedRender(renderPublished);
382
- const body = await getCachedPublished(locale, type, slug);
344
+ const body = await getCachedPublished(type, slug);
383
345
  if (body != null) return body;
384
- // A tree-only document has no published body. 404 only when preview is off,
385
- // so the preview branch can still render it. The dynamic read is on the miss path.
386
346
  if (options.preview) {
387
347
  const state: PreviewState = options.preview.enabled
388
348
  ? {
@@ -405,29 +365,13 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
405
365
  return failFor();
406
366
  }
407
367
 
408
- /**
409
- * Routes without `{locale}` always match as the default locale. The editor
410
- * puts the file's locale on the preview URL so a conflicted or translated
411
- * copy is the document that gets fetched.
412
- */
413
- async function previewHit(
414
- hit: RouteHit,
415
- searchParams: Promise<CatchAllSearchParams> | CatchAllSearchParams | undefined,
416
- ): Promise<RouteHit> {
417
- const model = byType.get(hit.type);
418
- if (!model || routeTemplate(model).includes("{locale}")) return hit;
419
- const locale = await previewLocaleQuery(searchParams);
420
- if (locale === undefined) return hit;
421
- return { ...hit, locale };
422
- }
423
-
424
368
  const CmsRoute: CatchAllRoute["CmsRoute"] = async ({ params, searchParams }) => {
425
369
  const resolved = await cachedRouteParams(params);
426
370
  const hit = resolve(resolved[CATCH_ALL_PARAM]);
427
371
  if (!hit) return failFor();
428
372
  const preview = await resolvePreviewState(cms, options.preview, searchParams);
429
- if (previewReplacesPublished(preview)) return loadPreviewDoc(await previewHit(hit, searchParams), preview);
430
- return publishedBody(hit.locale, hit.type, hit.slug);
373
+ if (previewReplacesPublished(preview)) return loadPreviewDoc(hit, preview);
374
+ return publishedBody(hit.type, hit.slug);
431
375
  };
432
376
 
433
377
  const PublishedRoute = async ({
@@ -439,7 +383,7 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
439
383
  const resolved = await params;
440
384
  const hit = resolve(resolved[CATCH_ALL_PARAM]);
441
385
  if (!hit) return failFor();
442
- const body = await publishedBody(hit.locale, hit.type, hit.slug);
386
+ const body = await publishedBody(hit.type, hit.slug);
443
387
  if (body == null) return body;
444
388
  return createElement(Fragment, null, docBridge(hit), body);
445
389
  };
@@ -456,7 +400,6 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
456
400
  if (!hit) return null;
457
401
  const preview = await resolvePreviewState(cms, options.preview, searchParams);
458
402
  if (!previewReplacesPublished(preview)) return null;
459
- const docHit = await previewHit(hit, searchParams);
460
403
  return createElement(
461
404
  HoldPreview,
462
405
  null,
@@ -468,17 +411,16 @@ export function createCatchAll<Models extends readonly ModelDef[]>(
468
411
  __html: "[data-backmatter-published]{display:none!important}",
469
412
  },
470
413
  }),
471
- docBridge(docHit),
472
- await loadPreviewDoc(docHit, preview),
414
+ docBridge(hit),
415
+ await loadPreviewDoc(hit, preview),
473
416
  ),
474
417
  );
475
418
  };
476
419
 
477
- function docBridge(hit: { locale: string; type: string; slug: string }) {
420
+ function docBridge(hit: RouteHit) {
478
421
  if (!cms.config.workspaceId) return null;
479
422
  return createElement(SiteDocBridge, {
480
423
  doc: {
481
- locale: hit.locale,
482
424
  type: hit.type,
483
425
  slug: hit.slug,
484
426
  workspaceId: cms.config.workspaceId,
@@ -35,13 +35,12 @@ export function subscribeSiteDoc(listener: (doc: SiteDocRef) => void): () => voi
35
35
  export function SiteDocBridge({ doc }: { doc: SiteDocRef }) {
36
36
  useEffect(() => {
37
37
  publishSiteDoc(doc);
38
- }, [doc.locale, doc.type, doc.slug, doc.workspaceId]);
38
+ }, [doc.type, doc.slug, doc.workspaceId]);
39
39
 
40
40
  return (
41
41
  <span
42
42
  hidden
43
43
  data-backmatter-doc=""
44
- data-locale={doc.locale}
45
44
  data-type={doc.type}
46
45
  data-slug={doc.slug}
47
46
  data-workspace={doc.workspaceId}
package/src/site-doc.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  // packages/next/src/site-doc.ts
2
2
  export type SiteDocRef = {
3
- locale: string;
4
3
  type: string;
5
4
  slug: string;
6
5
  workspaceId: string;
@@ -11,7 +10,6 @@ export function parseSiteDoc(content: string | null | undefined): SiteDocRef | n
11
10
  try {
12
11
  const value = JSON.parse(content) as Record<string, unknown>;
13
12
  if (
14
- (value.locale !== undefined && typeof value.locale !== "string") ||
15
13
  typeof value.type !== "string" ||
16
14
  typeof value.slug !== "string" ||
17
15
  typeof value.workspaceId !== "string" ||
@@ -22,7 +20,6 @@ export function parseSiteDoc(content: string | null | undefined): SiteDocRef | n
22
20
  return null;
23
21
  }
24
22
  return {
25
- locale: value.locale ?? "",
26
23
  type: value.type,
27
24
  slug: value.slug,
28
25
  workspaceId: value.workspaceId,
@@ -37,7 +34,6 @@ export function readSiteDoc(): SiteDocRef | null {
37
34
  const marked = document.querySelector("[data-backmatter-doc]");
38
35
  if (marked) {
39
36
  const doc = {
40
- locale: marked.getAttribute("data-locale") ?? "",
41
37
  type: marked.getAttribute("data-type") ?? "",
42
38
  slug: marked.getAttribute("data-slug") ?? "",
43
39
  workspaceId: marked.getAttribute("data-workspace") ?? "",
@@ -50,12 +46,7 @@ export function readSiteDoc(): SiteDocRef | null {
50
46
  export function sameSiteDoc(a: SiteDocRef | null, b: SiteDocRef | null): boolean {
51
47
  if (a === b) return true;
52
48
  if (!a || !b) return false;
53
- return (
54
- a.locale === b.locale &&
55
- a.type === b.type &&
56
- a.slug === b.slug &&
57
- a.workspaceId === b.workspaceId
58
- );
49
+ return a.type === b.type && a.slug === b.slug && a.workspaceId === b.workspaceId;
59
50
  }
60
51
 
61
52
  export type SiteBlock = {
@@ -45,7 +45,7 @@ function useSiteDoc(): SiteDocRef | null {
45
45
  subtree: true,
46
46
  childList: true,
47
47
  attributes: true,
48
- attributeFilter: ["data-locale", "data-type", "data-slug", "data-workspace"],
48
+ attributeFilter: ["data-type", "data-slug", "data-workspace"],
49
49
  });
50
50
  return () => {
51
51
  stop();
@@ -33,7 +33,7 @@ export async function resolveSourceData(
33
33
  ): Promise<SourceResult> {
34
34
  if (!source) return { ok: true, data: undefined };
35
35
  if (isTemplateSlug(model, ref.slug)) {
36
- const sample = source.sample ? await source.sample(ref.locale) : null;
36
+ const sample = source.sample ? await source.sample() : null;
37
37
  return { ok: true, data: sample };
38
38
  }
39
39
  const record = await source.load(ref);