Skip to content

Commit

Permalink
Load nuget packages explicitly
Browse files Browse the repository at this point in the history
  • Loading branch information
FlorianRappl committed May 5, 2024
1 parent 84c771d commit 2285c2a
Showing 1 changed file with 144 additions and 6 deletions.
150 changes: 144 additions & 6 deletions src/Piral.Blazor.Orchestrator/LocalMicrofrontendPackage.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,88 @@
using System.Reflection;
using NuGet.Frameworks;
using NuGet.Packaging;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;

namespace Piral.Blazor.Orchestrator;

internal class LocalMicrofrontendPackage(string path, JsonObject? config, IModuleContainerService container, IEvents events, IData data) :
MicrofrontendPackage(Path.GetFileNameWithoutExtension(path), "0.0.0", config, container, events, data)
{
private const string target = "net8.0";
private readonly string _path = path;
private readonly List<string> _contentRoots = [];
private readonly Dictionary<string, DependencyDescription> _deps = [];
private readonly List<PackageArchiveReader> _packages = [];

private Assembly? LoadAssembly(PackageArchiveReader package, string path)
{
using var msStream = GetFile(package, path).Result;

if (msStream is not null)
{
return Context.LoadFromStream(msStream);
}

return null;
}

protected override Assembly? ResolveAssembly(AssemblyName assemblyName)
{
var dllName = assemblyName.Name;
var basePath = Path.GetDirectoryName(_path)!;
return Context.LoadFromAssemblyPath(Path.Combine(basePath, $"{dllName}.dll"));
var dllName = assemblyName.Name!;
var version = assemblyName.Version?.ToString()!;
return ResolveAssembly(dllName, version);
}

private Assembly? ResolveAssembly(string name, string version)
{
var packageId = $"{name}/{version}";

if (_deps.TryGetValue(packageId, out var dep) && dep.Type == "package")
{
var packageName = name.ToLowerInvariant();
var userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
var packagePath = Path.Combine(userProfile, ".nuget", "packages", packageName, version, $"{packageName}.{version}.nupkg");
var stream = File.OpenRead(packagePath);
_packages.Add(new PackageArchiveReader(stream));
return AddAssemblyToContext(name);
}
else
{
var basePath = Path.GetDirectoryName(_path)!;
return Context.LoadFromAssemblyPath(Path.Combine(basePath, $"{name}.dll"));
}
}

protected override async Task OnInitializing()
{
await SetContentRoots();
await SetDependencies();
}

protected override async Task OnInitialized()
private async Task SetContentRoots()
{
var infos = Path.ChangeExtension(_path, ".staticwebassets.runtime.json");
using var fs = File.OpenRead(infos);
var assets = await JsonSerializer.DeserializeAsync<StaticWebAssets>(fs);
_contentRoots.AddRange(assets?.ContentRoots ?? Enumerable.Empty<string>());

if (assets?.ContentRoots is not null)
{
_contentRoots.AddRange(assets.ContentRoots);
}
}

private async Task SetDependencies()
{
var infos = Path.ChangeExtension(_path, ".deps.json");
using var fs = File.OpenRead(infos);
var deps = await JsonSerializer.DeserializeAsync<DependenciesList>(fs);

if (deps?.Libraries is not null)
{
_deps.AddRange(deps.Libraries);
}
}

protected override Assembly? GetAssembly() => Context.LoadFromAssemblyPath(_path);
Expand Down Expand Up @@ -68,8 +128,86 @@ protected override async Task OnInitialized()

protected override string GetCssName() => $"{Name}.styles.css";

private Assembly? AddAssemblyToContext(string dll)
{
foreach (var package in _packages)
{
var libItems = package.GetLibItems().FirstOrDefault(m => IsCompatible(m.TargetFramework))?.Items;

if (libItems is not null)
{
foreach (var lib in libItems)
{
if (lib.EndsWith(dll))
{
return LoadAssembly(package, lib);
}
}
}
}

return null;
}

private static bool IsCompatible(NuGetFramework framework)
{
var current = NuGetFramework.Parse(target);
return DefaultCompatibilityProvider.Instance.IsCompatible(current, framework);
}

private static async Task<MemoryStream?> GetFile(PackageArchiveReader package, string path)
{
try
{
var zip = package.GetEntry(path);

if (zip is not null)
{
using var zipStream = zip.Open();
var msStream = new MemoryStream();
await zipStream.CopyToAsync(msStream);
msStream.Position = 0;
return msStream;
}
}
catch (FileNotFoundException)
{
// This is expected - nothing wrong here
}
catch (InvalidDataException)
{
// This is not expected, but should be handled gracefully
}

return null;
}

class StaticWebAssets
{
public List<string>? ContentRoots { get; set; }
}

class DependenciesList
{
[JsonPropertyName("libraries")]
public Dictionary<string, DependencyDescription>? Libraries { get; set; }
}

class DependencyDescription
{
[JsonPropertyName("type")]
public string? Type { get; set; }

[JsonPropertyName("serviceable")]
public bool? IsServiceable { get; set; }

[JsonPropertyName("sha512")]
public string? SHA512 { get; set; }

[JsonPropertyName("path")]
public string? Path { get; set; }

[JsonPropertyName("hashPath")]
public string? HashPath { get; set; }
}
}

0 comments on commit 2285c2a

Please sign in to comment.