One of the sites I maintain at the University is a slightly complex beast. The theming, for one thing, is done using a higgledy piggledy of Javascript calls necessetated by the application it’s built on. Earlier this year I did a large personal project to strip this mess of Javascript and CSS down into a more manageable project that could be compiled in Gulp with JS, Pug and SCSS to make it easier to maintain.

There was one thing making the process slightly more complicated than it needed to be, however. In addition to the core site theme we also have a satelite site which is, to all intents and purpose, the same site with restricted access to certain resources and a more streamlined look and feel so that it can be called on our kiosk machines. Again, with this being built onto a very locked down hosted service I wasn’t able to do something server side to help detect the kiosks and serve a different version of the site to them. Traditionally we’ve maintained two separate themes, something that has always risked key changes not being mirrored in one or the other leading to unnecessary maintenance. I needed an alternative and saw the way through the process of rebuilding the theme into a compilable version.

My thought here was inspired by the Lego bricks my kids play with. Unlike the wonderfully crafted Lego models I build that sit in my office, my kids like to deconstruct their sets and build them up into something different. Aparently this is how Lego should be played with? I’m not sure, it seems barbaric. Yes, I am the Dad from The Lego Movie. Anyway, as always I digress. This idea got me thinking – could you use the compiled nature of Gulp projects to actually apply a building blocks approach, meaning that you only had to maintain one code base with smaller iterations where apropriate for different projects? I built it, it worked and it’s meant that other forks of the core code base have been far more easily achieveable than they have in the past.

I’m going to walk you through my logic.

1. Get your starting point.

I guess this is as good an oportunity as ever to point you in the direction of my fabulous project quickstarts that I posted about just the other week. You can find that post here! Anyway, go and grab the flat file default template from GitHub. I’m not sure if there’s a more elegant way of doing this – maybe forking it? Anyway, I grabbed it as a zip download and copied it into an empty folder on my machine. Fire up the gulpfile.js in your favourite editor (for example, VS Code!) and change the name of the project to something appropriate, for example building_blocks. On your comand line, in the project, fire off:

npm install

…to install all the dependencies. Congratulations! You’ve got your starting point!

There are a number of Gulp tasks already in this project but the one we’re looking at for now is the “build” task. Run this, either through your IDE or on the command line with:

gulp build

That will create, in the dist folder, your distribution files as well as a neato zip that would be your deployment version of your files. Right, it’s time to start taking what we have and applying it to the building block approach.

2. Define your building blocks

Let’s consider a basic Lego creation made up of five standard red blocks. Say we want to keep the basic shape of the creation but maybe add a little colour to the build – we could swap out two of those red blocks for green ones.

That’s our logic here – we’ve got our red blocks as the base creation but sometimes we might want to use green ones to create a completely different project, or a different flavour of the one we’re currently working on. With a bit of extra configuring, Gulp gives us the tools to easily do that without too much fuss.

Let’s start by giving ourselves a few extra blocks to play with, for example an iteration of the basic template that brings in a sidebar.

In the views folder, create a sidebar folder and add into that another index.pug file with the following code:

extends ../layout/layout.pug

block content
    .row
        .col-4
            h2
                | Sidebar
            p
                | This is our sidebar content
        .col
            h1
                | Header
            p
                | Paragraph
            img.img-fluid(src="img/IMG_20190422_110748-01.jpeg")

That’s basically the same as the standard index.pug with the addition of a changed path to the layout template and the addition of a new column for the sidebar.

We’re also going to create a new file in the views folder called about.pug:

extends layout/layout.pug

block content
    .row
        .col
            h1
                | About
            p
                | A basic about page

That’s just going to give us something to use as an example later on.

3. Add some build instructions

So, what we need now is to be able to tell the build task what version of our site we’re building; the non-sidebar version, or the sidebar version. To do this we need to tweak some of the tasks to add some complexity – this will pay off in the long run, though.

First off, let’s add a couple of new variables to our gulpfile.

var buildName = "default";

