logoMiyauchi

ViteでPreactのTypescript環境を構築する

はじめに

Vite は Vue.js の作者の Evan You 氏が開発しているビルドツールです。 ネイティブの ES Module のインポートを利用し、バンドル不要で高速に動作する開発環境を提供します。 Vue3 はもちろん、React や Preact も対応しています。 今回はそんな Vite を使って、Preact プロジェクトの環境構築をします。

できあがったテンプレートはこちらにあります。

やること

preact/cli の default テンプレートに近づけることを目標に、最低限開発に必要なツールを導入していきます。 ツールを個別に導入できるよう、それぞれ順を追って説明しています。

  • Typescript
  • ESLint
  • Prettier
  • Stylelint
  • husky と lint-staged
  • Path Alias

環境構築

まずは、vite のテンプレートを展開しましょう。

npm init vite-app <project-name> --template preact
cd <project-name>
npm i

開発サーバーを立ち上げるとその速さに感動します。

Typescript にする

続いてプロジェクトを Typescript 化しましょう。最小限の構成では次の2つを行うだけです。

1.すべての.jsxファイルを.tsxにします。 2.index.htmlの script タグの src を/src/main.tsxに変更します。

これで開発サーバーを立ち上げると、問題なく実行できるのが確認できます。

実際はこれだけでも動きますが、エディター上でのユーザーエクスペリエンスを向上させるために、さらに設定を加えます。

tsconfig.jsonをプロジェクトルートに設置します。これでエディターに Typescript プロジェクトであることを認識させます。

tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "lib": ["DOM", "DOM.Iterable", "esnext"],
    "allowJs": false,
    "skipLibCheck": false,
    "esModuleInterop": false,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve",
    "jsxFactory": "h",
    "jsxFragmentFactory": "Fragment"
  },
  "include": ["src"]
}

VSCode ではこの時点で、.tsxファイルにエラーが表示されているので、これを修正します。全ての.tsxファイルに次の一文を加えます。

import { h } from 'preact'

またFragmentを使っている場合は、更にそれもインポートします。

import { h, Fragment } from 'preact'

次に、エントリーポイントであるmain.tsxを修正します。 Typescript になったことで、型エラーが検出されています。 document.getElementByIdは戻り値がHTMLElementまたはnullなため、null チェックを入れてあげます。

`index.html`の id に app が必ず存在するなら、`Non-null assertion operator`も使えます。

main.tsx

const el = document.getElementById('app')
if (el) {
  render(<App />, el)
}

続いてvite.config.jsに変更を加えます。

vite.config.js

const config = {
  jsx: {
    factory: 'h',
    fragment: 'Fragment'
  },
  plugins: [preactRefresh()]
}

最小構成で Typescript 化できました。以下は、やらなくても問題ありません。

.jsファイルの撲滅のため、vite.config.js.tsに変更しましょう。また、ES Module 形式に変更し、プロジェクト全体の統一感を高めましょう。

vite.config.tsは以下のようになります。

vite.config.ts

import preactRefresh from '@prefresh/vite'
import type { UserConfig } from 'vite'

const config: UserConfig = {
  jsx: {
    factory: 'h',
    fragment: 'Fragment',
  },

  plugins: [preactRefresh()],
}

export default config

これで Typescript 化は終了です。

ESLint を導入する

リンターのない開発は厳しいので、必ず導入しましょう。

npm i -D eslint eslint-config-preact @typescript-eslint/parser typescript

.eslintrc

{
  "env": {
    "browser": true,
    "es2021": true
  },
  "extends": [
    "eslint:recommended",
    "preact"
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": 12,
    "sourceType": "module"
  },
  "rules": {}
}

package.jsonscriptにリント用のコマンドを用意するとのちのち楽です。

package.json

"scripts": {
  "lint:script": "eslint --ext .ts,tsx --ignore-path .gitignore ."
}

個人的には、fix したくない場面もあるので、--fixは外から付けるようにしています。

さてこれを実行させましょう。

npm run lint:script --fix

VSCode ユーザーは以下の設定もすることで、自動フォーマットを効かせることができます。 ESLint の拡張が必要なので、なければここを参考にインストールしてください。

.vscode/settings.json

{
  "editor.codeActionsOnSave": {
    "source.fixAll": true
  }
}

これによって保存時にフォーマットできました。

husky と lint-staged を設定する

コミット前に、静的チェックを走らせ、エラーコードをコミットできない仕組みにしましょう。

npm i -D husky lint-staged

package.jsonに次を追加します。

package.json

{
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{ts,tsx}": "eslint --fix"
  }
}

これによって、コミット前にコミットファイルのうち該当する拡張子のファイルに対し、ESLint が走ります。

もちろんリントエラーの場合はコミットがキャンセルされます。

Prettier を設定する

Prettier にプロジェクト全体のフォーマットを任せましょう。 また、Typescript のコードでは、セミコロンは視認性が悪くなるため、Prettier で自動的に削除しましょう。

npm i -D prettier eslint-config-prettier

.prettierrc

{
  "trailingComma": "es5",
  "semi": false,
  "singleQuote": true
}

ESLint と Prettier を併用する場合、ルールのバッティングがあるため、.eslintrcを修正します。

.eslintrc

{
  "extends": [
    "eslint:all",
    "preact",
    // 他のルールの下に追加
    "prettier",
    "prettier/@typescript-eslint"
  ]
}

コマンドによってフォーマッターを実行できます。

npm run prettier -w -u .

コミット前に自動フォーマットを適用させたいので、lint-stagedにその設定を加えます。

package.json

{
 "lint-staged": {
    "*.{ts,tsx}": "eslint --fix",
    "*": "prettier -w -u" // prettierは一番最後にします
  }
}

VSCode ユーザーは次の設定によって、自動的にフォーマットできます。 また、例によって拡張が必要なので、なければこちらを参考にインストールしてください。

.vscode/settings.json

{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode"
}

Stylelint を設定する

スタイルファイルもリント対象にしましょう。

npm i -D stylelint stylelint-config-recommended stylelint-config-standard

.stylelintrc

{
  "extends": ["stylelint-config-recommended", "stylelint-config-standard"]
}

package.jsoonを編集して、コマンドと lint-staged を設定します。

{
  "scripts": {
    "lint:style": "stylelint src/**/*.{css,scss}"
  },
  "lint-staged": {
    "*.{ts,tsx}": "eslint --fix",
    "*.{css,scss}": "stylelint --fix",
    "*": "prettier -w -u"
  }
}

VSCode ユーザーは次の設定によって、自動的にフォーマットできます。 拡張が必要なので、なければこちらを参考にインストールしてください。

長くなりましたがこれでリンターとフォーマッターの基本的な設定は終了です。

Path Alias を設定する

モジュールの import はデフォルトでは相対パスを指定しますが、alias を設定して常に同じルートを参照したいです。

vite.config.tstsconfig.json変更して alias を設定しましょう。

Key は`/`から始まらなければなりません。

vite.config.ts

import { join } from 'path'
import type { UserConfig } from 'vite'

const config: UserConfig = {
  alias: {
    '/@/': join(__dirname, 'src'),
  }
}

export default config

tsconfig.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "/@/*": ["src/*"]
    }
  },
  "include": ["src"]
}

これで alias の設定ができました。こんな感じで使います。

main.tsx

import { App } from '/@/app'

/から始まらなければならないのが、少し違和感ありますが、パッケージ名の alias との兼ね合いみたいです。 詳しくはこちらを参照ください。

以上で最低限の環境が構築できました。