逆引きマニュアル: webpack: ContextReplacementPluginの使い方

投稿日:

目的

requireの引数に変数がある場合に、 webpackはその変数を「全ての文字列」として解釈し、 マッチする全てのファイルをバンドル対象としますが、 その処理を上書きして、一部のファイルのみバンドル対象とするために使います。

使い方

Usageには以下のように定義されています。 2〜4番目の引数(?があるもの)は任意項目のようですが、 JavaScriptにこのような記法があるわけではなく、 自前で引数の型をチェックして、 どの項目が指定されたかどうかを判定しています1

new webpack.ContextReplacementPlugin(
  resourceRegExp: RegExp,
  newContentResource?: string,
  newContentRecursive?: boolean,
  newContentRegExp?: RegExp
)

一番簡単な例として、Moment.jsのロケール設定を置き換えるものが挙げられています。

new webpack.ContextReplacementPlugin(
  /moment[\/\\]locale$/,
  /de|fr|hu/
)

1番目の引数はresourceRegExp、2番目の引数はnewContentRegExpなのですが、 これをどう指定すればいいのか、 それはMoment.jsのソースコードを読む必要があります。

Moment.js 2.20.1の1844行目あたりに以下のコードがあります。

var aliasedRequire = require;
aliasedRequire('./locale/' + name);

1行目はrequireのaliasを作っているだけなので、問題は2行目です。 どうやらこれをwebpackが解釈する時に、 name変数を任意の文字列にマッチさせています。

ここでは./locale/.*になっていますが、 実際はMoment.jsの内部でのrequireなので、 実ファイルでは、node_modules/moment/locale/.*にマッチします。

このとき、置き換え前のディレクトリはmoment/locale, マッチする正規表現は.*になっています。 これをそれぞれ、/moment[\/\\]locale$/, /de|fr|hu/という正規表現に置き換えるのがこのプラグインの目的です。

なお、この/moment/locale$/ でなく /moment[\/\\]locale$/ になっている 理由は、Windows対応のためのようです。

また、newContentRegexpに拡張子がついていないのは、 moduleには拡張子は不要(自動補完される)ためです。

公式サイト

マニュアル