# Quy tắc codebase

Tài liệu tổng hợp toàn bộ quy tắc, convention và cấu hình linting/formatting của dự án AutoShopee Kit.

## 1. Quy tắc ESLint

### Cấu hình cơ bản

| Thuộc tính | Giá trị |
|------------|---------|
| Parser | `@babel/eslint-parser` |
| ECMAScript | `latest` |
| Source type | `module` |
| Extends | `plugin:vue/recommended`, `airbnb-base`, `plugin:jsdoc/recommended` |
| Plugins | `prettier`, `vue`, `jsdoc` |
| Env | `node`, `webextensions` |
| Globals | `globalThis: readonly` |

### Quy tắc đã tắt (off = 0)

#### Nhóm JavaScript Core

| Rule | Lý do |
|------|-------|
| `no-constant-condition` | Cho phép `while(true)` trong các vòng lặp polling |
| `no-console` | Cho phép `console.log` trong development (production sẽ strip bởi Babel) |
| `no-debugger` | Cho phép `debugger` trong development |
| `no-async-promise-executor` | Cho phép async function trong Promise constructor |
| `no-return-await` | Cho phép `return await` để giữ stack trace rõ ràng |
| `no-throw-literal` | Cho phép throw string/object thay vì chỉ Error instance |
| `no-param-reassign` | Cho phép gán lại tham số hàm (phổ biến trong Vuex mutations) |
| `no-empty` | Cho phép block rỗng (dùng cho try/catch bỏ qua lỗi) |
| `no-restricted-globals` | Cho phép sử dụng global variables không hạn chế |
| `no-alert` | Cho phép `alert()`, `confirm()`, `prompt()` |
| `no-nested-ternary` | Cho phép ternary lồng nhau |
| `no-new` | Cho phép `new` mà không gán vào biến |
| `no-await-in-loop` | Cho phép `await` trong vòng lặp (cần cho tuần tự hóa request) |
| `no-loop-func` | Cho phép tạo function trong vòng lặp |
| `no-plusplus` | Cho phép toán tử `++`/`--` |
| `no-useless-escape` | Cho phép escape character không cần thiết trong regex |
| `no-underscore-dangle` | Cho phép biến bắt đầu/kết thúc bằng `_` |
| `no-shadow` | Cho phép biến trùng tên ở scope khác nhau |
| `no-empty-pattern` | Cho phép destructuring pattern rỗng |

#### Nhóm Style

| Rule | Lý do |
|------|-------|
| `linebreak-style` | Không ép kiểu xuống dòng (LF/CRLF) - hỗ trợ cả Windows và Unix |
| `max-len` | Không giới hạn độ dài dòng (Prettier quản lý) |
| `quote-props` | Không ép quy tắc quote cho object property |
| `camelcase` | Không ép camelCase (API response thường dùng snake_case) |
| `prefer-destructuring` | Không ép destructuring |
| `arrow-body-style` | Không ép arrow function body style |
| `func-names` | Không ép đặt tên cho function expression |
| `object-property-newline` | Không ép xuống dòng cho object property |
| `max-classes-per-file` | Không giới hạn số class trong 1 file |
| `consistent-return` | Không ép return nhất quán |
| `array-callback-return` | Không ép return trong callback của array method |
| `global-require` | Cho phép `require()` ở bất kỳ đâu (dynamic import) |
| `class-methods-use-this` | Không ép method phải dùng `this` |

#### Nhóm Import

| Rule | Lý do |
|------|-------|
| `import/no-extraneous-dependencies` | Không kiểm tra dependency ngoài package.json |
| `import/order` | Không ép thứ tự import |
| `import/no-duplicates` | Không kiểm tra import trùng |
| `import/prefer-default-export` | Không ép default export |
| `import/no-cycle` | Không kiểm tra circular dependency |
| `import/no-named-as-default-member` | Không kiểm tra named export dùng như default member |
| `import/no-mutable-exports` | Cho phép export mutable binding |
| `import/no-named-as-default` | Không kiểm tra named export dùng như default |
| `import/no-relative-packages` | Cho phép import relative từ package khác |
| `import/named` | Không kiểm tra named import tồn tại |
| `import/no-useless-path-segments` | Không kiểm tra path segment thừa |
| `import/no-self-import` | Không kiểm tra self-import |
| `import/extensions` | Không ép extension trong import path |

#### Nhóm Vue

