How to stream sass into browserSync without Gulp? - node.js

I am trying to stream sass into browsersync without gulp. Here is my code
function streamSass() {
dev.sassTasks.forEach(task => {
const sass = new Sass(task.src, task.dest, task.opts)
sass.write().then(() => {
const stream = fs.createReadStream(sass.dest.segments.absolutePath)
stream.pipe(browserSync.stream())
})
})
}
The Sass class just process and creates css file. When the function executed nothing happens on the side of the browsersync. Why is that? Assume that browsersync is running and is active when function is executed.

If you are not seeing anything happen then I would wager that there is something wrong with your stream as it will not throw an error and without an event handler it will silently fail.
Try adding an error event handler:
stream.on('error', err => console.log(err));

Related

"Write after end": how to imitate gulp-watch with watchify?

The following Gulp task does almost what I want.
const gulp = require('gulp');
const browserify = require('browserify');
const vinylStream = require('vinyl-source-stream');
const vinylBuffer = require('vinyl-buffer');
const watchify = require('watchify');
const glob = require('glob');
const jasmineBrowser = require('gulp-jasmine-browser');
gulp.task('test', function() {
let testBundler = browserify({
entries: glob.sync('src/**/*-test.js'),
cache: {},
packageCache: {},
}).plugin(watchify);
function updateSpecs() {
return testBundler.bundle()
.pipe(vinylStream(jsBundleName))
.pipe(vinylBuffer())
.pipe(jasmineBrowser.specRunner({console: true}))
.pipe(jasmineBrowser.headless({driver: 'phantomjs'}));
}
testBundler.on('update', updateSpecs);
updateSpecs();
});
It bundles all my Jasmine specs using Browserify and has them tested through gulp-jasmine-browser. It also watches all specs and all modules that they depend on and re-runs the tests if any of these modules changes.
The only ugly bit, which I'd really like to see solved, is that a new PhantomJS instance and a new Jasmine server are created every time updateSpecs is run. I was hoping to avoid that with code like the following:
gulp.task('test', function() {
let testBundler = browserify({
entries: glob.sync('src/**/*-test.js'),
cache: {},
packageCache: {},
}).plugin(watchify);
// persist the Jasmine server and PhantomJS browser
let testServer = jasmineBrowser.headless({driver: 'phantomjs'});
function updateSpecs() {
return testBundler.bundle()
.pipe(vinylStream(jsBundleName))
.pipe(vinylBuffer())
.pipe(jasmineBrowser.specRunner({console: true}))
.pipe(testServer);
}
testBundler.on('update', updateSpecs);
updateSpecs();
});
Alas, this doesn't work. Right after starting the task, all tests run fine, but the next time updateSpecs is called, I get a write after end error and the task exits with status 1. This error originates from the readable-stream Node module.
As I understand it, the end event during the first run of updateSpecs leaves testServer in a state in which it doesn't accept any new inputs. Unfortunately, the Node.js streams documentation isn't very clear on how to remedy this.
I have tried breaking the pipe chain at a different place, but I got the same result, which seems to indicate this is universal behaviour for streams. I also tried stopping the end event from propagating by inserting a through-stream that didn't re-emit that event, but this prevented the tests from being run at all. Finally, I tried returning the testServer stream from the task; this stopped the error, but although the updateSpecs function gets called every time the sources change, the tests are only being run the first time the task starts. This time, the testServer simply seems to ignore the new input.
The gulp-jasmine-browser documentation suggests that the following code would work:
var watch = require('gulp-watch');
gulp.task('test', function() {
var filesForTest = ['src/**/*.js', 'spec/**/*-test.js'];
return gulp.src(filesForTest)
.pipe(watch(filesForTest))
.pipe(jasmineBrowser.specRunner())
.pipe(jasmineBrowser.server());
});
And it goes on to suggest that you can also make this work with Browserify, but this isn't illustrated. Apparently, gulp-watch does something which causes the follow-up pipes to accept updated inputs later. How can I imitate this behaviour with watchify?
GitHub issue
As it turns out, it is a hard rule in Node.js that you cannot write after the end event. In addition, jasmineBrowser.specRunner(), .server() and .headless() must receive the end signal in order to actually test anything. This restriction is inherited from the official Jasmine test runner.
The example with gulp-watch from the README doesn't actually work, either, for the same reason. In order to make it work, one would have to do something similar to the working version of my watchify code in the question:
gulp.task('test', function() {
var filesForTest = ['src/**/*.js', 'spec/**/*-test.js'];
function runTests() {
return gulp.src(filesForTest)
.pipe(jasmineBrowser.specRunner())
.pipe(jasmineBrowser.server());
}
watch(filesForTest).on('add change unlink', runTests);
});
(I didn't test it, but something very close to this should work.)
So whatever watching mechanism you're using, you'll always need to call .specRunner() and .server() again for every cycle. The good news is that apparently, the Jasmine server will be reused if you explicitly pass a port number:
.pipe(jasmineBrowser.server({port: 8080}));
this also applies to .headless().

How can process.stdin be used as the start point for a gulp task?

I'm using gulp to convert SCSS into CSS code with the gulp-sass plugin. This is all working fine, but I also want to use gulp to receive input (SCSS code) from a Unix pipe (i.e. read process.stdin) and consume this and stream the output to process.stdout.
From reading around process.stdin is a ReadableStream and vinyl seems like it could wrap stdin and then be used onwards in a gulp task, e.g.
gulp.task('stdin-sass', function () {
process.stdin.setEncoding('utf8');
var file = new File({contents: process.stdin, path: './test.scss'});
file.pipe(convert_sass_to_css())
.pipe(gulp.dest('.'));
});
However, when I do this I get an error:
TypeError: file.isNull is not a function
This makes me think that stdin is somehow special, but the official documentation for node.js states that it is a true ReadableStream.
So I got this to work by processing process.stdin and writing to process.stdout:
var buffer = require('vinyl-buffer');
var source = require('vinyl-source-stream');
var through = require('through2');
gulp.task('stdio-sass', function () {
process.stdin.setEncoding('utf8');
process.stdin.pipe(source('input.scss'))
.pipe(buffer())
.pipe(convert_sass_to_css())
.pipe(stdout_stream());
});
var stdout_stream = function () {
process.stdout.setEncoding('utf8');
return through.obj(function (file, enc, complete) {
process.stdout.write(file.contents.toString());
this.push(file);
complete();
});
};

Gulp streams not supported Error using gulp-istanbul

I'm getting an error with streams.
I'm working on adding istanbul to my existing mocha task. When I run this task I get the error below.
I'm using gulp-istanbul
(note: the config.test.src.bdd.features is set to the value 'test/bdd/features/**/*-spec.js')
var stream = gulp.src([config.test.src.bdd.features], { read: false });
gulp.task('mocha-bdd-features', function(cb) {
process.env.PORT = 8001;
return stream
.pipe(istanbul())
.pipe(istanbul.hookRequire())
.pipe(mocha({
compilers: {
js: babel
},
reporter: config.test.mocha.reporter,
ui: 'bdd'
}))
.on('finish', function () {
stream.pipe(istanbul.writeReports())
stream.pipe(istanbul.enforceThresholds({thresholds: {global: 90}}))
stream.on('end', cb);
});
});
the error I get is:
events.js:85
throw er; // Unhandled 'error' event
^
Error: streams not supported
and who knows I may not be setting up this task right when trying to incorporate gulp-istanbul but trying to at least get past this error first.
I was facing the exact same issue.
I believe the problem is in line:
var stream = gulp.src([config.test.src.bdd.features], { read: false });
Setting the read option to false causes the file.contents to be null and therefore istanbul is not able to cover the files. (check here)
so try the same thing but without the read option.
var stream = gulp.src([config.test.src.bdd.features]);
Hope this helps.

How can I make gulp-watch generate CSS file when SASS changes?

Here is my code:
gulp.task('sass', function () {
gulp.src('./public/stylesheets/*.scss')
.pipe(sass())
.pipe(gulp.dest('./public/stylesheets/'));
});
gulp.task('watch-saas', function () {
watch('./public/stylesheets/*.scss', function () {
gulp.start('sass');
});
});
Output:
[19:15:46] Using gulpfile ~/WebstormProjects/mySite/gulpFile.js
[19:15:46] Starting 'watch-saas'...
[19:15:46] Finished 'watch-saas' after 38 ms
Im afraid no CSS. Any ideas how to make this work?
I think my code looks very much like the example code here. And the 'sass' task runs fine on its own.
I'm not that experienced in Gulp, but I usually use:
gulp.task('watch-saas', function () {
return gulp.watch(['./public/stylesheets/*.scss'], ['sass']);
});
I assume that you have to return the result in your task, because it's an asynchronous task.
We should first pipe it and ask gulp to watch it in changes in our scss or file we alerted
var gulp = require('gulp')
var watch = require('gulp-watch');
gulp.task('styles', function(){
return gulp.src(''./public/stylesheets/.scss'').pipe(gulp.dest('./sass'));
});
watch('./public/stylesheets/.scss', function(){
gulp.start('styles');
});
styles is the task name which I assigned here

gulp-sass, watch stops when invalid property name

watch stops when error messages occur.
stream.js:94
throw er; // Unhandled stream error in pipe.
^
source string:51: error: invalid property name
How I can keep watch running and just to tell me where is the error located.
grunt could deal with errors and doesn't need to stop,
styleSheet.scss:41: error: invalid property name
otherwise, I need to keep typing "gulp" in the command-line when an error occurs.
This answer has been appended to reflect recent changes to Gulp. I've retained the original response, for relevance to the OPs question. If you are using Gulp 2.x, skip to the second section
Original response, Gulp 1.x
You may change this default behavior by passing errLogToConsole: true as an option to the sass() method.
Your task might look something like this, right now:
gulp.task('sass', function () {
gulp.src('./*.scss')
.pipe(sass())
.pipe(gulp.dest('./'));
});
Change the .pipe(sass()) line to include the errLogToConsole: true option:
.pipe(sass({errLogToConsole: true}))
This is what the task, with error logging, should look like:
gulp.task('sass', function () {
gulp.src('./*.scss')
.pipe(sass({errLogToConsole: true}))
.pipe(gulp.dest('./'));
});
Errors output will now be inline, like so:
[gulp] [gulp-sass] source string:1: error: invalid top-level expression
You can read more about gulp-sass options and configuration, on nmpjs.org
Gulp 2.x
In Gulp 2.x errLogToConsole may no longer be used. Fortunately, gulp-sass has a method for handling errors. Use on('error', sass.logError):
gulp.task('sass', function () {
gulp.src('./sass/**/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('./css'));
});
If you need more fine-grained control, feel free to provide a callback function:
gulp.task('sass', function () {
gulp.src('./sass/**/*.scss')
.pipe(sass()
.on('error', function (err) {
sass.logError(err);
this.emit('end');
})
)
.pipe(gulp.dest('./css'));
});
This is a good thread to read if you need more information on process-control: https://github.com/gulpjs/gulp/issues/259#issuecomment-55098512
Actually above anwsers doesn't work for me (Im using gulp-sass 3.XX). What really worked:
gulp.task('sass', function () {
return gulp.src(config.scssPath + '/styles.scss')
.pipe(sourcemaps.init())
.pipe(sass({ outputStyle: 'compressed' })
.on('error', sass.logError)
)
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(config.cssPath))
});
In gulp-sass 3.x.x when I was using "sass.logError(err);" I constantly recive error that "this.emit('end'); is not a function". Now when I'm using:
.pipe(sass({ outputStyle: 'compressed' })
.on('error', sass.logError)
)
everything is working like a charm
In gulp "^2.0.0" the option errLogToConsole will no longer work. Instead gulp-sass has a built in error logging callback that uses gulp-util under the hood. Also, because gulp has some problems with killing the process on errors, if you are using with watch you will have to call this.emit('end')
https://github.com/gulpjs/gulp/issues/259#issuecomment-55098512
var sass = require('gulp-sass');
//dev
sass(config.sassDev)
.on('error', function(err) {
sass.logError(err);
this.emit('end'); //continue the process in dev
})
)
//prod
sass(config.sassProd).on('error', sass.logError)
A heads up for Gulp 3 users:
I liked #dtotheftp solution above, regarding gulp 2.x. Interestingly, it doesn't work unter Gulp3, at least not under #3.9.1:
.on('error', function(err){
sass.logError(err);
this.emit('end'); //continue the process in dev
})
gets me
TypeError: this.emit is not a function
at Function.logError (/depot/myproject/node_modules/gulp-sass/index.js:181:8)
Note, that the complaint is not coming from his this.emit() in the gulpfile but rather from the sass node-module, hence from the prior line.
This works for me:
.on('error', function(err){
gutil.log(err);
this.emit('end');
})
I do get all errors², and the watch never ends ;) (I am also using plumber() right after gulp.src(), which might help with that).
(Yes, the fix might be highly illogical, since sass.logError is said to be based on gutil...)
—
²also on undefined macros which went silent before on my setup for whatever reason.

Resources