How do you create an empty stream in Gulp? - javascript

SSCCE:
gulp.task('foo', [], function() {
var barFiles = getBarFiles();
var bazFiles = getBazFiles();
var barStream, bazStream;
if (barFiles && barFiles.length > 0) {
barStream = gulp.src(barFiles);
}
if (bazStream && bazStream.length > 0) {
bazStream = gulp.src(bazStream);
}
return eventStream
.merge(barStream, bazStream)
.pipe(g.concat('bar-and-baz'))
.pipe(gulp.dest('./foo-output/'));
});
getBarFiles() or getBazFiles() may return an empty array,
which is not allowed by gulp.src(): Error: Invalid glob argument,
hence the need to wrap the creation of the stream with an if condition.
So the question is, how do I create an empty stream,
so that it can be merged with the other empty on non-empty stream?

How about using:
return gulp.src('.');
For newer Gulp its possible you will need to add allowEmpty: true option as follows:
return gulp.src('.', {allowEmpty: true});
As I assume it should just pass current directory in the stream with no actions on it. Works for me.

A function for creating an empty stream for use in gulp (gleaned from vinyl-fs source) is this:
var through2 = require('through2');
function createEmptyStream() {
var pass = through2.obj();
process.nextTick(pass.end.bind(pass));
return pass;
}
If you make barFiles = createEmptyStream() in the example, it is functionally the same.
Current version of gulp (3.9.1) and vinyl-fs (0.3.0) on 2016-03-05 allows for an empty src array (from my testing). It uses something similar to the above to create an empty stream. Your example works (mostly as-is) in current versions:
var eventStream = require('event-stream');
var gulp = require('gulp');
var g = require('gulp-load-plugins')();
function getBarFiles() {
return [
];
}
function getBazFiles() {
return [
'baz.txt'
];
}
gulp.task('foo', [], function() {
var barFiles = getBarFiles();
var bazFiles = getBazFiles();
var barStream = gulp.src(barFiles);
var bazStream = gulp.src(bazFiles);
return eventStream
.merge(barStream, bazStream)
.pipe(g.concat('bar-and-baz.txt'))
.pipe(gulp.dest('./'));
});
gulp.task('default', [ 'foo' ]);
The file bar-and-baz.txt is a concatenation of the contents of all files in the globs returned from those two functions. An empty list of globs is accepted.

Maybe my answer is too simplistic, but I had exactly the same question as the OP, and I was able to solve it without importing any additional modules:
if (platform != 'android') {
// Android doesn't need anything here
return gulp.src([]); // empty stream
}
In other words, gulp.src() doesn't work, but gulp.src([]) seems to work fine. Or am I missing something?
EDIT 18 Mar 2020: Apparently, Gulp 4.0 now requires the allowEmpty flag for this to work:
if (platform != 'android') {
// Android doesn't need anything here
return gulp.src([], {allowEmpty: true}); // empty stream
}
See Artur Stępień's answer to this question, or this related SO question.

Another solution that met my needs quite well was to use gulp-tap:
var config = require('./config');
var gulp = require('gulp');
var tap = require('gulp-tap');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var noop = function() {};
gulp.task('mytask', function() {
return gulp.src('*.js')
.pipe(config.production ? uglify() : tap(noop))
.pipe(concat('code.min.js'))
.pipe(gulp.dest('dist'));
});
This allows you to easily create an empty stream which you can pass inside a .pipe() so you don't have to have an if statement outside.
Note: As I just figured out, you shouldn't store the tap(noop) into a variable and attempt to use it across multiple tasks as that may actually mix your streams and cause erratic behavior.

I use this:
https://github.com/dominictarr/event-stream
es.merge([]);
It works!

One solution:
gulp.task('foo', [], function() {
var barFiles = getBarFiles();
var bazFiles = getBazFiles();
var barStream, bazStream;
if (barFiles && barFiles.length > 0) {
barStream = gulp.src(barFiles);
}
if (bazStream && bazStream.length > 0) {
bazStream = gulp.src(bazStream);
}
var mergedStream;
if (barStream && bazStream) {
mergedStream = eventStream
.merge(barStream, bazStream);
}
else if (barStream || bazStream) {
mergedStream = barStream || bazStream;
}
if (mergedStream) {
return mergedStream
.pipe(g.concat('bar-and-baz'))
.pipe(gulp.dest('./foo-output/'));
}
});
This side-steps the the need to create an empty stream by testing each of the streams, and merging them only when both are present.
However, I would still like to know of a way to create an empty stream in gulp.

Related

obfuscate js using cli

