WordPress カスタムブロック with TypeScript
Gutenberg 周りはそこそこアップデートすること、TypeScript を公式でサポートしていない事からあんまりお勧めできません。
また、私自身が WordPress の現状を追えていないことからあまり正確でない情報が含まれている場合があります。
一応本記事の内容は動作を確認しているものです。言うまでもないと思いますが、内容について間違い等があっても責任は負いかねます。
tsconfig.json は tsc --init
の標準出力で、 jsx
の値だけ react
に変更。
パッケージは以下を使用。
- webpack
- webpack-cli
- @wordpress/scripts
- @wordpress/dependency-extraction-webpack-plugin
- react
- react-dom
- ts-loader
- terser-webpack-plugin
CSS の出力や画像の出力が含まれていないため、必要に応じて mini-css-extract-plugin
や file-loader
等の webpack の loader をインストール。
webpack の設定ファイルは @wordpress/scripts
の付属の設定ファイルは使用しない。
const path = require("path");
const DependencyExtractionWebpackPlugin = require("@wordpress/dependency-extraction-webpack-plugin");
const TerserWebpackPlugin = require("terser-webpack-plugin");
module.exports = (env, argv) => ({
entry: {
editor: path.resolve(__dirname, "src", "editor.ts"),
script: path.resolve(__dirname, "src", "script.ts"),
},
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist"),
},
target: ["web"],
optimization: {
minimizer: [
new TerserWebpackPlugin({
terserOptions: {
sourceMap: env.development,
},
}),
],
},
plugins: [
new DependencyExtractionWebpackPlugin(),
],
devtool: env.production ? "source-map" : false,
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
use: [
{
loader: "ts-loader",
options: {},
},
],
},
],
},
resolve: {
extensions: [".js", ".jsx", ".ts", ".tsx"],
},
optimization: {
splitChunks: {
name: "vendor",
chunks: "initial",
},
},
});
エントリーポイントを 2 つ (entry.ts
, script.ts
) を用意することで、公開画面で動作するスクリプトと管理画面のスクリプトを出力する。
DependencyExtractionWebpackPlugin
は名前の通り WordPress のコアに組み込まれている js ファイルの依存解決を自動化できるプラグイン。webpack のバージョン等の関係で上手く動作しない場合がある。
次に、上記で出力される js ファイル等を WordPress に登録する。以下はプラグインとして作成する場合を想定しているため、出力位置や環境に応じて適宜変更。
add_action('init', 'register');
function register()
{
$editor_asset = require plugin_dir_path(__FILE__) . 'dist/editor.asset.php';
$script_asset = require plugin_dir_path(__FILE__) . 'dist/script.asset.php';
wp_register_script(
'plugin-vendor-script',
plugins_url('dist/vendor.js', __FILE__),
[],
filemtime(plugin_dir_path(__FILE__) . 'dist/vendor.js')
);
wp_register_scriot(
'plugin-script-script',
plugin_url('dist/script.js', __FILE__),
array_merge($script_asset['dependencies'], ['plugin-vendor-script']),
$script_asset['version']
);
wp_register_script(
'plugin-editor-script',
plugins_url('dist/editor.js', __FILE__),
array_merge($editor_asset['dependencies'], ['plugin-vendor-script']),
$editor_asset['version']
);
register_block_type(
'plugin/blocks',
[
'editor_script' => 'plugin-editor-script',
'script' => 'plugin-script-script',
]
);
}
[name].asest.php
は DependencyExtractionWebpackPlugin
から出力される php ファイル。中身を見ると dependencies
と version
がキーになった配列。そのまま wp_register_script
に設定できる。
カスタムブロックは editor.ts
へ記述。
import React from "react";
import {
BlockEditProps,
BlockSaveProps,
BlockAttribute,
BlockConfiguration,
registerBlockType,
} from "@wordpress/blocks";
import { PlainText } from "@wordpress/block-editor";
type Props = {
text: string;
};
const attributes: {
text: BlockAttribute<string>;
} = {
text: {
type: "string",
source: "text",
selector: ".className",
},
};
const edit: React.FC<BlockEditProps<Props>> = ({ attributes, setAttributes }) => (
<div className="className">
<PlainText
onChange={ text => setAttributes({ text }) }
value={ attributes.text }
placeholder=""
/>
</div>
);
const save: React.FC<BlockSaveProps<Props>> = ({ attributes }) => (
<div className="className">{ attributes.text }</div>
);
const config: BlockConfiguration<Props> = {
title: "Block name",
category: "Category name",
attributes,
edit,
save,
};
registerBlockType("blocks-name-space/block-name", config);
attributes
や config
型の設定はあってもなくても。type Props
と attributes
の内容が被ってるので何か上手い設定方法があるかもしれない。
元々カスタムブロックが書けるのであれば TypeScript への移行は難しくないと思う。 webpack の設定が WordPress の独自のプラグインを使っていたり最新版への互換が微妙だったりするところがしんどい。