Showing Posts From

Pug

Pugの文法まとめ

Pugの文法まとめ

Pug の文法について網羅的に説明された日本語のサイトが見つからなかったので、自分でまとめてみました。 英語でよければ公式にしっかりしたドキュメントがあります。 この記事はその内容の中でも特に文法に絞って、重要な順番で解説しています。 できるだけリファレンスを網羅している長い記事ですが、大事な順に書いているので、単に HTML を簡潔に書くためだけなら、最初の方の「HTML を簡潔に書くための基本文法」だけ読んでおけば大丈夫です。 目次 Pug って何? HTML を簡潔に書くことができるものです。HTML に変換されます。昔は Jade と呼ばれていましたが名前が変更されました。 単に HTML を簡潔に書けるだけでなく、テンプレートエンジン的な機能も持っています。他のファイルを include する レイアウトを他のファイルに書いて extends して内容を埋め込む 変数を定義する ループしたり条件分岐したり Markdown ファイルを HTML に変換して埋め込んだり、Sass を CSS に変換して埋め込んだりといったことができます。 HTML を簡潔に書くための基本文法 まずはテンプレートエンジン的な機能は置いておいて、HTML を簡潔に書く記法について解説します。 タグ 以下の HTML が Pug でどうなるか見てみましょう。 <main> <article> <header> <h1>記事タイトル</h1> </header> <section> <p>記事サンプルです。</p> </section> </article> </main>Pug は以下のようになります。 main article header h1 記事タイトル section p 記事サンプルです。タグを閉じるのではなく、インデント(行の先頭から続く空白の数)でタグの入れ子構造を表します。 doctype は以下のように記述します。 doctype html html head body↓ HTML に変換すると... <!DOCTYPE html> <html> <head></head> <body></body> </html>属性 以下のように属性があるタグをどう書くかの基本を解説します。 基本 <a id="google-link" class="button small" href="//google.com">Google</a>タグの属性は括弧の中に入れて表します。(文字列を囲うのはシングルクォーテーション、ダブルクォーテーションのどちらも使えます) a(id='google-link' class='button small' href='//google.com') Google属性と属性の間にコンマを入れても構いません。 a(id='google-link', class='button small', href='//google.com') Google属性を複数行に分けて書くこともできます。 a( id='google-link' class='button small' href='//google.com' ) Googleid の # 記法 id は特別に # を使った記法が使えます。 a#google-link(class='button small' href='//google.com') Googleなお、div タグの場合はタグ名を省略できます。 #link a#google-link(class='button small' href='//google.com') Google↓ <div id="link"> <a id="google-link" class="button small" href="//google.com">Google</a> </div>class の . 記法 クラスにも特別に . を使った記法が用意されています。複数つなげて指定できます。 a#google-link.button.small(href='//google.com') Googleこちらも id と同様に div を省略できます。 .link a#google-link.button.small(href='//google.com') Google↓ <div class="link"> <a id="google-link" class="button small" href="//google.com">Google</a> </div>style 属性 タグに直接スタイルを指定するときは以下のように書けます。 a(style={color: 'red', background: 'green'})Boolean 属性 真偽値を表す属性は true か false を受け付けます。値を指定しなければ true だとみなされます。 input(type='checkbox' checked)input(type='checkbox' checked=true)input(type='checkbox' checked=false)false を指定した場合、Pug はその属性を出力しません。 true の場合、Pug はその文書の doctype が HTML かどうかで適切に出力します。 出力の詳しい動作については後述する Boolean 属性の出力 を参照。 文字列(Plain Text) タグの後に続けて中身の文字列を記述することができます。 p 記事サンプルです。複数行に分けて書きたい場合は、パイプ( | ) を利用した記法があります。 p | これはサンプルです。 | Pugのパイプを使って複数行に分けて記述しています。これは以下の HTML になります。パイプを使って複数行に分けて書いたものは、まとめてタグの中身になります。 <p>これはサンプルです。Pugのパイプを使って複数行に分けて記述しています。</p>パイプを使うと、文章中にインラインタグを埋め込むのは以下のように書けます(後述の もうちょっと高度な話 で説明する #[] を使って埋め込むこともできます。)。 p | 文章の中に span.keyword インラインタグ | を埋め込むことができます。 br | 改行タグも埋め込めます。↓ <p> 文章の中に<span class="keyword">インラインタグ</span>を埋め込むことができます。<br /> 改行タグも埋め込めます。 </p>コメント HTML として出力されるコメントは以下のように書きます。 // これはHTMLに出力されるコメントです。 p foo以下のように HTML のコメントとして出力されます。 <!-- これはHTMLに出力されるコメントです。 --> <p>foo</p>HTML に出力して欲しくないコメントは以下のようにハイフンを追加して書きます。 //- これはHTMLに出力されないコメントです。 p foo複数行にわたるブロックコメントも記載できます。 body //- これはHTMLには出力されないコメントです。 複数行に渡るブロックコメントです。 // これはHTMLに出力されるコメントです。 複数行に渡るブロックコメントです。テンプレート構文 単に HTML を Pug でシンプルに記述したいだけであれば、覚えることは今まで説明しただけで事足ります。 他のテンプレート構文と組み合わせて使うなら、これから説明する内容は覚えなくてもいい・・・というより、混ぜると訳がわからなくなるので Pug のテンプレート構文は使うべきではないでしょう。 他のテンプレート構文と組み合わせて使うというのは、例えば Laravel のテンプレートエンジン Blade と組み合わせる とか、React で利用する とか Vue.js で利用する とか、Angular で利用する とかを指しています [^2]。この場合は、後述の もうちょっと高度な話 のうち、HTML 記述に関係するところには目を通しておくと良いかと思います。 [^2]: React をテンプレート構文と呼ぶのは違和感ありますが Pug の公式では変数の定義に var キーワードを使って説明されています。古いブラウザ(IE10 以下とか)でも動くようにという配慮からだと思いますが、もう var は使わず let や const を使うべきかと。少なくとも Node.js 上で Pug を HTML に変換するのであればブラウザの対応は関係ありません。ここでは let や const で説明します。 JavaScript コード ここで説明するのは HTML に含めるブラウザで動作させる JavaScript のことではなく、Pug が変換時に実行する JavaScript のことです。 Pug では HTML への変換時に JavaScript を実行する機能が用意されています。とはいえ、Node.js パッケージを読み込んで利用するような高度なことはできません。 Unbuffered Code 頭に -(ハイフン)を付ける記述方法で、HTML タグとは無関係に JavaScript コードを実行します。 Pug では HTML として出力されるものを buffered, 出力されないものを unbuffered と呼んでいます。 最もよく利用するのは変数の定義でしょう。 - const list = ["Uno", "Dos", "Tres", "Cuatro", "Cinco", "Seis"]このように - (ハイフン)を頭に付けると JavaScript コードと認識されます。 以下のように複数行に分けて記述することもできます。 - const list = ["Uno", "Dos", "Tres", "Cuatro", "Cinco", "Seis"]Buffered Code タグの中身に JavaScript コードの実行結果を埋め込みます。 //- イコール(=)を使ってタグの中身に JavaScript の結果を埋め込める p = 'This code is' + ' <escaped>' //- 1行で書くこともできる p= 'This code is' + ' <escaped>'タグの内容は HTML エスケープされます。 <p>This code is &lt;escaped&gt;!</p> <p>This code is &lt;escaped&gt;!</p>エスケープして欲しくない場合は = ではなく != を使います。次の「変数の埋め込み」で説明します。 変数の埋め込み エスケープする場合 #{変数名} で変数の内容を文字列に埋め込むことができます。また先ほど説明した Buffered Code を使って h1= 変数名 とか p= 変数名 の形でタグの中に変数の内容を埋め込めます。内容が埋め込まれる前に HTML はエスケープされます。 - const title = "On Dogs: Man's Best Friend"; - const author = "enlore"; - const theGreat = "<span>escape!</span>";h1= title p Written with love by #{author} p This will be safe: #{theGreat}出力される HTML は以下になります。 <h1>On Dogs: Man's Best Friend</h1> <p>Written with love by enlore</p> <p>This will be safe: &lt;span&gt;escape!&lt;/span&gt;</p>変数の埋め込みと紹介していますが、もちろん JavaScript のコードを実行して結果を埋め込むこともできます。 h1= title.toUpperCase() p Written with love by #{author.toUpperCase()}#{ 自体を文字列に含めたい場合は、\#{ のようにスラッシュでエスケープするか、#{} の中に入れて JavaScript の文字列として評価させる方法が取れます。 p Escaping works with \#{interpolation} p Interpolation works with #{'#{interpolation}'} too!以下の HTML に変換されます。 <p>Escaping works with #{interpolation}</p> <p>Interpolation works with #{interpolation} too!</p>エスケープしない場合 埋め込む文字列をエスケープしたくない場合は != や !{} を利用します。 当然ですがユーザーの入力をそのまま埋め込むのは危険です。 - const title = "On Dogs: <span class=\"subtitle\">Man's Best Friend</span>" - const theGreat = "<span>escape!</span>"; title!= title p This will be unsafe: !{theGreat}以下のように変数内の HTML タグがそのまま出力されます。 <h1>On Dogs: <span class="subtitle">Man's Best Friend</span></h1> <p>This will be safe: <span>escape!</span></p>条件分岐 if まずは if 文から。 - const user = {description: 'foo bar baz'} - const authorised = false #user if user.description h2.green Description p.description= user.description else if authorised h2.blue Description p.description. User has no description, why not add one... else h2.red Description p.description User has no descriptionunless if の逆の「もし〜でなければ」を表す unless が用意されています。 unless user.isAnonymous p You're logged in as #{user.name}これは if を使って以下を書いた場合と同じになります。 if !user.isAnonymous p You're logged in as #{user.name}case JavaScript では switch 文に相当するものですね。 - const friends = 10 case friends when 0 p you have no friends when 1 p you have a friend default p you have #{friends} friendsbreak は書かなくても勝手に下まで実行していったりしません。もし複数の条件で同じ内容を実行したい場合は以下のように記述できます。 - const friends = 0 case friends when 0 when 1 p you have very few friends default p you have #{friends} friendsある条件のときには何もしないという場合は、明示的に break を記述する必要があります。 - const friends = 0 case friends when 0 - break when 1 p you have very few friends default p you have #{friends} friends繰り返し処理 each 集合の中を順に処理する each が用意されています。ほとんどの場合、繰り返し処理で使うのはこれです。 - const values = [1, 2, 3, 4, 5] ul each val in values li= val↓ <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul>values の部分に JavaScript のコードを直接書いても構いません。 ul each val in [1, 2, 3, 4, 5] li= valul each val in values.length ? values : ['There are no values'] li= val値だけでなくインデックスも受け取れます。 ul each val, index in ['zero', 'one', 'two'] li= index + ': ' + valオブジェクトをループさせることもできます。(注: each の値に指定する変数は 値, キー の順番) - const dict = {1: 'one', 2: 'two', 3: 'three'} ul each val, key in dict li= key + ': ' + valwhile 条件に一致する間ループを回す while 文も利用できます。 - let n = 0; ul while n < 4 li= n++↓ <ul> <li>0</li> <li>1</li> <li>2</li> <li>3</li> </ul>他のファイルの埋め込み (inlucde) 他のファイルの内容を取り込みます。 例えば index.pug が以下の内容とします。 //- index.pug html //- ここで includes/_head.pug を読み込み include includes/_head.pug body h1 My Site p Welcome to my super lame site. //- ここで includes/_foot.pug を読み込み include includes/_foot.pugincludes/_head.pug は以下の内容とします。 //- includes/_head.pug head title My Site script(src='/javascripts/jquery.js') script(src='/javascripts/app.js')includes/_foot.pug は以下の内容とします。 //- includes/_foot.pug footer#footer p Copyright (c) foobarindex.pug を HTML に変換すると、inculdes/_head.pug と includes/_foot.pug の内容が埋め込まれて以下のようになります。 <!DOCTYPE html> <html> <head> <title>My Site</title> <script src="/javascripts/jquery.js"></script> <script src="/javascripts/app.js"></script> </head> <body> <h1>My Site</h1> <p>Welcome to my super lame site.</p> <footer id="footer"> <p>Copyright (c) foobar</p> </footer> </body> </html>平文テキストの include Pug ではないファイルを取り込むこともできます。例えば CSS ファイルや JavaScript ファイルですね。 //- index.pug doctype html html head style include style.css body h1 My Site p Welcome to my super lame site. script include script.js/* style.css */ h1 { color: red; }// script.js console.log('You are awesome');index.pug は以下の HTML に変換されます。 <!DOCTYPE html> <html> <head> <style> /* style.css */ h1 { color: red; } </style> </head> <body> <h1>My Site</h1> <p>Welcome to my super lame site.</p> <script> // script.js console.log('You are awesome'); </script> </body> </html>Markdown ファイルの埋め込み (Filter) Pug には 後述する Filter という機能 があります。これを使うと Markdown や SASS などの変換処理を行った上で埋め込むことができます。 //- index.pug doctype html html head title An Article body include:markdown-it article.md# article.mdThis is an article written in markdown.index.pug を変換すると以下の HTML になります。 <!DOCTYPE html> <html> <head> <title>An Article</title> </head> <body> <h1>article.md</h1> <p>This is an article written in markdown.</p> </body> </html>共通レイアウトの定義 (extends) include では例えばヘッダやフッタなどの複数ページで利用する共通部品を分離することができました。 そうではなく、全体で共通に利用するレイアウトを用意して、その中にページごとに異なる部分を埋め込みたい時に extends が利用できます。 基本 レイアウトファイルを以下のように作成します。 //- _layout.pug//- 外から block で変数(title)を渡せるようにする block variableshtml head //- title はページごとに異なるので外から与えられたものを埋め込む title My Site - #{title} //- ページによって利用する js ファイルが異なるので block で渡せるようにする block scripts //- もし block scripts が渡されなければ、デフォルト値として以下を利用 script(src='/jquery.js') body //- ページのコンテンツは block で渡せるようにする block content //- ページによってはフッタが異なるので block で渡してカスタマイズできるようにする block foot //- もし block foot が渡されなければ、デフォルト値として以下を利用 #footer p some footer contentこれを利用するページは以下のように extends を使います。 //- page-a.pug extends _layout.pug//- タイトルを変数で渡す block variables - const title = 'page A'//- jquery 以外に pets.js というページ独自の js ファイルも利用するのでデフォルト値を上書き block scripts script(src='/jquery.js') script(src='/pets.js')//- ページのコンテンツを渡す block content h1= title - var pets = ['cat', 'dog'] each petName in pets p= petName//- フッタはデフォルトのものを利用(block foot は渡さない)↓ page-a.pug を HTML に変換すると <html> <head> <title>My Site - page A</title> <script src="/jquery.js"></script> <script src="/pets.js"></script> </head> <body> <h1>page A</h1> <p>cat</p> <p>dog</p> <div id="footer"> <p>some footer content</p> </div> </body> </html>append / prepend (デフォルト値の前後に追加) 今までの説明では block に指定するとデフォルト値を上書き(置き換え)していました。 そうではなく、デフォルト値にさらに追加するための方法が用意されています。 例えば先の例で page-a.pug は jquery.js の他に pet.js も利用するために //- jquery 以外に pets.js というページ独自の js ファイルも利用するのでデフォルト値を上書き block scripts script(src='/jquery.js') script(src='/pets.js')と記述していました。 これは append を利用して以下のように記述できます。 //- デフォルト値である jquery.js の後に pets.js というページ独自の js ファイルの利用を追加 block append scripts script(src='/pets.js')append でなく prepend を指定するとデフォルト値の前に追加されます。 レイアウトを継承してサブレイアウトを定義 サイト全体で利用する _layout.pug を作ったわけですが、幾つかのページではさらにコンテンツ部分の構造が同じなので共通化したいとします。 以下のように _layout.pug を継承した _sub-layout.pug を作成します。 //- _sub-layout.pug extends _layout.pug//- コンテンツ部分の構造をさらに共通化 block content .sidebar block sidebar p nothing .primary block primary p nothingこれで sidebar と primary というブロックが content の内部に作られました。 これを以下のように利用できます。 //- page-b.pug extends _sub-layout.pug block variables - const title = 'page B' block sidebar p Sidebar block primary p Primary↓ <html> <head> <title>My Site - page B</title> <script src="/jquery.js"></script> </head> <body> <div class="sidebar"> <p>Sidebar</p> </div> <div class="primary"> <p>Primary</p> </div> <div id="footer"> <p>some footer content</p> </div> </body> </html>extends の継承については公式が注意書きをしているので、多用したり複雑な使い方をするのであれば extends 利用のよくある間違い には目を通しておいた方がいいでしょう。 Mixin 同じ部品を色々な箇所で利用したい場合、別ファイルにして include を利用する方法を既に説明しました。 でも1つのファイルでしか使わないので別ファイルにするほどでも・・・という場合もあります。 また小さい部品を幾つか集めて1つのファイルで管理したいということもあるでしょう。 Mixin はプログラム言語で言うところの関数みたいなものです。 あるいは独自タグを定義するような、SPA フレームワークでのコンポーネント的なものとも言えるかもしれません。 //- Mixinの宣言 mixin list ul li foo li bar li baz//- Mixinを利用するときは頭に + を付ける +list +list↓ <ul> <li>foo</li> <li>bar</li> <li>baz</li> </ul> <ul> <li>foo</li> <li>bar</li> <li>baz</li> </ul>Mixin に引数を渡す 引数を渡すこともできます。 mixin pet(name) li.pet= nameul +pet('cat') +pet('dog') +pet('pig')引数のデフォルト値もサポートしています。 mixin article(title='Default Title') .article .article-wrapper h1= title//- 引数省略してデフォルト値を利用 +article()+article('Hello world')可変長引数もサポートしています。 //- 最初の引数は id で、その後は items に配列として格納 mixin list(id, ...items) ul(id=id) //- items を順にループ each item in items li= item//- 最初の 'my-list' は id、残りは items +list('my-list', 1, 2, 3, 4)↓ <ul id="my-list"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> </ul>Mxin にブロックを渡す block を渡すこともできます。 mixin article(title) .article .article-wrapper h1= title if block //- ブロックが渡されてたらそれを埋め込む。 block else //- ブロックが渡されてなかったら以下 p No content provided//- ブロックを渡さないで呼び出し +article('Hello world')//- ブロックを渡して呼び出し +article('Hello world') p This is my p Amazing article↓ <div class="article"> <div class="article-wrapper"> <h1>Hello world</h1> <p>No content provided</p> </div> </div> <div class="article"> <div class="article-wrapper"> <h1>Hello world</h1> <p>This is my</p> <p>Amazing article</p> </div> </div>1つ目は "No content provided" が、2つ目は渡したブロックが埋め込まれていますね。 Mixin に属性を渡す Mixin では渡された属性を attributes で参照できます。 mixin link(href, name) //- 属性として渡される class を、a タグの class に設定(!= を使ってるのでエスケープはしない) a(class!=attributes.class href=href)= name//- 属性として class="btn" を渡している +link('/foo', 'foo')(class="btn")class!=attributes.class としてエスケープしないようにしているのは、Mixin を呼び出す際の class="btn" で既にエスケープされてるので、もう一度エスケープするのが無駄だからです。 渡された属性をそのまま全部展開したい場合は &attributes が利用できます。 mixin link(href, name) a(href=href)&attributes(attributes)= name+link('/foo', 'foo')(class="btn")渡された属性 class="btn" がそのまま link Mxin 内の属性として展開されます。 <a class="btn" href="/foo">foo</a>引数なしの Mixin に属性を渡す場合、+link(class="btn") と書いてもそれが引数でなく属性だと認識されます。 ただ Pug 公式では +link()(class="btn") のように引数がないことを明示する記述をお勧めしています。 Filter Pug に渡す前に変換処理を行う仕組みです。例えばマークダウンを HTML に変換するとか、SCSS を CSS に変換して埋め込むとかができます。 Filter は頭に : を付けて呼び出します。括弧の中にはフィルタに渡すオプションを指定できます。 :markdown-it(linkify langPrefix='highlight-') # Markdown Markdown document with http://links.com and ```js var codeBlocks; ```↓ <h1>Markdown</h1> <p>Markdown document with <a href="http://links.com">http://links.com</a> and</p> <pre><code class="highlight-js">var codeBlocks; </code></pre>この解説は文法の説明に絞っており、Pug の実行方法には触れません^3が、markdown-it フィルタを利用するにはそのためのパッケージのインストールが必要です。 全ての JSTransformer モジュールがフィルターとして利用可能です。 注意点として、フィルタに Pug の処理中に JavaScript で生成した動的なものを渡すことはできません。 ちょっとわかりにくいので以下の例で説明します。 - const mds = [ '# タイトル1', '# タイトル2', ]each md in mds :markdown-it(linkify langPrefix='highlight-') md上記で md の部分は変数の内容を渡したいのですが、このままでは単に "md" という文字列としてしか認識してくれません。 :markdown-it(linkify langPrefix='highlight-') #{md}のように #{md} で渡しても、フィルタの中身は単なる文字列としてしか認識されず、"#{md}" という文字列が渡されるだけです。 なおフィルタのオプションにも変数を渡したり JavaScript で動的に生成したものを渡したりできません。 JavaScirpt で生成したものをフィルタに渡す機能が一切提供されていないんですね。 高速に動作させるためとのことですが、Pug を静的サイトジェネレータとして利用しようとすると、これが大きな制限になります。 もうちょっと高度な話 タグ 文字列中へのタグの埋め込み 長い文字列の中にインラインタグを埋め込むときに #[] でタグを埋め込む構文を覚えておくと楽に書けます。 これを知らなくても |(パイプ)を使って書けるので基本としては紹介しませんでした。 p. This is a very long and boring paragraph that spans multiple lines. Suddenly there is a #[strong strongly worded phrase] that cannot be #[em ignored]. p. And here's an example of an interpolated tag with an attribute: #[q(lang="es") ¡Hola Mundo!]以下の HTML に変換されます。 <p> This is a very long and boring paragraph that spans multiple lines. Suddenly there is a <strong>strongly worded phrase</strong> that cannot be <em>ignored</em>. </p> <p> And here's an example of an interpolated tag with an attribute: <q lang="es">¡Hola Mundo!</q> </p>入れ子を1行で表現 a imgを以下のように1行で記述する方法が用意されています。 a: imgcase を短く書きたい場合にも使えます。 - const friends = 1 case friends when 0: p you have no friends when 1: p you have a friend default: p you have #{friends} friends閉じタグのいらないタグ img のような閉じタグのいらないタグを Pug は自動的に判別してそのように変換してくれます。 a imgこれは以下の HTML になります。 <a><img /></a>HTML 5 に規定されているタグなら勝手に判別してくれますが、コンポーネントなどの独自タグの場合はそうはいきません。以下のように閉じタグがないことを後ろにスラッシュを付けて伝える必要があります。 foo(bar='baz')/以下の HTML に変換されます。 <foo bar="baz" />属性 エスケープの抑止 Pug はデフォルトで全ての属性の値をエスケープします。エスケープして欲しくない場合は = ではなく != を使います。 div(escaped="<code>") div(unescaped!="<code>")上記が下記の HTML に変換されます。 "<" が &lt; に、 ">" が &gt; にエスケープされるかどうかが異なっています。 <div escaped="&lt;code&gt;"></div> <div unescaped="<code>"></div>特殊文字が含まれる場合 SPA フレームワークと組み合わせて利用すると、属性に [] とか () などの特殊文字が使われることがあります。そう Angular のことですね。そういう特殊文字が含まれる場合は '' や "" で囲ってやってください。 button(class='button' '(click)'='play()')Boolean 属性の出力 doctype html を指定していない場合、Pug は文書が HTML かどうか分からない(例えば XML かもしれない)ので、値として属性名を出力します。 input(type='checkbox' checked)input(type='checkbox' checked=true)↓ どちらも以下になります。値には属性名である "checked" が出力されます。 <input type="checkbox" checked="checked" />false の場合、属性自体を出力しません。 input(type='checkbox' checked=false)↓ <input type="checkbox" />文書に doctype html を指定している場合、Pug は HTML の真偽値の表し方で出力します。 つまり true だった場合に属性名を値として出力しません。 input(type='checkbox' checked=true)↓ checked という属性に値は設定されません。 <input type="checkbox" checked />なおこのルールはあくまで指定したのが真偽値だった場合のみ適用されます。 例えば以下の場合、値が true または false の真偽値ではなく、"true" という文字列なので、上記のルールは適用されずにそのまま文字列として出力されます。 input(type='checkbox' checked=true.toString())input(type='checkbox' checked='true')input(type='checkbox' checked=true && 'true')↓ 上記3つはどれも以下のように値に文字列がそのまま出力されます。 <input type="checkbox" checked="true" />&attributes 実はこれは既に「Mixin に属性を渡す」で紹介しています。 JavaScript のオブジェクトをそのまま属性として展開します。 div#foo(data-bar="foo")&attributes({'data-foo': 'bar'})↓ <div id="foo" data-bar="foo" data-foo="bar"></div>もちろん変数に入れたオブジェクトも展開されます。 - const attributes = { class: 'baz' } div#foo(data-bar="foo")&attributes(attributes)↓ <div class="baz" id="foo" data-bar="foo"></div>extends 利用のよくある間違い 公式がわざわざ注意書きを書いているので、説明しておきます。 Pug のテンプレート継承機能は複雑なページ構造を小さくシンプルなファイルに分解する強力な機能だけれども、あまり多くのテンプレートを繋げてしまうと余計複雑になってしまいます^4。 extends を使う子供テンプレートのトップレベル(つまりインデントされていない部分)には block と Mixin 定義だけが現れるようにすべきです。 これは重要です!親のテンプレートはページ全体の構造を定義し、子供のテンプレートはただ特定のブロックのロジックやマークアップを append / pretend したり置き換えるだけにすべきです。 このことはマークアップを含むことのできる unbuffered code(頭が -(ハイフン)で始まる JavaScript コード)にも当てはまります。 同じ理由で // 始まりの HTML に出力されるコメントも子供テンプレートのトップレベルに現れてはいけません。 (//- 始まりの HTML に出力しない Pug コメントはどこに書いても構いません。)