• 最終更新日:

webpack 5でTypeScriptとPugの設定方法 - BabelによるIE11対策も紹介

webpack 5でTypeScriptとPugの設定方法 - BabelによるIE11対策も紹介

今回はwebpack5でTypeScriptとPugの設定方法について説明します。

設定する内容は以下です。

  • TypeScriptの設定
  • Pugの設定
  • BabelによるIE11対応

説明する環境は以下です。

  • macOS Catalina v10.15.5
  • Visual Studio Code v1.57.0
  • webpack v 5.51.1
  • node.jsはインストール済み
この記事の目次

webpack 5でTypeScriptとPugの設定方法

説明はTypeScriptとPugの設定が中心になります。それ以外の設定については以下の記事で説明しているので参考にしてみてください。

  • Dart Sass
  • CSSのAutoprefixerの設定
  • JavaScriptをBabelでトランスパイル
  • 画像の圧縮
  • 開発サーバーの立ち上げ、ホットリロード
  • ライセンス情報をjsファイルの中に含める

フォルダ構成について

【src】フォルダで作業したものが、【dist】フォルダに出力されます。

【src】フォルダと【dist】フォルダの中身は以下です。

webpackをインストール

まずはpackage.jsonを作成します。VSCodeを使っているなら【control + shift + @】でターミナルが表示されるので、以下のコマンドを打ってpackage.jsonを作成します。

npm init -y

次に以下のコマンドを打ってwebpackをインストールします。

npm i -D webpack webpack-cli

TypeScriptとPugを含むパッケージをインストール

パッケージをインストールします。TypeScriptとPug以外は必要に応じてインストールしてください。

npm i -D typescript #TypeScript
npm i -D ts-loader #TypeScript用のローダー
npm i -D pug #Pug
npm i -D pug-loader #PugとPug用のローダー
npm i -D html-webpack-plugin #HTML出力用
npm i D globule #ファイルの検索用

1行でインストールする場合は以下です。

npm i -D typescript ts-loader pug pug-loader html-webpack-plugin globule

インストールができるとpackage.jsonにパッケージ名とバージョンが載っているので確認してみましょう。

一部のパッケージがインストールできない場合は?

他にもパッケージをインストールしていると、依存関係がwebpackでは解決できず、一部のパッケージがインストールできない場合があります。その場合は以下のコマンドで強制的にインストールすることもできます。ただ、互換性を無視するため動作の確認は必要になります。

npm i --legacy-peer-deps 【パッケージ名】

詳しくはこちらの記事を参考にしてみてください。

webpack.config.jsに設定を書く

webpackの設定を書くために、webpack.config.jsというファイルを作成してTypeScriptとPugの設定を書いていきます。

//path モジュールの読み込み
const path = require("path");
//globule の読み込み
const globule = require("globule");
//html-webpack-pluginの読み込み
const HtmlWebpackPlugin = require("html-webpack-plugin");

const app = {
  // 読み込み先(srcの中のjsフォルダのinit.tsを読み込む)
  entry: path.resolve(__dirname, "src/js/init.ts"),
  //出力先(distの中のjsフォルダへinit.jsを出力)
  output: {
    filename: "./js/init.js",
    path: path.resolve(__dirname, "dist")
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: "ts-loader",
        exclude: /node_modules/
      },
      {
        test: /\.pug$/,
        use: [
          {
            loader: "pug-loader",
            options: {
              pretty: true
            }
          }
        ]
      }
    ]
  },
  target: ["web", "es5"],
  resolve: {
    // 拡張子を配列で指定
    extensions: [".ts", ".js"]
  },
  //プラグインの設定
  plugins: [],
  //source-map タイプのソースマップを出力
  devtool: "source-map",
  // node_modules を監視(watch)対象から除外
  watchOptions: {
    ignored: /node_modules/ //正規表現で指定
  }
};

//srcフォルダからpngを探す
const templates = globule.find("./src/templates/**/*.pug", {
  ignore: ["./src/templates/**/_*.pug"]
});

//pugファイルがある分だけhtmlに変換する
templates.forEach((template) => {
  const fileName = template.replace("./src/templates/", "").replace(".pug", ".html");
  app.plugins.push(
    new HtmlWebpackPlugin({
      filename: `${fileName}`,
      template: template,
      inject: false, //false, head, body, trueから選べる
      minify: false //本番環境でも圧縮しない
    })
  );
});

