Table of Contents
The following setup is based on “tristanisfeld” which I found on Github. See https://gist.github.com/tristanisfeld/9deea503260324f5e9b0
The complete setup: DOWNLOAD
In the following setup you will have the ability to:
- Generate one minifed CSS from SCSS files (incl. Autoprefixer)
- Concatinate and minify multiple JS Modules into one JS file
- use “Browsersync” so you don’t need to reload the tab everytime
Also there will be a boolean variable called “production” which activates or deactivates the “minification”, “uglyfication” and generation of sourcemaps.
package.json
{
"name": "my_custom_gulp_setup",
"author": "Kevin Pfeifer",
"license": "WTFPL",
"version": "2.0.0",
"description": "Generate minified CSS and JS from source files",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"devDependencies": {
"browser-sync": "^2.26.7",
"gulp-autoprefixer": "^7.0.1",
"gulp-concat": "^2.6.1",
"gulp-if": "^3.0.0",
"gulp-load-plugins": "^2.0.1",
"gulp-minify-css": "^1.2.4",
"gulp-rename": "^1.4.0",
"gulp-sass": "^4.0.2",
"gulp-sourcemaps": "^2.6.5",
"gulp-uglify-es": "^2.0.0",
"gulp-watch": "^5.0.1",
"node-sass": "^4.13.0"
},
"dependencies": {
"gulp": "^4.0.2"
}
}
Don’t know what the package.json does? See npm vs. yarn – Package Managers for JavaScript
gulpfile.js
const gulp = require('gulp'),
gulpLoadPlugins = require('gulp-load-plugins'),
plugins = gulpLoadPlugins(),
config = require('./gulp/config');
// --------------------function to get tasks from gulp/tasks
function getTask(task) {
return require('./gulp/tasks/' + task)(gulp, plugins, config);
}
// ---------------------------------------------- Gulp Tasks
gulp.task('sass', getTask('sass'));
gulp.task('scripts', getTask('scripts'));
gulp.task('styles', getTask('styles'));
gulp.task('watch', getTask('watch'));
gulp.task('browsersync', getTask('browsersync'));
// --------------------------------------- Default Gulp Task
gulp.task('default', gulp.series('watch'));
Each Gulp setup starts with a “gulpfile.js“.
- gulp = require(‘gulp’)
- Load the gulp default functionality into the variable gulp
- gulpLoadPlugins = require(‘gulp-load-plugins’)
- Load the additional modul “gulp-load-plugins” which helps us use gulp modules later on
- plugins = gulpLoadPlugins()
- Load all modules defined in the package.json into the variable plugins
- For more details see https://www.npmjs.com/package/gulp-load-plugins
- config = require(‘./gulp/config’)
- Load the config file “./gulp/config” into the variable config
function getTask(task) {
return require('./gulp/tasks/' + task)(gulp, plugins, config);
}
Here we define a rather generic function which helps us load tasks from the folder “./gulp/tasks/“. With this function gulp allows us to have 1 file per task and not cram all the tasks into the gulpfile.js
gulp.task('sass', getTask('sass'));
gulp.task('scripts', getTask('scripts'));
gulp.task('styles', getTask('styles'));
gulp.task('watch', getTask('watch'));
gulp.task('browsersync', getTask('browsersync'));
Here we load all the tasks from each JS file in “./gulp/tasks/“.
Gulp Plugin Loader
Since we don’t want to require every module inside every seperate task file we can use the previously loaded module “gulp-load-plugin” to provide us all the modules in one variable.
Basically this module does the following (so we don’t have to do it):
plugins.sourcemaps = require('gulp-sourcemaps');
plugins.concat = require('gulp-concat');
plugins.uglifyEs = require('gulp-uglify-es');
This happens at the start of the gulpfile.js in the following 2 lines:
gulpLoadPlugins = require('gulp-load-plugins'),
plugins = gulpLoadPlugins()
One important thing here is the fact, that the module changes the name of the loaded modules so we can more easily use it inside our tasks.
Node Module Name | Function name |
---|---|
gulp-sourcemaps | sourcemaps |
gulp-concat | concat |
gulp-uglify-es | uglifyEs |
And with that we now have a variable “plugins” from which we can access all modules which are defined in the package.json.
Structure of tasks
// =========================================================
// Gulp Task: sass
// Description: transpiles sass, adds sourcemaps, prefixes
// npm install --save-dev node-sass gulp-sass gulp-sourcemaps gulp-autoprefixer gulp-load-plugins
// =========================================================
module.exports = function(gulp, plugins, config) {
return function() {
return gulp.src(config.sass.src)
.pipe(plugins.if(!config.production, plugins.sourcemaps.init()))
.pipe(plugins.sass(config.sass.opts).on('error', plugins.sass.logError))
.pipe(plugins.autoprefixer())
.pipe(plugins.if(!config.production, plugins.sourcemaps.write('.')))
.pipe(gulp.dest(config.sass.dest));
}
};
Now what does each line?
module.exports = function(gulp, plugins, config) {
With the “module.exports” we define, that this function can be included in other JS files. The parameters used inside the function() are the same gulp, plugins and config variables used in our generic getTask(task) function.
return function() {
The actual functionality of the task needs to be wrapped inside a function() so gulp can use it as a “Callback” in the background and therefore allow tasks to be executed in parallel or series.
return gulp.src(config.sass.src)
...
Now the “usual” Gulp code is being done where the different functionalites of the tasks are defnied. You just need to be sure that a valid “Gulp stream” is being returned here.
Difference between dev and production build
The above example already has the “if production” logic implemented.
.pipe(plugins.if(!config.production, plugins.sourcemaps.init()))
The base module here is called “gulp-if“. See https://www.npmjs.com/package/gulp-if
This means, if we have “production” set to false in our Config-JSON gulp will also create additional Sourcemaps for the SASS.
In the “scripts” tasks we also have a similar code example:
.pipe(plugins.if(config.production, plugins.uglifyEs.default(), plugins.sourcemaps.init()))
Autoprefixer settings
With the current version of Autoprefixer it is not recommended to define the autoprefixer rules inside the JS-File and use a separate file named “.browserslistrc” instead.
You can see all available rules here: https://github.com/postcss/autoprefixer#options
# Browsers that we support
last 2 version
> 1%
maintained node versions
not dead
config.js
The following JSON describes how the gulp setup should behave and therefore how the different tasks should output their code.
- opts => Options for the corresponding Gulp task
- src => Path to the source files
- dest => Path where the generated files should be placed
There is also inline documentation present for basically every option
module.exports = {
production: false,
rename: {
min: { suffix: '.min' }
},
// --------------------------------------------- browsersync
browsersync: {
opts: {
proxy: "http://localhost", // The URL of the website you want to browsersync
port: 4000, // The port, from which the browsersync tab will be opened
serveStatic: [{
route: '/', // The route, from which the "live" website serves CSS and JS files
dir: './dist' // Your local path coming from the gulpfile.js where the newly local generated files are laying
}],
},
watch: [
'./dist/assets/css',
'./dist/assets/js'
]
},
// ---------------------------------------------------- sass
sass: {
src: [
"./source/sass/**/*.scss",
],
opts: { }, // add sass options here
dest: "./dist/assets/css"
},
// ------------------------------------------------- scripts
scripts: {
src: [
'source/js/main.js'
],
base: 'source/js', // common base folder of all concatenated JS files so sourcempas are working correctly
filename: 'main.js', // filename of outputted, concatenated JS file
dest: "./dist/assets/js" // folder where the JS files should be populated
},
// -------------------------------------------------- styles
styles: {
src: [
"./source/css/**/*.css",
],
dest: './dist/assets/css'
},
};