SQL Visual Studio Find and Replace in Publish Profile
When writing cross-database code, I prefer to avoid three-part name references. The variability of the $(OtherDatabase) that you describe is the precise reason why I avoid three-part names. Someday, DatabaseA will be renamed to DatabaseC and have to update a bunch of references in code. In your case, you can just do a new build, but it's still a pain. 

Instead, create synonyms in the project for each cross-database reference. In the synonym, you will use the SSDT project reference variable once per object:

CREATE SYNONYM dbo.PurchaseOrder FOR [$(OtherDatabase)].dbo.PurchaseOrder;


Then, every time you reference the cross-database object, you only need to use the two-part name. Developers should use neither the [$(OtherDatabase)] nor  a placeholder DatabaseA reference for find-and-replace.

FROM dbo.PurchaseOrder


This has the added benefit that all of the code will always be identical across different iterations of the database. The only object types that will not match will be synonyms. Rather than inspecting code, you can simply query the sys.synonyms DMV to determine where dbo.PurchaseOrder points. 

In this query, name would return the two-part names (ex PurchaseOrder), and base_object_name would return the three-part names (ex DatabaseA.dbo.PurchaseOrder):

SELECT name, base_object_name FROM sys.synonyms;


If you ever have a database that needs to be repointed from DatabaseA to DatabaseB, you can simply re-point the synonyms via a series of DROP SYNONYM &amp; CREATE SYNONYM statements, without having to perforem ALTER statements for procedures/views/functions/triggers.