{"id":26514728,"url":"https://github.com/ligershark/weboptimizer","last_synced_at":"2025-05-13T19:13:55.609Z","repository":{"id":38310912,"uuid":"98229312","full_name":"ligershark/WebOptimizer","owner":"ligershark","description":"A bundler and minifier for ASP.NET Core","archived":false,"fork":false,"pushed_at":"2025-04-17T16:48:55.000Z","size":1649,"stargazers_count":811,"open_issues_count":145,"forks_count":119,"subscribers_count":32,"default_branch":"master","last_synced_at":"2025-04-27T20:02:10.784Z","etag":null,"topics":["aspnetcore","taghelper","web-optimization"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ligershark.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-07-24T19:55:06.000Z","updated_at":"2025-04-27T09:25:34.000Z","dependencies_parsed_at":"2023-02-09T03:46:29.202Z","dependency_job_id":"c2160e6d-cb7b-4e42-ab72-9df2e1dc81c7","html_url":"https://github.com/ligershark/WebOptimizer","commit_stats":{"total_commits":360,"total_committers":48,"mean_commits":7.5,"dds":0.3194444444444444,"last_synced_commit":"b74754604632d1a2f59367e088e179fc602227f4"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ligershark%2FWebOptimizer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ligershark%2FWebOptimizer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ligershark%2FWebOptimizer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ligershark%2FWebOptimizer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ligershark","download_url":"https://codeload.github.com/ligershark/WebOptimizer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254010813,"owners_count":21998995,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aspnetcore","taghelper","web-optimization"],"created_at":"2025-03-21T05:29:12.978Z","updated_at":"2025-05-13T19:13:55.586Z","avatar_url":"https://github.com/ligershark.png","language":"C#","readme":"# ASP.NET Core Web Optimizer\n\n[![Build status](https://ci.appveyor.com/api/projects/status/twj2lkgnm4th6qh9?svg=true)](https://ci.appveyor.com/project/madskristensen/weboptimizer)\n[![NuGet](https://img.shields.io/nuget/v/LigerShark.WebOptimizer.Core.svg)](https://nuget.org/packages/LigerShark.WebOptimizer.Core/)\n \nASP.NET Core middleware for bundling and minification of CSS and JavaScript files at runtime. With full server-side and client-side caching to ensure high performance. No complicated build process and no hassle.\n\nCheck out the **[demo website](https://weboptimizer.azurewebsites.net/)** or its **[source code](https://github.com/ligershark/WebOptimizer/tree/master/sample)**.\n\n## Versions\nMaster is being updated for ```ASP.NET Core 3.0```\nFor ```ASP.NET Core 2.x```, use the **[2.0 branch.](https://github.com/ligershark/WebOptimizer/tree/2.0)**\n## Content\n\n- [How it works](#how-it-works)\n- [Install and setup](#install-and-setup)\n- [Minification](#minification)\n- [Bundling](#bundling)\n- [Tag Helpers](#tag-helpers)\n  - [Cache busting](#cache-busting)\n  - [Inlining content](#inlining-content)\n- [Compiling Scss](#compiling-scss)\n- [Options](#options)\n- [Custom pipeline](#custom-pipeline) \n- [Extend](#extend) \n- [Plugins](#plugins)\n\n## How it works\nWebOptimizer sets up a pipeline for static files so they can be transformed (minified, bundled, etc.) before sent to the browser. This pipeline is highly flexible and can be used to combine many different transformations to the same files. \n\nFor instance, the pipeline for a single .css file could be orchestrated to first goes through minification, then through fingerprinting and finally through image inlining before being sent to the browser. \n\nWebOptimizer makes sure that the pipeline is orchestrated for maximum performance and compatability out of the box, yet at the same time allows for full customization and extensibility. \n\nThe pipeline is set up when the ASP.NET web application starts, but no output is being generated until the first time they are requested by the browser. The output is then being stored in memory and served very fast on all subsequent requests. This also means that no output files are being generated on disk.\n\n## Install and setup\nAdd the NuGet package [LigerShark.WebOptimizer.Core](https://nuget.org/packages/LigerShark.WebOptimizer.Core/) to any ASP.NET Core 2.0 project.\n\n```cmd\ndotnet add package LigerShark.WebOptimizer.Core \n```\n\nThen in **Startup.cs**, add `app.UseWebOptimizer()` to the `Configure` method anywhere before `app.UseStaticFiles` (if present), like so:\n\n```csharp\npublic void Configure(IApplicationBuilder app, IWebHostEnvironment env)\n{\n    if (env.IsDevelopment())\n    {\n        app.UseDeveloperExceptionPage();\n    }\n\n    app.UseWebOptimizer();\n\n    app.UseStaticFiles();\n    app.UseMvc(routes =\u003e\n    {\n        routes.MapRoute(\n            name: \"default\",\n            template: \"{controller=Home}/{action=Index}/{id?}\");\n    });\n}\n```\n\nThat sets up the middleware that handles the requests and transformation of the files. \n\nAnd finally modify the *ConfigureServices* method by adding a call to `services.AddWebOptimizer()`:\n\n```csharp\npublic void ConfigureServices(IServiceCollection services)\n{\n    services.AddMvc();\n    services.AddWebOptimizer();\n}\n```\n\nThe service contains all the configuration used by the middleware and allows your app to interact with it as well.\n\nThat's it. You have now enabled automatic CSS and JavaScript minification. No other code changes are needed for enabling this. \n\nTry it by requesting one of your .css or .js files in the browser and see if it has been minified.\n\n**Disabling minification:**  \nIf you want to disable minification (e.g. in development), the following overload for AddWebOptimizer() can be used:\n```\nif (env.IsDevelopment())\n{\n    services.AddWebOptimizer(minifyJavaScript:false,minifyCss:false);\n}\n```\n\n## Minification\nTo control the minification in more detail, we must interact with the pipeline that manipulates the file content. \n\n\u003e Minification is the process of removing all unnecessary characters from source code without changing its functionality in order to make it as small as possible.\n\nFor example, perhaps we only want a few certain JavaScript files to be minified automatically. Then we would write something like this:\n\n```csharp\npublic void ConfigureServices(IServiceCollection services)\n{\n    services.AddMvc();\n    services.AddWebOptimizer(pipeline =\u003e\n    {\n        pipeline.MinifyJsFiles(\"js/a.js\", \"js/b.js\", \"js/c.js\");\n    });\n}\n```\n\nNotice that the paths to the .js files are relative to the wwwroot folder.\n\nWe can do the same for CSS, but this time we're using a globbing pattern to allow minification for all .css files in a particular folder and its sub folders:\n\n```csharp\npipeline.MinifyCssFiles(\"css/**/*.css\");\n```\n\nWhen using globbing patterns, you still request the .css files on their relative path such as `http://localhost:1234/css/site.css`.\n\nSetting up automatic minification like this doesn't require any other code changes in your web application to work.\n\n\u003e Under the hood, WebOptimizer uses [NUglify](https://github.com/xoofx/NUglify) to minify JavaScript and CSS.\n\n## Bundling\nTo bundle multiple source file into a single output file couldn't be easier.\n\n\u003e Bundling is the process of taking multiple source files and combining them into a single output file. All CSS and JavaScript bundles are also being automatically minified.\n\nLet's imagine we wanted to bundle `/css/a.css` and `/css/b.css` into a single output file and we want that output file to be located at `http://localhost/css/bundle.css`.\n\nThen we would call the `AddCssBundle` method:\n\n```csharp\nservices.AddWebOptimizer(pipeline =\u003e\n{\n    pipeline.AddCssBundle(\"/css/bundle.css\", \"css/a.css\", \"css/b.css\");\n});\n```\n\nThe `AddCssBundle` method will combine the two source files in the order they are listed and then minify the resulting output file. The output file `/css/bundle.css` is created and kept in memory and not as a file on disk.\n\nTo bundle all files from a particular folder, we can use globbing patterns like this:\n\n```csharp\nservices.AddWebOptimizer(pipeline =\u003e\n{\n    pipeline.AddCssBundle(\"/css/bundle.css\", \"css/**/*.css\");\n});\n```\n\nWhen using bundling, we have to update our `\u003cscript\u003e` and `\u003clink\u003e` tags to point to the bundle route. It could look like this:\n\n```html\n\u003clink rel=\"stylesheet\" href=\"/css/bundle.css\" /\u003e\n```\n\n### Content Root vs. Web Root\nBy default, all bundle source files are relative to the Web Root (*wwwroot*) folder, but you can change it to be relative to the Content Root instead.\n\n\u003e The Content Root folder is usually the project root directory, which is the parent directory of *wwwroot*.\n\nAs an example, lets create a bundle of files found in a folder called node_modules that exist in the Content Root:\n\n```csharp\nservices.AddWebOptimizer(pipeline =\u003e\n{\n    pipeline.AddCssBundle(\"/css/bundle.css\", \"node_modules/jquery/dist/*.js\")\n            .UseContentRoot();\n});\n```\n\n\nThe `UseContentRoot()` method makes the bundle look for source files in the Content Root rather than in the Web Root.\n\nTo use a completely custom `IFileProvider`, you can use the `UseFileProvider` pipeline method.\n\n```csharp\nservices.AddWebOptimizer(pipeline =\u003e\n{\n    var provider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider(@\"C:\\path\\to\\my\\root\\folder\");\n    pipeline.AddJavaScriptBundle(\"/js/scripts.js\", \"a.js\", \"b.js\")\n        .UseFileProvider(provider);\n});\n```\n\n## Tag Helpers\nWebOptimizer ships with a few [Tag Helpers](https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro) that helps with a few important tasks.\n\n\u003e Tag Helpers enable server-side code to participate in creating and rendering HTML elements in Razor files.\n\nFirst, we need to register the TagHelpers defined in *LigerShark.WebOptimizer.Core* in our project.\n\nTo do that, go to `_ViewImports.cshtml` and register the Tag Helpers by adding `@addTagHelper *, WebOptimizer.Core` to the file.\n\n```text\n@addTagHelper *, WebOptimizer.Core\n@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers\n```\n### Cache busting\nAs soon as the Tag Helpers are registered in your project, you'll notice how the `\u003cscript\u003e` and `\u003clink\u003e` tags starts to render a little differently when they are referencing a file or bundle.\n\n**NOTE:** Unlike other ASP.NET Core Tag Helpers, `\u003cscript\u003e` and `\u003clink\u003e` tags don't need an `asp-` attribute to be rendered as a Tag Helper. \n\nThey will get a version string added as a URL parameter:\n\n```html\n\u003clink rel=\"stylesheet\" href=\"/css/bundle.css?v=OFNUnL-rtjZYOQwGomkVMwO415EOHtJ_Tu_s0SIlm9s\" /\u003e\n```\n\nThis version string changes every time one or more of the source files are modified. \n\n**NOTE:** TagHelpers will only work on files registered as assets on the pipeline (will not work for all files in your \u003csctipt\u003e and \u003clink\u003e tags out of the blue). make sure to add all required files as assets (glob is supported to add wildcard paths).\n\n```csharp\nservices.AddWebOptimizer(pipeline =\u003e\n{\n    pipeline.AddFiles(\"text/javascript\", \"/dist/*\");\n    pipeline.AddFiles(\"text/css\", \"/css/*\");\n});\n``` \n\nThis technique is called *cache busting* and is a critical component to achieving high performance, since we cannot utilize browser caching of the CSS and JavaScript files without it. That is also why it can not be disabled when using WebOptimizer.\n\n#### HTTPS Compression Considerations\nWhen utilized with the [`services.AddResponseCompression`](https://docs.microsoft.com/en-us/aspnet/core/performance/response-compression?view=aspnetcore-6.0) middleware included in the [Feature](https://github.com/ligershark/WebOptimizer/pull/147), it's important to note that by default the *cache busted* assets may not be included as the *Response Header: content-type* is modified from the default **application/javascript** to **text/javascript**, which is not a supported default [ResponseCompressionDefaults.MimeTypes](https://github.com/aspnet/BasicMiddleware/blob/master/src/Microsoft.AspNetCore.ResponseCompression/ResponseCompressionDefaults.cs) before ASP.NET Core 7.0 and can be added in the *Startup.ConfigureServices* like so:\n```csharp\nservices.AddResponseCompression( options =\u003e {\n    options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(\n        new[] { \"text/javascript\"}\n    );\n});\n```\n*Note: `app.UseResponseCompression()` should be used prior to `app.UseWebOptimizer()`*\n\n*Note: If you're using [ASP.NET Core 7.0 or later](https://github.com/dotnet/aspnetcore/blob/v7.0.0-preview.4.22251.1/src/Middleware/ResponseCompression/src/ResponseCompressionDefaults.cs), you don't need to add this, as support for `text/javascript` is included by default.*\n### Inlining content\nWe can also use Web Optimizer to inline the content of the files directly into the Razor page. This is useful for creating high-performance websites that inlines the above-the-fold CSS and lazy loads the rest later.\n\nTo do this, simply add the attribute `inline` to any `\u003clink\u003e` or `\u003cscript\u003e` element like so:\n\n```html\n\u003clink rel=\"stylesheet\" href=\"/css/bundle.css\" inline /\u003e\n\u003cscript src=\"/any/file.js\" inline\u003e\u003c/script\u003e\n```\n\nThere is a Tag Helper that understands what the `inline` attribute means and handles the inlining automatically.\n\n## Compiling Scss\nWebOptimizer can also compile [Scss](http://sass-lang.com/) files into CSS. For that you need to install the `LigerShark.WebOptimizer.Sass` NuGet package and hooking it up is a breeze. Read more on the [WebOptimizer.Sass](https://github.com/ligershark/WebOptimizer.sass) website.\n\n## Options\nYou can control the options from the appsettings.json file or in code \n\n```json\n{\n  \"webOptimizer\": {\n    \"enableCaching\": true,\n    \"enableMemoryCache\": true,\n    \"enableDiskCache\": true,\n    \"cacheDirectory\": \"/var/temp/weboptimizercache\",\n    \"enableTagHelperBundling\": true,\n    \"cdnUrl\": \"https://my-cdn.com/\",\n    \"allowEmptyBundle\": false\n  }\n}\n```\n\n```csharp\nservices.AddWebOptimizer(pipeline =\u003e\n    {\n        pipeline.AddCssBundle(\"/css/bundle.css\", \"css/*.css\");\n        pipeline.AddJavaScriptBundle(\"/js/bundle.js\", \"js/plus.js\", \"js/minus.js\");\n    },\n    option =\u003e\n    {\n        option.EnableCaching = true;\n        option.EnableDiskCache = false;\n        option.EnableMemoryCache = true;\n        option.AllowEmptyBundle = true;\n    });\n```\n\n**enableCaching** determines if the `cache-control` HTTP headers should be set and if conditional GET (304) requests should be supported. This could be helpful to disable while in development mode.\n\nDefault: **true**\n\n**enableTagHelperBundling** determines if `\u003cscript\u003e` and `\u003clink\u003e` elements should point to the bundled path or a reference per source file should be created. This is helpful to disable when in development mode.\n\nDefault: **true**\n\n**enableMemoryCache** determines if the `IMemoryCache` is used for caching.  Can be helpful to disable while in development mode.\n\nDefault: **true**\n\n**enableDiskCache** determines if the pipeline assets are cached to disk.  This can speed up application restarts by loading pipline assets from the disk instead of re-executing the pipeline.  Can be helpful to disable while in development mode.\n\nDefault: **true**\n\n**cacheDirectory** sets the directory where assets will be stored if `enableDiskCache` is **true**.  Must be read/write.\n\nDefault: `\u003cContentRootPath\u003e/obj/WebOptimizerCache`\n\n**cdnUrl** is an absolute URL that, if present, is automatically adds a prefix to any script, stylesheet or media file on the page. A Tag Helper adds the prefix automatically when the Tag Helpers have been registered. See how to [register the Tag Helpers here](#tag-helpers).\n\nFor example. if the cdnUrl is set to `\"http://my-cdn.com\"` then script and link tags will prepend the *cdnUrl* to the references. For instance, this script tag:\n\n```html\n\u003cscript src=\"/js/file.js\"\u003e\u003c/script\u003e\n```\n\n...will become this:\n\n```html\n\u003cscript src=\"http://my-cdn.com/js/file.js\"\u003e\u003c/script\u003e\n```\n\n**allowEmptyBundle** determines the behavior when there is no content in source file of a bundle, by default 404 exception will be thrown when the bundle is requested, set to true to get a bundle with empty content instead.\n\nDefault: **false**\n\n### Custom pipeline\nRead more in the [custom pipeline documentation](https://ligershark.github.io/WebOptimizer/custom-pipeline.html).\n\n### Extend\nExtensions can hook up new transformations and consume existing ones. \n\nA good extension to look at is the [WebOptimizer.Sass](https://github.com/ligershark/WebOptimizer.Sass) extension. It demonstrates how to write a processor and how to write extension methods that makes it easy to hook it up to the pipeline. \n\n### Plugins\n\n- [WebOptimizer.TypeScript](https://github.com/ligershark/WebOptimizer.TypeScript) - compiles TypeScript/ES6+/JSX files to JavaScript (ES5)\n- [WebOptimizer.Sass](https://github.com/ligershark/WebOptimizer.Sass) - compiles Scss files to CSS\n- [WebOptimizer.Less](https://github.com/ligershark/WebOptimizer.Less) - compiles LESS files to CSS\n- [WebOptimizer.Dotless](https://github.com/twenzel/WebOptimizer.Dotless) - compiles LESS files to CSS (using [dotless](https://github.com/dotless/dotless) instead of node.js/less).\n- [WebOptimizer.AutoPrefixer](https://github.com/ligershark/WebOptimizer.AutoPrefixer) - Adds vendor prefixes to CSS\n- [WebOptimizer.Markdown](https://github.com/ligershark/WebOptimizer.Markdown) - compiles markdown files to HTML\n- [WebOptimizer.i18n](https://github.com/ligershark/WebOptimizer.i18n) - localization of .js files\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fligershark%2Fweboptimizer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fligershark%2Fweboptimizer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fligershark%2Fweboptimizer/lists"}