Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,23 @@ WORKDIR /app/packages/chronicle
COPY --from=deps /app /app
COPY packages/chronicle ./
RUN bun build-cli.ts
RUN chmod +x bin/chronicle.js
RUN ln -s /app/packages/chronicle/bin/chronicle.js /usr/local/bin/chronicle

# --- init project ---
WORKDIR /docs
RUN bun add /app/packages/chronicle
RUN chronicle init

# --- runner ---
FROM base AS runner
WORKDIR /app/packages/chronicle

COPY --from=builder /app /app
WORKDIR /docs

RUN chmod +x bin/chronicle.js
COPY --from=builder /docs /docs
COPY --from=builder /app/packages/chronicle /app/packages/chronicle
RUN ln -s /app/packages/chronicle/bin/chronicle.js /usr/local/bin/chronicle

RUN mkdir -p /app/content && ln -s /app/content /app/packages/chronicle/content

VOLUME /app/content

ENV CHRONICLE_CONTENT_DIR=./content
WORKDIR /app/packages/chronicle
VOLUME /docs/content

EXPOSE 3000

Expand Down
39 changes: 18 additions & 21 deletions docs/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,28 @@ Chronicle provides a CLI to initialize, develop, build, and serve your documenta

## init

Scaffold a new Chronicle project.
Initialize a new Chronicle project. Must be run before other commands.

```bash
chronicle init [options]
```

| Flag | Description | Default |
|------|-------------|---------|
| `-d, --dir <path>` | Target directory | `.` (current directory) |
| `-c, --content <path>` | Content directory name | `content` |

Creates a `chronicle.yaml` config file and a sample `index.mdx` in the specified directory.
This creates:
- `chronicle.yaml` — site configuration
- `content/` (or custom name) — content directory with a sample `index.mdx`
- `package.json` — with `@raystack/chronicle` dependency (if not exists)
- `.chronicle/` — scaffolded build directory
- `.gitignore` — with `.chronicle` entry

If the content directory already exists and has files, the sample `index.mdx` is skipped.

## dev

Start the development server with hot reload.
Start the development server with hot reload. Requires `chronicle init` first.

```bash
chronicle dev [options]
Expand All @@ -33,20 +40,15 @@ chronicle dev [options]
| Flag | Description | Default |
|------|-------------|---------|
| `-p, --port <port>` | Port number | `3000` |
| `-c, --content <path>` | Content directory | Current directory |

## build

Build the site for production.
Build the site for production. Requires `chronicle init` first.

```bash
chronicle build [options]
chronicle build
```

| Flag | Description | Default |
|------|-------------|---------|
| `-c, --content <path>` | Content directory | Current directory |

## start

Start the production server. Requires a prior `chronicle build`.
Expand All @@ -58,11 +60,10 @@ chronicle start [options]
| Flag | Description | Default |
|------|-------------|---------|
| `-p, --port <port>` | Port number | `3000` |
| `-c, --content <path>` | Content directory | Current directory |

## serve

Build and start the production server in one step.
Build and start the production server in one step. Requires `chronicle init` first.

```bash
chronicle serve [options]
Expand All @@ -71,14 +72,10 @@ chronicle serve [options]
| Flag | Description | Default |
|------|-------------|---------|
| `-p, --port <port>` | Port number | `3000` |
| `-c, --content <path>` | Content directory | Current directory |

## Content Directory Resolution

The content directory is resolved in this order:
## Config Resolution

1. `--content` CLI flag (highest priority)
2. `CHRONICLE_CONTENT_DIR` environment variable
3. Current working directory (default)
`chronicle.yaml` is resolved in this order:

The content directory must contain a `chronicle.yaml` file.
1. Current working directory
2. Content directory
16 changes: 8 additions & 8 deletions docs/docker.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,23 @@ docker pull raystack/chronicle
The default command builds and starts a production server on port 3000.

