1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594 |
- "use strict";
- const promisify = require("util").promisify;
- const vm = require("vm");
- const fs = require("fs");
- const _uniq = require("lodash/uniq");
- const path = require("path");
- const { CachedChildCompilation } = require("./lib/cached-child-compiler");
- const {
- createHtmlTagObject,
- htmlTagObjectToString,
- HtmlTagArray,
- } = require("./lib/html-tags");
- const prettyError = require("./lib/errors.js");
- const chunkSorter = require("./lib/chunksorter.js");
- const { AsyncSeriesWaterfallHook } = require("tapable");
- const compilationHooksMap = new WeakMap();
- class HtmlWebpackPlugin {
-
-
-
-
- static getCompilationHooks(compilation) {
- let hooks = compilationHooksMap.get(compilation);
- if (!hooks) {
- hooks = {
- beforeAssetTagGeneration: new AsyncSeriesWaterfallHook(["pluginArgs"]),
- alterAssetTags: new AsyncSeriesWaterfallHook(["pluginArgs"]),
- alterAssetTagGroups: new AsyncSeriesWaterfallHook(["pluginArgs"]),
- afterTemplateExecution: new AsyncSeriesWaterfallHook(["pluginArgs"]),
- beforeEmit: new AsyncSeriesWaterfallHook(["pluginArgs"]),
- afterEmit: new AsyncSeriesWaterfallHook(["pluginArgs"]),
- };
- compilationHooksMap.set(compilation, hooks);
- }
- return hooks;
- }
-
- constructor(options) {
-
-
- this.userOptions = options || {};
- this.version = HtmlWebpackPlugin.version;
-
-
- const defaultOptions = {
- template: "auto",
- templateContent: false,
- templateParameters: templateParametersGenerator,
- filename: "index.html",
- publicPath:
- this.userOptions.publicPath === undefined
- ? "auto"
- : this.userOptions.publicPath,
- hash: false,
- inject: this.userOptions.scriptLoading === "blocking" ? "body" : "head",
- scriptLoading: "defer",
- compile: true,
- favicon: false,
- minify: "auto",
- cache: true,
- showErrors: true,
- chunks: "all",
- excludeChunks: [],
- chunksSortMode: "auto",
- meta: {},
- base: false,
- title: "Webpack App",
- xhtml: false,
- };
-
- this.options = Object.assign(defaultOptions, this.userOptions);
- }
-
- apply(compiler) {
- this.logger = compiler.getInfrastructureLogger("HtmlWebpackPlugin");
- const options = this.options;
- options.template = this.getTemplatePath(
- this.options.template,
- compiler.context,
- );
-
- if (
- options.scriptLoading !== "defer" &&
- options.scriptLoading !== "blocking" &&
- options.scriptLoading !== "module" &&
- options.scriptLoading !== "systemjs-module"
- ) {
-
- (this.logger).error(
- 'The "scriptLoading" option need to be set to "defer", "blocking" or "module" or "systemjs-module"',
- );
- }
- if (
- options.inject !== true &&
- options.inject !== false &&
- options.inject !== "head" &&
- options.inject !== "body"
- ) {
-
- (this.logger).error(
- 'The `inject` option needs to be set to true, false, "head" or "body',
- );
- }
- if (
- this.options.templateParameters !== false &&
- typeof this.options.templateParameters !== "function" &&
- typeof this.options.templateParameters !== "object"
- ) {
-
- (this.logger).error(
- "The `templateParameters` has to be either a function or an object or false",
- );
- }
-
- if (
- !this.userOptions.template &&
- options.templateContent === false &&
- options.meta
- ) {
- options.meta = Object.assign(
- {},
- options.meta,
- {
-
-
- viewport: "width=device-width, initial-scale=1",
- },
- this.userOptions.meta,
- );
- }
-
- const userOptionFilename =
- this.userOptions.filename || this.options.filename;
- const filenameFunction =
- typeof userOptionFilename === "function"
- ? userOptionFilename
- :
- (entryName) => userOptionFilename.replace(/\[name\]/g, entryName);
-
- const entryNames = Object.keys(compiler.options.entry);
- const outputFileNames = new Set(
- (entryNames.length ? entryNames : ["main"]).map(filenameFunction),
- );
-
- outputFileNames.forEach((outputFileName) => {
-
- const assetJson = { value: undefined };
-
- const previousEmittedAssets = [];
-
- const childCompilerPlugin = new CachedChildCompilation(compiler);
- if (!this.options.templateContent) {
- childCompilerPlugin.addEntry(this.options.template);
- }
-
-
- let filename = outputFileName;
- if (path.resolve(filename) === path.normalize(filename)) {
- const outputPath =
- (
- compiler.options.output.path
- );
- filename = path.relative(outputPath, filename);
- }
- compiler.hooks.thisCompilation.tap(
- "HtmlWebpackPlugin",
-
- (compilation) => {
- compilation.hooks.processAssets.tapAsync(
- {
- name: "HtmlWebpackPlugin",
- stage:
-
- compiler.webpack.Compilation
- .PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE,
- },
-
- (_, callback) => {
- this.generateHTML(
- compiler,
- compilation,
- filename,
- childCompilerPlugin,
- previousEmittedAssets,
- assetJson,
- callback,
- );
- },
- );
- },
- );
- });
- }
-
- getTemplatePath(template, context) {
- if (template === "auto") {
- template = path.resolve(context, "src/index.ejs");
- if (!fs.existsSync(template)) {
- template = path.join(__dirname, "default_index.ejs");
- }
- }
-
- if (template.indexOf("!") === -1) {
- template =
- require.resolve("./lib/loader.js") +
- "!" +
- path.resolve(context, template);
- }
-
- return template.replace(
- /([!])([^/\\][^!?]+|[^/\\!?])($|\?[^!?\n]+$)/,
- (match, prefix, filepath, postfix) =>
- prefix + path.resolve(filepath) + postfix,
- );
- }
-
- filterEntryChunks(chunks, includedChunks, excludedChunks) {
- return chunks.filter((chunkName) => {
-
- if (
- Array.isArray(includedChunks) &&
- includedChunks.indexOf(chunkName) === -1
- ) {
- return false;
- }
-
- if (
- Array.isArray(excludedChunks) &&
- excludedChunks.indexOf(chunkName) !== -1
- ) {
- return false;
- }
-
- return true;
- });
- }
-
- sortEntryChunks(entryNames, sortMode, compilation) {
-
- if (typeof sortMode === "function") {
- return entryNames.sort(sortMode);
- }
-
- if (typeof chunkSorter[sortMode] !== "undefined") {
- return chunkSorter[sortMode](entryNames, compilation, this.options);
- }
- throw new Error('"' + sortMode + '" is not a valid chunk sort mode');
- }
-
- urlencodePath(filePath) {
-
-
-
-
-
- const queryStringStart = filePath.indexOf("?");
- const urlPath =
- queryStringStart === -1 ? filePath : filePath.substr(0, queryStringStart);
- const queryString = filePath.substr(urlPath.length);
-
- const encodedUrlPath = urlPath.split("/").map(encodeURIComponent).join("/");
- return encodedUrlPath + queryString;
- }
-
- appendHash(url, hash) {
- if (!url) {
- return url;
- }
- return url + (url.indexOf("?") === -1 ? "?" : "&") + hash;
- }
-
- getPublicPath(compilation, filename, customPublicPath) {
-
- const webpackPublicPath = compilation.getAssetPath(
- (
- compilation.outputOptions.publicPath
- ),
- { hash: compilation.hash },
- );
-
- const isPublicPathDefined = webpackPublicPath !== "auto";
- let publicPath =
-
- customPublicPath !== "auto"
- ? customPublicPath
- : isPublicPathDefined
- ?
- webpackPublicPath
- :
- path
- .relative(
- path.resolve(
- (compilation.options.output.path),
- path.dirname(filename),
- ),
- (compilation.options.output.path),
- )
- .split(path.sep)
- .join("/");
- if (publicPath.length && publicPath.substr(-1, 1) !== "/") {
- publicPath += "/";
- }
- return publicPath;
- }
-
- getAssetsInformationByGroups(compilation, outputName, entryNames) {
-
- const publicPath = this.getPublicPath(
- compilation,
- outputName,
- this.options.publicPath,
- );
-
- const assets = {
-
- publicPath,
-
- js: [],
-
- css: [],
-
- manifest: Object.keys(compilation.assets).find(
- (assetFile) => path.extname(assetFile) === ".appcache",
- ),
-
- favicon: undefined,
- };
-
- if (this.options.hash && assets.manifest) {
- assets.manifest = this.appendHash(
- assets.manifest,
- (compilation.hash),
- );
- }
-
- const entryPointPublicPathMap = {};
- const extensionRegexp = /\.(css|js|mjs)(\?|$)/;
- for (let i = 0; i < entryNames.length; i++) {
- const entryName = entryNames[i];
-
- const entryPointUnfilteredFiles = (
- compilation.entrypoints.get(entryName)
- ).getFiles();
- const entryPointFiles = entryPointUnfilteredFiles.filter((chunkFile) => {
- const asset = compilation.getAsset(chunkFile);
- if (!asset) {
- return true;
- }
-
- const assetMetaInformation = asset.info || {};
- return !(
- assetMetaInformation.hotModuleReplacement ||
- assetMetaInformation.development
- );
- });
-
-
-
- const entryPointPublicPaths = entryPointFiles.map((chunkFile) => {
- const entryPointPublicPath = publicPath + this.urlencodePath(chunkFile);
- return this.options.hash
- ? this.appendHash(
- entryPointPublicPath,
- (compilation.hash),
- )
- : entryPointPublicPath;
- });
- entryPointPublicPaths.forEach((entryPointPublicPath) => {
- const extMatch = extensionRegexp.exec(
- (entryPointPublicPath),
- );
-
- if (!extMatch) {
- return;
- }
-
-
- if (entryPointPublicPathMap[entryPointPublicPath]) {
- return;
- }
- entryPointPublicPathMap[entryPointPublicPath] = true;
-
- const ext = extMatch[1] === "mjs" ? "js" : extMatch[1];
- assets[ext].push(entryPointPublicPath);
- });
- }
- return assets;
- }
-
- evaluateCompilationResult(source, publicPath, templateFilename) {
- if (!source) {
- return Promise.reject(
- new Error("The child compilation didn't provide a result"),
- );
- }
-
-
- if (source.indexOf("HTML_WEBPACK_PLUGIN_RESULT") >= 0) {
- source += ";\nHTML_WEBPACK_PLUGIN_RESULT";
- }
- const templateWithoutLoaders = templateFilename
- .replace(/^.+!/, "")
- .replace(/\?.+$/, "");
- const vmContext = vm.createContext({
- ...global,
- HTML_WEBPACK_PLUGIN: true,
- require: require,
- htmlWebpackPluginPublicPath: publicPath,
- __filename: templateWithoutLoaders,
- __dirname: path.dirname(templateWithoutLoaders),
- AbortController: global.AbortController,
- AbortSignal: global.AbortSignal,
- Blob: global.Blob,
- Buffer: global.Buffer,
- ByteLengthQueuingStrategy: global.ByteLengthQueuingStrategy,
- BroadcastChannel: global.BroadcastChannel,
- CompressionStream: global.CompressionStream,
- CountQueuingStrategy: global.CountQueuingStrategy,
- Crypto: global.Crypto,
- CryptoKey: global.CryptoKey,
- CustomEvent: global.CustomEvent,
- DecompressionStream: global.DecompressionStream,
- Event: global.Event,
- EventTarget: global.EventTarget,
- File: global.File,
- FormData: global.FormData,
- Headers: global.Headers,
- MessageChannel: global.MessageChannel,
- MessageEvent: global.MessageEvent,
- MessagePort: global.MessagePort,
- PerformanceEntry: global.PerformanceEntry,
- PerformanceMark: global.PerformanceMark,
- PerformanceMeasure: global.PerformanceMeasure,
- PerformanceObserver: global.PerformanceObserver,
- PerformanceObserverEntryList: global.PerformanceObserverEntryList,
- PerformanceResourceTiming: global.PerformanceResourceTiming,
- ReadableByteStreamController: global.ReadableByteStreamController,
- ReadableStream: global.ReadableStream,
- ReadableStreamBYOBReader: global.ReadableStreamBYOBReader,
- ReadableStreamBYOBRequest: global.ReadableStreamBYOBRequest,
- ReadableStreamDefaultController: global.ReadableStreamDefaultController,
- ReadableStreamDefaultReader: global.ReadableStreamDefaultReader,
- Response: global.Response,
- Request: global.Request,
- SubtleCrypto: global.SubtleCrypto,
- DOMException: global.DOMException,
- TextDecoder: global.TextDecoder,
- TextDecoderStream: global.TextDecoderStream,
- TextEncoder: global.TextEncoder,
- TextEncoderStream: global.TextEncoderStream,
- TransformStream: global.TransformStream,
- TransformStreamDefaultController: global.TransformStreamDefaultController,
- URL: global.URL,
- URLSearchParams: global.URLSearchParams,
- WebAssembly: global.WebAssembly,
- WritableStream: global.WritableStream,
- WritableStreamDefaultController: global.WritableStreamDefaultController,
- WritableStreamDefaultWriter: global.WritableStreamDefaultWriter,
- });
- const vmScript = new vm.Script(source, {
- filename: templateWithoutLoaders,
- });
-
- let newSource;
- try {
- newSource = vmScript.runInContext(vmContext);
- } catch (e) {
- return Promise.reject(e);
- }
- if (
- typeof newSource === "object" &&
- newSource.__esModule &&
- newSource.default !== undefined
- ) {
- newSource = newSource.default;
- }
- return typeof newSource === "string" || typeof newSource === "function"
- ? Promise.resolve(newSource)
- : Promise.reject(
- new Error(
- 'The loader "' + templateWithoutLoaders + "\" didn't return html.",
- ),
- );
- }
-
- prepareAssetTagGroupForRendering(assetTagGroup) {
- const xhtml = this.options.xhtml;
- return HtmlTagArray.from(
- assetTagGroup.map((assetTag) => {
- const copiedAssetTag = Object.assign({}, assetTag);
- copiedAssetTag.toString = function () {
- return htmlTagObjectToString(this, xhtml);
- };
- return copiedAssetTag;
- }),
- );
- }
-
- getTemplateParameters(compilation, assetsInformationByGroups, assetTags) {
- const templateParameters = this.options.templateParameters;
- if (templateParameters === false) {
- return Promise.resolve({});
- }
- if (
- typeof templateParameters !== "function" &&
- typeof templateParameters !== "object"
- ) {
- throw new Error(
- "templateParameters has to be either a function or an object",
- );
- }
- const templateParameterFunction =
- typeof templateParameters === "function"
- ?
- templateParameters
- :
- (compilation, assetsInformationByGroups, assetTags, options) =>
- Object.assign(
- {},
- templateParametersGenerator(
- compilation,
- assetsInformationByGroups,
- assetTags,
- options,
- ),
- templateParameters,
- );
- const preparedAssetTags = {
- headTags: this.prepareAssetTagGroupForRendering(assetTags.headTags),
- bodyTags: this.prepareAssetTagGroupForRendering(assetTags.bodyTags),
- };
- return Promise.resolve().then(() =>
- templateParameterFunction(
- compilation,
- assetsInformationByGroups,
- preparedAssetTags,
- this.options,
- ),
- );
- }
-
- executeTemplate(
- templateFunction,
- assetsInformationByGroups,
- assetTags,
- compilation,
- ) {
-
- const templateParamsPromise = this.getTemplateParameters(
- compilation,
- assetsInformationByGroups,
- assetTags,
- );
- return templateParamsPromise.then((templateParams) => {
- try {
-
-
- return templateFunction(templateParams);
- } catch (e) {
-
- compilation.errors.push(new Error("Template execution failed: " + e));
- return Promise.reject(e);
- }
- });
- }
-
- postProcessHtml(
- compiler,
- originalHtml,
- assetsInformationByGroups,
- assetTags,
- ) {
- let html = originalHtml;
- if (typeof html !== "string") {
- return Promise.reject(
- new Error(
- "Expected html to be a string but got " + JSON.stringify(html),
- ),
- );
- }
- if (this.options.inject) {
- const htmlRegExp = /(<html[^>]*>)/i;
- const headRegExp = /(<\/head\s*>)/i;
- const bodyRegExp = /(<\/body\s*>)/i;
- const metaViewportRegExp = /<meta[^>]+name=["']viewport["'][^>]*>/i;
- const body = assetTags.bodyTags.map((assetTagObject) =>
- htmlTagObjectToString(assetTagObject, this.options.xhtml),
- );
- const head = assetTags.headTags
- .filter((item) => {
- if (
- item.tagName === "meta" &&
- item.attributes &&
- item.attributes.name === "viewport" &&
- metaViewportRegExp.test(html)
- ) {
- return false;
- }
- return true;
- })
- .map((assetTagObject) =>
- htmlTagObjectToString(assetTagObject, this.options.xhtml),
- );
- if (body.length) {
- if (bodyRegExp.test(html)) {
-
- html = html.replace(bodyRegExp, (match) => body.join("") + match);
- } else {
-
- html += body.join("");
- }
- }
- if (head.length) {
-
- if (!headRegExp.test(html)) {
- if (!htmlRegExp.test(html)) {
- html = "<head></head>" + html;
- } else {
- html = html.replace(htmlRegExp, (match) => match + "<head></head>");
- }
- }
-
- html = html.replace(headRegExp, (match) => head.join("") + match);
- }
-
- if (assetsInformationByGroups.manifest) {
- html = html.replace(/(<html[^>]*)(>)/i, (match, start, end) => {
-
- if (/\smanifest\s*=/.test(match)) {
- return match;
- }
- return (
- start +
- ' manifest="' +
- assetsInformationByGroups.manifest +
- '"' +
- end
- );
- });
- }
- }
-
-
-
- const isProductionLikeMode =
- compiler.options.mode === "production" || !compiler.options.mode;
- const needMinify =
- this.options.minify === true ||
- typeof this.options.minify === "object" ||
- (this.options.minify === "auto" && isProductionLikeMode);
- if (!needMinify) {
- return Promise.resolve(html);
- }
- const minifyOptions =
- typeof this.options.minify === "object"
- ? this.options.minify
- : {
-
- collapseWhitespace: true,
- keepClosingSlash: true,
- removeComments: true,
- removeRedundantAttributes: true,
- removeScriptTypeAttributes: true,
- removeStyleLinkTypeAttributes: true,
- useShortDoctype: true,
- };
- try {
- html = require("html-minifier-terser").minify(html, minifyOptions);
- } catch (e) {
- const isParseError = String(e.message).indexOf("Parse Error") === 0;
- if (isParseError) {
- e.message =
- "html-webpack-plugin could not minify the generated output.\n" +
- "In production mode the html minification is enabled by default.\n" +
- "If you are not generating a valid html output please disable it manually.\n" +
- "You can do so by adding the following setting to your HtmlWebpackPlugin config:\n|\n|" +
- " minify: false\n|\n" +
- "See https://github.com/jantimon/html-webpack-plugin#options for details.\n\n" +
- "For parser dedicated bugs please create an issue here:\n" +
- "https://danielruf.github.io/html-minifier-terser/" +
- "\n" +
- e.message;
- }
- return Promise.reject(e);
- }
- return Promise.resolve(html);
- }
-
- getAssetFiles(assets) {
- const files = _uniq(
- Object.keys(assets)
- .filter((assetType) => assetType !== "chunks" && assets[assetType])
- .reduce((files, assetType) => files.concat(assets[assetType]), []),
- );
- files.sort();
- return files;
- }
-
- generateFavicon(
- compiler,
- favicon,
- compilation,
- publicPath,
- previousEmittedAssets,
- ) {
- if (!favicon) {
- return Promise.resolve(undefined);
- }
- const filename = path.resolve(compilation.compiler.context, favicon);
- return promisify(compilation.inputFileSystem.readFile)(filename)
- .then((buf) => {
- const source = new compiler.webpack.sources.RawSource(
- (buf),
- false,
- );
- const name = path.basename(filename);
- compilation.fileDependencies.add(filename);
- compilation.emitAsset(name, source);
- previousEmittedAssets.push({ name, source });
- const faviconPath = publicPath + name;
- if (this.options.hash) {
- return this.appendHash(
- faviconPath,
- (compilation.hash),
- );
- }
- return faviconPath;
- })
- .catch(() =>
- Promise.reject(
- new Error("HtmlWebpackPlugin: could not load file " + filename),
- ),
- );
- }
-
- generatedScriptTags(jsAssets) {
-
- return jsAssets.map((src) => {
- const attributes = {};
- if (this.options.scriptLoading === "defer") {
- attributes.defer = true;
- } else if (this.options.scriptLoading === "module") {
- attributes.type = "module";
- } else if (this.options.scriptLoading === "systemjs-module") {
- attributes.type = "systemjs-module";
- }
- attributes.src = src;
- return {
- tagName: "script",
- voidTag: false,
- meta: { plugin: "html-webpack-plugin" },
- attributes,
- };
- });
- }
-
- generateStyleTags(cssAssets) {
- return cssAssets.map((styleAsset) => ({
- tagName: "link",
- voidTag: true,
- meta: { plugin: "html-webpack-plugin" },
- attributes: {
- href: styleAsset,
- rel: "stylesheet",
- },
- }));
- }
-
- generateBaseTag(base) {
- return [
- {
- tagName: "base",
- voidTag: true,
- meta: { plugin: "html-webpack-plugin" },
-
- attributes:
- typeof base === "string"
- ? {
- href: base,
- }
- : base,
- },
- ];
- }
-
- generatedMetaTags(metaOptions) {
- if (metaOptions === false) {
- return [];
- }
-
-
-
- const metaTagAttributeObjects = Object.keys(metaOptions)
- .map((metaName) => {
- const metaTagContent = metaOptions[metaName];
- return typeof metaTagContent === "string"
- ? {
- name: metaName,
- content: metaTagContent,
- }
- : metaTagContent;
- })
- .filter((attribute) => attribute !== false);
-
-
- return metaTagAttributeObjects.map((metaTagAttributes) => {
- if (metaTagAttributes === false) {
- throw new Error("Invalid meta tag");
- }
- return {
- tagName: "meta",
- voidTag: true,
- meta: { plugin: "html-webpack-plugin" },
- attributes: metaTagAttributes,
- };
- });
- }
-
- generateFaviconTag(favicon) {
- return [
- {
- tagName: "link",
- voidTag: true,
- meta: { plugin: "html-webpack-plugin" },
- attributes: {
- rel: "icon",
- href: favicon,
- },
- },
- ];
- }
-
- groupAssetsByElements(assetTags, scriptTarget) {
-
- const result = {
- headTags: [...assetTags.meta, ...assetTags.styles],
- bodyTags: [],
- };
-
-
- if (scriptTarget === "body") {
- result.bodyTags.push(...assetTags.scripts);
- } else {
-
-
- const insertPosition =
- this.options.scriptLoading === "blocking"
- ? result.headTags.length
- : assetTags.meta.length;
- result.headTags.splice(insertPosition, 0, ...assetTags.scripts);
- }
- return result;
- }
-
- replacePlaceholdersInFilename(compiler, filename, fileContent, compilation) {
- if (/\[\\*([\w:]+)\\*\]/i.test(filename) === false) {
- return { path: filename, info: {} };
- }
- const hash = compiler.webpack.util.createHash(
- compilation.outputOptions.hashFunction,
- );
- hash.update(fileContent);
- if (compilation.outputOptions.hashSalt) {
- hash.update(compilation.outputOptions.hashSalt);
- }
- const contentHash = (
- hash
- .digest(compilation.outputOptions.hashDigest)
- .slice(0, compilation.outputOptions.hashDigestLength)
- );
- return compilation.getPathWithInfo(filename, {
- contentHash,
- chunk: {
- hash: contentHash,
-
- contentHash,
- },
- });
- }
-
- generateHTML(
- compiler,
- compilation,
- outputName,
- childCompilerPlugin,
- previousEmittedAssets,
- assetJson,
- callback,
- ) {
-
- const entryNames = Array.from(compilation.entrypoints.keys());
- const filteredEntryNames = this.filterEntryChunks(
- entryNames,
- this.options.chunks,
- this.options.excludeChunks,
- );
- const sortedEntryNames = this.sortEntryChunks(
- filteredEntryNames,
- this.options.chunksSortMode,
- compilation,
- );
- const templateResult = this.options.templateContent
- ? { mainCompilationHash: compilation.hash }
- : childCompilerPlugin.getCompilationEntryResult(this.options.template);
- if ("error" in templateResult) {
- compilation.errors.push(
- prettyError(templateResult.error, compiler.context).toString(),
- );
- }
-
-
- const isCompilationCached =
- templateResult.mainCompilationHash !== compilation.hash;
-
- const assetsInformationByGroups = this.getAssetsInformationByGroups(
- compilation,
- outputName,
- sortedEntryNames,
- );
-
- const newAssetJson = JSON.stringify(
- this.getAssetFiles(assetsInformationByGroups),
- );
- if (
- isCompilationCached &&
- this.options.cache &&
- assetJson.value === newAssetJson
- ) {
- previousEmittedAssets.forEach(({ name, source, info }) => {
- compilation.emitAsset(name, source, info);
- });
- return callback();
- } else {
- previousEmittedAssets.length = 0;
- assetJson.value = newAssetJson;
- }
-
-
-
- const assetsPromise = this.generateFavicon(
- compiler,
- this.options.favicon,
- compilation,
- assetsInformationByGroups.publicPath,
- previousEmittedAssets,
- ).then((faviconPath) => {
- assetsInformationByGroups.favicon = faviconPath;
- return HtmlWebpackPlugin.getCompilationHooks(
- compilation,
- ).beforeAssetTagGeneration.promise({
- assets: assetsInformationByGroups,
- outputName,
- plugin: this,
- });
- });
-
- const assetTagGroupsPromise = assetsPromise
-
- .then(({ assets }) =>
- HtmlWebpackPlugin.getCompilationHooks(
- compilation,
- ).alterAssetTags.promise({
- assetTags: {
- scripts: this.generatedScriptTags(assets.js),
- styles: this.generateStyleTags(assets.css),
- meta: [
- ...(this.options.base !== false
- ? this.generateBaseTag(this.options.base)
- : []),
- ...this.generatedMetaTags(this.options.meta),
- ...(assets.favicon
- ? this.generateFaviconTag(assets.favicon)
- : []),
- ],
- },
- outputName,
- publicPath: assetsInformationByGroups.publicPath,
- plugin: this,
- }),
- )
- .then(({ assetTags }) => {
-
- const scriptTarget =
- this.options.inject === "head" ||
- (this.options.inject !== "body" &&
- this.options.scriptLoading !== "blocking")
- ? "head"
- : "body";
-
- const assetGroups = this.groupAssetsByElements(assetTags, scriptTarget);
-
- return HtmlWebpackPlugin.getCompilationHooks(
- compilation,
- ).alterAssetTagGroups.promise({
- headTags: assetGroups.headTags,
- bodyTags: assetGroups.bodyTags,
- outputName,
- publicPath: assetsInformationByGroups.publicPath,
- plugin: this,
- });
- });
-
- const templateEvaluationPromise = Promise.resolve().then(() => {
- if ("error" in templateResult) {
- return this.options.showErrors
- ? prettyError(templateResult.error, compiler.context).toHtml()
- : "ERROR";
- }
-
- if (this.options.templateContent !== false) {
- return this.options.templateContent;
- }
-
- if ("compiledEntry" in templateResult) {
- const compiledEntry = templateResult.compiledEntry;
- const assets = compiledEntry.assets;
-
- for (const name in assets) {
- previousEmittedAssets.push({
- name,
- source: assets[name].source,
- info: assets[name].info,
- });
- }
- return this.evaluateCompilationResult(
- compiledEntry.content,
- assetsInformationByGroups.publicPath,
- this.options.template,
- );
- }
- return Promise.reject(
- new Error("Child compilation contained no compiledEntry"),
- );
- });
- const templateExecutionPromise = Promise.all([
- assetsPromise,
- assetTagGroupsPromise,
- templateEvaluationPromise,
- ])
-
- .then(([assetsHookResult, assetTags, compilationResult]) =>
- typeof compilationResult !== "function"
- ? compilationResult
- : this.executeTemplate(
- compilationResult,
- assetsHookResult.assets,
- { headTags: assetTags.headTags, bodyTags: assetTags.bodyTags },
- compilation,
- ),
- );
- const injectedHtmlPromise = Promise.all([
- assetTagGroupsPromise,
- templateExecutionPromise,
- ])
-
- .then(([assetTags, html]) => {
- const pluginArgs = {
- html,
- headTags: assetTags.headTags,
- bodyTags: assetTags.bodyTags,
- plugin: this,
- outputName,
- };
- return HtmlWebpackPlugin.getCompilationHooks(
- compilation,
- ).afterTemplateExecution.promise(pluginArgs);
- })
- .then(({ html, headTags, bodyTags }) => {
- return this.postProcessHtml(compiler, html, assetsInformationByGroups, {
- headTags,
- bodyTags,
- });
- });
- const emitHtmlPromise = injectedHtmlPromise
-
- .then((html) => {
- const pluginArgs = { html, plugin: this, outputName };
- return HtmlWebpackPlugin.getCompilationHooks(compilation)
- .beforeEmit.promise(pluginArgs)
- .then((result) => result.html);
- })
- .catch((err) => {
-
-
- compilation.errors.push(prettyError(err, compiler.context).toString());
- return this.options.showErrors
- ? prettyError(err, compiler.context).toHtml()
- : "ERROR";
- })
- .then((html) => {
- const filename = outputName.replace(
- /\[templatehash([^\]]*)\]/g,
- require("util").deprecate(
- (match, options) => `[contenthash${options}]`,
- "[templatehash] is now [contenthash]",
- ),
- );
- const replacedFilename = this.replacePlaceholdersInFilename(
- compiler,
- filename,
- html,
- compilation,
- );
- const source = new compiler.webpack.sources.RawSource(html, false);
-
- compilation.emitAsset(
- replacedFilename.path,
- source,
- replacedFilename.info,
- );
- previousEmittedAssets.push({ name: replacedFilename.path, source });
- return replacedFilename.path;
- })
- .then((finalOutputName) =>
- HtmlWebpackPlugin.getCompilationHooks(compilation)
- .afterEmit.promise({
- outputName: finalOutputName,
- plugin: this,
- })
- .catch((err) => {
-
- (this.logger).error(err);
- return null;
- })
- .then(() => null),
- );
-
-
- emitHtmlPromise.then(() => {
- callback();
- });
- }
- }
- function templateParametersGenerator(compilation, assets, assetTags, options) {
- return {
- compilation: compilation,
- webpackConfig: compilation.options,
- htmlWebpackPlugin: {
- tags: assetTags,
- files: assets,
- options: options,
- },
- };
- }
- HtmlWebpackPlugin.version = 5;
- HtmlWebpackPlugin.getHooks = HtmlWebpackPlugin.getCompilationHooks;
- HtmlWebpackPlugin.createHtmlTagObject = createHtmlTagObject;
- module.exports = HtmlWebpackPlugin;
|