AlfredのワークフローをNodeで作成する

2020.11.11
AlfredのワークフローをNodeで作成する

AlfredのワークフローをNode.jsを使って作成します。


AlfredのワークフローはデフォルトでNodeをサポートしていないため、

NodeでAlfredのワークフローを開発出来るようにする Alfy というパッケージを使います

sindresorhus/alfy: Create Alfred workflows with ease



2020/11/10現在 バージョンが 0.10.0 と1.0.0以下なのが気になりますが、スター数も1k超えていて使いやすいパッケージになってます!


Alfy を使うことでAlfredのワークフローをNodeで作ることができます

またGitでの管理もしやすく出来るようなかゆいところに手が届く工夫がされています!(素晴らしい。。😭)





作成の手順や Alfy を使った際の仕組みで理解が難しい部分があったため細かく書いていきます。

また、作成したものをGitで管理したいのでその手順も合わせて記載します。

検証した環境

1 yarn 1.22.4
2 node 14.13.1
3 Alfy 0.10.0

作成手順

先に大まかな作成手順を

  • 1.GitHubのリポジトリ作成(Git管理しない場合はスキップ)
  • 2.ダミーのワークフローを作成
    • ワークフローの核となる info.plist を作成するため
  • 3.リポジトリのフォルダでAlfy導入
    • 合わせて2.で作った info.plist を移動
  • 4.index.jsに処理を追加
  • 5.alfy-initを実行してワークフローを管理するフォルダにシンボリックリンクを貼る



理解しづらいのが2と5かな、と思います。

Alfredはワークフローを「info.plist」というもので管理していて、


2でinfo.plistを作る

3でinfo.plistをGit管理するフォルダに移動

5でAlfredのワークフローを管理するフォルダ内にシンボリックリンクを貼ってinfo.plistを戻す


という流れになります。

今回作るもの

今回はこのようなものを作ります

まず一般的なAlfredのワークフローようにキーワードを入力し起動させ、
jsで処理(Alfyを使用)を実行
処理した内容を画面に出力します

1.GitHubのリポジトリ作成

Git管理しない人はスキップして下さい。


本題とそれるので簡単に。

先にリポジトリの作成とクローンをしておきます。


この段階でREADME.mdを作ってfirst commitしておくとブランチ切ることも出来るようになるのでおすすめ

2.ダミーのワークフローを作成

ダミーのワークフローを作成します。


なぜダミーを作るかというと、Alfredの全ての設定が書かれているinfo.plistを手にいれるため。

空白のワークフローを作成

左下の「+」ボタンから Blank Workflow を選択して空白のワークフローを作成します

Node.jsを使用する、というのを分かりやすくするためにNode.jsのアイコンを拝借してみました。


Bundle Idが必須になります、設定しないと以下のようなエラーが出るためです。

This script must be called from Alfred, $alfred_workflow_cache is missing. Make sure a Bundle ID is set.



Workflowの流れを作成

作成したワークフローで 右クリック → Inputs → Script Filter を選択し、




以下のように入力

  • Keyword・・alfredのアクションを起こすための好きな文字列(ここでは hoge で起動)
  • Placeholder Title・・Keyword(この場合hoge)入力後に表示されるテキスト、必須
    • なくても大丈夫やろ、と思ったらないと表示されません、注意
  • Language・・ /bin/bash
  • Script・・./node_modules/.bin/run-node index.js "$1"
    • AlfyでNodeを実行する際の定型文、と思って下さい




またjsで処理した値をテキスト表示したいため Outputs → Large Type を選択しScript Filterとつなげます。


Large Typeの内容はシンプルにScript Filterで選択した内容を表示するだけでいきます。


info.plistの確認

作成したワークフローを選択し「Open in Finder」で該当ワークフローのフォルダを開くと「info.plist」が作成されています。
(アイコンを設定したため、 icon.png も一緒に存在しています)

info.plistをのぞいてみるとワークフローがどのように処理され作られているか、イメージがつきやすいです。

3.リポジトリのフォルダでAlfy導入

いよいよAlfredの処理の部分をNodeで書ける環境を作っていきます!

info.plistをリポジトリフォルダにコピー

Gitで管理するリポジトリフォルダにinfo.plistをコピーします


