ASP.NET Core Web Optimizer

repository·master·Indexed 21 days ago

https://github.com/ligershark/weboptimizer

Middleware for ASP.NET Core 2.0+ that provides automatic bundling and minification of CSS and JavaScript files at runtime. It features in-memory caching, cache busting, and content inlining via Tag Helpers. The library supports custom transformation pipelines, source maps, and various plugins for TypeScript, Sass, Less, and Markdown.

Tokens
3.3K
Snippets
10
Records
13
Agent score
24%

What's inside WebOptimizer

  1. How WebOptimizer works

    master

    WebOptimizer sets up a transformation pipeline for static files (CSS and JS) that runs at runtime.

    Key characteristics:

    • On-demand generation: No output is generated until the first time a file is requested by a browser.
    • In-memory storage: Transformed files are stored in memory and served quickly; no files are written to disk.
    • Pipeline orchestration: Files can undergo multiple transformations (e.g., minification $\rightarrow$ fingerprinting $\rightarrow$ inlining) before being sent to the browser.
    • High performance: Uses server-side and client-side caching to ensure minimal overhead.
  2. Use cdnUrl to prefix asset references

    master

    If you provide an absolute URL to the cdnUrl option, the Web Optimizer Tag Helpers will automatically prepend this URL to any <script> or <link> tags on your page.

    Example: If cdnUrl is set to http://my-cdn.com, the following HTML:

    <script src="/js/file.js"></script>

    will be rendered as:

    <script src="http://my-cdn.com/js/file.js"></script>
    <script src="/js/file.js"></script>
    <!-- Becomes -->
    <script src="http://my-cdn.com/js/file.js"></script>
  3. Install and setup ASP.NET Core Web Optimizer

    master

    To use WebOptimizer in an ASP.NET Core 2.0+ project, follow these steps:

    1. Install the NuGet package:

      dotnet add package LigerShark.WebOptimizer.Core
    2. Register services in ConfigureServices in Startup.cs:

      public void ConfigureServices(IServiceCollection services)
      {
          services.AddMvc();
          services.AddWebOptimizer();
      }
    3. Add the middleware in Configure in Startup.cs. It must be placed before app.UseStaticFiles():

      public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
      {
          // ... other middleware
          app.UseWebOptimizer();
          app.UseStaticFiles();
          // ...
      }

    Disabling minification in development: If you want to disable minification for JavaScript and CSS during development, use the following overload:

    services.AddWebOptimizer(minifyJavaScript: false, minifyCss: false);
    dotnet add package LigerShark.WebOptimizer.Core
    
    // In Startup.cs
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddWebOptimizer();
    }
    
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseWebOptimizer();
        app.UseStaticFiles();
    }
  4. Use WebOptimizer Tag Helpers

    master

    WebOptimizer provides Tag Helpers for cache busting and inlining content.

    1. Register Tag Helpers Add the following to your _ViewImports.cshtml file:

    @addTagHelper *, WebOptimizer.Core
    @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

    2. Cache Busting Once registered, <script> and <link> tags referencing assets in the pipeline will automatically receive a version string as a URL parameter (e.g., ?v=...). This string changes whenever the source files are modified.

    Important: Tag Helpers only work on files explicitly registered in the pipeline. Use AddFiles to register them:

    services.AddWebOptimizer(pipeline =>
    {
        pipeline.AddFiles("text/javascript", "/dist/*");
        pipeline.AddFiles("text/css", "/css/*");
    });

    3. Inlining Content To inline the content of a file directly into the HTML (useful for above-the-fold CSS), add the inline attribute to the tag:

    <link rel="stylesheet" href="/css/bundle.css" inline />
    <script src="/any/file.js" inline></script>
    @addTagHelper *, WebOptimizer.Core
    @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
  5. Create a custom pipeline using AddBundle

    master

    You can manually compose the WebOptimizer pipeline by chaining methods to define how files are processed. This allows you to create custom bundling and minification workflows for non-standard file extensions or specific content types.

    To create a bundle, use AddBundle(path, contentType, pattern) as the starting point. You can then chain processing steps such as .AdjustRelativePaths(), .Concatenate(), .FingerprintUrls(), and specific minifiers like .MinifyCss() or .MinifyJs().

    services.AddWebOptimizer(pipeline =>
    {
        pipeline.AddBundle("/bundle.css", "text/css; charset=utf-8", "/dir/*.txt")
                .AdjustRelativePaths()
                .Concatenate()
                .FingerprintUrls()
                .MinifyCss();
    });
  6. Configure Web Optimizer options

    master

    You can configure Web Optimizer settings using either appsettings.json or via C# code during service registration.

    Configuration via appsettings.json

    Use the webOptimizer key in your JSON configuration file.

    Configuration via C#

    Pass an Action<WebOptimizerOptions> to the AddWebOptimizer method. This allows you to define your asset pipeline (bundles) and configuration options in a single call.

    Available Options

    OptionTypeDefaultDescription
    enableCachingbooleantrueDetermines if cache-control headers are set and if conditional GET (304) requests are supported.
    enableTagHelperBundlingbooleantrueDetermines if <script> and <link> elements point to the bundled path or individual source files.
    enableMemoryCachebooleantrueEnables/disables IMemoryCache usage.
    enableDiskCachebooleantrueEnables/disables caching pipeline assets to disk.
    cacheDirectorystring<ContentRootPath>/obj/WebOptimizerCacheThe directory where assets are stored if enableDiskCache is true. Must be read/write.
    cdnUrlstringnullAn absolute URL prefix added to script, stylesheet, or media file references via Tag Helpers.
    allowEmptyBundlebooleanfalseIf true, requesting a bundle with no source content returns an empty bundle instead of a 404.
    httpsCompressionHttpsCompressionModeN/ASets the compression mode for HTTPS.
    {
      "webOptimizer": {
        "enableCaching": true,
        "enableMemoryCache": true,
        "enableDiskCache": true,
        "cacheDirectory": "/var/temp/weboptimizercache",
        "enableTagHelperBundling": true,
        "cdnUrl": "https://my-cdn.com/",
        "allowEmptyBundle": false,
        "httpsCompression": "Compress"
      }
    }
    services.AddWebOptimizer(pipeline =>
        {
            pipeline.AddCssBundle("/css/bundle.css", "css/*.css");
            pipeline.AddJavaScriptBundle("/js/bundle.js", "js/plus.js", "js/minus.js");
        },
        option =>
        {
            option.EnableCaching = true;
            option.EnableMemoryCache = true;
            option.EnableDiskCache = true;
            option.CacheDirectory = "/var/temp/weboptimizercache";
            option.EnableTagHelperBundling = true;
            option.CdnUrl = "https://my-cdn.com/";
            option.AllowEmptyBundle = false;
            option.HttpsCompression = HttpsCompressionMode.Compress;
        });
  7. Configure HTTPS Compression with WebOptimizer

    master

    When using services.AddResponseCompression, WebOptimizer's cache-busted assets might be excluded because the Content-Type is changed to text/javascript, which is not in the default allowed list for some ASP.NET Core versions.

    For ASP.NET Core 7.0 or later: Support for text/javascript is included by default. No action is required.

    For versions prior to 7.0: You must manually add text/javascript to the allowed MIME types:

    services.AddResponseCompression(options => {
        options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
            new[] { "text/javascript"}
        );
    });

    Note: app.UseResponseCompression() must be called before app.UseWebOptimizer() in the Configure method.

    services.AddResponseCompression(options => {
        options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
            new[] { "text/javascript"}
        );
    });
  8. Configure bundling for CSS and JavaScript

    master

    Bundling combines multiple source files into a single output file, which is then automatically minified. The output is kept in memory.

    Create a CSS bundle:

    services.AddWebOptimizer(pipeline =>
    {
        pipeline.AddCssBundle("/css/bundle.css", "css/a.css", "css/b.css");
    });

    Create a bundle using globbing patterns:

    services.AddWebOptimizer(pipeline =>
    {
        pipeline.AddCssBundle("/css/bundle.css", "css/**/*.css");
    });

    Usage in HTML: Update your <link> or <script> tags to point to the bundle route:

    <link rel="stylesheet" href="/css/bundle.css" />
    services.AddWebOptimizer(pipeline =>
    {
        pipeline.AddCssBundle("/css/bundle.css", "css/a.css", "css/b.css");
    });
  9. Use AddBundle to create custom bundles

    master

    The AddBundle method is the fundamental building block for creating bundles in WebOptimizer. It is the underlying method used by convenience methods like AddJsBundle and AddCssBundle.

    Signature Pattern: AddBundle(string path, string contentType, params string[] filePatterns)

    • path: The URL path where the resulting bundle will be served.
    • contentType: The MIME type for the output file (e.g., "text/css; charset=utf-8").
    • filePatterns: A list of glob patterns representing the source files to be included in the bundle.
  10. Enable Source Maps for bundles

    master

    By default, bundles do not generate source maps. To enable them, pass a JsSettings object to the AddJavaScriptBundle method:

    services.AddWebOptimizer(pipeline =>
    {
        pipeline.AddJavaScriptBundle("/js/scripts.js",
            new WebOptimizer.Processors.JsSettings { GenerateSourceMap = true },
            "a.js", "b.js");
    });
    pipeline.AddJavaScriptBundle("/js/scripts.js",
        new WebOptimizer.Processors.JsSettings { GenerateSourceMap = true },
        "a.js", "b.js");
  11. Change bundle source root (Web Root vs Content Root)

    master

    By default, source files are relative to the Web Root (wwwroot).

    Use the Content Root: To use files from the project root (e.g., node_modules), use .UseContentRoot():

    services.AddWebOptimizer(pipeline =>
    {
        pipeline.AddCssBundle("/css/bundle.css", "node_modules/jquery/dist/*.js")
                .UseContentRoot();
    });

    Use a custom File Provider: To use a completely custom IFileProvider:

    services.AddWebOptimizer(pipeline =>
    {
        var provider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(@"C:\path\to\my\root\folder");
        pipeline.AddJavaScriptBundle("/js/scripts.js", "a.js", "b.js")
            .UseFileProvider(provider);
    });
    pipeline.AddCssBundle("/css/bundle.css", "node_modules/jquery/dist/*.js").UseContentRoot();
  12. Configure file minification

    master

    You can control which files are automatically minified by interacting with the pipeline in AddWebOptimizer. Paths are relative to the wwwroot folder.

    Minify specific JavaScript files:

    services.AddWebOptimizer(pipeline =>
    {
        pipeline.MinifyJsFiles("js/a.js", "js/b.js", "js/c.js");
    });

    Minify CSS files using globbing patterns:

    services.AddWebOptimizer(pipeline =>
    {
        pipeline.MinifyCssFiles("css/**/*.css");
    });

    Note: WebOptimizer uses NUglify under the hood.

    services.AddWebOptimizer(pipeline =>
    {
        pipeline.MinifyJsFiles("js/a.js", "js/b.js");
        pipeline.MinifyCssFiles("css/**/*.css");
    });