Using Gulp

Overview

Gulp is a very powerful yet simple toolkit. It allows you to automate SCSS compilation using configured gulpfile.js and package.json 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.

Note - this configuration uses Gulp for SCSS compilation, but in 99.9% cases compilation will be integrated into your development environment and most likely done with webpack. Configuration of webpack option will be added in this package very soon.

Step 1 - Install Node.js

First - we need our most important requirement, Node. To install Node - simply visit https://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

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 gulpfile.js in your project environment
												cd [path to local web server]/Limitless/utils/
											
										

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-rename - rename files
  • gulp-rtlcss - generate RTL version of CSS files. Do not use it with LTR layout
  • gulp-dart-sass - Sass plugin for Gulp, using the Dart Sass compiler (recommended compiler)

Install required plugins:

npm install gulp-postcss autoprefixer gulp-jshint jshint gulp-sass gulp-clean-css gulp-concat gulp-uglify gulp-rename gulp-rtlcss gulp-dart-sass --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.

You can also use package.json file to install all plugins. Just run npm install command from directory where your gulpfile.js and package.json are located. This single command will create /node_modules/ directory and install all necessary plugins in there.

											
												{
													"name": "limitless",
													"version": "1.0.0",
													"description": "Limitless - responsive web application kit",
													"main": "index.js",
													"dependencies": {
														"autoprefixer": "^10.2.5",
														"gulp-clean-css": "^4.3.0",
														"gulp-concat": "^2.6.1",
														"gulp-jshint": "^2.1.0",
														"gulp-postcss": "^9.0.1",
														"gulp-rename": "^2.0.0",
														"gulp-rtlcss": "^2.0.0",
														"gulp-sass": "^5.1.0",
														"gulp-uglify": "^3.0.2",
														"gulp-dart-sass": "^1.0.2"
													},
													"devDependencies": {
														"gulp": "^4.0.2",
														"postcss": "^8.2.8",
														"jshint": "^2.10.3",
														"node": "16.17.0",
														"node-sass": "^7.0.1",
														"sass": "^1.51.0"
													},
													"scripts": {
														"test": "echo \"Error: no test specified\" && exit 1"
													},
													"author": "Eugene Kopyov",
													"license": "ISC"
												}
											
										

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
													*
													* ---------------------------------------------------------------------------- */


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

													// Define variables
													const layout = 'layout_1',  // 'layout_1', 'layout_2', 'layout_3', 'layout_4', 'layout_5', 'layout_6'
														direction = 'ltr',          // 'ltr' or 'rtl'
														type = 'full',              // 'full' or 'seed'
														iconset = 'phosphor';       // 'phosphor' (default), 'icomoon' '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')(require('node-sass')),
														// compileSass = require('gulp-dart-sass'),
														minifyCss = require('gulp-clean-css'),
														concat = require('gulp-concat'),
														rename = require('gulp-rename'),
														rtlcss = require('gulp-rtlcss');


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

													// Lint
													function lint() {
														return src('../../html/' + layout + '/' + 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('../../assets/scss/layouts/' + layout + '/compile/*.scss')
																.pipe(compileSass().on('error', compileSass.logError))
																.pipe(postcss(processors))
																.pipe(dest('../../html/' + layout + '/' + type + '/assets/css/' + direction))
																.pipe(minifyCss({
																	level: {1: {specialComments: 0}}
																}))
																.pipe(rename({
																	suffix: ".min"
																}))
																.pipe(dest('../../html/' + layout + '/' + type + '/assets/css/' + direction))
																.pipe(concat('all.min.css'))
																.pipe(dest('../../html/' + layout + '/' + type + '/assets/css/' + direction));
														}
														else {
															return src('../../assets/scss/layouts/' + layout + '/compile/*.scss')
																.pipe(compileSass().on('error', compileSass.logError))
																.pipe(postcss(processors))
																.pipe(dest('../../html/' + layout + '/' + type + '/assets/css/' + direction))
																.pipe(rtlcss())
																.pipe(dest('../../html/' + layout + '/' + type + '/assets/css/' + direction))
																.pipe(minifyCss({
																	level: {1: {specialComments: 0}}
																}))
																.pipe(rename({
																	suffix: ".min"
																}))
																.pipe(dest('../../html/' + layout + '/' + type + '/assets/css/' + direction))
																.pipe(concat('all.min.css'))
																.pipe(dest('../../html/' + layout + '/' + type + '/assets/css/' + direction));
														}
													}

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


													//
													// Watch files for changes
													//

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


													//
													// Default task
													//

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


													//
													// Register tasks
													//

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

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', 'layout_6'
														direction = 'ltr',          // 'ltr' or 'rtl'
														type = 'full',              // 'full' or 'seed'
														iconset = 'phosphor';       // 'phosphor' (default), 'icomoon' '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)

  • layout - defines the destination path where CSS files will be stored, on layout level
  • direction - CSS files for LTR version are stored in assets/css/ltr/ folder, for RTL - assets/css/rtl/ folder
  • type - either full or seed. Full version contains all components and pages, seed version contains only core and Bootstrap components
  • iconset - is needed only for gulp icons task that compiles styles for icons
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')(require('node-sass')),
														compileSass = require('gulp-dart-sass'),
														minifyCss = require('gulp-clean-css'),
														concat = require('gulp-concat'),
														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.

