クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【gulp】メタ言語(EJS、Sass(SCSS)、TypeScript)を同時コンパイルする!

gulpの続きです。前回はSass(SCSS)とTypeScriptを同時にコンパイルする環境を作りました。今回はここにEJSを加えてメタ言語(HTMLテンプレートエンジン、AltCSS、AltJS)フルセットの同時コンパイル環境にてみます。それではいきましょう!

【目次】

※参考:【gulp】Sass(SCSS)とTypeScriptを同時にコンパイルする環境を作る - クモのようにコツコツと

※メタ言語(HTMLテンプレートエンジン、AltCSS、AltJS)まとめ
qiita.com

前回のファイル構成

glup_frontend
├node_modules(中身は省略)
├src
│├scss
││├common.scss
││└other.scss
│└ts
│ └common.ts
├dest
│├css
││├common.css
││└other.css
│├js
││└common.js
│├index.html
│└other.html
├gulpfile.js
├package.json
├package-lock.json
└tsconfig.json
  • 「src」フォルダの中のscssファイルが「dest」フォルダでcssファイルにコンパイルされる
  • 「src」フォルダの中のtsファイルが「dest」フォルダでjsファイルにコンパイルされる

今回はここにHTMLテンプレートエンジンも追加したい。これでHTML、CSS、JSフルセットのメタ言語のコンパイル環境になる。

HTMLテンプレートエンジンはEJSを選定

HTMLテンプレートエンジンはこれまでPug、EJS、Markdownのコンパイルを体験した。

※参考:【HTML】PugをGulpでコンパイルしてみる(メタ言語初コンパイル!) - クモのようにコツコツと

※参考:【HTML】EJSをGulpでコンパイルしてみる(SyntaxErrorでちょい苦戦) - クモのようにコツコツと

※参考:【HTML】MarkdownをGulpでHTMLにコンパイルしてみる - クモのようにコツコツと

今回はこの中で、EJSを選定する。SCSSやTypeScriptと同様、ネイティブHTMLの書式が残る上位互換的なメタ言語。

※参考:【HTML】EJSをGulpでコンパイルしてみる(SyntaxErrorでちょい苦戦) - クモのようにコツコツと

https://cdn-ak.f.st-hatena.com/images/fotolife/i/idr_zz/20191206/20191206064434.jpg

この時は「ejs」フォルダの中のejsファイルをルート直下にindex.htmlとしてコンパイルした。今回はSass(SCSS)やTypeScriptと同様に「src」フォルダにコンパイルしたい。

EJSパッケージ(gulp-ejs他)のインストール

まずターミナルを開きcdコマンドで「gulp_frontend」フォルダに移動する。

cd /(フォルダ)/gulp_frontend

EJSのコンパイルに必要な下記のパッケージをインストールする。

  • gulp-ejs:EJSコンパイル
  • gulp-rename:拡張子リネイム(.ejs→.html)
  • gulp-replace:空白行削除(任意)
npm install -D gulp-ejs gulp-rename gulp-replace

成功するとpackage.jsonに追記される!

  "devDependencies": {
    "browser-sync": "^2.26.7",
    "gulp": "^4.0.2",
    "gulp-ejs": "^5.1.0", // ←追記された!
    "gulp-rename": "^2.0.0", // ←追記された!
    "gulp-replace": "^1.0.0", // ←追記された!
    "gulp-sass": "^4.0.2",
    "gulp-typescript": "^6.0.0-alpha.1"
  },
  "dependencies": {
    "@types/node": "^13.13.2",
    "typescript": "^3.8.3"
  }

gulpfile.js全体(修正前)

gulpの設定が書かれている「gulpfile.js」、前回はこうだった。

const gulp = require('gulp');
const sass = require('gulp-sass');
const typescript = require('gulp-typescript');
const browserSync = require('browser-sync'); 

