プラグイン

はじめに § 1

RequireJSでは、異なる種類の��ソースを依存関係としてロードし、最適化ビルドに依存関係を含めることができるローダープラグインを作成できます。

既存のローダープラグインの例としては、text! プラグインと i18n! プラグインがあります。 text! プラグインはテキストのロードを処理し、i18n プラグインは、いくつかの異なるモジュールからのオブジェクトで構成されるJavaScriptオブジェクトのロードを処理します。オブジェクトにはローカライズされた文字列が含まれています。

RequireJS wiki には、より長いプラグインのリストがあります。

プラグイン名 § 2

ローダープラグインは、単なる別のモジュールですが、特定のAPIを実装しています。ローダープラグインは、オプティマイザの最適化にも参加できるため、ロードするリソースを最適化ビルドにインライン化できます。

**注意:** プラグインとその依存関係は、Node.jsやNashornのようなブラウザ以外の環境で実行できる必要があります。実行できない場合は、最適化ビルドに参加できるように、それらの環境で実行できる代替のプラグインビルダーモジュールを使用する必要があります。

依存関係に ! の前にモジュール名を付けることで、プラグインを参照できます。たとえば、「foo.js」という名前のプラグインを作成した場合、次のように使用します。


require(['foo!something/for/foo'], function (something) {
    //something is a reference to the resource
    //'something/for/foo' that was loaded by foo.js.
});

つまり、プラグインのモジュール名は ! セパレータの前にあります。 ! セパレータの後にある部分は、**リソース名**と呼ばれます。リソース名は通常のモジュール名のように見える場合があります。プラグインのモジュール名は任意の有効なモジュール名にすることができるため、たとえば、相対インジケータを使用できます。


require(['./foo!something/for/foo'], function (something) {
});

または、パッケージまたはディレクトリ(bar/foo.jsなど)内にある場合


require(['bar/foo!something/for/foo'], function (something) {
});

API § 3

RequireJSは最初にプラグインモジュールをロードし、残りの依存関係名をプラグインのload()メソッドに渡します。モジュール名の正規化と、オプティマイザの一部としてのプラグインの利用に役立つメソッドもいくつかあります。

完全なプラグインAPI

  • **load**: リソースをロードするために呼び出される関数。これは、プラグインが有用であるために実装する必要がある唯一の必須APIメソッドです。
  • **normalize**: リソース名を正規化する関数。これは、最適なキャッシングと最適化を提供するのに役立ちますが、リソース名がモジュール名でない場合にのみ必要です。
  • **write**: オプティマイザが、プラグインが最適化されたファイルにリソースの表現を書き出すタイミングを示すために使用します。
  • **pluginBuilder**: オプティマイザで最適化作業を行うために使用する必要があるモジュールのモジュール名文字列。オプティマイザの実行時には、プラグインモジュールの代わりにそのモジュールが使用されます。

load: function (name, parentRequire, onload, config) § 3.1

loadは関数であり、次の引数で呼び出されます。

  • **name**: 文字列。ロードするリソースの名前。これは、名前の ! セパレータの後にある部分です。したがって、モジュールが 'foo!something/for/foo' を要求した場合、foo モジュールの load 関数は 'something/for/foo' を名前として受け取ります。
  • **parentRequire**: 関数。他のモジュールをロードするために使用するローカルの「require」関数。この関数は、このプラグインリソースを要求したモジュール名に対して相対的なモジュール名を解決します。ローダープラグインが独自のIDに対して相対的に何かを require() したい場合は、独自の define 呼び出しで require を要求できます。この require 関数には、いくつかのユーティリティがあります。
    • **parentRequire.toUrl(moduleResource)**: moduleResource はモジュール名と拡張子です。たとえば、「view/templates/main.html」です。RequireJS構成に従って、リソースへのフルパスを返します。
    • **parentRequire.defined(moduleName)**: モジュールがすでにロードされ定義されている場合は true を返します。RequireJS 0.25.0 より前は require.isDefined と呼ばれていました。
    • **parentRequire.specified(moduleName)**: モジュールがすでに要求されているか、ロード処理中で、いずれかの時点で利用可能になる場合は true を返します。
  • **onload**: 関数。name の値で呼び出す関数。これは、プラグインがリソースのロードを完了したことをローダーに伝えます。プラグインがリソースのロードが正しく行われないことを意味するエラー状態を検出した場合、エラーオブジェクトを渡して **onload.error()** を呼び出すことができます。
  • **config**: オブジェクト。構成オブジェクト。これは、オプティマイザとWebアプリケーションがプラグインに構成情報を渡すための方法です。 i18n! プラグインはこれを使用して、Webアプリケーションが特定のロケールを強制する場合に、現在のロケールを取得します。オプティマイザは、このプラグイン(または pluginBuilder)がオプティマイザビルドの一部として呼び出された場合、config に **isBuild** プロパティを true に設定します。

