@promptui-lib/cli 0.1.16 → 0.1.17
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/README.md
CHANGED
|
@@ -180,6 +180,37 @@ export FIGMA_FILE_ID=ABC123xyz
|
|
|
180
180
|
**File ID:**
|
|
181
181
|
Da URL do Figma: `https://www.figma.com/file/ABC123xyz/MeuProjeto` → `ABC123xyz`
|
|
182
182
|
|
|
183
|
+
### Entendendo o fileId vs node-id
|
|
184
|
+
|
|
185
|
+
> ⚠️ **Importante**: O `fileId` é do **arquivo Figma inteiro**, não de uma tela específica!
|
|
186
|
+
|
|
187
|
+
Na URL do Figma:
|
|
188
|
+
```
|
|
189
|
+
https://www.figma.com/design/5gCjy5F30XJySmOPpgDKLM/Tela-de-Login?node-id=13-2&m=dev
|
|
190
|
+
└──────────────────────┘ └──────┘
|
|
191
|
+
fileId node-id (NÃO usar)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
| Conceito | O que é | Precisa na config? |
|
|
195
|
+
|----------|---------|-------------------|
|
|
196
|
+
| `fileId` | Identifica o **arquivo Figma inteiro** | ✅ Sim |
|
|
197
|
+
| `node-id` | Identifica um frame específico | ❌ Não |
|
|
198
|
+
|
|
199
|
+
**Regra:**
|
|
200
|
+
- **Mesmo arquivo Figma, telas diferentes** → Use o **mesmo fileId**
|
|
201
|
+
- **Arquivo Figma diferente** → Precisa de um **novo fileId**
|
|
202
|
+
|
|
203
|
+
**Exemplo:**
|
|
204
|
+
```
|
|
205
|
+
📁 Meu Projeto (fileId: ABC123xyz)
|
|
206
|
+
├── #LoginScreen ← mesmo fileId
|
|
207
|
+
├── #HomeScreen ← mesmo fileId
|
|
208
|
+
├── #ProfileScreen ← mesmo fileId
|
|
209
|
+
└── #SettingsScreen ← mesmo fileId
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
O CLI busca automaticamente **todos os frames** que começam com `#` dentro do arquivo.
|
|
213
|
+
|
|
183
214
|
---
|
|
184
215
|
|
|
185
216
|
## Guia para Designers
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Generate Command
|
|
3
3
|
* Gera componente a partir de um node do Figma ou arquivo JSON local
|
|
4
4
|
*/
|
|
5
|
-
import type { ComponentLayer, FrameworkType } from
|
|
5
|
+
import type { ComponentLayer, FrameworkType } from "@promptui-lib/core";
|
|
6
6
|
export interface IGenerateOptions {
|
|
7
7
|
force?: boolean;
|
|
8
8
|
output?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.cmd.d.ts","sourceRoot":"","sources":["../../src/commands/generate.cmd.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"generate.cmd.d.ts","sourceRoot":"","sources":["../../src/commands/generate.cmd.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EACV,cAAc,EAEd,aAAa,EACd,MAAM,oBAAoB,CAAC;AAiB5B,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AA4ED,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC,CA2Jf;AAyBD;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,IAAI,CAAC,CA0If"}
|
|
@@ -2,26 +2,26 @@
|
|
|
2
2
|
* Generate Command
|
|
3
3
|
* Gera componente a partir de um node do Figma ou arquivo JSON local
|
|
4
4
|
*/
|
|
5
|
-
import { readFile } from
|
|
6
|
-
import { createHash } from
|
|
7
|
-
import { createFigmaClient } from
|
|
8
|
-
import { parseNode } from
|
|
9
|
-
import { writeComponent, generateCode, generateMuiComponent, generateTailwindComponent, generateBootstrapComponent, FRAMEWORKS, } from
|
|
10
|
-
import { loadConfig, resolveConfig, validateConfig } from
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
import { createFigmaClient } from "@promptui-lib/figma-parser";
|
|
8
|
+
import { parseNode } from "@promptui-lib/figma-parser";
|
|
9
|
+
import { writeComponent, generateCode, generateMuiComponent, generateTailwindComponent, generateBootstrapComponent, FRAMEWORKS, } from "@promptui-lib/codegen";
|
|
10
|
+
import { loadConfig, resolveConfig, validateConfig, } from "@promptui-lib/mcp-agent";
|
|
11
11
|
/**
|
|
12
12
|
* Detecta se o input é um arquivo JSON local ou nodeId do Figma
|
|
13
13
|
*/
|
|
14
14
|
function isLocalFile(input) {
|
|
15
|
-
return input.endsWith(
|
|
15
|
+
return (input.endsWith(".json") || input.startsWith("./") || input.startsWith("/"));
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* Carrega node de arquivo JSON local
|
|
19
19
|
*/
|
|
20
20
|
async function loadNodeFromFile(filePath) {
|
|
21
|
-
const content = await readFile(filePath,
|
|
21
|
+
const content = await readFile(filePath, "utf-8");
|
|
22
22
|
const node = JSON.parse(content);
|
|
23
23
|
if (!node.id || !node.name || !node.type) {
|
|
24
|
-
throw new Error(
|
|
24
|
+
throw new Error("Invalid Figma node JSON: missing id, name or type");
|
|
25
25
|
}
|
|
26
26
|
return node;
|
|
27
27
|
}
|
|
@@ -30,10 +30,10 @@ async function loadNodeFromFile(filePath) {
|
|
|
30
30
|
*/
|
|
31
31
|
function generateMeta(node, ast, source, files) {
|
|
32
32
|
const content = JSON.stringify({ node, ast, source, files });
|
|
33
|
-
const checksum = createHash(
|
|
33
|
+
const checksum = createHash("sha256").update(content).digest("hex");
|
|
34
34
|
return {
|
|
35
|
-
generator:
|
|
36
|
-
version:
|
|
35
|
+
generator: "promptui-figma-codegen",
|
|
36
|
+
version: "1.0.0",
|
|
37
37
|
generatedAt: new Date().toISOString(),
|
|
38
38
|
source: {
|
|
39
39
|
nodeId: node.id,
|
|
@@ -52,7 +52,7 @@ function generateMeta(node, ast, source, files) {
|
|
|
52
52
|
}
|
|
53
53
|
export async function generateCommand(input, options) {
|
|
54
54
|
const isFile = isLocalFile(input);
|
|
55
|
-
console.log(`[PromptUI] Generating component from ${isFile ?
|
|
55
|
+
console.log(`[PromptUI] Generating component from ${isFile ? "file" : "Figma node"}: ${input}`);
|
|
56
56
|
// Carrega config
|
|
57
57
|
const config = await loadConfig();
|
|
58
58
|
const resolved = resolveConfig(config);
|
|
@@ -69,7 +69,7 @@ export async function generateCommand(input, options) {
|
|
|
69
69
|
// Valida config para API
|
|
70
70
|
const errors = validateConfig(resolved);
|
|
71
71
|
if (errors.length > 0) {
|
|
72
|
-
console.error(
|
|
72
|
+
console.error("[PromptUI] Configuration errors:");
|
|
73
73
|
for (const error of errors) {
|
|
74
74
|
console.error(` - ${error}`);
|
|
75
75
|
}
|
|
@@ -88,32 +88,32 @@ export async function generateCommand(input, options) {
|
|
|
88
88
|
}
|
|
89
89
|
// Parseia
|
|
90
90
|
const ast = parseNode(node, { forceLayer: options.layer });
|
|
91
|
-
const framework = options.framework ??
|
|
91
|
+
const framework = options.framework ?? "react";
|
|
92
92
|
const frameworkInfo = FRAMEWORKS[framework];
|
|
93
93
|
console.log(`[PromptUI] Parsed: ${ast.name} (${ast.layer})`);
|
|
94
94
|
console.log(`[PromptUI] Framework: ${frameworkInfo?.displayName ?? framework}`);
|
|
95
95
|
// Gera código baseado no framework
|
|
96
96
|
const generateForFramework = () => {
|
|
97
97
|
switch (framework) {
|
|
98
|
-
case
|
|
98
|
+
case "mui":
|
|
99
99
|
return {
|
|
100
100
|
tsx: generateMuiComponent(ast),
|
|
101
|
-
scss:
|
|
101
|
+
scss: "", // MUI usa sx props inline
|
|
102
102
|
index: `export { ${ast.name} } from './${ast.fileName}';\nexport type { I${ast.name}Props } from './${ast.fileName}';\n`,
|
|
103
103
|
};
|
|
104
|
-
case
|
|
104
|
+
case "tailwind":
|
|
105
105
|
return {
|
|
106
106
|
tsx: generateTailwindComponent(ast),
|
|
107
|
-
scss:
|
|
107
|
+
scss: "", // Tailwind usa classes inline
|
|
108
108
|
index: `export { ${ast.name} } from './${ast.fileName}';\nexport type { I${ast.name}Props } from './${ast.fileName}';\n`,
|
|
109
109
|
};
|
|
110
|
-
case
|
|
110
|
+
case "bootstrap":
|
|
111
111
|
return {
|
|
112
112
|
tsx: generateBootstrapComponent(ast),
|
|
113
|
-
scss:
|
|
113
|
+
scss: "", // Bootstrap usa classes inline
|
|
114
114
|
index: `export { ${ast.name} } from './${ast.fileName}';\nexport type { I${ast.name}Props } from './${ast.fileName}';\n`,
|
|
115
115
|
};
|
|
116
|
-
case
|
|
116
|
+
case "react":
|
|
117
117
|
default:
|
|
118
118
|
return generateCode(ast);
|
|
119
119
|
}
|
|
@@ -121,22 +121,22 @@ export async function generateCommand(input, options) {
|
|
|
121
121
|
// Preview mode
|
|
122
122
|
if (options.preview) {
|
|
123
123
|
const code = generateForFramework();
|
|
124
|
-
console.log(
|
|
124
|
+
console.log("\n--- TSX ---");
|
|
125
125
|
console.log(code.tsx);
|
|
126
126
|
if (code.scss) {
|
|
127
|
-
console.log(
|
|
127
|
+
console.log("\n--- SCSS ---");
|
|
128
128
|
console.log(code.scss);
|
|
129
129
|
}
|
|
130
|
-
console.log(
|
|
130
|
+
console.log("\n--- index.ts ---");
|
|
131
131
|
console.log(code.index);
|
|
132
132
|
if (!options.noMeta) {
|
|
133
133
|
const meta = generateMeta(node, ast, sourceInfo, [
|
|
134
134
|
`${ast.fileName}.tsx`,
|
|
135
135
|
`${ast.fileName}.scss`,
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
"index.ts",
|
|
137
|
+
"meta.json",
|
|
138
138
|
]);
|
|
139
|
-
console.log(
|
|
139
|
+
console.log("\n--- meta.json ---");
|
|
140
140
|
console.log(JSON.stringify(meta, null, 2));
|
|
141
141
|
}
|
|
142
142
|
return;
|
|
@@ -148,7 +148,7 @@ export async function generateCommand(input, options) {
|
|
|
148
148
|
forceOverwrite: options.force,
|
|
149
149
|
});
|
|
150
150
|
if (!result.success) {
|
|
151
|
-
console.error(
|
|
151
|
+
console.error("[PromptUI] Failed to write files:");
|
|
152
152
|
for (const error of result.errors ?? []) {
|
|
153
153
|
console.error(` - ${error}`);
|
|
154
154
|
}
|
|
@@ -156,31 +156,31 @@ export async function generateCommand(input, options) {
|
|
|
156
156
|
}
|
|
157
157
|
// Gera meta.json
|
|
158
158
|
if (!options.noMeta) {
|
|
159
|
-
const { writeFile } = await import(
|
|
160
|
-
const { join, dirname } = await import(
|
|
161
|
-
const metaPath = join(dirname(result.files.tsx),
|
|
159
|
+
const { writeFile } = await import("node:fs/promises");
|
|
160
|
+
const { join, dirname } = await import("node:path");
|
|
161
|
+
const metaPath = join(dirname(result.files.tsx), "meta.json");
|
|
162
162
|
const meta = generateMeta(node, ast, sourceInfo, [
|
|
163
163
|
`${ast.fileName}.tsx`,
|
|
164
164
|
`${ast.fileName}.scss`,
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
"index.ts",
|
|
166
|
+
"meta.json",
|
|
167
167
|
]);
|
|
168
|
-
await writeFile(metaPath, JSON.stringify(meta, null, 2),
|
|
169
|
-
console.log(
|
|
168
|
+
await writeFile(metaPath, JSON.stringify(meta, null, 2), "utf-8");
|
|
169
|
+
console.log("[PromptUI] Generated files:");
|
|
170
170
|
console.log(` - ${result.files.tsx}`);
|
|
171
171
|
console.log(` - ${result.files.scss}`);
|
|
172
172
|
console.log(` - ${result.files.index}`);
|
|
173
173
|
console.log(` - ${metaPath}`);
|
|
174
174
|
}
|
|
175
175
|
else {
|
|
176
|
-
console.log(
|
|
176
|
+
console.log("[PromptUI] Generated files:");
|
|
177
177
|
console.log(` - ${result.files.tsx}`);
|
|
178
178
|
console.log(` - ${result.files.scss}`);
|
|
179
179
|
console.log(` - ${result.files.index}`);
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
catch (error) {
|
|
183
|
-
const message = error instanceof Error ? error.message :
|
|
183
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
184
184
|
console.error(`[PromptUI] Error: ${message}`);
|
|
185
185
|
process.exit(1);
|
|
186
186
|
}
|
|
@@ -192,7 +192,7 @@ function hasExportableChildren(node) {
|
|
|
192
192
|
if (!node.children)
|
|
193
193
|
return false;
|
|
194
194
|
for (const child of node.children) {
|
|
195
|
-
if (child.name.startsWith(
|
|
195
|
+
if (child.name.startsWith("#"))
|
|
196
196
|
return true;
|
|
197
197
|
if (hasExportableChildren(child))
|
|
198
198
|
return true;
|
|
@@ -212,10 +212,10 @@ function hasExportableChildren(node) {
|
|
|
212
212
|
export async function generateAllCommand(options) {
|
|
213
213
|
const exportAll = options.all;
|
|
214
214
|
if (exportAll) {
|
|
215
|
-
console.log(
|
|
215
|
+
console.log("[PromptUI] Generating ALL top-level frames (--all mode)...");
|
|
216
216
|
}
|
|
217
217
|
else {
|
|
218
|
-
console.log(
|
|
218
|
+
console.log("[PromptUI] Generating all exportable components...");
|
|
219
219
|
}
|
|
220
220
|
// Carrega config
|
|
221
221
|
const config = await loadConfig();
|
|
@@ -223,7 +223,7 @@ export async function generateAllCommand(options) {
|
|
|
223
223
|
// Valida
|
|
224
224
|
const errors = validateConfig(resolved);
|
|
225
225
|
if (errors.length > 0) {
|
|
226
|
-
console.error(
|
|
226
|
+
console.error("[PromptUI] Configuration errors:");
|
|
227
227
|
for (const error of errors) {
|
|
228
228
|
console.error(` - ${error}`);
|
|
229
229
|
}
|
|
@@ -238,12 +238,8 @@ export async function generateAllCommand(options) {
|
|
|
238
238
|
function findExportable(node, depth = 0, parentId) {
|
|
239
239
|
// Se --all, exporta frames de nível superior (depth <= 2 = document > page > frame)
|
|
240
240
|
// Se não, apenas os que começam com #
|
|
241
|
-
const isTopLevelFrame = depth === 2 && (node.type ===
|
|
242
|
-
const isMarkedForExport = node.name.startsWith(
|
|
243
|
-
// Debug: mostra frames de nível superior
|
|
244
|
-
if (depth === 2) {
|
|
245
|
-
console.log(` [DEBUG] Top-level frame: "${node.name}" (type: ${node.type}, starts with #: ${isMarkedForExport})`);
|
|
246
|
-
}
|
|
241
|
+
const isTopLevelFrame = depth === 2 && (node.type === "FRAME" || node.type === "COMPONENT");
|
|
242
|
+
const isMarkedForExport = node.name.startsWith("#");
|
|
247
243
|
if (exportAll ? isTopLevelFrame : isMarkedForExport) {
|
|
248
244
|
// Só adiciona se ainda não existe (evita duplicatas)
|
|
249
245
|
if (!exportableMap.has(node.id)) {
|
|
@@ -265,23 +261,23 @@ export async function generateAllCommand(options) {
|
|
|
265
261
|
console.log(`[PromptUI] Found ${exportableNodes.length} exportable components`);
|
|
266
262
|
if (exportableNodes.length === 0) {
|
|
267
263
|
if (exportAll) {
|
|
268
|
-
console.log(
|
|
264
|
+
console.log("[PromptUI] No top-level frames found in the file.");
|
|
269
265
|
}
|
|
270
266
|
else {
|
|
271
|
-
console.log(
|
|
272
|
-
console.log(
|
|
267
|
+
console.log("[PromptUI] No components found. Mark frames with # prefix to export.");
|
|
268
|
+
console.log("[PromptUI] Or use --all flag to export all top-level frames.");
|
|
273
269
|
}
|
|
274
270
|
return;
|
|
275
271
|
}
|
|
276
272
|
// Ordena por profundidade decrescente (folhas primeiro, pais depois)
|
|
277
273
|
// Isso garante que componentes filhos sejam gerados antes dos pais
|
|
278
274
|
exportableNodes.sort((a, b) => b.depth - a.depth);
|
|
279
|
-
console.log(
|
|
275
|
+
console.log("[PromptUI] Generation order (leaves first):");
|
|
280
276
|
for (const { node, depth } of exportableNodes) {
|
|
281
277
|
const hasChildren = hasExportableChildren(node);
|
|
282
|
-
console.log(` ${hasChildren ?
|
|
278
|
+
console.log(` ${hasChildren ? "📦" : "🍃"} ${node.name} (depth: ${depth}, id: ${node.id.slice(0, 8)}...)`);
|
|
283
279
|
}
|
|
284
|
-
console.log(
|
|
280
|
+
console.log("");
|
|
285
281
|
let successCount = 0;
|
|
286
282
|
let errorCount = 0;
|
|
287
283
|
for (const { node } of exportableNodes) {
|
|
@@ -297,21 +293,21 @@ export async function generateAllCommand(options) {
|
|
|
297
293
|
successCount++;
|
|
298
294
|
}
|
|
299
295
|
else {
|
|
300
|
-
console.log(` ❌ ${node.name}: ${result.errors?.join(
|
|
296
|
+
console.log(` ❌ ${node.name}: ${result.errors?.join(", ")}`);
|
|
301
297
|
errorCount++;
|
|
302
298
|
}
|
|
303
299
|
}
|
|
304
300
|
catch (error) {
|
|
305
|
-
const message = error instanceof Error ? error.message :
|
|
301
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
306
302
|
console.log(` ❌ ${node.name}: ${message}`);
|
|
307
303
|
errorCount++;
|
|
308
304
|
}
|
|
309
305
|
}
|
|
310
|
-
console.log(
|
|
306
|
+
console.log("");
|
|
311
307
|
console.log(`[PromptUI] Done: ${successCount} success, ${errorCount} errors`);
|
|
312
308
|
}
|
|
313
309
|
catch (error) {
|
|
314
|
-
const message = error instanceof Error ? error.message :
|
|
310
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
315
311
|
console.error(`[PromptUI] Error: ${message}`);
|
|
316
312
|
process.exit(1);
|
|
317
313
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promptui-lib/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "CLI for PromptUI - Figma to React code generator",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"bin"
|
|
46
46
|
],
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@promptui-lib/core": "0.1.
|
|
49
|
-
"@promptui-lib/figma-parser": "0.1.
|
|
50
|
-
"@promptui-lib/codegen": "0.1.
|
|
51
|
-
"@promptui-lib/mcp-agent": "0.1.
|
|
48
|
+
"@promptui-lib/core": "0.1.17",
|
|
49
|
+
"@promptui-lib/figma-parser": "0.1.17",
|
|
50
|
+
"@promptui-lib/codegen": "0.1.17",
|
|
51
|
+
"@promptui-lib/mcp-agent": "0.1.17"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^20.0.0",
|