feat: optimize the build pipeline with per-context caching and skip-unchanged writes#113
Merged
Conversation
…Options.parseError Move the parseError catch out of the prettier transform and into the page pipeline. The same policy now covers every transform — prettier, minifier, manipulateDOM, and any custom transforms supplied via the `transforms` option. Motivation: html-minifier-terser was throwing on malformed input with no source context, mirroring the original prettier problem. Adding a second per-transform parseError option would have meant duplicating the catch and giving users a way to set inconsistent policies. A single pipeline-level catch is simpler and more intuitive. Behavior on failure: - 'silent' (default): the failing transform is skipped, the previous step's output flows through to the next transform - 'warning': console.warn with `Transform '<name>' failed on <source>: <original>` then skip - 'error': throw an Error with the same message; original error preserved on `error.cause` BREAKING CHANGE: `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.
The orchestrator's output-path collision behavior is now configurable per compiler entry via `outputPathConflict: 'error' | 'warning' | 'silent'`, defaulting to `'warning'`. When a winner must be picked, a file whose outputPath came from the frontmatter override beats one using the default computed path; otherwise the first-seen file wins. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- add `yarn bench` script pointing to packages/kamado/benchmark - ignore generated benchmark fixtures (packages/**/.bench/) - document the bench command in CLAUDE.md
- clear module caches (asset group / file content / global data) at build start so consecutive builds in one process always reflect source edits - memoize getAssetGroup() by enumeration inputs so build() and getGlobalData() share a single glob + frontmatter pass; export clearAssetGroupCache from kamado/data - deduplicate output-directory mkdir calls within a build - add the skipUnchanged build option and --skip-unchanged CLI flag that skip rewriting outputs whose content is unchanged (stat size check first, then content comparison; mtime is preserved on a match) - add a build benchmark harness (yarn bench) and a CLI e2e test
…ntext BREAKING CHANGE: function forms of `compileHooks` and `transforms` are now resolved once per build/serve context instead of once per file. Resolved hooks and transform instances are shared by all pages of a context and may run concurrently, so factories must be file-independent and instances must not keep per-page mutable state. See MILESTONE.md for the migration guide. Details: - pass the compile cache flag through the transpile layer into compile hook compilers (CompilerFunction gains an optional 4th `cache` parameter) - hoist parseErrorMode resolution out of the per-file path - add specs for cache-flag plumbing and once-per-context hook resolution
- load the PostCSS config and construct plugins once per build context and reuse the processor across files; rebuild per compilation when cache=false so postcss.config.js edits apply during dev without restart - do not cache a failed processor build; the next compilation retries - warn when the PostCSS config fails to load for a reason other than not existing instead of silently dropping user plugins - resolve the banner once per context (recomputed when cache=false) - add style-compiler specs
- use esbuild write:false instead of a tmp-file round-trip - select the output file whose path matches the outfile instead of outputFiles[0]; warn about ignored extra outputs (e.g. extracted CSS) - resolve the banner once per context (recomputed when cache=false) - add script-compiler specs including a CSS-import case
- cache compiled template functions per compiler instance, keyed by the template source with a bounded LRU, so shared layouts compile once per build instead of once per page - honor the cache flag: false (serve mode) bypasses the cache so include and extends edits are always reflected per request - create a fresh compiler (and cache) per compile-hooks factory resolution, binding the cache lifetime to a single build context - add cache behavior specs and a real-FS build pipeline integration spec
- README (en/ja): add the --skip-unchanged build flag and a note on once-per-context resolution of compileHooks/transforms - ARCHITECTURE (en/ja): add the caching layers table, update the build flow diagram (cache clearing, skip-unchanged write), describe the cache-flag propagation, and document the yarn bench workflow
Add the v2.0.0 migration entry for the compileHooks/transforms resolution timing change (per file to per build/serve context) with before/after rewrite examples and guidance for affected factory patterns.
Resolve conflicts with the sourcemap-option feature (#112): - script/style compiler: combine per-context caching (banner/processor) with the per-command sourcemap flag derived from context.mode - style compiler: feed the normalized /*! banner through the cached processor with the inline source map option - specs: keep both the caching/retry suites and the sourcemap suites; default the postcss-load-config mock so the sourcemap tests stay green
- add clearBuildCaches() as the single entry point for resetting all module-level build caches; use it in build() and the benchmark runner - add createBannerResolver() and resolveSourcemapFlag() so asset compilers share the per-context banner caching and sourcemap-flag evaluation - export the SourcemapOption type from kamado/compiler - document the cache-flag contract: undefined means caching is enabled; test for serve mode with `cache === false`, never a truthiness check - defer output buffer allocation until needed (size precheck uses Buffer.byteLength; strings are written directly) - clamp benchmark --pages/--runs args to positive integers so invalid input falls back instead of reporting meaningless numbers
- replace local banner caching and sourcemap-flag evaluation with createBannerResolver and resolveSourcemapFlag from kamado/compiler - use the shared SourcemapOption type - unify the spec on a single test harness (makeContext/makeFile/ createCompileFn) instead of two parallel helper sets
Replace local banner caching and sourcemap-flag evaluation with createBannerResolver and resolveSourcemapFlag from kamado/compiler, and use the shared SourcemapOption type.
…gnals Clarify in ARCHITECTURE (en/ja) that build() leaves the cache flag undefined (caching enabled; compare with `cache === false`), and add a design note about the cache flag and context.mode being two parallel serve-mode signals to consolidate if a new mode is ever introduced.
- clear-build-caches.spec: wiring test asserting every registered cache clear is invoked — removing a clear call from clearBuildCaches fails - build.spec: files added between consecutive builds are picked up, guarding the asset-group cache clearing end-to-end - create-banner-resolver.spec: once-per-context resolution, cache=false re-resolution, string pass-through, and transform application - resolve-sourcemap-flag.spec: table-driven contract for all option/mode combinations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
3行まとめ
compileHooks/transformsを 関数で指定している場合、解決タイミングが「ファイルごと」→「build/serve コンテキストごとに1回」に変わります。createCompileHooks()を使っている場合は 影響なし。MILESTONE.mdを参照。変更点(パッケージ別)
getAssetGroup()のメモ化で二重 glob を解消 / mkdir の重複排除 /--skip-unchangedで未変更出力の write をスキップ(mtime 保持)compileHooks/transformsをコンテキストごと1回解決(上記の破壊的変更)。cacheフラグをコンパイルフックまで配管postcss.config.js編集を即反映。構築失敗は非キャッシュ(次回リトライ)write: falseでインメモリ化(tmp ファイル往復を排除)。出力は outfile パス一致で選択yarn bench(--pages/--runs/--full)ベンチマーク(中央値・ローカル計測)
テスト
yarn lint/yarn buildパス🤖 Generated with Claude Code