mirror of https://github.com/abpframework/abp.git
40 changed files with 861 additions and 249 deletions
@ -0,0 +1,19 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>net9.0</TargetFramework> |
||||
|
<Nullable>enable</Nullable> |
||||
|
<WarningsAsErrors>Nullable</WarningsAsErrors> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\Volo.Abp.Minify\Volo.Abp.Minify.csproj" /> |
||||
|
<ProjectReference Include="..\Volo.Abp.VirtualFileSystem\Volo.Abp.VirtualFileSystem.csproj" /> |
||||
|
<ProjectReference Include="..\Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions\Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,15 @@ |
|||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
||||
|
using Volo.Abp.Minify; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Bundling; |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpAspNetCoreMvcUiBundlingAbstractionsModule), |
||||
|
typeof(AbpMinifyModule), |
||||
|
typeof(AbpVirtualFileSystemModule) |
||||
|
)] |
||||
|
public class AbpAspNetCoreBundlingModule : AbpModule |
||||
|
{ |
||||
|
} |
||||
@ -1,9 +1,10 @@ |
|||||
using System; |
using System; |
||||
using System.Collections.Concurrent; |
using System.Collections.Concurrent; |
||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
||||
using Volo.Abp.DependencyInjection; |
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
namespace Volo.Abp.AspNetCore.Bundling; |
||||
|
|
||||
public class BundleCache : IBundleCache, ISingletonDependency |
public class BundleCache : IBundleCache, ISingletonDependency |
||||
{ |
{ |
||||
@ -1,7 +1,8 @@ |
|||||
using System; |
using System; |
||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
||||
|
|
||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
namespace Volo.Abp.AspNetCore.Bundling; |
||||
|
|
||||
public class BundleCacheItem |
public class BundleCacheItem |
||||
{ |
{ |
||||
@ -0,0 +1,248 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Collections.Immutable; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.Extensions.FileProviders; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.AspNetCore.Bundling.Scripts; |
||||
|
using Volo.Abp.AspNetCore.Bundling.Styles; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Bundling; |
||||
|
|
||||
|
public abstract class BundleManagerBase : IBundleManager |
||||
|
{ |
||||
|
public ILogger<BundleManagerBase> Logger { get; set; } |
||||
|
|
||||
|
protected readonly AbpBundlingOptions Options; |
||||
|
protected readonly AbpBundleContributorOptions ContributorOptions; |
||||
|
protected readonly IScriptBundler ScriptBundler; |
||||
|
protected readonly IStyleBundler StyleBundler; |
||||
|
protected readonly IServiceProvider ServiceProvider; |
||||
|
protected readonly IDynamicFileProvider DynamicFileProvider; |
||||
|
protected readonly IBundleCache BundleCache; |
||||
|
|
||||
|
public BundleManagerBase( |
||||
|
IOptions<AbpBundlingOptions> options, |
||||
|
IOptions<AbpBundleContributorOptions> contributorOptions, |
||||
|
IScriptBundler scriptBundler, |
||||
|
IStyleBundler styleBundler, |
||||
|
IServiceProvider serviceProvider, |
||||
|
IDynamicFileProvider dynamicFileProvider, |
||||
|
IBundleCache bundleCache) |
||||
|
{ |
||||
|
Options = options.Value; |
||||
|
ContributorOptions = contributorOptions.Value; |
||||
|
ScriptBundler = scriptBundler; |
||||
|
ServiceProvider = serviceProvider; |
||||
|
DynamicFileProvider = dynamicFileProvider; |
||||
|
BundleCache = bundleCache; |
||||
|
StyleBundler = styleBundler; |
||||
|
|
||||
|
Logger = NullLogger<BundleManagerBase>.Instance; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<IReadOnlyList<BundleFile>> GetStyleBundleFilesAsync(string bundleName) |
||||
|
{ |
||||
|
return await GetBundleFilesAsync(Options.StyleBundles, bundleName, StyleBundler); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<IReadOnlyList<BundleFile>> GetScriptBundleFilesAsync(string bundleName) |
||||
|
{ |
||||
|
return await GetBundleFilesAsync(Options.ScriptBundles, bundleName, ScriptBundler); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task<IReadOnlyList<BundleFile>> GetBundleFilesAsync(BundleConfigurationCollection bundles, |
||||
|
string bundleName, IBundler bundler) |
||||
|
{ |
||||
|
var files = new List<BundleFile>(); |
||||
|
|
||||
|
var contributors = GetContributors(bundles, bundleName); |
||||
|
var bundleFiles = await GetBundleFilesAsync(contributors); |
||||
|
var dynamicResources = await GetDynamicResourcesAsync(contributors); |
||||
|
|
||||
|
if (!IsBundlingEnabled()) |
||||
|
{ |
||||
|
return bundleFiles.Union(dynamicResources).ToImmutableList(); |
||||
|
} |
||||
|
|
||||
|
var localBundleFiles = new List<string>(); |
||||
|
foreach (var bundleFile in bundleFiles) |
||||
|
{ |
||||
|
if (!bundleFile.IsExternalFile) |
||||
|
{ |
||||
|
localBundleFiles.Add(bundleFile.FileName); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (localBundleFiles.Count != 0) |
||||
|
{ |
||||
|
files.AddRange(AddToBundleCache(bundleName, bundler, localBundleFiles).Files); |
||||
|
localBundleFiles.Clear(); |
||||
|
} |
||||
|
|
||||
|
files.Add(bundleFile); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (localBundleFiles.Count != 0) |
||||
|
{ |
||||
|
files.AddRange(AddToBundleCache(bundleName, bundler, localBundleFiles).Files); |
||||
|
} |
||||
|
|
||||
|
return files.Union(dynamicResources).ToImmutableList(); |
||||
|
} |
||||
|
|
||||
|
private BundleCacheItem AddToBundleCache(string bundleName, IBundler bundler, List<string> bundleFiles) |
||||
|
{ |
||||
|
var bundleRelativePath = |
||||
|
Options.BundleFolderName.EnsureEndsWith('/') + |
||||
|
bundleName + "." + bundleFiles.JoinAsString("|").ToMd5() + "." + bundler.FileExtension; |
||||
|
|
||||
|
return BundleCache.GetOrAdd(bundleRelativePath, () => |
||||
|
{ |
||||
|
var cacheValue = new BundleCacheItem( |
||||
|
new List<BundleFile> { new BundleFile("/" + bundleRelativePath) } |
||||
|
); |
||||
|
|
||||
|
WatchChanges(cacheValue, bundleFiles, bundleRelativePath); |
||||
|
|
||||
|
var bundleResult = bundler.Bundle( |
||||
|
new BundlerContext( |
||||
|
bundleRelativePath, |
||||
|
bundleFiles, |
||||
|
IsMinficationEnabled() |
||||
|
) |
||||
|
); |
||||
|
|
||||
|
SaveBundleResult(bundleRelativePath, bundleResult); |
||||
|
|
||||
|
return cacheValue; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
private void WatchChanges(BundleCacheItem cacheValue, List<string> files, string bundleRelativePath) |
||||
|
{ |
||||
|
lock (cacheValue.WatchDisposeHandles) |
||||
|
{ |
||||
|
foreach (var file in files) |
||||
|
{ |
||||
|
var watchDisposeHandle = GetFileProvider().Watch(file).RegisterChangeCallback(_ => |
||||
|
{ |
||||
|
lock (cacheValue.WatchDisposeHandles) |
||||
|
{ |
||||
|
cacheValue.WatchDisposeHandles.ForEach(h => h.Dispose()); |
||||
|
cacheValue.WatchDisposeHandles.Clear(); |
||||
|
} |
||||
|
|
||||
|
BundleCache.Remove(bundleRelativePath); |
||||
|
DynamicFileProvider.Delete("/wwwroot/" + bundleRelativePath); //TODO: get rid of wwwroot!
|
||||
|
}, null); |
||||
|
|
||||
|
cacheValue.WatchDisposeHandles.Add(watchDisposeHandle); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual void SaveBundleResult(string bundleRelativePath, BundleResult bundleResult) |
||||
|
{ |
||||
|
var fileName = bundleRelativePath.Substring(bundleRelativePath.IndexOf('/') + 1); |
||||
|
|
||||
|
DynamicFileProvider.AddOrUpdate( |
||||
|
new InMemoryFileInfo( |
||||
|
"/wwwroot/" + bundleRelativePath, //TODO: get rid of wwwroot!
|
||||
|
Encoding.UTF8.GetBytes(bundleResult.Content), |
||||
|
fileName |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
public abstract bool IsBundlingEnabled(); |
||||
|
|
||||
|
|
||||
|
protected abstract bool IsMinficationEnabled(); |
||||
|
|
||||
|
protected virtual async Task<List<BundleFile>> GetBundleFilesAsync(List<IBundleContributor> contributors) |
||||
|
{ |
||||
|
var context = CreateBundleConfigurationContext(); |
||||
|
|
||||
|
foreach (var contributor in contributors) |
||||
|
{ |
||||
|
await contributor.PreConfigureBundleAsync(context); |
||||
|
} |
||||
|
|
||||
|
foreach (var contributor in contributors) |
||||
|
{ |
||||
|
await contributor.ConfigureBundleAsync(context); |
||||
|
} |
||||
|
|
||||
|
foreach (var contributor in contributors) |
||||
|
{ |
||||
|
await contributor.PostConfigureBundleAsync(context); |
||||
|
} |
||||
|
|
||||
|
return context.Files; |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task<List<BundleFile>> GetDynamicResourcesAsync(List<IBundleContributor> contributors) |
||||
|
{ |
||||
|
var context = CreateBundleConfigurationContext(); |
||||
|
|
||||
|
foreach (var contributor in contributors) |
||||
|
{ |
||||
|
await contributor.ConfigureDynamicResourcesAsync(context); |
||||
|
} |
||||
|
|
||||
|
return context.Files; |
||||
|
} |
||||
|
|
||||
|
protected virtual BundleConfigurationContext CreateBundleConfigurationContext() |
||||
|
{ |
||||
|
return new BundleConfigurationContext(ServiceProvider, GetFileProvider(), |
||||
|
Options.Parameters); |
||||
|
} |
||||
|
|
||||
|
protected abstract IFileProvider GetFileProvider(); |
||||
|
|
||||
|
protected virtual List<IBundleContributor> GetContributors(BundleConfigurationCollection bundles, string bundleName) |
||||
|
{ |
||||
|
var contributors = new List<IBundleContributor>(); |
||||
|
|
||||
|
AddContributorsWithBaseBundles(contributors, bundles, bundleName); |
||||
|
|
||||
|
for (var i = 0; i < contributors.Count; ++i) |
||||
|
{ |
||||
|
var extensions = ContributorOptions.Extensions(contributors[i].GetType()).GetAll(); |
||||
|
if (extensions.Count > 0) |
||||
|
{ |
||||
|
contributors.InsertRange(i + 1, extensions); |
||||
|
i += extensions.Count; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return contributors; |
||||
|
} |
||||
|
|
||||
|
protected virtual void AddContributorsWithBaseBundles(List<IBundleContributor> contributors, |
||||
|
BundleConfigurationCollection bundles, string bundleName) |
||||
|
{ |
||||
|
var bundleConfiguration = bundles.Get(bundleName); |
||||
|
|
||||
|
foreach (var baseBundleName in bundleConfiguration.BaseBundles) |
||||
|
{ |
||||
|
AddContributorsWithBaseBundles(contributors, bundles, baseBundleName); //Recursive call
|
||||
|
} |
||||
|
|
||||
|
var selfContributors = bundleConfiguration.Contributors.GetAll(); |
||||
|
|
||||
|
if (selfContributors.Any()) |
||||
|
{ |
||||
|
contributors.AddRange(selfContributors); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,4 +1,4 @@ |
|||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
namespace Volo.Abp.AspNetCore.Bundling; |
||||
|
|
||||
public class BundleResult |
public class BundleResult |
||||
{ |
{ |
||||
@ -1,6 +1,6 @@ |
|||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
|
|
||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
namespace Volo.Abp.AspNetCore.Bundling; |
||||
|
|
||||
public class BundlerContext : IBundlerContext |
public class BundlerContext : IBundlerContext |
||||
{ |
{ |
||||
@ -1,6 +1,6 @@ |
|||||
using System; |
using System; |
||||
|
|
||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
namespace Volo.Abp.AspNetCore.Bundling; |
||||
|
|
||||
public interface IBundleCache |
public interface IBundleCache |
||||
{ |
{ |
||||
@ -1,7 +1,8 @@ |
|||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
using System.Threading.Tasks; |
using System.Threading.Tasks; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
||||
|
|
||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
namespace Volo.Abp.AspNetCore.Bundling; |
||||
|
|
||||
public interface IBundleManager |
public interface IBundleManager |
||||
{ |
{ |
||||
@ -1,4 +1,6 @@ |
|||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Bundling; |
||||
|
|
||||
public interface IBundler |
public interface IBundler |
||||
{ |
{ |
||||
@ -1,6 +1,6 @@ |
|||||
using System.Collections.Generic; |
using System.Collections.Generic; |
||||
|
|
||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
namespace Volo.Abp.AspNetCore.Bundling; |
||||
|
|
||||
public interface IBundlerContext |
public interface IBundlerContext |
||||
{ |
{ |
||||
@ -0,0 +1,6 @@ |
|||||
|
namespace Volo.Abp.AspNetCore.Bundling.Scripts; |
||||
|
|
||||
|
public interface IScriptBundler : IBundler |
||||
|
{ |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
namespace Volo.Abp.AspNetCore.Bundling.Styles; |
||||
|
|
||||
|
public interface IStyleBundler : IBundler |
||||
|
{ |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,106 @@ |
|||||
|
using System.Text; |
||||
|
using Microsoft.Extensions.FileProviders; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.AspNetCore.Bundling; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
||||
|
using Volo.Abp.Bundling.Styles; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.Threading; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling; |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpAspNetCoreComponentsMauiBlazorModule), |
||||
|
typeof(AbpAspNetCoreBundlingModule) |
||||
|
)] |
||||
|
public class AbpAspNetCoreComponentsMauiBlazorBundlingModule : AbpModule |
||||
|
{ |
||||
|
public override void OnApplicationInitialization(ApplicationInitializationContext context) |
||||
|
{ |
||||
|
AsyncHelper.RunSync(() => OnApplicationInitializationAsync(context)); |
||||
|
} |
||||
|
|
||||
|
public async override Task OnApplicationInitializationAsync(ApplicationInitializationContext context) |
||||
|
{ |
||||
|
await InitialGlobalAssetsAsync(context); |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task InitialGlobalAssetsAsync(ApplicationInitializationContext context) |
||||
|
{ |
||||
|
var bundlingOptions = context.ServiceProvider.GetRequiredService<IOptions<AbpBundlingOptions>>().Value; |
||||
|
var logger = context.ServiceProvider.GetRequiredService<ILogger<AbpAspNetCoreComponentsMauiBlazorBundlingModule>>(); |
||||
|
if (!bundlingOptions.GlobalAssets.Enabled) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var bundleManager = context.ServiceProvider.GetRequiredService<BundleManager>(); |
||||
|
var mauiBlazorContentFileProvider = context.ServiceProvider.GetRequiredService<IMauiBlazorContentFileProvider>(); |
||||
|
var dynamicFileProvider = context.ServiceProvider.GetRequiredService<IDynamicFileProvider>(); |
||||
|
if (!bundlingOptions.GlobalAssets.GlobalStyleBundleName.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
var styleFiles = await bundleManager.GetStyleBundleFilesAsync(bundlingOptions.GlobalAssets.GlobalStyleBundleName); |
||||
|
var styles = string.Empty; |
||||
|
foreach (var file in styleFiles) |
||||
|
{ |
||||
|
var fileInfo = mauiBlazorContentFileProvider.GetFileInfo(file.FileName); |
||||
|
if (!fileInfo.Exists) |
||||
|
{ |
||||
|
logger.LogError($"Could not find the file: {file.FileName}"); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
var fileContent = await fileInfo.ReadAsStringAsync(); |
||||
|
if (!bundleManager.IsBundlingEnabled()) |
||||
|
{ |
||||
|
fileContent = CssRelativePath.Adjust(fileContent, |
||||
|
file.FileName, |
||||
|
Path.Combine(Directory.GetCurrentDirectory(), "wwwroot")); |
||||
|
|
||||
|
styles += $"/*{file.FileName}*/{Environment.NewLine}{fileContent}{Environment.NewLine}{Environment.NewLine}"; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
styles += $"{fileContent}{Environment.NewLine}{Environment.NewLine}"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
dynamicFileProvider.AddOrUpdate( |
||||
|
new InMemoryFileInfo("/wwwroot/" + bundlingOptions.GlobalAssets.CssFileName, |
||||
|
Encoding.UTF8.GetBytes(styles), |
||||
|
bundlingOptions.GlobalAssets.CssFileName)); |
||||
|
} |
||||
|
|
||||
|
if (!bundlingOptions.GlobalAssets.GlobalScriptBundleName.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
var scriptFiles = await bundleManager.GetScriptBundleFilesAsync(bundlingOptions.GlobalAssets.GlobalScriptBundleName); |
||||
|
var scripts = string.Empty; |
||||
|
foreach (var file in scriptFiles) |
||||
|
{ |
||||
|
var fileInfo = mauiBlazorContentFileProvider.GetFileInfo(file.FileName); |
||||
|
if (!fileInfo.Exists) |
||||
|
{ |
||||
|
logger.LogError($"Could not find the file: {file.FileName}"); |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
var fileContent = await fileInfo.ReadAsStringAsync(); |
||||
|
if (!bundleManager.IsBundlingEnabled()) |
||||
|
{ |
||||
|
scripts += $"{fileContent.EnsureEndsWith(';')}{Environment.NewLine}{Environment.NewLine}"; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
scripts += $"//{file.FileName}{Environment.NewLine}{fileContent.EnsureEndsWith(';')}{Environment.NewLine}{Environment.NewLine}"; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
dynamicFileProvider.AddOrUpdate( |
||||
|
new InMemoryFileInfo("/wwwroot/" + bundlingOptions.GlobalAssets.JavaScriptFileName, |
||||
|
Encoding.UTF8.GetBytes(scripts), |
||||
|
bundlingOptions.GlobalAssets.JavaScriptFileName)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using Microsoft.AspNetCore.Components.WebView.Maui; |
||||
|
using Microsoft.Extensions.FileProviders; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling; |
||||
|
|
||||
|
public class AbpBlazorWebView : BlazorWebView |
||||
|
{ |
||||
|
public override IFileProvider CreateFileProvider(string contentRootDir) |
||||
|
{ |
||||
|
return new CompositeFileProvider(Handler!.GetRequiredService<IMauiBlazorContentFileProvider>(), base.CreateFileProvider(contentRootDir)); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,125 @@ |
|||||
|
using Microsoft.Extensions.FileProviders; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.AspNetCore.Bundling; |
||||
|
using Volo.Abp.AspNetCore.Bundling.Scripts; |
||||
|
using Volo.Abp.AspNetCore.Bundling.Styles; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling; |
||||
|
|
||||
|
public class BundleManager : BundleManagerBase, ITransientDependency |
||||
|
{ |
||||
|
protected IMauiBlazorContentFileProvider MauiBlazorContentFileProvider { get; } |
||||
|
|
||||
|
public BundleManager( |
||||
|
IOptions<AbpBundlingOptions> options, |
||||
|
IOptions<AbpBundleContributorOptions> contributorOptions, |
||||
|
IScriptBundler scriptBundler, |
||||
|
IStyleBundler styleBundler, |
||||
|
IServiceProvider serviceProvider, |
||||
|
IDynamicFileProvider dynamicFileProvider, |
||||
|
IBundleCache bundleCache, |
||||
|
IMauiBlazorContentFileProvider mauiBlazorContentFileProvider) : base( |
||||
|
options, |
||||
|
contributorOptions, |
||||
|
scriptBundler, |
||||
|
styleBundler, |
||||
|
serviceProvider, |
||||
|
dynamicFileProvider, |
||||
|
bundleCache) |
||||
|
{ |
||||
|
MauiBlazorContentFileProvider = mauiBlazorContentFileProvider; |
||||
|
} |
||||
|
|
||||
|
public override bool IsBundlingEnabled() |
||||
|
{ |
||||
|
switch (Options.Mode) |
||||
|
{ |
||||
|
case BundlingMode.None: |
||||
|
return false; |
||||
|
case BundlingMode.Bundle: |
||||
|
case BundlingMode.BundleAndMinify: |
||||
|
return true; |
||||
|
case BundlingMode.Auto: |
||||
|
return !IsDebug(); |
||||
|
default: |
||||
|
throw new AbpException($"Unhandled {nameof(BundlingMode)}: {Options.Mode}"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected async override Task<List<BundleFile>> GetBundleFilesAsync(List<IBundleContributor> contributors) |
||||
|
{ |
||||
|
var files = await base.GetBundleFilesAsync(contributors); |
||||
|
|
||||
|
foreach (var file in files) |
||||
|
{ |
||||
|
await CopyFileToAppDataDirectoryAsync(file); |
||||
|
} |
||||
|
|
||||
|
return files; |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task CopyFileToAppDataDirectoryAsync(BundleFile file) |
||||
|
{ |
||||
|
if (file.IsExternalFile) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var fileName = Path.Combine("wwwroot", file.FileName); |
||||
|
if(MauiBlazorContentFileProvider.GetFileInfo(fileName).Exists) |
||||
|
{ |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
await using var inputStream = await FileSystem.Current.OpenAppPackageFileAsync(fileName); |
||||
|
var targetFile = Path.Combine(FileSystem.Current.AppDataDirectory, fileName); |
||||
|
var fileDirectory = Path.GetDirectoryName(targetFile)!; |
||||
|
if (!Path.Exists(fileDirectory)) |
||||
|
{ |
||||
|
Directory.CreateDirectory(fileDirectory); |
||||
|
} |
||||
|
await using var outputStream = File.Create(targetFile); |
||||
|
await inputStream.CopyToAsync(outputStream); |
||||
|
} |
||||
|
catch (Exception e) |
||||
|
{ |
||||
|
Logger.LogError($"Could not copy the file to the app data directory: {fileName}", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected override bool IsMinficationEnabled() |
||||
|
{ |
||||
|
switch (Options.Mode) |
||||
|
{ |
||||
|
case BundlingMode.None: |
||||
|
case BundlingMode.Bundle: |
||||
|
return false; |
||||
|
case BundlingMode.BundleAndMinify: |
||||
|
return true; |
||||
|
case BundlingMode.Auto: |
||||
|
return !IsDebug(); |
||||
|
default: |
||||
|
throw new AbpException($"Unhandled {nameof(BundlingMode)}: {Options.Mode}"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected virtual bool IsDebug() |
||||
|
{ |
||||
|
#if DEBUG
|
||||
|
return true; |
||||
|
#else
|
||||
|
retur false; |
||||
|
#endif
|
||||
|
} |
||||
|
|
||||
|
protected override IFileProvider GetFileProvider() |
||||
|
{ |
||||
|
return MauiBlazorContentFileProvider; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
using Microsoft.Extensions.FileProviders; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling; |
||||
|
|
||||
|
public interface IMauiBlazorContentFileProvider : IFileProvider |
||||
|
{ |
||||
|
string ContentRootPath { get; } |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
using Microsoft.Extensions.FileProviders; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.AspNetCore.Bundling; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
||||
|
using Volo.Abp.Minify; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling; |
||||
|
|
||||
|
public abstract class MauiBlazorBundlerBase : BundlerBase |
||||
|
{ |
||||
|
protected IMauiBlazorContentFileProvider MauiBlazorContentFileProvider { get; } |
||||
|
|
||||
|
public MauiBlazorBundlerBase( |
||||
|
IMauiBlazorContentFileProvider mauiBlazorContentFileProvider, |
||||
|
IMinifier minifier, |
||||
|
IOptions<AbpBundlingOptions> bundlingOptions) : base(minifier, |
||||
|
bundlingOptions) |
||||
|
{ |
||||
|
MauiBlazorContentFileProvider = mauiBlazorContentFileProvider; |
||||
|
} |
||||
|
|
||||
|
protected override IFileInfo FindFileInfo(string file) |
||||
|
{ |
||||
|
return MauiBlazorContentFileProvider.GetFileInfo(file); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,65 @@ |
|||||
|
using Microsoft.Extensions.FileProviders; |
||||
|
using Microsoft.Extensions.Primitives; |
||||
|
using Microsoft.Maui.Controls.PlatformConfiguration; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling; |
||||
|
|
||||
|
public class MauiBlazorContentFileProvider : IMauiBlazorContentFileProvider, ISingletonDependency |
||||
|
{ |
||||
|
private readonly IVirtualFileProvider _virtualFileProvider; |
||||
|
private readonly IFileProvider _fileProvider; |
||||
|
private string _rootPath = "/wwwroot"; |
||||
|
|
||||
|
public MauiBlazorContentFileProvider(IVirtualFileProvider virtualFileProvider) |
||||
|
{ |
||||
|
_virtualFileProvider = virtualFileProvider; |
||||
|
_fileProvider = CreateFileProvider(); |
||||
|
} |
||||
|
|
||||
|
public string ContentRootPath => FileSystem.Current.AppDataDirectory; |
||||
|
|
||||
|
public IFileInfo GetFileInfo(string subpath) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(subpath)) |
||||
|
{ |
||||
|
return new NotFoundFileInfo(subpath); |
||||
|
} |
||||
|
|
||||
|
var fileInfo = _fileProvider.GetFileInfo(subpath); |
||||
|
return fileInfo.Exists ? fileInfo : _fileProvider.GetFileInfo( _rootPath + subpath.EnsureStartsWith('/')); |
||||
|
} |
||||
|
|
||||
|
public IDirectoryContents GetDirectoryContents(string subpath) |
||||
|
{ |
||||
|
if (string.IsNullOrEmpty(subpath)) |
||||
|
{ |
||||
|
return NotFoundDirectoryContents.Singleton; |
||||
|
} |
||||
|
|
||||
|
var directory = _fileProvider.GetDirectoryContents(subpath); |
||||
|
return directory.Exists ? directory : _fileProvider.GetDirectoryContents( _rootPath + subpath.EnsureStartsWith('/')); |
||||
|
} |
||||
|
|
||||
|
public IChangeToken Watch(string filter) |
||||
|
{ |
||||
|
return new CompositeChangeToken( |
||||
|
[ |
||||
|
_fileProvider.Watch(_rootPath + filter), |
||||
|
_fileProvider.Watch(filter) |
||||
|
] |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
protected virtual IFileProvider CreateFileProvider() |
||||
|
{ |
||||
|
var assetsDirectory = Path.Combine(ContentRootPath, _rootPath.TrimStart('/')); |
||||
|
if (!Path.Exists(assetsDirectory)) |
||||
|
{ |
||||
|
Directory.CreateDirectory(assetsDirectory); |
||||
|
} |
||||
|
|
||||
|
return new CompositeFileProvider(new PhysicalFileProvider(assetsDirectory), _virtualFileProvider); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.AspNetCore.Bundling; |
||||
|
using Volo.Abp.AspNetCore.Bundling.Scripts; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
||||
|
using Volo.Abp.Minify.Scripts; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling.Scripts; |
||||
|
|
||||
|
public class ScriptBundler : MauiBlazorBundlerBase, IScriptBundler |
||||
|
{ |
||||
|
public override string FileExtension => "js"; |
||||
|
|
||||
|
public ScriptBundler( |
||||
|
IMauiBlazorContentFileProvider mauiBlazorContentFileProvider, |
||||
|
IJavascriptMinifier minifier, |
||||
|
IOptions<AbpBundlingOptions> bundlingOptions) |
||||
|
: base( |
||||
|
mauiBlazorContentFileProvider, |
||||
|
minifier, |
||||
|
bundlingOptions) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
protected override string ProcessBeforeAddingToTheBundle(IBundlerContext context, string filePath, string fileContent) |
||||
|
{ |
||||
|
return fileContent.EnsureEndsWith(';') + Environment.NewLine; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.AspNetCore.Bundling; |
||||
|
using Volo.Abp.AspNetCore.Bundling.Styles; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
||||
|
using Volo.Abp.Bundling.Styles; |
||||
|
using Volo.Abp.Minify.Styles; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Components.MauiBlazor.Bundling.Styles; |
||||
|
|
||||
|
public class StyleBundler : MauiBlazorBundlerBase, IStyleBundler |
||||
|
{ |
||||
|
private readonly IMauiBlazorContentFileProvider _mauiBlazorContentFileProvider; |
||||
|
public override string FileExtension => "css"; |
||||
|
|
||||
|
public StyleBundler( |
||||
|
IMauiBlazorContentFileProvider mauiBlazorContentFileProvider, |
||||
|
ICssMinifier minifier, |
||||
|
IOptions<AbpBundlingOptions> bundlingOptions) |
||||
|
: base( |
||||
|
mauiBlazorContentFileProvider, |
||||
|
minifier, |
||||
|
bundlingOptions) |
||||
|
{ |
||||
|
_mauiBlazorContentFileProvider = mauiBlazorContentFileProvider; |
||||
|
} |
||||
|
|
||||
|
public string GetAbsolutePath(string relativePath) |
||||
|
{ |
||||
|
return Path.Combine(_mauiBlazorContentFileProvider.ContentRootPath, "wwwroot", relativePath.RemovePreFix("/")).Replace("file://", ""); |
||||
|
} |
||||
|
|
||||
|
protected override string ProcessBeforeAddingToTheBundle(IBundlerContext context, string filePath, string fileContent) |
||||
|
{ |
||||
|
return CssRelativePath.Adjust( |
||||
|
fileContent, |
||||
|
GetAbsolutePath(filePath), |
||||
|
GetAbsolutePath(context.BundleRelativePath) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
|
||||
|
<Project Sdk="Microsoft.NET.Sdk.Razor"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks> |
||||
|
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net9.0-windows10.0.19041.0</TargetFrameworks> |
||||
|
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET --> |
||||
|
<!-- <TargetFrameworks>$(TargetFrameworks);net9.0-tizen</TargetFrameworks> --> |
||||
|
<UseMaui>true</UseMaui> |
||||
|
<SingleProject>true</SingleProject> |
||||
|
<ImplicitUsings>enable</ImplicitUsings> |
||||
|
<Nullable>enable</Nullable> |
||||
|
|
||||
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">15.0</SupportedOSPlatformVersion> |
||||
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">15.0</SupportedOSPlatformVersion> |
||||
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion> |
||||
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion> |
||||
|
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion> |
||||
|
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" /> |
||||
|
<PackageReference Include="Microsoft.Maui.Controls"/> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\Volo.Abp.AspNetCore.Bundling\Volo.Abp.AspNetCore.Bundling.csproj" /> |
||||
|
<ProjectReference Include="..\Volo.Abp.AspNetCore.Components.MauiBlazor\Volo.Abp.AspNetCore.Components.MauiBlazor.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<Folder Include="Platforms\Android\" /> |
||||
|
<Folder Include="Platforms\iOS\" /> |
||||
|
<Folder Include="Platforms\MacCatalyst\" /> |
||||
|
<Folder Include="Platforms\Windows\" /> |
||||
|
</ItemGroup> |
||||
|
</Project> |
||||
@ -0,0 +1,25 @@ |
|||||
|
using Microsoft.AspNetCore.Hosting; |
||||
|
using Microsoft.Extensions.FileProviders; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.AspNetCore.Bundling; |
||||
|
using Volo.Abp.Minify; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
||||
|
|
||||
|
public abstract class MvcUiBundlerBase : BundlerBase |
||||
|
{ |
||||
|
protected IWebHostEnvironment WebHostingEnvironment { get; } |
||||
|
|
||||
|
protected MvcUiBundlerBase( |
||||
|
IWebHostEnvironment webHostingEnvironment, |
||||
|
IMinifier minifier, |
||||
|
IOptions<AbpBundlingOptions> bundlingOptions) : base(minifier, bundlingOptions) |
||||
|
{ |
||||
|
WebHostingEnvironment = webHostingEnvironment; |
||||
|
} |
||||
|
|
||||
|
protected override IFileInfo FindFileInfo(string file) |
||||
|
{ |
||||
|
return WebHostingEnvironment.WebRootFileProvider.GetFileInfo(file); |
||||
|
} |
||||
|
} |
||||
@ -1,6 +0,0 @@ |
|||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.Scripts; |
|
||||
|
|
||||
public interface IScriptBundler : IBundler |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
@ -1,6 +0,0 @@ |
|||||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.Styles; |
|
||||
|
|
||||
public interface IStyleBundler : IBundler |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
Loading…
Reference in new issue