Programmatically install with bower? - javascript

I'm writing a grunt task and I want to install a dependency programmatically. However, I can't seem to figure out how to use their API.
This works just fine, but parsing the response is brittle because it uses the CLI:
grunt.util.spawn({
cmd: 'bower',
args: ['install', '--save', 'git#github.com:foo/bar.git']
}, function(none, message) {
grunt.log.writeln(message);
});
This does not work:
bower.commands.install.line(['--save', 'git#github.com:foo/bar.git'])
.on('end', function(data) {
grunt.log.writeln(data);
done();
})
.on('err', function(err) {
grunt.log.fail(err);
done();
});
I get the following error:
$ grunt my-task
Running "my-task:default_options" (my-task) task
Fatal error: Could not find any dependencies
What is the right way to do this?

The line() function expects the whole argv, so should be:
bower.commands.install.line(['node', 'bower', '--save', 'git#github.com:foo/bar.git']);
However, you should rather just pass paths and options to the install() method directly:
bower.commands.install(['git#github.com:foo/bar.git'], {save: true});

Related

How to halt a series of tasks but continue watching files in Gulp?

I have a Gulp task that lints Javascript files before injecting them into a HTML page. I'd like the files not to be injected if a linting error is found. So if an error appears in the first task, cancel all subsequent tasks.
The caveat is that this task is run by gulp.watch and I'd like the watch to continue to monitor the files and inject them if the linting errors are resolved.
Is this possible in gulp? Plugins are acceptable.
Gulpfile below:
var gulp = require('gulp');
var runSequence = require('run-sequence');
//(plugin declarations removed for brevity)
gulp.task('eslint', function() {
return gulp.src(config.lint)
.pipe($.cached('eslint'))
.pipe($.eslint())
.on('error', $.gutil.log)
.pipe($.eslint.format());
});
gulp.task('inject:resources', function() {
return gulp.src(config.src.templates)
.pipe(
$.inject(gulp.src(config.dist.scripts + '/all*.js',
{read: false}),
{
ignorePath: config.dist.public,
addRootSlash: false,
name: 'scripts'
}
))
.pipe(gulp.dest(config.dist.public));
});
gulp.task('watch:js', function(callback) {
runSequence('eslint', 'inject:resources');
});
gulp.task('watch', function() {
gulp.watch(config.src.scripts, ['watch:js']);
});
First you need to make eslint fail when any of your files produces an error. You can do that by placing eslint.failAfterError() at the end of your stream:
gulp.task('eslint', function() {
return gulp.src(config.lint)
.pipe($.cached('eslint'))
.pipe($.eslint())
.on('error', $.gutil.log)
.pipe($.eslint.format())
.pipe($.eslint.failAfterError());
});
When your eslint task fails the subsequent inject:resources task will not be executed by runSequence.
Next you need to make the callback in your watch:js task available to runSequence, otherwise gulp will not notice when watch:js has finished (and since you can't run the same task in parallel with itself this would effectively prevent watch:js from being started again):
gulp.task('watch:js', function(callback) {
runSequence('eslint', 'inject:resources', callback);
});
The only downside to this is that the above will produce an ugly stack trace that doesn't really provide any useful additional info. If you want to prevent that you can just "swallow" the error that is being passed up the callback chain like this:
gulp.task('watch:js', function(callback) {
runSequence('eslint', 'inject:resources', function(err) {
callback();
});
});

Gulp location of unhandled error in events.js:141

When I run task for example using comman gulp scripts:common, I get this output:
[14:05:47] Requiring external module babel-core/register
[14:05:49] Using gulpfile /home/sites/blablabla/gulpfile.babel.js
[14:05:49] Starting 'scripts:common'...
events.js:141
throw er; // Unhandled 'error' event
^
SyntaxError: Unexpected token
Well, for SyntaxError it would be useful to know where it found that unexpected token. How to tell it to show at least file and line? Where to find that evetns.js file? Could I put console.trace() or something like that there?
I solve this problem by running jshint on my scripts:
/*
* `gulp check_scripts` - Lints script files
*/
gulp.task('check_scripts', function() {
return gulp.src([
'gulpfile.js' //, other scripts to check
])
.pipe(jshint())
.pipe(jshint.reporter('jshint-stylish'))
.pipe(gulpif(enabled.failJSHint, jshint.reporter('fail')));
});
enabled.failJSHint is there to allow errors to pass in local environment but fail in production.
This will lint any syntax errors in your scripts.
Additionally you may want to hook it to other task so it's run automatically before proper build:
gulp.task('default', ['check_scripts', 'clean'], function() {
gulp.start('build');
});
This is the general idea.
You can look at error stack trace by adding custom "on error" handler to the gulp task.
gulp.task('compile:js', function() {
return (
gulp.src(jsPath)
.pipe(yourCustomTask())
.on('error', function(err) {
console.log(err.stack);
this.end();
})
);
});
Also, as another variant, adding gulp-plumber into pipeline makes error messages more clear.

Run grunt task asynchronously before another task

I have a gruntfile setup so that I can develop my local angularjs frontend while forwarding all api requests to a java middle tier hosted seperately on the network.
This works great except the location of the server changes every few days and I have to continuously update the gruntfile with the latest server location.
The latest server location can be found by a URL shortening service that forwards to the correct location, so I can fetch it with this grunt task/node.js code:
grunt.registerTask('setProxyHost', 'Pings the url shortener to get the latest test server', function() {
request('http://urlshortener/devserver', function(error, response, body) {
if (!error) {
var loc = response.request.uri.href;
if (loc.slice(0, 7) === 'http://') {
proxyHost = loc.slice(7, loc.length - 1);
}
}
});
});
Of course this is asynchonous, and when I run it, grunt has already setup the proxy by the time the request completes.
How can I run this nodejs request synchronously or block grunt until it completes? This is just for development so hacky solutions welcomed.
Thanks
EDIT:
Great answer Cory, that has mostly solved the problem as grunt now waits for the task to complete before continuing. One last issue is that I can't access that config from the initConfig for setting the proxy as initConfig runs first:
module.exports = function(grunt) {
[...]
grunt.initConfig({
connect: {
proxies: [{
host: grunt.config.get('proxyHost')
This post (Access Grunt config data within initConfig()) outlines the issue but I'm not sure how I could run the request synchronously outside of a task?
EDIT2 [solved]:
Corys answer + this post Programmatically pass arguments to grunt task? solved the issue for me.
module.exports = function(grunt) {
[...]
grunt.initConfig({
connect: {
proxies: [{
host: '<%= proxyHost %>',
Instead of running the task synchronously, you can easily run the task asynchronously & share data between tasks through the grunt config object.
1. Run a task asynchronously
To run a task asynchronously, you must first inform Grunt that the task is going to be async by calling this.async() This async call returns a "done function" that you'll use to tell Grunt whether the task has passed or failed. You can complete the task by passing the handler true and fail it by passing it an error, or false.
Async Task:
module.exports = function(grunt) {
grunt.registerTask('foo', 'description of foo', function() {
var done = this.async();
request('http://www...', function(err, resp, body) {
if ( err ) {
done(false); // fail task with `false` or Error objects
} else {
grunt.config.set('proxyHost', someValue);
done(true); // pass task
}
});
});
}
2. Share data between tasks
That grunt.config.set() bit (in the code above) is probably the easiest way to share values between tasks. Since all tasks share the same grunt config, you simply set a property on the config and then read it from the following tasks via grunt.config.get('property')
Following Task
module.exports = function(grunt) {
grunt.registerTask('bar', 'description of bar', function() {
// If proxy host is not defined, the task will die early.
grunt.config.requires('proxyHost');
var proxyHost = grunt.config.get('proxyHost');
// ...
});
}
The grunt.config.requires bit will inform grunt that the task has config dependencies. This is useful in scenarios where tasks go untouched for a long time (very common) and the intricacies are forgotten about. If you decide on changing the async task (rename the variable? dev_proxyHost, prod_proxyHost?) the following task will gracefully inform you that proxyHost could not be found.
Verifying property proxyHost exists in config...ERROR
>> Unable to process task.
Warning: Required config property "proxyHost" missing. Use --force to continue.
3. Your code, async
grunt.registerTask('setProxyHost', 'Pings the url shortener to get the latest test server', function() {
var done = this.async(),
loc,
proxyHost;
request('http://urlshortener/devserver', function(error, response, body) {
if (error) {
done(error); // error out early
}
loc = response.request.uri.href;
if (loc.slice(0, 7) === 'http://') {
proxyHost = loc.slice(7, loc.length - 1);
// set proxy host on grunt config, so that it's accessible from the other task
grunt.config.set('proxyHost', proxyHost);
done(true); // success
}
else {
done(new Error("Unexpected HTTPS Error: The devserver urlshortener unexpectedly returned an HTTPS link! ("+loc+")"));
}
});
});
Then from your proxy task, you can retrieve this proxyHost value via the following
var proxyHost = grunt.config.get('proxyHost');

Gulp plumber cannot pipe to undefined

I am trying, to write a module that when gulp is watching a task, and a Less file changed with some error, it should give a message to the console, but it should not crash the system, which it does no:
This is the code I have written:
var onError = function (err) {
console.log(err);
};
gulp.task('styles', function () {
gulp.src('./{views}/**/*.{less}')
.pipe(plumber({
errorHandler: onError
}))
.pipe(less())
.pipe(gulp.dest('build/styles/common.css'));
});
When I run the above code, I get the error below:
'styles' errored after 20 ms
Error in plugin 'plumber'
Message:
Can't pipe to undefined
I have had the same error. I resolved it by adding () after the plumber. Here is the code.
Earlier Code:
gulp.task('scripts',function(){
gulp.src('admin-scripts.js')
.pipe(plumber)
.pipe(uglify().on('error', function(e){
console.log(e);
}))
.pipe(gulp.dest('build/js'));
});
Fixed Code:
gulp.task('scripts',function(){
gulp.src('admin-scripts.js')
.pipe(plumber())
.pipe(uglify().on('error', function(e){
console.log(e);
}))
.pipe(gulp.dest('build/js'));
});
I was missing the () after plumber.
Hope this helps.
Are you sure to have installed gulp-less using
npm install --save-dev
and referencing it in your gulpfile with
var less = require('gulp-less');
This error from plumber occurs when the destination of your pipe (here your less) is undefined.
Also why are you using the {} here ? They are a bit useless I think since you're targetting only one thing in it, you could remove them.
Yes I have installed gulp-less, and I figured out the solution for this problem aswell:
It should be as follow:
gulp.task('mystyle', function () {
gulp.src('build/stylesfiles.less').pipe(plumber()).pipe(less()).
pipe(gulp.dest ('build/styles/assets'));
});
Basically I do not need the Error message, it will give me a default error, which can get my job done.Using the above code will make sure it runs, and gives me my error, but not crash the server....but thank you "Apercu"

Running a command in a Grunt Task

I'm using Grunt (task-based command line build tool for JavaScript projects) in my project. I've created a custom tag and I am wondering if it is possible to run a command into it.
To clarify, I'm trying to use Closure Templates and "the task" should call the jar file to pre-compile the Soy file to a javascript file.
I'm running this jar from command line, but I want to set it as a task.
Alternatively you could load in grunt plugins to help this:
grunt-shell example:
shell: {
make_directory: {
command: 'mkdir test'
}
}
or grunt-exec example:
exec: {
remove_logs: {
command: 'rm -f *.log'
},
list_files: {
command: 'ls -l **',
stdout: true
},
echo_grunt_version: {
command: function(grunt) { return 'echo ' + grunt.version; },
stdout: true
}
}
Check out grunt.util.spawn:
grunt.util.spawn({
cmd: 'rm',
args: ['-rf', '/tmp'],
}, function done() {
grunt.log.ok('/tmp deleted');
});
I've found a solution so I'd like to share with you.
I'm using grunt under node so, to call terminal commands you need to require 'child_process' module.
For example,
var myTerminal = require("child_process").exec,
commandToBeExecuted = "sh myCommand.sh";
myTerminal(commandToBeExecuted, function(error, stdout, stderr) {
if (!error) {
//do something
}
});
If you are using the latest grunt version (0.4.0rc7 at the time of this writing) both grunt-exec and grunt-shell fail (they don't seem to be updated to handle the latest grunt). On the other hand, child_process's exec is async, which is a hassle.
I ended up using Jake Trent's solution, and adding shelljs as a dev dependency on my project so I could just run tests easily and synchronously:
var shell = require('shelljs');
...
grunt.registerTask('jquery', "download jquery bundle", function() {
shell.exec('wget http://jqueryui.com/download/jquery-ui-1.7.3.custom.zip');
});
Guys are pointing child_process, but try to use execSync to see output..
grunt.registerTask('test', '', function () {
var exec = require('child_process').execSync;
var result = exec("phpunit -c phpunit.xml", { encoding: 'utf8' });
grunt.log.writeln(result);
});
For async shell commands working with Grunt 0.4.x use https://github.com/rma4ok/grunt-bg-shell.

Categories

Resources