Глубокая односторонняя синхронизация двух каталогов с использованием 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'. Итак, я продолжаю использовать свой предыдущий код для операций "удалить". У этого все еще есть тот же недостаток, где после удаления будет выполняться задача просмотра на мгновение.