Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: ignore variable mode value if it is a variable alias #20

Closed
2 changes: 1 addition & 1 deletion src/sync_tokens_to_figma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ async function main() {
const api = new FigmaApi(process.env.PERSONAL_ACCESS_TOKEN)
const localVariables = await api.getLocalVariables(fileKey)

const postVariablesPayload = generatePostVariablesPayload(tokensByFile, localVariables)
const postVariablesPayload = generatePostVariablesPayload(tokensByFile, localVariables, false)

if (Object.values(postVariablesPayload).every((value) => value.length === 0)) {
console.log(green('✅ Tokens are already up to date with the Figma file'))
Expand Down
65 changes: 55 additions & 10 deletions src/token_import.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
LocalVariable,
LocalVariableCollection,
PostVariablesRequestBody,
VariableAlias,
VariableCodeSyntax,
VariableCreate,
VariableUpdate,
Expand Down Expand Up @@ -118,6 +119,12 @@ function isAlias(value: string) {
return value.toString().trim().charAt(0) === '{'
}

const getFigmaVariableNameFromTokenValue = (value: string) =>
value
.trim()
.replace(/\./g, '/')
.replace(/[\{\}]/g, '')

function variableValueFromToken(
token: Token,
localVariablesByCollectionAndName: {
Expand All @@ -127,10 +134,7 @@ function variableValueFromToken(
if (typeof token.$value === 'string' && isAlias(token.$value)) {
// Assume aliases are in the format {group.subgroup.token} with any number of optional groups/subgroups
// The Figma syntax for variable names is: group/subgroup/token
const value = token.$value
.trim()
.replace(/\./g, '/')
.replace(/[\{\}]/g, '')
const value = getFigmaVariableNameFromTokenValue(token.$value)

// When mapping aliases to existing local variables, we assume that variable names
// are unique *across all collections* in the Figma file
Expand Down Expand Up @@ -235,6 +239,7 @@ function tokenAndVariableDifferences(token: Token, variable: LocalVariable | nul
export function generatePostVariablesPayload(
tokensByFile: FlattenedTokensByFile,
localVariables: GetLocalVariablesResponse,
shouldGenerateVariableModeValuesThatAreMissingInFigma: boolean = true,
) {
const localVariableCollectionsByName: { [name: string]: LocalVariableCollection } = {}
const localVariablesByCollectionAndName: {
Expand Down Expand Up @@ -357,22 +362,62 @@ export function generatePostVariablesPayload(
})
}

const existingVariableValue = variable && variableMode ? variable.valuesByMode[modeId] : null
const newVariableValue = variableValueFromToken(token, localVariablesByCollectionAndName)
const valueFromFigma = variable ? variable.valuesByMode[modeId] : null
const valueFromToken = variableValueFromToken(token, localVariablesByCollectionAndName)
const isMissingVariableModeValueInFigma = valueFromFigma === null

// Only include the variable mode value in the payload if it's different from the existing value
if (
existingVariableValue === null ||
!compareVariableValues(existingVariableValue, newVariableValue)
isMissingVariableModeValueInFigma &&
shouldGenerateVariableModeValuesThatAreMissingInFigma
) {
// Include the variable mode value in the payload if it is missing in Figma and we have chosen to include missing values in the current library
postVariablesPayload.variableModeValues!.push({
variableId,
modeId,
value: newVariableValue,
value: valueFromToken,
})
} else {
// If the value from token is a VariableAlias and it references a remote variable, we don't want to include it in the payload
const isRemote = isVariableValueModeValueReferencingRemoteVariable({
valueFromToken,
token,
})

if (!isRemote) {
// Check if variable is different from the existing value
const isDifferent =
valueFromFigma === null || !compareVariableValues(valueFromFigma, valueFromToken)

if (isDifferent) {
// Include the variable mode value in the payload if it's different from the existing value
postVariablesPayload.variableModeValues!.push({
variableId,
modeId,
value: valueFromToken,
})
}
}
}
})
})

return postVariablesPayload
}

const isVariableValueModeValueReferencingRemoteVariable = ({
valueFromToken,
token,
}: {
valueFromToken: VariableValue
token: Token
}) => {
if (typeof token.$value === 'string' && isAlias(token.$value)) {
const valueFromTokenId = (valueFromToken as VariableAlias).id
const tokenValueAsFigmaVariable = getFigmaVariableNameFromTokenValue(token.$value)
// The VariableAlias.id equals the token value if it is not found in the local variables
// We then assume that the token id is referencing a remote variable.
return valueFromTokenId === tokenValueAsFigmaVariable
}

return false
}