@farming-labs/next 0.1.13 → 0.1.16

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.mjs CHANGED
@@ -1,6 +1,7 @@
1
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
2
2
  import { dirname, isAbsolute, join, relative } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
+ import matter from "gray-matter";
4
5
  import createMDX from "@next/mdx";
5
6
 
6
7
  //#region src/config.ts
@@ -73,6 +74,7 @@ export const { GET, POST } = createDocsAPI({
73
74
  entry: docsConfig.entry,
74
75
  contentDir: docsConfig.contentDir,
75
76
  i18n: docsConfig.i18n,
77
+ changelog: docsConfig.changelog,
76
78
  search: docsConfig.search,
77
79
  ai: docsConfig.ai,
78
80
  });
@@ -93,6 +95,34 @@ export const { GET, POST, DELETE } = createDocsMCPAPI({
93
95
  mcp: docsConfig.mcp,
94
96
  });
95
97
 
98
+ export const revalidate = false;
99
+ `;
100
+ const DOCS_MARKDOWN_ROUTE_TEMPLATE = `\
101
+ ${GENERATED_BANNER}
102
+ import docsConfig from "@/docs.config";
103
+ import { createDocsAPI } from "@farming-labs/next/api";
104
+
105
+ const handlers = createDocsAPI({
106
+ entry: docsConfig.entry,
107
+ contentDir: docsConfig.contentDir,
108
+ i18n: docsConfig.i18n,
109
+ changelog: docsConfig.changelog,
110
+ search: docsConfig.search,
111
+ ai: docsConfig.ai,
112
+ });
113
+
114
+ export async function GET(
115
+ request: Request,
116
+ { params }: { params: Promise<{ slug?: string[] }> },
117
+ ) {
118
+ const { slug = [] } = await params;
119
+ const url = new URL(request.url);
120
+ url.searchParams.set("format", "markdown");
121
+ if (slug.length > 0) url.searchParams.set("path", slug.join("/"));
122
+ else url.searchParams.delete("path");
123
+ return handlers.GET(new Request(url, { headers: request.headers }));
124
+ }
125
+
96
126
  export const revalidate = false;
97
127
  `;
98
128
  const API_REFERENCE_ROUTE_TEMPLATE = `\
@@ -126,6 +156,14 @@ const ApiReferenceLayout = createNextApiReferenceLayout(docsConfig);
126
156
 
127
157
  export default ApiReferenceLayout;
