Open10
Vite + React + eslint + Prettier + Storybook の開発環境構築
作業用コンテナ作成
Vite App
Vite aplication作成
yarn create vite
を実行
$ yarn create vite
yarn create v1.22.17
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
[4/4] Building fresh packages...
success Installed "create-vite@2.6.6" with binaries:
- create-vite
- cva
✔ Project name: … webapp
✔ Select a framework: › react
? Select a variant: › - Use arrow-keys. Return to submit.
react
❯ react-ts
パッケージインストール
$ yarn
実行
$ yarn dev
vite v2.6.14 dev server running at:
> Local: http://localhost:3000/
> Network: use `--host` to expose
ready in 418ms.
Eslintの導入
TSLint
は2019年で非推奨になったので、Typescript ESLint
を使用する
ESLintルール一覧
eslintのインストール
yarn add --dev eslint
.eslintrcの作成
yarn eslint --init
? How would you like to use ESLint? …
To check syntax only
▸ To check syntax and find problems
To check syntax, find problems, and enforce code style
? What type of modules does your project use? …
▸ JavaScript modules (import/export)
CommonJS (require/exports)
None of these
? Which framework does your project use? …
▸ React
Vue.js
None of these
? Does your project use TypeScript? ‣ No / > Yes
? Where does your code run? … (Press <space> to select, <a> to toggle all, <i> to invert selection)
✔ Browser
✔ Node
? What format do you want your config file to be in? …
JavaScript
▸ YAML
JSON
The config that you've selected requires the following dependencies:
eslint-plugin-react@latest @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest
? Would you like to install them now with npm? ‣ > No / Yes
最後のパッケージインストールはNPMでされるため、Noにしてyarnでインストールする
yarn add --dev eslint-plugin-react@latest @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest
スクリプト追加
lint
を作成
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"serve": "vite preview",
"lint": "eslint . --ext .js,.jsx,.ts,.tsx"
},
ESLintのルール追加
react/react-in-jsx-scope のルールを off
v17 より前は import React from 'react'
が無いとエラーになっていたが、現状はimport不要となっている。
しかし、plugin:react/recommended
でimport React from 'react'
がないとエラーになるようになっているため、react/react-in-jsx-scope
をoffにする
.eslintrc.yml
rules:
react/react-in-jsx-scope: off
eslint-config-prettierの追加
共有設定prettierを取り込むことで、Prettierと競合しそうなESLintの組み込みルールを全部OFFにしてくれる。
offになるrulu
yarn add --dev prettier eslint-config-prettier
version8からはこの1行でよくなった
.eslintrc.yml
extends:
- prettier
今はeslint-plugin-prettier
を使用する方法は非推奨
- エディターにフォーマットの警告が前面にでてきてしまう
- 直接 Prettier を実行するより遅い
- eslintとprettierの間にレイヤーを挟んでいるため不具合が起きる可能性がある
eslint-plugin-react-hooksの追加
yarn add --dev eslint-plugin-react-hooks
.eslintrc.yml
plugins:
- react
- react-hooks
- "@typescript-eslint"
reactプラグイン用にバージョン指定
プラグイン項目の下にsettings
を追加
.eslintrc.yml
plugins:
- react
- react-hooks
- '@typescript-eslint'
settings:
react:
version: detect
EditorConfig for VS Code
対応しているプロパティ
indent_style
indent_size
tab_width
end_of_line (on save)
insert_final_newline (on save)
trim_trailing_whitespace (on save)
Prettierルールの追加
.prettierrcの作成
.prettierrc.yml
printWidth: 90
tabWidth: 2
useTabs: false
semi: false
singleQuote: true
trailingComma: none
arrowParens: avoid
.prettierignoreの作成
.prettierignore
node_modules/*
VSCodeの設定
Prettierの拡張追加
Setting.jsonにファイルセーブ時に自動でフォーマットが実行されるようにする
setting.json
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.formatOnSave": true, // save時に自動format
}
型のインストール
prettier
とeslint
の型をインストール
npx typesync
Need to install the following packages:
typesync
Ok to proceed? (y) y
» TypeSync v0.8.0
✔ 2 new typings added.
📦 webapp — package.json (2 new typings added, 0 unused typings removed)
├─ + @types/prettier
└─ + @types/eslint
Storybookの追加
yarnでインストール
yarn add -D @storybook/cli
初期化
yarn sb init
yarn run v1.22.17
warning package.json: No license field
$ /home/vscode/develop/webapp/node_modules/.bin/sb init
sb init - the simplest way to add a Storybook to your project.
• Detecting project type. ✓
warning package.json: No license field
warning webapp@0.0.0: No license field
[1/4] Resolving packages...
warning @storybook/react > @storybook/core > @storybook/core-server > @storybook/builder-webpack4 > webpack-hot-middleware > querystring@0.2.1: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
warning @storybook/react > @storybook/core > @storybook/core-server > @storybook/manager-webpack4 > webpack-dev-middleware > webpack-log > uuid@3.4.0: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.
warning @storybook/addon-essentials > @storybook/addon-docs > @jest/transform > jest-haste-map > sane@4.1.0: some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added
[2/4] Fetching packages...
[3/4] Linking dependencies...
warning " > @storybook/cli@6.4.9" has unmet peer dependency "jest@*".
warning " > babel-loader@8.2.3" has unmet peer dependency "webpack@>=2".
[4/4] Building fresh packages...
success Saved lockfile.
warning webapp@0.0.0: No license field
success Saved 273 new dependencies.
info Direct dependencies
├─ @storybook/addon-actions@6.4.9
├─ @storybook/addon-essentials@6.4.9
├─ @storybook/addon-links@6.4.9
└─ @storybook/react@6.4.9
info All dependencies
├─ @base2/pretty-print-object@1.0.1
├─ @bcoe/v8-coverage@0.2.3
├─ @cnakazawa/watch@1.0.4
├─ @discoveryjs/json-ext@0.5.6
├─ @emotion/cache@10.0.29
├─ @emotion/css@10.0.27
├─ @emotion/is-prop-valid@0.8.8
├─ @emotion/styled-base@10.3.0
├─ @emotion/styled@10.3.0
├─ @emotion/stylis@0.8.5
├─ @emotion/unitless@0.7.5
├─ @gar/promisify@1.1.2
├─ @istanbuljs/load-nyc-config@1.1.0
├─ @jest/transform@26.6.2
├─ @mdx-js/loader@1.6.22
├─ @mdx-js/react@1.6.22
├─ @mrmlnc/readdir-enhanced@2.2.1
├─ @npmcli/fs@1.1.0
├─ @npmcli/move-file@1.1.2
├─ @pmmmwh/react-refresh-webpack-plugin@0.5.4
├─ @popperjs/core@2.11.2
├─ @storybook/addon-actions@6.4.9
├─ @storybook/addon-backgrounds@6.4.9
├─ @storybook/addon-controls@6.4.9
├─ @storybook/addon-docs@6.4.9
├─ @storybook/addon-essentials@6.4.9
├─ @storybook/addon-links@6.4.9
├─ @storybook/addon-measure@6.4.9
├─ @storybook/addon-outline@6.4.9
├─ @storybook/addon-toolbars@6.4.9
├─ @storybook/addon-viewport@6.4.9
├─ @storybook/channel-websocket@6.4.9
├─ @storybook/core-server@6.4.9
├─ @storybook/manager-webpack4@6.4.9
├─ @storybook/postinstall@6.4.9
├─ @storybook/react-docgen-typescript-plugin@1.0.2-canary.253f8c1.0
├─ @storybook/react@6.4.9
├─ @storybook/source-loader@6.4.9
├─ @types/color-convert@2.0.0
├─ @types/color-name@1.1.1
├─ @types/glob@7.2.0
├─ @types/graceful-fs@4.1.5
├─ @types/html-minifier-terser@5.1.2
├─ @types/istanbul-lib-coverage@2.0.4
├─ @types/istanbul-lib-report@3.0.0
├─ @types/istanbul-reports@3.0.1
├─ @types/minimatch@3.0.5
├─ @types/node-fetch@2.5.12
├─ @types/overlayscrollbars@1.12.1
├─ @types/react-syntax-highlighter@11.0.5
├─ @types/source-list-map@0.1.2
├─ @types/tapable@1.0.8
├─ @types/uglify-js@3.13.1
├─ @types/webpack-sources@3.2.0
├─ @types/webpack@4.41.32
├─ @types/yargs-parser@20.2.1
├─ @types/yargs@15.0.14
├─ acorn-walk@7.2.0
├─ airbnb-js-shims@2.2.1
├─ ansi-html-community@0.0.8
├─ array-uniq@1.0.3
├─ array.prototype.flat@1.2.5
├─ array.prototype.map@1.0.4
├─ arrify@2.0.1
├─ asynckit@0.4.0
├─ autoprefixer@9.8.8
├─ babel-plugin-add-react-displayname@0.0.5
├─ babel-plugin-istanbul@6.1.1
├─ babel-plugin-macros@2.8.0
├─ babel-plugin-named-asset-import@0.3.8
├─ babel-plugin-react-docgen@4.2.1
├─ babel-plugin-syntax-jsx@6.18.0
├─ batch-processor@1.0.0
├─ better-opn@2.1.1
├─ bser@2.1.1
├─ c8@7.11.0
├─ cacache@15.3.0
├─ call-me-maybe@1.0.1
├─ camel-case@4.1.2
├─ capture-exit@2.0.0
├─ clean-css@4.2.4
├─ clean-stack@2.2.0
├─ cli-table3@0.6.0
├─ cliui@7.0.4
├─ clsx@1.1.1
├─ combined-stream@1.0.8
├─ common-path-prefix@3.0.0
├─ compressible@2.0.18
├─ compression@1.7.4
├─ compute-scroll-into-view@1.0.17
├─ copy-to-clipboard@3.3.1
├─ core-js-pure@3.20.2
├─ cp-file@7.0.0
├─ cpy@8.1.2
├─ css-select@4.2.1
├─ css-what@5.1.0
├─ dedent@0.7.0
├─ deep-object-diff@1.1.0
├─ delayed-stream@1.0.0
├─ detect-port-alt@1.1.6
├─ detect-port@1.3.0
├─ dom-converter@0.2.0
├─ dom-serializer@1.3.2
├─ domutils@2.8.0
├─ dot-case@3.0.4
├─ downshift@6.1.7
├─ duplexer@0.1.2
├─ element-resize-detector@1.2.4
├─ endent@2.1.0
├─ error-stack-parser@2.0.6
├─ es-array-method-boxes-properly@1.0.0
├─ es-get-iterator@1.1.2
├─ es5-shim@4.6.4
├─ es6-shim@0.35.6
├─ escodegen@2.0.0
├─ estree-to-babel@3.2.1
├─ execa@1.0.0
├─ fast-json-parse@1.0.3
├─ fault@1.0.4
├─ filesize@6.1.0
├─ find-root@1.1.0
├─ foreground-child@2.0.0
├─ fork-ts-checker-webpack-plugin@4.1.6
├─ form-data@3.0.1
├─ format@0.2.2
├─ function.prototype.name@1.1.5
├─ functions-have-names@1.2.2
├─ fuse.js@3.6.1
├─ get-caller-file@2.0.5
├─ get-package-type@0.1.0
├─ github-slugger@1.4.0
├─ glob-promise@3.4.0
├─ glob-to-regexp@0.4.1
├─ global-modules@2.0.0
├─ global-prefix@3.0.0
├─ globalthis@1.0.2
├─ gzip-size@5.1.1
├─ has-glob@1.0.0
├─ he@1.2.0
├─ highlight.js@10.7.3
├─ hoist-non-react-statics@3.3.2
├─ html-escaper@2.0.2
├─ html-minifier-terser@5.1.1
├─ html-tags@3.1.0
├─ htmlparser2@6.1.0
├─ icss-utils@4.1.1
├─ immer@8.0.1
├─ indent-string@4.0.0
├─ ip@1.1.5
├─ is-absolute-url@3.0.3
├─ is-arguments@1.1.1
├─ is-dom@1.1.0
├─ is-map@2.0.2
├─ is-object@1.0.2
├─ is-root@2.1.0
├─ is-set@2.0.2
├─ is-stream@1.1.0
├─ is-window@1.0.2
├─ istanbul-lib-coverage@3.2.0
├─ istanbul-lib-instrument@5.1.0
├─ istanbul-reports@3.1.3
├─ iterate-iterator@1.0.2
├─ iterate-value@1.0.2
├─ jest-haste-map@26.6.2
├─ jest-serializer@26.6.2
├─ jest-worker@26.6.2
├─ junk@3.1.0
├─ klona@2.0.5
├─ lower-case@2.0.2
├─ lowlight@1.20.0
├─ makeerror@1.0.12
├─ mdast-util-to-string@1.1.0
├─ merge-stream@2.0.0
├─ microevent.ts@0.1.1
├─ min-indent@1.0.1
├─ minipass-collect@1.0.2
├─ minipass-flush@1.0.5
├─ minipass-pipeline@1.2.4
├─ minizlib@2.1.2
├─ nested-error-stacks@2.1.0
├─ nice-try@1.0.5
├─ node-int64@0.4.0
├─ normalize-range@0.1.2
├─ npm-run-path@2.0.2
├─ nth-check@2.0.1
├─ num2fraction@1.2.2
├─ objectorarray@1.0.5
├─ on-headers@1.0.2
├─ open@7.4.2
├─ overlayscrollbars@1.13.1
├─ p-all@2.1.0
├─ p-event@4.2.0
├─ p-filter@2.1.0
├─ p-timeout@3.2.0
├─ param-case@3.0.4
├─ pascal-case@3.1.2
├─ path-key@2.0.1
├─ pkg-up@3.1.0
├─ postcss-flexbugs-fixes@4.2.1
├─ postcss-loader@4.3.0
├─ postcss-modules-extract-imports@2.0.0
├─ postcss-modules-local-by-default@3.0.3
├─ postcss-modules-scope@2.2.0
├─ postcss-modules-values@3.0.0
├─ postcss-selector-parser@6.0.8
├─ postcss@7.0.39
├─ pretty-error@2.1.2
├─ prismjs@1.26.0
├─ promise.allsettled@1.0.5
├─ promise.prototype.finally@3.1.3
├─ raw-loader@4.0.2
├─ react-colorful@5.5.1
├─ react-docgen-typescript@2.2.2
├─ react-docgen@5.4.0
├─ react-draggable@4.4.4
├─ react-element-to-jsx-string@14.3.4
├─ react-error-overlay@6.0.10
├─ react-fast-compare@3.2.0
├─ react-helmet-async@1.2.2
├─ react-inspector@5.1.1
├─ react-popper-tooltip@3.1.1
├─ react-popper@2.2.5
├─ react-refresh@0.10.0
├─ react-router-dom@6.2.1
├─ react-router@6.2.1
├─ react-sizeme@3.0.2
├─ react-syntax-highlighter@13.5.3
├─ react-textarea-autosize@8.3.3
├─ recursive-readdir@2.2.2
├─ refractor@3.5.0
├─ relateurl@0.2.7
├─ remark-external-links@8.0.0
├─ remark-slug@6.1.0
├─ renderkid@2.0.7
├─ require-directory@2.1.1
├─ rsvp@4.8.5
├─ sane@4.1.0
├─ serialize-javascript@5.0.1
├─ serve-favicon@2.5.0
├─ shell-quote@1.7.2
├─ sprintf-js@1.0.3
├─ ssri@8.0.1
├─ stackframe@1.2.0
├─ string.prototype.padend@3.1.3
├─ string.prototype.padstart@3.1.3
├─ strip-eof@1.0.0
├─ strip-indent@3.0.0
├─ symbol.prototype.description@1.0.5
├─ tar@6.1.11
├─ throttle-debounce@3.0.1
├─ tmpl@1.0.5
├─ toggle-selection@1.0.6
├─ tr46@0.0.3
├─ ts-pnp@1.2.0
├─ use-composed-ref@1.2.1
├─ use-isomorphic-layout-effect@1.1.1
├─ use-latest@1.2.0
├─ util.promisify@1.0.0
├─ utila@0.4.0
├─ uuid-browser@3.1.0
├─ uuid@3.4.0
├─ v8-to-istanbul@8.1.0
├─ walker@1.0.8
├─ warning@4.0.3
├─ webidl-conversions@3.0.1
├─ webpack-filter-warnings-plugin@1.2.1
├─ webpack-hot-middleware@2.25.1
├─ webpack-log@2.0.0
├─ whatwg-url@5.0.0
├─ which@1.3.1
├─ worker-rpc@0.1.1
├─ yargs-parser@20.2.9
└─ yargs@16.2.0
. ✓
• Preparing to install dependencies. ✓
warning package.json: No license field
warning webapp@0.0.0: No license field
[1/4] Resolving packages...
success Already up-to-date.
. ✓
To run your Storybook, type:
yarn storybook
For more information visit: https://storybook.js.org
🔎 checking 'cra5'
🔎 checking 'webpack5'
🔎 checking 'angular12'
🔎 checking 'mainjsFramework'
🔎 checking 'eslintPlugin'
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ We've detected you are not using our eslint-plugin. │
│ │
│ In order to have the best experience with Storybook and follow best practices, we advise you to install eslint-plugin-storybook. │
│ │
│ More info: https://github.com/storybookjs/eslint-plugin-storybook#readme │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
✔ Do you want to run the 'eslintPlugin' fix on your project? … yes
✅ Adding dependencies: eslint-plugin-storybook
warning package.json: No license field
warning webapp@0.0.0: No license field
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
warning " > babel-loader@8.2.3" has unmet peer dependency "webpack@>=2".
warning "@storybook/addon-essentials > @storybook/addon-docs > @storybook/core > @storybook/core-client@6.4.9" has unmet peer dependency "webpack@*".
warning " > @storybook/cli@6.4.9" has unmet peer dependency "jest@*".
[4/4] Building fresh packages...
success Saved lockfile.
warning webapp@0.0.0: No license field
success Saved 2 new dependencies.
info Direct dependencies
└─ eslint-plugin-storybook@0.5.5
info All dependencies
├─ eslint-plugin-storybook@0.5.5
└─ requireindex@1.2.0
❌ error in eslintPlugin:
⚠️ The plugin was successfuly installed but failed to configure.
Found an .eslintrc config file with an unsupported automigration format: yml.
Supported formats for automigration are: js, cjs.
Please refer to https://github.com/storybookjs/eslint-plugin-storybook#usage to finish setting up the plugin manually.
Done in 121.86s.
Vitest
インストール
$ yarn add -D vitest
yarn add v1.22.17
warning package.json: No license field
warning webapp@0.0.0: No license field
[1/4] Resolving packages...
[2/4] Fetching packages...
[3/4] Linking dependencies...
warning " > babel-loader@8.2.3" has unmet peer dependency "webpack@>=2".
warning "@storybook/addon-essentials > @storybook/addon-docs > @storybook/core > @storybook/core-client@6.4.9" has unmet peer dependency "webpack@*".
warning " > @storybook/cli@6.4.9" has unmet peer dependency "jest@*".
[4/4] Building fresh packages...
success Saved lockfile.
warning webapp@0.0.0: No license field
success Saved 13 new dependencies.
info Direct dependencies
└─ vitest@0.0.139
info All dependencies
├─ @types/chai-subset@1.3.3
├─ @types/chai@4.3.0
├─ assertion-error@1.1.0
├─ chai@4.3.4
├─ check-error@1.0.2
├─ deep-eql@3.0.1
├─ get-func-name@2.0.0
├─ local-pkg@0.4.1
├─ pathval@1.1.1
├─ tinypool@0.1.1
├─ tinyspy@0.2.8
├─ type-detect@4.0.8
└─ vitest@0.0.139
Done in 6.34s.
vite.config.tsの修正
vite.config
/// <reference types="vitest" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
test: {
global: true,
environment: 'jsdom'
}
})
react-test-rendererのインストール
yarn add -D react-test-renderer @types/react-test-rendere
型のインストール
npx typesync
» TypeSync v0.8.0
✔ 3 new typings added.
📦 webapp — package.json (3 new typings added, 0 unused typings removed)
├─ + @types/react-test-renderer
├─ + @types/jsdom
└─ + @types/babel__core
サンプル
Windi CSS
インストール
yarn add -D vite-plugin-windicss
yarn add v1.22.17
warning package.json: No license field
warning webapp@0.0.0: No license field
[1/4] Resolving packages...
warning @storybook/addon-essentials > @storybook/addon-controls > @storybook/core-common > webpack > watchpack > watchpack-chokidar2 > chokidar > fsevents@1.2.13: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.
warning Lockfile has incorrect entry for "acorn@^8.5.0". Ignoring it.
[2/4] Fetching packages...
[3/4] Linking dependencies...
warning " > babel-loader@8.2.3" has unmet peer dependency "webpack@>=2".
warning "@storybook/addon-essentials > @storybook/addon-docs > @storybook/core > @storybook/core-client@6.4.9" has unmet peer dependency "webpack@*".
warning " > @storybook/cli@6.4.9" has unmet peer dependency "jest@*".
[4/4] Building fresh packages...
success Saved lockfile.
warning webapp@0.0.0: No license field
success Saved 9 new dependencies.
info Direct dependencies
└─ vite-plugin-windicss@1.6.2
info All dependencies
├─ @antfu/utils@0.4.0
├─ @types/throttle-debounce@2.1.0
├─ @windicss/config@1.6.2
├─ @windicss/plugin-utils@1.6.2
├─ jiti@1.12.9
├─ kolorist@1.5.1
├─ magic-string@0.25.7
├─ sourcemap-codec@1.4.8
└─ vite-plugin-windicss@1.6.2
Done in 34.65s.
vite.config.tsの編集
vite.config.ts
/// <reference types="vitest" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import WindiCSS from 'vite-plugin-windicss' // 追加
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), WindiCSS() // 追加],
test: {
global: true,
environment: 'jsdom'
}
})