create-fumadocs-app 16.0.3 → 16.0.4

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/bin.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/bin.js ADDED
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ create,
4
+ getPackageManager,
5
+ managers
6
+ } from "./chunk-UXPTMGXV.js";
7
+ import {
8
+ isCI,
9
+ templates
10
+ } from "./chunk-ZWEHS3HT.js";
11
+
12
+ // src/bin.ts
13
+ import fs from "fs/promises";
14
+ import path from "path";
15
+ import {
16
+ cancel,
17
+ confirm,
18
+ group,
19
+ intro,
20
+ isCancel,
21
+ outro,
22
+ select,
23
+ spinner,
24
+ text
25
+ } from "@clack/prompts";
26
+ import pc from "picocolors";
27
+ import { Option, program } from "@commander-js/extra-typings";
28
+ var command = program.argument("[name]", "the project name").option("--src", "(Next.js only) enable `src/` directory").option("--install", "install packages automatically").option("--no-git", "disable auto Git repository initialization").addOption(
29
+ new Option(
30
+ "--linter <name>",
31
+ "configure a linter/formatter, ESLint is currently Next.js only."
32
+ ).choices(["eslint", "biome"])
33
+ ).addOption(
34
+ new Option("--search <name>", "configure a search solution").choices([
35
+ "orama",
36
+ "orama-cloud"
37
+ ])
38
+ ).addOption(
39
+ new Option("--template <name>", "choose a template").choices(
40
+ templates.map((item) => item.value)
41
+ )
42
+ ).addOption(
43
+ new Option("--pm <name>", "choose a package manager").choices(managers).default(getPackageManager())
44
+ );
45
+ async function main() {
46
+ command.parse(process.argv);
47
+ const defaultName = command.args[0];
48
+ const config = command.opts();
49
+ intro(pc.bgCyan(pc.bold("Create Fumadocs App")));
50
+ const options = await group(
51
+ {
52
+ name: async () => {
53
+ if (defaultName) return defaultName;
54
+ if (isCI) return "untitled";
55
+ return text({
56
+ message: "Project name",
57
+ placeholder: "my-app",
58
+ defaultValue: "my-app"
59
+ });
60
+ },
61
+ template: async () => {
62
+ if (config.template) return config.template;
63
+ if (isCI) return "+next+fuma-docs-mdx";
64
+ return select({
65
+ message: "Choose a template",
66
+ initialValue: "+next+fuma-docs-mdx",
67
+ options: templates
68
+ });
69
+ },
70
+ src: async ({ results }) => {
71
+ if (config.src !== void 0) return config.src;
72
+ if (isCI || !results.template?.startsWith("+next")) return false;
73
+ return confirm({
74
+ message: "Use `/src` directory?",
75
+ initialValue: false
76
+ });
77
+ },
78
+ lint: async ({ results }) => {
79
+ if (config.linter !== void 0) return config.linter;
80
+ if (isCI) return "disabled";
81
+ return select({
82
+ message: "Configure linter?",
83
+ options: results.template === "+next+fuma-docs-mdx" ? [
84
+ {
85
+ value: "disabled",
86
+ label: "Disabled"
87
+ },
88
+ {
89
+ value: "eslint",
90
+ label: "ESLint"
91
+ },
92
+ {
93
+ value: "biome",
94
+ label: "Biome"
95
+ }
96
+ ] : [
97
+ {
98
+ value: "disabled",
99
+ label: "Disabled"
100
+ },
101
+ {
102
+ value: "biome",
103
+ label: "Biome"
104
+ }
105
+ ]
106
+ });
107
+ },
108
+ search: async () => {
109
+ if (config.search !== void 0) return config.search;
110
+ if (isCI) return "orama";
111
+ return select({
112
+ message: "Choose a search solution?",
113
+ options: [
114
+ {
115
+ value: "orama",
116
+ label: "Default",
117
+ hint: "local search powered by Orama, recommended"
118
+ },
119
+ {
120
+ value: "orama-cloud",
121
+ label: "Orama Cloud",
122
+ hint: "3rd party search solution, signup needed"
123
+ }
124
+ ]
125
+ });
126
+ },
127
+ installDeps: async () => {
128
+ if (config.install !== void 0) return config.install;
129
+ if (isCI) return false;
130
+ return confirm({
131
+ message: `Do you want to install packages automatically? (detected as ${config.pm})`
132
+ });
133
+ }
134
+ },
135
+ {
136
+ onCancel: () => {
137
+ cancel("Installation Stopped.");
138
+ process.exit(0);
139
+ }
140
+ }
141
+ );
142
+ const projectName = options.name.toLowerCase().replace(/\s/, "-");
143
+ if (!isCI) await checkDir(projectName);
144
+ const info = spinner();
145
+ info.start(`Generating Project`);
146
+ const plugins = [];
147
+ if (options.src) {
148
+ const { nextUseSrc } = await import("./plugins/next-use-src.js");
149
+ plugins.push(nextUseSrc());
150
+ }
151
+ if (options.search === "orama-cloud") {
152
+ const { oramaCloud } = await import("./plugins/orama-cloud.js");
153
+ plugins.push(oramaCloud());
154
+ }
155
+ if (options.lint === "eslint") {
156
+ const { eslint } = await import("./plugins/eslint.js");
157
+ plugins.push(eslint());
158
+ }
159
+ if (options.lint === "biome") {
160
+ const { biome } = await import("./plugins/biome.js");
161
+ plugins.push(biome());
162
+ }
163
+ await create({
164
+ packageManager: config.pm,
165
+ template: options.template,
166
+ outputDir: projectName,
167
+ installDeps: options.installDeps,
168
+ initializeGit: config.git,
169
+ plugins,
170
+ log: (message) => {
171
+ info.message(message);
172
+ }
173
+ });
174
+ info.stop("Project Generated");
175
+ outro(pc.bgGreen(pc.bold("Done")));
176
+ console.log(pc.bold("\nOpen the project"));
177
+ console.log(pc.cyan(`cd ${projectName}`));
178
+ console.log(pc.bold("\nRun Development Server"));
179
+ if (config.pm === "npm" || config.pm === "bun") {
180
+ console.log(pc.cyan(`${config.pm} run dev`));
181
+ } else {
182
+ console.log(pc.cyan(`${config.pm} dev`));
183
+ }
184
+ console.log(
185
+ pc.bold("\nYou can now open the project and start writing documents")
186
+ );
187
+ process.exit(0);
188
+ }
189
+ async function checkDir(outputDir) {
190
+ const destDir = await fs.readdir(outputDir).catch(() => null);
191
+ if (!destDir || destDir.length === 0) return;
192
+ const del = await confirm({
193
+ message: `directory ${outputDir} already exists, do you want to delete its files?`
194
+ });
195
+ if (isCancel(del)) {
196
+ cancel();
197
+ process.exit(1);
198
+ }
199
+ if (!del) return;
200
+ const info = spinner();
201
+ info.start(`Deleting files in ${outputDir}`);
202
+ await Promise.all(
203
+ destDir.map((item) => {
204
+ return fs.rm(path.join(outputDir, item), {
205
+ recursive: true,
206
+ force: true
207
+ });
208
+ })
209
+ );
210
+ info.stop(`Deleted files in ${outputDir}`);
211
+ }
212
+ main().catch((e) => {
213
+ console.error(e);
214
+ process.exit(1);
215
+ });
@@ -0,0 +1,51 @@
1
+ // src/plugins/biome.next.json
2
+ var $schema = "https://biomejs.dev/schemas/2.2.0/schema.json";
3
+ var vcs = {
4
+ enabled: true,
5
+ clientKind: "git",
6
+ useIgnoreFile: true
7
+ };
8
+ var files = {
9
+ ignoreUnknown: true,
10
+ includes: ["**", "!node_modules", "!.next", "!dist", "!build", "!.source"]
11
+ };
12
+ var formatter = {
13
+ enabled: true,
14
+ indentStyle: "space",
15
+ indentWidth: 2
16
+ };
17
+ var linter = {
18
+ enabled: true,
19
+ rules: {
20
+ recommended: true
21
+ },
22
+ domains: {
23
+ next: "recommended",
24
+ react: "recommended"
25
+ }
26
+ };
27
+ var assist = {
28
+ actions: {
29
+ source: {
30
+ organizeImports: "on"
31
+ }
32
+ }
33
+ };
34
+ var biome_next_default = {
35
+ $schema,
36
+ vcs,
37
+ files,
38
+ formatter,
39
+ linter,
40
+ assist
41
+ };
42
+
43
+ export {
44
+ $schema,
45
+ vcs,
46
+ files,
47
+ formatter,
48
+ linter,
49
+ assist,
50
+ biome_next_default
51
+ };
@@ -0,0 +1,50 @@
1
+ // src/plugins/biome.base.json
2
+ var $schema = "https://biomejs.dev/schemas/2.2.0/schema.json";
3
+ var vcs = {
4
+ enabled: true,
5
+ clientKind: "git",
6
+ useIgnoreFile: true
7
+ };
8
+ var files = {
9
+ ignoreUnknown: true,
10
+ includes: ["**", "!node_modules", "!.source"]
11
+ };
12
+ var formatter = {
13
+ enabled: true,
14
+ indentStyle: "space",
15
+ indentWidth: 2
16
+ };
17
+ var linter = {
18
+ enabled: true,
19
+ rules: {
20
+ recommended: true
21
+ },
22
+ domains: {
23
+ react: "recommended"
24
+ }
25
+ };
26
+ var assist = {
27
+ actions: {
28
+ source: {
29
+ organizeImports: "on"
30
+ }
31
+ }
32
+ };
33
+ var biome_base_default = {
34
+ $schema,
35
+ vcs,
36
+ files,
37
+ formatter,
38
+ linter,
39
+ assist
40
+ };
41
+
42
+ export {
43
+ $schema,
44
+ vcs,
45
+ files,
46
+ formatter,
47
+ linter,
48
+ assist,
49
+ biome_base_default
50
+ };
@@ -0,0 +1,130 @@
1
+ import {
2
+ copy,
3
+ depVersions,
4
+ sourceDir,
5
+ templates,
6
+ tryGitInit
7
+ } from "./chunk-ZWEHS3HT.js";
8
+
9
+ // src/index.ts
10
+ import path from "path";
11
+ import fs from "fs/promises";
12
+
13
+ // src/auto-install.ts
14
+ import { x } from "tinyexec";
15
+ var managers = ["npm", "yarn", "bun", "pnpm"];
16
+ function getPackageManager() {
17
+ const userAgent = process.env.npm_config_user_agent ?? "";
18
+ if (userAgent.startsWith("yarn")) {
19
+ return "yarn";
20
+ }
21
+ if (userAgent.startsWith("pnpm")) {
22
+ return "pnpm";
23
+ }
24
+ if (userAgent.startsWith("bun")) {
25
+ return "bun";
26
+ }
27
+ return "npm";
28
+ }
29
+ async function autoInstall(manager, dest) {
30
+ await x(manager, ["install"], {
31
+ throwOnError: true,
32
+ nodeOptions: {
33
+ env: {
34
+ ...process.env,
35
+ NODE_ENV: "development",
36
+ DISABLE_OPENCOLLECTIVE: "1"
37
+ },
38
+ cwd: dest
39
+ }
40
+ });
41
+ }
42
+
43
+ // src/index.ts
44
+ async function create(createOptions) {
45
+ const {
46
+ outputDir,
47
+ plugins = [],
48
+ packageManager = "npm",
49
+ initializeGit = false,
50
+ installDeps = false,
51
+ log = console.log
52
+ } = createOptions;
53
+ let template = templates.find(
54
+ (item) => item.value === createOptions.template
55
+ );
56
+ for (const plugin of plugins) {
57
+ template = await plugin.template?.call({ dest: outputDir }, template) ?? template;
58
+ }
59
+ const appDir = path.join(outputDir, template.appDir);
60
+ const projectName = path.basename(outputDir);
61
+ const pluginContext = {
62
+ template,
63
+ dest: outputDir,
64
+ log,
65
+ appDir
66
+ };
67
+ await copy(path.join(sourceDir, "template", template.value), outputDir, {
68
+ rename(file) {
69
+ file = file.replace("example.gitignore", ".gitignore");
70
+ return template.rename?.(file) ?? file;
71
+ }
72
+ });
73
+ const packageJsonPath = path.join(outputDir, "package.json");
74
+ let packageJson = await initPackageJson(projectName, packageJsonPath);
75
+ for (const plugin of plugins) {
76
+ packageJson = await plugin.packageJson?.call(pluginContext, packageJson) ?? packageJson;
77
+ }
78
+ await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2));
79
+ const readmePath = path.join(outputDir, "README.md");
80
+ let readme = `# ${projectName}
81
+
82
+ ${await fs.readFile(readmePath)}`;
83
+ for (const plugin of plugins) {
84
+ readme = await plugin.readme?.call(pluginContext, readme) ?? readme;
85
+ }
86
+ await fs.writeFile(readmePath, readme);
87
+ for (const plugin of plugins) {
88
+ await plugin.afterWrite?.call(pluginContext);
89
+ }
90
+ if (installDeps) {
91
+ try {
92
+ await autoInstall(packageManager, outputDir);
93
+ log("Installed dependencies");
94
+ } catch (err) {
95
+ log(`Failed to install dependencies: ${err}`);
96
+ }
97
+ }
98
+ if (initializeGit && await tryGitInit(outputDir)) {
99
+ log("Initialized Git repository");
100
+ }
101
+ }
102
+ async function initPackageJson(projectName, packageJsonPath) {
103
+ function replaceWorkspaceDeps(deps = {}) {
104
+ for (const k in deps) {
105
+ if (deps[k].startsWith("workspace:") && k in depVersions) {
106
+ deps[k] = depVersions[k];
107
+ }
108
+ }
109
+ return deps;
110
+ }
111
+ const packageJson = JSON.parse(
112
+ (await fs.readFile(packageJsonPath)).toString()
113
+ );
114
+ return {
115
+ ...packageJson,
116
+ name: projectName,
117
+ scripts: {
118
+ ...packageJson.scripts,
119
+ postinstall: "fumadocs-mdx"
120
+ },
121
+ dependencies: replaceWorkspaceDeps(packageJson.dependencies),
122
+ devDependencies: replaceWorkspaceDeps(packageJson.devDependencies)
123
+ };
124
+ }
125
+
126
+ export {
127
+ managers,
128
+ getPackageManager,
129
+ create
130
+ };
@@ -27,39 +27,39 @@ async function copy(from, to, options = {}) {
27
27
  await fs.copyFile(from, to);
28
28
  }
