Skip to content
This repository was archived by the owner on Sep 11, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions plugins/lang-js/.changes/260721-1302.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
bump: minor
section: Added
---
storybook: новий concern `ci` (ADR Кластер 5, CI-частина) — канонічний composite action `setup-playwright-chromium` (кеш Playwright-браузерів, лише chromium) і `.github/workflows/lint-storybook.yml` (швидкий PR-прогін `vitest --project=storybook`), гейтований `requires.capability: ci:github`
5 changes: 5 additions & 0 deletions plugins/lang-js/.changes/260721-1324.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
bump: patch
section: Fixed
---
storybook: viteFinal-фільтр стійкий до VueMacros-стека (Promise/масив-резолв, сімейний фільтр vite:*/vue-macros), vitest@^4 provider-factory (@vitest/browser-playwright) замість застарілого рядка `'playwright'`, flat-root layout у detectStoriesGlob (components без src/), точковий alias-мок одного модуля в mocking.mdc, і STORIES_RE false positive на `storybookTest({ configDir })` без явного include — усе за результатами пілота adopt-діагностики на nitra/components. Заодно governance package_json.rego: allowlist доповнено `@vitest/browser-playwright`.
4 changes: 2 additions & 2 deletions plugins/lang-js/rules/bun/package_json/package_json.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ Rego-пакет: `bun.package_json`
Gate виносить два класи deny:

1. **Заборонені top-level поля** — будь-яке поле з `package.json.deny.json` присутнє у файлі (навіть із порожнім значенням `{}`).
2. **devDependencies не з білого списку** — дозволені лише `@nitra/*`/`@7n/*` та root-only тестові peer/tools (`vitest`, `@vitest/coverage-v8`, `@vitest/browser`, `@stryker-mutator/vitest-runner`, `@stryker-mutator/core`, `@playwright/test`, `playwright`, `@storybook/addon-vitest`, `@7n/test`). Будь-який інший пакет → deny. CLI-тули, які `n-rules lint` спавнить через `bunx` (oxlint, jscpd, v8r, github-actionlint тощо), у root не пінуються — вони приїжджають як `dependencies` пакета `@7n/rules` (npm-module.mdc).
2. **devDependencies не з білого списку** — дозволені лише `@nitra/*`/`@7n/*` та root-only тестові peer/tools (`vitest`, `@vitest/coverage-v8`, `@vitest/browser`, `@vitest/browser-playwright`, `@stryker-mutator/vitest-runner`, `@stryker-mutator/core`, `@playwright/test`, `playwright`, `@storybook/addon-vitest`, `@7n/test`). Будь-який інший пакет → deny. CLI-тули, які `n-rules lint` спавнить через `bunx` (oxlint, jscpd, v8r, github-actionlint тощо), у root не пінуються — вони приїжджають як `dependencies` пакета `@7n/rules` (npm-module.mdc).