// sassコンパイル
gulp.task('sass', (done) => {
    gulp.src('./src/scss/*.scss')
        .pipe(sass({
                    outputStyle: 'expanded'
                })
            )
        .on("error", sass.logError)
        .pipe(gulp.dest('./dest/css'));
    done();
});

// TypeScriptコンパイル
gulp.task('ts',  (done) => {
    gulp.src('./src/ts/*.ts')
        .pipe(typescript())
        .js
        .pipe(gulp.dest('./dest/js'));
    done();
});

// リロードするhtml
gulp.task('browser-sync', (done) => {
    browserSync.init({
        server : {
            baseDir : './dest/',
            index : 'index.html',
        },
    });
    done();
});

// リロード設定
gulp.task('browser-reload', (done) => {
    browserSync.reload();
    done();
});

// 監視ファイル
gulp.task('watch-files', (done) =>  {
    gulp.watch("./dest/*.html", gulp.task('browser-reload'));
    gulp.watch("./src/scss/*.scss", gulp.task('sass'));
    gulp.watch("./dest/css/*.css", gulp.task('browser-reload'));
    gulp.watch("./src/ts/*.ts", gulp.task('ts'));
    gulp.watch("./dest/js/*.js", gulp.task('browser-reload'));
    done();
});

// タスク実行
gulp.task('default', 
    gulp.series(gulp.parallel(
        'watch-files', 'browser-sync', 'sass', 'ts'
    ), (done) => {
    done();
}));
  • 冒頭でgulpgulp-sassgulp-typescriptbrowser-syncを読み込んでいる
  • gulp.tasksasssassファイルコンパイルを設定
  • gulp.tasktstsファイルコンパイルを設定
  • gulp.taskbrowser-syncでリロードするhtmlを設定
  • gulp.taskbrowser-reloadでリロード設定
  • gulp.taskwatch-filesで監視ファイルを設定
  • gulp.taskdefaultで複数のタスクを同時に実行

詳細は前回の記事を参照

※参考:【gulp】Sass(SCSS)とTypeScriptを同時にコンパイルする環境を作る - クモのようにコツコツと

gulpfile.jsを修正(EJS設定を追加)

EJSのパッケージをインポート

まず冒頭で先程インストールした3つのパッケージをインポートする。

const rename = require("gulp-rename");
const ejs = require("gulp-ejs");
const replace = require("gulp-replace"); 
  • 変数renamerequire()メソッドでgulp-renameを読み込む
  • 変数ejsrequire()メソッドでgulp-ejsを読み込む
  • 変数replacerequire()メソッドでgulp-replaceを読み込む

EJSのコンパイル設定

// EJSコンパイル
gulp.task('ejs',  (done) => {
    gulp.src(["./src/*.ejs", "!./src/_*.ejs"])
      .pipe(ejs({}, {}, { ext: ".html" }))
      .pipe(rename({ extname: ".html" }))
      .pipe(replace(/[\s\S]*?(<!DOCTYPE)/, "$1"))
      .pipe(gulp.dest("./dest/"));
    done();
});
  • gulp.task()メソッド実行。第一引数はejs、第二引数は無名関数で引数はdone
  • 無名関数の中はgulp.src().pipe().pipe().pipe().pipe()とチェーンになっている
  • src()の第一引数は「○○.ejs」ファイル、第二引数は「_○○.ejs」ファイル以外(!)
  • 一つ目のpipe()ejs()メソッドでhtmlにコンパイル
  • 二つ目のpipe()はrename()メソッドで拡張子を.htmlにリネイム
  • 三つ目のpipe()はreplace ()メソッドで<!DOCTYPE〜より上の行を削除
  • 四つ目のpipe()はgulp.dest()メソッドで「dest」フォルダに吐き出す
  • 最後にdone()メソッドを実行

前回と同じくreturnを使わない書き方にしているのと、「src」や「dest」のフォルダ名を追記した。

