CLI tool for generating PDFs from training materials with multi-variant support
npm install md2pdf-training-system研修教材のMarkdownファイルからPDFを生成するCLIツール。複数バリアント対応、テンプレート変数展開、プレビュー機能を備えています。
- ✅ マルチバリアント対応: フレームワーク、環境、解答有無などの複数の軸でPDFを生成
- ✅ 3段階優先度解決: Combination > Dimension > Base の優先順位でファイルを解決
- ✅ インラインバリアント: {{variant:framework=spring}}...{{end}} タグで条件付きコンテンツ
- ✅ PlantUMLダイアグラム: `plantuml コードブロックを自動的にSVGに変換
- ✅ テンプレート変数展開: {courseId}, {framework}, {format} などの変数をサポート
- ✅ 複数フォーマット: PDF/HTML/両方の出力に対応
- ✅ プレビューモード: ライブリロード付きHTTPサーバー
- ✅ 設定バリデーション: YAML設定ファイルの自動検証
- ✅ Docker対応: CI/CD統合のためのコンテナ化
``bash`
cd md2pdf
pnpm install
pnpm build
`bashイメージをビルド
./docker-build.sh
使い方
$3
`bash
PDF生成
md2pdf generate -c config.yaml特定のタイプのみ生成
md2pdf generate -c config.yaml -t text特定のバリアントのみ生成
md2pdf generate -c config.yaml -v "framework=spring"設定ファイルの検証
md2pdf validate -c config.yamlプレビューモード
md2pdf preview -c config.yamlシンプルモード(設定ファイルなし)
md2pdf generate -i ./testdoc --output ./sample-pdf --title 'サンプル'
`$3
#### 画像最適化
`bash
docker run --rm \
-v $(pwd):/workspace \
-e IMAGE_QUALITY=80 \
md2pdf:latest optimize-images \
--course-dir /workspace \
--output /workspace/.tmp-optimized-images \
--mapping-output /workspace/optimized-images-map.json
`#### 全バリアントPDF生成
`bash
docker run --rm \
-v $(pwd):/workspace \
md2pdf:latest generate-all-pdfs \
--config /workspace/md2pdf-config.yaml \
--course-dir /workspace \
--output /workspace/.tmp-pdf \
--use-optimized-images /workspace/.tmp-optimized-images
`#### 環境変数
| 変数名 | 説明 | デフォルト |
|--------|------|-----------|
|
IMAGE_QUALITY | JPEG品質 (1-100) | 60 |
| PDF_QUALITY | PDF品質 | - |
| VIVLIOSTYLE_TIMEOUT | Vivliostyleタイムアウト秒数 | 120 |$3
設定ファイルは任意の名前(例:
config.yaml, materials.yaml)で作成できます。設定ファイルのパスは-c, --configオプションで指定します。#### 設定ファイルの例
`yaml
globals:
specFile: specs/spec.json # コース仕様ファイル(相対パス)
theme: default # デフォルトテーマ
cleanup: true # 生成後の一時ファイル削除materials:
text:
inputDir: text # 入力ディレクトリ(相対パス)
outDir: out/text # 出力ディレクトリ(相対パス)
sectionNumber: true # セクション番号を付与
tocLevel: 2 # 目次の深さ
variants:
- selection:
framework: spring
outputPattern: "text-{framework}.pdf"
- selection:
framework: express
outputPattern: "text-{framework}.pdf"
slides:
inputDir: slides
outDir: out/slides
format: both # pdf, html, or both
theme: gaia
variants:
- selection:
framework: spring
outputPattern: "slides-{framework}.{format}"
preview:
autoOpenBrowser: true
portRange: [3000, 3100]
`$3
コースのメタデータとバリアント定義は
spec.jsonに一元化されています:`json
{
"course_id": "ddd-tactical-design",
"training_name": "DDD戦術設計",
"training_description": "ドメイン駆動設計の戦術パターンを学ぶ",
"variants": {
"dimensions": [
{
"id": "framework",
"name": "フレームワーク",
"options": [
{
"id": "java-spring",
"name": "Java + Spring Boot"
},
{
"id": "typescript-nestjs",
"name": "TypeScript + NestJS"
}
]
}
],
"defaultSelections": {
"framework": "java-spring"
}
}
}
`注意: バリアント定義は
spec.jsonのvariantsフィールドに含まれます。variants.jsonは使用されません。ディレクトリ構造
`
training-materials/
└── ddd-tactical-design/
├── config.yaml # 設定ファイル
├── specs/
│ └── spec.json # コースメタデータとバリアント定義
├── text/
│ ├── chapter-01.md # ベースファイル
│ └── _variants/
│ ├── framework=spring/
│ │ └── chapter-01.md # Dimension variant
│ └── framework=spring,answer=yes/
│ └── chapter-01.md # Combination variant
└── out/
└── text/
├── text-spring.pdf
└── text-express.pdf
`バリアント解決の優先順位
1. Combination (複数ディメンション):
_variants/framework=spring,answer=yes/chapter-01.md
2. Dimension (単一ディメンション): _variants/framework=spring/chapter-01.md
3. Base (デフォルト): chapter-01.mdインラインバリアントタグ
Markdownファイル内で条件付きコンテンツを記述できます:
`markdown
Chapter 1
{{variant:framework=spring}}
Spring Framework での実装
このセクションはSpringを選択した場合のみ表示されます。
{{end}}
{{variant:framework=express}}
Express での実装
このセクションはExpressを選択した場合のみ表示されます。
{{end}}
共通セクション
このセクションは常に表示されます。
`テンプレート変数
出力パターンで使用できる変数:
-
{courseId} - コースID (spec.jsonのcourse_id)
- {type} - マテリアルタイプ (text, practice, quiz, slides)
- {framework} - フレームワーク (spring, express, etc.)
- {answer} - 解答表示 (yes, no)
- {format} - 出力フォーマット (pdf, html){format} が both の場合、配列として展開されます:
`
slides-{courseId}.{format}
→ ["slides-ddd.pdf", "slides-ddd.html"]
`開発
$3
`bash
全テスト実行
pnpm test特定のテスト実行
pnpm test ConfigLoader
pnpm test Strategy
pnpm test e2eカバレッジ確認
pnpm test:coverageウォッチモード
pnpm test:watchUIモード
pnpm test:ui
`$3
`bash
pnpm type-check
`$3
`bash
pnpm build
`テーマシステム
md2pdfは、プロジェクトルートの
themes/ディレクトリからテーマを読み込みます。テーマはエンジン(vivliostyle/marp/web)ごとに管理され、各エンジンごとに独立したテーマディレクトリを持ちます。$3
`
themes/
├── vivliostyle/ # PDF生成用(Vivliostyle)
│ ├── default/ # デフォルトテーマ
│ │ ├── index.html
│ │ ├── page.html
│ │ ├── postscript.html
│ │ ├── style.css
│ │ └── scss/ # SCSS ソース
│ └── quiz/ # クイズ専用テーマ
│ └── ...
├── marp/ # スライド生成用(Marp)
│ ├── default/ # デフォルトテーマ
│ │ └── theme.css # Marpテーマファイル
│ └── [custom]/ # カスタムテーマ
│ └── theme.css
└── web/ # Web素材生成用
└── default/
├── templates/
├── css/
└── js/
`$3
テーマは以下の優先順位で決定されます:
1. CLIオプション (
--theme) - 最優先
2. Material設定 (materials.{type}.theme) - 各materialタイプごとの設定
3. グローバル設定 (globals.theme) - デフォルト値`yaml
globals:
theme: default # グローバルなデフォルトテーマmaterials:
text:
theme: custom # textタイプのみcustomテーマを使用
slides:
theme: gaia # slidesタイプのみgaiaテーマを使用
# themeが指定されない場合はglobals.themeが使用される
`$3
Vivliostyleテーマは、PDF生成時に使用されるテーマです。
`
themes/vivliostyle/
├── default/ # デフォルトテーマ
│ ├── index.html # 表紙・目次ページ
│ ├── page.html # 本文ページ
│ ├── postscript.html # 奥付ページ
│ ├── style.css # コンパイル済みCSS
│ └── scss/ # SCSS ソース
└── quiz/ # クイズ専用テーマ
└── ...
`#### 新しいVivliostyleテーマの追加方法
1. テーマディレクトリを作成:
themes/vivliostyle/my-theme/
2. 必要なファイルを配置:
- index.html - 表紙・目次ページテンプレート
- page.html - 本文ページテンプレート
- postscript.html - 奥付ページテンプレート(オプション)
- style.css - スタイルシート
3. 設定ファイルで theme: my-theme を指定`yaml
globals:
theme: my-theme # themes/vivliostyle/my-theme/ が使用されるまたは、materialごとに指定
materials:
text:
theme: my-theme
`$3
Marpテーマは、スライド生成時に使用されるテーマです。
`
themes/marp/
├── default/ # デフォルトテーマ
│ └── theme.css # Marpテーマファイル
└── [custom]/ # カスタムテーマ
└── theme.css
`#### 新しいMarpテーマの追加方法
1. テーマディレクトリを作成:
themes/marp/my-theme/
2. theme.css を配置し、@theme my-theme を定義
3. 設定ファイルで theme: my-theme を指定`yaml
slides:
theme: my-theme # themes/marp/my-theme/theme.css が使用される
`#### Marpエンジン設定
全テーマ共通の設定は
config/marp-engine.mjs で管理されます:
- HTML タグの有効化 (html: true)
- コードハイライト (markdown-it-highlight-lines)$3
Webテーマは、Web素材生成時に使用されるテーマです。
`
themes/web/
└── default/
├── templates/
│ └── page.html # ページテンプレート
├── css/
│ ├── theme.css
│ ├── theme-dark.css
│ └── highlight-*.css
└── js/
├── main.js
├── dark-mode.js
└── search.js
`#### 新しいWebテーマの追加方法
1. テーマディレクトリを作成:
themes/web/my-theme/
2. 必要なファイルを配置:
- templates/page.html - ページテンプレート
- css/ - スタイルシート
- js/ - JavaScriptファイル(オプション)
3. 設定ファイルで theme: my-theme を指定`yaml
materials:
text:
format: web
theme: my-theme # themes/web/my-theme/ が使用される
`$3
指定されたテーマが見つからない場合、以下のフォールバック動作が行われます:
1. Vivliostyle/Web:
defaultテーマにフォールバック
2. Marp: エラーが発生(Marpはフォールバック機能なし)$3
シンプルモードでは、
--themeオプションでテーマを指定できます(実装予定):`bash
md2pdf generate -i ./docs --output ./docs.pdf --theme custom
`アーキテクチャ
md2pdfは4層アーキテクチャ(ヘキサゴナルアーキテクチャ)を採用しています:
`
src/
├── cli/ # CLI層(Primary Adapter)
│ ├── index.ts # CLIエントリーポイント
│ └── commands/
│ ├── generate.ts # PDF生成コマンド(レガシー)
│ ├── ConfigBasedGenerateCommand.ts # 設定ファイルベース生成
│ ├── SimpleGenerateCommand.ts # シンプルモード生成
│ ├── GenerateCommandFactory.ts # コマンドファクトリ
│ ├── PreviewCommand.ts # プレビューコマンド
│ ├── validate.ts # 設定検証コマンド
│ └── optimize-images.ts # 画像最適化コマンド
├── application/ # アプリケーション層(Use Cases)
│ └── services/
│ ├── ConfigService.ts # 設定管理サービス
│ ├── FileProcessingService.ts # ファイル処理サービス
│ ├── PreviewService.ts # プレビューサービス
│ └── ImageService.ts # 画像処理サービス
├── domain/ # ドメイン層(Business Logic)
│ ├── config/ # 設定管理
│ │ ├── ConfigLoader.ts
│ │ ├── SimpleConfigLoader.ts
│ │ └── ConfigValidator.ts
│ ├── generator/ # PDF生成(Strategy Pattern)
│ │ └── strategies/
│ │ ├── TextStrategy.ts
│ │ ├── PracticeStrategy.ts
│ │ ├── QuizStrategy.ts
│ │ └── SlidesStrategy.ts
│ ├── strategy/ # 戦略ファクトリ
│ │ └── StrategyFactory.ts
│ ├── markdown/ # Markdown処理
│ ├── html/ # HTML生成
│ ├── publication/ # publication.json生成
│ ├── variant/ # バリアント解決
│ │ ├── VariantResolver.ts
│ │ └── VariantProcessor.ts
│ ├── template/ # テンプレート変数展開
│ ├── preview/ # プレビューシステム
│ └── web/ # Web素材生成
├── infrastructure/ # インフラ層(Secondary Adapter)
│ └── adapters/
│ ├── VivliostyleAdapter.ts # Vivliostyle連携
│ └── MarpAdapter.ts # Marp連携
├── presentation/ # プレゼンテーション層
│ └── themes/
│ └── ThemeManager.ts # テーマ管理
├── di/ # 依存性注入
│ ├── container.ts # DIコンテナ
│ └── types.ts # サービス識別子
└── types/ # 型定義(インターフェース)
├── cli/
├── application/
├── domain/
└── infrastructure/
``- CLAUDE.md - AI開発ガイド
- CHANGES.md - 設計変更ログ
- 仕様書 - アーキテクチャ仕様書
- 現状分析 - 過去の実装分析(アーカイブ)
- 設計仕様 - 理想的なアーキテクチャ設計
- 実装計画 - アーキテクチャ改善タスクリスト