@7n/rules-lang-js 0.9.0 → 0.10.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 +13 -0
- package/package.json +1 -1
- package/rules/bun/package_json/package_json.mdc +3 -1
- package/rules/bun/package_json/package_json.rego +30 -1
- package/rules/npm-module/npm_package_json/npm_package_json.mdc +18 -3
- package/rules/npm-module/npm_package_json/npm_package_json.rego +42 -5
- package/rules/storybook/adopt/docs/index.md +9 -0
- package/rules/storybook/adopt/docs/main.md +46 -0
- package/rules/storybook/adopt/main.mjs +379 -0
- package/rules/storybook/hygiene/concern.json +4 -0
- package/rules/storybook/hygiene/docs/index.md +9 -0
- package/rules/storybook/hygiene/docs/main.md +45 -0
- package/rules/storybook/hygiene/main.mjs +254 -0
- package/rules/storybook/main.json +1 -0
- package/rules/storybook/main.mdc +60 -0
- package/rules/storybook/mocking/concern.json +3 -0
- package/rules/storybook/mocking/mocking.mdc +108 -0
- package/rules/storybook/scaffold/concern.json +5 -0
- package/rules/storybook/scaffold/docs/fix-scaffold.md +29 -0
- package/rules/storybook/scaffold/docs/index.md +10 -0
- package/rules/storybook/scaffold/docs/main.md +36 -0
- package/rules/storybook/scaffold/fix-scaffold.mjs +150 -0
- package/rules/storybook/scaffold/main.mjs +164 -0
- package/rules/storybook/scaffold/template/docs/index.md +10 -0
- package/rules/storybook/scaffold/template/docs/main.md +30 -0
- package/rules/storybook/scaffold/template/docs/preview.md +46 -0
- package/rules/storybook/scaffold/template/main.js +45 -0
- package/rules/storybook/scaffold/template/mocks/docs/gql-sse.md +36 -0
- package/rules/storybook/scaffold/template/mocks/docs/index.md +9 -0
- package/rules/storybook/scaffold/template/mocks/gql-sse.js +25 -0
- package/rules/storybook/scaffold/template/preview.js +47 -0
- package/rules/storybook/scope/concern.json +4 -0
- package/rules/storybook/scope/docs/index.md +9 -0
- package/rules/storybook/scope/docs/main.md +62 -0
- package/rules/storybook/scope/main.mjs +202 -0
- package/rules/storybook/vitest-config/concern.json +8 -0
- package/rules/storybook/vitest-config/docs/fix-vitest-config.md +38 -0
- package/rules/storybook/vitest-config/docs/index.md +10 -0
- package/rules/storybook/vitest-config/docs/main.md +72 -0
- package/rules/storybook/vitest-config/fix-vitest-config.mjs +340 -0
- package/rules/storybook/vitest-config/main.mjs +358 -0
- package/rules/storybook/vitest-config/template/docs/index.md +12 -0
- package/rules/storybook/vitest-config/template/docs/storybook-project-entry.md +34 -0
- package/rules/storybook/vitest-config/template/docs/unit-project-entry.md +30 -0
- package/rules/storybook/vitest-config/template/docs/vitest.config.baseline.md +29 -0
- package/rules/storybook/vitest-config/template/docs/vitest.stryker.config.baseline.md +31 -0
- package/rules/storybook/vitest-config/template/storybook-project-entry.js +22 -0
- package/rules/storybook/vitest-config/template/unit-project-entry.js +5 -0
- package/rules/storybook/vitest-config/template/vitest.config.baseline.mjs +37 -0
- package/rules/storybook/vitest-config/template/vitest.stryker.config.baseline.mjs +19 -0
- package/rules/storybook/vitest-config/vitest-config.mdc +33 -0
|
@@ -0,0 +1,202 @@
|
|
|
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 { loadCursorIgnorePaths } from '@7n/rules/scripts/lib/load-cursor-config.mjs'
|
|
8
|
+
import { walkDir } from '@7n/rules/scripts/utils/walkDir.mjs'
|
|
9
|
+
import { getMonorepoPackageRootDirs } from '@7n/rules/scripts/lib/workspaces.mjs'
|
|
10
|
+
import { isVueComponentLibraryPkg } from '../../vue/packages/main.mjs'
|
|
11
|
+
|
|
12
|
+
const CONFIG_FILE = '.n-rules.json'
|
|
13
|
+
const LEGACY_CONFIG_FILE = '.n-cursor.json'
|
|
14
|
+
const VITE_CONFIG_FILES = ['vite.config.js', 'vite.config.ts', 'vite.config.mjs']
|
|
15
|
+
|
|
16
|
+
/** Поріг кількості `.vue`-файлів для скоупу канону Storybook (ADR Кластер 1). */
|
|
17
|
+
export const VUE_FILE_THRESHOLD = 3
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {object} InScopePackage
|
|
21
|
+
* @property {string} rootDir відносний (posix) корінь пакета, `.` для кореня монорепо
|
|
22
|
+
* @property {string} absDir абсолютний шлях кореня пакета
|
|
23
|
+
* @property {Record<string, unknown>} pkg розпарсений `package.json` пакета
|
|
24
|
+
* @property {number} vueFileCount кількість знайдених `.vue`-файлів
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Читає `storybook.optOut` з `.n-rules.json` (fallback — legacy `.n-cursor.json`). Толерантно до
|
|
29
|
+
* відсутнього файлу/поля/невалідного JSON — повертає порожній масив (open-by-default, як
|
|
30
|
+
* `read-n-rules-config-lite.mjs`). Значення — root dir пакетів (`.` для кореня, `packages/ui` тощо),
|
|
31
|
+
* той самий формат, що повертає `getMonorepoPackageRootDirs`.
|
|
32
|
+
* @param {string} cwd абсолютний корінь репозиторію
|
|
33
|
+
* @returns {Promise<string[]>} перелік opt-out root dir-ів
|
|
34
|
+
*/
|
|
35
|
+
export async function readStorybookOptOut(cwd) {
|
|
36
|
+
let file = join(cwd, CONFIG_FILE)
|
|
37
|
+
if (!existsSync(file)) file = join(cwd, LEGACY_CONFIG_FILE)
|
|
38
|
+
if (!existsSync(file)) return []
|
|
39
|
+
let raw
|
|
40
|
+
try {
|
|
41
|
+
raw = JSON.parse(await readFile(file, 'utf8'))
|
|
42
|
+
} catch {
|
|
43
|
+
return []
|
|
44
|
+
}
|
|
45
|
+
const list = raw?.storybook?.optOut
|
|
46
|
+
if (!Array.isArray(list)) return []
|
|
47
|
+
return list.filter(v => typeof v === 'string' && v.trim().length > 0)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Читає прапорець хвилі 2 `storybook.detectApps` з `.n-rules.json`. За замовчуванням `false` —
|
|
52
|
+
* детекція app-проєктів (`vue` у dependencies + `src/pages/`) лишається відкритим питанням ADR
|
|
53
|
+
* і не впливає на скоуп, доки консюмер-репо не увімкне прапорець явно.
|
|
54
|
+
* @param {string} cwd абсолютний корінь репозиторію
|
|
55
|
+
* @returns {Promise<boolean>} true, якщо app-проєкти теж треба зібрати у скоуп
|
|
56
|
+
*/
|
|
57
|
+
export async function readDetectAppsFlag(cwd) {
|
|
58
|
+
let file = join(cwd, CONFIG_FILE)
|
|
59
|
+
if (!existsSync(file)) file = join(cwd, LEGACY_CONFIG_FILE)
|
|
60
|
+
if (!existsSync(file)) return false
|
|
61
|
+
let raw
|
|
62
|
+
try {
|
|
63
|
+
raw = JSON.parse(await readFile(file, 'utf8'))
|
|
64
|
+
} catch {
|
|
65
|
+
return false
|
|
66
|
+
}
|
|
67
|
+
return raw?.storybook?.detectApps === true
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Рахує `.vue`-файли в дереві пакета (поважає `.gitignore` й `ignore` з `.n-rules.json` через
|
|
72
|
+
* `walkDir`/`ignorePaths` — той самий обхід, що й `vue/packages`).
|
|
73
|
+
* @param {string} absPkgDir абсолютний шлях кореня пакета
|
|
74
|
+
* @param {string[]} ignorePaths абсолютні шляхи, повністю виключені з обходу
|
|
75
|
+
* @returns {Promise<number>} кількість `.vue`-файлів
|
|
76
|
+
*/
|
|
77
|
+
export async function countVueFiles(absPkgDir, ignorePaths) {
|
|
78
|
+
let count = 0
|
|
79
|
+
await walkDir(
|
|
80
|
+
absPkgDir,
|
|
81
|
+
absPath => {
|
|
82
|
+
if (absPath.endsWith('.vue')) count += 1
|
|
83
|
+
},
|
|
84
|
+
ignorePaths
|
|
85
|
+
)
|
|
86
|
+
return count
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Чи має пакет "стандартний" build — розпізнаваний `vite.config.{js,ts,mjs}` у корені пакета.
|
|
91
|
+
* Канонічний `.storybook/main.js` спирається саме на цей файл (`viteFinal` мерджить його
|
|
92
|
+
* плагіни) — без нього автоматичний скафолд неможливий, і пакет пропускається мовчки
|
|
93
|
+
* (ADR Кластер 1: "skip пакетів із нестандартним build").
|
|
94
|
+
* @param {string} absPkgDir абсолютний шлях кореня пакета
|
|
95
|
+
* @returns {boolean} true, якщо знайдено відомий `vite.config.*`
|
|
96
|
+
*/
|
|
97
|
+
export function hasStandardBuild(absPkgDir) {
|
|
98
|
+
return VITE_CONFIG_FILES.some(f => existsSync(join(absPkgDir, f)))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Чи є пакет app-проєктом (не бібліотекою) для хвилі 2: `vue` у `dependencies` (не лише
|
|
103
|
+
* `peerDependencies`) і не бібліотека компонентів. Реалізовано зараз (щоб не переписувати
|
|
104
|
+
* модуль пізніше), але результат впливає на скоуп лише за прапорця `storybook.detectApps`.
|
|
105
|
+
* @param {{ dependencies?: Record<string, unknown>, peerDependencies?: Record<string, unknown> }} pkg розпарсений package.json
|
|
106
|
+
* @returns {boolean} true — app-проєкт (кандидат хвилі 2)
|
|
107
|
+
*/
|
|
108
|
+
export function isVueAppPkg(pkg) {
|
|
109
|
+
return Boolean(pkg?.dependencies?.vue) && !isVueComponentLibraryPkg(pkg)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Перевіряє один workspace-корінь на відповідність предикату скоупу (бібліотека чи app) і,
|
|
114
|
+
* за успіху, повертає його `InScopePackage`-запис.
|
|
115
|
+
* @param {string} rootDir відносний корінь пакета
|
|
116
|
+
* @param {string} cwd абсолютний корінь репозиторію
|
|
117
|
+
* @param {(pkg: Record<string, unknown>) => boolean} matches предикат скоупу (бібліотека/app)
|
|
118
|
+
* @param {string[]} ignorePaths абсолютні шляхи, повністю виключені з обходу
|
|
119
|
+
* @returns {Promise<InScopePackage|null>} запис пакета або `null`, якщо поза скоупом
|
|
120
|
+
*/
|
|
121
|
+
async function evaluateCandidate(rootDir, cwd, matches, ignorePaths) {
|
|
122
|
+
const absDir = rootDir === '.' ? cwd : join(cwd, rootDir)
|
|
123
|
+
const pkgPath = join(absDir, 'package.json')
|
|
124
|
+
if (!existsSync(pkgPath)) return null
|
|
125
|
+
let pkg
|
|
126
|
+
try {
|
|
127
|
+
pkg = JSON.parse(await readFile(pkgPath, 'utf8'))
|
|
128
|
+
} catch {
|
|
129
|
+
return null
|
|
130
|
+
}
|
|
131
|
+
if (!matches(pkg)) return null
|
|
132
|
+
if (!hasStandardBuild(absDir)) return null
|
|
133
|
+
const vueFileCount = await countVueFiles(absDir, ignorePaths)
|
|
134
|
+
if (vueFileCount < VUE_FILE_THRESHOLD) return null
|
|
135
|
+
return { rootDir, absDir, pkg, vueFileCount }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Збирає workspace-пакети у скоупі канону Storybook хвилі 1: Vue-компонентна бібліотека
|
|
140
|
+
* (`vue` у `peerDependencies`, маркер `isVueComponentLibraryPkg` — той самий, що й `vue.mdc`)
|
|
141
|
+
* з не менше {@link VUE_FILE_THRESHOLD} `.vue`-файлами, без `storybook.optOut`, зі
|
|
142
|
+
* стандартним build (`vite.config.*`). Хвиля 2 (app-проєкти) додається лише за явного
|
|
143
|
+
* прапорця `storybook.detectApps` у `.n-rules.json`.
|
|
144
|
+
* @param {string} cwd абсолютний корінь репозиторію
|
|
145
|
+
* @returns {Promise<InScopePackage[]>} пакети у скоупі
|
|
146
|
+
*/
|
|
147
|
+
export async function collectInScopeVuePackages(cwd) {
|
|
148
|
+
const roots = await getMonorepoPackageRootDirs(cwd)
|
|
149
|
+
const ignorePaths = await loadCursorIgnorePaths(cwd)
|
|
150
|
+
const optOut = new Set(await readStorybookOptOut(cwd))
|
|
151
|
+
const candidateRoots = roots.filter(r => !optOut.has(r))
|
|
152
|
+
|
|
153
|
+
/** @type {InScopePackage[]} */
|
|
154
|
+
const result = []
|
|
155
|
+
for (const rootDir of candidateRoots) {
|
|
156
|
+
const found = await evaluateCandidate(rootDir, cwd, isVueComponentLibraryPkg, ignorePaths)
|
|
157
|
+
if (found) result.push(found)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (await readDetectAppsFlag(cwd)) {
|
|
161
|
+
for (const rootDir of candidateRoots) {
|
|
162
|
+
if (result.some(r => r.rootDir === rootDir)) continue
|
|
163
|
+
const absDir = rootDir === '.' ? cwd : join(cwd, rootDir)
|
|
164
|
+
if (!existsSync(join(absDir, 'src/pages'))) continue
|
|
165
|
+
const found = await evaluateCandidate(rootDir, cwd, isVueAppPkg, ignorePaths)
|
|
166
|
+
if (found) result.push(found)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return result
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Self-check конфігурації: `.n-rules.json` → `storybook.optOut` не має посилатись на
|
|
175
|
+
* неіснуючі workspace-пакети (застаріле налаштування — пакет перейменували/видалили, а
|
|
176
|
+
* opt-out лишився). Сама детекція скоупу (поріг, build, app-проєкти) — pure-функції вище,
|
|
177
|
+
* покриті тестами напряму; тут лише конфіг-гігієна.
|
|
178
|
+
* @param {import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintContext} ctx контекст лінту
|
|
179
|
+
* @returns {Promise<import('@7n/rules/scripts/lib/lint-surface/types.mjs').LintResult>} результат лінту
|
|
180
|
+
*/
|
|
181
|
+
export async function lint(ctx) {
|
|
182
|
+
const reporter = createViolationReporter(ctx)
|
|
183
|
+
const cwd = ctx.cwd
|
|
184
|
+
|
|
185
|
+
const optOut = await readStorybookOptOut(cwd)
|
|
186
|
+
if (optOut.length === 0) {
|
|
187
|
+
reporter.pass('storybook: storybook.optOut порожній або не заданий')
|
|
188
|
+
return reporter.result()
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const roots = new Set(await getMonorepoPackageRootDirs(cwd))
|
|
192
|
+
for (const rootDir of optOut) {
|
|
193
|
+
if (!roots.has(rootDir)) {
|
|
194
|
+
reporter.fail(
|
|
195
|
+
`.n-rules.json storybook.optOut містить '${rootDir}' — такого workspace-пакета немає (застаріле opt-out, storybook.mdc)`,
|
|
196
|
+
'stale-opt-out'
|
|
197
|
+
)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return reporter.result()
|
|
202
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: fix-vitest-config.mjs
|
|
4
|
+
resource: plugins/lang-js/rules/storybook/vitest-config/fix-vitest-config.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: b3961bc2
|
|
7
|
+
model: openai-codex/gpt-5.4-mini
|
|
8
|
+
tier: cloud-min
|
|
9
|
+
score: 100
|
|
10
|
+
issues: judge:inaccurate:0.98
|
|
11
|
+
judgeModel: openai-codex/gpt-5.4-mini
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Огляд
|
|
15
|
+
|
|
16
|
+
Файл детерміновано приводить `vitest.config.mjs` у Vue-компонентних пакетах із Storybook до канону `test.projects`: окремі проєкти `unit` і `storybook`, а browser-mode обмежує лише `chromium`. Для Stryker він генерує ізольований `vitest.stryker.config.*` у межах того ж пакета, згідно з підходом з `ADR Кластер 5`.
|
|
17
|
+
|
|
18
|
+
Для вже наявного `vitest.config.mjs` зміни вносяться точковими `string-splice`-ами лише на вставку, щоб не переписувати решту форматування й коментарів; після цього конфіг повторно парситься, і зміни відкочуються, якщо результат невалідний. Логіка побудови покладена на `buildFreshVitestConfig`, `buildStrykerConfig` і `patterns`, а читання й аналіз виконуються через `oxc-parser`. Детектор `main.mjs` працює read-only, тоді як запис зосереджений у `fix-stryker_config.mjs`.
|
|
19
|
+
|
|
20
|
+
## Поведінка
|
|
21
|
+
|
|
22
|
+
- `buildFreshVitestConfig` — створює новий `vitest.config.mjs` для пакета з канонічним `test.projects` для `unit` і `storybook`, підставляє шлях до локального Vite-конфіга та сторінковий glob для Storybook.
|
|
23
|
+
- `buildStrykerConfig` — генерує ізольований `vitest.stryker.config.*` на основі canonical baseline-шаблону для того самого пакета, підставляючи шлях до локального Vite-конфіга.
|
|
24
|
+
- `patterns` — описує autofix-сценарій, який реагує на відсутній або неканонічний vitest-конфіг, доповнює наявний файл без переписування зайвого форматування та створює `vitest.stryker.config.*` у відповідному package root.
|
|
25
|
+
|
|
26
|
+
## Публічний API
|
|
27
|
+
|
|
28
|
+
- buildFreshVitestConfig — Генерує повністю новий `vitest.config.mjs` (unit+storybook projects) для
|
|
29
|
+
пакета без жодного наявного vitest-конфіга. Експортовано — переюз у
|
|
30
|
+
`adopt/main.mjs` (генерація лише для повністю відсутніх файлів).
|
|
31
|
+
- buildStrykerConfig — Генерує ізольований `vitest.stryker.config.*` (той самий basename/ext що
|
|
32
|
+
й основний vitest-конфіг пакета) з canonical baseline-шаблону. Експортовано —
|
|
33
|
+
переюз у `adopt/main.mjs`.
|
|
34
|
+
- patterns — повертає набір шаблонів, які використовуються для зіставлення й обробки відповідних випадків у коді
|
|
35
|
+
|
|
36
|
+
## Гарантії поведінки
|
|
37
|
+
|
|
38
|
+
- (специфічних машинно-виведених гарантій немає)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Directory Index
|
|
3
|
+
title: plugins/lang-js/rules/storybook/vitest-config
|
|
4
|
+
resource: plugins/lang-js/rules/storybook/vitest-config/
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
| Файл | Тип |
|
|
8
|
+
| --------------------------------------------- | --------- |
|
|
9
|
+
| [fix-vitest-config.mjs](fix-vitest-config.md) | JS Module |
|
|
10
|
+
| [main.mjs](main.md) | JS Module |
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: JS Module
|
|
3
|
+
title: main.mjs
|
|
4
|
+
resource: plugins/lang-js/rules/storybook/vitest-config/main.mjs
|
|
5
|
+
docgen:
|
|
6
|
+
crc: f5db98b8
|
|
7
|
+
model: openai-codex/gpt-5.4-mini
|
|
8
|
+
tier: cloud-min
|
|
9
|
+
score: 85
|
|
10
|
+
issues: internal-name:detectStoriesGlob,anchor-miss:(vitest-config.mdc),judge:inaccurate:0.99
|
|
11
|
+
judgeModel: openai-codex/gpt-5.4-mini
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Огляд
|
|
15
|
+
|
|
16
|
+
Описує поведінку, за якою визначаються канонічні `vitest.config.*`, пов’язаний Stryker-конфіг, а також наявність і придатність `unit` та `storybook`-проєктів у пакеті. Для цього використовуються експортовані константи-рядки `REASON_VITEST_CONFIG_MISSING="vitest-config-missing"`, `REASON_STRYKER_CONFIG_MISSING="stryker-config-missing"`, `REASON_CONFIG_UNRESOLVABLE="vitest-config-unresolvable"`, `REASON_PROJECTS_DYNAMIC="projects-dynamic"`, `REASON_UNIT_PROJECT_MISSING="unit-project-missing"`, `REASON_STORYBOOK_PROJECT_MISSING="storybook-project-missing"`, `REASON_STORYBOOK_PROJECT_MARKER_MISSING="storybook-project-marker-missing"`, а повідомлення про такі стани передаються через маркери `vitest-config.mdc`.
|
|
17
|
+
|
|
18
|
+
## Поведінка
|
|
19
|
+
|
|
20
|
+
- `VITEST_CONFIG_NAMES` — задає канонічний пріоритет імен `vitest.config.*` для пошуку конфіга.
|
|
21
|
+
- `STORYBOOK_TEST_IMPORT` — містить обов’язковий import для резолву `storybookTest`.
|
|
22
|
+
- `REASON_VITEST_CONFIG_MISSING` — причина, коли в пакеті немає `vitest.config.*`.
|
|
23
|
+
- `REASON_STRYKER_CONFIG_MISSING` — причина, коли відсутній ізольований `vitest.stryker.config.*`.
|
|
24
|
+
- `REASON_CONFIG_UNRESOLVABLE` — причина, коли vitest-конфіг не вдається розпарсити або знайти в ньому `test`-блок.
|
|
25
|
+
- `REASON_PROJECTS_DYNAMIC` — причина, коли `test.projects` не є статичним масивом.
|
|
26
|
+
- `REASON_UNIT_PROJECT_MISSING` — причина, коли в `test.projects` немає `unit`.
|
|
27
|
+
- `REASON_STORYBOOK_PROJECT_MISSING` — причина, коли в `test.projects` немає `storybook`.
|
|
28
|
+
- `REASON_STORYBOOK_PROJECT_MARKER_MISSING` — причина, коли `storybook`-проєкт не має канонічних маркерів.
|
|
29
|
+
- `CHROMIUM_RE` — маркує наявність chromium-інстансу в `storybook`-проєкті.
|
|
30
|
+
- `BROWSER_KEY_RE` — маркує browser-mode в `storybook`-проєкті.
|
|
31
|
+
- `STORIES_RE` — маркує stories-glob у `storybook`-проєкті.
|
|
32
|
+
- `WHITESPACE_ONLY_RE` — визначає рядок, що складається лише з whitespace.
|
|
33
|
+
- `LEADING_COMMA_RE` — визначає фрагмент, що починається з коми з можливим whitespace.
|
|
34
|
+
- `resolveVitestConfigPath` — знаходить наявний `vitest.config.*` у корені пакета або повертає `null`.
|
|
35
|
+
- `resolveViteConfigName` — повертає ім’я доступного `vite.config.*` для пакета, а якщо його немає — `vite.config.js`.
|
|
36
|
+
- `strykerConfigPathFor` — будує шлях до поруч розташованого `vitest.stryker.config.*` з тим самим розширенням.
|
|
37
|
+
- `storiesGlobForVitestConfig` — повертає stories-glob у вигляді, придатному для vitest-конфіга в корені пакета.
|
|
38
|
+
- `parseModule` — парсить JS/TS модуль і повертає AST з діагностикою.
|
|
39
|
+
- `findTestObject` — знаходить у дереві AST `test`-об’єкт незалежно від обгорток конфігурації.
|
|
40
|
+
- `findProperty` — знаходить у об’єкті property за іменем.
|
|
41
|
+
- `classifyProjects` — визначає, чи є в `test.projects` `unit`, і витягає зріз `storybook`-запису для перевірки маркерів.
|
|
42
|
+
- `lint` — перевіряє всі пакети у скоупі на канонічний vitest-конфіг для `unit+storybook` і наявність ізольованого `vitest.stryker.config.*`, повідомляючи про порушення через маркери `vitest-config.mdc`.
|
|
43
|
+
|
|
44
|
+
## Публічний API
|
|
45
|
+
|
|
46
|
+
- STORYBOOK_TEST_IMPORT — Import, який має бути присутній у файлі, щоб `storybookTest(...)` резолвився.
|
|
47
|
+
- resolveVitestConfigPath — Резолвить абсолютний шлях наявного `vitest.config.*` пакета (перший знайдений
|
|
48
|
+
за пріоритетом {@link VITEST_CONFIG_NAMES}), або `null` якщо жодного немає.
|
|
49
|
+
- resolveViteConfigName — Резолвить ім'я `vite.config.*` пакета для import-шляху в baseline-шаблонах
|
|
50
|
+
(той самий файл, що й `viteFinal` у `.storybook/main.js`). Пакет у скоупі
|
|
51
|
+
гарантовано має один із них (`scope/main.mjs#hasStandardBuild`).
|
|
52
|
+
- strykerConfigPathFor — Шлях до ізольованого `vitest.stryker.config.*` — той самий каталог і
|
|
53
|
+
розширення, що й основний vitest-конфіг пакета.
|
|
54
|
+
- storiesGlobForVitestConfig — Stories-glob для vitest-конфіга пакета (на відміну від `detectStoriesGlob`
|
|
55
|
+
scaffold-концерна — той повертає шлях відносно `.storybook/`, тут vitest-конфіг
|
|
56
|
+
лежить у корені пакета, тож префікс `../` треба зняти).
|
|
57
|
+
- parseModule — Парсить JS/TS файл через oxc-parser з обраним lang за розширенням.
|
|
58
|
+
- findTestObject — Шукає перший `ObjectExpression` у дереві AST, що має property `test` зі
|
|
59
|
+
значенням-`ObjectExpression` — незалежно від того, чи огорнутий він у
|
|
60
|
+
`defineConfig(...)`/`mergeConfig(...)`, чи це простий об'єкт (стійко до
|
|
61
|
+
варіацій testing.mdc-канону).
|
|
62
|
+
- findProperty — Шукає property `name` у `ObjectExpression`.
|
|
63
|
+
- classifyProjects — Класифікує елементи масиву `test.projects`: чи є `unit`-проєкт, і
|
|
64
|
+
source-зріз елемента `storybook`-проєкту (для marker-перевірки).
|
|
65
|
+
- lint — Перевіряє канонічний vitest-конфіг (unit+storybook projects) і наявність
|
|
66
|
+
ізольованого `vitest.stryker.config` для всіх пакетів у скоупі Storybook.
|
|
67
|
+
Надішли, будь ласка, файл або вміст коду, до якого треба написати документацію.
|
|
68
|
+
|
|
69
|
+
## Гарантії поведінки
|
|
70
|
+
|
|
71
|
+
- Read-only: не виконує операцій запису (ФС/БД).
|
|
72
|
+
- Перехоплює помилки і не пропускає винятків назовні (fail-safe).
|