@nitra/cursor 1.8.192 → 1.8.197
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 +40 -0
- package/bin/auto-rules.md +3 -1
- package/mdc/image-avif.mdc +55 -0
- package/mdc/image-compress.mdc +56 -0
- package/package.json +1 -1
- package/scripts/auto-rules.mjs +4 -2
- package/scripts/check-image-avif.mjs +394 -0
- package/scripts/check-image-compress.mjs +216 -0
- package/scripts/check-js-run.mjs +1 -1
- package/scripts/utils/depcheck-workflow.mjs +3 -3
- package/mdc/image.mdc +0 -96
- package/scripts/check-image.mjs +0 -500
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Перевіряє відповідність репозиторію правилу `image-compress.mdc`: канонічний скрипт
|
|
3
|
+
* `lint-image` для оптимізації raster/SVG через `@nitra/minify-image` ≥ 3.2.0 (локально).
|
|
4
|
+
*
|
|
5
|
+
* Очікування:
|
|
6
|
+
* - у кореневому `package.json` є скрипт `lint-image`, який викликає `npx @nitra/minify-image`
|
|
7
|
+
* з обовʼязковими `--src=.` і `--write`. Прапорець `--avif` у `lint-image` заборонений —
|
|
8
|
+
* AVIF-генерацію виконує окреме правило `image-avif` (інакше `bun run lint` плодив би `.avif`
|
|
9
|
+
* для зображень, що ніде не вживаються);
|
|
10
|
+
* - якщо в `package.json` є агрегований скрипт `lint`, він викликає `bun run lint-image`
|
|
11
|
+
* (симетрично до `lint-text`, `lint-js`, `lint-ga`);
|
|
12
|
+
* - `@nitra/minify-image` не оголошений у `dependencies`/`devDependencies` —
|
|
13
|
+
* CLI запускається лише через `npx` (як `markdownlint-cli2` у `text.mdc`);
|
|
14
|
+
* - `.n-minify-image.tsv` (committed source of truth з sha1/originalSize/size) НЕ
|
|
15
|
+
* в `.gitignore` — він має бути в git. Локальний mtime-кеш у
|
|
16
|
+
* `node_modules/.cache/@nitra/minify-image/mtime.tsv` авто-gitignored через `node_modules/`,
|
|
17
|
+
* окремої перевірки не вимагає;
|
|
18
|
+
* - застарілий `.minify-image-cache.tsv` (з версій < 3.2) видалений з кореня — інакше
|
|
19
|
+
* проєкт лишається у напівпереміщеному стані.
|
|
20
|
+
*/
|
|
21
|
+
import { existsSync } from 'node:fs'
|
|
22
|
+
import { readFile } from 'node:fs/promises'
|
|
23
|
+
|
|
24
|
+
import { createCheckReporter } from './utils/check-reporter.mjs'
|
|
25
|
+
|
|
26
|
+
/** Імʼя CLI-пакета: рядок у `lint-image` і заборонений у залежностях. */
|
|
27
|
+
const MINIFY_PACKAGE_NAME = '@nitra/minify-image'
|
|
28
|
+
|
|
29
|
+
/** Імʼя committed-кешу (sha1 + originalSize + size) у `@nitra/minify-image` ≥ 3.2.0. */
|
|
30
|
+
const HASH_CACHE_FILENAME = '.n-minify-image.tsv'
|
|
31
|
+
|
|
32
|
+
/** Імʼя застарілого 4-колонкового кешу (`@nitra/minify-image` < 3.2). Має бути видалений після міграції. */
|
|
33
|
+
const LEGACY_CACHE_FILENAME = '.minify-image-cache.tsv'
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Перевіряє скрипт `lint-image` у `package.json`.
|
|
37
|
+
*
|
|
38
|
+
* Має містити виклик `npx @nitra/minify-image` з обовʼязковими прапорцями `--src=.`
|
|
39
|
+
* і `--write` (авто-оптимізація на місці). Прапорець `--avif` у `lint-image`
|
|
40
|
+
* заборонений — AVIF-генерацію виконує `check image-avif`, інакше `bun run lint` плодить
|
|
41
|
+
* `.avif` для зображень, що ніде не вживаються.
|
|
42
|
+
* @param {string|undefined} lintImage значення `scripts['lint-image']`
|
|
43
|
+
* @param {(msg: string) => void} pass callback при успішній перевірці
|
|
44
|
+
* @param {(msg: string) => void} fail callback при помилці
|
|
45
|
+
* @returns {void}
|
|
46
|
+
*/
|
|
47
|
+
function checkLintImageScript(lintImage, pass, fail) {
|
|
48
|
+
const canonical = `npx ${MINIFY_PACKAGE_NAME} --src=. --write`
|
|
49
|
+
if (typeof lintImage !== 'string' || !lintImage.trim()) {
|
|
50
|
+
fail(`package.json: додай скрипт "lint-image" з \`${canonical}\` (image-compress.mdc)`)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
if (!lintImage.includes(`npx ${MINIFY_PACKAGE_NAME}`)) {
|
|
54
|
+
fail(`package.json: lint-image має викликати \`npx ${MINIFY_PACKAGE_NAME}\` (image-compress.mdc)`)
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
/** @type {{ flag: string, variants: string[], hint: string }[]} */
|
|
58
|
+
const requiredFlags = [
|
|
59
|
+
{ flag: '--src=.', variants: ['--src=.', '--src .'], hint: '`--src=.`' },
|
|
60
|
+
{ flag: '--write', variants: ['--write'], hint: '`--write` (авто-оптимізація на місці)' }
|
|
61
|
+
]
|
|
62
|
+
const missing = requiredFlags.filter(f => !f.variants.some(v => lintImage.includes(v)))
|
|
63
|
+
if (missing.length > 0) {
|
|
64
|
+
fail(
|
|
65
|
+
`package.json: lint-image має містити ${missing.map(f => f.hint).join(', ')} — канонічний виклик: \`${canonical}\` (image-compress.mdc)`
|
|
66
|
+
)
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
if (lintImage.includes('--avif')) {
|
|
70
|
+
fail(
|
|
71
|
+
`package.json: прибери \`--avif\` з lint-image — AVIF-генерацію виконує \`npx @nitra/cursor check image-avif\` (image-compress.mdc). Канонічний виклик: \`${canonical}\``
|
|
72
|
+
)
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
pass(`package.json: lint-image викликає \`${canonical}\``)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Перевіряє, що агрегований `lint` (якщо є) кличе `bun run lint-image` —
|
|
80
|
+
* симетрично до `lint-text`, `lint-js`, `lint-ga`.
|
|
81
|
+
* @param {string|undefined} lintAggregate значення `scripts.lint`
|
|
82
|
+
* @param {(msg: string) => void} pass callback при успішній перевірці
|
|
83
|
+
* @param {(msg: string) => void} fail callback при помилці
|
|
84
|
+
* @returns {void}
|
|
85
|
+
*/
|
|
86
|
+
function checkLintAggregateIncludesImage(lintAggregate, pass, fail) {
|
|
87
|
+
if (typeof lintAggregate !== 'string' || !lintAggregate.trim()) {
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
if (lintAggregate.includes('bun run lint-image')) {
|
|
91
|
+
pass('package.json: агрегований `lint` викликає `bun run lint-image`')
|
|
92
|
+
} else {
|
|
93
|
+
fail(
|
|
94
|
+
'package.json: у `lint` додай `bun run lint-image` (image-compress.mdc, симетрично до lint-text / lint-js / lint-ga)'
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Забороняє `@nitra/minify-image` у `dependencies` чи `devDependencies` —
|
|
101
|
+
* CLI завжди запускається через `npx` (як `markdownlint-cli2` у `text.mdc`).
|
|
102
|
+
* @param {{ dependencies?: Record<string, unknown>, devDependencies?: Record<string, unknown> }} pkg розібраний package.json
|
|
103
|
+
* @param {(msg: string) => void} pass callback при успішній перевірці
|
|
104
|
+
* @param {(msg: string) => void} fail callback при помилці
|
|
105
|
+
* @returns {void}
|
|
106
|
+
*/
|
|
107
|
+
function checkMinifyImageNotInDeps(pkg, pass, fail) {
|
|
108
|
+
const inDeps = Boolean(pkg.dependencies && MINIFY_PACKAGE_NAME in pkg.dependencies)
|
|
109
|
+
const inDevDeps = Boolean(pkg.devDependencies && MINIFY_PACKAGE_NAME in pkg.devDependencies)
|
|
110
|
+
if (inDeps || inDevDeps) {
|
|
111
|
+
fail(
|
|
112
|
+
`package.json: ${MINIFY_PACKAGE_NAME} не додавай у dependencies/devDependencies — лише через \`npx\` (image-compress.mdc)`
|
|
113
|
+
)
|
|
114
|
+
} else {
|
|
115
|
+
pass(`package.json: ${MINIFY_PACKAGE_NAME} не оголошено в dependencies/devDependencies`)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Зчитує всі змістовні рядки `.gitignore` (без коментарів і порожніх). Якщо файла нема — `null`.
|
|
121
|
+
* @returns {Promise<string[] | null>} список trim-нутих рядків або `null`
|
|
122
|
+
*/
|
|
123
|
+
async function readGitignoreLines() {
|
|
124
|
+
if (!existsSync('.gitignore')) return null
|
|
125
|
+
const raw = await readFile('.gitignore', 'utf8')
|
|
126
|
+
return raw
|
|
127
|
+
.split('\n')
|
|
128
|
+
.map(l => l.trim())
|
|
129
|
+
.filter(l => l.length > 0 && !l.startsWith('#'))
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Перевіряє, що `.n-minify-image.tsv` НЕ в `.gitignore` — він має бути в git
|
|
134
|
+
* (split-cache 3.2.0: source of truth для slow-path і lifetime savings).
|
|
135
|
+
*
|
|
136
|
+
* Сам факт існування файла НЕ вимагається — на свіжому проєкті без обробки
|
|
137
|
+
* зображень його ще нема, це нормально.
|
|
138
|
+
* @param {(msg: string) => void} pass callback при успішній перевірці
|
|
139
|
+
* @param {(msg: string) => void} fail callback при помилці
|
|
140
|
+
* @returns {Promise<void>}
|
|
141
|
+
*/
|
|
142
|
+
async function checkHashCacheNotIgnored(pass, fail) {
|
|
143
|
+
const lines = await readGitignoreLines()
|
|
144
|
+
if (lines && lines.includes(HASH_CACHE_FILENAME)) {
|
|
145
|
+
fail(
|
|
146
|
+
`.gitignore: прибери рядок \`${HASH_CACHE_FILENAME}\` — це закомічений source of truth split-cache 3.2.0 (image-compress.mdc)`
|
|
147
|
+
)
|
|
148
|
+
} else {
|
|
149
|
+
pass(`${HASH_CACHE_FILENAME} не в .gitignore (має бути в git)`)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Перевіряє, що застарілий `.minify-image-cache.tsv` (з версій < 3.2) видалений
|
|
155
|
+
* з кореня. Якщо лежить — користувач не завершив міграцію на split-cache, що
|
|
156
|
+
* залишає файл як орфана у git-історії.
|
|
157
|
+
* @param {(msg: string) => void} pass callback при успішній перевірці
|
|
158
|
+
* @param {(msg: string) => void} fail callback при помилці
|
|
159
|
+
* @returns {Promise<void>}
|
|
160
|
+
*/
|
|
161
|
+
async function checkLegacyCacheRemoved(pass, fail) {
|
|
162
|
+
if (existsSync(LEGACY_CACHE_FILENAME)) {
|
|
163
|
+
fail(
|
|
164
|
+
`${LEGACY_CACHE_FILENAME} застарілий (split-cache 3.2.0) — видали: ` +
|
|
165
|
+
`\`git rm --cached ${LEGACY_CACHE_FILENAME} 2>/dev/null || true && rm -f ${LEGACY_CACHE_FILENAME}\` ` +
|
|
166
|
+
'(також прибери відповідний рядок з .gitignore, якщо є)'
|
|
167
|
+
)
|
|
168
|
+
return
|
|
169
|
+
}
|
|
170
|
+
const lines = await readGitignoreLines()
|
|
171
|
+
if (lines && lines.includes(LEGACY_CACHE_FILENAME)) {
|
|
172
|
+
fail(`.gitignore: прибери застарілий рядок \`${LEGACY_CACHE_FILENAME}\` — split-cache 3.2.0 його не використовує`)
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
pass(`${LEGACY_CACHE_FILENAME} відсутній (міграція на split-cache завершена)`)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Перевіряє кореневий `package.json`: скрипти, заборонені залежності, агрегований `lint`.
|
|
180
|
+
* @param {(msg: string) => void} pass callback при успішній перевірці
|
|
181
|
+
* @param {(msg: string) => void} fail callback при помилці
|
|
182
|
+
* @returns {Promise<boolean>} `true`, якщо `package.json` знайдено й оброблено; `false` — нема
|
|
183
|
+
*/
|
|
184
|
+
async function checkPackageJsonImage(pass, fail) {
|
|
185
|
+
if (!existsSync('package.json')) {
|
|
186
|
+
fail('package.json не знайдено в корені — додай (image-compress.mdc)')
|
|
187
|
+
return false
|
|
188
|
+
}
|
|
189
|
+
const pkg = JSON.parse(await readFile('package.json', 'utf8'))
|
|
190
|
+
const scripts = /** @type {Record<string, unknown>} */ (pkg.scripts || {})
|
|
191
|
+
checkLintImageScript(typeof scripts['lint-image'] === 'string' ? scripts['lint-image'] : undefined, pass, fail)
|
|
192
|
+
checkLintAggregateIncludesImage(typeof scripts.lint === 'string' ? scripts.lint : undefined, pass, fail)
|
|
193
|
+
checkMinifyImageNotInDeps(pkg, pass, fail)
|
|
194
|
+
return true
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Перевіряє відповідність проєкту правилу `image-compress.mdc`: канонічний `lint-image`
|
|
199
|
+
* (через `npx @nitra/minify-image --src=. --write`, без `--avif`!), агрегований `lint`,
|
|
200
|
+
* `@nitra/minify-image` не у залежностях, `.n-minify-image.tsv` НЕ в `.gitignore`,
|
|
201
|
+
* застарілий `.minify-image-cache.tsv` видалений. CI-workflow для image не вимагається —
|
|
202
|
+
* лінт зображень виконується лише локально.
|
|
203
|
+
* @returns {Promise<number>} 0 — все OK, 1 — є проблеми
|
|
204
|
+
*/
|
|
205
|
+
export async function check() {
|
|
206
|
+
const reporter = createCheckReporter()
|
|
207
|
+
const { pass, fail } = reporter
|
|
208
|
+
|
|
209
|
+
const pkgFound = await checkPackageJsonImage(pass, fail)
|
|
210
|
+
if (pkgFound) {
|
|
211
|
+
await checkHashCacheNotIgnored(pass, fail)
|
|
212
|
+
await checkLegacyCacheRemoved(pass, fail)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return reporter.getExitCode()
|
|
216
|
+
}
|
package/scripts/check-js-run.mjs
CHANGED
|
@@ -358,7 +358,7 @@ async function checkWorkspacePackage(rootDir, ignorePaths, workflows, fail, pass
|
|
|
358
358
|
*/
|
|
359
359
|
function checkDepcheckInWorkflows(rootDir, workflows, label, fail, passFn) {
|
|
360
360
|
if (workflows.length === 0) return
|
|
361
|
-
const violations = findDepcheckViolationsForPackage(workflows, rootDir.
|
|
361
|
+
const violations = findDepcheckViolationsForPackage(workflows, rootDir.replaceAll('\\', '/'))
|
|
362
362
|
if (violations.length === 0) {
|
|
363
363
|
passFn(`${label}depcheck у path-scoped workflow налаштовано (або відсутній path-scoped workflow для пакета)`)
|
|
364
364
|
return
|
|
@@ -37,7 +37,7 @@ const IGNORES_FLAG_RE = /--ignores\s*=?\s*(?:"([^"]*)"|'([^']*)'|([^\s"']+))/u
|
|
|
37
37
|
* @returns {boolean} `true`, якщо знайдено хоча б один підходящий glob
|
|
38
38
|
*/
|
|
39
39
|
export function workflowHasPathsScopedToPackage(root, pkgRoot) {
|
|
40
|
-
const prefix = `${pkgRoot.
|
|
40
|
+
const prefix = `${pkgRoot.replaceAll('\\', '/').replace(/\/+$/, '')}/`
|
|
41
41
|
const on = root?.on
|
|
42
42
|
if (!on || typeof on !== 'object') return false
|
|
43
43
|
for (const event of /** @type {const} */ (['push', 'pull_request'])) {
|
|
@@ -85,8 +85,8 @@ export function extractDepcheckArgs(runText) {
|
|
|
85
85
|
export function stepWorkingDirectoryEquals(step, pkgRoot) {
|
|
86
86
|
const wd = step['working-directory']
|
|
87
87
|
if (typeof wd !== 'string') return false
|
|
88
|
-
const norm = wd.
|
|
89
|
-
const expected = pkgRoot.
|
|
88
|
+
const norm = wd.replaceAll('\\', '/').replace(/\/+$/, '')
|
|
89
|
+
const expected = pkgRoot.replaceAll('\\', '/').replace(/\/+$/, '')
|
|
90
90
|
return norm === expected
|
|
91
91
|
}
|
|
92
92
|
|
package/mdc/image.mdc
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Оптимізація зображень через @nitra/minify-image у локальному lint
|
|
3
|
-
alwaysApply: true
|
|
4
|
-
version: '1.5'
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
CLI [`@nitra/minify-image`](https://www.npmjs.com/package/@nitra/minify-image) (≥ **3.2.0**) запускається через `npx` (як `markdownlint-cli2` у text.mdc) і **не** додається в `dependencies` / `devDependencies`. Канонічний `lint-image` — авто-оптимізація з прапорцем `--write`: стискає raster/SVG на місці. **AVIF-генерація (`--avif`) у `lint-image` заборонена** — її виконує лише `npx @nitra/cursor check image`, який заодно прибирає AVIF-сироти (див. секцію «AVIF-імпорти у `.vue`»). Split-cache робить повторні прогони дешевими — і локально, і після `git clone`.
|
|
8
|
-
|
|
9
|
-
Перевірка лише локальна — у CI `lint-image` не запускаємо (sharp/svgo тягнуть бінарні залежності, цінність на ubuntu-runner-ах нижча за час прогону). Окремий workflow `lint-image.yml` створювати не треба.
|
|
10
|
-
|
|
11
|
-
## `package.json`
|
|
12
|
-
|
|
13
|
-
```json title="package.json"
|
|
14
|
-
{
|
|
15
|
-
"scripts": {
|
|
16
|
-
"lint": "bun run lint-js && bun run lint-text && bun run lint-ga && bun run lint-image && oxfmt .",
|
|
17
|
-
"lint-image": "npx @nitra/minify-image --src=. --write"
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
Якщо в `package.json` уже є агрегований `lint`, додай у його ланцюжок `bun run lint-image` (як `bun run lint-text`, `bun run lint-js`, `bun run lint-ga`). Так розробник, що локально гонить `bun run lint`, перед фіксацією одразу бачить, чи зросли зображення.
|
|
23
|
-
|
|
24
|
-
## Split-cache
|
|
25
|
-
|
|
26
|
-
Починаючи з `@nitra/minify-image` **3.2.0** кеш розбитий на два файли з різною семантикою:
|
|
27
|
-
|
|
28
|
-
### `.n-minify-image.tsv` — source of truth у git
|
|
29
|
-
|
|
30
|
-
У корені сканованого каталогу. Формат: `<rel-path>\t<sha1-hex>\t<originalSize>\t<size>`.
|
|
31
|
-
|
|
32
|
-
Slow-path і джерело даних для `Project lifetime savings`. **Має бути в git** — після `git clone` чи `git checkout` (mtime скидається на час checkout-у) CLI читає файл, рахує SHA-1 і порівнює зі збереженим у TSV хешем; на match локальний mtime-кеш зігрівається без reprocess. Рядки відсортовані алфавітно, hash і size змінюються лише при реальній зміні контенту — diff чистий.
|
|
33
|
-
|
|
34
|
-
### `node_modules/.cache/@nitra/minify-image/mtime.tsv` — локальний fast-path
|
|
35
|
-
|
|
36
|
-
Формат: `<rel-path>\t<mtime>\t<size>`. При збігу `(size, mtime)` CLI пропускає файл без читання — константа per-file.
|
|
37
|
-
|
|
38
|
-
Лежить під `node_modules/`, тож **авто-gitignored** за конвенцією JS-tooling-у (так кешуються ESLint, Babel, webpack, Turbo). Окремий рядок у `.gitignore` не потрібен. `rm -rf node_modules` зносить — наступний запуск відновлює його через slow-path проти `.n-minify-image.tsv`, без reprocess.
|
|
39
|
-
|
|
40
|
-
### Міграція з versions < 3.2
|
|
41
|
-
|
|
42
|
-
Старий єдиний `.minify-image-cache.tsv` (4 колонки `path\tmtime\toriginalSize\tsize`, зазвичай у `.gitignore`) автоматично читається при першому запуску для seed-у `originalSize` у `.n-minify-image.tsv` (lifetime savings не скидається). Після цього старий файл видаляють вручну:
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
git rm --cached .minify-image-cache.tsv 2>/dev/null || true
|
|
46
|
-
rm -f .minify-image-cache.tsv
|
|
47
|
-
# прибери відповідний рядок з .gitignore, якщо був
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
AVIF-двійники (`<name>.<ext>.avif`) **зберігаємо в git** — це готові артефакти для віддачі браузеру (без них ефект від AVIF втрачається на чистому checkout-і).
|
|
51
|
-
|
|
52
|
-
## AVIF-імпорти у `.vue`
|
|
53
|
-
|
|
54
|
-
AVIF-двійники (`<name>.<ext>.avif`) генерує **виключно** `npx @nitra/cursor check image` — у `lint-image` прапорець `--avif` заборонений (інакше `bun run lint` плодить непотрібні `.avif` для зображень, що ніде не використовуються). Перевірка робить три кроки в порядку:
|
|
55
|
-
|
|
56
|
-
1. Запускає `npx @nitra/minify-image --src=. --write --avif` — генерує `<name>.<ext>.avif` поряд з кожним PNG/JPEG/GIF.
|
|
57
|
-
2. Сканує `.vue` (а також `.html`) файли в кожному workspace-пакеті (root + workspaces) і автоматично переписує raster-посилання на AVIF-двійник у двох формах:
|
|
58
|
-
- **Імпорт-пов'язані** — `import x from '...png|jpg|jpeg|gif'` (далі `:src="x"` у шаблоні);
|
|
59
|
-
- **Прямі статичні** — `<img src="...png" />` у `<template>` (Vite перетворює такий шлях на asset-імпорт на етапі збірки, тож вимога та сама).
|
|
60
|
-
3. Видаляє AVIF-сироти: ходить по всіх `<...>.avif` у репозиторії; якщо на двійник не лишилось жодного посилання у `.vue`/`.html` — файл видаляється. **AVIF на диску лишається лише там, де заміна реально відбулась** — тому невикористані оригінали не накопичують `.avif`-«хвости».
|
|
61
|
-
|
|
62
|
-
```vue title="App.vue (після check image)"
|
|
63
|
-
<script setup>
|
|
64
|
-
import welcomeImage from './assets/welcome.png.avif'
|
|
65
|
-
</script>
|
|
66
|
-
|
|
67
|
-
<template>
|
|
68
|
-
<img :src="welcomeImage" alt="Welcome" />
|
|
69
|
-
</template>
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
Реактивне `:src="..."` (з JS-виразом — змінною, тернарником, викликом тощо) **не сканується** — значення обчислюється у рантаймі й шлях туди потрапляє через імпорт або інший резолвинг, який ловить імпорт-перевірка вище. SVG не торкаємо (vector → AVIF безглуздо). Атрибути `data-src=`, `obj.src=` у `<script>` тощо також пропускаються.
|
|
73
|
-
|
|
74
|
-
Якщо raster-посилання у `.vue`/`.html` не вдалось переписати (наприклад, оригіналу немає на диску, тож і `.avif` не згенерувався) — `check image` падає з помилкою на конкретний файл, як раніше.
|
|
75
|
-
|
|
76
|
-
### Опт-аут для конкретного пакета
|
|
77
|
-
|
|
78
|
-
У workspace-пакеті, де AVIF-імпорти небажані (наприклад, мобільний бандл, де AVIF-підтримка не гарантована), додай у `package.json` цього пакета:
|
|
79
|
-
|
|
80
|
-
```json title="apps/mobile/package.json"
|
|
81
|
-
{
|
|
82
|
-
"@nitra/minify-image": {
|
|
83
|
-
"disable-avif": true
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
Тоді перевірка пропускає `.vue` файли цього пакета. У root-`package.json` опт-аут діє лише для файлів кореня (вкладені workspaces перевіряються незалежно — вмикай прапор у кожному пакеті, де треба).
|
|
89
|
-
|
|
90
|
-
## Заборонені залежності
|
|
91
|
-
|
|
92
|
-
`@nitra/minify-image` не повинен зʼявлятися ні в `dependencies`, ні в `devDependencies` кореневого `package.json` — CLI запускається лише через `npx` (так само, як `markdownlint-cli2` у text.mdc). Якщо потрібен явний пін — закладай діапазон версій npm у самому виклику (`npx @nitra/minify-image@^3 --src=. --write`).
|
|
93
|
-
|
|
94
|
-
## Перевірка
|
|
95
|
-
|
|
96
|
-
`npx @nitra/cursor check image` (охоплює `lint-image` з обовʼязковими `--src=.`, `--write` і **забороненим** `--avif`; агрегований `lint`; `.n-minify-image.tsv` НЕ в `.gitignore` — має бути в git; відсутність застарілого `.minify-image-cache.tsv` у корені; запуск AVIF-генерації + авто-заміна raster-посилань на `.avif` у `.vue`/`.html` кожного workspace-пакета + прибирання AVIF-сиріт).
|