- Pengenalan Code Quality Tools
- ESLint: Dasar dan Instalasi
- Konfigurasi ESLint (Flat Config)
- Memahami Rules ESLint
- Plugin Populer ESLint
- Prettier: Dasar dan Instalasi
- Konfigurasi Prettier
- Mengintegrasikan ESLint + Prettier
- Integrasi dengan VS Code
- Pre-commit Hooks dengan Husky
- CI/CD Integration
- Best Practices dan Cheat Sheet
- Quiz Pemahaman
1. Pengenalan Code Quality Tools
Saat bekerja dalam tim pengembangan software, menjaga kualitas dan konsistensi kode adalah hal yang sangat penting. Tanpa tools yang tepat, setiap developer bisa memiliki gaya penulisan yang berbeda β ada yang menggunakan tab, ada yang spasi; ada yang pakai titik koma, ada yang tidak.
Dua tools yang menjadi standar industri untuk menjaga kualitas kode JavaScript/TypeScript adalah ESLint dan Prettier:
ESLint vs Prettier: Apa Bedanya?
| Aspek | ESLint | Prettier |
|---|---|---|
| Fungsi Utama | Menemukan dan memperbaiki masalah kode (bugs, patterns, best practices) | Memformat tampilan kode (indentasi, spasi, line breaks) |
| Tipe Masalah | Logic errors, unused variables, no-undef, no-console, security issues | Code style: indentation, trailing commas, semicolons, quotes, line width |
| Customisasi | Sangat fleksibel β ratusan rules yang bisa diatur | Opsi terbatas β sengaja dibuat opinionated |
| Auto-fix | Bisa untuk sebagian rules | Bisa untuk semua formatting |
| Analogi | Seperti pemeriksa tata bahasa (grammar checker) | Seperti formatter dokumen (auto-format) |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β CODE QUALITY WORKFLOW β β β β Developer Menulis Kode β β β β β βΌ β β ββββββββββββββββββββββ β β β VS Code Extension β β Real-time feedback β β β (ESLint + Prettier)β β Format on save β β ββββββββββ¬ββββββββββββ β β βΌ β β ββββββββββββββββββββββ β β β Pre-commit Hook β β Husky + lint-staged β β β (git commit) β β Auto-fix sebelum commit β β ββββββββββ¬ββββββββββββ β β βΌ β β ββββββββββββββββββββββ β β β CI/CD Pipeline β β GitHub Actions / GitLab CI β β β (Push to remote) β β Block merge jika gagal β β ββββββββββ¬ββββββββββββ β β βΌ β β ββββββββββββββββββββββ β β β Code Review β β PR quality sudah terjaga β β β (Pull Request) β β Reviewer fokus ke logic β β ββββββββββββββββββββββ β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
2. ESLint: Dasar dan Instalasi
ESLint adalah static analysis tool yang secara otomatis mendeteksi masalah dalam kode JavaScript dan TypeScript. ESLint pertama kali dibuat oleh Nicholas C. Zakas pada tahun 2013 dan sekarang menjadi tool linting yang paling banyak digunakan di ekosistem JavaScript.
Instalasi
# Inisialisasi ESLint di proyek baru npm init @eslint@latest # Wizard akan menanyakan: # β How would you like to use ESLint? β To check syntax and find problems # β What type of modules does your project use? β ES Modules # β Which framework does your project use? β React / None # β Does your project use TypeScript? β Yes / No # β Where does your code run? β Browser / Node # β What format do you want your config file to be in? β ESM (eslint.config.mjs) # β Would you like to install them now? β Yes # Atau instal manual: npm install -D eslint @eslint/js # Untuk TypeScript: npm install -D @typescript-eslint/parser @typescript-eslint/eslint-plugin # Untuk React: npm install -D eslint-plugin-react eslint-plugin-react-hooks
Menjalankan ESLint
# Lint satu file
npx eslint src/app.js
# Lint seluruh direktori
npx eslint src/
# Lint dengan pattern
npx eslint "src/**/*.{js,ts,jsx,tsx}"
# Auto-fix masalah yang bisa diperbaiki
npx eslint --fix src/
# Cek tanpa memperbaiki (untuk CI)
npx eslint src/ --no-fix
# Output format untuk CI tools
npx eslint src/ --format json
npx eslint src/ --format junit
3. Konfigurasi ESLint (Flat Config)
ESLint v9+ menggunakan format flat config (eslint.config.mjs) yang menggantikan format lama .eslintrc. Flat config menggunakan JavaScript biasa sehingga lebih fleksibel dan mudah dipahami.
// eslint.config.mjs β Konfigurasi ESLint modern (v9+)
import js from '@eslint/js';
import tsPlugin from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import reactPlugin from 'eslint-plugin-react';
import reactHooksPlugin from 'eslint-plugin-react-hooks';
export default [
// === Global ignores ===
{
ignores: ['dist/**', 'build/**', 'node_modules/**', '*.min.js', 'coverage/**']
},
// === Base config untuk semua file ===
js.configs.recommended,
// === TypeScript config ===
{
files: ['**/*.{ts,tsx}'],
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: { jsx: true },
project: './tsconfig.json'
}
},
plugins: {
'@typescript-eslint': tsPlugin
},
rules: {
// TypeScript specific rules
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }],
'@typescript-eslint/no-non-null-assertion': 'warn',
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/await-thenable': 'error'
}
},
// === React config ===
{
files: ['**/*.{jsx,tsx}'],
plugins: {
'react': reactPlugin,
'react-hooks': reactHooksPlugin
},
settings: {
react: { version: 'detect' }
},
rules: {
'react/react-in-jsx-scope': 'off', // React 17+ tidak perlu import React
'react/prop-types': 'off', // Gunakan TypeScript
'react/jsx-uses-react': 'off',
'react/jsx-uses-vars': 'error',
'react/self-closing-comp': 'error',
'react/jsx-no-duplicate-props': 'error',
'react/jsx-key': 'error',
'react/no-array-index-key': 'warn',
'react/no-danger': 'warn',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn'
}
},
// === Custom rules untuk proyek ===
{
rules: {
// Best practices
'no-console': ['warn', { allow: ['warn', 'error'] }],
'no-debugger': 'error',
'no-alert': 'error',
'no-var': 'error',
'prefer-const': 'error',
'prefer-template': 'error',
'no-duplicate-imports': 'error',
'eqeqeq': ['error', 'always'],
'curly': ['error', 'multi-line'],
// Style (yang tidak ditangani Prettier)
'no-multiple-empty-lines': ['error', { max: 1 }],
'no-trailing-spaces': 'error',
'padding-line-between-statements': [
'error',
{ blankLine: 'always', prev: '*', next: 'return' }
]
}
}
];
4. Memahami Rules ESLint
Setiap rule ESLint memiliki tiga level severity:
| Level | Angka | Arti | Dampak di CI |
|---|---|---|---|
"off" / 0 | 0 | Rule dimatikan | Tidak ada |
"warn" / 1 | 1 | Peringatan (warning) | CI tetap pass, tapi ada warning |
"error" / 2 | 2 | Error β harus diperbaiki | CI gagal |
Contoh Rule dengan Opsi
// eslint.config.mjs β Contoh rules dengan opsi detail
{
rules: {
// === Error Prevention ===
'no-unused-vars': 'off', // Gunakan @typescript-eslint version
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_', // _arg diabaikan
varsIgnorePattern: '^_', // _var diabaikan
caughtErrorsIgnorePattern: '^_', // catch (_err)
destructuredArrayIgnorePattern: '^_'
}
],
// === Best Practices ===
'no-var': 'error', // Gunakan let/const
'prefer-const': 'error', // Gunakan const jika tidak berubah
'prefer-template': 'warn', // Gunakan template literal
'no-implicit-coercion': 'error', // Hindari !!, +, '' untuk konversi
'no-return-await': 'error', // Hindari return await yang tidak perlu
'require-await': 'warn', // async function harus punya await
'no-throw-literal': 'error', // throw new Error(), bukan throw "msg"
// === Security ===
'no-eval': 'error', // Larang eval()
'no-implied-eval': 'error', // Larang setTimeout("code")
'no-new-func': 'error', // Larang new Function()
// === Code Consistency ===
'arrow-body-style': ['error', 'as-needed'],
'object-shorthand': ['error', 'always'],
'prefer-destructuring': ['warn', {
array: false,
object: true
}],
'no-nested-ternary': 'warn', // Hindari ternary bersarang
'max-depth': ['warn', 3], // Max nesting depth
'max-lines-per-function': ['warn', {
max: 100,
skipBlankLines: true,
skipComments: true
}]
}
}
Contoh Kode yang Melanggar Rules
// β Kode yang melanggar rules
var name = "Budi"; // Error: no-var
const unused = 10; // Error: no-unused-vars
if (value == true) { } // Error: eqeqeq (harus ===)
console.log("debug"); // Warn: no-console
const result = "" + value; // Error: no-implicit-coercion
const name2 = "Hello " + name; // Warn: prefer-template
// β
Kode yang sudah diperbaiki
const name = "Budi"; // const, bukan var
// (hapus unused variable)
if (value === true) { } // === bukan ==
console.error("error message"); // console.error diizinkan
const result = String(value); // eksplisit konversi
const greeting = `Hello ${name}`; // template literal
5. Plugin Populer ESLint
| Plugin | Kegunaan | Instalasi |
|---|---|---|
eslint-plugin-react | Rules khusus untuk React | npm i -D eslint-plugin-react |
eslint-plugin-react-hooks | Rules untuk React Hooks | npm i -D eslint-plugin-react-hooks |
eslint-plugin-vue | Rules untuk Vue.js | npm i -D eslint-plugin-vue |
eslint-plugin-import | Rules untuk import/export statements | npm i -D eslint-plugin-import |
eslint-plugin-tailwindcss | Rules untuk Tailwind CSS class | npm i -D eslint-plugin-tailwindcss |
eslint-plugin-security | Deteksi security issues | npm i -D eslint-plugin-security |
eslint-plugin-sonarjs | Deteksi code smells dan bugs | npm i -D eslint-plugin-sonarjs |
eslint-plugin-jest | Rules untuk Jest testing | npm i -D eslint-plugin-jest |
eslint-plugin-perf | Deteksi pola kode yang lambat | npm i -D eslint-plugin-perf |
@eslint/js | Rekomendasi rules dasar ESLint | npm i -D @eslint/js |
6. Prettier: Dasar dan Instalasi
Prettier adalah opinionated code formatter yang mendukung banyak bahasa: JavaScript, TypeScript, HTML, CSS, JSON, Markdown, YAML, GraphQL, dan lainnya. Prettier memastikan semua kode di proyek memiliki format yang konsisten β tidak peduli siapa yang menulisnya.
Instalasi
# Instal Prettier
npm install -D prettier
# Buat file konfigurasi
echo {} > .prettierrc.json
# Buat file ignore
echo "node_modules
dist
build
coverage" > .prettignore
Menjalankan Prettier
# Format satu file
npx prettier --write src/app.ts
# Format seluruh direktori
npx prettier --write "src/**/*.{js,ts,jsx,tsx,json,css,md}"
# Cek format tanpa memperbaiki (untuk CI)
npx prettier --check "src/**/*.{js,ts,jsx,tsx}"
# Format semua file di proyek
npx prettier --write .
# Format dan lihat diff
npx prettier --write --list-different src/
7. Konfigurasi Prettier
// .prettierrc.json β Konfigurasi Prettier
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"useTabs": false,
"trailingComma": "all",
"printWidth": 100,
"bracketSpacing": true,
"bracketSameLine": false,
"arrowParens": "always",
"endOfLine": "lf",
"quoteProps": "as-needed",
"jsxSingleQuote": false,
"htmlWhitespaceSensitivity": "css",
"proseWrap": "preserve",
"embeddedLanguageFormatting": "auto"
}
Penjelasan Opsi Penting
| Opsi | Default | Penjelasan |
|---|---|---|
semi | true | Tambahkan titik koma di akhir statement |
singleQuote | false | Gunakan single quote alih-alih double quote |
tabWidth | 2 | Jumlah spasi per indentasi |
trailingComma | "all" | Tambahkan koma di akhir array/object/function params |
printWidth | 80 | Lebar maksimal baris sebelum Prettier memecah baris |
bracketSpacing | true | Tambahkan spasi di dalam kurung objek: { a: 1 } |
arrowParens | "always" | Selalu gunakan kurung pada arrow function params |
Contoh Hasil Format Prettier
// β SEBELUM di-format Prettier:
const user={name:"Budi",age:25,email:"budi@mail.com",isActive:true,skills:["JavaScript","TypeScript","React","Node.js"]}
const greeting = "Hello " + user.name + "! Welcome to " + "our platform. We hope you enjoy your stay here with us."
function calculateTotal(items){let total=0;for(let i=0;i<items.length;i++){total+=items[i].price*items[i].quantity}return total}
// β
SESUDAH di-format Prettier:
const user = {
name: 'Budi',
age: 25,
email: 'budi@mail.com',
isActive: true,
skills: ['JavaScript', 'TypeScript', 'React', 'Node.js'],
};
const greeting =
"Hello " + user.name + "! Welcome to " +
"our platform. We hope you enjoy your stay here with us.";
function calculateTotal(items) {
let total = 0;
for (let i = 0; i < items.length; i++) {
total += items[i].price * items[i].quantity;
}
return total;
}
8. Mengintegrasikan ESLint + Prettier
ESLint dan Prettier memiliki beberapa rules yang overlap (misalnya tentang semicolons, quotes, indentation). Tanpa integrasi yang benar, keduanya bisa saling konflik. Solusinya: gunakan eslint-config-prettier untuk mematikan rules ESLint yang konflik dengan Prettier.
Instalasi
# Matikan ESLint rules yang konflik dengan Prettier npm install -D eslint-config-prettier # (Opsional) Jalankan Prettier sebagai ESLint rule # NOTE: Banyak yang TIDAK merekomendasikan ini karena lambat # Lebih baik jalankan Prettier terpisah npm install -D eslint-plugin-prettier
Konfigurasi Terintegrasi
// eslint.config.mjs β ESLint + Prettier terintegrasi
import js from '@eslint/js';
import tsPlugin from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import prettierConfig from 'eslint-config-prettier';
export default [
js.configs.recommended,
{
files: ['**/*.{ts,tsx}'],
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: './tsconfig.json'
}
},
plugins: {
'@typescript-eslint': tsPlugin
},
rules: {
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/no-explicit-any': 'warn',
}
},
// HARUS di akhir β mematikan rules yang konflik dengan Prettier
prettierConfig,
{
ignores: ['dist/**', 'node_modules/**']
}
];
npm Scripts yang Direkomendasikan
// package.json
{
"scripts": {
"lint": "eslint \"src/**/*.{js,ts,jsx,tsx}\"",
"lint:fix": "eslint --fix \"src/**/*.{js,ts,jsx,tsx}\"",
"format": "prettier --write \"src/**/*.{js,ts,jsx,tsx,json,css,md}\"",
"format:check": "prettier --check \"src/**/*.{js,ts,jsx,tsx,json,css,md}\"",
"check": "npm run lint && npm run format:check",
"fix": "npm run lint:fix && npm run format"
}
}
9. Integrasi dengan VS Code
Untuk pengalaman development yang optimal, konfigurasikan VS Code agar ESLint dan Prettier berjalan otomatis saat kamu menulis kode.
Extensions yang Dibutuhkan
- ESLint (
dbaeumer.vscode-eslint) β Highlight masalah di editor - Prettier (
esbenp.prettier-vscode) β Format on save - EditorConfig (
EditorConfig.EditorConfig) β Konsistensi antar editor (opsional)
VS Code Settings
// .vscode/settings.json β VS Code workspace settings
{
// === Prettier sebagai default formatter ===
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.formatOnPaste": false,
// === ESLint auto-fix on save ===
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "explicit"
},
// === Language-specific settings ===
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[markdown]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.wordWrap": "on"
},
"[html]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// === ESLint validation ===
"eslint.validate": [
"javascript",
"typescript",
"javascriptreact",
"typescriptreact"
],
// === Tambahan ===
"editor.tabSize": 2,
"editor.insertSpaces": true,
"files.eol": "\n",
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
// === Exclude dari file explorer ===
"files.exclude": {
"**/node_modules": true,
"**/dist": true,
"**/.git": true
}
}
Rekomendasi Extensions (workspace)
// .vscode/extensions.json β Rekomendasikan extensions ke tim
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"EditorConfig.EditorConfig",
"bradlc.vscode-tailwindcss",
"csstools.postcss",
"christian-kohler.path-intellisense",
"streetsidesoftware.code-spell-checker"
]
}
10. Pre-commit Hooks dengan Husky
Husky dan lint-staged memungkinkan kita menjalankan ESLint dan Prettier secara otomatis sebelum setiap git commit. Ini memastikan tidak ada kode yang bermasalah yang masuk ke repository.
Instalasi Husky + lint-staged
# Instal Husky npm install -D husky # Inisialisasi Husky (membuat folder .husky/) npx husky init # Instal lint-staged npm install -D lint-staged
Konfigurasi
# .husky/pre-commit β Script yang dijalankan sebelum commit npx lint-staged
// package.json β Konfigurasi lint-staged
{
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,css,md,yml,yaml}": [
"prettier --write"
]
}
}
Alur Pre-commit
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β PRE-COMMIT HOOK FLOW β β β β Developer: git commit -m "feat: add product page" β β β β β βΌ β β ββββββββββββββββββββββ β β β Husky β β Intercept git hook β β β .husky/pre-commit β β β ββββββββββ¬ββββββββββββ β β βΌ β β ββββββββββββββββββββββ β β β lint-staged β β Hanya file yang di-staged β β β β β β β *.ts, *.tsx: β β β β 1. eslint --fix β β Auto-fix masalah kode β β β 2. prettier --writeβ β Auto-format β β β β β β β *.json, *.css: β β β β 1. prettier --writeβ β Format β β ββββββββββ¬ββββββββββββ β β βΌ β β ββββββββββββββββββββββ β β β Result: β β β β β Pass β Commit β β β β β Fail β Block β β Developer harus fix dulu β β ββββββββββββββββββββββ β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
11. CI/CD Integration
Mengintegrasikan ESLint dan Prettier ke dalam CI/CD pipeline memastikan bahwa setiap pull request yang masuk sudah memenuhi standar kualitas kode tim kamu.
GitHub Actions
# .github/workflows/lint.yml
name: Code Quality
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
lint-and-format:
name: Lint & Format Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npm run lint
- name: Check Prettier formatting
run: npm run format:check
- name: Run TypeScript type check
run: npx tsc --noEmit
test:
name: Unit Tests
runs-on: ubuntu-latest
needs: lint-and-format
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
GitLab CI
# .gitlab-ci.yml
stages:
- quality
- test
lint:
stage: quality
image: node:20
cache:
paths:
- node_modules/
script:
- npm ci
- npm run lint
- npm run format:check
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_BRANCH == "develop"
typecheck:
stage: quality
image: node:20
cache:
paths:
- node_modules/
script:
- npm ci
- npx tsc --noEmit
test:
stage: test
image: node:20
cache:
paths:
- node_modules/
script:
- npm ci
- npm test -- --coverage
coverage: '/Statements\s*:\s*(\d+\.?\d*)%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
12. Best Practices dan Cheat Sheet
Best Practices
| Praktik | Penjelasan |
|---|---|
| Gunakan Flat Config | ESLint v9+ menggunakan eslint.config.mjs β lebih fleksibel dan modern |
| Pisahkan ESLint dan Prettier | ESLint untuk logic, Prettier untuk format. Gunakan eslint-config-prettier |
| Commit Config ke Repo | Simpan .prettierrc dan eslint.config.mjs di repo agar semua developer konsisten |
| Gunakan lint-staged | Hanya lint file yang di-stage β lebih cepat dari linting seluruh proyek |
| Fail CI on Errors | Pastikan CI pipeline gagal jika ada ESLint error atau format yang salah |
| Mulai dari Strict | Lebih baik mulai ketat dan rilekskan rules daripada sebaliknya |
| Dokumentasikan Rule Custom | Beri komentar mengapa setiap rule diatur β bantu tim memahami |
| Review Rules Berkala | Evaluasi rules setiap beberapa bulan β sesuaikan dengan kebutuhan tim |
Cheat Sheet Perintah
# ================================
# ESLINT CHEAT SHEET
# ================================
# Lint file
npx eslint src/app.ts
# Auto-fix
npx eslint --fix src/app.ts
# Lint dengan debug info
npx eslint --debug src/app.ts
# Inisialisasi ESLint baru
npm init @eslint@latest
# ================================
# PRETTIER CHEAT SHEET
# ================================
# Format file
npx prettier --write src/app.ts
# Cek format (CI)
npx prettier --check "src/**/*.{ts,tsx}"
# Format semua
npx prettier --write .
# Override config via CLI
npx prettier --single-quote --tab-width 4 --write src/app.ts
# ================================
# GABUNGAN
# ================================
# Fix everything (ESLint + Prettier)
npm run lint:fix && npm run format
# Check everything (CI)
npm run lint && npm run format:check
# Type check
npx tsc --noEmit
# Semua sekaligus
npm run lint && npm run format:check && npx tsc --noEmit
13. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang ESLint dan Prettier: