UNPKG

style-dictionary

Version:

Style once, use everywhere. A build system for creating cross-platform styles.

146 lines (131 loc) 5.02 kB
/* * 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 usesReferences from '../utils/references/usesReferences.js'; import GroupMessages from '../utils/groupMessages.js'; import { transformTypes } from '../enums/index.js'; /** * @typedef {import('../../types/Volume.d.ts').Volume} Volume * @typedef {import('../../types/DesignToken.d.ts').TransformedToken} Token * @typedef {import('../../types/Config.d.ts').PlatformConfig} PlatformConfig * @typedef {import('../../types/Config.d.ts').Config} Config * @typedef {import('../../types/Transform.d.ts').Transform} Transform * @typedef {import('../../types/Transform.d.ts').NameTransform} NameTransform */ const TRANSFORM_ERRORS = GroupMessages.GROUP.TransformErrors; const { value: transformTypeValue, name: transformTypeName, attribute: transformTypeAttribute, } = transformTypes; /** * * @param {Token} token * @param {Error} error * @param {string} name * @param {boolean} usesDtcg */ function createErrorMessage(token, error, name, usesDtcg) { const { path, filePath } = token; const value = usesDtcg ? token.$value : token.value; return `Transform Error: token "${path.join('.')}"${filePath ? ` (${filePath})` : ''} with value: \`${JSON.stringify(value, null, 2)}\` (type: ${typeof value}) could not be transformed by "${name}" transform. Threw the following error: ${error ? `\n\n${error.message}\n${error.stack}` : ''}`; } /** * @param {Transform} transform * @param {Token} token * @param {PlatformConfig} config * @param {Config} options * @param {Volume} [vol] */ async function _transformTokenWrapper(transform, token, config, options, vol) { let to_ret; try { to_ret = await transform.transform(token, config, options, vol); } catch (e) { if (e instanceof Error) { const transformError = createErrorMessage(token, e, transform.name, !!options?.usesDtcg); // collect the errors so we can warn the user at the end of the run GroupMessages.add(TRANSFORM_ERRORS, transformError); // Return a sensible fallback value switch (transform.type) { case 'attribute': return token.attributes; case 'name': return token.name; case 'value': return options.usesDtcg ? token.$value : token.value; } } } return to_ret; } /** * Applies all transforms to a token. This is a pure function, * it returns a new token object rather than mutating it inline. * @private * @param {Token} token * @param {PlatformConfig} config * @param {Config} options * @param {Volume} [vol] * @returns {Promise<Token|undefined>} - A new property object with transforms applied. */ export default async function transformToken(token, config, options, vol) { const to_ret = structuredClone(token); const transforms = /** @type {Transform[]} */ (config.transforms) || []; for (let i = 0; i < transforms.length; i++) { const transform = transforms[i]; if (!transform.filter || transform.filter(to_ret, options)) { if (transform.type === transformTypeName) { to_ret.name = /** @type {string} */ ( await _transformTokenWrapper(transform, to_ret, config, options, vol) ); } // Don't try to transform the value if it is referencing another value // Only try to transform if the value is not a string or if it has '{}' if ( transform.type === transformTypeValue && !usesReferences(options.usesDtcg ? token.$value : token.value) ) { // Only transform non-referenced values (from original) // and transitive transforms if the value has been resolved if ( !usesReferences(options.usesDtcg ? token.original.$value : token.original.value) || transform.transitive ) { const transformedValue = await _transformTokenWrapper( transform, to_ret, config, options, vol, ); if (transformedValue === undefined) { return undefined; } to_ret[options.usesDtcg ? '$value' : 'value'] = transformedValue; } } if (transform.type === transformTypeAttribute) { const attrs = /** @type {Record<string, unknown>} */ ( await _transformTokenWrapper(transform, to_ret, config, options, vol) ); to_ret.attributes = { ...to_ret.attributes, ...attrs, }; } } } return to_ret; }