Files
dev-configs/docs/requirements.md
vigdorov cf64bf6d7d feat: initial dev-configs monorepo
Shared configs for TypeScript projects: ESLint, Prettier, TypeScript,
Vite, Jest, Playwright, Knip. Published as @vigdorov/* npm packages
to Gitea registry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 23:40:22 +03:00

334 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# dev-configs — Требования
## Общее описание
Monorepo библиотека с общими конфигами для TypeScript-проектов. Публикуется как набор npm-пакетов в Gitea npm registry.
## Архитектура
- **Monorepo:** npm workspaces
- **Scope:** `@vigdorov/`
- **Registry:** Gitea npm (https://git.vigdorov.ru/api/packages/vigdorov/npm/)
- **Сборка пакетов:** tsc → dist/
- **Версионирование:** Semver, независимое, ручное (`npm version -w`)
- **Публикация:** Только пакеты с поднятой версией (CI сравнивает с registry)
- **CI:** Drone CI, тип `library` в ci-templates, trigger только на main
## Пакеты
### @vigdorov/prettier-config
**Тип:** Статичный объект (без функции-генератора)
```json
{
"printWidth": 120,
"tabWidth": 4,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": false,
"jsxSingleQuote": false,
"arrowParens": "always"
}
```
### @vigdorov/eslint-config
**Тип:** Функции-генераторы
**Формат:** ESLint 9+ flat config
**Пресеты:** `base`, `react`, `node`
**Зависимости:**
- eslint
- typescript-eslint (v8+)
- @stylistic/eslint-plugin
- eslint-plugin-unused-imports
- eslint-plugin-react (для react)
- eslint-plugin-react-hooks (для react)
**Правила — качество кода (base):**
- `eqeqeq: "error"`
- `no-console: ["warn", {allow: ["warn", "error"]}]`
- `no-alert: "warn"`
- `no-param-reassign: ["error", {props: true}]`
- `no-useless-concat: "warn"`
- `no-else-return: "warn"`
- `no-lonely-if: "warn"`
- `no-constructor-return: "warn"`
- `no-sequences: "warn"`
- `prefer-promise-reject-errors: "warn"`
- `require-await: "warn"`
- `no-new: "warn"`
- `no-multi-str: "warn"`
- `no-multi-assign: "warn"`
- `no-nested-ternary: "warn"`
- `no-useless-computed-key: "warn"`
- `no-useless-constructor: "warn"`
- `no-var: "warn"`
- `no-duplicate-imports: "warn"`
- `no-plusplus: "warn"`
- `no-bitwise: "warn"`
- `prefer-const: "warn"`
- `prefer-rest-params: "warn"`
- `prefer-template: "warn"`
- `array-callback-return: ["warn", {allowImplicit: true, checkForEach: true}]`
- `default-param-last: "warn"`
- `yoda: "warn"`
**Правила — TypeScript (base):**
- `@typescript-eslint/no-explicit-any: "error"`
- `@typescript-eslint/no-empty-object-type: "error"`
- `@typescript-eslint/no-unsafe-function-type: "error"`
- `@typescript-eslint/no-wrapper-object-types: "error"`
- `@typescript-eslint/no-use-before-define: "warn"`
- `@typescript-eslint/no-shadow: "warn"`
**Правила — unused imports (base):**
- `unused-imports/no-unused-imports: "warn"`
- `unused-imports/no-unused-vars: ["warn", {vars: "all", varsIgnorePattern: "^_", args: "after-used", argsIgnorePattern: "^_"}]`
**Правила — @stylistic (base):**
- `@stylistic/no-multiple-empty-lines: ["warn", {max: 1}]`
- `@stylistic/lines-between-class-members: ["warn", "always"]`
- `@stylistic/line-comment-position: ["warn", {position: "above"}]`
- `@stylistic/multiline-comment-style: ["warn", "starred-block"]`
- `@stylistic/capitalized-comments: "warn"`
- `@stylistic/max-len: ["warn", {code: 120, ignoreComments: true, ignoreUrls: true, ignoreStrings: true, ignoreTemplateLiterals: true, ignoreRegExpLiterals: true}]`
**Правила — React (пресет react, поверх base):**
- `react/prop-types: "off"`
- `react/react-in-jsx-scope: "off"`
- `react/jsx-filename-extension: ["warn", {extensions: [".tsx"]}]`
- `react/jsx-props-no-spreading: "warn"`
- `react/jsx-key: "warn"`
- `react/no-array-index-key: "warn"`
- `react/destructuring-assignment: "warn"`
- `react/prefer-stateless-function: "warn"`
- `react/jsx-fragments: ["off", "element"]`
- `react-hooks/exhaustive-deps: "warn"`
**Правила — Node (пресет node):**
- Базовые правила из base
- React-специфичные правила отключены
### @vigdorov/typescript-config
**Тип:** JSON-файлы для extends (без сборки)
**base.json:**
```json
{
"compilerOptions": {
"strict": true,
"target": "ES2022",
"module": "ESNext",
"lib": ["ES2022"],
"moduleResolution": "bundler",
"resolveJsonModule": true,
"forceConsistentCasingInFileNames": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": false,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"allowSyntheticDefaultImports": true,
"removeComments": true,
"sourceMap": true,
"incremental": true,
"skipLibCheck": true,
"isolatedModules": true
}
}
```
**react.json:**
```json
{
"extends": "./base.json",
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"jsx": "react-jsx"
}
}
```
### @vigdorov/vite-config
**Тип:** Функции-генераторы
**Пресеты:** `spa`, `library`
**spa:**
```ts
import {spa} from '@vigdorov/vite-config';
export default spa({
port: 5176,
aliases: {'@': 'src'},
proxy: {'/api': 'http://localhost:3003'},
});
```
**library:**
```ts
import {library} from '@vigdorov/vite-config';
export default library({
entry: 'src/index.ts',
name: 'my-lib',
aliases: {'@': 'src'},
external: ['react', 'react-dom'],
formats: ['es', 'cjs'],
});
```
**Генератор spa:**
- Подключает @vitejs/plugin-react
- Преобразует aliases в resolve.alias с абсолютными путями
- Настраивает server.port и server.proxy
- Дефолты: base: "/", outDir: "dist"
**Генератор library:**
- Настраивает build.lib (entry, name, formats)
- rollupOptions.external — исключает peer-зависимости
- fileName: index.mjs / index.cjs
- Алиасы работают так же как в SPA
### @vigdorov/jest-config
**Тип:** Функция-генератор
```ts
import {jestConfig} from '@vigdorov/jest-config';
export default jestConfig({
environment: 'jsdom',
aliases: {'@': 'src'},
});
```
**Базовый конфиг:**
- clearMocks: true
- collectCoverage: true
- coverageReporters: ['html', 'text', 'text-summary', 'lcov']
- coverageDirectory: 'coverage'
- testMatch: ['**/__tests__/**/*.(j|t)s?(x)', '**/?(*.)+(spec|test).(j|t)s?(x)']
- testPathIgnorePatterns: ['/node_modules/', '/dist/']
- transform: @swc/jest
**Параметры:**
- environment: 'node' | 'jsdom'
- aliases: Record<string, string> — преобразуется в moduleNameMapper
### @vigdorov/playwright-config
**Тип:** Функция-генератор
```ts
import {playwrightConfig} from '@vigdorov/playwright-config';
export default playwrightConfig({
baseURL: 'http://localhost:5176',
testDir: 'e2e',
retries: 0,
});
```
**Базовый конфиг:**
- timeout: 30000
- retries: CI ? (params.retries ?? 2) : (params.retries ?? 0)
- reporter: CI ? 'html' : 'list'
- use.trace: 'on-first-retry'
- use.screenshot: 'only-on-failure'
- Три браузера: chromium, firefox, webkit
### @vigdorov/knip-config
**Тип:** Функция-генератор
```ts
import {knipConfig} from '@vigdorov/knip-config';
export default knipConfig({
entry: ['src/main.tsx'],
});
```
**Базовый конфиг:**
- entry: params.entry ?? ['src/index.ts']
- project: params.project ?? ['src/**/*.{ts,tsx,js,jsx}']
- ignore: ['**/*.test.*', '**/*.spec.*', 'e2e/**', '**/*.d.ts', ...params.ignore]
- ignoreDependencies: params.ignoreDependencies ?? []
- Knip завершается с exit code 1 при ошибках (используется в check-скрипте для блокировки сборки)
## CI/CD
### Тип `library` в ci-templates
**Отдельный `library.drone.yml`** (не base.drone.yml)
**Pipeline:** prepare → install → test → build → publish
**Скрипт `publish-lib.sh`:**
- Настраивает .npmrc с GITEA_TOKEN
- Для каждого пакета в packages/ сравнивает версию с registry
- Публикует только пакеты с новой версией
**Trigger:** только main, только push
### service.yaml для dev-configs
```yaml
service:
name: dev-configs
type: library
library:
scope: "@vigdorov"
registry: "https://git.vigdorov.ru/api/packages/vigdorov/npm/"
```
## Процесс работы с версиями
1. Разработка в feature-ветке
2. Merge в main
3. Поднятие версии: `npm version patch -w packages/eslint`
4. Commit + push: `git commit -am "release: eslint@1.0.1"`
5. CI публикует только пакеты с новой версией
## Потребители
В каждом проекте-потребителе:
**.npmrc:**
```ini
@vigdorov:registry=https://git.vigdorov.ru/api/packages/vigdorov/npm/
```
**.gitignore (добавить):**
```
coverage/
```
## Структура monorepo
```
dev-configs/
├── packages/
│ ├── eslint/ → @vigdorov/eslint-config
│ ├── prettier/ → @vigdorov/prettier-config
│ ├── typescript/ → @vigdorov/typescript-config
│ ├── vite/ → @vigdorov/vite-config
│ ├── jest/ → @vigdorov/jest-config
│ ├── playwright/ → @vigdorov/playwright-config
│ └── knip/ → @vigdorov/knip-config
├── package.json (workspace root, private: true)
├── tsconfig.base.json (общий tsconfig для сборки пакетов)
├── .npmrc
├── service.yaml
├── .drone.yml
├── .gitignore
└── CLAUDE.md
```