🚀 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.0.0
to
1.1.0
+32
lib/typePrimitives.d.ts
/**
* @internal
* https://github.com/microsoft/TypeScript/issues/17002#issuecomment-1529056512
*/
export type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;
/**
* @internal
* Returns array elements as union
*/
export type ArrayElementUnion<T extends readonly unknown[]> = T[number];
/**
* @internal
* Get all keys of a union type, instead of just the common keys that `keyof` returns
*/
type KeysOfUnion<T> = T extends infer P ? keyof P : never;
/**
* @internal
* Gets all property keys of an object that are not functions.
*
* When given a union type, it will return all possible property names from the union types.
*/
export type NonFunctionPropertyKeys<T extends object> = {
[K in KeysOfUnion<T>]: T[K] extends Function ? never : K;
}[KeysOfUnion<T>];
/**
* @internal
* Get the non-function property keys for an object from a set of property keys that may be larger than the keys of the object
*
* Does not work with union types
*/
export type ApplicableNonFunctionPropertyKeys<T extends object, K extends string | number | symbol> = K extends keyof T ? T[K] extends Function ? never : K : never;
export {};
/// <reference types="@figma/plugin-typings" />
import { ArrayType } from './typePrimitives';
export declare const isArray: <T>(arg: T) => arg is ArrayType<T>;
export declare const strictObjectKeys: <T extends object>(obj: T) => (keyof T)[];
export declare const nodeCanHaveChildren: <T extends SceneNode>(node: T) => node is T & ChildrenMixin;
/* eslint-disable @typescript-eslint/ban-types */
/**
* @internal
* https://github.com/microsoft/TypeScript/issues/17002#issuecomment-1529056512
*/
export type ArrayType<T> = Extract<
true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[],
T
>;
/**
* @internal
* Returns array elements as union
*/
export type ArrayElementUnion<T extends readonly unknown[]> = T[number];
/**
* @internal
* Get all keys of a union type, instead of just the common keys that `keyof` returns
*/
type KeysOfUnion<T> = T extends infer P ? keyof P : never;
/**
* @internal
* Gets all property keys of an object that are not functions.
*
* When given a union type, it will return all possible property names from the union types.
*/
export type NonFunctionPropertyKeys<T extends object> = {
[K in KeysOfUnion<T>]: T[K] extends Function ? never : K;
}[KeysOfUnion<T>];
/**
* @internal
* Get the non-function property keys for an object from a set of property keys that may be larger than the keys of the object
*
* Does not work with union types
*/
export type ApplicableNonFunctionPropertyKeys<T extends object, K extends string | number | symbol> = K extends keyof T
? T[K] extends Function
? never
: K
: never;
import { ArrayType } from './typePrimitives';
export const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;
export const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;
export const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {
return 'children' in node;
};
+1
-1
/**
* Used to replace `figma.mixed` during JSON serialization
*/
export declare const FIGMA_MIXED = "57999e63-7384-42a1-acf8-d80b9f6c36a7";
export declare const FIGMA_MIXED = "mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7";

@@ -300,6 +300,3 @@ "use strict";

// src/constants.ts
var FIGMA_MIXED = "57999e63-7384-42a1-acf8-d80b9f6c36a7";
// src/types.ts
// src/typeUtils.ts
var isArray = Array.isArray;

@@ -311,2 +308,5 @@ var strictObjectKeys2 = Object.keys;

// src/constants.ts
var FIGMA_MIXED = "mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7";
// src/utils.ts

