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

figma-plugin-react-hooks

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

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

Comparing version
3.0.0
to
3.1.0
+2
-0
lib/constants.d.ts

@@ -14,2 +14,4 @@ /**

readonly addAncestorsVisibleProperty: false;
readonly pluginDataKeys: [];
readonly sharedPluginDataKeys: {};
};
+40
-13

@@ -53,3 +53,5 @@ "use strict";

resolveProperties: "all",
addAncestorsVisibleProperty: false
addAncestorsVisibleProperty: false,
pluginDataKeys: [],
sharedPluginDataKeys: {}
};

@@ -128,3 +130,3 @@

const variableInstances = [];
aliases.forEach((variableAlias) => {
for (const variableAlias of aliases) {
const variable = figma.variables.getVariableById(variableAlias.id);

@@ -134,3 +136,3 @@ if (variable) {

}
});
}
variableInstances.length > 0 && (result[boundVariableKey] = variableInstances);

@@ -154,4 +156,24 @@ } else if (boundVariableKey === "componentProperties") {

};
var resolvePluginData = (node, pluginDataKeys) => {
const pluginData = {};
for (const key of pluginDataKeys) {
const value = node.getPluginData(key);
if (value !== void 0) {
pluginData[key] = value;
}
}
return pluginData;
};
var resolveSharedPluginData = (node, sharedPluginDataKeys) => {
const sharedPluginData = {};
for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {
sharedPluginData[namespace] = {};
for (const key of sharedPluginDataKeys[namespace]) {
sharedPluginData[namespace][key] = node.getSharedPluginData(namespace, key);
}
}
return sharedPluginData;
};
var resolveAndSerializeNodeProperties = (node, options2, ancestorsVisible) => {
const { resolveProperties, resolveVariables, addAncestorsVisibleProperty } = options2;
const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } = options2;
const resolvedNode = resolveNodeProperties(

@@ -166,5 +188,4 @@ node,

}
const boundVariables = node.boundVariables;
if (boundVariables !== void 0 && (resolveVariables === "all" || isArray(resolveVariables) && resolveVariables.length > 0)) {
resolvedNode.boundVariableInstances = resolveBoundVariables(boundVariables, options2);
if (node.boundVariables !== void 0 && (resolveVariables === "all" || isArray(resolveVariables) && resolveVariables.length > 0)) {
resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options2);
}

@@ -174,2 +195,8 @@ if (addAncestorsVisibleProperty) {

}
if (pluginDataKeys.length > 0) {
resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);
}
if (Object.keys(sharedPluginDataKeys).length > 0) {
resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);
}
return resolvedNode;

@@ -181,3 +208,3 @@ };

if (nodeTypes !== void 0) {
nodes.forEach((node) => {
for (const node of nodes) {
if (nodeTypes.includes(node.type)) {

@@ -188,5 +215,5 @@ result.push(resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible));

}
});
}
} else {
nodes.forEach((node) => {
for (const node of nodes) {
if (nodeCanHaveChildren(node) && resolveChildren) {

@@ -200,3 +227,3 @@ const newNode = __spreadProps(__spreadValues({}, resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible)), {

}
});
}
}

@@ -230,7 +257,7 @@ return result;

const descendants = [];
nodes.forEach((node) => {
for (const node of nodes) {
if (nodeCanHaveChildren(node)) {
descendants.push(...node.children);
}
});
}
if (descendants.length === 0) {

@@ -237,0 +264,0 @@ return false;

{
"version": 3,
"sources": ["../src/hook.ts", "../src/constants.ts", "../src/useMountedEffect.ts", "../src/index.ts", "../src/typeUtils.ts", "../src/utils.ts"],
"sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\n\nimport { useEffect, useState } from 'react';\n\nimport { DEFAULT_HOOK_OPTIONS } from './constants';\nimport useMountedEffect from './useMountedEffect';\n\nimport { api, listeners, setlisteners, updateApiWithOptions, updateUiApiWithOptions } from '.';\n\nimport { CombineObjects } from './typePrimitives';\nimport {\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n SerializedResolvedNode,\n FigmaSelectionHookType\n} from './types';\n\nexport { FigmaSelectionHookOptions, FigmaSelectionHookNode, FigmaSelectionHookType } from './types';\nexport { FIGMA_MIXED } from './constants';\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): FigmaSelectionHookType<Options> => {\n const opts = { ...DEFAULT_HOOK_OPTIONS, ...hookOptions } as const;\n\n const [selection, setSelection] = useState<\n readonly SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>[]\n >([]);\n\n useMountedEffect(() => {\n console.warn('useFigmaSelection: changing options once mounted will not affect the behavior of the hook');\n }, [hookOptions]);\n\n useEffect(() => {\n const mount = async () => {\n // Typing the listeners explicitly is difficult due to the architecture, so we have to assert\n listeners.push(setSelection as unknown as FigmaSelectionListener);\n\n // if it's the first listener, register for selection change\n if (listeners.length === 1) {\n try {\n if (opts.apiOptions) {\n updateApiWithOptions(opts.apiOptions);\n updateUiApiWithOptions(opts.apiOptions);\n }\n await api._registerForSelectionChange(opts);\n } catch (e) {\n console.error(e);\n }\n }\n };\n\n mount();\n\n return () => {\n setlisteners(listeners.filter((l) => l !== (setSelection as unknown as FigmaSelectionListener)));\n if (!listeners.length) {\n // if it was the last listener, then we don't have to listen to selection change anymore\n api._deregisterForSelectionChange();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [\n selection as readonly SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>[],\n api._setSelection\n ];\n};\n\nexport default useFigmaSelection;\n", "import { ResolverOptions } from './types';\n\n/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n\n/**\n * Default options for the hook\n */\nexport const DEFAULT_HOOK_OPTIONS = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: [],\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false\n} as const satisfies ResolverOptions;\n", "import { useEffect, useRef } from 'react';\n\nconst useMountedEffect = (effect: React.EffectCallback, deps?: React.DependencyList | undefined) => {\n const didMountRef = useRef(false);\n\n useEffect(() => {\n if (didMountRef.current) {\n return effect();\n }\n didMountRef.current = true;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n};\n\nexport default useMountedEffect;\n", "import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\n// Allow esbuild to drop the import\nimport type { RPCOptions } from 'figma-plugin-api';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n });\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: ResolverOptions & 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", "type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\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", "import { FIGMA_MIXED } from './constants';\nimport { isArray, nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { Mutable, NonFunctionPropertyKeys } from './typePrimitives';\nimport {\n BoundVariableInstances,\n BoundVariablesAliasArrays,\n BoundVariablesBareAliases,\n ResolvedNode,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode\n} 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 [K in keyof Node]: TypedPropertyDescriptor<Node[K]>;\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 resolveVariableProperties = (variable: Variable): Variable => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Variable>(Object.getPrototypeOf(variable)) as {\n [K in keyof Variable]: TypedPropertyDescriptor<Variable[K]>;\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 const getters = getterKeys.filter(\n (key) => typeof descriptors[key].get === 'function'\n ) as NonFunctionPropertyKeys<Variable>[];\n\n getters.push('id');\n\n const objectWithProperties = {} as Mutable<Variable>;\n\n for (const getter of getters) {\n // @ts-expect-error For some reason this gives a \"not assignable to never\" error with this way of accessing the object\n objectWithProperties[getter] = variable[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveBoundVariables = <\n BoundVariables extends NonNullable<SceneNode['boundVariables']>,\n const Options extends ResolverOptions\n>(\n boundVariables: BoundVariables,\n options: Options\n): BoundVariableInstances => {\n const { resolveVariables } = options;\n const result: Mutable<BoundVariableInstances> = {};\n\n for (const boundVariableKey of strictObjectKeys(boundVariables)) {\n if (\n resolveVariables === 'all' ||\n (isArray(resolveVariables) && resolveVariables.includes(boundVariableKey as keyof SceneNode['boundVariables']))\n ) {\n const boundVariable = boundVariables[boundVariableKey];\n\n // TODO: there is a lot of type assertions going on that could be avoided with smarter type guards\n if (Array.isArray(boundVariable)) {\n const aliases: VariableAlias[] = boundVariable;\n const variableInstances: Variable[] = [];\n\n aliases.forEach((variableAlias) => {\n const variable = figma.variables.getVariableById(variableAlias.id);\n if (variable) {\n variableInstances.push(resolveVariableProperties(variable));\n }\n });\n\n variableInstances.length > 0 &&\n (result[boundVariableKey as keyof BoundVariablesAliasArrays] = variableInstances);\n } else if (boundVariableKey === 'componentProperties') {\n const aliasObject = boundVariable as NonNullable<BoundVariables['componentProperties']>;\n const instanceObject: Record<string, Variable> = {};\n\n for (const componentPropertyKey of Object.keys(aliasObject)) {\n const variable = figma.variables.getVariableById(aliasObject[componentPropertyKey].id);\n variable && (instanceObject[componentPropertyKey] = resolveVariableProperties(variable));\n }\n\n Object.keys(instanceObject).length > 0 && (result[boundVariableKey as 'componentProperties'] = instanceObject);\n } else {\n const alias = boundVariable as VariableAlias;\n const variableInstance = figma.variables.getVariableById(alias.id);\n\n variableInstance &&\n (result[boundVariableKey as keyof BoundVariablesBareAliases] = resolveVariableProperties(variableInstance));\n }\n }\n }\n\n return result;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n node: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, resolveVariables, addAncestorsVisibleProperty } = options;\n\n const resolvedNode = resolveNodeProperties(\n node,\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 const boundVariables = node.boundVariables;\n if (\n boundVariables !== undefined &&\n (resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))\n ) {\n resolvedNode.boundVariableInstances = resolveBoundVariables(boundVariables, options);\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\nexport const resolveAndFilterNodes = <const Options extends ResolverOptions>(\n nodes: readonly SceneNode[],\n options: Options,\n ancestorsVisible: boolean = true\n): SerializedResolvedNode<Options>[] => {\n const result: SerializedResolvedNode<Options>[] = [];\n const { nodeTypes, resolveChildren } = options;\n\n if (nodeTypes !== undefined) {\n nodes.forEach((node) => {\n if (nodeTypes.includes(node.type)) {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n } else if (nodeCanHaveChildren(node) && resolveChildren) {\n result.push(...resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible));\n }\n });\n } else {\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n });\n }\n\n return result;\n};\n"],
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,IAAAA,gBAAoC;;;ACG7B,IAAM,cAAc;AAKpB,IAAM,uBAAuB;AAAA,EAClC,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,kBAAkB,CAAC;AAAA,EACnB,mBAAmB;AAAA,EACnB,6BAA6B;AAC/B;;;AChBA,mBAAkC;AAElC,IAAM,mBAAmB,CAAC,QAA8B,SAA4C;AAClG,QAAM,kBAAc,qBAAO,KAAK;AAEhC,8BAAU,MAAM;AACd,QAAI,YAAY,SAAS;AACvB,aAAO,OAAO;AAAA,IAChB;AACA,gBAAY,UAAU;AAAA,EAExB,GAAG,IAAI;AACT;AAEA,IAAO,2BAAQ;;;ACdf,8BAA6C;;;ACEtC,IAAM,UAAU,MAAM;AAEtB,IAAM,mBAAmB,OAAO;AAEhC,IAAM,sBAAsB,CAAsB,SAAuC;AAC9F,SAAO,cAAc;AACvB;;;ACMA,IAAM,kCAAkC,CAAyB,KAAiB,SAAwB;AAd1G;AAeE;AAAA;AAAA,IAEE,EAAE,QAAQ,oCAAkC,UAAK,WAAL,mBAAa,UAAS;AAAA,IAElE,QAAQ,uBACR,QAAQ;AAAA;AAEZ;AAEA,IAAM,wBAAwB,CAC5B,MACA,iBAC6B;AAE7B,QAAM,cAAc,OAAO,0BAAgC,OAAO,eAAe,IAAI,CAAC;AAMtF,QAAM,aAAa,iBAAiB,WAAW;AAC/C,MAAI,UAAU,WAAW,OAAO,CAAC,QAAQ,OAAO,YAAY,GAAG,EAAE,QAAQ,UAAU;AAGnF,UAAQ,KAAK,MAAM;AAEnB,MAAI,cAAc;AAChB,cAAU,QAAQ,OAAO,CAAC,QAAQ,aAAa,SAAS,GAA2B,CAAC;AAAA,EACtF;AAEA,YAAU,QAAQ,OAAO,CAAC,QAAQ,gCAAgC,KAAK,IAAI,CAAC;AAE5E,QAAM,uBAAuB;AAAA,IAC3B,IAAI,KAAK;AAAA,EACX;AAEA,aAAW,UAAU,SAAS;AAC5B,yBAAqB,MAAM,IAAI,KAAK,MAAM;AAAA,EAC5C;AAEA,SAAO;AACT;AAEA,IAAM,4BAA4B,CAAC,aAAiC;AAElE,QAAM,cAAc,OAAO,0BAAoC,OAAO,eAAe,QAAQ,CAAC;AAM9F,QAAM,aAAa,iBAAiB,WAAW;AAC/C,QAAM,UAAU,WAAW;AAAA,IACzB,CAAC,QAAQ,OAAO,YAAY,GAAG,EAAE,QAAQ;AAAA,EAC3C;AAEA,UAAQ,KAAK,IAAI;AAEjB,QAAM,uBAAuB,CAAC;AAE9B,aAAW,UAAU,SAAS;AAE5B,yBAAqB,MAAM,IAAI,SAAS,MAAM;AAAA,EAChD;AAEA,SAAO;AACT;AAEA,IAAM,wBAAwB,CAI5B,gBACAC,aAC2B;AAC3B,QAAM,EAAE,iBAAiB,IAAIA;AAC7B,QAAM,SAA0C,CAAC;AAEjD,aAAW,oBAAoB,iBAAiB,cAAc,GAAG;AAC/D,QACE,qBAAqB,SACpB,QAAQ,gBAAgB,KAAK,iBAAiB,SAAS,gBAAqD,GAC7G;AACA,YAAM,gBAAgB,eAAe,gBAAgB;AAGrD,UAAI,MAAM,QAAQ,aAAa,GAAG;AAChC,cAAM,UAA2B;AACjC,cAAM,oBAAgC,CAAC;AAEvC,gBAAQ,QAAQ,CAAC,kBAAkB;AACjC,gBAAM,WAAW,MAAM,UAAU,gBAAgB,cAAc,EAAE;AACjE,cAAI,UAAU;AACZ,8BAAkB,KAAK,0BAA0B,QAAQ,CAAC;AAAA,UAC5D;AAAA,QACF,CAAC;AAED,0BAAkB,SAAS,MACxB,OAAO,gBAAmD,IAAI;AAAA,MACnE,WAAW,qBAAqB,uBAAuB;AACrD,cAAM,cAAc;AACpB,cAAM,iBAA2C,CAAC;AAElD,mBAAW,wBAAwB,OAAO,KAAK,WAAW,GAAG;AAC3D,gBAAM,WAAW,MAAM,UAAU,gBAAgB,YAAY,oBAAoB,EAAE,EAAE;AACrF,uBAAa,eAAe,oBAAoB,IAAI,0BAA0B,QAAQ;AAAA,QACxF;AAEA,eAAO,KAAK,cAAc,EAAE,SAAS,MAAM,OAAO,gBAAyC,IAAI;AAAA,MACjG,OAAO;AACL,cAAM,QAAQ;AACd,cAAM,mBAAmB,MAAM,UAAU,gBAAgB,MAAM,EAAE;AAEjE,6BACG,OAAO,gBAAmD,IAAI,0BAA0B,gBAAgB;AAAA,MAC7G;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,IAAM,oCAAoC,CACxC,MACAA,UACA,qBACoC;AACpC,QAAM,EAAE,mBAAmB,kBAAkB,4BAA4B,IAAIA;AAE7E,QAAM,eAAe;AAAA,IACnB;AAAA,IACA,sBAAsB,QAAQ,SAAY;AAAA,EAC5C;AAEA,aAAW,OAAO,iBAAiB,YAAY,GAAG;AAChD,QAAI,aAAa,GAAG,MAAM,MAAM,OAAO;AACrC,mBAAa,GAAG,IAAI;AAAA,IACtB;AAAA,EACF;AAEA,QAAM,iBAAiB,KAAK;AAC5B,MACE,mBAAmB,WAClB,qBAAqB,SAAU,QAAQ,gBAAgB,KAAK,iBAAiB,SAAS,IACvF;AACA,iBAAa,yBAAyB,sBAAsB,gBAAgBA,QAAO;AAAA,EACrF;AAEA,MAAI,6BAA6B;AAC/B,iBAAa,mBAAmB;AAAA,EAClC;AAEA,SAAO;AACT;AAEO,IAAM,wBAAwB,CACnC,OACAA,UACA,mBAA4B,SACU;AACtC,QAAM,SAA4C,CAAC;AACnD,QAAM,EAAE,WAAW,gBAAgB,IAAIA;AAEvC,MAAI,cAAc,QAAW;AAC3B,UAAM,QAAQ,CAAC,SAAS;AACtB,UAAI,UAAU,SAAS,KAAK,IAAI,GAAG;AACjC,eAAO,KAAK,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG,WAAW,oBAAoB,IAAI,KAAK,iBAAiB;AACvD,eAAO,KAAK,GAAG,sBAAsB,KAAK,UAAUA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG;AAAA,IACF,CAAC;AAAA,EACH,OAAO;AACL,UAAM,QAAQ,CAAC,SAAS;AACtB,UAAI,oBAAoB,IAAI,KAAK,iBAAiB;AAChD,cAAM,UAAU,iCACX,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,IADtE;AAAA,UAEd,UAAU,sBAAsB,KAAK,UAAUA,UAAS,oBAAoB,KAAK,OAAO;AAAA,QAC1F;AACA,eAAO,KAAK,OAAO;AAAA,MACrB,OAAO;AACL,eAAO,KAAK,kCAAkC,MAAMA,UAAS,oBAAoB,KAAK,OAAO,CAAC;AAAA,MAChG;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;AFjLA,IAAM,eAAe;AAAA,EACnB,mBAAmB,WAA+D;AAChF,QAAI,OAAO,OAAO,6BAA6B,aAAa;AAC1D,aAAO,yBAAyB,SAAS;AAAA,IAC3C;AAAA,EACF;AACF;AAEO,IAAI,YAAQ,qCAAY,YAAY;AAEpC,IAAM,yBAAyB,CAAC,eAA2B;AAChE,cAAQ,qCAAY,cAAc,UAAU;AAC9C;AAEA,IAAM,yBAAyB,MAAM;AACnC,QAAM,oBAAoB,sBAAsB,MAAM,YAAY,WAAW,OAAO;AACpF,QAAM,mBAAmB,iBAAiB;AAC5C;AAEA,IAAM,2CAA2C,CAAC,GAAwB,UAAyC;AACjH,QAAM,sBAAsB,EAAE,gBAAgB;AAAA,IAC5C,CAAC,WAAW,MAAM,UAAU,CAAC,SAAS,KAAK,OAAO,OAAO,EAAE,MAAM;AAAA,EACnE;AAEA,MAAI,qBAAqB;AACvB,WAAO;AAAA,EACT;AAEA,QAAM,cAA2B,CAAC;AAElC,QAAM,QAAQ,CAAC,SAAS;AACtB,QAAI,oBAAoB,IAAI,GAAG;AAC7B,kBAAY,KAAK,GAAG,KAAK,QAAQ;AAAA,IACnC;AAAA,EACF,CAAC;AAED,MAAI,YAAY,WAAW,GAAG;AAC5B,WAAO;AAAA,EACT;AAEA,SAAO,yCAAyC,GAAG,WAAW;AAChE;AAEA,IAAM,wBAAwB,CAAC,MAA2B;AACxD,MAAI,MAAM,YAAY,UAAU,SAAS,GAAG;AAC1C,UAAM,YAAY,MAAM,YAAY;AAEpC,QAAI,yCAAyC,GAAG,SAAS,GAAG;AAC1D,6BAAuB;AAAA,IACzB;AAAA,EACF;AACF;AAEA,IAAM,aAAa;AAAA,EACjB,4BAA4B,MAAmD;AAC7E,cAAU;AAEV,UAAM,aAAa,KAAK;AACxB,QAAI,YAAY;AACd,6BAAuB,UAAU;AACjC,2BAAqB,UAAU;AAAA,IACjC;AAEA,UAAM,GAAG,mBAAmB,sBAAsB;AAClD,UAAM,GAAG,kBAAkB,qBAAqB;AAChD,2BAAuB;AAAA,EACzB;AAAA,EACA,gCAAgC;AAC9B,UAAM,IAAI,mBAAmB,sBAAsB;AACnD,UAAM,IAAI,kBAAkB,qBAAqB;AAAA,EACnD;AAAA,EACA,cAA6C,cAAiB;AAC5D,UAAM,YAAY,YAAY;AAAA,EAChC;AACF;AAEO,IAAI,UAAM,yCAAgB,UAAU;AAEpC,IAAM,uBAAuB,CAAC,eAA2B;AAC9D,YAAM,yCAAgB,YAAY,UAAU;AAC9C;AAEA,IAAI;AAEG,IAAI,YAAsC,CAAC;AAE3C,IAAM,eAAe,CAAC,iBAA2C;AACtE,cAAY;AACd;AAGA,IAAI,OAAO,WAAW,aAAa;AACjC,SAAO,2BAA2B,CAAC,cAAc;AAC/C,cAAU,QAAQ,CAAC,MAAM;AACvB,QAAE,SAAS;AAAA,IACb,CAAC;AAAA,EACH;AACF;;;AHlGA,IAAM,oBAAoB,CACxB,gBACoC;AACpC,QAAM,OAAO,kCAAK,uBAAyB;AAE3C,QAAM,CAAC,WAAW,YAAY,QAAI,wBAEhC,CAAC,CAAC;AAEJ,2BAAiB,MAAM;AACrB,YAAQ,KAAK,2FAA2F;AAAA,EAC1G,GAAG,CAAC,WAAW,CAAC;AAEhB,+BAAU,MAAM;AACd,UAAM,QAAQ,YAAY;AAExB,gBAAU,KAAK,YAAiD;AAGhE,UAAI,UAAU,WAAW,GAAG;AAC1B,YAAI;AACF,cAAI,KAAK,YAAY;AACnB,iCAAqB,KAAK,UAAU;AACpC,mCAAuB,KAAK,UAAU;AAAA,UACxC;AACA,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;AAAA,IACL;AAAA,IACA,IAAI;AAAA,EACN;AACF;AAEA,IAAO,eAAQ;",
"sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\n\nimport { useEffect, useState } from 'react';\n\nimport { DEFAULT_HOOK_OPTIONS } from './constants';\nimport useMountedEffect from './useMountedEffect';\n\nimport { api, listeners, setlisteners, updateApiWithOptions, updateUiApiWithOptions } from '.';\n\nimport { CombineObjects } from './typePrimitives';\nimport {\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n SerializedResolvedNode,\n FigmaSelectionHookType\n} from './types';\n\nexport { FigmaSelectionHookOptions, FigmaSelectionHookNode, FigmaSelectionHookType } from './types';\nexport { FIGMA_MIXED } from './constants';\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): FigmaSelectionHookType<Options> => {\n const opts = { ...DEFAULT_HOOK_OPTIONS, ...hookOptions } as const;\n\n const [selection, setSelection] = useState<\n readonly SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>[]\n >([]);\n\n useMountedEffect(() => {\n console.warn('useFigmaSelection: changing options once mounted will not affect the behavior of the hook');\n }, [hookOptions]);\n\n useEffect(() => {\n const mount = async () => {\n // Typing the listeners explicitly is difficult due to the architecture, so we have to assert\n listeners.push(setSelection as unknown as FigmaSelectionListener);\n\n // if it's the first listener, register for selection change\n if (listeners.length === 1) {\n try {\n if (opts.apiOptions) {\n updateApiWithOptions(opts.apiOptions);\n updateUiApiWithOptions(opts.apiOptions);\n }\n await api._registerForSelectionChange(opts);\n } catch (e) {\n console.error(e);\n }\n }\n };\n\n mount();\n\n return () => {\n setlisteners(listeners.filter((l) => l !== (setSelection as unknown as FigmaSelectionListener)));\n if (!listeners.length) {\n // if it was the last listener, then we don't have to listen to selection change anymore\n api._deregisterForSelectionChange();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [\n selection as readonly SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>[],\n api._setSelection\n ];\n};\n\nexport default useFigmaSelection;\n", "import { ResolverOptions } from './types';\n\n/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n\n/**\n * Default options for the hook\n */\nexport const DEFAULT_HOOK_OPTIONS = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: [],\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false,\n pluginDataKeys: [],\n sharedPluginDataKeys: {}\n} as const satisfies ResolverOptions;\n", "import { useEffect, useRef } from 'react';\n\nconst useMountedEffect = (effect: React.EffectCallback, deps?: React.DependencyList | undefined) => {\n const didMountRef = useRef(false);\n\n useEffect(() => {\n if (didMountRef.current) {\n return effect();\n }\n didMountRef.current = true;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n};\n\nexport default useMountedEffect;\n", "import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\n// Allow esbuild to drop the import\nimport type { RPCOptions } from 'figma-plugin-api';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n for (const node of nodes) {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n }\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: ResolverOptions & 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", "type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\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", "import { FIGMA_MIXED } from './constants';\nimport { isArray, nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { Mutable, NonFunctionPropertyKeys } from './typePrimitives';\nimport {\n BoundVariableInstances,\n BoundVariablesAliasArrays,\n BoundVariablesBareAliases,\n OptSharedPluginDataKeys,\n ResolvedNode,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode,\n SharedPluginData\n} 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 [K in keyof Node]: TypedPropertyDescriptor<Node[K]>;\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 resolveVariableProperties = (variable: Variable): Variable => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Variable>(Object.getPrototypeOf(variable)) as {\n [K in keyof Variable]: TypedPropertyDescriptor<Variable[K]>;\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 const getters = getterKeys.filter(\n (key) => typeof descriptors[key].get === 'function'\n ) as NonFunctionPropertyKeys<Variable>[];\n\n getters.push('id');\n\n const objectWithProperties = {} as Mutable<Variable>;\n\n for (const getter of getters) {\n // @ts-expect-error For some reason this gives a \"not assignable to never\" error with this way of accessing the object\n objectWithProperties[getter] = variable[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveBoundVariables = <\n BoundVariables extends NonNullable<SceneNode['boundVariables']>,\n const Options extends ResolverOptions\n>(\n boundVariables: BoundVariables,\n options: Options\n): BoundVariableInstances => {\n const { resolveVariables } = options;\n const result: Mutable<BoundVariableInstances> = {};\n\n for (const boundVariableKey of strictObjectKeys(boundVariables)) {\n if (\n resolveVariables === 'all' ||\n (isArray(resolveVariables) && resolveVariables.includes(boundVariableKey as keyof SceneNode['boundVariables']))\n ) {\n const boundVariable = boundVariables[boundVariableKey];\n\n // TODO: there is a lot of type assertions going on that could be avoided with smarter type guards\n if (Array.isArray(boundVariable)) {\n const aliases: VariableAlias[] = boundVariable;\n const variableInstances: Variable[] = [];\n\n for (const variableAlias of aliases) {\n const variable = figma.variables.getVariableById(variableAlias.id);\n if (variable) {\n variableInstances.push(resolveVariableProperties(variable));\n }\n }\n\n variableInstances.length > 0 &&\n (result[boundVariableKey as keyof BoundVariablesAliasArrays] = variableInstances);\n } else if (boundVariableKey === 'componentProperties') {\n const aliasObject = boundVariable as NonNullable<BoundVariables['componentProperties']>;\n const instanceObject: Record<string, Variable> = {};\n\n for (const componentPropertyKey of Object.keys(aliasObject)) {\n const variable = figma.variables.getVariableById(aliasObject[componentPropertyKey].id);\n variable && (instanceObject[componentPropertyKey] = resolveVariableProperties(variable));\n }\n\n Object.keys(instanceObject).length > 0 && (result[boundVariableKey as 'componentProperties'] = instanceObject);\n } else {\n const alias = boundVariable as VariableAlias;\n const variableInstance = figma.variables.getVariableById(alias.id);\n\n variableInstance &&\n (result[boundVariableKey as keyof BoundVariablesBareAliases] = resolveVariableProperties(variableInstance));\n }\n }\n }\n\n return result;\n};\n\nconst resolvePluginData = (node: SceneNode, pluginDataKeys: string[]): Record<string, string> => {\n const pluginData: Record<string, string> = {};\n\n for (const key of pluginDataKeys) {\n const value = node.getPluginData(key);\n if (value !== undefined) {\n pluginData[key] = value;\n }\n }\n\n return pluginData;\n};\n\nconst resolveSharedPluginData = <K extends OptSharedPluginDataKeys>(\n node: SceneNode,\n sharedPluginDataKeys: K\n): SharedPluginData<K> => {\n const sharedPluginData: Partial<Record<keyof K, Record<string, string>>> = {};\n\n for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {\n sharedPluginData[namespace] = {};\n for (const key of sharedPluginDataKeys[namespace]) {\n (sharedPluginData[namespace] as Record<string, string>)[key] = node.getSharedPluginData(namespace as string, key);\n }\n }\n\n return sharedPluginData as SharedPluginData<K>;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n node: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } =\n options;\n\n const resolvedNode = resolveNodeProperties(\n node,\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 (\n node.boundVariables !== undefined &&\n (resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))\n ) {\n resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options);\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n if (pluginDataKeys.length > 0) {\n resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);\n }\n\n if (Object.keys(sharedPluginDataKeys).length > 0) {\n resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\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 for (const node of nodes) {\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 for (const node of nodes) {\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": ["import_react", "options"]
}

@@ -31,3 +31,5 @@ var __defProp = Object.defineProperty;

resolveProperties: "all",
addAncestorsVisibleProperty: false
addAncestorsVisibleProperty: false,
pluginDataKeys: [],
sharedPluginDataKeys: {}
};

