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:
51
.drone.yml
Normal file
51
.drone.yml
Normal file
@ -0,0 +1,51 @@
|
||||
## Library CI pipeline — for npm package publishing
|
||||
## Configure project via service.yaml
|
||||
|
||||
kind: pipeline
|
||||
type: kubernetes
|
||||
name: ci
|
||||
|
||||
steps:
|
||||
- name: prepare
|
||||
image: alpine:3.19
|
||||
environment:
|
||||
GITEA_TOKEN:
|
||||
from_secret: GITEA_TOKEN
|
||||
commands:
|
||||
- apk add --no-cache git bash yq
|
||||
- git clone --depth 1 https://token:$GITEA_TOKEN@git.vigdorov.ru/vigdorov/ci-templates.git .ci
|
||||
- chmod +x .ci/scripts/*.sh
|
||||
- bash .ci/scripts/prepare.sh
|
||||
|
||||
- name: install
|
||||
image: node:22-alpine
|
||||
depends_on: [prepare]
|
||||
commands:
|
||||
- npm ci
|
||||
|
||||
- name: test
|
||||
image: node:22-alpine
|
||||
depends_on: [install]
|
||||
commands:
|
||||
- npm run lint
|
||||
- npm run test
|
||||
- npm run check
|
||||
|
||||
- name: build
|
||||
image: node:22-alpine
|
||||
depends_on: [test]
|
||||
commands:
|
||||
- npm run build
|
||||
|
||||
- name: publish
|
||||
image: node:22-alpine
|
||||
depends_on: [build]
|
||||
environment:
|
||||
GITEA_TOKEN:
|
||||
from_secret: GITEA_TOKEN
|
||||
commands:
|
||||
- bash .ci/scripts/publish-lib.sh
|
||||
|
||||
trigger:
|
||||
branch: [main]
|
||||
event: [push]
|
||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
node_modules/
|
||||
dist/
|
||||
coverage/
|
||||
.ci/
|
||||
*.tsbuildinfo
|
||||
1
.npmrc
Normal file
1
.npmrc
Normal file
@ -0,0 +1 @@
|
||||
@vigdorov:registry=https://git.vigdorov.ru/api/packages/vigdorov/npm/
|
||||
58
CLAUDE.md
Normal file
58
CLAUDE.md
Normal file
@ -0,0 +1,58 @@
|
||||
# dev-configs
|
||||
|
||||
Monorepo с общими конфигами для TypeScript-проектов. Публикуется как набор npm-пакетов в Gitea npm registry.
|
||||
|
||||
## Технологии
|
||||
|
||||
- **Monorepo:** npm workspaces
|
||||
- **Сборка:** TypeScript (tsc)
|
||||
- **Registry:** Gitea npm (`@vigdorov/` scope)
|
||||
- **CI:** Drone CI, тип `library`
|
||||
|
||||
## Пакеты
|
||||
|
||||
| Пакет | Тип | Описание |
|
||||
|-------|-----|----------|
|
||||
| `@vigdorov/prettier-config` | Статичный объект | Prettier конфиг |
|
||||
| `@vigdorov/eslint-config` | Функции-генераторы (base, react, node) | ESLint 9 flat config |
|
||||
| `@vigdorov/typescript-config` | JSON для extends (base.json, react.json) | TSConfig |
|
||||
| `@vigdorov/vite-config` | Функции-генераторы (spa, library) | Vite конфиг |
|
||||
| `@vigdorov/jest-config` | Функция-генератор (node/jsdom) | Jest + @swc/jest |
|
||||
| `@vigdorov/playwright-config` | Функция-генератор (CI-aware) | Playwright e2e |
|
||||
| `@vigdorov/knip-config` | Функция-генератор | Dead code detection |
|
||||
|
||||
## Структура
|
||||
|
||||
```
|
||||
packages/
|
||||
├── eslint/ → @vigdorov/eslint-config
|
||||
├── prettier/ → @vigdorov/prettier-config
|
||||
├── typescript/ → @vigdorov/typescript-config (без сборки, JSON-файлы)
|
||||
├── vite/ → @vigdorov/vite-config
|
||||
├── jest/ → @vigdorov/jest-config
|
||||
├── playwright/ → @vigdorov/playwright-config
|
||||
└── knip/ → @vigdorov/knip-config
|
||||
```
|
||||
|
||||
## Команды
|
||||
|
||||
```bash
|
||||
npm run build # Собрать все пакеты
|
||||
npm run test # Тесты всех пакетов
|
||||
npm run lint # Линтинг всех пакетов
|
||||
npm run check # Knip проверка всех пакетов
|
||||
|
||||
# Поднятие версии
|
||||
npm version patch -w packages/eslint
|
||||
npm version minor -w packages/vite
|
||||
```
|
||||
|
||||
## Версионирование
|
||||
|
||||
- Semver, независимое для каждого пакета
|
||||
- Разработка в feature-ветке → merge в main → поднять версию → push
|
||||
- CI публикует только пакеты с новой версией (сравнение с registry)
|
||||
|
||||
## Требования
|
||||
|
||||
Полные требования: [docs/requirements.md](docs/requirements.md)
|
||||
211
README.md
Normal file
211
README.md
Normal file
@ -0,0 +1,211 @@
|
||||
# @vigdorov/dev-configs
|
||||
|
||||
Набор общих конфигов для TypeScript-проектов. Единые правила линтинга, форматирования, сборки и тестирования.
|
||||
|
||||
## Установка
|
||||
|
||||
Добавь в `.npmrc` проекта:
|
||||
|
||||
```ini
|
||||
@vigdorov:registry=https://git.vigdorov.ru/api/packages/vigdorov/npm/
|
||||
```
|
||||
|
||||
Затем устанавливай нужные пакеты:
|
||||
|
||||
```bash
|
||||
npm i -D @vigdorov/prettier-config @vigdorov/eslint-config @vigdorov/typescript-config
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Пакеты
|
||||
|
||||
### @vigdorov/prettier-config
|
||||
|
||||
Единый стиль форматирования: 120 символов, 4 пробела, одинарные кавычки.
|
||||
|
||||
```js
|
||||
// prettier.config.js
|
||||
import config from '@vigdorov/prettier-config';
|
||||
export default config;
|
||||
```
|
||||
|
||||
### @vigdorov/eslint-config
|
||||
|
||||
ESLint 9 flat config. Три пресета: `base` (любой TS-проект), `react` (фронтенд), `node` (бэкенд).
|
||||
|
||||
```js
|
||||
// eslint.config.js — React-проект
|
||||
import {react} from '@vigdorov/eslint-config';
|
||||
export default react();
|
||||
```
|
||||
|
||||
```js
|
||||
// eslint.config.js — NestJS-проект
|
||||
import {node} from '@vigdorov/eslint-config';
|
||||
export default node();
|
||||
```
|
||||
|
||||
Кастомизация через callback:
|
||||
|
||||
```js
|
||||
import {react} from '@vigdorov/eslint-config';
|
||||
|
||||
export default react((configs) => [
|
||||
...configs,
|
||||
{
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
},
|
||||
},
|
||||
]);
|
||||
```
|
||||
|
||||
### @vigdorov/typescript-config
|
||||
|
||||
Базовые tsconfig для наследования.
|
||||
|
||||
```jsonc
|
||||
// tsconfig.json — React-проект
|
||||
{
|
||||
"extends": "@vigdorov/typescript-config/react",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
```
|
||||
|
||||
```jsonc
|
||||
// tsconfig.json — Node/NestJS-проект
|
||||
{
|
||||
"extends": "@vigdorov/typescript-config/base",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
```
|
||||
|
||||
### @vigdorov/vite-config
|
||||
|
||||
Конфиг Vite для SPA-приложений и библиотек.
|
||||
|
||||
```ts
|
||||
// vite.config.ts — SPA
|
||||
import {spa} from '@vigdorov/vite-config';
|
||||
|
||||
export default spa({
|
||||
port: 5176,
|
||||
aliases: {'@': 'src'},
|
||||
proxy: {'/api': 'http://localhost:3003'},
|
||||
});
|
||||
```
|
||||
|
||||
```ts
|
||||
// vite.config.ts — библиотека
|
||||
import {library} from '@vigdorov/vite-config';
|
||||
|
||||
export default library({
|
||||
entry: 'src/index.ts',
|
||||
name: 'my-lib',
|
||||
external: ['react', 'react-dom'],
|
||||
});
|
||||
```
|
||||
|
||||
| Параметр (spa) | По умолчанию | Описание |
|
||||
|----------------|-------------|----------|
|
||||
| `port` | `5173` | Порт dev-сервера |
|
||||
| `aliases` | — | Алиасы путей (`{'@': 'src'}`) |
|
||||
| `proxy` | — | Проксирование запросов |
|
||||
| `base` | `'/'` | Base URL |
|
||||
| `outDir` | `'dist'` | Папка сборки |
|
||||
|
||||
| Параметр (library) | По умолчанию | Описание |
|
||||
|--------------------|-------------|----------|
|
||||
| `entry` | — | Точка входа (обязательный) |
|
||||
| `name` | — | Имя библиотеки (обязательный) |
|
||||
| `aliases` | — | Алиасы путей |
|
||||
| `external` | `[]` | Зависимости, исключаемые из бандла |
|
||||
| `formats` | `['es', 'cjs']` | Форматы выходных файлов |
|
||||
|
||||
### @vigdorov/jest-config
|
||||
|
||||
Jest с быстрой трансформацией через SWC. Алиасы в едином формате с Vite.
|
||||
|
||||
```ts
|
||||
// jest.config.ts
|
||||
import {jestConfig} from '@vigdorov/jest-config';
|
||||
|
||||
export default jestConfig({
|
||||
environment: 'jsdom',
|
||||
aliases: {'@': 'src'},
|
||||
});
|
||||
```
|
||||
|
||||
| Параметр | По умолчанию | Описание |
|
||||
|----------|-------------|----------|
|
||||
| `environment` | `'node'` | `'node'` для бэкенда, `'jsdom'` для фронтенда |
|
||||
| `aliases` | — | Алиасы путей (формат как в Vite) |
|
||||
|
||||
> Папку `coverage/` нужно добавить в `.gitignore` проекта.
|
||||
|
||||
### @vigdorov/playwright-config
|
||||
|
||||
E2E-тесты в трёх браузерах: Chromium, Firefox, WebKit. Автоматически адаптируется к CI-окружению.
|
||||
|
||||
```ts
|
||||
// playwright.config.ts
|
||||
import {playwrightConfig} from '@vigdorov/playwright-config';
|
||||
|
||||
export default playwrightConfig({
|
||||
baseURL: 'http://localhost:5176',
|
||||
});
|
||||
```
|
||||
|
||||
| Параметр | По умолчанию | Описание |
|
||||
|----------|-------------|----------|
|
||||
| `baseURL` | — | URL приложения (обязательный) |
|
||||
| `testDir` | `'e2e'` | Папка с тестами |
|
||||
| `retries` | `0` (локально) / `2` (CI) | Количество ретраев |
|
||||
|
||||
### @vigdorov/knip-config
|
||||
|
||||
Поиск неиспользуемого кода: файлы, экспорты, зависимости. Завершается с ошибкой если находит проблемы — используй в check-скрипте для блокировки сборки.
|
||||
|
||||
```ts
|
||||
// knip.config.ts
|
||||
import {knipConfig} from '@vigdorov/knip-config';
|
||||
|
||||
export default knipConfig({
|
||||
entry: ['src/main.tsx'],
|
||||
});
|
||||
```
|
||||
|
||||
| Параметр | По умолчанию | Описание |
|
||||
|----------|-------------|----------|
|
||||
| `entry` | `['src/index.ts']` | Точки входа |
|
||||
| `project` | `['src/**/*.{ts,tsx,js,jsx}']` | Файлы проекта |
|
||||
| `ignore` | `['**/*.test.*', '**/*.spec.*', 'e2e/**', '**/*.d.ts']` | Игнорируемые паттерны |
|
||||
| `ignoreDependencies` | `[]` | Зависимости, которые не проверять |
|
||||
|
||||
---
|
||||
|
||||
## Рекомендуемые скрипты
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "npm run check && vite build",
|
||||
"check": "knip",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"format": "prettier --write src",
|
||||
"test": "jest",
|
||||
"e2e": "playwright test"
|
||||
}
|
||||
}
|
||||
```
|
||||
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
|
||||
```
|
||||
10026
package-lock.json
generated
Normal file
10026
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
13
package.json
Normal file
13
package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "dev-configs",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "npm run build -ws --if-present",
|
||||
"test": "npm run test -ws --if-present",
|
||||
"lint": "npm run lint -ws --if-present",
|
||||
"check": "npm run check -ws --if-present"
|
||||
}
|
||||
}
|
||||
36
packages/eslint/package.json
Normal file
36
packages/eslint/package.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "@vigdorov/eslint-config",
|
||||
"version": "1.0.0",
|
||||
"description": "Shared ESLint configuration with flat config",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": ["dist"],
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./base": "./dist/base.js",
|
||||
"./react": "./dist/react.js",
|
||||
"./node": "./dist/node.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": ">=9.0.0",
|
||||
"typescript": ">=5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"typescript-eslint": "^8.32.1",
|
||||
"@stylistic/eslint-plugin": "^4.2.0",
|
||||
"eslint-plugin-unused-imports": "^4.1.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^9.27.0",
|
||||
"typescript": "^5.8.3",
|
||||
"@types/eslint": "^9.6.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^5.2.0"
|
||||
}
|
||||
}
|
||||
30
packages/eslint/src/base.ts
Normal file
30
packages/eslint/src/base.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import tseslint from 'typescript-eslint';
|
||||
import stylistic from '@stylistic/eslint-plugin';
|
||||
import unusedImports from 'eslint-plugin-unused-imports';
|
||||
import {qualityRules, typescriptRules, unusedImportsRules, stylisticRules} from './rules.js';
|
||||
|
||||
type ConfigArray = ReturnType<typeof tseslint.config>;
|
||||
type ConfigOverride = (configs: ConfigArray) => ConfigArray;
|
||||
|
||||
export function base(override: ConfigOverride = (c) => c): ConfigArray {
|
||||
const configs = tseslint.config(
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
plugins: {
|
||||
'@stylistic': stylistic,
|
||||
'unused-imports': unusedImports,
|
||||
},
|
||||
rules: {
|
||||
...qualityRules,
|
||||
...typescriptRules,
|
||||
...unusedImportsRules,
|
||||
...stylisticRules,
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: ['dist/', 'node_modules/', 'coverage/'],
|
||||
},
|
||||
);
|
||||
|
||||
return override(configs);
|
||||
}
|
||||
3
packages/eslint/src/index.ts
Normal file
3
packages/eslint/src/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export {base} from './base.js';
|
||||
export {react} from './react.js';
|
||||
export {node} from './node.js';
|
||||
9
packages/eslint/src/node.ts
Normal file
9
packages/eslint/src/node.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import tseslint from 'typescript-eslint';
|
||||
import {base} from './base.js';
|
||||
|
||||
type ConfigArray = ReturnType<typeof tseslint.config>;
|
||||
type ConfigOverride = (configs: ConfigArray) => ConfigArray;
|
||||
|
||||
export function node(override: ConfigOverride = (c) => c): ConfigArray {
|
||||
return base(override);
|
||||
}
|
||||
33
packages/eslint/src/react.ts
Normal file
33
packages/eslint/src/react.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import tseslint from 'typescript-eslint';
|
||||
import reactPlugin from 'eslint-plugin-react';
|
||||
import reactHooksPlugin from 'eslint-plugin-react-hooks';
|
||||
import {base} from './base.js';
|
||||
import {reactRules, reactHooksRules} from './rules.js';
|
||||
|
||||
type ConfigArray = ReturnType<typeof tseslint.config>;
|
||||
type ConfigOverride = (configs: ConfigArray) => ConfigArray;
|
||||
|
||||
export function react(override: ConfigOverride = (c) => c): ConfigArray {
|
||||
const baseConfigs = base();
|
||||
|
||||
const configs = tseslint.config(
|
||||
...baseConfigs,
|
||||
{
|
||||
plugins: {
|
||||
'react': reactPlugin,
|
||||
'react-hooks': reactHooksPlugin,
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
...reactRules,
|
||||
...reactHooksRules,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return override(configs);
|
||||
}
|
||||
91
packages/eslint/src/rules.ts
Normal file
91
packages/eslint/src/rules.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import type {Linter} from 'eslint';
|
||||
|
||||
type Rules = Partial<Linter.RulesRecord>;
|
||||
|
||||
export const qualityRules: Rules = {
|
||||
'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',
|
||||
};
|
||||
|
||||
export const typescriptRules: Rules = {
|
||||
'@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',
|
||||
'no-shadow': 'off',
|
||||
};
|
||||
|
||||
export const unusedImportsRules: Rules = {
|
||||
'unused-imports/no-unused-imports': 'warn',
|
||||
'unused-imports/no-unused-vars': [
|
||||
'warn',
|
||||
{
|
||||
vars: 'all',
|
||||
varsIgnorePattern: '^_',
|
||||
args: 'after-used',
|
||||
argsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const stylisticRules: Rules = {
|
||||
'@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,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const reactRules: Rules = {
|
||||
'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'],
|
||||
};
|
||||
|
||||
export const reactHooksRules: Rules = {
|
||||
'react-hooks/exhaustive-deps': 'warn',
|
||||
};
|
||||
8
packages/eslint/tsconfig.json
Normal file
8
packages/eslint/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
23
packages/jest/package.json
Normal file
23
packages/jest/package.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "@vigdorov/jest-config",
|
||||
"version": "1.0.0",
|
||||
"description": "Shared Jest configuration",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": ["dist"],
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"jest": ">=29.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@swc/jest": "^0.2.38"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest": "^29.7.0",
|
||||
"@types/jest": "^29.5.14",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
28
packages/jest/src/index.ts
Normal file
28
packages/jest/src/index.ts
Normal file
@ -0,0 +1,28 @@
|
||||
export interface JestConfigOptions {
|
||||
environment?: 'node' | 'jsdom';
|
||||
aliases?: Record<string, string>;
|
||||
}
|
||||
|
||||
function resolveAliases(aliases: Record<string, string>): Record<string, string> {
|
||||
return Object.fromEntries(
|
||||
Object.entries(aliases).map(([key, aliasPath]) => [`^${key}(.*)$`, `<rootDir>/${aliasPath}$1`]),
|
||||
);
|
||||
}
|
||||
|
||||
export function jestConfig(options: JestConfigOptions = {}) {
|
||||
const {environment = 'node', aliases} = options;
|
||||
|
||||
return {
|
||||
testEnvironment: environment === 'jsdom' ? 'jsdom' : 'node',
|
||||
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: {
|
||||
'^.+\\.(t|j)sx?$': '@swc/jest',
|
||||
},
|
||||
...(aliases ? {moduleNameMapper: resolveAliases(aliases)} : {}),
|
||||
};
|
||||
}
|
||||
8
packages/jest/tsconfig.json
Normal file
8
packages/jest/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
19
packages/knip/package.json
Normal file
19
packages/knip/package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@vigdorov/knip-config",
|
||||
"version": "1.0.0",
|
||||
"description": "Shared Knip configuration",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": ["dist"],
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"knip": ">=5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"knip": "^5.51.0",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
22
packages/knip/src/index.ts
Normal file
22
packages/knip/src/index.ts
Normal file
@ -0,0 +1,22 @@
|
||||
export interface KnipConfigOptions {
|
||||
entry?: string[];
|
||||
project?: string[];
|
||||
ignore?: string[];
|
||||
ignoreDependencies?: string[];
|
||||
}
|
||||
|
||||
export function knipConfig(options: KnipConfigOptions = {}) {
|
||||
const {
|
||||
entry = ['src/index.ts'],
|
||||
project = ['src/**/*.{ts,tsx,js,jsx}'],
|
||||
ignore = [],
|
||||
ignoreDependencies = [],
|
||||
} = options;
|
||||
|
||||
return {
|
||||
entry,
|
||||
project,
|
||||
ignore: ['**/*.test.*', '**/*.spec.*', 'e2e/**', '**/*.d.ts', ...ignore],
|
||||
ignoreDependencies,
|
||||
};
|
||||
}
|
||||
8
packages/knip/tsconfig.json
Normal file
8
packages/knip/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
19
packages/playwright/package.json
Normal file
19
packages/playwright/package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@vigdorov/playwright-config",
|
||||
"version": "1.0.0",
|
||||
"description": "Shared Playwright configuration",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": ["dist"],
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@playwright/test": ">=1.40.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.52.0",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
29
packages/playwright/src/index.ts
Normal file
29
packages/playwright/src/index.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import type {PlaywrightTestConfig} from '@playwright/test';
|
||||
|
||||
export interface PlaywrightConfigOptions {
|
||||
baseURL: string;
|
||||
testDir?: string;
|
||||
retries?: number;
|
||||
}
|
||||
|
||||
export function playwrightConfig(options: PlaywrightConfigOptions): PlaywrightTestConfig {
|
||||
const {baseURL, testDir = 'e2e', retries} = options;
|
||||
const isCI = !!process.env.CI;
|
||||
|
||||
return {
|
||||
testDir,
|
||||
timeout: 30_000,
|
||||
retries: retries ?? (isCI ? 2 : 0),
|
||||
reporter: isCI ? 'html' : 'list',
|
||||
use: {
|
||||
baseURL,
|
||||
trace: 'on-first-retry',
|
||||
screenshot: 'only-on-failure',
|
||||
},
|
||||
projects: [
|
||||
{name: 'chromium', use: {browserName: 'chromium'}},
|
||||
{name: 'firefox', use: {browserName: 'firefox'}},
|
||||
{name: 'webkit', use: {browserName: 'webkit'}},
|
||||
],
|
||||
};
|
||||
}
|
||||
8
packages/playwright/tsconfig.json
Normal file
8
packages/playwright/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
19
packages/prettier/package.json
Normal file
19
packages/prettier/package.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@vigdorov/prettier-config",
|
||||
"version": "1.0.0",
|
||||
"description": "Shared Prettier configuration",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": ["dist"],
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prettier": ">=3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^3.5.3",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
15
packages/prettier/src/index.ts
Normal file
15
packages/prettier/src/index.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import type {Config} from 'prettier';
|
||||
|
||||
const config: Config = {
|
||||
printWidth: 120,
|
||||
tabWidth: 4,
|
||||
useTabs: false,
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
bracketSpacing: false,
|
||||
jsxSingleQuote: false,
|
||||
arrowParens: 'always',
|
||||
};
|
||||
|
||||
export default config;
|
||||
8
packages/prettier/tsconfig.json
Normal file
8
packages/prettier/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
22
packages/typescript/base.json
Normal file
22
packages/typescript/base.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
11
packages/typescript/package.json
Normal file
11
packages/typescript/package.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "@vigdorov/typescript-config",
|
||||
"version": "1.0.0",
|
||||
"description": "Shared TypeScript configurations",
|
||||
"type": "module",
|
||||
"files": ["base.json", "react.json"],
|
||||
"exports": {
|
||||
"./base": "./base.json",
|
||||
"./react": "./react.json"
|
||||
}
|
||||
}
|
||||
7
packages/typescript/react.json
Normal file
7
packages/typescript/react.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./base.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
||||
"jsx": "react-jsx"
|
||||
}
|
||||
}
|
||||
27
packages/vite/package.json
Normal file
27
packages/vite/package.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "@vigdorov/vite-config",
|
||||
"version": "1.0.0",
|
||||
"description": "Shared Vite configuration",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": ["dist"],
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./spa": "./dist/spa.js",
|
||||
"./library": "./dist/library.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vite": ">=6.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vitejs/plugin-react": "^4.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^6.3.5",
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
2
packages/vite/src/index.ts
Normal file
2
packages/vite/src/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export {spa, type SpaOptions} from './spa.js';
|
||||
export {library, type LibraryOptions} from './library.js';
|
||||
31
packages/vite/src/library.ts
Normal file
31
packages/vite/src/library.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import {defineConfig, type UserConfig} from 'vite';
|
||||
import {resolveAliases} from './utils.js';
|
||||
|
||||
export interface LibraryOptions {
|
||||
entry: string;
|
||||
name: string;
|
||||
aliases?: Record<string, string>;
|
||||
external?: string[];
|
||||
formats?: ('es' | 'cjs')[];
|
||||
}
|
||||
|
||||
export function library(options: LibraryOptions): UserConfig {
|
||||
const {entry, name, aliases, external = [], formats = ['es', 'cjs']} = options;
|
||||
|
||||
return defineConfig({
|
||||
resolve: {
|
||||
alias: resolveAliases(aliases),
|
||||
},
|
||||
build: {
|
||||
lib: {
|
||||
entry,
|
||||
name,
|
||||
formats,
|
||||
fileName: (format) => `index.${format === 'es' ? 'mjs' : 'cjs'}`,
|
||||
},
|
||||
rollupOptions: {
|
||||
external,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
30
packages/vite/src/spa.ts
Normal file
30
packages/vite/src/spa.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import react from '@vitejs/plugin-react';
|
||||
import {defineConfig, type UserConfig} from 'vite';
|
||||
import {resolveAliases} from './utils.js';
|
||||
|
||||
export interface SpaOptions {
|
||||
port?: number;
|
||||
aliases?: Record<string, string>;
|
||||
proxy?: Record<string, string>;
|
||||
base?: string;
|
||||
outDir?: string;
|
||||
}
|
||||
|
||||
export function spa(options: SpaOptions = {}): UserConfig {
|
||||
const {port = 5173, aliases, proxy, base = '/', outDir = 'dist'} = options;
|
||||
|
||||
return defineConfig({
|
||||
plugins: [react()],
|
||||
base,
|
||||
resolve: {
|
||||
alias: resolveAliases(aliases),
|
||||
},
|
||||
server: {
|
||||
port,
|
||||
proxy,
|
||||
},
|
||||
build: {
|
||||
outDir,
|
||||
},
|
||||
});
|
||||
}
|
||||
9
packages/vite/src/utils.ts
Normal file
9
packages/vite/src/utils.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import path from 'node:path';
|
||||
|
||||
export function resolveAliases(aliases: Record<string, string> | undefined): Record<string, string> {
|
||||
if (!aliases) return {};
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(aliases).map(([key, value]) => [key, path.resolve(process.cwd(), value)]),
|
||||
);
|
||||
}
|
||||
8
packages/vite/tsconfig.json
Normal file
8
packages/vite/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
7
service.yaml
Normal file
7
service.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
service:
|
||||
name: dev-configs
|
||||
type: library
|
||||
|
||||
library:
|
||||
scope: "@vigdorov"
|
||||
registry: "https://git.vigdorov.ru/api/packages/vigdorov/npm/"
|
||||
19
tsconfig.base.json
Normal file
19
tsconfig.base.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2022"],
|
||||
"moduleResolution": "bundler",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"resolveJsonModule": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"skipLibCheck": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user