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

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.

Related

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

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.

require is not defined error on gulp-concat

I want to uglify and combine my js files with gulp. Here is my code
var gulp = require('gulp');
var uglify = require('gulp-uglify');
var pump = require('pump');
var gutil = require('gulp-util');
gutil.env.type = 'production';
gulp.task('uglify', function (cb) {
return gulp.src([
'pure/modernizr.js',
'pure/horizon.js'
])
.pipe(gutil.env.type === 'production' ? uglify() : gutil.noop())
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest("ugly"));
});
var sourcemaps = require("gulp-sourcemaps");
var concat = require("gulp-concat-js");
gulp.task("concat", function () {
return gulp.src([
'ugly/modernizr.js',
'ugly/horizon.js'
])
.pipe(sourcemaps.init())
.pipe(concat({
"target": "concatenated.js", // Name to concatenate to
"entry": "./main.js" // Entrypoint for the application, main module
// The `./` part is important! The path is relative to
// whatever gulp decides is the base-path, in this
// example that is `./lib`
}))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest("pure/"));
});
I end up with some code enclosing my js files from obfuscator stating:
//// THIS FILE IS CONCATENATED WITH gulp-obfuscator-js
When I include this in code, it throws require is not defined, I surf around the web and found one similar question. But that answer is also not clear for me. I believe that I miss some small thing here, since I am new to gulp.
The issue here is I have used gulp-concat-js which obfuscate your js. I should have used gulp-concat. May help someone.

gulp - wrap plugin (which uses through2) output with string

