@raystack/chronicle 0.1.0-canary.5a2be79 → 0.1.0-canary.6511afe

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 (73) hide show
  1. package/dist/cli/index.js +224 -9916
  2. package/package.json +20 -12
  3. package/src/cli/commands/build.ts +27 -25
  4. package/src/cli/commands/dev.ts +24 -25
  5. package/src/cli/commands/init.ts +39 -125
  6. package/src/cli/commands/serve.ts +36 -49
  7. package/src/cli/commands/start.ts +20 -25
  8. package/src/cli/index.ts +14 -14
  9. package/src/cli/utils/config.ts +25 -26
  10. package/src/cli/utils/index.ts +3 -3
  11. package/src/cli/utils/resolve.ts +9 -3
  12. package/src/cli/utils/scaffold.ts +11 -124
  13. package/src/components/mdx/image.tsx +5 -34
  14. package/src/components/mdx/link.tsx +18 -15
  15. package/src/components/ui/search.module.css +7 -0
  16. package/src/components/ui/search.tsx +65 -49
  17. package/src/lib/config.ts +31 -28
  18. package/src/lib/head.tsx +49 -0
  19. package/src/lib/openapi.ts +8 -8
  20. package/src/lib/page-context.tsx +114 -0
  21. package/src/lib/source.ts +164 -45
  22. package/src/pages/ApiLayout.tsx +33 -0
  23. package/src/pages/ApiPage.tsx +73 -0
  24. package/src/pages/DocsLayout.tsx +18 -0
  25. package/src/pages/DocsPage.tsx +43 -0
  26. package/src/pages/NotFound.tsx +17 -0
  27. package/src/server/App.tsx +67 -0
  28. package/src/server/api/apis-proxy.ts +69 -0
  29. package/src/server/api/health.ts +5 -0
  30. package/src/server/api/page/[...slug].ts +18 -0
  31. package/src/server/api/search.ts +170 -0
  32. package/src/server/api/specs.ts +9 -0
  33. package/src/server/build-search-index.ts +117 -0
  34. package/src/server/entry-client.tsx +70 -0
  35. package/src/server/entry-server.tsx +95 -0
  36. package/src/server/routes/llms.txt.ts +61 -0
  37. package/src/server/routes/og.tsx +75 -0
  38. package/src/server/routes/robots.txt.ts +11 -0
  39. package/src/server/routes/sitemap.xml.ts +39 -0
  40. package/src/server/utils/safe-path.ts +17 -0
  41. package/src/server/vite-config.ts +83 -0
  42. package/src/themes/default/Layout.tsx +69 -42
  43. package/src/themes/default/Page.tsx +9 -11
  44. package/src/themes/default/Toc.tsx +30 -28
  45. package/src/themes/default/index.ts +7 -9
  46. package/src/themes/paper/ChapterNav.tsx +60 -41
  47. package/src/themes/paper/Layout.module.css +1 -1
  48. package/src/themes/paper/Layout.tsx +24 -12
  49. package/src/themes/paper/Page.module.css +11 -4
  50. package/src/themes/paper/Page.tsx +67 -48
  51. package/src/themes/paper/ReadingProgress.tsx +160 -139
  52. package/src/themes/paper/index.ts +5 -5
  53. package/src/themes/registry.ts +7 -7
  54. package/src/types/config.ts +11 -0
  55. package/src/types/content.ts +1 -0
  56. package/src/types/globals.d.ts +4 -0
  57. package/tsconfig.json +2 -3
  58. package/next.config.mjs +0 -10
  59. package/source.config.ts +0 -50
  60. package/src/app/[[...slug]]/layout.tsx +0 -15
  61. package/src/app/[[...slug]]/page.tsx +0 -57
  62. package/src/app/api/apis-proxy/route.ts +0 -59
  63. package/src/app/api/health/route.ts +0 -3
  64. package/src/app/api/search/route.ts +0 -90
  65. package/src/app/apis/[[...slug]]/layout.tsx +0 -26
  66. package/src/app/apis/[[...slug]]/page.tsx +0 -57
  67. package/src/app/layout.tsx +0 -26
  68. package/src/app/llms-full.txt/route.ts +0 -18
  69. package/src/app/llms.txt/route.ts +0 -15
  70. package/src/app/providers.tsx +0 -8
  71. package/src/cli/utils/process.ts +0 -7
  72. package/src/themes/default/font.ts +0 -6
  73. /package/src/{app/apis/[[...slug]]/layout.module.css → pages/ApiLayout.module.css} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raystack/chronicle",