I use javascript-obfuscator package, it works fine if I do operations with files, like:
javascript-obfuscator source.js
but I need to dynamically change js source content and get output on console in way like
javascript-obfuscator "var foo = 'bar'; alert(foo);"
Any suggestion how can I get rid from saving content to file and do it like in snippet above?
Not really considering why you'd need to do this, it looks like you'll need to use the programmatic API as described in the library's README.
Let's call this obfuscate.js:
var JavaScriptObfuscator = require('javascript-obfuscator');
var obfuscationResult = JavaScriptObfuscator.obfuscate(
process.argv[2],
{
compact: false,
controlFlowFlattening: true
}
);
console.log(obfuscationResult.getObfuscatedCode());
$ node obfuscate.js 'console.log(1)'
will then output (for example)
var _0x2b5a = ['log'];
(function (_0x630038, _0x2944a9) {
var _0x83df37 = function (_0x2ef1a5) {
while (--_0x2ef1a5) {
_0x630038['push'](_0x630038['shift']());
}
};
_0x83df37(++_0x2944a9);
}(_0x2b5a, 0xd7));
var _0x493b = function (_0x2b48eb, _0x33884a) {
_0x2b48eb = _0x2b48eb - 0x0;
var _0x41338b = _0x2b5a[_0x2b48eb];
return _0x41338b;
};
console[_0x493b('0x0')](0x1);

node.js directory search for file with name

i need help writing a node.js application that searches for all sub directories under the current directory which their names contain the specified string.
for example the user want to search all directories that have the string 'test' in it.
what is the js code i need to use?
i try using this:
var walk = function(dir) {
var results = []
var list = fs.readdirSync(dir)
list.forEach(function(file) {
file = dir + '/' + file
var stat = fs.statSync(file)
if (stat && stat.isDirectory()) results = results.concat(walk(file))
else results.push(file)
})
return results
}
Take a look at node-glob
In your case you could use it like this. This pattern will give you all files in the folder that contain at least once test in the name.
var glob = require("glob")
glob("+(test).js", options, function (er, files) {
// files is an array of filenames.
// If the `nonull` option is set, and nothing
// was found, then files is ["**/*.js"]
// er is an error object or null.
if (er) {
// omg something went wrong
throw new Exception(er);
}
var requiredFiles = files.map(function(filename) {
return require(filename);
});
// do something with the required files
});

Node.js, require all modules in folder and use loaded module directly

In MyModule folder, I have this two JS files.
SayHello.js
module.exports.SayHello = function() {
return('Hello !');
}
SayByeBye.js
module.exports.SayByeBye = function() {
return('Bye Bye!');
}
In Node.js, I want to require all files in MyModule folder and call function SayHello & SayByeBye directly something like:
require(./MyModule)
console.log(SayHello());
console.log(SayByeBye());
EDIT:
With answer of #Yanick Rochon,I do this :
> ./app/my-module/index.js
global.SayHello = require('./my-module/SayHello').SayHello;
global.SayByeBye = require('./my-module/SayByeBye').SayByeBye;
> ./app/my-module/say-hello.js
module.exports.SayHello = function() {
return('Hello !');
};
> ./app/my-module/say-byebye.js
module.exports.SayByeBye = function() {
return('Bye Bye !');
};
> ./app/main.js
require('./my-module');
console.log(SayHello());
console.log(SayByeBye());
There's a section about global objects in the node documentation.
However, globals should be used with care. By adding modules to the global space I reduce testability and encapsulation. But in this case, I think using this method is acceptable.
First thing first...
I believe you are mistaking Node.js with PHP or .Net, in the sense that you don't "import" into the current module what is exported in other modules. Not unless you manually do it anyway. For example, when you call
require('./my-module');
(Note that I renamed your MyModule into Node.js naming convention.)
You don't load things into the current context; you just load the script and don't assign it to anything. To access what my-module exposes, you need to assign it, or use it directly. For example :
require('./my-module').someFunction();
or
var myModule = require('./my-module');
myModule.someFunction();
Modules are not namespaces, but JavaScript objects that exposes public properties outside of their own contexts (i.e. using module.exports = ...)
Answer
You have two most popular ways to accomplish this :
Solution 1
Create an index.json file inside your folder where you want to load all of your scripts. The returned JSON object should be all the modules to load automatically :
> ./app/index.json
[
"say-hello.js",
"say-goodbye.js"
]
You should also consider having all your files API compatible :
> ./app/say-hello.js
module.exports = function sayHello() {
return 'Hello !';
};
> ./app/say-goodbye.js
module.exports.sayGoodbye = function () {
return 'Goodbye !';
};
Then load and execute everything like this :
var path = require('path');
var basePath = './app/';
var files = require(basePath);
var mods = files.forEach(function (loaded, file) {
var mod = require(path.join(basePath, file));
// mod is a function with a name, so use it!
if (mod instanceof Function) {
loaded[mod.name] = mod;
} else {
Object.keys(mod).forEach(function (property) {
loaded[property] = mod.property;
});
}
}, {});
mods.sayHello();
mods.sayGoodbye();
Solution 2
Read all .js files inside your folder and import them. I highly recommend you use glob for this.
var glob = require("glob")
var path = require('path');
var basePath = './app/';
var mods = glob.sync(path.join(basePath, '*.js')).reduce(function (loaded, file) {
var mod = require(file);
// mod is a function with a name, so use it!
if (mod instanceof Function) {
loaded[mod.name] = mod;
} else {
Object.keys(mod).forEach(function (property) {
loaded[property] = mod.property;
});
}
return loaded;
}, {});
mods.sayHello();
mods.sayGoodbye();
Note on the difference between module.exports and exports
Typically module.exports === exports, but it is recommended to use module.exports for the following reason
exports = function Foo() { } // will not do anything
module.exports = function Foo() { } // but this will do what you expect
// however these two lines produce the same result
exports.foo = 'Bar';
module.exports.foo = 'Bar';
For this reason, module.exports is recommended in all cases.
It's not perfect, but something like this should help you accomplish this:
var fs = require('fs');
var path = require('path');
var files = fs.readdirSync(__dirname);
var ownFilename = __filename.substr(__filename.lastIndexOf(path.delimiter) + 1);
var modules = {};
for (var i = 0; i < files.length; i++) {
var filename = files[i];
if (filename.substr(-3) === '.js' && filename !== ownFilename) {
modules[filename.slice(0, -3)] = require('./' + filename);
}
}
console.log(modules.SayByeBye());
console.log(modules.SayHello());

