barodoc 1.0.1 → 2.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.
@@ -2,6 +2,7 @@
2
2
  import fs from "fs-extra";
3
3
  import path from "path";
4
4
  import pc from "picocolors";
5
+ import { execa } from "execa";
5
6
  var BARODOC_DIR = ".barodoc";
6
7
  function isCustomProject(dir) {
7
8
  return fs.existsSync(path.join(dir, "astro.config.mjs")) || fs.existsSync(path.join(dir, "astro.config.ts")) || fs.existsSync(path.join(dir, "astro.config.js"));
@@ -37,14 +38,32 @@ async function createProject(options) {
37
38
  const { root, docsDir, config, configPath } = options;
38
39
  const projectDir = path.join(root, BARODOC_DIR);
39
40
  console.log(pc.dim(`Creating temporary project in ${BARODOC_DIR}/`));
40
- await fs.remove(projectDir);
41
- await fs.ensureDir(projectDir);
41
+ const nodeModulesDir = path.join(projectDir, "node_modules");
42
+ const hasNodeModules = fs.existsSync(nodeModulesDir);
43
+ if (hasNodeModules) {
44
+ const entries = await fs.readdir(projectDir);
45
+ for (const entry of entries) {
46
+ if (entry !== "node_modules") {
47
+ await fs.remove(path.join(projectDir, entry));
48
+ }
49
+ }
50
+ } else {
51
+ await fs.remove(projectDir);
52
+ await fs.ensureDir(projectDir);
53
+ }
42
54
  await fs.writeJSON(
43
55
  path.join(projectDir, "package.json"),
44
56
  {
45
57
  name: "barodoc-temp",
46
58
  type: "module",
47
- private: true
59
+ private: true,
60
+ dependencies: {
61
+ astro: "^5.0.0",
62
+ "@barodoc/core": "^1.0.0",
63
+ "@barodoc/theme-docs": "^1.0.0",
64
+ react: "^19.0.0",
65
+ "react-dom": "^19.0.0"
66
+ }
48
67
  },
49
68
  { spaces: 2 }
50
69
  );
@@ -85,16 +104,45 @@ async function createProject(options) {
85
104
  }
86
105
  return projectDir;
87
106
  }
107
+ async function installDependencies(projectDir, force = false) {
108
+ const nodeModulesDir = path.join(projectDir, "node_modules");
109
+ if (!force && fs.existsSync(nodeModulesDir)) {
110
+ console.log(pc.dim("Using cached dependencies..."));
111
+ console.log();
112
+ return;
113
+ }
114
+ if (force && fs.existsSync(nodeModulesDir)) {
115
+ console.log(pc.dim("Clearing cached dependencies..."));
116
+ await fs.remove(nodeModulesDir);
117
+ }
118
+ console.log(pc.dim("Installing dependencies..."));
119
+ await execa("npm", ["install", "--prefer-offline"], {
120
+ cwd: projectDir,
121
+ stdio: "inherit"
122
+ });
123
+ console.log(pc.green("\u2713 Dependencies installed"));
124
+ console.log();
125
+ }
88
126
  async function cleanupProject(root) {
89
127
  const projectDir = path.join(root, BARODOC_DIR);
90
- await fs.remove(projectDir);
128
+ if (!fs.existsSync(projectDir)) return;
129
+ const entries = await fs.readdir(projectDir);
130
+ for (const entry of entries) {
131
+ if (entry !== "node_modules") {
132
+ await fs.remove(path.join(projectDir, entry));
133
+ }
134
+ }
91
135
  }
92
136
  function generateAstroConfig(config, configPath, docsDir) {
137
+ const siteLine = config.site ? `
138
+ site: ${JSON.stringify(config.site)},` : "";
139
+ const baseLine = config.base ? `
140
+ base: ${JSON.stringify(config.base)},` : "";
93
141
  return `import { defineConfig } from "astro/config";
94
142
  import barodoc from "@barodoc/core";
95
143
  import docsTheme from "@barodoc/theme-docs";
96
144
 
97
- export default defineConfig({
145
+ export default defineConfig({${siteLine}${baseLine}
98
146
  integrations: [
99
147
  barodoc({
100
148
  config: "./barodoc.config.json",
@@ -110,7 +158,7 @@ function generateContentConfig() {
110
158
  const docsCollection = defineCollection({
111
159
  type: "content",
112
160
  schema: z.object({
113
- title: z.string(),
161
+ title: z.string().optional(),
114
162
  description: z.string().optional(),
115
163
  }),
116
164
  });
@@ -135,6 +183,7 @@ export {
135
183
  isCustomProject,
136
184
  loadProjectConfig,
137
185
  createProject,
186
+ installDependencies,
138
187
  cleanupProject,
139
188
  findDocsDir
140
189
  };
package/dist/cli.js CHANGED
@@ -3,16 +3,17 @@ import {
3
3
  cleanupProject,
4
4
  createProject,
5
5
  findDocsDir,
6
+ installDependencies,
6
7
  isCustomProject,
7
8
  loadProjectConfig
8
- } from "./chunk-KI3NAGJ3.js";
9
+ } from "./chunk-JIP64MDC.js";
9
10
 
10
11
  // src/cli.ts
11
12
  import cac from "cac";
12
- import pc5 from "picocolors";
13
+ import pc8 from "picocolors";
13
14
 
14
15
  // package.json
15
- var version = "1.0.1";
16
+ var version = "2.0.0";
16
17
 
17
18
  // src/commands/serve.ts
18
19
  import path from "path";
@@ -41,6 +42,7 @@ async function serve(dir, options) {
41
42
  });
42
43
  console.log(pc.green("\u2713 Project created"));
43
44
  console.log();
45
+ await installDependencies(projectDir, options.clean);
44
46
  await runAstroDev(projectDir, options);
45
47
  }
46
48
  async function runAstroDev(projectDir, options) {
@@ -51,6 +53,9 @@ async function runAstroDev(projectDir, options) {
51
53
  if (options.host) {
52
54
  args.push("--host");
53
55
  }
56
+ if (options.open) {
57
+ args.push("--open");
58
+ }
54
59
  try {
55
60
  await execa("npx", args, {
56
61
  cwd: projectDir,
@@ -95,6 +100,7 @@ async function build(dir, options) {
95
100
  });
96
101
  console.log(pc2.green("\u2713 Project created"));
97
102
  console.log();
103
+ await installDependencies(projectDir, options.clean);
98
104
  try {
99
105
  await runAstroBuild(projectDir, path2.join(projectDir, "dist"));
100
106
  const tempDist = path2.join(projectDir, "dist");
@@ -137,6 +143,235 @@ async function runAstroBuild(projectDir, outputDir) {
137
143
  import path3 from "path";
138
144
  import pc3 from "picocolors";
139
145
  import fs2 from "fs-extra";
146
+
147
+ // src/runtime/agentRules.ts
148
+ function generateAgentRules(projectName) {
149
+ return `# Barodoc Documentation Rules for AI Agents
150
+
151
+ This file defines the rules for generating and editing documentation in this project.
152
+ It is used by AI assistants (Claude, Cursor, GitHub Copilot, etc.) to produce
153
+ markdown files that are fully compatible with barodoc.
154
+
155
+ ## Project: ${projectName}
156
+
157
+ ---
158
+
159
+ ## File Structure
160
+
161
+ \`\`\`
162
+ docs/
163
+ {locale}/ # e.g., en/, ko/, ja/
164
+ {slug}.md # Markdown or MDX files
165
+ {slug}.mdx
166
+
167
+ public/ # Static assets (images, logo, etc.)
168
+ barodoc.config.json # Site configuration
169
+ \`\`\`
170
+
171
+ - Each page is a single \`.md\` or \`.mdx\` file inside \`docs/{locale}/\`.
172
+ - The filename (without extension) becomes the **slug** used in navigation.
173
+ - Use \`.mdx\` when you need to include interactive components (Callout, Steps, etc.).
174
+ - Use \`.md\` for plain text-only pages.
175
+
176
+ ---
177
+
178
+ ## Frontmatter
179
+
180
+ Every page **should** include frontmatter at the top:
181
+
182
+ \`\`\`md
183
+ ---
184
+ title: Page Title
185
+ description: A brief description shown in search results and meta tags.
186
+ ---
187
+ \`\`\`
188
+
189
+ - \`title\` \u2014 Optional. If omitted, the first \`#\` heading is used.
190
+ - \`description\` \u2014 Recommended. Used for SEO and search result snippets.
191
+
192
+ ---
193
+
194
+ ## Navigation Registration
195
+
196
+ After creating a new file, register it in \`barodoc.config.json\`:
197
+
198
+ \`\`\`json
199
+ {
200
+ "navigation": [
201
+ {
202
+ "group": "Getting Started",
203
+ "group:ko": "\uC2DC\uC791\uD558\uAE30",
204
+ "pages": ["introduction", "quickstart"]
205
+ },
206
+ {
207
+ "group": "Guides",
208
+ "pages": ["installation", "configuration", "deployment"]
209
+ }
210
+ ]
211
+ }
212
+ \`\`\`
213
+
214
+ - \`pages\` contains **slugs only** (filename without extension, without locale prefix).
215
+ - \`group:ko\`, \`group:ja\` etc. provide localized group names.
216
+ - **Every created file must appear in \`navigation\`** or it won't be reachable.
217
+ - Run \`barodoc check\` to detect orphaned files or missing nav entries.
218
+ - Run \`barodoc check --fix\` to auto-repair navigation.
219
+
220
+ ---
221
+
222
+ ## MDX Components
223
+
224
+ When using \`.mdx\` files, these components are available globally (no import needed):
225
+
226
+ ### Callout
227
+
228
+ \`\`\`mdx
229
+ <Callout type="info">This is an informational note.</Callout>
230
+
231
+ <Callout type="warning" title="Watch out!">
232
+ This action cannot be undone.
233
+ </Callout>
234
+
235
+ <Callout type="tip">Use keyboard shortcuts to save time.</Callout>
236
+
237
+ <Callout type="danger">This will permanently delete your data.</Callout>
238
+
239
+ <Callout type="note">Additional context goes here.</Callout>
240
+ \`\`\`
241
+
242
+ Supported types: \`info\` | \`warning\` | \`tip\` | \`danger\` | \`note\`
243
+
244
+ ---
245
+
246
+ ### Steps
247
+
248
+ Use for sequential step-by-step guides:
249
+
250
+ \`\`\`mdx
251
+ <Steps>
252
+ <Step title="Install dependencies">
253
+ \`\`\`bash
254
+ npm install
255
+ \`\`\`
256
+ </Step>
257
+ <Step title="Configure environment">
258
+ Copy \`.env.example\` to \`.env\` and fill in the values.
259
+ </Step>
260
+ <Step title="Run the server">
261
+ \`\`\`bash
262
+ npm run dev
263
+ \`\`\`
264
+ </Step>
265
+ </Steps>
266
+ \`\`\`
267
+
268
+ ---
269
+
270
+ ### Card and CardGroup
271
+
272
+ Use to display a grid of linked cards:
273
+
274
+ \`\`\`mdx
275
+ <CardGroup cols={2}>
276
+ <Card title="Quickstart" href="/quickstart">
277
+ Get up and running in minutes.
278
+ </Card>
279
+ <Card title="API Reference" href="/api-reference">
280
+ Explore the full API documentation.
281
+ </Card>
282
+ </CardGroup>
283
+ \`\`\`
284
+
285
+ - \`cols\` accepts \`1\`, \`2\`, or \`3\`.
286
+ - \`href\` is optional \u2014 cards can be informational without a link.
287
+
288
+ ---
289
+
290
+ ### CodeGroup
291
+
292
+ Display multiple code examples with tabs:
293
+
294
+ \`\`\`mdx
295
+ <CodeGroup>
296
+ \`\`\`bash npm
297
+ npm install @barodoc/core
298
+ \`\`\`
299
+
300
+ \`\`\`bash pnpm
301
+ pnpm add @barodoc/core
302
+ \`\`\`
303
+
304
+ \`\`\`bash yarn
305
+ yarn add @barodoc/core
306
+ \`\`\`
307
+ </CodeGroup>
308
+ \`\`\`
309
+
310
+ The text after the language name becomes the tab label.
311
+
312
+ ---
313
+
314
+ ## i18n (Internationalization)
315
+
316
+ If the project has multiple locales:
317
+
318
+ - Create parallel files: \`docs/en/page.md\` and \`docs/ko/page.md\`.
319
+ - Slugs must match across locales.
320
+ - Navigation uses the same slug for all locales \u2014 only group names can be localized.
321
+
322
+ \`\`\`json
323
+ {
324
+ "i18n": {
325
+ "defaultLocale": "en",
326
+ "locales": ["en", "ko"],
327
+ "labels": { "en": "English", "ko": "\uD55C\uAD6D\uC5B4" }
328
+ }
329
+ }
330
+ \`\`\`
331
+
332
+ ---
333
+
334
+ ## Workflow for Adding a New Page
335
+
336
+ 1. Create \`docs/{locale}/{slug}.md\` (or \`.mdx\` for components).
337
+ 2. Add frontmatter with \`title\` and \`description\`.
338
+ 3. Write content using standard markdown + MDX components if needed.
339
+ 4. Register the slug in \`barodoc.config.json\` under the appropriate navigation group.
340
+ 5. Run \`barodoc check\` to verify everything is correct.
341
+ 6. Run \`barodoc serve\` to preview in the browser.
342
+
343
+ ---
344
+
345
+ ## Content Guidelines
346
+
347
+ - Use \`##\` for top-level sections within a page (the page \`#\` title is auto-rendered).
348
+ - Keep descriptions concise (1\u20132 sentences for \`description\` frontmatter).
349
+ - Use fenced code blocks with language identifiers for syntax highlighting.
350
+ - Prefer \`<Callout type="warning">\` over bold text for important notices.
351
+ - Use \`<Steps>\` for any multi-step process (installation, setup, etc.).
352
+ - Images go in \`public/\` and are referenced as \`/image.png\`.
353
+
354
+ ---
355
+
356
+ ## Validation
357
+
358
+ \`\`\`bash
359
+ # Check for issues (missing files, orphan pages, frontmatter)
360
+ barodoc check
361
+
362
+ # Auto-fix navigation mismatches
363
+ barodoc check --fix
364
+
365
+ # Start dev server
366
+ barodoc serve
367
+
368
+ # Production build
369
+ barodoc build
370
+ \`\`\`
371
+ `;
372
+ }
373
+
374
+ // src/commands/create.ts
140
375
  async function create(name) {
141
376
  const targetDir = path3.resolve(process.cwd(), name);
142
377
  console.log();
@@ -222,6 +457,10 @@ barodoc preview
222
457
  </svg>
223
458
  `
224
459
  );
460
+ await fs2.writeFile(
461
+ path3.join(targetDir, "CLAUDE.md"),
462
+ generateAgentRules(name)
463
+ );
225
464
  await fs2.writeFile(
226
465
  path3.join(targetDir, ".gitignore"),
227
466
  `.barodoc/
@@ -279,12 +518,502 @@ async function preview(dir, options) {
279
518
  }
280
519
  }
281
520
 
521
+ // src/commands/init.ts
522
+ import path5 from "path";
523
+ import pc5 from "picocolors";
524
+ import fs4 from "fs-extra";
525
+ async function init(dir) {
526
+ const root = path5.resolve(process.cwd(), dir);
527
+ console.log();
528
+ console.log(pc5.bold(pc5.cyan(" barodoc init")));
529
+ console.log();
530
+ const configPath = path5.join(root, "barodoc.config.json");
531
+ if (await fs4.pathExists(configPath)) {
532
+ console.log(pc5.yellow("\u26A0 barodoc.config.json already exists."));
533
+ console.log(pc5.dim(" Run barodoc serve to start the development server."));
534
+ console.log();
535
+ return;
536
+ }
537
+ const hasAstroConfig = await fs4.pathExists(path5.join(root, "astro.config.mjs")) || await fs4.pathExists(path5.join(root, "astro.config.ts")) || await fs4.pathExists(path5.join(root, "astro.config.js"));
538
+ if (hasAstroConfig) {
539
+ console.log(pc5.yellow("\u26A0 Detected existing Astro config."));
540
+ console.log(
541
+ pc5.dim(
542
+ " This looks like a full Astro project. barodoc init is for quick mode only."
543
+ )
544
+ );
545
+ console.log();
546
+ return;
547
+ }
548
+ const projectName = path5.basename(root);
549
+ console.log(pc5.dim(`Initializing barodoc in ${dir === "." ? "current directory" : dir}/`));
550
+ const docsDir = path5.join(root, "docs", "en");
551
+ const docsExists = await fs4.pathExists(docsDir);
552
+ if (!docsExists) {
553
+ await fs4.ensureDir(docsDir);
554
+ await fs4.writeFile(
555
+ path5.join(docsDir, "introduction.md"),
556
+ `# Introduction
557
+
558
+ Welcome to your documentation site!
559
+
560
+ ## Getting Started
561
+
562
+ Edit this file at \`docs/en/introduction.md\` to customize your documentation.
563
+
564
+ ## Features
565
+
566
+ - Write documentation in Markdown
567
+ - Dark mode support
568
+ - Full-text search
569
+ - i18n support
570
+ `
571
+ );
572
+ await fs4.writeFile(
573
+ path5.join(docsDir, "quickstart.md"),
574
+ `# Quick Start
575
+
576
+ ## Development
577
+
578
+ \`\`\`bash
579
+ barodoc serve
580
+ \`\`\`
581
+
582
+ ## Build
583
+
584
+ \`\`\`bash
585
+ barodoc build
586
+ \`\`\`
587
+
588
+ ## Preview
589
+
590
+ \`\`\`bash
591
+ barodoc preview
592
+ \`\`\`
593
+ `
594
+ );
595
+ console.log(pc5.green("\u2713 Created docs/en/introduction.md"));
596
+ console.log(pc5.green("\u2713 Created docs/en/quickstart.md"));
597
+ } else {
598
+ console.log(pc5.dim(" docs/ directory already exists, skipping sample docs"));
599
+ }
600
+ await fs4.writeJSON(
601
+ configPath,
602
+ {
603
+ name: projectName,
604
+ navigation: [
605
+ {
606
+ group: "Getting Started",
607
+ pages: ["introduction", "quickstart"]
608
+ }
609
+ ],
610
+ i18n: {
611
+ defaultLocale: "en",
612
+ locales: ["en"]
613
+ },
614
+ search: {
615
+ enabled: true
616
+ }
617
+ },
618
+ { spaces: 2 }
619
+ );
620
+ console.log(pc5.green("\u2713 Created barodoc.config.json"));
621
+ const publicDir = path5.join(root, "public");
622
+ if (!await fs4.pathExists(publicDir)) {
623
+ await fs4.ensureDir(publicDir);
624
+ console.log(pc5.green("\u2713 Created public/"));
625
+ }
626
+ const claudeMdPath = path5.join(root, "CLAUDE.md");
627
+ if (!await fs4.pathExists(claudeMdPath)) {
628
+ await fs4.writeFile(claudeMdPath, generateAgentRules(projectName));
629
+ console.log(pc5.green("\u2713 Created CLAUDE.md (AI agent documentation rules)"));
630
+ }
631
+ const gitignorePath = path5.join(root, ".gitignore");
632
+ const gitignoreEntries = [".barodoc/", "dist/", "node_modules/", ".DS_Store"];
633
+ if (await fs4.pathExists(gitignorePath)) {
634
+ const existing = await fs4.readFile(gitignorePath, "utf-8");
635
+ const toAdd = gitignoreEntries.filter((e) => !existing.includes(e));
636
+ if (toAdd.length > 0) {
637
+ await fs4.appendFile(gitignorePath, "\n" + toAdd.join("\n") + "\n");
638
+ console.log(pc5.green("\u2713 Updated .gitignore"));
639
+ }
640
+ } else {
641
+ await fs4.writeFile(gitignorePath, gitignoreEntries.join("\n") + "\n");
642
+ console.log(pc5.green("\u2713 Created .gitignore"));
643
+ }
644
+ console.log();
645
+ console.log(pc5.bold("Done! Your project is ready."));
646
+ console.log();
647
+ console.log("Next steps:");
648
+ console.log();
649
+ if (dir !== ".") {
650
+ console.log(` ${pc5.cyan(`cd ${dir}`)}`);
651
+ }
652
+ console.log(` ${pc5.cyan("barodoc serve")}`);
653
+ console.log();
654
+ }
655
+
656
+ // src/commands/eject.ts
657
+ import path6 from "path";
658
+ import pc6 from "picocolors";
659
+ import fs5 from "fs-extra";
660
+ var BARODOC_DIR = ".barodoc";
661
+ async function eject(dir, options) {
662
+ const root = path6.resolve(process.cwd(), dir);
663
+ console.log();
664
+ console.log(pc6.bold(pc6.cyan(" barodoc eject")));
665
+ console.log();
666
+ if (isCustomProject(root)) {
667
+ console.log(pc6.yellow("\u26A0 This is already a full Astro project."));
668
+ console.log(pc6.dim(" Nothing to eject."));
669
+ console.log();
670
+ return;
671
+ }
672
+ console.log(
673
+ pc6.dim("Ejecting to a full Astro project in the current directory...")
674
+ );
675
+ console.log();
676
+ console.log(
677
+ pc6.yellow(
678
+ "This will create astro.config.mjs, tsconfig.json, and package.json"
679
+ )
680
+ );
681
+ console.log(
682
+ pc6.yellow("in the current directory. You can then run astro commands directly.")
683
+ );
684
+ console.log();
685
+ const conflictFiles = ["astro.config.mjs", "tsconfig.json"];
686
+ const conflicts = [];
687
+ for (const file of conflictFiles) {
688
+ if (await fs5.pathExists(path6.join(root, file))) {
689
+ conflicts.push(file);
690
+ }
691
+ }
692
+ if (conflicts.length > 0) {
693
+ console.log(pc6.red("Error: The following files already exist and would be overwritten:"));
694
+ for (const f of conflicts) {
695
+ console.log(` ${pc6.dim(f)}`);
696
+ }
697
+ console.log();
698
+ console.log(pc6.dim("Remove them first, then run barodoc eject again."));
699
+ console.log();
700
+ process.exit(1);
701
+ }
702
+ const docsDir = findDocsDir(root);
703
+ const { config } = await loadProjectConfig(root, options.config);
704
+ const projectDir = await createProject({
705
+ root,
706
+ docsDir,
707
+ config,
708
+ configPath: options.config
709
+ });
710
+ console.log(pc6.green("\u2713 Generated project files"));
711
+ await installDependencies(projectDir);
712
+ const astroConfigSrc = path6.join(projectDir, "astro.config.mjs");
713
+ let astroConfigContent = await fs5.readFile(astroConfigSrc, "utf-8");
714
+ await fs5.writeFile(path6.join(root, "astro.config.mjs"), astroConfigContent);
715
+ console.log(pc6.green("\u2713 Created astro.config.mjs"));
716
+ await fs5.copy(
717
+ path6.join(projectDir, "tsconfig.json"),
718
+ path6.join(root, "tsconfig.json")
719
+ );
720
+ console.log(pc6.green("\u2713 Created tsconfig.json"));
721
+ const tempPkg = await fs5.readJSON(path6.join(projectDir, "package.json"));
722
+ const rootPkgPath = path6.join(root, "package.json");
723
+ if (await fs5.pathExists(rootPkgPath)) {
724
+ const existingPkg = await fs5.readJSON(rootPkgPath);
725
+ const merged = {
726
+ ...existingPkg,
727
+ type: "module",
728
+ dependencies: {
729
+ ...existingPkg.dependencies ?? {},
730
+ ...tempPkg.dependencies
731
+ }
732
+ };
733
+ await fs5.writeJSON(rootPkgPath, merged, { spaces: 2 });
734
+ console.log(pc6.green("\u2713 Updated package.json (merged dependencies)"));
735
+ } else {
736
+ const newPkg = {
737
+ name: path6.basename(root),
738
+ type: "module",
739
+ private: true,
740
+ scripts: {
741
+ dev: "astro dev",
742
+ build: "astro build",
743
+ preview: "astro preview"
744
+ },
745
+ dependencies: tempPkg.dependencies
746
+ };
747
+ await fs5.writeJSON(rootPkgPath, newPkg, { spaces: 2 });
748
+ console.log(pc6.green("\u2713 Created package.json"));
749
+ }
750
+ const contentConfigSrc = path6.join(projectDir, "src", "content", "config.ts");
751
+ const contentConfigDst = path6.join(root, "src", "content", "config.ts");
752
+ if (!await fs5.pathExists(contentConfigDst)) {
753
+ await fs5.ensureDir(path6.dirname(contentConfigDst));
754
+ await fs5.copy(contentConfigSrc, contentConfigDst);
755
+ console.log(pc6.green("\u2713 Created src/content/config.ts"));
756
+ }
757
+ const srcNodeModules = path6.join(projectDir, "node_modules");
758
+ const dstNodeModules = path6.join(root, "node_modules");
759
+ if (await fs5.pathExists(srcNodeModules)) {
760
+ if (!await fs5.pathExists(dstNodeModules)) {
761
+ console.log(pc6.dim("Moving node_modules to project root..."));
762
+ await fs5.move(srcNodeModules, dstNodeModules);
763
+ console.log(pc6.green("\u2713 Moved node_modules"));
764
+ } else {
765
+ console.log(pc6.dim("node_modules already exists in root, skipping move"));
766
+ }
767
+ }
768
+ await fs5.remove(path6.join(root, BARODOC_DIR));
769
+ console.log(pc6.green("\u2713 Removed .barodoc/ directory"));
770
+ const gitignorePath = path6.join(root, ".gitignore");
771
+ if (await fs5.pathExists(gitignorePath)) {
772
+ const content = await fs5.readFile(gitignorePath, "utf-8");
773
+ const updated = content.split("\n").filter((line) => line.trim() !== ".barodoc/").join("\n");
774
+ await fs5.writeFile(gitignorePath, updated);
775
+ console.log(pc6.green("\u2713 Updated .gitignore (removed .barodoc/ entry)"));
776
+ }
777
+ console.log();
778
+ console.log(pc6.bold(pc6.green("Eject complete!")));
779
+ console.log();
780
+ console.log("You now have a full Astro project. You can run:");
781
+ console.log();
782
+ console.log(` ${pc6.cyan("npx astro dev")} ${pc6.dim("# development server")}`);
783
+ console.log(` ${pc6.cyan("npx astro build")} ${pc6.dim("# production build")}`);
784
+ console.log(` ${pc6.cyan("npx astro preview")} ${pc6.dim("# preview build")}`);
785
+ console.log();
786
+ console.log(pc6.dim("You can also install astro globally or add it as a dev dependency."));
787
+ console.log();
788
+ }
789
+
790
+ // src/commands/check.ts
791
+ import path7 from "path";
792
+ import pc7 from "picocolors";
793
+ import fs6 from "fs-extra";
794
+ async function scanDocsFiles(docsDir) {
795
+ const result = /* @__PURE__ */ new Map();
796
+ if (!await fs6.pathExists(docsDir)) {
797
+ return result;
798
+ }
799
+ const locales = await fs6.readdir(docsDir);
800
+ for (const locale of locales) {
801
+ const localeDir = path7.join(docsDir, locale);
802
+ const stat = await fs6.stat(localeDir);
803
+ if (!stat.isDirectory()) continue;
804
+ const slugs = [];
805
+ const files = await fs6.readdir(localeDir);
806
+ for (const file of files) {
807
+ if (file.endsWith(".md") || file.endsWith(".mdx")) {
808
+ slugs.push(file.replace(/\.(mdx?)$/, ""));
809
+ }
810
+ }
811
+ result.set(locale, slugs);
812
+ }
813
+ return result;
814
+ }
815
+ function getNavSlugs(config) {
816
+ const locales = config.i18n?.locales ?? ["en"];
817
+ const result = /* @__PURE__ */ new Map();
818
+ for (const locale of locales) {
819
+ result.set(locale, /* @__PURE__ */ new Set());
820
+ }
821
+ for (const group of config.navigation ?? []) {
822
+ for (const page of group.pages ?? []) {
823
+ for (const locale of locales) {
824
+ result.get(locale).add(page);
825
+ }
826
+ }
827
+ }
828
+ return result;
829
+ }
830
+ async function checkFrontmatter(filePath) {
831
+ const content = await fs6.readFile(filePath, "utf-8");
832
+ const missing = [];
833
+ if (!content.startsWith("---")) {
834
+ return missing;
835
+ }
836
+ const end = content.indexOf("---", 3);
837
+ if (end === -1) return missing;
838
+ const frontmatter = content.slice(3, end);
839
+ if (!frontmatter.includes("description:")) {
840
+ missing.push("description");
841
+ }
842
+ return missing;
843
+ }
844
+ async function check(dir, options) {
845
+ const root = path7.resolve(process.cwd(), dir);
846
+ const { config } = await loadProjectConfig(root, options.config);
847
+ console.log();
848
+ console.log(pc7.bold(pc7.cyan(" barodoc check")));
849
+ console.log();
850
+ const docsDir = path7.join(root, "docs");
851
+ const locales = config.i18n?.locales ?? ["en"];
852
+ const defaultLocale = config.i18n?.defaultLocale ?? "en";
853
+ const fileMap = await scanDocsFiles(docsDir);
854
+ const navSlugs = getNavSlugs(config);
855
+ const result = {
856
+ missingFiles: [],
857
+ orphanFiles: [],
858
+ missingFrontmatter: []
859
+ };
860
+ for (const locale of locales) {
861
+ const slugsInNav = navSlugs.get(locale) ?? /* @__PURE__ */ new Set();
862
+ const filesOnDisk = new Set(fileMap.get(locale) ?? []);
863
+ for (const slug of slugsInNav) {
864
+ if (!filesOnDisk.has(slug)) {
865
+ let groupIndex = 0;
866
+ let pageIndex = 0;
867
+ const nav = config.navigation ?? [];
868
+ for (let gi = 0; gi < nav.length; gi++) {
869
+ const pages = nav[gi].pages ?? [];
870
+ const pi = pages.indexOf(slug);
871
+ if (pi !== -1) {
872
+ groupIndex = gi;
873
+ pageIndex = pi;
874
+ break;
875
+ }
876
+ }
877
+ result.missingFiles.push({ slug, locale, groupIndex, pageIndex });
878
+ }
879
+ }
880
+ }
881
+ for (const locale of locales) {
882
+ const slugsInNav = navSlugs.get(locale) ?? /* @__PURE__ */ new Set();
883
+ const filesOnDisk = fileMap.get(locale) ?? [];
884
+ for (const slug of filesOnDisk) {
885
+ if (!slugsInNav.has(slug)) {
886
+ const ext = await fs6.pathExists(path7.join(docsDir, locale, `${slug}.mdx`)) ? ".mdx" : ".md";
887
+ result.orphanFiles.push({
888
+ slug,
889
+ locale,
890
+ filePath: path7.join(docsDir, locale, `${slug}${ext}`)
891
+ });
892
+ }
893
+ }
894
+ }
895
+ const defaultFiles = fileMap.get(defaultLocale) ?? [];
896
+ for (const slug of defaultFiles) {
897
+ const ext = await fs6.pathExists(path7.join(docsDir, defaultLocale, `${slug}.mdx`)) ? ".mdx" : ".md";
898
+ const filePath = path7.join(docsDir, defaultLocale, `${slug}${ext}`);
899
+ const missing = await checkFrontmatter(filePath);
900
+ if (missing.length > 0) {
901
+ result.missingFrontmatter.push({
902
+ filePath: path7.relative(root, filePath),
903
+ fields: missing
904
+ });
905
+ }
906
+ }
907
+ let hasIssues = false;
908
+ if (result.missingFiles.length > 0) {
909
+ hasIssues = true;
910
+ console.log(pc7.bold(pc7.red(` Missing files (${result.missingFiles.length})`)));
911
+ console.log(pc7.dim(" Navigation references files that don't exist on disk."));
912
+ console.log();
913
+ for (const item of result.missingFiles) {
914
+ const ext = ".md";
915
+ const relPath = `docs/${item.locale}/${item.slug}${ext}`;
916
+ console.log(` ${pc7.red("\u2717")} ${pc7.bold(item.slug)} ${pc7.dim(`\u2192 ${relPath}`)}`);
917
+ }
918
+ console.log();
919
+ }
920
+ if (result.orphanFiles.length > 0) {
921
+ hasIssues = true;
922
+ console.log(pc7.bold(pc7.yellow(` Orphan files (${result.orphanFiles.length})`)));
923
+ console.log(pc7.dim(" Files exist on disk but are not referenced in navigation."));
924
+ console.log();
925
+ for (const item of result.orphanFiles) {
926
+ const relPath = `docs/${item.locale}/${item.slug}`;
927
+ console.log(
928
+ ` ${pc7.yellow("\u26A0")} ${pc7.bold(item.slug)} ${pc7.dim(`\u2192 ${relPath}`)}`
929
+ );
930
+ }
931
+ console.log();
932
+ }
933
+ if (result.missingFrontmatter.length > 0) {
934
+ console.log(pc7.bold(pc7.dim(` Missing frontmatter fields (${result.missingFrontmatter.length})`)));
935
+ console.log();
936
+ for (const item of result.missingFrontmatter) {
937
+ console.log(
938
+ ` ${pc7.dim("\u25CB")} ${item.filePath} ${pc7.dim(`\u2014 missing: ${item.fields.join(", ")}`)}`
939
+ );
940
+ }
941
+ console.log();
942
+ }
943
+ if (options.fix && (result.missingFiles.length > 0 || result.orphanFiles.length > 0)) {
944
+ console.log(pc7.bold(pc7.cyan(" Fixing issues...")));
945
+ console.log();
946
+ if (result.orphanFiles.length > 0) {
947
+ const configPath = options.config ? path7.resolve(root, options.config) : path7.join(root, "barodoc.config.json");
948
+ if (await fs6.pathExists(configPath)) {
949
+ const configJson = await fs6.readJSON(configPath);
950
+ const nav = configJson.navigation ?? [];
951
+ const defaultLocaleOrphans = result.orphanFiles.filter(
952
+ (o) => o.locale === defaultLocale
953
+ );
954
+ if (defaultLocaleOrphans.length > 0) {
955
+ if (nav.length === 0) {
956
+ nav.push({ group: "Documentation", pages: [] });
957
+ }
958
+ for (const orphan of defaultLocaleOrphans) {
959
+ const lastGroup = nav[nav.length - 1];
960
+ if (!lastGroup.pages.includes(orphan.slug)) {
961
+ lastGroup.pages.push(orphan.slug);
962
+ console.log(
963
+ ` ${pc7.green("\u2713")} Added ${pc7.bold(orphan.slug)} to navigation group "${lastGroup.group}"`
964
+ );
965
+ }
966
+ }
967
+ await fs6.writeJSON(configPath, configJson, { spaces: 2 });
968
+ console.log(pc7.green(" \u2713 Updated barodoc.config.json"));
969
+ }
970
+ } else {
971
+ console.log(pc7.yellow(" \u26A0 Could not find barodoc.config.json to update navigation."));
972
+ }
973
+ }
974
+ if (result.missingFiles.length > 0) {
975
+ for (const item of result.missingFiles) {
976
+ const fileDir = path7.join(docsDir, item.locale);
977
+ await fs6.ensureDir(fileDir);
978
+ const filePath = path7.join(fileDir, `${item.slug}.md`);
979
+ const title = item.slug.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
980
+ await fs6.writeFile(
981
+ filePath,
982
+ `---
983
+ description: ${title}
984
+ ---
985
+
986
+ # ${title}
987
+
988
+ Add your content here.
989
+ `
990
+ );
991
+ console.log(` ${pc7.green("\u2713")} Created docs/${item.locale}/${item.slug}.md`);
992
+ }
993
+ }
994
+ console.log();
995
+ console.log(pc7.bold(" Done! Run barodoc check again to verify."));
996
+ console.log();
997
+ return;
998
+ }
999
+ if (!hasIssues) {
1000
+ console.log(pc7.green(" \u2713 All good! No issues found."));
1001
+ console.log();
1002
+ } else {
1003
+ console.log(
1004
+ pc7.dim(` Run ${pc7.cyan("barodoc check --fix")} to automatically fix missing/orphan files.`)
1005
+ );
1006
+ console.log();
1007
+ process.exit(1);
1008
+ }
1009
+ }
1010
+
282
1011
  // src/cli.ts
283
1012
  var cli = cac("barodoc");
284
- cli.command("serve [dir]", "Start development server").option("-p, --port <port>", "Port to listen on", { default: 4321 }).option("-h, --host", "Expose to network").option("-c, --config <file>", "Config file path").action(async (dir = ".", options) => {
1013
+ cli.command("serve [dir]", "Start development server").option("-p, --port <port>", "Port to listen on", { default: 4321 }).option("-h, --host", "Expose to network").option("--open", "Open browser on start").option("--clean", "Force reinstall dependencies").option("-c, --config <file>", "Config file path").action(async (dir = ".", options) => {
285
1014
  await serve(dir, options);
286
1015
  });
287
- cli.command("build [dir]", "Build for production").option("-o, --output <dir>", "Output directory", { default: "dist" }).option("-c, --config <file>", "Config file path").action(async (dir = ".", options) => {
1016
+ cli.command("build [dir]", "Build for production").option("-o, --output <dir>", "Output directory", { default: "dist" }).option("--clean", "Force reinstall dependencies").option("-c, --config <file>", "Config file path").action(async (dir = ".", options) => {
288
1017
  await build(dir, options);
289
1018
  });
290
1019
  cli.command("preview [dir]", "Preview production build").option("-p, --port <port>", "Port to listen on", { default: 4321 }).option("-o, --output <dir>", "Build output directory", { default: "dist" }).action(async (dir = ".", options) => {
@@ -293,13 +1022,22 @@ cli.command("preview [dir]", "Preview production build").option("-p, --port <por
293
1022
  cli.command("create <name>", "Create a new Barodoc project").action(async (name) => {
294
1023
  await create(name);
295
1024
  });
1025
+ cli.command("init [dir]", "Initialize Barodoc in an existing directory").option("-c, --config <file>", "Config file path").action(async (dir = ".", options) => {
1026
+ await init(dir);
1027
+ });
1028
+ cli.command("check [dir]", "Validate docs against navigation config").option("--fix", "Auto-fix missing files and orphan navigation entries").option("-c, --config <file>", "Config file path").action(async (dir = ".", options) => {
1029
+ await check(dir, options);
1030
+ });
1031
+ cli.command("eject [dir]", "Eject to a full Astro project").option("-c, --config <file>", "Config file path").action(async (dir = ".", options) => {
1032
+ await eject(dir, options);
1033
+ });
296
1034
  cli.help();
297
1035
  cli.version(version);
298
1036
  cli.parse();
299
1037
  if (!process.argv.slice(2).length) {
300
1038
  console.log();
301
- console.log(pc5.bold(pc5.cyan(" barodoc")));
302
- console.log(pc5.dim(" Documentation framework powered by Astro"));
1039
+ console.log(pc8.bold(pc8.cyan(" barodoc")));
1040
+ console.log(pc8.dim(" Documentation framework powered by Astro"));
303
1041
  console.log();
304
1042
  cli.outputHelp();
305
1043
  }
package/dist/index.d.ts CHANGED
@@ -12,7 +12,7 @@ interface ProjectOptions {
12
12
  */
13
13
  declare function createProject(options: ProjectOptions): Promise<string>;
14
14
  /**
15
- * Clean up temporary project
15
+ * Clean up temporary project files but preserve node_modules cache
16
16
  */
17
17
  declare function cleanupProject(root: string): Promise<void>;
18
18
 
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  cleanupProject,
3
3
  createProject
4
- } from "./chunk-KI3NAGJ3.js";
4
+ } from "./chunk-JIP64MDC.js";
5
5
 
6
6
  // src/index.ts
7
7
  export * from "@barodoc/core";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "barodoc",
3
- "version": "1.0.1",
3
+ "version": "2.0.0",
4
4
  "description": "Documentation framework powered by Astro",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,8 +24,8 @@
24
24
  "fs-extra": "^11.2.0",
25
25
  "gray-matter": "^4.0.3",
26
26
  "picocolors": "^1.1.1",
27
- "@barodoc/core": "1.0.1",
28
- "@barodoc/theme-docs": "1.0.1"
27
+ "@barodoc/core": "2.0.0",
28
+ "@barodoc/theme-docs": "2.0.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/fs-extra": "^11.0.4",