@nitra/cursor 1.4.1 → 1.5.1
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/README.md +6 -6
- package/bin/n-cursor.js +34 -19
- package/mdc/bun.mdc +8 -2
- package/mdc/js-pino.mdc +1 -1
- package/mdc/style-lint.mdc +1 -1
- package/mdc/text.mdc +218 -0
- package/mdc/vue.mdc +0 -2
- package/package.json +1 -1
- package/scripts/check-bun.mjs +22 -7
- package/scripts/check-ga.mjs +22 -11
- package/scripts/check-js-format.mjs +26 -12
- package/scripts/check-js-lint.mjs +29 -14
- package/scripts/check-js-pino.mjs +2 -1
- package/scripts/check-nginx-default-tpl.mjs +27 -16
- package/scripts/check-npm-module.mjs +22 -9
- package/scripts/check-style-lint.mjs +37 -18
- package/scripts/check-text.mjs +139 -0
- package/scripts/check-vue.mjs +42 -19
- package/scripts/utils/pass.mjs +7 -0
- package/skills/{n-fix-cursor → n-fix}/SKILL.md +7 -2
- package/skills/n-publish-telegram/SKILL.md +8 -7
- package/mdc/spell.mdc +0 -155
- package/scripts/check-spell.mjs +0 -57
- package/skills/n-lint/SKILL.md +0 -26
|
@@ -1,34 +1,41 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
2
|
import { readFile } from 'node:fs/promises'
|
|
3
3
|
|
|
4
|
+
import { pass } from './utils/pass.mjs'
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Перевіряє відповідність проєкту правилам js-lint.mdc
|
|
6
8
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
7
9
|
*/
|
|
8
10
|
export async function check() {
|
|
9
11
|
let exitCode = 0
|
|
10
|
-
const pass = msg => console.log(` ✅ ${msg}`)
|
|
11
12
|
const fail = msg => {
|
|
12
13
|
console.log(` ❌ ${msg}`)
|
|
13
14
|
exitCode = 1
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
existsSync('eslint.config.js')
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
if (existsSync('eslint.config.js')) {
|
|
18
|
+
pass('eslint.config.js існує')
|
|
19
|
+
} else if (existsSync('eslint.config.mjs')) {
|
|
20
|
+
pass('eslint.config.mjs існує')
|
|
21
|
+
} else {
|
|
22
|
+
fail('Відсутній eslint.config.js — створи його з getConfig від @nitra/eslint-config')
|
|
23
|
+
}
|
|
21
24
|
|
|
22
25
|
if (existsSync('package.json')) {
|
|
23
26
|
const pkg = JSON.parse(await readFile('package.json', 'utf8'))
|
|
24
27
|
|
|
25
|
-
pkg.scripts?.['lint-js']
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
if (pkg.scripts?.['lint-js']) {
|
|
29
|
+
pass('package.json містить скрипт lint-js')
|
|
30
|
+
} else {
|
|
31
|
+
fail('package.json не містить скрипт "lint-js" — додай: "oxlint --fix && bunx eslint --fix ."')
|
|
32
|
+
}
|
|
28
33
|
|
|
29
|
-
pkg.devDependencies?.['@nitra/eslint-config']
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
if (pkg.devDependencies?.['@nitra/eslint-config']) {
|
|
35
|
+
pass('@nitra/eslint-config є в devDependencies')
|
|
36
|
+
} else {
|
|
37
|
+
fail('@nitra/eslint-config відсутній в devDependencies — додай: bun add -d @nitra/eslint-config')
|
|
38
|
+
}
|
|
32
39
|
|
|
33
40
|
const nodeEngine = pkg.engines?.node
|
|
34
41
|
if (nodeEngine) {
|
|
@@ -46,8 +53,16 @@ export async function check() {
|
|
|
46
53
|
if (existsSync('.github/workflows/lint-js.yml')) {
|
|
47
54
|
const content = await readFile('.github/workflows/lint-js.yml', 'utf8')
|
|
48
55
|
pass('lint-js.yml існує')
|
|
49
|
-
content.includes('oxlint')
|
|
50
|
-
|
|
56
|
+
if (content.includes('oxlint')) {
|
|
57
|
+
pass('lint-js.yml містить oxlint')
|
|
58
|
+
} else {
|
|
59
|
+
fail('lint-js.yml не містить oxlint')
|
|
60
|
+
}
|
|
61
|
+
if (content.includes('eslint')) {
|
|
62
|
+
pass('lint-js.yml містить eslint')
|
|
63
|
+
} else {
|
|
64
|
+
fail('lint-js.yml не містить eslint')
|
|
65
|
+
}
|
|
51
66
|
} else {
|
|
52
67
|
fail('.github/workflows/lint-js.yml не існує — створи його')
|
|
53
68
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
2
|
import { readFile } from 'node:fs/promises'
|
|
3
3
|
|
|
4
|
+
import { pass } from './utils/pass.mjs'
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Перевіряє відповідність проєкту правилам js-pino.mdc
|
|
6
8
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
7
9
|
*/
|
|
8
10
|
export async function check() {
|
|
9
11
|
let exitCode = 0
|
|
10
|
-
const pass = msg => console.log(` ✅ ${msg}`)
|
|
11
12
|
const fail = msg => {
|
|
12
13
|
console.log(` ❌ ${msg}`)
|
|
13
14
|
exitCode = 1
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
2
|
import { readFile } from 'node:fs/promises'
|
|
3
3
|
|
|
4
|
+
import { pass } from './utils/pass.mjs'
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Перевіряє відповідність проєкту правилам nginx-default-tpl.mdc
|
|
6
8
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
7
9
|
*/
|
|
8
10
|
export async function check() {
|
|
9
11
|
let exitCode = 0
|
|
10
|
-
const pass = msg => console.log(` ✅ ${msg}`)
|
|
11
12
|
const fail = msg => {
|
|
12
13
|
console.log(` ❌ ${msg}`)
|
|
13
14
|
exitCode = 1
|
|
@@ -24,17 +25,23 @@ export async function check() {
|
|
|
24
25
|
pass(`${found} існує`)
|
|
25
26
|
const content = await readFile(found, 'utf8')
|
|
26
27
|
|
|
27
|
-
content.includes('listen 8080')
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
if (content.includes('listen 8080')) {
|
|
29
|
+
pass('Nginx слухає порт 8080')
|
|
30
|
+
} else {
|
|
31
|
+
fail(`${found}: має містити listen 8080`)
|
|
32
|
+
}
|
|
30
33
|
|
|
31
|
-
content.includes('/healthz')
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
if (content.includes('/healthz')) {
|
|
35
|
+
pass('Є location /healthz')
|
|
36
|
+
} else {
|
|
37
|
+
fail(`${found}: відсутній location /healthz`)
|
|
38
|
+
}
|
|
34
39
|
|
|
35
|
-
content.includes('gzip_static on')
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
if (content.includes('gzip_static on')) {
|
|
41
|
+
pass('gzip_static увімкнено')
|
|
42
|
+
} else {
|
|
43
|
+
fail(`${found}: має містити gzip_static on`)
|
|
44
|
+
}
|
|
38
45
|
|
|
39
46
|
if (content.includes('proxy_pass')) {
|
|
40
47
|
fail(`${found} містить proxy_pass — перенеси проксі-логіку до HTTPRoute в k8s`)
|
|
@@ -43,16 +50,20 @@ export async function check() {
|
|
|
43
50
|
|
|
44
51
|
if (existsSync('.vscode/extensions.json')) {
|
|
45
52
|
const ext = JSON.parse(await readFile('.vscode/extensions.json', 'utf8'))
|
|
46
|
-
ext.recommendations?.includes('ahmadalli.vscode-nginx-conf')
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
if (ext.recommendations?.includes('ahmadalli.vscode-nginx-conf')) {
|
|
54
|
+
pass('extensions.json містить ahmadalli.vscode-nginx-conf')
|
|
55
|
+
} else {
|
|
56
|
+
fail('extensions.json не містить ahmadalli.vscode-nginx-conf')
|
|
57
|
+
}
|
|
49
58
|
}
|
|
50
59
|
|
|
51
60
|
if (existsSync('.vscode/settings.json')) {
|
|
52
61
|
const s = JSON.parse(await readFile('.vscode/settings.json', 'utf8'))
|
|
53
|
-
s['[nginx]']?.['editor.defaultFormatter'] === 'ahmadalli.vscode-nginx-conf'
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
if (s['[nginx]']?.['editor.defaultFormatter'] === 'ahmadalli.vscode-nginx-conf') {
|
|
63
|
+
pass('settings.json: nginx formatter налаштовано')
|
|
64
|
+
} else {
|
|
65
|
+
fail('settings.json: [nginx] defaultFormatter має бути ahmadalli.vscode-nginx-conf')
|
|
66
|
+
}
|
|
56
67
|
}
|
|
57
68
|
|
|
58
69
|
return exitCode
|
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
2
|
import { readFile, stat } from 'node:fs/promises'
|
|
3
3
|
|
|
4
|
+
import { pass } from './utils/pass.mjs'
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Перевіряє відповідність проєкту правилам npm-module.mdc
|
|
6
8
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
7
9
|
*/
|
|
8
10
|
export async function check() {
|
|
9
11
|
let exitCode = 0
|
|
10
|
-
const pass = msg => console.log(` ✅ ${msg}`)
|
|
11
12
|
const fail = msg => {
|
|
12
13
|
console.log(` ❌ ${msg}`)
|
|
13
14
|
exitCode = 1
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
existsSync('package.json')
|
|
17
|
+
if (existsSync('package.json')) {
|
|
18
|
+
pass('package.json існує')
|
|
19
|
+
} else {
|
|
20
|
+
fail('package.json не існує')
|
|
21
|
+
}
|
|
17
22
|
|
|
18
23
|
if (existsSync('npm')) {
|
|
19
24
|
const s = await stat('npm')
|
|
20
|
-
s.isDirectory()
|
|
25
|
+
if (s.isDirectory()) {
|
|
26
|
+
pass('npm/ директорія існує')
|
|
27
|
+
} else {
|
|
28
|
+
fail('npm має бути директорією')
|
|
29
|
+
}
|
|
21
30
|
} else {
|
|
22
31
|
fail('npm/ директорія не існує')
|
|
23
32
|
}
|
|
@@ -32,13 +41,17 @@ export async function check() {
|
|
|
32
41
|
}
|
|
33
42
|
}
|
|
34
43
|
|
|
35
|
-
existsSync('npm/package.json')
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
if (existsSync('npm/package.json')) {
|
|
45
|
+
pass('npm/package.json існує')
|
|
46
|
+
} else {
|
|
47
|
+
fail('npm/package.json не існує — створи package.json для npm модуля')
|
|
48
|
+
}
|
|
38
49
|
|
|
39
|
-
existsSync('.github/workflows')
|
|
40
|
-
|
|
41
|
-
|
|
50
|
+
if (existsSync('.github/workflows')) {
|
|
51
|
+
pass('.github/workflows/ існує')
|
|
52
|
+
} else {
|
|
53
|
+
fail('.github/workflows/ не існує')
|
|
54
|
+
}
|
|
42
55
|
|
|
43
56
|
return exitCode
|
|
44
57
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
2
|
import { readFile } from 'node:fs/promises'
|
|
3
3
|
|
|
4
|
+
import { pass } from './utils/pass.mjs'
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Перевіряє відповідність проєкту правилам style-lint.mdc
|
|
6
8
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
7
9
|
*/
|
|
8
10
|
export async function check() {
|
|
9
11
|
let exitCode = 0
|
|
10
|
-
const pass = msg => console.log(` ✅ ${msg}`)
|
|
11
12
|
const fail = msg => {
|
|
12
13
|
console.log(` ❌ ${msg}`)
|
|
13
14
|
exitCode = 1
|
|
@@ -16,13 +17,17 @@ export async function check() {
|
|
|
16
17
|
if (existsSync('package.json')) {
|
|
17
18
|
const pkg = JSON.parse(await readFile('package.json', 'utf8'))
|
|
18
19
|
|
|
19
|
-
pkg.scripts?.['lint-style']
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
if (pkg.scripts?.['lint-style']) {
|
|
21
|
+
pass('package.json містить скрипт lint-style')
|
|
22
|
+
} else {
|
|
23
|
+
fail('package.json не містить скрипт "lint-style"')
|
|
24
|
+
}
|
|
22
25
|
|
|
23
|
-
pkg.devDependencies?.['@nitra/stylelint-config']
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
if (pkg.devDependencies?.['@nitra/stylelint-config']) {
|
|
27
|
+
pass('@nitra/stylelint-config є в devDependencies')
|
|
28
|
+
} else {
|
|
29
|
+
fail('@nitra/stylelint-config відсутній — bun add -d @nitra/stylelint-config')
|
|
30
|
+
}
|
|
26
31
|
|
|
27
32
|
const stylelintCfg = pkg.stylelint
|
|
28
33
|
if (stylelintCfg?.extends === '@nitra/stylelint-config') {
|
|
@@ -34,33 +39,47 @@ export async function check() {
|
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
|
|
37
|
-
existsSync('.stylelintignore')
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
if (existsSync('.stylelintignore')) {
|
|
43
|
+
pass('.stylelintignore існує')
|
|
44
|
+
} else {
|
|
45
|
+
fail('.stylelintignore не існує — створи з вмістом: dist/')
|
|
46
|
+
}
|
|
40
47
|
|
|
41
48
|
if (existsSync('.github/workflows/lint-style.yml')) {
|
|
42
49
|
const content = await readFile('.github/workflows/lint-style.yml', 'utf8')
|
|
43
50
|
pass('lint-style.yml існує')
|
|
44
|
-
content.includes('stylelint')
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
if (content.includes('stylelint')) {
|
|
52
|
+
pass('lint-style.yml містить stylelint')
|
|
53
|
+
} else {
|
|
54
|
+
fail('lint-style.yml не містить виклик stylelint')
|
|
55
|
+
}
|
|
47
56
|
} else {
|
|
48
57
|
fail('.github/workflows/lint-style.yml не існує — створи його')
|
|
49
58
|
}
|
|
50
59
|
|
|
51
60
|
if (existsSync('.vscode/extensions.json')) {
|
|
52
61
|
const ext = JSON.parse(await readFile('.vscode/extensions.json', 'utf8'))
|
|
53
|
-
ext.recommendations?.includes('stylelint.vscode-stylelint')
|
|
54
|
-
|
|
55
|
-
|
|
62
|
+
if (ext.recommendations?.includes('stylelint.vscode-stylelint')) {
|
|
63
|
+
pass('extensions.json містить stylelint.vscode-stylelint')
|
|
64
|
+
} else {
|
|
65
|
+
fail('extensions.json не містить stylelint.vscode-stylelint')
|
|
66
|
+
}
|
|
56
67
|
} else {
|
|
57
68
|
fail('.vscode/extensions.json не існує')
|
|
58
69
|
}
|
|
59
70
|
|
|
60
71
|
if (existsSync('.vscode/settings.json')) {
|
|
61
72
|
const s = JSON.parse(await readFile('.vscode/settings.json', 'utf8'))
|
|
62
|
-
s['css.validate'] === false
|
|
63
|
-
|
|
73
|
+
if (s['css.validate'] === false) {
|
|
74
|
+
pass('css.validate вимкнено')
|
|
75
|
+
} else {
|
|
76
|
+
fail('settings.json: css.validate має бути false')
|
|
77
|
+
}
|
|
78
|
+
if (s['scss.validate'] === false) {
|
|
79
|
+
pass('scss.validate вимкнено')
|
|
80
|
+
} else {
|
|
81
|
+
fail('settings.json: scss.validate має бути false')
|
|
82
|
+
}
|
|
64
83
|
}
|
|
65
84
|
|
|
66
85
|
return exitCode
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { readFile } from 'node:fs/promises'
|
|
3
|
+
|
|
4
|
+
import { pass } from './utils/pass.mjs'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Перевіряє відповідність проєкту правилам text.mdc (cspell, markdownlint-cli2, v8r)
|
|
8
|
+
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
9
|
+
*/
|
|
10
|
+
export async function check() {
|
|
11
|
+
let exitCode = 0
|
|
12
|
+
const fail = msg => {
|
|
13
|
+
console.log(` ❌ ${msg}`)
|
|
14
|
+
exitCode = 1
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (existsSync('.vscode/extensions.json')) {
|
|
18
|
+
try {
|
|
19
|
+
const ext = JSON.parse(await readFile('.vscode/extensions.json', 'utf8'))
|
|
20
|
+
const rec = ext.recommendations
|
|
21
|
+
if (Array.isArray(rec) && rec.includes('DavidAnson.vscode-markdownlint')) {
|
|
22
|
+
pass('extensions.json містить DavidAnson.vscode-markdownlint')
|
|
23
|
+
} else {
|
|
24
|
+
fail('extensions.json: додай "DavidAnson.vscode-markdownlint" у recommendations (див. n-text.mdc)')
|
|
25
|
+
}
|
|
26
|
+
} catch {
|
|
27
|
+
fail('.vscode/extensions.json — невалідний JSON')
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
fail('.vscode/extensions.json не існує — створи з recommendations згідно n-text.mdc')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (existsSync('.markdownlint-cli2.jsonc')) {
|
|
34
|
+
try {
|
|
35
|
+
const ml = JSON.parse(await readFile('.markdownlint-cli2.jsonc', 'utf8'))
|
|
36
|
+
pass('.markdownlint-cli2.jsonc існує і є валідним JSON')
|
|
37
|
+
if (ml.gitignore === true) {
|
|
38
|
+
pass('.markdownlint-cli2.jsonc: gitignore увімкнено')
|
|
39
|
+
} else {
|
|
40
|
+
fail('.markdownlint-cli2.jsonc: додай на верхньому рівні "gitignore": true (див. n-text.mdc)')
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
fail('.markdownlint-cli2.jsonc — невалідний JSON; перевір синтаксис')
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
fail('.markdownlint-cli2.jsonc не існує — створи згідно n-text.mdc')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (existsSync('.cspell.json')) {
|
|
50
|
+
const cfg = JSON.parse(await readFile('.cspell.json', 'utf8'))
|
|
51
|
+
|
|
52
|
+
if (cfg.version === '0.2') {
|
|
53
|
+
pass('.cspell.json version: 0.2')
|
|
54
|
+
} else {
|
|
55
|
+
fail('.cspell.json version має бути "0.2"')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (cfg.language) {
|
|
59
|
+
pass(`.cspell.json language: "${cfg.language}"`)
|
|
60
|
+
} else {
|
|
61
|
+
fail('.cspell.json не містить поле language')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const imports = cfg.import || []
|
|
65
|
+
if (imports.some(i => i.includes('@nitra/cspell-dict'))) {
|
|
66
|
+
pass('.cspell.json імпортує @nitra/cspell-dict')
|
|
67
|
+
} else {
|
|
68
|
+
fail('.cspell.json не імпортує @nitra/cspell-dict/cspell-ext.json')
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (Array.isArray(cfg.ignorePaths)) {
|
|
72
|
+
pass('.cspell.json містить ignorePaths')
|
|
73
|
+
} else {
|
|
74
|
+
fail('.cspell.json не містить ignorePaths')
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
fail('.cspell.json не існує — створи його')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (existsSync('package.json')) {
|
|
81
|
+
const pkg = JSON.parse(await readFile('package.json', 'utf8'))
|
|
82
|
+
const devDeps = pkg.devDependencies || {}
|
|
83
|
+
|
|
84
|
+
if (devDeps['@nitra/cspell-dict']) {
|
|
85
|
+
pass('@nitra/cspell-dict є в devDependencies')
|
|
86
|
+
} else {
|
|
87
|
+
fail('@nitra/cspell-dict відсутній — bun add -d @nitra/cspell-dict')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (devDeps['markdownlint-cli2']) {
|
|
91
|
+
pass('markdownlint-cli2 є в devDependencies')
|
|
92
|
+
} else {
|
|
93
|
+
fail('markdownlint-cli2 відсутній — bun add -d markdownlint-cli2')
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const lintText = pkg.scripts?.['lint-text']
|
|
97
|
+
const v8rCalls = typeof lintText === 'string' ? (lintText.match(/bunx v8r/g) || []).length : 0
|
|
98
|
+
const eq98Hints = typeof lintText === 'string' ? (lintText.match(/eq 98/g) || []).length : 0
|
|
99
|
+
if (
|
|
100
|
+
typeof lintText === 'string' &&
|
|
101
|
+
lintText.includes('cspell') &&
|
|
102
|
+
lintText.includes('markdownlint-cli2') &&
|
|
103
|
+
lintText.includes('**/*.mdc') &&
|
|
104
|
+
v8rCalls >= 4 &&
|
|
105
|
+
eq98Hints >= 4 &&
|
|
106
|
+
lintText.includes('**/*.json') &&
|
|
107
|
+
lintText.includes('**/*.yml') &&
|
|
108
|
+
lintText.includes('**/*.yaml') &&
|
|
109
|
+
lintText.includes('**/*.toml')
|
|
110
|
+
) {
|
|
111
|
+
pass('package.json: lint-text — чотири виклики v8r з || [ $? -eq 98 ] для json/yml/yaml/toml')
|
|
112
|
+
} else {
|
|
113
|
+
fail(
|
|
114
|
+
'package.json: lint-text — чотири (bunx v8r "<glob>" || [ $? -eq 98 ]) для **/*.json **/*.yml **/*.yaml **/*.toml (див. n-text.mdc)'
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (existsSync('.github/workflows/lint-text.yml')) {
|
|
119
|
+
const wf = await readFile('.github/workflows/lint-text.yml', 'utf8')
|
|
120
|
+
if (wf.includes('bun run lint-text')) {
|
|
121
|
+
pass('lint-text.yml викликає bun run lint-text')
|
|
122
|
+
} else {
|
|
123
|
+
fail('lint-text.yml має містити крок bun run lint-text')
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
fail('.github/workflows/lint-text.yml не існує — створи згідно n-text.mdc')
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (existsSync('.cspell.json')) {
|
|
130
|
+
const cfg = JSON.parse(await readFile('.cspell.json', 'utf8'))
|
|
131
|
+
const hasUkImport = (cfg.import || []).some(i => i.includes('@cspell/dict-uk-ua'))
|
|
132
|
+
if (hasUkImport && !devDeps['@cspell/dict-uk-ua']) {
|
|
133
|
+
fail('.cspell.json імпортує @cspell/dict-uk-ua, але пакет відсутній в devDependencies')
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return exitCode
|
|
139
|
+
}
|
package/scripts/check-vue.mjs
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs'
|
|
2
2
|
import { readFile } from 'node:fs/promises'
|
|
3
3
|
|
|
4
|
+
import { pass } from './utils/pass.mjs'
|
|
5
|
+
|
|
4
6
|
/**
|
|
5
7
|
* Перевіряє відповідність проєкту правилам vue.mdc
|
|
6
8
|
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
7
9
|
*/
|
|
8
10
|
export async function check() {
|
|
9
11
|
let exitCode = 0
|
|
10
|
-
const pass = msg => console.log(` ✅ ${msg}`)
|
|
11
12
|
const fail = msg => {
|
|
12
13
|
console.log(` ❌ ${msg}`)
|
|
13
14
|
exitCode = 1
|
|
@@ -15,9 +16,11 @@ export async function check() {
|
|
|
15
16
|
|
|
16
17
|
if (existsSync('.vscode/extensions.json')) {
|
|
17
18
|
const ext = JSON.parse(await readFile('.vscode/extensions.json', 'utf8'))
|
|
18
|
-
ext.recommendations?.includes('Vue.volar')
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
if (ext.recommendations?.includes('Vue.volar')) {
|
|
20
|
+
pass('extensions.json містить Vue.volar')
|
|
21
|
+
} else {
|
|
22
|
+
fail('extensions.json не містить Vue.volar — додай до recommendations')
|
|
23
|
+
}
|
|
21
24
|
} else {
|
|
22
25
|
fail('.vscode/extensions.json не існує')
|
|
23
26
|
}
|
|
@@ -28,7 +31,11 @@ export async function check() {
|
|
|
28
31
|
const devDeps = pkg.devDependencies || {}
|
|
29
32
|
const allDeps = { ...deps, ...devDeps }
|
|
30
33
|
|
|
31
|
-
|
|
34
|
+
if (deps.vue) {
|
|
35
|
+
pass(`vue в dependencies: ${deps.vue}`)
|
|
36
|
+
} else {
|
|
37
|
+
fail('vue відсутній в dependencies')
|
|
38
|
+
}
|
|
32
39
|
|
|
33
40
|
if (devDeps.vite) {
|
|
34
41
|
const match = devDeps.vite.match(/(\d+)/)
|
|
@@ -41,29 +48,45 @@ export async function check() {
|
|
|
41
48
|
fail('vite відсутній в devDependencies')
|
|
42
49
|
}
|
|
43
50
|
|
|
44
|
-
devDeps['@vitejs/plugin-vue']
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
if (devDeps['@vitejs/plugin-vue']) {
|
|
52
|
+
pass(`@vitejs/plugin-vue: ${devDeps['@vitejs/plugin-vue']}`)
|
|
53
|
+
} else {
|
|
54
|
+
fail('@vitejs/plugin-vue відсутній в devDependencies')
|
|
55
|
+
}
|
|
47
56
|
|
|
48
|
-
allDeps['vue-macros']
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
if (allDeps['vue-macros']) {
|
|
58
|
+
pass(`vue-macros: ${allDeps['vue-macros']}`)
|
|
59
|
+
} else {
|
|
60
|
+
fail('vue-macros відсутній — bun add -d vue-macros')
|
|
61
|
+
}
|
|
51
62
|
|
|
52
|
-
allDeps['unplugin-auto-import']
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
if (allDeps['unplugin-auto-import']) {
|
|
64
|
+
pass('unplugin-auto-import присутній')
|
|
65
|
+
} else {
|
|
66
|
+
fail('unplugin-auto-import відсутній — bun add -d unplugin-auto-import')
|
|
67
|
+
}
|
|
55
68
|
|
|
56
|
-
allDeps['vite-plugin-vue-layouts-next']
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
if (allDeps['vite-plugin-vue-layouts-next']) {
|
|
70
|
+
pass('vite-plugin-vue-layouts-next присутній')
|
|
71
|
+
} else {
|
|
72
|
+
fail('vite-plugin-vue-layouts-next відсутній — bun add -d vite-plugin-vue-layouts-next')
|
|
73
|
+
}
|
|
59
74
|
}
|
|
60
75
|
|
|
61
76
|
const configFiles = ['vite.config.js', 'vite.config.ts', 'vite.config.mjs']
|
|
62
77
|
const viteConfig = configFiles.find(f => existsSync(f))
|
|
63
78
|
if (viteConfig) {
|
|
64
79
|
const content = await readFile(viteConfig, 'utf8')
|
|
65
|
-
content.includes('VueMacros')
|
|
66
|
-
|
|
80
|
+
if (content.includes('VueMacros')) {
|
|
81
|
+
pass('vite.config використовує VueMacros')
|
|
82
|
+
} else {
|
|
83
|
+
fail(`${viteConfig} не містить VueMacros`)
|
|
84
|
+
}
|
|
85
|
+
if (content.includes('AutoImport')) {
|
|
86
|
+
pass('vite.config використовує AutoImport')
|
|
87
|
+
} else {
|
|
88
|
+
fail(`${viteConfig} не містить AutoImport`)
|
|
89
|
+
}
|
|
67
90
|
} else {
|
|
68
91
|
fail('vite.config.js не існує')
|
|
69
92
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: n-fix
|
|
2
|
+
name: n-fix
|
|
3
3
|
description: >-
|
|
4
4
|
Виправити проєкт відповідно до всіх правил в .cursor/rules/
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
#
|
|
7
|
+
# n-fix — автоматичне виправлення проєкту
|
|
8
8
|
|
|
9
9
|
## Workflow
|
|
10
10
|
|
|
@@ -40,9 +40,14 @@ oxfmt .
|
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
42
|
bun run lint-js
|
|
43
|
+
bun run lint-text
|
|
43
44
|
bun run lint-style
|
|
44
45
|
```
|
|
45
46
|
|
|
47
|
+
На помилках: auto-fix (якщо є), інакше правки в коді/конфігах; повторюй доки скрипт завершується з `0`.
|
|
48
|
+
|
|
49
|
+
Підсумок: Перелічи запущені скрипти та зміни; опиши блокери, якщо лишились.
|
|
50
|
+
|
|
46
51
|
7. **Верифікація** — перевір що все виправлено:
|
|
47
52
|
|
|
48
53
|
```bash
|
|
@@ -47,11 +47,12 @@ Telegram підтримує моноширний шрифт через markdown-
|
|
|
47
47
|
<1-2 речення — що отримали, яка вигода>
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
якщо публікація стосовно
|
|
53
|
-
якщо публікація стосовно
|
|
54
|
-
якщо публікація стосовно
|
|
50
|
+
Теги (хештеги в кінці поста) — підбір за темою:
|
|
51
|
+
|
|
52
|
+
- якщо публікація стосовно AI, то `#ai`
|
|
53
|
+
- якщо публікація стосовно npm модуля, то `#npm`
|
|
54
|
+
- якщо публікація стосовно розробки, то `#dev`
|
|
55
|
+
- якщо публікація стосовно інфраструктури, скриптів, налаштувань, ефективності коду чи безпеки, то `#sre`
|
|
55
56
|
|
|
56
57
|
## Правила
|
|
57
58
|
|
|
@@ -68,6 +69,8 @@ Telegram підтримує моноширний шрифт через markdown-
|
|
|
68
69
|
|
|
69
70
|
````
|
|
70
71
|
```
|
|
72
|
+
#dev
|
|
73
|
+
|
|
71
74
|
📌 Міграція з Prettier на oxfmt
|
|
72
75
|
|
|
73
76
|
Проблема:
|
|
@@ -87,7 +90,5 @@ Prettier повільно форматував великі файли і
|
|
|
87
90
|
Результат:
|
|
88
91
|
Форматування працює миттєво при збереженні,
|
|
89
92
|
зникли конфлікти між форматером і лінтером.
|
|
90
|
-
|
|
91
|
-
#dx #tooling #oxfmt
|
|
92
93
|
```
|
|
93
94
|
````
|