私のようにアイコンなど何かしら追加のファイルがある人はそのファイルも含めてコピーします

alfred-workflow-node-sample/
  icon.png
  info.plist
  README.md

ダミーのワークフローを削除

先程Alfredで作ったダミーのワークフローを削除します!!

後々$ alfy-init というコマンドを叩いて復活するのでいったん今は削除!

Alfyの導入

肝心のAlfyを導入します!



Git管理するリポジトリフォルダで yarn initnpm init を実行

$ yarn init -y

Alfyをインストール

$ yarn add alfy



パッケージを追加したので「node_modules」フォルダが作られます。
git管理したくないので「.gitignore」を作成しGit管理対象から外します

.gitignore
node_modules/

ここまででフォルダ構成はこのようになってます

alfred-workflow-node-sample/
  icon.png
  info.plist
  node_modules/
  package.json
  README.md
  yarn.lock

4.index.jsに処理を追加

Prettier導入(お好みで)

Prettierを使うと自動フォーマットしてくれてコーディングで考えることが減るので、jsを書く前のこのタイミングでの導入がおすすめ!



本題とそれるので簡単に。

Prettierをインストール

$ yarn add -D prettier

設定をpackage.jsonに追記(設定内容はお好みで)

package.json
{
  "name": "alfred-workflow-node-sample",
  ・・・
  "prettier": {
    "singleQuote": true,
    "semi": false,
    "arrowParens": "avoid"
  }
}

index.jsを追加

ここまでくれば後一息!

Alfredの核となる処理の部分を「index.js」に書いていきます。


簡単なリスト表示するコードです

index.js
const alfy = require('alfy')

const items = [
  { title: 'hello, Alfred', subtitle: 'alfred subtitle', arg: 'alfred' },
  { title: 'hello, Node', subtitle: 'node subtitle', arg: 'node' },
]

alfy.output(items)

最終的な成果物を実行した際のイメージを見ると、コードで何をやっているか分かりやすいです



「hoge a」と謎な a が付いているのは、ワークフローを作る際に「Argument Required」で設定したからですな。😎笑



arg は次のアクションに渡される値です。

例えば、argにURLを設定して次のアクションとして「Open URL」を設定すれば、Webページを開く、ということが出来ます。

Alfyのコードについて

公式GitHubのREADME.mdが充実しているので1度目を通すのがおすすめ!
sindresorhus/alfy: Create Alfred workflows with ease




alfy.output() の引数に渡した値が一覧で表示されます。


outputは以下のように定義されていて

output(list, options?)

listにはオブジェクトの配列を渡す必要があります。



listに渡すオブジェクトで定義出来るものはAlfred公式ページに書かれています
Script Filter JSON Format - Workflow Input Objects - Alfred Help and Support

{"items": [
    {
        "uid": "desktop",
        "type": "file",
        "title": "Desktop",
        "subtitle": "~/Desktop",
        "arg": "~/Desktop",
        "autocomplete": "Desktop",
        "icon": {
            "type": "fileicon",
            "path": "~/Desktop"
        }
    }
]}



最低限 title があれば成り立つため以下のコードでも動作します

index.js
const alfy = require('alfy')

const items = [{ title: 'hello, Alfred' }, { title: 'hello, Node' }]
alfy.output(items)

5.alfy-initを実行

Alfyのパッケージの中には alfy-init というコマンドが含まれています。

alfy-initはAlfredのワークフローを管理するためのフォルダにシンボリックリンクを貼ってくれる、というもの。

node_modules/.bin/alfy-init
#!/usr/bin/env node
'use strict';
const execa = require('execa');

(async () => {
    try {
        await execa('alfred-link', {
            preferLocal: true,
            localDir: __dirname
        });
    } catch (error) {
        console.error(error);
        process.exit(1);
    }
})();

exaca というライブラリでシンボリックリンクを貼ってくれてそうですね



実行してみると、

$ npx alfy-init

ワークフローのフォルダにシンボリックリンクが作られてます。



この状態でAlfredを見てみると、

!!
削除したワークフローが復活してる。。


つまり、Alfredは workflows/<ワークフローのフォルダ>/info.plist でワークフローの管理をしてるんですね。

Alfredを実行する

いよいよ実行してみます

できた!😍

文字でかいなw