Skip to content

Commit 57c5a4e

Browse files
fix: reemit favicon in serve/watch mode (#1804)
1 parent d3819ab commit 57c5a4e

File tree

1 file changed

+37
-41
lines changed

1 file changed

+37
-41
lines changed

index.js

Lines changed: 37 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,4 @@
11
// @ts-check
2-
// Import types
3-
/** @typedef {import("./typings").HtmlTagObject} HtmlTagObject */
4-
/** @typedef {import("./typings").Options} HtmlWebpackOptions */
5-
/** @typedef {import("./typings").ProcessedOptions} ProcessedHtmlWebpackOptions */
6-
/** @typedef {import("./typings").TemplateParameter} TemplateParameter */
7-
/** @typedef {import("webpack/lib/Compiler.js")} WebpackCompiler */
8-
/** @typedef {import("webpack/lib/Compilation.js")} WebpackCompilation */
92
'use strict';
103

114
const promisify = require('util').promisify;
@@ -23,7 +16,13 @@ const chunkSorter = require('./lib/chunksorter.js');
2316
const getHtmlWebpackPluginHooks = require('./lib/hooks.js').getHtmlWebpackPluginHooks;
2417
const { assert } = require('console');
2518

26-
const fsReadFileAsync = promisify(fs.readFile);
19+
/** @typedef {import("./typings").HtmlTagObject} HtmlTagObject */
20+
/** @typedef {import("./typings").Options} HtmlWebpackOptions */
21+
/** @typedef {import("./typings").ProcessedOptions} ProcessedHtmlWebpackOptions */
22+
/** @typedef {import("./typings").TemplateParameter} TemplateParameter */
23+
/** @typedef {import("webpack/lib/Compiler.js")} WebpackCompiler */
24+
/** @typedef {import("webpack/lib/Compilation.js")} WebpackCompilation */
25+
/** @typedef {Array<{ source: import('webpack').sources.Source, name: string }>} PreviousEmittedAssets */
2726

2827
class HtmlWebpackPlugin {
2928
/**
@@ -216,7 +215,7 @@ function hookIntoCompiler (compiler, options, plugin) {
216215
/**
217216
* store the previous generated asset to emit them even if the content did not change
218217
* to support watch mode for third party plugins like the clean-webpack-plugin or the compression plugin
219-
* @type {Array<{html: string, name: string}>}
218+
* @type {PreviousEmittedAssets}
220219
*/
221220
let previousEmittedAssets = [];
222221

@@ -302,8 +301,8 @@ function hookIntoCompiler (compiler, options, plugin) {
302301
// If the template and the assets did not change we don't have to emit the html
303302
const newAssetJson = JSON.stringify(getAssetFiles(assets));
304303
if (isCompilationCached && options.cache && assetJson === newAssetJson) {
305-
previousEmittedAssets.forEach(({ name, html }) => {
306-
compilation.emitAsset(name, new webpack.sources.RawSource(html, false));
304+
previousEmittedAssets.forEach(({ name, source }) => {
305+
compilation.emitAsset(name, source);
307306
});
308307
return callback();
309308
} else {
@@ -314,7 +313,7 @@ function hookIntoCompiler (compiler, options, plugin) {
314313
// The html-webpack plugin uses a object representation for the html-tags which will be injected
315314
// to allow altering them more easily
316315
// Just before they are converted a third-party-plugin author might change the order and content
317-
const assetsPromise = getFaviconPublicPath(options.favicon, compilation, assets.publicPath)
316+
const assetsPromise = generateFavicon(options.favicon, compilation, assets.publicPath, previousEmittedAssets)
318317
.then((faviconPath) => {
319318
assets.favicon = faviconPath;
320319
return getHtmlWebpackPluginHooks(compilation).beforeAssetTagGeneration.promise({
@@ -408,9 +407,12 @@ function hookIntoCompiler (compiler, options, plugin) {
408407
'[templatehash] is now [contenthash]')
409408
);
410409
const replacedFilename = replacePlaceholdersInFilename(filename, html, compilation);
410+
const source = new webpack.sources.RawSource(html, false);
411+
411412
// Add the evaluated html code to the webpack assets
412-
compilation.emitAsset(replacedFilename.path, new webpack.sources.RawSource(html, false), replacedFilename.info);
413-
previousEmittedAssets.push({ name: replacedFilename.path, html });
413+
compilation.emitAsset(replacedFilename.path, source, replacedFilename.info);
414+
previousEmittedAssets.push({ name: replacedFilename.path, source });
415+
414416
return replacedFilename.path;
415417
})
416418
.then((finalOutputName) => getHtmlWebpackPluginHooks(compilation).afterEmit.promise({
@@ -529,26 +531,6 @@ function hookIntoCompiler (compiler, options, plugin) {
529531
return Promise.resolve(htmlAfterMinification);
530532
}
531533

532-
/*
533-
* Pushes the content of the given filename to the compilation assets
534-
* @param {string} filename
535-
* @param {WebpackCompilation} compilation
536-
*
537-
* @returns {string} file basename
538-
*/
539-
function addFileToAssets (filename, compilation) {
540-
filename = path.resolve(compilation.compiler.context, filename);
541-
return fsReadFileAsync(filename)
542-
.then(source => new webpack.sources.RawSource(source, false))
543-
.catch(() => Promise.reject(new Error('HtmlWebpackPlugin: could not load file ' + filename)))
544-
.then(rawSource => {
545-
const basename = path.basename(filename);
546-
compilation.fileDependencies.add(filename);
547-
compilation.emitAsset(basename, rawSource);
548-
return basename;
549-
});
550-
}
551-
552534
/**
553535
* Replace [contenthash] in filename
554536
*
@@ -757,23 +739,37 @@ function hookIntoCompiler (compiler, options, plugin) {
757739
* Converts a favicon file from disk to a webpack resource
758740
* and returns the url to the resource
759741
*
760-
* @param {string|false} faviconFilePath
742+
* @param {string|false} favicon
761743
* @param {WebpackCompilation} compilation
762744
* @param {string} publicPath
745+
* @param {PreviousEmittedAssets} previousEmittedAssets
763746
* @returns {Promise<string|undefined>}
764747
*/
765-
function getFaviconPublicPath (faviconFilePath, compilation, publicPath) {
766-
if (!faviconFilePath) {
748+
function generateFavicon (favicon, compilation, publicPath, previousEmittedAssets) {
749+
if (!favicon) {
767750
return Promise.resolve(undefined);
768751
}
769-
return addFileToAssets(faviconFilePath, compilation)
770-
.then((faviconName) => {
771-
const faviconPath = publicPath + faviconName;
752+
753+
const filename = path.resolve(compilation.compiler.context, favicon);
754+
755+
return promisify(compilation.inputFileSystem.readFile)(filename)
756+
.then((buf) => {
757+
const source = new webpack.sources.RawSource(buf, false);
758+
const name = path.basename(filename);
759+
760+
compilation.fileDependencies.add(filename);
761+
compilation.emitAsset(name, source);
762+
previousEmittedAssets.push({ name, source });
763+
764+
const faviconPath = publicPath + name;
765+
772766
if (options.hash) {
773767
return appendHash(faviconPath, compilation.hash);
774768
}
769+
775770
return faviconPath;
776-
});
771+
})
772+
.catch(() => Promise.reject(new Error('HtmlWebpackPlugin: could not load file ' + filename)));
777773
}
778774

779775
/**

0 commit comments

Comments
 (0)