IPFS On Angular その2

4 min read読了の目安(約4000字

【deprecated】IPFS On Angular(ブラウザでIPFSのノードを動かす)

こちらの内容のアップデート版です。
ざっくり言うと型を含めてAngukarでjs-ipfs(ipfs-core)を使用する場合の設定方法などの備忘録になります。

バージョン

"ipfs-core": "0.5.4"
"angular": "11.2"

インストール

# ブラウザで使う範囲だと `ipfs` ではなく `ipfs-core` で必要十分
$ yarn add ipfs-core

呼び出し箇所

簡単に呼び出せます。コンポーネントをまたいで使う場合はサービスにラップするといいでしょう。

// app.component.ts 

// 省略
import * as ipfs from 'ipfs-core';

// 省略
export class AppComponent implements OnInit {

// 省略
  async ngOnInit(): Promise<void> {
    const node = await ipfs.create();
  }
}

ちなみに自分で使う用にラッパーサービスのライブラリを作成して公開しています。よかったら使ってみてください。

https://www.npmjs.com/package/ng-ipfs-service

アプリケーションの設定変更

angular.json

ここはその1と変わってないです。warnが出るのでcommonjsを許容します。

Warning: /home/ocknamo/workspace/ipfs/angular-ipfs-sample-app/src/app/services/ipfs.service.ts depends on 'ipfs'. CommonJS or AMD dependencies can cause optimization bailouts.
For more info see: https://angular.io/guide/build#configuring-commonjs-dependencies
// angular.json
allowedCommonJsDependencies": ["ipfs-core"]

tsconfig.json

// error
An export assignment cannot be used in a module with other exported elements.

こちらのエラー解消のため、skipLibCheckを有効にする必要があります。TS化が順調に進めば将来的には不要になるはずです。

また以下のビルドエラーの解消のためにstreamのpathを通しておく必要があります。
以前はdnsも必要と書いたのですがjs-ipfsのバージョンアップで不要になったようです(軽く調べたけどどのバージョンだったかよく分からなかった...)。

// example of errors
Error: ./node_modules/cbor/vendor/binary-parse-stream/index.js
Module not found: Error: Can't resolve 'stream' in ...

ipfsの内部でdefault importが使われているので、allowSyntheticDefaultImportsをtrueにセットする必要があります。

// error
Error: node_modules/ipfs-core-types/src/bitswap.ts:2:13 - error TS1259: Module '"/home/ocknamo/workspace/ipfs/angular-ipfs-sample-app/node_modules/peer-id/src/index"' can only be default-imported using the 'allowSyntheticDefaultImports' flag

tsconfig.jsonは以下のようになります。

// tsconfig.json
{
  // 省略
  "compilerOptions": {
    "skipLibCheck": true,
    "allowSyntheticDefaultImports": true,
    "paths": {
      "stream": [
        "node_modules/stream-browserify"
      ]
    }
  }
}

polyfill

もともとはpolyfills.tsにNodejsのポリフィルを自力で書いていたんですが、起動まではいけるもののadd()を実行すると実行時エラーでコケてしまうことが判明しました。

// こういうタイプエラーが大量に出るが、発生箇所を追ってみると`stream`をちゃんとshimできていないことが原因のようだった。
zone-evergreen.js:137 Uncaught TypeError: Cannot read property '_readableState' of undefined

対処方法としてとしては、自力でやることを諦め、node-polyfill-webpack-pluginというwebpackプラグインを使用することにしました。その名の通りNodeのポリフィルを入れてくれるプラグインです。js-ipfsのwebpack実装サンプルで使われていたためこれにしました。

プラグインを入れる手順ですが、Angularに@angular-builders/custom-webpackを導入して、必要なコンフィグファイルを用意するという順番になります。

Angularにカスタムwebpackプラグインを入れる。

@angular-builders/custom-webpackを導入します。が、バージョンによって導入方法が変わるようで、ここに手順を書いてもすぐ陳腐化すると思われるため省略します。
簡単なので公式のドキュメントを読んだら入れられると思います。

extra-webpack.config.ts

事前にgrobalyarn add grobalで、node-polyfill-webpack-pluginyarn add -D node-polyfill-webpack-pluginで入れます。

import { Configuration, ProvidePlugin } from 'webpack';
import * as NodePolyfillPlugin from 'node-polyfill-webpack-plugin';

export default {
  plugins: [
    new NodePolyfillPlugin({}),
    new ProvidePlugin({
      Buffer: ['buffer', 'Buffer'],
      global: ['global'],
      process: 'process/browser',
    }),
  ],
} as Configuration;

Buffer, global, processの解決もProvidePluginを使ってここでやってしまいました。

NodePolyfillPluginに引数が必須になってたんですが、オプションで不要なPolyfullを指定できるようです。精査すればバンドルサイズを節約できるかもしれません。

実装サンプル

https://github.com/ocknamo/angular-ipfs-sample-app

その他

  • console-browserify``util``assertCommonJS or AMD dependencies can cause optimization bailouts.というwarnが出る。angular.jsonallowedCommonJsDependenciesに追加したら黙る。
  • 記事を書くために再度いじっていたらnode-polyfill-webpack-pluginのマイナーアップデートでimportするところが壊れた。マイナーアップデートなのに壊れていいのか。