Качество кода
Проект использует комплексный набор инструментов для обеспечения качества и консистентности кода. Подробнее о каждом инструменте: ESLint, Stylelint, Prettier, TypeScript.
ESLint
Конфигурация: eslint.config.mjs (Flat Config v9+)
ESLint плагины
@angular-eslint
- Правила для Angular-специфичного кода
- Проверка директив, компонентов, инъекции
- Правило
prefer-inject: используйтеinject()вместо конструктора
sonarjs
- Обнаружение vulnerabilities
- Правила:
no-inverted-boolean-check,prefer-immediate-return
unicorn
- Best practices JavaScript/TypeScript
- Более 70 правил для качественного кода
- Правила:
throw-new-error,prefer-modern-dom-apisи др.
simple-import-sort
- Автоматическая сортировка импортов
- Группирует: external → internal → relative
unused-imports
- Удаление неиспользуемых импортов
- Проверка неиспользуемых переменных (кроме
_префикса)
Основные правила ESLint
Обязательные (Error)
// ✅ Правильно
const message = "Hello";
if (!value) return;
const sum = 2 + 3;
const equals = a === b;
// ❌ Неправильно
var message = "Hello"; // no-var
if (value) {
return;
} // no-else-return
const sum = 2 + 3; // space-infix-ops
const equals = a == b; // eqeqeqСтиль кода
// Обязательные скобки
if (condition) { /* ... */ } // curly: error
// Правильный spacing
{ a: 1, b: 2 } // object-curly-spacing
function test(param) {} // space-before-function-paren
// Правильные стрелки
const fn = () => console.log(); // arrow-parens
// Импорты отсортированы
import { Component } from "@angular/core";
import { OnInit } from "@angular/core";
import { Component } from "./component";Angular специфичные
// ✅ Правильно
@Component({
selector: "app-my-component", // kebab-case
standalone: true
})
export class MyComponent {}
@Directive({
selector: "[appHighlight]" // camelCase с app префиксом
})
export class HighlightDirective {}
constructor(private router = inject(Router)) {} // prefer-inject
// ❌ Неправильно
@Component({
selector: "MyComponent" // не kebab-case
})
[myHighlight] // неправильный префиксПроверка ESLint
# Проверить все файлы в src/
npm run lint:check
# Автоисправление
npm run lint:fix
# Проверка на staged файлы
npm run lint:stagedStylelint
Конфигурация: .stylelintrc.json
SCSS плагины
- stylelint-scss — поддержка SCSS синтаксиса и правил
Основные правила SCSS
Переменные
// ✅ Правильно
$margin-top: 10px;
$margin: $margin-top 0 20px;
// ❌ Неправильно
$marginTop: 10px; // kebab-case
margin: 0 0 20px 0; // должны быть переменныеAt-правила
// ✅ Правильно
@mixin button-reset {
/* ... */
}
@mixin button-hover {
/* ... */
}
// ❌ Неправильно
@mixin buttonReset {
// kebab-case
}Цвета
// ✅ Правильно
$color: #ffffff; // long hex
$opacity: 50%; // percentage notation
// ❌ Неправильно
$color: #fff; // short hex
$opacity: 0.5; // numeric notationДлины
// ✅ Правильно
$margin: 0; // zero без unit
$padding: 10px;
// ❌ Неправильно
$margin: 0px; // zero с unitПроверка Stylelint
# Проверить все SCSS
npm run slint:check
# Автоисправление
npm run slint:fix
# Проверка на staged файлы
npm run slint:stagedPrettier
Конфигурация: автоматическая (используется ESLint правила)
Prettier автоматически форматирует код при:
- Pre-commit hook (
pretty-quick --staged) - Ручное выполнение (
npm run format:write)
Форматирование
# Проверить форматирование
npm run format:check
# Отформатировать все файлы
npm run format:write
# Форматирование на staged файлах (в pre-commit)
npx pretty-quick --stagedTypeScript
Конфигурация: tsconfig.json
Strict Mode
Все файлы компилируются с strict: true:
// ✅ Типизировано
const user: IUser = {
id: "uuid",
email: "user@example.com"
};
// ❌ Не типизировано
const user: any = {
/* ... */
};
const obj = {
/* ... */
}; // type: unknownВажные настройки
{
"compilerOptions": {
"strict": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitAny": true,
"noImplicitThis": true,
"alwaysStrict": true
}
}Проверка типов
# Проверить типы без компиляции
npm run typecheck
# Это выполняется в pre-push hookКомплексная проверка
Перед коммитом (автоматически)
git commit -m "message"
# Выполняется:
# 1. removeComments:staged
# 2. removeReturnTypes:staged
# 3. pretty-quick --staged
# 4. lint-staged (ESLint + Stylelint)Перед пушем (автоматически)
git push
# Выполняется:
# 1. typecheck
# 2. lint:check
# 3. build --configuration=productionРучная проверка
# Все проверки перед пушем
npm run typecheck && npm run lint:check && npm run build:prod
# Или отдельно:
npm run typecheck # TypeScript типы
npm run lint:check # ESLint
npm run slint:check # Stylelint
npm run format:check # PrettierUnit Testing
Тестовий фреймворк
Проект використовує Vitest як тестовий раннер (не Jest). Конфігурація знаходиться у vite.config.mts:
// vite.config.mts
export default defineConfig({
plugins: [angular()],
test: {
globals: true,
environment: "jsdom",
setupFiles: ["src/setup-vitest.ts"],
include: ["src/**/*.spec.ts"],
reporters: ["default"]
}
});Ключові особливості:
globals: true—describe,it,expect,beforeEachдоступні без імпортуenvironment: "jsdom"— DOM оточення для тестування компонентів@analogjs/vite-plugin-angular+@analogjs/vitest-angular— підтримка Angular у Vitest
Mocking (Vitest API)
Для мокування використовуйте Vitest API замість Jest:
// ✅ Vitest
const spy = vi.fn();
vi.spyOn(service, "method").mockReturnValue(of(data));
// ❌ Jest (не використовується)
const spy = jest.fn();
jest.spyOn(service, "method");Test Setup
Використовуйте getTestingProviders() для стандартного setup тестового оточення:
import { getTestingProviders } from "../../testing/testing.providers";
describe("MyComponent", () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [MyComponent],
providers: getTestingProviders()
}).compileComponents();
});
});getTestingProviders() надає: provideHttpClient(), provideHttpClientTesting(), provideRouter([]), provideTransloco(...).
Mock Services
Використовуйте factory-функції для створення mock-сервісів:
import { createMockBroadcastsService } from "../../testing/testing.mocks";
beforeEach(() => {
TestBed.overrideProvider(BroadcastsService, {
useValue: createMockBroadcastsService()
});
});Test Data
Використовуйте попередньо створені mock-дані для консистентності:
import { MOCK_BROADCAST, createMockPaginatedResponse } from "../../testing/testing.data";
it("should load", () => {
mockService.getBroadcasts.and.returnValue(of(createMockPaginatedResponse([MOCK_BROADCAST], 1)));
});Best Practices для тестів
- Використовуйте
getTestingProviders()для стандартного setup - Створюйте mock-сервіси через factory-функції з
testing.mocks.ts - Використовуйте готові MOCK_* об'єкти з
testing.data.ts - Не дублюйте mock-дані в різних тестових файлах
- Використовуйте
vi.fn()таvi.spyOn()для мокування (Vitest API)
Запуск тестів
npm test # Запустити всі тести (vitest run)Игнорирование правил
ESLint
// Отключить для строки
// eslint-disable-next-line rule-name
const suspicious = someFunction();
// Отключить для блока
/* eslint-disable rule-name */
const code = "something";
/* eslint-enable rule-name */
// Отключить для файла
/* eslint-disable */
// весь код в файлеStylelint
// Отключить для строки
// stylelint-disable-next-line
color: red;
// Отключить для блока
/* stylelint-disable */
.something {
color: red;
}
/* stylelint-enable */Best Practices
✅ Делайте:
- Регулярно запускайте
npm run lint:fixво время разработки - Читайте сообщения об ошибках от linter
- Используйте IDE extensions (ESLint, Stylelint)
- Проверяйте типы перед пушем
❌ Не делайте:
- Не отключайте правила без причины
- Не используйте
anyтипы - Не коммитьте с ошибками линтера
- Не игнорируйте ошибки TypeScript
IDE Extensions
VS Code
{
"recommendations": [
"dbaeumer.vscode-eslint",
"stylelint.vscode-stylelint",
"esbenp.prettier-vscode",
"Angular.ng-template"
]
}Настройки .vscode/settings.json
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
},
"[scss]": {
"editor.codeActionsOnSave": {
"source.fixAll.stylelint": "explicit"
}
}
}Следующие шаги
- Workflow разработки — Git hooks и автоматизация
- Правила кода — стандарты написания
- Архитектура — структура проекта