@7n/rules-lang-js 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/package.json +1 -1
- package/rules/bun/package_json/package_json.mdc +2 -2
- package/rules/bun/package_json/package_json.rego +26 -10
- package/rules/npm-module/npm_package_json/npm_package_json.mdc +1 -1
- package/rules/npm-module/npm_package_json/npm_package_json.rego +14 -5
- package/rules/storybook/adopt/docs/main.md +1 -2
- package/rules/storybook/adopt/main.mjs +37 -5
- package/rules/storybook/ci/concern.json +15 -0
- package/rules/storybook/ci/docs/fix-ci.md +39 -0
- package/rules/storybook/ci/docs/index.md +10 -0
- package/rules/storybook/ci/docs/main.md +50 -0
- package/rules/storybook/ci/fix-ci.mjs +105 -0
- package/rules/storybook/ci/main.mjs +112 -0
- package/rules/storybook/ci/template/lint-storybook.yml.snippet.yml +41 -0
- package/rules/storybook/ci/template/setup-playwright-chromium.action.yml +34 -0
- package/rules/storybook/hygiene/concern.json +1 -1
- package/rules/storybook/main.json +1 -1
- package/rules/storybook/main.mdc +24 -3
- package/rules/storybook/mocking/mocking.mdc +24 -0
- package/rules/storybook/scaffold/concern.json +1 -1
- package/rules/storybook/scaffold/docs/fix-scaffold.md +1 -1
- package/rules/storybook/scaffold/docs/main.md +1 -1
- package/rules/storybook/scaffold/fix-scaffold.mjs +50 -0
- package/rules/storybook/scaffold/main.mjs +68 -6
- package/rules/storybook/scaffold/template/docs/empty-vite.config.md +28 -0
- package/rules/storybook/scaffold/template/docs/index.md +5 -4
- package/rules/storybook/scaffold/template/docs/main.md +1 -2
- package/rules/storybook/scaffold/template/docs/preview.md +1 -2
- package/rules/storybook/scaffold/template/empty-vite.config.js +18 -0
- package/rules/storybook/scaffold/template/main.js +76 -6
- package/rules/storybook/scaffold/template/preview.js +13 -5
- package/rules/storybook/vitest-config/concern.json +1 -1
- package/rules/storybook/vitest-config/docs/fix-vitest-config.md +1 -2
- package/rules/storybook/vitest-config/docs/main.md +1 -2
- package/rules/storybook/vitest-config/fix-vitest-config.mjs +22 -6
- package/rules/storybook/vitest-config/main.mjs +33 -2
- package/rules/storybook/vitest-config/template/docs/storybook-project-entry.md +1 -2
- package/rules/storybook/vitest-config/template/docs/vitest.config.baseline.md +1 -2
- package/rules/storybook/vitest-config/template/storybook-project-entry.js +6 -2
- package/rules/storybook/vitest-config/template/vitest.config.baseline.mjs +2 -1
- package/rules/storybook/vitest-config/vitest-config.mdc +7 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.12.0] - 2026-07-21
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- 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`
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- 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`.
|
|
12
|
+
- canon Storybook: viteConfigPath-обхід (empty-vite.config.js), валідний iconSet-імпорт, mswLoader замість mswDecorator, повний .storybook/**-glob у CI/lint, вирівняні governance-піни (storybook ^10.5.3, root Vite build-tooling deps), knip-виключення для .storybook-артефактів
|
|
13
|
+
|
|
14
|
+
## [0.11.0] - 2026-07-21
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- 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`
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- 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`.
|
|
23
|
+
|
|
3
24
|
## [0.10.0] - 2026-07-21
|
|
4
25
|
|
|
5
26
|
### Added
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@7n/rules-lang-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Плагін @7n/rules: JS/npm/bun-екосистема — lint-правила (js/bun/vue/js-run/npm-module/db), taze-провайдер (package.json, bunx taze) і doc-files-екстрактори (oxc AST)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"javascript",
|
|
@@ -9,8 +9,8 @@ Rego-пакет: `bun.package_json`
|
|
|
9
9
|
Gate виносить два класи deny:
|
|
10
10
|
|
|
11
11
|
1. **Заборонені top-level поля** — будь-яке поле з `package.json.deny.json` присутнє у файлі (навіть із порожнім значенням `{}`).
|
|
12
|
-
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).
|
|
12
|
+
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).
|
|
13
13
|
|
|
14
|
-
`@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'ів.
|
|
14
|
+
`@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'ів.
|
|
15
15
|
|
|
16
16
|
Перевірки, що потребують FS або cross-file контексту (наприклад наявність `yarn.lock`), лишаються у JS-шарі.
|
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
# - `devDependencies` лише `@nitra/*` + root-only тестові peer/tools для `@7n/test coverage`
|
|
9
9
|
# (правило `test` enabled завжди — див. `test/auto.md`; published workspace-и не мають
|
|
10
10
|
# `devDependencies` за `npm-module.mdc`)
|
|
11
|
-
# - `@vitest/browser`/`playwright`/`@storybook/addon-vitest`
|
|
12
|
-
# `storybookTest`-плагін для named vitest project
|
|
13
|
-
# Storybook кластер 5) теж root-only test peers:
|
|
14
|
-
# `unit`-проект, живе в корені монорепо-споживача.
|
|
11
|
+
# - `@vitest/browser`/`@vitest/browser-playwright`/`playwright`/`@storybook/addon-vitest`
|
|
12
|
+
# (browser-mode provider factory + `storybookTest`-плагін для named vitest project
|
|
13
|
+
# "storybook", лише chromium — канон Storybook кластер 5) теж root-only test peers:
|
|
14
|
+
# той самий vitest.config, що й `unit`-проект, живе в корені монорепо-споживача.
|
|
15
|
+
# Storybook-специфічні
|
|
15
16
|
# identity-пакети (`storybook`, `@storybook/vue3*`, `msw*`) НЕ сюди — вони живуть у
|
|
16
17
|
# `npm/package.json` (канон Storybook кластер 7, `npm-module.mdc`), бо
|
|
17
18
|
# `isStorybookRoot()` @7n/test читає саме той файл, не кореневий package.json.
|
|
@@ -59,25 +60,40 @@ deny contains msg if {
|
|
|
59
60
|
# @stryker-mutator/core — обов'язковий exact-pin peer vitest-runner@9+ (раніше тягнувся транзитивно)
|
|
60
61
|
# @7n/test — оркестратор `coverage` (npx @7n/test coverage); devDependency, щоб npx резолвив
|
|
61
62
|
# локально без мережевого fetch щоразу.
|
|
62
|
-
# @vitest/browser + playwright — провайдер browser-mode для
|
|
63
|
-
# "storybook" (канон Storybook кластер 5: лише chromium, PR —
|
|
64
|
-
# --project=storybook).
|
|
65
|
-
#
|
|
66
|
-
#
|
|
63
|
+
# @vitest/browser + @vitest/browser-playwright + playwright — провайдер browser-mode для
|
|
64
|
+
# named vitest project "storybook" (канон Storybook кластер 5: лише chromium, PR —
|
|
65
|
+
# швидкий --project=storybook). vitest@^4 прибрав рядкове API провайдера
|
|
66
|
+
# (`provider: 'playwright'`) — `@vitest/browser-playwright` постачає factory
|
|
67
|
+
# `import { playwright } from '@vitest/browser-playwright'` → `provider: playwright()`,
|
|
68
|
+
# окремий npm-пакет, не входить у `@vitest/browser` (звірено з реальним bun.lock пілота —
|
|
69
|
+
# усі три пакети встановлені поруч). `playwright` (не `@playwright/test`) — сирий driver,
|
|
70
|
+
# який `@vitest/browser-playwright` використовує як provider; `@playwright/test` лишається
|
|
71
|
+
# окремо для змістовних E2E-сценаріїв (n-vue.mdc).
|
|
67
72
|
# @storybook/addon-vitest — постачає `storybookTest` для vitest-плагіна в канонічному
|
|
68
73
|
# vitest.config named-проекту "storybook" (той самий канон Storybook кластер 5); версія
|
|
69
|
-
# з лінійки Storybook
|
|
74
|
+
# з лінійки Storybook 10.x (узгоджена з `storybook`@^10.5.3, запіненим у
|
|
70
75
|
# npm_package_json.rego) — allowlist тут за іменем, точний пінінг версії root-tooling
|
|
71
76
|
# не робимо (на відміну від Storybook-identity-пакетів у npm/package.json).
|
|
77
|
+
# @storybook/builder-vite/@vitejs/plugin-vue/@quasar/vite-plugin — root-only build-tooling
|
|
78
|
+
# для канонічного `.storybook/main.js` (storybook.mdc scaffold: `core.builder`,
|
|
79
|
+
# `viteFinal`-плагіни `vue()`/`quasar()`); @quasar/extras — постачає material-icons
|
|
80
|
+
# CSS/fallback-набір для `.storybook/preview.js` (iconMapFn). Усі чотири звірено з
|
|
81
|
+
# реальним root package.json пілотного консюмера (components) — раніше були відсутні
|
|
82
|
+
# в allowlist, хоча канонічний скафолд Storybook уже на них покладався (реальний gap).
|
|
72
83
|
allowed_root_test_deps := {
|
|
73
84
|
"vitest",
|
|
74
85
|
"@vitest/coverage-v8",
|
|
75
86
|
"@vitest/browser",
|
|
87
|
+
"@vitest/browser-playwright",
|
|
76
88
|
"@stryker-mutator/vitest-runner",
|
|
77
89
|
"@stryker-mutator/core",
|
|
78
90
|
"@playwright/test",
|
|
79
91
|
"playwright",
|
|
80
92
|
"@storybook/addon-vitest",
|
|
93
|
+
"@storybook/builder-vite",
|
|
94
|
+
"@vitejs/plugin-vue",
|
|
95
|
+
"@quasar/vite-plugin",
|
|
96
|
+
"@quasar/extras",
|
|
81
97
|
"@7n/test",
|
|
82
98
|
}
|
|
83
99
|
|
|
@@ -62,7 +62,7 @@ FS-перевірки (наявність файлу зі шляху `types`, с
|
|
|
62
62
|
✓ Правильно — канонічний Storybook-devDep із зафіксованою версією (канон Storybook):
|
|
63
63
|
|
|
64
64
|
```json
|
|
65
|
-
{ "devDependencies": { "storybook": "
|
|
65
|
+
{ "devDependencies": { "storybook": "^10.5.3" } }
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
✗ Неправильно — Storybook-devDep присутній, але версія не збігається з каноном:
|
|
@@ -41,12 +41,21 @@ storybook_version_template := concat(" ", [
|
|
|
41
41
|
])
|
|
42
42
|
|
|
43
43
|
# Канонічні Storybook-devDeps (isStorybookRoot()-маркери, канон Storybook кластер 7):
|
|
44
|
-
#
|
|
45
|
-
#
|
|
44
|
+
# зафіксований точний РЯДОК версії, як він має бути записаний у package.json (включно з
|
|
45
|
+
# `^`, якщо канон його використовує) — єдине дозволене значення для кожного пакета.
|
|
46
|
+
# Оновлення — ручна правка цієї map, після емпіричної верифікації (звірка з реальним
|
|
47
|
+
# консюмером, що пройшов повний прогін storybook build + тести на цих версіях).
|
|
48
|
+
#
|
|
49
|
+
# storybook/@storybook/vue3-vite піднято з 9.1.10 (exact) на ^10.5.3 — пілотний консюмер
|
|
50
|
+
# (components/npm/package.json) реально пройшов storybook build + 58/58 тестів саме на
|
|
51
|
+
# 10.5.3 з caret-діапазоном, а не на exact-пінованій 9.1.10 (яка на практиці ніде вже не
|
|
52
|
+
# встановлюється). @storybook/vue3 — не окремий devDep пілота (транзитивна залежність
|
|
53
|
+
# @storybook/vue3-vite), версія вирівняна до тієї самої лінійки про запас (allowlist за
|
|
54
|
+
# іменем, presence не обов'язковий).
|
|
46
55
|
storybook_canon_dev_deps := {
|
|
47
|
-
"storybook": "
|
|
48
|
-
"@storybook/vue3-vite": "
|
|
49
|
-
"@storybook/vue3": "
|
|
56
|
+
"storybook": "^10.5.3",
|
|
57
|
+
"@storybook/vue3-vite": "^10.5.3",
|
|
58
|
+
"@storybook/vue3": "^10.5.3",
|
|
50
59
|
"msw": "2.11.3",
|
|
51
60
|
"msw-storybook-addon": "2.0.5",
|
|
52
61
|
}
|
|
@@ -3,9 +3,8 @@ type: JS Module
|
|
|
3
3
|
title: main.mjs
|
|
4
4
|
resource: plugins/lang-js/rules/storybook/adopt/main.mjs
|
|
5
5
|
docgen:
|
|
6
|
-
crc:
|
|
6
|
+
crc: 986ffd02
|
|
7
7
|
model: openai-codex/gpt-5.4-mini
|
|
8
|
-
tier: cloud-min
|
|
9
8
|
score: 90
|
|
10
9
|
issues: anchor-miss:(mocking.mdc),anchor-miss:(vitest-config.mdc),judge:inaccurate:0.99
|
|
11
10
|
judgeModel: openai-codex/gpt-5.4-mini
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Adopt-режим канону Storybook (ADR канон-storybook-для-vue-компонентних-бібліотек,
|
|
3
3
|
* Кластер 8; main.mdc). Для пакетів, де ВЖЕ є ручний `.storybook/`, що не збігається з
|
|
4
4
|
* каноном, — діагностика diff по секціях проти канонічних `template/` (main.js,
|
|
5
|
-
* preview.js, mocks/gql-sse.js, package.json#scripts.storybook,
|
|
6
|
-
* vitest.stryker.config), БЕЗ сліпого перезапису розбіжних файлів. Автофікс
|
|
5
|
+
* preview.js, empty-vite.config.js, mocks/gql-sse.js, package.json#scripts.storybook,
|
|
6
|
+
* vitest test.projects, vitest.stryker.config), БЕЗ сліпого перезапису розбіжних файлів. Автофікс
|
|
7
7
|
* (`--fix-missing`) — лише для секцій, яких немає ВЗАГАЛІ (той самий рендер, що й
|
|
8
8
|
* T0-фікс concern-ів `scaffold`/`vitest-config` — переюз, не дублювання шаблонування).
|
|
9
9
|
*
|
|
@@ -21,7 +21,7 @@ import { isRunAsCli } from '@7n/rules/scripts/cli-entry.mjs'
|
|
|
21
21
|
|
|
22
22
|
import { collectInScopeVuePackages } from '../scope/main.mjs'
|
|
23
23
|
import { MAIN_JS_MARKERS, missingMarkers, PREVIEW_JS_MARKERS, STORYBOOK_SCRIPT } from '../scaffold/main.mjs'
|
|
24
|
-
import { renderMainJs, renderMocksGqlSse, renderPreviewJs } from '../scaffold/fix-scaffold.mjs'
|
|
24
|
+
import { renderEmptyViteConfig, renderMainJs, renderMocksGqlSse, renderPreviewJs } from '../scaffold/fix-scaffold.mjs'
|
|
25
25
|
import { buildFreshVitestConfig, buildStrykerConfig } from '../vitest-config/fix-vitest-config.mjs'
|
|
26
26
|
import {
|
|
27
27
|
BROWSER_KEY_RE,
|
|
@@ -29,9 +29,10 @@ import {
|
|
|
29
29
|
classifyProjects,
|
|
30
30
|
findProperty,
|
|
31
31
|
findTestObject,
|
|
32
|
+
hasStoriesMarker,
|
|
32
33
|
parseModule,
|
|
34
|
+
PROVIDER_FACTORY_RE,
|
|
33
35
|
resolveVitestConfigPath,
|
|
34
|
-
STORIES_RE,
|
|
35
36
|
strykerConfigPathFor
|
|
36
37
|
} from '../vitest-config/main.mjs'
|
|
37
38
|
|
|
@@ -42,6 +43,7 @@ export const STATUS = Object.freeze({ MATCH: 'match', DIFFER: 'differ', MISSING:
|
|
|
42
43
|
export const SECTION = Object.freeze({
|
|
43
44
|
MAIN_JS: 'main.js',
|
|
44
45
|
PREVIEW_JS: 'preview.js',
|
|
46
|
+
EMPTY_VITE_CONFIG: 'empty-vite.config.js',
|
|
45
47
|
MOCKS_GQL_SSE: 'mocks/gql-sse.js',
|
|
46
48
|
PACKAGE_SCRIPT: 'package.json#scripts.storybook',
|
|
47
49
|
VITEST_PROJECTS: 'vitest test.projects (unit+storybook)',
|
|
@@ -103,6 +105,30 @@ function diagnoseMocksSection(entry) {
|
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Діагностика `.storybook/empty-vite.config.js` — verbatim-порівняння з канонічним
|
|
110
|
+
* стенд-ін файлом (не залежить від пакета, як і `mocks/gql-sse.js`): `main.js` посилається
|
|
111
|
+
* на нього напряму через `core.builder.options.viteConfigPath`, тому розбіжність тут
|
|
112
|
+
* ламає обхід autodiscovery `@storybook/builder-vite`, навіть якщо `main.js` канонічний.
|
|
113
|
+
* @param {{ absDir: string, rootDir: string }} entry запис пакета
|
|
114
|
+
* @returns {{ name: string, file: string, status: string, detail?: string }} секція діагностики
|
|
115
|
+
*/
|
|
116
|
+
function diagnoseEmptyViteConfigSection(entry) {
|
|
117
|
+
const relPath = '.storybook/empty-vite.config.js'
|
|
118
|
+
const file = relFileFor(entry, relPath)
|
|
119
|
+
const abs = join(entry.absDir, relPath)
|
|
120
|
+
if (!existsSync(abs)) return { name: SECTION.EMPTY_VITE_CONFIG, file, status: STATUS.MISSING }
|
|
121
|
+
const actual = readFileSync(abs, 'utf8')
|
|
122
|
+
const canonical = renderEmptyViteConfig()
|
|
123
|
+
if (actual === canonical) return { name: SECTION.EMPTY_VITE_CONFIG, file, status: STATUS.MATCH }
|
|
124
|
+
return {
|
|
125
|
+
name: SECTION.EMPTY_VITE_CONFIG,
|
|
126
|
+
file,
|
|
127
|
+
status: STATUS.DIFFER,
|
|
128
|
+
detail: 'вміст відрізняється від канонічного стенд-іна (storybook.mdc) — має лишатись порожнім defineConfig({})'
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
106
132
|
/**
|
|
107
133
|
* Діагностика `package.json#scripts.storybook`.
|
|
108
134
|
* @param {{ pkg: Record<string, unknown>, rootDir: string }} entry запис пакета
|
|
@@ -167,7 +193,9 @@ function diagnoseVitestProjectsSection(entry) {
|
|
|
167
193
|
const missingHints = []
|
|
168
194
|
if (!CHROMIUM_RE.test(storybookSlice)) missingHints.push('chromium-інстанс')
|
|
169
195
|
if (!BROWSER_KEY_RE.test(storybookSlice)) missingHints.push('browser-mode')
|
|
170
|
-
if (!
|
|
196
|
+
if (!hasStoriesMarker(storybookSlice)) missingHints.push('stories-джерело (include або storybookTest({ configDir }))')
|
|
197
|
+
if (!PROVIDER_FACTORY_RE.test(storybookSlice))
|
|
198
|
+
missingHints.push('provider-factory (playwright() з @vitest/browser-playwright)')
|
|
171
199
|
if (missingHints.length > 0) {
|
|
172
200
|
return { name, file, status: STATUS.DIFFER, detail: `storybook-project без: ${missingHints.join(', ')}` }
|
|
173
201
|
}
|
|
@@ -216,6 +244,7 @@ export async function diagnosePackage(entry) {
|
|
|
216
244
|
const sections = [
|
|
217
245
|
diagnoseMarkerFile(entry, '.storybook/main.js', SECTION.MAIN_JS, MAIN_JS_MARKERS),
|
|
218
246
|
diagnoseMarkerFile(entry, '.storybook/preview.js', SECTION.PREVIEW_JS, PREVIEW_JS_MARKERS),
|
|
247
|
+
diagnoseEmptyViteConfigSection(entry),
|
|
219
248
|
diagnoseMocksSection(entry),
|
|
220
249
|
diagnoseScriptSection(entry),
|
|
221
250
|
diagnoseVitestProjectsSection(entry),
|
|
@@ -252,6 +281,9 @@ export async function fixMissingSections(entry, sections) {
|
|
|
252
281
|
if (byName.get(SECTION.PREVIEW_JS)?.status === STATUS.MISSING) {
|
|
253
282
|
writeFileEnsureDir(join(entry.absDir, '.storybook/preview.js'), renderPreviewJs())
|
|
254
283
|
}
|
|
284
|
+
if (byName.get(SECTION.EMPTY_VITE_CONFIG)?.status === STATUS.MISSING) {
|
|
285
|
+
writeFileEnsureDir(join(entry.absDir, '.storybook/empty-vite.config.js'), renderEmptyViteConfig())
|
|
286
|
+
}
|
|
255
287
|
if (byName.get(SECTION.MOCKS_GQL_SSE)?.status === STATUS.MISSING) {
|
|
256
288
|
writeFileEnsureDir(join(entry.absDir, '.storybook/mocks/gql-sse.js'), renderMocksGqlSse())
|
|
257
289
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://unpkg.com/@7n/rules/schemas/concern.json",
|
|
3
|
+
"fixability": "config",
|
|
4
|
+
"requires": { "capability": "ci:github" },
|
|
5
|
+
"lint": {
|
|
6
|
+
"scope": "full",
|
|
7
|
+
"glob": [
|
|
8
|
+
"**/package.json",
|
|
9
|
+
"**/*.vue",
|
|
10
|
+
"**/.storybook/**",
|
|
11
|
+
".github/actions/setup-playwright-chromium/action.yml",
|
|
12
|
+
".github/workflows/lint-storybook.yml"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: fix-ci.mjs
|
|
4
|
+
resource: plugins/lang-js/rules/storybook/ci/fix-ci.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: 877b9feb
|
|
7
|
+
model: openai-codex/gpt-5.4-mini
|
|
8
|
+
tier: cloud-min
|
|
9
|
+
score: 90
|
|
10
|
+
judgeModel: openai-codex/gpt-5.4-mini
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Огляд
|
|
14
|
+
|
|
15
|
+
Порожній шаблон `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`.
|
|
16
|
+
|
|
17
|
+
## Поведінка
|
|
18
|
+
|
|
19
|
+
- **TEMPLATE_DIR** — вказує на каталог `template/` цього concern-а як джерело канонічних CI-шаблонів.
|
|
20
|
+
- **renderPlaywrightAction** — читає канонічний composite action `setup-playwright-chromium` із шаблону без змін.
|
|
21
|
+
- **renderPackageDirsYaml** — перетворює список коренів пакетів у YAML-рядки для матриці Storybook CI.
|
|
22
|
+
- **renderStorybookWorkflow** — збирає канонічний `.github/workflows/lint-storybook.yml`, підставляючи в нього матрицю пакетів зі скоупу репозиторію.
|
|
23
|
+
- **patterns** — описує T0-правила автофіксу для відновлення відсутніх CI-файлів цього concern-а.
|
|
24
|
+
|
|
25
|
+
## Публічний API
|
|
26
|
+
|
|
27
|
+
- TEMPLATE_DIR — Каталог `template/` цього concern-а.
|
|
28
|
+
- renderPlaywrightAction — Вміст канонічного composite action `setup-playwright-chromium` — verbatim з template.
|
|
29
|
+
- renderPackageDirsYaml — Рендерить YAML-фрагмент `matrix.package` списку — по одному `- <rootDir>` на рядок, з тим
|
|
30
|
+
самим відступом, що й токен-рядок у шаблоні (10 пробілів — рівень елемента списку під
|
|
31
|
+
`strategy.matrix.package:`). `rootDir === '.'` (корінь монорепо) лишається `.` — валідний
|
|
32
|
+
`working-directory` для GitHub Actions.
|
|
33
|
+
- renderStorybookWorkflow — Вміст канонічного `.github/workflows/lint-storybook.yml` — template з підставленою
|
|
34
|
+
матрицею пакетів у скоупі.
|
|
35
|
+
- patterns — повертає набір шаблонів, за якими далі шукають або зіставляють потрібні значення.
|
|
36
|
+
|
|
37
|
+
## Гарантії поведінки
|
|
38
|
+
|
|
39
|
+
- (специфічних машинно-виведених гарантій немає)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Directory Index
|
|
3
|
+
title: plugins/lang-js/rules/storybook/ci
|
|
4
|
+
resource: plugins/lang-js/rules/storybook/ci/
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
| Файл | Тип |
|
|
8
|
+
| ----------------------- | --------- |
|
|
9
|
+
| [fix-ci.mjs](fix-ci.md) | JS Module |
|
|
10
|
+
| [main.mjs](main.md) | JS Module |
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: main.mjs
|
|
4
|
+
resource: plugins/lang-js/rules/storybook/ci/main.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: 65df5791
|
|
7
|
+
model: openai-codex/gpt-5.4-mini
|
|
8
|
+
tier: cloud-min
|
|
9
|
+
score: 85
|
|
10
|
+
issues: internal-name:collectInScopeVuePackages,anchor-miss:(storybook.mdc),judge:inaccurate:0.96
|
|
11
|
+
judgeModel: openai-codex/gpt-5.4-mini
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Огляд
|
|
15
|
+
|
|
16
|
+
Файл фіксує канонічні репо-шляхи й маркери для storybook CI, щоб у Vue component library пакетах у scope швидко знаходити правильні `.github/actions/setup-playwright-chromium/action.yml` і `.github/workflows/lint-storybook.yml` та зіставляти їх із повідомленнями в `storybook.mdc`. Експортовані константи-рядки: PLAYWRIGHT_ACTION_REL=".github/actions/setup-playwright-chromium/action.yml" — канонічний шлях до Playwright action; STORYBOOK_WORKFLOW_REL=".github/workflows/lint-storybook.yml" — канонічний шлях до workflow linting. Маркери повідомлень: PLAYWRIGHT_ACTION_MARKERS і STORYBOOK_WORKFLOW_MARKERS, які прив’язують перевірки до повідомлень у `storybook.mdc`. Це потрібно, щоб підтримувати єдиний storybook-канон без зайвого обходу `.github` і `.git`, із кешуванням результату в межах одного прогону.
|
|
17
|
+
|
|
18
|
+
## Поведінка
|
|
19
|
+
|
|
20
|
+
- PLAYWRIGHT_ACTION_REL — задає репо-рівневий шлях до канонічного composite action `.github/actions/setup-playwright-chromium/action.yml`, який перевіряється як частина storybook CI.
|
|
21
|
+
- STORYBOOK_WORKFLOW_REL — задає репо-рівневий шлях до канонічного workflow `.github/workflows/lint-storybook.yml`, що відповідає за швидкий storybook-лінт.
|
|
22
|
+
- PLAYWRIGHT_ACTION_MARKERS — описує канонічні ознаки composite action для storybook CI, щоб гарантувати кеш Playwright і встановлення лише chromium; у повідомленнях використовується прив’язка до `storybook.mdc`.
|
|
23
|
+
- STORYBOOK_WORKFLOW_MARKERS — описує канонічні ознаки workflow для storybook CI, щоб гарантувати запуск швидкого `vitest --project=storybook`; у повідомленнях використовується прив’язка до `storybook.mdc`.
|
|
24
|
+
- lint — перевіряє наявність storybook CI-канону лише для репозиторіїв із Vue component library пакетами в scope; якщо таких пакетів немає, завершується без зауважень. Свідомо не зачіпає `.github` і `.git` як окремі області обходу; кешує результат у межах одного прогону.
|
|
25
|
+
|
|
26
|
+
## Публічний API
|
|
27
|
+
|
|
28
|
+
- PLAYWRIGHT_ACTION_REL — Repo-relative шлях канонічного composite action (не per-package — один на репозиторій).
|
|
29
|
+
- STORYBOOK_WORKFLOW_REL — Repo-relative шлях канонічного workflow, що запускає `vitest --project=storybook`.
|
|
30
|
+
- PLAYWRIGHT_ACTION_MARKERS — Маркери канону composite action `setup-playwright-chromium` (ADR Кластер 5): кеш
|
|
31
|
+
`ms-playwright` через `actions/cache`, ключ від версії playwright, install лише chromium.
|
|
32
|
+
Текстовий пошук — той самий підхід, що й `MAIN_JS_MARKERS`/`PREVIEW_JS_MARKERS` у `scaffold`.
|
|
33
|
+
- STORYBOOK_WORKFLOW_MARKERS — Маркери канону `.github/workflows/lint-storybook.yml`: композитний Playwright-кеш ПІСЛЯ
|
|
34
|
+
setup-bun-deps, і швидкий `vitest --project=storybook` (ADR Кластер 5 — nightly-only
|
|
35
|
+
`@7n/test coverage`/mutation-testing на PR не запускається, лише цей швидкий шлях).
|
|
36
|
+
- lint — Detector concern-а `storybook/ci` (ADR Кластер 5, CI-частина): для репозиторіїв з бодай
|
|
37
|
+
одним Vue component library пакетом у скоупі Storybook (`collectInScopeVuePackages`) —
|
|
38
|
+
канонічний composite action `setup-playwright-chromium` (кеш Playwright-браузерів, лише
|
|
39
|
+
chromium) і канонічний `.github/workflows/lint-storybook.yml`, що запускає швидкий
|
|
40
|
+
`vitest --project=storybook` на PR. Гейтований `requires.capability: ci:github` — спить
|
|
41
|
+
у репозиторіях без плагіна `@7n/rules-ci-github` (немає `.github/workflows`).
|
|
42
|
+
|
|
43
|
+
Nightly-only `@7n/test coverage` (mutation testing) — поза обсягом цього concern-а: ADR
|
|
44
|
+
Кластер 5 явно розділяє швидкий PR-шлях (цей concern) і nightly mutation-прогін, який
|
|
45
|
+
лишається окремою інфраструктурою `test/stryker_config`.
|
|
46
|
+
|
|
47
|
+
## Гарантії поведінки
|
|
48
|
+
|
|
49
|
+
- Кешує результати в межах одного прогону.
|
|
50
|
+
- Свідомо пропускає шляхи: `.github`, `.git`.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* T0-autofix для concern-а `storybook/ci` (ADR Кластер 5, CI-частина): відтворює канонічний
|
|
3
|
+
* composite action `setup-playwright-chromium` (кеш Playwright-браузерів, лише chromium) і
|
|
4
|
+
* канонічний `.github/workflows/lint-storybook.yml` з `template/` цього concern-а.
|
|
5
|
+
*
|
|
6
|
+
* Обидва файли — репо-рівневі (не per-package): composite action — verbatim-копія; workflow —
|
|
7
|
+
* матриця `strategy.matrix.package` генерується з фактичного списку пакетів у скоупі
|
|
8
|
+
* (`collectInScopeVuePackages`), щоб CI реально покривав усі Storybook-пакети репозиторію.
|
|
9
|
+
*/
|
|
10
|
+
import { mkdirSync, writeFileSync } from 'node:fs'
|
|
11
|
+
import { dirname, join } from 'node:path'
|
|
12
|
+
import { fileURLToPath } from 'node:url'
|
|
13
|
+
import { readFile } from 'node:fs/promises'
|
|
14
|
+
|
|
15
|
+
import { collectInScopeVuePackages } from '../scope/main.mjs'
|
|
16
|
+
|
|
17
|
+
/** Каталог `template/` цього concern-а. */
|
|
18
|
+
export const TEMPLATE_DIR = join(dirname(fileURLToPath(import.meta.url)), 'template')
|
|
19
|
+
|
|
20
|
+
/** Токен у `lint-storybook.yml.snippet.yml`, що заміщається списком пакетів матриці. */
|
|
21
|
+
const PACKAGE_DIRS_TOKEN = '__STORYBOOK_CI_PACKAGE_DIRS__'
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Вміст канонічного composite action `setup-playwright-chromium` — verbatim з template.
|
|
25
|
+
* @param {string} [templateDir] каталог template/ (за замовчуванням — цього concern-а)
|
|
26
|
+
* @returns {Promise<string>} вміст `action.yml`
|
|
27
|
+
*/
|
|
28
|
+
export function renderPlaywrightAction(templateDir = TEMPLATE_DIR) {
|
|
29
|
+
return readFile(join(templateDir, 'setup-playwright-chromium.action.yml'), 'utf8')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Рендерить YAML-фрагмент `matrix.package` списку — по одному `- <rootDir>` на рядок, з тим
|
|
34
|
+
* самим відступом, що й токен-рядок у шаблоні (10 пробілів — рівень елемента списку під
|
|
35
|
+
* `strategy.matrix.package:`). `rootDir === '.'` (корінь монорепо) лишається `.` — валідний
|
|
36
|
+
* `working-directory` для GitHub Actions.
|
|
37
|
+
* @param {string[]} rootDirs відносні (posix) корені пакетів у скоупі
|
|
38
|
+
* @returns {string} YAML-рядки (без завершального переносу)
|
|
39
|
+
*/
|
|
40
|
+
export function renderPackageDirsYaml(rootDirs) {
|
|
41
|
+
return rootDirs.map(dir => ` - ${dir}`).join('\n')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Вміст канонічного `.github/workflows/lint-storybook.yml` — template з підставленою
|
|
46
|
+
* матрицею пакетів у скоупі.
|
|
47
|
+
* @param {string[]} rootDirs відносні корені пакетів у скоупі (`collectInScopeVuePackages`)
|
|
48
|
+
* @param {string} [templateDir] каталог template/ (за замовчуванням — цього concern-а)
|
|
49
|
+
* @returns {Promise<string>} вміст `lint-storybook.yml`
|
|
50
|
+
*/
|
|
51
|
+
export async function renderStorybookWorkflow(rootDirs, templateDir = TEMPLATE_DIR) {
|
|
52
|
+
const raw = await readFile(join(templateDir, 'lint-storybook.yml.snippet.yml'), 'utf8')
|
|
53
|
+
const tokenLineRe = new RegExp(`^[ \\t]*${PACKAGE_DIRS_TOKEN}[ \\t]*$`, 'mu')
|
|
54
|
+
// Replacer-функція (не рядок) — уникає unicorn/no-unsafe-string-replacement:
|
|
55
|
+
// рядок-заміна мав би трактувати `$`-послідовності як спецсимволи `String#replace`.
|
|
56
|
+
return raw.replace(tokenLineRe, () => renderPackageDirsYaml(rootDirs))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Записує файл, створюючи батьківські каталоги й реєструючи запис для rollback (той самий
|
|
61
|
+
* патерн, що й `writeScaffoldFile` у `scaffold/fix-scaffold.mjs`).
|
|
62
|
+
* @param {import('@7n/rules/scripts/lib/lint-surface/types.mjs').FixContext} ctx fix-контекст рунга
|
|
63
|
+
* @param {string} absPath абсолютний шлях цільового файлу
|
|
64
|
+
* @param {string} content вміст файлу
|
|
65
|
+
* @returns {void}
|
|
66
|
+
*/
|
|
67
|
+
function writeCiFile(ctx, absPath, content) {
|
|
68
|
+
mkdirSync(dirname(absPath), { recursive: true })
|
|
69
|
+
ctx.recordWrite?.(absPath)
|
|
70
|
+
writeFileSync(absPath, content, 'utf8')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** @type {import('@7n/rules/scripts/lib/lint-surface/types.mjs').T0Pattern[]} */
|
|
74
|
+
export const patterns = [
|
|
75
|
+
{
|
|
76
|
+
id: 'storybook-ci-playwright-action',
|
|
77
|
+
test: violations => violations.some(v => v.reason === 'missing-playwright-action' && v.file),
|
|
78
|
+
apply: async (violations, ctx) => {
|
|
79
|
+
const target = violations.find(v => v.reason === 'missing-playwright-action' && v.file)
|
|
80
|
+
if (!target || !ctx.concernDir) return { touchedFiles: [] }
|
|
81
|
+
|
|
82
|
+
const content = await renderPlaywrightAction(join(ctx.concernDir, 'template'))
|
|
83
|
+
const abs = join(ctx.cwd, target.file)
|
|
84
|
+
writeCiFile(ctx, abs, content)
|
|
85
|
+
return { touchedFiles: [abs], message: `${target.file}: створено composite action setup-playwright-chromium` }
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: 'storybook-ci-workflow',
|
|
90
|
+
test: violations => violations.some(v => v.reason === 'missing-storybook-workflow' && v.file),
|
|
91
|
+
apply: async (violations, ctx) => {
|
|
92
|
+
const target = violations.find(v => v.reason === 'missing-storybook-workflow' && v.file)
|
|
93
|
+
if (!target || !ctx.concernDir) return { touchedFiles: [] }
|
|
94
|
+
|
|
95
|
+
const pkgs = await collectInScopeVuePackages(ctx.cwd)
|
|
96
|
+
const rootDirs = pkgs.map(p => p.rootDir)
|
|
97
|
+
if (rootDirs.length === 0) return { touchedFiles: [] }
|
|
98
|
+
|
|
99
|
+
const content = await renderStorybookWorkflow(rootDirs, join(ctx.concernDir, 'template'))
|
|
100
|
+
const abs = join(ctx.cwd, target.file)
|
|
101
|
+
writeCiFile(ctx, abs, content)
|
|
102
|
+
return { touchedFiles: [abs], message: `${target.file}: створено (${rootDirs.length} пакет(ів) у матриці)` }
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
]
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/** @see ./docs/main.md */
|
|
2
|
+
import { existsSync } from 'node:fs'
|
|
3
|
+
import { readFile } from 'node:fs/promises'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
|
+
|
|
6
|
+
import { createViolationReporter } from '@7n/rules/scripts/lib/lint-surface/violation-reporter.mjs'
|
|
7
|
+
import { collectInScopeVuePackages } from '../scope/main.mjs'
|
|
8
|
+
import { missingMarkers } from '../scaffold/main.mjs'
|
|
9
|
+
|
|
10
|
+
/** Repo-relative шлях канонічного composite action (не per-package — один на репозиторій). */
|
|
11
|
+
export const PLAYWRIGHT_ACTION_REL = '.github/actions/setup-playwright-chromium/action.yml'
|
|
12
|
+
|
|
13
|
+
/** Repo-relative шлях канонічного workflow, що запускає `vitest --project=storybook`. */
|
|
14
|
+
export const STORYBOOK_WORKFLOW_REL = '.github/workflows/lint-storybook.yml'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Маркери канону composite action `setup-playwright-chromium` (ADR Кластер 5): кеш
|
|
18
|
+
* `ms-playwright` через `actions/cache`, ключ від версії playwright, install лише chromium.
|
|
19
|
+
* Текстовий пошук — той самий підхід, що й `MAIN_JS_MARKERS`/`PREVIEW_JS_MARKERS` у `scaffold`.
|
|
20
|
+
*/
|
|
21
|
+
export const PLAYWRIGHT_ACTION_MARKERS = [
|
|
22
|
+
{ token: 'ms-playwright', hint: 'кеш каталогу ms-playwright' },
|
|
23
|
+
{ token: 'actions/cache@', hint: 'actions/cache для Playwright-браузерів' },
|
|
24
|
+
{ token: 'playwright install chromium', hint: 'install лише chromium (не всі браузери)' }
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Маркери канону `.github/workflows/lint-storybook.yml`: композитний Playwright-кеш ПІСЛЯ
|
|
29
|
+
* setup-bun-deps, і швидкий `vitest --project=storybook` (ADR Кластер 5 — nightly-only
|
|
30
|
+
* `@7n/test coverage`/mutation-testing на PR не запускається, лише цей швидкий шлях).
|
|
31
|
+
*/
|
|
32
|
+
export const STORYBOOK_WORKFLOW_MARKERS = [
|
|
33
|
+
{ token: './.github/actions/setup-bun-deps', hint: 'setup-bun-deps перед Playwright-кроком' },
|
|
34
|
+
{ token: './.github/actions/setup-playwright-chromium', hint: 'композитний Playwright-кеш' },
|
|
35
|
+
{ token: '--project=storybook', hint: 'швидкий прогін лише storybook-проєкту (не повний coverage)' }
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Перевіряє один репо-рівневий канонічний файл (composite action чи workflow): відсутність →
|
|
40
|
+
* `missingReason`-порушення з посиланням на `npx \@7n/rules fix storybook`; присутність без
|
|
41
|
+
* якогось маркера → `markerReason`-порушення на конкретний маркер. Той самий патерн, що й
|
|
42
|
+
* `checkCanonFile` у `scaffold/main.mjs`, але без per-package `rootDir` — файл один на репо.
|
|
43
|
+
* @param {string} cwd абсолютний корінь репозиторію
|
|
44
|
+
* @param {string} relFile posix-relative шлях файлу від кореня репозиторію
|
|
45
|
+
* @param {{ token: string, hint: string }[]} markers канонічні маркери файлу
|
|
46
|
+
* @param {string} missingReason reason для порушення "файл відсутній"
|
|
47
|
+
* @param {string} markerReason reason для порушення "маркер відсутній"
|
|
48
|
+
* @param {ReturnType<typeof createViolationReporter>} reporter reporter поточного лінту
|
|
49
|
+
* @returns {Promise<void>}
|
|
50
|
+
*/
|
|
51
|
+
async function checkRepoCanonFile(cwd, relFile, markers, missingReason, markerReason, reporter) {
|
|
52
|
+
const abs = join(cwd, relFile)
|
|
53
|
+
if (existsSync(abs)) {
|
|
54
|
+
const content = await readFile(abs, 'utf8')
|
|
55
|
+
for (const m of missingMarkers(content, markers)) {
|
|
56
|
+
reporter.fail(`${relFile} не відповідає канону — бракує: ${m.hint} (storybook.mdc, ADR Кластер 5)`, {
|
|
57
|
+
reason: markerReason,
|
|
58
|
+
file: relFile
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
reporter.fail(
|
|
64
|
+
`Відсутній ${relFile} — канонічний Playwright-кеш для vitest storybook-проєкту: npx @7n/rules fix storybook (storybook.mdc, ADR Кластер 5)`,
|
|
65
|
+
{ reason: missingReason, file: relFile }
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Detector concern-а `storybook/ci` (ADR Кластер 5, CI-частина): для репозиторіїв з бодай
|
|
71
|
+
* одним Vue component library пакетом у скоупі Storybook (`collectInScopeVuePackages`) —
|
|
72
|
+
* канонічний composite action `setup-playwright-chromium` (кеш Playwright-браузерів, лише
|
|
73
|
+
* chromium) і канонічний `.github/workflows/lint-storybook.yml`, що запускає швидкий
|
|
74
|
+
* `vitest --project=storybook` на PR. Гейтований `requires.capability: ci:github` — спить
|
|
75
|
+
* у репозиторіях без плагіна `@7n/rules-ci-github` (немає `.github/workflows`).
|
|
76
|
+
*
|
|
77
|
+
* Nightly-only `@7n/test coverage` (mutation testing) — поза обсягом цього concern-а: ADR
|
|
78
|
+
* Кластер 5 явно розділяє швидкий PR-шлях (цей concern) і nightly mutation-прогін, який
|
|
79
|
+
* лишається окремою інфраструктурою `test/stryker_config`.
|
|
80
|
+
* @param {import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintContext} ctx контекст лінту
|
|
81
|
+
* @returns {Promise<import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintResult>} результат лінту
|
|
82
|
+
*/
|
|
83
|
+
export async function lint(ctx) {
|
|
84
|
+
const reporter = createViolationReporter(ctx)
|
|
85
|
+
const cwd = ctx.cwd
|
|
86
|
+
|
|
87
|
+
const pkgs = await collectInScopeVuePackages(cwd)
|
|
88
|
+
if (pkgs.length === 0) {
|
|
89
|
+
reporter.pass('storybook/ci: немає Vue component library пакетів у скоупі (storybook.mdc)')
|
|
90
|
+
return reporter.result()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
await checkRepoCanonFile(
|
|
94
|
+
cwd,
|
|
95
|
+
PLAYWRIGHT_ACTION_REL,
|
|
96
|
+
PLAYWRIGHT_ACTION_MARKERS,
|
|
97
|
+
'missing-playwright-action',
|
|
98
|
+
'playwright-action-marker-missing',
|
|
99
|
+
reporter
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
await checkRepoCanonFile(
|
|
103
|
+
cwd,
|
|
104
|
+
STORYBOOK_WORKFLOW_REL,
|
|
105
|
+
STORYBOOK_WORKFLOW_MARKERS,
|
|
106
|
+
'missing-storybook-workflow',
|
|
107
|
+
'storybook-workflow-marker-missing',
|
|
108
|
+
reporter
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return reporter.result()
|
|
112
|
+
}
|