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>
This commit is contained in:
333
docs/requirements.md
Normal file
333
docs/requirements.md
Normal file
@ -0,0 +1,333 @@
|
||||
# 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
|
||||
```
|
||||
Reference in New Issue
Block a user