Глубокая односторонняя синхронизация двух каталогов с использованием grunt-contrib-watch. Код работает, но время перезапуска grunt-contrib-watch слишком медленное

У меня есть два каталога src и compiled. Я хотел бы обеспечить одностороннюю синхронизацию данных от src до compiled с помощью Grunt Watch. В качестве промежуточного шага я хотел бы скомпилировать файлы *.less, а также подмножество файлов *.js, которые написаны с синтаксисом ES6.

Я успешно написал задачи, которые делают то, что мне нужно:

// NOTE: Spawn must be disabled to keep watch running under same context in order to dynamically modify config file.
watch: {
  // Compile LESS files to 'compiled' directory.
  less: {
    options: {
      interrupt: true,
      spawn: false,
      cwd: 'src/less'
    },
    files: ['**/*.less'],
    tasks: ['less']
  },
  // Copy all non-ES6/LESS files to 'compiled' directory. Include main files because they're not ES6. Exclude LESS because they're compiled.
  copyUncompiled: {
    options: {
      event: ['added', 'changed'],
      spawn: false,
      cwd: 'src'
    },
    files: ['**/*', '!**/background/**', '!**/common/**', '!contentScript/youTubePlayer/**/*', '!**/foreground/**', '!**/test/**', '!**/less/**', '**/main.js'],
    tasks: ['copy:compileSingle']
  },
  // Compile and copy ES6 files to 'compiled' directory. Exclude main files because they're not ES6.
  copyCompiled: {
    options: {
      event: ['added', 'changed'],
      spawn: false,
      cwd: 'src/js'
    },
    files: ['background/**/*', 'common/**/*', 'contentScript/youTubePlayer/**/*', 'foreground/**/*', 'test/**/*', '!**/main.js'],
    tasks: ['babel:compileSingle']
  },
  // Whenever a file is deleted from 'src' ensure it is also deleted from 'compiled'
  remove: {
    options: {
      event: ['deleted'],
      spawn: false,
      cwd: 'src'
    },
    files: ['**/*'],
    tasks: ['clean:compiledFile']
  }
}

  grunt.event.on('watch', function(action, filepath, target) {
    // Determine which task config to modify based on the event action.
    var taskTarget = '';
    if (action === 'deleted') {
      taskTarget = 'clean.compiledFile';
    } else if (action === 'changed' || action === 'added') {
      if (target === 'copyCompiled') {
        taskTarget = 'babel.compileSingle';
      } else if (target === 'copyUncompiled') {
        taskTarget = 'copy.compileSingle';
      }
    }

    if (taskTarget === '') {
      console.error('Unable to determine taskTarget for: ', action, filepath, target);
    } else {
      // Drop src off of filepath to properly rely on 'cwd' task configuration.
      grunt.config(taskTarget + '.src', filepath.replace('src\\', ''));
    }
  });

Эти задачи смотрят соответствующие файлы. Обработчик событий динамически изменяет задачи clean copy и babel, так что они работают над добавляемыми/измененными/удаленными файлами.

Тем не менее, я смотрю несколько тысяч файлов, а задача watch занимает нетривиальное количество времени для инициализации. На моем высоком уровне разработки ПК инициализация занимает 6+ секунд. Эта проблема усугубляется тем, что задача watch повторно инициализируется после каждой задачи.

Это означает, что если у меня есть два файла, fileA и fileB, и я изменяю fileA и сохраняю, тогда есть 6+ секундный период, когда часы не могут обнаружить изменения fileB. Это приводит к десинхронизации между двумя моими каталогами.

Я нашел эту проблему GitHub в отношении моей проблемы, но она по-прежнему открыта и остается без ответа: https://github.com/gruntjs/grunt-contrib-watch/issues/443

В обсуждении GitHub подчеркивается, что проблема может возникнуть только в том случае, если spawn: false установлен, но в соответствии с документацией по графическим документам:

Если вам необходимо динамически модифицировать вашу конфигурацию, параметр spawn должен быть отключен, чтобы часы работали в одном контексте.

Как таковой, я считаю, что мне нужно продолжать использовать spawn: false.

Я должен предположить, что это довольно стандартная процедура для задач Grunt. Я пропустил что-то очевидное здесь? Не подходит ли задача "Часы" для этой цели? Другие варианты?

Ответы

Ответ 1

Хорошо, поэтому у меня есть рабочее решение, но оно не довольно.

В итоге я использовал grunt-newer, чтобы помочь в решении. К сожалению, он не очень хорошо работает с копией grunt-contrib-copy, потому что копирование файла не обновляет последнее измененное время, и поэтому grunt-newer будет выполняться в течение 100% времени.

Итак, я форкировал grunt-contrib-copy и добавил параметр, позволяющий обновлять последнее измененное время: https://github.com/MeoMix/grunt-contrib-copy

С этим я теперь могу написать:

// NOTE: Spawn must be disabled to keep watch running under same context in order to dynamically modify config file.
watch: {
  // Compile LESS files to 'compiled' directory.
  less: {
    options: {
      interrupt: true,
      cwd: 'src/less'
    },
    files: ['**/*.less'],
    tasks: ['less']
  },
  // Copy all non-ES6/LESS files to 'compiled' directory. Include main files because they're not ES6. Exclude LESS because they're compiled.
  copyUncompiled: {
    options: {
      event: ['added', 'changed'],
      cwd: 'src'
    },
    files: ['**/*', '!**/background/**', '!**/common/**', '!contentScript/youTubePlayer/**/*', '!**/foreground/**', '!**/test/**', '!**/less/**', '**/main.js'],
    tasks: ['newer:copy:compiled']
  },
  // Compile and copy ES6 files to 'compiled' directory. Exclude main files because they're not ES6.
  copyCompiled: {
    options: {
      event: ['added', 'changed'],
      cwd: 'src/js'
    },
    files: ['background/**/*', 'common/**/*', 'contentScript/youTubePlayer/**/*', 'foreground/**/*', 'test/**/*', '!**/main.js'],
    tasks: ['newer:babel:compiled']
  },
  // Whenever a file is deleted from 'src' ensure it is also deleted from 'compiled'
  remove: {
    options: {
      event: ['deleted'],
      spawn: false,
      cwd: 'src'
    },
    files: ['**/*'],
    tasks: ['clean:compiledFile']
  }
}

grunt.event.on('watch', function(action, filepath) {
  if (action === 'deleted') {
    // Drop src off of filepath to properly rely on 'cwd' task configuration.
    grunt.config('clean.compiledFile.src', filepath.replace('src\\', ''));
  }
});

Теперь копирование файлов ES6, а также файлов, отличных от LESS/non-ES6, произойдет только в том случае, если 'src' новее, чем 'dest.'

К сожалению, grunt-newer действительно не поддерживает синхронизацию операции удаления при удалении из 'src'. Итак, я продолжаю использовать свой предыдущий код для операций "удалить". У этого все еще есть тот же недостаток, где после удаления будет выполняться задача просмотра на мгновение.