| Rule | Lý do |
|------|-------|
| `vue/multi-word-component-names` | Cho phép tên component 1 từ |
| `vue/no-v-html` | Cho phép `v-html` (cần cho render HTML động) |
| `vue/require-default-prop` | Không ép default value cho prop |
| `vue/valid-v-for` | Không kiểm tra `v-for` hợp lệ |
| `vue/order-in-components` | Không ép thứ tự option trong component |
| `vue/no-v-text-v-html-on-component` | Cho phép `v-html` trên component |
| `vue/require-prop-types` | Không ép khai báo type cho prop |
| `vue/no-deprecated-v-bind-sync` | Cho phép `.sync` modifier (Vue 2 feature) |
| `vue/no-use-v-if-with-v-for` | Cho phép dùng `v-if` cùng `v-for` |
| `vue/require-explicit-emits` | Không ép khai báo emits option |
| `vue/no-deprecated-slot-attribute` | Cho phép `slot` attribute (Vue 2 syntax) |
| `vue/no-deprecated-filter` | Cho phép filter syntax (Vue 2 feature) |
| `vue/no-v-for-template-key` | Cho phép key trên `<template v-for>` |

#### Nhom JSDoc

| Rule | Lý do |
|------|-------|
| `jsdoc/require-returns` | Không ép khai báo `@returns` |
| `jsdoc/require-returns-type` | Không ép type cho `@returns` |
| `jsdoc/require-param-type` | Không ép type cho `@param` (dùng import type thay thế) |
| `jsdoc/require-param-description` | Không ép mô tả cho `@param` |
| `jsdoc/no-undefined-types` | Cho phép type chưa định nghĩa (dùng import type) |
| `jsdoc/require-property-description` | Không ép mô tả cho `@property` |
| `jsdoc/require-returns-description` | Không ép mô tả cho `@returns` |

### Quy tắc được kế thừa (vẫn hoạt động)

Từ `plugin:vue/recommended`:
- Tất cả quy tắc `vue/essential` và `vue/strongly-recommended` (trừ các rule đã tắt ở trên)

Từ `airbnb-base`:
- `no-var` — buộc dùng `let`/`const`
- `prefer-const` — ưu tiên `const` khi không reassign
- `no-unused-vars` — cảnh báo biến không sử dụng
- `eqeqeq` — buộc dùng `===`/`!==`
- `no-eval` — cấm `eval()`
- Và nhiều rule khác từ Airbnb style guide

Từ `plugin:jsdoc/recommended`:
- `jsdoc/require-jsdoc` — yêu cầu JSDoc cho function/class
- `jsdoc/check-types` — kiểm tra type hợp lệ
- `jsdoc/valid-types` — kiểm tra cú pháp type

### Import Resolver

```js
'import/resolver': {
  'alias': [
    ['@', './src/app'],
    ['@content', './src/content'],
    ['@background', './src/background'],
  ],
}
```

ESLint được cấu hình để hiểu các path alias khi kiểm tra import.

---

## 2. Quy tắc Prettier

### Cấu hình chung

| Thuộc tính | Giá trị | Mô tả |
|------------|---------|-------|
| `semi` | `false` | Không dùng dấu chấm phẩy cuối dòng |
| `singleQuote` | `true` | Dùng dấu nháy đơn |
| `tabWidth` | `2` | Indent 2 spaces |
| `useTabs` | `false` | Dùng spaces, không dùng tabs |
| `trailingComma` | `"none"` | Không dùng trailing comma |
| `bracketSpacing` | `true` | Có space trong object `{ a: 1 }` |
| `bracketSameLine` | `true` | Dấu `>` của JSX/HTML đặt cùng dòng với attribute cuối |
| `arrowParens` | `"always"` | Luôn có ngoặc cho arrow function `(x) => x` |
| `quoteProps` | `"as-needed"` | Chỉ quote object key khi cần thiết |
| `jsxSingleQuote` | `false` | Dùng nháy kép trong JSX |
| `proseWrap` | `"preserve"` | Giữ nguyên xuống dòng trong prose |
| `insertPragma` | `false` | Không thêm pragma comment |

### Override cho file `.js`

| Thuộc tính | Giá trị |
|------------|---------|
| `printWidth` | `200` |

File JS có `printWidth` rất lớn (200 ký tự), cho phép dòng dài hơn bình thường.

### Override cho file `.vue`

| Thuộc tính | Giá trị |
|------------|---------|
| `parser` | `"vue"` |
| `printWidth` | `100` |
| `vueIndentScriptAndStyle` | `true` |
| `htmlWhitespaceSensitivity` | `"css"` |

File Vue dùng `printWidth` ngắn hơn (100) và indent nội dung bên trong `<script>` và `<style>`.

### Lệnh format

