create-fumadocs-app 16.1.6 → 16.1.7

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 (40) hide show
  1. package/dist/bin.js +2 -2
  2. package/dist/{constants-UZ9hhOMa.js → constants-wo9ZGmhE.js} +15 -14
  3. package/dist/constants-wo9ZGmhE.js.map +1 -0
  4. package/dist/index.js +1 -1
  5. package/dist/plugins/biome.js +1 -1
  6. package/dist/plugins/next-use-takumi.js +1 -1
  7. package/dist/plugins/orama-cloud.js +1 -1
  8. package/dist/plugins/oxlint.js +1 -1
  9. package/dist/{src-Dfp3MdVo.js → src-Dc0yUG4x.js} +2 -2
  10. package/dist/{src-Dfp3MdVo.js.map → src-Dc0yUG4x.js.map} +1 -1
  11. package/package.json +1 -1
  12. package/template/+next+fuma-docs-mdx/app/docs/[[...slug]]/page.tsx +2 -2
  13. package/template/+next+fuma-docs-mdx/app/og/docs/[...slug]/route.tsx +2 -2
  14. package/template/+next+fuma-docs-mdx/lib/source.ts +3 -3
  15. package/template/+next+fuma-docs-mdx/package.json +1 -1
  16. package/template/+next+fuma-docs-mdx+static/app/docs/[[...slug]]/page.tsx +2 -2
  17. package/template/+next+fuma-docs-mdx+static/app/og/docs/[...slug]/route.tsx +2 -2
  18. package/template/+next+fuma-docs-mdx+static/lib/source.ts +3 -3
  19. package/template/+next+fuma-docs-mdx+static/package.json +1 -1
  20. package/template/astro/package.json +1 -1
  21. package/template/astro/src/lib/source.ts +3 -6
  22. package/template/astro/src/pages/[...slug].astro +3 -2
  23. package/template/react-router/app/lib/shared.ts +6 -0
  24. package/template/react-router/app/lib/source.ts +1 -1
  25. package/template/react-router/app/routes/docs.tsx +2 -3
  26. package/template/react-router/react-router.config.ts +1 -1
  27. package/template/react-router-spa/app/lib/source.ts +1 -1
  28. package/template/tanstack-start/src/lib/shared.ts +21 -0
  29. package/template/tanstack-start/src/lib/source.ts +0 -23
  30. package/template/tanstack-start/src/routes/docs/$.tsx +3 -3
  31. package/template/tanstack-start/src/routes/docs/{$}[.]md.ts +3 -2
  32. package/template/tanstack-start/src/start.ts +2 -3
  33. package/template/tanstack-start-spa/src/lib/shared.ts +21 -0
  34. package/template/tanstack-start-spa/src/lib/source.ts +0 -23
  35. package/template/tanstack-start-spa/src/routes/docs/$.tsx +3 -3
  36. package/template/tanstack-start-spa/src/routes/docs/{$}[.]md.ts +3 -2
  37. package/template/waku/src/lib/source.ts +4 -7
  38. package/template/waku/src/pages/docs/[...slugs].tsx +2 -2
  39. package/dist/constants-UZ9hhOMa.js.map +0 -1
  40. package/template/react-router/app/lib/og.ts +0 -5
package/dist/bin.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { i as templates, n as isCI } from "./constants-UZ9hhOMa.js";
3
- import { n as getPackageManager, r as managers, t as create } from "./src-Dfp3MdVo.js";
2
+ import { i as templates, n as isCI } from "./constants-wo9ZGmhE.js";
3
+ import { n as getPackageManager, r as managers, t as create } from "./src-Dc0yUG4x.js";
4
4
  import fs from "node:fs/promises";
5
5
  import path from "node:path";
6
6
  import { cancel, confirm, group, intro, isCancel, outro, select, spinner, text } from "@clack/prompts";
@@ -47,7 +47,7 @@ var package_exports$2 = /* @__PURE__ */ __exportAll({
47
47
  version: () => version$2
48
48
  });
49
49
  var name$2 = "fumadocs-core";
50
- var version$2 = "16.12.0";
50
+ var version$2 = "16.12.1";
51
51
  var description$2 = "The React.js library for building a documentation website";
52
52
  var keywords$2 = ["Docs", "Fumadocs"];
53
53
  var homepage$2 = "https://fumadocs.dev";
@@ -449,7 +449,7 @@ var package_exports = /* @__PURE__ */ __exportAll({
449
449
  version: () => version
450
450
  });
451
451
  var name = "@fumadocs/base-ui";
452
- var version = "16.12.0";
452
+ var version = "16.12.1";
453
453
  var description = "The Base UI version of Fumadocs UI";
454
454
  var keywords = ["Docs", "Fumadocs"];
455
455
  var homepage = "https://fumadocs.dev";