※参考:【HTML】EJSをGulpでコンパイルしてみる(SyntaxErrorでちょい苦戦) - クモのようにコツコツと

※参考:【gulp】Sass(SCSS)とTypeScriptを同時にコンパイルする環境を作る - クモのようにコツコツと

監視ファイルにEJSを追加

gulp.watch()の常時監視にEJSの設定を追加

// 監視ファイル
gulp.task('watch-files', (done) =>  {
    gulp.watch("./src/*.ejs", gulp.task('ejs')); // 追記
    gulp.watch("./dest/*.html", gulp.task('browser-reload'));
    gulp.watch("./src/scss/*.scss", gulp.task('sass'));
    gulp.watch("./dest/css/*.css", gulp.task('browser-reload'));
    gulp.watch("./src/ts/*.ts", gulp.task('ts'));
    gulp.watch("./dest/js/*.js", gulp.task('browser-reload'));
    done();
});
  • gulp.watch()メソッドに.ejsファイルのパスとejsタスクを追加

タスクの同時実行にEJSを追加

最後にタスクを同時に実行するdefaultタスクにEJSコンパイルのタスクを追加する。

// タスク実行
gulp.task('default', 
    gulp.series(gulp.parallel(
        'watch-files', 'browser-sync', 'ejs', 'sass', 'ts'
    ), (done) => {
    done();
}));
  • gulp.series()メソッドのgulp.parallel()の中にejsを追加する。

これで動くか、「dest」フォルダにEJSファイルを作ってみる。

EJSのファイルを作成

「src」フォルダの中にejsファイルを作る

glup_frontend
├node_modules(中身は省略)
├src
│├scss
││├common.scss
││└other.scss
│└ts
││└common.ts
│├index.ejs // 追加
│└other.ejs // 追加
├dest
│├css
││├common.css
││└other.css
│├js
││└common.js
│├index.html
│└other.html
├gulpfile.js
├package.json
├package-lock.json
└tsconfig.json
  • 「src」フォルダの中に「index.ejs」と「other.ejs」を作る

これでようやく「src」と「dest」フォルダが全く同じファイル構成になった!

index.ejsの内容

最初にTopページ「index.ejs」。前回の「index.html」をベースにする。

<% var h1Title = 'メタ言語同時コンパイル(EJS、Sass(SCSS)、TypeScript)'; %>

<!doctype html>
<html>
<head>
   <meta charset="UTF-8">
   <title><%= h1Title %></title>
   <link rel="stylesheet" href="css/common.css">
   <link rel="stylesheet" href="css/other.css">
   <script src="js/common.js"></script>
</head>
<body>
    <section id="scss">
        <h1><%= h1Title %></h1>
        <p class="lede">CodepenのSettingの旅CSS編第2回。今回はSCSS。LESSに似てる?</p>
        <section id="nest">
            <h2>ネスト(階層化)</h2>
            <p>SCSSもdivのネストの階層化ができる。この段落は<em>ネスト</em>でスタイルを当てている。<br>
      ん?この書き方はLESSとほぼまったく同じだ。<br>
            <span class="bikou">インデントについても厳密でじゃでなくても解釈してくれるようだ(?)</span>
            </p>
        </section>
        <section id="oya">
            <h2>親セレクタの参照</h2>
            <p>おそらくLESSにはないSCSS専用の機能。ネストして「&」に擬似要素を付けると親セレクタを参照できる<br>
            <span class="bikou">この尾行欄につけている「※」印は「.bikou:before」ではなく「&:before」だけで済んでいる。 </span>
            </p>
        </section>
        <section id="oya">
            <h2>TypeScriptで書いたクリックイベント</h2>
            <button id="btn">ここ押せワンワン</button>
        </section>
          <p><a href="other.html">別ページへ</a></p>
    </section>
</body>
</html>
  • 変数h1Titleでh1タグに入れたいテキストを設定
  • h1タグにh1Titleを配置

