1 | 'use strict';
|
2 |
|
3 |
|
4 | const promisify = require('util.promisify');
|
5 |
|
6 | const vm = require('vm');
|
7 | const fs = require('fs');
|
8 | const _ = require('lodash');
|
9 | const path = require('path');
|
10 | const childCompiler = require('./lib/compiler.js');
|
11 | const prettyError = require('./lib/errors.js');
|
12 | const chunkSorter = require('./lib/chunksorter.js');
|
13 |
|
14 | const fsStatAsync = promisify(fs.stat);
|
15 | const fsReadFileAsync = promisify(fs.readFile);
|
16 |
|
17 | class HtmlWebpackPlugin {
|
18 | constructor (options) {
|
19 |
|
20 | this.options = _.extend({
|
21 | template: path.join(__dirname, 'default_index.ejs'),
|
22 | filename: 'index.html',
|
23 | hash: false,
|
24 | inject: true,
|
25 | compile: true,
|
26 | favicon: false,
|
27 | minify: false,
|
28 | cache: true,
|
29 | showErrors: true,
|
30 | chunks: 'all',
|
31 | excludeChunks: [],
|
32 | title: 'Webpack App',
|
33 | xhtml: false
|
34 | }, options);
|
35 | }
|
36 |
|
37 | apply (compiler) {
|
38 | const self = this;
|
39 | let isCompilationCached = false;
|
40 | let compilationPromise;
|
41 |
|
42 | this.options.template = this.getFullTemplatePath(this.options.template, compiler.context);
|
43 |
|
44 |
|
45 |
|
46 | const filename = this.options.filename;
|
47 | if (path.resolve(filename) === path.normalize(filename)) {
|
48 | this.options.filename = path.relative(compiler.options.output.path, filename);
|
49 | }
|
50 |
|
51 |
|
52 | if (compiler.hooks) {
|
53 | compiler.hooks.compilation.tap('HtmlWebpackPluginHooks', compilation => {
|
54 | const SyncWaterfallHook = require('tapable').SyncWaterfallHook;
|
55 | const AsyncSeriesWaterfallHook = require('tapable').AsyncSeriesWaterfallHook;
|
56 | compilation.hooks.htmlWebpackPluginAlterChunks = new SyncWaterfallHook(['chunks', 'objectWithPluginRef']);
|
57 | compilation.hooks.htmlWebpackPluginBeforeHtmlGeneration = new AsyncSeriesWaterfallHook(['pluginArgs']);
|
58 | compilation.hooks.htmlWebpackPluginBeforeHtmlProcessing = new AsyncSeriesWaterfallHook(['pluginArgs']);
|
59 | compilation.hooks.htmlWebpackPluginAlterAssetTags = new AsyncSeriesWaterfallHook(['pluginArgs']);
|
60 | compilation.hooks.htmlWebpackPluginAfterHtmlProcessing = new AsyncSeriesWaterfallHook(['pluginArgs']);
|
61 | compilation.hooks.htmlWebpackPluginAfterEmit = new AsyncSeriesWaterfallHook(['pluginArgs']);
|
62 | });
|
63 | }
|
64 |
|
65 |
|
66 | (compiler.hooks ? compiler.hooks.make.tapAsync.bind(compiler.hooks.make, 'HtmlWebpackPlugin') : compiler.plugin.bind(compiler, 'make'))((compilation, callback) => {
|
67 |
|
68 | compilationPromise = childCompiler.compileTemplate(self.options.template, compiler.context, self.options.filename, compilation)
|
69 | .catch(err => {
|
70 | compilation.errors.push(prettyError(err, compiler.context).toString());
|
71 | return {
|
72 | content: self.options.showErrors ? prettyError(err, compiler.context).toJsonHtml() : 'ERROR',
|
73 | outputName: self.options.filename
|
74 | };
|
75 | })
|
76 | .then(compilationResult => {
|
77 |
|
78 | isCompilationCached = compilationResult.hash && self.childCompilerHash === compilationResult.hash;
|
79 | self.childCompilerHash = compilationResult.hash;
|
80 | self.childCompilationOutputName = compilationResult.outputName;
|
81 | callback();
|
82 | return compilationResult.content;
|
83 | });
|
84 | });
|
85 |
|
86 |
|
87 | (compiler.hooks ? compiler.hooks.emit.tapAsync.bind(compiler.hooks.emit, 'HtmlWebpackPlugin') : compiler.plugin.bind(compiler, 'emit'))((compilation, callback) => {
|
88 | const applyPluginsAsyncWaterfall = self.applyPluginsAsyncWaterfall(compilation);
|
89 |
|
90 |
|
91 | const chunkOnlyConfig = {
|
92 | assets: false,
|
93 | cached: false,
|
94 | children: false,
|
95 | chunks: true,
|
96 | chunkModules: false,
|
97 | chunkOrigins: false,
|
98 | errorDetails: false,
|
99 | hash: false,
|
100 | modules: false,
|
101 | reasons: false,
|
102 | source: false,
|
103 | timings: false,
|
104 | version: false
|
105 | };
|
106 | const allChunks = compilation.getStats().toJson(chunkOnlyConfig).chunks;
|
107 |
|
108 | let chunks = self.filterChunks(allChunks, self.options.chunks, self.options.excludeChunks);
|
109 |
|
110 | chunks = self.sortChunks(chunks, self.options.chunksSortMode, compilation.chunkGroups);
|
111 |
|
112 | if (compilation.hooks) {
|
113 | chunks = compilation.hooks.htmlWebpackPluginAlterChunks.call(chunks, { plugin: self });
|
114 | } else {
|
115 |
|
116 | chunks = compilation.applyPluginsWaterfall('html-webpack-plugin-alter-chunks', chunks, { plugin: self });
|
117 | }
|
118 |
|
119 | const assets = self.htmlWebpackPluginAssets(compilation, chunks);
|
120 |
|
121 |
|
122 |
|
123 | if (self.isHotUpdateCompilation(assets)) {
|
124 | return callback();
|
125 | }
|
126 |
|
127 |
|
128 | const assetJson = JSON.stringify(self.getAssetFiles(assets));
|
129 | if (isCompilationCached && self.options.cache && assetJson === self.assetJson) {
|
130 | return callback();
|
131 | } else {
|
132 | self.assetJson = assetJson;
|
133 | }
|
134 |
|
135 | Promise.resolve()
|
136 |
|
137 | .then(() => {
|
138 | if (self.options.favicon) {
|
139 | return self.addFileToAssets(self.options.favicon, compilation)
|
140 | .then(faviconBasename => {
|
141 | let publicPath = compilation.mainTemplate.getPublicPath({hash: compilation.hash}) || '';
|
142 | if (publicPath && publicPath.substr(-1) !== '/') {
|
143 | publicPath += '/';
|
144 | }
|
145 | assets.favicon = publicPath + faviconBasename;
|
146 | });
|
147 | }
|
148 | })
|
149 |
|
150 | .then(() => compilationPromise)
|
151 | .then(compiledTemplate => {
|
152 |
|
153 | if (self.options.templateContent !== undefined) {
|
154 | return self.options.templateContent;
|
155 | }
|
156 |
|
157 |
|
158 | return self.evaluateCompilationResult(compilation, compiledTemplate);
|
159 | })
|
160 |
|
161 |
|
162 | .then(compilationResult => applyPluginsAsyncWaterfall('html-webpack-plugin-before-html-generation', false, {
|
163 | assets: assets,
|
164 | outputName: self.childCompilationOutputName,
|
165 | plugin: self
|
166 | })
|
167 | .then(() => compilationResult))
|
168 |
|
169 | .then(compilationResult => typeof compilationResult !== 'function'
|
170 | ? compilationResult
|
171 | : self.executeTemplate(compilationResult, chunks, assets, compilation))
|
172 |
|
173 | .then(html => {
|
174 | const pluginArgs = {html: html, assets: assets, plugin: self, outputName: self.childCompilationOutputName};
|
175 | return applyPluginsAsyncWaterfall('html-webpack-plugin-before-html-processing', true, pluginArgs);
|
176 | })
|
177 | .then(result => {
|
178 | const html = result.html;
|
179 | const assets = result.assets;
|
180 |
|
181 | const assetTags = self.generateAssetTags(assets);
|
182 | const pluginArgs = {head: assetTags.head, body: assetTags.body, plugin: self, chunks: chunks, outputName: self.childCompilationOutputName};
|
183 |
|
184 | return applyPluginsAsyncWaterfall('html-webpack-plugin-alter-asset-tags', true, pluginArgs)
|
185 | .then(result => self.postProcessHtml(html, assets, { body: result.body, head: result.head })
|
186 | .then(html => _.extend(result, {html: html, assets: assets})));
|
187 | })
|
188 |
|
189 | .then(result => {
|
190 | const html = result.html;
|
191 | const assets = result.assets;
|
192 | const pluginArgs = {html: html, assets: assets, plugin: self, outputName: self.childCompilationOutputName};
|
193 | return applyPluginsAsyncWaterfall('html-webpack-plugin-after-html-processing', true, pluginArgs)
|
194 | .then(result => result.html);
|
195 | })
|
196 | .catch(err => {
|
197 |
|
198 |
|
199 | compilation.errors.push(prettyError(err, compiler.context).toString());
|
200 |
|
201 | self.hash = null;
|
202 | return self.options.showErrors ? prettyError(err, compiler.context).toHtml() : 'ERROR';
|
203 | })
|
204 | .then(html => {
|
205 |
|
206 | compilation.assets[self.childCompilationOutputName] = {
|
207 | source: () => html,
|
208 | size: () => html.length
|
209 | };
|
210 | })
|
211 | .then(() => applyPluginsAsyncWaterfall('html-webpack-plugin-after-emit', false, {
|
212 | html: compilation.assets[self.childCompilationOutputName],
|
213 | outputName: self.childCompilationOutputName,
|
214 | plugin: self
|
215 | }).catch(err => {
|
216 | console.error(err);
|
217 | return null;
|
218 | }).then(() => null))
|
219 |
|
220 | .then(() => {
|
221 | callback();
|
222 | });
|
223 | });
|
224 | }
|
225 |
|
226 | |
227 |
|
228 |
|
229 |
|
230 | evaluateCompilationResult (compilation, source) {
|
231 | if (!source) {
|
232 | return Promise.reject('The child compilation didn\'t provide a result');
|
233 | }
|
234 |
|
235 |
|
236 |
|
237 | source = source.replace('var HTML_WEBPACK_PLUGIN_RESULT =', '');
|
238 | const template = this.options.template.replace(/^.+!/, '').replace(/\?.+$/, '');
|
239 | const vmContext = vm.createContext(_.extend({HTML_WEBPACK_PLUGIN: true, require: require}, global));
|
240 | const vmScript = new vm.Script(source, {filename: template});
|
241 |
|
242 | let newSource;
|
243 | try {
|
244 | newSource = vmScript.runInContext(vmContext);
|
245 | } catch (e) {
|
246 | return Promise.reject(e);
|
247 | }
|
248 | if (typeof newSource === 'object' && newSource.__esModule && newSource.default) {
|
249 | newSource = newSource.default;
|
250 | }
|
251 | return typeof newSource === 'string' || typeof newSource === 'function'
|
252 | ? Promise.resolve(newSource)
|
253 | : Promise.reject('The loader "' + this.options.template + '" didn\'t return html.');
|
254 | }
|
255 |
|
256 | |
257 |
|
258 |
|
259 |
|
260 |
|
261 | executeTemplate (templateFunction, chunks, assets, compilation) {
|
262 | const self = this;
|
263 | return Promise.resolve()
|
264 |
|
265 | .then(() => {
|
266 | const templateParams = {
|
267 | compilation: compilation,
|
268 | webpack: compilation.getStats().toJson(),
|
269 | webpackConfig: compilation.options,
|
270 | htmlWebpackPlugin: {
|
271 | files: assets,
|
272 | options: self.options
|
273 | }
|
274 | };
|
275 | let html = '';
|
276 | try {
|
277 | html = templateFunction(templateParams);
|
278 | } catch (e) {
|
279 | compilation.errors.push(new Error('Template execution failed: ' + e));
|
280 | return Promise.reject(e);
|
281 | }
|
282 | return html;
|
283 | });
|
284 | }
|
285 |
|
286 | |
287 |
|
288 |
|
289 |
|
290 |
|
291 | postProcessHtml (html, assets, assetTags) {
|
292 | const self = this;
|
293 | if (typeof html !== 'string') {
|
294 | return Promise.reject('Expected html to be a string but got ' + JSON.stringify(html));
|
295 | }
|
296 | return Promise.resolve()
|
297 |
|
298 | .then(() => {
|
299 | if (self.options.inject) {
|
300 | return self.injectAssetsIntoHtml(html, assets, assetTags);
|
301 | } else {
|
302 | return html;
|
303 | }
|
304 | })
|
305 |
|
306 | .then(html => {
|
307 | if (self.options.minify) {
|
308 | const minify = require('html-minifier').minify;
|
309 | return minify(html, self.options.minify);
|
310 | }
|
311 | return html;
|
312 | });
|
313 | }
|
314 |
|
315 | |
316 |
|
317 |
|
318 | addFileToAssets (filename, compilation) {
|
319 | filename = path.resolve(compilation.compiler.context, filename);
|
320 | return Promise.all([
|
321 | fsStatAsync(filename),
|
322 | fsReadFileAsync(filename)
|
323 | ])
|
324 | .then(([size, source]) => {
|
325 | return {
|
326 | size,
|
327 | source
|
328 | };
|
329 | })
|
330 | .catch(() => Promise.reject(new Error('HtmlWebpackPlugin: could not load file ' + filename)))
|
331 | .then(results => {
|
332 | const basename = path.basename(filename);
|
333 | if (compilation.fileDependencies.add) {
|
334 | compilation.fileDependencies.add(filename);
|
335 | } else {
|
336 |
|
337 | compilation.fileDependencies.push(filename);
|
338 | }
|
339 | compilation.assets[basename] = {
|
340 | source: () => results.source,
|
341 | size: () => results.size.size
|
342 | };
|
343 | return basename;
|
344 | });
|
345 | }
|
346 |
|
347 | |
348 |
|
349 |
|
350 | sortChunks (chunks, sortMode, chunkGroups) {
|
351 |
|
352 | if (typeof sortMode === 'undefined') {
|
353 | sortMode = 'auto';
|
354 | }
|
355 |
|
356 | if (typeof sortMode === 'function') {
|
357 | return chunks.sort(sortMode);
|
358 | }
|
359 |
|
360 | if (sortMode === 'none') {
|
361 | return chunkSorter.none(chunks);
|
362 | }
|
363 | if (sortMode === 'manual') {
|
364 | return chunkSorter.manual(chunks, this.options.chunks);
|
365 | }
|
366 |
|
367 | if (typeof chunkSorter[sortMode] !== 'undefined') {
|
368 | return chunkSorter[sortMode](chunks, chunkGroups);
|
369 | }
|
370 | throw new Error('"' + sortMode + '" is not a valid chunk sort mode');
|
371 | }
|
372 |
|
373 | |
374 |
|
375 |
|
376 | filterChunks (chunks, includedChunks, excludedChunks) {
|
377 | return chunks.filter(chunk => {
|
378 | const chunkName = chunk.names[0];
|
379 |
|
380 | if (chunkName === undefined) {
|
381 | return false;
|
382 | }
|
383 |
|
384 | if (typeof chunk.isInitial === 'function') {
|
385 | if (!chunk.isInitial()) {
|
386 | return false;
|
387 | }
|
388 | } else if (!chunk.initial) {
|
389 | return false;
|
390 | }
|
391 |
|
392 | if (Array.isArray(includedChunks) && includedChunks.indexOf(chunkName) === -1) {
|
393 | return false;
|
394 | }
|
395 |
|
396 | if (Array.isArray(excludedChunks) && excludedChunks.indexOf(chunkName) !== -1) {
|
397 | return false;
|
398 | }
|
399 |
|
400 | return true;
|
401 | });
|
402 | }
|
403 |
|
404 | isHotUpdateCompilation (assets) {
|
405 | return assets.js.length && assets.js.every(name => /\.hot-update\.js$/.test(name));
|
406 | }
|
407 |
|
408 | htmlWebpackPluginAssets (compilation, chunks) {
|
409 | const self = this;
|
410 | const compilationHash = compilation.hash;
|
411 |
|
412 |
|
413 | let publicPath = typeof compilation.options.output.publicPath !== 'undefined'
|
414 |
|
415 | ? compilation.mainTemplate.getPublicPath({hash: compilationHash})
|
416 |
|
417 | : path.relative(path.resolve(compilation.options.output.path, path.dirname(self.childCompilationOutputName)), compilation.options.output.path)
|
418 | .split(path.sep).join('/');
|
419 |
|
420 | if (publicPath.length && publicPath.substr(-1, 1) !== '/') {
|
421 | publicPath += '/';
|
422 | }
|
423 |
|
424 | const assets = {
|
425 |
|
426 | publicPath: publicPath,
|
427 |
|
428 | chunks: {},
|
429 |
|
430 | js: [],
|
431 |
|
432 | css: [],
|
433 |
|
434 | manifest: Object.keys(compilation.assets).filter(assetFile => path.extname(assetFile) === '.appcache')[0]
|
435 | };
|
436 |
|
437 |
|
438 | if (this.options.hash) {
|
439 | assets.manifest = self.appendHash(assets.manifest, compilationHash);
|
440 | assets.favicon = self.appendHash(assets.favicon, compilationHash);
|
441 | }
|
442 |
|
443 | for (let i = 0; i < chunks.length; i++) {
|
444 | const chunk = chunks[i];
|
445 | const chunkName = chunk.names[0];
|
446 |
|
447 | assets.chunks[chunkName] = {};
|
448 |
|
449 |
|
450 | let chunkFiles = [].concat(chunk.files).map(chunkFile => publicPath + chunkFile);
|
451 |
|
452 |
|
453 | if (this.options.hash) {
|
454 | chunkFiles = chunkFiles.map(chunkFile => self.appendHash(chunkFile, compilationHash));
|
455 | }
|
456 |
|
457 |
|
458 |
|
459 | const js = chunkFiles.find(chunkFile => /.js($|\?)/.test(chunkFile));
|
460 | if (js) {
|
461 | assets.chunks[chunkName].size = chunk.size;
|
462 | assets.chunks[chunkName].entry = js;
|
463 | assets.chunks[chunkName].hash = chunk.hash;
|
464 | assets.js.push(js);
|
465 | }
|
466 |
|
467 |
|
468 | const css = chunkFiles.filter(chunkFile => /.css($|\?)/.test(chunkFile));
|
469 | assets.chunks[chunkName].css = css;
|
470 | assets.css = assets.css.concat(css);
|
471 | }
|
472 |
|
473 |
|
474 |
|
475 | assets.css = _.uniq(assets.css);
|
476 |
|
477 | return assets;
|
478 | }
|
479 |
|
480 | |
481 |
|
482 |
|
483 | generateAssetTags (assets) {
|
484 |
|
485 | const scripts = assets.js.map(scriptPath => ({
|
486 | tagName: 'script',
|
487 | closeTag: true,
|
488 |
|
489 | attributes: {
|
490 | type: 'text/javascript',
|
491 | src: scriptPath
|
492 | }
|
493 | }));
|
494 |
|
495 | const selfClosingTag = !!this.options.xhtml;
|
496 |
|
497 | const styles = assets.css.map(stylePath => ({
|
498 | tagName: 'link',
|
499 | selfClosingTag: selfClosingTag,
|
500 |
|
501 | attributes: {
|
502 | href: stylePath,
|
503 | rel: 'stylesheet'
|
504 | }
|
505 | }));
|
506 |
|
507 | let head = [];
|
508 | let body = [];
|
509 |
|
510 |
|
511 | if (assets.favicon) {
|
512 | head.push({
|
513 | tagName: 'link',
|
514 | selfClosingTag: selfClosingTag,
|
515 | attributes: {
|
516 | rel: 'shortcut icon',
|
517 | href: assets.favicon
|
518 | }
|
519 | });
|
520 | }
|
521 |
|
522 | head = head.concat(styles);
|
523 |
|
524 | if (this.options.inject === 'head') {
|
525 | head = head.concat(scripts);
|
526 | } else {
|
527 | body = body.concat(scripts);
|
528 | }
|
529 | return {head: head, body: body};
|
530 | }
|
531 |
|
532 | |
533 |
|
534 |
|
535 | injectAssetsIntoHtml (html, assets, assetTags) {
|
536 | const htmlRegExp = /(<html[^>]*>)/i;
|
537 | const headRegExp = /(<\/head\s*>)/i;
|
538 | const bodyRegExp = /(<\/body\s*>)/i;
|
539 | const body = assetTags.body.map(this.createHtmlTag);
|
540 | const head = assetTags.head.map(this.createHtmlTag);
|
541 |
|
542 | if (body.length) {
|
543 | if (bodyRegExp.test(html)) {
|
544 |
|
545 | html = html.replace(bodyRegExp, match => body.join('') + match);
|
546 | } else {
|
547 |
|
548 | html += body.join('');
|
549 | }
|
550 | }
|
551 |
|
552 | if (head.length) {
|
553 |
|
554 | if (!headRegExp.test(html)) {
|
555 | if (!htmlRegExp.test(html)) {
|
556 | html = '<head></head>' + html;
|
557 | } else {
|
558 | html = html.replace(htmlRegExp, match => match + '<head></head>');
|
559 | }
|
560 | }
|
561 |
|
562 |
|
563 | html = html.replace(headRegExp, match => head.join('') + match);
|
564 | }
|
565 |
|
566 |
|
567 | if (assets.manifest) {
|
568 | html = html.replace(/(<html[^>]*)(>)/i, (match, start, end) => {
|
569 |
|
570 | if (/\smanifest\s*=/.test(match)) {
|
571 | return match;
|
572 | }
|
573 | return start + ' manifest="' + assets.manifest + '"' + end;
|
574 | });
|
575 | }
|
576 | return html;
|
577 | }
|
578 |
|
579 | |
580 |
|
581 |
|
582 | appendHash (url, hash) {
|
583 | if (!url) {
|
584 | return url;
|
585 | }
|
586 | return url + (url.indexOf('?') === -1 ? '?' : '&') + hash;
|
587 | }
|
588 |
|
589 | |
590 |
|
591 |
|
592 | createHtmlTag (tagDefinition) {
|
593 | const attributes = Object.keys(tagDefinition.attributes || {})
|
594 | .filter(attributeName => tagDefinition.attributes[attributeName] !== false)
|
595 | .map(attributeName => {
|
596 | if (tagDefinition.attributes[attributeName] === true) {
|
597 | return attributeName;
|
598 | }
|
599 | return attributeName + '="' + tagDefinition.attributes[attributeName] + '"';
|
600 | });
|
601 |
|
602 | const voidTag = tagDefinition.voidTag !== undefined ? tagDefinition.voidTag : !tagDefinition.closeTag;
|
603 | const selfClosingTag = tagDefinition.voidTag !== undefined ? tagDefinition.voidTag && this.options.xhtml : tagDefinition.selfClosingTag;
|
604 | return '<' + [tagDefinition.tagName].concat(attributes).join(' ') + (selfClosingTag ? '/' : '') + '>' +
|
605 | (tagDefinition.innerHTML || '') +
|
606 | (voidTag ? '' : '</' + tagDefinition.tagName + '>');
|
607 | }
|
608 |
|
609 | |
610 |
|
611 |
|
612 | getFullTemplatePath (template, context) {
|
613 |
|
614 | if (template.indexOf('!') === -1) {
|
615 | template = require.resolve('./lib/loader.js') + '!' + path.resolve(context, template);
|
616 | }
|
617 |
|
618 | return template.replace(
|
619 | /([!])([^/\\][^!?]+|[^/\\!?])($|\?[^!?\n]+$)/,
|
620 | (match, prefix, filepath, postfix) => prefix + path.resolve(filepath) + postfix);
|
621 | }
|
622 |
|
623 | |
624 |
|
625 |
|
626 |
|
627 | getAssetFiles (assets) {
|
628 | const files = _.uniq(Object.keys(assets).filter(assetType => assetType !== 'chunks' && assets[assetType]).reduce((files, assetType) => files.concat(assets[assetType]), []));
|
629 | files.sort();
|
630 | return files;
|
631 | }
|
632 |
|
633 | |
634 |
|
635 |
|
636 |
|
637 | applyPluginsAsyncWaterfall (compilation) {
|
638 | if (compilation.hooks) {
|
639 | return (eventName, requiresResult, pluginArgs) => {
|
640 | const ccEventName = trainCaseToCamelCase(eventName);
|
641 | if (!compilation.hooks[ccEventName]) {
|
642 | compilation.errors.push(
|
643 | new Error('No hook found for ' + eventName)
|
644 | );
|
645 | }
|
646 |
|
647 | return compilation.hooks[ccEventName].promise(pluginArgs);
|
648 | };
|
649 | }
|
650 |
|
651 |
|
652 | const promisedApplyPluginsAsyncWaterfall = function (name, init) {
|
653 | return new Promise((resolve, reject) => {
|
654 | const callback = function (err, result) {
|
655 | if (err) {
|
656 | return reject(err);
|
657 | }
|
658 | resolve(result);
|
659 | };
|
660 | compilation.applyPluginsAsyncWaterfall(name, init, callback);
|
661 | });
|
662 | };
|
663 |
|
664 | return (eventName, requiresResult, pluginArgs) => promisedApplyPluginsAsyncWaterfall(eventName, pluginArgs)
|
665 | .then(result => {
|
666 | if (requiresResult && !result) {
|
667 | compilation.warnings.push(
|
668 | new Error('Using ' + eventName + ' without returning a result is deprecated.')
|
669 | );
|
670 | }
|
671 | return _.extend(pluginArgs, result);
|
672 | });
|
673 | }
|
674 | }
|
675 |
|
676 |
|
677 |
|
678 |
|
679 |
|
680 |
|
681 |
|
682 |
|
683 | function trainCaseToCamelCase (word) {
|
684 | return word.replace(/-([\w])/g, (match, p1) => p1.toUpperCase());
|
685 | }
|
686 |
|
687 | module.exports = HtmlWebpackPlugin;
|