npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

md2pdf-training-system

v0.3.5

Published

CLI tool for generating PDFs from training materials with multi-variant support

Readme

md2pdf - Training Materials PDF Generator

研修教材の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統合のためのコンテナ化

インストール

ローカル開発

cd md2pdf
pnpm install
pnpm build

Docker

# イメージをビルド
./docker-build.sh

# または手動で
docker build -t md2pdf:latest .

使い方

ローカル実行

# 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 'サンプル'

Docker実行(CI/CD統合用)

画像最適化

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生成

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 |

設定ファイル

設定ファイルは任意の名前(例: config.yaml, materials.yaml)で作成できます。設定ファイルのパスは-c, --configオプションで指定します。

設定ファイルの例

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]

spec.json (メタデータとバリアント定義)

コースのメタデータとバリアント定義はspec.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.jsonvariantsフィールドに含まれます。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ファイル内で条件付きコンテンツを記述できます:

# 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"]

開発

テスト実行

# 全テスト実行
pnpm test

# 特定のテスト実行
pnpm test ConfigLoader
pnpm test Strategy
pnpm test e2e

# カバレッジ確認
pnpm test:coverage

# ウォッチモード
pnpm test:watch

# UIモード
pnpm test:ui

型チェック

pnpm type-check

ビルド

pnpm build

テーマシステム

md2pdfは、プロジェクトルートのthemes/ディレクトリからテーマを読み込みます。テーマはエンジン(vivliostyle/marp/web)ごとに管理され、各エンジンごとに独立したテーマディレクトリを持ちます。

テーマのディレクトリ構造

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/

テーマの優先順位

テーマは以下の優先順位で決定されます:

  1. CLIオプション (--theme) - 最優先
  2. Material設定 (materials.{type}.theme) - 各materialタイプごとの設定
  3. グローバル設定 (globals.theme) - デフォルト値
globals:
  theme: default          # グローバルなデフォルトテーマ

materials:
  text:
    theme: custom         # textタイプのみcustomテーマを使用
  slides:
    theme: gaia          # slidesタイプのみgaiaテーマを使用
    # themeが指定されない場合はglobals.themeが使用される

Vivliostyleテーマ (テキスト/演習/クイズ)

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 を指定
globals:
  theme: my-theme  # themes/vivliostyle/my-theme/ が使用される

# または、materialごとに指定
materials:
  text:
    theme: my-theme

Marpテーマ (スライド)

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 を指定
slides:
  theme: my-theme  # themes/marp/my-theme/theme.css が使用される

Marpエンジン設定

全テーマ共通の設定は config/marp-engine.mjs で管理されます:

  • HTML タグの有効化 (html: true)
  • コードハイライト (markdown-it-highlight-lines)

Webテーマ (Web素材生成)

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 を指定
materials:
  text:
    format: web
    theme: my-theme  # themes/web/my-theme/ が使用される

テーマのフォールバック動作

指定されたテーマが見つからない場合、以下のフォールバック動作が行われます:

  1. Vivliostyle/Web: defaultテーマにフォールバック
  2. Marp: エラーが発生(Marpはフォールバック機能なし)

CLIオプションでのテーマ指定

シンプルモードでは、--themeオプションでテーマを指定できます(実装予定):

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/

関連ドキュメント