初めに
React では eslint を使うのが一般的になってきています。その際に設定をいちいち考えるのなかなかめんどくさいので、忘備録としてここに残しておきます。
typescript・prettie 対応です。
eslintrc.js
.eslintrc.js
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'plugin:react/recommended',
'airbnb',
'airbnb/hooks',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
'plugin:cypress/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'prettier',
],
globals: {
cy: 'readonly',
Cypress: 'readonly',
},
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 12,
project: './tsconfig.eslint.json',
sourceType: 'module',
tsconfigRootDir: __dirname,
},
plugins: [
'@typescript-eslint',
'import',
'jsx-a11y',
'prefer-arrow',
'react',
'react-hooks',
'cypress',
],
root: true,
rules: {
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': ['error'],
'lines-between-class-members': [
'error',
'always',
{
exceptAfterSingleLine: true,
},
],
'no-void': [
'error',
{
allowAsStatement: true,
},
],
'padding-line-between-statements': [
'error',
{
blankLine: 'always',
prev: '*',
next: 'return',
},
],
'@typescript-eslint/no-unused-vars': [
'error',
{
vars: 'all',
args: 'after-used',
argsIgnorePattern: '_',
ignoreRestSiblings: false,
varsIgnorePattern: '_',
},
],
'import/extensions': [
'error',
'ignorePackages',
{
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
},
],
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: [
'**/setupTests.ts',
'.storybook/**',
'stories/**',
'**/*/*.story.*',
'**/*/*.stories.*',
'**/__specs__/**',
'**/*/*.spec.*',
'**/__tests__/**',
'**/*/*.test.*',
],
},
],
'prefer-arrow/prefer-arrow-functions': [
'error',
{
disallowPrototype: true,
singleReturnOnly: false,
classPropertiesAllowed: false,
},
],
'react/jsx-filename-extension': [
'error',
{
extensions: ['.jsx', '.tsx'],
},
],
'react/jsx-props-no-spreading': [
'error',
{
html: 'enforce',
custom: 'enforce',
explicitSpread: 'ignore',
},
],
'react/react-in-jsx-scope': 'off',
'react-hooks/rules-of-hooks': 'error',
'react/require-default-props': [0],
'react-hooks/exhaustive-deps': [
'warn',
{
additionalHooks: 'useRecoilCallback',
},
],
},
overrides: [
{
files: ['*.tsx'],
rules: {
'react/prop-types': 'off',
},
},
],
settings: {
'import/resolver': {
node: {
paths: ['src/'],
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
},
};
.eslintignore
.eslintignore
build/
public/
**/coverage/
**/node_modules/
**/*.min.js
*.config.js
.*lintrc.js
.eslintrc.js
cypress/plugins/
cypress/integration/
cypress/support/
.stylelintrc.js
.styleintrc.js
module.exports = {
extends: ['stylelint-config-standard', 'stylelint-config-recess-order'],
plugins: ['stylelint-order'],
ignoreFiles: ['**/node_modules/**'],
rules: {
'string-quotes': 'single',
},
};
.prettierrc
.prettierrc
singleQuote: true
trailingComma: "all"
tsconfig.eslint.json
tsconfig.eslint.json
{
"extends": "./tsconfig.json",
"include": [
"src/**/*.js",
"src/**/*.jsx",
"src/**/*.ts",
"src/**/*.tsx",
],
"exclude": [
"node_modules"
]
}
tsconfig.json
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext",
"es5"
],
"types": ["jest"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"baseUrl": "src",
"downlevelIteration": true
},
"include": [
"src",
]
}
package.json
package.json
{
"name": "coffee-oma",
"version": "0.1.0",
"private": true,
"dependencies": {
"@hookform/error-message": "^2.0.0",
"@storybook/react": "^6.2.9",
"@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^11.2.5",
"@testing-library/user-event": "^13.0.16",
"@types/jest": "^26.0.22",
"@types/node": "^14.14.37",
"@types/react": "^17.0.3",
"@types/react-dom": "^17.0.3",
"@types/react-router": "^5.1.18",
"@types/react-router-dom": "^5.3.3",
"@types/recoil": "^0.0.1",
"axios": "^0.21.1",
"browserslist": "^4.18.0",
"dayjs": "^1.10.4",
"eslint-config-react-app": "^6.0.0",
"framer-motion": "^4.1.17",
"history": "5",
"react": "^17.0.2",
"react-cookie": "^4.1.1",
"react-docgen-typescript-webpack-plugin": "^1.1.0",
"react-dom": "^17.0.2",
"react-helmet": "^6.1.0",
"react-hook-form": "^7.4.0",
"react-loader-spinner": "^4.0.0",
"react-query": "^3.32.1",
"react-rating": "^2.0.5",
"react-router": "6.2.1",
"react-router-dom": "6.2.1",
"react-scripts": "4.0.3",
"react-simple-star-rating": "^4.0.0",
"recoil": "^0.2.0",
"semantic-ui-css": "^2.4.1",
"semantic-ui-react": "^2.0.3",
"styled-components": "^5.2.3",
"typescript": "^4.2.3",
"use-file-upload": "^1.0.9",
"use-state-if-mounted": "^1.0.4",
"web-vitals": "^1.1.1"
},
"scripts": {
"start": "REACT_APP_NODE_ENV=development react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"heroku:start": "react-scripts build && react-scripts start",
"fix": "npm run -s format && npm run -s lint:fix",
"format": "prettier --write --loglevel=warn 'src/**/*.{js,jsx,ts,tsx,gql,graphql,json}'",
"lint": "npm run -s lint:style; npm run -s lint:es",
"lint:fix": "npm run -s lint:style:fix && npm run -s lint:es:fix",
"lint:es": "eslint 'src/**/*.{js,jsx,ts,tsx}'",
"lint:es:fix": "eslint --fix 'src/**/*.{js,jsx,ts,tsx}'",
"lint:conflict": "eslint-config-prettier 'src/**/*.{js,jsx,ts,tsx}'",
"lint:style": "stylelint 'src/**/*.{css,less,sass,scss}'",
"lint:style:fix": "stylelint --fix 'src/**/*.{css,less,sass,scss}'",
"preinstall": "typesync || :",
"storybool": "start-storybook -p 6006",
"build:storybook": "build-storybook -o build/stroybook",
"storybook": "start-storybook -p 6006 -s public",
"build-storybook": "build-storybook -s public",
"cypress": "cypress open",
"cypress:run": "cypress run",
"start:ci": "CHOKIDAR_USEPOLLING=true react-scripts start",
"cy:ci": "start-server-and-test start http://localhost:3000 cypress:run"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
],
"overrides": [
{
"files": [
"**/*.stories.*"
],
"rules": {
"import/no-anonymous-default-export": "off"
}
}
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"jest": {
"resetMocks": false
},
"devDependencies": {
"@storybook/addon-a11y": "^6.2.9",
"@storybook/addon-actions": "^6.2.9",
"@storybook/addon-console": "^1.2.3",
"@storybook/addon-controls": "^6.2.9",
"@storybook/addon-docs": "^6.2.9",
"@storybook/addon-essentials": "^6.2.9",
"@storybook/addon-info": "^5.3.21",
"@storybook/addon-knobs": "^6.2.9",
"@storybook/addon-links": "^6.2.9",
"@storybook/addon-storyshots": "^6.2.9",
"@storybook/addon-storyshots-puppeteer": "^6.2.9",
"@storybook/addon-storysource": "^6.2.9",
"@storybook/addon-viewport": "^6.2.9",
"@storybook/node-logger": "^6.2.9",
"@storybook/preset-create-react-app": "^3.1.7",
"@types/babel-plugin-macros": "^2.8.4",
"@types/prettier": "^2.2.3",
"@types/react-helmet": "^6.1.1",
"@types/react-test-renderer": "^17.0.1",
"@types/storybook-react-router": "^1.0.1",
"@types/storybook__addon-actions": "^5.2.1",
"@types/storybook__addon-info": "^5.2.3",
"@types/storybook__addon-knobs": "^5.2.1",
"@types/storybook__react": "^5.2.1",
"@types/styled-components": "^5.1.9",
"@types/stylelint": "^9.10.1",
"@types/testing-library__jest-dom": "^5.9.5",
"@types/testing-library__user-event": "^4.2.0",
"@typescript-eslint/eslint-plugin": "^4.20.0",
"@typescript-eslint/parser": "^4.20.0",
"axios-mock-adapter": "^1.20.0",
"babel-loader": "^8.2.2",
"babel-plugin-macros": "^3.1.0",
"cypress": "^8.7.0",
"eslint-config-airbnb": "18.2.1",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-prefer-arrow": "^1.2.3",
"eslint-plugin-react": "^7.23.1",
"eslint-plugin-react-hooks": "4.2.0",
"jest": "^27.0.1",
"jest-localstorage-mock": "^2.4.13",
"jest-watch-typeahead": "^0.6.4",
"prettier": "^2.2.1",
"puppeteer": "^10.0.0",
"react-docgen-typescript-loader": "^3.7.2",
"react-test-renderer": "^17.0.2",
"require-context.macro": "^1.2.2",
"start-server-and-test": "^1.14.0",
"storybook-addon-mock": "^0.1.0",
"storybook-axios": "^1.0.1",
"storybook-react-router": "^1.0.8",
"stylelint": "^13.12.0",
"stylelint-config-recess-order": "^2.3.0",
"stylelint-config-standard": "^21.0.0",
"stylelint-order": "^4.1.0",
"typescript-styled-plugin": "^0.15.0",
"typesync": "^0.8.0"
}
}
参考
下記の書籍を読んで学べば、React の事は完全に理解できると思います