@@ -330,3 +330,5 @@ var defaultNodePropertyGetterFilter = (key, node) => {

getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));
const objectWithProperties = __spreadValues({}, node);
const objectWithProperties = {
id: node.id
};
for (const getter of getters) {

@@ -337,4 +339,8 @@ objectWithProperties[getter] = node[getter];

};
var resolveAndSerializeNodeProperties = (object, propertyKeys, ancestorsVisible = true) => {
const resolvedNode = resolveNodeProperties(object, propertyKeys);
var resolveAndSerializeNodeProperties = (object, options2, ancestorsVisible) => {
const { resolveProperties, addAncestorsVisibleProperty } = options2;
const resolvedNode = resolveNodeProperties(
object,
resolveProperties === "all" ? void 0 : resolveProperties
);
for (const key of strictObjectKeys2(resolvedNode)) {

@@ -345,3 +351,5 @@ if (resolvedNode[key] === figma.mixed) {

}
resolvedNode.ancestorsVisible = ancestorsVisible;
if (addAncestorsVisibleProperty) {
resolvedNode.ancestorsVisible = ancestorsVisible;
}
return resolvedNode;

@@ -351,8 +359,8 @@ };

const result = [];
const { nodeTypes, resolveChildrenNodes, resolveProperties: propertyKeys } = options2;
const { nodeTypes, resolveChildren } = options2;
if (nodeTypes !== void 0) {
nodes.forEach((node) => {
if (nodeTypes.includes(node.type)) {
result.push(resolveAndSerializeNodeProperties(node, propertyKeys === "all" ? void 0 : propertyKeys));
} else if (nodeCanHaveChildren(node) && resolveChildrenNodes) {
result.push(resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible));
} else if (nodeCanHaveChildren(node) && resolveChildren) {
result.push(...resolveAndFilterNodes(node.children, options2, ancestorsVisible && node.visible));

@@ -363,4 +371,4 @@ }

nodes.forEach((node) => {
if (nodeCanHaveChildren(node) && resolveChildrenNodes) {
const newNode = __spreadProps(__spreadValues({}, resolveAndSerializeNodeProperties(node, propertyKeys === "all" ? void 0 : propertyKeys)), {
if (nodeCanHaveChildren(node) && resolveChildren) {
const newNode = __spreadProps(__spreadValues({}, resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible)), {
children: resolveAndFilterNodes(node.children, options2, ancestorsVisible && node.visible)

@@ -370,3 +378,3 @@ });

} else {
result.push(resolveAndSerializeNodeProperties(node, propertyKeys === "all" ? void 0 : propertyKeys));
result.push(resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible));
}

@@ -449,3 +457,4 @@ });

var defaultOptions = {
resolveChildrenNodes: false,
nodeTypes: void 0,
resolveChildren: false,
resolveVariables: false,

@@ -452,0 +461,0 @@ resolveProperties: "all",

{
"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/constants.ts", "../src/types.ts", "../src/utils.ts", "../src/index.ts"],
"sourcesContent": ["import { useEffect, useState } from 'react';\n\nimport useMountedEffect from './useMountedEffect';\n\nimport { api, listeners, setlisteners } from '.';\n\nimport { FigmaSelectionHookOptions, SerializedResolvedNode } from './types';\n\nexport { FigmaSelectionHookOptions } from './types';\nexport { FIGMA_MIXED } from './constants';\n\ntype FigmaSelectionReturnType = [ReadonlyArray<SerializedResolvedNode>, (selection: ReadonlyArray<SceneNode>) => void];\n\nconst defaultOptions: Required<Omit<FigmaSelectionHookOptions, 'nodeTypes' | 'apiOptions'>> = {\n resolveChildrenNodes: false,\n resolveVariables: false,\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false\n};\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 = (hookOptions?: FigmaSelectionHookOptions): FigmaSelectionReturnType => {\n const opts = { ...defaultOptions, ...hookOptions };\n\n const [selection, setSelection] = useState<ReadonlyArray<SerializedResolvedNode>>([]);\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 listeners.push(setSelection);\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));\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, 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", "/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = '57999e63-7384-42a1-acf8-d80b9f6c36a7';\n", "import { RPCOptions } from 'figma-plugin-api';\n\nimport { FIGMA_MIXED } from './constants';\n\n// For typedoc\nexport { RPCOptions } from 'figma-plugin-api';\nexport { FIGMA_MIXED } from './constants';\n\n/**\n * @internal\n */\nexport type FigmaSelectionListener = (selection: ReadonlyArray<SerializedResolvedNode>) => void;\n\n/**\n * @internal\n */\nexport type Mutable<T> = { -readonly [P in keyof T]: T[P] };\n\n/**\n * @internal\n */\nexport type SerializedNodeProperty<T> = T extends PluginAPI['mixed'] ? typeof FIGMA_MIXED : T;\n/**\n * @internal\n */\nexport type SerializedNode<T extends SceneNode> = {\n [key in keyof T]: SerializedNodeProperty<T[key]>;\n};\n\nexport type SerializedResolvedNode = SerializedNode<SceneNode> & {\n ancestorsVisible?: boolean;\n children?: readonly SerializedResolvedNode[];\n};\n\n// https://github.com/microsoft/TypeScript/issues/17002#issuecomment-1529056512\ntype ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\n/**\n * @internal\n */\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\n/**\n * @internal\n */\nexport const isStrictObject = (arg: unknown): arg is Record<string | number, unknown> => {\n return arg != undefined && typeof arg === 'object' && arg.constructor === Object;\n};\n\n/**\n * @internal\n */\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\n/**\n * @internal\n */\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n\n// eslint-disable-next-line @typescript-eslint/ban-types\ntype NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];\n\n/**\n * @internal\n */\nexport type SceneNodePropertyKey = NonFunctionPropertyNames<SceneNode>;\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?: ReadonlyArray<SceneNode['type']>;\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 resolveChildrenNodes?: boolean;\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?: ReadonlyArray<SceneNodePropertyKey> | 'all';\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;\n};\n\n/**\n * @internal\n */\nexport type ResolverOptions = Readonly<\n Pick<\n FigmaSelectionHookOptions,\n 'nodeTypes' | 'resolveChildrenNodes' | 'resolveProperties' | 'resolveVariables' | 'addAncestorsVisibleProperty'\n >\n>;\n", "import { FIGMA_MIXED } from './constants';\n\nimport {\n Mutable,\n nodeCanHaveChildren,\n strictObjectKeys,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode\n} from './types';\n\nconst defaultNodePropertyGetterFilter = <T extends SceneNode>(key: keyof T, node: T): 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 = <T extends SceneNode>(node: T, propertyKeys?: readonly SceneNodePropertyKey[]): T => {\n //console.log(node);\n const descriptors = Object.getOwnPropertyDescriptors<T>(Object.getPrototypeOf(node));\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) as (keyof T)[];\n let getters = getterKeys.filter((key: keyof T) => typeof descriptors[key].get === 'function');\n\n // type is not included in the node prototype, so we have to add it to match the type definition\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: Mutable<T> = {\n ...node\n };\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n //console.log(objectWithProperties);\n\n return objectWithProperties;\n};\n\nconst resolveAndSerializeNodeProperties = (\n object: SceneNode,\n propertyKeys?: readonly SceneNodePropertyKey[],\n ancestorsVisible: boolean = true\n): SerializedResolvedNode => {\n // The type for resolvedNode is SceneNode, but we need to assert the Serialized type to allow the mixed type conversion\n const resolvedNode = resolveNodeProperties(object, propertyKeys) as Mutable<SerializedResolvedNode>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n // @ts-expect-error for union types, keyof only gives the common property keys of the union members\n // figma.mixed is not used in the common properties so this gives a TS 2367 (no overlap) error\n // If we use a utility type to 'smoosh' the union types together, we lose the exact property types\n if (resolvedNode[key] === figma.mixed) {\n // @ts-expect-error string is not assignable to never error\n // This could possibly be avoided if the above check was also a type guard\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n // TODO: Make this optional\n resolvedNode.ancestorsVisible = ancestorsVisible;\n\n return resolvedNode;\n};\n\n// TODO: Resolve variables\n// TODO: make ancestorsVisible optional\nexport const resolveAndFilterNodes = (\n nodes: readonly SceneNode[],\n options: ResolverOptions,\n ancestorsVisible: boolean = true\n): readonly SerializedResolvedNode[] => {\n const result: SerializedResolvedNode[] = [];\n const { nodeTypes, resolveChildrenNodes, resolveProperties: propertyKeys } = options;\n\n if (nodeTypes !== undefined) {\n nodes.forEach((node) => {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, propertyKeys === 'all' ? undefined : propertyKeys));\n } else if (nodeCanHaveChildren(node) && resolveChildrenNodes) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n });\n } else {\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node) && resolveChildrenNodes) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, propertyKeys === 'all' ? undefined : propertyKeys),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n } as SerializedResolvedNode;\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, propertyKeys === 'all' ? undefined : propertyKeys));\n }\n });\n }\n\n return result;\n};\n", "import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { resolveAndFilterNodes } from './utils';\n\nimport {\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n SerializedResolvedNode,\n nodeCanHaveChildren\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: ReadonlyArray<SerializedResolvedNode>) => void;\n }\n}\n\nexport const uiApi = createUIAPI({\n _onSelectionChange(selection: ReadonlyArray<SerializedResolvedNode>) {\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(newSelection: ReadonlyArray<SceneNode>) {\n figma.currentPage.selection = newSelection;\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": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,gBAAoC;;;ACApC,mBAAkC;AAElC,IAAM,mBAAmB,CAAC,QAA8B,SAA4C;AAClG,QAAM,kBAAc,qBAAO,KAAK;AAEhC,8BAAU,MAAM;AACd,QAAI,YAAY,SAAS;AACvB,aAAO,OAAO;AAAA,IAChB;AACA,gBAAY,UAAU;AAAA,EAExB,GAAG,IAAI;AACT;AAEA,IAAO,2BAAQ;;;;;;;;;;;;;;;;;;;;;;;ACdR,IAAM,WAAN,cAAuB,MAAM;EAKlC,YAAY,MAAc,SAAiB,MAAgB;AACzD,UAAM;AALR,kBAAA,MAAA,MAAA;AACA,kBAAA,MAAA,SAAA;AACA,kBAAA,MAAA,MAAA;AAIE,SAAK,OAAO;AACZ,SAAK,UAAU;AACf,SAAK,OAAO;EACd;AACF;AAeO,IAAM,iBAAN,cAA6B,SAAS;EAC3C,YAAY,MAAgB;AAC1B,UAAM,QAAQ,mBAAmB,IAAI;EACvC;AACF;AAKO,IAAM,iBAAN,cAA6B,SAAS;EAC3C,YAAY,MAAgB;AAC1B,UAAM,QAAQ,oBAAoB,IAAI;EACxC;AACF;ACyDO,IAAM,gBAAgB,CAC3B,QAC8C;AAC9C,SAAO,OAAO,UAAU,eAAe,KAAK,KAAK,QAAQ;AAC3D;AAEO,IAAM,oBAAoB,CAC/B,QAC8B;AAC9B,SAAO,cAAc,GAAG,KAAK,CAAC,OAAO,UAAU,eAAe,KAAK,KAAK,IAAI;AAC9E;AAQO,IAAM,sBAAsB,CACjC,QACgC;AAChC,SAAO,OAAO,UAAU,eAAe,KAAK,KAAK,QAAQ;AAC3D;AAEO,IAAM,qBAAqB,CAChC,QAC4B;AAC5B,SAAO,OAAO,UAAU,eAAe,KAAK,KAAK,OAAO;AAC1D;AAEO,IAAM,gBAAgB,CAC3B,QAC0B;AAC1B,SAAO,oBAAoB,GAAG,KAAK,mBAAmB,GAAG;AAC3D;AC9GA,IAAI,UAAkB,CAAC,UAAU,QAAQ,QAAQ,KAAK,EAAE,GAAG,GAAG;AAE9D,IAAM,UAAU,IAAI,QAAmB,QAAQ,QAAQ,GAAG,GAAG;AAC7D,IAAM,WAAW,IAAI,QAAmB,QAAQ,SAAS,GAAG,GAAG;AAI/D,IAAM,UAAyC,CAAC;AAIhD,IAAM,UAAqE,CAAC;AAC5E,IAAI,WAAW;AAEf,IAAI;AAEJ,IAAM,WAAW,CAAyB,QAA0B;AAClE,MAAI;AACF,YAAQ,GAAG;EACb,SAAS,KAAK;AACZ,aAAS,GAAG;EACd;AACF;AAEA,IAAM,aAAa,CAAI,IAAY,WAAc;AAC/C,WAAS;IACP,SAAS;IACT;IACA;EACF,CAAC;AACH;AAEA,IAAM,YAAY,CAAC,IAAY,UAAoB;AACjD,WAAS;IACP,SAAS;IACT;IACA,OAAO;MACL,MAAM,MAAM;MACZ,SAAS,MAAM;MACf,MAAM,MAAM;IACd;EACF,CAAC;AACH;AAEA,IAAM,YAAY,CAAyB,SAA8C;AACvF,MAAI,CAAC,kBAAkB,IAAI,GAAG;AAC5B,QAAI,cAAc,IAAI,GAAG;AACvB,YAAM,WAAW,QAAQ,KAAK,EAAE;AAChC,UAAI,CAAC,UAAU;AACb,kBAAU,KAAK,IAAI,IAAI,eAAe,wBAAwB,KAAK,EAAE,EAAE,CAAC;AACxE;MACF;AACA,UAAI,SAAS,SAAS;AACpB,qBAAa,SAAS,OAAO;MAC/B;AACA,aAAO,QAAQ,KAAK,EAAE;AACtB,eAAS,oBAAoB,IAAI,IAAI,KAAK,SAAS,QAAW,mBAAmB,IAAI,IAAI,KAAK,QAAQ,MAAS;IACjH,OAAO;AACL,oBAAc,IAAI;IACpB;EACF,OAAO;AACL,uBAAmB,IAAI;EACzB;AACF;AAEO,IAAM,YAAY,CAAyB,SAA8C;AAC9F,MAAI;AACF,QAAI,CAAC,MAAM;AACT;IACF;AACA,cAAU,IAAI;EAChB,SAAS,KAAK;AACZ,aAAS,mBAAmB,KAAK,IAAI;EACvC;AACF;AAEA,IAAM,YAAY,CAAuB,QAAW,WAAwD;AAC1G,MAAI,CAAC,QAAQ,MAAM,GAAG;AACpB,aAAS,qBAAqB,MAAM,0BAA0B,OAAO,KAAK,OAAO,CAAC,EAAE;AACpF,UAAM,IAAI,eAAe,MAAM;EACjC;AACA,MAAI,WAAW,QAAW;AACxB,WAAO,QAAQ,MAAM,EAAE;EACzB;AACA,SAAO,QAAQ,MAAM,EAAE,GAAG,MAAM;AAClC;AAEA,IAAM,qBAAqB,CAAsB,SAA6B;AAC5E,MAAI,CAAC,KAAK,QAAQ;AAChB,YAAQ,sDAAsD,IAAI,EAAE;AACpE;EACF;AAEA,YAAU,KAAK,QAAQ,KAAK,MAAM;AACpC;AAEA,IAAM,gBAAgB,OAA4B,SAAwB;AACxE,MAAI,CAAC,KAAK,QAAQ;AAChB,YAAQ,iDAAiD,IAAI,EAAE;AAC/D,cAAU,KAAK,IAAI,IAAI,eAAe,gCAAgC,CAAC;AACvE;EACF;AAEA,MAAI;AACF,UAAM,SAAS,MAAM,UAAU,KAAK,QAAQ,KAAK,MAAM;AACvD,QAAI,WAAW,UAAa,kBAAkB,SAAS;AACrD,UAAI;AACF,cAAM;AACN,mBAAW,KAAK,IAAI,MAAM;MAC5B,SAAS,GAAG;AACV,kBAAU,KAAK,IAAI,CAAa;MAClC;IACF,OAAO;AACL,iBAAW,KAAK,IAAI,MAAM;IAC5B;EACF,SAAS,KAAK;AACZ,cAAU,KAAK,IAAI,GAAe;EACpC;AACF;AAQO,IAAM,QAAQ,CACnB,UACA,UACA,aACG;AACH,SAAO,OAAO,SAAS,QAAQ;AAC/B,YAAU;AACV,eAAa,WAAc,UAAU;AACvC;AAMO,IAAM,cAAc,CAA4C,QAAW,QAAW,cAAsB;AACjH,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,KAAK;AACX,UAAM,MAAqB,EAAE,SAAS,OAAO,QAAQ,QAAQ,GAAG;AAChE,gBAAY;AACZ,UAAM,WAA6C,CAAC,QAAQ,QAAQ;AAClE,UAAI,KAAK;AACP,cAAM,UAAU,IAAI,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,IAAI;AAC5D,eAAO,OAAO;AACd;MACF;AACA,cAAQ,MAAM;IAChB;AAGA,aAAS,UAAU,WAAW,MAAM;AAClC,aAAO,QAAQ,EAAE;AACjB,cAAQ,cAAc,EAAE,KAAK,MAAM,cAAc;AACjD,aAAO,IAAI,MAAM,WAAW,EAAE,KAAK,MAAM,cAAc,CAAC;IAC1D,GAAG,SAAS;AAEZ,YAAQ,EAAE,IAAI;AACd,aAAS,GAAG;EACd,CAAC;AACH;ACtLA,IAAM,UAAU,OAAO,UAAU;AACjC,IAAM,OAAO,OAAO,WAAW;AACxB,IAAMC,WAAU,CAAC,UAAoB,QAC1C,QAAQ,KAAK,EAAE,UAAU,UAAU,UAAU,OAAO,OAAO,SAAS,KAAK,GAAG,GAAG;AAE1E,IAAM,mBAAmB,OAAO;ACOvC,IAAM,kBAAqC;EACzC,WAAW;EACX,mBAAmB;EACnB,gBAAgB;AAClB;AAEA,IAAIC;AAIJ,IAAM,iBAAiB,CAAC,UAA8B,mBAA2B,mBAA2B;AAC1G,MAAI,OAAO,UAAU,aAAa;AAChC,UAAM,GAAG,GAAG,WAAW,CAAC,YAAY,UAAU,OAAO,CAAC;AAEtDA,eAAU,CAAC,YAAY,MAAM,GAAG,YAAY,SAAS,EAAE,QAAQ,kBAAkB,CAAC;EACpF,WAAW,OAAO,WAAW,aAAa;AACxC,gBAAY,CAAC,UAAU,UAAU,MAAM,KAAK,aAAa;AAEzD,QAAI,aAAa,QAAW;AAC1BA,iBAAU,CAAC,kBAAkB,OAAO,YAAY,EAAE,eAAe,SAAS,GAAG,cAAc;IAC7F,OAAO;AACLA,iBAAU,CAAC,kBAAkB,OAAO,YAAY,EAAE,cAAc,GAAG,cAAc;IACnF;EACF,OAAO;AAEL,YAAQ,KAAK,+FAA+F;EAC9G;AACF;AASA,IAAM,YAAY,CAChBC,UACA,UACAC,aACkC;AAClC,QAAM,OAAOC,gBAAAA,gBAAA,CAAA,GAAK,eAAA,GAAoBD,QAAA;AACtC,QAAM,EAAE,WAAW,UAAU,mBAAmB,eAAe,IAAI;AAEnE,MAAIF,aAAY,QAAW;AACzB,mBAAe,UAAU,mBAAmB,cAAc;EAC5D;AAEA,MAAI,aAAa,aAAa;AAE5B,QAAIA,aAAY,QAAW;AACzB,cAAQ,MAAM,6DAA6D;IAC7E;AAEA,UAAMC,UAASD,UAASD,QAAO;EACjC;AAEA,QAAMK,OAA2B,iBAAiBH,QAAO,EAAE,OAAO,CAAC,MAAM,MAAM;AAC7E,UAAM,SAAS,UAAU,WAAyC;AAChE,UAAI,aAAa,aAAa;AAC5B,eAAQ,MAAMA,SAAQ,CAAC,EAAE,GAAG,MAAM;MACpC;AAEA,aAAQ,MAAM,YAAY,GAAa,QAAQ,SAAS;IAC1D;AAEA,SAAK,CAAC,IAAI;AACV,WAAO;EACT,GAAG,CAAC,CAAwB;AAE5B,SAAOG;AACT;AAMO,IAAM,cAAc,CACzBH,UACAC,aACkC;AAClC,SAAO,UAAUD,UAAS,OAAO,QAAQC,QAAO;AAClD;AAMO,IAAM,kBAAkB,CAC7BD,UACAC,aACkC;AAClC,SAAO,UAAUD,UAAS,OAAO,OAAOC,QAAO;AACjD;;;ACxGO,IAAM,cAAc;;;ACoCpB,IAAM,UAAU,MAAM;AAYtB,IAAMG,oBAAmB,OAAO;AAKhC,IAAM,sBAAsB,CAAsB,SAAuC;AAC9F,SAAO,cAAc;AACvB;;;AC/CA,IAAM,kCAAkC,CAAsB,KAAc,SAAqB;AAXjG;AAYE;AAAA;AAAA,IAEE,EAAE,QAAQ,oCAAkC,UAAK,WAAL,mBAAa,UAAS;AAAA,IAElE,QAAQ,uBACR,QAAQ;AAAA;AAEZ;AAEA,IAAM,wBAAwB,CAAsB,MAAS,iBAAsD;AAEjH,QAAM,cAAc,OAAO,0BAA6B,OAAO,eAAe,IAAI,CAAC;AAInF,QAAM,aAAaC,kBAAiB,WAAW;AAC/C,MAAI,UAAU,WAAW,OAAO,CAAC,QAAiB,OAAO,YAAY,GAAG,EAAE,QAAQ,UAAU;AAG5F,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,uBAAmC,mBACpC;AAEL,aAAW,UAAU,SAAS;AAC5B,yBAAqB,MAAM,IAAI,KAAK,MAAM;AAAA,EAC5C;AAIA,SAAO;AACT;AAEA,IAAM,oCAAoC,CACxC,QACA,cACA,mBAA4B,SACD;AAE3B,QAAM,eAAe,sBAAsB,QAAQ,YAAY;AAE/D,aAAW,OAAOA,kBAAiB,YAAY,GAAG;AAIhD,QAAI,aAAa,GAAG,MAAM,MAAM,OAAO;AAGrC,mBAAa,GAAG,IAAI;AAAA,IACtB;AAAA,EACF;AAGA,eAAa,mBAAmB;AAEhC,SAAO;AACT;AAIO,IAAM,wBAAwB,CACnC,OACAC,UACA,mBAA4B,SACU;AACtC,QAAM,SAAmC,CAAC;AAC1C,QAAM,EAAE,WAAW,sBAAsB,mBAAmB,aAAa,IAAIA;AAE7E,MAAI,cAAc,QAAW;AAC3B,UAAM,QAAQ,CAAC,SAAS;AACtB,UAAI,UAAU,SAAS,KAAK,IAAI,GAAG;AACjC,eAAO,KAAK,kCAAkC,MAAM,iBAAiB,QAAQ,SAAY,YAAY,CAAC;AAAA,MACxG,WAAW,oBAAoB,IAAI,KAAK,sBAAsB;AAC5D,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,sBAAsB;AACrD,cAAM,UAAU,iCACX,kCAAkC,MAAM,iBAAiB,QAAQ,SAAY,YAAY,IAD9E;AAAA,UAEd,UAAU,sBAAsB,KAAK,UAAUA,UAAS,oBAAoB,KAAK,OAAO;AAAA,QAC1F;AACA,eAAO,KAAK,OAAO;AAAA,MACrB,OAAO;AACL,eAAO,KAAK,kCAAkC,MAAM,iBAAiB,QAAQ,SAAY,YAAY,CAAC;AAAA,MACxG;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;AC1FO,IAAM,QAAQ,YAAY;AAAA,EAC/B,mBAAmB,WAAkD;AACnE,QAAI,OAAO,OAAO,6BAA6B,aAAa;AAC1D,aAAO,yBAAyB,SAAS;AAAA,IAC3C;AAAA,EACF;AACF,CAAC;AAED,IAAM,yBAAyB,MAAM;AACnC,UAAQ,IAAI,4BAA4B,MAAM,YAAY,SAAS;AACnE,QAAM,oBAAoB,sBAAsB,MAAM,YAAY,WAAW,OAAO;AACpF,UAAQ,IAAI,uBAAuB,iBAAiB;AACpD,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;AAEO,IAAM,MAAM,gBAAgB;AAAA,EACjC,4BAA4B,MAAiC;AAC3D,YAAQ,IAAI,UAAU;AACtB,cAAU;AACV,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,cAAc,cAAwC;AACpD,UAAM,YAAY,YAAY;AAAA,EAChC;AACF,CAAC;AAED,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;;;AVvFA,IAAM,iBAAwF;AAAA,EAC5F,sBAAsB;AAAA,EACtB,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,6BAA6B;AAC/B;AAKA,IAAM,oBAAoB,CAAC,gBAAsE;AAC/F,QAAM,OAAO,kCAAK,iBAAmB;AAErC,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAgD,CAAC,CAAC;AAEpF,2BAAiB,MAAM;AACrB,YAAQ,KAAK,2FAA2F;AAAA,EAC1G,GAAG,CAAC,WAAW,CAAC;AAEhB,+BAAU,MAAM;AACd,YAAQ,IAAI,YAAY;AACxB,UAAM,QAAQ,YAAY;AACxB,gBAAU,KAAK,YAAY;AAG3B,UAAI,UAAU,WAAW,GAAG;AAC1B,YAAI;AACF,gBAAM,IAAI,4BAA4B,IAAI;AAAA,QAC5C,SAAS,GAAG;AACV,kBAAQ,MAAM,CAAC;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAEA,UAAM;AAEN,WAAO,MAAM;AACX,mBAAa,UAAU,OAAO,CAAC,MAAM,MAAM,YAAY,CAAC;AACxD,UAAI,CAAC,UAAU,QAAQ;AAErB,YAAI,8BAA8B;AAAA,MACpC;AAAA,IACF;AAAA,EAEF,GAAG,CAAC,CAAC;AAEL,SAAO,CAAC,WAAW,IAAI,aAAa;AACtC;AAEA,IAAO,eAAQ;",
"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"]
}

@@ -1,10 +0,8 @@

/// <reference types="@figma/plugin-typings" />
import { FigmaSelectionHookOptions, SerializedResolvedNode } from './types';
import { BareNode, FigmaSelectionHookOptions, SerializedResolvedNode } from './types';
export { FigmaSelectionHookOptions } from './types';
export { FIGMA_MIXED } from './constants';
type FigmaSelectionReturnType = [ReadonlyArray<SerializedResolvedNode>, (selection: ReadonlyArray<SceneNode>) => void];
/**
* Only one config will take presence and it will be the config of the first hook that is mounted
*/
declare const useFigmaSelection: (hookOptions?: FigmaSelectionHookOptions) => FigmaSelectionReturnType;
declare const useFigmaSelection: <const Options extends FigmaSelectionHookOptions>(hookOptions?: Options | undefined) => [readonly SerializedResolvedNode<Options>[], (selection: readonly BareNode[]) => void];
export default useFigmaSelection;

@@ -278,6 +278,3 @@ var __defProp = Object.defineProperty;

// src/constants.ts
var FIGMA_MIXED = "57999e63-7384-42a1-acf8-d80b9f6c36a7";
// src/types.ts
// src/typeUtils.ts
var isArray = Array.isArray;

@@ -289,2 +286,5 @@ var strictObjectKeys2 = Object.keys;

// src/constants.ts
var FIGMA_MIXED = "mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7";
// src/utils.ts

@@ -308,3 +308,5 @@ var defaultNodePropertyGetterFilter = (key, node) => {

getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));
const objectWithProperties = __spreadValues({}, node);
const objectWithProperties = {
id: node.id
};
for (const getter of getters) {

@@ -315,4 +317,8 @@ objectWithProperties[getter] = node[getter];

};
var resolveAndSerializeNodeProperties = (object, propertyKeys, ancestorsVisible = true) => {
const resolvedNode = resolveNodeProperties(object, propertyKeys);
var resolveAndSerializeNodeProperties = (object, options2, ancestorsVisible) => {
const { resolveProperties, addAncestorsVisibleProperty } = options2;
const resolvedNode = resolveNodeProperties(
object,
resolveProperties === "all" ? void 0 : resolveProperties
);
for (const key of strictObjectKeys2(resolvedNode)) {

@@ -323,3 +329,5 @@ if (resolvedNode[key] === figma.mixed) {

}
resolvedNode.ancestorsVisible = ancestorsVisible;
if (addAncestorsVisibleProperty) {
resolvedNode.ancestorsVisible = ancestorsVisible;
}
return resolvedNode;

@@ -329,8 +337,8 @@ };

const result = [];
const { nodeTypes, resolveChildrenNodes, resolveProperties: propertyKeys } = options2;
const { nodeTypes, resolveChildren } = options2;
if (nodeTypes !== void 0) {
nodes.forEach((node) => {
if (nodeTypes.includes(node.type)) {
result.push(resolveAndSerializeNodeProperties(node, propertyKeys === "all" ? void 0 : propertyKeys));
} else if (nodeCanHaveChildren(node) && resolveChildrenNodes) {
result.push(resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible));
} else if (nodeCanHaveChildren(node) && resolveChildren) {
result.push(...resolveAndFilterNodes(node.children, options2, ancestorsVisible && node.visible));

@@ -341,4 +349,4 @@ }

nodes.forEach((node) => {
if (nodeCanHaveChildren(node) && resolveChildrenNodes) {
const newNode = __spreadProps(__spreadValues({}, resolveAndSerializeNodeProperties(node, propertyKeys === "all" ? void 0 : propertyKeys)), {
if (nodeCanHaveChildren(node) && resolveChildren) {
const newNode = __spreadProps(__spreadValues({}, resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible)), {
children: resolveAndFilterNodes(node.children, options2, ancestorsVisible && node.visible)

@@ -348,3 +356,3 @@ });

} else {
result.push(resolveAndSerializeNodeProperties(node, propertyKeys === "all" ? void 0 : propertyKeys));
result.push(resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible));
}

@@ -427,3 +435,4 @@ });

var defaultOptions = {
resolveChildrenNodes: false,
nodeTypes: void 0,
resolveChildren: false,
resolveVariables: false,

@@ -430,0 +439,0 @@ resolveProperties: "all",

{
"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/constants.ts", "../src/types.ts", "../src/utils.ts", "../src/index.ts"],
"sourcesContent": ["import { useEffect, useState } from 'react';\n\nimport useMountedEffect from './useMountedEffect';\n\nimport { api, listeners, setlisteners } from '.';\n\nimport { FigmaSelectionHookOptions, SerializedResolvedNode } from './types';\n\nexport { FigmaSelectionHookOptions } from './types';\nexport { FIGMA_MIXED } from './constants';\n\ntype FigmaSelectionReturnType = [ReadonlyArray<SerializedResolvedNode>, (selection: ReadonlyArray<SceneNode>) => void];\n\nconst defaultOptions: Required<Omit<FigmaSelectionHookOptions, 'nodeTypes' | 'apiOptions'>> = {\n resolveChildrenNodes: false,\n resolveVariables: false,\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false\n};\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 = (hookOptions?: FigmaSelectionHookOptions): FigmaSelectionReturnType => {\n const opts = { ...defaultOptions, ...hookOptions };\n\n const [selection, setSelection] = useState<ReadonlyArray<SerializedResolvedNode>>([]);\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 listeners.push(setSelection);\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));\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, 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", "/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = '57999e63-7384-42a1-acf8-d80b9f6c36a7';\n", "import { RPCOptions } from 'figma-plugin-api';\n\nimport { FIGMA_MIXED } from './constants';\n\n// For typedoc\nexport { RPCOptions } from 'figma-plugin-api';\nexport { FIGMA_MIXED } from './constants';\n\n/**\n * @internal\n */\nexport type FigmaSelectionListener = (selection: ReadonlyArray<SerializedResolvedNode>) => void;\n\n/**\n * @internal\n */\nexport type Mutable<T> = { -readonly [P in keyof T]: T[P] };\n\n/**\n * @internal\n */\nexport type SerializedNodeProperty<T> = T extends PluginAPI['mixed'] ? typeof FIGMA_MIXED : T;\n/**\n * @internal\n */\nexport type SerializedNode<T extends SceneNode> = {\n [key in keyof T]: SerializedNodeProperty<T[key]>;\n};\n\nexport type SerializedResolvedNode = SerializedNode<SceneNode> & {\n ancestorsVisible?: boolean;\n children?: readonly SerializedResolvedNode[];\n};\n\n// https://github.com/microsoft/TypeScript/issues/17002#issuecomment-1529056512\ntype ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\n/**\n * @internal\n */\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\n/**\n * @internal\n */\nexport const isStrictObject = (arg: unknown): arg is Record<string | number, unknown> => {\n return arg != undefined && typeof arg === 'object' && arg.constructor === Object;\n};\n\n/**\n * @internal\n */\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\n/**\n * @internal\n */\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n\n// eslint-disable-next-line @typescript-eslint/ban-types\ntype NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];\n\n/**\n * @internal\n */\nexport type SceneNodePropertyKey = NonFunctionPropertyNames<SceneNode>;\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?: ReadonlyArray<SceneNode['type']>;\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 resolveChildrenNodes?: boolean;\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?: ReadonlyArray<SceneNodePropertyKey> | 'all';\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;\n};\n\n/**\n * @internal\n */\nexport type ResolverOptions = Readonly<\n Pick<\n FigmaSelectionHookOptions,\n 'nodeTypes' | 'resolveChildrenNodes' | 'resolveProperties' | 'resolveVariables' | 'addAncestorsVisibleProperty'\n >\n>;\n", "import { FIGMA_MIXED } from './constants';\n\nimport {\n Mutable,\n nodeCanHaveChildren,\n strictObjectKeys,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode\n} from './types';\n\nconst defaultNodePropertyGetterFilter = <T extends SceneNode>(key: keyof T, node: T): 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 = <T extends SceneNode>(node: T, propertyKeys?: readonly SceneNodePropertyKey[]): T => {\n //console.log(node);\n const descriptors = Object.getOwnPropertyDescriptors<T>(Object.getPrototypeOf(node));\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) as (keyof T)[];\n let getters = getterKeys.filter((key: keyof T) => typeof descriptors[key].get === 'function');\n\n // type is not included in the node prototype, so we have to add it to match the type definition\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: Mutable<T> = {\n ...node\n };\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n //console.log(objectWithProperties);\n\n return objectWithProperties;\n};\n\nconst resolveAndSerializeNodeProperties = (\n object: SceneNode,\n propertyKeys?: readonly SceneNodePropertyKey[],\n ancestorsVisible: boolean = true\n): SerializedResolvedNode => {\n // The type for resolvedNode is SceneNode, but we need to assert the Serialized type to allow the mixed type conversion\n const resolvedNode = resolveNodeProperties(object, propertyKeys) as Mutable<SerializedResolvedNode>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n // @ts-expect-error for union types, keyof only gives the common property keys of the union members\n // figma.mixed is not used in the common properties so this gives a TS 2367 (no overlap) error\n // If we use a utility type to 'smoosh' the union types together, we lose the exact property types\n if (resolvedNode[key] === figma.mixed) {\n // @ts-expect-error string is not assignable to never error\n // This could possibly be avoided if the above check was also a type guard\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n // TODO: Make this optional\n resolvedNode.ancestorsVisible = ancestorsVisible;\n\n return resolvedNode;\n};\n\n// TODO: Resolve variables\n// TODO: make ancestorsVisible optional\nexport const resolveAndFilterNodes = (\n nodes: readonly SceneNode[],\n options: ResolverOptions,\n ancestorsVisible: boolean = true\n): readonly SerializedResolvedNode[] => {\n const result: SerializedResolvedNode[] = [];\n const { nodeTypes, resolveChildrenNodes, resolveProperties: propertyKeys } = options;\n\n if (nodeTypes !== undefined) {\n nodes.forEach((node) => {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, propertyKeys === 'all' ? undefined : propertyKeys));\n } else if (nodeCanHaveChildren(node) && resolveChildrenNodes) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n });\n } else {\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node) && resolveChildrenNodes) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, propertyKeys === 'all' ? undefined : propertyKeys),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n } as SerializedResolvedNode;\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, propertyKeys === 'all' ? undefined : propertyKeys));\n }\n });\n }\n\n return result;\n};\n", "import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { resolveAndFilterNodes } from './utils';\n\nimport {\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n SerializedResolvedNode,\n nodeCanHaveChildren\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: ReadonlyArray<SerializedResolvedNode>) => void;\n }\n}\n\nexport const uiApi = createUIAPI({\n _onSelectionChange(selection: ReadonlyArray<SerializedResolvedNode>) {\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(newSelection: ReadonlyArray<SceneNode>) {\n figma.currentPage.selection = newSelection;\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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",
"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"]
}

@@ -289,6 +289,3 @@ "use strict";

// src/constants.ts
var FIGMA_MIXED = "57999e63-7384-42a1-acf8-d80b9f6c36a7";
// src/types.ts
// src/typeUtils.ts
var isArray = Array.isArray;

@@ -300,2 +297,5 @@ var strictObjectKeys2 = Object.keys;

// src/constants.ts
var FIGMA_MIXED = "mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7";
// src/utils.ts

@@ -319,3 +319,5 @@ var defaultNodePropertyGetterFilter = (key, node) => {

getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));
const objectWithProperties = __spreadValues({}, node);
const objectWithProperties = {
id: node.id
};
for (const getter of getters) {

@@ -326,4 +328,8 @@ objectWithProperties[getter] = node[getter];

};
var resolveAndSerializeNodeProperties = (object, propertyKeys, ancestorsVisible = true) => {
const resolvedNode = resolveNodeProperties(object, propertyKeys);
var resolveAndSerializeNodeProperties = (object, options2, ancestorsVisible) => {
const { resolveProperties, addAncestorsVisibleProperty } = options2;
const resolvedNode = resolveNodeProperties(
object,
resolveProperties === "all" ? void 0 : resolveProperties
);
for (const key of strictObjectKeys2(resolvedNode)) {

@@ -334,3 +340,5 @@ if (resolvedNode[key] === figma.mixed) {

}
resolvedNode.ancestorsVisible = ancestorsVisible;
if (addAncestorsVisibleProperty) {
resolvedNode.ancestorsVisible = ancestorsVisible;
}
return resolvedNode;

@@ -340,8 +348,8 @@ };

const result = [];
const { nodeTypes, resolveChildrenNodes, resolveProperties: propertyKeys } = options2;
const { nodeTypes, resolveChildren } = options2;
if (nodeTypes !== void 0) {
nodes.forEach((node) => {
if (nodeTypes.includes(node.type)) {
result.push(resolveAndSerializeNodeProperties(node, propertyKeys === "all" ? void 0 : propertyKeys));
} else if (nodeCanHaveChildren(node) && resolveChildrenNodes) {
result.push(resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible));
} else if (nodeCanHaveChildren(node) && resolveChildren) {
result.push(...resolveAndFilterNodes(node.children, options2, ancestorsVisible && node.visible));

@@ -352,4 +360,4 @@ }

nodes.forEach((node) => {
if (nodeCanHaveChildren(node) && resolveChildrenNodes) {
const newNode = __spreadProps(__spreadValues({}, resolveAndSerializeNodeProperties(node, propertyKeys === "all" ? void 0 : propertyKeys)), {
if (nodeCanHaveChildren(node) && resolveChildren) {
const newNode = __spreadProps(__spreadValues({}, resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible)), {
children: resolveAndFilterNodes(node.children, options2, ancestorsVisible && node.visible)

@@ -359,3 +367,3 @@ });

} else {
result.push(resolveAndSerializeNodeProperties(node, propertyKeys === "all" ? void 0 : propertyKeys));
result.push(resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible));
}

@@ -362,0 +370,0 @@ });

{
"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/constants.ts", "../src/types.ts", "../src/utils.ts"],
"sourcesContent": ["import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { resolveAndFilterNodes } from './utils';\n\nimport {\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n SerializedResolvedNode,\n nodeCanHaveChildren\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: ReadonlyArray<SerializedResolvedNode>) => void;\n }\n}\n\nexport const uiApi = createUIAPI({\n _onSelectionChange(selection: ReadonlyArray<SerializedResolvedNode>) {\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(newSelection: ReadonlyArray<SceneNode>) {\n figma.currentPage.selection = newSelection;\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", "/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = '57999e63-7384-42a1-acf8-d80b9f6c36a7';\n", "import { RPCOptions } from 'figma-plugin-api';\n\nimport { FIGMA_MIXED } from './constants';\n\n// For typedoc\nexport { RPCOptions } from 'figma-plugin-api';\nexport { FIGMA_MIXED } from './constants';\n\n/**\n * @internal\n */\nexport type FigmaSelectionListener = (selection: ReadonlyArray<SerializedResolvedNode>) => void;\n\n/**\n * @internal\n */\nexport type Mutable<T> = { -readonly [P in keyof T]: T[P] };\n\n/**\n * @internal\n */\nexport type SerializedNodeProperty<T> = T extends PluginAPI['mixed'] ? typeof FIGMA_MIXED : T;\n/**\n * @internal\n */\nexport type SerializedNode<T extends SceneNode> = {\n [key in keyof T]: SerializedNodeProperty<T[key]>;\n};\n\nexport type SerializedResolvedNode = SerializedNode<SceneNode> & {\n ancestorsVisible?: boolean;\n children?: readonly SerializedResolvedNode[];\n};\n\n// https://github.com/microsoft/TypeScript/issues/17002#issuecomment-1529056512\ntype ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\n/**\n * @internal\n */\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\n/**\n * @internal\n */\nexport const isStrictObject = (arg: unknown): arg is Record<string | number, unknown> => {\n return arg != undefined && typeof arg === 'object' && arg.constructor === Object;\n};\n\n/**\n * @internal\n */\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\n/**\n * @internal\n */\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n\n// eslint-disable-next-line @typescript-eslint/ban-types\ntype NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];\n\n/**\n * @internal\n */\nexport type SceneNodePropertyKey = NonFunctionPropertyNames<SceneNode>;\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?: ReadonlyArray<SceneNode['type']>;\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 resolveChildrenNodes?: boolean;\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?: ReadonlyArray<SceneNodePropertyKey> | 'all';\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;\n};\n\n/**\n * @internal\n */\nexport type ResolverOptions = Readonly<\n Pick<\n FigmaSelectionHookOptions,\n 'nodeTypes' | 'resolveChildrenNodes' | 'resolveProperties' | 'resolveVariables' | 'addAncestorsVisibleProperty'\n >\n>;\n", "import { FIGMA_MIXED } from './constants';\n\nimport {\n Mutable,\n nodeCanHaveChildren,\n strictObjectKeys,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode\n} from './types';\n\nconst defaultNodePropertyGetterFilter = <T extends SceneNode>(key: keyof T, node: T): 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 = <T extends SceneNode>(node: T, propertyKeys?: readonly SceneNodePropertyKey[]): T => {\n //console.log(node);\n const descriptors = Object.getOwnPropertyDescriptors<T>(Object.getPrototypeOf(node));\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) as (keyof T)[];\n let getters = getterKeys.filter((key: keyof T) => typeof descriptors[key].get === 'function');\n\n // type is not included in the node prototype, so we have to add it to match the type definition\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: Mutable<T> = {\n ...node\n };\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n //console.log(objectWithProperties);\n\n return objectWithProperties;\n};\n\nconst resolveAndSerializeNodeProperties = (\n object: SceneNode,\n propertyKeys?: readonly SceneNodePropertyKey[],\n ancestorsVisible: boolean = true\n): SerializedResolvedNode => {\n // The type for resolvedNode is SceneNode, but we need to assert the Serialized type to allow the mixed type conversion\n const resolvedNode = resolveNodeProperties(object, propertyKeys) as Mutable<SerializedResolvedNode>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n // @ts-expect-error for union types, keyof only gives the common property keys of the union members\n // figma.mixed is not used in the common properties so this gives a TS 2367 (no overlap) error\n // If we use a utility type to 'smoosh' the union types together, we lose the exact property types\n if (resolvedNode[key] === figma.mixed) {\n // @ts-expect-error string is not assignable to never error\n // This could possibly be avoided if the above check was also a type guard\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n // TODO: Make this optional\n resolvedNode.ancestorsVisible = ancestorsVisible;\n\n return resolvedNode;\n};\n\n// TODO: Resolve variables\n// TODO: make ancestorsVisible optional\nexport const resolveAndFilterNodes = (\n nodes: readonly SceneNode[],\n options: ResolverOptions,\n ancestorsVisible: boolean = true\n): readonly SerializedResolvedNode[] => {\n const result: SerializedResolvedNode[] = [];\n const { nodeTypes, resolveChildrenNodes, resolveProperties: propertyKeys } = options;\n\n if (nodeTypes !== undefined) {\n nodes.forEach((node) => {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, propertyKeys === 'all' ? undefined : propertyKeys));\n } else if (nodeCanHaveChildren(node) && resolveChildrenNodes) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n });\n } else {\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node) && resolveChildrenNodes) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, propertyKeys === 'all' ? undefined : propertyKeys),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n } as SerializedResolvedNode;\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, propertyKeys === 'all' ? undefined : propertyKeys));\n }\n });\n }\n\n return result;\n};\n"],
"mappings": "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",
"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"]
}

@@ -1,11 +0,10 @@

/// <reference types="@figma/plugin-typings" />
import { FigmaSelectionHookOptions, FigmaSelectionListener, SerializedResolvedNode } from './types';
import { BareNode, FigmaSelectionHookOptions, FigmaSelectionListener, ResolverOptions, SerializedResolvedNode } from './types';
export { FIGMA_MIXED } from './constants';
declare global {
interface Window {
_figma_onSelectionChange?: (selection: ReadonlyArray<SerializedResolvedNode>) => void;
_figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;
}
}
export declare const uiApi: Readonly<import("figma-plugin-api").RPCAPIReturnType<{
_onSelectionChange(selection: ReadonlyArray<SerializedResolvedNode>): void;
_onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]): void;
}>>;

@@ -15,5 +14,5 @@ export declare const api: Readonly<import("figma-plugin-api").RPCAPIReturnType<{

_deregisterForSelectionChange(): void;
_setSelection(newSelection: ReadonlyArray<SceneNode>): void;
_setSelection<N extends readonly BareNode[]>(newSelection: N): void;
}>>;
export declare let listeners: FigmaSelectionListener[];
export declare const setlisteners: (newListeners: FigmaSelectionListener[]) => void;

@@ -262,6 +262,3 @@ var __defProp = Object.defineProperty;

// src/constants.ts
var FIGMA_MIXED = "57999e63-7384-42a1-acf8-d80b9f6c36a7";
// src/types.ts
// src/typeUtils.ts
var isArray = Array.isArray;

@@ -273,2 +270,5 @@ var strictObjectKeys2 = Object.keys;

// src/constants.ts
var FIGMA_MIXED = "mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7";
// src/utils.ts

@@ -292,3 +292,5 @@ var defaultNodePropertyGetterFilter = (key, node) => {

getters = getters.filter((key) => defaultNodePropertyGetterFilter(key, node));
const objectWithProperties = __spreadValues({}, node);
const objectWithProperties = {
id: node.id
};
for (const getter of getters) {

@@ -299,4 +301,8 @@ objectWithProperties[getter] = node[getter];

};
var resolveAndSerializeNodeProperties = (object, propertyKeys, ancestorsVisible = true) => {
const resolvedNode = resolveNodeProperties(object, propertyKeys);
var resolveAndSerializeNodeProperties = (object, options2, ancestorsVisible) => {
const { resolveProperties, addAncestorsVisibleProperty } = options2;
const resolvedNode = resolveNodeProperties(
object,
resolveProperties === "all" ? void 0 : resolveProperties
);
for (const key of strictObjectKeys2(resolvedNode)) {

@@ -307,3 +313,5 @@ if (resolvedNode[key] === figma.mixed) {

}
resolvedNode.ancestorsVisible = ancestorsVisible;
if (addAncestorsVisibleProperty) {
resolvedNode.ancestorsVisible = ancestorsVisible;
}
return resolvedNode;

@@ -313,8 +321,8 @@ };

const result = [];
const { nodeTypes, resolveChildrenNodes, resolveProperties: propertyKeys } = options2;
const { nodeTypes, resolveChildren } = options2;
if (nodeTypes !== void 0) {
nodes.forEach((node) => {
if (nodeTypes.includes(node.type)) {
result.push(resolveAndSerializeNodeProperties(node, propertyKeys === "all" ? void 0 : propertyKeys));
} else if (nodeCanHaveChildren(node) && resolveChildrenNodes) {
result.push(resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible));
} else if (nodeCanHaveChildren(node) && resolveChildren) {
result.push(...resolveAndFilterNodes(node.children, options2, ancestorsVisible && node.visible));

@@ -325,4 +333,4 @@ }

nodes.forEach((node) => {
if (nodeCanHaveChildren(node) && resolveChildrenNodes) {
const newNode = __spreadProps(__spreadValues({}, resolveAndSerializeNodeProperties(node, propertyKeys === "all" ? void 0 : propertyKeys)), {
if (nodeCanHaveChildren(node) && resolveChildren) {
const newNode = __spreadProps(__spreadValues({}, resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible)), {
children: resolveAndFilterNodes(node.children, options2, ancestorsVisible && node.visible)

@@ -332,3 +340,3 @@ });

} else {
result.push(resolveAndSerializeNodeProperties(node, propertyKeys === "all" ? void 0 : propertyKeys));
result.push(resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible));
}

@@ -335,0 +343,0 @@ });

{
"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/constants.ts", "../src/types.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", "/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = '57999e63-7384-42a1-acf8-d80b9f6c36a7';\n", "import { RPCOptions } from 'figma-plugin-api';\n\nimport { FIGMA_MIXED } from './constants';\n\n// For typedoc\nexport { RPCOptions } from 'figma-plugin-api';\nexport { FIGMA_MIXED } from './constants';\n\n/**\n * @internal\n */\nexport type FigmaSelectionListener = (selection: ReadonlyArray<SerializedResolvedNode>) => void;\n\n/**\n * @internal\n */\nexport type Mutable<T> = { -readonly [P in keyof T]: T[P] };\n\n/**\n * @internal\n */\nexport type SerializedNodeProperty<T> = T extends PluginAPI['mixed'] ? typeof FIGMA_MIXED : T;\n/**\n * @internal\n */\nexport type SerializedNode<T extends SceneNode> = {\n [key in keyof T]: SerializedNodeProperty<T[key]>;\n};\n\nexport type SerializedResolvedNode = SerializedNode<SceneNode> & {\n ancestorsVisible?: boolean;\n children?: readonly SerializedResolvedNode[];\n};\n\n// https://github.com/microsoft/TypeScript/issues/17002#issuecomment-1529056512\ntype ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\n/**\n * @internal\n */\nexport const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;\n\n/**\n * @internal\n */\nexport const isStrictObject = (arg: unknown): arg is Record<string | number, unknown> => {\n return arg != undefined && typeof arg === 'object' && arg.constructor === Object;\n};\n\n/**\n * @internal\n */\nexport const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;\n\n/**\n * @internal\n */\nexport const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {\n return 'children' in node;\n};\n\n// eslint-disable-next-line @typescript-eslint/ban-types\ntype NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];\n\n/**\n * @internal\n */\nexport type SceneNodePropertyKey = NonFunctionPropertyNames<SceneNode>;\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?: ReadonlyArray<SceneNode['type']>;\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 resolveChildrenNodes?: boolean;\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?: ReadonlyArray<SceneNodePropertyKey> | 'all';\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;\n};\n\n/**\n * @internal\n */\nexport type ResolverOptions = Readonly<\n Pick<\n FigmaSelectionHookOptions,\n 'nodeTypes' | 'resolveChildrenNodes' | 'resolveProperties' | 'resolveVariables' | 'addAncestorsVisibleProperty'\n >\n>;\n", "import { FIGMA_MIXED } from './constants';\n\nimport {\n Mutable,\n nodeCanHaveChildren,\n strictObjectKeys,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode\n} from './types';\n\nconst defaultNodePropertyGetterFilter = <T extends SceneNode>(key: keyof T, node: T): 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 = <T extends SceneNode>(node: T, propertyKeys?: readonly SceneNodePropertyKey[]): T => {\n //console.log(node);\n const descriptors = Object.getOwnPropertyDescriptors<T>(Object.getPrototypeOf(node));\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) as (keyof T)[];\n let getters = getterKeys.filter((key: keyof T) => typeof descriptors[key].get === 'function');\n\n // type is not included in the node prototype, so we have to add it to match the type definition\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: Mutable<T> = {\n ...node\n };\n for (const getter of getters) {\n objectWithProperties[getter] = node[getter];\n }\n\n //console.log(objectWithProperties);\n\n return objectWithProperties;\n};\n\nconst resolveAndSerializeNodeProperties = (\n object: SceneNode,\n propertyKeys?: readonly SceneNodePropertyKey[],\n ancestorsVisible: boolean = true\n): SerializedResolvedNode => {\n // The type for resolvedNode is SceneNode, but we need to assert the Serialized type to allow the mixed type conversion\n const resolvedNode = resolveNodeProperties(object, propertyKeys) as Mutable<SerializedResolvedNode>;\n\n for (const key of strictObjectKeys(resolvedNode)) {\n // @ts-expect-error for union types, keyof only gives the common property keys of the union members\n // figma.mixed is not used in the common properties so this gives a TS 2367 (no overlap) error\n // If we use a utility type to 'smoosh' the union types together, we lose the exact property types\n if (resolvedNode[key] === figma.mixed) {\n // @ts-expect-error string is not assignable to never error\n // This could possibly be avoided if the above check was also a type guard\n resolvedNode[key] = FIGMA_MIXED;\n }\n }\n\n // TODO: Make this optional\n resolvedNode.ancestorsVisible = ancestorsVisible;\n\n return resolvedNode;\n};\n\n// TODO: Resolve variables\n// TODO: make ancestorsVisible optional\nexport const resolveAndFilterNodes = (\n nodes: readonly SceneNode[],\n options: ResolverOptions,\n ancestorsVisible: boolean = true\n): readonly SerializedResolvedNode[] => {\n const result: SerializedResolvedNode[] = [];\n const { nodeTypes, resolveChildrenNodes, resolveProperties: propertyKeys } = options;\n\n if (nodeTypes !== undefined) {\n nodes.forEach((node) => {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, propertyKeys === 'all' ? undefined : propertyKeys));\n } else if (nodeCanHaveChildren(node) && resolveChildrenNodes) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n });\n } else {\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node) && resolveChildrenNodes) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, propertyKeys === 'all' ? undefined : propertyKeys),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n } as SerializedResolvedNode;\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, propertyKeys === 'all' ? undefined : propertyKeys));\n }\n });\n }\n\n return result;\n};\n", "import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { resolveAndFilterNodes } from './utils';\n\nimport {\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n SerializedResolvedNode,\n nodeCanHaveChildren\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: ReadonlyArray<SerializedResolvedNode>) => void;\n }\n}\n\nexport const uiApi = createUIAPI({\n _onSelectionChange(selection: ReadonlyArray<SerializedResolvedNode>) {\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(newSelection: ReadonlyArray<SceneNode>) {\n figma.currentPage.selection = newSelection;\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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",
"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"]
}
/// <reference types="@figma/plugin-typings" />
import { RPCOptions } from 'figma-plugin-api';
import { FIGMA_MIXED } from './constants';
import { ApplicableNonFunctionPropertyKeys, ArrayElementUnion, NonFunctionPropertyKeys } from './typePrimitives';
export { RPCOptions } from 'figma-plugin-api';

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

* @internal
* Describes the properties of an unresolved node
*/
export type FigmaSelectionListener = (selection: ReadonlyArray<SerializedResolvedNode>) => void;
export type BareNode = Pick<SceneNode, 'id'>;
type SceneNodeType = SceneNode['type'];
/**
* @internal
*/
export type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};
export type SceneNodeFromTypes<T extends readonly SceneNodeType[] | undefined = undefined> = T extends undefined ? SceneNode : ExtractedSceneNode<ArrayElementUnion<T>>;
/**
* @internal
* Utility type to get only matching node types from the SceneNode union type.
*/
export type SerializedNodeProperty<T> = T extends PluginAPI['mixed'] ? typeof FIGMA_MIXED : T;
type ExtractedSceneNode<T extends SceneNodeType> = Extract<SceneNode, {
type: T;
}>;
/**
* @internal
* Get the non-function property keys for a SceneNode
*/
export type SerializedNode<T extends SceneNode> = {
[key in keyof T]: SerializedNodeProperty<T[key]>;
};
export type SerializedResolvedNode = SerializedNode<SceneNode> & {
ancestorsVisible?: boolean;
children?: readonly SerializedResolvedNode[];
};
export type SceneNodePropertyKey<T extends SceneNodeType | undefined = undefined> = NonFunctionPropertyKeys<T extends SceneNodeType ? ExtractedSceneNode<T> : SceneNode>;
export type OptSceneNodeProperties = readonly SceneNodePropertyKey[] | 'all';
/**
* @internal
* Use `satisfies` (for TS >= 4.9) with this type to allow for type checking the options object
* while the type of the object remains exact.
*
* This allows us to infer the type of the returned nodes correctly.
*
* Example:
* ```typescript
* const options = {
* nodeTypes: ['TEXT', 'FRAME'],
* resolveProperties: ['name', 'characters', 'children]
* } satisfies FigmaSelectionHookOptions;
* ```
*/
export declare const isArray: <T>(arg: T) => arg is Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;
/**
* @internal
*/
export declare const isStrictObject: (arg: unknown) => arg is Record<string | number, unknown>;
/**
* @internal
*/
export declare const strictObjectKeys: <T extends object>(obj: T) => (keyof T)[];
/**
* @internal
*/
export declare const nodeCanHaveChildren: <T extends SceneNode>(node: T) => node is T & ChildrenMixin;
type NonFunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];
/**
* @internal
*/
export type SceneNodePropertyKey = NonFunctionPropertyNames<SceneNode>;
export type FigmaSelectionHookOptions = {

@@ -62,12 +53,4 @@ /**

*/
nodeTypes?: ReadonlyArray<SceneNode['type']>;
nodeTypes?: readonly SceneNodeType[] | undefined;
/**
* 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`
*/
resolveChildrenNodes?: boolean;
/**
* Figma node properties are lazy-loaded, so to use any property you have to resolve it first.

@@ -83,4 +66,12 @@ *

*/
resolveProperties?: ReadonlyArray<SceneNodePropertyKey> | 'all';
resolveProperties?: OptSceneNodeProperties;
/**
* 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`
*/
resolveChildren?: boolean;
/**
* Resolve bound variables of the selection.

@@ -104,3 +95,3 @@ *

*/
apiOptions?: RPCOptions;
apiOptions?: RPCOptions | undefined;
};

@@ -110,2 +101,42 @@ /**

*/
export type ResolverOptions = Readonly<Pick<FigmaSelectionHookOptions, 'nodeTypes' | 'resolveChildrenNodes' | 'resolveProperties' | 'resolveVariables' | 'addAncestorsVisibleProperty'>>;
export type ResolverOptions = Omit<FigmaSelectionHookOptions, 'apiOptions'>;
type SerializedNodeProperty<Prop, Node extends SceneNode, Options extends ResolverOptions> = Prop extends PluginAPI['mixed'] ? typeof FIGMA_MIXED : Prop extends readonly SceneNode[] ? Options['resolveChildren'] extends true ? readonly SerializedNode<Node, Options>[] : readonly BareNode[] : Prop;
type ApplicableNodeKeys<Node extends SceneNode, Properties extends OptSceneNodeProperties> = Properties extends readonly SceneNodePropertyKey[] ? ApplicableNonFunctionPropertyKeys<Node, ArrayElementUnion<Properties>> : NonFunctionPropertyKeys<Node>;
/**
* @internal
*/
export type SerializedNode<Node extends SceneNode, Options extends ResolverOptions> = Node extends SceneNode ? {
type: Node['type'];
id: Node['id'];
} & {
[Key in Options['resolveProperties'] extends SceneNodePropertyKey[] ? ApplicableNodeKeys<Node, Options['resolveProperties']> : keyof Node]: SerializedNodeProperty<Node[Key], Node, Options>;
} : never;
/**
* @internal
*/
export type ResolvedNode<Node extends SceneNode, Keys extends readonly SceneNodePropertyKey[] | undefined = undefined> = Pick<Node, Keys extends readonly SceneNodePropertyKey[] ? ApplicableNonFunctionPropertyKeys<Node, ArrayElementUnion<Keys>> | 'id' | 'type' : ApplicableNonFunctionPropertyKeys<Node, SceneNodePropertyKey>>;
/**
* @internal
*/
type SerializedResolvedNodeBase<Node extends SceneNode, Options extends ResolverOptions> = Node extends SceneNode ? SerializedNode<Node, Options> : never;
/**
* @internal
*/
type AncestorsVisibleMixin = {
ancestorsVisible: boolean;
};
/**
* @internal
*/
type ResolveVariablesMixin = {
boundVariableInstances: readonly Variable[];
};
/**
* @internal
* All Figma nodes are converted to this type for serialization and sending to the plugin UI
*/
export type SerializedResolvedNode<Options extends ResolverOptions> = Options['addAncestorsVisibleProperty'] extends true ? Options['resolveVariables'] extends true ? SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options> & AncestorsVisibleMixin & ResolveVariablesMixin : SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options> & AncestorsVisibleMixin : SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options>;
/**
* @internal
*/
export type FigmaSelectionListener = (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;
/// <reference types="@figma/plugin-typings" />
import { ResolverOptions, SerializedResolvedNode } from './types';
export declare const resolveAndFilterNodes: (nodes: readonly SceneNode[], options: ResolverOptions, ancestorsVisible?: boolean) => readonly SerializedResolvedNode[];
export declare const resolveAndFilterNodes: <const Options extends ResolverOptions>(nodes: readonly SceneNode[], options: Options, ancestorsVisible?: boolean) => SerializedResolvedNode<Options>[];
{
"name": "figma-plugin-react-hooks",
"version": "1.0.0",
"version": "1.1.0",
"description": "",

@@ -43,2 +43,3 @@ "exports": {

"build:docs": "typedoc && cp docs/index.md docs-temp && node ./build-docs.mjs",
"test:types": "tsd --typings src/types.ts --files tests/types.test.ts",
"prepublishOnly": "npm run build"

@@ -69,2 +70,3 @@ },

"prettier": "^3.2.4",
"tsd": "^0.30.4",
"typedoc": "^0.25.7",

@@ -71,0 +73,0 @@ "typedoc-plugin-markdown": "^3.17.1",

@@ -99,5 +99,5 @@ # figma-plugin-react-hooks

### SerializedResolvedNode
### OptSceneNodeProperties
Ƭ **SerializedResolvedNode**: `SerializedNode`\<`SceneNode`\> & \{ `ancestorsVisible?`: `boolean` ; `children?`: readonly [`SerializedResolvedNode`](types.md#serializedresolvednode)[] }
Ƭ **OptSceneNodeProperties**: readonly `SceneNodePropertyKey`[] \| ``"all"``

@@ -110,2 +110,15 @@ ___

Use `satisfies` (for TS >= 4.9) with this type to allow for type checking the options object
while the type of the object remains exact.
This allows us to infer the type of the returned nodes correctly.
Example:
```typescript
const options = {
nodeTypes: ['TEXT', 'FRAME'],
resolveProperties: ['name', 'characters', 'children]
} satisfies FigmaSelectionHookOptions;
```
#### Type declaration

@@ -115,5 +128,5 @@

| :------ | :------ | :------ |
| `nodeTypes?` | `ReadonlyArray`\<`SceneNode`[``"type"``]\> | Only return specific types of nodes. If left undefined, all nodes in the selection will be returned. Default: `undefined` |
| `resolveChildrenNodes?` | `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` |
| `resolveProperties?` | `ReadonlyArray`\<`SceneNodePropertyKey`\> \| ``"all"`` | 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` |
| `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` |
| `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` |
| `resolveVariables?` | `boolean` | Resolve bound variables of the selection. Default: `false` |

@@ -127,4 +140,4 @@ | `addAncestorsVisibleProperty?` | `boolean` | Add `ancestorsVisible` property to all nodes. This property is true if all ancestors of the node are visible. Default: `false` |

• `Const` **FIGMA\_MIXED**: ``"57999e63-7384-42a1-acf8-d80b9f6c36a7"``
• `Const` **FIGMA\_MIXED**: ``"mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7"``
Used to replace `figma.mixed` during JSON serialization
/**
* Used to replace `figma.mixed` during JSON serialization
*/
export const FIGMA_MIXED = '57999e63-7384-42a1-acf8-d80b9f6c36a7';
export const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';

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

/* eslint-disable react-hooks/rules-of-hooks */
import { useEffect, useState } from 'react';

@@ -7,3 +9,9 @@

import { FigmaSelectionHookOptions, SerializedResolvedNode } from './types';
import {
BareNode,
FigmaSelectionHookOptions,
FigmaSelectionListener,
ResolverOptions,
SerializedResolvedNode
} from './types';

@@ -13,10 +21,9 @@ export { FigmaSelectionHookOptions } from './types';

type FigmaSelectionReturnType = [ReadonlyArray<SerializedResolvedNode>, (selection: ReadonlyArray<SceneNode>) => void];
const defaultOptions: Required<Omit<FigmaSelectionHookOptions, 'nodeTypes' | 'apiOptions'>> = {
resolveChildrenNodes: false,
const defaultOptions = {
nodeTypes: undefined,
resolveChildren: false,
resolveVariables: false,
resolveProperties: 'all',
addAncestorsVisibleProperty: false
};
} as const satisfies ResolverOptions;

@@ -26,6 +33,8 @@ /**

*/
const useFigmaSelection = (hookOptions?: FigmaSelectionHookOptions): FigmaSelectionReturnType => {
const opts = { ...defaultOptions, ...hookOptions };
const useFigmaSelection = <const Options extends FigmaSelectionHookOptions>(
hookOptions?: Options
): [readonly SerializedResolvedNode<Options>[], (selection: readonly BareNode[]) => void] => {
const opts = { ...defaultOptions, ...hookOptions } as const;
const [selection, setSelection] = useState<ReadonlyArray<SerializedResolvedNode>>([]);
const [selection, setSelection] = useState<readonly SerializedResolvedNode<Options>[]>([]);

@@ -39,3 +48,4 @@ useMountedEffect(() => {

const mount = async () => {
listeners.push(setSelection);
// Typing the listeners explicitly is difficult due to the architecture, so we have to assert
listeners.push(setSelection as unknown as FigmaSelectionListener);

@@ -55,3 +65,3 @@ // if it's the first listener, register for selection change

return () => {
setlisteners(listeners.filter((l) => l !== setSelection));
setlisteners(listeners.filter((l) => l !== (setSelection as unknown as FigmaSelectionListener)));
if (!listeners.length) {

@@ -65,5 +75,5 @@ // if it was the last listener, then we don't have to listen to selection change anymore

return [selection, api._setSelection];
return [selection as readonly SerializedResolvedNode<Options>[], api._setSelection];
};
export default useFigmaSelection;
import { createUIAPI, createPluginAPI } from 'figma-plugin-api';
import { nodeCanHaveChildren } from './typeUtils';
import { resolveAndFilterNodes } from './utils';
import {
BareNode,
FigmaSelectionHookOptions,
FigmaSelectionListener,
SerializedResolvedNode,
nodeCanHaveChildren
ResolverOptions,
SerializedResolvedNode
} from './types';

@@ -16,3 +18,3 @@

interface Window {
_figma_onSelectionChange?: (selection: ReadonlyArray<SerializedResolvedNode>) => void;
_figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;
}

@@ -22,3 +24,3 @@ }

export const uiApi = createUIAPI({
_onSelectionChange(selection: ReadonlyArray<SerializedResolvedNode>) {
_onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {
if (typeof window._figma_onSelectionChange !== 'undefined') {

@@ -83,4 +85,4 @@ window._figma_onSelectionChange(selection);

},
_setSelection(newSelection: ReadonlyArray<SceneNode>) {
figma.currentPage.selection = newSelection;
_setSelection<N extends readonly BareNode[]>(newSelection: N) {
figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];
}

@@ -87,0 +89,0 @@ });

import { RPCOptions } from 'figma-plugin-api';
import { FIGMA_MIXED } from './constants';
import { ApplicableNonFunctionPropertyKeys, ArrayElementUnion, NonFunctionPropertyKeys } from './typePrimitives';

@@ -11,9 +12,7 @@ // For typedoc

* @internal
* Describes the properties of an unresolved node
*/
export type FigmaSelectionListener = (selection: ReadonlyArray<SerializedResolvedNode>) => void;
export type BareNode = Pick<SceneNode, 'id'>;
/**
* @internal
*/
export type Mutable<T> = { -readonly [P in keyof T]: T[P] };
type SceneNodeType = SceneNode['type'];

@@ -23,49 +22,37 @@ /**

*/
export type SerializedNodeProperty<T> = T extends PluginAPI['mixed'] ? typeof FIGMA_MIXED : T;
/**
* @internal
*/
export type SerializedNode<T extends SceneNode> = {
[key in keyof T]: SerializedNodeProperty<T[key]>;
};
export type SceneNodeFromTypes<T extends readonly SceneNodeType[] | undefined = undefined> = T extends undefined
? SceneNode
: // @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.
ExtractedSceneNode<ArrayElementUnion<T>>;
export type SerializedResolvedNode = SerializedNode<SceneNode> & {
ancestorsVisible?: boolean;
children?: readonly SerializedResolvedNode[];
};
// https://github.com/microsoft/TypeScript/issues/17002#issuecomment-1529056512
type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;
/**
* @internal
* Utility type to get only matching node types from the SceneNode union type.
*/
export const isArray = Array.isArray as <T>(arg: T) => arg is ArrayType<T>;
type ExtractedSceneNode<T extends SceneNodeType> = Extract<SceneNode, { type: T }>;
/**
* @internal
* Get the non-function property keys for a SceneNode
*/
export const isStrictObject = (arg: unknown): arg is Record<string | number, unknown> => {
return arg != undefined && typeof arg === 'object' && arg.constructor === Object;
};
export type SceneNodePropertyKey<T extends SceneNodeType | undefined = undefined> = NonFunctionPropertyKeys<
T extends SceneNodeType ? ExtractedSceneNode<T> : SceneNode
>;
/**
* @internal
*/
export const strictObjectKeys = Object.keys as <T extends object>(obj: T) => Array<keyof T>;
export type OptSceneNodeProperties = readonly SceneNodePropertyKey[] | 'all';
/**
* @internal
* Use `satisfies` (for TS >= 4.9) with this type to allow for type checking the options object
* while the type of the object remains exact.
*
* This allows us to infer the type of the returned nodes correctly.
*
* Example:
* ```typescript
* const options = {
* nodeTypes: ['TEXT', 'FRAME'],
* resolveProperties: ['name', 'characters', 'children]
* } satisfies FigmaSelectionHookOptions;
* ```
*/
export const nodeCanHaveChildren = <T extends SceneNode>(node: T): node is T & ChildrenMixin => {
return 'children' in node;
};
// eslint-disable-next-line @typescript-eslint/ban-types
type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];
/**
* @internal
*/
export type SceneNodePropertyKey = NonFunctionPropertyNames<SceneNode>;
export type FigmaSelectionHookOptions = {

@@ -79,12 +66,4 @@ /**

*/
nodeTypes?: ReadonlyArray<SceneNode['type']>;
nodeTypes?: readonly SceneNodeType[] | undefined;
/**
* 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`
*/
resolveChildrenNodes?: boolean;
/**
* Figma node properties are lazy-loaded, so to use any property you have to resolve it first.

@@ -100,4 +79,12 @@ *

*/
resolveProperties?: ReadonlyArray<SceneNodePropertyKey> | 'all';
resolveProperties?: OptSceneNodeProperties;
/**
* 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`
*/
resolveChildren?: boolean;
/**
* Resolve bound variables of the selection.

@@ -121,3 +108,3 @@ *

*/
apiOptions?: RPCOptions;
apiOptions?: RPCOptions | undefined;
};

@@ -128,7 +115,87 @@

*/
export type ResolverOptions = Readonly<
Pick<
FigmaSelectionHookOptions,
'nodeTypes' | 'resolveChildrenNodes' | 'resolveProperties' | 'resolveVariables' | 'addAncestorsVisibleProperty'
>
export type ResolverOptions = Omit<FigmaSelectionHookOptions, 'apiOptions'>;
type SerializedNodeProperty<
Prop,
Node extends SceneNode,
Options extends ResolverOptions
> = Prop extends PluginAPI['mixed']
? typeof FIGMA_MIXED
: Prop extends readonly SceneNode[]
? Options['resolveChildren'] extends true
? readonly SerializedNode<Node, Options>[]
: readonly BareNode[]
: Prop;
type ApplicableNodeKeys<
Node extends SceneNode,
Properties extends OptSceneNodeProperties
> = Properties extends readonly SceneNodePropertyKey[]
? ApplicableNonFunctionPropertyKeys<Node, ArrayElementUnion<Properties>>
: NonFunctionPropertyKeys<Node>;
/**
* @internal
*/
export type SerializedNode<Node extends SceneNode, Options extends ResolverOptions> = Node extends SceneNode
? {
type: Node['type'];
id: Node['id'];
} & {
[Key in Options['resolveProperties'] extends SceneNodePropertyKey[]
? ApplicableNodeKeys<Node, Options['resolveProperties']>
: keyof Node]: SerializedNodeProperty<Node[Key], Node, Options>;
}
: never;
/**
* @internal
*/
export type ResolvedNode<
Node extends SceneNode,
Keys extends readonly SceneNodePropertyKey[] | undefined = undefined
> = Pick<
Node,
Keys extends readonly SceneNodePropertyKey[]
? ApplicableNonFunctionPropertyKeys<Node, ArrayElementUnion<Keys>> | 'id' | 'type'
: ApplicableNonFunctionPropertyKeys<Node, SceneNodePropertyKey>
>;
/**
* @internal
*/
type SerializedResolvedNodeBase<Node extends SceneNode, Options extends ResolverOptions> = Node extends SceneNode
? SerializedNode<Node, Options>
: never;
/**
* @internal
*/
type AncestorsVisibleMixin = {
ancestorsVisible: boolean;
};
/**
* @internal
*/
type ResolveVariablesMixin = {
boundVariableInstances: readonly Variable[];
};
/**
* @internal
* All Figma nodes are converted to this type for serialization and sending to the plugin UI
*/
export type SerializedResolvedNode<Options extends ResolverOptions> =
Options['addAncestorsVisibleProperty'] extends true
? Options['resolveVariables'] extends true
? SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options> &
AncestorsVisibleMixin &
ResolveVariablesMixin
: SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options> & AncestorsVisibleMixin
: SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options>;
/**
* @internal
*/
export type FigmaSelectionListener = (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;
import { FIGMA_MIXED } from './constants';
import { nodeCanHaveChildren, strictObjectKeys } from './typeUtils';
import {
Mutable,
nodeCanHaveChildren,
strictObjectKeys,
ResolverOptions,
SceneNodePropertyKey,
SerializedResolvedNode
} from './types';
import { ResolvedNode, ResolverOptions, SceneNodePropertyKey, SerializedResolvedNode } from './types';
const defaultNodePropertyGetterFilter = <T extends SceneNode>(key: keyof T, node: T): boolean => {
const defaultNodePropertyGetterFilter = <Node extends SceneNode>(key: keyof Node, node: Node): boolean => {
return (

@@ -22,12 +16,17 @@ // Can only get component property definitions of a component set or non-variant component

const resolveNodeProperties = <T extends SceneNode>(node: T, propertyKeys?: readonly SceneNodePropertyKey[]): T => {
//console.log(node);
const descriptors = Object.getOwnPropertyDescriptors<T>(Object.getPrototypeOf(node));
const resolveNodeProperties = <Node extends SceneNode, const Keys extends readonly SceneNodePropertyKey[]>(
node: Node,
propertyKeys?: Keys
): ResolvedNode<Node, Keys> => {
// Narrow the type from the default which has a mapped type
const descriptors = Object.getOwnPropertyDescriptors<Node>(Object.getPrototypeOf(node)) as {
[P in keyof Node]: TypedPropertyDescriptor<Node[P]>;
};
// In reality the type is string | number | keyof T from getOwnPropertyDescriptors
// but we can safely assert a narrower type
const getterKeys = strictObjectKeys(descriptors) as (keyof T)[];
let getters = getterKeys.filter((key: keyof T) => typeof descriptors[key].get === 'function');
const getterKeys = strictObjectKeys(descriptors);
let getters = getterKeys.filter((key) => typeof descriptors[key].get === 'function');
// type is not included in the node prototype, so we have to add it to match the type definition
// type has to be included manually as it doesn't have a getter but isn't a static property either
getters.push('type');

@@ -41,5 +40,6 @@

const objectWithProperties: Mutable<T> = {
...node
};
const objectWithProperties = {
id: node.id
} as Node;
for (const getter of getters) {

@@ -49,22 +49,19 @@ objectWithProperties[getter] = node[getter];

//console.log(objectWithProperties);
return objectWithProperties;
};
const resolveAndSerializeNodeProperties = (
object: SceneNode,
propertyKeys?: readonly SceneNodePropertyKey[],
ancestorsVisible: boolean = true
): SerializedResolvedNode => {
// The type for resolvedNode is SceneNode, but we need to assert the Serialized type to allow the mixed type conversion
const resolvedNode = resolveNodeProperties(object, propertyKeys) as Mutable<SerializedResolvedNode>;
const resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(
object: Node,
options: Options,
ancestorsVisible: boolean
): SerializedResolvedNode<Options> => {
const { resolveProperties, addAncestorsVisibleProperty } = options;
const resolvedNode = resolveNodeProperties(
object,
resolveProperties === 'all' ? undefined : resolveProperties
) as Record<string, unknown>;
for (const key of strictObjectKeys(resolvedNode)) {
// @ts-expect-error for union types, keyof only gives the common property keys of the union members
// figma.mixed is not used in the common properties so this gives a TS 2367 (no overlap) error
// If we use a utility type to 'smoosh' the union types together, we lose the exact property types
if (resolvedNode[key] === figma.mixed) {
// @ts-expect-error string is not assignable to never error
// This could possibly be avoided if the above check was also a type guard
resolvedNode[key] = FIGMA_MIXED;

@@ -74,17 +71,17 @@ }

// TODO: Make this optional
resolvedNode.ancestorsVisible = ancestorsVisible;
if (addAncestorsVisibleProperty) {
resolvedNode.ancestorsVisible = ancestorsVisible;
}
return resolvedNode;
return resolvedNode as SerializedResolvedNode<Options>;
};
// TODO: Resolve variables
// TODO: make ancestorsVisible optional
export const resolveAndFilterNodes = (
export const resolveAndFilterNodes = <const Options extends ResolverOptions>(
nodes: readonly SceneNode[],
options: ResolverOptions,
options: Options,
ancestorsVisible: boolean = true
): readonly SerializedResolvedNode[] => {
const result: SerializedResolvedNode[] = [];
const { nodeTypes, resolveChildrenNodes, resolveProperties: propertyKeys } = options;
): SerializedResolvedNode<Options>[] => {
const result: SerializedResolvedNode<Options>[] = [];
const { nodeTypes, resolveChildren } = options;

@@ -94,4 +91,4 @@ if (nodeTypes !== undefined) {

if (nodeTypes.includes(node.type)) {
result.push(resolveAndSerializeNodeProperties(node, propertyKeys === 'all' ? undefined : propertyKeys));
} else if (nodeCanHaveChildren(node) && resolveChildrenNodes) {
result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));
} else if (nodeCanHaveChildren(node) && resolveChildren) {
result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));

@@ -102,10 +99,10 @@ }

nodes.forEach((node) => {
if (nodeCanHaveChildren(node) && resolveChildrenNodes) {
if (nodeCanHaveChildren(node) && resolveChildren) {
const newNode = {
...resolveAndSerializeNodeProperties(node, propertyKeys === 'all' ? undefined : propertyKeys),
...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),
children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)
} as SerializedResolvedNode;
};
result.push(newNode);
} else {
result.push(resolveAndSerializeNodeProperties(node, propertyKeys === 'all' ? undefined : propertyKeys));
result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));
}

@@ -112,0 +109,0 @@ });