node.jsのモジュールをHTMLで読み込む

# 背景

HTMLファイルから3rd Partyのnode.jsのモジュールを利用しようとして、「require is not defined」というエラーにぶち当たった。

そこで、node.jsのモジュールをライブラリとしてHTMLに読み込む方法があるかを調べてみたところ、browserifyというものがあるのを知ったので、試してみた。

ちなみに、HTML用のjsファイルが用意されているモジュールもあるっぽいので、その場合は、それをそのまま読み込めばよいと思う。

# 「require is not defined」エラーについて

例えば、下記のようなhtmlとjsファイルを作ったとき。

```test.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Test</title>
  </head>

  <body>
    <p>Test</p>
    <div>
      <textarea id="token" rows="8" cols="120">{hoge: 'hogehoge'}</textarea>
      <textarea id="signed" rows="12" cols="120"></textarea>
    </div>
    <button id="sign">Sign</button>
  </body>

  <script src="test.js"></script>

</html>
```

```test.js
// 注意：このコードは動作しません。

var jwt = require('jsonwebtoken');    

document.getElementById("sign").onclick = () => {
  const jwt = document.getElementById("token").value;
  const signedJwt = sign(jwt);
  document.getElementById("signed").innerText = signedJwt;
};

function sign(payload) {
  return jwt.sign(payload, 'secret');
}
```

ブラウザで開くと、見た目はこんな感じ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643184/f50f6ab8-8ad7-943d-8e1e-bc35e43644a7.png)

「Sign」ボタンを押すと、上のテキストエリアにあるJSON形式のテキストに署名をして、下のテキストエリアに表示しようとしたコード。

実際には、このコードは動かなくて、以下のようなエラーになる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643184/707d37ab-9e36-73ad-749a-79fd8ac4d1de.png)


まぁ、当たり前なのかもしれないけど、ただのhtmlなので、**require('jsonwebtoken')**はエラーになる。

ちなみに、[jsonwebtoken](https://github.com/auth0/node-jsonwebtoken)はJWTとかに署名できるライブラリで、nodejsであれば「npm install jsonwebtoken」とかでインストール後、上記のような呼び出しで利用できるようになる。

# browserifyの利用

ということで、これを動くようにするために、browserifyを使って頑張ってみる。

browserifyは、node.jsのコードをブラウザで動作するようにしてくれるらしい。

最終的にはhtmlとjsだけで動くようにする予定だが、とりあえず、開発にはnode.jsの環境が必要なので環境を作る。

```
> mkdir browserify
> cd browserify
> yarn init
```

適当に作ったnode環境のプロジェクト直下に、さきほどのtest.htmlとtest.jsを置く。

続いて、jsonwebtokenをインストール。

```
> yarn add jsonwebtoken
```

もちろん、これだけでは状況は変わらない。

ということで、browserifyをインストール。

```
> yarn global add browserify
```

browserifyを使って実行してみる。

```
> browserify test.js -o test2.js
```

生成された「test2.js」は下記のような感じ。

```test2.js
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;...

... (約３万行)

document.getElementById("sign").onclick = () => {
  const jwt = document.getElementById("token").innerText;
  const signedJwt = sign(jwt);
  document.getElementById("signed").innerText = signedJwt;
};

function sign(payload) {
  return jwt.sign(payload, 'secret');
}
},{"jsonwebtoken":208}]},{},[232]);
```

3万行ぐらいのファイルが生成された。

依存のファイルがすべて一つにまとめられた模様。

test.htmlを以下のように編集して、動作確認。

```test.html
<!DOCTYPE html>
<html>
  ...

  <script src="test2.js"></script>

</html>
```

再度、test.htmlをブラウザで開いてみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643184/28849b5f-6037-449b-384e-d866514cc4f5.png)

「Sign」ボタンを押すと、署名されたっぽい文字列が表示された。

とりあえず、それっぽいものが表示されたけど、何か変な気もする。

このままだと、デバッグしようにも、test.jsを書き換えるたびに、browserifyでファイル生成が必要となる。

やはり「jsonwebtoken」部分だけをライブラリとしてhtmlに読み込んで、test.jsから利用するようにしたい。

# node.jsのモジュールをライブラリとしてHTMLで読み込む

ということで、「jsonwebtoken」をライブラリとしてHTMLで読み込める形に出来ないか試してみた。

色々試行錯誤してみたところ、下記のようなjsファイル(jsonwebtoken-wrapper.js)を用意し、browserifyをかけることで、それっぽく動作した。

```jsonwebtoken-wrapper.js
(function (factory) {
  typeof define === 'function' && define.amd ? define(factory) :
  factory();
}((function () { 'use strict';

  // 以降に、元のライブラリのI/Fをそのままそのまま呼び出すだけのfuntionを追加

  var jwt = require('jsonwebtoken');

  function sign(payload, secretOrPrivateKey, options, callback) {
    return jwt.sign(payload, secretOrPrivateKey, options, callback);
  }

　function verify(jwtString, secretOrPublicKey, options, callback) {
    return jwt.verify(jwtString, secretOrPublicKey, options, callback);
  }

  function decode(jjwt, options) {
    return jwt.decode(jwt, options);
  }

  // 以降で外から呼び出せるように定義する。
  if (window) {
    if (typeof window.define == "function" && window.define.amd) {
      window.define("jwt_sign", function() {
        return sign;
      });
      window.define("jwt_verify", function() {
        return verify;
      });
      window.define("jwt_decode", function() {
        return decode;
      });
    } else {
      window.jwt_sign = sign;
      window.jwt_verify = verify;
      window.jwt_decode = decode;
    }
  }

})));
```

browserifyを使って、htmlから読み込み可能にしておく。

```
> browserify jsonwebtoken-wrapper.js -o jsonwebtoken.js
```

jsonwebtoken.jsというファイルが生成される。

test.htmlは、下記のように修正。

```test.html
<!DOCTYPE html>
<html>
  ...
　<body>
    <script src="./jsonwebtoken.js"></script>
    ... 
  </body>

  <script src="test.js"></script>

</html>
```

ライブラリとして、生成したファイルを読み込むようにした。

また、さきほど、test2.jsに変更した部分をtest.jsに戻しておく。

test.jsは以下のように書き換え。

```test.js
// var jwt = require('jsonwebtoken'); この行を削除

...

function sign(payload) {
  return jwt_sign(payload, 'secret'); // jwt.sign(..)をjwt_sign(..)に変更
}
```

ブラウザにtest.htmlを表示して確認。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643184/f483d700-c903-1940-dde0-64428e371cf1.png)

とりあえず正しく動作している模様。

ちなみに、「test.html」、「test.js」、「jsonwebtoken.js」の3つだけを適当なフォルダにおいて実行しても、同じように動作した(nodeから切り離された環境でも動作した)。