10 Awesome Gulp Plugins Working with AngularJS and Bower

Below are some gulp plugins I'm using in my Angular.JS website for build and deployment. Basically what I need are

1, Generate

2, Generate <script> elements for all Angular.JS JavaScript files we wrote.

3, Generate configuration file based on environment variants.

4, Combine and minify (except those had been minified) JavaScript and CSS files in release mode, but NOT in debug mode.

Now let's go though gulp plugins I'm using one by one.

main-bower-files

This plugin loads the "bower.json" file of my application, retrieve files for each packages based on the "main" property defined in its own "bower.json", for future usage. So if I have packages installed through the command "bower install [package-name] --save", then I can retrieve the files it needs into my gulp task, and pipe to the next step, for example generate

I can specify where the "bower.json" for my project was located through "{path: 'app'}", also not let the plugin read the file content by using "{read: false}" if I don't need to deal with the files' content.

1: var gulp = require('gulp');

2: var bower = require('main-bower-files');

3: 

4: gulp.task('TASKNAME', function {

5: return gulp.src(bower({ paths: 'app' }), { read: false }))

6:.pipe(/* next step */)

7: });

In some cases we need to specify which files should be referenced in a package. For example, by default, only "jquery.js" is necessary for jQuery package. But if we want to use "jquery.min.js" as well as "jquery.min.map", we can override it in our project level "bower.json" through its "overrides" property, as below.

1: {

2: "name": "app",

3: "main": "app.js",

4: "version": "0.0.0",

5: "ignore": [

6: "**/.*",

7: "node_modules",

8: "bower_components",

9: "test",

10: "tests"

11: ],

12: "dependencies": {

13: "jquery": "~2.1.3",

14: "bootstrap": "~3.3.1",

15: "node-uuid": "~1.4.2",

16: "signalr": "~2.2.0",

17: "angular": "~1.3.9",

18: "angular-ui-router": "~0.2.13"

19: "angular-growl": "~0.4.0",

20: "moment": "~2.9.0",

21: "fontawesome": "~4.3.0"

22: },

23: "overrides": {

24: "jquery": {

25: "main": [

26: "dist/jquery.min.js",

27: "dist/jquery.min.map"

28: ]

29: }

30: }

31: }

gulp-inject

This plugin reads the file source, transforms each of them to a string and injects into placeholders in the target stream files, such as an HTML file. I used it to generate

1: var gulp = require('gulp');

2: var bower = require('main-bower-files');

3: var inject = require('gulp-inject');

4: 

5: gulp.task('TASKNAME', function {

6: return gulp.src('index.tpl.html')

7:.pipe(inject(

8: gulp.src(bower({ paths: 'app' }), { read: false }),

9: { name: 'bower', relative: true, transform: gulpInjectVersioningTranform }))

10:.pipe(inject(

11: gulp.src(javaScriptFiles, { read: false }),

12: { relative: true, transform: gulpInjectVersioningTranform }))

13:.pipe(inject(

14: gulp.src(cssFiles, { read: false }),

15: { relative: true, transform: gulpInjectVersioningTranform }))

16:.pipe(/* next step */);

17: });

By default, gulp-inject will generate

1: <!-- inject:js -->

2:

and elements between comments

1:

2:

But we can specify more targeting placeholders in gulp-inject name property. In the code above, JavaScript and CSS elements will be generated to the placeholders named "bower", while others will go default. Then the "index.tpl.html" would be like this.

1: <head lang="en">

2: <meta charset="UTF-8">

3: <meta http-equiv="X-UA-Compatible" content="IE=edge">

4: <title></title>

5: <base href="/">

6: 

7:

8:

9:

10: 

11:

12:

13:

14: 

15:

16:

17:

18: 

19:

20:

21:

22: </head>

I also specified "relevant: true" means the

And in order to add timestamp suffixing for each elements, I specified the transform function of the inject plugin. The function is very simple.

1: var gulpInjectVersioningTranform = function (filepath, i, length, sourceFile, targetFile) {

2: var extname = path.extname(filepath);

3: if (extname === '.js' || extname === '.css') {

4: filepath += '?v=' + version;

5: return inject.transform.apply(inject.transform, [filepath, i, length, sourceFile, targetFile]);

6: }

7: else {

8: return inject.transform.apply(inject.transform, arguments);

9: }

10: };

