WordPressカスタムブロック開発 TypeScript BABEL

開発の趣旨

Babel(webpackのbabel-loader)でTypeScriptをトランスパイルする。

ビルド時のトランスパイル手段に下記があるらしいですが、どのような違いがありますか?

Babel(webpackのbabel-loader)
ts-loader

  1. Babel(babel-loader):
  • JavaScriptのトランスパイラで、新しいJavaScript構文を古いバージョンに変換します。
  • TypeScriptもサポートしていますが、型チェックは行いません。
  • プラグインシステムが豊富で、様々な変換や最適化が可能です。
  • 設定が柔軟で、細かいカスタマイズが可能です。
  1. ts-loader:
  • TypeScript専用のローダーです。
  • TypeScriptのコンパイラ(tsc)を使用してトランスパイルします。
  • 型チェックを行うため、型の問題を早期に発見できます。
  • TypeScriptの設定ファイル(tsconfig.json)を直接使用します。

@wordpress/scripts(wp-scripts)を使用する場合、Babelを使用してトランスパイルすることが推奨されます。その理由は以下の通りです:

  1. 互換性: wp-scriptsは内部でBabelを使用しているため、Babelを使用することでwp-scriptsの設定と最も互換性が高くなります。
  2. 設定の簡素化: wp-scriptsの既存の設定を利用できるため、追加の設定が最小限で済みます。
  3. パフォーマンス: Babelはts-loaderよりも一般的に高速です。特に大規模なプロジェクトでその差が顕著になります。
  4. エコシステム: WordPressの開発エコシステムはBabelを中心に構築されており、関連するプラグインやツールとの親和性が高くなります。
  5. 柔軟性: Babelは豊富なプラグインエコシステムを持っており、必要に応じて追加の変換や最適化を行うことができます。

手順

Docker開発コンテナー用意

docker-compose.yml

version: "3.7"
services:
 db:
  image: mysql:8.0
  container_name: mysql8_02
  restart: always
  environment:
    MYSQL_ROOT_PASSWORD: password # rootユーザのパスワード
    MYSQL_DATABASE: db_local # WordPress用データベース名
    MYSQL_USER: wp_user # WordPress用データベース接続ユーザ名
    MYSQL_PASSWORD: password # WordPress用データベース接続パスワード
 WordPress:
  image: wordpress:latest
  container_name: wordpress_02
  restart: always
  depends_on:
     - db
  ports:
     - 10100:80
  environment:
    WORDPRESS_DB_HOST: db:3306 # データベースサーバ名:ポート番号
    WORDPRESS_DB_USER: wp_user # WordPress用データベース接続ユーザ名(dbの内容に合わせる)
    WORDPRESS_DB_PASSWORD: password # WordPress用データベース接続パスワード(dbの内容に合わせる)
    WORDPRESS_DB_NAME: db_local # WordPress用データベース名(dbの内容に合わせる)
    WORDPRESS_DEBUG: 1 # デバッグモードON
    WORDPRESS_CONFIG_EXTRA: | # wp-config.phpの追加設定
        define('WP_DEBUG', true);
        define('WP_DEBUG_LOG', true);
        define('WP_DEBUG_DISPLAY', false);
  volumes:
    - ./wp-content:/var/www/html/wp-content
  #    - ./html:/var/www/html
    - ./debug.log:/var/www/html/wp-content/debug.log
 phpmyadmin:
  image: phpmyadmin/phpmyadmin:latest
  container_name: phpmyadmin_ingrid_02
  restart: always
  depends_on:
     - db
  ports:
     - 10101:80
docker-compose up -d

ターミナルで以下のコマンドを実行し、package.jsonを作成します

npm init -y

必要なパッケージのインストール

WordPress scriptsをインストールします

npm install --save-dev @wordpress/scripts

TypeScript関連パッケージをインストールします

npm install --save-dev typescript ts-loader @types/react @types/react-dom

ESLintとPrettierをインストールします

npm install --save-dev eslint eslint-config-prettier prettier

テスト関連パッケージをインストールします

npm install --save-dev jest @testing-library/react @testing-library/jest-dom

WordPress関連の型定義とパッケージをインストールします

npm install --save-dev @types/wordpress__block-editor @types/wordpress__blocks @wordpress/block-editor @wordpress/blocks

プロジェクトのルートディレクトリにtsconfig.jsonファイルを作成

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": ["blocks"]
}
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    // 宣言ファイル(.d.ts)の型チェックをスキップします。
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    //  デフォルトエクスポートがないモジュールからのデフォルトインポートを許可します。
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    // moduleResolution: モジュール解決戦略を"node"に設定します。
    // これにより、TypeScriptはNode.jsと同じ方法でモジュールを解決します。
    // 具体的には、相対パスや絶対パス、node_modulesディレクトリなどを考慮して
    // インポートされたモジュールのファイルを探索します。これは、Node.js環境での
    // 開発や、Webpackなどのモジュールバンドラーを使用する際に適しています。
    "moduleResolution": "node",
    // resolveJsonModule: JSONモジュールのインポートを許可します。
    // この設定により、.jsonファイルを直接インポートしてTypeScriptコード内で
    // 使用することができます。JSONファイルの内容は自動的にJavaScriptオブジェクトとして
    // 解釈されます。これは設定ファイルやデータファイルを簡単に扱うのに役立ちます。
    "resolveJsonModule": true,
    // isolatedModules: 各ファイルを個別のモジュールとしてトランスパイルします。
    // この設定は、TypeScriptの一部の機能(型のみのインポートなど)が単一ファイルの
    // トランスパイル時に正しく動作しない可能性があることを考慮しています。
    // Babel等の外部トランスパイラを使用する場合に特に重要です。各ファイルが
    // 独立してトランスパイル可能であることを保証します。
    "isolatedModules": true,
    // noEmit: コンパイル結果を出力しません(型チェックのみを行います)。
    // この設定は、TypeScriptを型チェッカーとしてのみ使用し、実際のJavaScriptコードの
    // 生成は別のツール(例:Babel)に任せる場合に有用です。TypeScriptは型チェックを
    // 行いますが、.jsファイルは生成しません。
    "noEmit": true,
    // jsx: JSXのコンパイルを"react-jsx"モードに設定します。
    // この設定は、React 17以降で導入された新しいJSX変換を使用することを指定します。
    // 従来の`React.createElement`の呼び出しではなく、より効率的な形式にJSXを変換します。
    // これにより、バンドルサイズの削減とパフォーマンスの向上が期待できます。
    "jsx": "react-jsx",
    "sourceMap": true,
    "declaration": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "incremental": true,
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

プロジェクトのルートディレクトリにwebpack.config.jsファイルを作成

const defaultConfig = require('@wordpress/scripts/config/webpack.config');
const path = require('path');

module.exports = {
  ...defaultConfig,
  entry: {
    'sample01': './src/blocks/sample01/index.tsx',
    },
    output: {
        path: path.resolve(__dirname, 'wp-content/themes/customtheme/build'),
        filename: '[name].js', // [name] には entry で指定したキーが入る
    },
    module: {
        ...defaultConfig.module,
        rules: [
        ...defaultConfig.module.rules,
        {
            test: /\.tsx?$/,
            use: [
            {
                loader: 'ts-loader',
                options: {
                transpileOnly: true,
                },
            },
            ],
            exclude: /node_modules/,
        },
        ],
    },
    resolve: {
        ...defaultConfig.resolve,
        extensions: ['.ts', '.tsx', '.js', '.jsx'],
    },
};

プロジェクトのルートディレクトリに.eslintrc.jsファイルを作成

module.exports = {
  extends: [
    'plugin:@wordpress/eslint-plugin/recommended',
    'plugin:react/recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier',
  ],
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint'],
  rules: {
    // カスタムルールをここに追加
  },
};

プロジェクトのルートディレクトリに.prettierrcファイルを作成

{
  "singleQuote": true,
  "trailingComma": "es5",
  "tabWidth": 2,
  "semi": true
}

エントリーポイント

Babelのセットアップ

npm i @babel/core @babel/preset-env -D
npm i @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread @babel/preset-typescript -D

.babelrc

{
"presets": ["@babel/env", "@babel/typescript"],
"plugins": ["@babel/proposal-class-properties", "@babel/proposal-object-rest-spread"]
}


webpackのセットアップ

npm i webpack webpack-cli babel-loader -D

@wordpress/scriptsのインストール

(WordPress のブロックエディター用のパッケージ)

npm install @wordpress/scripts @wordpress/blocks @wordpress/i18n @wordpress/block-editor @wordpress/components @wordpress/data react react-dom

package.json追記

{
  "name": "my-theme-custom-block",
  "version": "1.0.0",
  "description": "My first WordPress custom block in a theme",
  "main": "build/index.js",
  "scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@wordpress/scripts": "^x.x.x"
  }
}

