mizdra's blog

ぽよぐらみんぐ

ネイティブモジュールに依存しない node-canvas 代替ライブラリを使う

Web フロントエンドにて、Canvas を使った View のテストを書きたいことがたまにあります。ブラウザであれば以下のようにして Canvas を利用できますが、テストが実行される Node.js ではそのような API は生えていません。

const canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 200;
const ctx = canvas.getContext('2d');

ctx.fillStyle = 'green';
ctx.fillRect(10, 10, 150, 100);

そこで Node.js では、node-canvas という npm パッケージがよく使われます。これを使うと、Web Canvas API 互換な API を用いて、Node.js でも Canvas を利用できます。

import { createCanvas } from 'canvas';

const canvas = createCanvas(200, 200);
const ctx = canvas.getContext('2d');

ctx.fillStyle = 'green';
ctx.fillRect(10, 10, 150, 100);

余談ですが、Node.js に DOM API の互換実装を提供するライブラリ「jsdom」では、HTMLCanvasElementHTMLImageElement の実装に、この node-canvas を利用しています。

ネイティブモジュールの依存問題

そんな node-canvas ですが、実はネイティブモジュールに依存しています。そのため、いくつかのモジュールがマシンにインストールされていなければ実行できません。

一度マシンにインストールしてしまえば良いものではありますが、複数人で開発するようなプロジェクトで node-canvas に依存していると、新しくチームメンバーがやってくる度にインストール方法を案内する必要があります。加えて、CI 上でテストを走らせる際も CI 環境にインストールが必要になってきます。

ちょっとした手間ではあるのですが、面倒なのでなんとかして解消できると嬉しいです。

@napi-rs/canvas を使う

@napi-rs/canvas という、同じく Web Canvas API 互換な API を実装したライブラリがあります。このパッケージはネイティブモジュールに依存しない作りになっていて、ネイティブモジュールをインストールせずとも node-canvas と同等のことができます。

$ npm i -D @napi-rs/canvas
import { createCanvas } from '@napi-rs/canvas';
//                            ^^^^^^^^^^^^^^^
//                            ここが canvas から変わっただけ

const canvas = createCanvas(200, 200);
const ctx = canvas.getContext('2d');

ctx.fillStyle = 'green';
ctx.fillRect(10, 10, 150, 100);

jsdom と組み合わせるにはもう一手間必要

先程ちょっと触れましたが、jsdom からは const Canvas = require("canvas"); で node-canvas が参照されています。従って、jsdom の HTMLCanvasElement を呼び出すと node-canvas が使われてしまう…ということになってしまいます。

そこで id:mizdra@napi-rs/canvas を以下のようなコマンドでインストールしています。

$ npm i -D canvas@npm:@napi-rs/canvas

このコマンドを実行すると、package.json 上では以下のような依存関係の情報が書き込まれます。

{
  // ...
  "devDependencies": {
    // ...
    "canvas": "npm:@napi-rs/canvas@^0.1.34",
  }
}

これで require('canvas') というコードが require('@napi-rs/canvas') へと解決され、node-canvas の代わりに @napi-rs/canvas が利用されるようになります。jsdom の HTMLCanvasElement からも @napi-rs/canvas が使われるようになり、ネイティブモジュール無しでテストを実行できるようになります。

こことかここを見るとわかるのですが、実際には @napi-rs/canvasnode-canvas の API を完全に模倣している訳ではないので、もしかしたらうまく動かないこともあるかもしれません… とりあえず手元の HTMLCanvasElement を使ったテストケースは正常に動作していますが、凝ったテストを書いてると動かないとかあるかもです。

ポケットモンスター・ポケモン・Pokémon・は任天堂・クリーチャーズ・ゲームフリークの登録商標です.

当ブログは @mizdra 個人により運営されており, 株式会社ポケモン及びその関連会社とは一切関係ありません.