Как можно перезапустить Gulp при каждом изменении Gulpfile?

Я разрабатываю Gulpfile. Можно ли его перезапустить, как только он изменится? Я разрабатываю его в CoffeeScript. Может ли Gulp смотреть Gulpfile.coffee, чтобы перезапустить после сохранения изменений?


person coool    schedule 05.04.2014    source источник


Ответы (14)


Вы можете создать task, который будет gulp.watch вместо gulpfile.js, и просто spawn еще один gulp child_process.

var gulp = require('gulp'),
    argv = require('yargs').argv, // for args parsing
    spawn = require('child_process').spawn;

gulp.task('log', function() {
  console.log('CSSs has been changed');
});

gulp.task('watching-task', function() {
  gulp.watch('*.css', ['log']);
});

gulp.task('auto-reload', function() {
  var p;

  gulp.watch('gulpfile.js', spawnChildren);
  spawnChildren();

  function spawnChildren(e) {
    // kill previous spawned process
    if(p) { p.kill(); }

    // `spawn` a child `gulp` process linked to the parent `stdio`
    p = spawn('gulp', [argv.task], {stdio: 'inherit'});
  }
});

Я использовал yargs, чтобы разрешить выполнение "основной задачи" после перезагрузки. Итак, чтобы запустить это, вы должны позвонить:

gulp auto-reload --task watching-task

И для проверки вызовите touch gulpfile.js или touch a.css, чтобы просмотреть журналы.

person Caio Cunha    schedule 06.04.2014
comment
Это хороший трюк. Одна вещь, которую я хотел бы сделать, это назвать задачу как-то вроде auto-reload и запустить ее как gulp auto-reload --task foo, иначе вы можете получить форк-бомбу, рекурсивно запускающую задачу по умолчанию. - person OverZealous; 06.04.2014
comment
Хорошее предложение @OverZealous. Применил изменения, чтобы избежать этой проблемы для будущих читателей. - person Caio Cunha; 06.04.2014
comment
Это кажется сложным, хотелось бы, чтобы это было так же просто, как в Grunt - person fregante; 17.05.2014
comment
Этот метод также имеет множество побочных эффектов, таких как случайные ошибки (2014-07-29 11:48 gulp[74366] (CarbonCore.framework) FSEventStreamStart: register_with_server: ERROR: f2d_register_rpc() => (null) (-21)) и, например. localhost:3000 номер порта увеличивается при использовании BrowserSync - person Murray Rowan; 29.07.2014
comment
В Windows вместо require('child_process') лучше использовать require('child-proc') - person Alexander Shagin; 05.12.2014
comment
@MurraySmith Да, с подходом есть некоторые проблемы ... Я согласен. Я все еще ищу хороший способ сделать это, но я думаю, что это потребует изменения исходного кода Gulp. Я начал работать над этим изменением, но пока не хватило времени. - person Caio Cunha; 05.12.2014
comment
Стоит отметить, что это будет работать только в Windows с использованием модуля cross_spawn. npmjs.com/package/cross-spawn - person DerekMT12; 21.01.2016
comment
Потрясающий ответ. Работал отлично (Y) - person softvar; 11.03.2018
comment
Новое поведение в gulp v4: функции-наблюдателю (spawnChildren()) передается аргумент обратного вызова, который должен быть вызван после выполнения spawnChildren(), иначе задача auto-reload никогда не вернется к отслеживанию дальнейших изменений (т. е. перезагрузка работает только один раз). github.com/gulpjs/gulp/blob/ v4.0.0/docs/API.md#fn-1 - person Sphinxxx; 24.03.2018

Я создал gulper, который является оболочкой gulp.js cli для перезапуска gulp при изменении gulpfile.

Вы можете просто заменить gulp на gulper.

$ gulper <task-name>
person anatoo    schedule 28.01.2015
comment
Очень простое и элегантное решение. - person cantfindaname88; 25.03.2015
comment
Жаль, что я не видел это раньше. Теперь я застрял в собственной реализации: github.com/samvv/node-galop - person samvv; 29.04.2016
comment
Позор мне, я использовал его в течение года с тех пор, как впервые увидел этот ответ, и только сейчас понял, что не проголосовал за него. Спасибо за полезный инструмент! - person baldrs; 29.10.2016
comment
хорошо для Linux и Mac, но не работает в Windows - он пытается запустить CMD gulp как JS - person SynCap; 30.07.2017
comment
Ты звездный друг :) - person Vik; 07.08.2017

