このページではHTMLを生成するJavaScript製テンプレートエンジンのPugについて解説します。
※Pugは元々Jadeという名称でしたが2016年3月、Pugという名称に変更されました。

目次

1. Pugとは
2. 導入方法
3. コンパイル
4. Pugの文法
5. 変数と制御文(ループ、条件分岐)
6. その他のコード

1. Pugとは

HTML、CSS、JavaScriptの拡張言語(メタ言語)の紹介

こちらで紹介している通り、HTMLを生成するためのテンプレートエンジンの一つです。元々はJadeという名称でしたが、商標権の都合上Pugへと名称変更がありました。

GitHubリポジトリ
https://github.com/pugjs/pug

公式ドキュメント
https://pugjs.org/api/getting-started.html

Pugはnode.js製のテンプレートエンジンですので、node.js環境が前提となります。
また、場合によってはgulpなどのタスクランナーも必須になるかと思います。

2. 導入方法

まずはnpmでグローバルにコマンドラインインターフェースをインストールします。

npm i pug-cli -g

基本的にはこれだけで、pugコマンドを使用してindex.pugなどのPugファイルをindex.htmlへと変換することができます。

3. コンパイル

適当な場所にindex.pugファイルを作り、以下のように記述してみましょう。

■Pug
doctype
html(lang='ja')
  head
    meta(charset='utf-8')
    style(src='css/style.css')
    title Hello, Pug world!

  body
    // HTMLに残るコメント
    //- HTMLに残らないコメント

    div.theme__yellow#wrapper
      h1.title Hello, Pug world!

      div.content
        h2 コンテンツタイトル
        p.text
          a.link(href='#') リンク
        p.image: img(
          src='http://placehold.jp/200x200.png'
          alt=''
        )
        p.text
          |本文テキスト
          br
          |本文テキスト

    script(src='js/app.js')

上記内容を記述したindex.pugファイルのあるディレクトリへ移動し、prettyオプション付きでpugコマンドを実行します。

pug index.pug --pretty

するとindex.pugファイルと同じディレクトリに以下と同じ内容のindex.htmlファイルが生成(コンパイル)されるかと思います。

■HTML
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <style src="css/style.css"></style>
    <title>Hello, Pug world!</title>
  </head>
  <body>
    <!-- -HTMLに残るコメント-->
    <div class="theme__yellow" id="wrapper"></div>
    <h1 class="title">Hello, Pug world!</h1>
    <div class="content">
      <h2>コンテンツタイトル</h2>
      <p class="text"><a class="link" href="#">リンク</a></p>
      <p class="image"><img src="http://placehold.jp/200x200.png" alt=""></p>
      <p class="text">本文テキスト<br>本文テキスト</p>
    </div>
    <script src="js/app.js"></script>
  </body>
</html>

prettyオプションをつけずにpugコマンドを実行した場合、開業やインデントが取り除かれて、一行に圧縮された状態でコンパイルされます。

pug index.pug

4. Pugの文法

では、PugからHTMLへどのように変換されていったか、上から順に見ていきましょう。

DOCTYPE宣言

一行目の

doctype

でDOCTYPE宣言を記述します。
デフォルトではHTML5の宣言になりますが、以下のように宣言型を指定することができます。

doctype xml
doctype transitional
doctype strict

要素の書き方とブロック構造

PugではHTMLのように要素名を<>で区切る必要がありません。一つの行にの先頭に記述される文字列が、要素名となります。
例えば<div>であればdivと書くだけでいいのです。

Pugではブロックをインデントで表現します。インデントはタブでもスペースでも構いませんし、スペースの個数も任意で問題ないですが、ブロック構造が破綻しないようにインデントの数をきちんと揃えてあげる必要があります。

例では

doctype
html(lang='ja')
  head
  // 中略
  // ・
  // ・
  // ・

  body
  // 中略
  // ・
  // ・
  // ・

と、スペース2つでheadとbodyのブロックを作っています。
HTMLと違い閉じタグを記述する必要がないため、タグの閉じ忘れによるレイアウト崩れを防ぎつつ、インデント区切りにすることでブロック構造がぱっと見でわかりやすくなっています。Pythonなどではおなじみですね。

要素の属性

html要素やmeta要素などで頻出していますが、属性は半角カッコ内に記述します。

html(lang='ja')

複数の属性を記述する場合、スペースか改行で区切ります。

img(
  src='http://placehold.jp/200x200.png'
  alt=''
)

