How do you use event-stream to pause and resume Gulp streams? - node.js

I'm trying clone npm packages to a client-side location, so I have created multiple streams to accomplish this and merge them to return from gulp.task(). However, it is my understanding that I will need to pause the streams in order for the task to receive the proper exit.
The following code runs without error, but if I comment the return line. One of the packages is still copied, when I would expect none of the streams to reach their gulp.dest(). Why aren't all the streams paused?
var gulp = require('gulp')
, eventStream = require('event-stream')
;
const projects = {
src: {
dependencies: {
codemirror: './src/lib/codemirror',
acorn: './src/lib/acorn'
}
}
};
gulp.task('init:client-packages', function () {
let streams = []
, ps = eventStream.pause();
// Load project client-side dependencies
for (let prj in projects) {
for (let pkg in projects[prj].dependencies) {
streams.push(
gulp.src('./node_modules/' + pkg + '/**')
.pipe(ps)
.pipe(gulp.dest(projects[prj].dependencies[pkg]))
);
}
}
// Merge source streams
return eventStream.merge(streams).pipe(ps.resume());
// ^^ Commenting this line doesn't block gulp.dest() from completing.
});

There maybe a bug in event-stream as the following code only works with merge-stream.
var merge = require('merge-stream')
, eventStream = require('event-stream')
;
gulp.task('init:client-packages', function() {
let streams = [];
// Load project client-side dependencies
for (let prj in projects) {
for (let pkg in projects[prj].dependencies) {
streams.push(
gulp.src('./node_modules/' + pkg + '/**')
.pipe(gulp.dest(projects[prj].dependencies[pkg]))
);
}
}
// Merge source streams
return merge(streams);
// --OR--
return eventStream.merge(streams); // Results in message below.
});
As stated above event-stream doesn't close the task properly. The output is as follows:
[23:36:46] Starting 'init:client-packages'...
[23:36:47] The following tasks did not complete: init:client-packages
[23:36:47] Did you forget to signal async completion?
Process terminated with code 1.

Related

Copy specified files with condition using gulp

I am trying copy my vendor files to my dev folder using gulp. When I was in development mode, I want copy only the unminified files, if unminified is not present copy minified files. And in production mode I want copy minifed files if files are not present minify the normal files.
my folder structure
js
app.js
jquery
jquery.min.js
jquery.js
fontawesome
fontawesome.min.js
fontawesome.min.css
fonts.ttf...
Here my basic I had written.
var scriptsPath = '../vendor/';
function getFolders(dir) {
return fs.readdirSync(dir)
.filter(function(file) {
return fs.statSync(path.join(dir, file)).isDirectory();
});
}
gulp.task('vendor', function() {
var folders = getFolders(scriptsPath);
var cssFilter = $.filter('**/*.css')
var tasks = folders.map(function(folder) {
var jsFilter;
if (isProduction) {
jsFilter = $.filter('**/*.min.js');
} else {
jsFilter = $.filter(['**/*.js', '!**/*.min.js']);
}
return gulp.src(path.join(scriptsPath, '**/'))
.pipe(jsFilter)
.pipe($.if(useSourceMaps, $.sourcemaps.init()))
.pipe($.if(isProduction, $.uglify({preserveComments: 'some'})))
.on('error', handleError)
.pipe(jsFilter.restore())
.pipe(cssFilter)
.pipe($.if( isProduction, $.minifyCss() ))
.on('error', handleError)
.pipe(cssFilter.restore())
.on('error', handleError)
.pipe(gulp.dest(build.vendor.js));
});
return es.concat.apply(null, tasks);
});
I am trying the last two days using gulp-if& some methods. But not yet get the solution.Thanks in advance.
You are trying to cram way to much into your vendor task. The stuff you do with your JS files is completely unrelated to the stuff you do with your CSS files. That's hard to read.
Instead of using gulp-filter try splitting vendor up into smaller tasks like vendor-js, vendor-css, etc... and then declare them as dependencies for your vendor task:
gulp.task('vendor', ['vendor-js', 'vendor-css' /* etc ... */]);
Your vendor-js task could then look like this:
var glob = require('glob');
gulp.task('vendor-js', function () {
var js = glob.sync('../vendor/**/*.js');
if (isProduction) {
// use <file>.min.js, unless there is only <file>.js
js = js.filter(function(file) {
return file.match(/\.min\.js$/) ||
js.indexOf(file.replace(/\.js$/, '.min.js')) < 0;
});
} else {
// use <file>.js, unless there is only <file>.min.js
js = js.filter(function(file) {
return !file.match(/\.min\.js$/) ||
js.indexOf(file.replace(/\.min\.js$/, '.js')) < 0;
});
}
gulp.src(js, { base: '../vendor' })
.pipe($.if(isProduction, // only minify for prod and when
$.if("!**/*.min.js", uglify()))) // the file isn't minified already
.pipe(gulp.dest('build'));
});
Adapting this to you specific needs should be fairly trivial from here on.

