【失敗】React + TypeScript + Storybook のプロジェクト作成
【結論】Storybook v6.1 は WebPack 5 に対応していないため、この手順では環境構築できません!
TypeError: Cannot read property 'createSnapshot' of undefined · Issue #13332 · storybookjs/storybook
Webpack 5 upgrade · Issue #9216 · storybookjs/storybook
#9216 が close したらもう一度やってみる。
WebPack のかわりに Rollup を使用したら、問題なく Storybook を起動できた。
コンポーネントをガッツリ作る必要が出てきたので、この際 Storybook にも入門してみる。
やること: React コンポーネントのプロジェクトを作成し、Storybook で動作確認する
環境など:
- Mac
- Node v15
- npm v7
- TypeScript v4
- React v17
- Storybook v6
これを参考に React コンポーネントのプロジェクトを作成する
プロジェクトを作成して npm, git の初期化。
ついでに .gitignore
と readme.md
の空ファイルを作成しておく。
mkdir react-ts-component-base
cd react-ts-component-base
npm init -y
git init
touch .gitignore
touch readme.md
.gitignore
は 他のプロジェクトで create-react-app
が生成した内容をコピペする。
細かい部分は後ほど調整。
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
React, TypeScript, WebPack のインストール
npm install --save-dev react react-dom webpack webpack-cli typescript ts-loader @types/react @types/react-dom
WebPack, TypeScript の設定ファイル作成
touch webpack.config.js
touch tsconfig.json
TypeScript をトランスパイルして dist
フォルダに書き出す
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.ts',
output: {
filename: 'index.js',
path: path.join(__dirname, 'dist'),
libraryTarget: 'commonjs2',
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modeules/,
use: [
'ts-loader',
],
},
],
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
},
}
TypeScript の設定。
src
フォルダを対象とする。"declaration": true
を忘れずつける。
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": true,
"esModuleInterop": true,
"module": "esnext",
"target": "es6",
"jsx": "react",
"declaration": true
},
"include": [
"src"
]
}
{
"name": "react-ts-component-base",
"version": "1.0.0",
"description": "",
- "main": "index.js",
+ "main": "dist/index.js",
"scripts": {
+ "build": "webpack",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.1",
"react": "^17.0.1",
"react-dom": "^17.0.1"
"ts-loader": "^8.0.17",
"typescript": "^4.1.5",
"webpack": "^5.23.0",
"webpack-cli": "^4.5.0"
- }
+ },
+ "peerDependencies": {
+ "react": "^17.0.1",
+ "react-dom": "^17.0.1"
+ }
}
コンポーネントを作成してみる
mkdir -p src/components
touch src/components/Text.tsx
touch src/index.ts
import React from 'react';
export interface TextProps {
value: string;
}
const Text: React.FC<TextProps> = ({ value }) => {
return <p>{value}</p>;
}
export default Text;
export { default as Text } from './components/Text';
export * from './components/Text';
トランスパイルしてみる
❯ npm run build
> react-ts-component-base@1.0.0 build
> webpack
asset index.js 80.9 KiB [emitted] (name: main)
asset components/Text.d.ts 139 bytes [emitted]
asset index.d.ts 88 bytes [emitted]
runtime modules 670 bytes 3 modules
cacheable modules 72.9 KiB
modules by path ./node_modules/ 72.7 KiB
./node_modules/react/index.js 190 bytes [built] [code generated]
./node_modules/react/cjs/react.development.js 70.5 KiB [built] [code generated]
./node_modules/object-assign/index.js 2.06 KiB [built] [code generated]
modules by path ./src/ 219 bytes
./src/index.ts 88 bytes [built] [code generated]
./src/components/Text.tsx 131 bytes [built] [code generated]
webpack 5.23.0 compiled successfully in 1749 ms
/dist
にファイルが生成されていることを確認する。
.d.ts
ファイルも存在することを忘れず確認。
.gitignore
に /dist
を追加しておく。
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+/dist
ESLint, Prettier のインストール
npm install --save-dev eslint eslint-config-react-app eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/eslint-plugin @typescript-eslint/parser prettier eslint-config-prettier
ESLint, Prettier の設定ファイルを作成
touch .eslintignore .prettierignore .prettierrc.js
.eslintignore
, .prettierignore
では dist
, node_modules
を除外する
(ESLint はデフォルトで node_modules
が除外されている?)
dist/
node_modules/
dist/
.prettierrc.js
の設定は僕の好みに合わせて。
Prettier デフォルトで問題無い人は不要。
module.exports = {
trailingComma: 'es5',
tabWidth: 4,
printWidth: 100,
singleQuote: true,
};
これを参考に .vscode/settings.json
を生成。
{
"typescript.tsdk": "node_modules/typescript/lib",
"editor.codeActionsOnSave": {
"source.organizeImports": true,
"source.fixAll.eslint": true
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}
src/index.ts
を開いて Command + S
してみる。
import
の順番が変わることが確認できる。
export * from './components/Text';
export { default as Text } from './components/Text';
src/components/Text.tsx
を開いて Command + S
する。
セミコロンの抜けが補完される。
import React from 'react';
export interface TextProps {
value: string;
}
const Text: React.FC<TextProps> = ({ value }) => {
return <p>{value}</p>;
-}
+};
export default Text;
CLI で ESLint、Prettier できるように npm script を設定しておく。
まとめてコマンド実行できるように npm-run-all
を使用する。
npm install --save-dev npm-run-all
あ、package.json の説明が抜けた。
先に package.json
に ESLint の設定を追加する。
これは ESLint, Prettier インストール直後に行う。
{
"name": "react-ts-component-base",
"version": "1.0.0",
"description": "",
"main": "dist/index.js",
"scripts": {
"build": "webpack",
+ "fix": "run-s fix:prettier fix:eslint",
+ "fix:eslint": "npm run lint:eslint -- --fix",
+ "fix:prettier": "npm run lint:prettier -- --write",
+ "lint": "run-p -l -c --aggregate-output lint:*",
+ "lint:eslint": "eslint .",
+ "lint:prettier": "prettier --check .",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.1",
"@typescript-eslint/eslint-plugin": "^4.15.1",
"@typescript-eslint/parser": "^4.15.1",
"eslint": "^7.20.0",
"eslint-config-prettier": "^7.2.0",
"eslint-config-react-app": "^6.0.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^4.2.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.2.1",
"ts-loader": "^8.0.17",
"typescript": "^4.1.5",
"webpack": "^5.23.0",
"webpack-cli": "^4.5.0"
},
"peerDependencies": {
"react": "^17.0.1",
"react-dom": "^17.0.1"
- }
+ },
+ "eslintConfig": {
+ "parser": "@typescript-eslint/parser",
+ "extends": [
+ "react-app",
+ "plugin:@typescript-eslint/eslint-recommended",
+ "plugin:@typescript-eslint/recommended",
+ "plugin:import/errors",
+ "plugin:import/warnings",
+ "plugin:import/typescript",
+ "prettier"
+ ]
+ }
}
lint を実行してみる
❯ npm run lint
> react-ts-component-base@1.0.0 lint
> run-p -l -c --aggregate-output lint:*
~~~中略~~~
[lint:eslint ] /Users/kazunorikimura/repository/react-ts-component-base/webpack.config.js
[lint:eslint ] 1:14 error Require statement not part of import statement @typescript-eslint/no-var-requires
[lint:eslint ]
[lint:eslint ] ✖ 1 problem (1 error, 0 warnings)
[lint:eslint ]
ERROR: "lint:eslint" exited with 1.
npm ERR! code 1
npm ERR! path /Users/kazunorikimura/repository/react-ts-component-base
npm ERR! command failed
npm ERR! command sh -c run-p -l -c --aggregate-output lint:*
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/kazunorikimura/.npm/_logs/2021-02-19T15_57_44_639Z-debug.log
webpack.config.js
が引っかかった。
const path = require('path');
が気に入らない模様。
webpack.config.js
はビルド設定なのであまりこだわる必要はない。ここはサクっと ESLint の指摘を無視する。
+/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.ts',
output: {
filename: 'index.js',
path: path.join(__dirname, 'dist'),
libraryTarget: 'commonjs2',
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modeules/,
use: ['ts-loader'],
},
],
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
},
};
再度 lint を実行。
❯ npm run lint
> react-ts-component-base@1.0.0 lint
> run-p -l -c --aggregate-output lint:*
[lint:prettier]
[lint:prettier] > react-ts-component-base@1.0.0 lint:prettier
[lint:prettier] > prettier --check .
[lint:prettier]
[lint:prettier] Checking formatting...
[lint:prettier] All matched files use Prettier code style!
[lint:eslint ]
[lint:eslint ] > react-ts-component-base@1.0.0 lint:eslint
[lint:eslint ] > eslint .
[lint:eslint ]
エラーがなくなった。
2021-02-20 現在、npm v7 で npx sb init
すると依存関係のチェックでエラーとなり、インストールが中断される。
npx --legacy-peer-deps sb init
とするとインストールが正常終了した。
❯ npx --legacy-peer-deps sb init
sb init - the simplest way to add a Storybook to your project.
• Detecting project type. ✓
• Adding Storybook support to your "React" library⸨░░░░░░░░░░░░░░░░░░⸩ ⠹ reify:npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
npm WARN deprecated fsevents@1.2.13: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.
npm WARN deprecated chokidar@2.1.8: Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.
added 1320 packages, removed 7 packages, and audited 1718 packages in 35s
180 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
. ✓
• Preparing to install dependencies. ✓
• Installing dependencies⸨░░░░░░░░░░░░░░░░░░⸩ ⠹ reify: timing arborist:ctor Com
up to date, audited 1718 packages in 2s
180 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
. ✓
To run your Storybook, type:
npm run storybook
For more information visit: https://storybook.js.org
試しに src/stories/Button.stories.tsx
を開いてみると、from '@storybook/react/types-6-0';
が解決できない、とエラーになっていた。
VSCode に表示されているエラーメッセージに従って、tsconfig.json
の内容を修正する。
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": true,
"esModuleInterop": true,
"module": "esnext",
"target": "es6",
"jsx": "react",
- "declaration": true
+ "declaration": true,
+ "moduleResolution": "Node"
},
"include": ["src"]
}
webpack5 に対応していない...だと...
rollup でやればいいのか。