128
158
  `;
159
+ const CHANGELOG_SOURCE_LAYOUT_TEMPLATE = `\
160
+ ${GENERATED_BANNER}
161
+ import { notFound } from "next/navigation";
162
+
163
+ export default function HiddenChangelogSourceLayout() {
164
+ notFound();
165
+ }
166
+ `;
129
167
  const FILE_EXTS = [
130
168
  "tsx",
131
169
  "ts",
@@ -174,6 +212,7 @@ function createDocsWorkspaceAliases() {
174
212
  "@farming-labs/docs/server": "./packages/docs/src/server.ts",
175
213
  "@farming-labs/next": "./packages/next/src/index.ts",
176
214
  "@farming-labs/next/api": "./packages/next/src/api.ts",
215
+ "@farming-labs/next/changelog": "./packages/next/src/changelog.tsx",
177
216
  "@farming-labs/next/client-callbacks": "./packages/next/src/client-callbacks.tsx",
178
217
  "@farming-labs/next/layout": "./packages/next/src/layout.tsx",
179
218
  "@farming-labs/next/mdx-plugins/rehype-code": "./packages/next/src/mdx-plugins/rehype-code.ts",
@@ -216,8 +255,8 @@ function readDocsContentDir(root) {
216
255
  const configPath = join(root, `docs.config.${ext}`);
217
256
  if (!existsSync(configPath)) continue;
218
257
  try {
219
- const match = readFileSync(configPath, "utf-8").match(/contentDir\s*:\s*["']([^"']+)["']/);
220
- if (match?.[1]) return match[1];
258
+ const contentDir = readTopLevelStringProperty(readFileSync(configPath, "utf-8"), "contentDir");
259
+ if (contentDir) return contentDir;
221
260
  } catch {}
222
261
  }
223
262
  }
@@ -284,6 +323,223 @@ function readApiReferenceConfig(root) {
284
323
  routeRoot: "api"
285
324
  };
286
325
  }
326
+ function readChangelogConfig(root) {
327
+ for (const ext of FILE_EXTS) {
328
+ const configPath = join(root, `docs.config.${ext}`);
329
+ if (!existsSync(configPath)) continue;
330
+ try {
331
+ const content = readFileSync(configPath, "utf-8");
332
+ if (content.match(/changelog\s*:\s*false/)) return {
333
+ enabled: false,
334
+ path: "changelog",
335
+ contentDir: "changelog"
336
+ };
337
+ if (content.match(/changelog\s*:\s*true/)) return {
338
+ enabled: true,
339
+ path: "changelog",
340
+ contentDir: "changelog"
341
+ };
342
+ const block = extractObjectLiteral(content, "changelog");
343
+ if (!block) continue;
344
+ const enabledMatch = block.match(/enabled\s*:\s*(true|false)/);
345
+ const pathMatch = block.match(/path\s*:\s*["']([^"']+)["']/);
346
+ const contentDirMatch = block.match(/contentDir\s*:\s*["']([^"']+)["']/);
347
+ return {
348
+ enabled: enabledMatch ? enabledMatch[1] !== "false" : true,
349
+ path: pathMatch?.[1]?.replace(/^\/+|\/+$/g, "") || "changelog",
350
+ contentDir: contentDirMatch?.[1]?.replace(/\/+$/, "") || "changelog"
351
+ };
352
+ } catch {
353
+ return {
354
+ enabled: false,
355
+ path: "changelog",
356
+ contentDir: "changelog"
357
+ };
358
+ }
359
+ }
360
+ return {
361
+ enabled: false,
362
+ path: "changelog",
363
+ contentDir: "changelog"
364
+ };
365
+ }
366
+ function resolveDocsContentDir(root, appDir, entry) {
367
+ const configuredContentDir = readDocsContentDir(root);
368
+ if (!configuredContentDir) return join(root, appDir, entry);
369
+ return isAbsolute(configuredContentDir) ? configuredContentDir : join(root, configuredContentDir);
370
+ }
371
+ function resolveChangelogContentDir(root, appDir, entry, changelogContentDir) {
372
+ if (isAbsolute(changelogContentDir)) return changelogContentDir;
373
+ return join(resolveDocsContentDir(root, appDir, entry), changelogContentDir);
374
+ }
375
+ function findChangelogSourceEntries(changelogDir) {
376
+ if (!existsSync(changelogDir)) return [];
377
+ const entries = [];
378
+ for (const name of readdirSync(changelogDir).sort()) {
379
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(name)) continue;
380
+ const entryDir = join(changelogDir, name);
381
+ try {
382
+ if (!statSync(entryDir).isDirectory()) continue;
383
+ } catch {
384
+ continue;
385
+ }
386
+ const sourceFile = ["page.mdx", "page.md"].map((fileName) => join(entryDir, fileName)).find((value) => existsSync(value));
387
+ if (!sourceFile) continue;
388
+ let metadata = {};
389
+ try {
390
+ metadata = matter(readFileSync(sourceFile, "utf-8")).data;
391
+ } catch {
392
+ metadata = {};
393
+ }
394
+ entries.push({
395
+ slug: name,
396
+ date: name,
397
+ sourceFile,
398
+ metadata
399
+ });
400
+ }
401
+ return entries.sort((left, right) => right.date.localeCompare(left.date));
402
+ }
403
+ function extractRootObjectLiteral(content) {
404
+ const candidateIndexes = [content.search(/\bdefineDocs\s*\(/), content.search(/\bexport\s+default\b/)].filter((value) => value !== -1);
405
+ for (const startIndex of candidateIndexes) {
406
+ const braceStart = content.indexOf("{", startIndex);
407
+ if (braceStart === -1) continue;
408
+ let depth = 0;
409
+ let inString = null;
410
+ let escaped = false;
411
+ for (let index = braceStart; index < content.length; index += 1) {
412
+ const char = content[index];
413
+ if (inString) {
414
+ if (escaped) {
415
+ escaped = false;
416
+ continue;
417
+ }
418
+ if (char === "\\") {
419
+ escaped = true;
420
+ continue;
421
+ }
422
+ if (char === inString) inString = null;
423
+ continue;
424
+ }
425
+ if (char === "\"" || char === "'" || char === "`") {
426
+ inString = char;
427
+ continue;
428
+ }
429
+ if (char === "{") {
430
+ depth += 1;
431
+ continue;
432
+ }
433
+ if (char !== "}") continue;
434
+ depth -= 1;
435
+ if (depth === 0) return content.slice(braceStart + 1, index);
436
+ }
437
+ }
438
+ }
439
+ function readTopLevelStringProperty(content, key) {
440
+ const block = extractRootObjectLiteral(content);
441
+ if (!block) return void 0;
442
+ let objectDepth = 0;
443
+ let arrayDepth = 0;
444
+ let parenDepth = 0;
445
+ let inString = null;
446
+ let inLineComment = false;
447
+ let inBlockComment = false;
448
+ let escaped = false;
449
+ for (let index = 0; index < block.length; index += 1) {
450
+ const char = block[index];
451
+ const next = block[index + 1];
452
+ if (inLineComment) {
453
+ if (char === "\n") inLineComment = false;
454
+ continue;
455
+ }
456
+ if (inBlockComment) {
457
+ if (char === "*" && next === "/") {
458
+ inBlockComment = false;
459
+ index += 1;
460
+ }
461
+ continue;
462
+ }
463
+ if (inString) {
464
+ if (escaped) {
465
+ escaped = false;
466
+ continue;
467
+ }
468
+ if (char === "\\") {
469
+ escaped = true;
470
+ continue;
471
+ }
472
+ if (char === inString) inString = null;
473
+ continue;
474
+ }
475
+ if (char === "/" && next === "/") {
476
+ inLineComment = true;
477
+ index += 1;
478
+ continue;
479
+ }
480
+ if (char === "/" && next === "*") {
481
+ inBlockComment = true;
482
+ index += 1;
483
+ continue;
484
+ }
485
+ if (char === "\"" || char === "'" || char === "`") {
486
+ inString = char;
487
+ continue;
488
+ }
489
+ if (char === "{") {
490
+ objectDepth += 1;
491
+ continue;
492
+ }
493
+ if (char === "}") {
494
+ objectDepth = Math.max(0, objectDepth - 1);
495
+ continue;
496
+ }
497
+ if (char === "[") {
498
+ arrayDepth += 1;
499
+ continue;
500
+ }
501
+ if (char === "]") {
502
+ arrayDepth = Math.max(0, arrayDepth - 1);
503
+ continue;
504
+ }
505
+ if (char === "(") {
506
+ parenDepth += 1;
507
+ continue;
508
+ }
509
+ if (char === ")") {
510
+ parenDepth = Math.max(0, parenDepth - 1);
511
+ continue;
512
+ }
513
+ if (objectDepth !== 0 || arrayDepth !== 0 || parenDepth !== 0) continue;
514
+ if (!block.startsWith(key, index)) continue;
515
+ const before = block[index - 1] ?? "";
516
+ const after = block[index + key.length] ?? "";
517
+ if (/[A-Za-z0-9_$]/.test(before) || /[A-Za-z0-9_$]/.test(after)) continue;
518
+ let cursor = index + key.length;
519
+ while (/\s/.test(block[cursor] ?? "")) cursor += 1;
520
+ if (block[cursor] !== ":") continue;
521
+ cursor += 1;
522
+ while (/\s/.test(block[cursor] ?? "")) cursor += 1;
523
+ const quote = block[cursor];
524
+ if (quote !== "\"" && quote !== "'") continue;
525
+ cursor += 1;
526
+ let value = "";
527
+ for (; cursor < block.length; cursor += 1) {
528
+ const valueChar = block[cursor];
529
+ if (valueChar === "\\") {
530
+ const escapedChar = block[cursor + 1];
531
+ if (escapedChar) {
532
+ value += escapedChar;
533
+ cursor += 1;
534
+ }
535
+ continue;
536
+ }
537
+ if (valueChar === quote) return value;
538
+ value += valueChar;
539
+ }
540
+ return;
541
+ }
542
+ }
287
543
  function extractObjectLiteral(content, key) {
288
544
  const keyIndex = content.search(new RegExp(`${key}\\s*:\\s*\\{`));
289
545
  if (keyIndex === -1) return void 0;
@@ -301,6 +557,84 @@ function extractObjectLiteral(content, key) {
301
557
  if (depth === 0) return content.slice(braceStart + 1, index);
302
558
  }
303
559
  }
