Skip to content

Developer Guide ​

You've cloned the repo and want to understand or extend chartjs2img. This page points you to the right spot for each kind of change.

What this project is ​

A thin service around Chart.js rendered on one of two engines — the default skia engine (native Skia canvas, in-process, no browser) or the browser engine (headless Chromium via puppeteer):

chartjs2img architecture overview: CLI and HTTP server both invoke renderer.ts, which dispatches to the skia engine (in-process Skia canvas) or the browser engine (puppeteer-driven headless Chromium), both running Chart.js and its plugins.

Everything on the Node side is written so it can run unchanged under bun --compile — there's no build step required for development.

Repo layout ​

chartjs2img/
├── src/
│   ├── index.ts         # CLI entry point: argv parser, subcommand dispatch
│   ├── cli.ts           # `render` + `examples` CLI implementations
│   ├── server.ts        # `serve` HTTP server (Bun.serve)
│   ├── renderer.ts      # Engine dispatch: skia (in-process) + browser (puppeteer/Chromium) pipelines
│   ├── template.ts      # Static HTML template loaded in the browser
│   ├── cache.ts         # In-memory LRU + TTL cache
│   ├── semaphore.ts     # Tiny async semaphore for concurrency control
│   ├── examples.ts      # Built-in chart examples (used by CLI + gallery)
│   ├── version.ts       # Single source of truth for VERSION
│   └── llm-docs/        # Per-module LLM-oriented Markdown snippets
│       ├── index.ts     # Aggregates + exports getLlmDocs()
│       ├── usage.ts
│       ├── chartjs-core.ts
│       ├── plugin-*.ts
│       ├── chart-*.ts
│       └── adapter-*.ts
├── examples/            # JSON inputs + PNG outputs (regenerable)
├── docs/                # VitePress bilingual documentation site
├── .github/workflows/   # CI
├── Dockerfile
├── package.json
└── README.md

The code base is intentionally small (~2000 LOC excluding llm-docs) — the heavy lifting happens either in-process on the native Skia canvas (default) or inside Chromium (browser engine). When you're reading, the interesting file is usually renderer.ts.

Common contribution flows ​

"I want to add another Chart.js plugin" ​

See Adding a Chart.js plugin.

"I want to document a plugin for LLMs" ​

See Adding LLM docs.

"I want to tune concurrency / cache / browser behavior" ​

See Architecture for the moving parts, then check Modules for which file to edit. Most knobs are env vars — no rebuild needed.

"I hit a bug in rendering" ​

Check the captured console messages first. Both engines collect Chart.js console output during a render — the skia engine in-process, the browser engine via renderer.ts's page.on('console', …) + window.__chartMessages. They surface to the caller via X-Chart-Messages (HTTP) or stderr (CLI) — see Error handling.

Running from source ​

bash
bun install
bun run dev              # HTTP server on :3000
bun run cli -- help      # CLI help
bun run cli -- llm       # LLM reference output

bun run prepends the project's local binaries and doesn't need a compiled binary. Type check with bun run typecheck.

Running the full site locally ​

bash
bun run docs:dev         # VitePress dev server on :5173

The docs site has no backend. It reads docs/en/** and docs/ja/**, plus the sidebar/nav defined in docs/.vitepress/config.ts.

Where to go next ​

Edit this pageLast updated: