[https://www.npmjs.com/package/@commitlint/config-conventional]
npm i --save-dev eslint-config-airbnb eslint-config-airbnb-typescript eslint-config-next @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest @commitlint/config-conventional @commitlint/cli husky
// init husky
npx husky init
git commit -m "commit message" --no-verify
{
"env": {
"browser": true,
"es2022": true,
"node": true
},
"settings": {
"react": {
"version": "detect"
}
},
"extends": [
"next/core-web-vitals",
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
"airbnb",
"airbnb-typescript"
],
"plugins": [
"react",
"import"
],
"parserOptions": {
"project": "./tsconfig.json",
"ecmaVersion": 2022,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"rules": {
// === React === /
"react/prop-types": "off",
"react/forbid-prop-types": "error",
"react/default-props-match-prop-types": "error",
"react/self-closing-comp": "error",
"react/no-unused-prop-types": "error",
"react/jsx-key": "error",
"react/no-unused-state": "error",
"react/state-in-constructor": "error",
"react/function-component-definition": "off",
"react/require-default-props": "off",
"react/no-array-index-key": "off",
"react/no-unescaped-entities": "off",
"react/no-unstable-nested-components": "off",
"react/no-danger": "off", // allow for next.js
// === React Hooks === //
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "error",
// === JSX === //
"jsx-a11y/img-redundant-alt": "error",
"jsx-a11y/anchor-is-valid": "error",
"jsx-a11y/alt-text": "error",
"jsx-a11y/mouse-events-have-key-events": "error",
"jsx-a11y/no-static-element-interactions": "off",
"jsx-a11y/no-noninteractive-element-interactions": "off",
"jsx-a11y/click-events-have-key-events": "off",
"jsx-a11y/interactive-supports-focus": "off",
// === React-JSX === //
"react/jsx-indent-props": "error",
"react/jsx-curly-newline": "error",
"react/jsx-equals-spacing": "error",
"react/jsx-indent": "warn",
"react/jsx-props-no-multi-spaces": "warn",
"react/jsx-curly-brace-presence": "warn",
"react/jsx-closing-bracket-location": "warn",
"react/jsx-tag-spacing": "warn",
"react/jsx-props-no-spreading": "off",
"react/react-in-jsx-scope": "off",
"react/jsx-closing-tag-location": "off",
"react/jsx-boolean-value": "off",
"react/jsx-wrap-multilines": "off",
"react/jsx-no-useless-fragment": "off",
"react/jsx-one-expression-per-line": "off",
"react/jsx-max-props-per-line": "off",
"react/jsx-curly-spacing": [
2,
"always",
{
"allowMultiline": true,
"spacing": {
"objectLiterals": "never"
}
}
],
// === TypeScript === //
"@typescript-eslint/return-await": "warn",
"@typescript-eslint/restrict-plus-operands": "error",
"@typescript-eslint/no-shadow": "error", // *** recommanded ***
"@typescript-eslint/object-curly-spacing": "warn",
"@typescript-eslint/no-misused-promises": [
"error",
{
"checksVoidReturn": false
}
],
"@typescript-eslint/no-redundant-type-constituents": "off",
"@typescript-eslint/no-unused-expressions": "warn",
"@typescript-eslint/no-unused-vars": "off", // use "warn" when code
"@typescript-eslint/indent": "warn",
"@typescript-eslint/space-before-blocks": "warn",
"@typescript-eslint/keyword-spacing": "warn",
"@typescript-eslint/comma-spacing": "warn",
"@typescript-eslint/space-infix-ops": "warn",
"@typescript-eslint/prefer-as-const": "warn",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/no-floating-promises": "off",
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/semi": "off",
"@typescript-eslint/comma-dangle": "off",
"@typescript-eslint/quotes": "off",
"@typescript-eslint/naming-convention": "off", // *** recommanded ***
"@typescript-eslint/no-var-requires": "off",
// === Import === //
"import/first": "error",
"import/no-mutable-exports": "error",
"import/no-useless-path-segments": "error",
"import/no-named-as-default": "error",
"import/no-duplicates": "error",
"import/newline-after-import": "error",
"import/no-extraneous-dependencies": "off",
"import/order": "off",
"import/no-cycle": "off",
"import/extensions": "off",
// === Others === //
"default-case": "off",
"function-paren-newline": "error",
"function-call-argument-newline": "error",
"spaced-comment": "error",
"operator-linebreak": "error",
"computed-property-spacing": "error",
"array-callback-return": "error",
"space-unary-ops": "error",
"object-shorthand": "error",
"key-spacing": "error",
"quote-props": "error",
"prefer-const": "error",
"prefer-destructuring": "error",
"prefer-template": "error",
"prefer-regex-literals": "error",
"prefer-promise-reject-errors": "error",
"guard-for-in": "error",
"one-var": "error",
"no-cond-assign": "error",
"no-sequences": "error",
"no-unneeded-ternary": "error",
"no-extra-boolean-cast": "error",
"no-lonely-if": "error",
"no-unsafe-optional-chaining": "error",
"no-mixed-operators": "error",
"no-confusing-arrow": "error",
"no-plusplus": "error",
"no-constant-condition": "error",
"no-floating-decimal": "error",
"eol-last": "error",
"array-bracket-spacing": "error", // *** recommanded ***
"space-in-parens": "error", // *** recommanded ***
"template-curly-spacing": [
"error",
"always"
], // *** recommanded ***
"no-tabs": [
"error",
{
"allowIndentationTabs": true
}
],
"dot-notation": "warn",
"implicit-arrow-linebreak": "warn",
"padded-blocks": "warn",
"no-multiple-empty-lines": "warn",
"no-multi-spaces": "warn",
"no-else-return": "off",
"indent": [
"warn",
2,
{ "SwitchCase": 1 }
],
"linebreak-style": [
"warn",
"unix"
],
"semi": [
"warn",
"never"
],
"no-param-reassign": [
"warn",
{
"props": true,
"ignorePropertyModificationsFor": [
"state"
]
}
],
"jsx-quotes": "off",
"max-len": "off",
"object-curly-newline": "off",
"arrow-body-style": "off",
"arrow-parens": "off",
"consistent-return": "off",
"eqeqeq": "off",
"no-console": ["error", { "allow": ["warn", "error"] }], // *** recommanded ***
"no-bitwise": "off",
"no-trailing-spaces": "off",
"no-useless-return": "off",
"no-underscore-dangle": "off",
"no-nested-ternary": "off",
"no-restricted-syntax": "off",
"no-unused-vars": "off", // "@typescript-eslint/no-unused-vars"
"no-return-await": "off" // "@typescript-eslint/return-await"
}
}
.eslintrc.json
tsconfig.json
*.config.js
.next
out
node_modules
{
"editor.codeActionsOnSave": [
"source.fixAll.eslint"
],
"eslint.format.enable": true, // on save fix format
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"editor.defaultFormatter": "dbaeumer.vscode-eslint", // Set default formatter
"editor.formatOnSave": true
}
{
"compilerOptions": {
"allowJs": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"declaration": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"jsx": "preserve",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"module": "esnext",
"moduleResolution": "node",
"noEmit": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"pretty": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "es2021",
"incremental": true
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
"**/*.js"
],
"exclude": [
"node_modules",
".next",
"out"
]
}
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
// TODO Add Scope Enum Here
// 'scope-enum': [2, 'always', ['yourscope', 'yourscope']],
'type-enum': [
2,
'always',
[
'feat',
'fix',
'docs',
'chore',
'style',
'refactor',
'ci',
'test',
'revert',
'perf',
'vercel',
],
],
},
};
// const { withSentryConfig } = require('@sentry/nextjs');
/** @type {import('next').NextConfig} **/
// Next Js Config
const nextConfig = {
reactStrictMode: true,
images: { unoptimized: true },
eslint: {
dirs: ['.']
}
// sentry: {
// hideSourceMaps: true
// }
}
module.exports = nextConfig
// module.exports = withSentryConfig(nextConfig)
{
"*.{js,jsx,ts,tsx}": [
"npm run lint",
"npm run check-types"
]
}
echo "Received commit message: $1"
NAME=$(git config user.name)
EMAIL=$(git config user.email)
if [ -z "$NAME" ]; then
echo "empty git config user.name"
exit 1
fi
if [ -z "$EMAIL" ]; then
echo "empty git config user.email"
exit 1
fi
git interpret-trailers --if-exists doNothing --trailer \
"Signed-off-by: $NAME <$EMAIL>" \
--in-place "$1"
npm exec --no -- commitlint --edit $1
# Get the branch name
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)
# This script runs linting, type checking, and builds before allowing a commit.
echo 'π οΈ Preparing to commit: Running linting, type checking, and build...'
# Run ESLint for linting
echo "π Running ESLint..."
npm run lint ||
(
echo 'β ESLint check failed. Please fix the linting issues before committing.'
exit 1
)
# Run TypeScript type checking
echo "π Running TypeScript type checking..."
npm run check-types ||
(
echo 'β TypeScript type checking failed. Please fix the type errors before committing.'
exit 1
)
# Run Build test only for main and staging branch
if [ "$BRANCH_NAME" = "main" ] || [ "$BRANCH_NAME" = "staging" ]; then
npm run build ||
(
echo 'β Build failed. Please ensure the build process completes successfully before committing.'
exit 1
)
fi
# All checks passed, allow the commit
echo 'β
All pre-commit checks passed. You can proceed with the commit.'
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint --dir .",
"lint:fix": "npm run lint -- --fix",
"prepare": "husky install",
"check-types": "tsc --pretty --noEmit",
"test-all": "npm run lint && npm run check-types && npm run build"
},
"devDependencies": {
"@commitlint/cli": "^18.6.1",
"@commitlint/config-conventional": "^18.6.3",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.18.0",
"eslint": "^8",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^18.0.0",
"eslint-config-next": "^15.1.7",
"husky": "^8.0.3",
"typescript": "^5"
}