29
29
  }
30
- async function isInGitRepository(cwd2) {
30
+ async function isInGitRepository(cwd) {
31
31
  const { exitCode } = await x("git", ["rev-parse", "--is-inside-work-tree"], {
32
- nodeOptions: { cwd: cwd2 }
32
+ nodeOptions: { cwd }
33
33
  });
34
34
  return exitCode === 0;
35
35
  }
36
- async function isDefaultBranchSet(cwd2) {
36
+ async function isDefaultBranchSet(cwd) {
37
37
  const { exitCode } = await x("git", ["config", "init.defaultBranch"], {
38
- nodeOptions: { cwd: cwd2 }
38
+ nodeOptions: { cwd }
39
39
  });
40
40
  return exitCode === 0;
41
41
  }
42
- async function tryGitInit(cwd2) {
42
+ async function tryGitInit(cwd) {
43
43
  const { exitCode } = await x("git", ["--version"]);
44
44
  if (exitCode !== 0) return false;
45
- if (await isInGitRepository(cwd2)) return false;
45
+ if (await isInGitRepository(cwd)) return false;
46
46
  try {
47
47
  await x("git", ["init"], {
48
48
  throwOnError: true,
49
- nodeOptions: { cwd: cwd2 }
49
+ nodeOptions: { cwd }
50
50
  });
51
- if (!await isDefaultBranchSet(cwd2)) {
51
+ if (!await isDefaultBranchSet(cwd)) {
52
52
  await x("git", ["checkout", "-b", "main"], {
53
53
  throwOnError: true,
54
54
  nodeOptions: {
55
- cwd: cwd2
55
+ cwd
56
56
  }
57
57
  });
58
58
  }
59
59
  await x("git", ["add", "-A"], {
60
60
  throwOnError: true,
61
61
  nodeOptions: {
62
- cwd: cwd2
62
+ cwd
63
63
  }
64
64
  });
65
65
  await x(
@@ -68,13 +68,13 @@ async function tryGitInit(cwd2) {
68
68
  {
69
69
  throwOnError: true,
70
70
  nodeOptions: {
71
- cwd: cwd2
71
+ cwd
72
72
  }
73
73
  }
74
74
  );
75
75
  return true;
76
76
  } catch {
77
- await fs.rmdir(join(cwd2, ".git"), { recursive: true }).catch(() => null);
77
+ await fs.rmdir(join(cwd, ".git"), { recursive: true }).catch(() => null);
78
78
  return false;
79
79
  }
80
80
  }
@@ -139,29 +139,39 @@ var package_default = {
139
139
 
140
140
  // src/constants.ts
141
141
  var sourceDir = fileURLToPath(new URL(`../`, import.meta.url).href);
142
- var cwd = process.cwd();
142
+ var isCI = Boolean(process.env.CI);
143
143
  var templates = [
144
144
  {
145
145
  value: "+next+fuma-docs-mdx",
146
146
  label: "Next.js: Fumadocs MDX",
147
- hint: "recommended"
147
+ hint: "recommended",
148
+ appDir: "",
149
+ rootProviderPath: "app/layout.tsx"
148
150
  },
149
151
  {
150
152
  value: "waku",
151
- label: "Waku: Fumadocs MDX"
153
+ label: "Waku: Fumadocs MDX",
154
+ appDir: "src",
155
+ rootProviderPath: "components/provider.tsx"
152
156
  },
153
157
  {
154
158
  value: "react-router",
155
- label: "React Router: Fumadocs MDX (not RSC)"
159
+ label: "React Router: Fumadocs MDX (not RSC)",
160
+ appDir: "app",
161
+ rootProviderPath: "root.tsx"
156
162
  },
157
163
  {
158
164
  value: "react-router-spa",
159
165
  label: "React Router SPA: Fumadocs MDX (not RSC)",
160
- hint: "SPA mode allows you to host the site statically, compatible with a CDN."
166
+ hint: "SPA mode allows you to host the site statically, compatible with a CDN.",
167
+ appDir: "app",
168
+ rootProviderPath: "root.tsx"
161
169
  },
162
170
  {
163
171
  value: "tanstack-start",
164
- label: "Tanstack Start: Fumadocs MDX (not RSC)"
172
+ label: "Tanstack Start: Fumadocs MDX (not RSC)",
173
+ appDir: "src",
174
+ rootProviderPath: "routes/__root.tsx"
165
175
  }
166
176
  ];
167
177
  var depVersions = {
@@ -175,7 +185,7 @@ export {
175
185
  tryGitInit,
176
186
  pick,
177
187
  sourceDir,
178
- cwd,
188
+ isCI,
179
189
  templates,
180
190
  depVersions
181
191
  };
package/dist/index.d.ts CHANGED
@@ -1 +1,63 @@
1
- #!/usr/bin/env node
1
+ type PackageManager = (typeof managers)[number];
2
+ declare const managers: readonly ["npm", "yarn", "bun", "pnpm"];
3
+
4
+ interface TemplateInfo {
5
+ value: '+next+fuma-docs-mdx' | 'waku' | 'react-router' | 'react-router-spa' | 'tanstack-start';
6
+ label: string;
7
+ appDir: string;
8
+ /**
9
+ * path to root provider, relative to `appDir``
10
+ */
11
+ rootProviderPath: string;
12
+ hint?: string;
13
+ /**
14
+ * rename files when copying from template
15
+ */
16
+ rename?: (name: string) => string;
17
+ }
18
+
19
+ type Template = TemplateInfo['value'];
20
+ interface Options {
21
+ outputDir: string;
22
+ template: Template;
23
+ /**
24
+ * the package manager to use
25
+ *
26
+ * @defaultValue 'npm'
27
+ */
28
+ packageManager?: PackageManager;
29
+ installDeps?: boolean;
30
+ initializeGit?: boolean;
31
+ log?: (message: string) => void;
32
+ plugins?: TemplatePlugin[];
33
+ }
34
+ interface TemplatePluginContext {
35
+ template: TemplateInfo;
36
+ log: (message: string) => void;
37
+ /**
38
+ * output directory
39
+ */
40
+ dest: string;
41
+ /**
42
+ * output directory for app code (e.g. under `/src`)
43
+ */
44
+ appDir: string;
45
+ }
46
+ type PackageJsonType = {
47
+ name?: string;
48
+ version?: string;
49
+ private?: boolean;
50
+ scripts?: Record<string, string>;
51
+ dependencies?: Record<string, string>;
52
+ devDependencies?: Record<string, string>;
53
+ } & Record<string, unknown>;
54
+ type Awaitable<T> = T | Promise<T>;
55
+ interface TemplatePlugin {
56
+ template?: (this: Pick<TemplatePluginContext, 'dest'>, info: TemplateInfo) => Awaitable<void | TemplateInfo>;
57
+ packageJson?: (this: TemplatePluginContext, packageJson: PackageJsonType) => Awaitable<void | PackageJsonType>;
58
+ afterWrite?: (this: TemplatePluginContext) => Awaitable<void>;
59
+ readme?: (this: TemplatePluginContext, content: string) => Awaitable<void | string>;
60
+ }
61
+ declare function create(createOptions: Options): Promise<void>;
62
+
63
+ export { type Options, type PackageJsonType, type Template, type TemplatePlugin, type TemplatePluginContext, create };