synthesisui 0.16.494 → 0.16.496
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/dist/commands/doctor.js +28 -0
- package/dist/commands/template.js +9 -3
- package/dist/config.js +10 -1
- package/dist/doctor/distinct-values.js +251 -0
- package/dist/doctor/scan.js +4 -1
- package/package.json +1 -1
package/dist/commands/doctor.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { describeDistinct, describeDistinctVerbose, distinctValues } from "../doctor/distinct-values.js";
|
|
1
2
|
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
2
3
|
import { readStyleSheet, scssVariables } from "../doctor/scss-lens.js";
|
|
3
4
|
import { THEME_SOURCE, tsTokenObjects, tsTokensAsVars } from "../doctor/ts-tokens-lens.js";
|
|
@@ -1143,6 +1144,22 @@ export async function doctor(opts) {
|
|
|
1143
1144
|
}
|
|
1144
1145
|
}
|
|
1145
1146
|
}
|
|
1147
|
+
/**
|
|
1148
|
+
* QUANTOS VALORES DIFERENTES, E DE ONDE - ver `distinct-values.ts` (jornada "o doctor conta o que ninguém escolheu").
|
|
1149
|
+
*
|
|
1150
|
+
* Fora do bloco da cobertura de propósito: um projeto sem token nenhum não tem cobertura para medir, e é justamente
|
|
1151
|
+
* nele que "8 cinzas da caixa do framework" é a frase que falta.
|
|
1152
|
+
*/
|
|
1153
|
+
const counted = distinctValues(reports, table);
|
|
1154
|
+
const countedBy = table.source === "installed" ? systemName : null;
|
|
1155
|
+
const distinct = describeDistinct(counted, countedBy);
|
|
1156
|
+
if (distinct.length > 0) {
|
|
1157
|
+
console.log("");
|
|
1158
|
+
const [head, ...rest] = distinct;
|
|
1159
|
+
console.log(body(head));
|
|
1160
|
+
for (const line of rest)
|
|
1161
|
+
console.log(body(paint.dim(` ${line.trimStart()}`)));
|
|
1162
|
+
}
|
|
1146
1163
|
/**
|
|
1147
1164
|
* THE RECORD - the layer that remembers, printed where the moment is shown.
|
|
1148
1165
|
*
|
|
@@ -1671,6 +1688,17 @@ export async function doctor(opts) {
|
|
|
1671
1688
|
say("");
|
|
1672
1689
|
}
|
|
1673
1690
|
}
|
|
1691
|
+
/**
|
|
1692
|
+
* CADA VALOR CONTADO, com a classe que o trouxe - só no `--verbose`, e numa seção própria depois da lista de valores à
|
|
1693
|
+
* mão: é o detalhe para conferir o número contra uma contagem feita por fora, não a primeira coisa que se lê.
|
|
1694
|
+
*/
|
|
1695
|
+
const oneByOne = describeDistinctVerbose(counted, countedBy);
|
|
1696
|
+
if (verbose && oneByOne.length > 0) {
|
|
1697
|
+
say(section("Different values, one by one"));
|
|
1698
|
+
for (const line of oneByOne)
|
|
1699
|
+
say(body(line));
|
|
1700
|
+
say("");
|
|
1701
|
+
}
|
|
1674
1702
|
say(section("What this means"));
|
|
1675
1703
|
for (const line of verdict(d, hasSystem, overrides.length, conflictsInUse.length))
|
|
1676
1704
|
say(line);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
1
|
+
import { mkdir, stat, writeFile } from "node:fs/promises";
|
|
2
2
|
import { dirname, join, relative } from "node:path";
|
|
3
3
|
import { readProjectConfig, resolveRegistry } from "../config.js";
|
|
4
4
|
import { installedSheetCss, RECIPE_HEADER, recipeRulesFor, } from "../recipe-css.js";
|
|
@@ -61,7 +61,13 @@ export async function template(slug, name, opts) {
|
|
|
61
61
|
// a loose landing.tsx at the app/ root read as a route without being one,
|
|
62
62
|
// and a second template turned the app dir into soup.
|
|
63
63
|
const [pageFile, ...siblings] = generated.files;
|
|
64
|
-
|
|
64
|
+
/**
|
|
65
|
+
* DENTRO DE `src/` QUANDO O APP MORA LÁ (14/10, três agentes do zero seguidos): o `create-next-app` com `src/` aponta o
|
|
66
|
+
* `@/` para `src/`, e os templates iam para `templates/` na raiz - o import que a frase abaixo sugere não resolvia, e o
|
|
67
|
+
* agente movia a pasta à mão. Com `src/app`, a pasta é `src/templates/<nome>`; sem, a raiz de sempre.
|
|
68
|
+
*/
|
|
69
|
+
const underSrc = await stat(join(root, "src", "app")).then((s) => s.isDirectory(), () => false);
|
|
70
|
+
const defaultDir = join(underSrc ? join("src", "templates") : "templates", asName ?? name);
|
|
65
71
|
const pageRel = opts.out ?? join(defaultDir, pageFile.filename);
|
|
66
72
|
const pageDir = dirname(join(root, pageRel));
|
|
67
73
|
/**
|
|
@@ -228,7 +234,7 @@ export async function template(slug, name, opts) {
|
|
|
228
234
|
console.log("");
|
|
229
235
|
console.log("Next steps:");
|
|
230
236
|
console.log(` • use it in a route, e.g. ${join(config.pagesDir, "page.tsx")}:`);
|
|
231
|
-
console.log(` import Page from "@/${defaultDir.replace(/\\/g, "/")}/${pageFile.filename.replace(/\.tsx$/, "")}";`);
|
|
237
|
+
console.log(` import Page from "@/${defaultDir.replace(/\\/g, "/").replace(/^src\//, "")}/${pageFile.filename.replace(/\.tsx$/, "")}";`);
|
|
232
238
|
console.log(` • nothing to install: this page and its stylesheet stand on their own`);
|
|
233
239
|
console.log(" • refine the file: wire real data, split into components, swap placeholders");
|
|
234
240
|
/**
|
package/dist/config.js
CHANGED
|
@@ -251,7 +251,16 @@ export function describeIntent(source) {
|
|
|
251
251
|
source.towards
|
|
252
252
|
? `derived: this group's reference is \`${source.towards}\`, and your vocabulary is converging on it`
|
|
253
253
|
: "derived: this is the group's reference system, so the design moves in"
|
|
254
|
-
:
|
|
254
|
+
: /**
|
|
255
|
+
* A ORDEM, dita como ordem - jornada "o doctor conta o que ninguém escolheu", 15/10.
|
|
256
|
+
*
|
|
257
|
+
* Era *"nobody chose, so this is the default"*, e a aula 01 do curso leu a frase como se fosse sobre os
|
|
258
|
+
* VALORES da página - a mesma página em que 53 usos da paleta padrão do Tailwind não entravam em conta
|
|
259
|
+
* nenhuma. O que ninguém escolheu aqui é a ordem da lista (nem `--migrate`, nem `--adopt`), e a frase
|
|
260
|
+
* precisa dizer isso sem que quem lê o terminal abra o código. Sem "nobody", também: a linha de valores
|
|
261
|
+
* distintos diz que ninguém escolheu a paleta, e duas frases iguais sobre coisas diferentes são a confusão.
|
|
262
|
+
*/
|
|
263
|
+
"default order - you never set one";
|
|
255
264
|
const flip = source.intent === "migrate"
|
|
256
265
|
? "`doctor --adopt` sorts the other way"
|
|
257
266
|
: "`doctor --migrate` sorts the other way";
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { fragmentsOfSource } from "./fragments.js";
|
|
2
|
+
import { normalizeValue } from "./tokens.js";
|
|
3
|
+
import { parseClass, readUtility } from "./transcribe.js";
|
|
4
|
+
const COLOUR_PROPERTIES = new Set([
|
|
5
|
+
"color",
|
|
6
|
+
"backgroundColor",
|
|
7
|
+
"borderColor",
|
|
8
|
+
"borderTopColor",
|
|
9
|
+
"borderRightColor",
|
|
10
|
+
"borderBottomColor",
|
|
11
|
+
"borderLeftColor",
|
|
12
|
+
"borderInlineColor",
|
|
13
|
+
"borderBlockColor",
|
|
14
|
+
"outlineColor",
|
|
15
|
+
"textDecorationColor",
|
|
16
|
+
"caretColor",
|
|
17
|
+
"accentColor",
|
|
18
|
+
"fill",
|
|
19
|
+
"stroke",
|
|
20
|
+
]);
|
|
21
|
+
const SPACING_PROPERTY = /^(padding|margin)(Inline|Block|Top|Right|Bottom|Left|InlineStart|InlineEnd|BlockStart|BlockEnd)?$|^(gap|rowGap|columnGap)$/;
|
|
22
|
+
/**
|
|
23
|
+
* AS UTILITIES DE COR QUE O TRADUTOR NÃO LÊ SOZINHAS - `divide-stone-200`, `from-stone-50`, `ring-stone-300`.
|
|
24
|
+
*
|
|
25
|
+
* O tradutor existe para transcrever um componente, e lá um `divide-*` é regra sobre os filhos, não declaração do
|
|
26
|
+
* elemento. Aqui a pergunta é outra - que cor a página usa -, e a cor é a mesma de `text-stone-200`. O prefixo é trocado
|
|
27
|
+
* só quando a leitura devolve uma COR: `ring-2` e `shadow-lg` continuam sem leitura.
|
|
28
|
+
*/
|
|
29
|
+
const COLOUR_ONLY_PREFIX = /^(divide|ring-offset|ring|from|via|to|decoration|outline|placeholder|caret|fill|stroke|shadow|inset-shadow)-(.+)$/;
|
|
30
|
+
/** O que não é escolha de valor: herdar, apagar, seguir a cor do texto. */
|
|
31
|
+
const NOT_A_VALUE = /^(transparent|currentcolor|current|inherit|initial|unset|auto|none)$/i;
|
|
32
|
+
/** As duas cores da caixa que o tradutor devolve em hex, sem o carimbo da paleta. */
|
|
33
|
+
const FRAMEWORK_PLAIN = /^(?:[a-z-]+-)?(white|black)$/;
|
|
34
|
+
/** O fim da classe, sem o alfa: `text-accent/80` é o `accent` dele, com transparência - a mesma cor escolhida. */
|
|
35
|
+
const withoutAlpha = (utility) => utility.includes("[") ? utility : utility.replace(/\/\d{1,3}$/, "");
|
|
36
|
+
/**
|
|
37
|
+
* AS PALAVRAS DE CLASSE DE UM ARQUIVO - pelo mesmo extrator que alimenta o ledger, então o que conta aqui é o que o resto
|
|
38
|
+
* do produto chama de classe: `className="…"`, template, e os literais dentro de `cn(…)`.
|
|
39
|
+
*
|
|
40
|
+
* Num template, o pedaço `${…}` é runtime e sai antes do corte: ele não está na fonte.
|
|
41
|
+
*/
|
|
42
|
+
export function classWordsOf(file, source) {
|
|
43
|
+
const words = new Set();
|
|
44
|
+
for (const f of fragmentsOfSource(file, source)) {
|
|
45
|
+
if (f.shape !== "class" && f.shape !== "template")
|
|
46
|
+
continue;
|
|
47
|
+
const text = f.shape === "template" ? f.text.replace(/\$\{[^}]*\}/g, " ") : f.text;
|
|
48
|
+
for (const w of text.split(/\s+/))
|
|
49
|
+
if (w)
|
|
50
|
+
words.add(w);
|
|
51
|
+
}
|
|
52
|
+
return [...words];
|
|
53
|
+
}
|
|
54
|
+
function lengthKey(value, rootPx) {
|
|
55
|
+
const m = /^-?(\d*\.?\d+)(px|rem)$/.exec(value.trim());
|
|
56
|
+
if (!m)
|
|
57
|
+
return null;
|
|
58
|
+
const px = Number(m[1]) * (m[2] === "rem" ? rootPx : 1);
|
|
59
|
+
/** `p-0` não é degrau de escala nenhum: é a ausência de espaço. */
|
|
60
|
+
return px === 0 ? null : `${Number(px.toFixed(3))}px`;
|
|
61
|
+
}
|
|
62
|
+
function readWord(word, declared, isSystem, rootPx) {
|
|
63
|
+
const utility = withoutAlpha(parseClass(word).utility.replace(/^!/, "").replace(/!$/, ""));
|
|
64
|
+
let decl = readUtility(utility, declared);
|
|
65
|
+
const prefixed = COLOUR_ONLY_PREFIX.exec(utility);
|
|
66
|
+
if (!decl && prefixed) {
|
|
67
|
+
const asText = readUtility(`text-${prefixed[2]}`, declared);
|
|
68
|
+
if (asText?.property === "color")
|
|
69
|
+
decl = asText;
|
|
70
|
+
}
|
|
71
|
+
if (!decl)
|
|
72
|
+
return null;
|
|
73
|
+
const kind = COLOUR_PROPERTIES.has(decl.property)
|
|
74
|
+
? "colour"
|
|
75
|
+
: decl.property === "fontSize"
|
|
76
|
+
? "textSize"
|
|
77
|
+
: SPACING_PROPERTY.test(decl.property)
|
|
78
|
+
? "spacing"
|
|
79
|
+
: null;
|
|
80
|
+
if (!kind || NOT_A_VALUE.test(decl.value.trim()))
|
|
81
|
+
return null;
|
|
82
|
+
const origin = decl.token
|
|
83
|
+
? isSystem(decl.token)
|
|
84
|
+
? "system"
|
|
85
|
+
: "own"
|
|
86
|
+
: utility.includes("[")
|
|
87
|
+
? "hand"
|
|
88
|
+
: "framework";
|
|
89
|
+
if (kind === "colour") {
|
|
90
|
+
const key = decl.token ?? decl.value.toLowerCase().trim().replace(/\s*,\s*/g, ",").replace(/\s+/g, " ");
|
|
91
|
+
const plain = FRAMEWORK_PLAIN.exec(utility);
|
|
92
|
+
const family = origin !== "framework"
|
|
93
|
+
? undefined
|
|
94
|
+
: plain
|
|
95
|
+
? plain[1]
|
|
96
|
+
: /-([a-z]+)-\d{2,3}$/.exec(utility)?.[1];
|
|
97
|
+
return { kind, key, origin, ...(family ? { family } : {}) };
|
|
98
|
+
}
|
|
99
|
+
const key = decl.token ?? lengthKey(decl.value, rootPx);
|
|
100
|
+
return key ? { kind, key, origin } : null;
|
|
101
|
+
}
|
|
102
|
+
const byLength = (a, b) => {
|
|
103
|
+
const x = Number.parseFloat(a);
|
|
104
|
+
const y = Number.parseFloat(b);
|
|
105
|
+
return Number.isNaN(x) || Number.isNaN(y) ? a.localeCompare(b) : x - y;
|
|
106
|
+
};
|
|
107
|
+
/** Quando o mesmo valor chega por dois caminhos, ele conta UMA vez, pelo caminho que tem nome. */
|
|
108
|
+
const PRIORITY = ["system", "own", "framework", "hand"];
|
|
109
|
+
export function distinctValues(reports, table) {
|
|
110
|
+
/**
|
|
111
|
+
* OS TOKENS DO PROJETO INTEIRO, na forma que o tradutor lê (`--color-ink` -> valor). Uma variável é declarada no CSS
|
|
112
|
+
* global e usada no `.tsx`, então só a varredura inteira responde se `text-ink` é dele.
|
|
113
|
+
*/
|
|
114
|
+
const declared = new Map();
|
|
115
|
+
for (const r of reports)
|
|
116
|
+
for (const d of r.ownDeclared ?? [])
|
|
117
|
+
if (!declared.has(d.name))
|
|
118
|
+
declared.set(d.name, d.value);
|
|
119
|
+
for (const [name, value] of table.byName)
|
|
120
|
+
if (!declared.has(name))
|
|
121
|
+
declared.set(name, value);
|
|
122
|
+
/**
|
|
123
|
+
* DO SISTEMA INSTALADO É O TOKEN QUE ELE TAMBÉM SEGURA - pelo nome que a tabela gera, ou pelo VALOR.
|
|
124
|
+
*
|
|
125
|
+
* O `init` escreve o tema do sistema no CSS dele, com os nomes dele (`--color-honey-500`, `--text-sm`), e esses nomes
|
|
126
|
+
* não estão no índice de utilities. Medido no projeto da aula com o Moss em 15/10: pelo nome só, 3 cores e 5 tamanhos
|
|
127
|
+
* escritos pelo `init` saíam como "your own tokens". Pelo valor é a mesma relação que o relatório já diz em "hold a
|
|
128
|
+
* value Moss also names". Sem sistema instalado, todo token é do projeto.
|
|
129
|
+
*/
|
|
130
|
+
const installed = table.source === "installed";
|
|
131
|
+
const systemNames = new Set(installed ? table.utilities.values() : []);
|
|
132
|
+
const isSystem = (token) => {
|
|
133
|
+
if (!installed)
|
|
134
|
+
return false;
|
|
135
|
+
if (systemNames.has(token) || token.startsWith("--ds-"))
|
|
136
|
+
return true;
|
|
137
|
+
const value = declared.get(token);
|
|
138
|
+
return value !== undefined && table.byValue.has(normalizeValue(value, table.rootPx));
|
|
139
|
+
};
|
|
140
|
+
const seen = {
|
|
141
|
+
colour: new Map(),
|
|
142
|
+
textSize: new Map(),
|
|
143
|
+
spacing: new Map(),
|
|
144
|
+
};
|
|
145
|
+
const families = new Set();
|
|
146
|
+
const keep = (kind, key, origin, example) => {
|
|
147
|
+
const before = seen[kind].get(key);
|
|
148
|
+
if (!before || PRIORITY.indexOf(origin) < PRIORITY.indexOf(before.origin))
|
|
149
|
+
seen[kind].set(key, { origin, example });
|
|
150
|
+
};
|
|
151
|
+
for (const r of reports) {
|
|
152
|
+
for (const word of r.classWords ?? []) {
|
|
153
|
+
const read = readWord(word, declared, isSystem, table.rootPx);
|
|
154
|
+
if (!read)
|
|
155
|
+
continue;
|
|
156
|
+
keep(read.kind, read.key, read.origin, word);
|
|
157
|
+
if (read.family)
|
|
158
|
+
families.add(read.family);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* A COR ESCRITA À MÃO FORA DE CLASSE - `#e7e5e4` num `style`, um `rgba()` dentro de uma sombra. São os achados de
|
|
162
|
+
* cor que o doctor já lista em "Values written by hand", então os dois números falam da mesma coisa.
|
|
163
|
+
*/
|
|
164
|
+
for (const f of r.findings)
|
|
165
|
+
if (f.kind === "color")
|
|
166
|
+
keep("colour", f.literal.toLowerCase().trim().replace(/\s*,\s*/g, ",").replace(/\s+/g, " "), "hand", `${r.file}:${f.line}`);
|
|
167
|
+
}
|
|
168
|
+
const count = (kind) => {
|
|
169
|
+
const by = { framework: 0, system: 0, own: 0, hand: 0 };
|
|
170
|
+
for (const { origin } of seen[kind].values())
|
|
171
|
+
by[origin] += 1;
|
|
172
|
+
return {
|
|
173
|
+
total: seen[kind].size,
|
|
174
|
+
by,
|
|
175
|
+
families: kind === "colour" ? [...families].sort() : [],
|
|
176
|
+
values: [...seen[kind]]
|
|
177
|
+
.map(([key, { origin, example }]) => ({ key, origin, example }))
|
|
178
|
+
.sort((a, b) => PRIORITY.indexOf(a.origin) - PRIORITY.indexOf(b.origin) || byLength(a.key, b.key)),
|
|
179
|
+
};
|
|
180
|
+
};
|
|
181
|
+
return { colour: count("colour"), textSize: count("textSize"), spacing: count("spacing") };
|
|
182
|
+
}
|
|
183
|
+
const NOUN = {
|
|
184
|
+
colour: ["colour", "colours"],
|
|
185
|
+
textSize: ["text size", "text sizes"],
|
|
186
|
+
spacing: ["spacing", "spacings"],
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* AS LINHAS DO RELATÓRIO. Curta quando tudo vem de um nome; aberta por origem quando alguma coisa veio da caixa do
|
|
190
|
+
* framework ou da mão - que é exatamente o que ninguém escolheu.
|
|
191
|
+
*/
|
|
192
|
+
export function describeDistinct(v, systemName) {
|
|
193
|
+
const kinds = ["colour", "textSize", "spacing"];
|
|
194
|
+
const present = kinds.filter((k) => v[k].total > 0);
|
|
195
|
+
if (present.length === 0)
|
|
196
|
+
return [];
|
|
197
|
+
const noun = (k, n) => `${n} ${NOUN[k][n === 1 ? 0 : 1]}`;
|
|
198
|
+
const head = present.map((k) => noun(k, v[k].total)).join(" · ");
|
|
199
|
+
const unchosen = present.some((k) => v[k].by.framework + v[k].by.hand > 0);
|
|
200
|
+
if (!unchosen) {
|
|
201
|
+
const whose = present.every((k) => v[k].by.own === 0) && systemName
|
|
202
|
+
? `from ${systemName}`
|
|
203
|
+
: present.every((k) => v[k].by.system === 0)
|
|
204
|
+
? "from your own tokens"
|
|
205
|
+
: `from ${systemName ?? "the system"} and your own tokens`;
|
|
206
|
+
return [`Different values ${head} - all ${whose}`];
|
|
207
|
+
}
|
|
208
|
+
const lines = [`Different values ${head}`];
|
|
209
|
+
const width = Math.max(...present.map((k) => NOUN[k][1].length));
|
|
210
|
+
for (const k of present) {
|
|
211
|
+
const { by, families } = v[k];
|
|
212
|
+
const parts = [];
|
|
213
|
+
if (by.framework > 0)
|
|
214
|
+
parts.push(k === "colour"
|
|
215
|
+
? `${by.framework} from the framework palette${families.length > 0 ? ` (${families.join(", ")})` : ""}`
|
|
216
|
+
: `${by.framework} from the framework scale`);
|
|
217
|
+
if (by.system > 0)
|
|
218
|
+
parts.push(`${by.system} from ${systemName ?? "the system"}`);
|
|
219
|
+
if (by.own > 0)
|
|
220
|
+
parts.push(`${by.own} from your own tokens`);
|
|
221
|
+
if (by.hand > 0)
|
|
222
|
+
parts.push(`${by.hand} by hand`);
|
|
223
|
+
lines.push(` ${NOUN[k][1].padEnd(width)} ${parts.join(", ")}`);
|
|
224
|
+
}
|
|
225
|
+
if (v.colour.by.framework + v.textSize.by.framework + v.spacing.by.framework > 0)
|
|
226
|
+
lines.push(" the framework ones came in the box - nobody in this project picked them");
|
|
227
|
+
return lines;
|
|
228
|
+
}
|
|
229
|
+
const ORIGIN_LABEL = {
|
|
230
|
+
framework: "framework",
|
|
231
|
+
system: "system",
|
|
232
|
+
own: "your token",
|
|
233
|
+
hand: "by hand",
|
|
234
|
+
};
|
|
235
|
+
/**
|
|
236
|
+
* O `--verbose`: cada valor contado, com a classe em que apareceu. É o que deixa conferir o número contra uma contagem
|
|
237
|
+
* feita por fora - o `gap-px` que uma regex de dígitos não pega aparece aqui com o nome.
|
|
238
|
+
*/
|
|
239
|
+
export function describeDistinctVerbose(v, systemName) {
|
|
240
|
+
const lines = [];
|
|
241
|
+
for (const k of ["colour", "textSize", "spacing"]) {
|
|
242
|
+
if (v[k].total === 0)
|
|
243
|
+
continue;
|
|
244
|
+
lines.push(`${NOUN[k][1]} (${v[k].total})`);
|
|
245
|
+
for (const { key, origin, example } of v[k].values) {
|
|
246
|
+
const label = origin === "system" && systemName ? systemName : ORIGIN_LABEL[origin];
|
|
247
|
+
lines.push(` ${label.padEnd(12)} ${key.padEnd(28)} ${example}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return lines;
|
|
251
|
+
}
|
package/dist/doctor/scan.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { propertyAt, roleFits } from "./their-names.js";
|
|
13
13
|
import { utilitiesOn } from "./idiom-names.js";
|
|
14
|
+
import { classWordsOf } from "./distinct-values.js";
|
|
14
15
|
import { normalizeValue, tokenMatch } from "./tokens.js";
|
|
15
16
|
import { THEME_SOURCE, tsTokenObjects } from "./ts-tokens-lens.js";
|
|
16
17
|
/**
|
|
@@ -489,7 +490,9 @@ export function scanSource(file, source, table) {
|
|
|
489
490
|
: null),
|
|
490
491
|
};
|
|
491
492
|
}
|
|
492
|
-
|
|
493
|
+
const report = stretchedStack(file, source, themeDefinitionsAside(file, source, scanCore(file, source, table)));
|
|
494
|
+
const classWords = /\.(tsx|jsx|html|vue|svelte|astro)$/i.test(file) ? classWordsOf(file, source) : [];
|
|
495
|
+
return classWords.length > 0 ? { ...report, classWords } : report;
|
|
493
496
|
}
|
|
494
497
|
/**
|
|
495
498
|
* A GRADE QUE DIVIDE A ALTURA DA TELA - bateria ampliada, item 17 (29/09).
|
package/package.json
CHANGED