@@ -649,16 +649,16 @@ const templates = [
649
649
  {
650
650
  value: "+next+fuma-docs-mdx",
651
651
  label: "Next.js: Fumadocs MDX",
652
- hint: "recommended",
652
+ hint: "recommended: powerful and mature",
653
653
  appDir: "",
654
654
  rootProviderPath: "app/layout.tsx"
655
655
  },
656
656
  {
657
- value: "astro",
658
- label: "Astro: React Islands",
659
- hint: "uses Astro Content Collections and Fumadocs UI as a React island",
657
+ value: "waku",
658
+ label: "Waku: Fumadocs MDX",
659
+ hint: "recommended: fast and simple",
660
660
  appDir: "src",
661
- rootProviderPath: "components/docs.tsx"
661
+ rootProviderPath: "components/provider.tsx"
662
662
  },
663
663
  {
664
664
  value: "+next+fuma-docs-mdx+static",
@@ -666,12 +666,6 @@ const templates = [
666
666
  appDir: "",
667
667
  rootProviderPath: "components/provider.tsx"
668
668
  },
669
- {
670
- value: "waku",
671
- label: "Waku: Fumadocs MDX",
672
- appDir: "src",
673
- rootProviderPath: "components/provider.tsx"
674
- },
675
669
  {
676
670
  value: "react-router",
677
671
  label: "React Router: Fumadocs MDX (not RSC)",
@@ -697,6 +691,13 @@ const templates = [
697
691
  hint: "SPA mode allows you to host the site statically, compatible with a CDN.",
698
692
  appDir: "src",
699
693
  rootProviderPath: "routes/__root.tsx"
694
+ },
695
+ {
696
+ value: "astro",
697
+ label: "Astro: React Islands",
698
+ hint: "partial support only, uses Astro Content Collections.",
699
+ appDir: "src",
700
+ rootProviderPath: "components/docs.tsx"
700
701
  }
701
702
  ];
702
703
  const workspaces = [
@@ -710,4 +711,4 @@ depVersions["fumadocs-ui"] = `npm:${name}@${version}`;
710
711
  //#endregion
711
712
  export { templates as i, isCI as n, sourceDir as r, depVersions as t };
712
713
 
713
- //# sourceMappingURL=constants-UZ9hhOMa.js.map
714
+ //# sourceMappingURL=constants-wo9ZGmhE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants-wo9ZGmhE.js","names":["corePkg","mdxPkg","basePkg","versionPkg.dependencies","basePkg.name","basePkg.version"],"sources":["../../create-app-versions/package.json","../../core/package.json","../../mdx/package.json","../../base-ui/package.json","../src/constants.ts"],"sourcesContent":["","","","","import { fileURLToPath } from 'node:url';\nimport versionPkg from '../../create-app-versions/package.json';\nimport * as corePkg from '../../core/package.json';\nimport * as mdxPkg from '../../mdx/package.json';\nimport * as basePkg from '../../base-ui/package.json';\n\nexport const sourceDir = fileURLToPath(new URL(`../`, import.meta.url).href);\n\nexport const isCI = Boolean(process.env.CI);\n\nexport interface TemplateInfo {\n value:\n | '+next+fuma-docs-mdx'\n | 'astro'\n | 'waku'\n | 'react-router'\n | 'react-router-spa'\n | 'tanstack-start'\n | 'tanstack-start-spa'\n | '+next+fuma-docs-mdx+static';\n label: string;\n appDir: string;\n /**\n * path to root provider, relative to `appDir``\n */\n rootProviderPath: string;\n hint?: string;\n /**\n * rename files when copying from template\n */\n rename?: (name: string) => string;\n}\n\nexport const templates: TemplateInfo[] = [\n {\n value: '+next+fuma-docs-mdx',\n label: 'Next.js: Fumadocs MDX',\n hint: 'recommended: powerful and mature',\n appDir: '',\n rootProviderPath: 'app/layout.tsx',\n },\n {\n value: 'waku',\n label: 'Waku: Fumadocs MDX',\n hint: 'recommended: fast and simple',\n appDir: 'src',\n rootProviderPath: 'components/provider.tsx',\n },\n {\n value: '+next+fuma-docs-mdx+static',\n label: 'Next.js Static: Fumadocs MDX',\n appDir: '',\n rootProviderPath: 'components/provider.tsx',\n },\n {\n value: 'react-router',\n label: 'React Router: Fumadocs MDX (not RSC)',\n appDir: 'app',\n rootProviderPath: 'root.tsx',\n },\n {\n value: 'react-router-spa',\n label: 'React Router SPA: Fumadocs MDX (not RSC)',\n hint: 'SPA mode allows you to host the site statically, compatible with a CDN.',\n appDir: 'app',\n rootProviderPath: 'root.tsx',\n },\n {\n value: 'tanstack-start',\n label: 'Tanstack Start: Fumadocs MDX (not RSC)',\n appDir: 'src',\n rootProviderPath: 'routes/__root.tsx',\n },\n {\n value: 'tanstack-start-spa',\n label: 'Tanstack Start SPA: Fumadocs MDX (not RSC)',\n hint: 'SPA mode allows you to host the site statically, compatible with a CDN.',\n appDir: 'src',\n rootProviderPath: 'routes/__root.tsx',\n },\n {\n value: 'astro',\n label: 'Astro: React Islands',\n hint: 'partial support only, uses Astro Content Collections.',\n appDir: 'src',\n rootProviderPath: 'components/docs.tsx',\n },\n];\n\nconst workspaces = [corePkg, mdxPkg, basePkg];\n\nexport const depVersions = versionPkg.dependencies;\n\nfor (const workspace of workspaces) {\n depVersions[workspace.name as keyof typeof depVersions] = workspace.version;\n}\n\ndepVersions['fumadocs-ui'] = `npm:${basePkg.name}@${basePkg.version}`;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AIMA,MAAa,YAAY,cAAc,IAAI,IAAI,OAAO,OAAO,KAAK,GAAG,CAAC,CAAC,IAAI;AAE3E,MAAa,OAAO,QAAQ,QAAQ,IAAI,EAAE;AAyB1C,MAAa,YAA4B;CACvC;EACE,OAAO;EACP,OAAO;EACP,MAAM;EACN,QAAQ;EACR,kBAAkB;CACpB;CACA;EACE,OAAO;EACP,OAAO;EACP,MAAM;EACN,QAAQ;EACR,kBAAkB;CACpB;CACA;EACE,OAAO;EACP,OAAO;EACP,QAAQ;EACR,kBAAkB;CACpB;CACA;EACE,OAAO;EACP,OAAO;EACP,QAAQ;EACR,kBAAkB;CACpB;CACA;EACE,OAAO;EACP,OAAO;EACP,MAAM;EACN,QAAQ;EACR,kBAAkB;CACpB;CACA;EACE,OAAO;EACP,OAAO;EACP,QAAQ;EACR,kBAAkB;CACpB;CACA;EACE,OAAO;EACP,OAAO;EACP,MAAM;EACN,QAAQ;EACR,kBAAkB;CACpB;CACA;EACE,OAAO;EACP,OAAO;EACP,MAAM;EACN,QAAQ;EACR,kBAAkB;CACpB;AACF;AAEA,MAAM,aAAa;CAACA;CAASC;CAAQC;AAAO;AAE5C,MAAa,cAAcC;AAE3B,KAAK,MAAM,aAAa,YACtB,YAAY,UAAU,QAAoC,UAAU;AAGtE,YAAY,iBAAiB,OAAOC,KAAa,GAAGC"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as create } from "./src-Dfp3MdVo.js";
1
+ import { t as create } from "./src-Dc0yUG4x.js";
2
2
  export { create };
@@ -1,4 +1,4 @@
1
- import { t as depVersions } from "../constants-UZ9hhOMa.js";
1
+ import { t as depVersions } from "../constants-wo9ZGmhE.js";
2
2
  import { i as writeFile, n as pick } from "../utils-BZh9vMXJ.js";
3
3
  import biome_base_default from "./biome.base.js";
4
4
  import biome_next_default from "./biome.next.js";
@@ -1,4 +1,4 @@
1
- import { t as depVersions } from "../constants-UZ9hhOMa.js";
1
+ import { t as depVersions } from "../constants-wo9ZGmhE.js";
2
2
  import { n as pick } from "../utils-BZh9vMXJ.js";
3
3
  import { readFile, writeFile } from "node:fs/promises";
4
4
  import { join } from "node:path";
@@ -1,4 +1,4 @@
1
- import { r as sourceDir, t as depVersions } from "../constants-UZ9hhOMa.js";
1
+ import { r as sourceDir, t as depVersions } from "../constants-wo9ZGmhE.js";
2
2
  import { i as writeFile$1, n as pick, t as copy } from "../utils-BZh9vMXJ.js";
3
3
  import { n as getCodeValue, t as createSourceFile } from "../shared-Bilr6W8M.js";
4
4
  import fs from "node:fs/promises";
@@ -1,4 +1,4 @@
1
- import { t as depVersions } from "../constants-UZ9hhOMa.js";
1
+ import { t as depVersions } from "../constants-wo9ZGmhE.js";
2
2
  import { n as pick } from "../utils-BZh9vMXJ.js";
3
3
  import { writeFile } from "node:fs/promises";
4
4
  import path from "node:path";
@@ -1,4 +1,4 @@
1
- import { i as templates, r as sourceDir, t as depVersions } from "./constants-UZ9hhOMa.js";
1
+ import { i as templates, r as sourceDir, t as depVersions } from "./constants-wo9ZGmhE.js";
2
2
  import { r as tryGitInit, t as copy } from "./utils-BZh9vMXJ.js";
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
@@ -86,4 +86,4 @@ async function initPackageJson(projectName, packageJsonPath) {
86
86
  //#endregion
87
87
  export { getPackageManager as n, managers as r, create as t };
88
88
 
89
- //# sourceMappingURL=src-Dfp3MdVo.js.map
89
+ //# sourceMappingURL=src-Dc0yUG4x.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"src-Dfp3MdVo.js","names":[],"sources":["../src/auto-install.ts","../src/index.ts"],"sourcesContent":["import { x } from 'tinyexec';\n\nexport type PackageManager = (typeof managers)[number];\n\nexport const managers = ['npm', 'yarn', 'bun', 'pnpm'] as const;\n\nexport function getPackageManager(): PackageManager {\n const userAgent = process.env.npm_config_user_agent ?? '';\n\n if (userAgent.startsWith('yarn')) {\n return 'yarn';\n }\n\n if (userAgent.startsWith('pnpm')) {\n return 'pnpm';\n }\n\n if (userAgent.startsWith('bun')) {\n return 'bun';\n }\n\n return 'npm';\n}\n\nexport async function autoInstall(manager: PackageManager, dest: string) {\n await x(manager, ['install'], {\n throwOnError: true,\n nodeOptions: {\n env: {\n ...process.env,\n NODE_ENV: 'development',\n DISABLE_OPENCOLLECTIVE: '1',\n },\n cwd: dest,\n },\n });\n}\n","import path from 'node:path';\nimport fs from 'node:fs/promises';\nimport { copy, tryGitInit } from '@/utils';\nimport type { PackageManager } from './auto-install';\nimport { autoInstall } from './auto-install';\nimport { depVersions, sourceDir, type TemplateInfo, templates } from './constants';\n\nexport type Template = TemplateInfo['value'];\nexport interface Options {\n outputDir: string;\n template: Template;\n\n /**\n * the package manager to use\n *\n * @defaultValue 'npm'\n */\n packageManager?: PackageManager;\n\n installDeps?: boolean;\n initializeGit?: boolean;\n log?: (message: string) => void;\n plugins?: TemplatePlugin[];\n}\n\nexport interface TemplatePluginContext {\n template: TemplateInfo;\n log: (message: string) => void;\n /**\n * output directory\n */\n dest: string;\n\n /**\n * output directory for app code (e.g. under `/src`)\n */\n appDir: string;\n}\n\nexport type PackageJsonType = {\n name?: string;\n version?: string;\n private?: boolean;\n scripts?: Record<string, string>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n} & Record<string, unknown>;\n\ntype Awaitable<T> = T | Promise<T>;\n\nexport interface TemplatePlugin {\n template?: (\n this: Pick<TemplatePluginContext, 'dest'>,\n info: TemplateInfo,\n ) => Awaitable<void | TemplateInfo>;\n packageJson?: (\n this: TemplatePluginContext,\n packageJson: PackageJsonType,\n ) => Awaitable<void | PackageJsonType>;\n afterWrite?: (this: TemplatePluginContext) => Awaitable<void>;\n readme?: (this: TemplatePluginContext, content: string) => Awaitable<void | string>;\n}\n\nexport async function create(createOptions: Options): Promise<void> {\n const {\n outputDir,\n plugins = [],\n packageManager = 'npm',\n initializeGit = false,\n installDeps = false,\n log = console.log,\n } = createOptions;\n\n let template = templates.find((item) => item.value === createOptions.template)!;\n for (const plugin of plugins) {\n template = (await plugin.template?.call({ dest: outputDir }, template)) ?? template;\n }\n\n const appDir = path.join(outputDir, template.appDir);\n const projectName = path.basename(outputDir);\n const pluginContext: TemplatePluginContext = {\n template,\n dest: outputDir,\n log,\n appDir,\n };\n\n await copy(path.join(sourceDir, 'template', template.value), outputDir, {\n rename(file) {\n file = file.replace('example.gitignore', '.gitignore');\n\n return template.rename?.(file) ?? file;\n },\n });\n\n const packageJsonPath = path.join(outputDir, 'package.json');\n let packageJson = await initPackageJson(projectName, packageJsonPath);\n for (const plugin of plugins) {\n packageJson = (await plugin.packageJson?.call(pluginContext, packageJson)) ?? packageJson;\n }\n await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));\n\n const readmePath = path.join(outputDir, 'README.md');\n let readme = `# ${projectName}\\n\\n${await fs.readFile(readmePath)}`;\n for (const plugin of plugins) {\n readme = (await plugin.readme?.call(pluginContext, readme)) ?? readme;\n }\n await fs.writeFile(readmePath, readme);\n\n for (const plugin of plugins) {\n await plugin.afterWrite?.call(pluginContext);\n }\n\n if (installDeps) {\n try {\n await autoInstall(packageManager, outputDir);\n log('Installed dependencies');\n } catch (err) {\n log(`Failed to install dependencies: ${err}`);\n }\n }\n\n if (initializeGit && (await tryGitInit(outputDir))) {\n log('Initialized Git repository');\n }\n}\n\nasync function initPackageJson(\n projectName: string,\n packageJsonPath: string,\n): Promise<PackageJsonType> {\n function replaceWorkspaceDeps(deps: Record<string, string> = {}) {\n for (const k in deps) {\n if (deps[k].startsWith('workspace:') && k in depVersions) {\n deps[k] = depVersions[k as keyof typeof depVersions];\n }\n }\n\n return deps;\n }\n\n const packageJson: PackageJsonType = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));\n const dependencies = replaceWorkspaceDeps(packageJson.dependencies);\n const devDependencies = replaceWorkspaceDeps(packageJson.devDependencies);\n const scripts = { ...packageJson.scripts };\n\n if (dependencies['fumadocs-mdx'] || devDependencies['fumadocs-mdx']) {\n scripts.postinstall = 'fumadocs-mdx';\n }\n\n return {\n ...packageJson,\n name: projectName,\n scripts,\n dependencies,\n devDependencies,\n };\n}\n"],"mappings":";;;;;;AAIA,MAAa,WAAW;CAAC;CAAO;CAAQ;CAAO;AAAM;AAErD,SAAgB,oBAAoC;CAClD,MAAM,YAAY,QAAQ,IAAI,yBAAyB;CAEvD,IAAI,UAAU,WAAW,MAAM,GAC7B,OAAO;CAGT,IAAI,UAAU,WAAW,MAAM,GAC7B,OAAO;CAGT,IAAI,UAAU,WAAW,KAAK,GAC5B,OAAO;CAGT,OAAO;AACT;AAEA,eAAsB,YAAY,SAAyB,MAAc;CACvE,MAAM,EAAE,SAAS,CAAC,SAAS,GAAG;EAC5B,cAAc;EACd,aAAa;GACX,KAAK;IACH,GAAG,QAAQ;IACX,UAAU;IACV,wBAAwB;GAC1B;GACA,KAAK;EACP;CACF,CAAC;AACH;;;AC2BA,eAAsB,OAAO,eAAuC;CAClE,MAAM,EACJ,WACA,UAAU,CAAC,GACX,iBAAiB,OACjB,gBAAgB,OAChB,cAAc,OACd,MAAM,QAAQ,QACZ;CAEJ,IAAI,WAAW,UAAU,MAAM,SAAS,KAAK,UAAU,cAAc,QAAQ;CAC7E,KAAK,MAAM,UAAU,SACnB,WAAY,MAAM,OAAO,UAAU,KAAK,EAAE,MAAM,UAAU,GAAG,QAAQ,KAAM;CAG7E,MAAM,SAAS,KAAK,KAAK,WAAW,SAAS,MAAM;CACnD,MAAM,cAAc,KAAK,SAAS,SAAS;CAC3C,MAAM,gBAAuC;EAC3C;EACA,MAAM;EACN;EACA;CACF;CAEA,MAAM,KAAK,KAAK,KAAK,WAAW,YAAY,SAAS,KAAK,GAAG,WAAW,EACtE,OAAO,MAAM;EACX,OAAO,KAAK,QAAQ,qBAAqB,YAAY;EAErD,OAAO,SAAS,SAAS,IAAI,KAAK;CACpC,EACF,CAAC;CAED,MAAM,kBAAkB,KAAK,KAAK,WAAW,cAAc;CAC3D,IAAI,cAAc,MAAM,gBAAgB,aAAa,eAAe;CACpE,KAAK,MAAM,UAAU,SACnB,cAAe,MAAM,OAAO,aAAa,KAAK,eAAe,WAAW,KAAM;CAEhF,MAAM,GAAG,UAAU,iBAAiB,KAAK,UAAU,aAAa,MAAM,CAAC,CAAC;CAExE,MAAM,aAAa,KAAK,KAAK,WAAW,WAAW;CACnD,IAAI,SAAS,KAAK,YAAY,MAAM,MAAM,GAAG,SAAS,UAAU;CAChE,KAAK,MAAM,UAAU,SACnB,SAAU,MAAM,OAAO,QAAQ,KAAK,eAAe,MAAM,KAAM;CAEjE,MAAM,GAAG,UAAU,YAAY,MAAM;CAErC,KAAK,MAAM,UAAU,SACnB,MAAM,OAAO,YAAY,KAAK,aAAa;CAG7C,IAAI,aACF,IAAI;EACF,MAAM,YAAY,gBAAgB,SAAS;EAC3C,IAAI,wBAAwB;CAC9B,SAAS,KAAK;EACZ,IAAI,mCAAmC,KAAK;CAC9C;CAGF,IAAI,iBAAkB,MAAM,WAAW,SAAS,GAC9C,IAAI,4BAA4B;AAEpC;AAEA,eAAe,gBACb,aACA,iBAC0B;CAC1B,SAAS,qBAAqB,OAA+B,CAAC,GAAG;EAC/D,KAAK,MAAM,KAAK,MACd,IAAI,KAAK,EAAE,CAAC,WAAW,YAAY,KAAK,KAAK,aAC3C,KAAK,KAAK,YAAY;EAI1B,OAAO;CACT;CAEA,MAAM,cAA+B,KAAK,MAAM,MAAM,GAAG,SAAS,iBAAiB,OAAO,CAAC;CAC3F,MAAM,eAAe,qBAAqB,YAAY,YAAY;CAClE,MAAM,kBAAkB,qBAAqB,YAAY,eAAe;CACxE,MAAM,UAAU,EAAE,GAAG,YAAY,QAAQ;CAEzC,IAAI,aAAa,mBAAmB,gBAAgB,iBAClD,QAAQ,cAAc;CAGxB,OAAO;EACL,GAAG;EACH,MAAM;EACN;EACA;EACA;CACF;AACF"}
1
+ {"version":3,"file":"src-Dc0yUG4x.js","names":[],"sources":["../src/auto-install.ts","../src/index.ts"],"sourcesContent":["import { x } from 'tinyexec';\n\nexport type PackageManager = (typeof managers)[number];\n\nexport const managers = ['npm', 'yarn', 'bun', 'pnpm'] as const;\n\nexport function getPackageManager(): PackageManager {\n const userAgent = process.env.npm_config_user_agent ?? '';\n\n if (userAgent.startsWith('yarn')) {\n return 'yarn';\n }\n\n if (userAgent.startsWith('pnpm')) {\n return 'pnpm';\n }\n\n if (userAgent.startsWith('bun')) {\n return 'bun';\n }\n\n return 'npm';\n}\n\nexport async function autoInstall(manager: PackageManager, dest: string) {\n await x(manager, ['install'], {\n throwOnError: true,\n nodeOptions: {\n env: {\n ...process.env,\n NODE_ENV: 'development',\n DISABLE_OPENCOLLECTIVE: '1',\n },\n cwd: dest,\n },\n });\n}\n","import path from 'node:path';\nimport fs from 'node:fs/promises';\nimport { copy, tryGitInit } from '@/utils';\nimport type { PackageManager } from './auto-install';\nimport { autoInstall } from './auto-install';\nimport { depVersions, sourceDir, type TemplateInfo, templates } from './constants';\n\nexport type Template = TemplateInfo['value'];\nexport interface Options {\n outputDir: string;\n template: Template;\n\n /**\n * the package manager to use\n *\n * @defaultValue 'npm'\n */\n packageManager?: PackageManager;\n\n installDeps?: boolean;\n initializeGit?: boolean;\n log?: (message: string) => void;\n plugins?: TemplatePlugin[];\n}\n\nexport interface TemplatePluginContext {\n template: TemplateInfo;\n log: (message: string) => void;\n /**\n * output directory\n */\n dest: string;\n\n /**\n * output directory for app code (e.g. under `/src`)\n */\n appDir: string;\n}\n\nexport type PackageJsonType = {\n name?: string;\n version?: string;\n private?: boolean;\n scripts?: Record<string, string>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n} & Record<string, unknown>;\n\ntype Awaitable<T> = T | Promise<T>;\n\nexport interface TemplatePlugin {\n template?: (\n this: Pick<TemplatePluginContext, 'dest'>,\n info: TemplateInfo,\n ) => Awaitable<void | TemplateInfo>;\n packageJson?: (\n this: TemplatePluginContext,\n packageJson: PackageJsonType,\n ) => Awaitable<void | PackageJsonType>;\n afterWrite?: (this: TemplatePluginContext) => Awaitable<void>;\n readme?: (this: TemplatePluginContext, content: string) => Awaitable<void | string>;\n}\n\nexport async function create(createOptions: Options): Promise<void> {\n const {\n outputDir,\n plugins = [],\n packageManager = 'npm',\n initializeGit = false,\n installDeps = false,\n log = console.log,\n } = createOptions;\n\n let template = templates.find((item) => item.value === createOptions.template)!;\n for (const plugin of plugins) {\n template = (await plugin.template?.call({ dest: outputDir }, template)) ?? template;\n }\n\n const appDir = path.join(outputDir, template.appDir);\n const projectName = path.basename(outputDir);\n const pluginContext: TemplatePluginContext = {\n template,\n dest: outputDir,\n log,\n appDir,\n };\n\n await copy(path.join(sourceDir, 'template', template.value), outputDir, {\n rename(file) {\n file = file.replace('example.gitignore', '.gitignore');\n\n return template.rename?.(file) ?? file;\n },\n });\n\n const packageJsonPath = path.join(outputDir, 'package.json');\n let packageJson = await initPackageJson(projectName, packageJsonPath);\n for (const plugin of plugins) {\n packageJson = (await plugin.packageJson?.call(pluginContext, packageJson)) ?? packageJson;\n }\n await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));\n\n const readmePath = path.join(outputDir, 'README.md');\n let readme = `# ${projectName}\\n\\n${await fs.readFile(readmePath)}`;\n for (const plugin of plugins) {\n readme = (await plugin.readme?.call(pluginContext, readme)) ?? readme;\n }\n await fs.writeFile(readmePath, readme);\n\n for (const plugin of plugins) {\n await plugin.afterWrite?.call(pluginContext);\n }\n\n if (installDeps) {\n try {\n await autoInstall(packageManager, outputDir);\n log('Installed dependencies');\n } catch (err) {\n log(`Failed to install dependencies: ${err}`);\n }\n }\n\n if (initializeGit && (await tryGitInit(outputDir))) {\n log('Initialized Git repository');\n }\n}\n\nasync function initPackageJson(\n projectName: string,\n packageJsonPath: string,\n): Promise<PackageJsonType> {\n function replaceWorkspaceDeps(deps: Record<string, string> = {}) {\n for (const k in deps) {\n if (deps[k].startsWith('workspace:') && k in depVersions) {\n deps[k] = depVersions[k as keyof typeof depVersions];\n }\n }\n\n return deps;\n }\n\n const packageJson: PackageJsonType = JSON.parse(await fs.readFile(packageJsonPath, 'utf-8'));\n const dependencies = replaceWorkspaceDeps(packageJson.dependencies);\n const devDependencies = replaceWorkspaceDeps(packageJson.devDependencies);\n const scripts = { ...packageJson.scripts };\n\n if (dependencies['fumadocs-mdx'] || devDependencies['fumadocs-mdx']) {\n scripts.postinstall = 'fumadocs-mdx';\n }\n\n return {\n ...packageJson,\n name: projectName,\n scripts,\n dependencies,\n devDependencies,\n };\n}\n"],"mappings":";;;;;;AAIA,MAAa,WAAW;CAAC;CAAO;CAAQ;CAAO;AAAM;AAErD,SAAgB,oBAAoC;CAClD,MAAM,YAAY,QAAQ,IAAI,yBAAyB;CAEvD,IAAI,UAAU,WAAW,MAAM,GAC7B,OAAO;CAGT,IAAI,UAAU,WAAW,MAAM,GAC7B,OAAO;CAGT,IAAI,UAAU,WAAW,KAAK,GAC5B,OAAO;CAGT,OAAO;AACT;AAEA,eAAsB,YAAY,SAAyB,MAAc;CACvE,MAAM,EAAE,SAAS,CAAC,SAAS,GAAG;EAC5B,cAAc;EACd,aAAa;GACX,KAAK;IACH,GAAG,QAAQ;IACX,UAAU;IACV,wBAAwB;GAC1B;GACA,KAAK;EACP;CACF,CAAC;AACH;;;AC2BA,eAAsB,OAAO,eAAuC;CAClE,MAAM,EACJ,WACA,UAAU,CAAC,GACX,iBAAiB,OACjB,gBAAgB,OAChB,cAAc,OACd,MAAM,QAAQ,QACZ;CAEJ,IAAI,WAAW,UAAU,MAAM,SAAS,KAAK,UAAU,cAAc,QAAQ;CAC7E,KAAK,MAAM,UAAU,SACnB,WAAY,MAAM,OAAO,UAAU,KAAK,EAAE,MAAM,UAAU,GAAG,QAAQ,KAAM;CAG7E,MAAM,SAAS,KAAK,KAAK,WAAW,SAAS,MAAM;CACnD,MAAM,cAAc,KAAK,SAAS,SAAS;CAC3C,MAAM,gBAAuC;EAC3C;EACA,MAAM;EACN;EACA;CACF;CAEA,MAAM,KAAK,KAAK,KAAK,WAAW,YAAY,SAAS,KAAK,GAAG,WAAW,EACtE,OAAO,MAAM;EACX,OAAO,KAAK,QAAQ,qBAAqB,YAAY;EAErD,OAAO,SAAS,SAAS,IAAI,KAAK;CACpC,EACF,CAAC;CAED,MAAM,kBAAkB,KAAK,KAAK,WAAW,cAAc;CAC3D,IAAI,cAAc,MAAM,gBAAgB,aAAa,eAAe;CACpE,KAAK,MAAM,UAAU,SACnB,cAAe,MAAM,OAAO,aAAa,KAAK,eAAe,WAAW,KAAM;CAEhF,MAAM,GAAG,UAAU,iBAAiB,KAAK,UAAU,aAAa,MAAM,CAAC,CAAC;CAExE,MAAM,aAAa,KAAK,KAAK,WAAW,WAAW;CACnD,IAAI,SAAS,KAAK,YAAY,MAAM,MAAM,GAAG,SAAS,UAAU;CAChE,KAAK,MAAM,UAAU,SACnB,SAAU,MAAM,OAAO,QAAQ,KAAK,eAAe,MAAM,KAAM;CAEjE,MAAM,GAAG,UAAU,YAAY,MAAM;CAErC,KAAK,MAAM,UAAU,SACnB,MAAM,OAAO,YAAY,KAAK,aAAa;CAG7C,IAAI,aACF,IAAI;EACF,MAAM,YAAY,gBAAgB,SAAS;EAC3C,IAAI,wBAAwB;CAC9B,SAAS,KAAK;EACZ,IAAI,mCAAmC,KAAK;CAC9C;CAGF,IAAI,iBAAkB,MAAM,WAAW,SAAS,GAC9C,IAAI,4BAA4B;AAEpC;AAEA,eAAe,gBACb,aACA,iBAC0B;CAC1B,SAAS,qBAAqB,OAA+B,CAAC,GAAG;EAC/D,KAAK,MAAM,KAAK,MACd,IAAI,KAAK,EAAE,CAAC,WAAW,YAAY,KAAK,KAAK,aAC3C,KAAK,KAAK,YAAY;EAI1B,OAAO;CACT;CAEA,MAAM,cAA+B,KAAK,MAAM,MAAM,GAAG,SAAS,iBAAiB,OAAO,CAAC;CAC3F,MAAM,eAAe,qBAAqB,YAAY,YAAY;CAClE,MAAM,kBAAkB,qBAAqB,YAAY,eAAe;CACxE,MAAM,UAAU,EAAE,GAAG,YAAY,QAAQ;CAEzC,IAAI,aAAa,mBAAmB,gBAAgB,iBAClD,QAAQ,cAAc;CAGxB,OAAO;EACL,GAAG;EACH,MAAM;EACN;EACA;EACA;CACF;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-fumadocs-app",
3
- "version": "16.1.6",
3
+ "version": "16.1.7",
4
4
  "description": "Create a new documentation site with Fumadocs",
5
5
  "keywords": [
6
6
  "Docs",
@@ -1,4 +1,4 @@
1
- import { getPageImage, getPageMarkdownUrl, source } from '@/lib/source';
1
+ import { getPageImageUrl, getPageMarkdownUrl, source } from '@/lib/source';
2
2
  import {
3
3
  DocsBody,
4
4
  DocsDescription,
@@ -57,7 +57,7 @@ export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): P
57
57
  title: page.data.title,
58
58
  description: page.data.description,
59
59
  openGraph: {
60
- images: getPageImage(page).url,
60
+ images: getPageImageUrl(page).url,
61
61
  },
62
62
  };
63
63
  }
@@ -1,4 +1,4 @@
1
- import { getPageImage, source } from '@/lib/source';
1
+ import { getPageImageUrl, source } from '@/lib/source';
2
2
  import { notFound } from 'next/navigation';
3
3
  import { ImageResponse } from 'next/og';
4
4
  import { generate as DefaultImage } from 'fumadocs-ui/og';
@@ -23,6 +23,6 @@ export async function GET(_req: Request, { params }: RouteContext<'/og/docs/[...
23
23
  export function generateStaticParams() {
24
24
  return source.getPages().map((page) => ({
25
25
  lang: page.locale,
26
- slug: getPageImage(page).segments,
26
+ slug: getPageImageUrl(page).segments,
27
27
  }));
28
28
  }
@@ -10,12 +10,12 @@ export const source = loader({
10
10
  plugins: [lucideIconsPlugin()],
11
11
  });
12
12
 
13
- export function getPageImage(page: (typeof source)['$inferPage']) {
13
+ export function getPageImageUrl(page: (typeof source)['$inferPage']) {
14
14
  const segments = [...page.slugs, 'image.png'];
15
15
 
16
16
  return {
17
17
  segments,
18
- url: `${docsImageRoute}/${segments.join('/')}`,
18
+ url: '/' + [page.locale, ...docsImageRoute.split('/'), ...segments].filter(Boolean).join('/'),
19
19
  };
20
20
  }
21
21
 
@@ -24,7 +24,7 @@ export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
24
24
 
25
25
  return {
26
26
  segments,
27
- url: `${docsContentRoute}/${segments.join('/')}`,
27
+ url: '/' + [page.locale, ...docsContentRoute.split('/'), ...segments].filter(Boolean).join('/'),
28
28
  };
29
29
  }
30
30
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "example-next",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "private": true,
5
5
  "scripts": {
6
6
  "build": "next build",
@@ -1,4 +1,4 @@
1
- import { getPageImage, getPageMarkdownUrl, source } from '@/lib/source';
1
+ import { getPageImageUrl, getPageMarkdownUrl, source } from '@/lib/source';
2
2
  import {
3
3
  DocsBody,
4
4
  DocsDescription,
@@ -57,7 +57,7 @@ export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): P
57
57
  title: page.data.title,
58
58
  description: page.data.description,
59
59
  openGraph: {
60
- images: getPageImage(page).url,
60
+ images: getPageImageUrl(page).url,
61
61
  },
62
62
  };
63
63
  }
@@ -1,4 +1,4 @@
1
- import { getPageImage, source } from '@/lib/source';
1
+ import { getPageImageUrl, source } from '@/lib/source';
2
2
  import { notFound } from 'next/navigation';
3
3
  import { ImageResponse } from 'next/og';
4
4
  import { generate as DefaultImage } from 'fumadocs-ui/og';
@@ -23,6 +23,6 @@ export async function GET(_req: Request, { params }: RouteContext<'/og/docs/[...
23
23
  export function generateStaticParams() {
24
24
  return source.getPages().map((page) => ({
25
25
  lang: page.locale,
26
- slug: getPageImage(page).segments,
26
+ slug: getPageImageUrl(page).segments,
27
27
  }));
28
28
  }
@@ -9,12 +9,12 @@ export const source = loader({
9
9
  plugins: [],
10
10
  });
11
11
 
12
- export function getPageImage(page: (typeof source)['$inferPage']) {
12
+ export function getPageImageUrl(page: (typeof source)['$inferPage']) {
13
13
  const segments = [...page.slugs, 'image.png'];
14
14
 
15
15
  return {
16
16
  segments,
17
- url: `${docsImageRoute}/${segments.join('/')}`,
17
+ url: '/' + [page.locale, ...docsImageRoute.split('/'), ...segments].filter(Boolean).join('/'),
18
18
  };
19
19
  }
20
20
 
@@ -23,7 +23,7 @@ export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
23
23
 
24
24
  return {
25
25
  segments,
26
- url: `${docsContentRoute}/${segments.join('/')}`,
26
+ url: '/' + [page.locale, ...docsContentRoute.split('/'), ...segments].filter(Boolean).join('/'),
27
27
  };
28
28
  }
29
29
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "example-next-static",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "private": true,
5
5
  "scripts": {
6
6
  "build": "next build",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "example-astro",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -13,13 +13,10 @@ export function getStructuredData(entry: CollectionEntry<'docs'>): StructuredDat
13
13
  return structure(entry.body);
14
14
  }
15
15
 
16
- export function getPageImage(slugs: string[]) {
17
- const segments = [...slugs, 'image.webp'];
16
+ export function getPageImageUrl(page: (typeof source)['$inferPage']) {
17
+ const segments = [...page.slugs, 'image.webp'];
18
18
 
19
- return {
20
- segments,
21
- url: `/og/docs/${segments.join('/')}`,
22
- };
19
+ return '/' + [page.locale, 'og', 'docs', ...segments].filter(Boolean).join('/');
23
20
  }
24
21
 
25
22
  async function createMySource() {
@@ -3,7 +3,7 @@ import { render, type CollectionEntry } from "astro:content";
3
3
  import { Docs } from "@/components/docs";
4
4
  import defaultMdxComponents from "fumadocs-ui/mdx";
5
5
  import "@/styles/global.css";
6
- import { getPageImage, source } from "@/lib/source";
6
+ import { getPageImageUrl, source } from "@/lib/source";
7
7
  import Layout from "@/components/layout.astro";
8
8
  import type { TOCItemType } from "fumadocs-core/toc";
9
9
 
@@ -21,7 +21,8 @@ export async function getStaticPaths() {
21
21
  const { page } = Astro.props;
22
22
  const { Content, headings } = await render(page);
23
23
  const slugs = typeof Astro.params.slug === "string" ? Astro.params.slug.split("/") : [];
24
- const image = getPageImage(slugs).url;
24
+ const docsPage = source.getPage(slugs)!;
25
+ const image = getPageImageUrl(docsPage);
25
26
 
26
27
  const toc: TOCItemType[] = headings.map((heading) => ({
27
28
  depth: heading.depth,
@@ -9,3 +9,9 @@ export const gitConfig = {
9
9
  repo: 'fumadocs',
10
10
  branch: 'main',
11
11
  };
12
+
13
+ export function getPageImagePath(slugs: string[], locale?: string) {
14
+ return (
15
+ '/' + [locale, ...docsImageRoute.split('/'), ...slugs, 'image.webp'].filter(Boolean).join('/')
16
+ );
17
+ }
@@ -12,7 +12,7 @@ export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
12
12
 
13
13
  return {
14
14
  segments,
15
- url: `${docsContentRoute}/${segments.join('/')}`,
15
+ url: '/' + [page.locale, ...docsContentRoute.split('/'), ...segments].filter(Boolean).join('/'),
16
16
  };
17
17
  }
18
18
 
@@ -11,9 +11,8 @@ import {
11
11
  import { getPageMarkdownUrl, source } from '@/lib/source';
12
12
  import browserCollections from 'collections/browser';
13
13
  import { baseOptions } from '@/lib/layout.shared';
14
- import { gitConfig } from '@/lib/shared';
14
+ import { gitConfig, getPageImagePath } from '@/lib/shared';
15
15
  import { useFumadocsLoader } from 'fumadocs-core/source/client';
16
- import { getPageImagePath } from '@/lib/og';
17
16
  import { useMDXComponents } from '@/components/mdx';
18
17
 
19
18
  export async function loader({ params }: Route.LoaderArgs) {
@@ -25,7 +24,7 @@ export async function loader({ params }: Route.LoaderArgs) {
25
24
  path: page.path,
26
25
  markdownUrl: getPageMarkdownUrl(page).url,
27
26
  pageTree: await source.serializePageTree(source.getPageTree()),
28
- imagePath: getPageImagePath(slugs),
27
+ imagePath: getPageImagePath(page.slugs, page.locale),
29
28
  };
30
29
  }
31
30
 
@@ -1,7 +1,7 @@
1
1
  import type { Config } from '@react-router/dev/config';
2
2
  import { glob } from 'node:fs/promises';
3
3
  import { createGetUrl, getSlugs } from 'fumadocs-core/source';
4
- import { getPageImagePath } from './app/lib/og';
4
+ import { getPageImagePath } from '@/lib/shared';
5
5
 
6
6
  const getUrl = createGetUrl('/docs');
7
7
 
@@ -12,7 +12,7 @@ export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
12
12
 
13
13
  return {
14
14
  segments,
15
- url: `${docsContentRoute}/${segments.join('/')}`,
15
+ url: '/' + [page.locale, ...docsContentRoute.split('/'), ...segments].filter(Boolean).join('/'),
16
16
  };
17
17
  }
18
18
 
@@ -8,3 +8,24 @@ export const gitConfig = {
8
8
  repo: 'fumadocs',
9
9
  branch: 'main',
10
10
  };
11
+
12
+ export function encodeMarkdownUrl(slugs: string[], locale?: string) {
13
+ const segments = [...slugs];
14
+ if (segments.length === 0) {
15
+ segments.push('index.md');
16
+ } else {
17
+ segments[segments.length - 1] += '.md';
18
+ }
19
+
20
+ return '/' + [locale, ...docsRoute.split('/'), ...segments].filter(Boolean).join('/');
21
+ }
22
+
23
+ /** @returns page slugs */
24
+ export function decodeMarkdownUrl(segments: string[]) {
25
+ if (segments.length === 0) return [];
26
+
27
+ const out = [...segments];
28
+ out[out.length - 1] = out[out.length - 1].replace(/\.md$/, '');
29
+ if (out.length === 1 && out[0] === 'index') out.pop();
30
+ return out;
31
+ }
@@ -9,29 +9,6 @@ export const source = loader({
9
9
  plugins: [lucideIconsPlugin()],
10
10
  });
11
11
 
12
- export function markdownPathToSlugs(segs: string[]) {
13
- if (segs.length === 0) return [];
14
-
15
- const out = [...segs];
16
- out[out.length - 1] = out[out.length - 1].replace(/\.md$/, '');
17
- if (out.length === 1 && out[0] === 'index') out.pop();
18
- return out;
19
- }
20
-
21
- export function slugsToMarkdownPath(slugs: string[]) {
22
- const segments = [...slugs];
23
- if (segments.length === 0) {
24
- segments.push('index.md');
25
- } else {
26
- segments[segments.length - 1] += '.md';
27
- }
28
-
29
- return {
30
- segments,
31
- url: `${docsRoute}/${segments.join('/')}`,
32
- };
33
- }
34
-
35
12
  export async function getLLMText(page: (typeof source)['$inferPage']) {
36
13
  const processed = await page.data.getText('processed');
37
14
 
@@ -1,7 +1,7 @@
1
1
  import { createFileRoute, notFound } from '@tanstack/react-router';
2
2
  import { DocsLayout } from 'fumadocs-ui/layouts/docs';
3
3
  import { createServerFn } from '@tanstack/react-start';
4
- import { slugsToMarkdownPath, source } from '@/lib/source';
4
+ import { source } from '@/lib/source';
5
5
  import browserCollections from 'collections/browser';
6
6
  import {
7
7
  DocsBody,
@@ -12,7 +12,7 @@ import {
12
12
  ViewOptionsPopover,
13
13
  } from 'fumadocs-ui/layouts/docs/page';
14
14
  import { baseOptions } from '@/lib/layout.shared';
15
- import { gitConfig } from '@/lib/shared';
15
+ import { encodeMarkdownUrl, gitConfig } from '@/lib/shared';
16
16
  import { useFumadocsLoader } from 'fumadocs-core/source/client';
17
17
  import { Suspense } from 'react';
18
18
  import { useMDXComponents } from '@/components/mdx';
@@ -37,7 +37,7 @@ const serverLoader = createServerFn({
37
37
 
38
38
  return {
39
39
  path: page.path,
40
- markdownUrl: slugsToMarkdownPath(page.slugs).url,
40
+ markdownUrl: encodeMarkdownUrl(page.slugs, page.locale),
41
41
  pageTree: await source.serializePageTree(source.getPageTree()),
42
42
  };
43
43
  });
@@ -1,11 +1,12 @@
1
1
  import { createFileRoute, notFound } from '@tanstack/react-router';
2
- import { getLLMText, markdownPathToSlugs, source } from '@/lib/source';
2
+ import { getLLMText, source } from '@/lib/source';
3
+ import { decodeMarkdownUrl } from '@/lib/shared';
3
4
 
4
5
  export const Route = createFileRoute('/docs/{$}.md')({
5
6
  server: {
6
7
  handlers: {
7
8
  GET: async ({ params }) => {
8
- const slugs = markdownPathToSlugs(params._splat?.split('/') ?? []);
9
+ const slugs = decodeMarkdownUrl(params._splat?.split('/') ?? []);
9
10
  const page = source.getPage(slugs);
10
11
  if (!page) throw notFound();
11
12
 
@@ -1,8 +1,7 @@
1
1
  import { createMiddleware, createCsrfMiddleware, createStart } from '@tanstack/react-start';
2
2
  import { isMarkdownPreferred } from 'fumadocs-core/negotiation';
3
3
  import { redirect } from '@tanstack/react-router';
4
- import { docsRoute } from '@/lib/shared';
5
- import { slugsToMarkdownPath } from './lib/source';
4
+ import { docsRoute, encodeMarkdownUrl } from '@/lib/shared';
6
5
 
7
6
  const csrfMiddleware = createCsrfMiddleware({
8
7
  filter: (ctx) => ctx.handlerType === 'serverFn',
@@ -20,7 +19,7 @@ const llmMiddleware = createMiddleware().server(({ next, request }) => {
20
19
  .slice(docsRoute.length)
21
20
  .split('/')
22
21
  .filter((v) => v.length > 0);
23
- url.pathname = slugsToMarkdownPath(slugs).url;
22
+ url.pathname = encodeMarkdownUrl(slugs);
24
23
 
25
24
  throw redirect(url);
26
25
  }
@@ -8,3 +8,24 @@ export const gitConfig = {
8
8
  repo: 'fumadocs',
9
9
  branch: 'main',
10
10
  };
11
+
12
+ export function encodeMarkdownUrl(slugs: string[], locale?: string) {
13
+ const segments = [...slugs];
14
+ if (segments.length === 0) {
15
+ segments.push('index.md');
16
+ } else {
17
+ segments[segments.length - 1] += '.md';
18
+ }
19
+
20
+ return '/' + [locale, ...docsRoute.split('/'), ...segments].filter(Boolean).join('/');
21
+ }
22
+
23
+ /** @returns page slugs */
24
+ export function decodeMarkdownUrl(segments: string[]) {
25
+ if (segments.length === 0) return [];
26
+
27
+ const out = [...segments];
28
+ out[out.length - 1] = out[out.length - 1].replace(/\.md$/, '');
29
+ if (out.length === 1 && out[0] === 'index') out.pop();
30
+ return out;
31
+ }
@@ -9,29 +9,6 @@ export const source = loader({
9
9
  plugins: [lucideIconsPlugin()],
10
10
  });
11
11
 
12
- export function markdownPathToSlugs(segs: string[]) {
13
- if (segs.length === 0) return [];
14
-
15
- const out = [...segs];
16
- out[out.length - 1] = out[out.length - 1].replace(/\.md$/, '');
17
- if (out.length === 1 && out[0] === 'index') out.pop();
18
- return out;
19
- }
20
-
21
- export function slugsToMarkdownPath(slugs: string[]) {
22
- const segments = [...slugs];
23
- if (segments.length === 0) {
24
- segments.push('index.md');
25
- } else {
26
- segments[segments.length - 1] += '.md';
27
- }
28
-
29
- return {
30
- segments,
31
- url: `${docsRoute}/${segments.join('/')}`,
32
- };
33
- }
34
-
35
12
  export async function getLLMText(page: (typeof source)['$inferPage']) {
36
13
  const processed = await page.data.getText('processed');
37
14
 
@@ -1,7 +1,7 @@
1
1
  import { createFileRoute, Link, notFound } from '@tanstack/react-router';
2
2
  import { DocsLayout } from 'fumadocs-ui/layouts/docs';
3
3
  import { createServerFn } from '@tanstack/react-start';
4
- import { slugsToMarkdownPath, source } from '@/lib/source';
4
+ import { source } from '@/lib/source';
5
5
  import browserCollections from 'collections/browser';
6
6
  import {
7
7
  DocsBody,
@@ -12,7 +12,7 @@ import {
12
12
  ViewOptionsPopover,
13
13
  } from 'fumadocs-ui/layouts/docs/page';
14
14
  import { baseOptions } from '@/lib/layout.shared';
15
- import { gitConfig } from '@/lib/shared';
15
+ import { encodeMarkdownUrl, gitConfig } from '@/lib/shared';
16
16
  import { staticFunctionMiddleware } from '@tanstack/start-static-server-functions';
17
17
  import { useFumadocsLoader } from 'fumadocs-core/source/client';
18
18
  import { Suspense } from 'react';
@@ -39,7 +39,7 @@ const loader = createServerFn({
39
39
 
40
40
  return {
41
41
  path: page.path,
42
- markdownUrl: slugsToMarkdownPath(page.slugs).url,
42
+ markdownUrl: encodeMarkdownUrl(page.slugs, page.locale),
43
43
  pageTree: await source.serializePageTree(source.getPageTree()),
44
44
  };
45
45
  });
@@ -1,11 +1,12 @@
1
1
  import { createFileRoute, notFound } from '@tanstack/react-router';
2
- import { getLLMText, markdownPathToSlugs, source } from '@/lib/source';
2
+ import { getLLMText, source } from '@/lib/source';
3
+ import { decodeMarkdownUrl } from '@/lib/shared';
3
4
 
4
5
  export const Route = createFileRoute('/docs/{$}.md')({
5
6
  server: {
6
7
  handlers: {
7
8
  GET: async ({ params }) => {
8
- const slugs = markdownPathToSlugs(params._splat?.split('/') ?? []);
9
+ const slugs = decodeMarkdownUrl(params._splat?.split('/') ?? []);
9
10
  const page = source.getPage(slugs);
10
11
  if (!page) throw notFound();
11
12
 
@@ -9,13 +9,10 @@ export const source = loader({
9
9
  plugins: [lucideIconsPlugin()],
10
10
  });
11
11
 
12
- export function getPageImage(slugs: string[]) {
13
- const segments = [...slugs, 'image.webp'];
12
+ export function getPageImageUrl(page: (typeof source)['$inferPage']) {
13
+ const segments = [...page.slugs, 'image.webp'];
14
14
 
15
- return {
16
- segments,
17
- url: `${docsImageRoute}/${segments.join('/')}`,
18
- };
15
+ return '/' + [page.locale, ...docsImageRoute.split('/'), ...segments].filter(Boolean).join('/');
19
16
  }
20
17
 
21
18
  export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
@@ -23,7 +20,7 @@ export function getPageMarkdownUrl(page: (typeof source)['$inferPage']) {
23
20
 
24
21
  return {
25
22
  segments,
26
- url: `${docsContentRoute}/${segments.join('/')}`,
23
+ url: '/' + [page.locale, ...docsContentRoute.split('/'), ...segments].filter(Boolean).join('/'),
27
24
  };
28
25
  }
29
26
 
@@ -1,4 +1,4 @@
1
- import { getPageImage, getPageMarkdownUrl, source } from '@/lib/source';
1
+ import { getPageImageUrl, getPageMarkdownUrl, source } from '@/lib/source';
2
2
  import { PageProps } from 'waku/router';
3
3
  import { createRelativeLink } from 'fumadocs-ui/mdx';
4
4
  import {
@@ -21,7 +21,7 @@ export default function Page({ slugs }: PageProps<'/docs/[...slugs]'>) {
21
21
  const markdownUrl = getPageMarkdownUrl(page).url;
22
22
  return (
23
23
  <DocsPage toc={page.data.toc}>
24
- <meta property="og:image" content={getPageImage(slugs).url} />
24
+ <meta property="og:image" content={getPageImageUrl(page)} />
25
25
  <DocsTitle>{page.data.title}</DocsTitle>
26
26
  <DocsDescription className="mb-0">{page.data.description}</DocsDescription>
27
27
  <div className="flex flex-row gap-2 items-center border-b pt-2 pb-6">
@@ -1 +0,0 @@
1
- {"version":3,"file":"constants-UZ9hhOMa.js","names":["corePkg","mdxPkg","basePkg","versionPkg.dependencies","basePkg.name","basePkg.version"],"sources":["../../create-app-versions/package.json","../../core/package.json","../../mdx/package.json","../../base-ui/package.json","../src/constants.ts"],"sourcesContent":["","","","","import { fileURLToPath } from 'node:url';\nimport versionPkg from '../../create-app-versions/package.json';\nimport * as corePkg from '../../core/package.json';\nimport * as mdxPkg from '../../mdx/package.json';\nimport * as basePkg from '../../base-ui/package.json';\n\nexport const sourceDir = fileURLToPath(new URL(`../`, import.meta.url).href);\n\nexport const isCI = Boolean(process.env.CI);\n\nexport interface TemplateInfo {\n value:\n | '+next+fuma-docs-mdx'\n | 'astro'\n | 'waku'\n | 'react-router'\n | 'react-router-spa'\n | 'tanstack-start'\n | 'tanstack-start-spa'\n | '+next+fuma-docs-mdx+static';\n label: string;\n appDir: string;\n /**\n * path to root provider, relative to `appDir``\n */\n rootProviderPath: string;\n hint?: string;\n /**\n * rename files when copying from template\n */\n rename?: (name: string) => string;\n}\n\nexport const templates: TemplateInfo[] = [\n {\n value: '+next+fuma-docs-mdx',\n label: 'Next.js: Fumadocs MDX',\n hint: 'recommended',\n appDir: '',\n rootProviderPath: 'app/layout.tsx',\n },\n {\n value: 'astro',\n label: 'Astro: React Islands',\n hint: 'uses Astro Content Collections and Fumadocs UI as a React island',\n appDir: 'src',\n rootProviderPath: 'components/docs.tsx',\n },\n {\n value: '+next+fuma-docs-mdx+static',\n label: 'Next.js Static: Fumadocs MDX',\n appDir: '',\n rootProviderPath: 'components/provider.tsx',\n },\n {\n value: 'waku',\n label: 'Waku: Fumadocs MDX',\n appDir: 'src',\n rootProviderPath: 'components/provider.tsx',\n },\n {\n value: 'react-router',\n label: 'React Router: Fumadocs MDX (not RSC)',\n appDir: 'app',\n rootProviderPath: 'root.tsx',\n },\n {\n value: 'react-router-spa',\n label: 'React Router SPA: Fumadocs MDX (not RSC)',\n hint: 'SPA mode allows you to host the site statically, compatible with a CDN.',\n appDir: 'app',\n rootProviderPath: 'root.tsx',\n },\n {\n value: 'tanstack-start',\n label: 'Tanstack Start: Fumadocs MDX (not RSC)',\n appDir: 'src',\n rootProviderPath: 'routes/__root.tsx',\n },\n {\n value: 'tanstack-start-spa',\n label: 'Tanstack Start SPA: Fumadocs MDX (not RSC)',\n hint: 'SPA mode allows you to host the site statically, compatible with a CDN.',\n appDir: 'src',\n rootProviderPath: 'routes/__root.tsx',\n },\n];\n\nconst workspaces = [corePkg, mdxPkg, basePkg];\n\nexport const depVersions = versionPkg.dependencies;\n\nfor (const workspace of workspaces) {\n depVersions[workspace.name as keyof typeof depVersions] = workspace.version;\n}\n\ndepVersions['fumadocs-ui'] = `npm:${basePkg.name}@${basePkg.version}`;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AIMA,MAAa,YAAY,cAAc,IAAI,IAAI,OAAO,OAAO,KAAK,GAAG,CAAC,CAAC,IAAI;AAE3E,MAAa,OAAO,QAAQ,QAAQ,IAAI,EAAE;AAyB1C,MAAa,YAA4B;CACvC;EACE,OAAO;EACP,OAAO;EACP,MAAM;EACN,QAAQ;EACR,kBAAkB;CACpB;CACA;EACE,OAAO;EACP,OAAO;EACP,MAAM;EACN,QAAQ;EACR,kBAAkB;CACpB;CACA;EACE,OAAO;EACP,OAAO;EACP,QAAQ;EACR,kBAAkB;CACpB;CACA;EACE,OAAO;EACP,OAAO;EACP,QAAQ;EACR,kBAAkB;CACpB;CACA;EACE,OAAO;EACP,OAAO;EACP,QAAQ;EACR,kBAAkB;CACpB;CACA;EACE,OAAO;EACP,OAAO;EACP,MAAM;EACN,QAAQ;EACR,kBAAkB;CACpB;CACA;EACE,OAAO;EACP,OAAO;EACP,QAAQ;EACR,kBAAkB;CACpB;CACA;EACE,OAAO;EACP,OAAO;EACP,MAAM;EACN,QAAQ;EACR,kBAAkB;CACpB;AACF;AAEA,MAAM,aAAa;CAACA;CAASC;CAAQC;AAAO;AAE5C,MAAa,cAAcC;AAE3B,KAAK,MAAM,aAAa,YACtB,YAAY,UAAU,QAAoC,UAAU;AAGtE,YAAY,iBAAiB,OAAOC,KAAa,GAAGC"}
@@ -1,5 +0,0 @@
1
- export function getPageImagePath(slugs: string[]) {
2
- const segments = [...slugs, 'image.webp'];
3
-
4
- return `/og/docs/${segments.join('/')}`;
5
- }