WP-CLIで始めるはじめてのGutenbergカスタムブロックプラグイン制作

カスタムブロックですが、WP-CLIを使うのが正直一番楽です。

WP-CLIのある開発環境を用意する

VCCWやWockerがカスタマイズできて便利ですが、黒い画面に抵抗のある人はLocal by Flywheel使いましょう。これもデフォルトで入ってます。

https://blog.mismithportfolio.com/web/20161218flywheelwpcli

WP-CLIのバージョンが古いことがあるので、要注意です。

# wp cli version
WP-CLI 1.4.1


# wp cli check-update
+---------+-------------+-----------------------------------------------------------------------------+
| version | update_type | package_url                                                                 |
+---------+-------------+-----------------------------------------------------------------------------+
| 1.5.1   | minor       | https://github.com/wp-cli/wp-cli/releases/download/v1.5.1/wp-cli-1.5.1.phar |
+---------+-------------+-----------------------------------------------------------------------------+


# wp cli update
You have version 1.4.1. Would you like to update to 1.5.1? [y/n] y
Downloading from https://github.com/wp-cli/wp-cli/releases/download/v1.5.1/wp-cli-1.5.1.phar...
md5 hash verified: c2953cd2563e8ad0be8fb0544215906b
New version works. Proceeding to replace.
Success: Updated WP-CLI to 1.5.1.

# wp cli version
WP-CLI 1.5.1

プラグインフォルダを用意する

今回はユニットテスト等は省略するので、--skip-testしときます。気持ち的には、入れておいた方がいいと思いますが。

# cd /app/public
# wp scaffold plugin guten-blocks --skip-tests --activate
Success: Created plugin files.
Plugin 'guten-blocks' activated.
Success: Activated 1 of 1 plugins.

カスタムブロックをセットアップする

# wp scaffold block first-block --category=formatting --title="hello custom"  --plugin=guten-blocks
Success: Created block 'Hello custom'.

プラグインディレクトリ内にblocksというフォルダが作られ、その中に4つのファイルが展開されます。

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   blocks/first-block.php
	new file:   blocks/first-block/editor.css
	new file:   blocks/first-block/index.js
	new file:   blocks/first-block/style.css

scaffoldしたファイルをプラグインで呼び出す

scaffoldしただけでは、まだ読み込みされていません。scaffold pluginで生成されたphpファイルでrequireする必要があります。

<?php
/**
 * Plugin Name:     Guten Blocks
 * Plugin URI:      PLUGIN SITE HERE
 * Description:     PLUGIN DESCRIPTION HERE
 * Author:          YOUR NAME HERE
 * Author URI:      YOUR SITE HERE
 * Text Domain:     guten-blocks
 * Domain Path:     /languages
 * Version:         0.1.0
 *
 * @package         Guten_Blocks
 */

require_once( plugin_dir_path( __FILE__ ) . 'blocks/first-block.php' );

これでプラグインを有効化していれば読み込みされるようになります。

scaffoldされたファイルは何をするファイルか見る

再度確認ですが、scaffold blockコマンドで4つのファイルが追加されています。それぞれの役割をざっとまとめてみました。

$ tree blocks/
blocks/
├── first-block
│   ├── editor.css  - エディターでの表示を調整するCSS
│   ├── index.js    - Gutenbergのブロックを定義するJS
│   └── style.css   - フロントでの表示を調整するCSS
└── first-block.php - JS / CSSの読み込み設定をするPHP 

あとで紹介しますが、このうちfirst-block/index.jsはそのまま使わない方がファイル容量の節約になって良いかなと思います。

カスタムブロックをjsxでかけるようにする

