公開日: 2018-06-28 変更日: 2018-06-28 Download PDF

autoscale: true

JXA for TypeScript/Node.js


自己紹介

アイコン right

  • Name : azu
  • Twitter : @azu_re
  • Website: Web scratch, JSer.info

JXA(JavaScript for Automation)

  • AppleScriptのJavaScript版(WebKit)
  • macOSにはビルトインされているので何もしなくても使える
    • osascriptという実行エンジンがある
  • Home · JXA-Cookbook/JXA-Cookbook Wiki
  • jxa@apple-dev.groups.io | Wiki

JXAの問題

  • クラッシュする
  • リファレンスがまともにない
  • 実行環境がまともではない – 落ちまくる
  • エディタ環境もまともにない
  • デバッグが難しい
  • macOSのみ対応

JXAのいいところ

  • (JXA)スクリプトファイルでmacOSの自動化ができる
  • Objective-Cと同等の表現ができる
  • mac向けにネイティブアプリはなんか操作できる
  • mac向けのアプリでは一部便利な関数が用意されている

JXAのできること

  • Automatorでできること全部
  • アプリへのキー入力、開く閉じるなどの操作
    • ラベルを見つけて操作など無理やりなんとかできる
  • 対応アプリはアプリから情報を取得できる
    • omnifocusはTodoデータをデータベースから引っ張れるとか
  • ダイアログとかでインタラクティブなものを簡単につくれる
    • しょぼいGUIアプリみたいのが作りやすい

JXAの用途

  • アプリ作るほどじゃないけど自動化したいときに使う
    • シェルスクリプトのアプリ向け処理みたいな感じで使う
  • macOSネイティブなので、Alfredなど対応アプリも多い
    • osascript コマンドで実行できるのでシェル叩けるならどこからでも
    • メニューバーからも実行できるようにもなってる
    • AppleScriptをメニューバーから実行するための設定

スクリプト言語としてのJXA

  • JavaScriptエンジンはmacOsに依存(WebKit/JavaScriptCore)

  • 癖のあるJavaScriptフレームワーク上のJavaScriptという印象

    • プロパティがgetterじゃなくて name() のような関数呼び出し
  • AppleScriptより扱いやすい

    • 非英語圏の人間にとってはAppleScriptの構文が難しすぎる
    • Objective-Cよりはるかに英語的な構文

AppleScriptでChromeでGoogleを開く

set myLink to "https://google.com/"
tell application "Google Chrome"
    tell its window 1
        set theTabs to count of tabs
        set URL of tab 1 to myLink
    end tell
end tell

AppleScriptは英語

次の3行はAppleScriptでは同じ意味(itはtellで指定したオブジェクトなどを表す)

handlerX() of it
it's handlerX()
its handlerX()

次の2行はAppleScriptでは同じ意味(count関数に直接引数"abc"を渡して呼ぶ。前後どちらも対応)

count "abc"
"abc" count

鳶嶋工房 / AppleScript / Tips / 用語の同義語 より


JXAでChromeでGoogleを開く

const myLink = "https://google.com/"
const chrome = Application("Google Chrome");
const window = chrome.windows[0];
const theTab = window.tabs[0];
theTab.url = myLink;

Apple想定の使い方

right, スクリプトエディタ

  • スクリプトエディタ
    • エディタ かつ 実行環境

Apple想定の使い方 - リファレンス

  • スクリプトエディタから"用語説明"でインストールしてるアプリのリファレンスが見られる

  • 鳶嶋工房 / AppleScript / 入門 / 用語説明の読み方(命令編)

  • 鳶嶋工房 / AppleScript / 入門 / 用語説明の読み方(オブジェクト編)
  • 鳶嶋工房 / AppleScript / 入門 / 用語説明の読み方(値編)

fit, left

fit, right


JXAのデバッグ

  • Safariを開く
  • JXAでdebugger;をいれて実行する
  • Safariでデバッグできる
  • コンソールが使えるのでREPLライク
debugger;
  • JavaScriptによるMacの自動操作入門の情報まとめ | 大石制作ブログ

JXAの問題

  • リファレンスが読めない
    • リファレンスに載ってないものが多すぎる
  • まともなエディタ環境がない
    • コードの補完が効く環境がないに等しい
  • デバッグが難しすぎる
    • 落ちる、クラッシュ、リファレンスない
  • Node.jsでできるところはNode.jsでやりたい
    • pathとか文字列処理とか、JXAはライブラリの仕組みはあるけどnpmは使えない

リファレンス

fit, right

  • リファレンス自体の読み方が難しい
    • Tabというクラスは定義されているが、Tabを得る方法が載ってない…
    • すべてのプロパティやコマンドなどが載っているわけじゃない(自動生成されているレベル)
  • リファレンスは.sdefという複雑なXMLで書かれている
    • クラスの定義、enumの定義、定数、interfaceなどやたら高度な定義ができるXML