変更点はh1タグを変数h1Titleにしている部分。

※参考:【gulp】Sass(SCSS)とTypeScriptを同時にコンパイルする環境を作る - クモのようにコツコツと

other.ejsの内容

次に別ページ「other.ejs」。前回の「other.html」とEJSでフィズバズのfor文をミックスしている。

<% var h1Title = '別ページ:EJSでフィズバス'; %>
<% var h2Title = 'EJSでフィズバズる'; %>
<% var count = 30; %>

<!doctype html>
<html>
<head>
   <meta charset="UTF-8">
   <title><%= h1Title %></title>
   <link rel="stylesheet" href="css/common.css">
   <link rel="stylesheet" href="css/other.css">
   <script src="js/common.js"></script>
</head>

<body>
    <section id="scss">
        <h1><%= h1Title %></h1>
        <p>別ページもリロードされるかテストですと。</p>
        <section>
            <h2 class="oya"><%= h2Title %></h1>
            <p>EJSで変数、for文、if文を使ってフィズってバズってみた。</p>
            <ul>
              <% for (var i = 1; i < count+1; i++) { %>
                  <% if (i % 3 == 0 && i % 5 == 0) { %>
                  <li>フィズってバズった!</li>
                  <% } else if (i % 3 == 0) { %>
                  <li>フィズった!</li>
                  <% } else if (i % 5 == 0) { %>
                  <li>バズった!</li>
                  <% } else { %>
                  <li><%= i %></li>
                  <% } %>
              <% } %>
            </ul>
          </section>
        <p class="lede"><a href="/">Topへ</a></p>
        </section>
    </section>
</body>
</html>

※参考:【gulp】Sass(SCSS)とTypeScriptを同時にコンパイルする環境を作る - クモのようにコツコツと

※参考:【HTML】テンプレートエンジンEJSでFizzBuzzる!(Pugとの比較あり) - クモのようにコツコツと

動作確認(gulpタスク実行)

index.ejs→index.htmlコンパイル

それでは挙動を確認しよう。gulpタスク実行!

npx gulp

おお!ブラウザが立ち上がった!

http://localhost:3000/

f:id:idr_zz:20200501053631j:plain

h1タグが先程の変数のテキスト「メタ言語同時コンパイル(EJS、Sass(SCSS)、TypeScript)」になっている。

コンパイルされた「index.html」のコード


<!doctype html>
<html>
<head>
   <meta charset="UTF-8">
   <title>メタ言語同時コンパイル(EJS、Sass(SCSS)、TypeScript)</title>
   <link rel="stylesheet" href="css/common.css">
   <link rel="stylesheet" href="css/other.css">
   <script src="js/common.js"></script>
</head>
<body>
    <section id="scss">
        <h1>メタ言語同時コンパイル(EJS、Sass(SCSS)、TypeScript)</h1>
        <p class="lede">CodepenのSettingの旅CSS編第2回。今回はSCSS。LESSに似てる?</p>
        <section id="nest">
            <h2>ネスト(階層化)</h2>
            <p>SCSSもdivのネストの階層化ができる。この段落は<em>ネスト</em>でスタイルを当てている。<br>
      ん?この書き方はLESSとほぼまったく同じだ。<br>
            <span class="bikou">インデントについても厳密でじゃでなくても解釈してくれるようだ(?)</span>
            </p>
        </section>
        <section id="oya">
            <h2>親セレクタの参照</h2>
            <p>おそらくLESSにはないSCSS専用の機能。ネストして「&」に擬似要素を付けると親セレクタを参照できる<br>
            <span class="bikou">この尾行欄につけている「※」印は「.bikou:before」ではなく「&:before」だけで済んでいる。 </span>
            </p>
        </section>
        <section id="oya">
            <h2>TypeScriptで書いたクリックイベント</h2>
            <button id="btn">ここ押せワンワン</button>
        </section>
          <p><a href="other.html">別ページへ</a></p>
    </section>
