Skip to content

Commit

Permalink
Add error check for invalid Figma variables references (#4640)
Browse files Browse the repository at this point in the history
* Add script to check for invalid figma references

* Add more comprehensive checks

* Fix error message to be more generic

* Update error message
  • Loading branch information
taysea authored Jan 14, 2025
1 parent f9c370c commit 411a8da
Show file tree
Hide file tree
Showing 7 changed files with 595 additions and 479 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/sync-figma-to-tokens.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ jobs:
FILE_KEY_SEMANTIC: ${{ secrets.GH_ACTION_FIGMA_FILE_KEY_SEMANTIC }}
FILE_KEY_COMPONENT: ${{ secrets.GH_ACTION_FIGMA_FILE_KEY_COMPONENT }}
PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_ACTION_VARIABLES_SYNC_FIGMA_TOKEN }}
FIGMA_COLOR_COLLECTION_KEY: ${{ secrets.GH_ACTION_FIGMA_COLOR_COLLECTION_KEY }}
FIGMA_DIMENSION_COLLECTION_KEY: ${{ secrets.GH_ACTION_FIGMA_DIMENSION_COLLECTION_KEY }}
FIGMA_PRIMITIVES_COLLECTION_KEY: ${{ secrets.GH_ACTION_FIGMA_PRIMITIVES_COLLECTION_KEY }}
FIGMA_GLOBAL_COLLECTION_KEY: ${{ secrets.GH_ACTION_FIGMA_GLOBAL_COLLECTION_KEY }}
- name: Format token files with prettier
run: yarn prettier tokens --write
working-directory: design-tokens
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/sync-tokens-to-figma.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,7 @@ jobs:
FILE_KEY_SEMANTIC: ${{ secrets.GH_ACTION_FIGMA_FILE_KEY_SEMANTIC }}
FILE_KEY_COMPONENT: ${{ secrets.GH_ACTION_FIGMA_FILE_KEY_COMPONENT }}
PERSONAL_ACCESS_TOKEN: ${{ secrets.GH_ACTION_VARIABLES_SYNC_FIGMA_TOKEN }}
FIGMA_COLOR_COLLECTION_KEY: ${{ secrets.GH_ACTION_FIGMA_COLOR_COLLECTION_KEY }}
FIGMA_DIMENSION_COLLECTION_KEY: ${{ secrets.GH_ACTION_FIGMA_DIMENSION_COLLECTION_KEY }}
FIGMA_PRIMITIVES_COLLECTION_KEY: ${{ secrets.GH_ACTION_FIGMA_PRIMITIVES_COLLECTION_KEY }}
FIGMA_GLOBAL_COLLECTION_KEY: ${{ secrets.GH_ACTION_FIGMA_GLOBAL_COLLECTION_KEY }}
2 changes: 2 additions & 0 deletions design-tokens/src/figma_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ export interface VariableModeChange {

export interface VariableCollection {
id: string;
key: string;
name: string;
modes: VariableMode[];
defaultModeId: string;
remote: boolean;
hiddenFromPublishing: boolean;
variableIds: string[];
}

export interface VariableCollectionChange
Expand Down
7 changes: 6 additions & 1 deletion design-tokens/src/scripts/sync_figma_to_tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as fs from 'fs';

import FigmaApi from '../figma_api.js';

import { green } from '../utils.js';
import { green, verifyReferences } from '../utils.js';
import { tokenFilesFromLocalVariables } from '../token_export.js';

/**
Expand Down Expand Up @@ -41,6 +41,11 @@ async function main() {

let outputDir = 'tokens_new';

const api = new FigmaApi(process.env.PERSONAL_ACCESS_TOKEN || '');
const componentTokens = await api.getLocalVariables(fileKeys.component);
const semanticTokens = await api.getLocalVariables(fileKeys.semantic);
verifyReferences([componentTokens, semanticTokens]);

tokenDirs.forEach(async dir => {
const api = new FigmaApi(process.env.PERSONAL_ACCESS_TOKEN || '');
const localVariables = await api.getLocalVariables(fileKeys[dir]);
Expand Down
7 changes: 6 additions & 1 deletion design-tokens/src/scripts/sync_tokens_to_figma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as fs from 'fs';

import FigmaApi from '../figma_api.js';

import { green } from '../utils.js';
import { green, verifyReferences } from '../utils.js';
import {
generatePostVariablesPayload,
readJsonFiles,
Expand Down Expand Up @@ -32,6 +32,11 @@ async function main() {
.filter(dir => dir.isDirectory())
.map(dir => dir.name);

const api = new FigmaApi(process.env.PERSONAL_ACCESS_TOKEN || '');
const componentTokens = await api.getLocalVariables(fileKeys.component);
const semanticTokens = await api.getLocalVariables(fileKeys.semantic);
verifyReferences([componentTokens, semanticTokens]);

tokenDirs.forEach(async dir => {
const tokensFiles = fs
.readdirSync(`${TOKENS_DIR}/${dir}`)
Expand Down
67 changes: 67 additions & 0 deletions design-tokens/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ApiGetLocalVariablesResponse } from './figma_api.js';

export function green(msg: string) {
return `\x1b[32m${msg}\x1b[0m`;
}
Expand Down Expand Up @@ -49,3 +51,68 @@ export const nonComponentTokens: string[] = [
];

export const numberToPixel = (value: number): string => `${value}px`;

/**
* Ensure variable references are to valid collections. Log errors for any variables that are referencing invalid Figma files.
*/
export const verifyReferences = (
localTokens: ApiGetLocalVariablesResponse[],
) => {
const invalidVariables: string[] = [];
localTokens.forEach(tokens => {
Object.keys(tokens.meta.variableCollections).forEach(key => {
const collection = tokens.meta.variableCollections[key];
if (collection.remote === true) {
if (
collection.name === 'color' &&
collection.key !== process.env['FIGMA_COLOR_COLLECTION_KEY']
) {
collection.variableIds.forEach(id =>
invalidVariables.push(tokens.meta.variables[id].id),
);
} else if (
collection.name === 'dimension' &&
collection.key !== process.env['FIGMA_DIMENSION_COLLECTION_KEY']
) {
collection.variableIds.forEach(id =>
invalidVariables.push(tokens.meta.variables[id].id),
);
} else if (
collection.name === 'primitives' &&
collection.key !== process.env['FIGMA_PRIMITIVE_COLLECTION_KEY']
) {
collection.variableIds.forEach(id =>
invalidVariables.push(tokens.meta.variables[id].id),
);
} else if (
collection.name === 'global' &&
collection.key !== process.env['FIGMA_GLOBAL_COLLECTION_KEY']
) {
collection.variableIds.forEach(id =>
invalidVariables.push(tokens.meta.variables[id].id),
);
}
}
});

Object.keys(tokens.meta.variables).forEach(key => {
const variable = tokens.meta.variables[key];
Object.keys(variable.valuesByMode).forEach(j => {
const modeValue = variable.valuesByMode[j];
if (
typeof modeValue === 'object' &&
'id' in modeValue &&
invalidVariables.includes(modeValue.id)
) {
console.error(
`🛑 Invalid collection reference for value of: ${variable.name}. Resolve reference error in Figma.`,
);
}
});
});
});
if (invalidVariables.length)
throw new Error(
'Invalid references were found. Resolve reference errors in Figma.',
);
};
Loading

0 comments on commit 411a8da

Please sign in to comment.