Skip to content

Commit

Permalink
Add startup & manifest for razor component modules. (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
StardustDL authored Mar 4, 2021
1 parent c39258c commit d242dd5
Show file tree
Hide file tree
Showing 30 changed files with 309 additions and 113 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ jobs:
uses: actions/checkout@v2
- name: Install docfx
run: choco install docfx -y
- name: Publish
run: ./build.ps1 -t publish
- name: Integration
run: ./build.ps1 -t integration
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
Expand Down
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,44 @@ It provides a place to unify resources, and it can be used to make Razor compone

1. Register modules.

For general modules (includes Razor component & AspNet server modules):
For general modules:

```cs
services.AddModules(builder => {
builder.AddModule<FooModule>();
});
```

2. Configure the module initilizing & shutdown.
1. Configure the module initilizing & shutdown.

```cs
var host = services.GetModuleHost();
await host.Initialize();

// do something
await host.Shutdown();

// Or use context:
// context: IServiceProvider services (provided by package Modulight.Modules.Core)
await using var _ = await services.UseModuleHost();

// do something
```

### Addition steps

#### Use Razor component modules

```cs
// in Startup: void ConfigureServices(ISeviceCollection services)
services.AddModules(builder => {
builder.UseRazorComponentClientModules().AddModule<FooModule>();
});
```

For razor components, add `ResourceDeclare` component to App.razor to load UI resources.

```razor
Expand Down
4 changes: 2 additions & 2 deletions build/Build.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Cake.Coverlet" Version="2.5.1" />
<PackageReference Include="Cake.Coverlet" Version="2.5.4" />
<PackageReference Include="Cake.DocFx" Version="0.13.1" />
<PackageReference Include="Cake.Frosting" Version="1.0.0-rc0003" />
<PackageReference Include="Cake.Frosting" Version="1.0.0" />
<PackageReference Include="Cake.Issues.PullRequests.GitHubActions" Version="0.9.0" />
</ItemGroup>
</Project>
4 changes: 1 addition & 3 deletions build/IntegrationTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
namespace Build
{
[TaskName("Integration")]
[IsDependentOn(typeof(BuildTask))]
[IsDependentOn(typeof(TestTask))]
[IsDependentOn(typeof(PackTask))]
[IsDependentOn(typeof(DocumentTask))]
[IsDependentOn(typeof(PublishTask))]
public class IntegrationTask : FrostingTask<BuildContext>
{

Expand Down
5 changes: 0 additions & 5 deletions global.json

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,6 @@ public interface IRazorComponentClientModule : IModule
/// </summary>
RenderFragment? Icon { get; }

/// <summary>
/// Get module UI resources.
/// </summary>
UIResource[] Resources { get; }

/// <summary>
/// Get global components.
/// </summary>
Type[] GlobalComponents { get; }

/// <summary>
/// Get module UI route root path, such as home, search, and so on.
/// Use <see cref="string.Empty"/> for no page module.
Expand Down Expand Up @@ -63,24 +53,7 @@ protected RazorComponentClientModule(IModuleHost host) : base(host)
if (attr is not null)
RootPath = attr.RootPath;
}
{
var attrs = type.GetCustomAttributes<ModuleUIResourceAttribute>();
List<UIResource> resources = new List<UIResource>();
foreach (var attr in attrs)
{
resources.Add(new UIResource(attr.Type, attr.Path) { Attributes = attr.Attributes });
}
Resources = resources.ToArray();
}
{
var attrs = type.GetCustomAttributes<ModuleUIGlobalComponentAttribute>();
List<Type> resources = new List<Type>();
foreach (var attr in attrs)
{
resources.Add(attr.Type);
}
GlobalComponents = resources.ToArray();
}

}

/// <inheritdoc/>
Expand All @@ -99,12 +72,6 @@ public virtual bool Contains(string path)
path = path.Trim('/') + "/";
return path.StartsWith($"{RootPath}/");
}

/// <inheritdoc/>
public UIResource[] Resources { get; protected set; }

/// <inheritdoc/>
public Type[] GlobalComponents { get; protected set; }
}

[Module(Author = "StardustDL", Description = "Provide services for razor component client modules.", Url = "https://github.com/StardustDL/delights")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@

namespace Modulight.Modules.Client.RazorComponents
{

/// <summary>
/// Specifies the contract for razor component module hosts.
/// </summary>
public interface IRazorComponentClientModuleCollection : IModuleCollection<IRazorComponentClientModule>
{
/// <summary>
/// Get manifest for the module.
/// </summary>
RazorComponentClientModuleManifest GetManifest(Type module);

/// <summary>
/// Load related assemblies for a given route.
/// </summary>
Expand Down Expand Up @@ -58,9 +64,15 @@ public static class RazorComponentClientModuleCollectionExtensions

internal class RazorComponentClientModuleCollection : ModuleHostFilterCollection<IRazorComponentClientModule>, IRazorComponentClientModuleCollection
{
Dictionary<Type, RazorComponentClientModuleManifest> Manifests { get; } = new Dictionary<Type, RazorComponentClientModuleManifest>();

public RazorComponentClientModuleCollection(IModuleHost host) : base(host)
{
Logger = host.Services.GetRequiredService<ILogger<RazorComponentClientModuleCollection>>();
foreach (var item in host.Services.GetServices<ModuleManifestItem>())
{
Manifests.Add(item.Type, item.Manifest);
}
}

public ILogger<RazorComponentClientModuleCollection> Logger { get; }
Expand All @@ -77,9 +89,9 @@ public async Task LoadResources(Type? moduleType = null)
targetModules = targetModules.Where(x => x.GetType().IsModule(moduleType));
}

foreach (var module in targetModules)
foreach (var manifest in Manifests.Values)
{
foreach (var resource in module.Resources)
foreach (var resource in manifest.Resources)
{
try
{
Expand All @@ -95,7 +107,7 @@ public async Task LoadResources(Type? moduleType = null)
}
catch (JSException ex)
{
Logger.LogError(ex, $"Failed to load resource {resource.Path} in module {module.Manifest.Name}");
Logger.LogError(ex, $"Failed to load resource {resource.Path}");
}
}
}
Expand Down Expand Up @@ -141,7 +153,7 @@ public async Task<List<Assembly>> GetAssembliesForRouting(string path, bool recu

if (module.Contains(path))
{
foreach (var resource in module.Resources.Where(x => x.Type is UIResourceType.Assembly))
foreach (var resource in GetManifest(module.GetType()).Resources.Where(x => x.Type is UIResourceType.Assembly))
{
cancellationToken.ThrowIfCancellationRequested();

Expand Down Expand Up @@ -207,5 +219,7 @@ public async Task<List<Assembly>> GetAssembliesForRouting(string path, bool recu

return results;
}

public RazorComponentClientModuleManifest GetManifest(Type module) => Manifests[module];
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Modulight.Modules.Client.RazorComponents.UI;
using Modulight.Modules.Hosting;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Modulight.Modules.Client.RazorComponents
{
Expand All @@ -10,11 +14,87 @@ namespace Modulight.Modules.Client.RazorComponents
/// </summary>
public static class RazorComponentClientModuleExtensions
{
/// <summary>
/// Use building plugin for graphql modules.
/// </summary>
/// <param name="modules"></param>
/// <returns></returns>
public static IModuleHostBuilder UseRazorComponentClientModules(this IModuleHostBuilder modules)
{
return modules.ConfigureBuilderServices(services =>
{
services.TryAddTransient<IRazorComponentClientModuleManifestBuilder, DefaultRazorComponentClientModuleManifestBuilder>();
}).UsePlugin<RazorComponentClientModulePlugin>();
}

/// <summary>
/// Get razor component module host from service provider.
/// </summary>
/// <param name="provider"></param>
/// <returns></returns>
public static IRazorComponentClientModuleCollection GetRazorComponentClientModuleCollection(this IServiceProvider provider) => provider.GetRequiredService<IRazorComponentClientModuleCollection>();

/// <summary>
/// Add resource to manifest.
/// </summary>
/// <param name="builder"></param>
/// <param name="resource"></param>
/// <returns></returns>
public static IRazorComponentClientModuleManifestBuilder WithResource(this IRazorComponentClientModuleManifestBuilder builder, UIResource resource)
{
builder.Resources.Add(resource);
return builder;
}

/// <summary>
/// Add global component to manifest.
/// </summary>
/// <param name="type"></param>
/// <param name="builder"></param>
/// <returns></returns>
public static IRazorComponentClientModuleManifestBuilder WithGlobalComponent(this IRazorComponentClientModuleManifestBuilder builder, Type type)
{
builder.GlobalComponents.Add(type);
return builder;
}

/// <summary>
/// Add global component to manifest.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="builder"></param>
/// <returns></returns>
public static IRazorComponentClientModuleManifestBuilder WithGlobalComponent<T>(this IRazorComponentClientModuleManifestBuilder builder) where T : IComponent
{
builder.GlobalComponents.Add(typeof(T));
return builder;
}

/// <summary>
/// Configure the builder by default from attributes.
/// </summary>
/// <param name="builder"></param>
/// <param name="type"></param>
/// <returns></returns>
public static IRazorComponentClientModuleManifestBuilder WithDefaultsFromModuleType(this IRazorComponentClientModuleManifestBuilder builder, Type type)
{
{
var attrs = type.GetCustomAttributes<ModuleUIResourceAttribute>();
List<UIResource> resources = new List<UIResource>();
foreach (var attr in attrs)
{
builder.WithResource(new UIResource(attr.Type, attr.Path) { Attributes = attr.Attributes });
}
}
{
var attrs = type.GetCustomAttributes<ModuleUIGlobalComponentAttribute>();
List<Type> resources = new List<Type>();
foreach (var attr in attrs)
{
builder.WithGlobalComponent(attr.Type);
}
}
return builder;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Modulight.Modules.Client.RazorComponents.UI;
using System;
using System.Linq;

namespace Modulight.Modules.Client.RazorComponents
{
/// <summary>
/// Manifest for <see cref="RazorComponentClientModule"/>.
/// </summary>
public record RazorComponentClientModuleManifest
{
/// <summary>
/// Get module UI resources.
/// </summary>
public UIResource[] Resources { get; init; } = Array.Empty<UIResource>();

/// <summary>
/// Get global components.
/// </summary>
public Type[] GlobalComponents { get; init; } = Array.Empty<Type>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using Modulight.Modules.Client.RazorComponents.UI;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Modulight.Modules.Client.RazorComponents
{
/// <summary>
/// Specifies the interface to build a module manifest.
/// </summary>
public interface IRazorComponentClientModuleManifestBuilder
{
/// <summary>
/// Get module UI resources.
/// </summary>
IList<UIResource> Resources { get; }

/// <summary>
/// Get global components.
/// </summary>
IList<Type> GlobalComponents { get; }

/// <summary>
/// Build the manifest.
/// </summary>
/// <returns></returns>
RazorComponentClientModuleManifest Build();
}

class DefaultRazorComponentClientModuleManifestBuilder : IRazorComponentClientModuleManifestBuilder
{
public IList<UIResource> Resources { get; } = new List<UIResource>();

public IList<Type> GlobalComponents { get; } = new List<Type>();

public RazorComponentClientModuleManifest Build()
{
return new RazorComponentClientModuleManifest
{
GlobalComponents = GlobalComponents.ToArray(),
Resources = Resources.ToArray()
};
}
}
}
Loading

0 comments on commit d242dd5

Please sign in to comment.