何も興味深いことはせず、JSモジュールをロードするために通常のrequireを実行するだけのプラグインの例


define({
    load: function (name, req, onload, config) {
        //req has the same API as require().
        req([name], function (value) {
            onload(value);
        });
    }
});

一部のプラグインは、テキストとして取得されたJavaScriptを評価し、その評価されたJavaScriptをリソースの値として使用する必要がある場合があります。評価されたJavaScriptを評価するために使用できる、onload() 引数の関数 **onload.fromText()** があります。 eval() は RequireJS によってその JavaScript を評価するために使用され、RequireJS は評価されたテキスト内の匿名 define() 呼び出しに対して適切な処理を行い、その define() モジュールをリソースの値として使用します。

onload.fromText() の引数 (RequireJS 2.1.0 以降)

  • **text**: 文字列。評価する JavaScript の文字列。

onload.fromText() を使用するプラグインの load 関数の例


define({
    load: function (name, req, onload, config) {
        var url = req.toUrl(name + '.customFileExtension'),
            text;

        //Use a method to load the text (provided elsewhere)
        //by the plugin
        fetchText(url, function (text) {
            //Transform the text as appropriate for
            //the plugin by using a transform()
            //method provided elsewhere in the plugin.
            text = transform(text);

            //Have RequireJS execute the JavaScript within
            //the correct environment/context, and trigger the load
            //call for this resource.
            onload.fromText(text);
        });
    }
});

RequireJS 2.1.0 より前では、onload.fromText は最初の引数として moduleName を受け入れていました: onload.fromText(moduleName, text)。ローダープラグインは、onload.fromText() 呼び出しの後に手動で require([moduleName], onload) を呼び出す必要がありました。

ビルドに関する考慮事項: オプティマイザは、最適化ロジックを簡素化するために依存関係を**同期的に**トレースします。これは、ブラウザでの require.js の動作とは異なり、ローダープラグイン値のインライン化を可能にする最適化ステップに参加できるのは、依存関係を同期的に満たすことができるプラグインのみであることを意味します。そうでない場合、プラグインは config.isBuild が true の場合、load() をすぐに呼び出す必要があります。


define({
    load: function (name, req, onload, config) {
        if (config.isBuild) {
            //Indicate that the optimizer should not wait
            //for this resource any more and complete optimization.
            //This resource will be resolved dynamically during
            //run time in the web browser.
            onload();
        } else {
            //Do something else that can be async.
        }
    }
});

一部のプラグインはブラウザで非同期操作を実行しますが、Node / Nashorn で実行される場合はリソースのロードを同期的に完了することを選択します。これは text プラグインが行うことです。Node.js で AMD モジュールを実行し、amdefine を使用してプラグインの依存関係をロードするだけの場合は、Node.js の同期モジュールシステムと一致するように、それらも同期的に完了する必要があります。

normalize: function (name, normalize) § 3.2

**normalize** は、リソースを識別するために使用される名前を正規化するために呼び出されます。一部のリソースは相対パスを使用できるため、フルパスに正規化する必要があります。 normalize は、次の引数で呼び出されます。

  • **name**: 文字列。正規化するリソース名。
  • **normalize**: 関数。通常のモジュール名を正規化するために呼び出すことができる関数。

