TypeScript を使い Drupal 9.3 以降で CKEditor 5 向けのプラグインを登録してみる
Drupal 9.3 になり CKEditor 5 が同梱されるようになりました。CKEditor 5 のプラグインは CKEditor 4 から大きく変わり、JavaScript を書くだけでは足りなくなってしまいました。そこで、この記事では CKEditor 5 に TypeScript で書いたプラグインを登録するところまでを、デモリポジトリのソースコードを基に説明します。
説明用デモレポジトリ
下準備
CKEditor 5 のプラグイン開発のため、package.json
に必要なライブラリや型定義を記述していき、yarn を使ってインストールします。
CKEditor 5 のプラグイン開発では、CKEditor 4 のようにブラウザ用の JavaScript を書くだけでは完結せず、Webpack を使って適切な形に変換する必要があるなど開発方法がかなり変更されています。
{
"name": "cke5_ts_test",
"engines": {
"yarn": ">= 1.6",
"node": ">= 16.0"
},
"scripts": {
"build": "yarn build:transpile & yarn build:webpack",
"build:transpile": "cross-env BABEL_ENV=modern node ./develop/transpile.mjs",
"build:webpack": "webpack --config ./webpack.config.js"
},
"dependencies": {},
"devDependencies": {
"@babel/core": "^7.17.8",
"@babel/preset-env": "^7.16.11",
"@babel/preset-typescript": "^7.16.7",
"@types/ckeditor__ckeditor5-core": "^33.0.2",
"@types/node": "^17.0.21",
"ckeditor5": "^33.0.0",
"cross-env": "^7.0.3",
"fast-glob": "^3.2.11",
"terser-webpack-plugin": "^5.3.1",
"typescript": "^4.6.3",
"webpack": "^5.70.0",
"webpack-cli": "^4.9.2"
}
}
プラグイン開発
TypeScript から JavaScript への変換
CKEditor 5 にプラグインとして登録するためには、下記のようにプラグインのキーと実装クラスを紐付けてデフォルトエクスポートする必要があります。
export default {
PluginKey: PluginClass,
}
デフォルトエクスポート部分は下記のように実装し、実装クラスはコードを整理しやすくするため別ファイルからインポートするようにします。
import { TestPlugin } from "./plugin-body"
export default {
TestPlugin,
}
実装クラスは CKEditor 5 のプラグインクラスを継承する必要がありますが、Drupal 側の実装を参考に ckeditor5/src/core
からクラスをインポートしています。
実装クラス側は最低限のコードさえ有れば良いので、呼び出されたときに console.log()
を実行するだけの init()
メソッドを実装します。
import { Plugin } from 'ckeditor5/src/core';
export class TestPlugin extends Plugin {
init() {
console.log('Test plugin is initialized.');
}
static get pluginName() {
return 'TestPlugin';
}
}
ここで、問題が生じます。ckeditor5/src/core
は TypeScript の型定義ではないため、このままだとエラーが出てしまいます。エラー解決のため、tsconfig.json
側でパスの読み替えを行う設定をします。
tsconfig.json
に baseUrl
を設定した上で paths
を設定すると、パスの読み替えが行われるようになるので、ckeditor5/src/core
に @types/ckeditor__ckeditor5-core
を割り当てます。これでエラーは解消されます。
"paths": {
"ckeditor5/src/core": [
"./node_modules/@types/ckeditor__ckeditor5-core"
],
},
TypeScript から JavaScript への変換は以前投稿した Non-decoupled Drupal プロジェクトでの TypeScript によるフロントエンド開発環境の試作の際に作成した、デモリポジトリの実装をほぼそのまま使っています。
Webpack の設定とライブラリ読み込みの設定
Webpack の設定は Drupal に添付されている設定を流用して下記を満たすようにします。
- 変換対象のファイルは各プラグインの起点となるファイルを設定
- 出力は所定の形式にする
- umd 形式
- 出力先は libraries.yml で設定するファイル
- library は、
[CKEditor5, plugin_name]
- 上記の
plugin_name
は ckeditor5.yml と同値
- プラグインに DLL プラグインを指定する
今回は、一つしかプラグインがないため Drupal の設定を少し変更し、下記のようにします。
const path = require('path');
const webpack = require('webpack');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = [{
mode: 'production',
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
format: {
comments: false,
},
},
test: /\.js(\?.*)?$/i,
extractComments: false,
}),
],
moduleIds: 'named',
},
entry: {
path: path.resolve(
__dirname,
'js/src/cke5-test-plugin.js',
),
},
output: {
path: path.resolve(__dirname, 'js/dist'),
filename: 'cke5-test-plugin.js',
library: ['CKEditor5', 'testPlugin'],
libraryTarget: 'umd',
libraryExport: 'default',
},
plugins: [
new webpack.BannerPlugin('cspell:disable'),
new webpack.DllReferencePlugin({
manifest: require(path.resolve(__dirname, './node_modules/ckeditor5/build/ckeditor5-dll.manifest.json')), // eslint-disable-line global-require, import/no-unresolved
scope: 'ckeditor5/src',
name: 'CKEditor5.dll',
}),
],
module: {
rules: [{ test: /\.svg$/, use: 'raw-loader' }],
},
}];
ライブラリの読み込み設定は通常のライブラリと変わりありませんが、下記の点は留意する必要があります。
- CKEditor 5 のプラグインなので、依存関係に
core/ckeditor5
を入れる - Drupal 標準の集約機能有効時に意図しない動作を避けるため、
preprocess: true
を設定する - ライブラリの JavaScript は TerserPlugin で minify されているので、
minified: true
を設定する-
minimize: true
が設定されているときのみ
-
プラグイン読み込みの設定
プラグインの実装である JavaScript 側の設定は終わったので、プラグインを読み込むための設定を追加します。Drupal 9.3 から CKEditor 5 のプラグイン設定用に module_name.ckeditor5.yml
というファイルが追加されました。このファイルに所定の形式に従って設定を書いていきます。
所定の形式に関しては、Drupal.org の記事 や プラグイン定義のソースコードに記述されています。プラグイン定義のソースコードに書かれた例外をみれば、命名規則や必須項目などを確認することができるので設定の際はこのファイルを参照すれば最低限の設定を満たすことが出来ます。
下に module_name.ckeditor5.yml
の例を挙げます。
cke5_ts_test_test_plugin:
ckeditor5:
plugins:
- testPlugin.TestPlugin
drupal:
label: "Test CKEditor 5 plugin"
library: cke5_ts_test/ckeditor5_test_plugin
elements: false
注意点として、ckeditor5.plugins
の値は Webpack の設定で出てきた library
の値に依存する点です。ckeditor5.plugins
の値は plugin_name.ClassName
の形で指定しますが、この plugin_name
は library
の配列の1番目の要素と等しくなります。よって、Webpack と module_name.ckeditor5.yml
のどちらかの設定を変えた際は変え忘れがないかを確認した方が良いと思います。
module_name.ckeditor5.yml
の他の項目とその詳細に関しては、上記の Drupal.org の記事を確認してください。
まとめ
この記事では、Drupal に同梱される CKEditor 5 のプラグイン開発を TypeScript で行い CKEditor 5 にプラグイン登録するまでの手順を解説しました。この記事で書かれている内容は登録までなので、CKEditor 5 のエディタ自体の拡張は公式の API ドキュメントを読み進めて行う必要があります。
プラグイン開発の方式が大幅に変わってしまったためか、2022/04 時点で CKEditor 5 対応のモジュールは CKEditor Tweetable Text のみで CKEditor 4 を拡張している貢献モジュールの CKEditor 5 対応状況はまだまだです。次期リリースの Drupal 10 に標準で含まれる WYSIWYG は CKEditor 5 のみになってしまうので、もし、Drupal 10 時点で CKEditor 5 に対応したプラグインが必要ならばソースコードに対する積極的な貢献をすべきでしょう。ただし、CKEditor 4 を拡張しているモジュールが CKEditor 5 で廃止されたような機能を基に拡張していた場合は厳しいかもしれません。
Discussion