クモのようにコツコツと

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

【TypeScript】gulpでwebpackを起動できるようにする(モジュールも再分割)

メタ言語の続きです。前回はwebpackでTypeScriptのモジュールをバンドルしました。今回はwebpackとgulpを連携して、gulpで他のタスクと一緒にwebpackのバンドルも実行します。さらにフォルダの変更やモジュール再分割も試みました。それではいきましょう!

【目次】

※参考:前回記事
【TypeScript】モジュールファイルをwebpackでバンドルする - クモのようにコツコツと

※参考:【メタ言語】HTMLテンプレートエンジン、AltCSS、AltJSのまとめ
qiita.com

前回のおさらい

gulpのパッケージ「gulp-typescript」でコンパイルすると、モジュールファイルはそのままの状態でコンパイルされる。
f:id:idr_zz:20200713215017j:plain そしてCommonJS形式になり、ブラウザではモジュールファイルが認識されなかった。。

webpackを使うと、コンパイルとモジュールのバンドル(統合)された! f:id:idr_zz:20200717012913j:plain コンパイルされたファイルはminify(圧縮)された状態になる。可読性は低いがスッキリしたボリューム。

詳細は前回の記事を参照

※参考:前回記事
【TypeScript】モジュールファイルをwebpackでバンドルする - クモのようにコツコツと

webpackをgulpと連携したい!

ただ、今の状態だとTypeScriptのファイルを更新するたびにwebpackを手作業で実行する必要がある。また、他のメタ言語(EJS、Sass(SCSS))と連携していないのも不便。gulpとwebpackの連携したい。

いろいろ調べたが下記の記事がわかりやすく、参考になりそう!

※参考:Gulpで始めるwebpack 4入門 - Qiita

webpack-streamをインストール

まず、gulpとwebpackを連携するには「webpack-stream」というパッケージが必要な模様(そして前回インストールした「webpack-cli」はgulpを使用する場合は不要になるようだ)

まず、cdコマンドでフォルダに移動

cd /(パス)/gulp_frontend

「webpack-stream」をインストール

npm i -D webpack-stream

「package.json」を見ると「webpack-stream」が追加された!

"devDependencies": {
    "browser-sync": "^2.26.7",
    "gulp": "^4.0.2",
    "gulp-ejs": "^5.1.0",
    "gulp-plumber": "^1.2.1",
    "gulp-rename": "^2.0.0",
    "gulp-replace": "^1.0.0",
    "gulp-sass": "^4.0.2",
    "gulp-typescript": "^6.0.0-alpha.1",
    "ts-loader": "^8.0.1",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.12", // (こちらはgulpでは不要)
    "webpack-stream": "^5.2.1" // 追加された!
  },

gulpfile.jsにwebpackの設定を追記

webpackパッケージをインポート

まず冒頭でwebpack-stream、「webpack.config.js」、webpack、をインポートする。

const gulp = require('gulp');
const rename = require('gulp-rename');
const ejs = require('gulp-ejs');
const fs = require('fs');
const replace = require('gulp-replace'); 
const sass = require('gulp-sass');
// const typescript = require('gulp-typescript');
const webpackStream = require("webpack-stream");
const webpackConfig = require("./webpack.config");
const webpack = require("webpack");
const browserSync = require('browser-sync'); 
const plumber = require('gulp-plumber');

なお、gulp-typescriptは不要と思われるのでコメントアウトしてみる。

gulpタスクをコメントアウト

「gulp-typescript」のコンパイル設定もコメントアウトしてみる。

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

webpackタスクの設定

「gulp-typescript」に変わる「webpack」のタスクを作成する。

// webpackのバンドル実行
gulp.task("webpack", (done) => {
    webpackStream(webpackConfig, webpack)
      .pipe(gulp.dest("./dest/js"));
    done();
});

webpackの詳細設定は「webpack.config.js」に書いてあるのでgulp上はスッキリしている。

他のタスクと同じく、returnは使わずにdone()を使う方法で書いてみた。

watchタスクの設定

watchの監視設定を修正

// 監視ファイル
gulp.task('watch-files', (done) =>  {
    gulp.watch(["./src/ejs/**/*.ejs", "./src/json/**/*.json"], 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('webpack'));
    gulp.watch("./dest/js/*.js", gulp.task('browser-reload'));
    done();
});

tsファイルと紐付けるタスクはtsではなくwebpackに変更

タスク実行設定

最後にタスク実行設定を修正

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

gulp.parallel()のタスク名のtswebpackに変更

gulp起動

いよいよgulpを起動してみる。

npx gulp

お、ブラウザが立ち上がった! f:id:idr_zz:20200720191341j:plain

おみくじボタンを押してみると… f:id:idr_zz:20200720191405j:plain

やた!おみくじのアラートがでた! f:id:idr_zz:20200720191428j:plain 結果は「凶」だけど、久々に開いたので嬉しいw

TypeScriptを修正

コンパイルがリアルタイムで行われるか、「omikuji.ts」を修正してみる。

