Node.jsアドベントカレンダー21日目になります。
npmといえば、Node.jsやフロントエンド開発で欠かせないツールですよね。そんな npmの中国版があることをご存知でしょうか。
仕事柄、中国に行ったり、中国にいる人とやりとりしたりすることがありますが、その辺に関連した話です。実用性はほぼ無いので話のネタだと思ってもらえると幸いです。(アドカレ遅刻すみません)
IoTLT vol70で話をしたネタです。 スライドはこちら
npmが何かについては触れませんのでご了承下さい。
cnpm - 中国ミラー版のnpm
For developers in China, please visit the China mirror.
中国の開発者向けに作られているnpmのミラーサイト及びツールです。
- https://github.com/cnpm/cnpm
- https://developer.aliyun.com/mirror/npm/package/romtool
- https://npm.taobao.org
そもそもなぜcnpmが必要なのか - グレートファイヤーウォール問題
中国ではグレートファイアウォール(金盾)というネット検閲の仕組みがあってGoogleやTwitterなどのサービスが使えないことが有名です。
試して無いですがおそらく、 中国国内からnpmを利用しようとすると、全く利用できないか、一部のパッケージのDL・インストールが出来ないという状況なのだと思います。
※中国以外の国のVPNを経由したり、香港SIMを使うなど回避方法はありますが、そういった回避方法を使わずに直で中国国内のネットワークを利用する際の話です。
見たところ、CNPMはtaobao(中国の楽天とかAmazonみたいなサイト)のドメイン下にミラーがある模様なので、中国国内のネットワークでも問題なく利用できるのでしょう。
怖いけどcnpmをインストールしてみた
正直情報抜かれてそうで怖かったりもしますので、試す場合は自己責任でお願いします。
実はnpm経由でインストールできる
メタな感じがしますが、npmにcnpmがいます。
なのでnpm i -g cnpm
でインストールできるのですが、どうやら--registry
オプションでtaobaorのサーバーを指定してインストールすることでcnpmのインストールができる模様です。
$ npm i -g cnpm --registry=https://registry.npm.taobao.org
でcnpmをインストール可能です。
ちなみに、指定している、https://registry.npm.taobao.org
が不安定で、アクセス出来たりできなかったりの差が激しかったです。(何の検証をしてるんだ......苦笑) コマンドとしてのcnpmを試すだけなら--registry
オプション無しでインストールでも良いと思います。
$ npm i -g cnpm
インストールから時間かかる
僕が試した際には、 6分強かかりました。
参考までにnpm i -g yarn
でyarnをインストールした場合は0.5秒でした。
参考: https://speakerdeck.com/n0bisuke2/zhong-hua-zhi-npmfalsecnpmgaarumo-yang-number-iotlt?slide=14
cnpmを触ってみた
次は実際にcnpmのコマンドを見ていきます。
npm initやnpm installなどは同様に使える
基本的なコマンド操作は同じです。
- パッケージインストール
npm i
がcnpm i
になるだけです。
$ cnpm i パッケージ名
- プロジェクト初期化
これもnpm init
がcnpm init
になるだけです。
$ cnpm init -y
ちなみにこの際に出来上がるpackage.jsonはほぼ一緒でした。
バージョンの違い、npxは使えないなど
手元の環境が
- Node.js v15.3.0
- npm v7.0.14
- cnpm v6.1.1
という状況で、若干の違いがありました。cnpmのバージョンがnpmの各バージョンの機能に追随出来てるかも不明です。
--saveオプション
例えば、最近のnpmはinstall時に--save
を付けなくても、package.jsonに記載してくれますが、現状のcnpmは--save
を付けないとダメでした。
cnpxは無かった
また、npxは使えるかなと思い、cnpx ngrok http 3000
みたいな試し方をしてみましたが、npxのcnpm版はパッと見存在しなさそうでした。
サンプルアプリケーションを作ってみる準備
実際にcnpm経由でインストールしたモジュールが問題なく使えるのかを試してみたく、mqttライブラリを使ったサンプルを作って試してみました。
$ cnpm init -y
Wrote to /Users/n0bisuke/Documents/ds/playground/test-cnpm/test2/package.json:
{
"name": "test2",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
初期化できました。
次にモジュールのインストールです。
サーバーに到達できなかったり、タイムアウトなどのトラブル
$ cnpm i mqtt --save
ここでトラブルが発生しました。
Get /bug-versions/latest from https://r.npm.taobao.org error: ConnectionTimeoutError: Connect timeout for 5000ms,
こんな感じのエラーでしたが単なるタイムアウトではなく、--registry=https://registry.npm.taobao.org
を指定してcnpmのインストールしてるとnpmではなくhttps://registry.npm.taobao.org
からモジュールをインストールしてこようとします。先程も書いたようにこのhttps://registry.npm.taobao.org
が落ちてたり不安定だったりするとここでエラーになります。
これは大元のサーバーの挙動が怪しいという話なので、このタイムアウトの場合はもうしばらく待つしかなさそうです。
また、サーバーの調子がよく、ある程度インストールが進んだかなぁと行った段階でも
Connect timeout for 60000ms......
といった タイムアウトエラーが発生しました。中国のサーバーに向かったり、内部でリダイレクトしたりをしてると思うので遅いのだと思うのですが、普通にタイムアウトするとは......
調べるとcnpm set timeout
でタイムアウトするまでの制限時間を伸ばすことが出来る模様です。もはやタイムアウトしてしまうのが前提なのかも
$ cnpm set timeout 120000
こんな感じで改めてcnpm iでmqttモジュールをインストールできました。
$ cnpm i mqtt --save
⠋ [0/1] Installing concat-map@0.0.1[npminstall:get] retry GET https://r.npm.taobao.org/concat-stream after 100ms, retry left 4, error: Error [ConnectionTimeoutError]: Connect timeout for 60000ms, GET https://r.npm.taobao.org/concat-stream -2 (connected: false, keepalive socket: false, agent status: {"createSocketCount":27,"createSocketErrorCount":0,"closeSocketCount":27,"errorSocketCount":0,"timeoutSocketCount":26,"requestCount":187,"freeSockets":{},"sockets":{},"requests":{}}, socketHandledRequests: 1, socketHandledResponses: 0)
headers: {}
at Timeout._onTimeout (/Users/n0bisuke/.nodebrew/node/v15.3.0/lib/node_modules/cnpm/node_modules/urllib/lib/urllib.js:946:15)
at listOnTimeout (node:internal/timers:556:17)
at processTimers (node:internal/timers:499:7) {
requestId: 7,
data: undefined,
path: '/concat-stream',
status: -2,
headers: {},
res: [Object]
}, status: -2, headers: {},
stack: ConnectionTimeoutError: Connect timeout for 60000ms, GET https://r.npm.taobao.org/concat-stream -2 (connected: false, keepalive socket: false, agent status: {"createSocketCount":27,"createSocketErrorCount":0,"closeSocketCount":27,"errorSocketCount":0,"timeoutSocketCount":26,"requestCount":187,"freeSockets":{},"sockets":{},"requests":{}}, socketHandledRequests: 1, socketHandledResponses: 0)
headers: {}
at Timeout._onTimeout (/Users/n0bisuke/.nodebrew/node/v15.3.0/lib/node_modules/cnpm/node_modules/urllib/lib/urllib.js:946:15)
at listOnTimeout (node:internal/timers:556:17)
at processTimers (node:internal/timers:499:7)
✔ Installed 1 packages
✔ Linked 61 latest versions
✔ Run 0 scripts
peerDependencies WARNING mqtt@4.2.6 › ws@^7.3.1 requires a peer of bufferutil@^4.0.1 but none was installed
peerDependencies WARNING mqtt@4.2.6 › ws@^7.3.1 requires a peer of utf-8-validate@^5.0.2 but none was installed
✔ All packages installed (66 packages installed from npm registry, used 1m(network 1m), speed 10.75kB/s, json 62(114.02kB), tarball 581.6kB)
ふう。。。
MQTTでPub/Subしてみる
- Pub -> cnpm経由でインストールしたmqttモジュール
- Sub -> npm経由でインストールしたmqttモジュール
という感じでcnpmとnpmでPub/Subしてみました。
先程mqttのインストールが完了したので、app.jsを書いてみます。
ここは通常と何ら変わりません。
- mqttでpublishするコード
const mqtt = require('mqtt');
const client = mqtt.connect('mqtt://test.mosquitto.org');
client.on('connect', () => {
client.publish('n0bisuke', 'Hello cnpm');
});
$ node app.js
- mqttでsubscribeするコマンド
$ npx mqtt sub -h test.mosquitto.org -t n0bisuke
Hello cnpm
こんな感じでPub/SubのMQTT通信ができています。
インストール後は割と普通ですね
cnpm限定コマンド
cnpm限定のコマンドもある模様です。
- cnpm sync -> npm側との差分を無くす
- cnpm i --by=npm -> npmからDL/インストール
調べたらもっとあるかも
まとめ
cnpmという中華製のnpmミラーがある!
日本で使う意味はほぼ皆無 だと思うので、ネタ的な感じですね。
中国に行って開発することがあれば覚えておくと良いかもしれません。