はじめに
Next.jsを使うことになったので、インストールして以下を設定します
今回は最低限な設定で構築したいと思います
- 静的サイトホスティング用の設定
- TypeScript
- ESLint + Prettier
- CSS
- Storybook
- 単体テスト
Next.js をインストール
npx create-next-app@latest --typescript [プロジェクト名] --use-npm
デフォルトが Yarn みたいなので、use-npm オプションを付けている
以下のコマンドで画面が見れたらオッケー
npm run dev
コマンドを設定
デフォルトは以下
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
}
}
今回作るアプリは S3 に静的サイトホスティングする
よって、以下に変更
npm i -D serve
{
"scripts": {
"dev": "next dev",
"export": "next build && next export",
"serve": "next build && next export && serve ./out"
}
}
dev は HMR をサポートしているので、普段の開発はこれでやる
export は CI で S3 にデプロイする時用
serve は静的ビルドの結果をローカルでサーバーを立てて確認する用
もし、アプリをサーバー上で動かす場合は"start": "next build && next start"
を追加する
ESLint + Prettier を設定
ESLint のルールは Airbnb+TypeScript にする
Lint 系は後でルール変えると修正が大変なので、最初からキッチリ設定しておく
npm i -D eslint eslint-loader eslint-config-airbnb eslint-plugin-react-hooks eslint-plugin-jsx-a11y eslint-plugin-react eslint-plugin-import @typescript-eslint/parser @typescript-eslint/eslint-plugin prettier eslint-config-prettier eslint-plugin-prettier
プロジェクトルートに.eslintrc.js
を作成
module.exports = {
env: { browser: true },
extends: [
'airbnb',
'airbnb/hooks',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
'prettier/@typescript-eslint',
'prettier/react'
],
parser: '@typescript-eslint/parser',
parserOptions: { ecmaFeatures: { jsx: true } },
settings: { react: { version: 'detect' } },
rules: {
'prettier/prettier': [
'error',
{
arrowParens: 'avoid',
semi: false,
singleQuote: true,
trailingComma: 'none'
}
]
}
}
ここは好みだが、自分は常にリンターのエラーを修正しながら開発したいので、eslint-loader を設定する
プロジェクトルートにnext.config.js
を作成し、以下を設定
module.exports = {
webpack: config => {
config.module.rules.push({
test: /\.tsx?$/,
loader: 'eslint-loader',
exclude: /node_modules/
})
return config
}
}
ちなみにnext.config.ts
はおそらくサポートしていないっぽい
https://github.com/vercel/next.js/issues/5318
CSS 周り
Next には CSS in JS の styled-jsx がビルトインされているので、これを使うのであれば環境設定は不要
使い方は以下のように<style jsx>
で囲む
import React from 'react'
const Home: React.FC = () => (
<>
<div>Hello Next.js!</div>
<style jsx>{`
div {
color: blue;
}
`}</style>
</>
)
export default Home
stylelint が使えたら良かったが、まだ検証段階だった
https://github.com/giuseppeg/styled-jsx-plugin-stylelint
Storybook
npm i -D @storybook/react
プロジェクトルートに.storybook/main.js
を作成し、以下を設定
const path = require('path')
module.exports = {
stories: ['../stories/**/*.stories.tsx'],
webpackFinal: async config => {
config.resolve.alias['@'] = path.join(__dirname, '../')
return config
}
}
stories/pages/index.stories.tsx
を作成
import App from '@/pages/index'
export default {
title: 'pages'
}
export const Home = (): JSX.Element => <App />
以下のコマンドを追加
{
"scripts": {
"sb": "start-storybook -p 9009"
}
}
npm run sb
で動作すれば成功
Storybook が TypeScript をサポートするようになったため、以前のように Storybook 用に webpack.config を用意する必要がなくなった
https://storybook.js.org/docs/react/configure/typescript
また、以下のコマンドで上記を書かなくても、全部セットアップしてくれる(不要なものも色々入るが)
公式の最新の構成にできるので、いったんこちらを実行するのがいいかも
npx storybook init
単体テスト
テストフレームワークは軽く調べた感じ、以下で良さそう
1 年前調べた時と変わってなかった
- テストランナー
- Jest
- テストフレームワーク
- @testing-library/react
- @testing-library/react-hooks
npm i -D jest @types/jest ts-jest @testing-library/react
以下のコマンドを追加
{
"scripts": {
"test": "jest --watchAll"
}
}
プロジェクトルートにjest.config.js
を作成
module.exports = {
testMatch: ['<rootDir>/test/unit/specs/**/*.spec.tsx'],
preset: 'ts-jest',
moduleNameMapper: { '^@/(.*)$': '<rootDir>/$1' },
collectCoverage: true,
collectCoverageFrom: ['<rootDir>/pages/**/*.tsx'],
coverageDirectory: '<rootDir>/test/unit/coverage',
coverageReporters: ['html', 'text-summary'],
verbose: true,
globals: { 'ts-jest': { tsconfig: { jsx: 'react' } } }
}
tsconfig.json
のjsx
オプションがpreserve
で ts-jest 使うとエラーになる
https://github.com/kulshekhar/ts-jest/issues/63
なので、jest のglobals
オプションでjsx
をreact
に書き換えている
test/unit/specs/pages/index.spec.tsx
を作成
(テスト内容は適当です)
import { cleanup, render, RenderResult } from '@testing-library/react'
import React from 'react'
import Home from '@/pages/index'
let wrapper: RenderResult
beforeEach(() => (wrapper = render(<Home />)))
afterEach(cleanup)
test('Get by text', () => {
expect(wrapper.getByText('Hello Next.js!'))
})
ts-jest やめて、babel-jest にした
ts-jest を使っていると、テスト実行時に以下の警告が出る
Warning: Received `true` for a non-boolean attribute `jsx`.
https://github.com/vercel/styled-jsx#rendering-in-tests
この辺見てもよく分からなかったので、babel-jest を使うことにした
npm i -D babel-jest
npm remove ts-jest
プロジェクトルートに.babelrc
を作成
{
"presets": ["next/babel"]
}
jest.config.js
を以下に変更
module.exports = {
testMatch: ['<rootDir>/test/unit/specs/**/*.spec.tsx'],
transform: { '\\.tsx$': 'babel-jest' },
moduleNameMapper: { '^@/(.*)$': '<rootDir>/$1' },
collectCoverage: true,
collectCoverageFrom: ['<rootDir>/pages/**/*.tsx'],
coverageDirectory: '<rootDir>/test/unit/coverage',
coverageReporters: ['html', 'text-summary'],
verbose: true
}