@rendra-ui/web 2.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.
Files changed (150) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +433 -0
  3. package/bin/rendra.mjs +114 -0
  4. package/dist/base.css +246 -0
  5. package/dist/button-DIcpJwK9.js +143 -0
  6. package/dist/button-DIcpJwK9.js.map +1 -0
  7. package/dist/chart.js +520 -0
  8. package/dist/chart.js.map +1 -0
  9. package/dist/cli/auditar.js +125 -0
  10. package/dist/cli/auditar.js.map +1 -0
  11. package/dist/cli/codigos.js +15 -0
  12. package/dist/cli/codigos.js.map +1 -0
  13. package/dist/cli/help.js +17 -0
  14. package/dist/cli/help.js.map +1 -0
  15. package/dist/cli/trocar.js +242 -0
  16. package/dist/cli/trocar.js.map +1 -0
  17. package/dist/cli/typescript-loader.js +27 -0
  18. package/dist/cli/typescript-loader.js.map +1 -0
  19. package/dist/cn-DVPheE99.js +22 -0
  20. package/dist/cn-DVPheE99.js.map +1 -0
  21. package/dist/components-Cm2ezlPT.js +800 -0
  22. package/dist/components-Cm2ezlPT.js.map +1 -0
  23. package/dist/components.css +3365 -0
  24. package/dist/document-viewer.js +204 -0
  25. package/dist/document-viewer.js.map +1 -0
  26. package/dist/empty-state-DtyZAfYb.js +168 -0
  27. package/dist/empty-state-DtyZAfYb.js.map +1 -0
  28. package/dist/index.js +10530 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/rendra-provider-cCnquOZy.js +88 -0
  31. package/dist/rendra-provider-cCnquOZy.js.map +1 -0
  32. package/dist/rich-text-editor.js +439 -0
  33. package/dist/rich-text-editor.js.map +1 -0
  34. package/dist/router-bridge.js +51 -0
  35. package/dist/router-bridge.js.map +1 -0
  36. package/dist/tokens.css +732 -0
  37. package/dist/tooltip-DyeuGPCc.js +105 -0
  38. package/dist/tooltip-DyeuGPCc.js.map +1 -0
  39. package/dist/types/brand/apply-theme.d.ts +9 -0
  40. package/dist/types/brand/brand-context.d.ts +21 -0
  41. package/dist/types/brand/brand-provider.d.ts +49 -0
  42. package/dist/types/brand/palette.d.ts +63 -0
  43. package/dist/types/brand/public.d.ts +6 -0
  44. package/dist/types/brand/theme.d.ts +80 -0
  45. package/dist/types/brand/types.d.ts +54 -0
  46. package/dist/types/brand/use-brand.d.ts +1 -0
  47. package/dist/types/catalog/components.d.ts +81 -0
  48. package/dist/types/chart.d.ts +1 -0
  49. package/dist/types/components/app-shell/app-shell.d.ts +36 -0
  50. package/dist/types/components/app-shell/command-search.d.ts +1 -0
  51. package/dist/types/components/app-shell/header.d.ts +1 -0
  52. package/dist/types/components/app-shell/layout.d.ts +53 -0
  53. package/dist/types/components/app-shell/mega-menu.d.ts +1 -0
  54. package/dist/types/components/app-shell/navigation-utils.d.ts +18 -0
  55. package/dist/types/components/app-shell/notifications.d.ts +6 -0
  56. package/dist/types/components/app-shell/shell-context.d.ts +48 -0
  57. package/dist/types/components/app-shell/sidebar.d.ts +1 -0
  58. package/dist/types/components/app-shell/types.d.ts +53 -0
  59. package/dist/types/components/layout/index.d.ts +4 -0
  60. package/dist/types/components/layout/page-header.d.ts +38 -0
  61. package/dist/types/components/layout/primitives.d.ts +71 -0
  62. package/dist/types/components/layout/tokens.d.ts +43 -0
  63. package/dist/types/components/rendra-provider.d.ts +48 -0
  64. package/dist/types/components/rendra-router-bridge.d.ts +18 -0
  65. package/dist/types/components/ui/accordion.d.ts +16 -0
  66. package/dist/types/components/ui/action-bar.d.ts +35 -0
  67. package/dist/types/components/ui/alert.d.ts +26 -0
  68. package/dist/types/components/ui/avatar.d.ts +23 -0
  69. package/dist/types/components/ui/badge.d.ts +15 -0
  70. package/dist/types/components/ui/brand-feedback-icon.d.ts +26 -0
  71. package/dist/types/components/ui/brand-logo.d.ts +8 -0
  72. package/dist/types/components/ui/breadcrumb.d.ts +19 -0
  73. package/dist/types/components/ui/button-group.d.ts +27 -0
  74. package/dist/types/components/ui/button.d.ts +30 -0
  75. package/dist/types/components/ui/calendar.d.ts +34 -0
  76. package/dist/types/components/ui/card.d.ts +32 -0
  77. package/dist/types/components/ui/chart.d.ts +57 -0
  78. package/dist/types/components/ui/chat.d.ts +137 -0
  79. package/dist/types/components/ui/checkbox.d.ts +37 -0
  80. package/dist/types/components/ui/checklist.d.ts +16 -0
  81. package/dist/types/components/ui/color-picker.d.ts +13 -0
  82. package/dist/types/components/ui/data-toolbar.d.ts +37 -0
  83. package/dist/types/components/ui/date-picker.d.ts +41 -0
  84. package/dist/types/components/ui/document-viewer.d.ts +19 -0
  85. package/dist/types/components/ui/drawer.d.ts +30 -0
  86. package/dist/types/components/ui/dropdown-menu.d.ts +20 -0
  87. package/dist/types/components/ui/empty-state.d.ts +15 -0
  88. package/dist/types/components/ui/error-page.d.ts +13 -0
  89. package/dist/types/components/ui/field.d.ts +42 -0
  90. package/dist/types/components/ui/form.d.ts +42 -0
  91. package/dist/types/components/ui/image-cropper.d.ts +55 -0
  92. package/dist/types/components/ui/image-viewer.d.ts +14 -0
  93. package/dist/types/components/ui/info-hint.d.ts +9 -0
  94. package/dist/types/components/ui/input.d.ts +91 -0
  95. package/dist/types/components/ui/kanban.d.ts +98 -0
  96. package/dist/types/components/ui/list.d.ts +31 -0
  97. package/dist/types/components/ui/modal.d.ts +27 -0
  98. package/dist/types/components/ui/otp-input.d.ts +17 -0
  99. package/dist/types/components/ui/overlay-shell.d.ts +14 -0
  100. package/dist/types/components/ui/pagination.d.ts +18 -0
  101. package/dist/types/components/ui/picker-panel.d.ts +24 -0
  102. package/dist/types/components/ui/popover.d.ts +12 -0
  103. package/dist/types/components/ui/progress.d.ts +16 -0
  104. package/dist/types/components/ui/qr-code.d.ts +13 -0
  105. package/dist/types/components/ui/radio-group.d.ts +27 -0
  106. package/dist/types/components/ui/rating.d.ts +19 -0
  107. package/dist/types/components/ui/rendra-credit.d.ts +12 -0
  108. package/dist/types/components/ui/repeatable-field.d.ts +22 -0
  109. package/dist/types/components/ui/rich-text-editor.d.ts +21 -0
  110. package/dist/types/components/ui/select.d.ts +65 -0
  111. package/dist/types/components/ui/separator.d.ts +7 -0
  112. package/dist/types/components/ui/skeleton.d.ts +3 -0
  113. package/dist/types/components/ui/slider.d.ts +18 -0
  114. package/dist/types/components/ui/sortable-handle.d.ts +16 -0
  115. package/dist/types/components/ui/spinner.d.ts +7 -0
  116. package/dist/types/components/ui/stat-card.d.ts +20 -0
  117. package/dist/types/components/ui/switch.d.ts +15 -0
  118. package/dist/types/components/ui/table.d.ts +134 -0
  119. package/dist/types/components/ui/tabs.d.ts +25 -0
  120. package/dist/types/components/ui/textarea.d.ts +10 -0
  121. package/dist/types/components/ui/timeline.d.ts +18 -0
  122. package/dist/types/components/ui/toast.d.ts +18 -0
  123. package/dist/types/components/ui/tooltip.d.ts +17 -0
  124. package/dist/types/components/ui/upload.d.ts +50 -0
  125. package/dist/types/components/ui/widget-grid.d.ts +22 -0
  126. package/dist/types/components/ui/wizard.d.ts +38 -0
  127. package/dist/types/document-viewer.d.ts +1 -0
  128. package/dist/types/hooks/use-breakpoint.d.ts +21 -0
  129. package/dist/types/hooks/use-debounced-value.d.ts +5 -0
  130. package/dist/types/hooks/use-fill-height.d.ts +6 -0
  131. package/dist/types/hooks/use-lookup.d.ts +30 -0
  132. package/dist/types/hooks/use-reduced-motion.d.ts +1 -0
  133. package/dist/types/index.d.ts +76 -0
  134. package/dist/types/lib/cn.d.ts +2 -0
  135. package/dist/types/lib/contrast.d.ts +2 -0
  136. package/dist/types/lib/control.d.ts +20 -0
  137. package/dist/types/lib/lookup.d.ts +22 -0
  138. package/dist/types/lib/masks.d.ts +46 -0
  139. package/dist/types/lib/qr-encode.d.ts +48 -0
  140. package/dist/types/lib/shape.d.ts +8 -0
  141. package/dist/types/lib/sortable.d.ts +72 -0
  142. package/dist/types/lib/validators.d.ts +14 -0
  143. package/dist/types/rendra-router-bridge.d.ts +1 -0
  144. package/dist/types/rich-text-editor.d.ts +1 -0
  145. package/dist/types/widget-grid.d.ts +1 -0
  146. package/dist/use-breakpoint-BmXOZtF9.js +49 -0
  147. package/dist/use-breakpoint-BmXOZtF9.js.map +1 -0
  148. package/dist/widget-grid.js +118 -0
  149. package/dist/widget-grid.js.map +1 -0
  150. package/package.json +182 -0
