Browser Sync + Gulp + Jade, Why separate the Jade watch task? - javascript

I was looking at this browser sync recipe which is a gulpfile configuration that works with jade, sass and browser sync, I don't care about sass so to simplify I modified the code a little:
var gulp = require('gulp');
var browserSync = require('browser-sync');
var jade = require('gulp-jade');
var reload = browserSync.reload;
/**
* Compile jade files into HTML
*/
gulp.task('templates', function() {
return gulp.src('./app/*.jade')
.pipe(jade())
.pipe(gulp.dest('./dist/'));
});
/**
* Important!!
* Separate task for the reaction to `.jade` files
*/
gulp.task('jade-watch', ['templates'], reload);
/**
* Serve and watch the jade files for changes
*/
gulp.task('default', ['templates'], function () {
browserSync({server: './dist'});
gulp.watch('./app/*.jade', ['jade-watch']);
});
What I don't understand is this comment:
/**
* Important!!
* Separate task for the reaction to `.jade` files
*/
Why is this important? Why not just do this?
/**
* Compile jade files into HTML
*/
gulp.task('templates', function() {
return gulp.src('./app/*.jade')
.pipe(jade())
.pipe(gulp.dest('./dist/'))
.pipe(reload({stream: true}));
});
/**
* Serve and watch the jade files for changes
*/
gulp.task('default', ['templates'], function () {
browserSync({server: './dist'});
gulp.watch('./app/*.jade', ['templates']);
});

You might have figured this out by now; but in case anyone else comes along wondering the same thing (as I did): by setting the 'templates' task as a dependency of 'jade-watch' you ensure it has completed before triggering reload.

Related

Modify gulpfile to read html files in subfolder and spit them out to build folder

