The idea for this plugin is not new; it's loosely based on the postcss-transform-shortcut plugin by Jonathan Neal, available from https://github.com/jonathantneal/postcss-transform-shortcut. The concept is not necessarily a shorter means to create transition statements, but it makes it easier by allowing authors to specify values independently. These are then automatically inserted into the correct order within the transition declaration.
The source code for this plugin is also available on GitHub, at https://github.com/alexlibby/postcss-transition-shortcut; the NPM package is also available at https://www.npmjs.com/package/postcss-transition-shortcut.
Let's dive in and take a look at how it is put together, in more detail:
git clone https://github.com/postcss/postcss-plugin-boilerplate.git
postcss-plugin-boilerplate repository to our project area, as shown in this screenshot:
node ./postcss-plugin-boilerplate/start
package.json file; if you spend any time developing plugins in the future, then it is recommended that you go ahead and create one:
postcss-plugin-boilerplate folder from the project root folder, as this is no longer needed.
index.js—to do this, open up a copy of the file from within the postcss-transition-shortcut plugin from within our project area, and alter the code as shown:var postcss = require('postcss');
module.exports = postcss.plugin('postcss-transition-shortcut', function (opts) {
opts = opts || {};
var defaults = {
property: 'all',
duration: '1s',
timing: 'ease-in-out',
delay: '1s'
};
return function (css, result) {
css.walkRules(function (rule) {
var transitionRule;
var transitionValues = [];
var index = -1, node;
var attributes = /^ (property|duration|timing|delay)$/;
while (node = rule.nodes[++index]) {
if (attributes.test(node.prop)) {
transitionRule = transitionRule || node.cloneBefore({ prop: 'transition' });
var transValues = postcss.list.space(node.value);
transitionValues.push(transValues.join(','));
node.remove();
--index;
}
}
transitionRule.value =
transitionValues.join(' ');
});
};
});At this stage we will have a working plugin—the proof, though (to quote an old English saying), is in the pudding: does the plugin work as we expect? Well, there's no better way to find out than by trying it out, so let's set up a quick demo to confirm it works as expected. Before we do so, though, there is one important point I need to make, which concerns the generation of PostCSS plugins.
The sharp-eyed amongst you will notice though that if we don't specify one of the four values for our transition plugin, then the code at present won't use the default; hopefully an update will come in a future version of the plugin.
This aside, the process for testing our plugin uses the AVA test runner, available from https://github.com/sindresorhus/ava. The framework for the test is already created within the plugin boilerplate, which leaves us to add the test code to test.js file. Let's take a peek at what's required:
package.json file:npm install --global ava ava --init
test.js, within the plugin folder we created in the previous exercise: t.same(result.warnings().length, 0);
});
}
test('transitionShtct', t => {
return run( t, 'div { property: all; duration: 1s; timing: ease-in-out; delay: 1s; }', 'div { transition: all 1s ease-in-out 1s; }', { });
});
npm test and press Enter.
All good so far, right—at this point, we're OK to create a simple demo to prove plugin works…or are we? Well, the test shows a pass, so the code should be OK. But further down there are a ton of errors displayed, similar to this screenshot—what gives?

The test has passed, yet the tests would seem to indicate otherwise; a look further down reveals yet more errors:

This raises some important points about testing, so let's cover these before continuing with our demo.
The main error, or Exported linebreaks to be 'LF'…, is a simple one to fix—it's being caused by Sublime Text being set to use Windows as the default line endings setting. Assuming we're using Sublime Text, let's go ahead and deal with that error:
index.js from our plugin folder.test.js—once done, close both files.If we re-run the test, we should see a significant drop in listed errors—there will be some left for us to fix in index.js and test.js, similar to this screenshot:

Most of the errors are self-explanatory—the two that are less obvious are Expected indentation of X spaces… and Line X exceeds the maximum line length…. We can fix the first by replacing all instances of tabs with four individual spaces per tab. The second error is simple to fix—simply split the line of code into two lines.
We need to work through all of the remaining errors, as far as possible—these won't entirely be the same for your version of the plugin, but some will be similar.
If you come across any errors where you want to understand the reason behind the error, take a look at https://jslinterrors.com/—it's a great source for defining what an error means!
Assuming we've cleared most of the errors, we should be left with just one:

