4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【WordPress】 @wordpress/scriptsとStylusでカスタムブロック自作環境構築

Last updated at Posted at 2020-07-15

Gutenbergブロックエディターのカスタムブロックを自作したい。しかもSassではなくStylusで。という、Stylus大好きスタイラサーのための環境構築編です。@wordpress/scripts をベースとなります。

ベーシックな部分はこちらを参考にさせていただきました。加えてSassに関連する部分をStylus版に変更した感じです。

【WordPress】カスタムブロックの作り方を書いてみた - Qiita -

ディレクトリ構成図

my-block-plugin
  ├ node_modules
  ├ public (公開フォルダ)
  │   ├ assets (buildフォルダ)
  │   └ my-block-plugin.php (プラグインのメインファイル)
  ├ src
  │   ├ editor.js (エディター用JS)
  │   ├ editor.styl (エディター用CSS)
  │   ├ script.js (エディター・フロント両用JS)
  │   └ style.styl (エディター・フロント両用CSS)
  ├ package.json
  ├ webpack.config.js
  └ (yarn.lock)

package.json

package.json (クリックで全体を表示)
package.json
{
  "name": "my-block-plugin",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "wp-scripts build --mode=development --config webpack.config.js --watch",
    "build": "wp-scripts build --mode=production --config webpack.config.js"
  },
  "author": "Watashi",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.10.4",
    "@babel/preset-env": "^7.10.4",
    "@babel/preset-react": "^7.10.4",
    "@lipemat/css-mqpacker": "^8.0.1",
    "@wordpress/block-editor": "^4.3.1",
    "@wordpress/blocks": "^6.20.1",
    "@wordpress/browserslist-config": "^2.7.0",
    "@wordpress/components": "^10.0.1",
    "@wordpress/dependency-extraction-webpack-plugin": "^2.8.0",
    "@wordpress/editor": "^9.20.1",
    "@wordpress/element": "^2.16.0",
    "@wordpress/scripts": "^12.1.1",
    "autoprefixer": "^9.8.5",
    "babel-loader": "^8.1.0",
    "clean-webpack-plugin": "^3.0.0",
    "css-declaration-sorter": "^5.1.2",
    "css-loader": "^3.6.0",
    "mini-css-extract-plugin": "^0.9.0",
    "optimize-css-assets-webpack-plugin": "^5.0.3",
    "path": "^0.12.7",
    "postcss-combine-duplicated-selectors": "^9.0.0",
    "postcss-loader": "^3.0.0",
    "sort-css-media-queries": "^1.5.0",
    "stylus": "^0.54.7",
    "stylus-loader": "^3.0.2",
    "terser-webpack-plugin": "^3.0.6",
    "webpack": "^4.43.0",
    "webpack-cli": "^3.3.12",
    "webpack-fix-style-only-entries": "^0.5.1",
    "webpack-merge": "^5.0.9"
  },
  "browserslist": [
    "extends @wordpress/browserslist-config"
  ]
}

モジュールをざっくり分類するとこんな感じです。

モジュールをざっくり分類 (クリックで全体を表示)
WordPress公式
@wordpress/block-editor
@wordpress/blocks
@wordpress/browserslist-config 
@wordpress/components 
@wordpress/dependency-extraction-webpack-plugin 
@wordpress/editor 
@wordpress/element 
@wordpress/scripts 

JS/BABEL関連
@babel/core
@babel/preset-env 
@babel/preset-react 
babel-loader 
terser-webpack-plugin 

Stylus/CSS関連
@lipemat/css-mqpacker 
autoprefixer 
css-declaration-sorter
css-loader 
mini-css-extract-plugin 
optimize-css-assets-webpack-plugin
postcss-combine-duplicated-selectors
postcss-loader
sort-css-media-queries 
stylus
stylus-loader 
webpack-fix-style-only-entries 

その他
clean-webpack-plugin 
path 
webpack 
webpack-cli 

モジュールごとの役割は webpack.config.jsのコメントにも書いてあります。

今回は使用していませんが、 img-loader, file-loader, url-loader, resolve-url-loader なども、もちろんSass同様に使えます。

webpack.config.js

