Code Convention Setup
· 6 min read
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 middlewarefix(user): resolve user registration validation issuedocs(readme): update installation instructionsrefactor(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:
- Husky hooks not running: Ensure hooks are executable with
chmod +x .husky/* - ESLint errors: Run
npm run lintto see and fix issues - Prettier conflicts: Ensure ESLint and Prettier configs are compatible
- TypeScript errors: Run
npm run type-checkto 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.