style-dictionary
Version:
Style once, use everywhere. A build system for creating cross-platform styles.
124 lines (117 loc) • 4.28 kB
JavaScript
/*
* Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with
* the License. A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
import flattenTokens from './flattenTokens.js';
/**
* @typedef {import('../../types/DesignToken').DesignTokens} DesignTokens
* @typedef {import('../../types/DesignToken').DesignToken} DesignToken
* @typedef {import('../../types/DesignToken').TransformedTokens} TransformedTokens
* @typedef {import('../../types/DesignToken').TransformedToken} TransformedToken
* @typedef {DesignToken|TransformedToken} Token
* @typedef {DesignTokens|TransformedTokens} Tokens
* @typedef {Map<string, Token>} TokenMap
* @typedef {{'map': TokenMap, 'object': Tokens, 'array': Token[]}} TypeMap
*/
/**
* @template {Token} T
* @param {Array<T>} flattenedTokens
* @returns {Map<string, T>}
*/
function convertToTokenMap(flattenedTokens) {
return new Map(
flattenedTokens.map((t) => {
return [/** @type {string} */ (t.key), t];
}),
);
}
/**
* @param {Token[]} tokenArray
* @returns
*/
function convertToTokenObject(tokenArray) {
const obj = /** @type {TransformedTokens} */ ({});
tokenArray.forEach((token) => {
const { key } = token;
const keyArr = /** @type {string} */ (key).replace('{', '').replace('}', '').split('.');
let slice = obj;
keyArr.forEach((k, i, arr) => {
if (slice[k] === undefined) {
slice[k] = {};
}
// end
if (i === arr.length - 1) {
slice[k] = token;
}
slice = slice[k];
});
});
return obj;
}
/**
* Allows converting your tokens to one of the 3 data structures available:
* 'map' -> JavaScript Map
* 'object' -> JavaScript Object
* 'array' -> JavaScript Array
*
* The input format is automatically detected
*
* The return type depends on the "output" prop input, hence the use of a generic / type mapping
* @template {keyof TypeMap} T
* @param {Tokens | Token[] | TokenMap} tokens
* @param {{output: T, usesDtcg?: boolean}} options
* @return {TypeMap[T]}
*/
export function convertTokenData(tokens, options) {
const { usesDtcg, output } = options;
/** @type {keyof TypeMap} */
const input = Array.isArray(tokens) ? 'array' : tokens instanceof Map ? 'map' : 'object';
switch (output) {
case 'array': {
if (input === 'object') {
// adds "key" prop
return /** @type {TypeMap[T]} */ (flattenTokens(/** @type {Tokens} */ (tokens), usesDtcg));
} else if (input === 'map') {
// we assume map is always keyed with "key" prop
return /** @type {TypeMap[T]} */ (
Array.from(/** @type {TokenMap} */ (tokens)).map(([key, value]) => ({
key,
...value,
}))
);
}
break;
}
case 'map': {
if (input === 'object') {
// adds "key" prop
const flattened = flattenTokens(/** @type {Tokens} */ (tokens), usesDtcg);
return /** @type {TypeMap[T]} */ (convertToTokenMap(flattened));
} else if (input === 'array') {
// we assume that if it's an array, this array was created by flattenTokens which adds the "key" props
return /** @type {TypeMap[T]} */ (convertToTokenMap(/** @type {Token[]} */ (tokens)));
}
break;
}
case 'object': {
if (input === 'map') {
// we assume map is always keyed with "key" prop
const arr = Array.from(/** @type {TokenMap} */ (tokens).values());
return /** @type {TypeMap[T]} */ (convertToTokenObject(arr));
} else if (input === 'array') {
// we assume that if it's an array, this array was created by flattenTokens which adds the "key" props
return /** @type {TypeMap[T]} */ (convertToTokenObject(/** @type {Token[]} */ (tokens)));
}
break;
}
}
return /** @type {TypeMap[T]} */ (tokens);
}