DEV Community

Cover image for Setting Up a Modern Preact Application With Typescript, Vite and Vitest
Sophia Brandt
Sophia Brandt

Posted on • Originally published at rockyourcode.com on

Setting Up a Modern Preact Application With Typescript, Vite and Vitest

Wiring up a TypeScript environment with Preact, Vite and Vitest and vitest-dom

I have heard good things about Vite and Vitest. When I gave them a test-drive, I stumbled over some minor annoyances in getting the whole suite running.

I'm writing down the steps I took, maybe they help you.

The article is basically a re-write of a blog post by Tomoki Miyaci adjusted to my needs.

Tooling:

  • TypeScript
  • Vite
  • Vitest (with vitest-dom)
  • Preact
  • Prettier
  • ESLint
  • husky & lint-staged
  • commitlint

All my commands use pnpm, feel free to replace them with npm or yarn.

Vite

pnpm create vite <project-name> --template preact-ts
cd <project-name>
pnpm install
Enter fullscreen mode Exit fullscreen mode

ESLint & prettier

pnpm i -D eslint eslint-config-prettier \
          prettier \
          @typescript-eslint/parser
Enter fullscreen mode Exit fullscreen mode

Create a new file called .eslintrc with the following content:

{
  "env": {
    "browser": true,
    "es2021": true
  },
  "extends": ["eslint:recommended", "preact", "prettier"],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "settings": {
    "jest": {
      "version": 27
    }
  },
  "ignorePatterns": ["*.d.ts"],
  "rules": {}
}
Enter fullscreen mode Exit fullscreen mode

One wrinkle was the Jest settings option. I know I wanted to use Vite, but eslint needs to know the Jest version for some of its tests.

Here is my Prettier configuration (.prettierrc), adjust to your needs:

{
  "trailingComma": "es5",
  "semi": false,
  "singleQuote": true
}
Enter fullscreen mode Exit fullscreen mode

Let's adjust package.json:

{
  "scripts": {
    "lint:fix": "eslint --fix --ext .ts,tsx --ignore-path .gitignore .",
    "prettier:write": "prettier -u -w --ignore-path .gitignore \"*.{ts,tsx,css,html}\"",
}
Enter fullscreen mode Exit fullscreen mode

husky & lint-staged

Install lint-staged.

pnpm add -D lint-staged
Enter fullscreen mode Exit fullscreen mode

Create a .lintstagedrc.json file in your project root folder.

Here you can add the commands that lint-staged should run on staging your files.

{
    "*.{ts,tsx}": ["pnpm run lint:fix", "pnpm run prettier:write"],
    "*.{html,css,js,json,md}": "pnpm run prettier:write"
}
Enter fullscreen mode Exit fullscreen mode

We run ESLint and prettier on TypeScript files in sequential order. For other files, prettier suffices.

Now we need husky.

We initialize it with a script:

pnpm dlx husky-init && pnpm install
Enter fullscreen mode Exit fullscreen mode

The above command will setup the tool and create the necessary files and hooks.

The default hook runs before committing the files to the staging area.

You can find it under .husky/pre-commit:

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

pnpm exec lint-staged
## if you want to run your tests before commiting,
## uncomment next line
# pnpm exec vitest run
Enter fullscreen mode Exit fullscreen mode

commitlint

commitlint checks if your commit messages meet the conventional commit format.

Example:

chore: run tests on travis ci

I personally find it quite useful to enforce a uniform commit style.

commitlint pairs well with husky.

pnpm add -D @commitlint/{config-conventional,cli}
Enter fullscreen mode Exit fullscreen mode

Let's add a configuration file (.commitlintrc.json):

{
  "extends": ["@commitlint/config-conventional"]
}
Enter fullscreen mode Exit fullscreen mode

Now we need a hook for husky. Run the following command in your terminal:

pnpm dlx husky add \
  .husky/commit-msg 'pnpm exec commitlint --edit'
Enter fullscreen mode Exit fullscreen mode

Vitest

Installation:

pnpm add -D vitest vitest-dom happy-dom
Enter fullscreen mode Exit fullscreen mode

vitest-dom extends the standard Jest matchers with convenient methods like .toBeDisabled.

Now you can write tests that assert on the state of the DOM.

The package is a fork of @testing-library/jest-dom.

Configuring vitest with the .vite.config.ts:

/// <reference types="vitest" />
import { fileURLToPath } from 'url'
import { defineConfig } from 'vite'
import preact from '@preact/preset-vite'

// https://vitejs.dev/config/
export default defineConfig({
  define: {
    'import.meta.vitest': 'undefined',
  },
  plugins: [preact()],
  test: {
    environment: 'happy-dom',
    setupFiles: ['./__test__/test-setup.ts'],
    includeSource: ['src/**/*.{ts,tsx}'],
    coverage: {
      reporter: ['text-summary', 'text'],
    },
    mockReset: true,
    restoreMocks: true,
  },
})
Enter fullscreen mode Exit fullscreen mode

The code section import.meta.vitest allows you to run tests within your source code.

For my test setup I've made a separate __test__ folder with a file called test-setup.ts:

import 'vitest-dom/extend-expect'
import * as domMatchers from 'vitest-dom/matchers'
import { expect } from 'vitest'

expect.extend(domMatchers)
Enter fullscreen mode Exit fullscreen mode

Here I add the vitest-dom extra matchers. You can add more setup logic if needed.

Sources

Top comments (1)

Collapse
 
topeogunleye profile image
topeogunleye

Thank you for sharing your detailed experience with Vite and Vitest! Your step-by-step guide is incredibly helpful, especially for those who might encounter similar minor annoyances. I appreciate the thoroughness of your instructions, from initializing the project with Vite to setting up ESLint, Prettier, and integrating Vitest. The inclusion of specific configuration files and scripts makes it easy to follow along. Great job on adapting Tomoki Miyaci's blog post to suit your needs and providing such a valuable resource to the community!