オープンソースのWikiであるGROWIにはプラグイン機能が用意されています。自社のデータを表示したり、表示をカスタマイズするのに利用できます。
今回は、GROWIプラグインの開発手順について解説します。以前にYouTubeのURLを自動で埋め込み表示にするプラグインを作成しましたが、今回は同様の操作をRemarkプラグインとして作成します。
コードについて
コードはgoofmint/growi-plugin-remark-youtube: GROWI plugin for embed YouTube by Remarkにあります。見るべきファイルとしては、以下の2つになります。
Remarkとは
RemarkはMarkdownのプロセッサです。GROWIでも、MarkdownからHTMLに変換する際のエンジンとして利用しています。
remarkjs/remark: markdown processor powered by plugins part of the @unifiedjs collective
Remarkはプラグインによって独自の記法をMarkdown内に追加できます。この仕組みを使って、GROWIでもプラグインを追加できます。
プラグインを追加する
今回はクライアントサイド(ブラウザ)で動作するプラグインを作成します。その場合、 client-entry.tsx
にプラグインのエントリーポイントを記述します。
remarkPlugins
に対して関数(後述)を追加することで、Remarkプラグインを追加できます。
import { plugin } from './src/youtube';
const activate = (): void => {
if (growiFacade == null || growiFacade.markdownRenderer == null) {
return;
}
const { optionsGenerators } = growiFacade.markdownRenderer;
optionsGenerators.customGenerateViewOptions = (...args) => {
const options = optionsGenerators.generateViewOptions(...args);
// プラグインを追加
options.remarkPlugins.push(plugin as any);
return options;
};
};
プラグインの内容
プラグインは src/youtube.ts
に記述しています。このファイルは、Remarkプラグインとして動作するための記述がされています。
// 読み込み必須
import type { Plugin } from 'unified';
import { visit } from 'unist-util-visit';
// 足りないプロパティを追加
interface GrowiNode extends Node {
name: string;
type: string;
attributes: {[key: string]: string}
children: GrowiNode[];
value: string;
}
// プラグインの関数を定義
export const plugin: Plugin = function() {
return (tree) => {
visit(tree, (node) => {
const n = node as unknown as GrowiNode;
try {
if (n.type === 'leafGrowiPluginDirective' && n.name === 'youtube') {
// 1つ目の引数がYouTubeの動画ID
const id = Object.keys(n.attributes)[0];
// 付属オプション(任意)のwidth/heightを取得
const { width, height } = n.attributes;
n.type = 'html';
// YouTubeの埋め込みHTMLを作成
n.value = `<div style="width: 100%; aspect-ratio: 16/9">
<iframe
width="${width || '560'}"
height="${height || '315'}"
src="https://www.youtube.com/embed/${id}"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin" allowfullscreen>
</iframe>
</div>
`;
}
}
catch (e) {
n.type = 'html';
n.value = `<div style="color: red;">Error: ${(e as Error).message}</div>`;
}
});
};
};
解説
プラグインの基本形は以下の通りです。
export const plugin: Plugin = function() {
return (tree) => {
visit(tree, (node) => {
// この中で処理
});
};
};
node
の中にRemarkでのAST(抽象構文木)が入っています。ASTはMarkdownをパースした結果のデータ構造です。すべてのノード情報が送られてくるので、必要なものだけを処理します。
そして、 node.value
に対してレンダリングしたいHTMLを送れば、GROWI上に表示されます。
使い方
プラグインの使い方です。これでYouTube動画が埋め込み表示になります。
$youtube(fGWaKXWrhdY,width=100%,height=100%)
width
と height
は任意です。指定しない場合はデフォルト値が適用されます。
$youtube(fGWaKXWrhdY)
$youtube(fGWaKXWrhdY,width=100%)
$youtube(fGWaKXWrhdY,height=100%)
何かエラーがあった場合は、その内容が赤文字で表示されます。
コンポーネントベースのプラグインとの違い
以前は https://youtube.com/watch?v=fGWaKXWrhdY
というURLを自動で埋め込み表示するプラグインを作成しました。このプラグインは、URLをMarkdownに貼り付けるだけで動作します。この場合、Reactのaタグ動作を上書きする形で動作します。
コンポーネントベースの場合、h1〜6タグやcodeタグなど、Markdown記法で使われるタグに対してのみ利用できます。つまり、divタグやpタグなどでは利用できません。
$youtube(fGWaKXWrhdY)
のようにエディタ内に地の文として記述してもプラグインとして使えるようにするためには、Remarkプラグインを作成する必要があります。
また、Remarkプラグインの場合はオプションを渡せる点が便利です。オプションを使うことで、表示をより細かくカスタマイズできます。
まとめ
GROWIのプラグイン機構はさらに拡張性があります(サーバーサイドで動かすものなど)。ぜひ、プラグインを開発してGROWIを自分好みにカスタマイズしてください。