module.exports = app;

TypeScriptのトランスパイルの指定をする

TypeScriptをJavaScriptにトランスパイルする指定をtsconfig.jsonに書きます。ターミナルで以下のように打つとtsconfig.jsonが作成されます。

tsc --init

作成されたtsconfig.jsonを見てみると、いろいろ書かれていますが、重要な指定は以下です。

{
  "compilerOptions": {
    "target": "es5", //ES5に変換
    "module": "es2015", //モジュールをES Modulesとして出力
    "sourceMap": true, //soucemapを出力する
    "strict": true, //厳格な型チェック機能を有効にする
    "moduleResolution": "node", //node_modules からライブラリを読み込む
    "esModuleInterop": true, //CommonJS 形式のモジュールを、ES Modules でインポートできるようにする
    //以下はお好みで。
    "skipLibCheck": true, //型システムの精度を犠牲にすることで、コンパイル実行時間を削減
    "forceConsistentCasingInFileNames": true //import時にファイルパスの文字列で大文字小文字を区別するかどうか
  }
}

webpack.config.jsの中身を説明する

webpack.config.jsに書いたTypeScriptとPugの設定について説明します。

読み込み先と出力先を指定する

entryでは読み込み先を指定して、outputでは出力先を指定します。

つまり、srcのjsフォルダの中にあるinit.tsを読み込み、distのjsフォルダへinit.jsという名前で出力させています。

// 読み込み先(srcの中のjsフォルダのinit.tsを読み込む)
  entry: path.resolve(__dirname, "src/js/init.ts"),
//出力先(distの中のjsフォルダへinit.jsを出力)
  output: {
    filename: "./js/init.js",
    path: path.resolve(__dirname, "dist")
  },

TypeScriptの指定について

TypeScriptのファイルは以下の部分で読み込んでいます。excludeでnode_moduleフォルダの中は除外しています。

{
  test: /\.ts$/,
  use: "ts-loader",
  exclude: /node_modules/
},

tsファイルでimportを使ったときに拡張子をつけるとエラーが表示されます。それを防ぐために以下の指定をしています。

resolve: {
 // 拡張子を配列で指定
 extensions: [".ts", ".js"]
},

Pugの指定について

Pugファイルを以下の指定で読み込んでいます。

{
 test: /\.pug$/,
   use: [
     {
       loader: "pug-loader",
         options: {
           pretty: true
         }
       }
   ]
}

Pugファイルが増えても自動でdistファイルに出力してくれる設定は以下です。

//srcフォルダからpngを探す
const templates = globule.find("./src/templates/**/*.pug", {
  ignore: ["./src/templates/**/_*.pug"]
});

//pugファイルがある分だけhtmlに変換する
templates.forEach((template) => {
  const fileName = template.replace("./src/templates/", "").replace(".pug", ".html");
  app.plugins.push(
    new HtmlWebpackPlugin({
      filename: `${fileName}`,
      template: template,
      inject: false, //false, head, body, trueから選べる
      minify: false //本番環境でも圧縮しない
    })
  );
});

HtmlWebpackPluginのオプションについては以下です。

  • filename … ファイル名を指定
  • template … どのフォルダから読み込むのか指定
  • inject … trueにするとCSSファイルとJavascriptファイルの読み込みをHTMLファイルに書いてくれる。JavaScriptファイルはheadを指定すればhead内に、bodyを設定すればbodyが閉じる前に書き込まれる。
  • minify … HTMLファイルを圧縮するかどうか

webpackを動かす

webpackのコマンドを省略して打てるようにpackage.jsonに以下を追記します。

"scripts": {
    "prod": "NODE_ENV=production webpack --mode production",
    "dev": "webpack --mode development"
    "watch": "webpack --mode development --watch"
  }
  • prod … 本番環境として出力
  • dev … 開発環境として出力
  • watch … 開発環境としてファイルを監視、反映する

コマンドを打つときは、最初にnpm runをつけます

開発環境で出力するときはdevコマンドを打ちます

npm run dev

本番環境で出力するときはprodコマンドを打ちます。

npm run prod

IE11対応について

TypeScriptはES6からES5など、バージョンを切り替えてJavaScriptとして変換してくれますが、これだけでIE11に対応できるわけではありません。IE11のみ使えないプロパティやメソッドが多く存在するため、babelなどを使ってさらに変換する必要があります。

