npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

figma-plugin-api

v1.1.2

Published

Harness JSON-RPC in your Figma plugin communication.

Downloads

25

Readme

figma-plugin-api

Harness the power of JSON-RPC in your Figma plugin communication.

figma-plugin-api allows you to create an API between your Figma plugin (logic) and plugin UI by defining a set of methods with your business logic. The plugin <-> UI communication is handled by the package using JSON-RPC 2.0 and all you need to do is call your methods asynchronously wherever you need the results.

The plugin is also strongly typed and the methods you pass to the API creators retain their types.

Based on the work of Mathieu Dutour in https://github.com/Lona/figma-jsonrpc

Usage

Define your plugin API:

import { createPluginAPI, createUIAPI } from 'figma-plugin-api';

// Create a set of methods that are called from plugin UI and executed in plugin logic
export const api = createPluginAPI({
  ping() {
    return 'pong';
  },
  setToken(token: string) {
    return figma.clientStorage.setAsync('token', token);
  },
  getToken() {
    return figma.clientStorage.getAsync('token');
  }
});

// Create a set of methods that are called from plugin logic and executed in plugin UI
export const uiApi = createUIAPI({
  selectionChanged(selection) {
    // Display Figma selection in UI
  }
});

Use the Plugin API in the UI:

import { api } from './api';

const pong = await api.ping();

await api.setToken('new token');

Use the UI API in the plugin:

// Import methods to use from uiApi
import { uiApi } from './api';

// Show plugin UI with figma.showUI(__html__) etc.

figma.on('selectionchange', () => {
  uiApi.selectionChange(figma.currentPage.selection);
});

NOTE: You must always import the file calling the API creator functions in both the plugin and the UI, even if you aren't calling any API methods in that module. It is necessary so that both modules can handle calls from each other. You can use an unnamed import (e.g. import './api') if you do not need to call any methods in the module.

Plugin options

If needed, plugin options can be used to set plugin ID, target origins for messages passed between plugin and UI, and adjust timeout for API calls.

NOTE: If you are running a hosted (non-null origin) plugin UI, you must set pluginId to allow messages to be passed between plugin logic and UI.

The options work on a "latest takes presence" principle: when createPluginAPI or createUIAPI are called, whatever options are passed to them override any previous options. Remember that recreating the APIs with new options only on the plugin or only on the UI side will lead to the different sides of your API working with different options, so it is recommended to define a static common options object and use it for creating both APIs.

import { createPluginAPI, createUIAPI, RPCOptions } from 'figma-plugin-api';

import manifest from '../manifest.json';

const pluginOptions: RPCOptions = {
  /**
   * Timeout in milliseconds
   * Default: 3000
   */
  timeoutMs: 5000,
  /**
   * If your plugin UI is hosted (non-null origin), pluginId must be defined to allow messages to be sent
   */
  pluginId: manifest.id,
  /**
   * Specifies what the origin of the plugin UI must be for a message to be dispatched from plugin logic to UI
   * If defined, add 'http://localhost:<port>' to this field in your local environment
   * to allow messaging while running on a dev server
   * Default: '*'
   */
  logicTargetOrigin: 'https://www.your-hosted-plugin.com',
  /**
   * Specifies what the origin of the plugin logic must be for a message to be dispatched from UI to plugin logic
   * Usually 'https://www.figma.com'
   * Default: '*'
   */
  uiTargetOrigin: 'https://www.figma.com'
};

export const api = createPluginAPI(
  {
    ...
  },
  pluginOptions
);

export const uiApi = createUIAPI(
  {
    ...
  },
  pluginOptions
);

Troubleshooting

Debugging tactics

  • Add logging to your API methods to see if they are being called
  • Add logging to the plugin and UI to see if messages are being received:
    • Plugin logic:

      figma.ui.onmessage = (msg) => {
        console.log('Plugin received message:', msg);
      };
    • Plugin UI:

      window.addEventListener('message', (e) => {
        // React specific: Do not log React dev tools messages
        if (e.data.source?.includes('react-devtools')) {
          return;
        }
      
        if (Object.hasOwn(e.data, 'pluginMessage')) {
          console.log('UI received message:', e.data.pluginMessage);
        } else {
          console.log('UI received message:', e);
        }
      });

Error: Request <number> (<method name>) timed out.

Most probably either the remote API call doesn't get through to the handler side, there is no handler for it, or the return message doesn't get through to the caller side. Checklist:

  • Have you imported the api module in both plugin logic and plugin UI?
    • Some packagers with certain configurations will drop unused named imports, so make sure to use import './api-wrapper'; instead of import { api } from './api-wrapper; if you are not using any of the API functions. The first syntax commonly means "this module has side effects" and the import will be included.
  • Hosted plugins (non-null origin): have you set the plugin id to the API options?
  • If you have set logicTargetOrigin and/or uiTargetOrigin, are they correct?
    • Omit them from the API options or set them to the '*' wildcard to test.

Library developers

This library is intended to be imported from one source for the entire project as it behaves like a singleton and expects to be the only handler of RPC procotol calls. This means if you use this library in your library, add it as a peerDependency and ensure it is not bundled with your code.