@raystack/chronicle 0.3.0 → 0.5.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 (84) hide show
  1. package/dist/cli/index.js +425 -9937
  2. package/package.json +19 -10
  3. package/src/cli/commands/build.ts +33 -31
  4. package/src/cli/commands/dev.ts +23 -31
  5. package/src/cli/commands/init.ts +38 -132
  6. package/src/cli/commands/serve.ts +41 -55
  7. package/src/cli/commands/start.ts +20 -31
  8. package/src/cli/index.ts +14 -14
  9. package/src/cli/utils/config.ts +58 -30
  10. package/src/cli/utils/index.ts +3 -3
  11. package/src/cli/utils/resolve.ts +7 -3
  12. package/src/cli/utils/scaffold.ts +11 -130
  13. package/src/components/mdx/code.tsx +10 -1
  14. package/src/components/mdx/details.module.css +1 -26
  15. package/src/components/mdx/details.tsx +2 -3
  16. package/src/components/mdx/image.tsx +5 -34
  17. package/src/components/mdx/index.tsx +15 -1
  18. package/src/components/mdx/link.tsx +18 -15
  19. package/src/components/ui/breadcrumbs.tsx +8 -42
  20. package/src/components/ui/search.tsx +63 -51
  21. package/src/lib/api-routes.ts +6 -8
  22. package/src/lib/config.ts +12 -36
  23. package/src/lib/head.tsx +49 -0
  24. package/src/lib/openapi.ts +8 -8
  25. package/src/lib/page-context.tsx +111 -0
  26. package/src/lib/remark-strip-md-extensions.ts +14 -0
  27. package/src/lib/source.ts +139 -63
  28. package/src/pages/ApiLayout.tsx +33 -0
  29. package/src/pages/ApiPage.tsx +73 -0
  30. package/src/pages/DocsLayout.tsx +18 -0
  31. package/src/pages/DocsPage.tsx +43 -0
  32. package/src/pages/NotFound.tsx +17 -0
  33. package/src/server/App.tsx +72 -0
  34. package/src/server/api/apis-proxy.ts +69 -0
  35. package/src/server/api/health.ts +5 -0
  36. package/src/server/api/page/[...slug].ts +18 -0
  37. package/src/server/api/search.ts +118 -0
  38. package/src/server/api/specs.ts +9 -0
  39. package/src/server/build-search-index.ts +117 -0
  40. package/src/server/entry-client.tsx +88 -0
  41. package/src/server/entry-server.tsx +102 -0
  42. package/src/server/routes/llms.txt.ts +21 -0
  43. package/src/server/routes/og.tsx +75 -0
  44. package/src/server/routes/robots.txt.ts +11 -0
  45. package/src/server/routes/sitemap.xml.ts +40 -0
  46. package/src/server/utils/safe-path.ts +17 -0
  47. package/src/server/vite-config.ts +133 -0
  48. package/src/themes/default/Layout.tsx +78 -48
  49. package/src/themes/default/Page.module.css +44 -0
  50. package/src/themes/default/Page.tsx +9 -11
  51. package/src/themes/default/Toc.tsx +25 -39
  52. package/src/themes/default/index.ts +7 -9
  53. package/src/themes/paper/ChapterNav.tsx +64 -45
  54. package/src/themes/paper/Layout.module.css +1 -1
  55. package/src/themes/paper/Layout.tsx +24 -12
  56. package/src/themes/paper/Page.module.css +16 -4
  57. package/src/themes/paper/Page.tsx +56 -63
  58. package/src/themes/paper/ReadingProgress.tsx +160 -139
  59. package/src/themes/paper/index.ts +5 -5
  60. package/src/themes/registry.ts +14 -7
  61. package/src/types/config.ts +86 -67
  62. package/src/types/content.ts +5 -21
  63. package/src/types/globals.d.ts +4 -0
  64. package/src/types/theme.ts +4 -3
  65. package/tsconfig.json +2 -3
  66. package/next.config.mjs +0 -10
  67. package/source.config.ts +0 -51
  68. package/src/app/[[...slug]]/layout.tsx +0 -15
  69. package/src/app/[[...slug]]/page.tsx +0 -106
  70. package/src/app/api/apis-proxy/route.ts +0 -59
  71. package/src/app/api/health/route.ts +0 -3
  72. package/src/app/api/search/route.ts +0 -90
  73. package/src/app/apis/[[...slug]]/layout.tsx +0 -26
  74. package/src/app/apis/[[...slug]]/page.tsx +0 -117
  75. package/src/app/layout.tsx +0 -57
  76. package/src/app/llms-full.txt/route.ts +0 -18
  77. package/src/app/llms.txt/route.ts +0 -15
  78. package/src/app/og/route.tsx +0 -62
  79. package/src/app/providers.tsx +0 -8
  80. package/src/app/robots.ts +0 -10
  81. package/src/app/sitemap.ts +0 -29
  82. package/src/cli/utils/process.ts +0 -7
  83. package/src/themes/default/font.ts +0 -6
  84. /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.3.0",
3
+ "version": "0.5.0",
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,26 +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": "3.0.260311-beta",
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-frontmatter": "^5.0.0",
61
+ "remark-gfm": "^4.0.1",
62
+ "remark-mdx-frontmatter": "^5.2.0",
63
+ "remark-parse": "^11.0.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",
68
+ "vite": "^8.0.0",
60
69
  "yaml": "^2.8.2",
61
70
  "zod": "^4.3.6"
62
71
  }
@@ -1,38 +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 { loadCLIConfig } 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('--content <path>', 'Content directory')
10
+ .option('--config <path>', 'Path to chronicle.yaml')
11
+ .option(
12
+ '--preset <preset>',
13
+ 'Deploy preset (vercel, cloudflare, node-server)'
14
+ )
15
+ .action(async options => {
16
+ const { contentDir, configPath, preset } = await loadCLIConfig(options.config, {
17
+ content: options.content,
18
+ preset: options.preset,
19
+ });
20
+ await linkContent(contentDir);
16
21
 
17
- let nextCli: string
18
- try {
19
- nextCli = resolveNextCli()
20
- } catch {
21
- console.log(chalk.red('Error: Next.js CLI not found. Run'), chalk.cyan('chronicle init'), chalk.red('first.'))
22
- process.exit(1)
23
- }
22
+ console.log(chalk.cyan('Building for production...'));
24
23
 
25
- console.log(chalk.cyan('Building for production...'))
24
+ const { createBuilder } = await import('vite');
25
+ const { createViteConfig } = await import('@/server/vite-config');
26
26
 
27
- const child = spawn(process.execPath, [nextCli, 'build'], {
28
- stdio: 'inherit',
29
- cwd: scaffoldPath,
30
- env: {
31
- ...process.env,
32
- CHRONICLE_PROJECT_ROOT: process.cwd(),
33
- CHRONICLE_CONTENT_DIR: './content',
34
- },
35
- })
27
+ const config = await createViteConfig({
28
+ packageRoot: PACKAGE_ROOT,
29
+ projectRoot: process.cwd(),
30
+ contentDir,
31
+ configPath,
32
+ preset
33
+ });
36
34
 
37
- attachLifecycleHandlers(child)
38
- })
35
+ const builder = await createBuilder({ ...config, builder: {} });
36
+ await builder.buildApp();
37
+
38
+ console.log(chalk.green('Build complete'));
39
+ console.log(chalk.cyan('Run `chronicle start` to start the server'));
40
+ });
@@ -1,39 +1,31 @@
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 { loadCLIConfig } 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 devCommand = new Command('dev')
9
8
  .description('Start development 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('--content <path>', 'Content directory')
11
+ .option('--config <path>', 'Path to chronicle.yaml')
12
+ .action(async options => {
13
+ const { contentDir, configPath } = await loadCLIConfig(options.config, { content: options.content });
14
+ const port = parseInt(options.port, 10);
17
15
 
18
- let nextCli: string
19
- try {
20
- nextCli = resolveNextCli()
21
- } catch {
22
- console.log(chalk.red('Error: Next.js CLI not found. Run'), chalk.cyan('chronicle init'), chalk.red('first.'))
23
- process.exit(1)
24
- }
16
+ await linkContent(contentDir);
25
17
 
26
- console.log(chalk.cyan('Starting dev server...'))
18
+ console.log(chalk.cyan('Starting dev server...'));
27
19
 
28
- const child = spawn(process.execPath, [nextCli, 'dev', '-p', options.port], {
29
- stdio: 'inherit',
30
- cwd: scaffoldPath,
31
- env: {
32
- ...process.env,
33
- CHRONICLE_PROJECT_ROOT: process.cwd(),
34
- CHRONICLE_CONTENT_DIR: './content',
35
- },
36
- })
20
+ const { createServer } = await import('vite');
21
+ const { createViteConfig } = await import('@/server/vite-config');
37
22
 
38
- attachLifecycleHandlers(child)
39
- })
23
+ const config = await createViteConfig({ packageRoot: PACKAGE_ROOT, projectRoot: process.cwd(), contentDir, configPath });
24
+ const server = await createServer({
25
+ ...config,
26
+ server: { ...config.server, port }
27
+ });
28
+
29
+ await server.listen();
30
+ server.printUrls();
31
+ });
@@ -1,44 +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, getChronicleVersion } 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
- type: 'module',
25
- scripts: {
26
- dev: 'chronicle dev',
27
- build: 'chronicle build',
28
- start: 'chronicle start',
29
- },
30
- dependencies: {
31
- '@raystack/chronicle': `^${getChronicleVersion()}`,
32
- },
33
- devDependencies: {
34
- '@raystack/tools-config': '0.56.0',
35
- 'openapi-types': '^12.1.3',
36
- typescript: '5.9.3',
37
- '@types/react': '^19.2.10',
38
- '@types/node': '^25.1.0',
39
- },
40
- }
41
- }
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
+ };
42
14
 
