synthesisui 0.16.472 → 0.16.474
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/import.js +18 -1
- package/dist/commands/login.js +18 -2
- package/dist/doctor/scan.js +44 -1
- package/dist/fonts.js +19 -1
- package/dist/install-marks.js +22 -3
- package/dist/last-sync.js +20 -0
- package/dist/not-expressed.js +4 -1
- package/dist/what-grew.js +38 -0
- package/package.json +1 -1
package/dist/commands/import.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { importMark, writeSyncMark } from "../last-sync.js";
|
|
2
3
|
import { readStyleSheet } from "../doctor/scss-lens.js";
|
|
3
4
|
import { tsTokenObjects, tsTokensAsVars } from "../doctor/ts-tokens-lens.js";
|
|
4
5
|
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
@@ -4648,6 +4649,16 @@ quiet = false) {
|
|
|
4648
4649
|
say(body(n));
|
|
4649
4650
|
}
|
|
4650
4651
|
}
|
|
4652
|
+
/**
|
|
4653
|
+
* A FRASE DO NASCIMENTO - e ela depende de o código dele nomear alguma coisa. Bateria, 28/09 (b2): num projeto sem
|
|
4654
|
+
* token nenhum o sistema inteiro é a semente, e "v1 is your tokens" levava ao contrário. Ver
|
|
4655
|
+
* `nao-espelha-o-que-nao-existe.spec.ts`.
|
|
4656
|
+
*/
|
|
4657
|
+
export function birthLine(name, declared) {
|
|
4658
|
+
return declared === 0
|
|
4659
|
+
? `${name} - your code names no token yet, so this system is our starting point, with your colours and sizes measured next to it (listed in the report).`
|
|
4660
|
+
: `${name} - v1 is your tokens, nothing improved yet; where your code names nothing, it keeps the starting point's value (listed in the report).`;
|
|
4661
|
+
}
|
|
4651
4662
|
export async function runImport(opts) {
|
|
4652
4663
|
const root = opts.root ?? process.cwd();
|
|
4653
4664
|
/**
|
|
@@ -5141,7 +5152,7 @@ export async function runImport(opts) {
|
|
|
5141
5152
|
* NÃO "ESPELHA EXATAMENTE" - rodadas 11 e 13, 28/09: a v1 trazia um `display` de 4rem e uma mono JetBrains que
|
|
5142
5153
|
* o projeto nunca declarou, e a frase dizia o contrário. As fundações vêm da semente onde ele não nomeia nada.
|
|
5143
5154
|
*/
|
|
5144
|
-
|
|
5155
|
+
birthLine(paint.strong(payload?.name ?? "Your system"), Object.keys(census.declared ?? {}).length)));
|
|
5145
5156
|
/**
|
|
5146
5157
|
* ONDE ELE NASCEU, dito pela plataforma e não pelo que foi pedido.
|
|
5147
5158
|
*
|
|
@@ -5224,6 +5235,12 @@ export async function runImport(opts) {
|
|
|
5224
5235
|
* mais longa do produto ficar mais longa no lugar onde ela já é mais difícil de ler.
|
|
5225
5236
|
*/
|
|
5226
5237
|
if (payload?.slug) {
|
|
5238
|
+
/**
|
|
5239
|
+
* O PONTO DE PARTIDA DO PRIMEIRO `sync` - ver `importMark`. Sem ele, o que o agente cria depois do import nunca é
|
|
5240
|
+
* anunciado (bateria, 28/09: 16 componentes no b7, nenhum dito). Só aqui, com o sistema nascido: um `--dry` não
|
|
5241
|
+
* envia nada, e uma marca sem envio mentiria sobre o que a plataforma tem.
|
|
5242
|
+
*/
|
|
5243
|
+
await writeSyncMark(root, importMark(census, opts.cli));
|
|
5227
5244
|
/**
|
|
5228
5245
|
* O QUE O CSS DELE AINDA IMPORTA - e este aviso sai do CLI, não da skill.
|
|
5229
5246
|
*
|
package/dist/commands/login.js
CHANGED
|
@@ -50,9 +50,25 @@ export async function login(opts) {
|
|
|
50
50
|
*/
|
|
51
51
|
if (!opts.force) {
|
|
52
52
|
const existing = await readCredentials();
|
|
53
|
-
|
|
53
|
+
/**
|
|
54
|
+
* E DIZ QUEM - bateria, 28/09 (b11). O passo Install da galeria acabou de dizer que a CONTA importa ("the
|
|
55
|
+
* account that owns <slug>"), e esta linha dizia só o host. A mesma pergunta que o `status` já faz (#1630).
|
|
56
|
+
* Um token que a plataforma responde e não aceita mais não é "já entrou": segue para o login de verdade.
|
|
57
|
+
* Sem resposta nenhuma, vale o que a máquina tem - offline não é motivo para abrir um browser.
|
|
58
|
+
*/
|
|
59
|
+
const who = existing && sameRegistry(existing.registry, base)
|
|
60
|
+
? await fetch(`${base}/api/auth/get-session`, {
|
|
61
|
+
headers: { Authorization: `Bearer ${existing.token}` },
|
|
62
|
+
})
|
|
63
|
+
.then(async (r) => r.ok ? (await r.json()) : null)
|
|
64
|
+
.then((j) => ({ reached: true, email: j?.user?.email ?? null }))
|
|
65
|
+
.catch(() => ({ reached: false, email: null }))
|
|
66
|
+
: null;
|
|
67
|
+
if (who && (who.email || !who.reached)) {
|
|
54
68
|
console.log("");
|
|
55
|
-
console.log(
|
|
69
|
+
console.log(who.email
|
|
70
|
+
? `✓ Already signed in as ${who.email} on ${base}.`
|
|
71
|
+
: `✓ Already signed in to ${base} on this machine (the account could not be confirmed - the registry did not answer).`);
|
|
56
72
|
console.log(" Run `synthesisui login --force` to sign in as someone else,");
|
|
57
73
|
console.log(" or `synthesisui logout` to sign out of this machine.");
|
|
58
74
|
return;
|
package/dist/doctor/scan.js
CHANGED
|
@@ -490,6 +490,44 @@ export function scanSource(file, source, table) {
|
|
|
490
490
|
}
|
|
491
491
|
return scanCore(file, source, table);
|
|
492
492
|
}
|
|
493
|
+
/**
|
|
494
|
+
* OS SELETORES QUE DECLARAM VOCABULÁRIO - `@theme`, `:root` e a redefinição por tema (bateria, 28/09: b5 escreve
|
|
495
|
+
* `[data-theme="dark"] { --c-bg: #0f172a; ... }`). Dentro deles uma `--x: valor` é a definição, não um uso.
|
|
496
|
+
*/
|
|
497
|
+
const TOKEN_SELECTOR_PART = /^(@theme\b.*|:root(\[[^\]]*\])?|html(\[[^\]]*\]|\.[\w-]+)?|\[data-(theme|mode|scheme|color-scheme)(=[^\]]*)?\]|\.(dark|light))$/;
|
|
498
|
+
/**
|
|
499
|
+
* O SELETOR INTEIRO precisa ser a raiz do tema - `html.dark .shell { --shell-fill: … }` é uma variável de UM
|
|
500
|
+
* componente redefinida por tema, e essa continua sendo acusada (o corpus `css-global-custom-property`).
|
|
501
|
+
*/
|
|
502
|
+
function isTokenSelector(selector) {
|
|
503
|
+
const parts = selector.split(",").map((p) => p.trim());
|
|
504
|
+
return parts.length > 0 && parts.every((p) => TOKEN_SELECTOR_PART.test(p));
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* ONDE ESTÃO OS VALORES QUE ESTA LINHA ATRIBUI A UMA VARIÁVEL, dentro de um bloco de vocabulário - bateria, 28/09.
|
|
508
|
+
*
|
|
509
|
+
* A guarda de `scanCore` lia `--x:` só no COMEÇO da linha, e `:root { --q-stone: #44403c; ... }` numa linha só
|
|
510
|
+
* voltava como quatro "replace now" (b8). Trocar `#44403c` por `var(--q-stone)` na própria declaração faz o token
|
|
511
|
+
* apontar para si mesmo: o navegador descarta o valor e toda cor do app some. O seletor que governa cada
|
|
512
|
+
* declaração é o do último `{` antes dela na linha; sem `{` antes, vale o bloco multilinha em que a linha está.
|
|
513
|
+
*/
|
|
514
|
+
function declarationSpans(line, inBlock) {
|
|
515
|
+
const out = [];
|
|
516
|
+
for (const m of line.matchAll(DECLARES_ALL)) {
|
|
517
|
+
const start = m.index ?? 0;
|
|
518
|
+
const open = line.lastIndexOf("{", start + 1);
|
|
519
|
+
const governed = open < 0
|
|
520
|
+
? inBlock
|
|
521
|
+
: line.lastIndexOf("}", start) < open &&
|
|
522
|
+
isTokenSelector(line.slice(line.lastIndexOf("}", open) + 1, open).trim());
|
|
523
|
+
if (!governed)
|
|
524
|
+
continue;
|
|
525
|
+
const colon = m[0].indexOf(":");
|
|
526
|
+
const valueAt = start + m[0].indexOf(m[2], colon);
|
|
527
|
+
out.push([valueAt, valueAt + m[2].length]);
|
|
528
|
+
}
|
|
529
|
+
return out;
|
|
530
|
+
}
|
|
493
531
|
/** `--color-ocean-500: #059aed;` - uma declaração de custom property no CSS dele. */
|
|
494
532
|
/** Cada declaração `--x: valor;` da linha, quantas houver - ver o laço em `scanCore`. */
|
|
495
533
|
const DECLARES_ALL = /(?:^|[\s{;])(--[a-z0-9-]+)\s*:\s*([^;{}]+);/gi;
|
|
@@ -558,7 +596,7 @@ function scanCore(file, source, table) {
|
|
|
558
596
|
const indent = line ? raw.indexOf(line) : 0;
|
|
559
597
|
const at = i + 1;
|
|
560
598
|
const depthBefore = braceDepth;
|
|
561
|
-
const opensTokenBlock =
|
|
599
|
+
const opensTokenBlock = line.includes("{") && isTokenSelector(line.slice(0, line.indexOf("{")).trim());
|
|
562
600
|
if (opensTokenBlock && tokenBlockDepth === 0)
|
|
563
601
|
tokenBlockDepth = depthBefore + 1;
|
|
564
602
|
const inTokenBlock = tokenBlockDepth > 0 && (opensTokenBlock || depthBefore >= tokenBlockDepth);
|
|
@@ -639,6 +677,8 @@ function scanCore(file, source, table) {
|
|
|
639
677
|
// written by hand - flagging it would be telling someone to tokenize a
|
|
640
678
|
// variable. And the same literal twice in one declaration (a two-stop
|
|
641
679
|
// shadow) is one decision, so it is reported once.
|
|
680
|
+
const declared = declarationSpans(line, inTokenBlock && !opensTokenBlock);
|
|
681
|
+
const inDeclaration = (at) => declared.some(([a, b]) => at >= a && at < b);
|
|
642
682
|
const spans = fallbackSpans(line, table);
|
|
643
683
|
const inFallback = (at) => spans.some(([a, b]) => at >= a && at <= b);
|
|
644
684
|
/** Ver `markupTextSpans`: HTML montado como texto sai do alcance da folha do app. */
|
|
@@ -653,6 +693,9 @@ function scanCore(file, source, table) {
|
|
|
653
693
|
const push = (kind, literal, col = -1) => {
|
|
654
694
|
if (literal.includes("$") || literal.includes("{"))
|
|
655
695
|
return;
|
|
696
|
+
/** A definição do token não é drift - ver `declarationSpans`. */
|
|
697
|
+
if (col >= 0 && inDeclaration(col))
|
|
698
|
+
return;
|
|
656
699
|
if (col >= 0 && inFallback(col)) {
|
|
657
700
|
setAside("a token's own fallback");
|
|
658
701
|
return;
|
package/dist/fonts.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { subsetsToEmit, weightsToEmit, } from "./next-font-weights.js";
|
|
2
|
-
// Fallbacks genéricos do CSS -
|
|
2
|
+
// Fallbacks genéricos do CSS e as famílias de sistema - nenhuma delas é webfont para baixar.
|
|
3
3
|
const GENERIC_FAMILIES = new Set([
|
|
4
4
|
"sans-serif",
|
|
5
5
|
"serif",
|
|
@@ -12,6 +12,24 @@ const GENERIC_FAMILIES = new Set([
|
|
|
12
12
|
"fantasy",
|
|
13
13
|
"inherit",
|
|
14
14
|
"initial",
|
|
15
|
+
/**
|
|
16
|
+
* AS FAMÍLIAS QUE JÁ ESTÃO EM TODA MÁQUINA - bateria, 28/09. Georgia, Arial e Verdana não se baixam de lugar
|
|
17
|
+
* nenhum, e pedi-las ao Google Fonts gerava um `<link>` para o que ele não tem, logo abaixo da frase que dizia isso.
|
|
18
|
+
*/
|
|
19
|
+
"arial",
|
|
20
|
+
"helvetica",
|
|
21
|
+
"helvetica neue",
|
|
22
|
+
"georgia",
|
|
23
|
+
"verdana",
|
|
24
|
+
"tahoma",
|
|
25
|
+
"trebuchet ms",
|
|
26
|
+
"times new roman",
|
|
27
|
+
"times",
|
|
28
|
+
"courier new",
|
|
29
|
+
"courier",
|
|
30
|
+
"segoe ui",
|
|
31
|
+
"-apple-system",
|
|
32
|
+
"blinkmacsystemfont",
|
|
15
33
|
]);
|
|
16
34
|
/**
|
|
17
35
|
* A PRIMEIRA FAMÍLIA DE UMA PILHA - `"Geist Mono", ui-monospace, monospace` é `Geist Mono`.
|
package/dist/install-marks.js
CHANGED
|
@@ -300,7 +300,11 @@
|
|
|
300
300
|
* o sync destacado deixa para o fim do turno dizer (re-rodada depois do #1631). Uma pasta escrita antes deixaria esse
|
|
301
301
|
* arquivo local entrar num commit.
|
|
302
302
|
*/
|
|
303
|
-
|
|
303
|
+
/**
|
|
304
|
+
* 0.16.472 -> 0.16.473 em 28/09, e o passo 1 dá **SIM** (a bateria): as famílias de sistema (Georgia, Arial, Verdana)
|
|
305
|
+
* deixam de virar `<link>` do Google Fonts, e a webfont ao lado delas ganha o `fonts.ts` que antes não saía.
|
|
306
|
+
*/
|
|
307
|
+
export const MATERIALISER_SINCE = "0.16.473";
|
|
304
308
|
/**
|
|
305
309
|
* A ÚLTIMA VERSÃO EM QUE O QUE O HOOK RODA MUDOU.
|
|
306
310
|
*
|
|
@@ -476,7 +480,17 @@ export const COUNTED_DIFFERENTLY = "this run counts a value as named only when Y
|
|
|
476
480
|
* 0.16.471 -> 0.16.472 em 28/09, e o passo 1 dá **SIM**: o fim do turno entrega ao agente o que o sync destacado do turno
|
|
477
481
|
* anterior mandou para o rascunho (re-rodada, f12). Um hook anterior deixa esses componentes entrarem calados.
|
|
478
482
|
*/
|
|
479
|
-
|
|
483
|
+
/**
|
|
484
|
+
* 0.16.472 -> 0.16.473 em 28/09, e o passo 1 dá **SIM** (a bateria, b8): o hook deixa de acusar o valor que um
|
|
485
|
+
* `:root { --q-stone: #44403c; … }` de uma linha atribui ao token. Um hook anterior manda trocar a definição pelo
|
|
486
|
+
* próprio nome, e quem obedece apaga a cor.
|
|
487
|
+
*/
|
|
488
|
+
/**
|
|
489
|
+
* 0.16.473 -> 0.16.474 em 28/09, e o passo 1 dá **SIM** (a bateria, b5): o que o fim do turno diz de um componente novo
|
|
490
|
+
* passa a medir as regras da folha global que ele veste pelo `className`. Um hook anterior diz "no colour or spacing of
|
|
491
|
+
* its own to measure" sobre o componente de um projeto que estiliza por classe global.
|
|
492
|
+
*/
|
|
493
|
+
export const CHECKER_SINCE = "0.16.474";
|
|
480
494
|
/**
|
|
481
495
|
* A ÚLTIMA VERSÃO EM QUE OS LEITORES PASSARAM A PRODUZIR UM CENSO DIFERENTE.
|
|
482
496
|
*
|
|
@@ -941,7 +955,12 @@ export const CHECKER_SINCE = "0.16.472";
|
|
|
941
955
|
* não põe no sistema a peça do framework (`StrictMode`, `next/link`) nem a raiz do Vite (`src/App.tsx`); e o que
|
|
942
956
|
* mora na pasta compartilhada de componentes nunca vira "feature". Um censo lido antes guardou tudo isso errado.
|
|
943
957
|
*/
|
|
944
|
-
|
|
958
|
+
/**
|
|
959
|
+
* 0.16.471 -> 0.16.473 em 28/09, e o passo 1 dá **SIM** (a bateria, b3, b5, b8): a varredura deixa de contar como valor à
|
|
960
|
+
* mão o que um `:root { --a: x; }` ou um `[data-theme="dark"] { … }` de UMA linha atribui a uma variável - a definição do
|
|
961
|
+
* token. Quatro apps do corpus moveram. Um censo lido antes guardou essas declarações como drift.
|
|
962
|
+
*/
|
|
963
|
+
export const READER_SINCE = "0.16.473";
|
|
945
964
|
/**
|
|
946
965
|
* O QUE ESTÁ INSTALADO AQUI FICOU PARA TRÁS - e as DUAS condições que fazem isso ser verdade.
|
|
947
966
|
*
|
package/dist/last-sync.js
CHANGED
|
@@ -45,6 +45,26 @@ components) {
|
|
|
45
45
|
}
|
|
46
46
|
return out;
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* A MARCA QUE O IMPORT DEIXA - o ponto de partida do primeiro `sync`. Bateria, 28/09 (b7): um projeto sem componente
|
|
50
|
+
* ganhou 16 ao longo de cinco telas e nenhum foi anunciado, porque só o `sync` gravava marca e `whatGrew` sem marca
|
|
51
|
+
* anterior não anuncia nada. Os mesmos campos que o `sync` grava, lidos do mesmo censo - nunca uma segunda conta.
|
|
52
|
+
* `components` é o que o censo admitiu, que é o que o import envia.
|
|
53
|
+
*/
|
|
54
|
+
export function importMark(census, cli) {
|
|
55
|
+
const sum = (r) => Object.values(r ?? {}).reduce((n, v) => n + v, 0);
|
|
56
|
+
const readings = fingerprintReadings(census.components);
|
|
57
|
+
return {
|
|
58
|
+
at: new Date().toISOString(),
|
|
59
|
+
components: Object.keys(readings).length,
|
|
60
|
+
declarations: census.coverage?.declarations ?? 0,
|
|
61
|
+
seen: sum(census.ledger?.counted),
|
|
62
|
+
interpreted: sum(census.ledger?.interpreted),
|
|
63
|
+
unread: Object.fromEntries((census.ledger?.unread ?? []).map((g) => [g.reason ?? "unknown", g.uses ?? 0])),
|
|
64
|
+
...(cli ? { cli } : {}),
|
|
65
|
+
readings,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
48
68
|
const path = (root) => join(root, "_synthesisui", LAST_SYNC_FILE);
|
|
49
69
|
export async function readSyncMark(root) {
|
|
50
70
|
try {
|
package/dist/not-expressed.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SKIP_LABEL } from "./doctor/component-gate.js";
|
|
1
2
|
/**
|
|
2
3
|
* O QUE ESTA LEITURA NÃO CONSEGUIU SEGURAR, escrito pela esteira e não pedido a ninguém.
|
|
3
4
|
*
|
|
@@ -69,7 +70,9 @@ export function notExpressed(census) {
|
|
|
69
70
|
" a reading to correct - and the reason below is the one to argue with.");
|
|
70
71
|
out.push("");
|
|
71
72
|
for (const s of skipped)
|
|
72
|
-
out.push(
|
|
73
|
+
out.push(
|
|
74
|
+
/** `because` é opcional; sem ele, o rótulo do `why` - nunca `undefined` (bateria, 28/09). */
|
|
75
|
+
` ${s.name}${s.file ? ` (${s.file})` : ""}\n ${s.because ?? SKIP_LABEL[s.why] ?? s.why}`);
|
|
73
76
|
}
|
|
74
77
|
out.push("");
|
|
75
78
|
return `${out.join("\n")}\n`;
|
package/dist/what-grew.js
CHANGED
|
@@ -16,12 +16,16 @@ before) {
|
|
|
16
16
|
return { born: [], gone };
|
|
17
17
|
const { table } = await loadSystem(root).catch(() => ({ table: null }));
|
|
18
18
|
const context = [];
|
|
19
|
+
/** As folhas GLOBAIS (não `.module.*`) - ver `globalRulesFor`. */
|
|
20
|
+
const globals = [];
|
|
19
21
|
if (table) {
|
|
20
22
|
for await (const file of walkAll([root])) {
|
|
21
23
|
const rel = relative(root, file);
|
|
22
24
|
if (!/\.(css|scss|sass|less)$/i.test(rel))
|
|
23
25
|
continue;
|
|
24
26
|
const src = await readFile(file, "utf8").catch(() => "");
|
|
27
|
+
if (src && !/\.module\.[a-z]+$/i.test(rel))
|
|
28
|
+
globals.push({ rel, src });
|
|
25
29
|
const report = src ? scanSource(rel, src, table) : null;
|
|
26
30
|
if (report?.ownDeclared?.length)
|
|
27
31
|
context.push(report);
|
|
@@ -49,6 +53,17 @@ before) {
|
|
|
49
53
|
if (css)
|
|
50
54
|
reports.push(scanSource(sheet, css, table));
|
|
51
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* E AS REGRAS GLOBAIS QUE ELE VESTE - bateria, 28/09 (b5): o componente de um projeto que estiliza por classe global
|
|
58
|
+
* não importa folha nenhuma, e saía "no colour or spacing of its own to measure".
|
|
59
|
+
*/
|
|
60
|
+
const worn = classesWorn(src);
|
|
61
|
+
if (worn.size > 0)
|
|
62
|
+
for (const g of globals) {
|
|
63
|
+
const rules = globalRulesFor(g.src, worn);
|
|
64
|
+
if (rules)
|
|
65
|
+
reports.push(scanSource(g.rel, rules, table));
|
|
66
|
+
}
|
|
52
67
|
const d = diagnose(reports, context);
|
|
53
68
|
/** Classes utilitárias numa string qualquer do arquivo - `className="…"`, `cn("…")`, `cva("…")` (f11, 28/09). */
|
|
54
69
|
const utilities = !d.reach.measured &&
|
|
@@ -117,3 +132,26 @@ export async function takeGrowthNote(root) {
|
|
|
117
132
|
return null;
|
|
118
133
|
}
|
|
119
134
|
}
|
|
135
|
+
/** As classes escritas por extenso no `className` - `className="a b"`, `'a'` ou `` `a b` `` sem interpolação. */
|
|
136
|
+
function classesWorn(src) {
|
|
137
|
+
const out = new Set();
|
|
138
|
+
for (const m of src.matchAll(/className=(?:"([^"]*)"|'([^']*)'|\{`([^`$]*)`\})/g))
|
|
139
|
+
for (const c of (m[1] ?? m[2] ?? m[3] ?? "").split(/\s+/))
|
|
140
|
+
if (c)
|
|
141
|
+
out.add(c);
|
|
142
|
+
return out;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* AS REGRAS DE UMA FOLHA GLOBAL CUJO SELETOR NOMEIA UMA DAS CLASSES QUE ELE VESTE, recortadas como CSS - só o bloco
|
|
146
|
+
* mais interno (`[^{}]`), então uma regra dentro de `@media` também entra. `null` quando nenhuma casa.
|
|
147
|
+
*/
|
|
148
|
+
function globalRulesFor(css, worn) {
|
|
149
|
+
const out = [];
|
|
150
|
+
for (const m of css.matchAll(/([^{}]+)\{([^{}]*)\}/g)) {
|
|
151
|
+
const selector = (m[1] ?? "").trim();
|
|
152
|
+
const names = [...selector.matchAll(/\.([A-Za-z_][\w-]*)/g)].map((x) => x[1]);
|
|
153
|
+
if (names.some((n) => worn.has(n)))
|
|
154
|
+
out.push(`${selector} { ${(m[2] ?? "").trim()} }`);
|
|
155
|
+
}
|
|
156
|
+
return out.length > 0 ? `${out.join("\n")}\n` : null;
|
|
157
|
+
}
|
package/package.json
CHANGED