UNPKG

9.49 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3var events_1 = require("events");
4var path_1 = require("path");
5var webpackApi = require("webpack");
6var logger_1 = require("./logger/logger");
7var config_1 = require("./util/config");
8var Constants = require("./util/constants");
9var errors_1 = require("./util/errors");
10var events_2 = require("./util/events");
11var helpers_1 = require("./util/helpers");
12var interfaces_1 = require("./util/interfaces");
13var eventEmitter = new events_1.EventEmitter();
14var INCREMENTAL_BUILD_FAILED = 'incremental_build_failed';
15var 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 */
25var pendingPromises = [];
26function 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}
39exports.webpack = webpack;
40function 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}
62exports.webpackUpdate = webpackUpdate;
63function 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}
77exports.webpackWorker = webpackWorker;
78function 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}
105function 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}
111exports.setBundledFiles = setBundledFiles;
112function 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}
136exports.runWebpackFullBuild = runWebpackFullBuild;
137function 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}
157function 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}
167function 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}
178function 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}
190function 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}
198exports.getWebpackConfig = getWebpackConfig;
199function getWebpackConfigFromDictionary(context, webpackConfigDictionary) {
200 // todo, support more ENV here
201 if (context.runAot) {
202 return webpackConfigDictionary['prod'];
203 }
204 return webpackConfigDictionary['dev'];
205}
206exports.getWebpackConfigFromDictionary = getWebpackConfigFromDictionary;
207function getOutputDest(context) {
208 var webpackConfig = getWebpackConfig(context, null);
209 return path_1.join(webpackConfig.output.path, webpackConfig.output.filename);
210}
211exports.getOutputDest = getOutputDest;
212var taskInfo = {
213 fullArg: '--webpack',
214 shortArg: '-w',
215 envVar: 'IONIC_WEBPACK',
216 packageConfig: 'ionic_webpack',
217 defaultConfigFile: 'webpack.config'
218};