mirror of
https://github.com/Ku6epXBOCTuK/easy-png-tools.git
synced 2026-11-13 17:08:20 +00:00
test: for plugin and lint
This commit is contained in:
+2
-1
@@ -57,7 +57,8 @@
|
|||||||
|
|
||||||
- production registry содержит 122 инструмента;
|
- production registry содержит 122 инструмента;
|
||||||
- `pnpm --dir web check` проходит без ошибок и предупреждений;
|
- `pnpm --dir web check` проходит без ошибок и предупреждений;
|
||||||
- `pnpm --dir web test` — 43 test files / 465 tests;
|
- `pnpm --dir web test` — 45 test files / 497 tests, включая тесты token audit и
|
||||||
|
PostCSS HCT-плагина;
|
||||||
- `pnpm --dir web test:rules` — 4 test files / 74 tests;
|
- `pnpm --dir web test:rules` — 4 test files / 74 tests;
|
||||||
- `pnpm --dir web lint:all` — 0 errors, 0 i18n warnings и 0 unused design tokens
|
- `pnpm --dir web lint:all` — 0 errors, 0 i18n warnings и 0 unused design tokens
|
||||||
от token audit;
|
от token audit;
|
||||||
|
|||||||
@@ -406,14 +406,39 @@ file-result-инструментам.
|
|||||||
- Второй пункт («после очистки решить, должен ли unused audit быть
|
- Второй пункт («после очистки решить, должен ли unused audit быть
|
||||||
fail-режимом») остаётся открытым: сам audit пока только сообщает.
|
fail-режимом») остаётся открытым: сам audit пока только сообщает.
|
||||||
|
|
||||||
#### Q7b–Q7d. Оставшиеся направления
|
#### Q7b. Тесты token audit и PostCSS HCT-плагина
|
||||||
|
|
||||||
|
**Статус:** выполнен. У обоих не было ни одного теста, хотя HCT-плагин
|
||||||
|
(`web/scripts/postcss-hct.mjs`, 430 строк) пересчитывает всю палитру.
|
||||||
|
|
||||||
|
- `web/scripts/__tests__/postcss-hct.test.mjs` — 17 тестов: literal и
|
||||||
|
производный `hct()`, seed из `:root` и из hex, цепочка hct-токенов, refs
|
||||||
|
`h/c/t`, `calc/min/max/clamp`, обёртка hue, клампы tone/chroma, несколько
|
||||||
|
вызовов в одном объявлении, а также warn-and-keep для неизвестного seed,
|
||||||
|
циклической ссылки и битого тела `hct()`.
|
||||||
|
- `web/scripts/__tests__/token-audit.test.mjs` — 15 тестов на parity
|
||||||
|
(light-only, dark-only, derived, не-цвет), authorship (hct, color-mix,
|
||||||
|
seed-токены, хардкод с локацией, игнор не-цветов и обычных свойств), unused и
|
||||||
|
предикаты `isColorValue`/`isDerived`.
|
||||||
|
- Ожидания не зашивают вывод плагина, а сравнивают его с эквивалентной
|
||||||
|
литеральной записью: `hct(from var(--seed) h c calc(t + 10))` должен совпасть
|
||||||
|
с `hct(120 40 60)`, если seed — `hct(120 40 50)`. Иначе тест просто
|
||||||
|
зафиксировал бы текущее поведение.
|
||||||
|
- Единственный seam: `checkUnused(root, used = collectUsedTokens())` —
|
||||||
|
зависимость от чтения `src` стала инъецируемой, production-вызов не изменился.
|
||||||
|
- `vitest.config.ts` подхватывает `scripts/**/*.test.mjs`, поэтому скрипты
|
||||||
|
покрываются обычным прогоном и `verify`; `test:rules` не трогали.
|
||||||
|
- Проверка мутацией выявила дыру: первая версия тестов не ловила кламп tone в
|
||||||
|
ветке `from` (смена `clamp(t, 0, 100)` на `0, 90` проходила зелёной). Добавлен
|
||||||
|
отдельный тест на производный кламп; после этого мутация падает.
|
||||||
|
|
||||||
|
#### Q7c–Q7d. Оставшиеся направления
|
||||||
|
|
||||||
- Добавить тесты для token audit и PostCSS HCT-плагина: у них сейчас нет
|
|
||||||
покрытия, `test:rules` их не видит.
|
|
||||||
- Покрыть базовым JS recommended сам код ESLint-плагинов и lint-скриптов.
|
- Покрыть базовым JS recommended сам код ESLint-плагинов и lint-скриптов.
|
||||||
- Отдельно решить, должен ли обычный CSS проходить проверку undefined tokens и
|
- Отдельно решить, должен ли обычный CSS проходить проверку undefined tokens и
|
||||||
hardcoded colors наравне со Svelte styles.
|
hardcoded colors наравне со Svelte styles.
|
||||||
- Новые правила подключать только после fixtures, `test:rules` и baseline.
|
- Новые правила подключать только после fixtures, `test:rules` и baseline.
|
||||||
|
- Решить, должен ли unused audit стать fail-режимом (см. Q7a).
|
||||||
|
|
||||||
### Q8. Новые автоматические правила
|
### Q8. Новые автоматические правила
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
- `lint-all.mjs` — оркестратор `lint:all`.
|
- `lint-all.mjs` — оркестратор `lint:all`.
|
||||||
- `check-tokens.mjs` + `token-audit/` — токен-аудит по `app.css`.
|
- `check-tokens.mjs` + `token-audit/` — токен-аудит по `app.css`.
|
||||||
- `postcss-hct.mjs` — постcss-плагин эммита `hct()` в sRGB-hex.
|
- `postcss-hct.mjs` — постcss-плагин эммита `hct()` в sRGB-hex.
|
||||||
|
- `__tests__/` — тесты аудита и HCT-плагина; подхватываются обычным
|
||||||
|
`vitest run` через `scripts/**/*.test.mjs`, поэтому входят в `verify`.
|
||||||
- `web/stylelint.config.js`, `web/postcss.config.js` — конфиги stylelint/css.
|
- `web/stylelint.config.js`, `web/postcss.config.js` — конфиги stylelint/css.
|
||||||
|
|
||||||
## Конфигурация ESLint (`web/eslint.config.js`)
|
## Конфигурация ESLint (`web/eslint.config.js`)
|
||||||
|
|||||||
@@ -0,0 +1,217 @@
|
|||||||
|
import postcss from "postcss";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import postcssHct from "../postcss-hct.mjs";
|
||||||
|
|
||||||
|
async function process(css) {
|
||||||
|
const result = await postcss([postcssHct]).process(css, { from: undefined });
|
||||||
|
return {
|
||||||
|
css: result.css,
|
||||||
|
warnings: result.warnings().map((warning) => warning.text),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function valueOf(css, token) {
|
||||||
|
const match = css.match(new RegExp(`${token}:\\s*([^;]+)`));
|
||||||
|
if (!match) throw new Error(`token ${token} not found in:\n${css}`);
|
||||||
|
return match[1].trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isHexColor(value) {
|
||||||
|
return /^#[0-9a-f]{6}$/i.test(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("postcss-hct", () => {
|
||||||
|
it("resolves a literal hct() to an sRGB hex color", async () => {
|
||||||
|
const { css, warnings } = await process(
|
||||||
|
":root { --black: hct(0 0 0); --white: hct(0 0 100); }",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(valueOf(css, "--black")).toBe("#000000");
|
||||||
|
expect(valueOf(css, "--white")).toBe("#ffffff");
|
||||||
|
expect(warnings).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps tone monotonic for achromatic colors", async () => {
|
||||||
|
const { css } = await process(
|
||||||
|
":root { --low: hct(0 0 25); --mid: hct(0 0 50); --high: hct(0 0 75); }",
|
||||||
|
);
|
||||||
|
const tone = (token) => Number.parseInt(valueOf(css, token).slice(1), 16);
|
||||||
|
|
||||||
|
expect(tone("--low")).toBeLessThan(tone("--mid"));
|
||||||
|
expect(tone("--mid")).toBeLessThan(tone("--high"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("wraps hue into the 0-360 range", async () => {
|
||||||
|
const { css } = await process(
|
||||||
|
[
|
||||||
|
":root {",
|
||||||
|
" --base: hct(350 40 50);",
|
||||||
|
" --negative: hct(-10 40 50);",
|
||||||
|
" --overflow: hct(370 40 50);",
|
||||||
|
" --ten: hct(10 40 50);",
|
||||||
|
"}",
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(valueOf(css, "--negative")).toBe(valueOf(css, "--base"));
|
||||||
|
expect(valueOf(css, "--overflow")).toBe(valueOf(css, "--ten"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("clamps tone into 0-100", async () => {
|
||||||
|
const { css } = await process(
|
||||||
|
":root { --over: hct(0 0 150); --under: hct(0 0 -20); }",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(valueOf(css, "--over")).toBe("#ffffff");
|
||||||
|
expect(valueOf(css, "--under")).toBe("#000000");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("clamps tone derived from a seed into 0-100", async () => {
|
||||||
|
const { css } = await process(
|
||||||
|
[
|
||||||
|
":root {",
|
||||||
|
" --seed: hct(120 40 50);",
|
||||||
|
" --too-light: hct(from var(--seed) h c calc(t + 80));",
|
||||||
|
" --expected-light: hct(120 40 100);",
|
||||||
|
" --too-dark: hct(from var(--seed) h c calc(t - 80));",
|
||||||
|
" --expected-dark: hct(120 40 0);",
|
||||||
|
"}",
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(valueOf(css, "--too-light")).toBe(valueOf(css, "--expected-light"));
|
||||||
|
expect(valueOf(css, "--too-dark")).toBe(valueOf(css, "--expected-dark"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("takes h, c and t from a seed token", async () => {
|
||||||
|
const { css } = await process(
|
||||||
|
":root { --seed: hct(120 40 50); --copy: hct(from var(--seed) h c t); }",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(valueOf(css, "--copy")).toBe(valueOf(css, "--seed"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("supports channel math through calc()", async () => {
|
||||||
|
const { css } = await process(
|
||||||
|
[
|
||||||
|
":root {",
|
||||||
|
" --seed: hct(120 40 50);",
|
||||||
|
" --lighter: hct(from var(--seed) h c calc(t + 10));",
|
||||||
|
" --expected: hct(120 40 60);",
|
||||||
|
"}",
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(valueOf(css, "--lighter")).toBe(valueOf(css, "--expected"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("supports min, max and clamp over channels", async () => {
|
||||||
|
const { css } = await process(
|
||||||
|
[
|
||||||
|
":root {",
|
||||||
|
" --seed: hct(120 40 50);",
|
||||||
|
" --capped: hct(from var(--seed) h c clamp(10, t, 45));",
|
||||||
|
" --expected: hct(120 40 45);",
|
||||||
|
" --smallest: hct(from var(--seed) h c min(t, 20));",
|
||||||
|
" --expected-smallest: hct(120 40 20);",
|
||||||
|
"}",
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(valueOf(css, "--capped")).toBe(valueOf(css, "--expected"));
|
||||||
|
expect(valueOf(css, "--smallest")).toBe(
|
||||||
|
valueOf(css, "--expected-smallest"),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves a hex seed through a variable and inline alike", async () => {
|
||||||
|
const { css } = await process(
|
||||||
|
[
|
||||||
|
":root {",
|
||||||
|
" --seed: #336699;",
|
||||||
|
" --via-var: hct(from var(--seed) h c t);",
|
||||||
|
" --inline: hct(from #336699 h c t);",
|
||||||
|
"}",
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(isHexColor(valueOf(css, "--via-var"))).toBe(true);
|
||||||
|
expect(valueOf(css, "--via-var")).toBe(valueOf(css, "--inline"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves seeds that are themselves hct() tokens", async () => {
|
||||||
|
const { css } = await process(
|
||||||
|
[
|
||||||
|
":root {",
|
||||||
|
" --first: hct(200 30 60);",
|
||||||
|
" --second: hct(from var(--first) h c t);",
|
||||||
|
"}",
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(valueOf(css, "--second")).toBe(valueOf(css, "--first"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("uses :root seeds inside other selectors", async () => {
|
||||||
|
const { css } = await process(
|
||||||
|
[
|
||||||
|
":root { --seed: hct(90 50 40); }",
|
||||||
|
".dark { --from-root: hct(from var(--seed) h c t); }",
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(valueOf(css, "--from-root")).toBe(valueOf(css, "--seed"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves every hct() call inside one declaration", async () => {
|
||||||
|
const { css } = await process(":root { --pair: hct(0 0 10) hct(0 0 90); }");
|
||||||
|
|
||||||
|
expect(valueOf(css, "--pair")).toBe(
|
||||||
|
`${valueOf(css, "--pair").split(" ")[0]} ${valueOf(css, "--pair").split(" ")[1]}`,
|
||||||
|
);
|
||||||
|
expect(valueOf(css, "--pair").split(" ")).toHaveLength(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("leaves values without hct() untouched", async () => {
|
||||||
|
const source = ":root { --size: 12px; --name: red; --ref: var(--size); }";
|
||||||
|
const { css } = await process(source);
|
||||||
|
|
||||||
|
expect(css).toContain("--size: 12px");
|
||||||
|
expect(css).toContain("--name: red");
|
||||||
|
expect(css).toContain("--ref: var(--size)");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("warns and keeps the value when the seed is unknown", async () => {
|
||||||
|
const { css, warnings } = await process(
|
||||||
|
":root { --orphan: hct(from var(--missing) h c t); }",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(valueOf(css, "--orphan")).toBe("hct(from var(--missing) h c t)");
|
||||||
|
expect(warnings).toHaveLength(1);
|
||||||
|
expect(warnings[0]).toContain("could not resolve");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("warns and keeps the value on a circular reference", async () => {
|
||||||
|
const { css, warnings } = await process(
|
||||||
|
[
|
||||||
|
":root {",
|
||||||
|
" --ping: hct(from var(--pong) h c t);",
|
||||||
|
" --pong: hct(from var(--ping) h c t);",
|
||||||
|
"}",
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(valueOf(css, "--ping")).toBe("hct(from var(--pong) h c t)");
|
||||||
|
expect(warnings).toHaveLength(2);
|
||||||
|
expect(warnings.every((text) => text.includes("could not resolve"))).toBe(
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("warns and keeps the value for a malformed hct() body", async () => {
|
||||||
|
const { css, warnings } = await process(":root { --bad: hct(1 2); }");
|
||||||
|
|
||||||
|
expect(valueOf(css, "--bad")).toBe("hct(1 2)");
|
||||||
|
expect(warnings).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
import postcss from "postcss";
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { checkColorAuthorship } from "../token-audit/colors.mjs";
|
||||||
|
import {
|
||||||
|
collectTokens,
|
||||||
|
isColorValue,
|
||||||
|
isDerived,
|
||||||
|
} from "../token-audit/helpers.mjs";
|
||||||
|
import { checkParity } from "../token-audit/parity.mjs";
|
||||||
|
import { checkUnused } from "../token-audit/unused.mjs";
|
||||||
|
|
||||||
|
function themes(css) {
|
||||||
|
const root = postcss.parse(css);
|
||||||
|
return {
|
||||||
|
root,
|
||||||
|
light: collectTokens(root, ":root"),
|
||||||
|
dark: collectTokens(root, '[data-theme="dark"]'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("checkParity", () => {
|
||||||
|
it("accepts a color token that has both themes", () => {
|
||||||
|
const { light, dark } = themes(
|
||||||
|
':root { --color-text: #111; } [data-theme="dark"] { --color-text: #eee; }',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(checkParity({ light, dark })).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reports a light-only color token", () => {
|
||||||
|
const { light, dark } = themes(":root { --color-text: #111; }");
|
||||||
|
|
||||||
|
expect(checkParity({ light, dark })).toEqual([
|
||||||
|
' --color-text in :root has no [data-theme="dark"] override',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reports a dark-only color token as orphaned", () => {
|
||||||
|
const { light, dark } = themes(
|
||||||
|
'[data-theme="dark"] { --color-text: #eee; }',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(checkParity({ light, dark })).toEqual([
|
||||||
|
' --color-text in [data-theme="dark"] is missing in :root',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("exempts derived tokens because they follow the theme", () => {
|
||||||
|
const { light, dark } = themes(
|
||||||
|
":root { --color-text: hct(from var(--brand-main) h c t); }",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(checkParity({ light, dark })).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("exempts non-color tokens", () => {
|
||||||
|
const { light, dark } = themes(":root { --space-page-top: 48px; }");
|
||||||
|
|
||||||
|
expect(checkParity({ light, dark })).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("checkColorAuthorship", () => {
|
||||||
|
it("accepts hct() and color-mix()", () => {
|
||||||
|
const { root } = themes(
|
||||||
|
[
|
||||||
|
":root {",
|
||||||
|
" --color-main: hct(160 80 48);",
|
||||||
|
" --color-mixed: color-mix(in oklch, var(--color-main), #fff);",
|
||||||
|
"}",
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(checkColorAuthorship(root)).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("accepts seed tokens in any format", () => {
|
||||||
|
const { root } = themes(":root { --brand-main: #ff8800; }");
|
||||||
|
|
||||||
|
expect(checkColorAuthorship(root)).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reports a hardcoded hex color with its location", () => {
|
||||||
|
const { root } = themes(":root { --color-text: #112233; }");
|
||||||
|
|
||||||
|
expect(checkColorAuthorship(root)).toEqual([
|
||||||
|
" --color-text (:root): #112233 — colors must be hct(); only --brand-main/--brand-alt may use other formats",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("reports an rgb() color outside hct()", () => {
|
||||||
|
const { root } = themes(
|
||||||
|
'[data-theme="dark"] { --color-accent: rgb(1 2 3); }',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(checkColorAuthorship(root)[0]).toContain("--color-accent");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("ignores non-color values and non-custom properties", () => {
|
||||||
|
const { root } = themes(
|
||||||
|
":root { --font-size-m: 14px; } .x { color: #ff0000; }",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(checkColorAuthorship(root)).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("checkUnused", () => {
|
||||||
|
it("reports a token that is never referenced", () => {
|
||||||
|
const { root } = themes(":root { --color-dust: #111; }");
|
||||||
|
|
||||||
|
expect(checkUnused(root, new Set())).toEqual(["--color-dust"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps a token that is referenced somewhere", () => {
|
||||||
|
const { root } = themes(
|
||||||
|
":root { --color-text: #111; } .x { color: var(--color-text); }",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(checkUnused(root, new Set(["--color-text"]))).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("collects definitions across both themes", () => {
|
||||||
|
const { root } = themes(
|
||||||
|
[
|
||||||
|
":root { --color-text: #111; }",
|
||||||
|
'[data-theme="dark"] { --color-text: #eee; --color-dust: #222; }',
|
||||||
|
].join("\n"),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(checkUnused(root, new Set(["--color-text"]))).toEqual([
|
||||||
|
"--color-dust",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("token predicates", () => {
|
||||||
|
it("detects color values in every supported notation", () => {
|
||||||
|
for (const value of [
|
||||||
|
"#abc",
|
||||||
|
"#aabbcc",
|
||||||
|
"rgb(1 2 3)",
|
||||||
|
"hsl(1 2% 3%)",
|
||||||
|
"oklch(0.5 0.1 30)",
|
||||||
|
"hct(1 2 3)",
|
||||||
|
"color-mix(in oklch, #fff, #000)",
|
||||||
|
]) {
|
||||||
|
expect(isColorValue(value), value).toBe(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not treat sizes and durations as colors", () => {
|
||||||
|
for (const value of ["12px", "0.3s", "1fr", "bold", "var(--size-m)"]) {
|
||||||
|
expect(isColorValue(value), value).toBe(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("treats values referencing tokens as derived", () => {
|
||||||
|
expect(isDerived("hct(from var(--brand-main) h c t)")).toBe(true);
|
||||||
|
expect(isDerived("hct(160 80 48)")).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
// Unused tokens: defined in app.css but never referenced via var()
|
// Unused tokens: defined in app.css but never referenced via var()
|
||||||
// anywhere in src. A dead token is not the single source of truth — it's dust.
|
// anywhere in src. A dead token is not the single source of truth — it's dust.
|
||||||
// Reported as a warning; it does not fail the run.
|
// Reported as a warning; it does not fail the run. The `used` set is injectable
|
||||||
|
// so tests can pass a fixture instead of scanning src.
|
||||||
|
|
||||||
import { collectUsedTokens } from "./helpers.mjs";
|
import { collectUsedTokens } from "./helpers.mjs";
|
||||||
|
|
||||||
export function checkUnused(root) {
|
export function checkUnused(root, used = collectUsedTokens()) {
|
||||||
const defined = new Set();
|
const defined = new Set();
|
||||||
root.walkDecls((decl) => {
|
root.walkDecls((decl) => {
|
||||||
if (decl.prop.startsWith("--")) defined.add(decl.prop);
|
if (decl.prop.startsWith("--")) defined.add(decl.prop);
|
||||||
});
|
});
|
||||||
const used = collectUsedTokens();
|
|
||||||
return [...defined].filter((token) => !used.has(token));
|
return [...defined].filter((token) => !used.has(token));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ import { defineConfig } from "vitest/config";
|
|||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [svelte()],
|
plugins: [svelte()],
|
||||||
test: {
|
test: {
|
||||||
include: ["src/**/*.test.ts", "eslint-plugins/**/*.test.ts"],
|
include: [
|
||||||
|
"src/**/*.test.ts",
|
||||||
|
"eslint-plugins/**/*.test.ts",
|
||||||
|
"scripts/**/*.test.mjs",
|
||||||
|
],
|
||||||
environment: "node",
|
environment: "node",
|
||||||
coverage: {
|
coverage: {
|
||||||
provider: "v8",
|
provider: "v8",
|
||||||
|
|||||||
Reference in New Issue
Block a user