0

I have a gulp css task that picks up a CSS file and runs several postCSS processors on it, then writes the file to a destination directory.

I have an html task that uses gulp-smoosher to pull the CSS file into the HTML file, replacing the link tag to the CSS file.

When I run the tasks separately from the command line, everything works as expected. However, when I use gulp.watch to automatically run the tasks when the CSS file changes, the changes aren't reflected in the final HTML file.

Is the html task somehow picking up the CSS file before the css task writes the changes? Is there a way to make sure the css task has finished before running the html task?

Update: I've done some reading and I realize Gulp runs both my css and html tasks at the same time. That explains why the CSS file isn't written yet when I start the html task. I've seen some solutions, but they either don't work or I don't understand how to use them. Here's my attempt at using the run-sequence plugin:

gulp.task('csshtml', function() {
    runSequence('css', 'html');
});

gulp.task('watch', function() {
    gulp.watch(paths.css, ['csshtml']);
});

... but the results were the same. I'm sure I'm doing something wrong.

gulpfile.js:

var gulp = require('gulp');
var postcss = require('gulp-postcss');
var autoprefixer = require('autoprefixer');
var cssnext = require('cssnext');
var precss = require('precss');
var nesting = require('postcss-nesting');
var cssnano = require('cssnano');
var htmlmin = require('gulp-htmlmin');
var smoosher = require('gulp-smoosher');

var paths = {
    css: 'src/*.css',
    html: 'src/*.html'
};

gulp.task('css', function() {
    var processors = [
        nesting,
        autoprefixer,
        cssnext,
        precss,
        cssnano
    ];
    return gulp.src(paths.css)
        .pipe(postcss(processors))
        .pipe(gulp.dest('css'));
});

gulp.task('html', function() {
  return gulp.src('src/*.html')
    .pipe(smoosher({ base: '.' }))
    .pipe(htmlmin({
        collapseWhitespace: true,
        conservativeCollapse: true,
        removeComments: true,
        collapseInlineTagWhitespace: true,
        collapseBooleanAttributes: true,
        removeAttributeQuotes: true,
        removeRedundantAttributes: true,
        removeEmptyAttributes: true,
        removeScriptTypeAttributes: true,
        removeStyleLinkTypeAttributes: true,
        removeOptionalTags: true,
        minifyCSS: true
    }))
    .pipe(gulp.dest('.'))
});

gulp.task('watch', function(){
    gulp.watch(paths.css, ['css', 'html']);
    gulp.watch(paths.html, ['html']);
});
Vince
  • 3,962
  • 3
  • 33
  • 58

1 Answers1

2

I am going to try and answer some of your questions:

Is the html task somehow picking up the CSS file before the css task writes the changes? Is there a way to make sure the css task has finished before running the html task?

Yes there is a way to make a task finish before the next one executes. You can do this via the second parameter (which is an array/list of dependencies) in a gulp task.

gulp.task("html", ["css"], function () {
    // additonal code goes here
});

This simple example is saying, before you run the html task, run the css task first. Once thats is done, then run the html task.

Issues with your watch task:

In your watch task you're saying when it detects a file change in the paths.css config value, run both css and html in parallel (at the same time).

gulp.task('watch', function(){
    gulp.watch(paths.css, ['css', 'html']);
    gulp.watch(paths.html, ['html']);
});

If you want your task to run in a particular order then you can make one depend on the other. As shown above.

gulp.task("watch", function() {
    gulp.watch(path.css, ["css"]);
    gulp.watch(paths.html, ["html"]);
});

You can also create a default task in gulp which runs both these without having to type the full task name. The example (only) shows the default task kicking off html task and watch for file changes and performed the necessary task if it detects file changes.

gulp.task("default", ["html", "watch"]);

This is what I think you wanted to do, let me know if its incorrect, I'll update as necessary:

var gulp = require('gulp');
var postcss = require('gulp-postcss');
var autoprefixer = require('autoprefixer');
var cssnext = require('cssnext');
var precss = require('precss');
var nesting = require('postcss-nesting');
var cssnano = require('cssnano');
var htmlmin = require('gulp-htmlmin');
var smoosher = require('gulp-smoosher');

var paths = {
    css: 'src/*.css',
    html: 'src/*.html'
};

gulp.task('css', function() {
    var processors = [
        nesting,
        autoprefixer,
        cssnext,
        precss,
        cssnano
    ];
    return gulp.src(paths.css)
        .pipe(postcss(processors))
        .pipe(gulp.dest('css'));
});

gulp.task('html', ['css'],  function() {
    return gulp.src('src/*.html')
        .pipe(smoosher({ base: '.' }))
        .pipe(htmlmin({
            collapseWhitespace: true,
            conservativeCollapse: true,
            removeComments: true,
            collapseInlineTagWhitespace: true,
            collapseBooleanAttributes: true,
            removeAttributeQuotes: true,
            removeRedundantAttributes: true,
            removeEmptyAttributes: true,
            removeScriptTypeAttributes: true,
            removeStyleLinkTypeAttributes: true,
            removeOptionalTags: true,
            minifyCSS: true
        }))
        .pipe(gulp.dest('.'));
});

gulp.task("default", ["html", "watch"]);

gulp.task("watch", ["watch:css", "watch:html"]);

gulp.task("watch:css", function() {
    gulp.watch(paths.css, ["css"]);
});

gulp.task("watch:html", function () {
    gulp.watch(paths.html, ["html"]);
});

An alternate, if you don't want html to depend on your css task and you are using run-sequence from the looks of things, you could make the following changes:

1. Remove the dependency

gulp.task("html", function () {
    // your code...
});

2. Add the require

var runSequence = require("run-sequence");

3. Setup the run-sequence

gulp.task("default", function () {
    runSequence(
        "css", 
        "html", 
        "watch"
    ); 
});

This will run them in that order and watch for any file changes and run the appropriate task based on what you've changed. The HTML task no longer depends on the css task. The watch will behave as you'd expect.

TheLazyChap
  • 1,852
  • 1
  • 19
  • 32
  • I read about the task dependency option after I posted the question. The only problem with adding the css task as a dependency of the html task is that the css task runs even if I only change the HTML. This isn't much of problem because the task finishes so quickly, but I'm going to wait a day before I mark yours as the accepted answer just in case someone knows a way to make the `gulp.watch` tasks run in order. – Vince Jan 18 '16 at 07:27
  • @Vince do you have this on github or somewhere, we can pull down and help you further, this will help diagnoise your issue a lot better. Feel free to keep asking if you aren't sure. If there is a better answer than mark that instead. Also you could create multiple watch tasks specific to that particular thing you want to watch. In your example you clumped them all into one place. I personally like to separate my watch into multiple smaller tasks. – TheLazyChap Jan 18 '16 at 07:36
  • I'm not sure anything outside of the `gulpfile.js` is relevant for this particular question, but I really appreciate any help / criticism and I guess I don't have anything to hide. I'm currently working on my own site and I just mirrored my git repo on github: https://github.com/Ghodmode/ghodmode – Vince Jan 18 '16 at 08:13
  • Adding the css task as a dependency of the html task solved the problem and using the [run-sequence](https://www.npmjs.com/package/run-sequence) plugin was an even better solution. My first attempt at using the plugin didn't work, but now it's working fine. I also found [an answer](http://stackoverflow.com/a/31329150/2948042) to a similar question that tells me that the next major release of Gulp will solve the problem without a plugin. – Vince Jan 19 '16 at 03:23