Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4b1d189
refactor(page-compiler)!: route all transform failures through format…
YusukeHirao May 12, 2026
e75dcbb
feat(kamado): add outputPathConflict policy for output-path collisions
YusukeHirao May 12, 2026
8f8241e
docs(page-compiler): document outputPathConflict policy
YusukeHirao May 12, 2026
5794229
chore(release): publish v2.0.0-alpha.16
YusukeHirao May 12, 2026
0baea5f
chore(repo): wire up build benchmark command
YusukeHirao Jun 6, 2026
445ae6a
feat(kamado): optimize the build pipeline and add skip-unchanged writes
YusukeHirao Jun 6, 2026
33132ee
feat(page-compiler)!: resolve compileHooks and transforms once per co…
YusukeHirao Jun 6, 2026
3c0e50f
feat(style-compiler): build the PostCSS processor once per context
YusukeHirao Jun 6, 2026
fb87d98
feat(script-compiler): bundle in memory and select output by path
YusukeHirao Jun 6, 2026
fe7da15
feat(pug-compiler): cache compiled templates per build context
YusukeHirao Jun 6, 2026
d8a5584
docs(kamado): document caching layers, skip-unchanged, and benchmarking
YusukeHirao Jun 6, 2026
514e929
docs(repo): add migration guide for per-context hook resolution
YusukeHirao Jun 6, 2026
9543aa6
Merge branch 'dev' into feat/build-performance
YusukeHirao Jun 9, 2026
ad148e4
feat(kamado): add shared compiler helpers and unified cache clearing
YusukeHirao Jun 10, 2026
d495848
refactor(style-compiler): use shared banner/sourcemap helpers
YusukeHirao Jun 10, 2026
18690e3
refactor(script-compiler): use shared banner/sourcemap helpers
YusukeHirao Jun 10, 2026
10e8e3d
docs(pug-compiler): explain why the template cache uses LRU over FIFO
YusukeHirao Jun 10, 2026
16dddf9
docs(kamado): document the cache-flag contract and dual serve-mode si…
YusukeHirao Jun 10, 2026
a11c328
test(kamado): cover the new public helpers and cache-clear wiring
YusukeHirao Jun 10, 2026
d06ab0d
test(style-compiler): assert string output instead of branching in th…
YusukeHirao Jun 10, 2026
3481f42
test(script-compiler): assert string output instead of branching in t…
YusukeHirao Jun 10, 2026
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ yarn-error.log
# build outputs
packages/**/dist/
packages/**/tsconfig.build.tsbuildinfo

# benchmark fixtures
packages/**/.bench/
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

# [2.0.0-alpha.16](https://github.com/d-zero-dev/kamado/compare/v2.0.0-alpha.15...v2.0.0-alpha.16) (2026-05-12)

