Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

create-figma-app

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

create-figma-app - npm Package Compare versions

Comparing version 0.0.3 to 0.0.4

template-react-ts/src/react/rpc/handlers.ts

6

CHANGELOG.md
# create-figma-app
## 0.0.4
### Patch Changes
- refactor: refactor postmessage bridge and fix some bugs
## 0.0.3

@@ -4,0 +10,0 @@

13

package.json
{
"name": "create-figma-app",
"version": "0.0.3",
"version": "0.0.4",
"type": "module",

@@ -21,7 +21,2 @@ "description": "create plugin app for figma",

},
"scripts": {
"dev": "unbuild --stub",
"build": "unbuild",
"prepublishOnly": "npm run build"
},
"files": [

@@ -49,3 +44,7 @@ "index.js",

"registry": "https://registry.npmjs.org/"
},
"scripts": {
"dev": "unbuild --stub",
"build": "unbuild"
}
}
}
# create-figma-app [![NPM Version](https://img.shields.io/npm/v/create-figma-app)](https://www.npmjs.com/package/create-figma-app) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](./CONTRIBUTING.md)
Quick create a [plugin app](https://www.figma.com/plugin-docs/) for figma
Quick create a [Figma plugin app](https://www.figma.com/plugin-docs/).

@@ -9,3 +9,4 @@ ## Usage

```bash
# npm
# npm
# my-figma-app is project dir name
npm create figma-app@latest my-figma-app

@@ -19,10 +20,24 @@

- `react-ts`:react、typescript、rspack
you can use `.` for the project name or input a project by
### react-ts
```bash
npm create figma-app@latest <projectName>
```
- using react、tailwind、typescript、rspack
It has the following advantages:
- Simplify postmessage communication between ui and sandbox
- Sandbox is published together with ui, no need to publish sandbox in Figma
- Good TypeScript type definition, ui and sandbox know each other's function interface
- Extremely small sandbox code size, only contains sandbox code
See [More about this template](./template-react-ts/README.md)
![](https://figma-plugin-template-1307850796.cos.ap-beijing.myqcloud.com/plugin-screenshot.png)
## Figma Plugins using this template
- [Quick Replace Font](https://www.figma.com/community/plugin/1241949869279607046/quick-replace-font): Replace fonts with one click. used by 3.5k users.
![](https://site-1307850796.cos.ap-beijing.myqcloud.com/quick-replace-font.png)
## Contribution

@@ -29,0 +44,0 @@

@@ -14,2 +14,4 @@ {

"build:sandbox": "esbuild ./src/sandbox/sandbox/index.ts --bundle --outfile=src/react/rpc/sandbox.js --target=chrome58",
"build:rsdoctor": "cross-env NODE_ENV=production RSDOCTOR=true rspack build",
"build:analyze": "cross-env NODE_ENV=production rspack build --analyze",
"lint": "eslint --ext .ts,.tsx --ignore-pattern node_modules .",

@@ -19,2 +21,3 @@ "lint:fix": "eslint --ext .ts,.tsx --ignore-pattern node_modules --fix ."

"dependencies": {
"jsonrpc-over-postmessage": "^0.0.2",
"react": "^18.2.0",

@@ -27,2 +30,3 @@ "react-dom": "^18.2.0"

"@figma/plugin-typings": "*",
"@rsdoctor/rspack-plugin": "^0.4.8",
"@rspack/cli": "1.0.2",

@@ -29,0 +33,0 @@ "@rspack/core": "1.0.2",

# figma-plugin-template
A figma template using React, Typescript, JSON RPC.
A figma template using React, Tailwind, Typescript, JSON RPC.
<img width="1728" alt="image" src="https://github.com/user-attachments/assets/07858825-23be-46e7-b723-b0da8cdf0cc9">
- Simplify postmessage communication between ui and sandbox
- Sandbox is published together with ui, no need to publish sandbox in Figma
- Good TypeScript type definition, ui and sandbox know each other's function interface
- Extremely small sandbox code size, only contains sandbox code
## Install
![](https://figma-plugin-template-1307850796.cos.ap-beijing.myqcloud.com/plugin-screenshot.png)
## Development
```bash
pnpm install
npm run dev
```
## Development
### Code Structure
```bash
npm run dev
```
├── react // react project
│ ├── App.tsx
│ ├── index.css
│ ├── index.html
│ ├── index.tsx
│ ├── rpc
| | ├── index.ts // create postmessage handler in ui
| | ├── handlers.ts // ui handlers for sandbox event
| | └── sandbox.js // [auto generated, no need to modify this file] ui will get the sandbox code (a url, same origin with ui), and then postmessage it to the sandbox and execute it
│ └── utils
└── sandbox
| ├── code // sandbox init code. When the plugin is first published in Figma, this file (`code.js` in dist) needs to be published
| └── sandbox // sandbox core code, will be bundled to `react/rpc/sandbox.js`
| ├── index.ts // create postmessage handler in sandbox
| └── handlers.ts // sandbox handlers for ui event
```
- we use [rspack](https://rspack.dev/) to bundle web project
- we use [esbuild](https://esbuild.github.io/) to bundle sandbox code

@@ -28,2 +46,4 @@ ## Build

![](https://figma-plugin-template-1307850796.cos.ap-beijing.myqcloud.com/build.png)
Here are the bundles:

@@ -46,73 +66,32 @@

<img width="1728" alt="image" src="https://github.com/user-attachments/assets/99f722e8-451d-4e24-9828-c9abcedf6cbb">
![](https://figma-plugin-template-1307850796.cos.ap-beijing.myqcloud.com/replace-your-cdn.png)
## One more thing
Your entrance page will be here, check it.
We use JSON rpc with typings, so that we can know each interface.
![](https://figma-plugin-template-1307850796.cos.ap-beijing.myqcloud.com/check-cdn.png)
- before
```typescript
// plugin ui postmessage to sandbox code
parent.postMessage({ pluginMessage: { type: 'create-rectangles', count } }, '*')
```
> If you need to customize the build process, [rspack doc](https://rspack.dev/) and [esbuild doc](https://esbuild.github.io/) will be helpful.
```typescript
// sandbox code listen and do sothing
figma.ui.onmessage = (msg: { type: string, count: number }) => {
// One way of distinguishing between different types of messages sent from
// your HTML page is to use an object with a "type" property like this.
if (msg.type === 'create-rectangles') {
const nodes: SceneNode[] = [];
for (let i = 0; i < msg.count; i++) {
const rect = figma.createRectangle();
rect.x = i * 150;
rect.fills = [{ type: 'SOLID', color: { r: 1, g: 0.5, b: 0 } }] as any;
figma.currentPage.appendChild(rect);
nodes.push(rect);
}
figma.currentPage.selection = nodes;
figma.viewport.scrollAndZoomIntoView(nodes);
}
## Publish
// Make sure to close the plugin when you're done. Otherwise the plugin will
// keep running, which shows the cancel button at the bottom of the screen.
figma.closePlugin();
};
```
- First publish: All you need is drop `dist/web` to your cdn, and check the effect in the development environment.
- after
- Subsequent publish: like first time, or you can automate this process through CI, depending on the code management tool or pipeline you use (Github, Gitlab, Jenkins, and so on)
```typescript
// plugin ui call codeApi by JSON rpc
codeApi.createRectangles(count);
```
## Performance
```typescript
// sandbox code declare handler
function createRectangles(count: number) {
const nodes: SceneNode[] = [];
for (let i = 0; i < count; i++) {
const rect = figma.createRectangle();
rect.x = i * 150;
rect.fills = [{ type: 'SOLID', color: { r: 1, g: 0.5, b: 0 } }] as any;
figma.currentPage.appendChild(rect);
nodes.push(rect);
}
figma.currentPage.selection = nodes;
figma.viewport.scrollAndZoomIntoView(nodes);
}
// other handlers
You can choose any one of the following, or use a combination of
const handlers = {
createRectangles,
};
- [Rsdoctor](https://rspack.dev/zh/guide/optimization/use-rsdoctor): `npm run build:rsdoctor`
export default handlers;
```
![](https://figma-plugin-template-1307850796.cos.ap-beijing.myqcloud.com/rsdoctor.png)
And we don't have to bundle plugin api's handlers to code api or vice versa using [Proxy](./src/rpc/proxy.ts)!
- [webpack-bundle-analyzer](https://rspack.dev/zh/guide/optimization/analysis#webpack-bundle-analyzer): `npm run build:analyze`
![](https://figma-plugin-template-1307850796.cos.ap-beijing.myqcloud.com/webpack-bundle-analyzer.png)
## Thanks
- JSON rpc work is inspired by [figma-JSONRPC](https://github.com/Lona/figma-jsonrpc/tree/master)

@@ -0,1 +1,2 @@

import { RsdoctorRspackPlugin } from '@rsdoctor/rspack-plugin';
import { defineConfig } from '@rspack/cli';

@@ -8,7 +9,6 @@ import { rspack } from '@rspack/core';

// 需要换成自己的 CDN 地址
const CDN_ADDRESS =
'https://pluin-1307850796.cos.ap-nanjing.myqcloud.com/template/';
const CDN_ADDRESS = 'https://YOUR_CDN_ADDRESS/';
const templatePath = isDev
? `http://localhost:3000/index.html`
: path.join(CDN_ADDRESS, 'index.html');
: `${CDN_ADDRESS}index.html`;

@@ -23,3 +23,3 @@ export default defineConfig({

path: path.resolve(__dirname, 'dist/web'),
publicPath: isDev ? '/' : CDN_ADDRESS,
publicPath: isDev ? 'http://localhost:3000/' : CDN_ADDRESS,
filename: '[name].[contenthash].js',

@@ -112,2 +112,8 @@ cssFilename: '[name].[contenthash].css',

new rspack.ProgressPlugin({}),
process.env.RSDOCTOR &&
new RsdoctorRspackPlugin({
supports: {
generateTileGraph: true,
},
}),
new rspack.HtmlRspackPlugin({

@@ -114,0 +120,0 @@ template: './src/react/index.html',

@@ -0,4 +1,6 @@

import { createRpcApi } from 'jsonrpc-over-postmessage';
import { SandboxHandlers } from '@sandbox/sandbox';
import manifest from '../../../manifest.json';
import { createRpcApi } from '../../rpc';
import { getSandboxContent } from '../utils';
// @ts-ignore ignore

@@ -31,7 +33,7 @@ import sandbox from './sandbox.js';

// 注册 sandbox.js
sandboxApi
.executeScriptURL(sandbox)
.then(({ message }) => console.log(message))
.catch(({ message }) => console.error(message));
export {};
export const registerSandboxApi = async () => {
const sandboxContent = await getSandboxContent(sandbox);
if (sandboxContent) {
return sandboxApi.executeScript(sandboxContent);
}
};

@@ -22,10 +22,7 @@ "use strict";

var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// src/rpc/errors.ts
// node_modules/.pnpm/jsonrpc-over-postmessage@0.0.2/node_modules/jsonrpc-over-postmessage/dist/errors.js
var InvalidRequest = class extends Error {
constructor(data) {
super("Invalid Request");
__publicField(this, "data");
__publicField(this, "statusCode");
this.data = data;

@@ -38,4 +35,2 @@ this.statusCode = -32600;

super("Method not found");
__publicField(this, "data");
__publicField(this, "statusCode");
this.data = data;

@@ -46,3 +41,3 @@ this.statusCode = -32601;

// src/rpc/rpc.ts
// node_modules/.pnpm/jsonrpc-over-postmessage@0.0.2/node_modules/jsonrpc-over-postmessage/dist/rpc.js
var rpcIndex = 0;

@@ -56,3 +51,3 @@ var pending = {};

} else {
target == null ? void 0 : target.postMessage(raw, targetOrigin);
target === null || target === void 0 ? void 0 : target.postMessage(raw, targetOrigin);
}

@@ -145,6 +140,3 @@ }

if (!callback) {
sendError(
id,
new InvalidRequest("Missing callback for " + json.id)
);
sendError(id, new InvalidRequest("Missing callback for " + json.id));
return;

@@ -178,3 +170,3 @@ }

// src/rpc/config.ts
// node_modules/.pnpm/jsonrpc-over-postmessage@0.0.2/node_modules/jsonrpc-over-postmessage/dist/config.js
var config = {

@@ -194,3 +186,3 @@ target: null,

// src/rpc/proxy.ts
// node_modules/.pnpm/jsonrpc-over-postmessage@0.0.2/node_modules/jsonrpc-over-postmessage/dist/proxy.js
function createApiProxy() {

@@ -212,3 +204,3 @@ const target = {

// src/rpc/index.ts
// node_modules/.pnpm/jsonrpc-over-postmessage@0.0.2/node_modules/jsonrpc-over-postmessage/dist/index.js
var createRpcApi = (options) => {

@@ -270,7 +262,7 @@ initConfig(options);

// src/sandbox/sandbox/index.ts
createRpcApi({
var uiApi = createRpcApi({
postmessage: (message) => figma.ui.postMessage(message, { origin: "*" }),
onmessage: (handler) => {
figma.ui.onmessage = (message) => {
console.log("sandbox message", message);
console.log("code message", message);
handler(message);

@@ -282,4 +274,11 @@ };

figma.on("selectionchange", () => {
console.log("selectionchange");
const _selection = figma.currentPage.selection;
const selection = _selection.map((item) => ({
id: item.id,
name: item.name,
type: item.type
}));
console.log("selectionchange", selection);
uiApi.handleSelectionChange(selection);
});
})();

@@ -1,2 +0,3 @@

import { createRpcApi } from '@rpc';
import { createRpcApi } from 'jsonrpc-over-postmessage';
import { codeHandlers } from './handlers';

@@ -3,0 +4,0 @@

@@ -1,2 +0,4 @@

import { createRpcApi } from '../../rpc/index';
import { createRpcApi } from 'jsonrpc-over-postmessage';
import { CodeHandlers } from '../../react/rpc/handlers';
import handlers from './handlers';

@@ -10,4 +12,3 @@

*/
createRpcApi({
const uiApi = createRpcApi<CodeHandlers>({
postmessage: (message: unknown) =>

@@ -17,3 +18,3 @@ figma.ui.postMessage(message, { origin: '*' }),

figma.ui.onmessage = (message) => {
console.log('sandbox message', message);
console.log('code message', message);
handler(message);

@@ -25,7 +26,17 @@ };

// 编写全局的监听函数也是可以的
/**
* 监听 figma 选中元素的变化
* 调用 uiApi
*/
figma.on('selectionchange', () => {
console.log('selectionchange');
const _selection = figma.currentPage.selection;
const selection = _selection.map((item) => ({
id: item.id,
name: item.name,
type: item.type,
}));
console.log('selectionchange', selection);
uiApi.handleSelectionChange(selection);
});
export type { SandboxHandlers } from './handlers';

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc