Old bundle version being served up (cached) after copy to server - asp.net-mvc

I have the following code which works fine on my local machine. Changes + build results in a new bundled file including the modified js code. However, when copied to our staging environment, an old version is being served up, so js changes are not seen.
Files copied include new dlls after a full rebuild + the changed js file. I've also recycled the web (even rebooted).
Even when I turn off optimisation, the staging site is still rendering the optimised script tag rather than a list of js files. Going off my head here.
Any ideas?
private void RegisterBundles()
{
BundleTable.Bundles.Add(new ScriptBundle("~/opt/code-scripts").Include(
"~/Scripts/ga.js",
"~/Scripts/jquery.unobtrusive*",
"~/Scripts/jquery.validate*",
"~/Scripts/code.js",
"~/Scripts/twitterFetcher.js",
"~/Scripts/tcode.js",
"~/Scripts/waypoints.js",
"~/Scripts/waypoints-sticky.js",
"~/Scripts/code-twitter.js"
));
BundleTable.Bundles.Add(new StyleBundle("~/opt/code-styles").Include(
"~/css/fonts.css",
"~/css/site.css"
));
BundleTable.EnableOptimizations = true;
}
...
#Scripts.Render("~/opt/code-scripts")

Related

Using NPM/Bower over Nuget MVC Site with bundling

