@promptui-lib/codegen 0.1.19 → 0.1.22
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/frameworks/antd.template.d.ts.map +1 -1
- package/dist/frameworks/antd.template.js +13 -0
- package/dist/frameworks/bootstrap.template.d.ts.map +1 -1
- package/dist/frameworks/bootstrap.template.js +36 -4
- package/dist/frameworks/mui.template.d.ts.map +1 -1
- package/dist/frameworks/mui.template.js +36 -4
- package/dist/frameworks/tailwind.template.d.ts.map +1 -1
- package/dist/frameworks/tailwind.template.js +36 -4
- package/dist/generators/tsx-generator.d.ts.map +1 -1
- package/dist/generators/tsx-generator.js +74 -2
- package/dist/writers/asset-writer.d.ts +27 -0
- package/dist/writers/asset-writer.d.ts.map +1 -0
- package/dist/writers/asset-writer.js +216 -0
- package/dist/writers/file-writer.d.ts +3 -0
- package/dist/writers/file-writer.d.ts.map +1 -1
- package/dist/writers/file-writer.js +43 -0
- package/dist/writers/index.d.ts +2 -0
- package/dist/writers/index.d.ts.map +1 -1
- package/dist/writers/index.js +1 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"antd.template.d.ts","sourceRoot":"","sources":["../../src/frameworks/antd.template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"antd.template.d.ts","sourceRoot":"","sources":["../../src/frameworks/antd.template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAA6E,MAAM,oBAAoB,CAAC;AAqNnI;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAiChE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAoB9E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAwC7D"}
|
|
@@ -96,6 +96,12 @@ function isComponentNode(child) {
|
|
|
96
96
|
function isJSXNode(child) {
|
|
97
97
|
return 'tag' in child;
|
|
98
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Check if a child is an asset node
|
|
101
|
+
*/
|
|
102
|
+
function isAssetNode(child) {
|
|
103
|
+
return 'type' in child && child.type === 'asset';
|
|
104
|
+
}
|
|
99
105
|
/**
|
|
100
106
|
* Collect all imports needed for the component
|
|
101
107
|
*/
|
|
@@ -162,6 +168,13 @@ function generateJSX(node, indent = 2) {
|
|
|
162
168
|
// Render child component as <ComponentName />
|
|
163
169
|
return `${spaces} <${child.componentName} />`;
|
|
164
170
|
}
|
|
171
|
+
if (isAssetNode(child)) {
|
|
172
|
+
// Render asset as <img /> with placeholder src
|
|
173
|
+
const classAttrAsset = child.className ? ` className="${child.className}"` : '';
|
|
174
|
+
const widthAttr = child.width ? ` width={${Math.round(child.width)}}` : '';
|
|
175
|
+
const heightAttr = child.height ? ` height={${Math.round(child.height)}}` : '';
|
|
176
|
+
return `${spaces} <img src="${child.fileName}.${child.format}" alt="${child.alt ?? ''}"${widthAttr}${heightAttr}${classAttrAsset} />`;
|
|
177
|
+
}
|
|
165
178
|
if (isJSXNode(child)) {
|
|
166
179
|
return generateJSX(child, indent + 2);
|
|
167
180
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.template.d.ts","sourceRoot":"","sources":["../../src/frameworks/bootstrap.template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"bootstrap.template.d.ts","sourceRoot":"","sources":["../../src/frameworks/bootstrap.template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAgE,MAAM,oBAAoB,CAAC;AA6TtH;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAkDrE"}
|
|
@@ -213,6 +213,30 @@ function generateBootstrapClasses(styles) {
|
|
|
213
213
|
}
|
|
214
214
|
return classes;
|
|
215
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Check if a child is a text node
|
|
218
|
+
*/
|
|
219
|
+
function isTextNode(child) {
|
|
220
|
+
return typeof child === 'object' && child !== null && 'type' in child && child.type === 'text';
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Check if a child is a component node
|
|
224
|
+
*/
|
|
225
|
+
function isComponentNode(child) {
|
|
226
|
+
return typeof child === 'object' && child !== null && 'type' in child && child.type === 'component';
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Check if a child is an asset node
|
|
230
|
+
*/
|
|
231
|
+
function isAssetNode(child) {
|
|
232
|
+
return typeof child === 'object' && child !== null && 'type' in child && child.type === 'asset';
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Check if a child is a JSX node
|
|
236
|
+
*/
|
|
237
|
+
function isJSXNode(child) {
|
|
238
|
+
return typeof child === 'object' && child !== null && 'tag' in child;
|
|
239
|
+
}
|
|
216
240
|
/**
|
|
217
241
|
* Gera JSX com classes Bootstrap
|
|
218
242
|
*/
|
|
@@ -238,11 +262,19 @@ function generateBootstrapJSX(node, styles, indent = 2) {
|
|
|
238
262
|
}
|
|
239
263
|
const childrenContent = [];
|
|
240
264
|
for (const child of node.children) {
|
|
241
|
-
if (
|
|
242
|
-
|
|
243
|
-
|
|
265
|
+
if (isTextNode(child)) {
|
|
266
|
+
childrenContent.push(`${spaces} {children ?? '${child.value}'}`);
|
|
267
|
+
}
|
|
268
|
+
else if (isComponentNode(child)) {
|
|
269
|
+
childrenContent.push(`${spaces} <${child.componentName} />`);
|
|
270
|
+
}
|
|
271
|
+
else if (isAssetNode(child)) {
|
|
272
|
+
const classAttrAsset = child.className ? ` className="${child.className}"` : '';
|
|
273
|
+
const widthAttr = child.width ? ` width={${Math.round(child.width)}}` : '';
|
|
274
|
+
const heightAttr = child.height ? ` height={${Math.round(child.height)}}` : '';
|
|
275
|
+
childrenContent.push(`${spaces} <img src="${child.fileName}.${child.format}" alt="${child.alt ?? ''}"${widthAttr}${heightAttr}${classAttrAsset} />`);
|
|
244
276
|
}
|
|
245
|
-
else {
|
|
277
|
+
else if (isJSXNode(child)) {
|
|
246
278
|
childrenContent.push(generateBootstrapJSX(child, styles, indent + 2));
|
|
247
279
|
}
|
|
248
280
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mui.template.d.ts","sourceRoot":"","sources":["../../src/frameworks/mui.template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"mui.template.d.ts","sourceRoot":"","sources":["../../src/frameworks/mui.template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAgE,MAAM,oBAAoB,CAAC;AA6TtH;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAgE/D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAwB5D"}
|
|
@@ -189,6 +189,30 @@ function getMuiProps(node, componentType) {
|
|
|
189
189
|
}
|
|
190
190
|
return props;
|
|
191
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Check if a child is a text node
|
|
194
|
+
*/
|
|
195
|
+
function isTextNode(child) {
|
|
196
|
+
return typeof child === 'object' && child !== null && 'type' in child && child.type === 'text';
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Check if a child is a component node
|
|
200
|
+
*/
|
|
201
|
+
function isComponentNode(child) {
|
|
202
|
+
return typeof child === 'object' && child !== null && 'type' in child && child.type === 'component';
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Check if a child is an asset node
|
|
206
|
+
*/
|
|
207
|
+
function isAssetNode(child) {
|
|
208
|
+
return typeof child === 'object' && child !== null && 'type' in child && child.type === 'asset';
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Check if a child is a JSX node
|
|
212
|
+
*/
|
|
213
|
+
function isJSXNode(child) {
|
|
214
|
+
return typeof child === 'object' && child !== null && 'tag' in child;
|
|
215
|
+
}
|
|
192
216
|
/**
|
|
193
217
|
* Gera JSX com componentes MUI
|
|
194
218
|
*/
|
|
@@ -215,11 +239,19 @@ function generateMuiJSX(node, styles, indent = 2) {
|
|
|
215
239
|
}
|
|
216
240
|
const childrenContent = [];
|
|
217
241
|
for (const child of node.children) {
|
|
218
|
-
if (
|
|
219
|
-
|
|
220
|
-
childrenContent.push(`${spaces} {children ?? '${text}'}`);
|
|
242
|
+
if (isTextNode(child)) {
|
|
243
|
+
childrenContent.push(`${spaces} {children ?? '${child.value}'}`);
|
|
221
244
|
}
|
|
222
|
-
else {
|
|
245
|
+
else if (isComponentNode(child)) {
|
|
246
|
+
childrenContent.push(`${spaces} <${child.componentName} />`);
|
|
247
|
+
}
|
|
248
|
+
else if (isAssetNode(child)) {
|
|
249
|
+
const classAttrAsset = child.className ? ` className="${child.className}"` : '';
|
|
250
|
+
const widthAttr = child.width ? ` width={${Math.round(child.width)}}` : '';
|
|
251
|
+
const heightAttr = child.height ? ` height={${Math.round(child.height)}}` : '';
|
|
252
|
+
childrenContent.push(`${spaces} <img src="${child.fileName}.${child.format}" alt="${child.alt ?? ''}"${widthAttr}${heightAttr}${classAttrAsset} />`);
|
|
253
|
+
}
|
|
254
|
+
else if (isJSXNode(child)) {
|
|
223
255
|
childrenContent.push(generateMuiJSX(child, styles, indent + 2));
|
|
224
256
|
}
|
|
225
257
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tailwind.template.d.ts","sourceRoot":"","sources":["../../src/frameworks/tailwind.template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAY,cAAc,
|
|
1
|
+
{"version":3,"file":"tailwind.template.d.ts","sourceRoot":"","sources":["../../src/frameworks/tailwind.template.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAY,cAAc,EAAsC,MAAM,oBAAoB,CAAC;AAuMtH;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,EAAE,CAY1E;AAyFD;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAsCpE"}
|
|
@@ -204,6 +204,30 @@ export function generateTailwindClasses(styles) {
|
|
|
204
204
|
}
|
|
205
205
|
return classes;
|
|
206
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* Check if a child is a text node
|
|
209
|
+
*/
|
|
210
|
+
function isTextNode(child) {
|
|
211
|
+
return typeof child === 'object' && child !== null && 'type' in child && child.type === 'text';
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Check if a child is a component node
|
|
215
|
+
*/
|
|
216
|
+
function isComponentNode(child) {
|
|
217
|
+
return typeof child === 'object' && child !== null && 'type' in child && child.type === 'component';
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Check if a child is an asset node
|
|
221
|
+
*/
|
|
222
|
+
function isAssetNode(child) {
|
|
223
|
+
return typeof child === 'object' && child !== null && 'type' in child && child.type === 'asset';
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Check if a child is a JSX node
|
|
227
|
+
*/
|
|
228
|
+
function isJSXNode(child) {
|
|
229
|
+
return typeof child === 'object' && child !== null && 'tag' in child;
|
|
230
|
+
}
|
|
207
231
|
/**
|
|
208
232
|
* Gera JSX com classes Tailwind
|
|
209
233
|
*/
|
|
@@ -229,11 +253,19 @@ function generateTailwindJSX(node, styles, indent = 2) {
|
|
|
229
253
|
}
|
|
230
254
|
const childrenContent = [];
|
|
231
255
|
for (const child of node.children) {
|
|
232
|
-
if (
|
|
233
|
-
|
|
234
|
-
|
|
256
|
+
if (isTextNode(child)) {
|
|
257
|
+
childrenContent.push(`${spaces} {children ?? '${child.value}'}`);
|
|
258
|
+
}
|
|
259
|
+
else if (isComponentNode(child)) {
|
|
260
|
+
childrenContent.push(`${spaces} <${child.componentName} />`);
|
|
261
|
+
}
|
|
262
|
+
else if (isAssetNode(child)) {
|
|
263
|
+
const classAttrAsset = child.className ? ` className="${child.className}"` : '';
|
|
264
|
+
const widthAttr = child.width ? ` width={${Math.round(child.width)}}` : '';
|
|
265
|
+
const heightAttr = child.height ? ` height={${Math.round(child.height)}}` : '';
|
|
266
|
+
childrenContent.push(`${spaces} <img src="${child.fileName}.${child.format}" alt="${child.alt ?? ''}"${widthAttr}${heightAttr}${classAttrAsset} />`);
|
|
235
267
|
}
|
|
236
|
-
else {
|
|
268
|
+
else if (isJSXNode(child)) {
|
|
237
269
|
childrenContent.push(generateTailwindJSX(child, styles, indent + 2));
|
|
238
270
|
}
|
|
239
271
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tsx-generator.d.ts","sourceRoot":"","sources":["../../src/generators/tsx-generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,aAAa,
|
|
1
|
+
{"version":3,"file":"tsx-generator.d.ts","sourceRoot":"","sources":["../../src/generators/tsx-generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,aAAa,EASd,MAAM,oBAAoB,CAAC;AA4T5B;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAuCtD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAUxD"}
|
|
@@ -18,6 +18,71 @@ function collectChildComponents(node) {
|
|
|
18
18
|
}
|
|
19
19
|
return components;
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Coleta todos os assets de uma árvore JSX recursivamente
|
|
23
|
+
*/
|
|
24
|
+
function collectAssets(node) {
|
|
25
|
+
const assets = [];
|
|
26
|
+
for (const child of node.children) {
|
|
27
|
+
if ('type' in child && child.type === 'asset') {
|
|
28
|
+
assets.push(child);
|
|
29
|
+
}
|
|
30
|
+
else if ('tag' in child) {
|
|
31
|
+
// Recursivamente busca em nós JSX
|
|
32
|
+
assets.push(...collectAssets(child));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return assets;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Gera imports de assets (SVGs)
|
|
39
|
+
*/
|
|
40
|
+
function generateAssetImports(assets) {
|
|
41
|
+
const imports = [];
|
|
42
|
+
const seen = new Set();
|
|
43
|
+
for (const asset of assets) {
|
|
44
|
+
// Evita duplicatas
|
|
45
|
+
if (seen.has(asset.fileName)) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
seen.add(asset.fileName);
|
|
49
|
+
// Gera nome de variável para o import (PascalCase + Icon)
|
|
50
|
+
const importName = asset.fileName
|
|
51
|
+
.split('-')
|
|
52
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
53
|
+
.join('') + 'Svg';
|
|
54
|
+
imports.push({
|
|
55
|
+
from: `./assets/${asset.fileName}.svg`,
|
|
56
|
+
default: importName,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
return imports;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Gera JSX de um asset (SVG como img)
|
|
63
|
+
*/
|
|
64
|
+
function generateAssetJSX(asset, indent) {
|
|
65
|
+
const spaces = ' '.repeat(indent);
|
|
66
|
+
// Gera nome de variável para o import
|
|
67
|
+
const importName = asset.fileName
|
|
68
|
+
.split('-')
|
|
69
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
70
|
+
.join('') + 'Svg';
|
|
71
|
+
let jsx = `${spaces}<img`;
|
|
72
|
+
jsx += ` src={${importName}}`;
|
|
73
|
+
jsx += ` alt="${asset.alt ?? ''}"`;
|
|
74
|
+
if (asset.className) {
|
|
75
|
+
jsx += ` className="${asset.className}"`;
|
|
76
|
+
}
|
|
77
|
+
if (asset.width) {
|
|
78
|
+
jsx += ` width={${Math.round(asset.width)}}`;
|
|
79
|
+
}
|
|
80
|
+
if (asset.height) {
|
|
81
|
+
jsx += ` height={${Math.round(asset.height)}}`;
|
|
82
|
+
}
|
|
83
|
+
jsx += ' />';
|
|
84
|
+
return jsx;
|
|
85
|
+
}
|
|
21
86
|
/**
|
|
22
87
|
* Calcula o caminho relativo de import entre duas camadas do Atomic Design
|
|
23
88
|
*/
|
|
@@ -188,6 +253,10 @@ function generateJSX(node, indent = 2) {
|
|
|
188
253
|
// Renderiza componente filho como <ComponentName />
|
|
189
254
|
childrenJSX.push(generateComponentJSX(child, indent + 2));
|
|
190
255
|
}
|
|
256
|
+
else if ('type' in child && child.type === 'asset') {
|
|
257
|
+
// Renderiza asset como <img src={importedSvg} />
|
|
258
|
+
childrenJSX.push(generateAssetJSX(child, indent + 2));
|
|
259
|
+
}
|
|
191
260
|
else {
|
|
192
261
|
childrenJSX.push(generateJSX(child, indent + 2));
|
|
193
262
|
}
|
|
@@ -205,8 +274,11 @@ export function generateTSX(ast) {
|
|
|
205
274
|
// Coleta componentes filhos para gerar imports
|
|
206
275
|
const childComponents = collectChildComponents(ast.jsx);
|
|
207
276
|
const componentImports = generateComponentImports(childComponents, ast.layer);
|
|
208
|
-
//
|
|
209
|
-
const
|
|
277
|
+
// Coleta assets para gerar imports de SVGs
|
|
278
|
+
const assets = collectAssets(ast.jsx);
|
|
279
|
+
const assetImports = generateAssetImports(assets);
|
|
280
|
+
// Combina imports existentes com imports de componentes e assets
|
|
281
|
+
const allImports = [...(ast.imports ?? []), ...componentImports, ...assetImports];
|
|
210
282
|
// Imports
|
|
211
283
|
const importsCode = generateImports(allImports);
|
|
212
284
|
if (importsCode) {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset Writer
|
|
3
|
+
* Baixa e salva assets (SVG, PNG, etc) do Figma
|
|
4
|
+
*/
|
|
5
|
+
import type { IComponentAST, IJSXNode, IJSXAsset } from '@promptui-lib/core';
|
|
6
|
+
export interface IAssetDownloadConfig {
|
|
7
|
+
figmaFileId: string;
|
|
8
|
+
figmaToken: string;
|
|
9
|
+
}
|
|
10
|
+
export interface IAssetWriteResult {
|
|
11
|
+
success: boolean;
|
|
12
|
+
files: string[];
|
|
13
|
+
errors?: string[];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Coleta todos os assets de uma árvore JSX recursivamente
|
|
17
|
+
*/
|
|
18
|
+
export declare function collectAssetsFromAST(node: IJSXNode): IJSXAsset[];
|
|
19
|
+
/**
|
|
20
|
+
* Coleta assets de múltiplos ASTs
|
|
21
|
+
*/
|
|
22
|
+
export declare function collectAllAssets(asts: IComponentAST[]): IJSXAsset[];
|
|
23
|
+
/**
|
|
24
|
+
* Baixa e salva assets do Figma
|
|
25
|
+
*/
|
|
26
|
+
export declare function writeAssets(assets: IJSXAsset[], assetsDir: string, config: IAssetDownloadConfig): Promise<IAssetWriteResult>;
|
|
27
|
+
//# sourceMappingURL=asset-writer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-writer.d.ts","sourceRoot":"","sources":["../../src/writers/asset-writer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE7E,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,GAAG,SAAS,EAAE,CAYhE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,SAAS,EAAE,CAgBnE;AA2DD;;GAEG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,SAAS,EAAE,EACnB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CA2J5B"}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset Writer
|
|
3
|
+
* Baixa e salva assets (SVG, PNG, etc) do Figma
|
|
4
|
+
*/
|
|
5
|
+
import { mkdir, writeFile, access } from 'node:fs/promises';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
/**
|
|
8
|
+
* Coleta todos os assets de uma árvore JSX recursivamente
|
|
9
|
+
*/
|
|
10
|
+
export function collectAssetsFromAST(node) {
|
|
11
|
+
const assets = [];
|
|
12
|
+
for (const child of node.children) {
|
|
13
|
+
if ('type' in child && child.type === 'asset') {
|
|
14
|
+
assets.push(child);
|
|
15
|
+
}
|
|
16
|
+
else if ('tag' in child) {
|
|
17
|
+
assets.push(...collectAssetsFromAST(child));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return assets;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Coleta assets de múltiplos ASTs
|
|
24
|
+
*/
|
|
25
|
+
export function collectAllAssets(asts) {
|
|
26
|
+
const assets = [];
|
|
27
|
+
const seenIds = new Set();
|
|
28
|
+
for (const ast of asts) {
|
|
29
|
+
const astAssets = collectAssetsFromAST(ast.jsx);
|
|
30
|
+
for (const asset of astAssets) {
|
|
31
|
+
// Evita duplicatas pelo nodeId
|
|
32
|
+
if (!seenIds.has(asset.nodeId)) {
|
|
33
|
+
seenIds.add(asset.nodeId);
|
|
34
|
+
assets.push(asset);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return assets;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Verifica se um arquivo existe
|
|
42
|
+
*/
|
|
43
|
+
async function fileExists(path) {
|
|
44
|
+
try {
|
|
45
|
+
await access(path);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Garante que o diretório existe
|
|
54
|
+
*/
|
|
55
|
+
async function ensureDir(path) {
|
|
56
|
+
await mkdir(path, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Baixa URLs de imagens do Figma
|
|
60
|
+
*/
|
|
61
|
+
async function getImageUrls(fileId, nodeIds, token, format = 'svg') {
|
|
62
|
+
const ids = nodeIds.join(',');
|
|
63
|
+
const url = `https://api.figma.com/v1/images/${fileId}?ids=${encodeURIComponent(ids)}&format=${format}`;
|
|
64
|
+
const response = await fetch(url, {
|
|
65
|
+
headers: {
|
|
66
|
+
'X-Figma-Token': token,
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
const error = await response.text();
|
|
71
|
+
throw new Error(`Figma API error: ${response.status} - ${error}`);
|
|
72
|
+
}
|
|
73
|
+
const data = (await response.json());
|
|
74
|
+
return data.images;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Baixa conteúdo de uma URL
|
|
78
|
+
*/
|
|
79
|
+
async function downloadContent(url) {
|
|
80
|
+
const response = await fetch(url);
|
|
81
|
+
if (!response.ok) {
|
|
82
|
+
throw new Error(`Failed to download: ${response.status}`);
|
|
83
|
+
}
|
|
84
|
+
return response.text();
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Baixa e salva assets do Figma
|
|
88
|
+
*/
|
|
89
|
+
export async function writeAssets(assets, assetsDir, config) {
|
|
90
|
+
const result = {
|
|
91
|
+
success: true,
|
|
92
|
+
files: [],
|
|
93
|
+
errors: [],
|
|
94
|
+
};
|
|
95
|
+
if (assets.length === 0) {
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
await ensureDir(assetsDir);
|
|
100
|
+
// Agrupa assets por formato
|
|
101
|
+
const svgAssets = assets.filter((a) => a.format === 'svg');
|
|
102
|
+
const pngAssets = assets.filter((a) => a.format === 'png');
|
|
103
|
+
const jpgAssets = assets.filter((a) => a.format === 'jpg');
|
|
104
|
+
// Baixa SVGs
|
|
105
|
+
if (svgAssets.length > 0) {
|
|
106
|
+
const nodeIds = svgAssets.map((a) => a.nodeId);
|
|
107
|
+
try {
|
|
108
|
+
const imageUrls = await getImageUrls(config.figmaFileId, nodeIds, config.figmaToken, 'svg');
|
|
109
|
+
for (const asset of svgAssets) {
|
|
110
|
+
const imageUrl = imageUrls[asset.nodeId];
|
|
111
|
+
if (!imageUrl) {
|
|
112
|
+
result.errors?.push(`No image URL returned for asset: ${asset.fileName}`);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const svgContent = await downloadContent(imageUrl);
|
|
117
|
+
const filePath = join(assetsDir, `${asset.fileName}.svg`);
|
|
118
|
+
// Só escreve se não existir ou conteúdo diferente
|
|
119
|
+
const exists = await fileExists(filePath);
|
|
120
|
+
if (!exists) {
|
|
121
|
+
await writeFile(filePath, svgContent, 'utf-8');
|
|
122
|
+
result.files.push(filePath);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
127
|
+
result.errors?.push(`Failed to download ${asset.fileName}: ${msg}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
133
|
+
result.errors?.push(`Failed to get SVG URLs: ${msg}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Baixa PNGs
|
|
137
|
+
if (pngAssets.length > 0) {
|
|
138
|
+
const nodeIds = pngAssets.map((a) => a.nodeId);
|
|
139
|
+
try {
|
|
140
|
+
const imageUrls = await getImageUrls(config.figmaFileId, nodeIds, config.figmaToken, 'png');
|
|
141
|
+
for (const asset of pngAssets) {
|
|
142
|
+
const imageUrl = imageUrls[asset.nodeId];
|
|
143
|
+
if (!imageUrl) {
|
|
144
|
+
result.errors?.push(`No image URL returned for asset: ${asset.fileName}`);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
const response = await fetch(imageUrl);
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
throw new Error(`HTTP ${response.status}`);
|
|
151
|
+
}
|
|
152
|
+
const buffer = await response.arrayBuffer();
|
|
153
|
+
const filePath = join(assetsDir, `${asset.fileName}.png`);
|
|
154
|
+
const exists = await fileExists(filePath);
|
|
155
|
+
if (!exists) {
|
|
156
|
+
await writeFile(filePath, Buffer.from(buffer));
|
|
157
|
+
result.files.push(filePath);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
162
|
+
result.errors?.push(`Failed to download ${asset.fileName}: ${msg}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
168
|
+
result.errors?.push(`Failed to get PNG URLs: ${msg}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Baixa JPGs
|
|
172
|
+
if (jpgAssets.length > 0) {
|
|
173
|
+
const nodeIds = jpgAssets.map((a) => a.nodeId);
|
|
174
|
+
try {
|
|
175
|
+
const imageUrls = await getImageUrls(config.figmaFileId, nodeIds, config.figmaToken, 'jpg');
|
|
176
|
+
for (const asset of jpgAssets) {
|
|
177
|
+
const imageUrl = imageUrls[asset.nodeId];
|
|
178
|
+
if (!imageUrl) {
|
|
179
|
+
result.errors?.push(`No image URL returned for asset: ${asset.fileName}`);
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
const response = await fetch(imageUrl);
|
|
184
|
+
if (!response.ok) {
|
|
185
|
+
throw new Error(`HTTP ${response.status}`);
|
|
186
|
+
}
|
|
187
|
+
const buffer = await response.arrayBuffer();
|
|
188
|
+
const filePath = join(assetsDir, `${asset.fileName}.jpg`);
|
|
189
|
+
const exists = await fileExists(filePath);
|
|
190
|
+
if (!exists) {
|
|
191
|
+
await writeFile(filePath, Buffer.from(buffer));
|
|
192
|
+
result.files.push(filePath);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
197
|
+
result.errors?.push(`Failed to download ${asset.fileName}: ${msg}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
203
|
+
result.errors?.push(`Failed to get JPG URLs: ${msg}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (result.errors && result.errors.length > 0) {
|
|
207
|
+
result.success = false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
212
|
+
result.success = false;
|
|
213
|
+
result.errors?.push(`Failed to write assets: ${msg}`);
|
|
214
|
+
}
|
|
215
|
+
return result;
|
|
216
|
+
}
|
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
* Escreve arquivos gerados no sistema de arquivos
|
|
4
4
|
*/
|
|
5
5
|
import type { IComponentAST, IGeneratedCode, ComponentLayer } from '@promptui-lib/core';
|
|
6
|
+
import { type IAssetDownloadConfig } from './asset-writer.js';
|
|
6
7
|
export interface IWriteOptions {
|
|
7
8
|
basePath: string;
|
|
8
9
|
forceOverwrite?: boolean;
|
|
10
|
+
/** Configuração para download de assets do Figma */
|
|
11
|
+
assetConfig?: IAssetDownloadConfig;
|
|
9
12
|
}
|
|
10
13
|
export interface IWriteResult {
|
|
11
14
|
success: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-writer.d.ts","sourceRoot":"","sources":["../../src/writers/file-writer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"file-writer.d.ts","sourceRoot":"","sources":["../../src/writers/file-writer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAKxF,OAAO,EAAiC,KAAK,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE7F,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,oDAAoD;IACpD,WAAW,CAAC,EAAE,oBAAoB,CAAC;CACpC;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE;QACL,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAqBD;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,cAAc,CAM/D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,cAAc,EACrB,QAAQ,EAAE,MAAM,GACf,MAAM,CAER;AAyFD;;GAEG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,CAAC,CAwFvB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,aAAa,EAAE,EACrB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,EAAE,CAAC,CAsCzB"}
|
|
@@ -8,6 +8,7 @@ import { generateTSX, generateIndex } from '../generators/tsx-generator.js';
|
|
|
8
8
|
import { generateSCSS } from '../generators/scss-generator.js';
|
|
9
9
|
import { generateDefaultVariables } from '../generators/variables-generator.js';
|
|
10
10
|
import { extractFontsFromSCSS, generateFontsScss } from '../generators/fonts-generator.js';
|
|
11
|
+
import { collectAllAssets, writeAssets } from './asset-writer.js';
|
|
11
12
|
/**
|
|
12
13
|
* Verifica se um arquivo existe
|
|
13
14
|
*/
|
|
@@ -157,6 +158,25 @@ export async function writeComponent(ast, options) {
|
|
|
157
158
|
await writeFile(tsxPath, code.tsx, 'utf-8');
|
|
158
159
|
await writeFile(scssPath, code.scss, 'utf-8');
|
|
159
160
|
await writeFile(indexPath, code.index, 'utf-8');
|
|
161
|
+
// Baixa e salva assets se configuração disponível
|
|
162
|
+
if (options.assetConfig) {
|
|
163
|
+
const assets = collectAllAssets([ast]);
|
|
164
|
+
if (assets.length > 0) {
|
|
165
|
+
console.log(`\n📦 Downloading ${assets.length} asset(s)...`);
|
|
166
|
+
// Pasta de assets fica na raiz do projeto (irmã de components)
|
|
167
|
+
const parentDir = dirname(options.basePath);
|
|
168
|
+
const assetsDir = join(parentDir, 'assets');
|
|
169
|
+
const assetResult = await writeAssets(assets, assetsDir, options.assetConfig);
|
|
170
|
+
if (assetResult.files.length > 0) {
|
|
171
|
+
console.log(` ✅ Downloaded ${assetResult.files.length} asset(s) to ${assetsDir}`);
|
|
172
|
+
}
|
|
173
|
+
if (assetResult.errors && assetResult.errors.length > 0) {
|
|
174
|
+
for (const error of assetResult.errors) {
|
|
175
|
+
errors.push(`Asset error: ${error}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
160
180
|
return {
|
|
161
181
|
success: true,
|
|
162
182
|
files: {
|
|
@@ -185,5 +205,28 @@ export async function writeComponents(asts, options) {
|
|
|
185
205
|
const result = await writeComponent(ast, options);
|
|
186
206
|
results.push(result);
|
|
187
207
|
}
|
|
208
|
+
// Baixa e salva assets se configuração disponível
|
|
209
|
+
if (options.assetConfig) {
|
|
210
|
+
const assets = collectAllAssets(asts);
|
|
211
|
+
if (assets.length > 0) {
|
|
212
|
+
console.log(`\n📦 Downloading ${assets.length} asset(s)...`);
|
|
213
|
+
// Pasta de assets fica na raiz do projeto (irmã de components)
|
|
214
|
+
const parentDir = dirname(options.basePath);
|
|
215
|
+
const assetsDir = join(parentDir, 'assets');
|
|
216
|
+
const assetResult = await writeAssets(assets, assetsDir, options.assetConfig);
|
|
217
|
+
if (assetResult.files.length > 0) {
|
|
218
|
+
console.log(` ✅ Downloaded ${assetResult.files.length} asset(s) to ${assetsDir}`);
|
|
219
|
+
for (const file of assetResult.files) {
|
|
220
|
+
console.log(` 📄 ${file}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (assetResult.errors && assetResult.errors.length > 0) {
|
|
224
|
+
console.log(` ⚠️ Some assets failed to download:`);
|
|
225
|
+
for (const error of assetResult.errors) {
|
|
226
|
+
console.log(` ❌ ${error}`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
188
231
|
return results;
|
|
189
232
|
}
|
package/dist/writers/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { writeComponent, writeComponents, generateCode, getOutputPath, } from './file-writer.js';
|
|
2
2
|
export type { IWriteOptions, IWriteResult } from './file-writer.js';
|
|
3
|
+
export { writeAssets, collectAllAssets, collectAssetsFromAST, } from './asset-writer.js';
|
|
4
|
+
export type { IAssetDownloadConfig, IAssetWriteResult } from './asset-writer.js';
|
|
3
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/writers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,eAAe,EACf,YAAY,EACZ,aAAa,GACd,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/writers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,eAAe,EACf,YAAY,EACZ,aAAa,GACd,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEpE,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/writers/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@promptui-lib/codegen",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Code generator for PromptUI - generates React TSX and SCSS",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"dist"
|
|
31
31
|
],
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@promptui-lib/core": "0.1.
|
|
33
|
+
"@promptui-lib/core": "0.1.22"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^20.0.0",
|