I'm looking to extend a zurb foundation for sites 6 site and use jekyll instead of panini to render the HTML. I'm using the out of the box ZURB foundation prototype template that includes ES6 + webpack. I want foundation to handle all the SASS and JS compiling and I also want to retain the browsersync functionality. I just want to know the best approach for modifying the gulp file to integrate jekyll, this is so I can work with GitHub Pages.
Here is what the default gulp.babel.js file looks like:
'use strict';
import plugins from 'gulp-load-plugins';
import yargs from 'yargs';
import browser from 'browser-sync';
import gulp from 'gulp';
import panini from 'panini';
import rimraf from 'rimraf';
import sherpa from 'style-sherpa';
import yaml from 'js-yaml';
import fs from 'fs';
import webpackStream from 'webpack-stream';
import webpack2 from 'webpack';
import named from 'vinyl-named';
// Load all Gulp plugins into one variable
const $ = plugins();
// Check for --production flag
const PRODUCTION = !!(yargs.argv.production);
// Load settings from settings.yml
const { COMPATIBILITY, PORT, UNCSS_OPTIONS, PATHS } = loadConfig();
function loadConfig() {
let ymlFile = fs.readFileSync('config.yml', 'utf8');
return yaml.load(ymlFile);
}
// Build the "dist" folder by running all of the below tasks
gulp.task('build',
gulp.series(clean, gulp.parallel(pages, sass, javascript, images, copy), styleGuide));
// Build the site, run the server, and watch for file changes
gulp.task('default',
gulp.series('build', server, watch));
// Delete the "dist" folder
// This happens every time a build starts
function clean(done) {
rimraf(PATHS.dist, done);
}
// Copy files out of the assets folder
// This task skips over the "img", "js", and "scss" folders, which are parsed separately
function copy() {
return gulp.src(PATHS.assets)
.pipe(gulp.dest(PATHS.dist + '/assets'));
}
// Copy page templates into finished HTML files
function pages() {
return gulp.src('src/pages/**/*.{html,hbs,handlebars}')
.pipe(panini({
root: 'src/pages/',
layouts: 'src/layouts/',
partials: 'src/partials/',
data: 'src/data/',
helpers: 'src/helpers/'
}))
.pipe(gulp.dest(PATHS.dist));
}
// Load updated HTML templates and partials into Panini
function resetPages(done) {
panini.refresh();
done();
}
// Generate a style guide from the Markdown content and HTML template in styleguide/
function styleGuide(done) {
sherpa('src/styleguide/index.md', {
output: PATHS.dist + '/styleguide.html',
template: 'src/styleguide/template.html'
}, done);
}
// Compile Sass into CSS
// In production, the CSS is compressed
function sass() {
return gulp.src('src/assets/scss/app.scss')
.pipe($.sourcemaps.init())
.pipe($.sass({
includePaths: PATHS.sass
})
.on('error', $.sass.logError))
.pipe($.autoprefixer({
browsers: COMPATIBILITY
}))
// Comment in the pipe below to run UnCSS in production
//.pipe($.if(PRODUCTION, $.uncss(UNCSS_OPTIONS)))
.pipe($.if(PRODUCTION, $.cleanCss({ compatibility: 'ie9' })))
.pipe($.if(!PRODUCTION, $.sourcemaps.write()))
.pipe(gulp.dest(PATHS.dist + '/assets/css'))
.pipe(browser.reload({ stream: true }));
}
let webpackConfig = {
rules: [
{
test: /.js$/,
use: [
{
loader: 'babel-loader'
}
]
}
]
}
// Combine JavaScript into one file
// In production, the file is minified
function javascript() {
return gulp.src(PATHS.entries)
.pipe(named())
.pipe($.sourcemaps.init())
.pipe(webpackStream({module: webpackConfig}, webpack2))
.pipe($.if(PRODUCTION, $.uglify()
.on('error', e => { console.log(e); })
))
.pipe($.if(!PRODUCTION, $.sourcemaps.write()))
.pipe(gulp.dest(PATHS.dist + '/assets/js'));
}
// Copy images to the "dist" folder
// In production, the images are compressed
function images() {
return gulp.src('src/assets/img/**/*')
.pipe($.if(PRODUCTION, $.imagemin({
progressive: true
})))
.pipe(gulp.dest(PATHS.dist + '/assets/img'));
}
// Start a server with BrowserSync to preview the site in
function server(done) {
browser.init({
server: PATHS.dist, port: PORT
});
done();
}
// Reload the browser with BrowserSync
function reload(done) {
browser.reload();
done();
}
// Watch for changes to static assets, pages, Sass, and JavaScript
function watch() {
gulp.watch(PATHS.assets, copy);
gulp.watch('src/pages/**/*.html').on('all', gulp.series(pages, browser.reload));
gulp.watch('src/{layouts,partials}/**/*.html').on('all', gulp.series(resetPages, pages, browser.reload));
gulp.watch('src/assets/scss/**/*.scss').on('all', sass);
gulp.watch('src/assets/js/**/*.js').on('all', gulp.series(javascript, browser.reload));
gulp.watch('src/assets/img/**/*').on('all', gulp.series(images, browser.reload));
gulp.watch('src/styleguide/**').on('all', gulp.series(styleGuide, browser.reload));
}
I assume as part of the jekyll build/rebuild I would need to use the keep-files config setting so when jekyll clobbers the output directory file don't get overwritten.
Appreciate any help
Thanks
Jekyll is a different solution than Panini and uses Ruby
https://jekyllrb.com/docs/installation/
In general you might need some git hook or Travis config.
https://github.com/DanielRuf/testblog/blob/source/.travis.yml
https://github.com/DanielRuf/testblog/blob/source/Rakefile
I'm using Gulp to automatically copy/bundle/compile my files, I basically copy the entire folder structure from the base folder (MyProject/Scripts) to wwwroot/js.
In the process of copying/processing I want to rename the output path/filenames to lowercase. I found the ChangeCase-module and the Rename-module but I can't get it to work in my setup below.
gulp.task("compile:less", function () {
return gulp.src(paths.lessSrc)
.pipe(less())
//how can I get to currentdirectory so I can use it for the rename? maybe there is another way
//.pipe(rename({ dirname: changeCase.lowerCase(??currentdir??) }))
.pipe(gulp.dest(paths.cssTarget));
});
and
gulp.task("compile:js", function () {
return gulp.src(paths.jsOrigin)
//simple copy of folders and files but how to rename all to lowercase?
.pipe(gulp.dest(paths.jsTarget));
});
You can pass a callback function to gulp-rename:
gulp.task("compile:js", function () {
return gulp.src(paths.jsOrigin)
.pipe(rename(function(path) {
path.dirname = changeCase.lowerCase(path.dirname);
path.basename = changeCase.lowerCase(path.basename);
path.extname = changeCase.lowerCase(path.extname);
}))
.pipe(gulp.dest(paths.jsTarget));
});
I'm creating 3 minified bundles for my application. I have 2 tasks to do this, minify, and bundle. Minify has a dependency on bundle. If I run minify, both tasks run with no errors. The bundles are created, but the minified files are not. If I remove the dependency on bundle, I can then run minify by itself and the minified files are created successfully. This leads me to believe maybe the files are in use when the minify task triggers (because bundle hasn't finished?). How do I cause it to wait until the files are fully ready? Can I pass the stream? Or maybe combine these into a single task? The reason they are not currently a single task is because they output 2 files per bundle (an unminified and a minified bundle).
var outFolder = __dirname + '\\Scripts\\dist';
var appBundles = [
{ scripts: ['Scripts/Common/**/*.js'], output: 'eStore.common.js' },
{ scripts: ['Scripts/Checkout/**/*.js'], output: 'eStore.checkout.js' },
{ scripts: ['Scripts/ProductDetail/**/*.js'], output: 'eStore.product.js' }
];
gulp.task('bundle', bundle);
gulp.task('minify', ['bundle'], minify); // this one doesn't work
gulp.task('minifyOnly', minify); // this one works
function bundle() {
appBundles.forEach(function (appBundle) {
gulp.src(appBundle.scripts)
.pipe(concat(appBundle.output))
.pipe(sourcemaps.init())
.pipe(sourcemaps.write(outFolder + '\\maps'))
.pipe(gulp.dest(outFolder))
.on('error', errorHandler);
});
}
function minify() {
appBundles.forEach(function(appBundle) {
var bundleSrc = outFolder + '\\' + appBundle.output;
gulp.src(bundleSrc)
.pipe(rename({ extname: '.min.js' }))
.pipe(uglify())
.pipe(gulp.dest(outFolder))
.on('error', errorHandler);
});
}
Have the minify task use the same source files that the bundle task uses. 'concat' will be used in both tasks. This way minify doesn't have a dependency on the output from the bundle task.
function minify() {
appBundles.forEach(function (appBundle) {
console.log('Creating minified bundle for: ' + appBundle.output);
gulp.src(appBundle.scripts)
.pipe(concat(appBundle.output))
.pipe(rename({ extname: '.min.js' }))
.pipe(uglify())
.pipe(gulp.dest(outFolder))
.on('error', errorHandler);
});
}
function bundle() {
appBundles.forEach(function (appBundle) {
console.log('Creating bundle and sourcemaps: ' + appBundle.output);
gulp.src(appBundle.scripts)
.pipe(concat(appBundle.output))
.pipe(sourcemaps.init())
.pipe(sourcemaps.write(outFolder + '\\maps'))
.pipe(gulp.dest(outFolder))
.on('error', errorHandler);
});
}
I need to use browserify-shim for some of my browserify dependencies, but I can't edit my package.json. I'm using browserify inside of gulp. It is possible to specify all of the package.json portion exclusibely form the API? I'm envisioning something like this:
return gulp.src('glob/path/to/my/main/js/file.js')
.pipe(browserify({
debug: false,
transform: ['browserify-shim'],
shim: {
'jquery': {
exports: 'jQuery'
}
}
}));
Then, my output with repalce var $ = require('jquery'); with var $ = jQuery;, since we are now utilizing jQuery as a global. Is this possible? Whne
You could use exposify transform in that case. browserify-shim is actially based on it and is written by the same guy.
E.g.:
return browserify('glob/path/to/my/main/js/file.js', { debug: false })
.transform('exposify', { expose: {'jquery': 'jQuery' }})
gulpfile.js
gulp = require('gulp')
browserify = require('gulp-browserify')
gulp.task('browserify', function(){
var src = 'test.js'
gulp.src(src)
.pipe(browserify({
shim: {
jQuery: {
path: './bowser_components/jquery/dist/jquery.min.js',
exports: '$'
}
}
}))
.pipe(gulp.dest('./build/js'))
})
test.js
$ = require('jQuery')
console.log($);
The problem: I want to maintain 'collections' of files. This will help with build times, and flexibility. for example, everytime i edit my app.js file, I don't want to re-compile all my twitter bootstrap files.
I can certainly achieve this with 2 tasks and 2 watch declarations - the problem is that the tasks are identical save for the files array. Ideally I would like to pass through these as parameters in the watch declaration Is there a way to do something like the following psuedo-code?:
var files = {
scripts: [
'www/assets/scripts/plugins/**/*.js',
'www/assets/scripts/main.js',
],
vendor: [
'vendor/jquery/dist/jquery.js',
'vendor/jqueryui/ui/jquery.ui.widget.js',
'vendor/holderjs/holder.js'
],
};
...
gulp.task('js', ['lint'], function (files, output) {
return gulp.src(files)
.pipe(debug())
.pipe(concat(output))
.pipe(uglify({outSourceMap: true}))
.pipe(gulp.dest(targetJSDir))
.pipe(notify('JS minified'))
.on('error', gutil.log)
});
...
gulp.watch('scripts/**/*.js', ['lint', 'js'], files.scripts, 'app.min.js');
gulp.watch('vendor/**/*.js', ['lint', 'js'], files.vendor, 'vendor.min.js');
Flipping round another way: is to namespace the watch declaration that called the task? That way I could check which watch triggered the task, and conditional those things within the task itself.
the problem is that the tasks are identical save for the files array.
I believe lazypipe (see its gh page) is well
suited to your wants. This was an interesting problem. I'm going to try to answer both what I think you're asking about (which is satisfied by lazypipe) as well as what I think you're probably thinking about or would end up thinking about if you got past the parameterization of pipes issue.
One aspect of what we want is that we don't want to rerun jshint on files that haven't changed. Additionally, we want to keep it DRY, and we want to pick up new files in addition to changed ones.
This is tested and works for me:
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var es = require('event-stream');
var lazypipe = require('lazypipe');
var gutil = require('gulp-util');
var path = require('path');
var files = {
scripts: ['src/**/*.js'],
vendor: ['vendor/**/*.js']
};
// sets up a lazy pipe that does jshint related stuff
function getJsMultiPipe(name) {
return lazypipe()
.pipe($.jshint)
.pipe($.jshint.reporter, 'jshint-stylish')
// if you don't want to fail on style errors remove/comment this out:
.pipe($.jshint.reporter, 'fail');
}
// sets up a lazy pipe that does concat and post-concat stuff
function getJsCombinedPipe(groupName, outfile) {
return lazypipe()
.pipe($.concat, outfile)
.pipe($.uglify, {outSourceMap: true})
.pipe(gulp.dest, 'build')
.pipe($.notify, {message: groupName + ' JS minified', onLast: true});
}
// sets up a pipe for the initial build task, combining the above two pipes
function getBuildPipe(groupName, outfile) {
return gulp.src(files[groupName])
.pipe(getJsMultiPipe(groupName)())
.pipe(getJsCombinedPipe(groupName, outfile)());
}
// sets up a watch pipe, such that only the changed file is jshinted,
// but all files are included in the concat steps
function setWatchPipe(groupName, outfile) {
return $.watch({
glob: files[groupName],
name: groupName,
emitOnGlob: false,
emit: 'one'
}, function(file, done) {
return file
.pipe($.debug({title: 'watch -- changed file'}))
.pipe(getJsMultiPipe(groupName)())
// switch context
.pipe(gulp.src(files[groupName]))
.pipe($.debug({title: 'watch -- entire group'}))
.pipe(getJsCombinedPipe(groupName, outfile)())
.pipe($.debug({title: 'watch -- concatted/source-mapped'}))
.pipe($.notify({message: 'JS minified', onLast: true}));
});
}
// task to do an initial full build
gulp.task('build', function() {
return es.merge(
getBuildPipe('scripts', 'app.min.js'),
getBuildPipe('vendor', 'vendor.min.js')
)
.pipe($.notify({message: 'JS minified', onLast: true}));
});
// task to do an initial full build and then set up watches for
// incremental change
gulp.task('watch', ['build'], function(done) {
setWatchPipe('scripts', 'app.min.js');
setWatchPipe('vendor', 'vendor.min.js');
done();
});
My dependencies look like:
"devDependencies": {
"jshint-stylish": "^0.1.5",
"gulp-concat": "^2.2.0",
"gulp-uglify": "^0.2.1",
"gulp-debug": "^0.3.0",
"gulp-notify": "^1.2.5",
"gulp-jshint": "^1.5.3",
"gulp": "^3.6.0",
"gulp-load-plugins": "^0.5.0",
"lazypipe": "^0.2.1",
"event-stream": "^3.1.1",
"gulp-util": "^2.2.14",
"gulp-watch": "^0.5.3"
}
EDIT: I just glanced at this again and I notice these lines:
// switch context
.pipe(gulp.src(files[groupName]))
Be aware that I believe the gulp.src API has changed since I wrote this, and that it currently doesn't switch the context when you pipe things into gulp.src, therefore this spot might require a change. For newer versions of gulp, I think what will happen is that you will be adding to the context, instead and presumably losing a small bit of efficiency.
You could write a wrapper function for tasks to capture parameters and pass it to the task. E.g. (with the help of the lodash library):
// We capture the options in this object. We use gulp.env as a base such that
// options from cli are also passed to the task.
var currentOpts = _.clone(gulp.env);
// Here we define a function that wraps a task such that it can receive
// an options object
function parameterized(taskFunc) {
return function() {
taskFunc.call(null, currentOpts);
}
}
// Here we create a function that can be used by gulp.watch to call
// a parameterized task. It can be passed an object of "task" : {options} pairs
// and it will return a task function that will capture these options
// before invoking the task.
function withArgs(tasks) {
return function() {
_.each(tasks, function (opts, task) {
currentOpts = _.extend(currentOpts, opts);
gulp.run(task);
currentOpts = _.clone(gulp.env);
});
}
}
var files = {
scripts : [ "src/**/*.js"],
vendor : ["vendor/**/*.js"
};
// We pass the task function to parameterized. This will create a wrapper
// function that will pass an options object to the actual task function
gulp.task("js", parameterized(function(opts) {
gulp.src(files[opts.target])
.pipe(concat(opts.output));
}));
gulp.task("watch", function() {
// The withArgs function creates a watch function that invokes
// tasks with an options argument
// In this case it will invoke the js task with the options object
// { target : "scripts", output : "scripts.min.js" }
gulp.watch(files.scripts, withArgs({
js : {
target : "scripts",
output : "scripts.min.js"
}
}));
gulp.watch(files.vendor, withArgs({
js : {
target : "vendor",
output : "vendor.min.js"
}
}));
});
I've faced the same problem - how to pass parameters to a gulp task. It's wierd that this feature is not builtin (it's such a common task to build, for instance, two versions of a package, parametrized task seems like a very DRY solution).
I wanted to make it as simple as possible, so my solution was to dynamically create tasks for an each possible parameter. It works ok if you have a small number of exactly defined values. It won't work for wide range values, like ints or floats.
The task definition is wrapped in a function that takes desired parameter and the parameter is appended to the task's name (with '$' between for convenience).
Your code could look like this:
function construct_js(myname, files, output) {
gulp.task('js$' + myname, ['lint'], function () {
return gulp.src(files)
.pipe(debug())
.pipe(concat(output))
.pipe(uglify({outSourceMap: true}))
.pipe(gulp.dest(targetJSDir))
.pipe(notify('JS minified'))
.on('error', gutil.log)
});
}
construct_js("app", files.scripts, 'app.min.js');
construct_js("vendor", files.vendor, 'vendor.min.js');
gulp.watch('scripts/**/*.js', ['lint', 'js$app']);
gulp.watch('vendor/**/*.js', ['lint', 'js$vendor']);
Or better, with a little change in the data definition, we invoke task generation in a loop (so if you add a new "version" in the configs array it will work right away:
var configs = [
{
name : "app",
output: 'app.min.js',
files: [ 'www/assets/scripts/plugins/**/*.js',
'www/assets/scripts/main.js',
]
},
{
name : "vendor",
output: 'vendor.min.js',
files: [ 'vendor/jquery/dist/jquery.js',
'vendor/jqueryui/ui/jquery.ui.widget.js',
'vendor/holderjs/holder.js'
]
}
];
function construct_js(taskConfig) {
gulp.task('js$' + taskConfig.name, ['lint'], function () {
return gulp.src(taskConfig.files)
.pipe(debug())
.pipe(concat(taskConfig.output))
.pipe(uglify({outSourceMap: true}))
.pipe(gulp.dest(targetJSDir))
.pipe(notify('JS minified'))
.on('error', gutil.log)
});
}
for (var i=0; i < configs.length; i++) {
construct_js(configs[i]);
}
If we use underscore for the last "for":
_(configs).each(construct_js);
I've used this approach in my project with good results.
I'd like to propose some alternatives. Suppose we have a task called build that we would like to conditionally uglify given a certain param.
The two approaches use two watches with a single build task.
Alternative #1:
You can use gulp-exec to fire up a task with parameters.
var exec = require('child_process').exec;
gulp.task('build', function(){
// Parse args here and determine whether to uglify or not
})
gulp.task('buildWithoutUglify' function(){
exec('gulp build --withoutUglify')
})
gulp.task('watch', function(){
gulp.watch(someFilePath, ['buildWithoutUglify'])
})
Please note that this approach is a bit slow since what it does is execute command line gulp.
Alternative #2:
Set a global variable:
var withUglify = false;
gulp.task('build', function(){
// Use something like ``gulp-if`` to conditionally uglify.
})
gulp.task('buildWithoutUglify' function(){
withUglify = true;
gulp.start('build');
})
gulp.task('watch', function(){
gulp.watch(someFilePath, ['buildWithoutUglify'])
})