node-redプログラミング：カスタムノードのデバッグ環境を整備する

# はじめに

node-redのカスタムノードを開発の際、実行時にJavascriptに何かエラーがあっても、下のようななんとも簡素なメッセージしか表示されません。これだけでは規模の大きいカスタムノードのデバッグは無理です。

node-red側: `[node-red-contrib-linking-device/linking-device] SyntaxError: Unexpected token .`
ブラウザ側：`SyntaxError: Undefined variable`

ということで、整えたデバッグ環境について以下に説明します。

# ESLintで事前にシンタックスチェックを行う

node-redのカスタムノードプログラミングでは、Nodejs用Javascriptと、htmlファイル内のJavascriptが混在するのが特徴で、ESLinの設定にひと手間必要です。

## ESLintのインストール

eslintと、htmlファイル内scriptをチェックするプラグイン: eslint-plugin-htmlをnpmインストール。また、ローカライズ用のjsonファイルをチェックするeslint-plugin-jsonもインストール。

```
npm i --save-dev eslint
npm i --save-dev eslint-plugin-html
npm i --save-dev eslint-plugin-json
```

## ESLint設定ファイル作成

### ES2017で書くNodejs用の設定

```javascript:.eslintrc.json
{
    "env": {
        "es6": true,
        "node": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "ecmaVersion": 2017
    },
    "rules": {
        "indent": [
            "error",
            4
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "error",
            "single"
        ],
        "semi": [
            "error",
            "always"
        ],
        "no-console": "off",
        "no-unused-vars": [2, {
            "args": "after-used",
            "argsIgnorePattern": "^_"                                                                       }]
    },
    "plugins": [
        "json"
    ]
}
```

### htmlファイル内のscript用設定

```javascript:.eslintrc.html.json
{
    "env": {
        "browser": true,
        "jquery": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "sourceType": "module"
    },
    "rules": {
        "indent": [
            "error",
            4
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "off"
        ],
        "semi": [
            "error",
            "always"
        ],
        "no-console": "off",
        "no-unused-vars": [2, {
            "args": "after-used",
            "argsIgnorePattern": "^_"
        }]                                                                                              },
    "globals": {
        "RED": false
    },
    "plugins": [
        "html"
    ]
}
```

## package.jsonにlintスクリプトを追加

ソースコードは `プロジェクトフォルダ/src`　にある想定です。

```javascript
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "lint": "eslint --ext .json --ext .js src || eslint -c .eslintrc.html.json --ext .html src"
  },
```

## ESLint実行

```
npm run lint
```

これで事前にスクリプトのチェックを行うことが出来ます。

# Chrome devtoolsを使ってカスタムノードのNodejsスクリプトをデバッグ

## node-red-template-embeddedの用意

[Setup a own Node-RED Dev Environment in 5 min.](https://medium.com/@sebastian.barwe/setup-a-own-node-red-dev-environment-in-5-min-78b21f995e19) という記事を参考に、node-redをピュアなNodejs実行環境下で動かす [node-red-template-embedded](https://github.com/sbarwe/node-red-template-embedded) を使います。これを使えば、Chrome devtoolsでソースコードデバッグができるようになります。

### 適当なフォルダにてnode-red-template-embeddedをcloneする

```
git clone https://github.com/sbarwe/node-red-template-embedded.git
cd node-red-template-embedded
```

commitはしない前提で、node-red-template-embeddedの内容をいじります。

### デバッグしたいカスタムノードをnpm link

node-red-template-embeddedのフォルダから `npm link node-red-contrib-mynode` します。

参考: [npm linkの基本的な使い方まとめ](https://qiita.com/103ma2/items/284b3f00948121f23ee4)

### package.jsonをいじる

本来は、これでnpm i && npm run debugすればいいのですが、node-red-template-embeddedは若干古く、最近のnodejsでは動きません。

以下のように、package.jsonのscript中の"debug"を変更、また"dev"を追加します。

```
  "scripts": {
    :
    "debug": "cross-env NODE_ENV=development \"nodemon --config .nodemonrc.json --inspect src/server.js\"",
    "dev": "NODE_ENV=development node --inspect=0.0.0.0:9929 src/server.js"
  },
```

### Raspberry Pi固有の設定 (結局は不要)

Raspberry Piで `npm run debug` を行ったところ、以下のエラーが出ました

```
[nodemon] Internal watch failed: watch /home/pi/git/node-red-template-embedded/node_modules/rc/node_modules/minimist/test/dotted.js ENOSPC
[nodemon] exiting
```

エラーを起こしているnodemonは、ソースファイルの変更を検知してデバッグ対象のプログラムを再起動してくれるのですが、監視ファイル数が多すぎてエラーになっている模様。

調べたところ、Raspberry Piでは監視ファイル最大数がデフォルトでは8192と少なかったので4倍にしました。

```
echo fs.inotify.max_user_watches=32768 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
```

### Chromeのdevtoolsでリモート接続できない

npm run debug でnode-redを立ち上げた後は、Chromeのdevtoolsで接続してデバッグすればいいのですが、上記設定では原則同じマシン上での接続となります。--inspect を --inspect=0.0.0.0:9229 と指定すればリモートから接続できるはずなのですがだめ。
どうやら、nodemonでは無効なようです。まあもともとセキュリティー上推奨しないとNodejsのドキュメントに書いてあるのでしょうがないですね(sshトンネルでのリモート接続が推奨されてます。でも家庭内LANなのでめんどくさい)。

ということで、Raspberry Piをリモートデバッグする為に、nodemonを使わずnodeを --inspect=0.0.0.0:9929 つきで直接起動するスクリプトdevを追加しています。npm run devでnode-redをリモートデバッグできるようになります。

### Chromeのdevtoolsでブレークポイント設定が出来ない

PC上のChrome devtoolsで接続し、ブレークポイントを設定しようとしたらどうも機能しない。
最初はRaspberry pi3が32bit OSだからこんなものなのかと思ったのですが、Chrome66以降の不具合とのこと: 

https://github.com/meteor/meteor/issues/9875

回避策が書いてありますが、devtoolsのChrome拡張 [NIM](https://chrome.google.com/webstore/detail/nodejs-v8-inspector-manag/gnhhdgbaldcilmgcpfddgdbkhjohddkj) をインストールします。これは勝手にdevtoolsが立ち上がって便利。

参考：[node --inspectの chrome-devtools://... コピペを自動化するChrome拡張を使う](https://qiita.com/onionmk2/items/de8a22d70e2be0b0ff92)

これでようやくデバッグの環境が整いました。

# デバッグの手順

1. `node-red-stop`を実行し、通常のnode-redは止めておく
2. chromeで`NIM`を実行し(window内右上の緑のアイコン)、デバッグターゲットのIPアドレスとポート(9229)を設定
3. カスタムノードのjsやhtmlファイルをエディット
4. custom node開発側で、`npm run lint` してシンタックスエラーをチェック・修正
5. node-red-template-embedded 側でnpm run dev (もしくはnpm run debug)してnode-red起動
6. Chromeブラウザで `http://<ターゲットのIPアドレス>:1880/admin/` (URLに注意) を開いて、カスタムノードをテストする。UI側もnodejs側も、Chrome devtoolsを使って便利にデバッグできます。
7. バグの調査が完了したら、`npm run dev` を実行中のコンソールでCtrl+Cを2回押してnode-redをとめる
8. 手順3に戻る

手順5で、npm run debugを使える場合は、7, 8は不要です。
なお、6では単にリロードボタンを押すのではなく、上記URLを再読み込みする必要があります。そうしないと、カスタムノードのhtmlファイルの内容が更新されないようでした。