@@ -106,3 +108,3 @@

const variableInstances = [];
aliases.forEach((variableAlias) => {
for (const variableAlias of aliases) {
const variable = figma.variables.getVariableById(variableAlias.id);

@@ -112,3 +114,3 @@ if (variable) {

}
});
}
variableInstances.length > 0 && (result[boundVariableKey] = variableInstances);

@@ -132,4 +134,24 @@ } else if (boundVariableKey === "componentProperties") {

};
var resolvePluginData = (node, pluginDataKeys) => {
const pluginData = {};
for (const key of pluginDataKeys) {
const value = node.getPluginData(key);
if (value !== void 0) {
pluginData[key] = value;
}
}
return pluginData;
};
var resolveSharedPluginData = (node, sharedPluginDataKeys) => {
const sharedPluginData = {};
for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {
sharedPluginData[namespace] = {};
for (const key of sharedPluginDataKeys[namespace]) {
sharedPluginData[namespace][key] = node.getSharedPluginData(namespace, key);
}
}
return sharedPluginData;
};
var resolveAndSerializeNodeProperties = (node, options2, ancestorsVisible) => {
const { resolveProperties, resolveVariables, addAncestorsVisibleProperty } = options2;
const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } = options2;
const resolvedNode = resolveNodeProperties(

@@ -144,5 +166,4 @@ node,

}
const boundVariables = node.boundVariables;
if (boundVariables !== void 0 && (resolveVariables === "all" || isArray(resolveVariables) && resolveVariables.length > 0)) {
resolvedNode.boundVariableInstances = resolveBoundVariables(boundVariables, options2);
if (node.boundVariables !== void 0 && (resolveVariables === "all" || isArray(resolveVariables) && resolveVariables.length > 0)) {
resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options2);
}

