UNPKG

34.1 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3var path = require('path'),
4 fs = require('fs');
5
6var async = require('async'),
7 request = require('request'),
8 mkdirp = require('mkdirp'),
9 cjson = require('cjson'),
10 _ = require('lodash'),
11 fastGlob = require('fast-glob'),
12 isGlob = require('is-glob'),
13 UglifyJS = require('uglify-js'),
14 md5 = require('md5'),
15 isUtf8 = require('is-utf8');
16
17var logger = require('note-down');
18logger.removeOption('showLogLine');
19
20var chalk = logger.chalk;
21
22var argv = require('yargs')
23 .help(false)
24 .version(false)
25 .argv;
26
27var packageJson = require('./package.json');
28
29var nodeVersion = process.versions.node;
30var paramHelp = argv.h || argv.help,
31 paramVersion = argv.v || argv.version,
32 paramVerbose = argv.verbose,
33 paramOutdated = argv.outdated,
34 paramWhenFileExists = argv.whenFileExists;
35
36var cwd = process.cwd();
37
38var utils = {
39
40 getEncoding: function(contents) {
41 return isUtf8(contents) ? 'utf8' : 'binary';
42 },
43 getColoredTypeString: function(encoding) {
44 switch (encoding) {
45 case 'remote': return chalk.cyan('remote');
46 case 'binary': return chalk.yellow('binary');
47 case 'utf8': return ' utf8 ';
48 default: return chalk.red(encoding);
49 }
50 },
51 isRemoteResource: function (resourcePath) {
52 if (
53 resourcePath.indexOf('https://') === 0 ||
54 resourcePath.indexOf('http://') === 0 ||
55 resourcePath.indexOf('ftp://') === 0
56 ) {
57 return true;
58 }
59 return false;
60 },
61 getRelativePath: function (wrt, fullPath) {
62 if (utils.isRemoteResource(fullPath)) {
63 return fullPath;
64 }
65 return path.relative(wrt, fullPath);
66 },
67
68 exitWithError: function (e, errMsg) {
69 if (errMsg) {
70 logger.log(chalk.magenta(errMsg));
71 }
72 if (e) {
73 logger.error(e);
74 }
75 process.exit(1);
76 },
77
78 // Returns true/false/<defaultValue>
79 booleanIntention: function (val, defaultValue) {
80 if (val === undefined) {
81 return defaultValue;
82 } else {
83 return !!val;
84 }
85 },
86
87 ensureDirectoryExistence: function(dirPath) {
88 var dirname = dirPath[dirPath.length-1] === '/' ? path.normalize(dirPath) : path.dirname(dirPath);
89 if (!fs.existsSync(dirPath)) {
90 try {
91 mkdirp.sync(dirname);
92 } catch (e) {
93 logger.error('\n' + chalk.bold.underline('Error:'));
94 logger.error('Unable to create directory ' + dirname);
95
96 logger.error('\n' + chalk.bold.underline('Error details:'));
97 logger.error(e);
98
99 process.exit(1);
100 }
101 }
102 },
103
104 doUglify: function (needsUglify, code, cb) {
105 if (needsUglify) {
106 var result = UglifyJS.minify(
107 code,
108 // Equivalent to: uglifyjs <source> --compress sequences=false --beautify beautify=false,semicolons=false,comments=some --output <destination>
109 {
110 compress: {
111 sequences: false
112 },
113 mangle: false,
114 output: {
115 semicolons: false,
116 comments: 'some'
117 }
118 }
119 );
120 var consoleCommand = 'uglifyjs <source> --compress sequences=false --beautify beautify=false,semicolons=false,comments=some --output <destination>';
121 cb(result.code, consoleCommand);
122 } else {
123 cb(code, null);
124 }
125 },
126
127 readContents: function (sourceFullPath, cb) {
128 if (utils.isRemoteResource(sourceFullPath)) {
129 request(
130 {
131 uri: sourceFullPath,
132 gzip: true,
133 timeout: 30000
134 },
135 function (err, response, body) {
136 if (err) {
137 cb(err);
138 } else {
139 if (response.statusCode === 200) {
140 cb(null, body, 'remote');
141 } else {
142 cb('Unexpected statusCode (' + response.statusCode + ') for response of: ' + sourceFullPath);
143 }
144 }
145 }
146 );
147 } else {
148 try {
149 var rawContents = fs.readFileSync(sourceFullPath);
150 var encoding = utils.getEncoding(rawContents);
151 var contents = encoding === 'binary'
152 ? rawContents
153 : rawContents.toString('utf8');
154 cb(null, contents, encoding);
155 } catch (e) {
156 cb(e);
157 }
158 }
159 }
160};
161
162if (module.parent) {
163 // Just show a warning and do not exit the process since a basic mocha test checks for the sanity of the code of this file
164 logger.warn('\nWarning: Please run this module (' + packageJson.name + ') from its binary file.' + '\n');
165} else {
166 var showHelp = function () {
167 logger.log([
168 '',
169 chalk.bold('Usage:'),
170 ' copy-files-from-to [--config <config-file>] [--mode <mode-name>] [...]',
171 '',
172 chalk.bold('Examples:'),
173 ' copy-files-from-to',
174 ' copy-files-from-to --config copy-files-from-to.json',
175 ' copy-files-from-to --mode production',
176 ' copy-files-from-to -h',
177 ' copy-files-from-to --version',
178 '',
179 chalk.bold('Options:'),
180 ' --config <config-file-path> Path to configuration file',
181 ' When unspecified, it looks for copy-files-from-to.cjson / copy-files-from-to.json',
182 ' --mode <mode-name> Mode to use for copying the files',
183 ' When unspecified, it uses "default" mode',
184 ' --when-file-exists <operation> Override "whenFileExists" setting specified in configuration file',
185 ' <operation> can be "notify-about-available-change" or "overwrite" or "do-nothing"',
186 ' --outdated Notify about outdated parts of the configuration file',
187 ' (takes cue from "latest" property, wherever specified)',
188 ' --verbose Verbose logging',
189 ' -v --version Output the version number',
190 ' -h --help Show help',
191 ''
192 ].join('\n'));
193 };
194
195 if (paramHelp) {
196 showHelp();
197 process.exit(0);
198 }
199
200 if (paramVersion || paramVerbose) {
201 logger.log(packageJson.name + ' version: ' + packageJson.version);
202 logger.log('Node JS version: ' + nodeVersion);
203 if (paramVersion) {
204 process.exit(0);
205 }
206 }
207
208 var configFile = null;
209
210 configFile = argv.config;
211 if (!configFile) {
212 if (fs.existsSync(path.resolve(cwd, 'copy-files-from-to.cjson'))) {
213 configFile = 'copy-files-from-to.cjson';
214 } else if (fs.existsSync(path.resolve(cwd, 'copy-files-from-to.json'))) {
215 configFile = 'copy-files-from-to.json';
216 } else {
217 logger.error(
218 '\n' +
219 chalk.bold('Error:') + ' Please ensure that you have passed correct arguments. Exiting with error (code 1).'
220 );
221 showHelp();
222 process.exit(1);
223 }
224 }
225
226 var configFileSource,
227 configFileSourceDirectory;
228
229 if (configFile.indexOf('/') === 0 || configFile.indexOf('\\') === 0) { // readListFromFile has an absolute path
230 configFileSource = configFile;
231 } else { // readListFromFile has a relative path
232 configFileSource = path.resolve(cwd, configFile);
233 }
234 configFileSourceDirectory = path.dirname(configFileSource);
235
236 var cjsonText;
237 try {
238 logger.info('Reading copy instructions from file ' + utils.getRelativePath(cwd, configFileSource));
239 cjsonText = fs.readFileSync(configFileSource, 'utf8');
240 } catch (e) {
241 utils.exitWithError(e, 'Error in reading file: ' + configFileSource);
242 }
243
244 var copyFiles = [],
245 settings = {};
246 try {
247 var cjsonData = cjson.parse(cjsonText);
248 if (cjsonData instanceof Object) {
249 if (Array.isArray(cjsonData.copyFiles)) {
250 copyFiles = cjsonData.copyFiles;
251 }
252 if (cjsonData.settings instanceof Object) {
253 settings = cjsonData.settings;
254 }
255 }
256 } catch (e) {
257 utils.exitWithError(e, 'Invalid (C)JSON data:\n ' + cjsonText.replace(/\n/g, '\n '));
258 }
259
260 // ".only" is useful for debugging (This feature is not mentioned in documentation)
261 copyFiles = (function (copyFiles) {
262 var copyFilesWithOnly = [];
263
264 copyFiles.forEach(function (copyFile) {
265 if (copyFile.only) {
266 copyFilesWithOnly.push(copyFile);
267 }
268 });
269
270 if (copyFilesWithOnly.length) {
271 return copyFilesWithOnly;
272 } else {
273 return copyFiles;
274 }
275 }(copyFiles));
276
277 if (paramOutdated) {
278 var arrFromAndLatest = [];
279
280 for (var i = 0; i < copyFiles.length; i++) {
281 let copyFile = copyFiles[i];
282 let from = copyFile.from;
283 if (from && typeof from === 'object') {
284 let modes = Object.keys(from);
285 for (let i = 0; i < modes.length; i++) {
286 let mode = modes[i],
287 fromMode = from[mode];
288 if (fromMode.src && fromMode.latest) {
289 var ob = {
290 src: fromMode.src,
291 latest: fromMode.latest
292 };
293 arrFromAndLatest.push(ob);
294 }
295 }
296 }
297 }
298
299 if (paramVerbose) {
300 logger.verbose('Need to check for updates for the following entries:');
301 logger.verbose(JSON.stringify(arrFromAndLatest, null, ' '));
302 }
303
304 var compareSrcAndLatest = function (arrSrcAndLatest) {
305 async.eachLimit(
306 arrSrcAndLatest,
307 8,
308 function (srcAndLatest, callback) {
309 var resourceSrc = srcAndLatest.src,
310 resourceSrcContents = null;
311 var resourceLatest = srcAndLatest.latest,
312 resourceLatestContents = null;
313
314 async.parallel(
315 [
316 function (cb) {
317 utils.readContents(resourceSrc, function (err, contents, encoding) {
318 if (err) {
319 logger.error(' ✗ Could not read: ' + resourceSrc);
320 } else {
321 if (encoding === 'binary')
322 resourceSrcContents = md5(contents);
323 else
324 resourceSrcContents = contents;
325 }
326 cb();
327 });
328 },
329 function (cb) {
330 utils.readContents(resourceLatest, function (err, contents, encoding) {
331 if (err) {
332 logger.error(' ✗ (Could not read) ' + chalk.gray(resourceLatest));
333 } else {
334 if (encoding === 'binary')
335 resourceLatestContents = md5(contents);
336 else
337 resourceLatestContents = contents;
338 }
339 cb();
340 });
341 }
342 ],
343 function () {
344 if (resourceSrcContents !== null && resourceLatestContents !== null) {
345 if (resourceSrcContents === resourceLatestContents) {
346 logger.success(' ✓' + chalk.gray(' (Up to date) ' + resourceSrc));
347 } else {
348 logger.warn(' 🔃 ("src" is outdated w.r.t. "latest") ' + chalk.gray(resourceSrc));
349 }
350 }
351 callback();
352 }
353 );
354 }
355 );
356 };
357
358 if (arrFromAndLatest.length) {
359 compareSrcAndLatest(arrFromAndLatest);
360 } else {
361 logger.warn('There are no "from" entries for which "from.<mode>.src" file needs to be compared with "from.<mode>.latest" file.');
362 }
363 } else {
364 var WHEN_FILE_EXISTS_NOTIFY_ABOUT_AVAILABLE_CHANGE = 'notify-about-available-change',
365 WHEN_FILE_EXISTS_OVERWRITE = 'overwrite',
366 WHEN_FILE_EXISTS_DO_NOTHING = 'do-nothing',
367 ARR_WHEN_FILE_EXISTS = [
368 WHEN_FILE_EXISTS_NOTIFY_ABOUT_AVAILABLE_CHANGE,
369 WHEN_FILE_EXISTS_OVERWRITE,
370 WHEN_FILE_EXISTS_DO_NOTHING
371 ];
372 var whenFileExists = paramWhenFileExists;
373 if (ARR_WHEN_FILE_EXISTS.indexOf(whenFileExists) === -1) {
374 whenFileExists = settings.whenFileExists;
375 if (ARR_WHEN_FILE_EXISTS.indexOf(whenFileExists) === -1) {
376 whenFileExists = WHEN_FILE_EXISTS_DO_NOTHING;
377 }
378 }
379 var overwriteIfFileAlreadyExists = (whenFileExists === WHEN_FILE_EXISTS_OVERWRITE),
380 notifyAboutAvailableChange = (whenFileExists === WHEN_FILE_EXISTS_NOTIFY_ABOUT_AVAILABLE_CHANGE);
381
382 var mode = argv.mode || 'default';
383
384 var warningsEncountered = 0;
385
386 copyFiles = copyFiles.map(function normalizeData(copyFile) {
387 var latest = null;
388 var from = null,
389 skipFrom = null;
390 if (typeof copyFile.from === 'string' || Array.isArray(copyFile.from)) {
391 from = copyFile.from;
392 } else {
393 var fromMode = copyFile.from[mode] || copyFile.from['default'] || {};
394 if (typeof fromMode === 'string') {
395 from = fromMode;
396 } else {
397 from = fromMode.src;
398 skipFrom = !!fromMode.skip;
399 latest = fromMode.latest;
400 }
401 }
402
403 var to = null,
404 skipTo = null,
405 uglify = null;
406 if (typeof copyFile.to === 'string') {
407 to = copyFile.to;
408 } else {
409 var toMode = copyFile.to[mode] || copyFile.to['default'] || {};
410 if (typeof toMode === 'string') {
411 to = toMode;
412 } else {
413 to = toMode.dest;
414 skipTo = !!toMode.skip;
415 }
416
417 if (typeof toMode === 'object' && toMode.uglifyJs !== undefined) {
418 uglify = utils.booleanIntention(toMode.uglifyJs, false);
419 } else {
420 uglify = utils.booleanIntention(settings.uglifyJs, false);
421 }
422 }
423
424 if (isGlob(to)) {
425 warningsEncountered++;
426 logger.log('');
427 logger.warn('The "to" entries should not be a "glob" pattern. ' + chalk.blue('(Reference: https://github.com/isaacs/node-glob#glob-primer)'));
428
429 to = null;
430 }
431
432 if ((typeof from === 'string' || Array.isArray(from)) && typeof to === 'string') {
433 if (!Array.isArray(from) && (from.match(/\.js$/) || to.match(/\.js$/))) {
434 // If "from" or "to" path ends with ".js", that indicates that it is a JS file
435 // So, retain the uglify setting.
436 // It is a "do nothing" block
437 } else {
438 // It does not seem to be a JS file. So, don't uglify it.
439 uglify = false;
440 }
441
442 return {
443 intendedFrom: from,
444 intendedTo: to,
445 latest: latest,
446 from: (function () {
447 if (utils.isRemoteResource(from)) {
448 return from;
449 }
450 if (Array.isArray(from)) {
451 // If array, it's a glob instruction. Any objects are
452 let globPatterns = [];
453 let globSettings = {};
454 from.forEach( globPart => {
455 if (typeof globPart === 'string') {
456 if (globPart.charAt(0) === '!')
457 globPatterns.push('!' + path.join(configFileSourceDirectory, globPart.substring(1)));
458 else
459 globPatterns.push(path.join(configFileSourceDirectory, globPart));
460 } else {
461 Object.assign(globSettings, globPart);
462 }
463 });
464 return {
465 globPatterns: globPatterns,
466 globSettings: globSettings,
467 };
468 }
469 return path.join(configFileSourceDirectory, from);
470 }()),
471 to: path.join(configFileSourceDirectory, to),
472 uglify: uglify
473 };
474 } else {
475 if (
476 (typeof from !== 'string' && !skipFrom) ||
477 (typeof to !== 'string' && !skipTo)
478 ) {
479 warningsEncountered++;
480 if (warningsEncountered === 1) { // Show this only once
481 logger.log('');
482 logger.warn('Some entries will not be considered in the current mode (' + mode + ').');
483 }
484
485 logger.log('');
486
487 var applicableModesForSkip = _.uniq(['"default"', '"' + mode + '"']).join(' / ');
488 if (typeof from !== 'string' && !skipFrom) {
489 var fromValuesToCheck = _.uniq([
490 '"from"',
491 '"from.default"',
492 '"from.default.src"',
493 '"from.' + mode + '"',
494 '"from.' + mode + '.src"'
495 ]).join(' / ');
496 logger.warn(' Please ensure that the value for ' + fromValuesToCheck + ' is a string.');
497 logger.warn(' Otherwise, add ' + chalk.blue('"skip": true') + ' under "from" for mode: ' + applicableModesForSkip);
498 }
499 if (typeof to !== 'string' && !skipTo) {
500 var toValuesToCheck = _.uniq([
501 '"to"',
502 '"to.default"',
503 '"to.default.dest"',
504 '"to.' + mode + '"',
505 '"to.' + mode + '.dest"'
506 ]).join(' / ');
507 logger.warn(' Please ensure that the value for ' + toValuesToCheck + ' is a string.');
508 logger.warn(' Otherwise, add ' + chalk.blue('"skip": true') + ' under "to" for mode: ' + applicableModesForSkip);
509 }
510
511 logger.warn(' ' + JSON.stringify(copyFile, null, ' ').replace(/\n/g, '\n '));
512 }
513 }
514 });
515
516 copyFiles = (function () {
517 var arr = [];
518 copyFiles.forEach(function (copyFile) {
519 if (copyFile && copyFile.from) {
520 const entries = function() {
521 if (typeof copyFile.from === 'string' && isGlob(copyFile.from)) {
522 return fastGlob.sync([copyFile.from], { dot: !settings.ignoreDotFilesAndFolders });
523 } else if (copyFile.from.globPatterns) {
524 return fastGlob.sync(
525 copyFile.from.globPatterns,
526 Object.assign({ dot: !settings.ignoreDotFilesAndFolders }, copyFile.from.globSettings)
527 );
528 } else {
529 return null;
530 }
531 }();
532 if (entries && entries.length) {
533 entries.forEach(function (entry) {
534 var ob = JSON.parse(JSON.stringify(copyFile));
535 ob.from = entry;
536 const entryPoint = entry.substring(configFileSourceDirectory.length+1);
537 const targetTo = entryPoint.substring(entryPoint.indexOf('/'));
538 ob.to = path.join(
539 ob.to,
540 targetTo
541 );
542 arr.push(ob);
543 });
544 } else {
545 arr.push(copyFile);
546 }
547 }
548 });
549 return arr;
550 }());
551
552 var writeContents = function (copyFile, options, cb) {
553 var to = copyFile.to,
554 intendedFrom = copyFile.intendedFrom;
555 var contents = options.contents,
556 uglified = options.uglified,
557 overwriteIfFileAlreadyExists = options.overwriteIfFileAlreadyExists;
558
559 utils.ensureDirectoryExistence(to);
560
561 var fileExists = fs.existsSync(to),
562 fileDoesNotExist = !fileExists;
563
564 var avoidedFileOverwrite;
565 let finalPath = '';
566 if (
567 fileDoesNotExist ||
568 (fileExists && overwriteIfFileAlreadyExists)
569 ) {
570 try {
571 if (to[to.length-1] === '/') {
572 const stats = fs.statSync(to);
573 if (stats.isDirectory()) {
574 if (typeof intendedFrom === 'string' && !isGlob(intendedFrom)) {
575 const fileName = path.basename(intendedFrom);
576 to = path.join(to, fileName);
577 }
578 }
579 }
580
581 fs.writeFileSync(to, contents, copyFile.encoding === 'binary' ? null : 'utf8');
582 finalPath = to;
583 } catch (e) {
584 cb(e);
585 return;
586 }
587 if (settings.addReferenceToSourceOfOrigin) {
588 var sourceDetails = intendedFrom;
589 if (uglified) {
590 sourceDetails += (uglified.uglifyCommand || '');
591 }
592
593 /*
594 TODO: Handle error scenario for this ".writeFileSync()" operation.
595 Not handling it yet because for all practical use-cases, if
596 the code has been able to write the "to" file, then it should
597 be able to write the "<to>.source.txt" file
598 */
599 fs.writeFileSync(to + '.source.txt', sourceDetails, 'utf8');
600 }
601 avoidedFileOverwrite = false;
602 } else {
603 avoidedFileOverwrite = true;
604 }
605 cb(null, avoidedFileOverwrite, finalPath);
606 };
607
608 var checkForAvailableChange = function (copyFile, contentsOfFrom, config, cb) {
609 var notifyAboutAvailableChange = config.notifyAboutAvailableChange;
610
611 if (notifyAboutAvailableChange) {
612 var to = copyFile.to;
613 utils.readContents(to, function (err, contentsOfTo, encoding) {
614 if (err) {
615 cb(chalk.red(' (unable to read "to.<mode>.dest" file at path ' + to + ')'));
616 warningsEncountered++;
617 } else {
618 copyFile.encoding = encoding;
619 var needsUglify = copyFile.uglify;
620
621 utils.doUglify(needsUglify, contentsOfFrom, function (processedCode) {
622 if (copyFile.encoding === 'binary') {
623 // Only run resource-intensive md5 on binary files
624 if (md5(processedCode) === md5(contentsOfTo)) {
625 cb(chalk.gray(' (up to date)'));
626 } else {
627 cb(chalk.yellow(' (md5 update is available)'));
628 }
629 } else {
630 if (processedCode === contentsOfTo) {
631 cb(chalk.gray(' (up to date)'));
632 } else {
633 cb(chalk.yellow(' (update is available)'));
634 }
635 }
636 });
637 }
638 });
639 } else {
640 cb();
641 }
642 };
643
644 var preWriteOperations = function (copyFile, contents, cb) {
645 var needsUglify = copyFile.uglify;
646 utils.doUglify(needsUglify, contents, function (processedCode, consoleCommand) {
647 if (needsUglify) {
648 cb({
649 contentsAfterPreWriteOperations: processedCode,
650 uglified: {
651 uglifyCommand:
652 '\n' +
653 '\n' +
654 '$ ' + consoleCommand +
655 '\n' +
656 '\nWhere:' +
657 '\n uglifyjs = npm install -g uglify-js@' + packageJson.dependencies['uglify-js'] +
658 '\n <source> = File ' + copyFile.intendedFrom +
659 '\n <destination> = File ./' + path.basename(copyFile.intendedTo)
660 }
661 });
662 } else {
663 cb({
664 contentsAfterPreWriteOperations: processedCode
665 });
666 }
667 });
668 };
669
670 var postWriteOperations = function (copyFile, originalContents, contentsAfterPreWriteOperations, config, cb) {
671 checkForAvailableChange(copyFile, originalContents, config, function (status) {
672 cb(status);
673 });
674 };
675
676 var doCopyFile = function (copyFile, cb) {
677 var from = copyFile.from,
678 to = copyFile.to;
679
680 var printFrom = ' ' + chalk.gray(utils.getRelativePath(cwd, from));
681 var printFromToOriginal = ' ' + chalk.gray(utils.getRelativePath(cwd, to));
682
683 var successMessage = ' ' + chalk.green('✓') + ` Copied `,
684 successMessageAvoidedFileOverwrite = ' ' + chalk.green('✓') + chalk.gray(' Already exists'),
685 errorMessageCouldNotReadFromSrc = ' ' + chalk.red('✗') + ' Could not read',
686 errorMessageFailedToCopy = ' ' + chalk.red('✗') + ' Failed to copy';
687
688 var destFileExists = fs.existsSync(to),
689 destFileDoesNotExist = !destFileExists;
690 if (
691 destFileDoesNotExist ||
692 (
693 destFileExists &&
694 (
695 overwriteIfFileAlreadyExists ||
696 notifyAboutAvailableChange
697 )
698 )
699 ) {
700 utils.readContents(copyFile.from, function (err, contentsOfFrom, encoding) {
701 if (err) {
702 warningsEncountered++;
703 if (destFileExists && notifyAboutAvailableChange) {
704 logger.log(errorMessageCouldNotReadFromSrc + printFrom);
705 } else {
706 logger.log(errorMessageFailedToCopy + printFromToOriginal);
707 }
708 cb();
709 return;
710 }
711 copyFile.encoding = encoding;
712 var typeString = `[${utils.getColoredTypeString(encoding)}]`;
713
714 preWriteOperations(copyFile, contentsOfFrom, function (options) {
715 var contentsAfterPreWriteOperations = options.contentsAfterPreWriteOperations,
716 uglified = options.uglified;
717 writeContents(
718 copyFile,
719 {
720 contents: contentsAfterPreWriteOperations,
721 uglified: uglified,
722 overwriteIfFileAlreadyExists: overwriteIfFileAlreadyExists
723 },
724 function (err, avoidedFileOverwrite, finalPath) {
725 if (err) {
726 warningsEncountered++;
727 logger.log(errorMessageFailedToCopy + printFromTo);
728 cb();
729 return;
730 } else {
731 var printTo = ' ' + chalk.gray(utils.getRelativePath(cwd, finalPath));
732 var printFromTo = printFrom + ' to' + printTo;
733
734 postWriteOperations(
735 copyFile,
736 contentsOfFrom,
737 contentsAfterPreWriteOperations,
738 {
739 notifyAboutAvailableChange: notifyAboutAvailableChange
740 },
741 function (appendToSuccessMessage) {
742 if (avoidedFileOverwrite) {
743 logger.log(successMessageAvoidedFileOverwrite + (appendToSuccessMessage || '') + printTo);
744 } else {
745 // Copying value of "destFileDoesNotExist" to "destFileDidNotExist" since that has a better
746 // sematic name for the given context
747 let destFileDidNotExist = destFileDoesNotExist;
748 if (destFileDidNotExist) {
749 logger.log(successMessage + typeString + printFromTo);
750 } else {
751 logger.log(successMessage + typeString + (appendToSuccessMessage || '') + printFromTo);
752 }
753 }
754 cb();
755 }
756 );
757 }
758 }
759 );
760 });
761 });
762 } else {
763 logger.log(successMessageAvoidedFileOverwrite + printFromToOriginal);
764 cb();
765 }
766 };
767
768 var done = function (warningsEncountered) {
769 if (warningsEncountered) {
770 if (warningsEncountered === 1) {
771 logger.warn('\nEncountered ' + warningsEncountered + ' warning. Please check.');
772 } else {
773 logger.warn('\nEncountered ' + warningsEncountered + ' warnings. Please check.');
774 }
775 logger.error('Error: Please resolve the above mentioned warnings. Exiting with code 1.');
776 process.exit(1);
777 }
778 };
779
780 if (copyFiles.length) {
781 logger.log(
782 chalk.blue('\nStarting copy operation in "' + (mode || 'default') + '" mode:') +
783 (overwriteIfFileAlreadyExists ? chalk.yellow(' (overwrite option is on)') : '')
784 );
785
786 async.eachLimit(
787 copyFiles,
788 8,
789 function (copyFile, callback) {
790 // "copyFile" would be "undefined" when copy operation is not applicable
791 // in current "mode" for the given file
792 if (copyFile && typeof copyFile === 'object') {
793 doCopyFile(copyFile, function () {
794 callback();
795 });
796 } else {
797 callback();
798 }
799 },
800 function () {
801 done(warningsEncountered);
802 }
803 );
804 } else {
805 logger.warn('No instructions applicable for copy operation.');
806 }
807 }
808}