Browse Source

Merge pull request #17894 from abpframework/bundle-cdn

Add External/CDN support for bundles.
pull/18107/head
Halil İbrahim Kalkan 3 years ago
committed by GitHub
parent
commit
b63f5e81dd
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 77
      docs/en/UI/AspNetCore/Bundling-Minification.md
  2. 77
      docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md
  3. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationContext.cs
  4. 13
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationExtensions.cs
  5. 10
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollectionExtensions.cs
  6. 31
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFile.cs
  7. 14
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFileContributor.cs
  8. 2
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleConfigurationContext.cs
  9. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleCacheItem.cs
  10. 50
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleManager.cs
  11. 4
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleManager.cs
  12. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleItemTagHelper.cs
  13. 34
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperResourceService.cs
  14. 14
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs
  15. 16
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs
  16. 6
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/BundleTagHelperFileItem.cs
  17. 3
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Resources/IWebRequestResources.cs
  18. 7
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Resources/WebRequestResources.cs
  19. 22
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs
  20. 8
      framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalStyleContributor.cs

77
docs/en/UI/AspNetCore/Bundling-Minification.md

@ -382,6 +382,83 @@ Configure<AbpBundlingOptions>(options =>
<script defer src="/libs/timeago/locales/jquery.timeago.en.js?_v=637674729040000000"></script>
````
### External/CDN file Support
The bundling system automatically recognizes the external/CDN files and adds them to the page without any change.
#### Using External/CDN files in `AbpBundlingOptions`
````csharp
Configure<AbpBundlingOptions>(options =>
{
options.StyleBundles
.Add("MyStyleBundle", configuration =>
{
configuration
.AddFiles("/styles/my-style1.css")
.AddFiles("/styles/my-style2.css")
.AddFiles("https://cdn.abp.io/bootstrap.css")
.AddFiles("/styles/my-style3.css")
.AddFiles("/styles/my-style4.css");
});
options.ScriptBundles
.Add("MyScriptBundle", configuration =>
{
configuration
.AddFiles("/scripts/my-script1.js")
.AddFiles("/scripts/my-script2.js")
.AddFiles("https://cdn.abp.io/bootstrap.js")
.AddFiles("/scripts/my-script3.js")
.AddFiles("/scripts/my-script4.js");
});
});
````
**Output HTML:**
````html
<link rel="stylesheet" href="/__bundles/MyStyleBundle.EA8C28419DCA43363E9670973D4C0D15.css?_v=638331889644609730" />
<link rel="stylesheet" href="https://cdn.abp.io/bootstrap.css" />
<link rel="stylesheet" href="/__bundles/MyStyleBundle.AC2E0AA6C461A0949A1295E9BDAC049C.css?_v=638331889644623860" />
<script src="/__bundles/MyScriptBundle.C993366DF8840E08228F3EE685CB08E8.js?_v=638331889644937120"></script>
<script src="https://cdn.abp.io/bootstrap.js"></script>
<script src="/__bundles/MyScriptBundle.2E8D0FDC6334D2A6B847393A801525B7.js?_v=638331889644943970"></script>
````
#### Using External/CDN files in Tag Helpers.
````html
<abp-style-bundle name="MyStyleBundle">
<abp-style src="/styles/my-style1.css" />
<abp-style src="/styles/my-style2.css" />
<abp-style src="https://cdn.abp.io/bootstrap.css" />
<abp-style src="/styles/my-style3.css" />
<abp-style src="/styles/my-style4.css" />
</abp-style-bundle>
<abp-script-bundle name="MyScriptBundle">
<abp-script src="/scripts/my-script1.js" />
<abp-script src="/scripts/my-script2.js" />
<abp-script src="https://cdn.abp.io/bootstrap.js" />
<abp-script src="/scripts/my-script3.js" />
<abp-script src="/scripts/my-script4.js" />
</abp-script-bundle>
````
**Output HTML:**
````html
<link rel="stylesheet" href="/__bundles/MyStyleBundle.C60C7B9C1F539659623BB6E7227A7C45.css?_v=638331889645002500" />
<link rel="stylesheet" href="https://cdn.abp.io/bootstrap.css" />
<link rel="stylesheet" href="/__bundles/MyStyleBundle.464328A06039091534650B0E049904C6.css?_v=638331889645012300" />
<script src="/__bundles/MyScriptBundle.55FDCBF2DCB9E0767AE6FA7487594106.js?_v=638331889645050410"></script>
<script src="https://cdn.abp.io/bootstrap.js"></script>
<script src="/__bundles/MyScriptBundle.191CB68AB4F41C8BF3A7AE422F19A3D2.js?_v=638331889645055490"></script>
````
## Themes
Themes uses the standard package contributors to add library resources to page layouts. Themes may also define some standard/global bundles, so any module can contribute to these standard/global bundles. See the [theming documentation](Theming.md) for more.

77
docs/zh-Hans/UI/AspNetCore/Bundling-Minification.md

@ -353,6 +353,83 @@ services.Configure<AbpBundlingOptions>(options =>
});
````
### 外部/CDN文件支持
捆绑系统会自动识别外部/CDN文件,并将其添加到页面中,无需进行任何更改。
#### 在`AbpBundlingOptions`中添加外部/CDN文件
````csharp
Configure<AbpBundlingOptions>(options =>
{
options.StyleBundles
.Add("MyStyleBundle", configuration =>
{
configuration
.AddFiles("/styles/my-style1.css")
.AddFiles("/styles/my-style2.css")
.AddFiles("https://cdn.abp.io/bootstrap.css")
.AddFiles("/styles/my-style3.css")
.AddFiles("/styles/my-style4.css");
});
options.ScriptBundles
.Add("MyScriptBundle", configuration =>
{
configuration
.AddFiles("/scripts/my-script1.js")
.AddFiles("/scripts/my-script2.js")
.AddFiles("https://cdn.abp.io/bootstrap.js")
.AddFiles("/scripts/my-script3.js")
.AddFiles("/scripts/my-script4.js");
});
});
````
**输出HTMl:**
````html
<link rel="stylesheet" href="/__bundles/MyStyleBundle.EA8C28419DCA43363E9670973D4C0D15.css?_v=638331889644609730" />
<link rel="stylesheet" href="https://cdn.abp.io/bootstrap.css" />
<link rel="stylesheet" href="/__bundles/MyStyleBundle.AC2E0AA6C461A0949A1295E9BDAC049C.css?_v=638331889644623860" />
<script src="/__bundles/MyScriptBundle.C993366DF8840E08228F3EE685CB08E8.js?_v=638331889644937120"></script>
<script src="https://cdn.abp.io/bootstrap.js"></script>
<script src="/__bundles/MyScriptBundle.2E8D0FDC6334D2A6B847393A801525B7.js?_v=638331889644943970"></script>
````
#### 在TagHelpers中添加外部/CDN文件
````html
<abp-style-bundle name="MyStyleBundle">
<abp-style src="/styles/my-style1.css" />
<abp-style src="/styles/my-style2.css" />
<abp-style src="https://cdn.abp.io/bootstrap.css" />
<abp-style src="/styles/my-style3.css" />
<abp-style src="/styles/my-style4.css" />
</abp-style-bundle>
<abp-script-bundle name="MyScriptBundle">
<abp-script src="/scripts/my-script1.js" />
<abp-script src="/scripts/my-script2.js" />
<abp-script src="https://cdn.abp.io/bootstrap.js" />
<abp-script src="/scripts/my-script3.js" />
<abp-script src="/scripts/my-script4.js" />
</abp-script-bundle>
````
**输出HTMl:**
````html
<link rel="stylesheet" href="/__bundles/MyStyleBundle.C60C7B9C1F539659623BB6E7227A7C45.css?_v=638331889645002500" />
<link rel="stylesheet" href="https://cdn.abp.io/bootstrap.css" />
<link rel="stylesheet" href="/__bundles/MyStyleBundle.464328A06039091534650B0E049904C6.css?_v=638331889645012300" />
<script src="/__bundles/MyScriptBundle.55FDCBF2DCB9E0767AE6FA7487594106.js?_v=638331889645050410"></script>
<script src="https://cdn.abp.io/bootstrap.js"></script>
<script src="/__bundles/MyScriptBundle.191CB68AB4F41C8BF3A7AE422F19A3D2.js?_v=638331889645055490"></script>
````
### 主题
主题使用标准包贡献者将库资源添加到页面布局. 主题还可以定义一些标准/全局包, 因此任何模块都可以为这些标准/全局包做出贡献. 有关更多信息, 请参阅[主题文档](Theming.md).

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationContext.cs

