#### Limitless - Compiling SCSS

##### Using Gulp

##### Overview

Gulp is a very powerful yet simple toolkit. It allows you to automate SCSS compilation using configured `gulpfile.js` that comes in the package. If you know how to use it - navigate to template folder and start doing stuff, if you don't - here is a simple instruction how you can setup everything and get your tasks done right.

##### Step 1 - Install Node.js

First - we need our most important requirement, Node. To install Node - simply visit http://nodejs.org and then click that big green “Install” button. Once your download completes, run that application and you should be all ready to go. The Node installer also includes npm, which we will come back to a little later.

##### Step 2 - Command line

Now, you may not be very familiar with your command-line interface (Terminal for OSX, Command Prompt for Windows) but you should be! It may seem intimidating at first, but once you get the hang of it you will have the ability to run many different command line applications such as Sass, Yeoman and Git. All of which are very useful tools that your workflow could benefit from!

As a quick example, open up your command line and we will throw a couple commands at it to ensure that Node is properly installed. Type that and then hit enter and you should get a response on the next line with the version number of Node that you have installed:

    node -v

Now, let’s do the same for npm:

    npm -v

If you didn’t get a response, then it may mean that Node didn’t install correctly or you may need to restart your command line application. If this still isn’t working after restarting, then simply jump back up the top and try the first step again.

##### Step 3 - Install Gulp

You’ve met your command-line and you know how to talk to it - you even know your way around your file system. Now, it’s time to get to the good stuff. Let’s meet npm and install gulp!

NPM stands for Node Package Manager and it is a command line tool that will allow you to install additional Node packages to your projects. It even comes with a nifty site that allows you to browse and search through all of the available packages.

In your command-line application, type:

    sudo npm install -g gulp

Let’s quickly break this down.

1. sudo is the command to grant you administrator access so you can properly install files globally. It will prompt you for your computer password before it continues. This is usually needed for global installation, but should not be required when installing plugins in your local project directory.
2. npm is the application we are using to install our package.
3. We are running the install command on that application.
4. The -g is an optional flag used to signify that we want to install this package globally so that any project can use it.
5. And finally, gulp is the name of the package we would like to install.

Once that has run it’s course check your command-line to ensure that there are no error messages. If there are none to be seen, then congratulations! You just installed gulp! Just to double check, let’s refer back to our versioning commands we used above for Node and npm.

    gulp -v

Like before, this should return the version number on the next line of your command-line.

Next, we also need to install gulp locally.

    npm install --save-dev gulp

##### Step 4 - Navigate to your project directory

Now that we have met our command-line interface and know how to communicate with it, our next step will be navigating it. Luckily, it only takes two commands to change directories and take a look at what is inside them. These commands are `ls` (or `dir`, for Windows) to list what is in a directory and `cd` to change directories.

Once you are comfortable with the `ls` and `cd` commands, we need to navigate to our project folder. This will likely be different for each person, but as an example this is what I would type to navigate to my local project:

    // Path to Limitless layout folder
    cd [path to local web server]/Limitless/BS4/Template/

##### Step 5 - Running tasks

Once gulp is installed we have to give it some instruction so it knows what tasks for perform for us. But, first, we need to figure out exactly what tasks we need to run in our project.

Gulp plugins we need:

- gulp-postcss - tool for transforming styles
- autoprefixer - postCSS plugin that parses CSS and adds vendor prefixes to CSS rules
- gulp-jshint - lint JavaScript
- gulp-sass - compile SASS files
- gulp-clean-css - fast and efficient CSS optimizer
- gulp-concat - concatenate our JavaScript
- gulp-uglify - minify files
- gulp-rename - rename files
- gulp-rtlcss - generate RTL version of CSS files. Do not use it with LTR layout

Install required plugins:

    npm install gulp-postcss autoprefixer gulp-jshint jshint gulp-sass gulp-clean-css gulp-concat gulp-uglify gulp-rename gulp-rtlcss --save-dev

If you are getting permissions errors installing these plugins you may have some issues with the permissions in your local project directory. You can prepend those commands with sudo, but it is not advised as it can lead to more problematic issues later. The best course of action is to ensure the permissions of your directories are setup properly.