Для этой цели я использую небольшой сценарий оболочки. Это работает и в Windows.

Нажмите Ctrl+C, чтобы остановить скрипт.

// gulpfile.js
gulp.task('watch', function() {
    gulp.watch('gulpfile.js', process.exit);
});

Сценарий оболочки Bash:

# watch.sh
while true; do
    gulp watch;
done;

Версия Windows: watch.bat

@echo off
:label
cmd /c gulp watch
goto label
person Simon Epskamp    schedule 11.03.2015
comment
Простой, понятный (по крайней мере для меня), уж точно никаких утечек. - person tuomassalo; 02.09.2015
comment
Это единственное, что у меня сработало, так как gulper выдает ошибки в винде. - person Dylan; 08.03.2016

Я получил кучу ошибок EADDRINUSE с решением в ответе Кайо Кунья. Мой gulpfile открывает локальный веб-сервер с connect и LiveReload. Похоже, что новый процесс gulp ненадолго сосуществует со старым, прежде чем старый процесс будет уничтожен, поэтому порты все еще используются процессом, который скоро умрет.

Вот похожее решение, которое решает проблему сосуществования (в основном основанное на этом ):

var gulp = require('gulp');
var spawn = require('child_process').spawn;

gulp.task('gulp-reload', function() {
  spawn('gulp', ['watch'], {stdio: 'inherit'});
  process.exit();
});

gulp.task('watch', function() {
  gulp.watch('gulpfile.js', ['gulp-reload']);
});

Это работает довольно хорошо, но имеет один довольно серьезный побочный эффект: Последний процесс gulp отключается от терминала. Таким образом, когда gulp watch завершает работу, потерянный процесс gulp все еще выполняется. Мне не удалось обойти эту проблему, дополнительный процесс gulp можно остановить вручную или просто сохранить синтаксическую ошибку в gulpfile.js.

person joemaller    schedule 15.12.2014
comment
чтобы перезапустить любую задачу: process.argv.shift(); spawn(process.argv.shift(), process.argv, {stdio: 'inherit'}); process.exit() - person Mbrevda; 15.01.2016

Другое решение для этого — обновить файл require.cache.

var gulp = require('gulp');

var __filenameTasks = ['lint', 'css', 'jade'];
var watcher = gulp.watch(__filename).once('change', function(){
  watcher.end(); // we haven't re-required the file yet
                 // so is the old watcher
  delete require.cache[__filename];
  require(__filename);
  process.nextTick(function(){
    gulp.start(__filenameTasks);
  });
});
person stringparser    schedule 18.09.2014
comment
Редактировать: лучше использовать once, иначе у вас могут быть утечки памяти - person stringparser; 11.11.2014
comment
Это все еще течет: (узел) предупреждение: обнаружена возможная утечка памяти EventEmitter. Добавлено 11 прослушивателей изменений. Используйте emitter.setMaxListeners() для увеличения лимита - person sleepycal; 30.04.2015
comment
Я немного изменил его. Проблема в том, что даже если вы используете один раз, наблюдатель все еще там, поэтому вам также нужно end его - person stringparser; 01.05.2015
comment
В конце концов, мое исправление состояло в том, чтобы прекратить использовать глоток, хех - person sleepycal; 01.05.2015

Я знаю, что это очень старый вопрос, но это главный комментарий в Google, поэтому он все еще очень актуален.

Вот более простой способ, если ваш исходный gulpfile.js находится в другом каталоге, чем тот, который используется. (Это важно!) Он использует модули gulp gulp-newer и gulp-data.

var gulp          = require('gulp'         )
  , data          = require('gulp-data'    )
  , newer         = require('gulp-newer'   )
  , child_process = require('child_process')
;

