Overview



In Getting Started with Universal (Web + Mobile) Application in Angular 2 article we saw how to create a Universal application in Angular 2. However, the build process was manual where we renamed files for mobile and web development build process. Also, we talked about automating the build process to avoid manual steps.

In this article, we will automate our Universal application build process to build the platform (web/mobile) specific code.

Pre-Requisites

Node JS

Gulp – Gulp is a JavaScript task runner that lets you automate tasks such as – Bundling and minifying libraries and style sheets.

Getting Ready

Step 1: Clone Universal app codebase from Github.

Step 2: Run the following command to verify that the application is running and you see the following browser output:

ionic serve 1 ionic serve

Excluding web/mobile specific code from build

Ionic internally uses gulp task to build & minify HTML/JS/Fonts and SCSS files and gulp has different tasks to minify HTML/JS/Fonts and SCSS.

Open the gulp.js file and follow these steps:

Step 1: Adding a parameter in build command.

Before we exclude web/mobile specific code from build process, we need to identify either we are building the app for mobile or web.

Let us pass a parameter to watch/build mobile-specific code by running the following command:

ionic build –mobile 1 ionic build – mobile

Read the above mobile parameter value in the gulp.js file with the help of process.argv:

var isMobile = argv.indexOf('--mobile') > -1 1 var isMobile = argv . indexOf ( '--mobile' ) > - 1

Step 2: Excluding Web/Mobile Specific TypeScript/JavaScript files from the build.

gulp-browserify-typescript module is responsible to provide TypeScript/JavaScript build options. You can find it under node_module/ gulp-browserify-typescript.

Include the dependency of gulp-browserify-typescript:

browserify = require('browserify'), watchify = require('watchify'), tsify = require('tsify'), pretty = require('prettysize'), merge = require('lodash.merge'), source = require('vinyl-source-stream'), buffer = require('vinyl-buffer'), sourcemaps = require('gulp-sourcemaps'), uglify = require('gulp-uglify'), stream = require('stream'); 1 2 3 4 5 6 7 8 9 10 browserify = require ( 'browserify' ) , watchify = require ( 'watchify' ) , tsify = require ( 'tsify' ) , pretty = require ( 'prettysize' ) , merge = require ( 'lodash.merge' ) , source = require ( 'vinyl-source-stream' ) , buffer = require ( 'vinyl-buffer' ) , sourcemaps = require ( 'gulp-sourcemaps' ) , uglify = require ( 'gulp-uglify' ) , stream = require ( 'stream' ) ;

You may refer to NPM page for the complete list of dependencies.

Override default options and function for gulp-browserify-typescript and set default app.ts file for web/mobile:

defaultOptions src config, by default, takes the app.ts file in the build process. We will use app.mobile.ts in the case of mobile build, as shown below:

