As part of creating the demos we've worked through in this book, we concentrated on ensuring plugins are installed, and that we have the right files in the right place. There is something missing though, and that is—what actually happens in the files? Why do we have tasks in a particular order? What is the reasoning behind choosing some of the plugins that we've used…and so on—you get the idea!
Over the next few pages, we're going to try to answer some of these questions (and more), by exploring the processor that we've used in some of the recent examples; you will see that there isn't a one-answer-fits-all approach, but more a case of working through your requirements, and picking plugins to suit your needs.
Before we go into depth, though, let's just quickly recap the make-up of our processor, starting with the package.json file.
The package.json file tells PostCSS which plugins to use, and may contain some of the key configuration settings to be used during compilation:
{
"name": "postcss",
"version": "1.0.0",
"description": "Configuration file for PostCSS",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Alex Libby",
"license": "ISC",
"dependencies": { "postcss": "^5.0.8" },The top half of our processor contains a number of key properties that tell us details such as the version, description, who created it, any dependencies, and the license being used for the project:
"devDependencies": {
"autoprefixer": "^6.0.3",
"cssnano": "^3.2.0",
"gulp": "^3.9.0",
"gulp-postcss": "^6.0.0",
"gulp-rename": "^1.2.2",
"gulp-sourcemaps": "^1.5.2",
"postcss-reporter": "^1.3.0",
"stylelint": "^2.3.7"
}
}In comparison, the key part for us is in the bottom half; this lists all of the plugins that will be used within our project. In many of our projects, we've installed the plugin—at point of installation, the plugin will add an entry into this file that contains the name and the minimum version required (represented by the ^ symbol).
It is worth noting that we can manually add entries to, or remove entries from this file, or even copy package.json files from one project to another if needed. This is particularly useful if we know that a new project has identical (or very similar) requirements to an existing one; plugins will only add an entry into this file at installation, if one does not already exist.
The gulpfile.js file is where the real magic happens—this contains all of the tasks that need to be performed on each style sheet within our project. Outside of the style sheet, this is the second of two files that we've simply copied across from the code download to our project area. Now that we've been using it in anger, it's worth taking a moment to explore what happens in more detail.
The gulpfile.js file is made up of several sections—in our example, we begin with a list of variables that define references to each of our plugins:
'use strict';
var gulp = require('gulp');
var postcss = require('gulp-postcss');
//var autoprefixer = require('autoprefixer');
var cssnano = require('gulp-cssnano');
var sourcemaps = require('gulp-sourcemaps');
var rename = require('gulp-rename');
var stylelint = require('stylelint');
var reporter = require('postcss-reporter');
var rucksack = require('rucksack-css');The first task in our list is the most important one—this picks up and compiles the source code into a valid CSS file and deposits it in the dest folder. As part of this, we provide links to any PostCSS plugin that is needed to transform our code—in this example, we're using Rucksack, set to include fallback support but not add vendor prefixes:
gulp.task('styles', function () {
return gulp.src('src/*.css')
.pipe(postcss([ rucksack({ fallbacks: true, autoprefixer: true }) ]))
.pipe(gulp.dest('dest/'));
});This chunky task is less complicated than it looks—it checks our code for consistency, based on the rules set; it outputs any warnings or errors on screen using the reporter plugin. The key here is the ['styles'] attribute—this tells PostCSS not to perform this task until the styles task has been completed:
gulp.task("lint-styles", ['styles'], function() {
return gulp.src("dest/*.css")
.pipe(postcss([ stylelint({
"rules": {
"color-no-invalid-hex": 2,
"declaration-colon-space-before": [2, "never"],
"indentation": [2, 2],
"number-leading-zero": [2, "always"]
}
}),
reporter({ clearMessages: true, })
]))
});In comparison, the next two tasks are relatively straightforward—this one takes care of compressing our compiled code, and renaming it with a .min.css extension:
gulp.task('rename', ['lint-styles'], function () {
return gulp.src('dest/*.css')
.pipe(postcss([ cssnano() ]))
.pipe(rename('style.min.css'))
.pipe(gulp.dest("dest/"));
});This task is equally straightforward—it creates a source map of our style sheet, and sets it in a format that PostCSS can release into a file within the dest folder of our project area:
gulp.task('sourcemap', ['rename'], function () {
return gulp.src('dest/*.css')
.pipe(sourcemaps.init())
.pipe(sourcemaps.write('maps/'))
.pipe(gulp.dest("dest/"));
});The last two steps play the most important role in any Gulp task file—the first will fire off calls to each of our tasks if we enter gulp in a command line prompt:
gulp.task('default', ['styles', 'lint-styles', 'rename', 'sourcemap']);This task, although not obligatory, watches out for any changes to our code and sets off the tasks in our Gulp file automatically. It will respect any constraints set, although for consistency, it is preferable to list the tasks being performed in the same order as they are shown in the file:
var watcher = gulp.watch('src/*.css', ['styles',
'lint-styles', 'rename', 'sourcemap']);
watcher.on('change', function(event) {
console.log('File ' + event.path + ' was ' +
event.type + ', running tasks...');
});There is more to the compilation process than these two files—thought should also be given to how we structure our working environment. A quick look at the Gulp task file should reveal that we've used a simple in-tray/out-tray approach; code is picked up from the src folder, and the results placed into the dest folder at the end of compilation.
This is an important part of the process—after all, there is no benefit in using PostCSS if we don't give any thought to the structure of our project area! Part of this is to maintain separation between source and compiled files, but also that we may decide to expand our compilation process to include tasks such as shrinking images. There is no right or wrong in how this area should be structured—this will be dictated by our project requirements.