packages/build-plugin-lce/src/common/htmlInjection.js (169 lines of code) (raw):
const cloneDeep = require('lodash.clonedeep');
const htmlContentStorage = {};
const multiHTMLContentStorage = {};
const HTML_POSITIONS = {
headAppend: {
defaultValue: [],
type: 'array',
},
headPrepend: {
defaultValue: [],
type: 'array',
},
bodyAppend: {
defaultValue: [],
type: 'array',
},
bodyPrepend: {
defaultValue: [],
type: 'array',
},
htmlAttrs: {
defaultValue: '',
type: 'object',
},
headAttrs: {
defaultValue: '',
type: 'object',
},
bodyAttrs: {
defaultValue: '',
type: 'object',
},
rootContainer: {
defaultValue: [],
type: 'string',
},
};
const TAGS_INFO = {
meta: {
selfClosing: true,
},
title: {},
link: {
selfClosing: true,
},
style: {},
script: {},
};
function injectHTML(callback, position) {
if (!Object.keys(HTML_POSITIONS).includes(position)) {
console.warn('[user config]', `unknown position ${position}.`);
}
// store html content added by applyMethod
htmlContentStorage[position] = callback(htmlContentStorage[position]);
}
function parseHTMLContent(content) {
if (Object.prototype.toString.call(content) === '[object Object]') {
const { tag, innerHTML, tagId, ...attrs } = content;
const { selfClosing } = TAGS_INFO[tag];
const attrStr = parseHTMLAttrs(attrs);
return selfClosing && !innerHTML ? `<${tag}${attrStr}/>` : `<${tag}${attrStr}>${innerHTML}</${tag}>`;
}
return content;
}
function parseHTMLAttrs(attrs) {
const attrKeys = Object.keys(attrs);
return attrKeys.length ? ` ${attrKeys.map((attrKey) => `${attrKey}="${attrs[attrKey]}"`).join(' ')}` : '';
}
function getHTMLParams(htmlContent) {
const htmlParams = {};
Object.keys(htmlContent).forEach((htmlPosition) => {
const htmlTags = htmlContent[htmlPosition];
htmlParams[htmlPosition] = Array.isArray(htmlTags) ? htmlTags.map((htmlTag) => parseHTMLContent(htmlTag)).join('\n') : parseHTMLContent(htmlTags);
});
return htmlParams;
}
function modifyHTMLPluginOptions(config, pluginName, options, params) {
if (config.plugins.get(pluginName)) {
// spa HtmlWebpackPlugin
config.plugin(pluginName).tap(([args]) => {
const defaultValues = {};
Object.keys(HTML_POSITIONS).forEach((positionKey) => {
defaultValues[positionKey] = HTML_POSITIONS[positionKey].defaultValue;
});
/**
* for getDemoConfig needs extra info to inject to html.
* merge htmlAppendInjection to params for getDemoConfig.
*/
const { htmlAppendInjection } = args || {};
const _params = {
...params,
};
if (htmlAppendInjection) {
Object.keys(htmlAppendInjection).forEach((position) => {
// eslint-disable-next-line no-param-reassign
_params[position] = (_params[position] || '').concat(htmlAppendInjection[position]);
});
}
const pluginOptions = {
...args,
templateParameters: {
...(args.templateParameters || {}),
...defaultValues, // default value of html positions
..._params,
},
...options,
};
return [pluginOptions];
});
}
}
function configHTMLPlugin(config) {
const htmlParams = getHTMLParams(htmlContentStorage);
const htmlPluginOptions = {
inject: false,
};
const entry = config.entryPoints.entries();
// mpa HtmlWebpackPlugin
if (Object.prototype.toString.call(entry) === '[object Object]' && Object.keys(entry).length > 1) {
// delete multi HtmlWebpackPlugin
Object.keys(entry).forEach((entryKey) => {
const pluginName = `HtmlWebpackPlugin_${entryKey}`;
modifyHTMLPluginOptions(
config,
pluginName,
htmlPluginOptions,
multiHTMLContentStorage[entryKey] ? getHTMLParams(multiHTMLContentStorage[entryKey]) : htmlParams,
);
});
} else {
modifyHTMLPluginOptions(config, 'HtmlWebpackPlugin', htmlPluginOptions, htmlParams);
}
}
function configHTMLContent(htmlInjection, entryKey) {
let storage = htmlContentStorage;
if (entryKey) {
multiHTMLContentStorage[entryKey] = cloneDeep(htmlContentStorage);
storage = multiHTMLContentStorage[entryKey];
}
Object.keys(htmlInjection).forEach((optionKey) => {
if (HTML_POSITIONS[optionKey]) {
const { type } = HTML_POSITIONS[optionKey];
const value = htmlInjection[optionKey];
if (type === 'array') {
const newValue = [];
// overwrite content by tagId / unique tag
value.forEach((tagInfo) => {
const { tagId, tag } = tagInfo;
let index = -1;
if (tag === 'title') {
index = (storage[optionKey] || []).findIndex((item) => item.tag === tag);
} else if (tagId) {
index = (storage[optionKey] || []).findIndex((item) => item.tagId === tagId);
}
if (index > -1) {
storage[optionKey][index] = tagInfo;
} else {
newValue.push(tagInfo);
}
});
storage[optionKey] = (storage[optionKey] || []).concat(newValue);
} else if (type === 'object') {
storage[optionKey] = {
...(storage[optionKey] || {}),
...value,
};
} else {
storage[optionKey] = value;
}
}
});
}
exports.configWebpack = (htmlInjection) => {
if (htmlInjection) {
configHTMLContent(htmlInjection);
}
};
exports.configHTMLPlugin = configHTMLPlugin;
exports.configHTMLContent = configHTMLContent;