@@ -0,0 +1,125 @@
1
+ /*! Rendra Design System v2.1.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
+ import { join, relative, sep } from "node:path";
4
+ //#region src/cli/auditar.ts
5
+ var isCommentLine = (line) => /^\s*(\/\/|\*|\/\*)/.test(line);
6
+ /** As seis regras genéricas mais simples: um regex por linha. */
7
+ var LINE_RULES = [
8
+ {
9
+ id: "cor-fixa",
10
+ test: /#[0-9a-fA-F]{3,8}\b|\b(?:rgba?|hsla?|oklch|oklab)\(/g,
11
+ message: "Cor fixa em componente. Use um token semântico (bg-primary, text-muted-foreground).",
12
+ skipLine: (line) => isCommentLine(line) || /&#\d+;/.test(line)
13
+ },
14
+ {
15
+ id: "valor-arbitrario",
16
+ test: /(?<![\w-])(?:[a-z]+:)*-?(?!transition-)[a-z][a-z-]*-\[[^\]\s]+\](?!(?:\/[\w-]+)?:)/g,
17
+ message: "Valor arbitrário do Tailwind. Use a escala de tokens ou crie um token nomeado.",
18
+ skipLine: isCommentLine
19
+ },
20
+ {
21
+ id: "estilo-inline",
22
+ test: /style=\{(?![^}]*['"]--)(?!\s*$)/g,
23
+ message: "Estilo inline. Use classes; para valor dinâmico use variável CSS (--x)."
24
+ },
25
+ {
26
+ id: "fonte-fixa",
27
+ test: /font-family|fontFamily|['"](?:Poppins|Inter|Roboto|Arial|Helvetica)(?:['",]| sans| serif)/g,
28
+ message: "Nome de fonte fixo. A fonte vem da variável de tema da marca (ex.: --rendra-brand-font)."
29
+ },
30
+ {
31
+ id: "100vh",
32
+ test: /100vh|\bh-screen\b|\bmin-h-screen\b/g,
33
+ message: "Use 100dvh (h-dvh / min-h-dvh), nunca 100vh."
34
+ },
35
+ {
36
+ id: "raio-fixo",
37
+ test: /border-radius\s*:\s*(?!var\()\d|borderRadius\s*:\s*(?!['"`]?var\()['"`]?\d/g,
38
+ message: "Raio fixo. Use um token de raio por papel (rounded-control, rounded-surface, rounded-item...) ou uma variável de tema.",
39
+ skipLine: isCommentLine
40
+ }
41
+ ];
42
+ var ALLOWED_STEPS = /* @__PURE__ */ new Set([
43
+ "0",
44
+ "1",
45
+ "2",
46
+ "3",
47
+ "4",
48
+ "6",
49
+ "8",
50
+ "12",
51
+ "16",
52
+ "24"
53
+ ]);
54
+ var STEP_CLASS = /(?<![\w-])-?(?:p[xytrbl]?|m[xytrbl]?|gap(?:-[xy])?|w|h|size|min-[wh]|max-[wh]|inset(?:-[xy])?|top|left|right|bottom|space-[xy]|translate-[xy]|scroll-[mp][xytrbl]?)-(\d+(?:\.\d+)?)(?![\d/.\w-])/g;
55
+ /** Sétima regra ("fora-da-escala"): olha todos os degraus da linha, não só o primeiro. */
56
+ function checkForaDaEscala(line) {
57
+ for (const m of line.matchAll(STEP_CLASS)) if (!ALLOWED_STEPS.has(m[1])) return true;
58
+ return false;
59
+ }
60
+ /**
61
+ * Varre o texto de um arquivo (linha a linha) contra as sete regras genéricas de
62
+ * DESIGN_RULES.md. Pura: não lê arquivo, não decide o que é escopo do projeto (isso é de quem
63
+ * chama, como `auditar` abaixo ou o `walk`/`ALLOWED_DIRS` de check-design-rules.mjs).
64
+ */
65
+ function auditLines(text) {
66
+ const found = [];
67
+ text.split("\n").forEach((line, i) => {
68
+ for (const rule of LINE_RULES) {
69
+ if (rule.skipLine?.(line)) continue;
70
+ rule.test.lastIndex = 0;
71
+ if (rule.test.test(line)) found.push({
72
+ line: i + 1,
73
+ rule: rule.id,
74
+ message: rule.message,
75
+ source: line.trim()
76
+ });
77
+ }
78
+ if (!isCommentLine(line) && checkForaDaEscala(line)) found.push({
79
+ line: i + 1,
80
+ rule: "fora-da-escala",
81
+ message: "Degrau fora da escala (permitidos: 0, 1, 2, 3, 4, 6, 8, 12, 16, 24). Use a escala ou um token nomeado.",
82
+ source: line.trim()
83
+ });
84
+ });
85
+ return found;
86
+ }
87
+ var SKIP_DIR_NAMES = /* @__PURE__ */ new Set([
88
+ "node_modules",
89
+ "dist",
90
+ "build",
91
+ ".git",
92
+ ".storybook-static"
93
+ ]);
94
+ function walk(dir) {
95
+ return readdirSync(dir).flatMap((name) => {
96
+ if (SKIP_DIR_NAMES.has(name)) return [];
97
+ const full = join(dir, name);
98
+ return statSync(full).isDirectory() ? walk(full) : [full];
99
+ });
100
+ }
101
+ /**
102
+ * Varre `cwd` (ou `cwd/src`, quando existir: a convenção deste boilerplate e da maioria dos
103
+ * projetos React) por arquivos `.ts`/`.tsx`/`.js`/`.jsx`/`.css`, ignorando teste e story, e
104
+ * devolve toda violação das sete regras genéricas. É o que `rendra auditar [cwd]` chama; não
105
+ * imprime nada e não lança (a CLI decide o formato de saída e o código de saída).
106
+ */
107
+ function auditar(cwd) {
108
+ const root = existsSync(join(cwd, "src")) ? join(cwd, "src") : cwd;
109
+ if (!existsSync(root)) return [];
110
+ const files = walk(root).filter((f) => /\.(tsx?|jsx?|css)$/.test(f) && !f.includes(".test.") && !f.includes(".stories."));
111
+ const problems = [];
112
+ for (const file of files) {
113
+ const rel = relative(cwd, file).split(sep).join("/");
114
+ const text = readFileSync(file, "utf8");
115
+ for (const violation of auditLines(text)) problems.push({
116
+ file: rel,
117
+ ...violation
118
+ });
119
+ }
120
+ return problems.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line);
121
+ }
122
+ //#endregion
123
+ export { LINE_RULES, auditLines, auditar };
124
+
125
+ //# sourceMappingURL=auditar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auditar.js","names":[],"sources":["../../src/cli/auditar.ts"],"sourcesContent":["/*\n * Regras genéricas de design (DESIGN_RULES.md) que não são específicas deste repositório:\n * qualquer projeto que use o Rendra pode ter cor fixa, valor arbitrário do Tailwind, estilo\n * inline, fonte fixa, 100vh ou raio fixo. Módulo único: `scripts/check-design-rules.mjs` (as\n * regras deste repositório, mais as específicas: var-inline, svg-marca, classe-dinamica,\n * texto-estreito, primaria-como-texto, componente-duplicado, css-fora-do-lugar, botao-solto)\n * e `rendra auditar` (a CLI publicada, para qualquer projeto de destino) importam daqui, para\n * nunca divergir.\n *\n * Duas camadas:\n * - `auditLines(text)`: pura, sem acesso a arquivo. Recebe o texto de UM arquivo e devolve\n * as violações das sete regras (linha, regra, mensagem, trecho). `check-design-rules.mjs`\n * chama isso por arquivo, porque ele já tem sua própria varredura de diretório com a\n * exceção `src/brand`/`src/styles` (regras próprias do tema deste repositório, que não\n * fazem sentido num projeto de destino qualquer).\n * - `auditar(cwd)`: varre `cwd` (ou `cwd/src`, quando existir) sozinha, sem exceção de\n * diretório, e devolve a lista completa. É o que `rendra auditar` chama.\n */\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'\nimport { join, relative, sep } from 'node:path'\n\nexport interface AuditViolation {\n line: number\n rule: string\n message: string\n source: string\n}\n\nexport interface FileAuditViolation extends AuditViolation {\n file: string\n}\n\ninterface LineRule {\n id: string\n test: RegExp\n message: string\n skipLine?: (line: string) => boolean\n}\n\nconst isCommentLine = (line: string) => /^\\s*(\\/\\/|\\*|\\/\\*)/.test(line)\n\n/** As seis regras genéricas mais simples: um regex por linha. */\nexport const LINE_RULES: LineRule[] = [\n {\n id: 'cor-fixa',\n test: /#[0-9a-fA-F]{3,8}\\b|\\b(?:rgba?|hsla?|oklch|oklab)\\(/g,\n message: 'Cor fixa em componente. Use um token semântico (bg-primary, text-muted-foreground).',\n skipLine: (line) => isCommentLine(line) || /&#\\d+;/.test(line),\n },\n {\n id: 'valor-arbitrario',\n // Valor arbitrário (p-[13px]). Variantes de estado (data-[state=open]:) terminam em ':' e são\n // permitidas; transition-[...] só lista propriedades, também permitido.\n test: /(?<![\\w-])(?:[a-z]+:)*-?(?!transition-)[a-z][a-z-]*-\\[[^\\]\\s]+\\](?!(?:\\/[\\w-]+)?:)/g,\n message: 'Valor arbitrário do Tailwind. Use a escala de tokens ou crie um token nomeado.',\n skipLine: isCommentLine,\n },\n {\n id: 'estilo-inline',\n // Qualquer style= que não seja só variável CSS (--x) é estilo inline.\n test: /style=\\{(?![^}]*['\"]--)(?!\\s*$)/g,\n message: 'Estilo inline. Use classes; para valor dinâmico use variável CSS (--x).',\n },\n {\n id: 'fonte-fixa',\n // Só em contexto de fonte (declaração ou nome entre aspas numa pilha de fontes): a\n // palavra \"Inter\" num texto da interface não é violação.\n test: /font-family|fontFamily|['\"](?:Poppins|Inter|Roboto|Arial|Helvetica)(?:['\",]| sans| serif)/g,\n message:\n 'Nome de fonte fixo. A fonte vem da variável de tema da marca (ex.: --rendra-brand-font).',\n },\n {\n id: '100vh',\n test: /100vh|\\bh-screen\\b|\\bmin-h-screen\\b/g,\n message: 'Use 100dvh (h-dvh / min-h-dvh), nunca 100vh.',\n },\n {\n id: 'raio-fixo',\n // border-radius fixo em CSS ou em estilo inline (borderRadius): nunca em px/rem/% direto,\n // sempre por um token de raio por papel (rounded-control, rounded-surface, rounded-item...)\n // ou uma variável de tema (--rendra-shape-*, --rendra-radius).\n test: /border-radius\\s*:\\s*(?!var\\()\\d|borderRadius\\s*:\\s*(?!['\"`]?var\\()['\"`]?\\d/g,\n message:\n 'Raio fixo. Use um token de raio por papel (rounded-control, rounded-surface, rounded-item...) ou uma variável de tema.',\n skipLine: isCommentLine,\n },\n]\n\n// Degraus fora da escala não geram CSS (o Tailwind ignora em silêncio e o layout quebra).\nconst ALLOWED_STEPS = new Set(['0', '1', '2', '3', '4', '6', '8', '12', '16', '24'])\nconst STEP_CLASS =\n /(?<![\\w-])-?(?:p[xytrbl]?|m[xytrbl]?|gap(?:-[xy])?|w|h|size|min-[wh]|max-[wh]|inset(?:-[xy])?|top|left|right|bottom|space-[xy]|translate-[xy]|scroll-[mp][xytrbl]?)-(\\d+(?:\\.\\d+)?)(?![\\d/.\\w-])/g\n\n/** Sétima regra (\"fora-da-escala\"): olha todos os degraus da linha, não só o primeiro. */\nfunction checkForaDaEscala(line: string): boolean {\n for (const m of line.matchAll(STEP_CLASS)) if (!ALLOWED_STEPS.has(m[1]!)) return true\n return false\n}\n\n/**\n * Varre o texto de um arquivo (linha a linha) contra as sete regras genéricas de\n * DESIGN_RULES.md. Pura: não lê arquivo, não decide o que é escopo do projeto (isso é de quem\n * chama, como `auditar` abaixo ou o `walk`/`ALLOWED_DIRS` de check-design-rules.mjs).\n */\nexport function auditLines(text: string): AuditViolation[] {\n const found: AuditViolation[] = []\n const lines = text.split('\\n')\n lines.forEach((line, i) => {\n for (const rule of LINE_RULES) {\n if (rule.skipLine?.(line)) continue\n rule.test.lastIndex = 0\n if (rule.test.test(line)) {\n found.push({ line: i + 1, rule: rule.id, message: rule.message, source: line.trim() })\n }\n }\n if (!isCommentLine(line) && checkForaDaEscala(line)) {\n found.push({\n line: i + 1,\n rule: 'fora-da-escala',\n message:\n 'Degrau fora da escala (permitidos: 0, 1, 2, 3, 4, 6, 8, 12, 16, 24). Use a escala ou um token nomeado.',\n source: line.trim(),\n })\n }\n })\n return found\n}\n\nconst SKIP_DIR_NAMES = new Set(['node_modules', 'dist', 'build', '.git', '.storybook-static'])\n\nfunction walk(dir: string): string[] {\n return readdirSync(dir).flatMap((name) => {\n if (SKIP_DIR_NAMES.has(name)) return []\n const full = join(dir, name)\n return statSync(full).isDirectory() ? walk(full) : [full]\n })\n}\n\n/**\n * Varre `cwd` (ou `cwd/src`, quando existir: a convenção deste boilerplate e da maioria dos\n * projetos React) por arquivos `.ts`/`.tsx`/`.js`/`.jsx`/`.css`, ignorando teste e story, e\n * devolve toda violação das sete regras genéricas. É o que `rendra auditar [cwd]` chama; não\n * imprime nada e não lança (a CLI decide o formato de saída e o código de saída).\n */\nexport function auditar(cwd: string): FileAuditViolation[] {\n const root = existsSync(join(cwd, 'src')) ? join(cwd, 'src') : cwd\n if (!existsSync(root)) return []\n const files = walk(root).filter(\n (f) => /\\.(tsx?|jsx?|css)$/.test(f) && !f.includes('.test.') && !f.includes('.stories.'),\n )\n const problems: FileAuditViolation[] = []\n for (const file of files) {\n const rel = relative(cwd, file).split(sep).join('/')\n const text = readFileSync(file, 'utf8')\n for (const violation of auditLines(text)) problems.push({ file: rel, ...violation })\n }\n return problems.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line)\n}\n"],"mappings":";;;;AAuCA,IAAM,iBAAiB,SAAiB,qBAAqB,KAAK,IAAI;;AAGtE,IAAa,aAAyB;CACpC;EACE,IAAI;EACJ,MAAM;EACN,SAAS;EACT,WAAW,SAAS,cAAc,IAAI,KAAK,SAAS,KAAK,IAAI;CAC/D;CACA;EACE,IAAI;EAGJ,MAAM;EACN,SAAS;EACT,UAAU;CACZ;CACA;EACE,IAAI;EAEJ,MAAM;EACN,SAAS;CACX;CACA;EACE,IAAI;EAGJ,MAAM;EACN,SACE;CACJ;CACA;EACE,IAAI;EACJ,MAAM;EACN,SAAS;CACX;CACA;EACE,IAAI;EAIJ,MAAM;EACN,SACE;EACF,UAAU;CACZ;AACF;AAGA,IAAM,gCAAgB,IAAI,IAAI;CAAC;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAM;CAAM;AAAI,CAAC;AACnF,IAAM,aACJ;;AAGF,SAAS,kBAAkB,MAAuB;CAChD,KAAK,MAAM,KAAK,KAAK,SAAS,UAAU,GAAG,IAAI,CAAC,cAAc,IAAI,EAAE,EAAG,GAAG,OAAO;CACjF,OAAO;AACT;;;;;;AAOA,SAAgB,WAAW,MAAgC;CACzD,MAAM,QAA0B,CAAC;CAEjC,KADmB,MAAM,IACzB,CAAA,CAAM,SAAS,MAAM,MAAM;EACzB,KAAK,MAAM,QAAQ,YAAY;GAC7B,IAAI,KAAK,WAAW,IAAI,GAAG;GAC3B,KAAK,KAAK,YAAY;GACtB,IAAI,KAAK,KAAK,KAAK,IAAI,GACrB,MAAM,KAAK;IAAE,MAAM,IAAI;IAAG,MAAM,KAAK;IAAI,SAAS,KAAK;IAAS,QAAQ,KAAK,KAAK;GAAE,CAAC;EAEzF;EACA,IAAI,CAAC,cAAc,IAAI,KAAK,kBAAkB,IAAI,GAChD,MAAM,KAAK;GACT,MAAM,IAAI;GACV,MAAM;GACN,SACE;GACF,QAAQ,KAAK,KAAK;EACpB,CAAC;CAEL,CAAC;CACD,OAAO;AACT;AAEA,IAAM,iCAAiB,IAAI,IAAI;CAAC;CAAgB;CAAQ;CAAS;CAAQ;AAAmB,CAAC;AAE7F,SAAS,KAAK,KAAuB;CACnC,OAAO,YAAY,GAAG,CAAC,CAAC,SAAS,SAAS;EACxC,IAAI,eAAe,IAAI,IAAI,GAAG,OAAO,CAAC;EACtC,MAAM,OAAO,KAAK,KAAK,IAAI;EAC3B,OAAO,SAAS,IAAI,CAAC,CAAC,YAAY,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI;CAC1D,CAAC;AACH;;;;;;;AAQA,SAAgB,QAAQ,KAAmC;CACzD,MAAM,OAAO,WAAW,KAAK,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,KAAK,IAAI;CAC/D,IAAI,CAAC,WAAW,IAAI,GAAG,OAAO,CAAC;CAC/B,MAAM,QAAQ,KAAK,IAAI,CAAC,CAAC,QACtB,MAAM,qBAAqB,KAAK,CAAC,KAAK,CAAC,EAAE,SAAS,QAAQ,KAAK,CAAC,EAAE,SAAS,WAAW,CACzF;CACA,MAAM,WAAiC,CAAC;CACxC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,MAAM,SAAS,KAAK,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG;EACnD,MAAM,OAAO,aAAa,MAAM,MAAM;EACtC,KAAK,MAAM,aAAa,WAAW,IAAI,GAAG,SAAS,KAAK;GAAE,MAAM;GAAK,GAAG;EAAU,CAAC;CACrF;CACA,OAAO,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,KAAK,EAAE,OAAO,EAAE,IAAI;AAChF"}
@@ -0,0 +1,15 @@
1
+ /*! Rendra Design System v2.1.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
+ import { t as CATALOG } from "../components-Cm2ezlPT.js";
3
+ //#region src/cli/codigos.ts
4
+ /** Todo o catálogo, na ordem cadastrada. Sem duplicata: garantido por assertCatalogIntegrity. */
5
+ function codigos() {
6
+ return CATALOG;
7
+ }
8
+ /** Uma linha por código, no formato que a CLI imprime: "CODE Nome (Componente)". */
9
+ function formatCodigos(entries = codigos()) {
10
+ return entries.map((entry) => `${entry.code} ${entry.name} (${entry.component})`).join("\n");
11
+ }
12
+ //#endregion
13
+ export { codigos, formatCodigos };
14
+
15
+ //# sourceMappingURL=codigos.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codigos.js","names":[],"sources":["../../src/cli/codigos.ts"],"sourcesContent":["/*\n * `rendra codigos`: lista o catálogo de códigos de componente (src/catalog/components.ts), o\n * mesmo catálogo da vitrine (/componentes) e do BRIEFING_MODELO. Função pura: devolve os dados;\n * quem chama (bin/rendra.mjs) decide o formato de impressão.\n */\nimport type { ComponentCatalogEntry } from '../catalog/components'\nimport { CATALOG } from '../catalog/components'\n\n/** Todo o catálogo, na ordem cadastrada. Sem duplicata: garantido por assertCatalogIntegrity. */\nexport function codigos(): ComponentCatalogEntry[] {\n return CATALOG\n}\n\n/** Uma linha por código, no formato que a CLI imprime: \"CODE Nome (Componente)\". */\nexport function formatCodigos(entries: ComponentCatalogEntry[] = codigos()): string {\n return entries.map((entry) => `${entry.code} ${entry.name} (${entry.component})`).join('\\n')\n}\n"],"mappings":";;;;AASA,SAAgB,UAAmC;CACjD,OAAO;AACT;;AAGA,SAAgB,cAAc,UAAmC,QAAQ,GAAW;CAClF,OAAO,QAAQ,KAAK,UAAU,GAAG,MAAM,KAAK,IAAI,MAAM,KAAK,IAAI,MAAM,UAAU,EAAE,CAAC,CAAC,KAAK,IAAI;AAC9F"}
@@ -0,0 +1,17 @@
1
+ /*! Rendra Design System v2.1.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
+ //#region src/cli/help.ts
3
+ /** Linha final de autoria, igual em toda saída publicável (CLI, banner do pacote). */
4
+ var AUTHOR_LINE = "Rendra Design System, por Bruno Magalhaes: brunomagalhaes.me · instagram.com/brunomagalhaes.me";
5
+ function formatHelp() {
6
+ return `Uso: rendra <codigos|auditar|trocar> ...
7
+
8
+ rendra codigos lista o catálogo de códigos de componente
9
+ rendra auditar [pasta] regras genéricas de DESIGN_RULES.md (padrão: pasta atual)
10
+ rendra trocar <DE> <PARA> [--dry-run] troca a variante DE pela PARA no projeto
11
+
12
+ ${AUTHOR_LINE}`;
13
+ }
14
+ //#endregion
15
+ export { AUTHOR_LINE, formatHelp };
16
+
17
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.js","names":[],"sources":["../../src/cli/help.ts"],"sourcesContent":["/*\n * `rendra` sem argumento ou `rendra --help`: texto de uso dos três subcomandos, terminado\n * pela linha de autoria (decisão do autor: todo o publicável leva essa assinatura, igual ao\n * banner do CSS/JS em scripts/lib/pkg-banner.ts). Função pura: bin/rendra.mjs decide quando\n * chamar e onde imprimir.\n */\n\n/** Linha final de autoria, igual em toda saída publicável (CLI, banner do pacote). */\nexport const AUTHOR_LINE =\n 'Rendra Design System, por Bruno Magalhaes: brunomagalhaes.me · instagram.com/brunomagalhaes.me'\n\nexport function formatHelp(): string {\n return `Uso: rendra <codigos|auditar|trocar> ...\n\n rendra codigos lista o catálogo de códigos de componente\n rendra auditar [pasta] regras genéricas de DESIGN_RULES.md (padrão: pasta atual)\n rendra trocar <DE> <PARA> [--dry-run] troca a variante DE pela PARA no projeto\n\n${AUTHOR_LINE}`\n}\n"],"mappings":";;;AAQA,IAAa,cACX;AAEF,SAAgB,aAAqB;CACnC,OAAO;;;;;;EAMP;AACF"}
@@ -0,0 +1,242 @@
1
+ /*! Rendra Design System v2.1.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
+ import { f as getCatalogEntry } from "../components-Cm2ezlPT.js";
3
+ import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
4
+ import { join, relative, sep } from "node:path";
5
+ //#region src/cli/trocar.ts
6
+ var SKIP_DIR_NAMES = /* @__PURE__ */ new Set([
7
+ "node_modules",
8
+ "dist",
9
+ "build",
10
+ ".git",
11
+ ".storybook-static"
12
+ ]);
13
+ function walk(dir) {
14
+ return readdirSync(dir).flatMap((name) => {
15
+ if (SKIP_DIR_NAMES.has(name)) return [];
16
+ const full = join(dir, name);
17
+ return statSync(full).isDirectory() ? walk(full) : [full];
18
+ });
19
+ }
20
+ /** Arquivos onde JSX pode aparecer (.tsx/.jsx), fora de teste e story. */
21
+ function listProjectFiles(cwd) {
22
+ const root = existsSync(join(cwd, "src")) ? join(cwd, "src") : cwd;
23
+ if (!existsSync(root)) return [];
24
+ return walk(root).filter((f) => /\.(tsx|jsx)$/.test(f) && !f.includes(".test.") && !f.includes(".stories."));
25
+ }
26
+ function lineOf(sourceFile, pos) {
27
+ return sourceFile.getLineAndCharacterOfPosition(pos).line + 1;
28
+ }
29
+ function firstLine(text) {
30
+ return text.split("\n")[0].trim();
31
+ }
32
+ /** Todo elemento JSX cuja tag é exatamente `componentName` (nunca um prefixo, ex.: FieldGroup por Field). */
33
+ function findElements(ts, sourceFile, componentName) {
34
+ const found = [];
35
+ const visit = (node) => {
36
+ if ((ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) && node.tagName.getText(sourceFile) === componentName) found.push(node);
37
+ ts.forEachChild(node, visit);
38
+ };
39
+ visit(sourceFile);
40
+ return found;
41
+ }
42
+ /**
43
+ * Valor de um atributo JSX (`name`) do elemento: ausente, presente sem valor (shorthand,
44
+ * `true` implícito), literal (`attr="x"` ou `attr={"x"}`/`attr={true}`/`attr={7}`) ou dinâmico
45
+ * (qualquer outra expressão dentro de `{...}`, ex.: identificador, chamada, condicional).
46
+ * `attrNode` (o atributo inteiro, não só o valor) vai junto sempre que a prop existe de
47
+ * verdade no elemento: é o que uma troca para a variante padrão remove por completo.
48
+ */
49
+ function readAttrLiteral(ts, attributes, name) {
50
+ const attr = attributes.properties.find((p) => ts.isJsxAttribute(p) && p.name.getText() === name);
51
+ if (!attr) return { kind: "absent" };
52
+ if (!attr.initializer) return {
53
+ kind: "shorthand",
54
+ attrNode: attr
55
+ };
56
+ if (ts.isStringLiteral(attr.initializer)) return {
57
+ kind: "literal",
58
+ value: attr.initializer.text,
59
+ node: attr.initializer,
60
+ attrNode: attr
61
+ };
62
+ if (ts.isJsxExpression(attr.initializer) && attr.initializer.expression) {
63
+ const expr = attr.initializer.expression;
64
+ if (ts.isStringLiteralLike(expr)) return {
65
+ kind: "literal",
66
+ value: expr.text,
67
+ node: expr,
68
+ attrNode: attr
69
+ };
70
+ if (expr.kind === ts.SyntaxKind.TrueKeyword) return {
71
+ kind: "literal",
72
+ value: true,
73
+ node: expr,
74
+ attrNode: attr
75
+ };
76
+ if (expr.kind === ts.SyntaxKind.FalseKeyword) return {
77
+ kind: "literal",
78
+ value: false,
79
+ node: expr,
80
+ attrNode: attr
81
+ };
82
+ if (ts.isNumericLiteral(expr)) return {
83
+ kind: "literal",
84
+ value: Number(expr.text),
85
+ node: expr,
86
+ attrNode: attr
87
+ };
88
+ return { kind: "dynamic" };
89
+ }
90
+ return { kind: "dynamic" };
91
+ }
92
+ /**
93
+ * O elemento usa a variante DE? Todo `variantProps` bate (ou, se `deEntry.isDefault`, a prop
94
+ * pode estar simplesmente ausente: o elemento sem a prop já é a variante padrão), e nenhum é
95
+ * dinâmico.
96
+ */
97
+ function classifyElement(ts, opening, deEntry) {
98
+ const edits = [];
99
+ for (const [key, expected] of Object.entries(deEntry.variantProps)) {
100
+ const found = readAttrLiteral(ts, opening.attributes, key);
101
+ if (found.kind === "dynamic") return { status: "dynamic" };
102
+ if (found.kind === "absent") {
103
+ if (!deEntry.isDefault) return { status: "no-match" };
104
+ edits.push({ key });
105
+ continue;
106
+ }
107
+ if (found.kind === "shorthand") {
108
+ if (expected !== true) return { status: "no-match" };
109
+ continue;
110
+ }
111
+ if (found.value !== expected) return { status: "no-match" };
112
+ edits.push({
113
+ key,
114
+ node: found.node,
115
+ attrNode: found.attrNode
116
+ });
117
+ }
118
+ return {
119
+ status: "match",
120
+ edits
121
+ };
122
+ }
123
+ /**
124
+ * O trecho a remover para apagar `attrNode` por completo, inclusive o espaço (ou quebra de
125
+ * linha) logo antes dele: sem isso, remover só o atributo deixaria um espaço a mais entre a
126
+ * tag anterior e a próxima prop (ou o `>`/`/>` de fechamento).
127
+ */
128
+ function attributeRemovalSpan(sourceFile, attrNode) {
129
+ let start = attrNode.getStart(sourceFile);
130
+ while (start > 0 && /\s/.test(sourceFile.text[start - 1])) start--;
131
+ return {
132
+ start,
133
+ end: attrNode.getEnd()
134
+ };
135
+ }
136
+ /** Aplica os cortes de trás para frente, para os índices anteriores continuarem válidos. */
137
+ function applyEdits(text, edits) {
138
+ const sorted = [...edits].sort((a, b) => b.start - a.start);
139
+ let out = text;
140
+ for (const edit of sorted) out = out.slice(0, edit.start) + edit.replacement + out.slice(edit.end);
141
+ return out;
142
+ }
143
+ /**
144
+ * Troca a variante DE pela PARA em todo o projeto (`cwd`, ou `cwd/src` quando existir).
145
+ * `dryRun` calcula os mesmos `reescritos` sem gravar nada em disco (usado para conferir o
146
+ * resultado antes de aplicar de verdade).
147
+ */
148
+ function trocar(options) {
149
+ const { ts, cwd, de, para, dryRun = false } = options;
150
+ const deEntry = getCatalogEntry(de);
151
+ const paraEntry = getCatalogEntry(para);
152
+ if (!deEntry) throw new Error(`Código "${de}" não existe no catálogo de componentes.`);
153
+ if (!paraEntry) throw new Error(`Código "${para}" não existe no catálogo de componentes.`);
154
+ const sameComponent = deEntry.component === paraEntry.component;
155
+ const variantKeys = Object.keys(deEntry.variantProps);
156
+ if (sameComponent && variantKeys.length === 0) throw new Error(`"${de}" e "${para}" são o mesmo componente (${deEntry.component}) sem prop que os distinga; não dá para saber qual elemento reescrever.`);
157
+ const reescritos = [];
158
+ const paraRevisao = [];
159
+ for (const file of listProjectFiles(cwd)) {
160
+ const text = readFileSync(file, "utf8");
161
+ if (!text.includes(deEntry.component)) continue;
162
+ const rel = relative(cwd, file).split(sep).join("/");
163
+ const scriptKind = file.endsWith(".jsx") ? ts.ScriptKind.JSX : ts.ScriptKind.TSX;
164
+ const sourceFile = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, scriptKind);
165
+ const elements = findElements(ts, sourceFile, deEntry.component);
166
+ if (elements.length === 0) continue;
167
+ const fileEdits = [];
168
+ for (const opening of elements) {
169
+ const line = lineOf(sourceFile, opening.getStart(sourceFile));
170
+ const source = firstLine(opening.getText(sourceFile));
171
+ if (!sameComponent) {
172
+ paraRevisao.push({
173
+ file: rel,
174
+ line,
175
+ motivo: "componentes-diferentes",
176
+ source
177
+ });
178
+ continue;
179
+ }
180
+ const classification = classifyElement(ts, opening, deEntry);
181
+ if (classification.status === "dynamic") {
182
+ paraRevisao.push({
183
+ file: rel,
184
+ line,
185
+ motivo: "prop-dinamica",
186
+ source
187
+ });
188
+ continue;
189
+ }
190
+ if (classification.status === "no-match") continue;
191
+ let editedAny = false;
192
+ for (const edit of classification.edits) {
193
+ if (paraEntry.isDefault) {
194
+ if (edit.attrNode) {
195
+ const span = attributeRemovalSpan(sourceFile, edit.attrNode);
196
+ fileEdits.push({
197
+ start: span.start,
198
+ end: span.end,
199
+ replacement: ""
200
+ });
201
+ editedAny = true;
202
+ }
203
+ continue;
204
+ }
205
+ const novo = paraEntry.variantProps[edit.key];
206
+ if (typeof novo !== "string") continue;
207
+ if (!edit.node) {
208
+ const insertPos = opening.tagName.getEnd();
209
+ fileEdits.push({
210
+ start: insertPos,
211
+ end: insertPos,
212
+ replacement: ` ${edit.key}="${novo}"`
213
+ });
214
+ } else {
215
+ const start = edit.node.getStart(sourceFile);
216
+ const end = edit.node.getEnd();
217
+ const original = sourceFile.text.slice(start, end);
218
+ const quote = original[0] === "\"" || original[0] === "'" ? original[0] : "\"";
219
+ fileEdits.push({
220
+ start,
221
+ end,
222
+ replacement: `${quote}${novo}${quote}`
223
+ });
224
+ }
225
+ editedAny = true;
226
+ }
227
+ if (editedAny) reescritos.push({
228
+ file: rel,
229
+ line
230
+ });
231
+ }
232
+ if (fileEdits.length > 0 && !dryRun) writeFileSync(file, applyEdits(text, fileEdits));
233
+ }
234
+ return {
235
+ reescritos,
236
+ paraRevisao
237
+ };
238
+ }
239
+ //#endregion
240
+ export { trocar };
241
+
242
+ //# sourceMappingURL=trocar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trocar.js","names":[],"sources":["../../src/cli/trocar.ts"],"sourcesContent":["/*\n * `rendra trocar <DE> <PARA> [--dry-run]`: troca todo elemento JSX que usa a variante do\n * código DE (catálogo de src/catalog/components.ts) pela variante PARA, reescrevendo só a\n * prop literal que os distingue, por fatia de texto (preserva formatação, comentário,\n * indentação). Prop dinâmica (variant={x}) nunca é reescrita, só listada para revisão manual;\n * troca entre códigos de componentes diferentes também nunca edita, só lista.\n *\n * O parser é o TypeScript (`ts.createSourceFile`/`ts.forEachChild`), mas este módulo nunca o\n * importa em tempo de execução: recebe a instância já carregada como `options.ts` (só o TIPO\n * vem de \"typescript\", apagado na compilação). Quem resolve a instância de verdade, a partir\n * do projeto do usuário, é src/cli/typescript-loader.ts; sem ela, este módulo nunca é chamado.\n */\nimport { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs'\nimport { join, relative, sep } from 'node:path'\nimport type * as TSNamespace from 'typescript'\nimport type { ComponentCatalogEntry } from '../catalog/components'\nimport { getCatalogEntry } from '../catalog/components'\n\nexport type TypeScriptModule = typeof TSNamespace\n\nexport interface TrocarReescrito {\n file: string\n line: number\n}\n\nexport interface TrocarParaRevisao {\n file: string\n line: number\n motivo: 'prop-dinamica' | 'componentes-diferentes'\n source: string\n}\n\nexport interface TrocarResultado {\n reescritos: TrocarReescrito[]\n paraRevisao: TrocarParaRevisao[]\n}\n\nexport interface TrocarOptions {\n ts: TypeScriptModule\n cwd: string\n de: string\n para: string\n dryRun?: boolean\n}\n\nconst SKIP_DIR_NAMES = new Set(['node_modules', 'dist', 'build', '.git', '.storybook-static'])\n\nfunction walk(dir: string): string[] {\n return readdirSync(dir).flatMap((name) => {\n if (SKIP_DIR_NAMES.has(name)) return []\n const full = join(dir, name)\n return statSync(full).isDirectory() ? walk(full) : [full]\n })\n}\n\n/** Arquivos onde JSX pode aparecer (.tsx/.jsx), fora de teste e story. */\nfunction listProjectFiles(cwd: string): string[] {\n const root = existsSync(join(cwd, 'src')) ? join(cwd, 'src') : cwd\n if (!existsSync(root)) return []\n return walk(root).filter(\n (f) => /\\.(tsx|jsx)$/.test(f) && !f.includes('.test.') && !f.includes('.stories.'),\n )\n}\n\nfunction lineOf(sourceFile: TSNamespace.SourceFile, pos: number): number {\n return sourceFile.getLineAndCharacterOfPosition(pos).line + 1\n}\n\nfunction firstLine(text: string): string {\n return text.split('\\n')[0]!.trim()\n}\n\ntype OpeningLike = TSNamespace.JsxOpeningElement | TSNamespace.JsxSelfClosingElement\n\n/** Todo elemento JSX cuja tag é exatamente `componentName` (nunca um prefixo, ex.: FieldGroup por Field). */\nfunction findElements(\n ts: TypeScriptModule,\n sourceFile: TSNamespace.SourceFile,\n componentName: string,\n): OpeningLike[] {\n const found: OpeningLike[] = []\n const visit = (node: TSNamespace.Node) => {\n if (\n (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) &&\n node.tagName.getText(sourceFile) === componentName\n ) {\n found.push(node)\n }\n ts.forEachChild(node, visit)\n }\n visit(sourceFile)\n return found\n}\n\ntype AttrLiteral =\n | { kind: 'absent' }\n | { kind: 'shorthand'; attrNode: TSNamespace.JsxAttribute }\n | {\n kind: 'literal'\n value: string | number | boolean\n node: TSNamespace.Node\n attrNode: TSNamespace.JsxAttribute\n }\n | { kind: 'dynamic' }\n\n/**\n * Valor de um atributo JSX (`name`) do elemento: ausente, presente sem valor (shorthand,\n * `true` implícito), literal (`attr=\"x\"` ou `attr={\"x\"}`/`attr={true}`/`attr={7}`) ou dinâmico\n * (qualquer outra expressão dentro de `{...}`, ex.: identificador, chamada, condicional).\n * `attrNode` (o atributo inteiro, não só o valor) vai junto sempre que a prop existe de\n * verdade no elemento: é o que uma troca para a variante padrão remove por completo.\n */\nfunction readAttrLiteral(\n ts: TypeScriptModule,\n attributes: TSNamespace.JsxAttributes,\n name: string,\n): AttrLiteral {\n const attr = attributes.properties.find(\n (p): p is TSNamespace.JsxAttribute => ts.isJsxAttribute(p) && p.name.getText() === name,\n )\n if (!attr) return { kind: 'absent' }\n if (!attr.initializer) return { kind: 'shorthand', attrNode: attr }\n if (ts.isStringLiteral(attr.initializer)) {\n return { kind: 'literal', value: attr.initializer.text, node: attr.initializer, attrNode: attr }\n }\n if (ts.isJsxExpression(attr.initializer) && attr.initializer.expression) {\n const expr = attr.initializer.expression\n if (ts.isStringLiteralLike(expr)) {\n return { kind: 'literal', value: expr.text, node: expr, attrNode: attr }\n }\n if (expr.kind === ts.SyntaxKind.TrueKeyword) {\n return { kind: 'literal', value: true, node: expr, attrNode: attr }\n }\n if (expr.kind === ts.SyntaxKind.FalseKeyword) {\n return { kind: 'literal', value: false, node: expr, attrNode: attr }\n }\n if (ts.isNumericLiteral(expr)) {\n return { kind: 'literal', value: Number(expr.text), node: expr, attrNode: attr }\n }\n return { kind: 'dynamic' }\n }\n return { kind: 'dynamic' }\n}\n\n/**\n * Uma prop a tratar depois de confirmado que o elemento usa a variante DE: `attrNode`\n * ausente quer dizer que a prop nem existia no elemento (só possível quando DE é a variante\n * padrão do componente, `isDefault`). Trocar para uma PARA que não é padrão precisa inserir\n * a prop; trocar para uma PARA que também é padrão não precisa fazer nada.\n */\ninterface PropEdit {\n key: string\n node?: TSNamespace.Node\n attrNode?: TSNamespace.JsxAttribute\n}\n\ntype Classification =\n { status: 'dynamic' } | { status: 'no-match' } | { status: 'match'; edits: PropEdit[] }\n\n/**\n * O elemento usa a variante DE? Todo `variantProps` bate (ou, se `deEntry.isDefault`, a prop\n * pode estar simplesmente ausente: o elemento sem a prop já é a variante padrão), e nenhum é\n * dinâmico.\n */\nfunction classifyElement(\n ts: TypeScriptModule,\n opening: OpeningLike,\n deEntry: ComponentCatalogEntry,\n): Classification {\n const edits: PropEdit[] = []\n for (const [key, expected] of Object.entries(deEntry.variantProps)) {\n const found = readAttrLiteral(ts, opening.attributes, key)\n if (found.kind === 'dynamic') return { status: 'dynamic' }\n if (found.kind === 'absent') {\n if (!deEntry.isDefault) return { status: 'no-match' }\n edits.push({ key }) // Sem attrNode: a prop precisa ser inserida (ou nada, se PARA também for padrão).\n continue\n }\n if (found.kind === 'shorthand') {\n if (expected !== true) return { status: 'no-match' }\n continue // Shorthand (true implícito) já é o valor esperado: nada a reescrever nesta prop.\n }\n if (found.value !== expected) return { status: 'no-match' }\n edits.push({ key, node: found.node, attrNode: found.attrNode })\n }\n return { status: 'match', edits }\n}\n\n/**\n * O trecho a remover para apagar `attrNode` por completo, inclusive o espaço (ou quebra de\n * linha) logo antes dele: sem isso, remover só o atributo deixaria um espaço a mais entre a\n * tag anterior e a próxima prop (ou o `>`/`/>` de fechamento).\n */\nfunction attributeRemovalSpan(\n sourceFile: TSNamespace.SourceFile,\n attrNode: TSNamespace.JsxAttribute,\n): { start: number; end: number } {\n let start = attrNode.getStart(sourceFile)\n while (start > 0 && /\\s/.test(sourceFile.text[start - 1]!)) start--\n return { start, end: attrNode.getEnd() }\n}\n\ninterface TextEdit {\n start: number\n end: number\n replacement: string\n}\n\n/** Aplica os cortes de trás para frente, para os índices anteriores continuarem válidos. */\nfunction applyEdits(text: string, edits: TextEdit[]): string {\n const sorted = [...edits].sort((a, b) => b.start - a.start)\n let out = text\n for (const edit of sorted) out = out.slice(0, edit.start) + edit.replacement + out.slice(edit.end)\n return out\n}\n\n/**\n * Troca a variante DE pela PARA em todo o projeto (`cwd`, ou `cwd/src` quando existir).\n * `dryRun` calcula os mesmos `reescritos` sem gravar nada em disco (usado para conferir o\n * resultado antes de aplicar de verdade).\n */\nexport function trocar(options: TrocarOptions): TrocarResultado {\n const { ts, cwd, de, para, dryRun = false } = options\n const deEntry = getCatalogEntry(de)\n const paraEntry = getCatalogEntry(para)\n if (!deEntry) throw new Error(`Código \"${de}\" não existe no catálogo de componentes.`)\n if (!paraEntry) throw new Error(`Código \"${para}\" não existe no catálogo de componentes.`)\n\n const sameComponent = deEntry.component === paraEntry.component\n const variantKeys = Object.keys(deEntry.variantProps)\n if (sameComponent && variantKeys.length === 0) {\n throw new Error(\n `\"${de}\" e \"${para}\" são o mesmo componente (${deEntry.component}) sem prop que os ` +\n 'distinga; não dá para saber qual elemento reescrever.',\n )\n }\n\n const reescritos: TrocarReescrito[] = []\n const paraRevisao: TrocarParaRevisao[] = []\n\n for (const file of listProjectFiles(cwd)) {\n const text = readFileSync(file, 'utf8')\n if (!text.includes(deEntry.component)) continue\n const rel = relative(cwd, file).split(sep).join('/')\n const scriptKind = file.endsWith('.jsx') ? ts.ScriptKind.JSX : ts.ScriptKind.TSX\n const sourceFile = ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true, scriptKind)\n const elements = findElements(ts, sourceFile, deEntry.component)\n if (elements.length === 0) continue\n\n const fileEdits: TextEdit[] = []\n for (const opening of elements) {\n const line = lineOf(sourceFile, opening.getStart(sourceFile))\n const source = firstLine(opening.getText(sourceFile))\n\n if (!sameComponent) {\n paraRevisao.push({ file: rel, line, motivo: 'componentes-diferentes', source })\n continue\n }\n\n const classification = classifyElement(ts, opening, deEntry)\n if (classification.status === 'dynamic') {\n paraRevisao.push({ file: rel, line, motivo: 'prop-dinamica', source })\n continue\n }\n if (classification.status === 'no-match') continue\n\n let editedAny = false\n for (const edit of classification.edits) {\n // PARA é a variante padrão do componente: a prop sai por completo (documentado no\n // CHANGELOG e no README), nunca grava o valor padrão por extenso. Sem attrNode (a\n // prop já não existia, DE também era padrão), não há nada a remover: o elemento já\n // está no formato de PARA.\n if (paraEntry.isDefault) {\n if (edit.attrNode) {\n const span = attributeRemovalSpan(sourceFile, edit.attrNode)\n fileEdits.push({ start: span.start, end: span.end, replacement: '' })\n editedAny = true\n }\n continue\n }\n\n const novo = paraEntry.variantProps[edit.key]\n // Só reescreve prop de valor string (variant, type...): as únicas usadas para\n // distinguir variante no catálogo hoje. Boolean/número ficam fora deste lote.\n if (typeof novo !== 'string') continue\n\n if (!edit.node) {\n // DE era a variante padrão (a prop não existia): insere a prop de PARA logo depois\n // do nome da tag, antes de qualquer outro atributo existente.\n const insertPos = opening.tagName.getEnd()\n fileEdits.push({\n start: insertPos,\n end: insertPos,\n replacement: ` ${edit.key}=\"${novo}\"`,\n })\n } else {\n const start = edit.node.getStart(sourceFile)\n const end = edit.node.getEnd()\n const original = sourceFile.text.slice(start, end)\n const quote = original[0] === '\"' || original[0] === \"'\" ? original[0] : '\"'\n fileEdits.push({ start, end, replacement: `${quote}${novo}${quote}` })\n }\n editedAny = true\n }\n if (editedAny) reescritos.push({ file: rel, line })\n }\n\n if (fileEdits.length > 0 && !dryRun) {\n writeFileSync(file, applyEdits(text, fileEdits))\n }\n }\n\n return { reescritos, paraRevisao }\n}\n"],"mappings":";;;;;AA6CA,IAAM,iCAAiB,IAAI,IAAI;CAAC;CAAgB;CAAQ;CAAS;CAAQ;AAAmB,CAAC;AAE7F,SAAS,KAAK,KAAuB;CACnC,OAAO,YAAY,GAAG,CAAC,CAAC,SAAS,SAAS;EACxC,IAAI,eAAe,IAAI,IAAI,GAAG,OAAO,CAAC;EACtC,MAAM,OAAO,KAAK,KAAK,IAAI;EAC3B,OAAO,SAAS,IAAI,CAAC,CAAC,YAAY,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI;CAC1D,CAAC;AACH;;AAGA,SAAS,iBAAiB,KAAuB;CAC/C,MAAM,OAAO,WAAW,KAAK,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,KAAK,IAAI;CAC/D,IAAI,CAAC,WAAW,IAAI,GAAG,OAAO,CAAC;CAC/B,OAAO,KAAK,IAAI,CAAC,CAAC,QACf,MAAM,eAAe,KAAK,CAAC,KAAK,CAAC,EAAE,SAAS,QAAQ,KAAK,CAAC,EAAE,SAAS,WAAW,CACnF;AACF;AAEA,SAAS,OAAO,YAAoC,KAAqB;CACvE,OAAO,WAAW,8BAA8B,GAAG,CAAC,CAAC,OAAO;AAC9D;AAEA,SAAS,UAAU,MAAsB;CACvC,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC,EAAE,CAAE,KAAK;AACnC;;AAKA,SAAS,aACP,IACA,YACA,eACe;CACf,MAAM,QAAuB,CAAC;CAC9B,MAAM,SAAS,SAA2B;EACxC,KACG,GAAG,oBAAoB,IAAI,KAAK,GAAG,wBAAwB,IAAI,MAChE,KAAK,QAAQ,QAAQ,UAAU,MAAM,eAErC,MAAM,KAAK,IAAI;EAEjB,GAAG,aAAa,MAAM,KAAK;CAC7B;CACA,MAAM,UAAU;CAChB,OAAO;AACT;;;;;;;;AAoBA,SAAS,gBACP,IACA,YACA,MACa;CACb,MAAM,OAAO,WAAW,WAAW,MAChC,MAAqC,GAAG,eAAe,CAAC,KAAK,EAAE,KAAK,QAAQ,MAAM,IACrF;CACA,IAAI,CAAC,MAAM,OAAO,EAAE,MAAM,SAAS;CACnC,IAAI,CAAC,KAAK,aAAa,OAAO;EAAE,MAAM;EAAa,UAAU;CAAK;CAClE,IAAI,GAAG,gBAAgB,KAAK,WAAW,GACrC,OAAO;EAAE,MAAM;EAAW,OAAO,KAAK,YAAY;EAAM,MAAM,KAAK;EAAa,UAAU;CAAK;CAEjG,IAAI,GAAG,gBAAgB,KAAK,WAAW,KAAK,KAAK,YAAY,YAAY;EACvE,MAAM,OAAO,KAAK,YAAY;EAC9B,IAAI,GAAG,oBAAoB,IAAI,GAC7B,OAAO;GAAE,MAAM;GAAW,OAAO,KAAK;GAAM,MAAM;GAAM,UAAU;EAAK;EAEzE,IAAI,KAAK,SAAS,GAAG,WAAW,aAC9B,OAAO;GAAE,MAAM;GAAW,OAAO;GAAM,MAAM;GAAM,UAAU;EAAK;EAEpE,IAAI,KAAK,SAAS,GAAG,WAAW,cAC9B,OAAO;GAAE,MAAM;GAAW,OAAO;GAAO,MAAM;GAAM,UAAU;EAAK;EAErE,IAAI,GAAG,iBAAiB,IAAI,GAC1B,OAAO;GAAE,MAAM;GAAW,OAAO,OAAO,KAAK,IAAI;GAAG,MAAM;GAAM,UAAU;EAAK;EAEjF,OAAO,EAAE,MAAM,UAAU;CAC3B;CACA,OAAO,EAAE,MAAM,UAAU;AAC3B;;;;;;AAsBA,SAAS,gBACP,IACA,SACA,SACgB;CAChB,MAAM,QAAoB,CAAC;CAC3B,KAAK,MAAM,CAAC,KAAK,aAAa,OAAO,QAAQ,QAAQ,YAAY,GAAG;EAClE,MAAM,QAAQ,gBAAgB,IAAI,QAAQ,YAAY,GAAG;EACzD,IAAI,MAAM,SAAS,WAAW,OAAO,EAAE,QAAQ,UAAU;EACzD,IAAI,MAAM,SAAS,UAAU;GAC3B,IAAI,CAAC,QAAQ,WAAW,OAAO,EAAE,QAAQ,WAAW;GACpD,MAAM,KAAK,EAAE,IAAI,CAAC;GAClB;EACF;EACA,IAAI,MAAM,SAAS,aAAa;GAC9B,IAAI,aAAa,MAAM,OAAO,EAAE,QAAQ,WAAW;GACnD;EACF;EACA,IAAI,MAAM,UAAU,UAAU,OAAO,EAAE,QAAQ,WAAW;EAC1D,MAAM,KAAK;GAAE;GAAK,MAAM,MAAM;GAAM,UAAU,MAAM;EAAS,CAAC;CAChE;CACA,OAAO;EAAE,QAAQ;EAAS;CAAM;AAClC;;;;;;AAOA,SAAS,qBACP,YACA,UACgC;CAChC,IAAI,QAAQ,SAAS,SAAS,UAAU;CACxC,OAAO,QAAQ,KAAK,KAAK,KAAK,WAAW,KAAK,QAAQ,EAAG,GAAG;CAC5D,OAAO;EAAE;EAAO,KAAK,SAAS,OAAO;CAAE;AACzC;;AASA,SAAS,WAAW,MAAc,OAA2B;CAC3D,MAAM,SAAS,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;CAC1D,IAAI,MAAM;CACV,KAAK,MAAM,QAAQ,QAAQ,MAAM,IAAI,MAAM,GAAG,KAAK,KAAK,IAAI,KAAK,cAAc,IAAI,MAAM,KAAK,GAAG;CACjG,OAAO;AACT;;;;;;AAOA,SAAgB,OAAO,SAAyC;CAC9D,MAAM,EAAE,IAAI,KAAK,IAAI,MAAM,SAAS,UAAU;CAC9C,MAAM,UAAU,gBAAgB,EAAE;CAClC,MAAM,YAAY,gBAAgB,IAAI;CACtC,IAAI,CAAC,SAAS,MAAM,IAAI,MAAM,WAAW,GAAG,yCAAyC;CACrF,IAAI,CAAC,WAAW,MAAM,IAAI,MAAM,WAAW,KAAK,yCAAyC;CAEzF,MAAM,gBAAgB,QAAQ,cAAc,UAAU;CACtD,MAAM,cAAc,OAAO,KAAK,QAAQ,YAAY;CACpD,IAAI,iBAAiB,YAAY,WAAW,GAC1C,MAAM,IAAI,MACR,IAAI,GAAG,OAAO,KAAK,4BAA4B,QAAQ,UAAU,wEAEnE;CAGF,MAAM,aAAgC,CAAC;CACvC,MAAM,cAAmC,CAAC;CAE1C,KAAK,MAAM,QAAQ,iBAAiB,GAAG,GAAG;EACxC,MAAM,OAAO,aAAa,MAAM,MAAM;EACtC,IAAI,CAAC,KAAK,SAAS,QAAQ,SAAS,GAAG;EACvC,MAAM,MAAM,SAAS,KAAK,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG;EACnD,MAAM,aAAa,KAAK,SAAS,MAAM,IAAI,GAAG,WAAW,MAAM,GAAG,WAAW;EAC7E,MAAM,aAAa,GAAG,iBAAiB,MAAM,MAAM,GAAG,aAAa,QAAQ,MAAM,UAAU;EAC3F,MAAM,WAAW,aAAa,IAAI,YAAY,QAAQ,SAAS;EAC/D,IAAI,SAAS,WAAW,GAAG;EAE3B,MAAM,YAAwB,CAAC;EAC/B,KAAK,MAAM,WAAW,UAAU;GAC9B,MAAM,OAAO,OAAO,YAAY,QAAQ,SAAS,UAAU,CAAC;GAC5D,MAAM,SAAS,UAAU,QAAQ,QAAQ,UAAU,CAAC;GAEpD,IAAI,CAAC,eAAe;IAClB,YAAY,KAAK;KAAE,MAAM;KAAK;KAAM,QAAQ;KAA0B;IAAO,CAAC;IAC9E;GACF;GAEA,MAAM,iBAAiB,gBAAgB,IAAI,SAAS,OAAO;GAC3D,IAAI,eAAe,WAAW,WAAW;IACvC,YAAY,KAAK;KAAE,MAAM;KAAK;KAAM,QAAQ;KAAiB;IAAO,CAAC;IACrE;GACF;GACA,IAAI,eAAe,WAAW,YAAY;GAE1C,IAAI,YAAY;GAChB,KAAK,MAAM,QAAQ,eAAe,OAAO;IAKvC,IAAI,UAAU,WAAW;KACvB,IAAI,KAAK,UAAU;MACjB,MAAM,OAAO,qBAAqB,YAAY,KAAK,QAAQ;MAC3D,UAAU,KAAK;OAAE,OAAO,KAAK;OAAO,KAAK,KAAK;OAAK,aAAa;MAAG,CAAC;MACpE,YAAY;KACd;KACA;IACF;IAEA,MAAM,OAAO,UAAU,aAAa,KAAK;IAGzC,IAAI,OAAO,SAAS,UAAU;IAE9B,IAAI,CAAC,KAAK,MAAM;KAGd,MAAM,YAAY,QAAQ,QAAQ,OAAO;KACzC,UAAU,KAAK;MACb,OAAO;MACP,KAAK;MACL,aAAa,IAAI,KAAK,IAAI,IAAI,KAAK;KACrC,CAAC;IACH,OAAO;KACL,MAAM,QAAQ,KAAK,KAAK,SAAS,UAAU;KAC3C,MAAM,MAAM,KAAK,KAAK,OAAO;KAC7B,MAAM,WAAW,WAAW,KAAK,MAAM,OAAO,GAAG;KACjD,MAAM,QAAQ,SAAS,OAAO,QAAO,SAAS,OAAO,MAAM,SAAS,KAAK;KACzE,UAAU,KAAK;MAAE;MAAO;MAAK,aAAa,GAAG,QAAQ,OAAO;KAAQ,CAAC;IACvE;IACA,YAAY;GACd;GACA,IAAI,WAAW,WAAW,KAAK;IAAE,MAAM;IAAK;GAAK,CAAC;EACpD;EAEA,IAAI,UAAU,SAAS,KAAK,CAAC,QAC3B,cAAc,MAAM,WAAW,MAAM,SAAS,CAAC;CAEnD;CAEA,OAAO;EAAE;EAAY;CAAY;AACnC"}
@@ -0,0 +1,27 @@
1
+ /*! Rendra Design System v2.1.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
+ import { join } from "node:path";
3
+ import { createRequire } from "node:module";
4
+ import { pathToFileURL } from "node:url";
5
+ //#region src/cli/typescript-loader.ts
6
+ var TYPESCRIPT_NOT_FOUND_MESSAGE = "Não encontrei o pacote \"typescript\" no projeto. O comando \"rendra trocar\" precisa dele para ler e reescrever o JSX: rode \"npm install --save-dev typescript\" (ou o equivalente do seu gerenciador de pacotes) no projeto de destino e tente de novo.";
7
+ /**
8
+ * Resolve e importa o `typescript` a partir de `cwd` (a árvore node_modules do PROJETO DE
9
+ * DESTINO, nunca a deste repositório): `createRequire` com um nome de arquivo dentro de `cwd`
10
+ * refaz a resolução `require` do Node a partir dali, subindo por node_modules como o Node
11
+ * faria para qualquer import do próprio projeto. Sem o pacote instalado ali (nem em nenhum
12
+ * node_modules acima), devolve `undefined`; quem chama decide a mensagem e o código de saída.
13
+ */
14
+ async function loadTypeScript(cwd) {
15
+ let specifier;
16
+ try {
17
+ specifier = createRequire(join(cwd, "package.json")).resolve("typescript");
18
+ } catch {
19
+ return;
20
+ }
21
+ const mod = await import(pathToFileURL(specifier).href);
22
+ return mod.default ?? mod;
23
+ }
24
+ //#endregion
25
+ export { TYPESCRIPT_NOT_FOUND_MESSAGE, loadTypeScript };
26
+
27
+ //# sourceMappingURL=typescript-loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typescript-loader.js","names":[],"sources":["../../src/cli/typescript-loader.ts"],"sourcesContent":["/*\n * `typescript` não é dependência de execução do pacote (decisão do coordenador, fase 3, Lote\n * B): entra em peerDependencies com peerDependenciesMeta.typescript.optional = true. `rendra\n * trocar` (o único comando que precisa do parser) carrega o `typescript` do PROJETO DO\n * USUÁRIO por import dinâmico, resolvido a partir do `cwd` passado na linha de comando, nunca\n * o `typescript` deste repositório: um projeto de destino sem `typescript` instalado não deve\n * quebrar ao instalar o pacote, só ao rodar `rendra trocar` sem o parser disponível, com uma\n * mensagem clara em português.\n */\nimport { createRequire } from 'node:module'\nimport { join } from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\nexport const TYPESCRIPT_NOT_FOUND_MESSAGE =\n 'Não encontrei o pacote \"typescript\" no projeto. O comando \"rendra trocar\" precisa dele ' +\n 'para ler e reescrever o JSX: rode \"npm install --save-dev typescript\" (ou o equivalente do ' +\n 'seu gerenciador de pacotes) no projeto de destino e tente de novo.'\n\n/**\n * Resolve e importa o `typescript` a partir de `cwd` (a árvore node_modules do PROJETO DE\n * DESTINO, nunca a deste repositório): `createRequire` com um nome de arquivo dentro de `cwd`\n * refaz a resolução `require` do Node a partir dali, subindo por node_modules como o Node\n * faria para qualquer import do próprio projeto. Sem o pacote instalado ali (nem em nenhum\n * node_modules acima), devolve `undefined`; quem chama decide a mensagem e o código de saída.\n */\nexport async function loadTypeScript(\n cwd: string,\n): Promise<typeof import('typescript') | undefined> {\n let specifier: string\n try {\n const requireFromCwd = createRequire(join(cwd, 'package.json'))\n specifier = requireFromCwd.resolve('typescript')\n } catch {\n return undefined\n }\n const mod = (await import(pathToFileURL(specifier).href)) as {\n default?: typeof import('typescript')\n } & typeof import('typescript')\n return (mod.default ?? mod) as typeof import('typescript')\n}\n"],"mappings":";;;;;AAaA,IAAa,+BACX;;;;;;;;AAWF,eAAsB,eACpB,KACkD;CAClD,IAAI;CACJ,IAAI;EAEF,YADuB,cAAc,KAAK,KAAK,cAAc,CACjD,CAAA,CAAe,QAAQ,YAAY;CACjD,QAAQ;EACN;CACF;CACA,MAAM,MAAO,MAAM,OAAO,cAAc,SAAS,CAAC,CAAC;CAGnD,OAAQ,IAAI,WAAW;AACzB"}
@@ -0,0 +1,22 @@
1
+ /*! Rendra Design System v2.1.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
+ import { clsx } from "clsx";
3
+ import { extendTailwindMerge } from "tailwind-merge";
4
+ //#region src/lib/cn.ts
5
+ var twMerge = extendTailwindMerge({ extend: { classGroups: { "font-size": [{ text: [
6
+ "xs",
7
+ "sm",
8
+ "base",
9
+ "lg",
10
+ "xl",
11
+ "2xl",
12
+ "3xl",
13
+ "label",
14
+ "help"
15
+ ] }] } } });
16
+ function cn(...inputs) {
17
+ return twMerge(clsx(inputs));
18
+ }
19
+ //#endregion
20
+ export { cn as t };
21
+
22
+ //# sourceMappingURL=cn-DVPheE99.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cn-DVPheE99.js","names":[],"sources":["../src/lib/cn.ts"],"sourcesContent":["import { clsx, type ClassValue } from 'clsx'\nimport { extendTailwindMerge } from 'tailwind-merge'\n\n// Ensina o tailwind-merge sobre os tokens próprios, para que text-xs e text-primary-text\n// não sejam tratados como conflito, e h-control-md substitua h-12 corretamente. text-label e\n// text-help são tamanho (rótulo e orientação do campo); text-label-foreground é cor.\nconst twMerge = extendTailwindMerge({\n extend: {\n classGroups: {\n 'font-size': [{ text: ['xs', 'sm', 'base', 'lg', 'xl', '2xl', '3xl', 'label', 'help'] }],\n },\n },\n})\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n"],"mappings":";;;;AAMA,IAAM,UAAU,oBAAoB,EAClC,QAAQ,EACN,aAAa,EACX,aAAa,CAAC,EAAE,MAAM;CAAC;CAAM;CAAM;CAAQ;CAAM;CAAM;CAAO;CAAO;CAAS;AAAM,EAAE,CAAC,EACzF,EACF,EACF,CAAC;AAED,SAAgB,GAAG,GAAG,QAAsB;CAC1C,OAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B"}