webpack.config.js (クリックして全体を表示)
webpack.config.js
const DependencyExtractionWebpackPlugin = require('@wordpress/dependency-extraction-webpack-plugin')
const FixStyleOnlyEntriesPlugin         = require('webpack-fix-style-only-entries')
const MiniCssExtractPlugin              = require('mini-css-extract-plugin')
const OptimizeCSSAssetsWebpackPlugin    = require('optimize-css-assets-webpack-plugin')
const TerserWebpackPlugin               = require('terser-webpack-plugin')
const autoprefixer                      = require('autoprefixer')
const path                              = require('path')
const {CleanWebpackPlugin}              = require('clean-webpack-plugin')

// For PostCSS
const cssDeclarationSorter              = require('css-declaration-sorter')
const cssMqpacker                       = require('@lipemat/css-mqpacker')
const postCssCombineDuplicatedSelectors = require('postcss-combine-duplicated-selectors')
const sortCssMediaQueries               = require('sort-css-media-queries')

module.exports = (env, argv) => {
  // モードが development なら trueを返す
  function isDevelopment () {
    return argv.mode === 'development'
  }

  var config = {
    entry: {
      editor: path.resolve(__dirname, 'src', 'editor.js'),
      script: path.resolve(__dirname, 'src', 'script.js'),
      style: path.resolve(__dirname, 'src', 'style.styl')
    },

    output: {
      filename: '[name].js',
      path: path.resolve(__dirname, 'public', 'assets')
    },

    optimization: {
      minimizer: [
        // JavaScriptを圧縮する
        new TerserWebpackPlugin({
          // sourceMapをmodeによって削除する
          sourceMap: isDevelopment()
        }),
        // CSSを圧縮する
        new OptimizeCSSAssetsWebpackPlugin(
          {
            cssProcessorOptions: {
              map: {
                inline: false,
                annotation: true
              }
            }
          }
        )
      ]
    },

    plugins: [
      // ビルドの度にビルドフォルダーを削除する
      new CleanWebpackPlugin(),

      // assets.phpファイルを出力する
      new DependencyExtractionWebpackPlugin(),

      // CSSをentryに指定したとき同名の不要なjsが出力されないようにする
      new FixStyleOnlyEntriesPlugin({
        extensions: ['styl', 'css']
      }),

      // CSSファイルを別ファイルで出力する
      new MiniCssExtractPlugin({
        filename: '[name].css'
      })
    ],

    devtool: 'source-map',

    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          use: {
            loader: 'babel-loader',
            options: {
              presets: [
                '@babel/preset-env',
                [
                  '@babel/preset-react',
                  {
                    // 通常は'React.createElement.xxxx'を記述しますが、WordPressのブロックエディターはWordPress用のReactを使用するため、以下のように記述します
                    'pragma': 'wp.element.createElement',
                    'pragmaFrag': 'wp.element.Fragment',
                    'development': isDevelopment()
                  }
                ]
              ]
            }
          }
        },
        {
          test: /\.(styl|css)$/,
          use: [
            {
              loader: MiniCssExtractPlugin.loader
            },
            {
              loader: 'css-loader'
            },
            {
              loader: 'postcss-loader',
              options: {
                ident: 'postcss',
                plugins: () => [
                  // 重複するセレクタをまとめる
                  postCssCombineDuplicatedSelectors({
                    removeDuplicatedProperties: true
                  }),
                  // 重複するメディアクエリをまとめる
                  cssMqpacker({
                    // メディアクエリをソート
                    sort: sortCssMediaQueries
                  }),
                  // CSSの属性をソート
                  cssDeclarationSorter({
                    order: 'smacss'
                  }),
                  // ベンダープレフィックスを付与
                  autoprefixer({
                    // CSSグリッドを使う
                    grid: true
                  })
                ]
              }
            },
            {
              loader: 'stylus-loader'
            }
          ]
        }
      ]
    }
  }

  return config
}


my-block-plugin.php

プラグインのメインファイル。

my-block-plugin.php (クリックして全体を表示)
my-block-plugin.php
<?php
/**
 * Plugin Name: 僕のStylusブロック
 */

if ( !defined( 'ABSPATH' ) ) {
  exit;
}

add_action( 'init', 'my_block_plugin_register' );

