DustBox

WordPress カスタムブロック with TypeScript

note

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-pluginfile-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.phpDependencyExtractionWebpackPlugin から出力される php ファイル。中身を見ると dependenciesversion がキーになった配列。そのまま 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);

attributesconfig 型の設定はあってもなくても。type Propsattributes の内容が被ってるので何か上手い設定方法があるかもしれない。
元々カスタムブロックが書けるのであれば TypeScript への移行は難しくないと思う。 webpack の設定が WordPress の独自のプラグインを使っていたり最新版への互換が微妙だったりするところがしんどい。

Back