Pug(旧Jade)というJavaScriptのテンプレートエンジンがありますが、このテンプレートファイル内では JavaScriptが使えるので、複雑なHTMLを生成することができます。
但しデフォルトの状態だと、npmでインストールしたモジュールを呼び出して使うことができないため、ある程度込み入った処理を書くのは面倒です。
このページでは、Pugのテンプレートファイル内から npmでインストールしたモジュールを呼び出すための手順を説明します。
目次
手順
- pugコマンドだとできないようなので、面倒ですが自分で「pugテンプレートファイルをHTMLに変換する JavaScriptコード」を書き、それを nodeコマンドで実行します。
- (リンク先では jadeコマンドになっていますが、pugコマンドに読み替えてください。)
- pugモジュールのコンパイルを行うメソッド(何種類かあります)の options引数と、そのメソッドの戻り値(関数が返ります)の引数に、Pugテンプレートファイル内で使用したいオブジェクトをセットしておけばOKです。
例
このサイトで実際に使っているJavaScriptを載せておきます(そのままではないですが)(注意:現在このサイトでは使用していません)。
以下が前提となっています。
./src/html
ディレクトリ以下の全ての階層にある Pugテンプレートファイル(拡張子は pug か jade)を、./public
ディレクトリ以下に同じ階層のままHTMLファイルに変換して出力します。- pugモジュールなどは、npmコマンドで導入します(他にどんなモジュールが必要なのかは下のJavaScriptコードを参照して下さい)。
$ npm install --save-dev pug
- JavaScriptのファイル名は、
runPug.js
とします。 - Pugテンプレートファイル内で、
_
が使えるようにします(中身は lodash)です。 - 以下のコマンドで実行すると、HTMLファイル生成処理が実行されます。
$ node runPug.js
- Windows 7 上の MSYS2環境でテストしています。
JavaScriptファイルの内容
- だいたい以下のような JavaScriptコードになっています。
/** * * 以下のページを参考にした。 * cf. pug-cli/index.js at 1.0.0-alpha5 · pugjs/pug-cli * https://github.com/pugjs/pug-cli/blob/1.0.0-alpha5/index.js * */ var fs = require('fs'), path = require('path'), _ = require('lodash'), pug = require('pug'), program = require('commander'), mkdirp = require('mkdirp'); var siteJson = require('./src/data/site.json'), // 元となるpugファイルが入っているディレクトリパス(末尾にスラッシュはつけない) srcPath = './src/html', // 出力先のディレクトリパス(末尾にスラッシュはつけない) dstDirPath = './public', options = {}; var basename = path.posix.basename; var dirname = path.posix.dirname; var resolve = path.posix.resolve; var join = path.posix.join; var relative = path.posix.relative; //-------------- // 引数の処理 //-------------- // cf. pug-cli/index.js at 1.0.0-alpha5 · pugjs/pug-cli // https://github.com/pugjs/pug-cli/blob/1.0.0-alpha5/index.js program .version('1.0.0') .option('-P, --pretty', 'compile pretty HTML output') .option('-D, --no-debug', 'compile without debugging (smaller functions)') .parse(process.argv); //------------------- // optionsを設定する // - pugテンプレートファイルに、npmで入れたモジュールを渡す場合は、ここで optionsにassignする。 //------------------- // siteJsonオブジェクトをテンプレートから使えるようにする _.assignIn(options, siteJson); // lodashをPugテンプレートファイル内から使えるようにする _.assignIn(options, { _: _ } ); [ // command option, compile option ['debug', 'compileDebug'], // --no-debug ['pretty', 'pretty'] // --pretty ].forEach(function (o) { 'use strict'; if (program[o[0]] !== undefined) { options[o[1]] = program[o[0]]; } }); //----------------- // メイン処理 //----------------- // 各ファイル毎に pug でコンパイルして HTMLファイルを生成する eachFiles(srcPath, null, function(filePath, rootPath) { 'use strict'; if (!/.*\.(?:pug|jade)$/.test(filePath)) { return } // 出力ファイルパスを生成 var dstFilePath = createDstFilePath(filePath, rootPath); // 出力先パス上に存在しないディレクトリがあれば作っておく var dir = resolve(dirname(dstFilePath)); mkdirp.sync(dir); // pugファイルをコンパイルしてファイルとして保存する // compileFile()とfn()には同じオプション用オブジェクトを渡せば良い var fn = pug.compileFile(filePath, options); fs.writeFileSync(dstFilePath, fn(options)); console.log('renderd src:', filePath, 'dst:', dstFilePath); }); //------------------ // Helper functions //------------------ /** * 出力先ファイルパスを生成して返す関数 * @param {string} filePath * @param {string} rootPath */ function createDstFilePath(filePath, rootPath) { 'use strict'; var dstPath = filePath.replace(/\.(?:pug|jade)$/, '.html'); if (rootPath) { // rootPathを基準とした相対パスを生成する // 空になることはほぼない dstPath = relative(rootPath, dstPath); } else { dstPath = basename(dstPath); } return './' + join(dstDirPath, dstPath); } /** * 特定のパス以下のファイルを順番に処理していく関数 * * 以下のページに載っていたのを少し修正して使っている。 * cf. node.jsでディレクトリ内のファイル全てに対して処理を回す関数 | hacknote * http://hacknote.jp/archives/11249/ * * @param {string} filePath * @param {string} rootPath * @param {callback} callback */ function eachFiles(filePath, rootPath, callback) { 'use strict'; if (!rootPath) { rootPath = filePath; } var stat = fs.statSync(filePath); if (stat && stat.isDirectory()) { var files = fs.readdirSync(filePath); if (files) { for (var _i in files) { (function (i) { var file = files[i]; if (filePath.match(/.*\/$/)) { eachFiles(filePath + file, rootPath, callback); } else { eachFiles(filePath + "/" + file, rootPath, callback); } }(_i)); } } } else if (stat.isFile()) { if (callback) { callback.call(this, filePath, rootPath); } } else { throw new Error(filePath + " is not a file or directory"); } }
- このJavaScriptコードを使用しなくても、pug-cli/index.jsをダウンロードして使うこともできます。その場合は、コンパイル系メソッド(compileメソッドなど)実行部分で、使用したいnpmモジュールを引数に追加するように書き換えます。(ご自分の環境にインストールした pugモジュールのバージョンに合わせた index.js ファイルをダウンロードして下さい)
JavaScriptファイルのポイント
- pugモジュールを取り込みます。
pug = require('pug'),
- Pugテンプレートファイル内から使用したいモジュールを、optionsにセットします。
_.assignIn(options, { _: _ } );
- pugオブジェクトの compileFileメソッドを実行して、Pugテンプレートファイル(filePath)をHTMLに変換するための関数を取得します。
var fn = pug.compileFile(filePath, options);
- 出力先ファイル(dstFilePath)に、HTMLを書き込みます。
fs.writeFileSync(dstFilePath, fn(options));
Pugテンプレートの作成
- 上記の JavaScriptコードにより、Pugテンプレートファイル内では、
_
が使えるようになります(中身はlodashです)。
処理を実行する(PugテンプレートファイルをHTMLに変換したファイルを生成する)
以下のコマンドで実行します。
$ node runPug.js
まとめ
- JavaScriptだけで複雑なHTMLを生成したい場合、Pug(旧Jade)は大変便利です。
- Pugを使って複雑なHTMLを作るには Pugテンプレートファイル内でJavaScriptを使うことになると思いますが、今回の方法で npmでインストールしたモジュールが使えるようになるので、更に可能性が広がります。