@ksmv/ui-checks 0.1.0 → 0.1.2
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 +13 -0
- package/README.md +141 -4
- package/dist/cli.js +40 -0
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.2 - 2026-09-28
|
|
4
|
+
|
|
5
|
+
- `./package.json` passa a ser exportado, como já era em `@ksmv/ui-react`. Ler a versão instalada por `require.resolve("@ksmv/ui-checks/package.json")` terminava com `ERR_PACKAGE_PATH_NOT_EXPORTED`.
|
|
6
|
+
- O README passa a ser a documentação completa para quem não vê o repositório de origem, que é privado: formato de `ksmv-ui.config.js`, `contractSource`, baseline, códigos de saída, quando a evidência calculada é necessária, o que a invalida e uma receita de captura com a API pública.
|
|
7
|
+
- `homepage` aponta para a página do pacote no npm, e `bugs` foi removido. Os dois levavam ao repositório privado, que responde 404 a quem está de fora.
|
|
8
|
+
- Nenhuma mudança nas verificações: regras, contratos e códigos de saída são os mesmos da 0.1.1.
|
|
9
|
+
|
|
10
|
+
## 0.1.1 - 2026-09-27
|
|
11
|
+
|
|
12
|
+
- A CLI responde a `--help`/`-h` e a `--version`/`-v` antes de ler qualquer configuração, com código de saída 0. Na 0.1.0 essas opções caíam no carregamento da configuração e terminavam com código 2, e `-h` era interpretado como caminho de arquivo de configuração.
|
|
13
|
+
- A ajuda lista os comandos de análise e de baseline, as opções que cada um aceita e os três códigos de saída.
|
|
14
|
+
- Sem configuração e sem essas opções, a CLI continua falhando fechada com código 2.
|
|
15
|
+
|
|
3
16
|
## 0.1.0 - 2026-09-22
|
|
4
17
|
|
|
5
18
|
- Primeira versão pública das verificações de contrato e acessibilidade.
|
package/README.md
CHANGED
|
@@ -1,12 +1,149 @@
|
|
|
1
1
|
# @ksmv/ui-checks
|
|
2
2
|
|
|
3
|
-
Verificações de qualidade para o contrato semântico `--ui
|
|
3
|
+
Verificações de qualidade para o contrato semântico `--ui-*` de `@ksmv/ui-react`: completude dos temas, contraste dos pares do contrato, prefixo dos tokens, cores literais fora dos arquivos de tema e evidência calculada vinculada às fontes.
|
|
4
|
+
|
|
5
|
+
## Antes de depender deste pacote
|
|
6
|
+
|
|
7
|
+
Os pacotes são publicados no registro público do npm para que as aplicações do próprio mantenedor os instalem sem autenticação. **O repositório de origem é privado.** Não há equipe de suporte, canal público de issues nem compromisso de atendimento a terceiros. Este README é a documentação completa para quem está de fora.
|
|
8
|
+
|
|
9
|
+
Vulnerabilidades não devem ser publicadas em nenhum canal público. Quem não tem acesso ao repositório relata pelo suporte do próprio npm, que encaminha relatos sobre pacotes publicados: https://www.npmjs.com/support
|
|
10
|
+
|
|
11
|
+
## Instalação e execução
|
|
4
12
|
|
|
5
13
|
```bash
|
|
6
14
|
npm install --save-dev @ksmv/ui-checks
|
|
7
|
-
npx
|
|
15
|
+
npx ksmv-ui-checks ksmv-ui.config.js
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Requer Node.js 22.14 ou superior. `npx ksmv-ui-checks --help` mostra os comandos; sem argumento, a configuração lida é `ksmv-ui.config.js` no diretório atual. `--json` troca o relatório legível por JSON.
|
|
19
|
+
|
|
20
|
+
## Configuração
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
// ksmv-ui.config.js
|
|
24
|
+
import { defineConfig } from "@ksmv/ui-checks/config";
|
|
25
|
+
|
|
26
|
+
export default defineConfig({
|
|
27
|
+
contractSource: "@ksmv/ui-react",
|
|
28
|
+
root: ".",
|
|
29
|
+
include: ["src/**/*.{css,tsx}"],
|
|
30
|
+
themes: [
|
|
31
|
+
{ name: "light", files: ["src/theme.css"], selector: '[data-theme="light"]' },
|
|
32
|
+
{ name: "dark", files: ["src/theme.css"], selector: '[data-theme="dark"]' },
|
|
33
|
+
],
|
|
34
|
+
baseline: ".ksmv-ui-baseline.json",
|
|
35
|
+
failOnUncovered: true,
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`contractSource` é obrigatório e explícito:
|
|
40
|
+
|
|
41
|
+
- `"@ksmv/ui-react"`: use quando a aplicação importa o pacote React. O verificador resolve os contratos da versão realmente instalada, então uma atualização do pacote é verificada contra os contratos novos;
|
|
42
|
+
- `"bundled"`: usa os contratos embutidos neste pacote, apenas para análises que não usam o pacote React, e falha fechado se encontrar essa importação.
|
|
43
|
+
|
|
44
|
+
O valor de `selector` deve ser **textualmente idêntico** ao seletor escrito no CSS, inclusive nas aspas. O verificador não tenta provar que grafias diferentes são equivalentes.
|
|
45
|
+
|
|
46
|
+
`include` precisa encontrar arquivos: nenhum arquivo analisado é falha, não sucesso. `exclude` e `suppressions` são opcionais.
|
|
47
|
+
|
|
48
|
+
## Baseline
|
|
49
|
+
|
|
50
|
+
`baseline` é opcional. Quando configurado, o arquivo precisa existir. Crie-o somente depois de revisar a análise completa:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npx ksmv-ui-checks baseline ksmv-ui.config.js --write --first-seen-version=0.1.2 --owner-role=frontend --reduction-target=2099-12-31
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
O arquivo guarda fingerprints, regra, papel responsável e meta de redução, sem copiar o trecho encontrado. Uma atualização que aumente a dívida exige `--allow-growth` e uma justificativa em arquivo via `--reason-file`. Erro operacional e cobertura obrigatória ausente nunca entram na baseline.
|
|
57
|
+
|
|
58
|
+
## Códigos de saída
|
|
59
|
+
|
|
60
|
+
- `0`: análise completa, sem violações novas;
|
|
61
|
+
- `1`: existe violação nova ou regressão em relação à baseline;
|
|
62
|
+
- `2`: configuração inválida, leitura ou operação falhou, nenhum arquivo encontrado, ou há cobertura obrigatória não resolvida.
|
|
63
|
+
|
|
64
|
+
Trate `2` como falha do pipeline, não como violação comum.
|
|
65
|
+
|
|
66
|
+
## Evidência calculada
|
|
67
|
+
|
|
68
|
+
Seletores disjuntos, como `[data-theme="light"]` e `[data-theme="dark"]`, são verificados estaticamente e dispensam evidência. Quando a cascata de um tema legado se sobrepõe, como `.theme` e `.theme.active`, só o navegador sabe o valor final de cada token. Nesse caso o tema declara `computedEvidence`:
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
{
|
|
72
|
+
name: "legacy",
|
|
73
|
+
files: ["src/theme.css"],
|
|
74
|
+
selector: ".theme.active",
|
|
75
|
+
computedEvidence: { file: "artifacts/theme-evidence.json", theme: "legacy" },
|
|
76
|
+
}
|
|
8
77
|
```
|
|
9
78
|
|
|
10
|
-
|
|
79
|
+
A evidência não é uma exceção manual. O arquivo é recusado, com código `2`, quando:
|
|
80
|
+
|
|
81
|
+
- `capturedAt` está no futuro;
|
|
82
|
+
- os seletores diferem dos configurados;
|
|
83
|
+
- o hash das fontes diverge, ou seja, o tema mudou depois da captura;
|
|
84
|
+
- a identidade de um dos contratos diverge da instalada, ou seja, o pacote React mudou depois da captura;
|
|
85
|
+
- falta algum token exigido, ou algum valor ainda contém `var(...)`.
|
|
86
|
+
|
|
87
|
+
A captura usa a API pública deste pacote e um navegador à sua escolha. Com Playwright, e a aplicação servida localmente:
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
// capture-theme-evidence.mjs
|
|
91
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
92
|
+
import { dirname, resolve } from "node:path";
|
|
93
|
+
import { computeThemeSourceHash, resolveContractSet } from "@ksmv/ui-checks";
|
|
94
|
+
import { chromium } from "playwright";
|
|
95
|
+
|
|
96
|
+
// Tudo aqui espelha ksmv-ui.config.js: o mesmo `root`, o mesmo
|
|
97
|
+
// `computedEvidence.file`, os mesmos arquivos de tema, e os mesmos nomes de
|
|
98
|
+
// `computedEvidence.theme` com seus seletores, cada um com uma URL em que o
|
|
99
|
+
// seletor está aplicado. O verificador resolve os caminhos a partir de `root`.
|
|
100
|
+
const root = resolve(".");
|
|
101
|
+
const evidenceFile = "artifacts/theme-evidence.json";
|
|
102
|
+
const themes = {
|
|
103
|
+
legacy: { selector: ".theme.active", url: "http://localhost:4173/" },
|
|
104
|
+
};
|
|
105
|
+
const files = ["src/theme.css"];
|
|
106
|
+
|
|
107
|
+
const contracts = await resolveContractSet({ root, source: "@ksmv/ui-react" });
|
|
108
|
+
const tokens = [...new Set([
|
|
109
|
+
...contracts.theme.required,
|
|
110
|
+
...Object.keys(contracts.theme.derived),
|
|
111
|
+
...Object.values(contracts.theme.derived),
|
|
112
|
+
...contracts.contrast.pairs.flatMap(({ background, foreground }) => [background, foreground]),
|
|
113
|
+
])];
|
|
114
|
+
|
|
115
|
+
const browser = await chromium.launch();
|
|
116
|
+
const captured = {};
|
|
117
|
+
for (const [name, { selector, url }] of Object.entries(themes)) {
|
|
118
|
+
const page = await browser.newPage();
|
|
119
|
+
await page.goto(url);
|
|
120
|
+
const values = await page.evaluate(({ selector, tokens }) => {
|
|
121
|
+
const style = getComputedStyle(document.querySelector(selector));
|
|
122
|
+
return Object.fromEntries(tokens.map((token) => [token, style.getPropertyValue(token).trim()]));
|
|
123
|
+
}, { selector, tokens });
|
|
124
|
+
captured[name] = { selector, tokens: values };
|
|
125
|
+
await page.close();
|
|
126
|
+
}
|
|
127
|
+
await browser.close();
|
|
128
|
+
|
|
129
|
+
const target = resolve(root, evidenceFile);
|
|
130
|
+
await mkdir(dirname(target), { recursive: true });
|
|
131
|
+
await writeFile(target, `${JSON.stringify({
|
|
132
|
+
capturedAt: new Date().toISOString(),
|
|
133
|
+
contracts: { contrast: contracts.identities.contrast, theme: contracts.identities.theme },
|
|
134
|
+
schemaVersion: 1,
|
|
135
|
+
selectors: [...new Set(Object.values(themes).map(({ selector }) => selector))],
|
|
136
|
+
sourceHash: await computeThemeSourceHash(root, files),
|
|
137
|
+
themes: captured,
|
|
138
|
+
}, null, 2)}\n`);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Capture de novo sempre que um arquivo de tema mudar ou o pacote React for atualizado; a verificação recusa a evidência antiga, que é exatamente o objetivo.
|
|
142
|
+
|
|
143
|
+
## API programática
|
|
144
|
+
|
|
145
|
+
O ponto de entrada `@ksmv/ui-checks` exporta `runChecks`, as regras, `resolveContractSet`, `computeThemeSourceHash`, `loadComputedEvidence`, as funções de baseline e os formatadores de relatório, com os tipos TypeScript correspondentes. Os contratos embutidos estão em `@ksmv/ui-checks/theme-contract.json` e `@ksmv/ui-checks/contrast-contract.json`.
|
|
146
|
+
|
|
147
|
+
## Licença
|
|
11
148
|
|
|
12
|
-
|
|
149
|
+
Apache-2.0.
|
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,28 @@ import { approveBaselineGrowth, baselineFor, validateBaseline, writeBaselineFile
|
|
|
6
6
|
import { applyConfiguredSuppressions } from "./governance.js";
|
|
7
7
|
import { formatHumanReport, formatJsonReport, } from "./result.js";
|
|
8
8
|
import { runChecks } from "./scan.js";
|
|
9
|
+
const usage = `Usage:
|
|
10
|
+
ksmv-ui-checks [config] [--json | --format=json]
|
|
11
|
+
ksmv-ui-checks baseline [config] --write --first-seen-version=<version>
|
|
12
|
+
--owner-role=<role> --reduction-target=<YYYY-MM-DD>
|
|
13
|
+
[--allow-growth --reason-file=<path>]
|
|
14
|
+
ksmv-ui-checks --help | -h
|
|
15
|
+
ksmv-ui-checks --version | -v
|
|
16
|
+
|
|
17
|
+
[config] defaults to ksmv-ui.config.js in the current directory.
|
|
18
|
+
|
|
19
|
+
Exit codes:
|
|
20
|
+
0 analysis complete, no new violations
|
|
21
|
+
1 a new violation, or a regression against the baseline
|
|
22
|
+
2 invalid configuration, a failed read or operation, no files found,
|
|
23
|
+
or mandatory coverage left unresolved
|
|
24
|
+
`;
|
|
25
|
+
// The manifest sits one level above both src/ and the published dist/, so the
|
|
26
|
+
// same relative URL resolves in the repository and in an installed package.
|
|
27
|
+
async function packageVersion() {
|
|
28
|
+
const manifest = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
|
|
29
|
+
return manifest.version;
|
|
30
|
+
}
|
|
9
31
|
function optionValue(argv, name) {
|
|
10
32
|
return argv
|
|
11
33
|
.find((argument) => argument.startsWith(`--${name}=`))
|
|
@@ -94,6 +116,24 @@ async function runBaselineWrite(config, argv) {
|
|
|
94
116
|
return 0;
|
|
95
117
|
}
|
|
96
118
|
export async function main(argv = process.argv.slice(2)) {
|
|
119
|
+
// Answered before anything else, and before any configuration is read. In
|
|
120
|
+
// 0.1.0 both flags fell through to configuration loading and failed with
|
|
121
|
+
// exit code 2, and `-h`, which does not start with `--`, was taken for a
|
|
122
|
+
// configuration path.
|
|
123
|
+
if (argv.some((argument) => argument === "--help" || argument === "-h")) {
|
|
124
|
+
process.stdout.write(usage);
|
|
125
|
+
return 0;
|
|
126
|
+
}
|
|
127
|
+
if (argv.some((argument) => argument === "--version" || argument === "-v")) {
|
|
128
|
+
try {
|
|
129
|
+
process.stdout.write(`${await packageVersion()}\n`);
|
|
130
|
+
return 0;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
process.stderr.write("ksmv-ui-checks: unable to read the package version.\n");
|
|
134
|
+
return 2;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
97
137
|
const useJson = argv.includes("--json") || argv.includes("--format=json");
|
|
98
138
|
const baselineCommand = argv[0] === "baseline";
|
|
99
139
|
const commandArguments = baselineCommand ? argv.slice(1) : argv;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ksmv/ui-checks",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Static and computed quality checks for semantic UI token contracts.",
|
|
5
5
|
"keywords": ["accessibility", "design-tokens", "quality", "static-analysis"],
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" },
|
|
11
11
|
"./config": { "types": "./dist/config.d.ts", "default": "./dist/config.js" },
|
|
12
12
|
"./theme-contract.json": "./dist/theme-contract.json",
|
|
13
|
-
"./contrast-contract.json": "./dist/contrast-contract.json"
|
|
13
|
+
"./contrast-contract.json": "./dist/contrast-contract.json",
|
|
14
|
+
"./package.json": "./package.json"
|
|
14
15
|
},
|
|
15
16
|
"files": ["CHANGELOG.md", "dist/**/*.d.ts", "dist/**/*.js", "dist/*.json"],
|
|
16
17
|
"scripts": {
|
|
@@ -27,7 +28,6 @@
|
|
|
27
28
|
"devDependencies": { "@types/node": "26.6.2", "vitest": "5.0.1" },
|
|
28
29
|
"engines": { "node": ">=22.14" },
|
|
29
30
|
"repository": { "type": "git", "url": "https://github.com/AxisGov/ui.git", "directory": "packages/checks" },
|
|
30
|
-
"homepage": "https://
|
|
31
|
-
"bugs": { "url": "https://github.com/AxisGov/ui/issues" },
|
|
31
|
+
"homepage": "https://www.npmjs.com/package/@ksmv/ui-checks",
|
|
32
32
|
"publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" }
|
|
33
33
|
}
|