@promptui-lib/codegen 0.1.20 → 0.1.23

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.
@@ -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;AAMxF,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;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,CA+DvB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,aAAa,EAAE,EACrB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,EAAE,CAAC,CASzB"}
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
  }
@@ -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"}
@@ -1 +1,2 @@
1
1
  export { writeComponent, writeComponents, generateCode, getOutputPath, } from './file-writer.js';
2
+ export { writeAssets, collectAllAssets, collectAssetsFromAST, } from './asset-writer.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@promptui-lib/codegen",
3
- "version": "0.1.20",
3
+ "version": "0.1.23",
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.20"
33
+ "@promptui-lib/core": "0.1.23"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@types/node": "^20.0.0",