@ -8,7 +8,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling;
public class BundleConfigurationContext : IBundleConfigurationContext
{
public List<string> Files { get; }
public List<BundleFile> Files { get; }
public IFileProvider FileProvider { get; }
@ -18,7 +18,7 @@ public class BundleConfigurationContext : IBundleConfigurationContext
public BundleConfigurationContext(IServiceProvider serviceProvider, IFileProvider fileProvider)
{
Files = new List<string>();
Files = new List<BundleFile>();
ServiceProvider = serviceProvider;
LazyServiceProvider = ServiceProvider.GetRequiredService<IAbpLazyServiceProvider>();
FileProvider = fileProvider;

13
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleConfigurationExtensions.cs

@ -1,4 +1,5 @@
using System;
using System.Linq;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling;
@ -10,6 +11,18 @@ public static class BundleConfigurationExtensions
return bundleConfiguration;
}
public static BundleConfiguration AddFiles(this BundleConfiguration bundleConfiguration, params BundleFile[] files)
{
bundleConfiguration.Contributors.AddFiles(files);
return bundleConfiguration;
}
public static BundleConfiguration AddExternalFiles(this BundleConfiguration bundleConfiguration, params string[] files)
{
bundleConfiguration.Contributors.AddExternalFiles(files.Select(x => new BundleFile(x, true)).ToArray());
return bundleConfiguration;
}
public static BundleConfiguration AddContributors(this BundleConfiguration bundleConfiguration, params IBundleContributor[] contributors)
{
Check.NotNull(contributors, nameof(contributors));

10
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleContributorCollectionExtensions.cs

@ -6,4 +6,14 @@ public static class BundleContributorCollectionExtensions
{
contributors.Add(new BundleFileContributor(files));
}
public static void AddFiles(this BundleContributorCollection contributors, params BundleFile[] files)
{
contributors.Add(new BundleFileContributor(files));
}
public static void AddExternalFiles(this BundleContributorCollection contributors, params BundleFile[] files)
{
contributors.Add(new BundleFileContributor(files));
}
}

31
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFile.cs

@ -0,0 +1,31 @@
using System;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling;
public class BundleFile
{
public string FileName { get; set; }
public bool IsExternalFile { get; set; }
public BundleFile(string fileName)
{
FileName = fileName;
IsExternalFile = fileName.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
fileName.StartsWith("https://", StringComparison.OrdinalIgnoreCase);
}
public BundleFile(string fileName, bool isExternalFile)
{
FileName = fileName;
IsExternalFile = isExternalFile;
}
/// <summary>
/// This method is used to compatible with old code.
/// </summary>
public static implicit operator BundleFile(string fileName)
{
return new BundleFile(fileName);
}
}

14
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleFileContributor.cs

@ -1,22 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling;
public class BundleFileContributor : BundleContributor
{
public string[] Files { get; }
public List<BundleFile> Files { get; }
public BundleFileContributor(params BundleFile[] files)
{
Files = new List<BundleFile>();
Files.AddRange(files);
}
public BundleFileContributor(params string[] files)
{
Files = files ?? Array.Empty<string>();
Files = new List<BundleFile>();
Files.AddRange(files.Select(file => new BundleFile(file)));
}
public override void ConfigureBundle(BundleConfigurationContext context)
{
foreach (var file in Files)
{
context.Files.AddIfNotContains(x => x.Equals(file, StringComparison.OrdinalIgnoreCase), () => file);
context.Files.AddIfNotContains(x => x.FileName.Equals(file.FileName, StringComparison.OrdinalIgnoreCase), () => file);
}
}
}

2
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling.Abstractions/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleConfigurationContext.cs

@ -5,5 +5,5 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling;
public interface IBundleConfigurationContext : IServiceProviderAccessor
{
List<string> Files { get; }
List<BundleFile> Files { get; }
}

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleCacheItem.cs

@ -5,11 +5,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling;
public class BundleCacheItem
{
public List<string> Files { get; }
public List<BundleFile> Files { get; }
public List<IDisposable> WatchDisposeHandles { get; }
public BundleCacheItem(List<string> files)
public BundleCacheItem(List<BundleFile> files)
{
Files = files;
WatchDisposeHandles = new List<IDisposable>();

50
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundleManager.cs

@ -12,7 +12,6 @@ using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling.Scripts;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling.Styles;
using Volo.Abp.AspNetCore.Mvc.UI.Resources;
using Volo.Abp.AspNetCore.VirtualFileSystem;
using Volo.Abp.DependencyInjection;
using Volo.Abp.VirtualFileSystem;
@ -56,18 +55,20 @@ public class BundleManager : IBundleManager, ITransientDependency
Logger = NullLogger<BundleManager>.Instance;
}
public virtual async Task<IReadOnlyList<string>> GetStyleBundleFilesAsync(string bundleName)
public virtual async Task<IReadOnlyList<BundleFile>> GetStyleBundleFilesAsync(string bundleName)
{
return await GetBundleFilesAsync(Options.StyleBundles, bundleName, StyleBundler);
}
public virtual async Task<IReadOnlyList<string>> GetScriptBundleFilesAsync(string bundleName)
public virtual async Task<IReadOnlyList<BundleFile>> GetScriptBundleFilesAsync(string bundleName)
{
return await GetBundleFilesAsync(Options.ScriptBundles, bundleName, ScriptBundler);
}
protected virtual async Task<IReadOnlyList<string>> GetBundleFilesAsync(BundleConfigurationCollection bundles, string bundleName, IBundler bundler)
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 = RequestResources.TryAdd(await GetBundleFilesAsync(contributors));
var dynamicResources = RequestResources.TryAdd(await GetDynamicResourcesAsync(contributors));
@ -77,16 +78,45 @@ public class BundleManager : IBundleManager, ITransientDependency
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;
var cacheItem = BundleCache.GetOrAdd(bundleRelativePath, () =>
return BundleCache.GetOrAdd(bundleRelativePath, () =>
{
var cacheValue = new BundleCacheItem(
new List<string>
new List<BundleFile>
{
"/" + bundleRelativePath
new BundleFile("/" + bundleRelativePath)
}
);
@ -104,8 +134,6 @@ public class BundleManager : IBundleManager, ITransientDependency
return cacheValue;
});
return cacheItem.Files.Union(dynamicResources).ToImmutableList();
}
private void WatchChanges(BundleCacheItem cacheValue, List<string> files, string bundleRelativePath)
@ -176,7 +204,7 @@ public class BundleManager : IBundleManager, ITransientDependency
}
}
protected async Task<List<string>> GetBundleFilesAsync(List<IBundleContributor> contributors)
protected async Task<List<BundleFile>> GetBundleFilesAsync(List<IBundleContributor> contributors)
{
var context = CreateBundleConfigurationContext();
@ -198,7 +226,7 @@ public class BundleManager : IBundleManager, ITransientDependency
return context.Files;
}
protected virtual async Task<List<string>> GetDynamicResourcesAsync(List<IBundleContributor> contributors)
protected virtual async Task<List<BundleFile>> GetDynamicResourcesAsync(List<IBundleContributor> contributors)
{
var context = CreateBundleConfigurationContext();

4
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/IBundleManager.cs

@ -5,7 +5,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling;
public interface IBundleManager
{
Task<IReadOnlyList<string>> GetStyleBundleFilesAsync(string bundleName);
Task<IReadOnlyList<BundleFile>> GetStyleBundleFilesAsync(string bundleName);
Task<IReadOnlyList<string>> GetScriptBundleFilesAsync(string bundleName);
Task<IReadOnlyList<BundleFile>> GetScriptBundleFilesAsync(string bundleName);
}

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpBundleItemTagHelper.cs

@ -1,4 +1,5 @@
using System;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers;
namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers;
@ -49,7 +50,7 @@ public abstract class AbpBundleItemTagHelper<TTagHelper, TTagHelperService> : Ab
if (Src != null)
{
return new BundleTagHelperFileItem(Src);
return new BundleTagHelperFileItem(new BundleFile(Src));
}
throw new AbpException("abp-script tag helper requires to set either src or type!");

34
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperResourceService.cs

@ -3,10 +3,12 @@ using Microsoft.AspNetCore.Razor.TagHelpers;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@ -63,18 +65,24 @@ public abstract class AbpTagHelperResourceService : ITransientDependency
foreach (var bundleFile in bundleFiles)
{
var file = HostingEnvironment.WebRootFileProvider.GetFileInfo(bundleFile);
if (file == null || !file.Exists)
if (bundleFile.IsExternalFile)
{
Logger.LogError($"Could not find the bundle file '{bundleFile}' for the bundle '{bundleName}'!");
AddErrorScript(viewContext, tagHelper, context, output, bundleFile, bundleName!);
continue;
AddHtmlTag(viewContext, tagHelper, context, output, bundleFile, null);
}
if (file.Length > 0)
else
{
AddHtmlTag(viewContext, tagHelper, context, output, bundleFile + "?_v=" + file.LastModified.UtcTicks);
var file = HostingEnvironment.WebRootFileProvider.GetFileInfo(bundleFile.FileName);
if (file == null || !file.Exists)
{
Logger.LogError($"Could not find the bundle file '{bundleFile.FileName}' for the bundle '{bundleName}'!");
AddErrorScript(viewContext, tagHelper, context, output, bundleFile, bundleName!);
continue;
}
if (file.Length > 0)
{
AddHtmlTag(viewContext, tagHelper, context, output, bundleFile, file);
}
}
}
@ -84,13 +92,13 @@ public abstract class AbpTagHelperResourceService : ITransientDependency
protected abstract void CreateBundle(string bundleName, List<BundleTagHelperItem> bundleItems);
protected abstract Task<IReadOnlyList<string>> GetBundleFilesAsync(string bundleName);
protected abstract Task<IReadOnlyList<BundleFile>> GetBundleFilesAsync(string bundleName);
protected abstract void AddHtmlTag(ViewContext viewContext, TagHelper tagHelper, TagHelperContext context, TagHelperOutput output, string file);
protected abstract void AddHtmlTag(ViewContext viewContext, TagHelper tagHelper, TagHelperContext context, TagHelperOutput output, BundleFile file, IFileInfo? fileInfo = null);
protected virtual void AddErrorScript(ViewContext viewContext, TagHelper tagHelper, TagHelperContext context, TagHelperOutput output, string file, string bundleName)
protected virtual void AddErrorScript(ViewContext viewContext, TagHelper tagHelper, TagHelperContext context, TagHelperOutput output, BundleFile file, string bundleName)
{
output.Content.AppendHtml($"<script>console.log(\"%cCould not find the bundle file '{file}' for the bundle '{bundleName}'!\", 'background: yellow; font-size:20px;');</script>{Environment.NewLine}");
output.Content.AppendHtml($"<script>console.log(\"%cCould not find the bundle file '{file.FileName}' for the bundle '{bundleName}'!\", 'background: yellow; font-size:20px;');</script>{Environment.NewLine}");
}
protected virtual string GenerateBundleName(List<BundleTagHelperItem> bundleItems)

14
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperScriptService.cs

@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
@ -32,12 +33,12 @@ public class AbpTagHelperScriptService : AbpTagHelperResourceService
);
}
protected override async Task<IReadOnlyList<string>> GetBundleFilesAsync(string bundleName)
protected override async Task<IReadOnlyList<BundleFile>> GetBundleFilesAsync(string bundleName)
{
return await BundleManager.GetScriptBundleFilesAsync(bundleName);
}
protected override void AddHtmlTag(ViewContext viewContext, TagHelper tagHelper, TagHelperContext context, TagHelperOutput output, string file)
protected override void AddHtmlTag(ViewContext viewContext, TagHelper tagHelper, TagHelperContext context, TagHelperOutput output, BundleFile file, IFileInfo? fileInfo = null)
{
var defer = tagHelper switch
{
@ -46,12 +47,13 @@ public class AbpTagHelperScriptService : AbpTagHelperResourceService
_ => false
};
var deferText = (defer || Options.DeferScriptsByDefault || Options.DeferScripts.Any(x => file.StartsWith(x, StringComparison.OrdinalIgnoreCase)))
? "defer"
var deferText = (defer || Options.DeferScriptsByDefault || Options.DeferScripts.Any(x => file.FileName.StartsWith(x, StringComparison.OrdinalIgnoreCase)))
? "defer "
: string.Empty;
var nonceText = (viewContext.HttpContext.Items.TryGetValue(AbpAspNetCoreConsts.ScriptNonceKey, out var nonce) && nonce is string nonceString && !string.IsNullOrEmpty(nonceString))
? $"nonce=\"{nonceString}\""
? $"nonce=\"{nonceString}\" "
: string.Empty;
output.Content.AppendHtml($"<script {deferText} {nonceText} src=\"{viewContext.GetUrlHelper().Content(file.EnsureStartsWith('~'))}\"></script>{Environment.NewLine}");
var src = file.IsExternalFile ? file.FileName : viewContext.GetUrlHelper().Content((file.FileName + "?_v=" + fileInfo!.LastModified.UtcTicks).EnsureStartsWith('~'));
output.Content.AppendHtml($"<script {deferText}{nonceText}src=\"{src}\"></script>{Environment.NewLine}");
}
}