It’s very important to mention that gulp only has 5 methods. These methods are as follows: task, run, watch, src, and dest. These are all you will need to write your tasks.

###### Gulp file

    /* ------------------------------------------------------------------------------
     *
     *  # Gulp file
     *
     *  Gulp tasks for Limitless template
     *
     *  Includes following tasks:
     *  # gulp lint - lints core JS files, excluding libraries
     *  # gulp sass - compiles SCSS files. Depends on variables defined below
     *  # gulp watch - watches for changes in all SCSS files and automatically recompiles them
     *  # gulp default - runs default set of tasks. Configurable by user
     *  # gulp icons - compiles icon set
     *  # gulp ckeditor - compiles ckeditor
     *
     * ---------------------------------------------------------------------------- */

    // Configuration
    // ------------------------------

    // Define variables
    const layout = 'layout_1',      // 'layout_1', 'layout_2', 'layout_3', 'layout_4', 'layout_5'
        theme = 'default',          // 'default', 'material' or 'dark'
        direction = 'LTR',          // 'LTR' or 'RTL'
        type = 'full',              // 'full' or 'seed'
        iconset = 'icomoon';        // 'icomoon' (default), 'fontawesome', 'material'

    // Define plugins
    const { src, dest, watch, series } = require('gulp'),
        postcss = require('gulp-postcss'),
        autoprefixer = require('autoprefixer'),
        jshint = require('gulp-jshint'),
        compileSass = require('gulp-sass'),
        minifyCss = require('gulp-clean-css'),
        concat = require('gulp-concat'),
        uglify = require('gulp-uglify'),
        rename = require('gulp-rename'),
        rtlcss = require('gulp-rtlcss');

    // Setup tasks
    // ------------------------------

    // Lint
    function lint() {
        return src(layout + '/' + direction + '/' + theme + '/' + type + '/assets/js/*.js')
            .pipe(jshint())
            .pipe(jshint.reporter('default'));
    }

    //
    // SCSS compilation
    //

    // Autoprefixer config
    const processors = [
        autoprefixer({
            overrideBrowserslist: [
                '>= 1%',
                'last 1 major version',
                'Chrome >= 45',
                'Firefox >= 38',
                'Edge >= 12',
                'Explorer >= 10',
                'iOS >= 9',
                'Safari >= 9',
                'Android >= 4.4',
                'Opera >= 30'
            ],
            map: false
        })
    ];

    // Make it dynamic by changing core variables. Sensitive to location: make sure
    // the paths are correct if you need to use a custom assets location
    function sass() {
        if(direction == 'LTR') {
            return src('global_assets/scss/layouts/' + layout + '/' + theme + '/compile/*.scss')
                .pipe(compileSass())
                .pipe(postcss(processors))
                .pipe(dest(layout + '/' + direction + '/' + theme + '/' + type + '/assets/css'))
                .pipe(minifyCss({
                    level: {1: {specialComments: 0}}
                }))
                .pipe(rename({
                    suffix: ".min"
                }))
                .pipe(dest(layout + '/' + direction + '/' + theme + '/' + type + '/assets/css'));
        }
        else {
            return src('global_assets/scss/layouts/' + layout + '/' + theme + '/compile/*.scss')
                .pipe(compileSass())
                .pipe(postcss(processors))
                .pipe(dest(layout + '/' + direction + '/' + theme + '/' + type + '/assets/css'))
                .pipe(rtlcss())
                .pipe(dest(layout + '/' + direction + '/' + theme + '/' + type + '/assets/css'))
                .pipe(minifyCss({
                    level: {1: {specialComments: 0}}
                }))
                .pipe(rename({
                    suffix: ".min"
                }))
                .pipe(dest(layout + '/' + direction + '/' + theme + '/' + type + '/assets/css'));
        }
    }

    // Icons
    function icons() {
        return src('global_assets/scss/shared/icons/' + iconset + '/compile/*.scss')
            .pipe(compileSass())
            .pipe(postcss(processors))
            .pipe(minifyCss({
                level: {1: {specialComments: 0}}
            }))
            .pipe(rename({
                suffix: ".min"
            }))
            .pipe(dest('global_assets/css/icons/' + iconset));
    }

    // CKEditor
    function ckeditor() {
        if (direction == 'LTR') {
            return src('global_assets/scss/layouts/' + layout + '/' + theme + '/compile/ckeditor/*.scss')
                .pipe(compileSass())
                .pipe(postcss(processors))
                .pipe(minifyCss({
                    level: { 1: { specialComments: 0 } }
                }))
                .pipe(dest('global_assets/js/plugins/editors/ckeditor/skins/' + theme + '/'));
        }
        else {
            return src('global_assets/scss/layouts/' + layout + '/' + theme + '/compile/ckeditor/*.scss')
                .pipe(compileSass())
                .pipe(postcss(processors))
                .pipe(rtlcss())
                .pipe(minifyCss({
                    level: { 1: { specialComments: 0 } }
                }))
                .pipe(dest('global_assets/js/plugins/editors/ckeditor/skins/' + theme + '_' + direction.toLowerCase() + '/'));
        }
    }

    //
    // Watch files for changes
    //

    function watchFiles() {
        return watch('global_assets/scss/**/*.scss', series(sass));
    }

    //
    // Default
    //

    exports.default = series(lint, sass, watchFiles);

    //
    // Register tasks
    //

    exports.lint = lint;
    exports.watch = watchFiles;
    exports.sass = sass;
    exports.icons = icons;
    exports.ckeditor = ckeditor;

