scribe-cms 0.0.7 → 0.0.9

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 (56) hide show
  1. package/README.md +5 -5
  2. package/dist/cli/index.cjs +534 -388
  3. package/dist/cli/index.cjs.map +1 -1
  4. package/dist/cli/index.js +534 -388
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/index.cjs +745 -516
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.js +744 -517
  9. package/dist/index.js.map +1 -1
  10. package/dist/runtime.cjs +292 -261
  11. package/dist/runtime.cjs.map +1 -1
  12. package/dist/runtime.js +290 -261
  13. package/dist/runtime.js.map +1 -1
  14. package/dist/src/config/resolve-config.d.ts.map +1 -1
  15. package/dist/src/core/builtin-fields.d.ts +8 -8
  16. package/dist/src/core/builtin-fields.d.ts.map +1 -1
  17. package/dist/src/core/types.d.ts +14 -4
  18. package/dist/src/core/types.d.ts.map +1 -1
  19. package/dist/src/create-project.d.ts.map +1 -1
  20. package/dist/src/create-scribe.d.ts.map +1 -1
  21. package/dist/src/export/build-static-raw-exports.d.ts.map +1 -1
  22. package/dist/src/i18n/build-url.d.ts +16 -3
  23. package/dist/src/i18n/build-url.d.ts.map +1 -1
  24. package/dist/src/i18n/resolve-document.d.ts +3 -2
  25. package/dist/src/i18n/resolve-document.d.ts.map +1 -1
  26. package/dist/src/index.d.ts +3 -1
  27. package/dist/src/index.d.ts.map +1 -1
  28. package/dist/src/loader/create-loader.d.ts.map +1 -1
  29. package/dist/src/redirects/build-json-redirects.d.ts +6 -0
  30. package/dist/src/redirects/build-json-redirects.d.ts.map +1 -0
  31. package/dist/src/redirects/build-redirects.d.ts +9 -7
  32. package/dist/src/redirects/build-redirects.d.ts.map +1 -1
  33. package/dist/src/redirects/load-type-redirects.d.ts +14 -0
  34. package/dist/src/redirects/load-type-redirects.d.ts.map +1 -0
  35. package/dist/src/redirects/redirect-schema.d.ts +31 -0
  36. package/dist/src/redirects/redirect-schema.d.ts.map +1 -0
  37. package/dist/src/runtime.d.ts +3 -0
  38. package/dist/src/runtime.d.ts.map +1 -1
  39. package/dist/src/sitemap/generate-sitemap.d.ts.map +1 -1
  40. package/dist/src/translate/page-translator.d.ts +1 -0
  41. package/dist/src/translate/page-translator.d.ts.map +1 -1
  42. package/dist/src/translate/resolve-translate-config.d.ts.map +1 -1
  43. package/dist/src/translate/sanitize-mdx-jsx.d.ts +9 -0
  44. package/dist/src/translate/sanitize-mdx-jsx.d.ts.map +1 -0
  45. package/dist/src/validate/validate-project.d.ts.map +1 -1
  46. package/dist/src/validate/validate-redirects.d.ts +4 -0
  47. package/dist/src/validate/validate-redirects.d.ts.map +1 -0
  48. package/dist/studio/server.cjs +90 -61
  49. package/dist/studio/server.cjs.map +1 -1
  50. package/dist/studio/server.js +90 -61
  51. package/dist/studio/server.js.map +1 -1
  52. package/package.json +1 -1
  53. package/dist/src/core/slug-aliases.d.ts +0 -27
  54. package/dist/src/core/slug-aliases.d.ts.map +0 -1
  55. package/dist/src/redirects/translation-index.d.ts +0 -4
  56. package/dist/src/redirects/translation-index.d.ts.map +0 -1
@@ -195,65 +195,93 @@ function pathSuffix(template) {
195
195
  if (idx === -1) throw new Error(`Invalid path template (missing {slug}): ${template}`);
196
196
  return template.slice(idx + SLUG_PLACEHOLDER.length);
197
197
  }
