synthesisui 0.16.416 → 0.16.418
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/anatomy-from-sketch.js +69 -5
- package/dist/commands/add.js +10 -1
- package/dist/commands/doctor.js +13 -2
- package/dist/commands/mcp.js +9 -2
- package/dist/doctrine.js +96 -0
- package/dist/install-marks.js +1 -1
- package/dist/rule-filter.js +12 -19
- package/package.json +1 -1
|
@@ -212,6 +212,23 @@ function nameOfTag(tag) {
|
|
|
212
212
|
.replace(/[^a-z0-9]+/g, "-")
|
|
213
213
|
.replace(/^-|-$/g, "");
|
|
214
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* A PROP QUE ALIMENTA O ELEMENTO, na mesma grafia dos outros nomes de part.
|
|
217
|
+
*
|
|
218
|
+
* `contentFrom` é a expressão verbatim que o JSX escreve, então ela pode ser um identificador
|
|
219
|
+
* simples (`label`), um acesso (`item.title`) ou uma chamada. Só o que é um nome simples vira
|
|
220
|
+
* nome de part: um pedaço de expressão viraria uma classe ilegível no CSS compilado, e o nó cai
|
|
221
|
+
* em `box` como antes. Medido nos dois censos: os 19 do `codelevel-ui` são todos identificadores
|
|
222
|
+
* simples, e dos 534 do `frontend-hub` a maioria também.
|
|
223
|
+
*/
|
|
224
|
+
function nameOfProp(expression) {
|
|
225
|
+
const clean = expression.trim();
|
|
226
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(clean))
|
|
227
|
+
return undefined;
|
|
228
|
+
const name = nameOfTag(clean);
|
|
229
|
+
/** `children` é a palavra do React, não do design dele - ali o nó é um slot, não uma part. */
|
|
230
|
+
return name && name !== "children" ? name : undefined;
|
|
231
|
+
}
|
|
215
232
|
/**
|
|
216
233
|
* O NOME QUE ELE MESMO DEU AO ELEMENTO - a classe do CSS Module.
|
|
217
234
|
*
|
|
@@ -535,6 +552,31 @@ consumedAt) {
|
|
|
535
552
|
const hasKids = children.length > 0;
|
|
536
553
|
let as;
|
|
537
554
|
let name;
|
|
555
|
+
/**
|
|
556
|
+
* O CONTEÚDO QUE VEM DE UMA PROP DELE É CONTEÚDO - A1 da etapa 15 (dono, 11/09).
|
|
557
|
+
*
|
|
558
|
+
* O QUE ELE VIA: o `CapabilityCard` dele desenhado como três losangos `✧` soltos, e o `Beam`
|
|
559
|
+
* como um `✧`. A pergunta dele foi a certa: *"mesmo tendo a receita vindo de um componente
|
|
560
|
+
* funcionando, não consigo entender o motivo"*.
|
|
561
|
+
*
|
|
562
|
+
* A SUPOSIÇÃO QUE ERRAVA estava escrita três linhas abaixo, em voz alta: *"a leaf box with no
|
|
563
|
+
* content of its own is a drawn SHAPE (...) `icon` is the form for 'a bare shape with no
|
|
564
|
+
* text'"*. Em React o texto quase nunca é literal no JSX - ele vem de `{label}`, `{children}`,
|
|
565
|
+
* `{value}`. **"Sem texto literal" não significa "sem conteúdo": significa "o conteúdo vem de
|
|
566
|
+
* fora".**
|
|
567
|
+
*
|
|
568
|
+
* E O COLETOR JÁ SABIA. `contentFrom` é a expressão que aquele elemento escreve, e o contrato
|
|
569
|
+
* a descreve como *"a evidência de QUAL prop alimenta este elemento"*. A derivação nunca a
|
|
570
|
+
* consultava. MEDIDO nos dois censos congelados: **19 nós** no `codelevel-ui` e **534** no
|
|
571
|
+
* `frontend-hub` - e no sistema dele isso era 36 dos 94 nós chegando como `icon`, 20 deles
|
|
572
|
+
* sem ser glifo nenhum (`label`, `value`, `title`, `eyebrow`, `meta`).
|
|
573
|
+
*
|
|
574
|
+
* É EVIDÊNCIA, NUNCA EXECUÇÃO: em outro projeto a prop tem outro nome, e o que atravessa é
|
|
575
|
+
* que ESTE elemento é onde o conteúdo entra. Por isso a regra lê a PRESENÇA do campo, e a
|
|
576
|
+
* palavra dele só vira o nome da part.
|
|
577
|
+
*/
|
|
578
|
+
const writesContent = typeof node.contentFrom === "string" &&
|
|
579
|
+
node.contentFrom.trim().length > 0;
|
|
538
580
|
if (capitalised) {
|
|
539
581
|
// A primitive's member name is the client's own word for the element.
|
|
540
582
|
name = nameOfTag(tag);
|
|
@@ -551,10 +593,25 @@ consumedAt) {
|
|
|
551
593
|
else if (form)
|
|
552
594
|
as = form;
|
|
553
595
|
else
|
|
554
|
-
as = hasKids
|
|
596
|
+
as = hasKids
|
|
597
|
+
? arrangement(classes)
|
|
598
|
+
: node.text || writesContent
|
|
599
|
+
? "text"
|
|
600
|
+
: "icon";
|
|
555
601
|
}
|
|
556
602
|
else if (byTag) {
|
|
557
|
-
|
|
603
|
+
/**
|
|
604
|
+
* A PALAVRA DELE VENCE O NOME QUE A TAG SUGERE (A5 da etapa 15).
|
|
605
|
+
*
|
|
606
|
+
* `BY_TAG` dá a um `<span>` o nome `text` e a um `<div>` nada - nomes corretos e MUDOS,
|
|
607
|
+
* que dizem o que a tag é e nada sobre o papel daquele nó no componente dele. Quando o
|
|
608
|
+
* JSX escreve `<span>{eyebrow}</span>`, `eyebrow` é a palavra que ele usa para aquilo, e
|
|
609
|
+
* é ela que precisa chegar na tela e no seletor. Perde só para um nome que também é dele
|
|
610
|
+
* e mais específico: uma classe de module, ou o membro de um componente que ele escreveu.
|
|
611
|
+
*/
|
|
612
|
+
name =
|
|
613
|
+
(writesContent ? nameOfProp(String(node.contentFrom)) : undefined) ??
|
|
614
|
+
byTag.name;
|
|
558
615
|
if (NEUTRAL.has(lower)) {
|
|
559
616
|
/**
|
|
560
617
|
* A leaf box with no content of its own is a drawn SHAPE - a dot, a
|
|
@@ -564,7 +621,7 @@ consumedAt) {
|
|
|
564
621
|
as =
|
|
565
622
|
hasKids || node.slot
|
|
566
623
|
? arrangement(classes)
|
|
567
|
-
: node.text
|
|
624
|
+
: node.text || writesContent
|
|
568
625
|
? "text"
|
|
569
626
|
: "icon";
|
|
570
627
|
}
|
|
@@ -572,7 +629,7 @@ consumedAt) {
|
|
|
572
629
|
as = byTag.as;
|
|
573
630
|
}
|
|
574
631
|
else {
|
|
575
|
-
as = hasKids ? arrangement(classes) : "icon";
|
|
632
|
+
as = hasKids ? arrangement(classes) : writesContent ? "text" : "icon";
|
|
576
633
|
}
|
|
577
634
|
/**
|
|
578
635
|
* ANYTHING HOLDING ELEMENTS ARRANGES THEM - the contract gives children to
|
|
@@ -598,9 +655,16 @@ consumedAt) {
|
|
|
598
655
|
/**
|
|
599
656
|
* `box` VIROU A ÚLTIMA OPÇÃO, não a primeira - ver `nameOfModule`. Um nó com
|
|
600
657
|
* classe de module tem nome, e o nome é dele.
|
|
658
|
+
*
|
|
659
|
+
* E A PROP QUE ALIMENTA O ELEMENTO VEM ANTES DE `box` (A5 da etapa 15). Os dois nós de
|
|
660
|
+
* texto do `CapabilityCard` dele chegavam como `box` e `box` - dois irmãos com o mesmo
|
|
661
|
+
* nome, que é o mesmo que não ter nome. `contentFrom: "label"` é a palavra que o CÓDIGO
|
|
662
|
+
* DELE escreve, e ela é melhor que qualquer coisa que a gente derivasse da tag.
|
|
601
663
|
*/
|
|
602
664
|
name =
|
|
603
|
-
nameOfModule(node.moduleClass) ??
|
|
665
|
+
nameOfModule(node.moduleClass) ??
|
|
666
|
+
(writesContent ? nameOfProp(String(node.contentFrom)) : undefined) ??
|
|
667
|
+
"box";
|
|
604
668
|
/**
|
|
605
669
|
* A NODE WHOSE CONTENT IS THE CALLER'S is a slot, and it keeps its own name
|
|
606
670
|
* and classes - the border and the hover on a `<tr>{children}</tr>` are real
|
package/dist/commands/add.js
CHANGED
|
@@ -3,6 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
import { agentsToMaintain } from "../agents-chosen.js";
|
|
4
4
|
import { syncClaudeMd } from "../claude-md.js";
|
|
5
5
|
import { readProjectConfig, readToken, resolveRegistry } from "../config.js";
|
|
6
|
+
import { parseRules } from "../doctrine.js";
|
|
6
7
|
import { customFontFamilies, googleFontsHref, nextFontSnippet, } from "../fonts.js";
|
|
7
8
|
import { detectAppDirs, globalSheetOf, prefixFrom } from "../global-sheet.js";
|
|
8
9
|
import { lockReference } from "../group-role.js";
|
|
@@ -375,9 +376,17 @@ export async function add(slug, opts) {
|
|
|
375
376
|
* registry working unchanged.
|
|
376
377
|
*/
|
|
377
378
|
const stack = await detectStack(projectRoot);
|
|
379
|
+
/**
|
|
380
|
+
* AS DUAS ENTRADAS VIRAM A MESMA FORMA ANTES DE TOCAR O DISCO.
|
|
381
|
+
*
|
|
382
|
+
* `ruleSet` vem estruturado e `rules` vem como frase - e enquanto a segunda caía direto no
|
|
383
|
+
* arquivo, o disco dele podia receber uma lista mista sem nada reclamar. `parseRules` normaliza
|
|
384
|
+
* as duas para `{ text, applies }`, e desfaz o prefixo `ds-a + ds-b:` da forma antiga no
|
|
385
|
+
* caminho, então um registry que ainda manda frase não perde o alcance da regra.
|
|
386
|
+
*/
|
|
378
387
|
const rules = payload.ruleSet && payload.ruleSet.length > 0
|
|
379
388
|
? rulesForProject(payload.ruleSet, stack)
|
|
380
|
-
: (payload.rules ?? []);
|
|
389
|
+
: parseRules(payload.rules ?? []);
|
|
381
390
|
const leftOut = payload.ruleSet
|
|
382
391
|
? describeFiltered(payload.ruleSet, stack)
|
|
383
392
|
: null;
|
package/dist/commands/doctor.js
CHANGED
|
@@ -16,6 +16,7 @@ import { diagnose, nameToWrite, scanSource, siblingTokens, } from "../doctor/sca
|
|
|
16
16
|
import { findSelfConflicts, forbiddenProps, isReset, propMatchesLabel, } from "../doctor/self-conflict.js";
|
|
17
17
|
import { withTheirNames } from "../doctor/their-names.js";
|
|
18
18
|
import { buildTable, EMPTY_TABLE, nearestToken, } from "../doctor/tokens.js";
|
|
19
|
+
import { parseRules } from "../doctrine.js";
|
|
19
20
|
/**
|
|
20
21
|
* `detectAppDirs` SAIU DESTE IMPORT em 09/09: quem pergunta "quais pastas são app?" agora é
|
|
21
22
|
* `readWiringPerApp`, que responde a fiação de cada uma. O comando não escolhe mais um app.
|
|
@@ -320,13 +321,23 @@ measured) {
|
|
|
320
321
|
if (doctrineRaw) {
|
|
321
322
|
try {
|
|
322
323
|
const parsed = JSON.parse(doctrineRaw);
|
|
323
|
-
|
|
324
|
+
/**
|
|
325
|
+
* AS DUAS FORMAS DO DISCO, lidas pela mesma porta (`parseRules`).
|
|
326
|
+
*
|
|
327
|
+
* Este filtro era `typeof r === "string"`, e com a regra passando a viajar como dado ele
|
|
328
|
+
* DESCARTARIA calado toda regra de uma instalação nova - o pior resultado possível para
|
|
329
|
+
* uma doutrina, que é sumir sem ninguém saber. E a leitura pela porta única também
|
|
330
|
+
* devolve o alcance às instalações ANTIGAS: o prefixo `ds-a + ds-b:` é desfeito ali, sem
|
|
331
|
+
* reimportar nada.
|
|
332
|
+
*/
|
|
333
|
+
const parsedRules = parseRules(parsed.rules);
|
|
334
|
+
if (parsedRules.length > 0)
|
|
324
335
|
doctrines.push({
|
|
325
336
|
...(typeof parsed.version === "number"
|
|
326
337
|
? { version: parsed.version }
|
|
327
338
|
: {}),
|
|
328
339
|
...(typeof parsed.name === "string" ? { name: parsed.name } : {}),
|
|
329
|
-
rules:
|
|
340
|
+
rules: parsedRules,
|
|
330
341
|
});
|
|
331
342
|
}
|
|
332
343
|
catch {
|
package/dist/commands/mcp.js
CHANGED
|
@@ -12,6 +12,7 @@ import { readEvents } from "../doctor/ledger.js";
|
|
|
12
12
|
import { fileRequest } from "../doctor/requests.js";
|
|
13
13
|
import { diagnose, nameToWrite, scanSource } from "../doctor/scan.js";
|
|
14
14
|
import { nearestToken, normalizeValue, tokenFor } from "../doctor/tokens.js";
|
|
15
|
+
import { ruleLine } from "../doctrine.js";
|
|
15
16
|
import { fromCensus } from "../memory/observation.js";
|
|
16
17
|
import { handleRecall, handleRemember, MEMORY_TOOLS } from "../memory/tools.js";
|
|
17
18
|
import { readInstalledConvention } from "../project-facts.js";
|
|
@@ -523,7 +524,13 @@ async function systemDoctrine(root) {
|
|
|
523
524
|
rules.length > 0
|
|
524
525
|
? `# Rules - maximum authority, ${rules.length} of them${pinned ? `, as of v${pinned}` : ""}`
|
|
525
526
|
: "",
|
|
526
|
-
|
|
527
|
+
/**
|
|
528
|
+
* `ruleLine` É O QUE MANTÉM ESTA SAÍDA IDÊNTICA. A regra passou a viajar como dado
|
|
529
|
+
* (`{ text, applies }`), e interpolar o objeto aqui imprimiria `[object Object]` sem o `tsc`
|
|
530
|
+
* reclamar - um template literal aceita qualquer coisa. O alcance volta a ser prefixo na hora
|
|
531
|
+
* de MOSTRAR, que é onde ele sempre esteve para quem lê.
|
|
532
|
+
*/
|
|
533
|
+
...rules.map((r) => `- ${ruleLine(r)}`),
|
|
527
534
|
].filter(Boolean);
|
|
528
535
|
const pulse = await sessionPulse(root, pinned);
|
|
529
536
|
return [
|
|
@@ -1706,7 +1713,7 @@ cli) {
|
|
|
1706
1713
|
const plan = composePlan({
|
|
1707
1714
|
documents,
|
|
1708
1715
|
table,
|
|
1709
|
-
rules: doctrines.flatMap((d) => d.rules),
|
|
1716
|
+
rules: doctrines.flatMap((d) => d.rules).map(ruleLine),
|
|
1710
1717
|
/**
|
|
1711
1718
|
* A regra tem a FRASE e o PACOTE em campos separados (`text`,
|
|
1712
1719
|
* `requires`) - mandar a frase sozinha esconderia o que instalar, e
|
package/dist/doctrine.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A REGRA DO SISTEMA DELE, COMO DADO - e não como frase.
|
|
3
|
+
*
|
|
4
|
+
* ═══ O QUE ABRIU ESTE ARQUIVO, medido em 12/09 ═══
|
|
5
|
+
*
|
|
6
|
+
* O agente dele apagou a serif itálica de seis lugares da landing do `codelevel`, e nada avisou.
|
|
7
|
+
* A regra existia, instalada, em disco, no `doctrine.json` daquele projeto - a décima de vinte e
|
|
8
|
+
* cinco, verbatim:
|
|
9
|
+
*
|
|
10
|
+
* "A display headline breaks onto a line set in the serif, italic - that line carries the
|
|
11
|
+
* emphasis of the sentence, never the whole headline."
|
|
12
|
+
*
|
|
13
|
+
* O conhecimento estava a dois diretórios de distância da edição e ninguém o entregou. Palavras
|
|
14
|
+
* dele: *"o sistema fala antes do dano"*.
|
|
15
|
+
*
|
|
16
|
+
* ═══ A CAUSA, e ela é de uma linha ═══
|
|
17
|
+
*
|
|
18
|
+
* Do NOSSO lado a regra é estruturada: `applies` diz quais componentes ela governa, e
|
|
19
|
+
* `rules-for-component.ts` casa por esse campo há semanas. No caminho até o disco dele,
|
|
20
|
+
* `rulesForProject` achatava a estrutura num PREFIXO DE PROSA - `ds-button + ds-icon-button: <o
|
|
21
|
+
* texto>` - e a partir dali nada mais podia perguntar *"esta regra vale para o arquivo que está
|
|
22
|
+
* sendo escrito?"*. A resposta existia e era jogada fora na exportação.
|
|
23
|
+
*
|
|
24
|
+
* ═══ POR QUE UM MÓDULO, e não um `JSON.parse` em cada leitor ═══
|
|
25
|
+
*
|
|
26
|
+
* O `doctrine.json` é lido em três lugares - o doctor, o `system_doctrine` do MCP e o compositor
|
|
27
|
+
* de plano -, e a partir de agora existem DUAS formas no disco do mundo: a nova, estruturada, e a
|
|
28
|
+
* antiga, em string, que está instalada em todo projeto que rodou `connect` antes de hoje. Três
|
|
29
|
+
* leitores decidindo sozinhos o que fazer com duas formas são três chances de discordar sobre a
|
|
30
|
+
* mesma regra.
|
|
31
|
+
*
|
|
32
|
+
* ═══ E A FORMA ANTIGA NÃO PERDE O ALCANCE ═══
|
|
33
|
+
*
|
|
34
|
+
* O prefixo era um achatamento REVERSÍVEL: `ds-card + ds-noise-overlay: <texto>` carrega os dois
|
|
35
|
+
* nomes, só que em prosa. `parseRule` desfaz isso na leitura, então uma instalação antiga ganha o
|
|
36
|
+
* alcance de volta sem reimportar nada - que é a diferença entre uma correção que alcança quem já
|
|
37
|
+
* é cliente e uma que só alcança quem instalar amanhã.
|
|
38
|
+
*/
|
|
39
|
+
/**
|
|
40
|
+
* O PREFIXO QUE A FORMA ANTIGA USA: `ds-card + ds-noise-overlay: `.
|
|
41
|
+
*
|
|
42
|
+
* Ancorado no começo e exigindo o `ds-` em cada nome, porque uma frase pode conter dois pontos -
|
|
43
|
+
* *"Dark is a class, never the OS preference: @custom-variant dark"* é a segunda regra real do
|
|
44
|
+
* sistema dele, e sem a âncora ela viraria um componente chamado `Dark is a class, never the OS
|
|
45
|
+
* preference`.
|
|
46
|
+
*/
|
|
47
|
+
const LEGACY_SCOPE = /^((?:ds-[a-z0-9-]+)(?:\s*\+\s*ds-[a-z0-9-]+)*)\s*:\s*/;
|
|
48
|
+
/** Uma regra vinda do disco, em qualquer das duas formas - ou `null` quando não há frase. */
|
|
49
|
+
export function parseRule(raw) {
|
|
50
|
+
if (typeof raw === "string") {
|
|
51
|
+
const text = raw.trim();
|
|
52
|
+
if (!text)
|
|
53
|
+
return null;
|
|
54
|
+
const m = LEGACY_SCOPE.exec(text);
|
|
55
|
+
if (!m)
|
|
56
|
+
return { text, applies: [] };
|
|
57
|
+
return {
|
|
58
|
+
text: text.slice(m[0].length).trim(),
|
|
59
|
+
applies: m[1].split("+").map((n) => n.trim().replace(/^ds-/, "")),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (!raw || typeof raw !== "object")
|
|
63
|
+
return null;
|
|
64
|
+
const r = raw;
|
|
65
|
+
const text = typeof r.text === "string" ? r.text.trim() : "";
|
|
66
|
+
if (!text)
|
|
67
|
+
return null;
|
|
68
|
+
const applies = Array.isArray(r.applies)
|
|
69
|
+
? r.applies.filter((a) => typeof a === "string" && !!a.trim())
|
|
70
|
+
: [];
|
|
71
|
+
return { text, applies: applies.map((a) => a.trim().replace(/^ds-/, "")) };
|
|
72
|
+
}
|
|
73
|
+
/** Todas as regras de um `doctrine.json`, nas duas formas, sem as ilegíveis. */
|
|
74
|
+
export function parseRules(raw) {
|
|
75
|
+
if (!Array.isArray(raw))
|
|
76
|
+
return [];
|
|
77
|
+
const out = [];
|
|
78
|
+
for (const entry of raw) {
|
|
79
|
+
const rule = parseRule(entry);
|
|
80
|
+
if (rule)
|
|
81
|
+
out.push(rule);
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A REGRA COMO ALGUÉM A LÊ - a mesma frase que o disco carregava antes.
|
|
87
|
+
*
|
|
88
|
+
* O alcance volta a ser prefixo AQUI, na hora de mostrar, e não no dado. Quem imprime continua
|
|
89
|
+
* imprimindo o que sempre imprimiu; quem PERGUNTA passa a ter o campo.
|
|
90
|
+
*/
|
|
91
|
+
export function ruleLine(rule) {
|
|
92
|
+
const scope = rule.applies.length > 0
|
|
93
|
+
? `${rule.applies.map((a) => `ds-${a}`).join(" + ")}: `
|
|
94
|
+
: "";
|
|
95
|
+
return `${scope}${rule.text}`;
|
|
96
|
+
}
|
package/dist/install-marks.js
CHANGED
|
@@ -793,7 +793,7 @@ export const CHECKER_SINCE = "0.16.413";
|
|
|
793
793
|
* deveriam estar lá. Quem tem censo gravado precisa de um `sync` para a fila de trabalho dele ser a
|
|
794
794
|
* real; ver o livro-razão de `corpus.spec.ts`.
|
|
795
795
|
*/
|
|
796
|
-
export const READER_SINCE = "0.16.
|
|
796
|
+
export const READER_SINCE = "0.16.417";
|
|
797
797
|
/**
|
|
798
798
|
* O QUE ESTÁ INSTALADO AQUI FICOU PARA TRÁS - e as DUAS condições que fazem isso ser verdade.
|
|
799
799
|
*
|
package/dist/rule-filter.js
CHANGED
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WHICH RULES BELONG IN *THIS* PROJECT.
|
|
3
|
-
*
|
|
4
|
-
* A rule travels with the design system, and the system gets installed places it
|
|
5
|
-
* was never written for. "The state arrives through this hook" is true about Next
|
|
6
|
-
* and actively wrong about Vue - so a rule carries the environments it holds in,
|
|
7
|
-
* and the filtering happens HERE, because the consumer's stack is the one thing
|
|
8
|
-
* the platform cannot know (dono, 01/08).
|
|
9
|
-
*
|
|
10
|
-
* THE DEFAULT WHEN WE CANNOT TELL IS TO KEEP IT. A rule whose condition does not
|
|
11
|
-
* match is removed, because obeying it would be a mistake. A rule we cannot
|
|
12
|
-
* evaluate - the stack came back empty, or it names something we do not detect -
|
|
13
|
-
* stays, because a rule nobody can judge is safer present than silently gone.
|
|
14
|
-
* Those are two different situations and they get two different answers.
|
|
15
|
-
*/
|
|
16
1
|
const norm = (v) => v.trim().toLowerCase();
|
|
17
2
|
/**
|
|
18
3
|
* True when this rule holds in a project built with `stack`.
|
|
@@ -45,10 +30,18 @@ export function rulesForProject(rules, stack) {
|
|
|
45
30
|
const text = rule.text?.trim();
|
|
46
31
|
if (!text)
|
|
47
32
|
continue;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
33
|
+
/**
|
|
34
|
+
* O ALCANCE SAI DAQUI COMO CAMPO, e não mais como prefixo de prosa.
|
|
35
|
+
*
|
|
36
|
+
* Esta função achatava `applies` em `ds-a + ds-b: <texto>` e era o último ponto onde a
|
|
37
|
+
* estrutura existia: do disco em diante, nada mais podia perguntar se a regra vale para o
|
|
38
|
+
* arquivo que está sendo escrito. Foi assim que a regra da serif do `codelevel` - a décima
|
|
39
|
+
* de vinte e cinco, instalada, correta - não disse nada quando um agente a apagou de seis
|
|
40
|
+
* lugares (12/09).
|
|
41
|
+
*
|
|
42
|
+
* A frase não mudou: `ruleLine` volta a montar o prefixo na hora de MOSTRAR.
|
|
43
|
+
*/
|
|
44
|
+
out.push({ text, applies: (rule.applies ?? []).map((a) => a.trim()) });
|
|
52
45
|
}
|
|
53
46
|
return out;
|
|
54
47
|
}
|
package/package.json
CHANGED