0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-09 14:21:42 -05:00

🐛 Fix shape-ref cycles

This commit is contained in:
Pablo Alba 2025-01-22 12:05:50 +01:00
parent 232b29cd89
commit 7592530fb4
3 changed files with 67 additions and 20 deletions

View file

@ -320,6 +320,35 @@
(pcb/with-file-data file-data)
(pcb/update-shapes shape-ids detach-shape))))))
(defmethod repair-error :shape-ref-cycle
[_ {:keys [shape args] :as error} file-data _]
(let [repair-component
(fn [component]
(let [objects (:objects component) ;; we only have encounter this on deleted components,
;; so the relevant objects are inside the component
to-detach (->> (:cycles-ids args)
(map #(get objects %))
(map #(ctn/get-head-shape objects %))
(map :id)
distinct
(mapcat #(ctn/get-children-in-instance objects %))
(map :id)
set)]
(update component :objects
(fn [objects]
(reduce-kv (fn [acc k v]
(if (contains? to-detach k)
(assoc acc k (ctk/detach-shape v))
(assoc acc k v)))
{}
objects)))))]
(log/dbg :hint "repairing component :shape-ref-cycle" :id (:id shape) :name (:name shape))
(-> (pcb/empty-changes nil nil)
(pcb/with-library-data file-data)
(pcb/update-component (:id shape) repair-component))))
(defmethod repair-error :shape-ref-in-main
[_ {:keys [shape page-id] :as error} file-data _]
(let [repair-shape

View file

@ -55,7 +55,8 @@
:component-nil-objects-not-allowed
:instance-head-not-frame
:misplaced-slot
:missing-slot})
:missing-slot
:shape-ref-cycle})
(def ^:private schema:error
[:map {:title "ValidationError"}
@ -482,6 +483,18 @@
"This deleted component has children with the same swap slot"
component file nil))))
(defn check-ref-cycles
[component file]
(let [cycles-ids (->> component
:objects
vals
(filter #(= (:id %) (:shape-ref %)))
(map :id))]
(when (seq cycles-ids)
(report-error :shape-ref-cycle
"This deleted component has shapes with shape-ref pointing to self"
component file nil :cycles-ids cycles-ids))))
(defn- check-component
"Validate semantic coherence of a component. Report all errors found."
@ -491,7 +504,8 @@
"Objects list cannot be nil"
component file nil))
(when (:deleted component)
(check-component-duplicate-swap-slot component file)))
(check-component-duplicate-swap-slot component file)
(check-ref-cycles component file)))
(defn- get-orphan-shapes
[{:keys [objects] :as page}]

View file

@ -335,24 +335,28 @@
(true? (= (:id component) (:id ref-component)))))
(defn find-swap-slot
[shape container file libraries]
(if-let [swap-slot (ctk/get-swap-slot shape)]
swap-slot
(let [ref-shape (find-ref-shape file
container
libraries
shape
:include-deleted? true
:with-context? true)
shape-meta (meta ref-shape)
ref-file (:file shape-meta)
ref-container (:container shape-meta)]
(when ref-shape
(if-let [swap-slot (ctk/get-swap-slot ref-shape)]
swap-slot
(if (ctk/main-instance? ref-shape)
(:id shape)
(find-swap-slot ref-shape ref-container ref-file libraries)))))))
([shape container file libraries]
(find-swap-slot shape container file libraries #{}))
([shape container file libraries viewed-ids]
(if (contains? viewed-ids (:id shape)) ;; prevent cycles
nil
(if-let [swap-slot (ctk/get-swap-slot shape)]
swap-slot
(let [ref-shape (find-ref-shape file
container
libraries
shape
:include-deleted? true
:with-context? true)
shape-meta (meta ref-shape)
ref-file (:file shape-meta)
ref-container (:container shape-meta)]
(when ref-shape
(if-let [swap-slot (ctk/get-swap-slot ref-shape)]
swap-slot
(if (ctk/main-instance? ref-shape)
(:id shape)
(find-swap-slot ref-shape ref-container ref-file libraries (conj viewed-ids (:id shape)))))))))))
(defn match-swap-slot?
[shape-main shape-inst container-inst container-main file libraries]