198
- function resolvePath(template, slug, locale, defaultLocale) {
199
- const relative = template.replace(SLUG_PLACEHOLDER, slug);
200
- if (locale === defaultLocale) return relative;
201
- return `/${locale}${relative}`;
198
+ function resolveDefaultLocaleRouting() {
199
+ return { strategy: "path-prefix", prefixDefaultLocale: false };
202
200
  }
203
- function extractSlugFromResolvedPath(template, resolvedPath) {
204
- const prefix = pathPrefix(template);
205
- const suffix = pathSuffix(template);
206
- if (!resolvedPath.startsWith(prefix)) return null;
207
- if (suffix && !resolvedPath.endsWith(suffix)) return null;
208
- const slugEnd = suffix ? resolvedPath.length - suffix.length : resolvedPath.length;
209
- const slug = resolvedPath.slice(prefix.length, slugEnd);
210
- return slug.length > 0 ? slug : null;
201
+ function splitPathAndSearch(pathname) {
202
+ const q = pathname.indexOf("?");
203
+ if (q === -1) return { pathname, search: "" };
204
+ return { pathname: pathname.slice(0, q), search: pathname.slice(q) };
205
+ }
206
+ function createUrlBuilder(config) {
207
+ const localeRouting = config.localeRouting ?? resolveDefaultLocaleRouting();
208
+ const defaultLocale = config.defaultLocale;
209
+ const prefixedLocales = localeRouting.strategy === "path-prefix" ? localeRouting.prefixDefaultLocale ? [...config.locales] : config.locales.filter((locale) => locale !== defaultLocale) : config.locales.filter((locale) => locale !== defaultLocale);
210
+ function applyLocaleToPath(pathname, locale) {
211
+ if (locale === defaultLocale) {
212
+ if (localeRouting.strategy === "path-prefix" && localeRouting.prefixDefaultLocale) {
213
+ return `/${defaultLocale}${pathname}`;
214
+ }
215
+ return pathname;
216
+ }
217
+ if (localeRouting.strategy === "search-param") {
218
+ const { pathname: base, search } = splitPathAndSearch(pathname);
219
+ const params = new URLSearchParams(search.startsWith("?") ? search.slice(1) : search);
220
+ params.set(localeRouting.param, locale);
221
+ const qs = params.toString();
222
+ return qs ? `${base}?${qs}` : base;
223
+ }
224
+ return `/${locale}${pathname}`;
225
+ }
226
+ function resolvePath(template, slug, locale) {
227
+ const relative = template.replace(SLUG_PLACEHOLDER, slug);
228
+ return applyLocaleToPath(relative, locale);
229
+ }
230
+ function extractSlugFromResolvedPath(template, resolvedPath) {
231
+ let pathname = resolvedPath;
232
+ if (localeRouting.strategy === "search-param") {
233
+ pathname = splitPathAndSearch(resolvedPath).pathname;
234
+ } else if (localeRouting.strategy === "path-prefix") {
235
+ for (const locale of config.locales) {
236
+ if (locale === defaultLocale && !localeRouting.prefixDefaultLocale) continue;
237
+ const prefix2 = `/${locale}`;
238
+ if (pathname === prefix2 || pathname.startsWith(`${prefix2}/`)) {
239
+ pathname = pathname.slice(prefix2.length) || "/";
240
+ break;
241
+ }
242
+ }
243
+ }
244
+ const prefix = pathPrefix(template);
245
+ const suffix = pathSuffix(template);
246
+ if (!pathname.startsWith(prefix)) return null;
247
+ if (suffix && !pathname.endsWith(suffix)) return null;
248
+ const slugEnd = suffix ? pathname.length - suffix.length : pathname.length;
249
+ const slug = pathname.slice(prefix.length, slugEnd);
250
+ return slug.length > 0 ? slug : null;
251
+ }
252
+ return {
253
+ defaultLocale,
254
+ locales: config.locales,
255
+ localeRouting,
256
+ prefixedLocales,
257
+ resolvePath,
258
+ extractSlugFromResolvedPath,
259
+ applyLocaleToPath
260
+ };
211
261
  }
212
262
 
213
263
  // src/core/builtin-fields.ts
214
264
  var SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
215
- var slugPatternSchema = zod.z.string().regex(SLUG_PATTERN, "slug must be lowercase-kebab-case");
265
+ zod.z.string().regex(SLUG_PATTERN, "slug must be lowercase-kebab-case");
216
266
  var isoDateSchema = zod.z.string().regex(/^\d{4}-\d{2}-\d{2}(T[\d:.Z+-]*)?$/, "Use ISO date YYYY-MM-DD or full ISO 8601");
217
267
  var canonicalPathSchema = zod.z.string().regex(/^\//, "canonicalPath must start with /");
218
- function extractBuiltinEnFields(data, pathTemplate, enSlug, defaultLocale) {
268
+ var DEPRECATED_REDIRECT_FIELDS = [
269
+ [
270
+ "aliases",
271
+ "aliases frontmatter is removed \u2014 add redirects to content/<type>/_redirects.json instead"
272
+ ],
273
+ [
274
+ "redirect_to",
275
+ "redirect_to frontmatter is removed \u2014 add redirects to content/<type>/_redirects.json instead"
276
+ ]
277
+ ];
278
+ function extractBuiltinEnFields(data, _pathTemplate, _enSlug, _defaultLocale) {
219
279
  const issues = [];
220
280
  const rest = { ...data };
221
- const aliasesResult = zod.z.array(slugPatternSchema).max(20).optional().default([]).safeParse(rest.aliases ?? []);
222
- delete rest.aliases;
223
- const redirectRaw = rest.redirect_to;
224
- delete rest.redirect_to;
225
- let redirectTo;
226
- if (redirectRaw !== void 0 && redirectRaw !== null && redirectRaw !== "") {
227
- if (typeof redirectRaw !== "string") {
228
- issues.push({
229
- field: "redirect_to",
230
- message: "redirect_to must be a string path",
231
- level: "error"
232
- });
233
- } else if (!pathTemplate) {
234
- issues.push({
235
- field: "redirect_to",
236
- message: "redirect_to is not allowed on reference-only content types",
237
- level: "error"
238
- });
239
- } else if (!extractSlugFromResolvedPath(pathTemplate, redirectRaw)) {
240
- issues.push({
241
- field: "redirect_to",
242
- message: `redirect_to must match path template "${pathTemplate}" (prefix "${pathPrefix(pathTemplate)}"${pathSuffix(pathTemplate) ? `, suffix "${pathSuffix(pathTemplate)}"` : ""})`,
243
- level: "error"
244
- });
245
- } else {
246
- redirectTo = redirectRaw;
247
- }
248
- }
249
- const aliases = aliasesResult.success ? aliasesResult.data : [];
250
- if (!aliasesResult.success) {
251
- for (const issue of aliasesResult.error.issues) {
252
- issues.push({
253
- field: `aliases.${issue.path.join(".")}`,
254
- message: issue.message,
255
- level: "error"
256
- });
281
+ for (const [field, message] of DEPRECATED_REDIRECT_FIELDS) {
282
+ if (rest[field] !== void 0) {
283
+ issues.push({ field, message, level: "error" });
284
+ delete rest[field];
257
285
  }
258
286
  }
259
287
  let publishedAt;
@@ -315,8 +343,6 @@ function extractBuiltinEnFields(data, pathTemplate, enSlug, defaultLocale) {
315
343
  }
316
344
  return {
317
345
  builtin: {
318
- aliases,
319
- redirectTo,
320
346
  publishedAt,
321
347
  updatedAt,
322
348
  noindex,
@@ -326,20 +352,24 @@ function extractBuiltinEnFields(data, pathTemplate, enSlug, defaultLocale) {
326
352
  issues
327
353
  };
328
354
  }
329
- function resolveCanonicalPathname(type, doc, defaultLocale) {
355
+ function resolveCanonicalPathname(type, doc, defaultLocale, localeRouting) {
356
+ const urlBuilder = createUrlBuilder({
357
+ locales: [defaultLocale, ...doc.locale !== defaultLocale ? [doc.locale] : []],
358
+ defaultLocale,
359
+ localeRouting: localeRouting ?? { strategy: "path-prefix", prefixDefaultLocale: false }
360
+ });
330
361
  if (doc.canonicalPathOverride) {
331
- if (doc.locale === defaultLocale) return doc.canonicalPathOverride;
332
- return `/${doc.locale}${doc.canonicalPathOverride}`;
362
+ return urlBuilder.applyLocaleToPath(doc.canonicalPathOverride, doc.locale);
333
363
  }
334
364
  if (!type.path) return `/${doc.slug}`;
335
- return resolvePath(type.path, doc.slug, doc.locale, defaultLocale);
365
+ return urlBuilder.resolvePath(type.path, doc.slug, doc.locale);
336
366
  }
337
- function mergeBuiltinsIntoFrontmatter(frontmatter, doc, type, defaultLocale) {
367
+ function mergeBuiltinsIntoFrontmatter(frontmatter, doc, type, defaultLocale, localeRouting) {
338
368
  const out = { ...frontmatter };
339
369
  if (doc.publishedAt !== void 0) out.publishedAt = doc.publishedAt;
340
370
  if (doc.updatedAt !== void 0) out.updatedAt = doc.updatedAt;
341
371
  out.noindex = doc.noindex;
342
- out.canonicalPath = resolveCanonicalPathname(type, doc, defaultLocale);
372
+ out.canonicalPath = resolveCanonicalPathname(type, doc, defaultLocale, localeRouting);
343
373
  return out;
344
374
  }
345
375
  var SCHEMA_VERSION = 4;
@@ -512,7 +542,8 @@ function parseEnMdx(filePath, config, type) {
512
542
  locale: config.defaultLocale
513
543
  },
514
544
  type,
515
- config.defaultLocale
545
+ config.defaultLocale,
546
+ config.localeRouting
516
547
  );
517
548
  const crossIssues = type.crossValidate?.(result.data, {
518
549
  locale: config.defaultLocale,
@@ -526,8 +557,6 @@ function parseEnMdx(filePath, config, type) {
526
557
  slug,
527
558
  enSlug: slug,
528
559
  locale: config.defaultLocale,
529
- aliases: builtin.aliases,
530
- redirectTo: builtin.redirectTo,
531
560
  publishedAt: builtin.publishedAt,
532
561
  updatedAt: builtin.updatedAt,
533
562
  noindex: builtin.noindex,