function my_block_plugin_register() {
  $editor_asset = include( plugin_dir_path( __FILE__ ) . 'assets/editor.asset.php' );
  $script_asset = include( plugin_dir_path( __FILE__ ) . 'assets/script.asset.php' );

  // エディター用JSを登録
  wp_register_script(
    'my-block-plugin-editor-script',
    plugins_url( 'assets/editor.js', __FILE__ ),
    $editor_asset['dependencies'],
    $editor_asset['version']
  );

  // エディター・フロント両用JSを登録
  wp_register_script(
    'my-block-plugin-script',
    plugins_url( 'assets/script.js', __FILE__ ),
    $script_asset['dependencies'],
    $script_asset['version']
  );

  // エディター用CSSを登録
  wp_register_style(
    'my-block-plugin-editor-style',
    plugins_url( 'assets/editor.css', __FILE__ ),
    array( 'wp-edit-blocks' ),
    filemtime( plugin_dir_path( __FILE__ ) . 'assets/editor.css' )
  );

  // エディター・フロント両用CSSを登録
  wp_register_style(
    'my-block-plugin-style',
    plugins_url( 'assets/style.css', __FILE__ ),
    array(),
    filemtime( plugin_dir_path( __FILE__ ) . 'assets/style.css' )
  );

  // カスタムブロックの登録
  register_block_type(
    'my-block-plugin/my-block',
    [
      'editor_script' => 'my-block-plugin-editor-script', // エディター用JSを読み込む
      // 'script' => 'my-block-plugin-script', // エディター・フロント両用JSを読み込む
      // 'editor_style'  => 'my-block-plugin-editor-style', //  エディター用CSSを読み込む
      'style'  => 'my-block-plugin-style' //  エディター・フロント両用CSSを読み込む
    ]
  );
}

my_block_plugin , my-block-plugin の文字列はご自分のプラグイン名に合わせて変更してください。

register_block_type の部分では必要ないJS, CSSは必要に応じてコメントアウトしてください。今回のサンプルでは editor_scriptstyle のみ使うので他はコメント化しています。

wp_register_style の第4引数 filetime は、ビルドするごとにブラウザキャッシュを消すための小技です。ただし、Stylus側が空でCSSが出力されていない場合にはエラーが出るのでご注意ください。

editor.js

editor.js (クリックして全体を表示)
editor.js
import { RichText } from '@wordpress/editor'
import { registerBlockType } from '@wordpress/blocks'

registerBlockType( 'my-block-plugin/my-block', {
  title: '僕のサンプル 青いブロック',
  category: 'common',
  icon: 'wordpress-alt',
  attributes: {
    text: {
      type: 'string',
      source: 'html',
      selector: 'p',
    },
  },

  edit: ( props ) => {
    const onChangeText = ( newText ) => {
      props.setAttributes( { text: newText } );
    };
    return (
      <div>
        <RichText
          className={ props.className }
          tagName="p"
          value={ props.attributes.text }
          onChange={ onChangeText }
        />
      </div>
    );
  },

  save: ( props ) => {
    return (
      <div>
        <p>{ props.attributes.text }</p>
      </div>
    );
  },
});

エディター画面のみで読み込まれるJS。実際にブロックを設計するファイルです。ここをReactで書いていきます。

内容は、動作確認用として、テキストを入力して表示するブロックのサンプルとなります。今回は環境構築が目的ということで、具体的な中身の解説は今回は割愛します。

style.styl

同じく動作確認用です。エディター、フロント両方で読み込まれるCSSです。

style.styl (クリックして全体を表示)
style.styl
.wp-block-my-block-plugin-my-block
  background-color: #5cb1e3
  border-radius: 4px
  color:  rgba(255,255,255,0.95)
  margin-top: 24px
  margin-bottom: 24px
  padding: 24px

動作確認

ターミナルでプラグインディレクトリへ移動して、ビルドしましょう。

npm run build

プラグインの public フォルダをまるごとWordPressの plugins フォルダ直下に入れます。僕は、テスト環境では publicフォルダのシンボリックリンクを作って pluginsフォルダ直下に入れています…が、もっといい方法があったら教えてください。

WordPressプラグイン画面で「僕のStylusブロック」を有効化します。

screenshot 2020-07-15 12.34.26.jpg

投稿編集画面へ行き、自作ブロックが「一般ブロック」の中に登録されているか確認し、あったら使ってみましょう。青い背景のブロックができます。

screenshot 2020-07-15 12.36.01.jpg

文字を書いて保存します。

screenshot 2020-07-15 13.02.54.jpg screenshot 2020-07-15 13.03.04.jpg

フロント側をプレビューして文字が表示されていればOKです。

以上 @wordpress/scripts と Stylusを使った環境構築の一例でした。

参考文献

【WordPress】カスタムブロックの作り方を書いてみた - Qiita -
registerBlockTypeで指定する内容について - Qiita -

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?