例:インデックスを指定してモジュール名をロードする **index!** プラグインがあるとします。これは、概念を説明するためだけの、人為的な例です。モジュールは、次のように index! リソースを参照できます。


define(['index!2?./a:./b:./c'], function (indexResource) {
    //indexResource will be the module that corresponds to './c'.
});

この場合、正規化された名前 './a'、'./b'、'./c' は、このリソースを要求しているモジュールに対して相対的に決定されます。 RequireJS は 'index!2?./a:./b:./c' を調べて './a'、'./b'、'./c' の名前を正規化する方法を知らないため、プラグインに尋ねる必要があります。これが normalize 呼び出しの目的です。

リソース名を適切に正規化することにより、ローダーは値を効果的にキャッシュし、オプティマイザで最適化されたビルドレイヤーを適切に構築できます。

**index!** プラグインは次のように記述できます。


(function () {

    //Helper function to parse the 'N?value:value:value'
    //format used in the resource name.
    function parse(name) {
        var parts = name.split('?'),
            index = parseInt(parts[0], 10),
            choices = parts[1].split(':'),
            choice = choices[index];

        return {
            index: index,
            choices: choices,
            choice: choice
        };
    }

    //Main module definition.
    define({
        normalize: function (name, normalize) {
            var parsed = parse(name),
                choices = parsed.choices;

            //Normalize each path choice.
            for (i = 0; i < choices.length; i++) {
                //Call the normalize() method passed in
                //to this function to normalize each
                //module name.
                choices[i] = normalize(choices[i]);
            }

            return parsed.index + '?' + choices.join(':');
        },

        load: function (name, req, onload, config) {
            req([parse(name).choice], function (value) {
                onload(value);
            });
        }
    });

}());

リソース名が通常のモジュール名である場合、normalize を実装する必要はありません。たとえば、text! プラグインは、依存関係名が 'text!./some/path.html' のように見えるため、normalize を実装しません。

プラグインが normalize を実装していない場合、ローダーは通常のモジュール名規則を使用してリソース名を正規化しようとします。

write: function (pluginName, moduleName, write) § 3.3

write はオプティマイザによってのみ使用され、プラグインが最適化されたレイヤーに属する何かを出力できる場合にのみ実装する必要があります。以下の引数で呼び出されます。

  • pluginName: 文字列。プラグインの**正規化された**名前。ほとんどのプラグインは名前なしで作成される(匿名プラグイン)ため、最適化されたファイルで使用するためにプラグインモジュールの正規化された名前を知っておくと便利です。
  • moduleName: 文字列。**正規化された**リソース名。
  • write: 関数。最適化されたファイルに書き込む出力の文字列で呼び出される関数。この関数には、プロパティ関数である **write.asModule(moduleName, text)** も含まれています。 asModule は、名前の挿入が必要な匿名 define 呼び出しが含まれている可能性があるモジュール、または最適化されたファイルのために抽出する必要がある暗黙的な require("") 依存関係が含まれているモジュールを書き出すために使用できます。 asModule は、CoffeeScript プラグインなどのテキスト変換プラグインに役立ちます。

text! プラグインは write を実装して、ロードしたテキストファイルの文字列値を書き出します。そのファイルからのスニペットです。


write: function (pluginName, moduleName, write) {
    //The text plugin keeps a map of strings it fetched
    //during the build process, in a buildMap object.
    if (moduleName in buildMap) {
        //jsEscape is an internal method for the text plugin
        //that is used to make the string safe
        //for embedding in a JS string.
        var text = jsEscape(buildMap[moduleName]);
        write("define('" + pluginName + "!" + moduleName  +
              "', function () { return '" + text + "';});\n");
    }
}

onLayerEnd: function (write, data) § 3.4

onLayerEnd はオプティマイザによってのみ使用され、オプティマイザのバージョン 2.1.0 以降でのみサポートされています。レイヤーのモジュールがレイヤーに書き込まれた後に呼び出されます。レイヤーの最後に配置する必要があるコードがある場合、またはプラグインが内部状態をリセットする必要がある場合に役立ちます。