gulp-concat throws exception 'file.isNull(), index.js:39 TypeError: file.isNull is not a function

I am attempting to compile react (.jsx), coffeescript (.coffee), and vanilla javascript (.js) files using gulp to package all of the resulting .js files into one file, app.js, which gets loaded into my index.html page. I am generating a stream for each compilation type and using merge-stream to collect the contents of the 3 feeder streams into a single stream, which I am passing to gulp-concat to create app.js.
I am getting an exception from gulp-concat, index.js, line 39, letting me know that 'file' is not a function. Here is my entire gulpfile.js, the reference to gulp-concat is near the bottom of this section.
var browserify = require('browserify');
var coffee = require('gulp-coffee');
var concat = require('gulp-concat');
var gulp = require('gulp');
var gutil = require('gulp-util');
var mergeStream = require('merge-stream');
var reactify = require('reactify');
var sass = require('gulp-sass');
var source = require('vinyl-source-stream');
gulp.task('javascript', function(){
// convert .jsx files to .js, collecting them in a stream
var b = browserify();
b.transform(reactify); // use the reactify transform
b.add('./jsx-transforms.js');
jsxStream = b.bundle();
if (gutil.isStream(jsxStream)) {
gutil.log("jsxStream is a stream");
} else {gulp-concatgulp
gutil.log("jsxStream is not a stream");
}
merged = mergeStream(jsxStream);
if (gutil.isStream(merged)) {
gutil.log("merged is a stream");
} else {
gutil.log("merged is not a stream");
}
// collect all .js files in a stream
jsStream = gulp.src(['./client/**/*.js','./common/**/*.js']);
if (gutil.isStream(jsStream)) {
gutil.log("jsStream is a stream");
} else {
gutil.log("jsStream is not a stream");
}
merged.add(jsStream);
// compile all .coffee file to .js, collect in a stream
coffeeStream = gulp.src(['./client/**/*.coffee','./common/**/*.coffee'])
.pipe(coffee({bare: true}).on('error', gutil.log));
if (gutil.isStream(coffeeStream)) {
gutil.log("coffeeStream is a stream");
} else {
gutil.log("coffeeStream is not a stream");
}
merged.add(coffeeStream);
// concatenate all of the .js files into ./build/app.js
merged
.pipe(concat('app.js'))
.pipe(gulp.dest('./build'));
});
gulp.task('styles', function() {
gulp.src('./client/assets/stylesheets/**/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(concat('app.css'))
.pipe(gulp.dest('./build'));
});
gulp.task('default', ['javascript', 'styles']);
I have used gulp-concat before, but never hit this problem before.
Gulp streams are a very particular sort of stream: they are Node streams in object mode containing vinyl file objects. If your stream comes from somewhere other than gulp.src(), such as from the browserify API, then you will have to first convert the stream into the sort that gulp can deal with.
There are two steps you need to take. First, convert your browserify bundle stream into a stream containing a vinyl file object with vinyl-source-stream (which you have required but not used).
var source = require('vinyl-source-stream');
...
var jsxStream = b.bundle()
.pipe(source('bundle.js'));
Now there is another catch. Vinyl streams may be in one of two modes: streaming mode or buffer mode. Vinyl-source-stream gives you a stream in streaming mode. Many Gulp plugins, including gulp-concat, only support buffer mode. The fix for this is easy: use vinyl-buffer.
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
...
var jsxStream = b.bundle()
.pipe(source('bundle.js'))
.pipe(buffer());
Now you have something you can merge with your other streams and pipe to gulp-concat. For more detail, see this recipe.

Restarting gulp after changes to gulpfile.js

I am attempting to re-run my gulp build when gulpfile.js changes, but I am having issues with the method all of my research has lead me to.
I have one watcher for all my less and javascript files and a configuration object that has the list of files to watch, how they are output, etc. This is a stripped-down example of what it looks like:
var $ = require('gulp-load-plugins')();
var config = {
root: rootPath,
output: {
app: 'app',
vendor: 'vendor'
}, // ...
};
gulp.task('default', ['build', 'watch']);
gulp.task('build', ['clean', 'less:app', 'less:theme', 'css:vendor', 'js:app', 'js:vendor', 'rev', 'css:copyfonts']);
gulp.task('watch', function () {
var allFiles = config.styles.appSrc
.concat(config.styles.vendorSrc)
.concat(config.scripts.appSrc)
.concat(config.scripts.vendorSrc);
$.watch(allFiles, function () {
gulp.start('default');
});
});
gulp.task('watch:gulp', function () {
var p;
gulp.watch('gulpfile.js', spawnUpdatedGulp);
spawnUpdatedGulp();
function spawnUpdatedGulp() {
if (p) {
p.kill();
}
p = spawn('gulp', ['default', '--color'], { stdio: 'inherit' });
}
});
// .. other build tasks ..
The above code shows how I tried the accepted answer to this:
How can Gulp be restarted upon each Gulpfile change?
However, it has a major issue. When I run watch:gulp, it runs the build just fine, and everything is great. The config.output.app variable is how the app specific css and js files are named, so my test case has been:
run gulp:watch, check that the css output is named according to config.output.app
change config.output.app, and perform step #1 again
save any random javascript file that it is watching, and see if it builds correctly
Step 3 is riddled with permission errors because of multiple watchers on the files, and this only gets worse the more I repeat steps 1 and 2. Visual Studio will even freeze.
I have not found a way to clean up the old watchers. I tried to manually kill them like this:
var appFileWatcher;
gulp.task('watch', function () {
var allFiles = config.styles.appSrc
.concat(config.styles.vendorSrc)
.concat(config.scripts.appSrc)
.concat(config.scripts.vendorSrc);
appFileWatcher = $.watch(allFiles, function () {
gulp.start('default');
});
});
gulp.task('watch:gulp', function () {
var p;
var gulpWatcher = $.watch('gulpfile.js', spawnUpdatedGulp);
spawnUpdatedGulp();
function spawnUpdatedGulp() {
if (p) {
p.kill();
}
if (appFileWatcher) {
appFileWatcher.unwatch();
}
gulpWatcher.unwatch();
p = spawn('gulp', ['default', '--color'], { stdio: 'inherit' });
}
});
This also does not work. I still get multiple watchers trying to perform the build when I perform my same test case.
How do I kill those watchers that stay around after the new gulp process is spawned?

Write to same file from multiple tasks (Gulp, Node, gulp-json)

I've just started using Gulp (and NodeJs)... Obviously I ran into my first wall.
Here it is:
I have a large project that uses themes. Each theme has it's own assets (scss and js files). Here is my gulpfile.js:
// < require block here (not included, to keep this short)
var themes = ["theme1", "theme2", "theme3"];
// Since I can have up to 20 different themes, I use the 'themes' array so I can create tasks dynamically, like this:
themes.forEach(function (theme) {
gulp.task('css:' + theme, function () {
setVersion([theme], 'css'); // write asset version into a json file
gulp.src('../themes/frontend/' + theme + '/assets/css/style.scss')
.pipe(sourcemaps.init())
.pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError))
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('../themes/frontend/' + theme + '/assets/css'))
});
});
// Of course, I need an "all" task to build all CSS in rare ocasions I need to do so:
gulp.task('css:all', ("css:" + themes.join(",css:")).split(","));
// ("css:" + themes.join(",css:")).split(",") => results in the needed ['css:theme1', 'css:theme2'] tasks array
// The same logic as above for JS files
// but added the use of gulp-concat and gulp-uglify
// Having scripts = { "theme1" : ['script1', 'script2'], "theme2": ['script1', 'script2'] }
// ...
// And "per theme" both "css and js"
themes.forEach(function (theme) {
gulp.task('theme:' + theme, ['css:' + theme, 'js:' + theme]);
});
// Next I need to set versions for each asset
// I'm writing all the versions into a json file
assetsVersion = someRandomGeneratedNumber;
function setVersion(themes, assetType) {
/**
* themes: array
* assetType: 'all', 'css' or 'js'
*/
var fs = require('fs'),
path = require("path");
var versionsFilePath = path.normalize(__dirname + '/../protected/config/theme/frontend/');
var versionsFileName = '_assets-version.json';
if (!fs.existsSync(versionsFilePath + versionsFileName)) {
// Create file if it doesn't exist
fs.writeFile(versionsFilePath + versionsFileName, "{}", function (err) {
if (err) {
return console.log(err);
}
});
}
gulp.src(versionsFilePath + versionsFileName)
.pipe(jeditor(function (json) {
themes.forEach(function(theme) {
if ("undefined" == typeof (json[theme])) {
json[theme] = {};
}
if ('css' == assetType) {
json[theme]['css'] = assetsVersion;
} else if ('js' == assetType) {
json[theme]['js'] = assetsVersion;
} else {
json[theme] = {"css": assetsVersion, "js": assetsVersion};
}
if ("undefined" == typeof(json[theme]['css'])) {
// if we're missing the 'css' key (i.e. we've just created the json file), add that too
json[theme]['css'] = assetsVersion;
}
if ("undefined" == typeof(json[theme]['js'])) {
// if we're missing the 'js' key (i.e. we've just created the json file), add that too
json[theme]['js'] = assetsVersion;
}
});
return json;
}))
.pipe(gulp.dest(versionsFilePath));
}
The assets versioning json should look like this:
{
"theme1": {
"css": "20150928163236",
"js": "20150928163236"
},
"theme2": {
"css": "20150928163236",
"js": "20150928163236"
},
"theme3": {
"css": "20150928163236",
"js": "20150928163236"
}
}
running 'gulp css:theme#' - works fine...
BUT running 'gulp css:all' - makes a messy json
Of course, this happens because all css:theme# (or js:theme#) tasks run async, and more often than not there are multiple tasks writing simultaneously to my json file.
I've read about tasks depending on other tasks, but that doesn't really fit into my whole "dynamic tasks" flow (or I don't know how to fit it in).
I mean I don't think that this:
gulp.task('css:theme1', ['versioning'], function() {
//do stuff after 'versioning' task is done.
});
would help me. SO what if it waits for the version to be written? Multiple tasks would still write to the file at the same time. Also, for this to work, I would need to pass parameters that I also don't know how to do... like:
gulp.task('css:'+theme, ['versioning --theme ' + theme], function() {
//do stuff after 'versioning' task is done.
});
Like I could make it work in the console. I know this isn't working, BUT would be really useful in some cases if it would somehow be possible to send parameters to the task in the task name.
Neither runSequence() { ... done(); }, I really don't see how could I make it work within my flow...
Please, anybody... help a newb...
How can I solve this, while:
Having tasks created dynamically;
Having one versioning json file for all themes.

Looking to use Gulp to create individual distributions of standalone HTML files

Basically I'm looking for a Gulp plugin to turn a directory like this:
/app
- htmlfile1.html
- htmlfile2.html
- htmlfile3.html
- /css
-cssmain.css
-/js
-global.js
And turn that into this:
/dist
-/htmlfile1
- htmlfile1.html
- /css
-cssmain.css
-/js
-global.js
- /htmlfile2
- htmlfile2.html
- /css
-cssmain.css
-/js
-global.js
- /htmlfile3
- htmlfile3.html
- /css
-cssmain.css
-/js
-global.js
Any thoughts on how to do accomplish a build system like this?
The code allows common files to be added to every page distribution as well as unique dependencies defined as an array in the pages object.
The following Gulp file relies on gulp-foreach, parse-filepath, and event-stream: npm install gulp gulp-foreach parse-filepath event-stream --save-dev
gulpfile.js:
// Command:
// npm install gulp gulp-foreach parse-filepath event-stream --save-dev
// Include gulp
var gulp = require('gulp');
var foreach = require('gulp-foreach'); // https://www.npmjs.org/package/gulp-foreach
var parsePath = require('parse-filepath'); // https://www.npmjs.org/package/parse-filepath
var es = require('event-stream'); // https://www.npmjs.org/package/event-stream
// The pages that each make a distribution
// Unique dependencies are defined as an array value for each page.
var pages = {
'./app/htmlfile1.html': [
'./app/images/1.png',
'./app/images/1-another.png',
],
'./app/htmlfile2.html': [],
'./app/htmlfile3.html': []
};
// Files added to each page distribution
var common = [
'./app/css/cssmain.css',
'./app/js/global.js',
];
function makeDistributionStream(page)
{
var gulpStream = gulp.src(page)
.pipe(foreach(function(stream, file) {
var pathParts = parsePath(file.path);
// Assemble the distribution path
var destinationPath = './dist/' + pathParts.name + '/';
// Pipe the html into the distribution folder
stream.pipe(gulp.dest(destinationPath));
// Move all of the unique and common files into the distibution
var uniqueDependencies = pages[page];
// Merge the common files to the unique ones
var distFiles = uniqueDependencies.concat(common);
gulp.src(distFiles, {base: './app/'})
.pipe(gulp.dest(destinationPath));
}));
return gulpStream;
}
// Assemble the distribution directories for each page
gulp.task('make-distributions', function() {
var mergedStream = null;
for(var page in pages)
{
var stream = makeDistributionStream(page);
// Merge the streams, if there is already one
if(mergedStream)
{
mergedStream = es.merge(mergedStream, stream);
}
// Otherwise, just make it this one
else
{
mergedStream = stream;
}
}
return mergedStream;
});
// Rerun the task when a file changes
gulp.task('watch', function() {
// If the html pages change, re-make the distributions
gulp.watch(Object.keys(pages), ['make-distributions']);
});
// Default Task
gulp.task('default', ['make-distributions', 'watch']);

Resources