560
+ function toImportPath(fromFile, toFile) {
561
+ const relativePath = relative(dirname(fromFile), toFile).replaceAll("\\", "/");
562
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
563
+ }
564
+ function buildInlineChangelogEntriesSource(root, pagePath, entryPath, routePath, entries) {
565
+ const imports = [];
566
+ const records = [];
567
+ entries.forEach((entry, index) => {
568
+ const importName = `ChangelogEntry${index + 1}`;
569
+ imports.push(`import ${importName} from "${toImportPath(pagePath, entry.sourceFile)}";`);
570
+ records.push(` {
571
+ slug: ${JSON.stringify(entry.slug)},
572
+ date: ${JSON.stringify(entry.date)},
573
+ url: ${JSON.stringify(`/${entryPath}/${routePath}/${entry.slug}`)},
574
+ sourcePath: ${JSON.stringify(relative(root, entry.sourceFile).replaceAll("\\", "/"))},
575
+ Component: ${importName},
576
+ metadata: ${JSON.stringify(entry.metadata)},
577
+ }`);
578
+ });
579
+ return {
580
+ importsSource: `import type { GeneratedChangelogEntry } from "@farming-labs/next/changelog";
581
+ ${imports.join("\n")}`.trimEnd(),
582
+ entriesSource: `export const changelogEntries: GeneratedChangelogEntry[] = [
583
+ ${records.join(",\n")}
584
+ ];
585
+ `
586
+ };
587
+ }
588
+ function buildChangelogIndexPageSource(root, pagePath, entryPath, routePath, entries) {
589
+ const { importsSource, entriesSource } = buildInlineChangelogEntriesSource(root, pagePath, entryPath, routePath, entries);
590
+ return `\
591
+ ${GENERATED_BANNER}
592
+ import docsConfig from "@/docs.config";
593
+ ${importsSource}
594
+ import {
595
+ createNextChangelogIndexMetadata,
596
+ createNextChangelogIndexPage,
597
+ } from "@farming-labs/next/changelog";
598
+
599
+ ${entriesSource}
600
+
601
+ export const metadata = createNextChangelogIndexMetadata(docsConfig);
602
+
603
+ const ChangelogPage = createNextChangelogIndexPage(docsConfig, changelogEntries);
604
+
605
+ export default ChangelogPage;
606
+ `;
607
+ }
608
+ function buildChangelogEntryPageSource(root, pagePath, entryPath, routePath, entries) {
609
+ const { importsSource, entriesSource } = buildInlineChangelogEntriesSource(root, pagePath, entryPath, routePath, entries);
610
+ return `\
611
+ ${GENERATED_BANNER}
612
+ import docsConfig from "@/docs.config";
613
+ ${importsSource}
614
+ import {
615
+ createNextChangelogEntryMetadata,
616
+ createNextChangelogEntryPage,
617
+ createNextChangelogStaticParams,
618
+ } from "@farming-labs/next/changelog";
619
+
620
+ ${entriesSource}
621
+
622
+ export const generateStaticParams = createNextChangelogStaticParams(changelogEntries);
623
+ export const generateMetadata = createNextChangelogEntryMetadata(docsConfig, changelogEntries);
624
+
625
+ const ChangelogEntryPage = createNextChangelogEntryPage(docsConfig, changelogEntries);
626
+
627
+ export default ChangelogEntryPage;
628
+ `;
629
+ }
630
+ function resolveManagedChangelogSourceLayoutPath(root, appDir, entry, changelogContentDir) {
631
+ if (isAbsolute(changelogContentDir)) return void 0;
632
+ const appRoot = join(root, appDir);
633
+ const contentRoot = resolveChangelogContentDir(root, appDir, entry, changelogContentDir);
634
+ const relativePath = relative(appRoot, contentRoot);
635
+ if (relativePath.startsWith("..") || isAbsolute(relativePath)) return void 0;
636
+ return join(contentRoot, "layout.tsx");
637
+ }
304
638
  function removeManagedFile(filePath) {
305
639
  if (isManagedGeneratedFile(filePath)) rmSync(filePath, { force: true });
306
640
  }