gulp.task( 'gulpfile.js' , function() {
    return gulp.src( 'sources/gulpfile.js' ) // source
        .pipe( newer(     '.'            ) ) // check
        .pipe( gulp.dest( '.'            ) ) // write
        .pipe( data( function(file)        { // reboot
            console.log('gulpfile.js changed! Restarting gulp...') ;
            var t , args = process.argv ;
            while ( args.shift().substr(-4) !== 'gulp' ) { t=args; }
            child_process.spawn( 'gulp' , args , { stdio: 'inherit' } ) ;
            return process.exit() ;
        } ) )
    ;
} ) ;

Это работает следующим образом:

  • Уловка 1: gulp-newer выполняет следующие каналы только в том случае, если исходный файл новее текущего. Таким образом мы убеждаемся, что нет цикла перезагрузки.
  • Цикл while удаляет все до и включая команду gulp из командной строки, поэтому мы можем передать любые аргументы.
  • child_process.spawn порождает новый процесс gulp, передающий ввод-вывод и ошибку родительскому процессу.
  • Уловка 2: process.exit убивает текущий процесс. Однако процесс будет ждать смерти, пока дочерний процесс не завершится.

Есть много других способов вставить функцию перезапуска в пайпы. Я так или иначе использую gulp-data в каждом из моих gulpfiles. Не стесняйтесь комментировать собственное решение. :)

person bid    schedule 12.11.2016

Я имел дело с той же проблемой, и решение в моем случае было на самом деле очень простым. Две вещи.

  1. npm install nodemon -g (или локально, если хотите)
  2. запустите с помощью cmd или создайте скрипт в таких пакетах:

    "dev": "nodemon --watch gulpfile.js --exec gulp"
    
  3. Просто введите npm run dev

--watch указывает файл, за которым нужно следить. --exec говорит выполнить следующее в очереди, а gulp является вашей задачей по умолчанию. Просто передайте аргумент, если вам нужна задача не по умолчанию.

Надеюсь, поможет.

РЕДАКТИРОВАТЬ: Сделать это модно ;) Теперь, хотя первая часть должна достичь того, что вы хотели, в моей настройке мне нужно было добавить немного больше, чтобы сделать ее действительно удобной для пользователя. Я хотел, чтобы

  1. Сначала откройте страницу.
  2. Найдите изменения в gulpfile.js и перезапустите gulp, если они есть.
  3. Глотайте его, так что следите за файлами, перестраивайте и перезагружайте

Если делать только то, что я сказал в первой части, то страница будет каждый раз открываться. Чтобы исправить это, создайте задачу gulp, которая откроет страницу. Как это :

