Gutenberg as a framework: WordPress 外でブロックエディターを動かしてみる

記事の背景

2023年8月に、Gutenberg プロジェクトにおいて「Gutenberg as a framework: streamline the experience」という issue が立ち上がりました。

自分には非常に難解な内容ですが、多くの議論が交わされており、現在も進行中の issue のようです。その一方、フレームワークとして ブロックエディターを動作させるためのドキュメントの整備も進められており、今回は、このドキュメントに従って環境構築に挑戦し、現時点でどこまで/どのような事が出来るのかを確認してみようと思います。

使用技術

環境構築にあたって、今回は Bun と Vite を使用してみます。どちらも初めて使用するため、もし間違い等ありましたらぜひご指摘ください。

開発環境の初期構築

とりあえず Bun のトップページにあるコマンドを使ってインストールする

$curl -fsSL https://bun.sh/install | bash
######################################################################### 100.0%
bun was installed successfully to ~/.bun/bin/bun 
Run 'bun --help' to get started

入ったっぽい

$bun --version
1.0.0

Vite テンプレートを作成。フレームワークはもちろん React。Gutenberg の各種パッケージに対して最新の型定義が全てあるか不明なので、variant は JavaScript を選択。

s$ bun create vite block-editor-platform
✔ Select a framework: › React
✔ Select a variant: › JavaScript
​
Scaffolding project in /home/wildworks/projects/block-editor-platform...
​
Done. Now run:
​
  cd block-editor-platform
  bun install
  bun run dev

こんなファイルが生成されました

$ tree
.
├── index.html
├── package.json
├── public
│   └── vite.svg
├── README.md
├── src
│   ├── App.css
│   ├── App.jsx
│   ├── assets
│   │   └── react.svg
│   ├── index.css
│   └── main.jsx
└── vite.config.js
​
3 directories, 10 files

ライブラリをインストールして起動してみる

$ bun install
bun install v1.0.0 (822a00c4)
 + @types/react@18.2.45
 + @types/react-dom@18.2.18
 + @vitejs/plugin-react@4.2.1
 + eslint@8.56.0
 + eslint-plugin-react@7.33.2
 + eslint-plugin-react-hooks@4.6.0
 + eslint-plugin-react-refresh@0.4.5
 + vite@5.0.10
 + react@18.2.0
 + react-dom@18.2.0
​
 270 packages installed [13.59s]
 
$ bun dev
$ vite
​
  VITE v5.0.10  ready in 315 ms
​
  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

起動しました。中央あたりのボタンをクリックすると、数字がカウントアップされる模様

初期画面

Gutenberg ドキュメントの確認

ここからブロックエディターを組み込んでいくことになりますが、そのドキュメントは Gutenberg のGitHub リポジトリにあります。この README を直接見ても良いのですが、このドキュメントには Docusaurus 2 が使われているとの事なので、せっかくなのでビルドしてみます。

README に従ってコマンドを実行します (Gutenberg リポジトリはローカルにクローン済で、プロジェクトルートにいるとします)。

$ cd ./platform-docs/
$ npm install
​
up to date, audited 1145 packages in 19s
​
215 packages are looking for funding
  run `npm fund` for details
​
26 vulnerabilities (14 moderate, 11 high, 1 critical)
​
To address issues that do not require attention, run:
  npm audit fix
​
To address all issues (including breaking changes), run:
  npm audit fix --force
​
Run `npm audit` for details.

検証時点では、broken links があるとの事で npm run build が失敗したので、代わりに npm run start を実行

$ npm run start
​
> platform-docs@0.0.1 start
> docusaurus start
​
(中略)
​
✔ Client
  Compiled successfully in 3.84s
​
[WARNING] Docs markdown link couldn't be resolved: (/docs/reference-guides/block-api/block-attributes.md) in "/home/wildworks/projects/gutenberg/platform-docs/docs/basic-concepts/data.md" for version current
client (webpack 5.88.2) compiled successfully

ドキュメントサイトが立ち上がりました !

ドキュメントサイト

ドキュメントの Getting Started を見ると、まずは依存関係をインストールする必要があるとの事

$bun i -D @wordpress/block-editor @wordpress/block-library @wordpress/components

ブロックエディターの組み込み

いよいよブロックエディターの組み込みを試してみます。

まずは、アプリを CSS も適用されていないとにかくフラットな状態にしてみます。

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)
function App() {
  return (
    <div>Hello World!</div>
  )
}

export default App

ここに、ドキュメントに記載されている通りブロックエディターを組み込んでみます。開発環境の構成に合わせて、一部調整しています。

import { useState } from "react";
import {
  BlockEditorProvider,
  BlockCanvas,
} from "@wordpress/block-editor";
import { registerCoreBlocks } from "@wordpress/block-library";

import "@wordpress/components/build-style/style.css";
import "@wordpress/block-editor/build-style/style.css";
import "@wordpress/block-library/build-style/common.css";
import "@wordpress/block-library/build-style/style.css";
import "@wordpress/block-library/build-style/editor.css";

