Home About Contact
UXP , InDesign , TypeScript , Deno

UXP InDesign, TypeScript を使う (Denoを使用)

Deno を使えば TypeScript を使って UXP InDesign スクリプトをバンドルできることがわかった。

以前軽く試したときは、deno bundle した段階で型関連のエラーが出てしまいバンドルできなかった。 でも考えてみたら(考えるほどでもないが)自分で型定義を用意して bundle 時とかコンパイル時に渡せば無問題なのでは? と思い立ち試したところ問題なくバンドルできた。

結論だけ知りたい場合はこちらのレポジトリの hello-world-ts を見てください。

このエントリーではごく簡単な UXP InDesign Scripting の例で説明します。

UXP InDesign Scripting Hello,World! with TypeScript

まずは環境の確認:

$ deno --version
deno 1.30.3 (release, x86_64-apple-darwin)
v8 10.9.194.5
typescript 4.9.4

JavaScript で記述

はじめに TypeScript ではなく JavaScript で題材とする Hello,World! を main.js ファイルに書きます。

const doc = app.documents.add();
const page = doc.pages.item(0);
const textFrame = page.textFrames.add({geometricBounds: ['20mm', '20mm', '30mm', '60mm']});
textFrame.contents = 'Hello,World!';

このコードは 拡張子を idjs にすればそのまま InDesign で実行できますが、一応今後の流れに沿う関係上 deno bundle します。

$ deno bundle main.js > hello-world.idjs

hello-world.idjs を UXP 対応バージョンの InDesign で実行すると(または UDT で実行すると):

UXP InDesign Scripting Hello,World! with TypeScript

このように Hello,World! 文字列がテキストフレームに入った InDesign 文書ができます。

TypeScript に書きかえ

この main.js を TypeScript に書きかえます。 ファイル名を main.ts にして、末尾のセミコロンを削除します(セミコロンは別に削除する必要もないですが、ここではそうします)。

main.ts

const doc = app.documents.add()
const page = doc.pages.item(0)
const textFrame = page.textFrames.add({geometricBounds: ['20mm', '20mm', '30mm', '60mm']})
textFrame.contents = 'Hello,World!'

実行してみる。

$ deno bundle main.ts > hello-world.idjs
Check file:///Users/foo/path-to/main.ts
error: TS2304 [ERROR]: Cannot find name 'app'.
const doc = app.documents.add()
            ~~~

エラーになりました。 app が問題になりました。

型宣言を追加

declare を使って型を宣言して問題を回避しましょう。

main.ts の先頭に以下の型定義を追加します。

declare var app: any

これで再度バンドル実行:

deno bundle main.ts > hello-world.idjs
Check file:///Users/foo/path-to/main.ts
Bundle file:///Users/foo/path-to/main.ts

うまくいきました。

これはこれでよいのですが、TypeScript を使いたい理由は、コンパイル時の型チェックが入ることです。 そこで、スクリプトに型を追加して、TypeScript らしくします。

declare var app: any

const doc: Document = app.documents.add()
const page: Page = doc.pages.item(0)
const textFrame: TextFrame = page.textFrames.add({geometricBounds: ['20mm', '20mm', '30mm', '60mm']})
textFrame.contents = 'Hello,World!'

例のための例のような状態ですが、とりあえず型を追記しました。

バンドルを実行。

$ deno bundle main.ts > hello-world.idjs
Check file:///Users/foo/path-to/main.ts
error: TS2304 [ERROR]: Cannot find name 'Document'.
const doc: Document = app.documents.add()
           ~~~~~~~~
TS2304 [ERROR]: Cannot find name 'Page'.
const page: Page = doc.pages.item(0)
            ~~~~

TS2304 [ERROR]: Cannot find name 'TextFrame'.
const textFrame: TextFrame = page.textFrames.add({geometricBounds: ['20mm', '20mm', '30mm', '60mm']})
                 ~~~~~~~~~

このエラーを回避するため型宣言を main.ts の先頭部分に書きます。

declare class TextFrame {
    contents: string
}
declare class TextFrames {
    add(params: any): TextFrame
}
declare class Page {
    textFrames: TextFrames
}
declare class Pages {
    item(index: number): Page
}
declare class Document {
    pages: Pages
}
declare class Documents {
    add(): Document
}
declare class Application {
    documents: Documents
}
declare var app: Application

実行します。

$ deno bundle main.ts > hello-world.idjs
Check file:///Users/foo/path-to/main.ts
Bundle file:///Users/foo/path-to/main.ts

はいできました。

型宣言を別ファイルに

型宣言部分は別ファイルにわけることができます。

記述方法は こちら https://deno.land/manual@v1.31.2/advanced/typescript/types をご覧ください。

ind.d.ts ファイルを作成して、main.ts の先頭に記述していた型宣言をここに移します。

declare class TextFrame {
    contents: string
}
declare class TextFrames {
    add(params: any): TextFrame
}
declare class Page {
    textFrames: TextFrames
}
declare class Pages {
    item(index: number): Page
}
declare class Document {
    pages: Pages
}
declare class Documents {
    add(): Document
}
declare class Application {
    documents: Documents
}
declare var app: Application

main.ts からは型宣言を削除して代わりに、ind.d.ts ファイル参照の記述を追加。

スクリプト全体は次のようになります。

/// <reference types="./ind.d.ts" />

const doc: Document = app.documents.add()
const page: Page = doc.pages.item(0)
const textFrame: TextFrame = page.textFrames.add({geometricBounds: ['20mm', '20mm', '30mm', '60mm']})

bundle を実行します。

$ deno bundle main.ts > hello-world.idjs
Check file:///Users/foo/path-to/main.ts
Bundle file:///Users/foo/path-to/main.ts

できました。 あとは生成された hello-world.idjs を実行して、意図通り Hello,World! ドキュメントが生成されるか確認しましょう。

まとめ

TypeScript で書けばコンパイル時(バンドル時)に型をチェックしてくれるので助かります。 ここでは、ind.d.ts ファイルを自分で用意しました。 これはそれなりに骨が折れる作業です。 しかし、これは InDesign UXP Scripting に共通する内容なので、 もしかすると既に InDesign 用の d.ts ファイルがネット上に存在しているかも。 もしまだそれが無いのであれば、そのうち Adobe か誰かすごい人がつくってくれるでしょう、たぶん。