Shaun Xu

The Sheep-Pen of the Shaun


News

logo

Shaun, the author of this blog is a semi-geek, clumsy developer, passionate speaker and incapable architect with about 10 years’ experience in .NET and JavaScript. He hopes to prove that software development is art rather than manufacturing. He's into cloud computing platform and technologies (Windows Azure, Amazon and Aliyun) and right now, Shaun is being attracted by JavaScript (Angular.js and Node.js) and he likes it.

Shaun is working at Worktile Inc. as the chief architect for overall design and develop worktile, a web-based collaboration and task management tool, and lesschat, a real-time communication aggregation tool.

MVP

My Stats

  • Posts - 122
  • Comments - 622
  • Trackbacks - 0

Tag Cloud


Recent Comments


Recent Posts


Archives


Post Categories


Image Galleries


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

    1, Generate <script> and <link> element in "index.html" page based on packages installed through Bower.

    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 <script> and <link> elements.

    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 <script> and <link> elements into the "index.html" file based on files detected by "main-bower-files".

       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 <script> elements in targeting file between comments

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

    and <link> elements between comments

       1: <!-- inject:css -->
       2: <!-- endinject -->

    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:     <!-- bower:css -->
       8:     <!-- <link> elements detected by bower will be here. -->
       9:     <!-- endinject -->
      10:  
      11:     <!-- inject:css -->
      12:     <!-- <link> elements specified in gulp will be here. -->
      13:     <!-- endinject -->
      14:  
      15:     <!-- bower:js -->
      16:     <!-- <script> elements detected by bower will be here. -->
      17:     <!-- endinject -->
      18:  
      19:     <!-- inject:js -->
      20:     <!-- <script> elements specified in gulp will be here. -->
      21:     <!-- endinject -->
      22: </head>

    I also specified "relevant: true" means the <script> and <link> elements will use relevant path.

    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:     <!-- bower:css -->
       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:     <!-- endinject -->
      11:  
      12:     <!-- inject:css -->
      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:     <!-- endinject -->
      21:  
      22:     <!-- bower:js -->
      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: </script>
       2:     <script src="bower_components/node-uuid/uuid.js?v=20150216161421">
       1: </script>
       2:     <script src="bower_components/signalr/jquery.signalR.js?v=20150216161421">
       1: </script>
       2:     <script src="bower_components/angular/angular.js?v=20150216161421">
       1: </script>
       2:     <script src="bower_components/angular-ui-router/release/angular-ui-router.js?v=20150216161421">
       1: </script>
       2:     <script src="bower_components/angular-cookies/angular-cookies.js?v=20150216161421">
       1: </script>
       2:     <script src="bower_components/angular-local-storage/dist/angular-local-storage.js?v=20150216161421">
       1: </script>
       2:     <script src="bower_components/angular-growl/build/angular-growl.js?v=20150216161421">
       1: </script>
       2:     <script src="bower_components/moment/moment.js?v=20150216161421">
       1: </script>
       2:     <!-- endinject -->
       3:  
       4:     <!-- inject:js -->
       5:     <script src="app.conf.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/module_1/module.conf.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/module_2/module.conf.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/module.conf.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/module_1/controllers.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/module_2/controllers.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/authorization.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/loadingIndicator.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/logger.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/message.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/security.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/signalr.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/utilities.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/wix.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/home/controller_home.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/login/controller_login.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/login/controller_session.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/validation/validation.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/task_status/task_status.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/view1/controller_view1.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/view2/controller_view2.js?v=20150216161421">
       1: </script>
       2:     <script src="modules/shared/widgets/serverTimeWidget.js?v=20150216161421">
       1: </script>
       2:     <script src="app.env.js?v=20150216161421">
       1: </script>
       2:     <script src="app.js?v=20150216161421">
       1: </script>
       2:     <script src="js/search.js?v=20150216161421">
       1: </script>
       2:     <script src="js/layout.js?v=20150216161421">
    </script>
      24:     <!-- endinject -->
      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 <script>, <link> elements are generated into this file stream in previous step. Then I need to save the file content and I need rename to "index.html" which is done by this plugin.

       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 gulp-minify-css

    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: });

     

    gulp-preprocess

    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.

    Comments

    Gravatar # re: 10 Awesome Gulp Plugins Working with AngularJS and Bower
    Posted by Yngve Bakken-Nilsen on 2/18/2015 3:20 AM
    You forgot ng-annotate!
    Gravatar # re: 10 Awesome Gulp Plugins Working with AngularJS and Bower
    Posted by Carlos Castillo on 10/16/2015 5:26 PM
    In gulpInjectVersioningTranform function, variable "version" is undefined. Where do you initialize or set it?

    Thanks in advance.
    Gravatar # re: 10 Awesome Gulp Plugins Working with AngularJS and Bower
    Posted by Pavlo Liulia on 11/10/2015 3:24 PM
    Carlos Castillo, i think the best option is to get it from your bower.json or package.json
    Gravatar # re: 10 Awesome Gulp Plugins Working with AngularJS and Bower
    Posted by Freya Emilia on 4/10/2018 5:27 PM
    If you are scanning or printing out your important documents but experiencing issues with your HP Printer? No Problem The organization has a profound well of assets, including web-based social networking, HP Support Assistant and the great, antiquated telephone, to enable you to locate the proper arrangement. We tried every one of the technical support assets to support your printer remotely with our experts Call HP TOLL FREE - +18885439946
    https://www.hptollfree.com/
    Post A Comment
    Title:
    Name:
    Email:
    Comment:
    Verification: