Skip to content

Instantly share code, notes, and snippets.

@nickludlam
Created March 18, 2026 12:41
Show Gist options
  • Select an option

  • Save nickludlam/ccd2f5449fe2f7315302170cfdcb29ec to your computer and use it in GitHub Desktop.

Select an option

Save nickludlam/ccd2f5449fe2f7315302170cfdcb29ec to your computer and use it in GitHub Desktop.
A gulpfile which gives live reload functionality while developing a ghost theme hosted inside a docker container
import fs from 'fs';
import { series, watch, src, dest, parallel } from 'gulp';
import pump from 'pump';
import path from 'path';
import merge from 'merge-stream';
// gulp plugins and utils
import livereload from 'gulp-livereload';
import postcss from 'gulp-postcss';
import zip from 'gulp-zip';
import concat from 'gulp-concat';
import replace from 'gulp-replace';
import uglify from 'gulp-uglify';
import beeper from 'beeper';
// postcss plugins
import autoprefixer from 'autoprefixer';
import colorFunction from 'postcss-color-mod-function';
import cssnano from 'cssnano';
import easyimport from 'postcss-easy-import';
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const pkg = require('./package.json');
// Configuration Block
const LIVE_RELOAD_URL = 'http://mydevbox.local:35729/livereload.js';
const GHOST_THEME_PATH = path.join(process.cwd(), '../docker/docker-ghost-content/themes/my-ghost-theme');
// End Configuration Block
// Check the parent of GHOST_THEME_PATH exists
if (!fs.existsSync(path.dirname(GHOST_THEME_PATH))) {
console.error('Error: Parent directory of GHOST_THEME_PATH does not exist. You must install the theme manually first.');
process.exit(1);
}
function serve(done) {
livereload.listen();
done();
}
const handleError = (done) => {
return function (err) {
if (err) {
beeper();
}
return done(err);
};
};
function hbs(done) {
pump([
src(['*.hbs', 'partials/**/*.hbs'], { base: '.' }),
replace('{{LIVERELOAD_SCRIPT}}', '<script async src="' + LIVE_RELOAD_URL + '"></script>'),
dest(GHOST_THEME_PATH + '/'),
livereload()
], handleError(done));
}
function css(done) {
pump([
src('assets/css/*.css', { sourcemaps: true }),
postcss([
easyimport,
colorFunction(),
autoprefixer(),
cssnano()
]),
dest('assets/built/', { sourcemaps: '.' }),
dest(GHOST_THEME_PATH + '/'),
livereload()
], handleError(done));
}
function js(done) {
pump([
src([
// pull in lib files first so our own code can depend on it
'assets/js/lib/*.js',
'assets/js/*.js'
], { sourcemaps: true }),
concat('casper.js'),
uglify(),
dest('assets/built/', { sourcemaps: '.' }),
dest(GHOST_THEME_PATH + '/'),
livereload()
], handleError(done));
}
function copyToDockerGhost(done) {
// 1) Copy all non-HBS assets as-is (no string replacement on binary/text assets).
pump([
src([
'**',
'!node_modules', '!node_modules/**',
'!dist', '!dist/**',
'!yarn-error.log',
'!yarn.lock',
'!gulpfile.js',
'!gulpfile.mjs',
'!.git', '!.git/**',
'!*.hbs',
'!partials/**/*.hbs'
], { encoding: false }),
dest(GHOST_THEME_PATH)
], function (err) {
if (err) {
return handleError(done)(err);
}
// 2) Copy HBS templates with live-reload script replacement.
pump([
src(['*.hbs', 'partials/**/*.hbs'], { base: '.' }),
replace('{{LIVERELOAD_SCRIPT}}', '<script async src="' + LIVE_RELOAD_URL + '"></script>'),
dest(GHOST_THEME_PATH)
], handleError(done));
});
}
function zipper(done) {
const filename = pkg.name + '.zip';
// Copy non-HBS files as-is (binary-safe)
const nonHbs = src([
'**',
'!node_modules', '!node_modules/**',
'!dist', '!dist/**',
'!src', '!src/**',
'!.git', '!.git/**',
'!gulpfile.js',
'!gulpfile.mjs',
'!package-lock.json',
'!yarn.lock',
'!.DS_Store',
'!*.log',
'!.vscode', '!.vscode/**',
'!.env', '!.env.*',
'!*.hbs',
'!partials/**/*.hbs'
], { encoding: false });
// Copy HBS files with livereload script removed
const hbs = src(['*.hbs', 'partials/**/*.hbs'], { base: '.' })
.pipe(replace('{{LIVERELOAD_SCRIPT}}', ''));
// Merge both streams, zip, and output
return merge(nonHbs, hbs)
.pipe(zip(filename))
.pipe(dest('dist/'));
}
const cssWatcher = () => watch('assets/css/**', series(css, copyToDockerGhost));
const jsWatcher = () => watch('assets/js/**', js);
const hbsWatcher = () => watch(['*.hbs', 'partials/**/*.hbs'], series(hbs, copyToDockerGhost));
const watcher = parallel(cssWatcher, jsWatcher, hbsWatcher);
const build = series(css, js);
export { build };
const _zip = series(build, zipper);
export { _zip as zip };
export default series(build, copyToDockerGhost, serve, watcher);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment