From 4997d840d52f2f8cbb742052ae6169e6219ae91f Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Wed, 17 Jun 2026 12:12:39 +0530 Subject: [PATCH 1/3] fix: detect $extensions changes in sync diff so modifier edits are no longer silently dropped --- .../src/utils/findDifferentState.test.ts | 171 +++++++++++++++++- .../src/utils/findDifferentState.ts | 2 +- 2 files changed, 171 insertions(+), 2 deletions(-) diff --git a/packages/tokens-studio-for-figma/src/utils/findDifferentState.test.ts b/packages/tokens-studio-for-figma/src/utils/findDifferentState.test.ts index 5157edcfd2..5d6a26d75f 100644 --- a/packages/tokens-studio-for-figma/src/utils/findDifferentState.test.ts +++ b/packages/tokens-studio-for-figma/src/utils/findDifferentState.test.ts @@ -75,5 +75,174 @@ describe('findDifferentState', () => { }); }); - // Additional test cases can be added to cover other scenarios like updated tokens, removed tokens, new/updated/removed themes, etc. + it('detects updated token value', () => { + const baseState: CompareStateType = { + tokens: { + set1: [{ + name: 'token1', value: '#ff0000', description: '', type: TokenTypes.COLOR, + }], + }, + themes: [], + metadata: null, + }; + const compareState: CompareStateType = { + tokens: { + set1: [{ + name: 'token1', value: '#00ff00', description: '', type: TokenTypes.COLOR, + }], + }, + themes: [], + metadata: null, + }; + + const result = findDifferentState(baseState, compareState); + + expect(result.tokens.set1).toEqual([ + expect.objectContaining({ name: 'token1', value: '#00ff00', oldValue: '#ff0000', importType: 'UPDATE' }), + ]); + }); + + it('detects removed tokens', () => { + const baseState: CompareStateType = { + tokens: { + set1: [ + { name: 'token1', value: 'value1', description: '', type: TokenTypes.COLOR }, + { name: 'token2', value: 'value2', description: '', type: TokenTypes.COLOR }, + ], + }, + themes: [], + metadata: null, + }; + const compareState: CompareStateType = { + tokens: { + set1: [{ name: 'token1', value: 'value1', description: '', type: TokenTypes.COLOR }], + }, + themes: [], + metadata: null, + }; + + const result = findDifferentState(baseState, compareState); + + expect(result.tokens.set1).toEqual([ + expect.objectContaining({ name: 'token2', importType: 'REMOVE' }), + ]); + }); + + it('detects opacity modifier change in $extensions as an update', () => { + const baseState: CompareStateType = { + tokens: { + set1: [{ + name: 'color.primary', + value: '#ff0000', + description: '', + type: TokenTypes.COLOR, + $extensions: { + 'studio.tokens': { + modify: { type: 'alpha', value: '0.5', space: 'srgb' }, + }, + }, + }], + }, + themes: [], + metadata: null, + }; + const compareState: CompareStateType = { + tokens: { + set1: [{ + name: 'color.primary', + value: '#ff0000', + description: '', + type: TokenTypes.COLOR, + $extensions: { + 'studio.tokens': { + modify: { type: 'alpha', value: '0.35', space: 'srgb' }, + }, + }, + }], + }, + themes: [], + metadata: null, + }; + + const result = findDifferentState(baseState, compareState); + + expect(result.tokens.set1).toHaveLength(1); + expect(result.tokens.set1[0]).toMatchObject({ name: 'color.primary', importType: 'UPDATE' }); + }); + + it('does not flag a change when $extensions modifier is identical', () => { + const token = { + name: 'color.primary', + value: '#ff0000', + description: '', + type: TokenTypes.COLOR, + $extensions: { + 'studio.tokens': { + modify: { type: 'alpha', value: '0.5', space: 'srgb' }, + }, + }, + }; + const baseState: CompareStateType = { tokens: { set1: [token] }, themes: [], metadata: null }; + const compareState: CompareStateType = { tokens: { set1: [{ ...token }] }, themes: [], metadata: null }; + + const result = findDifferentState(baseState, compareState); + + expect(result.tokens).toEqual({}); + }); + + it('detects when opacity modifier is added to a token', () => { + const baseState: CompareStateType = { + tokens: { + set1: [{ name: 'color.primary', value: '#ff0000', description: '', type: TokenTypes.COLOR }], + }, + themes: [], + metadata: null, + }; + const compareState: CompareStateType = { + tokens: { + set1: [{ + name: 'color.primary', + value: '#ff0000', + description: '', + type: TokenTypes.COLOR, + $extensions: { 'studio.tokens': { modify: { type: 'alpha', value: '0.5', space: 'srgb' } } }, + }], + }, + themes: [], + metadata: null, + }; + + const result = findDifferentState(baseState, compareState); + + expect(result.tokens.set1).toHaveLength(1); + expect(result.tokens.set1[0]).toMatchObject({ name: 'color.primary', importType: 'UPDATE' }); + }); + + it('detects when opacity modifier is removed from a token', () => { + const baseState: CompareStateType = { + tokens: { + set1: [{ + name: 'color.primary', + value: '#ff0000', + description: '', + type: TokenTypes.COLOR, + $extensions: { 'studio.tokens': { modify: { type: 'alpha', value: '0.5', space: 'srgb' } } }, + }], + }, + themes: [], + metadata: null, + }; + const compareState: CompareStateType = { + tokens: { + set1: [{ name: 'color.primary', value: '#ff0000', description: '', type: TokenTypes.COLOR }], + }, + themes: [], + metadata: null, + }; + + const result = findDifferentState(baseState, compareState); + + expect(result.tokens.set1).toHaveLength(1); + expect(result.tokens.set1[0]).toMatchObject({ name: 'color.primary', importType: 'UPDATE' }); + }); }); diff --git a/packages/tokens-studio-for-figma/src/utils/findDifferentState.ts b/packages/tokens-studio-for-figma/src/utils/findDifferentState.ts index db90ab3790..af59212db0 100644 --- a/packages/tokens-studio-for-figma/src/utils/findDifferentState.ts +++ b/packages/tokens-studio-for-figma/src/utils/findDifferentState.ts @@ -23,7 +23,7 @@ export function findDifferentState(baseState: CompareStateType, compareState: Co values.forEach((token) => { const oldValue = baseState.tokens[tokenSet]?.find((t) => t.name === token.name); if (oldValue) { - if (!isEqual(oldValue.value, token.value)) { + if (!isEqual(oldValue.value, token.value) || !isEqual(oldValue.$extensions, token.$extensions)) { const updatedToken: ImportToken = { ...token }; updatedToken.oldValue = oldValue.value; updatedToken.importType = 'UPDATE'; From 7e8f2fb4c31f71efe9ce04f91927ff558afbde53 Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Fri, 3 Jul 2026 02:15:16 +0530 Subject: [PATCH 2/3] fix: only set oldValue when value changed, not when $extensions-only diff detected --- .../src/utils/findDifferentState.test.ts | 4 +++- .../tokens-studio-for-figma/src/utils/findDifferentState.ts | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/tokens-studio-for-figma/src/utils/findDifferentState.test.ts b/packages/tokens-studio-for-figma/src/utils/findDifferentState.test.ts index 5d6a26d75f..ab0c004eca 100644 --- a/packages/tokens-studio-for-figma/src/utils/findDifferentState.test.ts +++ b/packages/tokens-studio-for-figma/src/utils/findDifferentState.test.ts @@ -168,6 +168,7 @@ describe('findDifferentState', () => { expect(result.tokens.set1).toHaveLength(1); expect(result.tokens.set1[0]).toMatchObject({ name: 'color.primary', importType: 'UPDATE' }); + expect(result.tokens.set1[0].oldValue).toBeUndefined(); }); it('does not flag a change when $extensions modifier is identical', () => { @@ -183,7 +184,8 @@ describe('findDifferentState', () => { }, }; const baseState: CompareStateType = { tokens: { set1: [token] }, themes: [], metadata: null }; - const compareState: CompareStateType = { tokens: { set1: [{ ...token }] }, themes: [], metadata: null }; + const compareToken = JSON.parse(JSON.stringify(token)); + const compareState: CompareStateType = { tokens: { set1: [compareToken] }, themes: [], metadata: null }; const result = findDifferentState(baseState, compareState); diff --git a/packages/tokens-studio-for-figma/src/utils/findDifferentState.ts b/packages/tokens-studio-for-figma/src/utils/findDifferentState.ts index af59212db0..1a18a05669 100644 --- a/packages/tokens-studio-for-figma/src/utils/findDifferentState.ts +++ b/packages/tokens-studio-for-figma/src/utils/findDifferentState.ts @@ -23,9 +23,11 @@ export function findDifferentState(baseState: CompareStateType, compareState: Co values.forEach((token) => { const oldValue = baseState.tokens[tokenSet]?.find((t) => t.name === token.name); if (oldValue) { - if (!isEqual(oldValue.value, token.value) || !isEqual(oldValue.$extensions, token.$extensions)) { + const valueChanged = !isEqual(oldValue.value, token.value); + const extensionsChanged = !isEqual(oldValue.$extensions, token.$extensions); + if (valueChanged || extensionsChanged) { const updatedToken: ImportToken = { ...token }; - updatedToken.oldValue = oldValue.value; + if (valueChanged) updatedToken.oldValue = oldValue.value; updatedToken.importType = 'UPDATE'; updatedTokens.push(updatedToken); } From bf08951630ccf59efb3391150e8459995863ea15 Mon Sep 17 00:00:00 2001 From: Akshay Gupta Date: Fri, 3 Jul 2026 02:16:21 +0530 Subject: [PATCH 3/3] add changeset --- .changeset/tough-chefs-return.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tough-chefs-return.md diff --git a/.changeset/tough-chefs-return.md b/.changeset/tough-chefs-return.md new file mode 100644 index 0000000000..5b220b32b2 --- /dev/null +++ b/.changeset/tough-chefs-return.md @@ -0,0 +1,5 @@ +--- +"@tokens-studio/figma-plugin": patch +--- + +Opacity modifier changes are now correctly detected in the sync diff and will appear as pending changes ready to commit.