scaffoldコマンドで生成されるjsファイルは、(React遣い的には)あまり楽しくない感じになっています。

	registerBlockType( 'guten-blocks/first-block', {
		/**
		 * This is the display title for your block, which can be translated with `i18n` functions.
		 * The block inserter will show this name.
		 */
		title: __( 'Hello custom' ),

		/**
		 * Blocks are grouped into categories to help users browse and discover them.
		 * The categories provided by core are `common`, `embed`, `formatting`, `layout` and `widgets`.
		 */
		category: 'formatting',

		/**
		 * Optional block extended support features.
		 */
		supports: {
			// Removes support for an HTML mode.
			html: false,
		},

		/**
		 * The edit function describes the structure of your block in the context of the editor.
		 * This represents what the editor will render when the block is used.
		 * @see https://wordpress.org/gutenberg/handbook/block-edit-save/#edit
		 *
		 * @param {Object} [props] Properties passed from the editor.
		 * @return {Element}       Element to render.
		 */
		edit: function( props ) {
			return el(
				'p',
				{ className: props.className },
				__( 'Hello from the editor!' )
			);
		},

コメントもありますし、ドキュメントもあるのでまったくわからないというわけではありません。ただ、Reactで作られているものならReactっぽく書きたいという気持ちが湧いてきます。

ということでここからはscaffoldしたものをReact(ES Next)っぽくかけるようにしていきます。

jsファイルの読み込み場所の変更

この後の設定でいろいろなファイルを1つにまとめることになります。なので読み込み場所を変えておきましょう。

$ cd blocks
$ mkdir dist
$ mv first-block/index.js dist/index.js

phpファイルの$index_jsで定義しているパスも変更しておきましょう。

$ git diff first-block.php 
diff --git a/blocks/first-block.php b/blocks/first-block.php
index 35260ef..1ad900d 100644
--- a/blocks/first-block.php
+++ b/blocks/first-block.php
@@ -15,7 +15,7 @@
 function first_block_block_init() {
        $dir = dirname( __FILE__ );
 
-       $index_js = 'first-block/index.js';
+       $index_js = 'dist/index.js';
        wp_register_script(
                'first-block-block-editor',
                plugins_url( $index_js, __FILE__ ),

babel & webpackの設定

いれます。Rollupとかもありますが、webpackでいきます。

$ pwd
/PATH/TO/WP/wp-content/plugins/guten-blocks/blocks
$ npm init -y
$ npm i -D webpack babel-core babel-loader babel-plugin-transform-react-jsx cross-env babel-preset-env
$ vim .babelrc
{
	"presets": [
		[ "env", {
			"modules": false,
			"targets": {
				"browsers": [
					"last 2 Chrome versions",
					"last 2 Firefox versions",
					"last 2 Safari versions",
					"last 2 iOS versions",
					"last 1 Android version",
					"last 1 ChromeAndroid version",
					"ie 11"
				]
			}
		} ]
	],
	"plugins": [
		[ "transform-react-jsx", {
			"pragma": "wp.element.createElement"
		} ]
	]
}

$ vim webpack.config.js
module.exports = {
  entry: './index.js',
  output: {
    path: __dirname,
    filename: 'dist/index.js',
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: 'babel-loader',
      },
    ]
  },
};

JSXでのコンポーネント作成

$ pwd
/PATH/TO/WP/wp-content/plugins/guten-blocks/blocks
$ vim first-block/index.jsx
const { __ } = wp.i18n;
const { registerBlockType } = wp.blocks;

registerBlockType( 'guten-blocks/first-block', {
	title: __( 'Hello custom' ),
	category: 'formatting',

	supports: {
		// Removes support for an HTML mode.
		html: false,
	},
	edit() {
		return <p>{__( 'Hello from the editor!' )}</p>;
	},
	save() {
		return <p>{__( 'Hello from the saved content!' )}</p>;
	},
} );

エントリーファイルの用意

$ pwd
/PATH/TO/WP/wp-content/plugins/guten-blocks/blocks
$ vim index.js 
require('./first-block/index.jsx');

ビルドの実行

$ npm run build

この後エディタでブロックの表示がきえてなければOKです。

テキスト入力を許可する

このままでは入力も保存される値も固定ですので、変更できるようにします。

const { __ } = wp.i18n;
const { registerBlockType, RichText } = wp.blocks;

registerBlockType( 'guten-blocks/first-block', {
	title: __( 'Hello custom' ),
	category: 'formatting',
	attributes: {
		content: {
			type: 'array',
			source: 'children',
			selector: 'p'
		}
	},
	supports: {
		// Removes support for an HTML mode.
		html: false,
	},
	edit({attributes, setAttributes}) {
		const { content } = attributes
		const onChangeContent = (newContent) => setAttributes({content: newContent})
		return (
			<RichText
				tagName="p"
				onChange={ onChangeContent }
				value={ content }
			/>
		);
	},
	save() {
		return <p>{__( 'Hello from the saved content!' )}</p>;
	},
} );

RichTextの読み込み場所に注意

The RichText component has been moved from wp.blocks.RichText to wp.editor.RichText and the documentation is updated to reflect this.
The result is the official documentation is ahead of the current release.
The currently published example code in the official Gutenberg handbook references wp.editor.RichText and therefore does not work with the current release of the Gutenberg plugin (2.8.0).

https://github.com/WordPress/gutenberg/issues/6514#issuecomment-387754124

保存した内容を出力する

これだけだと出力されません。保存する時に走る処理でも反映させましょう。save()をカスタマイズします。

	save({attributes}) {
		const { content } = attributes
		return <p>{content}</p>;
	},

入力と出力をコントロールする

エディタ画面と保存処理を個別にかけるので、「ここはユーザーに変更されたくない」という場所を切り分けることができます。

const { __ } = wp.i18n;
const { registerBlockType, RichText } = wp.blocks;

registerBlockType( 'guten-blocks/first-block', {
	title: __( 'Hello custom' ),
	category: 'formatting',
	attributes: {
		content: {
			type: 'array',
			source: 'children',
			selector: 'p'
		}
	},
	supports: {
		// Removes support for an HTML mode.
		html: false,
	},
	edit({attributes, setAttributes}) {
		const { content } = attributes
		const onChangeContent = (newContent) => setAttributes({content: newContent})
		return [
			<h2>ここに色々なテキスト書いてね</h2>,
			<RichText
				tagName="p"
				onChange={ onChangeContent }
				value={ content }
			/>
		];
	},
	save({attributes}) {
		const { content } = attributes
		return (<div>
			<h2>本文ここから</h2>
			<p>{content}</p>
		</div>);
	},
} );

エディター

出力

おわりに

WP-CLIをベースに作り始めるとどうなるかを一通りまとめました。RichTextだけしか紹介していませんが、これだけでも入力ガイドや表示の制御がかなりやりやすくなるので、エディタのカスタマイズ要望にも応えやすくなるかなと思います。

Comment