Skip to content

Instantly share code, notes, and snippets.

@mherchel
Last active January 19, 2019 03:14
Show Gist options
  • Save mherchel/49a5d4e3beb30d777c38f8db6ed6fd4a to your computer and use it in GitHub Desktop.
Save mherchel/49a5d4e3beb30d777c38f8db6ed6fd4a to your computer and use it in GitHub Desktop.
/* eslint-env node, es6 */
/* global require */
'use strict';
/**
* Configuration
*/
// Load dependencies
const { parallel, series, src, dest, task, watch, } = require('gulp');
const fs = require('fs');
const path = require('path');
const gulpIf = require('gulp-if');
const sourceMaps = require('gulp-sourcemaps');
const sass = require('gulp-sass');
const sassGlobbing = require('gulp-sass-globbing');
const sassLint = require('gulp-sass-lint');
const postCss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const cssNano = require('cssnano');
const pxToRem = require('postcss-pxtorem');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
const eslint = require('gulp-eslint');
const rename = require('gulp-rename');
const del = require('del');
// File locations
const cssSource = './scss/**/*.scss';
const cssOutput = './css/';
const jsSource = './js/src/*.es6';
const jsOutput = './js/compiled/';
// @see https://github.com/gulpjs/gulp/blob/master/docs/recipes/running-task-steps-per-folder.md
function getFolders(dir) {
return fs.readdirSync(dir)
.filter(function (file) {
return fs.statSync(path.join(dir, file)).isDirectory();
});
}
const isDev = process.env.NODE_ENV === 'dev';
if (isDev) {
console.log('/************************\n * Compiling in DEV mode!\n */\n');
}
else {
console.log('/*************************\n * Compiling in PROD mode.\n */\n');
}
/**
* Sass Globbing
*/
// Will be used to ignore globbed files later
let globbedScssFiles = [];
/**
* Helper to create proper globbing config
*
* @param {string} folderName Name of folder to glob
* @param {string} fileName Optional, if not provided, will create a partial
* with an underscore followed by the filename of the folder
*/
const globScssFolder = (folderName, fileName) => {
// If no filename is given default to scss dir and prepend underscore
if (typeof fileName !== 'string') {
fileName = `${folderName}.glob.scss`;
}
// Adds partial to globbedScssFiles, which then gets excluded from being watched.
if (Object.values(globbedScssFiles).indexOf(`scss/${fileName}`) === -1) {
globbedScssFiles.push(`scss/${fileName}`);
}
// Guarantees that functions, mixins, variables will be available to Sass partial.
const globSignature = '@import \'global_include\';';
return src(`scss/${folderName}/**/_*.scss`)
.pipe(
sassGlobbing(
{
'path': fileName,
},
{
'useSingleQuotes': true,
'signature': globSignature,
}
)
)
.pipe(dest('scss'));
};
// This is the array that holds static list of directories to glob.
const sassGlobs = [
() => globScssFolder('base'),
() => globScssFolder('utility'),
() => globScssFolder('layout'),
];
// Loop through component subdirectories, and create a SCSS file for each directory.
// @todo This only happens when you start Gulp, so if you create a new directory, you need to
// stop / start the process.
const folders = getFolders('scss/component');
const componentGlobs = folders.map(function(folder) {
return () => globScssFolder('component/' + folder, 'component/' + folder + '.glob.scss');
});
// Add component globs into the sassGlobs array.
sassGlobs.push(componentGlobs);
const globScss = parallel(...sassGlobs);
// Delete any *.glob.scss files that are autogenerated.
const deleteGlobFiles = () => del('scss/**/*.glob.scss');
const processScss = () =>
src(cssSource)
// Lint first
.pipe(sassLint())
.pipe(sassLint.format())
// Uncomment the next line to fail when Scss is invalid.
// .pipe(sassLint.failOnError())
// Start compiling
.pipe(gulpIf(isDev, sourceMaps.init()))
.pipe(sass({
includePaths: ['scss']
}))
// By default, the CSS files have a .glob.css extention. Replace this with .css.
.pipe(rename(
path => path.basename = path.basename.replace('.glob', '')
))
.pipe(
postCss([
pxToRem({
'propList': ['*',],
}),
autoprefixer({
'browsers': '> 0.25%, last 2 versions, Firefox ESR, not dead',
}),
])
)
// Minify if production build
.pipe(gulpIf(!isDev, postCss([cssNano(),])))
.pipe(gulpIf(isDev, sourceMaps.write()))
.pipe(dest(cssOutput));
const compileCSS = series(globScss, processScss, deleteGlobFiles);
/**
* JS Compilation
*/
const compileJavascript = () =>
src(jsSource)
.pipe(gulpIf(isDev, sourceMaps.init()))
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.failAfterError())
.pipe(
babel({
'presets': ['@babel/preset-env',],
})
// Provide meaningful error output
.on('error', (error) => logError(error))
)
// Minify if production build
.pipe(gulpIf(!isDev, uglify()))
.pipe(gulpIf(isDev, sourceMaps.write()))
.pipe(dest(jsOutput));
/**
* Watch Files
*/
const watchFiles = (done) => {
// Prepend globbedFiles with ! so they aren't watched causing an infinite loop,
// then add the source files to the beginning of the array
const watchedScss = globbedScssFiles.map(file => `!${file}`);
watchedScss.unshift(cssSource);
// Do what we came here to do
watch(watchedScss, compileCSS);
watch(jsSource, compileJavascript);
done();
};
task('default', parallel(compileCSS, compileJavascript));
task('watch', series(globScss, watchFiles));
{
"name": "bitcrusher",
"version": "0.1.0",
"license": "GPLv2",
"engines": {
"node": ">=8.x.x"
},
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/preset-env": "^7.2.0",
"autoprefixer": "^9.4.2",
"cssnano": "^4.1.7",
"del": "^3.0.0",
"file-system": "^2.2.2",
"gulp": "^4.0.0",
"gulp-babel": "^8.0.0",
"gulp-eslint": "^5.0.0",
"gulp-if": "^2.0.2",
"gulp-postcss": "^8.0.0",
"gulp-rename": "^1.4.0",
"gulp-sass": "^4.0.2",
"gulp-sass-globbing": "0.0.2",
"gulp-sass-lint": "^1.4.0",
"gulp-sourcemaps": "^2.6.4",
"gulp-uglify": "^3.0.1",
"postcss-pxtorem": "^4.0.1"
},
"scripts": {
"gulp": "gulp",
"build": "gulp",
"build:dev": "NODE_ENV=dev gulp",
"watch": "NODE_ENV=dev gulp watch",
"clean": "NODE_ENV=dev gulp clean",
"start": "npm run watch"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment