I'm performing Bundling and Minification in ASP.NET Core 2.0 MVC and I've run into an issue with the minification taking place when it shouldn't. In my page I have the following script tag:
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT"
crossorigin="anonymous"
asp-fallback-test="window.jQuery"
asp-fallback-src="~/js/jquery.min.js">
</script>
In my bundleconfig.json I have the following section:
{
"outputFileName": "wwwroot/js/jquery.min.js",
"inputFiles": [
"node_modules/jquery/dist/jquery.min.js"
],
"minify": {
"enabled": false
}
}
The problem is that the ~/js/jquery.min.js file is losing its trailing newline character when it's transformed by this bundling/minification process which makes the expected hash of the file no longer match. As a workaround I can specify 2 hashes for the integrity value to support a file with or without the newline like so:
integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT sha384-I7/UTpkJas2maMjJpGmrvEgQecqO8Dta/9Wwh+cQrH6Jj984WRRFhWg4MV/oTkIW"
But that's less efficient than just making sure the minification doesn't touch this file. How can I stop this newline from being trimmed?
I find the default minification system somewhat limited and for things like excluding files I usually end up using a task runner like Gulp instead.
So here is how to do what you want using gulp:
Adding npm support
First add support for npm packages by adding a package.json file if you don't already have it, inside the solution explorer, right click on your project name, then, add new item and search for npm configuration file.
Inside package.json add the following required gulp dependencies as well as any other client side library you want to use, for example, jquery, bootstrap, etc.:
{
"version": "1.0.0",
"name": "asp.net",
"private": true,
"devDependencies": {
"gulp": "3.9.1",
"gulp-concat": "2.6.1",
"gulp-cssmin": "0.2.0",
"gulp-uglify": "3.0.0",
"gulp-filter": "5.1.0",
"gulp-watch": "5.0.0",
"rimraf": "2.6.2",
"jquery": "3.3.1",
"bootstrap": "4.1.1",
"popper.js": "1.14.3"
}
}
Once you save the file a folder will be added to your project directory called node_modules (this folder is not visible unless you activate "Show all files" on the Solution Explorer toolbar.
This folder contains all the libraries and their dependencies, we will use gulp to copy the libraries you want to include from node_modules to your wwwroot folder.
Setting up Gulp
You can use these folders for your application scripts and style-sheets, we will use gulp to combine, minify and copy to the wwwroot folder all these resources.
The idea is to avoid using the wwwroot folder directly so you have full control of what you expose and how when you publish your Website.
You can use gulp to create different tasks that can be executed automatically before or after build, when cleaning the project, when the project is open in Visual Studio, etc...
For this example I will create the following tasks:
Gulp has a lot of plugins, you need to specify which ones are required for your project, to do that, add the following at the beginning of your script:
/// <binding BeforeBuild='clean, dev, min' Clean='clean' ProjectOpened='watch' />
"use strict";
var gulp = require("gulp"),
rimraf = require("rimraf"),
concat = require("gulp-concat"),
cssmin = require("gulp-cssmin"),
uglify = require("gulp-uglify"),
gulpFilter = require("gulp-filter");
As you may deduct from the name, the gulp-filter
plugin is what we will use to exclude files from the minification process.
Now, you need to set up some general paths:
var paths = {
webroot: "./wwwroot/",
scripts: "./Scripts/",
styles: "./Styles/",
node_modules: "./node_modules/"
};
//The following paths will be used to look for any js and css file in your Script and Styles folder or any subfolder inside them
paths.scripts = paths.scripts + "**/*.js";
paths.styles = paths.styles + "**/*.css";
Note: if you need to specify the order of any script inside these folders, you can do as follows:
paths.scripts = [paths.scripts + "/somescript.js", paths.scripts + "**/*.js"];
paths.styles = [paths.styles + "/somecss.css", paths.styles + "**/*.css"];
Next, define the paths to the vendor scripts which are inside the node_modules folder:
paths.vendorJs = [paths.node_modules + "jquery/dist/jquery.js",
paths.node_modules + "popper.js/dist/umd/popper.js",
paths.node_modules + "bootstrap/dist/js/bootstrap.js"];
paths.vendorCss = [paths.node_modules + "bootstrap/dist/css/bootstrap.css"];
paths.minVendorJs = [paths.node_modules + "jquery/dist/jquery.min.js",
paths.node_modules + "jquery/dist/umd/popper.min.js",
paths.node_modules + "bootstrap/dist/js/bootstrap.min.js"];
paths.minVendorCss = [paths.node_modules + "bootstrap/dist/css/bootstrap.min.css"];
The idea is to avoid minification for any file that is specified in paths.minVendorJs
as they are already minified. The following path will allow you to minify any specific vendor file if you need to do so:
paths.vendorCssToMinify = [paths.node_modules + "perfect-scrollbar/css/perfect-scrollbar.css"]
Then, we define the output files that will be generated, for this example, only one script and one style-sheet will be generated that contains all the application files as well as all vendor files combined within them:
paths.concatJsDest = paths.webroot + "js/application.js";
paths.concatCssDest = paths.webroot + "css/application.css";
paths.minConcatJsDest = paths.webroot + "js/application.min.js";
paths.minConcatCssDest = paths.webroot + "css/application.min.css";
Finally, we define each task:
gulp.task("clean:js", function (cb) {
rimraf(paths.webroot + "js/**/*.js", cb);
});
gulp.task("clean:css", function (cb) {
rimraf(paths.webroot + "css/**/*.css", cb);
});
gulp.task("clean", ["clean:js", "clean:css"]);
gulp.task('watch', function () {
gulp.watch(paths.styles, ['dev:css', 'clean:css']);
gulp.watch(paths.scripts, ['dev:js', 'clean:js', ]);
});
gulp.task("dev:js", function () {
return gulp.src(paths.vendorJs.concat(paths.scripts))
.pipe(concat(paths.concatJsDest))
.pipe(gulp.dest('.'));
});
gulp.task("dev:css", function () {
return gulp.src(paths.vendorCss.concat(paths.styles))
.pipe(concat(paths.concatCssDest))
.pipe(gulp.dest('.'));
});
gulp.task("min:js", function () {
// Files to skip from minification.
var filter = gulpFilter(paths.minVendorJs, {
restore: true
});
return gulp.src(paths.minVendorJs.concat(paths.scripts))
.pipe(filter)
.pipe(uglify())
.pipe(filter.restore)
.pipe(concat(paths.minConcatJsDest))
.pipe(gulp.dest("."));
});
gulp.task("min:css", function () {
// Files to skip from minification.
var filter = gulpFilter(paths.minVendorCss, {
restore: true
});
return gulp.src(paths.minVendorCss.concat(paths.vendorCssToMinify).concat(paths.styles))
.pipe(filter)
.pipe(cssmin())
.pipe(filter.restore)
.pipe(concat(paths.minConcatCssDest))
.pipe(gulp.dest("."));
});
gulp.task("dev", ["dev:js", "dev:css"]);
gulp.task("min", ["min:js", "min:css"]);
And that's it!
To test the configuration, right click on your gulpfile.js file and select Task Runner Explorer. If everything went correctly, you should see something like this:
You can run the tasks by double clicking on them.
You can use the generated resources in your razor views like this:
<environment include="Development">
<link rel="stylesheet" href="~/css/application.css" />
</environment>
<environment exclude="Development">
<link rel="stylesheet" href="~/css/application.min.css" />
</environment>
And
<environment include="Development">
<script src="~/js/application.js"></script>
</environment>
<environment exclude="Development">
<script src="~/js/application.min.js"></script>
</environment>
If you need to compile LESS or SASS files you need to use the corresponding Gulp plugin, see here for an example.
More information about using Gulp in ASP.NET Core: https://docs.microsoft.com/en-us/aspnet/core/client-side/using-gulp?view=aspnetcore-2.0
Try with following configuration :
//1. by default outputMode is singleLine which removes new lines and output on a single line. We are setting it to none.
{
"outputFileName": "wwwroot/js/jquery.min.js",
"inputFiles": [
"node_modules/jquery/dist/jquery.min.js"
],
"minify": {
"enabled": false,
outputMode: "none"
}
}
//Alternatively, outputMode = "multipleLines" should work with a indentSize of 0 as well.
//In this mode , output file has each line indented by specified indentSize
{
"outputFileName": "wwwroot/js/jquery.min.js",
"inputFiles": [
"node_modules/jquery/dist/jquery.min.js"
],
"minify": {
"enabled": false,
outputMode: "multipleLines",
indentSize: 0
}
}
You can read about available settings for javascript minifier here
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With