Skip to main content

One post tagged with "rule"

View All Tags

Code Convention Setup

· 6 min read
Liem Huynh
Backend Developer

1. Package Dependencies

First, install all necessary dependencies:

# Development dependencies
npm install --save-dev \
@typescript-eslint/eslint-plugin \
@typescript-eslint/parser \
eslint \
eslint-config-prettier \
eslint-plugin-prettier \
eslint-plugin-import \
eslint-plugin-simple-import-sort \
prettier \
husky \
lint-staged \
@commitlint/cli \
@commitlint/config-conventional \
commitizen \
cz-conventional-changelog

# Initialize husky
npx husky-init

2. Enhanced .eslintrc.js

module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: [
'@typescript-eslint/eslint-plugin',
'prettier',
'import',
'simple-import-sort',
],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:prettier/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js', 'dist/**/*', 'node_modules/**/*'],
rules: {
// Prettier
'prettier/prettier': [
'error',
{
endOfLine: 'auto',
},
],

// Import sorting
'simple-import-sort/imports': [
'error',
{
groups: [
// Side effect imports
['^\\u0000'],
// Node.js builtins
['^node:'],
// External packages
['^@?\\w'],
// NestJS specific imports
['^@nestjs/'],
// Internal packages
['^(@|@company|@app)(/.*|$)'],
// Parent imports
['^\\.\\.(?!/?$)', '^\\.\\./?$'],
// Other relative imports
['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'],
// Type imports
['^.+\\u0000$'],
],
},
],
'simple-import-sort/exports': 'error',

// Import rules
'import/first': 'error',
'import/newline-after-import': 'error',
'import/no-duplicates': 'error',
'import/no-unresolved': 'error',

// TypeScript specific rules
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-empty-object-type': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
caughtErrorsIgnorePattern: '^_',
},
],

// NestJS specific conventions
'@typescript-eslint/no-unsafe-assignment': 'warn',
'@typescript-eslint/no-unsafe-member-access': 'warn',
'@typescript-eslint/no-unsafe-call': 'warn',
'@typescript-eslint/no-unsafe-return': 'warn',
'@typescript-eslint/no-unsafe-argument': 'warn',

// Code quality
'@typescript-eslint/prefer-nullish-coalescing': 'error',
'@typescript-eslint/prefer-optional-chain': 'error',
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/await-thenable': 'error',
'@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/require-await': 'error',

// Naming conventions
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'default',
format: ['camelCase'],
},
{
selector: 'variable',
format: ['camelCase', 'UPPER_CASE'],
},
{
selector: 'parameter',
format: ['camelCase'],
leadingUnderscore: 'allow',
},
{
selector: 'memberLike',
format: ['camelCase'],
},
{
selector: 'typeLike',
format: ['PascalCase'],
},
{
selector: 'class',
format: ['PascalCase'],
},
{
selector: 'interface',
format: ['PascalCase'],
},
{
selector: 'enum',
format: ['PascalCase'],
},
{
selector: 'enumMember',
format: ['UPPER_CASE'],
},
],

// General best practices
'no-console': 'warn',
'no-debugger': 'error',
'no-duplicate-imports': 'error',
'no-unused-expressions': 'error',
'prefer-const': 'error',
'no-var': 'error',
},
settings: {
'import/resolver': {
typescript: {
alwaysTryTypes: true,
project: './tsconfig.json',
},
},
},
};

3. Enhanced .prettierrc

{
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"bracketSpacing": true,
"arrowParens": "avoid",
"endOfLine": "auto"
}

4. .prettierignore

# Dependencies
node_modules/

# Build outputs
dist/
build/

# Environment files
.env
.env.local
.env.*.local

# Logs
*.log

# Coverage
coverage/

# IDE
.vscode/
.idea/

# OS
.DS_Store
Thumbs.db

# Generated files
*.generated.ts

5. Commitlint Configuration

Create commitlint.config.js:

