Control how your bower packages are installed with a gulpfile in ASP.NET 5
ASP.NET 5 beta 8 is out. Yes, that's a lot of betas, but it's important to get things right when you're doing something new like this. You can find instructions in our documentation for installing ASP.NET 5 beta8 on Windows, Mac and Linux.
ASP.NET 5 uses the NuGet package manager to get server-side libraries but for client-side things we recommend folks use Bower. The most popular JavaScript and CSS libraries are there, and there's no need for us to duplicate them in NuGet. This means ASP.NET 5 folks get to use the same great client-side libraries that other open web technologies enjoy.
In very early builds of ASP.NET 5 we put those libraries in a folder outside the web root (wwwroot) into bower_components or npm_components and then used a gulp/grunt (think MSBuild for JavaScript) task to copy the files you want to deploy into wwwroot in preparation for deployment. However this confused a LOT of folks who weren't familiar with these tools. It also meant another step after installing a new JavaScript library. For example, you'd install angular with bower, then manually edit the gulp file to copy the .js you wanted into the folder of your choice, then run gulp to actually move it. These are common tasks for many of today's open web developers, but have been confusing for ASP.NET 5 users who don't usually work on the command line. So, this was changed a while back and your bower libraries show up right in your wwwroot.
While this is convenient change and great to starters, at some point you'll want to graduate to a more formal process and want to move your bower client libraries back out, and then setup a task to move in just a files you want. Let's take a moment and switch it back the way it was.
Here's how.
Update your .bowerrc and project.json
In the root of your project is a .bowerrc file. It looks like this:
{
"directory": "wwwroot/lib"
}
Change it to something like this, and delete your actual wwwroot/lib folder.
{
"directory": "bower_components"
}
Exclude your source bower folder from your project.json
You'll also want to go into your project.json file for ASP.NET 5 and make sure that your source bower_components folder is excluded from the project and any packing and publishing process.
"exclude": [
"wwwroot",
"node_modules",
"bower_components"
],
Update your gulpfile.js
In your gulpfile, make sure that path is present in paths. There are totally other ways to do this, including having gulp install bower and figure out the path. It's up to you how sophisticated you want your gulpfile to get as long as the result is that production ready .js ends up in your wwwroot ready to be served to the customer. Also include a lib or destination for where your resulting JavaScript gets copied. Could be scripts, could be js, could be lib as in my case.
var paths = {
webroot: "./" + project.webroot + "/",
bower: "./bower_components/",
lib: "./" + project.webroot + "/lib/"
};
Add a copy task to your Gulpfile
Now open your Gulpfile and note all the tasks. You're going to add a copy task to copy in just the files you want for deployment with your web app.
Here is an example copy task:
gulp.task("copy", ["clean"], function () {
var bower = {
"bootstrap": "bootstrap/dist/**/*.{js,map,css,ttf,svg,woff,eot}",
"bootstrap-touch-carousel": "bootstrap-touch-carousel/dist/**/*.{js,css}",
"hammer.js": "hammer.js/hammer*.{js,map}",
"jquery": "jquery/jquery*.{js,map}",
"jquery-validation": "jquery-validation/jquery.validate.js",
"jquery-validation-unobtrusive": "jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"
}
for (var destinationDir in bower) {
gulp.src(paths.bower + bower[destinationDir])
.pipe(gulp.dest(paths.lib + destinationDir));
}
});
Do note this is a very simple and very explicit copy tasks. Others might just copy more or less, or even use a globbing wildcard. It's up to you. The point is, if you don't like a behavior in ASP.NET 5 or in the general build flow of your web application you have more power than ever before.
Right click the Bower node in the Solution Explorer and "Restore Packages." You can also do this in the command line or just let it happen at build time.
Looking in this simplified screenshot, you can see the bower dependencies that come down into the ~/bower_components folder. Just the parts I want are moved into the ~/wwwroot/lib/** folder when the gulpfile runs the copy task.
Feel free to share in the comments links to your blog posts on how YOU like your gulpfiles and build process to work!
Sponsor: Thanks to Infragistics for sponsoring the feed this week! Responsive web design on any browser, any platform and any device with Infragistics jQuery/HTML5 Controls. Get super-charged performance with the world’s fastest HTML5 Grid - Download for free now!
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
VS is slouching into a mish-mash of opensource command line tools with many hand edited script files. Now may be mostly easy, though inheriting a system 5 years from now will be a costly costly task.
All web developers need to be comfortable with npm/node, bower, grunt/gulp, et all. These aren't that new of tools or workflows, and they've largely taken over the entire industry - except the isolated sector of .net development. MS is taking an amazing, and very likely platform-saving move with integrating these tools into their flagship development environment. I think ASP.NET 5 and VS's new tooling secures ASP.NET's position for the next decade, as opposed to it slowly dying out with the wave after wave of younger developers who typically ignore the platform entirely.
It's also hardly a mish-mash, either. The support is incredibly powerful and convenient - and that's coming from a die hard command-line guy (I thought I'd hate the task runner window, but it's actually awesome!). The only concern for me is what happens with newer versions of the package managers/task runners - but I'm sure MS could figure out something along the lines of auto-detecting which version of the CLI each respective tool uses based off of package.json.
@Scott: thanks for the writeup! I actually came across this the other day. To be honest, the new defaults actually confused me, and I'm a huge proponent of using a task runner to copy the files. When I saw my libraries dumped into wwwroot, I thought "oh, huh, is this how MS intends for us to use Bower now? That sucks."
But I was able to locate the .rc file and get things setup the way I wanted to. However, I always feel a little "dirty" about changing stuff like that, and when I first made the change I wasn't sure if the tooling supported such a thing. If only JSON files could have comments, I'd recommend the templated .rc file mentions that it's perfectly safe to change the directory.
Either way, the BETA8 is pretty slick! I was able to put together a quick project skeleton with everything from TypeScript/tsd to Less, a proper watch task in gulp with live-reload, and it worked *almost* flawlessly. There seemed to be a crash that happened half the time I added or removed a package from bower.json or package.json. When it crashed, the next time I started it up and hit CTRL+F5, the webserver would throw a random internal server error at me, until I went back and hit CTRL+F5 again.
Hope that gets sorted out, because otherwise it's looking amazing! Can't wait for RC!
I would use bower exportsOverride for this because your using bower anyway. Here's an example (using grunt), but it shows the bower override. I have added an override for angular and angular-ui-router.
http://damienbod.com/2015/03/28/asp-net-5-angularjs-application-using-angular-ui-router/
Greetings Damien
This is bad as it can make your build unpredictable by starting dependent tasks too early or not returning an appropriate exit code on failure, which causes issues with CI.
Instead of processing files from an external folder like bower_components, you can still process them from wwwroot/lib into a different place for production.
So for example in dev you can reference directly files below wwwroot/lib without having to run taskrunner, but for production you could run taskrunner to process just the files you want into a different folder such as wwwroot/js and you can make your scripts reference different files for dev vs production. There is a more elaborate discussion of this on stackoverflow
It should be possible to exclude wwwroot/lib from publishing, so it is basically the same approach as using bower_components except the workflow is easier because you don't need to run taskrunner to copy files for dev, you just use them directly, and then copy them elsewhere for production.
[1]: http://gofore.com/ohjelmistokehitys/stop-using-bower
[2]: http://blog.npmjs.org/post/101775448305/npm-and-front-end-packaging
[3]: http://blog.npmjs.org/post/122450408965/npm-weekly-20-npm-3-is-here-ish
Bower is STILL missing a feature like shrinkwrap (npm) or the lock file (dnu), meaning there is no practical way of locking down every package version of your dependency tree. So every "bower install" is based on pure luck. Oh, there's a PR for that feature. It doesn't get much love. https://github.com/bower/bower/pull/1748
We have removed bower wherever possible from our projects.
For mature applications, upgrade and QA re-verification costs are significant burden, measured in tens of man months, and not budgeted for by large organizations for a sizable portion of their portfolio of applications. Most or all of an organization's budget would be spent just upgrading/retesting applications if they upgraded them to the latest VS, tools version, OS version, ...
We will soon need a package managers manager to manage all those package managers.
It's all started from the NuGet command line making its way into the VS IDE. And now just look where we are - command lines everywhere, package managers installing another package managers and stuff to minify and bundle scripts...
People. JUST. MINIFY. NOTHING.
And leave SASS\LESS and [script-on-top-of-javascript]script compilers to IDEs, don't turn your machine into a trashcan installing all those things with meaningless names.
You are doing it wrong!!
Every bower.json file declares its dependencies and its 'main' files (= which files are relevant).
For instance, this is the bower.json of the angular-bootstrap package:
{
"author": {
"name": "https://github.com/angular-ui/bootstrap/graphs/contributors"
},
"name": "angular-bootstrap",
"keywords": [
"angular",
"angular-ui",
"bootstrap"
],
"license": "MIT",
"ignore": [],
"description": "Native AngularJS (Angular) directives for Bootstrap.",
"version": "0.13.4",
"main": ["./ui-bootstrap-tpls.js"],
"dependencies": {
"angular": ">=1.3.0"
}
}
It spells out what it depends on ("angular": ">=1.3.0") and where its important bits are (["./ui-bootstrap-tpls.js"]).
A plugin like 'main-bower-files' ( https://www.npmjs.com/package/gulp-main-bower-files ) will figure out the dependencies between your packages and put all the important files in a stream which can then be used to
a) copy to the output directory
b) inject *in order* into an html file (if you so require)
npm install main-bower-files
gulp.task('copy', function () {
log('Copying lib files to wwwroot...');
var mainBowerFiles = require('main-bower-files');
return gulp
.src(mainBowerFiles(), { base: './bower_components' })
.pipe(gulp.dest('wwwroot/lib'));
});
In addition you can override the main files in your own bower.json!
Can it be more simple?
Why doesn't MS make this the default behavior instead of tampering with the default bower_components directory?
This article is great. Indeed if I can understand the step back for a huge majority of users that do not grasp this philosophy, this way of separating the sources from what is effectively posted on the web site is the way it should be! So thank you for this clear explanation that will avoid me to be frustrated from this step back.
Frederic
I'd like to see VS just use the standardly installed node, bower, etc. rather than use older, hidden, non-upgradable versions. The fix is to adjust your 3rd party tooling path setting, but who knows about that setting in the first place, and who knows what other problems that could cause.
https://app.pluralsight.com/library/courses/npm-build-tool-introduction/table-of-contents
It would make more sense to bundle in webpack and use npm to manage client dependencies. Seems to be the direction things are headed. Especially when you consider that it then becomes way easier to integrate typescript or es6 with your project. Could even be the default!
1) Are there plans to upgrade the version of Bower to something later? The version Visual Studio uses is pretty old...
2) We are planning to define some Bower packages that will not be registered in the public Bower repository. Is there any way to update the intellisense data used in the bower.json editor? That is, we would like to have our custom packages show up in the list as a user adds dependencies.
I would like to make a request for another blog post.
I don't like so much that dnvm heavily on the user profile's location.
In my case I have to work with dnvm with several profiles and I would like to rely on only one installation of the environments and the caches for the different user profile I use on the same machine.
Would you elaborate a method in order to achieve this.
I would really appreciate as you always find the right way to do that type of improvement.
Thanks in advance for taking my request in consideration
Frederic
Nice article and thank you for your blog, I really like it!
I recently started using npm, bower and Gulp and noticed my files are excluded from the solution. Is there a way to auto-include them (for publish purposes)?
Thanks!
Anyone who is interested to see this can vote at the above link.
Comments are closed.
Actually npm folks themselves do not recommend npm for client side deployments.
From npm FAQ: “Use npm to manage dependencies in your dev environment, but not in your deployment scripts.”