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つ目が値」というパターンにぴったりなのです。