Skip to content

Instantly share code, notes, and snippets.

@hildissent
Last active April 13, 2022 08:05
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hildissent/f3e32fc30dcf114386214da4745535de to your computer and use it in GitHub Desktop.
Save hildissent/f3e32fc30dcf114386214da4745535de to your computer and use it in GitHub Desktop.
gulpfile - gulp 4, jekyll, tailwindcss, purged (css), minified (css, js, and html), and cache-busting.
"use strict";
//
// PLUGINS
//
const autoprefixer = require("autoprefixer");
const browsersync = require("browser-sync").create();
const clean = require("gulp-clean");
const concat = require("gulp-concat");
const cp = require("child_process");
const critical = require("critical");
const cssimport = require("gulp-cssimport");
const cssnano = require("cssnano");
const gulp = require("gulp");
const htmlmin = require("gulp-htmlmin");
const inject = require("gulp-inject");
const postcss = require("gulp-postcss");
const purgecss = require("gulp-purgecss");
const rename = require("gulp-rename");
const responsive = require("gulp-responsive");
const rev = require("gulp-rev");
const tailwindcss = require("tailwindcss");
const uglify = require("gulp-uglify");
//
// CSS BUILDS
//
// build critical css inline. must run after css + html builds. [production]
function build_nstyle() {
return gulp.src("dist/**/*.html")
.pipe(critical.stream(
{
base: "dist/",
css: ["dist/assets/temp/master.css"],
inline: true,
dimensions:
[
{ height: 896, width: 414 },
{ height: 1024, width: 768 },
{ height: 1336, width: 1024 },
{ height: 1440, width: 2560 }
],
ignore: { atrule: ["@font-face"] }
}
))
.pipe(gulp.dest("dist"))
}
// build a temporary style file with all styles. [production]
// this is only necessary as critical will not accept globs for css input.
function build_master() {
return gulp.src(
[
"src/css/normal.css",
"src/css/styles.css",
"src/css/work.css"
]
)
.pipe(concat("master.css"))
.pipe(cssimport())
.pipe(postcss(
[
tailwindcss("tailwind.js"),
autoprefixer({ browsers: ["last 1 version"] }),
cssnano({ discardComments: { removeAll: true } })
]
))
.pipe(gulp.dest("dist/assets/temp"))
}
// build a css normalize-reset. [development + production]
function build_normal() {
return gulp.src("src/css/normal.css")
.pipe(postcss(
[
tailwindcss("tailwind.js"),
autoprefixer({ browsers: ["last 1 version"] }),
cssnano({ discardComments: { removeAll: true } })
]
))
.pipe(rev())
.pipe(gulp.dest("dist/assets/css"))
.pipe(browsersync.stream());
}
// build full css framework. [development]
function build_styles() {
return gulp.src("src/css/styles.css")
.pipe(postcss(
[
tailwindcss("tailwind.js"),
autoprefixer({ browsers: ["last 1 version"] }),
cssnano({ discardComments: { removeAll: true } })
]
))
.pipe(rev())
.pipe(gulp.dest("dist/assets/css"))
.pipe(browsersync.stream());
}
// build css with only the used elements. [production]
function build_purged() {
// define the tailwind extractor.
class TailwindExtractor {
static extract(content) {
return content.match(/[A-z0-9-:\/]+/g) || [];
}
}
return gulp.src("src/css/styles.css")
.pipe(postcss(
[
tailwindcss("tailwind.js"),
autoprefixer({ browsers: ["last 1 version"] }),
cssnano({ discardComments: { removeAll: true } })
]
))
.pipe(rename({ basename: "purged" }))
.pipe(purgecss(
{
content: ["dist/**/*.html"],
extractors: [
{
extractor: TailwindExtractor,
extensions: ["html", "js"]
}
]
}
))
.pipe(rev())
.pipe(gulp.dest("dist/assets/css"))
.pipe(browsersync.stream());
}
// build css with custom elements. [development + production]
function build_custom() {
return gulp.src("src/css/work.css")
.pipe(cssimport())
.pipe(postcss(
[
tailwindcss("tailwind.js"),
autoprefixer({ browsers: ["last 1 version"] }),
cssnano({ discardComments: { removeAll: true } })
]
))
.pipe(rev())
.pipe(gulp.dest("dist/assets/css"))
.pipe(browsersync.stream());
}
//
// JS BUILDS
//
// build js to be injected inline. [development + production]
function build_ncode() {
return gulp.src(["src/js/vendor/loadCSS.js", "src/js/vendor/loadJS.js"])
.pipe(uglify())
.pipe(gulp.dest("dist/assets/inline"))
.pipe(browsersync.stream());
}
// build js bundle - current strategy is to combine most js. [development + production]
function build_bundle() {
return gulp.src(["src/js/vendor/jquery.js", "src/js/vendor/pushy.js", "src/js/custom.js"])
.pipe(concat("bundle.js"))
.pipe(uglify())
.pipe(rev())
.pipe(gulp.dest("dist/assets/js"))
.pipe(browsersync.stream());
}
//
// IMAGE BUILDS
//
// generate optmized, correctly sized, images. [development + production]
function build_images() {
return gulp.src("src/img/**/*.{jpg,png}")
.pipe(responsive(
{
"21x09/**/*.jpg":
[
{ width: 640, rename: { suffix: "-640", extname: ".jpeg" } },
{ width: 768, rename: { suffix: "-768", extname: ".jpeg" } },
{ width: 1024, rename: { suffix: "-1024", extname: ".jpeg" } },
{ width: 1280, rename: { suffix: "-1280", extname: ".jpeg" } },
{ width: 1536, rename: { suffix: "-1536", extname: ".jpeg" } },
{ width: 1920, rename: { suffix: "-1920", extname: ".jpeg" } }
],
"01x01/**/*.jpg":
[
{ width: 1024, rename: { suffix: "-1024", extname: ".jpeg" } }
],
"avatar.jpg":
[
{ width: 320, rename: { extname: ".jpeg" } }
],
"mythicdomains.png":
[
{ width: 320, rename: { extname: ".png" } }
]
},
{
// global configuration for all images
quality: 70,
progressive: true,
withMetadata: false,
errorOnUnusedImage: false,
passThroughUnused: true
}
))
.pipe(gulp.dest("dist/assets/img"))
.pipe(browsersync.stream());
}
//
// JEKYLL BUILDS
//
// build jekyll with development environment variable. exclude analytics, comments, etc. [development]
function build_static() {
return cp.spawn(
"bundle",
["exec", "jekyll", "build"],
{ stdio: "inherit" }
);
}
// build jekyll with production environment variable. [production]
function build_jekyll() {
var productionEnv = process.env;
productionEnv.JEKYLL_ENV = "production";
return cp.spawn(
"bundle",
["exec", "jekyll", "build"],
{
stdio: "inherit",
env: productionEnv
}
);
}
//
// HTML BUILDS
//
// process the html content in dist. [development + production]
function build_forged() {
return gulp.src("dist/**/*.html")
// inject inline js - each injection is to a specific file.
.pipe(inject(
gulp.src(["dist/assets/inline/*.js"]),
{
transform: function (filePath, file)
{ return '<script type=\"text/javascript\">' + file.contents.toString('utf8') + '</script>'; }
}
))
// include cache-busting css via loadCSS.js.
.pipe(inject(
gulp.src(["dist/assets/css/*.css"],{ read: false }),
{
relative: true,
transform: function (filepath, file, i, length)
{ return '<link rel=\"preload\" href=\"' + filepath + '\" as=\"style\" onload=\"this.onload=null\;this.rel=\'stylesheet\'\" /><noscript><link rel=\"stylesheet\" href=\"' + filepath + '\" /></noscript>'; }
}
))
//include cache-busting js via loadJS.js.
.pipe(inject(
gulp.src(["dist/assets/js/*.js"],{ read: false }),
{
starttag: "<!-- codefile:{{ext}} -->",
endtag: "<!-- endcodefile -->",
relative: true,
transform: function (filepath, file, i, length)
{ return '<script type=\"text/javascript\">loadJS( \"' + filepath + '\" )\;</script>'; }
}
))
// minify the html files.
.pipe(htmlmin(
{
collapseWhitespace: true,
removeComments: true,
minifyCSS: true,
processScripts: ["application/ld+json"],
sortAttributes: true,
sortClassName: true
}
))
.pipe(gulp.dest("dist"))
.pipe(browsersync.stream());
}
//
// CLEAN FOLDERS
//
// remove all css files from dist.
function clean_styles() {
return gulp.src(
"dist/assets/css/",
{
read: false,
allowEmpty: true
}
)
.pipe(clean());
}
// remove all js files from dist.
function clean_script() {
return gulp.src(
[
"dist/assets/js/",
"dist/assets/inline/*.js"
],
{
read: false,
allowEmpty: true
}
)
.pipe(clean());
}
// remove all image files from dist.
function clean_images() {
return gulp.src(
"dist/assets/img/",
{
read: false,
allowEmpty: true
}
)
.pipe(clean());
}
// remove all of dist.
function clean_folder() {
return gulp.src(
"dist/",
{
read: false,
allowEmpty: true
}
)
.pipe(clean());
}
//
// SERVICES
//
// websync initiation.
function local_browse(done) {
browsersync.init(
{
server: { baseDir: "./dist/" },
port: 4000
}
);
done();
}
// websync cycler.
function local_cycler(done) {
browsersync.reload();
done();
}
// project watcher & automated rebuilding.
function watch_mywork() {
gulp.watch("./src/css/normal.css", gulp.series(build_normal, build_static, build_forged, local_cycler));
gulp.watch("./src/css/styles.css", gulp.series(build_styles, build_static, build_forged, local_cycler));
gulp.watch("./src/js/**/*", gulp.series(build_ncode, build_bundle, build_static, build_forged, local_cycler));
gulp.watch("./src/img/**/*", build_images);
gulp.watch(
[
"app/_includes/**/*",
"app/_layouts/**/*",
"app/_data/**/*",
"app/_posts/**/*",
"app/_drafts/**/*",
"app/pages/**/*",
"app/_category/**/*",
"app/_grain-of-destiny/**/*"
],
gulp.series(build_static, build_forged, local_cycler)
);
}
//
// COMPLEX TASKS
//
// these tasks manage assets, and are called by the other tasks.
const pack_styles = gulp.series(clean_styles, gulp.parallel(build_normal, build_styles, build_custom));
const pack_script = gulp.series(clean_script, gulp.parallel(build_ncode, build_bundle));
const pack_images = gulp.series(clean_images, build_images);
// this task starts watching the project and running tasks on file changes.
const watch = gulp.parallel(local_browse, watch_mywork);
// this task forces a complete build in the development environment.
const pack_tester =
gulp.series(
clean_folder, // clear workspace
gulp.parallel(pack_styles, pack_script, pack_images), // build css, jss, & images
build_static, // generate html via jekyll
build_forged, // minify html, include js and external css
local_cycler // websync update
);
// use this to force a complete build in the production environment.
const pack_server =
gulp.series(
clean_folder, // clear workspace
gulp.parallel(pack_script, pack_images), // build js & images
gulp.parallel(build_normal, build_custom, build_master), // build base css
build_jekyll, // generate html via jekyll
build_purged, // build css w/ only used styles
build_forged, // minify html, include js and external css
build_nstyle // inline critical styles
);
//
// EXPORTS
//
exports.watch = watch;
exports.clean = clean_folder;
exports.styles = pack_styles;
exports.script = pack_script;
exports.images = pack_images;
exports.tester = pack_tester;
exports.server = pack_server;
@hildissent
Copy link
Author

Recently rebuilt this to try and streamline my build process. I broke a bunch of stuff out into individual functions, as I'm still playing with my build process and wanted to remain flexible enough to shift the order around without having to completely rebuild functions every time I incorporate a new life lesson.
I'm no javascript dev. I've only been at this for a few weeks now, so please forgive any strange redundancy or unnecessary bits. For now, I'm just proud I got this together and mostly functional (using multiple examples as my guide).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment