Skip to content

fix: cascade-delete all orphaned objects in PreviewHost cleanup#42

Merged
Iron-Ham merged 1 commit into
Iron-Ham:mainfrom
teradyl:fix/cleanup-orphaned-pbxproj-objects
May 11, 2026
Merged

fix: cascade-delete all orphaned objects in PreviewHost cleanup#42
Iron-Ham merged 1 commit into
Iron-Ham:mainfrom
teradyl:fix/cleanup-orphaned-pbxproj-objects

Conversation

@teradyl
Copy link
Copy Markdown
Contributor

@teradyl teradyl commented May 8, 2026

Summary

removeExisting only deleted the first matching PreviewHost native target and a single root-group child. Everything else attached to the target was orphaned in the pbxproj on every preview run, and because each leak left another PreviewHost target / group, the next run's .first(where:) only peeled one off — so projects accumulated hundreds of lines of cruft and broken file references pointing at deleted .preview-host-XXXXX/ temp dirs.

What changed

removeExisting now:

  • Iterates over all PreviewHost native targets (not just .first) and cascade-deletes:
    • build phases + their build files,
    • configuration list + individual configurations,
    • target dependencies + container item proxies,
    • SPM package product dependencies,
    • the PreviewHost.app product reference (also removing it from the Products group),
    • the target's slot in project.targets and the target object itself.
  • Recursively scans for all PreviewHost PBXGroups and deletes each child file ref before removing the group.
  • Final pass scrubs orphan PreviewHost.app refs in the Products group left behind by earlier broken cleanups.

Test plan

  • New regression test testCleanupLeavesNoOrphanedObjects runs inject + cleanup three times on the same project and asserts no PreviewHost-related target, group, file ref, or build configuration survives any cleanup pass.
  • All existing ProjectInjectorTests pass (cleanup, idempotent injection, scheme creation, etc.).
  • Verified end-to-end on a real project: after the patch, repeated /preview runs add 0 lines to project.pbxproj (and actually scrub pre-existing orphan refs from prior buggy runs).

The previous `removeExisting` only removed the first PreviewHost native
target and a single matching root-group child. Everything else attached
to the target was orphaned in the pbxproj on every preview run:

- XCBuildConfigurations (Debug + Release) and their XCConfigurationList
- PBXSourcesBuildPhase / PBXFrameworksBuildPhase / PBXResourcesBuildPhase
  (and any embed-frameworks copy-files phase) plus their PBXBuildFiles
- PBXTargetDependency entries and their PBXContainerItemProxy
- XCSwiftPackageProductDependency stubs forwarded onto the target
- The PBXFileReference for `PreviewHost.app` (and its slot in Products)
- Additional PreviewHost PBXGroups and their child file refs

Because each leak left another `PreviewHost` target / group in the tree,
the next run's `.first(where:)` only peeled one off, and every
subsequent inject piled another full set on top — so projects
accumulated hundreds of lines of cruft and broken file references
pointing at the deleted `.preview-host-XXXXX/` temp directories.

Cleanup now:
- Iterates over ALL PreviewHost targets (not just `.first`) and
  cascade-deletes their build phases + build files, configuration list
  + configurations, dependencies + container item proxies, package
  product deps, and product reference (also removing it from the
  Products group).
- Recursively scrubs every PreviewHost PBXGroup and its children.
- Final pass removes orphan `PreviewHost.app` refs from the Products
  group left behind by earlier broken cleanups.

Adds a regression test that runs inject + cleanup three times on the
same project and asserts no PreviewHost-related target, group, file
ref, or build configuration survives any cleanup pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants