diff --git a/frontend/src/app/main/data/style_dictionary.cljs b/frontend/src/app/main/data/style_dictionary.cljs index 1edbf89f7..97a0d153c 100644 --- a/frontend/src/app/main/data/style_dictionary.cljs +++ b/frontend/src/app/main/data/style_dictionary.cljs @@ -202,20 +202,20 @@ (build-dictionary [_] (let [platform "json" - config' (clj->js config)] - (-> (sd. config') - (.buildAllPlatforms platform) - (p/then #(.getPlatformTokens ^js % platform)) - (p/then #(.-allTokens ^js %)))))) + config' (clj->js config) + build+ (-> (sd. config') + (.buildAllPlatforms platform) + (p/then #(.getPlatformTokens ^js % platform)) + (p/then #(.-allTokens ^js %)))] + (rx/from build+)))) -(defn resolve-tokens-tree+ +(defn resolve-tokens-tree ([tokens-tree get-token] - (resolve-tokens-tree+ tokens-tree get-token (StyleDictionary. default-config))) + (resolve-tokens-tree tokens-tree get-token (StyleDictionary. default-config))) ([tokens-tree get-token style-dictionary] - (let [sdict (-> style-dictionary - (add-tokens tokens-tree) - (build-dictionary))] - (p/fmap #(process-sd-tokens % get-token) sdict)))) + (->> (add-tokens style-dictionary tokens-tree) + (build-dictionary) + (rx/map #(process-sd-tokens % get-token))))) (defn sd-token-name [^js sd-token] (.. sd-token -original -name)) @@ -223,12 +223,12 @@ (defn sd-token-uuid [^js sd-token] (uuid (.-uuid (.-id ^js sd-token)))) -(defn resolve-tokens+ +(defn resolve-tokens [tokens] (let [tokens-tree (ctob/tokens-tree tokens)] - (resolve-tokens-tree+ tokens-tree #(get tokens (sd-token-name %))))) + (resolve-tokens-tree tokens-tree #(get tokens (sd-token-name %))))) -(defn resolve-tokens-interactive+ +(defn resolve-tokens-interactive "Interactive check of resolving tokens. Uses a ids map to backtrace the original token from the resolved StyleDictionary token. @@ -241,10 +241,10 @@ 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] (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 %))))) -(defn resolve-tokens-with-errors+ [tokens] - (resolve-tokens-tree+ +(defn resolve-tokens-with-errors [tokens] + (resolve-tokens-tree (ctob/tokens-tree tokens) #(get tokens (sd-token-name %)) (StyleDictionary. (assoc default-config :log {:verbosity "verbose"})))) @@ -346,24 +346,23 @@ (when unknown-tokens (st/emit! (tokens-of-unknown-type-warning unknown-tokens))) (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)] - ;; We allow reference errors for the users to resolve in the ui and throw on any other errors - (if reference-errors - (p/resolved tokens-lib) - (throw (wte/error-ex-info :error.import/style-dictionary-unknown-error sd-error sd-error))))))) + (->> (ctob/get-all-tokens tokens-lib) + (resolve-tokens-with-errors) + (rx/map (fn [_] + tokens-lib)) + (rx/catch (fn [sd-error] + (let [reference-errors (reference-errors sd-error)] + ;; We allow reference errors for the users to resolve in the ui and throw on any other errors + (if reference-errors + (rx/of tokens-lib) + (throw (wte/error-ex-info :error.import/style-dictionary-unknown-error sd-error sd-error))))))) (catch js/Error e - (p/rejected (wte/error-ex-info :error.import/style-dictionary-unknown-error "" e)))))))))) + (throw (wte/error-ex-info :error.import/style-dictionary-unknown-error "" e)))))))))) ;; === Hooks (defonce !tokens-cache (atom nil)) -(defonce !theme-tokens-cache (atom nil)) - (defn use-resolved-tokens "The StyleDictionary process function is async, so we can't use resolved values directly. @@ -382,19 +381,17 @@ (cond (nil? tokens) nil ;; The tokens are already processing somewhere - (p/promise? cached) (-> cached - (p/then #(reset! tokens-state %)) - #_(p/catch js/console.error)) + (rx/observable? cached) (rx/sub! cached #(reset! tokens-state %)) ;; Get the cached entry (some? cached) (reset! tokens-state cached) ;; No cached entry, start processing - :else (let [promise+ (if interactive? - (resolve-tokens-interactive+ tokens) - (resolve-tokens+ tokens))] - (swap! cache-atom assoc tokens promise+) - (p/then promise+ (fn [resolved-tokens] - (swap! cache-atom assoc tokens resolved-tokens) - (reset! tokens-state resolved-tokens))))))) + :else (let [resolved-tokens-s (if interactive? + (resolve-tokens-interactive tokens) + (resolve-tokens tokens))] + (swap! cache-atom assoc tokens resolved-tokens-s) + (rx/sub! resolved-tokens-s (fn [resolved-tokens] + (swap! cache-atom assoc tokens resolved-tokens) + (reset! tokens-state resolved-tokens))))))) @tokens-state)) (defn use-resolved-tokens* @@ -408,12 +405,12 @@ (mf/with-effect [tokens interactive?] (if (seq tokens) (let [tpoint (dt/tpoint-ms) - promise (if interactive? - (resolve-tokens-interactive+ tokens) - (resolve-tokens+ tokens))] + tokens-s (if interactive? + (resolve-tokens-interactive tokens) + (resolve-tokens tokens))] - (->> promise - (p/fmap (fn [resolved-tokens] + (-> tokens-s + (rx/sub! (fn [resolved-tokens] (let [elapsed (tpoint)] (l/dbg :hint "use-resolved-tokens*" :elapsed elapsed) (reset! state* resolved-tokens)))))) diff --git a/frontend/src/app/main/data/workspace/tokens/application.cljs b/frontend/src/app/main/data/workspace/tokens/application.cljs index 16f5ff28e..b807353e2 100644 --- a/frontend/src/app/main/data/workspace/tokens/application.cljs +++ b/frontend/src/app/main/data/workspace/tokens/application.cljs @@ -45,7 +45,7 @@ (when-let [tokens (some-> (dsh/lookup-file-data state) (get :tokens-lib) (ctob/get-active-themes-set-tokens))] - (->> (rx/from (sd/resolve-tokens+ tokens)) + (->> (sd/resolve-tokens tokens) (rx/mapcat (fn [resolved-tokens] (let [undo-id (js/Symbol) diff --git a/frontend/src/app/main/data/workspace/tokens/propagation.cljs b/frontend/src/app/main/data/workspace/tokens/propagation.cljs index 7bef78254..6ee22ed9a 100644 --- a/frontend/src/app/main/data/workspace/tokens/propagation.cljs +++ b/frontend/src/app/main/data/workspace/tokens/propagation.cljs @@ -185,12 +185,11 @@ (watch [_ state _] (when-let [tokens-lib (-> (dsh/lookup-file-data state) (get :tokens-lib))] - (let [tokens (-> (ctob/get-active-themes-set-tokens tokens-lib) - (sd/resolve-tokens+))] - (->> (rx/from tokens) - (rx/mapcat (fn [sd-tokens] - (let [undo-id (js/Symbol)] - (rx/concat - (rx/of (dwu/start-undo-transaction undo-id :timeout false)) - (propagate-tokens state sd-tokens) - (rx/of (dwu/commit-undo-transaction undo-id)))))))))))) \ No newline at end of file + (->> (ctob/get-active-themes-set-tokens tokens-lib) + (sd/resolve-tokens) + (rx/mapcat (fn [sd-tokens] + (let [undo-id (js/Symbol)] + (rx/concat + (rx/of (dwu/start-undo-transaction undo-id :timeout false)) + (propagate-tokens state sd-tokens) + (rx/of (dwu/commit-undo-transaction undo-id))))))))))) diff --git a/frontend/src/app/main/ui/workspace/tokens/form.cljs b/frontend/src/app/main/ui/workspace/tokens/form.cljs index 60fe6f01f..921ddec12 100644 --- a/frontend/src/app/main/ui/workspace/tokens/form.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/form.cljs @@ -36,10 +36,10 @@ [app.util.functions :as uf] [app.util.i18n :refer [tr]] [app.util.keyboard :as k] + [beicon.v2.core :as rx] [cuerdas.core :as str] [malli.core :as m] [malli.error :as me] - [promesa.core :as p] [rumext.v2 :as mf])) ;; Schemas --------------------------------------------------------------------- @@ -94,14 +94,9 @@ (defn valid-value? [value] (seq (finalize-value value))) -(defn schema-validation->promise [validated] - (if (:errors validated) - (p/rejected validated) - (p/resolved validated))) - ;; Component ------------------------------------------------------------------- -(defn validate-token-value+ +(defn validate-token-value "Validates token value by resolving the value `input` using `StyleDictionary`. Returns a promise of either resolved tokens or rejects with an error state." [{:keys [value name-value token tokens]}] @@ -110,10 +105,10 @@ token-name (if (str/empty? name-value) "__TOKEN_STUDIO_SYSTEM.TEMP" name-value)] (cond (empty? (str/trim value)) - (p/rejected {:errors [(wte/get-error-code :error.token/empty-input)]}) + (rx/throw {:errors [(wte/get-error-code :error.token/empty-input)]}) (ctob/token-value-self-reference? token-name value) - (p/rejected {:errors [(wte/get-error-code :error.token/direct-self-reference)]}) + (rx/throw {:errors [(wte/get-error-code :error.token/direct-self-reference)]}) :else (let [tokens' (cond-> tokens @@ -122,14 +117,14 @@ :always (update token-name #(ctob/make-token (merge % {:value value :name token-name :type (:type token)}))))] - (-> tokens' - (sd/resolve-tokens-interactive+) - (p/then - (fn [resolved-tokens] - (let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens token-name)] - (cond - resolved-value (p/resolved resolved-token) - :else (p/rejected {:errors (or errors (wte/get-error-code :error/unknown-error))})))))))))) + (->> tokens' + (sd/resolve-tokens-interactive) + (rx/mapcat + (fn [resolved-tokens] + (let [{:keys [errors resolved-value] :as resolved-token} (get resolved-tokens token-name)] + (cond + resolved-value (rx/of resolved-token) + :else (rx/throw {:errors (or errors (wte/get-error-code :error/unknown-error))})))))))))) (defn use-debonced-resolve-callback "Resolves a token values using `StyleDictionary`. @@ -148,14 +143,14 @@ (js/setTimeout (fn [] (when (not (timeout-outdated-cb?)) - (-> (validate-token-value+ {:value value + (->> (validate-token-value {:value value :name-value @name-ref :token token :tokens tokens}) - (p/finally - (fn [x err] - (when-not (timeout-outdated-cb?) - (callback (or err x)))))))) + (rx/filter #(not (timeout-outdated-cb?))) + (rx/subs! + callback + callback)))) timeout))))] debounced-resolver-callback)) @@ -442,33 +437,36 @@ ;; and the user might have edited a valid form to make it invalid, ;; and press enter before the next validations could return. (let [final-name (finalize-name @token-name-ref) - valid-name?+ (-> (validate-name final-name) schema-validation->promise) + valid-name? (try + (not (:errors (validate-name final-name))) + (catch js/Error _ nil)) final-value (finalize-value (mf/ref-val value-ref)) final-description @description-ref - valid-description?+ (some-> final-description validate-descripion schema-validation->promise)] - (-> (p/all [valid-name?+ - valid-description?+ - (validate-token-value+ {:value final-value - :name-value final-name - :token token - :tokens active-theme-tokens})]) - (p/finally (fn [result err] - ;; The result should be a vector of all resolved validations - ;; We do not handle the error case as it will be handled by the components validations - (when (and (seq result) (not err)) - (st/emit! - (if (ctob/token? token) - (dwtl/update-token (:name token) - {:name final-name - :value final-value - :description final-description}) + valid-description? (if final-description + (try + (not (:errors (validate-descripion final-description))) + (catch js/Error _ nil)) + true)] + (when (and valid-name? valid-description?) + (->> (validate-token-value {:value final-value + :name-value final-name + :token token + :tokens active-theme-tokens}) + (rx/subs! + (fn [] + (st/emit! + (if (ctob/token? token) + (dwtl/update-token (:name token) + {:name final-name + :value final-value + :description final-description}) - (dwtl/create-token {:name final-name - :type token-type - :value final-value - :description final-description})) - (dwtp/propagate-workspace-tokens) - (modal/hide))))))))) + (dwtl/create-token {:name final-name + :type token-type + :value final-value + :description final-description})) + (dwtp/propagate-workspace-tokens) + (modal/hide))))))))) on-delete-token (mf/use-fn diff --git a/frontend/test/frontend_tests/tokens/helpers/state.cljs b/frontend/test/frontend_tests/tokens/helpers/state.cljs index c5a7f864d..af9ce4ca9 100644 --- a/frontend/test/frontend_tests/tokens/helpers/state.cljs +++ b/frontend/test/frontend_tests/tokens/helpers/state.cljs @@ -30,9 +30,9 @@ ptk/WatchEvent (watch [_ state _] (let [data (dsh/lookup-file-data state)] - (->> (rx/from (-> (get data :tokens-lib) - (ctob/get-active-themes-set-tokens) - (sd/resolve-tokens+))) + (->> (get data :tokens-lib) + (ctob/get-active-themes-set-tokens) + (sd/resolve-tokens) (rx/mapcat #(rx/of (end)))))))) (defn stop-on diff --git a/frontend/test/frontend_tests/tokens/style_dictionary_test.cljs b/frontend/test/frontend_tests/tokens/style_dictionary_test.cljs index 629bcee05..24a609960 100644 --- a/frontend/test/frontend_tests/tokens/style_dictionary_test.cljs +++ b/frontend/test/frontend_tests/tokens/style_dictionary_test.cljs @@ -10,8 +10,7 @@ [app.common.types.tokens-lib :as ctob] [app.main.data.style-dictionary :as sd] [beicon.v2.core :as rx] - [cljs.test :as t :include-macros true] - [promesa.core :as p])) + [cljs.test :as t :include-macros true])) (t/deftest resolve-tokens-test (t/async @@ -35,23 +34,23 @@ :value "{borderRadius.sm} * 200000000" :type :border-radius})) (ctob/get-all-tokens))] - (-> (sd/resolve-tokens+ tokens) - (p/finally - (fn [resolved-tokens] - (t/is (= 12 (get-in resolved-tokens ["borderRadius.sm" :resolved-value]))) - (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 (= "px" (get-in resolved-tokens ["borderRadius.md-with-dashes" :unit]))) - (t/is (nil? (get-in resolved-tokens ["borderRadius.large" :resolved-value]))) - (t/is (= :error.token/number-too-large - (get-in resolved-tokens ["borderRadius.large" :errors 0 :error/code]))) - (t/is (nil? (get-in resolved-tokens ["borderRadius.largePx" :resolved-value]))) - (t/is (= :error.token/number-too-large - (get-in resolved-tokens ["borderRadius.largePx" :errors 0 :error/code]))) - (t/is (nil? (get-in resolved-tokens ["borderRadius.largeFn" :resolved-value]))) - (t/is (= :error.token/number-too-large - (get-in resolved-tokens ["borderRadius.largeFn" :errors 0 :error/code]))) - (done)))))))) + (-> (sd/resolve-tokens tokens) + (rx/sub! + (fn [resolved-tokens] + (t/is (= 12 (get-in resolved-tokens ["borderRadius.sm" :resolved-value]))) + (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 (= "px" (get-in resolved-tokens ["borderRadius.md-with-dashes" :unit]))) + (t/is (nil? (get-in resolved-tokens ["borderRadius.large" :resolved-value]))) + (t/is (= :error.token/number-too-large + (get-in resolved-tokens ["borderRadius.large" :errors 0 :error/code]))) + (t/is (nil? (get-in resolved-tokens ["borderRadius.largePx" :resolved-value]))) + (t/is (= :error.token/number-too-large + (get-in resolved-tokens ["borderRadius.largePx" :errors 0 :error/code]))) + (t/is (nil? (get-in resolved-tokens ["borderRadius.largeFn" :resolved-value]))) + (t/is (= :error.token/number-too-large + (get-in resolved-tokens ["borderRadius.largeFn" :errors 0 :error/code]))) + (done)))))))) (t/deftest process-json-stream-test (t/async