amzn / style-dictionary

A build system for creating cross-platform styles.
https://styledictionary.com
Apache License 2.0
3.87k stars 543 forks source link

registerTransform transformer function modifying tokens that don't log #955

Open wzamites opened 1 year ago

wzamites commented 1 year ago

I have this transform. Note the console.log and that it modifies the value of the token to the string 'hello':

StyleDictionary.registerTransform({
    type: 'value',
    name: 'test-transformer',
    transformer: (token) => {
        console.log(token)
        return 'hello'
    }
});

I can see in the result that the JS build files that it modifies a token to what would have been this:

  "select": {
    "value": {
      "borderWidth": "5",
      "border": "000000"
    },
    "type": "composition",
  }
};

Modifies to this. Note that both borderWidth and border now have the value hello:

"select": {
    "value": {
      "borderWidth": "hello",
      "border": "hello"
    },
    "type": "composition",
  }

But it does not show up in the console log at all. And this transformation only takes place without a matcher function. If use this matcher function, the transformation to "hello" does not take place: matcher: token => token.type === 'composition',

This also only seems to happen with tokens whose values are not explicitly strings. What am I doing wrong? How can I write a correct matcher that will catch this token? And how can I see it in the console.log output?

JohnathanHuggett commented 1 year ago

I am seeing a similar thing with trying to match token.type = border. When I use type: 'attribute' or type: 'name' I am able to see the matcher work.

const convertBorder = {
  name: 'convertBorder',
  type: 'value',
  // type: 'attribute',
  // type: 'name',
  matcher: token => token.type === 'border',
  transformer: token => {
    console.log(token);
    // return { color: token.value.color, size: token.value.width, style: token.value.style };

    return token.value;

    // if (token.type === 'border') {
    //   return { ...token.value, color: token.value.color, size: token.value.width, style: token.value.style };
    // }
  },
};

because the token is not matched the transformer function never runs. The strange thing is I have other transformers that are working perfectly

Here is my set up

token that is causing the issue

