html-webpack-plugin
Advanced tools
Comparing version 5.0.0-alpha.2 to 5.0.0-alpha.3
291
index.js
@@ -186,160 +186,171 @@ // @ts-check | ||
compiler.hooks.emit.tapAsync('HtmlWebpackPlugin', | ||
compiler.hooks.thisCompilation.tap('HtmlWebpackPlugin', | ||
/** | ||
* Hook into the webpack emit phase | ||
* Hook into the webpack compilation | ||
* @param {WebpackCompilation} compilation | ||
*/ | ||
(compilation) => { | ||
compilation.hooks.processAssets.tapAsync( | ||
{ | ||
name: 'HtmlWebpackPlugin', | ||
stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS | ||
}, | ||
/** | ||
* Hook into the PROCESS_ASSETS_STAGE_ADDITIONS hook | ||
* @param {WebpackCompilation} compilationAssets | ||
* @param {(err?: Error) => void} callback | ||
*/ | ||
(compilation, callback) => { | ||
// Get all entry point names for this html file | ||
const entryNames = Array.from(compilation.entrypoints.keys()); | ||
const filteredEntryNames = filterChunks(entryNames, options.chunks, options.excludeChunks); | ||
const sortedEntryNames = sortEntryChunks(filteredEntryNames, options.chunksSortMode, compilation); | ||
(compilationAssets, callback) => { | ||
// Get all entry point names for this html file | ||
const entryNames = Array.from(compilation.entrypoints.keys()); | ||
const filteredEntryNames = filterChunks(entryNames, options.chunks, options.excludeChunks); | ||
const sortedEntryNames = sortEntryChunks(filteredEntryNames, options.chunksSortMode, compilation); | ||
const templateResult = options.templateContent | ||
? { mainCompilationHash: compilation.hash } | ||
: childCompilerPlugin.getCompilationEntryResult(options.template); | ||
const templateResult = options.templateContent | ||
? { mainCompilationHash: compilation.hash } | ||
: childCompilerPlugin.getCompilationEntryResult(options.template); | ||
if ('error' in templateResult) { | ||
compilation.errors.push(prettyError(templateResult.error, compiler.context).toString()); | ||
} | ||
if ('error' in templateResult) { | ||
compilation.errors.push(prettyError(templateResult.error, compiler.context).toString()); | ||
} | ||
const compiledEntries = 'compiledEntry' in templateResult ? { | ||
hash: templateResult.compiledEntry.hash, | ||
chunk: templateResult.compiledEntry.entry | ||
} : { | ||
hash: templateResult.mainCompilationHash | ||
}; | ||
const compiledEntries = 'compiledEntry' in templateResult ? { | ||
hash: templateResult.compiledEntry.hash, | ||
chunk: templateResult.compiledEntry.entry | ||
} : { | ||
hash: templateResult.mainCompilationHash | ||
}; | ||
const childCompilationOutputName = compilation.getAssetPath(options.filename, compiledEntries); | ||
const childCompilationOutputName = compilation.getAssetPath(options.filename, compiledEntries); | ||
// If the child compilation was not executed during a previous main compile run | ||
// it is a cached result | ||
const isCompilationCached = templateResult.mainCompilationHash !== compilation.hash; | ||
// If the child compilation was not executed during a previous main compile run | ||
// it is a cached result | ||
const isCompilationCached = templateResult.mainCompilationHash !== compilation.hash; | ||
// Turn the entry point names into file paths | ||
const assets = htmlWebpackPluginAssets(compilation, childCompilationOutputName, sortedEntryNames, options.publicPath); | ||
// Turn the entry point names into file paths | ||
const assets = htmlWebpackPluginAssets(compilation, childCompilationOutputName, sortedEntryNames, options.publicPath); | ||
// If the template and the assets did not change we don't have to emit the html | ||
const newAssetJson = JSON.stringify(getAssetFiles(assets)); | ||
if (isCompilationCached && options.cache && assetJson === newAssetJson) { | ||
return callback(); | ||
} else { | ||
assetJson = newAssetJson; | ||
} | ||
// If the template and the assets did not change we don't have to emit the html | ||
const newAssetJson = JSON.stringify(getAssetFiles(assets)); | ||
if (isCompilationCached && options.cache && assetJson === newAssetJson) { | ||
return callback(); | ||
} else { | ||
assetJson = newAssetJson; | ||
} | ||
// The html-webpack plugin uses a object representation for the html-tags which will be injected | ||
// to allow altering them more easily | ||
// Just before they are converted a third-party-plugin author might change the order and content | ||
const assetsPromise = getFaviconPublicPath(options.favicon, compilation, assets.publicPath) | ||
.then((faviconPath) => { | ||
assets.favicon = faviconPath; | ||
return getHtmlWebpackPluginHooks(compilation).beforeAssetTagGeneration.promise({ | ||
assets: assets, | ||
outputName: childCompilationOutputName, | ||
plugin: plugin | ||
}); | ||
}); | ||
// The html-webpack plugin uses a object representation for the html-tags which will be injected | ||
// to allow altering them more easily | ||
// Just before they are converted a third-party-plugin author might change the order and content | ||
const assetsPromise = getFaviconPublicPath(options.favicon, compilation, assets.publicPath) | ||
.then((faviconPath) => { | ||
assets.favicon = faviconPath; | ||
return getHtmlWebpackPluginHooks(compilation).beforeAssetTagGeneration.promise({ | ||
assets: assets, | ||
outputName: childCompilationOutputName, | ||
plugin: plugin | ||
}); | ||
}); | ||
// Turn the js and css paths into grouped HtmlTagObjects | ||
const assetTagGroupsPromise = assetsPromise | ||
// And allow third-party-plugin authors to reorder and change the assetTags before they are grouped | ||
.then(({ assets }) => getHtmlWebpackPluginHooks(compilation).alterAssetTags.promise({ | ||
assetTags: { | ||
scripts: generatedScriptTags(assets.js), | ||
styles: generateStyleTags(assets.css), | ||
meta: [ | ||
...generateBaseTag(options.base), | ||
...generatedMetaTags(options.meta), | ||
...generateFaviconTags(assets.favicon) | ||
] | ||
}, | ||
outputName: childCompilationOutputName, | ||
plugin: plugin | ||
})) | ||
.then(({ assetTags }) => { | ||
// Inject scripts to body unless it set explicitly to head | ||
const scriptTarget = options.inject === 'head' || | ||
(options.inject === false && options.scriptLoading !== 'blocking') ? 'head' : 'body'; | ||
// Group assets to `head` and `body` tag arrays | ||
const assetGroups = generateAssetGroups(assetTags, scriptTarget); | ||
// Allow third-party-plugin authors to reorder and change the assetTags once they are grouped | ||
return getHtmlWebpackPluginHooks(compilation).alterAssetTagGroups.promise({ | ||
headTags: assetGroups.headTags, | ||
bodyTags: assetGroups.bodyTags, | ||
outputName: childCompilationOutputName, | ||
plugin: plugin | ||
}); | ||
}); | ||
// Turn the js and css paths into grouped HtmlTagObjects | ||
const assetTagGroupsPromise = assetsPromise | ||
// And allow third-party-plugin authors to reorder and change the assetTags before they are grouped | ||
.then(({ assets }) => getHtmlWebpackPluginHooks(compilation).alterAssetTags.promise({ | ||
assetTags: { | ||
scripts: generatedScriptTags(assets.js), | ||
styles: generateStyleTags(assets.css), | ||
meta: [ | ||
...generateBaseTag(options.base), | ||
...generatedMetaTags(options.meta), | ||
...generateFaviconTags(assets.favicon) | ||
] | ||
}, | ||
outputName: childCompilationOutputName, | ||
plugin: plugin | ||
})) | ||
.then(({ assetTags }) => { | ||
// Inject scripts to body unless it set explicitly to head | ||
const scriptTarget = options.inject === 'head' || | ||
(options.inject === false && options.scriptLoading !== 'blocking') ? 'head' : 'body'; | ||
// Group assets to `head` and `body` tag arrays | ||
const assetGroups = generateAssetGroups(assetTags, scriptTarget); | ||
// Allow third-party-plugin authors to reorder and change the assetTags once they are grouped | ||
return getHtmlWebpackPluginHooks(compilation).alterAssetTagGroups.promise({ | ||
headTags: assetGroups.headTags, | ||
bodyTags: assetGroups.bodyTags, | ||
outputName: childCompilationOutputName, | ||
plugin: plugin | ||
}); | ||
}); | ||
// Turn the compiled template into a nodejs function or into a nodejs string | ||
const templateEvaluationPromise = Promise.resolve() | ||
.then(() => { | ||
if ('error' in templateResult) { | ||
return options.showErrors ? prettyError(templateResult.error, compiler.context).toHtml() : 'ERROR'; | ||
} | ||
// Allow to use a custom function / string instead | ||
if (options.templateContent !== false) { | ||
return options.templateContent; | ||
} | ||
// Once everything is compiled evaluate the html factory | ||
// and replace it with its content | ||
return ('compiledEntry' in templateResult) | ||
? plugin.evaluateCompilationResult(templateResult.compiledEntry.content, options.template) | ||
: Promise.reject(new Error('Child compilation contained no compiledEntry')); | ||
}); | ||
// Turn the compiled template into a nodejs function or into a nodejs string | ||
const templateEvaluationPromise = Promise.resolve() | ||
.then(() => { | ||
if ('error' in templateResult) { | ||
return options.showErrors ? prettyError(templateResult.error, compiler.context).toHtml() : 'ERROR'; | ||
} | ||
// Allow to use a custom function / string instead | ||
if (options.templateContent !== false) { | ||
return options.templateContent; | ||
} | ||
// Once everything is compiled evaluate the html factory | ||
// and replace it with its content | ||
return ('compiledEntry' in templateResult) | ||
? plugin.evaluateCompilationResult(templateResult.compiledEntry.content, options.template) | ||
: Promise.reject(new Error('Child compilation contained no compiledEntry')); | ||
}); | ||
const templateExectutionPromise = Promise.all([assetsPromise, assetTagGroupsPromise, templateEvaluationPromise]) | ||
// Execute the template | ||
.then(([assetsHookResult, assetTags, compilationResult]) => typeof compilationResult !== 'function' | ||
? compilationResult | ||
: executeTemplate(compilationResult, assetsHookResult.assets, { headTags: assetTags.headTags, bodyTags: assetTags.bodyTags }, compilation)); | ||
const templateExectutionPromise = Promise.all([assetsPromise, assetTagGroupsPromise, templateEvaluationPromise]) | ||
// Execute the template | ||
.then(([assetsHookResult, assetTags, compilationResult]) => typeof compilationResult !== 'function' | ||
? compilationResult | ||
: executeTemplate(compilationResult, assetsHookResult.assets, { headTags: assetTags.headTags, bodyTags: assetTags.bodyTags }, compilation)); | ||
const injectedHtmlPromise = Promise.all([assetTagGroupsPromise, templateExectutionPromise]) | ||
// Allow plugins to change the html before assets are injected | ||
.then(([assetTags, html]) => { | ||
const pluginArgs = { html, headTags: assetTags.headTags, bodyTags: assetTags.bodyTags, plugin: plugin, outputName: childCompilationOutputName }; | ||
return getHtmlWebpackPluginHooks(compilation).afterTemplateExecution.promise(pluginArgs); | ||
}) | ||
.then(({ html, headTags, bodyTags }) => { | ||
return postProcessHtml(html, assets, { headTags, bodyTags }); | ||
}); | ||
const injectedHtmlPromise = Promise.all([assetTagGroupsPromise, templateExectutionPromise]) | ||
// Allow plugins to change the html before assets are injected | ||
.then(([assetTags, html]) => { | ||
const pluginArgs = { html, headTags: assetTags.headTags, bodyTags: assetTags.bodyTags, plugin: plugin, outputName: childCompilationOutputName }; | ||
return getHtmlWebpackPluginHooks(compilation).afterTemplateExecution.promise(pluginArgs); | ||
}) | ||
.then(({ html, headTags, bodyTags }) => { | ||
return postProcessHtml(html, assets, { headTags, bodyTags }); | ||
}); | ||
const emitHtmlPromise = injectedHtmlPromise | ||
// Allow plugins to change the html after assets are injected | ||
.then((html) => { | ||
const pluginArgs = { html, plugin: plugin, outputName: childCompilationOutputName }; | ||
return getHtmlWebpackPluginHooks(compilation).beforeEmit.promise(pluginArgs) | ||
.then(result => result.html); | ||
}) | ||
.catch(err => { | ||
// In case anything went wrong the promise is resolved | ||
// with the error message and an error is logged | ||
compilation.errors.push(prettyError(err, compiler.context).toString()); | ||
return options.showErrors ? prettyError(err, compiler.context).toHtml() : 'ERROR'; | ||
}) | ||
.then(html => { | ||
// Allow to use [templatehash] as placeholder for the html-webpack-plugin name | ||
// See also https://survivejs.com/webpack/optimizing/adding-hashes-to-filenames/ | ||
// From https://github.com/webpack-contrib/extract-text-webpack-plugin/blob/8de6558e33487e7606e7cd7cb2adc2cccafef272/src/index.js#L212-L214 | ||
const finalOutputName = childCompilationOutputName.replace(/\[(?:(\w+):)?templatehash(?::([a-z]+\d*))?(?::(\d+))?\]/ig, (_, hashType, digestType, maxLength) => { | ||
return loaderUtils.getHashDigest(Buffer.from(html, 'utf8'), hashType, digestType, parseInt(maxLength, 10)); | ||
const emitHtmlPromise = injectedHtmlPromise | ||
// Allow plugins to change the html after assets are injected | ||
.then((html) => { | ||
const pluginArgs = { html, plugin: plugin, outputName: childCompilationOutputName }; | ||
return getHtmlWebpackPluginHooks(compilation).beforeEmit.promise(pluginArgs) | ||
.then(result => result.html); | ||
}) | ||
.catch(err => { | ||
// In case anything went wrong the promise is resolved | ||
// with the error message and an error is logged | ||
compilation.errors.push(prettyError(err, compiler.context).toString()); | ||
return options.showErrors ? prettyError(err, compiler.context).toHtml() : 'ERROR'; | ||
}) | ||
.then(html => { | ||
// Allow to use [templatehash] as placeholder for the html-webpack-plugin name | ||
// See also https://survivejs.com/webpack/optimizing/adding-hashes-to-filenames/ | ||
// From https://github.com/webpack-contrib/extract-text-webpack-plugin/blob/8de6558e33487e7606e7cd7cb2adc2cccafef272/src/index.js#L212-L214 | ||
const finalOutputName = childCompilationOutputName.replace(/\[(?:(\w+):)?templatehash(?::([a-z]+\d*))?(?::(\d+))?\]/ig, (_, hashType, digestType, maxLength) => { | ||
return loaderUtils.getHashDigest(Buffer.from(html, 'utf8'), hashType, digestType, parseInt(maxLength, 10)); | ||
}); | ||
// Add the evaluated html code to the webpack assets | ||
compilation.emitAsset(finalOutputName, new webpack.sources.RawSource(html, false)); | ||
return finalOutputName; | ||
}) | ||
.then((finalOutputName) => getHtmlWebpackPluginHooks(compilation).afterEmit.promise({ | ||
outputName: finalOutputName, | ||
plugin: plugin | ||
}).catch(err => { | ||
console.error(err); | ||
return null; | ||
}).then(() => null)); | ||
// Once all files are added to the webpack compilation | ||
// let the webpack compiler continue | ||
emitHtmlPromise.then(() => { | ||
callback(); | ||
}); | ||
// Add the evaluated html code to the webpack assets | ||
compilation.emitAsset(finalOutputName, new webpack.sources.RawSource(html, false)); | ||
return finalOutputName; | ||
}) | ||
.then((finalOutputName) => getHtmlWebpackPluginHooks(compilation).afterEmit.promise({ | ||
outputName: finalOutputName, | ||
plugin: plugin | ||
}).catch(err => { | ||
console.error(err); | ||
return null; | ||
}).then(() => null)); | ||
// Once all files are added to the webpack compilation | ||
// let the webpack compiler continue | ||
emitHtmlPromise.then(() => { | ||
callback(); | ||
}); | ||
}); | ||
}); | ||
@@ -346,0 +357,0 @@ |
{ | ||
"name": "html-webpack-plugin", | ||
"version": "5.0.0-alpha.2", | ||
"version": "5.0.0-alpha.3", | ||
"license": "MIT", | ||
@@ -5,0 +5,0 @@ "description": "Simplifies creation of HTML files to serve your webpack bundles", |
140555
2118