registerCoreBlocks();

function App() {
  const [blocks, setBlocks] = useState([]);
  return (
    <BlockEditorProvider
      value={blocks}
      onChange={setBlocks}
      onInput={setBlocks}
    >
      <BlockCanvas height="500px" />
    </BlockEditorProvider>
  );
}

export default App

以下のようなエラーがブラウザコンソールに記録され、何も表示されず。。

@wordpress_block-library.js?v=3a76d5ae:64859 Uncaught ReferenceError: process is not defined at @wordpress_block-library.js?v=3a76d5ae:64859:52

エラーが起こっている行を見てみると、確かに processs.env.IS_GUTENBERG_PLUGIN を読み取っているっぽいですが、そもそも processundefined であるのが問題

エラー

この記事によると、Vite では「process.env.HOGEimport.meta.env.VITE_HOGEに書き換える」とありますが、そもそも外部ライブラリなので書き換える事が出来ない。。

Gutenberg プロジェクトでも似た問題が報告されていた

https://github.com/WordPress/gutenberg/issues/48949

issue のこのコメントによると、@rollup.plugin-replace を使用して置き換える事でも解決出来るらしいですが、Vite のドキュメントによると、define プロパティでグローバル定数の置換 ? を定義出来るらしいので試してみる

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  // これを追加
  define: {
    'process.env': {}
  },
})

ブロックエディターが動いた !

ブロックエディター

WordPress の中の場合の見た目・動作と違う部分は多くありますが、基本的な機能は動作するようです。

せっかくなので、ブロックサイドバーも追加してみます。Gutenberg で公開されている Storybook にはブロックエディターもあり、サイドバー付きのストーリーも存在します。

該当ファイルを見てみると、BlockInspector コンポーネントを使用すればよいようです。エディターキャンバス (BlockCanvas コンポーネント) の高さも 100% に変更しておきます。

import { useState } from "react";
import {
  BlockEditorProvider,
  BlockCanvas,
  BlockInspector,
} from "@wordpress/block-editor";
import { registerCoreBlocks } from "@wordpress/block-library";

import "@wordpress/components/build-style/style.css";
import "@wordpress/block-editor/build-style/style.css";
import "@wordpress/block-library/build-style/common.css";
import "@wordpress/block-library/build-style/style.css";
import "@wordpress/block-library/build-style/editor.css";
import './App.scss';

registerCoreBlocks();

function App() {
  const [blocks, setBlocks] = useState([]);

  return (
    <div className="playground">
      <BlockEditorProvider
        value={blocks}
        onChange={setBlocks}
        onInput={setBlocks}
      >
        <div className="playground__sidebar">
          <BlockInspector />
        </div>
        <div className="playground__content">
          <BlockCanvas height="100%" />
        </div>
      </BlockEditorProvider>
    </div>
  );
}

export default App

さらに、エディターキャンバスとブロックサイドバーをレイアウトするために、Storybook のスタイルを参考に追加してみます。

このスタイルは Sass で記述されているので、sass をインストールする必要があります。さらに、Gutenberg の @wordpress/base-styles パッケージをインポートしているので、これもインストールしておきます。

$ bun i -D sass @wordpress/base-styles

最低限のスタイルとして、以下のように記述しました。CSS 変数は、@wordpres/base-stylesvariables に定義されているものを使用しています。

@import "@wordpress/base-styles/colors";
@import "@wordpress/base-styles/variables";
@import "@wordpress/base-styles/mixins";
@import "@wordpress/base-styles/breakpoints";
@import "@wordpress/base-styles/animations";
@import "@wordpress/base-styles/z-index";

.playground {
	iframe {
		width: 100%;
	}
}

.playground__content {
	position: fixed;
	top: 0;
	right: $sidebar-width;
	bottom: 0;
	left: 0;
	padding: 0 $grid-unit-20;
}

.playground__sidebar {
	position: fixed;
	top: 0;
	right: 0;
	bottom: 0;
	width: $sidebar-width;
	border-left: $border-width solid $gray-300;
	height: auto;
	overflow: auto;
}

サイドバーにはタイポグラフィパネル (フォントサイズのみ) と Advanced パネルしかありませんが、それっぽくなりました !

ブロックエディターサイドバー

State について

ここで、useState を介してコントロールされている blocks にどのような情報が入っているかを確認してみます。

WordPress のブロックエディター上でブロックの Edit コンポーネントが持つものと似ているようですが、RichText データの持ち方 (attributes.content 等) が異なるようです。

State

まとめ

過去にも、WordPress 外でブロックエディターを構築する事を試みた事はありますが、情報も少なく挫折してしまいました。今回、つまづきポイントは多少ありましたが、かなりシンプルな方法で構築出来ることが分かりました。

「Gutenberg as a framework」に関する取り組みはこれからも続いていくと思うので、ますます楽しみです !