I would like to know how exactly can I manipulate the output of my Gulp plugin so, for example, no matter how many files are passed to the plugin, it will wrap the output with a string. Currently I cannot know when does the last file is done.
The super simplified example below will iterate on 3 files and will create a new file named output.js and in it there will be three times the string xxx (xxxxxxxxx).
I would like the plugin itself to wrap the contents so the output will
be: +xxxxxxxxx+.
How can I do this?
Thanks!
Gulpfile
var gulp = require('gulp');
var concat = require('gulp-concat');
var foo = require('./index');
gulp.task('default', function() {
gulp.src([a.html, b.html, c.html])
.pipe(foo())
.pipe(concat('output.js'))
.pipe(gulp.dest('./test/output'))
});
The most basic gulp plugin (index.js):
var through2 = require('through2'),
gutil = require('gulp-util');
var PLUGIN_NAME = 'foo';
module.exports = function( options ){
// through2.obj(fn) is a convenience wrapper around
// through2({ objectMode: true }, fn)
return through2.obj(function( file, enc, callback ){
file.contents = new Buffer( 'xxx' );
this.push(file);
callback();
});
}
I understand the files are currently simply returned modified, but what I don't understand is how to append text and return the concatenated result that I want, while keeping it OK with Gulp working standards.
The "real" plugin should actually wrap the files results with:
var foo = { FILES_CONTENT }
where FILES_CONTENT will actually be a a concatenated string of all the files:
"file_name" : "file_content",
"file_name" : "file_content",
...
I would make the following changes to your gulpfile.js:
var gulp = require('gulp');
var foo = require('./index.js');
gulp.task('default', function() {
return gulp.src(['a.html', 'b.html', 'c.html'])
.pipe(foo({fileName:'output.js', varName:'bar'}))
.pipe(gulp.dest('./test/output'))
});
Since your foo() plugin itself will concatenate all the files, there's no need to use gulp-concat at all. Instead your plugin should accept an option fileName that provides the name of the generated file. I've also added another option varName that will provide the name of the var in the output file.
I'll assume that a.html, b.html and c.html are simple HTML files, something like this:
<h1 class="header">a</h1>
As you've already realized you need to concat all the files in the plugin itself. That's not really difficult however and doesn't require a lot of code. Here's a index.js which does exactly that:
var through2 = require('through2'),
gutil = require('gulp-util'),
path = require('path'),
File = require('vinyl');
var PLUGIN_NAME = 'foo';
module.exports = function(options) {
var files = { };
var outputFile = null;
return through2.obj(function(file, enc, callback){
outputFile = outputFile || file;
var filePath = path.relative(file.base, file.path);
files[filePath] = file.contents.toString();
callback();
}, function(callback) {
outputFile = outputFile ? outputFile.clone() : new File();
outputFile.path = path.resolve(outputFile.base, options.fileName);
outputFile.contents = new Buffer(
'var ' + options.varName + ' = ' +
JSON.stringify(files, null, 2) + ';'
);
this.push(outputFile);
callback();
});
}
Since you want to output a key/value mapping from file names to file contents our transformFunction just stores both of those things in a regular JavaScript object files. None of the input files themselves are emitted. Their names and contents are just stored until we have all of them.
The only tricky part is making sure that we respect the .base property of each file as is customary for gulp plugins. This allows the user to provide a custom base folder using the base option in gulp.src().
Once all files have been processed through2 calls the flushFunction. In there we create our output file with the provided fileName (once again making sure we respect the .base property).
Creating the output file contents is then just a matter of serializing our files object using JSON.stringify() (which automatically takes care of any escaping that has to be done).
The resulting ./test/output/output.js will then look like this:
var bar = {
"a.html": "<h1 class=\"header\">a</h1>\n",
"b.html": "<h1 class=\"header\">b</h1>\n",
"c.html": "<h1 class=\"header\">c</h1>\n"
};
You should use the gulp pipeline technique (standard).
This means that you can use the gulp-insert package in order
to add the string xxx.
var insert = require('gulp-insert');
.pipe(insert.append('xxx')); // Appends 'xxx' to the contents of every file
You can also prepend, append and wrap with this package and it support of course the gulp standards.
So the full example will be:
var gulp = require('gulp');
var concat = require('gulp-concat');
var foo = require('./index');
var insert = require('gulp-insert');
gulp.task('default', function() {
gulp.src([a.html, b.html, c.html])
.pipe(foo()
.pipe(insert.append('xxx'))
.pipe(concat('output.js'))
.pipe(gulp.dest('./test/output'))
});

Modify gulp file to run customized browserify command

I would like to use gulp to run a custom browserify command whenever a js file (function.js) is modified.
The browserify command that I want to run is;
$ browserify function.js --standalone function > bundle.js
I am using this gulpfile.js as sample.
https://github.com/gulpjs/gulp/blob/master/docs/recipes/fast-browserify-builds-with-watchify.md
How do I modify this gulpfile to run the customized browserify command?
'use strict';
var watchify = require('watchify');
var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var gutil = require('gulp-util');
var sourcemaps = require('gulp-sourcemaps');
var assign = require('lodash.assign');
// add custom browserify options here
var customOpts = {
entries: ['./src/index.js'],
debug: true
};
var opts = assign({}, watchify.args, customOpts);
var b = watchify(browserify(opts));
// add transformations here
// i.e. b.transform(coffeeify);
gulp.task('js', bundle); // so you can run `gulp js` to build the file
b.on('update', bundle); // on any dep update, runs the bundler
b.on('log', gutil.log); // output build logs to terminal
function bundle() {
return b.bundle()
// log errors if they happen
.on('error', gutil.log.bind(gutil, 'Browserify Error'))
.pipe(source('bundle.js'))
// optional, remove if you don't need to buffer file contents
.pipe(buffer())
// optional, remove if you dont want sourcemaps
.pipe(sourcemaps.init({loadMaps: true})) // loads map from browserify file
// Add transformation tasks to the pipeline here.
.pipe(sourcemaps.write('./')) // writes .map file
.pipe(gulp.dest('./dist'));
}
I am using node.js v6.9 on webstorm.
The command you want to run is;
$ browserify function.js --standalone function > bundle.js
Based on this, the modified code is;
// add custom browserify options here
var customOpts = {
entries: ['./function.js'],
standalone: 'function',
};
Simply add one more property to customOpts for the --standalone parameter. The rest of the code remains the same.

Duplicate Gulp Vinyl Stream with PassThrough- `TypeError: Invalid non-string/buffer chunk`

In Node, I am trying to duplicate a gulp vinyl stream using Passthrough. I get TypeError: Invalid non-string/buffer chunk when attempting c = fileStream.pipe(b);
I suspect it may be because fileStream is a gulp vinyl stream.
var pass = require('stream').PassThrough;
function duplicateStream(fileStream) {
b = new pass();
c = fileStream.pipe(b);
return c;
}
If you need to clone a gulp stream, you can just use gulp-clone. This task will write all of the single JS files to the out directory, as well as a concatenated bundle.js in the same directory.
var gulp = require('gulp');
var concat = require('gulp-concat');
var clone = require('gulp-clone');
var merge = require('merge-stream');
gulp.task('default', function () {
var scripts = gulp.src('assets/**/*.js');
var bundle = scripts.pipe(clone())
.pipe(concat('bundle.js'));
// Merge the streams together, then write them to the out folder
return merge(scripts, bundle).pipe(gulp.dest('out'));
});
https://github.com/mariocasciaro/gulp-clone

Resources