Skip to content

Качество кода

Проект использует комплексный набор инструментов для обеспечения качества и консистентности кода. Подробнее о каждом инструменте: ESLint, Stylelint, Prettier, TypeScript.

ESLint

Конфигурация: eslint.config.mjs (Flat Config v9+)

ESLint плагины

  1. @angular-eslint

    • Правила для Angular-специфичного кода
    • Проверка директив, компонентов, инъекции
    • Правило prefer-inject: используйте inject() вместо конструктора
  2. sonarjs

    • Обнаружение vulnerabilities
    • Правила: no-inverted-boolean-check, prefer-immediate-return
  3. unicorn

    • Best practices JavaScript/TypeScript
    • Более 70 правил для качественного кода
    • Правила: throw-new-error, prefer-modern-dom-apis и др.
  4. simple-import-sort

    • Автоматическая сортировка импортов
    • Группирует: external → internal → relative
  5. unused-imports

    • Удаление неиспользуемых импортов
    • Проверка неиспользуемых переменных (кроме _ префикса)

Основные правила ESLint

Обязательные (Error)

typescript
// ✅ Правильно
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

Стиль кода

typescript
// Обязательные скобки
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 специфичные

typescript
// ✅ Правильно
@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

bash
# Проверить все файлы в src/
npm run lint:check

# Автоисправление
npm run lint:fix

# Проверка на staged файлы
npm run lint:staged

Stylelint

Конфигурация: .stylelintrc.json

SCSS плагины

  • stylelint-scss — поддержка SCSS синтаксиса и правил

Основные правила SCSS

Переменные

scss
// ✅ Правильно
$margin-top: 10px;
$margin: $margin-top 0 20px;

// ❌ Неправильно
$marginTop: 10px; // kebab-case
margin: 0 0 20px 0; // должны быть переменные

At-правила

scss
// ✅ Правильно
@mixin button-reset {
	/* ... */
}

@mixin button-hover {
	/* ... */
}

// ❌ Неправильно
@mixin buttonReset {
	// kebab-case
}

Цвета

scss
// ✅ Правильно
$color: #ffffff; // long hex
$opacity: 50%; // percentage notation

// ❌ Неправильно
$color: #fff; // short hex
$opacity: 0.5; // numeric notation

Длины

scss
// ✅ Правильно
$margin: 0; // zero без unit
$padding: 10px;

// ❌ Неправильно
$margin: 0px; // zero с unit

Проверка Stylelint

bash
# Проверить все SCSS
npm run slint:check

# Автоисправление
npm run slint:fix

# Проверка на staged файлы
npm run slint:staged

Prettier

Конфигурация: автоматическая (используется ESLint правила)

Prettier автоматически форматирует код при:

  • Pre-commit hook (pretty-quick --staged)
  • Ручное выполнение (npm run format:write)

Форматирование

bash
# Проверить форматирование
npm run format:check

# Отформатировать все файлы
npm run format:write

# Форматирование на staged файлах (в pre-commit)
npx pretty-quick --staged

TypeScript

Конфигурация: tsconfig.json

Strict Mode

Все файлы компилируются с strict: true:

typescript
// ✅ Типизировано
const user: IUser = {
	id: "uuid",
	email: "user@example.com"
};

// ❌ Не типизировано
const user: any = {
	/* ... */
};
const obj = {
	/* ... */
}; // type: unknown

Важные настройки

json
{
	"compilerOptions": {
		"strict": true,
		"strictNullChecks": true,
		"strictFunctionTypes": true,
		"strictBindCallApply": true,
		"strictPropertyInitialization": true,
		"noImplicitAny": true,
		"noImplicitThis": true,
		"alwaysStrict": true
	}
}

Проверка типов

bash
# Проверить типы без компиляции
npm run typecheck

# Это выполняется в pre-push hook

Комплексная проверка

Перед коммитом (автоматически)

bash
git commit -m "message"

# Выполняется:
# 1. removeComments:staged
# 2. removeReturnTypes:staged
# 3. pretty-quick --staged
# 4. lint-staged (ESLint + Stylelint)

Перед пушем (автоматически)

bash
git push

# Выполняется:
# 1. typecheck
# 2. lint:check
# 3. build --configuration=production

Ручная проверка

bash
# Все проверки перед пушем
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       # Prettier

Unit Testing

Тестовий фреймворк

Проект використовує Vitest як тестовий раннер (не Jest). Конфігурація знаходиться у vite.config.mts:

typescript
// 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: truedescribe, it, expect, beforeEach доступні без імпорту
  • environment: "jsdom" — DOM оточення для тестування компонентів
  • @analogjs/vite-plugin-angular + @analogjs/vitest-angular — підтримка Angular у Vitest

Mocking (Vitest API)

Для мокування використовуйте Vitest API замість Jest:

typescript
// ✅ 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 тестового оточення:

typescript
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-сервісів:

typescript
import { createMockBroadcastsService } from "../../testing/testing.mocks";

beforeEach(() => {
	TestBed.overrideProvider(BroadcastsService, {
		useValue: createMockBroadcastsService()
	});
});

Test Data

Використовуйте попередньо створені mock-дані для консистентності:

typescript
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)

Запуск тестів

bash
npm test                # Запустити всі тести (vitest run)

Игнорирование правил

ESLint

typescript
// Отключить для строки
// eslint-disable-next-line rule-name
const suspicious = someFunction();

// Отключить для блока
/* eslint-disable rule-name */
const code = "something";
/* eslint-enable rule-name */

// Отключить для файла
/* eslint-disable */
// весь код в файле

Stylelint

scss
// Отключить для строки
// 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

json
{
	"recommendations": [
		"dbaeumer.vscode-eslint",
		"stylelint.vscode-stylelint",
		"esbenp.prettier-vscode",
		"Angular.ng-template"
	]
}

Настройки .vscode/settings.json

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"
		}
	}
}

Следующие шаги

SaaS Admin Documentation