I have been working on modifying this relatively simple gulpfile/project: https://github.com/ispykenny/sass-to-inline-css
The first issue I had was to update to gulp v4, but I've also tried to store variables for my src and destination folders which is a bit easier to control. So now my gulpfile looks like this:
const gulp = require('gulp');
const inlineCss = require('gulp-inline-css');
const sass = require('gulp-sass');
const browserSync = require('browser-sync').create();
const plumber = require('gulp-plumber');
const del = require('del');
const srcFolder = './src'; // TODO tidy this up once working
const buildFolder = srcFolder + '/build/'; // Tidy this up once working
const src = {
scss: 'src/scss/**/*.scss',
templates: 'src/templates/**/*.html'
}
const dest = {
build: 'build/',
css: 'build/css'
};
function processClean() {
return del(`${buildFolder}**`, { force: true });
}
function processSass() {
return gulp
.src(src.scss)
.pipe(plumber())
.pipe(sass())
.pipe(gulp.dest(dest.css))
.pipe(browserSync.stream())
}
function processInline() {
return gulp
.src('./*.html')
.pipe(inlineCss({
removeHtmlSelectors: true
}))
.pipe(gulp.dest('build/'))
}
function processWatch() {
gulp.watch(['./src/scss/**/*.scss'], processSass);
gulp.watch(srcFolder).on('change', browserSync.reload);
gulp.watch(distFolder).on('change', browserSync.reload);
}
const buildStyles = gulp.series(processSass, processInline);
const build = gulp.parallel(processClean, buildStyles);
gulp.task('clean', processClean);
gulp.task('styles', buildStyles);
gulp.task('sass', processSass);
gulp.task('inline', processInline);
gulp.task('build', build);
gulp.task('watch', processWatch);
But I am now wanting to create lots of template files, store them in a subfolder and have gulp spit out each file into the destination folder. if I have index.html, test1.html etc in the root it works fine.
I tried modifying this:
function processInline() { return gulp.src('./*.html')
To this:
function processInline() { return gulp.src(src.templates) // should equate to 'src/templates/**/*html'
Now I'm seeing this error in the console:
ENOENT: no such file or directory, open 'C:\Users\myuser\pathToApp\emailTemplates\src\templates\build\css\style.css'
In the head of index.html in the root is this:
<link rel="stylesheet" href="build/css/style.css">
I actually don't really care about the css file as the final output should be inline (for email templates). But I cannot get my head around why this is happening.
Does gulp create the css file and then read the class names from there? EDIT, Ah I guess it must because it has to convert the sass to readable css first before stripping out the class names and injecting the inline styles.
Years ago I worked with grunt a fair bit, and webpack, but haven't done much with gulp.
I hope it is obvious, but if you need more information just let me know.

Cordova / Gulp best practice

I'm trying to setup a Cordova develop/deployment chain using Gulp. I ended with this gulpfile.js, but I'm not really satisfied since I need to kill "gulp watch" task in order to run "gulp deploy" task.
var gulp = require('gulp'),
gutil = require('gulp-util'),
exec = require('gulp-exec');
var spawn = require('child_process').spawn;
var stripDebug = require('gulp-strip-debug');
var uglify = require('gulp-uglify');
/**
* Config ogj
*/
var config = {
jsDir: 'www/assets/js',
jsDirBrowser: 'platforms/browser/www/assets/js',
production: !!gutil.env.production
};
/**
* Automatically run 'cordova prepare browser' after any modification
* into the www directory - really useful for development/deplyment purpose
*
* #see watch task
*/
gulp.task('prepare', function () {
gutil.log('Prepare browser');
var options = {
continueOnError: false, // default = false, true means don't emit error event
pipeStdout: false, // default = false, true means stdout is written to file.contents
customTemplatingThing: "test" // content passed to gutil.template()
};
var reportOptions = {
err: true, // default = true, false means don't write err
stderr: true, // default = true, false means don't write stderr
stdout: true // default = true, false means don't write stdout
}
return gulp.src('./**/**')
.pipe(exec('cordova prepare browser', options))
.pipe(exec.reporter(reportOptions));
});
/**
* Watch for changes in www
*/
gulp.task('watch', function () {
gulp.watch('www/**/*', ['prepare']);
});
/**
* Default task
*/
gulp.task('default', ['prepare']);
/**
* Javascript production depolyment.
*/
gulp.task('deploy-js', function () {
gutil.log('Deploy');
return gulp.src(config.jsDir + '/*.js')
.pipe(stripDebug())
.pipe(uglify())
.pipe(gulp.dest(config.jsDirBrowser));
});
/**
* Production deployment
* To be run before uploading files to the server with no gulp instaces running
*/
gulp.task('deploy', ['deploy-js']);
Which could be a best practice for develop and deply a Cordova project using Gulp?
[EDIT]
I think the problem is in the "prepare" task: it never returns, probably due a gulp-exec issue, but I really don't know how to debug it.
From what I've understood the only issue is that gulp command does not return control to you for you to execute gulp deploy
the prepare task never returns because of the behavior of the watch feature - you've passed control to watch the files so it will return only when you stop watching. It is the expected behavior and not a probably due a gulp-exec issue.
The solution I would adopt in this situation is to run the gulp task in the background, using the native nohup gulp & so that the watching is moved to the background for me to execute deploy.

minify css/js into their individual files

I have this default gulp file from a Visual Studio template:
/// <binding BeforeBuild='clean, minPreBuild' />
"use strict";
var gulp = require("gulp"),
rimraf = require("rimraf"),
concat = require("gulp-concat"),
cssmin = require("gulp-cssmin"),
uglify = require("gulp-uglify");
var webroot = "./wwwroot/";
var paths = {
js: webroot + "js/**/*.js",
minJs: webroot + "js/**/*.min.js",
css: webroot + "css/**/*.css",
minCss: webroot + "css/**/*.min.css",
concatJsDest: webroot + "js/_site.min.js",
concatCssDest: webroot + "css/_site.min.css"
};
gulp.task("clean:js", function (cb) {
rimraf(paths.concatJsDest, cb);
});
gulp.task("clean:css", function (cb) {
rimraf(paths.concatCssDest, cb);
});
gulp.task("clean", ["clean:js", "clean:css"]);
gulp.task("min:js", function () {
return gulp.src([paths.js, "!" + paths.minJs], { base: "." })
.pipe(concat(paths.concatJsDest))
.pipe(uglify())
.pipe(gulp.dest("."));
});
gulp.task("min:css", function () {
return gulp.src([paths.css, "!" + paths.minCss])
.pipe(concat(paths.concatCssDest))
.pipe(cssmin())
.pipe(gulp.dest("."));
});
gulp.task("min", ["min:js", "min:css"]);
gulp.task("minPreBuild", ["min:js", "min:css"]);
The problem I'm having is one of my js files in the directory has a dependency on knockout, but I'm only using knockout on one of the pages on the site. I don't want to include knockout on my shared view, and the default bundling all files into a single file causes a JS error "ko is undefined" as one of the JS files is dependent on KO.
Is there a way that I can minify files individually, without concatting it into the main "site.min.css"?
First you need to exclude the Knockout file from your min:js task. Prepending a path with ! tells gulp to ignore that file:
gulp.task("min:js", function () {
return gulp.src([
paths.js,
"!" + paths.minJs,
"!js/path/to/knockout.js" // don't include knockout in _site.min.js
], { base: "." })
.pipe(concat(paths.concatJsDest))
.pipe(uglify())
.pipe(gulp.dest("."));
});
Then you need to create a new task min:knockout that does nothing but minify your Knockout file. You'll probably want the minified file to end with a .min.js extension so you'll have to install the gulp-rename plugin as well.
var rename = require('gulp-rename');
gulp.task("min:knockout", function () {
return gulp.src("js/path/to/knockout.js", { base: "." })
.pipe(rename("js/_knockout.min.js"))
.pipe(uglify())
.pipe(gulp.dest("."));
});
Finally you need to make sure your new min:knockout task is executed when running the min and minPreBuild tasks:
gulp.task("min", ["min:js", "min:knockout", "min:css"]);
gulp.task("minPreBuild", ["min:js", "min:knockout", "min:css"]);

Get Gulp watch to perform function only on changed file

I am new to Gulp and have the following Gulpfile
var gulp = require('gulp');
var jshint = require('gulp-jshint');
var concat = require('gulp-concat');
var rename = require('gulp-rename');
var uglify = require('gulp-uglify');
gulp.task('compress', function () {
return gulp.src('js/*.js') // read all of the files that are in js with a .js extension
.pipe(uglify()) // run uglify (for minification)
.pipe(gulp.dest('dist/js')); // write to the dist/js file
});
// default gulp task
gulp.task('default', function () {
// watch for JS changes
gulp.watch('js/*.js', function () {
gulp.run('compress');
});
});
I would like to configure this to rename, minify and save only my changed file to the dist folder. What is the best way to do this?
This is how:
// Watch for file updates
gulp.task('watch', function () {
livereload.listen();
// Javascript change + prints log in console
gulp.watch('js/*.js').on('change', function(file) {
livereload.changed(file.path);
gutil.log(gutil.colors.yellow('JS changed' + ' (' + file.path + ')'));
});
// SASS/CSS change + prints log in console
// On SASS change, call and run task 'sass'
gulp.watch('sass/*.scss', ['sass']).on('change', function(file) {
livereload.changed(file.path);
gutil.log(gutil.colors.yellow('CSS changed' + ' (' + file.path + ')'));
});
});
Also great to use gulp-livereload with it, you need to install the Chrome plugin for it to work btw.
See incremental builds on the Gulp docs.
You can filter out unchanged files between runs of a task using the gulp.src function's since option and gulp.lastRun

Compile JavaScripts with Gulp and Resolve Dependencies (separate files)

I want to compile JavaScript files with Gulp.
I have a src directory where all scripts are present with .js extension. I want all scripts to be compiled separately and placed into a destination directory (dist) with the same filename as the original.
Consider this example:
src/jquery.js:
/**
* #require ../../vendor/jquery/dist/jquery.js
*/
src/application.js:
/**
* #require ../../vendor/angular/angular.js
* #require ../../vendor/ngprogress-lite/ngprogress-lite.js
* #require ../../vendor/restangular/dist/restangular.js
* #require ../../vendor/lodash/dist/lodash.underscore.js
* #require ../../vendor/angular-input-locker/dist/angular-input-locker.js
* #require ../../vendor/angular-route/angular-route.js
*/
(function(document, angular) {
'use strict';
var moduleName = 'waApp';
angular.module(moduleName, [
// Some more code here.
;
// Bootstrapping application when DOM is ready.
angular.element(document).ready(function() {
angular.bootstrap(document, [moduleName]);
});
})(document, angular);
I'm using gulp-resolve-dependencies to resolve dependencies specified in the header of each source JavaScript file.
My gulpfile.js is looking like this:
//==============//
// Dependencies //
//==============//
var gulp = require('gulp');
var pathModule = require('path');
var resolveDependencies = require('gulp-resolve-dependencies');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
//=======//
// TASKS //
//=======//
gulp.task('build:scripts', function(callback) {
return gulp.src('scripts/*.js')
.pipe(resolveDependencies({
pattern: /\* #require [\s-]*(.*?\.js)/g,
log: true
}))
.pipe(concat('all.js'))
.pipe(uglify())
.pipe(gulp.dest('js/'))
;
});
In order to merge scripts resolved by resolveDependencies I have to use concat, but concat requires a filename and merges not only original file and dependencies resolved for it, but all JavaScript files specified via glob pattern.
So, How do I get individual JavaScript files as the output? Like this:
dist/jquery.js:
src/jquery.js
vendor/jquery.js
dist/application.js:
src/application.js
vendor/angular.js
vendor/ngprogress-lite.js
...
I have this workaround for now:
gulp.task('build:scripts', function(callback) {
var compileScript = function(stream, filename) {
return stream
.pipe(resolveDependencies({
pattern: /\* #require [\s-]*(.*?\.js)/g,
log: true
}))
.pipe(concat(filename))
.pipe(uglify())
.pipe(gulp.dest('dist/'))
;
};
var scripts = getListOfFiles('src/', 'js');
for (key in scripts) {
var filename = scripts[key];
var stream = gulp.src(pathModule.join('src/', filename));
compileScript(stream, filename);
}
callback(null);
});
//===================//
// FUNCTIONS & UTILS //
//===================//
/**
* Returns list of files in the specified directory
* with the specified extension.
*
* #param {string} path
* #param {string} extension
* #returns {string[]}
*/
function getListOfFiles(path, extension) {
var list = [];
var files = fs.readdirSync(path);
var pattern = new RegExp('.' + extension + '$');
for (var key in files) {
var filename = files[key];
if (filename.match(pattern)) {
list.push(filename);
}
}
return list;
}
But it looks hackish and I can't find a good way to make it work with gulp-watch.
Is there a better and simpler way to solve this problem and achieve desired result?
How do I get individual JavaScript files as the output?
Check an answer I gave to a similar problem here: Pass random value to gulp pipe template
Using this gulp plugin: https://github.com/adam-lynch/glob-to-vinyl
You can have access to single files.
This is how (assuming the use of this plugin):
function compileScript(file) {
gulp
.src('file')
.pipe(resolveDependencies({
pattern: /\* #require [\s-]*(.*?\.js)/g,
log: true
}))
.pipe(concat())
.pipe(uglify())
.pipe(gulp.dest('dist/'))
;
};
gulp.task('build:scripts', function() {
globToVinyl('src/**/*.js', function(err, files){
for (var file in files) {
compileScript(files[file].path);
}
});
});
Here's the result of rewriting my task using solution specified by #avcajaraville. It's a complete, tested and working code.
var targetDir = 'web';
var sourceDir = 'assets';
var gulp = require('gulp');
var pathModule = require('path');
var resolveDependencies = require('gulp-resolve-dependencies');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var globToVinyl = require('glob-to-vinyl');
gulp.task('build:scripts', function(callback) {
var compileScript = function(filePath, destinationDirectory) {
// Extracting filename from absolute path (required by concat).
var filename = pathModule.basename(filePath);
return gulp
.src(filePath)
.pipe(resolveDependencies({
pattern: /\* #require [\s-]*(.*?\.js)/g,
log: true
}))
.pipe(concat(filename))
.pipe(uglify())
.pipe(gulp.dest(destinationDirectory))
;
};
var sourceGlob = pathModule.join(sourceDir, '/scripts/*.js');
var destinationDirectory = pathModule.join(targetDir, '/js/');
globToVinyl(sourceGlob, function(errors, files) {
for (var file in files) {
compileScript(files[file].path, destinationDirectory);
}
});
callback(null);
});

Categories