figma-metadata-extractor 1.0.10 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -289,15 +289,20 @@ class FigmaService {
289
289
  */
290
290
  filterValidImages(images) {
291
291
  if (!images) return {};
292
- return Object.fromEntries(Object.entries(images).filter(([, value]) => !!value));
292
+ return Object.fromEntries(
293
+ Object.entries(images).filter(([, value]) => !!value)
294
+ );
293
295
  }
294
296
  async request(endpoint) {
295
297
  try {
296
298
  Logger.log(`Calling ${this.baseUrl}${endpoint}`);
297
299
  const headers = this.getAuthHeaders();
298
- return await fetchWithRetry(`${this.baseUrl}${endpoint}`, {
299
- headers
300
- });
300
+ return await fetchWithRetry(
301
+ `${this.baseUrl}${endpoint}`,
302
+ {
303
+ headers
304
+ }
305
+ );
301
306
  } catch (error) {
302
307
  const errorMessage = error instanceof Error ? error.message : String(error);
303
308
  throw new Error(
@@ -372,7 +377,9 @@ class FigmaService {
372
377
  const sanitizedPath = path.normalize(localPath).replace(/^(\.\.(\/|\\|$))+/, "");
373
378
  resolvedPath = path.resolve(sanitizedPath);
374
379
  if (!resolvedPath.startsWith(path.resolve(process.cwd()))) {
375
- throw new Error("Invalid path specified. Directory traversal is not allowed.");
380
+ throw new Error(
381
+ "Invalid path specified. Directory traversal is not allowed."
382
+ );
376
383
  }
377
384
  }
378
385
  const downloadPromises = [];
@@ -384,25 +391,45 @@ class FigmaService {
384
391
  );
385
392
  if (imageFills.length > 0) {
386
393
  const fillUrls = await this.getImageFillUrls(fileKey);
387
- const fillDownloads = imageFills.map(({ imageRef, fileName, needsCropping, cropTransform, requiresImageDimensions }) => {
388
- const imageUrl = fillUrls[imageRef];
389
- return imageUrl ? downloadAndProcessImage(
394
+ const fillDownloads = imageFills.map(
395
+ ({
396
+ imageRef,
390
397
  fileName,
391
- resolvedPath,
392
- imageUrl,
393
398
  needsCropping,
394
399
  cropTransform,
395
- requiresImageDimensions,
396
- returnBuffer
397
- ) : null;
398
- }).filter((promise) => promise !== null);
400
+ requiresImageDimensions
401
+ }) => {
402
+ const imageUrl = fillUrls[imageRef];
403
+ if (!imageUrl) {
404
+ Logger.log(
405
+ `Skipping image fill with missing URL for imageRef: ${imageRef}`
406
+ );
407
+ return null;
408
+ }
409
+ return downloadAndProcessImage(
410
+ fileName,
411
+ resolvedPath,
412
+ imageUrl,
413
+ needsCropping,
414
+ cropTransform,
415
+ requiresImageDimensions,
416
+ returnBuffer
417
+ );
418
+ }
419
+ ).filter(
420
+ (promise) => promise !== null
421
+ );
399
422
  if (fillDownloads.length > 0) {
400
423
  downloadPromises.push(Promise.all(fillDownloads));
401
424
  }
402
425
  }
403
426
  if (renderNodes.length > 0) {
404
- const pngNodes = renderNodes.filter((node) => !node.fileName.toLowerCase().endsWith(".svg"));
405
- const svgNodes = renderNodes.filter((node) => node.fileName.toLowerCase().endsWith(".svg"));
427
+ const pngNodes = renderNodes.filter(
428
+ (node) => !node.fileName.toLowerCase().endsWith(".svg")
429
+ );
430
+ const svgNodes = renderNodes.filter(
431
+ (node) => node.fileName.toLowerCase().endsWith(".svg")
432
+ );
406
433
  if (pngNodes.length > 0) {
407
434
  const pngUrls = await this.getNodeRenderUrls(
408
435
  fileKey,
@@ -410,18 +437,36 @@ class FigmaService {
410
437
  "png",
411
438
  { pngScale }
412
439
  );
413
- const pngDownloads = pngNodes.map(({ nodeId, fileName, needsCropping, cropTransform, requiresImageDimensions }) => {
414
- const imageUrl = pngUrls[nodeId];
415
- return imageUrl ? downloadAndProcessImage(
440
+ const pngDownloads = pngNodes.map(
441
+ ({
442
+ nodeId,
416
443
  fileName,
417
- resolvedPath,
418
- imageUrl,
419
444
  needsCropping,
420
445
  cropTransform,
421
- requiresImageDimensions,
422
- returnBuffer
423
- ).then((result) => ({ ...result, nodeId })) : null;
424
- }).filter((promise) => promise !== null);
446
+ requiresImageDimensions
447
+ }) => {
448
+ const imageUrl = pngUrls[nodeId];
449
+ if (!imageUrl) {
450
+ Logger.log(
451
+ `Skipping PNG render with missing URL for nodeId: ${nodeId}`
452
+ );
453
+ return null;
454
+ }
455
+ return downloadAndProcessImage(
456
+ fileName,
457
+ resolvedPath,
458
+ imageUrl,
459
+ needsCropping,
460
+ cropTransform,
461
+ requiresImageDimensions,
462
+ returnBuffer
463
+ ).then(
464
+ (result) => ({ ...result, nodeId })
465
+ );
466
+ }
467
+ ).filter(
468
+ (promise) => promise !== null
469
+ );
425
470
  if (pngDownloads.length > 0) {
426
471
  downloadPromises.push(Promise.all(pngDownloads));
427
472
  }
@@ -433,18 +478,36 @@ class FigmaService {
433
478
  "svg",
434
479
  { svgOptions }
435
480
  );
436
- const svgDownloads = svgNodes.map(({ nodeId, fileName, needsCropping, cropTransform, requiresImageDimensions }) => {
437
- const imageUrl = svgUrls[nodeId];
438
- return imageUrl ? downloadAndProcessImage(
481
+ const svgDownloads = svgNodes.map(
482
+ ({
483
+ nodeId,
439
484
  fileName,
440
- resolvedPath,
441
- imageUrl,
442
485
  needsCropping,
443
486
  cropTransform,
444
- requiresImageDimensions,
445
- returnBuffer
446
- ).then((result) => ({ ...result, nodeId })) : null;
447
- }).filter((promise) => promise !== null);
487
+ requiresImageDimensions
488
+ }) => {
489
+ const imageUrl = svgUrls[nodeId];
490
+ if (!imageUrl) {
491
+ Logger.log(
492
+ `Skipping SVG render with missing URL for nodeId: ${nodeId}`
493
+ );
494
+ return null;
495
+ }
496
+ return downloadAndProcessImage(
497
+ fileName,
498
+ resolvedPath,
499
+ imageUrl,
500
+ needsCropping,
501
+ cropTransform,
502
+ requiresImageDimensions,
503
+ returnBuffer
504
+ ).then(
505
+ (result) => ({ ...result, nodeId })
506
+ );
507
+ }
508
+ ).filter(
509
+ (promise) => promise !== null
510
+ );
448
511
  if (svgDownloads.length > 0) {
449
512
  downloadPromises.push(Promise.all(svgDownloads));
450
513
  }
@@ -458,7 +521,9 @@ class FigmaService {
458
521
  */
459
522
  async getRawFile(fileKey, depth) {
460
523
  const endpoint = `/files/${fileKey}${depth ? `?depth=${depth}` : ""}`;
461
- Logger.log(`Retrieving raw Figma file: ${fileKey} (depth: ${depth ?? "default"})`);
524
+ Logger.log(
525
+ `Retrieving raw Figma file: ${fileKey} (depth: ${depth ?? "default"})`
526
+ );
462
527
  const response = await this.request(endpoint);
463
528
  writeLogs("figma-raw.json", response);
464
529
  return response;
@@ -1630,6 +1695,59 @@ async function downloadFigmaFrameImage(figmaUrl, options) {
1630
1695
  throw new Error(`Failed to download frame image: ${error instanceof Error ? error.message : String(error)}`);
1631
1696
  }
1632
1697
  }
1698
+ function getImageNodeInfo(metadata) {
1699
+ if (!metadata.images || metadata.images.length === 0) {
1700
+ return [];
1701
+ }
1702
+ const imageAssets = findImageAssets(metadata.nodes, metadata.globalVars);
1703
+ return imageAssets.map((asset) => ({
1704
+ nodeId: asset.id,
1705
+ name: asset.name
1706
+ }));
1707
+ }
1708
+ function enrichMetadataWithImages(metadata, imagePaths, options = {}) {
1709
+ const { useRelativePaths = true, localPath } = options;
1710
+ if (!metadata.images || metadata.images.length === 0) {
1711
+ return metadata;
1712
+ }
1713
+ const imageAssets = findImageAssets(metadata.nodes, metadata.globalVars);
1714
+ let downloadResults;
1715
+ if (Array.isArray(imagePaths)) {
1716
+ if (imagePaths.length !== metadata.images.length) {
1717
+ throw new Error(`Number of image paths (${imagePaths.length}) must match number of images (${metadata.images.length})`);
1718
+ }
1719
+ downloadResults = imagePaths.map((filePath, index) => ({
1720
+ filePath,
1721
+ finalDimensions: metadata.images[index].finalDimensions,
1722
+ wasCropped: metadata.images[index].wasCropped,
1723
+ cssVariables: metadata.images[index].cssVariables
1724
+ }));
1725
+ } else {
1726
+ downloadResults = imageAssets.map((asset) => {
1727
+ const filePath = imagePaths[asset.id];
1728
+ if (!filePath) {
1729
+ throw new Error(`No image path provided for node ID: ${asset.id}`);
1730
+ }
1731
+ const imageMetadata = metadata.images.find((img) => img.nodeId === asset.id);
1732
+ return {
1733
+ filePath,
1734
+ finalDimensions: imageMetadata?.finalDimensions || { width: 0, height: 0 },
1735
+ wasCropped: imageMetadata?.wasCropped || false,
1736
+ cssVariables: imageMetadata?.cssVariables
1737
+ };
1738
+ });
1739
+ }
1740
+ const enrichedNodes = enrichNodesWithImages(
1741
+ metadata.nodes,
1742
+ imageAssets,
1743
+ downloadResults,
1744
+ useRelativePaths
1745
+ );
1746
+ return {
1747
+ ...metadata,
1748
+ nodes: enrichedNodes
1749
+ };
1750
+ }
1633
1751
  function sanitizeFileName(name) {
1634
1752
  return name.replace(/[^a-z0-9]/gi, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").toLowerCase();
1635
1753
  }
@@ -1706,8 +1824,10 @@ exports.componentExtractor = componentExtractor;
1706
1824
  exports.contentOnly = contentOnly;
1707
1825
  exports.downloadFigmaFrameImage = downloadFigmaFrameImage;
1708
1826
  exports.downloadFigmaImages = downloadFigmaImages;
1827
+ exports.enrichMetadataWithImages = enrichMetadataWithImages;
1709
1828
  exports.extractFromDesign = extractFromDesign;
1710
1829
  exports.getFigmaMetadata = getFigmaMetadata;
1830
+ exports.getImageNodeInfo = getImageNodeInfo;
1711
1831
  exports.layoutAndText = layoutAndText;
1712
1832
  exports.layoutExtractor = layoutExtractor;
1713
1833
  exports.layoutOnly = layoutOnly;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { getFigmaMetadata, downloadFigmaImages, downloadFigmaFrameImage, type FigmaMetadataOptions, type FigmaImageOptions, type FigmaFrameImageOptions, type FigmaImageNode, type FigmaMetadataResult, type FigmaImageResult, } from "./lib.js";
1
+ export { getFigmaMetadata, downloadFigmaImages, downloadFigmaFrameImage, enrichMetadataWithImages, getImageNodeInfo, type FigmaMetadataOptions, type FigmaImageOptions, type FigmaFrameImageOptions, type FigmaImageNode, type FigmaMetadataResult, type FigmaImageResult, } from "./lib.js";
2
2
  export type { SimplifiedDesign } from "./extractors/types.js";
3
3
  export type { ExtractorFn, TraversalContext, TraversalOptions, GlobalVars, StyleTypes, } from "./extractors/index.js";
4
4
  export { extractFromDesign, simplifyRawFigmaObject, layoutExtractor, textExtractor, visualsExtractor, componentExtractor, allExtractors, layoutAndText, contentOnly, visualsOnly, layoutOnly, collapseSvgContainers, } from "./extractors/index.js";
package/dist/index.js CHANGED
@@ -287,15 +287,20 @@ class FigmaService {
287
287
  */
288
288
  filterValidImages(images) {
289
289
  if (!images) return {};
290
- return Object.fromEntries(Object.entries(images).filter(([, value]) => !!value));
290
+ return Object.fromEntries(
291
+ Object.entries(images).filter(([, value]) => !!value)
292
+ );
291
293
  }
292
294
  async request(endpoint) {
293
295
  try {
294
296
  Logger.log(`Calling ${this.baseUrl}${endpoint}`);
295
297
  const headers = this.getAuthHeaders();
296
- return await fetchWithRetry(`${this.baseUrl}${endpoint}`, {
297
- headers
298
- });
298
+ return await fetchWithRetry(
299
+ `${this.baseUrl}${endpoint}`,
300
+ {
301
+ headers
302
+ }
303
+ );
299
304
  } catch (error) {
300
305
  const errorMessage = error instanceof Error ? error.message : String(error);
301
306
  throw new Error(
@@ -370,7 +375,9 @@ class FigmaService {
370
375
  const sanitizedPath = path.normalize(localPath).replace(/^(\.\.(\/|\\|$))+/, "");
371
376
  resolvedPath = path.resolve(sanitizedPath);
372
377
  if (!resolvedPath.startsWith(path.resolve(process.cwd()))) {
373
- throw new Error("Invalid path specified. Directory traversal is not allowed.");
378
+ throw new Error(
379
+ "Invalid path specified. Directory traversal is not allowed."
380
+ );
374
381
  }
375
382
  }
376
383
  const downloadPromises = [];
@@ -382,25 +389,45 @@ class FigmaService {
382
389
  );
383
390
  if (imageFills.length > 0) {
384
391
  const fillUrls = await this.getImageFillUrls(fileKey);
385
- const fillDownloads = imageFills.map(({ imageRef, fileName, needsCropping, cropTransform, requiresImageDimensions }) => {
386
- const imageUrl = fillUrls[imageRef];
387
- return imageUrl ? downloadAndProcessImage(
392
+ const fillDownloads = imageFills.map(
393
+ ({
394
+ imageRef,
388
395
  fileName,
389
- resolvedPath,
390
- imageUrl,
391
396
  needsCropping,
392
397
  cropTransform,
393
- requiresImageDimensions,
394
- returnBuffer
395
- ) : null;
396
- }).filter((promise) => promise !== null);
398
+ requiresImageDimensions
399
+ }) => {
400
+ const imageUrl = fillUrls[imageRef];
401
+ if (!imageUrl) {
402
+ Logger.log(
403
+ `Skipping image fill with missing URL for imageRef: ${imageRef}`
404
+ );
405
+ return null;
406
+ }
407
+ return downloadAndProcessImage(
408
+ fileName,
409
+ resolvedPath,
410
+ imageUrl,
411
+ needsCropping,
412
+ cropTransform,
413
+ requiresImageDimensions,
414
+ returnBuffer
415
+ );
416
+ }
417
+ ).filter(
418
+ (promise) => promise !== null
419
+ );
397
420
  if (fillDownloads.length > 0) {
398
421
  downloadPromises.push(Promise.all(fillDownloads));
399
422
  }
400
423
  }
401
424
  if (renderNodes.length > 0) {
402
- const pngNodes = renderNodes.filter((node) => !node.fileName.toLowerCase().endsWith(".svg"));
403
- const svgNodes = renderNodes.filter((node) => node.fileName.toLowerCase().endsWith(".svg"));
425
+ const pngNodes = renderNodes.filter(
426
+ (node) => !node.fileName.toLowerCase().endsWith(".svg")
427
+ );
428
+ const svgNodes = renderNodes.filter(
429
+ (node) => node.fileName.toLowerCase().endsWith(".svg")
430
+ );
404
431
  if (pngNodes.length > 0) {
405
432
  const pngUrls = await this.getNodeRenderUrls(
406
433
  fileKey,
@@ -408,18 +435,36 @@ class FigmaService {
408
435
  "png",
409
436
  { pngScale }
410
437
  );
411
- const pngDownloads = pngNodes.map(({ nodeId, fileName, needsCropping, cropTransform, requiresImageDimensions }) => {
412
- const imageUrl = pngUrls[nodeId];
413
- return imageUrl ? downloadAndProcessImage(
438
+ const pngDownloads = pngNodes.map(
439
+ ({
440
+ nodeId,
414
441
  fileName,
415
- resolvedPath,
416
- imageUrl,
417
442
  needsCropping,
418
443
  cropTransform,
419
- requiresImageDimensions,
420
- returnBuffer
421
- ).then((result) => ({ ...result, nodeId })) : null;
422
- }).filter((promise) => promise !== null);
444
+ requiresImageDimensions
445
+ }) => {
446
+ const imageUrl = pngUrls[nodeId];
447
+ if (!imageUrl) {
448
+ Logger.log(
449
+ `Skipping PNG render with missing URL for nodeId: ${nodeId}`
450
+ );
451
+ return null;
452
+ }
453
+ return downloadAndProcessImage(
454
+ fileName,
455
+ resolvedPath,
456
+ imageUrl,
457
+ needsCropping,
458
+ cropTransform,
459
+ requiresImageDimensions,
460
+ returnBuffer
461
+ ).then(
462
+ (result) => ({ ...result, nodeId })
463
+ );
464
+ }
465
+ ).filter(
466
+ (promise) => promise !== null
467
+ );
423
468
  if (pngDownloads.length > 0) {
424
469
  downloadPromises.push(Promise.all(pngDownloads));
425
470
  }
@@ -431,18 +476,36 @@ class FigmaService {
431
476
  "svg",
432
477
  { svgOptions }
433
478
  );
434
- const svgDownloads = svgNodes.map(({ nodeId, fileName, needsCropping, cropTransform, requiresImageDimensions }) => {
435
- const imageUrl = svgUrls[nodeId];
436
- return imageUrl ? downloadAndProcessImage(
479
+ const svgDownloads = svgNodes.map(
480
+ ({
481
+ nodeId,
437
482
  fileName,
438
- resolvedPath,
439
- imageUrl,
440
483
  needsCropping,
441
484
  cropTransform,
442
- requiresImageDimensions,
443
- returnBuffer
444
- ).then((result) => ({ ...result, nodeId })) : null;
445
- }).filter((promise) => promise !== null);
485
+ requiresImageDimensions
486
+ }) => {
487
+ const imageUrl = svgUrls[nodeId];
488
+ if (!imageUrl) {
489
+ Logger.log(
490
+ `Skipping SVG render with missing URL for nodeId: ${nodeId}`
491
+ );
492
+ return null;
493
+ }
494
+ return downloadAndProcessImage(
495
+ fileName,
496
+ resolvedPath,
497
+ imageUrl,
498
+ needsCropping,
499
+ cropTransform,
500
+ requiresImageDimensions,
501
+ returnBuffer
502
+ ).then(
503
+ (result) => ({ ...result, nodeId })
504
+ );
505
+ }
506
+ ).filter(
507
+ (promise) => promise !== null
508
+ );
446
509
  if (svgDownloads.length > 0) {
447
510
  downloadPromises.push(Promise.all(svgDownloads));
448
511
  }
@@ -456,7 +519,9 @@ class FigmaService {
456
519
  */
457
520
  async getRawFile(fileKey, depth) {
458
521
  const endpoint = `/files/${fileKey}${depth ? `?depth=${depth}` : ""}`;
459
- Logger.log(`Retrieving raw Figma file: ${fileKey} (depth: ${depth ?? "default"})`);
522
+ Logger.log(
523
+ `Retrieving raw Figma file: ${fileKey} (depth: ${depth ?? "default"})`
524
+ );
460
525
  const response = await this.request(endpoint);
461
526
  writeLogs("figma-raw.json", response);
462
527
  return response;
@@ -1628,6 +1693,59 @@ async function downloadFigmaFrameImage(figmaUrl, options) {
1628
1693
  throw new Error(`Failed to download frame image: ${error instanceof Error ? error.message : String(error)}`);
1629
1694
  }
1630
1695
  }
1696
+ function getImageNodeInfo(metadata) {
1697
+ if (!metadata.images || metadata.images.length === 0) {
1698
+ return [];
1699
+ }
1700
+ const imageAssets = findImageAssets(metadata.nodes, metadata.globalVars);
1701
+ return imageAssets.map((asset) => ({
1702
+ nodeId: asset.id,
1703
+ name: asset.name
1704
+ }));
1705
+ }
1706
+ function enrichMetadataWithImages(metadata, imagePaths, options = {}) {
1707
+ const { useRelativePaths = true, localPath } = options;
1708
+ if (!metadata.images || metadata.images.length === 0) {
1709
+ return metadata;
1710
+ }
1711
+ const imageAssets = findImageAssets(metadata.nodes, metadata.globalVars);
1712
+ let downloadResults;
1713
+ if (Array.isArray(imagePaths)) {
1714
+ if (imagePaths.length !== metadata.images.length) {
1715
+ throw new Error(`Number of image paths (${imagePaths.length}) must match number of images (${metadata.images.length})`);
1716
+ }
1717
+ downloadResults = imagePaths.map((filePath, index) => ({
1718
+ filePath,
1719
+ finalDimensions: metadata.images[index].finalDimensions,
1720
+ wasCropped: metadata.images[index].wasCropped,
1721
+ cssVariables: metadata.images[index].cssVariables
1722
+ }));
1723
+ } else {
1724
+ downloadResults = imageAssets.map((asset) => {
1725
+ const filePath = imagePaths[asset.id];
1726
+ if (!filePath) {
1727
+ throw new Error(`No image path provided for node ID: ${asset.id}`);
1728
+ }
1729
+ const imageMetadata = metadata.images.find((img) => img.nodeId === asset.id);
1730
+ return {
1731
+ filePath,
1732
+ finalDimensions: imageMetadata?.finalDimensions || { width: 0, height: 0 },
1733
+ wasCropped: imageMetadata?.wasCropped || false,
1734
+ cssVariables: imageMetadata?.cssVariables
1735
+ };
1736
+ });
1737
+ }
1738
+ const enrichedNodes = enrichNodesWithImages(
1739
+ metadata.nodes,
1740
+ imageAssets,
1741
+ downloadResults,
1742
+ useRelativePaths
1743
+ );
1744
+ return {
1745
+ ...metadata,
1746
+ nodes: enrichedNodes
1747
+ };
1748
+ }
1631
1749
  function sanitizeFileName(name) {
1632
1750
  return name.replace(/[^a-z0-9]/gi, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").toLowerCase();
1633
1751
  }
@@ -1705,8 +1823,10 @@ export {
1705
1823
  contentOnly,
1706
1824
  downloadFigmaFrameImage,
1707
1825
  downloadFigmaImages,
1826
+ enrichMetadataWithImages,
1708
1827
  extractFromDesign,
1709
1828
  getFigmaMetadata,
1829
+ getImageNodeInfo,
1710
1830
  layoutAndText,
1711
1831
  layoutExtractor,
1712
1832
  layoutOnly,
package/package.json CHANGED
@@ -1,69 +1,69 @@
1
1
  {
2
- "name": "figma-metadata-extractor",
3
- "version": "1.0.10",
4
- "description": "Extract metadata and download images from Figma files. A standalone library for accessing Figma design data and downloading frame images programmatically.",
5
- "type": "module",
6
- "main": "dist/index.cjs",
7
- "module": "dist/index.js",
8
- "types": "dist/index.d.ts",
9
- "exports": {
10
- ".": {
11
- "types": "./dist/index.d.ts",
12
- "import": "./dist/index.js",
13
- "require": "./dist/index.cjs"
14
- }
15
- },
16
- "files": [
17
- "dist",
18
- "README.md",
19
- "example.js"
20
- ],
21
- "scripts": {
22
- "build": "vite build && tsc --project tsconfig.declarations.json",
23
- "typecheck": "tsc --noEmit",
24
- "test": "jest",
25
- "dev": "vite build --watch",
26
- "lint": "eslint .",
27
- "format": "prettier --write \"src/**/*.ts\"",
28
- "prepack": "npm run build"
29
- },
30
- "engines": {
31
- "node": ">=18.0.0"
32
- },
33
- "repository": {
34
- "type": "git",
35
- "url": "git+https://github.com/mikmokpok/figma-context-extractor.git"
36
- },
37
- "keywords": [
38
- "figma",
39
- "design",
40
- "metadata",
41
- "extractor",
42
- "typescript",
43
- "api",
44
- "images"
45
- ],
46
- "author": "",
47
- "license": "MIT",
48
- "dependencies": {
49
- "@figma/rest-api-spec": "^0.33.0",
50
- "js-yaml": "^4.1.0",
51
- "remeda": "^2.20.1",
52
- "sharp": "^0.34.3",
53
- "zod": "^3.24.2"
54
- },
55
- "devDependencies": {
56
- "@types/jest": "^29.5.14",
57
- "@types/js-yaml": "^4.0.9",
58
- "@types/node": "^20.17.0",
59
- "@typescript-eslint/eslint-plugin": "^8.24.0",
60
- "@typescript-eslint/parser": "^8.24.0",
61
- "eslint": "^9.20.1",
62
- "eslint-config-prettier": "^10.0.1",
63
- "jest": "^29.7.0",
64
- "prettier": "^3.5.0",
65
- "ts-jest": "^29.2.5",
66
- "typescript": "^5.7.3",
67
- "vite": "^6.4.1"
2
+ "name": "figma-metadata-extractor",
3
+ "version": "1.0.12",
4
+ "description": "Extract metadata and download images from Figma files. A standalone library for accessing Figma design data and downloading frame images programmatically.",
5
+ "type": "module",
6
+ "main": "dist/index.cjs",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
68
14
  }
69
- }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "example.js"
20
+ ],
21
+ "scripts": {
22
+ "build": "vite build && tsc --project tsconfig.declarations.json",
23
+ "typecheck": "tsc --noEmit",
24
+ "test": "jest",
25
+ "dev": "vite build --watch",
26
+ "lint": "eslint .",
27
+ "format": "prettier --write \"src/**/*.ts\"",
28
+ "prepack": "npm run build"
29
+ },
30
+ "engines": {
31
+ "node": ">=18.0.0"
32
+ },
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/mikmokpok/figma-context-extractor.git"
36
+ },
37
+ "keywords": [
38
+ "figma",
39
+ "design",
40
+ "metadata",
41
+ "extractor",
42
+ "typescript",
43
+ "api",
44
+ "images"
45
+ ],
46
+ "author": "",
47
+ "license": "MIT",
48
+ "dependencies": {
49
+ "@figma/rest-api-spec": "^0.33.0",
50
+ "js-yaml": "^4.1.0",
51
+ "remeda": "^2.20.1",
52
+ "sharp": "^0.34.3",
53
+ "zod": "^3.24.2"
54
+ },
55
+ "devDependencies": {
56
+ "@types/jest": "^29.5.14",
57
+ "@types/js-yaml": "^4.0.9",
58
+ "@types/node": "^20.17.0",
59
+ "@typescript-eslint/eslint-plugin": "^8.24.0",
60
+ "@typescript-eslint/parser": "^8.24.0",
61
+ "eslint": "^9.20.1",
62
+ "eslint-config-prettier": "^10.0.1",
63
+ "jest": "^29.7.0",
64
+ "prettier": "^3.5.0",
65
+ "ts-jest": "^29.2.5",
66
+ "typescript": "^5.7.3",
67
+ "vite": "^6.4.1"
68
+ }
69
+ }