mirror of
https://github.com/penpot/penpot.git
synced 2026-04-30 21:59:10 +00:00
Copying a component with variants from a shared library file ("Lib")
and pasting it into a file that uses that library ("Using Lib") would
crash the destination file with the referential-integrity validator
error:
{:code :component-main-external
:hint "Main instance should refer to a component in the same file"}
Root cause
----------
Paste goes through `generate-duplicate-shape-change` in
`common/src/app/common/logic/libraries.cljc`. When the shape is a
main instance of a known component and the copy set includes its
variant container, dispatch lands in `duplicate-variant`, then
`generate-duplicate-component`, and finally `duplicate-component`,
which clones the main-instance shape tree. Its `update-new-shape`
helper already re-links the new outer main's `:component-id` to the
freshly created local component (`new-component-id`), but it never
touches `:component-file`. The cloned shape therefore inherits
`:component-file` from the source library while the new component is
registered in the destination's local library
(`:apply-changes-local-library? true`), leaving the main-instance
dangling.
Fix
---
Extend `update-new-shape` with a second clause, sibling to the
existing `:component-id` rewrite: when a destination file id is
provided and differs from the new main's current `:component-file`,
re-root the shape. The same `(= (:component-id new-shape) (:id
component))` guard already used for the id rewrite ensures only the
outer main-instance is touched; nested shapes are unaffected.
The destination file id is threaded from the paste entry point
through the two orchestration functions that already knew the
source/destination distinction:
- `generate-duplicate-shape-change` — supplies the destination
`file-id` it already has in scope when dispatching to
`generate-duplicate-component-change`.
- `generate-duplicate-component-change` — accepts `:new-component-file`
as a kwarg; renames its internal `file-id` binding to
`source-file-id` for clarity (it was always the component's
originating library file); forwards `new-component-file` to
`duplicate-variant`.
- `duplicate-variant` — takes and forwards the `new-component-file`
positional arg.
- `generate-duplicate-component` — accepts `:new-component-file` kwarg
and passes it to `duplicate-component`.
- `duplicate-component` — applies the rewrite inside
`update-new-shape`. The `new-component-file` parameter is placed
right after `new-component-id` since component-id and component-file
are typically managed together.
Same-file duplication is not affected: without `:new-component-file`
the new clause is skipped, and when source and destination match the
`(not= new-component-file (:component-file new-shape))` guard fails.
Tests
-----
Added in `common/test/common_tests/logic/comp_creation_test.cljc`:
- `test-duplicate-component-rewrites-component-file-to-destination`
asserts that passing `:new-component-file` to
`generate-duplicate-component` produces a main-instance with
`:component-file` equal to the destination id.
- `test-duplicate-component-keeps-component-file-without-dest`
baseline: without `:new-component-file`, `:component-file` is left
untouched, matching pre-existing same-file behavior.
Github #8144
Signed-off-by: FairyPigDev <luislee3108@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>