Skip to content

Commit

Permalink
Camel case each token group name, add Salt prefix
Browse files Browse the repository at this point in the history
  • Loading branch information
origami-z committed Jan 29, 2024
1 parent 2ae8308 commit 7fd5fb0
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { describe, expect, test } from "vitest";
import { updateApiResponse } from "../modifyData";
import { updateApiResponse, toCamelCase } from "../modifyData";

describe("toCamelCase", () => {
test("make camel cases", () => {
expect(toCamelCase("Corner Radius")).toEqual("cornerRadius");
});
});

describe("updateApiResponse", () => {
test("filter out remote collections and variables", () => {
Expand Down Expand Up @@ -188,7 +194,7 @@ describe("updateApiResponse", () => {
)
).toBeTruthy();

const output = updateApiResponse(input);
const output = updateApiResponse(input, { addDefault: true });
expect(output.variables["VariableID:1"].name).toEqual("Color/default");
expect(output.variables["VariableID:3"].name).not.toEqual("Color/default");
});
Expand Down
40 changes: 26 additions & 14 deletions packages/export-variables-rest-api/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { join, basename, extname } from "node:path";
import { ProxyAgent } from "proxy-agent";
import "dotenv/config";

import { updateApiResponse } from "./modifyData.js";
import { updateApiResponse, toCamelCase } from "./modifyData.js";

import StyleDictionary from "style-dictionary-utils";
import { w3cTokenJsonParser } from "style-dictionary-utils/dist/parser/w3c-token-json-parser.js";
Expand All @@ -21,12 +21,15 @@ StyleDictionary.registerParser(w3cTokenJsonParser);
const figmaFileId = process.env.FIGMA_FILE_ID;
const figmaAccessToken = process.env.FIGMA_ACCESS_TOKEN;

const TOKEN_PREFIX = "salt";
const OUTPUT_FOLDER = "./tokens";
const SD_BUILD_DIR = "build";
const LOCAL_EXAMPLE_FILE = "salt-example.json";
// Leave blank if you don't want to save the response from REST API call. Give it a name so API response will be saved to be used in `loadLocalMockData`, e.g. LOCAL_EXAMPLE_FILE
const SAVE_API_RESPONSE_PATH = LOCAL_EXAMPLE_FILE;

const processData = processWithStyleDictionary;

/** Either call API or use local mock data */
// callFigmaAPI(processData);
loadLocalMockData(processData);
Expand Down Expand Up @@ -101,10 +104,10 @@ function callFigmaAPI(successCallback) {
);
}

function processData(data) {
console.log("processData", data);
function processWithStyleDictionary(data) {
console.log("processWithStyleDictionary", data);

const newData = updateApiResponse(data);
const newData = updateApiResponse(data, { addDefault: true });
console.log("data with default name suffix", newData);

const tokens = extractTokenFromVariables(newData.variables);
Expand Down Expand Up @@ -138,7 +141,7 @@ function writeTokensToFile(data, tokens) {
// const defaultTransformed = addDefaultToNestedTokens(tokens);
// TODO: we can add default to tokens, what about reference names?

writeFileSync(tokenFilePath, JSON.stringify(modeTokens));
writeFileSync(tokenFilePath, JSON.stringify(modeTokens, null, 2));
console.log("Written token to", tokenFilePath);
}
}
Expand All @@ -162,21 +165,22 @@ function extractTokenFromVariables(allVariablesObj) {
modeId
);
name.split("/").forEach((groupName) => {
obj[groupName] = obj[groupName] || {};
obj = obj[groupName];
const camelGroupName = toCamelCase(groupName);
obj[camelGroupName] = obj[camelGroupName] || {};
obj = obj[camelGroupName];
});
obj.$type = resolvedType === "COLOR" ? "color" : "number";
if (
typeof value === "object" &&
"type" in value &&
value.type === "VARIABLE_ALIAS"
) {
obj.$value = `{${allVariablesObj[value.id].name.replace(
/\//g,
"."
)}}`;
obj.$value = `{${allVariablesObj[value.id].name
.split("/")
.map(toCamelCase)
.join(".")}}`;
} else {
obj.$value = resolvedType === "COLOR" ? rgbToHex(value) : value;
obj.$value = resolvedType === "COLOR" ? stringifyRGBA(value) : value;
}
}
}
Expand Down Expand Up @@ -205,6 +209,7 @@ function buildUsingStyleDictionary() {
web_mode: {
transformGroup: "web",
buildPath: `${SD_BUILD_DIR}/web/${mode}/`,
prefix: TOKEN_PREFIX,
files: [
{
destination: `${mode}.css`,
Expand All @@ -218,6 +223,7 @@ function buildUsingStyleDictionary() {
ios_mode: {
transformGroup: "ios",
buildPath: `${SD_BUILD_DIR}/ios/${mode}/`,
prefix: TOKEN_PREFIX,
files: [
{
destination: `${mode}.swift`,
Expand All @@ -235,6 +241,7 @@ function buildUsingStyleDictionary() {
// TODO: find out why size tokens, e.g. `--size-unit` is not getting `px`
transformGroup: "web",
buildPath: `${SD_BUILD_DIR}/web/${density}/${subfolder}/`,
prefix: TOKEN_PREFIX,
files: [
{
destination: `${cornerRadiusMode}.css`,
Expand All @@ -248,6 +255,7 @@ function buildUsingStyleDictionary() {
ios_density: {
transformGroup: "ios",
buildPath: `${SD_BUILD_DIR}/ios/${density}/${subfolder}/`,
prefix: TOKEN_PREFIX,
files: [
{
destination: `${cornerRadiusMode}.swift`,
Expand All @@ -267,7 +275,7 @@ function buildUsingStyleDictionary() {
source: [
`${OUTPUT_FOLDER}/Mode/${mode}.json`,
// `${OUTPUT_FOLDER}/Raw/*.json`,
`${OUTPUT_FOLDER}/Foundations/*.json`,
`${OUTPUT_FOLDER}/Foundation/*.json`,
],
platforms: getModePlatform(mode),
}).buildAllPlatforms();
Expand All @@ -290,12 +298,16 @@ function buildUsingStyleDictionary() {
console.log("StyleDictionary build done");
}

function rgbToHex({ r, g, b, a }) {
function stringifyRGBA({ r, g, b, a }) {
if (a !== 1) {
return `rgba(${[r, g, b]
.map((n) => Math.round(n * 255))
.join(", ")}, ${a.toFixed(4)})`;
}
// To RGB
return `rgb(${[r, g, b].map((n) => Math.round(n * 255)).join(", ")})`;

// To HEX
const toHex = (value) => {
const hex = Math.round(value * 255).toString(16);
return hex.length === 1 ? "0" + hex : hex;
Expand Down
12 changes: 10 additions & 2 deletions packages/export-variables-rest-api/src/modifyData.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
export function toCamelCase(str) {
return str.replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, function (match, index) {
if (+match === 0) return ""; // or if (/\s+/.test(match)) for white spaces
return index === 0 ? match.toLowerCase() : match.toUpperCase();
});
}

/**
* Filter out remote variables and collections, add "/default" suffix to any
* variable (in the same collection group) which also served as group name.
Expand All @@ -8,7 +15,8 @@
* @param {*} data Figma variables REST API response's `meta` data
* @returns new data with remote filtered out and variable name modified
*/
export function updateApiResponse(data) {
export function updateApiResponse(data, options = { addDefault: true }) {
const { addDefault } = options;
// New data object
const newData = { variableCollections: {}, variables: {} };

Expand Down Expand Up @@ -43,7 +51,7 @@ export function updateApiResponse(data) {
const element = variablesInCollection[index];
newData.variables[element.id] = element;

if (prevVariable !== undefined) {
if (addDefault && prevVariable !== undefined) {
// If name has exactly one more "/", previous one needs to be appended a "default"
const lastSlashIndex = element.name.lastIndexOf("/");
if (lastSlashIndex !== -1) {
Expand Down

0 comments on commit 7fd5fb0

Please sign in to comment.