@@ -342,6 +676,37 @@ function normalizeRoutePath(route) {
342
676
  const normalized = `/${route}`.replace(/\/+/g, "/");
343
677
  return normalized !== "/" ? normalized.replace(/\/+$/, "") : "/api/docs/mcp";
344
678
  }
679
+ function buildDocsMarkdownRewrites(entry) {
680
+ const normalizedEntry = entry.replace(/^\/+|\/+$/g, "") || "docs";
681
+ return [{
682
+ source: `/${normalizedEntry}.md`,
683
+ destination: "/api/docs/markdown"
684
+ }, {
685
+ source: `/${normalizedEntry}/:slug*.md`,
686
+ destination: "/api/docs/markdown/:slug*"
687
+ }];
688
+ }
689
+ function dedupeRewrites(rewrites) {
690
+ const seen = /* @__PURE__ */ new Set();
691
+ const result = [];
692
+ for (const rewrite of rewrites) {
693
+ const key = `${rewrite.source}=>${rewrite.destination}`;
694
+ if (seen.has(key)) continue;
695
+ seen.add(key);
696
+ result.push(rewrite);
697
+ }
698
+ return result;
699
+ }
700
+ function mergeDocsMarkdownRewrites(entry, result) {
701
+ const docsMarkdownRewrites = buildDocsMarkdownRewrites(entry);
702
+ if (!result) return [...docsMarkdownRewrites];
703
+ if (Array.isArray(result)) return dedupeRewrites([...docsMarkdownRewrites, ...result]);
704
+ return {
705
+ beforeFiles: dedupeRewrites([...docsMarkdownRewrites, ...result.beforeFiles ?? []]),
706
+ afterFiles: result.afterFiles ?? [],
707
+ fallback: result.fallback ?? []
708
+ };
709
+ }
345
710
  function withDocs(nextConfig = {}) {
346
711
  const root = process.cwd();
347
712
  const workspaceRoot = findDocsWorkspaceRoot(root);
@@ -363,6 +728,11 @@ function withDocs(nextConfig = {}) {
363
728
  mkdirSync(docsApiRouteDir, { recursive: true });
364
729
  writeFileSync(join(docsApiRouteDir, "route.ts"), DOCS_API_ROUTE_TEMPLATE);
365
730
  }
731
+ const docsMarkdownRouteDir = join(root, appDir, "api", "docs", "markdown", "[[...slug]]");
732
+ if (!isStaticExport && (!hasFile(docsMarkdownRouteDir, "route") || isManagedGeneratedFile(join(docsMarkdownRouteDir, "route.ts")))) {
733
+ mkdirSync(docsMarkdownRouteDir, { recursive: true });
734
+ writeFileSync(join(docsMarkdownRouteDir, "route.ts"), DOCS_MARKDOWN_ROUTE_TEMPLATE);
735
+ }
366
736
  const mcp = readMcpConfig(root);
367
737
  const docsMcpRouteDir = join(root, appDir, "api", "docs", "mcp");
368
738
  if (mcp.enabled && mcp.route === "/api/docs/mcp" && !isStaticExport && !hasFile(docsMcpRouteDir, "route")) {
@@ -398,6 +768,43 @@ function withDocs(nextConfig = {}) {
398
768
  }
399
769
  }
400
770
  }
