123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. var events_1 = require("events");
  4. var path_1 = require("path");
  5. var webpackApi = require("webpack");
  6. var logger_1 = require("./logger/logger");
  7. var config_1 = require("./util/config");
  8. var Constants = require("./util/constants");
  9. var errors_1 = require("./util/errors");
  10. var events_2 = require("./util/events");
  11. var helpers_1 = require("./util/helpers");
  12. var interfaces_1 = require("./util/interfaces");
  13. var eventEmitter = new events_1.EventEmitter();
  14. var INCREMENTAL_BUILD_FAILED = 'incremental_build_failed';
  15. var INCREMENTAL_BUILD_SUCCESS = 'incremental_build_success';
  16. /*
  17. * Due to how webpack watch works, sometimes we start an update event
  18. * but it doesn't affect the bundle at all, for example adding a new typescript file
  19. * not imported anywhere or adding an html file not used anywhere.
  20. * In this case, we'll be left hanging and have screwed up logging when the bundle is modified
  21. * because multiple promises will resolve at the same time (we queue up promises waiting for an event to occur)
  22. * To mitigate this, store pending "webpack watch"/bundle update promises in this array and only resolve the
  23. * the most recent one. reject all others at that time with an IgnorableError.
  24. */
  25. var pendingPromises = [];
  26. function webpack(context, configFile) {
  27. configFile = config_1.getUserConfigFile(context, taskInfo, configFile);
  28. var logger = new logger_1.Logger('webpack');
  29. return webpackWorker(context, configFile)
  30. .then(function () {
  31. context.bundleState = interfaces_1.BuildState.SuccessfulBuild;
  32. logger.finish();
  33. })
  34. .catch(function (err) {
  35. context.bundleState = interfaces_1.BuildState.RequiresBuild;
  36. throw logger.fail(err);
  37. });
  38. }
  39. exports.webpack = webpack;
  40. function webpackUpdate(changedFiles, context, configFile) {
  41. var logger = new logger_1.Logger('webpack update');
  42. var webpackConfig = getWebpackConfig(context, configFile);
  43. logger_1.Logger.debug('webpackUpdate: Starting Incremental Build');
  44. var promisetoReturn = runWebpackIncrementalBuild(false, context, webpackConfig);
  45. events_2.emit(events_2.EventType.WebpackFilesChanged, null);
  46. return promisetoReturn.then(function (stats) {
  47. // the webpack incremental build finished, so reset the list of pending promises
  48. pendingPromises = [];
  49. logger_1.Logger.debug('webpackUpdate: Incremental Build Done, processing Data');
  50. return webpackBuildComplete(stats, context, webpackConfig);
  51. }).then(function () {
  52. context.bundleState = interfaces_1.BuildState.SuccessfulBuild;
  53. return logger.finish();
  54. }).catch(function (err) {
  55. context.bundleState = interfaces_1.BuildState.RequiresBuild;
  56. if (err instanceof errors_1.IgnorableError) {
  57. throw err;
  58. }
  59. throw logger.fail(err);
  60. });
  61. }
  62. exports.webpackUpdate = webpackUpdate;
  63. function webpackWorker(context, configFile) {
  64. var webpackConfig = getWebpackConfig(context, configFile);
  65. var promise = null;
  66. if (context.isWatch) {
  67. promise = runWebpackIncrementalBuild(!context.webpackWatch, context, webpackConfig);
  68. }
  69. else {
  70. promise = runWebpackFullBuild(webpackConfig);
  71. }
  72. return promise
  73. .then(function (stats) {
  74. return webpackBuildComplete(stats, context, webpackConfig);
  75. });
  76. }
  77. exports.webpackWorker = webpackWorker;
  78. function webpackBuildComplete(stats, context, webpackConfig) {
  79. if (helpers_1.getBooleanPropertyValue(Constants.ENV_PRINT_WEBPACK_DEPENDENCY_TREE)) {
  80. logger_1.Logger.debug('Webpack Dependency Map Start');
  81. var dependencyMap = helpers_1.webpackStatsToDependencyMap(context, stats);
  82. helpers_1.printDependencyMap(dependencyMap);
  83. logger_1.Logger.debug('Webpack Dependency Map End');
  84. }
  85. // set the module files used in this bundle
  86. // this reference can be used elsewhere in the build (sass)
  87. var files = [];
  88. stats.compilation.modules.forEach(function (webpackModule) {
  89. if (webpackModule.resource) {
  90. files.push(webpackModule.resource);
  91. }
  92. else if (webpackModule.context) {
  93. files.push(webpackModule.context);
  94. }
  95. else if (webpackModule.fileDependencies) {
  96. webpackModule.fileDependencies.forEach(function (filePath) {
  97. files.push(filePath);
  98. });
  99. }
  100. });
  101. var trimmedFiles = files.filter(function (file) { return file && file.length > 0; });
  102. context.moduleFiles = trimmedFiles;
  103. return setBundledFiles(context);
  104. }
  105. function setBundledFiles(context) {
  106. var bundledFilesToWrite = context.fileCache.getAll().filter(function (file) {
  107. return path_1.dirname(file.path).indexOf(context.buildDir) >= 0 && (file.path.endsWith('.js') || file.path.endsWith('.js.map'));
  108. });
  109. context.bundledFilePaths = bundledFilesToWrite.map(function (bundledFile) { return bundledFile.path; });
  110. }
  111. exports.setBundledFiles = setBundledFiles;
  112. function runWebpackFullBuild(config) {
  113. return new Promise(function (resolve, reject) {
  114. var callback = function (err, stats) {
  115. if (err) {
  116. reject(new errors_1.BuildError(err));
  117. }
  118. else {
  119. var info = stats.toJson();
  120. if (stats.hasErrors()) {
  121. reject(new errors_1.BuildError(info.errors));
  122. }
  123. else if (stats.hasWarnings()) {
  124. logger_1.Logger.debug(info.warnings);
  125. resolve(stats);
  126. }
  127. else {
  128. resolve(stats);
  129. }
  130. }
  131. };
  132. var compiler = webpackApi(config);
  133. compiler.run(callback);
  134. });
  135. }
  136. exports.runWebpackFullBuild = runWebpackFullBuild;
  137. function runWebpackIncrementalBuild(initializeWatch, context, config) {
  138. var promise = new Promise(function (resolve, reject) {
  139. // start listening for events, remove listeners once an event is received
  140. eventEmitter.on(INCREMENTAL_BUILD_FAILED, function (err) {
  141. logger_1.Logger.debug('Webpack Bundle Update Failed');
  142. eventEmitter.removeAllListeners();
  143. handleWebpackBuildFailure(resolve, reject, err, promise, pendingPromises);
  144. });
  145. eventEmitter.on(INCREMENTAL_BUILD_SUCCESS, function (stats) {
  146. logger_1.Logger.debug('Webpack Bundle Updated');
  147. eventEmitter.removeAllListeners();
  148. handleWebpackBuildSuccess(resolve, reject, stats, promise, pendingPromises);
  149. });
  150. if (initializeWatch) {
  151. startWebpackWatch(context, config);
  152. }
  153. });
  154. pendingPromises.push(promise);
  155. return promise;
  156. }
  157. function handleWebpackBuildFailure(resolve, reject, error, promise, pendingPromises) {
  158. // check if the promise if the last promise in the list of pending promises
  159. if (pendingPromises.length > 0 && pendingPromises[pendingPromises.length - 1] === promise) {
  160. // reject this one with a build error
  161. reject(new errors_1.BuildError(error));
  162. return;
  163. }
  164. // for all others, reject with an ignorable error
  165. reject(new errors_1.IgnorableError());
  166. }
  167. function handleWebpackBuildSuccess(resolve, reject, stats, promise, pendingPromises) {
  168. // check if the promise if the last promise in the list of pending promises
  169. if (pendingPromises.length > 0 && pendingPromises[pendingPromises.length - 1] === promise) {
  170. logger_1.Logger.debug('handleWebpackBuildSuccess: Resolving with Webpack data');
  171. resolve(stats);
  172. return;
  173. }
  174. // for all others, reject with an ignorable error
  175. logger_1.Logger.debug('handleWebpackBuildSuccess: Rejecting with ignorable error');
  176. reject(new errors_1.IgnorableError());
  177. }
  178. function startWebpackWatch(context, config) {
  179. logger_1.Logger.debug('Starting Webpack watch');
  180. var compiler = webpackApi(config);
  181. context.webpackWatch = compiler.watch({}, function (err, stats) {
  182. if (err) {
  183. eventEmitter.emit(INCREMENTAL_BUILD_FAILED, err);
  184. }
  185. else {
  186. eventEmitter.emit(INCREMENTAL_BUILD_SUCCESS, stats);
  187. }
  188. });
  189. }
  190. function getWebpackConfig(context, configFile) {
  191. configFile = config_1.getUserConfigFile(context, taskInfo, configFile);
  192. var webpackConfigDictionary = config_1.fillConfigDefaults(configFile, taskInfo.defaultConfigFile);
  193. var webpackConfig = getWebpackConfigFromDictionary(context, webpackConfigDictionary);
  194. webpackConfig.entry = config_1.replacePathVars(context, webpackConfig.entry);
  195. webpackConfig.output.path = config_1.replacePathVars(context, webpackConfig.output.path);
  196. return webpackConfig;
  197. }
  198. exports.getWebpackConfig = getWebpackConfig;
  199. function getWebpackConfigFromDictionary(context, webpackConfigDictionary) {
  200. // todo, support more ENV here
  201. if (context.runAot) {
  202. return webpackConfigDictionary['prod'];
  203. }
  204. return webpackConfigDictionary['dev'];
  205. }
  206. exports.getWebpackConfigFromDictionary = getWebpackConfigFromDictionary;
  207. function getOutputDest(context) {
  208. var webpackConfig = getWebpackConfig(context, null);
  209. return path_1.join(webpackConfig.output.path, webpackConfig.output.filename);
  210. }
  211. exports.getOutputDest = getOutputDest;
  212. var taskInfo = {
  213. fullArg: '--webpack',
  214. shortArg: '-w',
  215. envVar: 'IONIC_WEBPACK',
  216. packageConfig: 'ionic_webpack',
  217. defaultConfigFile: 'webpack.config'
  218. };