Posted at

Monacaのアップロードやダウンロードが完了したら通知してくれるコマンドを作った(monaca-cli-notification)


はじめに

みなさんmonaca使ってますか?

巷ではReactNativeやFlutterが流行っていますがmonacaもReactのソースを(ほぼ)そのまま転用出来るので悪くないと思っています。

ただ....

ただ.....!!


ビルドが遅い!!!!

RNやFlutterが実機でもホットリロードで即反映なんて言われていますが、monacaはuploadしてからremote buildするまで結構時間がかかります。

私のプロジェクトでも大体15分くらいかかります。

そのため、1回のビルドに



  1. monaca uploadを実行してからQiitaを30分くらい巡回してアップロードの終了に気づく


  2. monaca remote buildを実行してからQiitaを30分くらい巡回してビルドの終了に気づく

合計1時間くらいかかります:innocent:

ちゃんと見てろって話なんですが、なにかと作業効率が悪いのでmonaca-cliの結果を通知してくれるコマンド、monaca-cli-notificationを作りました。

Github: https://github.com/masaki-shinkawa/monaca-cli-notification

npm: https://www.npmjs.com/package/monaca-cli-notification


monaca-cli-notification

動作確認環境:macOS Mojave v10.14.5

Windowsは動作未検証のため動かない可能性があります

使用方法は、グローバルにインストールするか、package.jsonに追加してnpm scriptsから起動するかの2パターンです

(現状npxに対応していません)


グローバルインストール

npm i -g monaca-cli-notification



package.jsonに追加

npm i -D monaca-cli-notification



package.json

"scripts": {

"remote-build": "mcn remote build ios --build-type=debug",
"upload": "mcn upload --delete"
},

基本的にはmonaca-cliをラップしているだけなのでmonacaのコマンドは何でも使えます。


example

$ mcn

$ mcn upload --delete
$ mcn remote build ios --build-type=debug

コマンド結果によりデスクトップ通知が表示されます。

成功した場合

成功.png

失敗した場合

失敗.png


実装の話

紹介だけも何なのでどのような構成で作成したか紹介していきます。


開発環境

開発環境は Typescript + Node.js で作成しました。

今回特に型を意識する必要もないのですが、babelを設定しなくていいというだけでTypescriptを使う価値があると思います。


依存関係のパッケージ

実現するために下記のライブラリを使用しました。

- node-pty

- node-notifier


対話式コマンドの対応

monaca-cliは随所で対話が発生します。そのため、対話式のcliに対応する必要がありました。

その解決策としてnode-ptyを使用します。

node-ptyはプロセスを仮想端末として扱ってくれるようで、標準出力と標準入力をやり取りすることができます。

使用方法は簡単で、pty.spawnにコマンドと引数を渡すとインスタンスが生成されので、

そこから生えている.on("data", (e) =>{})に標準出力内容が流れてきます。


インスタンスの準備

// ptyのインスタンスの生成

const ptyProcess = pty.spawn(command, argv, {
name: "xterm-color",
cols: 80,
rows: 30,
cwd: process.env.PWD,
env: process.env


標準出力が発生すると呼ばれる

ptyProcess.on("data", (data: any) => {

process.stdout.write(data); // コマンドの結果がdataに代入されている
});

また、.on("exit", (e) => {})にはコマンドのexitCodeが設定されるため、

その結果を見て正常終了、異常終了の判定が可能です。


コマンド終了時に呼ばれる

ptyProcess.on("exit", (exitCode: number) => {

if (exitCode === 0) {
// 成功の通知を出す
} else {
// 失敗の通知を出す
}
});

対話が発生した場合は.write(string)を実行することで入力を送信することができます。

これでmonacaの対話にも対応することが出来ました。


ptyに入力を送信

ptyProcess.write(str + "\n");



ユーザーからの入力受付

入力を送信することは出来る様になったのですが、ユーザーからの入力を受け入れれていないのでその対応にreadlineを使用します。

この処理は「Node.jsでの標準入力(コンソール)について」が非常に参考になりました。ありがとうございます。

単純にユーザーからの入力を無限ループで待受続け、入力が合った場合に.write(string)を呼び出すだけです。


ユーザーからの入力受付処理

//keypressイベントを使用可能にする

readline.emitKeypressEvents(process.stdin);

//readlineのインスタンスの生成
const rl = readline.createInterface({
input: process.stdin, //標準入力を指定
output: process.stdout, //標準出力を指定
prompt: "", //デフォルトのプロンプトを指定(指定なしで"> ")
terminal: true //process.stdin.setRawModeでエコーバックの切り替えを可能にする
});

(async function() {
while (true) {
const str = await new Promise(res => rl.once("line", res));
ptyProcess.write(str + "\n");
}
})();


注:readlineが生き続けているとプロセスが終了しません。

そのため、ptyの.on("exit", (e) => {})の最後にreadlineをcloseさせる処理を追加してあげます。

ptyProcess.on("exit", (exitCode: number) => {

if (exitCode === 0) {
// 成功の通知を出す
} else {
// 失敗の通知を出す
}
rl.close(); // readlineをcloseする
});


デスクトップ通知を出す

対話の処理が出来たので後はデスクトップ通知を出すだけです!

デスクトップ通知は非常に簡単で、node-notifierを使用すると簡単に実装できます。

const notifier = require("node-notifier");

module.exports = {
failedNotification: () => {
notifier.notify({
title: "Failed",
message: "コマンドが失敗しました..."
});
},
successfulNotification: () => {
notifier.notify({
title: "Successful",
message: "コマンドが成功しました!"
});
}
};

簡単すぎる・・・!

これだけでこのメソッドが呼ばれた時にデスクトップ通知が行われます。


npmに公開

npmに公開する前にwebpackで圧縮したmcn.jsを実行しようとしましたが下記のようなエラーが発生してコマンドラインから実行できませんでした。

bin/mcn.js: line 1: syntax error near unexpected token `e'

bin/mcn.js: line 1: ...

nodeで実行する分には動くのになぜ・・・?と思いましたが単純にnode.jsで実行する記述がないため、bashで起動されているのが原因でした。

shellやperlでも先頭行によく書く、あれです。


アレ

#!/bin/bash

#!/usr/bin/perl

nodejsの場合はこう書きます。

#!/usr/bin/env node

ただし、.tsや.jsにそのまま書くとwebpackに怒られるため、webpack.BannerPluginというプラグインを使います。

const webpack = require('webpack');

module.exports = {
:
plugins: [
new webpack.BannerPlugin({ banner: "#!/usr/bin/env node", raw: true }),
]
}

こうすることでビルド後のファイルの先頭に#!/usr/bin/env nodeを追記してくれ、無事にコマンドラインから実行することが出来ます。


おわりに

今回1,2時間程度で突貫で作ったツールのため、テスト環境なんかも全く用意していないのですが、よければ使ってみてください。 この記事の方が時間がかかったパターン

monacaはまだまだ戦えるプラットフォームだと思うのですが、実機テストがもう少し簡単になるといいですね・・・

このツールもせっかくなのでオリジナルコマンドとかを追加していければ良いなと思います。


アップロードしてからリモートビルドするみたいな(長い?)

mcn remote build ios --build-type=debug --upload --delete



参考


node-pty

node-pty


node-notifier

Node.jsでデスクトップ通知(gulpもね)

node-notifier


readline(標準入力)

Node.jsでの標準入力(コンソール)について


npmに公開

コマンドラインで動作する npm ライブラリを作る