mirror of https://github.com/abpframework/abp.git
committed by
GitHub
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.Collections.Concurrent; |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
namespace Volo.Abp.AspNetCore.Bundling; |
|||
|
|||
public class BundleCache : IBundleCache, ISingletonDependency |
|||
{ |
|||
@ -1,7 +1,8 @@ |
|||
using System; |
|||
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 |
|||
{ |
|||
@ -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 |
|||
{ |
|||
@ -1,6 +1,6 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
namespace Volo.Abp.AspNetCore.Bundling; |
|||
|
|||
public class BundlerContext : IBundlerContext |
|||
{ |
|||
@ -1,6 +1,6 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
namespace Volo.Abp.AspNetCore.Bundling; |
|||
|
|||
public interface IBundleCache |
|||
{ |
|||
@ -1,7 +1,8 @@ |
|||
using System.Collections.Generic; |
|||
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 |
|||
{ |
|||
@ -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 |
|||
{ |
|||
@ -1,6 +1,6 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
namespace Volo.Abp.AspNetCore.Bundling; |
|||
|
|||
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