How to Consume and Run Gulp Tasks in Other Files from a Central Gulpfile

Written by timbushell | Published 2021/02/28
Tech Story Tags: javascript | gulpjs | devops | sass-libraries | gulp-hub | gulp-chug | watchtask | scaffold-your-own-solution

TLDRvia the TL;DR App

aA common question asked about gulp is this: How can I run the tasks of other gulpfiles from a central gulpfile, like a hub?
A typical scenario might be a project containing two SASS libraries, each importing from a shared set of `scss` files, but also outputting a slightly different flavoured style sheet - one for the public facing website, another for the admin backend (for instance).
There are two gulp libraries which purport to help: gulp-hub and gulp-chug. Neither maintained for years, and the latter has been blacklisted by gulp: `no reason for this to exist, use the require-all module or node's require`.
Let's not use those: Vanilla is best; especially if it's simple; and - as it turns out - using `require` makes it dead simple. I will show you how.

A Multi-Gulpfile Solution

This article will scaffold a solution for the example given above. To play along, create a working folder somewhere and run the following (in one go):
mkdir great-gulping-website
cd great-gulping-website
npm init -y
npm i gulp browser-sync --save-dev
touch gulpfile.js index.html package.json stylesheet.scss

mkdir admin-site
cd admin-site
npm init -y
npm i gulp gulp-sass --save-dev
touch gulpfile.js index.html package.json stylesheet.scss

cd ..
mkdir public-site
cd public-site
npm init -y
npm i gulp gulp-sass --save-dev
touch gulpfile.js index.html package.json stylesheet.scss
This will scaffold the following files, replicating the scenario of a SASS library building two styles:
  • targeting admin
  • public sites.
great-gulping-website
    |- node_modules
    |- admin-site
        |- node_modules
        |- gulpfile.js 
        |- index.html
        |- package.json
        |- stylesheet.scss
    |- public-site
        |- node_modules
        |- gulpfile.js 
        |- index.html
        |- package.json
        |- stylesheet.scss
    |- gulpfile.js 
    |- index.html
    |- package.json
    |- stylesheet.scss
The goal is as follows:
  • `./admin-site/gulpfile.js` builds `./admin-site/stylesheet.scss` into `./admin-site/dist/stylesheet.css`
  • `./public-site/gulpfile.js` builds `./public-site/stylesheet.scss` into `./public-site/dist/stylesheet.css`
  • Both stylesheets will reference `./stylesheet.scss` for shared settings, styles, mixins, etc.
  • `./gulpfile.js` runs build for `./admin-site/gulpfile.js` and `./public-site/gulpfile.js`; will host a server; will watch all the `stylesheet.scss` files for changes.

Scaffold the Demo Files

Quickly copy the following code into the files indicated, starting with the webpages:
<!-- admin-site/index.html -->
<html>
  <head>
    <link rel="stylesheet" href="./dist/stylesheet.css" />
  </head>
  <body>
    <h1>Admin</h1>
    <a href="../public-site/index.html">public-site</a> |
    <a href="../index.html">home</a>
  </body>
</html>

<!-- public-site/index.html -->
<html>
  <head>
    <link rel="stylesheet" href="./dist/stylesheet.css" />
  </head>
  <body>
    <h1>Public</h1>
    <a href="../admin-site/index.html">admin-site</a> |
    <a href="../index.html">home</a>
  </body>
</html>
<!-- index.html -->
<html>
  <body>
    <a href="admin-site/index.html">admin-site</a> |
    <a href="public-site/index.html">public-site</a>
  </body>
</html>
That's all we need to check this solution is working.
Add some SASS!
// ./stylesheet.scss
$main-color: darkblue;
// ./admin-site/stylesheet.scss
@import "../stylesheet.scss";

h1 {
  color: $main-color;
  font-size: 200%;
}
// ./public-site/stylesheet.scss
@import "../stylesheet.scss";

h1 {
  color: $main-color;
  font-size: 600%;
}
Simple!
Three linked webpages; two `h1` styles, both sharing a settings color.

The Gulp Part

