UNPKG

ufilesync

Version:
218 lines (184 loc) 7.87 kB
'use strict'; /** * Весь велосипед заключается в том, чтобы переопределить колбек(если его передали) * и после того как выполнится fs.функция(иначе файла может не быть и нечего отправлять будет) * отправить задачу в очередь на синхронизацию * и если отправлка прошла успешно, только после этого вызвать колбек переданный для fs.функции */ const path = require('path'); class Decorator { constructor(functionName, uSync, config, logger) { this.functionName = functionName; this.config = config; this.uSync = uSync; this.logger = logger; } getRelativePath(pathToFile) { if (path.isAbsolute(pathToFile)) { return path.relative(this.config.baseDir, path.normalize(pathToFile)); } else { return pathToFile; } } /** * Получить или сгенерировать нужную "задачу" для отправки в очередь * @param me * @param arg - аргументы функции в которую возможно была передана задача на синхронизацию * @returns {Task || Skip} */ generateTaskFromArguments(arg) { const decorator = this; /** * Для возможности управления отправкой сообщения в очередь, первым параметром может прийти объект Task или SkipTask * Иначе создаем Task сообщение из того, что имеем * @type {Task} */ // Если синхронизация остановлена, то ничего не отправляем в очередь if (decorator.config.isRunSync === false) { // Если была передана задача первым аргументом, то убираем ее if (arg[0] instanceof decorator.uSync.tasks.Task) arg.shift(); return decorator.uSync.taskSkip(); } // Если первый аргумент это объект SkipTask или Task if (arg[0] instanceof decorator.uSync.tasks.Task) { return arg.shift(); } else { // Путь к файлу let pathToFiles = ''; switch (decorator.functionName) { case 'copy': case 'move': case 'copyFile': case 'rename': pathToFiles = { src: decorator.getRelativePath(arg[0]), dest: decorator.getRelativePath(arg[1]) }; break; case 'symlink': // ВНИМАНИЕ - для всех симлинков насильно вычисляем относительные пути if (path.isAbsolute(arg[0])) { arg[0] = path.relative(path.dirname(path.normalize(arg[1])), arg[0]); } if (path.isAbsolute(arg[1])) { arg[1] = decorator.getRelativePath(arg[1]); } pathToFiles = { src: arg[0], dest: arg[1] }; break; default: pathToFiles = decorator.getRelativePath(arg[0]); break; } // let queueName = decorator.uSync.selectQueuePostfix(pathToFiles.dest || pathToFiles); // // if (queueName.length === 0) { // decorator.uSync.debug(`Not give queueName for path: ${pathToFiles.dest || pathToFiles}`); // return decorator.uSync.taskSkip(); // } else { // return decorator.uSync.task(decorator.functionName, pathToFiles, queueName); // } return decorator.uSync.task(decorator.functionName, pathToFiles); } } /** * Готов ли декоратор отправлять в очередь на синхронизацию * @param arg * @param task * @return {boolean} */ isReadyRunFunction(arg, task) { const decorator = this; // Если синхронизация остановлена, то ничего не отправляем в очередь if (decorator.config.isRunSync === false) { return false; } // Если получили задачу для пропуска синхронизации, то сразу вызываем колбек функции if (task instanceof decorator.uSync.tasks.Skip) { decorator.uSync.debug('Skip synchronise', task, { functionName: decorator.functionName }); return false; } return true; } /** * Подмена колбека, который передал программист * @description Это наш колбек - вызоавится после того, как выполнится функция fs-модуля. Далее как отработает наш колбек, будет вызван колбек пользователя. * @param arg * @param fsMe * @param task */ wrapOriginCb(arg, fsMe, task) { const decorator = this; let cbOrigin = arg.pop(); // "Вырезаем" колбек котрый должен быть let stackPlace = { stackTasks: '' }; // На случай отладки получаем стек вызова Error.captureStackTrace(stackPlace, decorator.wrapOriginCb); arg.push(function() { // подменяем на свой let argCb = arguments; // Если при выполнении функции возникла ошибка, то НЕ отправляем на синхронизацию if (argCb[0]) { if (decorator.config.isRunDebugMode) { decorator.logger.debug({ message: 'Error when executing a function', taskId: task.id, taskCommand: task.command, taskPath: task.path, taskTimeElapsed: task.getTimeElapsed(), functionName: decorator.functionName, error: argCb[0], }); } // Добавляем НАШ стек вызова в ошибку argCb[0].stackTasks = stackPlace.stackTasks; // Вызываем cb который передал программист cbOrigin.apply(fsMe, argCb); } else { decorator.uSync.debug('fs end', task, { functionName: decorator.functionName }); // Отправляем в очередь на синхронизацию decorator.uSync.push(task, err => { if (err) { cbOrigin(err); } else { decorator.uSync.debug('Push end', task, { functionName: decorator.functionName }); // Вызываем cb который передал программист cbOrigin.apply(fsMe, argCb); } }); } }); } wrap(originFunction) { const decorator = this; // Возвращаем свою функцию return function() { let fsMe = this; let arg = Array.prototype.slice.call(arguments); let task = decorator.generateTaskFromArguments(arg); // Если при создании Task возникла ошибка, то отдаем ее в колбек if (task instanceof decorator.uSync.exceptions.ErrorUSync) { let cbOrigin = arg.pop(); return cbOrigin(task); } // Если готовы if (decorator.isReadyRunFunction(arg, task)) { // Подменяем коллбек ЕСЛИ он есть if (typeof arg[arg.length - 1] === 'function') { decorator.wrapOriginCb(arg, fsMe, task); } else { // Обработка асинхронных вызовов(по идее таких не должно быть) decorator.uSync.push(task); } } else { return originFunction.apply(fsMe, arg); } // Вызываем originFunction только если синхронизация готова к использованию decorator.uSync.on('ready', () => { task.setTimeStartDebug('fs'); decorator.uSync.debug('Synchronisation is ready', task, { functionName: decorator.functionName }); // Запускаем выполнение fs.функции originFunction.apply(fsMe, arg); }); }; } push(task, cb) { this.uSync.push(task, cb); } } module.exports = Decorator;