```bash
npm run pretty        # Chỉ chạy Prettier
npm run lint:fix      # Chỉ chạy ESLint fix
npm run format        # Chạy Prettier trước, ESLint fix sau
```

---

## 3. Quy tắc Vue

### Bắt buộc sử dụng Options API

Dự án sử dụng **Vue 2.7.10** với **Options API**. Không được dùng Composition API.

```js
// Đúng
export default {
  data() {
    return { count: 0 }
  },
  computed: {
    doubled() { return this.count * 2 }
  },
  methods: {
    increment() { this.count++ }
  }
}

// Sai - KHÔNG dùng Composition API
import { ref, computed } from 'vue'
export default {
  setup() {
    const count = ref(0)
    return { count }
  }
}
```

### Quy tắc component

- **PascalCase** cho tên component: `ProductCard.vue`, `ShopeeOrderList.vue`
- **camelCase** cho props: `shopId`, `productName`
- Cho phép tên component 1 từ (`vue/multi-word-component-names: off`)
- Không ép thứ tự option trong component (`vue/order-in-components: off`)
- Cho phép dùng `.sync` modifier và `slot` attribute (Vue 2 syntax)

### UI Framework

- Sử dụng **View Design** (iView cho Vue 2) làm UI component library
- Import component từ `view-design`

### Tổ chức component

```
src/app/components/
├── Shopee/      # Component riêng cho Shopee
├── Lazada/      # Component riêng cho Lazada
├── Tiktok/      # Component riêng cho TikTok
├── Layout/      # Layout components
├── Plugin/      # Plugin components
└── Widget/      # Widget components dùng chung
```

---

## 4. Quy tắc TypeScript/JSDoc

### Chỉ dùng JSDoc, không dùng TypeScript

Dự án không dùng TypeScript. Toàn bộ type annotation được viết bằng JSDoc.

### Nơi định nghĩa type

Tất cả type definition phải nằm trong `src/app/types/`. Không viết type inline.

```js
// Đúng - type trong file riêng
// src/app/types/Product.js
/**
 * @typedef {Object} Product
 * @property {string} id
 * @property {string} name
 * @property {number} price
 */

// Sử dụng
/** @param {import('@type/Product').Product} product */
function processProduct(product) {}
```

### Quy tắc JSDoc

- Kế thừa từ `plugin:jsdoc/recommended` nhưng tắt nhiều rule bắt buộc
- Không ép khai báo `@returns`, `@param` type, hay description
- Cho phép type chưa định nghĩa (vì dùng import type)
- Vẫn yêu cầu JSDoc cho function/class (`jsdoc/require-jsdoc`)

### Cú pháp import type

```js
/** @param {import('@/types/Shop').Shop} shop */
/** @type {import('@type/Order').OrderItem[]} */
```

---

## 5. Quy tắc CSS

### Tailwind CSS

- Phiên bản: `1.9.6`
- Dark mode: `false` (không hỗ trợ)
- Purge CSS: bật trong production
- Purge content: `./public/**/*.html`, `./src/**/*.{js,jsx,vue,ts,tsx}`

#### Safelist (class luôn giữ lại)

```js
safelist: [
  'bg-red-500',
  'text-white',
  'border',
  /^(hover|focus|active):/,    // Tất cả pseudo-class prefix
  /^text-(xs|sm|base|lg|xl)/,  // Các text size class
]
```

### SASS

- Preprocessor: `sass` v1.32.7
- Loader: `sass-loader` v12.0.0
- Dùng kết hợp với Tailwind CSS

### PostCSS

- `autoprefixer` v10.4.5 — tự động thêm vendor prefix
- `cssnano` v7.0.7 — minify CSS trong production

---

## 6. Git Hooks

### Husky

Dự án cài đặt `husky` v2.4.0 trong devDependencies. Tuy nhiên, không có thư mục `.husky/` và không có cấu hình `lint-staged` trong `package.json`.

Hiện tại husky được cài nhưng **chưa có hook nào được cấu hình** hoạt động.

### Quy trình format thủ công

Trước khi commit, developer nên chạy:

```bash
npm run format    # Prettier + ESLint fix
```

---

## 7. Quy tắc Build

### Babel

#### Preset

```js
'@vue/cli-plugin-babel/preset' {
  useBuiltIns: 'entry',
  corejs: 3,
  targets: {
    chrome: '88',
    firefox: '78',
  },
}
```

- `useBuiltIns: 'entry'` — import polyfill theo entry point
- `corejs: 3` — dùng core-js 3 cho polyfill
- Target: Chrome 88+, Firefox 78+

#### Plugin