Test context missing in before and after test hook in nightwatch js globals

I have multiple nightwatch tests with setup and teardown in every single test. I am trying to unify it into globalModule.js in before after(path set in globals_path in nightwatch.json).
//globalModule.js
before:function(test, callback){
// do something with test object
}
//sampletest.js
before: function(test){
..
},
'testing':function(test){
....
}
My problem is test context is not available in globalsModule.js. How do i get it there? Can someone let me know?
Test contex not available now. As said beatfactor, it will available soon.
While it not available try use local before first file, but it hack.
Also you can export all your file into one object and export it into nightwatch, but then you can use local before just in time.
For example:
var tests = {};
var befores = [];
var fs =require('fs');
var requireDir = require('require-dir');
var dirs = fs.readdirSync('build');
//if you have dirs that should exclude
var usefull = dirs.filter(function(item){
return !(item=='data')
});
usefull.forEach(function(item){
var dirObj = requireDir('../build/' + item);
for(key in dirObj){
if(dirObj.hasOwnProperty(key))
for(testMethod in dirObj[key])
if(dirObj[key].hasOwnProperty(testMethod))
if(testMethod == 'before')
befores.push(dirObj[key][testMethod]);
else
tests[testMethod] = dirObj[key][testMethod];
}
});
tests.before = function(browser){
//some global before actions here
//...
befores.forEach(function(item){
item.call(tests,browser);
});
};
module.exports = tests;
For more information https://github.com/beatfactor/nightwatch/issues/388

this._get is not a function - javascript oop and prototypes

I'm using something similar to NodeJS called bondi, it's build on the Firefox js engine.. Basically i'm getting this error and I believe it's due to the way i'm referencing "this" in the .Get function below.
Basically there is a tool called SFtpClient. It has the method of "Get", to list the contents of a folder, but I want to change the prototype for this with a drop in include file. I need to change it so that it
a/ retries several times when it fails, and b/ it has a recursive folder listing function.
So I used the prototype to change it - moved .Get to ._Get.
Can anyone see why I would be getting the error:
Jan 23 04:51:34 beta bondi: === this._Get is not a function --- Prio(6) Result(0x0) File(/home/nwo/approot/include/sftpclientenh
when I run the code below?
Thanks
SFtpClient.prototype._Get = SFtpClient.prototype.Get;
SFtpClient.prototype.Get = function(Folder, Retries){
//defaults
if(!Retries) Retries = 5;
if(!Folder) Folder = "~/";
//vars
var FileListing = [];
var connect = function(){
//TODO JRF 19.01.2012 : re-enable this when bondi is fixed
// this.HomeDirectory.replace(/\/?$/, "/");
FileListing = this._Get(Folder);
return true;
}
var i = 1;
do{
var res = false;
try {
res = connect();
}catch(e){
Debug.LogInfo(e.message);
}
i++;
Server.Sleep(i*2000);
} while(res==false && i < Retries);
return FileListing;
}
Try res = connect.call(this) instead of res = connect().

Categories

Resources