`@vitest/browser`/`playwright`/`@storybook/addon-vitest` — додані для named vitest project "storybook" (канон Storybook, кластер 5: browser-mode, лише chromium) — той самий root vitest.config, що й `unit`-проект; `@storybook/addon-vitest` постачає `storybookTest`-плагін для цього vitest-конфіга (канонічний template правила `storybook`). **Межа з `npm-module.mdc`**: Storybook-специфічні identity-пакети (`storybook`, `@storybook/vue3-vite`, `@storybook/vue3`, `msw`, `msw-storybook-addon`) у цей allowlist **не** додаються — вони живуть у `npm/package.json` консюмер-пакета (канон Storybook, кластер 7 Governance), бо `isStorybookRoot()` у `@7n/test` детектує Storybook-скоуп саме за тим файлом, не кореневим. `@storybook/addon-vitest` — виняток із цієї межі: він test-tooling (vitest-плагін), а не identity-маркер, тож root, як і решта vitest-peer'ів.
`@vitest/browser`/`@vitest/browser-playwright`/`playwright`/`@storybook/addon-vitest` — додані для named vitest project "storybook" (канон Storybook, кластер 5: browser-mode, лише chromium) — той самий root vitest.config, що й `unit`-проект; `@storybook/addon-vitest` постачає `storybookTest`-плагін для цього vitest-конфіга (канонічний template правила `storybook`). `@vitest/browser-playwright` — окремий npm-пакет: vitest@^4 прибрав рядкове API browser-провайдера (`provider: 'playwright'`), потрібна factory `import { playwright } from '@vitest/browser-playwright'` → `provider: playwright()` (звірено з реальним `bun.lock` пілотного консюмера — усі три `@vitest/*`/`playwright`-пакети встановлені поруч). **Межа з `npm-module.mdc`**: Storybook-специфічні identity-пакети (`storybook`, `@storybook/vue3-vite`, `@storybook/vue3`, `msw`, `msw-storybook-addon`) у цей allowlist **не** додаються — вони живуть у `npm/package.json` консюмер-пакета (канон Storybook, кластер 7 Governance), бо `isStorybookRoot()` у `@7n/test` детектує Storybook-скоуп саме за тим файлом, не кореневим. `@storybook/addon-vitest` — виняток із цієї межі: він test-tooling (vitest-плагін), а не identity-маркер, тож root, як і решта vitest-peer'ів.

