figma-metadata-extractor 1.0.6 → 1.0.8

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
@@ -8,6 +8,15 @@ A TypeScript library for extracting metadata and downloading images from Figma f
8
8
  npm install figma-metadata-extractor
9
9
  ```
10
10
 
11
+ ## Features
12
+
13
+ - 🎨 Extract comprehensive metadata from Figma files
14
+ - 📦 Auto-download image assets (to disk or as buffers)
15
+ - 🔄 Support for both file-based and buffer-based workflows
16
+ - 🎯 Enrich metadata with image paths and markup
17
+ - 🔐 Support for both API keys and OAuth tokens
18
+ - 📝 Multiple output formats (JSON, YAML, object)
19
+
11
20
  ## Quick Start
12
21
 
13
22
  ### Get Metadata with Auto-Downloaded Images (LLM-Ready!)
@@ -15,7 +24,7 @@ npm install figma-metadata-extractor
15
24
  ```typescript
16
25
  import { getFigmaMetadata } from 'figma-metadata-extractor';
17
26
 
18
- // Extract metadata AND automatically download image assets
27
+ // Extract metadata AND automatically download image assets to disk
19
28
  const metadata = await getFigmaMetadata(
20
29
  'https://figma.com/file/ABC123/My-Design',
21
30
  {
@@ -26,6 +35,55 @@ const metadata = await getFigmaMetadata(
26
35
  }
27
36
  );
28
37
 
38
+ // Nodes are enriched with downloadedImage property
39
+ metadata.nodes.forEach(node => {
40
+ if (node.downloadedImage) {
41
+ console.log(node.downloadedImage.filePath);
42
+ console.log(node.downloadedImage.markdown); // ![name](path)
43
+ }
44
+ });
45
+ ```
46
+
47
+ ### Get Images as Buffers (No Disk Write)
48
+
49
+ ```typescript
50
+ import { getFigmaMetadata, enrichMetadataWithImages } from 'figma-metadata-extractor';
51
+ import fs from 'fs/promises';
52
+
53
+ // Get metadata with images as ArrayBuffers
54
+ const result = await getFigmaMetadata(
55
+ 'https://figma.com/file/ABC123/My-Design',
56
+ {
57
+ apiKey: 'your-figma-api-key',
58
+ downloadImages: true,
59
+ returnBuffer: true // Get images as ArrayBuffer
60
+ }
61
+ );
62
+
63
+ // Images are returned separately, metadata is not enriched
64
+ console.log(`Downloaded ${result.images.length} images as buffers`);
65
+
66
+ // Process buffers (upload to S3, convert format, etc.)
67
+ const savedPaths: string[] = [];
68
+ for (const image of result.images) {
69
+ // Example: Save to disk after processing
70
+ const buffer = Buffer.from(image.buffer);
71
+ const path = `./processed/${Date.now()}.png`;
72
+ await fs.writeFile(path, buffer);
73
+ savedPaths.push(path);
74
+ }
75
+
76
+ // Optionally enrich metadata with saved file paths
77
+ const enrichedMetadata = enrichMetadataWithImages(result, savedPaths, {
78
+ useRelativePaths: true
79
+ });
80
+
81
+ // Now nodes have downloadedImage properties
82
+ enrichedMetadata.nodes.forEach(node => {
83
+ if (node.downloadedImage) {
84
+ console.log(node.downloadedImage.markdown);
85
+ }
86
+ });
29
87
  ```
30
88
 
31
89
  ### Get Metadata Only (No Downloads)
@@ -98,13 +156,25 @@ Extracts comprehensive metadata from a Figma file including layout, content, vis
98
156
  - `outputFormat?: 'json' | 'yaml' | 'object'` - Output format (default: 'object')
99
157
  - `depth?: number` - Maximum depth to traverse the node tree
100
158
  - `downloadImages?: boolean` - Automatically download image assets and enrich metadata (default: false)
101
- - `localPath?: string` - Local path for downloaded images (required if downloadImages is true)
159
+ - `localPath?: string` - Local path for downloaded images (optional if returnBuffer is true)
102
160
  - `imageFormat?: 'png' | 'svg'` - Image format for downloads (default: 'png')
103
161
  - `pngScale?: number` - Export scale for PNG images (default: 2)
162
+ - `returnBuffer?: boolean` - Return images as ArrayBuffer instead of saving to disk (default: false)
163
+ - `enableLogging?: boolean` - Enable JSON debug log files (default: false)
104
164
 
105
165
  **Returns:** Promise<FigmaMetadataResult | string>
106
166
 
107
- When `downloadImages` is true, nodes with image assets will include a `downloadedImage` property:
167
+ **FigmaMetadataResult:**
168
+ ```typescript
169
+ {
170
+ metadata: any; // File metadata
171
+ nodes: any[]; // Design nodes
172
+ globalVars: any; // Styles, colors, etc.
173
+ images?: FigmaImageResult[]; // Only present when downloadImages: true and returnBuffer: true
174
+ }
175
+ ```
176
+
177
+ When `downloadImages: true` and `returnBuffer: false`, nodes with image assets will include a `downloadedImage` property:
108
178
  ```typescript
109
179
  {
110
180
  filePath: string; // Absolute path
@@ -115,6 +185,8 @@ When `downloadImages` is true, nodes with image assets will include a `downloade
115
185
  }
116
186
  ```
117
187
 
188
+ When `downloadImages: true` and `returnBuffer: true`, images are returned in the `images` array and nodes are NOT enriched. Use `enrichMetadataWithImages()` to enrich them later after saving buffers to disk.
189
+
118
190
  ### `downloadFigmaImages(figmaUrl, nodes, options)`
119
191
 
120
192
  Downloads SVG and PNG images from a Figma file.
@@ -135,10 +207,50 @@ Downloads SVG and PNG images from a Figma file.
135
207
 
136
208
  **Additional Options:**
137
209
  - `pngScale?: number` - Export scale for PNG images (default: 2)
138
- - `localPath: string` - Absolute path to save images
210
+ - `localPath?: string` - Absolute path to save images (optional if returnBuffer is true)
211
+ - `returnBuffer?: boolean` - Return images as ArrayBuffer instead of saving to disk (default: false)
212
+ - `enableLogging?: boolean` - Enable JSON debug log files (default: false)
139
213
 
140
214
  **Returns:** Promise<FigmaImageResult[]>
141
215
 
216
+ When `returnBuffer` is true, each result will contain a `buffer` property instead of `filePath`.
217
+
218
+ ### `enrichMetadataWithImages(metadata, imagePaths, options)`
219
+
220
+ Enriches metadata with saved image file paths after saving buffers to disk.
221
+
222
+ **Parameters:**
223
+ - `metadata` (FigmaMetadataResult): The metadata result from getFigmaMetadata with returnBuffer: true
224
+ - `imagePaths` (string[]): Array of file paths where images were saved (must match order of metadata.images)
225
+ - `options` (object): Configuration options
226
+ - `useRelativePaths?: boolean | string` - How to generate paths (default: true)
227
+ - `localPath?: string` - Base path for relative path calculation
228
+
229
+ **Returns:** FigmaMetadataResult with enriched nodes
230
+
231
+ **Example:**
232
+ ```typescript
233
+ // Get metadata with buffers
234
+ const result = await getFigmaMetadata(url, {
235
+ apiKey: 'key',
236
+ downloadImages: true,
237
+ returnBuffer: true
238
+ });
239
+
240
+ // Save buffers to disk
241
+ const paths = await Promise.all(
242
+ result.images.map((img, i) =>
243
+ fs.writeFile(`./images/img-${i}.png`, Buffer.from(img.buffer))
244
+ .then(() => `./images/img-${i}.png`)
245
+ )
246
+ );
247
+
248
+ // Enrich metadata with file paths
249
+ const enriched = enrichMetadataWithImages(result, paths, {
250
+ useRelativePaths: true
251
+ });
252
+ ```
253
+
142
254
  ### `downloadFigmaFrameImage(figmaUrl, options)`
143
255
 
144
256
  Downloads a single frame image from a Figma URL that contains a node-id parameter.
@@ -151,13 +263,22 @@ Downloads a single frame image from a Figma URL that contains a node-id paramete
151
263
  - `apiKey?: string` - Figma API key (Personal Access Token)
152
264
  - `oauthToken?: string` - Figma OAuth Bearer token
153
265
  - `useOAuth?: boolean` - Whether to use OAuth instead of API key
154
- - `localPath: string` - Absolute path to save the image
155
- - `fileName: string` - Local filename (must end with .png or .svg)
266
+ - `localPath?: string` - Absolute path to save the image (optional if returnBuffer is true)
267
+ - `fileName?: string` - Local filename (must end with .png or .svg, optional if returnBuffer is true)
156
268
  - `format?: 'png' | 'svg'` - Image format to download (default: 'png')
157
269
  - `pngScale?: number` - Export scale for PNG images (default: 2)
270
+ - `returnBuffer?: boolean` - Return image as ArrayBuffer instead of saving to disk (default: false)
271
+ - `enableLogging?: boolean` - Enable JSON debug log files (default: false)
158
272
 
159
273
  **Returns:** Promise<FigmaImageResult>
160
274
 
275
+ **Result Properties:**
276
+ - `filePath?: string` - Path to saved file (only when returnBuffer is false)
277
+ - `buffer?: ArrayBuffer` - Image data as ArrayBuffer (only when returnBuffer is true)
278
+ - `finalDimensions: { width: number; height: number }` - Image dimensions
279
+ - `wasCropped: boolean` - Whether the image was cropped
280
+ - `cssVariables?: string` - CSS variables for dimensions (if requested)
281
+
161
282
  ## Authentication
162
283
 
163
284
  You need either a Figma API key or OAuth token:
@@ -184,6 +305,7 @@ import { downloadFigmaFrameImage } from 'figma-metadata-extractor';
184
305
  // Copy this URL from Figma when viewing a frame
185
306
  const figmaUrl = 'https://www.figma.com/design/ABC123/My-Design?node-id=1234-5678&t=xyz123';
186
307
 
308
+ // Save to disk
187
309
  const result = await downloadFigmaFrameImage(figmaUrl, {
188
310
  apiKey: 'your-figma-api-key',
189
311
  localPath: './downloads',
@@ -196,6 +318,29 @@ console.log(`Downloaded to: ${result.filePath}`);
196
318
  console.log(`Dimensions: ${result.finalDimensions.width}x${result.finalDimensions.height}`);
197
319
  ```
198
320
 
321
+ ### Get Frame Image as ArrayBuffer (No Disk Write)
322
+
323
+ If you want to process the image in memory without saving to disk:
324
+
325
+ ```typescript
326
+ import { downloadFigmaFrameImage } from 'figma-metadata-extractor';
327
+
328
+ const figmaUrl = 'https://www.figma.com/design/ABC123/My-Design?node-id=1234-5678';
329
+
330
+ // Get as ArrayBuffer
331
+ const result = await downloadFigmaFrameImage(figmaUrl, {
332
+ apiKey: 'your-figma-api-key',
333
+ returnBuffer: true,
334
+ format: 'png'
335
+ });
336
+
337
+ console.log(`Buffer size: ${result.buffer.byteLength} bytes`);
338
+ console.log(`Dimensions: ${result.finalDimensions.width}x${result.finalDimensions.height}`);
339
+
340
+ // Use the buffer directly (e.g., upload to cloud storage, process with sharp, etc.)
341
+ // const processedImage = await sharp(Buffer.from(result.buffer)).resize(100, 100).toBuffer();
342
+ ```
343
+
199
344
  ### Download Multiple Frame Images
200
345
 
201
346
  ```typescript
@@ -216,6 +361,31 @@ const results = await downloadFigmaImages(
216
361
  );
217
362
  ```
218
363
 
364
+ ### Download Multiple Images as Buffers
365
+
366
+ ```typescript
367
+ import { downloadFigmaImages } from 'figma-metadata-extractor';
368
+
369
+ // Get multiple images as ArrayBuffers
370
+ const results = await downloadFigmaImages(
371
+ 'https://figma.com/file/ABC123/My-Design',
372
+ [
373
+ { nodeId: '1234:5678', fileName: 'frame1.png' },
374
+ { nodeId: '9876:5432', fileName: 'frame2.png' }
375
+ ],
376
+ {
377
+ apiKey: 'your-figma-api-key',
378
+ returnBuffer: true
379
+ }
380
+ );
381
+
382
+ // Process each buffer
383
+ results.forEach((result, index) => {
384
+ console.log(`Image ${index}: ${result.buffer.byteLength} bytes`);
385
+ // Upload to S3, process with sharp, etc.
386
+ });
387
+ ```
388
+
219
389
  ## Advanced Usage
220
390
 
221
391
  The library also exports the underlying extractor system for custom processing:
package/dist/index.cjs CHANGED
@@ -64,11 +64,69 @@ async function getImageDimensions(imagePath) {
64
64
  return { width: 1e3, height: 1e3 };
65
65
  }
66
66
  }
67
- async function downloadAndProcessImage(fileName, localPath, imageUrl, needsCropping = false, cropTransform, requiresImageDimensions = false) {
67
+ async function downloadAndProcessImage(fileName, localPath, imageUrl, needsCropping = false, cropTransform, requiresImageDimensions = false, returnBuffer = false) {
68
68
  const { Logger: Logger2 } = await Promise.resolve().then(() => logger);
69
69
  const processingLog = [];
70
70
  const { downloadFigmaImage: downloadFigmaImage2 } = await Promise.resolve().then(() => common);
71
- const originalPath = await downloadFigmaImage2(fileName, localPath, imageUrl);
71
+ const downloadResult = await downloadFigmaImage2(fileName, localPath, imageUrl, returnBuffer);
72
+ if (returnBuffer && downloadResult instanceof ArrayBuffer) {
73
+ Logger2.log(`Downloaded image as buffer (${downloadResult.byteLength} bytes)`);
74
+ let imageBuffer = Buffer.from(downloadResult);
75
+ let sharpImage = sharp(imageBuffer);
76
+ const metadata = await sharpImage.metadata();
77
+ const originalDimensions2 = {
78
+ width: metadata.width || 0,
79
+ height: metadata.height || 0
80
+ };
81
+ Logger2.log(`Original dimensions: ${originalDimensions2.width}x${originalDimensions2.height}`);
82
+ let wasCropped2 = false;
83
+ let cropRegion2;
84
+ let finalDimensions2 = originalDimensions2;
85
+ if (needsCropping && cropTransform) {
86
+ Logger2.log("Applying crop transform to buffer...");
87
+ const scaleX = cropTransform[0]?.[0] ?? 1;
88
+ const scaleY = cropTransform[1]?.[1] ?? 1;
89
+ const translateX = cropTransform[0]?.[2] ?? 0;
90
+ const translateY = cropTransform[1]?.[2] ?? 0;
91
+ const cropLeft = Math.max(0, Math.round(translateX * originalDimensions2.width));
92
+ const cropTop = Math.max(0, Math.round(translateY * originalDimensions2.height));
93
+ const cropWidth = Math.min(
94
+ originalDimensions2.width - cropLeft,
95
+ Math.round(scaleX * originalDimensions2.width)
96
+ );
97
+ const cropHeight = Math.min(
98
+ originalDimensions2.height - cropTop,
99
+ Math.round(scaleY * originalDimensions2.height)
100
+ );
101
+ if (cropWidth > 0 && cropHeight > 0) {
102
+ cropRegion2 = { left: cropLeft, top: cropTop, width: cropWidth, height: cropHeight };
103
+ const croppedBuffer = await sharpImage.extract({
104
+ left: cropLeft,
105
+ top: cropTop,
106
+ width: cropWidth,
107
+ height: cropHeight
108
+ }).toBuffer();
109
+ imageBuffer = croppedBuffer;
110
+ wasCropped2 = true;
111
+ finalDimensions2 = { width: cropWidth, height: cropHeight };
112
+ Logger2.log(`Cropped to region: ${cropLeft}, ${cropTop}, ${cropWidth}x${cropHeight}`);
113
+ }
114
+ }
115
+ let cssVariables2;
116
+ if (requiresImageDimensions) {
117
+ cssVariables2 = generateImageCSSVariables(finalDimensions2);
118
+ }
119
+ return {
120
+ buffer: imageBuffer.buffer.slice(imageBuffer.byteOffset, imageBuffer.byteOffset + imageBuffer.byteLength),
121
+ originalDimensions: originalDimensions2,
122
+ finalDimensions: finalDimensions2,
123
+ wasCropped: wasCropped2,
124
+ cropRegion: cropRegion2,
125
+ cssVariables: cssVariables2,
126
+ processingLog
127
+ };
128
+ }
129
+ const originalPath = downloadResult;
72
130
  Logger2.log(`Downloaded original image: ${originalPath}`);
73
131
  const originalDimensions = await getImageDimensions(originalPath);
74
132
  Logger2.log(`Original dimensions: ${originalDimensions.width}x${originalDimensions.height}`);
@@ -302,17 +360,21 @@ class FigmaService {
302
360
  * - PNG vs SVG format (based on filename extension)
303
361
  * - Image cropping based on transform matrices
304
362
  * - CSS variable generation for image dimensions
363
+ * - Returning as ArrayBuffer instead of saving to disk
305
364
  *
306
365
  * @returns Array of local file paths for successfully downloaded images
307
366
  */
308
367
  async downloadImages(fileKey, localPath, items, options = {}) {
309
368
  if (items.length === 0) return [];
310
- const sanitizedPath = path.normalize(localPath).replace(/^(\.\.(\/|\\|$))+/, "");
311
- const resolvedPath = path.resolve(sanitizedPath);
312
- if (!resolvedPath.startsWith(path.resolve(process.cwd()))) {
313
- throw new Error("Invalid path specified. Directory traversal is not allowed.");
369
+ const { pngScale = 2, svgOptions, returnBuffer = false } = options;
370
+ let resolvedPath = "";
371
+ if (!returnBuffer) {
372
+ const sanitizedPath = path.normalize(localPath).replace(/^(\.\.(\/|\\|$))+/, "");
373
+ resolvedPath = path.resolve(sanitizedPath);
374
+ if (!resolvedPath.startsWith(path.resolve(process.cwd()))) {
375
+ throw new Error("Invalid path specified. Directory traversal is not allowed.");
376
+ }
314
377
  }
315
- const { pngScale = 2, svgOptions } = options;
316
378
  const downloadPromises = [];
317
379
  const imageFills = items.filter(
318
380
  (item) => !!item.imageRef
@@ -330,7 +392,8 @@ class FigmaService {
330
392
  imageUrl,
331
393
  needsCropping,
332
394
  cropTransform,
333
- requiresImageDimensions
395
+ requiresImageDimensions,
396
+ returnBuffer
334
397
  ) : null;
335
398
  }).filter((promise) => promise !== null);
336
399
  if (fillDownloads.length > 0) {
@@ -355,7 +418,8 @@ class FigmaService {
355
418
  imageUrl,
356
419
  needsCropping,
357
420
  cropTransform,
358
- requiresImageDimensions
421
+ requiresImageDimensions,
422
+ returnBuffer
359
423
  ) : null;
360
424
  }).filter((promise) => promise !== null);
361
425
  if (pngDownloads.length > 0) {
@@ -377,7 +441,8 @@ class FigmaService {
377
441
  imageUrl,
378
442
  needsCropping,
379
443
  cropTransform,
380
- requiresImageDimensions
444
+ requiresImageDimensions,
445
+ returnBuffer
381
446
  ) : null;
382
447
  }).filter((promise) => promise !== null);
383
448
  if (svgDownloads.length > 0) {
@@ -411,23 +476,27 @@ class FigmaService {
411
476
  return response;
412
477
  }
413
478
  }
414
- async function downloadFigmaImage(fileName, localPath, imageUrl) {
479
+ async function downloadFigmaImage(fileName, localPath, imageUrl, returnBuffer = false) {
415
480
  try {
416
- if (!fs.existsSync(localPath)) {
417
- fs.mkdirSync(localPath, { recursive: true });
418
- }
419
- const fullPath = path.join(localPath, fileName);
420
481
  const response = await fetch(imageUrl, {
421
482
  method: "GET"
422
483
  });
423
484
  if (!response.ok) {
424
485
  throw new Error(`Failed to download image: ${response.statusText}`);
425
486
  }
426
- const writer = fs.createWriteStream(fullPath);
487
+ if (returnBuffer) {
488
+ const arrayBuffer = await response.arrayBuffer();
489
+ return arrayBuffer;
490
+ }
491
+ if (!fs.existsSync(localPath)) {
492
+ fs.mkdirSync(localPath, { recursive: true });
493
+ }
494
+ const fullPath = path.join(localPath, fileName);
427
495
  const reader = response.body?.getReader();
428
496
  if (!reader) {
429
497
  throw new Error("Failed to get response body");
430
498
  }
499
+ const writer = fs.createWriteStream(fullPath);
431
500
  return new Promise((resolve, reject) => {
432
501
  const processStream = async () => {
433
502
  try {
@@ -1378,7 +1447,8 @@ async function getFigmaMetadata(figmaUrl, options = {}) {
1378
1447
  imageFormat = "png",
1379
1448
  pngScale = 2,
1380
1449
  useRelativePaths = true,
1381
- enableLogging = false
1450
+ enableLogging = false,
1451
+ returnBuffer = false
1382
1452
  } = options;
1383
1453
  Logger.enableLogging = enableLogging;
1384
1454
  if (!apiKey && !oauthToken) {
@@ -1420,8 +1490,8 @@ async function getFigmaMetadata(figmaUrl, options = {}) {
1420
1490
  globalVars
1421
1491
  };
1422
1492
  if (downloadImages) {
1423
- if (!localPath) {
1424
- throw new Error("localPath is required when downloadImages is true");
1493
+ if (!returnBuffer && !localPath) {
1494
+ throw new Error("localPath is required when downloadImages is true and returnBuffer is false");
1425
1495
  }
1426
1496
  Logger.log("Discovering and downloading image assets...");
1427
1497
  const imageAssets = findImageAssets(nodes, globalVars);
@@ -1433,12 +1503,20 @@ async function getFigmaMetadata(figmaUrl, options = {}) {
1433
1503
  }));
1434
1504
  const downloadResults = await figmaService.downloadImages(
1435
1505
  fileKey,
1436
- localPath,
1506
+ localPath || "",
1437
1507
  imageNodes,
1438
- { pngScale: imageFormat === "png" ? pngScale : void 0 }
1508
+ {
1509
+ pngScale: imageFormat === "png" ? pngScale : void 0,
1510
+ returnBuffer
1511
+ }
1439
1512
  );
1440
- result.nodes = enrichNodesWithImages(nodes, imageAssets, downloadResults, useRelativePaths, localPath);
1441
- Logger.log(`Successfully downloaded and enriched ${downloadResults.length} images`);
1513
+ if (returnBuffer) {
1514
+ result.images = downloadResults;
1515
+ Logger.log(`Successfully downloaded ${downloadResults.length} images as buffers`);
1516
+ } else {
1517
+ result.nodes = enrichNodesWithImages(nodes, imageAssets, downloadResults, useRelativePaths, localPath);
1518
+ Logger.log(`Successfully downloaded and enriched ${downloadResults.length} images`);
1519
+ }
1442
1520
  }
1443
1521
  }
1444
1522
  if (outputFormat === "json") {
@@ -1455,11 +1533,14 @@ async function getFigmaMetadata(figmaUrl, options = {}) {
1455
1533
  }
1456
1534
  }
1457
1535
  async function downloadFigmaImages(figmaUrl, nodes, options) {
1458
- const { apiKey, oauthToken, useOAuth = false, pngScale = 2, localPath, enableLogging = false } = options;
1536
+ const { apiKey, oauthToken, useOAuth = false, pngScale = 2, localPath, enableLogging = false, returnBuffer = false } = options;
1459
1537
  Logger.enableLogging = enableLogging;
1460
1538
  if (!apiKey && !oauthToken) {
1461
1539
  throw new Error("Either apiKey or oauthToken is required");
1462
1540
  }
1541
+ if (!returnBuffer && !localPath) {
1542
+ throw new Error("localPath is required when returnBuffer is false");
1543
+ }
1463
1544
  const urlMatch = figmaUrl.match(/figma\.com\/(file|design)\/([a-zA-Z0-9]+)/);
1464
1545
  if (!urlMatch) {
1465
1546
  throw new Error("Invalid Figma URL format");
@@ -1475,8 +1556,9 @@ async function downloadFigmaImages(figmaUrl, nodes, options) {
1475
1556
  ...node,
1476
1557
  nodeId: node.nodeId.replace(/-/g, ":")
1477
1558
  }));
1478
- const results = await figmaService.downloadImages(fileKey, localPath, processedNodes, {
1479
- pngScale
1559
+ const results = await figmaService.downloadImages(fileKey, localPath || "", processedNodes, {
1560
+ pngScale,
1561
+ returnBuffer
1480
1562
  });
1481
1563
  return results;
1482
1564
  } catch (error) {
@@ -1493,12 +1575,16 @@ async function downloadFigmaFrameImage(figmaUrl, options) {
1493
1575
  localPath,
1494
1576
  fileName,
1495
1577
  format = "png",
1496
- enableLogging = false
1578
+ enableLogging = false,
1579
+ returnBuffer = false
1497
1580
  } = options;
1498
1581
  Logger.enableLogging = enableLogging;
1499
1582
  if (!apiKey && !oauthToken) {
1500
1583
  throw new Error("Either apiKey or oauthToken is required");
1501
1584
  }
1585
+ if (!returnBuffer && (!localPath || !fileName)) {
1586
+ throw new Error("localPath and fileName are required when returnBuffer is false");
1587
+ }
1502
1588
  const urlMatch = figmaUrl.match(/figma\.com\/(file|design)\/([a-zA-Z0-9]+)/);
1503
1589
  if (!urlMatch) {
1504
1590
  throw new Error("Invalid Figma URL format");
@@ -1509,9 +1595,11 @@ async function downloadFigmaFrameImage(figmaUrl, options) {
1509
1595
  throw new Error("No frame node-id found in URL. Please provide a Figma URL with a node-id parameter (e.g., ?node-id=123-456)");
1510
1596
  }
1511
1597
  const nodeId = nodeIdMatch[1].replace(/-/g, ":");
1512
- const expectedExtension = `.${format}`;
1513
- if (!fileName.toLowerCase().endsWith(expectedExtension)) {
1514
- throw new Error(`Filename must end with ${expectedExtension} for ${format} format`);
1598
+ if (fileName) {
1599
+ const expectedExtension = `.${format}`;
1600
+ if (!fileName.toLowerCase().endsWith(expectedExtension)) {
1601
+ throw new Error(`Filename must end with ${expectedExtension} for ${format} format`);
1602
+ }
1515
1603
  }
1516
1604
  const figmaService = new FigmaService({
1517
1605
  figmaApiKey: apiKey || "",
@@ -1522,15 +1610,20 @@ async function downloadFigmaFrameImage(figmaUrl, options) {
1522
1610
  Logger.log(`Downloading ${format.toUpperCase()} image for frame ${nodeId} from file ${fileKey}`);
1523
1611
  const imageNode = {
1524
1612
  nodeId,
1525
- fileName
1613
+ fileName: fileName || `temp.${format}`
1526
1614
  };
1527
- const results = await figmaService.downloadImages(fileKey, localPath, [imageNode], {
1528
- pngScale: format === "png" ? pngScale : void 0
1615
+ const results = await figmaService.downloadImages(fileKey, localPath || "", [imageNode], {
1616
+ pngScale: format === "png" ? pngScale : void 0,
1617
+ returnBuffer
1529
1618
  });
1530
1619
  if (results.length === 0) {
1531
1620
  throw new Error(`Failed to download image for frame ${nodeId}`);
1532
1621
  }
1533
- Logger.log(`Successfully downloaded frame image to: ${results[0].filePath}`);
1622
+ if (returnBuffer) {
1623
+ Logger.log(`Successfully downloaded frame image as buffer`);
1624
+ } else {
1625
+ Logger.log(`Successfully downloaded frame image to: ${results[0].filePath}`);
1626
+ }
1534
1627
  return results[0];
1535
1628
  } catch (error) {
1536
1629
  Logger.error(`Error downloading frame image from ${fileKey}:`, error);
@@ -1568,7 +1661,7 @@ function enrichNodesWithImages(nodes, imageAssets, downloadResults, useRelativeP
1568
1661
  const imageMap = /* @__PURE__ */ new Map();
1569
1662
  imageAssets.forEach((asset, index) => {
1570
1663
  const result = downloadResults[index];
1571
- if (result) {
1664
+ if (result && result.filePath) {
1572
1665
  let pathForMarkup;
1573
1666
  if (useRelativePaths === false) {
1574
1667
  pathForMarkup = result.filePath;
package/dist/index.js CHANGED
@@ -62,11 +62,69 @@ async function getImageDimensions(imagePath) {
62
62
  return { width: 1e3, height: 1e3 };
63
63
  }
64
64
  }
65
- async function downloadAndProcessImage(fileName, localPath, imageUrl, needsCropping = false, cropTransform, requiresImageDimensions = false) {
65
+ async function downloadAndProcessImage(fileName, localPath, imageUrl, needsCropping = false, cropTransform, requiresImageDimensions = false, returnBuffer = false) {
66
66
  const { Logger: Logger2 } = await Promise.resolve().then(() => logger);
67
67
  const processingLog = [];
68
68
  const { downloadFigmaImage: downloadFigmaImage2 } = await Promise.resolve().then(() => common);
69
- const originalPath = await downloadFigmaImage2(fileName, localPath, imageUrl);
69
+ const downloadResult = await downloadFigmaImage2(fileName, localPath, imageUrl, returnBuffer);
70
+ if (returnBuffer && downloadResult instanceof ArrayBuffer) {
71
+ Logger2.log(`Downloaded image as buffer (${downloadResult.byteLength} bytes)`);
72
+ let imageBuffer = Buffer.from(downloadResult);
73
+ let sharpImage = sharp(imageBuffer);
74
+ const metadata = await sharpImage.metadata();
75
+ const originalDimensions2 = {
76
+ width: metadata.width || 0,
77
+ height: metadata.height || 0
78
+ };
79
+ Logger2.log(`Original dimensions: ${originalDimensions2.width}x${originalDimensions2.height}`);
80
+ let wasCropped2 = false;
81
+ let cropRegion2;
82
+ let finalDimensions2 = originalDimensions2;
83
+ if (needsCropping && cropTransform) {
84
+ Logger2.log("Applying crop transform to buffer...");
85
+ const scaleX = cropTransform[0]?.[0] ?? 1;
86
+ const scaleY = cropTransform[1]?.[1] ?? 1;
87
+ const translateX = cropTransform[0]?.[2] ?? 0;
88
+ const translateY = cropTransform[1]?.[2] ?? 0;
89
+ const cropLeft = Math.max(0, Math.round(translateX * originalDimensions2.width));
90
+ const cropTop = Math.max(0, Math.round(translateY * originalDimensions2.height));
91
+ const cropWidth = Math.min(
92
+ originalDimensions2.width - cropLeft,
93
+ Math.round(scaleX * originalDimensions2.width)
94
+ );
95
+ const cropHeight = Math.min(
96
+ originalDimensions2.height - cropTop,
97
+ Math.round(scaleY * originalDimensions2.height)
98
+ );
99
+ if (cropWidth > 0 && cropHeight > 0) {
100
+ cropRegion2 = { left: cropLeft, top: cropTop, width: cropWidth, height: cropHeight };
101
+ const croppedBuffer = await sharpImage.extract({
102
+ left: cropLeft,
103
+ top: cropTop,
104
+ width: cropWidth,
105
+ height: cropHeight
106
+ }).toBuffer();
107
+ imageBuffer = croppedBuffer;
108
+ wasCropped2 = true;
109
+ finalDimensions2 = { width: cropWidth, height: cropHeight };
110
+ Logger2.log(`Cropped to region: ${cropLeft}, ${cropTop}, ${cropWidth}x${cropHeight}`);
111
+ }
112
+ }
113
+ let cssVariables2;
114
+ if (requiresImageDimensions) {
115
+ cssVariables2 = generateImageCSSVariables(finalDimensions2);
116
+ }
117
+ return {
118
+ buffer: imageBuffer.buffer.slice(imageBuffer.byteOffset, imageBuffer.byteOffset + imageBuffer.byteLength),
119
+ originalDimensions: originalDimensions2,
120
+ finalDimensions: finalDimensions2,
121
+ wasCropped: wasCropped2,
122
+ cropRegion: cropRegion2,
123
+ cssVariables: cssVariables2,
124
+ processingLog
125
+ };
126
+ }
127
+ const originalPath = downloadResult;
70
128
  Logger2.log(`Downloaded original image: ${originalPath}`);
71
129
  const originalDimensions = await getImageDimensions(originalPath);
72
130
  Logger2.log(`Original dimensions: ${originalDimensions.width}x${originalDimensions.height}`);
@@ -300,17 +358,21 @@ class FigmaService {
300
358
  * - PNG vs SVG format (based on filename extension)
301
359
  * - Image cropping based on transform matrices
302
360
  * - CSS variable generation for image dimensions
361
+ * - Returning as ArrayBuffer instead of saving to disk
303
362
  *
304
363
  * @returns Array of local file paths for successfully downloaded images
305
364
  */
306
365
  async downloadImages(fileKey, localPath, items, options = {}) {
307
366
  if (items.length === 0) return [];
308
- const sanitizedPath = path.normalize(localPath).replace(/^(\.\.(\/|\\|$))+/, "");
309
- const resolvedPath = path.resolve(sanitizedPath);
310
- if (!resolvedPath.startsWith(path.resolve(process.cwd()))) {
311
- throw new Error("Invalid path specified. Directory traversal is not allowed.");
367
+ const { pngScale = 2, svgOptions, returnBuffer = false } = options;
368
+ let resolvedPath = "";
369
+ if (!returnBuffer) {
370
+ const sanitizedPath = path.normalize(localPath).replace(/^(\.\.(\/|\\|$))+/, "");
371
+ resolvedPath = path.resolve(sanitizedPath);
372
+ if (!resolvedPath.startsWith(path.resolve(process.cwd()))) {
373
+ throw new Error("Invalid path specified. Directory traversal is not allowed.");
374
+ }
312
375
  }
313
- const { pngScale = 2, svgOptions } = options;
314
376
  const downloadPromises = [];
315
377
  const imageFills = items.filter(
316
378
  (item) => !!item.imageRef
@@ -328,7 +390,8 @@ class FigmaService {
328
390
  imageUrl,
329
391
  needsCropping,
330
392
  cropTransform,
331
- requiresImageDimensions
393
+ requiresImageDimensions,
394
+ returnBuffer
332
395
  ) : null;
333
396
  }).filter((promise) => promise !== null);
334
397
  if (fillDownloads.length > 0) {
@@ -353,7 +416,8 @@ class FigmaService {
353
416
  imageUrl,
354
417
  needsCropping,
355
418
  cropTransform,
356
- requiresImageDimensions
419
+ requiresImageDimensions,
420
+ returnBuffer
357
421
  ) : null;
358
422
  }).filter((promise) => promise !== null);
359
423
  if (pngDownloads.length > 0) {
@@ -375,7 +439,8 @@ class FigmaService {
375
439
  imageUrl,
376
440
  needsCropping,
377
441
  cropTransform,
378
- requiresImageDimensions
442
+ requiresImageDimensions,
443
+ returnBuffer
379
444
  ) : null;
380
445
  }).filter((promise) => promise !== null);
381
446
  if (svgDownloads.length > 0) {
@@ -409,23 +474,27 @@ class FigmaService {
409
474
  return response;
410
475
  }
411
476
  }
412
- async function downloadFigmaImage(fileName, localPath, imageUrl) {
477
+ async function downloadFigmaImage(fileName, localPath, imageUrl, returnBuffer = false) {
413
478
  try {
414
- if (!fs.existsSync(localPath)) {
415
- fs.mkdirSync(localPath, { recursive: true });
416
- }
417
- const fullPath = path.join(localPath, fileName);
418
479
  const response = await fetch(imageUrl, {
419
480
  method: "GET"
420
481
  });
421
482
  if (!response.ok) {
422
483
  throw new Error(`Failed to download image: ${response.statusText}`);
423
484
  }
424
- const writer = fs.createWriteStream(fullPath);
485
+ if (returnBuffer) {
486
+ const arrayBuffer = await response.arrayBuffer();
487
+ return arrayBuffer;
488
+ }
489
+ if (!fs.existsSync(localPath)) {
490
+ fs.mkdirSync(localPath, { recursive: true });
491
+ }
492
+ const fullPath = path.join(localPath, fileName);
425
493
  const reader = response.body?.getReader();
426
494
  if (!reader) {
427
495
  throw new Error("Failed to get response body");
428
496
  }
497
+ const writer = fs.createWriteStream(fullPath);
429
498
  return new Promise((resolve, reject) => {
430
499
  const processStream = async () => {
431
500
  try {
@@ -1376,7 +1445,8 @@ async function getFigmaMetadata(figmaUrl, options = {}) {
1376
1445
  imageFormat = "png",
1377
1446
  pngScale = 2,
1378
1447
  useRelativePaths = true,
1379
- enableLogging = false
1448
+ enableLogging = false,
1449
+ returnBuffer = false
1380
1450
  } = options;
1381
1451
  Logger.enableLogging = enableLogging;
1382
1452
  if (!apiKey && !oauthToken) {
@@ -1418,8 +1488,8 @@ async function getFigmaMetadata(figmaUrl, options = {}) {
1418
1488
  globalVars
1419
1489
  };
1420
1490
  if (downloadImages) {
1421
- if (!localPath) {
1422
- throw new Error("localPath is required when downloadImages is true");
1491
+ if (!returnBuffer && !localPath) {
1492
+ throw new Error("localPath is required when downloadImages is true and returnBuffer is false");
1423
1493
  }
1424
1494
  Logger.log("Discovering and downloading image assets...");
1425
1495
  const imageAssets = findImageAssets(nodes, globalVars);
@@ -1431,12 +1501,20 @@ async function getFigmaMetadata(figmaUrl, options = {}) {
1431
1501
  }));
1432
1502
  const downloadResults = await figmaService.downloadImages(
1433
1503
  fileKey,
1434
- localPath,
1504
+ localPath || "",
1435
1505
  imageNodes,
1436
- { pngScale: imageFormat === "png" ? pngScale : void 0 }
1506
+ {
1507
+ pngScale: imageFormat === "png" ? pngScale : void 0,
1508
+ returnBuffer
1509
+ }
1437
1510
  );
1438
- result.nodes = enrichNodesWithImages(nodes, imageAssets, downloadResults, useRelativePaths, localPath);
1439
- Logger.log(`Successfully downloaded and enriched ${downloadResults.length} images`);
1511
+ if (returnBuffer) {
1512
+ result.images = downloadResults;
1513
+ Logger.log(`Successfully downloaded ${downloadResults.length} images as buffers`);
1514
+ } else {
1515
+ result.nodes = enrichNodesWithImages(nodes, imageAssets, downloadResults, useRelativePaths, localPath);
1516
+ Logger.log(`Successfully downloaded and enriched ${downloadResults.length} images`);
1517
+ }
1440
1518
  }
1441
1519
  }
1442
1520
  if (outputFormat === "json") {
@@ -1453,11 +1531,14 @@ async function getFigmaMetadata(figmaUrl, options = {}) {
1453
1531
  }
1454
1532
  }
1455
1533
  async function downloadFigmaImages(figmaUrl, nodes, options) {
1456
- const { apiKey, oauthToken, useOAuth = false, pngScale = 2, localPath, enableLogging = false } = options;
1534
+ const { apiKey, oauthToken, useOAuth = false, pngScale = 2, localPath, enableLogging = false, returnBuffer = false } = options;
1457
1535
  Logger.enableLogging = enableLogging;
1458
1536
  if (!apiKey && !oauthToken) {
1459
1537
  throw new Error("Either apiKey or oauthToken is required");
1460
1538
  }
1539
+ if (!returnBuffer && !localPath) {
1540
+ throw new Error("localPath is required when returnBuffer is false");
1541
+ }
1461
1542
  const urlMatch = figmaUrl.match(/figma\.com\/(file|design)\/([a-zA-Z0-9]+)/);
1462
1543
  if (!urlMatch) {
1463
1544
  throw new Error("Invalid Figma URL format");
@@ -1473,8 +1554,9 @@ async function downloadFigmaImages(figmaUrl, nodes, options) {
1473
1554
  ...node,
1474
1555
  nodeId: node.nodeId.replace(/-/g, ":")
1475
1556
  }));
1476
- const results = await figmaService.downloadImages(fileKey, localPath, processedNodes, {
1477
- pngScale
1557
+ const results = await figmaService.downloadImages(fileKey, localPath || "", processedNodes, {
1558
+ pngScale,
1559
+ returnBuffer
1478
1560
  });
1479
1561
  return results;
1480
1562
  } catch (error) {
@@ -1491,12 +1573,16 @@ async function downloadFigmaFrameImage(figmaUrl, options) {
1491
1573
  localPath,
1492
1574
  fileName,
1493
1575
  format = "png",
1494
- enableLogging = false
1576
+ enableLogging = false,
1577
+ returnBuffer = false
1495
1578
  } = options;
1496
1579
  Logger.enableLogging = enableLogging;
1497
1580
  if (!apiKey && !oauthToken) {
1498
1581
  throw new Error("Either apiKey or oauthToken is required");
1499
1582
  }
1583
+ if (!returnBuffer && (!localPath || !fileName)) {
1584
+ throw new Error("localPath and fileName are required when returnBuffer is false");
1585
+ }
1500
1586
  const urlMatch = figmaUrl.match(/figma\.com\/(file|design)\/([a-zA-Z0-9]+)/);
1501
1587
  if (!urlMatch) {
1502
1588
  throw new Error("Invalid Figma URL format");
@@ -1507,9 +1593,11 @@ async function downloadFigmaFrameImage(figmaUrl, options) {
1507
1593
  throw new Error("No frame node-id found in URL. Please provide a Figma URL with a node-id parameter (e.g., ?node-id=123-456)");
1508
1594
  }
1509
1595
  const nodeId = nodeIdMatch[1].replace(/-/g, ":");
1510
- const expectedExtension = `.${format}`;
1511
- if (!fileName.toLowerCase().endsWith(expectedExtension)) {
1512
- throw new Error(`Filename must end with ${expectedExtension} for ${format} format`);
1596
+ if (fileName) {
1597
+ const expectedExtension = `.${format}`;
1598
+ if (!fileName.toLowerCase().endsWith(expectedExtension)) {
1599
+ throw new Error(`Filename must end with ${expectedExtension} for ${format} format`);
1600
+ }
1513
1601
  }
1514
1602
  const figmaService = new FigmaService({
1515
1603
  figmaApiKey: apiKey || "",
@@ -1520,15 +1608,20 @@ async function downloadFigmaFrameImage(figmaUrl, options) {
1520
1608
  Logger.log(`Downloading ${format.toUpperCase()} image for frame ${nodeId} from file ${fileKey}`);
1521
1609
  const imageNode = {
1522
1610
  nodeId,
1523
- fileName
1611
+ fileName: fileName || `temp.${format}`
1524
1612
  };
1525
- const results = await figmaService.downloadImages(fileKey, localPath, [imageNode], {
1526
- pngScale: format === "png" ? pngScale : void 0
1613
+ const results = await figmaService.downloadImages(fileKey, localPath || "", [imageNode], {
1614
+ pngScale: format === "png" ? pngScale : void 0,
1615
+ returnBuffer
1527
1616
  });
1528
1617
  if (results.length === 0) {
1529
1618
  throw new Error(`Failed to download image for frame ${nodeId}`);
1530
1619
  }
1531
- Logger.log(`Successfully downloaded frame image to: ${results[0].filePath}`);
1620
+ if (returnBuffer) {
1621
+ Logger.log(`Successfully downloaded frame image as buffer`);
1622
+ } else {
1623
+ Logger.log(`Successfully downloaded frame image to: ${results[0].filePath}`);
1624
+ }
1532
1625
  return results[0];
1533
1626
  } catch (error) {
1534
1627
  Logger.error(`Error downloading frame image from ${fileKey}:`, error);
@@ -1566,7 +1659,7 @@ function enrichNodesWithImages(nodes, imageAssets, downloadResults, useRelativeP
1566
1659
  const imageMap = /* @__PURE__ */ new Map();
1567
1660
  imageAssets.forEach((asset, index) => {
1568
1661
  const result = downloadResults[index];
1569
- if (result) {
1662
+ if (result && result.filePath) {
1570
1663
  let pathForMarkup;
1571
1664
  if (useRelativePaths === false) {
1572
1665
  pathForMarkup = result.filePath;
package/dist/lib.d.ts CHANGED
@@ -27,12 +27,16 @@ export interface FigmaMetadataOptions {
27
27
  useRelativePaths?: boolean | string;
28
28
  /** Enable JSON debug log files (defaults to false) */
29
29
  enableLogging?: boolean;
30
+ /** Return images as ArrayBuffer instead of saving to disk (defaults to false) */
31
+ returnBuffer?: boolean;
30
32
  }
31
33
  export interface FigmaImageOptions {
32
34
  /** Export scale for PNG images (defaults to 2) */
33
35
  pngScale?: number;
34
- /** The absolute path to the directory where images should be stored */
35
- localPath: string;
36
+ /** The absolute path to the directory where images should be stored (optional if returnBuffer is true) */
37
+ localPath?: string;
38
+ /** Return images as ArrayBuffer instead of saving to disk (defaults to false) */
39
+ returnBuffer?: boolean;
36
40
  }
37
41
  export interface FigmaImageNode {
38
42
  /** The ID of the Figma node, formatted as '1234:5678' */
@@ -54,9 +58,11 @@ export interface FigmaMetadataResult {
54
58
  metadata: any;
55
59
  nodes: any[];
56
60
  globalVars: any;
61
+ images?: FigmaImageResult[];
57
62
  }
58
63
  export interface FigmaImageResult {
59
- filePath: string;
64
+ filePath?: string;
65
+ buffer?: ArrayBuffer;
60
66
  finalDimensions: {
61
67
  width: number;
62
68
  height: number;
@@ -73,14 +79,16 @@ export interface FigmaFrameImageOptions {
73
79
  useOAuth?: boolean;
74
80
  /** Export scale for PNG images (defaults to 2) */
75
81
  pngScale?: number;
76
- /** The absolute path to the directory where the image should be stored */
77
- localPath: string;
78
- /** The filename for the downloaded image (must end with .png or .svg) */
79
- fileName: string;
82
+ /** The absolute path to the directory where the image should be stored (optional if returnBuffer is true) */
83
+ localPath?: string;
84
+ /** The filename for the downloaded image (must end with .png or .svg, optional if returnBuffer is true) */
85
+ fileName?: string;
80
86
  /** Image format to download (defaults to 'png') */
81
87
  format?: 'png' | 'svg';
82
88
  /** Enable JSON debug log files (defaults to false) */
83
89
  enableLogging?: boolean;
90
+ /** Return image as ArrayBuffer instead of saving to disk (defaults to false) */
91
+ returnBuffer?: boolean;
84
92
  }
85
93
  /**
86
94
  * Extract metadata from a Figma file or specific nodes
@@ -107,6 +115,20 @@ export declare function downloadFigmaImages(figmaUrl: string, nodes: FigmaImageN
107
115
  * @returns Promise resolving to the download result
108
116
  */
109
117
  export declare function downloadFigmaFrameImage(figmaUrl: string, options: FigmaFrameImageOptions): Promise<FigmaImageResult>;
118
+ /**
119
+ * Enrich metadata with saved image file paths
120
+ *
121
+ * Use this function after saving images from buffers to disk to add file path information to the metadata.
122
+ *
123
+ * @param metadata - The metadata result from getFigmaMetadata
124
+ * @param imagePaths - Array of file paths corresponding to the images array
125
+ * @param options - Options for path generation
126
+ * @returns Enriched metadata with downloadedImage properties on nodes
127
+ */
128
+ export declare function enrichMetadataWithImages(metadata: FigmaMetadataResult, imagePaths: string[], options?: {
129
+ useRelativePaths?: boolean | string;
130
+ localPath?: string;
131
+ }): FigmaMetadataResult;
110
132
  export type { SimplifiedDesign } from "./extractors/types.js";
111
133
  export type { ExtractorFn, TraversalContext, TraversalOptions, GlobalVars, StyleTypes, } from "./extractors/index.js";
112
134
  export { extractFromDesign, simplifyRawFigmaObject, layoutExtractor, textExtractor, visualsExtractor, componentExtractor, allExtractors, layoutAndText, contentOnly, visualsOnly, layoutOnly, collapseSvgContainers, } from "./extractors/index.js";
@@ -49,6 +49,7 @@ export declare class FigmaService {
49
49
  * - PNG vs SVG format (based on filename extension)
50
50
  * - Image cropping based on transform matrices
51
51
  * - CSS variable generation for image dimensions
52
+ * - Returning as ArrayBuffer instead of saving to disk
52
53
  *
53
54
  * @returns Array of local file paths for successfully downloaded images
54
55
  */
@@ -62,6 +63,7 @@ export declare class FigmaService {
62
63
  }>, options?: {
63
64
  pngScale?: number;
64
65
  svgOptions?: SvgOptions;
66
+ returnBuffer?: boolean;
65
67
  }): Promise<ImageProcessingResult[]>;
66
68
  /**
67
69
  * Get raw Figma API response for a file (for use with flexible extractors)
@@ -2,14 +2,15 @@ export type StyleId = `${string}_${string}` & {
2
2
  __brand: "StyleId";
3
3
  };
4
4
  /**
5
- * Download Figma image and save it locally
5
+ * Download Figma image and save it locally or return as buffer
6
6
  * @param fileName - The filename to save as
7
7
  * @param localPath - The local path to save to
8
8
  * @param imageUrl - Image URL (images[nodeId])
9
- * @returns A Promise that resolves to the full file path where the image was saved
9
+ * @param returnBuffer - If true, return ArrayBuffer instead of saving to disk
10
+ * @returns A Promise that resolves to the full file path where the image was saved, or ArrayBuffer if returnBuffer is true
10
11
  * @throws Error if download fails
11
12
  */
12
- export declare function downloadFigmaImage(fileName: string, localPath: string, imageUrl: string): Promise<string>;
13
+ export declare function downloadFigmaImage(fileName: string, localPath: string, imageUrl: string, returnBuffer?: boolean): Promise<string | ArrayBuffer>;
13
14
  /**
14
15
  * Remove keys with empty arrays or empty objects from an object.
15
16
  * @param input - The input object or value.
@@ -16,7 +16,8 @@ export declare function getImageDimensions(imagePath: string): Promise<{
16
16
  height: number;
17
17
  }>;
18
18
  export type ImageProcessingResult = {
19
- filePath: string;
19
+ filePath?: string;
20
+ buffer?: ArrayBuffer;
20
21
  originalDimensions: {
21
22
  width: number;
22
23
  height: number;
@@ -43,9 +44,10 @@ export type ImageProcessingResult = {
43
44
  * @param needsCropping - Whether to apply crop transform
44
45
  * @param cropTransform - Transform matrix for cropping
45
46
  * @param requiresImageDimensions - Whether to generate dimension metadata
47
+ * @param returnBuffer - If true, return ArrayBuffer instead of saving to disk
46
48
  * @returns Promise<ImageProcessingResult> - Detailed processing information
47
49
  */
48
- export declare function downloadAndProcessImage(fileName: string, localPath: string, imageUrl: string, needsCropping?: boolean, cropTransform?: Transform, requiresImageDimensions?: boolean): Promise<ImageProcessingResult>;
50
+ export declare function downloadAndProcessImage(fileName: string, localPath: string, imageUrl: string, needsCropping?: boolean, cropTransform?: Transform, requiresImageDimensions?: boolean, returnBuffer?: boolean): Promise<ImageProcessingResult>;
49
51
  /**
50
52
  * Create CSS custom properties for image dimensions
51
53
  * @param imagePath - Path to the image file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "figma-metadata-extractor",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Extract metadata and download images from Figma files. A standalone library for accessing Figma design data and downloading frame images programmatically.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",