From 6f35b7db244b1ff7e9a0113526d577627e1c0dd8 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 25 Jul 2025 11:13:58 +0200 Subject: [PATCH] :sparkles: Add reader tag support for tokens related types --- backend/src/data_readers.clj | 6 +- common/src/app/common/types/tokens_lib.cljc | 209 ++++++++++++------ .../common_tests/types/tokens_lib_test.cljc | 2 +- 3 files changed, 152 insertions(+), 65 deletions(-) diff --git a/backend/src/data_readers.clj b/backend/src/data_readers.clj index d15ece02fa..e32d7c506c 100644 --- a/backend/src/data_readers.clj +++ b/backend/src/data_readers.clj @@ -3,4 +3,8 @@ penpot/duration app.common.time/duration penpot/path-data app.common.types.path/from-string penpot/matrix app.common.geom.matrix/decode-matrix - penpot/point app.common.geom.point/decode-point} + penpot/point app.common.geom.point/decode-point + penpot/token-lib app.common.types.tokens-lib/parse-multi-set-dtcg-json + penpot/token-set app.common.types.tokens-lib/make-token-set + penpot/token-theme app.common.types.tokens-lib/make-token-theme + penpot/token app.common.types.tokens-lib/make-token} diff --git a/common/src/app/common/types/tokens_lib.cljc b/common/src/app/common/types/tokens_lib.cljc index 5e6e869eb2..afbb104484 100644 --- a/common/src/app/common/types/tokens_lib.cljc +++ b/common/src/app/common/types/tokens_lib.cljc @@ -17,7 +17,9 @@ [app.common.transit :as t] [app.common.types.token :as cto] [app.common.uuid :as uuid] - [clojure.core.protocols :as protocols] + [clojure.core.protocols :as cp] + [clojure.datafy :refer [datafy]] + [clojure.pprint :as pp] [clojure.set :as set] [clojure.walk :as walk] [cuerdas.core :as str])) @@ -71,6 +73,9 @@ ;; === Token (defrecord Token [id name type value description modified-at] + cp/Datafiable + (datafy [this] (into {} this)) + INamedItem (get-name [_] name) @@ -87,6 +92,34 @@ (set-description [this new-description] (assoc this :description new-description))) +(defmethod pp/simple-dispatch Token + [^Token obj] + (.write *out* "#penpot/token ") + (pp/pprint-newline :miser) + (pp/pprint (datafy obj))) + +#?(:clj + (do + (defmethod print-method Token + [^Token this ^java.io.Writer w] + (.write w "#penpot/token ") + (print-method (datafy this) w)) + + (defmethod print-dup Token + [^Token this ^java.io.Writer w] + (print-method this w))) + + :cljs + (extend-type Token + cljs.core/IPrintWithWriter + (-pr-writer [this writer opts] + (-write writer "#penpot/token ") + (-pr-writer (datafy this) writer opts)) + + cljs.core/IEncodeJS + (-clj->js [this] + (clj->js (datafy this))))) + (defn token? [o] (instance? Token o)) @@ -175,25 +208,26 @@ (defrecord TokenSetLegacy [id name description modified-at tokens]) (deftype TokenSet [id name description modified-at tokens] - #?@(:clj [clojure.lang.IDeref - (deref [_] {:id id - :name name - :description description - :modified-at modified-at - :tokens tokens})] - :cljs [cljs.core/IDeref - (-deref [_] {:id id - :name name - :description description - :modified-at modified-at - :tokens tokens})]) + Object + (equals [_ other] + (and (instance? TokenSet other) + (= id (.-id ^TokenSet other)) + (= name (.-name ^TokenSet other)) + (= description (.-description ^TokenSet other)) + (= modified-at (.-modified-at ^TokenSet other)) + (= tokens (.-tokens ^TokenSet other)))) + + #?@(:cljs [cljs.core/IEquiv + (-equiv [this other] (.equals ^TokenSet this other))]) + + cp/Datafiable + (datafy [_] + {:id id + :name name + :description description + :modified-at modified-at + :tokens tokens}) - #?@(:cljs [cljs.core/IEncodeJS - (-clj->js [_] (js-obj "id" (clj->js id) - "name" (clj->js name) - "description" (clj->js description) - "modified-at" (clj->js modified-at) - "tokens" (clj->js tokens)))]) INamedItem (get-name [_] name) @@ -266,6 +300,33 @@ (get-tokens-map [_] tokens)) +(defmethod pp/simple-dispatch TokenSet [^TokenSet obj] + (.write *out* "#penpot/token-set ") + (pp/pprint-newline :miser) + (pp/pprint (datafy obj))) + +#?(:clj + (do + (defmethod print-method TokenSet + [^TokenSet this ^java.io.Writer w] + (.write w "#penpot/token-set ") + (print-method (datafy this) w)) + + (defmethod print-dup TokenSet + [^TokenSet this ^java.io.Writer w] + (print-method this w))) + + :cljs + (extend-type TokenSet + cljs.core/IPrintWithWriter + (-pr-writer [this writer opts] + (-write writer "#penpot/token-set ") + (-pr-writer (datafy this) writer opts)) + + cljs.core/IEncodeJS + (-clj->js [this] + (clj->js (datafy this))))) + (defn token-set? [o] (instance? TokenSet o)) @@ -528,6 +589,9 @@ (hidden-theme? [_] "if a theme is the (from the user ui) hidden temporary theme")) (defrecord TokenTheme [id name group description is-source external-id modified-at sets] + cp/Datafiable + (datafy [this] (into {} this)) + INamedItem (get-name [_] name) @@ -594,6 +658,34 @@ (hidden-theme? [this] (theme-matches-group-name this hidden-theme-group hidden-theme-name))) +(defmethod pp/simple-dispatch TokenTheme + [^TokenTheme obj] + (.write *out* "#penpot/token-theme ") + (pp/pprint-newline :miser) + (pp/pprint (datafy obj))) + +#?(:clj + (do + (defmethod print-method TokenTheme + [^TokenTheme this ^java.io.Writer w] + (.write w "#penpot/token-theme ") + (print-method (datafy this) w)) + + (defmethod print-dup TokenTheme + [^TokenTheme this ^java.io.Writer w] + (print-method this w))) + + :cljs + (extend-type TokenTheme + cljs.core/IPrintWithWriter + (-pr-writer [this writer opts] + (-write writer "#penpot/token-theme ") + (-pr-writer (datafy this) writer opts)) + + cljs.core/IEncodeJS + (-clj->js [this] + (clj->js (datafy this))))) + (defn token-theme? [o] (instance? TokenTheme o)) @@ -832,26 +924,12 @@ Will return a value that matches this schema: (deftype TokensLib [sets themes active-themes] ;; This is to convert the TokensLib to a plain map, for debugging or unit tests. - protocols/Datafiable + cp/Datafiable (datafy [_] - {:sets (d/update-vals sets deref) + {:sets sets :themes themes :active-themes active-themes}) - ;; TODO: this is used in serialization, but there should be a better way to do it - #?@(:clj [clojure.lang.IDeref - (deref [_] {:sets sets - :themes themes - :active-themes active-themes})] - :cljs [cljs.core/IDeref - (-deref [_] {:sets sets - :themes themes - :active-themes active-themes})]) - - #?@(:cljs [cljs.core/IEncodeJS - (-clj->js [_] (js-obj "sets" (clj->js sets) - "themes" (clj->js themes) - "active-themes" (clj->js active-themes)))]) #?@(:clj [json/JSONWriter (-write [this writter options] (json/-write (export-dtcg-json this) writter options))]) @@ -1228,6 +1306,33 @@ Will return a value that matches this schema: (valid-token-themes? themes) (valid-active-token-themes? active-themes)))) +(defmethod pp/simple-dispatch TokensLib + [^TokensLib obj] + (.write *out* "#penpot/token-lib ") + (pp/pprint-newline :miser) + (pp/pprint (export-dtcg-json obj))) + +#?(:clj + (do + (defmethod print-method TokensLib + [^TokensLib obj ^java.io.Writer w] + (.write w "#penpot/token-lib ") + (print-method (export-dtcg-json obj) w)) + + (defmethod print-dup TokensLib + [^TokensLib obj ^java.io.Writer w] + (print-method obj w))) + + :cljs + (extend-type TokensLib + cljs.core/IPrintWithWriter + (-pr-writer [this writer opts] + (-write writer "#penpot/token-lib ") + (-pr-writer (export-dtcg-json this) writer opts)) + + cljs.core/IEncodeJS + (-clj->js [this] (clj->js (datafy this))))) + (defn get-hidden-theme [tokens-lib] (get-theme tokens-lib hidden-theme-group hidden-theme-name)) @@ -1448,7 +1553,7 @@ Will return a value that matches this schema: (assert (= (get-json-format decoded-json-tokens) :json-format/legacy) "expected a legacy format for `decoded-json-tokens`") (parse-single-set-dtcg-json set-name (legacy-json->dtcg-json decoded-json-tokens))) -(defn- parse-multi-set-dtcg-json +(defn parse-multi-set-dtcg-json "Parse a decoded json file with multi sets in DTCG format into a TokensLib." [decoded-json] (assert (map? decoded-json) "expected a plain clojure map for `decoded-json`") @@ -1680,44 +1785,26 @@ Will return a value that matches this schema: (t/add-handlers! {:id "penpot/tokens-lib" :class TokensLib - :wfn deref + :wfn datafy :rfn #(make-tokens-lib %)} {:id "penpot/token-set" :class TokenSet - :wfn deref + :wfn datafy :rfn #(make-token-set %)} {:id "penpot/token-theme" :class TokenTheme - :wfn #(into {} %) + :wfn datafy :rfn #(map->TokenTheme %)} {:id "penpot/token" :class Token - :wfn #(into {} %) + :wfn datafy :rfn #(map->Token %)}) ;; === Serialization handlers for database (fressian) -#?(:clj - (defn- read-tokens-lib-v1-0 - "Reads the first version of tokens lib, now completly obsolete" - [r] - (let [;; Migrate sets tree without prefix to new format - prev-sets (->> (fres/read-object! r) - (tree-seq d/ordered-map? vals) - (filter (partial instance? TokenSet))) - - sets (-> (reduce add-set (make-tokens-lib) prev-sets) - (deref) - (:sets)) - - _set-groups (fres/read-object! r) - themes (fres/read-object! r) - active-themes (fres/read-object! r)] - (->TokensLib sets themes active-themes)))) - #?(:clj (defn- read-tokens-lib-v1-1 "Reads the tokens lib data structure and ensures that hidden @@ -1851,7 +1938,7 @@ Will return a value that matches this schema: :class TokenSet :wfn (fn [n w o] (fres/write-tag! w n 1) - (fres/write-object! w (into {} (deref o)))) + (fres/write-object! w (datafy o))) :rfn (fn [r] (let [obj (fres/read-object! r)] (make-token-set obj)))} @@ -1865,10 +1952,6 @@ Will return a value that matches this schema: (let [obj (fres/read-object! r)] (make-token-theme obj)))} - ;; LEGACY TOKENS LIB READERS (with migrations) - {:name "penpot/tokens-lib/v1" - :rfn read-tokens-lib-v1-0} - {:name "penpot/tokens-lib/v1.1" :rfn read-tokens-lib-v1-1} diff --git a/common/test/common_tests/types/tokens_lib_test.cljc b/common/test/common_tests/types/tokens_lib_test.cljc index 430dc7c97e..cd7fab3a65 100644 --- a/common/test/common_tests/types/tokens_lib_test.cljc +++ b/common/test/common_tests/types/tokens_lib_test.cljc @@ -1408,7 +1408,7 @@ #?(:clj (t/deftest export-parse-dtcg-json - (with-redefs [ct/now (constantly #inst "2024-10-16T12:01:20.257840055-00:00") + (with-redefs [ct/now (constantly (ct/inst "2024-10-16T12:01:20.257840055-00:00")) uuid/next (constantly uuid/zero)] (let [tokens-lib (-> (ctob/make-tokens-lib) (ctob/add-set (ctob/make-token-set :name "core"