create-unmint 1.0.0

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 (55) hide show
  1. package/README.md +57 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +499 -0
  4. package/package.json +48 -0
  5. package/template/LICENSE +21 -0
  6. package/template/README.md +278 -0
  7. package/template/__tests__/components/callout.test.tsx +46 -0
  8. package/template/__tests__/components/card.test.tsx +59 -0
  9. package/template/__tests__/components/tabs.test.tsx +61 -0
  10. package/template/__tests__/theme-config.test.ts +49 -0
  11. package/template/__tests__/utils.test.ts +25 -0
  12. package/template/app/api/og/route.tsx +90 -0
  13. package/template/app/api/search/route.ts +6 -0
  14. package/template/app/components/docs/docs-pager.tsx +41 -0
  15. package/template/app/components/docs/docs-sidebar.tsx +143 -0
  16. package/template/app/components/docs/docs-toc.tsx +61 -0
  17. package/template/app/components/docs/mdx/accordion.tsx +54 -0
  18. package/template/app/components/docs/mdx/callout.tsx +102 -0
  19. package/template/app/components/docs/mdx/card.tsx +110 -0
  20. package/template/app/components/docs/mdx/code-block.tsx +42 -0
  21. package/template/app/components/docs/mdx/frame.tsx +14 -0
  22. package/template/app/components/docs/mdx/index.tsx +167 -0
  23. package/template/app/components/docs/mdx/pre.tsx +82 -0
  24. package/template/app/components/docs/mdx/steps.tsx +59 -0
  25. package/template/app/components/docs/mdx/tabs.tsx +60 -0
  26. package/template/app/components/docs/mdx/youtube.tsx +18 -0
  27. package/template/app/components/docs/search-dialog.tsx +281 -0
  28. package/template/app/components/docs/theme-toggle.tsx +35 -0
  29. package/template/app/docs/[[...slug]]/page.tsx +139 -0
  30. package/template/app/docs/layout.tsx +98 -0
  31. package/template/app/globals.css +151 -0
  32. package/template/app/layout.tsx +33 -0
  33. package/template/app/page.tsx +5 -0
  34. package/template/app/providers/theme-provider.tsx +8 -0
  35. package/template/content/docs/components.mdx +82 -0
  36. package/template/content/docs/customization.mdx +34 -0
  37. package/template/content/docs/deployment.mdx +28 -0
  38. package/template/content/docs/index.mdx +91 -0
  39. package/template/content/docs/meta.json +13 -0
  40. package/template/content/docs/quickstart.mdx +110 -0
  41. package/template/content/docs/theming.mdx +41 -0
  42. package/template/lib/docs-source.ts +7 -0
  43. package/template/lib/theme-config.ts +89 -0
  44. package/template/lib/utils.ts +6 -0
  45. package/template/next.config.mjs +10 -0
  46. package/template/package-lock.json +10695 -0
  47. package/template/package.json +45 -0
  48. package/template/postcss.config.mjs +7 -0
  49. package/template/public/logo.png +0 -0
  50. package/template/public/logo.svg +9 -0
  51. package/template/public/logo.txt +1 -0
  52. package/template/source.config.ts +22 -0
  53. package/template/tailwind.config.ts +34 -0
  54. package/template/tsconfig.json +33 -0
  55. package/template/vitest.config.ts +16 -0
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # create-unmint
2
+
3
+ Create beautiful documentation sites with Unmint - a free, open-source Mintlify alternative.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ # Create a new documentation project
9
+ npx create-unmint@latest my-docs
10
+
11
+ # Create with all defaults (skip prompts)
12
+ npx create-unmint@latest my-docs --yes
13
+
14
+ # Update an existing project
15
+ cd my-docs
16
+ npx create-unmint@latest --update
17
+ ```
18
+
19
+ ## What You Get
20
+
21
+ - **Beautiful out-of-the-box** - Professional styling without configuration
22
+ - **MDX Components** - Cards, callouts, tabs, steps, accordions, and more
23
+ - **Built-in Search** - Full-text search powered by Fumadocs
24
+ - **Dynamic OG Images** - Auto-generated social preview images
25
+ - **Dark Mode** - Seamless light/dark theme switching
26
+ - **Easy Theming** - Single config file for all customization
27
+
28
+ ## Interactive Setup
29
+
30
+ When you run `npx create-unmint@latest`, you'll be prompted to configure:
31
+
32
+ - **Project name** - Name of your documentation project
33
+ - **Description** - A brief description for SEO and metadata
34
+ - **Accent color** - Choose from presets or enter a custom hex color
35
+ - **GitHub URL** - Link to your project's repository (optional)
36
+ - **Site URL** - Where your docs will be deployed
37
+
38
+ ## Project Structure
39
+
40
+ ```
41
+ my-docs/
42
+ ├── app/ # Next.js app directory
43
+ ├── content/
44
+ │ └── docs/ # Your MDX documentation files
45
+ ├── lib/
46
+ │ └── theme-config.ts # Site configuration
47
+ └── public/ # Static assets (logo, images)
48
+ ```
49
+
50
+ ## Learn More
51
+
52
+ - [Unmint GitHub](https://github.com/gregce/unmint)
53
+ - [Fumadocs Documentation](https://fumadocs.vercel.app)
54
+
55
+ ## License
56
+
57
+ MIT
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,499 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Command } from "commander";
5
+ import chalk4 from "chalk";
6
+
7
+ // src/commands/init.ts
8
+ import fs2 from "fs-extra";
9
+ import path2 from "path";
10
+ import chalk2 from "chalk";
11
+ import ora from "ora";
12
+ import { execa } from "execa";
13
+
14
+ // src/prompts.ts
15
+ import inquirer from "inquirer";
16
+ import chalk from "chalk";
17
+ var accentColors = [
18
+ { name: "Cyan", value: "#0891b2" },
19
+ { name: "Blue", value: "#3b82f6" },
20
+ { name: "Green", value: "#22c55e" },
21
+ { name: "Purple", value: "#a855f7" },
22
+ { name: "Orange", value: "#f97316" },
23
+ { name: "Pink", value: "#ec4899" },
24
+ { name: "Custom", value: "custom" }
25
+ ];
26
+ async function promptProjectConfig(defaultName) {
27
+ const answers = await inquirer.prompt([
28
+ {
29
+ type: "input",
30
+ name: "projectName",
31
+ message: "Project name:",
32
+ default: defaultName || "my-docs",
33
+ validate: (input) => {
34
+ if (!input.trim()) return "Project name is required";
35
+ if (!/^[a-z0-9-_]+$/i.test(input)) {
36
+ return "Project name can only contain letters, numbers, hyphens, and underscores";
37
+ }
38
+ return true;
39
+ }
40
+ },
41
+ {
42
+ type: "input",
43
+ name: "description",
44
+ message: "Description:",
45
+ default: "Documentation for my project"
46
+ },
47
+ {
48
+ type: "list",
49
+ name: "accentColor",
50
+ message: "Accent color:",
51
+ choices: accentColors.map((c) => ({
52
+ name: c.name === "Cyan" ? `${c.name} ${chalk.dim("(default)")}` : c.name,
53
+ value: c.value
54
+ })),
55
+ default: "#0891b2"
56
+ },
57
+ {
58
+ type: "input",
59
+ name: "customAccent",
60
+ message: "Custom accent color (hex):",
61
+ when: (answers2) => answers2.accentColor === "custom",
62
+ validate: (input) => {
63
+ if (!/^#[0-9a-f]{6}$/i.test(input)) {
64
+ return "Please enter a valid hex color (e.g., #ff5733)";
65
+ }
66
+ return true;
67
+ }
68
+ },
69
+ {
70
+ type: "input",
71
+ name: "githubUrl",
72
+ message: `GitHub URL ${chalk.dim("(optional)")}:`,
73
+ default: ""
74
+ },
75
+ {
76
+ type: "input",
77
+ name: "siteUrl",
78
+ message: "Site URL:",
79
+ default: "https://docs.example.com"
80
+ },
81
+ {
82
+ type: "confirm",
83
+ name: "initGit",
84
+ message: "Initialize git repository?",
85
+ default: true
86
+ },
87
+ {
88
+ type: "confirm",
89
+ name: "installDeps",
90
+ message: "Install dependencies?",
91
+ default: true
92
+ }
93
+ ]);
94
+ if (answers.accentColor === "custom" && answers.customAccent) {
95
+ answers.accentColor = answers.customAccent;
96
+ }
97
+ return answers;
98
+ }
99
+ function getDefaultConfig(projectName) {
100
+ return {
101
+ projectName,
102
+ description: "Documentation for my project",
103
+ accentColor: "#0891b2",
104
+ githubUrl: "",
105
+ siteUrl: "https://docs.example.com",
106
+ initGit: true,
107
+ installDeps: true
108
+ };
109
+ }
110
+
111
+ // src/scaffold.ts
112
+ import fs from "fs-extra";
113
+ import path from "path";
114
+ import { fileURLToPath } from "url";
115
+ var __dirname = path.dirname(fileURLToPath(import.meta.url));
116
+ var EXCLUDE_PATTERNS = [
117
+ "node_modules",
118
+ ".next",
119
+ ".git",
120
+ "dist",
121
+ ".turbo",
122
+ ".vercel"
123
+ ];
124
+ async function scaffoldProject(targetDir, config) {
125
+ const bundledTemplate = path.resolve(__dirname, "../template");
126
+ const devTemplate = path.resolve(__dirname, "../../template");
127
+ const templateDir = await fs.pathExists(bundledTemplate) ? bundledTemplate : devTemplate;
128
+ if (!await fs.pathExists(templateDir)) {
129
+ throw new Error(`Template not found. Please ensure you're running from a valid create-unmint installation.`);
130
+ }
131
+ await fs.ensureDir(targetDir);
132
+ await copyTemplateFiles(templateDir, targetDir);
133
+ await transformFiles(targetDir, config);
134
+ await createVersionFile(targetDir);
135
+ }
136
+ async function copyTemplateFiles(sourceDir, targetDir) {
137
+ const entries = await fs.readdir(sourceDir, { withFileTypes: true });
138
+ for (const entry of entries) {
139
+ const sourcePath = path.join(sourceDir, entry.name);
140
+ const targetPath = path.join(targetDir, entry.name);
141
+ if (EXCLUDE_PATTERNS.some((pattern) => entry.name === pattern)) {
142
+ continue;
143
+ }
144
+ if (entry.isDirectory()) {
145
+ await fs.ensureDir(targetPath);
146
+ await copyTemplateFiles(sourcePath, targetPath);
147
+ } else {
148
+ await fs.copy(sourcePath, targetPath);
149
+ }
150
+ }
151
+ }
152
+ async function transformFiles(targetDir, config) {
153
+ const packageJsonPath = path.join(targetDir, "package.json");
154
+ if (await fs.pathExists(packageJsonPath)) {
155
+ const packageJson = await fs.readJson(packageJsonPath);
156
+ packageJson.name = config.projectName;
157
+ packageJson.description = config.description;
158
+ packageJson.version = "0.1.0";
159
+ delete packageJson.repository;
160
+ delete packageJson.keywords;
161
+ delete packageJson.author;
162
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
163
+ }
164
+ const themeConfigPath = path.join(targetDir, "lib/theme-config.ts");
165
+ if (await fs.pathExists(themeConfigPath)) {
166
+ let content = await fs.readFile(themeConfigPath, "utf-8");
167
+ content = content.replace(
168
+ /name:\s*['"][^'"]*['"]/,
169
+ `name: '${config.projectName}'`
170
+ );
171
+ content = content.replace(
172
+ /description:\s*['"][^'"]*['"]/,
173
+ `description: '${config.description}'`
174
+ );
175
+ content = content.replace(
176
+ /url:\s*['"][^'"]*['"]/,
177
+ `url: '${config.siteUrl}'`
178
+ );
179
+ if (config.githubUrl) {
180
+ content = content.replace(
181
+ /github:\s*['"][^'"]*['"]/,
182
+ `github: '${config.githubUrl}'`
183
+ );
184
+ }
185
+ await fs.writeFile(themeConfigPath, content);
186
+ }
187
+ const globalsCssPath = path.join(targetDir, "app/globals.css");
188
+ if (await fs.pathExists(globalsCssPath)) {
189
+ let content = await fs.readFile(globalsCssPath, "utf-8");
190
+ content = content.replace(
191
+ /--accent:\s*#[0-9a-fA-F]{6}/g,
192
+ `--accent: ${config.accentColor}`
193
+ );
194
+ const lighterAccent = lightenColor(config.accentColor);
195
+ content = content.replace(
196
+ /\.dark\s*\{[^}]*--accent:\s*#[0-9a-fA-F]{6}/,
197
+ (match) => match.replace(/--accent:\s*#[0-9a-fA-F]{6}/, `--accent: ${lighterAccent}`)
198
+ );
199
+ await fs.writeFile(globalsCssPath, content);
200
+ }
201
+ const indexMdxPath = path.join(targetDir, "content/docs/index.mdx");
202
+ if (await fs.pathExists(indexMdxPath)) {
203
+ let content = await fs.readFile(indexMdxPath, "utf-8");
204
+ content = content.replace(
205
+ /title:\s*['"][^'"]*['"]/,
206
+ `title: '${config.projectName}'`
207
+ );
208
+ content = content.replace(
209
+ /description:\s*['"][^'"]*['"]/,
210
+ `description: '${config.description}'`
211
+ );
212
+ await fs.writeFile(indexMdxPath, content);
213
+ }
214
+ const readmePath = path.join(targetDir, "README.md");
215
+ await fs.writeFile(readmePath, generateReadme(config));
216
+ }
217
+ async function createVersionFile(targetDir) {
218
+ const unmintDir = path.join(targetDir, ".unmint");
219
+ await fs.ensureDir(unmintDir);
220
+ const versionInfo = {
221
+ version: "1.0.0",
222
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
223
+ generator: "create-unmint"
224
+ };
225
+ await fs.writeJson(path.join(unmintDir, "version.json"), versionInfo, { spaces: 2 });
226
+ }
227
+ function lightenColor(hex) {
228
+ const r = parseInt(hex.slice(1, 3), 16);
229
+ const g = parseInt(hex.slice(3, 5), 16);
230
+ const b = parseInt(hex.slice(5, 7), 16);
231
+ const lighten = (c) => Math.min(255, Math.round(c + (255 - c) * 0.3));
232
+ const lr = lighten(r).toString(16).padStart(2, "0");
233
+ const lg = lighten(g).toString(16).padStart(2, "0");
234
+ const lb = lighten(b).toString(16).padStart(2, "0");
235
+ return `#${lr}${lg}${lb}`;
236
+ }
237
+ function generateReadme(config) {
238
+ return `# ${config.projectName}
239
+
240
+ ${config.description}
241
+
242
+ ## Getting Started
243
+
244
+ \`\`\`bash
245
+ # Install dependencies
246
+ npm install
247
+
248
+ # Start development server
249
+ npm run dev
250
+ \`\`\`
251
+
252
+ Your docs will be available at [http://localhost:3000](http://localhost:3000)
253
+
254
+ ## Project Structure
255
+
256
+ \`\`\`
257
+ \u251C\u2500\u2500 app/ # Next.js app directory
258
+ \u251C\u2500\u2500 content/
259
+ \u2502 \u2514\u2500\u2500 docs/ # Your documentation (MDX files)
260
+ \u251C\u2500\u2500 lib/
261
+ \u2502 \u2514\u2500\u2500 theme-config.ts # Site configuration
262
+ \u2514\u2500\u2500 public/ # Static assets
263
+ \`\`\`
264
+
265
+ ## Writing Documentation
266
+
267
+ Add MDX files to \`content/docs/\` to create new pages. The sidebar navigation is automatically generated based on your file structure.
268
+
269
+ ## Built with Unmint
270
+
271
+ This documentation site was created with [Unmint](https://github.com/gregce/unmint), a free and open-source documentation system.
272
+ `;
273
+ }
274
+
275
+ // src/commands/init.ts
276
+ async function init(projectName, options = {}) {
277
+ let config;
278
+ if (options.yes && projectName) {
279
+ config = getDefaultConfig(projectName);
280
+ console.log(chalk2.dim(" Using default configuration..."));
281
+ console.log();
282
+ } else {
283
+ config = await promptProjectConfig(projectName);
284
+ }
285
+ const targetDir = path2.resolve(process.cwd(), config.projectName);
286
+ if (await fs2.pathExists(targetDir)) {
287
+ const isEmpty = (await fs2.readdir(targetDir)).length === 0;
288
+ if (!isEmpty) {
289
+ console.log(chalk2.red(` Error: Directory "${config.projectName}" already exists and is not empty.`));
290
+ console.log(chalk2.dim(` Please choose a different name or remove the existing directory.`));
291
+ process.exit(1);
292
+ }
293
+ }
294
+ console.log();
295
+ const scaffoldSpinner = ora("Creating project structure...").start();
296
+ try {
297
+ await scaffoldProject(targetDir, config);
298
+ scaffoldSpinner.succeed("Project structure created");
299
+ } catch (error) {
300
+ scaffoldSpinner.fail("Failed to create project structure");
301
+ console.error(chalk2.red(` ${error instanceof Error ? error.message : error}`));
302
+ process.exit(1);
303
+ }
304
+ if (config.initGit) {
305
+ const gitSpinner = ora("Initializing git repository...").start();
306
+ try {
307
+ await execa("git", ["init"], { cwd: targetDir });
308
+ await execa("git", ["add", "-A"], { cwd: targetDir });
309
+ await execa("git", ["commit", "-m", "Initial commit from create-unmint"], { cwd: targetDir });
310
+ gitSpinner.succeed("Git repository initialized");
311
+ } catch (error) {
312
+ gitSpinner.warn("Could not initialize git repository");
313
+ }
314
+ }
315
+ if (config.installDeps) {
316
+ const installSpinner = ora("Installing dependencies...").start();
317
+ try {
318
+ const packageManager = await detectPackageManager();
319
+ await execa(packageManager, ["install"], { cwd: targetDir });
320
+ installSpinner.succeed(`Dependencies installed with ${packageManager}`);
321
+ } catch (error) {
322
+ installSpinner.warn("Could not install dependencies");
323
+ console.log(chalk2.dim(` Run "npm install" manually to install dependencies`));
324
+ }
325
+ }
326
+ console.log();
327
+ console.log(chalk2.green.bold(" Success!") + ` Created ${chalk2.cyan(config.projectName)}`);
328
+ console.log();
329
+ console.log(" Next steps:");
330
+ console.log();
331
+ console.log(chalk2.cyan(` cd ${config.projectName}`));
332
+ if (!config.installDeps) {
333
+ console.log(chalk2.cyan(" npm install"));
334
+ }
335
+ console.log(chalk2.cyan(" npm run dev"));
336
+ console.log();
337
+ console.log(chalk2.dim(` Your docs will be at ${chalk2.white("http://localhost:3000")}`));
338
+ console.log();
339
+ }
340
+ async function detectPackageManager() {
341
+ const cwd = process.cwd();
342
+ if (await fs2.pathExists(path2.join(cwd, "pnpm-lock.yaml"))) {
343
+ return "pnpm";
344
+ }
345
+ if (await fs2.pathExists(path2.join(cwd, "yarn.lock"))) {
346
+ return "yarn";
347
+ }
348
+ if (await fs2.pathExists(path2.join(cwd, "bun.lockb"))) {
349
+ return "bun";
350
+ }
351
+ const userAgent = process.env.npm_config_user_agent || "";
352
+ if (userAgent.includes("pnpm")) return "pnpm";
353
+ if (userAgent.includes("yarn")) return "yarn";
354
+ if (userAgent.includes("bun")) return "bun";
355
+ return "npm";
356
+ }
357
+
358
+ // src/commands/update.ts
359
+ import fs3 from "fs-extra";
360
+ import path3 from "path";
361
+ import chalk3 from "chalk";
362
+ import ora2 from "ora";
363
+ import inquirer2 from "inquirer";
364
+ async function update(options = {}) {
365
+ const cwd = process.cwd();
366
+ const versionFilePath = path3.join(cwd, ".unmint/version.json");
367
+ if (!await fs3.pathExists(versionFilePath)) {
368
+ console.log(chalk3.red(" Error: This does not appear to be an Unmint project."));
369
+ console.log(chalk3.dim(" Run this command from the root of an Unmint documentation project."));
370
+ console.log();
371
+ console.log(chalk3.dim(" To create a new project, run:"));
372
+ console.log(chalk3.cyan(" npx create-unmint@latest my-docs"));
373
+ process.exit(1);
374
+ }
375
+ const versionInfo = await fs3.readJson(versionFilePath);
376
+ const currentVersion = versionInfo.version;
377
+ console.log(chalk3.dim(` Current version: ${currentVersion}`));
378
+ console.log();
379
+ if (options.dryRun) {
380
+ console.log(chalk3.yellow(" Dry run mode - no changes will be made"));
381
+ console.log();
382
+ }
383
+ const spinner = ora2("Checking for updates...").start();
384
+ try {
385
+ const latestVersion = "1.0.0";
386
+ if (currentVersion === latestVersion) {
387
+ spinner.succeed("Already up to date!");
388
+ return;
389
+ }
390
+ spinner.info(`Update available: ${currentVersion} \u2192 ${latestVersion}`);
391
+ console.log();
392
+ const changes = await analyzeChanges(cwd);
393
+ if (changes.length === 0) {
394
+ console.log(chalk3.green(" No files need updating."));
395
+ return;
396
+ }
397
+ console.log(" Changes:");
398
+ for (const change of changes) {
399
+ const icon = change.type === "update" ? "\u21BB" : change.type === "add" ? "+" : "\u2212";
400
+ const color = change.type === "update" ? chalk3.blue : change.type === "add" ? chalk3.green : chalk3.red;
401
+ const status = change.protected ? chalk3.yellow(" (protected - skip)") : "";
402
+ console.log(` ${color(icon)} ${change.file}${status}`);
403
+ }
404
+ console.log();
405
+ if (options.dryRun) {
406
+ console.log(chalk3.dim(" Dry run complete. No changes were made."));
407
+ return;
408
+ }
409
+ const { proceed } = await inquirer2.prompt([
410
+ {
411
+ type: "confirm",
412
+ name: "proceed",
413
+ message: "Proceed with update?",
414
+ default: true
415
+ }
416
+ ]);
417
+ if (!proceed) {
418
+ console.log(chalk3.dim(" Update cancelled."));
419
+ return;
420
+ }
421
+ const backupSpinner = ora2("Creating backup...").start();
422
+ const backupDir = await createBackup(cwd);
423
+ backupSpinner.succeed(`Backup created at ${path3.relative(cwd, backupDir)}`);
424
+ const updateSpinner = ora2("Applying updates...").start();
425
+ const results = await applyUpdates(cwd, changes);
426
+ updateSpinner.succeed("Updates applied");
427
+ versionInfo.version = latestVersion;
428
+ versionInfo.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
429
+ await fs3.writeJson(versionFilePath, versionInfo, { spaces: 2 });
430
+ console.log();
431
+ console.log(chalk3.green.bold(" Update complete!"));
432
+ console.log();
433
+ console.log(` Updated: ${results.updated}`);
434
+ console.log(` Skipped: ${results.skipped}`);
435
+ console.log(` Added: ${results.added}`);
436
+ console.log();
437
+ } catch (error) {
438
+ spinner.fail("Update failed");
439
+ console.error(chalk3.red(` ${error instanceof Error ? error.message : error}`));
440
+ process.exit(1);
441
+ }
442
+ }
443
+ async function analyzeChanges(projectDir) {
444
+ return [];
445
+ }
446
+ async function createBackup(projectDir) {
447
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
448
+ const backupDir = path3.join(projectDir, ".unmint/backup", timestamp);
449
+ await fs3.ensureDir(backupDir);
450
+ const filesToBackup = [
451
+ "lib/theme-config.ts",
452
+ "app/globals.css",
453
+ "package.json"
454
+ ];
455
+ for (const file of filesToBackup) {
456
+ const sourcePath = path3.join(projectDir, file);
457
+ if (await fs3.pathExists(sourcePath)) {
458
+ const targetPath = path3.join(backupDir, file);
459
+ await fs3.ensureDir(path3.dirname(targetPath));
460
+ await fs3.copy(sourcePath, targetPath);
461
+ }
462
+ }
463
+ return backupDir;
464
+ }
465
+ async function applyUpdates(projectDir, changes) {
466
+ const results = {
467
+ updated: 0,
468
+ skipped: 0,
469
+ added: 0
470
+ };
471
+ for (const change of changes) {
472
+ if (change.protected) {
473
+ results.skipped++;
474
+ continue;
475
+ }
476
+ if (change.type === "update") {
477
+ results.updated++;
478
+ } else if (change.type === "add") {
479
+ results.added++;
480
+ }
481
+ }
482
+ return results;
483
+ }
484
+
485
+ // src/index.ts
486
+ var program = new Command();
487
+ program.name("create-unmint").description("Create and manage Unmint documentation projects").version("1.0.0");
488
+ program.argument("[project-name]", "Name of the project to create").option("-y, --yes", "Skip prompts and use defaults").option("--update", "Update an existing Unmint project").option("--dry-run", "Show what would be updated without making changes").action(async (projectName, options) => {
489
+ console.log();
490
+ console.log(chalk4.cyan.bold(" Unmint"));
491
+ console.log(chalk4.dim(" Beautiful documentation, open source"));
492
+ console.log();
493
+ if (options.update) {
494
+ await update(options);
495
+ } else {
496
+ await init(projectName, options);
497
+ }
498
+ });
499
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "create-unmint",
3
+ "version": "1.0.0",
4
+ "description": "Create a new Unmint documentation project",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-unmint": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "template"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsup src/index.ts --format esm --dts --clean && npm run copy-template",
15
+ "copy-template": "rm -rf template && cp -r ../template template && rm -rf template/node_modules template/.next template/.git",
16
+ "dev": "tsup src/index.ts --format esm --watch",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "dependencies": {
20
+ "chalk": "^5.3.0",
21
+ "commander": "^12.1.0",
22
+ "execa": "^9.5.2",
23
+ "fs-extra": "^11.2.0",
24
+ "inquirer": "^12.3.0",
25
+ "ora": "^8.1.1"
26
+ },
27
+ "devDependencies": {
28
+ "@types/fs-extra": "^11.0.4",
29
+ "@types/node": "^22.10.7",
30
+ "tsup": "^8.3.6",
31
+ "typescript": "^5.7.3"
32
+ },
33
+ "keywords": [
34
+ "create",
35
+ "unmint",
36
+ "documentation",
37
+ "docs",
38
+ "cli",
39
+ "scaffold"
40
+ ],
41
+ "author": "Greg Ceccarelli",
42
+ "license": "MIT",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/gregce/unmint.git",
46
+ "directory": "packages/create-unmint"
47
+ }
48
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Unmint Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.