文字列

タグの中に文字列を記述する場合は、要素名の後ろにそのまま記述します。

title Hello, Pug world!

複数行の文字列は、以下のように|(バーティカルバー)を使用します。
|から始まる行はHTMLタグで囲まれない、プレーンな文字列を意味します。

p.text
  |本文テキスト
  br
  |本文テキスト

また、文字列が長くなりすぎるためコード内で改行を入れる場合、ブロック宣言の最後に.(ピリオド)を使用します。
.でブロッキングされた場合、その中は全てプレーンな文字列とみなされます。タグを入れたい場合はHTMLのように<>で表現する必要があります。

p.text.
  本文テキスト
  <br>
  本文テキスト
  本文テキスト
  本文テキスト

先ほどと、改行の表現の仕方が違うことに注意してください。
厳密にいうと、|から始まる行はタグではなくプレーンな文字列を意味しており、

コメントアウト

Pugのコメントアウトには////-の2種類あります。その違いはコンパイルされた時にHTML内に記述を残すか、残さないかになります。

// HTMLに残るコメント
//- HTMLに残らないコメント

複数行のコメントは、インデントによりブロッキングすることで表現できます。

//
  HTMLに残る複数行のコメント
  HTMLに残る複数行のコメント

//-
  HTMLに残らない複数行のコメント
  HTMLに残らない複数行のコメント

クラスとID

PugではCSSセレクタと同様の記法でクラスとIDを指定することができます。

■クラスの指定
div.theme__yellow
■IDの指定
div#wrapper

それぞれの指定に順序はありませんし、クラスの間にIDを挟み込むことも可能です。

div.theme__yellow#wrapper.only__pc

また、属性との順序にも決まりはありません。

a.link(href='#') リンク
a(href='#').link リンク

クラスや属性の識別子のみ記述した場合、div要素としてコンパイルされます。

■Pug
.content
■HTML
<div class="content"></div>

5. 変数と制御文(ループ、条件分岐)

ここまでは単にHTMLの記述をPugの記法に書き換えただけでした。もちろんそれだけでも、<>の分のタイピング量が減ったかとは思いますが、テンプレートエンジンを使用する真のメリットはここからになります。

まずはpugファイルを作成し、以下の内容を記述します。

■Pug
- var title = 'Hello, Pug world!'
- var wrapperClass = 'theme__yellow'

doctype
html(lang='ja')
  head
    meta(charset='utf-8')
    style(src='css/style.css')
    title #{title}

  body
    // HTMLに残るコメント
    //- HTMLに残らないコメント

    div#wrapper(class=wrapperClass)
      h1.title=title

      .content
        h2 コンテンツタイトル

        - for (var i = 0; i < 3; i++)
          if i === 0
            p.text
              a.link(href='#') リンク

          else if i === 1
            p.image: img(
              src='http://placehold.jp/200x200.png'
              alt=''
            )

          else
            p.text
              |本文テキスト
              br
              |本文テキスト

    script(src='js/app.js')

こちらを–pretyオプション付きでコンパイルすると以下のようなHTMLへ変換されるかと思います。

■HTML
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <style src="css/style.css"></style>
    <title>Hello, Pug world!</title>
  </head>
  <body>
    <!-- HTMLに残るコメント-->
    <div class="theme__yellow" id="wrapper">
      <h1 class="title">Hello, Pug world!</h1>
      <div class="content">
        <h2>コンテンツタイトル</h2>
        <p class="text"><a class="link" href="#">リンク</a></p>
        <p class="image"><img src="http://placehold.jp/200x200.png" alt=""></p>
        <p class="text">本文テキスト<br>本文テキスト</p>
      </div>
    </div>
    <script src="js/app.js"></script>
  </body>
</html>

変数

Pugファイル内で変数を扱う場合は以下のように宣言します。

- var title = 'Hello, Pug world!'

半角ハイフン・半角スペースの後に、JavaScriptと同じように変数を宣言します。プレーンなJavaScriptと異なり、行末のセミコロンはあってもなくても構いません。
宣言した変数は半角シャープと波括弧で、以下のように展開することができます。

title #{title}

もしくは、以下のように表現することも可能です。

h1.title=title

また、クラス名などの属性値として変数を使用する場合は以下のようにそのまま記述だけで展開されます。

■Pug
- var wrapperClass = 'theme__yellow'
div#wrapper(class=wrapperClass)
■HTML
<div class="theme__yellow" id="wrapper"></div>

