create-fumadocs-app 16.0.2 → 16.0.3

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.
@@ -0,0 +1,204 @@
1
+ import {
2
+ copy,
3
+ cwd,
4
+ depVersions,
5
+ pick,
6
+ sourceDir,
7
+ templates,
8
+ tryGitInit
9
+ } from "./chunk-HLHY7KAF.js";
10
+
11
+ // src/create-app.ts
12
+ import path from "path";
13
+ import fs from "fs/promises";
14
+
15
+ // src/auto-install.ts
16
+ import { x } from "tinyexec";
17
+ var managers = ["npm", "yarn", "bun", "pnpm"];
18
+ function getPackageManager() {
19
+ const userAgent = process.env.npm_config_user_agent ?? "";
20
+ if (userAgent.startsWith("yarn")) {
21
+ return "yarn";
22
+ }
23
+ if (userAgent.startsWith("pnpm")) {
24
+ return "pnpm";
25
+ }
26
+ if (userAgent.startsWith("bun")) {
27
+ return "bun";
28
+ }
29
+ return "npm";
30
+ }
31
+ async function autoInstall(manager, dest) {
32
+ await x(manager, ["install"], {
33
+ throwOnError: true,
34
+ nodeOptions: {
35
+ env: {
36
+ ...process.env,
37
+ NODE_ENV: "development",
38
+ DISABLE_OPENCOLLECTIVE: "1"
39
+ },
40
+ cwd: dest
41
+ }
42
+ });
43
+ }
44
+
45
+ // src/create-app.ts
46
+ function defaults(options) {
47
+ return {
48
+ ...options,
49
+ plugins: options.plugins ?? [],
50
+ useSrcDir: options.template.startsWith("+next") && options.useSrcDir === true,
51
+ lint: options.lint ?? false,
52
+ initializeGit: options.initializeGit ?? false,
53
+ installDeps: options.installDeps ?? false,
54
+ log: console.log
55
+ };
56
+ }
57
+ function isRelative(dir, file) {
58
+ return !path.relative(dir, file).startsWith(`..${path.sep}`);
59
+ }
60
+ async function create(createOptions) {
61
+ const options = defaults(createOptions);
62
+ const {
63
+ outputDir,
64
+ useSrcDir,
65
+ log,
66
+ installDeps,
67
+ template,
68
+ lint,
69
+ initializeGit,
70
+ packageManager,
71
+ plugins
72
+ } = options;
73
+ const projectName = path.basename(outputDir);
74
+ const dest = path.resolve(cwd, outputDir);
75
+ const isNext = options.template.startsWith("+next");
76
+ const pluginContext = {
77
+ template: templates.find((item) => item.value === template),
78
+ dest,
79
+ options
80
+ };
81
+ await copy(path.join(sourceDir, `template/${template}`), dest, {
82
+ rename(file) {
83
+ file = file.replace("example.gitignore", ".gitignore");
84
+ if (useSrcDir && (path.basename(file) === "mdx-components.tsx" || isRelative(path.join(dest, "app"), file) || isRelative(path.join(dest, "lib"), file))) {
85
+ return path.join(dest, "src", path.relative(dest, file));
86
+ }
87
+ return file;
88
+ }
89
+ });
90
+ if (isNext && lint) {
91
+ await copy(path.join(sourceDir, `template/+next+${lint}`), dest);
92
+ log("Configured Linter");
93
+ }
94
+ if (isNext && useSrcDir) {
95
+ const tsconfigPath = path.join(dest, "tsconfig.json");
96
+ const content = (await fs.readFile(tsconfigPath)).toString();
97
+ const config = JSON.parse(content);
98
+ if (config.compilerOptions?.paths) {
99
+ Object.assign(config.compilerOptions.paths, {
100
+ "@/*": ["./src/*"]
101
+ });
102
+ }
103
+ await fs.writeFile(tsconfigPath, JSON.stringify(config, null, 2));
104
+ }
105
+ let packageJson = await createPackageJson(projectName, dest, options);
106
+ for (const plugin of plugins) {
107
+ const result = await plugin.packageJson?.call(pluginContext, packageJson);
108
+ if (result) packageJson = result;
109
+ }
110
+ await fs.writeFile(
111
+ path.join(dest, "package.json"),
112
+ JSON.stringify(packageJson, null, 2)
113
+ );
114
+ let readme = await getReadme(dest, projectName);
115
+ for (const plugin of plugins) {
116
+ readme = await plugin.readme?.call(pluginContext, readme) ?? readme;
117
+ }
118
+ await fs.writeFile(path.join(dest, "README.md"), readme);
119
+ for (const plugin of plugins) {
120
+ await plugin.afterWrite?.call(pluginContext);
121
+ }
122
+ if (installDeps) {
123
+ try {
124
+ await autoInstall(packageManager, dest);
125
+ log("Installed dependencies");
126
+ } catch (err) {
127
+ log(`Failed to install dependencies: ${err}`);
128
+ }
129
+ }
130
+ if (initializeGit && await tryGitInit(dest)) {
131
+ log("Initialized Git repository");
132
+ }
133
+ }
134
+ async function getReadme(dest, projectName) {
135
+ const template = await fs.readFile(path.join(dest, "README.md")).then((res) => res.toString());
136
+ return `# ${projectName}
137
+
138
+ ${template}`;
139
+ }
140
+ async function createPackageJson(projectName, dir, { template, lint }) {
141
+ const isNext = template.startsWith("+next");
142
+ function replaceWorkspaceDeps(deps) {
143
+ for (const k in deps) {
144
+ if (deps[k].startsWith("workspace:") && k in depVersions) {
145
+ deps[k] = depVersions[k];
146
+ }
147
+ }
148
+ return deps;
149
+ }
150
+ let packageJson = JSON.parse(
151
+ await fs.readFile(path.join(dir, "package.json")).then((res) => res.toString())
152
+ );
153
+ packageJson = {
154
+ name: projectName,
155
+ ...packageJson,
156
+ dependencies: replaceWorkspaceDeps(packageJson.dependencies),
157
+ devDependencies: replaceWorkspaceDeps(packageJson.devDependencies)
158
+ };
159
+ if (isNext) {
160
+ packageJson = {
161
+ ...packageJson,
162
+ scripts: {
163
+ ...packageJson.scripts,
164
+ postinstall: "fumadocs-mdx"
165
+ }
166
+ };
167
+ }
168
+ if (isNext && lint === "biome") {
169
+ packageJson = {
170
+ ...packageJson,
171
+ scripts: {
172
+ ...packageJson.scripts,
173
+ lint: "biome check",
174
+ format: "biome format --write"
175
+ },
176
+ devDependencies: {
177
+ ...packageJson.devDependencies,
178
+ ...pick(depVersions, ["@biomejs/biome"])
179
+ }
180
+ };
181
+ }
182
+ if (isNext && lint === "eslint") {
183
+ packageJson = {
184
+ ...packageJson,
185
+ scripts: {
186
+ ...packageJson.scripts,
187
+ lint: "eslint"
188
+ },
189
+ devDependencies: {
190
+ ...packageJson.devDependencies,
191
+ eslint: "^9",
192
+ "eslint-config-next": depVersions.next,
193
+ "@eslint/eslintrc": "^3"
194
+ }
195
+ };
196
+ }
197
+ return packageJson;
198
+ }
199
+
200
+ export {
201
+ managers,
202
+ getPackageManager,
203
+ create
204
+ };
@@ -0,0 +1,181 @@
1
+ // src/utils.ts
2
+ import fs from "fs/promises";
3
+ import path, { join } from "path";
4
+ import { x } from "tinyexec";
5
+ async function writeFile(file, content) {
6
+ await fs.mkdir(path.dirname(file), { recursive: true });
7
+ await fs.writeFile(file, content);
8
+ }
9
+ async function copy(from, to, options = {}) {
10
+ const {
11
+ rename = (s) => s,
12
+ filterDir = () => true,
13
+ filter = () => true
14
+ } = options;
15
+ const stats = await fs.stat(from);
16
+ if (stats.isDirectory() && filterDir(from)) {
17
+ const files = await fs.readdir(from);
18
+ await Promise.all(
19
+ files.map(
20
+ (file) => copy(path.join(from, file), path.join(to, file), options)
21
+ )
22
+ );
23
+ }
24
+ if (stats.isFile() && filter(from)) {
25
+ to = rename(to);
26
+ await fs.mkdir(path.dirname(to), { recursive: true });
27
+ await fs.copyFile(from, to);
28
+ }
29
+ }
30
+ async function isInGitRepository(cwd2) {
31
+ const { exitCode } = await x("git", ["rev-parse", "--is-inside-work-tree"], {
32
+ nodeOptions: { cwd: cwd2 }
33
+ });
34
+ return exitCode === 0;
35
+ }
36
+ async function isDefaultBranchSet(cwd2) {
37
+ const { exitCode } = await x("git", ["config", "init.defaultBranch"], {
38
+ nodeOptions: { cwd: cwd2 }
39
+ });
40
+ return exitCode === 0;
41
+ }
42
+ async function tryGitInit(cwd2) {
43
+ const { exitCode } = await x("git", ["--version"]);
44
+ if (exitCode !== 0) return false;
45
+ if (await isInGitRepository(cwd2)) return false;
46
+ try {
47
+ await x("git", ["init"], {
48
+ throwOnError: true,
49
+ nodeOptions: { cwd: cwd2 }
50
+ });
51
+ if (!await isDefaultBranchSet(cwd2)) {
52
+ await x("git", ["checkout", "-b", "main"], {
53
+ throwOnError: true,
54
+ nodeOptions: {
55
+ cwd: cwd2
56
+ }
57
+ });
58
+ }
59
+ await x("git", ["add", "-A"], {
60
+ throwOnError: true,
61
+ nodeOptions: {
62
+ cwd: cwd2
63
+ }
64
+ });
65
+ await x(
66
+ "git",
67
+ ["commit", "-m", "Initial commit from Create Fumadocs App"],
68
+ {
69
+ throwOnError: true,
70
+ nodeOptions: {
71
+ cwd: cwd2
72
+ }
73
+ }
74
+ );
75
+ return true;
76
+ } catch {
77
+ await fs.rmdir(join(cwd2, ".git"), { recursive: true }).catch(() => null);
78
+ return false;
79
+ }
80
+ }
81
+ function pick(obj, keys) {
82
+ const result = {};
83
+ for (const key of keys) {
84
+ if (key in obj) {
85
+ result[key] = obj[key];
86
+ }
87
+ }
88
+ return result;
89
+ }
90
+
91
+ // src/constants.ts
92
+ import { fileURLToPath } from "url";
93
+
94
+ // src/versions.js
95
+ var versions = { "fumadocs-core": "16.0.3", "fumadocs-ui": "16.0.3", "fumadocs-mdx": "13.0.1", "@fumadocs/mdx-remote": "1.4.3", "@fumadocs/content-collections": "1.2.4" };
96
+
97
+ // ../create-app-versions/package.json
98
+ var package_default = {
99
+ name: "example-versions",
100
+ version: "0.0.0",
101
+ private: true,
102
+ description: "Used to track dependency versions in create-fumadocs-app",
103
+ dependencies: {
104
+ "@biomejs/biome": "^2.2.6",
105
+ "@content-collections/core": "^0.11.1",
106
+ "@content-collections/mdx": "^0.2.2",
107
+ "@content-collections/next": "^0.2.8",
108
+ "@orama/core": "^1.2.13",
109
+ "@react-router/dev": "^7.9.4",
110
+ "@react-router/node": "^7.9.4",
111
+ "@react-router/serve": "^7.9.4",
112
+ "@tailwindcss/postcss": "^4.1.15",
113
+ "@tailwindcss/vite": "^4.1.15",
114
+ "@tanstack/react-router": "^1.133.21",
115
+ "@tanstack/react-start": "^1.133.21",
116
+ "@types/mdx": "^2.0.13",
117
+ "@types/node": "24.9.1",
118
+ "@types/react": "^19.2.2",
119
+ "@types/react-dom": "^19.2.2",
120
+ "@vitejs/plugin-react": "^5.0.4",
121
+ "gray-matter": "^4.0.3",
122
+ isbot: "^5.1.31",
123
+ "lucide-react": "^0.546.0",
124
+ next: "16.0.0",
125
+ postcss: "^8.5.6",
126
+ react: "^19.2.0",
127
+ "react-dom": "^19.2.0",
128
+ "react-router": "^7.9.4",
129
+ "react-router-devtools": "^5.1.3",
130
+ shiki: "^3.13.0",
131
+ tailwindcss: "^4.1.15",
132
+ tinyglobby: "^0.2.15",
133
+ typescript: "^5.9.3",
134
+ vinxi: "^0.5.8",
135
+ vite: "^7.1.11",
136
+ "vite-tsconfig-paths": "^5.1.4"
137
+ }
138
+ };
139
+
140
+ // src/constants.ts
141
+ var sourceDir = fileURLToPath(new URL(`../`, import.meta.url).href);
142
+ var cwd = process.cwd();
143
+ var templates = [
144
+ {
145
+ value: "+next+fuma-docs-mdx",
146
+ label: "Next.js: Fumadocs MDX",
147
+ hint: "recommended"
148
+ },
149
+ {
150
+ value: "waku",
151
+ label: "Waku: Fumadocs MDX"
152
+ },
153
+ {
154
+ value: "react-router",
155
+ label: "React Router: Fumadocs MDX (not RSC)"
156
+ },
157
+ {
158
+ value: "react-router-spa",
159
+ label: "React Router SPA: Fumadocs MDX (not RSC)",
160
+ hint: "SPA mode allows you to host the site statically, compatible with a CDN."
161
+ },
162
+ {
163
+ value: "tanstack-start",
164
+ label: "Tanstack Start: Fumadocs MDX (not RSC)"
165
+ }
166
+ ];
167
+ var depVersions = {
168
+ ...versions,
169
+ ...package_default.dependencies
170
+ };
171
+
172
+ export {
173
+ writeFile,
174
+ copy,
175
+ tryGitInit,
176
+ pick,
177
+ sourceDir,
178
+ cwd,
179
+ templates,
180
+ depVersions
181
+ };
@@ -2,24 +2,12 @@ type PackageManager = (typeof managers)[number];
2
2
  declare const managers: readonly ["npm", "yarn", "bun", "pnpm"];