@@ -152,2 +173,8 @@ if (addAncestorsVisibleProperty) {

}
if (pluginDataKeys.length > 0) {
resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);
}
if (Object.keys(sharedPluginDataKeys).length > 0) {
resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);
}
return resolvedNode;

@@ -159,3 +186,3 @@ };

if (nodeTypes !== void 0) {
nodes.forEach((node) => {
for (const node of nodes) {
if (nodeTypes.includes(node.type)) {

@@ -166,5 +193,5 @@ result.push(resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible));

}
});
}
} else {
nodes.forEach((node) => {
for (const node of nodes) {
if (nodeCanHaveChildren(node) && resolveChildren) {

@@ -178,3 +205,3 @@ const newNode = __spreadProps(__spreadValues({}, resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible)), {

}
});
}
}

@@ -208,7 +235,7 @@ return result;

const descendants = [];
nodes.forEach((node) => {
for (const node of nodes) {
if (nodeCanHaveChildren(node)) {
descendants.push(...node.children);
}
});
}
if (descendants.length === 0) {

@@ -215,0 +242,0 @@ return false;

{
"version": 3,
"sources": ["../src/hook.ts", "../src/constants.ts", "../src/useMountedEffect.ts", "../src/index.ts", "../src/typeUtils.ts", "../src/utils.ts"],
"sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\n\nimport { useEffect, useState } from 'react';\n\nimport { DEFAULT_HOOK_OPTIONS } from './constants';\nimport useMountedEffect from './useMountedEffect';\n\nimport { api, listeners, setlisteners, updateApiWithOptions, updateUiApiWithOptions } from '.';\n\nimport { CombineObjects } from './typePrimitives';\nimport {\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n SerializedResolvedNode,\n FigmaSelectionHookType\n} from './types';\n\nexport { FigmaSelectionHookOptions, FigmaSelectionHookNode, FigmaSelectionHookType } from './types';\nexport { FIGMA_MIXED } from './constants';\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): FigmaSelectionHookType<Options> => {\n const opts = { ...DEFAULT_HOOK_OPTIONS, ...hookOptions } as const;\n\n const [selection, setSelection] = useState<\n readonly SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>[]\n >([]);\n\n useMountedEffect(() => {\n console.warn('useFigmaSelection: changing options once mounted will not affect the behavior of the hook');\n }, [hookOptions]);\n\n useEffect(() => {\n const mount = async () => {\n // Typing the listeners explicitly is difficult due to the architecture, so we have to assert\n listeners.push(setSelection as unknown as FigmaSelectionListener);\n\n // if it's the first listener, register for selection change\n if (listeners.length === 1) {\n try {\n if (opts.apiOptions) {\n updateApiWithOptions(opts.apiOptions);\n updateUiApiWithOptions(opts.apiOptions);\n }\n await api._registerForSelectionChange(opts);\n } catch (e) {\n console.error(e);\n }\n }\n };\n\n mount();\n\n return () => {\n setlisteners(listeners.filter((l) => l !== (setSelection as unknown as FigmaSelectionListener)));\n if (!listeners.length) {\n // if it was the last listener, then we don't have to listen to selection change anymore\n api._deregisterForSelectionChange();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [\n selection as readonly SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>[],\n api._setSelection\n ];\n};\n\nexport default useFigmaSelection;\n", "import { ResolverOptions } from './types';\n\n/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n\n/**\n * Default options for the hook\n */\nexport const DEFAULT_HOOK_OPTIONS = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: [],\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false\n} as const satisfies ResolverOptions;\n", "import { useEffect, useRef } from 'react';\n\nconst useMountedEffect = (effect: React.EffectCallback, deps?: React.DependencyList | undefined) => {\n const didMountRef = useRef(false);\n\n useEffect(() => {\n if (didMountRef.current) {\n return effect();\n }\n didMountRef.current = true;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n};\n\nexport default useMountedEffect;\n", "import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\n// Allow esbuild to drop the import\nimport type { RPCOptions } from 'figma-plugin-api';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n });\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: ResolverOptions & 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", "type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\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", "import { FIGMA_MIXED } from './constants';\nimport { isArray, nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { Mutable, NonFunctionPropertyKeys } from './typePrimitives';\nimport {\n BoundVariableInstances,\n BoundVariablesAliasArrays,\n BoundVariablesBareAliases,\n ResolvedNode,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode\n} 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 [K in keyof Node]: TypedPropertyDescriptor<Node[K]>;\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 resolveVariableProperties = (variable: Variable): Variable => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Variable>(Object.getPrototypeOf(variable)) as {\n [K in keyof Variable]: TypedPropertyDescriptor<Variable[K]>;\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 const getters = getterKeys.filter(\n (key) => typeof descriptors[key].get === 'function'\n ) as NonFunctionPropertyKeys<Variable>[];\n\n getters.push('id');\n\n const objectWithProperties = {} as Mutable<Variable>;\n\n for (const getter of getters) {\n // @ts-expect-error For some reason this gives a \"not assignable to never\" error with this way of accessing the object\n objectWithProperties[getter] = variable[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveBoundVariables = <\n BoundVariables extends NonNullable<SceneNode['boundVariables']>,\n const Options extends ResolverOptions\n>(\n boundVariables: BoundVariables,\n options: Options\n): BoundVariableInstances => {\n const { resolveVariables } = options;\n const result: Mutable<BoundVariableInstances> = {};\n\n for (const boundVariableKey of strictObjectKeys(boundVariables)) {\n if (\n resolveVariables === 'all' ||\n (isArray(resolveVariables) && resolveVariables.includes(boundVariableKey as keyof SceneNode['boundVariables']))\n ) {\n const boundVariable = boundVariables[boundVariableKey];\n\n // TODO: there is a lot of type assertions going on that could be avoided with smarter type guards\n if (Array.isArray(boundVariable)) {\n const aliases: VariableAlias[] = boundVariable;\n const variableInstances: Variable[] = [];\n\n aliases.forEach((variableAlias) => {\n const variable = figma.variables.getVariableById(variableAlias.id);\n if (variable) {\n variableInstances.push(resolveVariableProperties(variable));\n }\n });\n\n variableInstances.length > 0 &&\n (result[boundVariableKey as keyof BoundVariablesAliasArrays] = variableInstances);\n } else if (boundVariableKey === 'componentProperties') {\n const aliasObject = boundVariable as NonNullable<BoundVariables['componentProperties']>;\n const instanceObject: Record<string, Variable> = {};\n\n for (const componentPropertyKey of Object.keys(aliasObject)) {\n const variable = figma.variables.getVariableById(aliasObject[componentPropertyKey].id);\n variable && (instanceObject[componentPropertyKey] = resolveVariableProperties(variable));\n }\n\n Object.keys(instanceObject).length > 0 && (result[boundVariableKey as 'componentProperties'] = instanceObject);\n } else {\n const alias = boundVariable as VariableAlias;\n const variableInstance = figma.variables.getVariableById(alias.id);\n\n variableInstance &&\n (result[boundVariableKey as keyof BoundVariablesBareAliases] = resolveVariableProperties(variableInstance));\n }\n }\n }\n\n return result;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n node: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, resolveVariables, addAncestorsVisibleProperty } = options;\n\n const resolvedNode = resolveNodeProperties(\n node,\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 const boundVariables = node.boundVariables;\n if (\n boundVariables !== undefined &&\n (resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))\n ) {\n resolvedNode.boundVariableInstances = resolveBoundVariables(boundVariables, options);\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\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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",
"sourcesContent": ["/* eslint-disable react-hooks/rules-of-hooks */\n\nimport { useEffect, useState } from 'react';\n\nimport { DEFAULT_HOOK_OPTIONS } from './constants';\nimport useMountedEffect from './useMountedEffect';\n\nimport { api, listeners, setlisteners, updateApiWithOptions, updateUiApiWithOptions } from '.';\n\nimport { CombineObjects } from './typePrimitives';\nimport {\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n SerializedResolvedNode,\n FigmaSelectionHookType\n} from './types';\n\nexport { FigmaSelectionHookOptions, FigmaSelectionHookNode, FigmaSelectionHookType } from './types';\nexport { FIGMA_MIXED } from './constants';\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): FigmaSelectionHookType<Options> => {\n const opts = { ...DEFAULT_HOOK_OPTIONS, ...hookOptions } as const;\n\n const [selection, setSelection] = useState<\n readonly SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>[]\n >([]);\n\n useMountedEffect(() => {\n console.warn('useFigmaSelection: changing options once mounted will not affect the behavior of the hook');\n }, [hookOptions]);\n\n useEffect(() => {\n const mount = async () => {\n // Typing the listeners explicitly is difficult due to the architecture, so we have to assert\n listeners.push(setSelection as unknown as FigmaSelectionListener);\n\n // if it's the first listener, register for selection change\n if (listeners.length === 1) {\n try {\n if (opts.apiOptions) {\n updateApiWithOptions(opts.apiOptions);\n updateUiApiWithOptions(opts.apiOptions);\n }\n await api._registerForSelectionChange(opts);\n } catch (e) {\n console.error(e);\n }\n }\n };\n\n mount();\n\n return () => {\n setlisteners(listeners.filter((l) => l !== (setSelection as unknown as FigmaSelectionListener)));\n if (!listeners.length) {\n // if it was the last listener, then we don't have to listen to selection change anymore\n api._deregisterForSelectionChange();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n return [\n selection as readonly SerializedResolvedNode<CombineObjects<typeof DEFAULT_HOOK_OPTIONS, Options>>[],\n api._setSelection\n ];\n};\n\nexport default useFigmaSelection;\n", "import { ResolverOptions } from './types';\n\n/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n\n/**\n * Default options for the hook\n */\nexport const DEFAULT_HOOK_OPTIONS = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: [],\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false,\n pluginDataKeys: [],\n sharedPluginDataKeys: {}\n} as const satisfies ResolverOptions;\n", "import { useEffect, useRef } from 'react';\n\nconst useMountedEffect = (effect: React.EffectCallback, deps?: React.DependencyList | undefined) => {\n const didMountRef = useRef(false);\n\n useEffect(() => {\n if (didMountRef.current) {\n return effect();\n }\n didMountRef.current = true;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, deps);\n};\n\nexport default useMountedEffect;\n", "import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\n// Allow esbuild to drop the import\nimport type { RPCOptions } from 'figma-plugin-api';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n for (const node of nodes) {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n }\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: ResolverOptions & 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", "type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\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", "import { FIGMA_MIXED } from './constants';\nimport { isArray, nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { Mutable, NonFunctionPropertyKeys } from './typePrimitives';\nimport {\n BoundVariableInstances,\n BoundVariablesAliasArrays,\n BoundVariablesBareAliases,\n OptSharedPluginDataKeys,\n ResolvedNode,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode,\n SharedPluginData\n} 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 [K in keyof Node]: TypedPropertyDescriptor<Node[K]>;\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 resolveVariableProperties = (variable: Variable): Variable => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Variable>(Object.getPrototypeOf(variable)) as {\n [K in keyof Variable]: TypedPropertyDescriptor<Variable[K]>;\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 const getters = getterKeys.filter(\n (key) => typeof descriptors[key].get === 'function'\n ) as NonFunctionPropertyKeys<Variable>[];\n\n getters.push('id');\n\n const objectWithProperties = {} as Mutable<Variable>;\n\n for (const getter of getters) {\n // @ts-expect-error For some reason this gives a \"not assignable to never\" error with this way of accessing the object\n objectWithProperties[getter] = variable[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveBoundVariables = <\n BoundVariables extends NonNullable<SceneNode['boundVariables']>,\n const Options extends ResolverOptions\n>(\n boundVariables: BoundVariables,\n options: Options\n): BoundVariableInstances => {\n const { resolveVariables } = options;\n const result: Mutable<BoundVariableInstances> = {};\n\n for (const boundVariableKey of strictObjectKeys(boundVariables)) {\n if (\n resolveVariables === 'all' ||\n (isArray(resolveVariables) && resolveVariables.includes(boundVariableKey as keyof SceneNode['boundVariables']))\n ) {\n const boundVariable = boundVariables[boundVariableKey];\n\n // TODO: there is a lot of type assertions going on that could be avoided with smarter type guards\n if (Array.isArray(boundVariable)) {\n const aliases: VariableAlias[] = boundVariable;\n const variableInstances: Variable[] = [];\n\n for (const variableAlias of aliases) {\n const variable = figma.variables.getVariableById(variableAlias.id);\n if (variable) {\n variableInstances.push(resolveVariableProperties(variable));\n }\n }\n\n variableInstances.length > 0 &&\n (result[boundVariableKey as keyof BoundVariablesAliasArrays] = variableInstances);\n } else if (boundVariableKey === 'componentProperties') {\n const aliasObject = boundVariable as NonNullable<BoundVariables['componentProperties']>;\n const instanceObject: Record<string, Variable> = {};\n\n for (const componentPropertyKey of Object.keys(aliasObject)) {\n const variable = figma.variables.getVariableById(aliasObject[componentPropertyKey].id);\n variable && (instanceObject[componentPropertyKey] = resolveVariableProperties(variable));\n }\n\n Object.keys(instanceObject).length > 0 && (result[boundVariableKey as 'componentProperties'] = instanceObject);\n } else {\n const alias = boundVariable as VariableAlias;\n const variableInstance = figma.variables.getVariableById(alias.id);\n\n variableInstance &&\n (result[boundVariableKey as keyof BoundVariablesBareAliases] = resolveVariableProperties(variableInstance));\n }\n }\n }\n\n return result;\n};\n\nconst resolvePluginData = (node: SceneNode, pluginDataKeys: string[]): Record<string, string> => {\n const pluginData: Record<string, string> = {};\n\n for (const key of pluginDataKeys) {\n const value = node.getPluginData(key);\n if (value !== undefined) {\n pluginData[key] = value;\n }\n }\n\n return pluginData;\n};\n\nconst resolveSharedPluginData = <K extends OptSharedPluginDataKeys>(\n node: SceneNode,\n sharedPluginDataKeys: K\n): SharedPluginData<K> => {\n const sharedPluginData: Partial<Record<keyof K, Record<string, string>>> = {};\n\n for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {\n sharedPluginData[namespace] = {};\n for (const key of sharedPluginDataKeys[namespace]) {\n (sharedPluginData[namespace] as Record<string, string>)[key] = node.getSharedPluginData(namespace as string, key);\n }\n }\n\n return sharedPluginData as SharedPluginData<K>;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n node: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } =\n options;\n\n const resolvedNode = resolveNodeProperties(\n node,\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 (\n node.boundVariables !== undefined &&\n (resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))\n ) {\n resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options);\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n if (pluginDataKeys.length > 0) {\n resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);\n }\n\n if (Object.keys(sharedPluginDataKeys).length > 0) {\n resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\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 for (const node of nodes) {\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 for (const node of nodes) {\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": ["useEffect", "options", "useEffect"]
}

@@ -109,3 +109,3 @@ "use strict";

const variableInstances = [];
aliases.forEach((variableAlias) => {
for (const variableAlias of aliases) {
const variable = figma.variables.getVariableById(variableAlias.id);

@@ -115,3 +115,3 @@ if (variable) {

}
});
}
variableInstances.length > 0 && (result[boundVariableKey] = variableInstances);

@@ -135,4 +135,24 @@ } else if (boundVariableKey === "componentProperties") {

};
var resolvePluginData = (node, pluginDataKeys) => {
const pluginData = {};
for (const key of pluginDataKeys) {
const value = node.getPluginData(key);
if (value !== void 0) {
pluginData[key] = value;
}
}
return pluginData;
};
var resolveSharedPluginData = (node, sharedPluginDataKeys) => {
const sharedPluginData = {};
for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {
sharedPluginData[namespace] = {};
for (const key of sharedPluginDataKeys[namespace]) {
sharedPluginData[namespace][key] = node.getSharedPluginData(namespace, key);
}
}
return sharedPluginData;
};
var resolveAndSerializeNodeProperties = (node, options2, ancestorsVisible) => {
const { resolveProperties, resolveVariables, addAncestorsVisibleProperty } = options2;
const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } = options2;
const resolvedNode = resolveNodeProperties(

@@ -147,5 +167,4 @@ node,

}
const boundVariables = node.boundVariables;
if (boundVariables !== void 0 && (resolveVariables === "all" || isArray(resolveVariables) && resolveVariables.length > 0)) {
resolvedNode.boundVariableInstances = resolveBoundVariables(boundVariables, options2);
if (node.boundVariables !== void 0 && (resolveVariables === "all" || isArray(resolveVariables) && resolveVariables.length > 0)) {
resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options2);
}

@@ -155,2 +174,8 @@ if (addAncestorsVisibleProperty) {

}
if (pluginDataKeys.length > 0) {
resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);
}
if (Object.keys(sharedPluginDataKeys).length > 0) {
resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);
}
return resolvedNode;

