copy.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. var fs_extra_1 = require("fs-extra");
  4. var path_1 = require("path");
  5. var logger_1 = require("./logger/logger");
  6. var config_1 = require("./util/config");
  7. var Constants = require("./util/constants");
  8. var events_1 = require("./util/events");
  9. var glob_util_1 = require("./util/glob-util");
  10. var helpers_1 = require("./util/helpers");
  11. var watch_1 = require("./watch");
  12. var copyFilePathCache = new Map();
  13. var FILTER_OUT_DIRS_FOR_CLEAN = ['{{WWW}}', '{{BUILD}}'];
  14. function copy(context, configFile) {
  15. configFile = config_1.getUserConfigFile(context, exports.taskInfo, configFile);
  16. var logger = new logger_1.Logger('copy');
  17. return copyWorker(context, configFile)
  18. .then(function () {
  19. logger.finish();
  20. })
  21. .catch(function (err) {
  22. throw logger.fail(err);
  23. });
  24. }
  25. exports.copy = copy;
  26. function copyWorker(context, configFile) {
  27. var copyConfig = config_1.fillConfigDefaults(configFile, exports.taskInfo.defaultConfigFile);
  28. var keys = Object.keys(copyConfig);
  29. var directoriesToCreate = new Set();
  30. var toCopyList = [];
  31. return Promise.resolve().then(function () {
  32. // for each entry, make sure each glob in the list of globs has had string replacement performed on it
  33. cleanConfigContent(keys, copyConfig, context);
  34. return getFilesPathsForConfig(keys, copyConfig);
  35. }).then(function (resultMap) {
  36. // sweet, we have the absolute path of the files in the glob, and the ability to get the relative path
  37. // basically, we've got a stew goin'
  38. return populateFileAndDirectoryInfo(resultMap, copyConfig, toCopyList, directoriesToCreate);
  39. }).then(function () {
  40. if (helpers_1.getBooleanPropertyValue(Constants.ENV_CLEAN_BEFORE_COPY)) {
  41. cleanDirectories(context, directoriesToCreate);
  42. }
  43. }).then(function () {
  44. // create the directories synchronously to avoid any disk locking issues
  45. var directoryPathList = Array.from(directoriesToCreate);
  46. for (var _i = 0, directoryPathList_1 = directoryPathList; _i < directoryPathList_1.length; _i++) {
  47. var directoryPath = directoryPathList_1[_i];
  48. fs_extra_1.mkdirpSync(directoryPath);
  49. }
  50. }).then(function () {
  51. // sweet, the directories are created, so now let's stream the files
  52. var promises = [];
  53. var _loop_1 = function (file) {
  54. cacheCopyData(file);
  55. var promise = helpers_1.copyFileAsync(file.absoluteSourcePath, file.absoluteDestPath);
  56. promise.then(function () {
  57. logger_1.Logger.debug("Successfully copied " + file.absoluteSourcePath + " to " + file.absoluteDestPath);
  58. }).catch(function (err) {
  59. logger_1.Logger.warn("Failed to copy " + file.absoluteSourcePath + " to " + file.absoluteDestPath);
  60. });
  61. promises.push(promise);
  62. };
  63. for (var _i = 0, toCopyList_1 = toCopyList; _i < toCopyList_1.length; _i++) {
  64. var file = toCopyList_1[_i];
  65. _loop_1(file);
  66. }
  67. return Promise.all(promises);
  68. });
  69. }
  70. exports.copyWorker = copyWorker;
  71. function copyUpdate(changedFiles, context) {
  72. var logger = new logger_1.Logger('copy update');
  73. var configFile = config_1.getUserConfigFile(context, exports.taskInfo, null);
  74. var copyConfig = config_1.fillConfigDefaults(configFile, exports.taskInfo.defaultConfigFile);
  75. var keys = Object.keys(copyConfig);
  76. var directoriesToCreate = new Set();
  77. var toCopyList = [];
  78. return Promise.resolve().then(function () {
  79. changedFiles.forEach(function (changedFile) { return logger_1.Logger.debug("copyUpdate, event: " + changedFile.event + ", path: " + changedFile.filePath); });
  80. // for each entry, make sure each glob in the list of globs has had string replacement performed on it
  81. cleanConfigContent(keys, copyConfig, context);
  82. return getFilesPathsForConfig(keys, copyConfig);
  83. }).then(function (resultMap) {
  84. // sweet, we have the absolute path of the files in the glob, and the ability to get the relative path
  85. // basically, we've got a stew goin'
  86. return populateFileAndDirectoryInfo(resultMap, copyConfig, toCopyList, directoriesToCreate);
  87. }).then(function () {
  88. // first, process any deleted directories
  89. var promises = [];
  90. var directoryDeletions = changedFiles.filter(function (changedFile) { return changedFile.event === 'unlinkDir'; });
  91. directoryDeletions.forEach(function (changedFile) { return promises.push(processRemoveDir(changedFile)); });
  92. return Promise.all(promises);
  93. }).then(function () {
  94. // process any deleted files
  95. var promises = [];
  96. var fileDeletions = changedFiles.filter(function (changedFile) { return changedFile.event === 'unlink'; });
  97. fileDeletions.forEach(function (changedFile) { return promises.push(processRemoveFile(changedFile)); });
  98. return Promise.all(promises);
  99. }).then(function () {
  100. var promises = [];
  101. var additions = changedFiles.filter(function (changedFile) { return changedFile.event === 'change' || changedFile.event === 'add' || changedFile.event === 'addDir'; });
  102. additions.forEach(function (changedFile) {
  103. var matchingItems = toCopyList.filter(function (toCopyEntry) { return toCopyEntry.absoluteSourcePath === changedFile.filePath; });
  104. matchingItems.forEach(function (matchingItem) {
  105. // create the directories first (if needed)
  106. fs_extra_1.mkdirpSync(path_1.dirname(matchingItem.absoluteDestPath));
  107. // cache the data and copy the files
  108. cacheCopyData(matchingItem);
  109. promises.push(helpers_1.copyFileAsync(matchingItem.absoluteSourcePath, matchingItem.absoluteDestPath));
  110. events_1.emit(events_1.EventType.FileChange, additions);
  111. });
  112. });
  113. return Promise.all(promises);
  114. }).then(function () {
  115. logger.finish('green', true);
  116. logger_1.Logger.newLine();
  117. }).catch(function (err) {
  118. throw logger.fail(err);
  119. });
  120. }
  121. exports.copyUpdate = copyUpdate;
  122. function cleanDirectories(context, directoriesToCreate) {
  123. var filterOut = config_1.replacePathVars(context, FILTER_OUT_DIRS_FOR_CLEAN);
  124. var directoryPathList = Array.from(directoriesToCreate);
  125. // filter out any directories that we don't want to allow a clean on
  126. var cleanableDirectories = directoryPathList.filter(function (directoryPath) {
  127. for (var _i = 0, filterOut_1 = filterOut; _i < filterOut_1.length; _i++) {
  128. var uncleanableDir = filterOut_1[_i];
  129. if (uncleanableDir === directoryPath) {
  130. return false;
  131. }
  132. }
  133. return true;
  134. });
  135. return deleteDirectories(cleanableDirectories);
  136. }
  137. function deleteDirectories(directoryPaths) {
  138. var promises = [];
  139. for (var _i = 0, directoryPaths_1 = directoryPaths; _i < directoryPaths_1.length; _i++) {
  140. var directoryPath = directoryPaths_1[_i];
  141. promises.push(helpers_1.rimRafAsync(directoryPath));
  142. }
  143. return Promise.all(promises);
  144. }
  145. function processRemoveFile(changedFile) {
  146. // delete any destination files that match the source file
  147. var list = copyFilePathCache.get(changedFile.filePath) || [];
  148. copyFilePathCache.delete(changedFile.filePath);
  149. var promises = [];
  150. var deletedFilePaths = [];
  151. list.forEach(function (copiedFile) {
  152. var promise = helpers_1.unlinkAsync(copiedFile.absoluteDestPath);
  153. promises.push(promise);
  154. promise.catch(function (err) {
  155. if (err && err.message && err.message.indexOf('ENOENT') >= 0) {
  156. logger_1.Logger.warn("Failed to delete " + copiedFile.absoluteDestPath + " because it doesn't exist");
  157. return;
  158. }
  159. throw err;
  160. });
  161. deletedFilePaths.push(copiedFile.absoluteDestPath);
  162. });
  163. events_1.emit(events_1.EventType.FileDelete, deletedFilePaths);
  164. return Promise.all(promises).catch(function (err) {
  165. });
  166. }
  167. function processRemoveDir(changedFile) {
  168. // remove any files from the cache where the dirname equals the provided path
  169. var keysToRemove = [];
  170. var directoriesToRemove = new Set();
  171. copyFilePathCache.forEach(function (copiedFiles, filePath) {
  172. if (path_1.dirname(filePath) === changedFile.filePath) {
  173. keysToRemove.push(filePath);
  174. copiedFiles.forEach(function (copiedFile) { return directoriesToRemove.add(path_1.dirname(copiedFile.absoluteDestPath)); });
  175. }
  176. });
  177. keysToRemove.forEach(function (keyToRemove) { return copyFilePathCache.delete(keyToRemove); });
  178. // the entries are removed from the cache, now just rim raf the directoryPath
  179. var promises = [];
  180. directoriesToRemove.forEach(function (directoryToRemove) {
  181. promises.push(helpers_1.rimRafAsync(directoryToRemove));
  182. });
  183. events_1.emit(events_1.EventType.DirectoryDelete, Array.from(directoriesToRemove));
  184. return Promise.all(promises);
  185. }
  186. function cacheCopyData(copyObject) {
  187. var list = copyFilePathCache.get(copyObject.absoluteSourcePath);
  188. if (!list) {
  189. list = [];
  190. }
  191. list.push(copyObject);
  192. copyFilePathCache.set(copyObject.absoluteSourcePath, list);
  193. }
  194. function getFilesPathsForConfig(copyConfigKeys, copyConfig) {
  195. // execute the glob functions to determine what files match each glob
  196. var srcToResultsMap = new Map();
  197. var promises = [];
  198. copyConfigKeys.forEach(function (key) {
  199. var copyOptions = copyConfig[key];
  200. if (copyOptions && copyOptions.src) {
  201. var promise = glob_util_1.globAll(copyOptions.src);
  202. promises.push(promise);
  203. promise.then(function (globResultList) {
  204. srcToResultsMap.set(key, globResultList);
  205. });
  206. }
  207. });
  208. return Promise.all(promises).then(function () {
  209. return srcToResultsMap;
  210. });
  211. }
  212. function populateFileAndDirectoryInfo(resultMap, copyConfig, toCopyList, directoriesToCreate) {
  213. resultMap.forEach(function (globResults, dictionaryKey) {
  214. globResults.forEach(function (globResult) {
  215. // get the relative path from the of each file from the glob
  216. var relativePath = path_1.relative(globResult.base, globResult.absolutePath);
  217. // append the relative path to the dest
  218. var destFileName = path_1.resolve(path_1.join(copyConfig[dictionaryKey].dest, relativePath));
  219. // store the file information
  220. toCopyList.push({
  221. absoluteSourcePath: globResult.absolutePath,
  222. absoluteDestPath: destFileName
  223. });
  224. var directoryToCreate = path_1.dirname(destFileName);
  225. directoriesToCreate.add(directoryToCreate);
  226. });
  227. });
  228. }
  229. function cleanConfigContent(dictionaryKeys, copyConfig, context) {
  230. dictionaryKeys.forEach(function (key) {
  231. var copyOption = copyConfig[key];
  232. if (copyOption && copyOption.src && copyOption.src.length) {
  233. var cleanedUpSrc = config_1.replacePathVars(context, copyOption.src);
  234. copyOption.src = cleanedUpSrc;
  235. var cleanedUpDest = config_1.replacePathVars(context, copyOption.dest);
  236. copyOption.dest = cleanedUpDest;
  237. }
  238. });
  239. }
  240. function copyConfigToWatchConfig(context) {
  241. if (!context) {
  242. context = config_1.generateContext(context);
  243. }
  244. var configFile = config_1.getUserConfigFile(context, exports.taskInfo, '');
  245. var copyConfig = config_1.fillConfigDefaults(configFile, exports.taskInfo.defaultConfigFile);
  246. var results = [];
  247. for (var _i = 0, _a = Object.keys(copyConfig); _i < _a.length; _i++) {
  248. var key = _a[_i];
  249. if (copyConfig[key] && copyConfig[key].src) {
  250. var list = glob_util_1.generateGlobTasks(copyConfig[key].src, {});
  251. results = results.concat(list);
  252. }
  253. }
  254. var paths = [];
  255. var ignored = [];
  256. for (var _b = 0, results_1 = results; _b < results_1.length; _b++) {
  257. var result = results_1[_b];
  258. paths.push(result.pattern);
  259. if (result.opts && result.opts.ignore) {
  260. ignored = ignored.concat(result.opts.ignore);
  261. }
  262. }
  263. return {
  264. paths: paths,
  265. options: {
  266. ignored: ignored
  267. },
  268. callback: watch_1.copyUpdate
  269. };
  270. }
  271. exports.copyConfigToWatchConfig = copyConfigToWatchConfig;
  272. exports.taskInfo = {
  273. fullArg: '--copy',
  274. shortArg: '-y',
  275. envVar: 'IONIC_COPY',
  276. packageConfig: 'ionic_copy',
  277. defaultConfigFile: 'copy.config'
  278. };