16
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/AbpTagHelperStyleService.cs

@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Volo.Abp.AspNetCore.Security;
@ -18,7 +19,7 @@ public class AbpTagHelperStyleService : AbpTagHelperResourceService
public AbpTagHelperStyleService(
IBundleManager bundleManager,
IOptions<AbpBundlingOptions> options,
IWebHostEnvironment hostingEnvironment,
IWebHostEnvironment hostingEnvironment,
IOptions<AbpSecurityHeadersOptions> securityHeadersOptions) : base(
bundleManager,
options,
@ -36,12 +37,12 @@ public class AbpTagHelperStyleService : AbpTagHelperResourceService
);
}
protected override async Task<IReadOnlyList<string>> GetBundleFilesAsync(string bundleName)
protected override async Task<IReadOnlyList<BundleFile>> GetBundleFilesAsync(string bundleName)
{
return await BundleManager.GetStyleBundleFilesAsync(bundleName);
}
protected override void AddHtmlTag(ViewContext viewContext, TagHelper tagHelper, TagHelperContext context, TagHelperOutput output, string file)
protected override void AddHtmlTag(ViewContext viewContext, TagHelper tagHelper, TagHelperContext context, TagHelperOutput output, BundleFile file, IFileInfo? fileInfo = null)
{
var preload = tagHelper switch
{
@ -50,15 +51,16 @@ public class AbpTagHelperStyleService : AbpTagHelperResourceService
_ => false
};
if (preload || Options.PreloadStylesByDefault || Options.PreloadStyles.Any(x => file.StartsWith(x, StringComparison.OrdinalIgnoreCase)))
var href = file.IsExternalFile ? file.FileName : viewContext.GetUrlHelper().Content((file.FileName + "?_v=" + fileInfo!.LastModified.UtcTicks).EnsureStartsWith('~'));
if (preload || Options.PreloadStylesByDefault || Options.PreloadStyles.Any(x => file.FileName.StartsWith(x, StringComparison.OrdinalIgnoreCase)))
{
output.Content.AppendHtml(SecurityHeadersOptions.UseContentSecurityPolicyScriptNonce
? $"<link rel=\"preload\" href=\"{viewContext.GetUrlHelper().Content(file.EnsureStartsWith('~'))}\" as=\"style\" abp-csp-style />{Environment.NewLine}"
: $"<link rel=\"preload\" href=\"{viewContext.GetUrlHelper().Content(file.EnsureStartsWith('~'))}\" as=\"style\" onload=\"this.rel='stylesheet'\" />{Environment.NewLine}");
? $"<link rel=\"preload\" href=\"{href}\" as=\"style\" abp-csp-style />{Environment.NewLine}"
: $"<link rel=\"preload\" href=\"{href}\" as=\"style\" onload=\"this.rel='stylesheet'\" />{Environment.NewLine}");
}
else
{
output.Content.AppendHtml($"<link rel=\"stylesheet\" href=\"{viewContext.GetUrlHelper().Content(file.EnsureStartsWith('~'))}\" />{Environment.NewLine}");
output.Content.AppendHtml($"<link rel=\"stylesheet\" href=\"{href}\" />{Environment.NewLine}");
}
}
}