With these settings the output file content would be like this.

1: <head lang="en">

2: <meta charset="UTF-8">

3: <meta http-equiv="X-UA-Compatible" content="IE=edge">

4: <title></title>

5: <base href="/">

6: 

7:

8: <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.css?v=20150216161421">

9: <link rel="stylesheet" href="bower_components/fontawesome/css/font-awesome.css?v=20150216161421">

10:

11: 

12:

13: <link rel="stylesheet" href="styles/kendo.common-bootstrap.min.css?v=20150216161421">

14: <link rel="stylesheet" href="styles/kendo.bootstrap.min.css?v=20150216161421">

15: <link rel="stylesheet" href="styles/app.css?v=20150216161421">

16: <link rel="stylesheet" href="modules/module_1/k1.css?v=20150216161421">

17: <link rel="stylesheet" href="modules/shared/style.css?v=20150216161421">

18: <link rel="stylesheet" href="modules/shared/login/login.css?v=20150216161421">

19: <link rel="stylesheet" href="modules/shared/validation/validation.css?v=20150216161421">

20:

21: 

22:

23: <script src="bower_components/jquery/dist/jquery.js?v=20150216161421"></script>

1: 

2: <script src="bower_components/bootstrap/dist/js/bootstrap.js?v=20150216161421">

1:

2: <script src="bower_components/node-uuid/uuid.js?v=20150216161421">

1:

2: <script src="bower_components/signalr/jquery.signalR.js?v=20150216161421">

1:

2: <script src="bower_components/angular/angular.js?v=20150216161421">

1:

2: <script src="bower_components/angular-ui-router/release/angular-ui-router.js?v=20150216161421">

1:

2: <script src="bower_components/angular-cookies/angular-cookies.js?v=20150216161421">

1:

2: <script src="bower_components/angular-local-storage/dist/angular-local-storage.js?v=20150216161421">

1:

2: <script src="bower_components/angular-growl/build/angular-growl.js?v=20150216161421">

1:

2: <script src="bower_components/moment/moment.js?v=20150216161421">

1:

2:

3: 

4:

5: <script src="app.conf.js?v=20150216161421">

1:

2: <script src="modules/module_1/module.conf.js?v=20150216161421">

1:

2: <script src="modules/module_2/module.conf.js?v=20150216161421">

1:

2: <script src="modules/shared/module.conf.js?v=20150216161421">

1:

2: <script src="modules/module_1/controllers.js?v=20150216161421">

1:

2: <script src="modules/module_2/controllers.js?v=20150216161421">

1:

2: <script src="modules/shared/authorization.js?v=20150216161421">

1:

2: <script src="modules/shared/loadingIndicator.js?v=20150216161421">

1:

2: <script src="modules/shared/logger.js?v=20150216161421">

1:

2: <script src="modules/shared/message.js?v=20150216161421">

1:

2: <script src="modules/shared/security.js?v=20150216161421">

1:

2: <script src="modules/shared/signalr.js?v=20150216161421">

1:

2: <script src="modules/shared/utilities.js?v=20150216161421">

1:

2: <script src="modules/shared/wix.js?v=20150216161421">

1:

2: <script src="modules/shared/home/controller_home.js?v=20150216161421">

1:

2: <script src="modules/shared/login/controller_login.js?v=20150216161421">

1:

2: <script src="modules/shared/login/controller_session.js?v=20150216161421">

1:

2: <script src="modules/shared/validation/validation.js?v=20150216161421">

1:

2: <script src="modules/shared/task_status/task_status.js?v=20150216161421">

1:

2: <script src="modules/shared/view1/controller_view1.js?v=20150216161421">

1:

2: <script src="modules/shared/view2/controller_view2.js?v=20150216161421">

1:

2: <script src="modules/shared/widgets/serverTimeWidget.js?v=20150216161421">

1:

2: <script src="app.env.js?v=20150216161421">

1:

2: <script src="app.js?v=20150216161421">

1:

2: <script src="js/search.js?v=20150216161421">

1:

2: <script src="js/layout.js?v=20150216161421">

</script>

24:

25: </head>

gulp-rename

This plugin is very simple, it's to be used to rename and file. In my project I have a template of "index.html" named "index.tpl.html" and

