Getting a possible leak using gulp watch, tasks take longer each time - css

I'm using gulp to compile and minify my SASS in an ASP.NET5 website to use as a CDN. I've written a watcher that watches my *.scss files, it then uses dependencies to compile the scss and then concat/minify it into one style.min.css.
The issue I'm having is that the longer the project is open, or at least with each sequential run of the gulp tasks, they get considerably longer to the point where I had only been developing for about 20 minutes, and the compile task was taking 15+ second, and the minify was taking 25+ seconds. The very first run of each takes about 1 or 2 milliseconds so I'm really lost as to what's going on.
Here's my watcher:
gulp.task('Watch:Sass', function () {
watch(paths.cdn.sassLoc, { verbose: true }, function () {
gulp.start("Min:css");
});
});
Here is the Min:css task:
gulp.task("Min:css", ["Compile:Sass"], function () {
return gulp.src([paths.cdn.cssLoc, "!" + paths.cdnMinCssLoc + "**/*.*"])
.pipe(concat(paths.cdn.cssDest))
.pipe(cssmin())
.pipe(plumber({
handleError: function (err) {
console.log(err);
this.emit('end');
}
}))
.pipe(gulp.dest("."));
});
And here is the Compile:Sass task that is being injected as a dependency:
gulp.task('Compile:Sass', function () {
gulp.src(paths.cdn.imagesLoc)
.pipe(gulp.dest(paths.webroot + '/css/min/images'));
return gulp.src(paths.cdn.sassLoc).pipe(plumber({
handleError: function (err) {
console.log(err);
this.emit('end');
}
}))
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest(paths.cdn.sassDest));
});
Could it be because of my use of returns?
Any help would be greatly appreciated as I have to restart Visual Studio every half hour or so which gets extremely frustrating!
I'm sure I've included everything relevant, but please ask if you require any more info.

In this case it was in fact my return statements that were in the wrong place.
In order to fix the issue, I had to take off the return on the gulp.src(...) and I had to add a return true to the end of the Compile:Sass gulp task. I also took the return off of the Min:Css task completely and now my compile task takes ~1.5ms and my min task takes ~600us.
Here's the final Compile:Sass task:
gulp.task('Compile:Sass', function () {
gulp.src(paths.cdn.imagesLoc)
.pipe(gulp.dest(paths.webroot + '/css/min/images'));
gulp.src(paths.cdn.sassLoc).pipe(plumber({
handleError: function (err) {
console.log(err);
this.emit('end');
}
}))
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest(paths.cdn.sassDest));
return true;
});

Matt, you have a concat on one of your "Min:css" task pipes.
gulp.task("Min:css", ["Compile:Sass"], function () {
return gulp.src([paths.cdn.cssLoc, "!" + paths.cdnMinCssLoc + "**/*.*"])
.pipe(concat(paths.cdn.cssDest))
...
...
.pipe(gulp.dest("."));
});
Make sure that you are not infinitely concatenating that same css over and over and over again. I can't tell what your paths.cdn.cssLoc points to. That would account for longer and longer processing time. You can call that a leak if you want.

Related

Asynchronous tasks in grunt.registerTask

I need to call two functions within grunt.registerTask, but the second function has to be called after the first function is done.
So I was wondering if we can use callbacks or promises or other asynchronous mechanisms within grunt.registerTask.
(More specifically, I need to launch karma in my first function call, and run karma in the second function call (to execute the initial unit tests). But in order to run karma, I need to launch it first. And that's what I'm missing.)
I had this:
grunt.registerTask("name_of_task", ["task_a", "task_b", "task_c"]);
And "task_b" had to be executed after "task_a" was done. The problem is that "task_a" is asynchronous and returns right away, so I needed a way to give "task_a" a few seconds to execute.
The solution:
grunt.registerTask("name_of_task", ["task_a", "task_b:proxy", "task_c"]);
grunt.registerTask("task_b:proxy", "task_b description", function () {
var done = this.async();
setTimeout(function () {
grunt.task.run("task_b");
done();
}, 2000);
});
};
From http://gruntjs.com/creating-tasks:
Tasks can be asynchronous.
grunt.registerTask('asyncfoo', 'My "asyncfoo" task.', function() {
// Force task into async mode and grab a handle to the "done" function.
var done = this.async();
// Run some sync stuff.
grunt.log.writeln('Processing task...');
// And some async stuff.
setTimeout(function() {
grunt.log.writeln('All done!');
done();
}, 1000);
});
For the sake of simplicity... having this Grunfile.js
grunt.registerTask('a', function () {
let done = this.async();
setTimeout(function () {
console.log("a");
done();
}, 3000);
});
grunt.registerTask('b', function () {
let done = this.async();
console.log("b1");
setTimeout(function () {
console.log("b2");
done();
}, 3000);
console.log("b3");
});
grunt.registerTask('c', function () {
console.log("c");
});
grunt.registerTask("run", ["a", "b", "c"]);
and then running run task, will produce the following output
Running "a" task
a
Running "b" task
b1
b3
b2 <-- take a look here
Running "c" task
c
Done.
The command is executed in this order
wait 3000 ms
console.log("a")
console.log("b1")
console.log("b3")
wait 3000 ms
console.log("b2")
console.log("c")

Run a command after a grunt task finishes?

I want to run a command but after a task finishes in grunt.
uglify: {
compile: {
options: {...},
files: {...}
}
?onFinish?: {
cmd: 'echo done!',
// or even just a console.log
run: function(){
console.log('done!');
}
}
},
Either run a command in shell, or even just be able to console.log. Is this possible?
Grunt does not support before and after callbacks, but next version could implement events that would work in the same way, as discussed in issue #542.
For now, you should go the task composition way, this is, create tasks for those before and after actions, and group them with a new name:
grunt.registerTask('newuglify', ['before:uglify', 'uglify', 'after:uglify']);
Then remember to run newuglify instead of uglify.
Another option is not to group them but remember to add the before and after tasks individually to a queue containing uglify:
grunt.registerTask('default', ['randomtask1', 'before:uglify', 'uglify', 'after:uglify', 'randomtask2']);
For running commands you can use plugins like grunt-exec or grunt-shell.
If you only want to print something, try grunt.log.
The grunt has one of the horrible code that I've ever seen. I don't know why it is popular. I would never use it even as a joke. This is not related to "legacy code" problem. It is defected by design from the beginning.
var old_runTaskFn = grunt.task.runTaskFn;
grunt.task.runTaskFn = function(context, fn, done, asyncDone) {
var callback;
var promise = new Promise(function(resolve, reject) {
callback = function (err, success) {
if (success) {
resolve();
} else {
reject(err);
}
return done.apply(this, arguments);
};
});
something.trigger("new task", context.name, context.nameArgs, promise);
return old_runTaskFn.call(this, context, fn, callback, asyncDone);
}
You can use callback + function instead of promise + trigger. This function will request the new callback wrapper for new task.

passing grunt parameters from one task to another

I'm trying to pass the configuration values returned from the server(zookeeper) into compass (cdnHost, environment, etc) and seem to be having a hard time using the right approach.
I looked at ways to pass around args from one task to another on this page as a starting point
http://gruntjs.com/frequently-asked-questions#how-can-i-share-parameters-across-multiple-tasks
module.exports = function(grunt) {
grunt.initConfig({
compass: {
dist: {
//options: grunt.option('foo')
//options: global.bar
options: grunt.config.get('baz')
}
},
...
grunt.registerTask('compassWithConfig', 'Run compass with external async config loaded first', function () {
var done = this.async();
someZookeeperConfig( function () {
// some global.CONFIG object from zookeeper
var config = CONFIG;
// try grunt.option
grunt.option('foo', config);
// try config setting
grunt.config.set('bar', config);
// try global
global['baz'] = config;
done(true);
});
});
...
grunt.registerTask('default', ['clean', 'compassWithConfig', 'compass']);
I also tried calling the compass task directly, and it made no difference.
grunt.task.run('compass');
Any insights would be greatly appreciated. (e.g. way to use initConfig and have the value be available).
Thanks
When you write:
grunt.initConfig({
compass: {
dist: {
options: grunt.config.get('baz')
}
}
... grunt.config is called right away, and returns the value of baz as it is right now. Altering it (later) in another task simply won't get picked-up.
How to solve that?
#1: update compass.dist.options instead of updating baz
grunt.registerTask('compassWithConfig', 'Run compass with external async config loaded first', function () {
var done = this.async();
someZookeeperConfig( function () {
// some global.CONFIG object from zookeeper
var config = CONFIG;
grunt.config.set('compass.dist.options', config);
done();
});
});
Now, running task compassWithConfig first, then task compass will get the result you expect.
#2: wrap-up compass task execution in order to abstract away config mapping
grunt.registerTask('wrappedCompass', '', function () {
grunt.config.set('compass.dist.options', grunt.config.get('baz'));
grunt.task.run('compass');
});
// Then, you can manipulate 'baz' without knowing how it needs to be mapped for compass
grunt.registerTask('globalConfigurator', '', function () {
var done = this.async();
someZookeeperConfig( function () {
// some global.CONFIG object from zookeeper
var config = CONFIG;
grunt.config.set('baz', config);
done();
});
});
Finally, running task globalConfigurator then wrappedCompass will get you to the result.

How do I ensure that nested async code will test correctly using Mocha?

I am working with Mocha and am trying to test an API that I am in the process of building.
I am having trouble understanding where to place the done() function.
If I place it where it is now, it doesn't execute the callback function of User.findOne().
If I place the done at the bottom of the callback function of User.findOne(), then it creates a timeout.
I am relatively new to async and this done function, so can someone help explain why these two cases happen, and how to fix the code so that it will test correctly in Mocha?
describe('POST /signup', function() {
before(checkServerIsRunning); // Need to implement
it('create a new user if username is unique', function(done) {
httpReq({
method : 'POST',
url : url + '/signup',
json : true,
body : JSON.stringify({
username : 'test',
first : 'first',
last : 'last' })
},
function (err, res, body) {
if (err) {
done(err);
}
else {
res.statusCode.should.be.equal(201);
User.findOne( { username: 'test' }, function(err, user) {
user.should.have.property('username', 'testy');
user.should.have.property('firstName', 'first');
user.should.have.property('lastName', 'last');
usersToRemove.push(user);
});
done();
}
}
);
});
});
You should place done() inside the called to findOne.
If you're finding that it times out then either findOne is never calling its callback (which is an error!) or it's taking too long to execute.
In this case you could up the timeout by sticking something like this.timeout(5000) at the beginning of the test (which increases the timeout to 5 seconds).
In general you wouldn't usually want tests that slow though, so maybe try and figure out why it takes so long.

Trying to build LESS (less css) using a build script with nodeJS

We are using NodeJS to build our project. We have integrated LESS CSS as a part of the project. We are attempting to keep our build clean and would like to be able to call the lessc command (or something comparable) in order to build our LESS files.
The LESS documentation isn't very in depth, but wanted to reach out to the community to find a solution. Google has not be too helpful to me on this topic.
We have a build.sh file, that calls various other build.js files (in respective directories). How can I run LESSC to compile my LESS files?
Using node.js
var less = require('less')
,fs = require('fs');
fs.readFile('style.less',function(error,data){
data = data.toString();
less.render(data, function (e, css) {
fs.writeFile('style.css', css, function(err){
console.log('done');
});
});
});
Few years later ... less#3.9.0 returns output as:
{
css: 'rendered css',
imports: ['source.css']
}
Updated code can look like this:
const outputFile = 'style-iso.css';
const data = `.bootstrap-iso {
#import (less) "style.css";
} `;
less.render(data, { strictMath: true }).then(
output => {
fs.writeFile(outputFile, output.css, err => {
if (err) {
throw err;
}
console.log(`${outputFile} saved!`);
});
},
err => {
console.error(err);
});
Tip
strictMath: true
is necessary if you want to render bootstrap.css

Resources