Gulp is a build tool used by the Angular team and many other professional engineers. Speed and simplicity are touted as benefits over Grunt. I took another look and attempted to build a simple SPA Angular application using GULP. Here is my story. Install Gulp So install GULP — . And create a gulpfile.js: link var gulp = require('gulp'); gulp.task('default', function() {// place code for your default task here}); Now run it and it should do nothing. Eh Voila: Genes-MacBook-Pro-2:gulp_plunge thinkjones$ gulp[18:18:56] Using gulpfile ~/dev/GeneConroyJonesArticles/gulp_plunge/gulpfile.js[18:18:56] Starting 'default'...[18:18:56] Finished 'default' after 93 μsGenes-MacBook-Pro-2:gulp_plunge thinkjones$ So far so good. Directory Structure So for this app we will be transforming files in the directory and copying and transforming them over to client dist. # Dir Structure\client\ # Source Files\client\index.html \dist\ # Dist or converted files. Copying files with “ gulp.dest ” Our first GULP task is to copy index.html to the dist folder. To do this we can use . A built in Gulp command. gulp.dest gulp.task('default', () {gulp.src('./client/index.html').pipe(gulp.dest('./dist/index.html'))}); function Then run gulp This looks good right? WRONG What this will actually do is copy items to a folder /dist/index.html/index.html Gulp.dest needs a folder as a destination. What you really need is: gulp.task('default', () {gulp.src('./client/index.html').pipe(gulp.dest('./dist'))}); function However if you try to run that next you will get a directory permissions error. events.js:160throw er; // Unhandled 'error' event^ Error: EISDIR: illegal operation on a directory, open '/Users/thinkjones/dev/GeneConroyJonesArticles/gulp_plunge/dist/index.html'Genes-MacBook-Pro-2:gulp_plunge thinkjones$ This arises from not the directory before running. A quick Google search later, results in Gulp 4 needing to be installed for and installed via The new : cleaning gulp.series del npm. gulpfile.js gulp = require('gulp'); del = require('del'); var var gulp.task('default', gulp.series(clean, copyIndex)); clean(done) {del(['dist']);done(); # Async callback for completion.} function copyIndex(done) { gulp.src('./client/index.html').pipe(gulp.dest('./dist', {overwrite: }));} function return true What is nice about GULP is that you can modularise the tasks fairly nicely into functions. Now running gulp gives us: successfully Genes-MacBook-Pro-2:gulp_plunge thinkjones$ gulp[18:53:32] Using gulpfile ~/dev/GeneConroyJonesArticles/gulp_plunge/gulpfile.js[18:53:32] Starting 'default'...[18:53:32] Starting 'clean'...[18:53:33] Finished 'clean' after 6.5 ms[18:53:33] Starting 'copyIndex'...[18:53:33] Finished 'copyIndex' after 28 ms[18:53:33] Finished 'default' after 37 ms Nice output. Let’s check the directory: Perfect. Let’s add an Angular application in there. Add Angular App appears to becoming less favorable it seems unnecessary since we already have Let’s install Angular using NPM. Bower npm. npm install angular # installs to ./node_modules/angular.js Let’s refer to that file in our index.html file. We’ll change this later (maybe) but let’s assume we are doing that for now. <!DOCTYPE html><html><head><title>Gulp Plunge</title> <!-- Vendor Files --> <script src="/node\_modules/angular/angular.js"></script> <!-- App Files --> <script src="/app/app.js"></script> </head><body ng-app="app"></body></html> Now let’s serve this file and see what it looks like: npm install --save-dev http-server We know this will fail because we have added an app.js and have not copied that over to the dist directory. So I added a new copyAppJs function: gulp = require('gulp'); del = require('del'); var var gulp.task('default', gulp.series(clean, copyIndex, copyAppJs)); clean(done) {del(['dist/**/*.*']);done();} function copyIndex(done) { gulp.src('./client/index.html').pipe(gulp.dest('./dist', {overwrite: }));done();} function return true copyAppJs(done) { gulp.src('./client/**/*.js').pipe(gulp.dest('./dist', {overwrite: }));done();} function return true Problem solved after re-running GULP. But there is still an Angular issue: Folder node_modules is in the root directory so it will need copying to dist also. Since this involves a code let’s take a look at a potential pattern for adding vendor assets. vendor Adding vendor assets with GULP. With large applications you will want to separate code from your application code. The reason being when dev-working your app code will change much more often than vendor code, so in order to maintain the speed, it benefits you not to compile everything, but just the assets within your application. In addition Vendor code is added slightly differently. Whereas you will probably include everything in your folder you will probably only include certain files from installs. Let’s see how we can manage that process using GULP. vendor gulp-watch app vendor Since every engineer loves DRY let’s specify vendor files in one place, then use them as needed to populate dist/index.html and any other dependent assets. Vendor source code will be pushed to /dist/vendor: copyVendor(done) { vendor_files = ['./node_modules/angular/angular.js']; gulp.src(vendor_files).pipe(gulp.dest('./dist/vendor', {overwrite: }));} function var return true Which copies ‘./node_modules/angular/angular.js’ to /vendor/angular.js On the surface this looks good but it is missing the package folder. This could lead to conflicts or unintentional overwrites. What we really need is it to copy to: /dist/vendor/angular/angular.js To maintain the original directory structure or part of it whilst globbing you do you use the base option copyVendor(done) { vendor_files = ['./node_modules/angular/angular.js']; gulp.src(vendor_files, {base: './node_modules'}).pipe(gulp.dest('./dist/vendor', {overwrite: }));done();} function var return true Yay! Re-running gulp and refreshing the app results in an app which fully works. However one thing that is frustrating is having to re-run gulp each time. Let’s see how easy it is to get a watch going. Gulp-watch — Auto update your application code when making changes So lets add a new function and assign it to a task: watchAppJs(done) { gulp.watch('./client/**/*.*', gulp.series(clean, copyIndex, copyAppJs, copyVendor));} function return gulp.task('watch', gulp.series(watchAppJs)); Now when I edit app.js. I see this in the terminal: [19:42:40] Using gulpfile ~/dev/GeneConroyJonesArticles/gulp_plunge/gulpfile.js[19:42:40] Starting 'watch'...[19:42:40] Starting 'watchAppJs'...[19:42:52] Starting 'clean'...[19:42:52] Finished 'clean' after 4.98 ms[19:42:52] Starting 'copyIndex'...[19:42:52] Finished 'copyIndex' after 45 ms[19:42:52] Starting 'copyAppJs'...[19:42:52] Finished 'copyAppJs' after 6.72 ms[19:42:52] Starting 'copyVendor'...[19:42:52] Finished 'copyVendor' after 20 ms That was easy. So what next? Ensuring index.html contains the latests vendor files. So what is really annoying me now is that I am maintaining vendor files in index.html: <head><title>Gulp Plunge</title> <!-- Vendor Files --> <script src="/vendor/angular/angular.js"></script> <!-- App Files --> <script src="/app/app.js"></script> </head> and gulpfile.js: copyVendor(done) { vendor_files = ['./node_modules/angular/angular.js']; gulp.src(vendor_files, {base: './node_modules'}).pipe(gulp.dest('./dist/vendor', {overwrite: }));} function var return true Since is the source of truth we want to auto populate with the correct dependent assets. Seems like a good candidate. Install: gulpfile.js dist/index.html gulp-inject npm install --save-dev gulp-inject Then modify the index.html to add template markers to specify where to write assets: <!-- Vendor Files --><!-- app:js --><!-- endinject --> Then tell GULP to inject: vendor_files = ['./node_modules/angular/angular.js']; var copyIndex(done) { sources = gulp.src(vendor_files, {read: }); gulp.src('./client/index.html').pipe(inject(sources, {name: 'app'})).pipe(gulp.dest('./dist', {overwrite: }));} function var false return true Now dist/index.html looks like this: <!-- Vendor Files --><!-- app:js --><script src="/node_modules/angular/angular.js"></script><!-- endinject --> Which is ok but again the files are pointing to node_modules. Our web server is serving them from ./vendor/angular/angular.js. Therefore we need to manipulate the files on the way through. We can do this with the and options on gulp-inject. can strip prefix folders and can add the new location in. ignorePath addPrefix ignorePath addPrefix Function copy-index now becomes: copyIndex(done) { sources = gulp.src(vendor_files, {read: }); gulp.src('./client/index.html').pipe(inject(sources, {name: 'app', ignorePath: 'node_modules', addPrefix: 'vendor' })).pipe(gulp.dest('./dist', {overwrite: }));} function var false return true And when gulp is run the vendor files are pointing to the correct dist location: <head><title>Gulp Plunge</title> <!-- Vendor Files --> <!-- app:js --> <script src="/vendor/angular/angular.js"></script> <!-- endinject --> <!-- App Files --> <script src="/app/app.js"></script> </head> The reason I like this approach is because in we are specifying the actual location of the files: gulpfile.js vendor_files = ['./node_modules/angular/angular.js']; var And when they are required to exist in a different location they are manipulated as needed. Code Located Here There are many other things to look at but so far I like what I see. In subsequent articles we shall take a look at: LESS compilation HTML Templating Minification and Versioning Automating karma configuration and unit testing.