CEPからExtendScriptを実行する
皆さんご存知のようにCEPはパネル側とExtendScript側で分かれています。 これはパネル側とExtendScriptで異なるJavaScriptになっているわけです。 一番有名な方法としてパネル側からExtendScriptを実行する方法としてmanifestに 登録したhostscriptを実行するのが一番知られていると思います。
このhostscriptはmanifestに登録されたjsxファイルをcsInterface.evalScriptが実行されたタイミングで 即時実行します。試しに以下のようにalertを関数の外のグローバルスコープに追加して csInterface.evalScriptがsayHello関数を呼び出すとそのalert自体呼び出したわけでなくても直後にalertが同時に実行されます。

なぜcsInterface.evalScriptメソッド内で関数を実行するのか。
見慣れたcsInterface.evalScriptメソッドを見るとご存知の通りhostscript内のsayHello関数を実行しています。 これはhostscript内のグローバルスコープ内の関数をcsInterface.evalScriptが実行された時点で読み込んでいるからです。 一度jsxファイル内のグローバルスコープの関数は読み込んだ後はパネル上で保持されてcsInterface.evalScriptメソッドで 実行するとそのまま読み込んだ内容を実行するからです。即時関数や関数の実行をグローバルスコープ内で宣言するとそのままcsInterface.evalScrip メソッドが実行されたタイミングで実行されるので極力グローバルスコープ内に関数を実行させるのは控えた方が良いでしょう。 次に下のように新しくgoodbye関数を追加してcsInterface.evalScriptメソッドでgoodbye関数を実行すると そのまま関数が実行されます。
これで簡単なExtendScriptのやりとりの方法はわかります。しかしこれではhostscriptしか使えません。ファイルをいくつかに 分割する場合はどうすれば良いでしょうか?
csInterface.evalScriptメソッド内で$.evalFileメソッドを使用する
csInterface.evalScriptメソッド内で$.evalFileメソッドを実行して引数内にExtendScriptのパスを渡すと そのまま対象のExtendScriptを即時に実行してくれます。
事前にExtendScriptを読み込んでおけば上記のようにExtendScript内のグローバルスコープ内にある関数をいつでも 実行できるようになります。またmanifestで登録したhostscriptとの違いはcsInterface.evalScriptメソッドが実行されたタイミングで 読み込まれてその後はリロードされないのに対してこのように$.evalFileメソッドを実行して読み込んだjsxファイルは Extensionのリロードの度に読み込み直してコードの修正を反映してくれます。尚Extensionのリロードに関してはPersistenceの概念を参照してください。 逆にhostscriptは一回きりの読み込みなのでコードを修正した後のはアプリケーションを再起動しないと反映されません。但しPhotoshopの場合はhostscripもリロード後に読み込み直してくれるみたいです。
ExtendScript上のグローバルスコープ
何度も述べたようにcsInterface.evalScriptメソッドを通じてExtendScript上のグローバルスコープの関数を読み込んでいるわけですがこれを利用して 別々のファイルの関数、変数を事前に読み込むことによって別のファイルから関数の実行、変数の利用が可能です。以下例になります。
但しグローバルスコープ内での宣言になるのでどうしてもグローバル汚染と変数名の衝突になりやすいです。これを使用して 複数のファイルにコードを分ける場合は関数スコープを使用して工夫する必要があります。
パネル側からExtendScriptに値を投げる
次にパネル側からExtendScriptに値を投げます。これは普通の関数のようにcsInterface.evalScriptメソッド内に渡したい引数の値をそのまま 渡せばそのままExtendScript側に渡してくれます。
しかし原則string型の値しか渡せません。なのでObject型の値を渡してもundefinedと返ってきます。
それではObject型を渡したい場合はどうすれば良いでしょうか?オブジェクトをstring型にしてコードに組み込めば良いわけです。以下例になります。
JSON.stringifyでstring型に変換すれば良いだけです。このevalScriptメソッドのstringのスコープはExtendScriptを実行するスコープになっているので string型で渡してもそのままオブジェクトになります。面倒でなければこの関数内にそのままExtendScriptを書くこともできます。

アラートがパネルのアラートではなくホストアプケーションからのアラートになっていることに注意してください。 このように書くこともできますがactiveDocumentを呼び出してもアプケーションの名前が出てきたりしたので原則ファイル内の コードを呼び出すようにした方が良いでしょう。
ホストアプリケーションからパネル側に値を渡したい
ドキュメントの情報なりレイヤーの情報なりと何かアプケーション側の情報をパネル側に表示したい場合も あります。このためにはcsInterface.evalScript内から直接呼び出したグローバルスコープ内の関数からreturnで 値を返せば良いのです。
string型は返せました。しかしオブジェクトはどうでしょう。
結果は[object Object]です。オブジェクトの中身が返ってくるわけではありません。これでは元も子もないのでObjectをstring型にして返します、が 皆さんご存知のようにExtendScriptはECMA3です。JSON.stringifyは使えません。polyfillが必要ですね。ライブラリーの記事でも 紹介したようにJSON2を使用します。 事前に読み込んでおけば使用できるようになります。
これでExtendScriptとパネル側のやりとりができるようになりました。但しホストアプリケーションのネイティブオブジェクト をそのまま渡すとエラーでうまくいきません。
この場合面倒でも必要なプロパティーだけ抜き出しましょう。
また汎用性を持たせるためにcsInterface.evalScriptメソッドのコールバックをPromiseを返す関数化しておけば コールバックで返してくる値の処理がasync,awaitでシンプルに処理できます。
ExtendScriptの読み込みをExtendScriptから行いたい
ここまではパネル側から複数のExtendScriptの読み込みを行うことを前提の話でしたが実はExtendScriptからも 他のExtendScriptの読み込みも方法もあります。しかしこれらのやり方は一部の環境で動きませんので注意。 ExtendSctiptから他のExtendScriptを読み込み場合一番使われている#includeはですがCEP環境下だと動きません。エラーが起きて止まるので 代わりに似たようなものだと@includeとなります。しかしPhotoshopの場合hostscriptでは動きません。
もしくは$.evaFileメソッドをExtendScriptを実行すると他のファイルを読み込むことができます。$.includePathで Scriptファイルそれ自身のパスを取得できますのでこれを使用して相対的にファイルパスを取得できます。関数内で使用してください。
但し$.includePathはPhotoshopでは空の文字列しか返さないので使えません。$.fileNameを使った方が安全でしょう。
その他ExtendScript内から他のExtendScriptにアクセスする仕様は正直不明な部分が多いので使用する場合は詳しくは最後の参考URLも参照して 試してください。またどの方法でも規模が大きくなるとECMA3での開発そのものが苦しくなってきます。その場合はWebpack+TypeScript+Babelで モダンな開発環境下で開発する方法もブログで紹介しています。