var viewFiles = {
    'default': [
        'views/index.pug',
        'views/about.pug'
    ],
    'sidebar': [
        'views/sidebar/index.pug',
        'views/about.pug'
    ]
}

What we have here is a couple of things – a global variable which will tell our basic tasks what kind of site we’re building, and an object which will tell those same tasks what that site is made up from. You can see that the default one uses both the Pug files in the root views folder, while the ‘sidebar’ type uses the basic about.pug, but uses the sidebar index.pug instead.

4. Let your tasks know which version to build

So, we’ve got our global variable – that’s our key to telling the tasks how to handle things. A we’re using the views in this example, let’s see what we need to do to our views task to make use of those. Currently it looks like this:

gulp.task('views', function(){
    return gulp.src('views/*.pug')
        .pipe(pug())
        .pipe(gulp.dest('.'))
});

Let’s make some changes so that it looks like this:

gulp.task('views', function(){
    return gulp.src(viewFiles[buildName])
        .pipe(pug())
        .pipe(gulp.dest('.'))
});

So, now instead of using the basic views in the views folder, it’ll make use of the views as dictated by our viewFiles object, specifically those that match the buildName variable. You can test this out.

Run the views task – the pages it’ll render are the ones that are dictated by the default list in the viewFiles object. Now change the buildName variable in the gulpfile to “sidebar”, save the gulpfile and run the task again. You’ll now get the sidebar version of the index page rendered.

Okay, so the logic there is sound, but that doesn’t let you build each version dynamically. We need to add a couple of tasks here as well as making a change to our build task.

5. Do the build

Let’s add a couple of tasks to our gulpfile:

gulp.task('setDefault', function(done){
    buildName = "default";
    done();
});

gulp.task('setSidebar', function(done){
    buildName = "sidebar";
    done();
});

What we’re doing here is adding tasks we can call to change that buildName variable to something that will let us tell the views task which version of the site to build. Now let’s tweak our actual build task from :

gulp.task('build', gulp.series('clean', 'sass', 'minicss', 'minijs', 'views', 'makedist', 'makezip'), function(){});

…to:

gulp.task('buildDefault', gulp.series('setDefault', 'clean', 'sass', 'minicss', 'minijs', 'views', 'makedist', 'makezip'), function(){});

The task name is now buildDefault and the task will first set the global buildName to “default” before running all the relevant tasks. That’s fine for building the default site, but for the sidebar we need to add another task:

gulp.task('buildSidebar', gulp.series('setSidebar', 'clean', 'sass', 'minicss', 'minijs', 'views', 'makedist', 'makezip'), function(){});

Easy peasy – just create a sidebar version of our build task.

Running either of those tasks will render the relevant versions of the views, all associated css and js and pop them into the dist folder. All good stuff!

6. Giving each build its own destination

That’s all well and good, but it means that the dist folder will only be home to the last version you built, and you won’t be maintaining dists for each of your versions. That’s easily rectifiable, however, with a couple of tweaks to the other tasks. By default, clean, makedist and makezip use a distName variable to decide where to put the builds and what to call the zip. Swap this over to our new buildName variable like so:

gulp.task('clean', function () {
    return del('dist/'+buildName+'/**/*');
});

gulp.task('makedist', function(){
    return gulp.src(sourceFiles,{ base: '.' })
        .pipe(gulp.dest('dist/'+buildName))
});

gulp.task('makezip', function(){
    return gulp.src('dist/'+buildName+'/**/*')
        .pipe(zip(buildName+'.zip'))
        .pipe(gulp.dest('dist'))
});

Now building the default and sidebar types will pop them into their own respective folders in dist and create the relevant zip files ready for pushing up to your live server!

And there you have it! I’ve put all the code here up onto GitHub and you can grab it at
https://github.com/abeeken/building_blocks. Obviously this theory can be extended for rendering different versions of the JS and SASS libraries so go crazy. I’m also investigating more elegant ways of passing the buildName variable around that doesn’t involve multiple build tasks but if anyone has any suggestions, as always, sound off in the comments!