synthesisui 0.16.385 → 0.16.387
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/component.js +134 -26
- package/dist/commands/generate.js +5 -2
- package/dist/commands/refit.js +4 -1
- package/dist/commands/upgrade.js +10 -2
- package/dist/component-codegen.js +39 -2
- package/dist/install-marks.js +1 -1
- package/dist/their-tongue.js +27 -2
- package/package.json +1 -1
|
@@ -9,7 +9,7 @@ import { body, section, snippet } from "../output.js";
|
|
|
9
9
|
import { findCollision, reactMajorOf, readInstalledConvention, readInstalledScheme, } from "../project-facts.js";
|
|
10
10
|
import { fetchComponent, RegistryError } from "../registry.js";
|
|
11
11
|
import { flavourResolver } from "../styles-flavour.js";
|
|
12
|
-
import { inTheirTongue, projectTongue } from "../their-tongue.js";
|
|
12
|
+
import { inTheirTongue, projectTongue, sumSpoken, } from "../their-tongue.js";
|
|
13
13
|
import { readCensus, unreadComment, unreadForComponent, } from "../unread-for-component.js";
|
|
14
14
|
import { recordWritten } from "../written.js";
|
|
15
15
|
/**
|
|
@@ -100,27 +100,26 @@ export async function component(slug, name, opts) {
|
|
|
100
100
|
* continua sendo o caminho - o comando DIZ qual dos dois aconteceu.
|
|
101
101
|
*/
|
|
102
102
|
const tongue = await projectTongue(root, slug);
|
|
103
|
-
const spoken = tongue ? inTheirTongue(res.css, tongue) : null;
|
|
104
|
-
const css = spoken ? spoken.css : res.css;
|
|
105
103
|
/**
|
|
106
|
-
*
|
|
104
|
+
* O RELATÓRIO SÓ EXISTE DEPOIS DE OS BYTES EXISTIREM - e é essa ordem que corrige o defeito.
|
|
107
105
|
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
106
|
+
* Ele era calculado aqui, sobre `res.css`, e impresso ANTES da materialização. Num projeto
|
|
107
|
+
* tailwind o CSS não vai a disco: a frase falava de um arquivo inexistente enquanto o `.tsx`
|
|
108
|
+
* escrito apontava 45 vezes para a nossa folha (medido no `button` do codelevel, 07/09). Agora
|
|
109
|
+
* cada caminho de escrita preenche esta variável com a tradução DAQUELES arquivos, e a mensagem
|
|
110
|
+
* sai no fim - depois da linha que nomeia o que foi escrito.
|
|
111
111
|
*/
|
|
112
|
-
|
|
113
|
-
console.log(` ${spoken.named} reference${spoken.named === 1 ? "" : "s"} now speak${spoken.named === 1 ? "s" : ""} the name YOUR code gives the value${spoken.inlined > 0 ? `, and ${spoken.inlined} carr${spoken.inlined === 1 ? "ies" : "y"} the value because your code names no token for it` : ""}.`);
|
|
114
|
-
console.log(spoken.left.length === 0
|
|
115
|
-
? ` Nothing in this file points at our stylesheet - it renders on your own tokens alone.`
|
|
116
|
-
: ` ${spoken.left.length} still point${spoken.left.length === 1 ? "s" : ""} at our stylesheet (${spoken.left.slice(0, 3).join(", ")}${spoken.left.length > 3 ? ", …" : ""}), so tokens.css is still needed here.`);
|
|
117
|
-
}
|
|
112
|
+
let spoken = null;
|
|
118
113
|
const artifactsAreTheProduct = opts.artifactsOnly === true || config.target !== "next";
|
|
119
114
|
if (artifactsAreTheProduct) {
|
|
120
115
|
const dir = join(root, "_synthesisui", "ds", slug, "components");
|
|
121
116
|
await mkdir(dir, { recursive: true });
|
|
122
117
|
await writeFile(join(dir, `${res.name}.json`), `${JSON.stringify(res.recipe, null, 2)}\n`, "utf8");
|
|
123
|
-
|
|
118
|
+
/** A FOLHA TAMBÉM PASSA PELA PORTA - este caminho escreve sem o codegen. */
|
|
119
|
+
const sheet = tongue ? inTheirTongue(res.css, tongue) : null;
|
|
120
|
+
if (sheet)
|
|
121
|
+
spoken = sheet;
|
|
122
|
+
await writeFile(join(dir, `${res.name}.css`), `${sheet ? sheet.css : res.css}\n`, "utf8");
|
|
124
123
|
console.log(`✓ ${res.name} → _synthesisui/ds/${slug}/components/${res.name}.{json,css} (${slug} v${res.version})`);
|
|
125
124
|
}
|
|
126
125
|
// 2. YOUR component - a real, importable `export function <Pascal>()` in the
|
|
@@ -169,7 +168,29 @@ export async function component(slug, name, opts) {
|
|
|
169
168
|
*/
|
|
170
169
|
const theirs = res.recipe?.api;
|
|
171
170
|
const already = theirs?.file && existsSync(join(root, theirs.file)) ? theirs : null;
|
|
172
|
-
|
|
171
|
+
/**
|
|
172
|
+
* UM NOME PRÓPRIO JÁ É A RESPOSTA PARA A COLISÃO - achado do dono em 06/09, e é a SEGUNDA volta
|
|
173
|
+
* deste mesmo defeito.
|
|
174
|
+
*
|
|
175
|
+
* O QUE ELE FEZ: `component codelevel button --as=ButtonSample`, no repositório de onde o
|
|
176
|
+
* sistema foi importado. A recusa disparou, disse *"writing another one would leave you with
|
|
177
|
+
* two"* e ofereceu `--force` - com um `--as` na linha de comando, ignorado.
|
|
178
|
+
*
|
|
179
|
+
* OS DOIS ERROS DE UMA VEZ. O primeiro é a decisão: este guard responde "você já tem" sem
|
|
180
|
+
* nunca ler `opts.as`, embora `localName` - a função que resolve o nome - fique 70 linhas
|
|
181
|
+
* abaixo. O segundo é a frase: com um nome próprio NÃO ficariam dois `Button`; ficaria o dele
|
|
182
|
+
* e um `ButtonSample`. E a única saída oferecida, `--force`, escreve com o nome ORIGINAL - a
|
|
183
|
+
* única opção que de fato causaria a duplicação que a mensagem existe para evitar.
|
|
184
|
+
*
|
|
185
|
+
* A PRIMEIRA VOLTA está registrada em `component-as.spec.ts`: a recusa IMPRIMIA `--as` e a
|
|
186
|
+
* flag não existia em `ComponentOptions`. A flag nasceu; o guard não passou a consultá-la.
|
|
187
|
+
*
|
|
188
|
+
* E A POPULAÇÃO É O CLIENTE-ALVO INTEIRO. Medido na base viva em 06/09: no `codelevel`, **64
|
|
189
|
+
* dos 66** componentes (97%) apontam para um arquivo que existe no repositório dele - porque o
|
|
190
|
+
* sistema NASCEU dali. Nos 25 sistemas gerados aqui, **0 de ~45**. O comando funcionava para
|
|
191
|
+
* quem não é o cliente e recusava para quem é.
|
|
192
|
+
*/
|
|
193
|
+
if (already && !opts.force && !opts.as?.trim()) {
|
|
173
194
|
console.log(section("You already have this component"));
|
|
174
195
|
console.log(body(`\`${already.name}\` lives in ${already.file} - writing another one would leave you with two.`));
|
|
175
196
|
if (already.from) {
|
|
@@ -180,14 +201,44 @@ export async function component(slug, name, opts) {
|
|
|
180
201
|
console.log(body(`It is not re-exported from that folder's entry, so import it by path.`));
|
|
181
202
|
}
|
|
182
203
|
console.log("");
|
|
183
|
-
|
|
204
|
+
/**
|
|
205
|
+
* A SAÍDA QUE NÃO DUPLICA VEM PRIMEIRO, e ela é a que a pessoa quer em quase todo caso.
|
|
206
|
+
*
|
|
207
|
+
* `--force` continua existindo e continua last: ele escreve com o nome DELE, que é a única
|
|
208
|
+
* opção que realmente deixa dois. Oferecê-lo como única saída era mandar a pessoa fazer
|
|
209
|
+
* exatamente o que a frase acima diz para evitar.
|
|
210
|
+
*/
|
|
211
|
+
console.log(body(`To see what this recipe produces without touching your file, give it a name of its own:`));
|
|
212
|
+
console.log("");
|
|
213
|
+
console.log(snippet([
|
|
214
|
+
`npx synthesisui@latest component ${slug} ${res.name} --as ${res.name}-sample`,
|
|
215
|
+
]));
|
|
216
|
+
console.log("");
|
|
217
|
+
console.log(body("The recipe and the compiled CSS are on disk either way - only the .tsx was not written. Pass --force to overwrite yours instead."));
|
|
184
218
|
return;
|
|
185
219
|
}
|
|
186
|
-
|
|
220
|
+
/**
|
|
221
|
+
* A COLISÃO SE MEDE PELO NOME QUE VAI SER ESCRITO, e não pelo do blueprint.
|
|
222
|
+
*
|
|
223
|
+
* A metade que o guard acima não resolvia sozinha: passado o primeiro guard com `--as`, este
|
|
224
|
+
* procurava por `button`/`Button` - o nome do BLUEPRINT - e achava o componente dele de novo.
|
|
225
|
+
* Consertar um e deixar o outro deixaria o `--as` aceito e barrado uma linha depois, o que é
|
|
226
|
+
* pior que recusar de cara: a pessoa faz o que a tela mandou e recebe outra recusa.
|
|
227
|
+
*
|
|
228
|
+
* `local` já era calculado, 70 linhas abaixo, para decidir a PASTA. Ele sobe para cá porque é
|
|
229
|
+
* a mesma pergunta - qual nome este comando vai ocupar - e duas respostas para ela é o defeito.
|
|
230
|
+
*/
|
|
231
|
+
const local = localName(res.name, opts.as);
|
|
232
|
+
const localPascal = local
|
|
233
|
+
.split(/[^a-zA-Z0-9]+/)
|
|
234
|
+
.filter(Boolean)
|
|
235
|
+
.map((p) => p[0].toUpperCase() + p.slice(1))
|
|
236
|
+
.join("");
|
|
237
|
+
const clash = await findCollision(root, config.componentsDir, local, localPascal);
|
|
187
238
|
if (!opts.force && (clash.exported || (clash.file && !clash.ours))) {
|
|
188
239
|
console.log(section("This name is already taken in your project"));
|
|
189
240
|
if (clash.exported) {
|
|
190
|
-
console.log(body(`\`${
|
|
241
|
+
console.log(body(`\`${localPascal}\` is already exported from ${clash.exported}.`));
|
|
191
242
|
}
|
|
192
243
|
if (clash.file && !clash.ours) {
|
|
193
244
|
console.log(body(`${clash.file} exists and we did not write it.`));
|
|
@@ -211,7 +262,7 @@ export async function component(slug, name, opts) {
|
|
|
211
262
|
* componente. O CSS não muda de nome: a receita é a do blueprint, e as classes dela são
|
|
212
263
|
* `.ds-<blueprint>` - o componente novo veste o mesmo desenho, com o nome dele no arquivo.
|
|
213
264
|
*/
|
|
214
|
-
|
|
265
|
+
/** Ver o cálculo acima: uma resposta só para "qual nome este comando ocupa". */
|
|
215
266
|
const compDir = join(root, config.componentsDir, local);
|
|
216
267
|
await mkdir(compDir, { recursive: true });
|
|
217
268
|
let filenames;
|
|
@@ -220,9 +271,23 @@ export async function component(slug, name, opts) {
|
|
|
220
271
|
// it wears (.css) + the barrel. Ignores the css|tailwind flavor - the
|
|
221
272
|
// template drives itself off the .ds-* classes.
|
|
222
273
|
const tsx = interactiveTemplate(res.name);
|
|
274
|
+
/**
|
|
275
|
+
* O TEMPLATE CURADO TAMBÉM FALA A LÍNGUA DELE - e como `stylesheet`.
|
|
276
|
+
*
|
|
277
|
+
* Ele escreve `transition: opacity var(--ds-motion-durations-base)` num `style` inline de
|
|
278
|
+
* JSX, onde o espaço é válido: pedir o escape do Tailwind aqui trocaria um valor correto por
|
|
279
|
+
* um inválido. O caminho interativo escreve sem o codegen, então a porta é chamada aqui.
|
|
280
|
+
*/
|
|
281
|
+
const say = (text) => tongue ? inTheirTongue(text, tongue, "stylesheet") : null;
|
|
282
|
+
const saidTsx = say(tsx);
|
|
283
|
+
const saidCss = say(res.css);
|
|
284
|
+
spoken = sumSpoken([saidTsx, saidCss]);
|
|
223
285
|
const written = [
|
|
224
|
-
{ filename: `${local}.tsx`, content: tsx },
|
|
225
|
-
{
|
|
286
|
+
{ filename: `${local}.tsx`, content: saidTsx ? saidTsx.css : tsx },
|
|
287
|
+
{
|
|
288
|
+
filename: `${local}.css`,
|
|
289
|
+
content: `${saidCss ? saidCss.css : res.css}\n`,
|
|
290
|
+
},
|
|
226
291
|
{ filename: "index.ts", content: `export * from "./${local}";\n` },
|
|
227
292
|
];
|
|
228
293
|
for (const f of written)
|
|
@@ -232,7 +297,7 @@ export async function component(slug, name, opts) {
|
|
|
232
297
|
filenames = written.map((f) => f.filename);
|
|
233
298
|
}
|
|
234
299
|
else {
|
|
235
|
-
const files = generateComponentFiles(slug, res.name, res.recipe, css, res.version, flavour, await reactMajorOf(root),
|
|
300
|
+
const { files, spoken: said } = generateComponentFiles(slug, res.name, res.recipe, res.css, res.version, flavour, await reactMajorOf(root),
|
|
236
301
|
/**
|
|
237
302
|
* THE SPELLING, FROM THE VERSION WE JUST FETCHED.
|
|
238
303
|
*
|
|
@@ -251,7 +316,8 @@ export async function component(slug, name, opts) {
|
|
|
251
316
|
* O ARQUIVO e o EXPORT levam o nome dele; a CLASSE continua sendo a do blueprint. Um
|
|
252
317
|
* `<CardPreview>` vestindo `.ds-card` está estilizado certo e não faz sombra em nada dele.
|
|
253
318
|
*/
|
|
254
|
-
local, await readInstalledScheme(root, slug));
|
|
319
|
+
local, await readInstalledScheme(root, slug), tongue);
|
|
320
|
+
spoken = said;
|
|
255
321
|
/**
|
|
256
322
|
* AS DECLARAÇÕES DO ARQUIVO DELE QUE O INTERPRETADOR NÃO LEU - ver `unread-for-component.ts`.
|
|
257
323
|
*
|
|
@@ -285,6 +351,19 @@ export async function component(slug, name, opts) {
|
|
|
285
351
|
else if (opts.interactive && !hasInteractiveTemplate(res.name)) {
|
|
286
352
|
console.log(` note: no interactive template for "${res.name}" - materialized the standard shell.`);
|
|
287
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* O RELATÓRIO DA TRADUÇÃO - agora sobre os arquivos que ACABARAM de ser nomeados acima.
|
|
356
|
+
*
|
|
357
|
+
* Um ponto só, depois de todo caminho de escrita (artefato, interativo, codegen), porque a
|
|
358
|
+
* pergunta é uma: o que foi escrito ainda precisa da nossa folha? Enquanto isto era calculado
|
|
359
|
+
* antes da materialização, a resposta descrevia outro arquivo.
|
|
360
|
+
*/
|
|
361
|
+
if (spoken && (spoken.named > 0 || spoken.inlined > 0)) {
|
|
362
|
+
console.log(` ${spoken.named} reference${spoken.named === 1 ? "" : "s"} now speak${spoken.named === 1 ? "s" : ""} the name YOUR code gives the value${spoken.inlined > 0 ? `, and ${spoken.inlined} carr${spoken.inlined === 1 ? "ies" : "y"} the value because your code names no token for it` : ""}.`);
|
|
363
|
+
console.log(spoken.left.length === 0
|
|
364
|
+
? ` No variable in what was just written points at our stylesheet - they are all names YOUR code declares.`
|
|
365
|
+
: ` ${spoken.left.length} still point${spoken.left.length === 1 ? "s" : ""} at our stylesheet (${spoken.left.slice(0, 3).join(", ")}${spoken.left.length > 3 ? ", …" : ""}), so tokens.css carries ${spoken.left.length === 1 ? "it" : "those"}.`);
|
|
366
|
+
}
|
|
288
367
|
// ── DX: concrete paths + copy-pasteable snippets, with breathing room ──
|
|
289
368
|
const tailwind = flavour === "tailwind";
|
|
290
369
|
const imports = tailwind
|
|
@@ -295,6 +374,23 @@ export async function component(slug, name, opts) {
|
|
|
295
374
|
]
|
|
296
375
|
: [`@import "../_synthesisui/ds/${slug}/tokens.css";`];
|
|
297
376
|
console.log(section(`One-time setup (once per app, for "${slug}")`));
|
|
377
|
+
/**
|
|
378
|
+
* POR QUE O SETUP CONTINUA AQUI QUANDO NENHUMA VARIÁVEL É NOSSA - e dizer isto é o conserto.
|
|
379
|
+
*
|
|
380
|
+
* A tentação era suprimir o bloco quando `left` é vazio, e ele leria como a promessa "seu
|
|
381
|
+
* repositório dispensa a folha". Medido no codelevel (07/09): as duas coisas são independentes.
|
|
382
|
+
* As VARIÁVEIS podem ser todas dele e o arquivo continua vestindo utilitários que só o adaptador
|
|
383
|
+
* do sistema gera (`rounded-3`, `gap-2xs`) e movimento que o `theme.css` declara referenciando
|
|
384
|
+
* `--ds-*`. Suprimir entregaria um componente sem raio e sem gap.
|
|
385
|
+
*
|
|
386
|
+
* Então o bloco não muda de tamanho: ele muda de MOTIVO. A linha abaixo diz qual das duas coisas
|
|
387
|
+
* a importação ainda serve, e ela é derivada, nunca fixa.
|
|
388
|
+
*/
|
|
389
|
+
if (spoken && spoken.left.length === 0) {
|
|
390
|
+
console.log(body(`(the variables are already yours - what these imports still bring is the`));
|
|
391
|
+
console.log(body(` system's own utilities and its motion, which the app has to generate)`));
|
|
392
|
+
console.log("");
|
|
393
|
+
}
|
|
298
394
|
console.log(body(`1. Import the design system in your GLOBAL stylesheet, e.g. app/globals.css`));
|
|
299
395
|
console.log(body(` (the path is relative to that file - hence the leading ../):`));
|
|
300
396
|
console.log("");
|
|
@@ -307,7 +403,19 @@ export async function component(slug, name, opts) {
|
|
|
307
403
|
console.log(body(`(If you haven't installed the system yet, run: synthesisui add ${slug})`));
|
|
308
404
|
console.log(section("Use it"));
|
|
309
405
|
if (!opts.artifactsOnly && config.target === "next") {
|
|
310
|
-
|
|
406
|
+
/**
|
|
407
|
+
* O EXEMPLO APONTA PARA O QUE FOI ESCRITO - a terceira metade do defeito do `--as` (06/09).
|
|
408
|
+
*
|
|
409
|
+
* Este bloco derivava o nome e o caminho de `res.name`, o nome do BLUEPRINT. Com
|
|
410
|
+
* `--as button-sample`, o arquivo vai para `components/button-sample/` e o exemplo mandava
|
|
411
|
+
* importar de `components/button` - um caminho que não existe. Um comando que escreve certo e
|
|
412
|
+
* instrui errado é pior que um que recusa: a pessoa copia a linha e o build quebra.
|
|
413
|
+
*
|
|
414
|
+
* `localName` é a mesma função dos dois guards acima. Uma resposta só para o nome, do primeiro
|
|
415
|
+
* guard até a última linha impressa.
|
|
416
|
+
*/
|
|
417
|
+
const usedName = localName(res.name, opts.as);
|
|
418
|
+
const pascalName = usedName
|
|
311
419
|
.split(/[^a-zA-Z0-9]+/)
|
|
312
420
|
.filter(Boolean)
|
|
313
421
|
.map((p) => p[0].toUpperCase() + p.slice(1))
|
|
@@ -318,7 +426,7 @@ export async function component(slug, name, opts) {
|
|
|
318
426
|
? ` ${firstAxis[0]}="${Object.keys(firstAxis[1])[0]}"`
|
|
319
427
|
: "";
|
|
320
428
|
console.log(snippet([
|
|
321
|
-
`import { ${pascalName} } from "@/${config.componentsDir}/${
|
|
429
|
+
`import { ${pascalName} } from "@/${config.componentsDir}/${usedName}";`,
|
|
322
430
|
"",
|
|
323
431
|
`<${pascalName}${exampleProp} />`,
|
|
324
432
|
]));
|
|
@@ -327,7 +435,7 @@ export async function component(slug, name, opts) {
|
|
|
327
435
|
console.log("");
|
|
328
436
|
console.log(body("Or ask your agent:"));
|
|
329
437
|
console.log(snippet([
|
|
330
|
-
`"Use the ${pascalName} component from ${config.componentsDir}/${
|
|
438
|
+
`"Use the ${pascalName} component from ${config.componentsDir}/${usedName} (SynthesisUI ${slug})."`,
|
|
331
439
|
]));
|
|
332
440
|
}
|
|
333
441
|
else {
|
|
@@ -6,6 +6,7 @@ import { installedSlugs } from "../installed.js";
|
|
|
6
6
|
import { reactMajorOf, readInstalledConvention, readInstalledScheme, } from "../project-facts.js";
|
|
7
7
|
import { postGenerate, RegistryError } from "../registry.js";
|
|
8
8
|
import { flavourResolver } from "../styles-flavour.js";
|
|
9
|
+
import { projectTongue } from "../their-tongue.js";
|
|
9
10
|
import { recordWritten } from "../written.js";
|
|
10
11
|
/** PascalCase para o hint de import (course-card → CourseCard). */
|
|
11
12
|
function pascalName(name) {
|
|
@@ -69,10 +70,12 @@ export async function generate(description, opts) {
|
|
|
69
70
|
await mkdir(compDir, { recursive: true });
|
|
70
71
|
// A LÍNGUA DE CADA COMPONENTE, resolvida uma vez - ver `flavourResolver`.
|
|
71
72
|
const flavourOf = await flavourResolver(root, config.styles);
|
|
72
|
-
const files = generateComponentFiles(slug, res.name, res.recipe, res.css, version, flavourOf(res.name), await reactMajorOf(root),
|
|
73
|
+
const { files } = generateComponentFiles(slug, res.name, res.recipe, res.css, version, flavourOf(res.name), await reactMajorOf(root),
|
|
73
74
|
// Read off the installed document: a generated component lands in the same
|
|
74
75
|
// project as the stylesheet it has to match.
|
|
75
|
-
await readInstalledConvention(root, slug), res.name, await readInstalledScheme(root, slug)
|
|
76
|
+
await readInstalledConvention(root, slug), res.name, await readInstalledScheme(root, slug),
|
|
77
|
+
/** O VOCABULÁRIO DELE - um componente gerado cai no mesmo projeto e fala a mesma língua. */
|
|
78
|
+
await projectTongue(root, slug));
|
|
76
79
|
for (const file of files) {
|
|
77
80
|
await writeFile(join(compDir, file.filename), file.code, "utf8");
|
|
78
81
|
}
|
package/dist/commands/refit.js
CHANGED
|
@@ -7,6 +7,7 @@ import { body, section, snippet } from "../output.js";
|
|
|
7
7
|
import { reactMajorOf, readInstalledConvention, readInstalledScheme, } from "../project-facts.js";
|
|
8
8
|
import { fetchComponent, postRefit, postSaveComponent, RegistryError, } from "../registry.js";
|
|
9
9
|
import { flavourResolver } from "../styles-flavour.js";
|
|
10
|
+
import { projectTongue } from "../their-tongue.js";
|
|
10
11
|
import { recordWritten } from "../written.js";
|
|
11
12
|
/** Slugs INSTALLED under `_synthesisui/ds/` (a `.lock` marks a real install -
|
|
12
13
|
* a folder holding only refit artifacts doesn't count). */
|
|
@@ -118,7 +119,9 @@ export async function refit(file, opts) {
|
|
|
118
119
|
await mkdir(compDir, { recursive: true });
|
|
119
120
|
// A LÍNGUA DE CADA COMPONENTE, resolvida uma vez - ver `flavourResolver`.
|
|
120
121
|
const flavourOf = await flavourResolver(root, config.styles);
|
|
121
|
-
const files = generateComponentFiles(slug, res.name, res.recipe, res.css, saved.version, flavourOf(res.name), await reactMajorOf(root), await readInstalledConvention(root, slug), res.name, await readInstalledScheme(root, slug)
|
|
122
|
+
const { files } = generateComponentFiles(slug, res.name, res.recipe, res.css, saved.version, flavourOf(res.name), await reactMajorOf(root), await readInstalledConvention(root, slug), res.name, await readInstalledScheme(root, slug),
|
|
123
|
+
/** O VOCABULÁRIO DELE - o `refit` reescreve o componente e não passava pela porta. */
|
|
124
|
+
await projectTongue(root, slug));
|
|
122
125
|
for (const f of files) {
|
|
123
126
|
await writeFile(join(compDir, f.filename), f.code, "utf8");
|
|
124
127
|
}
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -11,6 +11,7 @@ import { body, section, snippet } from "../output.js";
|
|
|
11
11
|
import { reactMajorOf, readInstalledConvention, readInstalledScheme, } from "../project-facts.js";
|
|
12
12
|
import { fetchChangelog, fetchComponent, fetchDesignSystem, RegistryError, } from "../registry.js";
|
|
13
13
|
import { flavourResolver } from "../styles-flavour.js";
|
|
14
|
+
import { projectTongue } from "../their-tongue.js";
|
|
14
15
|
import { readCensus, unreadComment, unreadForComponent, } from "../unread-for-component.js";
|
|
15
16
|
import { editedSinceWritten, keptLine, readWritten, recordWritten, } from "../written.js";
|
|
16
17
|
import { add } from "./add.js";
|
|
@@ -323,6 +324,13 @@ export async function upgrade(asked, opts) {
|
|
|
323
324
|
// componente: `upgrade` reescreve tudo que já está instalado, e reler o censo a cada
|
|
324
325
|
// arquivo seria a mesma resposta lida N vezes. Ver `flavourResolver`.
|
|
325
326
|
const flavourOf = await flavourResolver(root, config.styles);
|
|
327
|
+
/**
|
|
328
|
+
* O VOCABULÁRIO DELE, e este comando é o que mais precisava dele: `upgrade` REESCREVE
|
|
329
|
+
* componentes que a pessoa já tem, e ele não passava pela porta - então cada corrida DESFAZIA a
|
|
330
|
+
* tradução que o `component` tinha feito, devolvendo `var(--ds-*)` a arquivos que já falavam a
|
|
331
|
+
* língua do repositório. Resolvido uma vez para a corrida inteira, como o sabor.
|
|
332
|
+
*/
|
|
333
|
+
const tongue = await projectTongue(root, slug);
|
|
326
334
|
for (const entry of entries) {
|
|
327
335
|
const tsxPath = join(componentsRoot, entry, `${entry}.tsx`);
|
|
328
336
|
let head = "";
|
|
@@ -347,11 +355,11 @@ export async function upgrade(asked, opts) {
|
|
|
347
355
|
}
|
|
348
356
|
try {
|
|
349
357
|
const res = await fetchComponent(base, slug, entry);
|
|
350
|
-
const files = generateComponentFiles(slug, res.name, res.recipe, res.css, res.version, flavourOf(res.name),
|
|
358
|
+
const { files } = generateComponentFiles(slug, res.name, res.recipe, res.css, res.version, flavourOf(res.name),
|
|
351
359
|
// Both were missing here, and `upgrade` is the command that REWRITES
|
|
352
360
|
// components somebody already has: without the convention it would have
|
|
353
361
|
// taken a working component and stripped its styles.
|
|
354
|
-
await reactMajorOf(root), res.classNames ?? (await readInstalledConvention(root, slug)), res.name, await readInstalledScheme(root, slug));
|
|
362
|
+
await reactMajorOf(root), res.classNames ?? (await readInstalledConvention(root, slug)), res.name, await readInstalledScheme(root, slug), tongue);
|
|
355
363
|
/**
|
|
356
364
|
* A NOTA DAS DECLARAÇÕES NÃO INTERPRETADAS SOBREVIVE AO UPGRADE.
|
|
357
365
|
*
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { emittableAttrs } from "./attr-shape.js";
|
|
2
|
+
import { inTheirTongue, sumSpoken, } from "./their-tongue.js";
|
|
2
3
|
import { PHRASING_FORMS, } from "./types.js";
|
|
3
4
|
export const DEFAULT_CONVENTION = {
|
|
4
5
|
prefix: "ds-",
|
|
@@ -1384,7 +1385,20 @@ localName = name,
|
|
|
1384
1385
|
* without ever deciding, and an optional argument that can be forgotten is the
|
|
1385
1386
|
* shape this file has already been bitten by. `tsc` refuses the half-call instead.
|
|
1386
1387
|
*/
|
|
1387
|
-
scheme
|
|
1388
|
+
scheme,
|
|
1389
|
+
/**
|
|
1390
|
+
* O VOCABULÁRIO DESTE REPOSITÓRIO - `projectTongue(root, slug)`, ou `null` quando não há mapa.
|
|
1391
|
+
*
|
|
1392
|
+
* OBRIGATÓRIO, e é a única forma de fechar a porta (INV-VOLTA-02). A tradução morava em cada
|
|
1393
|
+
* comando: o `component` traduzia o CSS por fora e o `upgrade`, o `refit` e o `generate` não
|
|
1394
|
+
* traduziam nada - três dos cinco materializadores reescreviam o repositório dele com
|
|
1395
|
+
* `var(--ds-*)` cru, e o `upgrade` DESFAZIA a tradução que o `component` tinha feito. O gate lia
|
|
1396
|
+
* o texto do módulo e via a porta importada, então passava verde.
|
|
1397
|
+
*
|
|
1398
|
+
* Aqui a tradução acontece uma vez, sobre os bytes que vão a disco, e `tsc` recusa a meia-chamada
|
|
1399
|
+
* de um materializador novo - o mesmo argumento do `scheme` logo acima.
|
|
1400
|
+
*/
|
|
1401
|
+
tongue) {
|
|
1388
1402
|
const files = [];
|
|
1389
1403
|
const props = propsTypeName(reactMajor);
|
|
1390
1404
|
if (styles === "css") {
|
|
@@ -1404,5 +1418,28 @@ scheme) {
|
|
|
1404
1418
|
filename: "index.ts",
|
|
1405
1419
|
code: `export * from "./${localName}";\n`,
|
|
1406
1420
|
});
|
|
1407
|
-
return files;
|
|
1421
|
+
return speak(files, tongue, styles === "tailwind" ? "tailwind-class" : "stylesheet");
|
|
1422
|
+
}
|
|
1423
|
+
/**
|
|
1424
|
+
* A TRADUÇÃO, SOBRE OS BYTES, UMA VEZ - e o destino é de quem GEROU o texto, não da extensão.
|
|
1425
|
+
*
|
|
1426
|
+
* No modo tailwind toda referência nossa está DENTRO de um valor arbitrário (`arbitrary()` é o
|
|
1427
|
+
* único ponto do módulo que escreve `var(--ds-…)`, e ele sempre a envolve em `[prop:…]`), então um
|
|
1428
|
+
* gradiente inlined precisa de `_` no lugar do espaço ou a classe não é gerada. Numa folha, e num
|
|
1429
|
+
* `style` inline de JSX, o espaço é válido e `_` seria o defeito - por isso `interactive-templates`
|
|
1430
|
+
* (que escreve `transition: opacity var(--ds-…)`) chama a porta como `stylesheet`.
|
|
1431
|
+
*
|
|
1432
|
+
* `index.ts` é um re-export e não carrega estilo, mas passa pela mesma porta: um arquivo isento por
|
|
1433
|
+
* lista é a lista que alguém esquece de atualizar.
|
|
1434
|
+
*/
|
|
1435
|
+
function speak(files, tongue, tsxDestination) {
|
|
1436
|
+
if (!tongue)
|
|
1437
|
+
return { files, spoken: null };
|
|
1438
|
+
const reports = [];
|
|
1439
|
+
const said = files.map((file) => {
|
|
1440
|
+
const spoken = inTheirTongue(file.code, tongue, file.filename.endsWith(".css") ? "stylesheet" : tsxDestination);
|
|
1441
|
+
reports.push(spoken);
|
|
1442
|
+
return { filename: file.filename, code: spoken.css };
|
|
1443
|
+
});
|
|
1444
|
+
return { files: said, spoken: sumSpoken(reports) };
|
|
1408
1445
|
}
|
package/dist/install-marks.js
CHANGED
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
* O que o cliente ganha ao rodar `upgrade`: o agente dele no Codex passa a poder PERGUNTAR ao
|
|
171
171
|
* sistema, em vez de só receber as regras e adivinhar o resto.
|
|
172
172
|
*/
|
|
173
|
-
export const MATERIALISER_SINCE = "0.16.
|
|
173
|
+
export const MATERIALISER_SINCE = "0.16.387";
|
|
174
174
|
/**
|
|
175
175
|
* A ÚLTIMA VERSÃO EM QUE O QUE O HOOK RODA MUDOU.
|
|
176
176
|
*
|
package/dist/their-tongue.js
CHANGED
|
@@ -1,6 +1,29 @@
|
|
|
1
1
|
/** `var(--ds-x)` e `var(--ds-x, fallback)` - o que a folha compilada escreve. */
|
|
2
2
|
const OUR_VAR = /var\(\s*(--ds-[a-zA-Z0-9_-]+)\s*(?:,([^()]*))?\)/g;
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* VÁRIOS ARQUIVOS, UM NÚMERO - porque a promessa é sobre a MATERIALIZAÇÃO, não sobre um arquivo.
|
|
5
|
+
*
|
|
6
|
+
* Um componente sai como `.tsx` + `.css` + barrel, e dizer "nada aponta para a nossa folha" só é
|
|
7
|
+
* verdade quando nenhum dos três aponta. Somar aqui, e não em cada comando, é o que impede duas
|
|
8
|
+
* contas diferentes para a mesma pergunta.
|
|
9
|
+
*
|
|
10
|
+
* `null` quando nada foi traduzido (projeto sem mapa) - e aí quem chama DIZ isso.
|
|
11
|
+
*/
|
|
12
|
+
export function sumSpoken(reports) {
|
|
13
|
+
const real = reports.filter((r) => r !== null);
|
|
14
|
+
if (real.length === 0)
|
|
15
|
+
return null;
|
|
16
|
+
const left = new Set();
|
|
17
|
+
for (const r of real)
|
|
18
|
+
for (const name of r.left)
|
|
19
|
+
left.add(name);
|
|
20
|
+
return {
|
|
21
|
+
named: real.reduce((n, r) => n + r.named, 0),
|
|
22
|
+
inlined: real.reduce((n, r) => n + r.inlined, 0),
|
|
23
|
+
left: [...left].sort(),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function inTheirTongue(css, tongue, destination = "stylesheet") {
|
|
4
27
|
let named = 0;
|
|
5
28
|
let inlined = 0;
|
|
6
29
|
const left = new Set();
|
|
@@ -18,7 +41,9 @@ export function inTheirTongue(css, tongue) {
|
|
|
18
41
|
const value = tongue.values.get(name);
|
|
19
42
|
if (value) {
|
|
20
43
|
inlined += 1;
|
|
21
|
-
return
|
|
44
|
+
return destination === "tailwind-class"
|
|
45
|
+
? value.replace(/\s+/g, "_")
|
|
46
|
+
: value;
|
|
22
47
|
}
|
|
23
48
|
left.add(name);
|
|
24
49
|
return fallback ? whole : whole;
|
package/package.json
CHANGED