Quantcast
Channel: Node.jsタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8832

ただ値を埋め込むだけの簡単なテンプレートエンジンをNode.jsで自作する

$
0
0

JavaScriptで Infrastructure as Codeのツールを開発しています

以前こちらの記事で、サーバからShellScriptで取得してきた値を、HTML表示する機能を紹介しましたが、その際にHTMLに値を埋め込む簡単なテンプレートエンジンを自前で実装したので、その紹介です

テンプレートエンジンとは?

Wikipediaのページが、ちゃんとあるような専門用語なんですね

固い説明はそちらに任せるとして、ものすごく乱暴に説明すると、 hoge='fuga'のとき I am a {{ hoge }}.という文字列の {{ hoge }}部分に I am a fuga.というように 'fuga'を埋め込んでくれるようなもののことです

私はAnsibleを本職でよく利用するのでPythonの世界で有名なテンプレートエンジンである Jinja2の経験があります

実際のテンプレートエンジンでは {{ 2 + 3 }}5というように四則演算ができたり、テンプレートの中に関数を埋め込んだりできるのですが、この記事で紹介するのは、単に変数の値を埋め込む機能だけの簡単なものになります

npmで適当なテンプレートエンジン提供されてないの?

Node.jsでもいくつかテンプレートエンジンはあるようですが、今回それらを使いませんでした。理由は2つあります

  1. 調べた限りでもテンプレートエンジンありすぎて、どれが良いのかよく分からない
  2. 可能な限り依存パッケージは減らしたい (というかクライアントサイドでVue.js使った以外はnpmの依存パッケージは0にしました)

どうやって実装したのか

おおまかな処理の流れは、以下のような関数にまとめられます

  1. テンプレートファイルを文字列としてロードする
  2. 1を改行コード /\r\n|\r|\n/ (正規表現)で分割する
  3. 2を1要素(1行)ごとに取り出し、値の埋め込み部分を表す {{%%}}で分割する
  4. 3の奇数番目の文字列({{%%}}で囲まれた文字列)の両端のスペース を取り除く(トリミング)
  5. 4の結果が引数として渡されたオブジェクトのキーとして存在する場合のみ、値を置換する
  6. 全体を 改行コード \nでつなげる

※テンプレート内に改行を含む場合は対応しませんでした
※また、別のテンプレートエンジンで使われそうな {{}}と見分けるために、あえて {{%%}}にしています

コードとしては以下のようになります

(1) テンプレートファイルを文字列としてロードする

TemplateEngine.js
// ファイルアクセスのモジュールをロードconstfs=require('fs');// テンプレートファイルを文字列としてロードする// テンプレートは関数の外で1回ロードすれば充分consttemplate=fs.readFileSync(`${__dirname}/templates/hoge.templ`).toString();

(2) (1)を改行コード /\r\n|\r|\n/ (正規表現)で分割する

TemplateEngine.js
constrender=(props)=>{returntemplate.split(/\r|\n|\r\n/)}

(3) (2)を1要素(1行)ごとに取り出し、値の埋め込み部分を表す {{%%}}で分割する

TemplateEngine.js
constrender=(props)=>{returntemplate.split(/\r|\n|\r\n/).map(line=>line.split(/{{%|%}}/););}

(4) (3)の奇数番目の文字列の両端のスペース を取り除く(トリミング)

TemplateEngine.js
constrender=(props)=>{returntemplate.split(/\r|\n|\r\n/).map(line=>{constwords=line.split(/{{%|%}}/);return0<words.length?words.map((w,i)=>0<i%2?w.replace(/^ */,'').replace(/ *$/,''):w).join(''):line});}
  1. 上記の処理をまとめ、必要な部分に値を埋め込む関数
TemplateEngine.js
// ファイルアクセスのモジュールをロードconstfs=require('fs');// テンプレートファイルを文字列としてロードする// テンプレートは関数の外で1回ロードすれば充分consttemplate=fs.readFileSync(`${__dirname}/templates/hoge.templ`).toString();constrender=(props)=>{returntemplate.split(/\r|\n|\r\n/).map(line=>{constwords=line.split(/{{%|%}}/);return0<words.length?words.map((w,i)=>0<i%2?props[w.replace(/^ */,'').replace(/ *$/,'')]:w).join(''):line}).join('\n');}

テンプレートを用意して実行するとこんな感じ

hoge.templ
I am a {{% hoge %}}.

TemplateEngine.js
console.log(render({hoge:'fuga',}));
$ node TemplateEngine.js

I am a fuga.

おわりに

ただ値を埋め込むだけなら、とても簡単に実装できることが分かります

これなら余計なパッケージのバージョン管理やnodeのバージョンとの互換を気にする必要がなくなるので、独自実装の方が長い目で見ると楽かもしれません


Viewing all articles
Browse latest Browse all 8832

Trending Articles