@iclips/ui 2.0.3 → 3.1.0
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/DESIGN.md +246 -0
- package/MIGRATION-v3.md +244 -0
- package/README.md +4 -0
- package/dist/alert-theme.css +20 -79
- package/dist/components/ui/charts/donut-chart.cjs +1 -1
- package/dist/components/ui/charts/donut-chart.cjs.map +1 -1
- package/dist/components/ui/charts/donut-chart.js +39 -60
- package/dist/components/ui/charts/donut-chart.js.map +1 -1
- package/dist/components/ui/charts/kpi-card.cjs +1 -1
- package/dist/components/ui/charts/kpi-card.cjs.map +1 -1
- package/dist/components/ui/charts/kpi-card.js +19 -19
- package/dist/components/ui/charts/kpi-card.js.map +1 -1
- package/dist/components/ui/data/comment-system.cjs +1 -1
- package/dist/components/ui/data/comment-system.cjs.map +1 -1
- package/dist/components/ui/data/comment-system.js +67 -67
- package/dist/components/ui/data/comment-system.js.map +1 -1
- package/dist/components/ui/data/data-table.cjs +1 -1
- package/dist/components/ui/data/data-table.cjs.map +1 -1
- package/dist/components/ui/data/data-table.js +112 -101
- package/dist/components/ui/data/data-table.js.map +1 -1
- package/dist/components/ui/data/editorial-calendar.cjs +1 -1
- package/dist/components/ui/data/editorial-calendar.cjs.map +1 -1
- package/dist/components/ui/data/editorial-calendar.js +291 -291
- package/dist/components/ui/data/editorial-calendar.js.map +1 -1
- package/dist/components/ui/data/filter-builder.cjs +1 -1
- package/dist/components/ui/data/filter-builder.cjs.map +1 -1
- package/dist/components/ui/data/filter-builder.js +1 -1
- package/dist/components/ui/data/filter-builder.js.map +1 -1
- package/dist/components/ui/feedback/empty-state.cjs +1 -1
- package/dist/components/ui/feedback/empty-state.cjs.map +1 -1
- package/dist/components/ui/feedback/empty-state.js +14 -14
- package/dist/components/ui/feedback/empty-state.js.map +1 -1
- package/dist/components/ui/feedback/toast.cjs +1 -1
- package/dist/components/ui/feedback/toast.cjs.map +1 -1
- package/dist/components/ui/feedback/toast.js +53 -22
- package/dist/components/ui/feedback/toast.js.map +1 -1
- package/dist/components/ui/forms/calendar.cjs +1 -1
- package/dist/components/ui/forms/calendar.cjs.map +1 -1
- package/dist/components/ui/forms/calendar.js +16 -16
- package/dist/components/ui/forms/calendar.js.map +1 -1
- package/dist/components/ui/forms/combobox.cjs +1 -1
- package/dist/components/ui/forms/combobox.cjs.map +1 -1
- package/dist/components/ui/forms/combobox.js +78 -76
- package/dist/components/ui/forms/combobox.js.map +1 -1
- package/dist/components/ui/forms/file-attachment-list.cjs +1 -1
- package/dist/components/ui/forms/file-attachment-list.cjs.map +1 -1
- package/dist/components/ui/forms/file-attachment-list.js +4 -4
- package/dist/components/ui/forms/file-attachment-list.js.map +1 -1
- package/dist/components/ui/forms/file-upload-zone.cjs +2 -2
- package/dist/components/ui/forms/file-upload-zone.cjs.map +1 -1
- package/dist/components/ui/forms/file-upload-zone.js +55 -49
- package/dist/components/ui/forms/file-upload-zone.js.map +1 -1
- package/dist/components/ui/forms/form.cjs +1 -1
- package/dist/components/ui/forms/form.cjs.map +1 -1
- package/dist/components/ui/forms/form.js +11 -11
- package/dist/components/ui/forms/form.js.map +1 -1
- package/dist/components/ui/forms/input-validation.cjs +1 -1
- package/dist/components/ui/forms/input-validation.cjs.map +1 -1
- package/dist/components/ui/forms/input-validation.js +20 -20
- package/dist/components/ui/forms/input-validation.js.map +1 -1
- package/dist/components/ui/forms/multi-select.cjs +1 -1
- package/dist/components/ui/forms/multi-select.cjs.map +1 -1
- package/dist/components/ui/forms/multi-select.js +69 -67
- package/dist/components/ui/forms/multi-select.js.map +1 -1
- package/dist/components/ui/forms/radio-group.cjs +1 -1
- package/dist/components/ui/forms/radio-group.cjs.map +1 -1
- package/dist/components/ui/forms/radio-group.js +19 -19
- package/dist/components/ui/forms/radio-group.js.map +1 -1
- package/dist/components/ui/forms/social-button.cjs +1 -1
- package/dist/components/ui/forms/social-button.cjs.map +1 -1
- package/dist/components/ui/forms/social-button.js +6 -6
- package/dist/components/ui/forms/social-button.js.map +1 -1
- package/dist/components/ui/forms/toggle.cjs +1 -1
- package/dist/components/ui/forms/toggle.cjs.map +1 -1
- package/dist/components/ui/forms/toggle.js +7 -7
- package/dist/components/ui/forms/toggle.js.map +1 -1
- package/dist/components/ui/navigation/navigation-menu.cjs +1 -1
- package/dist/components/ui/navigation/navigation-menu.cjs.map +1 -1
- package/dist/components/ui/navigation/navigation-menu.js +14 -14
- package/dist/components/ui/navigation/navigation-menu.js.map +1 -1
- package/dist/components/ui/navigation/sidebar.cjs +1 -1
- package/dist/components/ui/navigation/sidebar.cjs.map +1 -1
- package/dist/components/ui/navigation/sidebar.js +44 -44
- package/dist/components/ui/navigation/sidebar.js.map +1 -1
- package/dist/components/ui/overlays/context-menu.cjs +1 -1
- package/dist/components/ui/overlays/context-menu.cjs.map +1 -1
- package/dist/components/ui/overlays/context-menu.js +1 -1
- package/dist/components/ui/overlays/context-menu.js.map +1 -1
- package/dist/components/ui/overlays/dropdown-menu.cjs +1 -1
- package/dist/components/ui/overlays/dropdown-menu.cjs.map +1 -1
- package/dist/components/ui/overlays/dropdown-menu.js +5 -5
- package/dist/components/ui/overlays/dropdown-menu.js.map +1 -1
- package/dist/components/ui/overlays/menubar.cjs +1 -1
- package/dist/components/ui/overlays/menubar.cjs.map +1 -1
- package/dist/components/ui/overlays/menubar.js +29 -29
- package/dist/components/ui/overlays/menubar.js.map +1 -1
- package/dist/components/ui/overlays/tooltip.cjs +1 -1
- package/dist/components/ui/overlays/tooltip.cjs.map +1 -1
- package/dist/components/ui/overlays/tooltip.js +12 -12
- package/dist/components/ui/overlays/tooltip.js.map +1 -1
- package/dist/components/ui/primitives/avatar.cjs +1 -1
- package/dist/components/ui/primitives/avatar.cjs.map +1 -1
- package/dist/components/ui/primitives/avatar.js +20 -27
- package/dist/components/ui/primitives/avatar.js.map +1 -1
- package/dist/components/ui/primitives/badge.cjs +1 -1
- package/dist/components/ui/primitives/badge.cjs.map +1 -1
- package/dist/components/ui/primitives/badge.js +21 -18
- package/dist/components/ui/primitives/badge.js.map +1 -1
- package/dist/components/ui/primitives/button.cjs +1 -1
- package/dist/components/ui/primitives/button.cjs.map +1 -1
- package/dist/components/ui/primitives/button.js +11 -8
- package/dist/components/ui/primitives/button.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +34 -35
- package/dist/index.js.map +1 -1
- package/dist/preset.css +1 -2
- package/dist/toast-theme.css +100 -64
- package/dist/tokens.css +59 -159
- package/docs/README.md +44 -0
- package/docs/acoes.md +103 -0
- package/docs/escolha.md +171 -0
- package/docs/formularios.md +137 -0
- package/docs/mensagens.md +114 -0
- package/docs/overlays.md +192 -0
- package/docs/status.md +71 -0
- package/package.json +7 -3
- package/scripts/check-colors.mjs +208 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// Aponta cor crua — `bg-gray-100`, `text-purple-600`, `bg-zinc-900`, `var(--teal-500)` —
|
|
2
|
+
// e sugere o token semântico da paleta iClips. Não altera nenhum arquivo.
|
|
3
|
+
//
|
|
4
|
+
// Na lib roda no `npm run lint` (sem argumentos varre `src`). Num app que usa a
|
|
5
|
+
// @iclips/ui, depois de subir para a 3.0.0:
|
|
6
|
+
//
|
|
7
|
+
// node node_modules/@iclips/ui/scripts/check-colors.mjs src
|
|
8
|
+
//
|
|
9
|
+
// Exit: 0 nada encontrado · 1 encontrou cor crua · 2 algum caminho não pôde ser
|
|
10
|
+
// lido (e nada foi encontrado). Uma linha com `check-colors-ignore` é pulada —
|
|
11
|
+
// para documentação que precisa mostrar o que não fazer.
|
|
12
|
+
//
|
|
13
|
+
// node scripts/check-colors.mjs --self-test checa as regras e o CLI
|
|
14
|
+
|
|
15
|
+
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
16
|
+
import { extname, join, relative } from "node:path";
|
|
17
|
+
|
|
18
|
+
// Paleta padrão do Tailwind v4 — nenhuma gera classe com o preset da 3.0.0.
|
|
19
|
+
const FAMILIAS = "slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose";
|
|
20
|
+
const PREFIXOS = "bg|text|border(?:-[xytrblse])?|ring(?:-offset)?|outline|fill|stroke|from|via|to|divide|placeholder|caret|accent|decoration|shadow";
|
|
21
|
+
const CLASSE = new RegExp(`(?<![\\w-])((?:[\\w\\[\\]&=.-]+:)*(${PREFIXOS})-(${FAMILIAS})-(\\d{2,3})(?:\\/\\d+)?)(?![\\w-])`, "g");
|
|
22
|
+
// Custom properties que deixaram de existir na 3.0.0.
|
|
23
|
+
const VAR_REMOVIDA = /var\(--((?:pink|blue|teal|orange)-\d+|purple-600|(?:red|green|cyan)-(?:50|100|200|500|700)|yellow-(?:50|100|200|700)|cta(?:-foreground)?)\)/g;
|
|
24
|
+
|
|
25
|
+
const EXTENSOES = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".mdx", ".html", ".vue", ".svelte", ".css"]);
|
|
26
|
+
const PULAR_DIR = new Set(["node_modules", "dist", ".git", "storybook-static", "coverage"]);
|
|
27
|
+
|
|
28
|
+
const ESTADO = { red: "destructive", green: "success", emerald: "success", yellow: "warning", amber: "warning", cyan: "info", sky: "info", blue: "info" };
|
|
29
|
+
const NEUTRO = new Set(["slate", "gray", "zinc", "neutral", "stone"]);
|
|
30
|
+
|
|
31
|
+
export function sugerir(classe) {
|
|
32
|
+
const m = new RegExp(`^(?:.*:)?(${PREFIXOS})-(${FAMILIAS})-(\\d{2,3})`).exec(classe);
|
|
33
|
+
if (!m) return null;
|
|
34
|
+
const [, prefixo, familia, degrauTxt] = m;
|
|
35
|
+
const degrau = Number(degrauTxt);
|
|
36
|
+
const base = prefixo.replace(/-[xytrblse]$/, "");
|
|
37
|
+
if (NEUTRO.has(familia)) {
|
|
38
|
+
if (base === "bg") return degrau <= 50 ? "bg-background ou bg-muted/40" : degrau <= 200 ? "bg-muted" : degrau >= 700 ? "bg-card (superfície escura)" : "bg-muted";
|
|
39
|
+
if (base === "text") return degrau >= 700 ? "text-foreground" : degrau >= 400 ? "text-muted-foreground" : "text-muted-foreground (400 ou mais claro nunca em texto)";
|
|
40
|
+
if (base === "border" || base === "divide" || base === "outline") return degrau >= 300 ? `${base}-input` : `${base}-border`;
|
|
41
|
+
if (base === "ring") return "ring-ring";
|
|
42
|
+
}
|
|
43
|
+
if (familia === "purple") {
|
|
44
|
+
if (base === "bg") return degrau <= 100 ? "bg-secondary (seleção)" : "bg-primary";
|
|
45
|
+
if (base === "text") return degrau >= 700 ? "text-secondary-foreground" : "text-primary";
|
|
46
|
+
if (base === "ring") return "ring-ring";
|
|
47
|
+
if (base === "border" || base === "outline") return `${base}-primary`;
|
|
48
|
+
}
|
|
49
|
+
const estado = ESTADO[familia];
|
|
50
|
+
if (estado) {
|
|
51
|
+
if (base === "text") return `text-${estado}-emphasis`;
|
|
52
|
+
if (base === "bg") return degrau <= 100 ? `bg-${estado}/10` : `bg-${estado}`;
|
|
53
|
+
if (base === "border") return `border-${estado}/20`;
|
|
54
|
+
return `${base}-${estado}`;
|
|
55
|
+
}
|
|
56
|
+
return "sem equivalente — a paleta iClips não tem esta cor; use um token semântico ou var(--chart-N) em dados";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function varrerTexto(texto) {
|
|
60
|
+
const achados = [];
|
|
61
|
+
texto.split(/\r?\n/).forEach((linha, i) => {
|
|
62
|
+
if (linha.includes("check-colors-ignore")) return;
|
|
63
|
+
for (const m of linha.matchAll(CLASSE)) achados.push({ linha: i + 1, classe: m[1], sugestao: sugerir(m[1]) });
|
|
64
|
+
for (const m of linha.matchAll(VAR_REMOVIDA)) achados.push({ linha: i + 1, classe: m[0], sugestao: "removida na 3.0.0 — use o token semântico" });
|
|
65
|
+
});
|
|
66
|
+
return achados;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function arquivos(caminho, erros) {
|
|
70
|
+
let st;
|
|
71
|
+
try {
|
|
72
|
+
st = statSync(caminho);
|
|
73
|
+
} catch {
|
|
74
|
+
erros.push(caminho);
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
if (st.isFile()) return [caminho];
|
|
78
|
+
const out = [];
|
|
79
|
+
for (const e of readdirSync(caminho, { withFileTypes: true })) {
|
|
80
|
+
if (e.isDirectory()) {
|
|
81
|
+
if (!PULAR_DIR.has(e.name)) out.push(...arquivos(join(caminho, e.name), erros));
|
|
82
|
+
} else if (EXTENSOES.has(extname(e.name)) && !/\.test\.[jt]sx?$/.test(e.name)) {
|
|
83
|
+
out.push(join(caminho, e.name));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function executar(caminhos, { out = console.log, err = console.error } = {}) {
|
|
90
|
+
const erros = [];
|
|
91
|
+
let total = 0;
|
|
92
|
+
for (const alvo of caminhos.length ? caminhos : ["src"]) {
|
|
93
|
+
for (const f of arquivos(alvo, erros)) {
|
|
94
|
+
let texto;
|
|
95
|
+
try {
|
|
96
|
+
texto = readFileSync(f, "utf8");
|
|
97
|
+
} catch {
|
|
98
|
+
erros.push(f);
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
for (const a of varrerTexto(texto)) {
|
|
102
|
+
total++;
|
|
103
|
+
out(`${relative(process.cwd(), f).replace(/\\/g, "/")}:${a.linha} ${a.classe} → ${a.sugestao}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
for (const e of erros) err(`check-colors: não foi possível ler ${e}`);
|
|
108
|
+
if (total) err(`check-colors: ${total} cor(es) crua(s) — a paleta iClips só aceita tokens semânticos.`);
|
|
109
|
+
return total ? 1 : erros.length ? 2 : 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function selfTest() {
|
|
113
|
+
const assert = (await import("node:assert/strict")).default;
|
|
114
|
+
const { mkdtempSync, writeFileSync, statSync: st, rmSync } = await import("node:fs");
|
|
115
|
+
const { tmpdir } = await import("node:os");
|
|
116
|
+
const { spawnSync } = await import("node:child_process");
|
|
117
|
+
const { fileURLToPath } = await import("node:url");
|
|
118
|
+
|
|
119
|
+
// detecção: toda família × todo prefixo base, com variante e opacidade
|
|
120
|
+
// lista literal (não derivada da regex): renomear ou esquecer uma família no FAMILIAS falha aqui
|
|
121
|
+
const familias = ["slate", "gray", "zinc", "neutral", "stone", "red", "orange", "amber", "yellow", "lime", "green", "emerald", "teal", "cyan", "sky", "blue", "indigo", "violet", "purple", "fuchsia", "pink", "rose"];
|
|
122
|
+
assert.deepEqual(FAMILIAS.split("|"), familias, "FAMILIAS cobre exatamente a paleta padrão do Tailwind v4");
|
|
123
|
+
const prefixos = ["bg", "text", "border", ...["x", "y", "t", "r", "b", "l", "s", "e"].map((l) => `border-${l}`), "ring", "ring-offset", "outline", "fill", "stroke", "from", "via", "to", "divide", "placeholder", "caret", "accent", "decoration", "shadow"];
|
|
124
|
+
assert.equal(familias.length, 22, "22 famílias do Tailwind");
|
|
125
|
+
for (const f of familias)
|
|
126
|
+
for (const p of prefixos) {
|
|
127
|
+
const c = `${p}-${f}-500`;
|
|
128
|
+
const achados = varrerTexto(`<div className="hover:${c}/40" />`);
|
|
129
|
+
assert.equal(achados.length, 1, `detecta ${c}`);
|
|
130
|
+
assert.equal(achados[0].classe, `hover:${c}/40`, `cita a classe inteira: ${c}`);
|
|
131
|
+
}
|
|
132
|
+
for (const ok of ["bg-primary", "text-muted-foreground", "bg-black/50", "text-white", "bg-white", "border-border", "text-success-emphasis", "bg-chart-2/15", "bg-[#24292F]", "grayscale", "text-gray", "blue-500", "bg-gray-5000"])
|
|
133
|
+
assert.equal(varrerTexto(`<div className="${ok}" />`).length, 0, `não acusa ${ok}`);
|
|
134
|
+
assert.equal(varrerTexto('style={{ color: "var(--teal-500)" }}').length, 1, "var removida");
|
|
135
|
+
assert.equal(varrerTexto('style={{ color: "var(--cta)" }}').length, 1, "var --cta removida");
|
|
136
|
+
assert.equal(varrerTexto('style={{ color: "var(--gray-100)" }}').length, 0, "var mantida");
|
|
137
|
+
assert.equal(varrerTexto('<p className="bg-teal-500" /> {/* check-colors-ignore */}').length, 0, "ignore");
|
|
138
|
+
|
|
139
|
+
// sugestões: um caso por ramo de sugerir(); as linhas marcadas (guia) espelham a tabela do MIGRATION-v3.md
|
|
140
|
+
const tabela = {
|
|
141
|
+
// neutros
|
|
142
|
+
"bg-gray-50": "bg-background ou bg-muted/40", /* guia */ "bg-gray-100": "bg-muted", /* guia */ "bg-gray-200": "bg-muted",
|
|
143
|
+
"bg-gray-400": "bg-muted", "bg-zinc-900": "bg-card (superfície escura)", /* guia */ "bg-gray-700": "bg-card (superfície escura)",
|
|
144
|
+
"text-gray-800": "text-foreground", /* guia */ "text-gray-900": "text-foreground", "text-gray-500": "text-muted-foreground", /* guia */
|
|
145
|
+
"text-gray-400": "text-muted-foreground", "text-gray-300": "text-muted-foreground (400 ou mais claro nunca em texto)",
|
|
146
|
+
"border-gray-200": "border-border", /* guia */ "border-gray-100": "border-border", "border-gray-300": "border-input", /* guia */
|
|
147
|
+
"divide-gray-200": "divide-border", "outline-slate-300": "outline-input", "ring-gray-300": "ring-ring",
|
|
148
|
+
// roxo = ação
|
|
149
|
+
"bg-purple-500": "bg-primary", /* guia */ "bg-purple-600": "bg-primary", "bg-purple-50": "bg-secondary (seleção)", /* guia */ "bg-purple-100": "bg-secondary (seleção)",
|
|
150
|
+
"text-purple-600": "text-primary", /* guia */ "text-purple-500": "text-primary", "text-purple-700": "text-secondary-foreground", /* guia */
|
|
151
|
+
"border-purple-500": "border-primary", /* guia */ "ring-purple-500": "ring-ring", /* guia */ "outline-purple-400": "outline-primary",
|
|
152
|
+
// estado
|
|
153
|
+
"text-red-600": "text-destructive-emphasis", /* guia */ "text-green-600": "text-success-emphasis", "text-yellow-700": "text-warning-emphasis", "text-blue-600": "text-info-emphasis",
|
|
154
|
+
"text-amber-700": "text-warning-emphasis", "text-emerald-600": "text-success-emphasis", "text-sky-600": "text-info-emphasis", "text-cyan-600": "text-info-emphasis",
|
|
155
|
+
"bg-red-600": "bg-destructive", /* guia */ "bg-red-50": "bg-destructive/10", "bg-green-100": "bg-success/10", "bg-blue-500": "bg-info",
|
|
156
|
+
"border-red-200": "border-destructive/20", /* guia */ "fill-green-600": "fill-success", "hover:bg-gray-100": "bg-muted", "dark:bg-zinc-900": "bg-card (superfície escura)",
|
|
157
|
+
};
|
|
158
|
+
for (const [c, esperado] of Object.entries(tabela)) assert.equal(sugerir(c), esperado, c);
|
|
159
|
+
for (const semEquivalente of ["bg-teal-500", "text-orange-600", "bg-pink-500", "text-indigo-500", "bg-violet-500", "text-violet-600", "border-rose-300", "fill-lime-400", "bg-fuchsia-500"])
|
|
160
|
+
assert.match(sugerir(semEquivalente), /^sem equivalente/, `${semEquivalente} sem equivalente`);
|
|
161
|
+
|
|
162
|
+
// CLI de verdade
|
|
163
|
+
const script = fileURLToPath(import.meta.url);
|
|
164
|
+
const run = (args, cwd) => spawnSync(process.execPath, [script, ...args], { cwd, encoding: "utf8" });
|
|
165
|
+
const dir = mkdtempSync(join(tmpdir(), "check-colors-"));
|
|
166
|
+
try {
|
|
167
|
+
const limpo = join(dir, "limpo");
|
|
168
|
+
const sujo = join(dir, "sujo");
|
|
169
|
+
for (const d of [limpo, sujo]) spawnSync(process.execPath, ["-e", `require("fs").mkdirSync(${JSON.stringify(d)})`]);
|
|
170
|
+
writeFileSync(join(limpo, "a.tsx"), '<div className="bg-primary text-white" />\n');
|
|
171
|
+
const alvo = join(sujo, "b.tsx");
|
|
172
|
+
writeFileSync(alvo, 'const x = 1;\n<div className="bg-teal-500" />\n<p className="text-gray-600" />\n');
|
|
173
|
+
writeFileSync(join(sujo, "b.test.tsx"), '<div className="bg-teal-500" />\n');
|
|
174
|
+
const antes = { txt: readFileSync(alvo, "utf8"), mtime: st(alvo).mtimeMs };
|
|
175
|
+
|
|
176
|
+
let r = run([limpo], dir);
|
|
177
|
+
assert.equal(r.status, 0, "dir limpo → exit 0");
|
|
178
|
+
|
|
179
|
+
r = run([sujo], dir);
|
|
180
|
+
assert.equal(r.status, 1, "dir sujo → exit 1");
|
|
181
|
+
const linhas = r.stdout.trim().split("\n");
|
|
182
|
+
assert.equal(linhas.length, 2, "lista todas as ocorrências (e ignora *.test.*)");
|
|
183
|
+
assert.match(linhas[0], /sujo\/b\.tsx:2 {2}bg-teal-500 {2}→ sem equivalente/, "cita arquivo:linha e a classe");
|
|
184
|
+
assert.match(linhas[1], /sujo\/b\.tsx:3 {2}text-gray-600 {2}→ text-muted-foreground/, "sugere o token");
|
|
185
|
+
|
|
186
|
+
assert.equal(readFileSync(alvo, "utf8"), antes.txt, "não altera o conteúdo");
|
|
187
|
+
assert.equal(st(alvo).mtimeMs, antes.mtime, "não toca o arquivo");
|
|
188
|
+
|
|
189
|
+
r = run([join(dir, "nao-existe")], dir);
|
|
190
|
+
assert.equal(r.status, 2, "caminho ilegível sem achado → exit 2");
|
|
191
|
+
assert.match(r.stderr, /não foi possível ler/, "avisa no stderr");
|
|
192
|
+
r = run([join(dir, "nao-existe"), sujo], dir);
|
|
193
|
+
assert.equal(r.status, 1, "ilegível + achado → continua e sai 1");
|
|
194
|
+
} finally {
|
|
195
|
+
rmSync(dir, { recursive: true, force: true });
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// o lint da lib encadeia o script
|
|
199
|
+
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
200
|
+
assert.match(pkg.scripts.lint, /node scripts\/check-colors\.mjs/, "npm run lint roda check-colors");
|
|
201
|
+
|
|
202
|
+
console.log("check-colors: self-test ok");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (process.argv[1] && import.meta.url.endsWith(process.argv[1].replace(/\\/g, "/").split("/").pop())) {
|
|
206
|
+
if (process.argv.includes("--self-test")) await selfTest();
|
|
207
|
+
else process.exitCode = executar(process.argv.slice(2));
|
|
208
|
+
}
|