@@ -598,6 +598,7 @@ namespace vix::commands
598598 const fs::path &path,
599599 const std::string &dependency,
600600 const std::string &linkTarget,
601+ const std::vector<std::string> &replacedPackageIds,
601602 std::string &error)
602603 {
603604 if (!fs::exists (path))
@@ -625,6 +626,23 @@ namespace vix::commands
625626 std::vector<std::string> links =
626627 parse_ini_array (original, " deps" , " links" );
627628
629+ registry.erase (
630+ std::remove_if (
631+ registry.begin (),
632+ registry.end (),
633+ [&](const std::string &existingDependency)
634+ {
635+ PkgSpec existingSpec;
636+
637+ if (!parse_pkg_spec (existingDependency, existingSpec))
638+ return false ;
639+
640+ return contains_string (
641+ replacedPackageIds,
642+ existingSpec.id ());
643+ }),
644+ registry.end ());
645+
628646 if (!contains_string (registry, dependency))
629647 registry.push_back (dependency);
630648
@@ -763,6 +781,128 @@ namespace vix::commands
763781 return j;
764782 }
765783
784+ void write_json_file_or_throw (
785+ const fs::path &path,
786+ const json &value)
787+ {
788+ std::ofstream out (path);
789+
790+ if (!out)
791+ {
792+ throw std::runtime_error (
793+ " cannot write file: " + path.string ());
794+ }
795+
796+ out << value.dump (2 ) << " \n " ;
797+
798+ if (!out.good ())
799+ {
800+ throw std::runtime_error (
801+ " failed to write file: " + path.string ());
802+ }
803+ }
804+
805+ std::vector<std::string> find_packages_moved_to (
806+ const std::string &targetPackageId)
807+ {
808+ std::vector<std::string> packageIds;
809+ const fs::path indexPath = registry_index_dir ();
810+
811+ if (!fs::exists (indexPath))
812+ return packageIds;
813+
814+ for (const auto &item :
815+ fs::directory_iterator (indexPath))
816+ {
817+ if (!item.is_regular_file () ||
818+ item.path ().extension () != " .json" )
819+ {
820+ continue ;
821+ }
822+
823+ try
824+ {
825+ const json entry =
826+ read_json_file_or_throw (item.path ());
827+
828+ const std::string movedTo =
829+ entry.value (" movedTo" , std::string{});
830+
831+ if (movedTo != targetPackageId)
832+ continue ;
833+
834+ const std::string ns =
835+ entry.value (" namespace" , std::string{});
836+
837+ const std::string name =
838+ entry.value (" name" , std::string{});
839+
840+ if (ns.empty () || name.empty ())
841+ continue ;
842+
843+ const std::string packageId =
844+ ns + " /" + name;
845+
846+ if (!contains_string (packageIds, packageId))
847+ packageIds.push_back (packageId);
848+ }
849+ catch (...)
850+ {
851+ }
852+ }
853+
854+ return packageIds;
855+ }
856+
857+ std::size_t remove_manifest_dependencies (
858+ const fs::path &path,
859+ const std::vector<std::string> &packageIds)
860+ {
861+ if (packageIds.empty ())
862+ return 0 ;
863+
864+ json manifest = read_json_file_or_throw (path);
865+
866+ if (!manifest.contains (" deps" ) ||
867+ !manifest[" deps" ].is_array ())
868+ {
869+ return 0 ;
870+ }
871+
872+ json dependencies = json::array ();
873+ std::size_t removed = 0 ;
874+
875+ for (const auto &dependency : manifest[" deps" ])
876+ {
877+ if (!dependency.is_object () ||
878+ !dependency.contains (" id" ) ||
879+ !dependency[" id" ].is_string ())
880+ {
881+ dependencies.push_back (dependency);
882+ continue ;
883+ }
884+
885+ const std::string packageId =
886+ dependency[" id" ].get <std::string>();
887+
888+ if (contains_string (packageIds, packageId))
889+ {
890+ ++removed;
891+ continue ;
892+ }
893+
894+ dependencies.push_back (dependency);
895+ }
896+
897+ if (removed > 0 )
898+ {
899+ manifest[" deps" ] = std::move (dependencies);
900+ write_json_file_or_throw (path, manifest);
901+ }
902+
903+ return removed;
904+ }
905+
766906 fs::path entry_path (const std::string &ns, const std::string &name)
767907 {
768908 return registry_index_dir () / (ns + " ." + name + " .json" );
@@ -1062,7 +1202,9 @@ namespace vix::commands
10621202 return 1 ;
10631203 }
10641204
1065- const fs::path packageEntryPath = entry_path (spec.ns , spec.name );
1205+ fs::path packageEntryPath =
1206+ entry_path (spec.ns , spec.name );
1207+
10661208 if (!fs::exists (packageEntryPath))
10671209 {
10681210 vix::cli::util::err_line (std::cerr, " package not found: " + spec.id ());
@@ -1084,9 +1226,57 @@ namespace vix::commands
10841226
10851227 try
10861228 {
1087- const json entry = read_json_file_or_throw (packageEntryPath);
1229+ json entry =
1230+ read_json_file_or_throw (packageEntryPath);
1231+
1232+ const std::string originalPackageId = spec.id ();
1233+
1234+ if (entry.contains (" movedTo" ) &&
1235+ entry[" movedTo" ].is_string ())
1236+ {
1237+ const std::string movedTo =
1238+ trim_copy (entry[" movedTo" ].get <std::string>());
1239+
1240+ if (!movedTo.empty ())
1241+ {
1242+ PkgSpec movedSpec;
1243+
1244+ if (!parse_pkg_spec (movedTo, movedSpec))
1245+ {
1246+ throw std::runtime_error (
1247+ " invalid movedTo package id: " + movedTo);
1248+ }
1249+
1250+ movedSpec.requestedVersion =
1251+ spec.requestedVersion ;
1252+
1253+ movedSpec.resolvedVersion .clear ();
1254+
1255+ spec = std::move (movedSpec);
1256+
1257+ packageEntryPath =
1258+ entry_path (spec.ns , spec.name );
1259+
1260+ if (!fs::exists (packageEntryPath))
1261+ {
1262+ throw std::runtime_error (
1263+ " moved package not found: " + spec.id ());
1264+ }
10881265
1089- const int resolveRc = resolve_version_v1 (entry, spec);
1266+ entry =
1267+ read_json_file_or_throw (packageEntryPath);
1268+
1269+ vix::cli::util::warn_line (
1270+ std::cout,
1271+ " Package moved: " +
1272+ originalPackageId +
1273+ " -> " +
1274+ spec.id ());
1275+ }
1276+ }
1277+
1278+ const int resolveRc =
1279+ resolve_version_v1 (entry, spec);
10901280 if (resolveRc != 0 )
10911281 {
10921282 return resolveRc;
@@ -1146,6 +1336,9 @@ namespace vix::commands
11461336 const std::string dependencySpec =
11471337 packageId + " @" + requested;
11481338
1339+ const std::vector<std::string> replacedPackageIds =
1340+ find_packages_moved_to (packageId);
1341+
11491342 if (!options.moduleName .empty ())
11501343 {
11511344 const std::string linkTarget =
@@ -1159,6 +1352,7 @@ namespace vix::commands
11591352 module_manifest_path (options.moduleName ),
11601353 dependencySpec,
11611354 linkTarget,
1355+ replacedPackageIds,
11621356 moduleError))
11631357 {
11641358 vix::cli::util::err_line (std::cerr, moduleError);
@@ -1199,12 +1393,29 @@ namespace vix::commands
11991393 return 0 ;
12001394 }
12011395
1396+ const std::size_t replacedCount =
1397+ remove_manifest_dependencies (
1398+ manifest_path (),
1399+ replacedPackageIds);
1400+
12021401 vix::cli::util::manifest::upsert_manifest_dependency_or_throw (
12031402 manifest_path (),
12041403 vix::cli::util::manifest::Dependency{
12051404 packageId,
12061405 requested});
12071406
1407+ if (replacedCount > 0 )
1408+ {
1409+ for (const std::string &oldPackageId :
1410+ replacedPackageIds)
1411+ {
1412+ vix::cli::util::kv (
1413+ std::cout,
1414+ " replaced" ,
1415+ oldPackageId + " -> " + packageId);
1416+ }
1417+ }
1418+
12081419 vix::cli::util::section (std::cout, " Add" );
12091420 vix::cli::util::kv (std::cout, " id" , packageId);
12101421 vix::cli::util::kv (std::cout, " version" , spec.resolvedVersion );
0 commit comments