```bash
docker run -p 3000:3000 -v ./content:/app/content raystack/chronicle
docker run -p 3000:3000 -v ./content:/docs/content raystack/chronicle
```

Mount your content directory (containing `chronicle.yaml` and MDX files) to `/app/content`.
Mount your content directory (containing MDX files) to `/docs/content`. Place `chronicle.yaml` in the content directory or it will use defaults.

## Development Server

Override the default command with `dev` for hot reload:

```bash
docker run -p 3000:3000 -v ./content:/app/content raystack/chronicle dev --port 3000
docker run -p 3000:3000 -v ./content:/docs/content raystack/chronicle dev --port 3000
```

## Custom Port

```bash
docker run -p 8080:8080 -v ./content:/app/content raystack/chronicle serve --port 8080
docker run -p 8080:8080 -v ./content:/docs/content raystack/chronicle serve --port 8080
```

## Docker Compose
Expand All @@ -47,7 +47,7 @@ services:
ports:
- "3000:3000"
volumes:
- ./content:/app/content
- ./content:/docs/content
```

## Available Commands
Expand All @@ -56,11 +56,11 @@ The entrypoint is `chronicle`, so any CLI command can be passed:

```bash
# Build only
docker run -v ./content:/app/content raystack/chronicle build
docker run -v ./content:/docs/content raystack/chronicle build

# Start pre-built server
docker run -p 3000:3000 -v ./content:/app/content raystack/chronicle start --port 3000
docker run -p 3000:3000 -v ./content:/docs/content raystack/chronicle start --port 3000

