What is a good strategy for automatically applying and/or updating cache buster variables on my javascript and stylesheet references in an ASP.Net website?
E.g. Transforming
<script type="text/javascript" src="/js/myScript.js" />
to
<script type="text/javascript" src="/js/myScript.js?rev=12345" />
UPDATE: Continuous Integration is not required.
I am using continuous integration (Jenkins to be specific), so it would be great if the method for updating the variables was based on the build number for instance.
While I can live with applying the original variables manually in the source code and just updating them via the strategy, it would be a nice addition if the strategy could also apply the original variable if it did not already exist (say to legacy code).
Off the top of my head, I can imagine using a Powershell script that scans through all *.aspx and *.ascx files and uses a regular expression to find the appropriate references and update them. But you know what they say about using regular expressions... then I have two problems :)
You may also want to look into Cassette, RequestReduce and Bundler.
Cassette: Automatically sorts, concatenates, minifies, caches and versions all your JavaScript, CoffeeScript, CSS, LESS, Sass and HTML templates.
RequestReduce Makes your website faster - sometimes much faster - with almost no effort
Auto generates sprites from your background images
Optimizes Sprite PNG format and compression
Minifies CSS and Javascript
Optimizes caching headers and ETags
Runs on any IIS web site including Classic ASP and PHP
Can sync content accross multiple web servers
Works well with CDNs
Compiles Less, Sass and Coffee script
Bundler: "Compile, Minify, Combine Less/Sass/Css/JS/CoffeeScript files. Easily use from MVC"
The answer to cache buster variables in ASP.Net is to use one of the various CSS/JS minification libraries.
I was thinking that the cache buster variables would need to be updated with each deployment to our servers, but the minification libraries apply a hash tag based on the contents of the individual CSS/JS files.
Since I am developing a .Net 3.5 website my choices were a little restricted. I ended up using SquishIt (available as a NuGet package) and it was SO easy to integrate.
<link href="/<my_css_path>/<css_file_1>.css" rel="stylesheet" type="text/css" />
<link href="/<my_css_path>/<css_file_2>.css" rel="stylesheet" type="text/css" />
<link href="/<my_css_path>/<css_file_3>.css" rel="stylesheet" type="text/css" />
became
<%= Bundle.Css()
.Add("~/<my_css_path>")
.Render("~/<my_css_path>/combined_#.css") %>
and that is basically it! Similar idea with the javascript. As long as debug="true" in your web.config for local development and debug="false" for your staging/production environments, SquishIt will leave your CSS/JS unseparated and unminified for local development and then combine, minify and hash (for cache busting) for your other environments.
Related
As asked previously in Have Grunt include different Javascript files in development and production and Have Grunt generate index.html for different setups
I want expanded assets includes for development:
<script src="js/lib-1.js"></script>
<script src="js/lib-2.js"></script>
<script src="js/lib-3.js"></script>
...
And for production or staging:
<script src="js/all-files-in-one.js"></script>
However, I don't want to synchronize my include code in two places. For most of the available answers, I have to specify the list of files in grunt task first and then replicate the include code again in the html. The closest solution I could find is this answer: https://stackoverflow.com/a/21488659/515585, in which I only need to maintain an array of files in the grunt task. And all the answers requires a daunting amount of setup to make it work.
An even better solution would allow me to use sprocket syntax to require other asset files right in the context of the code (instead of using a build file) a la Rails asset pipeline. And expand them with a single switch of variable, so I could debug each individual file in development. I've looked at the grunt-sprockets-directives, but I don't see it offer the expanding option. Maybe it does?
I would like to not use asset pipeline from my backend framework, and I am asking for any front end build tools, not just grunt specifically. Gulp, Yeoman, or even better Broccoli.js answers are also appreciated.
I want some expert advice on ASP.NET MVC Bundling and Minification. I have in my project script files that have both unminified (.js) and minified versions (.min.js). I have included them in my script bundle as follows:
bundles.Add(new ScriptBundle("~/bundles/layout").Include(
"~/Scripts/jquery-{version}.js",
"~/Scripts/lib/errorhandling.js",
"~/Scripts/lib/errorhandling.min.js",
"~/Scripts/vendor/modernizr.custom.js",
"~/Scripts/vendor/modernizr.custom.min.js",
"~/Scripts/toastr.js",
"~/Scripts/toastr.min.js"));
It seems that the bundle indeed contains only once each script file, not twice. I have confirmed this both for development and production. (As a side note, in development, that is, when debug=true, the bundles are not rendered but the files are included as separate script tags. This is the desired behaviour for me, as well.)
My questions are:
(1) Is this the best and recommended way to include already minified files for production setup and unminified files for development?
(2) Does ASP.NET try to minify the whole bundle in production (even though it is already minified)? If yes, what is the best way to prevent ASP.NET from trying to minify the bundle?
Thanks in advance!
There is no need to specifically include the minified versions in your script bundle. By default, MVC will search for a matching file with .min.js and include that (not entirely sure if it trys to minify further). If not, it creates a minified version. You can test this by adding the following to BundleConfig.cs
using System.Web.Optimization;
then adding the following at the end to override debug=true in development
BundleTable.EnableOptimizations = true;
From MS documentation
The bundling framework follows several common conventions such as:
Selecting “.min” file for release when “FileX.min.js” and “FileX.js”
exist.
Selecting the non “.min” version for debug.
Ignoring “-vsdoc” files (such as jquery-1.7.1-vsdoc.js), which are used only by
IntelliSense.
Minification, or not, the bundling feature is useful to for logical groupings of scripts and css that go together, and as a single place to control things. It also generates unique URLs, so eliminates browser cache problems.
If you use ScriptBundle, the engine will try to minify, (except when you set debug=true as you've shown).
You can turn off minification, but retain bundling, by just using Bundle() instead of ScriptBundle(). See Martin's answer here:
ASP.NET Bundles how to disable minification
As an aside, using pre-minified files with Bundle() as opposed to ScriptBundle(), will preserve the license headers. With jquery's MIT license, it at least stipulates that it should not be removed. I'm not sure how to interpret the fact that the default Microsoft MVC template uses ScriptBundle().
I have an ASP .NET MVC 5 project. The template came with bootstrap, and I have generally left it alone. I now want to use the Bootstrap source as well as Bootswatch variables files for styles. Previously, I have put all the resources needed to build the libraries in to a separate project and written a custom build script, but there must be an easier way. I have found a few related packages, but none provide anything comprehensive.
Are there premade NuGet packages that will set up the two libraries? If not, what do I need to do to get it up-and-running (including automatic build in VS)?
Depending on the setup of your project, it may be better to get your Bootstrap-specific CSS from a CDN, even if using MVC.
Check Bootstrap CDN as well as their Bootswatch resources for quick easy usage:
<!-- Default Theme -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css" />
<!-- if you wanted Amelia, use this instead: -->
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootswatch/3.1.1/amelia/bootstrap.min.css" />
TL;DR: IItemTransform isn't getting executed when a minified file already exists in the same folder as the original (non-minified) file.
Problem explanation
I'm having this issue mainly because of CSS relative image references. If you used IItemTransform with Javascript files, the same applies.
This is what I'm using:
I'm using Visual Studio with Web Essentials addin to have support for LESS files
I'm writing LESS files and have Web Essentials addin automatically minify files on save
I'm also using bundling and minification in my project
When creating CSS bundles I'm using CssRewriteUrlTransform to make CSS URLs absolute (i.e. background images) so that images still work after bundling several CSS files together
Nothing unusual here so far, but it doesn't work.
What seems to be the problem?
The way that bundling and minification works is it tries to avoid excessive processing. This means that when a minified file exists in the same folder as the original one it won't run its own minification and rather serve existing file.
This would be all right as long as it would at least run transforms over those preexisting minified files. But it doesn't. So I end up with relative URLs in a bundle which breaks pretty much all those resources.
Workarounds
Always provide absolute paths in LESS files
Disable file minification on save in Web Essentials settings
Refer to minified files when defining my bundles because they don't have a minified version (*.min.css doens't have a *.min.min.css) so minifier actually picks up the file and minifies while also running transformations over it.
From the standpoint of my development process and tools used (and configured the way they are) this looks like a bug. If those files would be the result of the same minification process this wouldn't be a bug at all as transformations would be executed when minification would execute. It's true that such functionality doesn't exist and likely never will as app would need write permissions to make it work. Outcome: this is a bug. Existing minified files should be processed through transformations before being cached.
Question
Is it possible to somehow convince bundling and minification to either:
not use existing minified file versions
run transformations over existing minified versions
Have you considered using Grunt? http://gruntjs.com/
It has a learning curve, but, the information pool is huge. The issues that you are having with web essentials wouldn't be a problem with grunt.
I'm using it in VS, now, to minify, bundle and transpile both css and javascript as well as reorganize files into a deployment directory. Once you've set up a directory structure, a grunt file could very easily be reused.
With the add-on in VS (linked, below), you can right click on the grunt file and select the grunt tasks to run from a popup menu.
https://visualstudiogallery.msdn.microsoft.com/dcbc5325-79ef-4b72-960e-0a51ee33a0ff
Grunt "tasks" as they are called can be created by downloading various plugins i.e. https://www.npmjs.com/package/grunt-contrib-less.
I have never used LESS or web essentials, so please take this post for what it is worth (not much.) Could you add a pre-build command to simply delete the old files, then do a rebuild when you need to update the CSS.
I am currently using PHP minify to combine and compress the static files (CSS and JS). With PHP minify it is very easy to develop and deploy. Because:
Say there are two files: a.js and b.js and we combine and minify them in ab.js. Now its enough for me to include only one script tag:
<script type="text/javascript" src="http://static.example.com/min/g=ab&v=7"></script>
With this flexibility I can develop in a.js and b.js and at the same time test the final minified version without changing the include tag above. I don't even need to change while releasing.
But now I want to move my static files to CDN server where PHP won't be there, so I guess I have to use YUI compressor to minify and combine before uploading. Now If I am combining a.js and b.js with YUI compressor, I have to change the include tag which I used to develop.
So when developing I've to use:
<script type="text/javascript" src="http://static.example.com/a.js"></script>
<script type="text/javascript" src="http://static.example.com/b.js"></script>
And when uploading I've to use:
<script type="text/javascript" src="http://static.example.com/ab.min.js"></script>
Then it becomes a problem because, the two lines has to be combined into one. Whats your setup to manage this?
Specify the base URL to ab.min.js (i.e. http://static.example.com/) in a config file. In the production config, use the CDN location. In your development config, use the automatically minified location.
Looks like you're comfortable with using different base URLs for development and production, but that combining two lines into one is your problem.
If this is the case, perhaps you want to split this step in two.
Combine a.js and b.js manually into ab.js, but don't minify. You only need to do this once. Now during development you can work directly on the source code in ab.js.
Before uploading, typically as an automated step in your build process, use YUI compressor to minify ab.js.
If you want to keep a.js and b.js separated, then the above will not work for you and you probably need some kind of preprocessor that will modify the script tags in your source code as required.