1: var gulp = require('gulp');

2: var bower = require('main-bower-files');

3: var inject = require('gulp-inject');

4: var rename = require('gulp-rename');

5: 

6: gulp.task('TASKNAME', function {

7: return gulp.src('index.tpl.html')

8:.pipe(inject(

9: gulp.src(bower({ paths: 'app' }), { read: false }),

10: { name: 'bower', relative: true, transform: gulpInjectVersioningTranform }))

11:.pipe(inject(

12: gulp.src(javaScriptFiles, { read: false }),

13: { relative: true, transform: gulpInjectVersioningTranform }))

14:.pipe(inject(

15: gulp.src(cssFiles, { read: false }),

16: { relative: true, transform: gulpInjectVersioningTranform }))

17:.pipe(rename(target))

18:.pipe(gulp.dest('app'));

19: });

gulp-chmod

When we are working with some version control system, for example Team Foundation Server, if the workspace was set to Server Mode, all local files will be read-only. Then when using gulp-rename and dest function to write the output file, it will maintain read-only mode. This will make the second time we run gulp task failed since you cannot overwrite a read-only file.

In this case we need to use this plugin to change the file mode. It uses Linux "chmod" argument syntax. So if I want to remove the read-only flag, I need the code as below.

1: var gulp = require('gulp');

2: var bower = require('main-bower-files');

3: var inject = require('gulp-inject');

4: var rename = require('gulp-rename');

5: var chmod = require('gulp-chmod');

6: 

7: gulp.task('TASKNAME', function {

8: return gulp.src('index.tpl.html')

9:.pipe(/* load script and css files then inject */)

10:.pipe(rename(target))

11:.pipe(chmod(666))

12:.pipe(gulp.dest('app'));

13: });

gulp-concat, gulp-uglify and

In release build, I need to combine all JavaScript and CSS files, minify them and inject into "index.html". I used these 3 plugins for combination and minification.

1: var gulp = require('gulp');

2: var bower = require('main-bower-files');

3: var inject = require('gulp-inject');

4: var rename = require('gulp-rename');

5: var chmod = require('gulp-chmod');

6: var uglify = require('gulp-uglify');

7: var minifyCSS = require('gulp-minify-css');

8: 

9: gulp.task('TASKNAME1', function {

10: return gulp.src(javaScriptFiles)

11:.pipe(uglify)

12:.pipe(concat('app.min.js'))

13:.pipe(chmod(666))

14:.pipe(gulp.dest(build + '/js'));

15: });

16: 

17: gulp.task('TASKNAME2', function {

18: return gulp.src(cssFiles)

19:.pipe(minifyCSS)

20:.pipe(concat('app.min.css'))

21:.pipe(chmod(666))

22:.pipe(gulp.dest(build + '/css'));

23: });

gulp-filter

The code works well for JavaScript and CSS files we created, but didn't work for files installed through bower. Since the files detected by "main-bower-file" includes JavaScript and CSS files, we need to somehow filter them and ran "gulp-uglify" and "gulp-minifyCSS".

"gulp-filter" enables us to work based on a subset of the original files by filtering them using globbing. Now we can get all JavaScript files from "main-bower-file", by specifying "**/*.js" into "gulp-filter", and pipe "gulp-uglify", while "**/*.css" and pipe "gulp-minifyCSS".

1: var gulp = require('gulp');

2: var bower = require('main-bower-files');

3: var inject = require('gulp-inject');

4: var rename = require('gulp-rename');

5: var chmod = require('gulp-chmod');

6: var uglify = require('gulp-uglify');

7: var minifyCSS = require('gulp-minify-css');

8: ar filter = require('gulp-filter');

9: 

10: gulp.task('TASKNAME1', function {

11: return gulp.src(bower({ paths: 'app' }))

12:.pipe(filter('**/*.js'))

13:.pipe(uglify)

14:.pipe(concat('bower.min.js'))

15:.pipe(chmod(666))

16:.pipe(gulp.dest('.build/js'));

17: });

18: 

19: gulp.task('TASKNAME2', function {

20: return gulp.src(bower({ paths: 'app' }))

21:.pipe(filter('**/*.css'))

22:.pipe(minifyCSS)

23:.pipe(concat('bower.min.css'))

24:.pipe(chmod(666))

25:.pipe(gulp.dest('.build/css'));

26: });