3
3
 
4
4
  interface TemplateInfo {
5
+ value: '+next+fuma-docs-mdx' | 'waku' | 'react-router' | 'react-router-spa' | 'tanstack-start';
5
6
  label: string;
6
- value: string;
7
7
  hint?: string;
8
- componentsDir: string;
9
8
  }
10
- declare const templates: ({
11
- value: string;
12
- label: string;
13
- hint: string;
14
- componentsDir: string;
15
- } | {
16
- value: string;
17
- label: string;
18
- componentsDir: string;
19
- hint?: undefined;
20
- })[];
21
9
 
22
- type Template = (typeof templates)[number]['value'];
10
+ type Template = TemplateInfo['value'];
23
11
  interface Options {
24
12
  outputDir: string;
25
13
  template: Template;
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  create
3
- } from "./chunk-6C2B326N.js";
3
+ } from "./chunk-3VWJJEHU.js";
4
+ import "./chunk-HLHY7KAF.js";
4
5
  export {
5
6
  create
6
7
  };
package/dist/index.js CHANGED
@@ -1,19 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- copy,
4
3
  create,
5
- cwd,
6
- depVersions,
7
4
  getPackageManager,
8
- managers,
9
- pick,
10
- sourceDir,
5
+ managers
6
+ } from "./chunk-3VWJJEHU.js";
7
+ import {
8
+ cwd,
11
9
  templates
12
- } from "./chunk-6C2B326N.js";
10
+ } from "./chunk-HLHY7KAF.js";
13
11
 
14
12
  // src/index.ts
15
- import fs2 from "fs/promises";
16
- import path2 from "path";
13
+ import fs from "fs/promises";
14
+ import path from "path";
17
15
  import {
18
16
  cancel,
19
17
  confirm,
@@ -27,85 +25,6 @@ import {
27
25
  } from "@clack/prompts";
28
26
  import pc from "picocolors";
29
27
  import { program } from "commander";
30
-
31
- // src/plugins/orama-cloud.ts
32
- import path from "path";
33
- import fs from "fs/promises";
34
- var oramaCloud = {
35
- packageJson(packageJson) {
36
- return {
37
- ...packageJson,
38
- scripts: {
39
- ...packageJson.scripts,
40
- build: `${packageJson.build} && bun scripts/sync-content.ts`
41
- },
42
- dependencies: {
43
- ...packageJson.dependencies,
44
- ...pick(depVersions, ["@orama/core"])
45
- }
46
- };
47
- },
48
- readme(content) {
49
- return `${content}
50
-
51
- ## Orama Cloud
52
-
53
- This project uses Orama Cloud for 3rd party search solution.
54
-
55
- See https://fumadocs.dev/docs/headless/search/orama-cloud for integrating Orama Cloud to Fumadocs.`;
56
- },
57
- async afterWrite() {
58
- const { dest, template, options } = this;
59
- const appDir = path.join(dest, options.useSrcDir ? "src" : ".");
60
- await copy(path.join(sourceDir, "template/+orama-cloud/@root"), dest);
61
- await copy(path.join(sourceDir, "template/+orama-cloud/@app"), appDir);
62
- const filePath = {
63
- "+next+fuma-docs-mdx": ".next/server/app/static.json.body",
64
- "tanstack-start": ".output/public/static.json",
65
- "react-router": "build/client/static.json",
66
- "react-router-spa": "build/client/static.json",
67
- waku: "dist/public/static.json"
68
- }[template.value];
69
- const SyncContentScript = `import { type OramaDocument, sync } from 'fumadocs-core/search/orama-cloud';
70
- import * as fs from 'node:fs/promises';
71
- import { OramaCloud } from '@orama/core';
72
-
73
- // the path of pre-rendered \`static.json\`
74
- const filePath = ${JSON.stringify(filePath)};
75
-
76
- async function main() {
77
- const orama = new OramaCloud({
78
- projectId: process.env.NEXT_PUBLIC_ORAMA_PROJECT_ID,
79
- apiKey: process.env.ORAMA_PRIVATE_API_KEY,
80
- });
81
-
82
- const content = await fs.readFile(filePath);
83
- const records = JSON.parse(content.toString()) as OramaDocument[];
84
-
85
- await sync(orama, {
86
- index: process.env.NEXT_PUBLIC_ORAMA_DATASOURCE_ID,
87
- documents: records,
88
- });
89
-
90
- console.log(\`search updated: \${records.length} records\`);
91
- }
92
-
93
- void main();
94
- `;
95
- await fs.mkdir(path.join(dest, "scripts"), { recursive: true });
96
- await fs.writeFile(
97
- path.join(dest, "scripts/sync-content.ts"),
98
- SyncContentScript
99
- );
100
- await copy(
101
- path.join(sourceDir, `template/+orama-cloud/${template.value}`),
102
- appDir
103
- );
104
- }
105
- };
106
- var orama_cloud_default = oramaCloud;
107
-
108
- // src/index.ts
109
28
  program.argument("[name]", "the project name");
110
29
  program.option("--src", "(Next.js only) enable `src/` directory");
111
30
  program.option("--no-src", "(Next.js only) disable `src/` directory");
@@ -177,6 +96,10 @@ async function main(config) {
177
96
  message: "Configure linter?",
178
97
  initialValue: false,
179
98
  options: [
99
+ {
100
+ value: false,
101
+ label: "Disabled"
102
+ },
180
103
  {
181
104
  value: "eslint",
182
105
  label: "ESLint"
@@ -184,10 +107,6 @@ async function main(config) {
184
107
  {
185
108
  value: "biome",
186
109
  label: "Biome"
187
- },
188
- {
189
- value: false,
190
- label: "Disabled"
191
110
  }
192
111
  ]
193
112
  });
@@ -225,8 +144,8 @@ async function main(config) {
225
144
  }
226
145
  );
227
146
  const projectName = options.name.toLowerCase().replace(/\s/, "-");
228
- const dest = path2.resolve(cwd, projectName);
229
- const destDir = await fs2.readdir(dest).catch(() => null);
147
+ const dest = path.resolve(cwd, projectName);
148
+ const destDir = await fs.readdir(dest).catch(() => null);
230
149
  if (destDir && destDir.length > 0) {
231
150
  const del = await confirm({
232
151
  message: `directory ${projectName} already exists, do you want to delete its files?`
@@ -240,7 +159,7 @@ async function main(config) {
240
159
  info2.start(`Deleting files in ${projectName}`);
241
160
  await Promise.all(
242
161
  destDir.map((item) => {
243
- return fs2.rm(path2.join(dest, item), {
162
+ return fs.rm(path.join(dest, item), {
244
163
  recursive: true,
245
164
  force: true
246
165
  });
@@ -251,6 +170,11 @@ async function main(config) {
251
170
  }
252
171
  const info = spinner();
253
172
  info.start(`Generating Project`);
173
+ const plugins = [];
174
+ if (options.search === "orama-cloud") {
175
+ const { oramaCloud } = await import("./orama-cloud-VQUOOEZ2.js");
176
+ plugins.push(oramaCloud());
177
+ }
254
178
  await create({
255
179
  packageManager: manager,
256
180
  template: options.template,
@@ -259,7 +183,7 @@ async function main(config) {
259
183
  lint: options.lint,
260
184
  useSrcDir: options.src,
261
185
  initializeGit: config.git ?? true,
262
- plugins: options.search === "orama-cloud" ? [orama_cloud_default] : [],
186
+ plugins,
263
187
  log: (message) => {
264
188
  info.message(message);
265
189
  }