771
+ const changelog = readChangelogConfig(root);
772
+ if (changelog.enabled) {
773
+ const changelogBaseDir = join(root, appDir, entry, ...changelog.path.split("/"));
774
+ const changelogEntryDir = join(changelogBaseDir, "[slug]");
775
+ const changelogIndexPath = join(changelogBaseDir, "page.tsx");
776
+ const changelogEntryPath = join(changelogEntryDir, "page.tsx");
777
+ const changelogSourceDir = resolveChangelogContentDir(root, appDir, entry, changelog.contentDir);
778
+ const changelogUsesSourcePagesAsDetails = !isAbsolute(changelog.contentDir) && changelog.contentDir === changelog.path;
779
+ const changelogEntries = findChangelogSourceEntries(changelogSourceDir);
780
+ const changelogSourceLayoutPath = resolveManagedChangelogSourceLayoutPath(root, appDir, entry, changelog.contentDir);
781
+ const legacyChangelogBaseDirs = new Set([join(root, appDir, ...changelog.path.split("/")), ...isAbsolute(changelog.contentDir) ? [] : [join(root, appDir, ...changelog.contentDir.split("/"))]]);
782
+ for (const legacyChangelogBaseDir of legacyChangelogBaseDirs) {
783
+ if (legacyChangelogBaseDir === changelogBaseDir) continue;
784
+ removeManagedFile(join(legacyChangelogBaseDir, "[slug]", "page.tsx"));
785
+ removeManagedFile(join(legacyChangelogBaseDir, "page.tsx"));
786
+ removeManagedFile(join(legacyChangelogBaseDir, "__changelog.generated.tsx"));
787
+ }
788
+ mkdirSync(changelogBaseDir, { recursive: true });
789
+ removeManagedFile(join(changelogBaseDir, "__changelog.generated.tsx"));
790
+ if (!hasFile(changelogBaseDir, "page") || isManagedGeneratedFile(changelogIndexPath)) writeFileSync(changelogIndexPath, buildChangelogIndexPageSource(root, changelogIndexPath, entry, changelog.path, changelogEntries));
791
+ if (changelogUsesSourcePagesAsDetails) {
792
+ removeManagedFile(changelogEntryPath);
793
+ if (changelogSourceLayoutPath) removeManagedFile(changelogSourceLayoutPath);
794
+ } else {
795
+ if (!hasFile(changelogEntryDir, "page") || isManagedGeneratedFile(changelogEntryPath)) {
796
+ mkdirSync(changelogEntryDir, { recursive: true });
797
+ writeFileSync(changelogEntryPath, buildChangelogEntryPageSource(root, changelogEntryPath, entry, changelog.path, changelogEntries));
798
+ }
799
+ if (changelogSourceLayoutPath) {
800
+ const changelogSourceLayoutDir = dirname(changelogSourceLayoutPath);
801
+ if (!hasFile(changelogSourceLayoutDir, "layout") || isManagedGeneratedFile(changelogSourceLayoutPath)) {
802
+ mkdirSync(changelogSourceLayoutDir, { recursive: true });
803
+ writeFileSync(changelogSourceLayoutPath, CHANGELOG_SOURCE_LAYOUT_TEMPLATE);
804
+ }
805
+ }
806
+ }
807
+ }
401
808
  const ogEndpoint = readOgEndpoint(root);
