UNPKG

@hyext/preload

Version:

A tools for huya miniapp builder preload something

504 lines (485 loc) 22.4 kB
/*! * @hyext/preload * (c) 2020-2025 Alex * Released under the MIT License. */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var fs = require('fs-extra'); var path = require('path'); var axios = require('axios'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs); var path__default = /*#__PURE__*/_interopDefaultLegacy(path); var axios__default = /*#__PURE__*/_interopDefaultLegacy(axios); /*! ***************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } function __generator(thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } var loadingConfig = { logo: { dark: 'https://a.msstatic.com/huya/hd/cdn_libs/img/loading-1.webp', light: 'https://a.msstatic.com/huya/hd/cdn_libs/img/loading-2.webp' }, rect: { width: 36, height: 36 } }; function transformStyleCode(styleCode) { return styleCode.split(';').reduce(function (acc, item) { var _a = item.split(':'), key = _a[0], val = _a[1]; acc[key.trim()] = val.trim(); return acc; }, {}); } function genStyleCode(style) { var styleMap = style; return Object.keys(styleMap).reduce(function (code, key) { var val = styleMap[key]; code += key + ":" + val + ";"; return code; }, ''); } var styles = { bg: { 'z-index': '-1', 'background-repeat': 'no-repeat', 'background-position': 'left top', 'background-size': '100%' }, loading: { 'z-index': '9999', display: 'flex', 'flex-direction': 'column', 'align-items': 'center', 'justify-content': 'center', 'background-color': '#fff' } }; var LoadingScreenCode = /** @class */ (function () { function LoadingScreenCode(props, extInfo) { this.timeout = 10 * 1000; this.style = {}; this.logo = loadingConfig.logo.light; // 用 主题 light this.extName = (extInfo === null || extInfo === void 0 ? void 0 : extInfo.extName) || '小程序'; this.autoRemove = props.autoRemove; Object.assign(this.style, styles.loading); } LoadingScreenCode.prototype.getUrl = function () { return this.logo; }; LoadingScreenCode.prototype.renderStyleTag = function () { return "<style>\n\n.firstScreen__logo {\n display: block;\n width: " + loadingConfig.rect.width + "px;\n height: " + loadingConfig.rect.height + "px;\n}\n\n </style>\n"; }; LoadingScreenCode.prototype.renderHTML = function () { return "\n\n <img\n class=\"firstScreen__logo\"\n src=\"" + this.logo + "\"\n alt=\"logo\"\n />\n \n"; }; LoadingScreenCode.prototype.renderScript = function (renderRemoveFn) { var code = ''; var guardTimerId = 'guardTimer'; var implementCode = renderRemoveFn(false, guardTimerId + " && clearTimeout(" + guardTimerId + ");"); if (this.autoRemove) { code += "window.__autoRemoveFirstScreen = true;\n"; } code += "var " + guardTimerId + "\n"; code += "window.__removeFirstScreen = " + implementCode + "\n"; code += "function guard() {\n " + guardTimerId + " = setTimeout(function () {\n window.__removeFirstScreen && window.__removeFirstScreen()\n }, " + this.timeout + ")\n }\nguard()\n"; return code; }; return LoadingScreenCode; }()); var StaicBgScreenCode = /** @class */ (function () { function StaicBgScreenCode(props) { this.autoRemove = true; this.dynamicSize = false; this.delay = 16; this.style = {}; this.dynamicStyle = {}; var urlIsString = typeof props.url === 'string' && props.url !== ''; if (!urlIsString) { throw new Error('props.url must be a string with length.'); } this.url = props.url; this.style['background-image'] = "url(" + props.url + ")"; this.autoRemove = props.autoRemove; if (typeof props.delay !== 'undefined') { this.delay = props.delay; } Object.assign(this.style, styles.bg, typeof props.style === 'string' ? transformStyleCode(props.style) : props.style); var bgHeight = props.bgHeight; if (typeof bgHeight === 'string') { this.dynamicSize = true; this.style['opacity'] = '0'; this.dynamicStyle['background-size'] = "100% " + bgHeight; } } StaicBgScreenCode.prototype.updateUrl = function (url) { this.style['background-image'] = "url(" + url + ")"; this.url = url; }; StaicBgScreenCode.prototype.getUrl = function () { return this.url; }; StaicBgScreenCode.prototype.renderStyleTag = function () { return ''; }; StaicBgScreenCode.prototype.renderHTML = function () { return ''; }; StaicBgScreenCode.prototype._renderDynamicStyleCode = function () { return "\n var el = document.getElementById('_firstScreen')\n if (el == null) return\n var dynamicStyle = " + JSON.stringify(this.dynamicStyle) + "\n var BASE_DEVICE_WIDTH = 750\n var deviceWidth = Math.min(window.innerWidth, window.innerHeight)\n function transformRPX(number, newDeviceWidth) {\n if (number === 0) return 0;\n if (number === 1) return 1;\n number = (number / BASE_DEVICE_WIDTH) * (newDeviceWidth || deviceWidth);\n number = Math.floor(number);\n return number;\n }\n var regex = /\\b\\d+(\\.\\d+)?rpx\\b/\n Object.keys(dynamicStyle).map(function (key) {\n var val = dynamicStyle[key]\n dynamicStyle[key] = val.replace(regex, (s, s1) => {\n var val = s.replace('rpx', '')\n return transformRPX(Number(val)) + 'px'\n })\n })\n Object.keys(dynamicStyle).map(function (key) {\n var val = dynamicStyle[key]\n el.style[key] = val\n })\n el.style['opacity'] = 1\n "; }; StaicBgScreenCode.prototype.renderScript = function (renderRemoveFn) { var autoRemove = this.autoRemove; var code = ''; var implementCode = ''; if (this.dynamicSize) { code += this._renderDynamicStyleCode(); } if (autoRemove) { var isTimeout = typeof autoRemove === 'number' && autoRemove > 0; if (isTimeout) { var timerId = '__firstscreen_timer'; implementCode = renderRemoveFn(this.delay, timerId + " && clearTimeout(" + timerId + ")"); code += "var " + timerId + " = setTimeout(" + implementCode + ", " + autoRemove + ");"; } else { implementCode = renderRemoveFn(this.delay); code += "window.addEventListener('load', " + implementCode + ");"; } } else { implementCode = renderRemoveFn(this.delay); } code += "\n\nwindow.__removeFirstScreen = " + implementCode; return code; }; return StaicBgScreenCode; }()); var FirstScreenCode = /** @class */ (function () { function FirstScreenCode(props, extInfo) { this.style = { position: 'absolute', width: '100%', height: '100%', left: '0', top: '0' }; this.autoRemove = true; this.init(props, extInfo); } FirstScreenCode.prototype.init = function (props, extInfo) { if (typeof props.autoRemove !== 'undefined') { this.autoRemove = props.autoRemove; } this.mode = props.mode; if (props.mode === 'loading') { this.modeCode = new LoadingScreenCode({ autoRemove: this.autoRemove }, extInfo); } else { this.modeCode = new StaicBgScreenCode(__assign(__assign({}, props), { autoRemove: this.autoRemove })); } }; FirstScreenCode.prototype.wrapSetTimeout = function (delay, content) { if (delay === void 0) { delay = 16; } return "\n setTimeout(function() {\n " + content + "\n }, " + delay + ")\n "; }; FirstScreenCode.prototype.renderRemoveFirstScreenCode = function (delay, extraCode) { var content = " var fs = document.getElementById('_firstScreen')\n if (fs != null) {\n document.body.removeChild(fs)\n }\n " + (extraCode || ''); content = typeof delay === 'number' ? this.wrapSetTimeout(delay, content) : content; return "function () {\n " + content + "\n }"; }; FirstScreenCode.prototype.renderHandleFirstScreenCode = function (wrapScriptTag) { if (wrapScriptTag === void 0) { wrapScriptTag = true; } var content = this.modeCode.renderScript(this.renderRemoveFirstScreenCode.bind(this)); var code = "\n(function () {\n" + content + "\n})();\n"; if (wrapScriptTag) { return "<script>" + code + "</script>"; } return code; }; FirstScreenCode.prototype.renderStyleTag = function () { return this.modeCode.renderStyleTag(); }; FirstScreenCode.prototype.genPreloadLinkConfig = function () { var url = this.modeCode.getUrl(); if (url.startsWith('data:image/')) return null; return { rel: 'preload', href: this.modeCode.getUrl(), as: 'image', }; }; FirstScreenCode.prototype.renderFirstScreenNodeCode = function () { // 合并 style Object.assign(this.style, this.modeCode.style); return "<div id=\"_firstScreen\" style=\"" + genStyleCode(this.style) + "\">" + this.modeCode.renderHTML() + "</div>"; }; FirstScreenCode.prototype.render = function () { return this.renderFirstScreenNodeCode() + "\n" + this.renderHandleFirstScreenCode(); }; return FirstScreenCode; }()); var LinkTagCode = /** @class */ (function () { function LinkTagCode(props) { this.links = []; this.links = props; } LinkTagCode.prototype.add = function (link) { this.links.push(link); }; LinkTagCode.prototype.renderLinkTag = function (linkConfig) { var rel = linkConfig.rel, href = linkConfig.href, extra = linkConfig.extra, as = linkConfig.as; return "<link rel=\"" + rel + "\" href=\"" + href + "\" as=\"" + as + "\" " + (extra || '') + " />\n"; }; LinkTagCode.prototype.render = function () { var _this = this; var code = this.links.reduce(function (acc, item) { acc += _this.renderLinkTag(item); return acc; }, ''); return "\n" + code; }; return LinkTagCode; }()); function getImageSizeByLocal(imagePath) { try { var stats = fs__default["default"].statSync(imagePath); // 获取文件信息 var fileSizeInBytes = stats.size; // 获取文件大小,单位为字节 var fileSizeInKB = fileSizeInBytes / 1024; // 转换为 KB return Number(fileSizeInKB.toFixed(2)); // 保留两位小数 } catch (error) { console.error('获取本地图片大小失败: ', error.message); } } // 获取网络图片大小的函数 function getImageSizeByNetwork(url) { return __awaiter(this, void 0, void 0, function () { var response, contentLength, fileSizeInKB, error_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, axios__default["default"].head(url) // 从响应头中获取 content-length ]; case 1: response = _a.sent(); contentLength = response.headers['content-length']; if (contentLength) { fileSizeInKB = contentLength / 1024; return [2 /*return*/, Number(fileSizeInKB.toFixed(2))]; // 保留两位小数 } else { throw new Error('无法获取图片大小'); } case 2: error_1 = _a.sent(); console.error('获取网络图片大小失败: ', error_1.message); return [3 /*break*/, 3]; case 3: return [2 /*return*/]; } }); }); } var maxBgSzie = 200; // KB function checkBgSize(url, projectPath) { return __awaiter(this, void 0, void 0, function () { var size, imagePath; return __generator(this, function (_a) { switch (_a.label) { case 0: imagePath = url; if (!projectPath) return [3 /*break*/, 1]; checkBgSizeByLocal(imagePath); return [3 /*break*/, 3]; case 1: if (!url.startsWith("http")) return [3 /*break*/, 3]; return [4 /*yield*/, getImageSizeByNetwork(imagePath)]; case 2: size = _a.sent(); _a.label = 3; case 3: if (size && size > 0 && size > maxBgSzie) { throwOverSizeError(maxBgSzie, size, imagePath); } return [2 /*return*/]; } }); }); } function throwOverSizeError(limit, real, imagePath) { throw new Error("\u80CC\u666F\u56FE\u7247\u5927\u5C0F\u4E0D\u80FD\u8D85\u8FC7" + limit + "KB, \u5927\u5C0F\u4E3A" + real + "KB, \u56FE\u7247\u8DEF\u5F84\u4E3A" + imagePath + "\uFF0C\u8BF7\u5C1D\u8BD5\u538B\u7F29\u3002"); } function checkBgSizeByLocal(imageLocalPath) { var size, imagePath = imageLocalPath; if (!fs__default["default"].existsSync(imagePath)) { throw new Error("本地静态图片资源路径不存在,图片路径为:" + imagePath); } size = getImageSizeByLocal(imagePath); if (size && size > 0 && size > maxBgSzie) { throwOverSizeError(maxBgSzie, size, imagePath); } } function tryCheckBgSizeByLocal(url, projectPath) { var filePath = findAssetPath(url, projectPath); checkBgSizeByLocal(filePath); } function findAssetPath(url, projectPath) { var fromStaticDir = path__default["default"].join(projectPath, "static", url); var fromProjectDir = path__default["default"].join(projectPath, url); if (fs__default["default"].existsSync(fromStaticDir)) { return fromStaticDir; } else if (fs__default["default"].existsSync(fromProjectDir)) { return fromProjectDir; } else { throw new Error("\u4F7F\u7528\u9759\u6001\u56FE\u7247\u8D44\u6E90\u524D\uFF0C\u8BF7\u5148\u786E\u4FDD\u6587\u4EF6\u5B58\u5728\u9879\u76EE\u4E2D, \u539F\u56FE\u7247\u8DEF\u5F84\u4E3A:" + url); } } function genPreloadCode(preloadConfig, extInfo) { var preload = preloadConfig; var _a = preload.links, links = _a === void 0 ? [] : _a; preload.script; var firstScreen = preload.firstScreen; var bodyContent = ""; var headerContent = ""; var firstScreenCode; if (typeof firstScreen === "object") { firstScreenCode = new FirstScreenCode(firstScreen, extInfo); var preloadImgConf = firstScreenCode.genPreloadLinkConfig(); preloadImgConf && links.push(preloadImgConf); bodyContent += firstScreenCode.render(); } if (links != null && links.length > 0) { headerContent += new LinkTagCode(links).render(); } if (firstScreenCode != null) { headerContent += firstScreenCode.renderStyleTag(); } return { bodyContent: bodyContent, headerContent: headerContent, firstScreenCode: firstScreenCode, }; } function genPreloadNode(preloadConfig, extInfo) { var preload = preloadConfig; var _a = preload.links, links = _a === void 0 ? [] : _a, firstScreen = preload.firstScreen; var linkTagNode = null; var firstScreenNode = null; if (typeof firstScreen === "object") { firstScreenNode = new FirstScreenCode(firstScreen, extInfo); } linkTagNode = new LinkTagCode(links); return { linkTagNode: linkTagNode, firstScreenNode: firstScreenNode, }; } function createResolvePreloadByExtTypeFn(genFn) { return function (opts) { var _a; var extInfo = opts.extInfo, projectPath = opts.projectPath; var preloadConfig = getPreloadConfigByExtType(opts); if (preloadConfig != null) { if (((_a = preloadConfig === null || preloadConfig === void 0 ? void 0 : preloadConfig.firstScreen) === null || _a === void 0 ? void 0 : _a.mode) === "static-bg") { tryCheckBgSizeByLocal(preloadConfig.firstScreen.url, projectPath); } return genFn(preloadConfig, extInfo); } return null; }; } function getPreloadConfigByExtType(opts) { var _a; var preload = opts.preloadEntry; var extInfo = opts.extInfo; // 未配置 或者 浮窗 直接跳过。 if (preload == null || extInfo.extType.includes("popup")) { return null; } var firstScreenScope = (_a = preload === null || preload === void 0 ? void 0 : preload.scope) === null || _a === void 0 ? void 0 : _a.firstScreen; if (firstScreenScope) { if (Array.isArray(firstScreenScope.includes)) { if (firstScreenScope.includes.find(function (extType) { return extType === extInfo.extType; })) { return preload; } } if (Array.isArray(firstScreenScope.excludes)) { if (firstScreenScope.excludes.find(function (extType) { return extType === extInfo.extType; })) { return null; } } } // @ts-ignore var conf = preload[extInfo.extType] || preload; return conf; } var resolvePreloadCodeByExtType = createResolvePreloadByExtTypeFn(genPreloadCode); var resolvePreloadNodeByExtType = createResolvePreloadByExtTypeFn(genPreloadNode); exports.checkBgSize = checkBgSize; exports.checkBgSizeByLocal = checkBgSizeByLocal; exports.findAssetPath = findAssetPath; exports.genPreloadNode = genPreloadNode; exports.resolvePreloadCodeByExtType = resolvePreloadCodeByExtType; exports.resolvePreloadNodeByExtType = resolvePreloadNodeByExtType; exports.tryCheckBgSizeByLocal = tryCheckBgSizeByLocal;