Repository: penpot/penpot
File: frontend/src/app/main/data/style_dictionary.cljs (ClojureScript)

Code before:

(defonce !tokens-cache (atom nil))

(defn use-resolved-tokens
  "The StyleDictionary process function is async, so we can't use resolved values directly.

  This hook will return the unresolved tokens as state until they are processed,
  then the state will be updated with the resolved tokens."
  [tokens & {:keys [cache-atom interactive?]
             :or {cache-atom !tokens-cache}
             :as config}]
  (let [tokens-state (mf/use-state (get @cache-atom tokens))]

    ;; FIXME: this with effect with trigger all the time because
    ;; `config` will be always a different instance

    (mf/with-effect [tokens config]
      (let [cached (get @cache-atom tokens)]
        (cond
          (nil? tokens) nil
          ;; The tokens are already processing somewhere
          (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 [resolved-tokens (if interactive?
                                        (resolve-tokens-interactive tokens)
                                        (resolve-tokens tokens))]
                  (swap! cache-atom assoc tokens resolved-tokens)
                  (rx/sub! resolved-tokens (fn [resolved-tokens]
                                             (swap! cache-atom assoc tokens resolved-tokens)
                                             (reset! tokens-state resolved-tokens)))))))
    @tokens-state))

(defn use-resolved-tokens*
  "This hook will return the unresolved tokens as state until they are
  processed, then the state will be updated with the resolved tokens.

  This is a cache-less, simplified version of use-resolved-tokens
  hook."
  [tokens & {:keys [interactive?]}]
  (let [state* (mf/use-state tokens)]
    (mf/with-effect [tokens interactive?]
      (if (seq tokens)
        (let [tpoint  (dt/tpoint-ms)
              tokens  (if interactive?
                        (resolve-tokens-interactive tokens)
                        (resolve-tokens tokens))]

          (-> tokens
              (rx/sub! (fn [resolved-tokens]

Review comment (suggestion):
When a variable contains not a final value but a rx stream, we usually add the `-s` suffix. For example `resolved-tokens-s` in this case. Same applies to other variables below.

Code after:
           (throw err))))
     :unknown-tokens unknown-tokens}))

(defn process-json-stream
  ([data-stream]
   (process-json-stream nil data-stream))
  ([params data-stream]
   (let [{:keys [file-name]} params]
     (->> data-stream
          (rx/map parse-json)
          (rx/map #(decode-json-data % file-name))
          (rx/mapcat (fn [{:keys [tokens-lib unknown-tokens]}]
                       (when unknown-tokens
                         (st/emit! (tokens-of-unknown-type-warning unknown-tokens)))
                       (try
                         (->> (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
                           (throw (wte/error-ex-info :error.import/style-dictionary-unknown-error "" e))))))))))

;; === Hooks

(defonce !tokens-cache (atom nil))

(defn use-resolved-tokens
  "The StyleDictionary process function is async, so we can't use resolved values directly.

  This hook will return the unresolved tokens as state until they are processed,
  then the state will be updated with the resolved tokens."
  [tokens & {:keys [cache-atom interactive?]
             :or {cache-atom !tokens-cache}
             :as config}]
  (let [tokens-state (mf/use-state (get @cache-atom tokens))]

    ;; FIXME: this with effect with trigger all the time because
    ;; `config` will be always a different instance

    (mf/with-effect [tokens config]
      (let [cached (get @cache-atom tokens)]
        (cond
          (nil? tokens) nil
          ;; The tokens are already processing somewhere
          (rx/observable? cached) (rx/sub! cached #(reset! tokens-state %))