UNPKG

sync-glob

Version:

Synchronize files and folders locally by glob patterns, watch option included.

283 lines (220 loc) 8.86 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /* globals process */ var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs); var _path = require('path'); var _path2 = _interopRequireDefault(_path); var _globAll = require('glob-all'); var _globAll2 = _interopRequireDefault(_globAll); var _chokidar = require('chokidar'); var _chokidar2 = _interopRequireDefault(_chokidar); var _bluebird = require('bluebird'); var _bluebird2 = _interopRequireDefault(_bluebird); var _resolveTarget = require('./lib/resolve-target'); var _resolveTarget2 = _interopRequireDefault(_resolveTarget); var _sourcesBases = require('./lib/sources-bases'); var _sourcesBases2 = _interopRequireDefault(_sourcesBases); var _isGlob = require('./lib/is-glob'); var _isGlob2 = _interopRequireDefault(_isGlob); var _trimQuotes = require('./lib/trim-quotes'); var _trimQuotes2 = _interopRequireDefault(_trimQuotes); var _fs3 = require('./lib/fs'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } _bluebird2.default.config({ cancellation: true }); var defaults = { watch: false, delete: true, depth: Infinity }; /** * Synchronise files, directories and/or glob patterns, optionally watching for changes. * * @param {string|Array.<string>} sources - A list of files, directories and/or glob patterns. * @param {string} target - The destination directory. * @param {Object} [options] - An optional configuration object. * @param {bool} [options.watch=false] - Enable or disable watch mode. * @param {bool} [options.delete=true] - Whether to delete the `target`'s content initially. * @param {bool} [options.depth=Infinity] - Chokidars `depth` (If set, limits how many levels of subdirectories will be traversed). * @param {string} [options.transform=false] - A module path resolved by node's `require`. * @param {NotifyCallback} [notify] - An optional notification callback. * @returns {CloseFunc} - Returns a close function which cancels active promises and watch mode. */ // eslint-disable-next-line consistent-return var syncGlob = function syncGlob(sources, target) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var notify = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : function () {}; if (!Array.isArray(sources)) { // eslint-disable-next-line no-param-reassign sources = [sources]; } // eslint-disable-next-line no-param-reassign sources = sources.map(_trimQuotes2.default); if (typeof options === 'function') { // eslint-disable-next-line no-param-reassign notify = options; // eslint-disable-next-line no-param-reassign options = {}; } // eslint-disable-next-line no-param-reassign options = _extends({}, defaults, options); var notifyError = function notifyError(err) { notify('error', err); }; var bases = (0, _sourcesBases2.default)(sources); var resolveTargetFromBases = (0, _resolveTarget2.default)(bases); var _options = options, depth = _options.depth, watch = _options.watch; var _options2 = options, transform = _options2.transform; if (typeof depth !== 'number' || isNaN(depth)) { notifyError('Expected valid number for option "depth"'); return false; } if (transform) { var transformPath = transform; try { require.resolve(transformPath); } catch (err) { transformPath = _path2.default.join(process.cwd(), transformPath); try { require.resolve(transformPath); } catch (err2) { notifyError(err2); } } // eslint-disable-next-line transform = require(transformPath); } // Initial mirror var mirrorInit = [(0, _bluebird.promisify)(_globAll2.default)(sources.map(function (source) { return (0, _isGlob2.default)(source) === -1 && _fs2.default.statSync(source).isDirectory() ? source + '/**' : source; })).then(function (files) { return files.map(function (file) { return _path2.default.normalize(file); }); })]; if (options.delete) { mirrorInit.push((0, _fs3.remove)(target).then(function () { notify('remove', [target]); }).catch(notifyError)); } else { notify('no-delete', target); } var mirrorPromiseAll = _bluebird2.default.all(mirrorInit).then(function (_ref) { var _ref2 = _slicedToArray(_ref, 1), files = _ref2[0]; return _bluebird2.default.all(files.map(function (source) { var resolvedTarget = resolveTargetFromBases(source, target); return (0, _fs3.stat)(source).then(function (stats) { var result = void 0; if (stats.isFile()) { result = (0, _fs3.copyFile)(source, resolvedTarget, transform); } else if (stats.isDirectory()) { result = (0, _fs3.copyDir)(source, resolvedTarget); } if (result) { result = result.then(function () { notify('copy', [source, resolvedTarget]); }); } return result; }).catch(notifyError); })); }).then(function () { notify('mirror', [sources, target]); }).catch(notifyError).finally(function () { mirrorPromiseAll = null; }); var watcher = void 0; var activePromises = []; var close = function close() { if (watcher) { watcher.close(); watcher = null; } if (mirrorPromiseAll) { mirrorPromiseAll.cancel(); mirrorPromiseAll = null; } if (activePromises) { activePromises.forEach(function (promise) { promise.cancel(); }); activePromises = null; } }; // Watcher to keep in sync from that if (watch) { watcher = _chokidar2.default.watch(sources, { persistent: true, depth: depth, ignoreInitial: true, awaitWriteFinish: true }); watcher.on('ready', notify.bind(undefined, 'watch', sources)).on('all', function (event, source) { var resolvedTarget = resolveTargetFromBases(source, target); var promise = void 0; switch (event) { case 'add': case 'change': promise = (0, _fs3.copyFile)(source, resolvedTarget, transform); break; case 'addDir': promise = (0, _fs3.copyDir)(source, resolvedTarget); break; case 'unlink': case 'unlinkDir': promise = (0, _fs3.remove)(resolvedTarget); break; default: return; } activePromises.push(promise.then(function () { var eventMap = { add: 'copy', addDir: 'copy', change: 'copy', unlink: 'remove', unlinkDir: 'remove' }; notify(eventMap[event] || event, [source, resolvedTarget]); }).catch(notifyError).finally(function () { if (activePromises) { var index = activePromises.indexOf(promise); if (index !== -1) { activePromises.slice(index, 1); } } promise = null; })); }).on('error', notifyError); process.on('SIGINT', close); process.on('SIGQUIT', close); process.on('SIGTERM', close); } return close; }; exports.default = syncGlob; /** * This callback notifies you about various steps, like: * - **copy:** File or directory has been copied to `target`. * - **remove:** File or directory has been removed from `target`. * - **no-delete:** No initial deletion of `target`s contents. * - **mirror:** Initial copy of all `sources` to `target` done. * - **watch:** Watch mode has started. * - **error:** Any error which may occurred during program execution. * * @callback NotifyCallback * @param {string} type - The type of notification. * @param {...any} args - Event specific variadic arguments. */ /** * A cleanup function which cancels all active promises and closes watch mode if enabled. * * @typedef {function} CloseFunc */