I am trying to train myself on the new preferred way of referencing Front-end libraries in .Net Applications, so I have started to read into doing this with Gulp. I have read this article on replacing the Nuget packages with ones from bower
https://blogs.msdn.microsoft.com/cdndevs/2015/02/17/using-bower-with-visual-studio/
And this one to use Gulp
http://www.davepaquette.com/archive/2014/10/08/how-to-use-gulp-in-visual-studio.aspx
However I am having some trouble putting it all together. I want together some process that will not only replace all the Pre-Installed Nuget packages with bower packages, but also minify and bundle them with gulp instead of using the Web.Optimization library. The first link walks through doing that, but the gulp script is not providing the output I would expect (no dist folders for instance). This SO question has some good answers, but I am not seeing how I bundle all the libraries from bower (I read through all the comments and answers).
Using Grunt, Bower, Gulp, NPM with Visual Studio 2015 for a ASP.NET 4.5 Project
Obviously this stuff is new to me so I will get confused, but I want to make sure I do it the right way.
I am using Visual Studio 2015 and am creating a new MVC 4.5.2 Application, and I want to have all front-end libraries referenced and bundled/minified without using any .Net libraries. It seems at this point far easier to do it the old way
The question is a little broad but Ill hit on the key points since I've been doing this exact thing for a few weeks now.
Break what you are doing into two phases - replacing packages from nuget as phase one and replacing the .net bundling as the other.
Phase one - pretty simple actually, remove (uninstall) all the packages you have from nuget that have bower equivalents, then add those packages via the bower commands (don't forget to --save and --save-dev where needed). Then replace your script locations in the .net bundling with the new locations in bower_components. Once you can confirm your site works under this new layout while still using .net bundling you are ready for phase two.
Now for phase two, first and formost you need to learn the concept of 'globs' which are basically wild card based include and excludes for files in gulp. Probably the single most helpful thing I've found to help with this is the gulp plugin main-bower-files. So to create a good glob to start with I had this...
var paths = {};
paths.baseReleaseFolder = "app";
paths.baseJSReleaseFile = "site.min.js";
paths.jsSource = mainBowerFiles();//this gets all your bower scripts in an array
.concat([
"node_modules/angular/angular.js",//my angular is loaded via npm not bower because the bower version was giving me dependency issues (bug)
"node_modules/angular-route/angular-route.js",
"Source/output.js",//generated by MY typescript compiler
"!/Scripts", //this is an exclude
"!**/*.min.js" //also an exclude
]);
This is basically saying I want to select all the DISTRO bower plugins files needed to run whatever, include my angular js, and exclude anything in my scripts folder (typescript files and outputs) and exclude any ALREADY minified files (I want to do it all myself as one concatenated file).
Now I separate the js and css operations and wrap another event to call both so I end up with
gulp.task("min", ["min:js", "min:css"]);
gulp.task("min:js", function () {});
gulp.task("min:css", function () {});
Now as an example of how that works I have the following in my min:js
gulp.task("min:js", function () {
var jsFilter = filter('**/*.js', { restore: true });//extra file safty incase my glob is getting non js files somehow
return gulp
.src(paths.jsSource) //this is the 'glob' IE the file selector
.pipe(jsFilter) //enforce my filter from above (gulp-filter)
//.pipe(debug()) //useful to output what files are in use (gulp-debug)
.pipe(sourcemaps.init({loadMaps:true})) //create sourcemaps for final output(gulp-sourcemaps)
.pipe(uglify()) //min and ugilfy files on the way to next step (gulp-uglify)
.pipe(concat(paths.baseReleaseFolder + "/" + paths.baseJSReleaseFile)) //this takes all the files in the glob and creates a single file out of them in app/site.min.js
.pipe(rev()) //ignore this, cache-busting but requires work on the .net side to load the files, basically adds the file hash to the file name in the output
.pipe(sourcemaps.write(".")) //actually write my .map.js file to the final destination
.pipe(gulp.dest(".")) //write the final site.min.js to its location
.pipe(jsFilter.restore); //not sure but all filter examples do this.
});
So when this is all said and done I end up with a single site.min.js file in the 'app' folder that is the concatenated, minified, uglified version off all my bower components (and whatever else the glob hit). Now just to give you an idea on how plugin intensive using gulp is this is the declaration of all plugins I load into my main gulp script to do bascailly what .net bundling does for you....
var gulp = require('gulp'),
rimraf = require("gulp-rimraf"),
concat = require("gulp-concat"),
cssmin = require("gulp-cssmin"),
debug = require("gulp-debug"),
uglify = require("gulp-uglify"),
filter = require("gulp-filter"),
rename = require("gulp-rename"),
rev = require('gulp-rev'),
sourcemaps = require('gulp-sourcemaps'),
csslint = require('gulp-csslint'),
jslint = require('gulp-jslint'),
typescript = require("gulp-typescript"),
tslint = require("gulp-tslint"),
runSequence = require('run-sequence'),
mainNodeFiles = require("npm-main-files"),
mainBowerFiles = require('main-bower-files');
You probably don't need all of these (some are typescript, some are linters)
Edit: Heres my css function
gulp.task("min:css", function () {
var cssFilter = filter('**/*.css', { restore: true });
return gulp
.src(paths.cssSource)
.pipe(cssFilter)
//.pipe(debug())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(concat(paths.baseReleaseFolder + "/" + paths.baseCSReleaseFile))
.pipe(cssmin())
.pipe(rev())
.pipe(sourcemaps.write("."))
.pipe(gulp.dest("."))
.pipe(cssFilter.restore);
});

How can I use bundling with TinyMCE?

Internet Explorer has a fit when I try and render the minified TinyMCE bundle. So, in order to create a bundle that avoids minification, I followed these instructions to set up a "generic" or "basic" bundle in my BundleConfig:
bundles.Add( new Bundle( "~/Scripts/tinymce" )
.Include( "~/Scripts/tinymce/tinymce.min.js",
"~/Scripts/tinymce/jquery.tinymce.min.js" ) );
How do I render this on the page? None of the following seem to work:
#Bundles.Render( "~/Scripts/tinymce" )
#Bundle.Render( "~/Scripts/tinymce" )
#Bundle( "~/Scripts/tinymce" )
This renders the tag at least:
#Scripts.Render( "~/Scripts/tinymce" )
But it throws a 403 error when it tries to load the file:
"NetworkError: 403 Forbidden - http://localhost:1062/Scripts/tinymce/?v=MTojK5ORvAZmonNqNGJ3aWhOYyor-Fa8dZHTqfSOlUM1"
As pointed out in the comments, this question addresses part of the issue. Since you can't give your bundle a name that is also the name of an existing directory, I renamed it:
bundles.Add( new Bundle( "~/js/tinymce" )
.Include( "~/Scripts/tinymce/tinymce.min.js",
"~/Scripts/tinymce/jquery.tinymce.min.js" ) );
However, due to the way TinyMCE dynamically loads dependencies (plugin and theme files) I have to make sure all of that is included in the bundle as well:
bundles.Add( new Bundle( "~/js/tinymce" )
.Include( "~/Scripts/tinymce/tinymce.min.js",
"~/Scripts/tinymce/jquery.tinymce.min.js",
"~/Scripts/tinymce/plugins/autoresize/plugin.min.js",
"~/Scripts/tinymce/plugins/charmap/plugin.min.js",
"~/Scripts/tinymce/plugins/code/plugin.min.js",
"~/Scripts/tinymce/plugins/image/plugin.min.js",
"~/Scripts/tinymce/plugins/table/plugin.min.js",
"~/Scripts/tinymce/plugins/textcolor/plugin.min.js",
"~/Scripts/tinymce/themes/modern/theme.min.js") );
This means that if I wanted a new plugin it would require a code change and redeployment of bin files instead of a simple script update. However, the requirement for this application hasn't changed and probably won't any time soon (plus, it's an internal app), so this isn't really a problem.
Finally, TinyMCE also dynamically loads skin files (css, images and fonts). I can fix this by changing the TinyMCE configuration to specify the skin directory:
$('.tinymce').tinymce({
skin_url: '/Scripts/tinymce/skins/lightgray',
// other stuff...
});
I tried creating a bundle for the theme files:
bundles.Add( new Bundle( "~/css/tinymce" )
.Include( "~/Scripts/tinymce/skins/lightgray/*.css" ) );
And changing my configuration like so:
$('.tinymce').tinymce({
skin_url: '/css/tinymce',
// blah blah blah...
});
But, because TinyMCE looks for individual files, this did not work. It threw 404 errors for skin.min.css and content.min.css.
So now I have some of the TinyMCE files bundled, but not all of them. This is at least better than what I had before (separate HTTP requests for every single file).
Change your bundle name to something other than an existing location on disk
For example: Bundle( "~/Scripts/tinymce") to Bundle( "~/js/tinymce")

Unable to minify css in asp.net mvc

I am trying to minify a css file using System.Web.Optimizations and it won't work
var fontAwsomeBundle = new Bundle("~/bundles/css/font-awsome", new CssMinify())
.Include("~/content/packages/font-awesome/css/font-awesome-{version}.css", new CssRewriteUrlTransform());
I have run site with optimizations enabled and debug=false
There is no pre-minified file in the same location
.net 4.5, IIS Express
System.Web.Optimizations 1.1.0.0, WebGrease 1.6.5135.21930. I tried updating WebGrease to latest, but that didn't work
I am not getting any minification errors in the bundle file, just unminified version of file
Tried this version as well. fontAwsomeBundle.Transforms.Add(new CssMinify());
You should use the StyleBundle for .css and ScriptBundle for .js.
var fontAwsomeBundle = new StyleBundle("~/bundles/css/font-awsome", new CssMinify())
.Include("~/content/packages/font-awesome/css/font-awesome-{version}.css", new CssRewriteUrlTransform());

Custom script transform in ASP.NET MVC bundle ignored when debug is false and minified file exists

I'm working on an MVC 5.0 (.Net 4.5) application where I need to apply a custom JavaScript transform to my included bundle files. One of those files, which I'm calling dummy.js for illustration purposes, has a minified file called dummy.min.js.
I created a custom script transform to replace injected window.jQuery references with a different expression. Everything works fine when I run locally and in debug mode, but when debug mode is turned off in the Web.config file, the Bundle returns the contents of the dummy.min.js file, but my script transform is not applied to it. It only gets applied to JavaScript files that don't have an associated .min.js file.
Does anyone have an idea on how to resolve this? It almost sounds like a bug in MVC.
A workaround is to remove the minified file. This post kind of addresses my situation by suggesting removing the .min.js file since MVC minifies by default, but I'm looking for an alternative solution (if any).
Thank you so much in advance.
Here's how to reproduce the above:
If you're interested in reproducing my issue, here's a quick BundleConfig and the actual custom script transform. It replaces all instances of window.jQuery with window.$jq1_9||window.jQuery, assuming it is injected via a self-executing anonymous function.
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(
new ScriptBundle("~/bundles/dummy")
.Include("~/Scripts/dummy.js", new InjectedJQueryVariableRewriteTransform()));
}
}
public class InjectedJQueryVariableRewriteTransform : System.Web.Optimization.IItemTransform
{
public string Process(string includedVirtualPath, string javaScriptCode)
{
// TODO: I understand this approach is naiive, but it does the trick for now.
return javaScriptCode.Replace("window.jQuery", "window.$jq1_9 || window.jQuery");
}
}
If you have Visual Studio 2012 and MVC 4, you will need version 1.1.0 of the System.Web.Optimization assembly, which you can obtain by running the following command in the Nuget Package Manager. At time of writing it installs version 1.1.2 of the package.
Install-Package Microsoft.AspNet.Web.Optimization
Here's the sample JavaScript dummy.js. You can create a copy of it and name it dummy.min.js:
(function ($) {
"use strict";
// TODO: Do something interesting...
})(window.jQuery);
Set the debug attribute to false in the following element in Web.config:
<compilation debug="false" targetFramework="4.5" />
Assuming the application's port is 9221, render the bundle in Firefox or Chrome:
http://localhost:9221/bundles/dummy
You will see that when debug is set to true, the transform is applied, as shown below:
(function(){"use strict"})(window.$jq1_9||window.jQuery)
When it is set to false. It is ignored and only the .min.js file is used:
(function(){"use strict"})(window.jQuery)
If you add this line:
bundles.FileExtensionReplacementList.Clear();
you will remove the rule for using .min files when bundling is enabled. You will remove all rules, unfortunately, so if you need any of the other ones you'll need to add them manually. Also, this will change the rules for all bundles.
If you just want to disable these replacement rules for just one bundle, you can just set the EnableFileExtensionReplacements property to false on that specific bundle:
var bundle = new ScriptBundle("...");
bundle.EnableFileExtensionReplacements = false;