402
809
  const remarkPlugins = ["remark-gfm", "remark-frontmatter"];
403
810
  if (ogEndpoint) remarkPlugins.push(["@farming-labs/next/mdx-plugins/remark-og", { endpoint: ogEndpoint }]);
@@ -450,6 +857,17 @@ function withDocs(nextConfig = {}) {
450
857
  const resolvedConfig = userWebpack ? userWebpack(config, options) : config;
451
858
  resolvedConfig.resolve ??= {};
452
859
  resolvedConfig.resolve.alias ??= {};
860
+ if (workspaceRoot) Object.assign(resolvedConfig.resolve.alias, {
861
+ "@farming-labs/docs$": join(workspaceRoot, "packages", "docs", "dist", "index.mjs"),
862
+ "@farming-labs/docs/server": join(workspaceRoot, "packages", "docs", "dist", "server.mjs"),
863
+ "@farming-labs/next$": join(workspaceRoot, "packages", "next", "dist", "index.mjs"),
864
+ "@farming-labs/next/api": join(workspaceRoot, "packages", "next", "dist", "api.mjs"),
865
+ "@farming-labs/next/changelog": join(workspaceRoot, "packages", "next", "dist", "changelog.mjs"),
866
+ "@farming-labs/next/client-callbacks": join(workspaceRoot, "packages", "next", "dist", "client-callbacks.mjs"),
867
+ "@farming-labs/next/layout": join(workspaceRoot, "packages", "next", "dist", "layout.mjs"),
868
+ "@farming-labs/theme$": join(workspaceRoot, "packages", "fumadocs", "dist", "index.mjs"),
869
+ "@farming-labs/theme/api": join(workspaceRoot, "packages", "fumadocs", "dist", "docs-api.mjs")
870
+ });
453
871
  resolvedConfig.resolve.alias[INTERNAL_DOCS_CONFIG_ALIAS] = docsConfigAbsolutePath;
454
872
  resolvedConfig.resolve.alias["fumadocs-openapi"] = FUMADOCS_OPENAPI_PACKAGE_ALIAS;
455
873
  resolvedConfig.resolve.alias["fumadocs-openapi/ui"] = FUMADOCS_OPENAPI_UI_ALIAS;
@@ -465,9 +883,17 @@ function withDocs(nextConfig = {}) {
465
883
  };
466
884
  const existingTracingIncludes = nextConfig.outputFileTracingIncludes ?? {};
467
885
  const docsTraceGlob = docsContentDir.replace(/\\/g, "/").replace(/^\.?\//, "") + "/**/*";
886
+ if (!isStaticExport) {
887
+ const existingRewrites = nextConfig.rewrites;
888
+ nextConfig.rewrites = async () => {
889
+ return mergeDocsMarkdownRewrites(entry, typeof existingRewrites === "function" ? await existingRewrites() : existingRewrites);
890
+ };
891
+ }
468
892
  nextConfig.outputFileTracingIncludes = {
469
893
  ...existingTracingIncludes,
470
894
  "/api/docs": [...new Set([...existingTracingIncludes["/api/docs"] ?? [], docsTraceGlob])],
895
+ "/api/docs/markdown": [...new Set([...existingTracingIncludes["/api/docs/markdown"] ?? [], docsTraceGlob])],
896
+ "/api/docs/markdown/[[...slug]]": [...new Set([...existingTracingIncludes["/api/docs/markdown/[[...slug]]"] ?? [], docsTraceGlob])],
471
897
  "/api/docs/mcp": [...new Set([...existingTracingIncludes["/api/docs/mcp"] ?? [], docsTraceGlob])]
472
898
  };
473
899
  return withMDX(nextConfig);
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { buildNextOpenApiDocument, createNextApiReference, createNextApiReferenceLayout, createNextApiReferencePage, getNextApiReferenceMode, getNextApiReferenceSourceState, resolveApiReferenceConfig, withNextApiReferenceBanner } from "./api-reference.mjs";
2
2
  import { createDocsAPI, createDocsMCPAPI } from "./api.mjs";
3
+ import { GeneratedChangelogEntry, createNextChangelogEntryMetadata, createNextChangelogEntryPage, createNextChangelogIndexMetadata, createNextChangelogIndexPage, createNextChangelogStaticParams } from "./changelog.mjs";
3
4
  import { withDocs } from "./config.mjs";
4
- export { buildNextOpenApiDocument, createDocsAPI, createDocsMCPAPI, createNextApiReference, createNextApiReferenceLayout, createNextApiReferencePage, getNextApiReferenceMode, getNextApiReferenceSourceState, resolveApiReferenceConfig, withDocs, withNextApiReferenceBanner };
5
+ export { type GeneratedChangelogEntry, buildNextOpenApiDocument, createDocsAPI, createDocsMCPAPI, createNextApiReference, createNextApiReferenceLayout, createNextApiReferencePage, createNextChangelogEntryMetadata, createNextChangelogEntryPage, createNextChangelogIndexMetadata, createNextChangelogIndexPage, createNextChangelogStaticParams, getNextApiReferenceMode, getNextApiReferenceSourceState, resolveApiReferenceConfig, withDocs, withNextApiReferenceBanner };
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { withDocs } from "./config.mjs";
2
2
  import { createDocsAPI, createDocsMCPAPI } from "./api.mjs";
3
3
  import { buildNextOpenApiDocument, createNextApiReference, createNextApiReferenceLayout, createNextApiReferencePage, getNextApiReferenceMode, getNextApiReferenceSourceState, resolveApiReferenceConfig, withNextApiReferenceBanner } from "./api-reference.mjs";
4
+ import { createNextChangelogEntryMetadata, createNextChangelogEntryPage, createNextChangelogIndexMetadata, createNextChangelogIndexPage, createNextChangelogStaticParams } from "./changelog.mjs";
4
5
 
5
- export { buildNextOpenApiDocument, createDocsAPI, createDocsMCPAPI, createNextApiReference, createNextApiReferenceLayout, createNextApiReferencePage, getNextApiReferenceMode, getNextApiReferenceSourceState, resolveApiReferenceConfig, withDocs, withNextApiReferenceBanner };
6
+ export { buildNextOpenApiDocument, createDocsAPI, createDocsMCPAPI, createNextApiReference, createNextApiReferenceLayout, createNextApiReferencePage, createNextChangelogEntryMetadata, createNextChangelogEntryPage, createNextChangelogIndexMetadata, createNextChangelogIndexPage, createNextChangelogStaticParams, getNextApiReferenceMode, getNextApiReferenceSourceState, resolveApiReferenceConfig, withDocs, withNextApiReferenceBanner };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/next",
3
- "version": "0.1.13",
3
+ "version": "0.1.16",
4
4
  "description": "Next.js adapter for @farming-labs/docs — MDX config wrapper",
5
5
  "keywords": [
6
6
  "docs",
@@ -34,6 +34,11 @@
34
34
  "import": "./dist/api.mjs",
35
35
  "default": "./dist/api.mjs"
36
36
  },
37
+ "./changelog": {
38
+ "types": "./dist/changelog.d.mts",
39
+ "import": "./dist/changelog.mjs",
40
+ "default": "./dist/changelog.mjs"
41
+ },
37
42
  "./client-callbacks": {
38
43
  "types": "./dist/client-callbacks.d.mts",
39
44
  "import": "./dist/client-callbacks.mjs",
@@ -78,6 +83,7 @@
78
83
  "fumadocs-openapi": "^10.7.0",
79
84
  "fumadocs-core": "^16.6.1",
80
85
  "fumadocs-ui": "^16.6.1",
86
+ "gray-matter": "^4.0.3",
81
87
  "remark-frontmatter": "^5.0.0",
82
88
  "remark-gfm": "^4.0.1",
83
89
  "remark-mdx-frontmatter": "^5.0.0"
@@ -89,8 +95,8 @@
89
95
  "tsdown": "^0.20.3",
90
96
  "typescript": "^5.9.3",
91
97
  "vitest": "^3.2.4",
92
- "@farming-labs/docs": "0.1.13",
93
- "@farming-labs/theme": "0.1.13"
98
+ "@farming-labs/docs": "0.1.16",
99
+ "@farming-labs/theme": "0.1.16"
94
100
  },
95
101
  "peerDependencies": {
96
102
  "@farming-labs/docs": ">=0.0.1",