Grunt смотреть меньше только на измененный файл

Я хочу иметь gruntfile с 2 задачами: less (компилирует все файлы less) и watch (слушает изменения и перекомпилирует измененный файл).

У меня есть следующий Gruntfile.js:

module.exports = function(grunt) {
    var files = [
        {
            expand: true,
            cwd: 'media/less',
            src: ['*.less'],
            dest: 'media/css/',
            ext: '.css'
        },
        {
            expand: true,
            cwd: 'media/less/vendor',
            src: ['*.less'],
            dest: 'media/css/vendor/',
            ext: '.css'
        },
        {
            expand: true,
            cwd: 'media/admin/less',
            src: ['*.less'],
            dest: 'media/admin/css/',
            ext: '.css'
        }
    ];

    grunt.initConfig({
        less: {
            development: {
                options: {
                    compress: false,
                    yuicompress: true,
                    optimization: 2
                },
                files: files
            },
            production: {
                options: {
                    compress: true,
                    yuicompress: true,
                    optimization: 2
                },
                files: files
            }
        },
        watch: {
            styles: {
                files: ['media/**/*.less'],
                tasks: ['less:development'],
                options: {
                    nospawn: true
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-watch');

    grunt.registerTask('default', ['less:development']);
};

Задача less выполняется правильно, без проблем. Однако задача watch прослушивает изменения, но перекомпилирует все файлы при изменении одного из них. Я подозреваю, что это как-то связано с тем, как я настроил свою задачу less, потому что я хочу, чтобы мой список файлов с меньшим количеством файлов был динамическим, а не добавлял каждый файл вручную.

В соответствии с этот ответ grunt уже должен поддерживать это, но я не уверен, как это сделать.


person Eduard Luca    schedule 01.04.2014    source источник


Ответы (2)


Закончилось использованием события watch и переопределением свойства files задачи less. Вот мой окончательный код:

module.exports = function(grunt) {
    var files = [
        {
            expand: true,
            cwd: 'media/less',
            src: ['*.less'],
            dest: 'media/css/',
            ext: '.css',
            extDot: 'last'
        },
        {
            expand: true,
            cwd: 'media/less/vendor',
            src: ['*.less'],
            dest: 'media/css/vendor/',
            ext: '.css',
            extDot: 'last'
        },
        {
            expand: true,
            cwd: 'media/admin/less',
            src: ['*.less'],
            dest: 'media/admin/css/',
            ext: '.css',
            extDot: 'last'
        }
    ];

    grunt.initConfig({
        less: {
            development: {
                options: {
                    compress: false,
                    yuicompress: true,
                    optimization: 2
                },
                files: files
            },
            production: {
                options: {
                    compress: true,
                    yuicompress: true,
                    optimization: 2
                },
                files: files
            }
        },
        watch: {
            styles: {
                files: ['media/**/*.less'],
                tasks: ['less:development'],
                options: {
                    nospawn: true
                }
            }
        }
    });

    grunt.event.on('watch', function(action, filepath){
        // ignore include files, TODO: have naming convention
        // if an include file has been changed, all files will be re-compiled
        if(filepath.indexOf('.inc.') > -1)
            return true;

        // might not be the most efficient way to do this
        var srcDir = filepath.split('/');
        var filename = srcDir[srcDir.length - 1];
        delete srcDir[srcDir.length - 1];
        srcDir = srcDir.join('/');
        var destDir = srcDir.replace(/less/g, 'css');

        grunt.config('less.development.files', [{
            src: filename,
            dest: destDir,
            expand: true,
            cwd: srcDir,
            ext: '.css',
            extDot: 'last'
        }]);
    });

    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-watch');

    grunt.registerTask('default', ['less:development']);
};
person Eduard Luca    schedule 03.04.2014

Я не уверен, что вы имеете в виду под текущим заголовком вашего вопроса Меньше смотреть только на измененный файл. Вы хотите сказать, что это проблема? Это ожидаемое поведение задачи watch, она будет отслеживать файлы, указанные для изменений, и запускать указанные вами задачи — в данном случае компиляцию LESS.

Я внес некоторые изменения в ваш файл. Что-то было упрощено, что-то изменено с учетом гибкости и расширяемости скрипта.

Сначала установите underscore как зависимость, запустив:

npm install underscore --save-dev

Затем внесите следующие изменения в свой Gruntfile.js:

module.exports = function(grunt) {

    var _ = require('underscore');

    var files = {
        app : {
            '<%= path.styles.css %>/styles.css' : '<%= path.styles.less %>/*.less'
        },
        vendor : {
            '<%= path.styles.css %>/styles-vendor.css' : '<%= path.styles.vendor %>/*.less'
        },
        admin : {
            '<%= path.styles.css %>/styles-admin.css' : '<%= path.styles.admin %>/*.less'
        }
    }

    function all() {
        'use strict';
        var allfiles = {},
            i = {};

        for (i in files) {
            _.extend(allfiles, files[i]);
        }
        return allfiles;
    }

    grunt.initConfig({
        path : {
            media : 'media',
            styles : {
                css: 'media/css',
                less: 'media/less',
                admin: 'media/admin/less',
                vendor: '<%= path.styles.less %>/vendor'                
            }
        },
        less: {
            development: {
                options: {
                    compress: false,
                    yuicompress: true,
                    optimization: 2
                },
                files: (all())
            },
            production: {
                options: {
                    compress: true,
                    yuicompress: true,
                    optimization: 2
                },
                files: (all())
            }
        },
        watch: {
            styles: {
                files: ['<%= path.media %>/**/*.less'],
                tasks: ['less:development'],
                options: {
                    nospawn: true
                }
            }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-less');
    grunt.loadNpmTasks('grunt-contrib-watch');

    // run several tasks as default (handy for complex projects)
    grunt.registerTask('dist', [ // run with 'grunt dist'
        'less:production'
    ]);
    grunt.registerTask('dev', [ // default, will run with 'grunt' only
        'less:development'
    ]);

    grunt.registerTask('default', 'dev');
};

Если вы хотите на самом деле скомпилировать наборы файлов отдельно (files.app, files.vendor и files.admin), вам может потребоваться еще немного разделить задачу, например так:

        less: {
            app: {
                options: {
                    compress: false,
                    yuicompress: true,
                    optimization: 2
                },
                files: files.app
            },
            vendor: {
                options: {
                    compress: true,
                    yuicompress: true,
                    optimization: 2
                },
                files: files.vendor
            },
            admin: {
                options: {
                    compress: true,
                    yuicompress: true,
                    optimization: 2
                },
                files: files.admin
            },
            development: {
                options: {
                    compress: false,
                    yuicompress: true,
                    optimization: 2
                },
                files: (all())
            },
            production: {
                options: {
                    compress: true,
                    yuicompress: true,
                    optimization: 2
                },
                files: (all())
            }
        },
        watch: {
            all: {
                files: ['<%= path.media %>/**/*.less'],
                tasks: ['less:development'],
                options: {
                    nospawn: true
                }
            },
            app : {
                files: ['<%= path.styles.less %>/*.less'],
                tasks: ['less:app'],
                options: {
                    nospawn: true
                }
            },
            vendor : {
                files: ['<%= path.styles.vendor %>/*.less'],
                tasks: ['less:vendor'],
                options: {
                    nospawn: true
                }
            },
            admin : {
                files: ['<%= path.styles.admin %>/*.less'],
                tasks: ['less:admin'],
                options: {
                    nospawn: true
                }
            }
        }

Затем вы можете запустить любой из них:

grunt watch:app
grunt watch:vendor
grunt watch:admin

Вы всегда можете выбрать запуск задач напрямую, один раз:

grunt less:app
grunt less:vendor
grunt less:admin

Надеюсь это поможет! Обратите внимание, что я не проверял это.

person Wallace Sidhrée    schedule 01.04.2014
comment
Не совсем то, что я хочу, хотя и недалеко. Итак, желаемый поток: я меняю один файл, этот файл компилируется. Текущий поток: я меняю один файл, все файлы перекомпилируются. С вашим решением (вторым) выполняются как задача less:development, так и задача less:app. Кроме того, я также не хочу объединять файлы LESS в один файл CSS, я хочу сохранить структуру файла такой же. - person Eduard Luca; 02.04.2014
comment
Я воспроизвел вашу настройку и не смог решить головоломку. Упомянутый вами SO указывает на две проблемы, возникающие в разных проектах, которые пытаются решить одну и ту же проблему [1] и [2], но это не t было решено на 100%. На данный момент я думаю, что LESS задачи основаны на папках, как и Compass задачи (для SASS). В случае с Compass они даже явно упоминают об этом в своем файле readme. - person Wallace Sidhrée; 03.04.2014
comment
Удалось решить. Заняло около 3 часов, но, надеюсь, теперь все работает правильно. Смотрите мой ответ, чтобы увидеть, что я сделал. - person Eduard Luca; 03.04.2014