There are 2 compilers available: DartSass and LibSass. LibSass is deprecated, but works 3x times faster than DartSass (~10seconds versus ~3seconds). DartSass is recommended though.
Lint task
												
													// Lint Task
													function lint() {
														return src('../../html/' + layout + '/' + 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('../../assets/scss/layouts/' + layout + '/compile/*.scss')
																.pipe(compileSass().on('error', compileSass.logError))
																.pipe(postcss(processors))
																.pipe(dest('../../html/' + layout + '/' + type + '/assets/css/' + direction))
																.pipe(minifyCss({
																	level: {1: {specialComments: 0}}
																}))
																.pipe(rename({
																	suffix: ".min"
																}))
																.pipe(dest('../../html/' + layout + '/' + type + '/assets/css/' + direction))
																.pipe(concat('all.min.css'))
																.pipe(dest('../../html/' + layout + '/' + type + '/assets/css/' + direction));
														}
														else {
															return src('../../assets/scss/layouts/' + layout + '/compile/*.scss')
																.pipe(compileSass().on('error', compileSass.logError))
																.pipe(postcss(processors))
																.pipe(dest('../../html/' + layout + '/' + type + '/assets/css/' + direction))
																.pipe(rtlcss())
																.pipe(dest('../../html/' + layout + '/' + type + '/assets/css/' + direction))
																.pipe(minifyCss({
																	level: {1: {specialComments: 0}}
																}))
																.pipe(rename({
																	suffix: ".min"
																}))
																.pipe(dest('../../html/' + layout + '/' + type + '/assets/css/' + direction))
																.pipe(concat('all.min.css'))
																.pipe(dest('../../html/' + layout + '/' + type + '/assets/css/' + direction));
														}
													}
												
											
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('../../assets/scss/shared/icons/' + iconset + '/compile/*.scss')
															.pipe(compileSass().on('error', compileSass.logError))
															.pipe(postcss(processors))
															.pipe(minifyCss({
																level: {1: {specialComments: 0}}
															}))
															.pipe(rename({
																suffix: ".min"
															}))
															.pipe(dest('../../assets/icons/' + iconset));
													}
												
											
4 icon sets: phosphor, icomoon, font awesome and material design icons.
Watch task
												
													// Listen for changes in all SCSS files and automatically re-compile
													function watchFiles() {
														return watch('../../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;
												
											
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 separtely. Simply call gulp and then specify the task you would like to run directly afterwards. 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