Optimizing Gulp tasks - javascript

I have the following tasks as part of my gulp file:
gulp.task('jshint', function() {
return gulp.src(jsSrc)
.pipe(jshint())
.pipe(jshint.reporter('jshint-stylish'));
});
gulp.task('jscs', function() {
return gulp.src(jsSrc)
.pipe(jscs());
});
gulp.task('jslint', [ 'jshint', 'jscs' ]);
As I understand it, if I run jslint I'm not going to get the benefit of only reading from disc one. To do that, I would have to rewrite it to look like this:
gulp.task('jslint', function() {
return gulp.src(jsSrc)
.pipe(jshint())
.pipe(jscs())
.pipe(jshint.reporter('jshint-stylish'));
});
This is fine, except that it doesn't allow me to run the jshint task independently of the jscs if I wanted to.
Hence I'm wondering what best practice is around this? I was thinking that I could break things out into different functions and orchestrate things together, but that doesn't seem like the right approach.

A solution I have seen and used is:
var merge = require('merge-stream');
gulp.task('analyze', function() {
var jshint = analyzejshint(config.js);
var jscs = analyzejscs(config.css);
return merge(jshint, jscs);
});
function analyzejshint(sources, overrideRcFile) {
var jshintrcFile = overrideRcFile || './.jshintrc';
console.log('Running JSHint');
return gulp
.src(config.js)
.pipe(plug.jshint(jshintrcFile))
.pipe(plug.jshint.reporter('jshint-stylish'));
}
function analyzejscs(sources, overrideRcFile) {
var jscsrcFile = overrideRcFile || './.jscsrc';
console.log('Running JSCS');
return gulp
.src(config.js)
.pipe(jscs(jscsrcFile))
}
If you wanted to perform just one, you can just return the stream from one of the functions instead.

Related

How can I pass arbitrary parameters to my mocha tests with mochaPhantomJS & gulp?

I'm using gulp & mochaPhantomJS. I'd like to use the same test html and run different tests against it. How can I pass arbitrary parameters (by which I mean not the phantom args) that I can retrieve from within my test JavaScript?
var gulp = require('gulp');
var mochaPhantomJS = require('gulp-mocha-phantomjs');
gulp.task('testMisc', function() {
return gulp.src('testMisc.html')
.pipe(mochaPhantomJS());
});
But to be clear what I want to be able to do is
gulp.task('testMisc-1', function() {
return gulp.src('testMisc.html')
.pipe(mochaPhantomJS({whatever:1}));
});
gulp.task('testMisc-2', function() {
return gulp.src('testMisc.html')
.pipe(mochaPhantomJS({whatever:2}));
});

Chain Gulp glob to browserify transform

I have a project with a few relatively disjoint pages, each including their own entry point script. These scripts require a number of others using commonjs syntax, and need to be transformed by 6to5 and bundled by browserify.
I would like to set up a gulp task that captures all the files matching a pattern and passes them on to the bundler, but I'm not sure how to pass files from gulp.src to browserify(filename).
My gulpfile looks like:
var gulp = require("gulp");
var browserify = require("browserify");
var to5browserify = require("6to5-browserify");
var source = require("vinyl-source-stream");
var BUNDLES = [
"build.js",
"export.js",
"main.js"
];
gulp.task("bundle", function () {
/* Old version, using glob:
return gulp.src("src/** /*.js")
.pipe(sixto5())
.pipe(gulp.dest("dist"));
*/
// New version, using array:
return BUNDLES.map(function (bundle) {
return browserify("./src/" + bundle, {debug: true})
.transform(to5browserify)
.bundle()
.pipe(source(bundle))
.pipe(gulp.dest("./dist"));
});
});
gulp.task("scripts", ["bundle"]);
gulp.task("html", function () {
return gulp.src("src/**/*.html")
.pipe(gulp.dest("dist"));
});
gulp.task("styles", function () {
return gulp.src("src/**/*.css")
.pipe(gulp.dest("dist"));
});
gulp.task("default", ["scripts", "html", "styles"]);
This seems to work, but isn't maintainable: I'll be adding more scripts relatively soon, and don't want to add them to the array every time.
I've tried using gulp.src(glob).pipe within the browserify call and piping after calling (shown here), and gulp.src(glob).map (method doesn't exist).
How can you chain gulp.src with a name-based transformer like browserify?
Use through2 to make a one-off custom plugin stream that does all of the dirty work.
Unfortanately vinyl-transform and vinyl-source-stream and the solutions that go along with those have flaws so we have to go for something custom.
var gulp = require('gulp');
var through = require('through2');
var browserify = require('browserify');
gulp.task('bundle', function() {
var browserified = function() {
return through.obj(function(chunk, enc, callback) {
if(chunk.isBuffer()) {
var b = browserify(chunk.path);
// Any custom browserify stuff should go here
//.transform(to5browserify);
chunk.contents = b.bundle();
this.push(chunk);
}
callback();
});
};
return gulp.src(['./src/**/*.js'])
.pipe(browserified())
.pipe(gulp.dest('dest'));
});
You can specify globs in your BUNDLES array as well as exclude any files:
var BUNDLES = [
"app/**/*.js",
"export.js",
"app/modules/**/*.js",
"!app/modules/excluded/*.js"
];

How do I use gulp with tape?

I'm trying to integrate Gulp with Tape (https://github.com/substack/tape), the NodeJs test harness.
How can I do this? There doesn't seem to be an existing gulp plugin.
I've see this, but it looks really inelegant:
var shell = require('gulp-shell')
gulp.task('exec-tests', shell.task([
'tape test/* | faucet',
]));
gulp.task('autotest', ['exec-tests'], function() {
gulp.watch(['app/**/*.js', 'test/**/*.js'], ['exec-tests']);
});
I've tried this, which looks like it should work:
var tape = require('tape');
var spec = require('tap-spec');
gulp.task('test', function() {
return gulp.src(paths.serverTests, {
read: false
})
.pipe(tape.createStream())
.pipe(spec())
.pipe(process.stdout);
});
but I get a TypeError: Invalid non-string/buffer chunk error
Your "inelegant" answer is the best one. Not every problem can be best solved with streams, and using gulp just as a wrapper is not a sin.
Right, your task won't work because gulp streams are based on vinyl, a virtual file abstraction. I don't really think there's a good way of handling this in gulp, it seems like you should be using the tape API directly. I mean, you could put some gulp task sugar around it if you wish:
var test = require('tape');
var spec = require('tap-spec');
var path = require('path');
var gulp = require('gulp');
var glob = require('glob');
gulp.task('default', function () {
var stream = test.createStream()
.pipe(spec())
.pipe(process.stdout);
glob.sync('path/to/tests/**/*.js').forEach(function (file) {
require(path.resolve(file));
});
return stream;
});
Seems kind of messy to me; not only because we're not using any of gulp's streaming abstractions, but we're not even putting it into a way that could hook into a gulp pipeline afterwards. Furthermore, you can't get gulp's task finished message when using this code either. If anyone knows a way around that then, please, be my guest. :-)
I think I would prefer to use tape on the command line. But, if you want all of your build step tasks in your gulpfile this might be the route to go.
Just use code below and gulp tdd and having TDD :) with tape
const tapNotify = require('tap-notify');
const colorize = require('tap-colorize');
const tape = require('gulp-tape');
const through = require('through2');
gulp.task('test',function(){
process.stdout.write('\x1Bc');
const reporter = through.obj();
reporter.pipe(tapNotify({
passed: {title: 'ok', wait:false},
failed: {title: 'missing',wait:false}
}));
reporter
.pipe(colorize())
.pipe(process.stdout);
return gulp.src('test/**/*.js')
.pipe(tape({
outputStream: through.obj(),
reporter: reporter
}));
});
gulp.task('tdd', function() {
gulp.run('test');
gulp.watch(['app/scripts/**/*.js*', 'test/**/*.js'],['test']);
});
In a GitHub issue for tape jokeyrhyme mentions that gulp tasks can be Promises, and suggests a way to use that for running tape tests. Based upon that advice I've done this:
gulpfile.babel.js:
import glob from "glob";
gulp.task("test", () => {
let module = process.argv[process.argv.length - 1];
return new Promise(resolve => {
// Crude test for 'gulp test' vs. 'gulp test --module mod'
if (module !== "test") {
require(`./js/tape/${module}.js`);
resolve();
return;
}
glob.sync("./js/tape/*.js").forEach(f => require(f)));
resolve();
});
});
Looking at Ben's answer I suspect what I've done isn't very nice though, for one thing I've noticed that failing tests don't result in a non-zero exit code (although I've not tried Ben's approach to validate whether that does).
// npm i --save-dev gulp-tape
// npm i --save-dev faucet (just an example of using a TAP reporter)
import gulp from 'gulp';
import tape from 'gulp-tape';
import faucet from 'faucet';
gulp.task('test:js', () => {
return gulp.src('src/**/*test.js')
.pipe(tape({
reporter: faucet()
}));
});
Here's an example of my solution:
var gulp = require('gulp');
var tape = require('tape');
var File = require('vinyl');
var through = require('through2');
var exec = (require('child_process')).execSync;
function execShell(shcmd, opts) {
var out = '';
try {
out = exec(shcmd, opts);
} catch (e) {
if (e.error) throw e.error;
if (e.stdout) out = e.stdout.toString();
}
return out;
};
gulp.task('testreport', function(){
return gulp.src(
'testing/specs/tape_unit.js', {read: false}
).pipe(
through.obj(function(file, encoding, next) {
try{
// get tape's report
var tapout = execShell(
"./node_modules/.bin/tape " + file.path
);
// show the report in a console with tap-spec
execShell(
"./node_modules/.bin/tap-spec", { input: tapout, stdio: [null, 1, 2] }
);
// make a json report
var jsonout = execShell(
"./node_modules/.bin/tap-json", { input: tapout }
);
// do something with report's object
// or prepare it for something like Bamboo
var report = JSON.parse(jsonout.toString());
// continue the stream with the json report
next(null, new File({
path: 'spec_report.json',
contents: new Buffer(JSON.stringify(report, null, 2))
}));
}catch(err){ next(err) }
})
).pipe(
gulp.dest('testing/reports')
);
});

Iterating over directories with Gulp?

I'm new to gulp, but I'm wondering if its possible to iterate through directories in a gulp task.
Here's what I mean, I know a lot of the tutorials / demos show processing a bunch of JavaScript files using something like "**/*.js" and then they compile it into a single JavaScript file. But I want to iterate over a set of directories, and compile each directory into it's own JS file.
For instance, I have a file structure like:
/js/feature1/something.js
/js/feature1/else.js
/js/feature1/foo/bar.js
/js/feature1/foo/bar2.js
/js/feature2/another-thing.js
/js/feature2/yet-again.js
...And I want two files: /js/feature1/feature1.min.js and /js/feature2/feature2.min.js where the first contains the first 4 files and the second contains the last 2 files.
Is this possible, or am I going to have to manually add those directories to a manifest? It would be really nice to pragmatically iterate over all the directories within /js/.
Thanks for any help you can give me.
-Nate
Edit: It should be noted that I don't only have 2 directories, but I have many (maybe 10-20) so I don't really want to write a task for each directory. I want to handle each directory the same way: get all of the JS inside of it (and any sub-directories) and compile it down to a feature-based minified JS file.
There's an official recipe for this: Generating a file per folder
var fs = require('fs');
var path = require('path');
var merge = require('merge-stream');
var gulp = require('gulp');
var concat = require('gulp-concat');
var rename = require('gulp-rename');
var uglify = require('gulp-uglify');
var scriptsPath = 'src/scripts';
function getFolders(dir) {
return fs.readdirSync(dir)
.filter(function(file) {
return fs.statSync(path.join(dir, file)).isDirectory();
});
}
gulp.task('scripts', function() {
var folders = getFolders(scriptsPath);
var tasks = folders.map(function(folder) {
return gulp.src(path.join(scriptsPath, folder, '/**/*.js'))
// concat into foldername.js
.pipe(concat(folder + '.js'))
// write to output
.pipe(gulp.dest(scriptsPath))
// minify
.pipe(uglify())
// rename to folder.min.js
.pipe(rename(folder + '.min.js'))
// write to output again
.pipe(gulp.dest(scriptsPath));
});
// process all remaining files in scriptsPath root into main.js and main.min.js files
var root = gulp.src(path.join(scriptsPath, '/*.js'))
.pipe(concat('main.js'))
.pipe(gulp.dest(scriptsPath))
.pipe(uglify())
.pipe(rename('main.min.js'))
.pipe(gulp.dest(scriptsPath));
return merge(tasks, root);
});
You could use glob to get a list of directories and iterate over them, using gulp.src to create a separate pipeline for each feature. You can then return a promise which is resolved when all of your streams have ended.
var fs = require('fs');
var Q = require('q');
var gulp = require('gulp');
var glob = require('glob');
gulp.task('minify-features', function() {
var promises = [];
glob.sync('/js/features/*').forEach(function(filePath) {
if (fs.statSync(filePath).isDirectory()) {
var defer = Q.defer();
var pipeline = gulp.src(filePath + '/**/*.js')
.pipe(uglify())
.pipe(concat(path.basename(filePath) + '.min.js'))
.pipe(gulp.dest(filePath));
pipeline.on('end', function() {
defer.resolve();
});
promises.push(defer.promise);
}
});
return Q.all(promises);
});
I am trying myself to get how streams work in node.
I made a simple example for you, on how to make a stream to filter folders and start a new given stream for them.
'use strict';
var gulp = require('gulp'),
es = require('event-stream'),
log = require('consologger');
// make a simple 'stream' that prints the path of whatever file it gets into
var printFileNames = function(){
return es.map(function(data, cb){
log.data(data.path);
cb(null, data);
});
};
// make a stream that identifies if the given 'file' is a directory, and if so
// it pipelines it with the stream given
var forEachFolder = function(stream){
return es.map(function(data, cb){
if(data.isDirectory()){
var pathToPass = data.path+'/*.*'; // change it to *.js if you want only js files for example
log.info('Piping files found in '+pathToPass);
if(stream !== undefined){
gulp.src([pathToPass])
.pipe(stream());
}
}
cb(null, data);
});
};
// let's make a dummy task to test our streams
gulp.task('dummy', function(){
// load some folder with some subfolders inside
gulp.src('js/*')
.pipe(forEachFolder(printFileNames));
// we should see all the file paths printed in the terminal
});
So in your case, you can make a stream with whatever you want to make with the files in a folder ( like minify them and concatenate them ) and then pass an instance of this stream to the forEachFolder stream I made. Like I do with the printFileNames custom stream.
Give it a try and let me know if it works for you.
First, install gulp-concat & gulp-uglify.
$ npm install gulp-concat
$ npm install gulp-uglify
Next, do something like:
//task for building feature1
gulp.task('minify-feature1', function() {
return gulp.src('/js/feature1/*')
.pipe(uglify()) //minify feature1 stuff
.pipe(concat('feature1.min.js')) //concat into single file
.pipe(gulp.dest('/js/feature1')); //output to dir
});
//task for building feature2
gulp.task('minify-feature2', function() { //do the same for feature2
return gulp.src('/js/feature2/*')
.pipe(uglify())
.pipe(concat('feature2.min.js'))
.pipe(gulp.dest('/js/feature2'));
});
//generic task for minifying features
gulp.task('minify-features', ['minify-feature1', 'minify-feature2']);
Now, all you have to do to minify everything from the CLI is:
$ gulp minify-features
I had trouble with the gulp recipe, perhaps because I'm using gulp 4 and/or because I did not want to merge all my folders' output anyway.
I adapted the recipe to generate (but not run) an anonymous function per folder and return the array of functions to enable them to be processed by gulp.parallel - in a way where the number of functions I would generate would be variable. The keys to this approach are:
Each generated function needs to be a function or composition (not a stream). In my case, each generated function was a series composition because I do lots of things when building each module folder.
The array of functions needs to passed into my build task using javascript apply() since every member of the array needs to be turned into an argument to gulp.parallel in my case.
Excerpts from my function that generates the array of functions:
function getModuleFunctions() {
//Get list of folders as per recipe above - in my case an array named modules
//For each module return a function or composition (gulp.series in this case).
return modules.map(function (m) {
var moduleDest = env.folder + 'modules/' + m;
return gulp.series(
//Illustrative functions... all must return a stream or call callback but you can have as many functions or compositions (gulp.series or gulp.parallel) as desired
function () {
return gulp.src('modules/' + m + '/img/*', { buffer: false })
.pipe(gulp.dest(moduleDest + '/img'));
},
function (done) {
console.log('In my function');
done();
}
);
});
}
//Illustrative build task, running two named tasks then processing all modules generated above in parallel as dynamic arguments to gulp.parallel, the gulp 4 way
gulp.task('build', gulp.series('clean', 'test', gulp.parallel.apply(gulp.parallel, getModuleFunctions())));
`

Gulp synchronous task issue

I'm trying to gather 3 tasks needed to debug in a 1. Of course, since nature of gulp is asynchronous, I have problems with that. So I searched and find a soulution to use run-sequence module for solving that issue. I tried the following code, but it doesn't seem to be working as intended. It's not getting synchronous.
Here's what I tried. Any thoughts guys? I don't want to run all this three commands to complete all the tasks. How can I do that?
var gulp = require('gulp'),
useref = require('gulp-useref'),
gulpif = require('gulp-if'),
debug = require('gulp-debug'),
rename = require("gulp-rename"),
replace = require('gulp-replace'),
runSequence = require('run-sequence'),
path = '../dotNet/VolleyManagement.UI';
gulp.task('debug', function () {
gulp.src('client/*.html')
.pipe(debug())
.pipe(gulp.dest(path + '/Areas/WebAPI/Views/Shared'));
});
gulp.task('rename', function () {
gulp.src(path + '/Areas/WebAPI/Views/Shared/index.html')
.pipe(rename('/Areas/WebAPI/Views/Shared/_Layout.cshtml'))
.pipe(gulp.dest(path));
gulp.src(path + '/Areas/WebAPI/Views/Shared/index.html', {read: false})
.pipe(clean({force: true}));
});
gulp.task('final', function(){
gulp.src([path + '/Areas/WebAPI/Views/Shared/_Layout.cshtml'])
.pipe(replace('href="', 'href="~/Content'))
.pipe(replace('src="', 'src="~/Scripts'))
.pipe(gulp.dest(path + '/Areas/WebAPI/Views/Shared/'));
});
gulp.task('debugAll', runSequence('debug', 'rename', 'final'));
 In gulp you can actually set dependant task. Try this:
gulp.task('debug', function () {
//run debug task
});
gulp.task('rename',['debug'], function () {
//run rename once debug is done
});
I think you are not defining the 'debugAll' task right. Try like this:
gulp.task('debugAll', function () {
runSequence('debug', 'rename', 'final');
});
And also you need to return the stream for those tasks, just add 'return' in front of gulp.src for each of them: debug, rename, final. Here is the example for 'debug' task:
gulp.task('debug', function () {
return gulp.src('client/*.html')
.pipe(debug())
.pipe(gulp.dest(path + '/Areas/WebAPI/Views/Shared'));
});
Both items are mentioned in the docs: https://www.npmjs.com/package/run-sequence

Categories

Resources