3
- "version": "0.1.0-canary.5a2be79",
3
+ "version": "0.1.0-canary.6511afe",
4
4
  "description": "Config-driven documentation framework",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -9,15 +9,14 @@
9
9
  "dist",
10
10
  "src",
11
11
  "templates",
12
- "next.config.mjs",
13
- "source.config.ts",
14
12
  "tsconfig.json"
15
13
  ],
16
14
  "bin": {
17
15
  "chronicle": "./bin/chronicle.js"
18
16
  },
19
17
  "scripts": {
20
- "build:cli": "bun build-cli.ts"
18
+ "build:cli": "bun build-cli.ts",
19
+ "lint": "biome lint src/"
21
20
  },
22
21
  "devDependencies": {
23
22
  "@biomejs/biome": "^2.3.13",
@@ -37,27 +36,36 @@
37
36
  "@codemirror/theme-one-dark": "^6.1.3",
38
37
  "@codemirror/view": "^6.39.14",
39
38
  "@heroicons/react": "^2.2.0",
40
- "@raystack/apsara": "^0.56.0",
41
- "@types/unist": "^3.0.3",
39
+ "@raystack/apsara": "0.55.1",
40
+ "@shikijs/rehype": "^4.0.2",
41
+ "@vitejs/plugin-react": "^6.0.1",
42
42
  "chalk": "^5.6.2",
43
43
  "class-variance-authority": "^0.7.1",
44
44
  "codemirror": "^6.0.2",
45
45
  "commander": "^14.0.2",
46
46
  "fumadocs-core": "16.6.15",
47
47
  "fumadocs-mdx": "^14.2.6",
48
+ "glob": "^11.0.0",
49
+ "gray-matter": "^4.0.3",
50
+ "h3": "^2.0.1-rc.16",
48
51
  "lodash": "^4.17.23",
49
52
  "mermaid": "^11.13.0",
50
- "next": "16.1.6",
53
+ "minisearch": "^7.2.0",
54
+ "nitro": "latest",
55
+ "openapi-types": "^12.1.3",
51
56
  "react": "^19.0.0",
52
- "react-device-detect": "^2.2.3",
53
57
  "react-dom": "^19.0.0",
54
- "remark-attr": "^0.11.1",
58
+ "react-router": "^7.13.1",
55
59
  "remark-directive": "^4.0.0",
60
+ "remark-parse": "^11.0.0",
61
+ "remark-frontmatter": "^5.0.0",
62
+ "remark-gfm": "^4.0.1",
63
+ "remark-mdx-frontmatter": "^5.2.0",
64
+ "satori": "^0.25.0",
56
65
  "slugify": "^1.6.6",
57
66
  "unified": "^11.0.5",
58
67
  "unist-util-visit": "^5.1.0",
59
- "openapi-types": "^12.1.3",
60
- "yaml": "^2.8.2",
61
- "zod": "^4.3.6"
68
+ "vite": "^8.0.0",
69
+ "yaml": "^2.8.2"
62
70
  }
63
71
  }
@@ -1,32 +1,34 @@
1
- import { Command } from 'commander'
2
- import { spawn } from 'child_process'
3
- import path from 'path'
4
- import fs from 'fs'
5
- import chalk from 'chalk'
6
- import { attachLifecycleHandlers, resolveNextCli } from '@/cli/utils'
1
+ import chalk from 'chalk';
2
+ import { Command } from 'commander';
3
+ import { resolveContentDir } from '@/cli/utils/config';
4
+ import { PACKAGE_ROOT } from '@/cli/utils/resolve';
5
+ import { linkContent } from '@/cli/utils/scaffold';
7
6
 
