Madogiwa Blog

主に技術系の学習メモに使っていきます。

Webpackでentryとentryを読み込むhtmlを動的に設定するMEMO

Webpackを使って複数entryを指定して、それらを読み込むhtmlをhtml-webpack-pluginを使って用意すると下記のような感じの設定になるかと思うのですが、 これだとfileが増えるたびにwebpack.config.jsを変更しないといけないので不便です。。。

module.exports = {
  entry: {
    index: './src/entries/index.js',
    home: './src/entries/home.js',
  },
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: './src/pages/index.html',
      chunks: [index]
    }),
    new HtmlWebpackPlugin({
      filename: 'home.html',
      template: './src/pages/home.html',
      chunks: [home]
    })
  ]
}

今回はそれに対応するためにentryとentryを読み込むhtmlを動的に設定する方法を調べて実装してみたので、そのへんもMEMOしておきます。

entries配下のファイルをentryに指定する

方針としては、下記のような形を考えてみました。

  • 指定ディレクトリに配置されたjs/tsファイルを取得
  • 拡張子を除外したファイル名をkey、ファイルパスをvalとしたobjectを作成
  • それをentryに設定

そういうわけで指定ディレクトリに配置されたjs/tsファイルを取得して、 拡張子を除外したファイル名をkey、ファイルパスをvalとしたobjectを作成するmoduleを作ってみました。

const glob = require('glob');
const path = require('path');

/**
 * 指定したentryのルートディレクトリ配下のjsまたはtsファイルのファイル名とパスのobjectを取得
 * 例) /entries/foo.ts => { foo: "/entries/foo.ts" }
 * ※ルートディレクトリ配下にディレクトリを切り、そこにjs,tsファイルをおいた場合は無視される。
 * @param {string} entryRoot entryのルートディレクトリ
 */
module.exports = function getEntries(entryRoot) {
  let ret = {}
  const filePaths = glob.sync(`${entryRoot}/*.{js,ts}`)
  filePaths.forEach(filePath => { ret[path.basename(filePath, path.extname(filePath))] = filePath })
  return ret
}

このmoduleを使って下記のようにしてあげると特定のディレクトリ配下のファイルを自動的にentryとして指定できます💪

const getEntries = require('./config/webpack/utils/getEntries.js')
const entries = getEntries('./src/entries/')

module.exports = {
  entry: entries,
  plugins: [
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: './src/pages/index.html',
      chunks: [index]
    }),
    new HtmlWebpackPlugin({
      filename: 'home.html',
      template: './src/pages/home.html',
      chunks: [home]
    })
  ]
}

entryとしたファイルと同名のhtmlファイルをtemplateとして指定する

方針としては、下記のような形を考えてみました。

  • entryとテンプレートのhtmlを配置しているディレクトリのパスを引数で取る
  • ディレクトリ配下のentryと同名のファイルとentry名を紐付けるようにHtmlWebpackPluginのインスタンスを生成して配列にして返却
  • 生成した配列をpluginsに展開して設定

そういうわけでentryとテンプレートのhtmlを配置しているディレクトリのパスを引数で取って、ディレクトリ配下のentryと同名のファイルとentry名を紐付けるようにHtmlWebpackPluginのインスタンスを生成して配列にして返却するmoduleを作ってみました。

const HtmlWebpackPlugin = require('html-webpack-plugin')

/**
 * 指定したentriesと同名のhtmlテンプレートを読み込みHtmlWebpackPluginを生成する
 * ※ルートディレクトリ配下にディレクトリを切り、そこにhtmlファイルをおいた場合は無視される。
 * @param {object} entries { foo: "/entries/foo.ts" }のようなentryを表すオブジェクト
 * @param {string} templateRootPath entryのjsを読み込むtemplateファイル(html)のRootパス
 */
module.exports = function buildHtmlWebpackPlugins(entries, templateRootPath) {
  return Object.keys(entries, templateRootPath).map(
    entryName => new HtmlWebpackPlugin({
      filename: `${entryName}.html`,
      template: `${templateRootPath}${entryName}.html`,
      chunks: [entryName]
    })
  )
}

このmoduleを使って下記のようにしてあげると指定ディレクトリのhtmlファイルをentryと紐付けることが出来ます🤝

const getEntries = require('./config/webpack/utils/getEntries.js')
const entries = getEntries('./src/entries/')

module.exports = {
  entry: entries,
  plugins: [
    ...buildHtmlWebpackPlugins(entries, './src/templates)
  ]
}

おわりに

今回はWebpackで動的に色々やる方法を調べて、自分なりに方法を考えてみました。これが正しいかはわからないですが参考になれば🙏

参考

moneyforward.com

nodejs.org

github.com

developer.mozilla.org