ロケーションコンテキストとパス
Yeomanのファイルユーティリティは、ディスク上に常に2つのロケーションコンテキストを持つという考えに基づいています。これらのコンテキストは、ジェネレーターが読み書きする可能性が最も高いフォルダです。
出力コンテキスト
最初のコンテキストは出力コンテキストです。出力先は、Yeomanが新しいアプリケーションをスキャフォールディングするフォルダです。これはユーザーのプロジェクトフォルダであり、スキャフォールディングの大部分を記述する場所です。
出力コンテキストは、現在の作業ディレクトリ、または.yo-rc.jsonファイルを含む最も近い親フォルダとして定義されます。.yo-rc.jsonファイルは、Yeomanプロジェクトのルートを定義します。このファイルにより、ユーザーはサブディレクトリでコマンドを実行し、プロジェクトでそれらを動作させることができます。これにより、エンドユーザーにとって一貫性のある動作が保証されます。
this.destinationRoot()を使用して出力パスを取得するか、this.destinationPath('sub/path')を使用してパスを結合することで取得できます。
// Given destination root is ~/projects
class extends Generator {
  paths() {
    this.destinationRoot();
    // returns '~/projects'
    this.destinationPath('index.js');
    // returns '~/projects/index.js'
  }
}
this.destinationRoot('new/path')を使用して手動で設定することもできます。しかし、一貫性を保つために、デフォルトの出力先を変更すべきではありません。
ユーザーがyoを実行している場所を知りたい場合は、this.contextRootを使用してパスを取得できます。これは.yo-rc.jsonでプロジェクトルートを決定する前、yoが呼び出された生のパスです。
テンプレートコンテキスト
テンプレートコンテキストは、テンプレートファイルを保存するフォルダです。通常は、読み取りとコピーを行うフォルダです。
テンプレートコンテキストは、デフォルトで./templates/として定義されています。this.sourceRoot('new/template/path')を使用して、このデフォルトを上書きできます。
this.sourceRoot()を使用してパス値を取得するか、this.templatePath('app/index.js')を使用してパスを結合することで取得できます。
class extends Generator {
  paths() {
    this.sourceRoot();
    // returns './templates'
    this.templatePath('index.js');
    // returns './templates/index.js'
  }
};
「インメモリ」ファイルシステム
Yeomanは、ユーザーのファイルを上書きする場合に非常に注意深いです。基本的に、既存のファイルに対するすべての書き込みは、競合解決プロセスを経ることになります。このプロセスでは、ユーザーがコンテンツをファイルに上書きするすべてのファイル書き込みを検証する必要があります。
この動作により、予期せぬ事態を防ぎ、エラーのリスクを軽減します。一方、これはすべてのファイルが非同期的にディスクに書き込まれることを意味します。
非同期APIは使用が困難なため、Yeomanは同期ファイルシステムAPIを提供します。このAPIでは、すべてのファイルがインメモリファイルシステムに書き込まれ、Yeomanの実行が完了した時点で一度だけディスクに書き込まれます。
このメモリファイルシステムは、すべての合成されたジェネレーター間で共有されます。
ファイルユーティリティ
ジェネレーターは、mem-fsエディターのインスタンスであるthis.fsで、すべてのファイルメソッドを公開します。使用可能なメソッドについては、モジュールドキュメントを確認してください。
this.fsがcommitを公開していることに注意してください。ジェネレーター内で呼び出すべきではありません。Yeomanは、実行ループの競合段階の後、内部的にこれを呼び出します。
例:テンプレートファイルのコピー
テンプレートファイルのコピーと処理を行う例を次に示します。
./templates/index.htmlの内容が次のようになっているとします。
<html>
  <head>
    <title><%= title %></title>
  </head>
</html>
次に、コンテンツをテンプレートとして処理しながらファイルをコピーするために、copyTplメソッドを使用します。copyTplはejsテンプレート構文を使用しています。
class extends Generator {
  writing() {
    this.fs.copyTpl(
      this.templatePath('index.html'),
      this.destinationPath('public/index.html'),
      { title: 'Templating with Yeoman' }
    );
  }
}
ジェネレーターの実行が完了すると、public/index.htmlには次の内容が含まれます。
<html>
  <head>
    <title>Templating with Yeoman</title>
  </head>
</html>
非常に一般的なシナリオは、プロンプト段階でユーザーの回答を保存し、テンプレートに使用することです。
class extends Generator {
  async prompting() {
    this.answers = await this.prompt([{
      type    : 'input',
      name    : 'title',
      message : 'Your project title',
    }]);
  }
  writing() {
    this.fs.copyTpl(
      this.templatePath('index.html'),
      this.destinationPath('public/index.html'),
      { title: this.answers.title } // user answer `title` used
    );
  }
}
ストリームによる出力ファイルの変換
ジェネレーターシステムでは、すべてのファイル書き込みにカスタムフィルターを適用できます。ファイルの自動整形、空白の正規化などは、完全に可能です。
Yeomanプロセスごとに1回、変更されたすべてのファイルをディスクに書き込みます。このプロセスは、vinylオブジェクトストリーム(gulpと同様)を通過します。ジェネレーターの作者は、ファイルパスやコンテンツを変更するためにtransformStreamを登録できます。
registerTransformStream()メソッドを使用して、新しいモディファイアを登録します。例を次に示します。
var beautify = require("gulp-beautify");
this.registerTransformStream(beautify({ indent_size: 2 }));
すべてのタイプのすべてのファイルがこのストリームを通過することに注意してください。サポートしていないファイルは、すべての変換ストリームが通過するようにしてください。gulp-ifやgulp-filterなどのツールは、無効なタイプをフィルタリングして通過させるのに役立ちます。
基本的に、生成されたファイルを書き込みフェーズ中に処理するために、Yeoman変換ストリームで任意のgulpプラグインを使用できます。
ヒント:既存ファイルのコンテンツの更新
既存のファイルを更新することは、必ずしも簡単な作業ではありません。最も信頼性の高い方法は、ファイルAST(抽象構文木)を解析して編集することです。このソリューションの主な問題は、ASTの編集が冗長で、少し理解しにくいことです。
いくつかの一般的なASTパーサーは次のとおりです。
- HTMLの解析にはCheerio。
- JavaScriptの解析にはEsprima。Esprima構文木を編集するためのより低レベルなAPIを提供するAST-Queryに興味があるかもしれません。
- JSONファイルの場合、ネイティブのJSONオブジェクトメソッドを使用できます。
- Gruntfileを動的に変更するにはGruntfile Editor。
RegExでコードファイルを解析することは危険な道であり、そうする前に、このCS人類学的な回答を読んで、RegEx解析の欠点を理解する必要があります。ASTツリーではなくRegExを使用して既存のファイルを編集することを選択した場合、注意深く、完全な単体テストを提供してください。- ユーザーのコードを壊さないでください。
