gulp-sass slower with each compile - gulp

I have a very simple *.scss compilation task with the use of npm and gulp 4.
It is watched by
const scssWatcher = watch(['scss/*.scss']);
scssWatcher.on('all', function(event, path){ compileSass(path); });
And the compileSass function just
gulp.src(path, {'base' : pathRoot + './scss'})
.pipe(sassInheritance({base: pathRoot + 'scss/'}))
.pipe(sass()).on('error', sass.logError)
.pipe(gulp.dest(pathRoot + 'css'));
The sassInheritance is gulp-better-sass-inheritance module, which makes sure that all files that include the updated file are also updated.
It works, but get slower with every compile.
Restarting the gulp task with this watcher works but is cumbersome.
I have found out that removing the gulp-better-sass-inheritance from the pipeline solves the issue.
I have had this working with gulp 3 with no problem.
There is this thread https://github.com/dlmanning/gulp-sass/issues/467 with same behaving issue from 2016.
Anybody with the same experience? Any help?

These watchers and live-re-loaders are full of bugs.
Just restart it automatically using the shell.
while true; do
timeout 600 gulp run:live:watch whatever...
sleep 0.5
done

Related

Gulp4. "AssertionError : Task never defined" when calling or importing tasks

Below you can see simplified view of an issue. Basically, I'm able to call task1.js using gulp.series in tasks task2,3.js, but once I add same code to call task1.js in task4.js - Task never defined: task1 error gets thrown.
There are more tasks in the tasks folder than in file structure example below.
I've got three tasks,
...
/tasks
build.js
clean.js
dev.js
gulpfile.babel.js
...
all of them required in gulpfile.babel.js using the require-dir package
import requireDir from 'require-dir';
requireDir('./tasks', {recurse: true});
This allows me to call a task from clean.js at dev.js, and it works fine.
import gulp from 'gulp';
gulp.task('dev', gulp.series('clean');
But after I add same code structure at build.js.
import gulp from 'gulp';
gulp.task('build', gulp.series('clean');
it somehow breaks gulp stream (I guess), so now on any task call I get:
$gulp dev
-AssertionError [ERR_ASSERTION]: Task never defined: clean.
$gulp -v
[11:50:11] CLI version 2.0.1
[11:50:11] Local version 4.0.0
For those migrating from gulp v3 to v4 or are using gulp.task() to define tasks in gulp v4 and get this error message: Task never defined, the problem usually lies here:
Forward references
A forward reference is when you compose tasks, using string
references, that haven't been registered yet. This was a common
practice in older versions, but this feature was removed to achieve
faster task runtime and promote the use of named functions. In newer
versions, you'll get an error, with the message "Task never defined",
if you try to use forward references. You may experience this when
trying to use exports for your task registration and composing tasks
by string. In this situation, use named functions instead of string
references.
During migration, you may need to use the forward reference registry.
This will add an extra closure to every task reference and
dramatically slow down your build. Don't rely on this fix for very
long.
From gulpjs documentation re: gulp.series and gulp.parallel documentation.
Here is what that means. There are two ways to create tasks:
1. gulp.task('someStringAsTask', function() {..})
2. function myNamedFunction () {…}
When you use version 1 (gulp.task…) you cannot refer to that task by its string name until it has been registered. So you cannot do this:
exports.sync = gulp.series('sass2css', serve, watch);
// or gulp.task('dev', gulp.series('sass2css', serve, watch); doesn't work either
gulp.task('sass2css', function() {
return gulp.src(paths.sass.stylesFile)
.pipe(sass().on("error", sass.logError))
.pipe(gulp.dest(paths.css.temp))
.pipe(reload({ stream: true }));
})
Results in
AssertionError [ERR_ASSERTION]: Task never defined: sass2css
That is a forward reference, composing a task (using gulp.series or gulp.parallel) and referring to a task by its string name (in the above case 'sass2css') before it has been registered. (calling "gulp.task(…..)" is the act of registering) Putting the gulp.task('sass2css',...) first fixes the problem.
If you use version two of defining a task:
function sass2css() {
return gulp.src(paths.sass.stylesFile)
.pipe(sass().on("error", sass.logError))
.pipe(gulp.dest(paths.css.temp))
.pipe(reload({ stream: true }));
}
you are now using a named function to register a task and do not need to use its name as a string. So this now works:
exports.sync = gulp.series(sass2css, serve, watch);
// gulp.task('dev', gulp.series(sass2css, serve, watch); this also works
followed by (or preceded by - either works):
function sass2css() {
return gulp.src(paths.sass.stylesFile)
.pipe(sass().on("error", sass.logError))
.pipe(gulp.dest(paths.css.temp))
.pipe(reload({ stream: true }));
}
The original OP used this and it worked:
import gulp from 'gulp';
gulp.task('dev', gulp.series('clean');
Noted that clean.js got imported before dev.js so that was okay.
This didn't work:
import gulp from 'gulp';
gulp.task('build', gulp.series('clean');
because the string-referenced task, 'clean' gets imported (and thus registered) after build.js where it is referenced - thus creating an illegal forward reference to a string-referenced task.
So there are two standard ways to fix this error:
Use named functions to define tasks not gulp.task('someTask',...). Then it doesn't matter the order of using those named functions when composing other tasks, i.e., when using gulp.series or gulp.parallel. And there are other advantages to using named functions, such as passing arguments, so this is the best option.
If you do use the older gulp v3 gulp.task method of creating tasks with string references, be careful to not refer to those tasks until after the task is actually created.
Also see my answer at task never defined error for fixing another problem which results in the same error message. Specifically using gulp.task('someTask', ['anotherTask'], function(){}) synatx in a gulp4 file.
The series and parallel functions of gulp 4 do not create a task definition as its README seems to suggest, but instead they both run the tasks in parameter. In order to work as intended, one need to surround the call with a closure.
So, to fix the excerpt
gulp.task('build', gulp.series('clean'));
it is necessary to add the closure:
// Older EcmaScripts:
gulp.task('build', function() { return gulp.series('clean') });
// EcmaScript 6:
gulp.task('build', () => gulp.series('clean'));
I had a similar setup where I had recursively require'd all tasks under a directory. And after updating to gulp 4 started getting error Task never defined.
I tried Pedro solution, but this caused another error:
The following tasks did not complete: default
Did you forget to signal async completion?
The solution was fairly simple for me, just import the missing tasks.
import gulp from 'gulp';
import './clean';
gulp.task('build', gulp.series('clean'));
The easiest solution might be using the official undertaker-forward-reference package:
const gulp = require("gulp");
const FwdRef = require("undertaker-forward-reference");
gulp.registry(FwdRef()); // Or gulp.registry(new FwdRef());
gulp.task("firstRegisteredTask", gulp.series("laterRegisteredTask")); // Works thanks to undertaker-forward-reference
gulp.task("laterRegisteredTask", () => {
return gulp.src("someGlob").pipe(gulp.dest("someFolder"));
});
This solution might negatively affect performance though (source):
This will add an extra closure to every task reference and dramatically slow down your build.

Gulp watch runs only once

I'm trying to finish my gulpfile.js but I'm running into a problem with "gulp watch". When I set my watcher to run, it detects changes and run the assigned task, but only once. The next changes on the files don't trigger the tasks anymore. I don't know why, but my watcher is working just once.
I believe the watcher is not realizing the tasks it triggered are finished. Or, maybe it's something to deal with the lazy loading or run-sequence plugins I'm using...
var
gulp = require('gulp'),
plugins = require('gulp-load-plugins')({
DEBUG: true
});
plugins.runSequence = require('run-sequence');
gulp.task('watch',function(){
gulp.watch('public/**/*.+(css|js)',['buildEspecifico']);
});
gulp.task('buildEspecifico',function(callback){
return plugins.runSequence(
['deletarMainMin'],
['agruparJS','agruparCSS']
);
});
'deletarMainMin','agruparJS','agruparCSS' are other tasks defined in the same gulpfile.
Thanks in advance!
Cheers,
Fabio.
I believe the watcher is not realizing the tasks it triggered are finished.
Yes, that is exactly it. In your buildEspecifico task you accept a callback. That means gulp will not consider your buildEspecifico task to have finished unless you invoke that callback.
Since you don't invoke callback the buildEspecifico task never actually finishes once it runs. And because your buildEspecifico task is technically still running gulp doesn't start the task again when a file changes.
The solution is to pass callback to runSequence. It will invoke the callback when all of your tasks in the sequence have finished.
gulp.task('buildEspecifico',function(callback){
return plugins.runSequence(
['deletarMainMin'],
['agruparJS','agruparCSS'],
callback
);
});
Alternatively you can also just leave out the callback altogether:
gulp.task('buildEspecifico',function(){
return plugins.runSequence(
['deletarMainMin'],
['agruparJS','agruparCSS']
);
});
Obviously you have to make sure that the tasks deletarMainMin, agruparJS, agruparCSS themselves finish, so either call or leave out the callback in those tasks too.

Wait for completion in Gulp tasks

I've specified some Gulp tasks in a gulpSequence like this:
gulp.task('default', function(done){
gulpSequence(
'private:clean',
'private:copy-app-package-file',
'private:copy-app-main-file',
'private:copy-libs',
'private:copy-app-files',
'private:copy-css-files',
'private:build-html',
'private:package-app',
done);
});
I thought that they should run one after another. The build-html tasks needs the libs, app-files and css-files, because they get included within one html file.
But if I run the default task, the build-html task is missing some files. For example the task can only inject 10 files in the html file but there are 16 files.
Is there another way to specify dependent tasks. I thought the gulpSequence will handle this.
You're on the right lines, but the npm package you are trying to use is depreciated. Might be worth considering checking out run-sequence (https://www.npmjs.com/package/run-sequence) which waits for a previous task to complete before starting.
So in your instance, this would become
gulp.task('default', function( cb ) {
$.runSequence(
'private:clean',
'private:copy-app-package-file',
'private:copy-app-main-file',
'private:copy-libs',
'private:copy-app-files',
'private:copy-css-files',
'private:build-html',
'private:package-app',
cb
)
});
You specified that all tasks should run in sequence with no parallelism at all. A new task starts as soon as a former task shows completion.
Probably you have some issues in a task before the build-html task. Do they handle the completion (done() callback) correctly? In most cases I saw there was some async action triggered and the callback was called before the async task finished. So the sequence continues before the task is really over...

With gulp, are watch tasks guaranteed to run before other tasks?

I have a gulp watch task that uses gulp-sass to convert SASS to CSS. For development, separated CSS files are all I want.
For release, I have a min tasks that uses gulp-cssmin and concat to bundle and minify the CSS. However, the min task doesn't deal with the SASS files. It assumes they have already been converted to CSS.
Is this a valid assumption? What if an automated build gets the latest source (which does not have .scss files) and runs min? Is there a race condition here as to whether the watch task will run in time for min to pick up the SASS files?
If your min task is also watching the SASS files, your assumption is not necessarily valid. By default, all the tasks will run at the same time. You can however define dependencies in your task, so that a task does not start until another task is finished. This could solve your problem. The documentation about dependencies can be found here.
There are also two other solutions:
Watch the CSS files for the min task.
Run the logic in your min task at the end of the task that compiles the SASS.
Edward! Better use optional minification to prevent extra reading from the disk.
var env = require('minimist')(process.argv.slice(2));
var noop = require('readable-stream/passthrough').bind(null, { objectMode: true });
gulp.task('style', function () {
return gulp.src('...')
.pipe(process())
.pipe(env.min ? uglify() : noop())
.pipe(gulp.dest('...'))
});
Also I don't recommend these tools from gulp-util which will be deprecated
Even if gulp guaranteed that it would wait for watch tasks to complete before starting dependent tasks, a race could still occur during the interval from when a source file is changed until the OS notifies gulp. So I decided not to use the SASS → CSS transform output. For as infrequently as I need to build the minimized versions, there's no payoff for saving those milliseconds.
Instead I used stream-combiner2 to reuse the transform logic:
var combiner = require('stream-combiner2');
function scssTransform() {
return combiner(
sourcemaps.init(),
sass(),
sourcemaps.write());
}
I use the transform for my non-mimized "sandbox" for local development:
gulp.task('sandbox:css', function () {
return gulp.src(paths.scss)
.pipe(scssTransform())
.pipe(gulp.dest(dirs.styles));
});
My task for serving the site locally depends on and watches the task:
gulp.task('serve.sandbox', ['sandbox:css' /* ... more ... */], function () {
// ... Start the web server ...
gulp.watch(paths.scss, ['sandbox:css']);
// ... Watch more stuff ...
});
I also use the transform for my minimized deployment:
gulp.task('deploy:css', function () {
return gulp.src(paths.scss)
.pipe(scssTransform())
.pipe(concat(files.cssMin))
.pipe(cssmin())
.pipe(gulp.dest(dirs.deploy));
});

Gulp task order

I'm having some trouble with a couple of gulp tasks. i've tried everything and followed the documentation but nothing seems to work. Hoping someone can help.
What i'm trying to do is have sass minification happen, then rsync the result to a local vagrant folder (NOT a remote server), then reload browser sync.
The problem i am having is that I'm fighting the fact that gulp wants to run all the tasks together. I need them to happen one after the other. All the tasks work on their own, i am just having trouble making them run sequentially. I've tried callbacks and playing with dependency but i'm obviously missing something.
My setup is complicated, all my tasks are in separate js files but i've tried to combine what i have into this single github gist so people can help. Thanks for any assistance.
https://gist.github.com/CodeStalker/9661725dcaf105d2ed6c
The only way I have got this to work is to pipe rsync onto the end of the sass magnification, and wrap it in gulp-if using a server: true variable so it only does the rsync if it knows its running on a VM. Not ideal.
This is a common issue people run into with Gulp, especially when coming from Grunt. Gulp is async to the max, it always wants to do as many things as it can all at the same time.
The first step to getting tasks to run sequentially is to let gulp know when your task ends, this can be done a couple different ways.
1) Return a stream, gulp will wait for the stream's "end" event as the trigger for that task being done. Example:
gulp.task( "stream-task", function(){
return gulp.src( srcGlob )
.pipe(sass())
.pipe(compressCSS())
.pipe(gulp.dest( destGlob ));
});
2) Return a Promise, gulp will wait for the promise to enter a resolved state before signaling the task as done. Example: (not perfect just to get the point across)
gulp.task( "promise-task", function() {
return new Promise(function(resolve, reject){
fs.readFile( "filename", function( err, data ){
if( err ){ return reject(err); }
return resolve( data );
});
});
});
3) Call the task callback, if the function doesn't return anything but the function signature takes an argument, that argument will be a callback function you can call to signal the task as done. Example:
gulp.task( "cb-task", function( done ){
fs.readFile( "filename", function( err, data ){
// do stuff, call done...
done();
});
});
Most often you are going to be returning a stream like example 1, which is what a typical gulp task looks like. Options 2 and 3 are more for when you are doing something that isn't really a traditional stream based gulp task.
The next thing is setting the "dependency" of one task for another. The gulp docs show this as the way you do that:
gulp.task( "some-task-with-deps", [ "array-of-dep", "task-names" ], function(){ /* task body */ });
Now I don't know the current status but there were some issues with these dependency tasks not running in the proper order. This was originally caused by a problem with one of gulp's dependencies (orchestrator I believe). One kind gentleman out there in NPM land made a nice little package to be used in the interim while the bugs were being worked out. I started using it to order my tasks and haven't looked back.
https://www.npmjs.com/package/run-sequence
The documentation is good so I won't go into a lot of detail here. Basically run-sequence lets explicitly order your gulp tasks, just remember it doesn't work if you don't implement one of the three options above for each of your tasks.
Looking at your gist adding a couple missing return statements in your tasks may just do the trick, but as an example this is what my "dev" task looks like for my project at work...
gulp.task( "dev", function( done ){
runSequence(
"build:dev",
"build:tests",
"server:dev",
[
"less:watch",
"jscs:watch",
"lint:watch",
"traceur:watch"
],
"jscs:dev",
"lint:dev",
"tdd",
done // <-- thats the callback method to gulp let know when this task ends
);
});
Also for reference my "build:dev" task is another use of run-sequence
gulp.task( "build:dev", function( done ){
runSequence(
"clean:dev",
[
"less:dev",
"symlink:dev",
"vendor:dev",
"traceur:dev"
],
done // <-- thats the callback method to let know when this task ends
);
});
If the tasks need to be run in order the task name gets added as its own argument to runSequence if they don't conflict send them in as an array to have the tasks run at the same time and speed up your build process.
One thing to note about watch tasks! Typically watch tasks run indefinitely so trying to return the "infinite" stream from them may make gulp think that the task never ends. In that case I'll use the callback method to make gulp think the task is done even if it is still running, something like...
gulp.task( "watch-stuff", function( done ){
gulp.watch( watchGlob )
.on( "change", function( event ){ /* process file */ });
done();
});
For more on watch tasks and how I do incremental builds check out an answer I wrote the other day, Incremental gulp less build
Here you go, I believe it should work now. If not let me know, I might have made a typo.
Your gist fixed
I think you are hitting (i don't have enough reputation points to post link, google issues #96) which is fixed in 4.x which isn't out yet :-). That said, check this hack out: My post about Gulp v3.x bug