# Build and start (default)
docker run -p 3000:3000 -v ./content:/app/content raystack/chronicle serve --port 3000
docker run -p 3000:3000 -v ./content:/docs/content raystack/chronicle serve --port 3000
```
32 changes: 22 additions & 10 deletions docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,15 @@ chronicle init

This creates:
- `chronicle.yaml` — your site configuration
- `index.mdx` — a sample documentation page
- `content/` — content directory with a sample `index.mdx`
- `package.json` — with `@raystack/chronicle` dependency
- `.chronicle/` — scaffolded build directory (gitignored)

To use an existing directory as content (e.g. `docs/`):

```bash
chronicle init -c docs
```

### 2. Start the development server

Expand All @@ -39,13 +47,15 @@ Your docs site is now running at [http://localhost:3000](http://localhost:3000).
Create `.mdx` files in your content directory. Each file becomes a page. Use folders to create nested navigation.

```
content/
my-docs/
├── chronicle.yaml
├── index.mdx
├── getting-started.mdx
└── guides/
├── installation.mdx
└── configuration.mdx
├── content/
│ ├── index.mdx
│ ├── getting-started.mdx
│ └── guides/
│ ├── installation.mdx
│ └── configuration.mdx
└── .chronicle/ # generated, gitignored
```

### 4. Build for production
Expand All @@ -68,9 +78,11 @@ A minimal Chronicle project looks like:
```
my-docs/
├── chronicle.yaml # Site configuration
├── index.mdx # Home page
└── guides/
└── setup.mdx # Nested page at /guides/setup
├── content/
│ ├── index.mdx # Home page
│ └── guides/
│ └── setup.mdx # Nested page at /guides/setup
└── .chronicle/ # Generated by init, gitignored
```

All configuration is done through `chronicle.yaml`. No additional config files needed.
Expand Down
Empty file modified packages/chronicle/bin/chronicle.js
100644 → 100755
Empty file.
5 changes: 3 additions & 2 deletions packages/chronicle/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"src",
"templates",
"next.config.mjs",
"source.config.ts"
"source.config.ts",
"tsconfig.json"
],
"bin": {
"chronicle": "./bin/chronicle.js"
Expand All @@ -27,7 +28,6 @@
"@types/react": "^19.2.10",
"@types/react-dom": "^19.2.3",
"@types/semver": "^7.7.1",
"openapi-types": "^12.1.3",
"semver": "^7.7.4",
"typescript": "5.9.3"
},
Expand Down Expand Up @@ -56,6 +56,7 @@
"slugify": "^1.6.6",
"unified": "^11.0.5",
"unist-util-visit": "^5.1.0",
"openapi-types": "^12.1.3",
"yaml": "^2.8.2",
"zod": "^4.3.6"
}
Expand Down
27 changes: 14 additions & 13 deletions packages/chronicle/src/cli/commands/build.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
import { Command } from 'commander'
import { spawn } from 'child_process'
import path from 'path'
import { fileURLToPath } from 'url'
import fs from 'fs'
import chalk from 'chalk'
import { resolveContentDir, loadCLIConfig, attachLifecycleHandlers } from '@/cli/utils'

const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..')
const nextBin = path.join(PACKAGE_ROOT, 'node_modules', '.bin', process.platform === 'win32' ? 'next.cmd' : 'next')
import { attachLifecycleHandlers, resolveNextCli } from '@/cli/utils'

export const buildCommand = new Command('build')
.description('Build for production')
.option('-c, --content <path>', 'Content directory')
.action((options) => {
const contentDir = resolveContentDir(options.content)
loadCLIConfig(contentDir)
.action(() => {
const scaffoldPath = path.join(process.cwd(), '.chronicle')
if (!fs.existsSync(scaffoldPath)) {
console.log(chalk.red('Error: .chronicle/ not found. Run'), chalk.cyan('chronicle init'), chalk.red('first.'))
process.exit(1)
}

const nextCli = resolveNextCli()

console.log(chalk.cyan('Building for production...'))
console.log(chalk.gray(`Content: ${contentDir}`))

const child = spawn(nextBin, ['build'], {
const child = spawn(process.execPath, [nextCli, 'build'], {
stdio: 'inherit',
cwd: PACKAGE_ROOT,
cwd: scaffoldPath,
env: {
...process.env,
CHRONICLE_CONTENT_DIR: contentDir,
CHRONICLE_PROJECT_ROOT: process.cwd(),
CHRONICLE_CONTENT_DIR: './content',
},
})

Expand Down
25 changes: 13 additions & 12 deletions packages/chronicle/src/cli/commands/dev.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
import { Command } from 'commander'
import { spawn } from 'child_process'
import path from 'path'
import { fileURLToPath } from 'url'
import fs from 'fs'
import chalk from 'chalk'
import { resolveContentDir, loadCLIConfig, attachLifecycleHandlers } from '@/cli/utils'

const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..')
const nextBin = path.join(PACKAGE_ROOT, 'node_modules', '.bin', process.platform === 'win32' ? 'next.cmd' : 'next')
import { attachLifecycleHandlers, resolveNextCli } from '@/cli/utils'

export const devCommand = new Command('dev')
.description('Start development server')
.option('-p, --port <port>', 'Port number', '3000')
.option('-c, --content <path>', 'Content directory')
.action((options) => {
const contentDir = resolveContentDir(options.content)
loadCLIConfig(contentDir)
const scaffoldPath = path.join(process.cwd(), '.chronicle')
if (!fs.existsSync(scaffoldPath)) {
console.log(chalk.red('Error: .chronicle/ not found. Run'), chalk.cyan('chronicle init'), chalk.red('first.'))
process.exit(1)
}

const nextCli = resolveNextCli()

console.log(chalk.cyan('Starting dev server...'))
console.log(chalk.gray(`Content: ${contentDir}`))

const child = spawn(nextBin, ['dev', '-p', options.port], {
const child = spawn(process.execPath, [nextCli, 'dev', '-p', options.port], {
stdio: 'inherit',
cwd: PACKAGE_ROOT,
cwd: scaffoldPath,
env: {
...process.env,
CHRONICLE_CONTENT_DIR: contentDir,
CHRONICLE_PROJECT_ROOT: process.cwd(),
CHRONICLE_CONTENT_DIR: './content',
},
})

Expand Down
Loading