CEP上のExtendScriptをモダンな環境で開発する(Webpack + TypeScript + Babel)

ExtendScript

みなさんご存知の通りExtendScriptはECMA3の古いシステムがベースとなっています。小規模なExtensionの開発をするのでしたらそのまま古い環境で書いても問題ないのですが問題はコードの規模が大きくなればなるほど問題が増えていく事です。CEP下のExtendScriptは従来のコンソールの値が表示されなくデバッグがやりづらいという問題もありましたがExtendScriptDebuggerver2の登場によってこれらの問題は解決されつつあります。しかしimport、export文が使えない事によるグローバル汚染。パネルとExtendScriptでやりとりを行う時の値の受け渡しで失敗しやすい、失敗してもどこでエラーが出ているのかわかりづらいという問題は未だに解決されていません。ということで今回はこれらの問題をWebpack + TypeScript + Babelで問題を解決してある程度の規模の環境下でも開発しやすい環境を作っています。今回の記事ではWebpackとTypeScriptを扱った記事になるのである程度それらの知識に精通している方が対象となります。(Githubのサンプルコードもありますのでご参考に

Webpackをそのまま通すと日本語が文字化けします。
ExtendScriptで書いたコードをビルドしたら日本語が文字化けした件の記事を参考にwebpack-utf8-bomプラグインをGitHubのサンプルコードに追加しました。

開発の前に

まずExtendScriptはECMA3です。今回WebpackによるコンパイルとBabelによるコンパイルを行うのですが世の中のBabelによるコンパイルといったらECMA5への変換となります。これはIE11がECMA5までをサポートしていた影響なのですが逆にECMA5以下のコンパイルは世の中的にほとんど行われていません。なので基本のBabelの設定を行ってもExtendScriptの場合コンパイル後に実行できない、という事が起こります。この辺りを考慮してWebpackの環境を構築する必要があります。

環境構築

とりあえず必要なパッケージをインストールします。今回しようしたパッケージを以下まとめるので参考にしてください。

Babel、Webpack、TypeScriptの基本的なパッケージとExtendScritpの型データのまとまったtypes-for-adobeをインストールしています。今回IllustratorのExtensionのために使用していますが他のアプリケーションも対応しています。extendscript-es5-shimはECMA3環境下でもArrayメソッドが使用できるようにするためのpolyfillです。別途json2のpolyfillファイルをパッケージではなく個別のファイルとしてダウンロードして保存しています。これより設定のためのファイルを作成しますがWebpackは開発用と本番用でファイルを分けます。まずは共通のファイル。

今回重要なのはtargetとしてbrowserslistを指定しています。ECMA3にコンパイルしなければならないので別途.browserslistrcのファイルでie6をターゲットにします。後々説明しますが環境変数も今回扱いますので開発環境か本番環境かの状態の環境変数をwebpack.DefinePluginで管理します。また書き出し後のファイル名の拡張子がjsxだとうまくいかなかったのでjsにしています。

次にtypeScriptの設定です。ターゲットはもちろんES3です。その他types-for-adobeの中の型データを取り込むためにその中の型データのパスを指定しています。Illustrator以外のアプリケーションを使用する場合は指定のアプリケーションの型のパスを入れます。そして今回環境変数も使用するので別途typesフォルダーの中の型データを読み込みます。

これでWebpackの基本の設定は以上になります。

$.writelnメソッドを本番環境時に実行させたくない

web向けでしたterser-webpack-pluginプラグインのdrop_consoleでconsole.logを本番環境時にそのまま取り除くことができるのですがExtendScriptの場合console.logではなく$.writelnメソッドです。なのでterser-webpack-pluginプラグインではなく以下のように環境変数DEBUGを使用して開発環境時は$.writelnメソッドが走る
ようにして本番環境時は走らないようにしています。

ExtendScriptのスコープについて

Webpackで書き出されるコードは基本スコープで囲まれています。これによりグローバル汚染を気にせず各ファイルのコーディングができるわけですが問題はCEPのパネルからExtendScriptを実行する場合なんらかしらの関数をグローバルスコープで宣言する必要があります。しかしグローバルレベルでの安易な宣言はグローバルの汚染につながりWebpackを使用するメリットが無くなってしまいます。そこで最小限のグローバル関数の宣言によりグローバル汚染を防ぐようにします。やり方はトップレベルの関数内からCEPパネルから受け取った値を元に各ローカルスコープ内の関数に分岐すれば良いだけです。

今回switchFuncs関数から各ローカルスコープ内の関数に分岐させています。そしてパネル側からの実行の方法ですがPhotoshop以外の環境でもパネルが閉じた時と開いた時にリロードさせて一々ホストアプリケーションを閉じないとコードの修正が反映されない、という事を避けたいです。これにはwindow(csinterface)のイベントにリロードイベントを追加します。詳しくはこちらの記事へ。

hostScript関数は先程のswitchFuncs関数の呼び出しとデバッグレベルの宣言のみとなっています。(ExtendScriptDebuggerでデバッグする為詳しくはこちら)switchFuncs関数は別途csInterface.evalScriptメソッドで読み込めばその他のコードを修正後にリロードイベントが発火すればExtendScriptの内容が更新されます。これで一々アプリケーションを再起動する必要がありません。但しそれでもグローバル汚染によるコードの不具合が絶対無いとは言い切れないのでExtendScriptの調子が悪いと感じたら一度アプリケーションを再起動した方がいいかもしれません。また本番環境時はリロードの度にcsInterface.evalScriptメソッドの再読み込みは極力控えた方が良いでしょう。ちなみにhostScriptを経由したくない場合は以下のようなコードでcsInterface.evalScriptメソッドでhostScript以外のExtendScriptと直接やりとりできます。

以上になります。パネルとExtendScriptのやりとりですがTypeScriptでの型管理を行えばチグハグなオブジェクトをお互い受け渡す、ということもなくなります。また最新のExtendScriptDebuggerを使えばコールバックでコンソールも拾えるのでより開発環境が快適になります。但しEC2015からEC3にコンパイルしているといってもこのままだと一部機能が使えません。例えばgetterやsetter、ExtendScriptのネイティブレベルの配列のようなオブジェクト(app.activeDocument.layersのような)をスプレッド構文を使用したArrayオブジェクトへの変換などができません。さらに最新の環境を構築したい場合はさらに工夫が必要です。

最後にGitHubにサンプルコードを上げておきます。Illustrator用のExtensionで現在開いているドキュメントとレイヤーの簡単な情報を取得する簡単なExtensionです。Reactで作られていますが今回CEP部分のReactの部分は必ずしも理解する必要はありません。

modernCEPEnvironment
Public

参考

extendscript-ts-sample-project

ExtendScriptで書いたコードをビルドしたら日本語が文字化けした件

Beer 寄付してサイトを応援する。