UNPKG

@ksconsole/qiankun-plus

Version:

A completed implementation of Micro Frontends

195 lines (188 loc) 8.39 kB
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray"; /** * @author Kuitos * @homepage https://github.com/kuitos/ * @since 2018-09-03 15:04 */ import { getInlineCode, isModuleScriptSupported, parseUrl } from './utils'; var ALL_SCRIPT_REGEX = /(<script[\s\S]*?>)[\s\S]*?<\/script>/gi; var SCRIPT_TAG_REGEX = /<(script)\s+((?!type=('|")text\/ng-template\3)[^])*?>[^]*?<\/\1>/i; var SCRIPT_SRC_REGEX = /.*\ssrc=('|")?([^>'"\s]+)/; var SCRIPT_TYPE_REGEX = /.*\stype=('|")?([^>'"\s]+)/; var SCRIPT_ENTRY_REGEX = /.*\sentry\s*.*/; var SCRIPT_ASYNC_REGEX = /.*\sasync\s*.*/; var SCRIPT_CROSSORIGIN_REGEX = /.*\scrossorigin=('|")?use-credentials\1/; var SCRIPT_NO_MODULE_REGEX = /.*\snomodule\s*.*/; var SCRIPT_MODULE_REGEX = /.*\stype=('|")?module('|")?\s*.*/; var LINK_TAG_REGEX = /<(link)\s+[^]*?>/ig; var LINK_PRELOAD_OR_PREFETCH_REGEX = /\srel=('|")?(preload|prefetch)\1/; var LINK_HREF_REGEX = /.*\shref=('|")?([^>'"\s]+)/; var LINK_AS_FONT = /.*\sas=('|")?font\1.*/; var STYLE_TAG_REGEX = /<style[^>]*>[\s\S]*?<\/style>/gi; var STYLE_TYPE_REGEX = /\s+rel=('|")?stylesheet\1.*/; var STYLE_HREF_REGEX = /.*\shref=('|")?([^>'"\s]+)/; var HTML_COMMENT_REGEX = /<!--([\s\S]*?)-->/g; var LINK_IGNORE_REGEX = /<link(\s+|\s+[^]+\s+)ignore(\s*|\s+[^]*|=[^]*)>/i; var STYLE_IGNORE_REGEX = /<style(\s+|\s+[^]+\s+)ignore(\s*|\s+[^]*|=[^]*)>/i; var SCRIPT_IGNORE_REGEX = /<script(\s+|\s+[^]+\s+)ignore(\s*|\s+[^]*|=[^]*)>/i; function hasProtocol(url) { return url.startsWith('http://') || url.startsWith('https://'); } function getEntirePath(path, baseURI) { return new URL(path, baseURI).toString(); } function isValidJavaScriptType(type) { var handleTypes = ['text/javascript', 'module', 'application/javascript', 'text/ecmascript', 'application/ecmascript']; return !type || handleTypes.indexOf(type) !== -1; } export var genLinkReplaceSymbol = function genLinkReplaceSymbol(linkHref) { var preloadOrPrefetch = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; return "<!-- ".concat(preloadOrPrefetch ? 'prefetch/preload' : '', " link ").concat(linkHref, " replaced by import-html-entry -->"); }; export var genScriptReplaceSymbol = function genScriptReplaceSymbol(scriptSrc) { var async = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var crossOrigin = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; return "<!-- ".concat(crossOrigin ? 'cors' : '', " ").concat(async ? 'async' : '', " script ").concat(scriptSrc, " replaced by import-html-entry -->"); }; export var inlineScriptReplaceSymbol = "<!-- inline scripts replaced by import-html-entry -->"; export var genIgnoreAssetReplaceSymbol = function genIgnoreAssetReplaceSymbol(url) { return "<!-- ignore asset ".concat(url || 'file', " replaced by import-html-entry -->"); }; export var genModuleScriptReplaceSymbol = function genModuleScriptReplaceSymbol(scriptSrc, moduleSupport) { return "<!-- ".concat(moduleSupport ? 'nomodule' : 'module', " script ").concat(scriptSrc, " ignored by import-html-entry -->"); }; /** * parse the script link from the template * 1. collect stylesheets * 2. use global eval to evaluate the inline scripts * see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function#Difference_between_Function_constructor_and_function_declaration * see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#Do_not_ever_use_eval! * @param tpl * @param baseURI * @param postProcessTemplate * @stripStyles whether to strip the css links * @returns {{template: void | string | *, scripts: *[], entry: *}} */ export default function processTpl(tpl, baseURI, postProcessTemplate) { var scripts = []; var styles = []; var entry = null; var moduleSupport = isModuleScriptSupported(); var template = tpl /* remove html comment first */.replace(HTML_COMMENT_REGEX, '').replace(LINK_TAG_REGEX, function (match) { /* change the css link */ var styleType = !!match.match(STYLE_TYPE_REGEX); if (styleType) { var styleHref = match.match(STYLE_HREF_REGEX); var styleIgnore = match.match(LINK_IGNORE_REGEX); if (styleHref) { var href = styleHref && styleHref[2]; var newHref = href; if (href && !hasProtocol(href)) { newHref = getEntirePath(href, baseURI); } if (styleIgnore) { return genIgnoreAssetReplaceSymbol(newHref); } newHref = parseUrl(newHref); styles.push(newHref); return genLinkReplaceSymbol(newHref); } } var preloadOrPrefetchType = match.match(LINK_PRELOAD_OR_PREFETCH_REGEX) && match.match(LINK_HREF_REGEX) && !match.match(LINK_AS_FONT); if (preloadOrPrefetchType) { var _match$match = match.match(LINK_HREF_REGEX), _match$match2 = _slicedToArray(_match$match, 3), linkHref = _match$match2[2]; return genLinkReplaceSymbol(linkHref, true); } return match; }).replace(STYLE_TAG_REGEX, function (match) { if (STYLE_IGNORE_REGEX.test(match)) { return genIgnoreAssetReplaceSymbol('style file'); } return match; }).replace(ALL_SCRIPT_REGEX, function (match, scriptTag) { var scriptIgnore = scriptTag.match(SCRIPT_IGNORE_REGEX); var moduleScriptIgnore = moduleSupport && !!scriptTag.match(SCRIPT_NO_MODULE_REGEX) || !moduleSupport && !!scriptTag.match(SCRIPT_MODULE_REGEX); // in order to keep the exec order of all javascripts var matchedScriptTypeMatch = scriptTag.match(SCRIPT_TYPE_REGEX); var matchedScriptType = matchedScriptTypeMatch && matchedScriptTypeMatch[2]; if (!isValidJavaScriptType(matchedScriptType)) { return match; } // if it is a external script if (SCRIPT_TAG_REGEX.test(match) && scriptTag.match(SCRIPT_SRC_REGEX)) { /* collect scripts and replace the ref */ var matchedScriptEntry = scriptTag.match(SCRIPT_ENTRY_REGEX); var matchedScriptSrcMatch = scriptTag.match(SCRIPT_SRC_REGEX); var matchedScriptSrc = matchedScriptSrcMatch && matchedScriptSrcMatch[2]; if (entry && matchedScriptEntry) { throw new SyntaxError('You should not set multiply entry script!'); } if (matchedScriptSrc) { // append the domain while the script not have a protocol prefix if (!hasProtocol(matchedScriptSrc)) { matchedScriptSrc = getEntirePath(matchedScriptSrc, baseURI); } matchedScriptSrc = parseUrl(matchedScriptSrc); } entry = entry || matchedScriptEntry && matchedScriptSrc; if (scriptIgnore) { return genIgnoreAssetReplaceSymbol(matchedScriptSrc || 'js file'); } if (moduleScriptIgnore) { return genModuleScriptReplaceSymbol(matchedScriptSrc || 'js file', moduleSupport); } if (matchedScriptSrc) { var asyncScript = !!scriptTag.match(SCRIPT_ASYNC_REGEX); var crossOriginScript = !!scriptTag.match(SCRIPT_CROSSORIGIN_REGEX); scripts.push(asyncScript || crossOriginScript ? { async: asyncScript, src: matchedScriptSrc, crossOrigin: crossOriginScript } : matchedScriptSrc); return genScriptReplaceSymbol(matchedScriptSrc, asyncScript, crossOriginScript); } return match; } else { if (scriptIgnore) { return genIgnoreAssetReplaceSymbol('js file'); } if (moduleScriptIgnore) { return genModuleScriptReplaceSymbol('js file', moduleSupport); } // if it is an inline script var code = getInlineCode(match); // remove script blocks when all of these lines are comments. var isPureCommentBlock = code.split(/[\r\n]+/).every(function (line) { return !line.trim() || line.trim().startsWith('//'); }); if (!isPureCommentBlock) { scripts.push(match); } return inlineScriptReplaceSymbol; } }); scripts = scripts.filter(function (script) { // filter empty script return !!script; }); var tplResult = { template: template, scripts: scripts, styles: styles, // set the last script as entry if have not set entry: entry || scripts[scripts.length - 1] }; if (typeof postProcessTemplate === 'function') { tplResult = postProcessTemplate(tplResult); } return tplResult; }