mirror of
https://github.com/penpot/penpot.git
synced 2025-01-21 22:22:43 -05:00
♻ Make process-sd-tokens
more readable
This commit is contained in:
parent
f5596b2b3f
commit
0923dcc43f
2 changed files with 88 additions and 50 deletions
|
@ -41,33 +41,73 @@
|
||||||
:warnings "silent"
|
:warnings "silent"
|
||||||
:errors {:brokenReferences "console"}}})
|
:errors {:brokenReferences "console"}}})
|
||||||
|
|
||||||
(defn process-sd-tokens [sd-tokens get-origin-token]
|
(defn parse-sd-token-color-value
|
||||||
(reduce
|
"Parses `value` of a color `sd-token` into a map like `{:value 1 :unit \"px\"}`.
|
||||||
(fn [acc ^js sd-token]
|
If the value is not parseable and/or has missing references returns a map with `:errors`."
|
||||||
(let [{:keys [type] :as origin-token} (get-origin-token sd-token)
|
[value]
|
||||||
value (.-value sd-token)
|
(if-let [tc (tinycolor/valid-color value)]
|
||||||
token-or-err (case type
|
|
||||||
:color (if-let [tc (tinycolor/valid-color value)]
|
|
||||||
{:value value :unit (tinycolor/color-format tc)}
|
{:value value :unit (tinycolor/color-format tc)}
|
||||||
{:errors [(wte/error-with-value :error.token/invalid-color value)]})
|
{:errors [(wte/error-with-value :error.token/invalid-color value)]}))
|
||||||
(or (wtt/parse-token-value value)
|
|
||||||
(if-let [references (-> (ctob/find-token-value-references value)
|
(defn parse-sd-token-dimensions-value
|
||||||
(seq))]
|
"Parses `value` of a dimensions `sd-token` into a map like `{:value 1 :unit \"px\"}`.
|
||||||
|
If the `value` is not parseable and/or has missing references returns a map with `:errors`."
|
||||||
|
[value]
|
||||||
|
(or
|
||||||
|
(wtt/parse-token-value value)
|
||||||
|
(if-let [references (seq (ctob/find-token-value-references value))]
|
||||||
{:errors [(wte/error-with-value :error.style-dictionary/missing-reference references)]
|
{:errors [(wte/error-with-value :error.style-dictionary/missing-reference references)]
|
||||||
:references references}
|
:references references}
|
||||||
{:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value value)]})))
|
{:errors [(wte/error-with-value :error.style-dictionary/invalid-token-value value)]})))
|
||||||
output-token (if (:errors token-or-err)
|
|
||||||
(merge origin-token token-or-err)
|
(defn process-sd-tokens
|
||||||
|
"Converts a StyleDictionary dictionary with resolved tokens (aka `sd-tokens`) back to clojure.
|
||||||
|
The `get-origin-token` argument should be a function that takes an `sd-token` and returns the original penpot token, so we can merge the resolved attributes back in.
|
||||||
|
|
||||||
|
The `sd-token` will have references in `value` replaced with the computed value as a string.
|
||||||
|
Here's an example for a `sd-token`:
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
name: 'token.with.reference',
|
||||||
|
value: '12px',
|
||||||
|
type: 'border-radius',
|
||||||
|
path: ['token', 'with', 'reference'],
|
||||||
|
|
||||||
|
// The penpot origin token converted to a js object
|
||||||
|
original: {
|
||||||
|
name: 'token.with.reference',
|
||||||
|
value: '{referenced.token}',
|
||||||
|
type: 'border-radius'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We also convert `sd-token` value string into a unit that can be used as penpot shape attributes.
|
||||||
|
- Dimensions like '12px' will be converted into numbers
|
||||||
|
- Colors will be validated & converted to hex
|
||||||
|
|
||||||
|
Lastly we check for errors in each token
|
||||||
|
`sd-token` will keep the missing references in the `value` (E.g \"{missing} + {existing}\" -> \"{missing} + 12px\")
|
||||||
|
So we parse out the missing references and add them to `:errors` in the final token."
|
||||||
|
[sd-tokens get-origin-token]
|
||||||
|
(reduce
|
||||||
|
(fn [acc ^js sd-token]
|
||||||
|
(let [origin-token (get-origin-token sd-token)
|
||||||
|
value (.-value sd-token)
|
||||||
|
parsed-token-value (case (:type origin-token)
|
||||||
|
:color (parse-sd-token-color-value value)
|
||||||
|
(parse-sd-token-dimensions-value value))
|
||||||
|
output-token (if (:errors parsed-token-value)
|
||||||
|
(merge origin-token parsed-token-value)
|
||||||
(assoc origin-token
|
(assoc origin-token
|
||||||
:resolved-value (:value token-or-err)
|
:resolved-value (:value parsed-token-value)
|
||||||
:unit (:unit token-or-err)))]
|
:unit (:unit parsed-token-value)))]
|
||||||
(assoc acc (wtt/token-identifier output-token) output-token)))
|
(assoc acc (:name output-token) output-token)))
|
||||||
{} sd-tokens))
|
{} sd-tokens))
|
||||||
|
|
||||||
(defprotocol IStyleDictionary
|
(defprotocol IStyleDictionary
|
||||||
(add-tokens [_ tokens])
|
(add-tokens [_ tokens])
|
||||||
(enable-debug [_])
|
(enable-debug [_])
|
||||||
(set-config [_])
|
|
||||||
(get-config [_])
|
(get-config [_])
|
||||||
(build-dictionary [_]))
|
(build-dictionary [_]))
|
||||||
|
|
||||||
|
@ -79,9 +119,6 @@
|
||||||
(enable-debug [_]
|
(enable-debug [_]
|
||||||
(StyleDictionary. (update config :log merge {:verbosity "verbose"})))
|
(StyleDictionary. (update config :log merge {:verbosity "verbose"})))
|
||||||
|
|
||||||
(set-config [_]
|
|
||||||
(StyleDictionary. config))
|
|
||||||
|
|
||||||
(get-config [_]
|
(get-config [_]
|
||||||
config)
|
config)
|
||||||
|
|
||||||
|
@ -111,7 +148,14 @@
|
||||||
(defn resolve-tokens-interactive+
|
(defn resolve-tokens-interactive+
|
||||||
"Interactive check of resolving tokens.
|
"Interactive check of resolving tokens.
|
||||||
Uses a ids map to backtrace the original token from the resolved StyleDictionary token.
|
Uses a ids map to backtrace the original token from the resolved StyleDictionary token.
|
||||||
This is necessary as the user might have removed/changed the token name but we still want to validate the value interactively."
|
|
||||||
|
We have to pass in all tokens from all sets in the entire library to style dictionary
|
||||||
|
so we know if references are missing / to resolve them and possibly show interactive previews (in the tokens form) to the user.
|
||||||
|
|
||||||
|
Since we're using the :name path as the identifier we might be throwing away or overriding tokens in the tree that we pass to StyleDictionary.
|
||||||
|
|
||||||
|
So to get back the original token from the resolved sd-token (see my updates for what an sd-token is) we include a temporary :id for the token that we pass to StyleDictionary,
|
||||||
|
this way after the resolving computation we can restore any token, even clashing ones with the same :name path by just looking up that :id in the ids map."
|
||||||
[tokens]
|
[tokens]
|
||||||
(let [{:keys [tokens-tree ids]} (ctob/backtrace-tokens-tree tokens)]
|
(let [{:keys [tokens-tree ids]} (ctob/backtrace-tokens-tree tokens)]
|
||||||
(resolve-tokens-tree+ tokens-tree #(get ids (sd-token-uuid %)))))
|
(resolve-tokens-tree+ tokens-tree #(get ids (sd-token-uuid %)))))
|
||||||
|
|
|
@ -1,31 +1,25 @@
|
||||||
(ns token-tests.style-dictionary-test
|
(ns token-tests.style-dictionary-test
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.transit :as tr]
|
||||||
|
[app.common.types.tokens-lib :as ctob]
|
||||||
[app.main.ui.workspace.tokens.style-dictionary :as sd]
|
[app.main.ui.workspace.tokens.style-dictionary :as sd]
|
||||||
[beicon.v2.core :as rx]
|
[beicon.v2.core :as rx]
|
||||||
[app.common.transit :as tr]
|
|
||||||
[cljs.test :as t :include-macros true]
|
[cljs.test :as t :include-macros true]
|
||||||
[promesa.core :as p]
|
[promesa.core :as p]))
|
||||||
[app.common.types.tokens-lib :as ctob]))
|
|
||||||
|
|
||||||
(def border-radius-token
|
|
||||||
{:value "12px"
|
|
||||||
:name "borderRadius.sm"
|
|
||||||
:type :border-radius})
|
|
||||||
|
|
||||||
(def reference-border-radius-token
|
|
||||||
{:value "{borderRadius.sm} * 2"
|
|
||||||
:name "borderRadius.md-with-dashes"
|
|
||||||
:type :border-radius})
|
|
||||||
|
|
||||||
(def tokens (d/ordered-map
|
|
||||||
(:name border-radius-token) border-radius-token
|
|
||||||
(:name reference-border-radius-token) reference-border-radius-token))
|
|
||||||
|
|
||||||
(t/deftest resolve-tokens-test
|
(t/deftest resolve-tokens-test
|
||||||
(t/async
|
(t/async
|
||||||
done
|
done
|
||||||
(t/testing "resolves tokens using style-dictionary from a ids map"
|
(t/testing "resolves tokens using style-dictionary from a ids map"
|
||||||
|
(let [tokens (-> (ctob/make-tokens-lib)
|
||||||
|
(ctob/add-set (ctob/make-token-set :name "core"))
|
||||||
|
(ctob/add-token-in-set "core" (ctob/make-token {:name "borderRadius.sm"
|
||||||
|
:value "12px"
|
||||||
|
:type :border-radius}))
|
||||||
|
(ctob/add-token-in-set "core" (ctob/make-token {:value "{borderRadius.sm} * 2"
|
||||||
|
:name "borderRadius.md-with-dashes"
|
||||||
|
:type :border-radius}))
|
||||||
|
(ctob/get-all-tokens))]
|
||||||
(-> (sd/resolve-tokens+ tokens)
|
(-> (sd/resolve-tokens+ tokens)
|
||||||
(p/finally
|
(p/finally
|
||||||
(fn [resolved-tokens]
|
(fn [resolved-tokens]
|
||||||
|
@ -33,7 +27,7 @@
|
||||||
(t/is (= "px" (get-in resolved-tokens ["borderRadius.sm" :unit])))
|
(t/is (= "px" (get-in resolved-tokens ["borderRadius.sm" :unit])))
|
||||||
(t/is (= 24 (get-in resolved-tokens ["borderRadius.md-with-dashes" :resolved-value])))
|
(t/is (= 24 (get-in resolved-tokens ["borderRadius.md-with-dashes" :resolved-value])))
|
||||||
(t/is (= "px" (get-in resolved-tokens ["borderRadius.md-with-dashes" :unit])))
|
(t/is (= "px" (get-in resolved-tokens ["borderRadius.md-with-dashes" :unit])))
|
||||||
(done)))))))
|
(done))))))))
|
||||||
|
|
||||||
(t/deftest process-json-stream-test
|
(t/deftest process-json-stream-test
|
||||||
(t/async
|
(t/async
|
||||||
|
|
Loading…
Add table
Reference in a new issue