var defaultOptions = { watch: false, src: ((isMobile) ? ['./app/app.mobile.ts', './typings/index.d.ts'] : ['./app/app.ts', './typings/index.d.ts']), outputPath: 'www/build/js/', outputFile: 'app.bundle.js', minify: false, browserifyOptions: { cache: {}, packageCache: {}, debug: true }, watchifyOptions: {}, tsifyOptions: {}, uglifyOptions: {}, onError: function(err){ console.error(err.toString()); this.emit('end'); }, onLog: function(log){ console.log((log = log.split(' '), log[0] = pretty(log[0]), log.join(' '))); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var defaultOptions = { watch : false , src : ( ( isMobile ) ? [ './app/app.mobile.ts' , './typings/index.d.ts' ] : [ './app/app.ts' , './typings/index.d.ts' ] ) , outputPath : 'www/build/js/' , outputFile : 'app.bundle.js' , minify : false , browserifyOptions : { cache : { } , packageCache : { } , debug : true } , watchifyOptions : { } , tsifyOptions : { } , uglifyOptions : { } , onError : function ( err ) { console . error ( err . toString ( ) ) ; this . emit ( 'end' ) ; } , onLog : function ( log ) { console . log ( ( log = log . split ( ' ' ) , log [ 0 ] = pretty ( log [ 0 ] ) , log . join ( ' ' ) ) ) ; } }

The only change that we had to make is the src config value. We don’t have to touch other configs and their values.

buildBrowserify function override:

var buildBrowserify = function(options) { <strong>options = merge(defaultOptions, options);</strong> var b = browserify(options.src, options.browserifyOptions) .plugin(tsify, options.tsifyOptions); if (options.watch) { b = watchify(b, options.watchifyOptions); b.on('update', bundle); b.on('log', options.onLog); } return bundle(); function bundle() { var debug = options.browserifyOptions.debug; return b.bundle() .on('error', options.onError) .pipe(source(options.outputFile)) .pipe(buffer()) .pipe(debug ? sourcemaps.init({ loadMaps: true }) : noop()) .pipe(options.minify ? uglify(options.uglifyOptions) : noop()) .pipe(debug ? sourcemaps.write('./',{includeContent:true, sourceRoot:'../../../'}) : noop()) .pipe(gulp.dest(options.outputPath)); } function noop(){ return new stream.PassThrough({ objectMode: true }); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 var buildBrowserify = function ( options ) { < strong > options = merge ( defaultOptions , options ) ; < / strong > var b = browserify ( options . src , options . browserifyOptions ) . plugin ( tsify , options . tsifyOptions ) ; if ( options . watch ) { b = watchify ( b , options . watchifyOptions ) ; b . on ( 'update' , bundle ) ; b . on ( 'log' , options . onLog ) ; } return bundle ( ) ; function bundle ( ) { var debug = options . browserifyOptions . debug ; return b . bundle ( ) . on ( 'error' , options . onError ) . pipe ( source ( options . outputFile ) ) . pipe ( buffer ( ) ) . pipe ( debug ? sourcemaps . init ( { loadMaps : true } ) : noop ( ) ) . pipe ( options . minify ? uglify ( options . uglifyOptions ) : noop ( ) ) . pipe ( debug ? sourcemaps . write ( './' , { includeContent : true , sourceRoot : '../../../' } ) : noop ( ) ) . pipe ( gulp . dest ( options . outputPath ) ) ; } function noop ( ) { return new stream . PassThrough ( { objectMode : true } ) ; } }

The only change that we had to make is to use defaultOptions to construct the final set of options. Rest of the code is from the standard buildBrowserify method definition.

Step 3: Excluding HTML files from the build.

copyHTML module is responsible to provide HTML build options. It comes with node module dependency. It has src config for the HTML files and the build output config. We can find it under node_modules/ionic-gulp-html-copy.

Open gulp.js file and add the following code to override copyHTML function:

var copyHTML = function(options) { //options.src = options.src || 'app/**/*.html'; options.dest = options.dest || 'www/build'; /*Excluding the web/mobile code*/ options.src = ((isMobile) ? ['app/**/*.html', '!app/mobile', '!app/mobile/**'] : ['app/**/*.html', '!app/web', '!app/web/**']); return gulp.src(options.src) .pipe(gulp.dest(options.dest)); } 1 2 3 4 5 6 7 8 9 var copyHTML = function ( options ) { //options.src = options.src || 'app/**/*.html'; options . dest = options . dest || 'www/build' ; /*Excluding the web/mobile code*/ options . src = ( ( isMobile ) ? [ 'app/**/*.html' , '!app/mobile' , '!app/mobile/**' ] : [ 'app/**/*.html' , '!app/web' , '!app/web/**' ] ) ; return gulp . src ( options . src ) . pipe ( gulp . dest ( options . dest ) ) ; }

We have excluded web/mobile folder based on –mobile build parameter.

Running Build Commands

Run the following command to build web-specific code:

ionic build 1 ionic build

Verify build folder under www/build and you will not see the mobile specific code, as shown below:

Run the following command to build mobile-specific code :

ionic build –mobile 1 ionic build – mobile

Verify build folder under www/build and you will not see the web-specific code, as shown below:

So, now we have our build customized for Universal Angular 2 application and based on the Web or Mobile options, we can create a corresponding deployable package. In this article the default option (i.e. if you don’t mention anything) is for the Web.

How to get the code

You can download ng2_universal_app demo application from GitHub this contains the updated gulp.js.

Summary

In this article, we learned how to build Universal Application (Web+Mobile) in Angular 2 using Ionic 2/NodeJS and Gulp. Hope you found this helpful!

At WalkingTree we build hybrid applications using Ext JS and Angular.js and native application using Xamarin. We will be happy to respond to your queries and help you in taking advantage of great work done by the respective framework teams.

References