From c89c7eaacc36ad267ed7e5001740bb4b5c82bc06 Mon Sep 17 00:00:00 2001 From: Gabriel Horner Date: Sun, 17 May 2026 01:26:33 -0400 Subject: [PATCH] fix: build-edn fails to export :build/class-properties that roundtrip When running `bb dev:cli export-edn --roundtrip` on a graph with included test, :build/class-properties sort order kept changing because built-in properties order is lost on export --- .gitignore | 1 + deps/db/src/logseq/db/sqlite/build.cljs | 12 ++++++++-- .../db/test/logseq/db/sqlite/export_test.cljs | 23 +++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 229c2acb58..3db21cd4d3 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,7 @@ resources/electron.js /libs/dist/ charlie/ .vscode +/.claude/settings.local.json /.preprocessor-cljs docker android/app/src/main/assets/capacitor.plugin.json diff --git a/deps/db/src/logseq/db/sqlite/build.cljs b/deps/db/src/logseq/db/sqlite/build.cljs index f97201f3cd..732a7d3949 100644 --- a/deps/db/src/logseq/db/sqlite/build.cljs +++ b/deps/db/src/logseq/db/sqlite/build.cljs @@ -384,8 +384,16 @@ (into {})) new-properties-tx (vec (mapcat (partial build-property-tx properties' page-uuids all-idents property-db-ids class-property-orders options) - properties'))] - new-properties-tx)) + properties')) + ;; Apply the topological :block/order to properties that already exist in + ;; the target DB (e.g. built-ins) so per-class property order survives a + ;; round-trip alongside the user-defined properties built above. + existing-property-orders-tx + (->> class-property-orders + (keep (fn [[ident order]] + (when-not (contains? properties' ident) + {:db/ident ident :block/order order}))))] + (into new-properties-tx existing-property-orders-tx))) (defn- build-class-extends [{:build/keys [class-parent class-extends]} class-db-ids] (when-let [class-extends' (if class-parent diff --git a/deps/db/test/logseq/db/sqlite/export_test.cljs b/deps/db/test/logseq/db/sqlite/export_test.cljs index 586fa84050..5bd108628c 100644 --- a/deps/db/test/logseq/db/sqlite/export_test.cljs +++ b/deps/db/test/logseq/db/sqlite/export_test.cljs @@ -926,6 +926,29 @@ (is (= (set original-property-history) property-history) "Original property history equals exported property history"))) +;; When a built-in property appears in :build/class-properties alongside +;; user-defined properties, the per-class order must survive a round-trip +(deftest import-graph-preserves-class-properties-order-with-built-in + (let [original-data + {:properties {:user.property/url {:logseq.property/type :default} + :user.property/about {:logseq.property/type :default}} + :classes {:user.class/UrlFirst {:build/class-properties [:user.property/url :logseq.property/status]} + :user.class/StatusFirst {:build/class-properties [:logseq.property/status :user.property/about]}}} + conn (db-test/create-conn-with-import-map original-data) + ;; Simulate a UI-built graph where the user positioned :logseq.property/status + ;; ahead of the user-defined properties referenced from the same classes. + _ (d/transact! conn [{:db/ident :logseq.property/status :block/order "a0"}]) + export-map (sqlite-export/build-export @conn {:export-type :graph}) + valid-result (sqlite-export/validate-export export-map) + _ (assert (not (:error valid-result)) "No error when importing export-map into new graph") + export-map2 (sqlite-export/build-export (:db valid-result) {:export-type :graph})] + (is (= [:logseq.property/status :user.property/about] + (get-in export-map [:classes :user.class/StatusFirst :build/class-properties])) + "Original export reflects the built-in's UI-set order") + (is (= nil + (sqlite-export/diff-exports export-map export-map2)) + "No diff between original export and export after importing into a new graph"))) + (deftest import-graph-with-different-property-value-cases (let [pvalue-uuid1 (random-uuid) original-data