43
15
  const sampleMdx = `---
44
16
  title: Welcome
@@ -49,115 +21,49 @@ order: 1
49
21
  # Welcome
50
22
 
51
23
  This is your documentation home page.
52
- `
24
+ `;
53
25
 
54
26
  export const initCommand = new Command('init')
55
27
  .description('Initialize a new Chronicle project')
56
28
  .option('-c, --content <path>', 'Content directory name', 'content')
57
- .action((options) => {
58
- const projectDir = process.cwd()
59
- const dirName = path.basename(projectDir) || 'docs'
60
- const contentDir = path.join(projectDir, options.content)
29
+ .action(options => {
30
+ const projectDir = process.cwd();
31
+ const contentDir = path.join(projectDir, options.content);
61
32
 
62
- // Create content directory if it doesn't exist
63
33
  if (!fs.existsSync(contentDir)) {
64
- fs.mkdirSync(contentDir, { recursive: true })
65
- console.log(chalk.green(''), 'Created', contentDir)
66
- }
67
-
68
- // Create or update package.json in project root
69
- const packageJsonPath = path.join(projectDir, 'package.json')
70
- if (!fs.existsSync(packageJsonPath)) {
71
- fs.writeFileSync(packageJsonPath, JSON.stringify(createPackageJson(dirName), null, 2) + '\n')
72
- console.log(chalk.green('✓'), 'Created', packageJsonPath)
73
- } else {
74
- const existing = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))
75
- const template = createPackageJson(dirName)
76
- let updated = false
77
-
78
- // Set type to module
79
- if (existing.type !== 'module') {
80
- existing.type = 'module'
81
- updated = true
82
- }
83
-
84
- // Merge missing scripts
85
- if (!existing.scripts) existing.scripts = {}
86
- for (const [key, value] of Object.entries(template.scripts as Record<string, string>)) {
87
- if (!existing.scripts[key]) {
88
- existing.scripts[key] = value
89
- updated = true
90
- }
91
- }
92
-
93
- // Merge missing dependencies
94
- if (!existing.dependencies) existing.dependencies = {}
95
- for (const [key, value] of Object.entries(template.dependencies as Record<string, string>)) {
96
- if (!existing.dependencies[key]) {
97
- existing.dependencies[key] = value
98
- updated = true
99
- }
100
- }
101
-
102
- // Merge missing devDependencies
103
- if (!existing.devDependencies) existing.devDependencies = {}
104
- for (const [key, value] of Object.entries(template.devDependencies as Record<string, string>)) {
105
- if (!existing.devDependencies[key]) {
106
- existing.devDependencies[key] = value
107
- updated = true
108
- }
109
- }
110
-
111
- if (updated) {
112
- fs.writeFileSync(packageJsonPath, JSON.stringify(existing, null, 2) + '\n')
113
- console.log(chalk.green('✓'), 'Updated', packageJsonPath, 'with missing scripts/deps')
114
- } else {
115
- console.log(chalk.yellow('⚠'), packageJsonPath, 'already has all required entries')
116
- }
34
+ fs.mkdirSync(contentDir, { recursive: true });
35
+ console.log(chalk.green('\u2713'), 'Created', contentDir);
117
36
  }
118
37
 
119
- // Create chronicle.yaml in project root
120
- const configPath = path.join(projectDir, 'chronicle.yaml')
38
+ const configPath = path.join(projectDir, 'chronicle.yaml');
121
39
  if (!fs.existsSync(configPath)) {
122
- fs.writeFileSync(configPath, stringify(createConfig()))
123
- console.log(chalk.green(''), 'Created', configPath)
40
+ fs.writeFileSync(configPath, stringify(defaultConfig));
41
+ console.log(chalk.green('\u2713'), 'Created', configPath);
124
42
  } else {
125
- console.log(chalk.yellow(''), configPath, 'already exists')
43
+ console.log(chalk.yellow('\u26a0'), configPath, 'already exists');
126
44
  }
127
45
 
128
- // Create sample index.mdx only if content dir is empty
129
- const contentFiles = fs.readdirSync(contentDir)
46
+ const contentFiles = fs.readdirSync(contentDir);
130
47
  if (contentFiles.length === 0) {
131
- const indexPath = path.join(contentDir, 'index.mdx')
132
- fs.writeFileSync(indexPath, sampleMdx)
133
- 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);
134
51
  }
135
52
 
136
- // Add entries to .gitignore
137
- const gitignorePath = path.join(projectDir, '.gitignore')
138
- const gitignoreEntries = ['.chronicle', 'node_modules', '.next']
53
+ const gitignorePath = path.join(projectDir, '.gitignore');
54
+ const gitignoreEntries = ['node_modules', 'dist', '.output'];
139
55
  if (fs.existsSync(gitignorePath)) {
140
- const existing = fs.readFileSync(gitignorePath, 'utf-8')
141
- const missing = gitignoreEntries.filter(e => !existing.includes(e))
56
+ const existing = fs.readFileSync(gitignorePath, 'utf-8');
57
+ const missing = gitignoreEntries.filter(e => !existing.includes(e));
142
58
  if (missing.length > 0) {
143
- fs.appendFileSync(gitignorePath, `\n${missing.join('\n')}\n`)
144
- console.log(chalk.green(''), 'Added', missing.join(', '), 'to .gitignore')
59
+ fs.appendFileSync(gitignorePath, `\n${missing.join('\n')}\n`);
60
+ console.log(chalk.green('\u2713'), 'Added', missing.join(', '), 'to .gitignore');
145
61
  }
146
62
  } else {
147
- fs.writeFileSync(gitignorePath, `${gitignoreEntries.join('\n')}\n`)
148
- console.log(chalk.green(''), 'Created .gitignore')
63
+ fs.writeFileSync(gitignorePath, `${gitignoreEntries.join('\n')}\n`);
64
+ console.log(chalk.green('\u2713'), 'Created .gitignore');
149
65
  }
150
66
 
151
- // Install dependencies
152
- const pm = detectPackageManager()
153
- console.log(chalk.cyan(`\nInstalling dependencies with ${pm}...`))
154
- execSync(`${pm} install`, { cwd: projectDir, stdio: 'inherit' })
155
-
156
- // Scaffold .chronicle/ directory
157
- loadCLIConfig(contentDir)
158
- scaffoldDir(contentDir)
159
-
160
- const runCmd = pm === 'npm' ? 'npx' : pm === 'bun' ? 'bunx' : `${pm} dlx`
161
- console.log(chalk.green('\n✓ Chronicle initialized!'))
162
- console.log('\nRun', chalk.cyan(`${runCmd} chronicle dev`), 'to start development server')
163
- })
67
+ console.log(chalk.green('\n\u2713 Chronicle initialized!'));
68
+ console.log('\nRun', chalk.cyan('chronicle dev'), 'to start development server');
69
+ });
@@ -1,59 +1,45 @@
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 { loadCLIConfig } 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
- let nextCli: string
19
- try {
20
- nextCli = resolveNextCli()
21
- } catch {
22
- console.log(chalk.red('Error: Next.js CLI not found. Run'), chalk.cyan('chronicle init'), chalk.red('first.'))
23
- process.exit(1)
24
- }
25
-
26
- const env = {
27
- ...process.env,
28
- CHRONICLE_PROJECT_ROOT: process.cwd(),
29
- CHRONICLE_CONTENT_DIR: './content',
30
- }
31
-
32
- console.log(chalk.cyan('Building for production...'))
33
-
34
- const buildChild = spawn(process.execPath, [nextCli, 'build'], {
35
- stdio: 'inherit',
36
- cwd: scaffoldPath,
37
- env,
38
- })
39
-
40
- process.once('SIGINT', () => buildChild.kill('SIGINT'))
41
- process.once('SIGTERM', () => buildChild.kill('SIGTERM'))
42
-
43
- buildChild.on('close', (code) => {
44
- if (code !== 0) {
45
- console.log(chalk.red('Build failed'))
46
- process.exit(code ?? 1)
47
- }
48
-
49
- console.log(chalk.cyan('Starting production server...'))
50
-
51
- const startChild = spawn(process.execPath, [nextCli, 'start', '-p', options.port], {
52
- stdio: 'inherit',
53
- cwd: scaffoldPath,
54
- env,
55
- })
56
-
57
- attachLifecycleHandlers(startChild)
58
- })
59
- })
10
+ .option('--content <path>', 'Content directory')
11
+ .option('--config <path>', 'Path to chronicle.yaml')
12
+ .option(
13
+ '--preset <preset>',
14
+ 'Deploy preset (vercel, cloudflare, node-server)'
15
+ )
16
+ .action(async options => {
17
+ const { contentDir, configPath, preset } = await loadCLIConfig(options.config, {
18
+ content: options.content,
19
+ preset: options.preset,
20
+ });
21
+ const port = parseInt(options.port, 10);
22
+ await linkContent(contentDir);
23
+
24
+ const { build, preview } = await import('vite');
25
+ const { createViteConfig } = await import('@/server/vite-config');
26
+
27
+ const config = await createViteConfig({
28
+ packageRoot: PACKAGE_ROOT,
29
+ projectRoot: process.cwd(),
30
+ contentDir,
31
+ configPath,
32
+ preset
33
+ });
34
+
35
+ console.log(chalk.cyan('Building for production...'));
36
+ await build(config);
37
+
38
+ console.log(chalk.cyan('Starting production server...'));
39
+ const server = await preview({
40
+ ...config,
41
+ preview: { port }
42
+ });
43
+
44
+ server.printUrls();
45
+ });
@@ -1,39 +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 { loadCLIConfig } 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('--content <path>', 'Content directory')
11
+ .action(async options => {
12
+ const { contentDir, configPath } = await loadCLIConfig(undefined, { content: options.content });
13
+ const port = parseInt(options.port, 10);
14
+ await linkContent(contentDir);
17
15
 
18
- let nextCli: string
19
- try {
20
- nextCli = resolveNextCli()
21
- } catch {
22
- console.log(chalk.red('Error: Next.js CLI not found. Run'), chalk.cyan('chronicle init'), chalk.red('first.'))
23
- process.exit(1)
24
- }
16
+ console.log(chalk.cyan('Starting production server...'));
25
17
 
26
- console.log(chalk.cyan('Starting production server...'))
18
+ const { preview } = await import('vite');
19
+ const { createViteConfig } = await import('@/server/vite-config');
27
20
 
28
- const child = spawn(process.execPath, [nextCli, 'start', '-p', options.port], {
29
- stdio: 'inherit',
30
- cwd: scaffoldPath,
31
- env: {
32
- ...process.env,
33
- CHRONICLE_PROJECT_ROOT: process.cwd(),
34
- CHRONICLE_CONTENT_DIR: './content',
35
- },
36
- })
21
+ const config = await createViteConfig({ packageRoot: PACKAGE_ROOT, projectRoot: process.cwd(), contentDir, configPath });
22
+ const server = await preview({
23
+ ...config,
24
+ preview: { port }
25
+ });
37
26
 
38
- attachLifecycleHandlers(child)
39
- })
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();