First we'll write two, almost identical gulpfiles, which will contain the tasks for building the two flavours of stylesheet. These files are almost identical in this scenario, but this solution works as a hub for gulpfiles which could all have different tasks:
// admin-site/gulpfile.js
const { src, dest } = require("gulp")
const sass = require("gulp-sass")

const HUBNAME = "admin-site"

function scssTask() {
  return src(`./${HUBNAME}/stylesheet.scss`)
    .pipe(sass())
    .pipe(dest(`./${HUBNAME}/dist`))
}

exports.build = scssTask
exports.default = scssTask
// public-site/gulpfile.js
const { src, dest } = require("gulp")
const sass = require("gulp-sass")

const HUBNAME = "public-site"

function scssTask() {
  return src(`./${HUBNAME}/stylesheet.scss`)
    .pipe(sass())
    .pipe(dest(`./${HUBNAME}/dist`))
}

exports.build = scssTask
exports.default = scssTask
Yep...
SASS compiling...
Vanilla gulp...
No surprises here. But you can make these as complicated as you want.
Finally we get to the point of the article: the bit which explains what those gulp maintainer meant when they said: "use the require-all module or node's require":
var { parallel, series, src, task, watch } = require("gulp")
var browserSync = require("browser-sync").create()

// Create a convenient and descriptive list
// of the sub folders this "hub" will be
// targeting.
const HUB = [
  "admin-site",
  "public-site"
]

// A config object listing all the `stylesheet.scss`
// we want to `watch` AND the gulpfiles whose tasks
// we will run.
const FILES = {
  watch: HUB.map(hub => `./${hub}/stylesheet.scss`).concat(
    "./stylesheet.scss"
  ),
  gulpers: HUB.map(hub => `./${hub}/gulpfile.js`),
}

// Map the gulpfiles into a list of tasks. `.build`
// specifically targets the build task.
const builders = FILES.gulpers.map(
  GULPFILE => require(GULPFILE).build
)

// Group the tasks into a `parallel` set.
const buildHub = parallel(builders)

// Standard watch task which launches the server
// then calls the buildHub task when the stylesheet
// files change.
function watchTask() {
  browserSync.init({
    server: {
      baseDir: "./",
    },
  })
  watch(FILES.watch, buildHub).on("change", browserSync.stream)
}

// Export a series task to first build all the
// stylesheets start the watchTask.
exports.default = series(buildHub, watchTask)
Breakdown:
  • Create a convenient and descriptive list of the sub folders this "hub" will be targeting.
  • A config object listing all the `stylesheet.scss` we want to `watch` AND the gulpfiles whose tasks we will run.
  • The latter maps into a list of the (duh!) required "build" tasks which will be run in `parallel`.
  • We added a standard watchTask.
  • We finish with a standard export of the buildTask and the watchTask in `series`.
Now all you need to do is run `gulp` in the root directory:
gulp

# Output of first "build"
# NB Each build happens twice

> [17:02:21] Using gulpfile ~/great-gulping-website/gulpfile.js
> [17:02:21] Starting 'default'...
> [17:02:21] Starting 'scssTask'...
> [17:02:21] Starting 'scssTask'...
> [17:02:21] Finished 'scssTask' after 34 ms
> [17:02:21] Finished 'scssTask' after 36 ms
> [17:02:21] Starting 'watchTask'...
> [Browsersync] Access URLs:
>  --------------------------------------
>        Local: http://localhost:3000
>     External: http://192.168.1.107:3000
>  --------------------------------------
>           UI: http://localhost:3001
>  UI External: http://localhost:3001
>  --------------------------------------
> [Browsersync] Serving files from: ./

# And then when we change any scss file:

> [17:02:34] Starting 'scssTask'...
> [17:02:34] Starting 'scssTask'...
> [17:02:34] Finished 'scssTask' after 11 ms
> [17:02:34] Finished 'scssTask' after 12 ms

# Again, notice there are two builds each time. 
I hope this simple explanation helps you scaffold your own solution.

Credits


Written by timbushell | 20 years web development; leader in the elioWay; Quora Top Writer (2014-2018);
Published by HackerNoon on 2021/02/28