SolidJS + vite + tailwindcss な環境を作る(Storybook, vitest対応)
先日公開された State of JS 2021 を見ていて、 SolidJS
という気になるフレームワークを見つけたので、少し齧ってみようと思いました。
まだ情報が少ない、かつ、 テストツールや Storybook などのツールを併用するにはまだひと工夫必要だったので、その対応方法と実際に作成したテンプレートについてサクッとまとめておきます。
SolidJS とは
SolidJS
React や Vue などの Front-end フレームワークの 1 つです。
すごく雑にまとめると、 Svelte と React の融合みたいなものです...!
昨今の Web Frontend では、 React が使われる事が多いなという感覚です。
SolidJS は、 React の宣言的な書き味を持ちつつ、 Svelte のように実 DOM を操作するパフォーマンス性を兼ね備えています。
import { createSignal, onCleanup } from 'solid-js'
import { render } from 'solid-js/web'
const CountingComponent = () => {
const [count, setCount] = createSignal(0)
const interval = setInterval(() => setCount(count => count + 1), 1000)
onCleanup(() => clearInterval(interval))
return <div>Count value is {count()}</div>
}
render(() => <CountingComponent />, document.getElementById('app'))
React から入った自分は、 Svelte の書き味に慣れず... SolidJS が v1 として公開されたタイミングで、シンプルに「良さそう...!」と思いました。
(どのフレームワークも思想がしっかりしていて、選択肢が増えたという気持ちです)
どんな構成を作ったか
今回作成したテンプレートは、以下の構成で作成しました。
開発体験として実用的な環境を構築できそうかという観点で作成しています。
- SolidJS
- TypeScript
- tailwindcss
- vite
- vitest
- ESlint
- Prettier
- Storybook
- pnpm
決してイケイケだからという理由ではありません...!
自分のキャッチアップも兼ねています。本当です。
作成したテンプレートの完成形はこちら starter-for-solid です。
環境を作っていく
1 から環境を作っていきます。
この辺は公式ドキュメントを見ながらでスイスイでした。
パッケージマネージャーには pnpm を使っています。適宜 npm
や yarn
に読み替えてください。
package.json を用意する
まずはここから。
環境を用意するディレクトリで、以下のコマンドを実行。
$ pnpm init
結果
.
`-- package.json
SolidJS 実行に最低限必要なものを入れる
まずは SolidJS を実行できる環境を用意します。
以下のコマンドで install します。
$ pnpm add solid-js
$ pnpm add -D typescript vite vite-plugin-solid
今回 TypeScript を使うため、 tsconfig.json
を作成します。
$ pnpm tsc --init
以下に書き換える。
適宜好きなオプションに書き換えてください。
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"target": "es5",
"module": "ESNext",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"esModuleInterop": true,
"isolatedModules": true,
"jsx": "preserve", // ここ大事
"jsxImportSource": "solid-js", // ここ大事
"moduleResolution": "node",
"newLine": "lf",
"noEmit": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"resolveJsonModule": true,
"types": ["vite/client"],
"skipLibCheck": true,
"strict": true
},
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
vite で SolidJS を扱えるようにします。
// vite.config.ts
import { defineConfig } from 'vite'
import solidPlugin from 'vite-plugin-solid'
export default defineConfig({
plugins: [solidPlugin()],
})
プロジェクトルートに index.html
を作成します。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<link rel="shortcut icon" type="image/ico" href="/src/assets/favicon.ico" />
<title>Solid App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="/src/index.tsx" type="module"></script>
</body>
</html>
/src/index.tsx
にコンポーネントを作成します。
(React の感覚のまま jsx
が使えるの、良い...!)
// ./src/index.tsx
import { createSignal, onCleanup } from 'solid-js'
import { render } from 'solid-js/web'
const CountingComponent = () => {
const [count, setCount] = createSignal(0)
const interval = setInterval(() => setCount(count => count + 1), 1000)
onCleanup(() => clearInterval(interval))
return <div>Count value is {count()}</div>
}
render(() => <CountingComponent />, document.getElementById('root'))
package.json
の scripts
に実行コマンドを追加して実行してみる。
// package.json
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
$ pnpm dev
localhost:3000
にアクセスすると、表示ができていることが確認できるかと思います。
基本的にはこれだけです。次に tailwindcss の設定をしていきます。
tailwindcss を使えるようにする
必要なものは以下です。インストールしていきましょう!
$ pnpm add -D tailwindcss postcss autoprefixer
次に tailwind.config.js
と postcss.config.js
を作成します。
// tailwind.config.js
module.exports = {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
darkMode: 'media',
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
// postcss.config.json
module.exports = {
plugins: [require('tailwindcss'), require('autoprefixer')],
}
後は /src/index.css
と、それを App で読み込ませれば OK です。
試しに文字色を赤にして反映されているか確認してみます。
// ./src/index.css
@tailwind base;
@tailwind components;
@tailwind utilities;
// ./src/index.tsx
import { createSignal, onCleanup } from 'solid-js'
import { render } from 'solid-js/web'
import './index.css'
const CountingComponent = () => {
const [count, setCount] = createSignal(0)
const interval = setInterval(() => setCount(count => count + 1), 1000)
onCleanup(() => clearInterval(interval))
return <div class="text-red-500">Count value is {count()}</div>
}
render(() => <CountingComponent />, document.getElementById('root'))
文字色が変わっていれば OK です。
ここからは、開発時によく使うエコシステムとの連携をしていきます。
ESLint, prettier を追加する
この辺りも難しくはありません。
SolidJS も jsx
を扱えるので、 React 同様の設定でいけます。
SolidJS 用の eslint-plugin-solid
を追加するくらいの差分です。
必要なものをインストールします。
適宜ルールの追加や設定を行ってください。
※本テンプレートでは eslint-plugin-jsx-a11y
と eslint-plugin-import
を追加しています。
$ pnpm add -D eslint eslint-plugin-solid eslint-config-prettier prettier @typescript-eslint/eslint-plugin @typescript-eslint/parser
.eslintrc.js
と .prettierrc
を作成します。
// .eslintrc.js
module.exports = {
root: true,
env: {
node: true,
es2022: true,
},
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
ignorePatterns: ['node_modules/*'],
extends: ['eslint:recommended'],
overrides: [
{
files: ['**/*.ts', '**/*.tsx'],
parser: '@typescript-eslint/parser',
env: {
browser: true,
},
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:solid/typescript',
'eslint-config-prettier',
],
},
],
}
// .prettierrc
{
"arrowParens": "avoid",
"semi": false,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2
}
package.json
の scripts
にコマンドを追加しておきます。
// package.json
{
"scripts": {
"format": "prettier --check './src/**/*.{ts,tsx,json}'",
"format:fix": "prettier --write './src/**/*.{ts,tsx,json}'",
"lint": "eslint './src/**/*.{ts,tsx}'",
"lint:fix": "eslint --fix './src/**/*.{ts,tsx}'"
}
}
pnpm lint
と pnpm format
コマンドを実行してみると、上手く効くと思います。
余談ですが、 ESLint の plugins
と extends
は以下のことを行っています。
- plugins
ルールの追加 - extends
ルールの設定、その他
eslint-plugin-solid の README を見てみると、 "plugins": ["solid"]
の記述がありますが、 plugin:solid/recommended
及び plugin:solid/typescript
の中で plugins: ["solid"]
の設定もしてくれているので、プロジェクトの .eslintrc.js
側では省略可能です。
FYI: https://github.com/joshwilsonvu/eslint-plugin-solid/blob/main/src/index.ts#L33-L90
把握していると捗ります!!
躓いたポイント
後は Storybook とテスト環境を用意するだけなのですが、この辺りで少し躓いたのでその備忘録を記載しておきます。
Storybook
SoliJS のコンポーネントを Storybook でデバッグするための設定をしている時に躓きました。。
結論としては、babel preset に babel-preset-solid
を使うことで解決できました。
また、 tailwindcss も使えるように @storybook/addon-postcss
を追加する必要があります。
Storybook デフォルトでは、トランスパイルに Babel が使われます。
つまり、 SolidJS 構文を Babel がトランスパイル可能な設定を行えば良いことになります。
本テンプレートはデフォルト設定のまま、 webpack4 による build 、 babel によるトランスパイルとしています。(2022/03 時点)
将来的には Storybook の builder を vite に統一しようと思っています...!
まずは必要なライブラリをインストールします。
適宜アドオンの追加や設定を行ってください。
※本テンプレートでは @storybook/addon-a11y
を追加しています。
$ pnpm add -D @storybook/html @storybook/addon-essentials @storybook/addon-postcss babel-preset-solid @babel/preset-typescript
.storybook
ディレクトリに main.js
と preview.js
を作成します。
// ./.storybook/main.js
module.exports = {
stories: ['../src'],
addons: [
'@storybook/addon-essentials',
{
name: '@storybook/addon-postcss',
options: {
postcssLoaderOptions: {
implementation: require('postcss'),
},
},
},
],
// babelrc でも良いですが、他に babel config を流用している箇所が無いため、Storybook の設定に書いています。
babel: async options => ({
...options,
presets: ['solid', '@babel/preset-typescript'],
}),
}
// ./.storybook/preview.js
import '../src/index.css' // tailwindcss 用
package.json
の scripts
にコマンドを追加しておきます。
// package.json
{
"scripts": {
"storybook": "start-storybook -p 6006"
}
}
src
ディレクトリ以下に、何かしら *.stories.tsx
を用意して pnpm storybook
コマンドを実行してみると、 Storybook で SolidJS のコンポーネントが確認できると思います。
テストツール
テストが書けることは必須です。
本テンプレートにもテストを書ける基盤を用意しました。
躓いた点は、 jest を使おうとしていて、その設定が上手く出来ませんでした。。
これは圧倒的に jest 力が足りませんでした。
まず最初に SolidJS でテストを書きたいと思ったときに、普段使っている jest を選びました。
公式にも solid-jest というライブラリが用意されており、これを使えばいけそうと安直に考えていました。
結果、うまくいかず...グヌヌ
発送を切り替え、 vite によるビルド設定を行っているので、そのまま vitest を使えないかと思いました。
そしたら意外とすんなりいけたので、その設定を記載しておきます。
まずは必要なライブラリをインストールします。
$ pnpm add -D vitest jsdom
tsconfig.json
の types
に vitest/global
を設定します。
// tsconfig.json
{
"types": ["vite/client", "vitest/globals"]
}
vite.config.ts
に以下のように変更します。
// vite.config.ts
import { defineConfig } from 'vite'
import solidPlugin from 'vite-plugin-solid'
export default defineConfig({
plugins: [solidPlugin()],
// ここ以下を追加
test: {
environment: 'jsdom',
transformMode: {
web: [/\.[jt]sx?$/],
},
deps: {
inline: [/solid-js/],
},
},
resolve: {
conditions: ['development', 'browser'],
},
})
package.json
の scripts
にコマンドを追加しておきます。
// package.json
{
"scripts": {
"test": "vitest --passWithNoTests" // test ファイルが無いときにエラーにしないオプションを付けています
}
}
これで vitest を使って SolidJS のテストを書くことが出来ます。
ここまでで SolidJS を触る準備が整いました!
自分もこれから触っていきます...!
まとめ
今回は SolidJS の v1 公開を知って、 SolidJS を触るためのテンプレートを作成する手順を記載しました。
まだガッツリ触ったわけでは無いので、設定不備が見つかる可能性はあります。ご了承ください :pray:
SolidJS は SSR も行うことが可能です。
今回は React チックに CSR の SPA の用途を想定しているため、 SSR したい場合は設定がいくつか異なります。
今回作成したテンプレートも都度アップデートを行っていく予定です。
知見のある方いましたら issue や DM でご教示頂けると嬉しいです!
作成したテンプレート:https://github.com/hey3/starter-for-solid