How to make web_ui compile css files automatically

I'm using web_ui and whenever I change a CSS file in web/css/ it will not be compiled unless I change the web/index.html file. I guess that's because only the file 'web/index.html' is listed as entry point in build.dart.
But adding the stylesheet to the entry points list didn't work.
Is there a way to autocompile CSS files every time they are changed without having to edit the .html files?
Keep in mind that you can edit any .dart or .html file and the compiler will run; it doesn't have to be the entry point file.
Autocompilation of CSS files on change can be achieved by passing the compiler the full flag:
build(['--machine', '--full'], ['web/index.html']);
The machine flag tells the compiler to print messages to the Dart Editor console. For a full list of flags see Build.dart and the Dart Editor Build System.
This method means that every time a file is changed your entire project will be rebuilt instead of the usual incremental approach. If you have a large project this may take a while. Here is a more comprehensive build file that takes advantage of incremental compilation and only rebuilds the whole project if a css file was changed:
List<String> args = new Options().arguments;
bool fullRebuild = false;
for (String arg in args) {
if (arg.startsWith('--changed=') && arg.endsWith('.css')) {
fullRebuild = true;
}
}
if(fullRebuild) {
build(['--machine', '--full'], ['web/index.html']);
} else {
build(args, ['web/index.html']);
}

Resources