@nitra/cursor 1.8.105 → 1.8.106

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.
@@ -90,233 +90,310 @@ export function verifyOxlintRcE18e(cfg) {
90
90
  }
91
91
 
92
92
  /**
93
- * Перевіряє відповідність проєкту правилам js-lint.mdc
94
- * @returns {Promise<number>} 0 все OK, 1 є проблеми
93
+ * Перевіряє ESLint flat config файл.
94
+ * @param {(msg: string) => void} passFn callback при успішній перевірці
95
+ * @param {(msg: string) => void} failFn callback при помилці
95
96
  */
96
- export async function check() {
97
- const reporter = createCheckReporter()
98
- const { pass, fail } = reporter
99
-
97
+ async function checkEslintConfig(passFn, failFn) {
100
98
  let eslintPath = ''
101
99
  if (existsSync('eslint.config.js')) {
102
100
  eslintPath = 'eslint.config.js'
103
- pass('eslint.config.js існує')
101
+ passFn('eslint.config.js існує')
104
102
  } else if (existsSync('eslint.config.mjs')) {
105
103
  eslintPath = 'eslint.config.mjs'
106
- pass('eslint.config.mjs існує')
104
+ passFn('eslint.config.mjs існує')
107
105
  } else {
108
- fail('Відсутній eslint.config.js або eslint.config.mjs — flat config з getConfig (js-lint.mdc)')
106
+ failFn('Відсутній eslint.config.js або eslint.config.mjs — flat config з getConfig (js-lint.mdc)')
107
+ return
109
108
  }
110
-
111
- if (eslintPath) {
112
- const eslintRaw = await readFile(eslintPath, 'utf8')
113
- if (eslintRaw.includes('getConfig')) {
114
- pass(`${eslintPath}: містить getConfig`)
115
- } else {
116
- fail(`${eslintPath}: потрібен виклик getConfig (js-lint.mdc)`)
117
- }
118
- if (eslintRaw.includes('@nitra/eslint-config')) {
119
- pass(`${eslintPath}: імпорт @nitra/eslint-config`)
120
- } else {
121
- fail(`${eslintPath}: імпортуй getConfig з @nitra/eslint-config`)
109
+ const eslintRaw = await readFile(eslintPath, 'utf8')
110
+ const checks = [
111
+ {
112
+ needle: 'getConfig',
113
+ ok: `${eslintPath}: містить getConfig`,
114
+ err: `${eslintPath}: потрібен виклик getConfig (js-lint.mdc)`
115
+ },
116
+ {
117
+ needle: '@nitra/eslint-config',
118
+ ok: `${eslintPath}: імпорт @nitra/eslint-config`,
119
+ err: `${eslintPath}: імпортуй getConfig з @nitra/eslint-config`
120
+ },
121
+ {
122
+ needle: '**/auto-imports.d.ts',
123
+ ok: `${eslintPath}: ignores містить **/auto-imports.d.ts`,
124
+ err: `${eslintPath}: додай у ignores запис **/auto-imports.d.ts (js-lint.mdc)`
122
125
  }
123
- if (eslintRaw.includes('**/auto-imports.d.ts')) {
124
- pass(`${eslintPath}: ignores містить **/auto-imports.d.ts`)
126
+ ]
127
+ for (const { needle, ok, err } of checks) {
128
+ if (eslintRaw.includes(needle)) {
129
+ passFn(ok)
125
130
  } else {
126
- fail(`${eslintPath}: додай у ignores запис **/auto-imports.d.ts (js-lint.mdc)`)
131
+ failFn(err)
127
132
  }
128
133
  }
134
+ }
129
135
 
130
- if (existsSync('package.json')) {
131
- const pkg = JSON.parse(await readFile('package.json', 'utf8'))
136
+ /**
137
+ * Перевіряє залежності lint-js у package.json (prettier, `@nitra/eslint-config`).
138
+ * @param {{ dependencies?: Record<string, string>, devDependencies?: Record<string, string> }} pkg parsed package.json
139
+ * @param {(msg: string) => void} passFn callback при успішній перевірці
140
+ * @param {(msg: string) => void} failFn callback при помилці
141
+ */
142
+ function checkPackageJsonLintDeps(pkg, passFn, failFn) {
143
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies }
144
+ if (allDeps.prettier) {
145
+ failFn('package.json: видали залежність prettier (oxfmt замість prettier, js-lint.mdc)')
146
+ } else {
147
+ passFn('package.json не містить prettier')
148
+ }
149
+ if (allDeps['@nitra/prettier-config']) {
150
+ failFn('package.json: видали @nitra/prettier-config (js-lint.mdc)')
151
+ } else {
152
+ passFn('package.json не містить @nitra/prettier-config')
153
+ }
132
154
 
133
- if (pkg.scripts?.['lint-js']) {
134
- pass('package.json містить скрипт lint-js')
135
- const lintJs = String(pkg.scripts['lint-js'])
136
- if (isCanonicalLintJs(lintJs)) {
137
- pass(`lint-js збігається з каноном: ${CANONICAL_LINT_JS}`)
138
- } else {
139
- fail(
140
- `lint-js має бути рівно: "${CANONICAL_LINT_JS}" (див. js-lint.mdc / check-js-lint.mjs). Зараз: ${JSON.stringify(normalizeLintJsScript(lintJs))}`
141
- )
142
- }
155
+ const nitraEslint = pkg.devDependencies?.['@nitra/eslint-config']
156
+ if (nitraEslint) {
157
+ passFn('@nitra/eslint-config є в devDependencies')
158
+ if (nitraEslintConfigDeclaresE18eTransitive(nitraEslint)) {
159
+ passFn('@nitra/eslint-config: мінімум 3.5.0 (транзитивний @e18e/eslint-plugin для oxlint jsPlugins, js-lint.mdc)')
143
160
  } else {
144
- fail(`package.json не містить скрипт "lint-js" — додай: ${JSON.stringify(CANONICAL_LINT_JS)}`)
161
+ failFn(
162
+ '@nitra/eslint-config: онови до мінімум "^3.5.0" — з цієї версії постачається @e18e/eslint-plugin для .oxlintrc.json (js-lint.mdc)'
163
+ )
145
164
  }
165
+ } else {
166
+ failFn('@nitra/eslint-config відсутній в devDependencies — додай: bun add -d @nitra/eslint-config')
167
+ }
168
+ }
146
169
 
147
- const allDeps = { ...pkg.dependencies, ...pkg.devDependencies }
148
- if (allDeps.prettier) {
149
- fail('package.json: видали залежність prettier (oxfmt замість prettier, js-lint.mdc)')
150
- } else {
151
- pass('package.json не містить prettier')
152
- }
153
- if (allDeps['@nitra/prettier-config']) {
154
- fail('package.json: видали @nitra/prettier-config (js-lint.mdc)')
155
- } else {
156
- pass('package.json не містить @nitra/prettier-config')
157
- }
170
+ /**
171
+ * Перевіряє package.json на lint-js, prettier, eslint-config, engines.node.
172
+ * @param {(msg: string) => void} passFn callback при успішній перевірці
173
+ * @param {(msg: string) => void} failFn callback при помилці
174
+ */
175
+ async function checkPackageJsonJsLint(passFn, failFn) {
176
+ if (!existsSync('package.json')) return
177
+ const pkg = JSON.parse(await readFile('package.json', 'utf8'))
158
178
 
159
- const nitraEslint = pkg.devDependencies?.['@nitra/eslint-config']
160
- if (nitraEslint) {
161
- pass('@nitra/eslint-config є в devDependencies')
162
- if (nitraEslintConfigDeclaresE18eTransitive(nitraEslint)) {
163
- pass('@nitra/eslint-config: мінімум 3.5.0 (транзитивний @e18e/eslint-plugin для oxlint jsPlugins, js-lint.mdc)')
164
- } else {
165
- fail(
166
- '@nitra/eslint-config: онови до мінімум "^3.5.0" — з цієї версії постачається @e18e/eslint-plugin для .oxlintrc.json (js-lint.mdc)'
167
- )
168
- }
179
+ const lintJs = pkg.scripts?.['lint-js']
180
+ if (lintJs) {
181
+ passFn('package.json містить скрипт lint-js')
182
+ if (isCanonicalLintJs(String(lintJs))) {
183
+ passFn(`lint-js збігається з каноном: ${CANONICAL_LINT_JS}`)
169
184
  } else {
170
- fail('@nitra/eslint-config відсутній в devDependencies — додай: bun add -d @nitra/eslint-config')
185
+ failFn(
186
+ `lint-js має бути рівно: "${CANONICAL_LINT_JS}" (див. js-lint.mdc / check-js-lint.mjs). Зараз: ${JSON.stringify(normalizeLintJsScript(String(lintJs)))}`
187
+ )
171
188
  }
189
+ } else {
190
+ failFn(`package.json не містить скрипт "lint-js" — додай: ${JSON.stringify(CANONICAL_LINT_JS)}`)
191
+ }
172
192
 
173
- const nodeEngine = pkg.engines?.node
174
- if (nodeEngine) {
175
- const firstNumeric = String(nodeEngine).split(NON_DIGITS_RE).find(Boolean)
176
- if (firstNumeric && Number(firstNumeric) >= 24) {
177
- pass(`engines.node: "${nodeEngine}"`)
178
- } else {
179
- fail(`engines.node: "${nodeEngine}" — має бути >=24`)
180
- }
193
+ checkPackageJsonLintDeps(pkg, passFn, failFn)
194
+
195
+ const nodeEngine = pkg.engines?.node
196
+ if (nodeEngine) {
197
+ const firstNumeric = String(nodeEngine).split(NON_DIGITS_RE).find(Boolean)
198
+ if (firstNumeric && Number(firstNumeric) >= 24) {
199
+ passFn(`engines.node: "${nodeEngine}"`)
181
200
  } else {
182
- fail('package.json не містить engines.node — додай: "engines": { "node": ">=24" }')
201
+ failFn(`engines.node: "${nodeEngine}" має бути >=24`)
183
202
  }
203
+ } else {
204
+ failFn('package.json не містить engines.node — додай: "engines": { "node": ">=24" }')
184
205
  }
206
+ }
185
207
 
186
- if (existsSync('.oxlintrc.json')) {
187
- let oxCfg
188
- try {
189
- oxCfg = JSON.parse(await readFile('.oxlintrc.json', 'utf8'))
190
- } catch {
191
- fail('.oxlintrc.json не є валідним JSON')
192
- oxCfg = null
193
- }
194
- if (oxCfg !== null) {
195
- pass('.oxlintrc.json існує')
196
- const oxV = verifyOxlintRcE18e(oxCfg)
197
- if (oxV.ok) {
198
- pass('.oxlintrc.json: jsPlugins з @e18e/eslint-plugin і e18e/prefer-includes: error')
199
- } else {
200
- for (const msg of oxV.failures) {
201
- fail(msg)
202
- }
203
- }
208
+ /**
209
+ * Перевіряє .oxlintrc.json.
210
+ * @param {(msg: string) => void} passFn callback при успішній перевірці
211
+ * @param {(msg: string) => void} failFn callback при помилці
212
+ */
213
+ async function checkOxlintRc(passFn, failFn) {
214
+ if (!existsSync('.oxlintrc.json')) {
215
+ failFn('.oxlintrc.json не існує — додай конфіг oxlint (js-lint.mdc)')
216
+ return
217
+ }
218
+ let oxCfg
219
+ try {
220
+ oxCfg = JSON.parse(await readFile('.oxlintrc.json', 'utf8'))
221
+ } catch {
222
+ failFn('.oxlintrc.json не є валідним JSON')
223
+ return
224
+ }
225
+ passFn('.oxlintrc.json існує')
226
+ const oxV = verifyOxlintRcE18e(oxCfg)
227
+ if (oxV.ok) {
228
+ passFn('.oxlintrc.json: jsPlugins з @e18e/eslint-plugin і e18e/prefer-includes: error')
229
+ } else {
230
+ for (const msg of oxV.failures) {
231
+ failFn(msg)
204
232
  }
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Перевіряє .vscode/extensions.json на потрібні розширення.
238
+ * @param {(msg: string) => void} passFn callback при успішній перевірці
239
+ * @param {(msg: string) => void} failFn callback при помилці
240
+ */
241
+ async function checkVscodeExtensions(passFn, failFn) {
242
+ if (!existsSync('.vscode/extensions.json')) {
243
+ failFn('.vscode/extensions.json не існує — додай recommendations з js-lint.mdc (див. check-js-lint.mjs)')
244
+ return
245
+ }
246
+ let ext
247
+ try {
248
+ ext = JSON.parse(await readFile('.vscode/extensions.json', 'utf8'))
249
+ } catch {
250
+ failFn('.vscode/extensions.json не є валідним JSON')
251
+ return
252
+ }
253
+ const rec = ext.recommendations
254
+ if (!Array.isArray(rec)) {
255
+ failFn('.vscode/extensions.json: поле recommendations має бути масивом')
256
+ return
257
+ }
258
+ const missing = REQUIRED_VSCODE_EXTENSIONS.filter(id => !rec.includes(id))
259
+ if (missing.length > 0) {
260
+ failFn(`.vscode/extensions.json: додай у recommendations: ${missing.join(', ')} (мінімум для js-lint.mdc)`)
205
261
  } else {
206
- fail('.oxlintrc.json не існує додай конфіг oxlint (js-lint.mdc)')
262
+ passFn('.vscode/extensions.json: є рекомендації oxlint, eslint і GitHub Actions')
207
263
  }
264
+ }
208
265
 
209
- if (existsSync('.vscode/extensions.json')) {
210
- let ext
211
- try {
212
- ext = JSON.parse(await readFile('.vscode/extensions.json', 'utf8'))
213
- } catch {
214
- fail('.vscode/extensions.json не є валідним JSON')
215
- ext = null
266
+ /**
267
+ * Перевіряє lint-js.yml workflow (fallback — текстовий пошук).
268
+ * @param {string} content вміст workflow файлу
269
+ * @param {(msg: string) => void} passFn callback при успішній перевірці
270
+ * @param {(msg: string) => void} failFn callback при помилці
271
+ */
272
+ function checkLintJsWorkflowFallback(content, passFn, failFn) {
273
+ const checks = [
274
+ ['actions/checkout@v6', 'lint-js.yml: потрібен крок actions/checkout@v6 (ga.mdc)'],
275
+ ['persist-credentials: false', 'lint-js.yml: checkout з persist-credentials: false'],
276
+ ['./.github/actions/setup-bun-deps', 'lint-js.yml: потрібен uses: ./.github/actions/setup-bun-deps'],
277
+ ['bunx oxlint', 'lint-js.yml: у run має бути bunx oxlint'],
278
+ ['bunx eslint .', 'lint-js.yml: у run має бути bunx eslint . (без --fix у CI)'],
279
+ ['bunx jscpd .', 'lint-js.yml: у run має бути bunx jscpd .']
280
+ ]
281
+ for (const [needle, errMsg] of checks) {
282
+ if (content.includes(needle)) {
283
+ passFn(`lint-js.yml містить: ${needle}`)
284
+ } else {
285
+ failFn(errMsg)
216
286
  }
217
- if (ext) {
218
- const rec = ext.recommendations
219
- if (Array.isArray(rec)) {
220
- const missing = REQUIRED_VSCODE_EXTENSIONS.filter(id => !rec.includes(id))
221
- if (missing.length > 0) {
222
- fail(`.vscode/extensions.json: додай у recommendations: ${missing.join(', ')} (мінімум для js-lint.mdc)`)
223
- } else {
224
- pass('.vscode/extensions.json: є рекомендації oxlint, eslint і GitHub Actions')
225
- }
226
- } else {
227
- fail('.vscode/extensions.json: поле recommendations має бути масивом')
287
+ }
288
+ if (content.includes('bunx oxlint') && OXLINT_FIX_RE.test(content)) {
289
+ failFn('lint-js.yml: у CI не використовуй bunx oxlint --fix (лише bunx oxlint)')
290
+ }
291
+ if (content.includes('eslint --fix')) {
292
+ failFn('lint-js.yml: у CI не використовуй eslint --fix (лише bunx eslint .)')
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Перевіряє вміст lint-js.yml через YAML або fallback.
298
+ * @param {string} content вміст файлу
299
+ * @param {(msg: string) => void} passFn callback при успішній перевірці
300
+ * @param {(msg: string) => void} failFn callback при помилці
301
+ */
302
+ function checkLintJsYmlContent(content, passFn, failFn) {
303
+ const root = parseWorkflowYaml(content)
304
+ if (root) {
305
+ const v = verifyLintJsWorkflowStructure(root)
306
+ if (v.ok) {
307
+ passFn('lint-js.yml: кроки checkout, setup-bun-deps, oxlint/eslint/jscpd (YAML + кроки)')
308
+ } else {
309
+ for (const msg of v.failures) {
310
+ failFn(`lint-js.yml: ${msg}`)
228
311
  }
229
312
  }
230
313
  } else {
231
- fail('.vscode/extensions.json не існує — додай recommendations з js-lint.mdc (див. check-js-lint.mjs)')
314
+ checkLintJsWorkflowFallback(content, passFn, failFn)
232
315
  }
316
+ }
233
317
 
318
+ /**
319
+ * Перевіряє lint-js.yml і lint.yml workflow.
320
+ * @param {(msg: string) => void} passFn callback при успішній перевірці
321
+ * @param {(msg: string) => void} failFn callback при помилці
322
+ */
323
+ async function checkLintJsWorkflows(passFn, failFn) {
234
324
  if (existsSync('.github/workflows/lint-js.yml')) {
235
325
  const content = await readFile('.github/workflows/lint-js.yml', 'utf8')
236
- pass('lint-js.yml існує')
237
- const root = parseWorkflowYaml(content)
238
- if (root) {
239
- const v = verifyLintJsWorkflowStructure(root)
240
- if (v.ok) {
241
- pass('lint-js.yml: кроки checkout, setup-bun-deps, oxlint/eslint/jscpd (YAML + кроки)')
242
- } else {
243
- for (const msg of v.failures) {
244
- fail(`lint-js.yml: ${msg}`)
245
- }
246
- }
247
- } else {
248
- const checks = [
249
- ['actions/checkout@v6', 'lint-js.yml: потрібен крок actions/checkout@v6 (ga.mdc)'],
250
- ['persist-credentials: false', 'lint-js.yml: checkout з persist-credentials: false'],
251
- ['./.github/actions/setup-bun-deps', 'lint-js.yml: потрібен uses: ./.github/actions/setup-bun-deps'],
252
- ['bunx oxlint', 'lint-js.yml: у run має бути bunx oxlint'],
253
- ['bunx eslint .', 'lint-js.yml: у run має бути bunx eslint . (без --fix у CI)'],
254
- ['bunx jscpd .', 'lint-js.yml: у run має бути bunx jscpd .']
255
- ]
256
- for (const [needle, errMsg] of checks) {
257
- if (content.includes(needle)) {
258
- pass(`lint-js.yml містить: ${needle}`)
259
- } else {
260
- fail(errMsg)
261
- }
262
- }
263
- if (content.includes('bunx oxlint') && OXLINT_FIX_RE.test(content)) {
264
- fail('lint-js.yml: у CI не використовуй bunx oxlint --fix (лише bunx oxlint)')
265
- }
266
- if (content.includes('eslint --fix')) {
267
- fail('lint-js.yml: у CI не використовуй eslint --fix (лише bunx eslint .)')
268
- }
269
- }
326
+ passFn('lint-js.yml існує')
327
+ checkLintJsYmlContent(content, passFn, failFn)
270
328
  } else {
271
- fail('.github/workflows/lint-js.yml не існує — створи його (див. check-js-lint.mjs / js-lint.mdc)')
329
+ failFn('.github/workflows/lint-js.yml не існує — створи його (див. check-js-lint.mjs / js-lint.mdc)')
272
330
  }
273
331
 
274
332
  if (existsSync('.github/workflows/lint.yml')) {
275
333
  const lintYml = await readFile('.github/workflows/lint.yml', 'utf8')
276
- const looksLikeJsLint = lintYml.includes('bunx oxlint') && lintYml.includes('bunx eslint') && lintYml.includes('jscpd')
277
- if (looksLikeJsLint) {
278
- fail('.github/workflows/lint.yml дублює кроки lint-js.yml — залиш один workflow на лінт JS (js-lint.mdc)')
334
+ if (lintYml.includes('bunx oxlint') && lintYml.includes('bunx eslint') && lintYml.includes('jscpd')) {
335
+ failFn('.github/workflows/lint.yml дублює кроки lint-js.yml — залиш один workflow на лінт JS (js-lint.mdc)')
279
336
  } else {
280
- pass('.github/workflows/lint.yml не дублює oxlint/eslint/jscpd з lint-js.yml')
337
+ passFn('.github/workflows/lint.yml не дублює oxlint/eslint/jscpd з lint-js.yml')
281
338
  }
282
339
  }
340
+ }
283
341
 
284
- if (existsSync('.jscpd.json')) {
285
- let jscpdCfg
286
- try {
287
- jscpdCfg = JSON.parse(await readFile('.jscpd.json', 'utf8'))
288
- } catch {
289
- fail('.jscpd.json не є валідним JSON')
290
- jscpdCfg = null
291
- }
292
- if (jscpdCfg) {
293
- pass('.jscpd.json існує')
294
- if (jscpdCfg.gitignore === true) {
295
- pass('.jscpd.json: gitignore увімкнено')
296
- } else {
297
- fail('.jscpd.json має містити "gitignore": true')
298
- }
299
- if (jscpdCfg.exitCode === 1) {
300
- pass('.jscpd.json: exitCode 1 при дублікатах')
301
- } else {
302
- fail('.jscpd.json має містити "exitCode": 1 (інакше CI не впаде на клонах)')
303
- }
304
- const reporters = jscpdCfg.reporters
305
- if (Array.isArray(reporters) && reporters.includes('console')) {
306
- pass('.jscpd.json: reporters містить console')
307
- } else {
308
- fail('.jscpd.json має містити "reporters": ["console"] (або масив із "console")')
309
- }
310
- const minLines = jscpdCfg.minLines
311
- if (typeof minLines === 'number' && minLines >= 25) {
312
- pass(`.jscpd.json: minLines ${minLines} (>=25)`)
313
- } else {
314
- fail('.jscpd.json має містити "minLines" як число >= 25')
315
- }
316
- }
342
+ /**
343
+ * Перевіряє .jscpd.json.
344
+ * @param {(msg: string) => void} passFn callback при успішній перевірці
345
+ * @param {(msg: string) => void} failFn callback при помилці
346
+ */
347
+ async function checkJscpdConfig(passFn, failFn) {
348
+ if (!existsSync('.jscpd.json')) {
349
+ failFn('.jscpd.json не існує — створи з полями згідно check js-lint')
350
+ return
351
+ }
352
+ let jscpdCfg
353
+ try {
354
+ jscpdCfg = JSON.parse(await readFile('.jscpd.json', 'utf8'))
355
+ } catch {
356
+ failFn('.jscpd.json не є валідним JSON')
357
+ return
358
+ }
359
+ passFn('.jscpd.json існує')
360
+ if (jscpdCfg.gitignore === true) {
361
+ passFn('.jscpd.json: gitignore увімкнено')
362
+ } else {
363
+ failFn('.jscpd.json має містити "gitignore": true')
364
+ }
365
+ if (jscpdCfg.exitCode === 1) {
366
+ passFn('.jscpd.json: exitCode 1 при дублікатах')
367
+ } else {
368
+ failFn('.jscpd.json має містити "exitCode": 1 (інакше CI не впаде на клонах)')
369
+ }
370
+ if (Array.isArray(jscpdCfg.reporters) && jscpdCfg.reporters.includes('console')) {
371
+ passFn('.jscpd.json: reporters містить console')
317
372
  } else {
318
- fail('.jscpd.json не існує створи з полями згідно check js-lint')
373
+ failFn('.jscpd.json має містити "reporters": ["console"] (або масив із "console")')
319
374
  }
375
+ const minLines = jscpdCfg.minLines
376
+ if (typeof minLines === 'number' && minLines >= 25) {
377
+ passFn(`.jscpd.json: minLines ${minLines} (>=25)`)
378
+ } else {
379
+ failFn('.jscpd.json має містити "minLines" як число >= 25')
380
+ }
381
+ }
382
+
383
+ /**
384
+ * Перевіряє відповідність проєкту правилам js-lint.mdc
385
+ * @returns {Promise<number>} 0 — все OK, 1 — є проблеми
386
+ */
387
+ export async function check() {
388
+ const reporter = createCheckReporter()
389
+ const { pass, fail } = reporter
390
+
391
+ await checkEslintConfig(pass, fail)
392
+ await checkPackageJsonJsLint(pass, fail)
393
+ await checkOxlintRc(pass, fail)
394
+ await checkVscodeExtensions(pass, fail)
395
+ await checkLintJsWorkflows(pass, fail)
396
+ await checkJscpdConfig(pass, fail)
320
397
 
321
398
  for (const dup of ['.eslintrc', '.eslintrc.js', '.eslintrc.json', '.eslintrc.yml']) {
322
399
  if (existsSync(dup)) fail(`Знайдено застарілий конфіг ESLint: ${dup} — видали, використовуй flat config`)
@@ -949,10 +949,7 @@ function formatKustomizePatchTargetForMessage(target) {
949
949
  */
950
950
  function failIfExplicitPatchTargetsNotInCatalog(rel, first, catalog, fail) {
951
951
  for (const { section, index, target } of extractExplicitPatchTargetsFromKustomization(first)) {
952
- if (
953
- shouldValidateKustomizePatchTarget(target) &&
954
- !kustomizeResourceCatalogMatchesPatchTarget(catalog, target)
955
- ) {
952
+ if (shouldValidateKustomizePatchTarget(target) && !kustomizeResourceCatalogMatchesPatchTarget(catalog, target)) {
956
953
  fail(
957
954
  `${rel}: ${section}[${index}].target — немає відповідного ресурсу в resources/bases/components/crds (рекурсивно): ${formatKustomizePatchTargetForMessage(target)}`
958
955
  )
@@ -1163,7 +1160,16 @@ async function validatePatchTargetsOneKustomizationFile(root, kustAbs, rootNorm,
1163
1160
  const kustNs = typeof rec.namespace === 'string' && rec.namespace.trim() !== '' ? rec.namespace.trim() : ''
1164
1161
  failIfExplicitPatchTargetsNotInCatalog(rel, first, catalog, fail)
1165
1162
  await failIfPathOnlyPatchesNotInCatalog(rel, rec.patches, kustDir, rootNorm, root, catalog, kustNs, fail)
1166
- await failIfStrategicMergePatchesNotInCatalog(rel, rec.patchesStrategicMerge, kustDir, rootNorm, root, catalog, kustNs, fail)
1163
+ await failIfStrategicMergePatchesNotInCatalog(
1164
+ rel,
1165
+ rec.patchesStrategicMerge,
1166
+ kustDir,
1167
+ rootNorm,
1168
+ root,
1169
+ catalog,
1170
+ kustNs,
1171
+ fail
1172
+ )
1167
1173
  }
1168
1174
 
1169
1175
  /**
@@ -1607,7 +1613,9 @@ async function auditJson6902PatchExternalFile(rel, resolved, root, patchRef, fai
1607
1613
  return
1608
1614
  }
1609
1615
  const relPatch = (relative(root, resolved) || patchRef).replaceAll('\\', '/')
1610
- fail(`${rel}: patch-файл «${relPatch}»: один path має і remove, і add — оформи як op: replace (k8s.mdc): ${bad.join(', ')}`)
1616
+ fail(
1617
+ `${rel}: patch-файл «${relPatch}»: один path має і remove, і add — оформи як op: replace (k8s.mdc): ${bad.join(', ')}`
1618
+ )
1611
1619
  }
1612
1620
 
1613
1621
  /**
@@ -1621,15 +1629,7 @@ async function auditJson6902PatchExternalFile(rel, resolved, root, patchRef, fai
1621
1629
  * @param {(msg: string) => void} fail реєстрація порушення
1622
1630
  * @returns {Promise<void>}
1623
1631
  */
1624
- async function auditOneKustomizationJson6902Patch(
1625
- rel,
1626
- pr,
1627
- patchIdx,
1628
- kustAbs,
1629
- rootNorm,
1630
- root,
1631
- fail
1632
- ) {
1632
+ async function auditOneKustomizationJson6902Patch(rel, pr, patchIdx, kustAbs, rootNorm, root, fail) {
1633
1633
  if (typeof pr.patch === 'string' && pr.patch.trim() !== '') {
1634
1634
  failIfJson6902RemoveAddConflictOnSamePath(rel, `patches[${patchIdx}] inline JSON6902`, pr.patch, fail)
1635
1635
  }
@@ -2378,15 +2378,7 @@ export function isK8sBaseManifestYamlPath(rel, baseLower) {
2378
2378
  * @param {(msg: string) => void} fail реєстрація помилки
2379
2379
  * @returns {void}
2380
2380
  */
2381
- function failIfK8sPolicyNamespaceRulesViolated(
2382
- rel,
2383
- docIndex,
2384
- obj,
2385
- skipMetaNs,
2386
- inBaseManifest,
2387
- kustomizeManaged,
2388
- fail
2389
- ) {
2381
+ function failIfK8sPolicyNamespaceRulesViolated(rel, docIndex, obj, skipMetaNs, inBaseManifest, kustomizeManaged, fail) {
2390
2382
  if (skipMetaNs) {
2391
2383
  return
2392
2384
  }
@@ -2479,15 +2471,7 @@ function validateK8sYamlPolicyDocuments(rel, baseLower, body, fail, kustomizeMan
2479
2471
  fail(`${rel}: YAML (документ ${di + 1}): ${doc.errors.map(e => e.message).join('; ')}`)
2480
2472
  } else {
2481
2473
  const obj = doc.toJSON()
2482
- failIfK8sPolicyNamespaceRulesViolated(
2483
- rel,
2484
- di + 1,
2485
- obj,
2486
- skipMetaNs,
2487
- inBaseManifest,
2488
- kustomizeManaged,
2489
- fail
2490
- )
2474
+ failIfK8sPolicyNamespaceRulesViolated(rel, di + 1, obj, skipMetaNs, inBaseManifest, kustomizeManaged, fail)
2491
2475
  failIfK8sPolicyResourceRulesViolated(rel, baseLower, di + 1, obj, fail)
2492
2476
  }
2493
2477
  }