Now, let’s break this down and review what each part does.

###### Variables

    // Define variables
    const layout = 'layout_1',      // 'layout_1', 'layout_2', 'layout_3', 'layout_4', 'layout_5'
        theme = 'default',          // 'default', 'material' or 'dark'
        direction = 'LTR',          // 'LTR' or 'RTL'
        type = 'full',              // 'full' or 'seed'
        iconset = 'icomoon';        // 'icomoon' (default), 'fontawesome', 'material'

Variables define folder names and construct the paths for CSS files. They also provide an option for running different tasks in different directions (LTR and RTL). They are connected with SCSS variables in `_config.scss` file, they should always match: if you need to compile SCSS files for material theme in 1st layout in LTR direction, make sure `theme`, `layout` and `direction` variables match in both gulpfile.js and _config.scss files.

###### Core & plugins

    // Include our plugins
    const { src, dest, watch, series } = require('gulp'),
        postcss = require('gulp-postcss'),
        autoprefixer = require('autoprefixer'),
        jshint = require('gulp-jshint'),
        compileSass = require('gulp-sass'),
        minifyCss = require('gulp-clean-css'),
        concat = require('gulp-concat'),
        uglify = require('gulp-uglify'),
        rename = require('gulp-rename'),
        rtlcss = require('gulp-rtlcss');

This includes the gulp core and plugins associated with the tasks that we will be performing. The available tasks are lint, sass, compress and default.

###### Lint task

    // Lint Task
    function lint() {
        return src(layout + '/' + direction + '/' + theme + '/' + type + '/assets/js/*.js')
            .pipe(jshint())
            .pipe(jshint.reporter('default'));
    }

Lint task checks any JavaScript file in our js/ directory and makes sure there are no errors in our core JS code.

###### SASS task

    // Autoprefixer config
    const processors = [
        autoprefixer({
            overrideBrowserslist: [
                '>= 1%',
                'last 1 major version',
                'Chrome >= 45',
                'Firefox >= 38',
                'Edge >= 12',
                'Explorer >= 10',
                'iOS >= 9',
                'Safari >= 9',
                'Android >= 4.4',
                'Opera >= 30'
            ],
            map: false
        })
    ];

    // Make it dynamic by changing core variables. Sensitive to location: make sure
    // the paths are correct if you need to use a custom assets location
    function sass() {
        if(direction == 'LTR') {
            return src('global_assets/scss/layouts/' + layout + '/' + theme + '/compile/*.scss')
                .pipe(compileSass())
                .pipe(postcss(processors))
                .pipe(dest(layout + '/' + direction + '/' + theme + '/' + type + '/assets/css'))
                .pipe(minifyCss({
                    level: {1: {specialComments: 0}}
                }))
                .pipe(rename({
                    suffix: ".min"
                }))
                .pipe(dest(layout + '/' + direction + '/' + theme + '/' + type + '/assets/css'));
        }
        else {
            return src('global_assets/scss/layouts/' + layout + '/' + theme + '/compile/*.scss')
                .pipe(compileSass())
                .pipe(postcss(processors))
                .pipe(dest(layout + '/' + direction + '/' + theme + '/' + type + '/assets/css'))
                .pipe(rtlcss())
                .pipe(dest(layout + '/' + direction + '/' + theme + '/' + type + '/assets/css'))
                .pipe(minifyCss({
                    level: {1: {specialComments: 0}}
                }))
                .pipe(rename({
                    suffix: ".min"
                }))
                .pipe(dest(layout + '/' + direction + '/' + theme + '/' + type + '/assets/css'));
        }
    }

