Search code examples
gulpgulp-watch

How to detect css theme and work only with this theme?


I have a project in which there are about 30 css themes. It means I have the next css files structure:

src/
    themes/
        default/
            a.scss
            b.scss
        rockStar/
            a.scss
            b.scss
        oneMoreTheme/
            a.scss
            b.scss
dist/
    themes/
        default/
            styles.css
        rockStar/
            styles.css
        oneMoreTheme/
            styles.css

Here is just example of gulpfile:

var gulp = require('gulp'),
  glob = require('glob'),
  path = require('path'),
  _ = require('underscore'),
  $ = require('gulp-load-plugins')(),
  options = {};

options.themes = [
    'default',
    'rockStar',
    'oneMoreTheme'
];

gulp.task('styles', function () {
    _.each(options.themes, function(themeName, themeKey) {
        gulp.src('src/themes/' + themeName + '/**/*.scss')
            .pipe($.concat('styles.scss'))
            .pipe($.sass())
            .pipe(gulp.dest('dist/themes/' + themeName + '/'));
    });
});

gulp.task('watch', function () {
    gulp.watch('src/**/*.*', ['styles']);
});

In my gulp file I have a task "styles", which compiles scss files from each theme and puts compiled files to dist folder. And I have task "watch" which run "styles" task when any scss file form any source theme changes. It works, but it takes much time because of lots of themes! How can my task "watch" detect from which theme files changes and run task "styles" only for this changed theme?


Solution

  • That is indeed a tough one, but here is a solution. Please refer to the comments in the code for an explanation.

    version 1

    var gulp = require('gulp');
    var merge = require('merge2');
    var $ = require('gulp-load-plugins')();
    var path = require('path');
    var options = {};
    
    options.themes = [
        'default',
        'rockStar',
        'oneMoreTheme'
    ];
    
    // we extract the task itself to a new function
    // this allows us to reuse it
    var styleTask = function(themeName) {
        return gulp.src('src/themes/' + themeName + '/**/*.scss')
            .pipe($.concat('styles.scss'))
            .pipe($.sass())
            .pipe(gulp.dest('dist/themes/' + themeName + '/'));
    }
    
    // we adapt the style task to use that function
    // please note that I switched _.each for merge
    // this allows you to work with streams!
    gulp.task('styles', function() {
        var tasks = themes.map(styleTask);
        return merge(tasks);
    });
    
    // here we set up a new watcher. Instead of running 'styles'
    // we filter the theme directory from the file that has changed
    // and run the styleTask function
    gulp.task('default', function() {
        var watcher = gulp.watch('src/themes/**/*.scss', function(e) {
            var theme = path
                .relative(__dirname, e.path)
                .substr('src/themes/'.length)
                .split('/')[0];
            console.log('rebuilding ' + theme);
            return styleTask('theme');
        });
    });
    

    version 2

    // same as above except the default task. we save the theme
    // we want to build in a file
    var singleTheme;
    
    // and call the styleTask function should it be set
    gulp.task('single-style', function(done) {
        if(singleTheme) {
            return styleTask(singleTheme);
        } else {
            done();
        }
    });
    
    // here we have a watcher that calls single-style, but before calling
    // it gets the right themename.
    gulp.task('default', function() {
        var watcher = gulp.watch('src/themes/**/*.scss', 'single-style');
        watcher.on('change', function(e) {
            singleTheme = path
                .relative(__dirname, e.path)
                .substr('src/themes/'.length)
                .split('/')[0];
            console.log('rebuilding ' + theme);
        })
    })
    

    I hope this helped.

    Update If you want to run more tasks and have a status call on if they ended, please go for version 2. Than you can add all the tasks you want to run in

    gulp.task('default', function() {
        var watcher = gulp.watch('src/themes/**/*.scss', ['single-style', 'another-task']);
        watcher.on('change', function(e) {
            singleTheme = path
                .relative(__dirname, e.path)
                .substr('src/themes/'.length)
                .split('/')[0];
            console.log('rebuilding ' + theme);
        })
    })
    

    Instead of gulp.run you can use gulp.start.