figma-metadata-extractor 1.0.14 → 1.0.16

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
@@ -22,21 +22,21 @@ npm install figma-metadata-extractor
22
22
  ### Get Metadata with Auto-Downloaded Images (LLM-Ready!)
23
23
 
24
24
  ```typescript
25
- import { getFigmaMetadata } from 'figma-metadata-extractor';
25
+ import { getFigmaMetadata } from "figma-metadata-extractor";
26
26
 
27
27
  // Extract metadata AND automatically download image assets to disk
28
28
  const metadata = await getFigmaMetadata(
29
- 'https://figma.com/file/ABC123/My-Design',
29
+ "https://figma.com/file/ABC123/My-Design",
30
30
  {
31
- apiKey: 'your-figma-api-key',
32
- outputFormat: 'object',
33
- downloadImages: true, // Auto-download image assets
34
- localPath: './assets/images' // Where to save images
35
- }
31
+ apiKey: "your-figma-api-key",
32
+ outputFormat: "object",
33
+ downloadImages: true, // Auto-download image assets
34
+ localPath: "./assets/images", // Where to save images
35
+ },
36
36
  );
37
37
 
38
38
  // Nodes are enriched with downloadedImage property
39
- metadata.nodes.forEach(node => {
39
+ metadata.nodes.forEach((node) => {
40
40
  if (node.downloadedImage) {
41
41
  console.log(node.downloadedImage.filePath);
42
42
  console.log(node.downloadedImage.markdown); // ![name](path)
@@ -47,17 +47,20 @@ metadata.nodes.forEach(node => {
47
47
  ### Get Images as Buffers (No Disk Write)
48
48
 
49
49
  ```typescript
50
- import { getFigmaMetadata, enrichMetadataWithImages } from 'figma-metadata-extractor';
51
- import fs from 'fs/promises';
50
+ import {
51
+ getFigmaMetadata,
52
+ enrichMetadataWithImages,
53
+ } from "figma-metadata-extractor";
54
+ import fs from "fs/promises";
52
55
 
53
56
  // Get metadata with images as ArrayBuffers
54
57
  const result = await getFigmaMetadata(
55
- 'https://figma.com/file/ABC123/My-Design',
58
+ "https://figma.com/file/ABC123/My-Design",
56
59
  {
57
- apiKey: 'your-figma-api-key',
60
+ apiKey: "your-figma-api-key",
58
61
  downloadImages: true,
59
- returnBuffer: true // Get images as ArrayBuffer
60
- }
62
+ returnBuffer: true, // Get images as ArrayBuffer
63
+ },
61
64
  );
62
65
 
63
66
  // Images are returned separately, metadata is not enriched