We have 2 options for `sass` task here - for LTR and RTL layouts. The difference is `rtlcss()` thingy, which mirrors all CSS rules for RTL layout. Both options are very sensitive to variables, because they construct the paths for generated CSS files. Make sure there's no typo in variables. Also I would recommend not to change autoprefixer settings without specific need.

###### Iconset task

    // Icons
    function icons() {
        return src('global_assets/scss/shared/icons/' + iconset + '/compile/*.scss')
            .pipe(compileSass())
            .pipe(postcss(processors))
            .pipe(minifyCss({
                level: {1: {specialComments: 0}}
            }))
            .pipe(rename({
                suffix: ".min"
            }))
            .pipe(dest('global_assets/css/icons/' + iconset));
    }

3 icon sets: icomoon, font awesome and material design icons. Depending on variable value, correct task would be `iconset:icomoon`, `iconset:fontawesome` and `iconset:material`.

###### CKEditor task

    // CKEditor
    function ckeditor() {
        if (direction == 'LTR') {
            return src('global_assets/scss/layouts/' + layout + '/' + theme + '/compile/ckeditor/*.scss')
                .pipe(compileSass())
                .pipe(postcss(processors))
                .pipe(minifyCss({
                    level: { 1: { specialComments: 0 } }
                }))
                .pipe(dest('global_assets/js/plugins/editors/ckeditor/skins/' + theme + '/'));
        }
        else {
            return src('global_assets/scss/layouts/' + layout + '/' + theme + '/compile/ckeditor/*.scss')
                .pipe(compileSass())
                .pipe(postcss(processors))
                .pipe(rtlcss())
                .pipe(minifyCss({
                    level: { 1: { specialComments: 0 } }
                }))
                .pipe(dest('global_assets/js/plugins/editors/ckeditor/skins/' + theme + '_' + direction.toLowerCase() + '/'));
        }
    }

CKEditor styling logic is hard to understand, but it's working, for some reason. Now we have 3 different theme options: default, material and dark. All themes are located in the same folder where main ckeditor files are. Just run `gulp ckeditor` with proper variables and make sure you use correct ckeditor config file.

###### Watch task

    // Listen for changes in all SCSS files and automatically re-compile
    function watchFiles() {
        return watch('global_assets/scss/**/*.scss', series(sass));
    }

The watch task is used to run tasks as we make changes to our files. As you write code and modify your files, the `watch()` method will listen for changes and automatically run our tasks again so we don't have to continuously jump back to our command-line and run the gulp command each time.

###### Default task

    // Default tasks
    exports.default = series(lint, sass, watchFiles);

Finally, we have our default task which is basically a wrapper to our other tasks. This will be the task that is ran upon entering gulp into the command line without any additional parameters.

###### Register tasks

    // Register tasks
    exports.lint = lint;
    exports.watch = watchFiles;
    exports.sass = sass;
    exports.icons = icons;
    exports.ckeditor = ckeditor;

Finally, we have our default task which is basically a wrapper to our other tasks. This will be the task that is ran upon entering gulp into the command line without any additional parameters.

##### Running tasks:

Now, all we have left to do is run gulp. Switch back over to your command-line and type:

    gulp

This will call gulp and run everything we have defined in our default task. So, in other words It’s the same thing as running:

    gulp default

Additionally, we don’t have to run the default task. We could run any of the tasks we defined at any time. Simply call gulp and then specify the task you would like to run directly afterward. For example, we can run our sass task manually at any time like so:

    gulp sass

or to watch changes and re-run task when file changes:

    gulp watch

To compile icon set:

    gulp icons

To compile CKEditor theme:

    gulp ckeditor