amzn / style-dictionary

A build system for creating cross-platform styles.
https://amzn.github.io/style-dictionary/#/
Apache License 2.0
3.72k stars 524 forks source link

Custom transform matcher not working as expected #836

Open MysticEarth opened 2 years ago

MysticEarth commented 2 years ago

I've written a custom transform in my style-dictionary.config.js, following the docs at https://amzn.github.io/style-dictionary/#/config?id=configuration-file-formats. The transform works if I omit the matcher function, but as expected it transforms every token which is not what I want.

But when I add the matcher function below, no token is transformed since token.attributes = {}.

matcher: function (token) {
  return token.attributes.category === 'color';
},

The complete transform:

 transform: {
    test: {
      type: 'value',
      matcher: function (token) {
       // `token.attributes.category` is always `undefined`
        return token.attributes.category === 'color';
      },
      transformer: function (token) {
        var color = Color(token.value);
        if (color.getAlpha() === 1) {
          const rgb = color.toRgb();
          return `rgb(${rgb.r} ${rgb.g} ${rgb.b} / var(--color-opacity))`;
        } else {
          return color.toRgbString();
        }
      },
    },
  },

Is this a known issue? Or a misconfiguration perhaps?

dbanksdesign commented 1 year ago

Short answer: you will need to add the attribute/cti transform at the beginning of your transforms array.

Explanation: transforms run sequentially and so one transform can modify a token in a way for other transforms to use (in a matcher or transformer). The attribute/cti transform adds data to the attributes object on each token and for most examples is the first transform in the transforms array so that other transforms can use that attributes object in their matcher function.

belmuseri commented 1 year ago

@dbanksdesign Hi! I have a similar situation, where do I need to add the attribute/cti ? I read the documentation but I'm still a little confused

json

"box-shadow": {
  "type": "shadow",
  "value": {
    "x": "0",
    "y": "0",
    "blur": "0",
    "spread": "3",
    "color": "#000000",
    "type": "shadow"
   }
}

build.js

const StyleDictionary = require('style-dictionary').extend({
  source: ['tokens/**/test.json'],
  platforms: {
    scss: {
      transformGroup: 'scss',
      buildPath: 'build/scss/',
      files: [{
        destination: 'test.scss',
        format: 'scss/variables',
      }]
    }
  }
});

Dictionary.registerTransform({
  name: 'scss/variables',
  type: 'value',
  matcher: function(prop) {
    return prop.attributes.category === 'shadow';
  },
  transformer: function(prop) {
   //destructure shadow values from original token value
    const {
      x,
      y,
      blur,
      spread,
      color
    } = prop.original.value

    return `${x}px ${y}px ${blur}px ${spread}px ${color}`
  }
});
raza-jamil-reckon commented 12 months ago

@dbanksdesign Bump, seeing this issue as well. I've added the attribute/cti as the first transform. When I log inside the transform I can see the attribute and it's evaluated correctly but when I return the boolean it doesn't match.

StyleDictionary.registerTransform({
  type: 'value',
  name: `sizeValueToPx`,
  matcher: (token) => {
    const isSize = token.attributes.category === 'sizes';
    console.log(isSize) // true
    return isSize;
  },
  transformer: (prop) => {
    console.log('matched'); // never gets hit
    return `${parseFloat(prop.original.value)}px`;
  },
});

But when I just return true from the matcher it works. I've done all sorts of debugging up and down to make sure the condition resolves to boolean but can't get it to work.

EdwardHinkle commented 10 months ago

Just to throw in here as well, I have the same problem.

This is my config for the transform:

export default {
    name: 'radiusToComposeShape',
    type: 'value',
    matcher: (token) => {
        const isRadius = token.attributes.category === `radius`;
        if (isRadius) {
            console.log('checking radiusToComposeShape', token, isRadius);
        }
        return isRadius;
    },
    transformer: (token) => {
        console.log('!! transforming radiusToComposeShape', token);
        return `RoundedCornerShape(${token.value})`;
    }
}

I get the console log in the matcher, I get true, I am returning that SAME variable that was logged and the transformer function never runs.

However if I return true without a variable it works (of course for every token)

EdwardHinkle commented 10 months ago

@dbanksdesign Following up on this, it seems like transforms aren't being always run in the correct order.

This screenshot is my terminal output with console logs. Notice how radius to compose shape is being called before RADIUS

Screenshot 2023-09-01 at 10 36 59 AM

Here is my array of transforms:

transforms: [
    `company/attribute/cti`,
    `attribute/tokenGroup`,
    `attribute/primitiveColor`,
    `name/cti/pascal`,
    `size/compose/remToDp`,
    `color/composeColor`,
    `radiusToComposeShape`,
],

Here is my company/attribute/cti config:

import StyleDictionary from 'style-dictionary';

export default {
    name: 'company/attribute/cti',
    type: 'attribute',
    transformer: (prop) => {
        const ctiAttributes = StyleDictionary.transform['attribute/cti'].transformer(prop);

        if (ctiAttributes.category === 'colors') {
            ctiAttributes.category = 'color';
        }

        if (ctiAttributes.category === 'sizing') {
            ctiAttributes.category = 'size';
        }

        if (ctiAttributes.category === 'radius') {
            console.log("RADIUS", ctiAttributes, prop);
        }

        return ctiAttributes;
    }
}

You can see the config for radiusToComposeShape above

torleifhalseth commented 3 weeks ago

Is there any update on this issue? I am experiencing the same problem: the matcher is matched, but the transformer is not used. I find this confusing because other custom transforms work as expected. I will keep on debugging this locally. If anyone has a possible solution, I would be forever grateful 🙌

The previous solution mentioned by @dbanksdesign did not change the output in my setup.

torleifhalseth commented 3 weeks ago

In my context, I had to set the transform to transitive: true. Then, the transform was applied to referenced values as well as absolute values.