figma-plugin-react-hooks
Advanced tools
| /** | ||
| * @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; | ||
| }; |
| /** | ||
| * 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"; |
+24
-15
@@ -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", |
+3
-3
| { | ||
| "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": ["import_react", "logBase", "sendRaw", "methods", "options", "__spreadValues", "api", "strictObjectKeys", "strictObjectKeys", "options"] | ||
| } |
+2
-4
@@ -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; |
+24
-15
@@ -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", |
+3
-3
| { | ||
| "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": ";;;;;;;;;;;;;;;;;;;;;AAEA,SAAS,aAAAA,YAAW,gBAAgB;;;ACFpC,SAAS,WAAW,cAAc;AAElC,IAAM,mBAAmB,CAAC,QAA8B,SAA4C;AAClG,QAAM,cAAc,OAAO,KAAK;AAEhC,YAAU,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;;;ACzGO,IAAM,UAAU,MAAM;AAEtB,IAAMG,oBAAmB,OAAO;AAEhC,IAAM,sBAAsB,CAAsB,SAAuC;AAC9F,SAAO,cAAc;AACvB;;;ACLO,IAAM,cAAc;;;ACE3B,IAAM,kCAAkC,CAAyB,KAAiB,SAAwB;AAL1G;AAME;AAAA;AAAA,IAEE,EAAE,QAAQ,oCAAkC,UAAK,WAAL,mBAAa,UAAS;AAAA,IAElE,QAAQ,uBACR,QAAQ;AAAA;AAEZ;AAEA,IAAM,wBAAwB,CAC5B,MACA,iBAC6B;AAE7B,QAAM,cAAc,OAAO,0BAAgC,OAAO,eAAe,IAAI,CAAC;AAMtF,QAAM,aAAaC,kBAAiB,WAAW;AAC/C,MAAI,UAAU,WAAW,OAAO,CAAC,QAAQ,OAAO,YAAY,GAAG,EAAE,QAAQ,UAAU;AAGnF,UAAQ,KAAK,MAAM;AAEnB,MAAI,cAAc;AAChB,cAAU,QAAQ,OAAO,CAAC,QAAQ,aAAa,SAAS,GAA2B,CAAC;AAAA,EACtF;AAEA,YAAU,QAAQ,OAAO,CAAC,QAAQ,gCAAgC,KAAK,IAAI,CAAC;AAE5E,QAAM,uBAAuB;AAAA,IAC3B,IAAI,KAAK;AAAA,EACX;AAEA,aAAW,UAAU,SAAS;AAC5B,yBAAqB,MAAM,IAAI,KAAK,MAAM;AAAA,EAC5C;AAEA,SAAO;AACT;AAEA,IAAM,oCAAoC,CACxC,QACAC,UACA,qBACoC;AACpC,QAAM,EAAE,mBAAmB,4BAA4B,IAAIA;AAE3D,QAAM,eAAe;AAAA,IACnB;AAAA,IACA,sBAAsB,QAAQ,SAAY;AAAA,EAC5C;AAEA,aAAW,OAAOD,kBAAiB,YAAY,GAAG;AAChD,QAAI,aAAa,GAAG,MAAM,MAAM,OAAO;AACrC,mBAAa,GAAG,IAAI;AAAA,IACtB;AAAA,EACF;AAEA,MAAI,6BAA6B;AAC/B,iBAAa,mBAAmB;AAAA,EAClC;AAEA,SAAO;AACT;AAGO,IAAM,wBAAwB,CACnC,OACAC,UACA,mBAA4B,SACU;AACtC,QAAM,SAA4C,CAAC;AACnD,QAAM,EAAE,WAAW,gBAAgB,IAAIA;AAEvC,MAAI,cAAc,QAAW;AAC3B,UAAM,QAAQ,CAAC,SAAS;AACtB,UAAI,UAAU,SAAS,KAAK,IAAI,GAAG;AACjC,eAAO,KAAK,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG,WAAW,oBAAoB,IAAI,KAAK,iBAAiB;AACvD,eAAO,KAAK,GAAG,sBAAsB,KAAK,UAAUA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG;AAAA,IACF,CAAC;AAAA,EACH,OAAO;AACL,UAAM,QAAQ,CAAC,SAAS;AACtB,UAAI,oBAAoB,IAAI,KAAK,iBAAiB;AAChD,cAAM,UAAU,iCACX,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,IADtE;AAAA,UAEd,UAAU,sBAAsB,KAAK,UAAUA,UAAS,oBAAoB,KAAK,OAAO;AAAA,QAC1F;AACA,eAAO,KAAK,OAAO;AAAA,MACrB,OAAO;AACL,eAAO,KAAK,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;ACrFO,IAAM,QAAQ,YAAY;AAAA,EAC/B,mBAAmB,WAA+D;AAChF,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,cAA6C,cAAiB;AAC5D,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;;;AVnFA,IAAM,iBAAiB;AAAA,EACrB,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,6BAA6B;AAC/B;AAKA,IAAM,oBAAoB,CACxB,gBAC2F;AAC3F,QAAM,OAAO,kCAAK,iBAAmB;AAErC,QAAM,CAAC,WAAW,YAAY,IAAI,SAAqD,CAAC,CAAC;AAEzF,2BAAiB,MAAM;AACrB,YAAQ,KAAK,2FAA2F;AAAA,EAC1G,GAAG,CAAC,WAAW,CAAC;AAEhB,EAAAC,WAAU,MAAM;AACd,YAAQ,IAAI,YAAY;AACxB,UAAM,QAAQ,YAAY;AAExB,gBAAU,KAAK,YAAiD;AAGhE,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,MAAO,YAAkD,CAAC;AAC/F,UAAI,CAAC,UAAU,QAAQ;AAErB,YAAI,8BAA8B;AAAA,MACpC;AAAA,IACF;AAAA,EAEF,GAAG,CAAC,CAAC;AAEL,SAAO,CAAC,WAAyD,IAAI,aAAa;AACpF;AAEA,IAAO,eAAQ;", | ||
| "names": ["useEffect", "logBase", "sendRaw", "methods", "options", "__spreadValues", "api", "strictObjectKeys", "strictObjectKeys", "options", "useEffect"] | ||
| } |
+22
-14
@@ -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"] | ||
| } |
+4
-5
@@ -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; |
+22
-14
@@ -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": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAO,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,IAAMA,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;", | ||
| "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": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAO,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,IAAMA,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;;;ACzGO,IAAM,UAAU,MAAM;AAEtB,IAAMG,oBAAmB,OAAO;AAEhC,IAAM,sBAAsB,CAAsB,SAAuC;AAC9F,SAAO,cAAc;AACvB;;;ACLO,IAAM,cAAc;;;ACE3B,IAAM,kCAAkC,CAAyB,KAAiB,SAAwB;AAL1G;AAME;AAAA;AAAA,IAEE,EAAE,QAAQ,oCAAkC,UAAK,WAAL,mBAAa,UAAS;AAAA,IAElE,QAAQ,uBACR,QAAQ;AAAA;AAEZ;AAEA,IAAM,wBAAwB,CAC5B,MACA,iBAC6B;AAE7B,QAAM,cAAc,OAAO,0BAAgC,OAAO,eAAe,IAAI,CAAC;AAMtF,QAAM,aAAaC,kBAAiB,WAAW;AAC/C,MAAI,UAAU,WAAW,OAAO,CAAC,QAAQ,OAAO,YAAY,GAAG,EAAE,QAAQ,UAAU;AAGnF,UAAQ,KAAK,MAAM;AAEnB,MAAI,cAAc;AAChB,cAAU,QAAQ,OAAO,CAAC,QAAQ,aAAa,SAAS,GAA2B,CAAC;AAAA,EACtF;AAEA,YAAU,QAAQ,OAAO,CAAC,QAAQ,gCAAgC,KAAK,IAAI,CAAC;AAE5E,QAAM,uBAAuB;AAAA,IAC3B,IAAI,KAAK;AAAA,EACX;AAEA,aAAW,UAAU,SAAS;AAC5B,yBAAqB,MAAM,IAAI,KAAK,MAAM;AAAA,EAC5C;AAEA,SAAO;AACT;AAEA,IAAM,oCAAoC,CACxC,QACAC,UACA,qBACoC;AACpC,QAAM,EAAE,mBAAmB,4BAA4B,IAAIA;AAE3D,QAAM,eAAe;AAAA,IACnB;AAAA,IACA,sBAAsB,QAAQ,SAAY;AAAA,EAC5C;AAEA,aAAW,OAAOD,kBAAiB,YAAY,GAAG;AAChD,QAAI,aAAa,GAAG,MAAM,MAAM,OAAO;AACrC,mBAAa,GAAG,IAAI;AAAA,IACtB;AAAA,EACF;AAEA,MAAI,6BAA6B;AAC/B,iBAAa,mBAAmB;AAAA,EAClC;AAEA,SAAO;AACT;AAGO,IAAM,wBAAwB,CACnC,OACAC,UACA,mBAA4B,SACU;AACtC,QAAM,SAA4C,CAAC;AACnD,QAAM,EAAE,WAAW,gBAAgB,IAAIA;AAEvC,MAAI,cAAc,QAAW;AAC3B,UAAM,QAAQ,CAAC,SAAS;AACtB,UAAI,UAAU,SAAS,KAAK,IAAI,GAAG;AACjC,eAAO,KAAK,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG,WAAW,oBAAoB,IAAI,KAAK,iBAAiB;AACvD,eAAO,KAAK,GAAG,sBAAsB,KAAK,UAAUA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG;AAAA,IACF,CAAC;AAAA,EACH,OAAO;AACL,UAAM,QAAQ,CAAC,SAAS;AACtB,UAAI,oBAAoB,IAAI,KAAK,iBAAiB;AAChD,cAAM,UAAU,iCACX,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,IADtE;AAAA,UAEd,UAAU,sBAAsB,KAAK,UAAUA,UAAS,oBAAoB,KAAK,OAAO;AAAA,QAC1F;AACA,eAAO,KAAK,OAAO;AAAA,MACrB,OAAO;AACL,eAAO,KAAK,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;ACrFO,IAAM,QAAQ,YAAY;AAAA,EAC/B,mBAAmB,WAA+D;AAChF,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,cAA6C,cAAiB;AAC5D,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;", | ||
| "names": ["logBase", "sendRaw", "methods", "options", "__spreadValues", "api", "strictObjectKeys", "strictObjectKeys", "options"] | ||
| } |
+76
-45
| /// <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; |
+1
-1
| /// <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>[]; |
+3
-1
| { | ||
| "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", |
+19
-6
@@ -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 |
+1
-1
| /** | ||
| * 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'; |
+22
-12
@@ -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; |
+8
-6
| 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 @@ }); |
+123
-56
| 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; |
+44
-47
| 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 @@ }); |
27
17.39%2452
9.71%140
10.24%222250
-1.68%18
5.88%