esbuildからpreact/compatを利用する

preactを単体で利用する場合には、 tsconfig.jsonjsxFactoryを適切に指定することで動作します。

{
    "compilerOptions": {
        "jsx": "react",
        "jsxFactory": "h"
    }
}

他のライブラリとの組み合わせの際に、Reactを参照していることがあり不足するケースがあります。
@preact/compatは、reactとreact-domの互換APIを提供するパッケージです。
react-reduxなどreactに依存するライブラリをpreact上で動作させられます。

webpackやrollupでバンドルする場合には、aliasプラグインを利用して参照を差し替えます。esbuildの場合は、Javascript APIの Resolve callbacksを利用して同等の処理を実装できます。

import * as esbuild from 'https://deno.land/x/[email protected]/mod.js'
import { resolve } from 'https://deno.land/std/path/mod.ts'

const preactResolvePlugin = {
	name: 'preact-compat resolver',
	setup(build) {
		build.onResolve({ filter: /^react$/ }, args => {
			return { path: resolve('./node_modules/@preact/compat/index.js') }
		})
		build.onResolve({ filter: /^react-dom$/ }, args => {
			return { path: resolve('./node_modules/@preact/compat/index.js') }
		})
	}
}

await esbuild.build({
    entryPoints: ['app.js'],
    bundle: true,
    outfile: 'out.js',
    minify: config_minify,
    plugins: [preactResolvePlugin]
});

esbuild.stop()

なお、このビルドスクリプトはdeno用のものです。
プラグイン実装のうち、resolve()の部分がnodeと異なるため、esbuildのリファレンスを参照して実装すると動作するでしょう。

Jest用のpreact/compat設定

テストランナーのJestからesbuildを呼ぶ場合には esbuild-jestを利用するとesbuildの オプションを指定できます。
ビルド時にはtsconfig.jsonを参照するのですが、jestから起動する場合にはjesct.config.jsから供給しないとオプションが反映されないようです。

jest向けのalias設定と合わせたjest.config.jsの記述(抜粋)は以下のとおりです。

  transform: {
	"\\.[jt]sx?$": ["esbuild-jest", { jsxFactory: "h", target: "safari13" }],
  },
  moduleNameMapper: {
    "^react$": "preact/compat",
    "^react-dom$": "preact/compat",
  },

esbuildがJSXをh()に変形できるため、JSXについてはBabelは不要です。
なお、現状Jestはnodeに依存しているため、この設定はdenoではなくnode用です。

また、条件は不明ながら、esbuild/jestの組み合わせでReferenceError: h is not definedというエラーが発生することがあり、コンポーネントの名称を変更すると直りました。これはかなり不可解です。

テストライブラリ

testing-libraryのバリアントとして、 Preact Testing Librarypreact-hooks-testing-libraryを利用できます。

中馬崇尋
Chuma Takahiro