🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

figma-plugin-react-hooks

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

figma-plugin-react-hooks - npm Package Compare versions

Comparing version
1.1.0
to
2.0.0
+29
-251
lib/hook.cjs

@@ -59,246 +59,8 @@ "use strict";

// node_modules/figma-plugin-api/lib/index.mjs
var __defProp2 = Object.defineProperty;
var __getOwnPropSymbols2 = Object.getOwnPropertySymbols;
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
var __propIsEnum2 = Object.prototype.propertyIsEnumerable;
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues2 = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp2.call(b, prop))
__defNormalProp2(a, prop, b[prop]);
if (__getOwnPropSymbols2)
for (var prop of __getOwnPropSymbols2(b)) {
if (__propIsEnum2.call(b, prop))
__defNormalProp2(a, prop, b[prop]);
}
return a;
};
var __publicField = (obj, key, value) => {
__defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
var RPCError = class extends Error {
constructor(code, message, data) {
super();
__publicField(this, "code");
__publicField(this, "message");
__publicField(this, "data");
this.code = code;
this.message = message;
this.data = data;
}
};
var InvalidRequest = class extends RPCError {
constructor(data) {
super(-32600, "Invalid request", data);
}
};
var MethodNotFound = class extends RPCError {
constructor(data) {
super(-32601, "Method not found", data);
}
};
var isRpcOutGoing = (req) => {
return Object.prototype.hasOwnProperty.call(req, "method");
};
var isRpcNotification = (req) => {
return isRpcOutGoing(req) && !Object.prototype.hasOwnProperty.call(req, "id");
};
var isRpcResponseResult = (req) => {
return Object.prototype.hasOwnProperty.call(req, "result");
};
var isRpcResponseError = (req) => {
return Object.prototype.hasOwnProperty.call(req, "error");
};
var isRpcResponse = (req) => {
return isRpcResponseResult(req) || isRpcResponseError(req);
};
var logBase = (level, ...msg) => console[level](...msg);
var logWarn = (...msg) => logBase("warn", ...msg);
var logError = (...msg) => logBase("error", ...msg);
var methods = {};
var pending = {};
var rpcIndex = 0;
var sendRaw;
var sendJson = (msg) => {
try {
sendRaw(msg);
} catch (err) {
logError(err);
}
};
var sendResult = (id, result) => {
sendJson({
jsonrpc: "2.0",
id,
result
});
};
var sendError = (id, error) => {
sendJson({
jsonrpc: "2.0",
id,
error: {
code: error.code,
message: error.message,
data: error.data
}
});
};
var handleRpc = (json) => {
if (!isRpcNotification(json)) {
if (isRpcResponse(json)) {
const callback = pending[json.id];
if (!callback) {
sendError(json.id, new InvalidRequest(`Missing callback for ${json.id}`));
return;
}
if (callback.timeout) {
clearTimeout(callback.timeout);
}
delete pending[json.id];
callback(isRpcResponseResult(json) ? json.result : void 0, isRpcResponseError(json) ? json.error : void 0);
} else {
handleRequest(json);
}
} else {
handleNotification(json);
}
};
var handleRaw = (data) => {
try {
if (!data) {
return;
}
handleRpc(data);
} catch (err) {
logError("handleRaw error", err, data);
}
};
var onRequest = (method, params) => {
if (!methods[method]) {
logError(`onRequest: Method ${method} not found in methods: ${Object.keys(methods)}`);
throw new MethodNotFound(method);
}
if (params === void 0) {
return methods[method]();
}
return methods[method](...params);
};
var handleNotification = (json) => {
if (!json.method) {
logWarn(`handleNotification: no method specified in message ${json}`);
return;
}
onRequest(json.method, json.params);
};
var handleRequest = async (json) => {
if (!json.method) {
logWarn(`handleRequest: no method specified in message ${json}`);
sendError(json.id, new InvalidRequest("No method specified in message"));
return;
}
try {
const result = await onRequest(json.method, json.params);
if (result !== void 0 && result instanceof Promise) {
try {
await result;
sendResult(json.id, result);
} catch (e) {
sendError(json.id, e);
}
} else {
sendResult(json.id, result);
}
} catch (err) {
sendError(json.id, err);
}
};
var setup = (_methods, _sendRaw, _logBase) => {
Object.assign(methods, _methods);
sendRaw = _sendRaw;
_logBase !== void 0 && (logBase = _logBase);
};
var sendRequest = (method, params, timeoutMs) => {
return new Promise((resolve, reject) => {
const id = rpcIndex;
const req = { jsonrpc: "2.0", method, params, id };
rpcIndex += 1;
const callback = (result, err) => {
if (err) {
const jsError = new RPCError(err.code, err.message, err.data);
reject(jsError);
return;
}
resolve(result);
};
callback.timeout = setTimeout(() => {
delete pending[id];
logWarn(`Request id ${id} (${method}) timed out.`);
reject(new Error(`Request ${id} (${method}) timed out.`));
}, timeoutMs);
pending[id] = callback;
sendJson(req);
});
};
var isFigma = typeof figma !== "undefined";
var isUi = typeof parent !== "undefined";
var logBase2 = (level, ...msg) => console[level](`RPC in ${isFigma ? "logic" : isUi ? "ui" : "UNKNOWN"}:`, ...msg);
var strictObjectKeys = Object.keys;
var DEFAULT_OPTIONS = {
timeoutMs: 3e3,
logicTargetOrigin: "*",
uiTargetOrigin: "*"
};
var sendRaw2;
var setupMessaging = (pluginId, logicTargetOrigin, uiTargetOrigin) => {
if (typeof figma !== "undefined") {
figma.ui.on("message", (message) => handleRaw(message));
sendRaw2 = (message) => figma.ui.postMessage(message, { origin: logicTargetOrigin });
} else if (typeof parent !== "undefined") {
onmessage = (event) => handleRaw(event.data.pluginMessage);
if (pluginId !== void 0) {
sendRaw2 = (pluginMessage) => parent.postMessage({ pluginMessage, pluginId }, uiTargetOrigin);
} else {
sendRaw2 = (pluginMessage) => parent.postMessage({ pluginMessage }, uiTargetOrigin);
}
} else {
console.warn("Both parent and figma are undefined, it seems like the runtime is neither Figma nor a browser");
}
};
var createAPI = (methods2, hostType, options2) => {
const opts = __spreadValues2(__spreadValues2({}, DEFAULT_OPTIONS), options2);
const { timeoutMs, pluginId, logicTargetOrigin, uiTargetOrigin } = opts;
if (sendRaw2 === void 0) {
setupMessaging(pluginId, logicTargetOrigin, uiTargetOrigin);
}
if (hostType !== "undefined") {
if (sendRaw2 === void 0) {
console.error("sendRaw is undefined at during setup, the API will not work");
}
setup(methods2, sendRaw2, logBase2);
}
const api2 = strictObjectKeys(methods2).reduce((prev, p) => {
const method = async (...params) => {
if (hostType !== "undefined") {
return await methods2[p](...params);
}
return await sendRequest(p, params, timeoutMs);
};
prev[p] = method;
return prev;
}, {});
return api2;
};
var createUIAPI = (methods2, options2) => {
return createAPI(methods2, typeof parent, options2);
};
var createPluginAPI = (methods2, options2) => {
return createAPI(methods2, typeof figma, options2);
};
// src/index.ts
var import_figma_plugin_api = require("figma-plugin-api");
// src/typeUtils.ts
var isArray = Array.isArray;
var strictObjectKeys2 = Object.keys;
var strictObjectKeys = Object.keys;
var nodeCanHaveChildren = (node) => {

@@ -322,3 +84,3 @@ return "children" in node;

const descriptors = Object.getOwnPropertyDescriptors(Object.getPrototypeOf(node));
const getterKeys = strictObjectKeys2(descriptors);
const getterKeys = strictObjectKeys(descriptors);
let getters = getterKeys.filter((key) => typeof descriptors[key].get === "function");

@@ -344,3 +106,3 @@ getters.push("type");

);
for (const key of strictObjectKeys2(resolvedNode)) {
for (const key of strictObjectKeys(resolvedNode)) {
if (resolvedNode[key] === figma.mixed) {

@@ -382,3 +144,3 @@ resolvedNode[key] = FIGMA_MIXED;

// src/index.ts
var uiApi = createUIAPI({
var uiApiMethods = {
_onSelectionChange(selection) {

@@ -389,7 +151,9 @@ if (typeof window._figma_onSelectionChange !== "undefined") {

}
});
};
var uiApi = (0, import_figma_plugin_api.createUIAPI)(uiApiMethods);
var updateUiApiWithOptions = (rpcOptions) => {
uiApi = (0, import_figma_plugin_api.createUIAPI)(uiApiMethods, rpcOptions);
};
var selectionChangeHandler = () => {
console.log("Selection change handler", figma.currentPage.selection);
const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);
console.log("Filtered selection:", resolvedSelection);
uiApi._onSelectionChange(resolvedSelection);

@@ -423,6 +187,10 @@ };

};
var api = createPluginAPI({
var apiMethods = {
_registerForSelectionChange(opts) {
console.log("Register");
options = opts;
const apiOptions = opts.apiOptions;
if (apiOptions) {
updateUiApiWithOptions(apiOptions);
updateApiWithOptions(apiOptions);
}
figma.on("selectionchange", selectionChangeHandler);

@@ -439,3 +207,7 @@ figma.on("documentchange", documentChangeHandler);

}
});
};
var api = (0, import_figma_plugin_api.createPluginAPI)(apiMethods);
var updateApiWithOptions = (rpcOptions) => {
api = (0, import_figma_plugin_api.createPluginAPI)(apiMethods, rpcOptions);
};
var options;

@@ -454,2 +226,5 @@ var listeners = [];

// src/types.ts
var import_figma_plugin_api2 = require("figma-plugin-api");
// src/hook.ts

@@ -470,3 +245,2 @@ var defaultOptions = {

(0, import_react2.useEffect)(() => {
console.log("Hook mount");
const mount = async () => {

@@ -476,2 +250,6 @@ listeners.push(setSelection);

try {
if (opts.apiOptions) {
updateApiWithOptions(opts.apiOptions);
updateUiApiWithOptions(opts.apiOptions);
}
await api._registerForSelectionChange(opts);

@@ -478,0 +256,0 @@ } catch (e) {

+4
-4
{
"version": 3,
"sources": ["../src/hook.ts", "../src/useMountedEffect.ts", "../node_modules/figma-plugin-api/src/errors.ts", "../node_modules/figma-plugin-api/src/types.ts", "../node_modules/figma-plugin-api/src/rpc.ts", "../node_modules/figma-plugin-api/src/utils.ts", "../node_modules/figma-plugin-api/src/index.ts", "../src/typeUtils.ts", "../src/constants.ts", "../src/utils.ts", "../src/index.ts"],
"sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\n\nimport { useEffect, useState } from 'react';\n\nimport useMountedEffect from './useMountedEffect';\n\nimport { api, listeners, setlisteners } from '.';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FigmaSelectionHookOptions } from './types';\nexport { FIGMA_MIXED } from './constants';\n\nconst defaultOptions = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: false,\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false\n} as const satisfies ResolverOptions;\n\n/**\n * Only one config will take presence and it will be the config of the first hook that is mounted\n */\nconst useFigmaSelection = <const Options extends FigmaSelectionHookOptions>(\n hookOptions?: Options\n): [readonly SerializedResolvedNode<Options>[], (selection: readonly BareNode[]) => void] => {\n const opts = { ...defaultOptions, ...hookOptions } as const;\n\n const [selection, setSelection] = useState<readonly SerializedResolvedNode<Options>[]>([]);\n\n useMountedEffect(() => {\n console.warn('useFigmaSelection: changing options once mounted will not affect the behavior of the hook');\n }, [hookOptions]);\n\n useEffect(() => {\n console.log('Hook mount');\n const mount = async () => {\n // Typing the listeners explicitly is difficult due to the architecture, so we have to assert\n listeners.push(setSelection as unknown as FigmaSelectionListener);\n\n // if it's the first listener, register for selection change\n if (listeners.length === 1) {\n try {\n await api._registerForSelectionChange(opts);\n } catch (e) {\n console.error(e);\n }\n }\n };\n\n mount();\n\n return () => {\n setlisteners(listeners.filter((l) => l !== (setSelection as unknown as FigmaSelectionListener)));\n if (!listeners.length) {\n // if it was the last listener, then we don't have to listen to selection change anymore\n api._deregisterForSelectionChange();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [selection as readonly SerializedResolvedNode<Options>[], api._setSelection];\n};\n\nexport default useFigmaSelection;\n", "import { useEffect, useRef } from 'react';\n\nconst useMountedEffect = (effect: React.EffectCallback, deps?: React.DependencyList | undefined) => {\n const didMountRef = useRef(false);\n\n useEffect(() => {\n if (didMountRef.current) {\n return effect();\n }\n didMountRef.current = true;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n};\n\nexport default useMountedEffect;\n", "export class RPCError extends Error {\n code: number;\n message: string;\n data: unknown;\n\n constructor(code: number, message: string, data?: unknown) {\n super();\n this.code = code;\n this.message = message;\n this.data = data;\n }\n}\n\n/**\n * Invalid JSON was received by the server.\n * An error occurred on the server while parsing the JSON text.\n */\nexport class ParseError extends RPCError {\n constructor(data?: unknown) {\n super(-32700, 'Parse error', data);\n }\n}\n\n/**\n * The JSON sent is not a valid Request object.\n */\nexport class InvalidRequest extends RPCError {\n constructor(data?: unknown) {\n super(-32600, 'Invalid request', data);\n }\n}\n\n/**\n * The method does not exist / is not available.\n */\nexport class MethodNotFound extends RPCError {\n constructor(data?: unknown) {\n super(-32601, 'Method not found', data);\n }\n}\n\n/**\n * Invalid method parameter(s).\n */\nexport class InvalidParams extends RPCError {\n constructor(data?: unknown) {\n super(-32602, 'Invalid params', data);\n }\n}\n\n/**\n * Internal JSON-RPC error.\n */\nexport class InternalError extends RPCError {\n constructor(data?: unknown) {\n super(-32603, 'Internal error', data);\n }\n}\n", "/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport type LogLevel = 'log' | 'warn' | 'error';\n\nexport type AwaitedReturn<T extends (...params: any) => any> = ReturnType<T> extends Promise<any>\n ? Awaited<ReturnType<T>>\n : ReturnType<T>;\n\nexport type StrictParameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;\n\nexport type ApiFunctions = {\n [x: string]: (...params: any[]) => any;\n};\n\nexport type RPCMethodObject<T extends ApiFunctions> = {\n [K in keyof T]: (...args: StrictParameters<T[K]>) => Promise<AwaitedReturn<T[K]>>;\n};\n\nexport type RPCAPIReturnType<T extends RPCMethodObject<ApiFunctions>> = {\n [K in keyof T]: (...args: StrictParameters<T[K]>) => Promise<AwaitedReturn<T[K]>>;\n};\n\nexport type RPCErrorObject = {\n code: number;\n message: string;\n data: unknown;\n};\n\nexport interface RPCNotification<P extends unknown[]> {\n jsonrpc: '2.0';\n method: string;\n params?: P;\n}\n\nexport interface RPCRequest<P extends unknown[]> extends RPCNotification<P> {\n jsonrpc: '2.0';\n method: string;\n params?: P;\n id: number;\n}\n\nexport interface RPCResponseBase {\n jsonrpc: '2.0';\n id: number;\n}\n\nexport interface RPCResponseResult<T> extends RPCResponseBase {\n result: T;\n}\n\nexport interface RPCResponseError extends RPCResponseBase {\n error: RPCErrorObject;\n}\n\nexport type RPCResponse<T> = RPCResponseResult<T> | RPCResponseError;\n\nexport type RPCMessage<P extends unknown[], T> = RPCRequest<P> | RPCNotification<P> | RPCResponse<T>;\n\nexport type RPCCallBack<T extends (...args: any[]) => any> = {\n (result: AwaitedReturn<T> | undefined, error: RPCErrorObject | undefined): AwaitedReturn<T>;\n timeout: number;\n};\n\nexport type RPCOptions = {\n /**\n * Timeout in milliseconds\n *\n * Default: `3000`\n */\n timeoutMs?: number;\n /**\n * If your plugin UI is hosted (non-null origin), pluginId must be defined to allow messages to be sent\n */\n pluginId?: string;\n /**\n * Specifies what the origin of the plugin UI must be for a message to be dispatched from plugin logic to UI\n *\n * If defined, add `http://localhost:<port>` to this field in your local environment to allow messaging while running on a dev server\n *\n * Default: `'*'`\n */\n logicTargetOrigin?: string;\n /**\n * Specifies what the origin of the plugin logic must be for a message to be dispatched from UI to plugin logic\n *\n * Usually `'https://www.figma.com'`\n *\n * Default: `'*'`\n */\n uiTargetOrigin?: string;\n};\n\nexport type RPCDefaultOptions = Required<Omit<RPCOptions, 'pluginId'>>;\n\nexport type RPCSendRaw = <P extends unknown[], T>(message: RPCMessage<P, T>) => void;\n\nexport const isRpcOutGoing = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCNotification<P> | RPCRequest<P> => {\n return Object.prototype.hasOwnProperty.call(req, 'method');\n};\n\nexport const isRpcNotification = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCNotification<P> => {\n return isRpcOutGoing(req) && !Object.prototype.hasOwnProperty.call(req, 'id');\n};\n\nexport const isRpcRequest = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCRequest<P> => {\n return isRpcOutGoing(req) && Object.prototype.hasOwnProperty.call(req, 'id');\n};\n\nexport const isRpcResponseResult = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCResponseResult<T> => {\n return Object.prototype.hasOwnProperty.call(req, 'result');\n};\n\nexport const isRpcResponseError = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCResponseError => {\n return Object.prototype.hasOwnProperty.call(req, 'error');\n};\n\nexport const isRpcResponse = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCResponse<T> => {\n return isRpcResponseResult(req) || isRpcResponseError(req);\n};\n", "import { InvalidRequest, MethodNotFound, RPCError } from './errors';\nimport {\n ApiFunctions,\n LogLevel,\n RPCCallBack,\n RPCMessage,\n RPCMethodObject,\n RPCNotification,\n RPCRequest,\n RPCResponse,\n RPCSendRaw,\n isRpcNotification,\n isRpcResponse,\n isRpcResponseError,\n isRpcResponseResult\n} from './types';\n\n// Debugging setup\n// Generic logger that is overridden with a more specific one by setup\ntype Logger = (level: LogLevel, ...msg: unknown[]) => void;\nlet logBase: Logger = (level, ...msg) => console[level](...msg);\n\nconst logWarn = (...msg: unknown[]) => logBase('warn', ...msg);\nconst logError = (...msg: unknown[]) => logBase('error', ...msg);\n/* eslint-enable @typescript-eslint/no-unused-vars */\n\n// Methods for processing remote API calls (set up on-host)\nconst methods: RPCMethodObject<ApiFunctions> = {};\ntype MethodName = keyof typeof methods & string;\n\n// Pending API calls (used off-host)\nconst pending: Record<number, RPCCallBack<(typeof methods)[MethodName]>> = {};\nlet rpcIndex = 0;\n\nlet sendRaw: RPCSendRaw;\n\nconst sendJson = <P extends unknown[], T>(msg: RPCMessage<P, T>) => {\n try {\n sendRaw(msg);\n } catch (err) {\n logError(err);\n }\n};\n\nconst sendResult = <T>(id: number, result: T) => {\n sendJson({\n jsonrpc: '2.0',\n id,\n result\n });\n};\n\nconst sendError = (id: number, error: RPCError) => {\n sendJson({\n jsonrpc: '2.0',\n id,\n error: {\n code: error.code,\n message: error.message,\n data: error.data\n }\n });\n};\n\nconst handleRpc = <P extends unknown[], T>(json: RPCNotification<P> | RPCResponse<T>) => {\n if (!isRpcNotification(json)) {\n if (isRpcResponse(json)) {\n const callback = pending[json.id];\n if (!callback) {\n sendError(json.id, new InvalidRequest(`Missing callback for ${json.id}`));\n return;\n }\n if (callback.timeout) {\n clearTimeout(callback.timeout);\n }\n delete pending[json.id];\n callback(isRpcResponseResult(json) ? json.result : undefined, isRpcResponseError(json) ? json.error : undefined);\n } else {\n handleRequest(json);\n }\n } else {\n handleNotification(json);\n }\n};\n\nexport const handleRaw = <P extends unknown[], T>(data: RPCNotification<P> | RPCResponse<T>) => {\n try {\n if (!data) {\n return;\n }\n handleRpc(data);\n } catch (err) {\n logError('handleRaw error', err, data);\n }\n};\n\nconst onRequest = <K extends MethodName>(method: K, params: Parameters<(typeof methods)[K]> | undefined) => {\n if (!methods[method]) {\n logError(`onRequest: Method ${method} not found in methods: ${Object.keys(methods)}`);\n throw new MethodNotFound(method);\n }\n if (params === undefined) {\n return methods[method]();\n }\n return methods[method](...params);\n};\n\nconst handleNotification = <P extends unknown[]>(json: RPCNotification<P>) => {\n if (!json.method) {\n logWarn(`handleNotification: no method specified in message ${json}`);\n return;\n }\n\n onRequest(json.method, json.params);\n};\n\nconst handleRequest = async <P extends unknown[]>(json: RPCRequest<P>) => {\n if (!json.method) {\n logWarn(`handleRequest: no method specified in message ${json}`);\n sendError(json.id, new InvalidRequest('No method specified in message'));\n return;\n }\n\n try {\n const result = await onRequest(json.method, json.params);\n if (result !== undefined && result instanceof Promise) {\n try {\n await result;\n sendResult(json.id, result);\n } catch (e) {\n sendError(json.id, e as RPCError);\n }\n } else {\n sendResult(json.id, result);\n }\n } catch (err) {\n sendError(json.id, err as RPCError);\n }\n};\n\n/**\n *\n * @param _methods Remote API call handlers\n * @param _sendRaw Function for sending the raw JSON, e.g. `parent.postMessage`\n * @param _logBase Logger function for context-aware logging\n */\nexport const setup = <T extends RPCMethodObject<ApiFunctions>>(\n _methods: T,\n _sendRaw: RPCSendRaw,\n _logBase?: Logger\n) => {\n Object.assign(methods, _methods);\n sendRaw = _sendRaw;\n _logBase !== undefined && (logBase = _logBase);\n};\n\nexport const sendNotification = <P extends unknown[]>(method: string, params: P) => {\n sendJson({ jsonrpc: '2.0', method, params });\n};\n\nexport const sendRequest = <K extends MethodName, P extends unknown[]>(method: K, params: P, timeoutMs: number) => {\n return new Promise((resolve, reject) => {\n const id = rpcIndex;\n const req: RPCRequest<P> = { jsonrpc: '2.0', method, params, id };\n rpcIndex += 1;\n const callback: RPCCallBack<(typeof methods)[K]> = (result, err) => {\n if (err) {\n const jsError = new RPCError(err.code, err.message, err.data);\n reject(jsError);\n return;\n }\n resolve(result);\n };\n\n // Set the timeout\n callback.timeout = setTimeout(() => {\n delete pending[id];\n logWarn(`Request id ${id} (${method}) timed out.`);\n reject(new Error(`Request ${id} (${method}) timed out.`));\n }, timeoutMs);\n\n pending[id] = callback;\n sendJson(req);\n });\n};\n", "import { LogLevel } from './types';\n\nconst isFigma = typeof figma !== 'undefined';\nconst isUi = typeof parent !== 'undefined';\nexport const logBase = (level: LogLevel, ...msg: unknown[]) =>\n console[level](`RPC in ${isFigma ? 'logic' : isUi ? 'ui' : 'UNKNOWN'}:`, ...msg);\n\nexport const strictObjectKeys = Object.keys as <T extends Record<string, unknown>>(obj: T) => Array<keyof T>;\n", "import { handleRaw, sendRequest, setup } from './rpc';\nimport {\n ApiFunctions,\n AwaitedReturn,\n RPCAPIReturnType,\n RPCDefaultOptions,\n RPCOptions,\n RPCSendRaw,\n StrictParameters\n} from './types';\nimport { logBase, strictObjectKeys } from './utils';\n\nexport { ApiFunctions, RPCAPIReturnType, RPCOptions } from './types';\n\nconst DEFAULT_OPTIONS: RPCDefaultOptions = {\n timeoutMs: 3000,\n logicTargetOrigin: '*',\n uiTargetOrigin: '*'\n};\n\nlet sendRaw: RPCSendRaw;\n/**\n * Set up sending and receiving of messages for Figma plugin logic and UI\n */\nconst setupMessaging = (pluginId: string | undefined, logicTargetOrigin: string, uiTargetOrigin: string) => {\n if (typeof figma !== 'undefined') {\n figma.ui.on('message', (message) => handleRaw(message));\n\n sendRaw = (message) => figma.ui.postMessage(message, { origin: logicTargetOrigin });\n } else if (typeof parent !== 'undefined') {\n onmessage = (event) => handleRaw(event.data.pluginMessage);\n\n if (pluginId !== undefined) {\n sendRaw = (pluginMessage) => parent.postMessage({ pluginMessage, pluginId }, uiTargetOrigin);\n } else {\n sendRaw = (pluginMessage) => parent.postMessage({ pluginMessage }, uiTargetOrigin);\n }\n } else {\n // Should not happen but log an error just in case\n console.warn('Both parent and figma are undefined, it seems like the runtime is neither Figma nor a browser');\n }\n};\n\n/**\n * Creates one side of a JSON-RPC API for a Figma plugin\n * @param hostType A typeof string of an object that differentiates between on-host and off-host.\n * For example, if `typeof figma` is used, when `figma` is defined the creator is considered to be on-host\n * and the methods for processing the remote API calls are set up.\n * Off-host the creator sets up the equivalent remote API calls to make requests to host.\n */\nconst createAPI = <T extends ApiFunctions>(\n methods: T,\n hostType: string,\n options?: RPCOptions\n): Readonly<RPCAPIReturnType<T>> => {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n const { timeoutMs, pluginId, logicTargetOrigin, uiTargetOrigin } = opts;\n\n if (sendRaw === undefined) {\n setupMessaging(pluginId, logicTargetOrigin, uiTargetOrigin);\n }\n\n if (hostType !== 'undefined') {\n // Should not happen but log an error just in case\n if (sendRaw === undefined) {\n console.error('sendRaw is undefined at during setup, the API will not work');\n }\n\n setup(methods, sendRaw, logBase);\n }\n\n const api: RPCAPIReturnType<T> = strictObjectKeys(methods).reduce((prev, p) => {\n const method = async (...params: StrictParameters<T[keyof T]>) => {\n if (hostType !== 'undefined') {\n return (await methods[p](...params)) as AwaitedReturn<T[keyof T]>;\n }\n\n return (await sendRequest(p as string, params, timeoutMs)) as AwaitedReturn<T[keyof T]>;\n };\n\n prev[p] = method;\n return prev;\n }, {} as RPCAPIReturnType<T>);\n\n return api;\n};\n\n/**\n * Create a set of methods that can be called from plugin logic and are executed in plugin UI\n * This side has access to the browser API\n */\nexport const createUIAPI = <T extends ApiFunctions>(\n methods: T,\n options?: RPCOptions\n): Readonly<RPCAPIReturnType<T>> => {\n return createAPI(methods, typeof parent, options);\n};\n\n/**\n * Create a set of methods that can be called from plugin UI and are executed in plugin logic\n * This side has acccess to the `figma` API\n */\nexport const createPluginAPI = <T extends ApiFunctions>(\n methods: T,\n options?: RPCOptions\n): Readonly<RPCAPIReturnType<T>> => {\n return createAPI(methods, typeof figma, options);\n};\n", "import { ArrayType } from './typePrimitives';\n\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n", "/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n", "import { FIGMA_MIXED } from './constants';\nimport { nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { ResolvedNode, ResolverOptions, SceneNodePropertyKey, SerializedResolvedNode } from './types';\n\nconst defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {\n return (\n // Can only get component property definitions of a component set or non-variant component\n !(key === 'componentPropertyDefinitions' && node.parent?.type === 'COMPONENT_SET') &&\n // reading horizontalPadding and verticalPadding is no longer supported as left and right padding may differ\n key !== 'horizontalPadding' &&\n key !== 'verticalPadding'\n );\n};\n\nconst resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(\n node: Node,\n propertyKeys?: Keys\n): ResolvedNode<Node, Keys> => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {\n [P in keyof Node]: TypedPropertyDescriptor<Node[P]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');\n\n // type has to be included manually as it doesn't have a getter but isn't a static property either\n getters.push('type');\n\n if (propertyKeys) {\n getters = getters.filter((key) => propertyKeys.includes(key as SceneNodePropertyKey));\n }\n\n getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));\n\n const objectWithProperties = {\n id: node.id\n } as Node;\n\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n object: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, addAncestorsVisibleProperty } = options;\n\n const resolvedNode = resolveNodeProperties(\n object,\n resolveProperties === 'all' ? undefined : resolveProperties\n ) as Record<string, unknown>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n if (resolvedNode[key] === figma.mixed) {\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\n// TODO: Resolve variables\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n nodes.forEach((node) => {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n });\n } else {\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n });\n }\n\n return result;\n};\n", "import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nexport const uiApi = createUIAPI({\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n});\n\nconst selectionChangeHandler = () => {\n console.log('Selection change handler', figma.currentPage.selection);\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n console.log('Filtered selection:', resolvedSelection);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n });\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nexport const api = createPluginAPI({\n _registerForSelectionChange(opts: FigmaSelectionHookOptions) {\n console.log('Register');\n options = opts;\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n});\n\nlet options: FigmaSelectionHookOptions;\n\nexport let listeners: FigmaSelectionListener[] = [];\n\nexport const setlisteners = (newListeners: FigmaSelectionListener[]) => {\n listeners = newListeners;\n};\n\n// In plugin UI, add a global function to receive selection change events\nif (typeof window !== 'undefined') {\n window._figma_onSelectionChange = (selection) => {\n listeners.forEach((l) => {\n l(selection);\n });\n };\n}\n"],
"mappings": "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",
"names": ["import_react", "logBase", "sendRaw", "methods", "options", "__spreadValues", "api", "strictObjectKeys", "strictObjectKeys", "options"]
"sources": ["../src/hook.ts", "../src/useMountedEffect.ts", "../src/index.ts", "../src/typeUtils.ts", "../src/constants.ts", "../src/utils.ts", "../src/types.ts"],
"sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\n\nimport { useEffect, useState } from 'react';\n\nimport useMountedEffect from './useMountedEffect';\n\nimport { api, listeners, setlisteners, updateApiWithOptions, updateUiApiWithOptions } from '.';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FigmaSelectionHookOptions } from './types';\nexport { FIGMA_MIXED } from './constants';\n\nconst defaultOptions = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: false,\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false\n} as const satisfies ResolverOptions;\n\n/**\n * Only one config will take presence and it will be the config of the first hook that is mounted\n */\nconst useFigmaSelection = <const Options extends FigmaSelectionHookOptions>(\n hookOptions?: Options\n): [readonly SerializedResolvedNode<Options>[], (selection: readonly BareNode[]) => void] => {\n const opts = { ...defaultOptions, ...hookOptions } as const;\n\n const [selection, setSelection] = useState<readonly SerializedResolvedNode<Options>[]>([]);\n\n useMountedEffect(() => {\n console.warn('useFigmaSelection: changing options once mounted will not affect the behavior of the hook');\n }, [hookOptions]);\n\n useEffect(() => {\n const mount = async () => {\n // Typing the listeners explicitly is difficult due to the architecture, so we have to assert\n listeners.push(setSelection as unknown as FigmaSelectionListener);\n\n // if it's the first listener, register for selection change\n if (listeners.length === 1) {\n try {\n if (opts.apiOptions) {\n updateApiWithOptions(opts.apiOptions);\n updateUiApiWithOptions(opts.apiOptions);\n }\n await api._registerForSelectionChange(opts);\n } catch (e) {\n console.error(e);\n }\n }\n };\n\n mount();\n\n return () => {\n setlisteners(listeners.filter((l) => l !== (setSelection as unknown as FigmaSelectionListener)));\n if (!listeners.length) {\n // if it was the last listener, then we don't have to listen to selection change anymore\n api._deregisterForSelectionChange();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [selection as readonly SerializedResolvedNode<Options>[], api._setSelection];\n};\n\nexport default useFigmaSelection;\n", "import { useEffect, useRef } from 'react';\n\nconst useMountedEffect = (effect: React.EffectCallback, deps?: React.DependencyList | undefined) => {\n const didMountRef = useRef(false);\n\n useEffect(() => {\n if (didMountRef.current) {\n return effect();\n }\n didMountRef.current = true;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n};\n\nexport default useMountedEffect;\n", "import { createUIAPI, createPluginAPI, RPCOptions } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n });\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: FigmaSelectionHookOptions;\n\nexport let listeners: FigmaSelectionListener[] = [];\n\nexport const setlisteners = (newListeners: FigmaSelectionListener[]) => {\n listeners = newListeners;\n};\n\n// In plugin UI, add a global function to receive selection change events\nif (typeof window !== 'undefined') {\n window._figma_onSelectionChange = (selection) => {\n listeners.forEach((l) => {\n l(selection);\n });\n };\n}\n", "import { ArrayType } from './typePrimitives';\n\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n", "/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n", "import { FIGMA_MIXED } from './constants';\nimport { nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { ResolvedNode, ResolverOptions, SceneNodePropertyKey, SerializedResolvedNode } from './types';\n\nconst defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {\n return (\n // Can only get component property definitions of a component set or non-variant component\n !(key === 'componentPropertyDefinitions' && node.parent?.type === 'COMPONENT_SET') &&\n // reading horizontalPadding and verticalPadding is no longer supported as left and right padding may differ\n key !== 'horizontalPadding' &&\n key !== 'verticalPadding'\n );\n};\n\nconst resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(\n node: Node,\n propertyKeys?: Keys\n): ResolvedNode<Node, Keys> => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {\n [P in keyof Node]: TypedPropertyDescriptor<Node[P]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');\n\n // type has to be included manually as it doesn't have a getter but isn't a static property either\n getters.push('type');\n\n if (propertyKeys) {\n getters = getters.filter((key) => propertyKeys.includes(key as SceneNodePropertyKey));\n }\n\n getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));\n\n const objectWithProperties = {\n id: node.id\n } as Node;\n\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n object: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, addAncestorsVisibleProperty } = options;\n\n const resolvedNode = resolveNodeProperties(\n object,\n resolveProperties === 'all' ? undefined : resolveProperties\n ) as Record<string, unknown>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n if (resolvedNode[key] === figma.mixed) {\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\n// TODO: Resolve variables\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n nodes.forEach((node) => {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n });\n } else {\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n });\n }\n\n return result;\n};\n", "import { RPCOptions } from 'figma-plugin-api';\n\nimport { FIGMA_MIXED } from './constants';\nimport { ApplicableNonFunctionPropertyKeys, ArrayElementUnion, NonFunctionPropertyKeys } from './typePrimitives';\n\n// For typedoc\nexport { RPCOptions } from 'figma-plugin-api';\nexport { FIGMA_MIXED } from './constants';\n\n/**\n * @internal\n * Describes the properties of an unresolved node\n */\nexport type BareNode = Pick<SceneNode, 'id'>;\n\ntype SceneNodeType = SceneNode['type'];\n\n/**\n * @internal\n */\nexport type SceneNodeFromTypes<T extends readonly SceneNodeType[] | undefined = undefined> = T extends undefined\n ? SceneNode\n : // @ts-expect-error If the check is flipped, the type widens to SceneNode. On simpler types this doesn't happen but it seems like Extract does this.\n ExtractedSceneNode<ArrayElementUnion<T>>;\n\n/**\n * @internal\n * Utility type to get only matching node types from the SceneNode union type.\n */\ntype ExtractedSceneNode<T extends SceneNodeType> = Extract<SceneNode, { type: T }>;\n\n/**\n * @internal\n * Get the non-function property keys for a SceneNode\n */\nexport type SceneNodePropertyKey<T extends SceneNodeType | undefined = undefined> = NonFunctionPropertyKeys<\n T extends SceneNodeType ? ExtractedSceneNode<T> : SceneNode\n>;\n\nexport type OptSceneNodeProperties = readonly SceneNodePropertyKey[] | 'all';\n\n/**\n * Use `satisfies` (for TS >= 4.9) with this type to allow for type checking the options object\n * while the type of the object remains exact.\n *\n * This allows us to infer the type of the returned nodes correctly.\n *\n * Example:\n * ```typescript\n * const options = {\n * nodeTypes: ['TEXT', 'FRAME'],\n * resolveProperties: ['name', 'characters', 'children]\n * } satisfies FigmaSelectionHookOptions;\n * ```\n */\nexport type FigmaSelectionHookOptions = {\n /**\n * Only return specific types of nodes.\n *\n * If left undefined, all nodes in the selection will be returned.\n *\n * Default: `undefined`\n */\n nodeTypes?: readonly SceneNodeType[] | undefined;\n /**\n * Figma node properties are lazy-loaded, so to use any property you have to resolve it first.\n *\n * Resolving all node properties causes a performance hit, so you can specify which properties you want to resolve.\n *\n * If set to `[]`, no properties will be resolved and you will only get the ids of the nodes.\n *\n * Node methods (such as `getPluginData`) will never be resolved.\n *\n * Default: `'all'`\n */\n resolveProperties?: OptSceneNodeProperties;\n /**\n * Resolve children nodes of the selection.\n *\n * If used with `nodeTypes`, all nodes of the specified types will be returned as a flat array.\n *\n * Default: `false`\n */\n resolveChildren?: boolean;\n /**\n * Resolve bound variables of the selection.\n *\n * Default: `false`\n */\n resolveVariables?: boolean;\n /**\n * Add `ancestorsVisible` property to all nodes.\n *\n * This property is true if all ancestors of the node are visible.\n *\n * Default: `false`\n */\n addAncestorsVisibleProperty?: boolean;\n /**\n * Options for figma-plugin-api\n *\n * Default: see the RPCOptions type\n */\n apiOptions?: RPCOptions | undefined;\n};\n\n/**\n * @internal\n */\nexport type ResolverOptions = Omit<FigmaSelectionHookOptions, 'apiOptions'>;\n\ntype SerializedNodeProperty<\n Prop,\n Node extends SceneNode,\n Options extends ResolverOptions\n> = Prop extends PluginAPI['mixed']\n ? typeof FIGMA_MIXED\n : Prop extends readonly SceneNode[]\n ? Options['resolveChildren'] extends true\n ? readonly SerializedNode<Node, Options>[]\n : readonly BareNode[]\n : Prop;\n\ntype ApplicableNodeKeys<\n Node extends SceneNode,\n Properties extends OptSceneNodeProperties\n> = Properties extends readonly SceneNodePropertyKey[]\n ? ApplicableNonFunctionPropertyKeys<Node, ArrayElementUnion<Properties>>\n : NonFunctionPropertyKeys<Node>;\n\n/**\n * @internal\n */\nexport type SerializedNode<Node extends SceneNode, Options extends ResolverOptions> = Node extends SceneNode\n ? {\n type: Node['type'];\n id: Node['id'];\n } & {\n [Key in Options['resolveProperties'] extends SceneNodePropertyKey[]\n ? ApplicableNodeKeys<Node, Options['resolveProperties']>\n : keyof Node]: SerializedNodeProperty<Node[Key], Node, Options>;\n }\n : never;\n\n/**\n * @internal\n */\nexport type ResolvedNode<\n Node extends SceneNode,\n Keys extends readonly SceneNodePropertyKey[] | undefined = undefined\n> = Pick<\n Node,\n Keys extends readonly SceneNodePropertyKey[]\n ? ApplicableNonFunctionPropertyKeys<Node, ArrayElementUnion<Keys>> | 'id' | 'type'\n : ApplicableNonFunctionPropertyKeys<Node, SceneNodePropertyKey>\n>;\n\n/**\n * @internal\n */\ntype SerializedResolvedNodeBase<Node extends SceneNode, Options extends ResolverOptions> = Node extends SceneNode\n ? SerializedNode<Node, Options>\n : never;\n\n/**\n * @internal\n */\ntype AncestorsVisibleMixin = {\n ancestorsVisible: boolean;\n};\n\n/**\n * @internal\n */\ntype ResolveVariablesMixin = {\n boundVariableInstances: readonly Variable[];\n};\n\n/**\n * @internal\n * All Figma nodes are converted to this type for serialization and sending to the plugin UI\n */\nexport type SerializedResolvedNode<Options extends ResolverOptions> =\n Options['addAncestorsVisibleProperty'] extends true\n ? Options['resolveVariables'] extends true\n ? SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options> &\n AncestorsVisibleMixin &\n ResolveVariablesMixin\n : SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options> & AncestorsVisibleMixin\n : SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options>;\n\n/**\n * @internal\n */\nexport type FigmaSelectionListener = (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n"],
"mappings": "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",
"names": ["import_react", "options", "import_figma_plugin_api"]
}

@@ -37,246 +37,8 @@ var __defProp = Object.defineProperty;

// node_modules/figma-plugin-api/lib/index.mjs
var __defProp2 = Object.defineProperty;
var __getOwnPropSymbols2 = Object.getOwnPropertySymbols;
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
var __propIsEnum2 = Object.prototype.propertyIsEnumerable;
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues2 = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp2.call(b, prop))
__defNormalProp2(a, prop, b[prop]);
if (__getOwnPropSymbols2)
for (var prop of __getOwnPropSymbols2(b)) {
if (__propIsEnum2.call(b, prop))
__defNormalProp2(a, prop, b[prop]);
}
return a;
};
var __publicField = (obj, key, value) => {
__defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
var RPCError = class extends Error {
constructor(code, message, data) {
super();
__publicField(this, "code");
__publicField(this, "message");
__publicField(this, "data");
this.code = code;
this.message = message;
this.data = data;
}
};
var InvalidRequest = class extends RPCError {
constructor(data) {
super(-32600, "Invalid request", data);
}
};
var MethodNotFound = class extends RPCError {
constructor(data) {
super(-32601, "Method not found", data);
}
};
var isRpcOutGoing = (req) => {
return Object.prototype.hasOwnProperty.call(req, "method");
};
var isRpcNotification = (req) => {
return isRpcOutGoing(req) && !Object.prototype.hasOwnProperty.call(req, "id");
};
var isRpcResponseResult = (req) => {
return Object.prototype.hasOwnProperty.call(req, "result");
};
var isRpcResponseError = (req) => {
return Object.prototype.hasOwnProperty.call(req, "error");
};
var isRpcResponse = (req) => {
return isRpcResponseResult(req) || isRpcResponseError(req);
};
var logBase = (level, ...msg) => console[level](...msg);
var logWarn = (...msg) => logBase("warn", ...msg);
var logError = (...msg) => logBase("error", ...msg);
var methods = {};
var pending = {};
var rpcIndex = 0;
var sendRaw;
var sendJson = (msg) => {
try {
sendRaw(msg);
} catch (err) {
logError(err);
}
};
var sendResult = (id, result) => {
sendJson({
jsonrpc: "2.0",
id,
result
});
};
var sendError = (id, error) => {
sendJson({
jsonrpc: "2.0",
id,
error: {
code: error.code,
message: error.message,
data: error.data
}
});
};
var handleRpc = (json) => {
if (!isRpcNotification(json)) {
if (isRpcResponse(json)) {
const callback = pending[json.id];
if (!callback) {
sendError(json.id, new InvalidRequest(`Missing callback for ${json.id}`));
return;
}
if (callback.timeout) {
clearTimeout(callback.timeout);
}
delete pending[json.id];
callback(isRpcResponseResult(json) ? json.result : void 0, isRpcResponseError(json) ? json.error : void 0);
} else {
handleRequest(json);
}
} else {
handleNotification(json);
}
};
var handleRaw = (data) => {
try {
if (!data) {
return;
}
handleRpc(data);
} catch (err) {
logError("handleRaw error", err, data);
}
};
var onRequest = (method, params) => {
if (!methods[method]) {
logError(`onRequest: Method ${method} not found in methods: ${Object.keys(methods)}`);
throw new MethodNotFound(method);
}
if (params === void 0) {
return methods[method]();
}
return methods[method](...params);
};
var handleNotification = (json) => {
if (!json.method) {
logWarn(`handleNotification: no method specified in message ${json}`);
return;
}
onRequest(json.method, json.params);
};
var handleRequest = async (json) => {
if (!json.method) {
logWarn(`handleRequest: no method specified in message ${json}`);
sendError(json.id, new InvalidRequest("No method specified in message"));
return;
}
try {
const result = await onRequest(json.method, json.params);
if (result !== void 0 && result instanceof Promise) {
try {
await result;
sendResult(json.id, result);
} catch (e) {
sendError(json.id, e);
}
} else {
sendResult(json.id, result);
}
} catch (err) {
sendError(json.id, err);
}
};
var setup = (_methods, _sendRaw, _logBase) => {
Object.assign(methods, _methods);
sendRaw = _sendRaw;
_logBase !== void 0 && (logBase = _logBase);
};
var sendRequest = (method, params, timeoutMs) => {
return new Promise((resolve, reject) => {
const id = rpcIndex;
const req = { jsonrpc: "2.0", method, params, id };
rpcIndex += 1;
const callback = (result, err) => {
if (err) {
const jsError = new RPCError(err.code, err.message, err.data);
reject(jsError);
return;
}
resolve(result);
};
callback.timeout = setTimeout(() => {
delete pending[id];
logWarn(`Request id ${id} (${method}) timed out.`);
reject(new Error(`Request ${id} (${method}) timed out.`));
}, timeoutMs);
pending[id] = callback;
sendJson(req);
});
};
var isFigma = typeof figma !== "undefined";
var isUi = typeof parent !== "undefined";
var logBase2 = (level, ...msg) => console[level](`RPC in ${isFigma ? "logic" : isUi ? "ui" : "UNKNOWN"}:`, ...msg);
var strictObjectKeys = Object.keys;
var DEFAULT_OPTIONS = {
timeoutMs: 3e3,
logicTargetOrigin: "*",
uiTargetOrigin: "*"
};
var sendRaw2;
var setupMessaging = (pluginId, logicTargetOrigin, uiTargetOrigin) => {
if (typeof figma !== "undefined") {
figma.ui.on("message", (message) => handleRaw(message));
sendRaw2 = (message) => figma.ui.postMessage(message, { origin: logicTargetOrigin });
} else if (typeof parent !== "undefined") {
onmessage = (event) => handleRaw(event.data.pluginMessage);
if (pluginId !== void 0) {
sendRaw2 = (pluginMessage) => parent.postMessage({ pluginMessage, pluginId }, uiTargetOrigin);
} else {
sendRaw2 = (pluginMessage) => parent.postMessage({ pluginMessage }, uiTargetOrigin);
}
} else {
console.warn("Both parent and figma are undefined, it seems like the runtime is neither Figma nor a browser");
}
};
var createAPI = (methods2, hostType, options2) => {
const opts = __spreadValues2(__spreadValues2({}, DEFAULT_OPTIONS), options2);
const { timeoutMs, pluginId, logicTargetOrigin, uiTargetOrigin } = opts;
if (sendRaw2 === void 0) {
setupMessaging(pluginId, logicTargetOrigin, uiTargetOrigin);
}
if (hostType !== "undefined") {
if (sendRaw2 === void 0) {
console.error("sendRaw is undefined at during setup, the API will not work");
}
setup(methods2, sendRaw2, logBase2);
}
const api2 = strictObjectKeys(methods2).reduce((prev, p) => {
const method = async (...params) => {
if (hostType !== "undefined") {
return await methods2[p](...params);
}
return await sendRequest(p, params, timeoutMs);
};
prev[p] = method;
return prev;
}, {});
return api2;
};
var createUIAPI = (methods2, options2) => {
return createAPI(methods2, typeof parent, options2);
};
var createPluginAPI = (methods2, options2) => {
return createAPI(methods2, typeof figma, options2);
};
// src/index.ts
import { createUIAPI, createPluginAPI } from "figma-plugin-api";
// src/typeUtils.ts
var isArray = Array.isArray;
var strictObjectKeys2 = Object.keys;
var strictObjectKeys = Object.keys;
var nodeCanHaveChildren = (node) => {

@@ -300,3 +62,3 @@ return "children" in node;

const descriptors = Object.getOwnPropertyDescriptors(Object.getPrototypeOf(node));
const getterKeys = strictObjectKeys2(descriptors);
const getterKeys = strictObjectKeys(descriptors);
let getters = getterKeys.filter((key) => typeof descriptors[key].get === "function");

@@ -322,3 +84,3 @@ getters.push("type");

);
for (const key of strictObjectKeys2(resolvedNode)) {
for (const key of strictObjectKeys(resolvedNode)) {
if (resolvedNode[key] === figma.mixed) {

@@ -360,3 +122,3 @@ resolvedNode[key] = FIGMA_MIXED;

// src/index.ts
var uiApi = createUIAPI({
var uiApiMethods = {
_onSelectionChange(selection) {

@@ -367,7 +129,9 @@ if (typeof window._figma_onSelectionChange !== "undefined") {

}
});
};
var uiApi = createUIAPI(uiApiMethods);
var updateUiApiWithOptions = (rpcOptions) => {
uiApi = createUIAPI(uiApiMethods, rpcOptions);
};
var selectionChangeHandler = () => {
console.log("Selection change handler", figma.currentPage.selection);
const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);
console.log("Filtered selection:", resolvedSelection);
uiApi._onSelectionChange(resolvedSelection);

@@ -401,6 +165,10 @@ };

};
var api = createPluginAPI({
var apiMethods = {
_registerForSelectionChange(opts) {
console.log("Register");
options = opts;
const apiOptions = opts.apiOptions;
if (apiOptions) {
updateUiApiWithOptions(apiOptions);
updateApiWithOptions(apiOptions);
}
figma.on("selectionchange", selectionChangeHandler);

@@ -417,3 +185,7 @@ figma.on("documentchange", documentChangeHandler);

}
});
};
var api = createPluginAPI(apiMethods);
var updateApiWithOptions = (rpcOptions) => {
api = createPluginAPI(apiMethods, rpcOptions);
};
var options;

@@ -432,2 +204,5 @@ var listeners = [];

// src/types.ts
import { RPCOptions as RPCOptions2 } from "figma-plugin-api";
// src/hook.ts

@@ -448,3 +223,2 @@ var defaultOptions = {

useEffect2(() => {
console.log("Hook mount");
const mount = async () => {

@@ -454,2 +228,6 @@ listeners.push(setSelection);

try {
if (opts.apiOptions) {
updateApiWithOptions(opts.apiOptions);
updateUiApiWithOptions(opts.apiOptions);
}
await api._registerForSelectionChange(opts);

@@ -456,0 +234,0 @@ } catch (e) {

{
"version": 3,
"sources": ["../src/hook.ts", "../src/useMountedEffect.ts", "../node_modules/figma-plugin-api/src/errors.ts", "../node_modules/figma-plugin-api/src/types.ts", "../node_modules/figma-plugin-api/src/rpc.ts", "../node_modules/figma-plugin-api/src/utils.ts", "../node_modules/figma-plugin-api/src/index.ts", "../src/typeUtils.ts", "../src/constants.ts", "../src/utils.ts", "../src/index.ts"],
"sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\n\nimport { useEffect, useState } from 'react';\n\nimport useMountedEffect from './useMountedEffect';\n\nimport { api, listeners, setlisteners } from '.';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FigmaSelectionHookOptions } from './types';\nexport { FIGMA_MIXED } from './constants';\n\nconst defaultOptions = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: false,\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false\n} as const satisfies ResolverOptions;\n\n/**\n * Only one config will take presence and it will be the config of the first hook that is mounted\n */\nconst useFigmaSelection = <const Options extends FigmaSelectionHookOptions>(\n hookOptions?: Options\n): [readonly SerializedResolvedNode<Options>[], (selection: readonly BareNode[]) => void] => {\n const opts = { ...defaultOptions, ...hookOptions } as const;\n\n const [selection, setSelection] = useState<readonly SerializedResolvedNode<Options>[]>([]);\n\n useMountedEffect(() => {\n console.warn('useFigmaSelection: changing options once mounted will not affect the behavior of the hook');\n }, [hookOptions]);\n\n useEffect(() => {\n console.log('Hook mount');\n const mount = async () => {\n // Typing the listeners explicitly is difficult due to the architecture, so we have to assert\n listeners.push(setSelection as unknown as FigmaSelectionListener);\n\n // if it's the first listener, register for selection change\n if (listeners.length === 1) {\n try {\n await api._registerForSelectionChange(opts);\n } catch (e) {\n console.error(e);\n }\n }\n };\n\n mount();\n\n return () => {\n setlisteners(listeners.filter((l) => l !== (setSelection as unknown as FigmaSelectionListener)));\n if (!listeners.length) {\n // if it was the last listener, then we don't have to listen to selection change anymore\n api._deregisterForSelectionChange();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [selection as readonly SerializedResolvedNode<Options>[], api._setSelection];\n};\n\nexport default useFigmaSelection;\n", "import { useEffect, useRef } from 'react';\n\nconst useMountedEffect = (effect: React.EffectCallback, deps?: React.DependencyList | undefined) => {\n const didMountRef = useRef(false);\n\n useEffect(() => {\n if (didMountRef.current) {\n return effect();\n }\n didMountRef.current = true;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n};\n\nexport default useMountedEffect;\n", "export class RPCError extends Error {\n code: number;\n message: string;\n data: unknown;\n\n constructor(code: number, message: string, data?: unknown) {\n super();\n this.code = code;\n this.message = message;\n this.data = data;\n }\n}\n\n/**\n * Invalid JSON was received by the server.\n * An error occurred on the server while parsing the JSON text.\n */\nexport class ParseError extends RPCError {\n constructor(data?: unknown) {\n super(-32700, 'Parse error', data);\n }\n}\n\n/**\n * The JSON sent is not a valid Request object.\n */\nexport class InvalidRequest extends RPCError {\n constructor(data?: unknown) {\n super(-32600, 'Invalid request', data);\n }\n}\n\n/**\n * The method does not exist / is not available.\n */\nexport class MethodNotFound extends RPCError {\n constructor(data?: unknown) {\n super(-32601, 'Method not found', data);\n }\n}\n\n/**\n * Invalid method parameter(s).\n */\nexport class InvalidParams extends RPCError {\n constructor(data?: unknown) {\n super(-32602, 'Invalid params', data);\n }\n}\n\n/**\n * Internal JSON-RPC error.\n */\nexport class InternalError extends RPCError {\n constructor(data?: unknown) {\n super(-32603, 'Internal error', data);\n }\n}\n", "/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport type LogLevel = 'log' | 'warn' | 'error';\n\nexport type AwaitedReturn<T extends (...params: any) => any> = ReturnType<T> extends Promise<any>\n ? Awaited<ReturnType<T>>\n : ReturnType<T>;\n\nexport type StrictParameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;\n\nexport type ApiFunctions = {\n [x: string]: (...params: any[]) => any;\n};\n\nexport type RPCMethodObject<T extends ApiFunctions> = {\n [K in keyof T]: (...args: StrictParameters<T[K]>) => Promise<AwaitedReturn<T[K]>>;\n};\n\nexport type RPCAPIReturnType<T extends RPCMethodObject<ApiFunctions>> = {\n [K in keyof T]: (...args: StrictParameters<T[K]>) => Promise<AwaitedReturn<T[K]>>;\n};\n\nexport type RPCErrorObject = {\n code: number;\n message: string;\n data: unknown;\n};\n\nexport interface RPCNotification<P extends unknown[]> {\n jsonrpc: '2.0';\n method: string;\n params?: P;\n}\n\nexport interface RPCRequest<P extends unknown[]> extends RPCNotification<P> {\n jsonrpc: '2.0';\n method: string;\n params?: P;\n id: number;\n}\n\nexport interface RPCResponseBase {\n jsonrpc: '2.0';\n id: number;\n}\n\nexport interface RPCResponseResult<T> extends RPCResponseBase {\n result: T;\n}\n\nexport interface RPCResponseError extends RPCResponseBase {\n error: RPCErrorObject;\n}\n\nexport type RPCResponse<T> = RPCResponseResult<T> | RPCResponseError;\n\nexport type RPCMessage<P extends unknown[], T> = RPCRequest<P> | RPCNotification<P> | RPCResponse<T>;\n\nexport type RPCCallBack<T extends (...args: any[]) => any> = {\n (result: AwaitedReturn<T> | undefined, error: RPCErrorObject | undefined): AwaitedReturn<T>;\n timeout: number;\n};\n\nexport type RPCOptions = {\n /**\n * Timeout in milliseconds\n *\n * Default: `3000`\n */\n timeoutMs?: number;\n /**\n * If your plugin UI is hosted (non-null origin), pluginId must be defined to allow messages to be sent\n */\n pluginId?: string;\n /**\n * Specifies what the origin of the plugin UI must be for a message to be dispatched from plugin logic to UI\n *\n * If defined, add `http://localhost:<port>` to this field in your local environment to allow messaging while running on a dev server\n *\n * Default: `'*'`\n */\n logicTargetOrigin?: string;\n /**\n * Specifies what the origin of the plugin logic must be for a message to be dispatched from UI to plugin logic\n *\n * Usually `'https://www.figma.com'`\n *\n * Default: `'*'`\n */\n uiTargetOrigin?: string;\n};\n\nexport type RPCDefaultOptions = Required<Omit<RPCOptions, 'pluginId'>>;\n\nexport type RPCSendRaw = <P extends unknown[], T>(message: RPCMessage<P, T>) => void;\n\nexport const isRpcOutGoing = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCNotification<P> | RPCRequest<P> => {\n return Object.prototype.hasOwnProperty.call(req, 'method');\n};\n\nexport const isRpcNotification = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCNotification<P> => {\n return isRpcOutGoing(req) && !Object.prototype.hasOwnProperty.call(req, 'id');\n};\n\nexport const isRpcRequest = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCRequest<P> => {\n return isRpcOutGoing(req) && Object.prototype.hasOwnProperty.call(req, 'id');\n};\n\nexport const isRpcResponseResult = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCResponseResult<T> => {\n return Object.prototype.hasOwnProperty.call(req, 'result');\n};\n\nexport const isRpcResponseError = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCResponseError => {\n return Object.prototype.hasOwnProperty.call(req, 'error');\n};\n\nexport const isRpcResponse = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCResponse<T> => {\n return isRpcResponseResult(req) || isRpcResponseError(req);\n};\n", "import { InvalidRequest, MethodNotFound, RPCError } from './errors';\nimport {\n ApiFunctions,\n LogLevel,\n RPCCallBack,\n RPCMessage,\n RPCMethodObject,\n RPCNotification,\n RPCRequest,\n RPCResponse,\n RPCSendRaw,\n isRpcNotification,\n isRpcResponse,\n isRpcResponseError,\n isRpcResponseResult\n} from './types';\n\n// Debugging setup\n// Generic logger that is overridden with a more specific one by setup\ntype Logger = (level: LogLevel, ...msg: unknown[]) => void;\nlet logBase: Logger = (level, ...msg) => console[level](...msg);\n\nconst logWarn = (...msg: unknown[]) => logBase('warn', ...msg);\nconst logError = (...msg: unknown[]) => logBase('error', ...msg);\n/* eslint-enable @typescript-eslint/no-unused-vars */\n\n// Methods for processing remote API calls (set up on-host)\nconst methods: RPCMethodObject<ApiFunctions> = {};\ntype MethodName = keyof typeof methods & string;\n\n// Pending API calls (used off-host)\nconst pending: Record<number, RPCCallBack<(typeof methods)[MethodName]>> = {};\nlet rpcIndex = 0;\n\nlet sendRaw: RPCSendRaw;\n\nconst sendJson = <P extends unknown[], T>(msg: RPCMessage<P, T>) => {\n try {\n sendRaw(msg);\n } catch (err) {\n logError(err);\n }\n};\n\nconst sendResult = <T>(id: number, result: T) => {\n sendJson({\n jsonrpc: '2.0',\n id,\n result\n });\n};\n\nconst sendError = (id: number, error: RPCError) => {\n sendJson({\n jsonrpc: '2.0',\n id,\n error: {\n code: error.code,\n message: error.message,\n data: error.data\n }\n });\n};\n\nconst handleRpc = <P extends unknown[], T>(json: RPCNotification<P> | RPCResponse<T>) => {\n if (!isRpcNotification(json)) {\n if (isRpcResponse(json)) {\n const callback = pending[json.id];\n if (!callback) {\n sendError(json.id, new InvalidRequest(`Missing callback for ${json.id}`));\n return;\n }\n if (callback.timeout) {\n clearTimeout(callback.timeout);\n }\n delete pending[json.id];\n callback(isRpcResponseResult(json) ? json.result : undefined, isRpcResponseError(json) ? json.error : undefined);\n } else {\n handleRequest(json);\n }\n } else {\n handleNotification(json);\n }\n};\n\nexport const handleRaw = <P extends unknown[], T>(data: RPCNotification<P> | RPCResponse<T>) => {\n try {\n if (!data) {\n return;\n }\n handleRpc(data);\n } catch (err) {\n logError('handleRaw error', err, data);\n }\n};\n\nconst onRequest = <K extends MethodName>(method: K, params: Parameters<(typeof methods)[K]> | undefined) => {\n if (!methods[method]) {\n logError(`onRequest: Method ${method} not found in methods: ${Object.keys(methods)}`);\n throw new MethodNotFound(method);\n }\n if (params === undefined) {\n return methods[method]();\n }\n return methods[method](...params);\n};\n\nconst handleNotification = <P extends unknown[]>(json: RPCNotification<P>) => {\n if (!json.method) {\n logWarn(`handleNotification: no method specified in message ${json}`);\n return;\n }\n\n onRequest(json.method, json.params);\n};\n\nconst handleRequest = async <P extends unknown[]>(json: RPCRequest<P>) => {\n if (!json.method) {\n logWarn(`handleRequest: no method specified in message ${json}`);\n sendError(json.id, new InvalidRequest('No method specified in message'));\n return;\n }\n\n try {\n const result = await onRequest(json.method, json.params);\n if (result !== undefined && result instanceof Promise) {\n try {\n await result;\n sendResult(json.id, result);\n } catch (e) {\n sendError(json.id, e as RPCError);\n }\n } else {\n sendResult(json.id, result);\n }\n } catch (err) {\n sendError(json.id, err as RPCError);\n }\n};\n\n/**\n *\n * @param _methods Remote API call handlers\n * @param _sendRaw Function for sending the raw JSON, e.g. `parent.postMessage`\n * @param _logBase Logger function for context-aware logging\n */\nexport const setup = <T extends RPCMethodObject<ApiFunctions>>(\n _methods: T,\n _sendRaw: RPCSendRaw,\n _logBase?: Logger\n) => {\n Object.assign(methods, _methods);\n sendRaw = _sendRaw;\n _logBase !== undefined && (logBase = _logBase);\n};\n\nexport const sendNotification = <P extends unknown[]>(method: string, params: P) => {\n sendJson({ jsonrpc: '2.0', method, params });\n};\n\nexport const sendRequest = <K extends MethodName, P extends unknown[]>(method: K, params: P, timeoutMs: number) => {\n return new Promise((resolve, reject) => {\n const id = rpcIndex;\n const req: RPCRequest<P> = { jsonrpc: '2.0', method, params, id };\n rpcIndex += 1;\n const callback: RPCCallBack<(typeof methods)[K]> = (result, err) => {\n if (err) {\n const jsError = new RPCError(err.code, err.message, err.data);\n reject(jsError);\n return;\n }\n resolve(result);\n };\n\n // Set the timeout\n callback.timeout = setTimeout(() => {\n delete pending[id];\n logWarn(`Request id ${id} (${method}) timed out.`);\n reject(new Error(`Request ${id} (${method}) timed out.`));\n }, timeoutMs);\n\n pending[id] = callback;\n sendJson(req);\n });\n};\n", "import { LogLevel } from './types';\n\nconst isFigma = typeof figma !== 'undefined';\nconst isUi = typeof parent !== 'undefined';\nexport const logBase = (level: LogLevel, ...msg: unknown[]) =>\n console[level](`RPC in ${isFigma ? 'logic' : isUi ? 'ui' : 'UNKNOWN'}:`, ...msg);\n\nexport const strictObjectKeys = Object.keys as <T extends Record<string, unknown>>(obj: T) => Array<keyof T>;\n", "import { handleRaw, sendRequest, setup } from './rpc';\nimport {\n ApiFunctions,\n AwaitedReturn,\n RPCAPIReturnType,\n RPCDefaultOptions,\n RPCOptions,\n RPCSendRaw,\n StrictParameters\n} from './types';\nimport { logBase, strictObjectKeys } from './utils';\n\nexport { ApiFunctions, RPCAPIReturnType, RPCOptions } from './types';\n\nconst DEFAULT_OPTIONS: RPCDefaultOptions = {\n timeoutMs: 3000,\n logicTargetOrigin: '*',\n uiTargetOrigin: '*'\n};\n\nlet sendRaw: RPCSendRaw;\n/**\n * Set up sending and receiving of messages for Figma plugin logic and UI\n */\nconst setupMessaging = (pluginId: string | undefined, logicTargetOrigin: string, uiTargetOrigin: string) => {\n if (typeof figma !== 'undefined') {\n figma.ui.on('message', (message) => handleRaw(message));\n\n sendRaw = (message) => figma.ui.postMessage(message, { origin: logicTargetOrigin });\n } else if (typeof parent !== 'undefined') {\n onmessage = (event) => handleRaw(event.data.pluginMessage);\n\n if (pluginId !== undefined) {\n sendRaw = (pluginMessage) => parent.postMessage({ pluginMessage, pluginId }, uiTargetOrigin);\n } else {\n sendRaw = (pluginMessage) => parent.postMessage({ pluginMessage }, uiTargetOrigin);\n }\n } else {\n // Should not happen but log an error just in case\n console.warn('Both parent and figma are undefined, it seems like the runtime is neither Figma nor a browser');\n }\n};\n\n/**\n * Creates one side of a JSON-RPC API for a Figma plugin\n * @param hostType A typeof string of an object that differentiates between on-host and off-host.\n * For example, if `typeof figma` is used, when `figma` is defined the creator is considered to be on-host\n * and the methods for processing the remote API calls are set up.\n * Off-host the creator sets up the equivalent remote API calls to make requests to host.\n */\nconst createAPI = <T extends ApiFunctions>(\n methods: T,\n hostType: string,\n options?: RPCOptions\n): Readonly<RPCAPIReturnType<T>> => {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n const { timeoutMs, pluginId, logicTargetOrigin, uiTargetOrigin } = opts;\n\n if (sendRaw === undefined) {\n setupMessaging(pluginId, logicTargetOrigin, uiTargetOrigin);\n }\n\n if (hostType !== 'undefined') {\n // Should not happen but log an error just in case\n if (sendRaw === undefined) {\n console.error('sendRaw is undefined at during setup, the API will not work');\n }\n\n setup(methods, sendRaw, logBase);\n }\n\n const api: RPCAPIReturnType<T> = strictObjectKeys(methods).reduce((prev, p) => {\n const method = async (...params: StrictParameters<T[keyof T]>) => {\n if (hostType !== 'undefined') {\n return (await methods[p](...params)) as AwaitedReturn<T[keyof T]>;\n }\n\n return (await sendRequest(p as string, params, timeoutMs)) as AwaitedReturn<T[keyof T]>;\n };\n\n prev[p] = method;\n return prev;\n }, {} as RPCAPIReturnType<T>);\n\n return api;\n};\n\n/**\n * Create a set of methods that can be called from plugin logic and are executed in plugin UI\n * This side has access to the browser API\n */\nexport const createUIAPI = <T extends ApiFunctions>(\n methods: T,\n options?: RPCOptions\n): Readonly<RPCAPIReturnType<T>> => {\n return createAPI(methods, typeof parent, options);\n};\n\n/**\n * Create a set of methods that can be called from plugin UI and are executed in plugin logic\n * This side has acccess to the `figma` API\n */\nexport const createPluginAPI = <T extends ApiFunctions>(\n methods: T,\n options?: RPCOptions\n): Readonly<RPCAPIReturnType<T>> => {\n return createAPI(methods, typeof figma, options);\n};\n", "import { ArrayType } from './typePrimitives';\n\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n", "/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n", "import { FIGMA_MIXED } from './constants';\nimport { nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { ResolvedNode, ResolverOptions, SceneNodePropertyKey, SerializedResolvedNode } from './types';\n\nconst defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {\n return (\n // Can only get component property definitions of a component set or non-variant component\n !(key === 'componentPropertyDefinitions' && node.parent?.type === 'COMPONENT_SET') &&\n // reading horizontalPadding and verticalPadding is no longer supported as left and right padding may differ\n key !== 'horizontalPadding' &&\n key !== 'verticalPadding'\n );\n};\n\nconst resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(\n node: Node,\n propertyKeys?: Keys\n): ResolvedNode<Node, Keys> => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {\n [P in keyof Node]: TypedPropertyDescriptor<Node[P]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');\n\n // type has to be included manually as it doesn't have a getter but isn't a static property either\n getters.push('type');\n\n if (propertyKeys) {\n getters = getters.filter((key) => propertyKeys.includes(key as SceneNodePropertyKey));\n }\n\n getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));\n\n const objectWithProperties = {\n id: node.id\n } as Node;\n\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n object: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, addAncestorsVisibleProperty } = options;\n\n const resolvedNode = resolveNodeProperties(\n object,\n resolveProperties === 'all' ? undefined : resolveProperties\n ) as Record<string, unknown>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n if (resolvedNode[key] === figma.mixed) {\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\n// TODO: Resolve variables\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n nodes.forEach((node) => {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n });\n } else {\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n });\n }\n\n return result;\n};\n", "import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nexport const uiApi = createUIAPI({\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n});\n\nconst selectionChangeHandler = () => {\n console.log('Selection change handler', figma.currentPage.selection);\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n console.log('Filtered selection:', resolvedSelection);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n });\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nexport const api = createPluginAPI({\n _registerForSelectionChange(opts: FigmaSelectionHookOptions) {\n console.log('Register');\n options = opts;\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n});\n\nlet options: FigmaSelectionHookOptions;\n\nexport let listeners: FigmaSelectionListener[] = [];\n\nexport const setlisteners = (newListeners: FigmaSelectionListener[]) => {\n listeners = newListeners;\n};\n\n// In plugin UI, add a global function to receive selection change events\nif (typeof window !== 'undefined') {\n window._figma_onSelectionChange = (selection) => {\n listeners.forEach((l) => {\n l(selection);\n });\n };\n}\n"],
"mappings": "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",
"names": ["useEffect", "logBase", "sendRaw", "methods", "options", "__spreadValues", "api", "strictObjectKeys", "strictObjectKeys", "options", "useEffect"]
"sources": ["../src/hook.ts", "../src/useMountedEffect.ts", "../src/index.ts", "../src/typeUtils.ts", "../src/constants.ts", "../src/utils.ts", "../src/types.ts"],
"sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\n\nimport { useEffect, useState } from 'react';\n\nimport useMountedEffect from './useMountedEffect';\n\nimport { api, listeners, setlisteners, updateApiWithOptions, updateUiApiWithOptions } from '.';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FigmaSelectionHookOptions } from './types';\nexport { FIGMA_MIXED } from './constants';\n\nconst defaultOptions = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: false,\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false\n} as const satisfies ResolverOptions;\n\n/**\n * Only one config will take presence and it will be the config of the first hook that is mounted\n */\nconst useFigmaSelection = <const Options extends FigmaSelectionHookOptions>(\n hookOptions?: Options\n): [readonly SerializedResolvedNode<Options>[], (selection: readonly BareNode[]) => void] => {\n const opts = { ...defaultOptions, ...hookOptions } as const;\n\n const [selection, setSelection] = useState<readonly SerializedResolvedNode<Options>[]>([]);\n\n useMountedEffect(() => {\n console.warn('useFigmaSelection: changing options once mounted will not affect the behavior of the hook');\n }, [hookOptions]);\n\n useEffect(() => {\n const mount = async () => {\n // Typing the listeners explicitly is difficult due to the architecture, so we have to assert\n listeners.push(setSelection as unknown as FigmaSelectionListener);\n\n // if it's the first listener, register for selection change\n if (listeners.length === 1) {\n try {\n if (opts.apiOptions) {\n updateApiWithOptions(opts.apiOptions);\n updateUiApiWithOptions(opts.apiOptions);\n }\n await api._registerForSelectionChange(opts);\n } catch (e) {\n console.error(e);\n }\n }\n };\n\n mount();\n\n return () => {\n setlisteners(listeners.filter((l) => l !== (setSelection as unknown as FigmaSelectionListener)));\n if (!listeners.length) {\n // if it was the last listener, then we don't have to listen to selection change anymore\n api._deregisterForSelectionChange();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [selection as readonly SerializedResolvedNode<Options>[], api._setSelection];\n};\n\nexport default useFigmaSelection;\n", "import { useEffect, useRef } from 'react';\n\nconst useMountedEffect = (effect: React.EffectCallback, deps?: React.DependencyList | undefined) => {\n const didMountRef = useRef(false);\n\n useEffect(() => {\n if (didMountRef.current) {\n return effect();\n }\n didMountRef.current = true;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n};\n\nexport default useMountedEffect;\n", "import { createUIAPI, createPluginAPI, RPCOptions } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n });\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: FigmaSelectionHookOptions;\n\nexport let listeners: FigmaSelectionListener[] = [];\n\nexport const setlisteners = (newListeners: FigmaSelectionListener[]) => {\n listeners = newListeners;\n};\n\n// In plugin UI, add a global function to receive selection change events\nif (typeof window !== 'undefined') {\n window._figma_onSelectionChange = (selection) => {\n listeners.forEach((l) => {\n l(selection);\n });\n };\n}\n", "import { ArrayType } from './typePrimitives';\n\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n", "/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n", "import { FIGMA_MIXED } from './constants';\nimport { nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { ResolvedNode, ResolverOptions, SceneNodePropertyKey, SerializedResolvedNode } from './types';\n\nconst defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {\n return (\n // Can only get component property definitions of a component set or non-variant component\n !(key === 'componentPropertyDefinitions' && node.parent?.type === 'COMPONENT_SET') &&\n // reading horizontalPadding and verticalPadding is no longer supported as left and right padding may differ\n key !== 'horizontalPadding' &&\n key !== 'verticalPadding'\n );\n};\n\nconst resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(\n node: Node,\n propertyKeys?: Keys\n): ResolvedNode<Node, Keys> => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {\n [P in keyof Node]: TypedPropertyDescriptor<Node[P]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');\n\n // type has to be included manually as it doesn't have a getter but isn't a static property either\n getters.push('type');\n\n if (propertyKeys) {\n getters = getters.filter((key) => propertyKeys.includes(key as SceneNodePropertyKey));\n }\n\n getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));\n\n const objectWithProperties = {\n id: node.id\n } as Node;\n\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n object: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, addAncestorsVisibleProperty } = options;\n\n const resolvedNode = resolveNodeProperties(\n object,\n resolveProperties === 'all' ? undefined : resolveProperties\n ) as Record<string, unknown>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n if (resolvedNode[key] === figma.mixed) {\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\n// TODO: Resolve variables\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n nodes.forEach((node) => {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n });\n } else {\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n });\n }\n\n return result;\n};\n", "import { RPCOptions } from 'figma-plugin-api';\n\nimport { FIGMA_MIXED } from './constants';\nimport { ApplicableNonFunctionPropertyKeys, ArrayElementUnion, NonFunctionPropertyKeys } from './typePrimitives';\n\n// For typedoc\nexport { RPCOptions } from 'figma-plugin-api';\nexport { FIGMA_MIXED } from './constants';\n\n/**\n * @internal\n * Describes the properties of an unresolved node\n */\nexport type BareNode = Pick<SceneNode, 'id'>;\n\ntype SceneNodeType = SceneNode['type'];\n\n/**\n * @internal\n */\nexport type SceneNodeFromTypes<T extends readonly SceneNodeType[] | undefined = undefined> = T extends undefined\n ? SceneNode\n : // @ts-expect-error If the check is flipped, the type widens to SceneNode. On simpler types this doesn't happen but it seems like Extract does this.\n ExtractedSceneNode<ArrayElementUnion<T>>;\n\n/**\n * @internal\n * Utility type to get only matching node types from the SceneNode union type.\n */\ntype ExtractedSceneNode<T extends SceneNodeType> = Extract<SceneNode, { type: T }>;\n\n/**\n * @internal\n * Get the non-function property keys for a SceneNode\n */\nexport type SceneNodePropertyKey<T extends SceneNodeType | undefined = undefined> = NonFunctionPropertyKeys<\n T extends SceneNodeType ? ExtractedSceneNode<T> : SceneNode\n>;\n\nexport type OptSceneNodeProperties = readonly SceneNodePropertyKey[] | 'all';\n\n/**\n * Use `satisfies` (for TS >= 4.9) with this type to allow for type checking the options object\n * while the type of the object remains exact.\n *\n * This allows us to infer the type of the returned nodes correctly.\n *\n * Example:\n * ```typescript\n * const options = {\n * nodeTypes: ['TEXT', 'FRAME'],\n * resolveProperties: ['name', 'characters', 'children]\n * } satisfies FigmaSelectionHookOptions;\n * ```\n */\nexport type FigmaSelectionHookOptions = {\n /**\n * Only return specific types of nodes.\n *\n * If left undefined, all nodes in the selection will be returned.\n *\n * Default: `undefined`\n */\n nodeTypes?: readonly SceneNodeType[] | undefined;\n /**\n * Figma node properties are lazy-loaded, so to use any property you have to resolve it first.\n *\n * Resolving all node properties causes a performance hit, so you can specify which properties you want to resolve.\n *\n * If set to `[]`, no properties will be resolved and you will only get the ids of the nodes.\n *\n * Node methods (such as `getPluginData`) will never be resolved.\n *\n * Default: `'all'`\n */\n resolveProperties?: OptSceneNodeProperties;\n /**\n * Resolve children nodes of the selection.\n *\n * If used with `nodeTypes`, all nodes of the specified types will be returned as a flat array.\n *\n * Default: `false`\n */\n resolveChildren?: boolean;\n /**\n * Resolve bound variables of the selection.\n *\n * Default: `false`\n */\n resolveVariables?: boolean;\n /**\n * Add `ancestorsVisible` property to all nodes.\n *\n * This property is true if all ancestors of the node are visible.\n *\n * Default: `false`\n */\n addAncestorsVisibleProperty?: boolean;\n /**\n * Options for figma-plugin-api\n *\n * Default: see the RPCOptions type\n */\n apiOptions?: RPCOptions | undefined;\n};\n\n/**\n * @internal\n */\nexport type ResolverOptions = Omit<FigmaSelectionHookOptions, 'apiOptions'>;\n\ntype SerializedNodeProperty<\n Prop,\n Node extends SceneNode,\n Options extends ResolverOptions\n> = Prop extends PluginAPI['mixed']\n ? typeof FIGMA_MIXED\n : Prop extends readonly SceneNode[]\n ? Options['resolveChildren'] extends true\n ? readonly SerializedNode<Node, Options>[]\n : readonly BareNode[]\n : Prop;\n\ntype ApplicableNodeKeys<\n Node extends SceneNode,\n Properties extends OptSceneNodeProperties\n> = Properties extends readonly SceneNodePropertyKey[]\n ? ApplicableNonFunctionPropertyKeys<Node, ArrayElementUnion<Properties>>\n : NonFunctionPropertyKeys<Node>;\n\n/**\n * @internal\n */\nexport type SerializedNode<Node extends SceneNode, Options extends ResolverOptions> = Node extends SceneNode\n ? {\n type: Node['type'];\n id: Node['id'];\n } & {\n [Key in Options['resolveProperties'] extends SceneNodePropertyKey[]\n ? ApplicableNodeKeys<Node, Options['resolveProperties']>\n : keyof Node]: SerializedNodeProperty<Node[Key], Node, Options>;\n }\n : never;\n\n/**\n * @internal\n */\nexport type ResolvedNode<\n Node extends SceneNode,\n Keys extends readonly SceneNodePropertyKey[] | undefined = undefined\n> = Pick<\n Node,\n Keys extends readonly SceneNodePropertyKey[]\n ? ApplicableNonFunctionPropertyKeys<Node, ArrayElementUnion<Keys>> | 'id' | 'type'\n : ApplicableNonFunctionPropertyKeys<Node, SceneNodePropertyKey>\n>;\n\n/**\n * @internal\n */\ntype SerializedResolvedNodeBase<Node extends SceneNode, Options extends ResolverOptions> = Node extends SceneNode\n ? SerializedNode<Node, Options>\n : never;\n\n/**\n * @internal\n */\ntype AncestorsVisibleMixin = {\n ancestorsVisible: boolean;\n};\n\n/**\n * @internal\n */\ntype ResolveVariablesMixin = {\n boundVariableInstances: readonly Variable[];\n};\n\n/**\n * @internal\n * All Figma nodes are converted to this type for serialization and sending to the plugin UI\n */\nexport type SerializedResolvedNode<Options extends ResolverOptions> =\n Options['addAncestorsVisibleProperty'] extends true\n ? Options['resolveVariables'] extends true\n ? SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options> &\n AncestorsVisibleMixin &\n ResolveVariablesMixin\n : SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options> & AncestorsVisibleMixin\n : SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options>;\n\n/**\n * @internal\n */\nexport type FigmaSelectionListener = (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n"],
"mappings": "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",
"names": ["useEffect", "options", "RPCOptions", "useEffect"]
}

@@ -44,250 +44,12 @@ "use strict";

setlisteners: () => setlisteners,
uiApi: () => uiApi
uiApi: () => uiApi,
updateApiWithOptions: () => updateApiWithOptions,
updateUiApiWithOptions: () => updateUiApiWithOptions
});
module.exports = __toCommonJS(src_exports);
var import_figma_plugin_api = require("figma-plugin-api");
// node_modules/figma-plugin-api/lib/index.mjs
var __defProp2 = Object.defineProperty;
var __getOwnPropSymbols2 = Object.getOwnPropertySymbols;
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
var __propIsEnum2 = Object.prototype.propertyIsEnumerable;
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues2 = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp2.call(b, prop))
__defNormalProp2(a, prop, b[prop]);
if (__getOwnPropSymbols2)
for (var prop of __getOwnPropSymbols2(b)) {
if (__propIsEnum2.call(b, prop))
__defNormalProp2(a, prop, b[prop]);
}
return a;
};
var __publicField = (obj, key, value) => {
__defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
var RPCError = class extends Error {
constructor(code, message, data) {
super();
__publicField(this, "code");
__publicField(this, "message");
__publicField(this, "data");
this.code = code;
this.message = message;
this.data = data;
}
};
var InvalidRequest = class extends RPCError {
constructor(data) {
super(-32600, "Invalid request", data);
}
};
var MethodNotFound = class extends RPCError {
constructor(data) {
super(-32601, "Method not found", data);
}
};
var isRpcOutGoing = (req) => {
return Object.prototype.hasOwnProperty.call(req, "method");
};
var isRpcNotification = (req) => {
return isRpcOutGoing(req) && !Object.prototype.hasOwnProperty.call(req, "id");
};
var isRpcResponseResult = (req) => {
return Object.prototype.hasOwnProperty.call(req, "result");
};
var isRpcResponseError = (req) => {
return Object.prototype.hasOwnProperty.call(req, "error");
};
var isRpcResponse = (req) => {
return isRpcResponseResult(req) || isRpcResponseError(req);
};
var logBase = (level, ...msg) => console[level](...msg);
var logWarn = (...msg) => logBase("warn", ...msg);
var logError = (...msg) => logBase("error", ...msg);
var methods = {};
var pending = {};
var rpcIndex = 0;
var sendRaw;
var sendJson = (msg) => {
try {
sendRaw(msg);
} catch (err) {
logError(err);
}
};
var sendResult = (id, result) => {
sendJson({
jsonrpc: "2.0",
id,
result
});
};
var sendError = (id, error) => {
sendJson({
jsonrpc: "2.0",
id,
error: {
code: error.code,
message: error.message,
data: error.data
}
});
};
var handleRpc = (json) => {
if (!isRpcNotification(json)) {
if (isRpcResponse(json)) {
const callback = pending[json.id];
if (!callback) {
sendError(json.id, new InvalidRequest(`Missing callback for ${json.id}`));
return;
}
if (callback.timeout) {
clearTimeout(callback.timeout);
}
delete pending[json.id];
callback(isRpcResponseResult(json) ? json.result : void 0, isRpcResponseError(json) ? json.error : void 0);
} else {
handleRequest(json);
}
} else {
handleNotification(json);
}
};
var handleRaw = (data) => {
try {
if (!data) {
return;
}
handleRpc(data);
} catch (err) {
logError("handleRaw error", err, data);
}
};
var onRequest = (method, params) => {
if (!methods[method]) {
logError(`onRequest: Method ${method} not found in methods: ${Object.keys(methods)}`);
throw new MethodNotFound(method);
}
if (params === void 0) {
return methods[method]();
}
return methods[method](...params);
};
var handleNotification = (json) => {
if (!json.method) {
logWarn(`handleNotification: no method specified in message ${json}`);
return;
}
onRequest(json.method, json.params);
};
var handleRequest = async (json) => {
if (!json.method) {
logWarn(`handleRequest: no method specified in message ${json}`);
sendError(json.id, new InvalidRequest("No method specified in message"));
return;
}
try {
const result = await onRequest(json.method, json.params);
if (result !== void 0 && result instanceof Promise) {
try {
await result;
sendResult(json.id, result);
} catch (e) {
sendError(json.id, e);
}
} else {
sendResult(json.id, result);
}
} catch (err) {
sendError(json.id, err);
}
};
var setup = (_methods, _sendRaw, _logBase) => {
Object.assign(methods, _methods);
sendRaw = _sendRaw;
_logBase !== void 0 && (logBase = _logBase);
};
var sendRequest = (method, params, timeoutMs) => {
return new Promise((resolve, reject) => {
const id = rpcIndex;
const req = { jsonrpc: "2.0", method, params, id };
rpcIndex += 1;
const callback = (result, err) => {
if (err) {
const jsError = new RPCError(err.code, err.message, err.data);
reject(jsError);
return;
}
resolve(result);
};
callback.timeout = setTimeout(() => {
delete pending[id];
logWarn(`Request id ${id} (${method}) timed out.`);
reject(new Error(`Request ${id} (${method}) timed out.`));
}, timeoutMs);
pending[id] = callback;
sendJson(req);
});
};
var isFigma = typeof figma !== "undefined";
var isUi = typeof parent !== "undefined";
var logBase2 = (level, ...msg) => console[level](`RPC in ${isFigma ? "logic" : isUi ? "ui" : "UNKNOWN"}:`, ...msg);
var strictObjectKeys = Object.keys;
var DEFAULT_OPTIONS = {
timeoutMs: 3e3,
logicTargetOrigin: "*",
uiTargetOrigin: "*"
};
var sendRaw2;
var setupMessaging = (pluginId, logicTargetOrigin, uiTargetOrigin) => {
if (typeof figma !== "undefined") {
figma.ui.on("message", (message) => handleRaw(message));
sendRaw2 = (message) => figma.ui.postMessage(message, { origin: logicTargetOrigin });
} else if (typeof parent !== "undefined") {
onmessage = (event) => handleRaw(event.data.pluginMessage);
if (pluginId !== void 0) {
sendRaw2 = (pluginMessage) => parent.postMessage({ pluginMessage, pluginId }, uiTargetOrigin);
} else {
sendRaw2 = (pluginMessage) => parent.postMessage({ pluginMessage }, uiTargetOrigin);
}
} else {
console.warn("Both parent and figma are undefined, it seems like the runtime is neither Figma nor a browser");
}
};
var createAPI = (methods2, hostType, options2) => {
const opts = __spreadValues2(__spreadValues2({}, DEFAULT_OPTIONS), options2);
const { timeoutMs, pluginId, logicTargetOrigin, uiTargetOrigin } = opts;
if (sendRaw2 === void 0) {
setupMessaging(pluginId, logicTargetOrigin, uiTargetOrigin);
}
if (hostType !== "undefined") {
if (sendRaw2 === void 0) {
console.error("sendRaw is undefined at during setup, the API will not work");
}
setup(methods2, sendRaw2, logBase2);
}
const api2 = strictObjectKeys(methods2).reduce((prev, p) => {
const method = async (...params) => {
if (hostType !== "undefined") {
return await methods2[p](...params);
}
return await sendRequest(p, params, timeoutMs);
};
prev[p] = method;
return prev;
}, {});
return api2;
};
var createUIAPI = (methods2, options2) => {
return createAPI(methods2, typeof parent, options2);
};
var createPluginAPI = (methods2, options2) => {
return createAPI(methods2, typeof figma, options2);
};
// src/typeUtils.ts
var isArray = Array.isArray;
var strictObjectKeys2 = Object.keys;
var strictObjectKeys = Object.keys;
var nodeCanHaveChildren = (node) => {

@@ -311,3 +73,3 @@ return "children" in node;

const descriptors = Object.getOwnPropertyDescriptors(Object.getPrototypeOf(node));
const getterKeys = strictObjectKeys2(descriptors);
const getterKeys = strictObjectKeys(descriptors);
let getters = getterKeys.filter((key) => typeof descriptors[key].get === "function");

@@ -333,3 +95,3 @@ getters.push("type");

);
for (const key of strictObjectKeys2(resolvedNode)) {
for (const key of strictObjectKeys(resolvedNode)) {
if (resolvedNode[key] === figma.mixed) {

@@ -371,3 +133,3 @@ resolvedNode[key] = FIGMA_MIXED;

// src/index.ts
var uiApi = createUIAPI({
var uiApiMethods = {
_onSelectionChange(selection) {

@@ -378,7 +140,9 @@ if (typeof window._figma_onSelectionChange !== "undefined") {

}
});
};
var uiApi = (0, import_figma_plugin_api.createUIAPI)(uiApiMethods);
var updateUiApiWithOptions = (rpcOptions) => {
uiApi = (0, import_figma_plugin_api.createUIAPI)(uiApiMethods, rpcOptions);
};
var selectionChangeHandler = () => {
console.log("Selection change handler", figma.currentPage.selection);
const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);
console.log("Filtered selection:", resolvedSelection);
uiApi._onSelectionChange(resolvedSelection);

@@ -412,6 +176,10 @@ };

};
var api = createPluginAPI({
var apiMethods = {
_registerForSelectionChange(opts) {
console.log("Register");
options = opts;
const apiOptions = opts.apiOptions;
if (apiOptions) {
updateUiApiWithOptions(apiOptions);
updateApiWithOptions(apiOptions);
}
figma.on("selectionchange", selectionChangeHandler);

@@ -428,3 +196,7 @@ figma.on("documentchange", documentChangeHandler);

}
});
};
var api = (0, import_figma_plugin_api.createPluginAPI)(apiMethods);
var updateApiWithOptions = (rpcOptions) => {
api = (0, import_figma_plugin_api.createPluginAPI)(apiMethods, rpcOptions);
};
var options;

@@ -431,0 +203,0 @@ var listeners = [];

{
"version": 3,
"sources": ["../src/index.ts", "../node_modules/figma-plugin-api/src/errors.ts", "../node_modules/figma-plugin-api/src/types.ts", "../node_modules/figma-plugin-api/src/rpc.ts", "../node_modules/figma-plugin-api/src/utils.ts", "../node_modules/figma-plugin-api/src/index.ts", "../src/typeUtils.ts", "../src/constants.ts", "../src/utils.ts"],
"sourcesContent": ["import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nexport const uiApi = createUIAPI({\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n});\n\nconst selectionChangeHandler = () => {\n console.log('Selection change handler', figma.currentPage.selection);\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n console.log('Filtered selection:', resolvedSelection);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n });\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nexport const api = createPluginAPI({\n _registerForSelectionChange(opts: FigmaSelectionHookOptions) {\n console.log('Register');\n options = opts;\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n});\n\nlet options: FigmaSelectionHookOptions;\n\nexport let listeners: FigmaSelectionListener[] = [];\n\nexport const setlisteners = (newListeners: FigmaSelectionListener[]) => {\n listeners = newListeners;\n};\n\n// In plugin UI, add a global function to receive selection change events\nif (typeof window !== 'undefined') {\n window._figma_onSelectionChange = (selection) => {\n listeners.forEach((l) => {\n l(selection);\n });\n };\n}\n", "export class RPCError extends Error {\n code: number;\n message: string;\n data: unknown;\n\n constructor(code: number, message: string, data?: unknown) {\n super();\n this.code = code;\n this.message = message;\n this.data = data;\n }\n}\n\n/**\n * Invalid JSON was received by the server.\n * An error occurred on the server while parsing the JSON text.\n */\nexport class ParseError extends RPCError {\n constructor(data?: unknown) {\n super(-32700, 'Parse error', data);\n }\n}\n\n/**\n * The JSON sent is not a valid Request object.\n */\nexport class InvalidRequest extends RPCError {\n constructor(data?: unknown) {\n super(-32600, 'Invalid request', data);\n }\n}\n\n/**\n * The method does not exist / is not available.\n */\nexport class MethodNotFound extends RPCError {\n constructor(data?: unknown) {\n super(-32601, 'Method not found', data);\n }\n}\n\n/**\n * Invalid method parameter(s).\n */\nexport class InvalidParams extends RPCError {\n constructor(data?: unknown) {\n super(-32602, 'Invalid params', data);\n }\n}\n\n/**\n * Internal JSON-RPC error.\n */\nexport class InternalError extends RPCError {\n constructor(data?: unknown) {\n super(-32603, 'Internal error', data);\n }\n}\n", "/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport type LogLevel = 'log' | 'warn' | 'error';\n\nexport type AwaitedReturn<T extends (...params: any) => any> = ReturnType<T> extends Promise<any>\n ? Awaited<ReturnType<T>>\n : ReturnType<T>;\n\nexport type StrictParameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;\n\nexport type ApiFunctions = {\n [x: string]: (...params: any[]) => any;\n};\n\nexport type RPCMethodObject<T extends ApiFunctions> = {\n [K in keyof T]: (...args: StrictParameters<T[K]>) => Promise<AwaitedReturn<T[K]>>;\n};\n\nexport type RPCAPIReturnType<T extends RPCMethodObject<ApiFunctions>> = {\n [K in keyof T]: (...args: StrictParameters<T[K]>) => Promise<AwaitedReturn<T[K]>>;\n};\n\nexport type RPCErrorObject = {\n code: number;\n message: string;\n data: unknown;\n};\n\nexport interface RPCNotification<P extends unknown[]> {\n jsonrpc: '2.0';\n method: string;\n params?: P;\n}\n\nexport interface RPCRequest<P extends unknown[]> extends RPCNotification<P> {\n jsonrpc: '2.0';\n method: string;\n params?: P;\n id: number;\n}\n\nexport interface RPCResponseBase {\n jsonrpc: '2.0';\n id: number;\n}\n\nexport interface RPCResponseResult<T> extends RPCResponseBase {\n result: T;\n}\n\nexport interface RPCResponseError extends RPCResponseBase {\n error: RPCErrorObject;\n}\n\nexport type RPCResponse<T> = RPCResponseResult<T> | RPCResponseError;\n\nexport type RPCMessage<P extends unknown[], T> = RPCRequest<P> | RPCNotification<P> | RPCResponse<T>;\n\nexport type RPCCallBack<T extends (...args: any[]) => any> = {\n (result: AwaitedReturn<T> | undefined, error: RPCErrorObject | undefined): AwaitedReturn<T>;\n timeout: number;\n};\n\nexport type RPCOptions = {\n /**\n * Timeout in milliseconds\n *\n * Default: `3000`\n */\n timeoutMs?: number;\n /**\n * If your plugin UI is hosted (non-null origin), pluginId must be defined to allow messages to be sent\n */\n pluginId?: string;\n /**\n * Specifies what the origin of the plugin UI must be for a message to be dispatched from plugin logic to UI\n *\n * If defined, add `http://localhost:<port>` to this field in your local environment to allow messaging while running on a dev server\n *\n * Default: `'*'`\n */\n logicTargetOrigin?: string;\n /**\n * Specifies what the origin of the plugin logic must be for a message to be dispatched from UI to plugin logic\n *\n * Usually `'https://www.figma.com'`\n *\n * Default: `'*'`\n */\n uiTargetOrigin?: string;\n};\n\nexport type RPCDefaultOptions = Required<Omit<RPCOptions, 'pluginId'>>;\n\nexport type RPCSendRaw = <P extends unknown[], T>(message: RPCMessage<P, T>) => void;\n\nexport const isRpcOutGoing = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCNotification<P> | RPCRequest<P> => {\n return Object.prototype.hasOwnProperty.call(req, 'method');\n};\n\nexport const isRpcNotification = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCNotification<P> => {\n return isRpcOutGoing(req) && !Object.prototype.hasOwnProperty.call(req, 'id');\n};\n\nexport const isRpcRequest = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCRequest<P> => {\n return isRpcOutGoing(req) && Object.prototype.hasOwnProperty.call(req, 'id');\n};\n\nexport const isRpcResponseResult = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCResponseResult<T> => {\n return Object.prototype.hasOwnProperty.call(req, 'result');\n};\n\nexport const isRpcResponseError = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCResponseError => {\n return Object.prototype.hasOwnProperty.call(req, 'error');\n};\n\nexport const isRpcResponse = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCResponse<T> => {\n return isRpcResponseResult(req) || isRpcResponseError(req);\n};\n", "import { InvalidRequest, MethodNotFound, RPCError } from './errors';\nimport {\n ApiFunctions,\n LogLevel,\n RPCCallBack,\n RPCMessage,\n RPCMethodObject,\n RPCNotification,\n RPCRequest,\n RPCResponse,\n RPCSendRaw,\n isRpcNotification,\n isRpcResponse,\n isRpcResponseError,\n isRpcResponseResult\n} from './types';\n\n// Debugging setup\n// Generic logger that is overridden with a more specific one by setup\ntype Logger = (level: LogLevel, ...msg: unknown[]) => void;\nlet logBase: Logger = (level, ...msg) => console[level](...msg);\n\nconst logWarn = (...msg: unknown[]) => logBase('warn', ...msg);\nconst logError = (...msg: unknown[]) => logBase('error', ...msg);\n/* eslint-enable @typescript-eslint/no-unused-vars */\n\n// Methods for processing remote API calls (set up on-host)\nconst methods: RPCMethodObject<ApiFunctions> = {};\ntype MethodName = keyof typeof methods & string;\n\n// Pending API calls (used off-host)\nconst pending: Record<number, RPCCallBack<(typeof methods)[MethodName]>> = {};\nlet rpcIndex = 0;\n\nlet sendRaw: RPCSendRaw;\n\nconst sendJson = <P extends unknown[], T>(msg: RPCMessage<P, T>) => {\n try {\n sendRaw(msg);\n } catch (err) {\n logError(err);\n }\n};\n\nconst sendResult = <T>(id: number, result: T) => {\n sendJson({\n jsonrpc: '2.0',\n id,\n result\n });\n};\n\nconst sendError = (id: number, error: RPCError) => {\n sendJson({\n jsonrpc: '2.0',\n id,\n error: {\n code: error.code,\n message: error.message,\n data: error.data\n }\n });\n};\n\nconst handleRpc = <P extends unknown[], T>(json: RPCNotification<P> | RPCResponse<T>) => {\n if (!isRpcNotification(json)) {\n if (isRpcResponse(json)) {\n const callback = pending[json.id];\n if (!callback) {\n sendError(json.id, new InvalidRequest(`Missing callback for ${json.id}`));\n return;\n }\n if (callback.timeout) {\n clearTimeout(callback.timeout);\n }\n delete pending[json.id];\n callback(isRpcResponseResult(json) ? json.result : undefined, isRpcResponseError(json) ? json.error : undefined);\n } else {\n handleRequest(json);\n }\n } else {\n handleNotification(json);\n }\n};\n\nexport const handleRaw = <P extends unknown[], T>(data: RPCNotification<P> | RPCResponse<T>) => {\n try {\n if (!data) {\n return;\n }\n handleRpc(data);\n } catch (err) {\n logError('handleRaw error', err, data);\n }\n};\n\nconst onRequest = <K extends MethodName>(method: K, params: Parameters<(typeof methods)[K]> | undefined) => {\n if (!methods[method]) {\n logError(`onRequest: Method ${method} not found in methods: ${Object.keys(methods)}`);\n throw new MethodNotFound(method);\n }\n if (params === undefined) {\n return methods[method]();\n }\n return methods[method](...params);\n};\n\nconst handleNotification = <P extends unknown[]>(json: RPCNotification<P>) => {\n if (!json.method) {\n logWarn(`handleNotification: no method specified in message ${json}`);\n return;\n }\n\n onRequest(json.method, json.params);\n};\n\nconst handleRequest = async <P extends unknown[]>(json: RPCRequest<P>) => {\n if (!json.method) {\n logWarn(`handleRequest: no method specified in message ${json}`);\n sendError(json.id, new InvalidRequest('No method specified in message'));\n return;\n }\n\n try {\n const result = await onRequest(json.method, json.params);\n if (result !== undefined && result instanceof Promise) {\n try {\n await result;\n sendResult(json.id, result);\n } catch (e) {\n sendError(json.id, e as RPCError);\n }\n } else {\n sendResult(json.id, result);\n }\n } catch (err) {\n sendError(json.id, err as RPCError);\n }\n};\n\n/**\n *\n * @param _methods Remote API call handlers\n * @param _sendRaw Function for sending the raw JSON, e.g. `parent.postMessage`\n * @param _logBase Logger function for context-aware logging\n */\nexport const setup = <T extends RPCMethodObject<ApiFunctions>>(\n _methods: T,\n _sendRaw: RPCSendRaw,\n _logBase?: Logger\n) => {\n Object.assign(methods, _methods);\n sendRaw = _sendRaw;\n _logBase !== undefined && (logBase = _logBase);\n};\n\nexport const sendNotification = <P extends unknown[]>(method: string, params: P) => {\n sendJson({ jsonrpc: '2.0', method, params });\n};\n\nexport const sendRequest = <K extends MethodName, P extends unknown[]>(method: K, params: P, timeoutMs: number) => {\n return new Promise((resolve, reject) => {\n const id = rpcIndex;\n const req: RPCRequest<P> = { jsonrpc: '2.0', method, params, id };\n rpcIndex += 1;\n const callback: RPCCallBack<(typeof methods)[K]> = (result, err) => {\n if (err) {\n const jsError = new RPCError(err.code, err.message, err.data);\n reject(jsError);\n return;\n }\n resolve(result);\n };\n\n // Set the timeout\n callback.timeout = setTimeout(() => {\n delete pending[id];\n logWarn(`Request id ${id} (${method}) timed out.`);\n reject(new Error(`Request ${id} (${method}) timed out.`));\n }, timeoutMs);\n\n pending[id] = callback;\n sendJson(req);\n });\n};\n", "import { LogLevel } from './types';\n\nconst isFigma = typeof figma !== 'undefined';\nconst isUi = typeof parent !== 'undefined';\nexport const logBase = (level: LogLevel, ...msg: unknown[]) =>\n console[level](`RPC in ${isFigma ? 'logic' : isUi ? 'ui' : 'UNKNOWN'}:`, ...msg);\n\nexport const strictObjectKeys = Object.keys as <T extends Record<string, unknown>>(obj: T) => Array<keyof T>;\n", "import { handleRaw, sendRequest, setup } from './rpc';\nimport {\n ApiFunctions,\n AwaitedReturn,\n RPCAPIReturnType,\n RPCDefaultOptions,\n RPCOptions,\n RPCSendRaw,\n StrictParameters\n} from './types';\nimport { logBase, strictObjectKeys } from './utils';\n\nexport { ApiFunctions, RPCAPIReturnType, RPCOptions } from './types';\n\nconst DEFAULT_OPTIONS: RPCDefaultOptions = {\n timeoutMs: 3000,\n logicTargetOrigin: '*',\n uiTargetOrigin: '*'\n};\n\nlet sendRaw: RPCSendRaw;\n/**\n * Set up sending and receiving of messages for Figma plugin logic and UI\n */\nconst setupMessaging = (pluginId: string | undefined, logicTargetOrigin: string, uiTargetOrigin: string) => {\n if (typeof figma !== 'undefined') {\n figma.ui.on('message', (message) => handleRaw(message));\n\n sendRaw = (message) => figma.ui.postMessage(message, { origin: logicTargetOrigin });\n } else if (typeof parent !== 'undefined') {\n onmessage = (event) => handleRaw(event.data.pluginMessage);\n\n if (pluginId !== undefined) {\n sendRaw = (pluginMessage) => parent.postMessage({ pluginMessage, pluginId }, uiTargetOrigin);\n } else {\n sendRaw = (pluginMessage) => parent.postMessage({ pluginMessage }, uiTargetOrigin);\n }\n } else {\n // Should not happen but log an error just in case\n console.warn('Both parent and figma are undefined, it seems like the runtime is neither Figma nor a browser');\n }\n};\n\n/**\n * Creates one side of a JSON-RPC API for a Figma plugin\n * @param hostType A typeof string of an object that differentiates between on-host and off-host.\n * For example, if `typeof figma` is used, when `figma` is defined the creator is considered to be on-host\n * and the methods for processing the remote API calls are set up.\n * Off-host the creator sets up the equivalent remote API calls to make requests to host.\n */\nconst createAPI = <T extends ApiFunctions>(\n methods: T,\n hostType: string,\n options?: RPCOptions\n): Readonly<RPCAPIReturnType<T>> => {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n const { timeoutMs, pluginId, logicTargetOrigin, uiTargetOrigin } = opts;\n\n if (sendRaw === undefined) {\n setupMessaging(pluginId, logicTargetOrigin, uiTargetOrigin);\n }\n\n if (hostType !== 'undefined') {\n // Should not happen but log an error just in case\n if (sendRaw === undefined) {\n console.error('sendRaw is undefined at during setup, the API will not work');\n }\n\n setup(methods, sendRaw, logBase);\n }\n\n const api: RPCAPIReturnType<T> = strictObjectKeys(methods).reduce((prev, p) => {\n const method = async (...params: StrictParameters<T[keyof T]>) => {\n if (hostType !== 'undefined') {\n return (await methods[p](...params)) as AwaitedReturn<T[keyof T]>;\n }\n\n return (await sendRequest(p as string, params, timeoutMs)) as AwaitedReturn<T[keyof T]>;\n };\n\n prev[p] = method;\n return prev;\n }, {} as RPCAPIReturnType<T>);\n\n return api;\n};\n\n/**\n * Create a set of methods that can be called from plugin logic and are executed in plugin UI\n * This side has access to the browser API\n */\nexport const createUIAPI = <T extends ApiFunctions>(\n methods: T,\n options?: RPCOptions\n): Readonly<RPCAPIReturnType<T>> => {\n return createAPI(methods, typeof parent, options);\n};\n\n/**\n * Create a set of methods that can be called from plugin UI and are executed in plugin logic\n * This side has acccess to the `figma` API\n */\nexport const createPluginAPI = <T extends ApiFunctions>(\n methods: T,\n options?: RPCOptions\n): Readonly<RPCAPIReturnType<T>> => {\n return createAPI(methods, typeof figma, options);\n};\n", "import { ArrayType } from './typePrimitives';\n\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n", "/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n", "import { FIGMA_MIXED } from './constants';\nimport { nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { ResolvedNode, ResolverOptions, SceneNodePropertyKey, SerializedResolvedNode } from './types';\n\nconst defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {\n return (\n // Can only get component property definitions of a component set or non-variant component\n !(key === 'componentPropertyDefinitions' && node.parent?.type === 'COMPONENT_SET') &&\n // reading horizontalPadding and verticalPadding is no longer supported as left and right padding may differ\n key !== 'horizontalPadding' &&\n key !== 'verticalPadding'\n );\n};\n\nconst resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(\n node: Node,\n propertyKeys?: Keys\n): ResolvedNode<Node, Keys> => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {\n [P in keyof Node]: TypedPropertyDescriptor<Node[P]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');\n\n // type has to be included manually as it doesn't have a getter but isn't a static property either\n getters.push('type');\n\n if (propertyKeys) {\n getters = getters.filter((key) => propertyKeys.includes(key as SceneNodePropertyKey));\n }\n\n getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));\n\n const objectWithProperties = {\n id: node.id\n } as Node;\n\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n object: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, addAncestorsVisibleProperty } = options;\n\n const resolvedNode = resolveNodeProperties(\n object,\n resolveProperties === 'all' ? undefined : resolveProperties\n ) as Record<string, unknown>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n if (resolvedNode[key] === figma.mixed) {\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\n// TODO: Resolve variables\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n nodes.forEach((node) => {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n });\n } else {\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n });\n }\n\n return result;\n};\n"],
"mappings": "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",
"names": ["logBase", "sendRaw", "methods", "options", "__spreadValues", "api", "strictObjectKeys", "strictObjectKeys", "options"]
"sources": ["../src/index.ts", "../src/typeUtils.ts", "../src/constants.ts", "../src/utils.ts"],
"sourcesContent": ["import { createUIAPI, createPluginAPI, RPCOptions } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n });\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: FigmaSelectionHookOptions;\n\nexport let listeners: FigmaSelectionListener[] = [];\n\nexport const setlisteners = (newListeners: FigmaSelectionListener[]) => {\n listeners = newListeners;\n};\n\n// In plugin UI, add a global function to receive selection change events\nif (typeof window !== 'undefined') {\n window._figma_onSelectionChange = (selection) => {\n listeners.forEach((l) => {\n l(selection);\n });\n };\n}\n", "import { ArrayType } from './typePrimitives';\n\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n", "/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n", "import { FIGMA_MIXED } from './constants';\nimport { nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { ResolvedNode, ResolverOptions, SceneNodePropertyKey, SerializedResolvedNode } from './types';\n\nconst defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {\n return (\n // Can only get component property definitions of a component set or non-variant component\n !(key === 'componentPropertyDefinitions' && node.parent?.type === 'COMPONENT_SET') &&\n // reading horizontalPadding and verticalPadding is no longer supported as left and right padding may differ\n key !== 'horizontalPadding' &&\n key !== 'verticalPadding'\n );\n};\n\nconst resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(\n node: Node,\n propertyKeys?: Keys\n): ResolvedNode<Node, Keys> => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {\n [P in keyof Node]: TypedPropertyDescriptor<Node[P]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');\n\n // type has to be included manually as it doesn't have a getter but isn't a static property either\n getters.push('type');\n\n if (propertyKeys) {\n getters = getters.filter((key) => propertyKeys.includes(key as SceneNodePropertyKey));\n }\n\n getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));\n\n const objectWithProperties = {\n id: node.id\n } as Node;\n\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n object: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, addAncestorsVisibleProperty } = options;\n\n const resolvedNode = resolveNodeProperties(\n object,\n resolveProperties === 'all' ? undefined : resolveProperties\n ) as Record<string, unknown>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n if (resolvedNode[key] === figma.mixed) {\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\n// TODO: Resolve variables\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n nodes.forEach((node) => {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n });\n } else {\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n });\n }\n\n return result;\n};\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAAyD;;;ACElD,IAAM,UAAU,MAAM;AAEtB,IAAM,mBAAmB,OAAO;AAEhC,IAAM,sBAAsB,CAAsB,SAAuC;AAC9F,SAAO,cAAc;AACvB;;;ACLO,IAAM,cAAc;;;ACE3B,IAAM,kCAAkC,CAAyB,KAAiB,SAAwB;AAL1G;AAME;AAAA;AAAA,IAEE,EAAE,QAAQ,oCAAkC,UAAK,WAAL,mBAAa,UAAS;AAAA,IAElE,QAAQ,uBACR,QAAQ;AAAA;AAEZ;AAEA,IAAM,wBAAwB,CAC5B,MACA,iBAC6B;AAE7B,QAAM,cAAc,OAAO,0BAAgC,OAAO,eAAe,IAAI,CAAC;AAMtF,QAAM,aAAa,iBAAiB,WAAW;AAC/C,MAAI,UAAU,WAAW,OAAO,CAAC,QAAQ,OAAO,YAAY,GAAG,EAAE,QAAQ,UAAU;AAGnF,UAAQ,KAAK,MAAM;AAEnB,MAAI,cAAc;AAChB,cAAU,QAAQ,OAAO,CAAC,QAAQ,aAAa,SAAS,GAA2B,CAAC;AAAA,EACtF;AAEA,YAAU,QAAQ,OAAO,CAAC,QAAQ,gCAAgC,KAAK,IAAI,CAAC;AAE5E,QAAM,uBAAuB;AAAA,IAC3B,IAAI,KAAK;AAAA,EACX;AAEA,aAAW,UAAU,SAAS;AAC5B,yBAAqB,MAAM,IAAI,KAAK,MAAM;AAAA,EAC5C;AAEA,SAAO;AACT;AAEA,IAAM,oCAAoC,CACxC,QACAA,UACA,qBACoC;AACpC,QAAM,EAAE,mBAAmB,4BAA4B,IAAIA;AAE3D,QAAM,eAAe;AAAA,IACnB;AAAA,IACA,sBAAsB,QAAQ,SAAY;AAAA,EAC5C;AAEA,aAAW,OAAO,iBAAiB,YAAY,GAAG;AAChD,QAAI,aAAa,GAAG,MAAM,MAAM,OAAO;AACrC,mBAAa,GAAG,IAAI;AAAA,IACtB;AAAA,EACF;AAEA,MAAI,6BAA6B;AAC/B,iBAAa,mBAAmB;AAAA,EAClC;AAEA,SAAO;AACT;AAGO,IAAM,wBAAwB,CACnC,OACAA,UACA,mBAA4B,SACU;AACtC,QAAM,SAA4C,CAAC;AACnD,QAAM,EAAE,WAAW,gBAAgB,IAAIA;AAEvC,MAAI,cAAc,QAAW;AAC3B,UAAM,QAAQ,CAAC,SAAS;AACtB,UAAI,UAAU,SAAS,KAAK,IAAI,GAAG;AACjC,eAAO,KAAK,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG,WAAW,oBAAoB,IAAI,KAAK,iBAAiB;AACvD,eAAO,KAAK,GAAG,sBAAsB,KAAK,UAAUA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG;AAAA,IACF,CAAC;AAAA,EACH,OAAO;AACL,UAAM,QAAQ,CAAC,SAAS;AACtB,UAAI,oBAAoB,IAAI,KAAK,iBAAiB;AAChD,cAAM,UAAU,iCACX,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,IADtE;AAAA,UAEd,UAAU,sBAAsB,KAAK,UAAUA,UAAS,oBAAoB,KAAK,OAAO;AAAA,QAC1F;AACA,eAAO,KAAK,OAAO;AAAA,MACrB,OAAO;AACL,eAAO,KAAK,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;AHrFA,IAAM,eAAe;AAAA,EACnB,mBAAmB,WAA+D;AAChF,QAAI,OAAO,OAAO,6BAA6B,aAAa;AAC1D,aAAO,yBAAyB,SAAS;AAAA,IAC3C;AAAA,EACF;AACF;AAEO,IAAI,YAAQ,qCAAY,YAAY;AAEpC,IAAM,yBAAyB,CAAC,eAA2B;AAChE,cAAQ,qCAAY,cAAc,UAAU;AAC9C;AAEA,IAAM,yBAAyB,MAAM;AACnC,QAAM,oBAAoB,sBAAsB,MAAM,YAAY,WAAW,OAAO;AACpF,QAAM,mBAAmB,iBAAiB;AAC5C;AAEA,IAAM,2CAA2C,CAAC,GAAwB,UAAyC;AACjH,QAAM,sBAAsB,EAAE,gBAAgB;AAAA,IAC5C,CAAC,WAAW,MAAM,UAAU,CAAC,SAAS,KAAK,OAAO,OAAO,EAAE,MAAM;AAAA,EACnE;AAEA,MAAI,qBAAqB;AACvB,WAAO;AAAA,EACT;AAEA,QAAM,cAA2B,CAAC;AAElC,QAAM,QAAQ,CAAC,SAAS;AACtB,QAAI,oBAAoB,IAAI,GAAG;AAC7B,kBAAY,KAAK,GAAG,KAAK,QAAQ;AAAA,IACnC;AAAA,EACF,CAAC;AAED,MAAI,YAAY,WAAW,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,SAAO,yCAAyC,GAAG,WAAW;AAChE;AAEA,IAAM,wBAAwB,CAAC,MAA2B;AACxD,MAAI,MAAM,YAAY,UAAU,SAAS,GAAG;AAC1C,UAAM,YAAY,MAAM,YAAY;AAEpC,QAAI,yCAAyC,GAAG,SAAS,GAAG;AAC1D,6BAAuB;AAAA,IACzB;AAAA,EACF;AACF;AAEA,IAAM,aAAa;AAAA,EACjB,4BAA4B,MAAiC;AAC3D,cAAU;AAEV,UAAM,aAAa,KAAK;AACxB,QAAI,YAAY;AACd,6BAAuB,UAAU;AACjC,2BAAqB,UAAU;AAAA,IACjC;AAEA,UAAM,GAAG,mBAAmB,sBAAsB;AAClD,UAAM,GAAG,kBAAkB,qBAAqB;AAChD,2BAAuB;AAAA,EACzB;AAAA,EACA,gCAAgC;AAC9B,UAAM,IAAI,mBAAmB,sBAAsB;AACnD,UAAM,IAAI,kBAAkB,qBAAqB;AAAA,EACnD;AAAA,EACA,cAA6C,cAAiB;AAC5D,UAAM,YAAY,YAAY;AAAA,EAChC;AACF;AAEO,IAAI,UAAM,yCAAgB,UAAU;AAEpC,IAAM,uBAAuB,CAAC,eAA2B;AAC9D,YAAM,yCAAgB,YAAY,UAAU;AAC9C;AAEA,IAAI;AAEG,IAAI,YAAsC,CAAC;AAE3C,IAAM,eAAe,CAAC,iBAA2C;AACtE,cAAY;AACd;AAGA,IAAI,OAAO,WAAW,aAAa;AACjC,SAAO,2BAA2B,CAAC,cAAc;AAC/C,cAAU,QAAQ,CAAC,MAAM;AACvB,QAAE,SAAS;AAAA,IACb,CAAC;AAAA,EACH;AACF;",
"names": ["options"]
}

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

import { RPCOptions } from 'figma-plugin-api';
import { BareNode, FigmaSelectionHookOptions, FigmaSelectionListener, ResolverOptions, SerializedResolvedNode } from './types';

@@ -8,6 +9,7 @@ export { FIGMA_MIXED } from './constants';

}
export declare const uiApi: Readonly<import("figma-plugin-api").RPCAPIReturnType<{
export declare let uiApi: Readonly<import("figma-plugin-api").RPCAPIReturnType<{
_onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]): void;
}>>;
export declare const api: Readonly<import("figma-plugin-api").RPCAPIReturnType<{
export declare const updateUiApiWithOptions: (rpcOptions: RPCOptions) => void;
export declare let api: Readonly<import("figma-plugin-api").RPCAPIReturnType<{
_registerForSelectionChange(opts: FigmaSelectionHookOptions): void;

@@ -17,3 +19,4 @@ _deregisterForSelectionChange(): void;

}>>;
export declare const updateApiWithOptions: (rpcOptions: RPCOptions) => void;
export declare let listeners: FigmaSelectionListener[];
export declare const setlisteners: (newListeners: FigmaSelectionListener[]) => void;

@@ -21,246 +21,8 @@ var __defProp = Object.defineProperty;

// node_modules/figma-plugin-api/lib/index.mjs
var __defProp2 = Object.defineProperty;
var __getOwnPropSymbols2 = Object.getOwnPropertySymbols;
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
var __propIsEnum2 = Object.prototype.propertyIsEnumerable;
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues2 = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp2.call(b, prop))
__defNormalProp2(a, prop, b[prop]);
if (__getOwnPropSymbols2)
for (var prop of __getOwnPropSymbols2(b)) {
if (__propIsEnum2.call(b, prop))
__defNormalProp2(a, prop, b[prop]);
}
return a;
};
var __publicField = (obj, key, value) => {
__defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
return value;
};
var RPCError = class extends Error {
constructor(code, message, data) {
super();
__publicField(this, "code");
__publicField(this, "message");
__publicField(this, "data");
this.code = code;
this.message = message;
this.data = data;
}
};
var InvalidRequest = class extends RPCError {
constructor(data) {
super(-32600, "Invalid request", data);
}
};
var MethodNotFound = class extends RPCError {
constructor(data) {
super(-32601, "Method not found", data);
}
};
var isRpcOutGoing = (req) => {
return Object.prototype.hasOwnProperty.call(req, "method");
};
var isRpcNotification = (req) => {
return isRpcOutGoing(req) && !Object.prototype.hasOwnProperty.call(req, "id");
};
var isRpcResponseResult = (req) => {
return Object.prototype.hasOwnProperty.call(req, "result");
};
var isRpcResponseError = (req) => {
return Object.prototype.hasOwnProperty.call(req, "error");
};
var isRpcResponse = (req) => {
return isRpcResponseResult(req) || isRpcResponseError(req);
};
var logBase = (level, ...msg) => console[level](...msg);
var logWarn = (...msg) => logBase("warn", ...msg);
var logError = (...msg) => logBase("error", ...msg);
var methods = {};
var pending = {};
var rpcIndex = 0;
var sendRaw;
var sendJson = (msg) => {
try {
sendRaw(msg);
} catch (err) {
logError(err);
}
};
var sendResult = (id, result) => {
sendJson({
jsonrpc: "2.0",
id,
result
});
};
var sendError = (id, error) => {
sendJson({
jsonrpc: "2.0",
id,
error: {
code: error.code,
message: error.message,
data: error.data
}
});
};
var handleRpc = (json) => {
if (!isRpcNotification(json)) {
if (isRpcResponse(json)) {
const callback = pending[json.id];
if (!callback) {
sendError(json.id, new InvalidRequest(`Missing callback for ${json.id}`));
return;
}
if (callback.timeout) {
clearTimeout(callback.timeout);
}
delete pending[json.id];
callback(isRpcResponseResult(json) ? json.result : void 0, isRpcResponseError(json) ? json.error : void 0);
} else {
handleRequest(json);
}
} else {
handleNotification(json);
}
};
var handleRaw = (data) => {
try {
if (!data) {
return;
}
handleRpc(data);
} catch (err) {
logError("handleRaw error", err, data);
}
};
var onRequest = (method, params) => {
if (!methods[method]) {
logError(`onRequest: Method ${method} not found in methods: ${Object.keys(methods)}`);
throw new MethodNotFound(method);
}
if (params === void 0) {
return methods[method]();
}
return methods[method](...params);
};
var handleNotification = (json) => {
if (!json.method) {
logWarn(`handleNotification: no method specified in message ${json}`);
return;
}
onRequest(json.method, json.params);
};
var handleRequest = async (json) => {
if (!json.method) {
logWarn(`handleRequest: no method specified in message ${json}`);
sendError(json.id, new InvalidRequest("No method specified in message"));
return;
}
try {
const result = await onRequest(json.method, json.params);
if (result !== void 0 && result instanceof Promise) {
try {
await result;
sendResult(json.id, result);
} catch (e) {
sendError(json.id, e);
}
} else {
sendResult(json.id, result);
}
} catch (err) {
sendError(json.id, err);
}
};
var setup = (_methods, _sendRaw, _logBase) => {
Object.assign(methods, _methods);
sendRaw = _sendRaw;
_logBase !== void 0 && (logBase = _logBase);
};
var sendRequest = (method, params, timeoutMs) => {
return new Promise((resolve, reject) => {
const id = rpcIndex;
const req = { jsonrpc: "2.0", method, params, id };
rpcIndex += 1;
const callback = (result, err) => {
if (err) {
const jsError = new RPCError(err.code, err.message, err.data);
reject(jsError);
return;
}
resolve(result);
};
callback.timeout = setTimeout(() => {
delete pending[id];
logWarn(`Request id ${id} (${method}) timed out.`);
reject(new Error(`Request ${id} (${method}) timed out.`));
}, timeoutMs);
pending[id] = callback;
sendJson(req);
});
};
var isFigma = typeof figma !== "undefined";
var isUi = typeof parent !== "undefined";
var logBase2 = (level, ...msg) => console[level](`RPC in ${isFigma ? "logic" : isUi ? "ui" : "UNKNOWN"}:`, ...msg);
var strictObjectKeys = Object.keys;
var DEFAULT_OPTIONS = {
timeoutMs: 3e3,
logicTargetOrigin: "*",
uiTargetOrigin: "*"
};
var sendRaw2;
var setupMessaging = (pluginId, logicTargetOrigin, uiTargetOrigin) => {
if (typeof figma !== "undefined") {
figma.ui.on("message", (message) => handleRaw(message));
sendRaw2 = (message) => figma.ui.postMessage(message, { origin: logicTargetOrigin });
} else if (typeof parent !== "undefined") {
onmessage = (event) => handleRaw(event.data.pluginMessage);
if (pluginId !== void 0) {
sendRaw2 = (pluginMessage) => parent.postMessage({ pluginMessage, pluginId }, uiTargetOrigin);
} else {
sendRaw2 = (pluginMessage) => parent.postMessage({ pluginMessage }, uiTargetOrigin);
}
} else {
console.warn("Both parent and figma are undefined, it seems like the runtime is neither Figma nor a browser");
}
};
var createAPI = (methods2, hostType, options2) => {
const opts = __spreadValues2(__spreadValues2({}, DEFAULT_OPTIONS), options2);
const { timeoutMs, pluginId, logicTargetOrigin, uiTargetOrigin } = opts;
if (sendRaw2 === void 0) {
setupMessaging(pluginId, logicTargetOrigin, uiTargetOrigin);
}
if (hostType !== "undefined") {
if (sendRaw2 === void 0) {
console.error("sendRaw is undefined at during setup, the API will not work");
}
setup(methods2, sendRaw2, logBase2);
}
const api2 = strictObjectKeys(methods2).reduce((prev, p) => {
const method = async (...params) => {
if (hostType !== "undefined") {
return await methods2[p](...params);
}
return await sendRequest(p, params, timeoutMs);
};
prev[p] = method;
return prev;
}, {});
return api2;
};
var createUIAPI = (methods2, options2) => {
return createAPI(methods2, typeof parent, options2);
};
var createPluginAPI = (methods2, options2) => {
return createAPI(methods2, typeof figma, options2);
};
// src/index.ts
import { createUIAPI, createPluginAPI } from "figma-plugin-api";
// src/typeUtils.ts
var isArray = Array.isArray;
var strictObjectKeys2 = Object.keys;
var strictObjectKeys = Object.keys;
var nodeCanHaveChildren = (node) => {

@@ -284,3 +46,3 @@ return "children" in node;

const descriptors = Object.getOwnPropertyDescriptors(Object.getPrototypeOf(node));
const getterKeys = strictObjectKeys2(descriptors);
const getterKeys = strictObjectKeys(descriptors);
let getters = getterKeys.filter((key) => typeof descriptors[key].get === "function");

@@ -306,3 +68,3 @@ getters.push("type");

);
for (const key of strictObjectKeys2(resolvedNode)) {
for (const key of strictObjectKeys(resolvedNode)) {
if (resolvedNode[key] === figma.mixed) {

@@ -344,3 +106,3 @@ resolvedNode[key] = FIGMA_MIXED;

// src/index.ts
var uiApi = createUIAPI({
var uiApiMethods = {
_onSelectionChange(selection) {

@@ -351,7 +113,9 @@ if (typeof window._figma_onSelectionChange !== "undefined") {

}
});
};
var uiApi = createUIAPI(uiApiMethods);
var updateUiApiWithOptions = (rpcOptions) => {
uiApi = createUIAPI(uiApiMethods, rpcOptions);
};
var selectionChangeHandler = () => {
console.log("Selection change handler", figma.currentPage.selection);
const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);
console.log("Filtered selection:", resolvedSelection);
uiApi._onSelectionChange(resolvedSelection);

@@ -385,6 +149,10 @@ };

};
var api = createPluginAPI({
var apiMethods = {
_registerForSelectionChange(opts) {
console.log("Register");
options = opts;
const apiOptions = opts.apiOptions;
if (apiOptions) {
updateUiApiWithOptions(apiOptions);
updateApiWithOptions(apiOptions);
}
figma.on("selectionchange", selectionChangeHandler);

@@ -401,3 +169,7 @@ figma.on("documentchange", documentChangeHandler);

}
});
};
var api = createPluginAPI(apiMethods);
var updateApiWithOptions = (rpcOptions) => {
api = createPluginAPI(apiMethods, rpcOptions);
};
var options;

@@ -420,4 +192,6 @@ var listeners = [];

setlisteners,
uiApi
uiApi,
updateApiWithOptions,
updateUiApiWithOptions
};
//# sourceMappingURL=index.mjs.map
{
"version": 3,
"sources": ["../node_modules/figma-plugin-api/src/errors.ts", "../node_modules/figma-plugin-api/src/types.ts", "../node_modules/figma-plugin-api/src/rpc.ts", "../node_modules/figma-plugin-api/src/utils.ts", "../node_modules/figma-plugin-api/src/index.ts", "../src/typeUtils.ts", "../src/constants.ts", "../src/utils.ts", "../src/index.ts"],
"sourcesContent": ["export class RPCError extends Error {\n code: number;\n message: string;\n data: unknown;\n\n constructor(code: number, message: string, data?: unknown) {\n super();\n this.code = code;\n this.message = message;\n this.data = data;\n }\n}\n\n/**\n * Invalid JSON was received by the server.\n * An error occurred on the server while parsing the JSON text.\n */\nexport class ParseError extends RPCError {\n constructor(data?: unknown) {\n super(-32700, 'Parse error', data);\n }\n}\n\n/**\n * The JSON sent is not a valid Request object.\n */\nexport class InvalidRequest extends RPCError {\n constructor(data?: unknown) {\n super(-32600, 'Invalid request', data);\n }\n}\n\n/**\n * The method does not exist / is not available.\n */\nexport class MethodNotFound extends RPCError {\n constructor(data?: unknown) {\n super(-32601, 'Method not found', data);\n }\n}\n\n/**\n * Invalid method parameter(s).\n */\nexport class InvalidParams extends RPCError {\n constructor(data?: unknown) {\n super(-32602, 'Invalid params', data);\n }\n}\n\n/**\n * Internal JSON-RPC error.\n */\nexport class InternalError extends RPCError {\n constructor(data?: unknown) {\n super(-32603, 'Internal error', data);\n }\n}\n", "/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport type LogLevel = 'log' | 'warn' | 'error';\n\nexport type AwaitedReturn<T extends (...params: any) => any> = ReturnType<T> extends Promise<any>\n ? Awaited<ReturnType<T>>\n : ReturnType<T>;\n\nexport type StrictParameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;\n\nexport type ApiFunctions = {\n [x: string]: (...params: any[]) => any;\n};\n\nexport type RPCMethodObject<T extends ApiFunctions> = {\n [K in keyof T]: (...args: StrictParameters<T[K]>) => Promise<AwaitedReturn<T[K]>>;\n};\n\nexport type RPCAPIReturnType<T extends RPCMethodObject<ApiFunctions>> = {\n [K in keyof T]: (...args: StrictParameters<T[K]>) => Promise<AwaitedReturn<T[K]>>;\n};\n\nexport type RPCErrorObject = {\n code: number;\n message: string;\n data: unknown;\n};\n\nexport interface RPCNotification<P extends unknown[]> {\n jsonrpc: '2.0';\n method: string;\n params?: P;\n}\n\nexport interface RPCRequest<P extends unknown[]> extends RPCNotification<P> {\n jsonrpc: '2.0';\n method: string;\n params?: P;\n id: number;\n}\n\nexport interface RPCResponseBase {\n jsonrpc: '2.0';\n id: number;\n}\n\nexport interface RPCResponseResult<T> extends RPCResponseBase {\n result: T;\n}\n\nexport interface RPCResponseError extends RPCResponseBase {\n error: RPCErrorObject;\n}\n\nexport type RPCResponse<T> = RPCResponseResult<T> | RPCResponseError;\n\nexport type RPCMessage<P extends unknown[], T> = RPCRequest<P> | RPCNotification<P> | RPCResponse<T>;\n\nexport type RPCCallBack<T extends (...args: any[]) => any> = {\n (result: AwaitedReturn<T> | undefined, error: RPCErrorObject | undefined): AwaitedReturn<T>;\n timeout: number;\n};\n\nexport type RPCOptions = {\n /**\n * Timeout in milliseconds\n *\n * Default: `3000`\n */\n timeoutMs?: number;\n /**\n * If your plugin UI is hosted (non-null origin), pluginId must be defined to allow messages to be sent\n */\n pluginId?: string;\n /**\n * Specifies what the origin of the plugin UI must be for a message to be dispatched from plugin logic to UI\n *\n * If defined, add `http://localhost:<port>` to this field in your local environment to allow messaging while running on a dev server\n *\n * Default: `'*'`\n */\n logicTargetOrigin?: string;\n /**\n * Specifies what the origin of the plugin logic must be for a message to be dispatched from UI to plugin logic\n *\n * Usually `'https://www.figma.com'`\n *\n * Default: `'*'`\n */\n uiTargetOrigin?: string;\n};\n\nexport type RPCDefaultOptions = Required<Omit<RPCOptions, 'pluginId'>>;\n\nexport type RPCSendRaw = <P extends unknown[], T>(message: RPCMessage<P, T>) => void;\n\nexport const isRpcOutGoing = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCNotification<P> | RPCRequest<P> => {\n return Object.prototype.hasOwnProperty.call(req, 'method');\n};\n\nexport const isRpcNotification = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCNotification<P> => {\n return isRpcOutGoing(req) && !Object.prototype.hasOwnProperty.call(req, 'id');\n};\n\nexport const isRpcRequest = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCRequest<P> => {\n return isRpcOutGoing(req) && Object.prototype.hasOwnProperty.call(req, 'id');\n};\n\nexport const isRpcResponseResult = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCResponseResult<T> => {\n return Object.prototype.hasOwnProperty.call(req, 'result');\n};\n\nexport const isRpcResponseError = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCResponseError => {\n return Object.prototype.hasOwnProperty.call(req, 'error');\n};\n\nexport const isRpcResponse = <P extends unknown[], T>(\n req: RPCNotification<P> | RPCResponse<T>\n): req is RPCResponse<T> => {\n return isRpcResponseResult(req) || isRpcResponseError(req);\n};\n", "import { InvalidRequest, MethodNotFound, RPCError } from './errors';\nimport {\n ApiFunctions,\n LogLevel,\n RPCCallBack,\n RPCMessage,\n RPCMethodObject,\n RPCNotification,\n RPCRequest,\n RPCResponse,\n RPCSendRaw,\n isRpcNotification,\n isRpcResponse,\n isRpcResponseError,\n isRpcResponseResult\n} from './types';\n\n// Debugging setup\n// Generic logger that is overridden with a more specific one by setup\ntype Logger = (level: LogLevel, ...msg: unknown[]) => void;\nlet logBase: Logger = (level, ...msg) => console[level](...msg);\n\nconst logWarn = (...msg: unknown[]) => logBase('warn', ...msg);\nconst logError = (...msg: unknown[]) => logBase('error', ...msg);\n/* eslint-enable @typescript-eslint/no-unused-vars */\n\n// Methods for processing remote API calls (set up on-host)\nconst methods: RPCMethodObject<ApiFunctions> = {};\ntype MethodName = keyof typeof methods & string;\n\n// Pending API calls (used off-host)\nconst pending: Record<number, RPCCallBack<(typeof methods)[MethodName]>> = {};\nlet rpcIndex = 0;\n\nlet sendRaw: RPCSendRaw;\n\nconst sendJson = <P extends unknown[], T>(msg: RPCMessage<P, T>) => {\n try {\n sendRaw(msg);\n } catch (err) {\n logError(err);\n }\n};\n\nconst sendResult = <T>(id: number, result: T) => {\n sendJson({\n jsonrpc: '2.0',\n id,\n result\n });\n};\n\nconst sendError = (id: number, error: RPCError) => {\n sendJson({\n jsonrpc: '2.0',\n id,\n error: {\n code: error.code,\n message: error.message,\n data: error.data\n }\n });\n};\n\nconst handleRpc = <P extends unknown[], T>(json: RPCNotification<P> | RPCResponse<T>) => {\n if (!isRpcNotification(json)) {\n if (isRpcResponse(json)) {\n const callback = pending[json.id];\n if (!callback) {\n sendError(json.id, new InvalidRequest(`Missing callback for ${json.id}`));\n return;\n }\n if (callback.timeout) {\n clearTimeout(callback.timeout);\n }\n delete pending[json.id];\n callback(isRpcResponseResult(json) ? json.result : undefined, isRpcResponseError(json) ? json.error : undefined);\n } else {\n handleRequest(json);\n }\n } else {\n handleNotification(json);\n }\n};\n\nexport const handleRaw = <P extends unknown[], T>(data: RPCNotification<P> | RPCResponse<T>) => {\n try {\n if (!data) {\n return;\n }\n handleRpc(data);\n } catch (err) {\n logError('handleRaw error', err, data);\n }\n};\n\nconst onRequest = <K extends MethodName>(method: K, params: Parameters<(typeof methods)[K]> | undefined) => {\n if (!methods[method]) {\n logError(`onRequest: Method ${method} not found in methods: ${Object.keys(methods)}`);\n throw new MethodNotFound(method);\n }\n if (params === undefined) {\n return methods[method]();\n }\n return methods[method](...params);\n};\n\nconst handleNotification = <P extends unknown[]>(json: RPCNotification<P>) => {\n if (!json.method) {\n logWarn(`handleNotification: no method specified in message ${json}`);\n return;\n }\n\n onRequest(json.method, json.params);\n};\n\nconst handleRequest = async <P extends unknown[]>(json: RPCRequest<P>) => {\n if (!json.method) {\n logWarn(`handleRequest: no method specified in message ${json}`);\n sendError(json.id, new InvalidRequest('No method specified in message'));\n return;\n }\n\n try {\n const result = await onRequest(json.method, json.params);\n if (result !== undefined && result instanceof Promise) {\n try {\n await result;\n sendResult(json.id, result);\n } catch (e) {\n sendError(json.id, e as RPCError);\n }\n } else {\n sendResult(json.id, result);\n }\n } catch (err) {\n sendError(json.id, err as RPCError);\n }\n};\n\n/**\n *\n * @param _methods Remote API call handlers\n * @param _sendRaw Function for sending the raw JSON, e.g. `parent.postMessage`\n * @param _logBase Logger function for context-aware logging\n */\nexport const setup = <T extends RPCMethodObject<ApiFunctions>>(\n _methods: T,\n _sendRaw: RPCSendRaw,\n _logBase?: Logger\n) => {\n Object.assign(methods, _methods);\n sendRaw = _sendRaw;\n _logBase !== undefined && (logBase = _logBase);\n};\n\nexport const sendNotification = <P extends unknown[]>(method: string, params: P) => {\n sendJson({ jsonrpc: '2.0', method, params });\n};\n\nexport const sendRequest = <K extends MethodName, P extends unknown[]>(method: K, params: P, timeoutMs: number) => {\n return new Promise((resolve, reject) => {\n const id = rpcIndex;\n const req: RPCRequest<P> = { jsonrpc: '2.0', method, params, id };\n rpcIndex += 1;\n const callback: RPCCallBack<(typeof methods)[K]> = (result, err) => {\n if (err) {\n const jsError = new RPCError(err.code, err.message, err.data);\n reject(jsError);\n return;\n }\n resolve(result);\n };\n\n // Set the timeout\n callback.timeout = setTimeout(() => {\n delete pending[id];\n logWarn(`Request id ${id} (${method}) timed out.`);\n reject(new Error(`Request ${id} (${method}) timed out.`));\n }, timeoutMs);\n\n pending[id] = callback;\n sendJson(req);\n });\n};\n", "import { LogLevel } from './types';\n\nconst isFigma = typeof figma !== 'undefined';\nconst isUi = typeof parent !== 'undefined';\nexport const logBase = (level: LogLevel, ...msg: unknown[]) =>\n console[level](`RPC in ${isFigma ? 'logic' : isUi ? 'ui' : 'UNKNOWN'}:`, ...msg);\n\nexport const strictObjectKeys = Object.keys as <T extends Record<string, unknown>>(obj: T) => Array<keyof T>;\n", "import { handleRaw, sendRequest, setup } from './rpc';\nimport {\n ApiFunctions,\n AwaitedReturn,\n RPCAPIReturnType,\n RPCDefaultOptions,\n RPCOptions,\n RPCSendRaw,\n StrictParameters\n} from './types';\nimport { logBase, strictObjectKeys } from './utils';\n\nexport { ApiFunctions, RPCAPIReturnType, RPCOptions } from './types';\n\nconst DEFAULT_OPTIONS: RPCDefaultOptions = {\n timeoutMs: 3000,\n logicTargetOrigin: '*',\n uiTargetOrigin: '*'\n};\n\nlet sendRaw: RPCSendRaw;\n/**\n * Set up sending and receiving of messages for Figma plugin logic and UI\n */\nconst setupMessaging = (pluginId: string | undefined, logicTargetOrigin: string, uiTargetOrigin: string) => {\n if (typeof figma !== 'undefined') {\n figma.ui.on('message', (message) => handleRaw(message));\n\n sendRaw = (message) => figma.ui.postMessage(message, { origin: logicTargetOrigin });\n } else if (typeof parent !== 'undefined') {\n onmessage = (event) => handleRaw(event.data.pluginMessage);\n\n if (pluginId !== undefined) {\n sendRaw = (pluginMessage) => parent.postMessage({ pluginMessage, pluginId }, uiTargetOrigin);\n } else {\n sendRaw = (pluginMessage) => parent.postMessage({ pluginMessage }, uiTargetOrigin);\n }\n } else {\n // Should not happen but log an error just in case\n console.warn('Both parent and figma are undefined, it seems like the runtime is neither Figma nor a browser');\n }\n};\n\n/**\n * Creates one side of a JSON-RPC API for a Figma plugin\n * @param hostType A typeof string of an object that differentiates between on-host and off-host.\n * For example, if `typeof figma` is used, when `figma` is defined the creator is considered to be on-host\n * and the methods for processing the remote API calls are set up.\n * Off-host the creator sets up the equivalent remote API calls to make requests to host.\n */\nconst createAPI = <T extends ApiFunctions>(\n methods: T,\n hostType: string,\n options?: RPCOptions\n): Readonly<RPCAPIReturnType<T>> => {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n const { timeoutMs, pluginId, logicTargetOrigin, uiTargetOrigin } = opts;\n\n if (sendRaw === undefined) {\n setupMessaging(pluginId, logicTargetOrigin, uiTargetOrigin);\n }\n\n if (hostType !== 'undefined') {\n // Should not happen but log an error just in case\n if (sendRaw === undefined) {\n console.error('sendRaw is undefined at during setup, the API will not work');\n }\n\n setup(methods, sendRaw, logBase);\n }\n\n const api: RPCAPIReturnType<T> = strictObjectKeys(methods).reduce((prev, p) => {\n const method = async (...params: StrictParameters<T[keyof T]>) => {\n if (hostType !== 'undefined') {\n return (await methods[p](...params)) as AwaitedReturn<T[keyof T]>;\n }\n\n return (await sendRequest(p as string, params, timeoutMs)) as AwaitedReturn<T[keyof T]>;\n };\n\n prev[p] = method;\n return prev;\n }, {} as RPCAPIReturnType<T>);\n\n return api;\n};\n\n/**\n * Create a set of methods that can be called from plugin logic and are executed in plugin UI\n * This side has access to the browser API\n */\nexport const createUIAPI = <T extends ApiFunctions>(\n methods: T,\n options?: RPCOptions\n): Readonly<RPCAPIReturnType<T>> => {\n return createAPI(methods, typeof parent, options);\n};\n\n/**\n * Create a set of methods that can be called from plugin UI and are executed in plugin logic\n * This side has acccess to the `figma` API\n */\nexport const createPluginAPI = <T extends ApiFunctions>(\n methods: T,\n options?: RPCOptions\n): Readonly<RPCAPIReturnType<T>> => {\n return createAPI(methods, typeof figma, options);\n};\n", "import { ArrayType } from './typePrimitives';\n\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n", "/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n", "import { FIGMA_MIXED } from './constants';\nimport { nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { ResolvedNode, ResolverOptions, SceneNodePropertyKey, SerializedResolvedNode } from './types';\n\nconst defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {\n return (\n // Can only get component property definitions of a component set or non-variant component\n !(key === 'componentPropertyDefinitions' && node.parent?.type === 'COMPONENT_SET') &&\n // reading horizontalPadding and verticalPadding is no longer supported as left and right padding may differ\n key !== 'horizontalPadding' &&\n key !== 'verticalPadding'\n );\n};\n\nconst resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(\n node: Node,\n propertyKeys?: Keys\n): ResolvedNode<Node, Keys> => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {\n [P in keyof Node]: TypedPropertyDescriptor<Node[P]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');\n\n // type has to be included manually as it doesn't have a getter but isn't a static property either\n getters.push('type');\n\n if (propertyKeys) {\n getters = getters.filter((key) => propertyKeys.includes(key as SceneNodePropertyKey));\n }\n\n getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));\n\n const objectWithProperties = {\n id: node.id\n } as Node;\n\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n object: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, addAncestorsVisibleProperty } = options;\n\n const resolvedNode = resolveNodeProperties(\n object,\n resolveProperties === 'all' ? undefined : resolveProperties\n ) as Record<string, unknown>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n if (resolvedNode[key] === figma.mixed) {\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\n// TODO: Resolve variables\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n nodes.forEach((node) => {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n });\n } else {\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n });\n }\n\n return result;\n};\n", "import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nexport const uiApi = createUIAPI({\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n});\n\nconst selectionChangeHandler = () => {\n console.log('Selection change handler', figma.currentPage.selection);\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n console.log('Filtered selection:', resolvedSelection);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n });\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nexport const api = createPluginAPI({\n _registerForSelectionChange(opts: FigmaSelectionHookOptions) {\n console.log('Register');\n options = opts;\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n});\n\nlet options: FigmaSelectionHookOptions;\n\nexport let listeners: FigmaSelectionListener[] = [];\n\nexport const setlisteners = (newListeners: FigmaSelectionListener[]) => {\n listeners = newListeners;\n};\n\n// In plugin UI, add a global function to receive selection change events\nif (typeof window !== 'undefined') {\n window._figma_onSelectionChange = (selection) => {\n listeners.forEach((l) => {\n l(selection);\n });\n };\n}\n"],
"mappings": "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",
"names": ["logBase", "sendRaw", "methods", "options", "__spreadValues", "api", "strictObjectKeys", "strictObjectKeys", "options"]
"sources": ["../src/index.ts", "../src/typeUtils.ts", "../src/constants.ts", "../src/utils.ts"],
"sourcesContent": ["import { createUIAPI, createPluginAPI, RPCOptions } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n });\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: FigmaSelectionHookOptions;\n\nexport let listeners: FigmaSelectionListener[] = [];\n\nexport const setlisteners = (newListeners: FigmaSelectionListener[]) => {\n listeners = newListeners;\n};\n\n// In plugin UI, add a global function to receive selection change events\nif (typeof window !== 'undefined') {\n window._figma_onSelectionChange = (selection) => {\n listeners.forEach((l) => {\n l(selection);\n });\n };\n}\n", "import { ArrayType } from './typePrimitives';\n\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n", "/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n", "import { FIGMA_MIXED } from './constants';\nimport { nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { ResolvedNode, ResolverOptions, SceneNodePropertyKey, SerializedResolvedNode } from './types';\n\nconst defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {\n return (\n // Can only get component property definitions of a component set or non-variant component\n !(key === 'componentPropertyDefinitions' && node.parent?.type === 'COMPONENT_SET') &&\n // reading horizontalPadding and verticalPadding is no longer supported as left and right padding may differ\n key !== 'horizontalPadding' &&\n key !== 'verticalPadding'\n );\n};\n\nconst resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(\n node: Node,\n propertyKeys?: Keys\n): ResolvedNode<Node, Keys> => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {\n [P in keyof Node]: TypedPropertyDescriptor<Node[P]>;\n };\n\n // In reality the type is string | number | keyof T from getOwnPropertyDescriptors\n // but we can safely assert a narrower type\n const getterKeys = strictObjectKeys(descriptors);\n let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');\n\n // type has to be included manually as it doesn't have a getter but isn't a static property either\n getters.push('type');\n\n if (propertyKeys) {\n getters = getters.filter((key) => propertyKeys.includes(key as SceneNodePropertyKey));\n }\n\n getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));\n\n const objectWithProperties = {\n id: node.id\n } as Node;\n\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n object: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, addAncestorsVisibleProperty } = options;\n\n const resolvedNode = resolveNodeProperties(\n object,\n resolveProperties === 'all' ? undefined : resolveProperties\n ) as Record<string, unknown>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n if (resolvedNode[key] === figma.mixed) {\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\n// TODO: Resolve variables\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n nodes.forEach((node) => {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n });\n } else {\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n });\n }\n\n return result;\n};\n"],
"mappings": "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",
"names": ["options"]
}

@@ -62,3 +62,3 @@ /// <reference types="@figma/plugin-typings" />

*
* Default: `all`
* Default: `'all'`
*/

@@ -65,0 +65,0 @@ resolveProperties?: OptSceneNodeProperties;

{
"name": "figma-plugin-react-hooks",
"version": "1.1.0",
"version": "2.0.0",
"description": "",

@@ -35,6 +35,6 @@ "exports": {

"dev": "concurrently -n index-cjs,index-esm,hook-cjs,hook-esm,ts 'npm run build:index:common -- --watch' 'npm run build:index:esm -- --watch' 'npm run build:hook:common -- --watch' 'npm run build:hook:esm -- --watch' 'npm run build:types -- --watch'",
"build:index": "esbuild src/index.ts --target=es2017 --sourcemap --bundle",
"build:index": "esbuild src/index.ts --target=es2017 --sourcemap --bundle --external:figma-plugin-api",
"build:index:common": "npm run build:index -- --format=cjs --outfile=lib/index.cjs",
"build:index:esm": "npm run build:index -- --format=esm --outfile=lib/index.mjs",
"build:hook": "esbuild src/hook.ts --target=es2017 --sourcemap --bundle --external:react",
"build:hook": "esbuild src/hook.ts --target=es2017 --sourcemap --bundle --external:react --external:figma-plugin-api",
"build:hook:common": "npm run build:hook -- --format=cjs --outfile=lib/hook.cjs",

@@ -50,7 +50,5 @@ "build:hook:esm": "npm run build:hook -- --format=esm --outfile=lib/hook.mjs",

"peerDependencies": {
"react": ">=17.0.0"
"react": ">=17.0.0",
"figma-plugin-api": ">=1.1.0"
},
"dependencies": {
"figma-plugin-api": "^1.0.1"
},
"devDependencies": {

@@ -57,0 +55,0 @@ "@figma/plugin-typings": "*",

@@ -77,7 +77,2 @@ # figma-plugin-react-hooks

## TODO
- Use rpcOptions when creating APIs
- Make addParentChainVisibleProperty do stuff
## Types

@@ -128,3 +123,3 @@

| `nodeTypes?` | readonly `SceneNodeType`[] | Only return specific types of nodes. If left undefined, all nodes in the selection will be returned. Default: `undefined` |
| `resolveProperties?` | [`OptSceneNodeProperties`](types.md#optscenenodeproperties) | Figma node properties are lazy-loaded, so to use any property you have to resolve it first. Resolving all node properties causes a performance hit, so you can specify which properties you want to resolve. If set to `[]`, no properties will be resolved and you will only get the ids of the nodes. Node methods (such as `getPluginData`) will never be resolved. Default: `all` |
| `resolveProperties?` | [`OptSceneNodeProperties`](types.md#optscenenodeproperties) | Figma node properties are lazy-loaded, so to use any property you have to resolve it first. Resolving all node properties causes a performance hit, so you can specify which properties you want to resolve. If set to `[]`, no properties will be resolved and you will only get the ids of the nodes. Node methods (such as `getPluginData`) will never be resolved. Default: `'all'` |
| `resolveChildren?` | `boolean` | Resolve children nodes of the selection. If used with `nodeTypes`, all nodes of the specified types will be returned as a flat array. Default: `false` |

@@ -131,0 +126,0 @@ | `resolveVariables?` | `boolean` | Resolve bound variables of the selection. Default: `false` |

@@ -7,3 +7,3 @@ /* eslint-disable react-hooks/rules-of-hooks */

import { api, listeners, setlisteners } from '.';
import { api, listeners, setlisteners, updateApiWithOptions, updateUiApiWithOptions } from '.';

@@ -44,3 +44,2 @@ import {

useEffect(() => {
console.log('Hook mount');
const mount = async () => {

@@ -53,2 +52,6 @@ // Typing the listeners explicitly is difficult due to the architecture, so we have to assert

try {
if (opts.apiOptions) {
updateApiWithOptions(opts.apiOptions);
updateUiApiWithOptions(opts.apiOptions);
}
await api._registerForSelectionChange(opts);

@@ -55,0 +58,0 @@ } catch (e) {

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

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

@@ -22,3 +22,3 @@ import { nodeCanHaveChildren } from './typeUtils';

export const uiApi = createUIAPI({
const uiApiMethods = {
_onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {

@@ -29,8 +29,12 @@ if (typeof window._figma_onSelectionChange !== 'undefined') {

}
});
};
export let uiApi = createUIAPI(uiApiMethods);
export const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {
uiApi = createUIAPI(uiApiMethods, rpcOptions);
};
const selectionChangeHandler = () => {
console.log('Selection change handler', figma.currentPage.selection);
const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);
console.log('Filtered selection:', resolvedSelection);
uiApi._onSelectionChange(resolvedSelection);

@@ -73,6 +77,12 @@ };

export const api = createPluginAPI({
const apiMethods = {
_registerForSelectionChange(opts: FigmaSelectionHookOptions) {
console.log('Register');
options = opts;
const apiOptions = opts.apiOptions;
if (apiOptions) {
updateUiApiWithOptions(apiOptions);
updateApiWithOptions(apiOptions);
}
figma.on('selectionchange', selectionChangeHandler);

@@ -89,4 +99,10 @@ figma.on('documentchange', documentChangeHandler);

}
});
};
export let api = createPluginAPI(apiMethods);
export const updateApiWithOptions = (rpcOptions: RPCOptions) => {
api = createPluginAPI(apiMethods, rpcOptions);
};
let options: FigmaSelectionHookOptions;

@@ -93,0 +109,0 @@

@@ -74,3 +74,3 @@ import { RPCOptions } from 'figma-plugin-api';

*
* Default: `all`
* Default: `'all'`
*/

@@ -77,0 +77,0 @@ resolveProperties?: OptSceneNodeProperties;