{
  "global-outline-base": {
    "value": {
      "color": "{global-color-focus}",
      "width": "3px",
      "style": "solid"
    },
    "type": "border"
  }
}
Config ``` #!/usr/bin/env node const args = require('minimist')(process.argv.slice(2)); let bank = 'catalyst'; if (args.bank && args.bank !== 'undefined' && args.bank !== '') { bank = args.bank; } console.log(bank); const filters = require('./registers/filters'); const transforms = require('./registers/transforms'); const formatters = require('./registers/formatters'); const StyleDictionary = require('style-dictionary').extend({ source: [`libs/design-tokens/src/tokens/${bank}/**/*.json`], platforms: { js: { transforms: [...transforms.map(t => t.name)], transformGroup: 'js', buildPath: 'libs/design-tokens/src/tokens/', files: [ { destination: 'tokens.js', format: 'tokenFormatter', }, ], }, }, }); filters.forEach(f => StyleDictionary.registerFilter(f)); transforms.forEach(t => StyleDictionary.registerTransform(t)); formatters.forEach(f => StyleDictionary.registerFormat(f)); StyleDictionary.buildAllPlatforms(); ```
Transformers ``` const { isTypeSpacing, convertPxToRem, convertPxBaseLineHeightToUnitless } = require('./utils'); const pxToRem = { name: 'pxToRem', matcher: token => isTypeSpacing(token) || token.type === 'sizing', type: 'value', transformer: token => convertPxToRem(token.name, token.value), }; const convertEdgeSizeName = { name: 'convertEdgeSizeName', type: 'attribute', matcher: token => isTypeSpacing(token) && token.attributes?.property === 'edgeSize', transformer: token => { const value = parseFloat(token.attributes.variant); let newValue; switch (value) { case 0: newValue = 'none'; break; case 1: newValue = 'single'; break; case 2: newValue = 'double'; break; case 3: newValue = 'triple'; break; case 4: newValue = 'quadruple'; break; default: { newValue = `${(value / 8).toFixed(3).replace(/\.?0+$/, '')}x`; } } return { variant: newValue }; }, }; const tokensAttribute = { name: 'attribute/global/tokens', type: 'attribute', matcher: token => token.type !== 'composition', // we can exclude compositions for now they will require a different approach transformer: token => { const attributes = token.name.split('-'); const attrNames = ['category', 'property', 'variant', 'state']; const originalAttrs = token.attributes || {}; const generatedAttrs = attributes.reduce((result, attr, i) => { return i < attrNames.length ? { ...result, [attrNames[i]]: attr } : result; }, {}); return { ...generatedAttrs, ...originalAttrs }; }, }; const convertBoxShadow = { name: 'convertBoxShadow', type: 'value', matcher: token => token.type === 'boxShadow', transformer: token => { const { value } = token; const tokenValue = Array.isArray(value) ? value : [value]; return tokenValue.map(({ x, y, blur, spread, color, type }) => ({ inset: type === 'innerShadow', shadow: `${x} ${y} ${blur} ${spread}`, color, })); }, }; const convertFontSizeAndLineHeight = { name: 'convertFontSizeAndLineHeight', type: 'value', matcher: token => token.type === 'typography', transformer: token => { const { value: { fontSize, lineHeight }, name, } = token; return { fontSize: convertPxToRem(name, fontSize), lineHeight: convertPxBaseLineHeightToUnitless(name, fontSize, lineHeight), }; }, }; const convertBorder = { name: 'convertBorder', type: 'value', // type: 'attribute', // type: 'name', matcher: token => token.type === 'border', transformer: token => { console.log(token); // return { color: token.value.color, size: token.value.width, style: token.value.style }; return token.value; // if (token.type === 'border') { // return { ...token.value, color: token.value.color, size: token.value.width, style: token.value.style }; // } }, }; module.exports = [pxToRem, tokensAttribute, convertEdgeSizeName, convertBoxShadow, convertFontSizeAndLineHeight, convertBorder]; ```
Formatter ``` const { merge, camelCase } = require('lodash'); const addGlobalTokens = (tokens, token) => { const category = token.attributes.category; const property = token.attributes.property; const variant = token.attributes.variant; const state = token.attributes.state; if (!tokens[category]) { tokens[category] = {}; } if (!tokens[category][property]) { tokens[category][property] = {}; } if (!tokens[category][property][variant] && !state) { tokens[category][property][variant] = {}; tokens[category][property][variant] = token.value; } if (state) { tokens[category][property][camelCase(`${variant} ${state}`)] = token.value; } }; const tokenFormatter = { name: 'tokenFormatter', formatter: ({ dictionary }) => { const tokens = {}; dictionary.allTokens.forEach(token => { const category = token.attributes.category; if (category === 'global') { addGlobalTokens(tokens, token); } else { tokens[token.name] = token.value; } }); const formattedDate = `${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()}`; const headerComments = `/** * Do not edit directly * Generated on ${formattedDate} */\n\r`; const tokenExports = `module.exports = ${JSON.stringify(tokens, null, 2)};`; return headerComments + tokenExports; }, }; module.exports = [tokenFormatter]; ```
JohnathanHuggett commented 1 year ago

It does seem to be caused by the border color having a reference to anther token. If I update the value of color to a hex value this prob is resolved. Although, when pointing to a reference it is being resolved in the finial output

JohnathanHuggett commented 1 year ago

@wzamites I am able to work around this issue by using a type other than value. You can set the token value that you wish, and return the token.

example

const convertBorder = {
  name: 'convertBorder',
  type: 'attribute',
  matcher: token => token.type === 'border',
  transformer: token => {
    token.value = { color: token.value.color, size: token.value.width, style: token.value.style };
    return token;
  },
};
JohnathanHuggett commented 1 year ago

@wzamites After a little more research on transformers looks like we just need to add the property transitive: true,

https://amzn.github.io/style-dictionary/#/transforms?id=transitive-transforms