8
7
  export const buildCommand = new Command('build')
9
8
  .description('Build for production')
10
- .action(() => {
11
- const scaffoldPath = path.join(process.cwd(), '.chronicle')
12
- if (!fs.existsSync(scaffoldPath)) {
13
- console.log(chalk.red('Error: .chronicle/ not found. Run'), chalk.cyan('chronicle init'), chalk.red('first.'))
14
- process.exit(1)
15
- }
9
+ .option('-c, --content <path>', 'Content directory')
10
+ .option(
11
+ '--preset <preset>',
12
+ 'Deploy preset (vercel, cloudflare, node-server)'
13
+ )
14
+ .action(async options => {
15
+ const contentDir = resolveContentDir(options.content);
16
+ await linkContent(contentDir);
16
17
 
17
- const nextCli = resolveNextCli()
18
+ console.log(chalk.cyan('Building for production...'));
18
19
 
19
- console.log(chalk.cyan('Building for production...'))
20
+ const { createBuilder } = await import('vite');
21
+ const { createViteConfig } = await import('@/server/vite-config');
20
22
 
21
- const child = spawn(process.execPath, [nextCli, 'build'], {
22
- stdio: 'inherit',
23
- cwd: scaffoldPath,
24
- env: {
25
- ...process.env,
26
- CHRONICLE_PROJECT_ROOT: process.cwd(),
27
- CHRONICLE_CONTENT_DIR: './content',
28
- },
29
- })
23
+ const config = await createViteConfig({
24
+ packageRoot: PACKAGE_ROOT,
25
+ projectRoot: process.cwd(),
26
+ contentDir,
27
+ preset: options.preset
28
+ });
30
29
 
31
- attachLifecycleHandlers(child)
32
- })
30
+ const builder = await createBuilder({ ...config, builder: {} });
31
+ await builder.buildApp();
32
+
33
+ console.log(chalk.green('Build complete'));
34
+ });
@@ -1,33 +1,32 @@
1
- import { Command } from 'commander'
2
- import { spawn } from 'child_process'
3
- import path from 'path'
4
- import fs from 'fs'
5
- import chalk from 'chalk'
6
- import { attachLifecycleHandlers, resolveNextCli } from '@/cli/utils'
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import chalk from 'chalk';
4
+ import { Command } from 'commander';
5
+ import { resolveContentDir } from '@/cli/utils/config';
6
+ import { PACKAGE_ROOT } from '@/cli/utils/resolve';
7
+ import { linkContent } from '@/cli/utils/scaffold';
7
8
 
8
9
  export const devCommand = new Command('dev')
9
10
  .description('Start development server')
10
11
  .option('-p, --port <port>', 'Port number', '3000')
