はじめに
Symbolでアカウント情報やトランザクションをQRコード化して共有するための symbol-qr-library ライブラリ。
便利なライブラリなのですが、2020年頃から更新が止まっており、2025年現在のNode.js環境ではビルドは愚か開発環境構築すら困難な状態になっていました。
特にApple Silicon Macではnpm installの段階で失敗してしまい、開発環境を構築することができません。Symbol界隈ではQRコード周りのライブラリがほぼこれ一択なのに、これではちょっと困ります。
なので、最新のLTSであるNode.js v22およびARM macで実行できる環境を作りました。
symbol-qr-libraryとは
このライブラリは、Symbolブロックチェーンで使用する以下のような情報をQRコード化するためのものです:
- アカウント情報: 秘密鍵を暗号化して安全に共有
- ニーモニック: ウォレット復元用のフレーズ
- トランザクション情報: 署名リクエストの共有
- コンタクト情報: アドレスと名前のセット
内部的には、これらの情報をJSON形式に整形・暗号化し、node-canvasを使ってQRコード画像として出力します。Data URL形式やファイル出力、コンソール表示にも対応しています。
何が問題だったのか
Node.js 12とPython 2への依存
ライブラリが最後に更新されたのは2020年頃で、Node.js 12を前提としていました。しかし:
- Node.js 12: 2022年4月にEOL(サポート終了)
- Python 2: 2020年1月にEOL
- canvas 2.x: ネイティブモジュールで、ビルドにPython 2が必要
つまり、すでにサポートが終了した技術スタックに依存している状態でした。
Apple Silicon Macでビルドできない
個人的に最も深刻な問題は、Apple Silicon(M1〜)搭載のMacで全く動作しないことでした:
-
プリビルドバイナリが存在しない:
canvas@2.xのプリビルドバイナリはdarwin-x64(Intel Mac)のみで、ARM64版が提供されていない -
ネイティブビルドが失敗する: ソースからビルドしようとしても、Node.js 12 + Python 2 + 最新Xcodeの組み合わせで
node-gypがエラーを吐く -
Rosettaでも解決しない: Intel版Node.jsをRosetta経由で動かしても、Homebrewの
pkg-configや依存ライブラリのパス解決が壊れる
なので2回のフェーズに分けてこれを解消しました。
フェーズ1: Dockerで古い環境を再現可能にする
まず最初に、「古い依存関係をそのまま維持しつつ、どの環境でも動くようにする」ようにやってみます。
解決策: Docker環境の構築
ARM Mac上で直接ビルドするのは困難なので、Dockerを使ってLinux AMD64環境を用意することにしました。
Dockerfileのポイント:
- ベースイメージ:
node:12-buster(ここでx86版のlinuxコンテナを指定する) - 古いDebian Busterのパッケージリポジトリがアーカイブに移動していたため、URLを修正
- Python 2とcanvasのビルドに必要なネイティブライブラリ(
libcairo2-dev,libpango1.0-devなど)をインストール
# x86(amd64) + node v12なコンテナを指定
FROM --platform=linux/amd64 node:12-buster
SHELL ["/bin/bash","-lc"]
# コンテナが既にアーカイブされてるので取得先を変更
RUN sed -i -e 's|deb.debian.org/debian|archive.debian.org/debian|g' \
-e 's|security.debian.org/debian-security|archive.debian.org/debian-security|g' \
/etc/apt/sources.list \
&& printf 'Acquire::Check-Valid-Until "false";\n' >/etc/apt/apt.conf.d/99no-check-valid
# node-canvasに必要なネイティブモジュールの追加 & npm install
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
python2 \
pkg-config \
git \
libcairo2-dev \
libpango1.0-dev \
libjpeg-dev \
libgif-dev \
librsvg2-dev \
&& ln -sf /usr/bin/python2 /usr/bin/python \
&& npm i -g npm@6 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /work
Docker Composeの設定:
※今考えると別に常駐させるプロセスでもないのでdocker composeは不要でしたが作ったので念の為。
-
platform: linux/amd64を明示的に指定し、ARM Macでも強制的にAMD64エミュレーションで動作 - 環境変数で
npm_config_python=/usr/bin/python2を設定し、node-gypが正しいPythonを使用するように - ソースコードをボリュームマウントして、コンテナ内で開発できるように
services:
dev:
build: .
platform: linux/amd64 # ARMでもamd64を強制
working_dir: /work
volumes:
- .:/work:cached
environment:
- npm_config_python=/usr/bin/python2
- npm_config_build_from_source=true
tty: true
stdin_open: true
command: bash
これにより、以下のコマンドだけで開発環境が立ち上がるようになりました:
docker compose up -d dev
docker compose exec dev bash
npm install
npm run build
この段階での成果
✅ ARM Macを含む、あらゆる環境で確実にビルドできるようになった
✅ 環境構築の手順が明確化され、再現性が確保された
⚠️ ただし、Node.js 12とPython 2という古い技術スタックはそのまま
この時点では「とにかく動かせる状態にする」ことを最優先としました。
⚠️ セキュリティ上の注意点
フェーズ1で使用しているnode:12-busterには、以下のセキュリティリスクがあります:
- Debian Buster: 2022年8月にLTSサポートが終了し、セキュリティアップデートが提供されていない
- Node.js 12: 2022年4月にEOLとなり、既知の脆弱性が修正されていない可能性
-
アーカイブリポジトリ:
archive.debian.orgから取得するパッケージは最新のセキュリティパッチが適用されていない
推奨事項:
- このフェーズ1の環境は開発・検証目的のみに使用してください
- 本番環境やインターネットに公開するサービスでは使用しないでください
- 可能な限り早くフェーズ2(Node.js 20/22)に移行することを強く推奨します
フェーズ2: Node.js 20/22への移行
次のステップは、現代のNode.js環境で動作するようにアップデートすることでした。
主な変更内容
1. Dockerイメージの更新
- ベースイメージを
node:12-busterからnode:22-bookwormへ - Python 2からPython 3へ移行
- npm 6の固定を解除し、Node.jsに付属する最新版を使用
FROM node:22-bookworm # x86の視程を解除して使ってる端末に合わせる
SHELL ["/bin/bash","-lc"]
# Install build dependencies for node-canvas (Cairo, Pango, etc.)
# and common native build toolchain (python3, make, g++, pkg-config)
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
python3 \
python3-pip \
pkg-config \
git \
libcairo2-dev \
libpango1.0-dev \
libjpeg-dev \
libgif-dev \
librsvg2-dev \
&& rm -rf /var/lib/apt/lists/*
ENV npm_config_python=/usr/bin/python3 \
npm_config_build_from_source=true
WORKDIR /work
2. canvas 3.xへのアップデート
canvas@2.xからcanvas@3.xへアップデートすることで以下のような感じでシンプルになります:
- Node.js 22のサポート
- Python 3でのビルドに対応
- より新しいプリビルドバイナリの提供
package.jsonの変更:
{
"dependencies": {
"canvas": "^3.2.0"
}
}
これにより、ホスト側とコンテナ側で異なるアーキテクチャのバイナリが混在する問題を回避できます。
また、platform: linux/amd64の強制指定を削除し、Docker Desktopのアーキテクチャ自動検出に任せるようにしました。これでARM MacでもネイティブでDockerが動作し、パフォーマンスが向上します。
この段階での成果
✅ Node.js 20/22で動作するようになった
✅ Python 3ベースのビルド環境に移行
✅ ARM MacでもネイティブDockerで高速に動作
✅ ブラウザでの動作確認が簡単に
技術的なポイント
なぜDockerを使ったのか
ネイティブモジュールのビルドは環境依存が激しく、特に以下の問題があります:
- OSごとに異なるビルドツールチェーン(Xcode, Visual Studio, gcc)
- システムライブラリのバージョン違い
- Python 2/3の混在
Dockerを使うことで、これらの環境差異を吸収し、「どこでも同じように動く」環境を提供できます。
canvas 3.xへの移行の意義
canvas 3.xは単なるバージョンアップではなく、以下の重めな変更が含まれています:
- Node-API(N-API)への移行: Node.jsのバージョンに依存しないネイティブモジュールの仕組み
- プリビルドバイナリの充実: より多くのプラットフォーム向けにビルド済みバイナリを提供
- モダンなビルドツールチェーン: Python 3とnode-gyp最新版に対応
これにより、将来的なNode.jsのアップデートにも追従しやすくなります。
ここまでの変更について
近日中に 本家のリポジトリ に対してPR投げます。
投げたら追記します。
(だれか見てくれるかな・・・)
今後の展望
現在の状態でも一応動くようになりますが、思い切ってforkして別ライブラリとして公開しちゃおうかなと思っています。
主な変更点は以下
1. node-canvas依存の削除
これまではライブラリ側で画像の生成までやっていましたが、その機能を削ぎ落とします。
QR画像生成を呼び出し側に委譲し、ライブラリはJSON整形と暗号化に専念する設計に変更する予定です。これにより:
- ✅ ネイティブモジュールのビルドが不要になり、JSで完結する
- ✅ ブラウザ・Node.js・React Native(Expo含む)を同一コードでサポート
- ✅ WebWorkersやDenoでも動作する軽量モジュールに
- ✅ バンドルサイズの大幅な削減
2. 暗号化ライブラリの刷新(crypto-js → noble)
現在使用しているcrypto-jsは古く、以下の問題があります:
- CommonJS形式でTree-shakingが効かない
- TypeScript型定義が不完全
noble-cryptoに移行することで:
- ✅ モダンなESM形式でTree-shakingに対応
- ✅ TypeScriptファーストな設計
- ✅ バンドルサイズの削減
まとめ
4年間放置されていたsymbol-qr-libraryを、2段階のアプローチで現代の開発環境に対応させました:
フェーズ1: Docker環境の構築
- 古い依存関係をそのまま維持しつつ、Dockerで再現可能な環境を構築
- ARM Macでも確実にビルドできるように
フェーズ2: Node.js 20/22への移行
- canvas 3.xへのアップデート
- Python 3ベースのビルド環境へ移行
今回の記事は以上となります。
まだ絶賛作業途中なので今後も進捗あるごとにこの記事に追記していくのでたまに見に来てください。
では!