Developing Software: Configure the MEAN stack files. Build System: Gulp

We know how to manage our dependencies, we know were to place our files, and once we know that, we need to build our application. But what does build our application exactly mean? We are developing a web application just with JavaScript, so we don’t need to compile our code, although there are several useful task we need to do to improve our development such as optimize, minify or obfuscate our code among others. To execute those tasks we can use Gulp.

After the execution of the ng-fullstack generator through Yeoman in previous posts, there are a lot of files we don’t know what they actually do. One of those files are gulpfile.babel.js. This is the Gulp configuration file. The configuration has been split in several files in order to have a clear configuration and avoid to have unique, large configuration file and difficult to maintain. The folder that contains all files needed by gulpfile.babel.js is under tasks folder as shown in the image below:

Blog004GulpTaskFolder

The folder is organized to contain the files needed for both client and server. Let’s see what kind of files are under client folder:

Blog005GulpClientTaskFolderTherefore, gulpfile.babel.js internally calls to index.js inside tasks folder and this file calls index.js under client folder. According to the documentation provided by Gulp,  the configuration file should be called gulpfile.js, but in our project it is called gulpfile.babel.js, but why? The reason for that change is because we are going to use ES6 (or ECMAScript 6) to implement the different tasks involved in our project.

The Yeoman generator has included several useful tasks for us, so we don’t have to implement anything more. However, I’d like to explain how Gulp works using the files provided by the generator. Open gulpfile.babel.js with an Editor of your choice, it looks like follows:

require('require-dir')('tasks');

It has just one line, but this line makes much you can imagine. As I said previously, this line is invoking the index.js file under tasks folder. Let’s see its content:

import gulp from "gulp";
import {tasks} from "./client/const";

gulp.task("default", [tasks.CLIENT_WATCH]);

require("require-dir")("client");
require("require-dir")("server");

First of all, we are importing the needed modules to develop our different tasks (lines 1-2). The import sentence allows us to include all the content of a module into an object that it is installed under the folder node_modules inside the root path of our project like in line 1 (that module has been installed using npm previously) or, as in line 2, import a member of a module that it belongs to our source code. The string in curly braces is a member of the module given after the from keyword.

We have imported all the functionality that Gulp provide us into the object called gulp. In line 4 we are creating a default task passing its name as the first argument of the function, and an array of tasks to be executed and completed before our task runs. The last argument is supposed to be a function that execute the code of our custom task. This is not the case, the sentence just create a default task and it will execute the task passed into the array of the second argument of the function. For further information, just read Gulp API documentation.

Lines 6 and 7, are sentences provided by Node.js and they work as an import loading external libraries or modules. The first string in parentheses refers to the name of the module that our script is requiring. The second parenthesis allows us to pass an initialization argument because the require-dir module exports a function with a parameter.

As we have seen in line 2, we are importing a file of constants: /client/const.js

export const base = {
    ROOT: "./",
    DEV: "./client/dev/",
    DIST: "./client/dist/",
    TEST: "./tests/"
}

export const tasks = {
    CLIENT_BUILD_DEV: "client.build:dev",
    CLIENT_BUILD_DIST: "client.build:dist",
    CLIENT_BUILD_CSS_DIST: "client.build_css:dist",
    CLIENT_JS_DIST: "client.build_js:dist",
    CLIENT_VIEWS_DIST: "client.views:dist",
    CLIENT_IMAGE_DIST: "client.imgs:dist",
    CLIENT_DEL_DIST: "client.del:dist",
    CLIENT_COPY: "client.copy",
    CLIENT_UNIT_TEST: "client.unit_test",
    CLIENT_COVERAGE: "client.coverage",
    CLIENT_RELOAD: "client.reload",
    CLIENT_WATCH: "client.watch",
    CLIENT_BUILD_TS: "client.build_ts",
    CLIENT_COMPILE_TO_CSS: "client.compile_from_sass_to_css:dev"
}

This file just export some constant values. Those values are names for different tasks placed in different files. Pay attention to line 20, it its the constant used in the previous file (./tasks/index.js): CLIENT_WATCH = "client.watch". It gives us a clue about where that task is implemented. If we look at our task folder structure there are two subfolders: client and server. client folder contains a file named watch.js, so the implementation of the "client.watch" task will probably be in that file:

import gulp from "gulp";
import Server from "aliv";
import {base, tasks} from "./const";

let aliv = new Server({
    watch: false,
    root: process.cwd()
});

gulp.task(tasks.CLIENT_RELOAD, () => {
    return aliv.reload();
});