@@ -75,11 +78,11 @@ for (const image of result.images) {
75
78
 
76
79
  // Optionally enrich metadata with saved file paths
77
80
  const enrichedMetadata = enrichMetadataWithImages(result, savedPaths, {
78
- useRelativePaths: true
81
+ useRelativePaths: true,
79
82
  });
80
83
 
81
84
  // Now nodes have downloadedImage properties
82
- enrichedMetadata.nodes.forEach(node => {
85
+ enrichedMetadata.nodes.forEach((node) => {
83
86
  if (node.downloadedImage) {
84
87
  console.log(node.downloadedImage.markdown);
85
88
  }
@@ -89,15 +92,15 @@ enrichedMetadata.nodes.forEach(node => {
89
92
  ### Get Metadata Only (No Downloads)
90
93
 
91
94
  ```typescript
92
- import { getFigmaMetadata } from 'figma-metadata-extractor';
95
+ import { getFigmaMetadata } from "figma-metadata-extractor";
93
96
 
94
97
  // Extract metadata from a Figma file
95
98
  const metadata = await getFigmaMetadata(
96
- 'https://figma.com/file/ABC123/My-Design',
99
+ "https://figma.com/file/ABC123/My-Design",
97
100
  {
98
- apiKey: 'your-figma-api-key',
99
- outputFormat: 'object' // or 'json' or 'yaml'
100
- }
101
+ apiKey: "your-figma-api-key",
102
+ outputFormat: "object", // or 'json' or 'yaml'
103
+ },
101
104
  );
102
105
 
103
106
  console.log(metadata.nodes); // Array of design nodes
@@ -105,35 +108,35 @@ console.log(metadata.globalVars); // Styles, colors, etc.
105
108
 
106
109
  // Download images from the file
107
110
  const images = await downloadFigmaImages(
108
- 'https://figma.com/file/ABC123/My-Design',
111
+ "https://figma.com/file/ABC123/My-Design",
109
112
  [
110
113
  {
111
- nodeId: '1234:5678',
112
- fileName: 'icon.svg'
114
+ nodeId: "1234:5678",
115
+ fileName: "icon.svg",
113
116
  },
114
117
  {
115
- nodeId: '9876:5432',
116
- fileName: 'hero-image.png'
117
- }
118
+ nodeId: "9876:5432",
119
+ fileName: "hero-image.png",
120
+ },
118
121
  ],
119
122
  {
120
- apiKey: 'your-figma-api-key',
121
- localPath: './assets/images'
122
- }
123
+ apiKey: "your-figma-api-key",
124
+ localPath: "./assets/images",
125
+ },
123
126
  );
124
127
 
125
128
  console.log(images); // Array of download results
126
129
 
127
130
  // Download a single frame image from a Figma URL
128
131
  const frameImage = await downloadFigmaFrameImage(
129
- 'https://figma.com/file/ABC123/My-Design?node-id=1234-5678',
132
+ "https://figma.com/file/ABC123/My-Design?node-id=1234-5678",
130
133
  {
131
- apiKey: 'your-figma-api-key',
132
- localPath: './assets/frames',
133
- fileName: 'my-frame.png',
134
- format: 'png', // or 'svg'
135
- pngScale: 2
136
- }
134
+ apiKey: "your-figma-api-key",
135
+ localPath: "./assets/frames",
136
+ fileName: "my-frame.png",
137
+ format: "png", // or 'svg'
138
+ pngScale: 2,
139
+ },
137
140
  );
138
141
 
139
142
  console.log(frameImage.filePath); // Path to downloaded image
@@ -146,13 +149,14 @@ console.log(frameImage.filePath); // Path to downloaded image
146
149
  Extracts comprehensive metadata from a Figma file including layout, content, visuals, and component information.
147
150
 
148
151
  **Parameters:**
152
+
149
153
  - `figmaUrl` (string): The Figma file URL
150
154
  - `options` (FigmaMetadataOptions): Configuration options
151
155
 
152
156
  **Options:**
153
- - `apiKey?: string` - Figma API key (Personal Access Token)
154
- - `oauthToken?: string` - Figma OAuth Bearer token
155
- - `useOAuth?: boolean` - Whether to use OAuth instead of API key
157
+
158
+ - `apiKey?: string` - Figma API key (Personal Access Token). Either apiKey or oauthToken is required
159
+ - `oauthToken?: string` - Figma OAuth Bearer token. When provided, OAuth is used automatically
156
160
  - `outputFormat?: 'json' | 'yaml' | 'object'` - Output format (default: 'object')
157
161
  - `depth?: number` - Maximum depth to traverse the node tree
158
162
  - `downloadImages?: boolean` - Automatically download image assets and enrich metadata (default: false)
@@ -165,6 +169,7 @@ Extracts comprehensive metadata from a Figma file including layout, content, vis
165
169
  **Returns:** Promise<FigmaMetadataResult | string>
166
170
 
167
171
  **FigmaMetadataResult:**
172
+
168
173
  ```typescript
169
174
  {
170
175
  metadata: any; // File metadata
@@ -175,13 +180,16 @@ Extracts comprehensive metadata from a Figma file including layout, content, vis
175
180
  ```
176
181
 
177
182
  When `downloadImages: true` and `returnBuffer: false`, nodes with image assets will include a `downloadedImage` property:
183
+
178
184
  ```typescript
179
185
  {
180
- filePath: string; // Absolute path
181
- relativePath: string; // Relative path for code
182
- dimensions: { width, height };
183
- markdown: string; // ![name](path)
184
- html: string; // <img src="..." />
186
+ filePath: string; // Absolute path
187
+ relativePath: string; // Relative path for code
188
+ dimensions: {
189
+ width, height;
190
+ }
191
+ markdown: string; // ![name](path)
192
+ html: string; // <img src="..." />
185
193
  }
186
194
  ```
187
195
 
@@ -192,11 +200,13 @@ When `downloadImages: true` and `returnBuffer: true`, images are returned in the
192
200
  Downloads SVG and PNG images from a Figma file.
193
201
 
194
202
  **Parameters:**
203
+
195
204
  - `figmaUrl` (string): The Figma file URL
196
205
  - `nodes` (FigmaImageNode[]): Array of image nodes to download
197
206
  - `options` (FigmaMetadataOptions & FigmaImageOptions): Configuration options
198
207
 
199
208
  **Node Properties:**
209
+
200
210
  - `nodeId: string` - The Figma node ID (format: '1234:5678')
201
211
  - `fileName: string` - Local filename (must end with .png or .svg)
202
212
  - `imageRef?: string` - Image reference for image fills
@@ -206,6 +216,7 @@ Downloads SVG and PNG images from a Figma file.
206
216
  - `filenameSuffix?: string` - Suffix for unique filenames
207
217
 
208
218
  **Additional Options:**
219
+
209
220
  - `pngScale?: number` - Export scale for PNG images (default: 2)
210
221
  - `localPath?: string` - Absolute path to save images (optional if returnBuffer is true)
211
222
  - `returnBuffer?: boolean` - Return images as ArrayBuffer instead of saving to disk (default: false)
@@ -220,6 +231,7 @@ When `returnBuffer` is true, each result will contain a `buffer` property instea
220
231
  Enriches metadata with saved image file paths after saving buffers to disk.
221
232
 
222
233
  **Parameters:**
234
+
223
235
  - `metadata` (FigmaMetadataResult): The metadata result from getFigmaMetadata with returnBuffer: true
224
236
  - `imagePaths` (string[]): Array of file paths where images were saved (must match order of metadata.images)
225
237
  - `options` (object): Configuration options
@@ -229,25 +241,27 @@ Enriches metadata with saved image file paths after saving buffers to disk.
229
241
  **Returns:** FigmaMetadataResult with enriched nodes
230
242
 
231
243
  **Example:**
244
+
232
245
  ```typescript
233
246
  // Get metadata with buffers
234
247
  const result = await getFigmaMetadata(url, {
235
- apiKey: 'key',
248
+ apiKey: "key",
236
249
  downloadImages: true,
237
- returnBuffer: true
250
+ returnBuffer: true,
238
251
  });
239
252
 
240
253
  // Save buffers to disk
241
254
  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
- )
255
+ result.images.map((img, i) =>
256
+ fs
257
+ .writeFile(`./images/img-${i}.png`, Buffer.from(img.buffer))
258
+ .then(() => `./images/img-${i}.png`),
259
+ ),
246
260
  );
247
261
 
248
262
  // Enrich metadata with file paths
249
263
  const enriched = enrichMetadataWithImages(result, paths, {
250
- useRelativePaths: true
264
+ useRelativePaths: true,
251
265
  });
252
266
  ```
253
267
 
@@ -256,13 +270,14 @@ const enriched = enrichMetadataWithImages(result, paths, {
256
270
  Downloads a single frame image from a Figma URL that contains a node-id parameter.
257
271
 
258
272
  **Parameters:**
273
+
259
274
  - `figmaUrl` (string): The Figma URL with node-id parameter (e.g., `https://figma.com/file/ABC123/My-Design?node-id=1234-5678`)
260
275
  - `options` (FigmaFrameImageOptions): Configuration options
261
276
 
262
277
  **Options:**
263
- - `apiKey?: string` - Figma API key (Personal Access Token)
264
- - `oauthToken?: string` - Figma OAuth Bearer token
265
- - `useOAuth?: boolean` - Whether to use OAuth instead of API key
278
+
279
+ - `apiKey?: string` - Figma API key (Personal Access Token). Either apiKey or oauthToken is required
280
+ - `oauthToken?: string` - Figma OAuth Bearer token. When provided, OAuth is used automatically
266
281
  - `localPath?: string` - Absolute path to save the image (optional if returnBuffer is true)
267
282
  - `fileName?: string` - Local filename (must end with .png or .svg, optional if returnBuffer is true)
268
283
  - `format?: 'png' | 'svg'` - Image format to download (default: 'png')
@@ -273,6 +288,7 @@ Downloads a single frame image from a Figma URL that contains a node-id paramete
273
288
  **Returns:** Promise<FigmaImageResult>
274
289
 
275
290
  **Result Properties:**
291
+
276
292
  - `filePath?: string` - Path to saved file (only when returnBuffer is false)
277
293
  - `buffer?: ArrayBuffer` - Image data as ArrayBuffer (only when returnBuffer is true)
278
294
  - `finalDimensions: { width: number; height: number }` - Image dimensions
@@ -281,17 +297,28 @@ Downloads a single frame image from a Figma URL that contains a node-id paramete
281
297
 
282
298
  ## Authentication
283
299
 
284
- You need either a Figma API key or OAuth token:
300
+ You need either a Figma API key (Personal Access Token) or an OAuth token:
285
301
 
286
302
  ### API Key (Personal Access Token)
303
+
287
304
  1. Go to Figma → Settings → Account → Personal Access Tokens
288
305
  2. Generate a new token
289
306
  3. Use it in the `apiKey` option
290
307
 
308
+ ```typescript
309
+ getFigmaMetadata(url, { apiKey: "figd_xxx..." });
310
+ ```
311
+
291
312
  ### OAuth Token
313
+
292
314
  1. Set up Figma OAuth in your application
293
315
  2. Use the bearer token in the `oauthToken` option
294
- 3. Set `useOAuth: true`
316
+
317
+ ```typescript
318
+ getFigmaMetadata(url, { oauthToken: "figu_xxx..." });
319
+ ```
320
+
321
+ **Note:** When `oauthToken` is provided, OAuth (Bearer auth) is used automatically. If both `apiKey` and `oauthToken` are provided, `oauthToken` takes precedence.
295
322
 
296
323
  ## Usage Examples
297
324
 
@@ -300,22 +327,25 @@ You need either a Figma API key or OAuth token:
300
327
  The easiest way to download a frame image is to copy the Figma URL directly from your browser when viewing a specific frame:
301
328
 
302
329
  ```typescript
303
- import { downloadFigmaFrameImage } from 'figma-metadata-extractor';
330
+ import { downloadFigmaFrameImage } from "figma-metadata-extractor";
304
331
 
305
332
  // Copy this URL from Figma when viewing a frame
306
- const figmaUrl = 'https://www.figma.com/design/ABC123/My-Design?node-id=1234-5678&t=xyz123';
333
+ const figmaUrl =
334
+ "https://www.figma.com/design/ABC123/My-Design?node-id=1234-5678&t=xyz123";
307
335
 
308
336
  // Save to disk
309
337
  const result = await downloadFigmaFrameImage(figmaUrl, {
310
- apiKey: 'your-figma-api-key',
311
- localPath: './downloads',
312
- fileName: 'my-frame.png',
313
- format: 'png',
314
- pngScale: 2 // High resolution
338
+ apiKey: "your-figma-api-key",
339
+ localPath: "./downloads",
340
+ fileName: "my-frame.png",
341
+ format: "png",
342
+ pngScale: 2, // High resolution
315
343
  });
316
344
 
317
345
  console.log(`Downloaded to: ${result.filePath}`);
318
- console.log(`Dimensions: ${result.finalDimensions.width}x${result.finalDimensions.height}`);
346
+ console.log(
347
+ `Dimensions: ${result.finalDimensions.width}x${result.finalDimensions.height}`,
348
+ );
319
349
  ```
320
350
 
321
351
  ### Get Frame Image as ArrayBuffer (No Disk Write)
@@ -323,19 +353,22 @@ console.log(`Dimensions: ${result.finalDimensions.width}x${result.finalDimension
323
353
  If you want to process the image in memory without saving to disk:
324
354
 
325
355
  ```typescript
326
- import { downloadFigmaFrameImage } from 'figma-metadata-extractor';
356
+ import { downloadFigmaFrameImage } from "figma-metadata-extractor";
327
357
 
328
- const figmaUrl = 'https://www.figma.com/design/ABC123/My-Design?node-id=1234-5678';
358
+ const figmaUrl =
359
+ "https://www.figma.com/design/ABC123/My-Design?node-id=1234-5678";
329
360
 
330
361
  // Get as ArrayBuffer
331
362
  const result = await downloadFigmaFrameImage(figmaUrl, {
332
- apiKey: 'your-figma-api-key',
363
+ apiKey: "your-figma-api-key",
333
364
  returnBuffer: true,
334
- format: 'png'
365
+ format: "png",
335
366
  });
336
367
 
337
368
  console.log(`Buffer size: ${result.buffer.byteLength} bytes`);
338
- console.log(`Dimensions: ${result.finalDimensions.width}x${result.finalDimensions.height}`);
369
+ console.log(
370
+ `Dimensions: ${result.finalDimensions.width}x${result.finalDimensions.height}`,
371
+ );
339
372
 
340
373
  // Use the buffer directly (e.g., upload to cloud storage, process with sharp, etc.)
341
374
  // const processedImage = await sharp(Buffer.from(result.buffer)).resize(100, 100).toBuffer();
@@ -344,39 +377,39 @@ console.log(`Dimensions: ${result.finalDimensions.width}x${result.finalDimension
344
377
  ### Download Multiple Frame Images
345
378
 
346
379
  ```typescript
347
- import { downloadFigmaImages } from 'figma-metadata-extractor';
380
+ import { downloadFigmaImages } from "figma-metadata-extractor";
348
381
 
349
382
  // For multiple frames, use the batch download function
350
383
  const results = await downloadFigmaImages(
351
- 'https://figma.com/file/ABC123/My-Design',
384
+ "https://figma.com/file/ABC123/My-Design",
352
385
  [
353
- { nodeId: '1234:5678', fileName: 'frame1.png' },
354
- { nodeId: '9876:5432', fileName: 'frame2.svg' },
355
- { nodeId: '1111:2222', fileName: 'frame3.png' }
386
+ { nodeId: "1234:5678", fileName: "frame1.png" },
387
+ { nodeId: "9876:5432", fileName: "frame2.svg" },
388
+ { nodeId: "1111:2222", fileName: "frame3.png" },
356
389
  ],
357
390
  {
358
- apiKey: 'your-figma-api-key',
359
- localPath: './frames'
360
- }
391
+ apiKey: "your-figma-api-key",
392
+ localPath: "./frames",
393
+ },
361
394
  );
362
395
  ```
363
396
 
364
397
  ### Download Multiple Images as Buffers
365
398
 
366
399
  ```typescript
367
- import { downloadFigmaImages } from 'figma-metadata-extractor';
400
+ import { downloadFigmaImages } from "figma-metadata-extractor";
368
401
 
369
402
  // Get multiple images as ArrayBuffers
370
403
  const results = await downloadFigmaImages(
371
- 'https://figma.com/file/ABC123/My-Design',
404
+ "https://figma.com/file/ABC123/My-Design",
372
405
  [
373
- { nodeId: '1234:5678', fileName: 'frame1.png' },
374
- { nodeId: '9876:5432', fileName: 'frame2.png' }
406
+ { nodeId: "1234:5678", fileName: "frame1.png" },
407
+ { nodeId: "9876:5432", fileName: "frame2.png" },
375
408
  ],
376
409
  {
377
- apiKey: 'your-figma-api-key',
378
- returnBuffer: true
379
- }
410
+ apiKey: "your-figma-api-key",
411
+ returnBuffer: true,
412
+ },
380
413
  );
381
414
 
382
415
  // Process each buffer
@@ -391,20 +424,20 @@ results.forEach((result, index) => {
391
424
  The library also exports the underlying extractor system for custom processing:
392
425
 
393
426
  ```typescript
394
- import {
395
- simplifyRawFigmaObject,
427
+ import {
428
+ simplifyRawFigmaObject,
396
429
  allExtractors,
397
430
  layoutExtractor,
398
- textExtractor
399
- } from 'figma-metadata-extractor';
431
+ textExtractor,
432
+ } from "figma-metadata-extractor";
400
433
 
401
434
  // Use specific extractors
402
- const customResult = simplifyRawFigmaObject(
403
- rawFigmaResponse,
404
- [layoutExtractor, textExtractor]
405
- );
435
+ const customResult = simplifyRawFigmaObject(rawFigmaResponse, [
436
+ layoutExtractor,
437
+ textExtractor,
438
+ ]);
406
439
  ```
407
440
 
408
441
  ## License
409
442
 
410
- MIT
443
+ MIT
package/dist/index.cjs CHANGED
@@ -386,10 +386,10 @@ class FigmaService {
386
386
  oauthToken;
387
387
  useOAuth;
388
388
  baseUrl = "https://api.figma.com/v1";
389
- constructor({ figmaApiKey, figmaOAuthToken, useOAuth }) {
390
- this.apiKey = figmaApiKey || "";
391
- this.oauthToken = figmaOAuthToken || "";
392
- this.useOAuth = !!useOAuth && !!this.oauthToken;
389
+ constructor({ figmaApiKey, figmaOAuthToken }) {
390
+ this.apiKey = figmaApiKey ?? "";
391
+ this.oauthToken = figmaOAuthToken ?? "";
392
+ this.useOAuth = !!this.oauthToken;
393
393
  }
394
394
  getAuthHeaders() {
395
395
  if (this.useOAuth) {
@@ -499,143 +499,67 @@ class FigmaService {
499
499
  );
500
500
  }
501
501
  }
502
- const downloadPromises = [];
503
- const imageFills = items.filter(
504
- (item) => !!item.imageRef
505
- );
506
- const renderNodes = items.filter(
507
- (item) => !!item.nodeId
508
- );
509
- if (imageFills.length > 0) {
510
- const fillUrls = await this.getImageFillUrls(fileKey);
511
- const fillTasks = imageFills.map(
512
- ({
513
- imageRef,
514
- fileName,
515
- needsCropping,
516
- cropTransform,
517
- requiresImageDimensions
518
- }) => {
519
- const imageUrl = fillUrls[imageRef];
520
- if (!imageUrl) {
521
- Logger.log(
522
- `Skipping image fill with missing URL for imageRef: ${imageRef}`
523
- );
524
- return null;
525
- }
526
- return () => downloadAndProcessImage(
527
- fileName,
528
- resolvedPath,
529
- imageUrl,
530
- needsCropping,
531
- cropTransform,
532
- requiresImageDimensions,
533
- returnBuffer
534
- );
502
+ const imageRefs = [
503
+ ...new Set(items.map((i) => i.imageRef).filter((r) => !!r))
504
+ ];
505
+ const pngNodeIds = items.filter(
506
+ (i) => !i.imageRef && i.nodeId && !i.fileName.toLowerCase().endsWith(".svg")
507
+ ).map((i) => i.nodeId);
508
+ const svgNodeIds = items.filter(
509
+ (i) => !i.imageRef && i.nodeId && i.fileName.toLowerCase().endsWith(".svg")
510
+ ).map((i) => i.nodeId);
511
+ const [fillUrls, pngUrls, svgUrls] = await Promise.all([
512
+ imageRefs.length > 0 ? this.getImageFillUrls(fileKey) : Promise.resolve({}),
513
+ pngNodeIds.length > 0 ? this.getNodeRenderUrls(fileKey, pngNodeIds, "png", { pngScale }) : Promise.resolve({}),
514
+ svgNodeIds.length > 0 ? this.getNodeRenderUrls(fileKey, svgNodeIds, "svg", { svgOptions }) : Promise.resolve({})
515
+ ]);
516
+ const tasks = items.map((item) => {
517
+ const {
518
+ imageRef,
519
+ nodeId,
520
+ fileName,
521
+ needsCropping,
522
+ cropTransform,
523
+ requiresImageDimensions
524
+ } = item;
525
+ let imageUrl;
526
+ if (imageRef) {
527
+ imageUrl = fillUrls[imageRef];
528
+ } else if (nodeId) {
529
+ if (fileName.toLowerCase().endsWith(".svg")) {
530
+ imageUrl = svgUrls[nodeId];
531
+ } else {
532
+ imageUrl = pngUrls[nodeId];
535
533
  }
536
- ).filter(
537
- (task) => task !== null
538
- );
539
- if (fillTasks.length > 0) {
540
- downloadPromises.push(runWithConcurrency(fillTasks, CONCURRENCY_LIMIT));
541
534
  }
542
- }
543
- if (renderNodes.length > 0) {
544
- const pngNodes = renderNodes.filter(
545
- (node) => !node.fileName.toLowerCase().endsWith(".svg")
546
- );
547
- const svgNodes = renderNodes.filter(
548
- (node) => node.fileName.toLowerCase().endsWith(".svg")
549
- );
550
- if (pngNodes.length > 0) {
551
- const pngUrls = await this.getNodeRenderUrls(
552
- fileKey,
553
- pngNodes.map((n) => n.nodeId),
554
- "png",
555
- { pngScale }
556
- );
557
- const pngTasks = pngNodes.map(
558
- ({
559
- nodeId,
560
- fileName,
561
- needsCropping,
562
- cropTransform,
563
- requiresImageDimensions
564
- }) => {
565
- const imageUrl = pngUrls[nodeId];
566
- if (!imageUrl) {
567
- Logger.log(
568
- `Skipping PNG render with missing URL for nodeId: ${nodeId}`
569
- );
570
- return null;
571
- }
572
- return () => downloadAndProcessImage(
573
- fileName,
574
- resolvedPath,
575
- imageUrl,
576
- needsCropping,
577
- cropTransform,
578
- requiresImageDimensions,
579
- returnBuffer
580
- ).then(
581
- (result) => ({ ...result, nodeId })
582
- );
583
- }
584
- ).filter(
585
- (task) => task !== null
586
- );
587
- if (pngTasks.length > 0) {
588
- downloadPromises.push(
589
- runWithConcurrency(pngTasks, CONCURRENCY_LIMIT)
535
+ if (!imageUrl) {
536
+ return () => {
537
+ Logger.log(
538
+ `Skipping missing image for ${imageRef ? `imageRef: ${imageRef}` : `nodeId: ${nodeId}`}`
590
539
  );
591
- }
592
- }
593
- if (svgNodes.length > 0) {
594
- const svgUrls = await this.getNodeRenderUrls(
595
- fileKey,
596
- svgNodes.map((n) => n.nodeId),
597
- "svg",
598
- { svgOptions }
599
- );
600
- const svgTasks = svgNodes.map(
601
- ({
540
+ return Promise.resolve({
602
541
  nodeId,
603
- fileName,
604
- needsCropping,
605
- cropTransform,
606
- requiresImageDimensions
607
- }) => {
608
- const imageUrl = svgUrls[nodeId];
609
- if (!imageUrl) {
610
- Logger.log(
611
- `Skipping SVG render with missing URL for nodeId: ${nodeId}`
612
- );
613
- return null;
614
- }
615
- return () => downloadAndProcessImage(
616
- fileName,
617
- resolvedPath,
618
- imageUrl,
619
- needsCropping,
620
- cropTransform,
621
- requiresImageDimensions,
622
- returnBuffer
623
- ).then(
624
- (result) => ({ ...result, nodeId })
625
- );
626
- }
627
- ).filter(
628
- (task) => task !== null
629
- );
630
- if (svgTasks.length > 0) {
631
- downloadPromises.push(
632
- runWithConcurrency(svgTasks, CONCURRENCY_LIMIT)
633
- );
634
- }
542
+ originalDimensions: { width: 0, height: 0 },
543
+ finalDimensions: { width: 0, height: 0 },
544
+ wasCropped: false,
545
+ processingLog: ["Skipped: URL not found"]
546
+ });
547
+ };
635
548
  }
636
- }
637
- const results = await Promise.all(downloadPromises);
638
- return results.flat();
549
+ return async () => {
550
+ const result = await downloadAndProcessImage(
551
+ fileName,
552
+ resolvedPath,
553
+ imageUrl,
554
+ needsCropping,
555
+ cropTransform,
556
+ requiresImageDimensions,
557
+ returnBuffer
558
+ );
559
+ return { ...result, nodeId };
560
+ };
561
+ });
562
+ return runWithConcurrency(tasks, CONCURRENCY_LIMIT);
639
563
  }
640
564
  /**
641
565
  * Get raw Figma API response for a file (for use with flexible extractors)
@@ -1525,7 +1449,6 @@ async function getFigmaMetadata(figmaUrl, options = {}) {
1525
1449
  const {
1526
1450
  apiKey,
1527
1451
  oauthToken,
1528
- useOAuth = false,
1529
1452
  outputFormat = "object",
1530
1453
  depth,
1531
1454
  downloadImages = false,
@@ -1548,9 +1471,8 @@ async function getFigmaMetadata(figmaUrl, options = {}) {
1548
1471
  const nodeIdMatch = figmaUrl.match(/node-id=([^&]+)/);
1549
1472
  const nodeId = nodeIdMatch ? nodeIdMatch[1].replace(/-/g, ":") : void 0;
1550
1473
  const figmaService = new FigmaService({
1551
- figmaApiKey: apiKey || "",
1552
- figmaOAuthToken: oauthToken || "",
1553
- useOAuth: useOAuth && !!oauthToken
1474
+ figmaApiKey: apiKey,
1475
+ figmaOAuthToken: oauthToken
1554
1476
  });
1555
1477
  try {
1556
1478
  Logger.log(
@@ -1598,7 +1520,11 @@ async function getFigmaMetadata(figmaUrl, options = {}) {
1598
1520
  if (imageAssets.length > 0) {
1599
1521
  const imageNodes = imageAssets.map((asset) => ({
1600
1522
  nodeId: asset.id,
1601
- fileName: sanitizeFileName(asset.name) + `.${imageFormat}`
1523
+ imageRef: asset.imageRef,
1524
+ fileName: sanitizeFileName(asset.name) + (asset.filenameSuffix ? `-${asset.filenameSuffix}` : "") + `.${imageFormat}`,
1525
+ needsCropping: asset.needsCropping,
1526
+ cropTransform: asset.cropTransform,
1527
+ requiresImageDimensions: asset.requiresImageDimensions
1602
1528
  }));
1603
1529
  const downloadResults = await figmaService.downloadImages(
1604
1530
  fileKey,
@@ -1645,7 +1571,6 @@ async function downloadFigmaImages(figmaUrl, nodes, options) {
1645
1571
  const {
1646
1572
  apiKey,
1647
1573
  oauthToken,
1648
- useOAuth = false,
1649
1574
  pngScale = 2,
1650
1575
  localPath,
1651
1576
  enableLogging = false,
@@ -1664,9 +1589,8 @@ async function downloadFigmaImages(figmaUrl, nodes, options) {
1664
1589
  }
1665
1590
  const fileKey = urlMatch[2];
1666
1591
  const figmaService = new FigmaService({
1667
- figmaApiKey: apiKey || "",
1668
- figmaOAuthToken: oauthToken || "",
1669
- useOAuth: useOAuth && !!oauthToken
1592
+ figmaApiKey: apiKey,
1593
+ figmaOAuthToken: oauthToken
1670
1594
  });
1671
1595
  try {
1672
1596
  const processedNodes = nodes.map((node) => ({
@@ -1694,7 +1618,6 @@ async function downloadFigmaFrameImage(figmaUrl, options) {
1694
1618
  const {
1695
1619
  apiKey,
1696
1620
  oauthToken,
1697
- useOAuth = false,
1698
1621
  pngScale = 2,
1699
1622
  localPath,
1700
1623
  fileName,
@@ -1732,9 +1655,8 @@ async function downloadFigmaFrameImage(figmaUrl, options) {
1732
1655
  }
1733
1656
  }
1734
1657
  const figmaService = new FigmaService({
1735
- figmaApiKey: apiKey || "",
1736
- figmaOAuthToken: oauthToken || "",
1737
- useOAuth: useOAuth && !!oauthToken
1658
+ figmaApiKey: apiKey,
1659
+ figmaOAuthToken: oauthToken
1738
1660
  });
1739
1661
  try {
1740
1662
  Logger.log(
@@ -1837,28 +1759,37 @@ function sanitizeFileName(name) {
1837
1759
  return name.replace(/[^a-z0-9]/gi, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").toLowerCase();
1838
1760
  }
1839
1761
  function findImageAssets(nodes, globalVars) {
1840
- const images = [];
1762
+ const assets = [];
1841
1763
  function traverse(node) {
1842
- const isImageAsset = node.type === "IMAGE-SVG" || hasImageFill(node, globalVars);
1843
- if (isImageAsset) {
1844
- images.push(node);
1764
+ if (node.type === "IMAGE-SVG") {
1765
+ assets.push({
1766
+ ...node,
1767
+ id: node.id,
1768
+ name: node.name,
1769
+ type: "render"
1770
+ });
1771
+ } else if (node.fills && typeof node.fills === "string") {
1772
+ const fillData = globalVars?.styles?.[node.fills];
1773
+ if (Array.isArray(fillData)) {
1774
+ const imageFill = fillData.find((f) => f.type === "IMAGE");
1775
+ if (imageFill) {
1776
+ assets.push({
1777
+ ...node,
1778
+ id: node.id,
1779
+ name: node.name,
1780
+ type: "fill",
1781
+ imageRef: imageFill.imageRef,
1782
+ ...imageFill.imageDownloadArguments
1783
+ });
1784
+ }
1785
+ }
1845
1786
  }
1846
1787
  if (node.children && Array.isArray(node.children)) {
1847
1788
  node.children.forEach(traverse);
1848
1789
  }
1849
1790
  }
1850
1791
  nodes.forEach(traverse);
1851
- return images;
1852
- }
1853
- function hasImageFill(node, globalVars) {
1854
- if (!node.fills || typeof node.fills !== "string") {
1855
- return false;
1856
- }
1857
- const fillData = globalVars?.styles?.[node.fills];
1858
- if (!fillData || !Array.isArray(fillData)) {
1859
- return false;
1860
- }
1861
- return fillData.some((fill) => fill?.type === "IMAGE");
1792
+ return assets;
1862
1793
  }
1863
1794
  function enrichNodesWithImages(nodes, imageAssets, downloadResults, useRelativePaths = true, localPath) {
1864
1795
  const imageMap = /* @__PURE__ */ new Map();
package/dist/index.js CHANGED
@@ -384,10 +384,10 @@ class FigmaService {
384
384
  oauthToken;
385
385
  useOAuth;
386
386
  baseUrl = "https://api.figma.com/v1";
387
- constructor({ figmaApiKey, figmaOAuthToken, useOAuth }) {
388
- this.apiKey = figmaApiKey || "";
389
- this.oauthToken = figmaOAuthToken || "";
390
- this.useOAuth = !!useOAuth && !!this.oauthToken;
387
+ constructor({ figmaApiKey, figmaOAuthToken }) {
388
+ this.apiKey = figmaApiKey ?? "";
389
+ this.oauthToken = figmaOAuthToken ?? "";
390
+ this.useOAuth = !!this.oauthToken;
391
391
  }
392
392
  getAuthHeaders() {
393
393
  if (this.useOAuth) {
@@ -497,143 +497,67 @@ class FigmaService {
497
497
  );
498
498
  }
499
499
  }
500
- const downloadPromises = [];
501
- const imageFills = items.filter(
502
- (item) => !!item.imageRef
503
- );
504
- const renderNodes = items.filter(
505
- (item) => !!item.nodeId
506
- );
507
- if (imageFills.length > 0) {
508
- const fillUrls = await this.getImageFillUrls(fileKey);
509
- const fillTasks = imageFills.map(
510
- ({
511
- imageRef,
512
- fileName,
513
- needsCropping,
514
- cropTransform,
515
- requiresImageDimensions
516
- }) => {
517
- const imageUrl = fillUrls[imageRef];
518
- if (!imageUrl) {
519
- Logger.log(
520
- `Skipping image fill with missing URL for imageRef: ${imageRef}`
521
- );
522
- return null;
523
- }
524
- return () => downloadAndProcessImage(
525
- fileName,
526
- resolvedPath,
527
- imageUrl,
528
- needsCropping,
529
- cropTransform,
530
- requiresImageDimensions,
531
- returnBuffer
532
- );
500
+ const imageRefs = [
501
+ ...new Set(items.map((i) => i.imageRef).filter((r) => !!r))
502
+ ];
503
+ const pngNodeIds = items.filter(
504
+ (i) => !i.imageRef && i.nodeId && !i.fileName.toLowerCase().endsWith(".svg")
505
+ ).map((i) => i.nodeId);
506
+ const svgNodeIds = items.filter(
507
+ (i) => !i.imageRef && i.nodeId && i.fileName.toLowerCase().endsWith(".svg")
508
+ ).map((i) => i.nodeId);
509
+ const [fillUrls, pngUrls, svgUrls] = await Promise.all([
510
+ imageRefs.length > 0 ? this.getImageFillUrls(fileKey) : Promise.resolve({}),
511
+ pngNodeIds.length > 0 ? this.getNodeRenderUrls(fileKey, pngNodeIds, "png", { pngScale }) : Promise.resolve({}),
512
+ svgNodeIds.length > 0 ? this.getNodeRenderUrls(fileKey, svgNodeIds, "svg", { svgOptions }) : Promise.resolve({})
513
+ ]);
514
+ const tasks = items.map((item) => {
515
+ const {
516
+ imageRef,
517
+ nodeId,
518
+ fileName,
519
+ needsCropping,
520
+ cropTransform,
521
+ requiresImageDimensions
522
+ } = item;
523
+ let imageUrl;
524
+ if (imageRef) {
525
+ imageUrl = fillUrls[imageRef];
526
+ } else if (nodeId) {
527
+ if (fileName.toLowerCase().endsWith(".svg")) {
528
+ imageUrl = svgUrls[nodeId];
529
+ } else {
530
+ imageUrl = pngUrls[nodeId];
533
531
  }
534
- ).filter(
535
- (task) => task !== null
536
- );
537
- if (fillTasks.length > 0) {
538
- downloadPromises.push(runWithConcurrency(fillTasks, CONCURRENCY_LIMIT));
539
532
  }
540
- }
541
- if (renderNodes.length > 0) {
542
- const pngNodes = renderNodes.filter(
543
- (node) => !node.fileName.toLowerCase().endsWith(".svg")
544
- );
545
- const svgNodes = renderNodes.filter(
546
- (node) => node.fileName.toLowerCase().endsWith(".svg")
547
- );
548
- if (pngNodes.length > 0) {
549
- const pngUrls = await this.getNodeRenderUrls(
550
- fileKey,
551
- pngNodes.map((n) => n.nodeId),
552
- "png",
553
- { pngScale }
554
- );
555
- const pngTasks = pngNodes.map(
556
- ({
557
- nodeId,
558
- fileName,
559
- needsCropping,
560
- cropTransform,
561
- requiresImageDimensions
562
- }) => {
563
- const imageUrl = pngUrls[nodeId];
564
- if (!imageUrl) {
565
- Logger.log(
566
- `Skipping PNG render with missing URL for nodeId: ${nodeId}`
567
- );
568
- return null;
569
- }
570
- return () => downloadAndProcessImage(
571
- fileName,
572
- resolvedPath,
573
- imageUrl,
574
- needsCropping,
575
- cropTransform,
576
- requiresImageDimensions,
577
- returnBuffer
578
- ).then(
579
- (result) => ({ ...result, nodeId })
580
- );
581
- }
582
- ).filter(
583
- (task) => task !== null
584
- );
585
- if (pngTasks.length > 0) {
586
- downloadPromises.push(
587
- runWithConcurrency(pngTasks, CONCURRENCY_LIMIT)
533
+ if (!imageUrl) {
534
+ return () => {
535
+ Logger.log(
536
+ `Skipping missing image for ${imageRef ? `imageRef: ${imageRef}` : `nodeId: ${nodeId}`}`
588
537
  );
589
- }
590
- }
591
- if (svgNodes.length > 0) {
592
- const svgUrls = await this.getNodeRenderUrls(
593
- fileKey,
594
- svgNodes.map((n) => n.nodeId),
595
- "svg",
596
- { svgOptions }
597
- );
598
- const svgTasks = svgNodes.map(
599
- ({
538
+ return Promise.resolve({
600
539
  nodeId,
601
- fileName,
602
- needsCropping,
603
- cropTransform,
604
- requiresImageDimensions
605
- }) => {
606
- const imageUrl = svgUrls[nodeId];
607
- if (!imageUrl) {
608
- Logger.log(
609
- `Skipping SVG render with missing URL for nodeId: ${nodeId}`
610
- );
611
- return null;
612
- }
613
- return () => downloadAndProcessImage(
614
- fileName,
615
- resolvedPath,
616
- imageUrl,
617
- needsCropping,
618
- cropTransform,
619
- requiresImageDimensions,
620
- returnBuffer
621
- ).then(
622
- (result) => ({ ...result, nodeId })
623
- );
624
- }
625
- ).filter(
626
- (task) => task !== null
627
- );
628
- if (svgTasks.length > 0) {
629
- downloadPromises.push(
630
- runWithConcurrency(svgTasks, CONCURRENCY_LIMIT)
631
- );
632
- }
540
+ originalDimensions: { width: 0, height: 0 },
541
+ finalDimensions: { width: 0, height: 0 },
542
+ wasCropped: false,
543
+ processingLog: ["Skipped: URL not found"]
544
+ });
545
+ };
633
546
  }
634
- }
635
- const results = await Promise.all(downloadPromises);
636
- return results.flat();
547
+ return async () => {
548
+ const result = await downloadAndProcessImage(
549
+ fileName,
550
+ resolvedPath,
551
+ imageUrl,
552
+ needsCropping,
553
+ cropTransform,
554
+ requiresImageDimensions,
555
+ returnBuffer
556
+ );
557
+ return { ...result, nodeId };
558
+ };
559
+ });
560
+ return runWithConcurrency(tasks, CONCURRENCY_LIMIT);
637
561
  }
638
562
  /**
639
563
  * Get raw Figma API response for a file (for use with flexible extractors)
@@ -1523,7 +1447,6 @@ async function getFigmaMetadata(figmaUrl, options = {}) {
1523
1447
  const {
1524
1448
  apiKey,
1525
1449
  oauthToken,
1526
- useOAuth = false,
1527
1450
  outputFormat = "object",
1528
1451
  depth,
1529
1452
  downloadImages = false,
@@ -1546,9 +1469,8 @@ async function getFigmaMetadata(figmaUrl, options = {}) {
1546
1469
  const nodeIdMatch = figmaUrl.match(/node-id=([^&]+)/);
1547
1470
  const nodeId = nodeIdMatch ? nodeIdMatch[1].replace(/-/g, ":") : void 0;
1548
1471
  const figmaService = new FigmaService({
1549
- figmaApiKey: apiKey || "",
1550
- figmaOAuthToken: oauthToken || "",
1551
- useOAuth: useOAuth && !!oauthToken
1472
+ figmaApiKey: apiKey,
1473
+ figmaOAuthToken: oauthToken
1552
1474
  });
1553
1475
  try {
1554
1476
  Logger.log(
@@ -1596,7 +1518,11 @@ async function getFigmaMetadata(figmaUrl, options = {}) {
1596
1518
  if (imageAssets.length > 0) {
1597
1519
  const imageNodes = imageAssets.map((asset) => ({
1598
1520
  nodeId: asset.id,
1599
- fileName: sanitizeFileName(asset.name) + `.${imageFormat}`
1521
+ imageRef: asset.imageRef,
1522
+ fileName: sanitizeFileName(asset.name) + (asset.filenameSuffix ? `-${asset.filenameSuffix}` : "") + `.${imageFormat}`,
1523
+ needsCropping: asset.needsCropping,
1524
+ cropTransform: asset.cropTransform,
1525
+ requiresImageDimensions: asset.requiresImageDimensions
1600
1526
  }));
1601
1527
  const downloadResults = await figmaService.downloadImages(
1602
1528
  fileKey,
@@ -1643,7 +1569,6 @@ async function downloadFigmaImages(figmaUrl, nodes, options) {
1643
1569
  const {
1644
1570
  apiKey,
1645
1571
  oauthToken,
1646
- useOAuth = false,
1647
1572
  pngScale = 2,
1648
1573
  localPath,
1649
1574
  enableLogging = false,
@@ -1662,9 +1587,8 @@ async function downloadFigmaImages(figmaUrl, nodes, options) {
1662
1587
  }
1663
1588
  const fileKey = urlMatch[2];
1664
1589
  const figmaService = new FigmaService({
1665
- figmaApiKey: apiKey || "",
1666
- figmaOAuthToken: oauthToken || "",
1667
- useOAuth: useOAuth && !!oauthToken
1590
+ figmaApiKey: apiKey,
1591
+ figmaOAuthToken: oauthToken
1668
1592
  });
1669
1593
  try {
1670
1594
  const processedNodes = nodes.map((node) => ({
@@ -1692,7 +1616,6 @@ async function downloadFigmaFrameImage(figmaUrl, options) {
1692
1616
  const {
1693
1617
  apiKey,
1694
1618
  oauthToken,
1695
- useOAuth = false,
1696
1619
  pngScale = 2,
1697
1620
  localPath,
1698
1621
  fileName,
@@ -1730,9 +1653,8 @@ async function downloadFigmaFrameImage(figmaUrl, options) {
1730
1653
  }
1731
1654
  }
1732
1655
  const figmaService = new FigmaService({
1733
- figmaApiKey: apiKey || "",
1734
- figmaOAuthToken: oauthToken || "",
1735
- useOAuth: useOAuth && !!oauthToken
1656
+ figmaApiKey: apiKey,
1657
+ figmaOAuthToken: oauthToken
1736
1658
  });
1737
1659
  try {
1738
1660
  Logger.log(
@@ -1835,28 +1757,37 @@ function sanitizeFileName(name) {
1835
1757
  return name.replace(/[^a-z0-9]/gi, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").toLowerCase();
1836
1758
  }
1837
1759
  function findImageAssets(nodes, globalVars) {
1838
- const images = [];
1760
+ const assets = [];
1839
1761
  function traverse(node) {
1840
- const isImageAsset = node.type === "IMAGE-SVG" || hasImageFill(node, globalVars);
1841
- if (isImageAsset) {
1842
- images.push(node);
1762
+ if (node.type === "IMAGE-SVG") {
1763
+ assets.push({
1764
+ ...node,
1765
+ id: node.id,
1766
+ name: node.name,
1767
+ type: "render"
1768
+ });
1769
+ } else if (node.fills && typeof node.fills === "string") {
1770
+ const fillData = globalVars?.styles?.[node.fills];
1771
+ if (Array.isArray(fillData)) {
1772
+ const imageFill = fillData.find((f) => f.type === "IMAGE");
1773
+ if (imageFill) {
1774
+ assets.push({
1775
+ ...node,
1776
+ id: node.id,
1777
+ name: node.name,
1778
+ type: "fill",
1779
+ imageRef: imageFill.imageRef,
1780
+ ...imageFill.imageDownloadArguments
1781
+ });
1782
+ }
1783
+ }
1843
1784
  }
1844
1785
  if (node.children && Array.isArray(node.children)) {
1845
1786
  node.children.forEach(traverse);
1846
1787
  }
1847
1788
  }
1848
1789
  nodes.forEach(traverse);
1849
- return images;
1850
- }
1851
- function hasImageFill(node, globalVars) {
1852
- if (!node.fills || typeof node.fills !== "string") {
1853
- return false;
1854
- }
1855
- const fillData = globalVars?.styles?.[node.fills];
1856
- if (!fillData || !Array.isArray(fillData)) {
1857
- return false;
1858
- }
1859
- return fillData.some((fill) => fill?.type === "IMAGE");
1790
+ return assets;
1860
1791
  }
1861
1792
  function enrichNodesWithImages(nodes, imageAssets, downloadResults, useRelativePaths = true, localPath) {
1862
1793
  const imageMap = /* @__PURE__ */ new Map();
package/dist/lib.d.ts CHANGED
@@ -1,10 +1,8 @@
1
1
  export interface FigmaMetadataOptions {
2
- /** The Figma API key (Personal Access Token) */
2
+ /** The Figma API key (Personal Access Token). Either apiKey or oauthToken must be provided. */
3
3
  apiKey?: string;
4
- /** The Figma OAuth Bearer token */
4
+ /** The Figma OAuth Bearer token. When provided, OAuth is used automatically. Preferred over apiKey if both are set. */
5
5
  oauthToken?: string;
6
- /** Whether to use OAuth instead of API key */
7
- useOAuth?: boolean;
8
6
  /** Output format for the metadata */
9
7
  outputFormat?: "json" | "yaml" | "object";
10
8
  /** Maximum depth to traverse the node tree */
@@ -72,12 +70,10 @@ export interface FigmaImageResult {
72
70
  cssVariables?: string;
73
71
  }
74
72
  export interface FigmaFrameImageOptions {
75
- /** The Figma API key (Personal Access Token) */
73
+ /** The Figma API key (Personal Access Token). Either apiKey or oauthToken must be provided. */
76
74
  apiKey?: string;
77
- /** The Figma OAuth Bearer token */
75
+ /** The Figma OAuth Bearer token. When provided, OAuth is used automatically. Preferred over apiKey if both are set. */
78
76
  oauthToken?: string;
79
- /** Whether to use OAuth instead of API key */
80
- useOAuth?: boolean;
81
77
  /** Export scale for PNG images (defaults to 2) */
82
78
  pngScale?: number;
83
79
  /** The absolute path to the directory where the image should be stored (optional if returnBuffer is true) */
@@ -1,9 +1,8 @@
1
1
  import type { GetFileResponse, GetFileNodesResponse } from "@figma/rest-api-spec";
2
2
  import { type ImageProcessingResult } from "~/utils/image-processing.js";
3
3
  export type FigmaAuthOptions = {
4
- figmaApiKey: string;
5
- figmaOAuthToken: string;
6
- useOAuth: boolean;
4
+ figmaApiKey?: string;
5
+ figmaOAuthToken?: string;
7
6
  };
8
7
  type SvgOptions = {
9
8
  outlineText: boolean;
@@ -15,7 +14,7 @@ export declare class FigmaService {
15
14
  private readonly oauthToken;
16
15
  private readonly useOAuth;
17
16
  private readonly baseUrl;
18
- constructor({ figmaApiKey, figmaOAuthToken, useOAuth }: FigmaAuthOptions);
17
+ constructor({ figmaApiKey, figmaOAuthToken }: FigmaAuthOptions);
19
18
  private getAuthHeaders;
20
19
  /**
21
20
  * Filters out null values from Figma image responses. This ensures we only work with valid image URLs.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "figma-metadata-extractor",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
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",