gulp.task('open', function(){
return gulp
.src(config.buildDest + '/index.html')
.pipe(plugins.open({
    uri: config.url
}));

Тогда в моих основных задачах у меня есть:

gulp.task('default', ['dev-open']);
gulp.task('dev-open', function(done){
    plugins.sequence('build', 'connect', 'open', 'watch', done);
});
gulp.task('dev', function(done){
    plugins.sequence('build', 'connect', 'watch', done);
});

Затем изменив ваши сценарии npm на

"dev": "gulp open & nodemon --watch gulpfile.js --watch webpack.config.js --exec gulp dev"

Даст вам именно то, что вы хотите. Сначала откройте страницу, а затем просто продолжайте перезагрузку. Кстати, для livereload я использую тот, который поставляется с подключением, который всегда использует один и тот же порт. Надеюсь, это сработает для вас, наслаждайтесь!

person Peter Kottas    schedule 06.06.2016
comment
Nodemon — это именно то, что мне было нужно. Я совсем забыл о его существовании. Какой позор. - person cram2208; 05.05.2021

Вот еще одна версия кода перезагрузки @CaioToOn, которая больше соответствует обычной процедуре задачи Gulp. Это также не зависит от yargs.

Требовать создание и инициализацию переменной процесса (yargs не требуется):

var spawn = require('child_process').spawn;
var p;

Задача gulp по умолчанию будет генератором:

gulp.task('default', function() {
  if(p) { p.kill(); }
  // Note: The 'watch' is the new name of your normally-default gulp task. Substitute if needed.
  p = spawn('gulp', ['watch'], {stdio: 'inherit'});
});

Ваша задача просмотра, вероятно, была вашей задачей глотка по умолчанию. Переименуйте его в watch и добавьте gulp.watch() для просмотра вашего gulpfile и запускайте задачу default при изменениях:

gulp.task('watch', ['sass'], function () {
  gulp.watch("scss/*.scss", ['sass']);
  gulp.watch('gulpfile.js', ['default']);
});

Теперь просто запустите gulp, и он автоматически перезагрузится, если вы измените gulpfile!

person ddunderfelt    schedule 21.10.2014
comment
Этот ответ не касается открытых соединений, которые могут привести к ошибкам. См. здесь решение проблемы%: stackoverflow.com/a/27477988/747749 - person Mbrevda; 15.01.2016

попробуйте этот код (только для платформы win32)

gulp.task('default', ['less', 'scripts', 'watch'], function(){
    gulp.watch('./gulpfile.js').once('change' ,function(){
        var p;
        var childProcess = require('child_process');
        if(process.platform === 'win32'){
            if(p){
                childProcess.exec('taskkill /PID' + p.id + ' /T /F', function(){});
                p.kill();
            }else{
                p = childProcess.spawn(process.argv[0],[process.argv[1]],{stdio: 'inherit'});
            }
        }
    });
});
person Haydar C.    schedule 09.05.2016
comment
это единственное решение, которое сработало для меня в Windows. Но все равно не убивает предыдущий процесс, в конце приходится вручную убивать все порожденные процессы. - person nazikus; 11.08.2016
comment
также, если вы используете connect.reload(), вам нужно запустить connect.serverClose() перед обработкой процесса. В противном случае порт не освобождается и выдает больше ошибок. - person nazikus; 11.08.2016

Хорошее решение для Windows, которое также хорошо работает с исполнителем задач Visual Studio.

/// <binding ProjectOpened='auto-watchdog' />
const spawn = require('child-proc').spawn,
      configPaths = ['Gulpconfig.js', 'bundleconfig.js'];

gulp.task('watchdog', function () {
    // TODO: add other watches here

    gulp.watch(configPaths, function () {
        process.exit(0);
    });
});

gulp.task('auto-watchdog', function () {
    let p = null;

    gulp.watch(configPaths, spawnChildren);
    spawnChildren();

    function spawnChildren() {
        const args = ['watchdog', '--color'];

        // kill previous spawned process
        if (p) {
            // You might want to trigger a build as well
            args.unshift('build');

            setTimeout(function () {
                p.kill();
            }, 1000);
        }

        // `spawn` a child `gulp` process linked to the parent `stdio`
        p = spawn('gulp', args, { stdio: 'inherit' });
    }
});

Основные изменения по сравнению с другими ответами:

  • Использует child-proc, поскольку в Windows происходит сбой child_process.
  • Сторожевой таймер завершает работу при изменении файлов, потому что в Windows вызов gulp заключен в пакетный сценарий. Уничтожение пакетного сценария не убьет сам gulp, что приведет к появлению нескольких часов с течением времени.
  • Создание изменений: обычно изменение gulpfile также требует пересборки проекта.
person Sebazzz    schedule 25.01.2017

Установите nodemon глобально: npm i -g nodemon

И добавьте в свой .bashrc (или .bash_profile или .profile) псевдоним:

alias gulp='nodemon --watch gulpfile.js --watch gulpfile.babel.js --quiet --exitcrash --exec gulp'

Это будет отслеживать изменения файлов gulpfile.js и gulpfile.babel.js. (см. Google)

P.S. Это может быть полезно для бесконечных задач (например, watch), но не для однократных задач. Я имею в виду, что он использует часы, поэтому он продолжит процесс даже после выполнения задачи gulp. ;)

person Davit Yavryan    schedule 14.11.2016

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

gulp.task('watch', function() {
  const restartingGulpProcessCmd = 'while true; do gulp watch2 --colors; done;';
  const restartingGulpProcess = require('child_process').exec(restartingGulpProcessCmd);
  restartingGulpProcess.stdout.pipe(process.stdout);
  restartingGulpProcess.stderr.pipe(process.stderr);
});

gulp.task('watch2', function() {
  gulp.watch(['config/**.js', 'webpack.config.js', './gulpfile.js'],
    () => {
      console.log('Config file changed. Quitting so gulp can be restarted.');
      process.exit();
    });

  // Add your other watch and build commands here
}

gulp.task('default', ['watch']);
person fstr    schedule 26.08.2017
comment
Что бы это было для окон? Пока верно; часть возвращает «пока не распознается как внутренняя или внешняя команда..» - person MattV; 30.11.2019

Установить gulp-restart

npm install gulp-restart

Этот код будет работать для вас.

var gulp = require('gulp'); var restart = require('gulp-restart');

gulp.task('watch', function() { gulp.watch(['gulpfile.js'], restart); })

он перезапустит gulp, где вы вносите изменения в gulpfile.js

person Dilakshan Sooriyanathan    schedule 25.09.2017
comment
gulp 4.0 на MacOs: TypeError: stdin.setRawMode не является функцией в Object.‹anonymous› (/.../node_modules/gulp-restart/index.js:18:7) - person Vitamon; 10.04.2018

Я потратил целый день, пытаясь заставить это работать на Windows/Gulp 4.0.2, и у меня (наконец-то) получилось...

Я использовал некоторые решения от людей на этой странице и на одной другой странице. В комментариях все есть...

Любые изменения в любой функции внутри allTasks повлияют на сохранение gulpfile.js (или других отслеживаемых файлов)...

Остались бесполезные комментарии и логи console.log, можете их удалить... ;)

const { gulp, watch, src, dest, series, parallel } = require("gulp");
const spawn = require('child_process').spawn;

// This function contains all that is necessary: start server, watch files...
const allTasks = function (callback) {
   console.log('==========');
   console.log('========== STARTING THE GULP DEFAULT TASK...');
   console.log('========== USE CTRL+C TO STOP THE TASK');
   console.log('==========');

   startServer();
   // other functions (watchers) here

   // *** Thanks to Sebazzz ***
   // Stop all on gulpfile.js change
   watch('gulpfile.js', function (callback) {
      callback(); // avoid "task didn't complete" error
      process.exit();
   });

   callback(); // avoid "task didn't complete" error
}


// Restart allTasks
// ********************************************
// CALL GULPDEFAULT WITH THE GULP DEFAULT TASK:
// export.default = gulpDefault
// ********************************************
const gulpDefault = function (callback) {
   let p = null;

   watch('gulpfile.js', spawnChildren);

   // *** Thanks to Sphinxxx: ***
   // New behavior in gulp v4: The watcher function (spawnChildren()) is passed a callback argument
   // which must be called after spawnChildren() is done, or else the auto-reload task
   // never goes back to watching for further changes (i.e.the reload only works once).
   spawnChildren(callback);

   function spawnChildren(callback) {

      /*
      // This didn't do anything for me, with or without the delay,
      // so I left it there, but commented it out, together with the console.logs...

      // kill previous spawned process
      if (p) {
         // You might want to trigger a build as well
         //args.unshift('build');

         setTimeout(function () {
            console.log('========== p.pid before kill: ' + p.pid); // a random number
            console.log('========== p before kill: ' + p); // [object Object]
            p.kill();
            console.log('========== p.pid after kill: ' + p.pid); // the same random number
            console.log('========== p after kill: ' + p); // still [object Object]
         }, 1000);
      }
      */

      // `spawn` a child `gulp` process linked to the parent `stdio`
      // ['watch'] is the task that calls the main function (allTasks):
      // exports.watch = allTasks;
      p = spawn('gulp', ['watch'], { stdio: 'inherit', shell: true });
      // *** Thanks to people from: ***
      // https://stackoverflow.com/questions/27688804/how-do-i-debug-error-spawn-enoent-on-node-js
      // Prevent Error: spawn ENOENT
      // by passing "shell: true" to the spawn options

      callback(); // callback called - thanks to Sphinxxx
   }
}

exports.default = gulpDefault;
exports.watch = allTasks;
person V.IGGS    schedule 11.02.2021