diff --git a/common/src/app/common/types/tokens_lib.cljc b/common/src/app/common/types/tokens_lib.cljc index f53c806d3..06b190340 100644 --- a/common/src/app/common/types/tokens_lib.cljc +++ b/common/src/app/common/types/tokens_lib.cljc @@ -665,20 +665,20 @@ ["$value" :map] ["$type" :string]]])) -(defn has-legacy-format? +(defn get-json-format "Searches through parsed token file and returns: - - true when first node satisfies `legacy-node?` predicate - - false when first node satisfies `dtcg-node?` predicate - - nil if neither combination is found" + - `:json-format/legacy` when first node satisfies `legacy-node?` predicate + - `:json-format/dtcg` when first node satisfies `dtcg-node?` predicate + - `nil` if neither combination is found" ([data] - (has-legacy-format? data legacy-node? dtcg-node?)) + (get-json-format data legacy-node? dtcg-node?)) ([data legacy-node? dtcg-node?] (let [branch? map? children (fn [node] (vals node)) check-node (fn [node] (cond - (legacy-node? node) true - (dtcg-node? node) false + (legacy-node? node) :json-format/legacy + (dtcg-node? node) :json-format/dtcg :else nil)) walk (fn walk [node] (lazy-seq @@ -690,6 +690,10 @@ (filter some?) first)))) +(defn single-set? [data] + (and (not (contains? data "$metadata")) + (not (contains? data "$themes")))) + ;; DEPRECATED (defn walk-sets-tree-seq "Walk sets tree as a flat list. @@ -826,6 +830,24 @@ (declare make-tokens-lib) +(defn- legacy-nodes->dtcg-nodes [sets-data] + (walk/postwalk + (fn [node] + (cond-> node + (and (map? node) + (contains? node "value") + (sequential? (get node "value"))) + (update "value" + (fn [seq-value] + (map #(set/rename-keys % {"type" "$type"}) seq-value))) + + (and (map? node) + (and (contains? node "type") + (contains? node "value"))) + (set/rename-keys {"value" "$value" + "type" "$type"}))) + sets-data)) + (defprotocol ITokensLib "A library of tokens, sets and themes." (set-path-exists? [_ path] "if a set at `path` exists") @@ -844,6 +866,8 @@ Will return a value that matches this schema: (get-active-themes-set-tokens [_] "set of set names that are active in the the active themes") (encode-dtcg [_] "Encodes library to a dtcg compatible json string") (decode-dtcg-json [_ parsed-json] "Decodes parsed json containing tokens and converts to library") + (decode-single-set-json [_ set-name tokens] "Decodes parsed json containing single token set and converts to library") + (decode-single-set-legacy-json [_ set-name tokens] "Decodes parsed legacy json containing single token set and converts to library") (decode-legacy-json [_ parsed-json] "Decodes parsed legacy json containing tokens and converts to library") (get-all-tokens [_] "all tokens in the lib") (validate [_])) @@ -1287,6 +1311,17 @@ Will return a value that matches this schema: (assoc-in ["$metadata" "activeThemes"] active-themes-clear) (assoc-in ["$metadata" "activeSets"] active-sets)))) + (decode-single-set-json [this set-name tokens] + (assert (map? tokens) "expected a map data structure for `data`") + + (add-set this (make-token-set :name (normalize-set-name set-name) + :tokens (flatten-nested-tokens-json tokens "")))) + + + (decode-single-set-legacy-json [this set-name tokens] + (assert (map? tokens) "expected a map data structure for `data`") + (decode-single-set-json this set-name (legacy-nodes->dtcg-nodes tokens))) + (decode-dtcg-json [_ data] (assert (map? data) "expected a map data structure for `data`") @@ -1370,22 +1405,7 @@ Will return a value that matches this schema: (decode-legacy-json [this parsed-legacy-json] (let [other-data (select-keys parsed-legacy-json ["$themes" "$metadata"]) sets-data (dissoc parsed-legacy-json "$themes" "$metadata") - dtcg-sets-data (walk/postwalk - (fn [node] - (cond-> node - (and (map? node) - (contains? node "value") - (sequential? (get node "value"))) - (update "value" - (fn [seq-value] - (map #(set/rename-keys % {"type" "$type"}) seq-value))) - - (and (map? node) - (and (contains? node "type") - (contains? node "value"))) - (set/rename-keys {"value" "$value" - "type" "$type"}))) - sets-data)] + dtcg-sets-data (legacy-nodes->dtcg-nodes sets-data)] (decode-dtcg-json this (merge other-data dtcg-sets-data)))) (get-all-tokens [this] diff --git a/common/test/common_tests/types/data/legacy-single-set.json b/common/test/common_tests/types/data/legacy-single-set.json new file mode 100644 index 000000000..e69153a76 --- /dev/null +++ b/common/test/common_tests/types/data/legacy-single-set.json @@ -0,0 +1,6 @@ +{"color": + {"red": + {"100": + {"value":"red", + "type":"color", + "description":""}}}} diff --git a/common/test/common_tests/types/data/single-set.json b/common/test/common_tests/types/data/single-set.json new file mode 100644 index 000000000..d558781ae --- /dev/null +++ b/common/test/common_tests/types/data/single-set.json @@ -0,0 +1,6 @@ +{"color": + {"red": + {"100": + {"$value":"red", + "$type":"color", + "$description":""}}}} diff --git a/common/test/common_tests/types/tokens_lib_test.cljc b/common/test/common_tests/types/tokens_lib_test.cljc index 0b80eed16..0a41f37e5 100644 --- a/common/test/common_tests/types/tokens_lib_test.cljc +++ b/common/test/common_tests/types/tokens_lib_test.cljc @@ -1371,6 +1371,30 @@ (t/testing "invalid tokens got discarded" (t/is (nil? (get-set-token "typography" "H1.Bold"))))))) +#?(:clj + (t/deftest single-set-legacy-json-decoding + (let [json (-> (slurp "test/common_tests/types/data/legacy-single-set.json") + (tr/decode-str)) + lib (ctob/decode-single-set-legacy-json (ctob/ensure-tokens-lib nil) "single_set" json) + get-set-token (fn [set-name token-name] + (some-> (ctob/get-set lib set-name) + (ctob/get-token token-name)))] + (t/is (= '("single_set") (ctob/get-ordered-set-names lib))) + (t/testing "token added" + (t/is (some? (get-set-token "single_set" "color.red.100"))))))) + +#?(:clj + (t/deftest single-set-dtcg-json-decoding + (let [json (-> (slurp "test/common_tests/types/data/single-set.json") + (tr/decode-str)) + lib (ctob/decode-single-set-json (ctob/ensure-tokens-lib nil) "single_set" json) + get-set-token (fn [set-name token-name] + (some-> (ctob/get-set lib set-name) + (ctob/get-token token-name)))] + (t/is (= '("single_set") (ctob/get-ordered-set-names lib))) + (t/testing "token added" + (t/is (some? (get-set-token "single_set" "color.red.100"))))))) + #?(:clj (t/deftest dtcg-encoding-decoding-json (let [json (-> (slurp "test/common_tests/types/data/tokens-multi-set-example.json") diff --git a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs index 8a3b3d150..5cb6da341 100644 --- a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs @@ -40,6 +40,7 @@ [app.util.i18n :refer [tr]] [app.util.webapi :as wapi] [beicon.v2.core :as rx] + [cuerdas.core :as str] [okulary.core :as l] [potok.v2.core :as ptk] [rumext.v2 :as mf] @@ -367,9 +368,10 @@ (fn [event] (let [file (-> (dom/get-target event) (dom/get-files) - (first))] + (first)) + file-name (str/replace (.-name file) ".json" "")] (->> (wapi/read-file-as-text file) - (sd/process-json-stream) + (sd/process-json-stream {:file-name file-name}) (rx/subs! (fn [lib] (st/emit! (ptk/data-event ::ev/event {::ev/name "import-tokens"}) (dt/import-tokens-lib lib))) diff --git a/frontend/src/app/main/ui/workspace/tokens/style_dictionary.cljs b/frontend/src/app/main/ui/workspace/tokens/style_dictionary.cljs index d6fc48b7c..a31f0635a 100644 --- a/frontend/src/app/main/ui/workspace/tokens/style_dictionary.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/style_dictionary.cljs @@ -6,6 +6,7 @@ [app.common.schema :as sm] [app.common.transit :as t] [app.common.types.tokens-lib :as ctob] + [app.main.refs :as refs] [app.main.ui.workspace.tokens.errors :as wte] [app.main.ui.workspace.tokens.tinycolor :as tinycolor] [app.main.ui.workspace.tokens.token :as wtt] @@ -249,33 +250,51 @@ (= header-2 "Reference Errors:")) errors))) -(defn process-json-stream [data-stream] - (->> data-stream - (rx/map (fn [data] - (try - (t/decode-str data) - (catch js/Error e - (throw (wte/error-ex-info :error.import/json-parse-error data e)))))) - (rx/map (fn [json-data] - (try - (if-not (ctob/has-legacy-format? json-data) - (ctob/decode-dtcg-json (ctob/ensure-tokens-lib nil) json-data) - (ctob/decode-legacy-json (ctob/ensure-tokens-lib nil) json-data)) - (catch js/Error e - (throw (wte/error-ex-info :error.import/invalid-json-data json-data e)))))) - (rx/mapcat (fn [tokens-lib] +(defn process-json-stream + ([data-stream] + (process-json-stream nil data-stream)) + ([params data-stream] + (let [{:keys [file-name]} params] + (->> data-stream + (rx/map (fn [data] (try - (-> (ctob/get-all-tokens tokens-lib) - (resolve-tokens-with-errors+) - (p/then (fn [_] tokens-lib)) - (p/catch (fn [sd-error] - (let [reference-errors (reference-errors sd-error) - err (if reference-errors - (wte/error-ex-info :error.import/style-dictionary-reference-errors reference-errors sd-error) - (wte/error-ex-info :error.import/style-dictionary-unknown-error sd-error sd-error))] - (throw err))))) + (t/decode-str data) (catch js/Error e - (p/rejected (wte/error-ex-info :error.import/style-dictionary-unknown-error "" e)))))))) + (throw (wte/error-ex-info :error.import/json-parse-error data e)))))) + (rx/map (fn [json-data] + (let [single-set? (ctob/single-set? json-data) + json-format (ctob/get-json-format json-data)] + (try + (cond + (and single-set? + (= :json-format/legacy json-format)) + (ctob/decode-single-set-legacy-json (ctob/ensure-tokens-lib (deref refs/tokens-lib)) file-name json-data) + + (and single-set? + (= :json-format/dtcg json-format)) + (ctob/decode-single-set-json (ctob/ensure-tokens-lib (deref refs/tokens-lib)) file-name json-data) + + (= :json-format/legacy json-format) + (ctob/decode-legacy-json (ctob/ensure-tokens-lib nil) json-data) + + :else + (ctob/decode-dtcg-json (ctob/ensure-tokens-lib nil) json-data)) + + (catch js/Error e + (throw (wte/error-ex-info :error.import/invalid-json-data json-data e))))))) + (rx/mapcat (fn [tokens-lib] + (try + (-> (ctob/get-all-tokens tokens-lib) + (resolve-tokens-with-errors+) + (p/then (fn [_] tokens-lib)) + (p/catch (fn [sd-error] + (let [reference-errors (reference-errors sd-error) + err (if reference-errors + (wte/error-ex-info :error.import/style-dictionary-reference-errors reference-errors sd-error) + (wte/error-ex-info :error.import/style-dictionary-unknown-error sd-error sd-error))] + (throw err))))) + (catch js/Error e + (p/rejected (wte/error-ex-info :error.import/style-dictionary-unknown-error "" e)))))))))) ;; === Errors