Is this an error we should fix, and therefore can clear from the report? The simple answer is that it depends—it highlights an important point about using linting for code, so let's take a moment to cover this in more detail.
The last error shown in the report presents some challenges—the code is valid, yet ESLint flags the error. The reason for this is that it has found an assignment expression within a while statement initializer; it is treated as a possible mistake in the code and may have unintended effects on the code.
In some respects, it can be treated as a warning, and not necessarily as an error. Prior to July 2013 we could have configured our test to ignore this, but changes made to ESLint since that date mean that this error cannot be cleared without reworking the code.
If you would like to understand more about the causes of this error, then please refer to http://jslinterrors.com/unexpected-assignment-expression/.
In our instance, the code is valid and will not cause any errors—it leaves us with several options as to what we can do going forward:
For now, we're going to switch off the test for this error—we can do this by editing the .eslintrc file from within our plugin, and set the value in square brackets to 0:

This will work in the short term, but with a view to revisiting the code to design out the ambiguity at some point in the future.
With our plugin in place, let's test it out—for this, we need a couple of files from the code download that accompanies this book; the files are available in the T43 – building a transition shortcut plugin folder:
gulpfile.js and package.json, then save them to the root of our project area.style.css in the src folder in our project area:div {
property: all;
duration: 1s;
timing: ease-in-out;
delay: 1s;
}gulp then press Enter—PostCSS will go away and compile the source style sheet. If all is well, we should see the compiled results of our style sheet in the dest folder of our project area:
At this stage, we've run the test for our plugin—we go one step further, and add our plugin to a test runner service such as Travis CI (at https://travis-ci.org). Although this is a mandatory part of the process for creating any PostCSS plugin, there is a fairly steep learning curve, and anyone working on Windows may run into difficulties! If you are a Windows user, you will have to make test.js executable via the command line—this requires prior knowledge of using Git, which is beyond the scope of this book.
For now, we'll skip past the Travis CI part of the process—the plugin is sufficiently straightforward that the local testing with test.js will suffice. Let's change tack—our plugin contains a number of useful concepts in PostCSS, so let's explore how it is put together in more detail.
The inspiration for this plugin is twofold—at the time of writing, PostCSS doesn't have a great number of animation-based plugins, and it borrows the same concept used in the postcss-transform-shortcut plugin.
We start with the ubiquitous call to initialize PostCSS as a dependency for our plugin:
var postcss = require('postcss');Next up, we initialize postcss.plugin, to expose functionality within our plugin to the ecosystem:
module.exports = postcss.plugin('postcss-transition-shortcut', function (options) {At present, our plugin doesn't contain any options, so it will be set as blank; if we had had some options set, then these will be stored in the options array:
options = options || {};A key part of our plugin is to set some default options—we need to have some default values set, if we don't specify one or more values:
var defaults = {
property: 'all',
duration: '1s',
timing: 'ease-in-out',
delay: '1s'
};Up next comes the crux of our plugin—it returns the result of this function:
return function (css) {
css.walkRules(function (rule) {
var transitionRule;
var transitionValues = [];
var index = -1, node;
var attributes = /^(property|duration|timing|delay)$/;We walk through each rule using css.walkRules—it sets up a number of variables and an array; we also set a search string that will be used to find any instance of our transition properties.
If we find a suitable instance of our property, we then clone it, adding the property name transition before it. We then work through each of up to four properties that may be set, joining them together into the final transition declaration:
while (node = rule.nodes[++index]) {
if (attributes.test(node.prop)) {
transitionRule = transitionRule || node.cloneBefore({ prop: 'transition' });
var transValues = postcss.list.space(node.value);
transitionValues.push(transValues.join(','));
node.remove();
--index;
}
}
transitionRule.value = transitionValues.join(' ');
});
};
});Let's move on. Our first example was a straightforward plugin; although it does need some further development (as indicated in Testing our plugin), it still serves a useful purpose. In our next example, we'll take a different approach: we will use an existing plugin as a basis for our new version. This plugin, unlike the first one, will not see the light of day in GitHub, though—we'll explore the reasons for this, and more, as part of our next exercise.