</body>
</html>

h1タグはプレーンテキストになっている。ただ、冒頭の変数設定を書いていた部分の行が開いているんだよなー。やはり「gulp-replace」の行削除設定が効いていないっぽい。

other.ejs→other.htmlのコンパイル

次に別ページ「other.html」も見てみる。

http://localhost:3000/other.html

おお!フィズバズになってる! f:id:idr_zz:20200501054310j:plain




<!doctype html>
<html>
<head>
   <meta charset="UTF-8">
   <title>別ページ:EJSでフィズバス</title>
   <link rel="stylesheet" href="css/common.css">
   <link rel="stylesheet" href="css/other.css">
   <script src="js/common.js"></script>
</head>

<body>
    <section id="scss">
        <h1>別ページ:EJSでフィズバス</h1>
        <p>別ページもリロードされるかテストですと。</p>
        <section>
            <h2 class="oya">EJSでフィズバズる</h1>
            <p>EJSで変数、for文、if文を使ってフィズってバズってみた。</p>
            <ul>
              
                  
                  <li>1</li>
                  
              
                  
                  <li>2</li>
                  
              
                  
                  <li>フィズった!</li>
                  
              
                  
                  <li>4</li>
                  
              
                  
                  <li>バズった!</li>
                  
              
                  
                  <li>フィズった!</li>
                  
              
                  
                  <li>7</li>
                  
              
                  
                  <li>8</li>
                  
              
                  
                  <li>フィズった!</li>
                  
              
                  
                  <li>バズった!</li>
                  
              
                  
                  <li>11</li>
                  
              
                  
                  <li>フィズった!</li>
                  
              
                  
                  <li>13</li>
                  
              
                  
                  <li>14</li>
                  
              
                  
                  <li>フィズってバズった!</li>
                  
              
                  
                  <li>16</li>
                  
              
                  
                  <li>17</li>
                  
              
                  
                  <li>フィズった!</li>
                  
              
                  
                  <li>19</li>
                  
              
                  
                  <li>バズった!</li>
                  
              
                  
                  <li>フィズった!</li>
                  
              
                  
                  <li>22</li>
                  
              
                  
                  <li>23</li>
                  
              
                  
                  <li>フィズった!</li>
                  
              
                  
                  <li>バズった!</li>
                  
              
                  
                  <li>26</li>
                  
              
                  
                  <li>フィズった!</li>
                  
              
                  
                  <li>28</li>
                  
              
                  
                  <li>29</li>
                  
              
                  
                  <li>フィズってバズった!</li>
                  
              
            </ul>
          </section>
        <p class="lede"><a href="/">Topへ</a></p>
        </section>
    </section>
</body>
</html>

ソースを見ると、やはり行削除の設定が効いていないなー。やはり「gulp-replace」が動いてない。これについては改めて検証したい。

Sass(SCSS)→CSSコンパイル

念のためSass(SCSS)の修正をしてみる。

srcフォルダの「common.css」のh2タグを

#nest, 
#oya {
  background: #fff;
  padding: 20px;
  margin: 0 0 20px;
  h2 {
    color: brown;
    font-size: 1.5em;
    line-height: 1.2;
    }
  /* 中略 */
}

茶色から紫にすると…

#nest, 
#oya {
  background: #fff;
  padding: 20px;
  margin: 0 0 20px;
  h2 {
    color: purple; /* ←修正 */
    font-size: 1.5em;
    line-height: 1.2;
    }
  /* 中略 */
}

おお、自動リロードで紫になった! f:id:idr_zz:20200501182622j:plain

destフォルダの「common.css」もコンパイルされている!

#nest h2,
#oya h2 {
  color: purple;
  font-size: 1.5em;
  line-height: 1.2;
}

TypeScript→JSコンパイル

