From 70a8678743f490ad80419c207e36c1799882a2d2 Mon Sep 17 00:00:00 2001 From: robonen Date: Sun, 7 Jun 2026 16:28:20 +0700 Subject: [PATCH] chore: workspace eslint catalog, typescript dev dep, monorepo skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - pnpm-workspace catalog: replace oxlint with eslint ^10.4.1. - Add typescript dev dep (root) so vitest typecheck (stdlib) resolves tsc. - Root vitest projects: configs/oxlint → configs/eslint. - Update monorepo skill + add component-test-auditor agent. --- .../agents/component-test-auditor.agent.md | 71 +++++++++++++++++++ .github/skills/monorepo/SKILL.md | 70 ++++++++---------- package.json | 1 + pnpm-workspace.yaml | 10 +-- vitest.config.ts | 2 +- 5 files changed, 109 insertions(+), 45 deletions(-) create mode 100644 .github/agents/component-test-auditor.agent.md diff --git a/.github/agents/component-test-auditor.agent.md b/.github/agents/component-test-auditor.agent.md new file mode 100644 index 0000000..2851570 --- /dev/null +++ b/.github/agents/component-test-auditor.agent.md @@ -0,0 +1,71 @@ +--- +description: "Audit and complete Vue/web component test suites. Use when the user asks to check test coverage, find missing test cases, fill gaps in component tests, verify a11y of a component, audit *.test.ts / a11y.test.ts files, or ensure a component is fully covered by vitest. Triggers: \"check tests\", \"complete tests\", \"test coverage\", \"missing test cases\", \"audit a11y\", \"проверь тесты\", \"допиши тесты\", \"покрытие тестов\"." +name: "Component Test Auditor" +tools: [read, search, edit, execute, todo] +model: "Claude Sonnet 4.5 (copilot)" +argument-hint: "Path to a component or test file to audit" +user-invocable: true +--- + +You are a specialist in Vue/web component test completeness auditing. Your single job: given a component, determine whether its test suite fully covers behavior, props, emits, slots, edge cases, and accessibility — then fill the gaps with additional vitest specs. + +## Constraints + +- DO NOT modify production component source. ONLY add or refine test files. +- DO NOT introduce new test frameworks. Use the existing `vitest` + `@vue/test-utils` setup found in the repo. +- DO NOT create redundant tests for behavior already covered. Each new spec must close a concrete gap. +- DO NOT skip running the suite — every change must be verified with vitest before reporting back. +- ONLY audit one component (or a small explicit list) per invocation. Stay focused. + +## Required Skills + +Always load these skills via `read_file` before acting: + +1. `/Users/robonen/.agents/skills/vue-testing-best-practices/SKILL.md` — patterns for Vue component tests. +2. `/Users/robonen/.agents/skills/vitest/SKILL.md` — vitest API, mocking, coverage. +3. `/Users/robonen/.agents/skills/accessibility-a11y/SKILL.md` — WCAG checks, ARIA, keyboard nav. Load whenever the component has interactive behavior, focus management, ARIA attributes, or a sibling `a11y.test.ts` file exists. +4. `/Users/robonen/Projects/tools/.github/skills/monorepo/SKILL.md` — for repo-specific commands (`pnpm`, test scoping, workspace layout). + +## Approach + +1. **Locate the component.** Resolve the target `.vue` / `.ts` source and its `__test__/` folder. Inspect sibling `*.test.ts`, `a11y.test.ts`, and any AGENTS.md / README.md for the package. +2. **Inventory the surface.** From the component source, enumerate: props (with defaults & types), emits, exposed methods, slots (named + scoped), `v-model`s, internal state machines, conditional branches, lifecycle effects, DOM/ARIA attributes, keyboard handlers. +3. **Inventory existing coverage.** Read all related test files and map each `it(...)` to the surface item it covers. Build a gap table. +4. **Decide on a11y scope.** If the component is interactive (focusable, ARIA roles, keyboard, form-related) → run the a11y skill checklist and ensure an `a11y.test.ts` exists with: role, accessible name, keyboard interaction, focus order, `aria-*` invariants, and (where applicable) `axe`-style assertions consistent with neighboring components. +5. **Write the missing specs.** Match the file/folder conventions used by neighbors (e.g. `src//__test__/.test.ts` + `a11y.test.ts`). Mirror style (mount helpers, fixtures, naming). Keep specs deterministic and isolated. +6. **Run vitest.** Use `runTests` when available, otherwise `pnpm --filter test -- `. Iterate until green. If a failure reveals a real component bug, STOP and report it — do not silently fix production code. +7. **Optionally measure coverage.** If coverage is requested or unclear, run vitest in coverage mode scoped to the component file and report uncovered lines/branches. + +## Output Format + +Return a concise markdown report: + +``` +### Component: () + +**Surface inventory** +- props: ... +- emits: ... +- slots: ... +- interactive/a11y: yes/no — + +**Existing coverage** +- : N specs — covers X, Y +- a11y.test.ts: present/absent + +**Gaps closed** +- + +- + + +**Files changed** +- (+N specs) + +**Verification** +- vitest: PASS (M tests) | coverage: L% lines / B% branches +- a11y skill applied: yes/no + +**Follow-ups (not done)** +- +``` + +If no gaps exist, say so explicitly and do not touch any files. diff --git a/.github/skills/monorepo/SKILL.md b/.github/skills/monorepo/SKILL.md index fc10fe0..3b5c2ff 100644 --- a/.github/skills/monorepo/SKILL.md +++ b/.github/skills/monorepo/SKILL.md @@ -1,6 +1,6 @@ --- name: monorepo -description: "Manage the @robonen/tools monorepo. Use when: installing dependencies, creating new packages, linting, building, testing, publishing, or scaffolding workspace packages. Covers pnpm catalogs, tsdown builds, oxlint presets, vitest projects, JSR/NPM publishing." +description: "Manage the @robonen/tools monorepo. Use when: installing dependencies, creating new packages, linting, building, testing, publishing, or scaffolding workspace packages. Covers pnpm catalogs, tsdown builds, eslint presets, vitest projects, JSR/NPM publishing." --- # Monorepo Management @@ -13,7 +13,7 @@ This is a pnpm workspace monorepo (`@robonen/tools`) with shared configs, strict | Directory | Purpose | Examples | |-----------|---------|---------| -| `configs/*` | Shared tooling configs | `oxlint`, `tsconfig`, `tsdown` | +| `configs/*` | Shared tooling configs | `eslint`, `tsconfig`, `tsdown` | | `core/*` | Platform-agnostic TS libraries | `stdlib`, `platform`, `encoding` | | `vue/*` | Vue 3 packages | `primitives`, `toolkit` | | `docs` | Nuxt 4 documentation site | — | @@ -35,7 +35,7 @@ pnpm -C add -D Examples: ```bash -pnpm -C core/stdlib add -D oxlint +pnpm -C core/stdlib add -D eslint pnpm -C vue/primitives add vue ``` @@ -47,7 +47,7 @@ Versions shared across multiple packages MUST use the pnpm catalog system. The c catalog: vitest: ^4.0.18 tsdown: ^0.21.0 - oxlint: ^1.2.0 + eslint: ^10.4.1 vue: ^3.5.28 # ... etc ``` @@ -57,7 +57,7 @@ In `package.json`, reference catalog versions with the `catalog:` protocol: { "devDependencies": { "vitest": "catalog:", - "oxlint": "catalog:" + "eslint": "catalog:" } } ``` @@ -72,7 +72,7 @@ Reference sibling packages with the workspace protocol: ```json { "devDependencies": { - "@robonen/oxlint": "workspace:*", + "@robonen/eslint": "workspace:*", "@robonen/tsconfig": "workspace:*", "@robonen/tsdown": "workspace:*" } @@ -87,7 +87,7 @@ Always run `pnpm install` at the root after editing `pnpm-workspace.yaml` or any ## Creating a New Package -> **Note:** The existing `bin/cli.ts` (`pnpm create`) is outdated — it generates Vite configs instead of tsdown and lacks oxlint/vitest setup. Follow the manual steps below instead. +> **Note:** The existing `bin/cli.ts` (`pnpm create`) is outdated — it generates Vite configs instead of tsdown and lacks eslint/vitest setup. Follow the manual steps below instead. ### 1. Create the directory @@ -116,18 +116,17 @@ Choose the correct parent based on package type: } }, "scripts": { - "lint:check": "oxlint -c oxlint.config.ts", - "lint:fix": "oxlint -c oxlint.config.ts --fix", + "lint:check": "eslint .", + "lint:fix": "eslint . --fix", "test": "vitest run", "dev": "vitest dev", "build": "tsdown" }, "devDependencies": { - "@robonen/oxlint": "workspace:*", + "@robonen/eslint": "workspace:*", "@robonen/tsconfig": "workspace:*", "@robonen/tsdown": "workspace:*", - "@stylistic/eslint-plugin": "catalog:", - "oxlint": "catalog:", + "eslint": "catalog:", "tsdown": "catalog:" } } @@ -139,7 +138,6 @@ For Vue packages, also add: "dependencies": { "vue": "catalog:", "@vue/shared": "catalog:" - "@stylistic/eslint-plugin": "catalog:", }, "devDependencies": { "@vue/test-utils": "catalog:" @@ -215,16 +213,17 @@ export default defineConfig({ }); ``` -### 5. Create `oxlint.config.ts` +### 5. Create `eslint.config.ts` Standard (node packages): ```typescript -import { defineConfig } from 'oxlint'; -import { compose, base, typescript, imports, stylistic } from '@robonen/oxlint'; +import { base, compose, imports, stylistic, typescript } from '@robonen/eslint'; -export default defineConfig(compose(base, typescript, imports, stylistic)); +export default compose(base, typescript, imports, stylistic); ``` +> ESLint auto-discovers `eslint.config.ts` (loaded via `jiti`, which `@robonen/eslint` depends on). No `defineConfig` wrapper is needed — `compose()` returns the flat-config array directly. + ### 6. Create `vitest.config.ts` Node package: @@ -292,7 +291,7 @@ pnpm -C test ## Linting -Uses **oxlint** (not ESLint) with composable presets from `@robonen/oxlint`. +Uses **ESLint** (flat config) with composable presets from `@robonen/eslint`. ### Run linting @@ -314,38 +313,29 @@ pnpm lint:fix | Preset | Purpose | |--------|---------| -| `base` | ESLint core + Oxc + Unicorn rules | -| `typescript` | TypeScript rules (via overrides on `*.ts` files) | -| `imports` | Import ordering, cycles, duplicates | +| `base` | ESLint core + Unicorn rules + global ignores | +| `typescript` | TypeScript rules (via `typescript-eslint`, on `*.ts` + `*.vue`) | +| `imports` | Import ordering, cycles, duplicates (`eslint-plugin-import-x`) | | `stylistic` | Code style via `@stylistic/eslint-plugin` | -| `vue` | Vue 3 Composition API rules | -| `vitest` | Test file rules | -| `node` | Node.js-specific rules | +| `vue` | Vue 3 Composition API rules (`eslint-plugin-vue`) | +| `vitest` | Test file rules (`@vitest/eslint-plugin`) | +| `node` | Node.js-specific rules (`eslint-plugin-n`) | -Compose presets in `oxlint.config.ts`: +Compose presets in `eslint.config.ts`: ```typescript -import { defineConfig } from 'oxlint'; -import { compose, base, typescript, imports } from '@robonen/oxlint'; +import { base, compose, imports, typescript } from '@robonen/eslint'; -export default defineConfig(compose(base, typescript, imports)); +export default compose(base, typescript, imports); +``` **Recommended:** Include `stylistic` preset for code formatting: ```typescript -import { defineConfig } from 'oxlint'; -import { compose, base, typescript, imports, stylistic } from '@robonen/oxlint'; +import { base, compose, imports, stylistic, typescript } from '@robonen/eslint'; -export default defineConfig(compose(base, typescript, imports, stylistic)); +export default compose(base, typescript, imports, stylistic); ``` -When using `stylistic`, add `@stylistic/eslint-plugin` to devDependencies: -```json -{ - "devDependencies": { - "@stylistic/eslint-plugin": "catalog:" - } -} -``` -``` +All plugins (including `@stylistic/eslint-plugin`) ship as dependencies of `@robonen/eslint`, so packages only need `@robonen/eslint` + `eslint` in devDependencies. ## Building diff --git a/package.json b/package.json index 10de0fc..f32a68b 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "jiti": "^2.6.1", "jsdom": "catalog:", "scule": "^1.3.0", + "typescript": "^5.9.3", "vitest": "catalog:" }, "scripts": { diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8955f9a..d3b63f1 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,19 +3,21 @@ packages: - core/* - infra/* - vue/* + - vue/*/playground - docs catalog: '@stylistic/eslint-plugin': ^5.10.0 - '@vitest/coverage-v8': ^4.0.18 - '@vitest/ui': ^4.0.18 + '@vitest/browser': ^4.1.4 + '@vitest/coverage-v8': ^4.1.4 + '@vitest/ui': ^4.1.4 '@vue/shared': ^3.5.29 '@vue/test-utils': ^2.4.6 + eslint: ^10.4.1 jsdom: ^28.1.0 nuxt: ^4.3.1 - oxlint: ^1.51.0 tsdown: ^0.21.0 - vitest: ^4.0.18 + vitest: ^4.1.4 vue: ^3.5.29 ignoredBuiltDependencies: diff --git a/vitest.config.ts b/vitest.config.ts index f233d49..3d2f565 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -3,7 +3,7 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ test: { projects: [ - 'configs/oxlint/vitest.config.ts', + 'configs/eslint/vitest.config.ts', 'core/fetch/vitest.config.ts', 'core/stdlib/vitest.config.ts', 'core/platform/vitest.config.ts',