1つの例:最初の write 呼び出しの一部として、レイヤーの先頭にいくつかのユーティリティ関数を書き出す必要があるプラグイン。プラグインは、次のレイヤーのユーティリティをいつ書き出すかを知るために、内部状態をいつリセットするかを知る必要があります。プラグインが onLayerEnd を実装する場合、内部状態をいつリセットするかを通知できます。

onLayerEnd は以下の引数で呼び出されます。

  • write: 関数。最適化されたレイヤーに書き込む出力の文字列で呼び出される関数。**この呼び出しではモジュールを書き出してはいけません。** ファイルに既に存在する他の define() 呼び出しと共存するために正しく正規化されません。 define() 以外のコードを書き出す場合にのみ役立ちます。
  • data: オブジェクト。レイヤーに関する情報。2つのプロパティのみがあります。
    • name: レイヤーのモジュール名。未定義の場合があります。
    • path: レイヤーのファイルパス。特に出力が別のスクリプトによって消費される文字列だけの場合、未定義の場合があります。

    writeFile: function (pluginName, name, parentRequire, write) § 3.5

    writeFile はオプティマイザによってのみ使用され、プラグインがプラグインによって処理される依存関係の代替バージョンを書き出す必要がある場合にのみ実装する必要があります。プロジェクト内のすべてのモジュールをスキャンしてすべてのプラグインの依存関係を探すのは少しコストがかかるため、この writeFile メソッドは、RequireJS オプティマイザのビルドプロファイルに **optimizeAllPluginResources: true** がある場合にのみ呼び出されます。 writeFile は以下の引数で呼び出されます。

    • pluginName: 文字列。プラグインの**正規化された**名前。ほとんどのプラグインは名前なしで作成される(匿名プラグイン)ため、最適化されたファイルで使用するためにプラグインモジュールの正規化された名前を知っておくと便利です。
    • name: 文字列。**正規化された**リソース名。
    • parentRequire: 関数。ローカルの「require」関数。 writeFile でこれを使用する主な目的は、parentRequire.toUrl() を呼び出してビルドディレクトリ内にあるファイルパスを生成することです。
    • write: 関数。2つの引数で呼び出される関数。
      • fileName: 文字列。書き込むファイルの名前。 parentRequire.toUrl() を相対パスで使用して、ビルド出力ディレクトリ内にあるファイル名を生成できます。
      • text: 文字列。ファイルの内容。UTF-8 エンコードされている必要があります。
      この関数には、プロパティ関数である **write.asModule(moduleName, fileName, text)** も含まれています。 asModule は、名前の挿入が必要な匿名 define 呼び出しが含まれている可能性があるモジュール、または最適化されたファイルのために抽出する必要がある暗黙的な require("") 依存関係が含まれているモジュールを書き出すために使用できます。

    writeFile の例については、text! プラグイン を参照してください。

    pluginBuilder § 3.6

    pluginBuilder は、プラグインがオプティマイザビルドの一部として使用される場合に、現在のプラグインの代わりに使用する別のモジュールを指す文字列にすることができます。

    プラグインは、ブラウザのような特定の環境に依存する非常に具体的なロジックを持つ場合があります。ただし、オプティマイザ内で実行すると、環境は大きく異なり、プラグインはブラウザにロードされる通常のプラグインの一部として配信したくない **write** プラグイン API 実装を持つ場合があります。このような場合、pluginBuilder を指定すると便利です。

    pluginBuilder の使用に関する注意事項

    • プラグインまたは pluginBuilder に名前付きモジュールを使用しないでください。 pluginBuilder のテキストコンテンツはプラグインファイルのコンテンツの代わりに使用されますが、これはファイルが define() を名前で呼び出さない場合にのみ機能します。
    • ビルドプロセスの一部として実行されるプラグインと pluginBuilder は、非常に限られた環境にあります。オプティマイザは、いくつかの異なる JS 環境で実行されます。プラグインをオプティマイザの一部として実行する場合、環境の仮定に注意してください。