Webpackには、CommonJSのrequireよりも強力な、依存関係処理機能があります。
ふつうのrequireとWebpack
npmで入れたライブラリを指定する場合にしても、自分で作ったファイルを呼び出す場合にしても、ほとんどの状況でrequireすべきファイルはソースコードの時点で決まっているものです。
Webpackでは、リテラルでrequireを行った場合、コンパイル後にはrequireの引数が、モジュールの番号に置き換わっています。
動的なrequireとコンテキスト
ただ、CommonJS Modulesの仕様でも、requireが取る引数は一定の書式の「文字列」となっているだけで、もちろんリテラル限定なんてことはありません。任意の文字列を指定することができます。
このような場合、ソースコードの時点では何をrequireするか決まらないので、固定的にコンパイルすることはもちろん不可能です。Webpackでは、requireするモジュールが決まりきらないコードを発見すると、「requireコンテキスト」を生成します。
requireコンテキストは、ソースコードから判別できる接頭辞・接尾辞(フォルダ位置・拡張子など)からできるだけ絞り込んだ上で、候補になりうるすべてのモジュールを用意した上で、最終的にrequireを呼び出した文字列で実際にrequireするものを決定する、というような流れになっています。
コンテキストを自分で作る
上で述べた「requireコンテキスト」は、Webpackが自動で用意するだけではなく、require.contextを呼び出すことで、自分で作ることもできます。
const context = require.context(directory, useSubdirectories = false, regExp = /^\.\//)
引数の意味は次のようになっていますが、コンパイル時に解決しないといけないため、すべてリテラルである必要があります。
-
directory…どこのディレクトリの中身をrequireの対象とするか -
useSubdirectories…対象のディレクトリ直下だけ拾うならfalse、以下の全ディレクトリを対象にするならtrue -
regExp…対象になるファイルが満たすべき条件を書いた正規表現(/\.js$/のように、拡張子指定に使うと便利です)
コンテキストの操作
コンテキストを作っただけでは、requireは行われません。このcontextは、以下のようなものからなっています。
-
context(パス)…指定されたパスのモジュールをrequireする(もちろん、コンテキストになければ失敗) -
context.resolve(パス)…指定されたパスをモジュール番号に変換する(もちろん、コンテキストになければ失敗) -
context.keys()…コンテキストに含まれるパスの配列を返す -
context.id…コンテキスト自体のモジュール番号
実用例
Riot.jsとWebpackを組み合わせる場合、riot/tag-loaderがあるので、これを使えば.tagファイルもrequireできますし、require('riot')で呼び出されるRiot本体に、requireした時点でタグとして登録されます。
Riotのタグをセットする側でriot.mount('*')のようにするのであれば、requireしたものを自分で保存しておく必要もない、ということになります。ということで、タグは「ただrequireしておくだけでいい」ということになります。
このような場合、以下のコードで一括requireできます。
function allRequire(context){
context.keys().forEach(context);
}
allRequire(require.context('./path/to/tags/', true, /\.tag$/))
forEach(context)が直感的ではありませんが、context自体が「モジュールのパスを受け取ってrequireする関数」なので、forEachが投げてくる「1つ目が値」というパターンにぴったりなのです。