使えない機能については以下を参考にしてみてください。

例として、IE11では使えない以下の機能をTypeScriptを使ってES5でJavaScriptで変換してみます。

  • forEach
  • find
  • テンプレート文字列
  • Promise
let test: NodeList = document.querySelectorAll(".element");
test.forEach(function (elem, index) {
  console.log(elem);
});

const array1: number[] = [1, 19, 3, 160, 31];
const found = array1.find(function (element) {
  return element > 10;
});
console.log(found);

const text: string = "ほげほげ";
const massage = `メッセージは${text}です。`;
console.log(massage);

const promise = new Promise<void>((resolve) => {
  setTimeout(() => {
    resolve();
  }, 1000);
});
promise.then(() => {
  console.log("次の処理");
});

JavaScriptのES5で出力すると以下です。forEachやfindなどそのまま出力されているのでIE11では機能しません。

!(function () {
  "use strict";
  document.querySelectorAll(".element").forEach(function (o, n) {
    console.log(o);
  });
  var o = [1, 19, 3, 160, 31].find(function (o) {
    return o > 10;
  });
  console.log(o),
    console.log("メッセージはほげほげです。"),
    new Promise(function (o) {
      setTimeout(function () {
        o();
      }, 1e3);
    }).then(function () {
      console.log("次の処理");
    });
})();

Babelを使ってIE11対応する

IE11対策としてTypeScriptをBabelを通してからトランスパイルする方法があります。まずはwebpackにBabelを動かすのに必要な5つのパッケージをインストールします。

※@babel/polyfillは非推奨のため、core.jsを使います。

npm i -D @babel/core @babel/preset-env babel-loader @babel/preset-typescript core-js

webpack.config.jsの書き方も変わります。
先ほどまでの書き方は以下でした。

{
  test: /\.ts$/,
  use: "ts-loader",
  exclude: /node_modules/
},

そしてBabelを使って書くと以下のようになります。

{
  test: /\.(ts|js)$/,
  exclude: /node_modules/,
  use: [
    {
      loader: "babel-loader",
      // Babel のオプションを指定する
      options: {
        presets: [
          [
            "@babel/preset-env",
            {
              targets: {
                ie: 11,
                esmodules: true
               },
              useBuiltIns: "usage",
              corejs: { version: "3", proposals: true }
            }
          ],
          ["@babel/preset-typescript"]
        ]
      }
    }
  ]
},
  • targets … どのブラウザを対象のするか指定する
  • esmodules … trueだとES Moduleをターゲットにする。これがあるとbrowserslistに書かれた指定より、targetsにある指定が優先される
  • useBuiltIns … 必要なポリフィル分だけ自動で出力させる
  • corejs … インストールしているcore.jsのバージョンを明確に指定する必要がある

package.jsonでbrowserslistにIE11を指定しているなら、targetsの指定はいりません。例えばbrowserslistに別の指定がされていて、targetsにIE11の指定があり、esmodulesもtrueになっている場合は、targetsに書かれている指定が優先されます。

Babelの細かい指定については公式サイトを参照してください。

pakage.jsonでbrowserlistを書く場合は以下のようになります。

実際にトランスパイルしてみると以下のようにIE11で表示させるために必要なポリフィル分が出力されているのがわかります。

polyfill.ioを使ってIE11対応する

Babelを使わず、polyfillo.ioというサイトを使ってIE11対応することもできます。これはwebpackで特に設定する必要がなく、polyfillo.ioから必要なポリフィルを選択してリンクを読み込ませるだけです。

polyfillo.ioの良い点は、表示されたブラウザで足りないポリフィルのみ読み込みます。モダンなブザウザで問題なく表示できるのであれば、無駄なポリフィルは読み込まれません。またIE11対応が必要なくなったときに、リンク1行消すだけで済みます。

Babelでは対応できないappendやbefore、afterなどDOM機能のポリフィルが揃っているのも良いですね。

headeにリンクを貼るイメージは以下です。

さいごに

今回はwebpack5でTypeScriptとPugの設定方法について説明させてもらいました。基本的にはTypeScriptでES5に変換できていればほとんどのブラウザで表示が可能です。

IE11は2022年6月15日でサポートが終了するので、もう対策する必要はないかもしれませんが、念のため書いておきました。参考にしてみてください。