private List <PackageData> GetPackagesFromManifestJson() { if (!myManifestPath.ExistsFile) { // This is not really expected, unless we're on an older Unity that doesn't support package manager myLogger.Info("manifest.json does not exist"); return(null); } myLogger.Verbose("Getting packages from manifest.json"); try { var projectManifest = ManifestJson.FromJson(myManifestPath.ReadAllText2().Text); // Now we've deserialised manifest.json, log why we skipped packages-lock.json LogWhySkippedPackagesLock(projectManifest); var appPath = myUnityVersion.GetActualAppPathForSolution(); var builtInPackagesFolder = UnityInstallationFinder.GetBuiltInPackagesFolder(appPath); // Read the editor's default manifest, which gives us the minimum versions for various packages var globalManifestPath = UnityInstallationFinder.GetPackageManagerDefaultManifest(appPath); if (globalManifestPath.ExistsFile && myLastReadGlobalManifestPath != globalManifestPath) { myLastReadGlobalManifestPath = globalManifestPath; myGlobalManifest = SafelyReadGlobalManifestFile(globalManifestPath); } var registry = projectManifest.Registry ?? DefaultRegistryUrl; var packages = new Dictionary <string, PackageData>(); foreach (var(id, version) in projectManifest.Dependencies) { if (version.Equals("exclude", StringComparison.OrdinalIgnoreCase)) { continue; } projectManifest.Lock.TryGetValue(id, out var lockDetails); packages[id] = GetPackageData(id, version, registry, builtInPackagesFolder, lockDetails); } // From observation, Unity treats package folders in the Packages folder as actual packages, even if they're // not registered in manifest.json. They must have a */package.json file, in the root of the package itself foreach (var child in myPackagesFolder.GetChildDirectories()) { // The folder name is not reliable to act as ID, so we'll use the ID from package.json. All other // packages get the ID from manifest.json or packages-lock.json. This is assumed to be the same as // the ID in package.json var packageData = GetPackageDataFromFolder(null, child, PackageSource.Embedded); if (packageData != null) { packages[packageData.Id] = packageData; } } // Calculate the transitive dependencies. Based on observation, we simply go with the highest available var packagesToProcess = new List <PackageData>(packages.Values); while (packagesToProcess.Count > 0) { var foundDependencies = GetPackagesFromDependencies(registry, builtInPackagesFolder, packages, packagesToProcess); foreach (var package in foundDependencies) { packages[package.Id] = package; } packagesToProcess = foundDependencies; } return(new List <PackageData>(packages.Values)); } catch (Exception e) { myLogger.LogExceptionSilently(e); return(null); } }