Skip to content

Commit

Permalink
Fix loading additional Handlebars.Net.Helpers dll's when running appl…
Browse files Browse the repository at this point in the history
…ication from commandline using 'dotnet run' (#97)

* Fix loading additional Handlebars.Net.Helpers dll's when running application from commandline using 'dotnet run'

* .

* var paths = options.CustomHelperPaths ?? new List<string>

* ...

* fix
  • Loading branch information
StefH authored Jul 12, 2024
1 parent 40368e1 commit 9d15bcb
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 41 deletions.
16 changes: 16 additions & 0 deletions src/Handlebars.Net.Helpers/Compatibility/AppContextHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.IO;

namespace HandlebarsDotNet.Helpers.Compatibility;

internal static class AppContextHelper
{
public static string GetBaseDirectory()
{
#if NETSTANDARD1_3_OR_GREATER || NET6_0_OR_GREATER
return AppContext.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar);
#else
return AppDomain.CurrentDomain.BaseDirectory.TrimEnd(Path.DirectorySeparatorChar);
#endif
}
}
35 changes: 34 additions & 1 deletion src/Handlebars.Net.Helpers/HandlebarsHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
using HandlebarsDotNet.Helpers.Utils;
using Stef.Validation;
using HandlebarsDotNet.Helpers.Models;
using System.Diagnostics;
using HandlebarsDotNet.Helpers.Compatibility;


#if NETSTANDARD1_3_OR_GREATER || NET46_OR_GREATER || NET6_0_OR_GREATER
using System.Threading;
#else
Expand Down Expand Up @@ -78,7 +82,36 @@ public static void Register(IHandlebars handlebarsContext, Action<HandlebarsHelp
{ Category.Xslt, "XsltHelpers" }
};

var paths = options.CustomHelperPaths ?? new List<string> { Directory.GetCurrentDirectory() };


List<string> paths;
if (options.CustomHelperPaths != null)
{
paths = options.CustomHelperPaths.ToList();
}
else
{
paths = new List<string>
{
Directory.GetCurrentDirectory(),
AppContextHelper.GetBaseDirectory(),
};

#if !NETSTANDARD1_3_OR_GREATER
void Add(string? path, ICollection<string> customHelperPaths)
{
if (!string.IsNullOrEmpty(path))
{
customHelperPaths.Add(path!);
}
}
Add(Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location), paths);
Add(Path.GetDirectoryName(Assembly.GetCallingAssembly().Location), paths);
Add(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), paths);
Add(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule?.FileName), paths);
#endif
}

var extraHelpers = PluginLoader.Load(paths, extra, handlebarsContext);

foreach (var item in extraHelpers)
Expand Down
11 changes: 5 additions & 6 deletions src/Handlebars.Net.Helpers/Options/HandlebarsHelpersOptions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.IO;
using HandlebarsDotNet.Helpers.Enums;
using HandlebarsDotNet.Helpers.Helpers;
using HandlebarsDotNet.Helpers.Utils;
Expand Down Expand Up @@ -36,20 +35,20 @@ public class HandlebarsHelpersOptions
/// <summary>
/// The categories to register. By default all categories are registered. See the WIKI for details.
/// </summary>
public Category[]? Categories { get; set; } = null;
public Category[]? Categories { get; set; }

/// <summary>
/// Used for unit-testing DateTime related functionality.
/// </summary>
public IDateTimeService? DateTimeService { get; set; } = null;
public IDateTimeService? DateTimeService { get; set; }

/// <summary>
/// A Dictionary with additional Custom Helpers (Key = CategoryPrefix, Value = IHelpers)
/// </summary>
public IDictionary<string, IHelpers>? CustomHelpers { get; set; } = null;
public IDictionary<string, IHelpers>? CustomHelpers { get; set; }

/// <summary>
/// The paths to search for additional helpers. If null, the CurrentDirectory is used.
/// The paths to search for additional helpers. If null, the CurrentDirectory and BaseDirectory are used.
/// </summary>
public IReadOnlyList<string>? CustomHelperPaths = new List<string> { Directory.GetCurrentDirectory() };
public IReadOnlyList<string>? CustomHelperPaths;
}
69 changes: 35 additions & 34 deletions src/Handlebars.Net.Helpers/Plugin/PluginLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,54 +6,55 @@
using HandlebarsDotNet.Helpers.Enums;
using HandlebarsDotNet.Helpers.Helpers;

namespace HandlebarsDotNet.Helpers.Plugin
namespace HandlebarsDotNet.Helpers.Plugin;

internal static class PluginLoader
{
internal static class PluginLoader
public static IDictionary<Category, IHelpers> Load(IEnumerable<string> paths, IDictionary<Category, string> items, params object[] args)
{
public static IDictionary<Category, IHelpers> Load(IEnumerable<string> paths, IDictionary<Category, string> items, params object[] args)
var helpers = new Dictionary<Category, IHelpers>();

var pluginTypes = new List<Type>();
try
{
var pluginTypes = new List<Type>();
try
foreach (var file in paths.Distinct().SelectMany(path => Directory.GetFiles(path, "Handlebars*dll")).Distinct())
{
foreach (var file in paths.SelectMany(path => Directory.GetFiles(path, "*.dll")))
try
{
try
{
var assembly = Assembly.Load(new AssemblyName
{
Name = Path.GetFileNameWithoutExtension(file)
});

pluginTypes.AddRange(GetImplementationTypeByInterface(assembly));
}
catch
var assembly = Assembly.Load(new AssemblyName
{
// no-op: just try next .dll
}
}
}
catch
{
// no-op: file system access possibly denied, don't search for files
}

Name = Path.GetFileNameWithoutExtension(file)
});

var helpers = new Dictionary<Category, IHelpers>();
foreach (var item in items)
{
var matchingType = pluginTypes.FirstOrDefault(pt => pt.Name == item.Value);
if (matchingType is { })
pluginTypes.AddRange(GetImplementationTypeByInterface(assembly));
}
catch
{
helpers.Add(item.Key, (IHelpers)Activator.CreateInstance(matchingType, args)!);
// Just try next .dll
}
}

}
catch
{
// File system access possibly denied, don't search for any more files
return helpers;
}

private static IEnumerable<Type> GetImplementationTypeByInterface(Assembly assembly)

foreach (var item in items)
{
return assembly.GetTypes().Where(t => typeof(IHelpers).IsAssignableFrom(t) && !t.GetTypeInfo().IsInterface);
var matchingType = pluginTypes.FirstOrDefault(pt => pt.Name == item.Value);
if (matchingType is not null)
{
helpers.Add(item.Key, (IHelpers)Activator.CreateInstance(matchingType, args)!);
}
}

return helpers;
}

private static IEnumerable<Type> GetImplementationTypeByInterface(Assembly assembly)
{
return assembly.GetTypes().Where(t => typeof(IHelpers).IsAssignableFrom(t) && !t.GetTypeInfo().IsInterface);
}
}

0 comments on commit 9d15bcb

Please sign in to comment.