リファレンスから型定義を生成する

  • TypeScriptの型定義ファイルがあれば今どきのエディタは補完が効く
  • リファレンス(.sdef)はある種の型定義XMLに見えた
  • パースしてXMLから.d.tsを作ればいい
  • 作った: => @jxa/sdef-to-dts

sdef-to-ts.png


リファレンス to 型定義のテスト

  • 型定義(TypeScript)への変換が上手くいってるのかをテストする必要がある
    • でもこれがJXA触ったの初めて全くわからない
  • 既存のJXAで書かれたコードに型を適応すればOK
  • Mac : JavaScript for Automation (JXA) 例文辞典のサンプルコードを全部ダウンロードして、型チェックして通すテストを書きながら開発した

JXAの型定義ファイルを使ってコード補完する

  • macOSに入ってるアプリなどのリファレンスからTypeScriptの型定義を生成
    • @jxa/typesと@jxa/global-type
  • @jxa/global-typeはglobalにJXAのグローバルオブジェクトの定義を追加するモジュール
  • 読み込めば、ざっくりとした補完 + 型チェックが効くようになる
    • リファレンス自体に載ってないものが多いためanyで逃げている…
import "@jxa/global-type";
// your JXA application
var userName = Application("System Events").currentUser().name();

fit, example JXA

^ VSCodeやWebStormなどではある程度まともになる


JXAのデバッグ

  • console.logするとスクリプトエディタが落ちる
  • SafariにつないでもSafariごと落ちる
  • CLIのREPLから叩くと落ちても安全
  • 公式のREPLが osascript にビルトインされている

REPLを起動する

$ osascript -il JavaScript

JXAの自作REPL

  • osascript にREPLがあるのをしらなくてNode.jsで動くREPLを書いた
  • const repl = require('repl');
    • REPL | Node.js v10.5.0 Documentation
  • 作ったREPL: @jxa/repl
$ npx @jxa/repl
> Application("System Events").currentUser().name();
'azu'

JXAのデバッグ

  • JXAはとにかくデバッグしにくい
  • JXAで書かなければ解決する
  • JXAがいらない部分はNode.jsで書こう
  • => Node.jsの中でJXAを実行すればいい

JXA in Node.js

  • @jxa/run

    • Node.jsからJXAを実行して結果を取得するモジュール
  • 先駆者

    • sindresorhus/run-jxa: Run JXA code and get the result
    • wtfaremyinitials/osa2: Interact with Apple's Open Scripting Architecture in node.js

@jxa/run

import "@jxa/global-type";
import { run } from "@jxa/run";
export const currentUserName = () => {
    // このコールバック関数内はJXAとしてosascriptで実行される
    return run(() => {
        const sys = Application("System Events");
        return sys.currentUser().name();
    });
};

// メインとかは普通にNode.js
export const example = async () => {
    const userName = await currentUserName();
    return `User: ${userName}`;
};

JXA in Node.js

  • JXAが最小限になる
    • 最小限のJXAは書かないといけないのは同じだけど…
  • メインがNode.jsになってデバッグしやすい
  • npmを簡単に組み合わせることができてエコシステムが拡張できる
  • JXAで書いたものをモジュール化できる(npmで配れる)
    • つかえるのはmacOSだけなのは同じだけど

export type ModifierOption = {
    shift?: boolean;
    control?: boolean;
    option?: boolean;
    command?: boolean;
};
function createModifier(modifierOption: ModifierOption) {
    const modifiers = [];
    if (modifierOption.shift)
        modifiers.push("shift down");
    if (modifierOption.command)
        modifiers.push("command down");
    if (modifierOption.control)
        modifiers.push("control down");
    if (modifierOption.option)
        modifiers.push("option down");
    return modifiers;
}
// キーストロークを送信する関数 from JXA in Node.js
export function sendKeyStroke(key: string, modifierOption: ModifierOption) {
    const modifiers = createModifier(modifierOption);
    return run((key, modifiers) => {
        const SystemEvents = Application("System Events");
        SystemEvents.keystroke(key, { using: modifiers });
    }, key, modifiers);
}


ユースケース

  • Firefoxで開発者ツールを開いた状態でのスクリーンショットを撮って保存する
    • Seleniumだと開発者ツールを開けない?
    • screenshot-dev-tools.ts

まとめ

  • JXA in Node.jsをやるためにいろいろ書いた
  • JXA-userland/JXAのmonorepoで公開

  • Integration JXA with TypeScript

  • Run JXA from Node.js
    • See @jxa/run and @jxa/repl
  • Support Auto complete for editor/IDE via TypeScript definition file(.d.ts)
    • See @jxa/types and @jxa/global-type

似た概念

  • NILScript
    • WindowsでSpiderMonkeyを使ったもの
    • とても良くできていて便利だった