0.5.0 • Published 3 months ago

@neodx/figma v0.5.0

Weekly downloads
-
License
MIT
Repository
github
Last release
3 months ago

@neodx/figma

The modern Figma integration tools, typed API, human-friendly files traversing, assets exporter, and more.

Warning This project is still in the development stage, under 0.x.x version breaking changes can be introduced in any release, but I'll try to make them loud.

  • Flexible optimized Export API - the simple but powerful way to automate your design system
  • Fully Typed Figma API and common helpers
  • Brilliant human-friendly Figma documents example below (inspired by figma-transformer)

Installation

# yarn
yarn add -D @neodx/figma
# pnpm
pnpm install -D @neodx/figma
# npm
npm install -D @neodx/figma

Note We're migrating our documentation to the standalone website, so some parts of the current documentation may be outdated.

Please visit the https://neodx.pages.dev/ for more information.

Getting started

We will start with the short example of the powerful export CLI:

  1. Receive the Figma access token (see Personal access token)
  2. Create the figma.config.js file in the root of your project (see Configuration)
  3. Run the CLI command figma export (see CLI)

Personal access token

We are using the Figma API, so you need to provide a personal access token to access the API.

The token can be obtained from the Figma account settings (Figma > Help and Account > Account Settings > Personal Access Tokens).

You can provide the token via the FIGMA_TOKEN environment variable, the --token CLI argument, or the token option in the configuration file.

# via env variable
FIGMA_TOKEN=xxx figma export
# via CLI argument
figma export --token xxx

Configure your project

The simple config is just a file ID, output path and rules for collecting components (what exactly you want to export).

We're using the cosmiconfig package to load the configuration file, so you can use any supported format: figma.config.js, .figmarc.cjs, etc. (we recommend to use the .js or .cjs format for possible future extensions).

Let's write it:

// figma.config.js
const { formatExportFileName } = require('@neodx/figma');

/**
 * @type {import('@neodx/figma').Configuration}
 */
module.exports = {
  export: {
    fileId: 'YOUR_FILE_ID_OR_LINK',
    output: 'assets/icons',
    collect: {
      target: [
        {
          // First of all - select the "Icons" page
          type: 'CANVAS',
          filter: 'Icons'
        },
        {
          // Then select all components with names that starts with "32/"
          type: 'COMPONENT',
          filter: /32\/.*/
        }
      ]
    }
  }
};

We're highly recommend to check the Configuration section to see all available options (There's a lot to see there!).

CLI: ✨figma export

Export that you had configured in the previous step (don't forget to provide the token 🌝).

example

Programmatically usage

Our CLI is just a wrapper around the library, when is not enough, you can use the library programmatically, all functions are exported from the main package!

import { createFigmaApi, parseFileIdFromLink, createFileGraph } from '@neodx/figma';

const figma = createFigmaApi({
  personalAccessToken: 'xxx'
  // or
  // accessToken: 'my-oauth-token'
});
// Load file by file key
const myFileId = parseFileIdFromLink('https://www.figma.com/file/xxx/yyy');
const myFile = await figma.getFile({
  id: myFileId
});
// Create well-typed human-friendly graph for convenient access to all file data
const graph = createFileGraph(myFileId, myFile);

const allTexts = graph.registry.types.TEXT.map(text => text.source.characters);

Traverse file graph

See actual version in "Traverse Figma File" recipe

The Figma API is designed as simple nested tree structure and not fit for high-level interactions.

For example, every change in the real document will lead to a change in the tree structure from the API, you can't navigate through the tree structure, you can't get all nodes of the same type or complex filters, etc.

We're providing powerful graph structure for easy traversing and accessing any data in the document.

import { createFileGraph, createFigmaApi } from '@neodx/figma';

const fileId = 'xxx';
const api = createFigmaApi({
  /* ... */
});
const file = await api.getFile({ id: fileId });
const graph = createFileGraph(fileId, file);
// ...

Collect specific components within the components sets

import { collectNodes } from '@neodx/figma';

// All components in "Icons" page withing "32/..." component set
const icons32 = collectNodes(graph, {
  target: [
    {
      // First of all - select the "Icons" page
      type: 'CANVAS',
      filter: 'Icons'
    },
    {
      // Then select all components with names that starts with "32/"
      type: 'COMPONENT',
      filter: /^32\//
    }
  ]
});

Collect instances by multiple criteria

import { collectNodes, extractNodeType } from '@neodx/figma';

const iconsInstances = collectNodes(graph, {
  target: [
    {
      type: 'CANVAS',
      filter: ['Icons', 'Assets'] // Include 2 pages
    },
    {
      // Filter frames with names that contains "Colored", "Outlined" or "Filled"
      type: 'FRAME',
      filter: /Colored|Outlined|Filled/
    }
  ],
  extract: 'INSTANCE' // Get all instances
});

Get all text nodes

const allTextNodes = graph.registry.types.TEXT.map(text => text.source.characters);

Get defined colors

import { getColor, isPaintSolid } from '@neodx/figma';

const filledColors = Object.values(graph.registry.styles).filter(
  // remote colors are an external styles
  ({ styleType, styles, remote }) => styleType === 'FILL' && !remote
);

for (const { name, styles } of filledColors) {
  const solid = styles.find(isPaintSolid);

  if (!solid) continue;
  const key = name.toLowerCase().replaceAll(/[-\/\s]/g, '.');
  const color = getColor(solid.color).toHex();

  console.log(name, color);
}

figma export

Export Figma file to the filesystem (file can be defined in the config)

  • --output, -o - Output directory, e.g. --output ./assets
  • --verbose - Verbose mode for debug
  • --dry-run - Dry run mode, don't write any files, just show what will be done in the console
  • --token, -t - Figma personal access token, can be defined in the config

Motivation

I've been looking for a stable maintaining Figma API with built-in high-level structures, abstractions and common features for implementing high-level tools for Figma. But I didn't find anything that would suit me, figma-js and figma-api both are not maintained, low-level API only and depends on the axios library, which is not suitable for me.

Next, I found figma-transformer, nice project for creating a human-friendly data structure, but it's not a full-featured, not maintained, and written in very unsafe and untyped style.

So I decided to create my own Figma API, which will be:

  • Powerful CLI solves the most common tasks
  • Fully typed consistent Figma API and common helpers
  • Convenient high-level Node.JS API for working with Figma projects
  • Web API based and not depends on specific third-party libraries
  • Safe and stable as possible without strict value validation (via zod, runtypes or something like that)
  • etc.

In other words, the holistic high-level well-featured instrument.

Inspiration

This project got inspiration about API design and some features from the following projects:

License

MIT

0.5.0

3 months ago

0.3.0

10 months ago

0.4.1

7 months ago

0.2.3

10 months ago

0.4.0

7 months ago

0.2.2

12 months ago

0.2.1

12 months ago

0.2.0

12 months ago

0.1.0

12 months ago