// おみくじ
export let omikuji = (): void => {
    let massage: string = 'あなたの運勢は「' + kekka() + '」です!';
    alert(massage);
}

アラートテキストの最後「です。」を「です!」に。

再度おみくじを引いてみると… f:id:idr_zz:20200720191833j:plain やた!テキストが「です!」に変わった!

modulesフォルダ作成(フォルダの移動)

今後、モジュールが増えることを考慮し、「modules」フォルダの中に「omikuji」モジュールを丸ごと移動したい。

念のため「Control + C」で一旦gulpを停止し、フォルダを作成、移動。 f:id:idr_zz:20200720192354j:plain

次にエントリーポイント「script.ts」の冒頭のインポートパスを変更…

import { omikuji } from "./modules/omikuji/omikuji";

と思ったら、自動的に/modulesが追加されてた!

もしかしたらこれはテキストエディタ(VSCode)の補完機能かもしれない(パスが変わってない場合は/modulesを追記する)。

この状態で再度gulpを起動!

npx gulp

問題なく動いているようだ。
f:id:idr_zz:20200720191833j:plain

よし、これが行けるならもっとやりたいことがある。

omikujiモジュールをさらに分割

「omikuji.ts」のコードを機能ごとにさらにモジュール分割してみる!

unseiモジュール作成

まず運勢のデータを「unsei.ts」として分割

// 運勢
export const unsei: string[] = [ 
    "大吉",
    "吉",
    "中吉",
    "小吉",
    "末吉",
    "凶",
    "大凶"
];
  • 変数のconstの前にexportを追加。

kekkaモジュール作成

次に結果を出す部分を「kekka.ts」として分割

import { unsei } from "./unsei";

// 結果
export let kekka = (): string => {
    let nmb: number = Math.floor(Math.random() * unsei.length );
    return unsei[nmb]; 
}
  • 冒頭でunseiモジュールをインポート
  • 変数のconstの前にexportを追加。

omikujiモジュールを修正

最後に「omikuji.js」を修正。2つのモジュールに該当するコードを削除。

import { kekka } from "./kekka";

// おみくじ
export let omikuji = (): void => {
    let massage: string = 'あなたの運勢は「' + kekka() + '」です!!';
    alert(massage);
}
  • 冒頭でkekkaモジュールをインポート
  • アラートテキストの「です!」を「です!!」に変更

これで動いてくれるはず!

動作確認(モジュール分割成功!)

tsファイルの構成はこうなった 。 f:id:idr_zz:20200720194332j:plain

モジュールはこのような流れになる。

unsei → kekka → omikuji → script

ブラウザで「おみくじ」ボタンを押すと… f:id:idr_zz:20200720194200j:plain やた!アラートのテキストが「です!!」になった。問題なくコンパイルされているようだ。

バンドル後のscript.jsの比較

バンドル後のファイルを比較してみる。

前回の「script.js」

!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t);var r=["大吉","吉","中吉","小吉","末吉","凶","大凶"],o=function(){var e,t="あなたの運勢は「"+(e=Math.floor(Math.random()*r.length),r[e]+"」です。");alert(t)};document.addEventListener("DOMContentLoaded",(function(){document.querySelector(".inner__text-btn").addEventListener("click",o,!1)}),!1)}]);

今回の「script.js」

!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t);var r=["大吉","吉","中吉","小吉","末吉","凶","大凶"],o=function(){var e,t="あなたの運勢は「"+(e=Math.floor(Math.random()*r.length),r[e]+"」です!!");alert(t)};document.addEventListener("DOMContentLoaded",(function(){document.querySelector(".inner__text-btn").addEventListener("click",o,!1)}),!1)}]);

ほとんど違いはないように見える。

GitHubにソースコードを公開!(2020/07/24追記)

フロントエンドメタ言語シリーズのソースコードをGitHubに公開しました!

※参考:GitHub - ryo-i/frontendMetaLanguage: メタ言語(EJS、Sass(SCSS)、TypeScript)で作ったWebページ。gulpでコンパイル。JSはWebpackでバンドル。

また、プレビュー画面もGitHub pagesで公開しています。

※参考:メタ言語同時コンパイル(EJS、Sass(SCSS)、TypeScript)

手付かずだった「other.html」にヘッダーフッターを追加したり、少しコードに修正を加えていますが、全体的なファイル構成は変わらないので、参考にしてください♪

最後に

ということで、今回はgulpでのwebpack実行を成功し、さらにモジュールを細かく再分割することも成功しました。

webpackでバンドルされた「script.js」は前回と今回で違いはほとんどなさそうです。そうであれば、やはりモジュールは機能ごとに細かく分割して作った方がコードが読みやすくなり、修正や再利用がしやすくなると感じました!

メタ言語シリーズ、モジュール分割編はEJS、Sass(SCSS)、TypeScriptと来て、今回で当初やりたいことは実現できました。

次回はまた別のテーマに入っていこうと思います。それではまた!


※参考:【メタ言語】HTMLテンプレートエンジン、AltCSS、AltJSのまとめ
qiita.com