@@ -162,3 +187,3 @@ };

if (nodeTypes !== void 0) {
nodes.forEach((node) => {
for (const node of nodes) {
if (nodeTypes.includes(node.type)) {

@@ -169,5 +194,5 @@ result.push(resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible));

}
});
}
} else {
nodes.forEach((node) => {
for (const node of nodes) {
if (nodeCanHaveChildren(node) && resolveChildren) {

@@ -181,3 +206,3 @@ const newNode = __spreadProps(__spreadValues({}, resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible)), {

}
});
}
}

@@ -211,7 +236,7 @@ return result;

const descendants = [];
nodes.forEach((node) => {
for (const node of nodes) {
if (nodeCanHaveChildren(node)) {
descendants.push(...node.children);
}
});
}
if (descendants.length === 0) {

@@ -218,0 +243,0 @@ return false;

{
"version": 3,
"sources": ["../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\n// Allow esbuild to drop the import\nimport type { RPCOptions } from 'figma-plugin-api';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n });\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: ResolverOptions & 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", "type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\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", "import { ResolverOptions } from './types';\n\n/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n\n/**\n * Default options for the hook\n */\nexport const DEFAULT_HOOK_OPTIONS = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: [],\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false\n} as const satisfies ResolverOptions;\n", "import { FIGMA_MIXED } from './constants';\nimport { isArray, nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { Mutable, NonFunctionPropertyKeys } from './typePrimitives';\nimport {\n BoundVariableInstances,\n BoundVariablesAliasArrays,\n BoundVariablesBareAliases,\n ResolvedNode,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode\n} 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 [K in keyof Node]: TypedPropertyDescriptor<Node[K]>;\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 resolveVariableProperties = (variable: Variable): Variable => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Variable>(Object.getPrototypeOf(variable)) as {\n [K in keyof Variable]: TypedPropertyDescriptor<Variable[K]>;\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 const getters = getterKeys.filter(\n (key) => typeof descriptors[key].get === 'function'\n ) as NonFunctionPropertyKeys<Variable>[];\n\n getters.push('id');\n\n const objectWithProperties = {} as Mutable<Variable>;\n\n for (const getter of getters) {\n // @ts-expect-error For some reason this gives a \"not assignable to never\" error with this way of accessing the object\n objectWithProperties[getter] = variable[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveBoundVariables = <\n BoundVariables extends NonNullable<SceneNode['boundVariables']>,\n const Options extends ResolverOptions\n>(\n boundVariables: BoundVariables,\n options: Options\n): BoundVariableInstances => {\n const { resolveVariables } = options;\n const result: Mutable<BoundVariableInstances> = {};\n\n for (const boundVariableKey of strictObjectKeys(boundVariables)) {\n if (\n resolveVariables === 'all' ||\n (isArray(resolveVariables) && resolveVariables.includes(boundVariableKey as keyof SceneNode['boundVariables']))\n ) {\n const boundVariable = boundVariables[boundVariableKey];\n\n // TODO: there is a lot of type assertions going on that could be avoided with smarter type guards\n if (Array.isArray(boundVariable)) {\n const aliases: VariableAlias[] = boundVariable;\n const variableInstances: Variable[] = [];\n\n aliases.forEach((variableAlias) => {\n const variable = figma.variables.getVariableById(variableAlias.id);\n if (variable) {\n variableInstances.push(resolveVariableProperties(variable));\n }\n });\n\n variableInstances.length > 0 &&\n (result[boundVariableKey as keyof BoundVariablesAliasArrays] = variableInstances);\n } else if (boundVariableKey === 'componentProperties') {\n const aliasObject = boundVariable as NonNullable<BoundVariables['componentProperties']>;\n const instanceObject: Record<string, Variable> = {};\n\n for (const componentPropertyKey of Object.keys(aliasObject)) {\n const variable = figma.variables.getVariableById(aliasObject[componentPropertyKey].id);\n variable && (instanceObject[componentPropertyKey] = resolveVariableProperties(variable));\n }\n\n Object.keys(instanceObject).length > 0 && (result[boundVariableKey as 'componentProperties'] = instanceObject);\n } else {\n const alias = boundVariable as VariableAlias;\n const variableInstance = figma.variables.getVariableById(alias.id);\n\n variableInstance &&\n (result[boundVariableKey as keyof BoundVariablesBareAliases] = resolveVariableProperties(variableInstance));\n }\n }\n }\n\n return result;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n node: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, resolveVariables, addAncestorsVisibleProperty } = options;\n\n const resolvedNode = resolveNodeProperties(\n node,\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 const boundVariables = node.boundVariables;\n if (\n boundVariables !== undefined &&\n (resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))\n ) {\n resolvedNode.boundVariableInstances = resolveBoundVariables(boundVariables, options);\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\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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",
"sourcesContent": ["import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\n// Allow esbuild to drop the import\nimport type { RPCOptions } from 'figma-plugin-api';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n for (const node of nodes) {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n }\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: ResolverOptions & 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", "type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\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", "import { ResolverOptions } from './types';\n\n/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n\n/**\n * Default options for the hook\n */\nexport const DEFAULT_HOOK_OPTIONS = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: [],\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false,\n pluginDataKeys: [],\n sharedPluginDataKeys: {}\n} as const satisfies ResolverOptions;\n", "import { FIGMA_MIXED } from './constants';\nimport { isArray, nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { Mutable, NonFunctionPropertyKeys } from './typePrimitives';\nimport {\n BoundVariableInstances,\n BoundVariablesAliasArrays,\n BoundVariablesBareAliases,\n OptSharedPluginDataKeys,\n ResolvedNode,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode,\n SharedPluginData\n} 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 [K in keyof Node]: TypedPropertyDescriptor<Node[K]>;\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 resolveVariableProperties = (variable: Variable): Variable => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Variable>(Object.getPrototypeOf(variable)) as {\n [K in keyof Variable]: TypedPropertyDescriptor<Variable[K]>;\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 const getters = getterKeys.filter(\n (key) => typeof descriptors[key].get === 'function'\n ) as NonFunctionPropertyKeys<Variable>[];\n\n getters.push('id');\n\n const objectWithProperties = {} as Mutable<Variable>;\n\n for (const getter of getters) {\n // @ts-expect-error For some reason this gives a \"not assignable to never\" error with this way of accessing the object\n objectWithProperties[getter] = variable[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveBoundVariables = <\n BoundVariables extends NonNullable<SceneNode['boundVariables']>,\n const Options extends ResolverOptions\n>(\n boundVariables: BoundVariables,\n options: Options\n): BoundVariableInstances => {\n const { resolveVariables } = options;\n const result: Mutable<BoundVariableInstances> = {};\n\n for (const boundVariableKey of strictObjectKeys(boundVariables)) {\n if (\n resolveVariables === 'all' ||\n (isArray(resolveVariables) && resolveVariables.includes(boundVariableKey as keyof SceneNode['boundVariables']))\n ) {\n const boundVariable = boundVariables[boundVariableKey];\n\n // TODO: there is a lot of type assertions going on that could be avoided with smarter type guards\n if (Array.isArray(boundVariable)) {\n const aliases: VariableAlias[] = boundVariable;\n const variableInstances: Variable[] = [];\n\n for (const variableAlias of aliases) {\n const variable = figma.variables.getVariableById(variableAlias.id);\n if (variable) {\n variableInstances.push(resolveVariableProperties(variable));\n }\n }\n\n variableInstances.length > 0 &&\n (result[boundVariableKey as keyof BoundVariablesAliasArrays] = variableInstances);\n } else if (boundVariableKey === 'componentProperties') {\n const aliasObject = boundVariable as NonNullable<BoundVariables['componentProperties']>;\n const instanceObject: Record<string, Variable> = {};\n\n for (const componentPropertyKey of Object.keys(aliasObject)) {\n const variable = figma.variables.getVariableById(aliasObject[componentPropertyKey].id);\n variable && (instanceObject[componentPropertyKey] = resolveVariableProperties(variable));\n }\n\n Object.keys(instanceObject).length > 0 && (result[boundVariableKey as 'componentProperties'] = instanceObject);\n } else {\n const alias = boundVariable as VariableAlias;\n const variableInstance = figma.variables.getVariableById(alias.id);\n\n variableInstance &&\n (result[boundVariableKey as keyof BoundVariablesBareAliases] = resolveVariableProperties(variableInstance));\n }\n }\n }\n\n return result;\n};\n\nconst resolvePluginData = (node: SceneNode, pluginDataKeys: string[]): Record<string, string> => {\n const pluginData: Record<string, string> = {};\n\n for (const key of pluginDataKeys) {\n const value = node.getPluginData(key);\n if (value !== undefined) {\n pluginData[key] = value;\n }\n }\n\n return pluginData;\n};\n\nconst resolveSharedPluginData = <K extends OptSharedPluginDataKeys>(\n node: SceneNode,\n sharedPluginDataKeys: K\n): SharedPluginData<K> => {\n const sharedPluginData: Partial<Record<keyof K, Record<string, string>>> = {};\n\n for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {\n sharedPluginData[namespace] = {};\n for (const key of sharedPluginDataKeys[namespace]) {\n (sharedPluginData[namespace] as Record<string, string>)[key] = node.getSharedPluginData(namespace as string, key);\n }\n }\n\n return sharedPluginData as SharedPluginData<K>;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n node: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } =\n options;\n\n const resolvedNode = resolveNodeProperties(\n node,\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 (\n node.boundVariables !== undefined &&\n (resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))\n ) {\n resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options);\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n if (pluginDataKeys.length > 0) {\n resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);\n }\n\n if (Object.keys(sharedPluginDataKeys).length > 0) {\n resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\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 for (const node of nodes) {\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 for (const node of nodes) {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n }\n }\n\n return result;\n};\n"],
"mappings": "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",
"names": ["options"]
}

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

const variableInstances = [];
aliases.forEach((variableAlias) => {
for (const variableAlias of aliases) {
const variable = figma.variables.getVariableById(variableAlias.id);

@@ -88,3 +88,3 @@ if (variable) {

}
});
}
variableInstances.length > 0 && (result[boundVariableKey] = variableInstances);

@@ -108,4 +108,24 @@ } else if (boundVariableKey === "componentProperties") {

};
var resolvePluginData = (node, pluginDataKeys) => {
const pluginData = {};
for (const key of pluginDataKeys) {
const value = node.getPluginData(key);
if (value !== void 0) {
pluginData[key] = value;
}
}
return pluginData;
};
var resolveSharedPluginData = (node, sharedPluginDataKeys) => {
const sharedPluginData = {};
for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {
sharedPluginData[namespace] = {};
for (const key of sharedPluginDataKeys[namespace]) {
sharedPluginData[namespace][key] = node.getSharedPluginData(namespace, key);
}
}
return sharedPluginData;
};
var resolveAndSerializeNodeProperties = (node, options2, ancestorsVisible) => {
const { resolveProperties, resolveVariables, addAncestorsVisibleProperty } = options2;
const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } = options2;
const resolvedNode = resolveNodeProperties(

@@ -120,5 +140,4 @@ node,

}
const boundVariables = node.boundVariables;
if (boundVariables !== void 0 && (resolveVariables === "all" || isArray(resolveVariables) && resolveVariables.length > 0)) {
resolvedNode.boundVariableInstances = resolveBoundVariables(boundVariables, options2);
if (node.boundVariables !== void 0 && (resolveVariables === "all" || isArray(resolveVariables) && resolveVariables.length > 0)) {
resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options2);
}

@@ -128,2 +147,8 @@ if (addAncestorsVisibleProperty) {

}
if (pluginDataKeys.length > 0) {
resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);
}
if (Object.keys(sharedPluginDataKeys).length > 0) {
resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);
}
return resolvedNode;