gulp-if

Some bower package specifies original JavaScript and CSS files while some specified minified version. I don't want to re-minify those files in my gulp task. So I need to use "gulp-if" it filter then out.

"gulp-if" allows me to use a function to check input files, pipe plugins for those pass the condition check. In this case I tested files' name, and perform "gulp-uglify" or "gulp-minifyCSS" only if their extension name were not "min.js" or "min.css".

1: var gulp = require('gulp');

2: var bower = require('main-bower-files');

3: var inject = require('gulp-inject');

4: var rename = require('gulp-rename');

5: var chmod = require('gulp-chmod');

6: var uglify = require('gulp-uglify');

7: var minifyCSS = require('gulp-minify-css');

8: var filter = require('gulp-filter');

9: var gulpif = require('gulp-if');

10: 

11: var isNotMinified = function (file) {

12: var extname = path.extname(file.path);

13: if (extname === '.js' || extname === '.css') {

14: return path.extname(file.path.substr(0, file.path.length - extname.length))!== '.min';

15: }

16: else {

17: return false;

18: }

19: };

20: 

21: gulp.task('TASKNAME1', function {

22: return gulp.src(bower({ paths: 'app' }))

23:.pipe(filter('**/*.js'))

24:.pipe(gulpif(isNotMinified, uglify))

25:.pipe(concat('bower.min.js'))

26:.pipe(chmod(666))

27:.pipe(gulp.dest('.build/js'));

28: });

29: 

30: gulp.task('TASKNAME2', function {

31: return gulp.src(bower({ paths: 'app' }))

32:.pipe(filter('**/*.css'))

33:.pipe(gulpif(isNotMinified, minifyCSS))

34:.pipe(concat('bower.min.css'))

35:.pipe(chmod(666))

36:.pipe(gulp.dest('.build/css'));

37: });

In order to generate some configuration files based on the system environment variant, such as the WebAPI endpoint, protocol and debug flag, I need to use "gulp-preprocess".

1: var gulp = require('gulp');

2: var preprocess = require('gulp-preprocess');

3: 

4: gulp.task('app.env.js', function {

5: return gulp.src('app/app.env.tpl.js')

6:.pipe(preprocess)

7:.pipe(rename('app.env.js'))

8:.pipe(chmod(666))

9:.pipe(gulp.dest('app'));

10: });

The content of the template file "app.env.tpl.js" specified which environment variant should be replaced.

1: (function (window) {

2: angular.module('environment', [])

3: /* @ifdef DEBUG */

4:.value('debug', true)

5: /* @endif */

6:.factory('wixEndpoint', [ function {

7: var scheme = '/* @echo WIX_ENDPOINT_SCHEME */';

8: var address = '/* @echo WIX_ENDPOINT_ADDRESS */';

9: var port = '/* @echo WIX_ENDPOINT_PORT */';

10: return scheme + '://' + address + ':' + port;

11: }])

12:.factory('apiEndpoint', [ 'wixEndpoint', function (wixEndpoint) {

13: var api = '/* @echo WIX_ENDPOINT_API */';

14: return wixEndpoint + api;

15: }]);

16: })(window);

It will output debug value if DEBUG was specified in environment variant. It will also load values for WIX_ENDPOINT_SCHEME, WIX_ENDPOINT_ADDRESS, WIX_ENDPOINT_PORT and WIX_ENDPOINT_API from environment variant, and write the value into this file. So the result in one of my development lab would be like this.

1: (function (window) {

2: angular.module('environment', [])

3:.value('debug', true)

4:.factory('wixEndpoint', [ function {

5: var scheme = 'http';

6: var address = '10.222.115.220';

7: var port = '8080';

8: return scheme + '://' + address + ':' + port;

9: }])

10:.factory('apiEndpoint', [ 'wixEndpoint', function (wixEndpoint) {

11: var api = '/api';

12: return wixEndpoint + api;

13: }]);

14: })(window);

Hope this helps,

Shaun

All documents and related graphics, codes are provided "AS IS" without warranty of any kind. Copyright © Shaun Ziyan Xu. This work is licensed under the Creative Commons License.

This article is part of the GWB Archives. Original Author: Shaun

New on Geeks with Blogs