module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat', // New feature
'fix', // Bug fix
'docs', // Documentation
'style', // Code style changes
'refactor', // Code refactoring
'perf', // Performance improvements
'test', // Testing
'chore', // Maintenance
'ci', // CI/CD
'build', // Build system
'revert', // Revert changes
],
],
'type-case': [2, 'always', 'lower-case'],
'type-empty': [2, 'never'],
'scope-empty': [2, 'never'],
'scope-case': [2, 'always', 'lower-case'],
'subject-case': [2, 'always', 'lower-case'],
'subject-empty': [2, 'never'],
'subject-full-stop': [2, 'never', '.'],
'header-max-length': [2, 'always', 72],
'body-max-line-length': [2, 'always', 100],
},
};

6. Husky Configuration

Create .husky/pre-commit:

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

# Run lint-staged
npx lint-staged

Create .husky/commit-msg:

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

# Run commitlint
npx --no-install commitlint --edit "$1"

Create .husky/pre-push:

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

# Run tests before push
npm run test
npm run build

7. Lint-staged Configuration

Create .lintstagedrc.js:

export default {
'*.{ts,js}': [
'eslint --fix',
'prettier --write',
],
'*.{json,md,yml,yaml}': [
'prettier --write',
],
};

8. EditorConfig

Create .editorconfig:

root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

[*.{yml,yaml}]
indent_size = 2

[*.json]
indent_size = 2

9. VSCode Settings

Create .vscode/settings.json:

{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true,
"source.organizeImports": true
},
"eslint.validate": [
"javascript",
"typescript"
],
"typescript.preferences.importModuleSpecifier": "relative",
"typescript.suggest.autoImports": true,
"files.exclude": {
"node_modules": true,
"dist": true,
"coverage": true
}
}

Create .vscode/extensions.json:

{
"recommendations": [
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"ms-vscode.vscode-typescript-next",
"editorconfig.editorconfig"
]
}

10. Package.json Scripts

Add these scripts to your package.json:

{
"scripts": {
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"lint:check": "eslint \"{src,apps,libs,test}/**/*.ts\"",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\"",
"prepare": "husky install",
"commit": "git-cz",
"precommit": "lint-staged",
"type-check": "tsc --noEmit"
},
"config": {
"commitizen": {
"path": "cz-conventional-changelog"
}
}
}

11. TypeScript Configuration Enhancement

Update your tsconfig.json:

{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": true,
"noImplicitAny": true,
"strictBindCallApply": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"paths": {
"@/*": ["src/*"],
"@app/*": ["src/app/*"],
"@modules/*": ["src/modules/*"],
"@shared/*": ["src/shared/*"],
"@config/*": ["src/config/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "test"]
}

12. Git Hooks Setup Commands

Run these commands to set up everything:

# Install dependencies
npm install

# Initialize husky
npx husky-init

# Make hooks executable
chmod +x .husky/pre-commit
chmod +x .husky/commit-msg
chmod +x .husky/pre-push

# Test the setup
npm run lint
npm run format
npm run type-check

13. Usage Examples

Committing Code

# Stage your changes
git add .

# Use commitizen for standardized commits
npm run commit

# Or use git directly with proper format
git commit -m "feat(auth): add JWT token validation"

Commit Message Format

<type>(<scope>): <subject>

<body>

<footer>

Examples:

  • feat(auth): add JWT authentication middleware
  • fix(user): resolve user registration validation issue
  • docs(readme): update installation instructions
  • refactor(database): optimize query performance

Pre-commit Checks

The pre-commit hook will automatically:

  • Run ESLint and fix issues
  • Format code with Prettier
  • Check TypeScript compilation
  • Run tests (if configured)

14. Troubleshooting

Common Issues:

  1. Husky hooks not running: Ensure hooks are executable with chmod +x .husky/*
  2. ESLint errors: Run npm run lint to see and fix issues
  3. Prettier conflicts: Ensure ESLint and Prettier configs are compatible
  4. TypeScript errors: Run npm run type-check to see compilation issues

Bypassing Hooks (Emergency):

# Skip pre-commit hook
git commit --no-verify -m "emergency fix"

# Skip pre-push hook
git push --no-verify

This setup provides a complete, production-ready code convention system for your NestJS project with automated formatting, linting, and commit message validation.