- refactor(page-compiler)!: route all transform failures through formatOptions.parseError ([4b1d189](https://github.com/d-zero-dev/kamado/commit/4b1d1897c4db2daadba7a4555f916f6682eed48c))

### Features

- **kamado:** add outputPathConflict policy for output-path collisions ([e75dcbb](https://github.com/d-zero-dev/kamado/commit/e75dcbb6a5bc39e524811c9a0c49386043f40f3c))

### BREAKING CHANGES

- `PrettierOptions.parseError` and the `PrettierParseErrorMode`
type are removed. The `DefaultPageTransformsOptions` interface is removed and
`createDefaultPageTransforms()` no longer takes an argument. The unified type
is now `ParseErrorMode` exported from the package entry. Error message format
changed to `Transform '<name>' failed on <source>: <original>`.

Tests cover prettier, minifier, and custom-transform failure under each of the
three modes, plus pipeline continuation (a second failure after the first is
still logged) and the `inputPath` -> `outputPath` fallback in the message.

# [2.0.0-alpha.15](https://github.com/d-zero-dev/kamado/compare/v2.0.0-alpha.14...v2.0.0-alpha.15) (2026-05-12)

### Bug Fixes
Expand Down
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ kamado — オンデマンド静的サイトジェネレータ。Lerna + Yarn Wo
- `yarn dev` — `lerna run dev`
- `yarn test` — Vitest でテスト(test-timeout 60000)
- `yarn lint` — eslint / prettier / textlint / cspell を直列実行
- `yarn bench` — ビルドベンチマーク(`--pages=N` / `--runs=N` / `--full`。要事前 `yarn build`。詳細は `packages/kamado/ARCHITECTURE.md` 参照)
- `yarn release` / `yarn release:alpha` 等 — `lerna version`(push なし)。リリース手順は `/release` コマンド参照

### コマンド制約
Expand Down
46 changes: 46 additions & 0 deletions MILESTONE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,55 @@
- [x] `getTitleFromStaticFile` を削除
- [x] `kamado/features` に deprecation 警告を追加(v2.0.0 で削除予定)
- [x] `PageData` 型を追加(`metaData.title` でタイトル管理)
- [x] `compileHooks` / `transforms` の関数形式の解決タイミングを「ファイルごと」から「build/serve コンテキストごとに1回」に変更(ビルド高速化)

## Migration Guide

### `compileHooks` / `transforms` の解決タイミング変更 (v2.0.0)

ビルド高速化のため、`PageCompilerOptions` の `compileHooks` と `transforms` を関数として指定した場合の解決タイミングが変更されました。

#### 変更内容

| API | v1.x(変更前) | v2.0.0(変更後) |
| ---------------------- | -------------------- | ----------------------------------------- |
| `compileHooks`(関数) | ページごとに毎回呼出 | build/serve コンテキストごとに1回だけ呼出 |
| `transforms`(関数) | ページごとに毎回呼出 | build/serve コンテキストごとに1回だけ呼出 |

返されたフック・transform インスタンスは、そのコンテキストの全ページ(並行コンパイルを含む)で共有されます。

#### 影響を受けるケースと対応

ファクトリ関数内でページごとに変わる値を読んでいた場合のみ影響があります。

```ts
// ❌ v1.x ではページごとに評価されていたが、v2.0.0 では起動時に1回だけ評価される
def(createPageCompiler(), {
transforms: (defaults) => [
{
name: 'timestamp',
transform: (content) => content.replace('%TIME%', factoryTime), // factoryTime はファクトリ実行時に固定
},
...defaults,
],
});

// ✅ v2.0.0 — ページごとに変わる値は transform 関数の「実行時」(content, context を受け取る側)で読む
def(createPageCompiler(), {
transforms: (defaults) => [
{
name: 'timestamp',
transform: (content) => content.replace('%TIME%', String(Date.now())), // 変換実行時に評価
},
...defaults,
],
});
```

`compileHooks` も同様に、ページごとの動的な値はファクトリ内ではなく `before` / `compiler` / `after` フックの実行時(`content` と `data` を受け取る側)で評価してください。transform / フックのインスタンスにページごとの可変状態(カウンタ・蓄積バッファ等)を持たせることはできません。

ファクトリがページに依存しない初期化のみを行っている場合(`@kamado-io/pug-compiler` の `createCompileHooks` を含む)、対応は不要です。

### `kamado/features` の削除 (v2.0.0)

`kamado/features` エクスポートは v2.0.0 で削除されました。
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"lerna": "8.1.2",
"version": "2.0.0-alpha.15",
"version": "2.0.0-alpha.16",
"npmClient": "yarn",
"packages": ["packages/*", "packages/@kamado-io/*"],
"command": {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"private": true,
"type": "module",
"scripts": {
"bench": "node packages/kamado/benchmark/run-bench.ts",
"build": "lerna run build",
"dev": "lerna run dev",
"test": "vitest run --test-timeout 60000",
Expand Down
16 changes: 16 additions & 0 deletions packages/@kamado-io/page-compiler/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

# [2.0.0-alpha.16](https://github.com/d-zero-dev/kamado/compare/v2.0.0-alpha.15...v2.0.0-alpha.16) (2026-05-12)

- refactor(page-compiler)!: route all transform failures through formatOptions.parseError ([4b1d189](https://github.com/d-zero-dev/kamado/commit/4b1d1897c4db2daadba7a4555f916f6682eed48c))

### BREAKING CHANGES

- `PrettierOptions.parseError` and the `PrettierParseErrorMode`
type are removed. The `DefaultPageTransformsOptions` interface is removed and
`createDefaultPageTransforms()` no longer takes an argument. The unified type
is now `ParseErrorMode` exported from the package entry. Error message format
changed to `Transform '<name>' failed on <source>: <original>`.

Tests cover prettier, minifier, and custom-transform failure under each of the
three modes, plus pipeline continuation (a second failure after the first is
still logged) and the `inputPath` -> `outputPath` fallback in the message.

# [2.0.0-alpha.15](https://github.com/d-zero-dev/kamado/compare/v2.0.0-alpha.14...v2.0.0-alpha.15) (2026-05-12)

### Bug Fixes
Expand Down
46 changes: 36 additions & 10 deletions packages/@kamado-io/page-compiler/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,18 @@ export default defineConfig({
- `(defaultTransforms: readonly Transform[]) => Transform[]` - Function that receives default transforms (5 transforms) and returns modified array
- If omitted, uses `createDefaultPageTransforms()` (5 transforms: manipulateDOM, doctype, prettier, minifier, lineBreak). See [Transform Pipeline](#transform-pipeline) for details.
- **Note**: Uses the same `Transform` interface as `devServer.transforms`, but applies only to HTML pages in both build and serve modes. The `filter` option is ignored here (use `devServer.transforms` for filtering).
- **Note**: When given as a function, it is resolved **once per build/serve context**, not per file. The returned transform instances are shared by all pages in that context (and may run concurrently), so they must not keep per-page mutable state.
- `transformBreadcrumbItem`: Function to transform each breadcrumb item. Each item includes a `meta` property containing the source page's metadata, enabling metadata-based transformations (e.g., redirect URLs). `(item: BreadcrumbItem<M>) => BreadcrumbItem<M>`
- `filterNavigationNode`: Function to filter navigation nodes. Return `true` to keep the node, `false` to remove it. `(node: NavNode<M>) => boolean`
- `navigationComparator`: Sort comparator for the navigation path list. Can be overridden per-call via `nav({ comparator })` in templates.
- `'path'`: Sort by path using `pathComparator`
- `(a: string, b: string) => number`: Custom comparator function
- `null` (default): No sorting (preserve original order)
- `formatOptions`: Options applied to the **default** format transforms. Only forwarded to transforms produced by `createDefaultPageTransforms`; if a custom `transforms` array is supplied, pass these settings to the relevant transform factories directly (e.g. `prettier({ parseError })`).
- `parseError`: Behavior when Prettier fails to parse the input. One of `'silent' | 'warning' | 'error'` (default: `'silent'`). See the [`prettier` transform](#transform-pipeline) section for details.
- `formatOptions`: Pipeline-level error policy applied to **every** transform in the compiled chain (both built-in transforms like `prettier`/`minifier` and any custom ones from `transforms`).
- `parseError`: Behavior when a transform throws. One of:
- `'silent'` (default) — swallow the error, skip the failing transform, and pass the previous step's output to the next one
- `'warning'` — `console.warn` with `Transform '<name>' failed on <source>: <original>`, then skip the failing transform
- `'error'` — throw an `Error` with the same message prefix; the original error is preserved on `error.cause`
- Example:

```typescript
Expand All @@ -69,14 +73,15 @@ export default defineConfig({

- `compileHooks`: Compilation hooks for customizing compile process
- Can be an object or a function `(options: PageCompilerOptions<M>) => CompileHooksObject<M> | Promise<CompileHooksObject<M>>` that returns an object (sync or async)
- **Note**: A function form is resolved **once per build/serve context**, not per file. Hook factories must be file-independent; the returned hooks are shared by all pages compiled in that context.
- `main`: Hooks for main content compilation
- `before`: Hook called before compilation (receives content and data, returns processed content)
- `after`: Hook called after compilation (receives HTML and data, returns processed HTML)
- `compiler`: Custom compiler function `(content: string, data: CompileData<M>, extension: string) => Promise<string> | string`
- `compiler`: Custom compiler function `(content: string, data: CompileData<M>, extension: string, cache?: boolean) => Promise<string> | string`. The optional `cache` flag tells the compiler whether it may reuse cached compilation artifacts (e.g. compiled template functions) — it is `false` in serve mode so that template/include edits are always reflected
- `layout`: Hooks for layout compilation
- `before`: Hook called before compilation (receives content and data, returns processed content)
- `after`: Hook called after compilation (receives HTML and data, returns processed HTML)
- `compiler`: Custom compiler function `(content: string, data: CompileData<M>, extension: string) => Promise<string> | string`
- `compiler`: Custom compiler function `(content: string, data: CompileData<M>, extension: string, cache?: boolean) => Promise<string> | string` (same `cache` semantics as `main.compiler`)

**Note**: To use Pug templates, install `@kamado-io/pug-compiler` and use `createCompileHooks` helper. See the [@kamado-io/pug-compiler README](../@kamado-io/pug-compiler/README.md) for integration examples.

Expand Down Expand Up @@ -122,7 +127,7 @@ Rules:
- The value must start with `/`.
- `.` and `..` segments are rejected (the resolved path must stay inside `dir.output`).
- Non-string values (numbers, arrays, etc.) for the configured field are ignored — only string values trigger an override.
- If two source files resolve to the same output path, the build aborts with a collision error pointing to both sources.
- If two source files resolve to the same output path, the behavior is controlled by `outputPathConflict` (see below).
- A same-name `.json` sidecar takes precedence over the YAML frontmatter — the field declared in JSON wins.
- Quote the value when it contains characters with special meaning in YAML (e.g. `:`). For example, `path: '/foo:bar/'` instead of `path: /foo:bar/`.

Expand All @@ -132,6 +137,30 @@ Rules:

`outputPathField` is intentionally configurable so it cannot collide with a frontmatter key your project already uses for other purposes. Pick a name that doesn't conflict — common choices are `path`, `permalink`, `outputPath`, or a project-specific name. Without `outputPathField` set, the page compiler does **not** read frontmatter eagerly and **does not** treat any field as routing data.

### Handling Output-Path Conflicts

When two source files resolve to the same output path, `outputPathConflict` selects how the build reacts. The option lives on the compiler entry next to `outputPathField` and accepts three values:

| Value | Behavior |
| ----------- | ---------------------------------------------------------------- |
| `'error'` | Abort the build with a collision error pointing to both sources. |
| `'warning'` | Log a warning to `stderr` and keep one file. **Default.** |
| `'silent'` | Keep one file with no log output. |

```ts
def(createPageCompiler(), {
outputPathField: 'path',
outputPathConflict: 'error', // abort the build on any collision
});
```

When the policy is `'warning'` or `'silent'` and a winner must be picked:

1. A file whose `outputPath` came from the frontmatter override **beats** one using the default computed path.
2. Among ties (both override, or both default), the **first-seen** file wins.

This means a routing override always takes precedence over an accidental default-path collision, regardless of the order in which the files are processed.

## Transform Pipeline

The page compiler uses a Transform Pipeline to process HTML content after compilation. Each transform is an object with a `name` and `transform` function that receives content and contextual information.
Expand Down Expand Up @@ -207,11 +236,7 @@ The package provides **6 transform factory functions** (5 included in default pi
// ... other Prettier options
}
```
- `options.parseError`: Behavior when Prettier fails to parse the input (for example, when the HTML parser chokes on malformed markup). Defaults to `'silent'`.
- `'silent'` — swallow the error and emit the unformatted source as-is.
- `'warning'` — `console.warn` with a message prefixed by the source file path (`ctx.inputPath`, falling back to `ctx.outputPath`), then emit the unformatted source.
- `'error'` — throw an `Error` whose message is prefixed by the source file path. The underlying Prettier error is preserved on `error.cause`, so handlers can inspect `loc` and other Prettier-specific fields.
- **Top-level shortcut**: When using `createDefaultPageTransforms()`, pass `formatOptions.parseError` on `PageCompilerOptions` instead of constructing the transform yourself.
- **Errors**: This transform throws raw Prettier errors (typically `SyntaxError` for malformed input). Failures are handled at the pipeline level by `PageCompilerOptions.formatOptions.parseError` — see the [Options](#options) section.

5. **`minifier(options?)`** - Minify HTML
- `options.options`: html-minifier-terser configuration object
Expand All @@ -224,6 +249,7 @@ The package provides **6 transform factory functions** (5 included in default pi
// ... other minifier options
}
```
- **Errors**: This transform throws raw `html-minifier-terser` errors when the input cannot be parsed. Failures are handled at the pipeline level by `PageCompilerOptions.formatOptions.parseError` — see the [Options](#options) section.

6. **`lineBreak(options?)`** - Normalize line breaks
- `options.lineBreak`: Line break style
Expand Down
4 changes: 2 additions & 2 deletions packages/@kamado-io/page-compiler/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@kamado-io/page-compiler",
"version": "2.0.0-alpha.15",
"version": "2.0.0-alpha.16",
"description": "Page compiler for Kamado",
"repository": {
"type": "git",
Expand Down Expand Up @@ -83,7 +83,7 @@
"fast-glob": "3.3.3",
"html-minifier-terser": "7.2.0",
"image-size": "2.0.2",
"kamado": "2.0.0-alpha.15",
"kamado": "2.0.0-alpha.16",
"prettier": "3.8.1"
},
"devDependencies": {
Expand Down
Loading