- `@babel/plugin-transform-runtime` — tái sử dụng helper, hỗ trợ `regenerator` cho async/await

#### Production

```js
'transform-remove-console' { exclude: ['error', 'warn'] }
```

Trong production, tự động xóa tất cả `console.log()`. Chỉ giữ lại `console.error()` và `console.warn()`.

### WXT / Vite (wxt.config.ts)

#### Development
- Vite dev server voi HMR cho popup/options
- Auto-reload content scripts va background
- Source maps: Vite default

#### Production
- Vite/Rollup tu dong: tree-shaking, minimize, code splitting

#### Manifest
Cau hinh trong `wxt.config.ts` voi function `manifest({ browser, mode })`:
- Development: them label `Dev`, doi icon, mo rong CSP va host_permissions
- Chrome: them `incognito: 'split'`
- Firefox: WXT tu dong chuyen MV3 → MV2, them `gecko.id`
- Content scripts: tu dong generate tu `entrypoints/*.content.js`

---

## 8. Quy tắc đặt tên

### File và thư mục

| Loại | Convention | Ví dụ |
|------|-----------|-------|
| Vue component | PascalCase | `ProductCard.vue`, `ShopeeOrderList.vue` |
| JS module | camelCase | `shopApi.js`, `orderService.js` |
| Type definition | PascalCase | `Product.js`, `OrderItem.js` |
| Thư mục | camelCase hoặc lowercase | `api/`, `store/`, `constant/` |
| Content script | lowercase với dấu `/` | `content/shopee/seller/chat` |
| Constant file | camelCase | `orderStatus.js` |

### Biến và hàm

| Loại | Convention | Ví dụ |
|------|-----------|-------|
| Biến | camelCase | `shopId`, `productList` |
| Hàm | camelCase | `fetchOrders()`, `getShopInfo()` |
| Component name | PascalCase | `ProductCard` |
| Props | camelCase | `shopId`, `productName` |
| Vuex mutation | UPPER_SNAKE_CASE hoặc camelCase | `SET_SHOP`, `updateOrder` |
| CSS class | Tailwind utility hoặc kebab-case | `text-white`, `product-card` |

### Quy tắc từ CLAUDE.md

- Không viết inline comment mô tả code làm gì
- Bắt buộc dùng dấu ngoặc nhọn `{}` cho tất cả `if` statement
- Không dùng toán tử ternary — dùng `if/else` đầy đủ
- Mỗi điều kiện đặt trên dòng riêng
- Không tách file khi refactor — giữ code trong file hiện tại

---

## 9. Quy tắc Import

### Path Alias

| Alias | Trỏ đến | Dùng cho |
|-------|---------|----------|
| `@` | `./src/app/` | Code chính của ứng dụng |
| `@content` | `./src/content/` | Content scripts |
| `@background` | `./src/background/` | Background service worker |
| `@type` | `./src/app/types/` | Type definitions |

Alias duoc cau hinh trong `wxt.config.ts`:
- `@` — built-in, map tu `srcDir: 'src/app'`
- `@content`, `@background`, `@type` — custom aliases trong `alias` field
- `.eslintrc.js` — ESLint import resolver

### jsconfig.json

```json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/app/*"],
      "@content/*": ["./src/content/*"],
      "@background/*": ["./src/background/*"]
    }
  },
  "include": ["./src/**/*.js"],
  "exclude": ["node_modules", "test", "build", "dist", "artifacts", "public"]
}
```

### Thứ tự import

ESLint không ép thứ tự import (`import/order: off`). Tuy nhiên, convention khuyến nghị:

1. Node built-in modules
2. Third-party packages (`vue`, `axios`, `lodash`, ...)
3. Alias imports (`@/...`, `@content/...`, `@background/...`)
4. Relative imports (`./`, `../`)

### Không ép extension

`import/extensions: off` — không yêu cầu ghi extension trong import path.

```js
// Cả hai đều hợp lệ
import shopApi from '@/api/shopApi'
import shopApi from '@/api/shopApi.js'
```

---

## 10. Tổng hợp lệnh kiểm tra

| Lệnh | Mô tả |
|-------|-------|
| `npm run lint` | Kiểm tra ESLint cho `src/**/*.{js,vue}` |
| `npm run lint:fix` | Tự động sửa lỗi ESLint |
| `npm run pretty` | Format bằng Prettier cho `src/**/*.{js,vue}` |
| `npm run format` | Chạy Prettier trước, rồi ESLint fix |
| `npm run build:chrome` | Build Chrome extension (MV3) |
| `npm run build:firefox` | Build Firefox extension (MV2) |