Перевірки, що потребують FS або cross-file контексту (наприклад наявність `yarn.lock`), лишаються у JS-шарі.
24 changes: 15 additions & 9 deletions plugins/lang-js/rules/bun/package_json/package_json.rego
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
# - `devDependencies` лише `@nitra/*` + root-only тестові peer/tools для `@7n/test coverage`
# (правило `test` enabled завжди — див. `test/auto.md`; published workspace-и не мають
# `devDependencies` за `npm-module.mdc`)
# - `@vitest/browser`/`playwright`/`@storybook/addon-vitest` (browser-mode provider +
# `storybookTest`-плагін для named vitest project "storybook", лише chromium — канон
# Storybook кластер 5) теж root-only test peers: той самий vitest.config, що й
# `unit`-проект, живе в корені монорепо-споживача. Storybook-специфічні
# - `@vitest/browser`/`@vitest/browser-playwright`/`playwright`/`@storybook/addon-vitest`
# (browser-mode provider factory + `storybookTest`-плагін для named vitest project
# "storybook", лише chromium — канон Storybook кластер 5) теж root-only test peers:
# той самий vitest.config, що й `unit`-проект, живе в корені монорепо-споживача.
# Storybook-специфічні
# identity-пакети (`storybook`, `@storybook/vue3*`, `msw*`) НЕ сюди — вони живуть у
# `npm/package.json` (канон Storybook кластер 7, `npm-module.mdc`), бо
# `isStorybookRoot()` @7n/test читає саме той файл, не кореневий package.json.
Expand Down Expand Up @@ -59,11 +60,15 @@ deny contains msg if {
# @stryker-mutator/core — обов'язковий exact-pin peer vitest-runner@9+ (раніше тягнувся транзитивно)
# @7n/test — оркестратор `coverage` (npx @7n/test coverage); devDependency, щоб npx резолвив
# локально без мережевого fetch щоразу.
# @vitest/browser + playwright — провайдер browser-mode для named vitest project
# "storybook" (канон Storybook кластер 5: лише chromium, PR — швидкий
# --project=storybook). `playwright` (не `@playwright/test`) — сирий driver, який
# @vitest/browser використовує як provider; `@playwright/test` лишається окремо для
# змістовних E2E-сценаріїв (n-vue.mdc).
# @vitest/browser + @vitest/browser-playwright + playwright — провайдер browser-mode для
# named vitest project "storybook" (канон Storybook кластер 5: лише chromium, PR —
# швидкий --project=storybook). vitest@^4 прибрав рядкове API провайдера
# (`provider: 'playwright'`) — `@vitest/browser-playwright` постачає factory
# `import { playwright } from '@vitest/browser-playwright'` → `provider: playwright()`,
# окремий npm-пакет, не входить у `@vitest/browser` (звірено з реальним bun.lock пілота —
# усі три пакети встановлені поруч). `playwright` (не `@playwright/test`) — сирий driver,
# який `@vitest/browser-playwright` використовує як provider; `@playwright/test` лишається
# окремо для змістовних E2E-сценаріїв (n-vue.mdc).
# @storybook/addon-vitest — постачає `storybookTest` для vitest-плагіна в канонічному
# vitest.config named-проекту "storybook" (той самий канон Storybook кластер 5); версія
# з лінійки Storybook 9.x (узгоджена з `storybook`@9.1.10, запіненим у
Expand All @@ -73,6 +78,7 @@ allowed_root_test_deps := {
"vitest",
"@vitest/coverage-v8",
"@vitest/browser",
"@vitest/browser-playwright",
"@stryker-mutator/vitest-runner",
"@stryker-mutator/core",
"@playwright/test",
Expand Down
17 changes: 16 additions & 1 deletion plugins/lang-js/rules/bun/package_json/package_json_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ test_allow_7n_test_coverage_orchestrator if {
count(package_json.deny) == 0 with input as pkg with data.template as template_data
}

# ── allow: @vitest/browser + playwright — provider для named vitest project "storybook" ──
# ── allow: @vitest/browser + @vitest/browser-playwright + playwright — provider factory
# (vitest@^4) для named vitest project "storybook" ──

test_allow_vitest_browser_and_playwright_provider if {
pkg := json.patch(valid_pkg, [{
Expand All @@ -80,6 +81,20 @@ test_allow_vitest_browser_and_playwright_provider if {
count(package_json.deny) == 0 with input as pkg with data.template as template_data
}

test_allow_vitest_browser_playwright_provider_package if {
pkg := json.patch(valid_pkg, [{
"op": "replace",
"path": "/devDependencies",
"value": {
"@nitra/eslint-config": "^3.9.2",
"@vitest/browser": "^4.1.10",
"@vitest/browser-playwright": "^4.1.10",
"playwright": "^1.61.1",
},
}])
count(package_json.deny) == 0 with input as pkg with data.template as template_data
}

test_allow_storybook_addon_vitest_root_tooling if {
pkg := json.patch(valid_pkg, [{
"op": "replace",
Expand Down
3 changes: 1 addition & 2 deletions plugins/lang-js/rules/storybook/adopt/docs/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ type: JS Module
title: main.mjs
resource: plugins/lang-js/rules/storybook/adopt/main.mjs
docgen:
crc: 640409c7
crc: c8e6e421
model: openai-codex/gpt-5.4-mini
tier: cloud-min
score: 90
issues: anchor-miss:(mocking.mdc),anchor-miss:(vitest-config.mdc),judge:inaccurate:0.99
judgeModel: openai-codex/gpt-5.4-mini
Expand Down
7 changes: 5 additions & 2 deletions plugins/lang-js/rules/storybook/adopt/main.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ import {
classifyProjects,
findProperty,
findTestObject,
hasStoriesMarker,
parseModule,
PROVIDER_FACTORY_RE,
resolveVitestConfigPath,
STORIES_RE,
strykerConfigPathFor
} from '../vitest-config/main.mjs'

Expand Down Expand Up @@ -167,7 +168,9 @@ function diagnoseVitestProjectsSection(entry) {
const missingHints = []
if (!CHROMIUM_RE.test(storybookSlice)) missingHints.push('chromium-інстанс')
if (!BROWSER_KEY_RE.test(storybookSlice)) missingHints.push('browser-mode')
if (!STORIES_RE.test(storybookSlice)) missingHints.push('stories-glob')
if (!hasStoriesMarker(storybookSlice)) missingHints.push('stories-джерело (include або storybookTest({ configDir }))')
if (!PROVIDER_FACTORY_RE.test(storybookSlice))
missingHints.push('provider-factory (playwright() з @vitest/browser-playwright)')
if (missingHints.length > 0) {
return { name, file, status: STATUS.DIFFER, detail: `storybook-project без: ${missingHints.join(', ')}` }
}
Expand Down
2 changes: 1 addition & 1 deletion plugins/lang-js/rules/storybook/adopt/tests/adopt.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ async function writeCanonicalStorybookSetup(root, rootDir) {
' test: {',
" name: 'storybook',",
" include: ['src/components/**/*.stories.@(js|ts)'],",
" browser: { enabled: true, headless: true, provider: 'playwright', instances: [{ browser: 'chromium' }] }",
" browser: { enabled: true, headless: true, provider: playwright(), instances: [{ browser: 'chromium' }] }",
' }',
' }',
' ]',
Expand Down
15 changes: 15 additions & 0 deletions plugins/lang-js/rules/storybook/ci/concern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
"fixability": "config",
"requires": { "capability": "ci:github" },
"lint": {
"scope": "full",
"glob": [
"package.json",
"**/*.vue",
".storybook/**",
".github/actions/setup-playwright-chromium/action.yml",
".github/workflows/lint-storybook.yml"
]
}
}
39 changes: 39 additions & 0 deletions plugins/lang-js/rules/storybook/ci/docs/fix-ci.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
type: JS Module
title: fix-ci.mjs
resource: plugins/lang-js/rules/storybook/ci/fix-ci.mjs
docgen:
crc: 877b9feb
model: openai-codex/gpt-5.4-mini
tier: cloud-min
score: 90
judgeModel: openai-codex/gpt-5.4-mini
---

## Огляд

Порожній шаблон `storybook/ci`, з якого відтворюються репо-рівневі CI-артефакти: `setup-playwright-chromium` як verbatim-копія composite action для Playwright Chromium і `.github/workflows/lint-storybook.yml` із матрицею `strategy.matrix.package`, згенерованою з фактичного списку пакетів через `collectInScopeVuePackages`. Публічні anchors: `TEMPLATE_DIR`, `renderPlaywrightAction`, `renderPackageDirsYaml`, `renderStorybookWorkflow`, `patterns`.

## Поведінка

- **TEMPLATE_DIR** — вказує на каталог `template/` цього concern-а як джерело канонічних CI-шаблонів.
- **renderPlaywrightAction** — читає канонічний composite action `setup-playwright-chromium` із шаблону без змін.
- **renderPackageDirsYaml** — перетворює список коренів пакетів у YAML-рядки для матриці Storybook CI.
- **renderStorybookWorkflow** — збирає канонічний `.github/workflows/lint-storybook.yml`, підставляючи в нього матрицю пакетів зі скоупу репозиторію.
- **patterns** — описує T0-правила автофіксу для відновлення відсутніх CI-файлів цього concern-а.

## Публічний API

- TEMPLATE_DIR — Каталог `template/` цього concern-а.
- renderPlaywrightAction — Вміст канонічного composite action `setup-playwright-chromium` — verbatim з template.
- renderPackageDirsYaml — Рендерить YAML-фрагмент `matrix.package` списку — по одному `- <rootDir>` на рядок, з тим
самим відступом, що й токен-рядок у шаблоні (10 пробілів — рівень елемента списку під
`strategy.matrix.package:`). `rootDir === '.'` (корінь монорепо) лишається `.` — валідний
`working-directory` для GitHub Actions.
- renderStorybookWorkflow — Вміст канонічного `.github/workflows/lint-storybook.yml` — template з підставленою
матрицею пакетів у скоупі.
- patterns — повертає набір шаблонів, за якими далі шукають або зіставляють потрібні значення.

## Гарантії поведінки

- (специфічних машинно-виведених гарантій немає)
10 changes: 10 additions & 0 deletions plugins/lang-js/rules/storybook/ci/docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
type: Directory Index
title: plugins/lang-js/rules/storybook/ci
resource: plugins/lang-js/rules/storybook/ci/
---

| Файл | Тип |
| ----------------------- | --------- |
| [fix-ci.mjs](fix-ci.md) | JS Module |
| [main.mjs](main.md) | JS Module |
Loading
Loading