はじめに

IIIFビューアであるMiradorの最新版(Mirador 4)に対応した回転プラグイン「mirador-rotation」を開発し、npmで公開しました。本記事では、プラグインの開発から公開、そして実際に利用するための統合方法について解説します。

背景

Mirador 3からMirador 4へのメジャーアップデートに伴い、以下の変更がありました:

  • React 16 → React 18
  • Material-UI v4 → MUI v7
  • その他多数の依存関係の更新

これにより、既存のMirador 3用プラグインはそのままでは動作しなくなりました。

mirador-rotation-pluginの開発

リポジトリ

https://github.com/nakamura196/mirador-rotation-plugin

主な機能

  • 画像の回転機能
  • Mirador 4のプラグインメニューへの統合

npmへの公開

開発したプラグインはnpmで公開しています:

npm install mirador-rotation

mirador-integrationの更新

公式のmirador-integrationリポジトリを参考に、Mirador 4対応の統合環境を構築しました。

主な変更点

項目
Mirador3.x4.0.0
React16.14.018.x
ビルドツールWebpackParcel
UIMaterial-UI v4MUI v7

package.json

最小限の依存関係で構成しています:

{
  "dependencies": {
    "mirador": "^4.0.0",
    "mirador-rotation": "^4.0.0",
    "parcel": "^2.0.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  }
}

外部から利用するための工夫

問題

公式のmirador-integrationをそのままビルドすると、ESモジュール形式で出力されます。しかし、別のHTMLページから<script>タグで読み込んで使いたい場合、グローバル変数としてMiradorが定義されないという問題がありました。

script src="mirador.js">script>
script>
    Mirador.viewer({...}); // Mirador is not defined
script>

解決策

1. ライブラリ用エントリーポイントの作成

index.jsでグローバル変数として明示的にエクスポートします:

import Mirador, * as MiradorAll from 'mirador';
import { miradorRotationPlugin } from 'mirador-rotation';

window.Mirador = { ...Mirador, ...MiradorAll };
window.miradorRotationPlugin = miradorRotationPlugin;

重要: * as MiradorAllでnamed exportsもすべて取得し、スプレッド構文でフラット化しています。この理由については後述します。

2. Parcelのビルド設定

package.jsonでグローバル形式での出力を指定:

{
  "source": "index.js",
  "targets": {
    "global": {
      "outputFormat": "global",
      "distDir": "./dist"
    }
  },
  "scripts": {
    "build": "parcel build --target global"
  }
}

3. ファイル名の正規化

Parcelはデフォルトでハッシュ付きのファイル名を生成するため、リネームスクリプトを作成:

// scripts/rename-build.js
const fs = require('fs');
const path = require('path');

const distDir = path.join(__dirname, '..', 'dist');

fs.readdirSync(distDir).forEach(file => {
  if (file.startsWith('index') && file.endsWith('.js')) {
    fs.renameSync(
      path.join(distDir, file),
      path.join(distDir, 'mirador.js')
    );
  }
});

使い方

ビルド後、dist/mirador.jsを任意のHTMLで読み込めます:

div id="demo">div>
script src="mirador.js">script>
script>
    Mirador.viewer({
        id: 'demo',
        windows: [{
            rotationEnabled: true,
            manifestId: 'https://example.com/manifest.json',
        }],
    }, [...miradorRotationPlugin]);
script>

APIのフラット化問題

問題の発覚

unpkg.comで配布されているMiradorのUMDビルドを使用していた場合、以下のようにAPIを直接呼び出せていました:

Mirador.addCompanionWindow(windowId, { content: 'info' });

しかし、Parcelでビルドした後に同じコードを実行すると、以下のエラーが発生しました:

TypeError: Mirador.addCompanionWindow is not a function

原因の調査

unpkg.comのUMDビルドとParcelビルドを比較した結果、エクスポート構造の違いが原因でした:

ビルド方法エクスポート構造API呼び出し
unpkg.com (UMD)フラット化Mirador.addCompanionWindow()
Parcel (default exportのみ)ネストMirador.actions.addCompanionWindow()

unpkg.comのUMDビルドでは、すべてのアクション・セレクターがトップレベルにフラット化されています:

// UMDビルドの末尾(抜粋)
G.addCompanionWindow=Oj,
G.removeCompanionWindow=Aj,
G.updateCompanionWindow=yg,
// ... その他多数

一方、単純にimport Mirador from 'mirador'でdefault exportのみを取得した場合:

// default exportの構造
{
  viewer: function,
  actions: { addCompanionWindow, ... },
  selectors: { ... },
  // ...
}

解決策

index.jsでnamed exportsもすべて取得し、スプレッド構文でフラット化:

// Before動かない
import Mirador from 'mirador';
window.Mirador = Mirador;

// After動く
import Mirador, * as MiradorAll from 'mirador';
window.Mirador = { ...Mirador, ...MiradorAll };

これにより、ビルド後のファイルでは以下のようにフラット化されます:

window.Mirador={...f.default,...f}

補足:mirador-rotationのバージョン

mirador-rotationプラグインには4.0系と4.1系があります:

バージョンMUI対象
4.0.xv5Mirador 4 alpha初期版
4.1.xv7Mirador 4.0.0 正式版

Mirador 4.0.0正式版はMUI v7を使用しているため、mirador-rotation 4.1.x を使用してください。

まとめ

Mirador 4用のプラグイン開発と公開、そして外部から利用可能なライブラリとしてビルドする方法を紹介しました。

ポイント:

  • ESモジュール形式ではグローバル変数が定義されない
  • import Mirador, * as MiradorAllでdefault exportとnamed exportsの両方を取得
  • { ...Mirador, ...MiradorAll }でフラット化してエクスポート
  • ParcelのoutputFormat: "global"でグローバル形式ビルド

リポジトリ