I am creating a gulp task which might fail under certain circumstances.
gulp.task('favicon', function () {
try {
require('child_process').execSync('icotool --version');
} catch( e ) {
var err = new Error( 'Unix bash and icotool required for generating favicon' );
throw err;
}
return gulp.src('', {read: false})
.pipe(shell([
'./generate-favicon.sh'
]));
});
When running my task via gulp and running into the error, the error will be presented rather ugly.
I would like to present the error in a way as it is done by e.g. jslint gulp-util's PluginError.
It actually works to just create a PluginError there and throw it but that doesn't seem quite right.
Another solution not that nice would be to set
err.showStack = false;
for at least a little nicer error output. A gulp.task.Error would be nice.
From what I've seen its not great to throw an error from gulp. But I found this blog entry that I used to work for me.
http://gotofritz.net/blog/geekery/how-to-generate-error-in-gulp-task/
Edit: gulp-util has been deprecated. Instead, use the plugin-error package.
My Example:
var gulp = require('gulp');
var error = require('plugin-error');
gulp.task('deploy', function(cb) {
if(typeof(specialId) === 'undefined') {
var err = new PluginError({
plugin: 'deploy',
message: 'specialId is empty.'
});
}
}
Related
I've created:
var access = fs.createWriteStream('/var/log/node/api.access.log', { flags: 'w' });
Then piped:
process.stdout.pipe(access);
Then tried:
console.log("test");
And nothing has appeared in /var/log/node/api.access.log. However this way is working:
process.stdout.pipe(access).write('test');
Could someone explain what am I doing wrong ?
I solved this problem the following way:
var access = fs.createWriteStream('/var/log/node/api.access.log');
process.stdout.write = process.stderr.write = access.write.bind(access);
Of course you can also separate stdout and stderr if you want.
I also would strongly recommend to handle uncaught exceptions:
process.on('uncaughtException', function(err) {
console.error((err && err.stack) ? err.stack : err);
});
This will cover the following situations:
process.stdout.write
process.stderr.write
console.log
console.dir
console.error
someStream.pipe(process.stdout);
throw new Error('Crash');
throw 'never do this';
throw undefined;
Checkout console.Console, the parent class of the normal console.
var myLogFileStream = fs.createWriteStream(pathToMyLogFile);
var myConsole = new console.Console(myLogFileStream, myLogFileStream);
You can then you use myConsole.log, myConsole.error, myConsole.dir, etc. and write directly to your file.
You can also monkey patch process.stdout.write as follows:
var fn = process.stdout.write;
function write() {
fn.apply(process.stdout, arguments);
myLogFileStream.write.apply(myLogFileStream, arguments);
}
process.stdout.write = write;
there are also other options for overwriting console._stdout depending on the motivation for logging the stdout to a file.
process.stdout is a Writable. pipe is a method of Readable(Cf StreamAPI documentation : https://nodejs.org/api/stream.html
You can see the documentation of process.stdout here : https://nodejs.org/api/process.html#process_process_stdout
It's surprising that you can do process.stdout.pipe(...); without any error. But i suppose this call just do nothing. Except returning a new Writable stream binded to stdout (or maybe it returns process.stdout itself. There's no specification for that in the documentation).
If you want to redirect stdout to a file, you have many solutions :
Just use your command line to do that. Windows style : node myfile.js > api.access.log.
Replace the console object by your own object. And you can rewrite console methods.
I'm not sure, but it may be possible to replace process.stdout with your own stream (and you can do whatever you want with this)
#user3173842
for the reply on
I solved this problem the following way:
var access = fs.createWriteStream('/var/log/node/api.access.log');
process.stdout.write = process.stderr.write = access.write.bind(access);
you do understand that process.stdout continues after process.on('exit') and therefore the fs.WriteStream closes after with process.stdout, according to
https://github.com/nodejs/node/issues/7606
so now the question remains, if the developer desired to have the fs.Writestream.write() return to its normal functionality and when fs.Writestream.end is called the writestream closes. How would the developer go about doing this I did
a_l = asyncify_listener
p_std_stream_m is a process stream manager object
p_std_stream_m.std_info.p_stdout_write = process.stdout.write
process.stdout.write = w_stream.write.bind(w_stream)
process.once('beforeExit', a_l( p_std_stream_m.handler,process.stdout,w_stream ) )
where in the 'beforeExit' event listener I did
process.stdout.write = p_std_stream_m.std_info.p_stdout_write
w_stream.end()
It works and you use the once method because the process.stdout seems to do a lot of work
at this time.
Is this good practice, would you do this or what would you do in this situation
anyone can feel free to reply.
Originally based on #Anatol-user3173842 answer
But in my case I needed to hook the stdout & stderr and also write into a file.
So for those who need to keep the normal stdout behaviour in addition to writing into the file. Use the following.
For non-errors:
// stdout logging hook
const stdoutWrite0 = process.stdout.write;
process.stdout.write = (args) => { // On stdout write
CustomLogger.writeToLogFile('log', args); // Write to local log file
args = Array.isArray(args) ? args : [args]; // Pass only as array to prevent internal TypeError for arguments
return stdoutWrite0.apply(process.stdout, args);
};
For errors:
// stderr logging hook
const stderrWrite0 = process.stderr.write;
process.stderr.write = (args) => { // On stderr write
CustomLogger.writeToLogFile('error', args); // Write to local error file
args = Array.isArray(args) ? args : [args]; // Pass only as array to prevent internal TypeError for arguments
return stderrWrite0.apply(process.stderr, args);
};
// uncaught exceptions
process.on('uncaughtException', (err) => {
CustomLogger.writeToLogFile('error', ((err && err.stack) ? err.stack : err));
});
Here is the CustomLogger code, where I also separate the log files by date:
export class CustomLogger {
static LOGS_DIR = 'location-of-my-log-files';
private static logDailyName(prefix: string): string {
const date = new Date().toLocaleDateString().replace(/\//g, '_');
return `${CustomLogger.LOGS_DIR}/${prefix}_${date}.log`;
}
private static writeToLogFile(prefix, originalMsg) {
const timestamp = Date.now();
const fileName = this.logDailyName(prefix);
const logMsg = prepareForLogFile(originalMsg);
fs.appendFileSync(fileName, `${timestamp}\t${logMsg}\n\n`);
return originalMsg;
}
}
Here's a quick example of a logger class that redirects stdout, stderr and exceptions to a file, while still writting everything to the console:
class Logger {
#log_stream
#stdout_write
#stderr_write
constructor(path) {
this.#log_stream = fs.createWriteStream(path, { flags: 'a' })
this.#stdout_write = process.stdout.write.bind(process.stdout)
this.#stderr_write = process.stderr.write.bind(process.stderr)
process.stdout.write = this.stdout_write.bind(this)
process.stderr.write = this.stderr_write.bind(this)
process.on('uncaughtException', function(err) {
console.error((err && err.stack) ? err.stack : err)
})
}
stdout_write(buffer) {
this.#log_stream.write(buffer)
this.#stdout_write(buffer)
}
stderr_write(buffer) {
this.#log_stream.write(buffer)
this.#stderr_write(buffer)
}
}
const logger = new Logger('example.log')
I'm trying to push to my remote repository using the gulp-git module from npm. The add & commit portion runs fine, but it runs into a stream error when trying to perform the remote push.
bump: function () {
var branch = argv.branch || 'development';
fs.readFile('./package.json', function (err, data) {
if (err) { return ; }
return gulp.src(['./package.json', './bower.json'])
.pipe(git.add())
.pipe(git.commit('chore(core): bump to ' + JSON.parse(data).version))
.pipe(git.push('origin', branch, function(err) {
if(err) throw (err);
}));
});
}
The stack trace:
C:\src\git\ig\node_modules\gulp-git\node_modules\through2\node_modules\readable-stream\lib_stream_readable.js:623
var written = dest.write(chunk);
^
TypeError: undefined is not a function
at write (C:\src\git\ig\node_modules\gulp-git\node_modules\through2\node_modules\readable-stream\lib_stream_readable.js:623:24)
at flow (C:\src\git\ig\node_modules\gulp-git\node_modules\through2\node_modules\readable-stream\lib_stream_readable.js:632:7)
at DestroyableTransform.pipeOnReadable (C:\src\git\ig\node_modules\gulp-git\node_modules\through2\node_modules\readable-stream\lib_stream_readable.js:664:5)
at DestroyableTransform.emit (events.js:104:17)
at emitReadable_ (C:\src\git\ig\node_modules\gulp-git\node_modules\through2\node_modules\readable-stream\lib_stream_readable.js:448:10)
at emitReadable (C:\src\git\ig\node_modules\gulp-git\node_modules\through2\node_modules\readable-stream\lib_stream_readable.js:444:5)
at readableAddChunk (C:\src\git\ig\node_modules\gulp-git\node_modules\through2\node_modules\readable-stream\lib_stream_readable.js:187:9)
at DestroyableTransform.Readable.push (C:\src\git\ig\node_modules\gulp-git\node_modules\through2\node_modules\readable-stream\lib_stream_readable.js:149:10)
at DestroyableTransform.Transform.push (C:\src\git\ig\node_modules\gulp-git\node_modules\through2\node_modules\readable-stream\lib_stream_transform.js:145:32)
at Array.forEach (native)
I'm running gulp-git version 1.6.0. It looks like they are at 1.7.0. Maybe the upgrade path would help however this seems like a pretty standard usage of the command, so I think it's something I'm doing wrong.
With help from stevelacy (the project admin) I was able to make it work with this code change:
.pipe(git.commit('chore(core): bump to ' + JSON.parse(data).version))
.on('end', function() {
git.push('origin', branch, function(err) {
if(err) throw (err);
});
});
It turns out that the git push command cannot be done from a stream as of yet.
Update: Seems like this is a bug in gulp-protractor. On their github page they filed it as a bug and will take a look into it. Source: https://github.com/mllrsohn/gulp-protractor/issues/64
Only possible workaround you can do until the bug is resolved is change the directory of your project to something that doesn't include spaces.
So I'm trying to get an Aurelia project started including front end unit testing. Here is where the problem starts. When I try to run the e2e gulp task I get the following error:
[10:45:44] using gulpfile ~\Documents\Visual Studio 2013\Projects\ProjectX\ProjectX\gulpfile.js
[10:45:44] Starting 'build-e2e'...
[10:45:44] Finished 'build-e2e' after 207 ms
[10:45:44] Starting 'e2e'...
'C:\Users\jorisd\Documents\Visual' is not recognized as an internal or external command, operable program or batch file.
C:\Users\jorisd\Documents\Visual Studio 2013\Projects\ProjectX\ProjectX\build\tasks\e2e.js:34
.on('error', function(e) {throw e; });
Error: protractor exited with code 1
Basically it's the highlighted code that has the problem. Since my path includes a space, it'll stop there for some reason.
Here's how my e2e.js file looks like right now:
var gulp = require('gulp');
var paths = require('../paths');
var to5 = require('gulp-babel');
var plumber = require('gulp-plumber');
var webdriverUpdate = require('gulp-protractor').webdriver_update;
var webdriverStandalone = require("gulp-protractor").webdriver_standalone;
var protractor = require('gulp-protractor').protractor;
// for full documentation of gulp-protractor,
// please check https://github.com/mllrsohn/gulp-protractor
gulp.task('webdriver-update', webdriverUpdate);
gulp.task('webdriver-standalone', ['webdriver-update'], webdriverStandalone);
// transpiles files in
// /test/e2e/src/ from es6 to es5
// then copies them to test/e2e/dist/
gulp.task('build-e2e', function() {
return gulp.src(paths.e2eSpecsSrc)
.pipe(plumber())
.pipe(to5())
.pipe(gulp.dest(paths.e2eSpecsDist));
});
// runs build-e2e task
// then runs end to end tasks
// using Protractor: http://angular.github.io/protractor/
gulp.task('e2e', ['build-e2e'], function(cb) {
return gulp.src(paths.e2eSpecsDist + '/*.js')
.pipe(protractor({
configFile: '/protractor.conf.js',
args: ['--baseUrl', 'http://127.0.0.1:9000']
}))
.on('end', function() { process.exit(); })
.on('error', function(e) { throw e; });
});
The problem is situating in the e2e task with the configFile option.
I tried change the line into the following:
configFIle: __dirname + '/protractor.conf.js',
But this aswell without result. If any of you know a workaround for including spaces in the configFile path, I'll be happy to hear it.
For me its working fine.
var angularProtractor = require('gulp-angular-protractor');
gulp.task('test', function (callback) {
gulp
.src([__dirname+'/public/apps/adminapp/**/test/**_test.js'])
.pipe(angularProtractor({
'configFile': 'public/apps/adminapp/app.test.config.js',
'debug': false,
'args': ['--suite', 'adminapp'],
'autoStartStopServer': true
}))
.on('error', function(e) {
console.log(e);
})
.on('end',callback);
});
I am trying to get amazon pricing information with nodejs.
Here's the target url:
http://aws.amazon.com/ec2/pricing/
But the content of the pricing tables which I am reading in nodejs is not fully rendered and there are only javascripts.
So far I have used jsdom, jquerygo and phantom but I was not successful. Even setting timeouts does not help. Can anyone please provide me with a working solution for this specific case?
Thanks and best regards.
There are different ways to scrape a web page using node.js
I was inspired by spookjs
var Spooky = require('spooky');
var spooky = new Spooky({
child: {
transport: 'http'
},
casper: {
logLevel: 'debug',
verbose: true
}
}, function (err) {
if (err) {
e = new Error('Failed to initialize SpookyJS');
e.details = err;
throw e;
}
spooky.start(
'http://en.wikipedia.org/wiki/Spooky_the_Tuff_Little_Ghost');
spooky.then(function () {
this.emit('hello', 'Hello, from ' + this.evaluate(function () {
return document.title;
}));
});
spooky.run();
});
spooky.on('error', function (e, stack) {
console.error(e);
if (stack) {
console.log(stack);
}
});
spooky.on('console', function (line) {
console.log(line);
});
spooky.on('hello', function (greeting) {
console.log(greeting);
});
spooky.on('log', function (log) {
if (log.space === 'remote') {
console.log(log.message.replace(/ \- .*/, ''));
}
});
Note: Gives flexibility to run casperjs and phantom js using node.js
This solved my issue:
I noticed that when installing phantom module in node, it was complaining about version of phantomjs (version 2) and was downloading version (1.9.8) in some temporary location.
Thus I installed version 1.9.8 instead and set the PATH variable to that. And it worked!
Also must note that inside page.open(...) function you must setTimeout for quite a long time (in my case about 35 seconds) so that the whole page is fully loaded and rendered.
I'm building a Yeoman generator and after it has finished I want to perform some command line actions like 'npm install', 'bower install' and 'grunt less'. I'm using spawnCommand for this and I nested all actions using event listeners to perform them synchronously. However, to avoid this endless nesting, I'm looking for a cleaner implementation, to make it easily expandable.
Perfectly, I would like to have an array with commands (like ['npm install', 'grunt install', 'less:dev']) and have this processed synchronously with proper error detection.
// Install npm packages
this.spawnCommand('npm', ['install'])
.on('exit', function (err) {
if (err) {
this.log.error('npm package installation failed. Please run \'npm install\' and \'bower install\'. Error: ' + err);
} else {
// Install bower packages
this.spawnCommand('bower', ['install'])
.on('exit', function (err) {
if (err) {
this.log.error('bower package installation failed. Please run \'bower install\'. Error: ' + err);
} else {
this.spawnCommand('grunt', ['less'])
.on('exit', function (err) {
if (err) {
this.log.error('Less compilation failed. Please run \'grunt less:dev\'. Error: ' + err);
} else {
}
}.bind(this));
}
}.bind(this));
}
}.bind(this));
Something like this? (untested though):
this.processTask = function (task) {
this.spawnCommand(task.cmd, task.args)
.on('exit', function (err) {
if (err) {
this.log.error('task failed. Error: ' + err);
} else {
this.emit('nextTask');
}
});
};
this.on('nextTask' function(){
var next = this.tasks.shift();
if (next){
this.processTask(next);
} else {
console.log('we are done');
}
}.bind(this));
//preparing the list of tasks:
this.tasks = [];
this.tasks.push({cmd: 'npm', args:['install']});
this.tasks.push({cmd: 'bower', args:['install']});
this.tasks.push({cmd: 'grunt', args:['less']});
//start first task
this.processTask(this.tasks.shift());
I used execSync from Node.js and it seems to work, eg:
var child_process = require('child_process');
var result = execSync('grunt less');
Node.js 0.12 and io.js 1.10 support execSync:
child_process.execSync(command[, options])
and returns, "Buffer|String The stdout from the command", which may be an error code.
API documentation.
The back story about the synchronous API.
You can make a script like init.sh and put your commands that need to be run in order in it, like:
#!/usr/bin/env bash
npm install
your-funky-command
gulp something-special
gulp
...then wherever you need to put the spawnCommand code (I do it in end method), add somehting like this:
var done = this.async();
this.spawnCommand('sh', ['init.sh'], /* maybe cwd? {cwd: 'src'} */)
.on('close', done);
Ain't pretty or anything but it works, and it's obvious.
Optionally, if you need one command to only run if the prev succeeded, do this:
#!/usr/bin/env bash
npm install \
&& your-funky-command \
&& gulp something-special \
&& gulp
(Bonus advantage is that now your app init logic is no longer tied to Yo.)