6
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/TagHelpers/BundleTagHelperFileItem.cs

@ -5,16 +5,16 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling.TagHelpers;
public class BundleTagHelperFileItem : BundleTagHelperItem
{
[NotNull]
public string File { get; }
public BundleFile File { get; }
public BundleTagHelperFileItem([NotNull] string file)
public BundleTagHelperFileItem([NotNull] BundleFile file)
{
File = Check.NotNull(file, nameof(file));
}
public override string ToString()
{
return File;
return File.FileName;
}
public override void AddToConfiguration(BundleConfiguration configuration)

3
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Resources/IWebRequestResources.cs

@ -1,8 +1,9 @@
using System.Collections.Generic;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
namespace Volo.Abp.AspNetCore.Mvc.UI.Resources;
public interface IWebRequestResources
{
List<string> TryAdd(List<string> resources);
List<BundleFile> TryAdd(List<BundleFile> resources);
}

7
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Resources/WebRequestResources.cs

@ -1,23 +1,24 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Volo.Abp.AspNetCore.Mvc.UI.Bundling;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.AspNetCore.Mvc.UI.Resources;
public class WebRequestResources : IWebRequestResources, IScopedDependency
{
protected Dictionary<string, List<string>> Resources { get; }
protected Dictionary<string, List<BundleFile>> Resources { get; }
protected IHttpContextAccessor HttpContextAccessor { get; }
public WebRequestResources(IHttpContextAccessor httpContextAccessor)
{
HttpContextAccessor = httpContextAccessor;
Resources = new Dictionary<string, List<string>>();
Resources = new Dictionary<string, List<BundleFile>>();
}
public List<string> TryAdd(List<string> resources)
public List<BundleFile> TryAdd(List<BundleFile> resources)
{
var path = HttpContextAccessor.HttpContext?.Request?.Path ?? "";

22
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalScriptContributor.cs

@ -35,20 +35,20 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Bundling;
)]
public class SharedThemeGlobalScriptContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddRange(new[]
context.Files.AddRange(new BundleFile[]
{
"/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery-form/jquery-form-extensions.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/widget-manager.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert2/abp-sweetalert2.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js"
"/libs/abp/aspnetcore-mvc-ui-theme-shared/ui-extensions.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/jquery-extensions.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery-form/jquery-form-extensions.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/jquery/widget-manager.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/dom-event-handlers.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/bootstrap/modal-manager.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-extensions.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/sweetalert2/abp-sweetalert2.js",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/toastr/abp-toastr.js"
});
}
}

8
framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared/Bundling/SharedThemeGlobalStyleContributor.cs

@ -27,10 +27,10 @@ public class SharedThemeGlobalStyleContributor : BundleContributor
{
public override void ConfigureBundle(BundleConfigurationContext context)
{
context.Files.AddRange(new[]
context.Files.AddRange(new BundleFile[]
{
"/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.css",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/date-range-picker/date-range-picker-styles.css"
});
"/libs/abp/aspnetcore-mvc-ui-theme-shared/datatables/datatables-styles.css",
"/libs/abp/aspnetcore-mvc-ui-theme-shared/date-range-picker/date-range-picker-styles.css"
});
}
}

Loading…
Cancel
Save