@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.
- package/bin/auto-rules.md +2 -2
- package/bin/n-cursor.js +5 -15
- package/mdc/k8s.mdc +11 -10
- package/package.json +1 -1
- package/scripts/check-abie.mjs +515 -528
- package/scripts/check-bun.mjs +106 -78
- package/scripts/check-ga.mjs +151 -119
- package/scripts/check-js-lint.mjs +256 -179
- package/scripts/check-k8s.mjs +17 -33
- package/scripts/check-nginx-default-tpl.mjs +109 -91
- package/scripts/check-npm-module.mjs +163 -116
- package/scripts/check-style-lint.mjs +74 -61
- package/scripts/check-text.mjs +289 -209
- package/scripts/check-vue.mjs +108 -67
- package/scripts/utils/gha-workflow.mjs +3 -1
|
@@ -90,233 +90,310 @@ export function verifyOxlintRcE18e(cfg) {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
|
-
* Перевіряє
|
|
94
|
-
* @
|
|
93
|
+
* Перевіряє ESLint flat config файл.
|
|
94
|
+
* @param {(msg: string) => void} passFn callback при успішній перевірці
|
|
95
|
+
* @param {(msg: string) => void} failFn callback при помилці
|
|
95
96
|
*/
|
|
96
|
-
|
|
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
|
-
|
|
101
|
+
passFn('eslint.config.js існує')
|
|
104
102
|
} else if (existsSync('eslint.config.mjs')) {
|
|
105
103
|
eslintPath = 'eslint.config.mjs'
|
|
106
|
-
|
|
104
|
+
passFn('eslint.config.mjs існує')
|
|
107
105
|
} else {
|
|
108
|
-
|
|
106
|
+
failFn('Відсутній eslint.config.js або eslint.config.mjs — flat config з getConfig (js-lint.mdc)')
|
|
107
|
+
return
|
|
109
108
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
124
|
-
|
|
126
|
+
]
|
|
127
|
+
for (const { needle, ok, err } of checks) {
|
|
128
|
+
if (eslintRaw.includes(needle)) {
|
|
129
|
+
passFn(ok)
|
|
125
130
|
} else {
|
|
126
|
-
|
|
131
|
+
failFn(err)
|
|
127
132
|
}
|
|
128
133
|
}
|
|
134
|
+
}
|
|
129
135
|
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
262
|
+
passFn('.vscode/extensions.json: є рекомендації oxlint, eslint і GitHub Actions')
|
|
207
263
|
}
|
|
264
|
+
}
|
|
208
265
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
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
|
-
|
|
237
|
-
|
|
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
|
-
|
|
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
|
-
|
|
277
|
-
|
|
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
|
-
|
|
337
|
+
passFn('.github/workflows/lint.yml не дублює oxlint/eslint/jscpd з lint-js.yml')
|
|
281
338
|
}
|
|
282
339
|
}
|
|
340
|
+
}
|
|
283
341
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
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
|
-
|
|
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`)
|
package/scripts/check-k8s.mjs
CHANGED
|
@@ -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(
|
|
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(
|
|
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
|
}
|