この時、以下のように三項演算子を用いることもできます。

■Pug
div#wrapper(class=wrapperClass ? wrapperClass : 'theme__default')

クラスやインラインスタイルを複数指定する場合、以下のように配列やオブジェクトで指定することが可能です。

■HTML
- var wrapperClass = ['theme__yellow', 'only__pc']
- var wrapperStyle = {color: '#333', backgroundColor: '#EEE'}

div#wrapper(class=wrapperClass style=wrapperStyle)
■Pug
<div class="theme__yellow only__pc" id="wrapper"></div>

ループ

forループ

まず、forループを使うには、変数宣言の時と同じようにfor文の前に半角ハイフンを記述します。

■Pug
- for (var i = 0; i < 3; i++)
  p 本文テキスト
■HTML
<p>本文テキスト</p>
<p>本文テキスト</p>
<p>本文テキスト</p>

each in・for inループ

PugではJavaScriptのforEachと同様の機能の、each infor inループが用意されています。こちらは半角ハイフン・半角スペースなしで使用することが可能です。Pugではeach inループもfor inループも本質的には変わりません。

■Pug
- var list = ['link', 'image', 'text']

each item, index in list
  p #{item + index}
- var list = ['link', 'image', 'text']

for item, index in list
  p #{item + index}
■HTML
<p>link0</p>
<p>image1</p>
<p>text2</p>

whileループ

whileループも以下のように用意されています。

■Pug
- var n = 0;

while n < 4
  p #{n}
  - n++
■HTML
<p>0</p>
<p>1</p>
<p>2</p>
<p>3</p>

条件分岐

ループと条件分岐を組み合わせることで、表現力がさらに広がります。
ifによる条件分岐は以下の通りです。

- for (var i = 0; i < 3; i++)
  if i === 0
    p.text
      a.link(href='#') リンク

  else if i === 1
    p.image: img(
      src='http://placehold.jp/200x200.png'
      alt=''
    )

  else
    p.text
      |本文テキスト
      br
      |本文テキスト

通常のJavaScriptと違い、評価式の括弧を省略することができます。括弧はつけていても構いません。
また、PugではJavaScriptのswitchと同様の機能の、case文が用意されています。

- var list = ['link', 'image', 'text']

each item in list
  case item
    when 'link'
      p.text
        a.link(href='#') リンク

    when 'image'
      p.image: img(
        src='http://placehold.jp/200x200.png'
        alt=''
      )

    default
      p.text
        |本文テキスト
        br
        |本文テキスト

先に示した例を、eachとcaseに直した場合

- var title = 'Hello, Pug world!'
- var wrapperClass = 'theme__yellow'
- var list = ['link', 'image', 'text']

doctype
html(lang='ja')
  head
    meta(charset='utf-8')
    style(src='css/style.css')
    title #{title}

  body
    // HTMLに残るコメント
    //- HTMLに残らないコメント

    div#wrapper(class=wrapperClass)
      h1.title=title

      .content
        h2 コンテンツタイトル

        each item in list
          case item
            when 'link'
              p.text
                a.link(href='#') リンク

            when 'image'
              p.image: img(
                src='http://placehold.jp/200x200.png'
                alt=''
              )

            default
              p.text
                |本文テキスト
                br
                |本文テキスト

    script(src='js/app.js')

となります。

6. その他のコード

Pug内では、-(半角ハイフン・半角スペース)で始まる行は全てJavaScriptコードとみなされます。
複数行のコードを記述したい場合は、以下のようにブロッキングします。

■Pug
-
  var datetime = new Date()
  var year = datetime.getFullYear()
  var month = datetime.getMonth() + 1
  var date = datetime.getDate()

  var format = year + '/' + month + '/' + date

p #{format}
■HTML(実際にはコンパイル時の日付になります)
<p>2017/1/8</p>

ここまでで、-で始まらない制御文もいくつか紹介してきました。それらはJavaScriptの制御文ではなくPugの制御文です。
一方で、Pugでは用意されていないforなどの制御文は、-でJavaScriptのコードであることを明示して使用する必要があります。

以上、Pugの基本的な使用方法と、JavaScriptならではの制御文を紹介してきました。次回は複数ファイルのインクルードや拡張(エクステンド)について紹介したいと思います。

Pug(旧称Jade)のインクルードとテンプレート継承、ミックスインの使用方法