@@ -135,3 +160,3 @@ };

if (nodeTypes !== void 0) {
nodes.forEach((node) => {
for (const node of nodes) {
if (nodeTypes.includes(node.type)) {

@@ -142,5 +167,5 @@ result.push(resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible));

}
});
}
} else {
nodes.forEach((node) => {
for (const node of nodes) {
if (nodeCanHaveChildren(node) && resolveChildren) {

@@ -154,3 +179,3 @@ const newNode = __spreadProps(__spreadValues({}, resolveAndSerializeNodeProperties(node, options2, ancestorsVisible && node.visible)), {

}
});
}
}

@@ -184,7 +209,7 @@ return result;

const descendants = [];
nodes.forEach((node) => {
for (const node of nodes) {
if (nodeCanHaveChildren(node)) {
descendants.push(...node.children);
}
});
}
if (descendants.length === 0) {

@@ -191,0 +216,0 @@ return false;

{
"version": 3,
"sources": ["../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\n// Allow esbuild to drop the import\nimport type { RPCOptions } from 'figma-plugin-api';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n nodes.forEach((node) => {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n });\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: ResolverOptions & 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", "type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\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", "import { ResolverOptions } from './types';\n\n/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n\n/**\n * Default options for the hook\n */\nexport const DEFAULT_HOOK_OPTIONS = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: [],\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false\n} as const satisfies ResolverOptions;\n", "import { FIGMA_MIXED } from './constants';\nimport { isArray, nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { Mutable, NonFunctionPropertyKeys } from './typePrimitives';\nimport {\n BoundVariableInstances,\n BoundVariablesAliasArrays,\n BoundVariablesBareAliases,\n ResolvedNode,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode\n} 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 [K in keyof Node]: TypedPropertyDescriptor<Node[K]>;\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 resolveVariableProperties = (variable: Variable): Variable => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Variable>(Object.getPrototypeOf(variable)) as {\n [K in keyof Variable]: TypedPropertyDescriptor<Variable[K]>;\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 const getters = getterKeys.filter(\n (key) => typeof descriptors[key].get === 'function'\n ) as NonFunctionPropertyKeys<Variable>[];\n\n getters.push('id');\n\n const objectWithProperties = {} as Mutable<Variable>;\n\n for (const getter of getters) {\n // @ts-expect-error For some reason this gives a \"not assignable to never\" error with this way of accessing the object\n objectWithProperties[getter] = variable[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveBoundVariables = <\n BoundVariables extends NonNullable<SceneNode['boundVariables']>,\n const Options extends ResolverOptions\n>(\n boundVariables: BoundVariables,\n options: Options\n): BoundVariableInstances => {\n const { resolveVariables } = options;\n const result: Mutable<BoundVariableInstances> = {};\n\n for (const boundVariableKey of strictObjectKeys(boundVariables)) {\n if (\n resolveVariables === 'all' ||\n (isArray(resolveVariables) && resolveVariables.includes(boundVariableKey as keyof SceneNode['boundVariables']))\n ) {\n const boundVariable = boundVariables[boundVariableKey];\n\n // TODO: there is a lot of type assertions going on that could be avoided with smarter type guards\n if (Array.isArray(boundVariable)) {\n const aliases: VariableAlias[] = boundVariable;\n const variableInstances: Variable[] = [];\n\n aliases.forEach((variableAlias) => {\n const variable = figma.variables.getVariableById(variableAlias.id);\n if (variable) {\n variableInstances.push(resolveVariableProperties(variable));\n }\n });\n\n variableInstances.length > 0 &&\n (result[boundVariableKey as keyof BoundVariablesAliasArrays] = variableInstances);\n } else if (boundVariableKey === 'componentProperties') {\n const aliasObject = boundVariable as NonNullable<BoundVariables['componentProperties']>;\n const instanceObject: Record<string, Variable> = {};\n\n for (const componentPropertyKey of Object.keys(aliasObject)) {\n const variable = figma.variables.getVariableById(aliasObject[componentPropertyKey].id);\n variable && (instanceObject[componentPropertyKey] = resolveVariableProperties(variable));\n }\n\n Object.keys(instanceObject).length > 0 && (result[boundVariableKey as 'componentProperties'] = instanceObject);\n } else {\n const alias = boundVariable as VariableAlias;\n const variableInstance = figma.variables.getVariableById(alias.id);\n\n variableInstance &&\n (result[boundVariableKey as keyof BoundVariablesBareAliases] = resolveVariableProperties(variableInstance));\n }\n }\n }\n\n return result;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n node: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, resolveVariables, addAncestorsVisibleProperty } = options;\n\n const resolvedNode = resolveNodeProperties(\n node,\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 const boundVariables = node.boundVariables;\n if (\n boundVariables !== undefined &&\n (resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))\n ) {\n resolvedNode.boundVariableInstances = resolveBoundVariables(boundVariables, options);\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\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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",
"sourcesContent": ["import { createUIAPI, createPluginAPI } from 'figma-plugin-api';\n\nimport { nodeCanHaveChildren } from './typeUtils';\nimport { resolveAndFilterNodes } from './utils';\n\n// Allow esbuild to drop the import\nimport type { RPCOptions } from 'figma-plugin-api';\n\nimport {\n BareNode,\n FigmaSelectionHookOptions,\n FigmaSelectionListener,\n ResolverOptions,\n SerializedResolvedNode\n} from './types';\n\nexport { FIGMA_MIXED } from './constants';\n\ndeclare global {\n interface Window {\n _figma_onSelectionChange?: (selection: readonly SerializedResolvedNode<ResolverOptions>[]) => void;\n }\n}\n\nconst uiApiMethods = {\n _onSelectionChange(selection: readonly SerializedResolvedNode<ResolverOptions>[]) {\n if (typeof window._figma_onSelectionChange !== 'undefined') {\n window._figma_onSelectionChange(selection);\n }\n }\n};\n\nexport let uiApi = createUIAPI(uiApiMethods);\n\nexport const updateUiApiWithOptions = (rpcOptions: RPCOptions) => {\n uiApi = createUIAPI(uiApiMethods, rpcOptions);\n};\n\nconst selectionChangeHandler = () => {\n const resolvedSelection = resolveAndFilterNodes(figma.currentPage.selection, options);\n uiApi._onSelectionChange(resolvedSelection);\n};\n\nconst changesApplyToSelectedNodesOrDescendants = (e: DocumentChangeEvent, nodes: readonly SceneNode[]): boolean => {\n const changesApplyToNodes = e.documentChanges.some(\n (change) => nodes.findIndex((node) => node.id === change.id) !== -1\n );\n\n if (changesApplyToNodes) {\n return true;\n }\n\n const descendants: SceneNode[] = [];\n\n for (const node of nodes) {\n if (nodeCanHaveChildren(node)) {\n descendants.push(...node.children);\n }\n }\n\n if (descendants.length === 0) {\n return false;\n }\n\n return changesApplyToSelectedNodesOrDescendants(e, descendants);\n};\n\nconst documentChangeHandler = (e: DocumentChangeEvent) => {\n if (figma.currentPage.selection.length > 0) {\n const selection = figma.currentPage.selection;\n\n if (changesApplyToSelectedNodesOrDescendants(e, selection)) {\n selectionChangeHandler();\n }\n }\n};\n\nconst apiMethods = {\n _registerForSelectionChange(opts: ResolverOptions & FigmaSelectionHookOptions) {\n options = opts;\n\n const apiOptions = opts.apiOptions;\n if (apiOptions) {\n updateUiApiWithOptions(apiOptions);\n updateApiWithOptions(apiOptions);\n }\n\n figma.on('selectionchange', selectionChangeHandler);\n figma.on('documentchange', documentChangeHandler);\n selectionChangeHandler();\n },\n _deregisterForSelectionChange() {\n figma.off('selectionchange', selectionChangeHandler);\n figma.off('documentchange', documentChangeHandler);\n },\n _setSelection<N extends readonly BareNode[]>(newSelection: N) {\n figma.currentPage.selection = newSelection as unknown as readonly SceneNode[];\n }\n};\n\nexport let api = createPluginAPI(apiMethods);\n\nexport const updateApiWithOptions = (rpcOptions: RPCOptions) => {\n api = createPluginAPI(apiMethods, rpcOptions);\n};\n\nlet options: ResolverOptions & 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", "type ArrayType<T> = Extract<true extends T & false ? unknown[] : T extends readonly unknown[] ? T : unknown[], T>;\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", "import { ResolverOptions } from './types';\n\n/**\n * Used to replace `figma.mixed` during JSON serialization\n */\nexport const FIGMA_MIXED = 'mixed-57999e63-7384-42a1-acf8-d80b9f6c36a7';\n\n/**\n * Default options for the hook\n */\nexport const DEFAULT_HOOK_OPTIONS = {\n nodeTypes: undefined,\n resolveChildren: false,\n resolveVariables: [],\n resolveProperties: 'all',\n addAncestorsVisibleProperty: false,\n pluginDataKeys: [],\n sharedPluginDataKeys: {}\n} as const satisfies ResolverOptions;\n", "import { FIGMA_MIXED } from './constants';\nimport { isArray, nodeCanHaveChildren, strictObjectKeys } from './typeUtils';\n\nimport { Mutable, NonFunctionPropertyKeys } from './typePrimitives';\nimport {\n BoundVariableInstances,\n BoundVariablesAliasArrays,\n BoundVariablesBareAliases,\n OptSharedPluginDataKeys,\n ResolvedNode,\n ResolverOptions,\n SceneNodePropertyKey,\n SerializedResolvedNode,\n SharedPluginData\n} 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 [K in keyof Node]: TypedPropertyDescriptor<Node[K]>;\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 resolveVariableProperties = (variable: Variable): Variable => {\n // Narrow the type from the default which has a mapped type\n const descriptors = Object.getOwnPropertyDescriptors<Variable>(Object.getPrototypeOf(variable)) as {\n [K in keyof Variable]: TypedPropertyDescriptor<Variable[K]>;\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 const getters = getterKeys.filter(\n (key) => typeof descriptors[key].get === 'function'\n ) as NonFunctionPropertyKeys<Variable>[];\n\n getters.push('id');\n\n const objectWithProperties = {} as Mutable<Variable>;\n\n for (const getter of getters) {\n // @ts-expect-error For some reason this gives a \"not assignable to never\" error with this way of accessing the object\n objectWithProperties[getter] = variable[getter];\n }\n\n return objectWithProperties;\n};\n\nconst resolveBoundVariables = <\n BoundVariables extends NonNullable<SceneNode['boundVariables']>,\n const Options extends ResolverOptions\n>(\n boundVariables: BoundVariables,\n options: Options\n): BoundVariableInstances => {\n const { resolveVariables } = options;\n const result: Mutable<BoundVariableInstances> = {};\n\n for (const boundVariableKey of strictObjectKeys(boundVariables)) {\n if (\n resolveVariables === 'all' ||\n (isArray(resolveVariables) && resolveVariables.includes(boundVariableKey as keyof SceneNode['boundVariables']))\n ) {\n const boundVariable = boundVariables[boundVariableKey];\n\n // TODO: there is a lot of type assertions going on that could be avoided with smarter type guards\n if (Array.isArray(boundVariable)) {\n const aliases: VariableAlias[] = boundVariable;\n const variableInstances: Variable[] = [];\n\n for (const variableAlias of aliases) {\n const variable = figma.variables.getVariableById(variableAlias.id);\n if (variable) {\n variableInstances.push(resolveVariableProperties(variable));\n }\n }\n\n variableInstances.length > 0 &&\n (result[boundVariableKey as keyof BoundVariablesAliasArrays] = variableInstances);\n } else if (boundVariableKey === 'componentProperties') {\n const aliasObject = boundVariable as NonNullable<BoundVariables['componentProperties']>;\n const instanceObject: Record<string, Variable> = {};\n\n for (const componentPropertyKey of Object.keys(aliasObject)) {\n const variable = figma.variables.getVariableById(aliasObject[componentPropertyKey].id);\n variable && (instanceObject[componentPropertyKey] = resolveVariableProperties(variable));\n }\n\n Object.keys(instanceObject).length > 0 && (result[boundVariableKey as 'componentProperties'] = instanceObject);\n } else {\n const alias = boundVariable as VariableAlias;\n const variableInstance = figma.variables.getVariableById(alias.id);\n\n variableInstance &&\n (result[boundVariableKey as keyof BoundVariablesBareAliases] = resolveVariableProperties(variableInstance));\n }\n }\n }\n\n return result;\n};\n\nconst resolvePluginData = (node: SceneNode, pluginDataKeys: string[]): Record<string, string> => {\n const pluginData: Record<string, string> = {};\n\n for (const key of pluginDataKeys) {\n const value = node.getPluginData(key);\n if (value !== undefined) {\n pluginData[key] = value;\n }\n }\n\n return pluginData;\n};\n\nconst resolveSharedPluginData = <K extends OptSharedPluginDataKeys>(\n node: SceneNode,\n sharedPluginDataKeys: K\n): SharedPluginData<K> => {\n const sharedPluginData: Partial<Record<keyof K, Record<string, string>>> = {};\n\n for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {\n sharedPluginData[namespace] = {};\n for (const key of sharedPluginDataKeys[namespace]) {\n (sharedPluginData[namespace] as Record<string, string>)[key] = node.getSharedPluginData(namespace as string, key);\n }\n }\n\n return sharedPluginData as SharedPluginData<K>;\n};\n\nconst resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(\n node: Node,\n options: Options,\n ancestorsVisible: boolean\n): SerializedResolvedNode<Options> => {\n const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } =\n options;\n\n const resolvedNode = resolveNodeProperties(\n node,\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 (\n node.boundVariables !== undefined &&\n (resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))\n ) {\n resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options);\n }\n\n if (addAncestorsVisibleProperty) {\n resolvedNode.ancestorsVisible = ancestorsVisible;\n }\n\n if (pluginDataKeys.length > 0) {\n resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);\n }\n\n if (Object.keys(sharedPluginDataKeys).length > 0) {\n resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);\n }\n\n return resolvedNode as SerializedResolvedNode<Options>;\n};\n\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 for (const node of nodes) {\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 for (const node of nodes) {\n if (nodeCanHaveChildren(node) && resolveChildren) {\n const newNode = {\n ...resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible),\n children: resolveAndFilterNodes(node.children, options, ancestorsVisible && node.visible)\n };\n result.push(newNode);\n } else {\n result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));\n }\n }\n }\n\n return result;\n};\n"],
"mappings": "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",
"names": ["options"]
}

@@ -34,2 +34,3 @@ export type Mutable<T> = {

};
export type ArrayHasElements<T extends readonly unknown[]> = T extends readonly never[] ? false : true;
export {};
/// <reference types="@figma/plugin-typings" />
import { DEFAULT_HOOK_OPTIONS, FIGMA_MIXED } from './constants';
import type { RPCOptions } from 'figma-plugin-api';
import { ApplicableNonFunctionPropertyKeys, ArrayElementUnion, CombineObjects, ExtractProps, NonFunctionPropertyKeys } from './typePrimitives';
import { ApplicableNonFunctionPropertyKeys, ArrayElementUnion, ArrayHasElements, CombineObjects, ExtractProps, NonFunctionPropertyKeys } from './typePrimitives';
export type { RPCOptions } from 'figma-plugin-api';

@@ -18,3 +18,2 @@ export { FIGMA_MIXED } from './constants';

/**
* @internal
* Utility type to get only matching node types from the SceneNode union type.

@@ -33,2 +32,3 @@ */

export type OptSceneNodeVariables = readonly BoundVariableKey[] | 'all';
export type OptSharedPluginDataKeys = Record<string, string[]>;
/**

@@ -96,2 +96,16 @@ * Use `satisfies` (for TS >= 4.9) with this type to allow for type checking the options object

/**
* Get the corresponding plugin data for all nodes.
*
* Default: `[]`
*/
pluginDataKeys?: string[];
/**
* Get the corresponding shared plugin data for all nodes.
*
* The object keys are treated as namespaces and the array values as keys.
*
* Default: `{}`
*/
sharedPluginDataKeys?: OptSharedPluginDataKeys;
/**
* Options for figma-plugin-api

@@ -125,12 +139,18 @@ *

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;
type AncestorsVisibleMixin<Options extends ResolverOptions> = Options['addAncestorsVisibleProperty'] extends true ? {
ancestorsVisible: boolean;
} : unknown;
type PluginDataMixin<Options extends ResolverOptions> = ArrayHasElements<Options['pluginDataKeys']> extends true ? {
pluginData: Record<ArrayElementUnion<Options['pluginDataKeys']>, string>;
} : unknown;
/**
* @internal
*/
type AncestorsVisibleMixin = {
ancestorsVisible: boolean;
export type SharedPluginData<K extends OptSharedPluginDataKeys> = {
[N in keyof K]: Record<ArrayElementUnion<K[N]>, string>;
};
type SharedPluginDataMixin<Options extends ResolverOptions> = Options['sharedPluginDataKeys'] extends Record<string, never> ? unknown : {
sharedPluginData: SharedPluginData<Options['sharedPluginDataKeys']>;
};
/**

@@ -158,7 +178,6 @@ * @internal

export type BoundVariableInstances = ReplaceTypeInObject<NonNullable<SceneNode['boundVariables']>, VariableAlias, Variable>;
/**
* @internal
*/
type ResolveVariablesMixin<Options extends ResolverOptions> = {
boundVariableInstances?: Options['resolveVariables'] extends readonly BoundVariableKey[] ? Pick<BoundVariableInstances, ArrayElementUnion<Options['resolveVariables']>> : Options['resolveVariables'] extends 'all' ? BoundVariableInstances : never;
type ResolveVariablesMixin<Options extends ResolverOptions> = Options['resolveVariables'] extends readonly BoundVariableKey[] ? ArrayHasElements<Options['resolveVariables']> extends true ? {
boundVariableInstances?: Pick<BoundVariableInstances, ArrayElementUnion<Options['resolveVariables']>>;
} : unknown : {
boundVariableInstances?: BoundVariableInstances;
};

@@ -169,3 +188,3 @@ /**

*/
export type SerializedResolvedNode<Options extends ResolverOptions> = Options['addAncestorsVisibleProperty'] extends true ? Options['resolveVariables'] extends OptSceneNodeVariables ? SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options> & AncestorsVisibleMixin & ResolveVariablesMixin<Options> : SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options> & AncestorsVisibleMixin : SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options>;
export type SerializedResolvedNode<Options extends ResolverOptions> = SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options> & AncestorsVisibleMixin<Options> & ResolveVariablesMixin<Options> & PluginDataMixin<Options> & SharedPluginDataMixin<Options>;
/**

@@ -172,0 +191,0 @@ * @internal

{
"name": "figma-plugin-react-hooks",
"version": "3.0.0",
"version": "3.1.0",
"description": "",

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

"devDependencies": {
"@figma/eslint-plugin-figma-plugins": "*",
"@figma/plugin-typings": "*",

@@ -55,0 +56,0 @@ "@types/react": "^18.2.48",

@@ -132,2 +132,8 @@ # figma-plugin-react-hooks

### OptSharedPluginDataKeys
Ƭ **OptSharedPluginDataKeys**: `Record`\<`string`, `string`[]\>
___
### FigmaSelectionHookOptions

@@ -159,2 +165,4 @@

| `addAncestorsVisibleProperty?` | `boolean` | Add `ancestorsVisible` property to all nodes. This property is true if all ancestors of the node are visible. Default: `false` |
| `pluginDataKeys?` | `string`[] | Get the corresponding plugin data for all nodes. Default: `[]` |
| `sharedPluginDataKeys?` | [`OptSharedPluginDataKeys`](types.md#optsharedplugindatakeys) | Get the corresponding shared plugin data for all nodes. The object keys are treated as namespaces and the array values as keys. Default: `{}` |
| `apiOptions?` | [`RPCOptions`](types.md#rpcoptions) | Options for figma-plugin-api Default: see the RPCOptions type |

@@ -161,0 +169,0 @@

@@ -16,3 +16,5 @@ import { ResolverOptions } from './types';

resolveProperties: 'all',
addAncestorsVisibleProperty: false
addAncestorsVisibleProperty: false,
pluginDataKeys: [],
sharedPluginDataKeys: {}
} as const satisfies ResolverOptions;

@@ -55,7 +55,7 @@ import { createUIAPI, createPluginAPI } from 'figma-plugin-api';

nodes.forEach((node) => {
for (const node of nodes) {
if (nodeCanHaveChildren(node)) {
descendants.push(...node.children);
}
});
}

@@ -62,0 +62,0 @@ if (descendants.length === 0) {

@@ -55,1 +55,3 @@ /* eslint-disable @typescript-eslint/ban-types */

};
export type ArrayHasElements<T extends readonly unknown[]> = T extends readonly never[] ? false : true;

@@ -8,2 +8,3 @@ import { DEFAULT_HOOK_OPTIONS, FIGMA_MIXED } from './constants';

ArrayElementUnion,
ArrayHasElements,
CombineObjects,

@@ -37,3 +38,2 @@ ExtractProps,

/**
* @internal
* Utility type to get only matching node types from the SceneNode union type.

@@ -57,2 +57,4 @@ */

export type OptSharedPluginDataKeys = Record<string, string[]>;
/**

@@ -81,2 +83,3 @@ * Use `satisfies` (for TS >= 4.9) with this type to allow for type checking the options object

nodeTypes?: readonly SceneNodeType[] | undefined;
/**

@@ -94,2 +97,3 @@ * Figma node properties are lazy-loaded, so to use any property you have to resolve it first.

resolveProperties?: OptSceneNodeProperties;
/**

@@ -105,2 +109,3 @@ * Resolve bound variables of the selection.

resolveVariables?: OptSceneNodeVariables;
/**

@@ -114,2 +119,3 @@ * Resolve children nodes of the selection.

resolveChildren?: boolean;
/**

@@ -123,3 +129,20 @@ * Add `ancestorsVisible` property to all nodes.

addAncestorsVisibleProperty?: boolean;
/**
* Get the corresponding plugin data for all nodes.
*
* Default: `[]`
*/
pluginDataKeys?: string[];
/**
* Get the corresponding shared plugin data for all nodes.
*
* The object keys are treated as namespaces and the array values as keys.
*
* Default: `{}`
*/
sharedPluginDataKeys?: OptSharedPluginDataKeys;
/**
* Options for figma-plugin-api

@@ -187,5 +210,2 @@ *

/**
* @internal
*/
type SerializedResolvedNodeBase<Node extends SceneNode, Options extends ResolverOptions> = Node extends SceneNode

@@ -195,9 +215,29 @@ ? SerializedNode<Node, Options>

type AncestorsVisibleMixin<Options extends ResolverOptions> = Options['addAncestorsVisibleProperty'] extends true
? {
ancestorsVisible: boolean;
}
: unknown;
type PluginDataMixin<Options extends ResolverOptions> =
ArrayHasElements<Options['pluginDataKeys']> extends true
? {
pluginData: Record<ArrayElementUnion<Options['pluginDataKeys']>, string>;
}
: unknown;
/**
* @internal
*/
type AncestorsVisibleMixin = {
ancestorsVisible: boolean;
export type SharedPluginData<K extends OptSharedPluginDataKeys> = {
[N in keyof K]: Record<ArrayElementUnion<K[N]>, string>;
};
type SharedPluginDataMixin<Options extends ResolverOptions> =
Options['sharedPluginDataKeys'] extends Record<string, never>
? unknown
: {
sharedPluginData: SharedPluginData<Options['sharedPluginDataKeys']>;
};
/**

@@ -240,12 +280,12 @@ * @internal

/**
* @internal
*/
type ResolveVariablesMixin<Options extends ResolverOptions> = {
boundVariableInstances?: Options['resolveVariables'] extends readonly BoundVariableKey[]
? Pick<BoundVariableInstances, ArrayElementUnion<Options['resolveVariables']>>
: Options['resolveVariables'] extends 'all'
? BoundVariableInstances
: never;
};
type ResolveVariablesMixin<Options extends ResolverOptions> =
Options['resolveVariables'] extends readonly BoundVariableKey[]
? ArrayHasElements<Options['resolveVariables']> extends true
? {
boundVariableInstances?: Pick<BoundVariableInstances, ArrayElementUnion<Options['resolveVariables']>>;
}
: unknown
: {
boundVariableInstances?: BoundVariableInstances;
};

@@ -256,10 +296,10 @@ /**

*/
export type SerializedResolvedNode<Options extends ResolverOptions> =
Options['addAncestorsVisibleProperty'] extends true
? Options['resolveVariables'] extends OptSceneNodeVariables
? SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options> &
AncestorsVisibleMixin &
ResolveVariablesMixin<Options>
: SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options> & AncestorsVisibleMixin
: SerializedResolvedNodeBase<SceneNodeFromTypes<Options['nodeTypes']>, Options>;
export type SerializedResolvedNode<Options extends ResolverOptions> = SerializedResolvedNodeBase<
SceneNodeFromTypes<Options['nodeTypes']>,
Options
> &
AncestorsVisibleMixin<Options> &
ResolveVariablesMixin<Options> &
PluginDataMixin<Options> &
SharedPluginDataMixin<Options>;

@@ -266,0 +306,0 @@ /**

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

BoundVariablesBareAliases,
OptSharedPluginDataKeys,
ResolvedNode,
ResolverOptions,
SceneNodePropertyKey,
SerializedResolvedNode
SerializedResolvedNode,
SharedPluginData
} from './types';

@@ -107,3 +109,3 @@

aliases.forEach((variableAlias) => {
for (const variableAlias of aliases) {
const variable = figma.variables.getVariableById(variableAlias.id);

@@ -113,3 +115,3 @@ if (variable) {

}
});
}

@@ -141,2 +143,31 @@ variableInstances.length > 0 &&

const resolvePluginData = (node: SceneNode, pluginDataKeys: string[]): Record<string, string> => {
const pluginData: Record<string, string> = {};
for (const key of pluginDataKeys) {
const value = node.getPluginData(key);
if (value !== undefined) {
pluginData[key] = value;
}
}
return pluginData;
};
const resolveSharedPluginData = <K extends OptSharedPluginDataKeys>(
node: SceneNode,
sharedPluginDataKeys: K
): SharedPluginData<K> => {
const sharedPluginData: Partial<Record<keyof K, Record<string, string>>> = {};
for (const namespace of strictObjectKeys(sharedPluginDataKeys)) {
sharedPluginData[namespace] = {};
for (const key of sharedPluginDataKeys[namespace]) {
(sharedPluginData[namespace] as Record<string, string>)[key] = node.getSharedPluginData(namespace as string, key);
}
}
return sharedPluginData as SharedPluginData<K>;
};
const resolveAndSerializeNodeProperties = <Node extends SceneNode, const Options extends ResolverOptions>(

@@ -147,3 +178,4 @@ node: Node,

): SerializedResolvedNode<Options> => {
const { resolveProperties, resolveVariables, addAncestorsVisibleProperty } = options;
const { resolveProperties, resolveVariables, addAncestorsVisibleProperty, pluginDataKeys, sharedPluginDataKeys } =
options;

@@ -161,8 +193,7 @@ const resolvedNode = resolveNodeProperties(

const boundVariables = node.boundVariables;
if (
boundVariables !== undefined &&
node.boundVariables !== undefined &&
(resolveVariables === 'all' || (isArray(resolveVariables) && resolveVariables.length > 0))
) {
resolvedNode.boundVariableInstances = resolveBoundVariables(boundVariables, options);
resolvedNode.boundVariableInstances = resolveBoundVariables(node.boundVariables, options);
}

@@ -174,2 +205,10 @@

if (pluginDataKeys.length > 0) {
resolvedNode.pluginData = resolvePluginData(node, pluginDataKeys);
}
if (Object.keys(sharedPluginDataKeys).length > 0) {
resolvedNode.sharedPluginData = resolveSharedPluginData(node, sharedPluginDataKeys);
}
return resolvedNode as SerializedResolvedNode<Options>;

@@ -187,3 +226,3 @@ };

if (nodeTypes !== undefined) {
nodes.forEach((node) => {
for (const node of nodes) {
if (nodeTypes.includes(node.type)) {

@@ -194,5 +233,5 @@ result.push(resolveAndSerializeNodeProperties(node, options, ancestorsVisible && node.visible));

}
});
}
} else {
nodes.forEach((node) => {
for (const node of nodes) {
if (nodeCanHaveChildren(node) && resolveChildren) {

@@ -207,3 +246,3 @@ const newNode = {

}
});
}
}

@@ -210,0 +249,0 @@