11
- .action((options) => {
12
- const scaffoldPath = path.join(process.cwd(), '.chronicle')
13
- if (!fs.existsSync(scaffoldPath)) {
14
- console.log(chalk.red('Error: .chronicle/ not found. Run'), chalk.cyan('chronicle init'), chalk.red('first.'))
15
- process.exit(1)
16
- }
12
+ .option('-c, --content <path>', 'Content directory')
13
+ .action(async options => {
14
+ const contentDir = resolveContentDir(options.content);
15
+ const port = parseInt(options.port, 10);
17
16
 
18
- const nextCli = resolveNextCli()
17
+ await linkContent(contentDir);
19
18
 
20
- console.log(chalk.cyan('Starting dev server...'))
19
+ console.log(chalk.cyan('Starting dev server...'));
21
20
 
22
- const child = spawn(process.execPath, [nextCli, 'dev', '-p', options.port], {
23
- stdio: 'inherit',
24
- cwd: scaffoldPath,
25
- env: {
26
- ...process.env,
27
- CHRONICLE_PROJECT_ROOT: process.cwd(),
28
- CHRONICLE_CONTENT_DIR: './content',
29
- },
30
- })
21
+ const { createServer } = await import('vite');
22
+ const { createViteConfig } = await import('@/server/vite-config');
31
23
 
32
- attachLifecycleHandlers(child)
33
- })
24
+ const config = await createViteConfig({ packageRoot: PACKAGE_ROOT, projectRoot: process.cwd(), contentDir });
25
+ const server = await createServer({
26
+ ...config,
27
+ server: { ...config.server, port }
28
+ });
29
+
30
+ await server.listen();
31
+ server.printUrls();
32
+ });
@@ -1,43 +1,16 @@
1
- import { Command } from 'commander'
2
- import { execSync } from 'child_process'
3
- import fs from 'fs'
4
- import path from 'path'
5
- import chalk from 'chalk'
6
- import { stringify } from 'yaml'
7
- import type { ChronicleConfig } from '@/types'
8
- import { loadCLIConfig, scaffoldDir, detectPackageManager } from '@/cli/utils'
9
-
10
-
11
- function createConfig(): ChronicleConfig {
12
- return {
13
- title: 'My Documentation',
14
- description: 'Documentation powered by Chronicle',
15
- theme: { name: 'default' },
16
- search: { enabled: true, placeholder: 'Search documentation...' },
17
- }
18
- }
19
-
20
- function createPackageJson(name: string): Record<string, unknown> {
21
- return {
22
- name,
23
- private: true,
24
- scripts: {
25
- dev: 'chronicle dev',
26
- build: 'chronicle build',
27
- start: 'chronicle start',
28
- },
29
- dependencies: {
30
- '@raystack/chronicle': 'latest',
31
- },
32
- devDependencies: {
33
- '@raystack/tools-config': '0.56.0',
34
- 'openapi-types': '^12.1.3',
35
- typescript: '5.9.3',
36
- '@types/react': '^19.2.10',
37
- '@types/node': '^25.1.0',
38
- },
39
- }
40
- }
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import chalk from 'chalk';
4
+ import { Command } from 'commander';
5
+ import { stringify } from 'yaml';
6
+ import type { ChronicleConfig } from '@/types';
7
+
8
+ const defaultConfig: ChronicleConfig = {
9
+ title: 'My Documentation',
10
+ description: 'Documentation powered by Chronicle',
11
+ theme: { name: 'default' },
12
+ search: { enabled: true, placeholder: 'Search documentation...' }
13
+ };
41
14
 