srcフォルダの「common.ts」も修正しする。

    /* アロー関数 */
    const hellow = () => {
        window.alert('hellow TypeScript!!!!!!!!!!');
        window.alert('こんにちは、型スクリプト');
        window.alert(title + 'は第' + num + '回です。');
        window.alert('おしまい');
    }

1個目のアラートの「!」を「?」にしてみる。

    /* アロー関数 */
    const hellow = () => {
        window.alert('hellow TypeScript??????????'); // 修正
        window.alert('こんにちは、型スクリプト');
        window.alert(title + 'は第' + num + '回です。');
        window.alert('おしまい');
    }

ボタンを押すと、アラートのテキストも変わった! f:id:idr_zz:20200501183059j:plain

destフォルダの「common.js」もコンパイルされている!

    /* アロー関数 */
    var hellow = function () {
        window.alert('hellow TypeScript??????????');
        window.alert('こんにちは、型スクリプト');
        window.alert(title + 'は第' + num + '回です。');
        window.alert('おしまい');
    };

よし!これでHTML、CSS、JSの全てのメタ言語が同時コンパイルされる環境が作れた!

gulpfile.js全体(修正後)

最終的にこうなった。

const gulp = require('gulp');
const rename = require("gulp-rename");
const ejs = require("gulp-ejs");
const replace = require("gulp-replace"); 
const sass = require('gulp-sass');
const typescript = require('gulp-typescript');
const browserSync = require('browser-sync'); 

// EJSコンパイル
gulp.task('ejs',  (done) => {
    gulp.src(["./src/*.ejs", "!./src/_*.ejs"])
      .pipe(ejs({}, {}, { ext: ".html" }))
      .pipe(replace(/[\s\S]*?(<!DOCTYPE)/, "$1"))
      .pipe(replace("EJS", "EJS"))
      .pipe(gulp.dest("./dest/"));
    done();
});

// sassコンパイル
gulp.task('sass', (done) => {
    gulp.src('./src/scss/*.scss')
        .pipe(sass({
                    outputStyle: 'expanded'
                })
            )
        .on("error", sass.logError)
        .pipe(gulp.dest('./dest/css'));
    done();
});

// TypeScriptコンパイル
gulp.task('ts',  (done) => {
    gulp.src('./src/ts/*.ts')
        .pipe(typescript())
        .js
        .pipe(gulp.dest('./dest/js'));
    done();
});

// リロードするhtml
gulp.task('browser-sync', (done) => {
    browserSync.init({
        server : {
            baseDir : './dest/',
            index : 'index.html',
        },
    });
    done();
});

// リロード設定
gulp.task('browser-reload', (done) => {
    browserSync.reload();
    done();
});

// 監視ファイル
gulp.task('watch-files', (done) =>  {
    gulp.watch("./src/*.ejs", gulp.task('ejs'));
    gulp.watch("./dest/*.html", gulp.task('browser-reload'));
    gulp.watch("./src/scss/*.scss", gulp.task('sass'));
    gulp.watch("./dest/css/*.css", gulp.task('browser-reload'));
    gulp.watch("./src/ts/*.ts", gulp.task('ts'));
    gulp.watch("./dest/js/*.js", gulp.task('browser-reload'));
    done();
});

// タスク実行
gulp.task('default', 
    gulp.series(gulp.parallel(
        'watch-files', 'browser-sync', 'ejs', 'sass', 'ts'
    ), (done) => {
    done();
}));

最後に

ということで、ついにフルセットのメタ言語コンパイル環境ができましたー。EJS環境は前回の前回のTypeScriptに比べるとスムーズに作れたと思います。

ただ、改行を詰める「gulp-replace」が動いていないようなので、ちょっと検証しています。これによって正規表現についても詳しくなれそうなので、「補講編」的な感じにまた別の記事に分けて書いてみたいと思います。

それではまた!


※メタ言語(HTMLテンプレートエンジン、AltCSS、AltJS)まとめ
qiita.com