This article has been translated on the basis of machine translation. If there are any mistakes, please fix it.pull request

Building a Typescript Environment for Preact with Vite

Building a Typescript Preact environment using the No bundle tool Vite, along with ESLint and Prettier configuration.

4/1/20227 min read
..
hero image

Introduction

Vite is a build tool developed by Evan You, the author of Vue. It uses native ES Module imports and provide a fast running development environment with no bundling required. Vue3, React and Preact are also supported. In this article, I'll use Vite to build a Preact project environment.

You can find the result template in here.

To do

I will introduce the minimum tools necessary for development. The goal is making it close to the default preact/cli template. The following is a step-by-step explanation of each tool, so that you can introduce them individually.

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

Building Environments

First, let's expand the vite template.

1
2
3
yarn create vite-app <project-name> --template preact
cd <project-name>
yarn
bash

Once the development server is up, you'll be impressed by how fast it is.

Typescript

Then, let's typescript the project. In a minimal configuration, you only need to do two things.

1.Change all .jsx files to .tsx. 2.Change the src of the script tag of index.html to /src/main.tsx.

Now you can start up the development server and see that it runs without any problems.

It should work, but I'll add a few more settings to improve the user experience in the editor.

Place the tsconfig.json in your project root. This will tell the editor to recognize the project as a Typescript project.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"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"]
}
tsconfig.jsonjson

VSCode shows an error in the .tsx file at this point, so fix it. Add this sentence to all the .tsx files.

1
import { h } from 'preact'
ts

If you are using Fragment, import it as well.

1
import { h, Fragment } from 'preact'
ts

Next, fix the entry point, main.tsx. Now that it's in Typescript, a type error has been detected. The document.getElementById returns HTMLElement or null, give it a null check.

You can use the `Non-null assertion operator` if the `app` is always exists in `index.html`.
1
2
3
4
const el = document.getElementById('app')
if (el) {
render(<App />, el)
}
main.tsxtsx

Then make some changes to vite.config.js.

1
2
3
4
5
6
7
8
9
const config = {
jsx: {
factory: 'h',
fragment: 'Fragment'
},
plugins: [preactRefresh()]
}
export default config
vite.config.jsts

I was able to make Typescript with minimal configuration. You don't have to do the following.

Change vite.config.js to .ts to eliminate .js files. Also, change it to the ES Module format to make the whole project more consistent.

The vite.config.ts should look like this

1
2
3
4
5
6
7
8
9
10
11
12
13
import preactRefresh from '@prefresh/vite'
import type { UserConfig } from 'vite'
const config: UserConfig = {
jsx: {
factory: 'h',
fragment: 'Fragment',
},
plugins: [preactRefresh()],
}
export default config
vite.config.tsts

That's the end of Typescript.

Introducing ESLint

Development without a linter is tough, so be sure to install it.

1
yarn add -D eslint eslint-config-preact @typescript-eslint/parser typescript
bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"preact"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"rules": {}
}
.eslintrcjson

It is easy to prepare a linting command in the script of the package.json for later.

It will be easier later on if you have a command for linting in the package.json script of the package.json.

1
2
3
"scripts": {
"lint:script": "eslint --ext .ts,tsx --ignore-path .gitignore ."
}
package.jsonjson

Personally, I don't want to fix some situations, so I use --fix from outside.

Now let's run this.

1
yarn lint:script --fix
bash

VSCode users can also set up the following settings to make the automatic formatting work. An extension to ESLint is required, so if you don't have it, please install it here.

1
2
3
4
5
{
"editor.codeActionsOnSave": {
"source.fixAll": true
}
}
.vscode/settings.jsonjson

This allowed me to format the file on save.

Configuring husky and lint-staged

Before committing, let's run a static check to make sure you can't commit the error code.

1
yarn add -D husky lint-staged
bash

Add the following to package.json.

1
2
3
4
5
6
7
8
9
10
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{ts,tsx}": "eslint --fix"
}
}
package.jsonjson

This causes ESLint to run against any files with the appropriate extensions in the commit file before you commit.

Of course, on a linting error, the commit is canceled.

Configuring Prettier

Let Prettier do the formatting for your entire project. Also, let Prettier automatically remove semicolons in Typescript code, as they are less visible.

1
yarn add -D prettier eslint-config-prettier
bash
1
2
3
4
5
{
"trailingComma": "es5",
"semi": false,
"singleQuote": true
}
.prettierrcjson

When ESLint and Prettier are used together, I need to fix the .eslintrc to avoid duplicate rules.

1
2
3
4
5
6
7
8
9
{
"extends": [
"eslint:all",
"preact",
// Added under other rules
"prettier",
"prettier/@typescript-eslint"
]
}
.eslintrcjson

command to execute the formatter.

1
yarn prettier -w -u .
bash

We want to apply automatic formatting before committing, so we add the setting to lint-staged.

1
2
3
4
5
6
{
"lint-staged": {
"*.{ts,tsx}": "eslint --fix",
"*": "prettier -w -u" // Prettier is the last one to go
}
}
package.jsonjson

VSCode users can format it automatically with the following settings. Also, an extension is required, so if it is not available, please install it here.

1
2
3
4
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
.vscode/settings.jsonjson

Configuring Stylelint

Let's make the style file a target for linting as well.

1
yarn add -D stylelint stylelint-config-recommended stylelint-config-standard
bash
1
2
3
{
"extends": ["stylelint-config-recommended", "stylelint-config-standard"]
}
.stylelintrcjson

Edit the package.jsoon and set the commands and lint-staged.

1
2
3
4
5
6
7
8
9
10
{
"scripts": {
"lint:style": "stylelint src/**/*.{css,scss}"
},
"lint-staged": {
"*.{ts,tsx}": "eslint --fix",
"*.{css,scss}": "stylelint --fix",
"*": "prettier -w -u"
}
}
package.jsonjson

VSCode users can format it automatically with the following settings. Extensions are required, so if you don't have them, install them here.

That's the end of the basic setup of the linker and formatter.

Configuring Path Alias

Module import is relative by default, but we want to set alias to always refer to the same root.

Change the vite.config.ts and tsconfig.json to set the alias.

Keys must start with `/`.
1
2
3
4
5
6
7
8
import { join } from 'path'
import type { UserConfig } from 'vite'
const config: UserConfig = {
alias: {
'/@/': join(__dirname, 'src'),
}
}
vite.config.tsts
1
2
3
4
5
6
7
8
9
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"/@/*": ["src/*"]
}
},
"include": ["src"]
}
tsconfig.jsonjson

Now you can set up alias. We'll use it like this.

1
import { App } from '/@/app'
main.tsxtsx

It's a little strange that it has to start from /, but it seems to combine with the alias of the package name. For more information, please refer to here.

That's the minimum environment you can build.


Edit this page on GitHub

Comments