42
15
  const sampleMdx = `---
43
16
  title: Welcome
@@ -48,108 +21,49 @@ order: 1
48
21
  # Welcome
49
22
 
50
23
  This is your documentation home page.
51
- `
24
+ `;
52
25
 
53
26
  export const initCommand = new Command('init')
54
27
  .description('Initialize a new Chronicle project')
55
28
  .option('-c, --content <path>', 'Content directory name', 'content')
56
- .action((options) => {
57
- const projectDir = process.cwd()
58
- const dirName = path.basename(projectDir) || 'docs'
59
- const contentDir = path.join(projectDir, options.content)
29
+ .action(options => {
30
+ const projectDir = process.cwd();
31
+ const contentDir = path.join(projectDir, options.content);
60
32
 
61
- // Create content directory if it doesn't exist
62
33
  if (!fs.existsSync(contentDir)) {
63
- fs.mkdirSync(contentDir, { recursive: true })
64
- console.log(chalk.green(''), 'Created', contentDir)
65
- }
66
-
67
- // Create or update package.json in project root
68
- const packageJsonPath = path.join(projectDir, 'package.json')
69
- if (!fs.existsSync(packageJsonPath)) {
70
- fs.writeFileSync(packageJsonPath, JSON.stringify(createPackageJson(dirName), null, 2) + '\n')
71
- console.log(chalk.green('✓'), 'Created', packageJsonPath)
72
- } else {
73
- const existing = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))
74
- const template = createPackageJson(dirName)
75
- let updated = false
76
-
77
- // Merge missing scripts
78
- if (!existing.scripts) existing.scripts = {}
79
- for (const [key, value] of Object.entries(template.scripts as Record<string, string>)) {
80
- if (!existing.scripts[key]) {
81
- existing.scripts[key] = value
82
- updated = true
83
- }
84
- }
85
-
86
- // Merge missing dependencies
87
- if (!existing.dependencies) existing.dependencies = {}
88
- for (const [key, value] of Object.entries(template.dependencies as Record<string, string>)) {
89
- if (!existing.dependencies[key]) {
90
- existing.dependencies[key] = value
91
- updated = true
92
- }
93
- }
94
-
95
- // Merge missing devDependencies
96
- if (!existing.devDependencies) existing.devDependencies = {}
97
- for (const [key, value] of Object.entries(template.devDependencies as Record<string, string>)) {
98
- if (!existing.devDependencies[key]) {
99
- existing.devDependencies[key] = value
100
- updated = true
101
- }
102
- }
103
-
104
- if (updated) {
105
- fs.writeFileSync(packageJsonPath, JSON.stringify(existing, null, 2) + '\n')
106
- console.log(chalk.green('✓'), 'Updated', packageJsonPath, 'with missing scripts/deps')
107
- } else {
108
- console.log(chalk.yellow('⚠'), packageJsonPath, 'already has all required entries')
109
- }
34
+ fs.mkdirSync(contentDir, { recursive: true });
35
+ console.log(chalk.green('\u2713'), 'Created', contentDir);
110
36
  }
111
37
 
112
- // Create chronicle.yaml in project root
113
- const configPath = path.join(projectDir, 'chronicle.yaml')
38
+ const configPath = path.join(projectDir, 'chronicle.yaml');
114
39
  if (!fs.existsSync(configPath)) {
115
- fs.writeFileSync(configPath, stringify(createConfig()))
116
- console.log(chalk.green(''), 'Created', configPath)
40
+ fs.writeFileSync(configPath, stringify(defaultConfig));
41
+ console.log(chalk.green('\u2713'), 'Created', configPath);
117
42
  } else {
118
- console.log(chalk.yellow(''), configPath, 'already exists')
43
+ console.log(chalk.yellow('\u26a0'), configPath, 'already exists');
119
44
  }
120
45
 
121
- // Create sample index.mdx only if content dir is empty
122
- const contentFiles = fs.readdirSync(contentDir)
46
+ const contentFiles = fs.readdirSync(contentDir);
123
47
  if (contentFiles.length === 0) {
124
- const indexPath = path.join(contentDir, 'index.mdx')
125
- fs.writeFileSync(indexPath, sampleMdx)
126
- console.log(chalk.green(''), 'Created', indexPath)
48
+ const indexPath = path.join(contentDir, 'index.mdx');
49
+ fs.writeFileSync(indexPath, sampleMdx);
50
+ console.log(chalk.green('\u2713'), 'Created', indexPath);
127
51
  }
128
52
 
129
- // Add .chronicle to .gitignore
130
- const gitignorePath = path.join(projectDir, '.gitignore')
131
- const chronicleEntry = '.chronicle'
53
+ const gitignorePath = path.join(projectDir, '.gitignore');
54
+ const gitignoreEntries = ['node_modules', 'dist', '.output'];
132
55
  if (fs.existsSync(gitignorePath)) {
133
- const existing = fs.readFileSync(gitignorePath, 'utf-8')
134
- if (!existing.includes(chronicleEntry)) {
135
- fs.appendFileSync(gitignorePath, `\n${chronicleEntry}\n`)
136
- console.log(chalk.green(''), 'Added .chronicle to .gitignore')
56
+ const existing = fs.readFileSync(gitignorePath, 'utf-8');
57
+ const missing = gitignoreEntries.filter(e => !existing.includes(e));
58
+ if (missing.length > 0) {
59
+ fs.appendFileSync(gitignorePath, `\n${missing.join('\n')}\n`);
60
+ console.log(chalk.green('\u2713'), 'Added', missing.join(', '), 'to .gitignore');
137
61
  }
138
62
  } else {
139
- fs.writeFileSync(gitignorePath, `${chronicleEntry}\n`)
140
- console.log(chalk.green(''), 'Created .gitignore with .chronicle')
63
+ fs.writeFileSync(gitignorePath, `${gitignoreEntries.join('\n')}\n`);
64
+ console.log(chalk.green('\u2713'), 'Created .gitignore');
141
65
  }
142
66
 
143
- // Install dependencies
144
- const pm = detectPackageManager()
145
- console.log(chalk.cyan(`\nInstalling dependencies with ${pm}...`))
146
- execSync(`${pm} install`, { cwd: projectDir, stdio: 'inherit' })
147
-
148
- // Scaffold .chronicle/ directory
149
- loadCLIConfig(contentDir)
150
- scaffoldDir(contentDir)
151
-
152
- const runCmd = pm === 'npm' ? 'npx' : pm === 'bun' ? 'bunx' : `${pm} dlx`
153
- console.log(chalk.green('\n✓ Chronicle initialized!'))
154
- console.log('\nRun', chalk.cyan(`${runCmd} chronicle dev`), 'to start development server')
155
- })
67
+ console.log(chalk.green('\n\u2713 Chronicle initialized!'));
68
+ console.log('\nRun', chalk.cyan('chronicle dev'), 'to start development server');
69
+ });
@@ -1,53 +1,40 @@
1
- import { Command } from 'commander'
2
- import { spawn } from 'child_process'
3
- import path from 'path'
4
- import fs from 'fs'
5
- import chalk from 'chalk'
6
- import { attachLifecycleHandlers, resolveNextCli } from '@/cli/utils'
1
+ import chalk from 'chalk';
2
+ import { Command } from 'commander';
3
+ import { resolveContentDir } from '@/cli/utils/config';
4
+ import { PACKAGE_ROOT } from '@/cli/utils/resolve';
5
+ import { linkContent } from '@/cli/utils/scaffold';
7
6
 
8
7
  export const serveCommand = new Command('serve')
9
8
  .description('Build and start production server')
10
9
  .option('-p, --port <port>', 'Port number', '3000')
11
- .action((options) => {
12
- const scaffoldPath = path.join(process.cwd(), '.chronicle')
13
- if (!fs.existsSync(scaffoldPath)) {
14
- console.log(chalk.red('Error: .chronicle/ not found. Run'), chalk.cyan('chronicle init'), chalk.red('first.'))
15
- process.exit(1)
16
- }
17
-
18
- const nextCli = resolveNextCli()
19
-
20
- const env = {
21
- ...process.env,
22
- CHRONICLE_PROJECT_ROOT: process.cwd(),
23
- CHRONICLE_CONTENT_DIR: './content',
24
- }
25
-
26
- console.log(chalk.cyan('Building for production...'))
27
-
28
- const buildChild = spawn(process.execPath, [nextCli, 'build'], {
29
- stdio: 'inherit',
30
- cwd: scaffoldPath,
31
- env,
32
- })
33
-
34
- process.once('SIGINT', () => buildChild.kill('SIGINT'))
35
- process.once('SIGTERM', () => buildChild.kill('SIGTERM'))
36
-
37
- buildChild.on('close', (code) => {
38
- if (code !== 0) {
39
- console.log(chalk.red('Build failed'))
40
- process.exit(code ?? 1)
41
- }
42
-
43
- console.log(chalk.cyan('Starting production server...'))
44
-
45
- const startChild = spawn(process.execPath, [nextCli, 'start', '-p', options.port], {
46
- stdio: 'inherit',
47
- cwd: scaffoldPath,
48
- env,
49
- })
50
-
51
- attachLifecycleHandlers(startChild)
52
- })
53
- })
10
+ .option('-c, --content <path>', 'Content directory')
11
+ .option(
12
+ '--preset <preset>',
13
+ 'Deploy preset (vercel, cloudflare, node-server)'
14
+ )
15
+ .action(async options => {
16
+ const contentDir = resolveContentDir(options.content);
17
+ const port = parseInt(options.port, 10);
18
+ await linkContent(contentDir);
19
+
20
+ const { build, preview } = await import('vite');
21
+ const { createViteConfig } = await import('@/server/vite-config');
22
+
23
+ const config = await createViteConfig({
24
+ packageRoot: PACKAGE_ROOT,
25
+ projectRoot: process.cwd(),
26
+ contentDir,
27
+ preset: options.preset
28
+ });
29
+
30
+ console.log(chalk.cyan('Building for production...'));
31
+ await build(config);
32
+
33
+ console.log(chalk.cyan('Starting production server...'));
34
+ const server = await preview({
35
+ ...config,
36
+ preview: { port }
37
+ });
38
+
39
+ server.printUrls();
40
+ });
@@ -1,33 +1,28 @@
1
- import { Command } from 'commander'
2
- import { spawn } from 'child_process'
3
- import path from 'path'
4
- import fs from 'fs'
5
- import chalk from 'chalk'
6
- import { attachLifecycleHandlers, resolveNextCli } from '@/cli/utils'
1
+ import chalk from 'chalk';
2
+ import { Command } from 'commander';
3
+ import { resolveContentDir } from '@/cli/utils/config';
4
+ import { PACKAGE_ROOT } from '@/cli/utils/resolve';
5
+ import { linkContent } from '@/cli/utils/scaffold';
7
6
 
8
7
  export const startCommand = new Command('start')
9
8
  .description('Start production server')
10
9
  .option('-p, --port <port>', 'Port number', '3000')
11
- .action((options) => {
12
- const scaffoldPath = path.join(process.cwd(), '.chronicle')
13
- if (!fs.existsSync(scaffoldPath)) {
14
- console.log(chalk.red('Error: .chronicle/ not found. Run'), chalk.cyan('chronicle init'), chalk.red('first.'))
15
- process.exit(1)
16
- }
10
+ .option('-c, --content <path>', 'Content directory')
11
+ .action(async options => {
12
+ const contentDir = resolveContentDir(options.content);
13
+ const port = parseInt(options.port, 10);
14
+ await linkContent(contentDir);
17
15
 
18
- const nextCli = resolveNextCli()
16
+ console.log(chalk.cyan('Starting production server...'));
19
17
 
20
- console.log(chalk.cyan('Starting production server...'))
18
+ const { preview } = await import('vite');
19
+ const { createViteConfig } = await import('@/server/vite-config');
21
20
 
22
- const child = spawn(process.execPath, [nextCli, 'start', '-p', options.port], {
23
- stdio: 'inherit',
24
- cwd: scaffoldPath,
25
- env: {
26
- ...process.env,
27
- CHRONICLE_PROJECT_ROOT: process.cwd(),
28
- CHRONICLE_CONTENT_DIR: './content',
29
- },
30
- })
21
+ const config = await createViteConfig({ packageRoot: PACKAGE_ROOT, projectRoot: process.cwd(), contentDir });
22
+ const server = await preview({
23
+ ...config,
24
+ preview: { port }
25
+ });
31
26
 
32
- attachLifecycleHandlers(child)
33
- })
27
+ server.printUrls();
28
+ });
package/src/cli/index.ts CHANGED
@@ -1,21 +1,21 @@
1
- import { Command } from 'commander'
2
- import { initCommand } from './commands/init'
3
- import { devCommand } from './commands/dev'
4
- import { buildCommand } from './commands/build'
5
- import { startCommand } from './commands/start'
6
- import { serveCommand } from './commands/serve'
1
+ import { Command } from 'commander';
2
+ import { buildCommand } from './commands/build';
3
+ import { devCommand } from './commands/dev';
4
+ import { initCommand } from './commands/init';
5
+ import { serveCommand } from './commands/serve';
6
+ import { startCommand } from './commands/start';
7
7
 
8
- const program = new Command()
8
+ const program = new Command();
9
9
 
10
10
  program
11
11
  .name('chronicle')
12
12
  .description('Config-driven documentation framework')
13
- .version('0.1.0')
13
+ .version('0.1.0');
14
14
 
15
- program.addCommand(initCommand)
16
- program.addCommand(devCommand)
17
- program.addCommand(buildCommand)
18
- program.addCommand(startCommand)
19
- program.addCommand(serveCommand)
15
+ program.addCommand(initCommand);
16
+ program.addCommand(devCommand);
17
+ program.addCommand(buildCommand);
18
+ program.addCommand(startCommand);
19
+ program.addCommand(serveCommand);
20
20
 
21
- program.parse()
21
+ program.parse();
@@ -1,43 +1,42 @@
1
- import fs from 'fs'
2
- import path from 'path'
3
- import { parse } from 'yaml'
4
- import chalk from 'chalk'
5
- import type { ChronicleConfig } from '@/types'
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import chalk from 'chalk';
4
+ import { parse } from 'yaml';
5
+ import type { ChronicleConfig } from '@/types';
6
6
 
7
7
  export interface CLIConfig {
8
- config: ChronicleConfig
9
- configPath: string
10
- contentDir: string
8
+ config: ChronicleConfig;
9
+ configPath: string;
10
+ contentDir: string;
11
11
  }
12
12
 
13
13
  export function resolveContentDir(contentFlag?: string): string {
14
- if (contentFlag) return path.resolve(contentFlag)
15
- if (process.env.CHRONICLE_CONTENT_DIR) return path.resolve(process.env.CHRONICLE_CONTENT_DIR)
16
- return path.resolve('content')
14
+ if (contentFlag) return path.resolve(contentFlag);
15
+ return path.resolve('content');
17
16
  }
18
17
 
19
18
  function resolveConfigPath(contentDir: string): string | null {
20
- const cwdPath = path.join(process.cwd(), 'chronicle.yaml')
21
- if (fs.existsSync(cwdPath)) return cwdPath
22
- const contentPath = path.join(contentDir, 'chronicle.yaml')
23
- if (fs.existsSync(contentPath)) return contentPath
24
- return null
19
+ const cwdPath = path.join(process.cwd(), 'chronicle.yaml');
20
+ if (fs.existsSync(cwdPath)) return cwdPath;
21
+ const contentPath = path.join(contentDir, 'chronicle.yaml');
22
+ if (fs.existsSync(contentPath)) return contentPath;
23
+ return null;
25
24
  }
26
25
 
27
26
  export function loadCLIConfig(contentDir: string): CLIConfig {
28
- const configPath = resolveConfigPath(contentDir)
27
+ const configPath = resolveConfigPath(contentDir);
29
28
 
30
29
  if (!configPath) {
31
- console.log(chalk.red('Error: chronicle.yaml not found in'), process.cwd(), 'or', contentDir)
32
- console.log(chalk.gray(`Run 'chronicle init' to create one`))
33
- process.exit(1)
30
+ console.log(
31
+ chalk.red(
32
+ `Error: chronicle.yaml not found in '${process.cwd()}' or '${contentDir}'`
33
+ )
34
+ );
35
+ console.log(chalk.gray("Run 'chronicle init' to create one"));
36
+ process.exit(1);
34
37
  }
35
38
 
36
- const config = parse(fs.readFileSync(configPath, 'utf-8')) as ChronicleConfig
39
+ const config = parse(fs.readFileSync(configPath, 'utf-8')) as ChronicleConfig;
37
40
 
38
- return {
39
- config,
40
- configPath,
41
- contentDir,
42
- }
41
+ return { config, configPath, contentDir };
43
42
  }
@@ -1,3 +1,3 @@
1
- export * from './config'
2
- export * from './process'
3
- export * from './scaffold'
1
+ export * from './config';
2
+ export * from './resolve';
3
+ export * from './scaffold';