@wordpress パッケージの型定義ファイルをインストール

npm install --save-dev @types/wordpress__blocks
npm install --save-dev @types/wordpress__block-editor @types/wordpress__components

webpack.config.jsの設定

npm i webpack webpack-cli babel-loader -D

package.json

{
  // スクリプトセクション: npm run <コマンド名> で実行できるスクリプトを定義
  "scripts": {
    // WordPress用のスクリプトをビルド
    "build": "wp-scripts build",
    // 開発サーバーを起動
    "start": "wp-scripts start",
    // TypeScriptファイルの静的解析を実行
    "lint": "eslint 'blocks/**/*.{ts,tsx}' 'stories/**/*.{ts,tsx}'",
    // 静的解析で見つかった問題を自動修正
    "lint:fix": "eslint --fix 'blocks/**/*.{ts,tsx}' 'stories/**/*.{ts,tsx}'",
    // Prettierを使用してコードを整形
    "format": "prettier --write 'blocks/**/*.{ts,tsx}' 'stories/**/*.{ts,tsx}'",
    // Jestを使用してテストを実行
    "test": "jest",
    // Storybookを起動(UI開発環境)
    "storybook": "start-storybook -p 6006",
    // Storybookをビルド
    "build-storybook": "build-storybook"
  },
  // 開発依存関係: プロジェクトの開発に必要なパッケージを定義
  "devDependencies": {
    // Babel: 最新のJavaScript機能を古いブラウザでも動作するようにトランスパイル
    "@babel/preset-env": "^7.24.4",
    "@babel/preset-react": "^7.24.1",
    "@babel/preset-typescript": "^7.24.1",

    // Storybook: UIコンポーネントの開発・テスト環境
    "@storybook/addon-actions": "^8.0.9",
    "@storybook/addon-links": "^8.0.9",
    "@storybook/addons": "^7.6.17",
    "@storybook/preset-typescript": "^3.8.9",
    "@storybook/react": "^8.0.9",

    // テスト関連: JavaScriptのテストフレームワークとReactコンポーネントのテストユーティリティ
    "@testing-library/jest-dom": "^6.4.2",
    "@testing-library/react": "^15.0.2",
    "@types/jest": "^29.5.12",

    // React関連の型定義
    "@types/react": "^18.2.79",
    "@types/react-dom": "^18.2.25",
    "@types/testing-library__jest-dom": "^6.0.0",

    // WordPress関連の型定義
    "@types/wordpress__block-editor": "^11.5.14",
    "@types/wordpress__blocks": "^12.5.14",
    "@types/wordpress__components": "^23.8.0",
    "@types/wordpress__compose": "^6.1.0",
    "@types/wordpress__core-data": "^5.0.0",
    "@types/wordpress__data": "^7.0.0",
    "@types/wordpress__edit-post": "^7.5.7",
    "@types/wordpress__element": "^2.14.1",
    "@types/wordpress__hooks": "^2.11.0",
    "@types/wordpress__media-utils": "^4.14.4",
    "@types/wordpress__plugins": "^6.0.0",

    // TypeScript用ESLint
    "@typescript-eslint/eslint-plugin": "^7.7.1",
    "@typescript-eslint/parser": "^7.7.1",

    // WordPress JavaScript パッケージ
    "@wordpress/block-editor": "^12.24.0",
    "@wordpress/blocks": "^12.33.0",
    "@wordpress/components": "^27.4.0",
    "@wordpress/compose": "^6.33.0",
    "@wordpress/core-data": "^6.35.0",
    "@wordpress/data": "^9.26.0",
    "@wordpress/edit-post": "^7.33.0",
    "@wordpress/element": "^5.33.0",
    "@wordpress/hooks": "^3.56.0",
    "@wordpress/i18n": "^4.56.0",
    "@wordpress/plugins": "^6.24.0",
    "@wordpress/scripts": "^27.7.8",

    // Linter/Formatter: コードの静的解析と整形ツール
    "eslint": "^8.57.0",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-react": "^7.34.1",
    "prettier": "^3.2.5",

    // その他の開発ツール
    "jest": "^29.7.0",
    "ts-loader": "^9.5.1",
    "typescript": "^5.4.5"
  }
}

参考サイト

Babel 7でTypeScriptをトランスパイルしつつ型チェックをする
〜webpack 4 + Babel 7 + TypeScript + TypeScript EsLint + Prettierの開発環境を構築する〜
https://qiita.com/soarflat/items/d583356e46250a529ed5