gulp.task(tasks.CLIENT_WATCH, [tasks.CLIENT_BUILD_TS, tasks.CLIENT_COMPILE_TO_CSS ], () => {
    aliv.start();
    let _watchable = [];

    _watchable.push(base.DEV + "**/.ts");
    _watchable.push(base.DEV + "**/*.css");
    _watchable.push(base.DEV + "**/*.html");
    _watchable.push(base.DEV + "**/*.{sass,scss}");

    return gulp.watch(_watchable, [
        tasks.CLIENT_BUILD_TS,
        tasks.CLIENT_COMPILE_TO_CSS,
        tasks.CLIENT_RELOAD,
    ]);
});

Indeed, line 14 begins a new Gulp task which name is "client.watch". The code is not showing the real name of the task, it is using the constant file showed before, so the real value of tasks.CLIENT_WATCH is "client.watch". But, what else does this file do? Lines 1 to 3 import useful modules. Once again we import the Gulp module. We can find a new module called Aliv, and finally the constants file. We are importing the two exported members of const.js file: base and tasks. Aliv help us to reload our server when new changes are made in the client code. The configuration for Aliv are between lines  5 and 8.

We have two tasks: tasks.CLIENT_RELOAD, and tasks.CLIENT_WATCH. The first task, which real name is "client.reload", reloads the browsers to see the changes in the front-end when we save a file (it performs a reload operation over the opened browsers, instead of having to do it manually). The second task, "client.watch", first of all execute the tasks in the array of the second argument, tasks.CLIENT_BUILD_TS, and tasks.CLIENT_COMPILE_TO_CSS, and then execute the code in the function of the third argument. That function starts the server, and create different watcher for different files.

Let’s see the tasks.CLIENT_WATCH task in depth:

gulp.task(tasks.CLIENT_WATCH, [tasks.CLIENT_BUILD_TS, tasks.CLIENT_COMPILE_TO_CSS ], () => {
    aliv.start();
    let _watchable = [];

    _watchable.push(base.DEV + "**/.ts");
    _watchable.push(base.DEV + "**/*.css");
    _watchable.push(base.DEV + "**/*.html");
    _watchable.push(base.DEV + "**/*.{sass,scss}");

    return gulp.watch(_watchable, [
        tasks.CLIENT_BUILD_TS,
        tasks.CLIENT_COMPILE_TO_CSS,
        tasks.CLIENT_RELOAD,
    ]);
});

The watch function observes a single or an array of globs that indicate what files to watch for changes. We are building an array of files using the _watchable vairable and pushing in it the path of the files to watch: all of them which extension would be *.ts, *.css, *.htm, *.sass and *.scss inside the specified folders. When an observed file changes its content, a task (or a callback function) is executed to perform any operation. Therefore, the return statement of the function create an observer for all the files contained in the _watchable array and, if one of then changes, three tasks will be executed: tasks.CLIENT_BUILD_TS, tasks.CLIENT_COMPILE_TO_CSS and tasks.CLIENT_RELOAD.

As we have seen previously, there are several files that contains all the tasks to be executed by Gulp when we launch the appropriate command. I’m not going to explain of all them because otherwise the post would be eternal. I’m going to explain what we can find in each file:

  • ./tasks/client/build_css.js: this file includes tasks to compile all *.sass and *.scss files into plain CSS files, and a task to minify all CSS files into one which it will be used in the final app distribution. Gulp will create a distribution folder where write all the final files.
  • ./tasks/client/build_html.js: it contains just a task to create a minified version of all *.html files to use them later in the final app distribution.
  • ./tasks/client/build_image.js: this task will create compressed images.
  • ./tasks/client/build_js.js: a task to uglify all *.js files.
  • ./tasks/client/build_ts.js: it is not a mistake, this line is not equal to the previous line 😀 pay attention to the name of the .js file. This file executes a task to compile all TypeScript files (*.ts) into JavaScript files (*.js).
  • ./tasks/client/const.js: we have talk about this file a few lines above. It is a file that contains constants with the name of the tasks in addition to relative paths of the project.
  • ./tasks/client/copy.js: it copies all files in the development folder to the distribution folder.
  • ./tasks/client/del.js: it deletes synchronously the distribution folder.
  • ./tasks/client/indes.js: it is the entry point from the index.js in the ./tasks/client/ path.
  • ./tasks/client/test.js: it starts the execution of the unit tests through Karma performing a coverage of the results.
  • ./tasks/client/watch.js: it is the above-mentioned file. Nothing else to add.
  • ./tasks/server/index.js: again, it is an entry point from the index.js file in the upper path.
  • ./tasks/server/build_tsc.js: it compiles all TypeScript files (*.ts) into JavaScript files (*.js).
  • ./taskt/server/test.js: it stats the execution of the unit tests of the server side.

That is all. Thanks to these tasks we have been able to automate and optimize our project. We have automated the process of execute the server side of the project and generate a distribution of client side that it will be executed through the server side. Besides, we can execute any of these tasks with a single command line, taking the name of the task as an argument:

gulp client.build:dist
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s