diff --git a/packages/tokens-studio-for-figma/src/constants/ValidNodeTypes.ts b/packages/tokens-studio-for-figma/src/constants/ValidNodeTypes.ts index 04059b0a0..9d72ea7ed 100644 --- a/packages/tokens-studio-for-figma/src/constants/ValidNodeTypes.ts +++ b/packages/tokens-studio-for-figma/src/constants/ValidNodeTypes.ts @@ -12,4 +12,5 @@ export const ValidNodeTypes: NodeType[] = [ 'TEXT', 'VECTOR', 'STAR', + 'SECTION', ]; diff --git a/packages/tokens-studio-for-figma/src/plugin/NodeManager.ts b/packages/tokens-studio-for-figma/src/plugin/NodeManager.ts index 9737a69e9..753944701 100644 --- a/packages/tokens-studio-for-figma/src/plugin/NodeManager.ts +++ b/packages/tokens-studio-for-figma/src/plugin/NodeManager.ts @@ -78,12 +78,15 @@ export class NodeManager { for (let nodeIndex = 0; nodeIndex < relevantNodes.length; nodeIndex += 1) { promises.add(defaultWorker.schedule(async () => { const node = relevantNodes[nodeIndex]; + const tokens = await tokensSharedDataHandler.getAll(node); - returnedNodes.push({ - node: relevantNodes[nodeIndex], - tokens: await tokensSharedDataHandler.getAll(node), - id: node.id, - }); + if (Object.keys(tokens).length > 0) { + returnedNodes.push({ + node, + tokens, + id: node.id, + }); + } tracker.next(); tracker.reportIfNecessary(); })); diff --git a/packages/tokens-studio-for-figma/src/plugin/SharedDataHandler.ts b/packages/tokens-studio-for-figma/src/plugin/SharedDataHandler.ts index e3eda1995..d883c83ec 100644 --- a/packages/tokens-studio-for-figma/src/plugin/SharedDataHandler.ts +++ b/packages/tokens-studio-for-figma/src/plugin/SharedDataHandler.ts @@ -21,23 +21,19 @@ class SharedDataHandler { } getAll(node: BaseNode) { - const keys = this.keys(node); const result: Record = {}; - keys.forEach((key) => { - if (key in Properties) { - const value = this.get(node, key); - if (value) { - try { - // make sure we catch JSON parse errors in case invalid property keys are set and found - // we're storing `none` as a string without quotes - const parsedValue = value === 'none' ? 'none' : JSON.parse(value); - result[key] = parsedValue; - } catch (err) { - console.warn(err); - } + Object.values(Properties).forEach((key) => { + const value = this.get(node, key); + if (value) { + try { + // make sure we catch JSON parse errors in case invalid property keys are set and found + // we're storing `none` as a string without quotes + const parsedValue = value === 'none' ? 'none' : JSON.parse(value); + result[key] = parsedValue; + } catch (err) { + console.warn(err); } } - return null; }); return result; } diff --git a/packages/tokens-studio-for-figma/src/plugin/asyncMessageHandlers/update.ts b/packages/tokens-studio-for-figma/src/plugin/asyncMessageHandlers/update.ts index 754681674..5a0c844a4 100644 --- a/packages/tokens-studio-for-figma/src/plugin/asyncMessageHandlers/update.ts +++ b/packages/tokens-studio-for-figma/src/plugin/asyncMessageHandlers/update.ts @@ -50,6 +50,7 @@ export const update: AsyncMessageChannelHandlers[AsyncMessageTypes.UPDATE] = asy allWithData = await defaultNodeManager.findBaseNodesWithData({ updateMode: msg.settings.updateMode, + nodesWithoutPluginData: true, }); await updateNodes(allWithData, String(msg.settings.baseFontSize || 16)); diff --git a/packages/tokens-studio-for-figma/src/plugin/updateNodes.ts b/packages/tokens-studio-for-figma/src/plugin/updateNodes.ts index 948fbcbc4..b5b11583e 100644 --- a/packages/tokens-studio-for-figma/src/plugin/updateNodes.ts +++ b/packages/tokens-studio-for-figma/src/plugin/updateNodes.ts @@ -35,7 +35,7 @@ export async function updateNodes( try { const rawTokenMap = destructureTokenForAlias(tokens, appliedTokens); const tokenValues = mapValuesToTokens(tokens, appliedTokens); - setValuesOnNode( + await setValuesOnNode( { node, values: tokenValues, diff --git a/packages/tokens-studio-for-figma/src/utils/findAll.ts b/packages/tokens-studio-for-figma/src/utils/findAll.ts index 013e40f9a..ea98ced21 100644 --- a/packages/tokens-studio-for-figma/src/utils/findAll.ts +++ b/packages/tokens-studio-for-figma/src/utils/findAll.ts @@ -1,23 +1,40 @@ import { ValidNodeTypes } from '@/constants/ValidNodeTypes'; export function findAll(nodes: readonly BaseNode[], includeSelf = false, nodesWithoutPluginData = false): BaseNode[] { - let allNodes = includeSelf ? [...nodes] : []; - const pluginDataOptions = nodesWithoutPluginData - ? {} - : { - sharedPluginData: { - namespace: 'tokens', - }, - }; + const allNodesSet = new Set(includeSelf ? nodes : []); + nodes.forEach((node) => { if ('children' in node) { - allNodes = allNodes.concat( + if (nodesWithoutPluginData) { + // Smart Discovery: + // 1. Find all nodes with local token data (Figma optimized search) + const withLocalData = node.findAllWithCriteria({ + types: ValidNodeTypes, + sharedPluginData: { namespace: 'tokens' }, + }); + withLocalData.forEach(n => allNodesSet.add(n)); + + // 2. Find all instances (their children have inherited token data) + const instances = node.findAllWithCriteria({ + types: ['INSTANCE'], + }); + instances.forEach(instance => { + allNodesSet.add(instance); + // For each instance, we must also check all its children + instance.findAllWithCriteria({ + types: ValidNodeTypes, + }).forEach(n => allNodesSet.add(n)); + }); + } else { + // Standard Discovery: Only nodes with local token data node.findAllWithCriteria({ types: ValidNodeTypes, - ...pluginDataOptions, - }), - ); + sharedPluginData: { namespace: 'tokens' }, + }).forEach(n => allNodesSet.add(n)); + } } }); + + const allNodes = Array.from(allNodesSet); return allNodes; }