amzn / style-dictionary

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

Reference doesn't exist in a value transform that outputs { fill: #000000} #674

Open arielwb opened 3 years ago

arielwb commented 3 years ago

Hi!

I'm writing a transform that removes the fill attribute from an inline svg and then adds a style tag with the fill information. It's a very specific series of transformations to enable us to better style this assets in react-native

Anyways, the error is that i'm getting is: Reference doesn't exist: asset.brand.custom.a.file.value tries to reference fill: #000000, which is not defined

What I'm trying to do: transform a svg file from

<svg>
  <path fill="#000000"></path>
</svg>

to

<svg>
  <style>
      path {
        fill: #000000
      }
  </style>
  <path></path>
</svg>

I believe that the error is because of the pattern { fill: #000000 } and style dictionary is confusing this with a reference? I've tried to use the transitive and the outputReferences props with no luck. I also tried escaping this characters, same error.

Is there a way to get around this?

Thank you very much!

dbanksdesign commented 3 years ago

That is correct, Style Dictionary is confusing that with a reference. It looks for anything in a string that has opening and closing curly braces. Generally I keep SVGs in files and then have the value of the token as the path to that file then use a custom action to generate artifacts. Part of the reason to have separate SVG files and not keep it as inline is performance. Style Dictionary does not handle resolving multiple references in a large string very well. I have an example here: https://github.com/dbanksdesign/style-dictionary-dark-mode

Token file: https://github.com/dbanksdesign/style-dictionary-dark-mode/blob/main/tokens/asset/svg.json5 SVG files: https://github.com/dbanksdesign/style-dictionary-dark-mode/tree/main/assets/svg Custom action to handle SVGs: https://github.com/dbanksdesign/style-dictionary-dark-mode/blob/main/actions/generateGraphics.js

You could do something similar and rather than use a transform, manipulate the SVG in an action. Or depending on your use case, you could skip Style Dictionary completely if you are just manipulating the SVG code and not really using other parts of Style Dictionary.

arielwb commented 3 years ago

Thank you for your reply!

I will leave some more context here as it may be helpful to someone :)

I agree that inlining svg is not the best approach and it was not our first choice hahaha The thing is, we have a multi theme multi platform design system, and we want to control the brand assets with the theme. Plus each design system lib have a component logo to abstract loading the asset, changing size and color (in run time). Also, we want the easiest setup for our consumer as possible. We tried to avoid approaches that involved having to mess around with bundling configs. Each theme has only 4 assets, and this list will not grow.

For most platforms, the theme contains only the name of the asset the design system component handle the path building (it can be remote, local and they can move the assets folder around without having to change the theme library), but for react-native, the most straight forward approach that we found that meet our requirements is using react-native-svg and working with the svg as strings.

Its not optimal, but we do optimize the svgs prior to inlining (we use svgo and a custom script to remove and replace base64 string with remote png images)

About the issue with the {} pattern and style dictionary: Currently my workaround is to do a transform using other characters like @@@ fill: #000 !!! and then after the build I replace this characters with a script /@@@/ , '{' /!!!/ , '}' 👀

I now that this is a very specific issue, but I wonder if others have stumbled into this for other needs and if would be acceptable to style-dictionary having a way to customize the reference characters. To me, double curly brackets would solve the issue I guess {{my.token.reference}}

chazzmoney commented 3 years ago

Hi, sorry for the delay. I think you can do this today, but I'm not 100% sure because we have no tests for it.

The reference detection is created via a regex using variables for opening and closing characters.

I believe that I traced the options for that function back to the platform config, which maybe isn't the best place for them but it might work... if you were to do something like this:

{
  "source": ["tokens/**/*.json"],
  "platforms": {
    "scss": {
      "opening_character": "\\{\\{",
      "closing_character": "\\}\\}",
      "transformGroup": "scss",
      "buildPath": "build/scss/",
      "files": [{
        "destination": "_variables.scss",
        "format": "scss/variables"
      }]
    }
  }
}

I'm using backslashes because it will end up going into the string and then into the regex creator, so escaping is going to be a real pain. I'm not sure I did it right in this example. You might try something stupid like "XXX" to see if it works at all first.

Note that ALL your token references would have to use this syntax and ALL your platforms would have to have this config. Not ideal.

But it might work.

Also, we would love to see a pull request if you have a good solution in mind!

Hope this helps.

chazzmoney commented 2 years ago

Hi @arielwb did you still need help here?

mchiasson-youi commented 1 year ago

I am having the same issue with my custom transform trying to return a string that has an opening and closing curly braces for a custom scripting language. I'm not sure how to solve this.

StyleDictionaryPackage.registerTransform({
  name: 'myTransform',
  type: 'value',
  matcher: myMatcher
  transformer: (token) => {
    const {value} = token;
    return `{ ${value} }`
  }
})